leetcode 精选面试题1 c++

文章目录

73. 矩阵置零

题目要求原地修改,故使用数组第一行第一列作为标记位,bool flag用来判断标记位是否需要全部置零。
依次遍历除标记位外数组,如果=0在标记位记录。
通过标记位对二维数组置零,最后处理标记位
时间复杂度:O(mn)

class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {
        int m = matrix.size(), n = matrix[0].size();
        bool row_flag = false, column_flag = false;
        for (int i = 0; i < m; ++i) {
            if (matrix[i][0] == 0)  {
                column_flag = true;
                break;
            }
        }
        for (int i = 0; i < n; ++i) {
            if (matrix[0][i] == 0) {
                row_flag = true;
                break;
            }
        }

        for (int i = 1; i < m; ++i) {
            for (int j = 1; j < n; ++j) {
                if (matrix[i][j] == 0) {
                    matrix[i][0] = matrix[0][j] = 0;
                }
            }
        }
        for (int i = 1; i < m; ++i) {
            for (int j = 1; j < n; ++j) {
                if (matrix[i][0] == 0 || matrix[0][j] == 0) {
                    matrix[i][j] = 0;
                }
            }
        }
        if (row_flag) {
            for (int i = 0; i < n; ++i) {
                matrix[0][i] = 0;
            }
        }
        if (column_flag) {
            for (int i = 0; i < m; ++i) {
                matrix[i][0] = 0;
            }
        }
    }
};
202. 快乐数

Hash记录冲突

set.find() != set.end()证明环路,return false

res == 1 return true

class Solution {
public:
    set<int> st;
    bool Square(int n) {
        int res = 0;
        while (n) {
            int temp = n % 10;
            n /= 10;
            res += pow(temp, 2);
        }
        if (st.find(res) != st.end()) {
            return false;
        }
        st.insert(res);
        return res == 1 || Square(res);
    }
    bool isHappy(int n) {
        // Hash
        return Square(n);
    }
};

龟兔赛跑问题

快慢指针,相交证明成环 return false

fast == 1 return true

class Solution {
public:
    int Square(int n) {
        int res = 0;
        while (n) {
            int temp = n % 10;
            n /= 10;
            res += pow(temp, 2);
        }
        return res;
    }
    bool isHappy(int n) {
        // slow fast point
        int slow = n, fast = Square(n);
        if (fast == 1)  return true;
        while (slow != fast) {
            slow = Square(slow);
            fast = Square(fast);
            if (fast == 1)  return true;
            fast = Square(fast);
            if (fast == 1)  return true;
        }
        return false;
    }
};
剑指 Offer 04. 二维数组中的查找

从右上角开始遍历,matrix[i][j] == target直接退出

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        int m = matrix.size(), n = matrix[0].size();
        int i = 0, j = n - 1;
        while (i < m && j >= 0) {
            if (matrix[i][j] < target) {
                ++i;
            } else if (matrix[i][j] > target) {
                --j;
            } else {
                return true;
            }
        }
        return false;
    }
};
131. 分割回文串

动态规划dp标记i到j是否为回文子串
回溯,类似全排列组合过程

class Solution {
public:
    vector<vector<string>> res;
    vector<vector<int>> dp;
    vector<string> str;

    void dfs(const string& s, int idx) {
        if (idx == s.length()) {
            res.push_back(str);
            return;
        }
        for (int i = idx; i < s.length(); ++i) {
            if (dp[idx][i]) {
                str.push_back(s.substr(idx, i - idx + 1));
                dfs(s, i + 1);
                str.pop_back();
            }
        }
    }

    vector<vector<string>> partition(string s) {
        int len = s.length();
        dp.resize(len, vector<int>(len, 1));  
        for (int i = 1; i < len; ++i) {
            for (int j = 0; j < len - i; ++j) {
                if (i == 1) {
                    dp[j][j + i] = (s[j] == s[j + i] ? true : false);
                } else {
                    dp[j][j + i] = (s[j] == s[j + i] ? dp[j + 1][j + i - 1] : false);
                }
            }
        };
        dfs(s, 0);    
        return res;
    }
};
144. 二叉树的前序遍历 --迭代版

可以用迭代的方式实现方法一的递归函数,两种方式是等价的,区别在于递归的时候隐式地维护了一个栈,而在迭代的时候需要显式地将这个栈模拟出来。

