LeetCode鏈表專題

(一)LeetCode206.反轉(zhuǎn)鏈表

題目描述:

反轉(zhuǎn)一個(gè)單鏈表。

代碼實(shí)現(xiàn)
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */

ListNode* reverseList(ListNode* head)//非遞歸版本
{
    if (head == NULL)
        return head;
    ListNode * pre = NULL;
    ListNode * p = head;
    while (p)
    {
        ListNode *tmp = p->next;
        p->next = pre;
        pre = p;
        p = tmp;
    }
    return pre;
}
ListNode* reverseList1(ListNode* head)//遞歸版本
{
    if (head == NULL || head->next == NULL)
        return head;
    ListNode *newHead = reverseList(head->next);

    //反轉(zhuǎn)指針
    ListNode * tmp = head->next;
    tmp->next = head;
    head->next = NULL;
    return newHead;
}

(二)LeetCode160. 相交鏈表

題目描述

編寫一個(gè)程序,找到兩個(gè)單鏈表相交的起始節(jié)點(diǎn)。

思路

假設(shè)鏈表A與B一共有三部分構(gòu)成,A獨(dú)有的結(jié)點(diǎn)個(gè)數(shù)為a,B獨(dú)有的結(jié)點(diǎn)個(gè)數(shù)為b,A,B公有的結(jié)點(diǎn)個(gè)數(shù)為c.
則有 a+c+b=b+c+a
那么可以用兩個(gè)指針p,q,分別從A,B的頭結(jié)點(diǎn)開始,當(dāng)p指向空的時(shí)候,將p指向B的頭結(jié)點(diǎn),同理當(dāng)q指向空的時(shí)候,將q指向A的頭結(jié)點(diǎn)。
如果兩個(gè)鏈表有相交部分,他們一定會(huì)相遇,如果沒有,最后都同時(shí)為空。

代碼
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB)
{
    ListNode * p = headA;
    ListNode * q = headB;
    if (!p || !q)
        return NULL;
    while (p != q)
    {
        if (!p)
            p = headB;
        else
            p = p->next;
        if (!q)
            q = headA;
        else
            q = q->next;
    }
    return p;
}

(三)LeetCode142. 環(huán)形鏈表 II

題目描述

給定一個(gè)鏈表,返回鏈表開始入環(huán)的第一個(gè)節(jié)點(diǎn)。 如果鏈表無環(huán),則返回 null。

思路

快慢指針即可解決,快指針走兩步,慢指針走一步。假如有環(huán),那么兩個(gè)指針一定會(huì)相遇

證明

假如存在環(huán),可以將問題看出快指針在追趕慢指針。

  1. 快指針與慢指針之間差1步。此時(shí)繼續(xù)往后走,慢指針前進(jìn)1步,快指針前進(jìn)2步,兩者相遇。
  2. 快指針與慢指針之間差2步。此時(shí)繼續(xù)往后走,慢指針前進(jìn)1步,快指針前進(jìn)2步,兩者之間相差1步,轉(zhuǎn)化為第1種情況。
  3. 快指針與慢指針之間差n步。此時(shí)繼續(xù)往后走,慢指針前進(jìn)1步,快指針前進(jìn)2步,兩者之間相差(n+1-2)步。
尋找環(huán)入口

假如快慢指針在環(huán)的i個(gè)結(jié)點(diǎn)相遇,假設(shè)慢指針一共走了n步,那么快指針走了2n步,因?yàn)榭熘羔樀乃俣仁瞧鋬杀叮敲绰羔樤僮遪步(剛好是2n步),肯定還是在i點(diǎn),所以可以得到環(huán)的大小為n
我們假設(shè)環(huán)入口與結(jié)點(diǎn)i的距離為k,起點(diǎn)距離環(huán)入口距離為l,
那么根據(jù)上述有n=l+k(慢指針第一次走n步),則l=n-k
所以,我們用兩個(gè)指針,分別從起點(diǎn)和相遇點(diǎn)同時(shí)走,則一定在環(huán)入口相遇
因?yàn)橄嘤鳇c(diǎn)距離環(huán)入口:n-k
起點(diǎn)距離環(huán)入口:l

