算法集錦

背包問題

描述
在n個物品中挑選若干物品裝入背包,最多能裝多滿?假設背包的大小為m,每個物品的大小為A[i]
動態規劃之背包問題系列
dp[i][j]
前i件物品在承重j內,最多能裝多少重量
dp[i][j] = max(dp[i-1][j], dp[i-1][j-A[i]] + A[i])

    public int backPack(int m, int[] A) {
        // write your code here

        int size = sizeof(A);

        vector<int>dp(m+1, 0);

        for(int i = 1; i <=m;i++) {

            for(int j = 0; j < i;j++) {

                if(i-A[j]>= 0) {
                    dp[i] = max(dp[i - A[j]]+1, dp[i]);
                }
                
            }
        }

        return dp[m];
    }

動態規劃-完全背包問題

動態規劃-完全背包問題
前i件物品在承重j內,最多能裝多少重量
dp[i][j] = max(dp[i-1][j], dp[i][j-A[i]] + V[i])
第二個max是dp[i][j-A[i]]不是dp[i-1][j-A[i]],是因為物體無數個

image.png

木材加工

https://www.lintcode.com/problem/wood-cut
給定長度為n的數組,每個元素代表一個木頭的長度,木頭可以任意截斷,從這堆木頭中截出至少k個相同長度為m的木塊。已知k,求max(m)。

使用二分查找,對木頭最大長度做二分,mid做為截斷長度,遍歷每個木頭來計算一個臨時k,臨時k比k小,證明截斷長度需要更小,r=mid-1,否者l = mid + 1

劍指 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>rootMap;
    
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {

        for(int i = 0;i < inorder.size();i++) {
            rootMap[inorder[i]] = i;
        }

        TreeNode *root = build(preorder, inorder, 0, preorder.size()-1, 0, preorder.size()-1);

        return root;
    }

    TreeNode *build(vector<int>& preorder, vector<int>& inorder, int preorderL,int preorderR, int inorderL, int inorderR) {

        if(preorderL>preorderR) {
            return NULL;
        }

        int rootValue = preorder[preorderL];

        TreeNode *root = new TreeNode(rootValue);

        int mid = rootMap[rootValue];

        int midToLeftLength = mid - inorderL;

        TreeNode *left = build(preorder, inorder, preorderL+1,preorderL+ midToLeftLength,inorderL, mid - 1);

        TreeNode *right = build(preorder, inorder, preorderL+1+ midToLeftLength,preorderR,mid + 1, inorderR);

        root->left = left;
        root->right = right;

        return root;


    }
};

劍指 Offer 11. 旋轉數組的最小數字

把一個數組最開始的若干個元素搬到數組的末尾,我們稱之為數組的旋轉。輸入一個遞增排序的數組的一個旋轉,輸出旋轉數組的最小元素。例如,數組 [3,4,5,1,2] 為 [1,2,3,4,5] 的一個旋轉,該數組的最小值為1。

二分查找,難點在numbers[mid] > numbers[high]時為什么時mid+1,因為此時可以確定numbers[mid]不是最小

class Solution {
public:
    int minArray(vector<int>& numbers) {
        
        int low = 0;
        int high = numbers.size()-1;

        while(low < high) {

            int mid = (low + high) / 2;

            if(numbers[mid] > numbers[high]) {
                low = mid + 1;
            } else if (numbers[mid] < numbers[high]) {
                high = mid;
            } else {
                high -=1;
            }
        }

        return numbers[low];
    }
};

劍指 Offer 12. 矩陣中的路徑

請設計一個函數,用來判斷在一個矩陣中是否存在一條包含某字符串所有字符的路徑。路徑可以從矩陣中的任意一格開始,每一步可以在矩陣中向左、右、上、下移動一格。如果一條路徑經過了矩陣的某一格,那么該路徑不能再次進入該格子。例如,在下面的3×4的矩陣中包含一條字符串“bfce”的路徑(路徑中的字母用加粗標出)。

[["a","b","c","e"],
["s","f","c","s"],
["a","d","e","e"]]

但矩陣中不包含字符串“abfb”的路徑,因為字符串的第一個字符b占據了矩陣中的第一行第二個格子之后,路徑不能再次進入這個格子。

暴力回溯,每個節點都往四個方向走,完全匹配最后返回true,遞歸層層傳遞 用 ‘ ’標記走過的節點來標記走過的節點,遞歸結束后再恢復

class Solution {
public:
    bool exist(vector<vector<char>>& board, string word) {

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

            vector<char>row = board[i];
            
            for(int j = 0; j < row.size();j++) {
                
                if(dfs(board, word, i,j,0)) {
                    return true;
                }
            }
        }

