一、线性表
优点:可以很快速的找到内存地址 查询,修改快
缺点:在中间部分新增,删除部时需要移动后续的元素
像java中的stream流的过滤等操作都是新建立一个集合有序插入返回,空间换时间
java中list下标为什么要从0开始
对于寻址来说减少一次减法运算
java中常用集合和map的结构分析
**List:**List是一种有序集合,它允许元素重复。List中的元素可以按照插入的顺序进行访问,因此具有线性表的特性。常见的List实现类包括ArrayList、LinkedList等。List中的元素可以通过索引进行随机访问,这在需要频繁访问元素的情况下非常有用。然而,插入和删除操作可能会导致其他元素的移动,因此在处理大量数据时,其效率可能不如Set和Map高。
**Set:**Set也是一种线性表,但它不允许元素重复。Set中的元素是无序的,即元素的存储顺序与插入顺序可能不一致。Set的实现类包括HashSet、LinkedHashSet等。由于Set不包含重复元素,因此在需要去除重复数据时非常有用。然而,由于Set不保证元素的顺序,因此在需要保持元素插入顺序的场景中,List或Map可能是更好的选择。
**Map:**Map不是线性表,而是一种关联数据结构,它存储的是键值对。Map中的键是唯一的,每个键关联一个值。Map的实现类包括HashMap、TreeMap等。Map在需要快速查找特定数据时非常有用,例如,通过键查找对应的值。由于Map关注的是键值对的关联,而不是元素的顺序,因此它不具有线性表的特性。
二、链表
单链表:只能从前往后查
优点:新增,删除部时比较快,只需要开辟一个空间->修改指向->更新指向
缺点:查询和修改需要遍历
双链表(优化单链表)
为什么c语言中的链表带有头节点和尾结点,java中没有
在C语言中,链表通常包含头节点和尾节点,因为C语言中的链表操作相对复杂,需要直接操作节点,因此需要直接访问头节点和尾节点。这样可以方便进行插入和删除操作。
然而在Java中,链表的实现方式不同。在Java中,链表是通过Node类的内部类实现的,每个Node只知道自己的下一个节点,不知道头节点和尾节点。这是因为Java的内存管理和垃圾收集机制使得在Node类中保存对头节点或尾节点的引用没有任何意义,反而会增加复杂性和潜在的错误风险。
在Java中,如果需要访问链表的头节点或尾节点,可以通过额外的变量来引用头节点或尾节点,或者在链表操作时维护一个头节点或尾节点的引用。这样做的目的是为了简化链表操作的接口和实现复杂性,使得在进行链表插入和删除等操作时不需要考虑特殊情况。
三、栈
栈的优点
快速分配和释放:栈的内存分配和释放速度很快,因为它只需要简单地移动栈指针。这种机制使得栈在处理大量数据时具有较高的效率。
数据局部性:栈上存储的数据通常具有良好的局部性,这意味着对这些数据的访问很可能会利用到缓存,从而提高访问速度。
存取速度快:栈的存取速度仅次于直接位于CPU中的寄存器,虽然比不上寄存器,但仍然非常快。
栈的缺点包括:
数据大小与生存期限制:存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。这是因为栈空间是预先分配的,大小固定,无法动态调整。
栈溢出问题:由于顺序栈的存储空间是有限的,当入栈的元素超过栈的容量时,会发生栈溢出问题。这需要采取相应的措施进行处理。
扩容开销:顺序栈在创建时容量是固定的,当栈中元素个数超过最大容量时,需要进行扩容操作,这需要将原栈中的元素复制到新栈中,带来较大的开销。
综上所述,栈作为一种数据结构,具有其独特的优势和局限性。在实际应用中,需要根据具体需求来选择是否使用栈以及如何优化栈的使用。
四、队列
简单队列
循环队列
队列的用处
五、哈希查找表
哈希表与哈希方法
哈希表与哈希方法:选取某个函数,依该函数按关键字计算元素的存储位置,并按此存放;查找
时,由同一个函数对给定值kx计算地址,将kx与地址单元中元素关键码进行比,确定查找是否成
功,这就是哈希方法(杂凑法);哈希方法中使用的转换函数称为哈希函数(杂凑函数);按这个思
想构造的表称为哈希表(杂凑表)
通常关键码的集合比哈希地址集合大得多,因而经过哈希函数变换后,可能将不同的关键码映射
到同一个哈希地址上,这种现象称为冲突
在哈希查找方法中,冲突是不可能避免的,只能尽可能减少。所以,哈希方法必须解决以下两个问题:
1)构造好的哈希函数
(a)所选函数尽可能简单,以便提高转换速度。
(b)所选函数对关键码计算出的地址,应在哈希
地址集中大致均匀分布,以减少空间浪费。
2)制定一个好的解决冲突的方案
哈希函数的构造方法
1)直接定址法
Hash(key)=a·key+b(a、b为常数)
即取关键码的某个线性函数值为哈希地址,这类函数是一一对应函数,不会产生冲突,但要求地址集合与关键码集合大小相同,因此,对于较大的关键码集合不适用
例如:关键码集合为{100,300,500,700,800,900},选取哈希函数为
Hash(key)=key/100,则存放如下:
2) 除留余数法
Hash(key)=key mod p(p是一个整数)
即取关键码除以p的余数作为哈希地址。使用除留余数法,选取合适的p很重要,若哈希表表长为m,则要求p≤m,且接近m或等于m。p一般选取质数,也可以是不包含小于20质因子的合数。
3)乘余取整法
Hash(key)=_B*(Akey mod 1)(A、B均为常数,且0<A<1,E为整数)
以关键码key乘以A,取其小数部分(Akey mod1就是取A*key的小数部分),之后再用整数B乘以这个值,取结果的整数部分作为哈希地址。
4)数字分析法
数字分析法根据r种不同的符号,在各位上的分布情况,选取某几位,组合成哈希地址。所选的位应是各种符号在该位上出现的频率大致相同
5)平方取中法
对关键码平方后,按哈希表大小,取中间的若干位作为哈希地址
6)折叠法
此方法将关键码自左到右分成位数相等的几部分:最后一部分位数可以短些,然后将这几部分叠加求和,并按哈希表表长,取后几位作为哈希地址。这种方法称为折叠法。通常有两种叠加方法:
1.移位法–将各部分的最后一位对齐相加。
2.间界叠加法–从一端向另一端沿各部分分界来回折叠后,最后一位对齐相加随机数法
7)随机数法
选择一个随机函数,将关键字作为这个随机函数的自变量,随机函数的函数值作为关键字的哈希
地址。H(key)= random(key)当关键字的长度不相等时,采用此方法构造的哈希函数比较适合。
哈希函数的冲突解决方法
1、开放定址法
所谓开放定址法,即是由关键码得到的哈希地址一旦产生冲突,就去寻找下一个空的哈希地址,
只要哈希表足够大,空的哈希地址总能找到,并将数据元素存入。寻找空哈希地址方法很多,下面介绍三种:
线性探测法
Hi=(Hash(key)+d;) mod m( 1≤i < m )
其中:
Hash(key)为哈希函数
m为哈希表长度
d为增量序列1,2,…m-1,且d=i
【例如】
关键码集为{47,7,29,11,16,92,22,8,3},
哈希表表长为11,
Hash(key)=key mod 11,用线性探测法处理冲突,建表如下:
47、7、11、16、92均是由哈希函数得到的没有冲突的哈希地址;Hash(29)=7,哈希地址有冲突,需寻找下一个空的哈希地址:由H1=(Hash(29)+1)mod 11=8哈希地址8为空,将29存入另外,22、8同样在哈希地址上有冲突,也是由H1找到空的哈希地址的;
Hash(3)=3,哈希地址上冲突,由
H1=(Hash(3)+1) mod 11=4仍然冲突;
H2=(Hash(3)+2) mod 11=5仍然冲突;
H3=(Hash(3)+3) mod 11=6不冲突,存入
线性探测法可能使第i个哈希地址的同义词存入第i+1个哈希地址,这样本应存入第i+1个哈希地址的元素变成了第i+2个哈希地址的同义词,…,因此,可能出现很多元素在相邻的哈希地址上“堆积”起来,大大降低了查找效率。为此,可采用二次探测法,或双哈希
函数探测法,以改善“堆积”问题。
二次探测法
Hi=(Hash(key)±d;) mod m
其中:
Hash(key)为哈希函数
m为哈希表长度,m要求是某个4k+3的质数;
di为增量序列 12 ,-12,22,-22,…,q2
仍以上例用二次探测法处理冲突,建表如下
链地址法
基本思想是:将具有相同哈希地址的记录链成一个单链表,m个哈希地址,则有m个单链表,然后用一个数组将m个单链表的表头指针存储起来,形成一个动态的结构。
【例如】
设*{47,7, 29,11,16,92,22,8,3,50,37,89}的哈希函数为Hash(key)=key mod 11,用链地址法处理冲突,建表如图所示:
ASL=(19+2*4)/12
=17/12
哈希表的装填因子α的定义
α = 填入表中的元素个数/哈希表的长度
α是哈希表装满程度的标志因子。由于表长是定值,α与“填入表中的元素个数”成正比,所以,a越大,填入表中的元素较多,产生冲
突的可能性就越大;a越小,填入表中的元素较少,产生冲突的可能性就越小。