Leetcode链表系列

本文介绍了LeetCode中关于链表的一系列问题,包括找出两个链表的交点、链表反转、归并两个有序链表、删除重复节点、删除倒数第n个节点、交换相邻节点、链表求和、回文链表、分隔链表以及按奇偶聚集等,详细讲解了每个问题的解决方案和思路。
摘要由CSDN通过智能技术生成

参考:https://github.com/CyC2018/CS-Notes

链表是空节点,或者有一个值和一个指向下一个链表的指针,因此很多链表问题可以用递归来处理。

1. 找出两个链表的交点

160. Intersection of Two Linked Lists (Easy)

Leetcode / 力扣

例如以下示例中 A 和 B 两个链表相交于 c1:

A:          a1 → a2
                    ↘
                      c1 → c2 → c3
                    ↗
B:    b1 → b2 → b3

但是不会出现以下相交的情况,因为每个节点只有一个 next 指针,也就只能有一个后继节点,而以下示例中节点 c 有两个后继节点。

A:          a1 → a2       d1 → d2
                    ↘  ↗
                      c
                    ↗  ↘
B:    b1 → b2 → b3        e1 → e2

要求时间复杂度为 O(N),空间复杂度为 O(1)。如果不存在交点则返回 null。

设 A 的长度为 a + c,B 的长度为 b + c,其中 c 为尾部公共部分长度,可知 a + c + b = b + c + a。

当访问 A 链表的指针访问到链表尾部时,令它从链表 B 的头部开始访问链表 B;同样地,当访问 B 链表的指针访问到链表尾部时,令它从链表 A 的头部开始访问链表 A。这样就能控制访问 A 和 B 两个链表的指针能同时访问到交点。

如果不存在交点,那么 a + b = b + a,以下实现代码中 l1 和 l2 会同时为 null,从而退出循环。

