leetcode加剑指offer

记住三条:
1.代码规范性,缩进,变量命名,
2.代码完整性,考虑所有边界输入
3.代码鲁棒性,考虑出错处理,防御性编程

另外三条:
1.功能测试
2.边界测试
3.错误输入
https://github.com/0voice/interview_internal_reference
https://blog.csdn.net/u013457167/article/details/82814413?utm_source=distribute.pc_relevant.none-task C++面试题
https://blog.csdn.net/amoscykl/article/details/100034019
https://github.com/Miyamoto-Konatsu
https://blog.csdn.net/llllllyyy/article/details/79953071 面试经验

https://www.cnblogs.com/alantu2018/p/8460958.html判断一棵树是否平衡,不是判断平衡二叉树,而是判断最远节点到根节点的距离比最近的距离是否大于1.平衡二叉树的定义是指他是一颗空树,并且他的左右2个字数的高度差不超过1,并且左右子树也是平衡二叉树
https://blog.csdn.net/xiezhi123456/article/details/90141172 指针的引用或者指针的指针就是指的是要改变指针的值,比如链表头结点为空,删除需要用**head 或者* &head
https://blog.csdn.net/qq_31820761/category_8983532_2.html C++算法
https://blog.csdn.net/majianfei1023/article/details/45337085?utm_source=wechat_session&utm_medium=social&utm_oi=936211785573335040 linux后台需要学会的东西
https://zhuanlan.zhihu.com/p/46917270?utm_source=wechat_session&utm_medium=social&utm_oi=936211785573335040
https://news.51cto.com/art/202001/609544.htm 菜鸟成神所有博客集合

面试题 39快速排序 面试题 51 归并排序

125:验证回文字串
思路:将字符串转换为小写,用tolower(s[i]);
然后用双指针办法,start指向开始,end指向字符串最末尾一个,然后利用快速排序的思想
如果start<end,并且不是数字或者字母,就是start++,当是字符或者数字的时候,就break
如果start<end,并且不是数字或者字母,就end–,当是的时候,就break
判断此时s[start]!=s[end],return false,
判断字母数字直接用c>=0&&c<=9||c>=a&&c<=z

代码
class Solution {
public:
bool isPalindrome(string s) {
if (s.length() == 0)
return true;
for(int i=0;i<s.length();i++)
{
s[i]=tolower(s[i]);
}

int start = 0;
int end = s.length() - 1;
while (start <=end)
{
    while (start <end&&!IsCharOrNumber(s[start]))
        start++;
    while (start <end&&!IsCharOrNumber(s[end]))
        end--;
    if (s[start] != s[end])
        return false;
    start++;
    end--;
}
return true;
}
bool IsCharOrNumber(char c){
if ((c >= 'a'&&c <= 'z') || (c >= '0'&&c <= '9'))
    return true;
else
    return false;
}

};

字符串转换整数
定义一个索引i,long long res,int flag正负号
遍历字符串,然后如果i<length()&&str[i]’ ',那么就一直往前走,直到遇到不是空格的
这一步得做个判断,如果i
str.length(),那么直接return 0;
继续判断如果下一位是正负号,那么就记录flag的值,如果是-号,就flag=-1,方便与后面的乘法
如果是+号,就flag=1;

然后开始主循环,还有个i<str.length(),并且str[i]是数字,str[i]<=‘9’&&str[i]>=‘0’
最重要的要背下来 res=10*res+str[i++]-‘0’;(注意如果算一个字符串的和,那么这样算是对的,乘以10,最后都会乘以了这个字符串的位数)
然后判断res是否>=2147…,再判断flag是否大于0,然后返回INT_MAX或者INT_MIN

class Solution {
public:
int myAtoi(string str) {
long long res=0;
int i=0;
int flag=1;
while(i<str.length()&&str[i]’ '){
i++;
}
if(i
str.length()) return 0;
if(str[i]’-’) {flag=-1;i++;}
else if(str[i]
’+’) {flag=1;i++;}

    while(i<str.length()&&str[i]>='0'&&str[i]<='9'){
        res=10*res+str[i++]-'0';
        if(res>=2147483648){
            if(flag==-1){
                return INT_MIN;
            }
            else{
                return INT_MAX;
            }   
        }
    }
    return res*flag;
}

};

实现strstr
for循环小于原字符串
然后判断是否此时的j已经等于了needle的size,如果相等那么直接返回i-needle.size()
不相等的话,判断此时的heystack[i]是否等于needle[j],如果相等让j++,然后进入下一轮for循环i++,
判断,如果不相等的话,让j=0;i呢 -=j,减到0,因为i还要进行++,所以下次从1开始,如果不相等,i=1,下次i=2

最后判断for循环出来以后,如果此时的j==needle.size()相等,那么return i-needle.size();
反之返回-1,最后之所以要判断的原因就是可能haystack中根本没有needle的子串,所以如果j加到了子串的size,
那么证明haystack里有,反之返回-1;

class Solution {
public:
int strStr(string haystack, string needle) {
if (needle.size()0)
return 0;
if (needle.size() > haystack.size())
return -1;
int i=0;
int j=0;
for( i=0;i<haystack.size();i++){
if(j
needle.size()){
return i-needle.size();
}
if(haystack[i]==needle[j]){
j++;
}else{
i-=j;
j=0;
}

    }
    if(j==needle.size())
    {
        return i-needle.size();
    }
        return -1;

    
    
}

};

字符串中唯一的字符
定义一个数组,然后遍历一次,记录个数,
然后再遍历一次,如果这个字符所在的索引下标所对应的s[i],对应于vec数组的值==1,就返回索引

最长公共前缀
算出数组中最小尺寸那个,然后for循环从0开始遍历,然后再for循环遍历,每个数组,然后根据最小尺寸从0
开始,判断i和i+1是否相等,如果不相等直接返回substr,

或者直接
while(ture){

用i记录最大长度
for(int i=0;i<ves.size();i++)){
    判断j是否超出了ves的最小数组的范围
    
    然后定义char c=vec[0][j];
    一次循环每个数组,判断c!=vec[i][j]
    如果不相等就return substr
    
}

j++;最长公共长度++;
}
跳出循环也返回
abc a abdefg
class Solution {
public:
string longestCommonPrefix(vector& vec) {
if (vec.size() == 0)
{
return “”;
}

int j = 0;//记录最大公共长度
while (true)
{

	for (int i = 0; i < vec.size(); i++)
	{

		if (j > vec[i].size())
		{
			return vec[0].substr(0, j);
		}

		char c = vec[0][j];
		if (c != vec[i][j])
		{
			return vec[0].substr(0, j);
		}
	}
    			j++;

}
return vec[0].substr(0, j);

};
};

class Solution {
public:
string longestCommonPrefix(vector& strs) {
if(strs.size()==0)
return “”;
if(strs.size()==1){
return strs[0];
}
int minSize = strs[0].size();
string res;
for (auto&val : strs){
if (val.size() < minSize){
minSize = val.size();
}
}
int i = 0;
int j=0;
for( j=0;j<minSize;j++){
for (i = 0; i < strs.size() - 1; i++){
if (strs[i][j] != strs[i + 1][j]){
return strs[i].substr(0, j);
}
}
}
return strs[i].substr(0, j);

}

};

删除链表的节点:用移花接木方式,注意要判断pnext是否为空

删除链表倒数第n个节点:
首先判断是否为空,判断第n个节点,是否在移动的时候变为空节点,然后再next=next->next.jiu
是for循环移动节点的时候,先要判断fast是否为空,才能fast=fast=fast->next;

然后定义slow指针,然后接着whilei循环,这里判断fast->next是否为空,当跳出while循环的时候
要移动指针slow->next=slow->next->next;
然后return head;



class Solution {

public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
if(headNULL)
return NULL;
ListNode* fast=head;
ListNode* slow=head;
for(int i=0;i<n;i++)
{
fast=fast->next;
}
if(fast
NULL)
{
head=head->next;//假如有三个元素,123,n=3,那么此时fast=null了,说明删除的是1,那么我肯定要移动head了,然后return head;
return head;
}
while(fast->next!=NULL)
{
fast=fast->next;
slow=slow->next;
}
slow->next=slow->next->next;
return head;
}
};

环形链表,给定一个链表,判定是否有环
首先判断给定的head是否为空,如果head->next也为空,一个节点的链表一定return false

定义2个指针
判断fast->next&&fast->next->next,这里先判断fast->next是否空,如果哦为空直接break.不为空
直接判断fast->next->next为空,如果不为空,才进去循环.
进入循环,fast=fast->next->Next,这里不用判断了,因为我已经在while循环条件中判断了
slow=slow->next;
如果相等直接return true;

跳出循环return false

class Solution {
public:
bool hasCycle(ListNode head) {
if (head == NULL || head->next == NULL)
return false;
ListNode
slow = head;
ListNode*fast = head;
while (fast->next != NULL&&fast->next->next != NULL)
{
fast = fast->next->next;
//if(fast==NULL){
// return false;
// }
slow = slow->next;
if (slow == fast)
{
return true;
}
}
return false;
}
};

输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

最大子数组和:注意是连续最大子序列和

定义一个sum和res等于数组的第一个元素,然后从第二个数开始循环,如果
判断此时前面的sum>0,那么加上此时的nums[i],证明前面这个sum数具有正的增益作用
如果sum小于0,那么证明,我加上你还不如不加呢,此时sum=num[i]

然后就得判断此时我的res和sum的值的比较,如果sum>res了,res=sum.

class Solution {
public:
int maxSubArray(vector& nums) {
int ncursum=0;
int ngreatesum=0x80000000;
for(int i=0;i<nums.size();i++){
if(ncursum<=0){
ncursum=nums[i];
}
else
{
ncursum+=nums[i];
}
if(ncursum>ngreatesum){
ngreatesum=ncursum;
}
}
return ngreatesum;
}
};

class Solution {
public:
int maxSubArray(vector& nums) {
int res = nums[0];
int sum = nums[0];

for (int i = 1; i<nums.size(); i++)
{
	if (sum>0)
	{
		sum += nums[i];
	}
	else
	{
		sum = nums[i];
	}

	if (res<sum)
		res = sum;
}
return res;

}

};

//第二种方法
定义dp[i]为以下标i结尾的元素的最大子序列和
那么dp[i]一定等于dp[i-1]+nums[i]和nums[i]的最大值,
然后定义一个res变量,初始化位nums[0],如果res<dp[i],就是随时更新res的值
最后返回res,注意注意这是数组的最大西序列和,连续数组,别急混了,用动态规划加增益的方法
-2,1,-3,4,-1,2,1,-5,4
dp[0]=-2;
dp[1]=1;因为-2+1小于1,所以嘛,我以下标位1结尾的元素的序列最大和位1,不取-3
dp[2]=-2;至于为什么是-2呢,因为我如果假如了-3作为结尾,那么-3肯定小于-3+1,所以如果以-3结尾
就是结果-2了,就是-2 1 -3 .1 -3. -3 这三个子序列的和的最大值

class Solution {
public:
int maxSubArray(vector& nums) {

    int size=nums.size();
    if(size==0)
        return 0;
    vector<int>dp(nums.size());
    dp[0]=nums[0];
    int res=dp[0];
    for(int i=1;i<size;i++){
        dp[i]=dp[i-1]<0?(nums[i]):(dp[i-1]+nums[i]);
        if(dp[i]>res)
            res=dp[i];
    }
    return res;
    //只有dp[i]是正数的时候,才有促进作用,否则只取numsi这个数字,因为加上之前负数的dpi没有意义

/*
vectordp(nums.size());
dp[0]=nums[0];
int max=dp[0];
for(int i=1;i<nums.size();i++){
dp[i]=dp[i-1]<0?nums[i]:dp[i-1]+nums[i];//如果前面i-1个最大子序和已经小于0了,那么前i位的子序和就是当前元素
if(dp[i]>max)max=dp[i];
}
return max;
*/
}
};

买卖股票的最佳时机
设定一个maxvalue和curmin,
最开始初始化的值maxvalue=0;curmin就等于数组的第一个值,
那么从第一天开始循环,那么第一天的maxvalue,等于此时的maxvalue和当天的价格减去此时这天之前
的cur_min的值,而cur_min的值,就是cur_min的值和此时的prices的值,而最开始初始化的值就是prices[0],后续
随着i每次变化而变化.所以这道题主要是初始化的问题,以及更新最大值的过程,以及更新当前遍历的价格之前的最小的值,然后用当前的
值减去之前最小的cur_min,

class Solution {
public:
int maxProfit(vector& prices) {
int len=prices.size();
if(len<=1){
return 0;
}
int maxValue=0;
int curMin=prices[0];
for(int i=1;i<len;i++){
maxValue=max(maxValue,prices[i]-curMin);
curMin=min(curMin,prices[i]);
}
return maxValue;
}
};

class Solution {
public:
int maxProfit(vector& prices) {
int n = prices.size();
if (n == 0) return 0; // 边界条件
int minprice = prices[0];
vector dp (n, 0);

    for (int i = 1; i < n; i++){
        dp[i] = max(dp[i - 1], prices[i] - minprice);
        minprice = min(minprice, prices[i]);

    }
    return dp[n - 1];

}

};
方法2:直接利用动态规划的思想,一定要理清第i天和第i-1天的关系,然后用手写出来.
vectordp(n,0);
dp[i]表示前i天的最大利润
dp[0]=0;
int curmin=prices[0];
for(int i=1;i<n;i++){
dp[i]=max(dp[i-1],prices[i]-curmin);
curmin=min(curmin,prices[i]);
}
return dp[n-1];

青蛙一次跳一层或者2层,跳上n层,需要多少种跳发
定义dp[i]为跳上i层台阶的跳发
则dp[i]=dp[i-1]+dp[i-2];
注意:
必须判断n的值,是否小于2,如果小于2,直接返回
第二定义是是n+1个数组,因为如果是0层台阶,则是0种跳发
如果是n层台阶,则是n种跳发,而0-n是n+1个数,所以定义n+1
注意初始化,dp0 1 2
然后for从3开始,最后返回dp[n]

打家劫舍
设置dp[i]位偷到第i家,所能偷到的最大金额,那么有关系式
dp[i]=dp[i-2]+nums[i-1]和dp[i-1]的最大值才行,nums[i-1]表示的是如果是偷到第三家,
那么代表的是nums[i的第3个元素,也就是i=2;所以要减去1;
最后return dp[len];注意dp[0]=0;

class Solution {
public:
int rob(vector& nums) {

    if(nums.size()==0)
        return 0;
    if(nums.size()==1)
        return nums[0];
    if(nums.size()==2)
        return max(nums[0],nums[1]);
    
    //int max=0;
    int len=nums.size();
    int*dp=new int[len];
    dp[0]=nums[0];
    dp[1]=max(nums[0],nums[1]);

    for(int i=2;i<len;i++)
    {
        dp[i]=max(dp[i-2]+nums[i],dp[i-1]);
    }
    //就是比较我不偷i-1这块和偷i-1这块,
    不偷i-1,那么就是偷dp[i-2]和nums[i]的和
    偷i-1,那么i就不偷了,所以就是dp[i-1];
    return dp[len-1];
    
}

};

打乱数组:
用一个origin和nums接收,求出int index=rand()%i+1;,然后返回nums
然后从i=len-1开始,然后swap(nums[i],num[index]);

最小栈:得考虑弹出来的时候,在栈顶依旧是最小栈,所以对于每插入的 一个元素,都将此时连同
这个元素的最小值插入进去栈中,即使弹出来了,那么当前栈顶的那个元素的最小值也弹出来了,
此时剩下的就是次最小值,也就是最小值,所以用stackstd::make_pair>;

最小栈的弹出要更新最小值,以便常数时间内插入.
if(s.size()>0)){
s.pop());
if(s.size()>0){
curmin=s.top().second();
}
}

