LeetCode高频面试题记录(一)

LeetCode高频面试题记录(一)

K 个一组翻转链表 困难

class Solution {
    ListNode* reverseList(ListNode *a, ListNode *b){
        ListNode *pre=NULL, *cur = a, *nxt;
        while (cur != b){
            nxt = a->next;
            a = a->next;
            cur->next = pre;
            pre = cur;
            cur = nxt;
        }
        return pre;
    }
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
        if (head == NULL || head->next == NULL) return head;
        ListNode *a = head; ListNode *b = head;
        for (int i=0; i<k; i++){
            if (b == NULL)
                return a;
            b = b->next;
        }
        ListNode *node = reverseList(a, b);
        a->next = reverseKGroup(b, k);
        return node;
    }
   
};

买卖股票的最佳时机 III 困难
参考学习:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/solution/yi-ge-tong-yong-fang-fa-tuan-mie-6-dao-gu-piao-wen/

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if (prices.empty()) return 0;
        int max_k = 2;
        vector<vector<vector<int>>> dp(prices.size(),vector<vector<int>>(max_k+1,vector<int>(2,0)));
        for (int i=0; i<prices.size(); i++){
            for (int k=max_k; k>=1; k--){
                if (i-1 == -1){
                    dp[i][k][0] = 0;
                    dp[i][k][1] = -prices[i];
                    continue;
                }
                dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1]+prices[i]);
                dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0]-prices[i]);
            }
        }
        return dp[prices.size()-1][max_k][0];
    }
};

最佳买卖股票时机含冷冻期 中等

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if (prices.empty()) return 0;
        if (prices.size() < 2) return 0;
        vector<vector<int>> dp(prices.size(),vector<int>(2,0));
        dp[0][0] = 0; // 第一天,不持有股票
        dp[0][1] = -prices[0]; // 第一天,持有股票
        dp[1][0] = max(dp[0][0], dp[0][1] + prices[1]); // 第二天,不持有股票
        dp[1][1] = max(dp[0][0]-prices[1], dp[0][1]); // 第二天,持有股票
        for (int i=2; i<prices.size(); i++){
            dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i]);
            dp[i][1] = max(dp[i-1][1], dp[i-2][0]-prices[i]);
        }
        return dp[prices.size()-1][0];
    }
};

买卖股票的最佳时机 II 简单

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

三数之和 中等

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        if (nums.size() < 3) return {};
        sort(nums.begin(), nums.end());
        int left,right;
        vector<vector<int>> res;
        for (int i=0; i<nums.size(); i++){
            if (nums[i] > 0) break;
            if (i > 0 && nums[i] == nums[i-1]) continue;
            left = i + 1;
            right = nums.size()-1;
            while (left < right){
                int sum = nums[i] + nums[left] + nums[right];
                if (sum == 0){
                    res.push_back({nums[i], nums[left], nums[right]});
                    while (left<right && nums[left] == nums[left+1]) left++;
                    while (left<right && nums[right] == nums[right-1]) right--;
                    left++; right--;
                }
                else if (sum > 0) right--;
                else left++; 
            }
        }
        return res;
    }
};

二叉树中的最大路径和 困难

class Solution {
public:
    int PathSum(TreeNode* root, int& val) {
        if(root == NULL) return 0;
        // 若左子树和比0小,则它取值=0,因为加上负数,最大值会变小;右子树同理
        int left_max = max(PathSum(root->left, val), 0);
        int right_max = max(PathSum(root->right, val), 0);
        int new_path_sum = root->val + right_max + left_max;
        // 如果new_path_sum < maxn, 则最大路径不包括当前的root节点,只包括左(或右)节点
        val = max(val, new_path_sum);
        // 最大和路径只能是左子树 or 右子树,不能同时选
        return root->val + max(left_max, right_max);
    }
    int maxPathSum(TreeNode* root) {
        int val = INT_MIN;
        PathSum(root, val);
        return val;
    }
};

二叉树的右视图 中等
BFS

class Solution {
public:
    vector<int> rightSideView(TreeNode* root) {
        if (root == NULL) return {};
        vector<int> res;
        queue<TreeNode*> que; que.push(root);
        while (!que.empty()){
            int len = que.size();
            for (int i=0; i<len; i++){
                auto temp = que.front();
                que.pop();
                if (i == len-1)
                    res.push_back(temp->val);
                if (temp->left != NULL) que.push(temp->left);
                if (temp->right != NULL) que.push(temp->right);
            }
        }
        return res;
    }
};

