LeetCode热题100刷题笔记(上)

(一)两数之和

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
       unordered_map<int, int> map;  // <nums[i], i>
       for(int i = 0; i < nums.size(); i ++){
        auto iter = map.find(target - nums[i]);
        if(iter != map.end()) return {iter -> second, i};
        else map.insert(pair<int, int>(nums[i], i));
       }
       return {};
    }
};

(二)字母异位词分组

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        unordered_map<string, vector<string>> result;
        for(int i = 0; i < strs.size(); i ++){
            string tempstr = strs[i];
            sort(tempstr.begin(), tempstr.end());
            result[tempstr].push_back(strs[i]);
        }
        vector<vector<string>> out;
        for(const auto& pair: result){
            out.push_back(pair.second);
        }
        return out;
    }
};

(三)最长连续序列

class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
        if(nums.size() == 0) return 0;
        sort(nums.begin(), nums.end());
        int result = 1;
        int slow = 0;
        int flag = 0;
        for(int i = 1; i < nums.size(); i ++){
            int count = 1; // 每一次进入新的一组连续序列,都重置count
            // 统计累计的连续长度以及其中重复的次数
            while(i < nums.size() && (nums[i] == nums[i - 1] + 1 || nums[i] == nums[i - 1])){     
                count ++;
                if(nums[i] == nums[i - 1]) flag ++;
                i ++;
            }
            // 记录下当前最长的连续序列
            result = max(result, count - flag);
            // 进入下一个序列的统计,flag清0,slow表示下一个序列的开始位置
            flag = 0;
            slow = i;
        }
        return result;
    }
};

(四)移动零

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int slow = 0;
        for(int i = 0; i < nums.size(); i ++){
            if(nums[i] != 0){
                nums[slow] = nums[i];
                slow ++;
            }
        }
        for(int i = slow; i < nums.size(); i ++){
            nums[i] = 0;
        }
    }
};

(五)盛最多水的容器

class Solution {
public:
    int maxArea(vector<int>& height) {
        // 因为两条线相差越远,水的宽度越大,所以从两边向中间试探
        // 因为水高度是由最短的边决定的,所以移动最短边,向中间找到一条比他高的边,计算面积是否更大
        int left = 0;
        int right = height.size() - 1;
        int result = 0;
        while(left != right){
            // 这里是线,所以宽度不需要减1
            result = max(result, (right - left) * min(height[left], height[right]));
            if(height[left] < height[right]){
                left ++;
            }else{
                right --;
            }
        }
        return result;
    }
};

(六)三数之和

class Solution {
public: 
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> result;
        sort(nums.begin(), nums.end());
        for(int i = 0; i < nums.size(); i ++){
            if(i > 0 && nums[i] == nums[i - 1]) continue;
            int left = i + 1;
            int right = nums.size() - 1;
            while(left < right){
                if(nums[i] + nums[left] + nums[right] < 0) left ++;
                else if(nums[i] + nums[left] + nums[right] > 0) right --;
                else{
                    result.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 --;
                }
            }
        }
        return result;
    }
};

(七)接雨水

class Solution {
public:
    int trap(vector<int>& height) {
        stack<int> st;
        st.push(0);
        int result = 0;
        for(int i = 1; i < height.size(); i ++){
            if(height[i] < height[st.top()]) st.push(i);
            else if(height[i] == height[st.top()]){
                st.pop();
                st.push(i);
            }else{
                while(!st.empty() && height[i] > height[st.top()]){
                    int mid = st.top();
                    st.pop();
                      // 这里pop出去后要判断栈内是否还有元素,就是mid左边是否有比他高的元素
                    if(!st.empty()){
                        int h = min(height[i], height[st.top()]) - height[mid];
                        int w = i - st.top() - 1;
                        result += h * w;
                    }
                }
                st.push(i);
            }
        }
        return result;
    }
};

(八)无重复字符的最长子串

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int slow = 0;
        int result = 0;
        unordered_set<char> set; // 因为不确定长度,所以用set
        for(int i = 0; i < s.size(); i ++){
            if(set.find(s[i]) == set.end()){
                set.insert(s[i]);
                result = max(result, i - slow + 1);
            } 
            else{ // 之前已经存在该字符,将left左移,并删除对应的元素,直到set中不存在当前字符
                while(set.find(s[i]) != set.end()){
                    // unordered_set删除元素,erase
                    set.erase(s[slow]);
                    slow ++;
                }
                // 将当前字符插入set中
                set.insert(s[i]);            
            }
        }
        return result;
    }
};

(九)找到字符串中所有字母异位词

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        if(p.size() > s.size()) return {};
        vector<int> result;
        vector<int> hashs(26);
        vector<int> hashp(26);
        for(int i = 0; i < p.size(); i ++){
            hashp[p[i] - 'a'] ++;
        }
        int left = 0;
        for(int i = 0; i < s.size(); i ++){
            hashs[s[i] - 'a'] ++;
            if(i >= p.size() - 1){
                // 如果vector里面的元素类型是简单类型(内置类型),可以直接使用“==”或者“!=”进行比较
                if(hashp == hashs) result.push_back(left);
                hashs[s[left] - 'a'] --;
                left ++;
            }
        }
        return result;
    }
};

(十)和为 K 的子数组

class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        // 前缀和
        // 因为是连续非空序列,确定其元素总和,pre[j] - pre[i]
        // 用map记录下前面所有的前缀和,直接查找pre[j] - k
        // 出现的次数不止一次,用map记录某个前缀和出现的次数
        unordered_map<int, int> map;
        vector<int> pre(nums.size(), 0);
        int result = 0;
        int sum = 0;
        map[0] = 1; // 从0开始的连续数组和为k,那么pre[-1] = 0;
        for(int i = 0; i < nums.size(); i ++){
            sum += nums[i]; // 记录当前前缀和
            if(map.find(sum - k) != map.end()) result += map[sum - k];
            map[sum] ++;
        }
        return result;      
    }
};

(十一)滑动窗口最大值

