剑指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;
}
}