2.3.4 树
遍历:前中后序,宽度优先。
二叉树的特例:二叉搜索树、堆(最大堆和最小堆,用于找最值)、红黑树(c++ STL中的很多数据结果就是基于这实现的);
题7-重建二叉树:递归,设置四个位点;
1 class Solution { 2 public: 3 TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) { 4 if(pre.size()!=vin.size()||vin.size()==0) return nullptr; 5 return Construct(0,pre.size()-1,0,vin.size()-1,pre,vin); 6 } 7 8 TreeNode* Construct(int prestart,int preend,int vinstart,int vinend,const vector<int> &pre,const vector<int> &vin){ 9 if(prestart>preend) return nullptr; 10 TreeNode* root=new TreeNode(pre[prestart]); 11 for(int i=vinstart;i<=vinend;i++) 12 if(pre[prestart]==vin[i]){ 13 root->left=Construct(prestart+1,prestart+i-vinstart,vinstart,i-1,pre,vin); 14 root->right=Construct(prestart+i-vinstart+1,preend,i+1,vinend,pre,vin); 15 break; 16 } 17 return root; 18 } 19 };
题8-二叉树的下一个节点
1 class Solution { 2 public: 3 TreeLinkNode* GetNext(TreeLinkNode* pNode) 4 { 5 if(pNode==nullptr) return nullptr; 6 if(pNode->right!=nullptr){ 7 pNode=pNode->right; 8 while(pNode->left!=nullptr){ 9 pNode=pNode->left; 10 } 11 return pNode; 12 } 13 else{ 14 if(pNode->next==nullptr) 15 return nullptr; 16 else{ 17 if(pNode==pNode->next->left) 18 return pNode->next; 19 else{ 20 while(pNode->next!=nullptr){ 21 if(pNode==pNode->next->left) 22 return pNode->next; 23 pNode=pNode->next; 24 } 25 return nullptr; 26 } 27 } 28 } 29 } 30 };
2.3.5 栈和队列
题9-两个栈实现队列:一个用于插入,一个用于删除,增加判空操作,每次插入/删除操作后只有一个栈是有数据的;
2.4 算法和数据结构
递归/循环,排序/查找,搜索路径(回溯法),最优解(动态规划),贪心,位运算(与、或、异或、左右移)
2.4.1 递归和循环
如果面试官没有要求,多采用递归;递归存在时间效率和调用栈溢出的问题;
题10-斐波那契数列:递归效率低,采用循环O(n),也可以用到数学公式用递归O(logn),青蛙跳和格子都是这个问题;
2.4.2 查找和排序
顺序查找、二分、哈希表查找、二叉排序树
交换使用swap函数,使用随机数配合递归来进行快排;
面试官的交流:要问排序的是什么数据、数据量,能用多少辅助空间?明确场景
题11-旋转数组的最小数字:O(n)到O(logn),使用二分,但是要有特殊处理,例如如果两个位点元素相等,则内部的应该进行顺序查找;
class Solution { public: int minNumberInRotateArray(vector<int> rotateArray) { if(rotateArray.size()==0) return 0; int low=0,high=rotateArray.size()-1,mid=(low+high)/2; if(rotateArray[low]<rotateArray[high]) return rotateArray[low]; if(high<2) return rotateArray[high]; while(low!=high-1){ if(rotateArray[low]<rotateArray[high]) return rotateArray[low]; if(rotateArray[low]==rotateArray[mid]&&rotateArray[mid]==rotateArray[high]){ int min=rotateArray[low]; for(int i=low+1;i<=high;i++) if(min>rotateArray[i]) min=rotateArray[i]; return min; } if(rotateArray[low]>rotateArray[mid]){ //low=low+1; high=mid; mid=(low+high)/2; } if(rotateArray[high]<rotateArray[mid]){ low=mid; mid=(low+high)/2; } } return rotateArray[low+1]; } };
2.4.3 回溯法
适合用递归实现,其实也可以用堆栈。
题13-机器人的运动规划:本来觉得两轮遍历就可以找到不可以到达的格子,后来发现是不对的,所以还是用了回溯去找
class Solution { public: int movingCount(int threshold, int rows, int cols) { if(threshold<=0||rows<=0||cols<=0) return 0; bool *visited=new bool[rows*cols]; for(int i=0;i<rows*cols;i++) visited[i]=false; int res=getCount(rows,cols,0,0,threshold,visited); delete[] visited; return res; } int getCount(int rows,int cols,int row,int col,int threshold,bool* visited){ int count=0; if(!visited[row*cols+col]&&(getSum(row,col)<=threshold)&&row>=0&&col>=0&&row<rows&&col<cols){ visited[row*cols+col]=true; count=1+getCount(rows,cols,row-1,col,threshold,visited)+getCount(rows,cols,row+1,col,threshold,visited)+getCount(rows,cols,row,col-1,threshold,visited)+getCount(rows,cols,row,col+1,threshold,visited); } return count; } int getSum(int rows,int cols){ int sum=0; while(rows>0||cols>0){ sum+=(rows%10+cols%10); rows/=10; cols/=10; } return sum; } };
2.4.4 动态规划与贪婪算法
动规:
- 分解成很多子问题;
- 整体的问题依赖于子问题
- 子问题之间还有相互重叠的更小的子问题
从上往下分析,从下往上求解(顺序先计算出小问题的最优解并存储下来)
题-剪绳子:使用动态规划来做(两个循环 O(n方));用贪婪法(o(1)的时间和空间,需要证明);
2.4.5 位运算
与、或、异或、左右移(计算效率比乘除高);
注意右移时如果是负数的话补的是1,还有把整数减去1之后与原来的数做位与运算后结果相当于把整数的二进制表示中最右边的1变为0,很多二进制问题可以用这个套路;
题-二进制中1的个数
class Solution { public: int NumberOf1(int n) { int count=0; while(n){ count++; n=n&(n-1); } return count; } };