剑指offer刷题总结(c++)

部分是官方题解或者是别人比较好的思路,有一些是自己写的。准备边刷题边整理,仅作为自己的学习笔记!

按题型分类

  • 1.数组
    • JZ1. 二维数组的查找
    • JZ6. 旋转数组的最小数字
    • JZ28. 数组出现次数超过一半的数字
    • JZ29. 最小的k个数
    • JZ32. 把数组排成最小的数
    • JZ35. 数组中的逆序对
    • JZ37. 数字在排序数组中出现的次数
    • JZ40. 数组中只出现一次的数字
    • JZ42. 和为S的两个数字
    • JZ50. 数组中重复的数字
    • JZ51. 构建乘积数组
  • 2. 字符串
    • JZ2. 替换空格
    • JZ27. 字符串的排列
    • JZ34. 第一个只出现一次的字符
    • JZ43. 左旋转字符串
    • JZ44. 反转单词顺序列
    • JZ45. 扑克牌顺子
    • JZ49. 把字符串转换为整数
    • JZ52. 正则表达式匹配
    • JZ53. 表示数值的字符串
    • JZ54.字符流中第一个不重复的字符
  • 3. 链表
    • JZ3. 从尾到头打印链表
    • JZ25. 复杂链表的复制
    • JZ36.两份链表的第一个公共结点
    • JZ55. 链表中环的入口节点
    • JZ56. 删除链表中重复的结点
  • 4. 二叉树
    • JZ4. 重建二叉树
    • JZ22. 从上到下打印二叉树
    • JZ23. 二叉树的后序遍历序列
    • JZ24. 二叉树中和为某一值的路径
    • JZ26. 二叉搜索树与双向链表
    • JZ38. 二叉树的深度
    • JZ39. 平衡二叉树
    • JZ57. 二叉树的下一个结点
    • JZ58. 对称的二叉树
    • JZ59. 按之字形顺序打印二叉树
    • JZ60. 把二叉树打印成多行
    • JZ61. 序列化二叉树
    • JZ62. 二叉搜索树的第k个结点
  • 5. 栈
    • JZ5. 两个栈实现队列
    • JZ21. 栈的压入弹出序列
  • 6. 动态规划
    • JZ7. 斐波那契数列
    • JZ7. 青蛙跳台阶
    • JZ30. 连续子数组的最大和
    • JZ67. 动态规划
  • 7. 查找
    • JZ31. 整数中1出现的次数
  • 其他
    • JZ33. 丑数
    • JZ41. 和为s的连续正数序列
    • JZ46. 孩子们的游戏
    • JZ47. 求1+2+...+n
    • JZ48. 不用加减乘除做加法
    • JZ63. 数据流中的中位数
    • JZ64. 滑动窗口的最大值
    • JZ65. 矩阵中的路径
    • JZ66. 机器人的运动范围

1.数组

JZ1. 二维数组的查找

题目描述
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

class Solution {
public:
    bool Find(int target, vector<vector<int> > array) {
        if(array.empty()||array[0].empty())
            return false;
        int row=array.size(),col=array[0].size();
        int x=0,y=col-1;
        while(x<row&&y>=0)
        {
            if(array[x][y]<target)
            {
                x+=1;
            }
            else if(array[x][y]>target)
            {
                y-=1;
            }
            else
            {
                return true;
            }
        }
        return false;
    }
};

tips:双重vector输入输出
vector初始化

JZ6. 旋转数组的最小数字

题目描述
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组[3,4,5,1,2]为[1,2,3,4,5]的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        int length=rotateArray.size();
        if(length<=0) return 0;
        if(length==1) return rotateArray[0];
        int pre=0,cur=1;
        int mark=1,mark2=1,min=0;
        for(int i=0;i<length-1;i++)
        {
            if(rotateArray[pre]==rotateArray[cur])
            {
                ;
            }
            else if(rotateArray[pre]>rotateArray[cur])
            {
                mark=0;
                mark2=0;
                min=rotateArray[cur];
                break;
            }
            else
            {
                mark=0;
            }    
            pre+=1;
            cur=pre+1;
        }
        if((mark==0) && (mark2==0))
            return min;
        else
            return rotateArray[0];
    }
};

JZ28. 数组出现次数超过一半的数字

题目描述
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。

class Solution {
public:
    int MoreThanHalfNum_Solution(vector<int> numbers) {
        //方法一:map
//         int length=numbers.size();
//         map mp;
//         for(int i=0;i
//             mp[numbers[i]]++;
//         for(int i=0;i
//         {
//             if(mp[numbers[i]]>length/2)
//                 return numbers[i];
//         }
//         return 0;
        //方法二
        int count=0,num=0;
        int length=numbers.size();
        for(int i=0;i<length;i++)
        {
            if(count==0)
            {
                count++;
                num=numbers[i];
            }
            else if(numbers[i]==num)
            {
                count++;
            }
            else
            {
                count--;
            }
        }
        count=0;
        for(int i=0;i<length;i++)
            if(numbers[i]==num) count++;
        if(count>length/2)
            return num;
        else
            return 0;
    }
};

JZ29. 最小的k个数

题目描述
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。

class Solution {
public:
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        vector<int> ret;
        if (k==0 || k>input.size()) return ret;
        sort(input.begin(), input.end());
        return vector<int>({input.begin(), input.begin()+k});        
    }
};

JZ32. 把数组排成最小的数

题目描述
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。

class Solution {
public:
    string PrintMinNumber(vector<int> numbers) {
         vector<string> str;
        for (int val : numbers) str.push_back(to_string(val));
        sort(str.begin(), str.end(), [](string a, string b) {
            return a + b < b + a;
        });
        string ret = "";
        for (string s : str) ret += s;
        return ret;       
    }
};

JZ35. 数组中的逆序对