/**
 * 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<int> preorderTraversal(TreeNode* root) {
        if (root == nullptr) {
            return {};
        }
        vector<int> res;
        stack<TreeNode*> sk;
        TreeNode* node = root;
        while (!sk.empty() || node != nullptr) {
            while (node != nullptr) {
                res.push_back(node->val);
                sk.push(node);
                node = node->left;
            }
            node = sk.top();
            sk.pop();
            node = node->right;
        }
        return res;

    }
};
347. 前 K 个高频元素
方法一:堆(这一部分解释搬运自leetcode官方题解)
思路与算法

首先遍历整个数组,并使用哈希表记录每个数字出现的次数,并形成一个「出现次数数组」。找出原数组的前 kk 个高频元素,就相当于找出「出现次数数组」的前 kk 大的值。

最简单的做法是给「出现次数数组」排序。但由于可能有 O(N)O(N) 个不同的出现次数(其中 NN 为原数组长度),故总的算法复杂度会达到 O(N\log N)O(NlogN),不满足题目的要求。

在这里,我们可以利用堆的思想:建立一个小顶堆,然后遍历「出现次数数组」:

如果堆的元素个数小于 kk,就可以直接插入堆中。
如果堆的元素个数等于 kk,则检查堆顶与当前出现次数的大小。如果堆顶更大,说明至少有 kk 个数字的出现次数比当前值大,故舍弃当前值;否则,就弹出堆顶,并将当前值插入堆中。
遍历完成后,堆中的元素就代表了「出现次数数组」中前 kk 大的值。
class Solution {
public:
    static bool cmp(pair<int, int>& a, pair<int, int>& b) {
        return a.second > b.second;
    }

    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int, int> mp;
        for (int& x : nums) {
            ++mp[x];
        }    

        priority_queue<pair<int,int>, vector<pair<int, int>>, decltype(&cmp)> q(cmp);
        for (auto& [first, second] : mp) {
            if (q.size() == k) {
                if (q.top().second < second) {
                    q.pop();
                    q.emplace(first, second);
                } 
            } else {
                q.emplace(first, second);
            }
        }

        vector<int> ret;
        while (!q.empty()) {
            ret.emplace_back(q.top().first);
            q.pop();
        }
        return ret;
    }
};

快排效率没有堆排高,而且最坏时间复杂度可达O(n),就当练手了

这里采用的是rand随机选取idx,随机快速选择排序,通常来说效果比普通的快排要好些

map的使用和上面一致,保存value+count

class Solution {
public:
    void quicksort(vector<int>& ret, vector<pair<int, int>> values, int l, int r, int k) {
        int rand_idx = rand() % (r - l + 1) + l;
        swap(values[rand_idx], values[r]);
        int index = l;
        int x = values[r].second;
        for (int i = l; i < r; ++i) {
            if (values[i].second <= x) {
                swap(values[i], values[index++]);
            }
        }
        swap(values[r], values[index]);

        if (k < r - index + 1) {
            quicksort(ret, values, index, r, k);
        } else {
            for (int i = r; i >= index; --i) {
                ret.push_back(values[i].first);
            }
            if (k - (r - index + 1) > 0)    quicksort(ret, values, l, index - 1, k - (r - index + 1));
        }
    }

    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int, int> mp;
        for (int& x : nums) {
            ++mp[x];
        }

        vector<pair<int, int>> values;
        for (auto& x : mp) {
            values.emplace_back(x);
        }
        vector<int> ret;
        quicksort(ret, values, 0, values.size() -1, k);
        return ret;
    }
};
977. 有序数组的平方

双指针

如果原地修改,目前有个思路是针对全负、全正、正负这三种情况分别处理。lt上看的题解时间复杂度都超O(n)

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        // two points
        int l = 0, r = nums.size() - 1, idx = r;
        vector<int> res(idx + 1);
        while (l <= r ) {
            if (abs(nums[l]) > abs(nums[r])) {
                res[idx--] = pow(nums[l], 2);
                ++l;
            } else {
                res[idx--] = pow(nums[r], 2);
                --r;
            }
        }
        return res;
    }
};
350. 两个数组的交集 II

两个hash表,空间开销大

class Solution {
public:
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
        unordered_map<int, int> mp1, mp2;
        vector<int> res;
        for (int& x : nums1) {
            ++mp1[x];
        }
        for (int& x : nums2) {
            ++mp2[x];
        }
        for (auto& [first, second] : mp1) {
            if (mp2.count(first)) {
                int count = min(mp2[first], mp1[first]);
                while (count--) {
                    res.push_back(first);
                }
            }
        }
        return res;
    }
};

map和set两种容器的底层结构都是红黑树,所以容器中不会出现相同的元素,因此count()的结果只能为0和1,可以以此来判断键值元素是否存在(当然也可以使用find()方法判断键值是否存在)。

class Solution {
public:
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
        if (nums1.size() > nums2.size()) {
            return intersect(nums2, nums1);
        }
        unordered_map<int, int> mp;
        for (int& x : nums1) {
            ++mp[x];
        }
        vector<int> res;
        for (int& x : nums2) {
            if (mp.count(x) && mp[x] > 0) {
                res.push_back(x);
                --mp[x];
            }
            // if (mp[x] == 0) {
            //     mp.erase(x);
            // }
        }
        return res;
    }
};

双指针,不用开辟hash空间

class Solution {
public:
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
        sort(nums1.begin(), nums1.end());
        sort(nums2.begin(), nums2.end());
        int idx1 = 0, idx2 = 0;
        vector<int> res;
        while (idx1 < nums1.size() && idx2 < nums2.size()) {
            while (idx1 < nums1.size() && idx2 < nums2.size() && nums1[idx1] == nums2[idx2]) {
                res.push_back(nums1[idx1]);
                ++idx1; ++idx2;
            } 
            while(idx1 < nums1.size() && idx2 < nums2.size() && nums1[idx1] < nums2[idx2]) {
                ++idx1;
            }
            while (idx1 < nums1.size() && idx2 < nums2.size() && nums1[idx1] > nums2[idx2]) {
                ++idx2;
            }
        }
        return res;
    }
};
279. 完全平方数

动态规划,记忆化搜索? 感觉都差不多,总之依次计算前n个数的完全平方数最小数量

时间复杂度O(n√n) 空间复杂度O(n)

class Solution {
public:
    int numSquares(int n) {
        // dp
        vector<int> dp(n + 1);
        for (int i = 1; i <= n; ++i) {
            int minn = INT_MAX;
            for (int j = 1; j * j <= i; ++j) {
                minn = min(minn, dp[i - j * j]);
            }
            dp[i] = minn + 1;
        }
        return dp[n];
    }
};
322. 零钱兑换

跟上一题相同

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        // 记忆化搜索
        sort(coins.begin(), coins.end());
        int len = coins.size();
        vector<int> dp(amount + 1);
        for (int i = 1; i <= amount; ++i) {
            int minn = INT_MAX;
            for (int j = 0; j < len && coins[j] <= i; ++j) {
                if (dp[i - coins[j]] != -1) {
                    minn = min(minn, dp[i - coins[j]]);
                }
            }
            if (minn == INT_MAX) {
                dp[i] = -1;
            } else {
                dp[i] = minn + 1;
            }
        }
        return dp[amount];
    }
};
class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        // dp
        int M =INT_MAX - 1;
        sort(coins.begin(), coins.end());
        int len = coins.size();
        vector<int> dp(amount + 1,  M);
        dp[0] = 0;
        for (int i = 1; i <= amount; ++i) {
            for (int j = 0; j < len && coins[j] <= i; ++j) {
                dp[i] = min(dp[i], dp[i - coins[j]] + 1);
            }
        }
        return dp[amount] == INT_MAX - 1 ? -1 : dp[amount];
    }
};
189. 轮转数组

注意k = k % nums.size()

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        // 智力题?
        int len = nums.size();
        k = k % len;
        reverse(nums.begin(), nums.begin() + len - k);
        reverse(nums.begin() + len - k, nums.end());
        reverse(nums.begin(), nums.end());
    }
};
74. 搜索二维矩阵

分别对行、列进行二分查找,返回第一个>=target的值。

如果不开辟空间的话,另写一个binarysearch()也是可以的

class Solution {
public:
    int binarySearch(vector<int>& nums, int l, int r, int target) {
        int pos = l;
        while (l <= r) {
            int mid = l + (r - l) / 2;
            if (nums[mid] < target) {
                l = mid + 1;
            } else {
                pos = mid;
                r = mid - 1;
            }
        }
        return pos;
    }
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        int m = matrix.size(), n = matrix[0].size();
        // 简单思路 一次对行,列进行二分查找
        vector<int>nums;
        for (int i = 0; i < m; ++i) {
            nums.emplace_back(matrix[i][n - 1]);
        }
        int row = binarySearch(nums, 0, m - 1, target);
        int column = binarySearch(matrix[row], 0, n - 1, target);
        return matrix[row][column] == target;
    }
};

一次二分,时间复杂度相同,主要进行了位置分割

class Solution {
public:
    int binarySearch(vector<int>& nums, int l, int r, int target) {
        int pos = l;
        while (l <= r) {
            int mid = l + (r - l) / 2;
            if (nums[mid] < target) {
                l = mid + 1;
            } else {
                pos = mid;
                r = mid - 1;
            }
        }
        return pos;
    }
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        int m = matrix.size(), n = matrix[0].size();
        int l = 0, r = m * n - 1;
        while (l <= r) {
            int mid = l + (r - l) / 2;
            int x = matrix[mid / n][mid % n];
            if (x == target) {
                return true;
            } else if (x < target) {
                l = mid + 1;
            } else {
                r = mid - 1;
            }
        }
        return false;
    }
};
392. 判断子序列

双指针

class Solution {
public:
    bool isSubsequence(string s, string t) {
        // two points
        int len1 = s.length(), len2 = t.length();
        if (!len1) return true;
        int idx1 = 0, idx2 = 0;
        while (idx1 < len1 && idx2 < len2) {
            if (s[idx1] == t[idx2]) {
                ++idx1; ++idx2;
            } else {
                ++idx2;
            }
        }
        return idx1 == len1;
    }
};
746. 使用最小花费爬楼梯
class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        //  爬楼梯衍生问题
        int len = cost.size();
        vector<int> dp(len);
        dp[0] = cost[0];    dp[1] = cost[1];
        for (int i = 2; i < len; ++i) {
            dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i];
        }
        return min(dp[len - 1], dp[len - 2]);
    }
};
383. 赎金信

用vector速度会比unordered_map快些

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        int len1 = ransomNote.size(), len2 = magazine.size();
        if (len1 > len2)    return false;
        unordered_map<int, int> mp;
        for (char& x : magazine) {
            ++mp[x];
        }
        for (char& x : ransomNote) {
            if (!mp.count(x) || mp[x] == 0) {
                return false;
            }
            --mp[x];
        }
        return true;
    }
};
63. 不同路径 II
class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        // dp
        int m = obstacleGrid.size(), n = obstacleGrid[0].size();
        vector<vector<int>> dp(m, vector<int>(n));
        dp[0][0] = (obstacleGrid[0][0] == 1 ? 0 : 1);
        
        for (int i = 1; i < m; ++i) {
            if (obstacleGrid[i][0] == 0) {
                dp[i][0] = dp[i - 1][0];
            }
        }
        for (int i = 1; i < n; ++i) {
            if (obstacleGrid[0][i] == 0) {
                dp[0][i] = dp[0][i - 1];
            }
        }

        for (int i = 1; i < m; ++i) {
            for (int j = 1; j < n; ++j) {
                if (obstacleGrid[i][j] == 1) {
                    dp[i][j] = 0;
                } else {
                    dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
                }
            }
        }

        cout << dp[m - 1][n - 1] << " " << INT_MAX << " ";
        return dp[m - 1][n - 1];
    }
};

滚动数组思想,优化空间

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int m = obstacleGrid.size(), n = obstacleGrid[0].size();
        vector<int> dp(n);
        dp[0] = (obstacleGrid[0][0] == 0);
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                if (obstacleGrid[i][j] == 1) {
                    dp[j] = 0;
                    continue;
                }
                if (j - 1 >= 0) {
                    dp[j] += dp[j - 1];
                }
            }
        }
        return dp[n - 1];
    }
};
209. 长度最小的子数组

滑动窗口 时间复杂度O(n)

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        // 滑动窗口
        int len = nums.size(), res = INT_MAX;
        int i = 0, j = 1, count = nums[0];
        if (len == 0)   return count >= target ? 1 : 0;
        while (j < len) {
            while (j < len && count < target) {
                count += nums[j++];
            }
            if (count >= target) {
                res = min(res, j - i);
            }
            while (count >= target) {
                count -= nums[i++];
                if (count >= target) {
                    res = min(res, j - i);
                }
            }
        }
        return res == INT_MAX ? 0 : res;
    }
};

代码精简版

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int len = nums.size();
        int res = INT_MAX, count = 0;
        int l = 0, r = 0;
        while (r < len) {
            count += nums[r++];
            while(count >= target) {
                res = min(res, r - l);
                count -= nums[l++];
            }
        }
        return res == INT_MAX ? 0 : res;
    }
};

前缀和+二分 时间复杂度O(Nlogn)

这个地方有个tips, 就是一开始我们计算的sums[0] = 0,依次遍历

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int len = nums.size();
        int res = INT_MAX;
        vector<int> sums(len + 1, 0);
        
        for (int i = 1; i <= len; ++i) {
            sums[i] = nums[i - 1] + sums[i - 1];
        }

        for (int i = 1; i <= len; ++i) {
            int object = target + sums[i - 1];
            auto idx = lower_bound(sums.begin(), sums.end(), object);
            if (idx != sums.end()) {
                res = min(res, static_cast<int>(idx - sums.begin()) - i + 1);
            }
        }
        return res == INT_MAX ? 0 : res;
    }
};
232. 用栈实现队列
class MyQueue {
public:
    stack<int> sk1, sk2;
    MyQueue() {

    }

    void push(int x) {
        sk2.push(x);
    }
    
    int pop() {
        flag_sk1(sk1);
        int temp = sk1.top();
        sk1.pop();
        return temp;
    }
    
    int peek() {
        flag_sk1(sk1);
        return sk1.top();
    }
    
    bool empty() {
        return sk1.empty() && sk2.empty();
    }

        void flag_sk1(stack<int>& sk1) {
        if (sk1.empty()) {
            while(!sk2.empty()) {
                sk1.push(sk2.top());
                sk2.pop();
            }
        }
    }
};

/**
 * Your MyQueue object will be instantiated and called as such:
 * MyQueue* obj = new MyQueue();
 * obj->push(x);
 * int param_2 = obj->pop();
 * int param_3 = obj->peek();
 * bool param_4 = obj->empty();
 */