class Solution {
public:
    deque<int> que;
    void push(int value){
        // 将队尾所有比当前元素小的都清理掉
        while(!que.empty() && value > que.back()){
            que.pop_back();
        }
        que.push_back(value);
    }
    
    void pop(int value){
        if(!que.empty() && que.front() == value){
            que.pop_front();
        }
    }

    int getMaxValue(){
        return que.front();
    }

    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        vector<int> result;
        for(int i = 0; i < k; i ++){
            push(nums[i]);
        }
        result.push_back(getMaxValue());
        for(int i = k; i < nums.size(); i ++){
            push(nums[i]);
            pop(nums[i - k]);
            result.push_back(getMaxValue());
        }
        return result;      
    }
};

(十二)最小覆盖子串

class Solution {
public:
    string minWindow(string s, string t) {
        unordered_map<char, int> smap;
        unordered_map<char, int> tmap;
        for(int i = 0; i < t.size(); i ++){
            tmap[t[i]] ++;
        }
        int left = 0;
        int count = 0;
        int minLenth = INT_MAX;
        string res;
        for(int i = 0; i < s.size(); i ++){
            smap[s[i]] ++;
            // 记录s中有效的增加
            if(tmap.find(s[i]) != tmap.end() && smap[s[i]] <= tmap[s[i]]) count ++;
            // 有效字母等于t的长度
            if(count == t.size()){    
                // 缩减左指针,直到找到最短的子串           
                while(left < i && (tmap.find(s[left]) == tmap.end() || smap[s[left]] > tmap[s[left]])){
                    // 注意这里左指针右移的时候要将map中对应的value减小
                    smap[s[left]] --;
                    left ++;
                } 
                if(i - left + 1 < minLenth){
                    // 更新最短子串长度
                    minLenth = i - left + 1;
                    // substr(开始下表,子串长度)
                    res = s.substr(left, i - left + 1);
                }
                // 跳出当前找到的子串(去掉第一个字母),注意要改变相应的count、map值
                smap[s[left]] --;
                left ++;
                count --;
            }
        }
        return res;
    }
};

(十三)最大子数组和

贪心法

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int count = 0;
        int result = INT_MIN;
        for(int i = 0; i < nums.size(); i ++){
            count += nums[i];
            result = max(result, count);
            if(count < 0) count = 0;
        }
        return result;
    }
};

动态规划法

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

(十四)合并区间

class Solution {
public:
    static bool cmp(const vector<int>& a, const vector<int>& b){
        return a[0] < b[0];
    }
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        sort(intervals.begin(), intervals.end(), cmp);
        vector<vector<int>> result;
        result.push_back(intervals[0]);
        for(int i = 1; i < intervals.size(); i ++){
            if(intervals[i][0] > result[result.size() - 1][1]){
                result.push_back(intervals[i]);
            }else{
                result[result.size() - 1][1] = max(result[result.size() - 1][1], intervals[i][1]);
            }
        }
        return result;      
    }
};

(十五)轮转数组

class Solution {
public:
    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());
    }
};

(十六)除自身以外数组的乘积

class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) {
        vector<int> preffix(nums.size());
        vector<int> suffix(nums.size());
        vector<int> result(nums.size());
        preffix[0] = nums[0];
        suffix[nums.size() - 1] = nums[nums.size() - 1];
        // 前缀和的思想,再同样思想求一个后缀和,除自身以外的乘积就是这两部分的乘积
        for(int i = 1; i < nums.size(); i ++){
            preffix[i] = preffix[i - 1] * nums[i];
        }
        for(int j = nums.size() - 2; j >= 0; j --){
            suffix[j] = suffix[j + 1] * nums[j];
        }
        result[0] = suffix[1];
        result[nums.size() - 1] = preffix[nums.size() - 2];
        for(int i = 1; i < nums.size() - 1; i ++){
            result[i] = preffix[i - 1] * suffix[i + 1];
        }
        return result;
    }
};

(十七)缺失的第一个正数

class Solution {
public:
    int firstMissingPositive(vector<int>& nums) {
        for(int i = 0; i < nums.size(); i ++){
            // 将nums[i] 交换到它正确的位置
            // 交换时注意nums[i]是否符合交换范围,要将其交换到下表为nums[i] - 1处
            // 就要nums[i] - 1 >= 0 并且 < nums.size()
            // 同时为了放置交换出现死循环,要求交换位置的数和原来的数不相同
            while(nums[i] != i + 1 && nums[i] > 0 &&  nums[i] <= nums.size() && nums[i] != nums[nums[i] - 1]){
                swap(nums[i], nums[nums[i] - 1]);
            }
        }
        for(int i = 0; i < nums.size(); i ++){
            if(nums[i] != i + 1) return i + 1;
        }
        return nums.size() + 1;
    }
};

(十八)矩阵置零

class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {
        int xx, yy;
        int flag = 0; // 记录是否找到了为0的元素
        // xx,yy记录第一个为0的元素所在的行和列,这这一行和列来标记整个矩阵中0所在的行和列
        for(int i = 0; i < matrix.size(); i ++){
            for(int j = 0; j < matrix[0].size(); j ++){
                if(matrix[i][j] == 0){
                    xx = i;
                    yy = j;
                    flag = 1;
                    break;
                }
            }
        }
        // 根本不存在为0的元素,下面的都不要做
        if(flag == 0) return;
    
       // 用xx行和yy列来记录其余行列是否应该清零
        for(int i = 0; i < matrix.size(); i ++){
            for(int j = 0; j < matrix[0].size(); j ++){
                if(matrix[i][j] == 0){
                    matrix[xx][j] = 0;
                    matrix[i][yy] = 0;
                }
            }
        }

