转载注明!!https://blog.csdn.net/qq_31842777/article/details/90632307
涉及以下问题:
- 查找倒数第k个结点;
- 查找中间结点;
- 判断链表是否有环;
- 计算环的长度;
- 计算环的入口结点,原理参见(s含快慢指针推导):https://blog.csdn.net/puss0/article/details/78462375;
- 计算两个链表的第一个公共结点(Y型),原理参见:https://blog.csdn.net/yanxiaolx/article/details/52132435
直接上代码:
import java.util.Stack;
public class MyLinkedList {
//定义头结点
public Node head;
//定义结点类
public static class Node{//类必须是static,后面测试会用到此类创建实例
int data;
Node next;
Node(){}
Node(int data){
this.data=data;
}
}
/*
*以下是表的基本操作
*/
//添加结点,以创建链表 遍历链表到尾结点,然后添加结点
public void addNode(Node node) {
Node temp=head;
//以下操作找到链表的尾结点,然后插入
while(temp.next!=null) {
temp=temp.next;
}
//如果temp.next为空,即到尾结点,加入新结点
temp.next=node;
}
//获取链表长度 从头遍历链表
public static int getLength(Node head) {
if(head==null) {
return 0;
}
Node temp=head;
int length=1;
while(temp.next!=null) {
length++;
temp=temp.next;
}
return length;
}
/**
* 快慢指针的使用及相关算法题
*/
//1.查找倒数第k个结点
/*
* 思路:第一种,直接查找第length-k个结点;
* 第二种,快慢指针,初始都指在同一个结点,然后快指针走k-1步,然后快慢指针同时走,直到快指针到达尾结点,返回慢指针
* 如:链表1,2,3,4, 查找倒数第3个,快慢指针指在1,快指针走k-1=2步,到3,快慢指针同时走,一步后快指针到尾结点,慢指针找到倒数第三个,即2
*/
public Node queryByIndex(int index) {
if(head==null||index<1||index>getLength(head)) {//注意这里的temp!=null条件,非常重要,因为temp.next!=null会忽略最后一个结点
return null;
}
Node slow=head;
Node quick=head;
Node temp=head;
int step=0;
while(temp.next!=null||temp!=null) {
//快指针走k-1步
if((index-1)==step++) {
quick=temp;
}
temp=temp.next;
}
//快慢指针同时走
while(quick.next!=null) {
slow=slow.next;
quick=quick.next;
}
return slow;
}
//2.查找中间结点
/*
* 快慢指针,初始指在同一个结点,快指针每走两步,慢指针走一步,快指针到尾结点,返回慢指针
*/
public static Node getMidNode(Node head) {
if(head==null) {
return null;
}
if(head.next==null) {
return head;
}
Node quick=head;
Node slow=head;
while(slow.next!=null&&quick.next!=null&&quick.next.next!=null) {
quick=quick.next.next;
slow=slow.next;
}
return slow;
}
//3.判断是否有环
/*
* 快慢指针,初始指在同一节点,快指针走两步,慢指针走一步,直到快慢指针的data相等,返回true,证明有环
*/
public boolean hasLoop(Node head) {
if(head==null||head.next==null) {
return false;
}
Node quick=head;
Node slow=head;
while(slow.next!=null&&quick.next!=null&&quick.next.next!=null) {
if(slow.data==quick.data) {
return true;
}
quick=quick.next.next;
slow=slow.next;
}
return false;
}
//4.确定环的长度
/*
* 快慢指针,初始指在同一节点,快指针走两步,慢指针走一步,直到快慢指针的data相等
* 返回快指针走的长度-慢指针走的长度,就是环的长度
*/
public static int getLoopLength(Node head) {
Node quick=head;
Node slow=head;
int quickSteps=0;int slowSteps=0;
while(quick.next!=null&&quick.next.next!=null&&slow.next!=null) {
if(slow.data==quick.data) {
return quickSteps-slowSteps;
}
quickSteps++;
slowSteps++;
slow=slow.next;
quick=quick.next.next;
}
return 0;
}
//5.确定环的入口节点
/*
* 结论:从头结点到环入口的距离=快慢指针相遇处继续走到环入口的距离+环长度的整数倍
* 思路:first、second结点,先让second结点走环的长度(整数倍)
* 然后first、second同时走,相遇处即为环入口处
*/
public static Node getLoopNode(Node head) {
if(head==null && head.next==null) {
return null;
}
int length=MyLinkedList.getLoopLength(head);
Node first=head;
Node second=head;
while(length-->0&&second.next!=null) {
second=second.next;
}
while(first!=null&&second!=null) {
if(first.data==second.data) {
return second;
}
first=first.next;
second=second.next;
}
return null;
}
public static void printFromLastRec(Node head) {
if(head==null) {
return;
}
printFromLastRec(head.next);
System.out.println(head.data);
}
//6.找到两个链的第一个公共节点
/*
* Y型拓扑结构
* 思路:计算两个链表的长度;计算长度差;长的链表走长度差步;然后同时走,直到找到公共结点
*/
public static Node findFirstCommonNode(Node head1,Node head2) {
if(head1==null&&head2==null) {
return null;
}
int x=Math.abs(getLength(head1)-getLength(head2));
if(getLength(head1)<getLength(head2)) {
while(x-->0) {
head2=head2.next;
}
}else {
head1=head1.next;
}
Node temp1=head1;
Node temp2=head2;
while(head1!=null) {
if(temp1==temp2) {
return temp2;
}
temp1=temp1.next;
temp2=temp2.next;
}
return null;
}
}