219. 存在重复元素 II

滑动窗口

也可以用Hash完成,不过空间复杂度高些

class Solution {
public:
    bool containsNearbyDuplicate(vector<int>& nums, int k) {
        // two points
        unordered_map<int, int> mp;
        for (int i = 0; i < nums.size(); ++i) {
            if (i > k) {
                --mp[nums[i - k - 1]];
            }
            ++mp[nums[i]];
            if (mp[nums[i]] == 2)   return true;
        }
        return false;
    }
};
130. 被围绕的区域

对边界上O进行深度优先遍历,标记位一个OX之外的字符型char,最后分别替换

时间复杂度:O(mn)

class Solution {
public:
    int m, n;

    void DFS(vector<vector<char>>& board, int i, int j) {
        board[i][j] = 'Z';
        if (i + 1 < m && board[i + 1][j] == 'O') {
            DFS(board, i + 1, j);
        }
        if (i - 1 >= 0 && board[i - 1][j] == 'O') {
            DFS(board, i - 1, j);
        }
        if (j + 1 < n && board[i][j + 1] == 'O') {
            DFS(board, i, j + 1);
        }
        if (j - 1 >= 0 && board[i][j - 1] == 'O') {
            DFS(board, i, j - 1);
        }
}
    void solve(vector<vector<char>>& board) {
        // 简单思路,对于边界O,进行DFS
        m = board.size(), n = board[0].size();

        for (int i = 0; i < m; ++i) {
            if (board[i][0] == 'O') {
                DFS(board, i, 0);
            }
            if (board[i][n - 1] == 'O') {
                DFS(board, i, n - 1);
            }
        }
        for (int j = 0; j < n; ++j) {
            if (board[0][j] == 'O') {
                DFS(board, 0, j);
            }
            if (board[m - 1][j] == 'O') {
                DFS(board, m - 1, j);
            }
        }

        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                if (board[i][j] == 'O') {
                    board[i][j] = 'X';
                } else if (board[i][j] == 'Z') {
                    board[i][j] = 'O';
                }
            }
        }
    }
};
120. 三角形最小路径和

打卡经典题,注意每一行第一个和最后一个dp单独判断

空间复杂度O(n^2)

class Solution {
public:
    int minimumTotal(vector<vector<int>>& triangle) {
        // 动态规划
        int len = triangle.size();
        vector<vector<int>>dp(len, vector<int>(len));
        dp[0][0] = triangle[0][0];
        for (int i = 1; i < len; ++i) {
            dp[i][0] = dp[i - 1][0] + triangle[i][0];
            for (int j = 1; j < i; ++j) {
                dp[i][j] = min(dp[i - 1][j], dp[i - 1][j - 1]) + triangle[i][j];
            }
            dp[i][i] = dp[i - 1][i - 1] + triangle[i][i];
        }
        return *min_element(dp[len - 1].begin(), dp[len - 1].end());

    }
};

空间优化

动态滚动数组

class Solution {
public:
    int minimumTotal(vector<vector<int>>& triangle) {
        // 动态规划
        int len = triangle.size();
        vector<int> dp(len);
        dp[0] = triangle[0][0];
        for (int i = 1; i < len; ++i) {
            dp[i] = dp[i - 1] + triangle[i][i];
            for (int j = i - 1; j > 0; --j) {
                dp[j] = min(dp[j], dp[j - 1]) + triangle[i][j];
            }
            dp[0] += triangle[i][0]; 
        }
        return *min_element(dp.begin(), dp.end());
    }
};
695. 岛屿的最大面积

经典题,注意这里不是求路径长度,而是求面积

class Solution {
public:
    int m, n;
    int res = 0;
    int DFS(vector<vector<int>>& grid, int i, int j) {
        if (i >= m || i < 0 || j >= n || j < 0 || grid[i][j] != 1) {
            return 0;
        }
        grid[i][j] = 0;
        return DFS(grid, i + 1, j) + DFS(grid, i - 1, j) + DFS(grid, i, j + 1) + DFS(grid, i, j - 1) + 1;
    }
    int maxAreaOfIsland(vector<vector<int>>& grid) {
        // DFS经典题
        m = grid.size(), n = grid[0].size();
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                if (grid[i][j] == 1) {
                    res = max(res, DFS(grid, i, j));
                }
            }
        }
        return res;
    }
};
409. 最长回文串