        // 检查记录元素所在的行和列,(除了记录元素外)
        // 这里要跳过记录元素,因为如果将其对应的行列全部置0了,之前做的标记就都没用了
        // 行中某一元素为0,将其对应的列全部置0
        // 列中某一元素为0,将其对应的行全部置0
        for(int j = 0; j < matrix[0].size(); j ++){
            if(j == yy) continue;
            if(matrix[xx][j] == 0){
                for(int i = 0; i < matrix.size(); i ++){
                    matrix[i][j] = 0;
                }
            }
        }
        for(int i = 0; i < matrix.size(); i ++){
            if(i == xx) continue;
            if(matrix[i][yy] == 0){
                for(int j = 0; j < matrix[0].size(); j ++){
                    matrix[i][j] = 0;
                }
            }
        }
        // 最后处理标记元素的行列,全部置0
        for(int i = 0; i < matrix.size(); i ++){
            matrix[i][yy] = 0;
        }
        for(int j = 0; j < matrix[0].size(); j ++){
            matrix[xx][j] = 0;
        }
    }
};

(十九)螺旋矩阵

(1)套用螺旋矩阵写法,但是这里循环结束的条件、最后没有遍历到的元素都和螺旋举证不一样,细节处理比较麻烦

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        int m = matrix.size();
        int n = matrix[0].size();
        vector<int> result;
        if(m == 1) return matrix[0];
        if(n == 1){
            for(int i = 0; i < m; i ++){
                result.push_back(matrix[i][0]);
            }
            return result;
        }
        int size = min(m, n)/2;

        int offset = 1;
        int i, j;
        int starty = 0;
        int startx = 0;
        int count = 0;

        while(size --){
            for(j = starty; j < n - offset; j ++){
                result.push_back(matrix[startx][j]);
                count ++;
            }
            for(i = startx; i < m - offset; i ++){
                result.push_back(matrix[i][j]);
                count ++;
            }
            for(; j > starty; j --){
                result.push_back(matrix[i][j]);
                count ++;
            }
            for(; i > startx; i --){
                result.push_back(matrix[i][j]);
                count ++;
            }
            
            starty ++;
            startx ++;
            offset ++;
        }
        
        if(m == n && m % 2 != 0) {
            result.push_back(matrix[n/2][n/2]);
        }
        if(n > m && count < m * n){            
            for(j = j + 1; j <= n - offset; j ++){
                result.push_back(matrix[m/2][j]);
            }
        }
        if(m > n && count < m * n){            
            for(i = i + 1; i <= m - offset; i ++){
                result.push_back(matrix[i][n/2]);
            }
        }
        return result;
    }
};

(2) 另外一种写法,比较灵活,每次遍历完一行/一列,动态判断是否需要结束,同时改变边界值。

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        if(matrix.empty() || matrix[0].empty()) return {};
        vector<int> res;
        int m = matrix.size(), n = matrix[0].size();
        // 确定上下左右四条边的位置
        int up = 0, down = m - 1, left = 0, right = n - 1;
        while (true)
        { 
            for (int i = left; i <= right; i++) res.push_back(matrix[up][i]);
            if (++up > down) break;
            for (int i = up; i <= down; i++) res.push_back(matrix[i][right]);
            if (--right < left) break;
            for (int i = right; i >= left; i--) res.push_back(matrix[down][i]);
            if (--down < up) break;
            for (int i = down; i >= up; i--) res.push_back(matrix[i][left]);
            if (++left > right) break;
        }
        return res;
    }
};

(二十)旋转图像

class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        // 先沿着左对角线反转,在逐行逆序
        for(int i = 0; i < matrix.size(); i ++){
            for(int j = i; j < matrix[0].size(); j ++){
                swap(matrix[i][j], matrix[j][i]);
            }
        }
        for(int i = 0; i < matrix.size(); i ++){
            reverse(matrix[i].begin(), matrix[i].end());
        }
    }
};

(二十一)搜索二维矩阵 II

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        // 搜索法,找一个初始元素,根据target和初始元素的大小关系,移动位置,直到找到为止
        // 就是要选择一个点,使得横向和纵向移动,martix能够有不同的变化
        // 左上角:往右往下移动,martix值都变大,无法区分,不可用
        // 右上角:往左martix变小,往下martix变大,可区分,可用
        // 左下角:往右martix变大,往上martix变小,可区分,可用  
        // 右下角:往左往上移动,martix都变小,不可区分,不可用
        int i = 0;
        int j = matrix[0].size() - 1;
        while(i < matrix.size() && j >= 0){
            if(matrix[i][j] == target) return true;
            else if(matrix[i][j] < target) i ++;
            else if(matrix[i][j] > target) j --;
        }
        return false;
    }
};

(二十二)相交链表

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        int sizeA = 0;
        int sizeB = 0;
        ListNode* curA = headA;
        ListNode* curB = headB;
        while(curA){
            curA = curA -> next;
            sizeA ++;
        }
        while(curB){
            curB = curB -> next;
            sizeB ++;
        }
        curA = headA;
        curB = headB;
        // 确保curA和sizeA对应的较长的链表
        if(sizeB > sizeA){
            swap(curA, curB);
            swap(sizeA, sizeB);
        }
        int between = sizeA - sizeB;
        while(between --){
            curA = curA -> next;
        }
        while(curA){
            if(curA == curB) return curA;
            curA = curA -> next;
            curB = curB -> next;
        }
        return NULL;
    }
};

(二十三)反转链表

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* first = head;
        ListNode* second = NULL;
        while(first != NULL){
            ListNode* temp = first -> next;
            first -> next = second;
            second = first;
            first = temp;
        }
        head = second;
        return head;
    }
};

(二十四)回文链表

class Solution {
public:
    bool isPalindrome(ListNode* head) {
        ListNode* cur = head;
        vector<int> vec;
        while(cur){
            vec.push_back(cur -> val);
            cur = cur -> next;
        }
        vector<int> temp = vec;
        reverse(vec.begin(), vec.end());
        return temp == vec;
    }
};

(二十五)环形链表

class Solution {
public:
    bool hasCycle(ListNode *head) {
        ListNode* fast = head;
        ListNode* slow = head;
        while(fast != NULL && fast -> next != NULL){
            fast = fast -> next -> next;
            slow = slow -> next;
            if(fast == slow) return true;
        }
        return false;
    }
};