DFS

class Solution {
    vector<int> res;
public:
    void dfs(TreeNode* root, int depth) {
        if (root == NULL) return;
        // 先访问 当前节点,再递归地访问 右子树 和 左子树。
        // 如果当前节点所在深度还没有出现在res里,说明在该深度下当前节点是第一个被访问的节点,因此将当前节点加入res中。
        if (depth == res.size())  
            res.push_back(root->val);
        depth++;
        dfs(root->right, depth);//注意这里是先访问右节点
        dfs(root->left, depth);
    }
    vector<int> rightSideView(TreeNode* root) {
        if (root == NULL) return {};
        dfs(root, 0);
        return res;
    }
};

无重复字符的最长子串 中等偏难
学习网址:https://leetcode-cn.com/problems/find-all-anagrams-in-a-string/solution/hua-dong-chuang-kou-tong-yong-si-xiang-jie-jue-zi-/

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        if (s.empty()) return 0;
        int res = 1; int k = 0;
        for (int i=1; i<s.length(); i++){
            int temp = 1;
            for (int j=k; j<i; j++){
                if (s[i] == s[j])
                    k = j + 1;
                else
                    temp++;
            }
            res = max(res, temp);
        }
        return res;
    }
};

滑窗思路

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        unordered_map<char, int> window;
        int left = 0, right = 0;
        int res = 0; // 记录结果
        while (right < s.size()) {
            char c = s[right];
            right++;
            window[c]++;// 进行窗口内数据的一系列更新
            // 判断左侧窗口是否要收缩
            while (window[c] > 1) {
                char d = s[left];
                left++;
                window[d]--;// 进行窗口内数据的一系列更新
            }
            // 在这里更新答案
            res = max(res, right - left);
        }
        return res;
    }
};

找到字符串中所有字母异位词 中等偏难
滑窗模板——必须牢记

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        unordered_map<char, int> window;
        unordered_map<char, int> need;
        for (char c:p) need[c]++;
        int left = 0; int right = 0;
        int valid = 0; vector<int> res;
        while (right < s.length()){
            char c = s[right];
            right++;
            if (need.count(c) > 0){//扩大窗口
                window[c]++;
                if (window[c] == need[c])
                    valid++;
            }
            while (right-left >= p.size()){
                if (valid == need.size())//处理数据
                    res.push_back(left);
                char d = s[left];
                left++;
                if (need.count(d) > 0){//缩小窗口
                    if (window[d] == need[d])
                        valid--;
                    window[d]--;
                }
            }
        }
        return res;
    }
};

最小覆盖子串 困难

string minWindow(string s, string t) {
    unordered_map<char, int> need, window;
    for (char c : t) need[c]++;

    int left = 0, right = 0;
    int valid = 0;
    // 记录最小覆盖子串的起始索引及长度
    int start = 0, len = INT_MAX;
    while (right < s.size()) {
        // c 是将移入窗口的字符
        char c = s[right];
        // 右移窗口
        right++;
        // 进行窗口内数据的一系列更新
        if (need.count(c)) {
            window[c]++;
            if (window[c] == need[c])
                valid++;
        }
        // 判断左侧窗口是否要收缩
        while (valid == need.size()) {
            // 在这里更新最小覆盖子串
            if (right - left < len) {
                start = left;
                len = right - left;
            }
            // d 是将移出窗口的字符
            char d = s[left];
            // 左移窗口
            left++;
            // 进行窗口内数据的一系列更新
            if (need.count(d)) {
                if (window[d] == need[d])
                    valid--;
                window[d]--;
            }                    
        }
    }
    // 返回最小覆盖子串
    return len == INT_MAX ? "" : s.substr(start, len);
}

合并两个有序数组 简单
看似简单,若是没思路还是做不出来,这道题要从后比较,避免移位

class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        int i = m-1; int j = n-1; int t = m-1;
        while (i >= 0 && j >= 0){
            if (nums1[i] < nums2[j]){
                nums1[t+n] = nums2[j];
                j--; 
            }
            else{
                nums1[t+n] = nums1[i];
                i--;
            }
            t--;
        }
        for (int i=j; i>=0; i--,t--)
            nums1[t+n] = nums2[i];
    }
};

将有序数组转换为二叉搜索树 简单