2数之和,返回数组的下表
所以一定要有个map来定义数组的数字和索引的关系
返回数组,一定要有个vector
所以从头开始循环,
判断map中target-此时遍历的数组中的数字的count是否大于0,如果不大于0的话,证明此时不满足
对于此时数组的值,和map中的所有值相加没有target这个值,
如果大于0的话,证明此时遍历的这数,map中有一个能满足和它相加为target,所以构造 res数组
res[0]=map[target -num[i]];map中存放的,之前遍历的
res[1]=i;此时的值

class Solution {
public:
vector twoSum(vector& nums, int target) {
vectorb(2,-1);
map<int,int>m;
for(int i=0;i<nums.size();i++){

        if(m.count(target-nums[i])>0){
            b[0]=m[target-nums[i]];
            b[1]=i;
        }
        m[nums[i]]=i;
    }
    return b;
}

};

无重复字符的最长子串
定义substr,用来存放子串
从头开始遍历子串
如果在substr里没有发现当前遍历的字串,那么我就执行把当前遍历的这个字串给插入到
substr中,并计算res和此时substr的长度的最大值
如果在substr中发现了我此时遍历的字符,那么我需要从substr中找出这个字符出现的位置,然后pos+1
然后将substr中的0-pos的值给删除,然后将此时的这个字符插入到substr中,用push_back.
然后再计算res和当前substr的最大值res=max(res,substr);
return res;

class Solution {
public:
int lengthOfLongestSubstring(string s) {
int len=s.size();
if(len==0)
return 0;
string substr;
int res=0;
for(int i=0;i<len;++i)
{
if(substr.find(s[i])!=string::npos)
{
int pos=substr.find(s[i])+1;
substr.erase(0,pos);
substr.push_back(s[i]);
res=max(res,(int)substr.size());

        }
        else
        {
            substr.push_back(s[i]);
            res=max(res,(int)substr.size());


        }
    }
    return res;
}

};

四数之和

剑指offer,第一个数组中重复的数字
bool isDuplicate(vector&vec){
int size=vec.size();
//做一步判断,如果<0或者什么的
for(int i=0;i<size;i++){
while(vec[i]!=i){

        if(vec[vec[i]]=vec[i]){
            return true;
        }
        int temp=vec[i];
        vec[i]=vec[vec[i]];
        vec[vec[i]]=temp;
    }
}
return false

}

数组中重复的数字
n+1个数字,数字范围是1-n

可以创建一个辅助数组,然后将原数组中的数字复制到新数组下标位这个数字的地方,比如2,2,那么都放到新数组的索引2上那么肯定就重复了
int countRange(vector&nums,int len,int start,int end){
int count=0;
for(int i=0.i<len;i++){
if(nums[i]>=start&&nums[i]<=end){
count++;
}

}
return count;

}
int duplicate(vector&nums,int len){
//todu,要判断空指针,或者len的值,要注意测试用例的书写,再给面试官之前,要自己先想好测试用例,以及临界情况
//不要只觉得结果对就可以,那只是最基本的东西.测试用例:分为重复的和不重复的,以及输入非法
int start=1;
int end=nums.size()-1;
while(start<=end){
int middle=(end-start)>>1+start;
int count=countRange(nums,len,start,middle);
if(start==end){
if(count>1){
return start;
}
else{
break;
}
}
if(count>middle-start+1){
end=middle;
}
else{
start=middle+1;
}
}
}

思路就是:
我用二分查找算法:
然后算出一个middle值,start=1,end=数组的长度-1,然后在这个中间取一个中间值
然后算出从开始到这个middle的,nums[i]的数量,加入1-4之间,遍历num[i]中,返回的
count=5;那证明了1-4之间的数一定有重复,所以这个start-end,是数的范围,不是下标
如果count>middle-start+1,就是说如果nums[i]的数量多于了4个,假如是5个,那么我的end就等于
middle,如果在1-4之间,nums[i]的数量不大于4,那么就证明1-4之间的数,在nums中没有重复,那么就
start=middle+1; 但是在计算完count之后,还要判断此时是否start==end,如果等于,判断count>1,如果大于1的haul
那么就返回start,如果不大于,就break

二维数组的查找,查找一个行递增和列递增的数组

思想:从右上角开始,如果右上角这数大于此时的target,那么右上角这个数所在的列就被去除了
如果右上角这数的小于target,那么这一行肯定被排除了
所以沿着这个思路

//最开始不要忘记了判空,如果数组为空,直接return false;
定义bool fount=false;
我定义rows=matrix.size();
columns=matrix[0].size();
然后定义变量row=0,column=columns-1;
此时开始循环
while(row<rows&&column>=0){
//判断此时的row和column对应这个值是否==,如果==,直接break fount=true;
//判断大于target的话,那么肯定这一列去除了,因为这是这一列的最小值
//如果小,那么这一行去除了,因为这是这一行的最大值
}
return found;

class Solution {
public:
bool findNumberIn2DArray(vector<vector>& matrix, int target) {
if(matrix.size()==0){
return false;
}
bool found=false;
int rows=matrix.size();
int columns=matrix[0].size();
int row=0;
int column=columns-1;
while(row<rows&&column>=0){
if(matrix[row][column]==target){
found=true;
break;
}
else if(matrix[row][column]>target){
–column;
}
else{
row++;
}
}
return found;
}
};

3数之和,用3个指针
首先先对数组进行排序
第二:从头到尾遍历数组,首先判断nums[i]如果>0,那么直接break;
第三:如果i>0并且nums[i]nums[i-1],continue;
然后定义L=i+1;R=len-1;
直接一个大循环一直往后走
while(L<R){
int sum=nums[i]+nums[L]+nums[R];
if(sum
0){
vectortemp;
push三个元素
res.push_back(temp);//此时已经有一个结果,所以要继续循环
要移动L和R,但是有前提,如果
L<R&&L的下一位和L相等,那么我直接L移动到下一位跟此时L相等,
同理,R也是一样的,R的前一位和此时的R相等,R–;
但是在最后我还要移动L++;R–;因为刚才那一步只是保证状态是一样的,还需要移动
到状态不一样的,因为L++,一定是L这个数增大了,那么如果R不动,一定不行,因为已经
去重了,L++了在while循环中,所以R–也要同时移动,这2步是需要的.
}else if(sum>0){
R–;
}else{
L++;
}
}
return res;

class Solution {
public:
vector<vector> threeSum(vector& nums) {
vector<vector>res;
int len=nums.size();
if(len==0){
return res;
}
std::sort(nums.begin(),nums.end());
for(int i=0;i<len;i++){
if(nums[i]>0){
break;
}
if(i>0&&nums[i]nums[i-1]){continue;}
int L=i+1;
int R=len-1;
while(L<R){
int sum=nums[i]+nums[L]+nums[R];
if(sum
0){
vectortemp;
temp.push_back(nums[i]);
temp.push_back(nums[L]);
temp.push_back(nums[R]);
res.push_back(temp);
while(L<R&&nums[L]==nums[L+1]) L++;
while(L<R&&nums[R]==nums[R-1]) R–;
L++;
R–;

            }else if(sum>0){
                R--;
            }else{
                L++;
            }
        }
    }
    return res;
}

};

四数之和
跟三数之和一样的思想,不同之处在于用了4个变量
for(int a=0;a<=len-4;a++){
//去重判断,此时的a>0,并且和前一个相等,nums[a]==nums[a-1] continue;
for(int b=a+1;b<=len-3;b++){
//去重判断
c=b+1;
d=len-1;
while(c<d){
if 相等{
push a b c d;用成员初始化列表{1,2,3,4}就是一个vector
while去重
c++;
d–;
}
else if target>
else{

        }
    }
}

}

class Solution {
public:
vector<vector> fourSum(vector& nums, int target) {
int len=nums.size();
vector<vector>res;
if(len<4){
return res;
}
//忘记了,我没有排序,所以一定要排序,才能知道大小
std::sort(nums.begin(),nums.end());
int a,b,c,d;
for(a=0;a<=len-4;a++){
if(a>0&&nums[a]==nums[a-1]) continue;
for(b=a+1;b<=len-3;b++){
if(b>a+1&&nums[b]nums[b-1]) continue;
c=b+1;
d=len-1;
while(c<d){
if(target
nums[a]+nums[b]+nums[c]+nums[d]){
res.push_back({nums[a],nums[b],nums[c],nums[d]});
while(c<d&&nums[c]==nums[c+1]) c++;
while(c<d&&nums[d]==nums[d-1]) d–;
c++;
d–;
}else if(target<nums[a]+nums[b]+nums[c]+nums[d]){
d–;
}else{
c++;
}

            }
        }
    }
    return res;
}

};

只出现一次的数字
用抑或来,不同的为1,相同的为0
我首先取数组第一个元素为ans,然后从第二个开始依次跟他抑或
for(int i=1;i<len;i++){
ans=ans^nums[i];
}
return ans;
或者i=0;j=1;i<Len;j<len;i+=2j+=2
if(nums[i]!=nums[j])
return nums[i];

快乐数
首先要求得一个数的平方的函数
int getSquare(int n){
int sum=0;
while(n>0){
int temp=n%10;
sum+=temp*temp;
n=n/10;

}
return sum;

}
bool ishappy(){
int slow=n;
int fast=n;
do{
slow=getsquare(slow);
fast=getsquare(fast);
fast=getsquare(fast);
}while(slow!=fast)
//如果最后出了循环slow=1,如果不是1,说明进入了循环4…4,跟循环链表一样,快慢指针一定会追上,此时slow=fast!=1;然后直接退出循环.
return true
else return false;
}

class Solution {
public:
int getSquareNum(int n)
{
int sum=0;

    while(n>0)
    {
    int temp=n%10;
    sum+=temp*temp;
    n=n/10;
    }
    return sum;
}
bool isHappy(int n) {
    
    int slow=n;
    int fast=n;
    do
    {
         slow=getSquareNum(slow);
         fast=getSquareNum(fast);
        fast=getSquareNum(fast);
    }while(slow!=fast);
    if(slow==1)
        return true;
    else
    {
        return false;
    }
}

};

unordered_setus;
while(true){
//假如set里包含了45 63 65,假如这次计算出了任何一个45,那么下次还是63,假如是65,还是65,就一直这样循环下去永远不会为1,
int sum=getsquare(n);
if(sum==1){
return true;
}
if(us.count(sum)>0){
return false;
}
else{
us.insert(sum);
}
n=sum;
}
return true;

替换空格,替换字符串,搜狗给我发的面试题,把字符串中的abc替换成de,一样的道理
定义2个指针,扩展str的容量,从头到尾遍历空格字符的数量,然后用resize扩容,
然后用indexprev=originlen指向源字符串的最后一个\0字符,用indexafter=newlen,指向
扩展后的末尾字符串,然后循环遍历,2个指针,拷贝,然后–,如果遇见空格,则indexprev–,indexafter–=‘0’;…==‘2’
注意循环结束的条件是,prev!=after并且,他俩都>0
class Solution {
public:
string replaceSpace(string str) {
int len = str.length();
int blankCount = 0;
for (auto&val : str){
if (val == ’ ‘){
blankCount++;
}
}
int newLen = len + 2 * blankCount;
str.resize(newLen);
//str.push_back(‘d’);
//str.push_back(‘d’);
//str.push_back(‘d’);
//str[newLen - 2] = ‘c’;
//char c = str[newLen - 2];
int indexPrev = len;
int indexAfter = newLen;
//we are happy
while (indexPrev>=0&&indexAfter>=0&&indexAfter!=indexPrev){
if (str[indexPrev]!=’ '){/最开始是最后一个\0字符
str[indexAfter–] = str[indexPrev–];
}
else
{
indexPrev–;
str[indexAfter–] = ‘0’;
str[indexAfter–] = ‘2’;
str[indexAfter–] = ‘%’;

	}

}
string res = str;
return res;
}

};

有2个排序的数组A1,A2,现在要求把A2中的所有数字都插入到A1中并且保证数字的排序的,
我之前想的是从头到尾,其实也可以从尾部到头部,尾部的位置就是A1+A2的长度,然后依次判断,
利用双指针的思想

所以要举一反三:在合并2个字符串或者数字的时候,如果从前往后需要复制每个数字,则需要移动很多数字
多次,那么我们可以考虑从后往前复制,减少移动的次数.

这些都是jianzhioffer的

链表
插入到尾部
注意2点:用head=newnode;
第二:判断
head是否为空,不为空的话,遍历pnode->next是否为空,如果为空,直接插入到这里pnode->next=newnode,

void removenode(**head,int value){
//判断是否为空head或者head为空
// 第二如果
head->val=value,说明删除的是投节点,然后设置个指针tobedel=*head;
如果不是头,那么就遍历pnode=*head,while(pnode->next!=NULL&pnode->next->val!=value)(pnode=pnode->next;)
然后再来个if(pnode->next!=NULL&&pnode->next->value==value){
tobedelete=pnode->next;
pnode->next=pnode->next->next;
}
//删除tobedel节点,if(!=NULL)free(tobedel);

}

//根据前序遍历喝中序遍历,构建二叉树 1)构建根节点 2)返回根节点(相对根,每一个遍历的节点) 3)构建根节点的左子树和右子树,用索引来确定
首先要定义根节点的值,等于前序遍历中的第一个值
所以根节点 此时就可以new出来,并赋值

如果此时的前序的开始等于结尾
如果此时的中序的开始等于中序的结
return root

然后在中序遍历中找到根节点的值
循环中序遍历的数组,然后找到此时中序遍历中的位置,现在得到了中序遍历中根节点的位置,
判断如果没有找到,就抛出异常

再求出左节点的长度,根据中序遍历的根节点的位置-中序遍历的起始位置就知道了左子树的长度
如果左节点长度>0:
构建左子树,递归构建
//startpre+1,start+leftleght, startinorder,rootinorder-1
root->left=constructcore(前序遍历的开始,前序遍历的末尾,中序的开始,中序的末尾)
如果左的长度小于前序的末尾-前序的开始,说明此时还有右子树待构建
//start_leftlenth+1,endpreoder,rootinorder+1,endinorder
root->right=constructore()

