Leetcode刷题笔记--Hot51-60

目录

1--环形链表II(142)

2--LRU缓存(146)

3--排序列表(148)

4--乘积最大子数组(152)

5--最小栈(155)

6--相交链表(160)

7--多数元素(169)

8--打家劫舍(198)

9--岛屿数量(200)

10--反转链表(206)


1--环形链表II(142)

主要思路:

        快慢指针,快指针每次走两步,慢指针每次走一步;

        第一次相遇时,假设慢指针共走了 f 步,则快指针走了 2f 步;

        假设起点到环入口结点的长度为 a(不包括入口结点),环的结点数为 b;快指针比慢指针多走的步数肯定全在环中,则有 2f - f = f = nb;则慢指针共走了 nb 步;

        由于慢指针共走了 nb 步,而起点到环入口结点的步数为 a,则实际慢指针在环内走了 nb - a 步;

        此时让慢指针从起点重新出发走 a 步,每次走 1 步;快指针从相遇的地方出发,每次也走 1 步,快慢指针必然在环入口结点相遇;因此快指针相当于也走了 a 步,恰好与 nb - a 步互补,构成完整圈数的 nb 环;

#include <iostream>
#include <vector>

struct ListNode {
    int val;
    ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        if(head == nullptr) return head;
        ListNode *slow = head;
        ListNode *fast = head;
        while(fast != NULL && fast->next != NULL){
            fast = fast->next->next;
            slow = slow->next;
            if(fast == slow) break; // 第一次相遇
        }
        if(fast == NULL || fast->next == NULL) return NULL; // 没有环
        // 从头开始走
        slow = head;
        while(slow != fast){
            slow = slow->next;
            fast = fast->next;
        }
        // 第二次相遇就是环的入口
        return slow;
    }
};

int main(int argc, char *argv[]) {
	// head = [3, 2, 0, -4], pos = 1
	ListNode *Node1 = new ListNode(3);
	ListNode *Node2 = new ListNode(2);
	ListNode *Node3 = new ListNode(0);
	ListNode *Node4 = new ListNode(-4);

	Node1->next = Node2;
	Node2->next = Node3;
	Node3->next = Node4;
	Node4->next = Node2;

	Solution S1;
	ListNode* res = S1.detectCycle(Node1);
	if(res != nullptr) std::cout << res->val << std::endl;
	else std::cout << "nullptr" << std::endl;
	return 0;
}

2--LRU缓存(146)

主要思路:

        基于双向链表和哈希表;

        访问元素时,若元素不存在则返回;若元素存在,则将元素记录,并将元素移动到双向链表头部;(确保访问热度最高的元素放在双向链表头部,访问热度最低的元素放在双向链表尾部);

        插入元素时,若元素不存在:当容量已满时,先移除双向链表尾部的元素,再将新元素插入到双向链表头部;若元素存在,则取出元素并更新元素的值,将更新后的元素插入到双向链表头部;

#include <iostream>
#include <unordered_map>

class LRUCache {
public:
    struct ListNode{
        ListNode(int key, int val){
            this->key = key;
            this->val = val;
        }
        ListNode* pre = nullptr;
        ListNode* next = nullptr;
        int val = 0;
        int key = 0;
    };

    LRUCache(int capacity) {
        this->cap = capacity; // 容量
        head = new ListNode(-1, -1);
        tail = new ListNode(-1, -1);
        head->next = tail;
        tail->pre = head;
    }
    
    int get(int key) {
        if(hash.count(key) == 0) return -1; // 元素不存在
        ListNode* ptr = hash[key]; // 取出元素
        remove(ptr); // 从双向链表中删除元素
        insert(ptr); // 将元素插入到双向链表头部
        return ptr->val; // 返回元素的值
    }
    
    void put(int key, int value) {
        if(hash.count(key) == 0){ // 元素不存在
            if(hash.size() == cap){ // 容量已满
                ListNode* ptr = tail->pre;
                remove(ptr); // 去除尾部节点
                hash.erase(ptr->key);
                delete(ptr);
            }
            ListNode* new_node = new ListNode(key, value); // 新建节点
            insert(new_node); // 插入新节点到头部
            hash[new_node->key] = new_node;
            return;
        }
        else{ // 元素存在
            ListNode* ptr = hash[key]; // 取出元素
            ptr->val = value; // 更新元素
            remove(ptr); // 先删除元素
            insert(ptr); // 再将元素插入到头部
            return;
        }
    }