(二十六)环形链表 II

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode* fast = head;
        ListNode* slow = head;
        while(fast != NULL && fast -> next != NULL){
            fast = fast -> next -> next;
            slow = slow -> next;
            if(fast == slow){
                ListNode* first = head;
                ListNode* second = fast;    
                // x = (n - 1)(y + z) + z
                while(first != second){
                    first = first -> next;
                    second = second -> next;
                }   
                return first;                      
            }
        }
        return NULL;
    }
};

(二十七)合并两个有序链表

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        ListNode* cur1 = list1;
        ListNode* cur2 = list2;
        ListNode* newHead = new ListNode();
        ListNode* pre = newHead;
        while(cur1 != NULL && cur2 != NULL){
            if(cur1 -> val < cur2 -> val){
                pre -> next = cur1;
                pre = cur1;
                cur1 = cur1 -> next;
            }else{
                pre -> next = cur2;
                pre = cur2;
                cur2 = cur2 -> next;
            }
        }
        if(cur1 != NULL){
            pre -> next = cur1;
        }
        if(cur2 != NULL){
            pre -> next = cur2;
        }
        return newHead -> next;
    }
};

(二十八)两数相加

class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode* cur1 = l1;
        ListNode* cur2 = l2;
        ListNode* preHead = new ListNode();
        ListNode* cur = preHead;
        int addNum = 0;
        while(cur1 != NULL && cur2 != NULL){
            int temp = cur1 -> val + cur2 -> val + addNum;
            cur -> next = new ListNode(temp % 10);
            cur = cur -> next;
            addNum = temp / 10;
            cur1 = cur1 -> next;
            cur2 = cur2 -> next;
        }
        while(cur1 != NULL){
            int temp = cur1 -> val + addNum;
            cur -> next = new ListNode(temp % 10);
            addNum = temp / 10;
            cur = cur -> next;
            cur1 = cur1 -> next;
        }
        while(cur2 != NULL){
            int temp = cur2 -> val + addNum;
            cur -> next = new ListNode(temp % 10);
            addNum = temp / 10;
            cur = cur -> next;
            cur2 = cur2 -> next;
        }
        if(addNum != 0){
            cur -> next = new ListNode(addNum);
        }
        return preHead -> next;
    }
};

(二十九)删除链表的倒数第 N 个结点

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        // 使用preHead,使得链表删除第一个节点和删除其余节点是一样的
        ListNode* preHead = new ListNode();
        preHead -> next = head;
        ListNode* fast = preHead;
        ListNode* slow = preHead;
        while(n --){
            fast = fast -> next;
        }
        // 删除一个节点要知道该节点前一个节点,因此fast要多走一步
        fast = fast -> next;
        while(fast != NULL){
            fast = fast -> next;
            slow = slow -> next;
        }
        slow -> next = slow -> next -> next;
        return preHead -> next;
    }
};

(三十)两两交换链表中的节点

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode* preHead = new ListNode();
        preHead -> next = head;
        ListNode* fast = preHead -> next;
        ListNode* slow = preHead;
        // 这里必须保证有两个节点才能交换
        while(fast != NULL && fast -> next != NULL){
            ListNode* temp = fast -> next -> next;
            slow -> next = fast -> next;
            fast -> next -> next = fast;
            fast -> next = temp;
            slow = fast;
            fast = temp;
        }
        return preHead -> next;
    }
};

(三十一)K 个一组翻转链表

方法一:

class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
        ListNode* cur = head;
        int count = 0;
        while(cur){
            cur = cur -> next;
            count ++;
        }
        int num = count / k;
        ListNode* preHead = new ListNode();
        preHead -> next = head;
        ListNode* temp1 = preHead;
        ListNode* temp2 = preHead;
        ListNode* temp3 = preHead;
        while(num --){
            for(int i = 0; i < k; i ++){
                temp1 = temp1 -> next;
            }
            temp2 = temp1 -> next;
            ListNode* fast = temp3 -> next;
            ListNode* slow = temp2;
            while(fast != temp2){
                ListNode* temp = fast -> next;
                fast -> next = slow;
                slow = fast;
                fast = temp;
            }
            ListNode* temp4 = temp3 -> next;
            temp3 -> next = temp1;
            temp1 = temp4;
            temp2 = temp4;
            temp3 = temp4;
        }
        return preHead -> next;
    }
};

方法二:

每次都将cur后面一个节点提到这一组节点的最前面(pre→next位置),同时由于位置的交换cur节点也在不断向后,因此每次要交换的都是cur后面的一个节点,即next节点。

class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        ListNode dummy = new ListNode(0), prev = dummy, curr = head, next;
        dummy.next = head;
        int length = 0;
        while(head != null) {
            length++;
            head = head.next;
        }
        head = dummy.next;
        for(int i = 0; i < length / k; i++) {
            for(int j = 0; j < k - 1; j++) {
                next = curr.next;
                curr.next = next.next;
                next.next = prev.next;
                prev.next = next;
            }
            prev = curr;
            curr = prev.next;
        }
        return dummy.next;
    }
}

(三十二)随机链表的复制

class Solution {
public:
    Node* copyRandomList(Node* head) {
        // 用map存储Node指针,将原链表节点的指针(key)和新创建的节点的指针(value)一一对应起来
        unordered_map<Node*, Node*> mp;
        for(Node* node = head; node; node = node -> next){
            mp[node] = new Node(node -> val);
        }
        for(Node* node = head; node; node = node -> next){
            mp[node] -> next = mp[node -> next];
            mp[node] -> random = mp[node -> random];
        }
        return mp[head];       
    }
};

(三十三)排序链表

方法一:归并

class Solution {
    public ListNode sortList(ListNode head) {
        return mergeSort(head);
    }