用vector记录每个字母出现的次数

flag用来判断是否有奇数且只能判断一次

vector速度比unordered_map快

class Solution {
public:
    int longestPalindrome(string s) {
       int res = 0, len = s.length();
       vector<int> v(26 * 2, 0);
       bool flag = false;
       for (char& x : s) {
           if (x >= 'a' && x <= 'z') {
               ++v[x - 'a'];
           } else {
               ++v[x - 'A' + 26];
           }
       }
       for (int i = 0; i < 26 * 2; ++i) {
           res += (v[i] & 1 ? v[i] - 1 : v[i]);
           v[i] %= 2;
           if (!flag && v[i] != 0) {
               res += 1;
               flag = true;
           }
       }
       return res;
    }
};
43. 字符串相乘

模拟字符串相乘

class Solution {
public:
    string ride(string a, char b) {
        int len = a.length();
        int next = 0, b1 = b - '0';
        for (int i = len - 1; i >= 0; --i) {
            int a1 = a[i] - '0';
            a[i] = (a1 * b1 + next) % 10 + '0';
            next = (a1 * b1 + next) / 10; 
        }
        if (next != 0) {
            a.insert(0, to_string(next));
        }
        return a;
    }
    string add(string a, string b) {
        int idx1 = a.length() - 1, idx2 = b.length() - 1; 
        int next = 0;
        while (idx1 >= 0) {
            int a1 = a[idx1] - '0', b1 = (idx2 < 0 ? 0 : b[idx2] - '0');
            a[idx1] = (a1 + b1 + next) % 10 + '0';
            next = (a1 + b1 + next) / 10;
            --idx1; --idx2;
        }
        if (next != 0) {
            a.insert(0, to_string(next));
        }
        return a;
    }

    string multiply(string num1, string num2) {
        if (num2 == "0")  return "0";
        int len1 = num1.length(), len2 = num2.length();
        if (len1 < len2)    return multiply(num2, num1);
        string str;
        string temp;
        for (int i = len2 - 1; i >= 0; --i) {
            temp = add(ride(num1, num2[i]), temp);
            str += temp[temp.length() - 1];
            temp.erase(temp.end() - 1);
        }
        reverse(str.begin(), str.end());
        str.insert(0, temp);
        return str;
    }
};
151. 颠倒字符串中的单词

题目要求原地解法并且空间复杂度O(1),第一时间想到的是模拟,收尾空格全部删除

class Solution {
public:
    int delete_space(string& s, int idx, bool flag) {
        int j = idx;
        while (j < s.length() && s[j] == ' ') {
            ++j;
        }
        s.erase(idx, flag? j - idx : j - idx - 1);
        return flag ? idx : idx + 1;
    }
    string reverseWords(string s) {
        int len = s.length();
        int i = delete_space(s, 0, true);
        while(i < s.length()) {
            if (i > 0)  i = delete_space(s, i, false);
            int j = i;
            while (j < s.length() && s[j] != ' ') {
                ++j;
            }
            reverse(s.begin() + i, s.begin() + j);
            i = j;
        }
        reverse(s.begin(), s.end());
        delete_space(s, 0, true);
        return s;
    }
};

官方题解注释的很好,就再进行修改了

class Solution {
public:
    string reverseWords(string s) {
        // 反转整个字符串
        reverse(s.begin(), s.end());

        int n = s.size();
        int idx = 0;
        for (int start = 0; start < n; ++start) {
            if (s[start] != ' ') {
                // 填一个空白字符然后将idx移动到下一个单词的开头位置
                if (idx != 0) s[idx++] = ' ';

                // 循环遍历至单词的末尾
                int end = start;
                while (end < n && s[end] != ' ') s[idx++] = s[end++];

                // 反转整个单词
                reverse(s.begin() + idx - (end - start), s.begin() + idx);

                // 更新start,去找下一个单词
                start = end;
            }
        }
        s.erase(s.begin() + idx, s.end());
        return s;
    }
};
605. 种花问题
class Solution {
public:
    bool canPlaceFlowers(vector<int>& flowerbed, int n) {
        // two points
        int len = flowerbed.size();
        int l = -1;
        for (int i = 0; i < len; ++i) {
            if (flowerbed[i] == 1) {
                if (l < 0) {
                    n -= i / 2;
                } else {
                    n -= (i - l - 2) / 2;
                }  
                l = i;
            }
        }
        if (l < 0) {
            n -= (len + 1) /2;
        } else {
            n -= (len - l - 1) / 2;
        }
        return n <= 0;
    }
};
231. 2 的幂

位运算

class Solution {
public:
    bool isPowerOfTwo(int n) {
        if (n == 0) return false;
        while (n) {
            if (n != 1 && n & 1) {
                return false;
            } 
            n >>= 1;
        }
        return true;
    }
};
class Solution {
public:
    bool isPowerOfTwo(int n) {
        return n > 0 && (n & (n - 1)) == 0;
    }
};
class Solution {
public:
    bool isPowerOfTwo(int n) {
        return n > 0 && (1 << 30) % n == 0;
    }
};
191. 位1的个数
class Solution {
public:
    int hammingWeight(uint32_t n) {
        int count = 0;
        while (n) {
            n &= (n - 1);
            ++count;
        }
        return count;
    }
};
40. 组合总和 II

类似全排列问题

时间复杂度O(2^n * n)

class Solution {
public:
    vector<vector<int>> res;
    vector<int> vis;
    void DFS(vector<int>&candidates, int target, int count, vector<int> temp, int idx) {
        if (count == target) {
            res.push_back(temp);
            return;
        }
        for (int i = idx + 1; i < candidates.size(); ++i) {
            if (i > 0 && candidates[i] == candidates[i - 1] && vis[i - 1] == 0)    continue;
            if (count + candidates[i] > target) break;
            vis[i] = 1;
            temp.push_back(candidates[i]);
            DFS(candidates, target, count + candidates[i], temp, i);
            temp.pop_back();
            vis[i] = 0;
        }
    }

    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        vis.resize(candidates.size(), 0);
        sort(candidates.begin(), candidates.end());
        DFS(candidates, target, 0, vector<int>{}, -1);

        return res;
    }
};
216. 组合总和 III
class Solution {
public:
    vector<vector<int>> res;
    void DFS(int k, int n, vector<int> temp, int count, int i) {
        if (k == 0 && count == n) {
            res.push_back(temp);
            return;
        }
        if (k < 0 || count > n)  return;
        for ( ; i <= (n >= 9 ? 9 : n); ++i) {
            count += i;
            temp.push_back(i);
            DFS(--k, n, temp, count, i + 1);
            ++k;
            count -= i;
            temp.pop_back();
        }
    }
    vector<vector<int>> combinationSum3(int k, int n) {
        DFS(k, n, vector<int>{}, 0, 1);
        return res;
    }
};
414. 第三大的数

set.rbegin():它返回反向的迭代器(reverse iterator),该迭代器指向集合的最后一个元素

set可以做到除重 + 排序, 但是利用容器特性比调用泛型算法来的慢

class Solution {
public:
    int thirdMax(vector<int>& nums) {
        set<int> st;
        for (int& x : nums) {
            st.insert(x);
            if (st.size() > 3) {
                st.erase(st.begin());
            }
        }
        return st.size() == 3 ? *st.begin() : *st.rbegin();
    }
};

"unique"是C++语言中的STL函数,包含于algorithm头文件中。功能是将数组中相邻的重复元素去除。然而其本质是将重复的元素移动到数组的末尾,最后再将迭代器末尾指向最后不重复的下标。

class Solution {
public:
    int thirdMax(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        nums.erase(unique(nums.begin(), nums.end()), nums.end());
        return nums.size() < 3 ? nums[nums.size() - 1] : nums[nums.size() - 3];
    }
};
994. 腐烂的橘子