第二种方法:
treenodehelp(vector&preorder,vector&inorder,int root ,int start,int end){
if(start>end){
return NULL;
}
treenode
root=new treenode(preorder[root]);
int i=start;
//找到中序遍历中的根节点
while(i<end&&preorder[root]!=inorder[i]) i++;
root->left=help(preorder,inorder,root+1,start,i-1);
root->right=help(preorder,inorder,root+i+1-start,i+1,end);

          如果start>end,return NULL,然后new出一个此时的root节点
          然后从中序遍历中找到这个节点,然后构造她的左子树和右子树
          这个节点的左子树等于下一个root的值,然后从中序遍历中,找出这个root对应的节点,然后继续递归调用.
          思路就是:从前序遍历的每一个根节点选择一个root值,然后让root+1,
          
          
          我知道了:是这样的,第一次root=0,start=0,end=len-1;
          第一次root的跟节点就是前序中root+1,加上root+i+1+-start,这2个根节点,

struct TreeNode1 {
int val;
TreeNode1 *left;
TreeNode1 *right;
TreeNode1(int x) : val(x), left(NULL), right(NULL) {}
};

TreeNode1help(vector& preorder, vector& inorder, int root, int start, int end){
if (start>end) return NULL;
TreeNode1
tree = new TreeNode1(preorder[root]);
int i = start;//从start开始偏移多少作为根,所以下文要减去start
while (i<end&&preorder[root] != inorder[i]) i++;
tree->left = help(preorder, inorder, root + 1, start, i - 1);
tree->right = help(preorder, inorder, root + 1 + i - start, i + 1, end);
root + 1 + i - start=4,preorder[4]=3就是根节点的右子树,(4,4,7)
root+1,=1,preorder[1]=2,2这个节点就是根节点的左子树 ,因为在代码中是创建根节点是根据前序创建的
//preorder[1]=2 此时的以preorder[0]节点为根节点的左子树,根的值是preorder[1]=2,就是中序遍历中的序列0~2index
//preorder[4]=3 此时的以preorder[0]节点为根节点的右子树,preorder[4]=3,就是中序遍历中的序列 4~7index
//表明root[0]这个节点1,的left=2,right=
return tree;
}
TreeNode1* buildTree(vector& preorder, vector& inorder) {
return help(preorder, inorder, 0, 0, inorder.size() - 1);
}
void main(){
string str = " we are happy";
//replaceBlank(str);
//str.resize(100);
vector preorder = {1,2,4,7,3,5,6,8};
vector inorder = {4,7,2,1,5,3,8,6};
// 0 1 2 3 4 5 6 7
buildTree(preorder, inorder);

这才是正确的打开方式,根据前序和终须遍历,很好理解
这道题剑指offer已经做过了,见重建二叉树
直接复制思路:
C++。首先要知道一个结论,前序/后序+中序序列可以唯一确定一棵二叉树,所以自然而然可以用来建树。
看一下前序和中序有什么特点,前序1,2,4,7,3,5,6,8 ,中序4,7,2,1,5,3,8,6;
有如下特征:
前序中左起第一位1肯定是根结点,我们可以据此找到中序中根结点的位置rootin;
中序中根结点左边就是左子树结点,右边就是右子树结点,即[左子树结点,根结点,右子树结点],我们就可以得出左子树结点个数为int left = rootin - leftin;;
前序中结点分布应该是:[根结点,左子树结点,右子树结点];
根据前一步确定的左子树个数,可以确定前序中左子树结点和右子树结点的范围;
如果我们要前序遍历生成二叉树的话,下一层递归应该是:
左子树:root->left = pre_order(前序左子树范围,中序左子树范围,前序序列,中序序列);;
右子树:root->right = pre_order(前序右子树范围,中序右子树范围,前序序列,中序序列);。
每一层递归都要返回当前根结点root;

class Solution {
public:
TreeNode* buildTree(vector& preorder, vector& inorder) {
return pre_order(0, inorder.size() - 1, 0, inorder.size() - 1, preorder, inorder);
}

TreeNode *pre_order(int leftpre, int rightpre, int leftin, int rightin, vector<int> &pre, vector<int> &in) {
    if (leftpre > rightpre || leftin > rightin) return NULL;
    TreeNode *root = new TreeNode(pre[leftpre]);
    int rootin = leftin;
    while (rootin <= rightin && in[rootin] != pre[leftpre]) rootin++;
    int left = rootin - leftin;
    root->left = pre_order(leftpre + 1, leftpre + left, leftin, rootin - 1, pre, in);
    root->right = pre_order(leftpre + left + 1, rightpre, rootin + 1, rightin, pre, in);
    return root;
}

};

根据前序和后续遍历构建二叉树
class Solution {
public TreeNode constructFromPrePost(int[] pre, int[] post) {
return helper(pre,post,0,pre.length-1,0,post.length-1);
}
public TreeNode helper(int[] pre,int[] post,int prestart,int preend,int poststart,int postend){
if(prestart>preend||poststart>postend)return null;
TreeNode root=new TreeNode(pre[prestart]);
if (prestart == preend)
return root;
int index=0;
while(post[index]!=pre[prestart+1]){
index++;
}
root.left=helper(pre,post,prestart+1,prestart+1+index-poststart,poststart,index);
root.right=helper(pre,post,prestart+2+index-poststart,preend,index+1,preend-1);
return root;

}

}

pre = [1,2,4,5,3,6,7], post = [4,5,2,6,7,3,1]
输出:[1,2,3,4,5,6,7]
//以先序数组为主 创建节点
TreeNode* node = new TreeNode(pre.at(pre_begin));

    //假设pre_begin + 1为左孩子, 找左孩子在后序数组中的索引 划分左、右子树区域
    if (pre_begin + 1 > pre_end) //注意:防止越界
    {
        return node;
    }

    int index = find_index(post, post_begin, post_end, pre.at(pre_begin + 1));
    
    //后序数组 左子树区域 [post_begin, index]  个数为 index - post_begin + 1
    //         右子树区域 [index + 1, post_end - 1]

    //先序数组  左子树区域 [pre_begin + 1, pre_begin + index - post_begin + 1]
    //          右子树区域 [pre_begin + index - post_begin + 2, pre_end]
    node->left = process(pre, pre_begin + 1, pre_begin + index - post_begin + 1, post, post_begin, index);
    node->right = process(pre, pre_begin + index - post_begin + 2, pre_end, post, index + 1, post_end - 1);

    return node;//+2是紧跟着上面+1来算的,前序遍历的结尾没,name右子树就是从前序遍历的末尾,之后再加1,就是右子树的前序遍历数组开始的位置
}

//通过2来确定,因为2是根节点,在中序遍历中首先是左右根,而在左子树当中,2是根节点,所以从前序遍历中的2是1的左节点,能判断出中序中,左子树的范围就是到2,因为左右根,到根了才到2
pre = [1,2,4,5,3,6,7], post = [4,5,2,6,7,3,1]
输出:[1,2,3,4,5,6,7]
作者:eric-345
链接:https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-postorder-traversal/solution/889-gen-ju-qian-xu-he-hou-xu-bian-li-gou-zao-er–4/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

根据中序和后序遍历来确定一个二叉树,这样的题一定要举例子,把数组写出来,然后确定范围
以下思路参考柳婼文章:已知后序与中序输出前序(先序)
C++。首先要知道一个结论,前序/后序+中序序列可以唯一确定一棵二叉树,所以自然而然可以用来建树。
看一下中序和后序有什么特点,中序[9,3,15,20,7] ,后序[9,15,7,20,3];
有如下特征:
后序中右起第一位3肯定是根结点,我们可以据此找到中序中根结点的位置rootin;
中序中根结点左边就是左子树结点,右边就是右子树结点,即[左子树结点,根结点,右子树结点],我们就可以得出左子树结点个数为int left = rootin - leftin;;
后序中结点分布应该是:[左子树结点,右子树结点,根结点];
根据前一步确定的左子树个数,可以确定后序中左子树结点和右子树结点的范围;
如果我们要前序遍历生成二叉树的话,下一层递归应该是:
左子树:root->left = pre_order(中序左子树范围,后序左子树范围,中序序列,后序序列);;
右子树:root->right = pre_order(中序右子树范围,后序右子树范围,中序序列,后序序列);。
每一层递归都要返回当前根结点root;
代码如下:
class Solution {
public:
TreeNode* buildTree(vector& inorder, vector& postorder) {
return pre_order(0, inorder.size() - 1, 0, inorder.size() - 1, inorder, postorder);
}

TreeNode *pre_order(int leftin, int rightin, int leftpost, int rightpost, vector<int> &in, vector<int> &post) {
    if (leftin > rightin) return NULL;
    TreeNode *root = new TreeNode(post[rightpost]);
    int rootin = leftin;
    while (rootin <= rightin && in[rootin] != post[rightpost]) rootin++;
    int left = rootin - leftin;
    root->left = pre_order(leftin, rootin - 1, leftpost, leftpost + left - 1, in, post);
    root->right = pre_order(rootin + 1, rightin, leftpost + left, rightpost - 1, in, post);
    return root;
}

};

懂了!!!,3道题的套路都是一样的,就是确定大范围就行了,范围用例子来确定,
例如,给出

中序遍历 inorder = [9,3,15,20,7]
后序遍历 postorder = [9,15,7,20,3]
返回如下的二叉树:

3

/
9 20
/
15 7

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

二叉树的下一个节点
bitreenode* getNext(bitreeroot){
if(root==NULL){
return NULL;
}
bitree
pnext=NULL;
if(root->right!=NULL){
bitreenodepnode=root->right;
while(pnode!=NULL&&pnode->left!=NULL){
pnode=pnode->left;
}
pnext=pnode;
}
else if(root->parent!=NULL){
bitreenode
pnode=root;
bitreenode*pparent=node->parent;
while(pparent!=NULL&&pparent->right==pnode){
pnode=pparent;
pparent=pparent->parent;
}
pnext=pparent;

}
return pnext;

}

用2个栈实现队列(所以要用实际的例子模拟一下)
一个栈始终用来插入push,
最主要是另外一个栈,当弹出的时候,就是删除头的时候,那么最先插入的应该被删除,
所以判断如果s2的size如果小于0,那么就从s1中弹出所有元素,然后插入到s2中,如果从s1中插入以后,
s2的size还是<0,那么就return -1;这点逻辑判断得注意,

typename
class cqueue{
typename
void push(const T&val){

}
typename<class T>
T deletehead(){
    判断s2/size小于=0
    循环弹出s1到s2中
    
    出if,判断s2是否小于0,return -1;
    弹出s2,return val;
}

}

class CQueue {
public:
CQueue() {

}

void appendTail(int value) {
    s1.push(value);
}

int deleteHead() {
    if(s2.size()<=0){
        while(s1.size()>0){
            int val=s1.top();
            s2.push(val);
            s1.pop();
        }

    }
        if(s2.size()==0)
            return -1;
        int head=s2.top();
        s2.pop();
        return head;
}
stack<int>s1;
stack<int>s2;

};

/**

  • Your CQueue object will be instantiated and called as such:
  • CQueue* obj = new CQueue();
  • obj->appendTail(value);
  • int param_2 = obj->deleteHead();
    */

因为可能里面就么有数据,然后直接删除头,此时s1也为0,那么s2也为0,所以得判断从s1插入以后
是否s2为空

用2个队列模拟栈,核心就是:一个队列始终用来插入,另一个队列始终用来pop,在要弹出一个元素的时候,
对于pushqueue,while(pushqueue.SIZE()>1)) 剩下一个元素,不插入到popqueue,然后
并且在push的时候,topvalue=pushqueue.front();
popqueue.push(topvalue);
pushqueue.pop();再弹出,
接下来就是重要的了,需要交换2个队列,queue tmp;
pushqueue=popqueue;
popqueue=tmp;
rturn top;

可以记录一个topvalue=push(x)中的x,那么就会复杂度降低到-1;

class MyStack {
//双队列实现栈
public:
/** Initialize your data structure here. */
MyStack() {

}

/** Push element x onto stack. */
void push(int x) {
    pushQueue.push(x);
    topValue = x;
}

/** Removes the element on top of the stack and returns that element. */
int pop() {
    while(pushQueue.size()>1){
        topValue = pushQueue.front();
        popQueue.push(topValue);
        pushQueue.pop();
    }
    int result = pushQueue.front();
    pushQueue.pop();
    queue<int> tmp = pushQueue;
    pushQueue = popQueue;
    popQueue = tmp;
    return result;
}

/** Get the top element. */
int top() {
    return topValue;
}

/** Returns whether the stack is empty. */
bool empty() {
    return pushQueue.empty();
}

private:
queue pushQueue;
queue popQueue;
int topValue;
};

/**

  • Your MyStack object will be instantiated and called as such:
  • MyStack* obj = new MyStack();
  • obj->push(x);
  • int param_2 = obj->pop();
  • int param_3 = obj->top();
  • bool param_4 = obj->empty();
    */

递归:
计算1+2+…n
int getRes(int n){
if(n<=0)
return 0;
return n+getRes(n-1);
}
//非递归算法
int fbcsdn(int n){
int a = 1;
int b = 1;
int c = a;
while (n>2){
c = a + b;
a = b;
b = c;
n–;
}
return c;
}

测试用例:
1.功能测试,2.边界测试 3.性能测试

计数质数
用一个n+1的数组来记录最开始的初始状态
i=2开始,判断每个数是否是质数,如果是质数,然后更新此时与i有关的,j从i+i开始,j的步长是j+=i;
对于每一个符合条件的,都设置flag[j]=false;
然后对于flag[i]=true的,cnt++;
最后返回cnt;

class Solution {
public:
int countPrimes(int n) {
bool flag[n+1];
for(int i=0;i<n+1;i++){
flag[i]=true;
}
int cnt=0;
for(int i=2;i<n;i++){
if(flag[i]==true){

            for(int j=i+i;j<n;j+=i ){
                flag[j]=false;
            }
            cnt++;
        }
    }
    return cnt;
}

};

是否存在重复元素
bool containsNearbyDuplicate(vector& nums, int k) {
map<int,int>m;
for(int i=0;i<nums.size();i++){
if(m.count(nums[i])==0){
m.insert(std::make_pair(nums[i]),i));
}
else{
int index=m[nums[i]];
if(abs(index-i)<=k){
return true;
}
else{
m[nums[i]]=i;
}
}
}
//持续更新索引
return false;

    也可以维护一个set,大小始终是k个,一直遍历数组,比如k=3;此时set里已经有了三个元素,如果
    遍历第四个元素,没有一样的,则清除第一个元素,继续遍历
    
    
    重复元素1;次数大于2
        bool containsDuplicate(vector<int>& nums) {
    unordered_set<int> s;
    int len=nums.size();
    if(len==0)
        return false;
    for(int i=0;i<len;i++)
    {
        if(s.count(nums[i])>0)
        {
            return true;
        }
        else
        {
            s.insert(nums[i]);
        }
    }
        return false;

}

