LeetCode刷題(十七)-----鏈表-------easy部分(Java、C++)

160. 相交鏈表

編寫一個程序,找到兩個單鏈表相交的起始節點。
如下面的兩個鏈表:

在這裏插入圖片描述
在節點 c1 開始相交。
示例 1:
在這裏插入圖片描述
輸入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
輸出:Reference of the node with value = 8
輸入解釋:相交節點的值爲 8 (注意,如果兩個列表相交則不能爲 0)。從各自的表頭開始算起,鏈表 A 爲 [4,1,8,4,5],鏈表 B 爲 [5,0,1,8,4,5]。在 A 中,相交節點前有 2 個節點;在 B 中,相交節點前有 3 個節點。

示例 2:
在這裏插入圖片描述
輸入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
輸出:Reference of the node with value = 2
輸入解釋:相交節點的值爲 2 (注意,如果兩個列表相交則不能爲 0)。從各自的表頭開始算起,鏈表 A 爲 [0,9,1,2,4],鏈表 B 爲 [3,2,4]。在 A 中,相交節點前有 3 個節點;在 B 中,相交節點前有 1 個節點。
示例 3:
在這裏插入圖片描述
輸入:intersectVal = 0, listA = [2,6,4],listB =[1,5],skipA = 3, skipB = 2
輸出:null
輸入解釋:從各自的表頭開始算起,鏈表 A 爲 [2,6,4],鏈表 B 爲 [1,5]。由於這兩個鏈表不相交,所以 intersectVal 必須爲 0,而 skipA 和 skipB 可以是任意值。
解釋:這兩個鏈表不相交,因此返回 null。

注意:
如果兩個鏈表沒有交點,返回 null.
在返回結果後,兩個鏈表仍須保持原有的結構。
可假定整個鏈表結構中沒有循環。
程序儘量滿足 O(n) 時間複雜度,且僅用 O(1) 內存。

思路一:
方法一: 暴力法
對鏈表A中的每一個結點ai,遍歷整個鏈表B並檢查鏈表B中是否存在結點和ai相同。
複雜度分析
• 時間複雜度 : (mn)。
• 空間複雜度 : O(1)。
方法二: 哈希表法
遍歷鏈表A並將每個結點的地址/引用存儲在哈希表中。然後檢查鏈表 B 中的每一個結點 bi是否在哈希表中。若在,則 bi爲相交結點。
複雜度分析
• 時間複雜度 : O(m+n)。
• 空間複雜度 : O(m)或O(n)。

方法三:雙指針法
1.創建兩個指針pA和pB,分別初始化爲鏈表 A 和 B 的頭結點。然後讓它們向後逐結點遍歷。
2.當pA到達鏈表的尾部時,將它重定位到鏈表 B的頭結點 (你沒看錯,就是鏈表 B); 類似的,當 pB到達鏈表的尾部時,將它重定位到鏈表 A 的頭結點。
3.若在某一時刻 pA和pB 相遇,則 pA/pB爲相交結點。
4.想弄清楚爲什麼這樣可行,可以考慮以下兩個鏈表: A={1,3,5,7,9,11} 和 B={2,4,9,11},相交於結點 9。由於 B.length (=4) < A.length (=6),pB比 pA少經過 2個結點,會先到達尾部。將 pB重定向到 A 的頭結點,pA 重定向到 B 的頭結點後,pB要比 pA多走 2 個結點。因此,它們會同時到達交點。
如果兩個鏈表存在相交,它們末尾的結點必然相同。因此當pA/pB到達鏈表結尾時,記錄下鏈表 A/B 對應的元素。若最後元素不相同,則兩個鏈表不相交。
複雜度分析
• 時間複雜度 : O(m+n)O(m+n)。
• 空間複雜度 : O(1)O(1)。
方法三很好理解,就是a + all +b = b + all + a
all 是兩個鏈表相交部分的個數, a 是 A 鏈表不相交的個數, b 是 B 鏈表不相交的個數.

作者:LeetCode
鏈接:https://leetcode-cn.com/problems/intersection-of-two-linked-lists/solution/xiang-jiao-lian-biao-by-leetcode/
來源:力扣(LeetCode)
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

思路二:
解法一
思路
暴力法,快慢指針,一個指針在A鏈表,一個指針在B鏈表,逐一遍歷兩個鏈表,比較節點的位置,若位置相交,返回任意一個地址(兩者指向位置相同) 這個方法比較容易想到,但是使用了兩個while循環,時間複雜度非常高O(n^2)。
在這裏插入圖片描述
解法二
思路
這個方法比較巧妙,時間複雜度較低O(n)
1.假設兩條鏈表有交點,可知相交部分等長
2.那麼交點位置距離鏈表尾的距離必小於等於較短的鏈表
3.先將較長的鏈表剪去前面部分,使其的長度等於較短的鏈表
4.此時將指針從當前的headA 和headB同時向後移動,且對比指針是否相同,若相同則輸出指針。
在這裏插入圖片描述
作者:youlookdeliciousc
鏈接:https://leetcode-cn.com/problems/intersection-of-two-linked-lists/solution/cxiang-xi-ti-jie-by-youlookdeliciousc-12/
來源:力扣(LeetCode)
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