代碼
ListNode *detectCycle (ListNode *head)
{
    if (head == NULL || head->next == NULL)
        return NULL;
    ListNode * fast = head->next->next;
    ListNode * slow = head->next;

    while (fast != slow)
    {
        if (fast == NULL || slow == NULL || fast->next == NULL)
            return NULL;
        fast = fast->next->next;
        slow = slow->next;
    }
    fast = head;
    while (fast != slow)
    {
        fast = fast->next;
        slow = slow->next;
    }
    return slow;
}

(四)LeetCode234. 回文鏈表

題目描述

判斷一個(gè)鏈表是否為回文鏈表。

思路

將鏈表后半段原地翻轉(zhuǎn),再將前半段、后半段依次比較,判斷是否相等

代碼
//鏈表后半段原地翻轉(zhuǎn),再將前半段、后半段依次比較,判斷是否相等
bool isPalindrome(ListNode* head)
{
    //如果鏈表為空或者僅有一個(gè)元素那么肯定是回文鏈表
    if (!head || !head->next)
        return true;
        
    //快慢指針法,尋找鏈表中心
    ListNode * slow, *fast;
    slow = fast = head;
    while (fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
    }
    
    if (fast) //鏈表元素奇數(shù)個(gè)
    {
        slow->next = reverseList(slow->next);
        slow = slow->next;
    }
    else//鏈表元素偶數(shù)個(gè)
        slow = reverseList(slow);

    //slow此時(shí)為鏈表后半段第一個(gè)元素    
    while (slow)
    {
        if (head->val != slow->val)
        {
            return false;
        }
        slow = slow->next;
        head = head->next;
    }
    return true;
}
//非遞歸版本的反轉(zhuǎn)鏈表
ListNode* reverseList(ListNode* head)
{
    if (head == NULL)
        return head;
    ListNode * pre = NULL;
    ListNode * p = head;
    while (p)
    {
        ListNode *tmp = p->next;
        p->next = pre;
        pre = p;
        p = tmp;
    }
    return pre;
}

(五)LeetCode109. 有序鏈表轉(zhuǎn)換二叉搜索樹

題目描述

給定一個(gè)單鏈表,其中的元素按升序排序,將其轉(zhuǎn)換為高度平衡的二叉搜索樹。
一個(gè)高度平衡二叉樹是指一個(gè)二叉樹每個(gè)節(jié)點(diǎn) 的左右兩個(gè)子樹的高度差的絕對值不超過 1。

思路

鏈表有序,二叉搜索樹的左子樹小于根結(jié)點(diǎn),右子樹大于根結(jié)點(diǎn),又需要滿足AVL的性質(zhì)。
則找到鏈表中間元素,設(shè)為根結(jié)點(diǎn),前半段為左子樹,后半段為右子樹,遞歸即可。

代碼
TreeNode* sortedListToBST(ListNode* head)
{
    if (!head)
        return NULL;
    if (!head->next)
    {
        TreeNode * root = new TreeNode(head->val);
        return root;
    }
    //快慢指針法,尋找鏈表中心
    ListNode * slow, *fast;
    slow = fast = head;
    ListNode * tail = head;//保存前半段的尾巴
    while (fast && fast->next)
    {
        tail = slow;
        slow = slow->next;
        fast = fast->next->next;
    }
    tail->next = NULL;//前半段

    TreeNode * root = new TreeNode(slow->val);

    root->left = sortedListToBST(head);
    root->right = sortedListToBST(slow->next);
    return root;
}

(六)LeetCode24. 兩兩交換鏈表中的節(jié)點(diǎn)

題目描述

給定一個(gè)鏈表,兩兩交換其中相鄰的節(jié)點(diǎn),并返回交換后的鏈表。

思路

三個(gè)指針,p表示上一組交換完成后的后面結(jié)點(diǎn),l,r表示當(dāng)前交換的左右結(jié)點(diǎn)
交換過程可以簡化為:p指向r,r指向l即可,最后將p指向NULL
注意處理奇數(shù)長度的情況

