1.如何在一次递归后找到单链表的中间元素
思路
使用两个指针,一个一次走一步,一个一次走两步,一次走两步的走完时,另一个刚好走到中间.
public ListNode findMid(ListNode head){
int i = 0;
ListNode quick = head;
ListNode slow = head;
while(true){
if(null == quick || null == quick.next){
System.out.print("tmp =" +slow);
return;
}
slow = slow.next;
quick = quick.next.next;
}
return slow;
}
2.检查给定的链表中是否包含循环链表,并找出循环链表的起始节点
思路
1.使用两个在链表中具有不同移动速度的指针(如:fastNode每次移动两个节点,slowNode每次移动一个节点),两个指针同时从表头开始移动,如果在某一时刻它们相遇了,则表明该链表存在环.
2.在找到环之后,将slowNode重新设置为表头节点,接下来slowNode和fastNode每次分别移动一个节点,当它们再次相遇时即为环的起始节点
证明:
设飞环长度为:C1,整个环的长度为:C2,两个指针相遇时走过的环中的弧长为:C3
第一次相遇时:
Sslow = C1 + C3
Sfast = C1 + C2 + C3
且:Sfast = 2Sslow
则:C1 = C2 – C3
当slowNode重置为表头节点,两个指针只需要分别移动C1即可第二次相遇:
slowNode移动长度:C1,此时slowNode的位置是环的开始节点
fastNode移动长度:C1 = C2 – C3,也就是说fastNode此时的位置是:初始位置C3 + C2 – C3 = C2,也就是说fastNode此时刚好移动到环的开始节点,二者相遇
public void findRepeat(ListNode listNode){
ListNode fastNode = listNode;
ListNode slowNode = listNode;
boolean hasLoop = false;
while(null != slow.next && null != fast.next && null != fast.next.next){
fastNode = fastNode.next.next;
slowNode = slowNode.next;
if(fastNode == slowNode){
hasLoop = true;
break;
}
}
if(hasLoop){
slowNode = listNode;
while(slowNode != fastNode){
slowNode.next;
fastNode.next;
}
retrue fastNode;
}
retrue null;
}
3.如果链表中存在环,则返回环的长度
思路
1.判定算法判断一个链表是否存在环。
2.在找到环之后,保持fastNode不动,接下来slowNode每次移动一个节点,同时计数器加一,当它们再次相遇时即可求出环的长度
public int findLoop(ListNode listNode){
ListNode slowNode = listNode;
ListNode fastNode = listNode;
boolean hasLoop = false;
while(null != slowNode.next && null != fastNode.next && null != fastNode.next.next){
fastNode = fastNode.next.next;
slowNode = slowNode.next;
if(slowNode = fastNode){
hasLoop = ture;
break;
}
}
if(hasLoop){
slowNode = listNode;
int lenght = 0;
while(slowNode != fastNode){
slowNode.next;
lenght++;
}
}
}
4.旋转链表*
思路
例:链表为: 1->2->3->4
k=1, 4->1->2->3 , 1->2->3 4
k=2, 3->4->1->2 , 1->2 3->4
k=3, 2->3->4->1 , 1 2->3->4
1.得到链表长度len,并拿到链表最后一个node,并把其next从null变成head.
2.k对len求余,找到切断点,然后重新修改头尾指向.
public ListNode rotate(ListNode head,int k){
if(null == head || null == head.next || k==0){
return head;
}
int len = 1;
ListNode cur = head;
while(null != cur.next){ //计算链表长度
len++;
cur = cur.next;
}
k = k % len;
if(k == 0){
return head;
}
for(int i=0;i<(len - k);i++){
cur = cur.next;
}
ListNode newNode = cur.next;
cur.next=null;
return cur;
}
5.反转链表*
思路
修改ListNode.next的指针指向,指向前一个node
1->2->3->4 变成 4->3->2->1
public ListNode reserver(ListNode headNode){
if(null == headNode || headNode.next == null){
return headNode;
}
ListNode nextNode = null;
ListNode tmpNode = null;
while(headNode != null){
nextNode = headNode.next;
headNode.next = tmpNode;
tmpNode = headNode;
headNode = nextNode;
}
return headNode;
}
6.从表尾一次输出
思路
递归
public ListNode printFromEnd(ListNode head){
if(null != head && null != head.next){
lastOutput(head.next);
}
System.out.print(head.data+" ,");
}
7.环形单链表约瑟夫问题
思路
1.先把链表变成环
2.开始循环,满足条件删除
public ListNode josephusKill(ListNode head,int k){
if(null == head || null== head.next || k <= 1){
return head;
}
ListNode last = head;
while(null != last.next){
last = last.next;
}
last.next = head;//成环
int count = 1;
while(head.next != head){
if(++count == k){
head.next=head.next.next;
count = 0;
}else{
head = head.next;
}
}
return head;
}