leetcode链表题整理

1. 2. 两数相加

 给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例 1:
输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.

解答:链表入门题  用一个新链表存两链表每个节点之和,在模10即可

class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode* res = new ListNode(-1);
        ListNode* cus = res;
        int c = 0;
        while(l1 || l2){
            int a = l1 ? l1->val : 0;
            int b = l2 ? l2->val : 0;
            int d = a + b + c;
            res->next = new ListNode(d % 10);
            res = res->next;
            if(l1) l1 = l1->next;
            if(l2) l2 = l2->next;
            c = d / 10;
        }
        while(c) res->next = new ListNode(c % 10), c /= 10;
        return cus->next;
    }
};

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

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

解答:法一:遍历模拟,没啥说的上代码

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* l1 = new ListNode(0, head);
        ListNode* l2 = l1;
        int length = 0;
        while(l1->next != nullptr){
            length++;
            l1=l1->next;
        }
        l1 = l2;
        for(int i = 0; i < length - n; i++) l1 = l1->next;
        l1->next = l1 ->next->next;
        return l2->next;
    }
};

解答:法二:将链表节点都存栈里,然后出栈n个得到倒数n+1个元素,然后删第n个即可。


class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* l1 = new ListNode(0, head);
        ListNode* l2 = l1;
        stack<ListNode*> stk;
        while(l1){
            stk.push(l1); // 把头节点存进去,避免全出去了造成空栈
            l1 = l1->next;
        }
        while(n--){
            stk.pop();
        }
        ListNode* pre = stk.top();
        pre->next = pre->next->next;
        return l2->next;
    }
};

法三:双指针做法采用双指针,快指针先走n布然后和慢指针一起走就可以找到第n个但注意我们要找的是第n-1个元素,因此我们要给慢指针设个头节点或者其他方法均可(少走一步)


class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        // 双指针
        ListNode* first = head;
        ListNode* second = new ListNode(0, head); // 找第n-1个元素,所以比first多一个元素
        ListNode* re = second;
        while(n--){
            first = first->next;
        }
        while(first){
            first = first->next;
            second = second->next;
        }
        second->next = second->next->next;
        return re->next;
    }
};

3.21. 合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

法一:迭代

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        ListNode* res = new ListNode(-1);
        ListNode* cus = res;
        while(l1 && l2){
            if(l1->val < l2->val) res->next = l1, l1= l1->next;
            else res->next = l2, l2 =l2->next;
            res = res->next;
        }
        res ->next = l1 == nullptr ? l2 : l1;
        return cus->next;
    }
};

法二:递归

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        if(l1 == nullptr) {
            return l2;
        }
        else if(l2 == nullptr){
            return l1;
        }
        else if(l1->val < l2->val){
            l1->next = mergeTwoLists(l1->next, l2);
            return l1;
        }
        else {
            l2->next = mergeTwoLists(l1, l2->next);
            return l2;
        }
    }
};

4.23. 合并K个升序链表

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

示例 1:

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
  1->4->5,
  1->3->4,
  2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6

解答:优先队列解决把每条链表都存入优先队列中,优先队列按每条链表的首元素从小到大排序,然后每次输出首值最小的那条链表。

struct status{
    int val;
    ListNode *pre;
    bool operator< (const status &sta) const{
        return val > sta.val;
    } 
};
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        priority_queue<status> pri;
        int n = lists.size();
        for(auto list : lists) {
            if(list) pri.push({list->val, list});
        }
        ListNode* l1 = new ListNode(-1);
        ListNode* l2 = l1;
        while(!pri.empty()){
            auto p = pri.top(); pri.pop();
            l1->next = p.pre;
            l1 = l1->next;
            if(p.pre->next)pri.push({p.pre->next->val, p.pre->next});
        }
        return l2->next;
    }
};

法二:分治思想俩两合并时间复杂度O(nk*logk)

class Solution {
public:
    ListNode* mergeTwoList(ListNode* aList, ListNode* bList){
        if(aList == nullptr || bList == nullptr) return aList ? aList : bList;
        ListNode* l1 = new ListNode(-1);
        ListNode* l2 = l1, *a = aList, *b = bList;
        while(a && b){
            if(a->val < b->val){
                l1->next = a; a = a->next;
            }else{
                l1->next = b; b = b->next;
            }
            l1 = l1->next;
        }
        l1->next = a ? a:b;  
        return l2->next;  
    }
    ListNode* merge(vector<ListNode*>& lists, int l, int r){
        int mid = l + r >> 1;
        if(l == r) return lists[l];
        if(l > r) return nullptr;
        return mergeTwoList(merge(lists, l, mid), merge(lists, mid + 1, r));
    }
    ListNode* mergeKLists(vector<ListNode*>& lists) {     
        return merge(lists, 0, lists.size() - 1);
    }
};

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

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