class Solution {
public:
    TreeNode* sortedArrayToBST(vector<int> nums) {
        if (nums.empty()) return NULL;
        int mid = nums.size() / 2;
        TreeNode* root = new TreeNode(nums[mid]);
        vector<int> leftTree(nums.begin(), nums.begin()+mid);//这里要注意,不是mid+1
        vector<int> rightTree(nums.begin()+mid+1,nums.end());
        root->left = sortedArrayToBST(leftTree);
        root->right = sortedArrayToBST(rightTree);
        return root;
    }
};

先序遍历构造二叉树 简单

class Solution {
public:
    TreeNode* bstFromPreorder(vector<int>& preorder) {
        if (preorder.empty()) return nullptr;
        TreeNode* root = new TreeNode(preorder[0]);
        int temp; 
        for (int i=0;i<preorder.size();i++) if (preorder[0]<preorder[i]) {temp=i;break;}
        preorder.erase(preorder.begin());
        vector<int> lpreorder(preorder.begin(), preorder.begin()+temp-1);
        vector<int> rpreorder(preorder.begin()+temp-1, preorder.end());
        root->left = bstFromPreorder(lpreorder);
        root->right = bstFromPreorder(rpreorder);
        return root;
    }
};

平衡二叉树 简单

class Solution {
    int depth(TreeNode* root){
        if (root == NULL) return 0;
        int leftl = depth(root->left);
        int rightr = depth(root->right);
        return max(leftl, rightr)+1;
    }
public:
    bool isBalanced(TreeNode* root) {
        if (root == NULL) return true;
        return isBalanced(root->left) && isBalanced(root->right) && (abs(depth(root->left)-depth(root->right))<2);
    }
};

这个方法有大量重复递归,相对慢了。下面方法进行了剪枝

class Solution {
    int depth(TreeNode* root){
        if (root == NULL) return 1;
        int l = depth(root->left);
        if (l == -1) return -1;
        int r = depth(root->right);
        if (r == -1) return -1;
        return (abs(l-r)<2) ? max(l, r)+1 : -1;
    }
public:
    bool isBalanced(TreeNode* root) {
        return depth(root) != -1;
    }
};

二叉树的最近公共祖先 中等

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (!root || root == p || root == q) return root;
        TreeNode *left = lowestCommonAncestor(root->left, p, q);
        TreeNode *right = lowestCommonAncestor(root->right, p, q);
        if (left == NULL) return right;
        if (right== NULL) return left;
        return root;
    }
};

零钱兑换 II 中等

class Solution {
public:
    int change(int amount, vector<int>& coins) {
        vector<int> dp(amount+1, 0); dp[0] = 1;
        for (int i=0; i<coins.size(); i++){
            for (int j=coins[i]; j<=amount; j++){
                dp[j] += dp[j-coins[i]];
            }
        }
        return dp[amount];
    }
};

零钱兑换 中等

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        vector<int> dp(amount+1, amount+1); dp[0] = 0;
        for (int i=1; i<=amount; i++){
            int temp = amount+1;
            for (int j=0; j<coins.size(); j++){
                if (i >= coins[j])
                    temp = min(dp[i-coins[j]], temp);
            }
            dp[i] = temp + 1;
        }
        return dp[amount] > amount ? -1 : dp[amount];
    }
};

删除排序链表中的重复元素 简单
思路理清楚就不难,链表要注意头尾节点的处理,这个搞清楚了就简单了

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        if (head == NULL || head->next == NULL) return head;
        ListNode* root = head;
        while (head != NULL && head->next != NULL){
            if (head->val == head->next->val)
                head->next = head->next->next;
            else
                head = head->next;
        }
        return root;
    }
};

删除排序链表中的重复元素 II 中等
这道题一定要理清思路,不然做不出来,实际思路其实不难,但一定要理清。链表题解题就是一定要思路清晰。借助头结点是常用方法

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
		ListNode* dummy = new ListNode(-1);
		dummy->next = head;
		ListNode *cur = dummy, *pre;
		while (cur){
			pre = cur;
			cur = cur->next;
			while (cur && cur->next && cur->next->val == cur->val){
				int temp = cur->val;
				while (cur && cur->val == temp)
					cur = cur->next;		
			}
			pre->next = cur;
		}
		return dummy->next;
    }
};

反转链表 简单

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if (head == NULL || head->next == NULL) return head;
        ListNode* node = reverseList(head->next);
        head->next->next = head;
        head->next = NULL;
        return node;//注意返回谁
    }
};

两两交换链表中的节点 中等难度
这两道题有助于把递归可以想清楚,静态思考与动态思考。

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        if (head == NULL || head->next == NULL) return head;
        ListNode* node = head->next;
        head->next = swapPairs(head->next->next);
        node->next = head;
        return node;
    }
};