    void remove(ListNode* ptr){
        // 取出前驱和后驱元素
        ListNode* a = ptr->pre;
        ListNode* b = ptr->next;
        // 更新前驱和后驱元素的指向
        a->next = b;
        b->pre = a;
        ptr->pre = ptr->next = nullptr;
    }
    void insert(ListNode* ptr){
        ListNode* tmp = head->next; // 头指针的下一个元素
        // 将元素插入到双向链表头部
        ptr->pre = head;
        head->next = ptr;
        ptr->next = tmp;
        tmp->pre = ptr;
    }
private:
    int cap = 0; // 容量
    std::unordered_map<int, ListNode*> hash; // 哈希表
    ListNode* head; // 双向链表哨兵头节点
    ListNode* tail; // 双向链表哨兵尾节点
};

int main(int argc, char argv[]){

    return 0;
}

3--排序列表(148)

主要思路:

        基于归并排序,先二分链表,再对链表进行归并排序;

#include <iostream>

struct ListNode {
    int val;
    ListNode *next;
    ListNode() : val(0), next(nullptr) {}
    ListNode(int x) : val(x), next(nullptr) {}
    ListNode(int x, ListNode *next) : val(x), next(next) {}
};

class Solution {
public:
    ListNode* sortList(ListNode* head) {
        return SplitList(head);
    }

    ListNode* SplitList(ListNode* head){
        if(head == nullptr || head->next == nullptr) return head; // 空或者只有一个结点

        ListNode* slow = head, *fast = head, *pre = head;
        while(fast != nullptr){
            pre = slow; // 慢指针的前继指针
            fast = fast->next;
            slow = slow->next;
            if(fast == nullptr) break;
            fast = fast->next;
        }

        pre->next = nullptr; // 断开
        return(merge(SplitList(head), SplitList(slow))); // 递归二分至底层,然后归并
    }

    ListNode* merge(ListNode* head1, ListNode* head2){
        ListNode* dummyNode = new ListNode(0);
        ListNode *tmp = dummyNode, *tmp1 = head1, *tmp2 = head2;
        while(tmp1 != nullptr && tmp2 != nullptr){
            if(tmp1->val <= tmp2->val){
                tmp->next = tmp1;
                tmp1 = tmp1->next; 
            }
            else{
                tmp->next = tmp2;
                tmp2 = tmp2->next;
            }
            tmp = tmp->next;
        }
        while(tmp1 != nullptr){
            tmp->next = tmp1;
            tmp1 = tmp1->next;
            tmp = tmp->next;
        }
        while(tmp2 != nullptr){
            tmp->next = tmp2;
            tmp2 = tmp2->next;
            tmp = tmp->next;
        }
        return dummyNode->next;
    }
};

int main(int argc, char *argv[]) {
    // head = [4, 2, 1, 3]
    ListNode* Node1 = new ListNode(4);
    ListNode* Node2 = new ListNode(2);
    ListNode* Node3 = new ListNode(1);
    ListNode* Node4 = new ListNode(3);

    Node1->next = Node2;
    Node2->next = Node3;
    Node3->next = Node4;

    Solution S1;
    ListNode* res = S1.sortList(Node1);
    while(res != nullptr){
        std::cout << res->val << " ";
        res = res->next;
    }
	return 0;
}

4--乘积最大子数组(152)

主要思路:

        基于动态规划,dp_max[i] 和 dp_min[i] 分别表示以坐标 i 结尾的连续子数组和的最大值和最小值;

        状态转移方程:dp_max[i]  = std::max(dp_max[i-1] * nums[i], dp_min[i-1] * nums[i], nums[i]);dp_min[i]  = std::min(dp_max[i-1] * nums[i], dp_min[i-1] * nums[i], nums[i]);

        之所以使用 dp_min[i],是由于会出现负数的情况,当负负得正时会出现更大的连续和,因此要考虑 dp_min[i];

#include <iostream>
#include <vector>

