【C++】leetcode力扣 剑指 Offer 题解

news2024/11/24 22:24:08

文章预览:

    • 剑指 Offer 03. 数组中重复的数字
    • 剑指 Offer 04. 二维数组中的查找
    • 剑指 Offer 05. 替换空格
    • 剑指 Offer 06. 从尾到头打印链表
    • 剑指 Offer 07. 重建二叉树
    • 剑指 Offer 09. 用两个栈实现队列
    • 剑指 Offer 10- I. 斐波那契数列
    • 剑指 Offer 10- II. 青蛙跳台阶问题
    • 剑指 Offer 11. 旋转数组的最小数字
    • 剑指 Offer 12. 矩阵中的路径
    • 剑指 Offer 13. 机器人的运动范围
    • 剑指 Offer 14- I. 剪绳子
    • 剑指 Offer 15. 二进制中1的个数
    • 剑指 Offer 16. 数值的整数次方
    • 剑指 Offer 17. 打印从1到最大的n位数
    • 剑指 Offer 18. 删除链表的节点
    • 剑指 Offer 20. 表示数值的字符串
    • 剑指 Offer 21. 调整数组顺序使奇数位于偶数前面
    • 剑指 Offer 22. 链表中倒数第k个节点
    • 剑指 Offer 24. 反转链表
    • 剑指 Offer 25. 合并两个排序的链表
    • 剑指 Offer 26. 树的子结构
    • 剑指 Offer 27. 二叉树的镜像
    • 剑指 Offer 28. 对称的二叉树
    • 剑指 Offer 30. 包含min函数的栈
    • 剑指 Offer 31. 栈的压入、弹出序列
    • 剑指 Offer 32 - I. 从上到下打印二叉树
    • 剑指 Offer 32 - II. 从上到下打印二叉树 II
    • 剑指 Offer 32 - III. 从上到下打印二叉树 III
    • 剑指 Offer 33. 二叉搜索树的后序遍历序列
    • 剑指 Offer 34. 二叉树中和为某一值的路径
    • 剑指 Offer 35. 复杂链表的复制
    • 剑指 Offer 36. 二叉搜索树与双向链表
    • 剑指 Offer 38. 字符串的排列
    • 剑指 Offer 40. 最小的k个数
    • 剑指 Offer 42. 连续子数组的最大和
    • 剑指 Offer 45. 把数组排成最小的数
    • 剑指 Offer 46. 把数字翻译成字符串
    • 剑指 Offer 47. 礼物的最大价值
    • 剑指 Offer 48. 最长不含重复字符的子字符串
    • 剑指 Offer 49. 丑数
    • 剑指 Offer 50. 第一个只出现一次的字符
    • 剑指 Offer 52. 两个链表的第一个公共节点
    • 剑指 Offer 53 - I. 在排序数组中查找数字 I
    • 剑指 Offer 53 - II. 0~n-1中缺失的数字
    • 剑指 Offer 54. 二叉搜索树的第k大节点
    • 剑指 Offer 55 - I. 二叉树的深度
    • 剑指 Offer 55 - II. 平衡二叉树
    • 剑指 Offer 57. 和为s的两个数字
    • 剑指 Offer 58 - I. 翻转单词顺序
    • 剑指 Offer 59 - II. 队列的最大值
    • 剑指 Offer 60. n个骰子的点数
    • 剑指 Offer 61. 扑克牌中的顺子
    • 剑指 Offer 63. 股票的最大利润
    • 剑指 Offer 64. 求1+2+…+n
    • 剑指 Offer 67. 把字符串转换成整数
    • 剑指 Offer 68 - I. 二叉搜索树的最近公共祖先
    • 剑指 Offer 68 - II. 二叉树的最近公共祖先

力扣链接

剑指 Offer 03. 数组中重复的数字

法一:暴力解法
使用两层循环,超时

class Solution {
public:
    int findRepeatNumber(vector<int>& nums) {
        
        for(int i=0;i<nums.size();i++){
            for(int j=i+1;j<nums.size();j++){
                if(nums[j]==nums[i]){
                    return nums[i];
                }
            }
        }
    return 0;
    }
};

法二:使用unordered_map计数

class Solution {
public:
    int findRepeatNumber(vector<int>& nums) {
        unordered_map<int,int> m;

        for(int i=0;i<nums.size();i++){
            if(m.find(nums[i])!=m.end()){//如果存在说明出现重复的
                return nums[i];
            }
            m[nums[i]]++;//记录每个数字出现的次数
        }
        
    return -1;
    }
};

剑指 Offer 04. 二维数组中的查找

class Solution {
public:
    bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
        
        int max_x=matrix.size();//二维数组行数
        if(max_x==0) return false;//注意特殊情况的判断
        int max_y=matrix[0].size();//二维数组列数
        for(int i=0;i<max_x;i++){
            for(int j=0;j<max_y;j++){
                if(matrix[i][j]>target){//说明第j列之后的列(包括第j列)都大于target
                    max_y=j;//更新能到达的最大列
                }
                else if(matrix[i][j]==target){
                    return true;
                }
                else continue;//小于,继续遍历
            }
        }
        return false;
    }
};

剑指 Offer 05. 替换空格

法一:创建新字符串result存放

class Solution {
public:
    string replaceSpace(string s) {
        string result="";
        for(char c:s){
            if(c==' '){
                result+="%20";
            }
            else{
                result+=c;
            }
        }
        return result;
    }
};

法二:使用双指针原地操作

class Solution {
public:
    string replaceSpace(string s) {
        //双指针
        int cout=0;
        for(char c:s){
            if(c==' ') cout++;
        }
        int i=s.size()-1;
        s.resize(s.size()+2*cout);
        int j=s.size()-1;
        for(i,j;i>=0;i--){
            if(s[i]==' '){
                s[j]='0';
                s[j-1]='2';
                s[j-2]='%';
                j=j-3;
            }
            else{
                s[j--]=s[i];
            }
        }
        return s;
    }
};

剑指 Offer 06. 从尾到头打印链表

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    vector<int> reversePrint(ListNode* head) {
        vector<int> result;
        while(head){
            result.push_back(head->val);
            head=head->next;
        }
        for(int i=0,j=result.size()-1;i<j;i++,j--){//使用双指针反转数组
            swap(result[i],result[j]);
        }
        return result;
    }
};

剑指 Offer 07. 重建二叉树

题目给了双向链表节点的结构体
采用递归思想来写代码,每次递归传入的前序遍历和中序遍历的数组都是去掉的head节点之后的左子树的前序遍历和中序遍历数组、右子树的前序遍历和中序遍历数组

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        if(preorder.size()==0) return NULL;

        TreeNode* head=new TreeNode(preorder[0]);//前序遍历的第一个值就是根节点val值

        vector<int> left_inorder;//head节点的左子树的中序遍历结果
        vector<int> right_inorder;//head节点的右子树的中序遍历结果
        int i=0;
        for(;i<inorder.size();i++){
            if(inorder[i]==preorder[0]) break;
            left_inorder.push_back(inorder[i]);
        }
        i++;//记得加一跳过根节点,因为break不会执行i++
        for(;i<inorder.size();i++){
            right_inorder.push_back(inorder[i]);
        }

        vector<int> left_pre;//head节点的左子树的前序遍历结果
        vector<int> right_pre;//head节点的右子树的前序遍历结果
        int j=1;
        for(;j<1+left_inorder.size();j++){
            left_pre.push_back(preorder[j]);
        }
        for(;j<preorder.size();j++){
            right_pre.push_back(preorder[j]);
        }

        head->left=buildTree(left_pre,left_inorder);
        head->right=buildTree(right_pre,right_inorder);

        return head;
    }
};

