關於鏈表的心得體會


1.反轉鏈表

  經典考題,針對鏈表的反轉,第一時間需要聯想到將鏈表的指針進行反轉,而這種一系列的變化,可以使用遞歸,也可以使用while

迭代

假設鏈表為 1 \rightarrow 2 \rightarrow 3 \rightarrow \varnothing1→2→3→∅,我們想要把它改成 \varnothing \leftarrow 1 \leftarrow 2 \leftarrow 3∅←1←2←3。

在遍歷鏈表時,將當前節點的 \textit{next}next 指針改為指向前一個節點。由於節點沒有引用其前一個節點,因此必須事先存儲其前一個節點。在更改引用之前,還需要存儲后一個節點。最后返回新的頭引用。

class Solution {
public ListNode reverseList(ListNode head) {

        //使用指針的方法對全部的值進行反轉
        //首先定義空的節點
        ListNode pre = null;
        ListNode cur = head;
        while(cur!=null){
        ListNode next = cur.next;
        //當前節點的子節點將指向前節點
        cur.next = pre;
        //當前節點變為前節點
        pre = cur;
        //下一個節點變為當前節點
        cur = next;
        }
        return pre;
}
}

 

 

遞歸

遞歸版本稍微復雜一些,其關鍵在於反向工作。假設鏈表的其余部分已經被反轉,現在應該如何反轉它前面的部分?

假設鏈表為:

n1nk1nknk+1nm

若從節點 n_{k+1}nk+1 到 n_mnm 已經被反轉,而我們正處於 n_knk
n1nk1nknk+1nm

我們希望 n_{k+1}nk+1 的下一個節點指向 n_knk

​所以,n_k.\textit{next}.\textit{next} = n_knk.next.next=nk
需要注意的是 n_1n1 的下一個節點必須指向 \varnothing∅。如果忽略了這一點,鏈表中可能會產生環

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

 

2.鏈表取中間節點

給定一個頭結點為 head 的非空單鏈表,返回鏈表的中間結點。

如果有兩個中間結點,則返回第二個中間結點。

示例 1:

輸入:[1,2,3,4,5]
輸出:此列表中的結點 3 (序列化形式:[3,4,5])
返回的結點值為 3 。 (測評系統對該結點序列化表述是 [3,4,5])。
注意,我們返回了一個 ListNode 類型的對象 ans,這樣:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.
示例 2:

輸入:[1,2,3,4,5,6]
輸出:此列表中的結點 4 (序列化形式:[4,5,6])
由於該列表有兩個中間結點,值分別為 3 和 4,我們返回第二個結點。

 

需要聯想到的方法:

1.雙指針(快慢指針)法,既然是一個數組,我分別定義一個走兩步的head和一個走一步的head,每當head1走兩步,我的head2走1步,head1走完,head2剛好走到中間。

public ListNode middleNode(ListNode head) {
  ListNode slow = head, fast = head;
  while (fast != null && fast.next != null) {
  slow = slow.next;
  fast = fast.next.next;
  }
  return slow;
}

 

2.單指針法:我每走一步,i++,在第二次遍歷是,我走到i/2步,結束

public ListNode middleNode(ListNode head) {
  int n = 0;
  ListNode cur = head;
  while (cur != null) {
  ++n;
  cur = cur.next;
}
int k = 0;
cur = head;
while (k < n / 2) {
++k;
cur = cur.next;
}
return cur;
}

  

 

3.數組法:將節點變為數組,並記錄一個i++,變成節點數組之后直接獲取最大i/2的數組內容,也就是中間的結點。

public ListNode middleNode(ListNode head) {
ListNode[] A = new ListNode[100];
int t = 0;
while (head != null) {
A[t++] = head;
head = head.next;
}
return A[t / 2];
}

 

3.合並兩個有序鏈表

將兩個升序鏈表合並為一個新的 升序 鏈表並返回。新鏈表是通過拼接給定的兩個鏈表的所有節點組成的。 

 

輸入:l1 = [1,2,4], l2 = [1,3,4]
輸出:[1,1,2,3,4,4]
示例 2:

輸入:l1 = [], l2 = []
輸出:[]
示例 3:

輸入:l1 = [], l2 = [0]
輸出:[0]

 

遞歸,思想是這樣的,我先將l1和l2看作一個單獨的結點,進行判斷,如果L1小於L2,我就講L1.next和L2的剩余內容又看作一個整體,再進行遞歸。

public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if(l1 == null){
            return l2;
        }else if(l2 == null){
            return l1;
        }else if(l1.val<l2.val){
            l1.next = mergeTwoLists(l1.next,l2);
            return l1;
        }else{
            l2.next = mergeTwoLists(l1,l2.next);
            return l2;
        }
    }

 

 

4.將鏈表兩兩進行反轉

給定一個鏈表,兩兩交換其中相鄰的節點,並返回交換后的鏈表。

你不能只是單純的改變節點內部的值,而是需要實際的進行節點交換。

 

輸入:head = [1,2,3,4]
輸出:[2,1,4,3]

 

遞歸,將前兩個看作一個單位進行遞歸,返回

public ListNode swapPairs(ListNode head) {
        if(head==null||head.next==null){return head;}
        
        ListNode pre = head;
        ListNode cur = pre.next;
        ListNode next = cur.next;
        pre.next = swapPairs(next);
        cur.next = pre;
        return cur;

    }

 

 

 

5.將鏈表回文

變成數組和遞歸方法

1.變成數組

class Solution {
public boolean isPalindrome(ListNode head) {
List<Integer> vals = new ArrayList<Integer>();

// 將鏈表的值復制到數組中
ListNode currentNode = head;
while (currentNode != null) {
vals.add(currentNode.val);
currentNode = currentNode.next;
}

// 使用雙指針判斷是否回文
int front = 0;
int back = vals.size() - 1;
while (front < back) {
if (!vals.get(front).equals(vals.get(back))) {
return false;
}
front++;
back--;
}
return true;
}
}

2.遞歸

 

 

class Solution {
private ListNode frontPointer;

private boolean recursivelyCheck(ListNode currentNode) {
if (currentNode != null) {
if (!recursivelyCheck(currentNode.next)) {
return false;
}
if (currentNode.val != frontPointer.val) {
return false;
}
frontPointer = frontPointer.next;
}
return true;
}

public boolean isPalindrome(ListNode head) {
frontPointer = head;
return recursivelyCheck(head);
}
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM