文章目錄
鏈表題
- 設置兩個指針。
- 用棧或隊列或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;
}
}
}