【刷题笔记】链表

141 阅读4分钟

题目集

1. 分隔链表

原题链接:86. 分隔链表 - 力扣(LeetCode)

笔记:

主体思路: 把原链表分成两个小链表,一个链表中的元素大小都小于 x,另一个链表中的元素都大于等于 x,最后再把这两条链表接到一起;

代码:

class Solution {
public:
    ListNode* partition(ListNode* head, int x) {
        ListNode *dummy1 = new ListNode(-1);
        ListNode *dummy2 = new ListNode(-1);

        ListNode *p1 = dummy1, *p2 = dummy2;

        ListNode *p = head;

        while(p != NULL){
            if(p->val >= x){
                p2->next = p;
                p2 = p2->next;
            }else{
                p1->next = p;
                p1 = p1->next;
            }

            ListNode *temp = p->next;
            p->next = NULL;
            p = temp;
        }


        p1->next = dummy2->next;

        return dummy1->next;
    }

};

2. 合并 K 个升序链表

原题链接:23. 合并 K 个升序链表 - 力扣(LeetCode)

笔记:

思路:合并 k 个有序链表,需快速得到 k 个节点中的最小节点,接到结果链表上;

-->使用优先队列(Priority Queue),把链表节点放入一个最小堆,就可以每次获得 k 个节点中的最小节点;

优先队列

定义:priority_queue<int, vector<int>, greater<int> > >pq; / priority_queue<node> q;

特点:自动排序,排序的比较函数可重写;操作与queue相同,有push/pop/top/empyt/size等;

代码:

class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {

        if(lists.empty()) return nullptr;

        ListNode *dummy = new ListNode(-1);
        ListNode *p = dummy;

        priority_queue<ListNode*, vector<ListNode*>, function<bool(ListNode*,ListNode*)>> pq([](ListNode* a,ListNode* b) {return a->val > b->val ;});

        for (auto head : lists){
            if(head != nullptr){
                pq.push(head);
            }
        }

        while(!pq.empty()){

            ListNode *node = pq.top();
            pq.pop();
            p->next = node;
            if( node->next != nullptr){
                pq.push(node->next);
            }

            p = p->next;
        }

        return dummy->next;
    }
};

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

原题链接:19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)

笔记:

寻找链表倒数第N+1个节点,将倒数第N+1的指针指向倒数第N-1的节点,实现删除链表的倒数第 N 个结点;

只遍历一遍链表,步骤:

  1. 使用两个指针p1与p2,p1先出发k步,然后p2出发,即p1到第k+1个节点的时候,p2指向第1个节点;
  2. 随后p1、p2同时同步前进,当p1到达末端,指向null的时候,p2就指向倒数第n个节点。

代码:

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {

        ListNode *dummy = new ListNode(-1);
        dummy->next = head;

        ListNode *x = findfromEnd(dummy, n+1);
        x->next = x->next->next;
        return dummy->next;
    }

    ListNode* findfromEnd(ListNode* head, int k){
        ListNode *p1 = head;
        for(int i=0;i<k;i++){
            p1 = p1->next;
        }
        ListNode *p2 = head;
        while(p1!=nullptr){
            p1 = p1->next;
            p2 = p2->next;
        }

        return p2;
    }
};

4. 链表的中间结点

原题链接:876. 链表的中间结点 - 力扣(LeetCode)

笔记:

同样只遍历一遍链表,创建两个指针fastslow,每个每次前进的步数不同,达到一个到末尾,另个以在中点的状态,详细:

  1. 如果链表长度为单数,当fast->next = nullptr时,fast指向最后一个,slow指向唯一的中间结点;
  2. 当长度为偶数,当fast = nullptr时,slow会指向两个中间结点中的第二个;
  3. 如果需要返回的是两个中间节点的第一个,判定条件应为fast->next->next = nullptr

PS: 对末尾的判断条件,可以根据观察slow到达终点时,fast的情况来写;

代码:

class Solution {
public:
    ListNode* middleNode(ListNode* head) {
        ListNode *slow = head;
        ListNode *fast = head;

        while(fast!= nullptr && fast->next!=nullptr){
            slow = slow->next;
            fast = fast->next->next;
        }

        return slow;
    }
};

5. 环形链表 II

原题链接:142. 环形链表 II - 力扣(LeetCode)

笔记:

巧在思路,先判定是否有环,若fastslow相遇,即有环,则slow回到七点,然后和fast同步前行,相遇的位置则是环的起点(如下图所示);

image.png

代码:

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode *slow = head;
        ListNode *fast = head;

        while(fast != nullptr && fast->next!=nullptr){
            fast = fast->next->next;
            slow = slow->next;

            if(slow==fast) break;
        }

        if(fast == nullptr || fast->next==nullptr){
            return nullptr;
        }

        slow = head;

        while(slow!=fast){
            slow = slow->next;
            fast = fast->next;
        }

        return slow;
    }
};

5. 相交链表

原题链接:160. 相交链表 - 力扣(LeetCode)

笔记:

该题有多种解法:

  1. 将其中一条链表的尾部与其头部相连,转化成一个带环的链表,即将问题转化成求环形链表的环的起点,(若题干要求最初的两个链表形状不变,在求到答案以后释放即可);
  2. (统一列表长度)将两个链表拼接,两边同步遍历,当上下结点相同时,则为答案;
  3. (统一列表长度)分别求出两个链条的长度,让两个指针到达链条尾部的距离相同,然后同步前进,即可求出答案(代码如下)。

代码:

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        int lenA=0, lenB=0;

        for(ListNode *p1=headA; p1!=nullptr;p1=p1->next){
            lenA++;
        }
        for(ListNode *p2=headB; p2!=nullptr;p2=p2->next){
            lenB++;
        }

        ListNode *p1=headA;
        ListNode *p2=headB;
        if(lenA>lenB){
            for(int i=0;i<lenA-lenB;i++)
            {
                p1 = p1->next;
            }
        }
        else{
            for(int i=0;i<lenB-lenA;i++)
            {
                p2 = p2->next;
            }
        }

        while(p1!=p2){
            p1 = p1->next;
            p2 = p2->next;
        }
        return p1;
        // 没相遇则为空指针
    }
};