【leetcode】剑指offer专项训练详解(C++)

题目所在链接:

leetcode剑指offer

1. 栈与队列

剑指 Offer 09. 用两个栈实现队列
class CQueue {
public:
    stack<int> a,b;
    CQueue() {
        // 维护两个栈,第一个栈支持插入操作,第二个栈支持删除操作
        while(!a.empty()){
            a.pop();
        }
        while(!b.empty()){
            b.pop();
        }

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

    }
    
    int deleteHead() {
        if(b.empty()){
            // 如果b是空的
            while(!a.empty()){
                // 容易忘记这个条件
                b.push(a.top());
                a.pop();
            }
        }
        if(b.empty()){
            return -1;
        }else{
            int res = b.top();
            b.pop();
            return res;
        }

    }
};

/**
 * Your CQueue object will be instantiated and called as such:
 * CQueue* obj = new CQueue();
 * obj->appendTail(value);
 * int param_2 = obj->deleteHead();
 */
剑指 Offer 30. 包含min函数的栈
class MinStack {
public:
    stack<int> data, min_s;
    /** initialize your data structure here. */
    MinStack() {
        // 使用一个辅助栈,该辅助栈负责保存目前操作下前面所有数据中最小的值
        min_s.push(INT_MAX);

    }
    
    void push(int x) {
        data.push(x);
        if(x < min_s.top()){
            min_s.push(x);
        }else{
            min_s.push(min_s.top());
        }

    }
    
    void pop() {
        data.pop();
        min_s.pop();

    }
    
    int top() {
        return data.top();

    }
    
    int min() {
        return min_s.top();
    }
};

/**
 * 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();
 */

2. 链表

剑指 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) {
        // 利用栈来保存链表中的值
        stack<int> sta;
        vector<int> res;
        while(head != NULL){
            sta.push(head->val);
            head = head -> next;
        }
        while(!sta.empty()){
            res.push_back(sta.top());
            sta.pop();
        }
        return res;
    }
};
剑指 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:
    map<Node*, Node*> listmap;
    Node* copyRandomList(Node* head) {
        // 本题中因为随机指针的存在,当我们拷贝节点时,「当前节点的随机指针指向的节点」可能还没创建,因此我们需要变换思路。
        // 递归+map
        if(head == NULL){
            return NULL;
        }
        if(!listmap.count(head)){
            Node* newone = new Node(head->val);
            listmap.emplace(head, newone);
            newone->next = copyRandomList(head->next);
            newone->random = copyRandomList(head->random);
        }
        return listmap[head];

        
    }
};

3.字符串

剑指 Offer 05. 替换空格
class Solution {
public:
    string replaceSpace(string s) {
        // 循环遍历放进一个新的string即可
        string res;
        for(int i = 0;i<s.size();i++){
            if(s[i] == ' '){
                res = res + "%20";
            }
            else{
                res = res + s[i];
            }
        }
        return res;
    }
};
剑指 Offer 58 - II. 左旋转字符串
class Solution {
public:
    string reverseLeftWords(string s, int n) {
        string res;
        // 先放[n...length-1]部分的字符串,再放[0...n-1]的字符串
        int length = s.size();
        for(int i = n;i<length;i++){
            res = res + s[i];
        }
        for(int i = 0;i<n;i++){
            res = res + s[i];
        }
        return res;
    }
};

4.查找算法

剑指 Offer 03. 数组中重复的数字
class Solution {
public:
    int findRepeatNumber(vector<int>& nums) {
        // 用set
        set<int> numtimes;
        for(int i =0;i<nums.size();i++){
            if(numtimes.count(nums[i])){
                return nums[i];
            }
            else{
                numtimes.insert(nums[i]);
            }
        }
        return -1;
    }
};
剑指 Offer 53 - I. 在排序数组中查找数字 I
class Solution {
public:
    int search(vector<int>& nums, int target) {
        // 二分查找
        int left = 0;
        int right = nums.size() - 1;
        int res = 0;
        while(left <= right){
            int mid = left + (right - left)/2;
            if(nums[mid] == target){
                left = mid;
                right = mid;
                while(left >=0 && nums[left] == target){
                    left--;
                }
                while(right<nums.size() && nums[right] == target){
                    right++;
                }
                res = right - left -1;
                return res;
            }
            else if(nums[mid] > target){
                right = mid - 1;
            }
            else if(nums[mid] < target){
                left = mid + 1;
            }
        }
        return 0;
    }
};
剑指 Offer 53 - II. 0~n-1中缺失的数字
class Solution {
public:
    int missingNumber(vector<int>& nums) {
        // 二分查找
        int left = 0;
        int right = nums.size() - 1;

        while(left <= right){
            int mid = left + (right - left)/2;
            if(left == right && nums[left] != left){
                return left;
            }
            // 考虑有可能是缺少最后一个数
            else if(left == right && nums[left] == left){
                return left + 1;
            }

            if(nums[mid] == mid){
                left = mid + 1;
            }
            else if(nums[mid] != mid){
                right = mid - 1;
            }
        }
        return left;
    }
};
剑指 Offer 04. 二维数组中的查找
class Solution {
public:
    bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
        // 二分查找 只能想到nlog(n)级别的
        // 答案最好是初始值在右上角 只用考虑row++和col--
        if(matrix.size() <= 0){
            return false;
        }
        for(int i=0;i<matrix.size();i++){
            int left = 0;
            int right = matrix[i].size() - 1;
            while(left <= right){
                int mid = left + (right - left)/2;
                if(matrix[i][mid] == target){
                    return true;
                }
                else if(matrix[i][mid] < target){
                    left = mid + 1;
                }else{
                    right = mid - 1;
                }
            }
        }
        return false;

    }
};
剑指 Offer 11. 旋转数组的最小数字
class Solution {
public:
    int minArray(vector<int>& numbers) {
        // 遍历
        if(numbers.size() <= 0){
            return -1;
        }
        if(numbers.size() == 1){
            return numbers[0];
        }
        int length;
        for(int i = 0;i<numbers.size()-1;i++){
            int j = i + 1;
            if(numbers[j] < numbers[i]){
                return numbers[j];
            }
            length++;
        }
        if(length+1 == numbers.size()){
            return numbers[0];
        }
        return 0;

    }
};
剑指 Offer 50. 第一个只出现一次的字符
class Solution {
public:
    char firstUniqChar(string s) {
        // 两个26位的数组,一个记录次数,一个记录该字母的位置
        // 普通数组放INT_MAX会溢出,所以用尽量用vector
        vector<int> index = vector<int>(26, INT_MAX);
        int number[26];
        memset(number, 0, sizeof(number));
        for(int i=0;i<s.size();i++){
            int cur = s[i] - 'a';
            index[cur] = min(index[cur], i);
            number[cur] = number[cur] + 1;
        }

        int res_index = INT_MAX;
        for(int j=0;j<26;j++){
            if(number[j] == 1){
                res_index = min(index[j], res_index);
            }
        }
        if(res_index == INT_MAX){
            return ' ';
        }
        return s[res_index];
    }
};

5. 搜索与回溯算法

