Java实现链表

首先来了解一下什么是链表?

链表是一种线性表,是物理存储上非连续非顺序的存储结构。(可以理解为一串链子上有许多节点,每个节点有属于自己的成员和指向下一节点的指针)

画图简单理解一下

在链表的点里面存放了数据域和地址域,那么如何用Java来写一个链表的类呢?

整个单链表是一个类,链表里的每一个节点也是一个类,这就要用到Java的内部类来写了。

class TestLink{
	
	public Entry head;//这个是整个链表的头结点
	public TestLink(){//构造函数
		head = new Entry();//初始化头结点,让其data为-1,并指向空
	}
	
	class Entry{//链表中的节点
		int data;//节点的数据域
		Entry next;//地址
		
		public Entry(){//无参的构造函数,用于头节点的初始化
			data = -1;
			next = null;
		}
		public Entry(int val){//存放值的节点,传进了一个val参数,赋值给data域
			data = val;
			next = null;
		}
	}

这样就写好了一个链表了。

一. 链表的插入

1.头插法

就是在第一个存放值的节点之前插入新的节点(插在头结点之后)

public void insertHead(int val){
		Entry cur = new Entry(val);//new了一个新的节点
		cur.next = head.next;//注意先保后面的节点~
		head.next = cur;
	}

2.尾插法

插在了链表的尾端,需要对单链表进行遍历,找到最后一个节点,然后插入。

 

 

public void insertTail(int val){
		Entry cur = head;//遍历指针
		Entry t = new Entry(val);//新的节点
		while(cur.next != null){//对单链表进行遍历的循环
			cur = cur.next;
		}
		cur.next = t;//插入操作
	}

3.任意位置插入

public boolean insertPos(int val,int pos){
		Entry cur = new Entry(val);
		Entry t1 = head;//前驱
		Entry t2 = head.next;
		int len = 0;
		if(pos < 0||pos > getLength()){//没有找到指定位置
			return false;
		}
		else{
			while(t2!=null){//找pos位置
				len++;//计算当前节点的位置
				if(len == pos){//相等则插入
					cur.next = t2;
					t1.next = cur;
					break;
				}
				//否则一直向后遍历找pos位置
				t1 = t1.next;
				t2 = t2.next;	
			}
			if(t1.next == null){//在最后一个位置插入
				t1.next = cur;
			}
			return true;
		}
	}

二. 单链表的逆置

public void reverse2(){
		Entry newhead = head;
		Entry pre = null;
		Entry cur = head;
		while(cur != null){//循环情况见上图
			Entry curNext = cur.next;
			if(curNext == null){
				newhead = cur;
			}
			cur.next = pre;
			pre = cur;
			cur =curNext;
		}
		cur = newhead;
		while(cur.next!=null){//输出逆置后的单链表
			System.out.println(cur.data);
			cur = cur.next;
		}
	}

利用尾插法插入四个元素

逆置后的运行结果如图

三.求倒数第k个数据元素

有两种方法

第一种:

倒数第k即正数第n-k+1个元素,遍历单链表找到这个数就好了

public void kBackwards(int k){
		int n = getLength();//链表的长度
		int t = 1;//t表示第一个节点
		Entry cur = head.next;
		if(k < 0||k > n){//如果没有倒数第k元素
			System.out.println("无此元素");
		}
		while(cur != null){
			if(t == n-k+1){//判断当前节点是否为倒数第k个元素
				System.out.println("倒数第"+k+": "+cur.data);//如果是,打印当前节点的值
			}
			cur = cur.next;
			t++;
		}
	}

第二种:

public int kBackwards2(int k){
		if(k < 0||k >getLength()){
			return -1;
		}
		Entry cur1 = head;
		Entry cur2 = head;//快的
		while(k-1 > 0){//先让cur2走k-1步
				cur2  = cur2.next;
				--k;
			
		}
		while(cur2.next != null){//然后让cur2走完链表
			cur1 = cur1.next;
			cur2 = cur2.next;		
		}
		return cur1.data;
		
	}

 

两种方法各有其优点,第一种更容易想到,第二种更能脱颖而出。

 

四. 判断两个链表是否相交

//创建两个相交的链表
	public static void creatCut(TestLink t1,TestLink t2){
		TestLink.Entry head1 = t1.getHead();
		TestLink.Entry head2 = t2.getHead();
		head1.next.next = head2.next.next;//在第二个节点处相交
	}
	//判断相交
	public static boolean isCut(TestLink t1,TestLink t2){
		TestLink.Entry head1 = t1.getHead();//得t1链表的头结点
		TestLink.Entry head2 = t2.getHead();//得t2链表的头结点
		
		int len1 = t1.getLength();
		int len2 = t2.getLength();
		int my_len = len1 - len2;
		
		if(my_len < 0){
			head1 = t2.getHead();
			head2 = t1.getHead();
		}
		
		//肯定确定head1指向的单链表较长
		for(int i = 0;i < my_len;i++){
			head1 = head1.next;
		}
		//一人一步走
		while(head1 !=null && head2 != null && head1 != head2){
			head1 = head1.next;
			head2 = head2.next;
		}
		if(head1 == head2 && head1 !=null && head2 != null){
			System.out.println("交点:"+head1.data+" "+head2.data);
			return true;	
		}else{
			return false;
		}
	}

这里应该注意两个链表相交应该是地址相交,不是值相等

五. 判断一个链表是否有环

//创建一个环
public void  Loop(){
		Entry cur = head;
		while(cur.next!=null){
			cur = cur.next;
		}
		cur.next=head.next;
	}
//4.判断单链表是否有环?
	 public boolean hasLoop(){
		 
		 Entry fast = head;//快指针
		 Entry slow = head;//慢指针
		 if(head.next ==null){//只有头结点
			 return false;
		 }
		 while(fast != null && fast.next != null){
			 fast = fast.next.next;//快的走两步
			 slow = slow.next;//慢的走一步
			 if(fast == slow)//如果相等,说明快的追上了慢的,二者相遇,必定有环
				 return true;
		 }
		return false;
		 
	 }
//环的入口点
	 public int intoLoop(){
		 Entry fast = head;
		 Entry slow = head;
		 while(fast.next != null && fast != null){
			 fast = fast.next.next;
			 slow = slow.next;
			 if(fast == slow)//相遇就退出
				 break;
		 }
		 slow = head;
		 while(fast != slow){//以相同的速度开始走
			 fast = fast.next;
			 slow = slow.next;
		 }
		 return slow.data;
	 }

 

 //求环的长度
	 public int getLooplen(){
		 Entry fast = head;
		 Entry slow = head;
		 int len = 0;
		 boolean tag = false;//标志位
		 //判断是否有环
		 if(!hasLoop()){
				return -1;
			}
		 //第一二次相遇之间的长度就是环的长度
		 while(fast != null&&fast.next != null){
			 fast =fast.next.next;
			 slow = slow.next;
			 if(fast == slow && tag == true){//第二次相遇,跳出
				 break;
			 }
			 if(fast == slow && tag == false){//第一次相遇,将标志位赋值为true
				 tag = true;
			 }
			 if(tag == true){//如果是true,就给长度加一,直到二者再次相遇
					len++;
				}
		 }
		 return len;
	 }

运行结果:

 


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值