    // 归并排序
    private ListNode mergeSort(ListNode head){
        // 如果没有结点/只有一个结点,无需排序,直接返回
        if (head==null||head.next==null) return head;
        // 快慢指针找出中位点
        // 这里找到的是中位点的前一个,因为之后要将中位点置为NULL,而中位点的指向必须保留,所以用slow记录其前一个,这样即使slow -> next置为NULL,也能根据slow 回溯时找到之前的中位点
        ListNode slowp=head,fastp=head.next,next,l,r;
        while (fastp!=null&&fastp.next!=null){
            slowp=slowp.next;
            fastp=fastp.next.next;
        }
        // 对右半部分进行归并排序
        // 先对有半部分排序,然后将其头结点置为NULL,这样左半部分就是独立的排序列表
        r=mergeSort(slowp.next);
        // 链表判断结束的标志:末尾节点.next==null
        slowp.next=null;
        // 对左半部分进行归并排序
        l=mergeSort(head);
        return mergeList(l,r);
    }
    // 合并链表
    private ListNode mergeList(ListNode l,ListNode r){
        // 临时头节点
        ListNode tmpHead=new ListNode(-1);
        ListNode p=tmpHead;
        while (l!=null&&r!=null){
            if (l.val<r.val){
                p.next=l;
                l=l.next;
            }else {
                p.next=r;
                r=r.next;
            }
            p=p.next;
        }
        p.next=l==null?r:l;
        return tmpHead.next;
    }
}
class Solution {
public:
    ListNode* sortList(ListNode* head) {
        if(head == NULL || head -> next == NULL) return head;
        ListNode* slow = head;
        ListNode* fast = head -> next -> next;
        while(fast != NULL && fast -> next != NULL){
            slow = slow -> next;
            fast = fast -> next -> next;
        }
        // 先对后半部分排序
        ListNode* r = sortList(slow -> next);
        // 再对前半部分排序
        slow -> next = NULL;
        ListNode* l = sortList(head);
        return mergeList(l, r);
    }

    ListNode* mergeList(ListNode* left, ListNode* right){
        ListNode* preHead = new ListNode();
        ListNode* cur = preHead;
        // 将两支分支合并
        while(left != NULL && right != NULL){
            if(left -> val < right -> val){
                cur -> next = left;
                left = left -> next;
            }else{
                cur -> next = right;
                right = right -> next;
            }
            cur = cur -> next;
        }
        // 剩下的部分
        if(left != NULL) cur -> next = left;
        else cur -> next = right;
        return preHead -> next;
    }
};

方法二:快排

class Solution {
public ListNode sortList(ListNode head) {
        if(head==null||head.next==null) return head;
        // 没有条件,创造条件。自己添加头节点,最后返回时去掉即可。
        ListNode newHead=new ListNode(-1);
        newHead.next=head;
        return quickSort(newHead,null);
    }
    // 带头结点的链表快速排序
    private ListNode quickSort(ListNode head,ListNode end){
        if (head==end||head.next==end||head.next.next==end) return head;
        // 将小于划分点的值存储在临时链表中
        ListNode tmpHead=new ListNode(-1);
        // partition为划分点,p为链表指针,tp为临时链表指针
        ListNode partition=head.next,p=partition,tp=tmpHead;
        // 将小于划分点的结点放到临时链表中
        while (p.next!=end){
            if (p.next.val<partition.val){
                tp.next=p.next;
                tp=tp.next;
                p.next=p.next.next;
            }else {
                p=p.next;
            }
        }
        // 合并临时链表和原链表,将原链表接到临时链表后面即可
        tp.next=head.next;
        // 将临时链表插回原链表,注意是插回!(不做这一步在对右半部分处理时就断链了)
        head.next=tmpHead.next;
        quickSort(head,partition);
        quickSort(partition,end);
        // 题目要求不带头节点,返回结果时去除
        return head.next;
    }
}

(三十四)合并 K 个升序链表

法一:暴力解法,逐个合并

class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        ListNode* list = NULL;
        for(int i = 0; i < lists.size(); i ++){
            list = mergeList(list, lists[i]);
        }
        return list;
    }
    ListNode* mergeList(ListNode* listA, ListNode* listB){
        ListNode* preHead = new ListNode();
        ListNode* cur = preHead;
        while(listA != NULL && listB != NULL){
            if(listA -> val < listB -> val){
                cur -> next = listA;
                listA = listA -> next;
            }else{
                cur -> next = listB;
                listB = listB -> next;
            }
            cur = cur -> next;
        }
        if(listA != NULL){
            cur -> next = listA;
        }else if(listB != NULL){
            cur -> next = listB;
        }
        return preHead -> next;
    }
};

法二:优先队列法(比较巧妙)

用容量为K的最小堆优先队列,把链表的头结点都放进去,然后出队当前优先队列中最小的,挂上链表,,然后让出队的那个节点的下一个入队,再出队当前优先队列中最小的,直到优先队列为空。

class Solution {
private:
    struct cmp{
        bool operator()(const ListNode* node1, const ListNode* node2) {
            return node1 -> val > node2 -> val; //小的优先级高 ,从小到大排,这里是按照val的大小构造的小顶堆
        }
    };
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        if(lists.size() == 0) return NULL;
        ListNode* preHead = new ListNode();
        ListNode* cur = preHead;

        priority_queue<ListNode*, vector<ListNode*>, cmp> pq;

        for(ListNode* list: lists){
            if(list == NULL) continue;
            pq.push(list);
        }
        
        while(!pq.empty()){
            ListNode* topNode = pq.top();
            pq.pop();
            cur -> next = topNode;
            cur = cur -> next;
            if(topNode -> next != NULL){
                pq.push(topNode -> next);
            }
        }
        return preHead -> next;
    }
};

(三十五)LRU 缓存

哈希表+双向链表(这里自己实现了一个简单的双向链表)

// 用双向链表存储所有节点,头部为最新的,尾部为旧的等待被替换的
struct DListNode{
    int key;
    int value;
    DListNode* pre;
    DListNode* next;
    DListNode(): key(0),value(0), pre(nullptr), next(nullptr){}
    DListNode(int k, int v): key(k), value(v), pre(nullptr), next(nullptr){}
};