题目描述
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
输入描述:
题目保证输入的数组中没有的相同的数字

数据范围:

对于%50的数据,size<=10^4

对于%75的数据,size<=10^5

对于%100的数据,size<=2*10^5

大佬思路

class Solution {
public:
    int cnt=0;
    void MergeSort(int array[], int start, int end){
        if(start>=end)return;
        int mid = (start+end)/2;
        MergeSort(array, start, mid);
        MergeSort(array, mid+1, end);
        MergeOne(array, start, mid, end);
    }
    void MergeOne(int array[], int start, int mid, int end){
        int* temp = new int[end-start+1];
        int k=0,i=start,j=mid+1;
        while(i<=mid && j<= end){
//如果前面的元素小于后面的不能构成逆序对
            if(array[i] <= array[j])
                temp[k++] = array[i++];
            else{
//如果前面的元素大于后面的,那么在前面元素之后的元素都能和后面的元素构成逆序对
                temp[k++] = array[j++];
                cnt = (cnt + (mid-i+1))%1000000007;
            }
        }
        while(i<= mid)
            temp[k++] = array[i++];
        while(j<=end)
            temp[k++] = array[j++];
        for(int l=0; l<k; l++){
            array[start+l] = temp[l];
        }
    }
    int InversePairs(vector<int> data) {
        int* p=new int[data.size()];
        for(int i=0;i<data.size();i++)
        {
            p[i]=data[i];
        }
        MergeSort(p, 0, data.size()-1);
        return cnt;
    }
};

JZ37. 数字在排序数组中出现的次数

题目描述
统计一个数字在升序数组中出现的次数

class Solution {
public:
    int GetNumberOfK(vector<int> data ,int k) {
        int length=data.size();
        int l=0,h=data.size();
        int mid;
        while(l<h)
        {
            mid=(l+h-1)/2;
            if(data[mid]<k)
                l=mid+1;
            else
                h=mid;
        }
        int lbound=l;
        l=0;
        h=data.size();
        while(l<h)
        {
            mid=(l+h-1)/2;
            if(data[mid]<=k)
                l=mid+1;
            else
                h=mid;
        }
        int hbound=l;
        return hbound-lbound;
    }
};

JZ40. 数组中只出现一次的数字

题目描述
一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

class Solution {
public:
    void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) {
        map<int,int> mp;
        vector<int> ans;
        for(int i=0;i<data.size();i++)
        {
            mp[data[i]]++;
        }
        for(int i:data)
        {
            if(mp[i]==1)
                ans.push_back(i);
        }
        *num1=ans[0];
        *num2=ans[1];
    }
};

JZ42. 和为S的两个数字

题目描述
输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
思路
暴力穷举的…不过内层循环找到第一个数字对就可以break。
优化的话可以双指针。

class Solution {
public:
    vector<int> FindNumbersWithSum(vector<int> array,int sum) {
        map<int,int> mp;
        int mark=0;
        for(int i=0;i<array.size();i++)
            for(int j=i+1;j<array.size();j++)
            {
                if(array[i]+array[j]==sum)
                {
                    mp[array[i]]=array[j];
                    mark=1;
                    break;
                }
            }
        int tmp=INT_MAX;
        pair<int,int> ans;
        for(pair<int,int> i:mp)
        {
            if(i.first*i.second<tmp)
            {
                ans=i;
                tmp=i.first*i.second;
            }
        }
        vector<int> ret;
        if(!mark)
            return ret;
        ret.push_back(ans.first);
        ret.push_back(ans.second);
        return ret;
    }
};

JZ50. 数组中重复的数字

题目描述
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中第一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
返回描述:
如果数组中有重复的数字,函数返回true,否则返回false。
如果数组中有重复的数字,把重复的数字放到参数duplication[0]中。(ps:duplication已经初始化,可以直接赋值使用。)

class Solution {
public:
    // Parameters:
    //        numbers:     an array of integers
    //        length:      the length of array numbers
    //        duplication: (Output) the duplicated number in the array number
    // Return value:       true if the input is valid, and there are some duplications in the array number
    //                     otherwise false
    bool duplicate(int numbers[], int length, int* duplication) {
        map<int,int> mp;
        if(length<=1)
            return false;
        for(int i=0;i<length;i++)
        {
            mp[numbers[i]]++;
        }
        for(int i=0;i<length;i++)
        {
            if(mp[numbers[i]]>=2)
            {
                duplication[0]=numbers[i];
                return true;
            }
        }
        return false;
    }
};

JZ51. 构建乘积数组

题目描述
给定一个数组A[0,1,…,n-1],请构建一个数组B[0,1,…,n-1],其中B中的元素B[i]=A[0]A[1]…*A[i-1]A[i+1]…*A[n-1]。不能使用除法。(注意:规定B[0] = A[1] * A[2] * … * A[n-1],B[n-1] = A[0] * A[1] * … * A[n-2];)
对于A长度为1的情况,B无意义,故而无法构建,因此该情况不会存在。

class Solution {
public:
    vector<int> multiply(const vector<int>& A) {
        vector<int> ans;
        if(A.size()<=1)
            return ans;
        int length=A.size();
        for(int i=0;i<length;i++)
        {
            int temp=1;
            for(int j=0;j<length;j++)
            {
                if(i!=j)
                {
                    temp*=A[j];
                }
            }
            ans.push_back(temp);
        }
        return ans;
    }
};

2. 字符串

JZ2. 替换空格

题目描述
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

class Solution {
public:
	void replaceSpace(char *str,int length) {
        if(str==nullptr||length<0)
            return;
        int num=0;
        for(int i=0;i<length;i++)
        {
            if(str[i] == ' ')
                num++;
        }
        int nl=length+num*2;
        for(int i=length;i>=0;i--)
        {
            if(str[i]==' ')
            {
                str[nl--]='0';
                str[nl--]='2';
                str[nl--]='%';
            }
            else{
                str[nl--]=str[i];
            }
        }
        return;
	}
};

