数据结构
1. 数据结构逻辑图
2. 定义
- 数据结构
- 数据结构是指相互之间存在一种或多种特定关系的数据元素的集合,就是描述对象间逻辑关系的学科。
- 所有的数据结构都支持几个基本操作:读取、插入、删除。
- 数据存储结构
- 就是数据在计算机中的存储方式
- 常用的数据存储方式有两种
- 顺序存储
- 把数据存储在一块联系的存储介质(硬盘或内存等)中
- 例如数组,存储数据时会开辟出一块联系内存,按顺序存储
- 非顺序存储
- 与顺序存储相反
- 例如链表,先不会开辟出一块内存来,而是只需要知道下一个节点存储的位置,就能把所以的数据连起来了。所以单向链表的最后一个节点是指向Null的。
- 顺序存储
- 数组、链表、栈和队列是最基本的数据结构,任何程序语言都会涉及到其中的一种或多种
3. 数组
3.1 介绍
- 概念
- 存储数据长度固定的容器,存储多个数据的数据类型要一致
- 原理
- 数组创建会在内存划分一块连续的内存将数据按照顺序存储
- 格式
- 数据类型 [ ] 数组名
- 数据类型 数组名 [ ]
3.2 初始化
- 动态初始化
- 数据类型 [ ] 数组名 = new 数据类型 [数组长度] ;
- 当动态初始化之后,数组中的每一个数据都拥有一个值。
- 静态初始化
- 数据类型[ ] 数组名 = new 数据类型 { 元素1,元素2,……元素n};
- 简写
- 数据类型[ ] 数组名 = { 元素1,元素2 …… 元素n };
- 静态初始化和动态初始化的区别
- 动态初始化
- 只明确元素个数,不知道具体的值,推荐动态初始化
- 只指定长度,并没有给数组里面的每一个元素赋值.
- 静态初始化
- 知道具体的元素值,直接静态初始化即可
- 在指定了长度的同时也给里面的每一个元素都进行了赋值.
- 动态初始化
3.3 存取
- 存储
- 在java中当创建数组时会在内存中划分出一块连续的内存
- 当有数据进入的时候会将数据按顺序的存储在这块连续的内存中
- 读取
- 当需要读取数组中的数据时,提供数组中的索引
- 数组会根据索引将内存中的数据取出来,返回给读取程序
- 注意事项
- 并不是所有的数据都能存储到数组中
- 相同类型的数据才可以一起存储到数组中
- 访问
- 索引
- 每一个存储到数组的元素,都会自动拥有一个编号,从0开始,这个编号称之为索引,可以通过索引访问到数组中的元素。
- 访问格式: 数组名 [ 索引 ];
- 数组访问常见问题
- 索引越界异常(访问了一个数组没有的索引)
- 空指针异常 (数组等于null,则数组的内存地址将不再保存,也就意味着数组将不能被操作)
- 索引
3.4 数组遍历
- 将数组中的每个元素分别获取出来,就是遍历
- 数组名.length为数组的存储长度
- 利用for循环
- for(int i = 0;i < arr.length; i ++){ }
3.5 特点
- 读取容易
- 数组在存储数据时是按顺序存储的
- 只需要告诉数组索引取数据就可以了
- 数组会直接把你想要的位置的数据取出来给你
- 插入和删除困难
- 这些存储数据的内存是连续的
- 插入和删除需要变更整个数组中的数据的位置
4. 链表
4.1 链表的存取
- 存储
- 分两块区域
- 一块存储数据
- 另一块记录下一个数据的地址
- 读取
- 从头开始
- 如果当前不是要找的数据,则根据储存的下一个数据的地址找下一个数据
4.2 特点
- 读取麻烦
- 没有索引
- 只有上一个数据记录下一个数据的地址值
- 需要遍历查找读取
- 插入和删除容易
- 将要插入的数据填入
- 更改前后数据记录的地址值即可
- 删除同理
- 链表的存储方式可以利用到碎片空间
5. 栈
- 栈是一种先进后出的数据结构
- 数组和链表都可以生成栈
- 当数据进入到栈时会按照规则压入到栈的底部
- 再次进入的数据会压在前次进入的数据上面
- 在取出栈中的数据的时候会先取出最上面的数据
- 栈由什么生成,它的操作特点会继承相应的操作特点
6. 队列
- 队列是一种先进先出的数据结构
- 数组和链表也都可以生成队列
- 当数据进入到队列中时也是先进入的在下面后进入的在上面
- 但是出队列的时候是先从下面出,然后才是上面的数据出
- 注意:栈和队列是可以互相转换的
7. 树
7.1 概念
- 树结构是一种包括节点(nodes)和边(edges)的拥有层级关系的一种结构
7.2 树中的相关概念
- 根节点(root): 树的最上层的节点,任何非空的树都有一个节点
- 路径(path): 从起始节点到终止节点经历过的边
- 父亲(parent):除了根节点,每个节点的上一层边连接的节点就是它的父亲(节点)
- 孩子(children): 每个节点由边指向的下一层节点
- 兄弟(siblings): 同一个父亲并且处在同一层的节点
- 叶子节点(leaf node): 没有孩子的节点成为叶子节点
- 节点: 在树结构中,每一个元素称之为节点
- 度: 每一个节点的子节点数量称之为度
7.3 二叉树
- 特点
- 任意一个节点的度小于等于2
- 结构图
7.4 二叉查找树
- 特点
- 二叉查找树,又称二叉排序树或二叉搜索树
- 每个节点上最多两个子节点
- 左子树上的所有节点的值都小于根节点的值
- 右子树上的所有节点的值都大于根节点的值
- 添加规则
- 小的存左边
- 大的存右边
- 一样的不存
- 二叉查找树结构图
7.5 平衡二叉树
- 特点
- 二叉树左右两个子树的高度差不超过1
- 任意节点的左右两个子树都是平衡二叉树
- 平衡二叉树的旋转
- 旋转触发时机
- 当添加一个节点后,该树不再是一个平衡二叉树
- 左旋
- 就是将根节点的右侧往左拉,原先的右子节点变成新的父节点,并把多余的左子节点让出,给已经降级的根节点当右子节点
- 右旋
- 就是将根节点的左侧往右拉,左子节点成了新的父节点,并把多余的右子节点让出,给已经降级的根节点当作左子节点
- 旋转触发时机
- 旋转的四种情况
-
左左
- 当根节点左子树的左子树有节点插入导致二叉树不平衡
- 直接对整体进行右旋即可
-
左右
- 当根节点左子树的右子树有节点插入,导致二叉树不平衡
- 先在左子树对应的节点位置进行左旋,再对整体进行右旋
-
右右
- 当根节点右子树的右子树有节点插入,导致二叉树不平衡
- 直接对整体进行左旋即可
-
右左
- 当根节点右子树的左子树有节点插入,导致二叉树不平衡
- 先在右子树对应的节点位置进行右旋,在对整体进行左旋
-
7.6 二叉树的遍历
- 二叉树是一种递归结构
- 可以直接用递归的方式来遍历它
- 但是当处理顺序不同的时候,树又分为三种遍历方式
- 先(根)序遍历: 先处理根,之后是左子树,然后是右子树
- 中(根)序遍历: 先处理左子树,之后是根,最后是右子树
- 后(根)序遍历: 先处理左子树,之后是右子树,最后是根
7.7 红黑树
7.7.1 特点
- 平衡二叉B树
- 每一个节点可以说红或者黑
- 红黑树不是高度平衡的,它的平衡通过“自己的红黑规则”进行实现
7.7.2 红黑规则
- 每一个节点或是红色的,或是黑色的
- 根节点必须是黑色的
- 如果一个节点没有子节点或父节点,则该节点的相应属性值为Nil,这些Nil视为叶节点,每个叶节点是黑色的
- 如果某一个节点是红色的,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)
- 对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点
7.7.3 红黑树添加节点的默认颜色
- 添加节点时,默认为红色,效率高
7.7.4 添加节点后如何保持红黑规则
- 根节点位置
- 直接变为黑色
- 非根节点位置
- 父节点为黑色
- 不需要任何操作,默认红色即可
- 父节点为红色
- 叔叔节点为红色
- 将父节点设为黑色,将叔叔节点设为黑色
- 将祖父节点设为红色
- 如果祖父节点为根节点,则将根节点再次变为黑色
- 叔叔节点为黑色
- 将父节点设为黑色
- 将祖父节点设为红色
- 以祖父节点为支点进行旋转
- 叔叔节点为红色
- 父节点为黑色
8. 哈希表
8.1 哈希值
- 概念
- 是根据对象的地址值或者属性值计算出来的一个int 类型的值
- 使用
- 哈希表在进行元素存储的时候就需要用到元素的哈希值
- 获取
- 可以直接调用Object类中的hashCode方法进行获取
public native int hashCode(); //本地方法,这个方法调用C语言的代码获取对象的哈希值
如何调用C语言的代码
JNI ----> Java Native interface(此方法在本地方法栈中运行)
- 使用案例
- HashSet底层的数据结构使用的就是哈希表
- 特点
- 同一个对象调用多次hashCode方法获取的哈希值是一样的
- 不同对象的哈希码值是不一样的,但是我们可以通过重写Object类中的hashCode方法让不同的对象具有相同的哈希码值
- 一般重写的时候可以借助idea开发工具的快捷方式进行重写
8.2 1.7版本哈希表的数据结构
- 哈希表 = 数组 + 链表
- 先创建数组,默认长度为16,默认加载因子为0.75,数组名为table
- 根据元素哈希值跟数组长度计算出应存入的位置,如果一个位置有多个属性不同的元素,则这些元素组成链表
- 存储元素的过程
- 步骤说明
- 首先调用对象的hashCode方法获取对象的哈希码值
- 然后根据对象的哈希码值计算对象在数组中的存储位置
- 然后判断该位置是否存在元素,如果没有将元素存储到该位置
- 如果该位置存在元素,则遍历该位置所有元素获取其哈希值和新存入的元素哈希值对比
- 如果不相同将元素存储在该位置,原有的元素挂在下面
- 如果相同调用对象的equals方法,比较对象的内容是否相同
- 如果不相同将元素存在该位置,如果相同则说明重复,不存储
8.3 1.8版本哈希表的数据结构
- 哈希表 = 数组 + 链表 + 红黑树
- JDK1.7版本中的哈希表存在弊端
- 当链表的长度过长会影响插入和查询元素的效率,所以做了优化
- 当链表的长度超过8并且数组的长度超过64的时候就会把链表变成红黑树
- 如果数组的长度没有超过64,仅仅会进行扩容