class LRUCache {
private:
    // 哈希表中直接存储指针,这样可以直接通过Key找到节点在链表中的位置
    unordered_map<int, DListNode*> cache;
    DListNode* head;
    DListNode* tail;
    int size; // 当前长度
    int capacity; //最大容量
public:
    // 使用成员初始化列表来初始化所有成员变量,确保成员变量在构造函数体执行之前就已经被赋予了正确的初始值。
    LRUCache(int capacity): capacity(capacity), size(0) {
        // 使用伪头部和伪尾部节点     
        head = new DListNode();
        tail = new DListNode();
        head -> next = tail;
        tail -> pre = head;
    }

    int get(int key) {
        if(!cache.count(key)){
            return -1;
        }
        // 如果key存在,先通过哈希表定位,再更新节点位置,移到头部
        DListNode* node = cache[key];
        moveToHead(node);
        return node -> value;
    }
    
    void put(int key, int value) {
        if(!cache.count(key)){
            // 如果key不存在,创建一个新的节点
            DListNode* node = new DListNode(key, value);
            // 添加进哈希表
            cache[key] = node;
            // 新节点添加至链表头部
            addToHead(node);
            ++size;
            // 如果超出容量,删除双向链表的尾部节点
            if(size > capacity){
                DListNode* removed = removeTail();
                // 删除哈希表中对应项
                cache.erase(removed -> key);
                // 删除节点,防止内存泄漏
                delete removed;
                --size;
            }
        }
        // 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
        else{
            DListNode* node = cache[key];
            node -> value = value;
            moveToHead(node);
        }
    }

    void addToHead(DListNode* node){
        node -> pre = head;
        node -> next = head -> next;
        head -> next -> pre = node;
        head -> next = node;
    }

    void removeNode(DListNode* node){
        node -> pre -> next = node -> next;
        node -> next -> pre = node -> pre;
    }

    void moveToHead(DListNode* node){
        removeNode(node);
        addToHead(node);
    }

    DListNode* removeTail(){
        DListNode* node = tail -> pre;
        removeNode(node);
        return node;
    }
};

(三十六)二叉树的中序遍历

(1)法一:递归法

class Solution {
public:
    vector<int> result;
    void traversal(TreeNode* root, vector<int>& vec){
        if(root == NULL) return;
        traversal(root -> left, vec);
        vec.push_back(root -> val);
        traversal(root -> right, vec);
    }
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> result;
        traversal(root, result);
        return result;       
    }
};

(2)法二:迭代法

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> st;
        TreeNode* cur = root;
        while(cur != NULL || !st.empty()){
            if(cur != NULL){
                st.push(cur);
                cur = cur -> left;
            }else{
                cur = st.top();
                st.pop();
                result.push_back(cur -> val);
                cur = cur -> right;
            }
        }
        return result;       
    }
};

(三十七)二叉树的最大深度

(1)法一:递归法

class Solution {
public:
    int maxDepth(TreeNode* root) {
        if(root == NULL) return 0;
        int leftDepth = maxDepth(root -> left);
        int rightDepth = maxDepth(root -> right);
        return max(leftDepth, rightDepth) + 1;     
    }
};

(2)层序比那里

class Solution {
public:
    int maxDepth(TreeNode* root) {
        if(root == NULL) return 0;
        int height = 0;
        queue<TreeNode*> que;
        que.push(root);
        while(!que.empty()){
            height ++;
            int size = que.size();
            while(size --){
                TreeNode* cur = que.front();
                que.pop();
                if(cur -> left) que.push(cur -> left);
                if(cur -> right) que.push(cur -> right);
            }
        }
        return height;
    }
};

(三十八)翻转二叉树

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if(root == NULL) return root;
        root -> left = invertTree(root -> left);
        root -> right = invertTree(root -> right);
        swap(root -> left, root -> right);  
        return root;
    }
};

(三十九)对称二叉树

两边子树的遍历顺序不一样,但是要同时遍历

左子树:左-右-中

右子树:右-左-中

对于整个树而言,遍历顺序为:左-右-中

class Solution {
public:
    bool isSymmetric(TreeNode* root) {
        return compare(root -> left, root -> right);
    }
    bool compare(TreeNode* left, TreeNode* right){
        if(left == NULL && right == NULL) return true;
        else if(left == NULL && right != NULL) return false;
        else if(left != NULL && right == NULL) return false;
        else if(left -> val != right -> val) return false;
      // 如果left和right都不为NULL且值相等,还要判断他们的outside和inside是否相等
        bool outside = compare(left -> left, right -> right);
        bool inside = compare(left -> right, right -> left);
        return outside && inside;
    }
};

(四十)二叉树的直径

class Solution {
public:
    int maxLength = 0;
    int getHeight(TreeNode* root){
        if(root == NULL) return 0;
        int leftHeight = getHeight(root -> left);
        int rightHeight = getHeight(root -> right);
        // 对每个节点计算其高度的同时,刷新当前的最长路径(最长路径可能以任意一个节点为根节点)
        maxLength = max(maxLength, leftHeight + rightHeight);
        return max(leftHeight, rightHeight) + 1;
    }
    int diameterOfBinaryTree(TreeNode* root) {   
        getHeight(root);
        return maxLength;   
    }
};

(四十一)二叉树的层序遍历

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> result;
        queue<TreeNode*> que;
        if(root != NULL) que.push(root);
        while(!que.empty()){
            int size = que.size();
            vector<int> path;
            while(size --){
                TreeNode* topNode = que.front();
                path.push_back(topNode -> val);
                que.pop();
                // 将当前pop出来的节点的左右孩子入队列
                if(topNode -> left) que.push(root -> left);
                if(topNode -> right) que.push(root -> right);
            }
            result.push_back(path);
        }
        return result;  
    }
};

(四十二)将有序数组转换为二叉搜索树

class Solution {
public:
    TreeNode* traversal(vector<int>& nums, int left, int right){
        if(left > right) return NULL;
        int mid = (left + right) / 2;
        TreeNode* root = new TreeNode(nums[mid]);
        root -> left = traversal(nums, left, mid - 1);
        root -> right = traversal(nums, mid + 1, right);
        return root;
    }
    TreeNode* sortedArrayToBST(vector<int>& nums) {
        TreeNode* root = traversal(nums, 0, nums.size() - 1);
        return root;
    }
};

