求区间大小的时候,可以直接设置一个res(int),每次计算的时候如果大于res,则动态更新,否则res不变
求区间是什么的时候,需要设置一个vector temp与vector res,当temp.size()>res.size()的时候更新res
子区间为回文串
子区间为上升区间
动态规划,dp[i]存储截止i为止的最长上升序列
无重复的最长子串
重复的最长子串(可以修改K个字符)
数组中的重复数字
1.时间优先,哈希表 时间On,空间On
2.空间优先,鸽巢算法,时间On,空间O1,(改变了数组)
3.不能改变数组的空间优先,范围二分,只适用于n个数,它们的范围是1~n-1,即可以通过某范围内数字的数量判断其中是否有重复的,时间Onlogn,空间O1,未改变数组
//空间优先O1,时间Onlogn,未改变了数组,二分法,
//应用条件:n大小的数组每个数的值都在1-n-1中间,可以根据某个范围内数的总数判断该区间是否有重复的数,
//如果每个数的值在0-n-1中间,则不能用此方法判断
IP
字母
下一个排列
中位数
n为奇数k=n/2+1,即是数组中位数
n为偶数,k1=n/2与n/2+1两个数的平均数是中位数
优先级队列(on),空间on
排序找第N个数(nlogn),空间O1
只能买卖一次,求出每个股票与前一天的差,求该数组的最大连续区间和
int pre_sum=0;
int res=INT_MIN;//(允许结果为负,否则res=0)
int min_pre_sum=0; //保证初始的时候sum为0,才可以把第一个数也加
进去,计算res的时候也得先算一下第一个数
for(int i=0;i
无限制买卖,可以用动态规划dp[i][0]与dp[i][1]表示当天是否持有
只买卖两次
分区间,复杂度On^2
其他
递归
非递归
dfs
前序遍历
中序遍历
后序遍历
bfs
分行的层序遍历
队列
不分行的层序遍历
之字形层序遍历
最大深度
分治递归
层序遍历求层数
最小深度
三种情况
左节点(包括左节点的所有子节点)小于根节点,右节点(包括右节点的所有子节点)大于根节点
判断一个树是否是二叉搜索树
任意节点左右深度差小于等于1
判断一个树是否是平衡二叉树
class Solution {//遍历一边,递归
public:
ListNode* deleteNode(ListNode* head, int val) {
if(head= =nullptr)
return nullptr;
if(head->val==val)
return head->next;
head->next=deleteNode(head->next,val);
return head;
}
};
- 注意是否为最后一个结点
删除链表的倒数第K个节点
删除排序链表重复的节点(留一个)
删除排序链表重复的节点(都删除)
不修改链表——利用一个栈
从头到尾
从m到n
一个未排序链表
两个排序链表
K个排序链表
```cpp
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
int n=lists.size();
if (n = =0)
return nullptr;
return mergeHelper(lists,0,lists.size()-1);
}
private:
ListNode*mergeHelper(vector<ListNode*>& lists,int left,int right)
{
if(left==right)
return lists[left];
int mid=left+(right-left)/2;
ListNode* l1=mergeHelper(lists,left,mid);
ListNode* l2=mergeHelper(lists,mid+1,right);
return mergeTwo(l1,l2);
}
ListNode* mergeTwo(ListNode* l1,ListNode* l2)
{
ListNode*dummyNode=new ListNode(0);
ListNode*temp=dummyNode;
while(l1&&l2)
{
if(l1->val<l2->val)
{
temp->next=l1;
l1=l1->next;
}
else
{
temp->next=l2;
l2=l2->next;
}
temp=temp->next;
}
if(l1)
{
temp->next=l1;
}
if(l2)
{
temp->next=l2;
}
return dummyNode->next;
}
};
前后交替排列
1-2-3-4-5 -> 1-5-2-4-3
判断环形链表
求环形链表入口
//升序队列,顶上的数为最小值
priority_queue
//降序队列 ,顶上的数为最大值(默认是这个)
priority_queue
删除增加的时间复杂度为o(logn),n为队列大小
最大/最小的K个数,最大最小的第K个数
十进制转换为N进制
N进制转换为十进制
五种运算:
并,或,异或,右移,左移(分类)
负数的二进制为按位取反+1
有1/2个出现一次,其余出现两次
有1个出现一次,其余出现三次
int singleNumber(vector<int>& nums) {
//特殊情况
//一般情况
vector<int> bit_num(32,0);
for(auto n:nums)
{
unsigned int flag=1; //不支持负值左移
for(int i=31;i>=0;i--)
{
int bit=n&flag;
if(bit!=0) //存储顺序要和解码顺序相反
{
bit_num[i]+=1;
}
flag=flag<<1;
}
}
//解过程
int res=0; //负数也没问题,按照res一直左移,会使最高位变成1,得到的数就负的
for(int i=0;i<32;i++)
{
res=res<<1;
res+=bit_num[i]%3;
}
return res;
}
- 1.没有办法用异或
2.开辟一个数组bit_num,大小32,每个数每一位的个数存储上去
3.解码顺序与存储顺序相反,res=0;res+=bit_num[i]%3;res<<1;i++
double help(double x,long num) //处理n>0的情况
{
double res=1.0;
while(num)
{
if(num&1)
res=res*x;
x=x*x;
num=num>>1;
}
return res;
}
指数=0,<0,>0
int add(int a, int b) {
while(b)
{
int temp=a^b;
b=((unsigned int)a&b)<<1;
a=temp;
}
return a;
}
- 分为3步while(b)
1.求temp=a^b
2.b=求a,b与左移1位
3.a=temp
return a;
上楼梯/斐波那契数列
自底向上
解码字母
class Solution { //dp[i]代表前i个元素,比上一种好
public:
int numDecodings(string s) {
int n=s.size();
//特例
if(n==0)
return 0;
if(n==1)
{
if(s[0]=='0')
return 0;
else
return 1;
}
vector<int>dp(n+1,0); //dp[i]代表前i个元素
dp[0]=1;
for(int i=1;i<=n;i++)
{
//dp[i]=dp[i-1]+dp[i-2];
dp[i]+=s[i-1]=='0'?0:dp[i-1];
if(i>=2)
{
if(s[i-2]=='1'||(s[i-2]=='2'&&s[i-1]<='6'))
dp[i]+=dp[i-2];
}
}
return dp[n];
}
};
找零钱
能凑出零钱的最少零钱个数
凑出该零钱的方案
买门票
与找零钱的区别在于如果当天天数小于门票的天数,仍可以购买该长度的门票
切绳子
分割字符串
实际问题,重复多,范围小
时间复杂度ON,空间复杂度OM(非重复元素个数,最大为n)
//给员工年龄排序,时间复杂度on,空间复杂度oM(M为数组中数的大小)
class solution
{
public:
void sort(vector<int>&nums)
{
int n=nums.size();
if(n==0||n==1)
return ;
int oldestAge=99;
vector<int>age_num(oldestAge+1,0);
for(int i=0;i<n;i++)
{
age_num[nums[i]]++;//统计每个年龄有多少人
}
int cur=0;
for(int i=1;i<=oldestAge;i++)
{
for(int j=0;j<age_num[i];j++)
{
nums[cur++]=i;
}
}
}
};
class solution1_1
{
public:
vector<int>sort(vector<int>&nums)
{
if(nums.size()==0||nums.size()==1)
return nums;
int n=nums.size();
for(int i=0;i<n-1;i++)
{
bool isSort=true;
for(int j=n-1;j>i;j--)
{
if(nums[j-1]>nums[j])
{
swap(nums[j-1],nums[j]);
isSort=false;
}
}
if(isSort==true)
return nums;
}
return nums;
}
};
class solution0
{
public:
vector<int>sort(vector<int>&nums)
{
for(int i=0;i<nums.size()-1;i++)
{
int min_temp=i;
for(int j=i+1;j<nums.size();j++)
{
if(nums[j]<nums[min_temp])
{
min_temp=j;
}
}
if(min_temp!=i)
{
swap(nums[i],nums[min_temp]);
}
}
return nums;
}
};
class solution2
{
public:
vector<int>sort(vector<int>&nums)
{
int n=nums.size();
if(n==0||n==1)
return nums;
mergeSort(nums,0,n-1);
return nums;
}
private:
void mergeSort(vector<int>&nums,int left,int right)
{
vector<int>temp(right-left+1);
//vectortemp;
if(left>=right)
return ;
int mid=(right-left)/2+left;
mergeSort(nums,left,mid);
mergeSort(nums,mid+1,right);
//合并两个排序数组
int i=left;
int j=mid+1;
int cur=0;
while(i<=mid&&j<=right)
{
if(nums[i]<nums[j])
{
//temp.push_back(nums[i]);//如果用push,vector初始就不要设置大小
temp[cur++]=nums[i++];
//i++;
}
else
{
temp[cur++]=nums[j++];
//temp.push_back(nums[j]);
//j++;
}
}
while(i<=mid)
{
temp[cur++]=nums[i++];
//temp.push_back(nums[i]);
//i++;
}
while(j<=right)
{
temp[cur++]=nums[j++];
//temp.push_back(nums[j]);
//j++;
}
for(int i=0;i<right-left+1;i++)
{
nums[i+left]=temp[i];
}
return;
}
};
排序
数组
链表
合并两个排序数组
合并两个排序链表
合并n个排序数组
合并n个排序链表
class solution3
{
public:
vector<int>sort(vector<int>&nums)
{
int n=nums.size();
if(n==0||n==1)
return nums;
srand((unsigned)time(NULL));//时间种子
quick_sort(nums,0,n-1);
return nums;
}
private:
void quick_sort(vector<int>&nums,int left,int right)
{
if(left>right)
return ;
int pos=random_partition(nums,left,right);
quick_sort(nums,left,pos-1);
quick_sort(nums,pos+1,right);
}
int random_partition(vector<int>&nums,int left,int right)
{
int ran=rand()%(right-left+1)+left;
int pivot=nums[ran];
swap(nums[ran],nums[right]);
int i=left;
for(int j=left;j<right;j++)
{
if(nums[j]<pivot)
{
swap(nums[i],nums[j]);
i++;
}
}
swap(nums[i],nums[right]);
return i;
}
};
- 需要一个时间种子srand((unsigned)time(NULL)
class Solution {
public:
int findLeastk(vector<int>& nums,int k)
{
srand((unsigned)time(NULL));
return quickSelect(nums,0,nums.size()-1,k);
}
private:
int quickSelect(vector<int>&nums,int left,int right,int k)
{
if(left==right)
return nums[left];
int pos=random_partition(nums,left,right);
if(pos==k)
return nums[pos];
else if(pos<k)
return quickSelect(nums,pos+1,right);
else
return quickSelect(nums,left,pos-1);
}
int random_partition(vector<int>&nums,int left,int right)
{
int ran=rand()%(right-left+1)+left;
int pivot=nums[ran];
swap(nums[ran],nums[right]);
int i=left;
for(int j=left;j<right;j++)
{
if(nums[j]<pivot)
{
swap(nums[i],nums[j]);
i++;
}
}
swap(nums[i],nums[right]);
return i;
}
};
- quickSelect与quickSort不同,
quickSelect:if(left==right)return nums[left]为k位置的数
quickSort:if(left>right)return;//终止递归
k=0,代表最小的数
k=n-1,代表最大的数
中位数,时间复杂度oN
(数组中出现超过一半的数字)
二路快排
int random_partition(vector<int>&nums,int left,int right)
{
int ran=rand()%(right-left+1)+left;
int pivot=nums[ran];
swap(nums[ran],nums[left]);
int i=left+1;
int j=right;
while(true)
{
while(nums[i]<pivot&&i<=right)
i++;
while(nums[j]>pivot&&j>=left+1)
j--;
if(i>j)
break;
swap(nums[i],nums[j]);
i++;
j--;
}
swap(nums[left],nums[j]);
return j;
}
- 当数组内重复元素较多时,每次partition的时候总是把大于等于pivot的数都排到右边,所以数组内两边不平衡,退化为N^2
改进,partition的时候,将小于pivot的放在左边,大于pivot的放在右边,让数组尽可能平衡
- 易错点
1.int i=left+1,int j=right
2.while(true)循环终止条件if(i>j)break
int random_partition(vector<int>&nums,int left,int right)
{
int ran=rand()%(right-left+1)+left;
int pivot=nums[ran];
swap(nums[ran],nums[left]);
int i=left+1;//等于pivot的
int j=left; //小于pivot的
int k=right+1; //大于pivot的
while(i<k)
{
if(nums[i]<pivot)//小于pivot的
{
swap(nums[i],nums[j+1]);
i++;
j++;
}
else if(nums[i]>pivot)//大于pivot的
{
swap(nums[i],nums[k-1]);
k--;
}
else//等于pivot的
{
i++;
}
}
swap(nums[left],nums[j]);
return j;
}
易错点
1.int i=left+1,int left_part=left,int right_part=right+1
2.循环终止条件while(i
4.swap(nums[left],nums[left_part]);
5.return left_part;
//堆排序,3个步骤,1.建立大根堆 2,将堆顶元素放到最后3,将剩下的变成大根堆
class solution
{
public:
void sort (vector<int>&nums)
{
int n=nums.size()-1;
buildHeap(nums,n);
for(int i=n;i>0;i--)
{
swap(nums[0],nums[i]);
//n--;maxHeapify(nums,0,n);
maxHeapify(nums,0,i-1);
}
return ;
}
private:
void buildHeap(vector<int>&nums,int n)
{
for(int i=n>>1;i>=0;i--)
{
maxHeapify(nums,i,n);
}
return;
}
void maxHeapify(vector<int>&nums,int i,int n)
{
for(;(i<<1)+1<=n;)
{
int lson=(i<<1)+1;
int rson=(i<<1)+2;
int large=0;
if(lson<=n&&nums[lson]>nums[i])
large=lson;
else
large=i;
if(rson<=n&&nums[rson]>nums[large])
large=rson;
if(large!=i)
{
swap(nums[i],nums[large]);
i=large;
}
else
break;
}
return;
}
};
三个部分
1.建堆,需要nums与规模n(nums.size()-1),分层建堆i=nums.size()/2->0
2.每层堆化,需要nums与规模n和开始位置i
3.先将数组建堆,每次将最大的(nums[0])换到最后,n–重新将堆首堆化(i=0)
排序一维区间
二维数组
vector<int> findTarget(vector<vector<int>>& nums,int target)
{
vector<int>res;
if(nums.size()==0)
{
res={-1,-1};
return res;
}
int m=nums.size();
int n=nums[0].size();
int start=0;int end=n*m-1;
int mid=(start+end)>>1;
if(nums[mid/n][mid%n]<=target)
start=mid;
else
{
end=mid;
}
if(nums[start/n][start%n]==target)
{
res={start/n,start%n};
return res;
}
if(nums[end/n][end%n]==target)
{
res={end/n,end%n};
return res;
}
res={-1,-1};
return res;
}
- 每行递增,每列递增
- 每次选取的数是最右上角的数
if(nums[i][j]>target)//说明这一列都大于
column–; //列数
else row++;
等于直接出结果
不确定数组长度
不确定目标(旋转数组)
旋转数组的最小值
(数组内的值可能重复)
旋转数组中的某一个值
while(start+1<end)
{
int mid=(start+end)>>1;
if(nums[start]<=nums[mid]) //首先判断在那一列上
{
if(nums[mid]==target)
return mid;
else if(nums[start]<=target&&target<nums[mid])//其次判断在该列的一般区间上,是的话该该列的方向缩进
end=mid;
else
start=mid;
}
else
{
if(nums[mid]==target)
return mid;
else if(nums[mid]<target&&target<=nums[end])
start=mid;
else
end=mid;
}
}
- 分为两种情况,前一列后一列
1.判断在哪一列上,根据if(nums[start]<=nums[mid]) 求sqrt x 整数 小数 不能判断小数是否相等,设置一个equal函数,如果两个double或者float的差小于1e-7就认为他们相等 无重复元素 有重复元素(结果不能重复) 无重复 按位与 bfs回溯,每次选择放或者不放 dfs回溯,每次遍历从index到nums.size-1,选择放或者不放,放的话将temp->res,当index=nums.size()时return 有重复 把二进制字符串转换成十进制数字(int)stoi(string,0,2)//第一个数字是开始位置,第二个数字是二进制
2.在第一列上,if(nums[start]<=target
注意:判断的时候不可以写mid*mid与x比较,可能会越界 double sqrt (double x)
{
double res=0;
//特殊情况
if(x==0)
return 0;
if(x==1.0)
return 1.0;
//正常情况
double start=0;
double end=0;
if(0<x&&x<1.0)
{
start=0;
end=1.0;
}
if(x>1)
{
start=1.0;
end=x;
}
while(start+1e-6<end)
{
double mid=start+(end-start)/2;
if(x/mid==mid)
return mid;
else if(x/mid>mid)
start=mid;
else
end=mid;
}
if(x/end>=end)
return end;
else
return start;
}
- while(start+1e-6
- 0
哈希表查找
回溯
全排列
判断合法条件:temp中没有该元素
2.需要一个hash,可以是数组,可以是set
3.多一步判断条件:if(i!=0&&nums[i]==nums[i-1]&&hash[i-1]==0)//hash.count(i-1)==0全子集
一共的可能情况共2^n种,设置一个i++
让每个i与(1<
设置index位,达到nums.size时将temp放入res
vector.pop_back();
剪枝条件,当index>i并且nums[i]=nums[i-1];在字母矩阵中找目标单词
1.结束条件:index=word.size()
2.回溯失败条件1:cow<0||cow>=nums.size()||column…
回溯失败条件2:nums[cow][column]!=word[index]
回溯失败条件3:往剩下四个方向寻找下一步都失败
3.回溯成功…机器人能到达的矩阵位置(坐标按位和小于等于K)
int n;
int res;
while(n>0)
{
res+=n%10;
n/=10;
}
return res;
2.或者把结果作为int,每次递归都返回递归
一般需要建立一个递归函数(void),用于自循环
递归三要素
常考题
接雨水
递归找零钱
ip地址区分方式93
一些函数
排序sort(nums.begin(),nums.end())
把字符串转换成十进制数字(int)stoi(string)
注意事项
变量初始化!
变量声明位置(若在循环里则循环外是看不到的)
使用一个vector可以不设定大小,但是如果函数中有对于vector[i]的判断则必须初始化大小
区别
break与continue