LeetCode 题解随笔:补充题目

目录

一、数组

1365. 有多少小于当前数字的数字

941. 有效的山脉数组

1207. 独一无二的出现次数

189. 轮转数组

724. 寻找数组的中心下标

922. 按奇偶排序数组 II

二、哈希表

205. 同构字符串

925. 长按键入

三、二叉树

129. 求根节点到叶节点数字之和

1382. 将二叉搜索树变平衡

四、回溯

52. N皇后 II

五、贪心

649. Dota2 参议院

1221. 分割平衡字符串

 870. 优势洗牌

六、动态规划

132. 分割回文串 II[*]

673. 最长递增子序列的个数[*]

七、位运算

1356. 根据数字二进制下 1 的数目排序


一、数组

1365. 有多少小于当前数字的数字

vector<int> smallerNumbersThanCurrent(vector<int>& nums) {
        vector<int> numsSort = nums;
        sort(numsSort.begin(), numsSort.end());
        unordered_map<int, int> record;
        // 从后向前赋值,保证下标为第一个出现的元素
        for(int i = numsSort.size() - 1; i >= 0; i--) {
            record[numsSort[i]] = i;    
        }
        vector<int> res(nums.size());
        for (int i = 0; i < nums.size(); i++) {
            res[i] = record[nums[i]];
        }
        return res;
    }

941. 有效的山脉数组

bool validMountainArray(vector<int>& arr) {
        // 双指针法
        if (arr.size() <= 2)   return false;
        int left = 0;
        int right = arr.size() - 1;
        while (left < arr.size() - 1 && arr[left + 1] > arr[left]) {
            left++;
        }
        while (right > 0 && arr[right - 1] > arr[right]) {
            right--;
        }
        if (left == right && left != 0 && right != arr.size() - 1) return true;
        else    return false;
    }

1207. 独一无二的出现次数

bool uniqueOccurrences(vector<int>& arr) {
        unordered_map<int, int> arrCount;
        for (int i : arr) {
            arrCount[i]++;
        }
        set<int> count;
        for (auto it = arrCount.begin(); it != arrCount.end(); it++) {
            count.insert(it->second);
        }
        if (arrCount.size() == count.size())   return true;
        else    return false;
    }

189. 轮转数组

void rotate(vector<int>& nums, int k) {
        // 参考翻转字符串问题
        k = k % nums.size();
        reverse(nums.begin(), nums.end());
        reverse(nums.begin(), nums.begin() + k);
        reverse(nums.begin() + k, nums.end());
    }

724. 寻找数组的中心下标

int pivotIndex(vector<int>& nums) {
        int sum = accumulate(nums.begin(), nums.end(), 0);
        int tempSum = 0;
        for (int i = 0; i < nums.size(); i++) {
            if (sum - nums[i] == 2 * tempSum)   return i;
            tempSum += nums[i];
        }
        return -1;
    }

922. 按奇偶排序数组 II

vector<int> sortArrayByParityII(vector<int>& nums) {
        // 双指针法
        int odd = 1;
        for (int even = 0; even < nums.size(); even += 2) {
            // 偶数位不符合,在奇数位寻找不符合的元素进行替换
            if (nums[even] % 2 != 0) {
                while (nums[odd] % 2 != 0)   odd += 2;
                swap(nums[odd], nums[even]);
            }
        }
        return nums;
    }

二、哈希表

205. 同构字符串

bool isIsomorphic(string s, string t) {
        // 利用unordered_map直接记录字符与字符之间的映射
        unordered_map<char, char> map1;
        unordered_map<char, char> map2;
        for (int i = 0, j = 0; i < s.size(); i++, j++) {
            if (map1.find(s[i]) == map1.end()) { // map1保存s[i] 到 t[j]的映射
                map1[s[i]] = t[j];
            }
            if (map2.find(t[j]) == map2.end()) { // map2保存t[j] 到 s[i]的映射
                map2[t[j]] = s[i];
            }
            // 发现映射 对应不上,立刻返回false
            if (map1[s[i]] != t[j] || map2[t[j]] != s[i]) {
                return false;
            }
        }
        return true;
    }

925. 长按键入

bool isLongPressedName(string name, string typed) {
        int index = 0;
        // 记录重复元素数
        int count = 0;
        for (int i = 0; i < name.size(); i++) {
            // 名字中有连续重复字符
            if (i > 0 && name[i] == name[i - 1]) {
                count--;
                if (count < 0)     return false;
            }
            // 名字中没有连续重复字符
            else {
                count = 0;
                if (name[i] != typed[index])     return false;
                if (index >= typed.size())     return false;
                while (index + 1 < typed.size() && typed[index + 1] == typed[index]) {
                    index++;
                    count++;
                }
                index++;
            }    
        }
        // 是否有多余字符
        if (index != typed.size())   return false;
        return true;
    }