剑指 Offer 09. 用两个栈实现队列

class CQueue {
public:
    stack<int> st;//存放和队列顺序一样的值
    stack<int> temp;//中转栈
    CQueue() {
        
    }
    
    void appendTail(int value) {//队列尾部插入整数
      
        while(!st.empty()){
            int val=st.top();
            st.pop();
            temp.push(val);
        }
        temp.push(value);
        while(!temp.empty()){
            int val=temp.top();
            temp.pop();
            st.push(val);
        }
    }
    
    int deleteHead() {//队列头部删除整数
        if(st.size()==0) return -1;
        int val=st.top();
        st.pop();
        return val;
    }
};


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

剑指 Offer 10- I. 斐波那契数列

使用动态规划
注意在循环里面每一步的结果都应该取模后再进行赋值,而不应该最后return的时候再取模,会溢出

class Solution {
public:
    int fib(int n) {
        if(n==0) return 0;
        vector<int> dp(n+1,0);
        dp[0]=0;dp[1]=1;
        for(int i=2;i<dp.size();i++){
            dp[i]=(dp[i-1]+dp[i-2])%1000000007;
        }
        return dp[n];
    }
};

剑指 Offer 10- II. 青蛙跳台阶问题

class Solution {
public:
    int numWays(int n) {
        if(n==0) return 1;
        vector<int> dp(n+1,0);
        dp[0]=1;
        dp[1]=1;
        for(int i=2;i<dp.size();i++){
            dp[i]=(dp[i-1]+dp[i-2])%1000000007;
        }
        return dp[n];
    }
};

剑指 Offer 11. 旋转数组的最小数字

法一:
一开始想的是对数组进行排序,利用sort函数返回第一个元素,以为会超时,结果没有

class Solution {
public:
    int minArray(vector<int>& numbers) {
        sort(numbers.begin(),numbers.end());
        return numbers[0];
    }
};

法二:对法一优化一下,循环查找,直到找到第一个比前一个数小的就返回

class Solution {
public:
    int minArray(vector<int>& numbers) {
        int num=numbers[0];
        for(int i=1;i<numbers.size();i++){
            if(numbers[i]<numbers[i-1]){
                num=numbers[i];
                break;
            }
        }
        return num;
    }
};

剑指 Offer 12. 矩阵中的路径

第一遍写的代码是有问题的
比如word="SEE"时
代码相当于只考虑了一种情况,S找到上面E后继续循环,然后返回false,这样是有问题的,其实下面的E完全没考虑到
在这里插入图片描述

class Solution {
public:
    bool is_true(vector<vector<char>>& board,string word,int index_i,int index_j){
        //i,j是word第一个字母在board中的位置
        int m=board.size();
        int n=board[0].size();
        vector<vector<bool>> is_used(m,vector<bool>(n,false));//判断某个位置是否已经使用过了,false代表没使用
        for(int i=1;i<word.size();i++){
            //判断上下左右的位置是否有符合要求的
            //上面
            if(index_i-1>=0&&is_used[index_i-1][index_j]==false&&board[index_i-1][index_j]==word[i]){//不能超限&&上方格子未使用
                    is_used[index_i-1][index_j]=true;
                    index_i--;
                    continue;
            }
            //下面
            if(index_i+1<m&&is_used[index_i+1][index_j]==false&&board[index_i+1][index_j]==word[i]){//不能超限&&下方格子未使用
                    is_used[index_i+1][index_j]=true;
                    index_i++;
                    
                    continue;
            } 
            //左边
            if(index_j-1>=0&&is_used[index_i][index_j-1]==false&&board[index_i][index_j-1]==word[i]){//不能超限&&左边格子未使用
                    index_j--;
                    is_used[index_i][index_j]=true;
                    continue;
            
            } 
            //右边
            if(index_j+1<n&&is_used[index_i][index_j+1]==false&&board[index_i][index_j+1]==word[i]){//不能超限&&右边格子未使用
      
                    index_j++;
                    is_used[index_i][index_j]=true;
                    continue;
            
            }
            return false;//四个格子都不符合
        
        }
        return true;
    }
    bool exist(vector<vector<char>>& board, string word) {
        for(int i=0;i<board.size();i++){
            for(int j=0;j<board[0].size();j++){
                if(board[i][j]==word[0]){
                    bool label=is_true(board,word,i,j);
                    if(label) return true;
                    else continue;
                }
            }
        }
        return false;
    }
};

改正后:使用递归进行改正
dfs经典例子

class Solution {
public:

    bool is_true(vector<vector<char>>& board,string word,int index,int index_i,int index_j,vector<vector<bool>> &is_used){
        //index_i,index_j是index-1位置的字母在board中的位置
        //index是word中本次要比较的下标
        if(index==word.size()) return true;//说明前面的字母都符合要求,返回true

        int m=board.size();
        int n=board[0].size();
        bool label1=false;
        bool label2=false;
        bool label3=false;
        bool label4=false;

        //搜索上下左右的位置是否有=word[index]的
        //上面
        if(index_i-1>=0&&is_used[index_i-1][index_j]==false&&board[index_i-1][index_j]==word[index]){//不能超限&&上方格子未使用
            is_used[index_i-1][index_j]=true;
            label1=is_true(board,word,index+1,index_i-1,index_j,is_used);
            is_used[index_i-1][index_j]=false;
                 
        }
        //下面
        if(index_i+1<m&&is_used[index_i+1][index_j]==false&&board[index_i+1][index_j]==word[index]){//不能超限&&下方格子未使用
            is_used[index_i+1][index_j]=true;
            label2=is_true(board,word,index+1,index_i+1,index_j,is_used);
            is_used[index_i+1][index_j]=false;    
                  
        } 
        //左边
        if(index_j-1>=0&&is_used[index_i][index_j-1]==false&&board[index_i][index_j-1]==word[index]){//不能超限&&左边格子未使用      
            is_used[index_i][index_j-1]=true;
            label3=is_true(board,word,index+1,index_i,index_j-1,is_used);
            is_used[index_i][index_j-1]=false;                      
        } 
        //右边
        if(index_j+1<n&&is_used[index_i][index_j+1]==false&&board[index_i][index_j+1]==word[index]){//不能超限&&右边格子未使用          
            is_used[index_i][index_j+1]=true;
            label4=is_true(board,word,index+1,index_i,index_j+1,is_used);
            is_used[index_i][index_j+1]=false;                        
        }
           
        bool label=label1||label2||label3||label4;//只要有一个方向找到了就可以
        return label;
    }
    bool exist(vector<vector<char>>& board, string word) {
        int m=board.size();
        int n=board[0].size();
        vector<vector<bool>> is_used(m,vector<bool>(n,false));//判断某个位置是否已经使用过了,false代表没使用
        for(int i=0;i<board.size();i++){
            for(int j=0;j<board[0].size();j++){
                if(board[i][j]==word[0]){
                    is_used[i][j]=true;
                    //根据is_true函数参数的定义,这里必须要先对word[0]进行判断后才可以调用这个递归函数
                    bool label=is_true(board,word,1,i,j,is_used);
                    is_used[i][j]=false;
                    if(label) return true;                    
                }

            }
        }
        return false;
    }
};

剑指 Offer 13. 机器人的运动范围

这道题如果这样思考就是错误的:直接两层for循环嵌套判断每个格子的数位之和是否大于k
其实有些格子数位之和大于k之后它就成了障碍物,比如m=16,n=8,k=4,期望输出是15,(10,0)数位之和小于4,看似符合要求,其实并不符合要求,因为前面某些行整行都是障碍物,根本没办法到达(10,0)这个点

