LeetCode之路

  • 热题HOT100
    • 双指针
      • 53 最大(连续)子序和
      • 11 成水最多的容器
      • 15 三数之和
      • 75 颜色分类
      • 283 移动零
      • 42 接雨水
    • 哈希表
      • 1 两数之和
      • 20 有效的括号
      • 3 无重复的最长子串
      • 287 寻找重复数
      • 136 只出现一次的数字
      • 49 字母异位词分组
      • 560 和为k的子数组
      • 1737 满足三条件之一需改变的最少字符数
      • 两个大数据集找交集(阿里面试)
      • 739 每日温度
      • 155 最小栈
      • 394 字符串解码
      • int型整数逆序(华为笔试)
      • 用两个栈实现队列(阿里面试题)
      • 347 前k个高频元素
      • 215 数组中的第K个最大元素
    • 贪心
      • 621 任务调度器
      • 55 跳跃游戏
      • 406 根据身高重建队列
    • 排序
      • 56 合并区间
      • 定义
      • 建树
      • 617 合并二叉树
      • 226 翻转二叉树
      • 104 二叉树的最大深度
      • 101 对称二叉树
      • 543 二叉树的直径
      • 102 二叉树的层序遍历
      • 103 二叉树锯齿层序遍历(deque)
      • 94 二叉树的中序遍历
      • 114 二叉树展开为链表(单向)
      • 将树(中序遍历)转化成双向链表(字节面试)
      • 105 从前序与中序遍历序列构造二叉树
      • 98 验证二叉搜索树
      • 236 二叉树的最近公共祖先
    • 链表
      • 206 反转链表
      • 2 两数相加
      • 21 合并两个有序链表
      • 148 排序链表
      • 19 删除链表的倒数第 N 个结点
      • 141 环形链表
      • 142 环形链表2
      • 287 快慢指针
      • 25 K个一组翻转链表
      • 160 相交链表
    • BFS
      • 200 岛屿数量
    • DFS
      • 200 岛屿数量
    • DP
      • 70 爬楼梯
      • 5 最长回文子串
      • 121 买卖股票的最佳时机
      • 300 最长递增子序列
      • 322 零钱兑换(需要后续进一步理解)
      • 面试题 08.11. 硬币(需要后续进一步理解)
      • 887 鸡蛋掉落(阿里面试)
    • 二分查找
      • 6010
      • 4 寻找两个正序数组的中位数
      • 33 搜索旋转排序数组
    • 字符串
      • 28 KMP 实现 strStr()
      • 1143 最长公共子序列
      • 最长公共子串
      • 14 最长公共前缀
      • 大数加法 NC1
      • 字符串中的数排序 美团笔试题
    • map
      • 特征提取 字节2019春招题
    • 数学推导
      • 1411 给 N x 3 网格图涂色的方案数(阿里笔试)
    • 图论
      • 815 公交线路(阿里笔试)
      • 329 矩阵中的最长递增路径 再看一下
      • 记忆化搜索 美团笔试
    • 设计
      • 146 LRU缓存机制

热题HOT100

双指针

53 最大(连续)子序和

做的时候觉得是一个贪心,即如果当前想加的下一个结点>=0且当前子串和<0,则放弃前面的子串,把头指针指到此点。再做一些特判(vector长度为1,全为负值)

最后发现其实是个dp问题

定义dp[i]为:(起始设为1,不然怪怪的) 在1—i 这个串中,所有以i为结尾的子串中的最大子串和,即 1—i/2—i/3—i等等等,这个串可能不是1—i这个串中的最大字串和,但是就是这么定义,因为只有这样dp[i]才能跟第i+1的节点联系上,才好使用dp。

dp[i+1]=max(dp[i],0)+第i+1节点值

结果:dp[1],dp[2],dp[3]…dp[n]中的max

//双指针+贪心(类似dp)+不断放弃<0class Solution {public:int maxSubArray(vector<int>& nums) {int i=0; //头指针int j=0; //尾指针int maxv=nums[0];//当前最大和int curv=nums[0];//当前和if(nums.size()==1)//特判{return nums[0];}int maxx=nums[0];for(int p=0;p<nums.size();p++) //特判全负数{maxx=(maxx>=nums[p])?maxx:nums[p];}while(j<nums.size()-1){if(nums[j+1]>=0&&curv<0) //放弃前面的{j++;curv=nums[j];i=j;}else //不放弃{j++;curv+=nums[j];}maxv=(curv>maxv)?curv:maxv;}return max(maxv,maxx);}
};

dp解法

class Solution {public:int maxSubArray(vector<int>& nums) {if(nums.size()==1) return nums[0];;vector<int> dp(nums.size());dp[0]=nums[0];int maxv=nums[0];for(int i=1;i<nums.size();i++){dp[i]=max(dp[i-1],0)+nums[i];maxv=(dp[i]>maxv)?dp[i]:maxv;}return maxv;}
};

11 成水最多的容器

双指针,数学可证

//两侧双指针居中移动 每次都移动较短的边 数学可证
class Solution {public:int maxArea(vector<int>& height) {int i=0;int j=height.size()-1;int curv=0;int maxv=min(height[i],height[j])*(j-i);while(i!=j){if(height[i]>=height[j])j--;else i++;curv=min(height[i],height[j])*(j-i);maxv=(curv>maxv)?curv:maxv;}return maxv;}
};

15 三数之和

1.排序 2.外层确定第一个元素进行遍历 3.内层另两个元素用双指针确定 4.最后去重

