力扣刷題筆記(七)

來水一波吧,這兩天有事兒都沒寫這一部分的,有點難受。

前兩題都很基本,第三題還好啦。

這篇主要是關於鏈表的,所以我先放一個鏈表的基礎篇,今天整理的。

回顧通用鏈表(親測代碼示例)

接下來如正題:

第一題 刪除指定節點

請編寫一個函數,使其可以刪除某個鏈表中給定的(非末尾)節點,你將只被給定要求被刪除的節點。

現有一個鏈表 – head = [4,5,1,9]

示例 1:

輸入: head = [4,5,1,9], node = 5
輸出: [4,1,9]
解釋: 給定你鏈表中值爲 5 的第二個節點,那麼在調用了你的函數之後,該鏈表應變爲 4 -> 1 -> 9.
示例 2:

輸入: head = [4,5,1,9], node = 1
輸出: [4,5,9]
解釋: 給定你鏈表中值爲 1 的第三個節點,那麼在調用了你的函數之後,該鏈表應變爲 4 -> 5 -> 9.

說明:

鏈表至少包含兩個節點。
鏈表中所有節點的值都是唯一的。
給定的節點爲非末尾節點並且一定是鏈表中的一個有效節點。
不要從你的函數中返回任何結果。

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/delete-node-in-a-linked-list
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。


第二題 刪除倒數第n個節點

給定一個鏈表,刪除鏈表的倒數第 n 個節點,並且返回鏈表的頭結點。

示例:

給定一個鏈表: 1->2->3->4->5, 和 n = 2.

當刪除了倒數第二個節點後,鏈表變爲 1->2->3->5.
說明:

給定的 n 保證是有效的。

進階:

你能嘗試使用一趟掃描實現嗎?

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。


第三題 翻轉鏈表

反轉一個單鏈表。

示例:

輸入: 1->2->3->4->5->NULL
輸出: 5->4->3->2->1->NULL
進階:
你可以迭代或遞歸地反轉鏈表。你能否用兩種方法解決這道題?

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/reverse-linked-list
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。

我的題解(1)

濫竽充數一下

void deleteNode(ListNode* node) {
        // write your code here
        if(node==NULL) return;
        if(node->next==NULL)
        {
            node=NULL;
            return;
        }
        node->val=node->next->val;
        node->next=node->next->next;
    }

我的題解(2)

這題難度居然能排在中等。。。
還是濫竽充數一下。

ListNode* removeNthFromEnd(ListNode* head, int n) {
       if(!head) return NULL;
        ListNode* new_head = new ListNode(0);
        new_head->next = head;
        ListNode* slow = new_head, *fast = new_head;
        for(int i = 0;i<n;++i){  //fast先移動n
            fast = fast->next;
        }
        if(!fast) return new_head->next;
        while(fast->next){      //一起移動
            fast = fast->next;
            slow = slow->next;
        }
        slow->next = slow->next->next;
        return new_head->next;
    }

我的題解(3)

這題有點意思,重點講這題吧

一開始我的想法是這樣的:先遍歷一遍,將鏈表中的值都取出來,然後逆向插回去,不過這樣要遍歷兩遍,於是我就想一遍實現。

我的想法是這樣的:頭向後遍歷,每次將當前遍歷到的尾部節點提取出來,放到最前面,實現逆轉乾坤。
但是,想法總是好的。

先看我的第一個實驗:

struct ListNode {
    int val;
    ListNode* next;
    ListNode(int x) : val(x), next(NULL) {}
};	//通用
//ListNode* reverseList(ListNode* head)
//{
//    ListNode* node_temp;
//    ListNode* new_head;
//
//    node_temp = head;
//    //遍歷一個節點,就把它拿下來放到頭去
//    while (head->next != NULL)
//    {
//        //先考慮只又兩個節點的情況 
//        head = head->next;
//        new_head = head;
//        new_head->next = node_temp;
//        node_temp = new_head;
//    }
//    return new_head;
//}