        return false;
    }

    bool dfs(vector<vector<char>>& board, string word, int i, int j, int k) {

        if(i >= board.size() || j >= board[0].size() || board[i][j] != word[k]) {
            return false;
        }

        if (k == word.size()-1){
            return true;
        }

        board[i][j] = ' ';

        bool vaild = dfs(board, word, i,j+1,k+1)  || dfs(board, word, i,j-1,k+1)  || dfs(board, word, i-1,j,k+1)  || dfs(board, word, i+1,j,k+1) ;

        board[i][j] = word[k];

        return vaild;
    }
};

劍指 Offer 13. 機器人的運動范圍

地上有一個m行n列的方格,從坐標 [0,0] 到坐標 [m-1,n-1] 。一個機器人從坐標 [0, 0] 的格子開始移動,它每次可以向左、右、上、下移動一格(不能移動到方格外),也不能進入行坐標和列坐標的數位之和大于k的格子。例如,當k為18時,機器人能夠進入方格 [35, 37] ,因為3+5+3+7=18。但它不能進入方格 [35, 38],因為3+5+3+8=19。請問該機器人能夠到達多少個格子?
1.dfs 2.機器人只能往右走 3.visited??走過沒有

class Solution {
public:

    int movingCount(int m, int n, int k) {
        vector<vector<bool>> visited(m, vector<bool>(n, 0));

        return dfs(m,n,0,0,k,visited);
    }

    int sum(int n) {

        int sum = 0;
        while(n) {

            sum+= n %10;
            n /= 10;
        }
        return sum;
    }

    int dfs(int m, int n,int i, int j, int k, vector<vector<bool>> &visited) {

        if(i >=m || j >= n || (sum(i) + sum(j)) > k || visited[i][j]) {
            return 0;
        }

        visited[i][j] = true;

        return 1 + dfs(m,n,i+1, j, k, visited) + dfs(m,n,i,j+1,k,visited);
    }
};

劍指 Offer 14- I. 剪繩子

給你一根長度為 n 的繩子,請把繩子剪成整數長度的 m 段(m、n都是整數,n>1并且m>1),每段繩子的長度記為 k[0],k[1]...k[m-1] 。請問 k[0]k[1]...*k[m-1] 可能的最大乘積是多少?例如,當繩子的長度是8時,我們把它剪成長度分別為2、3、3的三段,此時得到的最大乘積是18。
(i-j) * j是對應3,5的情況 ,5的時候切兩段更好,同時,這個問題可以轉化為完全背包問題,轉為物品1,2,3...n在最大載重n下,乘積最多是多少
https://leetcode-cn.com/problems/jian-sheng-zi-lcof/solution/xu-lie-xing-dong-tai-gui-hua-by-muyids-2/

class Solution {
public:
    int cuttingRope(int n) {

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

            for(int j = 1; j < i;j++) {
                dp[i] = max(dp[i], max(dp[i-j] *j, (i-j) * j));
            }
        }

        return dp[n];
    }

};

劍指 Offer 16. 數值的整數次方

實現函數double Power(double base, int exponent),求base的exponent次方。不得使用庫函數,同時不需要考慮大數問題。
快速冪,其實是個數學問題,如x的10次方可以寫出 x的(1010)b次方

class Solution {
public:
    double myPow(double x, int n) {
    
    if(n == 0) {
        return 1;
    }

    if(x == 0) {
        return 0;
    }
    long b = n;

    double res = 1;

    if(n < 0) {
        b = -b;
        x = 1 / x;
    }

    while(b > 0) {

        if (b & 1) {
            res *= x;
            
        }
        x = x*x;
        b = b >> 1;
    }
    return res;
    }
};

劍指 Offer 19. 正則表達式匹配

請實現一個函數用來匹配包含'. '和''的正則表達式。模式中的字符'.'表示任意一個字符,而''表示它前面的字符可以出現任意次(含0次)。在本題中,匹配是指字符串的所有字符匹配整個模式。例如,字符串"aaa"與模式"a.a"和"abaca"匹配,但與"aa.a"和"ab*a"均不匹配。

這題狀態方程

p[j] == *
dp[i][j] = dp[i][j-2],拋棄和p[j-1]
dp[i][j] != dp[i][j] | dp[i-1][j] ,baaaa和ba
匹配這種情況,
2.p[j] != * ,dp[i][j] = s[i] == p[j] || p[j] == .

然后把dp數組輸出化為多一個長度,便于處理空字符串情況,例如, ""和""匹配, ""和a*匹配,都應該為true