//排序+去重+双指针
//双指针:在过程中比较,决定移动哪个指针
class Solution {public:vector<vector<int>> threeSum(vector<int>& nums) {vector<vector<int>> res; //输出sort(nums.begin(),nums.end());int n = nums.size();if(n<3)return res;for(int i=0;i<=n-3;i++){int j=i+1; //双指针int k=n-1;if(nums[i]==0&&nums[j]==0&&nums[k]==0){res.push_back({nums[i],nums[j],nums[k]});break;}//特判if(nums[i]>0)break;  //特判while(j!=k){if(nums[i]+nums[j]+nums[k]==0){res.push_back({nums[i],nums[j],nums[k]});j++;}else if(nums[i]+nums[j]+nums[k]<0)j++;else k--;}}sort(res.begin(),res.end());//去重res.resize(unique(res.begin(),res.end())-res.begin());return res;}
};

利用hash
1.构建hash表,统计每个元素的出现次数
2.特判[0,0,0]
3.特判[a,a,b]
4.去重
5.两层循环定两个元素,另外一个元素用hash查
6.结果去重

75 颜色分类

//双指针(三)+扫描一遍
//p0指针:初始指向开头,过程中指向0的末尾的下一个
//p2指针:初始指向结尾,过程中指向2的开端的前一个
//i指针:用于遍历
//当i遍历到0时,与p0指向的元素swap,同时p0++,i++
//当i遍历到2时,与p2指向的元素swap,同时p2--,i不变
//上述操作i变化不同的原因是,i和p0都是从左向右遍历,所以当nums[i]与nums[p0]交换后,当前的nums[i](之前的nums[p0])一定不是0/2,因为i已经遍历过该部分,已经处理过;另一方面,也要保证i>=p0(不然会出bug),综上所述,i++。相反,p2之前指向的元素没有处理过,所以交换后不能i++。
//当i遍历到1时,i++
class Solution {public:void sortColors(vector<int>& nums) {int p0=0;int p2=nums.size()-1;int i=0;while(i<=p2){if(nums[i]==0) swap(nums[i++],nums[p0++]);else if(nums[i]==2) swap(nums[i],nums[p2--]);else i++;}}
};

283 移动零

//双指针(一个扫描,一个标定位置)
//px指针:初始指向开头,过程中指向非零元素的下一个
//i指针:用于遍历
//nums[i]==0时,i++
//nums[i]!=0时,nums[i]与nums[px]进行swap,i++,px++
class Solution {public:void moveZeroes(vector<int>& nums) {int px=0;for(int i=0;i<nums.size();i++)if(nums[i]!=0) swap(nums[i],nums[px++]);   }
};

42 接雨水

/*
双指针
用两个指针把每一轮想计算的部分圈起来
p0:本轮计算起始点
p1:本轮计算结束点
定位每一轮的p0:遍历寻找第一个nums[p0]>nums[p0+1]的p0;初始p1=p0+1
定位每一轮的p1的两种情况:(a)nums[p1]>=nums[p0] 直接圈起 (b)nums[p1]<nums[p0]的所有p1中选择max(nums[p1])对应的p1
计算:画图看(减法)
特殊情况:若p1==p0+1,则需要将p1后移一位,重新开始下一轮
*/
class Solution {public://计算每一轮的雨水量int perTrap(int p0,int p1,vector<int>& nums){int res1=min(nums[p0],nums[p1])*(p1-p0-1);int res2=0;int maxSub=min(nums[p0],nums[p1]);for(int i=p0+1;i<p1;i++)res2+=nums[i];return res1-res2;}int trap(vector<int>& nums) {if(nums.size()<3) return 0;int p0=0;int p1=0;int res=0;int mark=true;while(p1!=nums.size()-1){//定位该轮p0,p1初始位置for(int i=p0;i<nums.size()-1;i++){if(nums[i]!=0 && nums[i+1]<nums[i]){p0=i;p1=i+1;break;}if(i==nums.size()-2)mark=false; //本轮无雨水能积累}if(mark==false)break;//无雨水积累,直接跳出循环//定位新一轮p1的位置int curp1=p1;int bestp1=p1;while(curp1<nums.size()){if(nums[curp1]>=nums[p0]){bestp1=curp1;break;}else if(nums[curp1]>0){bestp1=(nums[curp1]>=nums[bestp1])?curp1:bestp1;curp1++;}//为0else curp1++; }p1=bestp1;//特判:如果p1没更新,即p1==p0+1。该情况为p0,p1相邻,此时需要将p0后移一个,重新开始下一轮if(p1==p0+1) p0++;else {res+=perTrap(p0,p1,nums);p0=p1;//将p0指向p1,开始下一轮}}return res;}
};

优化做法:初始 p1=p0+2 && 雨水计算部分减法需要注意(maxSub)

/*
双指针
用两个指针把每一轮想计算的部分圈起来
p0:本轮计算起始点
p1:本轮计算结束点
定位每一轮的p0:遍历寻找第一个nums[p0]>nums[p0+1]的p0;初始p1=p0+2
定位每一轮的p1的两种情况:(a)nums[p1]>=nums[p0] 直接圈起 (b)nums[p1]<nums[p0]的所有p1中选择max(nums[p1])对应的p1
计算:画图看(减法)
*/
class Solution {public://计算每一轮的雨水量int perTrap(int p0,int p1,vector<int>& nums){int res1=min(nums[p0],nums[p1])*(p1-p0-1);int res2=0;int maxSub=min(nums[p0],nums[p1]);for(int i=p0+1;i<p1;i++){if(nums[i]>maxSub) res2+=maxSub; //防止减去了超过边界的值else res2+=nums[i];}return res1-res2;}int trap(vector<int>& nums) {if(nums.size()<3) return 0;int p0=0;int p1=0;int res=0;int mark=true;while(p1!=nums.size()-1){//定位该轮p0,p1初始位置for(int i=p0;i<nums.size()-2;i++){if(nums[i]!=0 && nums[i+1]<nums[i]){p0=i;p1=i+2;break;}if(i==nums.size()-3)mark=false; //本轮无雨水能积累}if(mark==false)break;//无雨水积累,直接跳出循环if(p0>=nums.size()-2)break;//无雨水积累(针对没进入for的部分)//定位新一轮p1的位置int curp1=p1;int bestp1=p1;while(curp1<nums.size()){if(nums[curp1]>=nums[p0]){bestp1=curp1;break;}else if(nums[curp1]>0){bestp1=(nums[curp1]>=nums[bestp1])?curp1:bestp1;curp1++;}//为0else curp1++; }p1=bestp1;if(p1==p0+1)//一直为0的特判;一直为0,p1没更新,所以还是p0+1break;res+=perTrap(p0,p1,nums);p0=p1;//将p0指向p1,开始下一轮}return res;}
};

数学解法 时间复杂度O(n)

class Solution {//左侧遍历+右侧遍历-全部体积-柱子体积public:int trap(vector<int>& height) {int n=height.size();int max1=0,max2=0,max3=0,max4=0;int x1=0,x2=0,x3=0,x4=0;for(int i=0;i<n;i++){max1=(height[i]>max1)?height[i]:max1;max2=(height[n-i-1]>max2)?height[n-i-1]:max2;x1+=max1;x2+=max2;max3=(height[i]>max3)?height[i]:max3;x4+=height[i];}x3=max3*n;return x1+x2-x3-x4;}
};

哈希表

1 两数之和

leetcode #1 两数之和 应用hash O(1)查找特性 查找target-x

一般用count即可

class Solution {public:vector<int> twoSum(vector<int>& nums, int target) {   unordered_map<int,int>hashtable;for(int i=0; i!=nums.size(); ++i){auto it = hashtable.find(target-nums[i]); // 应用findif(it!=hashtable.end()){return {it->second, i};}hashtable[nums[i]]=i;}return {};}    };class Solution {public:vector<int> twoSum(vector<int>& nums, int target) {unordered_map<int,int> hashtable;for (int i = 0; i != nums.size(); i++) {if (hashtable.count(target - nums[i])) //应用count{return { hashtable[target - nums[i]], i };}hashtable[nums[i]] = i;    }return {};}
};

20 有效的括号

先剪枝
需要用到两两对应时,想想hash

class Solution {public:bool isValid(string s) {stack<char> mystack;unordered_map<char,char>hashtable = {{'(',')'},{'[',']'},{'{','}'}};if(s.size()%2!=0)return false;for(int i=0;i<s.size();i++){if(s[i]=='('||s[i]=='['||s[i]=='{') mystack.push(s[i]);else if(!mystack.empty() && hashtable[mystack.top()]==s[i]) mystack.pop();else return false;}if(!mystack.empty())return false;return true;}
};

3 无重复的最长子串

<下面有后续的优化>

首先想到queue,其次想到hash来实现字符的状态标识,字符状态标识想到利用mark[int(‘A’)]这种形式的bool数组实现,asii码0-128,所以创造一个bool mark[150]

注:遇到下一个重复的字符后,当队列里之前的字符以及重复的字符pop掉后,要记得加入当前这个重复的字符,同时更新length,最大长度的计算最外层循环每循环一次计算一次即可。

//队列+asii码版hash+遍历
class Solution {public:int lengthOfLongestSubstring(string s) {queue<char> que;int maxl=0;    //最大长度int length=0; //当前长度bool mark[150];  //构造asii码版hashmemset(mark,true,sizeof(mark)); //true:该字符之前未出现  false:该字符之前已出现for(int i=0;i<s.size();i++){int tmp = int(s[i]);   //该字符对应的asii码       if(mark[tmp]) //之前未出现过{que.push(s[i]);mark[tmp]=false;length++;}else    //出现过{while(1)  //去掉重复字符以及其之前的字符,为继续读入新字符做准备{mark[int(que.front())]=true; //更新即将弹出的字符状态length--;if(que.front()==s[i]) //到了重复的那个字符{que.pop();break;}else{que.pop();}}que.push(s[i]);   //重复的下一个的录入!!!mark[tmp]=false;length++;}  maxl=(length>maxl)?length:maxl; //更新maxl}return maxl;}
};

优化算法
双指针:其实根本没有必要去模拟队列,只需两个指针,一个模拟当前队头,一个模拟当前队尾,这样可以将空间复杂度从O(n)优化到 O(1),直接在原string上进行操作
拓展
若需要输出该串,只需记录maxl时把首指针和尾指针的下标记下来。

//双指针代替队列+asii码版hash+遍历
class Solution {public:int lengthOfLongestSubstring(string s) {int maxl=0;    //最大长度int length=0; //当前长度int begin=0;  //该串的起始下标int end=0;    //该串的终止下标int q1=0;        //队头指针int q2=0;        //队尾指针bool mark[150];  //构造asii码版hashmemset(mark,true,sizeof(mark)); //true:该字符之前未出现  false:该字符之前已出现while(q2<s.size()) {int tmp = int(s[q2]);   //该字符对应的asii码       if(mark[tmp]) //之前未出现过{q2++;mark[tmp]=false;length++;}else    //出现过{while(1)  //去掉重复字符以及其之前的字符,为继续读入新字符做准备{mark[int(s[q1])]=true; //更新即将弹出的字符状态length--;if(s[q1]==s[q2]) //到了重复的那个字符{q1++;break;}else{q1++;}}q2++;mark[tmp]=false;length++;}if(length>maxl)     //更新maxl,begin,end{maxl=length;begin=q1;end=q2;}  }//cout<<s.substr(begin,maxl)<<endl; //输出该字串return maxl;}
};

进一步简化hash;以及将hash的value赋为下标(类似 1.两数之和)
注意:不论队首是否需要调整,队尾所对应的hash值都需要调整

class Solution {public:int lengthOfLongestSubstring(string s) {int i=0;//队首int j=0;//队尾vector<int> mp(128,-1);//mp[i]=t; i表示字母,t表示字母出现的位置,为-1表示从未出现过int res=0;while(j<s.size()){int loc=mp[int(s[j])];if(loc>=0) { //i后移且更新mpwhile(i<loc){mp[int(s[i])]=-1;i++;}i++;}mp[int(s[j])]=j;res=((j-i+1)>res)?j-i+1:res;j++;}return res;}
};
//hash容器版本
#include<bits/stdc++.h>
class Solution {public:/*** * @param arr int整型vector the array* @return int整型*/int maxLength(vector<int>& arr) {// write code hereint n=arr.size();if(n==1||n==0) return n;int maxx=0;int head,tail=0;unordered_map<int,int> hash;for(int i=0;i<n;i++){if(!hash.count(arr[i])) hash[arr[i]]=i;else {maxx=(tail-head>maxx)?tail-head:maxx;head=hash[arr[i]]+1;//clear掉head前的东西hash.clear();for(int j=head;j<=tail;j++) hash[arr[j]]=j;}tail++;}return max(maxx,tail-head);}
};

287 寻找重复数

方法一:哈希表
时间复杂度 O(n)
空间复杂度 O(n)
注:一般的数据结构,新开一个长度为n的结构,空间复杂度就是O(n)。若空间复杂度为O(1),一般情况下是直接对原数据结构进行操作。

//hash统计出现次数,遇见多次的直接跳出
class Solution {public:int findDuplicate(vector<int>& nums) {unordered_map<int,int> mp;int key;for(int i=0;i<nums.size();i++){if(mp.count(nums[i])){key=nums[i];break;} else mp[nums[i]]=1;}return key;}
};

方法二:快慢指针(链表题)
题解

136 只出现一次的数字

hash解法
空间复杂度O(n)
时间复杂度O(n)

/*
hash表统计出现次数
注意:O(Kn)=O(n)
*/
class Solution {public:int singleNumber(vector<int>& nums) {unordered_map<int,int> mp;int key;for(int i=0;i<nums.size();i++){if(!mp.count(nums[i])){mp[nums[i]]=1;}else mp[nums[i]]++;}for(int i=0;i<nums.size();i++){if(mp[nums[i]]==1){key=nums[i];break;}}return key;}
};

异或解法
空间复杂度O(1)
时间复杂度O(n)

/*
异或运算:
a⊕a=0
a⊕0=a
a1⊕a1⊕a2⊕a2⊕a3=a3
*/
class Solution {public:int singleNumber(vector<int>& nums) {int res=0;for(int i=0;i<nums.size();i++){res=res^nums[i];}return res;}
};

49 字母异位词分组

/*
构建hash表:key为sort后的字符串;value为vector<string>,用于存储所有该类的字符串
*/
class Solution {public:vector<vector<string>> groupAnagrams(vector<string>& strs) {//hashunordered_map<string,vector<string>> mp;for(auto elem:strs){string cur = elem;sort(elem.begin(),elem.end());mp[elem].push_back(cur);}//输出vector<vector<string>> res;for(auto elem:mp)res.push_back(elem.second); //直接整体push_back进来return res;}
};

560 和为k的子数组

/*
连续和为k -> 前缀和简化
不需要输出子数组,只需输出次数 -> 使用hash记录前缀和的次数
解析链接:https://leetcode-cn.com/problems/subarray-sum-equals-k/solution/dai-ni-da-tong-qian-zhui-he-cong-zui-ben-fang-fa-y/
统计前缀和 O(n)
遍历前缀和数组,使用hash统计各前缀和的出现次数,遍历过程中判断hash[当前前缀和-k]
总复杂度O(n)
*/
class Solution {public:int subarraySum(vector<int>& nums, int k) {//统计前缀和vector<int> preSum;int last = 0;for(auto elem:nums){preSum.push_back(last+elem);last+=elem;}//遍历+hashunordered_map<int,int> mp;mp[0]=1;//前缀和为0的情况=1int res=0;for(auto elem:preSum){//首先进行判断,需要注意有多个答案的情况(直接加mp[elem-k]即可) if(mp.count(elem-k)) res+=mp[elem-k];//之后,统计本次,注意前后顺序,防止k=0时多算本次的情况if(!mp.count(elem))mp[elem]=1;else mp[elem]++;}return res;}
};

1737 满足三条件之一需改变的最少字符数

/*
hash-->统计26个字母可以用数组作hash  <注意:26的遍历不算复杂度>
讨论+遍历
*/
class Solution {public:int ans;void x(vector<int> a,vector<int> b) //使得b全在a前面{for(int i=1;i<26;i++) //i为分界点{int tmp=0;for(int j=i;j<26;j++) tmp+=a[j];for(int j=0;j<i;j++) tmp+=b[j];ans=min(ans,tmp);}   }int minCharacters(string a, string b) {vector<int> ca(26),cb(26); //这个方法可以直接给vector设置大小,且初值为0int n=a.size(),m=b.size();for(char c:a) ca[c-'a']++; //string的迭代简化for(char c:b) cb[c-'a']++;ans=n+m;//第三种情况for(int i=0;i<26;i++)ans=min(ans,n+m-ca[i]-cb[i]);//第一二种情况x(ca,cb);x(cb,ca);return ans;}
};

前缀和解法题解,基本类似上面解法,更巧妙

两个大数据集找交集(阿里面试)

若电脑内存只有16G,两个数据集都有上百G,如何找交集?

利用hash嵌套的思想
首先,将数据集取模,按结果的不同把每个数据集分成若干个小块,因为两者模相同的小块才有可能有交集,所以将两个模相同的小块拿到电脑中,进行取交集。
取交集的思路:遍历第一个小块,建hash,遍历第二个,有重复的部分,拿出来即可。

739 每日温度

/*
单调递减栈:维护一个栈,保证里面的元素从栈底至栈顶单调递减构造pair<温度,序列号> 维护pair的递减栈
若当前元素温度>栈顶元素温度,栈顶元素对应的输出=当前元素序列号-栈顶元素序列号,再将栈顶pop,继续比较新栈顶
若当前<=栈顶,则直接压栈
*/
class Solution {public:vector<int> dailyTemperatures(vector<int>& T) {stack<pair<int,int>> st;vector<int> res;res.resize(T.size(),0);for(int i=0;i<T.size();i++){while(!st.empty() && T[i]>st.top().first) //短路算法 先算前面{res[st.top().second]=i-st.top().second;st.pop();}if(st.empty() || T[i]<=st.top().first) st.push(make_pair(T[i],i));}return res;}
};

后来发现根本不用pair,因为有序号自然可以知道所对应的温度

关于单调栈的使用情况