JZ27. 字符串的排列

题目描述
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则按字典序打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba

class Solution {
public:
    void perm(int f,string s,set<string> &ans)
    {
        if(f+1==s.length())
        {
            ans.insert(s);
            return;
        }
        for(int i=f;i<s.length();i++)
        {
            swap(s[f],s[i]);
            perm(f+1,s,ans);
            swap(s[f],s[i]);
        }
    }
    vector<string> Permutation(string str) {
        set<string> ans;
        if(str.size()==0)
            return {};
        perm(0,str,ans);
        return vector<string>({ans.begin(),ans.end()});
    }
};

JZ34. 第一个只出现一次的字符

题目描述
在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).(从0开始计数)

class Solution {
public:
    int FirstNotRepeatingChar(string str) {
        map<char,int> mp;
        for(const char s:str)
        {
            mp[s]++;
        }
        for(int i=0;i<str.length();i++)
        {
            if(mp[str[i]]==1)
                return i;
        }
        return -1;
    }
};

JZ43. 左旋转字符串

题目描述
汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!

class Solution {
public:
    string LeftRotateString(string str, int n) {
        string s="";
        int length=str.size();
        if(n>length) return str;
        int k=n;
        for(k;k<length;k++)
        {
            s+=str[k];
        }
        for(int i=0;i<n;i++)
        {
            s+=str[i];
        }
        return s;
    }
};

JZ44. 反转单词顺序列

题目描述
牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?

class Solution {
public:
    string ReverseSentence(string str) {
        vector<string> ans;
        string s;
        if(str.length()<=0)
            return s;
        for(int i;i<str.length();i++)
        {
            if(str[i]!=' ')
            {
                s+=str[i];
            }
            else
            {
                ans.push_back(s);
                s="";
            }
        }
        ans.push_back(s);
        s="";
        for(int i=ans.size()-1;i>=0;i--)
        {
            s+=ans[i];
            s+= i==0?"":" ";
        }
        return s;
    }
};

JZ45. 扑克牌顺子

题目描述
LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张_)…他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子…LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何, 如果牌能组成顺子就输出true,否则就输出false。为了方便起见,你可以认为大小王是0。
思路
分两种情况考虑,
一. 如果vector中不包含0的情况:
那么如何判断呢?因为需要是顺子,所以首先不能有重复值, 如果没有重复值,那么形如[1 2 3 4 5]
[5 6 7 8 9], 会发现最大值与最小值的差值应该小于5.

二. 如果vector中包含0:
发现除去0后的值,判断方法和1中是一样的。

所以根据如上两个条件,算法过程如下:

初始化一个set,最大值max_ = 0, 最小值min_ = 14
遍历数组, 对于大于0的整数,没有在set中出现,则加入到set中,同时更新max_, min_
如果出现在了set中,直接返回false
数组遍历完,最后再判断一下最大值与最小值的差值是否小于5

class Solution {
public:
    bool IsContinuous( vector<int> numbers ) {
        if (numbers.empty()) return false;
        set<int> st;
        int max_ = 0, min_ = 14;
        for (int val : numbers) {
            if (val > 0) {
                if (st.count(val) > 0) return false;
                st.insert(val);
                max_ = max(max_, val);
                min_ = min(min_, val);
            }
        }
        return max_ - min_ < 5;
    }
};

JZ49. 把字符串转换为整数

题目描述
将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0
输入描述:
输入一个字符串,包括数字字母符号,可以为空
输出描述:
如果是合法的数值表达则返回该数字,否则返回0
示例1
输入
+2147483647
1a33
输出
2147483647
0
思路
参考思路

class Solution {
public:
    int StrToInt(string str) {
        const int length = str.length();
        int isNegtive = 1, overValue = 0;
        int digit = 0, value = 0;
 
        if (length == 0) return 0;
        else {
            int idx = 0;
            if (str[0] == '-') { isNegtive = -1; idx = 1;}
            else if (str[0] == '+') {idx = 1;}
 
            for (; idx<length; idx++) {
                digit = str[idx]-'0';
                // overValue表示本轮循环是否会越界
                overValue = isNegtive*value - INT_MAX/10
                          + (((isNegtive+1)/2 + digit > 8) ? 1:0);
 
                if (digit<0 || digit>9) return 0;
                else if (overValue > 0) return 0;
 
                value = value*10 + isNegtive*digit;
            }
            return value;
        }
    }
};

JZ52. 正则表达式匹配

题目描述
请实现一个函数用来匹配包括’.‘和’‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但是与"aa.a"和"ab*a"均不匹配
思路
参考思路

class Solution {
public:
    bool match(char* str, char* pattern)
    {
         if(*str=='\0' && *pattern=='\0')
            return true;
        if(*str!='\0' && *pattern=='\0')
            return false;
        if(*(pattern+1)!='*'){
            if(*str==*pattern || (*str!='\0' && *pattern=='.'))
                return match(str+1,pattern+1);
            else return false;
        }
        else{
            if(*str==*pattern || (*str!='\0' && *pattern=='.'))
                return match(str,pattern+2) || match(str+1,pattern);
            else return match(str,pattern+2);
        }
    }
};

JZ53. 表示数值的字符串

题目描述
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100",“5e2”,"-123",“3.1416"和”-1E-16"都表示数值。 但是"12e",“1a3.14”,“1.2.3”,"±5"和"12e+4.3"都不是。