class Solution {
public:
    bool isMatch(string s, string p) {

        int sLength = s.size();
        int pLength = p.size();

        vector<vector<bool>> dp(sLength+1, vector<bool>(pLength+1, false));

        dp[0][0] = true;

        for(int i = 0; i <= sLength ; i++) {

            for(int j = 1; j <= pLength; j++ ) {


                if (p[j-1] == '*') {

                    if(j>=2) {
                        dp[i][j] = dp[i][j-2];
                    }
                    
                    if(i>=1 && j>=2 && (s[i-1] == p[j-2] || p[j-2] == '.')) {
                        dp[i][j] = dp[i-1][j] | dp[i][j];
                    }
                    

                } else {
                    if(i>0&&(s[i-1] == p[j-1] || p[j-1] == '.')) {
                        if(i >= 1 && j >=1) {
                            dp[i][j] = dp[i-1][j-1];
                        }
                        
                    } else {
                        dp[i][j] = false;
                    }
                }

            }
        }

        return dp[sLength][pLength];
    }
};

劍指 Offer 31. 棧的壓入、彈出序列

輸入兩個整數序列,第一個序列表示棧的壓入順序,請判斷第二個序列是否為該棧的彈出順序。假設壓入棧的所有數字均不相等。例如,序列 {1,2,3,4,5} 是某棧的壓棧序列,序列 {4,5,3,2,1} 是該壓棧序列對應的一個彈出序列,但 {4,3,5,1,2} 就不可能是該壓棧序列的彈出序列。

先遍歷押入順序,放進輔助棧,遍歷過程再循環輔助棧的棧頂與當前彈出位置是否一致,一致的話彈出到不一致為止

class Solution {
public:
    bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {

        stack<int>tempStack;

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

            int temp = pushed[i];
            tempStack.push(temp);

            while(!tempStack.empty() && tempStack.top() == popped[popIndex]) {
                tempStack.pop();
                popIndex++;
            }
        }

        return tempStack.empty();
    }
};

劍指 Offer 36. 二叉搜索樹與雙向鏈表

輸入一棵二叉搜索樹,將該二叉搜索樹轉換成一個排序的循環雙向鏈表。要求不能創建任何新的節點,只能調整樹中節點指針的指向。

先思考中序遍歷打印的調用順序,然后用全局變量來存儲上一個打印的節點,根據這思路去連接當前節點和上一個節點

class Solution {
public:

    Node *pre;
    Node *head;
    Node* treeToDoublyList(Node* root) {
        if(!root) {
            return root;
        }
        dfs(root);
        pre->right = head;
        head->left = pre;

        return head;
    }

    void dfs(Node *cur) {

        if(!cur) {
            return;
        }

        dfs(cur->left);

        if(pre != NULL) {
            pre->right = cur;
        } else {
            head = cur;
        }

        cur->left = pre;
        pre = cur;

        dfs(cur->right);
    }
};

劍指 Offer 38. 字符串的排列

輸入一個字符串,打印出該字符串中字符的所有排列。

你可以以任意順序返回這個字符串數組,但里面不能有重復元素。

示例:

輸入:s = "abc"
輸出:["abc","acb","bac","bca","cab","cba"]
1.固定第一個字符,然后遍歷dfs 2.重復字符用set來區分,然后跳過 3.遞歸前后注意恢復字符串

class Solution {
public:

    vector<string>result;

    vector<string> permutation(string s) {
        if(s.size() == 0) {
            return result;
        }
        recur(s, 0);
        return result;
    }

    void recur(string s, int head) {

        if(head == s.size()-1) {
            result.push_back(s);
            return;
        }

        unordered_set<int>set;
        for(int i = head;i < s.size();i++) {

            if(set.find(s[i]) != set.end()) {
                continue;
            }
            set.insert(s[i]);
            swap(s[i],s[head]);
            recur(s, head+1);
            swap(s[head],s[i]);
        }
    }

};

劍指 Offer 40. 最小的k個數

注意,遞歸結束調節不能是l>=r,是l>r,不同于快排,等于的時候也要讓她走下去,因為當遞歸剩下到一個數時,也是要進結果的

class Solution {
public:

    vector<int>result;

    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        quickSort(arr,k, 0, arr.size()-1);
        return result;
    }

    void quickSort(vector<int>& arr, int k, int l, int r) {

        if(l > r) {
            return;
        }
        int pov = arr[l];
        int i = l;
        int j = r;

        while(i < j) {
            while(arr[j] >= pov && i < j) {
                j--;
            }

            while(arr[i] <= pov && i < j) {
                i++;
            }

            swap(arr[i], arr[j]);
        }

        swap(arr[l], arr[i]);


        

        int length = i - l + 1;

        if(length == k) {
            
            for(int a = l; a <= i;a++) {
                result.push_back(arr[a]);
            }
        } else if(k < length) {
            quickSort(arr, k, l, i-1);
        } else {
            for(int a = l; a <= i;a++) {
                result.push_back(arr[a]);
            }
            quickSort(arr, k - length, i + 1, r);
        }


    }
};

劍指 Offer 41. 數據流中的中位數