剑指 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> res;
        queue<TreeNode*> que;
        if(root == NULL){
            return res;
        }
        que.push(root);
        while(!que.empty()){
            TreeNode* node = que.front();
            res.push_back(node->val);
            que.pop();
            if(node->left != NULL){
                que.push(node->left);
            }
            if(node->right != NULL){
                que.push(node->right);
            }

        }

        return res;
    }
};
剑指 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) {
        //i的值初始化赋值queue.length,循环开始时执行那一次,判断时不受queue变化影响 一定要注意
        // 层序遍历 队列
        vector<vector<int>> res;
        if(root == NULL){
            return res;
        }     
        queue<TreeNode*> que;
        que.push(root);
        while(!que.empty()){
            vector<int> sub_res;
            int length = que.size();
            // 没有想到,当前que的长度就是当前层的节点数
            for(int i=0;i<length;i++){
                TreeNode* node = que.front();
                sub_res.push_back(node->val);
                que.pop();
                if(node->left != NULL){
                    que.push(node->left);
                }
                if(node->right != NULL){
                    que.push(node->right);
                }
            }
            res.push_back(sub_res);
        }
        return res;
    }
};
剑指 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) {
        // 层序遍历 BFS 队列 需要考虑是第几层
        // 0. 错误版本,一边遍历一遍根据层数换顺序,有可能产生换了2遍次序,就又换回去了,
        // 1. 因此,从左到右顺序得到一层的结果后,最后再根据层数换顺序
        vector<vector<int>> res;
        if(root == NULL){
            return res;
        }
        int depth = 0;
        queue<TreeNode*> que;
        que.push(root);
        while(!que.empty()){
            vector<int> sub_res;
            depth = depth + 1;
            int length = que.size();
            //cout<<"length"<<length<<endl;
            //cout<<"depth"<<depth<<endl;
            for(int i=0;i<length;i++){
                TreeNode* node = que.front();
                sub_res.push_back(node->val);
                que.pop();
                if(node->left != NULL){
                    que.push(node->left);
                }
                if(node->right != NULL){
                    que.push(node->right);
                }
            }
            cout<<"depth"<<depth<<endl;
            if(depth % 2 == 0){
                reverse(sub_res.begin(), sub_res.end());
                res.push_back(sub_res);
            }else{
                res.push_back(sub_res);
            }
        }
        return res;
    }
};
剑指 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;
        TreeNode* tmp = root->left;
        root->left = mirrorTree(root->right);
        root->right = mirrorTree(tmp);
        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 isSymmetric(TreeNode* root) {
        bool index = true;
        if(root != NULL){
            index = isgoodtree(root->left, root->right);
        }
        return index;


    }
    bool isgoodtree(TreeNode* leftnode, TreeNode* rightnode){
        if(leftnode == NULL && rightnode == NULL){
            return true;
        }
        if(leftnode == NULL || rightnode == NULL){
            return false;
        }
        if(leftnode->val != rightnode->val){
            return false;
        }
        return isgoodtree(leftnode->right, rightnode->left) && isgoodtree(leftnode->left, rightnode->right);
    }
};
剑指 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 isSubStructure(TreeNode* A, TreeNode* B) {
        // 1.先序遍历树 AA 中的每个节点 n_A(对应函数 isSubStructure(A, B))
        // 2.判断树 AA 中 以 n_A为根节点的子树 是否包含树 BB 。(对应函数 recur(A, B))
        if(A == NULL && B == NULL){
            return true;
        }
        if(A == NULL || B == NULL){
            return false;
        }
        return recur(A,B) || isSubStructure(A->left, B) || isSubStructure(A->right, B);

    }
    bool recur(TreeNode* A, TreeNode* B) {
        if(B == NULL){
            return true;
        }
        if(A == NULL){
            return false;
        }
        if(A->val != B->val){
            return false;
        }
        return recur(A->left, B->left) && recur(A->right, B->right);
        
        

    }
};

6. 动态规划

剑指 Offer 10- I. 斐波那契数列
class Solution {
public:
    int fib(int n) {
        // 动态规划
        
        vector<int> memo(n+1, 0);
        if(n == 0){
            return 0;
        }
        if(n == 1){
            return 1;
        }
        memo[0] = 0;
        memo[1] = 1;

        for(int i=2;i<=n;i++){
            memo[i] = memo[i-1] + memo[i-2];
            memo[i] = memo[i] % 1000000007;
        }
        return memo[n];
    }
};
剑指 Offer 10- II. 青蛙跳台阶问题
class Solution {
public:
    int numWays(int n) {
        // 动态规划
        // dp[n] = dp[n-1] + dp[n-2]
        if(n == 0){
            return 1;
        }
        if(n == 1){
            return 1;
        }
        if(n == 2){
            return 2;
        }
        vector<int> memo(n+1);
        memo[1] = 1;
        memo[2] = 2;
        for(int i=3;i<=n;i++){
            memo[i] = memo[i-1] + memo[i-2];
            memo[i] = memo[i] % 1000000007;
        }
        return memo[n];
    }
};
剑指 Offer 63. 股票的最大利润
/*
class Solution {
public:
    int res = 0;
    void find_maxprofit(vector<int>& prices, int index, vector<int>& pre){
        if(pre.size() == 2){
            res = max(res, pre[1] - pre[0]);
            return;
        }
        for(int i = index; i<prices.size();i++){
            pre.push_back(prices[i]);
            find_maxprofit(prices, i+1, pre);
            pre.pop_back();
        }
        return;
    }
    int maxProfit(vector<int>& prices) {
        // 1. 暴力回溯法,最后一个测试用例会超时,但现实中可以解决该问题
        // 2. 直接双重循环也可以
        if(prices.size() <= 1){
            return 0;
        }
        vector<int> pre;
        find_maxprofit(prices, 0, pre);
        return res;
    }
};
*/

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        // 动态规划
        // 一个数组用来存dp[i]
        // 一个数用来存上一轮的成本
        // dp[i]=max(dp[i−1],prices[i]−min(prices[0:i]))
        if(prices.size() <= 1){
            return 0;
        }
        vector<int> dp(prices.size()+1);
        dp[0] = 0;
        dp[1] = 0;
        int cost = prices[0];
        for(int i=2;i<=prices.size();i++){
            cost = min(cost, prices[i-1]);
            dp[i] = max(dp[i-1], prices[i-1] - cost);
        }
        return dp[prices.size()];
    }
};
剑指 Offer 42. 连续子数组的最大和
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        //1. 同样可以用暴力回溯法
        // 2. 动态规划
        // dp[i] = max(dp[i-1], dp[i-1]+nums[i-1])没有体现连续
        // dp[i] = max(nums[i-1], dp[i-1]+nums[i-1])
        // 为何定义最大和 dp[i]中必须包含元素 nums[i] :保证 dp[i] 递推到 dp[i+1] 的正确性;如果不包含 nums[i],递推时则不满足题目的 连续子数组 要求
        vector<int> memo(nums.size()+1);
        if(nums.size() == 1){
            return nums[0];
        }
        memo[1] = nums[0];
        int res = INT_MIN;
        for(int i=2;i<=nums.size();i++){
            if(memo[i-1]>0){
                memo[i] = memo[i-1]+nums[i-1];
            }
            if(memo[i-1] <=0){
                memo[i] = nums[i-1];
            }
            res  = max(memo[i], res);
        }
        return max(res, memo[1]);
    }
};
剑指 Offer 47. 礼物的最大价值
class Solution {
public:
    int maxValue(vector<vector<int>>& grid) {
        // 动态规划
        // dp[i][j] = max(dp[i-1][j-1],dp[i-1][j], dp[i][j-1]) + grid[i][j];
        int m = grid.size();
        int n = grid[0].size();
        vector<vector<int>> dp(m,vector<int>(n));
        dp[0][0] = grid[0][0];
        // 对于第0行和第0列,只能有一个方向,首先初始化
        for(int j=1;j<n;j++){
            dp[0][j] = grid[0][j] + dp[0][j-1];
        }
        for(int i=1;i<m;i++){
            dp[i][0] = grid[i][0] + dp[i-1][0];
        }
        for(int i=1;i<m;i++){
            for(int j=1;j<n;j++){
                dp[i][j] = max(dp[i-1][j-1], max(dp[i-1][j], dp[i][j-1])) + grid[i][j];
            }
        }
        return dp[m-1][n-1];
    }
};
剑指 Offer 46. 把数字翻译成字符串
class Solution {
public:
    int translateNum(int num) {
        string nums = to_string(num);
        vector<int> dp(nums.size()+1);
        // 动态规划
        // if(10<= pre <=25) dp[i] = dp[i-2] +dp[i-1]
        // else dp[i] = dp[i-1]
        if(nums.size() <= 1){
            return 1;
        }
        dp[0] = 1; 
        dp[1] = 1;
        for(int i=2;i<=nums.size();i++){
            auto pre = nums.substr(i-2, 2);
            cout<<pre<<endl;
            if(pre <="25" && pre >= "10"){
                dp[i] = dp[i-1] + dp[i-2];
            }else if(pre>"25" || pre<"10"){
                dp[i] = dp[i-1];
            }
        }
        return dp[nums.size()];


    }
};
剑指 Offer 48. 最长不含重复字符的子字符串
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        // 滑动窗口可以用
        /*
        int left =0;
        int right = 0;
        int res = 0;
        set<char> substring;
        while(left <= right && right <s.size()){
            if(!substring.count(s[right])){
                substring.insert(s[right]);
                right ++;

            }else{
                res = max<int>(res, substring.size());
                substring.erase(s[left]);
                left++;
            }
        }
        return max<int>(res, substring.size());
        */
        // 动态规划
        // 一定包含s[i]才能保证连续
        // 1.if dp[i-1] ==0 dp[i] = 1;
        // 2.if dp[i-1] >0 
        // 2.1 如果前面包含了这个字符位置是j dp[i] = i-1-j;
        // 2.2 如果前面没有这个字符dp[i] = dp[i-1] +1 
        vector<int> dp(s.size()+1);
        int res = 0;
        if(s.size()==0){
            return 0;
        }
        if(s.size() ==1){
            return 1;
        }
        dp[1] = 1;
        for(int i=2;i<=s.size();i++){
            if(dp[i-1] == 0){
                dp[i] = 1;
            }
            else if(dp[i-1] >0){
                // 需要考虑前面的dp[i-1]里面是否包含这个字符
                bool flag = false;
                for(int j = i-2;j>=i-1-dp[i-1];j--){
                    if(s[j] == s[i-1]){
                        dp[i] = i - 1 - j;
                        flag = true;
                    }
                }
                if(!flag){
                    dp[i] = dp[i-1] +1;
                }
            }
            res = max(dp[i], res);
        }
        return max(res, dp[1]);
    }
};
剑指 Offer 49. 丑数(*)
class Solution {
public:
    int nthUglyNumber(int n) {
        // 动态规划
        vector<int> dp(n);
        dp[0] = 1;
        int a=0, b=0, c=0;
        for(int i=1;i<n;i++){
            int n2 = dp[a] * 2;
            int n3 = dp[b] * 3;
            int n5 = dp[c] * 5;
            dp[i] = min(n2, min(n3, n5));
            if(dp[i] == n2) a++;
            if(dp[i] == n3) b++;
            if(dp[i] == n5) c++;
        }

        return dp[n-1];
    }
};
剑指 Offer 60. n个骰子的点数(*)
class Solution {
public:
    vector<double> dicesProbability(int n) {
        // 动态规划
        // 逆向思维
        vector<vector<double>>dp(n+1, vector<double>(6*n+1));
        vector<double>res(5*n+1);
        for(int i=1;i<=6;i++){
            dp[1][i] = 1.0/6;
        }
        

        if(n == 1){
            for(int i=0;i<=5;i++){
                res[i] = (1.0/6);
            }
            return res;
        }

        for(int i=2;i<=n;i++){
            for(int j=i;j<=6*n;j++){
                for(int k=1;k<=6;k++){
                    if(j-k>0){
                        dp[i][j] = dp[i][j] + dp[i-1][j-k]/6;
                    }
                    else{
                        break;
                    }
                }
            }
        }
        for(int i=0;i<=5*n;i++){
            res[i] = dp[n][n+i];
        }
        return res;
    }
};
剑指 Offer 19. 正则表达式匹配(*)
class Solution {
public:
    bool isMatch(string s, string p) {
        // 动态规划 脑子在打铁
        int m = s.size() + 1;
        int n = p.size() + 1;
        
        vector<vector<bool>> dp(m, vector<bool>(n, false));
        dp[0][0] = true;
        // 初始化第一行
        for(int j=2;j<n;j+=2){
            dp[0][j] = dp[0][j-2] && p[j-1] == '*';
        }
        // 状态转移
        for(int i=1;i<m;i++){
            for(int j=1;j<n;j++){
                if(p[j-1] == '*'){
                    if(dp[i][j-2]) dp[i][j] = true;
                    else if(dp[i-1][j] && s[i-1] == p[j-2]) dp[i][j] = true;
                    else if(dp[i-1][j] && p[j-2] == '.') dp[i][j] = true;
                }
                else{
                    if(dp[i-1][j-1] && s[i-1] == p[j-1]) dp[i][j] = true;
                    else if(dp[i-1][j-1] && p[j-1] == '.') dp[i][j] = true;
                }
            }
        }
        return dp[m-1][n-1];

    }
};

