数组
数组是指的有限个类型相同的变量的集合。组成数组的各个变量称为数组的元素,有时也称为下标变量。
数组的特点
对于数组有以下特点
- 数组内的元素应该具有相同的数据类型
- 数组元素用整个数组的名字和它自己在数组中的顺序位置来表示,如array[1]
- 数组创建后其长度就不能发生变化
- 数组所占用的存储空间是固定且连续的
- 数组中各个元素可以使用数组名称和下标进行直接访问
优点
- 因为长度固定,集合内元素类型一致,整个数据结构非常简单
- 从数组中查找元素的时候,只需要知道其所在数组以及其下标就可以直接获得数,查询数据效率很高。
缺点
- 资源浪费,数组声明的时候求需要申请所有存储空间,即使最终没有使用那么多空间。
- 修改自困难,在实际使用空间大于申请空间的时候,需要重新申请新的数组并将旧数据转移至新数组中。
- 当删除数组中元素的时候,会导致连续的存储空间中出现未使用的存储空间会遍布数组各处,而不是集中在尾部。
java中的数组
声明数组
- 数据类型[] 数组名 : String[] str = new String[10];
- 数据类型 数组名[] : String str[] = new String[10];
赋值
str[2] = “data”
输出值
System.out.println(str[2]);
初始化赋值
String[] str = new String{“data1”,“datat2”,“data3”};
二维数组
二维数组可以认为是每个数组的元素也是一个一维数组。二维数组可以理解为存在横纵结构的网状结构
声明二维数组组
- 数据类型[][] 数组名 : String[][] str = new String[10][10];
- 数据类型 数组名[][] : String str[][] = new String[10][10];
- 数据类型[] 数组名[] : String[] str[] = new String[10][10];
赋值
str[2][3] = “data”
输出值
System.out.println(str[2][3]);
初始化赋值
String[] str = new String{{“data1”,“datat2”,“data3”},{“val1”,“val2”,“val3”},{“num1”,“num2”,“num3”}};
散列表
散列表也叫哈希表。是根据关键码值而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。
在JAVA中的HashMap就是通过于散列函数获得数组下标,然后去对应的下标位置去获取数据。
散列函数(哈希函数)
对于线性表、树来说记录在结构中的位置是随机的,记录的值和关键key不存在确定关系。所以在此类结构中查找数据需要进行一系列的比较操作。这类比较操作的效率取决于进行比较的次数。而散列函数则实现了通过关键字(key)经过计算得到了一个散列值。而通过这个值可以确定数据结构中一个唯一的存储位置。
使用散列函数可以得到下面的内容;
- 因为是确定数组下标,所以散列函数计算到的值是一个非负整数
- 同样的关键字经过函数计算得到的值是相同的
- 虽然散列函数极力尝试避免出现不同的关键字(key)出现一样的散列值。但是实际上这种情况总会发生。
哈希碰撞
上面说到,散列函数可能出现不同的关键字(key)获得相同的散列表。这种情况我们称为哈希碰撞。哈希碰撞将会使数据返回一个错误的内容。
解决冲突
目前解决哈希碰撞主要有下面几个方法:
- 开放寻址法
- 再哈希法
- 链表法
- 建立一个公共溢出区
开放寻址法
开放寻址法主要是根据下面的函数
Hi=(H(key) + di) MOD m,i=1,2,…,k(k<=m-1)
上面函数中:Hi
为最终地址,H(key)
为哈希函数,di
为增量。当出现冲突的时候,使用上面公式,在哈希函数之后添加增量来寻找空闲的地址。而di
计算的结果主要有三种方式
线性探测再散列
依次向后查询
di=1,2,3,…,m-1
二次探测再散列
依次向前后查询,增量为1、2、3、…………的二次方
di=1^2,-1^2,2^2,-2^2,⑶^2,…,±(k)^2,(k<=m/2)
伪随机探测再散列
使用随机的方式产生一个增量
优点
所有的数据都都保存到数组中,查找起来会比较快。
缺点
- 删除数据的时候并不能直接删除,需要标记为deleted,所以相比链表方法会消耗更多内存空间。
- 当数据量较少的时候,产生冲突的几率不高,但是当数据量较大的时候,出现散列冲突的概率就越大,插入数据需要经过很多次的寻址或者拉很长的链,查找过程也会很慢。
再哈希法
当出现冲突的时候,使用其他的哈希函数计算,直到冲突消失
链表法
链表法也是目前最常用且简单的方法。
每个数组对应一个链表,所有散列值相同的元素,被放在对应链表中,链表中保存关键字以及其对应的值。当出现冲突的时候,通过遍历链表,对比关键字以及链表中的关键字,来确定最终数据的位置。
优点
- 链表法处理冲突方式简单,即非同义词决不会发生冲突,因此平均查找长度较短
- 因为链表法中的链表节点可以动态扩容,所以它适用于无法确定表长的情况
- 对于有大量元素的时候,使用寻址法可能产生大量冲突导致性能下降。而链表法对于链表法,使用链表保存了冲突的数据,查询虽然会有所下降但是相比于寻址法好很多
- 在用链表法构造的散列表中,删除结点只要简单地删去链表上相应的结点即可,非常简单
缺点
- 链表法需要保存指针地址,指针需要额外的空间
- 和数组不同链表会零散的分布在内存的各个位置中,非连续数据无法使用CPU的查询优化。
个人水平有限,上面的内容可能存在没有描述清楚或者错误的地方,假如开发同学发现了,请及时告知,我会第一时间修改相关内容。假如我的这篇内容对你有任何帮助的话,麻烦给我点一个赞。你的点赞就是我前进的动力。