class Solution {
public:
    int maxProduct(std::vector<int>& nums) {
        std::vector<int> dp_max(nums.size(), 0), dp_min(nums.size(), 0);
        dp_max[0] = dp_min[0] = nums[0];
        int max = dp_max[0];
        for(int i = 1; i < nums.size(); i++){
            dp_max[i] = std::max(std::max(dp_max[i-1] * nums[i], dp_min[i-1] * nums[i]), nums[i]);
            dp_min[i] = std::min(std::min(dp_max[i-1] * nums[i], dp_min[i-1] * nums[i]), nums[i]);
            max = std::max(max, dp_max[i]);
        }
        return max;
    }
};

int main(int argc, char argv[]){
    // nums = [2, 3, -2, 4]
    std::vector<int> test = {2, 3, -2, 4};
    Solution S1;
    int res = S1.maxProduct(test);
    std::cout << res << std::endl;
    return 0;
}

5--最小栈(155)

主要思路:

        使用一个辅助栈,记录截止到当前元素的最小值。

#include <iostream>
#include <stack>

class MinStack{
public:
    MinStack(){}
    
    void push(int val){
        stk1.push(val);
        if(stk2.empty() || val <= stk2.top()){
            stk2.push(val);
        }
    }
    
    void pop(){
        int top = stk1.top();
        stk1.pop();
        if(top == stk2.top()) stk2.pop();
    }
    
    int top(){
        return stk1.top();
    }
    
    int getMin() {
        return stk2.top();
    }
private: 
    std::stack<int> stk1, stk2;
};

int main(int argc, char argv[]){

    return 0;
}

6--相交链表(160)

主要思路:

        走过自己的路,再走你走过的路,要么在相交的节点相遇,要么都是 nullptr;

        非常有哲学意义的一道题!!!!!

#include <iostream>
#include <vector>

struct ListNode{
    int val;
    ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};

class Solution{
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        if(headA == nullptr || headB == nullptr){
            return nullptr;
        }

        ListNode* tmp1 = headA, *tmp2 = headB;
        while(tmp1 != tmp2){
            tmp1 = tmp1->next;
            tmp2 = tmp2->next;
            if(tmp1 == tmp2) break; // 相遇或同时为null
            if(tmp1 == nullptr) tmp1 = headB;
            if(tmp2 == nullptr) tmp2 = headA;
        }        
        if(tmp1 == nullptr) return nullptr;
        return tmp1;
    }
};

int main(int argc, char argv[]){
    // intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
    ListNode *Node1 = new ListNode(4);
    ListNode *Node2 = new ListNode(1);
    ListNode *Node3 = new ListNode(8);
    ListNode *Node4 = new ListNode(4);
    ListNode *Node5 = new ListNode(5);
    Node1->next = Node2;
    Node2->next = Node3;
    Node3->next = Node4;
    Node4->next = Node5;

    ListNode *Node6 = new ListNode(5);
    ListNode *Node7 = new ListNode(6);
    ListNode *Node8 = new ListNode(1);
    Node6->next = Node7;
    Node7->next = Node8;
    Node8->next = Node3;

    Solution S1;
    ListNode* res = S1.getIntersectionNode(Node1, Node6);
    if(res != nullptr) std::cout << res->val << std::endl;
    else std::cout << "null" << std::endl;

    return 0;
}

7--多数元素(169)

主要思路:

        假设有一个桶,当一个桶为空时,放入一个新的元素;当一个桶不为空时,当准备放入一个新的元素时,如果新的元素与桶内的元素不相同,则不放入这个新元素且将桶内的一个元素拿出来;

        对于本题,最后桶剩下的元素肯定是多数元素,因为题目规定多数元素必定存在;具体到本题可以用一个计数器表示桶内的元素个数,当计数器为空时可以随意放入新的元素;

#include <iostream>
#include <vector>

class Solution {
public:
    int majorityElement(std::vector<int>& nums) {
        int count = 0, val = nums[0]; // count 表示桶内元素的个数, val表示桶内的元素值
        for(int num : nums){
            if(num == val) count++;
            else{
                if(count == 0){ // 桶为空
                    val = num;
                    count = 1;
                }
                else{ // 桶不为空,抵消一个元素
                    count--;
                }
            }
        }
        return val;
    }
};