(四十三)验证二叉搜索树

(1)中序遍历整理转化为数组进行判断

class Solution {
public:
    void traversal(TreeNode* root, vector<int>& vec){
        if(root == NULL) return;
        traversal(root -> left, vec);
        vec.push_back(root -> val);
        traversal(root -> right, vec);
    }
    bool isValidBST(TreeNode* root) {
        vector<int> vec;
        traversal(root, vec);
        for(int i = 1; i < vec.size(); i ++){
            if(vec[i] <= vec[i - 1]) return false;
        }
        return true;
    }
};

(2)一边中序遍历一边比较

class Solution {
public:
    // 按照左中右遍历,当前节点的值一定是大于Pre的值
    TreeNode* pre = NULL;
    bool isValidBST(TreeNode* root) {
        if(root == NULL) return true;
        bool left = isValidBST(root -> left); // 左
        // 跳过第一个节点,依次将后一个节点与前一个节点的值作比较
        if(pre != NULL && pre -> val >= root -> val) return false; // 中
        pre = root;
        bool right = isValidBST(root -> right);// 右
        // 左子树和右子树都为二叉搜索树
        return left && right;
    }
};

(四十四)二叉搜索树中第 K 小的元素

class Solution {
public:
    void traversal(TreeNode* root, vector<int>& vec){
        if(root == NULL) return;
        traversal(root -> left, vec);
        vec.push_back(root -> val);
        traversal(root -> right, vec);
    }
    int kthSmallest(TreeNode* root, int k) {
        vector<int> vec;
        traversal(root, vec);
        return vec[k - 1];
    }
};

(四十五)二叉树的右视图

class Solution {
public:
    vector<int> rightSideView(TreeNode* root) {
        vector<int> vec;
        queue<TreeNode*> que;
        if(root == NULL) return vec;
        que.push(root);
        // 层序遍历,取每一层最后的节点的值
        while(!que.empty()){
            int size = que.size();
            while(size --){
                TreeNode* topNode = que.front();
                que.pop();
                if(size == 0) vec.push_back(topNode -> val);
                if(topNode -> left) que.push(topNode -> left);
                if(topNode -> right) que.push(topNode -> right);
            }
        }
        return vec;       
    }
};

(四十六)二叉树展开为链表

(1)迭代法(自顶向下)

class Solution {
public:
    void flatten(TreeNode* root) {
        // 一边遍历一边解构,不断将左子树移到右边,右子树接到左子树后面
        while(root != NULL){
            // 如果当前节点没有左子树,则直接跳到下一个节点
            if(root -> left == NULL){
                root = root -> right;
            }else{
                TreeNode* temp = root -> left;
                // 找到左子树的先序遍历到的最后一个节点(即最右边的节点)
                while(temp -> right != NULL){
                    temp = temp -> right;
                }
                // 将左子树整个移到根节点右边,原来的右子树移到左子树最后一个节点的右子树上
                temp -> right = root -> right;
                root -> right = root -> left;
                // 注意要将左子树置为空
                root -> left = NULL;
                root = root -> right;
            }
        }
    }
};

(2)递归法(自底向上)

利用后序遍历,依次遍历 6 5 4 3 2 1,然后每遍历一个节点就将当前节点的右指针更新为上一个节点。相应的左孩子也要置为 null,同样的也不用担心左孩子丢失,因为是后序遍历,左孩子已经遍历过了。

遍历到 5,把 5 的右指针指向 6。6 <- 5 4 3 2 1。

遍历到 4,把 4 的右指针指向 5。6 <- 5 <- 4 3 2 1。
链接:https://leetcode.cn/problems/flatten-binary-tree-to-linked-list/solutions/17274/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by–26/

class Solution {
public:
    TreeNode* pre = NULL;
    void flatten(TreeNode* root) {
       if(root == NULL) return;
       flatten(root -> right);
       flatten(root -> left);
       root -> right = pre;
       pre = root;
       root -> left = NULL;
    }
};

(四十七)从前序与中序遍历序列构造二叉树

class Solution {
public:
    TreeNode* traversal(vector<int>& preorder, vector<int>& inorder, int preLeft, int preRight, int inLeft, int inRight){
        if(preLeft > preRight || inLeft > inRight) return NULL; // 终止条件
        TreeNode* root = new TreeNode(preorder[preLeft]); // 中
        int inRootId = inLeft;
        while(inRootId <= inRight && inorder[inRootId] != preorder[preLeft]) inRootId ++;
        int subLeftLen = inRootId - inLeft;
        root -> left = traversal(preorder, inorder, preLeft + 1, preLeft + subLeftLen, inLeft, inRootId - 1); // 左
        root -> right = traversal(preorder, inorder, preLeft + subLeftLen + 1, preRight, inRootId + 1, inRight); // 右
        return root;
    }
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        return traversal(preorder, inorder, 0, preorder.size() - 1, 0, inorder.size() - 1);  
    }
};

(四十八)路径总和 III

(1)双重递归

class Solution {
public:
    int result = 0;
    void getPath(TreeNode* root, long long target){
        if(root == NULL) return;
        if(target == root -> val) result ++;
        getPath(root -> left, target - root -> val);
        getPath(root -> right, target - root -> val);
    }
    int pathSum(TreeNode* root, int targetSum) {
        if(root == NULL) return 0;
        // 以左子树为根节点可能的路径总数
        int left = pathSum(root -> left, targetSum);
        // 以右子树为根节点可能的路径总数
        int right = pathSum(root -> right, targetSum);
        // 以当前节点为根节点可能的路径总数
        getPath(root, targetSum);   
        return result;
    }
};

(2)前缀和+递归