正确做法如下:使用深度优先搜索dfs算法

class Solution {
public:
    int sum=1;//[0,0]位置肯定是符合的
    void dfs(int m, int n, int k,vector<vector<bool>> &is_used,int index_i,int index_j){
        //index_i,index_j是当前要搜索的位置
        //向上走
        if(index_i-1>=0&&is_used[index_i-1][index_j]==false){
            if((index_i-1)/10+(index_i-1)%10+index_j/10+index_j%10<=k){
                sum++;
                is_used[index_i-1][index_j]=true;
                dfs(m,n,k,is_used,index_i-1,index_j);

                //注意这道题并不需要回溯,因为能运行到这里都是符合要求的方格,需要计算出所有符合要求的方格
                // is_used[index_i-1][index_j]=false;
                // sum--;
            }
            
        }
        //向下走
        if(index_i+1<m&&is_used[index_i+1][index_j]==false){
            if((index_i+1)/10+(index_i+1)%10+index_j/10+index_j%10<=k){//说明下面的格子符合要求
                sum++;
                is_used[index_i+1][index_j]=true;
                dfs(m,n,k,is_used,index_i+1,index_j);

            }
            
        }
        //向左走
        if(index_j-1>=0&&is_used[index_i][index_j-1]==false){
            if(index_i/10+index_i%10+(index_j-1)/10+(index_j-1)%10<=k){
                sum++;
                is_used[index_i][index_j-1]=true;
                dfs(m,n,k,is_used,index_i,index_j-1);

            }
            
        }
        //向右走
        if(index_j+1<n&&is_used[index_i][index_j+1]==false){
            if(index_i/10+index_i%10+(index_j+1)/10+(index_j+1)%10<=k){
                sum++;
                is_used[index_i][index_j+1]=true;
                dfs(m,n,k,is_used,index_i,index_j+1);

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

        vector<vector<bool>> is_used(m,vector<bool>(n,false));
        is_used[0][0]=true;
        dfs(m,n,k,is_used,0,0);
        return sum;
    }
};

剑指 Offer 14- I. 剪绳子

class Solution {
public:
    int cuttingRope(int n) {
        //dp[i]:拆分正整数i(至少分成两段),能得到的最大乘积
        //(i-j)*j相当于是把i分为两个数,而dp[i-j]*j相当于是把i拆分成三个数及以上,因为dp拆分个数>=2     
        vector<int> dp(n+1,1);
        for(int i=2;i<=n;i++){
            for(int j=i-1;j>=1;j--){
                dp[i]=max(j*(i-j),max(dp[i],dp[i-j]*j));
            }
            
        }
        return dp[n];

    }
};

剑指 Offer 15. 二进制中1的个数

class Solution {
public:
    int hammingWeight(uint32_t n) {
        int sum=0;
        while(n){
            if(n%2==1){//判断最末尾的数是否为1
                sum++;
            }
            n=n/2;//左移一位
        }
        return sum;
    }
};

剑指 Offer 16. 数值的整数次方

使用快速幂算法
快速幂算法介绍
代码如下:
核心思想:利用二进制来加快运算

class Solution {
public:
    double myPow(double x, int n) {
        //快速幂算法
        if( n == 0) return 1;
        if( x == 0.0) return 0;
        long  exp = n;//
        if(n < 0) {
            //int的取值范围:-2147483648~2147483647
            exp = n* (-1.0);
            //当n == INT_MIN时正数时大于INT_MAX的,所以要用一个大于 INT_MAX的类型来保存,同时在将他转正的时候, n*(-1)的结果依然是一个 int,此时的int是个隐藏类型,然后才将这个结果赋值给 exp,所以用来保存结果值的不应该是个int型,我们用double型的 -1 ,这样就可以将相乘的结果值保存为一个 double类型了,然后再进行赋值
        } 
        
        double res = 1.0;
        while(exp != 0){
            if( (exp &1) == 1 ){//指数为奇数,exp & 1这相当于exp % 2 == 1
            //如果二进制位是0的话相当于乘上1,可以不用管
                res *=x;
            }
            x *=x;//2^0,2^1,2^2,2^4....
            exp >>= 1;//右移一位,相当于除以2
            //一次的右移将舍弃一个位例如1011(2)一次左移后变成101(2)
        }

        return n<0 ? 1/res: res;
    }
};

剑指 Offer 17. 打印从1到最大的n位数

class Solution {
public:
    vector<int> printNumbers(int n) {
        
        int num=1;
        for(int i=0;i<n;i++){
            num*=10;//计算出最大的n位数+1的值
        }
        vector<int> result(num-1,0);
        for(int i=1;i<num;i++){
            result[i-1]=i;
        }
        return result;
    }
};

剑指 Offer 18. 删除链表的节点

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* deleteNode(ListNode* head, int val) {
        ListNode* virtul_head=new ListNode(0);
        virtul_head->next=head;//虚拟头节点
        ListNode* temp=head;
        ListNode* pre=virtul_head;//前一个节点
        while(temp){
            if(temp->val==val){
                pre->next=temp->next;
                break;
            }
            //更新前一个节点和当前节点
            pre=temp;
            temp=temp->next;
        }
        return virtul_head->next;
    }
};

剑指 Offer 20. 表示数值的字符串

感觉在做这道题的时候就是在面向用例编程,要考虑的情况很多

class Solution {
public:
    bool isNumber(string s) {
        //出现其他字母为false
        int j=0;
        while(s[j]==' '){
            j++;
        }

        //跳过小数和整数的符号位
        if(s[j]=='+'||s[j]=='-'){
            j++;
        }
        int i=j;

        if(i==s.size()) return false;

        //处理小数和整数,保证i指向小数或者整数的第一个数字的位置
        if(s[i]=='.'){
            if(i==s.size()-1) return false;
            //小数点前面是至少一位数字
            int index=i;
            while(i+1<s.size()&&s[i+1]<='9'&&s[i+1]>='0') i++;//跳到后面跟着的数字的位置
            if(index==i) return false;//后面跟着0个数字
        }
        else if(s[i]<='9'&&s[i]>='0'){
            if(i==s.size()-1) return true;
            while(i+1<s.size()&&s[i+1]<='9'&&s[i+1]>='0') i++;//跳到后面跟着的数字位置            
            if(i+1<s.size()&&s[i+1]=='.'){
                i++;
                i++;//跳到小数点后面的一位
                
            }
        }

        //跳过i位置开始的数字
        while((i<s.size()&&s[i]<='9'&&s[i]>='0')){
            i++;
        }
        if(i==s.size()) return true;//说明只有小数或整数
        if(i==j) return false;//说明没有小数或整数(必选)
        if(s[i]=='e'||s[i]=='E'){
            i++;
            if(i==s.size()) return false;
            if(s[i]=='+'||s[i]=='-'){
                i++;
                if(i==s.size()) return false;//说明符号位后面没有跟数字 
            }
            int temp=i;           
            while(i<s.size()&&s[i]<='9'&&s[i]>='0'){
                i++;
            }
            if(i==s.size()) return true;
           if(i==temp) return false;//说明后面没有数字
        }
        while(s[i]==' '){//后面跟着的空格
            i++;
        }
        if(i==s.size()) return true;//说明都是空格
        else return false;
        
    }
};

剑指 Offer 21. 调整数组顺序使奇数位于偶数前面

设计一个新数组和双指针来存放奇偶数

class Solution {
public:
    vector<int> exchange(vector<int>& nums) {
        if(nums.size()<=1) return nums;
        vector<int> result(nums.size(),0);
        int slow=0;
        int fast=nums.size()-1;
        for(int i=0;i<nums.size();i++){
            if(nums[i]%2==1){//奇数
                result[slow++]=nums[i];
            }
            else{
                result[fast--]=nums[i];
            }
        }
        return result;
    }
};

剑指 Offer 22. 链表中倒数第k个节点

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* getKthFromEnd(ListNode* head, int k) {
        int size=1;//节点个数
        ListNode* temp=head;
        while(temp->next){
            temp=temp->next;
            size++;
        }
        temp=head;
        int path=size-k;//需要走的步数
        while(path--){
            temp=temp->next;
        }
        return temp;
    }
};