三、二叉树

129. 求根节点到叶节点数字之和

int res;
    vector<int> path;
    int sumNumbers(TreeNode* root) {
        res = 0;
        path.clear();
        if (!root)   return res;
        path.push_back(root->val);
        BackTracking(root);
        return res;
    }
    void BackTracking(TreeNode* node) {
        // 返回条件
        if (!node->left && !node->right) {
            res += VecToInt(path);
            return;
        }
        // 前序遍历
        if (node->left) {
            path.push_back(node->left->val);
            BackTracking(node->left);
            path.pop_back();
        }
        if (node->right) {
            path.push_back(node->right->val);
            BackTracking(node->right);
            path.pop_back();
        }
        return;
    }
    int VecToInt(vector<int>& v) {
        int res = 0;
        for (int i : v) {
            res = res * 10 + i;
        }
        return res;
    }

1382. 将二叉搜索树变平衡

vector<int> elem;
    TreeNode* balanceBST(TreeNode* root) {
        // 搜索树元素存入数组
        elem.clear();
        Traversal(root);
        BuildTree(root, 0, elem.size() - 1);
        return root;
    }
    void Traversal(TreeNode* node) {
        if (!node)   return;
        Traversal(node->left);
        elem.push_back(node->val);
        Traversal(node->right);
        return;
    }
    void BuildTree(TreeNode* node, int start, int end) {
        // 前序遍历
        int mid = start + (end - start) / 2;
        node->val = elem[mid];
        if (start <= mid - 1) {
            TreeNode* left = new TreeNode();
            node->left = left;
            BuildTree(left, start, mid - 1);
        }
        if (end >= mid + 1) {
            TreeNode* right = new TreeNode();
            node->right = right;
            BuildTree(right, mid + 1, end);
        }
        return;
    }

四、回溯

52. N皇后 II

int res;
    int totalNQueens(int n) {
        res = 0;
        vector<string> chessboard(n, string(n, '.'));
        BackTracking(0, chessboard, n);
        return res;
    }
    void BackTracking(int row, vector<string>& chessboard, const int n) {
        if (row == n) {
            res++;
            return;
        }
        for (int col = 0; col < n; col++) {
            // 棋盘有效时才继续递归,无效则进行下一次同层迭代
            if (IsValid(row, col, chessboard, n)) {
                chessboard[row][col] = 'Q';
                BackTracking(row + 1, chessboard, n);
                chessboard[row][col] = '.';
            }
        }
    }
    // 检查当前棋盘是否合法
    bool IsValid(int row, int col, vector<string>& chessboard, const int n) {
        // 检查列
        for (int i = 0; i < row; i++) {
            if (chessboard[i][col] == 'Q') {
                return false;
            }
        }
        // 检查135度角是否有皇后
        for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
            if (chessboard[i][j] == 'Q') {
                return false;
            }
        }
        // 检查45度角是否有皇后
        for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
            if (chessboard[i][j] == 'Q') {
                return false;
            }
        }
        return true;
    }

五、贪心

649. Dota2 参议院

string predictPartyVictory(string senate) {
        // 局部最优:有一次权利机会,就消灭自己后面的对手
        // 全局最优:为自己的阵营赢取最大利益
        // 利用队列储存两个阵营成员下标,出队代表被消灭
        queue<int> teamR, teamD;
        for (int i = 0; i < senate.size(); i++) {
            if (senate[i] == 'R')    teamR.push(i);
            else    teamD.push(i);
        }
        if (teamR.empty())   return "Dire";
        if (teamD.empty())   return "Radiant";
        // 模拟投票过程
        while(!teamR.empty() && !teamD.empty()) {
            // 优先剥夺身后下一个位置的对方阵营权利
            if (teamR.front() < teamD.front()) {
                int R = teamR.front();
                teamD.pop();
                teamR.pop();
                // 该成员进入下一轮投票
                teamR.push(R + senate.size());
            }
            else {
                int D = teamD.front();
                teamR.pop();
                teamD.pop();
                // 该成员进入下一轮投票
                teamD.push(D + senate.size());
            }
        }
        return teamR.empty() ? "Dire" : "Radiant";
    }

要注意巧妙运用各种数据结构,尤其是流程模拟类问题。

1221. 分割平衡字符串

int balancedStringSplit(string s) {
        // 贪心:每遇到一个平衡子串就收录
        int res = 0;
        stack<char> st;
        for (char str : s) {
            if (st.empty() || str == st.top())   st.push(str);
            else {
                st.pop();
                if (st.empty())  res++;
            }
        }
        return res;
    }

本题可以不利用堆栈,利用一个count记录即可。

 870. 优势洗牌