数组第K个最大数 中等
这道题我们可以构建最小堆来实现。

class Solution {
    void heap(vector<int>& nums, int p){
        for (int parant=p; parant*2+1<nums.size();){
            int child = parant*2+1;
            if (child+1 < nums.size() && nums[child] > nums[child+1])
                child++;
            if (nums[parant] <= nums[child])
                break;
            swap(nums[parant], nums[child]);
            parant = child;
        }
    }
    void buildHeap(vector<int>& nums){
        for (int i=nums.size()/2; i>=0; i--)
            heap(nums, i);
    }
public:
    int findKthLargest(vector<int>& nums, int k) {
        vector<int> temp(nums.begin(), nums.begin()+k);
        buildHeap(temp);//这里我们是构建最小堆,最大堆其实不能很好的完成
        for (int i=k; i<nums.size(); i++){
            if (temp[0] < nums[i]){
                temp[0] = nums[i];//插入元素
                heap(temp, 0);//再次构建
            }
        }
        return temp[0];
    }
};

螺旋矩阵 中等
这道题还是一样,要求把思路理清楚

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        if (matrix.empty()) return {};
        int l=0; int t=0; int r=matrix[0].size()-1; int b=matrix.size()-1;
        vector<int> res;
        while (1){
            for (int i=l; i<=r; i++) res.push_back(matrix[t][i]);
            if (++t > b) break;
            for (int i=t; i<=b; i++) res.push_back(matrix[i][r]);
            if (--r < l) break;
            for (int i=r; i>=l; i--) res.push_back(matrix[b][i]);
            if (--b < t) break;
            for (int i=b; i>=t; i--) res.push_back(matrix[i][l]);
            if (++l > r) break;
        }
        return res;
    }
};

将每个元素替换为右侧最大元素 简单
看似简单并不简单,要想到才行
我个人用了双端队列来解决

class Solution {
public:
    vector<int> replaceElements(vector<int>& arr) {
        deque<int> que;
        int len = arr.size()-1;
        que.push_front(arr[len]);
        for (int i=len; i>=1; i--){
            int temp = que.front();
            if (arr[i] >= temp)
                que.push_front(arr[i]);
        }
        vector<int> res;
        for (int i=0; i<arr.size()-1; i++){
            if (arr[i] == que.front())
                que.pop_front();
            res.push_back(que.front());
        }
        res.push_back(-1);
        return res;
    }
};

不是最优解,下面解法是最优解

class Solution {
public:
    vector<int> replaceElements(vector<int>& arr) {
        int n = arr.size();
        vector<int> ans(n);
        ans[n - 1] = -1;
        for (int i = n - 2; i >= 0; --i) {
            ans[i] = max(ans[i + 1], arr[i + 1]);
        }
        return ans;
    }
};

接雨水 困难

class Solution {
public:
    int trap(vector<int>& height) {
        int left_max = 0; int right_max = 0;
        int res = 0;
        int left = 0; int right = height.size()-1;
        while (left < right){
            if (height[left] <= height[right]){
                if (height[left] >= left_max)
                    left_max = height[left];
                else
                    res += left_max - height[left];
                left++;
            }
            else{
                if (height[right] >= right_max)
                    right_max = height[right];
                else
                    res += right_max - height[right];
                right--;
            }
        }
        return res;
    }
};

相交链表 简单
链表的题都是看似简单,实际要注意NULL的问题

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        if (headA == NULL || headB == NULL) return NULL;
        ListNode *tempA = headA;
        ListNode *tempB = headB;
        while (tempA != tempB){
            tempA = tempA->next;
            tempB = tempB->next;
            if (tempA == NULL && tempB == NULL)//这里同时为空时跳出,避免死循环
                return NULL;
            if (tempA == NULL)
                tempA = headB;
            if (tempB == NULL)
                tempB = headA;
        }
        return tempA;
    }
};

单词拆分 中等

class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        unordered_set<string> wordDictSet;
        for (auto word: wordDict) 
            wordDictSet.insert(word);
        vector<bool> dp(s.size()+1, false); dp[0] = true;
        for (int i=1; i<=s.size(); ++i) {
            for (int j=0; j<i; ++j) {
                if (dp[j] && wordDictSet.find(s.substr(j, i-j)) != wordDictSet.end()) {
                    dp[i] = true;
                    break;
                }
            }
        }
        return dp[s.size()];
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

华为云计算搬砖工

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值