java实现几种简单的数据结构

本文介绍了Java中常见的数据结构,包括数组、链表(单链表、双向链表、循环链表)、队列、栈和HashMap。详细讲解了这些数据结构的概念和实现方式,特别提到了HashMap的哈希表原理和解决冲突的方法。此外,还简单提及了递归算法,如在斐波那契数列和汉诺塔问题中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

java实现几种简单的数据结构

备注:本文内容参考学习自阿发老师(网易云:阿发你好)的数据结构与算法课程。

何为数据结构?何为算法?

算法可以理解为我们解决、实现问题用到的数学逻辑、方法,而数据结构则是数据组织结构。数据结构是为算法服务的。

算法的应用场景:

  1. 操作系统
  2. 服务器
  3. 图片视频处理
  4. 大数据
  5. 人工智能领域

常见的数据结构

常见的数据结构有数组、队列、链表、栈、哈希表等。

数组

数组是最简单的一种数据结构。

	int[] array1 = new int[20]; //动态初始化 0
	Integer[] array2 = new Integer[20]; //动态初始化 null
	int[] array3 = {1,2,3,4}; //静态初始化--默认初始化长度为4

创建数组有多种办法,初始化的数组必须指定数组的长度。初始化的数组如果没有赋值的话注意其是有默认值的。

链表

链表(LinkedList)也是一种常见的数据结构,样式如下图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eAb0Kqkj-1592479268325)(C:\Users\xyz\AppData\Roaming\Typora\typora-user-images\image-20200616154515982.png)]

链表中的每个节点(node)都存在一个next指针指向下一个节点,呈现一个链状结构。

普通有头链表的实现 java自带(ArrayList 、 LinkedList)

public class XyzList
{
	public static class Node{  
        //静态内部类,与外部类无关
		public Object value;
		public Node next;
	}
	
	//定义一个头节点/有头链表
	public Node head = new Node(); //实例化XyZList时自动加入到实例化的对象中 ===emmm...相当于XyzList内的属性
	
	public void add(Object value)
	{
		Node tail = head;
		while(tail.next != null)
			tail = tail.next;
		//在尾部添加一个新节点
		Node node = new Node();
		node.value = value;
		tail.next = node;
	}
	
	public void remove(Object value)
	{
		Node node = head;
		while(node.next != null)
		{
			if(node.next.value.equals(value))
			{
				//value相等则移除该节点
				node.next = node.next.next;
			}
			node = node.next;
		}
	}
	
	public int size()
	{
		int count = 0;
		Node node = head;
		while(node.next != null)
		{
			count ++;
			node = node.next;
		}
		return count;
	}	
}

双向链表 java.util.LinkedList

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ncGhUl8H-1592479268327)(C:\Users\xyz\AppData\Roaming\Typora\typora-user-images\image-20200616175459180.png)]

​ 添加节点

public class XyzDoubleList
{
	public static class Node{
		public Object value;
		public Node prev;
		public Node next;
	}

	//头节点
	public Node head = new Node(); //field
	//尾节点
	public Node tail = new Node();//field
	
	public XyzDoubleList()
	{
        //初始化
		head.next = tail; 
		tail.prev = head;
	}
	
	public void add(Object value)
	{
		// 附加到末尾
		Node node = new Node ();
		node.value = value;
		
		// 插在尾节点之前
		tail.prev.next = node; 
		node.prev = tail.prev;
		node.next = tail;		
		tail.prev = node;
		
			
	}
	public void remove(Object value)
	{
		Node node = head.next;
		while(node != tail)
		{			
			if(node.value.equals(value)) // 用equals方法进行比较
			{
				node.prev.next = node.next;
				node.next.prev = node.prev;
			}
			node = node.next;
		}
		
		
	}
	public int size()
	{
		int count = 0;
		Node node = head;
		while(node.next != tail)
		{
			count++;
			node = node.next;
		}
		return count;
	}
}

循环链表

public class XyzCircularList
{
	public static class Node{
		public Object value;
		public Node prev;
		public Node next;
	}
	
	//采用无头链表
	public Node head = null;
	public Node tail = null;
	
	public void add(Object value )
	{
		Node node = new Node();
		node.value = value;
		
		if(head == null)
		{
			head = node;
			tail = node;
			node.prev = node.next = node;
		}else {
			
			//最好画图理解一下 /此时的添加节点是在末尾添加
			tail.next = node;
			node.prev = tail;
			node.next = head;
			head.prev = node;	
			
			tail = node;
			
		}
	}
	
}
//////////////////////////////////////////////////////////////////////////////
//适用的一种算法--报数,只留下最后一个没有报到指定数字的人
XyzCircularList list = new XyzCircularList();
		list.add(1);
		list.add(2);
		list.add(3);
		list.add(4);
		list.add(5);
		list.add(6);
		list.add(7);
		list.add(8);
		list.add(9);
		list.add(10);
		
		Node node = list.head;
		int count = 0;
		while(node.next != node)
		{
			count ++;
			if(count == 3)
			{
				count = 0;
				node.prev.next = node.next;
				node.next.prev = node.prev;
				if(node == list.head)
				{
					list.head = node.next;
				}
			}else{}
			node = node.next;
		}
		System.out.println(list.head.value);
	}