代碼
ListNode* swapPairs(ListNode* head)
{
    if (!head || !head->next)
        return head;
    ListNode *p, *l, *r;    //p表示上一組交換完成后的后面結(jié)點(diǎn),l,r表示當(dāng)前交換的左右結(jié)點(diǎn)
    p = NULL, l = head, r = head->next;
    head = head->next;      //頭結(jié)點(diǎn)更換位置
    ListNode *l1, *r1;      //記錄下兩組需要交換的結(jié)點(diǎn)
    while (l && r)
    {
        l1 = r->next;
        r1 = l1 ? l1->next : NULL;

        if (p)//上一組交換完成后的后結(jié)點(diǎn)的 next指向當(dāng)前交換后的前結(jié)點(diǎn)
            p->next = r;
        r->next = l;//r指向l
        p = l;//保存當(dāng)前交換后的后結(jié)點(diǎn)

        l = l1;
        r = r1;
    }
    if (l)//奇數(shù)長度情況
    {
        p->next = l;
        p = l;
    }
    p->next = NULL;//鏈表尾端

    return head;
}

(七)LeetCode328. 奇偶鏈表

題目描述

給定一個(gè)單鏈表,把所有的奇數(shù)節(jié)點(diǎn)和偶數(shù)節(jié)點(diǎn)分別排在一起。請注意,這里的奇數(shù)節(jié)點(diǎn)和偶數(shù)節(jié)點(diǎn)指的是節(jié)點(diǎn)編號的奇偶性,而不是節(jié)點(diǎn)的值的奇偶性。

思路

用兩個(gè)指針odd,even分別表示奇數(shù)指針和偶數(shù)指針
先更新odd的next,然后更新odd
再更新even的next,然后更新even

代碼
ListNode* oddEvenList(ListNode* head)
{
    if (!head || !head->next)
        return head;
    ListNode *odd = head;//奇數(shù)結(jié)點(diǎn)
    ListNode * even = head->next;//偶數(shù)結(jié)點(diǎn)
    ListNode * evenHead = head->next;//偶數(shù)頭結(jié)點(diǎn)
    while (odd->next && even->next)
    {
        //奇數(shù)結(jié)點(diǎn)下一個(gè)指向偶數(shù)結(jié)點(diǎn)的下一個(gè)元素
        odd->next = even->next;//更新奇數(shù)結(jié)點(diǎn)的next
        odd = odd->next;//更新奇數(shù)結(jié)點(diǎn)
        //偶數(shù)結(jié)點(diǎn)下一個(gè)指向新的奇數(shù)結(jié)點(diǎn)的下一個(gè)元素
        even->next = odd->next;//更新偶數(shù)結(jié)點(diǎn)的next
        even = even->next;//更新偶數(shù)結(jié)點(diǎn)
    }
    odd->next = evenHead;//連接奇數(shù)和偶數(shù)兩個(gè)鏈表
    return head;
}

(八)LeetCode143. 重排鏈表

題目描述

給定一個(gè)單鏈表 L:L0→L1→…→Ln-1→Ln
將其重新排列后變?yōu)椋?L0→Ln→L1→Ln-1→L2→Ln-2→…

思路
  1. 把整個(gè)鏈表劃分成2個(gè)等長的子鏈表,如果原鏈表長度為奇數(shù),那么第一個(gè)子鏈表的長度多1。
  2. 翻轉(zhuǎn)第二個(gè)子鏈表。
  3. 將兩個(gè)子鏈表合并。
代碼
ListNode * reorderList (ListNode* head)
{
    if (!head || !head->next)
        return head;

    ListNode * fast = head;
    ListNode * slow = head;

    while (fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
    }
    //利用快慢指針找到中心位置,翻轉(zhuǎn)后面鏈表
    ListNode * mid = reverseList (slow->next);
    slow->next = NULL;

    //合并p和q,p的長度 = q的長度 | q的長度 +1
    ListNode * p, *q;
    p = head->next;
    q = mid;
    ListNode *t = head;
    while (p && q)
    {
        t->next = q;
        t = t->next;
        q = q->next;

        t->next = p;
        t = t->next;
        p = p->next;
    }

    return head;
}
ListNode * reverseList (ListNode * head)
{
    if (!head || !head->next)
        return head;
    ListNode * pre = NULL;
    ListNode * p = head;
    while (p)
    {
        ListNode * tmp = p->next;
        p->next = pre;
        pre = p;
        p = tmp;
    }
    return pre;
}

(九)LeetCode445. 兩數(shù)相加 II

題目描述