剑指 Offer 24. 反转链表

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* pre=NULL;
        while(head){//两两交换
            ListNode* next=head->next;
            head->next=pre;
            pre=head;
            head=next;
        }
        return pre;
    }
};

剑指 Offer 25. 合并两个排序的链表

建立新链表,两两比较节点值

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        ListNode* virtual_head=new ListNode(0);
        ListNode* temp=virtual_head;
        while(l1&&l2){
            ListNode* node=new ListNode(0);
            if(l1->val<=l2->val){
                node->val=l1->val;
                l1=l1->next;
                
            }
            else{
                node->val=l2->val;
                l2=l2->next;

            }
            temp->next=node;
            temp=temp->next;
        }
        if(l1){
            temp->next=l1;
        }
        if(l2){
            temp->next=l2;
        }
        return virtual_head->next;
    }
};

剑指 Offer 26. 树的子结构

/**
 * 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 is_true(TreeNode* A, TreeNode* B){
        //使用递归判断树的结构是否相等
        if(B==NULL) return true;
        if(A==NULL) return false;
        if(A->val!=B->val) return false;
        bool left=false;
        bool right=false;
        left=is_true(A->left,B->left);
        right=is_true(A->right,B->right);
        return left&&right;//左右子树结构都相等才是true

    }
    bool isSubStructure(TreeNode* A, TreeNode* B) {
        //使用层次遍历法判断每一个节点的值是否和B的根节点的值相等
        //如果相等就执行判断函数
        if(A==NULL||B==NULL) return false;
        queue<TreeNode*> que;
        que.push(A);
        while(!que.empty()){
            int size=que.size();
            for(int i=0;i<size;i++){
                TreeNode* node=que.front();
                que.pop();
                if(node->val==B->val){
                    if(is_true(node,B)) return true;
                }
                if(node->left) que.push(node->left);
                if(node->right) que.push(node->right);
            }
            
        }
        return false;
    }
};

剑指 Offer 27. 二叉树的镜像

使用层次遍历法进行镜像,交叉左右节点

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* mirrorTree(TreeNode* root) {
        if(root==NULL) return NULL;
        queue<TreeNode*> que;
        que.push(root);
        while(!que.empty()){
            int size=que.size();
            for(int i=0;i<size;i++){
                TreeNode* node=que.front();
                que.pop();
                TreeNode* temp=node->left;
                node->left=node->right;
                node->right=temp;
                if(node->left) que.push(node->left);
                if(node->right) que.push(node->right);
            }
        }
        return root;
    }
};

剑指 Offer 28. 对称的二叉树

/**
 * 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 is_syem(TreeNode* left,TreeNode* right){
        //使用递归进行判断
        if(left==NULL&&right==NULL) return true;
        if(left==NULL) return false;//只有左边为空
        if(right==NULL) return false;
        if(left->val!=right->val) return false;

        //左右节点的值都是相等的
        bool label1=is_syem(left->left,right->right);
        bool label2=is_syem(left->right,right->left);

        return label1&&label2;

    }
    bool isSymmetric(TreeNode* root) {
        if(root==NULL) return true;
        return is_syem(root->left,root->right);
    }
};

剑指 Offer 30. 包含min函数的栈

class MinStack {
public:
    /** initialize your data structure here. */
    stack<int> st;
    map<int,int> m;//map会自动排序,key是插入的数,value是这个数的个数
    MinStack() {

    }
    
    void push(int x) {
        st.push(x);
        m[x]++;
    }
    
    void pop() {
        int val=st.top();
        m[val]--;
        st.pop();
    }
    
    int top() {
        return st.top();
    }
    
    int min() {
        int min_num=0;
        for(auto it=m.begin();it!=m.end();it++){
            if(it->second>0){//第一个就是最小值
                min_num=it->first;
                break;
            }
        }
        return min_num;
    }
};

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack* obj = new MinStack();
 * obj->push(x);
 * obj->pop();
 * int param_3 = obj->top();
 * int param_4 = obj->min();
 */

剑指 Offer 31. 栈的压入、弹出序列

根据给定的数组模拟栈的压入、弹出过程

class Solution {
public:
    bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
        stack<int> st;
        int j=0;
        for(int i=0;i<pushed.size();i++){
            st.push(pushed[i]);
            while(!st.empty()&&popped[j]==st.top()){//如果要弹出
                st.pop();
                j++;
            }
        }
        if(st.empty()) return true;//如果最后所有元素都弹出去了,说明弹出顺序是正确的
        return false;
    }
};

剑指 Offer 32 - I. 从上到下打印二叉树

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<int> levelOrder(TreeNode* root) {
        //层次遍历法
        vector<int> result;
        if(root==NULL) return result;
        queue<TreeNode*> que;
        que.push(root);
        while(!que.empty()){
            
            int size=que.size();
            for(int i=0;i<size;i++){
                TreeNode* node=que.front();
                result.push_back(node->val);
                que.pop();
                if(node->left) que.push(node->left);
                if(node->right) que.push(node->right);
            }
        }
        return result;
    }
};

剑指 Offer 32 - II. 从上到下打印二叉树 II

层次遍历法

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> result;
        if(root==NULL) return result;
        queue<TreeNode*> que;
        que.push(root);
        while(!que.empty()){
            int size=que.size();
            vector<int> temp;
            for(int i=0;i<size;i++){
                TreeNode* node=que.front();
                que.pop();
                temp.push_back(node->val);
                if(node->left) que.push(node->left);
                if(node->right) que.push(node->right);
            }
            result.push_back(temp);
        }
        return result;
    }
};

剑指 Offer 32 - III. 从上到下打印二叉树 III

注意这道题是整个一行的顺序反过来,而不是只对一个节点的左右节点交换顺序
错误写法:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> result;
        if(root==NULL) return result;
        bool label=false;//true:从左到右的顺序打印  false:从右到左的顺序
        queue<TreeNode*> que;
        que.push(root);
        while(!que.empty()){
            int size=que.size();
            vector<int> temp;
            for(int i=0;i<size;i++){
                TreeNode* node=que.front();
                que.pop();
                temp.push_back(node->val);
                if(!label){
                    if(node->right) que.push(node->right);
                    if(node->left) que.push(node->left);
                }
                else{
                    if(node->left) que.push(node->left);
                    if(node->right) que.push(node->right);
                }
            }
            label=~label;
            result.push_back(temp);

        }
        return result;
    }
};

正确写法

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> result;
        if(root==NULL) return result;
        bool label=true;//true:从左到右的顺序打印  false:从右到左的顺序
        queue<TreeNode*> que;
        que.push(root);
        while(!que.empty()){
            int size=que.size();
            vector<int> temp;
            for(int i=0;i<size;i++){
                TreeNode* node=que.front();
                que.pop();
                temp.push_back(node->val);
                if(node->left) que.push(node->left);
                if(node->right) que.push(node->right);
                
            }
            if(label==false) reverse(temp.begin(),temp.end());
            label=!label;
            result.push_back(temp);

        }
        return result;
    }
};