7. 双指针

剑指 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* fast=head;
        ListNode* slow=head;
        // 注意如果就是头节点
        if(head->val == val){
            return head->next;
        }
        while(fast !=NULL){
            if(fast->val == val){
                ListNode* fina = fast->next;
                slow->next = fina;
                return head;
            }
            else{
                slow = fast;
                fast = fast->next;
            }
        }
        return head;
    }
};
剑指 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) {
        ListNode* slow = head;
        ListNode* fast = head;
        // 双指针,一定要注意2个while循环分开
        while(fast!=NULL && k>0){
            fast = fast->next;
            k--;
        }
        while(fast != NULL){
            fast = fast->next;
            slow = slow->next;
        }
        return slow;
    }
};
剑指 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) {
        if(l1 == NULL){
            return l2;
        }
        if(l2 == NULL){
            return l1;
        }
        if(l1->val > l2->val){
            l2->next = mergeTwoLists(l1, l2->next);
            return l2;
        }
        else if(l1->val <= l2->val){
            l1->next = mergeTwoLists(l1->next, l2);
            return l1;
        }
        return l1;
    }
};
剑指 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) {
        // 双指针太绝了,根本想不到
        // 还可以利用hashset
        // 注意公共节点是地址一样,并不是只比较节点中的val一样就行
        if(headA == NULL && headB == NULL){
            return NULL;
        }
        if(headA == NULL || headB == NULL){
            return NULL;
        }
        ListNode* a = headA;
        ListNode* b = headB;
        while(a != b){
            a = a == NULL?headB:a->next;
            b = b== NULL?headA:b->next;

        }
        return a;
    }
};
剑指 Offer 21. 调整数组顺序使奇数位于偶数前面
class Solution {
public:
    vector<int> exchange(vector<int>& nums) {
        // 双指针 O(n) O(1)
        int length = nums.size();
        if(length <=1){
            return nums;
        }
        int i = 0;
        int j = length -1;
        while(i <= j){
            if(nums[i]%2!=0 && nums[j]%2==0){
                i++;
                j--;
            }
            else if(nums[i]%2==0 && nums[j]%2!=0){
                swap(nums[i], nums[j]);
                i++;
                j--;
            }
            else if(nums[i]%2==0 && nums[j]%2==0){
                j--;
            }
            else if(nums[i]%2!=0 && nums[j]%2!=0){
                i++;
            }
        }
        return nums;
    }
};
剑指 Offer 57. 和为s的两个数字
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        // 双指针,头尾指针
        vector<int> res;
        if(nums.size() == 1){
            return res;
        }
        int left = 0;
        int right = nums.size() - 1;
        while(left < right){
            if(nums[left] + nums[right] == target){
                res.push_back(nums[left]);
                res.push_back(nums[right]);
                return res;
            }
            else if(nums[left] + nums[right] > target){
                right--;
            }
            else if(nums[left] + nums[right] < target){
                left++;
            }
        }
        return res;
    }
};