  1. 每个元素找到它右边第一个比它大的元素的位置,求它们的距离
    从左到右遍历,维护一个从栈底到栈顶递减的栈,因为遇到新元素大于栈顶元素时,栈顶元素就遇到了对应的比它大的最近元素,要弹栈。这也是栈从底到顶单调递减的原因
  2. 每个元素找到它右边第一个比它小的元素的位置,求它们的距离
    从左到右,维护栈底到栈顶递增的栈,遇到递减元素,弹栈
  3. 每个元素找到它左边第一个比它大的元素的位置,求它们的距离
    相当于把1的数组头尾调过来,只需要从右向左遍历维护栈底到栈顶递减栈即可
  4. 每个元素找到它左边第一个比它小的元素的位置,求它们的距离
    相当于把2的数组头尾调过来,只需要从右向左遍历维护栈底到栈顶递增栈即可

155 最小栈

辅助栈:可以直接把两个栈结合成一个pair为元素的栈;也可以再建立一个栈。

/*
构造栈内元素pair,pair.first存值,pair.second为当其为栈顶时,栈内最小元素的值。
*/
class MinStack {public:stack<pair<int,int>> st;/** initialize your data structure here. */MinStack() {}void push(int x) {if(st.empty()) st.push(make_pair(x,x));else st.push(make_pair(x,min(x,st.top().second)));}void pop() {st.pop();}int top() {return st.top().first;}int getMin() {return st.top().second;}
};/*** Your MinStack object will be instantiated and called as such:* MinStack* obj = new MinStack();* obj->push(x);* obj->pop();* int param_3 = obj->top();* int param_4 = obj->getMin();*/
class MinStack {public:/** initialize your data structure here. */MinStack() {}stack<int> st;stack<int> mst;void push(int x) {st.push(x);if(mst.empty()||x<mst.top()) mst.push(x);else mst.push(mst.top());    }void pop() {st.pop();mst.pop();}int top() {return st.top();}int getMin() {return mst.top();}
};/*** Your MinStack object will be instantiated and called as such:* MinStack* obj = new MinStack();* obj->push(x);* obj->pop();* int param_3 = obj->top();* int param_4 = obj->getMin();*/

394 字符串解码

本题难点在于括号内嵌套括号,需要从内向外生成与拼接字符串,这与栈的先入后出特性对应。

/*
遍历s,维护多个栈
栈1:存数字 用stack实现,又因为是string类型,所以设一个stTmp,存当前一段的数字,遇到[时,把之前那一段存到stack里,stTmp清空
栈2:存字母字符 用string实现,遇到],从栈3栈顶元素对应下标的开始复制,按栈1栈顶元素的值决定复制次数(for+substr)
栈3:存遇到'['时栈2的大小,所以栈3栈顶元素为 最近的一个'['后的第一个字母在栈2中的下标
*/
class Solution {public:string decodeString(string s) {stack<int> st;string res;stack<int> mark;string stTmp;for(int i=0;i<s.size();i++){cout<<"i="<<i<<endl;if(s[i]>='0'&&s[i]<='9'){   stTmp+=s[i];cout<<"stTmp:"<<stTmp<<endl;} else if(s[i]>='a'&&s[i]<='z') {res+=s[i];cout<<"res:"<<res<<endl;}else if(s[i]=='[') {st.push(atoi(stTmp.c_str()));stTmp="";cout<<"st"<<" top:"<<st.top()<<" size:"<<st.size()<<endl;mark.push(res.size());cout<<"mark"<<" top:"<<mark.top()<<" size:"<<mark.size()<<endl;}else if(s[i]==']'){string tmp = res.substr(mark.top(),res.size()-mark.top());cout<<"tmp:"<<tmp<<endl;mark.pop();cout<<"st top:"<<st.top()<<endl;for(int j=0;j<st.top()-1;j++){res+=tmp;}cout<<"res:"<<res<<endl;st.pop();}}return res;}
};

注:可简化为只有遇到[和]时再进行栈操作 相关链接
构造一个stack<pair<int,string>>,int存这个‘[’之前的num,string存这个‘[’之前的res。

#include<bits/stdc++.h>
using namespace std;
int main()
{string s;cin>>s;stack<pair<int,string> > st;int n=s.size();int multi=0;string res="";for(int i=0;i<n;i++){if(s[i]>='0'&&s[i]<='9') multi=s[i]-'0';else if(s[i]>='a'&&s[i]<='z') res+=s[i];else if(s[i]=='['){st.push(make_pair(multi,res));multi=0;res="";}else if(s[i]==']'){int k = st.top().first;string s="";while(k--){s+=res;}res=st.top().second+s;st.pop();}}cout<<res<<endl;return 0;
}

int型整数逆序(华为笔试)

要注意溢出

#include<bits/stdc++.h>
using namespace std;
int main()
{int input;cin>>input;int x=input;stack<int> st;while(x){st.push(x%10);x=x/10;}long output=0;int i=1;while(!st.empty()){output+=i*st.top();i=i*10;st.pop();}cout<<output<<endl;return 0;
}

用两个栈实现队列(阿里面试题)

这里是引用

347 前k个高频元素

nlogn的解法

/*
hash统计出现次数 o(n)
hash装入vector<pair<key,vaule>> 按value进行从大到小sort o(n) o(nlogn)
循环取前k个key
总复杂度 o(nlogn)
*/
class Solution {public:bool static cmp(pair<int,int> a, pair<int,int> b) //写在类内 需要+static{return a.second>b.second; //按第二个元素从大往小排序}vector<int> topKFrequent(vector<int>& nums, int k) {//hash统计次数unordered_map<int,int> mp;for(auto elem:nums){if(!mp.count(elem)) mp[elem]=1;else mp[elem]++;}//排序vector<pair<int,int>> tmp;for(auto elem:mp)//tmp.push_back(make_pair(elem.first,elem.second)); //另一种pair写法tmp.push_back({elem.first,elem.second});sort(tmp.begin(),tmp.end(),cmp);//取结果vector<int> res;for(int i=0;i<k;i++)res.push_back(tmp[i].first);return res;}
};

时间复杂度为O(nlogk)的解法(优先队列作堆)

/*
hash统计出现次数 o(n)
维护一个长度为k的优先队列(value最小的放在队首),遍历hash,每次将遍历的元素的value值与队列队首元素的value比较,决定插入与否。插入环节时间复杂度logk,所以总复杂度nlogk
输出优先队列里的key值
总复杂度 o(nlogk)
*/
class Solution {public:struct cmp{bool operator ()(pair<int,int> a, pair<int,int> b){return a.second>b.second; //第二个元素最小值优先}};vector<int> topKFrequent(vector<int>& nums, int k) {//hash统计次数unordered_map<int,int> mp;for(auto elem:nums){if(!mp.count(elem)) mp[elem]=1;else mp[elem]++;}//维护长度为k的优先队列priority_queue<pair<int,int>,vector<pair<int,int> >,cmp > que;for(auto elem:mp){if(que.size()<k) que.push(make_pair(elem.first,elem.second));else if(elem.second>que.top().second && que.size()==k) {que.pop();que.push(make_pair(elem.first,elem.second));}}//取结果vector<int> res;int n = que.size();for(int i=0;i<n;i++){res.push_back(que.top().first);que.pop();}return res;}
};

215 数组中的第K个最大元素

/*
优先队列作堆(小顶堆)
维护一个长度为k的优先队列,每次与堆顶比较,大于堆顶即插入
最后输出堆顶
nlogk
*/
class Solution {public:int findKthLargest(vector<int>& nums, int k) {priority_queue<int,vector<int>,greater<int>> que;for(auto elem:nums){if(que.size()<k) que.push(elem);else if(elem>que.top()) {que.pop();que.push(elem);}}return que.top();}
};

扩展 O(n)求topk的做法
复杂度O(n)+O(2/n)+O(4/n)+…
类似快排
但这种算出的topk内部没有大小顺序,只能确定topk这个元素和所有topk都有哪些

第一次partition,划分之后:<快排的每一次遍历>
与快排的不同之处:从大到小排序,舍弃另一半i = partition(arr, 1, n);如果i大于k,则说明arr[i]左边的元素都大于k,于是只递归arr[1, i-1]里第k大的元素即可;
如果i小于k,则说明说明第k大的元素在arr[i]的右边,于是只递归arr[i+1, n]里第k-i大的元素即可;
画外音:这一段非常重要,多读几遍。这就是随机选择算法randomized_select,RS,其伪代码如下:int RS(arr, low, high, k){if(low== high) return arr[low];i= partition(arr, low, high);temp= i-low; //数组前半部分元素个数if(temp>=k)return RS(arr, low, i-1, k); //求前半部分第k大elsereturn RS(arr, i+1, high, k-i); //求后半部分第k-i大}/代码如下:由快排代码更改得来
class Solution {public:int qsort(vector<int> &nums,int left,int right,int k){int x=nums[left];int i=left;int j=right;while(i<j){while(i<j&&nums[j]>=x) j--;if(i<j) nums[i++]=nums[j];while(i<j&&nums[i]<=x) i++;if(i<j) nums[j--]=nums[i];}nums[i]=x;int tmp = i-left; //因为k代表的是left-right这个部分的第几个 所以要求出tmp进行比较if(tmp==k) return x;else if(tmp>k) return qsort(nums,left,i-1,k);else return qsort(nums,i+1,right,k-tmp-1); //k-tmp-1:可以这么想,当tmp=0时,k若不减一就没变化了}int findKth(vector<int> a, int n, int K) {// write code hereint k = n-K; //找第k小的数 k在0到n-1之间return qsort(a,0,n-1,k);}
};

贪心

621 任务调度器

方法1 模拟+贪心

注意:
bestchoice和choice遍历时的初值设置,可以先在循环外设best的初值(设一个非常特殊的值,遍历不到的),然后在循环内部进行特判,来实现初值的赋值。

/*
greedy:在每次选择时,选择当前状况下可选列表中剩余最多的task O(26n)构造hash表,key值为任务名,value为下面的vector
vector<pair<int,int>> 第一个存该任务的剩余个数,第二个存该任务可以使用的最早时间*/
class Solution {public:int leastInterval(vector<char>& tasks, int n) {unordered_map<char,pair<int,int> > mp;//build hashfor(auto elem:tasks){if(!mp.count(elem)) mp[elem]=make_pair(1,0);else mp[elem].first ++;}// greedyint num=tasks.size();char choice; //本轮初步选择char choiceBest;//最佳最佳选择bool mark = true; //本轮是否可以执行taskint i = 0; //timefor(;num!=0;i++){choiceBest='0';for(auto elem:mp){if(mp[elem.first].first > 0 && mp[elem.first].second <= i){choice = elem.first;mark = true;if(choiceBest == '0' || mp[choiceBest].first < mp[choice].first){choiceBest = choice;}  }               } if(mark == true){//cout<<choiceBest<<" ";mp[choiceBest].first--; //cout<<mp[choiceBest].first<<" ";mp[choiceBest].second = i+n+1;  num--;    }//else cout<<"null ";mark = false;}return i;}
};

方法2 桶思想

class Solution {public:int leastInterval(vector<char>& tasks, int n) {int len = tasks.size();vector<int> hash(26,0);int maxx=0;for(auto elem:tasks) {hash[elem-'A']++;maxx=(hash[elem-'A']>maxx)?hash[elem-'A']:maxx;}int k=0;for(auto elem:hash){if(elem==maxx) k++;}return max(len,(maxx-1)*(n+1)+k);}
};

要注意 并列的if 如果不想有相交的情况,要用 if/else if

55 跳跃游戏

/*
维护能跳至的最远距离k <只要能跳到最远点,最远点之前的点全能跳到>
只要起跳点i<=k,则能跳到
*/
class Solution {public:bool canJump(vector<int>& nums) {int k=0;for(int i=0;i<nums.size();i++){if(i>k) return false;k=max(k,i+nums[i]);}return true;}
};

406 根据身高重建队列

注:sort用于vector时

sort(vec.begin(),vec.end(),cmp);
/*
hint: What can you say about the position of the shortest person?
If the position of the shortest person is i, how many people would be in front of the shortest person?从身高最小的人开始填,其对应的pair的第二个元素==所填位置之前的空格数目
若身高有相同,先填第二个元素大的(试试看就知道)greedy:从身高小的入手
*/
class Solution {public:bool static cmp(vector<int> a,vector<int> b){if(a[0]==b[0]) return a[1]>b[1];else return a[0]<b[0];}vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {vector<vector<int>> que;int n=people.size();que.resize(n);bool mark[n];memset(mark,0,sizeof(mark));sort(people.begin(),people.end(),cmp); //.begin .endfor(int i=0;i<n;i++){int x=people[i][1];for(int j=0;j<n;j++){if(x==0 && mark[j]==false) {que[j]={people[i][0],people[i][1]};mark[j]=true;break;}if(mark[j]==false) x--;}}return que;}
};

排序

56 合并区间

/*
按第一个元素从小到大(第一个元素相同时,第二个元素从大到小(或从小到大))排序 nlogn
遍历 前一个元素的second与后一个的first和second比较 n
*/
class Solution {public:bool static cmp(vector<int> &a,vector<int> &b){if(a[0]==b[0]) return a[1]>b[1];else return a[0]<b[0];}vector<vector<int>> merge(vector<vector<int>>& intervals) {int n = intervals.size();if(n==1) return intervals;vector<vector<int>> res;sort(intervals.begin(),intervals.end(),cmp);res.push_back(intervals[0]);int x=0;for(int i=1;i<intervals.size();i++){int cur1 = res[x][0];int cur2 = res[x][1];if(cur2>=intervals[i][0] && cur2<intervals[i][1]){res.pop_back();res.push_back({cur1,intervals[i][1]});}else if(cur2<intervals[i][0]){res.push_back(intervals[i]);x++;}}return res;}
};

关于tree结构体的一些操作

定义

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/1. TreeNode* a //a是一个指向TreeNode结构体的指针2. a->val,a->left,a->right / (*a).val,(*a).left,(*a).right //1的操作3. TreeNode* a = new TreeNode();//TreeNode()调用构造函数,新建一个节点,new的返回类型为指针,所以前面是TreeNode* a4. TreeNode b //b是结构体5. TreeNode b(); a=&b; //这两句就相当于3

建树

前序建树

TreeNode* buildTree() //递归建树(前序建立)
{int a;cin>>a;if(a==-1) return nullptr;else{TreeNode* root = new TreeNode(a);root->left = buildTree();root->right = buildTree();return root;}
}
若为列表
vector<int> vec;
int p = 0;
TreeNode* buildTree() //递归建树(前序建立)
{if(p == vec.size()) return nullptr;int a = vec[p++];if(a==-1) return nullptr;else{TreeNode* root = new TreeNode(a);root->left = buildTree();root->right = buildTree();return root;}
}

注意!这里不能void(TreeNode * root)这样建
这样相当于 TreeNode * root_local = root; 然后在function内root_local指向了新的空间, 而外部的root还是原先的位置,没法同步。

617 合并二叉树

/*
递归dfs
1.先写边界条件(特殊情况)
2.再写一般情况
*/
/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {public:TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {if(!t1 && !t2) return nullptr;else if(!t1) return t2;else if(!t2) return t1;else //t1 t2 都不为空{t1->val+=t2->val;t1->left=mergeTrees(t1->left,t2->left);t1->right=mergeTrees(t1->right,t2->right);return t1;}}
};

226 翻转二叉树

/*
递归dfs
*/
/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {public:TreeNode* invertTree(TreeNode* root) {if(!root) return nullptr;swap(root->left,root->right);root->left=invertTree(root->left);root->right=invertTree(root->right);return root;}
};

104 二叉树的最大深度

//递归
/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {public:int maxDepth(TreeNode* root) {if(!root) return 0;else return 1 + max(maxDepth(root->left),maxDepth(root->right));}
};

101 对称二叉树

/*
递归
*/
/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {public:bool x(TreeNode* r1,TreeNode* r2){if (!r1 && !r2) return true;else if (!r1 || !r2) return false;else if(r1->val == r2->val) return x(r1->left,r2->right) && x(r1->right,r2->left);else return false;}bool isSymmetric(TreeNode* root) {if(!root) return true;return x(root->left,root->right);}
};

543 二叉树的直径

递归方式
设函数来求:经过根节点的直径长度
遍历树的各个节点,带入到前面的函数中

递归环节太多

/*
递归
每个节点的直径=左子树深度+右子树深度
*/
/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}* };*/
class Solution {public:int maxv=0;int maxDepth(TreeNode* root) {if(!root) return 0;else return 1 + max(maxDepth(root->left),maxDepth(root->right));}int x(TreeNode* root)//以root为根节点且路径穿过root根节点时,路径的max(左子树深度+右子树深度){     if(!root) return 0;else return maxDepth(root->left)+maxDepth(root->right);}int diameterOfBinaryTree(TreeNode* root) {if(!root) return 0;maxv=(x(root)>maxv)?x(root):maxv;int cur1=diameterOfBinaryTree(root->left);int cur2=diameterOfBinaryTree(root->right);return max(maxv,max(cur1,cur2));}
};

减少递归环节。

递归函数,粗略理解即反复调用自己。 但是在函数内部,仅调用尽可能少次数的自身,使得递归可以进行下去即可,不需要调用多次,会导致超时。
递归的过程想维护最优解,可以设全局变量。 调用递归的时候,不一定要在return中进行,访问自身函数即是递归调用。

/*
递归
每个节点的直径=左子树深度+右子树深度
递归过程中维护maxv
*/
/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}* };*//*
递归返回当前节点的最大深度,
过程中维护一个max
max=(当前节点的)左子树最大深度+右子树最大深度*/
class Solution {public:int maxv=0;int maxDepth(TreeNode* root) //返回值为当前节点的最大深度{//求的时候维护maxv(maxv是我们所求的,函数返回值不一定是全局最优)if(!root) return 0;int left=maxDepth(root->left);int right=maxDepth(root->right);int cur=left+right;maxv=(cur>maxv)?cur:maxv;return 1 + max(left,right); //注:此处不要再用maxDepth(root->left)了,又来一边递归,费时。}int diameterOfBinaryTree(TreeNode* root) {maxDepth(root); //注:有返回值的函数也可以直接写return maxv;}
};

102 二叉树的层序遍历

/*
bfs
为了把每层放入到一个vector中,需要在正常的while里再加一层循环,把当前队列里的元素打入到同一个vector中
*/
/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}* };*/
class Solution {public: vector<vector<int>> levelOrder(TreeNode* root) {vector<vector<int>> res;if(!root) return res;queue<TreeNode*> que;que.push(root);while(!que.empty()){  int n = que.size(); vector<int>level;while(n--){auto cur = que.front();que.pop();level.push_back(cur->val);if(cur->left) que.push(cur->left);if(cur->right) que.push(cur->right);}res.push_back(level);}return res;        }
};

103 二叉树锯齿层序遍历(deque)

https://leetcode-cn.com/problems/binary-tree-zigzag-level-order-traversal/solution/103-ju-chi-ceng-xu-bian-li-deque-by-flyt-v5fb/

94 二叉树的中序遍历

前序 后序都可以用类似的递归实现,调整val(中)的push_back的位置即可
前序: 中(val) - 左 - 右
中序: 左 - 中(val) - 右
后序: 左 - 右 - 中(val)

下面的是递归实现方式。
非递归实现见leetcode之路(2)。

/*
递归 新建void函数处理
*/
/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {public:void inorder(TreeNode* root,vector<int>& res){if(!root) return;inorder(root->left,res);res.push_back(root->val);inorder(root->right,res);}vector<int> inorderTraversal(TreeNode* root) {vector<int> res;inorder(root,res);return res;}
};
/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {public:vector<int> res;vector<int> inorderTraversal(TreeNode* root) {if(!root) return {};inorderTraversal(root->left);res.push_back(root->val);inorderTraversal(root->right);return res;}
};

114 二叉树展开为链表(单向)

非常好的解析

递归的一个非常重要的点就是:不去管函数的内部细节是如何处理的,我们只看其函数作用以及输入与输出。

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {public:void flatten(TreeNode* root) {if(!root) return;flatten(root->left);flatten(root->right);TreeNode* tmp=root->right;root->right=root->left;root->left=nullptr;while(root->right) root=root->right;root->right=tmp;}
};

双向链表的扩展

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {public:void flatten(TreeNode* root) {if(!root) return;flatten(root->left);flatten(root->right);TreeNode* l=root->left;TreeNode* r=root->right;root->right=l;root->left=nullptr;l->left=root;while(root->right) root=root->right;root->right=r;r->left=root;}
};

将树(中序遍历)转化成双向链表(字节面试)

这个写法是万能写法!!!!!

由于inorder(x)后我们不知道x部分的头节点是谁,(前序遍历一定还是x),同时inorder没返回值,所以这种以外围全局变量保存的方式,一定要注意inorder的调用顺序。

/*
通过保存pre,在递归中序遍历树的基础上,每次执行当前部分,都将left指向pre。同时将pre的right指向当前root,构造双向链表
*/
#include<bits/stdc++.h>
using namespace std;
struct Treenode
{int val;Treenode *left;Treenode *right;Treenode(int x):val(x),left(nullptr),right(nullptr){}
};
Treenode* pre = nullptr;
void inorder(Treenode* root)
{if(!root) return;inorder(root->left); //左边已经调整好,最后一个是pre;//处理中间部分 把左侧链表和右侧链表连起来 ,left指向前驱,right指向后继if(!pre) root->left = nullptr; //没有左边的,该点做头;else{root->left=pre;    pre->right=root;}pre=root;inorder(root->right);
}
int main()
{inorder(root);pre->right=nullptr; //双向链表的最后一个指向空//再通过 root找到最左端的头while(root->left!=nullptr){root=root->left;   }
}

先序

#include<bits/stdc++.h>
using namespace std;
struct Treenode
{int val;Treenode *left;Treenode *right;Treenode(int x):val(x),left(nullptr),right(nullptr){}
};
Treenode* pre = nullptr;
void inorder(Treenode* root)
{if(!root) return;if(!pre) root->left = nullptr; //没有左边的,该点做头;else{root->left=pre;    pre->right=root;}pre=root;inorder(root->left); //左边已经调整好,最后一个是pre;inorder(root->right);
}
int main()
{inorder(root);pre->right=nullptr; //双向链表的最后一个指向空//root为头
}

后序

#include<bits/stdc++.h>
using namespace std;
struct Treenode
{int val;Treenode *left;Treenode *right;Treenode(int x):val(x),left(nullptr),right(nullptr){}
};
Treenode* pre = nullptr;
void inorder(Treenode* root)
{if(!root) return;inorder(root->left); //左边已经调整好,最后一个是pre;inorder(root->right);if(!pre) root->left = nullptr; //没有左边的,该点做头;else{root->left=pre;    pre->right=root;}pre=root;
}
int main()
{inorder(root);pre->right=nullptr; //双向链表的最后一个指向空while(root->left!=nullptr){root=root->left;    }
}

105 从前序与中序遍历序列构造二叉树

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*//*递归前序遍历:[根节点,[左子树],[右子树]]中序遍历:[[左子树],根节点,[右子树]]所以可以根据从前序遍历数组中找到根节点,再去中序遍历数组中定位(可以现存个中序的hash,方便查找定位),把左子树和右子树确认出来,之后子树进行递归操作。建树过程:函数返回值为TreeNode*,函数内部root->right和root->left递归赋值,相当于每层函数只建立一个根节点,左子树的根节点和右子树的根节点由下一级递归返回关于递归参数:因为肯定要传递子树所对应的vector的信息,然而每层都赋值一个新vector会开辟很多空间,且操作麻烦,所以只传递子树在原vector上的左右界限。*/
class Solution {public:unordered_map<int,int> mp;TreeNode* build(vector<int>& preorder, vector<int>& inorder,int preorder_left,int preorder_right,int inorder_left,int inorder_right) //最后一个参数是该层建立的树的root{  //边界条件:无子树,返回nullptrif(preorder_left>preorder_right) return nullptr;int x=mp[preorder[preorder_left]];//根节点在中序遍历数组中的位置TreeNode* root = new TreeNode(preorder[preorder_left]); //建立根节点//左子树递归:在preorder中,preorder_left+1成为新左子树的起点,依次由示意图可推断其他参数root->left = build(preorder,inorder,preorder_left+1,preorder_left+x-inorder_left,inorder_left,x-1); //右子树递归root->right = build(preorder,inorder,preorder_left+1+x-inorder_left,preorder_right,x+1,inorder_right);return root;}TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {for(int i=0;i<inorder.size();i++) mp[inorder[i]]=i;int n=preorder.size();return build(preorder,inorder,0,n-1,0,n-1);}
};

98 验证二叉搜索树

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*//*
递归
注:节点要大于左子树的所有点(左子树的左子树+左子树的右子树,所以不能单纯比较一次大小,需要维护一个区间*/
class Solution {public:bool x(TreeNode* root,long long l,long long r) //有longlong的数据{if(!root) return true;if(root->val<=l || root->val>=r) return false;return x(root->left,l,root->val) && x(root->right,root->val,r);}bool isValidBST(TreeNode* root) {return x(root,LONG_MIN, LONG_MAX); //LONG_MIN/MAX为long long类型的极值}
};

236 二叉树的最近公共祖先

清晰题解
这类型的还可以看看 865

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}* };*///递归
class Solution {public:TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {//临界条件:思考-当root为什么的时候,会有直接的返回值if(!root) return NULL;if(root==p||root==q) return root;//递归part: 思考-这个时候就相当于求出了左子树和右子树的情况//1.若子树中只有p或q的一个 返回的是这个点的位置 //2.若子树中同时有p和q,返回其pq的最近root//3.若子树无p和q 返回NULLTreeNode* left = lowestCommonAncestor(root->left,p,q);TreeNode* right = lowestCommonAncestor(root->right,p,q);if(!left) return right;if(!right) return left;if(left && right) return root;return NULL;}
};

链表

Listnode*a,*b;
a=b;
即:a指针 指向 b指针所指的东西(a,b要指向同一个 且 a是变量)
a为指向a结构体的指针;a->next为a结构体引出来的next指针
—a–>[|||结构体a|||]–next–>

206 反转链表

递归法比较难理解 看看即可
题解视频

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*//*
递归
归的过程翻转*/
class Solution {public:ListNode* reverseList(ListNode* head) {if(!head||!head->next) return head;ListNode* res =reverseList(head->next);head->next->next=head;head->next=nullptr;return res; //递归返回的过程中 res一直指向旧链表最后一个元素,也就是新链表的表头//递归的归的过程中,head在逐层向前,从后向前完成翻转。}
};

方法2 迭代遍历(更好理解+更普遍)

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*//*
迭代遍历
1->2->3->null 变为 null<-1<-2<-3,重点在于,逐个反转的过程中,要保存前一个和后一个
设pre cur next 三个指针
null  1->  2  ->  3->null
|     |    |
pre  cur  next      每次将cur->next= pre;之后pre cur next 都向后移
注:指针的赋值就是指向同一个元素*/class Solution {public:ListNode* reverseList(ListNode* head) {ListNode* pre,*cur,*next;pre=nullptr;cur=head;next=head;while(cur){next=next->next;cur->next=pre;pre=cur;cur=next;}return pre;}
};

非常熟练的写法,如下所示:

/*
struct ListNode {int val;struct ListNode *next;ListNode(int x) :val(x), next(NULL) {}
};*/
class Solution {public:ListNode* ReverseList(ListNode* pHead) {if(!pHead||!pHead->next) return pHead;ListNode* pre = nullptr;ListNode* cur = pHead;ListNode* nxt = cur->next;while(nxt){cur->next=pre;pre=cur;cur=nxt;nxt=nxt->next;}cur->next=pre;return cur;}
};

2 两数相加

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*//*建立新链表 a->next=new Listnode(x);a=a->next;新链表头可先设一个头指针指向val为0的节点,之后保存头指针,返回的时候直接返回头指针的next循环判断时判断当前指针即可同时要考虑到只有进位的情况*/
class Solution {public:ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {if(!l1) return l2;if(!l2) return l1;ListNode* cur1 = l1;ListNode* cur2 = l2;ListNode* res = new ListNode(0);ListNode* cur3 = res;int c=0; //进位while(cur1||cur2||c){int tmp1=(cur1)?cur1->val:0; int tmp2=(cur2)?cur2->val:0;int tmp3=tmp1+tmp2+c;if(tmp3>=10){tmp3=tmp3%10;c=1;}else c=0; cur3->next = new ListNode(tmp3);cur1=(cur1)?cur1->next:nullptr;cur2=(cur2)?cur2->next:nullptr;cur3=cur3->next;}return res->next;}
};

21 合并两个有序链表

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*//*直接在原链表上操作,l1指针标定现在链表1遍历到的位置,l2标定链表2,l3连接已经选择的节点l3每选择一个节点,将其标定原链表的指针指向next(l1/l2遍历下一点),同时l3也要指向下一个点注意:return的是l3的头节点,遍历过程中l3在改变,应该在最开始备份一个head节点*/
class Solution {public:ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {if(!l1) return l2;if(!l2) return l1;//初始化ListNode* l3;if(l1->val <= l2->val) {l3=l1;l1=l1->next;}else{l3=l2;l2=l2->next;}ListNode* res=l3;while(l1||l2){if(!l1) {l3->next=l2;break;}else if(!l2) {l3->next=l1;break;}else if(l1->val <= l2->val) {l3->next=l1;l1=l1->next;}else{l3->next=l2;l2=l2->next;}l3=l3->next;}return res;}
};

148 排序链表

详情见链接中的方法一

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {public:ListNode* sortList(ListNode* head) {if(!head||!(head->next)) return head;return sortList(head, nullptr);}ListNode* sortList(ListNode* head, ListNode* tail) {//分割的单个最小单元    if (head->next == tail) {head->next = nullptr; //断开return head;}//快慢指针确定当前分割中点ListNode* slow = head;ListNode* fast = head;while (fast != tail) {slow = slow->next;fast = fast->next;if (fast != tail) {fast = fast->next;}}ListNode* mid = slow;ListNode* list1 = sortList(head, mid);ListNode* list2 = sortList(mid, tail);ListNode* sorted = merge(list1, list2);return sorted;}ListNode* merge(ListNode* l1, ListNode* l2) {if(!l1) return l2;if(!l2) return l1;//初始化ListNode* l3;if(l1->val <= l2->val) {l3=l1;l1=l1->next;}else{l3=l2;l2=l2->next;}ListNode* res=l3;while(l1||l2){if(!l1) {l3->next=l2;break;}else if(!l2) {l3->next=l1;break;}else if(l1->val <= l2->val) {l3->next=l1;l1=l1->next;}else{l3->next=l2;l2=l2->next;}l3=l3->next;}return res;}
};

19 删除链表的倒数第 N 个结点

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*//*Hint: Maintain two pointers and update one with a delay of n steps.*/class Solution {public:ListNode* removeNthFromEnd(ListNode* head, int n) {if(!head->next) return nullptr;ListNode* first = head;ListNode* second = head;while(n--) second=second->next;if(!second) return first->next;while(second->next){first=first->next;second=second->next;}first->next=first->next->next;return head;}
};

141 环形链表

/*
单层hash<unordered_set>
*/
/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {public:bool hasCycle(ListNode *head) {unordered_set<ListNode*> hash;while(head!=nullptr){if(hash.count(head)) return true;hash.insert(head);head=head->next;}return false;        }
};

快慢指针法

「Floyd 判圈算法」(又称龟兔赛跑算法)
假想「乌龟」和「兔子」在链表上移动,「兔子」跑得快,「乌龟」跑得慢。当「乌龟」和「兔子」从链表上的同一个节点开始移动时,如果该链表中没有环,那么「兔子」将一直处于「乌龟」的前方;如果该链表中有环,那么「兔子」会先于「乌龟」进入环,并且一直在环内移动。等到「乌龟」进入环时,由于「兔子」的速度快,它一定会在某个时刻与乌龟相遇,即套了「乌龟」若干圈。

我们可以根据上述思路来解决本题。具体地,我们定义两个指针,一快一满。慢指针每次只移动一步,而快指针每次移动两步。初始时,慢指针在位置 head,而快指针在位置 head.next。这样一来,如果在移动的过程中,快指针反过来追上慢指针,就说明该链表为环形链表。否则快指针将到达链表尾部,该链表不为环形链表。

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {public:bool hasCycle(ListNode *head) {if(head==NULL||head->next==NULL) return false;ListNode* slow=head;ListNode* fast=head->next;while(slow!=fast){if(fast==NULL) return false;slow=slow->next;fast=fast->next;if(fast!=NULL) fast=fast->next;}return true;}
};
#include<bits/stdc++.h>
/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {public:bool hasCycle(ListNode *head) {if(!head) return false;ListNode* fast = head;ListNode* slow = head;do{if(!fast) return false;fast=fast->next;slow=slow->next;if(fast) fast=fast->next;else return false;}while(fast!=slow);return true;}
};

142 环形链表2

/*
方法一同141题方法一
*/
/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {public:ListNode *detectCycle(ListNode *head) {unordered_set<ListNode*> hash;if(head==NULL||head->next==NULL) return NULL;while(head!=NULL){if(!hash.count(head)) {hash.insert(head);head=head->next;}else return head;}return NULL;}
};

方法二 快慢指针
数学推导

/*
若初始fast=head->next;
数学推导为
2(a+b)=a-1+n(b+c)+b
得
a=(n-1)(b+c)+c-1
所以slow再走一步
*/
/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {public:ListNode *detectCycle(ListNode *head) {if(head==NULL||head->next==NULL) return NULL;ListNode* slow = head;ListNode* fast = head->next;while(slow!=fast){if(fast==NULL) return NULL;slow=slow->next;fast=fast->next;if(fast!=NULL) fast=fast->next;}ListNode* p=head;slow=slow->next;while(p!=slow){p=p->next;slow=slow->next;}return p;     }
};

287 快慢指针

/*
空间复杂度O(1)解法:快慢指针
若想使得slow fast初始值一样,就用do while处理循环部分
*/
class Solution {public:int findDuplicate(vector<int>& nums) {int slow=0;int fast=0;do{slow=nums[slow];fast=nums[nums[fast]];}while(slow!=fast);int p=0;while(p!=slow){slow=nums[slow];p=nums[p];}return p;}
};

25 K个一组翻转链表

class Solution {public:pair<ListNode*,ListNode*> reverseL(ListNode* begin, ListNode* end){   //翻转时需要 pre cur nxt 三个指针:前提要保证end处不为nullListNode* pre = end->next; //把下一个的最前面的节点连接上ListNode* cur = begin;ListNode* nxt = begin->next;while(cur!=end) //这样可以防止nxt越界报错{cur->next=pre;pre = cur;cur = nxt;nxt = nxt->next;}cur->next=pre; //补上最后一个return {end,begin}; //返回当前的头尾指针}ListNode* reverseKGroup(ListNode* head, int k) {ListNode* start = new ListNode(0);start->next = head; //这两步很关键 在翻转链表题中,在首指针前加一个点,后续有用ListNode* cur = start; //当前指针指向这个新建的节点while(cur){int ktmp=k;ListNode* last_end = cur; //保存一下上次的尾节点,以供后续连接使用ListNode* begin=cur->next;ListNode* end; //该轮需要翻转的首尾指针while(ktmp--){cur = cur->next;if(!cur) break;}if(!cur) return start->next; //该轮未满k个 不能写if ktmp>0, 因为=0情况有可能最后一次循环cur==nullelse{end = cur;pair<ListNode*,ListNode*> res = reverseL(begin,end);//连接last_end->next=res.first;//更新curcur=res.second;}}return start->next;}
};

不要求空间复杂度可以用stack

160 相交链表

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*//*
链表1:A-相交点-C
链表2:B-相交点-C
A+C+B=B+C+A
所以遍历1之后马上开始2,与此同时遍历2后马上开始1,两者相交处为焦点*/// 无交点的情况也满足:第二轮都指向null
class Solution {public:ListNode *getIntersectionNode( ListNode* pHead1, ListNode* pHead2) {ListNode* p = pHead1;ListNode* q = pHead2;while(p!=q){p=(p)?p->next:pHead2;q=(q)?q->next:pHead1;}return p;}
};

BFS

200 岛屿数量

时间复杂度O(MN)
空间复杂度O(min(M,N)) 因为队列的长度一直在变 有pop 有push 画图可得长度最值

/*
bfs
遍历矩阵,值为1,岛屿数++,从这个点开始进行bfs遍历
bfs遍历到值为1的点后,把该点值变为0,相当于标记
*/
class Solution {public:int dir[4][2]={{-1,0},{0,1},{1,0},{0,-1}};//二维数组的初始化int numIslands(vector<vector<char>>& grid) {int res=0;int n=grid.size();if(n==0) return 0;int m=grid[0].size();queue<pair<int,int>> que;for(int i=0;i<n;i++){for(int j=0;j<m;j++){if(grid[i][j]=='1'){res++;grid[i][j]='0';que.push(make_pair(i,j));while(!que.empty()){auto cur = que.front();que.pop();for(int a=0;a<4;a++){int newi=cur.first+dir[a][0];int newj=cur.second+dir[a][1];if(newi<0||newj<0||newi>=n||newj>=m||grid[newi][newj]=='0')continue;grid[newi][newj]='0';que.push(make_pair(newi,newj));}}}}}return res;           }
};

DFS

200 岛屿数量

时间复杂度O(MN)
空间复杂度O(MN)

/*
dfs
遍历矩阵,值为1,岛屿数++,从这个点开始进行dfs遍历
dfs边界条件为矩阵边界和值为0,遍历到值为1的点后,把该点值变为0,相当于标记
*/
class Solution {public:int dir[4][2]={{-1,0},{0,1},{1,0},{0,-1}};//二维数组的初始化void dfs(int i, int j, vector<vector<char>>& grid){int n=grid.size();int m=grid[0].size(); if(i<0||j<0||i>=n||j>=m||grid[i][j]=='0') return;grid[i][j]='0';for(int a=0;a<4;a++){dfs(i+dir[a][0],j+dir[a][1],grid);}}int numIslands(vector<vector<char>>& grid) {int res=0;if(grid.size()==0) return 0;for(int i=0;i<grid.size();i++){for(int j=0;j<grid[0].size();j++){if(grid[i][j]=='1'){res++;dfs(i,j,grid);}}}return res;           }
};

DP

动态规划做题步骤明确 dp(i)应该表示什么(二维情况:dp(i)(j));
根据 dp(i)和 dp(i-1) 的关系得出状态转移方程(二维情况:dp[i][j]和dp[i+x][j+y]等)
确定初始条件,如 dp(0)。

70 爬楼梯

/*
DP
dp[i]为i阶楼梯的方法数
dp[1]=1
dp[2]=2
dp[i]=dp[i-1]+dp[i-2](i>3)
*/
class Solution {public:int climbStairs(int n) {int dp[n+5];dp[1]=1;dp[2]=2;for(int i=3;i<=n;i++){dp[i]=dp[i-1]+dp[i-2];}return dp[n];}
};

扩展
不能连续跳两个台阶

假设:
f(x) 表示爬到第 x 级台阶的方案数,
g(x, 1) 表示爬到第 x 级台阶并且最后一步只跨越一个台阶的方案数,
g(x, 2) 表示爬到第 x 级台阶并且最后一步跨越了两个台阶的方案数。由 :
f(x) = g(x, 1)+g(x,2),
g(x, 1) = f(x-1),
g(x, 2) = g(x-2, 1) // 最后一步跨越了两步,那么上一步只能跨越一步得:
f(x) = g(x, 1) + g(x, 2) = f(x-1) + g(x-2, 1) = f(x-1) + f((x-2)-1)= f(x-1) + f(x-3)

5 最长回文子串

/*
dp[i][j]:从s[i]到s[j]的字符串是否为回文子串,是为1,不是为0边界条件:
dp[i][i]=1
dp[i][i+1]=(S[i]==s[i+1])if(s[i]==s[j]) dp[i][j]=dp[i+1][j-1]*/
class Solution {public:string longestPalindrome(string s) {int n = s.size();int dp[n+5][n+5];memset(dp,0,sizeof(dp));string ans;for (int l = 0; l < n; ++l) {for (int i = 0; i + l < n; ++i) {int j = i + l;if(l == 0) dp[i][j] = 1;else if(l == 1) dp[i][j] = (s[i] == s[j]);else if(s[i] == s[j]) dp[i][j] =  dp[i + 1][j - 1];if (dp[i][j] && l + 1 > ans.size()) ans = s.substr(i, l + 1);}}return ans;}
};

121 买卖股票的最佳时机

/*
一次遍历
遍历过程中,记录当前min,并计算prices[i]-min,更新res,并实时更新min
*/
class Solution {public:int maxProfit(vector<int>& prices) {if(!prices.size()) return 0;int min=prices[0];int res=0;for(int i=0;i<prices.size();i++){if(prices[i]<min) min=prices[i];else if(prices[i]>min){int cur=prices[i]-min;res=(cur>res)?cur:res;}}return res;}
};

DP思路

/*
dp
dp[i]表示第i天时的最大收益
dp[i]=max(dp[i-1],prices[i]-min)
*/
class Solution {public:int maxProfit(vector<int>& prices) {if(!prices.size()) return 0;int min=prices[0];int dp[prices.size()+5];dp[0]=0;for(int i=1;i<prices.size();i++){if(prices[i]<min) min=prices[i];dp[i]=max(dp[i-1],prices[i]-min);}return dp[prices.size()-1];}
};

300 最长递增子序列

/*
dp
dp[i]:以nums[i]为结尾的递增子序列长度
dp[i]=max(dp[i],dp[j]+1)  (j从0到i-1遍历 && nums[i]>num[j])
找到最大的dp[x],即为答案
O(n*n)
*/
class Solution {public:int lengthOfLIS(vector<int>& nums) {vector<int> dp (nums.size());dp[0]=1;int maxdp=1;for(int i=1;i<nums.size();i++){dp[i]=1;for(int j=0;j<i;j++){if(nums[i]>nums[j])dp[i]=max(dp[i],dp[j]+1);}maxdp=(dp[i]>maxdp)?dp[i]:maxdp;}return maxdp;}
};

322 零钱兑换(需要后续进一步理解)

关于背包
01背包问题

/*
for i = 1, 2, ..., n: # 枚举前 i 个物品for v = 0, 1, ..., V: # 枚举体积f[i][v] = f[i-1][v]; # 不选第 i 个物品if v >= c[i]:  # 第 i 个物品的体积必须小于 v 才能选f[i][v] = max(f[i][v], f[i-1][v-c[i]] + w[i])
return max(f[n][0...V]) # 返回前 n 个物品的最大值
*/
/*
类比背包问题: 背包总体积 :硬币总金额物品单个体积:硬币的单个金额每个物品的价值:1(币的数量)背包问题是求最多的价值是多少,该题是求最少的价值是多少
*/
class Solution {public:int coinChange(vector<int>& coins, int amount) {int Max = amount + 1;vector<int> dp(amount + 1, Max);dp[0] = 0;for (int j = 0; j <coins.size(); ++j) {for (int i = 1; i <= amount; ++i) {if (coins[j] <= i) dp[i] = min(dp[i], dp[i - coins[j]] + 1);}}return dp[amount] > amount ? -1 : dp[amount];}
};

面试题 08.11. 硬币(需要后续进一步理解)

完全背包

class Solution {private:static constexpr int mod = 1000000007;static constexpr int coins[4] = {25, 10, 5, 1};public:int waysToChange(int n) {vector<int> f(n + 1);f[0] = 1;for (int c = 0; c < 4; ++c) {int coin = coins[c];for (int i = coin; i <= n; ++i) {f[i] = (f[i] + f[i - coin]) % mod;}}return f[n];}
};

887 鸡蛋掉落(阿里面试)

李永乐老师b站视频
详细解析

讨论下为什么双蛋情况第一颗蛋要N,N+N-1,N+N-1+N-2,…这样扔最佳
1.若间隔始终为N,即N,2N,3N。这种情况下,假设说最大代价出现在最大楼层-1的位置,第二颗蛋的扔的次数是不变的,都是N-1,所以每多扔一次第一颗蛋,扔的总数就+1,这样的话楼层越高,次数越多
2.所以,如果我们第一个蛋按N,N+N-1,N+N-1+N-2,…的形式扔,没多扔一次第一个蛋,第二个蛋扔的次数对应的会减一,这就使得无楼层为多高,总的扔蛋数都是固定的。
3.N+N-1+N-2+N-3+…+1=N(N+1)/2=X; X为楼层高;最后到1是因为,这样可以使N尽可能地小,而我们最后扔的次数正是N

二分查找

6010

https://leetcode-cn.com/problems/minimum-time-to-complete-trips/solution/6010-chao-shi-shi-xiang-xiang-er-fen-by-3q7is/

二分模版
while l < r:
m = (l + r) >> 1
if check(m):
l = m + 1
else:
r = m

对于二分查找,一般可用以下模板,注意区别。另外可用类似 m = l + (r - l) / 2的方式解决溢出的问题。

4 寻找两个正序数组的中位数

题解

/*
归并找中位数算法O(m+n)要想log(m+n):二分  先记个思想 具体实现之后有时间再进行
*/class Solution {public:double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {int m = nums1.size();int n = nums2.size();//中位数 = (left + right)/2int left = (m + n + 1) / 2;int right = (m + n + 2) / 2;return (findKth(nums1, 0, nums2, 0, left) + findKth(nums1, 0, nums2, 0, right)) / 2.0;}//在两个有序数组中找到第k个元素(例如找第一个元素,k=1,即nums[0])//i: nums1的起始位置 j: nums2的起始位置(i,j都是从0开始)int findKth(vector<int>& nums1, int i, vector<int>& nums2, int j, int k){//若nums1为空(或是说其中数字全被淘汰了)//在nums2中找第k个元素,此时nums2起始位置是j,所以是j+k-1if(i >= nums1.size())    return nums2[j + k - 1];//nums2同理if(j >= nums2.size())    return nums1[i + k - 1];//递归出口if(k == 1)  return min(nums1[i], nums2[j]);//这两个数组的第K/2小的数字,若不足k/2个数字则赋值整型最大值,以便淘汰另一数组的前k/2个数字int midVal1 = (i + k/2 - 1 < nums1.size()) ? nums1[i + k/2 - 1] : INT_MAX;int midVal2 = (j + k/2 - 1 < nums2.size()) ? nums2[j + k/2 - 1] : INT_MAX;//二分法核心部分if(midVal1 < midVal2)return findKth(nums1, i + k/2, nums2, j, k - k/2);elsereturn findKth(nums1, i, nums2, j + k/2, k - k/2);}
};

33 搜索旋转排序数组

题解

/*
二分
通过比较nums[left]和nums[mid]来确认旋转点与mid的关系
进而分类讨论
1.左侧单调递增:若target在左侧范围内,则right=mid,否则left=mid
2.右侧单调递增:若target在右侧范围内,则left=mid,否则right=mid
注意:二分时要设为mid-1和mid+1!
*/
//因为只有单调递增的部分能确定target是在这个范围内 所以要分类讨论
class Solution {public:int search(vector<int>& nums, int target) {int n=nums.size();if(!n) return -1;int left=0;int right=n-1;int mid =(left+right)/2;while(left<=right){if(target == nums[mid]) return mid;if(nums[left]<=nums[mid]){if(nums[left]<=target&&target<nums[mid])right = mid-1;else left = mid+1;}else{if(nums[mid]<target&&target<=nums[right])left = mid+1;else right = mid-1;}mid = (left+right)/2;}return -1;}
};

字符串

28 KMP 实现 strStr()

KMP: 一种改进的字符串匹配算法,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。它的时间复杂度是O(m+n) <正常暴力匹配的复杂度为O(mn)>
KMP算法详谈
求next时注意,next[i](i初值为0)的值是模式串从a[0]到a[i-1](不包括a[i])所对应的公共前后缀最长长度。同时注意,求模式串a[i-1]的公共前后缀最长长度时,前后缀的长度要小于a[i-1]的长度。例如字串为abcd时,求next[2],首先求的是ab的公共前后缀最长长度,对于ab来说,前缀只有a,后缀只有b,所以next[2]==0
i为遍历过程中主串S上的指针位置,j为遍历过程中模式串P上的指针位置。
当主串S与模式串P失配时,j=next[j],P向右移动j - next[j]。也就是当模式串P后缀pj-kpj-k+1…pj-1与主串S的si-ksi-k+1…si-1匹配成功,但是pj和si失配时,因为next[j]=k,相当于在不包含pj的模式串中有最大长度为k的相同前后缀,也就是p0p1…pk-1 = pj-kpj-k+1…pj-1,所以令j = next[j],让模式串右移j - next[j]位,使模式串p0p1…pk-1 与主串si-ksi-k+1…si-1对齐,让pk和si继续匹配。
在代码中,模式串的右移其实不需要表现出来,只要有当前i和j的位置即可比较。

明白思想即可,代码之后有时间再细抠

class Solution {public:void getNext(int* next, const string& s) {int j = -1;next[0] = j;for(int i = 1; i < s.size(); i++) { // 注意i从1开始while (j >= 0 && s[i] != s[j + 1]) { // 前后缀不相同了j = next[j]; // 向前回溯}if (s[i] == s[j + 1]) { // 找到相同的前后缀j++;}next[i] = j; // 将j(前缀的长度)赋给next[i]}}int strStr(string haystack, string needle) {if (needle.size() == 0) {return 0;}int next[needle.size()];getNext(next, needle);int j = -1; // // 因为next数组里记录的起始位置为-1for (int i = 0; i < haystack.size(); i++) { // 注意i就从0开始while(j >= 0 && haystack[i] != needle[j + 1]) { // 不匹配j = next[j]; // j 寻找之前匹配的位置}if (haystack[i] == needle[j + 1]) { // 匹配,j和i同时向后移动j++; // i的增加在for循环里}if (j == (needle.size() - 1) ) { // 文本串s里出现了模式串treturn (i - needle.size() + 1);}}return -1;}
};

简化记忆版

#include <bits/stdc++.h>
using namespace std;
#define maxn int(1e6 + 10)
int nxt[maxn];
vector<int> ans;
void getNext(const string& s) {int len = s.size();int i = 0;int j = -1;nxt[0] = -1;while (i < len) {if (j == -1 || s[i] == s[j]) {++i;++j;nxt[i] = j;} else {j = nxt[j];}}
}
// s2在s1中第一次出现的位置,否则返回-1
int findIdx(const string& s1, const string& s2) {int l1 = s1.size(), l2 = s2.size();int i = 0, j = 0;while (i < l1 && j < l2) {if (j == -1 || s1[i] == s2[j]) {++i;++j;} else {j = nxt[j];}}if (j == l2) {return i - j;} else {return -1;}
}
// 在可重叠的情况下,s2在s1中出现的次数
int findCnt1(const string& s1, const string& s2) {int l1 = s1.size(), l2 = s2.size();int i = 0, j = 0;int cnt = 0;while (i < l1 && j < l2) {if (j == -1 || s1[i] == s2[j]) {++i;++j;} else {j = nxt[j];}if (j == l2) {++cnt;ans.push_back(i - j);j = nxt[j];}}return cnt;
}
// 在不重叠的情况下,s2在s1中出现的次数
int findCnt2(const string& s1, const string& s2) {int l1 = s1.size(), l2 = s2.size();int i = 0, j = 0;int cnt = 0;while (i < l1 && j < l2) {if (j == -1 || s1[i] == s2[j]) {++i;++j;} else {j = nxt[j];}if (j == l2) {++cnt;j = 0;}}return cnt;
}
int main() {ios::sync_with_stdio(0);string s1;string s2;cin >> s1 >> s2;getNext(s2);assert(nxt[0] == -1);int len = s2.size();findCnt1(s1, s2);for (auto e : ans) {cout << e + 1 << endl;}for (int i = 1; i <= len; ++i) {cout << nxt[i];if (i != len) {cout << " ";}}// cout << findIdx(s1, s2) << endl;// cout << findCnt1(s1, s2) << endl;// cout << findCnt2(s1, s2) << endl;return 0;
}

1143 最长公共子序列

详细解析
要注意 text中的i和dp中的i查一位
text[0]是text的第一个元素
dp[0][x]指当text1为空串时的dp值

/*
dp
dp[i][j]:text1前i个字符和text2前j个字符的最长公共子序列的长度dp[0][j]=dp[i][0]=0: 当text1为空串或text2为空串时if(text[i]==text[j]) dp[i][j]=dp[i-1][j-1]+1
else dp[i][j]=max(dp[i-1][j],dp[i][j-1])从左往右,从上往下遍历dp数组 [text1.size()+1][text2.size()+1] 初始化外层一圈0,0
*/
class Solution {public:int longestCommonSubsequence(string text1, string text2) {vector<vector<int>> dp(text1.size()+1,vector<int>(text2.size()+1,0));//二重vector的初始化for(int i=0;i<text1.size();i++)for(int j=0;j<text2.size();j++){if(text1[i]==text2[j]) dp[i+1][j+1]=dp[i][j]+1;else dp[i+1][j+1]=max(dp[i+1][j],dp[i][j+1]);}return dp[text1.size()][text2.size()];}
};

如何输出该子序列

/*
dp
dp[i][j]:text1前i个字符和text2前j个字符的最长公共子序列的长度dp[0][j]=dp[i][0]=0: 当text1为空串或text2为空串时if(text[i]==text[j]) dp[i][j]=dp[i-1][j-1]+1
else dp[i][j]=max(dp[i-1][j],dp[i][j-1])从左往右,从上往下遍历dp数组 [text1.size()+1][text2.size()+1] 初始化外层一圈0,0
*/
class Solution {public:int longestCommonSubsequence(string text1, string text2) {vector<vector<int>> dp(text1.size()+1,vector<int>(text2.size()+1,0));//二重vector的初始化for(int i=0;i<text1.size();i++)for(int j=0;j<text2.size();j++){if(text1[i]==text2[j]) dp[i+1][j+1]=dp[i][j]+1;else dp[i+1][j+1]=max(dp[i+1][j],dp[i][j+1]);}//输出子序列 : 只有当斜着增加的时候,记录该点的字符int l1=text1.size();int l2=text2.size();string res;while(dp[l1][l2]>0){if(dp[l1][l2]==dp[l1-1][l2]) l1--;else if(dp[l1][l2]==dp[l1][l2-1]) l2--;else //l1 l2 ==l1-1 l2-1 {res=text1[l1-1]+res;l1--;l2--;}}cout<<res<<endl;return dp[text1.size()][text2.size()];}
};

最长公共子串

牛客链接

如何输出该子串:直接在str1上取sub即可

/*
与最长公共子序列的不同之处在于
if(str1[i]!=str2[j]) dp[i+1][j+1]=0;
*/
class Solution {public:/*** longest common substring* @param str1 string字符串 the string* @param str2 string字符串 the string* @return string字符串*/string LCS(string str1, string str2) {// write code hereint l1=str1.size();int l2=str2.size();vector<vector<int>> dp(l1+1,vector<int>(l2+1,0));pair<int,int> res = make_pair(0,0);for(int i=0;i<l1;i++)for(int j=0;j<l2;j++){if(str1[i]==str2[j]) dp[i+1][j+1]=dp[i][j]+1;else dp[i+1][j+1]=0;if(dp[i+1][j+1]>dp[res.first][res.second]) res={i+1,j+1};}int n=dp[res.first][res.second];string x;x=str1.substr(res.first-n,n);return x;}
};

14 最长公共前缀

纵向查找

class Solution {public:string longestCommonPrefix(vector<string>& strs) {if(strs.size()==0) return"";int len=0;bool mark = true;int s=300;for(auto elem:strs) s=(elem.size()<s)?elem.size():s;for(int i=0;i<s;i++){char c = strs[0][i]; mark = true;for(auto elem:strs){if(elem[i]!=c) {mark=false;break;}   }if(mark) len++;else break;}string ans = strs[0].substr(0,len);return ans;}
};

简化:先str排序,再比较第一个str和最后一个str

大数加法 NC1

class Solution {public:/*** 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可* 计算两个数之和* @param s string字符串 表示第一个整数* @param t string字符串 表示第二个整数* @return string字符串*/string solve(string s, string t) {// write code herestring ans="";int sS = s.size();int tS = t.size();int carry = 0;for( int i=sS-1,j=tS-1; i>=0||j>=0; i--,j-- ){int a = i>=0 ? s[i]-'0' : 0;int b = j>=0 ? t[j]-'0' : 0;int sum = a+b;ans = to_string((sum+carry) % 10 ) + ans;if( sum+carry>=10 ){carry = 1;} else {carry = 0;}}if( carry )ans = '1' + ans;return ans;}
};

字符串中的数排序 美团笔试题

这道题的重点是,当我们抠出来数字的string时,不用转化成数字,再sort。而是直接重写cmp,对string进行sort,这样可以避免string过长的情况

#include<bits/stdc++.h>
using namespace std;
string x(string s)
{int n=s.size();if(n==1) return s;int i;for(i=0;i<n;i++){if(s[i]!='0') break;}if(i==n) return "0";return s.substr(i,n-i);
}
bool cmp(string s1,string s2)
{int n1=s1.size();int n2=s2.size();if(n1==n2) return s1<s2;return n1<n2;}
int main()
{string s;getline(cin,s);int n=s.size();string buff;vector<string> res;for(int i=0;i<n;i++){if(buff.size()>0&&(s[i]>'9'||s[i]<'0')){//cout<<x(buff)<<endl;res.push_back(x(buff));buff="";}else if(s[i]<='9'&&s[i]>='0'){buff+=s[i];}}//最后一个buff特判if(buff.size()>0) res.push_back(x(buff));//cout<<x(buff)<<endl;sort(res.begin(),res.end(),cmp);for(auto elem:res){cout<<elem<<endl;}return 0;
}

map

特征提取 字节2019春招题

#include<bits/stdc++.h>
using namespace std;
int main()
{int N;cin>>N;while(N--){int M;cin>>M;int res=0;map<pair<int,int>,pair<int,int>> mp; //key:存特征     //value:first存这个状态的时刻,second存这个状态到这个时刻持续的时间for(int i=0;i<M;i++){int l;cin>>l;for(int j=0;j<l;j++){int a,b;cin>>a>>b;pair<int,int> cur = make_pair(a,b);// cout<<"cur="<<cur.first<<" "<<cur.second<<endl;if(!mp.count(cur)) mp[cur]=make_pair(i,1);else if(mp[cur].first==i-1) mp[cur]=make_pair(i,++mp[cur].second);else mp[cur]=make_pair(i,1);// cout<<"mp[cur]="<< mp[cur].first <<" "<<mp[cur].second<<endl;res=(mp[cur].second>res)?mp[cur].second:res;// cout<<"res="<<res<<endl;}}cout<<res<<endl;}return 0;
}

数学推导

1411 给 N x 3 网格图涂色的方案数(阿里笔试)

解析

图论

815 公交线路(阿里笔试)

还有错误,不过思路正确,以后有时间继续调

//把每一个环路当作一个点,环路和环路间的距离用邻接表表示,dfs求距离
class Solution {public:int n;vector<vector<int> > table = vector<vector<int> >(505,vector<int>(505,1000)); //vector全局变量要这么写vector<bool> visit=vector<bool>(505,false);int nstart;int nend;int ans=550;void dfs(int i,int res) //第一个参数为当前到的点,第二个参数为到达该点用的换乘次数{if(table[i][nend]==0){ans = min(ans,res);} for(int j=0;j<505;j++){if(table[i][j]==0&&visit[j]==false) {visit[j]=true;dfs(j,res);visit[j]=false;}else if(table[i][j]==1&&visit[j]==false) {visit[j]=true;dfs(j,res+1);visit[j]=false;}}}int numBusesToDestination(vector<vector<int>>& routes, int source, int target) {unordered_map<int,vector<int>> hash;n=routes.size();//存一个hash:key值为每一个节点,value为这个节点所在的所有环路组成的vectorfor(int i=0;i<n;i++){for(int j=0;j<routes[i].size();j++){if(!hash.count(routes[i][j])) hash[routes[i][j]]={i};else hash[routes[i][j]].push_back(i);}//把一个routes里节点的距离标成0if(routes[i].size()==1) continue;for(int j=0;j<routes[i].size();j++){int pre=routes[i][j];for(int k=j+1;k<routes[i].size();k++){int cur = routes[i][k];table[pre][cur]=0;table[cur][pre]=0;}}}//遍历hash的每一个节点(key),把对应的value里vector的每一项间的距离设为1(之前标成0的除外)for(auto elem:hash){vector<int> tmp = elem.second;for(int m=0;m<tmp.size();m++){for(int n=m+1;n<tmp.size();n++){if(!table[m][n]) {table[m][n]=1;table[n][m]=1;}}}}vector<int> start=hash[source];vector<int> end=hash[target];//设置起始点和终止点,由于起始点/终止点可能属于多个环路,另建两个新环路,一个到start中所有环路的距离为0,一个到end中所有环路的距离为0,求这两个环路间的距离nstart=n;nend=n+1;for(auto elem:start){table[nstart][elem]=0;table[elem][nstart]=0;}for(auto elem:end){table[nend][elem]=0;table[elem][nend]=0;}//用dfs求table中nstart到nend的距离int res=0;visit[nstart]=true;dfs(nstart,res);return ans;}
};

329 矩阵中的最长递增路径 再看一下

class Solution {public:static constexpr int dirs[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};int rows, columns;int longestIncreasingPath(vector< vector<int> > &matrix) {if (matrix.size() == 0 || matrix[0].size() == 0) {return 0;}rows = matrix.size();columns = matrix[0].size();auto memo = vector< vector<int> > (rows, vector <int> (columns));int ans = 0;for (int i = 0; i < rows; ++i) {for (int j = 0; j < columns; ++j) {ans = max(ans, dfs(matrix, i, j, memo));}}return ans;}int dfs(vector< vector<int> > &matrix, int row, int column, vector< vector<int> > &memo) {if (memo[row][column] != 0) {return memo[row][column];}++memo[row][column];for (int i = 0; i < 4; ++i) {int newRow = row + dirs[i][0], newColumn = column + dirs[i][1];if (newRow >= 0 && newRow < rows && newColumn >= 0 && newColumn < columns && matrix[newRow][newColumn] < matrix[row][column]) {memo[row][column] = max(memo[row][column], dfs(matrix, newRow, newColumn, memo) + 1);}return memo[row][column];}
};

记忆化搜索 美团笔试

之后研究一下

#include<bits/stdc++.h>
#define N 100010
using namespace std;
struct e{int v,w;e(int v,int w):v(v),w(w){}
};
vector<int> vec[N];
int dp[N];
bool vis[N];
int h[N];int dfs(int x)
{if(dp[x]!=-1){return dp[x];}dp[x]=1;for(auto adj:vec[x]){int v=adj;if(vis[v]||h[v]>h[x]) continue;vis[v]=1;dp[x]=max(dp[x],dfs(v)+1);vis[v]=0;}return dp[x];
}
int main()
{int n,m;cin>>n>>m;for(int i=1;i<=n;i++){cin>>h[i];}for(int i=0;i<m;i++){int u,v;cin>>u>>v;vec[u].push_back(v);vec[v].push_back(u);}for(int i=1;i<=n;i++){dp[i]=-1;vis[i]=0;}int ans=1;for(int i=1;i<=n;i++){vis[i]=1;ans=max(ans,dfs(i));vis[i]=0;}cout<<ans<<endl;return 0;
}

设计

146 LRU缓存机制

使用了STL当中的list和unordered_map

#include<bits/stdc++.h>
using namespace std;
class LRUCache {private:list<pair<int, int>> cache;   < @note pair[key]=valueunordered_map<int, list<pair<int, int>>::iterator> key2node;int cap;                      < @note 最大容量public:LRUCache(int capacity) : cap(capacity) {}int get(int key) {if (!key2node.count(key)) {return -1;}pair<int, int> node = *key2node[key];cache.erase(key2node[key]); < @note 将节点移到链表头部并更新mapcache.push_front(node);key2node[key] = cache.begin();return node.second;}void put(int key, int val) {auto newNode = make_pair(key, val);if (key2node.count(key)) {  < @note 若该节点已存在,则删除旧的节点cache.erase(key2node[key]);} else {if (cap == cache.size()) {key2node.erase(cache.back().first);cache.pop_back();       < @note 删除链表最后一个数据}}cache.push_front(newNode);  < @node 插入新的节点到头部key2node[key] = cache.begin();}
};/*** Your LRUCache object will be instantiated and called as such:* LRUCache* obj = new LRUCache(capacity);* int param_1 = obj->get(key);* obj->put(key,value);*/

LeetCode之路相关推荐

  1. 开启LeetCode之路

    终于有时间开始每天坚持做LeetCode题,博主现在以在CSDN上撰写解题思路为自我监督方式,坚持每天将当天的题目的解题思路记录下来. LeetCode刷题路线:博主将计划按tag顺序做题,先以简单和 ...

  2. LeetCode 1052 爱生气的书店老板 HERODING的LeetCode之路

    今天,书店老板有一家店打算试营业 customers.length 分钟.每分钟都有一些顾客(customers[i])会进入书店,所有这些顾客都会在那一分钟结束后离开. 在某些时候,书店老板会生气. ...

  3. LeetCode之路:122. Best Time to Buy and Sell Stock II

    一.引言 这是一道非常有趣的题目! 这是一道非常有趣的题目!! 这是一道非常有趣的题目!!! 重要的事情先说三遍 : ) 好了,接下来让我们看看这道题: Say you have an array f ...

  4. LeetCode 1091 二进制矩阵中的最短路径问题[BFS 队列] HERODING的LeetCode之路

    解题思路: 使用广度优先遍历的方法,可以遍历下一步能走的位置,一般用到广度优先就离不开队列,队列存储着当前轮次能够走的位置,每一轮都要将能走的长度++,如果队列无元素,说明无处可走,此时没有到终点就直 ...

  5. LeetCode.874-走路机器人模拟(Walking Robot Simulation)

    这是悦乐书的第335次更新,第360篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第205题(顺位题号是874).网格上的机器人从点(0,0)开始并朝北.机器人可以接收三 ...

  6. LeetCode之路:520. Detect Capital

    一.引言 这道题有关于处理字符的大小写问题,对于熟悉字符的大小写处理函数非常有帮助. 这里粘出题目信息: Given a word, you need to judge whether the usa ...

  7. LeetCode 649 Dota2参议院 HERODING的LeetCode之路

    Dota2 的世界里有两个阵营:Radiant(天辉)和 Dire(夜魇) Dota2 参议院由来自两派的参议员组成.现在参议院希望对一个 Dota2 游戏里的改变作出决定.他们以一个基于轮为过程的投 ...

  8. LeetCode 13 罗马数字转整数[找规律 多条件判断] HERODING的LeetCode之路

    解题思路: 两种解题方法,第一种就是硬判断,把所有情况都考虑进去,一共十三种条件,怎么说还是有点麻烦的,代码如下: class Solution {public:int romanToInt(stri ...

  9. LeetCode 203 移除链表元素 HERODING的LeetCode之路

    给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 . 示例 1: 输入:head = [1,2,6,3,4,5, ...

最新文章

  1. hilbert变换_希尔伯特变换 matlab实现
  2. 【专栏必读】王道考研408操作系统万字笔记(从学生角度辅助大家理解):各章节导航及思维导图
  3. 信息学奥赛一本通(1032:大象喝水查)
  4. 用DELETE删除的文件怎么免费找回不用购买不用注册码
  5. 开课吧:人工智能技术会如何影响青年就业
  6. css3兼容IE8的方案 各个ie的hack
  7. Linux 查看网络速率
  8. Active Directory之强制占有操作主机
  9. Excel文件解密软件
  10. poi导出execl固定表头表尾
  11. python批量图片进行双三插值BiCubic后,输出保存(亲测可用)。
  12. 2019年ACM大赛:北大清华无缘前十,莫斯科大学第一
  13. 晨晖c语言,晨晖C语言学习系统
  14. 视频剪切合并器如何分割音频文件
  15. 深入理解Plasma(四)Plasma Cash
  16. css html文字淡入淡出,Css淡入淡出
  17. asterisk连接sip139网络电话
  18. 消费者满意度调查方案
  19. 初识自定义View-View的弹性滑动
  20. Aurora8B10B IP使用 -04- IP例程应用实例

热门文章

  1. DE2-115学习计划
  2. DCDC模块电源的选择与应用 选择篇
  3. JS获取当前时间作为订单编号
  4. YUV与RGB间的转换公式
  5. 真正的头号玩家——游戏AI
  6. [转]LaTeX中字体设置总结
  7. 二进制转换成十六进制输出的C代码
  8. 河南省漯河市谷歌高清卫星地图下载
  9. 胖客户端与瘦客户端的区别?
  10. 西软服务器显示数据库异常,异常:数据库异常