class Solution {
public:
    bool isNumeric(char* string)
    {
        int i=0,eflag=0,dotflag=0;
        if(string[i] == '+' || string[i] == '-'){
            i++;
        }
        while(string[i] != '\0'){
            if(string[i] == 'e' || string[i] == 'E'){
                if(eflag == 1){
                    return 0;
                }
                eflag = 1;
                if(string[i+1] == '+' || string[i+1] == '-'){
                    i++;
                }
                if(string[i+1] == '\0'){
                    return 0;
                }
            }
            else if(string[i] == '.'){
                if(eflag == 1 || dotflag == 1){
                    return 0;
                }
                dotflag = 1;
            }
            else if(string[i] < '0' || string[i] > '9'){
                return 0;
            }
            i++;
        }
        return 1;
    }

};

JZ54.字符流中第一个不重复的字符

题目描述
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。
返回值描述:
如果当前字符流没有存在出现一次的字符,返回#字符。

c++ map,find()

class Solution
{
public:
  //Insert one char from stringstream
    map<char,int> mp;
    queue<char> p;
    void Insert(char ch)
    {
        map<char,int>::iterator tmp=mp.find(ch);
        if(tmp==mp.end())
        {
            p.push(ch);
        }
        ++mp[ch];
        
    }
  //return the first appearence once char in current stringstream
    char FirstAppearingOnce()
    {
        while(!p.empty())
        {
            char ch;
            ch=p.front();
            if(mp[ch]==1)
                return ch;
            else
                p.pop();
        }
        return '#';
    }

};

3. 链表

JZ3. 从尾到头打印链表

题目描述
输入一个链表,按链表从尾到头的顺序返回一个ArrayList

/**
*  struct ListNode {
*        int val;
*        struct ListNode *next;
*        ListNode(int x) :
*              val(x), next(NULL) {
*        }
*  };
*/
class Solution {
public:
    vector<int> printListFromTailToHead(ListNode* head) {
        ListNode *pre=nullptr;
        ListNode *cur=head;
        ListNode *temp=cur;
        while(cur)
        {
            temp=cur->next;
            cur->next=pre;
            pre=cur;
            cur=temp;
        }
        vector<int> ans;
        while(pre)
        {
            ans.push_back(pre->val);
            pre=pre->next;
        }
        return ans;
    }
};

JZ25. 复杂链表的复制

题目描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针random指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头结点。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

/*
struct RandomListNode {
    int label;
    struct RandomListNode *next, *random;
    RandomListNode(int x) :
            label(x), next(NULL), random(NULL) {
    }
};
*/
class Solution {
public:
    RandomListNode* Clone(RandomListNode* pHead)
    {
        if (pHead==nullptr) return nullptr;
        RandomListNode* i=pHead, *newNode=nullptr;
        while(i!=nullptr)
        {
            newNode = new RandomListNode(i->label);
            newNode->next= i->next;
            i->next = newNode;
            i=i->next->next;
        }
        i=pHead;
        while(i!=nullptr)
        {
            newNode=i->next;
            if (i->random == nullptr) newNode->random=nullptr;
            else
                newNode->random=i->random->next;
            i=newNode->next;
        }
        i=pHead;
        newNode=pHead->next;
        RandomListNode *res = pHead->next;
        while(i!=nullptr)
        {
           newNode=i->next;
           i->next=newNode->next;
           if (newNode->next!=nullptr)
               newNode->next=newNode->next->next;
           i=i->next;
        }
        return res;        
    }
};

JZ36.两份链表的第一个公共结点

题目描述
输入两个链表,找出它们的第一个公共结点。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)
思路
两链表尾部拼接上对方,使得而这一样长,直接找公共节点

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
        ListNode *ta = pHead1, *tb = pHead2;
        while (ta != tb) {
            ta = ta ? ta->next : pHead2;
            tb = tb ? tb->next : pHead1;
        }
        return ta;        
    }
};

JZ55. 链表中环的入口节点

题目描述
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
思路
哈希或者快慢指针

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};
*/
class Solution {
public:
    ListNode* EntryNodeOfLoop(ListNode* pHead)
    {
//        //方法一:哈希
//         set st;
//         while (pHead) {
//             if (st.find(pHead) == st.end()) {
//                 st.insert(pHead);
//                 pHead = pHead->next;
//             }
//             else {
//                 return pHead;
//             }
//         }
//         return nullptr;
        //方法二:快慢指针
        ListNode *fast = pHead;
        ListNode *slow = pHead;
        while (fast && fast->next) {
            fast = fast->next->next;
            slow = slow->next;
            if (fast == slow) break;
        }
        if (!fast || !fast->next) return nullptr;
        fast = pHead;
        while (fast != slow) {
            fast = fast->next;
            slow = slow->next;
        }
        return fast;
    }
};

JZ56. 删除链表中重复的结点

题目描述
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};
*/
class Solution {
public:
    ListNode* deleteDuplication(ListNode* pHead)
    {
        ListNode *vhead = new ListNode(-1);
        vhead->next = pHead;
        ListNode *pre = vhead, *cur = pHead;       
        while (cur) {
            if (cur->next && cur->val == cur->next->val) {
                cur = cur->next;
                while (cur->next && cur->val == cur->next->val) {
                    cur = cur->next;
                }
                cur = cur->next;
                pre->next = cur;
            }
            else {
                pre = cur;
                cur = cur->next;
            }
        }
        return vhead->next;
    }
};

4. 二叉树

JZ4. 重建二叉树

题目描述
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
思路(某牛友)
前序加中序序列,分解过程图示如下(王道数据结构P120)
剑指offer刷题总结(c++)_第1张图片

  1. 由先序序列第一个pre[0]在中序序列中找到根节点位置gen
  2. 以gen为中心遍历
    0~gen左子树
    子中序序列:0~gen-1,放入vin_left[]
    子先序序列:1~gen放入pre_left[],+1可以看图,因为头部有根节点
    gen+1~vinlen为右子树
    子中序序列:gen+1 ~ vinlen-1放入vin_right[]
    子先序序列:gen+1 ~ vinlen-1放入pre_right[]
  3. 由先序序列pre[0]创建根节点
  4. 连接左子树,按照左子树子序列递归(pre_left[]和vin_left[])
  5. 连接右子树,按照右子树子序列递归(pre_right[]和vin_right[])
  6. 返回根节点