我的:

/**
 * 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) 
    {
        if(!headA || !headB)
        {
            return nullptr;
        }    
        ListNode *countA = headA;
        ListNode *countB = headB;
        int longA = 0;
        int longB = 0;
        while(countA)
        {
            ++longA;
            countA = countA -> next;
        }
        while(countB)
        {
            ++longB;
            countB = countB -> next;
        }
        int s = max(longA,longB) - min(longA,longB);
        if(longA > longB)
        {
            for(int i = 0;i < s;i++)
            {
                headA = headA -> next;
            }
        }
        else
        {
            for(int i=0;i<s;i++)
            {
                headB = headB -> next;
            }
        }
        while(headA)
        {
            if(headA == headB)
            {
                return headA;
            }
            headA = headA -> next;
            headB = headB -> next;
        }
        return nullptr;
    }
};

141. 環形鏈表

給定一個鏈表,判斷鏈表中是否有環。
爲了表示給定鏈表中的環,我們使用整數 pos 來表示鏈表尾連接到鏈表中的位置(索引從 0 開始)。如果 pos 是-1,則在該鏈表中沒有環。
示例 1:
輸入:head = [3,2,0,-4], pos = 1
輸出:true
解釋:鏈表中有一個環,其尾部連接到第二個節點。

在這裏插入圖片描述
示例 2:
輸入:head = [1,2], pos = 0
輸出:true
解釋:鏈表中有一個環,其尾部連接到第一個節點。
在這裏插入圖片描述
示例 3:
輸入:head = [1], pos = -1
輸出:false
解釋:鏈表中沒有環。
在這裏插入圖片描述
進階:你能用O(1)(即,常量)內存解決此問題嗎?
思路一:
方法一:哈希表

思路
我們可以通過檢查一個結點此前是否被訪問過來判斷鏈表是否爲環形鏈表。常用的方法是使用哈希表。
算法
我們遍歷所有結點並在哈希表中存儲每個結點的引用(或內存地址)。如果當前結點爲空結點 null(即已檢測到鏈表尾部的下一個結點),那麼我們已經遍歷完整個鏈表,並且該鏈表不是環形鏈表。如果當前結點的引用已經存在於哈希表中,那麼返回 true(即該鏈表爲環形鏈表)。
在這裏插入圖片描述
複雜度分析
時間複雜度:O(n),對於含有n個元素的鏈表,我們訪問每個元素最多一次。添加一個結點到哈希表中只需要花費 O(1)的時間。
空間複雜度:O(n),空間取決於添加到哈希表中的元素數目,最多可以添加 n個元素。

方法二:雙指針
思路

想象一下,兩名運動員以不同的速度在環形賽道上跑步會發生什麼?
算法

通過使用具有 不同速度 的快、慢兩個指針遍歷鏈表,空間複雜度可以被降低至O(1)。慢指針每次移動一步,而快指針每次移動兩步。
如果列表中不存在環,最終快指針將會最先到達尾部,此時我們可以返回 false。

現在考慮一個環形鏈表,把慢指針和快指針想象成兩個在環形賽道上跑步的運動員(分別稱之爲慢跑者與快跑者)。而快跑者最終一定會追上慢跑者。這是爲什麼呢?考慮下面這種情況(記作情況 A)- 假如快跑者只落後慢跑者一步,在下一次迭代中,它們就會分別跑了一步或兩步並相遇。
其他情況又會怎樣呢?例如,我們沒有考慮快跑者在慢跑者之後兩步或三步的情況。但其實不難想到,因爲在下一次或者下下次迭代後,又會變成上面提到的情況 A。
在這裏插入圖片描述
複雜度分析
時間複雜度:O(n),讓我們將n設爲鏈表中結點的總數。爲了分析時間複雜度,我們分別考慮下面兩種情況。
鏈表中不存在環:
快指針將會首先到達尾部,其時間取決於列表的長度,也就是 O(n)O(n)。
鏈表中存在環:
我們將慢指針的移動過程劃分爲兩個階段:非環部分與環形部分:

1.慢指針在走完非環部分階段後將進入環形部分:此時,快指針已經進入環中迭代次數=非環部分長度=N
2.兩個指針都在環形區域中:考慮兩個在環形賽道上的運動員 - 快跑者每次移動兩步而慢跑者每次只移動一步。其速度的差值爲 1,因此需要經過,,,循環後,快跑者可以追上慢跑者。這個距離幾乎就是 “\text{環形部分長度 K}環形部分長度 K” 且速度差值爲 1,我們得出這樣的結論迭代次數=近似於 “環形部分長度 K”.

因此,在最糟糕的情形下,時間複雜度爲 O(N+K),也就是 O(n)。
空間複雜度:O(1),我們只使用了慢指針和快指針兩個結點,所以空間複雜度爲 O(1)。

作者:LeetCode
鏈接:https://leetcode-cn.com/problems/linked-list-cycle/solution/huan-xing-lian-biao-by-leetcode/
來源:力扣(LeetCode)
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

思路二:
方法三:快慢指針

初始化 slow = head->next,每次走一步
初始化 fast =head->next->next,每次走兩步,每走一步判斷一次
存在環 fast 和 slow 會相遇
在這裏插入圖片描述
作者:chenlele
鏈接:https://leetcode-cn.com/problems/linked-list-cycle/solution/huan-xing-lian-biao-by-gpe3dbjds1/
來源:力扣(LeetCode)
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。
我的:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) 
    {
        if(head == NULL || head->next == NULL)
        {
            return false;
        }  
        ListNode *slow = head;
        ListNode *fast = head -> next;
        while(fast)
        {
            slow = slow -> next;
            for(int i = 0;i<2;i++)
            {
                if(fast -> next == NULL)
                {
                    return false;
                }
                fast = fast -> next;
                if(fast == slow)
                {
                    return true;
                }
            }
        }
        return false;
    }
};

234. 迴文鏈表

請判斷一個鏈表是否爲迴文鏈表。
示例 1:
輸入: 1->2
輸出: false
示例 2:
輸入: 1->2->2->1
輸出: true
進階:
你能否用 O(n) 時間複雜度和 O(1) 空間複雜度解決此題?

思路一:
思路:

此題基本思路就是講鏈表分爲兩段,並且把前面一段進行倒置,然後再比較前後兩段是否相同即可,最簡單的方法就是先計算鏈表的長度,然後將前面一半用頭插法的辦法進行倒置,最後再比較,但是由於需要的是一半這個特殊的位置,所以我們可以考慮快慢指針,何謂快慢指針,其實就是兩個指針,一根指針一次移動一個位置,另一個指針一次移動兩個位置,就好比兩個人走路一個速度爲v,一個速度爲2v,當速度快的人走到底了設長度爲x,這個速度快的人所話的時間就爲x/(2v)而對於相同的時間而言對於那個走得慢的就走到的距離爲d. d = v* t,而t爲x/(2v),代入就爲d = x/2所以當一個人走到底了另一個人正好走到了一半。所以快慢指針適合用於有中點相關的場景,同時爲了更加簡便,我們可以採用一邊移動一邊將其倒置,這個倒置的順序只需要在圖上畫一畫就湊出來了,同時需要注意鏈表個數爲奇數和爲偶數對於後一半的頭結點位置有所不同。所以此題最關鍵就是可以在找到中點的同時就進行倒置了,也要記住有中點或倍數的時候可以考慮快慢指針來解決問題。
在這裏插入圖片描述
作者:heroine-yun
鏈接:https://leetcode-cn.com/problems/palindrome-linked-list/solution/wei-shi-yao-yong-kuai-man-zhi-zhen-by-vailing/
來源:力扣(LeetCode)
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

思路二:
整體思路是先用快慢指針找中點(慢指針的最終位置)
然後再反轉中點指向的後半部分鏈表
前半部分再與後半部分逐元素比較

時間複雜度爲O(n)
空間複雜度關鍵看反轉鏈表是迭代還是遞歸,我這裏用迭代所以是O(1),遞歸則是O(n)
爲了行數,犧牲了代碼可讀性,可能看起來有點費勁…
在這裏插入圖片描述
作者:mei-de-gan-qing-de-fu-du-ya
鏈接:https://leetcode-cn.com/problems/palindrome-linked-list/solution/cji-jian-xing-shu-jie-fa-by-mei-de-gan-qing-de-fu-/
來源:力扣(LeetCode)
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

我的:

/**
 * 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 || !head -> next)
        {
            return true;
        }
        ListNode*pre = NULL;
        ListNode*slow = head;
        ListNode*fast = head;
        ListNode*s = NULL;
        while(fast != NULL && fast -> next != NULL)
        {
            pre = slow;
            slow = slow -> next;
            fast =  fast -> next -> next;
            pre -> next = s;
            s = pre;
        }
        ListNode*temp = slow;
        if(fast != NULL)
        {
            temp = temp -> next;
        }    
        slow = pre;
        while(temp != NULL)
        {
            if(temp -> val != slow -> val)
            {
                return false;
            }
            else
            {
                temp = temp -> next;
                slow = slow -> next;
            }
        }
        return true;
    }
};

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