劍指offer刷題:鏈表篇

鏈表題

  1. 設置兩個指針。
  2. 用棧或隊列或hash表來存儲相關信息。

一、從尾到頭打印鏈表(1版面試題5)

1.題目

輸入一個鏈表,按鏈表從尾到頭的順序返回一個ArrayList。

2.思路

要注意算法是否會改變原鏈表的結構
後進先出首先就想到用棧結構。

3.代碼

import java.util.*;
public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
       Stack<Integer> temp = new Stack<>();
        ArrayList<Integer> arr = new ArrayList<>();
        while(listNode != null){            //把結點依次放入棧中
            temp.push(listNode.val);
            listNode = listNode.next;
        }
        while(!(temp.isEmpty())){           //依次彈棧存到目標鏈表中
            arr.add(temp.pop());
        }
        return arr;
    }
}

二、鏈表中環的入口結點(2版面試題23)

1.題目

給一個鏈表,若其中包含環,請找出該鏈表的環的入口結點,否則,輸出null。

2.思路

遍歷鏈表,當遇到之前遍歷過的結點時(說明有環),返回這個結點。
在這裏插入圖片描述
判斷是否遇到遍歷過的結點的方法:
將之前遍歷的結點以此放入到ArrayList中,然後利用contains函數即可。

3.代碼

import java.util.*;
/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public ListNode EntryNodeOfLoop(ListNode pHead)
    {
         ListNode start = null;
        ArrayList<ListNode> arr = new ArrayList<>();
        while(pHead != null){
            if(arr.contains(pHead)){
                return pHead;
            }
            arr.add(pHead);
            pHead = pHead.next;
        }
        return start;
    }   
}

三、刪除鏈表中的重複結點

1.題目

在一個排序的鏈表中,存在重複的結點,請刪除該鏈表中重複的結點,重複的結點不保留,返回鏈表頭指針。 例如,鏈表1->2->3->3->4->4->5 處理後爲 1->2->5

2.算法思路

設置兩個結點,一個是上一個不重複的結點pre。一個是尋找下一個不重複的結點last。同時,再創建一個頭節點head(好判斷pHead是否重複)

3.代碼

public class Solution {
    public ListNode deleteDuplication(ListNode pHead)
    {
        if(pHead==null || pHead.next==null){return pHead;}
       ListNode head = new ListNode(Integer.MIN_VALUE);
       head.next = pHead;
       ListNode pre = head;         //pre是上一個不重複的結點
       ListNode last = pHead;       //last是用來尋找下一個不重複的結點
        while(last != null){
            if(last.next != null && last.val == last.next.val){    //發現last和last.next重複
                while(last.next != null && last.val == last.next.val){   //該循環是找到當前一直重複的最後一個結點
                    last = last.next;
                }
                pre.next = last.next;
                last = last.next;
            }else{
                pre = pre.next;
                last = last.next;
            }
    }
        return head.next;       //注意,pHead可能已經改變(有可能出現11112的情況)
    }
}

四、鏈表中倒數第k個結點

1.題目

輸入一個鏈表,輸出該鏈表中倒數第k個結點。

2.思路

(1)利用棧,需要額外的空間
先將鏈表放入棧中,再彈出第k個結點即可。
(2)設置兩個指針p,q
p從開始就走,q比p慢k步。當p走到鏈表尾時,這是q就處於倒數第k個結點。

3.代碼

(1)利用棧,需要額外的空間

import java.util.Stack;

public class Solution {
    public ListNode FindKthToTail(ListNode head,int k) {
        if(head==null || k<=0){return null;}	//特殊情況
        Stack<ListNode> sta = new Stack<>();
        ListNode temp = head;
        while(temp!=null){		//鏈表放入棧中
            sta.push(temp);
            temp = temp.next;
        }
        for(int i=1;i<k;i++){		//先彈出k-1個結點
            sta.pop();
        }
        if(sta.size() == 0){			//若不存在倒數第k個
            return null;
        }
        return sta.pop();		//彈出倒數第k個
    }
}

(2)設置兩個指針p,q