这题相比于DFS,用BFS更合适,因为DFS会访问非常多的重复路径

class Solution {
public:
    int cnt = 0;
    int dis[10][10];
    int dir_x[4] = {0, 1, 0, -1};
    int dir_y[4] = {1, 0, -1, 0};

    int orangesRotting(vector<vector<int>>& grid) {
        //BFS求坏掉的橘子到新鲜橘子的最短路径
        queue<pair<int,int>> q;
        int res = 0;
        int m = grid.size(), n = grid[0].size();
        memset(dis, 0, sizeof(dis));
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                if (grid[i][j] == 2) {
                    q.push({i, j});
                }
                if (grid[i][j] == 1)    ++cnt;
            }
        }
        while (!q.empty()) {
            pair<int, int> k = q.front();   q.pop();
            for (int i = 0; i < 4; ++i) {
                int x = k.first + dir_x[i];
                int y = k.second + dir_y[i];
                if (x < 0 || x >= m || y < 0 || y >= n || grid[x][y] != 1)  continue;    
                dis[x][y] = dis[k.first][k.second] + 1;
                q.push({x, y});
                --cnt;
                grid[x][y] = 2;
                res = dis[x][y];
            }
        }
        return cnt == 0 ? res : -1;
    }
};
71. 简化路径

1、先通过/分隔字符串

2、对…和空字符串判断处理

class Solution {
public:
    vector<string> split(string& s, char x) {
        vector<string> ans;
        string cur;
        for (char& ch : s) {
            if (ch == x) {
                ans.push_back(move(cur));
                cur.clear();
            } else {
                cur += ch;
            }
        }
        ans.push_back(move(cur));
        return ans;
    }

    string simplifyPath(string path) {
        vector<string> ans = split(path, '/');
        vector<string> stack;
        for (string& ch : ans) {
            if (ch == "..") {
                if (!stack.empty()) {
                    stack.pop_back();
                }
            } else if (!ch.empty() && ch != ".") {
                stack.push_back(ch);
            }
        }
        string str;
        if (stack.empty()) {
            str = '/';
        } else {
            for (string& ch : stack) {
                str += "/" + move(ch);
            }
        }
        return str;
    }
};
1480. 一维数组的动态和

明明是前缀和

class Solution {
public:
    vector<int> runningSum(vector<int>& nums) {
        // 前缀和
        for (int i = 1; i < nums.size(); ++i) {
            nums[i] += nums[i - 1];
        }
        return nums;
    }
};
485. 最大连续 1 的个数

用前缀和思想做

如果不能对原数组修改的话,直接判断0、1; 1的话count++, 0的话计算max,count = 0

class Solution {
public:
    int findMaxConsecutiveOnes(vector<int>& nums) {
        int res = nums[0];
        for (int i = 1; i < nums.size(); ++i) {
            if (nums[i] == 1) {
                nums[i] = nums[i - 1] + 1;
                res = max(res, nums[i]);
            }
        }
        return res;
    }
};
面试题 17.16. 按摩师
class Solution {
public:
    int massage(vector<int>& nums) {
        // 动态规划dp
        int len = nums.size();
        if (len < 2)    return len == 1 ? nums[0] : 0;
        vector<int> dp(len);
        for (int i = 0; i < len; ++i) {
            dp[i] = max(i - 2 >= 0 ? dp[i - 2] : 0, i - 3 >= 0 ? dp[i - 3] : 0) + nums[i];
        }
        return max(dp[len - 1], dp[len - 2]);
    }
};

买卖股票问题

同一时间两种选择:买、卖

class Solution {
public:
    int massage(vector<int>& nums) {
        // 跟买卖股票很像
        int len = nums.size();
        if (len < 2)    return len == 1 ? nums[0] : 0;
        int dp0 = 0, dp1 = nums[0];
        for (int i = 1; i < len; ++i) {
            int temp0 = max(dp0, dp1);
            int temp1 = dp0 + nums[i];
            dp0 = temp0;
            dp1 = temp1;
        }
        return max(dp0, dp1);
    }
};
303. 区域和检索 - 数组不可变

前缀和

不可以修改数组,所以新建了一个vector

class NumArray {
public:
    vector<int> ans;
    NumArray(vector<int>& nums) {
        ans = nums;
        for (int i = 1; i < ans.size(); ++i) {
            ans[i] += ans[i - 1];
        }
    }
    
    int sumRange(int left, int right) {
        return ans[right] - (left - 1 >= 0 ? ans[left - 1] : 0);
    }
};

/**
 * Your NumArray object will be instantiated and called as such:
 * NumArray* obj = new NumArray(nums);
 * int param_1 = obj->sumRange(left,right);
 */
230. 二叉搜索树中第K小的元素

key:二叉搜索树、中序遍历、第k个最小元素

