gogogo!
面对恐惧最好的办法就是面对它,战胜它!奥里给!
在leetcode官网上刷
面试题51.数组中的逆序对
一开始的想法自然而然想要暴力解,想冒泡一样两层循环对比
class Solution {
public:
int reversePairs(vector<int>& nums) {
int count=0;
for(int i=0;i<=nums.size()-2;++i)//从第一个序号到倒数第二个序号
{
for(int j=i+1;j<=nums.size()-1;++j)//从后一个位置遍历到最后,比较i跟j
{
if(nums[j]<nums[i])
{
++count;
}
}
}
return count;
}
};
超时= =
官方思路:使用归并排序,阶段性统计逆序数
来日自己写
面试题03.数组中重复的数字
要求只是找出任意重复的数字,而且数字大小范围都在0-n-1,这样难度就偏低了。
class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
vector<int>book(nums.size(),0);
for(int i=0;i<=nums.size()-1;++i)
{
++book[nums[i]];
}
for(int i=0;i<=book.size()-1;i++)
{
if(book[i]>1)
{
return i;
}
}
return 0;
}
};
利用桶排序,不管了,下一道
面试题04.二维数组中的查找
看到这道题第一反应就是用搜索,DFS和BFS都可以,可能最近搜索看的多吧。。
class Solution {
void DFS(vector<vector<int>>& matrix,int x,int y,int target,int &logo,vector<vector<int>> direction)
{
if(x>matrix.size()-1||y>(matrix[0].size()-1))
{
return;
}
if(matrix[x][y]==target)
{
logo=1;
}
for(int i=0;i<=1;i++)//可能的两种方向
{
x=x+direction[i][0];
y=y+direction[i][1];
DFS(matrix,x,y,target,logo,direction);
}
return ;
}
public:
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
vector<vector<int>> direction={{1,0},{0,1}};
int logo=0;
if(matrix.size()<1||matrix[0].size()<1)
return false;
DFS(matrix, 0,0,target,logo,direction);
if(logo==1)
{
return true;
}
return false;
}
};
上述代码是有错的,错误的原因是x和y是和当时状态绑定的坐标,然而我却让它成为一个随程序运行变化的坐标,应该用类似栈的结构来存这个坐标。
所以我们应该在每次的for循环之后后退一步让它返回原来的位置(相当于它此时此地就是这个状态,无数种可能性中的一环),其实还应该增加一个map记录是否曾经访问用于优化。
岛屿着色法为毛不用回退?
因为它可以一次性涂到底涂完一个岛,最后return一次就够了。而我们这里显然是不行的。
总而言之,只要是多次return找目标的,一定要回退!
修改之后:
结果应该是对的,但是超时
class Solution {
void DFS(vector<vector<int>>& matrix,int x,int y,int target,int &logo,vector<vector<int>> direction)
{
if(x>matrix.size()-1||y>(matrix[0].size()-1))
{
return;
}
if(matrix[x][y]==target)
{
logo=1;
}
for(int i=0;i<=1;i++)//可能的两种方向
{
x=x+direction[i][0];
y=y+direction[i][1];
if(x<=matrix.size()-1&&y<=(matrix[0].size()-1))
{
DFS(matrix,x,y,target,logo,direction);
}
x=x-direction[i][0];
y=y-direction[i][1];
}
return ;
}
public:
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
vector<vector<int>> direction={{1,0},{0,1}};
int logo=0;
if(matrix.size()<1||matrix[0].size()<1)
return false;
DFS(matrix, 0,0,target,logo,direction);
if(logo==1)
{
return true;
}
return false;
}
};
直接暴力解给过了,唉
class Solution {
public:
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
if(matrix.size()<1||matrix[0].size()<1)
return false;
for(int i=0;i<=matrix.size()-1;++i)
{
for(int j=0;j<=matrix[0].size()-1;++j)
{
if(matrix[i][j]==target)
{
return true;
}
}
}
return false;
}
};
方法2:
若数组为空,返回 false
初始化行下标为 0,列下标为二维数组的列数减 1
重复下列步骤,直到行下标或列下标超出边界
获得当前下标位置的元素 num
如果 num 和 target 相等,返回 true
如果 num 大于 target,列下标减 1
如果 num 小于 target,行下标加 1
可以证明这种方法不会错过目标值。如果当前元素大于目标值,说明当前元素的下边的所有元素都一定大于目标值,因此往下查找不可能找到目标值,往左查找可能找到目标值。如果当前元素小于目标值,说明当前元素的左边的所有元素都一定小于目标值,因此往左查找不可能找到目标值,往下查找可能找到目标值。
(若是这样左下角和右上角都是可以的)
面试题05.替换空格
leetccode要注意特殊情况
class Solution {
public:
string replaceSpace(string s) {
if(s=="")
{
return s;
}
string copy="";
for(int i=0;i<=s.size()-1;++i)
{
if(s[i]==' ')
{
copy+='%';
copy+='2';
copy+='0';
}
else
{
copy+=s[i];
}
}
return copy;
}
};
面试题06.从尾到头打印链表
看到这个理所当然想到栈,先进后出.
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
vector<int> reversePrint(ListNode* head) {
vector<int>stack;int top=0;
vector<int>stackcopy;
ListNode*p;
p=head;
while(p!=NULL)
{
stack.push_back(p->val);
top++;
p=p->next;
}
for(int j=stack.size()-1;j>=0;--j)
{
stackcopy.push_back(stack[j]);
}
return stackcopy;
}
};
不过这个不算多快,也不会翻转链表。
一个利用递归的例子,利用了后调用先return:
真牛逼,记住了
class Solution {
public:
vector<int> reversePrint(ListNode* head) {
if(!head)
return {};
vector<int> a=reversePrint(head->next);
a.push_back(head->val);
return a;
}
};
面试题07.重建二叉树
好像是两种遍历可以确定一棵树来着,可是不记得了,直接看解法!!
思路是这样:
前序遍历的首个元素即为根节点 root 的值;
在中序遍历中搜索根节点 root 的索引 ,可将中序遍历划分为
[ 左子树 | 根节点 | 右子树 ] 。
根据中序遍历中的左(右)子树的节点数量,可将前序遍历划分为
[ 根节点 | 左子树 | 右子树 ] 。
同样的办法继续对左右子树继续划分(递归),就OK了
划分(vector1,vector2,left,right)
{
得到root值
得到root在中序中坐标index
将root值添加进树怎么操作是个问题。。
划分(vector1,vector2,left,index-1)//划分左子树
划分(vector,index+1,right)//划分右子树
}
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
//递归分治
return recursionBuild(preorder.begin(),preorder.end(),inorder.begin(),inorder.end());
}
//递归分治
TreeNode* recursionBuild(vector<int>::iterator preBegin, vector<int>::iterator preEnd,vector<int>::iterator inBegin, vector<int>::iterator inEnd )
{
if(inEnd==inBegin) return NULL;
TreeNode* cur = new TreeNode(*preBegin);
auto root = find(inBegin,inEnd,*preBegin);
cur->left = recursionBuild(preBegin+1,preBegin+1+(root-inBegin),inBegin,root);
cur->right = recursionBuild(preBegin+1+(root-inBegin),preEnd,root+1,inEnd);
return cur;
}
};
面试题11:旋转数组的最小数字
为毛要旋转,不能直接找最小数字?
用的是直接找,也可以用其他办法,比如二分查找优化效率,懒得看了。
整体思路是不断缩小搜索的范围。
面试题12. 矩阵中的路径
这道题一看到立马想到用深度搜索,但是不管怎么样复杂度都太高了,这次的终止条件是字符串匹配成功而不是找到某个点。这样是不是最好新定义一个字符串跟它对比。
自己的思路有点乱,就算最后做出来也是千疮百孔,看看别人的吧。
思路:
深度优先搜索: 可以理解为暴力法遍历矩阵中所有字符串可能性。DFS 通过递归,先朝一个方向搜到底,再回溯至上个节点,沿另一个方向搜索,以此类推。
剪枝: 在搜索中,遇到 这条路不可能和目标字符串匹配成功 的情况(例如:此矩阵元素和目标字符不同、此元素已被访问),则应立即返回,称之为 可行性剪枝 。
递归参数:
当前元素在矩阵 board 中的行列索引 i 和 j ,
当前目标字符在 word 中的索引 k 。
终止条件:
返回 false
false : ① 行或列索引越界 或 ② 当前矩阵元素与目标字符不同 或 ③ 当前矩阵元素已访问过 (③ 可合并至 ② ) 。
返回 true
true : 字符串 word 已全部匹配,即 k = len(word) - 1 。
递推工作:
标记当前矩阵元素: 将 board[i][j] 值暂存于变量 tmp ,并修改为字符 ‘/’ ,代表此元素已访问过,防止之后搜索时重复访问。
搜索下一单元格: 朝当前元素的 上、下、左、右 四个方向开启下层递归,使用 或 连接 (代表只需一条可行路径) ,并记录结果至 res 。
还原当前矩阵元素: 将 tmp 暂存值还原至 board[i][j] 元素。
回溯返回值: 返回 res ,代表是否搜索到目标字符串。
贴一份java的版本
class Solution {
public boolean exist(char[][] board, String word) {
char[] words = word.toCharArray();
for(int i = 0; i < board.length; i++) {
for(int j = 0; j < board[0].length; j++) {
if(dfs(board, words, i, j, 0)) return true;
}
}
return false;
}
boolean dfs(char[][] board, char[] word, int i, int j, int k) {
if(i >= board.length || i < 0 || j >= board[0].length || j < 0 || board[i][j] != word[k]) return false;
//如果等于word下一个元素就继续下一步了
if(k == word.length - 1) return true;
char tmp = board[i][j];
board[i][j] = '/';
boolean res = dfs(board, word, i + 1, j, k + 1) || dfs(board, word, i - 1, j, k + 1) ||
dfs(board, word, i, j + 1, k + 1) || dfs(board, word, i , j - 1, k + 1);
board[i][j] = tmp;//回溯很重要!!除了着色法只有一次return不需要回溯,其他都需要,像这里就算每次不符合条件或者符号条件return true或者false,找一条路径的都需要回溯状态
return res;
}
}
//这份代码没有另外定义bool[][]以及用||符号剪枝值得学习
class Solution {
public:
bool exist(vector<vector<char>>& board, string word) {
if(board.size() == 0) return false;
for (int i=0;i<board.size();i++){
for(int j=0;j<board[0].size();j++){
if (dfs(board,word,i,j,0)){
return true;
}
}
}
return false;
}
bool dfs(vector<vector<char>>& board, string& word, int i,int j,int length){
if(i>=board.size()||j>=board[0].size()||i<0||j<0||length>=word.size()||word[length]!=board[i][j]){
return false;
}
if(length==word.size()-1&&word[length]==board[i][j]){
return true;
}
char temp=board[i][j];//其实每一层都存了一个temp留待之后回溯
board[i][j]='0';//之后访问到也是不会等于字符串里的东西的
bool flag=dfs(board,word,i,j+1,length+1)||dfs(board,word,i,j-1,length+1)||dfs(board,word,i+1,j,length+1)||dfs(board,word,i-1,j,length+1);
board[i][j]=temp;//只有当上面return true后者false会执行这一步
return flag;
}
};
//相当于上一份代码C++版本
学习的点:
这个将字符设置为‘0’然后之后重置省去了bool[][]数组的空间。
这个C++版本,想法跟我基本是一样,
在board中找到一个位置,使得board[i][j] == word[0],可以作为搜索的入口
由于一个格子不能重复进入,因此需要定义一个visit数组,保证每个格子只进入一次
找到一个可行解即返回true。若该次搜索返回false,那么进行步骤1.,找到下一个可行的入口,进入下一次搜索
直到遍历完整个board,仍没有搜索到目标路径,返回false
class Solution {
public:
vector<vector<int>> dxy = {{-1, 0}, {1, 0}, {0, 1}, {0, -1}};
int rows, cols;
bool dfs(vector<vector<char>>& board, vector<bool>& visit, int i, int j, string& word, int idx){
if(board[i][j] != word[idx]) return false;
visit[i*cols+j] = true;
idx++;
for(auto xy : dxy){
int x = xy[0] + i;
int y = xy[1] + j;
if(x < 0 || x >= rows || y < 0 || y >= cols || visit[x*cols+y]) continue;
else{
if(dfs(board, visit, x, y, word, idx)) return true;
}
}
visit[i*cols+j] = false;
if(idx == word.size()) return true;
return false;
}
bool exist(vector<vector<char>>& board, string word) {
if(word == "") return false;
rows = board.size();
cols = board[0].size();
vector<bool> visit(rows * cols, false);
for(int i = 0;i < rows; i++){
for(int j = 0; j < cols; j++){
if(board[i][j] == word[0]){
if(dfs(board, visit, i, j, word, 0)) return true;
}
}
}
return false;
}
};
学习的点:
定义bool只需要定义一维数组就可以
总结一下DFS要点:
1.终止搜索的条件,剪枝 return false或者是达到目的,返回ture
2.回溯,DFS之后紧跟着返回上一步的状态。
面试题13 机器人的运动范围
贴一个自己写的有问题的
class Solution {
public:
vector<vector<int>>dir={{0,1},{1,0},{-1,0},{0,-1}};
void DFS(int x,int y,int m,int n,int k,int&count,vector<bool>& visit)
{ int x1=x/100;
int x2=(x-x1*100)/10;
int x3=x-x1*100-x2*10;
int sumx=x1+x2+x3;
int y1=y/100;
int y2=(y-y1*100)/10;
int y3=y-y1*100-y2*10;
int sumy=y1+y2+y3;
if(sumx+sumy<=k)//满足条件count++
{
visit[x*n+y]=true;
count++;
}
for(int i=0;i<=3;i++)
{
x+=dir[i][0];
y+=dir[i][1];
if(x<0||y<0||x>m-1||y>n-1||visit[x*n+y]==true)
{
continue;
}
DFS(x,y,m,n,k,count,visit);
}
return;
}
int movingCount(int m, int n, int k) {
vector<bool> visit(m*n,false);
int count=1;//记录能够达到的格子
DFS(0,0,m,n,k,count,visit);
return count;
}
};
一直一直都是他妈的overflow
网友的
class Solution {
public:
int s=0;
int movingCount(int m, int n, int k) {
vector<vector<int>>v(m,vector<int>(n));
dfs(0,0,m,n,k,v);
return s;
}
void dfs(int x,int y,int m,int n,int k,vector<vector<int>>& v){
if(x>=m||y>=n||x<0||y<0||x/10+x%10+y/10+y%10>k||v[x][y]==1)return ;
s++;
v[x][y]=1;
dfs(x+1,y,m,n,k,v);
dfs(x,y+1,m,n,k,v);
}
};
//