public class Solution {
public ListNode FindKthToTail(ListNode head,int k) { 
        ListNode p, q;
        p = q = head;               //兩個指針都從頭開始走
        int i = 0;
        for (; p != null; i++) {        //p開始就走,一直走到鏈表結尾
        if (i >= k){                    //當p走了k步時,q纔開始走
            q = q.next;
        }
        p = p.next;                     //p開始就走
        }
        return i < k ? null : q;        //當p走到鏈表尾時,這是q剛好在倒數第k(q比p慢k步)
        }
}

五、反轉鏈表

1.題目

輸入一個鏈表,反轉鏈表後,輸出新鏈表的表頭。

2.思路

鏈表的題目先畫圖。
在這裏插入圖片描述

3.代碼

public class Solution {
    public ListNode ReverseList(ListNode head) {       
        ListNode pre = null;    //pre用於記錄當前結點的前一個結點      
        ListNode next = null;    //next用於記錄當前結點的下一個結點    
        while(head != null){
            next = head.next;    //用next記錄當前結點的下一個結點地址           
            head.next = pre;     //讓被當前結點與鏈表斷開並指向前一個結點pre。          
            pre = head;         //pre指針指向當前結點          
            head = next;        //head指向next(保存着原鏈表中head的下一個結點地址)
        }
        return pre;//當循環結束時,pre所指的就是反轉鏈表的頭結點
    }
}

六、兩個鏈表的第一個公共結點

1.題目

輸入兩個鏈表,找出它們的第一個公共結點。

2.思路

**公共結點的特徵:**有兩個前結點,有一個後結點。
在這裏插入圖片描述
(1)利用公共結點特徵+棧
用兩個棧分別來裝這兩個鏈表。然後這兩個棧一起彈棧,若相等,則說明使用的同一個結點。第一次遇到不同,說明,上一個結點就是第一個公共結點。

3.代碼

(1)利用公共結點特徵+棧

import java.util.Stack;

public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        Stack<ListNode> sta1 = new Stack<>();
        Stack<ListNode> sta2 = new Stack<>();
        ListNode result = null;
        while(pHead1!=null){        //用棧sta1裝鏈表1
            sta1.push(pHead1);
            pHead1 = pHead1.next;
        }
        while(pHead2!=null){        //用棧sta2裝鏈表2
            sta2.push(pHead2);
            pHead2 = pHead2.next;
        }
        while(!(sta1.isEmpty() || sta2.isEmpty())){  //兩個棧同時彈棧
            if(sta1.peek().val == sta2.peek().val){  //若相等,說明是公共結點,並記錄這個公共結點
                result = sta1.peek();
                sta1.pop();
                sta2.pop();
            }else{          //第一次發現不相等,直接結束循環,此時result保存的就是第一個公共結點
                break;
            }
        }
        return result;
    }
}

七、合併兩個排序的鏈表

1.題目

輸入兩個單調遞增的鏈表,輸出兩個鏈表合成後的鏈表,當然我們需要合成後的鏈表滿足單調不減規則。

2.思路

要說明的是,ListNode類是指鏈表的節點,每個節點都存放着本節點的值和下一節點的地址(對象)
先考慮特殊的情況,如果 l1 或者 l2 一開始就是 null ,那麼沒有任何操作需要合併,所以我們只需要返回非空鏈表。否則,我們要判斷 l1 和 l2 哪一個的頭元素更小,然後遞歸地決定下一個添加到結果裏的值(即相當於從同時從兩個鏈表中找出下一個最小值放在下一節點)。如果兩個鏈表都是空的,那麼過程終止。
部分步驟如下,也就是說merge(L1,L2)這個函數相當於找出L1與L2中的最小值
在這裏插入圖片描述

3.代碼


public class Solution {
    public ListNode Merge(ListNode l1,ListNode l2){
        if(l1 == null){
            return l2;
        }
        if(l2 == null){
            return l1;
        }
        if(l1.val < l2.val){										//若L1的頭節點值 < L2的頭節點值
            l1.next = Merge(l1.next,l2);		//相當於把L2插入到L1中
            return l1;
        } else{													//若L1的頭節點值 >= L2的頭節點值
            l2.next = Merge(l1,l2.next);			//相當於把L1插入到L2中
            return l2;
        }
    }
    }

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