/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
        int vinlen=vin.size();
        if(vinlen<=0)
            return nullptr;
        vector<int> vinleft,vinright,preleft,preright;
        TreeNode *head=new TreeNode(pre[0]);
        int gen=0;
        for(int i=0;i<vinlen;i++)
        {
            if(vin[i]==pre[0])
            {
                gen=i;
                break;
            }
        }
        for(int i=0;i<gen;i++)
        {
            vinleft.push_back(vin[i]);
            preleft.push_back(pre[i+1]);
        }
        for(int i=gen+1;i<vinlen;i++)
        {
            vinright.push_back(vin[i]);
            preright.push_back(pre[i]);
        }
        head->left=reConstructBinaryTree(preleft, vinleft);
        head->right=reConstructBinaryTree(preright, vinright);
        return head;
    }
};

JZ22. 从上到下打印二叉树

题目描述
从上往下打印出二叉树的每个节点,同层节点从左至右打印。

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    vector<int> PrintFromTopToBottom(TreeNode* root) {
        vector<int> ans;
        if(!root) return ans;
        queue<TreeNode*> qt;
        qt.push(root);
        while(!qt.empty())
        {
            TreeNode *temp=qt.front();
            qt.pop();
            ans.push_back(temp->val);
            if(temp->left) qt.push(temp->left);
            if(temp->right) qt.push(temp->right);
        }
        return ans;
    }
};

JZ23. 二叉树的后序遍历序列

题目描述
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则返回true,否则返回false。假设输入的数组的任意两个数字都互不相同。

class Solution {
public:
    bool IsBST(const vector<int>& sequence, const int start, const int end){
    if (start>=end) return true;  // 可能出现start>end的情况
 
    int pivot;
    for (pivot=start; sequence[pivot]<sequence[end]; ++pivot);  // 查找分界点
    for (int i=pivot; i<=end; ++i)
        if (sequence[i]<sequence[end]) return false;
    return IsBST(sequence, start, pivot-1) && IsBST(sequence, pivot, end-1);
}    
    bool VerifySquenceOfBST(vector<int> sequence) {
    if (!sequence.size()) return false;
    return IsBST(sequence, 0, sequence.size()-1);
    }
};

JZ24. 二叉树中和为某一值的路径

题目描述
输入一颗二叉树的根节点和一个整数,按字典序打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    void dfs(TreeNode* root,int num,vector<vector<int>> &ans,vector<int> &path)
    {
        path.push_back(root->val);
        if(root->val==num&&!root->left&&!root->right)
            ans.push_back(path);
        if(root->left)
            dfs(root->left,num-root->val,ans,path);
        if(root->right)
            dfs(root->right,num-root->val,ans,path);
        path.pop_back();
    }
    vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
        vector<vector<int>> ans;
        vector<int> path;
        if(root==nullptr)
            return ans;
        dfs(root,expectNumber,ans,path);
        return ans;
    }
};

JZ26. 二叉搜索树与双向链表

题目描述
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
思路
中序遍历二叉树,结果存在list中,修改结点指针。

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    void adjust(TreeNode* root,vector<TreeNode*> &list)
    {
        if(root->left!=nullptr)
            adjust(root->left, list);
        list.push_back(root);
        if(root->right!=nullptr)
            adjust(root->right, list);
    }
    TreeNode* get(vector<TreeNode*> &list)
    {
        for(int i=0;i<list.size()-1;i++)
        {
            list[i]->right=list[i+1];
            list[i+1]->left=list[i];
        }
        return list[0];
    }
    TreeNode* Convert(TreeNode* pRootOfTree)
    {
        if(pRootOfTree==nullptr)
            return nullptr;
        vector<TreeNode*> list;
        adjust(pRootOfTree,list);
        return get(list);
    }
};

JZ38. 二叉树的深度

题目描述
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    int TreeDepth(TreeNode* pRoot)
    {
        if(pRoot==nullptr) return 0;
        int l=TreeDepth(pRoot->left);
        int r=TreeDepth(pRoot->right);
        int m=1+((l>r)?l:r);
        return m;   
    }
};

JZ39. 平衡二叉树

题目描述
输入一棵二叉树,判断该二叉树是否是平衡二叉树。

在这里,我们只需要考虑其平衡性,不需要考虑其是不是排序二叉树

class Solution {
public:
    int depth(TreeNode *root) {
        if (!root) return 0;
        int ldep = depth(root->left);
        if (ldep == -1) return -1;
        int rdep = depth(root->right);
        if (rdep == -1) return -1;
        int sub = abs(ldep - rdep);
        if (sub > 1) return -1;
        return max(ldep, rdep) + 1;
    }
    bool IsBalanced_Solution(TreeNode* root) {
        return depth(root) != -1;
    }
};

JZ57. 二叉树的下一个结点

题目描述
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
思路
暴力求解法or根据中序遍历特性求解

/*
struct TreeLinkNode {
    int val;
    struct TreeLinkNode *left;
    struct TreeLinkNode *right;
    struct TreeLinkNode *next;
    TreeLinkNode(int x) :val(x), left(NULL), right(NULL), next(NULL) {
        
    }
};
*/
class Solution {
public:
    TreeLinkNode* GetNext(TreeLinkNode* pNode)
    {
        if(!pNode) return pNode;
        
        if(pNode->right)
        {
            pNode = pNode->right;
            while(pNode->left != nullptr)
                pNode = pNode->left;
            return pNode;
        }
 
        while(pNode->next)
        {
            TreeLinkNode* tmp=pNode->next;
            if(tmp->left == pNode)
                return tmp;
            pNode = pNode->next;
        }
        return nullptr;
    }
};