/**
 * 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:
    int res;
    void DFS(TreeNode* node, int &k) {
        if (node == nullptr || k < 0) {
            return;
        }
        DFS(node->left, k);
        --k;
        if (k == 0) {
            res = node->val;
            return;
        } 
        DFS(node->right, k);
    }

    int kthSmallest(TreeNode* root, int k) {
        // 中序遍历
        DFS(root, k);
        return res;
    }
};
class MyBst {
public:
    MyBst(TreeNode *root) {
        this->root = root;
        countNodeNum(root);
    }

    // 返回二叉搜索树中第k小的元素
    int kthSmallest(int k) {
        TreeNode *node = root;
        while (node != nullptr) {
            int left = getNodeNum(node->left);
            if (left < k - 1) {
                node = node->right;
                k -= left + 1;
            } else if (left == k - 1) {
                break;
            } else {
                node = node->left;
            }
        }
        return node->val;
    }

private:
    TreeNode *root;
    unordered_map<TreeNode *, int> nodeNum;

    // 统计以node为根结点的子树的结点数
    int countNodeNum(TreeNode * node) {
        if (node == nullptr) {
            return 0;
        }
        nodeNum[node] = 1 + countNodeNum(node->left) + countNodeNum(node->right);
        return nodeNum[node];
    }

    // 获取以node为根结点的子树的结点数
    int getNodeNum(TreeNode * node) {
        if (node != nullptr && nodeNum.count(node)) {
            return nodeNum[node];
        }else{
            return 0;
        }
    }
};

class Solution {
public:
    int kthSmallest(TreeNode* root, int k) {
        MyBst bst(root);
        return bst.kthSmallest(k);
    }
};
public class Solution {
    public int KthSmallest(TreeNode root, int k) {
        // 中序遍历生成数值列表
        IList<int> inorderList = new List<int>();
        Inorder(root, inorderList);

        // 构造平衡二叉搜索树
        AVL avl = new AVL(inorderList);

        // 模拟1000次插入和删除操作
        int[] randomNums = new int[1000];
        Random random = new Random();
        for (int i = 0; i < 1000; ++i) {
            randomNums[i] = random.Next(10001);
            avl.Insert(randomNums[i]);
        }
        Shuffle(randomNums); // 列表乱序
        for (int i = 0; i < 1000; ++i) {
            avl.Delete(randomNums[i]);
        }

        return avl.KthSmallest(k);
    }

    private void Inorder(TreeNode node, IList<int> inorderList) {
        if (node.left != null) {
            Inorder(node.left, inorderList);
        }
        inorderList.Add(node.val);
        if (node.right != null) {
            Inorder(node.right, inorderList);
        }
    }

    private void Shuffle(int[] arr) {
        Random random = new Random();
        int length = arr.Length;
        for (int i = 0; i < length; i++) {
            int randIndex = random.Next(length);
            int temp = arr[i];
            arr[i] = arr[randIndex];
            arr[randIndex] = temp;
        }
    }
}

// 平衡二叉搜索树(AVL树):允许重复值
class AVL {
    Node root;

    // 平衡二叉搜索树结点
    class Node {
        public int val;
        public Node parent;
        public Node left;
        public Node right;
        public int size;
        public int height;

        public Node(int val) : this(val, null) {
        }

        public Node(int val, Node parent) : this(val, parent, null, null) {
        }

        public Node(int val, Node parent, Node left, Node right) {
            this.val = val;
            this.parent = parent;
            this.left = left;
            this.right = right;
            this.height = 0; // 结点高度:以node为根节点的子树的高度(高度定义:叶结点的高度是0)
            this.size = 1; // 结点元素数:以node为根节点的子树的节点总数
        }
    }

    public AVL(IList<int> vals) {
        if (vals != null) {
            this.root = Build(vals, 0, vals.Count - 1, null);
        }
    }

    // 根据vals[l:r]构造平衡二叉搜索树 -> 返回根结点
    private Node Build(IList<int> vals, int l, int r, Node parent) {
        int m = (l + r) >> 1;
        Node node = new Node(vals[m], parent);
        if (l <= m - 1) {
            node.left = Build(vals, l, m - 1, node);
        }
        if (m + 1 <= r) {
            node.right = Build(vals, m + 1, r, node);
        }
        Recompute(node);
        return node;
    }

    // 返回二叉搜索树中第k小的元素
    public int KthSmallest(int k) {
        Node node = root;
        while (node != null) {
            int left = GetSize(node.left);
            if (left < k - 1) {
                node = node.right;
                k -= left + 1;
            } else if (left == k - 1) {
                break;
            } else {
                node = node.left;
            }
        }
        return node.val;
    }

    public void Insert(int v) {
        if (root == null) {
            root = new Node(v);
        } else {
            // 计算新结点的添加位置
            Node node = SubtreeSearch(root, v);
            bool isAddLeft = v <= node.val; // 是否将新结点添加到node的左子结点
            if (node.val == v) { // 如果值为v的结点已存在
                if (node.left != null) { // 值为v的结点存在左子结点,则添加到其左子树的最右侧
                    node = SubtreeLast(node.left);
                    isAddLeft = false;
                } else { // 值为v的结点不存在左子结点,则添加到其左子结点
                    isAddLeft = true;
                }
            }

            // 添加新结点
            Node leaf = new Node(v, node);
            if (isAddLeft) {
                node.left = leaf;
            } else {
                node.right = leaf;
            }

            Rebalance(leaf);
        }
    }

    // 删除值为v的结点 -> 返回是否成功删除结点
    public bool Delete(int v) {
        if (root == null) {
            return false;
        }

        Node node = SubtreeSearch(root, v);
        if (node.val != v) { // 没有找到需要删除的结点
            return false;
        }

        // 处理当前结点既有左子树也有右子树的情况
        // 若左子树比右子树高度低,则将当前结点替换为右子树最左侧的结点,并移除右子树最左侧的结点
        // 若右子树比左子树高度低,则将当前结点替换为左子树最右侧的结点,并移除左子树最右侧的结点
        if (node.left != null && node.right != null) {
            Node replacement = null;
            if (node.left.height <= node.right.height) {
                replacement = SubtreeFirst(node.right);
            } else {
                replacement = SubtreeLast(node.left);
            }
            node.val = replacement.val;
            node = replacement;
        }

        Node parent = node.parent;
        Delete(node);
        Rebalance(parent);
        return true;
    }

    // 删除结点p并用它的子结点代替它,结点p至多只能有1个子结点
    private void Delete(Node node) {
        if (node.left != null && node.right != null) {
            return;
            // throw new Exception("Node has two children");
        }
        Node child = node.left != null ? node.left : node.right;
        if (child != null) {
            child.parent = node.parent;
        }
        if (node == root) {
            root = child;
        } else {
            Node parent = node.parent;
            if (node == parent.left) {
                parent.left = child;
            } else {
                parent.right = child;
            }
        }
        node.parent = node;
    }

    // 在以node为根结点的子树中搜索值为v的结点,如果没有值为v的结点,则返回值为v的结点应该在的位置的父结点
    private Node SubtreeSearch(Node node, int v) {
        if (node.val < v && node.right != null) {
            return SubtreeSearch(node.right, v);
        } else if (node.val > v && node.left != null) {
            return SubtreeSearch(node.left, v);
        } else {
            return node;
        }
    }

    // 重新计算node结点的高度和元素数
    private void Recompute(Node node) {
        node.height = 1 + Math.Max(GetHeight(node.left), GetHeight(node.right));
        node.size = 1 + GetSize(node.left) + GetSize(node.right);
    }

    // 从node结点开始(含node结点)逐个向上重新平衡二叉树,并更新结点高度和元素数
    private void Rebalance(Node node) {
        while (node != null) {
            int oldHeight = node.height, oldSize = node.size;
            if (!IsBalanced(node)) {
                node = Restructure(TallGrandchild(node));
                Recompute(node.left);
                Recompute(node.right);
            }
            Recompute(node);
            if (node.height == oldHeight && node.size == oldSize) {
                node = null; // 如果结点高度和元素数都没有变化则不需要再继续向上调整
            } else {
                node = node.parent;
            }
        }
    }

    // 判断node结点是否平衡
    private bool IsBalanced(Node node) {
        return Math.Abs(GetHeight(node.left) - GetHeight(node.right)) <= 1;
    }

    // 获取node结点更高的子树
    private Node TallChild(Node node) {
        if (GetHeight(node.left) > GetHeight(node.right)) {
            return node.left;
        } else {
            return node.right;
        }
    }

    // 获取node结点更高的子树中的更高的子树
    private Node TallGrandchild(Node node) {
        Node child = TallChild(node);
        return TallChild(child);
    }

    // 重新连接父结点和子结点(子结点允许为空)
    private static void Relink(Node parent, Node child, bool isLeft) {
        if (isLeft) {
            parent.left = child;
        } else {
            parent.right = child;
        }
        if (child != null) {
            child.parent = parent;
        }
    }

    // 旋转操作
    private void Rotate(Node node) {
        Node parent = node.parent;
        Node grandparent = parent.parent;
        if (grandparent == null) {
            root = node;
            node.parent = null;
        } else {
            Relink(grandparent, node, parent == grandparent.left);
        }

        if (node == parent.left) {
            Relink(parent, node.right, true);
            Relink(node, parent, false);
        } else {
            Relink(parent, node.left, false);
            Relink(node, parent, true);
        }
    }

    // trinode操作
    private Node Restructure(Node node) {
        Node parent = node.parent;
        Node grandparent = parent.parent;

        if ((node == parent.right) == (parent == grandparent.right)) { // 处理需要一次旋转的情况
            Rotate(parent);
            return parent;
        } else { // 处理需要两次旋转的情况:第1次旋转后即成为需要一次旋转的情况
            Rotate(node);
            Rotate(node);
            return node;
        }
    }

    // 返回以node为根结点的子树的第1个元素
    private static Node SubtreeFirst(Node node) {
        while (node.left != null) {
            node = node.left;
        }
        return node;
    }

    // 返回以node为根结点的子树的最后1个元素
    private static Node SubtreeLast(Node node) {
        while (node.right != null) {
            node = node.right;
        }
        return node;
    }

    // 获取以node为根结点的子树的高度
    private static int GetHeight(Node node) {
        return node != null ? node.height : 0;
    }

    // 获取以node为根结点的子树的结点数
    private static int GetSize(Node node) {
        return node != null ? node.size : 0;
    }
}
297. 二叉树的序列化与反序列化

这题不知道为什么只有list能过,vector死活过不了…

先写个先序遍历的

/**
 * 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:
    void serialize(TreeNode* node, string& str) {
        if (node == NULL) {
            str += "NULL,";
            return;
        }
        str += to_string(node->val) + ",";
        serialize(node->left, str);
        serialize(node->right, str);
    }

    // Encodes a tree to a single string.
    string serialize(TreeNode* root) {
        string str;
        serialize(root, str);
        return str;
    }

    TreeNode* deserialize(list<string>& ans) {
        if (ans.front() == "NULL") {
            ans.erase(ans.begin());
            return NULL;
        }
        TreeNode* node = new TreeNode(stoi(ans.front()));
        ans.erase(ans.begin());
        node->left = deserialize(ans);
        node->right = deserialize(ans);

        return node;

    }

    // Decodes your encoded data to tree.
    TreeNode* deserialize(string data) {
        list<string> ans;
        int len = data.length();
        for (int i = 0; i < len; ) {
            int j = i;
            while (j < len && data[j] != ',') {
                ++j;
            }
            ans.push_back(data.substr(i, j - i));
            i = j + 1;
        }
        cout << ans.size();
        return deserialize(ans);
    }
};

// Your Codec object will be instantiated and called as such:
// Codec ser, deser;
// TreeNode* ans = deser.deserialize(ser.serialize(root));
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Codec {
public:
    void serialize(TreeNode* node, string& str) {
        if (node == NULL) {
            str += "NULL,";
            return;
        }
        str += to_string(node->val) + ",";
        serialize(node->left, str);
        serialize(node->right, str);
    }

    // Encodes a tree to a single string.
    string serialize(TreeNode* root) {
        string str;
        serialize(root, str);
        return str;
    }

    TreeNode* deserialize(vector<string>& ans, int& i) {
        if (ans[i] == "NULL") {
            i += 1;
            return NULL;
        }
        TreeNode* node = new TreeNode(stoi(ans[i]));
        i += 1;
        node->left = deserialize(ans, i);
        node->right = deserialize(ans, i);
        return node;

    }

    // Decodes your encoded data to tree.
    TreeNode* deserialize(string data) {
        vector<string> ans;
        int len = data.length();
        for (int i = 0; i < len; ) {
            int j = i;
            while (j < len && data[j] != ',') {
                ++j;
            }
            ans.push_back(data.substr(i, j - i));
            i = j + 1;
        }
        int i = 0;
        return deserialize(ans, i);
    }
};

// Your Codec object will be instantiated and called as such:
// Codec ser, deser;
// TreeNode* ans = deser.deserialize(ser.serialize(root));

再实现个层次遍历

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

    // Encodes a tree to a single string.
    string serialize(TreeNode* root) {
        string str;
        if (root == NULL) {
            str += "NULL";
            return str;
        }
        queue<TreeNode*> q;
        q.push(root);
        while (!q.empty()) {
            TreeNode* node = q.front(); q.pop();
            if (node == NULL) {
                str += "NULL,";
                continue;
            }
            str += to_string(node->val) + ",";
            q.push(node->left);
            q.push(node->right);
        }
        return str;
    }

    TreeNode* deserialize(queue<string>& ans) {
        queue<TreeNode*> temp;
        queue<TreeNode*> que;
        if (ans.empty()) {
            return NULL;
        }
        TreeNode* root = (ans.front() == "NULL" ? NULL : new TreeNode(stoi(ans.front())));
        temp.push(root);
        ans.pop();
        while (!ans.empty()) {
            while (!temp.empty()) {
                TreeNode* node = temp.front();
                temp.pop();
                TreeNode* left = (ans.front() == "NULL" ? NULL : new TreeNode(stoi(ans.front())));
                ans.pop();
                TreeNode* right = (ans.front() == "NULL" ? NULL : new TreeNode(stoi(ans.front())));
                ans.pop();
                node->left = left;  node->right = right;
                if (left != NULL)   que.push(left);
                if (right != NULL)  que.push(right);
            }
            temp = que;
            while (!que.empty()) {
                que.pop();
            }
        }

        return root;
    }

    // Decodes your encoded data to tree.
    TreeNode* deserialize(string data) {
        queue<string> ans;
        int len = data.length();
        for (int i = 0; i < len; ) {
            int j = i;
            while (j < len && data[j] != ',') {
                ++j;
            }
            ans.push(data.substr(i, j - i));
            i = j + 1;
        }
        return deserialize(ans);
    }
};

// Your Codec object will be instantiated and called as such:
// Codec ser, deser;
// TreeNode* ans = deser.deserialize(ser.serialize(root));
707. 设计链表

目前该题用的是官方题解
单链表

  • 创建了结构体结点struct Listnode,成员变量有数据域和next指针域
  • Listnode含有三种初始化列表(无参,单参,双参)
  • 创建了自定义链表类MyLinkedList,成员变量有struct Listnode *head作为头结点,int size保存链表长度
  • MyLinkedList无参构造函数–新建头结点,size初始化
  • int get(int index)–顺序遍历到索引为index的结点,返回val
  • void addHead(int val)–先断开头结点与首结点,再连接newNode与首结点,最后连接头结点与newNode
  • addAtTail–可顺序遍历到最后结点,也可在MyLinkedList中增加尾结点
  • addAtIndex–顺序遍历到[index - 1],断开[index - 1]与[index],连接新结点和index,再连接index - 1与新结点
  • deleteAtIndex–顺序遍历到index,将index - 1直接与index + 1相连接,然后删除index
struct Listnode
{
    int val;
    struct Listnode *next;
    Listnode() : val(0), next(nullptr) {}
    Listnode(int v) : val(v), next(nullptr) {}
    Listnode(int v, struct Listnode *x) : val(v), next(x) {}
};
class MyLinkedList
{
    struct Listnode *head;
    int size;

public:
    /** Initialize your data structure here. */
    MyLinkedList()
    {
        head = new Listnode();
        size = 0;
    }

    /** Get the value of the index-th node in the linked list. If the index is invalid, return -1. */
    int get(int index)
    {
        if (index < 0 || index >= size)
            return -1;
        struct Listnode *trav = head;
        for (int i = 0; i < index + 1; ++i)
            trav = trav->next;
        return trav->val;
    }

    /** Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list. */
    void addAtHead(int val)
    {
        struct Listnode *newNode = new struct Listnode(val);
        newNode->next = head->next;
        head->next = newNode;
        size += 1;
    }

    /** Append a node of value val to the last element of the linked list. */
    void addAtTail(int val)
    {
        struct Listnode *trav = head;
        for (int i = 0; i < size; ++i)
            trav = trav->next;
        trav->next = new struct Listnode(val);
        size += 1;
    }

    /** Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted. */
    void addAtIndex(int index, int val)
    {
        if (index < 0)
            addAtHead(val);
        else if (index == size)
            addAtTail(val);
        else if (index > size)
            return;
        else
        {
            struct Listnode *trav = head;
            for (int i = 0; i < index; ++i)
                trav = trav->next;
            struct Listnode *newNode = new struct Listnode(val);
            newNode->next = trav->next;
            trav->next = newNode;
            size += 1;
        }
    }

    /** Delete the index-th node in the linked list, if the index is valid. */
    void deleteAtIndex(int index)
    {
        if (index < 0 || index >= size || size == 0)
            return;
        struct Listnode *trav = head;
        struct Listnode *del = nullptr;
        for (int i = 0; i < index; i++)
            trav = trav->next;
        del = trav->next;
        trav->next = del->next;
        delete del;
        size -= 1;
    }
};