剑指 Offer 58 - I. 翻转单词顺序
class Solution {
public:
    string reverseWords(string s) {
        // 时间复杂度 O(n) 空间复杂度:O(n)
        if(s.size() == 0){
            return "";
        }
        // 删除多余空格,将s存储进vector里面
        string res;
        vector<string> ss;
        s.erase(s.find_last_not_of(" ") + 1);
        s.erase(0, s.find_first_not_of(" "));
        vector<int> index;
        index.push_back(-1);
        for(int i =0;i<s.size();i++){
            if(s[i] == ' '){
                index.push_back(i);
            }
        }
        index.push_back(s.size());
        string sub_string;
        for(int i=0;i<index.size()-1;i++){
            sub_string = s.substr(index[i]+1, index[i+1] - index[i]);
            // 就算中间夹了多少个空格,最后截取下来的sub_string还是一个空格
            if(sub_string == " ") continue;
            sub_string.erase(sub_string.find_last_not_of(" ") + 1);
            sub_string.erase(0, sub_string.find_first_not_of(" "));
            ss.push_back(sub_string);
            cout<<sub_string<<endl;
        }

        // 对vector用双指针
        int left = 0;
        int right = ss.size() - 1;
        while(left < right){
            swap(ss[left], ss[right]);
            left++;
            right--;
        }

        for(int i=0;i<ss.size();i++){
            res = res+ss[i] + ' ';
        }
        res.erase(res.find_last_not_of(" ") + 1);
        res.erase(0, res.find_first_not_of(" "));
        return res;
    }
};

8. 搜索和回溯算法

剑指 Offer 12. 矩阵中的路径
class Solution {
public:
    const int dx[4] = {1,-1,0,0};
    const int dy[4] = {0,0,1,-1};
    bool res = false;
    vector<vector<bool>> flag;
    int m;
    int n;
    bool exist(vector<vector<char>>& board, string word) {
        // dfs无返回值,dfs就类似于回溯中全排列的形式
        m = board.size();
        n = board[0].size();
        flag = vector<vector<bool>>(m, vector<bool>(n, false));
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(board[i][j] == word[0]){
                    dfs(board, word, i, j, 0);
                }
            }
        }
        return res;

    }
    void dfs(vector<vector<char>>& board, string word, int i, int j, int k){
        if(res) return;
        if(k == word.size()-1) res = true;
        flag[i][j] = true;
        for(int f=0;f<4;f++){
            int mx = i + dx[f];
            int my = j + dy[f];
            if(mx>=0 && mx<m && my>=0 && my<n && !flag[mx][my] && board[mx][my]==word[k+1]){
                dfs(board, word, mx, my, k+1);
            } 
        }
        flag[i][j] = false;
    }
};
剑指 Offer 13. 机器人的运动范围

BFS解法:

class Solution {
public:
    const int dx[4] = {1,-1,0,0};
    const int dy[4] = {0,0,1,-1};
    int sums(int x){
        int s = 0;
        while(x != 0) {
            s += x % 10;
            x = x / 10;
        }
        return s;
    }

    int movingCount(int m, int n, int k) {
        // bfs广度优先遍历
        vector<vector<bool>> flag = vector<vector<bool>>(m, vector<bool>(n, false));
        queue<pair<int, int>> que;
        int res = 0;
        if(k<0){
            return res;
        }
        que.emplace(0,0);
        res++;
        flag[0][0] = true;
        while(!que.empty()){
            int x = que.front().first;
            int y = que.front().second;
            que.pop();
            for(int i=0;i<4;i++){
                int mx = x + dx[i];
                int my = y + dy[i];
                if(mx>=0 && mx<m && my>=0 && my<n && !flag[mx][my] && sums(mx)+sums(my)<=k){
                    que.emplace(mx, my);
                    flag[mx][my] = true;
                    res++;
                }
            }
        }
        return res;

    }
};

DFS解法:

class Solution {
public:
    const int dx[4] = {1,-1,0,0};
    const int dy[4] = {0,0,1,-1};
    vector<vector<bool>> flag;
    int res;
    int hang;
    int lie;
    int sums(int x){
        int s = 0;
        while(x != 0) {
            s += x % 10;
            x = x / 10;
        }
        return s;
    }