JZ58. 对称的二叉树

题目描述
请实现一个函数,用来判断一棵二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/
class Solution {
public:
    bool judge(TreeNode* l,TreeNode* r)
    {
        if(!l&&!r)
            return true;
        if(!l||!r)
            return false;
        return l->val==r->val && judge(l->left,r->right)&&judge(l->right, r->left);
    }
    bool isSymmetrical(TreeNode* pRoot)
    {
        if(!pRoot)
            return true;
        return judge(pRoot->left,pRoot->right);
    }

};

JZ59. 按之字形顺序打印二叉树

题目描述
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/
class Solution {
public:
    vector<vector<int> > Print(TreeNode* pRoot) {
        vector<vector<int>> ret;
        if (!pRoot) return ret;
        queue<TreeNode*> q;
        q.push(pRoot);
        int level = 0;
 
        while (!q.empty()) {
            int sz = q.size();
            vector<int> ans;
            while (sz--) {
                TreeNode *node = q.front();
                q.pop();
                ans.push_back(node->val);
 
                if (node->left) q.push(node->left);
                if (node->right) q.push(node->right);
            }
            ++level;
            if (!(level&1)) // 偶数层 反转一下
                reverse(ans.begin(), ans.end());
            ret.push_back(ans);
        }
        return ret;
    }
    
};

JZ60. 把二叉树打印成多行

题目描述
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/
class Solution {
public:
        vector<vector<int> > Print(TreeNode* pRoot) {
            vector<vector<int>> ans;
            if(!pRoot)
                return ans;
            queue<TreeNode*> qt;
            qt.push(pRoot);
            while(!qt.empty())
            {
                int l=qt.size();
                vector<int> tmp;
                while(l--)
                {
                    TreeNode* p=qt.front();
                    qt.pop();
                    tmp.push_back(p->val);
                    if(p->left)
                        qt.push(p->left);
                    if(p->right)
                        qt.push(p->right);
                }
                ans.push_back(tmp);
            }
            return ans;
        }
    
};

JZ61. 序列化二叉树

题目描述
请实现两个函数,分别用来序列化和反序列化二叉树

二叉树的序列化是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存。序列化可以基于先序、中序、后序、层序的二叉树遍历方式来进行修改,序列化的结果是一个字符串,序列化时通过 某种符号表示空节点(#),以 ! 表示一个结点值的结束(value!)。

二叉树的反序列化是指:根据某种遍历顺序得到的序列化字符串结果str,重构二叉树。

例如,我们可以把一个只有根节点为1的二叉树序列化为"1,",然后通过自己的函数来解析回这个二叉树

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/
class Solution {
public:
    char* Serialize(TreeNode *root) {    
        if (!root) {
            return "#";
        }

        string res = to_string(root->val);
        res.push_back(',');

        char* left = Serialize(root->left);
        char* right = Serialize(root->right);

        char* ret = new char[strlen(left)+strlen(right)+res.size()];
        // 如果是string类型,直接用operator += ,这里char* 需要用函数
        strcpy(ret,res.c_str());
        strcat(ret,left);
        strcat(ret,right);

        return ret;
    }
    // 参数使用引用&, 以实现全局变量的目的
    TreeNode* deseri(char *&s) {
        if (*s == '#') {
            ++s;
            return nullptr;
        }

        // 构造根节点值
        int num = 0;
        while (*s != ',') {
            num = num * 10 + (*s - '0');
            ++s;
        }
        ++s;
        // 递归构造树
        TreeNode *root = new TreeNode(num);
        root->left = deseri(s);
        root->right = deseri(s);

        return root;
    }

    TreeNode* Deserialize(char *str) {
        return deseri(str);
    }
};

JZ62. 二叉搜索树的第k个结点

题目描述
给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/
class Solution {
public:
    void range(TreeNode* root,vector<TreeNode*> &t)
    {
        if(!root)
            return;
        range(root->left,t);
        t.push_back(root);
        range(root->right,t);
    }
    TreeNode* KthNode(TreeNode* pRoot, int k)
    {
        if(!pRoot)
            return nullptr;
        vector<TreeNode*> t;
        range(pRoot,t);
        if(k>=1&&t.size()>=k)//第一次没写这个判断条件,报错递归溢出,还是要注意程序健壮性
            return t[k-1];
        return nullptr;
    }

    
};

5. 栈

JZ5. 两个栈实现队列

题目描述
用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型

class Solution
{
public:
    void push(int node) {
        stack1.push(node);
    }

    int pop() {
        if(stack2.empty())
        {
            while(!stack1.empty())
            {
                stack2.push(stack1.top());
                stack1.pop();
            }
        }
        int ret=stack2.top();
        stack2.pop();
        return ret;
    }

private:
    stack<int> stack1;
    stack<int> stack2;
};

JZ21. 栈的压入弹出序列

题目描述
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

class Solution {
public:
    bool IsPopOrder(vector<int> pushV,vector<int> popV) {     
        int length=pushV.size();
        if(length<=0)
            return false;
        stack<int> st;
        int i=0,j=0;
        while(i<length)
        {
            if(pushV[i]==popV[j])
            {
                i++;j++;
                while(!st.empty()&&st.top()==popV[j])//memo一下,这两个判断条件顺序不能反,不然栈空时会报错
                {
                    st.pop();
                    j++;
                }
            }
            else
            {
                st.push(pushV[i++]);
            }
        }
        return st.empty();
//         int length=pushV.size();
//         if(length<=0)
//             return false;
//         stack st;
//         int p=0,q=0;
//         while(q<5)
//         {
//             if(!st.empty()&&st.top()==popV[q])
//             {
//                 while(st.top()==popV[q]&&!st.empty())
//                 {
//                     st.pop();
//                     q++;
//                     if(q==5)
//                         return true;
//                 }
//                 if(q<5&&p>=5)
//                     return false;
//             }
//             else if(!st.empty()&&st.top()!=popV[q])
//             {
//                 while(st.top()!=popV[q]&&p<5)
//                 {
//                     st.push(pushV[p]);
//                     p++;
//                 }
//                 if(p>=5&&st.top()!=popV[q])
//                     return false;
//             }
//             else
//             {
//                 if(p>5)
//                     return false;
//                 do
//                 {
//                     st.push(pushV[p++]);
//                 }while(st.top()!=popV[q]&&p<5);
//                 if(p>5&&st.top()!=popV[q])
//                     return false;
//             }
//         }
//         return false;
    }
};

6. 动态规划

JZ7. 斐波那契数列

题目描述
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0,第1项是1)。
n<=39