如何得到一個數據流中的中位數?如果從數據流中讀出奇數個數值,那么中位數就是所有數值排序之后位于中間的數值。如果從數據流中讀出偶數個數值,那么中位數就是所有數值排序之后中間兩個數的平均值。
用最大堆和最小堆,最大堆頂為最大值,最小堆堆頂為最小值
C++里最大堆: priority_queue<int, vector<int>, less<int>> maxHeap;
C++里最小堆: priority_queue<int, vector<int>, greater<int>> minHeap;

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);
            maxHeap.push(minHeap.top());
            minHeap.pop();
        } else {
            maxHeap.push(num);
            minHeap.push(maxHeap.top());
            maxHeap.pop();
        }
    }
    
    double findMedian() {
        return maxHeap.size() == minHeap.size() ? (minHeap.top()+maxHeap.top()) / 2.0 : maxHeap.top();
    }
};

劍指 Offer 43. 1~n 整數中 1 出現的次數

輸入一個整數 n ,求1~n這n個整數的十進制表示中1出現的次數。

例如,輸入12,1~12這些整數中包含1 的數字有1、10、11和12,1一共出現了5次。
1. 固定一個位置,當這個位置為0,1或者其他這三種情況,這個位置能為1的次數 ,類似于滾動密碼鎖,注意,當當前位置為0時,高位變小,此位置就能為1了!,比如 2302,固定十位為0時,2212,十位還是有一,注意這點

class Solution {
public:
    int countDigitOne(int n) {
        
        int digit = 1;
        int hight = n / 10;
        
        int cur = n % 10;
        int low = 0;
        int res = 0;
        while(hight != 0 || cur != 0) {

            if(cur == 0) {
                res += hight * digit;
            } else if (cur == 1) {
                res += hight * digit + low + 1;
            } else {
                res += hight *digit + digit;
            }

            
            low = cur * digit + low;

            cur = hight % 10;

            digit *= 10;
            hight = hight / 10;
        }

        return res;
    }
};

劍指 Offer 45. 把數組排成最小的數

輸入一個非負整數數組,把數組里所有數字拼接起來排成一個數,打印能拼接出的所有數字中最小的一個。
輸入: [10,2]
輸出: "102"
這題實際上是個排序題x + y > y + x, x > y

class Solution {
public:
    string minNumber(vector<int>& nums) {

        vector<string>numStrings;
        for(auto num : nums) {
            numStrings.push_back(to_string(num));
        }

        quickSort(numStrings, 0, numStrings.size() - 1);
        string res;
        for(string s : numStrings)
            res.append(s);
        return res;

    }

    void quickSort(vector<string>& nums, int l, int r) {
        if(l >= r) return;

        int i = l;
        int j = r;
        string p = nums[l];
        while(i < j) {
            while(nums[j] + p >= p + nums[j] && i < j) j--;
            while(nums[i] + p <= p + nums[i] && i < j) i++;  
            swap(nums[i],nums[j]);
        }

        swap(nums[i],nums[l]);

        quickSort(nums, l, i-1);
        quickSort(nums, i+1, r);
    }
};

劍指 Offer 47. 禮物的最大價值

在一個 m*n 的棋盤的每一格都放有一個禮物,每個禮物都有一定的價值(價值大于 0)。你可以從棋盤的左上角開始拿格子里的禮物,并每次向右或者向下移動一格、直到到達棋盤的右下角。給定一個棋盤及其上面的禮物的價值,請計算你最多能拿到多少價值的禮物?
不能用dfs!會超時,用dp就好了dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + a[i][j]

class Solution {
public:

    int maxVale = 0;

    int maxValue(vector<vector<int>>& grid) {
        
        if(grid.size() == 0) {
            return 0;
        }
        int row = grid.size();
        int colum = grid[0].size();

        vector<vector<int>>dp(row, vector<int>(colum, 0));

        dp[0][0] = grid[0][0];

        for(int i = 0; i < row;i++) {

            for(int j = 0; j < colum;j++ ) {

                if(i == 0) {
                    if(j >= 1) {
                        dp[i][j] = dp[i][j-1] + grid[i][j];
                    }
                } else if (j == 0) {
                    if(i >= 1) {
                        dp[i][j] = dp[i-1][j] + grid[i][j];
                    }
                } else {
                    dp[i][j] = max(dp[i-1][j] , dp[i][j-1]) + grid[i][j];
                }
            }
        }

        return dp[row-1][colum-1];
    }


};

劍指 Offer 48. 最長不含重復字符的子字符串

請從字符串中找出一個最長的不包含重復字符的子字符串,計算該最長子字符串的長度。

示例 1:

輸入: "abcabcbb"
輸出: 3
解釋: 因為無重復字符的最長子串是 "abc",所以其長度為 3。
也是用dp dp[j] = i - j, i-j<=dp[j-1],i為j左邊第一個相同的字母 dp[j] = dp[j-1] + 1, i-j>dp[j-1]

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        if(s.size() == 0) {
            return 0;
        }
        vector<int>dp(s.size(), 0);
        dp[0] = 1;

        for(int j = 1;j < s.size();j++) {

            int i = j - 1;
            bool same = false;
            for(; i >= 0;i--) {
                if(s[i] == s[j]) {
                    same = true;
                    break;
                }
            }

            if(j - i <= dp[j-1]) {
                dp[j] = j - i;
            } else {
                dp[j] = dp[j-1] + 1;
            }
         }
        
        int maxL = 0;
        for(auto num : dp) {
            maxL = max(num, maxL);
        }
        return maxL;
    }
};

劍指 Offer 49. 丑數

我們把只包含質因子 2、3 和 5 的數稱作丑數(Ugly Number)。求按從小到大的順序的第 n 個丑數。

示例:

輸入: n = 10
輸出: 12
解釋: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 個丑數。
一個丑數肯定是由前面任意個數乘 2,3,5得出來的

class Solution {
public:
    int nthUglyNumber(int n) {
        if(n == 0) {
            return 0;
        }
        int a = 0;
        int b = 0;
        int c = 0;
        vector<int>dp(n, 0);
        dp[0] = 1;
        for(int i = 1; i <n;i++) {

            dp[i] = min(dp[a] * 2, min(dp[b] * 3, dp[c] * 5));

            if(dp[i] == dp[a] * 2) a++;
            if(dp[i] == dp[b] * 3) b++;
            if(dp[i] == dp[c] * 5) c++;
        }

        return dp[n-1];
    }
};

劍指 Offer 51. 數組中的逆序對

在數組中的兩個數字,如果前面一個數字大于后面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個數組中的逆序對的總數。

示例 1:

輸入: [7,5,6,4]
輸出: 5

分治,歸并排序,先找子序列的逆序對數,再遞歸回來找父序列的逆序對,這樣找是對的。

class Solution {
public:

    int sum;

    int reversePairs(vector<int>& nums) {

        vector<int>temp(nums.size(),0);
        mergeSort(nums,0, nums.size()-1,temp);
        return sum;
    }

    void mergeSort(vector<int>& nums, int l, int r, vector<int>&temp) {

        if(l>=r) {
            return;
        }
        
        int mid = (l + r) / 2;

        mergeSort(nums, l, mid, temp);
        mergeSort(nums, mid+1, r, temp);

        vector<int>seq;
        int i = l;
        int j = mid+1;

        for(int k=l;k<=r;k++){
            temp[k] = nums[k];
        }

        for(int k=l;k<=r;k++) {

            if(i == mid+1) {
                nums[k] = temp[j];
                j++;
            } else if (j == r+1) {
                nums[k] = temp[i];
                i++;
            } else if(temp[i] <= temp[j]) {
                nums[k] = temp[i];
                i++;
            } else {
                sum += mid-i+1;
                nums[k] = temp[j];
                j++;
            }
        }

    }
};

劍指 Offer 52. 兩個鏈表的第一個公共節點

a鏈a節點走完再走 b鏈,b鏈b節點走完再走a鏈,相等時即相遇

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        
        ListNode *a = headA;
        ListNode *b = headB;

        while(a != b) {
            a = a == NULL ? headB : a->next;
            b = b == NULL ? headA : b->next;
        }

        return a;
    }
};

劍指 Offer 54. 二叉搜索樹的第k大節點

給定一棵二叉搜索樹,請找出其中第k大的節點。

示例 1:

輸入: root = [3,1,4,null,2], k = 1
3
/
1 4

2
輸出: 4
中序遍歷改良,先遍歷右邊就是從大到小

class Solution {
public:

    int idx = 0;
    int v = 0;
    int kthLargest(TreeNode* root, int k) {
        travel(root, k);
        return v;
    }

    void travel(TreeNode* root, int k) {

        if(root == NULL) {
            return;
        }
        
        travel(root->right,k);
        idx++;
        if(idx == k) {
            v = root->val;
            return;
        }
        travel(root->left,k);
        
    }
};

劍指 Offer 55 - II. 平衡二叉樹

輸入一棵二叉樹的根節點,判斷該樹是不是平衡二叉樹。如果某二叉樹中任意節點的左右子樹的深度相差不超過1,那么它就是一棵平衡二叉樹。

后續遍歷,得到左右兩個子節點的高度,比較,大于2返回-1,注意,子節點dfs后如果得到-1,需要特別判斷然后返回-1

class Solution {
public:
    bool isBalanced(TreeNode* root) {

        return dfs(root) != -1;
    }

