LeetCode hot100@链表

单链表定义方式:

struct ListNode {
    int val;  // 节点上存储的元素
    ListNode *next;  // 指向下一个节点的指针
    ListNode(int x) : val(x), next(NULL) {}  // 节点的构造函数
};

STL链表:
list<int> mylist; 是一个双向循环链表,不需要关心节点管理和内存管理(详细语法见c++提高编程)

160. 相交链表

gap

/**
 * 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) {
        ListNode *curA =  headA;
        ListNode *curB =  headB;
        int lenA = 1;
        int lenB = 1;
        while(curA->next != NULL) {
            curA = curA->next;
            lenA++;
        }
        while(curB->next != NULL) {
            curB = curB->next;
            lenB++;
        }
        // cout << "lenA = " << lenA << endl;
        // cout << "lenB = " << lenB << endl;
        
        curA = headA;
        curB = headB;
        if(lenA > lenB) {
            swap(curA, curB);
            swap(lenA, lenB);
        }

        int gap = lenB - lenA;
        for(int i = 0; i < gap; i++) curB = curB->next;
        // cout << "val = " << (*curB).val << endl;
        while(curB != NULL) {
            if(curB == curA) return curA;
            curA = curA->next;
            curB = curB->next;
        }

        return NULL;
    }
};

206. 反转链表

双指针法

/**
 * Definition for singly-linked list.
 * 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) {
        ListNode *cur = head;
        ListNode *pre = nullptr;
        ListNode *tmp = nullptr;

        while(cur) {
            //先用tmp保存一下后一个节点,因为一会方向改变了,就找不到它了
            tmp = cur->next; 

            cur->next = pre; //改变方向
            pre = cur; //pre往后移一步
            cur = tmp; //把之前保存的后一个节点还给cur
        }

        return pre; //此时cur已经出界了
    }
};

234. 回文链表

把val用数组存起来再判断就简单了,因为可以双向遍历

/**
 * Definition for singly-linked list.
 * 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:
    bool isPalindrome(ListNode* head) {
        ListNode *cur = head;
        vector<int> v;
        while(cur) {
            v.push_back(cur->val);
            cur = cur->next;
        }

        int left = 0, right = v.size() - 1;
        while(left < right) {
            if(v[left++] != v[right--]) return false;
        }

        return true;
    }
};

141. 环形链表

Solution1:用哈希存节点地址是否被访问过

Solution2:如果一个链表存在环,那么快慢指针必然会相遇(第二次相遇时快慢指针移动次数差值即为环的长度)

class Solution1 {
public:
    bool hasCycle(ListNode *head) {
        unordered_map<ListNode*, int> mp;
        ListNode *cur = head;
        while(cur) {
            if(mp[cur]) {
                return true;
            }
            mp[cur]++;
            cur = cur->next;
        }

        return false;
    }
};
class Solution2 {
public:
    bool hasCycle(ListNode *head) {
        ListNode *slow = head;
        ListNode *fast = head;
        while(fast) {
            fast = fast->next;
            if(fast) {
                fast = fast->next;
            }
            //先判断再移动慢指针,否则会漏掉只有一个节点的情况
            if(fast == slow) return true; 
            slow = slow->next;  
        }

        return false;
    }
};

142. 环形链表 II

用哈希存节点地址是否被访问过

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        unordered_map<ListNode*, int> mp;
        ListNode *cur = head;
        while(cur) {
            if(mp[cur]) {
                return cur;
            }
            mp[cur]++;
            cur = cur->next;
        }

        return NULL;
    }
};

21. 合并两个有序链表

优秀的链表语法练习题

/**
 * Definition for singly-linked list.
 * 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* mergeTwoLists(ListNode* list1, ListNode* list2) {
        ListNode *merge = new ListNode(); //先创建一个虚拟结点作为新链表头节点的前一个节点
        ListNode *cur = merge; //跟踪新链表尾部
        ListNode *cur1 = list1;
        ListNode *cur2 = list2;
  
        while(cur1 != nullptr && cur2 != nullptr) {
            if(cur1->val < cur2->val) {
                cur->next = cur1;
                cur = cur->next;
                cur1 = cur1->next;
            } else {
                cur->next = cur2;
                cur = cur->next;
                cur2 = cur2->next;
            }
        }

        if(cur1 == nullptr) cur->next = cur2;    
        else if(cur2 == nullptr) cur->next = cur1;    

        return merge->next;
    }
};

2. 两数相加

模拟相加,无需转换,最后统一处理进位更简单

class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode *res = new ListNode();
        ListNode *cur = res;
        ListNode *cur1 = l1;
        ListNode *cur2 = l2;
        bool carry_bit = false; 

        while(cur1 && cur2) {
            cur->next = new ListNode(cur1->val + cur2->val);
            cur = cur->next;
            cur1 = cur1->next;
            cur2 = cur2->next;
        }

        while(cur1) {
            cur->next = new ListNode(cur1->val);
            cur = cur->next;
            cur1 = cur1->next;
        }
        while(cur2) {
            cur->next = new ListNode(cur2->val);
            cur = cur->next;
            cur2 = cur2->next;
        }

        cur = res->next;
        while(cur) {
            if(cur->val >= 10) {
                cur->val -= 10;
                if(cur->next) cur->next->val += 1;
                else cur->next = new ListNode(1);
            }
            cur = cur->next;
        }

        return res->next; 
    }
};

19. 删除链表的倒数第 N 个结点

双指针 + 虚拟头节点

ps:一般需要处理头节点的时候就可以加一个虚拟头节点 ,比如头节点可能为空的情况

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode *dummy = new ListNode();
        dummy->next = head;
        ListNode *cur = dummy;
        ListNode *pre = dummy;

        while(n--) {
            pre = pre->next;
        } 
        while(pre->next) {
            pre = pre->next;
            cur = cur->next;
        }

        ListNode* tmp = cur->next;
        cur->next = cur->next->next;
        delete(tmp);

        return dummy->next;
    }
};

24. 两两交换链表中的节点

三个节点

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode *dummy = new ListNode();
        dummy->next = head;
        ListNode *cur = dummy;

        while(cur->next && cur->next->next) {
            ListNode *tmp1 = cur->next; //存下第一个节点
            ListNode *tmp2 = cur->next->next->next; //存下第三个节点

            cur->next = tmp1->next;
            cur->next->next = tmp1;
            tmp1->next = tmp2;

            cur = cur->next->next;
        }

        return dummy->next;
    }
};

25. K 个一组翻转链表

不能用哈希存一下节点地址,然后两两交换,那样只是节点的位置变了,但是next指针没变化,也就是说连线没有变化,把线扯成直线后,还是1→2→3

其实就是206.反转链表复杂版,k个一组进行反转

/**
 * Definition for singly-linked list.
 * 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 {
private:
    ListNode* myReverse(ListNode* head) {
        ListNode* pre = nullptr;
        ListNode* cur = head;
        ListNode* tmp = nullptr;
        while(cur) {
            tmp = cur->next;
            cur->next = pre;
            pre = cur;
            cur = tmp;
        }
        return pre;
    }
public:
    //反转链表复杂版
    ListNode* reverseKGroup(ListNode* head, int k) {
        ListNode *dummy = new ListNode();
        dummy->next = head;
        ListNode *cur = dummy;
        int n = k;

        while(cur) {
            ListNode *first = cur; //first指向0
            //如果遍历后链表长度不足k 则退出
            while(cur && cur->next && n) {
                cur = cur->next;
                n--;
            }
            if(n) break;
            //cur指向3(以样例2举例)
            ListNode *next_node = cur->next; //next_node指向4
            cur->next = nullptr; //断开连接 防止调用反转函数时超过k还一直运行下去
            ListNode *tail = first->next; //tail指向1
            first->next = myReverse(tail); 
            tail->next = next_node; //反转后的尾部1指向next_node4
            cur = tail; //新一轮的起始点 即4的前一个节点
            n = k;
        }
         
        return dummy->next; 
    }
};

138. 随机链表的复制

关键是如何让新的节点指向新的节点

/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* next;
    Node* random;
    
    Node(int _val) {
        val = _val;
        next = NULL;
        random = NULL;
    }
};
*/

