字節跳動+百度+阿里巴巴高頻面試題之鏈表專題(二)

高頻面試題之鏈表專題公開課(二)

本節目標

  • 1、逆置一個單鏈表。(2020年阿里巴巴二面原題)
  • 2、判斷單鏈表是否是迴文結構。(2019年字節跳動二面原題)
  • 3、刪除一個有序單鏈表中的重複節點。(2019年字節跳動二面原題)
  • 4、複雜鏈表的複製。(2020年百度二面原題)

在這裏插入圖片描述

1、逆置一個單鏈表。

OJ鏈接:https://leetcode-cn.com/problems/reverse-linked-list/description/

高頻考察的大廠雲圖:

在這裏插入圖片描述

解題思路:
  1. 三指針翻轉法:記錄連續的三個節點,原地修改節點指向的方向。
  2. 頭插法:取下原鏈表的每個節點都進行頭插到新鏈表。

在這裏插入圖片描述

代碼實現:
// 三個指針翻轉的思路
/**
 * 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 == NULL || head->next == NULL)
            return head;

        ListNode* n1, *n2, *n3;
        n1 = NULL;
        n2 = head;
        n3 = n2->next;

        //中間節點不爲空,繼續修改指向
        while(n2)
        {
            //中間節點指向反轉
            n2->next = n1;

            //更新三個連續的節點
            n1 = n2;
            n2 = n3;

            // 需要注意到最後兩個節點時,n3已經爲空
            if(n3)
                n3 = n3->next;
        }

        //返回新的頭
        return n1;
    }
};

// 頭插的思路
/**
 * 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) {
        ListNode* newhead = NULL;
        ListNode* cur = head;
        while(cur)
        {
            struct ListNode* next = cur->next;
            //頭插新節點,更新頭
            cur->next = newhead;
            newhead = cur;
            cur = next;
        }

        return newhead;
    }
};

2、判斷單鏈表是否是迴文結構。

OJ鏈接:https://www.nowcoder.com/practice/d281619e4b3e4a60a2cc66ea32855bfa?tpId=49&&tqId=29370&rp=1&ru=/activity/oj&qru=/ta/2016test/question-ranking

高頻考察的大廠雲圖:

在這裏插入圖片描述

解題思路:

需要注意本題要求了複雜度爲O(N),空間複雜度爲O(1)

1、本題說明了鏈表的長度小於900,所以這裏我們可以開闢一個900個空間的數組,將鏈表的數據放到數組中再進行判斷。這種方法雖然能過OJ,但是實際面試中是不符合面試官的要求的。

2、通過slow走一步,fast走兩步,找到中間節點。然後將後半段鏈表反轉,反轉後的鏈表跟原鏈表對比是否相同,相同則是迴文結構。這樣寫才滿足上面的對複雜度的要求。

在這裏插入圖片描述

代碼實現:
/*
此題也可以先把鏈表中的元素值全部保存到數組中,然後再判斷數組是否爲迴文。不建議使用這種解法,因爲如果沒有告訴鏈表最大長度,則不能同此解法
*/
class PalindromeList {
public:
    bool chkPalindrome(ListNode* A) {
        // write code here
        int a[900] = {0};
        ListNode* cur = A;
        int n = 0;
        //保存鏈表元素
        while(cur)
        {
            a[n++] = cur->val;
            cur = cur->next;
        }
        //判斷數組是否爲迴文結構
        int begin = 0, end = n-1;
        while(begin < end)
        {
            if(a[begin] != a[end])
                return false;
            ++begin;
            --end;
        }

        return true;
    }
};

/*
解題思路:
此題可以先找到中間節點,然後把後半部分逆置,最近前後兩部分一一比對,如果節點的值全部相同,則即爲迴文。
*/
class PalindromeList {
public:
    bool chkPalindrome(ListNode* A) {
        if (A == NULL || A->next == NULL)
            return true;
        ListNode* slow, *fast, *prev, *cur, *nxt;
        slow = fast = A;
        //找到中間節點
        while (fast && fast->next)
        {
            slow = slow->next;
            fast = fast->next->next;
        }
        prev = NULL;
        //後半部分逆置
        cur = slow;
        while (cur)
        {
            nxt = cur->next;
            cur->next = prev;
            prev = cur;
            cur = nxt;
        }
        //逐點比對
        while (A && prev)
        {
            if (A->val != prev->val)
                return false;
            A = A->next;
            prev = prev->next;
        }
        return true;
    }
};

3、刪除一個有序單鏈表中的重複節點。

OJ鏈接:https://www.nowcoder.com/practice/fc533c45b73a41b0b44ccba763f866ef?tpId=13&&tqId=11209&rp=1&ru=/activity/oj&qru=/ta/coding-interviews/question-ranking

高頻考察的大廠雲圖:

在這裏插入圖片描述

解題思路:
  1. 定義三個指針n1、n2、n3,使用n2和n3去找鏈表中的重複階段區間段,找到以後,刪除n2到n3中間的重複區間,然後讓n1,鏈接上重複區間後面一段。再繼續往後找重複區間,再重複上面的過程。ps:需要注意的是本題邏輯並不難,但是代碼控制中有很多邊界條件需要注意,控制不好,代碼很容易崩潰。

在這裏插入圖片描述

代碼實現:
/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};
*/
/*
解題思路:
此題可以先找出相同節點的區間,然後刪除區間中的所有值,直到把鏈表遍歷完結束
*/
class Solution {
public:
    ListNode* deleteDuplication(ListNode* pHead)
    {
        if(pHead == NULL || pHead->next == NULL)
            return pHead;

        ListNode* n1 = NULL;
        ListNode* n2 = pHead;
        ListNode* n3 = n2->next;
        while(n3 != NULL)
        {
            //如果相鄰節點不相同,則不需要刪除,更新節點,繼續向後遍歷
            if(n2->val != n3->val)
            {
                n1 = n2;
                n2 = n3;
                n3 = n3->next;
            }
            else
            {
                //如果相鄰節點相同
                //則n3去找第一個不相同的節點
                while(n3 && n3->val == n2->val)
                {
                    n3 = n3->next;
                }
                //重新鏈接,如果要刪除的包括頭節點,則更新頭節點
                if(n1)
                    n1->next = n3;
                else
                    pHead = n3;

                // 刪除掉重複的節點
                while(n2 != n3)
                {
                    ListNode* next = n2->next;
                    delete n2;
                    n2 = next;
                }

                //更新節點
                n2 = n3;
                if(n3)
                    n3 = n3->next;
            }
        }

        return pHead;
    }
};

4、複雜鏈表的複製

OJ鏈接:https://leetcode-cn.com/problems/copy-list-with-random-pointer/description/

高頻考察的大廠雲圖:

在這裏插入圖片描述

解題思路:

此題可以分三步進行:

  1. 拷貝鏈表的每一個節點,拷貝的節點先鏈接到原鏈表被拷貝節點的後面
  2. 處理拷貝節點的random指針:拷貝節點的random指針指向被拷貝節點隨機指針的下一個位置
  3. 拆解鏈表,把拷貝的鏈表從原鏈表中拆解出來,再鏈接出拷貝鏈表

在這裏插入圖片描述

代碼實現:
/*
// 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) {
        // 1.拷貝鏈表,並插入到原節點的後面
        Node* cur = head;
        while(cur)
        {
            Node* copy = new Node(cur->val);

            Node* next = cur->next;
            // 插入
            cur->next = copy;
            copy->next = next;

            // 迭代往下走
            cur = next;
        }

        // 2.置拷貝節點的random
        cur = head;
        while(cur)
        {
            Node* copy = cur->next;
            if(cur->random != NULL)
                copy->random = cur->random->next;
            else
                copy->random = NULL;

            cur = copy->next;
        }

        // 3.解拷貝節點,鏈接拷貝節點
        Node* copyHead = NULL, *copyTail = NULL;
        cur = head;
        while(cur)
        {
            Node* copy = cur->next;
            Node* next = copy->next;

            // copy解下來尾插鏈接到拷貝節點
            if(copyTail == NULL)
            {
                copyHead = copyTail = copy;
            }
            else
            {   
                copyTail->next = copy;
                copyTail = copy;
            }

            cur->next = next;

            cur = next;
        }

        return copyHead;
    }
};
/*思路跟上面的方法基本一致,差別不再是拷貝節點掛在原節點後面建立映射關係,而是通過將原節點和拷貝節點存儲到map中建立映射關係,這樣也是通過3步就可以複製出拷貝鏈表*/
// 1.拷貝鏈表,使用map建立原鏈表節點和拷貝節點的映射
// 2.置拷貝節點的random. 通過map找到node->random的拷貝節點,就可以值copy節點的random。
// 3.從map中找到拷貝節點,鏈接拷貝節點
// 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) {
        map<Node*, Node*> nodeCopyMap;

        // 1.拷貝鏈表,使用map建立原鏈表節點和拷貝節點的映射
        Node* cur = head;
        while(cur)
        {
            Node* copy = new Node(cur->val);
            nodeCopyMap[cur] = copy;

            cur = cur->next;
        }

        // 2.置拷貝節點的random
        cur = head;
        while(cur)
        {
            Node* copy = nodeCopyMap[cur];
            if(cur->random != NULL)
                copy->random = nodeCopyMap[cur->random];
            else
                copy->random = NULL;

            cur = cur->next;
        }

        // 3.從map中找到拷貝節點,鏈接拷貝節點
        Node* copyHead = NULL, *copyTail = NULL;
        cur = head;
        while(cur)
        {
            // 找到cur映射的拷貝節點
            Node* copy = nodeCopyMap[cur];
            if(copyTail == NULL)
            {
                copyHead = copyTail = copy;
            }
            else
            {   
                copyTail->next = copy;
                copyTail = copy;
            }

            cur = cur->next;
        }

        return copyHead;
    }
};

如果你看了以後不是很明白,你可以點擊看下面的視頻講解:
視頻講解(鼠標點這裏)

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章