給定兩個(gè)非空鏈表來代表兩個(gè)非負(fù)整數(shù)。數(shù)字最高位位于鏈表開始位置。它們的每個(gè)節(jié)點(diǎn)只存儲(chǔ)單個(gè)數(shù)字。將這兩數(shù)相加會(huì)返回一個(gè)新的鏈表。

思路

簡單的大數(shù)相加,首先各自翻轉(zhuǎn),然后逐位想加,最后處理進(jìn)位,需要注意最高位進(jìn)位時(shí)需要new一個(gè)新的結(jié)點(diǎn),再翻轉(zhuǎn)回去即可。

代碼
ListNode* addTwoNumbers (ListNode* l1, ListNode* l2)
{
    if (!l1 || !l2)
        return !l1 ? l2 : l1;
    //首先各自翻轉(zhuǎn)
    l1 = reverseList (l1);
    l2 = reverseList (l2);

    ListNode * head = new ListNode (l1->val + l2->val);
    ListNode * sum = head;
    ListNode * p = l1->next;
    ListNode * q = l2->next;
    //然后逐位相加
    while (p || q)//
    {
        ListNode * tmp = new ListNode (0);
        tmp->val = (p ? p->val : 0) + (q ? q->val : 0);

        sum->next = tmp;
        sum = sum->next;
        p = p ? p->next : NULL;
        q = q ? q->next : NULL;
    }
    sum->next = NULL;

    //最后處理進(jìn)位
    p = head;
    while (p->next)
    {
        if (p->val >= 10)
        {
            p->val -= 10;
            p->next->val += 1;
        }
        p = p->next;
    }
    if (p->val >= 10) //最后一位數(shù)需要進(jìn)位
    {
        p->val -= 10;
        ListNode * tmp = new ListNode (1);
        p->next = tmp;
        p = p->next;
        p->next = NULL;
    }
    //翻轉(zhuǎn)回去
    head = reverseList (head);
    return head;
}
ListNode * reverseList (ListNode * head)
{
    if (!head || !head->next)
        return head;
    ListNode * pre = NULL;
    ListNode * p = head;
    while (p)
    {
        ListNode * tmp = p->next;
        p->next = pre;
        pre = p;
        p = tmp;
    }
    return pre;
}

(十)LeetCode725. Split Linked List in Parts

題目描述

要求將鏈表進(jìn)行分割,使分割的部分盡可能一樣,如果不是平均分,應(yīng)使任意部分大小不相差1,如果要求的分割段數(shù)小于鏈表的元素?cái)?shù),則末尾用nullptr填充。

思路

求出鏈表的長度,對k求除數(shù)和余數(shù),商res代表分割的長度,余數(shù)mod表示前mod分割塊的長度要+1。

代碼
vector<ListNode*> splitListToParts (ListNode* root, int k)
{
    vector<ListNode*> vec (k, nullptr);
    if (!root)
        return vec;
    ListNode * p = root;
    int len = 0;
    while (p)
    {
        len++;
        p = p->next;
    }
    int res = len / k;
    int mod = len % k;

    p = root;
    if (res == 0)//鏈表長度小于k
    {
        ListNode * nxt = p->next;
        for (int i = 0; i < len; i++)
        {
            vec[i] = p;
            nxt = p->next;
            p->next = NULL;
            p = nxt;
        }
        return vec;
    }
    for (int i = 0; i < k; i++)
    {
        int tmp = res + (i < mod ? 1 : 0);
        vec[i] = p;
        tmp--; //少走一步,方便保存下一個(gè)結(jié)點(diǎn)和將當(dāng)前部分的末尾置為空
        while (tmp--)
        {
            p = p->next;
        }
        ListNode * nxt = p->next;
        p->next = NULL;
        p = nxt;
    }
    return vec;
}

(十一)LeetCode148. 排序鏈表

題目描述

在 O(n log n) 時(shí)間復(fù)雜度和常數(shù)級空間復(fù)雜度下,對鏈表進(jìn)行排序。

思路

鏈表歸并排序模板題。