    int dfs(TreeNode* root) {
        
        if(!root){
            return 0;
        }
        int left = dfs(root->left);
        if(left == -1) return -1;
        int right = dfs(root->right);
        if(right==-1) return -1;

        return (abs(left - right) >= 2 ? -1: ((max(left, right)) + 1));
    }
};

劍指 Offer 56 - I. 數組中數字出現的次數

一個整型數組 nums 里除兩個數字之外,其他數字都出現了兩次。請寫程序找出這兩個只出現一次的數字。要求時間復雜度是O(n),空間復雜度是O(1)。

1.兩個相同的數抑或等于 0
2. 0和1抑或等于1,用這個性質來分組
下面可以更簡單
https://leetcode-cn.com/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof/solution/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-by-leetcode/

class Solution {
public:
    vector<int> singleNumbers(vector<int>& nums) {
        
        int ret = 0;
        for(auto num: nums) {
            ret ^= num;
        }

        int n = 0;
        while(ret > 0) {
            if(ret & 1) {
                break;
            }
            ret = ret >> 1;
            n++;
        }

        vector<int>first;
        vector<int>second;

        for(auto num: nums) {
            if((num >> n) & 1) {
                first.push_back(num);
            } else {
                second.push_back(num);
            }
        }

        int firstRet = 0;
        int secondRet = 0;
        
        for(auto num: first) {
            firstRet ^= num;
        }

        for(auto num: second) {
            secondRet ^= num;
        }

        vector<int>r;
        r.push_back(firstRet);
        r.push_back(secondRet);

        return r;

    }
};

劍指 Offer 56 - II. 數組中數字出現的次數 II

在一個數組 nums 中除一個數字只出現一次之外,其他數字都出現了三次。請找出那個只出現一次的數字。
1. 出現三次的數,他們二進制某一位為1時,加起來這些1肯定是3的倍數,如不不是,就是那個出現一次的數造成的

class Solution {
public:
    int singleNumber(vector<int>& nums) {

        vector<long>bitSums(32, 0);

        for(long i = 0;i < nums.size();i++) {

            long num = nums[i];
            long mask = 1;
            long bit = 0;
            while(bit < 32) {
                if(mask & num) {
                    bitSums[bit] += 1;
                }
                mask = mask << 1;
                bit ++;
            }
        }

        long res = 0;
        long mask = 1;
        for(long i = 0 ;i < bitSums.size();i++) {

            long sum = bitSums[i];
            if(sum %3 != 0) {
                res |= mask;
            }
            mask = mask << 1;
        }

        return res;

    }
};

劍指 Offer 57. 和為s的兩個數字

輸入一個遞增排序的數組和一個數字s,在數組中查找兩個數,使得它們的和正好是s。如果有多對數字的和等于s,則輸出任意一對即可。
頭尾雙指針,頭尾之和與目標s比較,小于s,頭前進,大于s,尾前進

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        int l = 0;
        int r = nums.size() - 1;

        while(l<r) {

            int sum = nums[l] + nums[r];
            if(sum > target) {
                r--;
            } else if(sum < target) {
                l++;
            } else {
                return vector<int>{nums[l], nums[r]};
            }
        }

        return vector<int>();
    }
};

劍指 Offer 57 - II. 和為s的連續正數序列

輸入一個正整數 target ,輸出所有和為 target 的連續正整數序列(至少含有兩個數)。

序列內的數字由小到大排列,不同序列按照首個數字從小到大排列。
滑動窗口,當sum大于target,left++, sum小于target,right++,等于的時候left++,整個窗口往右邊移動

class Solution {
public:
    vector<vector<int>> findContinuousSequence(int target) {
        
        int l = 1;
        int r = 1;
        
        int sum = 0;

        vector<vector<int>>result;

        while(l <= target / 2) {

            if(sum < target) {
                sum+=r;
                r++;
            } else if(sum > target) {
                sum-=l;
                l++;
            } else {
                vector<int>temp;
                for(int i = l;i < r;i++) {
                    temp.push_back(i);
                }

                result.push_back(temp);
                sum-=l;
                l++;
            }

        }

        return result;
    }
};

輸入一個英文句子,翻轉句子中單詞的順序,但單詞內字符的順序不變。為簡單起見,標點符號和普通字母一樣處理。例如輸入字符串"I am a student. ",則輸出"student. a am I"。

中間多余空格消除是難點,判斷前后都為空格時,刪除當前位置的空格

class Solution {
public:
    string reverseWords(string s) {
        trim(s);
        int i = s.length() - 1;
        int j = s.length() - 1;
        string result;
        reverse(s,0, s.length() - 1);
        while(i >= 0) {

            while(i>=0&&s[i] != ' ') {
                char a = s[i];
                i--;
            }
            reverse(s, i+1,j);

            while(i>=0&&s[i]==' ') {
                i--;
                //消除中間多余空格
                if(s[i+1] == ' ' && s[i] == ' ') {
                    s.erase(i, 1);
                }
            }

            j = i;
        }

        return s;
    }