    int movingCount(int m, int n, int k) {
        // bfs广度优先遍历
        /*
        vector<vector<bool>> flag = vector<vector<bool>>(m, vector<bool>(n, false));
        queue<pair<int, int>> que;
        int res = 0;
        if(k<0){
            return res;
        }
        que.emplace(0,0);
        res++;
        flag[0][0] = true;
        while(!que.empty()){
            int x = que.front().first;
            int y = que.front().second;
            que.pop();
            for(int i=0;i<4;i++){
                int mx = x + dx[i];
                int my = y + dy[i];
                if(mx>=0 && mx<m && my>=0 && my<n && !flag[mx][my] && sums(mx)+sums(my)<=k){
                    que.emplace(mx, my);
                    flag[mx][my] = true;
                    res++;
                }
            }
        }
        return res;
        */
        // dfs+回溯思想
        hang = m;
        lie = n;
        flag = vector<vector<bool>>(hang, vector<bool>(lie, false));
        res = 0;
        dfs(0, 0, k);
        return res;

    }
    void dfs(int x, int y, int k){
        int sub = sums(x)+sums(y);
        if(flag[x][y] == true || sub > k) return;
        if(flag[x][y] == false && sub <= k) res++;
        flag[x][y] = true;
        for(int i=0;i<4;i++){
            int mx = x + dx[i];
            int my = y + dy[i];
            if(mx>=0 && mx<hang && my>=0 && my<lie && !flag[mx][my]){
                dfs(mx, my, k);
            }
        }
    }
};
剑指 Offer 34. 二叉树中和为某一值的路径
/**
 * 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>> res;

    vector<vector<int>> pathSum(TreeNode* root, int target) {
        // 回溯 dfs
        if(root == nullptr){
            return res;
        }
        vector<int> pre;
        dfs(root, target, pre);
        return res;
    }
    void dfs(TreeNode* node, int target, vector<int>& pre){
        if(node == nullptr) return;
        pre.push_back(node->val);
        target = target - node->val;
        if(node->left == nullptr && node->right == nullptr && target == 0){
            res.push_back(pre);
        }
        dfs(node->left, target, pre);
        dfs(node->right, target, pre);
        pre.pop_back();
    }
};
剑指 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:
    Node* pre;
    Node* cur;
    Node* head;
    Node* treeToDoublyList(Node* root) {
        // 二叉搜索树 中序遍历是递增数据 左根右
        if(root == nullptr) return nullptr;
        dfs(root);
        pre->right = head;
        head->left = pre;
        //pre->right = head;
        return head;
        
    }
    void dfs(Node* cur){
        if(cur == nullptr) return;
        dfs(cur->left);
        if(pre != nullptr){
            pre->right = cur;
        }else{
            head = cur;
        }
        cur->left = pre;
        pre = cur;
        dfs(cur->right);
    }
};
剑指 Offer 54. 二叉搜索树的第k大节点
/**
 * 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> num_res;
    int kthLargest(TreeNode* root, int k) {
        // 笨办法,前序遍历每一个值,排序后找到第k大
        // 聪明方式,二叉搜索树中序遍历是从小到大的
        if(root == NULL){
            return -1;
        }
        int res = -1;
        binali(root);
        sort(num_res.begin(), num_res.end());
        res = num_res[num_res.size() - k];
        return res;
    }
    void binali(TreeNode* node){
        if(node == NULL){
            return;
        }
        num_res.push_back(node->val);
        binali(node->left);
        binali(node->right);
    }

};
剑指 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 res = 0;
    int maxDepth(TreeNode* root) {
        // 递归 想好递归终止的条件 以及递归过程
        if(root == NULL){
            return 0;
        }
        if(root->left != NULL && root->right == NULL){
            res = 1 + maxDepth(root->left);
        }
        else if(root->left == NULL && root->right != NULL){
            res = 1 + maxDepth(root->right);
        }
        else{
            res = 1 + max(maxDepth(root->left), maxDepth(root->right));
        }
        return res;

    }
};
剑指 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:
    bool flag = false;
    int res = 0;
    bool isBalanced(TreeNode* root) {
        //1. 递归 算每一个节点的左右子树深度 符合条件即可
 
        if(root == NULL){
            return true;
        }
        flag = abs(depthtree(root->right) - depthtree(root->left))<=1?true:false;
        return flag && isBalanced(root->left) && isBalanced(root->right);



    }


    int depthtree(TreeNode* node){
        if(node == NULL){
            return 0;
        }
        if(node->left == NULL && node->right != NULL){
            res = 1 + depthtree(node->right);
        }
        else if(node->left != NULL && node->right == NULL){
            res = 1 + depthtree(node->left);
        }
        else{
            res = 1 + max(depthtree(node->right), depthtree(node->left));
        }
        return res;
    }
};
剑指 Offer 64. 求1+2+…+n
class Solution {
public:
    int sumNums(int n) {
        // 递归
        if(n == 1){
            return 1;
        }
        return sumNums(n-1) + n;
    }
};
剑指 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:
    //vector<TreeNode*> trees;
    vector<TreeNode*> p_path;
    vector<TreeNode*> q_path;


    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        /*
        // 1. 前序遍历后两点前距离前面一点最近的一点或者这点本身就是
        // 先判断是否该点本身就是另一点的父节点,如果是直接返回
        // 不对,并不是前面最近一点就是祖先,例如[3,1,4,null,2] 2 4
        if(isancestor(p, q)) return p;
        if(isancestor(q, p)) return q;
        cout<<isancestor(p, q)<<endl;

        qianxu(root);
        for(int i=0;i<trees.size();i++){
            cout<<trees[i]<<endl;
        }
        int index_p, index_q;
        for(int i=0;i<trees.size();i++){
            if(p->val == q->val && trees[i]->val == p->val){
                index_p = i;
                index_q = i;
                break;
            }
            if(p->val != q->val && trees[i]->val == p->val){
                index_p = i;
                cout<<index_p<<endl;
            }
            if(p->val != q->val && trees[i]->val == q->val){
                index_q = i;
                cout<<index_q<<endl;
            }
        }
        if(index_p < index_q){
            return trees[index_p - 1];
        }
        else if(index_p > index_q){
            return trees[index_q - 1];
        }
        return p;
        */

        // 2.比较到p节点的祖先路径, 和到q节点的祖先路径,然后其中最后一个相等的数字就是最近公共祖先
        p_path = get_path(root, p);
        q_path = get_path(root, q);
        TreeNode* res;
        for(int i=0;i<p_path.size() && i<q_path.size();i++){
            if(p_path[i] == q_path[i]){
                res = p_path[i];
            }else{
                return res;
            }

        }
        return res;

    }

    vector<TreeNode*> get_path(TreeNode* node, TreeNode* p){
        vector<TreeNode*> path;
        while(node != p){
            path.push_back(node);
            cout<<node->val<<endl;
            if(p->val < node->val){
                node = node->left;
            }else{
                node = node->right;
            }
        }
        path.push_back(node);
        return path;
    }
    
    /*
    void qianxu(TreeNode* node){
        if(node == NULL){
            return;
        }
        trees.push_back(node);
        qianxu(node->left);
        qianxu(node->right);
    }
    bool isancestor(TreeNode* father, TreeNode* son){
        if(father == NULL){
            return false;
        }
        if(father == son){
            return true;
        }
        return isancestor(father->left, son) || isancestor(father->right, son);
    }
    */
};
剑指 Offer 68 - 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<TreeNode*> p_path;
    vector<TreeNode*> q_path; 
    vector<vector<TreeNode*>> res;  
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        // 1.dfs找到这两个节点的路径
        // 2. 一一对应 最后一个相同的节点就是最近的公共祖先
        get_path(root, p, p_path);
        get_path(root, q, q_path);
        TreeNode* res_node;
        for(int i=0;i<res[0].size() && i<res[1].size();i++){
            if(res[0][i] == res[1][i]){
                res_node = res[0][i];
            }
            else{
                return res_node;
            }
        }
        return res_node;

    }
    void get_path(TreeNode* node, TreeNode* target, vector<TreeNode*>& path){
        if(node == NULL){
            return;
        }
        path.push_back(node);
        if(node == target){ 
            // 重要!如果只用path作为储存,随着递归path指向的空间会一直改变的
            res.push_back(path);     
            return;
        }
        get_path(node->left, target, path);
        get_path(node->right, target, path);
        path.pop_back();
    }
};
剑指 Offer 38. 字符串的排列
class Solution {
public:
    set<string> res;
    vector<bool> used;
    vector<string> permutation(string s) {
        // 回溯,全排列
        // 然后对结果进行去重

        string pre;
        used = vector<bool>(s.size(), false);
        permuta(s, used, 0, pre);
        vector<string> final_res;
        for(auto string1:res){
            final_res.push_back(string1);
        }
        return final_res;
    }

    void permuta(string s, vector<bool>& used,int index, string pre){
        if(s.size() == pre.size()){
            res.emplace(pre);
            return;
        }

        for(int i=0;i<s.size();i++){
            if(!used[i]){
                pre.push_back(s[i]);
                used[i] = true;
                permuta(s, used, index+1, pre);
                pre.pop_back();
                used[i] = false;
            }
        }
    }
};
剑指 Offer 37. 序列化二叉树(*)
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */

class Codec {
public:

    // Encodes a tree to a single string.
    string serialize(TreeNode* root) {
        // 层序遍历 BFS
        // string a = a+b {a +=b == a.append(b)}
        if(root == NULL) return "[]";
        string res = "[";
        queue<TreeNode*> que;
        que.push(root);
        while(!que.empty()){
            TreeNode* node = que.front();
            que.pop();
            if(node != NULL){
                res += to_string(node->val) + ",";
                que.push(node->left);
                que.push(node->right);
            }
            else{
                res += "null,";
            }
        }
        res += "]";
        return res;
    }

    // Decodes your encoded data to tree.
    TreeNode* deserialize(string data) {
        if(data == "[]"){
            return NULL;
        }
        queue<TreeNode*> deseque;
        stringstream iss(data.substr(1, data.size() - 2));
        string str;
        getline(iss, str, ',');
        TreeNode* root = new TreeNode(atoi(str.c_str()));
        deseque.push(root);
        while(!deseque.empty()){
            TreeNode* node = deseque.front();
            deseque.pop();
            getline(iss, str, ',');
            if(str != "null"){
                TreeNode* left = new TreeNode(atoi(str.c_str()));
                node->left = left;
                deseque.push(left);
            }
            getline(iss, str, ',');
            if(str != "null"){
                TreeNode* right = new TreeNode(atoi(str.c_str()));
                node->right = right;
                deseque.push(right);
            }
        }
        return root;
    }
};


// Your Codec object will be instantiated and called as such:
// Codec codec;
// codec.deserialize(codec.serialize(root));

9. 排序

剑指 Offer 61. 扑克牌中的顺子(*)
class Solution {
public:
    bool isStraight(vector<int>& nums) {
        //此 5 张牌是顺子的 充分条件 如下:
        // 除大小王外,所有牌 无重复 ;设此 5 张牌中最大的牌为 max ,最小的牌为 min (大小王除外),则需满足:max - min < 5
        // 利用set 以及遍历找出最大最小值
        set<int> norepeat;
        int min_num = 14;
        int max_num = -1;
        for(int i = 0;i<nums.size();i++){
            if(nums[i] == 0){
                continue;
            }
            if(norepeat.count(nums[i])) return false;
            norepeat.insert(nums[i]);
            min_num = min(min_num, nums[i]);
            max_num = max(max_num, nums[i]);

        }
        if(max_num - min_num < 5){
            return true;
        }
        return false;

    }
};
剑指 Offer 45. 把数组排成最小的数(*)
class Solution {
public:
    string minNumber(vector<int>& nums) {
        /*
        此题求拼接起来的最小数字,本质上是一个排序问题。设数组 numsnums 中任意两数字的字符串为x和y ,则规定 排序判断规则 为:
        若拼接字符串 x + y > y + x ,则 x “大于” y;
        反之,若 x + y < y + x ,则 x “小于” y ;
        x “小于” y 代表:排序完成后,数组中 x 应在 y 左边;“大于” 则反之。
        */
        vector<string> strs;
        string res;
        for(int i=0;i<nums.size();i++){
            strs.push_back(to_string(nums[i]));
        }
        sort(strs.begin(), strs.end(), [](string& x, string& y){return x+y < y+x;});

        for(int i =0;i<strs.size();i++){
            res.append(strs[i]);
        }

        return res;

    }
};
剑指 Offer 40. 最小的k个数