class Solution {
public:
    int Fibonacci(int n) {
        if(n<0) return -1;
        if(n==0) return 0;
        if(n==1) return 1;
        vector<int> f(n+1,0);//记得初始化,不然会提示数组越界
        f[0]=0;
        f[1]=1;
        for(int i=2;i<=n;i++)
            f[i]=f[i-1]+f[i-2];
        return f[n];
    }
};

JZ7. 青蛙跳台阶

题目描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

class Solution {
public:
    int jumpFloor(int number) {
//         if(number==0)
//             return 0;
//         if(number==1)
//             return 1;
//         if(number==2)
//             return 2;
//         return jumpFloor(number-1)+jumpFloor(number-2);
        if(number==0||number==1)
            return number;
        int a=1,b=1,c;
        for(int i=2;i<=number;i++)
        {
            c=a+b;
            a=b;
            b=c;
        }
        return c;
    }
};

JZ30. 连续子数组的最大和

题目描述
HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)

class Solution {
public:
    int FindGreatestSumOfSubArray(vector<int> array) {
        if(array.size()<=0) return 0;
        int max=array[0],b=0;
        for(int i=0;i<array.size();i++)
        {
            if(array[i]+b<0) 
            {
                b=0;
                if(array[i]>max)
                    max=array[i];
            }
            else
            {
                b=b+array[i];
                if(b>max)
                    max=b;
            }
        }
        return max;
    }
};

JZ67. 动态规划

题目描述
给你一根长度为n的绳子,请把绳子剪成整数长的m段(m、n都是整数,n>1并且m>1,m<=n),每段绳子的长度记为k[1],…,k[m]。请问k[1]x…xk[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
思路
动态规划

其他方法

class Solution {
public:
    int cutRope(int number) {
        if (number == 2) {
            return 1;
        }
        else if (number == 3) {
            return 2;
        }
 
        vector<int> f(number + 1, -1);
        for (int i = 1; i <= 4; ++i) {
            f[i] = i;
        }
        for (int i = 5; i <= number; ++i) {
            for (int j = 1; j < i; ++j) {
                f[i] = max(f[i], j * f[i - j]);
            }
        }
        return f[number];
    }
};

7. 查找

JZ31. 整数中1出现的次数

题目描述
求出113的整数中1出现的次数,并算出1001300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。
思路

一个较清晰的思路

class Solution {
public:
    int NumberOf1Between1AndN_Solution(int n)
    {
        int digit=1,ans=0;
        int high=n/10,cur=n%10,low=0;
        while(high||cur)
        {
            if(cur==0)
            {
                ans+=high*digit;
            }
            else if(cur==1)
            {
                ans+=high*digit+low+1;
            }
            else
            {
                ans+=(high+1)*digit;
            }
            low=cur*digit+low;
            digit*=10;
            cur=high%10;
            high/=10;
        }
        return ans;
    }
};
//         digit, res = 1, 0
//         high, cur, low = n // 10, n % 10, 0
//         while high != 0 or cur != 0:
//             if cur == 0: res += high * digit
//             elif cur == 1: res += high * digit + low + 1
//             else: res += (high + 1) * digit
//             low += cur * digit
//             cur = high % 10
//             high //= 10
//             digit *= 10
//         return res

其他

JZ33. 丑数

题目描述
把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
思路

参考一个大神思路

class Solution {
public:
    int GetUglyNumber_Solution(int index) {
        if(index <= 0)return 0;
        int p2=0,p3=0,p5=0;//初始化三个指向三个潜在成为最小丑数的位置
        int* result = new int[index];
        result[0] = 1;//
        for(int i=1; i < index; i++){
            result[i] = min(result[p2]*2, min(result[p3]*3, result[p5]*5));
            if(result[i] == result[p2]*2)p2++;//为了防止重复需要三个if都能够走到
            if(result[i] == result[p3]*3)p3++;//为了防止重复需要三个if都能够走到
            if(result[i] == result[p5]*5)p5++;//为了防止重复需要三个if都能够走到 
        }
        return result[index-1];    
    }
};

JZ41. 和为s的连续正数序列

题目描述
小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!

class Solution {
public:
    vector<vector<int> > FindContinuousSequence(int sum) {
        vector<vector<int>> ans;
        for(int j=1;j<=sum/2;j++)
            for(int i=j+1;i<sum;i++)
            {
                if((i*(i+1)/2-j*(j-1)/2)==sum)
                {
                    vector<int> temp;
                    int k=j;
                    while(k<=i)
                    {
                        temp.push_back(k++);
                    }
                    ans.push_back(temp);
                } 
            }
        return ans;
    }
};

JZ46. 孩子们的游戏

题目描述
每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数…这样下去…直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!_)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)
思路
长度为n的序列的最终胜利者f[n]:
f[1] = 0
f[2] = (f{1] + m) % 2
f[3] = (f[2] + m) % 3