判断子序列
while循环.
bool isSubsequence(string sub, string src) {
int len_sub = sub.length();
int len_src = src.length();
int sub_start = 0;
int src_start = 0;
bool flag = false;
if (sub.empty())
return true;
while (sub_start<len_sub)
{

	if ((sub[sub_start] == src[src_start])&&sub_start==len_sub-1)
	{
		flag = true;
		break;
	}
	else if (sub[sub_start] == src[src_start])
	{
		sub_start++;
		src_start++;
	}
	else
	{
		src_start++;
	}
	if (src_start >= len_src)
	{
		
		break;
	}

}
return flag;
}


or
    bool isSubsequence(string s, string t) {
    int lenS=s.length();
    int lenT=t.length();
    int indexS=0;
    int indexT=0;
    if(lenS==0){
        return true;
    }
    while(indexS<lenS&&indexT<lenT){
        while(t[indexT]!=s[indexS]&&indexT<lenT){
            indexT++;
        }
        if(s[indexS]==t[indexT]&&indexS==lenS-1){
            return true;
        }
        indexS++;
        indexT++;

    }
    return false;

}

旋转数组的最小值:
初始版本:index1和index2,然后求出indexmid,然后判断此时indexmid的这个值如果大于
数组的最开始,那么就让index1=indexmid;还有如果indexmid的这个值小于index2这个值
那么让index2=indexmid;这个思想就是让大的更大,让小的更小,第一种情况,一定出现在右边,
所以index1=indexmid;第二种情况,一定最小值在indexmid左边,所以让index2=indexmid;

于是:while循环条件就是numbers[index1]>=numbers[index2];
里面最开始必须有个判断,if(index2-index1==1)直接break,让indexmid=index2;因为此时index1指向
第一个数组中的最大值,index2指向第二个数组中的最小值,

但是如果出现了10111或者11101这样的情况,那么就无法区分此时的indexmid指向的这个值是位于
第一个排序子数组中还是第二个排序子数组中.
所以如果判断index1和index2还有indexmid的值相等,就顺序查找index1,index2,查找最小值


class Solution {

public:
int minInOrder(vector&numbers,int index1,int index2){
int result=numbers[index1];
for(int i=index1+1;i<=index2;i++){
if(result>numbers[i]){
result=numbers[i];
}
}
return result;
}
int minArray(vector& numbers) {
if(numbers.size()==1){
return numbers[0];
}
int len=numbers.size();
int index1=0;
int index2=len-1;
//int indexMid=0;
int indexMid=index1;

      while(numbers[index1]>=numbers[index2]){
          if(index2-index1==1){
              indexMid=index2;
              break;
          }
          indexMid=(index1+index2)/2;
          if(numbers[index1]==numbers[index2]&&numbers[index1]==numbers[indexMid]){
              return minInOrder(numbers,index1,index2);
          }
          if(numbers[indexMid]>=numbers[index1]){
              index1=indexMid;
          }
          if(numbers[indexMid]<=numbers[index2]){
              index2=indexMid;
          }
          
      }
       return numbers[indexMid]; 
}

};

矩阵中的路径
class Solution {
public:
bool existCore(vector<vector>&board,int row,int col,string word,vector<vector>&visited_,int &pathLen){
if(word[pathLen]==’\0’){
return true;
}
bool flag=false;
if(row>=0&&row<board.size()&&col>=0&&col<board[0].size()&&board[row][col]==word[pathLen]&&visited_[row][col]false){
++pathLen;
visited_[row][col]=true;
flag=existCore(board,row,col+1,word,visited_,pathLen)||
existCore(board,row,col-1,word,visited_,pathLen)||
existCore(board,row-1,col,word,visited_,pathLen)||
existCore(board,row+1,col,word,visited_,pathLen);
if(flag
false){
visited_[row][col]=false;
pathLen–;
}
}

    return flag;

}
bool exist(vector<vector<char>>& board, string word) {
    if(board.size()==0||word.length()==0){
        return false;
    }
    vector<vector<bool>> visited(board.size(),vector<bool>(board[0].size(),0));
    int rows=board.size();
    int cols=board[0].size();
    int pathLen=0;
    for(int row=0;row<rows;row++){
        for(int col=0;col<cols;col++){
            if(existCore(board,row,col,word,visited,pathLen)){
                return true;
            }
        }
    }
    return false;

}

};

主要思路就是:定义一个访问数组,然后定义一个pathlen,然后对于二维矩阵中的每一行每一列,
循环遍历.然后用一个existcore函数,来循环,
existcore里面:判断col row的要求,并且判断visited[row][col]==false,并且我此时board[row][col]==word[pathlen],则{
此时匹配上的路径长度++;
此时的visited数组相对应的row和col设置为true,然后从此row和col开始,用flag=四个函数的||来,判断,row col+1 col-1 col row+1,row-1;
来判断我此时已经增加了的也就是下一个pathlen(要匹配的word的字符)是否相等,如果相等了就返回flag,
}

以上问题考察回溯法的理解,通常在二维矩阵上找路径都可以用

机器人的移动范围,
class Solution {
public:

int movingCount(int m, int n, int k) {

    vector<vector<bool>> visited(m,vector<bool>(n,false));
    int count = movingCountCore(visited, m,n,0, 0, k);
    return count;

}
int movingCountCore(vector<vector<bool>> &visited,int rows,int cols,int m,int n,int k){
    int count = 0;
    if (check(visited,rows, cols,m,n,k)){
        count = 1 + movingCountCore(visited, rows, cols, m, n+1, k)
            + movingCountCore(visited, rows, cols, m, n - 1, k)
            + movingCountCore(visited, rows, cols, m + 1, n, k)
            + movingCountCore(visited, rows, cols, m - 1, n, k);
    }
    return count;
}
bool check(vector<vector<bool>> &visited, int rows, int cols, int m, int n, int k){
    if (m >= 0 && m < rows&&n >= 0 && n < cols&&visited[m][n] == false&&(getNumber(m)+getNumber(n)<=k)){
        visited[m][n] = true;
        return true;
    }
    return false;
}
int getNumber(int n){

    int sum = 0;
    while (n>0)
    {
        sum += n % 10;
        n = n / 10;
    }
    return sum;
}

};

其实核心点还是一样的:
1.定义一个二维数组,然后定义一个核心函数,来进行递归
2.在核心函数里,定义一个临时变量count=0;然后check,此时的row和col是否满足条件
3.check里判断row col,vistied还有数字的位数之和<=k,
4.定义getNumbers函数,

注意:核心的递归函数,count=1+core()+core()+core()+core(),这个1此时就是我当时遍历的col,row指定的方格

注意:通常物体或者人在二维方格上移动都可以用回溯法来解决.

动态规划
剪绳子
class Solution {
public:
int cuttingRope(int n) {
if(n0){
return 0;
}
if(n
1)
return 0;
if(n2)
return 1;
if(n
3)
return 2;
vectordp(n+1,0);
dp[0]=0;//
dp[1]=1;//
dp[2]=2;//注意这不是1
dp[3]=3;//注意这不是2

    for(int i=4;i<=n;i++){
        int max=0;
        for(int j=1;j<=i/2;j++){
            int tmp=dp[j]*dp[i-j];
            if(tmp>max)
                max=tmp;
            dp[i]=max;
        }
    }
    return dp[n];
}

};
注意,起始条件的结果并不是,在动态规划的时候的最优解,所以并不是如if判断那样
给dp赋值,而是这样想,如果一个绳子为3米的时候,那么就是3,2米就是2,1米就是1

lt:虽然有f(0)=f(1)=0;f(2)=1;f(3)=2;f(0)=f(1)=0;f(2)=1;f(3)=2;
但是在表中存下来却不是这个值,
而应该是:p(0)=0;p(1)=1;p(2)=2;p(3)=3;p(0)=0;p(1)=1;p(2)=2;p(3)=3;
即在起始条件的时候小问题的最优解并不是我们求解大问题时使用的那个值

二进制1的个数:
int hammingWeight(uint32_t n) {
int count=0;//这是找规律找到的,n-1之后和n进行与运算,得出清除二进制最后的一个1,
//相当于和1与,但是效率更高,更青睐.
while(n){
count++;
n=(n-1)&n;
}
return count;
}

第二种方法,是判断1,一直左移,注意的是得定义unsigned int flag=1;如果直接定义
int的话,那么当移动到最高位的时候,就是是负数了10000000000000000000000...

第三种:
    移位n,不好,如果n是正数还好,如果n是负数,就不行了,因为负数右移动,会一直填满1
    当移动到最后的时候,编程了32个1,0XFF FF FF FF,

注意负数的边界和正数的边界,最后一个字符是0111也就是7所以,0x7f ff ff ff
0x80 00 00 00 ,8的二进制是1000 0000 …

还需要了解,如果是负数右移动,则不会填充0,而是填充1,移动2位,填充2位1,在最高位

打印1-n的最大数

bool increase(char*str){
int length = strlen(str);
bool overflow = false;
int nTakeOver = 0;

for (int i = length - 1; i >= 0; i--){
	int sum = str[i] - '0' + nTakeOver;
	if (i == length - 1){
		sum++;
	}
	if (sum >= 10){
		if (i == 0){
			overflow = true;
		}
		else
		{
			sum -= 10;
			nTakeOver = 1;
			str[i] =  '0' + sum;
		}


	}
	else{
		str[i] =  '0' + sum;
		break;
	}
}
return overflow;

}
void print(char*str){
bool isBegin0 = true;
int len = strlen(str);
for (int i = 0; i < len; i++){
if (isBegin0&&str[i] != ‘0’){
isBegin0 = false;

	}
	if (!isBegin0){
		printf("%c",str[i]);
	}
}

}
void print1ToMaxN(int n){
if (n <= 0){
return;
}
char*numbers = new char[n + 1];
memset(numbers, 0, n);
numbers[n] = ‘\0’;
while (!increase(numbers)){
print(numbers);
}
delete []numbers;
}

注意的几点:
1.各位数字00 01 02,先取出各位数字的,然后转换成数字并且加上此时是否有进位,相对于前一位,
就是十位看个位是否有,初始肯定没有,ntakeover=0;假设加到10以后,那么此时ntakeover=1;
再循环到十位的时候,也要取出十位的数字str[i]-‘0’+ntakeover;这个就是因为个位相加导致的结果;

2.每次加1都是加在个位上,所以只有i==len-1的时候,才会相加.因为一次for循环,要循环个位和十位,
所以当i=0的时候,也就是十位,而len-1=1的时候,十位的进位还是取决于个位,所以sum不会++

3.

dp[i][j]表示s的0到i-1是否匹配p的0-j-1,就是s的长度为i的时候,能否匹配p的长度为j

class Solution {
public boolean isMatch(String s, String p) {
int slen=s.length();
int plen=p.length();
if(slen0&&plen0)return true;
//if(slen0||plen0)return false;

    boolean[][] dp=new boolean[slen+1][plen+1];
    //dp[i][j]表示s的0到i-1和p的0到j-1是否匹配
    dp[0][0]=true;
    //初始化s=0
    for(int j=1;j<=plen;j++){
        //当s为空时,a*b*c*可以匹配
        //当判断到下标j-1是*,j-2是b,b对应f,要看之前的能否匹配
        //比如a*b*下标依次为ftft,b之前的位t,所以j-1也是true
        //即dp[0][j]对应的下标j-1位true
        if(j==1)dp[0][j]=false;
        if(p.charAt(j-1)=='*'&&dp[0][j-2])dp[0][j]=true;
    }

    //for循环当s长度为1时能否匹配,一直到s长度为slen
    for(int i=1;i<=slen;i++){
        for(int j=1;j<=plen;j++){
            //最简单的两种情况   字符相等或者p的字符是‘.'
            if(s.charAt(i-1)==p.charAt(j-1)||p.charAt(j-1)=='.'){
                dp[i][j]=dp[i-1][j-1];
            }
            //p当前字符是*时,要判断*前边一个字符和s当前字符   
            
            else if(p.charAt(j-1)=='*'){
                if(j<2)dp[i][j]=false;
                 //如果p的*前边字符和s当前字符相等或者p的字符是‘.'
                 //三种可能
                 //匹配0个,比如aa aaa*也就是没有*和*之前的字符也可以匹配上(在你(a*)没来之前我们(aa)已经能匹配上了)dp[i][j]=dp[i][j-2]
                 //匹配1个,比如aab aab* 也就是*和*之前一个字符只匹配s串的当前一个字符就不看*号了  即 dp[i][j]=dp[i][j-1]
                 //匹配多个,比如aabb aab*  b*匹配了bb两个b  那么看aab 和aab*是否能匹配上就行了,即dp[i][j]=dp[i-1][j]
                 if(p.charAt(j-2)==s.charAt(i-1)||p.charAt(j-2)=='.'){
                    dp[i][j]=dp[i-1][j]||dp[i][j-1]||dp[i][j-2];
                }
                //如果p的*前边字符和s当前字符不相等或者p的字符不是‘.',那就把*和*前边一个字符都不要了呗
                //你会发现不管是这种情况还是上边的情况都会有dp[i][j]=dp[i][j-2];所以可以把下边剪枝,不用分开写了
                //这里分开写是为了好理解
                else if(p.charAt(j-2)!=s.charAt(i-1)&&p.charAt(j-2)!='.'){
                    dp[i][j]=dp[i][j-2];
                }
            }
            //其他情况肯定不能匹配上了  直接false  比如 aba abb*c  
            else{
                dp[i][j]=false;
            }
        }
    }
    return dp[slen][plen];
}

}

jinazhoffer上的正则
bool match(charstr,charpattern){
if(str==’\0’&&pattern==’\0’)
return true;
if(str!=’\0’&&pattern==’\0’){
return false;
}
if(
(pattern+1)==’
’){
if(pattern==str||(pattern==’.’&&str!=’\0’)) {
return match(str+1,pattern+2)||
match(str+1,pattern)||
match(str,pattern+2);//str不动,pattern+2,说明我不要这个
和前面的字符
}
else{
return match(str,pattern+2);//str==\0,pattern前面的都匹配,后面的
,所以这个得需要
}
}
if(*str==*pattern||(*pattern==’.’&&*str!=’\0’)){
return match(str+1,pattern+1);
}
return false;
}

说一下思路:
1.首先判断返回true的情况,当str和pattern分别到了最后一个,返回ttrue
2.判断返回false,当pattern到了\0,而str没到\0,则false
3.判断pattern+1,是不是*,
如果是*,判断此时pattern和str指向的是否相等,或者pattern是.并且str!=\0,
则有3种可能,进行下一个状态,str+1,pa+2, str+1,pa,str,pa+2;

如果不是,则str不动,pat+2;


跳出循环,判断之前一个条件,这回是

	const char**pp1 = &p;//表明const修饰的是**pp1指向的值,不能修改

//是否能表示成数字,分别整数部分,小数部分,指数部分
重点:1.最后isnumber,返回isnumnber&&*str==’\0’;看看是否到了最后一个字符,如果不是最后一个字符串
假如是1a,那么不会到达最后一个字符,那么在判断整形的时候就不行.
const char**str,指的是

bool scanUnsignedInteger(const char** str);
bool scanInteger(const char** str);