剑指 Offer 33. 二叉搜索树的后序遍历序列

class Solution {
public:
    bool trans(vector<int>& postorder,int start,int end){
        //[start,end]
        if(end-start<=1) return true;
        int root=postorder[end];//根节点的值
        int i=start;
        for(;i<end;i++){
            if(postorder[i]>root) break;
        }
        //i为右子树的起始位置
        for(int j=i;j<end;j++){
            if(postorder[j]<root) return false;

        }
        bool left=trans(postorder,start,i-1);
        bool right=trans(postorder,i,end-1);
        return left&&right;

    }
    bool verifyPostorder(vector<int>& postorder) {
        //后序遍历:左右中
        if(postorder.size()==0) return true;
        return trans(postorder,0,postorder.size()-1);

    }
};

剑指 Offer 34. 二叉树中和为某一值的路径

采用递归进行寻找,由于节点值可能为负,所以在判断返回条件的时候不能说当前target<0就返回

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<vector<int>> result;
    void look_target(TreeNode* node,int target,vector<int> &path){
        if(node->left==NULL&&node->right==NULL&&target==0){
            result.push_back(path);
            return;
        }
        if(node->left){
            int val=node->left->val;
            path.push_back(val);
            look_target(node->left,target-val,path);
            path.pop_back();
        }
        if(node->right){
            int val=node->right->val;
            path.push_back(val);
            look_target(node->right,target-val,path);
            path.pop_back();
        }
    }
    vector<vector<int>> pathSum(TreeNode* root, int target) {
        if(root==NULL) return result;
        vector<int> path;
        path.push_back(root->val);
        look_target(root,target-root->val,path);
        return result;
        
    }
};

剑指 Offer 35. 复杂链表的复制

/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* next;
    Node* random;
    
    Node(int _val) {
        val = _val;
        next = NULL;
        random = NULL;
    }
};
*/
class Solution {
public:
    Node* copyRandomList(Node* head) {
        if(head==NULL) return NULL;
        Node* new_head=new Node(head->val);//因为是复制,所以必须new一个新地址充当复制链表的头结点
        Node* temp=new_head;
        Node* virtual_head=new Node(0);//原链表的虚拟头结点
        virtual_head->next=head;
        //先处理next节点的指向
        while(head->next){
            Node* next=new Node(head->next->val);//因为是复制,所以必须new一个新地址
            temp->next=next;
            head=head->next;
            temp=temp->next;
        }
        //处理random节点的指向
        head=virtual_head->next;//原链表中的处理节点
        temp=new_head;//复制链表中的处理节点
        while(head){
            if(head->random==NULL){
                head=head->next;
                temp=temp->next;
                continue;
            }
            //注意这里不能判断val值是否相等,因为多个节点中的val值可能相等,所以会导致random指针指向错误
            int gap=0;//头节点距离random节点有多远
            Node* node_gap=virtual_head->next;//这里不是head,head只是处理过程的一个节点
            while(node_gap!=head->random){//找到地址相同的
                node_gap=node_gap->next;
                gap++;
            }
            
            Node* node=new_head;
            while(gap--) node=node->next;
            temp->random=node;
            head=head->next;
            temp=temp->next;
        }
        return new_head;
    }
};

剑指 Offer 36. 二叉搜索树与双向链表

/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* left;
    Node* right;

    Node() {}

    Node(int _val) {
        val = _val;
        left = NULL;
        right = NULL;
    }

    Node(int _val, Node* _left, Node* _right) {
        val = _val;
        left = _left;
        right = _right;
    }
};
*/
class Solution {
public:
    vector<Node*> vec;//存放中序遍历结果
    void mid_look(Node* node){
        //中序遍历的结果就是按照节点值从小到大的顺序
        if(node==NULL){
            return;
        }
        
        mid_look(node->left); 
        vec.push_back(node);
        mid_look(node->right);
        
    }
    Node* treeToDoublyList(Node* root) {
        if(root==NULL) return NULL;
        mid_look(root);
        for(int i=0;i<vec.size()-1;i++){
            Node* node=vec[i];
            Node* next=vec[i+1];
            node->right=next;
            next->left=node;
        }
        vec[vec.size()-1]->right=vec[0];
        vec[0]->left=vec[vec.size()-1];

        return vec[0];
    }
};

剑指 Offer 38. 字符串的排列

去重可以参考下面的讲解:
全排列去重

class Solution {
public:
    vector<string> result;
    void permu(string s,string path,vector<int> &used_char){
        if(path.size()==s.size()){
            result.push_back(path);
            return;
        }
        for(int i=0;i<s.size();i++){
            if(i>0&&s[i]==s[i-1]&&used_char[i-1]==0) continue;//同树层相同元素去重
            if(used_char[i]==0){//代表还没用过
               used_char[i]=1; 
               path+=s[i];
               permu(s,path,used_char);
               path.pop_back();
               used_char[i]=0;
            }

        }
    }
    vector<string> permutation(string s) {
        string path="";
        sort(s.begin(),s.end());
        vector<int> used_char(s.size(),0);
        permu(s,path,used_char);
        return result;
    }
};

剑指 Offer 40. 最小的k个数

法一:直接使用api
sort函数的排序方法类似于快排方法,时间复杂度为n*log2(n)

class Solution {
public:
    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        vector<int> result(k,0);
        sort(arr.begin(),arr.end());
        for(int i=0;i<k;i++){
            result[i]=arr[i];
        }
        return result;
    }
};

法二:使用快排

class Solution {
public:
        // 快速排序 不稳定排序  时间复杂度O(n*log n),空间复杂度O(logn)(递归会使用栈)
        //1、选取第一个数为基准
        //2、将比基准小的数交换到前面,比基准大的数交换到后面
        //3、对左右区间重复第二步,直到各区间只有一个数
    void quickSort(vector<int>& vec,int index_start,int index_end){
        //使用递归 传入整个数组和需要处理的数据起始位置[index_start,index_end]
        if(index_end-index_start<=0) return;//区间内只有一个数
        int index=index_start;
        for(int i=index_start+1;i<=index_end;i++){
            if(vec[i]<vec[index]){
                //把vec[i]的值换到index前面去
                for(int j=i;j>index;j--){
                    swap(vec[j],vec[j-1]);
                }
                
                index++;//更新基准的位置
            }
        }
        quickSort(vec,index_start,index-1);
        quickSort(vec,index+1,index_end);
    }
    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        vector<int> result(k,0);
        quickSort(arr,0,arr.size()-1);
        for(int i=0;i<k;i++){
            result[i]=arr[i];
        }
        return result;
    }
};

剑指 Offer 42. 连续子数组的最大和

使用前缀和的思想

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

剑指 Offer 45. 把数组排成最小的数

class Solution {
public:
    bool static cmp(int a,int b){
        //按照a和b转换成string字符串结合起来的最小结果来决定a和b的前后位置
        string s1=to_string(a);
        string s2=to_string(b);
        return (s1+s2)<(s2+s1);
    }
    string minNumber(vector<int>& nums) {
        sort(nums.begin(),nums.end(),cmp);
        string result;
        for(auto num:nums){
            result+=to_string(num);
        }
        
        return result;
    }
};

剑指 Offer 46. 把数字翻译成字符串

使用动态规划
感觉这道题也可以用回溯里面的分割来做,每一层的节点值代表本次分割的情况,比如1223,第一层可以是1或者12

