劍指offer(10) 有環鏈表 反轉鏈表
題目:
給一個鏈表,若其中包含環,請找出該鏈表的環的入口結點,否則,輸出null。
思路:
快慢指針,當前一個指針和後一個指針相遇時,有環,提前結束則無.
然後將慢指針指向頭節點,兩個指針一起走,每次都走一步,相遇時即爲入口節點。
(以下證明學習自牛客卻顧所來徑的回答)
1、設置快慢指針,假如有環,他們最後一定相遇。
2、兩個指針分別從鏈表頭和相遇點繼續出發,每次走一步,最後一定相遇與環入口。
證明結論1:設置快慢指針fast和low,fast每次走兩步,low每次走一步。假如有環,兩者一定會相遇(因爲low一旦進環,可看作fast在後面追趕low的過程,每次兩者都接近一步,最後一定能追上)。
證明結論2:
設:
鏈表頭到環入口長度爲–a
環入口到相遇點長度爲–b
相遇點到環入口長度爲–c
則:相遇時
快指針路程=a+(b+c)k+b ,k>=1 其中b+c爲環的長度,k爲繞環的圈數(k>=1,即最少一圈,不能是0圈,不然和慢指針走的一樣長,矛盾)。
慢指針路程=a+b
快指針走的路程是慢指針的兩倍,所以:
(a+b)*2=a+(b+c)k+b
化簡可得:
a=(k-1)(b+c)+c 這個式子的意思是: 鏈表頭到環入口的距離=相遇點到環入口的距離+(k-1)圈環長度。其中k>=1,所以k-1>=0圈。所以兩個指針分別從鏈表頭和相遇點出發,最後一定相遇於環入口。
鏈接:https://www.nowcoder.com/questionTerminal/253d2c59ec3e4bc68da16833f79a38e4?f=discussion來源:牛客網
代碼:
public ListNode EntryNodeOfLoop(ListNode pHead)
{
ListNode fast=pHead;
ListNode low=pHead;
while(fast!=null&&fast.next!=null){
fast=fast.next.next;
low=low.next;
if(fast==low)
break;
//找到相遇點
}
if(fast==null||fast.next==null)
return null;
low=pHead;
//慢指針從頭開始
while(fast!=low){
fast=fast.next;
low=low.next;
}
return low;
//如果相遇返回環入口
}
題目:
輸入一個鏈表,反轉鏈表後,輸出新鏈表的表頭。
思路:
方法1
建立一個棧,每次push進去節點,知道爲空,然後每次pop出來的節點的next指針指向下一個pop出來的節點,直到棧爲空。時間空間均爲O(n)
方法2
維護2個指針,遍歷的時候,直接將指針的指向逆序。時間O(n),空間O(1)。
方法3
遞歸,先遞歸到最後一個節點,然後逆向處理每個節點,直到遞歸至反轉前的第一個節點。
假如1-2-3-4
反轉後的是 4-3-2-1
修改了指向後,原鏈表指向還存在,需要刪除,所以有head->next = NULL; 依照這樣的思路一直遞歸到1,返回之前記錄的節點
代碼:
/**
* 建立一個棧,每次push進去節點,知道爲空,
* 然後每次pop出來的節點的next指針指向下一個pop出來的節點,直到棧爲空。時間空間均爲O(n)
*/
public ListNode ReverseList(ListNode head)
{
if (head == null)
{
return null;
}
if (head.next == null)
{
return head;
}
ListNode p = head;
Stack<ListNode> stack = new Stack();
while (p.next != null)
{
stack.push(p);
p = p.next;
}
ListNode newHead = p;
while (!stack.empty())
{
p.next = stack.pop();
p = p.next;
}
p.next = null;
//最後一個指向null
return newHead;
}
/**
* 維護2個指針,遍歷的時候,
* 直接將指針的指向逆序。時間O(n),空間O(1)。
*/
public ListNode ReverseList1(ListNode head)
{
if (head == null)
{
return null;
}
if (head.next == null)
{
return head;
}
ListNode pre = null;
ListNode next = null;
while (head != null)
{
next = head.next;
head.next = pre;
pre = head;
head = next;
}
return pre;
}
/**
* 遞歸方法
* 處理當前節點前,處理當前節點的下一個節點
* 遞歸到最後一個節點開始處理
*/
public static ListNode ReverseList2(ListNode pHead)
{
//如果鏈表爲空或者鏈表中只有一個元素
if(pHead==null||pHead.next==null) return pHead;
//先反轉後面的鏈表,走到鏈表的末端結點
ListNode pReverseNode=ReverseList2(pHead.next);
//再將當前節點設置爲後面節點的後續節點
pHead.next.next=pHead;
pHead.next=null;
return pReverseNode;
}
public static void main(String[] args)
{
ListNode node1 = new ListNode(1);
ListNode node2 = new ListNode(2);
ListNode node3 = new ListNode(3);
ListNode node4 = new ListNode(4);
ListNode node5 = new ListNode(5);
ListNode node6 = new ListNode(6);
ListNode node7 = new ListNode(7);
node1.next = node2;
node2.next = node3;
node3.next = node4;
node4.next = node5;
node5.next = node6;
node6.next = node7;
ListNode old = node1;
while (old != null)
{
System.out.print(" " + old.val);
old = old.next;
}
System.out.println();
// deleteDuplication(node1);
ListNode newNode = ReverseList2(node1);
while (newNode != null)
{
System.out.print(" " + newNode.val);
newNode = newNode.next;
}
}
ion(node1);
ListNode newNode = ReverseList2(node1);
while (newNode != null)
{
System.out.print(" " + newNode.val);
newNode = newNode.next;
}
}