// 数字的格式可以用A[.[B]][e|EC]或者.B[e|EC]表示,其中A和C都是
// 整数(可以有正负号,也可以没有),而B是一个无符号整数
bool isNumeric(const char* str)
{
if (str == nullptr)
return false;

bool numeric = scanInteger(&str);

// 如果出现'.',接下来是数字的小数部分
if (*str == '.')
{
	++str;

	// 下面一行代码用||的原因:
	// 1. 小数可以没有整数部分,例如.123等于0.123;
	// 2. 小数点后面可以没有数字,例如233.等于233.0;
	// 3. 当然小数点前面和后面可以有数字,例如233.666
	numeric = scanUnsignedInteger(&str) || numeric;
}

// 如果出现'e'或者'E',接下来跟着的是数字的指数部分
if (*str == 'e' || *str == 'E')
{
	++str;

	// 下面一行代码用&&的原因:
	// 1. 当e或E前面没有数字时,整个字符串不能表示数字,例如.e1、e1;
	// 2. 当e或E后面没有整数时,整个字符串不能表示数字,例如12e、12e+5.4
	numeric = numeric && scanInteger(&str);
}

return numeric && *str == '\0';

}

bool scanUnsignedInteger(const char** str)
{
const char* before = *str;
while (**str != ‘\0’ && **str >= ‘0’ && **str <= ‘9’)
++(*str);

// 当str中存在若干0-9的数字时,返回true
return *str > before;

}

// 整数的格式可以用[+|-]B表示, 其中B为无符号整数
bool scanInteger(const char** str)
{
if (**str == ‘+’ || **str == ‘-’)
++(*str);
return scanUnsignedInteger(str);
}

// 测试代码
void Test(const char* testName, const char* str, bool expected)
{
if (testName != nullptr)
printf("%s begins: ", testName);

if (isNumeric(str) == expected)
	printf("Passed.\n");
else
	printf("FAILED.\n");

}

调整数组使奇数位于偶数前面
1.ON2,循环遍历数组,发现偶数,取出这个偶数,然后让数组全部往前移动,将这个数放在最后面

2.快速排序思想:2个指针指向收尾,然后前面找奇数,后面找偶数,循环条件注意是start<end,否则
会无限循环.偶数奇数的判定可以用&0x1来判定,奇数一定1结尾,偶数不是

class Solution {
public:
vector exchange(vector& nums) {
int start=0;
int end=nums.size()-1;

    while(start<end){
        while(nums[start]%2==1&&start<end){
            start++;
        }
        while(nums[end]%2==0&&start<end){
            end--;
        }

        if(start<end){
            int temp=nums[start];
            nums[start]=nums[end];
            nums[end]=temp;
        }
    }
    return nums;
}

};

0x80000000,表示最大的负数,0xffffffff,-1, 如果是unsigend int 是42亿
返回链表倒数第k个节点
ListNode* getKthFromEnd(ListNode* head, int k) {
if(head==NULL){
return NULL;
}
ListNodeslow=head;
ListNode
fast=head;
for(int i=0;i<k;i++){
fast=fast->next;
}

    while(fast!=NULL){
        fast=fast->next;
        slow=slow->next;
    }
    return slow;
}

//链表环的相关问题
1.链表是否有环:定义2个指针,慢指针走一步,快指针走2步,如果有环,一定相遇,如果快指针到了末尾,则返回NULL
2.链表中环的入口:还是可以用2个指针,来解决这个,如果链表中的环有n个节点,则让快指针首先走n步,然后2个指针以相同的速度走,当慢指针走向环的入口点试试,快指针已经走了一圈,到入口节点
3.得到环中节点的数目,前面提到定义2个指针,相遇了就是环中相遇的节点,从这个节点出发,开始计数,再次走到这个节点的时候,就是环中节点的数量.

//返回链表的入口节点 思路:找到相遇的节点,然后求出环的节点个数,然后让快指针走n,快慢指针交点
就是环的入口节点

我写的
ListNodemeetNode(ListNodephead){
if (phead == NULL)
return NULL;
ListNodefast = phead;
ListNode
slow = phead;

while (fast->m_pnext!=NULL&&fast->m_pnext->m_pnext!=NULL&&fast!=slow)
{
	fast = fast->m_pnext;
	//if (fast = NULL)
	//	return NULL;
	fast = fast->m_pnext;
	slow = slow->m_pnext;
}
if (fast==slow)
return slow;
else{
	return NULL;
}

}

求出入口以后:判定是不是NULL;
如果是NULL,返回NULL;
不是NULL
那么求出节点数目:
让快指针先移动n,
然后慢指针开始移动,循环条件是快慢不相等,
返回fast

ListNodeentryOfList(ListNodephead){
ListNodemeetnode = meetNode(phead);
if (meetnode == NULL){
return NULL;
}
int count = 1;
ListNode
p = meetnode;
while (p->m_pnext != meetnode){
count++;
p = p->m_pnext;
}

ListNode*fast = phead;
for (int i = 0; i < count; i++){
	fast = fast->m_pnext;
}

ListNode*slow = phead;
while (slow!=fast)
{
	fast = fast->m_pnext;
	slow = slow->m_pnext;
}
return fast;

}

反转链表 20204.1写的,
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(head==NULL){
return NULL;
}
ListNodeprev=NULL;
ListNode
cur=head;
ListNode*pnext=cur->next;
while(pnext!=NULL){
cur->next=prev;
prev=cur;
cur=pnext;
pnext=pnext->next;
}
cur->next=prev;
return cur;

}

};

以前写的
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(head==NULL)
return NULL;
ListNodepre=NULL;
ListNode
cur=head;
ListNodepnext=cur->next;
/

得判断fast为空
1234 ,当slow移动到3时候,fast移动到4后面一位,如果不判断fast为空,直接用fast->next,崩溃
*/

    while(cur)
    {
        cur->next=pre;
        pre=cur;
        cur=pnext;
        if(pnext!=NULL)
        pnext=pnext->next;
    }
    head=pre;
    return head;
}

};

合并2个排序的链表
if(l1NULL)
return l2;
if(l2
NULL)
return l1;
ListNodedummy=new ListNode(-1);
if(dummy==NULL){
return NULL;
}
ListNode
cur=dummy;
while(l1&&l2){
if(l1->val<=l2->val){
cur->next=l1;
cur=l1;
l1=l1->next;
}
else{
cur->next=l2;
cur=l2;
l2=l2->next;
}
}
cur->next=(l2==NULL?l1:l2);
return dummy->next;
}

也可以用递归的方式,灵魂画手

class Solution {

public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
if(l1NULL)
return l2;
if(l2
NULL)
return l1;
ListNode*newhead=NULL;
if(l1->valval){
newhead=l1;
newhead->next=mergeTwoLists(l1->next,l2);
}
else{
newhead=l2;
newhead->next=mergeTwoLists(l1,l2->next);
}
return newhead;
}
};

二叉树的子结构
/**

  • 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:
    bool isSub(TreeNode
    root,TreeNodesroot){
    if(srootNULL){
    return true;
    }
    if(root
    NULL){
    return false;
    }
    if(root->val!=sroot->val){
    return false;
    }
    return isSub(root->left,sroot->left)&&isSub(root->right,sroot->right);
    }
    bool isSubStructure(TreeNode
    A, TreeNode* B) {
    if(ANULL||BNULL){
    return false;
    }
    bool result=false;
    if(A->val==B->val){
    result=isSub(A,B);
    }
    if(!result){
    result=isSubStructure(A->left,B);
    }
    if(!result){
    result=isSubStructure(A->right,B);

     }
     return result;
    

    }
    };

思路:

  1. issubstruce判断,根节点是否相等,如果相等,则用issub函数,递归判断这2个子树
    2.issubstruce判断,用result,如果此时的root,和sroot判断,不相等,那么result返回false,
    所以!result为真,接着判断此时的root的左子树和sroot,是否相等,也用result接收,
    如果左子树判断也是false,则判断右子树,

所以代码分层很重要啊

第三章:上面的,完整性,规范性,鲁棒性

第四章:
解决面试题的思路:
1.画图,想清楚再动手
2.举例子,想好测试用例,然后从测试用例分析问题,会更好一点,上回那个旋转数组就是因为只想一个用例导致的,不止让指针++的操作
3.

二叉树的镜像:
递归方法:

    迭代方法:
            TreeNode* mirrorTree(TreeNode* root) {
    stack<TreeNode*>s;
    s.push(root);
    while(!s.empty()){
        TreeNode*p=s.top();
        s.pop();
                  if(p==NULL){
                      continue;
                  }
            swap(p->left,p->right);

            s.push(p->left);
            s.push(p->right);


    }
    return root;
}

class Solution {
public:
TreeNode* mirrorTree(TreeNode* root) {
if(rootNULL){
return NULL;//对应于迭代方法的continue
}
/*if(root->left
NULL&&root->right==NULL){
return NULL;
}/
TreeNode
tmp=root->left;
root->left=root->right;
root->right=tmp;
//不要加if,因为要循环递归每一个节点,不是因为不为空才递归
mirrorTree(root->left);//对应于迭代方法push(root->left)

        mirrorTree(root->right);
  
    return root;
    




}

};

对称二叉树

/**

  • 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:
    bool isSymmetricCore(TreeNode
    root1,TreeNode*root2){
    if(root1NULL&&root2NULL){
    return true;//同时到了叶子节点的下一个节点
    }
    if(root1NULL||root2NULL){
    return false;//遍历的这2个节点,如果有一个为空,肯定不行,那么值肯定不相等了
    }
    if(root1->val!=root2->val){
    return false;
    }
    return isSymmetricCore(root1->left,root2->right)&&isSymmetricCore(root1->right,root2->left);

    }
    bool isSymmetric(TreeNode* root) {
    return isSymmetricCore(root,root);
    }
    };

vector spiralOrder(vector<vector>& matrix) {
/判断是否为空/
if(matrix.size() == 0 || matrix[0].size() == 0) return {};
/设置上下左右四个界限/
vector res; /存储遍历结果/
int top = 0;
int bottom = matrix.size() - 1;
int left = 0;
int right = matrix[0].size() - 1;
/此算法模拟顺时针输出的过程,请联想打印过程/
while(true)
{
/1.top行从左到右遍历/
for(int i=left;i<=right;i++){
res.push_back(matrix[top][i]);
}
/top移动至下一行,并进行边界检测/
top++;
if(top > bottom ) break;

      /*2.right列从上到下遍历*/
      for(int i=top;i<=bottom;i++){
          res.push_back(matrix[i][right]);
      }
      /*right左移,并进行边界检测*/
      right--;
      if(right < left) break;
      
      /*3.bottom行从右往左遍历*/
      for(int i = right;i>=left;i--){
          res.push_back(matrix[bottom][i]);
      }
      /*bottom行上移,并进行边界检测*/
      bottom -- ;
      if(bottom < top) break;
      /*4.left列从下往上遍历*/
      for(int i=bottom;i>=top;i--){
          res.push_back(matrix[i][left]);
      }
      /*left右移,并进行边界检测*/
      left++;
      if(left > right) break;
  }
  /*返回遍历结果*/
  return res;
}

class Solution {
public:
void printMatrix(vector<vector>& matrix,int rows,int columns,int start,vector&res){

    //0000   endx


    //       endy
    int endx=columns-start-1;
    int endy=rows-start-1;
    for(int i=start;i<=endx;i++){
        int number=matrix[start][i];
        res.push_back(number);
    }
    if(start<endy){
        for(int i=start+1;i<=endy;i++){
            //int num=matrix[i][start];
            int num=matrix[i][endx];

            res.push_back(num);

        }
    }

    if(start<endx&&start<endy){
        for(int i=endx-1;i>=start;i--){
            int num=matrix[endy][i];
            res.push_back(num);

        }
    }

    if(start<endx&&start<endy-1){
        for(int i=endy-1;i>start;i--){
            int num=matrix[i][start];
            res.push_back(num);

        }
    }
}
vector<int> spiralOrder(vector<vector<int>>& matrix) {
    vector<int>res;

    int rows=matrix.size();
    if(matrix.size()==0)
        return res;
    int columns=matrix[0].size();
    int start=0;
    while(rows>start*2&&columns>start*2){
        printMatrix(matrix,rows,columns,start,res);
        start++;
    }
    return res;
}

};

栈的压入和弹出序列,
第一个是自己写的,思想和后面的一样,就是有点啰嗦
4.2

class Solution {
public:
bool validateStackSequences(vector& pushed, vector& popped) {
/*
if (pushed.size() == 0 && popped.size() == 0){
return true;
}
if(pushed.size() == 0 || popped.size() == 0){
return false;
}
stackst;
int pushLen = pushed.size();
int pushstart = 0;
int popstart = 0;
int poppedLen = popped.size();
while (popstart<poppedLen){

        if (st.size() == 0){
            st.push(pushed[pushstart]);
            pushstart++;
        }
        
        if (st.top() != popped[popstart] && pushstart<pushLen){
            st.push(pushed[pushstart]);
            pushstart++;
            continue;
        }
        else if (st.top() == popped[popstart]){
            popstart++;
            st.pop();
        }
        else{
            popstart++;

        }
    }
    return st.empty();
    */

    stack<int>st;
    int popindex=0;
    for(int i=0;i<pushed.size();i++){
        st.push(pushed[i]);
        while((st.size()>0)&&(st.top()==popped[popindex])){
            popindex++;
            st.pop();
        }
    }
    return st.empty();]
    
    用插入索引来控制,插入
    用while循环来控制弹出,剩下
    2
    1
    的时候,poparray,剩下1,2,
    while循环中,失败,还想继续插入pusharray,但是索引不满足要求,所以退出

}

};

//二叉搜索树的后续遍历,看是否为二叉搜索树

这道题,一定要画出具体的数组,然后指明哪个是end,哪个是end-1;
core函数的,start和end没有确定好,需要用实际例子来写出end-1,或者i-1啥的,注意!,
本章就是用例子来

思路:
先确定根节点,
从头开始遍历,比根节点小的,直到遇到大的
break,
然后遍历比根节点大的,直到遇到小的,return false;
class Solution {
public:
bool verifyPostorderCore(vector&postorder, int start, int end){
if (start >= end){
return true;
}
int root = postorder[end];
int i = start;
for (; i <end; i++){
if (postorder[i] > root){
break;
}
}
int j = i;
for (; j <end; j++){
if (postorder[j] < root){
return false;
}
}
bool left = true;
left = verifyPostorderCore(postorder, start, i - 1);
bool right = true;
right = verifyPostorderCore(postorder, i, end - 1);

    return left&&right;
}
//   7 6 9 11 10 8
bool verifyPostorder(vector<int>& postorder) {
    return verifyPostorderCore(postorder, 0, postorder.size() - 1);
}

};