代碼
ListNode* sortList (ListNode* head)
{
    if (!head || !head->next)
        return head;

    ListNode * fast = head;
    ListNode * slow = head;
    ListNode * pre = NULL;

    while (fast && fast->next)
    {
        pre = slow;
        fast = fast->next->next;
        slow = slow->next;
    }
    pre->next = NULL;
    return mergeTwoLists (sortList (head), sortList (slow));
}
ListNode* mergeTwoLists (ListNode* l1, ListNode* l2)
{
    if (l1 == NULL || l2 == NULL)
    {
        return l1 == NULL ? l2 : l1;
    }
    ListNode * head = new ListNode (0);
    ListNode * p = head;
    while (l1 && l2)
    {
        if (l1->val <= l2->val)
        {
            p->next = l1;
            l1 = l1->next;
        }
        else
        {
            p->next = l2;
            l2 = l2->next;
        }
        p = p->next;
    }
    p->next = l1 == NULL ? l2 : l1;
    return head->next;
}

(十二)LeetCode817. Linked List Components

題目描述

給出一個(gè)鏈表和一個(gè)數(shù)組,尋找有幾個(gè)component, 每一個(gè)component的val必須在數(shù)組G中而且在鏈表中是連接的。

思路

遍歷鏈表,使用tar變量來標(biāo)記,初始化為0,查看鏈表中當(dāng)前元素在是否在集合中

  • 假如不在集合中,如果tar為0,表示前面沒有找到或者已經(jīng)處理了;tar為1則表示前面的component結(jié)束了,則res++,并將tar置為0;
  • 假如在集合中,將tar置為1即可。
代碼
int numComponents (ListNode* head, vector<int>& G)
{
    if (!head || !G.size())
        return 0;
    set<int > S;
    for (int i = 0; i < G.size(); i++)
        S.insert (G[i]);
    ListNode * p = head;
    int tar = 0;
    int res = 0;
    while (p)
    {
        if (S.find (p->val) != S.end()) //查找成功
            tar = 1;
        else
        {
            if (tar)
            {
                tar = 0;
                res++;
            }
        }
        p = p->next;
    }
    if (tar == 1)
        res++;
    return res;
}

(十三)LeetCode147. 對鏈表進(jìn)行插入排序

題目描述

對鏈表進(jìn)行插入排序。

思路

直接模擬插入排序過程即可

代碼
ListNode* insertionSortList (ListNode* head)
{
    if (!head || !head->next)
        return head;
        
    ListNode * q = head->next;//q表示當(dāng)前需要排序的元素
    ListNode * qPre = head;//表示已經(jīng)排好序的最后一個(gè)元素,也就是q的前一個(gè)元素
    while (q)
    {
        ListNode * p = head, *pPre = NULL;//pPre表示p的前一個(gè)元素
        while (p != q)
        {
            if (p->val > q->val)//需要將q插入到p前面
            {
                ListNode * tmp = q->next;//保存q的next信息
                if (!pPre)//q作為頭結(jié)點(diǎn)
                    head = q;
                else//p的前一個(gè)元素指向q
                    pPre->next = q;
                q->next = p;//q指向p
                qPre->next = tmp;//已經(jīng)排好序的最后一個(gè)元素指向tmp(原來q的next)
                q = qPre;//恢復(fù)位置,繼續(xù)循環(huán)
                break;
            }
            pPre = p;
            p = p->next;
        }
        qPre = q;
        q = q->next;
    }
    return head;
}

(十四)LeetCode86. 分隔鏈表

題目描述

給定一個(gè)鏈表和一個(gè)特定值 x,對鏈表進(jìn)行分隔,使得所有小于 x 的節(jié)點(diǎn)都在大于或等于 x 的節(jié)點(diǎn)之前。
應(yīng)當(dāng)保留兩個(gè)分區(qū)中每個(gè)節(jié)點(diǎn)的初始相對位置。
輸入: head = 1->4->3->2->5->2, x = 3
輸出: 1->2->2->4->3->5

思路

分別用兩個(gè)鏈表記錄在左邊還是右邊,最后將左右兩個(gè)鏈表合并即可。

代碼
ListNode* partition (ListNode* head, int x)
{
    if (!head || !head->next)
        return head;
    ListNode * leftHead = NULL, * rightHead = NULL;
    ListNode * left = NULL, * right = NULL;
    ListNode * p = head;
    while (p)
    {
        if (p->val < x)
        {
            if (!left)
            {
                left = p;
                leftHead = p;
            }
            else
            {
                left->next = p;
                left = left->next;
            }
        }
        else
        {
            if (!right)
            {
                right = p;
                rightHead = p;
            }
            else
            {
                right->next = p;
                right = right->next;
            }
        }
        p = p->next;
    }
    if (!right)
    {
        left->next = NULL;
        return leftHead;
    }

    right->next = NULL;

    if (!left)
        return rightHead;

    left->next = rightHead;
    return leftHead;
}