我也不多說,爲什麼失敗,要是一眼看出來的話,容我呼你一聲大神。反正這失敗我是無語了。


再看第二次,算了我還是說一下上面那個。
我想原地逆置,然後一不小心,成環了。。。。。。。

於是我就換了個思路,用兩條鏈表,一個原鏈表,一個鏈棧。
然而,想法還是好的。

//ListNode* reverseList(ListNode* head) 
//{
//    ListNode* node_temp = new ListNode(NULL);
//    ListNode* new_head = new ListNode(NULL);   //鏈棧的頭
//    
//    //遍歷一個節點,就把它拿下來放到頭去
//    while (head != NULL)
//    {
//        node_temp = head;   //先將節點取出
//        //先考慮只又兩個節點的情況 
//        head = head->next;  //這個不放這裏又成環了
//        
//        node_temp->next = new_head;
//        new_head= node_temp;
//    }
//    return new_head;
//}

哎,要是一眼沒看出來哪裏出問題了,那咱倆水平半斤八兩。

這時候,我的好兄弟一直喊我用頭插法,我不以爲然,讓他給我發過來瞅瞅。


在我看之前,我好像發現了什麼。於是我就把上面的代碼稍微調了一下。。。
成功了。。。

ListNode* reverseList(ListNode* head) 
{
    ListNode* node_temp = NULL; //這裏設爲NULL
    ListNode* new_head = NULL;   //鏈棧的頭
    
    //遍歷一個節點,就把它拿下來放到頭去
    while (head != NULL)
    {
        node_temp = head;   //先將節點取出
        //先考慮只又兩個節點的情況 
        head = head->next;  //這個不放這裏又成環了
        
        node_temp->next = new_head;
        new_head= node_temp;
    }
    return new_head;
}

ListNode* reverseList(ListNode* head) 
    {
        ListNode* node_temp = NULL; //這裏設爲NULL
        ListNode* new_head = NULL;   //鏈棧的頭

        //遍歷一個節點,就把它拿下來放到頭去
        while (head != NULL)
        {
            node_temp = head;   //先將節點取出
            //先考慮只又兩個節點的情況 
            head = head->next;  //這個不放這裏又成環了

            node_temp->next = new_head;
            new_head= node_temp;
        }
        return new_head;
    }

成功就算了,我回頭一看他的“頭插法”,我都懵了,怎麼一模一樣。。。。。


官方題解(3)

假設存在鏈表 1 → 2 → 3 → Ø,我們想要把它改成 Ø ← 1 ← 2 ← 3。

在遍歷列表時,將當前節點的 next 指針改爲指向前一個元素。由於節點沒有引用其上一個節點,因此必須事先存儲其前一個元素。在更改引用之前,還需要另一個指針來存儲下一個節點。不要忘記在最後返回新的頭引用!

public ListNode reverseList(ListNode head) {
    ListNode prev = null;
    ListNode curr = head;
    while (curr != null) {
        ListNode nextTemp = curr.next;
        curr.next = prev;
        prev = curr;
        curr = nextTemp;
    }
    return prev;
}

複雜度分析

時間複雜度:O(n),假設 n 是列表的長度,時間複雜度是 O(n)。
空間複雜度:O(1)。

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

方法二:遞歸
遞歸版本稍微複雜一些,其關鍵在於反向工作。假設列表的其餘部分已經被反轉,現在我該如何反轉它前面的部分?

假設列表爲:

在這裏插入圖片描述

public ListNode reverseList(ListNode head) {
    if (head == null || head.next == null) return head;
    ListNode p = reverseList(head.next);
    head.next.next = head;
    head.next = null;
    return p;
}

複雜度分析

時間複雜度:O(n)O(n),假設 nn 是列表的長度,那麼時間複雜度爲 O(n)O(n)。
空間複雜度:O(n)O(n),由於使用遞歸,將會使用隱式棧空間。遞歸深度可能會達到 nn 層。

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

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