示例 1:
输入:head = [1,2,3,4]
输出:[2,1,4,3]

解释:链表交换题画图便可清晰解决

 如图所示L1为有空头节点的链表,a为L1下一个节点,b为L1下下个节点。想要两两交换链表节点,所以b为交换后链表的下一个节点,如图交换俩节点,L1下个位置为a处。

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode* l1 = new ListNode(0, head);
        ListNode* l2 = l1;
        while(l1->next && l1->next->next){
            auto a = l1->next, b = l1->next->next;
            l1->next = b;
            a->next = b->next;
            b->next = a;
            l1 = a;
        }
        return l2->next;
    }
};

6.25. K 个一组翻转链表

给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。

k 是一个正整数,它的值小于或等于链表的长度。

如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

进阶:

你可以设计一个只使用常数额外空间的算法来解决此问题吗?
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。

解释:5题的加强版,就是选k个节点不断交换即可,注意不要直接在链表上操作,开临时变量,方便操作。

class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
        ListNode* l1 = new ListNode(0, head);
        ListNode* l2 = l1;
        for(auto p = l1;;){
            auto q = p;
            for(int i = 0; i < k && q; i++) q = q->next;
            if(!q) break;
            auto x = p;
            p->next = q;
            for(int i = 0; i < k - 1; i++) {
                auto a = p->next, b = p->next->next; 
                b->next = a;
                p = p->next->next;
            }
            x->next = p->next;
            p = x;
        }
        return l2->next;
    }
};

7.61. 旋转链表

给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。

示例 1:


输入:head = [1,2,3,4,5], k = 2
输出:[4,5,1,2,3]

 解释:把链表头尾连起来,然后找到第n-k%n的位置把他的下个位置设为尾节点即可。

class Solution {
public:
    ListNode* rotateRight(ListNode* head, int k) {
        if(k == 0 || !head || !head->next) return head;
        auto p = head;
        int n = 1;
        while(p->next) p = p->next, n++;
        p->next = head;
        n = n - k % n;
        while(n--) p = p->next;
        head = p->next;
        p->next = nullptr;
        return head;
    }
};

8.82. 删除排序链表中的重复元素 II

存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除链表中所有存在数字重复情况的节点,只保留原始链表中 没有重复出现 的数字。

返回同样按升序排列的结果链表。

输入:head = [1,2,3,3,4,4,5]
输出:[1,2,5]

解释:删节点没啥说的

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        ListNode* p = new ListNode(0, head);
        ListNode* q = p;
        while(p->next && p->next->next){
            if(p->next->val == p->next->next->val){
                int re = p->next->val;
                while(p->next && p->next->val == re) p->next=p->next->next;
            } 
            else p = p->next;
        }
        return q->next;
    }
};

9.86. 分隔链表

给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。

你应当 保留 两个分区中每个节点的初始相对位置。

解释:把小于x的单独存大于x的单独存,然后链接起来即可。

class Solution {
public:
    ListNode* partition(ListNode* head, int x) {
        ListNode* s1 = new ListNode(0);
        ListNode* b1 = new ListNode(0);
        ListNode* s2 = s1;
        ListNode* b2 = b1;
        while(head){
            if(head->val < x){
                s1->next = head;
                s1 = s1->next;
            }else{
                b1->next = head;
                b1 = b1->next;
            }
            head = head->next;
        }
        b1->next = nullptr;
        s1->next = b2->next;
        return s2->next;
    }
};

10. 链表反转问题

(1)92. 反转链表 II

给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
输入:head = [1,2,3,4,5], left = 2, right = 4
输出:[1,4,3,2,5]

解释:参考T5的反转思路即可

class Solution {
public:
    ListNode* reverseBetween(ListNode* head, int left, int right) {
        if(left == right) return head;
        int n = left;
        ListNode* p = new ListNode(0, head);
        ListNode* q = p;
        n--;
        while(n--) p = p->next;
        auto a = p->next, b = a->next;
        for(int i = 0; i < right - left; i++){
            auto c = b->next;
            b->next = a;
            a = b; b = c;  
        }
        auto c = p->next;
        p->next = a;
        c->next = b;
        return q->next; 
    }
};

(2)206. 反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(!head || !head->next) return head;
        ListNode* p = new ListNode(0, head);
        ListNode* q = p;

        auto a = p->next, b = a->next;
        while(b){
            auto c = b->next;
            b->next = a; a = b; b = c;
        }
        auto c = p->next;
        p->next = a; c->next = b;
        return p->next;
    }
};

11.138. 复制带随机指针的链表

给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。

构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 。

例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 x 和 y ,同样有 x.random --> y 。