class Solution {
public:
    int translateNum(int num) {
        string str=to_string(num);
        int size=str.size();
        if(size<=1) return size;
        vector<int> dp(size+1,0);//dp[i]表示i个数字有多少种翻译方法
        dp[0]=1;
        dp[1]=1;
        for(int i=2;i<=size;i++){
            string s="";
            s+=str[i-2];
            s+=str[i-1];
  
            int k=stoi(s);
            if(k>=10&&k<=25){//两个条件都必须有,因为可能是06之类的
                dp[i]=dp[i-1]+dp[i-2];
            }
            else{
                dp[i]=dp[i-1];//第i位的数字和i-1位数字的分割方法进行组合
            }
        }
        return dp[size];
    }
};

剑指 Offer 47. 礼物的最大价值

class Solution {
public:
    int maxValue(vector<vector<int>>& grid) {
        int m=grid.size();
        int n=grid[0].size();
        vector<vector<int>> dp(m,vector<int>(n,0));//[i,j]位置的最大值
        for(int i=0;i<n;i++){
            if(i==0) dp[0][0]=grid[0][0];
            else{
                dp[0][i]=dp[0][i-1]+grid[0][i];
            }
        }
        for(int i=1;i<m;i++){
            dp[i][0]=dp[i-1][0]+grid[i][0];
        }
        for(int i=1;i<m;i++){
            for(int j=1;j<n;j++){
                dp[i][j]=max(dp[i-1][j]+grid[i][j],dp[i][j-1]+grid[i][j]);
            }
        }
        return dp[m-1][n-1];
    }
};

剑指 Offer 48. 最长不含重复字符的子字符串

使用动态规划+哈希表完成

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        if(s.size()<=1) return s.size();
        unordered_map<char,int> m;//key记录最长子字符串的字符,value记录对应的字符在s中的下标位置
        vector<int> dp(s.size(),1);//dp[i]:字符串的最长子字符串以第i个字符结尾的
        m.insert(pair<char,int>(s[0],0));
        int start=0;//最长子字符串的长度的开始位置
        int max=0;//最长子字符串的长度
        for(int i=1;i<dp.size();i++){
            auto it=m.find(s[i]);
            if(it==m.end()){
                //说明不重复
                m.insert(pair<char,int>(s[i],i));
                dp[i]=dp[i-1]+1;
               
            }
            else{
                int index=it->second;//重复的下标位置
                for(int j=start;j<=index;j++){
                    m.erase(s[j]);//删掉哈希表重复位置之前的所有元素
                }
                m.insert(pair<char,int>(s[i],i));
                dp[i]=i-index;//从index后一位算起
                start=index+1;//更新开始位置
            }
            if(dp[i]>max) max=dp[i];
        }
        return max;

    }
};

剑指 Offer 49. 丑数

使用动态规划和三指针
这里需要注意下去重的问题,如果某次寻找丑数,找到了下一个丑数10,则pointer2和pointer5都需要加一,因为5乘2等于10, 5乘2也等于10,这样可以确保10只被数一次。

代码里面是用的三个并行的if操作,所以包含去重操作了

class Solution {
public:
    int nthUglyNumber(int n) {
        //丑数*质因子可以得到丑数
        vector<int> dp(n+1,1);
        int p1=1,p2=1,p3=1;//三个指针分别代表*2、*3、*5操作,如果某个操作被用过了,那么就将该指针+1
        for(int i=2;i<dp.size();i++){
            int val=min(min(2*dp[p1],3*dp[p2]),5*dp[p3]);
            if(val==2*dp[p1]) p1++;
            if(val==3*dp[p2]) p2++;
            if(val==5*dp[p3]) p3++;
            dp[i]=val;

        }
        return dp[n];
    }
};

剑指 Offer 50. 第一个只出现一次的字符

class Solution {
public:
    char firstUniqChar(string s) {
        if(s.size()==0) return ' ';
        unordered_map<char,int> m;
        
        for(int i=0;i<s.size();i++){
            m[s[i]]++;
        }
        for(int i=0;i<s.size();i++){
            if(m[s[i]]==1){
                return s[i];
            }
        }
        return ' ';
    }
};

剑指 Offer 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) {
        ListNode* node1=headA;
        ListNode* node2=headB;
        int size_A=0;
        int size_B=0;
        //算出两个链表的长度
        while(node1){
            size_A++;
            node1=node1->next;
        }
        while(node2){
            size_B++;
            node2=node2->next;
        }
        //对两个链表进行尾对齐
        int path=abs(size_A-size_B);
        node1=headA;
        node2=headB;
        if(size_A>size_B){
            
            while(path--){
                node1=node1->next;
            }
            while(node1&&node2){
                if(node1==node2) return node1;
                node1=node1->next;
                node2=node2->next;
            }
        }
        else{
            while(path--){
                node2=node2->next;
            }
            while(node1&&node2){
                if(node1==node2) return node1;
                node1=node1->next;
                node2=node2->next;
            }                
            
        }

        return NULL;//没找到返回NULL
    }
};

剑指 Offer 53 - I. 在排序数组中查找数字 I

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int result=0;
        for(int i=0;i<nums.size();i++){
            if(nums[i]==target){
                result++;
            }
        }
        return result;
    }
};

剑指 Offer 53 - II. 0~n-1中缺失的数字

class Solution {
public:
    int missingNumber(vector<int>& nums) {
        int result=0;
        vector<int> vec(nums.size()+1,0);
        for(int i=0;i<nums.size();i++){
            vec[nums[i]]=1;
        }
        for(int i=0;i<vec.size();i++){
            if(vec[i]==0){
               result=i; 
               break;
            }
        }
        return result;
    }
};

剑指 Offer 54. 二叉搜索树的第k大节点

/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* left;
    Node* right;

    Node() {}

    Node(int _val) {
        val = _val;
        left = NULL;
        right = NULL;
    }

    Node(int _val, Node* _left, Node* _right) {
        val = _val;
        left = _left;
        right = _right;
    }
};
*/
class Solution {
public:
    vector<Node*> vec;//存放中序遍历结果
    void mid_look(Node* node){
        //中序遍历的结果就是按照节点值从小到大的顺序
        if(node==NULL){
            return;
        }
        
        mid_look(node->left); 
        vec.push_back(node);
        mid_look(node->right);
        
    }
    Node* treeToDoublyList(Node* root) {
        if(root==NULL) return NULL;
        mid_look(root);
        for(int i=0;i<vec.size()-1;i++){
            Node* node=vec[i];
            Node* next=vec[i+1];
            node->right=next;
            next->left=node;
        }
        vec[vec.size()-1]->right=vec[0];
        vec[0]->left=vec[vec.size()-1];

        return vec[0];
    }
};

剑指 Offer 55 - I. 二叉树的深度

使用递归遍历搜所有达到根节点的路径的深度,再选出最大深度

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    int result=0;
    void max_depth(TreeNode* root,int depth){
        if(root==NULL){
            if(depth>result) result=depth;
            return;
        }
        max_depth(root->left,depth+1);
        max_depth(root->right,depth+1);

    }
    int maxDepth(TreeNode* root) {
        max_depth(root,0);
        return result;
    }
};

剑指 Offer 55 - II. 平衡二叉树

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    int max_depth=0;
    void is_banlanced(TreeNode* root,int depth){
        //递归遍历计算树的深度
        if(root==NULL){
            if(max_depth<depth) max_depth=depth;
            return;
        }
        is_banlanced(root->left,depth+1);
        is_banlanced(root->right,depth+1);


    }

    bool isBalanced(TreeNode* root) {
        if(root==NULL) return true;
        bool label1=true;//左子树是否为平衡二叉树的标志位
        bool label2=true;//右子树是否为平衡二叉树的标志位
        if(root->left) label1=isBalanced(root->left);
        if(root->right) label2=isBalanced(root->right);
        
        max_depth=0;
        is_banlanced(root->right,0);
        int depth_right=max_depth;//右子树的深度

        max_depth=0;
        is_banlanced(root->left,0);
        int depth_left=max_depth;//左子树的深度

        //若当前节点左右子树的深度相差超过1
        if(abs(depth_left-depth_right)>1) return false;

        //只有当前节点、当前节点的左右子树都符合要求时才会返回true
        return label1&&label2;
    }
};

