《链表》——单向链表和双向链表(Java)

简介: 完整的代码地址为:github  点击查看 单链表 单链表包括数据域和指向下一个节点的指针域,其结构如上图所示 首先定义一个数据类: class DATA{ //定义链表的一个节点 String key; //节点的关键字 Stri...

完整的代码地址为:github  点击查看

单链表

单链表包括数据域和指向下一个节点的指针域,其结构如上图所示

首先定义一个数据类:

class DATA{           //定义链表的一个节点
	String key;           //节点的关键字
	String name;
	int age;
}

定义一个单向链表类(包括以下几种方法):

1:在尾部添加节点

2:在头部添加节点

3:查找节点

4:插入节点

5:删除节点

6:计算链表长度

7:显示所有节点对应的代码如下

public class Link {     //定义链表结构
	
	DATA nodeData = new DATA();     //声明一个节点
	Link nextNode;                                //指向下一个节点的指针
	
	
	//添加节点
	Link linkAddEnd(Link head, DATA nodeData)
	{
		Link node, hTemp;
		if( (node = new Link()) ==null)        //如果内存空间分配失败,则返回为空
		{
			System.out.println("内存空间分配失败!");
			return null;
		}
		else
		{
			node.nodeData = nodeData;
			node.nextNode = null;
			if(head == null)      //如果头节点为空,则把当前节点赋给head,并返回
			{
				head = node;
				return head;
			}      
			hTemp = head;       //如果头节点不为空
			while(hTemp.nextNode!=null)        //查找链表的末尾
			{
				hTemp = hTemp.nextNode;
			}
			hTemp.nextNode = node;
			return head;
		}
	}
	
	//插入头节点
	Link linkAddFirst(Link head, DATA nodeData)
	{
		Link node;
		if((node=new Link()) == null ) //如果内存空间分配失败,则返回为空
		{
			System.out.println("内存分配失败");
			return null;
		}
		else
		{
			node.nodeData = nodeData;
			node.nextNode = head;
			head = node;
			return head;
		}
	}
	
	//查找节点
	Link linkFindNode(Link head, String key)
	{
		Link hTemp;
		hTemp = head;
		while(hTemp!=null)       //若节点有效,则进行查找
		{
			if(hTemp.nodeData.key.compareTo(key) == 0) //若节点的关键字与传入的关键字相同
			{
				return hTemp;
			}
			hTemp = hTemp.nextNode;     //处理下一个节点
		}
		return null;	
	}
	
	//插入节点
	Link linkInsertNode(Link head, String findKey,DATA nodeData)
	{
		Link node,hTemp;
		if((node = new Link() ) == null ) //分配内存失败,则返回
		{
			System.out.println("分配内存失败...");
			return null;
		}
		node.nodeData = nodeData;      //保存当前集节点信息
		hTemp = linkFindNode(head, findKey);      //查找要插入的节点
		if(hTemp != null)
		{
			node.nextNode = hTemp.nextNode;
			hTemp.nextNode = node;
		}
		else
		{
			System.out.println("未找到正确的插入位置.........");
		}
		return head;          //返回头引用
	}
	
	//删除节点
	int linkDeleteNode(Link head, String key)
	{
		Link node,hTemp;
		hTemp = head;
		node = head;
		while(hTemp != null )
		{
			if(hTemp.nodeData.key.compareTo(key) == 0)   //若找到关键字,则删除
			{
				node.nextNode = hTemp.nextNode;
				hTemp = null;
				return 1;
			}
			else               //跳到下一个节点
			{
				node = hTemp;
				hTemp = hTemp.nextNode;
			}
		}
		return 0;
	}
	
	//计算链表长度
	int linkLength(Link head)
	{
		Link hTemp;
		hTemp = head;
		int num = 0;
		while(hTemp!=null)
		{
			num ++ ;
			hTemp = hTemp.nextNode;
		}
		return num;	
	}
	
	//显示所有节点
	void linkShow(Link head)
	{
		Link hTemp;
		DATA nodeData;
		hTemp = head;
		System.out.printf("当前链表共有 %d 个节点,链表所有的数据如下:\n" , linkLength(head));
		while(hTemp!=null)
		{
			nodeData = hTemp.nodeData;     //获取当前的节点数据
			System.out.printf("节点(%s %s  %d)\n",nodeData.key,nodeData.name,nodeData.age);
			hTemp = hTemp.nextNode;
		}
	}
	
}


编写测试类:

public class linkTest {

	public static void main(String[] args) {
		Link node = null , head=null;
		Link link = new Link();
		String key, findKey;
		Scanner input = new Scanner(System.in);
		
		System.out.printf("链表测试开始,先输出链表中的数据,格式为:关键字	姓名	年龄\n");
		do
		{                        //循环插入节点,知道输入的key 为0 结束
			DATA nodeData = new DATA();
			nodeData.key = input.next();
			if(nodeData.key.equals("0"))
			{
				break;
			}
			else
			{
				nodeData.name = input.next();
				nodeData.age = input.nextInt();
				head = link.linkAddEnd(head, nodeData);  //在链表尾部添加节点
			}
		}while(true);
		link.linkShow(head);     //显示所有节点
		
		System.out.printf("\n演示插入节点,输入插入位置的关键字:");
		findKey = input.next();                //输入插入的关键字
		System.out.println("输入插入节点的数据(关键字 姓名 年龄)");
		DATA nodeData = new DATA();              //输入节点的元素值
		nodeData.key = input.next();
		nodeData.name = input.next();
		nodeData.age = input.nextInt();
		head = link.linkInsertNode(head, findKey, nodeData);           //调用插入函数
		link.linkShow(head);    //显示所有节点
		
		System.out.println("演示删除节点,输入要删除的关键字:");
		key = input.next();
		link.linkDeleteNode(head, key);         //调用删除节点的函数
		link.linkShow(head);                     //显示所有节点
		
		System.out.println("演示在链表中差找,输入要查找的关键字:");
		key = input.next();
		node = link.linkFindNode(head, key);  //调用查找函数,返回节点引用
		if(node!=null)
		{
			nodeData = node.nodeData;         //获取节点的数据
			System.out.printf("关键字 %s 对应的节点数据为 (%s %s %s)\n", key,nodeData.key,nodeData.name,nodeData.age);
		}
		else
		{
			System.out.printf("在链表中为查找的为%s 的关键字 \n" , key);
		}
		
		
	}
	
}


双向链表

链结点的结构:

┌────┬────┬────────┐

│data│next│previous│

└────┴────┴────────┘

结构图如下:



package Link;

import java.util.Scanner;

class Data{           //定义链表的一个节点
	String key;           //节点的关键字,唯一
	String name;
	int age;
}

public class DoubleLink {

	
	int flag; //输入选择值
	Scanner scan = new Scanner(System.in);
	Data data = new Data();
	DoubleLink nextNode;  //后继节点
	DoubleLink priorNode;    //前驱节点
	
	//链表添加节点
	DoubleLink addNode(DoubleLink head, String priorKey, String nextKey, Data nodeData){
		
		DoubleLink node=null, htemp=null;
		if((node = new DoubleLink()) == null)
			System.out.println("内存空间分配失败");
		if(head== null)        //如果head为空
		{
			System.out.println("当前链表为空,是否将当前节点当作头节点?\n0:否\t1:是");
			
			node.data=nodeData;
			node.nextNode=null;
			node.priorNode=null;
			flag = scan.nextInt();
			switch(flag)
			{
			case 0:
				break;
			case 1:
				head=node;
				break;
			default:
					System.out.println("你输入的数据不合法");;
			}
		}       //如果head不为空
		else{
			if(linkFindNode(head, priorKey,nextKey,nodeData))
				System.out.println("插入成功");
			else
				System.out.println("插入失败(原因可能是你输入的前驱和后继即诶但均不存在)");
		}
					
		return head;
	}

	//查找并插入节点
	boolean linkFindNode(DoubleLink head, String priorKey, String nextKey,Data nodeData) {
		// TODO Auto-generated method stub
		DoubleLink htemp=null,node=null;
		
		if( (node = new DoubleLink()) == null )
		{
			System.out.println("内存分配失败");
			return false;
		}
		//将传进来的值赋值给node
		node.data = nodeData;
		node.nextNode = null;
		node.priorNode=null;
		//两大类情况
		htemp = head;
		while(htemp != null)
		{
			if(htemp.data.key.equals(priorKey)) //前驱节点存在
			{
				if(htemp.nextNode == null)     //该节点的后继节点为空,说明该节点为头节点
				{
					System.out.println("你输入的后继节点不存在,前驱节点为头节点,是否插入在其后面?\n 1:是 \t 0 :否 ");
					flag = scan.nextInt();
					if(flag == 0)
						break;
					else if(flag==1)
					{
						htemp.nextNode = node;       //将查找到的节点的后继节点指向node
						node.nextNode = null;
						node.priorNode = htemp;
						
						return true;
					}
					else
						System.out.println("你输入的数字不合法!!!");
				}
				else             //后继节点不为空
				{
					if(htemp.nextNode.data.key.equals(nextKey))            //存在的后继节点与nextKey相同。相同执行if
					{
						node.nextNode = htemp.nextNode;
						htemp.nextNode.priorNode = node;
						
						htemp.nextNode = node;
						node.priorNode = htemp;
						return true;
					
					}
					else         //不同执行else
					{
						htemp = htemp.nextNode; //若当前节点没找到,遍历下一个节点
					}
				}
			}
			else //前驱节点不存在,后驱节点存在
			{
				if(htemp.data.key.equals(nextKey))      //如果当前节点与nextKey相同
				{
					if(htemp.nextNode==null)  //如果后继节点为空,即当前节点为尾节点
					{
						System.out.println("你输入的前驱节点不存在,后继节点为头节点,是否插入在其前面?\n 1:是 \t 0 :否 ");
						flag = scan.nextInt();
						if(flag == 0)
							break;
						else if(flag==1)
						{
							htemp.priorNode = node;
							node.nextNode = htemp;
							
							node.priorNode=null;
							return true;
						}
						else
							System.out.println("你输入的数字不合法!!!");
					}
					else //如果当前节点的后继节点不为空,则执行下一个节点
					{
						htemp = htemp.nextNode; //若当前节点没找到,遍历下一个节点
					}
				}
				else
					htemp = htemp.nextNode; //若当前节点没找到,遍历下一个节点
			}
		}
		return false;
	}
	
	//输出节点
	public void OutputLinkNode(DoubleLink head)
	{
		if(head == null)
			System.out.println("当前链表为空");
		else{
			System.out.println("输入的链表数据如下:");
			DoubleLink htemp;
			htemp = head;
			while(htemp!=null)
			{
				System.out.println(htemp.data.key + "\t" + htemp.data.name + "\t" + htemp.data.age);
				htemp= htemp.nextNode;
			}
		}
		System.out.println();
	}
	
	//输出链表的深度
	int LinkDepth(DoubleLink head)
	{
		int sum = 0;
		DoubleLink htemp = head;
		while(htemp!=null)
		{
			sum ++;
			htemp = htemp.nextNode;
		}
		return sum;
	}
	
	//查找节点
	DoubleLink FindLink(DoubleLink head, String findKey)
	{
		DoubleLink htemp=head;
		while(htemp!=null)
		{
			if(htemp.data.key.equals(findKey))
				return htemp;
			htemp = htemp.nextNode;
		}
		return null;
	}
	
	//删除节点
	DoubleLink DeleteNode(DoubleLink head, String deleteKey)
	{
		DoubleLink htemp = head;
		while(htemp!=null)
		{
			if(htemp.data.key.equals(deleteKey))
			{
				if(htemp.priorNode==null)  //如果是头节点
				{
					return htemp.nextNode;
				}
				else if (htemp.nextNode==null)     //如果是尾节点
				{
					htemp.priorNode.nextNode=null;
					htemp.priorNode=null;
					return head;
				}
				else //如果是中间
				{
					htemp.priorNode.nextNode=htemp.nextNode;
					htemp.nextNode.priorNode = htemp.priorNode;
					return head;
				}
			}
			else
				htemp = htemp.nextNode;
		}	
		System.out.println("你要删除的节点不存在!");
		return head;
	}
	
}

测试实现:

package Link;

import java.util.Scanner;

public class DoubleLinkTest {

	public static void main(String[] args) {
		
		DoubleLink node=null, head=null;
		DoubleLink dlink = new DoubleLink(); //声明一个双向链表对象
		Scanner scan  = new Scanner(System.in);
		
		System.out.println("双向链表测试开始....");
		do{
			System.out.println("请输入插入节点的关键字,姓名和年龄,格式为:关键字	姓名	年龄");
			Data data = new Data();
			data.key = scan.next();
			data.name = scan.next();
			data.age = scan.nextInt();

			if(data.key.contains("0"))  //循环插入节点,直到插入的为0时结束
				break;
			else
			{
				System.out.println("请输入插入节点的前驱节点和后继节点,格式为 前驱节点  后继节点");
				String priorKey = scan.next();
				String nextKey = scan.next();
				
				head = dlink.addNode(head, priorKey, nextKey, data);   //添加节点 
				dlink.OutputLinkNode(head);   //输出链表
			}
		}while(true);
		
		//输出链表的深度
		System.out.println("该链表的深度为:" + dlink.LinkDepth(head));
		
		//查找链表中的某个节点
		System.out.println("请输入要查找的节点的关键字...");
		String findKey = scan.next();
		node = dlink.FindLink(head, findKey);
		 if(node==null)
			 System.out.println("你所查找的节点不存在!");
		 else
			 System.out.println("该节点的值为:" + node.data.key + "\t" + node.data.name + "\t" + node.data.age);
		
		 //删除节点值
		 System.out.println("请输入要删除的节点的关键字...");
		 String deleteKey = scan.next();
		 node =  dlink.DeleteNode(head, deleteKey);
		 if(node == null)
			 System.out.println("删除节点后的链表为空,其深度为:" + 0);
		 else
		 	{
				 System.out.println("删除后的链表为:");
				 dlink.OutputLinkNode(head);
				 System.out.println("删除节点后链表的深度为:" + dlink.LinkDepth(head));
		 	}
	}
}

结果展示


相关文章
|
2月前
|
Java
java数据结构,双向链表的实现
文章介绍了双向链表的实现,包括数据结构定义、插入和删除操作的代码实现,以及双向链表的其他操作方法,并提供了完整的Java代码实现。
java数据结构,双向链表的实现
|
1月前
|
存储 安全 Java
【用Java学习数据结构系列】探索顺序表和链表的无尽秘密(附带练习唔)pro
【用Java学习数据结构系列】探索顺序表和链表的无尽秘密(附带练习唔)pro
23 3
|
1月前
|
存储
【初阶数据结构】深入解析单链表:探索底层逻辑(无头单向非循环链表)(一)
【初阶数据结构】深入解析单链表:探索底层逻辑(无头单向非循环链表)
|
1月前
|
算法 Java
数据结构与算法学习六:单向环形链表应用实例的约瑟夫环问题
这篇文章通过单向环形链表的应用实例,详细讲解了约瑟夫环问题的解决方案,并提供了Java代码实现。
19 0
|
1月前
|
存储 缓存
【初阶数据结构】深入解析单链表:探索底层逻辑(无头单向非循环链表)(二)
【初阶数据结构】深入解析单链表:探索底层逻辑(无头单向非循环链表)
|
3月前
|
存储 Java
|
3月前
|
存储 Java
java实现单链表的创建、增、删、改、查
这篇文章详细介绍了Java中如何实现单链表的创建以及对单链表进行增加、删除、修改、查询等操作的方法,并提供了相应的代码示例。
java实现单链表的创建、增、删、改、查
|
3月前
|
存储 Java 开发者
揭秘!HashMap底层结构大起底:从数组到链表,再到红黑树,Java性能优化的秘密武器!
【8月更文挑战第24天】HashMap是Java集合框架中的核心组件,以其高效的键值对存储和快速访问能力广受开发者欢迎。在JDK 1.8及以后版本中,HashMap采用了数组+链表+红黑树的混合结构,实现了高性能的同时解决了哈希冲突问题。数组作为基石确保了快速定位;链表则用于处理哈希冲突;而当链表长度达到一定阈值时,通过转换为红黑树进一步提升性能。此外,HashMap还具备动态扩容机制,当负载因子超过预设值时自动扩大容量并重新哈希,确保整体性能。通过对HashMap底层结构的深入了解,我们可以更好地利用其优势解决实际开发中的问题。
101 0
|
3月前
|
存储 Java
【Java集合类面试十】、HashMap中的循环链表是如何产生的?
在多线程环境下,HashMap在扩容时如果发生条件竞争,元素的插入顺序可能形成循环链表,导致死循环。
|
3月前
|
存储 Java
java实现双向链表的增删改查
这篇文章展示了如何在Java中实现双向链表的增加、删除、修改和查询操作,并通过代码示例演示了在双向链表中存储和操作学生信息的过程。