vector<int> advantageCount(vector<int>& nums1, vector<int>& nums2) {
        vector<int> res(nums1.size());
        sort(nums1.begin(), nums1.end(), greater<int>());
        // 从大到小排序,同时还记录了原下标位置
        multimap<int, int, greater<int>> nums2_index;
        for (size_t i = 0; i < nums2.size(); i++) {
            nums2_index.insert(make_pair(nums2[i], i));
        }
        int big = 0;
        int small = nums1.size() - 1;
        // 田忌赛马策略:如果当前马比得过对方,就派出当前马;若比不过,派出最小马保存实力。
        for (auto it = nums2_index.begin(); it != nums2_index.end(); it++) {
            if (nums1[big] > it->first) {
                res[it->second] = nums1[big];
                big++;
            }
            else {
                res[it->second] = nums1[small];
                small--;
            }
        }
        return res;
    }

六、动态规划

132. 分割回文串 II[*]

int minCut(string s) {
        if (s.size() <= 1)    return 0;
        // dp[i]:下标为0-i区间内字符串分割为回文子串的最小分割数
        vector<int> dp(s.size());
        // 初始化:由于要取最小值,应将数组初始化为可能的最大值,即字符串长度
        for (int i = 0; i < s.size(); i++)    dp[i] = i;
        // 递推公式
        for (int i = 1; i < s.size(); i++) {
            // 0-i是回文则为0
            if (isPalindromic(s, 0, i)) {
                dp[i] = 0;
                continue;
            }    
            // 0-i非回文,j从0至i-1遍历,若j+1至i为回文,只需再分割一次:dp[i] = dp[j] + 1 
            // i一定会被更新,取最小值即可
            for (int j = 0; j < i; j++) {
                if (isPalindromic(s, j + 1, i)) {
                    dp[i] = min(dp[j] + 1, dp[i]);
                }
            }
        }
        return dp[s.size() - 1];
    }
    bool isPalindromic(const string& s, int start, int end) {
        for (int i = start, j = end; i < j; i++, j--) {
            if (s[i] != s[j])  return false;
        }
        return true;
    }

131. 分割回文串问题使用了回溯法,本题采用回溯法会超时。一般需要返回所有结果的采用回溯法,而只需要记录数量的问题可以采用动态规划法。

动态规划递推公式中存在min/max时,要注意初始化时将元素置为可能的最大值或最小值,以免影响结果。本题递推公式较难理解,dp[i]不止取决于上一个元素,而取决于从0至i-1所有的dp数组元素,因此需要j从0至i-1进行循环,找到最小值即可。

为提高速度,一种优化方法是仍利用动态规划法,采用二维dp[i][j]数组记录下标为i至j的字符串是否为回文串,保存这些信息在循环中使用。

673. 最长递增子序列的个数[*]

int findNumberOfLIS(vector<int>& nums) {
        if (nums.size() == 1)    return 1;
        int maxLength = 1;
        int res = 0;
        // length[i]:0-i的序列中最长递增子序列的长度【更新方式说明见题目300】
        // cnt[i]:以nums[i]结尾的最长递增子序列【子序列含nums[i]】的个数
        vector<int> length(nums.size(), 1);
        vector<int> cnt(nums.size(), 1);
        // 递推公式:
        for (int i = 1; i < nums.size(); i++) {
            for (int j = 0; j < i; j++) {
                // 出现了更大元素,需要进行遍历判断该元素的出现是否改变了最大递增子序列长度
                if (nums[i] > nums[j]) {
                    // 不影响最大递增子序列长度
                    if (length[j] + 1 > length[i]) {
                        length[i] = length[j] + 1;
                        cnt[i] = cnt[j];
                    }
                    // 最大子序列长度改变
                    else if (length[j] + 1 == length[i]) {
                        cnt[i] += cnt[j];
                    }
                }
            }
            maxLength = max(length[i], maxLength);
        }
        for (int i = 0; i < nums.size(); i++) {
            if (length[i] == maxLength)    res += cnt[i];
        }
        return res;
    }

【本题仍需深入思考】,其技巧在于定义了两个dp数组。


七、位运算

1356. 根据数字二进制下 1 的数目排序

class Solution {
private:
    static int BitCount(int num) {
        int count = 0;
        while (num > 0) {
            count += num & 1;
            num >>= 1;
        }
        return count;
    }
    static bool cmp(int a, int b) {
        int bitA = BitCount(a);
        int bitB = BitCount(b);
        if (bitA == bitB)    return a < b;
        else    return bitA < bitB;
    }

public: 
    vector<int> sortByBits(vector<int>& arr) {
        sort(arr.begin(), arr.end(), cmp);
        return arr;
    }
};

按位进行“与”运算即可,要注意sort函数的使用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值