剑指 Offer 57. 和为s的两个数字

法一:暴力解法+判断。超时
两层for循环 O(n^2)

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
   
        vector<int> result;
        if(nums.size()<=1) return result;

        for(int i=0;i<nums.size();i++){
            if(nums[i]>target) break;
            for(int j=i+1;j<nums.size();j++){
                if(nums[i]+nums[j]>target) break;
                if(nums[i]+nums[j]==target){
                    result.push_back(nums[i]);
                    result.push_back(nums[j]);
                    return result;
                }
            }
        }
        return result;
    }
};

法二:
时间复杂度O(n)

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        vector<int> result;
        if(nums.size()<=1) return result;
        int i=0,j=nums.size()-1;//使用双指针从头尾开始逼近
        
        for(;i>=0&&j<nums.size()&&i<j;){
            int sum=nums[i]+nums[j];
            if(sum>target){
                j--;
                continue;
            }
            else if(sum<target){
                i++;
                continue;
            }
            else{
                result.push_back(nums[i]);
                result.push_back(nums[j]);
                break;
            }
        }
        return result;
    }
};

剑指 Offer 58 - I. 翻转单词顺序

这道题虽然是简单题,但是需要注意考虑全是空格的情况

class Solution {
public:
    string reverseWords(string s) {
        // if(s.size()==0) return s;//记得判断特殊情况
        string result="";
        vector<string> vec;
        reverse(s.begin(),s.end());
        //“去掉”前后空格
        int i=0;
        for(;i<s.size();i++){
            if(s[i]!=' '){
                break;
            }
        }
        if(i==s.size()) return "";//去除全是空格的情况
        int j=s.size()-1;
        while(s[j]==' '){
            j--;
        }

        string str="";
        for(int k=i;k<=j;){
            if(s[k]!=' '){
                str.push_back(s[k]);
                k++;
            }
            else{//遇到空格就放入单词
                reverse(str.begin(),str.end());
                vec.push_back(str);
                while(s[k]==' ') k++;
                str="";
            }
        }
        reverse(str.begin(),str.end());
        vec.push_back(str);
        for(int i=0;i<vec.size();i++){
            result+=vec[i];
            if(i!=vec.size()-1) result+=" ";
        }
        return result;
    }
};

剑指 Offer 59 - II. 队列的最大值

class MaxQueue {
public:
    queue<int> que;
    map<int,int,greater<int>> m;//按照key的值从大到小排序
    MaxQueue() {

    }
    
    int max_value() {
        if(que.size()==0) return -1;
        auto it=m.begin();
        int max_num=0;
        for(;it!=m.end();it++){
            if(it->second>0){
                max_num=it->first;
                break;
            }
        }
        return max_num;
    }
    
    void push_back(int value) {
        que.push(value);
        m[value]++;
    }
    
    int pop_front() {
        if(que.size()==0) return -1;
        m[que.front()]--;
        int val=que.front();
        que.pop();
        return val;
    }
};

/**
 * Your MaxQueue object will be instantiated and called as such:
 * MaxQueue* obj = new MaxQueue();
 * int param_1 = obj->max_value();
 * obj->push_back(value);
 * int param_3 = obj->pop_front();
 */

剑指 Offer 60. n个骰子的点数

剑指 Offer 61. 扑克牌中的顺子

class Solution {
public:
    bool isStraight(vector<int>& nums) {
        //0相当于是癞子
        int num_zero=0;
        sort(nums.begin(),nums.end());
        int i=0;
        for(;i<nums.size();i++){
            if(nums[i]==0) num_zero++;
            else{
                break;
            }
        }
        int gap=0;
        for(;i<nums.size()-1;i++){
            if(nums[i]==nums[i+1]) return false;
            if(nums[i+1]-nums[i]>1) gap+=(nums[i+1]-nums[i]-1);//其他数之间的空隙
        }
        if(num_zero>=gap) return true;
        return false;
    }
};

剑指 Offer 63. 股票的最大利润

如果一旦出现加上某天相比前一天卖出的利润小于0,那么就重新开始计算

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int result=0;
        int max=0;
        for(int i=1;i<prices.size();i++){
            result+=prices[i]-prices[i-1];
            if(result<0){
                result=0;
                
            }
            else{
                if(result>max) max=result;
            }
        }
        return max;
    }
};

剑指 Offer 64. 求1+2+…+n

这道题其实就是在提示用递归的方法做

class Solution {
public:
    int sum=0;
    void sum_num(int n){
        if(n==0) return;
        sum+=n;
        sum_num(n-1);
        
    }
    int sumNums(int n) {
        sum_num(n);
        return sum;
    }
};

剑指 Offer 67. 把字符串转换成整数

class Solution {
public:
    int strToInt(string str) {
        //分情况进行讨论
        int result=0;
        int i=0;
        for(;i<str.size();i++){
            if(str[i]!=' '){
                break;
            }
        }
        if(i==str.size()) return 0;//字符串为空
        int j=i;
        long int num=0;
        if(str[i]=='-'){
            int a=0;
            for(j=i+1;j<str.size();j++){
                if(isdigit(str[j])){

                    num=num*10+str[j]-'0';
                    if(-num<INT_MIN) return INT_MIN;
                }
                else break;
            }
            num=-num;
            if(num<INT_MIN){
                result=INT_MIN;
            }
            else result=num;
        }
        if(str[i]=='+'){
            int a=0;
            for(j=i+1;j<str.size();j++){
                if(str[j]>='0'&&str[j]<='9'){
                    a=str[j]-'0';
                    num=num*10+a;  
                    if(num>INT_MAX) return INT_MAX;                  
                }
                else break;
            }
          
           result=num;
        }
        if('0'<=str[i]<='9'){
        // if(str[i]<='9'&&str[i]>='0'){
            int a=0;
            for(j=i;j<str.size();j++){
                if(str[j]>='0'&&str[j]<='9'){
                    a=str[j]-'0';
                    num=num*10+a;   
                    if(num>INT_MAX) return INT_MAX;                  
                }
                else break;
            }
            result=num;
        }
        return result;

    }
};

剑指 Offer 68 - I. 二叉搜索树的最近公共祖先

利用好二叉搜索树的特性,先判断当前节点是否符合要求,再根据节点值一步步地向下进行搜索

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:

    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
       if(root==q||root==p||root==NULL||(root->val<p->val&&root->val>q->val)||(root->val>p->val&&root->val<q->val)){
           return root;
       }
       TreeNode* node;
       if(root->val<q->val&&root->val<p->val) node=lowestCommonAncestor(root->right,p,q);
       if(root->val>q->val&&root->val>p->val) node=lowestCommonAncestor(root->left,p,q);
       return node;
        
    }
};

剑指 Offer 68 - II. 二叉树的最近公共祖先

使用递归遍历,使用前序遍历进行搜寻,如果left和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:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root==p||root==q||root==NULL){
            return root;
        }

        TreeNode* left=lowestCommonAncestor(root->left,p,q);
        TreeNode* right=lowestCommonAncestor(root->right,p,q);

        if(left==NULL&&right==NULL) return NULL;
        else if(left==NULL) return right;
        else if(right==NULL) return left;
        else return root;
       

    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/534936.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大模型训练数据多样性的重要性

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

