数据结构之集合和链表

底层数据结构

线性结构

线性表是一种线性结构,它是由零个或多个数据元素构成的有限序列。线性表的特征是在一个序列中,除了头尾元素,每个元素都有且只有一个直接前驱,有且只有一个直接后继,而序列头元素没有直接前驱,序列尾元素没有直接后继。
数据结构中常见的线性结构有数组、单链表、双链表、循环链表等。线性表中的元素为某种相同的抽象数据类型。可以是C语言的内置类型或结构体,也可以是C++自定义类型。

single linked list 单向链表
double linked list 双向链表

当然,以上我们所介绍的链表是single linked list(单向链表),有时候我们更喜欢double linked list(双向链表),double linked list就是每个node不仅包含指向下后一个结点的引用,还包含着指向前一个结点的引用。后文我们在介绍链表的具体实现是会对这两种链表进行更加详细地介绍。

通常来说,链表支持插入和删除这两种操作,并且删除/插入链表头部/尾部结点的时间复杂度通常都是常数级别的,链表的不足在于不支持高效的random access(随机访问)。

数据结构图文解析之:数组、单链表、双链表介绍及C++模板实现

循环单链表和循环双链表由对应的单链表和双链表改造而来,只需在终端结点和头结点间建立联系即可。

利用底层数据结构实现 栈、队列、二叉树

栈(Stack)是一种线性存储结构,
队列(Queue)与栈一样,是一种线性存储结构,

链表描述
二叉树最常用的描述方法是用链表或指针。每个元素都用一个有两个指针域的节点表示,这两个域是LeftChild和RightChild。除此两个指针域外,每个节点还有一个data域。

时间复杂度

参考博客
时间复杂度就是
给出一个上或下(确)界

Java集合

有序集合List

arrayList

保证其元素中的唯一性,但不关心元素的顺序 Set

默认set集合内的元素是无序的,但是 可以指定set集合的元素排序方式
hashSet

映射Map, key值唯一

hashMap

4165335-2fa931a12950feef.png
image.png

集合元素排序

怎么排?

集合底层数据结构是啥?

hash table
key-value 键值对
有个HashTable类实现的,过时了
图中的 hash table 只是指明
set元素唯一,
map元素key值唯一
和HashTable并没有啥关系

resizable array
可变数组

balanced tree
平衡二叉树
底层还是链表

linked list
双向链表

默认线程不安全,实现线程安全

4165335-5ebcadd8dedb631e.png
image.png

They are unsynchronized, but the Collections class contains static factories called synchronization wrappers that can be used to add synchronization to many unsynchronized collections. All of the new implementations have fail-fast iterators, which detect invalid concurrent modification, and fail quickly and cleanly (rather than behaving erratically).

集合元素可以是null吗?

当然可以了, 集合元素只能是对象, 也就是对象的引用,
null,当然也可以作为空引用, 空对象了。
并且null和一般对象的处理是一样的, 在arraylist集合中可以添加多个null, 而在hashset集合中, 元素不可重复的,
无需区别对待。

Collections Framework Overview

map, set, list, lru cache 和 linkedHashMap 实现原理

HashMap 是基于哈希表的 Map 接口的非同步实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。

在 Java 编程语言中,最基本的结构就是两种,一个是数组,另外一个是指针(引用),HashMap 就是通过这两个数据结构进行实现。HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。

hash 算法 和 hash table

  • hash 算法
    数字“指纹”
    hash 值不同, 输入一定不同(同一个hash算法)
    hash 值相同, 输入值一般相同, 但是也会出现 “散列碰撞”, 即输入值不同。 “散列碰撞”会导致伪造数据的情况。
    不可逆, 也就是不可已根据hash值恢复出原始数据;
    所谓的输入, 经过hash算法后, 得到输出, 也就是 hash值, 散列值,
    hash算法只是随机的选取输入的部分内容作为特征值, 进行hash计算, 所以从理论上来说, 当多份输入,它们只是很微小的不同的话, 在hash算法随机选取特征值的过程中, 很可能会有相同的输出, 也就是相同的hash值, 散列值。

常见的hash算法及其应用

  • hash table
    散列表是散列函数的一个主要应用,使用散列表能够快速的按照关键字查找数据记录。(注意:关键字不是像在加密中所使用的那样是秘密的,但它们都是用来“解锁”或者访问数据的。)例如,在英语字典中的关键字是英文单词,和它们相关的记录包含这些单词的定义。在这种情况下,散列函数必须把按照字母顺序排列的字符串映射到为散列表的内部数组所创建的索引上。

散列表散列函数的几乎不可能/不切实际的理想是把每个关键字映射到唯一的索引上(参考完美散列),因为这样能够保证直接访问表中的每一个数据。

一个好的散列函数(包括大多数加密散列函数)具有均匀的真正随机输出,因而平均只需要一两次探测(依赖于装填因子)就能找到目标。同样重要的是,随机散列函数不太会出现非常高的冲突率。但是,少量的可以估计的冲突在实际状况下是不可避免的(参考生日悖论鸽洞原理)。

  • hashMap的基础数据结构, 数组和链表, 元素位置的选取就是用的hash table

散列表,它是基于高速存取的角度设计的,也是一种典型的“空间换时间”的做法。顾名思义,该数据结构能够理解为一个线性表,可是当中的元素不是紧密排列的,而是可能存在空隙。

散列表(Hash table,也叫哈希表),是依据关键码值(Key value)而直接进行訪问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来訪问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。

比方我们存储70个元素,但我们可能为这70个元素申请了100个元素的空间。70/100=0.7,这个数字称为负载因子。我们之所以这样做,也是为了“高速存取”的目的。我们基于一种结果尽可能随机平均分布的固定函数H为每一个元素安排存储位置,这样就能够避免遍历性质的线性搜索,以达到高速存取。可是因为此随机性,也必定导致一个问题就是冲突。所谓冲突,即两个元素通过散列函数H得到的地址同样,那么这两个元素称为“同义词”。这类似于70个人去一个有100个椅子的饭店吃饭。散列函数的计算结果是一个存储单位地址,每一个存储单位称为“桶”。设一个散列表有m个桶,则散列函数的值域应为[0,m-1]。

解决冲突是一个复杂问题。冲突主要取决于:

(1)散列函数,一个好的散列函数的值应尽可能平均分布。
(2)处理冲突方法。
(3)负载因子的大小。太大不一定就好,并且浪费空间严重,负载因子和散列函数是联动的。

hashMap 和 Hashtable

Hashtable 与 HashMap 的简单比较

HashTable 基于 Dictionary 类,而 HashMap 是基于 AbstractMap。Dictionary 是任何可将键映射到相应值的类的抽象父类,而 AbstractMap 是基于 Map 接口的实现,它以最大限度地减少实现此接口所需的工作。

HashMap 的 key 和 value 都允许为 null,而 Hashtable 的 key 和 value 都不允许为 null。HashMap 遇到 key 为 null 的时候,调用 putForNullKey 方法进行处理,而对 value 没有处理;Hashtable遇到 null,直接返回 NullPointerException。

Hashtable 方法是同步,而HashMap则不是。我们可以看一下源码,Hashtable 中的几乎所有的 public 的方法都是 synchronized 的,而有些方法也是在内部通过 synchronized 代码块来实现。所以有人一般都建议如果是涉及到多线程同步时采用 HashTable,没有涉及就采用 HashMap,但是在 Collections 类中存在一个静态方法:synchronizedMap(),该方法创建了一个线程安全的 Map 对象,并把它作为一个封装的对象来返回。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值