/**
 * Your MyLinkedList object will be instantiated and called as such:
 * MyLinkedList* obj = new MyLinkedList();
 * int param_1 = obj->get(index);
 * obj->addAtHead(val);
 * obj->addAtTail(val);
 * obj->addAtIndex(index,val);
 * obj->deleteAtIndex(index);
 */

双链表

  • 结点结构中增加Listnode *prev指针指向当前结点的前驱结点
struct Listnode
{
    int val;
    struct Listnode *prev;
    struct Listnode *next;
    Listnode() : val(0), prev(nullptr), next(nullptr) {}
    Listnode(int v) : val(v), prev(nullptr), next(nullptr) {}
    Listnode(int v, Listnode *p) : val(v), prev(p), next(nullptr) {}
    Listnode(int v, Listnode *p, Listnode *n) : val(v), prev(p), next(n) {}
};

class MyLinkedList
{
    int size;
    Listnode *head;
    Listnode *tail;

public:
    /** Initialize your data structure here. */
    MyLinkedList()
    {
        size = 0;
        head = new Listnode(0);
        tail = new Listnode(0);
        head->next = tail;
        tail->prev = head;
    }

    /** Get the value of the index-th node in the linked list. If the index is invalid, return -1. */
    int get(int index)
    {
        if (index < 0 || index >= size)
            return -1;
        Listnode *trav = head;
        if (index + 1 < size - index)
        {
            for (int i = 0; i < index + 1; ++i)
                trav = trav->next;
        }
        else
        {
            trav = tail;
            for (int i = 0; i < size - index; ++i)
                trav = trav->prev;
        }
        return trav->val;
    }