    void reverse(string &s, int l, int r) {

        if(l < 0) {
            return;
        }
        if(s.size() == 0 || l >= r) {
            return;
        }
        int i = l;
        int j = r;
        while(i < j) {
            swap(s[i], s[j]);
            i++;
            j--;
        }
    }

    void trim(string &s) {

        while(s.length() > 0 && s[0] == ' ') {
            s.erase(0, 1);
        }
        while(s.length() > 0 && s[s.size()-1] == ' ') {
            s.erase(s.size()-1, 1);
        }
    }
};

劍指 Offer 59 - I. 滑動窗口的最大值

給定一個數組 nums 和滑動窗口的大小 k,請找出所有滑動窗口里的最大值。

示例:

輸入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
輸出: [3,3,5,5,6,7]

滑動窗口,用雙端隊列,當窗口右移,1.判斷隊列頭部與i-1位置的大小,相等這干掉隊列頭部的最大值 2.n[j]與隊列所有比較,小于(不能等于)它的將被干掉 3.最后把n[j]加到末尾

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {

        int i = 1-k;
        int j = 0;

        deque<int>q;
        vector<int>res;
        for (;i <=0;) {
            int num = nums[j];
            while(!q.empty() && num >= q.back()) {
                q.pop_back();
            }

            q.push_back(num);
            i++;
            j++;
        }
        
        if(!q.empty()) {
            res.push_back(q.front());
        }

        while(j < nums.size()) {
            int num = nums[j];
            if(nums[i-1] == q.front()) {
                q.pop_front();
            }

            while(!q.empty() && num > q.back()) {
                q.pop_back();
            }
            q.push_back(num);
            res.push_back(q.front());

            i++;
            j++;
        }

        return res;
        
    }
};

劍指 Offer 59 - II. 隊列的最大值

請定義一個隊列并實現函數 max_value 得到隊列里的最大值,要求函數max_value、push_back 和 pop_front 的均攤時間復雜度都是O(1)。

若隊列為空,pop_front 和 max_value 需要返回 -1

難點是用雙端隊列去存最大值

class MaxQueue {
public:

    queue<int>q;
    deque<int>q2;

    MaxQueue() {

    }
    
    int max_value() {
        if(!q2.empty()) {
            return q2.front();
        }
        return -1;
    }
    
    void push_back(int value) {
        q.push(value);
        while(!q2.empty() && q2.back()<value) {
            q2.pop_back();
        }

        q2.push_back(value);
    }
    
    int pop_front() {
        if(q2.empty()) {
            return -1;
        }
        int v = q.front();
        q.pop();
        
        while(!q2.empty() && q2.front() == v) {
            q2.pop_front();
        }

        return v;
    }
};

/**
 * 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 68 - I. 二叉搜索樹的最近公共祖先

其實就是二分搜索,比pq小就遞歸left,比pq大就遞歸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 == NULL) {
            return NULL;
        }
        if(p->val < root->val && q->val < root->val) {
            return lowestCommonAncestor(root->left, p, q);
        } else if (p->val > root->val && q->val > root->val) {
            return lowestCommonAncestor(root->right, p, q);
        }

        return root;
    }
};

第k大

注意是第k大,不是第k小

class Solution {
public:
    int findKth(vector<int> a, int n, int K) {
        quicksort(a, K, 0, n-1);
        return kValue;
    }
    
    int kValue = 0;
    
    int partition(vector<int> &a, int l, int r) {
        int randIndex = rand() % (r - l + 1) + l;
        swap(a[randIndex], a[l]);
        
        int qvot = a[l];
        int i = l;
        int j = r;
        
        while(i < j) {
            
            while(a[j] >= qvot && i < j) j--;
            while(a[i] <= qvot && i < j) i++;
            swap(a[i], a[j]);
        }
        
        swap(a[i], a[l]);
        
        return i;
    }
    
    void quicksort(vector<int> &a, int K, int l, int r) {
        
        if(l > r) {
            return;
        }
        
        int i = partition(a, l, r);
        
        int kIndex = r - K + 1;
        if(kIndex == i) {
            kValue = a[i];
        } else if (kIndex < i) {
            quicksort(a, i - kIndex, l, i-1);
        } else {
            quicksort(a,K, i+1, r);
        }
       
        
    }
};

43. 字符串相乘

讓乘數短一點,可以減少相乘的次數,降低時間復雜度
result[i+j] = t[i] * t[j];

class Solution {
public:
    /**
     * 代碼中的類名、方法名、參數名已經指定,請勿修改,直接返回方法規定的值即可
     * 
     * @param s string字符串 第一個整數
     * @param t string字符串 第二個整數
     * @return string字符串
     */
    string solve(string s, string t) {
        // write code here
        
        if(t.size() > s.size()){
            swap(s,t);
        }
        
        int sLength = s.size();
        int tLength = t.size();
        
        int resultLength = sLength + tLength;
        
        vector<int> result(resultLength, 0);
        
        for(int i = tLength-1; i >= 0;i--) {
            
            int temp = t[i] - '0';
            
            for(int j = 0; j < s.size();j++) {
                
                result[i+j+1] += temp * (s[j] - '0');
            }
        }
        
        int d = 0;
        for(int i = resultLength - 1; i >= 0;i--) {
            int value = result[i] + d;
            d = value / 10;
            result[i] = value % 10;
        }
        
        string strResult = "";
        
        int index = result[0] == 0 ? 1 : 0;
        
        while(index < resultLength) {
            strResult += (result[index] + '0');
            index++;
        }
         
        return strResult;
    }
};