二叉树和为某一路径
/**

  • 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:
    void pathSumCore(TreeNode
    root,int sum,int &currentSum,vector&path,vector<vector>&res){
    currentSum+=root->val;
    path.push_back(root->val);
    bool isLeaf=root->leftNULL&&root->rightNULL;
    if(isLeaf&&currentSumsum){
    res.push_back(path);
    }
    if(root->left!=NULL){
    pathSumCore(root->left,sum,currentSum,path,res);
    }
    if(root->right!=NULL){
    pathSumCore(root->right,sum,currentSum,path,res);
    }
    //回到父节点
    currentSum-=root->val;
    path.pop_back();
    }
    vector<vector> pathSum(TreeNode* root, int sum) {
    vector<vector>res;
    vectorpath;
    if(root
    NULL){
    return res;
    }
    int currentSum=0;
    pathSumCore(root,sum,currentSum,path,res);
    return res;
    }
    };

关键点:
1.定义一个currentsum表示,当前加的路径和
2.定义一个path,插入当前遍历的节点
3.判断是否是叶子节点,并且和相等,
4.如果第三部没有过,那么遍历当前节点的左子树,和右子树,这前提是不为空的前提下
5.遍历完了以后,我还要回到当前节点,所以currentsum-=当前节点的value;
并且path也弹出当前节的值.

复制链表,用三步:
1.创建节点
2.创建spible指针
3.进行连接

void createComplexList(){
    1.
    2.
    3.
}

将二叉搜索树转换成双向链表

void convertCore(treeroot,treepLastNode){
if(root==NULL{
return;

}
tree*pcur=root;
if(pcur->left!=NULL){
    convertCore(pcur->left,plastnode);
}

// *plastnode=pcur->left;
pcur->left=*plastnode;
if(*plastnode!=NULL){ 1
2 3
*plastnode->right=pcur;// 4 5 6
}//这句话的意义在于当我设置plastnode=2的时候,
// 2->right=5,plastnode=2,再次循环的时候,让2的right指向5
*plastnode=pcur;
if(pcur->right!=NULL){
convertcore(pcur->right,plastnode);
}

}

void convert(treeroot){
if(root==NULL)
return;
tree
plastnode=NULL;
convertCore(&plastnode);
//此时plastnode指向链表尾部

    while(plastnode!=NULL&&plastnoe->left!=NULL){
            plastnode=plastnode->left;
    }
    return plastnode;

}

以下是leetcode题解的答案:比较简洁,但是思路都是一样的,遍历左右子树,然后移动指针

class Solution {
public:
Node* treeToDoublyList(Node* root) {
if(!root) return nullptr;
Node* head = nullptr, pre = nullptr;
helper(root, head, pre);
head->left = pre;
pre->right = head;
return head;
}
void helper(Node
root, Node*& head, Node*& pre) {
if(!root) return;
helper(root->left, head, pre);
if(!head) {
head = root; // 找到head
pre = root; // 对pre进行初始化
} else {
pre->right = root;
root->left = pre;
pre = root;
}
helper(root->right, head, pre);
}
};

       1
   2         3

4 5

最开始遍历到4,然后遍历4的左子树,为空,那么此时head和prev=NULL;
所以head=root(4);prev=root(4);
然后遍历4的右子树,为空,return.返回到2->left的调用栈,然后此时head!=NULL;
然后prev(4)->right=root(2); 2->left=prev(4)更新prev=此时的root值,作为一个中转
节点,然后继续2的右子树(5),
5的左子树为空,说明该访问的就是5,执行prev->right=root;也就是,2->right=5;

然后继续回到2,此时遍历的是1->left,

public class Solution {
public Node treeToDoublyList(Node root) {
if (root == null) {
return null;
}
LinkedList stack = new LinkedList<>();
Node pre = null, head = null;
while (root != null || !stack.isEmpty()) {
if (root != null) {
stack.addLast(root);
root = root.left;
} else {
root = stack.removeLast();
if (pre == null) {
//此时是头节点
head = root;
} else {
pre.right = root;
}
root.left = pre;
pre = root;
root = root.right;
}
}
head.left = pre;
pre.right = head;
return head;
}
}

二叉树的序列化和反序列化

你可以将以下二叉树:

1

/
2 3
/
4 5

序列化为 “[1,2,3,null,null,4,5]”

class Codec {
public:

// Encodes a tree to a single string.
string serialize(TreeNode* root) {
    string data;
    queue<TreeNode*> que;
    if (root) que.push(root);
    
    while (!que.empty()) {
        auto curr = que.front();
        que.pop();
        
        if (curr) {
            data += to_string(curr->val) + ',';
            que.push(curr->left);
            que.push(curr->right);
        } else {
            data += "null,";
        }
    }
    
    if (!data.empty()) data.pop_back();
    return data;
}

// Decodes your encoded data to tree.
TreeNode* deserialize(string data) {
    unique_ptr<TreeNode> dummy(new TreeNode(0));
    queue<TreeNode*> que;
    que.push(dummy.get());
    size_t beg = 0, end = 0;//begin
    bool left_side = false;
    
    while (beg < data.size()) {
        while (end < data.size() && data[end] != ',') ++end;
        auto str = data.substr(beg,   end - beg);
        TreeNode *node = nullptr;
        if (str != "null") node = new TreeNode(atoi(str.c_str()));
        
        auto curr = que.front();
        if (left_side) {
            curr->left = node;
        } else {
            curr->right = node;
            que.pop();
        }
        
        if (node) que.push(node);
        left_side = !left_side;
        beg = ++end;
    }
    
    return dummy->right;
}

};

//下面是我写的

string serialize(TreeNode* root) {

    string res;
    queue<TreeNode*>q;
    q.push(root);
    while(!q.empty()){
        TreeNode*p=q.front();
        q.pop();
        if(p!=NULL){
            res+=to_string(p->val)+",";
            q.push(p->left);
            q.push(p->right);

        }else{
            res+="null,";
        }
    }
    if(!res.empty()) res.pop_back();
    return res;
}

// Decodes your encoded data to tree.
TreeNode* deserialize(string data) {
    bool isLeftTree=false;
    unique_ptr<TreeNode> dummy(new TreeNode(-1));

    queue<TreeNode*>q;
    q.push(dummy.get());
    int start=0;
    int end=0;
    while(!q.empty()){
        while(end<data.length()&&data[end]!=','){
            end++;
        }
        auto str=data.substr(start,end-start);
        TreeNode*newnode=NULL;
        if(str!="null")newnode =new TreeNode(atoi(str.c_str()));
        TreeNode*p=q.front();
        if(!isLeftTree){
            p->right=newnode;
            q.pop();
        }else{ 
            p->left=newnode;
        }
        if(newnode!=NULL)
        q.push(newnode);
        isLeftTree=!isLeftTree;
        start=++end;
        
    }
    return dummy->right;
    }
    }

字符串的全排列:利用回溯方法的最典型的例子;

class Solution {
public:
std::vectorstd::string permutation(std::string s) {
if(s.empty()){
return {};
}

// 对字符串进行排序
std::sort(s.begin(), s.end());
std::vector<std::string> res;
// 标记字符是否遍历过
std::vector<bool> visit(s.size(), false);
std::string track;
backtrack(res, s, track, visit);

return res;

}

/*
 * 回溯函数
 * 使用sort函数对字符串排序,使重复的字符相邻,
 * 使用visit数组记录遍历决策树时每个节点的状态,
 * 节点未遍历且相邻字符不是重复字符时,
 * 则将该字符加入排列字符串中,依次递归遍历。
 * */

void backtrack(std::vectorstd::string &res, std::string s, std::string &track, std::vector &visit) {
// 回溯结束条件
if(track.size() == s.size()){
res.push_back(track);
return;
}

// 选择和选择列表
for(int i = 0; i < s.size(); i++){
    // 排除不合法的选择
    if(visit[i]){
        continue;
    }

    if(i > 0 && !visit[i-1] && s[i-1] == s[i]){
        continue;
    }
    visit[i] = true;

    // 做选择
    track.push_back(s[i]);
    // 进入下一次决策树
    backtrack(res, s, track, visit);
    // 撤销选择
    track.pop_back();
    visit[i] = false;
}

}

};

        if(i>0&&!visited[i-1]&&s[i-1]==s[i]){
            continue;
        }
        注意i-1这个节点保证没有访问过 ,没有理解,下次看看回溯的讲解吧
        
        
        注意这是典型的回溯法,定义一个函数,
        然后用来递归,然后定义一个visited数组,然后定义一个临时tmp字符串
        递归函数开始判断,tmp字符串的是否==s.size(),如果等于的话,
        用res.pushback(tmp);
        然后接下来就是最核心的:
            循环遍历字符串
            然后定义规则:continue
            然后设置visted数组,然后pushback(s[i]);
            然后继续递归
            然后pop出当前字符,然后设置vistied数组

数组中出现一半的数字
class Solution {
public:
int majorityElement(vector& nums) {
//方法1:排序后中间的元素一定是出现超过一半的数字
sort(nums.begin(),nums.end());
return nums[nums.size()/2];

    //方法2:哈希表
    unordered_map<int,int>mp;
    for(auto it : nums){
        mp[it]++;
        if(mp[it]>nums.size()/2) return it;
    }
    return 0;

13121
//方法3:超过一半的数字比其他所有数字的总和次数多
int n=1;
int result=nums[0];
for(int i=1;i<nums.size();i++){
if(n0){
result=nums[i];
n=1;
}
else if(result
nums[i])n++;
else n–;
}
return result;
}
};

    int n=1;//用来记录此时的次数 
    //思路:1.如果下一个数字和当前result不相等,n--,如果相等++,如果次数=0;那么让result=num[i],然后n=1;

之所以定义一个检查,是为了考虑全面,如果函数退出了,result在数组当中不满足.所以看题意.
int Partition(vector&data, int length, int start, int end)
{
//if (data == nullptr || length <= 0 || start < 0 || end >= length)
// throw new std::exception(“Invalid Parameters”);

int index = RandomInRange(start, end);//选取6位中间数
Swap(&data[index], &data[end]);

int small1 = start - 1;
for (index = start; index < end; ++index)
{
	if (data[index] < data[end])
	{
		++small1;
		if (small1 != index)
			Swap(&data[index], &data[small1]);
	}
}

++small1;
Swap(&data[small1], &data[end]);

return small1;	//return small1;//表明比当前选择数小的个数,或者当前中位数的索引

}

选取一个数,然后排序

最小的k个数

vector getLeastKnum(int k, vector&input, int start, int end){

int index = Partition(input, input.size(), 0, input.size() - 1);
while (index!=k-1)
{
	if (index > k - 1){
		end = index - 1;
		index = Partition(input, input.size(), start, end);
	}
	else{
		start = index + 1;
		index = Partition(input, input.size(), start, end);

	}
}
vector<int>output(k,0);
for (int i = 0; i < k; i++){
	output[i] = input[i];
}
return output;

}

class Solution {
public:

vector<int> getLeastNumbers(vector<int>& arr, int k) {
    multiset<int,greater<int>>mset;
    for(auto&val:arr){
        if(mset.size()<k){
            mset.insert(val);
        }
        else{
            multiset<int,greater<int>>::iterator iter=mset.begin();
            if(*iter>val){

          //注意erase(*iter)是有区别的,erase(传入的是迭代器)      
                            mset.erase(iter);
                mset.insert(val);
            }
        }
    }

    vector<int>v(mset.begin(),mset.end());
    return v;
}

};

解释:
方法三:快排思想
思路和算法

我们可以借鉴快速排序的思想。我们知道快排的划分函数每次执行完后都能将数组分成两个部分,小于等于分界值 pivot 的元素的都会被放到数组的左边,大于的都会被放到数组的右边,然后返回分界值的下标。与快速排序不同的是,快速排序会根据分界值的下标递归处理划分的两侧,而这里我们只处理划分的一边。

我们定义函数 randomized_selected(arr, l, r, k) 表示划分数组 arr 的 [l,r] 部分,使前 k 小的数在数组的左侧,在函数里我们调用快排的划分函数,假设划分函数返回的下标是 pos(表示分界值 pivot 最终在数组中的位置),即 pivot 是数组中第 pos - l + 1 小的数,那么一共会有三种情况:

如果 pos - l + 1 == k,表示 pivot 就是第 kk 小的数,直接返回即可;

如果 pos - l + 1 < k,表示第 kk 小的数在 pivot 的右侧,因此递归调用 randomized_selected(arr, pos + 1, r, k - (pos - l + 1));

如果 pos - l + 1 > k,表示第 kk 小的数在 pivot 的左侧,递归调用 randomized_selected(arr, l, pos - 1, k)。

函数递归入口为 randomized_selected(arr, 0, arr.length - 1, k)。在函数返回后,将前 k 个数放入答案数组返回即可。

C++Python
class Solution {
int partition(vector& nums, int l, int r) {
int pivot = nums[r];
int i = l - 1;
for (int j = l; j <= r - 1; ++j) {
if (nums[j] <= pivot) {
i = i + 1;
swap(nums[i], nums[j]);
}
}
swap(nums[i + 1], nums[r]);
return i + 1;
}
// 基于随机的划分
int randomized_partition(vector& nums, int l, int r) {
int i = rand() % (r - l + 1) + l;
swap(nums[r], nums[i]);
return partition(nums, l, r);
}
void randomized_selected(vector& arr, int l, int r, int k) {
if (l >= r) return;
int pos = randomized_partition(arr, l, r);
int num = pos - l + 1;
if (k == num) return;
else if (k < num) randomized_selected(arr, l, pos - 1, k);
else randomized_selected(arr, pos + 1, r, k - num);
}
public:
vector getLeastNumbers(vector& arr, int k) {
srand((unsigned)time(NULL));
randomized_selected(arr, 0, (int)arr.size() - 1, k);
vectorvec;
for (int i = 0; i < k; ++i) vec.push_back(arr[i]);
return vec;
}
};
复杂度分析

时间复杂度:期望为 O(n)O(n) ,由于证明过程很繁琐,所以不再这里展开讲。具体证明可以参考《算法导论》第 9 章第 2 小节。

最坏情况下的时间复杂度为 O(n^2)O(n
2
)。情况最差时,每次的划分点都是最大值或最小值,一共需要划分 n - 1n−1 次,而一次划分需要线性的时间复杂度,所以最坏情况下时间复杂度为 O(n^2)O(n
2
)。

空间复杂度:期望为 O(\log n)O(logn),递归调用的期望深度为 O(\log n)O(logn),每层需要的空间为 O(1)O(1),只有常数个变量。

最坏情况下的空间复杂度为 O(n)O(n)。最坏情况下需要划分 nn 次,即 randomized_selected 函数递归调用最深 n - 1n−1 层,而每层由于需要 O(1)O(1) 的空间,所以一共需要 O(n)O(n) 的空间复杂度。

方法二:快排变形
Top K 问题的另一个解法就比较难想到,需要在平时有算法的积累。实际上,“查找第 k 大的元素”是一类算法问题,称为选择问题。找第 k 大的数,或者找前 k 大的数,有一个经典的 quick select(快速选择)算法。这个名字和 quick sort(快速排序)看起来很像,算法的思想也和快速排序类似,都是分治法的思想。