f[n] = (f[n-1] + m) % n

class Solution {
public:
    int LastRemaining_Solution(int n, int m)
    {
        if (n <= 0) return -1;
        int index = 0;
        for (int i=2; i<=n; ++i) {
            index = (index + m) % i;
        }
        return index;
//         if(n<=0||m<1)
//             return -1;
//         int k=n,num=0,count=0;
//         map mp;
//         for(int i=0;i
//             mp[i]=0;
//         while(k>1)
//         {
//             while(count<=m)
//             {
//                 if(mp[num]==0)
//                 {
//                     count++;
//                     num=(num+1)%k;
//                 }
//             }
//             mp[num-1]=1;
//             k--;
//             count=0;
//         }
//         return k;
    }
};

JZ47. 求1+2+…+n

题目描述
求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
思路
f (n==1) return 1;

变形递归:
如果n==1,需要终止递归,所以我们想到了逻辑与&&连接符。
A&&B,表示如果A成立则执行B,否则如果A不成立,不用执行B
因此我们可以这样。在n>1的时候,执行递归函数

class Solution {
public:
    int Sum_Solution(int n) {
        bool x=n>1 && (n+=Sum_Solution(n-1));
        return n;
    }
};

JZ48. 不用加减乘除做加法

题目描述
写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
思路
利用位运算

class Solution {
public:
    int Add(int num1, int num2)
    {
        while (num2 != 0) {
            // 负数左移会在低位补1,所以转化为无符号整数
            int c = ((unsigned int)(num1 & num2)) << 1;
            num1 ^= num2;
            num2 = c;
        }
        return num1;
    }
};

JZ63. 数据流中的中位数

题目描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。

class Solution {
    vector<double> data;
public:
    void Insert(int num)
    {
        data.push_back(num);
        sort(data.begin(),data.end());
    }
    double GetMedian()
    {
        int length = data.size();
        if(length%2==0){
            return (data[length/2]+data[length/2-1])/2;
        }
        else{
            return data[(length/2)];
        }
    }
};

JZ64. 滑动窗口的最大值

题目描述
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
窗口大于数组长度的时候,返回空。

class Solution {
public:
    vector<int> maxInWindows(const vector<int>& num, unsigned int size)
    {
        vector<int> ans;
        if(size>num.size()||!num.size()||size<1)
            return ans;
        for(int i=0;i<num.size()-size+1;i++)
        {
            int temp=num[i];
            for(int j=i;j<i+size;j++)
            {
                if(num[j]>temp)
                {
                    temp=num[j];
                }
            }
            ans.push_back(temp);
        }
        return ans;
    }
};

JZ65. 矩阵中的路径

题目描述
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。

class Solution {
public:
    char *mat = 0;
    int h = 0, w = 0;
    int str_len = 0;
    int dir[5] = {-1, 0, 1, 0, -1};
 
    bool dfs(int i, int j, int pos, char *str) {
        // 因为dfs调用前,没有进行边界检查,
        // 所以需要第一步进行边界检查,
        // 因为后面需要访问mat中元素,不能越界访问
        if (i < 0 || i >= h || j < 0 || j >= w) {
            return false;
        }
 
        char ch = mat[i * w + j];
        // 判断是否访问过
        // 如果没有访问过,判断是否和字符串str[pos]匹配
        if (ch == '#' || ch != str[pos]) {
            return false;
        }
 
         // 如果匹配,判断是否匹配到最后一个字符
        if (pos + 1  == str_len) {
            return true;
        }
 
        // 说明当前字符成功匹配,标记一下,下次不能再次进入
        mat[i * w + j] = '#';
        for (int k = 0; k < 4; ++k) {
            if (dfs(i + dir[k], j + dir[k + 1], pos + 1, str)) {
                return true;
            }
        }
        // 如果4个方向都无法匹配 str[pos + 1]
        // 则回溯, 将'#' 还原成 ch         
        mat[i * w + j] = ch;
        // 说明此次匹配是不成功的
        return false;  
    }
    bool hasPath(char* matrix, int rows, int cols, char* str)
    {
        mat = matrix;
        h = rows, w = cols;
         str_len = strlen(str);
 
        for (int i = 0; i < rows; ++i) {
            for (int j = 0; j < cols; ++j) {
                if (dfs(i, j, 0, str)) {
                    return true;
                }
            }
        }
        return false;
    }
};

JZ66. 机器人的运动范围

题目描述
地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?

class Solution {
public:
    int dir[5] = {-1, 0, 1, 0, -1};
 
    int check(int n) {
        int sum = 0;
 
        while (n) {
            sum += (n % 10);
            n /= 10;
        }
 
        return sum;
    }
 
    void dfs(int x, int y, int sho, int r, int c, int &ret, vector<vector<int>> &mark) {
        // 检查下标 和 是否访问
        if (x < 0 || x >= r || y < 0 || y >= c || mark[x][y] == 1) {
            return;
        }
        // 检查当前坐标是否满足条件
        if (check(x) + check(y) > sho) {
            return;
        }
        // 代码走到这里,说明当前坐标符合条件
        mark[x][y] = 1;
        ret += 1;
 
        for (int i = 0; i < 4; ++i) {
            dfs(x + dir[i], y + dir[i + 1], sho, r, c, ret, mark);
        }
 
 
 
    }
    int movingCount(int sho, int rows, int cols)
    {
        if (sho <= 0) {
            return 0;
        }
 
        vector<vector<int>> mark(rows, vector<int> (cols, -1));
        int ret = 0;
        dfs(0, 0, sho, rows, cols, ret, mark);
        return ret;
    }
};

你可能感兴趣的:(c/c++,c++)