class Solution {
public:
    void qsort(vector<int>& arr, int low, int high){
        if(low < high){
            int pivot = partition(arr, low, high);
            qsort(arr, low, pivot - 1);
            qsort(arr, pivot + 1, high);
        }
    }

    int partition(vector<int>& arr, int low, int high){
        int pivot = arr[low];
        while(low < high){
            while(low < high && arr[high] >= pivot) high--;
            arr[low] = arr[high];
            while(low < high && arr[low] <= pivot) low++;
            arr[high] = arr[low];
        }
        arr[low] = pivot;
        return low;
    }
    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        // 快排
        vector<int> res;
        if(k < 0){
            return res;
        }
        qsort(arr, 0, arr.size()-1);
        for(int i=0;i<k;i++){
            res.push_back(arr[i]);
        }
        return res;

    }
};

/*
class Solution {
public:
    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        // 维护一个k长度的堆,大根堆
        vector<int> vec(k, 0);
        if (k == 0) { // 排除 0 的情况
            return vec;
        }
        priority_queue<int> Q;
        for (int i = 0; i < k; i++) {
            Q.push(arr[i]);
        }
        for (int i = k; i < arr.size(); i++) {
            if (Q.top() > arr[i]) {
                Q.pop();
                Q.push(arr[i]);
            }
        }
        for (int i = 0; i < k; i++) {
            vec[i] = Q.top();
            Q.pop();
        }
        return vec;
    }
};
*/
剑指 Offer 41. 数据流中的中位数(*)
class MedianFinder {
public:
    /** initialize your data structure here. */
    // 用一个小根堆维持大的数,用一个大根堆维持小的数
    // 大根堆,存前半部分
    priority_queue<int, vector<int>, less<int>> maxheap;
    // 小根堆,存后半部分
    priority_queue<int, vector<int>, greater<int>> minheap;
    MedianFinder() {

    }
    // 维持两个堆保持数量平衡,并且保证左边堆的最大值小于右边堆的最小值
    void addNum(int num) {
        /*
         * 当两堆的数据个数相等时候,左边堆添加元素。
         * 采用的方法不是直接将数据插入左边堆,而是将数据先插入右边堆,算法调整后
         * 将堆顶的数据插入到左边堆,这样保证左边堆插入的元素始终是右边堆的最小值。
         * 同理左边数据多,往右边堆添加数据的时候,先将数据放入左边堆,选出最大值放到右边堆中。
         */
         if(maxheap.size() == minheap.size()){
             minheap.push(num);
             int top = minheap.top();
             minheap.pop();
             maxheap.push(top);
         }else{
             maxheap.push(num);
             int top = maxheap.top();
             maxheap.pop();
             minheap.push(top);
         }
    }
    
    double findMedian() {
        if(maxheap.size() == minheap.size()){
            return (maxheap.top() + minheap.top()) / 2.0;
        }
        else{
            return maxheap.top() / 1.0;
        }

    }
};

/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder* obj = new MedianFinder();
 * obj->addNum(num);
 * double param_2 = obj->findMedian();
 */

10. 分治算法(*)

剑指 Offer 07. 重建二叉树
/**
 * 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:
    unordered_map<int,int> map;
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        //hashmap inorder方便在中序遍历中确定root的位置
        for(int i = 0;i< inorder.size();i++){
            map[inorder[i]] = i;
        }
        // 传入参数:前序,中序,前序序列根节点,中序序列左边界,中序序列右边界
        return build(preorder, inorder, 0, 0, inorder.size() - 1);
    }
    TreeNode* build(vector<int>& preorder, vector<int>& inorder, int pre_root, int index_left, int index_right){
        if(index_left>index_right){
            return NULL;
        }
        TreeNode* root = new TreeNode(preorder[pre_root]);
        int index_root = map[preorder[pre_root]];
        root->left = build(preorder, inorder, pre_root+1, index_left, index_root-1);
        root->right = build(preorder, inorder, pre_root+index_root-index_left+1, index_root+1, index_right);
        return root;
    }
};
剑指 Offer 16. 数值的整数次方
class Solution {
public:
    double myPow(double x, int n) {
        if(n == 0){
            return 1;
        }
        // 递归,区分一下奇偶数情况就不超时了
        else if(n > 0 && n%2==0){
            return myPow(x*x, n/2);
        }
        else if(n>0 && n%2!=0){
            return x * myPow(x*x,n/2);
        }
        else{
            return 1.0 / (myPow(x, abs(n)-1) *x);
        }
    }
};
剑指 Offer 33. 二叉搜索树的后序遍历序列
class Solution {
public:
    bool verifyPostorder(vector<int>& postorder) {
        // 递归分治
        return recur(postorder, 0, postorder.size()-1);
    }
    bool recur(vector<int>& postorder, int left, int root){
        if(left >= root){
            return true;
        }
        int p = left;
        while(postorder[p] <postorder[root]) p++;
        int right = p;
        while(postorder[p] > postorder[root]) p++;
        return p==root && recur(postorder, left, right-1) && recur(postorder, right, root - 1);
    }
};
剑指 Offer 17. 打印从1到最大的n位数
class Solution {
public:
    vector<int> printNumbers(int n) {
        // 暴力法
        vector<int> res;
        string ans;
        ans = "1";
        for(int i=0;i<n;i++){
            ans += "0";
        }
        int a = atoi(ans.c_str());

        for(int i=1;i<a;i++){
            res.push_back(i);
        }

        return res;
    }
};
剑指 Offer 51. 数组中的逆序对
class Solution {
public:
/*
    vector<vector<int>> res;
    int final_res;

    int reversePairs(vector<int>& nums) {
        // 回溯得到所有数组对 组合问题
        // 严重超时
        if(nums.size() <= 1){
            return 0;
        }
        vector<int> pre;
        permute(nums, 0, pre);
        final_res = res.size();
        return final_res;

    }
    void permute(vector<int>& nums, int depth, vector<int>& pre){
        if(pre.size() == 2){
            if(pre[0] > pre[1]){
                res.push_back(pre);
                return;
            }
            return;
        }

        for(int i=depth;i<nums.size();i++){
            pre.push_back(nums[i]);
            permute(nums, i+1, pre);
            pre.pop_back();
        }
    }
    */

    //归并排序的过程,恰好是跟逆顺序对,息息相关
    int count = 0;
    int reversePairs(vector<int>& nums) {
        mergesort(nums, 0, nums.size() - 1);
        return count;
    }

    void mergesort(vector<int>& nums, int low, int high){
        int mid = (high - low)/2 + low;
        if(low < high){
            mergesort(nums, low, mid);
            mergesort(nums, mid+1, high);
            merge(nums, low, mid, high);
        }
    }
    void merge(vector<int>& nums, int low, int mid, int high){
        vector<int> temparr(high-low+1);
        int left = low;
        int right = mid + 1;
        int k = 0;
        while(left<=mid && right<=high){
            if(nums[left] <= nums[right]){
                temparr[k++] = nums[left++];
            }else{
                count += (mid - left + 1);
                temparr[k++] = nums[right++];
            }
        }
        // 把左边的数放进数组中
        while(left <= mid){
            temparr[k++] = nums[left++];

        }
        // 把右边剩余的数放进数组中
        while(right <= high){
            temparr[k++] = nums[right++];
        }
        // 把临时数组中的值覆盖到原数组nums中
        for(int k1=0;k1<temparr.size();k1++){
            nums[low + k1] = temparr[k1];
        }
    }
};