(十五)LeetCode92. 反轉(zhuǎn)鏈表 II&LeetCode25. k個(gè)一組翻轉(zhuǎn)鏈表

題目描述
  1. 反轉(zhuǎn)從位置 m 到 n 的鏈表。請使用一趟掃描完成反轉(zhuǎn)。
  2. 給出一個(gè)鏈表,每 k 個(gè)節(jié)點(diǎn)一組進(jìn)行翻轉(zhuǎn),并返回翻轉(zhuǎn)后的鏈表。
    k 是一個(gè)正整數(shù),它的值小于或等于鏈表的長度。如果節(jié)點(diǎn)總數(shù)不是 k 的整數(shù)倍,那么將最后剩余節(jié)點(diǎn)保持原有順序。
思路
  1. 處理好細(xì)節(jié)即可,將需要反轉(zhuǎn)的區(qū)間斷開,將斷開出的結(jié)點(diǎn)信息保存下來,然后反轉(zhuǎn)對應(yīng)的區(qū)間,最后在將該區(qū)間插入到原來的位置
  2. 第二題需要用到第一題的解法,即每k個(gè)反轉(zhuǎn)一次,轉(zhuǎn)化成對應(yīng)區(qū)間的反轉(zhuǎn),調(diào)用第一題的代碼即可
代碼
ListNode* reverseKGroup (ListNode* head, int k)//K個(gè)一組反轉(zhuǎn)鏈表
{
    if (!head || !head->next)
        return head;
    ListNode * p = head;
    int len = 0;
    while (p)
    {
        len++;
        p = p->next;
    }
    if (k > len)
        return head;
    for (int i = 1; i + k - 1 <= len; i += k)
    {
        //反轉(zhuǎn)從位置 i 到 i+k-1 的鏈表
        head = reverseBetween (head, i, i + k - 1);
    }
    return head;
}

ListNode* reverseBetween (ListNode* head, int m, int n)//反轉(zhuǎn)從位置 m 到 n 的鏈表
{
    if (!head || !head->next)
        return head;
    int cnt = 1;
    ListNode * p = head, * pre = NULL;
    //分別表示翻轉(zhuǎn)前面的最后一個(gè)結(jié)點(diǎn),翻轉(zhuǎn)后面的第一個(gè)結(jié)點(diǎn),和需要翻轉(zhuǎn)的第一個(gè)結(jié)點(diǎn)
    ListNode * preTail = NULL, * nextHead = NULL, * revHead = NULL;
    while (p)
    {
        if (cnt == m)
        {
            preTail = pre;//保存翻轉(zhuǎn)前的最后一個(gè)結(jié)點(diǎn)
            revHead = p;//從該結(jié)點(diǎn)開始翻轉(zhuǎn)
        }
        if (cnt == n)
        {
            nextHead = p->next;//保存翻轉(zhuǎn)后面的第一個(gè)結(jié)點(diǎn)
            p->next = NULL;//使得翻轉(zhuǎn)的部分隔開
            break;
        }
        pre = p;
        p = p->next;
        cnt++;
    }
    revHead = reverseList (revHead);

    p = revHead;
    while (p->next)//找到翻轉(zhuǎn)部分最后一個(gè)元素
        p = p->next;

    if (!preTail)//從第一個(gè)元素翻轉(zhuǎn)則需更新頭結(jié)點(diǎn)
        head = revHead;
    else
        preTail->next = revHead;

    p->next = nextHead;
    return head;

}

ListNode * reverseList (ListNode * head)//反轉(zhuǎn)鏈表
{
    if (!head || !head->next)
        return head;
    ListNode * pre = NULL;
    ListNode * p = head;
    while (p)
    {
        ListNode * tmp = p->next;
        p->next = pre;
        pre = p;
        p = tmp;
    }
    return pre;
}

(十六)LeetCode61. 旋轉(zhuǎn)鏈表

題目描述

給定一個(gè)鏈表,旋轉(zhuǎn)鏈表,將鏈表每個(gè)節(jié)點(diǎn)向右移動(dòng) k 個(gè)位置,其中 k 是非負(fù)數(shù)。