返回复制链表的头节点。

用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:

val:一个表示 Node.val 的整数。
random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为  null 。
你的代码 只 接受原链表的头节点 head 作为传入参数。

输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]

解释:再原链表上复制链表,先复制他子节点,然后再复制random节点,然后把链表拆分。


class Solution {
public:
    Node* copyRandomList(Node* head) {
        // 复制小弟
        for(auto p = head; p; p = p->next->next){
            auto q = new Node(p->val);
            q->next = p->next;
            p->next = q;
        }
        // 复制random
        for(auto p = head; p; p = p->next->next){
            if(p->random) p->next->random = p->random->next;
        }
        // 拆分
        auto l1 = new Node(-1);
        auto l2 = l1;
        for(auto p = head; p; p = p->next){
            auto q = p->next;
            l1 = l1->next = q;
            p->next = q->next;
        }
        return l2->next;
    }
};

12.环形链表

(1)141. 环形链表

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

如果链表中存在环,则返回 true 。 否则,返回 false 。

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

解释:也没啥说的改变值看能不能再次访问到即可。

class Solution {
public:
    bool hasCycle(ListNode *head) {
        if(!head) return 0;
        for(auto p = head; p; p = p->next){
            if(p->val == INT_MAX) return 1;
            p->val = INT_MAX;
        }
        return 0;
    }
};

(2)142. 环形链表 II

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

解释:跟(1)一样但不允许修改链表,所以可以搞一个快慢指针,两指针肯定可以连到一起。

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

13.143. 重排链表

给定一个单链表 L 的头节点 head ,单链表 L 表示为:

L0 → L1 → … → Ln - 1 → Ln
请将其重新排列后变为:

L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …
不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

输入:head = [1,2,3,4]
输出:[1,4,2,3]

解释:将所有节点存到vector中去,然后再用vector操作链表重排即可。

class Solution {
public:
    void reorderList(ListNode* head) {
       if(!head) return;
       vector<ListNode*> vc;
       ListNode* p = head;
       while(p){
           vc.push_back(p);
           p = p->next;
       }
       int i = 0, j = vc.size() - 1;
       while(i < j){
           vc[i]->next = vc[j];
           i++;
           if(i == j) break;
           vc[j]->next = vc[i];
           j--;
       }
       vc[i]->next = nullptr;
    }
};

14.147. 对链表进行插入排序

对链表进行插入排序。
插入排序的动画演示如上。从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示)。
每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中。

解释:插入排序

class Solution {
public:
    ListNode* insertionSortList(ListNode* head) {
        ListNode* re = new ListNode(-1);
        for(auto p = head; p;){
            auto pre = re, next = p->next;
            while(pre->next && pre->next->val <= p->val) pre = pre->next;
            p->next = pre->next;
            pre->next = p;
            p = next;
        }

        return re->next;
    }
};

15.146. LRU 缓存

请你设计并实现一个满足  LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache 类:
LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。
函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。

示例:

输入
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出
[null, null, null, 1, null, -1, null, -1, 3, 4]

解释:用双链表实现,双链表模拟LRU,开map存所有数。

class LRUCache {
public:
    struct Node{
        int key, val;
        Node *left, *right;
        Node(int _key, int _val) : key(_key), val(_val), left(NULL), right(NULL){}
    }*L, *R;
    int n;
    unordered_map<int, Node*> hash;
    LRUCache(int capacity) {
        n = capacity;
        L = new Node(-1, -1), R = new Node(-1, -1);
        L->right = R, R->left = L;
    }
    
    int get(int key) {
        if(hash.count(key) == 0) return -1;
        auto p = hash[key];
        remove(p);
        insert(p);
        return p->val;
    }
    
    void put(int key, int value) {
        if(hash.count(key)){
            auto p = hash[key];
            p->val = value;
            remove(p);
            insert(p);
        }else{
            if(hash.size() == n){
                auto p = R->left;
                remove(p);
                hash.erase(p->key);
            }
            auto p = new Node(key, value);
            insert(p);
            hash[key] = p;
        }
    }
    void remove(Node *p){
        p->left->right = p->right;
        p->right->left = p->left;
    }
    void insert(Node *p){
        p->left = L; p->right = L->right;
        L->right->left = p;L->right = p; 
        
    }
};

16.160. 相交链表

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

图示两个链表在节点 c1 开始相交:

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构 。

解释:找到相交链表,可以不断遍历等一个遍历完,转为遍历另一个链表,两链表如果相交一定会相遇。

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
       ListNode* p = headA, *q = headB;
       while(p != q){
           p=p==nullptr?headB:p->next;
           q=q==nullptr?headA:q->next;
       }
       return p;
    }
};

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值