让我们回顾快速排序的思路。快速排序中有一步很重要的操作是 partition(划分),从数组中随机选取一个枢纽元素 v,然后原地移动数组中的元素,使得比 v 小的元素在 v 的左边,比 v 大的元素在 v 的右边,如下图所示:

这个 partition 操作是原地进行的,需要 O(n)O(n) 的时间,接下来,快速排序会递归地排序左右两侧的数组。而快速选择(quick select)算法的不同之处在于,接下来只需要递归地选择一侧的数组。快速选择算法想当于一个“不完全”的快速排序,因为我们只需要知道最小的 k 个数是哪些,并不需要知道它们的顺序。

我们的目的是寻找最小的 kk 个数。假设经过一次 partition 操作,枢纽元素位于下标 mm,也就是说,左侧的数组有 mm 个元素,是原数组中最小的 mm 个数。那么:

若 k = mk=m,我们就找到了最小的 kk 个数,就是左侧的数组;
若 k<mk<m ,则最小的 kk 个数一定都在左侧数组中,我们只需要对左侧数组递归地 parition 即可;
若 k>mk>m,则左侧数组中的 mm 个数都属于最小的 kk 个数,我们还需要在右侧数组中寻找最小的 k-mk−m 个数,对右侧数组递归地 partition 即可。
这种方法需要多加领会思想,如果你对快速排序掌握得很好,那么稍加推导应该不难掌握 quick select 的要领。
public int[] getLeastNumbers(int[] arr, int k) {
if (k == 0) {
return new int[0];
} else if (arr.length <= k) {
return arr;
}

// 原地不断划分数组
partitionArray(arr, 0, arr.length - 1, k);

// 数组的前 k 个数此时就是最小的 k 个数,将其存入结果
int[] res = new int[k];
for (int i = 0; i < k; i++) {
    res[i] = arr[i];
}
return res;

}

void partitionArray(int[] arr, int lo, int hi, int k) {
// 做一次 partition 操作
int m = partition(arr, lo, hi);
// 此时数组前 m 个数,就是最小的 m 个数
if (k == m) {
// 正好找到最小的 k(m) 个数
return;
} else if (k < m) {
// 最小的 k 个数一定在前 m 个数中,递归划分
partitionArray(arr, lo, m-1, k);
} else {
// 在右侧数组中寻找最小的 k-m 个数
partitionArray(arr, m+1, hi, k);
}
}

// partition 函数和快速排序中相同,具体可参考快速排序相关的资料
// 代码参考 Sedgewick 的《算法4》
int partition(int[] a, int lo, int hi) {
int i = lo;
int j = hi + 1;
int v = a[lo];
while (true) {
while (a[++i] < v) {
if (i == hi) {
break;
}
}
while (a[–j] > v) {
if (j == lo) {
break;
}
}

    if (i >= j) {
        break;
    }
    swap(a, i, j);
}
swap(a, lo, j);

// a[lo .. j-1] <= a[j] <= a[j+1 .. hi]
return j;

}

void swap(int[] a, int i, int j) {
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}

//数据流中的中位数
class MedianFinder {
priority_queue lo; // max heap
priority_queue<int, vector, greater> hi; // min heap

public:
// Adds a number into the data structure.
void addNum(int num)
{
lo.push(num); // Add to max heap

	hi.push(lo.top());                               // balancing step
	lo.pop();

	if (lo.size() < hi.size()) {                     // maintain size property
		lo.push(hi.top());
		hi.pop();
	}
}

// Returns the median of current data stream
double findMedian()
{
	return lo.size() > hi.size() ? (double)lo.top() : (lo.top() + hi.top()) * 0.5;
}

};

最大连续子数组
定义一个cursum=0;
定义一个greatest=0x80000000;
for循环从最开始,
如果cursum<=0,则更新ncursum=nums[i];
否则让cursum+=nums[i];

然后判断ncursum是否大于greatest
如果大于,ngreat=ncursum,
return ngreat

     int ncursum=0;
    int ngreatesum=0x80000000;
    for(int i=0;i<nums.size();i++){
        if(ncursum<=0){
            ncursum=nums[i];
        }
        else
            {
                ncursum+=nums[i];
            }
        if(ncursum>ngreatesum){
            ngreatesum=ncursum;
        }
    }
    return ngreatesum;


动态规划法
dp[i]表示,到第i个元素的最大连续子数组的和
dp[1]表示,到第二个元素的最大值
dp[2]表示,到第三个元素的最大值,