思路

鏈表每個(gè)節(jié)點(diǎn)向右移動(dòng) k 個(gè)位置,可以理解為將鏈表前l(fā)en-k個(gè)元素放在了鏈表的末尾。
因?yàn)檫@樣對第一個(gè)結(jié)點(diǎn)來說,他現(xiàn)在的位置正好處于沒有移動(dòng)的k個(gè)元素后面。

代碼
ListNode* rotateRight (ListNode* head, int k)
{
    if (!head || !head->next || !k)
        return head;
    int len = 0;
    ListNode * p = head;
    ListNode * tail = head;
    while (tail->next)//找到末尾結(jié)點(diǎn)
    {
        len++;
        tail = tail->next;
    }
    len++;
    k = k % len;
    if (!k)
        return head;
    k = len - k;//向右旋轉(zhuǎn)k步可以理解成將前l(fā)en-k個(gè)元素放到鏈表尾部
    p = head;
    int cnt = 1;
    ListNode * newHead = NULL;
    while (p)
    {
        if (cnt == k)
        {
            newHead = p->next;
            p->next = NULL;
            break;
        }
        cnt++;
        p = p->next;
    }
    tail->next = head;
    return newHead;
}

(十七)LeetCode82. 刪除排序鏈表中的重復(fù)元素 II&LeetCode19. 刪除鏈表的倒數(shù)第N個(gè)節(jié)點(diǎn)

題目描述

給定一個(gè)排序鏈表,刪除所有含有重復(fù)數(shù)字的節(jié)點(diǎn),只保留原始鏈表中 沒有重復(fù)出現(xiàn) 的數(shù)字。

思路

注意細(xì)節(jié),小心頭結(jié)點(diǎn)
刪除鏈表的倒數(shù)第N個(gè)節(jié)點(diǎn)時(shí),用兩個(gè)指針,第一個(gè)先走N步,然后一起走,第一個(gè)指針為空時(shí),第二個(gè)指針則為倒數(shù)第N個(gè)結(jié)點(diǎn)。

代碼
ListNode* deleteDuplicates (ListNode* head)
{
    if (head == NULL || head->next == NULL)
        return head;
    ListNode * pre = NULL;
    ListNode * p = head;
    ListNode * next = NULL;
    while (p)
    {
        bool flag = false;
        next = p->next;
        while (next && next->val == p->val)
        {
            flag = true;
            next = next->next;
        }
        if (flag) //需要?jiǎng)h除
        {
            if (pre == NULL) //頭結(jié)點(diǎn)也需要?jiǎng)h除
                head = next;
            else
                pre->next = next;
        }
        else//不需要?jiǎng)h除,更新pre
            pre = p;
        p = next;
    }
    return head;
}

ListNode* removeNthFromEnd (ListNode* head, int n)
{
    if (!head)
        return NULL;
    ListNode * p1 = head;
    while (n--)
        p1 = p1->next;
    if (!p1)
        return head->next;
    ListNode * p2 = head;
    ListNode * pre = NULL;
    while (p1 && p2)
    {
        p1 = p1->next;
        pre = p2;
        p2 = p2->next;
    }
    //刪除p2
    pre->next = p2->next;
    return head;
}

(十八)LeetCode138. 復(fù)制帶隨機(jī)指針的鏈表

題目描述

給定一個(gè)鏈表,每個(gè)節(jié)點(diǎn)包含一個(gè)額外增加的隨機(jī)指針,該指針可以指向鏈表中的任何節(jié)點(diǎn)或空節(jié)點(diǎn)。
要求返回這個(gè)鏈表的深度拷貝。

思路

可以用哈希表保存隨機(jī)指針,更簡單的解法是:

  1. 首先在每個(gè)結(jié)點(diǎn)后面復(fù)制一個(gè)當(dāng)前結(jié)點(diǎn),并將其插入到該結(jié)點(diǎn)后面。
  2. 更新復(fù)制結(jié)點(diǎn)的random信息,注意細(xì)節(jié)
  3. 將鏈表一分為二,即將原鏈表中的奇數(shù)結(jié)點(diǎn)和偶數(shù)結(jié)點(diǎn)構(gòu)成兩個(gè)鏈表,返回偶數(shù)結(jié)點(diǎn)構(gòu)成的鏈表即可。