11. 位运算

剑指 Offer 15. 二进制中1的个数
class Solution {
public:
    int hammingWeight(uint32_t n) {
        int res = 0;
        for(int i=0;i<32;i++){
            if(n & (1<<i)){
                res++;
            }
        }
        return res;
    }
};
剑指 Offer 65. 不用加减乘除做加法
class Solution {
public:
    int add(int a, int b) {
        while(b != 0){
            int c = (unsigned int) (a&b) << 1;
            a = a^b;
            b = c;
        }
        return a;

    }
};
剑指 Offer 56 - I. 数组中数字出现的次数
class Solution {
public:
    vector<int> singleNumbers(vector<int>& nums) {
        int x=0, y=0, n=0, m=1;
        vector<int> res;
        // 1. 遍历异或
        for(int num:nums){
            n = n ^ num;
        } 
        //2. 循环左移,计算m
        while((n & m) == 0){
            m <<= 1;
        }
        //3. 遍历nums分组
        for(int num:nums){
            if(num & m) x = x^num;
            else y = y^num;
        }
        res.push_back(x);
        res.push_back(y);
        return res;

    }
};
剑指 Offer 56 - II. 数组中数字出现的次数 II
class Solution {
public:
    int singleNumber(vector<int>& nums) {
        // map
        unordered_map<int, int> num_map;
        for(int num:nums){
            if(num_map.count(num)){
                int temp = num_map[num];
                num_map[num] = temp + 1;
            }
            else{
                num_map.emplace(num,1);
            }
        }

        for(auto& v :num_map){
            if(v.second == 1){
                return v.first;
            }
        }
        return 0;
    }
};

12. 数学(*)

剑指 Offer 39. 数组中出现次数超过一半的数字
class Solution {
public:
    int majorityElement(vector<int>& nums) {
        // 排序 中间的数就是这个数字
        sort(nums.begin(), nums.end());
        return nums[nums.size() /2];
    }
};
剑指 Offer 66. 构建乘积数组
class Solution {
public:
    vector<int> constructArr(vector<int>& a) {
        // 本质就是两个dp数组,分别维护 i 左侧、右侧的乘积和
        if(a.size() <=0){
            return a;
        }
        int length = a.size();
        
        vector<int> left(length);
        vector<int> right(length);
        vector<int> res(length);
        left[0] = 1;
        right[length - 1] = 1;

        for(int i=1;i<length;i++){
            left[i] = left[i-1] * a[i-1];
        }

        for(int i=length-2;i>=0;i--){
            right[i] = right[i+1] * a[i+1];
        }
        for(int i=0;i<length;i++){
            res[i] = left[i] * right[i];
        }
        return res;

    }
};
剑指 Offer 62. 圆圈中最后剩下的数字
class Solution {
public:
    int lastRemaining(int n, int m) {
        /* K神牛逼!约瑟夫环
        状态定义: 设「i, m问题」的解为 dp[i] ;
        转移方程: 通过以下公式可从 dp[i - 1]递推得到 dp[i] ;
                    dp[i]=(dp[i−1]+m)%i

        初始状态:「1, m 问题」的解恒为 0 ,即 dp[1] = 0;
        返回值: 返回「n, m问题」的解 dp[n];
        */
        vector<int> dp(n+1);
        dp[1] = 0;
        if(n == 1){
            return dp[1];
        }
        for(int i=2;i<=n;i++){
            dp[i] = (dp[i-1] + m)%i;
        }
        return dp[n];

    }
};
剑指 Offer 57 - II. 和为s的连续正数序列
class Solution {
public:
    vector<vector<int>> res;
    vector<vector<int>> findContinuousSequence(int target) {
        // 暴力回溯法 100就超时了
        /*
        if(target <= 2){
            return res;
        }
        int length = target/2 +1;
        vector<int> nums(length);
        for(int i=0;i<nums.size();i++){
            nums[i] = i+1;
        }
        vector<int> pre;
        int sum = 0;
        findcombine(nums, target, 0, pre, sum);
        return res;
        


    }
    void findcombine(vector<int>& nums, int target, int index, vector<int>& pre, int sum){
        if(pre.size()>=2 && sum == target && islianxu(pre)){
            res.push_back(pre);
            return;
        }
        for(int i=index;i<nums.size();i++){
            pre.push_back(nums[i]);
            sum = sum+nums[i];
            findcombine(nums, target, i+1, pre, sum);
            pre.pop_back();
            sum = sum-nums[i];
        }
        return;
    }
    bool islianxu(vector<int>& pre){

        for(int i=0;i<pre.size()-1;i++){
            if(pre[i]+1 != pre[i+1]){
                return false;
            }
        }
        return true;
    }
    */

        // 滑动窗口
        int left = 1;
        int right = 2;
        int sum = 3;
        while(left < right){
            if(sum == target){
                vector<int> arr;
                for(int i=left;i<=right;i++){
                    arr.push_back(i);
                }
                res.push_back(arr);
            }
            if(sum >= target){
                sum = sum - left;
                left++;
            }
            else{
                //此处的顺序很重要
                right++;
                sum = sum + right;
            }
        }
        return res;
    }
};
剑指 Offer 14- I. 剪绳子
class Solution {
public:
    int cuttingRope(int n) {
        // 动态规划
        // dp[i]
        if(n <=1){
            return 0;
        }
        if(n==2){
            return 1;
        }

        vector<int> dp(n+1, 0);
        dp[2] = 1;
        dp[3] = 2;

        for(int i=4;i<=n;i++){
            for(int j = 1;j<i;j++){
                // 剩下的i-j长度还剪不剪,如果不剪就是j*(i-j),如果剪就是j*dp[i-j]
                // 对于每一个j,哪个dp[i]最大
                dp[i] = max(dp[i], max(j*(i-j), j*dp[i-j]));
            }
        }
        return dp[n];
    }
};
剑指 Offer 14- II. 剪绳子 II
class Solution {
public:
    long p = 1000000007;
    int cuttingRope(int n) {
        // 本题唯一区别在于存在大数越界问题
        // 无法使用动态规划,只能利用数学+贪心算法
        if(n <= 1){
            return 0;
        }
        if(n == 2){
            return 1;
        }
        if(n == 3){
            return 2;
        }
        if(n == 4){
            return 4;
        }
        long res = 1;
        int mod = 1000000007;
        while(n > 4){
            res = res * 3 % mod;
            n -= 3;
        }
        return (int)(res * n % mod);
    }
};
剑指 Offer 43. 1~n 整数中 1 出现的次数(*)
class Solution {
public:
    int countDigitOne(int n) {
        long digit = 1;
        int res = 0;
        int high = n/10;
        int cur = n%10;
        int low = 0;
        // 当 high 和 cur 同时为 0 时,说明已经越过最高位,因此跳出
        while(high != 0 || cur != 0){
            if(cur==0) res += high * digit;
            else if(cur == 1) res += high * digit + low + 1;
            else res += (high + 1) * digit;
            // 将 cur 加入 low ,组成下轮 low
            low += cur * digit;
            // 下轮 cur 是本轮 high 的最低位
            cur = high % 10;
            // 将本轮 high 最低位删除,得到下轮 high
            high /= 10;
            // 位因子每轮 × 10
            digit *= 10;
        }
        return res;
    }
};
剑指 Offer 44. 数字序列中某一位的数字
class Solution {
public:
    int findNthDigit(int n) {
        long start = 1;
        int digit = 1;
        long count = 9;
        while(n>count){
            n -=count;
            start *= 10;
            digit ++;
            count = start* digit* 9;  
        }
        int num = start +(n-1)/digit;
        string s = to_string(num);
        return s[(n-1)%digit] -'0';
    }
};

13. 模拟(*)