class Solution {
public:
    Node* copyRandomList(Node* head) {
        Node *cur = head;
        unordered_map<Node*, Node*> mp;

        while(cur) {
            mp[cur] = new Node(cur->val);   
            cur = cur->next;
        }

        cur = head;
        while(cur) {
            //新节点的next指向旧节点的下一个节点的新节点
            mp[cur]->next = mp[cur->next]; 
            mp[cur]->random = mp[cur->random];
            cur = cur->next;
        }

        return mp[head];  
    }
};

148. 排序链表

不需要用到原节点的地址,只需新节点的val值不变就行,想复杂了

/**
 * Definition for singly-linked list.
 * 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) {
        ListNode *res = new ListNode();
        ListNode *cur = head;

        vector<int> v;
        while(cur) {
            v.push_back(cur->val);
            cur = cur->next;
        }
        ranges::sort(v);
      
        cur = res;
        for(const auto &i : v) {
            cur->next = new ListNode(i);
            cur = cur->next;
        }

        return res->next;
    }
};

23. 合并 K 个升序链表

优先队列(底层逻辑是堆排序

class Solution {
public:
    struct Comparator { //自定义比较器
        bool operator()(ListNode *a, ListNode *b) {
            return a->val > b->val; //升序 即小根堆
        }
    };

    ListNode* mergeKLists(vector<ListNode*>& lists) {
        priority_queue<ListNode*, vector<ListNode*>, Comparator> pq;
        //遍历每一条链表的每一个节点
        for(auto &list_cur : lists) { //注意不能加const 因为list_cur一直在改变
            while(list_cur) {
                pq.push(list_cur);
                list_cur = list_cur->next;
            }
        }
        
        ListNode *dummy = new ListNode(0);
        ListNode *cur = dummy;
        //从队列中不断取出节点并连接
        while(!pq.empty()) {
            cur->next = pq.top();
            cur = cur->next;
            pq.pop();
        }
        cur->next = nullptr; //标志链表结束

        return dummy->next;
    }
};

146. LRU 缓存✅❌

很多的语法练习题 细节不少 但可惜用单链表写了半天 debug变天 最后几个样例超时

最后用STL中的 list(双向循环链表) + 复杂map

很好的STL练习题

class LRUCache {
private:
    int cpy = 0;
    list<pair<int, int>> m_list; //双向链表存k、v
    unordered_map<int, list<pair<int, int>>::iterator> mp; //map存k到list迭代器的映射
public:
    LRUCache(int capacity) {
        cpy = capacity;
    }
    
    int get(int key) {
        //如果不存在
        if(mp.find(key) == mp.end()) return -1;
        else {
            //key存在 需要用splice函数更新key对应节点在链表中的位置 
            //更新到链表头部 代表最近使用
            m_list.splice(m_list.begin(), m_list, mp[key]);
            return mp[key]->second;
        }
    }
 
    void put(int key, int value) {
        //如果key存在 调用get已经更新到头部 再更新value
        if(get(key) != -1) mp[key]->second = value;
        else { //key不存在
            if(m_list.size() >= cpy) { //如果满了 先从尾部删一个 map也要删
                mp.erase(m_list.back().first);
                m_list.pop_back();
            } 
            m_list.push_front(pair(key, value)); //在头部插入新元素
            mp[key] = m_list.begin(); //把新元素存到map中
        }
    }
};

/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache* obj = new LRUCache(capacity);
 * int param_1 = obj->get(key);
 * obj->put(key,value);
 */


不准投币喔 👆

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