代碼
RandomListNode *copyRandomList (RandomListNode *head)
{
    if (!head)
        return NULL;
    RandomListNode * p = head;
    while (p)//在每個(gè)結(jié)點(diǎn)后面復(fù)制一個(gè)當(dāng)前結(jié)點(diǎn)
    {
        RandomListNode * node = new RandomListNode (p->label);
        node->next = p->next;
        p->next = node;
        p = node->next;
    }

    p = head;
    while (p)//更新復(fù)制后的結(jié)點(diǎn)的random指針
    {
        if (p->random)
            p->next->random = p->random->next;
        p = p->next->next;
    }

    RandomListNode * newHead = head->next;
    p = head;
    while (p)//將鏈表一分為二
    {
        RandomListNode * tmp = p->next;
        p->next = p->next ? p->next->next : NULL;
        p = tmp;
    }
    return newHead;
}

(十九)LeetCode23. 合并K個(gè)排序鏈表

題目描述

合并 k 個(gè)排序鏈表,返回合并后的排序鏈表。請分析和描述算法的復(fù)雜度。

思路
常規(guī)解法

可以用歸并排序的思想。
首先將k個(gè)鏈表兩兩合并成k/2個(gè)新鏈表,然后將k/2個(gè)鏈表兩兩合并成k/4個(gè)新鏈表
如此繼續(xù)直到最終只有一個(gè)鏈表即為結(jié)果。

高效解法---優(yōu)先級隊(duì)列實(shí)現(xiàn)

優(yōu)先級隊(duì)列的優(yōu)先級情況:堆頂始終為最小的結(jié)點(diǎn),空結(jié)點(diǎn)在堆底
首先將所有頭結(jié)點(diǎn)壓入優(yōu)先級隊(duì)列,然后逐個(gè)彈出,插入新的鏈表,同時(shí)將彈出的結(jié)點(diǎn)的next壓入優(yōu)先級隊(duì)列
直到優(yōu)先級隊(duì)列為空集或者隊(duì)首元素為空則結(jié)束

代碼
//法一   常規(guī)解法
ListNode* mergeKLists (vector<ListNode*>& lists)
{
    if (!lists.size())
        return NULL;
    int low=0,high=lists.size()-1;
    while(high)
    {
        low=0;//low重置為0,繼續(xù)合并
        while(low<high)
        {
            //合并后保存在low中
            lists[low]=mergeTwoLists(lists[low],lists[high]);
            low++;
            high--;
        }
    }
    return lists[0];
}
ListNode* mergeTwoLists (ListNode* l1, ListNode* l2)//合并兩個(gè)排序鏈表
{
    if (l1 == NULL || l2 == NULL)
    {
        return l1 == NULL ? l2 : l1;
    }
    ListNode * head = new ListNode (0);
    ListNode * p = head;
    while (l1 && l2)
    {
        if (l1->val <= l2->val)
        {
            p->next = l1;
            l1 = l1->next;
        }
        else
        {
            p->next = l2;
            l2 = l2->next;
        }
        p = p->next;
    }
    p->next = l1 == NULL ? l2 : l1;
    return head->next;
}
//法二  優(yōu)先級隊(duì)列實(shí)現(xiàn)
struct cmp //自定義優(yōu)先級
{
    bool operator () (ListNode * l1, ListNode * l2) // 重載括號
    {
        if (!l1 || !l2)
            return !l1;//NULL置于堆底
        return l1->val > l2->val; // 等同于greater, 堆頂元素是最小值
    }
};
ListNode* mergeKLists (vector<ListNode*>& lists)
{
    if (!lists.size())
        return NULL;
    priority_queue<ListNode *, vector<ListNode * >, cmp > Q; //優(yōu)先級隊(duì)列
    for (int i = 0; i < lists.size(); i++) //將每個(gè)序列的頭結(jié)點(diǎn)插入優(yōu)先級隊(duì)列
        Q.push (lists[i]);
    ListNode * head = new ListNode (-1); //偽頭結(jié)點(diǎn)
    ListNode * p = head;
    while (!Q.empty() && Q.top())
    {
        ListNode * tmp = Q.top();
        Q.pop();
        p->next = tmp;
        p = p->next;
        Q.push (tmp->next);
    }
    return head->next;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容