p76 把字符串转换成整数
strToInt()
主要思路:
1. 先判断正负号的存在sign
2. 从正负号之后开始遍历字符串
if(str[i]>='0' && str[i]<='9'){
num+=num*10+str[i]-'0';
}
p3 找出数组中重复的数字
主要思路:
就是num[i]与nums[nums[i]]之间的关系
p3不修改数组找重复数组
方法一
暴力
方法二
二分法
for(int i=0;i<nums.size();i++){
// 先统计左区间值在[l,mid]区间有多少个数字
记录在count
}
// 根据定理,[count单位长度有count+1个数,那么必定有一个数重复]
if(count>mid-l+1) r=mid; // 重复数字在左区间,否则在右区间
else l=mid+1;
p3 二维数组中的查找
主要思路:
- 二维数组是一个行递增,列递增的数组
- 因此从数组的右上角开始查找
i=0,j=size()-1
if(array[i][j]>target) j--; // 此列不考虑
else i++; // 此行不考虑
p4替换空格
主要思路:
- 先统计空格的数量
- 其次再从字符串的末尾覆盖原字符串,遇到空格则用%20代替
需要注意的是,新的字符串的长度变为str.size()+2*count;
p5 从尾到头打印链表2
主要思路:
1. 先用stack去保存链表顺序输出的value
2. 在把stack出栈给vector,vector里面即是逆序
另一种思路,直接用vector去承接链表的值,然后再返回vector(rbegin(),rend());
p6 重建二叉树
主要思路:
二叉树的问题一般都是分治思想,递归去做,因为二叉树本身就是递归定义的
整体的思路是
- 前序序列获取根节点的信息,映射到中序序列,在中序序列中可以得到左右子树的长度
- 将左右子序的长度输入前序序列,就可以得到左右子树在前序序列中的索引
- 前序序列中左右子树的第一个节点为根节点,把这个根节点传入中序序列中
- 递归
unorded_map<int,int> map; // map['中序序列的元素']=index; // index为在中序序列中的索引号
TreeNode* buildTree(vector<int>& preorder, vector<int>& inordered){
int n = preorder.size(); // 序列的长度
// 将中序序列的位置信息用哈希表存储,便于查找根节点
for(int i =0; i<inorder.size();++i){
map[inorder[i]]=i; // map[key]=value
}
return dfs(preordered, inordered,0,n-1,0,n-1); // 前序序列,中序序列,前序的左右边界相对于前序序列),中序的左右边界(相对于中序序列))
}
TreeNode* dfs(vector<int> pre, vector<int> vin, int pl, int pr, int vl, int vr){
// 前序 根左右 pre[pl]:根节点
// 中序 左根右
// base case
if(pl>pr) return NULL; // 到叶子节点了:当pl==pr时就到叶子节点了,在这里返回叶子节点的左右节点
// 为什么是base case
// 需要看递归论证
// 因为你在递归中传入的是:pl+1,pl+k
// 当到叶子节点时,k==0,此时pl+1>pl
// 先序遍历重建二叉树
// 找根节点
TreeNode* root = new TreeNode(per[pl]);
// 左子树的长度(在中序序列中计算)
int k = map[pre[pl]]-vl; // 根节点在中序中的index/右边界-左(右)子树在中序序列的左边界
// 左子树递归:传参结合画图
root->left=dfs(pre,vin,pl+1/*左子树左边界在pre中*/,pl+k/*左子树右边界pre中*/,vl/*左子树左边界在vin中*/,vl+k-1/*左子树右边界在vin中*/);
// 右子树递归
root->right=dfs(pre,vin,pl+k+1,pr,vl)
}
p9 用两个栈实现队列
主要思路:
- 队列:管道, 栈:杯子
- 实现队列的push(),pop()功能,主要是通过两个stack来实现pop()
具体操作如下:
push(){
stack1<int>.push()
}
pop(){
copy_stack(stack1,stack2);
// stack1->stack2
auto top_ele = stack2.top();
copy_stack(stack2,stack1);
return top_ele;
}
p10 旋转数组的最小数字
主要思路:
二分法用在具有两面性质的数组上,比如左区间都大于等于某个数,右区间都小于某个数,这样便就具有了某种性质
因此为了让数组具有二分的性质,先要对右区间末尾的数进行去重
while(nums[n=size()-1]==nums[0]) n--;
// base case
// 如果去重后是单调区间,直接返回(因为如果具有二段性,那么此时右区间都<nums[0])
if(nums[n]>nums[0]) return nums[0]
// 开始二分
int l=0,r=n;
while(l<r)}{
int mid = (l+r)>>2;
if(nums[mid]>nums[0]/*说明mid在左区间*/){
// 缩小到右区间的第一个数即旋转数组最小数
l=mid+1;
}
else if(nums[mid]<nums[0]){
r=mid; // 缩小区间到右区间的第一个数即旋转数组最小数
}
return mid;
}
p11 矩阵中的路径(dfs+回溯)
主要思路:
dfs+回溯
还有就是char *matrix是一维,怎么取元素,其实就相当于将二维matrix按行首尾相接铺开
int a = x+dx[i];
int b = y+dy[j];
matrix[a*cols+b]; // 取到按vector<vector<int>> matrix[a][b]定义的元素
// 其实关于dfs()其实需要明确的是dfs中的递归如何写
// 至于回溯,那就是为了减小内存消耗,递归树在某一支路没有搜到结果,往回搜索时接着上一个父节点往另一个分支取搜索
P12 机器人的运动范围(BFS+条件判断)
主要思路:
BFS的一些模板往上套
// 初始化队列
queue<pair<int,int>> q;
q.push({0,0});
vector<vector<bool>> visited(m,vector<int> n); // 是否遍历
// 开始BFS
while(q.size()){
// dx={0,1,0-1};
// dy={1,0,-1,0};
auto tmp = q.front();
q.pop();
x=tmp.first;
y=tmp.second;
for(i=4){
x=x+dx;
y=y+dy;
if(!inArea() && digit_sum>k) continue;
}
}
p13 剪绳子
主要思路:
数学公式方法
动态规划(比较难)
p15 二进制中1的个数
主要思路:
- 将int n转为unsigned int n;
- 对于n, 统计n中1的个数
int n1 =9;
unsigned int n = n1;
int count = 0;
while (n)
{
if (n & 1)
count++;
n = n >> 1;
}
cout << count << endl;
return 0;
p15 数值的整数次方
主要思路:
如果是负数,连乘之后要取倒数
链表
p17 删除链表中重复的节点
主要思路:
做链表的题一定要画图
- 建立虚拟头结点
- 双指针法
整个链表分为三段
p指针指向前一段链表(没有重复节点)的最后一位
中间段为跳过的重复链表
q指针指向新的链表的第一位
定好位后将p->next=q,就相当于删除了重复链表
ListNode* deleteDuplication(ListNode* head){
auto dummy = new ListNode(-1);
dummy->next=head;
auto p=dummy;
while(p->next){
auto q=p->next;
while(q && p->next->val==q->val) q=q->next; // p->next与q之间为重复节点,q不断向后跳
if(p->next->next==q) p=p->next; // 这两个节点之间的距离是否是1,是1说明经历了上面while一次,中间没有重复数字移动p
else p->next=q; // p,q之间距离不止1,说明中间有重复数字,直接指向下一个元素
}
return dummy->next;
}
参考牛客网的题解,感觉更易懂
if(pHead==null || pHead.next==null){return pHead;}
ListNode dummy = new ListNode(0);
dummy.next = pHead;
ListNode pre = dummy;
ListNode last = dummy.next;
while(last!=null){
if(last.next!=null && last.val==last.next.val){
// 找到最后的一个相同节点
while(last.next!=null && last.val == last.next.val){
last = last.next;
}
}
else{
pre=pre.next;
last=last.next;
}
}
return dummy.next;
p21 链表中的倒数第k个节点
ListNode* kthNode(ListNode* pHead, int k){
int length;
ListNode* tmp=pHead;
while(tmp){
length++;
tmp=tmp->next;
}
k=k%length;
ListNode* first, second=pHead;
while(k>0){
first=first->next;
k--;
}
// 快慢指针同时出发
while(first->next){
first=first->next;
second=second->next;
}
return second;
}
p22 链表中环的入口结点
ListNode* circleNode(ListNode* pHead){
ListNode* fast,*slow=pHead;
while(fast){
//fast=fast->next->next; 如果fast->next为null,则fast->next->next不能访问
fast=fast->next;
slow=slow->next;
if(fast) fast=fast->next; // 正确的快指针的走法
if(fast==slow){
// 在环中相遇,回到出发点
slow = pHead;
while(fast!=slow){
slow=slow->next;
fast=fast->next;
}
return fast;
}
}
return nullptr;
}
p24 合并两个排序的链表
主要思路:
归并排序的思想
然后链表的题一定要画图才能有思路
ListNode *sortGuibing(ListNode *phead1, ListNode *phead2)
{
ListNode *newHead = new ListNode(-1);
auto cur = newHead;
while (phead1 && phead2)
{
if (phead1->val <= phead2->val)
{
cur->next = phead1;
cur = phead1;
phead1 = phead1->next;
}
else
{
cur->next = phead2;
cur = phead2;
phead2 = phead2->next;
}
}
if (phead1 /*说明pHead2先走到终点*/)
{
cur->next = phead1;
}
else
{
cur->next = phead2;
}
return newHead->next;
}
p55 两个链表的第一个公共节点
ListNode *FindFirstCommonNode(ListNode *pHead1, ListNode *pHead2)
{
auto p1 = pHead1;
auto p2 = pHead2;
while (p1 != p2)
{
p1 = p1->next;
p2 = p2->next;
if (!p1)
p1 = pHead2;
if (!p2)
p2 = pHead1;
}
return p1;
}
数组
p21 调整数组顺序使奇数位于偶数前面
主要的思路:
两种方法:
方法一:
分别用队列保存奇数和偶数
然后再分别将队列push_back到数组中,
这个方法效率低,也重新开辟了空间,但是比较容易想到
方法二:
冒泡排序的思路,冒泡排序具有稳定性,所以采用
// 排序前后的稳定性
for(int i=0;i<array.size();++i){
for(int j=array.size()-1;j>i;--j){
if(array[j]%2==1 && array[j-1]%2==0){
swap(array[j],array[j-1]);
}
}
}
动态规划
p43 连续子数组的最大和(面试高频)
主要思路:
这个用动态规划来解释可能会更明了
dp[i]:以 nums[i] 为结尾的最大子数组和为 dp[i]定义非常关键
int FindGreatestSumOfSubArray(vector<int> array){
int n = num.size();
if(0==n) return 0;
vector<int> dp(n);
// base case
dp[0]=nums[0];
// 状态转移方程
for(int i=1;i<n;++i){
dp[i]=max(nums[i],dp[i-1]+nums[i]);
}
// 结果,dp[i]最大值
int res =INI_MIN;
for(int i=0;i<n;++i){
res=max(res,dp[i]);
}
return res;
}