class Solution {
public:
    // 前缀和表示从根节点到当前节点(包括当前节点)前面所有节点之和。
    unordered_map<long, int> prefix;
    int dfs(TreeNode* root, long long cur, int target){
        if(root == NULL) return 0;
        int res = 0;
        cur += root -> val; // 当前节点的前缀和
        if(prefix.count(cur - target) != 0){
            res = prefix[cur - target];
        }
        prefix[cur] ++;
        res += dfs(root -> left, cur, target);
        res += dfs(root -> right, cur, target);
        // 退出某个节点时将其cur值从哈希表中删除
        prefix[cur] --;
        // 这行可以省略,因为cur作为参数压栈了,所以其每个节点对应的值被保存了下来
        cur -= root -> val;
        return res;
    }
    int pathSum(TreeNode* root, int targetSum) {
        // 注意:空路径cur为0
        prefix[0] = 1;
        return dfs(root, 0, targetSum);
    }
};

(四十九)二叉树的最近公共祖先

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

(五十)二叉树中的最大路径和

class Solution {
public:
    int maxTemp = INT_MIN;
    int getMax(TreeNode* root){
        if(root == NULL) return 0;
        // 如果其子树最大路径和为负数,这样对父节点的贡献为0,舍弃该子树
        int left = max(0, getMax(root -> left));
        int right = max(0, getMax(root -> right));
        // 最大路径包含root,左子树、右子树的情况,无法递归(递归了就不是一条路径了),这里用一个temp记录下来最大的
        maxTemp = max(maxTemp, root -> val + left + right);
        return max(left, right) + root -> val;      
    }
    int maxPathSum(TreeNode* root) {
        getMax(root);
        // 对于最后的root,当然是要返回包含root、左子树、右子树的最大路径长度,所以最终返回的是maxTemp
        // maxTemp保存的是以任意节点为根节点的最长路径
        return maxTemp;   
    }
};

(五十一)岛屿数量

(1)深搜

class Solution {
public:
    int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1};
    void dfs(vector<vector<char>>& grid, vector<vector<bool>>& visited, int x, int y){
        for(int i = 0; i < 4; i ++){
            int nextx = x + dir[i][0];
            int nexty = y + dir[i][1];
            if(nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue;
            if(grid[nextx][nexty] == '1' && !visited[nextx][nexty]){
                visited[nextx][nexty] = true;
                dfs(grid, visited, nextx, nexty);
            }
        }
    }
    int numIslands(vector<vector<char>>& grid) {
        int n = grid.size();
        int m = grid[0].size();
        vector<vector<bool>> visited(n, vector<bool>(m, false));
        int count = 0;
        for(int i = 0; i < n; i ++){
            for(int j = 0; j < m; j ++){
                if(grid[i][j] == '1' && !visited[i][j]){
                    count ++;
                    visited[i][j] = true;
                    dfs(grid, visited, i, j);
                }
            }
        }
        return count;      
    }
};

(2)广搜

class Solution {
public:
    int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1};
    void bfs(vector<vector<char>>& grid, vector<vector<bool>>& visited, int x, int y){
        queue<pair<int, int>> que;
        que.push({x, y});
        visited[x][y] = true;
        while(!que.empty()){
            pair<int, int> temp = que.front();
            que.pop();
            int tempx = temp.first;
            int tempy = temp.second;
            for(int i = 0; i < 4; i ++){
                int nextx = tempx + dir[i][0];
                int nexty = tempy + dir[i][1];
                if(nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue;
                if(grid[nextx][nexty] == '1' && !visited[nextx][nexty]){
                    visited[nextx][nexty] = true;
                    que.push({nextx, nexty});
                }
            }
        }
    }
    int numIslands(vector<vector<char>>& grid) {
        int n = grid.size();
        int m = grid[0].size();
        vector<vector<bool>> visited(n, vector<bool>(m, false));
        int count = 0;
        for(int i = 0; i < n; i ++){
            for(int j = 0; j < m; j ++){
                if(grid[i][j] == '1' && !visited[i][j]){
                    count ++;
                    bfs(grid, visited, i, j);
                }
            }
        }
        return count;      
    }
};

(五十二)腐烂的橘子

class Solution {
public:
    // 深搜
    int sum = 0; // 统计当前一共有多少烂橘子(包括原来就烂的和后来烂掉的)
    int dir[4][2] = {1, 0, 0, 1, -1, 0, 0, -1};
    int bfs(vector<vector<int>>& grid, queue<pair<int, int>> que){
        int minute = 0;
        while(!que.empty()){
          // 类比二叉树层序遍历,记录当前层的橘子数目
            int size = que.size();
            sum += size;
            minute ++; // 每一层都使得时间+1
            while(size --){
                pair<int, int> temp = que.front();
                que.pop();
                int tempx = temp.first;
                int tempy = temp.second;
                for(int i = 0; i < 4; i ++){
                    int nextx = tempx + dir[i][0];
                    int nexty = tempy + dir[i][1];
                    if(nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue;
                    if(grid[nextx][nexty] == 1){
                        grid[nextx][nexty] = 2;
                        que.push({nextx, nexty});
                    }
                }
            }
        }
        return minute - 1; //因为第一层是已经腐烂的橘子,不算时间,所以时间=层数-1
    }
    int orangesRotting(vector<vector<int>>& grid) {
        int m = grid.size();
        int n = grid[0].size();
        int total = 0; // 统计一共多少橘子,判断最后橘子有没有烂完
        int fresh = 0; // 新鲜的橘子数目
        for(int i = 0; i < m; i ++){
            for(int j = 0; j < n; j ++){
                if(grid[i][j]) total ++;
                if(grid[i][j] == 1) fresh ++;
            }
        }
        if(fresh == 0) return 0; // 如果原来就没有新鲜的橘子,所需时间为0
        // 因为所有腐烂的橘子同时扩散到周围的橘子,所以将所有腐烂的橘子视为同一层
        queue<pair<int, int>> que;
        for(int i = 0; i < m; i ++){
            for(int j = 0; j < n; j ++){
                if(grid[i][j] == 2){
                    que.push({i, j});            
                }
            }
        }
        int result = bfs(grid, que);
        if(sum != total) return -1;
        return result;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值