/**
 * 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 lenA = getLength(headA);
        int lenB = getLength(headB);
        if(lenA>lenB){
            for(int i=0; i<lenA-lenB; i++)
                headA = headA->next;
        }else{
            for(int j=0; j<lenB-lenA; j++)
                    headB = headB->next;
        }

        while(headA!=headB && headA && headB){
            headA = headA->next;
            headB = headB->next;
        }
        return (headA && headB) ? headA : NULL;
        
    }
    int getLength(ListNode* head){
        int cur = 0;
        while(head){
            cur++;
            head = head->next;
        }
        return cur;
    }
    // ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
    //     if(!headA || !headB) return NULL;
    //     ListNode *a=headA, *b=headB;
    //     while(a != b){
    //         a = a ? a->next : headB;
    //         b = b ? b->next : headA;
    //     }     
    //     return a;  
    // }
};

如果只是判断是否存在交点,那么就是另一个问题,即 编程之美 3.6 的问题。有两种解法:

  • 把第一个链表的结尾连接到第二个链表的开头,看第二个链表是否存在环;
  • 或者直接比较两个链表的最后一个节点是否相同。

2. 链表反转

206. Reverse Linked List (Easy)

Leetcode / 力扣

递归

// java版本
public ListNode reverseList(ListNode head) {
    if (head == null || head.next == null) {
        return head;
    }
    ListNode next = head.next;
    ListNode newHead = reverseList(next);
    next.next = head;
    head.next = null;
    return newHead;
}

头插法

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

3. 归并两个有序的链表

21. Merge Two Sorted Lists (Easy)

Leetcode / 力扣

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        if(!l1) return l2;
        if(!l2) return l1;
        ListNode *t1=l1, *t2=l2;
        ListNode* dummy = new ListNode(-1);
        ListNode* res = dummy;
        while(t1!=NULL && t2!=NULL){
            if(t1->val <= t2->val){
                res->next=t1;  
                t1 = t1->next;
            }else{
                res->next = t2;
                t2 = t2->next;
            }
            res = res->next;
        }
        
        res->next = t1 ? t1 : t2;
        
        res = dummy->next;
        delete dummy;
        return res;
    }
};

4. 从有序链表中删除重复节点

83. Remove Duplicates from Sorted List (Easy)

Leetcode / 力扣

Given 1->1->2, return 1->2.
Given 1->1->2->3->3, return 1->2->3.
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        ListNode* cur = head;

        while(cur && cur->next){
            if(cur->val == cur->next->val)
                cur->next = cur->next->next;
            else
                cur = cur->next;
        }
        return head;
        
    }
};


ListNode* deleteDuplicates(ListNode* head) {
    // 递归解法
        if(head==NULL || head->next==NULL) return head;
        head->next = deleteDuplicates(head->next);
        return head->val==head->next->val ? head->next : head;   
}

5. 删除链表的倒数第 n 个节点

19. Remove Nth Node From End of List (Medium)

Leetcode / 力扣

Given linked list: 1->2->3->4->5, and n = 2.
After removing the second node from the end, the linked list becomes 1->2->3->5.
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
//双指针,一次遍历
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummy = new ListNode(0);
        dummy->next = head;

        ListNode *first = dummy;
        ListNode *second = dummy;

        for(int i=1; i<=n+1; i++){
            first = first->next;
        }

        while(first){
            first = first->next;
            second = second->next;
        }
        second->next = second->next->next;

        ListNode* res = dummy->next;
        delete dummy;
        return res;
    }

};

class Solution {
public:
    
    // 两次遍历
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummy = new ListNode(0);
        dummy->next = head;
        int c = 0;
        ListNode* cur = head;
        while(cur){
            ++c;
            cur = cur->next;
        }

        c -= n;
        cur = dummy;
        while(c--){
            cur = cur->next;
        }
   
        cur->next = cur->next->next;
        return dummy->next;
    }
};

6. 交换链表中的相邻结点

24. Swap Nodes in Pairs (Medium)

Leetcode / 力扣

Given 1->2->3->4, you should return the list as 2->1->4->3.

题目要求:不能修改结点的 val 值,O(1) 空间复杂度。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        if(head==NULL || head->next==NULL) return head;
        
        ListNode* dummy = new ListNode(-1);
        dummy->next = head;
        ListNode* preNode = dummy;
        
        while(head && head->next){
            ListNode* firsyNode = head;
            ListNode* secondNode = head->next;

            // 交换节点
            firsyNode->next = secondNode->next;
            secondNode->next = firsyNode;
            preNode->next = secondNode;

            // 变换节点
            preNode = firsyNode;
            head = firsyNode->next;
            
        }

        ListNode* res = dummy->next;
        delete dummy;
        return res;
    }
};

7. 链表求和

445. Add Two Numbers II (Medium)

Leetcode / 力扣

Input: (7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 8 -> 0 -> 7

题目要求:不能修改原始链表。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        stack<int> l1s, l2s;
        while(l1){
            l1s.push(l1->val);
            l1 = l1->next;
        }
        while(l2){
            l2s.push(l2->val);
            l2 = l2->next;
        }

        ListNode* dummy = new ListNode(-1);
        int plus=0;
        while(!l1s.empty() || !l2s.empty()||plus){
            int num, sum, n1, n2;
            if(!l1s.empty()) {
                n1 = l1s.top();
                l1s.pop();
            }else{
                n1 = 0;
            }
            if(!l2s.empty()){
                n2 = l2s.top();
                l2s.pop();
            }else{
                n2 = 0;
            }
            sum = n1 + n2 + plus;

            num = sum%10;
            cout<<sum<<endl;
            plus = sum/10;

            ListNode* cur = new ListNode(num);
            cur->next = dummy->next;
            dummy->next = cur;
            //  delete cur;

        }
        ListNode* res = dummy->next;
        delete dummy;
        return res;
    }
};

8. 回文链表

234. Palindrome Linked List (Easy)

Leetcode / 力扣

题目要求:以 O(1) 的空间复杂度来求解。

切成两半,把后半段反转,然后比较两半是否相等。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool isPalindrome(ListNode* head) {
        if(!head) return true;
        ListNode* fastNode = head;
        ListNode* slowNode = head;

        //寻找中间节点
        while(fastNode && fastNode->next){
            fastNode = fastNode->next->next;
            slowNode = slowNode->next;
        }
        if(fastNode != NULL)
            slowNode = slowNode->next;

        // 截断第一个串
        ListNode* cur = head;
        while(cur->next!=slowNode){
            cur = cur->next;
        }
        cur->next = NULL;

        // 反转第二个串
        ListNode* dummy = new ListNode(-1);
        while(slowNode){
            ListNode* tmp = slowNode->next;
            slowNode->next = dummy->next;
            dummy->next = slowNode;
            slowNode = tmp;
        }

        // 判断是否相等
        ListNode* newHead = dummy->next;
        delete dummy;
        while(head && newHead){
            if(head->val!=newHead->val)
                return false;
            head = head->next;
            newHead = newHead->next;
        }

        return true;

    }
};

9. 分隔链表

725. Split Linked List in Parts(Medium)

Leetcode / 力扣

Input:
root = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], k = 3
Output: [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]]
Explanation:
The input has been split into consecutive parts with size difference at most 1, and earlier parts are a larger size than the later parts.

题目描述:把链表分隔成 k 部分,每部分的长度都应该尽可能相同,排在前面的长度应该大于等于后面的。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    vector<ListNode*> splitListToParts(ListNode* root, int k) {
        vector<ListNode*> res(k);
        int len = 0;
        ListNode* t = root;
        while(t){
            ++len;
            t = t->next;
        }

        int avg = len/k, ext = len%k;
        
        for(int i=0; i<k && root; i++){
            res[i] = root;
            for(int j=1; j<avg+(i<ext); j++){
                root = root->next;
            }

            t = root->next;
            root->next = NULL;
            root = t;
        }

        return res;
    }
};

10. 链表元素按奇偶聚集

328. Odd Even Linked List (Medium)

Leetcode / 力扣

给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。

请尝试使用原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(nodes),nodes 为节点总数。

Example:
Given 1->2->3->4->5->NULL,
return 1->3->5->2->4->NULL.
/**
 * 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* oddEvenList(ListNode* head) {
        if(!head) return head;
        ListNode *odd = head, *even = head->next, *evenHead = even;

        while(even && even->next){
            odd->next = odd->next->next;
            odd = odd->next;
            even->next = even->next->next;
            even = even->next;    
        }
        odd->next = evenHead;
        return head;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值