剑指 Offer 29. 顺时针打印矩阵
class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        // 递归yyds
        int hang = matrix.size();
        vector<int> res;
        if(hang <= 0){
            return res;
        }
        int lie = matrix[0].size();
        res = vector<int>(hang * lie);
        //四个角
        recur(matrix, res, hang-1, lie-1, 0, 0, 0);
        return res;
    }

    void recur(vector<vector<int>>& matrix, vector<int>& res, int hang, int lie, int i, int j, int k){
        if( k>=res.size() || i>hang || j>lie){
            return;
        }
        // 从左到右
        for(int m=j; m<=lie&&k<res.size() ;m++){
            res[k++] = matrix[i][m];
        }
        // 从上到下
        for(int n=i+1; n<=hang&&k<res.size() ;n++){
            res[k++] = matrix[n][lie];
        }
        // 从右到左
        for(int p=lie-1; p>=j&&k<res.size(); p--){
            res[k++] = matrix[hang][p];
        }
        // 从下到上
        for(int q=hang-1; q>=i+1&&k<res.size(); q--){
            res[k++] = matrix[q][j];
        }
        recur(matrix, res, hang-1, lie-1, i+1, j+1, k);
    }
};
剑指 Offer 31. 栈的压入、弹出序列
class Solution {
public:
    bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
        // 用到一个辅助栈来模拟整个过程
        stack<int> help_stack;
        int i = 0;
        for(int num:pushed){
            help_stack.push(num);
            while(!help_stack.empty() && help_stack.top() == popped[i]){
                help_stack.pop();
                i++;
            }
        }
        return help_stack.empty();
    }
};
有限状态自动机(牛掰)
剑指 Offer 20. 表示数值的字符串
class Solution {
public:
    bool isNumber(string s) {
        // 有限状态自动机
        /*
        状态定义:按照字符串从左到右的顺序,定义以下 9 种状态。
        1开始的空格
        2幂符号前的正负号
        3小数点前的数字
        4小数点、小数点后的数字
        5当小数点前为空格时,小数点、小数点后的数字
        6幂符号
        7幂符号后的正负号
        8幂符号后的数字
        9结尾的空格
        结束状态:

        合法的结束状态有 2, 3, 7, 8 。


         //0:规定0是初值,字符串表示数值,有4种起始状态,开头空格、符号、数字、前面没有数的小数点
            //其中 开头空格 还是指向states[0],上一位是 开头空格,下一位可以是 空格、符号、数字、前面没有数的小数点
            new HashMap<>() {{ put(' ', 0); put('s', 1); put('d', 2); put('.', 4); }}, 
            //1:上一位是符号,符号位后面可以是 数字、前面没有数的小数点
            new HashMap<>() {{ put('d', 2); put('.', 4); }},
            //2:上一位是数字,数字的下一位可以是 数字、前面有数的小数点、e、结尾空格
            new HashMap<>() {{ put('d', 2); put('.', 3); put('e', 5); put(' ', 8); }}, 
            //3:上一位是前面有数的小数点,下一位可以是 数字、e(8.e2 = 8e2,和2的情况一样)、结尾空格
            new HashMap<>() {{ put('d', 3); put('e', 5); put(' ', 8); }},
            //4:上一位是前面没有数的小数点,下一位只能是 数字(符号肯定不行,e得前面有数才行)              
            new HashMap<>() {{ put('d', 3); }},
            //5:上一位是e,下一位可以是 符号、数字
            new HashMap<>() {{ put('s', 6); put('d', 7); }},
            //6::上一位是e后面的符号,下一位只能是 数字
            new HashMap<>() {{ put('d', 7); }},
            //7:上一位是e后面的数字,下一位可以是 数字、结尾空格
            new HashMap<>() {{ put('d', 7); put(' ', 8); }},
            //8:上一位是结尾空格,下一位只能是 结尾空格
            new HashMap<>() {{ put(' ', 8); }}
        */
         vector<unordered_map<char,int>> states = {
            {{' ', 0},{'s', 1},{'d', 2},{'.', 4}},  //0
            {{'d', 2},{'.', 4}},                    //1
            {{'d', 2},{'.', 3},{'e', 5},{' ', 8}},  //2
            {{'d', 3},{'e', 5},{' ', 8}},           //3
            {{'d', 3}},                             //4
            {{'s', 6},{'d', 7}},                    //5
            {{'d', 7}},                             //6
            {{'d', 7},{' ', 8}},                    //7
            {{' ', 8}},                             //8
        };
        int p = 0;
        char t;
        for(char c : s) {
            if(c >= '0' && c <= '9') t = 'd';
            else if(c == '+' || c == '-') t = 's';
            else if(c == 'e' || c == 'E') t = 'e';
            else if(c == '.' || c == ' ') t = c;
            else t = '?';
            if(!states[p].count(t)) return false;
            p = states[p][t];
        }
        return p == 2 || p == 3 || p == 7 || p == 8;
    }
};
剑指 Offer 67. 把字符串转换成整数
class Solution {
public:
    int strToInt(string str) {
        // 有限状态自动机
        /*
        状态:0:起始状态,1:符号+/-状态,2:数字状态,3:终止状态
        转移条件:空格、符号、数字、其他
        */
        vector<unordered_map<char, int>> states = {
            {{' ',0}, {'s',1}, {'d', 2}, {'e', 3}}, //0
            {{'d',2}, {' ',3}, {'s',3}, {'e',3}}, //1
            {{'e',3}, {'d',2}, {' ',3}, {'s',3}}, //2
            {{' ',3}, {'d',3}, {'s',3}, {'e',3}}, //3
        };

        long res = 0;
        int p = 0;
        int flag = 1;
        char t;
        for(char c:str){
            if(c == ' ') t = ' ';
            else if(c == '+' || c=='-') t = 's';
            else if(c >= '0' && c <= '9') t = 'd';
            else{
                t = 'e';
            }
            p = states[p][t];
            if(p == 1){
                if(c == '+'){
                    flag = 1;
                }else{
                    flag = -1;
                }
            }
            else if(p == 2){
                res = 10 * res + (c - '0');
                if(flag == 1 && res >= INT_MAX){
                    return INT_MAX;
                }
                if(flag == -1 && res > INT_MAX){
                    return INT_MIN;
                }
            }
        }
        return flag * res;
    }
};

14. 栈与队列

剑指 Offer 59 - II. 队列的最大值
class MaxQueue {
public:
    deque<int> que;
    deque<int> fuzhu;
    // 利用一个辅助的双端队列
    MaxQueue() {

    }
    
    int max_value() {
        if(fuzhu.empty()){
            return -1;
        }
        int index = fuzhu.front();
        return index;
    }
    
    void push_back(int value) {
        while(!fuzhu.empty() && fuzhu.back() < value){
            fuzhu.pop_back();
        }
        fuzhu.push_back(value);
        que.push_back(value);

    }
    
    int pop_front() {
        if (que.empty())
            return -1;
        int deq_front = que.front();
        if(fuzhu.front() == deq_front){
            fuzhu.pop_front();
        }
        que.pop_front();
        return deq_front;

    }
};

/**
 * 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 59 - I. 滑动窗口的最大值
class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        vector<int> res;
        // 维护一个单调双端队列
        if(nums.size() <=0){
            return res;
        }
        if(k == 1){
            return nums;
        }

        deque<int> fuzhu;
        for(int i=0;i<k;i++){
            while(!fuzhu.empty() && fuzhu.back() < nums[i]){
                fuzhu.pop_back();
            }
            fuzhu.push_back(nums[i]);
        }
        res.push_back(fuzhu.front());

        for(int i=k;i<nums.size();i++){
            if(nums[i-k] == fuzhu.front()){
                fuzhu.pop_front();
            }
            while(!fuzhu.empty() && fuzhu.back() < nums[i]){
                fuzhu.pop_back();
            }
            fuzhu.push_back(nums[i]);
            res.push_back(fuzhu.front());
        }
        return res;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值