队列

FIFO ,先进先出,只能从尾部添加数据,头部取出

队列实现:指定一个头节点(也可以实现无头列表,即头部也存放数据),同普通链表(尾部添加数据,头部取出)。

public class XyzQueue
{
	public static class Node{
		public Object value;
		public Node next;
	}
	
	private Node head = new Node(); //head = null 无头列表,下面方法需要修改
	private int count = 0;
	
	public void add(Object value)
	{
		Node tail = head;
		while(tail.next != null)
			tail = tail.next;
		Node node = new Node();
		node.value = value;
		tail.next = node;
		count += 1 ;
	}
	
	public Object poll()
	{
		if(head.next == null) return null;
		Node node = head.next;
		head.next = node.next;
		count -= 1;
		return node.value;
	}
	
	public int size()
	{
		return count;
	}
}

FILO,先进后出

栈可以用链表和数组实现,但是,一般的栈都是指定空间的,用数组实现可以最大程度的减少

开销。

public class XyzStack
{
	private Object[] array;
	public XyzStack(int size)
	{
		array = new Object[size];
	}
	private int index = 0; //记录当前索引
	
	public void add(Object value)
	{
		array[index] = value;
		index += 1;
	}
	public Object poll()
	{
		if(index == 0) return -1;
		Object value = array[index-1];
		array[index-1] = null;
		index -= 1;
		return value;
	}
	
	public Object top()
	{
		if(index == 0) return -1;
		return array[index-1];
	}
	//查看大小
	public int size()
	{
		return index;
	}
	//查看容量
	public int capacity()
	{
		return array.length;
	}

HashMap

hashmap是一种映射关系的数据结构

HashMap的实现

1 hashmap原理

hash表是key-value形式,并且通过key值可以直接取出对应的value,我们知道数组结构与其有类似之处:我们可以通过数组的下标,直接取出对应的值。那么,我们在存储的时候,直接将key值转化为一个下标,将数据存入数组。在取出的时候,又通过key值得到下标,便可以直接将值取出。

好了,那么我们哈希表的雏形就有了如下

public class XyzHashMap
{
	Object[] array = new Object[10000];
	
	public void put(String key,Object value)
	{
		int index = key2index(key);
		array[index] = value;
	}
	public Object get(String key)
	{
		int index = key2index(key);
		Object value = array[index];
		return value;
	}
	//哈希函数
	public int key2index(String key)
	{
		int index = Integer.valueOf(key);
		return index;
	}

好了,我们现在的哈希表可以按键值对存在,但存在一个问题,就是key值仅仅只能为“0”、“1”、“2”、…“10000”; 所以接下来我们考虑如何将传入的其它类型key值传化为整形下标以产生对应关系,不难想到,只需要修改上面的哈希函数就可以达到这一目的了。

