day07 Java链表(环、快慢指针)

转载注明!!https://blog.csdn.net/qq_31842777/article/details/90632307
涉及以下问题:

  1. 查找倒数第k个结点;
  2. 查找中间结点;
  3. 判断链表是否有环;
  4. 计算环的长度;
  5. 计算环的入口结点,原理参见(s含快慢指针推导):https://blog.csdn.net/puss0/article/details/78462375;
  6. 计算两个链表的第一个公共结点(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;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值