下面的代码中,我也可以不用res,直接遍历dp数组,这样会增加复杂度,
所以每次更新res值,会更好点.
int maxSubArray(vector<int>& nums) {
    
    int size=nums.size();
    if(size==0)
        return 0;
    vector<int>dp(nums.size());
    dp[0]=nums[0];
    int res=dp[0];
    for(int i=1;i<size;i++){
        dp[i]=dp[i-1]<0?(nums[i]):(dp[i-1]+nums[i]);
        if(dp[i]>res)
            res=dp[i];
    }
    return res;

数字序列中某一位的数字
int countOfIntegers(int digits);
int digitAtIndex(int index, int digits);
int beginNumber(int digits);

int digitAtIndex(int index)
{
if (index < 0)
return -1;

int digits = 1;
while (true)
{
	int numbers = countOfIntegers(digits);
	if (index < numbers * digits)
		return digitAtIndex(index, digits);

	index -= digits * numbers;
	digits++;
}

return -1;

}

int countOfIntegers(int digits)
{
if (digits == 1)
return 10;

int count = (int)std::pow(10, digits - 1);
return 9 * count;

}

int digitAtIndex(int index, int digits)
{
int number = beginNumber(digits) + index / digits;
int indexFromRight = digits - index % digits;
for (int i = 1; i < indexFromRight; ++i)
number /= 10;
return number % 10;//如果此时是100,偏移是3,那么我100要除2次=1,然后1%10=1,就取出来1了,这个indexfromright就是从右面数是第几个索引,
//一个数字有3位数,如果indexfromright=3;那么最右面就是1,中间是2,最左边是3,所以要取的就是最左面的数字
}

int beginNumber(int digits)
{
if (digits == 1)
return 0;

return (int)std::pow(10, digits - 1);

}

// 测试代码
void test(const char* testName, int inputIndex, int expectedOutput)
{
if (digitAtIndex(inputIndex) == expectedOutput)
cout << testName << " passed." << endl;
else
cout << testName << " FAILED." << endl;
}

int main()
{
test(“Test1”, 0, 0);
test(“Test2”, 1, 1);
test(“Test3”, 9, 9);
test(“Test4”, 10, 1);
test(“Test5”, 189, 9); // 数字99的最后一位,9
test(“Test6”, 190, 1); // 数字100的第一位,1
test(“Test7”, 1000, 3); // 数字370的第一位,3
test(“Test8”, 1001, 7); // 数字370的第二位,7
test(“Test9”, 1002, 0); // 数字370的第三位,0
return 0;
}

class Solution {
public:
int findNthDigit(int n) {
if(n<=9){
return n;
}
n-=9;
long long count=90,dig=2;
while(n-digcount>0){
n-=(count
dig);
count*=10;
dig++;
}
long long num=pow(10,dig-1)+n/dig;
if(n%dig==0){
num–;
return num % 10;
}
else
{ //如果是这个数的第二位例如 7888 那么应该78/100%10
for (int i = 0; i < (dig - n % dig); i++)
{
num /= 10;
}
return num % 10;
}
}
};

把数组排成最小的数 12 3 45,但是最后还需要证明,算了
class Solution {
public:
string minNumber(vector& nums) {
vectorstrs;
string res;
for(auto num:nums){
strs.push_back(to_string(num));
}
sort(strs.begin(),strs.end(),compare);
for(auto str:strs){
res+=str;
}
return res;

}
static bool compare(string&a,string &b){
    return a+b<b+a;
}

};

将数字翻译成字符串
class Solution {
public:

int translateNum(int num) {

    string str=to_string(num);
    int length=str.length();
    vector<int>counts(length,0);
    int count=0;
    for(int i=length-1;i>=0;i--){
        count=0;
        if(i<length-1){
            count=counts[i+1];

        }
        else{
            count=1;

        }
        if(i<length-1){
            int dig1=str[i]-'0';
            int dig2=str[i+1]-'0';
            int tmp=dig1*10+dig2;
            if(tmp>=10&&tmp<=25){
                if(i<length-2){
                count+=counts[i+2];

                }
                else{
                    count+=1;
                }
            }
        }
        counts[i]=count;

    }
    return counts[0];
}

};

重点理解就是这:
count+=counts[i+2];

 假如12258
 此时我从后往前数,258此时的counts[2]=2;
 i=1的时候,那么再加上一个2,counts[1](22 5 8 ,2258,2 258)一共3个,此时258是2个,
 那么i=1,我2跟2结合以后,22和58,count=count+counts[3](假如2581,就是258后还有数据,这个数就不是1了)

求礼物的最大价值
class Solution {
public:
int maxValue(vector<vector>& grid) {
//定义dp[i][j]为到达i,j所拿的最大值
if(grid.size()==0){
return 0;
}
int rows=grid.size();
int cols=grid[0].size();
vector<vector>dp(rows,vector(cols,0));
//初始化
dp[0][0] = grid[0][0];
for (int i = 1; i<rows; i++){
dp[i][0] += dp[i-1][0]+grid[i][0];
}
for (int j = 1; j<cols; j++){
dp[0][j] += dp[0][j-1] + grid[0][j];
}
for(int i=1;i<rows;i++){
for(int j=1;j<cols;j++){
dp[i][j]=max(dp[i-1][j],dp[i][j-1])+grid[i][j];
}
}
return dp[rows-1][cols-1];

}

};

无重复字符的最长子串,无重复字符的最长子串,无重复字符的最长子串,无重复字符的最长子串
用substr方法来,用substr方法,定义一个空的str
如果不包含字符,然后插入,包含了,找出位置,然后擦除substr0,pos位置的字符,然后
将当前字符插进去,比较res的大小
//效率最高的解法
int len=s.size();
if(len==0)
return 0;
string substr;
int res=0;
for(int i=0;i<len;++i)
{
if(substr.find(s[i])!=string::npos)
{
int pos=substr.find(s[i])+1;
substr.erase(0,pos);
substr.push_back(s[i]);
res=max(res,(int)substr.size());

        }
        else
        {
            substr.push_back(s[i]);
            res=max(res,(int)substr.size());


        }
    }
    return res;

//无重复字符的最长子串,下面有BUG,offer上的
if(str==" "){
return 1;
}
int curLen=0;
int maxLen=0;
vectorposition(26,0);
for(int i=0;i<26;i++){
position[i]=-1;
}
for(int i=0;i<str.length();i++){
int prevIndex=position[str[i]-‘a’];
if(prevIndex<0||i-prevIndex>curLen){
curLen++;
}
else{
if(curLen>maxLen){
maxLen=curLen;
}
curLen=i-prevIndex;
}
position[str[i]-‘a’]=i;

        if(curLen>maxLen){
            maxLen=curLen;
        }
    }
    return maxLen;

.

效率不高的解法
int lengthOfLongestSubstring(string s) {
    

    int len=s.size();
    if(len==0)
    {
        return 0;
    }

    //unordered_set<char> uset1;
    unordered_set<char> lookup;
    int maxlen=0;
    int left=0;//这也是双指针法,只是用for循环i编程j,来循环字符串
    
    for(int i=0;i<len;i++)
    {
        while(lookup.find(s[i])!=lookup.end())//这个while循环,在
        //双指针里,直接从最外面的while循环解决.,这里直接从内层循环解决
        //如果用subsr.find返回pos会少去遍历的结果,时间复杂度减小
        {
            //uset1.erase(s[left]);
            lookup.erase(s[left]);
            left++;//left始终指向原字符串待删除的字符,
        }
        
        
            lookup.insert(s[i]);
        
        maxlen=max(maxlen,i-left+1);

    }
    return maxlen;
}

双指针法
int lengthOfLongestSubstring(string s) {

    int len=s.length();
    if(len==0)
    return 0;

    int maxLen=0;

    int i=0;
    int j=0;
    unordered_set<char> uset;
    while(i<len&&j<len)
    {   
        if(uset.find(s[j])!=uset.end())
        {
            uset.erase(s[i]);
            i++;
        }
        else
        {
            uset.insert(s[j]);
            maxLen=max(maxLen,j-i+1);//要在j++之前进行取值
            j++;
        }
    }
    return maxLen;

class Solution {
public:
int lengthOfLongestSubstring(string s) {
int len=s.length();
if(len==0)
return 0;
int i=0,j=0;
int maxlen=0;
unordered_setuset;
while(i<len&&j<len){
if(uset.find(s[j])!=uset.end()){
uset.erase(s[i]);
i++;

        }
        else{
            uset.insert(s[j]);
            maxlen=max(maxlen,j-i+1);
            j++;

        }
    }
    return maxlen;
}

};

面试题49,丑数,
丑数数组只装丑数,丑数,一定是一个丑数乘以2,3,5,
同时更新索引
class Solution {
public:
int minNum(int num1,int num2,int num3){
int num=num1>num2?num2:num1;
return num>num3?num3:num;
}
int nthUglyNumber(int n) {

intuglynumber=new int[n];
uglynumber[0]=1;
int nextindex=1;
int
p1=uglynumber;
intp2=uglynumber;
int
p3=uglynumber;
while(nextindex<n){
int tmp=minNum(p12,p23,p35);
uglynumber[nextindex]=tmp;
//更新指针指向
while(p12<=uglynumber[nextindex]){
p1++;
}
while(p23<=uglynumber[nextindex]){
p2++;
}
while(p35<=uglynumber[nextindex]){
p3++;
}

   nextindex++;

}
return uglynumber[n-1];
}
};

C++

//int*uglynumber = new int[n];
vectoruglynumber(n, 0);
uglynumber[0] = 1;
int nextindex = 1;
int p1=0 ;
int p2 =0;
int p3=0;
while (nextindex<n){
int tmp = minNum(uglynumber[p1] * 2, uglynumber[p2] * 3, uglynumber[p3] * 5);
uglynumber[nextindex] = tmp;
//更新指针指向
while ((uglynumber[p1] * 2) <= uglynumber[nextindex]){
p1++;
//这个p1用于下次的比较,最开始就是P1 P2 P3=0,每一次都更新P1,P2,P3,下次计算minnum的时候,
//此时uglynumber[p1] * 2,是此时大于uglynumber[nextindex]最小的数,也就是书上所说的,此时这个数在所有丑数乘以2的时候
//他是第一个大于uglynumber[nextindex]的数,同理p2,p3,也是, 但是还需要比较他们3个到底哪个才是大于uglynumber[nextindex]最小的数
}
while ((uglynumber[p2] * 3) <= uglynumber[nextindex]){
p2++;
}
while ((uglynumber[p3] * 5) <= uglynumber[nextindex]){
p3++;
}

		nextindex++;
	}
	return uglynumber[n - 1];

第一个只出现次的字符

class Solution {
public:
char firstUniqChar(string s) {
vectorv(256,0);
for(int i=0;i<s.length();i++){
v[s[i]]++;
}
int i=0;
for( i=0;i<s.length();i++){
if(v[s[i]]==1){
return s[i];
}
}

        return ' ';

}

};


//判断第一个字符串中的字符是不是在第二个字符串中,除了用string.find==str2.end();以外.
还可以用数组,也就是上面的方式,这样省去了时间复杂度,提前for循环
for(int i=0;i<str.length();i++){
vec[str[i]]++;
}
string test=123;
if(vec[test[0]]==1){
证明存在,
}


string go = "google";
vector<bool>vb(256, false);
for (int i = 0; i < go.length();){
	if (vb[go[i]] == false){
		vb[go[i]] = true;
		i++;
	}
	else{
		go.erase(go.begin() + i);
	}
}

s2.erase(s2.begin());//删除开始的字符,erase出入的是迭代器
s2.erase(s2.begin(),s2.begin()+2);//从开始删除2个字符

变位词组
class Solution {
public:
vector<vector> groupAnagrams(vector& strs) {
unordered_map<string,vector>um;
for(int i=0;i<strs.size();i++){
auto t=strs[i];
sort(t.begin(),t.end());
um[t].push_back(strs[i]);
}
vector<vector>res;
for(auto&val:um){
res.push_back(val.second);
}
return res;
}
};

/*******************************************************************
Copyright© 2016, Harry He
All rights reserved.

Distributed under the BSD license.
(See accompanying file LICENSE.txt at
https://github.com/zhedahht/CodingInterviewChinese2/blob/master/LICENSE.txt)
*******************************************************************/

//==================================================================
// 《剑指Offer——名企面试官精讲典型编程题》代码
// 作者:何海涛
//==================================================================

// 面试题50(二):字符流中第一个只出现一次的字符
// 题目:请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从
// 字符流中只读出前两个字符"go"时,第一个只出现一次的字符是’g’。当从该字
// 符流中读出前六个字符"google"时,第一个只出现一次的字符是’l’。

#include
#include
#include

using namespace std;

class CharStatistics
{
public:
CharStatistics() : index(0)
{
for(int i = 0; i < 256; ++i)
occurrence[i] = -1;
}

void Insert(char ch)
{
    if(occurrence[ch] == -1)
        occurrence[ch] = index;
    else if(occurrence[ch] >= 0)
        occurrence[ch] = -2;

    index++;
}

char FirstAppearingOnce()
{
    char ch = '\0';
    int minIndex =INT_MAX;
    for(int i = 0; i < 256; ++i)
    {
        if(occurrence[i] >= 0 && occurrence[i] < minIndex)
        {
            ch = (char) i;
            minIndex = occurrence[i];
            //MININDEX 作用就是假如是za,z(122)的索引是0
            //a(97) 的索引是1
            在循环256的时候,我先遍历a,minindex=1;
            下次便利z的时候,z是0<mininex,并且occu[97]>=0(只出现一次),因为
            插入的时候,更新索引了,如果是重复的不会进入,因为是-2了,
            
            
            index用于记录每次插入的索引,因为可能会一直调用,所以用类成员变量保存,然后赋值给
            字母,列如map[a]=1;map[z]=0;
        }
    }

    return ch;
}

private:
// occurrence[i]: A character with ASCII value i;
// occurrence[i] = -1: The character has not found;
// occurrence[i] = -2: The character has been found for mutlple times
// occurrence[i] >= 0: The character has been found only once
int occurrence[256];
int index;
};

// 测试代码
void Test(const char* testName, CharStatistics chars, char expected)
{
if(testName != nullptr)
printf("%s begins: ", testName);

if(chars.FirstAppearingOnce() == expected)
    printf("Passed.\n");
else
    printf("FAILED.\n");

}

int main(int argc, char* argv[])
{
CharStatistics chars;

Test("Test1", chars, '\0');

chars.Insert('g');
Test("Test2", chars, 'g');

chars.Insert('o');
Test("Test3", chars, 'g');

chars.Insert('o');
Test("Test4", chars, 'g');

chars.Insert('g');
Test("Test5", chars, '\0');

chars.Insert('l');
Test("Test6", chars, 'l');

chars.Insert('e');
Test("Test7", chars, 'l');

return 0;

}

面试题 51
数组中的逆序对

利用归并排序,不会,暂时没看

面试题 52,链表的第一个公共节点
/**

  • Definition for singly-linked list.
  • struct ListNode {
  • int val;
    
  • ListNode *next;
    
  • ListNode(int x) : val(x), next(NULL) {}
    
  • };
    */
    class Solution {
    public:
    ListNode *getIntersectionNode(ListNode headA, ListNode headB) {
    if(headANULL||headBNULL){
    return NULL;
    }
    map<ListNode
    ,bool>mp;
    ListNode
    tmp=headA;
    while(tmp!=NULL){
    mp[tmp]=true;
    tmp=tmp->next;
    }
    tmp=headB;
    while(tmp!=NULL){
    if(mp[tmp]!=NULL){
    return tmp;
    }
    tmp=tmp->next;
    }
    return NULL;
    }
    };

//走的路线一样长,都是一整个
class Solution {
public:
ListNode *getIntersectionNode(ListNode headA, ListNode headB) {
ListNode
PA=headA;
ListNode
PB=headB;

    while(PA!=PB){
        PA=PA==NULL?headB:PA->next;
        PB=PB==NULL?headA:PB->next;
    }
    return PA;

}

};

也可以用求长度方法,
有个小技巧,先定义一个长度diff-len1-len2,假设len1比len2长,
所以之后再做个判断,如果len2>len1,再更新pshort,plong,ndiff的值,这样就不用求绝对值了
unsigned int GetListLength(ListNode* pHead);

ListNode* FindFirstCommonNode(ListNode *pHead1, ListNode *pHead2)
{
// 得到两个链表的长度
unsigned int nLength1 = GetListLength(pHead1);
unsigned int nLength2 = GetListLength(pHead2);
int nLengthDif = nLength1 - nLength2;

ListNode* pListHeadLong = pHead1;
ListNode* pListHeadShort = pHead2;
if(nLength2 > nLength1)
{
    pListHeadLong = pHead2;
    pListHeadShort = pHead1;
    nLengthDif = nLength2 - nLength1;
}

// 先在长链表上走几步,再同时在两个链表上遍历
for(int i = 0; i < nLengthDif; ++i)
    pListHeadLong = pListHeadLong->m_pNext;

while((pListHeadLong != nullptr) &&
    (pListHeadShort != nullptr) &&
    (pListHeadLong != pListHeadShort))
{
    pListHeadLong = pListHeadLong->m_pNext;
    pListHeadShort = pListHeadShort->m_pNext;
}

// 得到第一个公共结点
ListNode* pFisrtCommonNode = pListHeadLong;

return pFisrtCommonNode;

}

unsigned int GetListLength(ListNode* pHead)
{
unsigned int nLength = 0;
ListNode* pNode = pHead;
while(pNode != nullptr)
{
++nLength;
pNode = pNode->m_pNext;
}

return nLength;

}

//我写的,还得各种判断哪个长哪个短,所以定义一个plong和pshort真的能方便很多,
记住了!

getlistlength封装成函数会比较好看点

在排序数组中查找数字出现的次数

123333345 3出现4次
class Solution {
public:
int getFirstK(vector& nums,int len,int target,int start,int end){
if(start>end){
return -1;
}
int middleindex=(start+end)/2;
int middledata=nums[middleindex];
if(middledatatarget){
if((middleindex>0&&nums[middleindex-1]!=target)||middleindex
0){
return middleindex;
}
else{
end=middleindex-1;
}
}
else if(middledata>target){
end=middleindex-1;
}
else{
start=middleindex+1;
}
return getFirstK(nums,len,target,start,end);

}
int getLastK(vector<int>& nums,int len,int target,int start,int end){
    if(start>end){
        return -1;
    }
    int middleindex=(start+end)/2;
    int middledata=nums[middleindex];
    if(middledata==target){
        if((middleindex<len-1&&nums[middleindex+1]!=target)||middleindex==len-1){
            return middleindex;
        }else{
            start=middleindex+1;
        }
    }else if(middledata>target){
        end=middleindex-1;
    }else{
        start=middleindex+1;
    }
    return getLastK(nums,len,target,start,end);
}
int search(vector<int>& nums, int target) {
    int number=0;
    if(nums.size()!=0){
        int firstk=getFirstK(nums,nums.size(),target,0,nums.size()-1);
        int lastk=getLastK(nums,nums.size(),target,0,nums.size()-1);
        if(firstk!=-1&&lastk!=-1){
            number= lastk-firstk+1;
        }
    }
    return number;
    
    
}

};

0-n-1,缺失的数字
class Solution {
public:
int missingNumber(vector& nums) {
int len=nums.size();
int start=0;
int end=len-1;
while(start<=end){
int mid=(start+end)/2;
if(nums[mid]!=mid){
if(mid==0||nums[mid-1]==mid-1){
return mid;
}
end=mid-1;
}else{
start=mid+1;
}

    }
    if(start==len){ //防止输入1个数组,比如0,   应该输出1,因为n=2,
        return len;
    }
    return -1;
}

};

数组中数值和下标相等的元素(经验,一定要举例子,实际的才行,才能写出来代码,所以要想好测试用例)

//排序数组中元素和索引相等的元素
//-3 -1 1 3 5

//-3 -1 1 2 5

//-3 -1 1 2 4

//126 9 38
//如果此时我的num[mid]大于此时的索引,那么无论如何,索引的速度都追不上排序数组数字增长的速度,都是按照+1,也是由于此时数字大于索引的原因,所以追不上
int findItem(vector&nums){
if (nums.size() == 0){
return -1;
}
int len = nums.size();
int start = 0;
int end = len - 1;
while (start<=end){
int mid = (start + end) / 2;
if (nums[mid]==mid){
return nums[mid];
}
else if (nums[mid]<mid){
start = mid + 1;
}
else{
end = mid - 1;
}

}
return -1;

}

二叉搜索树中

class Solution {
public:
int ans=0,cnt=0;
int kthLargest(TreeNode* root, int k) {

    dfs(root,k);
    return ans;
}

void dfs(TreeNode*root,int k){
    if(root==NULL)
        return;
    dfs(root->right,k);//右
    if(++cnt==k){//根
        ans=root->val;
    }
    dfs(root->left,k);//左
}

};

上面是右跟左,

以下是比较直白的方式
class Solution {
public:
void help(TreeNode* root,vector&vec){
if(root==NULL)
return ;
help(root->left,vec);
vec.push_back(root->val);
help(root->right,vec);
}
int kthLargest(TreeNode* root, int k) {
vectorvec;
help(root,vec);
return vec[vec.size()-k];
}
};

二叉树的最大深度
class Solution {
public:
int maxDepth(TreeNode* root) {
if(root==NULL){
return 0;
}

    //return maxDepth(root->left)>maxDepth(root->right)?(maxDepth(root->left)+1):(maxDepth(root->right)?+1);
    int left=maxDepth(root->left);
    int right=maxDepth(root->right);
    return left>right?left+1:right+1;
}

};

是否为平衡二叉树
class Solution {
public:
int depth(TreeNode* root)
{
if(rootNULL)
return 0;
return max(depth(root->right),depth(root->left))+1;
}
bool isBalanced(TreeNode* root) {
if(root
NULL)
return true;
int left=isBalanced(root->left);
int right=isBalanced(root->right);
return abs(left-right)<2&&isBalanced(root->left)&&isBalanced(root->right);
}
};

上面的方法比较啰嗦,还用了一个深度
/**

  • 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:
    bool help(TreeNode
    root,int*depth){
    if(root==NULL){
    *depth=0;
    return true;
    }
    int left,right;
    if(help(root->left,&left)&&help(root->right,&right)){
    int diff=left-right;
    if(diff>=-1&&diff<=1){
    depth=left>right?left+1:right+1;
    return true;
    }
    }
    return false;
    }
    bool isBalanced(TreeNode
    root) {
    int depth=0;
    return help(root,&depth);
    }

};

就是说,相对于根节点,我遍历根节点的时候,遍历它的左右子树,看是否平衡,如果都为true,那么计算diff,判断是否满足,然后计算出此时这个根节点的深度,深度就是left和right的最大值+1

746
bool isPalStr(string s)
{
int len = s.length();//如果是3个字符,那么3/2=1,只需比较一次,如果是4次,4/2是2次,如果长度是5,5/2=2,中间的字符不用校验w
for (int i = 0; i < len / 2; i++)
{
if (s[i] != s[len - i - 1])
return false;
}
return true;
}

#include

string longestPalStr(string s)
{
string ans = “”;
int max = 0;
int len = s.length();
for (int i = 0; i < len; i++)
{
for (int j = i + 1; j <= len; j++)//j=i+1是为了substr(i,j)中开环的j而是设计的
{
string test = s.substr(i, j-i);
if (test == “bbd”)
{
int a = 0;
}
if (isPalStr(test) && test.length()>max)
{
ans = s.substr(i, j - i);
max = max > ans.length() ? max : ans.length();
}
}
}//max记录此时的结果是否大于之前的长度,如果大于才更新ans结果
return ans;
}

说明:该方法可以,但是超时了.

string longestPalindrome3(string s) {
if (s.length() == 1) return s;//大小为1的字符串必为回文串
string rev = s;//rev存放s反转结果
string res;//存放结果
std::reverse(rev.begin(), rev.end());
if (rev == s) return s;
int len = 0;//存放回文子串的长度
for (int i = 0; i<s.length(); i++)//查找s与rev的最长公共子串
{
string temp;//存放待验证子串
for (int j = i; j<s.length(); j++)
{
temp = temp + s[j];
if (len >= temp.length())
continue;
else if (rev.find(temp) != -1)//在rev中找到temp
{
string q = temp;//q用来验证temp是否是回文子串
std::reverse(q.begin(), q.end());
if (q == temp)
{
len = temp.length();
res = temp;
}
}
else
{
break;
}
}
temp = “”;
}
return res;
}

这个比上面的多了break操作,就是我如果取的子字符串在逆置字符串中不存在的话,那么直接break外面的i循环,进行下一次i循环的操作. 而之前的2层for循环,就是比如abc435cba,当i=0的时候,abc4在逆置字符串abc534cba没有的时候,那么j++以后,比如abc43,以后所有的都不用循环了.直接进行i=1的操作,b bc 当bc4的时候,又发现没有,直接break循环,然后i=2,c c4,直接退出循环,i=4,43退出循环.以此类推.

了解2个算法:第一个2层for循环,遍历每一个子串,然后判断是否是回文字串,如果是的话,记录下最大长度和any,然后进行对比,下次的,更新最大长度.
第二个,也是循环取字串,然后用一个逆置的字符串,来对取出的字串进行拆分,如果逆置字符串中没有我这个字串的话,那么直接break,如果有的话,判断这个字串逆置后,是不是跟他是一样的,一样,就更新值.
然后用这个len可以进行另外一次筛选,如果此时字串的长度小于len的话,直接continue,进行j循环,取相同i下的下一个字串,也就是j变化.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值