    /** Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list. */
    void addAtHead(int val)
    {
        Listnode *newNode = new Listnode(val, head, head->next);
        newNode->next->prev = newNode;
        head->next = newNode;
        ++size;
    }

    /** Append a node of value val to the last element of the linked list. */
    void addAtTail(int val)
    {
        Listnode *newNode = new Listnode(val, tail->prev, tail);
        tail->prev->next = newNode;
        tail->prev = newNode;
        ++size;
    }

    /** Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted. */
    void addAtIndex(int index, int val)
    {
        if (index > size)
            return;
        Listnode *trav = head;
        if (index + 1 < size - index)
        {
            for (int i = 0; i < index + 1; ++i)
                trav = trav->next;
        }
        else
        {
            trav = tail;
            for (int i = 0; i < size - index; ++i)
                trav = trav->prev;
        }
        Listnode *newNode = new Listnode(val, trav->prev, trav);
        trav->prev->next = newNode;
        trav->prev = newNode;
        ++size;
    }

    /** Delete the index-th node in the linked list, if the index is valid. */
    void deleteAtIndex(int index)
    {
        if (index < 0 || index >= size)
            return;
        Listnode *trav = head;
        if (index + 1 < size - index)
        {
            for (int i = 0; i < index + 1; ++i)
                trav = trav->next;
        }
        else
        {
            trav = tail;
            for (int i = 0; i < size - index; ++i)
                trav = trav->prev;
        }
        trav->prev->next = trav->next;
        trav->next->prev = trav->prev;
        delete trav;
        --size;
    }
};

/**
 * Your MyLinkedList object will be instantiated and called as such:
 * MyLinkedList* obj = new MyLinkedList();
 * int param_1 = obj->get(index);
 * obj->addAtHead(val);
 * obj->addAtTail(val);
 * obj->addAtIndex(index,val);
 * obj->deleteAtIndex(index);
 */
771. 宝石与石头

Hash表

class Solution {
public:
    int numJewelsInStones(string jewels, string stones) {
        // Hash
        unordered_map<char, int> mp;
        for (char& x : jewels) {
            ++mp[x];
        }
        int res = 0;

        for (char& x : stones) {
            if (mp.count(x)) {
                ++res;
            }
        }
        return res;
    }
};

数组效率高些

class Solution {
public:
    int numJewelsInStones(string jewels, string stones) {
        // Hash
        int ans[26 * 2] = {0};
        for (char& x : jewels) {
            int idx = ((x >= 'a' && x <= 'z') ? x - 'a' : x - 'A' + 26);
            ++ans[idx];
        }
        int res = 0;

        for (char& x : stones) {
            int idx = ((x >= 'a' && x <= 'z') ? x - 'a' : x - 'A' + 26);
            if (ans[idx] != 0)  ++res;
        }
        return res;
    }
};
1005. K 次取反后最大化的数组和

不开空间

class Solution {
public:
    int largestSumAfterKNegations(vector<int>& nums, int k) {
        sort(nums.begin(), nums.end());
        int idx = 0;
        for ( ; idx < nums.size() && k > 0; ++idx) {
            if (nums[idx] < 0) {
                nums[idx] = -nums[idx];
                --k;
            } else break;
        }
        sort(nums.begin(), nums.end());
        k %= 2;
        if (k & 1) {
            nums[0] = -nums[0];
        }
        return accumulate(nums.begin(), nums.end(), 0);
    }
};

开空间,时间复杂度O(n + C) C = 201

虽然说sort时间复杂度O(nlogn), 但是其实省去了很多map的开销,实际编程根据不同场景选择不同方法

class Solution {
public:
    int largestSumAfterKNegations(vector<int>& nums, int k) {
        unordered_map<int, int> mp;
        int count = accumulate(nums.begin(), nums.end(), 0);
        for (int& x : nums) {
            ++mp[x];
        }
        for (int i = -100; i <= 0; ++i) {
            if (mp[i]) {
                int appear = min(k, mp[i]);
                count += appear * (-i) * 2;
                k -= appear;
                mp[i] -= appear;
                mp[-i] += appear;
                if (k == 0) {
                    break;
                }
            }
        }
        k %= 2;
        for (int i = 0; i <= 100 && k > 0; ++i) {
            if (mp[i]) {
                count -= 2 * i;
                break;
            }
        }
        return count;
    }
};
1143. 最长公共子序列

最长公共子序列是经典二维动态规划问题

力扣官方题解写的很好

class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        int m = text1.length(), n = text2.length();
        vector<vector<int>> dp(m + 1, vector<int>(n + 1));
        for (int i = 1; i <= m; i++) {
            char c1 = text1.at(i - 1);
            for (int j = 1; j <= n; j++) {
                char c2 = text2.at(j - 1);
                if (c1 == c2) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
                }
            }
        }
        return dp[m][n];
    }
};
905. 按奇偶排序数组

双指针,原地交换

class Solution {
public:
    vector<int> sortArrayByParity(vector<int>& nums) {
        // two points
        int idx = 0;
        for(int& x : nums) {
            if (!(x & 1)) {
                swap(x, nums[idx++]);
            }
        }
        return nums;
    }
};
763. 划分字母区间

开辟map存储每个元素最后出现位置

遍历更新end

class Solution {
public:
    vector<int> partitionLabels(string s) {
        int len = s.length();
        unordered_map<int, int> mp;
        for (int i = 0; i < len; ++i) {
            if (!mp.count(s[i])) {
                mp[s[i]] = i;
            } else {
                mp[s[i]] = max(i, mp[s[i]]);
            }
        }
        vector<int> res;
        int begin = 0, end = mp[s[0]];
        for (int i = 1; i < len; ++i) {
            if (i > end) {
                res.push_back(end - begin + 1);
                begin = i;
                end = mp[s[i]];
            } else if (mp[s[i]] > end) {
                end = mp[s[i]];
            }
        }
        res.push_back(end - begin + 1);
        return res;
    }
};

能用数组不用map

class Solution {
public:
    vector<int> partitionLabels(string s) {
        int len = s.length();
        int mp[26] = {0};
        for (int i = 0; i < len; ++i) {
            if (!mp[s[i] - 'a']) {
                mp[s[i] - 'a'] = i;
            } else {
                mp[s[i] - 'a'] = max(i, mp[s[i] - 'a']);
            }
        }
        vector<int> res;
        int begin = 0, end = mp[s[0] - 'a'];
        for (int i = 1; i < len; ++i) {
            if (i > end) {
                res.push_back(end - begin + 1);
                begin = i;
                end = mp[s[i] - 'a'];
            } else if (mp[s[i] - 'a'] > end) {
                end = mp[s[i] - 'a'];
            }
        }
        res.push_back(end - begin + 1);
        return res;
    }
};
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值