图形编程周刊(2023.001)

图形编程周刊(2023.001) key: webgpu webgl 3d webgis three.js cesium.js 这里是力博荣(Libaro)三维可视化带来的 图形编程周刊, 争取每周五发布。 更新源位置: https://gitee.com/lianming/graphics-programming-weekly/blob/master/2023001/2023001.md 发现的代码 1、th…

少儿编程 中国电子学会图形化编程等级考试Scratch编程三级真题解析(判断题)2023年3月

2023年3月scratch编程等级考试三级真题 判断题(共10题,每题2分,共20分) 26、单击如图所示积木,将生成一个介于1.5和2.5之间的一位小数 答案:错 考点分析:考查随机数积木的使用,随机生成小数的时候,生成的小数位不止一位,所以错误 27、为新建变量命名时,不区分大小…

红黑树封装map和set

文章目录 红黑树封装map和set1. 改良红黑树1.1 改良后的节点1.2 改良后的类分别添加仿函数代码 3. 封装map和set3.1 set3.2 map 3. 迭代器3.1 begin 和 end3.2 operator()和operator--()3.3 const迭代器set的迭代器map的迭代器 4. map的operator[]的重载5. 完整代码实现5.1 RBT…

美团二面:聊聊ConcurrentHashMap的存储流程

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱敲代码的小黄&#xff0c;独角兽企业的Java开发工程师&#xff0c;CSDN博客专家&#xff0c;阿里云专家博主&#x1f4d5;系列专栏&#xff1a;Java设计模式、Spring源码系列、Netty源码系列、Kafka源码系列、JUC源码…

手把手教你彻底卸载MySQL

❤写在前面 ❤博客主页&#xff1a;努力的小鳴人 ❤系列专栏&#xff1a;MySQL8.0基础学习 ❤欢迎小伙伴们&#xff0c;点赞&#x1f44d;关注&#x1f50e;收藏&#x1f354;一起学习&#xff01; ❤如有错误的地方&#xff0c;还请小伙伴们指正&#xff01;&#x1f339; ​ …

抖音SEO矩阵系统源码开发搭建(一)

抖音SEI矩阵系统源码开发&#xff0c;需要遵循一下步骤&#xff1a; 1. 确定需求和功能&#xff1a;明确系统的主要目标和需要实现的功能&#xff0c;包括关键词研究、短视频制作、外链建设、数据分析、账号设置优化等方面。 2. 设计系统架构&#xff1a;根据需求和功能确定系…

Golang每日一练(leetDay0068) 二叉树右视图、岛屿数量

目录 199. 二叉树的右视图 Binarytree Right Side View &#x1f31f;&#x1f31f; 200. 岛屿数量 Number-of-islands &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日…

【C++】图解类和对象(中)

类和对象&#xff08;中&#xff09; 文章目录 类和对象&#xff08;中&#xff09;一、类的6个默认成员函数二、构造函数1.定义2.特性3.对特性的理解及几点注意事项 二、析构函数总结 一、类的6个默认成员函数 如果一个类中什么成员都没有&#xff0c;简称为空类。 空类中真的…

只需6步,就能让你的 React +Tailwind.css站点实现暗黑功能

欢迎回来&#xff0c;开始一次新的编码之旅吧&#xff01;今天&#xff0c;我们将进入神秘的世界&#xff0c;探索如何在你的React.js网站中使用Tailwind.css实现暗黑模式。Tailwind.css 是你编码工具中的强大助手&#xff0c;结合React.js使用&#xff0c;你可以创造出令人惊叹…

Swoft中使用Consul微服务

目录 Swoft中接入Consul Swoft服务限流 Swoft服务熔断和降级 在之前我写的一篇内容&#xff1a;PHP中接入consul&#xff0c;实现微服务的注册发现和配置中心_浮尘笔记的博客-CSDN博客 中&#xff0c;使用ThinkPHP6.0框架接入了微服务Consul&#xff0c;并且留下了一个彩蛋 …

【K8s】Helm

文章目录 一、Helm介绍1、背景2、介绍3、核心概念4、chart的基本结构5、helm官网 二、部署Helm1、安装helm客户端2、安装Tiller 三、常用指令1、仓库相关 helm repo2、chart相关 四、入门案例1、构建第一个chart2、将chart包发布到Repository3、在 Kubernetes 中部署应用4、升级…

用JS实现虚拟列表(IT枫斗者)

用JS实现虚拟列表 简介 当一个列表需要渲染大量数据的时候是非常耗时的&#xff0c;而且在列表滚动的过程中会出现卡顿的现象。即使用上懒加载解决了列表初始化时渲染过慢的问题&#xff0c;但是每次拉取下一页数据的时候都会造成列表的重新渲染。随着拉取的数据越来越多&…

使用火焰图进行性能分析(一)

为什么会用到火焰图&#xff1f;火焰图能干那些事儿&#xff1f; 分析函数执行的频度&#xff1b;分析哪些函数经常阻塞&#xff1b;分析哪些函数频繁操作内存&#xff1b; 火焰图的主要特点&#xff1a; 每一列代表一个调用栈&#xff0c;每个格子代表一个函数&#xff1b;…

计算机图形学-GAMES101-4

一、变换矩阵中的旋转部分 当我们旋转Q角度和旋转-Q角度时&#xff0c;变换矩阵中旋转的部分如下图所示&#xff1a; 旋转Q和旋转-Q的变换矩阵应该互为逆矩阵&#xff0c;而我们可以看到它们互为对方矩阵的转置。其实Rq是一个正交矩阵&#xff0c;因此其逆矩阵就是它自己的转…

chrome渲染引擎的工作主流程

一见如故 浏览器的渲染&#xff1a;HTML字符串>渲染成最终的像素1、CSS Parser发生在css预解析线程中&#xff0c;不在主线程中&#xff1b;会预览整个HTML文档&#xff0c;下载css相关全部内容&#xff0c;解析生成CSSOM树 2、attachment >以及生成布局树>分层>按…

微服务的使用场景和架构设计方案

目录 【单体架构】 【微服务解决哪些问题】 微服务的拆分原则 微服务使用过程中有哪些坑&#xff1f; 【RPC框架】 常见的网络 IO 模型 RPC 执行过程总结 【CAP原理】 如何使用 CAP 理论 【服务注册和发现】 【配置中心】 【Consul】 Consul介绍 Consul角色 Con…

Go语言的结构体、方法、指针

目录 【定义新数据类型】 【结构体】 定义结构体 结构体变量的声明和初始化 结构体的内存表示 【方法】 receiver 参数 receiver参数的约束 方法的深入理解 goroutine中方法的使用 receiver 参数类型如何选择&#xff1f;T还是*T&#xff1f; 方法集合 【指针】 …

9. 三星索引和Mysql内核查询成本计算实战

MySQL性能调优 1. 高性能的索引创建策略1.1 只为用于搜索、排序或分组的列创建索引1.2 合理设计多列索引1.3 尽可能设计三星索引1.4 主键尽量是很少改变的列1.5 处理冗余和重复索引1.6 删除未使用的索引1.7 InnoDB中的索引 2. 补充资料&#xff1a;磁盘和B树Mysql内核查询成本计…

从0到1无比流畅的React入门教程

无比流畅的React入门教程TOC React 是什么 简介 用于构建 Web 和原生交互界面的库React 用组件创建用户界面通俗来讲&#xff1a;是一个将数据渲染为HTML视图的开源JS库 其他信息 Facebook 开发&#xff0c;并且开源 为什么使用React? 原生JS使用DOM-API修改UI代码很繁…