一丶十种数据结构相关特性和使用

本文探讨了十种基本数据结构中的数组和链表,重点讲解了数组的固定存储空间特性、优缺点以及涉及的排序算法,如冒泡排序、选择排序和快速排序。同时,介绍了链表的非顺序存储结构及其在空间需求上的优势,以及链表的基本操作。

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

一丶十种数据结构相关特性和使用
开始努力认真的学习,看看自己能达到哪个水平
常见的十种最基本的数据结构:数组、链表、栈、队列、散列表、跳表、图、树、堆、字典树
1.数组(java中的HashMap底层就是数组加链表加红黑树的形式,链表长度大于8转换为红黑树,更根据统计学原理,链表长度超过8的情况很低,这是就可以忽略红黑树所占空间比链表大的影响,相对于链表而言。个人感觉:普通算法时间换空间或者空间换时间,顶级的算法,时间和空间可以极大的缩小)
固定存储空间(空间方面)在最初的设计层面上,数 组是依赖内存分配形成的,在使用数组前必须先为它申请空间。这使得数组这种数据结构有了下的特性:(四种特性的实现原理有待查找资料
①一个数组占据的存储空间大小固定,不能改变。(联想:所以在java的数组中,当数组需要扩容的时候,是新建一个新的长度更大的数组,并把之前数组的数据全部复制过去)
②所占据的存储空间是专用的,不能被其他信息占据。
③所占据的存储空间是连续的,中间不能间隔其他信息
④数组中的各个元素可以用数组名和下标直接访问
按数组下标取值的时候,a[i] = *(a + i)计算机需要知道待访问内存的内存地址是多少,数组名就是数组存放的首地址,根据数据类型以及下标求出偏移量就可以访问到对应位置的数据。

优点:
①方便。要读要写都很直接。无论多长的数字,要访问其中的一个元素,只要知道它的下标,就能一部到位,直接访问对应的样式。

缺点:
①占内存空间。开始时就要把以后所有用到的空间都申请下来,就算长时间里装不满,也不许存入其他信息。(数组性第②点),空置的空间浪费了。
②修改困难。理论上讲,一个数组如果没有装满,那么所有的空置位置都应该在尾部,而不是到处乱空。如果给数组中加入新元素,则只能加在尾部,如果要插入到中间位置,就有一些元素被移动。反过来,如果删掉一个元素,也得把后面的再往前挪一位。
连续存储的关系,所有的这些限制,都是因为数组是连续的一快存储空间,且个元素由下标标识引起的。如果不遵守这些限制,数组相应的好处也就得不到了。

数组设计到的排序算法:冒泡排序,选择排序,插入排序,快速排序,合并排序,随机排序,统计排序
①冒泡排序:法是把较小的元素往前调或者把较大的元素往后调。这种方法主要是通过对相邻两个元素进行大小的比较,根据比较结果和算法规则对该二元素的位置进行交换,这样逐个依次进行比较和交换。
优点:稳定(假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。); 缺点:慢,每次只能移动相邻两个数据。

public static void bubbleSort(int[] array){
		int length = array.length;
		//要完成排序总共需要比较的循环的次数
		for(int i = 0;i< length-1;i++){
			//length-i-1每次比较的最后一位,每循环一次后,就有一个数字排好了位置
			for(int j = 0;j < length - i-1;j++){
				//决定是升序还是降序排列,把较大的数字往后交换为升序,把较小的数组往后交换为降序
				if(array[j]>array[j+1]){
					int temp = array[j];
					array[j] = array[j+1];
					array[j+1] = temp;
				}
			}
		}
	}

选择排序:每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在最前或最后,后面每一次选出来的依次排序,直到全部待排序的 数据元素排完
优点:空间复杂度为O(1) 缺点:效率不是很好,实际中很少使用

public static void selectSort(int[] array){
		int length = array.length;
		//要完成排序总共需要比较的循环的次数
		for(int i = 0;i< length-1;i++){
			//这里是每次循环挑选出最小的依次放在数组前面,最后完成排序
			for(int j = i+1;j < length;j++){
				//决定是升序还是降序排列,把最大最小的数字选出来放在应该在的位置
				if(array[i]>array[j]){
					int temp = array[j];
					array[j] = array[i];
					array[i] = temp;
				}
			}
		}
	}

插入排序:将第一个元素标记为已排序,对于每个未排序元素X“提取”元素X对于j = lastSortedIndex降至0,如果当前元素j> X, 将排序的元素向右移动1
中断循环并在此处插入X
优点:稳定,快。对于已经有一定顺序的数组,排序速度很快,因为只要找到比标记为已排序的数小的时候,这一轮循环就结束了–升序排序时,只要找到比标记为已排序的数大的时候,这一轮循环就结束了–降序排序时。
缺点:比较次数不一定

public static void insertSort(int[] array){
		//数组的长度
		int length = array.length;
		//要完成排序总共需要比较的循环的次数(和它之前的所有数比较)
		for(int i = 1;i< length;i++){
			//标记为已排序的元素(下标为 1-->length-1,第一位数前面没有数可以看成是已经排序的)
			int temp = array[i];
			//每个数都和它之前的所有数进行一个比较(第一位数前面没有数可以看成是已经排序的)
			for(int j = i-1;j >= 0;j--){
				//决定是升序还是降序排列,把数字插入到前一个它小后一个数比它大的位置(升序),
				//把数字插入到前一个它大后一个数比它下的位置(降序)
				//比选定的已排序大的时候,该数往后移动一位。(排序的数字已经取出,可以覆盖原有的位置)
				if(temp<array[j]){
					int more  = array[j+1];
					array[j+1] = array[j];
					array[j] = more;
				}else {
					array[j+1] = temp;
					break;
				}
			}
		}
	}

快速排序:采取的是分而治之的思想,先取一个基准数,将小于于基准数的的放在基准数的左边,比它大的放在基准数的右边(升序排序,降序排序相反),因为这个基准数左边是不小于它,右边是大于它的,所以现在这个基准数就找到了排序后它应该在的位置。然后基准数左右两边分别循环这个过程,直到整个序列有序。
优点:极快,数据移动少
缺点:不稳定

public static void quickSort(int[] array) {
		//数组的长度
		int length;
		//要判断array为null,长度为0,1的情况不需要进行排序
		if (array == null || (length = array.length) == 0 || length == 1) {
			return;
		}
		// 基准值设置为数组的第一个数
		quick(0, length - 1, array);
	}

	public static void quick(int left, int right, int[] array) {
		// 升序排列
		//到左指针大于右指针时递归结束
		if (left > right) {
			return;
		}
		// 双指针,分别从右往左比它小,和从左往右查询比他大,
		int i = left;
		int j = right;
		// 基准值
		int base = array[left];
		// i<=j意味着这个分区还没有排序左右分区完。
		while (i != j) {
			// 先从右往左找,找到比基准数小的,并且i<j
			//这里必须先从右往左找,因为我们需要的位置是比基准值小的最大下
			//标的数,因为先从右往左找一个区间的最后一次比较j停在的是比基
			//准数小的数的位置。如果是先从左往右找i会停在比基准数大的值的
			//最小下标的位置
			while (array[j] >= base && i < j) {
				j--;
			}
			// 先从左往右找,找到比基准数大的,并且i<j
			while (array[i] <= base && i < j) {
				i++;
			}
			if (i < j) {
				int temp = array[j];
				array[j] = array[i];
				array[i] = temp;
			}
		}
		// 将基准值和比它小的数的下标的最大一位交换(例:基准值是8;左右分区后的数组{8,2,4,5,7,12,45,24}
		// 当8与比它小的下标最大的一位交换后,数组为(2,4,5,7,8,12,45,24),就达到了左边都是比8小,右边都是比8大的效果)
		array[left] = array[i];
		array[i] = base;
		// 递归排序左右这两个分区
		quick(left, i - 1, array);
		quick(i + 1, right, array);

	}

总结:数组因为存储空间是连续的,所以想要查找数组的第n个数的时间复杂度为o(1),List添加数据只要不是在尾部进行,时间复杂度都为O(n),在其余位置添加都需要移动数据位置,删除和添加同理。数组是很多复杂数据结构的基础类型。对于数组几种不同的排序方法,他们优劣都必须要熟练掌握,主要从稳定性和时间,空间这个三个方面。

2.链表 一种物理上可以是非连续、非顺序的存储结构,通过指针链接次序实现的,想对于数组的顺序存储,对空间的要求就没那高了,数组必须要一段符合数组大小的连续的存储空间,而链表不同,逻辑上连续即可。链表中中的每一个单元除了存储自身数据之外,还存储了指向下一个数据的指针。(单向链表是最基本的数据结构类型,其余的什么双向链表,循环链表,单向循环链表,双向循环链表都是以单向列表而来,操作也基本类似,这里就不过多的描述)

Object a{
  //存储的是自身的数组
  private Object data;
  //存储的是到下一个数据的指针
  private Object next;
}

对于链表的使用,需要的掌握的有,链表的翻转、添加、删除及链表有环的时候的一些操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值