int main(int argc, char argv[]){
    // nums = [3,2,3]
    std::vector<int> test = {3, 2, 3};
    Solution S1;
    int res = S1.majorityElement(test);
    std::cout << res << std::endl;
    return 0;
}

8--打家劫舍(198)

主要思路:

        访问到第i间房屋时,设dp[i][0]表示打劫第i间房屋,dp[i][1]表示不打劫第i间房屋,能够获得的最高金额;

        状态转移方程 dp[i][0] = dp[i-1][1] + nums[i],dp[i][1] = dp[i-1][0]; 

#include <iostream>
#include <vector>

class Solution {
public:
    int rob(std::vector<int>& nums) {
        std::vector<std::vector<int>> dp(nums.size(), std::vector<int>(2, 0));
        // 初始化
        dp[0][0] = nums[0];
        dp[0][1] = 0;
        for(int i = 1; i < nums.size(); i++){
            dp[i][0] = dp[i-1][1] + nums[i]; // 打劫房屋i,前一间房屋只能不被打劫
            dp[i][1] = std::max(dp[i-1][0], dp[i-1][1]); // 不打劫房屋i,前一间房屋可以被打劫也可以不被打劫
        }
        return std::max(dp[nums.size() - 1][0], dp[nums.size() - 1][1]);
    }
};

int main(int argc, char *argv[]) {
    std::vector<int> test = {1, 2, 3, 1};
    Solution S1;
    int res = S1.rob(test);
    std::cout << res << std::endl;
    return 0;
}

9--岛屿数量(200)

主要思路:

        深度优先搜索,从(i, j)出发,递归搜索上下左右四个方向的陆地,将相连的陆地置为0;

#include <iostream>
#include <vector>

class Solution {
public:
    int numIslands(std::vector<std::vector<char>>& grid) {
        int m = grid.size(), n = grid[0].size();
        int res = 0;
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                if(grid[i][j] == '1'){
                    res++;
                    dfs(grid, i, j);
                }
            }
        }
        return res;
    }

    void dfs(std::vector<std::vector<char>>& grid, int i, int j){
        if(i >= grid.size() || j >= grid[0].size() || grid[i][j] == '0' || i < 0 || j < 0) return;
        grid[i][j] = '0';
        dfs(grid, i+1, j);
        dfs(grid, i, j+1);
        dfs(grid, i-1, j);
        dfs(grid, i, j-1);
    }
};

int main(int argc, char *argv[]) {
    std::vector<std::vector<char>> test = {{'1', '1', '0', '0', '0'}, 
                                           {'1', '1', '0', '0', '0'},
                                           {'0', '0', '1', '0', '0'},
                                           {'0', '0', '0', '1', '1'}};
    Solution S1;
    int res = S1.numIslands(test);
    std::cout << res << std::endl;
    return 0;
}

10--反转链表(206)

主要思路:

        头插法,使用虚拟头节点。

#include <iostream>

struct ListNode {
    int val;
    ListNode *next;
    ListNode() : val(0), next(nullptr) {}
    ListNode(int x) : val(x), next(nullptr) {}
    ListNode(int x, ListNode *next) : val(x), next(next) {}
};

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(head == nullptr) return nullptr;
        ListNode* FHead = new ListNode(-1); // 虚拟头节点
        FHead->next = head;
        ListNode* tmp = head->next;
        head->next= nullptr;
        while(tmp != nullptr){
            ListNode* next = tmp->next; // 记录下一个结点
            // 头插
            tmp->next = FHead->next;
            FHead->next = tmp;
            // 更新
            tmp = next;
        }
        return FHead->next;
    }
};

int main(int argc, char *argv[]) {
    ListNode *Node1 = new ListNode(1);
    ListNode *Node2 = new ListNode(2);
    ListNode *Node3 = new ListNode(3);
    ListNode *Node4 = new ListNode(4);
    ListNode *Node5 = new ListNode(5);

    Node1->next = Node2;
    Node2->next = Node3;
    Node3->next = Node4;
    Node4->next = Node5;

    Solution S1;
    ListNode *res = S1.reverseList(Node1);
    ListNode *tmp = res;
    while(tmp != nullptr){
        std::cout << tmp->val << " ";
        tmp = tmp->next;
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值