	public int key2index(Object key)
	{
		//将传入的key值首字母
		String name = (String) key;
		char first = name.charAt(0);
		int index = first;
		return index % 10000;// 取余,因为我们现在设计的数组容量只有10000
	}

好了,这样我们的key值就既可以是整形如1、2、3 ;也可以是字符串型如“wo”、“ai”、“你”、亦可以:“你好”、“我是”、“钟汉良”等等。那么在继续考虑这样的情况,如有两个不同的key值“wo”、“wow”,在我们这种方法转化后下标相同(位置冲突),这样后面传入的值就会覆盖前面的。那么,如何继续改进呢?

回到前面介绍的链表,假如,我们把数组中每一个下标索引都设置为一个链表头(node),在放入数据的时候将数据挂到相应的索引下面、在查找时,先找到对应的索引,再从该索引下,按照链表查找的方法就可以取出特定key值的数据了。(这种方法就被称为挂链法

public class XyzHashMap
{
	public Node[] array = new Node[10000];
	
	public static class Node 
	{
		public Object key;
		public Object value;
		public Node next;
	}
	
	public XyzHashMap() {
		//初始化将每个下标设置为链表头
		for(int i =0;i<10000; i++)
		{
			array[i] = new Node();
		}
	}
	public void put(Object key,Object value)
	{
		int index = key2index(key);
		
		Node node = new Node();
		node.key = key;
		node.value = value;
		
		// 将节点挂在链表上 
		// TODO: 此处最好检查一个是否重复 ,如果已经存在则覆盖之
		Node tail = array[index];
		while(tail.next != null)
		{
			if(tail.next.key.equals(key))
			{
				node.next = tail.next.next;
				tail.next = node; //进行替换
				return;
			}
			else {
				tail = tail.next;
			}
			
		}
		node.next = array[index].next;
		array[index].next = node;
		
	}
	public Object get(Object key)
	{
		// 将Key值转成数组下标
		int index = key2index(key);
		
		// 找到对应的链表, 遍历查找
		Node node = array[index].next;
		while(node != null)
		{
			if(node.key.equals(key))
			{
				return node.value;
			}
			node = node.next;
		}	
		return null;
	}
	
	// 删除一项
	public void remove(Object key)
	{	
		// 先找到对应的链表,再从链表中移除 ( 注意这里面是有头链表)
		int index = key2index(key);
		Node node =	array[index];
		while(node.next != null)
		{
			if(node.next.key.equals(key))
			{
				node.next = node.next.next;
			}else
			{
				node = node.next;
			}
			
		}
	}
	
	public int key2index(Object key)
	{
		if(key instanceof Integer)
		{
			return (Integer)key % 10000;
		}
		//将传入的key值首字母
		String name = (String) key;
		char first = name.charAt(0);
		int index = first;
		return index % 10000;
	}

这样我们的简单哈希表就实现了。

在java1.8后的版本中,HashMap是通过hashcode()的方法得到索引,这种方法在存入大量数据的时候,会将数据较为平均的分配到每一个索引下。此外,当挂链的数据超过一定数量时,链表便会转化为红黑树,加快查找速度。

算法

函数栈 ,Call Stack

堆栈 Heap,Stack

递归

递归是最为常见的算法之一,什么是递归呢?“一层一层一层的剥开我的心,你会鼻酸你会流泪”,

​ 将工具手(工具人)类比函数、方法,洋葱(那女孩的心)类比求解的问题,我们想要看看洋葱(心)的最深处是什么(她在想什么呀)? 所以呀,你开始尝试,一层一层的剥开洋葱(去慢慢探索走到她的内心最深处),终于,你剥开了洋葱,也看到了她的心思。可是,它们刺激的你心酸,你有不得不一层一层的合上它。。终于,从外界看来,什么都没有变,但你的确得到的最终的结果,无论喜不喜欢。

​ 递归利用的栈的原理,总是调用一个函数,但传入不同的参数,一层一层一层嵌套下去,当它遇到了一个终止条件(心酸了),又会将函数结果一层一层一层从里至外的返回,也就得到最终的结果了。

常见的问题:斐波那契数列、汉诺塔等经典问题

斐波那契数列(Fibonacci sequence)1、1、2、3、5、8、13、21、34

F(1)=1,F(2)=1, F(n)=F(n - 1)+F(n - 2)

public static int count(int number)
	{
		if(number == 1) return 1;
		if(number == 2) return 1;
		return count(number -1) + count(number -2) ;
	}

汉诺塔

a,b,c ,n层

初始: function = Hnot(n,a,b,c)

step1:a->b,n-1, c为过渡层 function = Hnot(n-1,a,c,b)

step2:a->c,第n层,直接移动过去 moveTo(number,a,c)

step3:b->c,n-1,a为过渡层 function = Hnot(n-1,b,a,c)

终止条件,n=0,return

,**初始A,B,C ,n-为塔数。

	public static void  move(int number,String from ,String to)
	{
		count ++ ;
		System.out.println(number + "号盘子" + from + "->" + to);
		
	}
	public static void  hanNuoTa(int n, String a ,String b ,String c)
	{
		if(n == 0)return;
		hanNuoTa(n-1,a,c,b);
		move(n,a,c);
		hanNuoTa(n-1,b,a,c);
	}

初始: function = Hnot(n,a,b,c)

step1:a->b,n-1, c为过渡层 function = Hnot(n-1,a,c,b)

step2:a->c,第n层,直接移动过去 moveTo(number,a,c)

step3:b->c,n-1,a为过渡层 function = Hnot(n-1,b,a,c)

终止条件,n=0,return

,**初始A,B,C ,n-为塔数。

	public static void  move(int number,String from ,String to)
	{
		count ++ ;
		System.out.println(number + "号盘子" + from + "->" + to);
		
	}
	public static void  hanNuoTa(int n, String a ,String b ,String c)
	{
		if(n == 0)return;
		hanNuoTa(n-1,a,c,b);
		move(n,a,c);
		hanNuoTa(n-1,b,a,c);
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值