面試題 16.25. LRU 緩存

雙鏈表,加哈希表,被操作就把節點挪到前面,超出緩存數目就刪除尾部的節點

struct DLinkNode {
    int key;
    int value;
    DLinkNode *next;
    DLinkNode *pre;
};


class LRUCache {
public:

    unordered_map<int, DLinkNode*>cache;
    DLinkNode *head = nullptr;
    DLinkNode *tail = nullptr;
    
    int capacity_ = 0;
    int size = 0;

    LRUCache(int capacity) {
        capacity_ = capacity;
        
        head = (DLinkNode *)malloc(sizeof(DLinkNode));
        tail = (DLinkNode *)malloc(sizeof(DLinkNode));
        
        tail->next = head;
        head->pre = tail;
    }
    
    int get(int key) {
        
        DLinkNode *node = cache[key];
        if(node == nullptr) {
            return -1;
        }
        
        moveToHead(node);
        
        return node->value;
    }
    
    void put(int key, int value) {
        DLinkNode *node = cache[key];
        if(node) {
            node->value = value;
            moveToHead(node);
        } else {
            
            if(size == capacity_) {
                DLinkNode *node = removeTail();
                cache.erase(node->key);
                free(node);
                size--;
            }
            
            size++;
            
            DLinkNode *node = pushHead(key, value);
            cache[key] = node;
        }
    }

        DLinkNode* removeTail() {
        

        DLinkNode *node = tail->next;
        removeNode(node);Z
        return node;
    }
    
    DLinkNode* pushHead(int key, int value) {
        
        DLinkNode *node = (DLinkNode *)malloc(sizeof(DLinkNode));
        node->key = key;
        node->value = value;
        addToHead(node);
        
        return node;
    }
    
    void removeNode(DLinkNode *node){
        node->pre->next = node->next;
        node->next->pre = node->pre;
    }
    
    void addToHead(DLinkNode *node) {
        node->pre = head->pre;
        head->pre->next = node;
        head->pre = node;
        node->next = head;
    }
    
    void moveToHead(DLinkNode *node) {
        
        removeNode(node);
        addToHead(node);
    }


    

 
};

/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache* obj = new LRUCache(capacity);
 * int param_1 = obj->get(key);
 * obj->put(key,value);
 */

最長公共子串

dp[i][j] = dp[i-1][j-1] + 1 s1[i] ==s2[j]
dp[i][j] = 0 s1[i] !=s2[j]

class Solution {
public:
    /**
     * longest common substring
     * @param str1 string字符串 the string
     * @param str2 string字符串 the string
     * @return string字符串
     */
    string LCS(string str1, string str2) {
        // write code here
        int m = str1.length();
        int n = str2.length();
        
        int dp[m+1][n+1];
        
        for(int i = 0; i < m;i++) {
            dp[i][0] = 0;
        }
        
        for(int i = 0; i < n;i++) {
            dp[0][i] = 0;
        }
        
        int max = 0;
        int end = 0;
        
        for(int i = 1; i <= m;i++) {
            
            for(int j = 1;j <= n;j++) {
                
                if(str1[i-1] == str2[j-1]) {
                    dp[i][j] = dp[i-1][j-1] + 1;
                } else {
                    dp[i][j] = 0;
                }
                
                if(dp[i][j] > max) {
                    end = i-1;
                    max = dp[i][j];
                }
            }
        }
        
        if(max == 0) return "-1";
        
        return str1.substr(end - max + 1, max);
        
    }
};
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,837評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,196評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,688評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,654評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,456評論 6 406
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,955評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,044評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,195評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,725評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,608評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,802評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,318評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,048評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,422評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,673評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,424評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,762評論 2 372

推薦閱讀更多精彩內容