手撕code(3)

文章目录

  • 迷宫最短路径和输出
    • 深度优先
    • 广度优先
  • 48 旋转矩阵图像
  • 大数加减法
  • 146 LRU 缓存算法
  • 460 LFU 缓存算法


迷宫最短路径和输出

给定一个 n × m 的二维整数数组,用来表示一个迷宫,数组中只包含 0 或 1 ,其中 0 表示可以走的路,1 表示不可通过的墙壁。
最初,有一个人位于左上角 (1, 1) 处,已知该人每次可以向上、下、左、右任意一个方向移动一个位置。
请问,该人从左上角移动至右下角 ( n, m ) 处,至少需要移动多少次。
数据保证 (1, 1) 处和 ( n, m ) 处的数字为 0 ,且一定至少存在一条通路。

Input

第一行包含两个整数 n 和 m 。1 ≤ n, m ≤ 100

接下来 n 行,每行包含 m 个整数( 0 或 1 ),表示完整的二维数组迷宫。

Output

输出一个整数,表示从左上角移动至右下角的最少移动次数以及移动路径。

Sample Input

5 5
0 1 0 0 0
0 1 0 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0

Sample Output

8
1 1
2 1
3 1
3 2
3 3
3 4
3 5
4 5
5 5

深度优先

#include<iostream>
#include<vector>using namespace std;vector<pair<int, int>> path;  //当前搜索到的路径
vector<pair<int, int>> res;   //记录的最短的路径
int minLength = INT_MAX;void dfs(vector<vector<int>>& grid, int x, int y, int step) {if (x == grid.size() - 1 && y == grid[0].size() - 1) {  //搜索到目的地点if (res.size() == 0 || res.size() > path.size()) res = path;if (step < minLength) minLength = step;return;}int dx[4] = {0, 1, 0, -1};  //右下左上int dy[4] = {1, 0, -1, 0};for (int i = 0; i < 4; i++) {int nx = x + dx[i];int ny = y + dy[i];if (nx < 0 || nx >= grid.size() || ny < 0 || ny >= grid[0].size() || grid[nx][ny] != 0) continue;// 边界判定一定要在前面!!!grid[nx][ny] += 5;  //将迷宫路径置为其他值  不走重复路    未设置原先的grid[x][y]但是不受影响 在下一个坐标会把x、y覆盖path.push_back({nx, ny});dfs(grid, nx, ny, step + 1);  //小循环path.pop_back();grid[nx][ny] -= 5; //恢复现场   因为有可能其他路与之前的路径会有重叠路径   若不恢复现场  则此次走过的路 在下一个大小循环中不能再走}}int main() {int row, col;cin >> row >> col;vector<vector<int>> grid(row, vector<int> (col));for (int i = 0; i < row; i++) {for (int j = 0; j < col; j++) {cin >> grid[i][j];}} dfs(grid, 0, 0, 0);   // 大循环cout << "minLength is " << minLength << endl;for (int i = 0; i < res.size(); i++) {cout << res[i].first << " " << res[i].second << endl;}system("pause");return 0;
}

广度优先

#include<iostream>
#include<vector>
#include<queue>
using namespace std;int minLength = 0;
void print(int x,int y, vector<vector<int>>& fax, vector<vector<int>>& fay){if(x==0 && y==0){cout << "(" << x << ", " << y << ")" <<endl;return;}print(fax[x][y], fay[x][y], fax, fay); // 从最后一个坐标递归回溯到前一个坐标cout << "(" << x << ", " << y << ")" << endl;
}void bfs(vector<vector<int>>& grid, vector<vector<int>>& visited, int x, int y, vector<vector<int>>& fax, vector<vector<int>>& fay) {queue<pair<int, int>> q;if (grid[x][y] != 0) return;  // 判断起点是否合法q.push(make_pair(x, y));  //队列q中存起点坐标x y//bfs注意一定不能重复走!!!!int dx[4] = {0, 1, 0, -1};int dy[4] = {1, 0, -1, 0};while (!q.empty()) {int size = q.size();for (int i = 0; i < size; i++) {int row = q.front().first;int col = q.front().second;q.pop();if (row == grid.size() - 1 && col == grid[0].size() - 1) {print(grid.size() - 1, grid[0].size() - 1, fax, fay);return; // 只要遍历到结果一定是最短路 }for (int i = 0; i < 4; i++) {int nx = row + dx[i];int ny = col + dy[i];if (nx < 0 || nx >= grid.size() || ny < 0 || ny >= grid[0].size() || grid[nx][ny] != 0 || visited[nx][ny] != 0) continue;visited[nx][ny] = visited[row][col] + 1;  // 走过的节点进行赋值  保证不走重复路 可用grid[nx][ny] = 1代替q.push(make_pair(nx, ny));fax[nx][ny] = row;fay[nx][ny] = col;}}minLength++;  // 代表遍历的层数}} 
int main() {int row, col;cin >> row >> col;vector<vector<int>> grid(row, vector<int> (col));for (int i = 0; i < row; i++) {for (int j = 0; j < col; j++) {cin >> grid[i][j];}} vector<vector<int>> visited(row, vector<int> (col, 0)); vector<vector<int>> fax(row, vector<int> (col, -1)); vector<vector<int>> fay(row, vector<int> (col, -1)); bfs(grid, visited, 0, 0, fax, fay);cout << "minLength is " << minLength << endl;  //或者visited[row - 1][col - 1]    cout << "------------------------------------" << endl;for (int i = 0; i < row; i++) {for (int j = 0; j < col; j++) {cout << fax[i][j] << "    ";}cout << endl;} cout << "------------------------------------" << endl;for (int i = 0; i < row; i++) {for (int j = 0; j < col; j++) {cout << fay[i][j] << "    ";}cout << endl;} system("pause");return 0;
}/*
求输出路径方法
开两个二维数组fax  和 fay  然后  分别记录上一个位置的x 和 y
*/

48 旋转矩阵图像

在这里插入图片描述

class Solution {
public:void rotate(vector<vector<int>>& matrix) {int n = matrix.size();// 水平翻转for (int i = 0; i < n / 2; ++i) {for (int j = 0; j < n; ++j) {swap(matrix[i][j], matrix[n - i - 1][j]);}}// 主对角线翻转for (int i = 0; i < n; ++i) {for (int j = 0; j < i; ++j) {swap(matrix[i][j], matrix[j][i]);}}}
};

大数加减法

参考链接:https://blog.csdn.net/mrqiuwen/article/details/127057549

因为较大整数的相加很可能超出整型的32位限制,或者本身就是超出限制的大数之间的加减运算。
所以需要单独写一个能大数相加减的函数。
基本原理:把数字用字符串的形式来保存加减的结果或大数字本身就不会超出限制,比如“999999999999999999999999999999” 多长都可以。

#include<iostream>
#include<string>
#include<algorithm>
using namespace std;class BigInt 
{
public:BigInt(string str) : strDigit(str){ }
private:string strDigit;//使用字符串存储大整数//都是全局函数声明为友元friend ostream& operator<<(ostream& out, const BigInt& src);friend BigInt operator+(const BigInt& lhs, const BigInt& rhs);friend BigInt operator-(const BigInt& lhs, const BigInt& rhs);};
//打印函数
ostream& operator<<(ostream& out, const BigInt& src)
{out << src.strDigit;return out;
}//大数加法
BigInt operator+(const BigInt& lhs, const BigInt& rhs) {//从后往前遍历字符串lhs,和rhs,用flag来标记是否需要进位,结果存在result数组中(反向的)string result;bool flag = false;int i = lhs.strDigit.length() - 1;//string的length和size没有区别,length是沿用C语言习惯,早先只有lengthint j = rhs.strDigit.length() - 1;for (; i >= 0 && j >= 0; --i, --j) //i 和 j其中一个会被减到负数然后退出for循环{int ret = lhs.strDigit[i] - '0' + rhs.strDigit[j] - '0';//单个位加减时需要减'0'转成整型,每次都重新定义一个新的retif (flag){ret += 1;//flag为true说明此位因为上一位进位而需要多加一个1flag = false;//再将其重新置为false}//两个if不能互换,否则不是下一位进1if (ret >= 10){ret %= 10;flag = true;}result.push_back(ret + '0');}//如果遍历完,i还有剩下,第一个字符串没完if (i >= 0)//注意要取等,因为更短的那个字符串的下标是被减到-1,而不是0,0依然说明还剩余1位{while (i >= 0){int ret = lhs.strDigit[i] - '0';if (flag)                 //前面加过来可能还有进位,然后当前可能为9,加了1之后又 = 10,又得进位,所以直接复制前面的代码{ret += 1;flag = false;}if (ret >= 10){ret %= 10;flag = true;}result.push_back(ret + '0');i--;	}}//第二个字符串没完else if (j >= 0){while (j >= 0){int ret = lhs.strDigit[j] - '0';if (flag)                 //前面加过来可能还有进位,然后当前可能为9,加了1之后又 = 10,又得进位,所以直接复制前面的代码{ret += 1;flag = false;}if (ret >= 10){ret %= 10;flag = true;}result.push_back(ret + '0');j--;}}//最高位可能也进位if (flag) {result.push_back('1');}reverse(result.begin(), result.end());return result;//result是string类,而不是BigInt,因为隐式转换,p263//因为编译器看到该类的构造函数只接受一个实参(而且是string类的),所以可以触发隐式转换机制,定义string result时编译器也会构建一个BigInt类的临时对象,并把result赋值给他//函数里的return本来就是返回临时对象,这个时候就返回的就是那个BigInt类的临时对象//当然,参考书上的例子,即使后面函数用string类的result,编译器实际传入的也是BigInt的临时对象//如某成员函数定义为func(BigInt& a){...}; 调用时传入string类 func(result);也是合法的
}//大数减法
BigInt operator-(const BigInt& lhs, const BigInt& rhs) {//让大的减小的,如果lhs比rhs小,则让rhs - lhs,然后最后添加负号string result;bool flag = false;bool minor = false;//标记lhs是否和rhs互换了string maxStr = lhs.strDigit;string minStr = rhs.strDigit;if (maxStr.length() < minStr.length()){//互换,让maxStr一直是最长的maxStr = rhs.strDigit;minStr = lhs.strDigit;minor = true;}//长度一样也得比较else if (maxStr.length() == minStr.length()){if (maxStr < minStr){maxStr = rhs.strDigit;//让maxStr是最大的minStr = lhs.strDigit;minor = true;}else if (maxStr == minStr){return string("0");}}int i = maxStr.length() - 1;//i肯定大于等于j,所以后面j会先完int j = minStr.length() - 1;for (; i >= 0 && j >= 0; --i, --j) {int ret = maxStr[i]  - minStr[j];//减法的话,char类型相减就是int型了,不用+‘0’再相减/*if (ret >= 0){result.push_back(ret + '0');} 一定要先看标记,因为被借位的话,当前ret需要减1*/if (flag){ret -= 1;flag = false;}//当前位有可能因为被借位了而减,小于0,所以紧接着判断是否为负if (ret < 0){ret += 10;// 如2 - 6,应该是12 - 6,所以为 2 - 6 + 10 = 6flag = true;}result.push_back(ret + '0');}//肯定是j先完,所以不用再像加法那样判断,而是直接把i多余的处理完while (i >= 0) {int ret = maxStr[i] - '0';if (flag) {ret -= 1;flag = false;}//同样的,ret可能原本是0,被借位了又为-1了if (ret < 0) {ret += 10;flag = true;}result.push_back(ret + '0');i--;}//翻转前先看看末尾有没有0,如1000,否则反转后就是0001while(result.back() == '0'){result.pop_back();}if (minor) {result.push_back('-');}reverse(result.begin(), result.end());return result;
}int main()
{string s1;string A;string s2;getline(cin, s1);getline(cin, A);//把加减号定义为char会报错,getline第二个参数只能是string类型getline(cin, s2);BigInt int1(s1);BigInt int2(s2);if (A == "+") {cout << int1 + int2 << endl;}if (A == "-") {cout << int1 - int2 << endl;}return 0;
}

146 LRU 缓存算法

面筋:
今天面试的时候,面试官告诉我,put 函数里面要先写 if 判断 size 是否到达 cap,然后再添加数据。他的理由是内存可能就是没办法再新加一个元素。

在这里插入图片描述

最近最少使用算法(Least Recently Used,LRU):一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 t,当须淘汰一个页面时,选择现有页面中其 t 值最大的,即最近最少使用的页面予以淘汰。

实现本题的两种操作,需要用到一个哈希表和一个双向链表。在面试中,面试官一般会期望我们能够自己实现一个简单的双向链表,而不是使用语言自带的、封装好的数据结构。

思路:用一个哈希表和一个双向链表维护所有在缓存中的键值对

  • 双向链表按照被使用的顺序存储了这些键值对,靠近头部的键值对是最近使用的,而靠近尾部的键值对是最久未使用的。
  • 哈希表即为普通的哈希映射(HashMap),通过缓存数据的键映射到其在双向链表中的位置。

这样以来,我们首先使用哈希表进行定位,找出缓存项在双向链表中的位置,随后将其移动到双向链表的头部,即可在 O(1)O(1)O(1) 的时间内完成 get 或者 put 操作。具体的方法如下:
对于 get 操作,首先判断 key 是否存在:
如果 key 不存在,则返回 −1-1−1;
如果 key 存在,则 key 对应的节点是最近被使用的节点。通过哈希表定位到该节点在双向链表中的位置,并将其移动到双向链表的头部,最后返回该节点的值。
对于 put 操作,首先判断 key 是否存在:
如果 key 不存在,使用 key 和 value 创建一个新的节点,在双向链表的头部添加该节点,并将 key 和该节点添加进哈希表中。然后判断双向链表的节点数是否超出容量,如果超出容量,则删除双向链表的尾部节点,并删除哈希表中对应的项;
如果 key 存在,则与 get 操作类似,先通过哈希表定位,再将对应的节点的值更新为 value,并将该节点移到双向链表的头部。

上述各项操作中,访问哈希表的时间复杂度为 O(1)O(1)O(1),在双向链表的头部添加节点、在双向链表的尾部删除节点的复杂度也为 O(1)O(1)O(1)。而将一个节点移到双向链表的头部,可以分成「删除该节点」和「在双向链表的头部添加节点」两步操作,都可以在 O(1)O(1)O(1) 时间内完成。

小贴士
在双向链表的实现中,使用一个伪头部(dummy head)和伪尾部(dummy tail)标记界限,这样在添加节点和删除节点的时候就不需要检查相邻的节点是否存在。

struct DLinkedNode {int key, value;DLinkedNode* prev;DLinkedNode* next;DLinkedNode(): key(0), value(0), prev(nullptr), next(nullptr) {}DLinkedNode(int _key, int _value): key(_key), value(_value), prev(nullptr), next(nullptr) {}
};class LRUCache {
private:unordered_map<int, DLinkedNode*> cache;DLinkedNode* head;DLinkedNode* tail;int size;int capacity;public:LRUCache(int _capacity): capacity(_capacity), size(0) {// 使用伪头部和伪尾部节点head = new DLinkedNode();tail = new DLinkedNode();head->next = tail;tail->prev = head;}int get(int key) {if (!cache.count(key)) {return -1;}// 如果 key 存在,先通过哈希表定位,再移到头部DLinkedNode* node = cache[key];moveToHead(node);return node->value;}void put(int key, int value) {if (!cache.count(key)) {// 如果 key 不存在,创建一个新的节点DLinkedNode* node = new DLinkedNode(key, value);// 添加进哈希表cache[key] = node;// 添加至双向链表的头部addToHead(node);++size;if (size > capacity) {// 如果超出容量,删除双向链表的尾部节点DLinkedNode* removed = removeTail();// 删除哈希表中对应的项cache.erase(removed->key);// 防止内存泄漏delete removed;--size;}}else {// 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部DLinkedNode* node = cache[key];node->value = value;moveToHead(node);}}void addToHead(DLinkedNode* node) {node->prev = head;node->next = head->next;head->next->prev = node;head->next = node;}void removeNode(DLinkedNode* node) {node->prev->next = node->next;node->next->prev = node->prev;}void moveToHead(DLinkedNode* node) {removeNode(node);addToHead(node);}DLinkedNode* removeTail() {DLinkedNode* node = tail->prev;removeNode(node);return node;}
};

460 LFU 缓存算法

最不经常使用算法(LFU):这个缓存算法使用一个计数器来记录条目被访问的频率。通过使用LFU缓存算法,最低访问数的条目首先被移除。这个方法并不经常使用,因为它无法对一个拥有最初高访问率之后长时间没有被访问的条目缓存负责。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/4223.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软考A计划-系统集成项目管理工程师-项目整体管理-上

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff…

SurfaceFlinge/InputFlinger分析-android画面缩放后依然点击正常原理分析

hi&#xff0c;粉丝朋友们&#xff1a; 这两天刚好在做自由窗口相关国内需求&#xff0c;刚好遇到一个疑惑&#xff0c;那就是画面进行缩放后发现依然触摸画面可以正常反映问题。 具体疑惑背景 疑问点如下&#xff1a; 坐标是针对屏幕的&#xff0c;按钮也是相对Activity的&…

深蓝学院C++基础与深度解析笔记 第 9 章 序列与关联容器

第 9 章 序列与关联容器 1. 容器概述 A、容器&#xff1a; 一种特殊的类型&#xff0c;其对象可以放置其它类型的对象&#xff08;元素&#xff09; – 需要支持的操作&#xff1a;对象的添加、删除、索引、遍历 – 有多种算法可以实现容器&#xff0c;每种方法各有利弊B、容…

Redis的高可用与持久化

目录 一、Redis 高可用1. 持久化2. 主从复制3. 哨兵4. 集群(cluster) 二、Redis 持久化方式1. 持久化的功能2. 持久化的方式 三、RDB 持久化1. 触发条件2.执行流程3. 启动时加载 四、AOF持久化1.开启 AOF2. 执行流程2.1 命令追加2.2 文件写入&#xff08;write&#xff09;和文…

【机器学习】——续上:卷积神经网络(CNN)与参数训练

目录 引入 一、CNN基本结构 1、卷积层 2、下采样层 3、全连接层 二、CNN参数训练 总结 引入 卷积神经网络&#xff08;CNN&#xff09;是一种有监督深度模型框架&#xff0c;尤其适合处理二维数据问题&#xff0c;如行人检测、人脸识别、信号处理等领域&#xff0c;是带…

threejs动画

个人博客地址: https://cxx001.gitee.io 前面我们所用的模型大都是静态的&#xff0c;没有动画&#xff0c;没有生命。这节我们将赋予它们生命。 动画本质是通过改变物体的旋转、缩放、位置、材质、顶点、面以及其它你所能想到的属性来实现的。这些其实在前面章节示例里或多或…

win11,win10睡眠自动被唤醒部分总结

网上查了很多的解决方法&#xff0c;试了关闭 启用快速启动&#xff0c;大致有几点 1. powercfg /lastwake cmd命令行输入powercfg /lastwake&#xff0c;可以查询最后一次被哪个设备唤醒 2. PowerCfg -DEVICEQUERY wake_armed 也是命令行输入&#xff0c;查询所有可以唤醒电…

基于Thinkphp6框架全新UI的AI网址导航系统源码

2023全新UI的AI网址导航系统源码&#xff0c;基于thinkphp6框架开发的 AI 网址导航是一个非常实用的工具&#xff0c;它能够帮助用户方便地浏览和管理自己喜欢的网站。 相比于其他的 AI 网址导航&#xff0c;这个项目使用了更加友好和易用的 ThinkPHP 框架进行搭建&#xff0c…

学习系统编程No.25【核心转储实战】

引言&#xff1a; 北京时间&#xff1a;2023/6/16/8:39&#xff0c;实训课中&#xff0c;大一下学期最后有课的一天&#xff0c;还有两天就要期末考啦&#xff01;目前什么都还没有复习&#xff0c;不到星期天晚上&#xff0c;咱不慌&#xff0c;小小挂科&#xff0c;岂能拦得…

系列二、Maven下载安装配置

一、下载 链接&#xff1a;https://pan.baidu.com/s/1BvwLzAk9kRSP-daxSYe4Vw?pwdyyds 提取码&#xff1a;yyds 二、安装 第一步&#xff1a;下载安装包 第二步&#xff1a;解压至安装目录&#xff0c;例如 第三步&#xff1a;配置settings.xml&#xff08;主要配置maven本…

STM32F407 滴答定时器

介绍STM32F407滴答定时器配置方法、使用方式&#xff0c;封装延时函数得到精确的时间。 【1】介绍滴答定时器的章节 STM32F407参考手册中第10章介绍了滴答定时器的校准值。 M4权威指南介绍滴答定时器的章节&#xff0c;M3权威指南中与M4权威指南中的介绍一样。 【2】滴答定时…

Linux--打印内容或者插入内容:echo

语法&#xff1a; echo 内容 作用&#xff1a;打印内容到显示器echo 内容 > 不存在的文件 作用&#xff1a;文件创建&#xff0c;并将内容插入新创建的文件中echo 内容 > 存在的文件 作用&#xff1a;覆盖文件原有的内容 echo 内容 >> 存在的文件 作用&#xff1a…