数据结构总结复习

数据结构总结

分为三部分:1.数据结构理论2.和Java语言相关知识3.代码编写

一.数据结构理论

1.复杂度

  1. 度量标准: 算法的执行速度、需要的最大空间
  2. 衡量的是算法随着“数据规模”变化的一个变化趋势(数据规模一般比较大)
  3. 衡量算法复杂度最重要的一点:找到数据规模(n、m …)
  4. (假设 CPU 执行基本语句的频率恒定)用基本语句的执行次数去衡量
  5. 常见的复杂度:
    O(1)、O(log(n))、O(n)、O(n * log(n))、O(n^2)
  6. 排序算法的复杂度
    排序算法的复杂度

2.元素组织的基本结构

1.线性结构

关于线性表:

  1. 特点:(1)存在下标(第一个、第二个 …)。只有在线性结构中,才有讨论下标的意义、(2)有排序的意义
  2. 两个特殊的元素:第一个元素(没有前驱)、最后一个元素(没有后继)
  3. 下标的取值范围:[0, 元素个数)
  4. 插入的时候,取值范围:[0, 元素个数]

顺序表: 根据下标访问特点位置元素的时间复杂度是 O(1)
链表: 在给定前驱结点的情况下,插入、删除是 O(1),如果真的要使用链表,轻易不要使用下标访问、修改元素,成本太高。

空间:
顺序表是有一部分空闲空间视为浪费;
链表每个结点都需要保存 next或者还有 prev,也都是额外的空间成本。

常见操作:增、删、查、改

  1. 往指定位置添加元素
  2. 删除元素
  3. 获取某个位置的元素
  4. 修改某个位置的元素
  5. 给定某元素,确定其位置下标(第一个、最后一个、所有)
  6. 给定某元素,判断是否存在于线性表中
  7. 获取元素个数
  8. 判断是否为空的线性表
  9. 排序
  10. 洗牌(随机打乱顺序)
2.树形结构

重点学习了树形结构的子集 —— 二叉树结构

1. 常见概念
根结点:一棵树中,没有双亲结点的结点;
节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
树的高度或深度:树中节点的最大层次;
节点的度:一个节点含有的子树的个数称为该节点的度;
树的度:一棵树中,最大的节点的度称为树的度;
双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点;
孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点;

2. 完全二叉树、满二叉树
满二叉树: 一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就
是说,如果一个二叉树的层数为K,且结点总数是 2 k − 1 2^k-1 2k1 ,则它就是满二叉树。
完全二叉树: 完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度
为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点
一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。

3. 二叉树的性质
1.若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有 2 i − 1 2^{i-1} 2i1(i>0)个结点
2. 若规定只有根节点的二叉树的深度为1,则深度为K的二叉树的最大结点数是 2 k − 1 2^k-1 2k1(k>=0)
3. 对任何一棵二叉树, 如果其叶结点个数为 n0, 度为2的非叶结点个数为 n2,则有n0=n2+1
4. 具有n个结点的完全二叉树的深度k为 l o g 2 ( n + 1 ) log_2(n+1) log2(n+1)上取整
5. 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的顺序对所有节点从0开始编号,则对
于序号为i的结点有:
若i>0,双亲序号:(i-1)/2;i=0,i为根节点编号,无双亲节点
若2i+1<n,左孩子序号:2i+1,否则无左孩子
若2i+2<n,右孩子序号:2i+2,否则无右孩子

4. 遍历问题
1. 深度优先遍历 —— 前序遍历、中序遍历、后序遍历 —— 栈支持
2. 广度优先遍历 —— 层序遍历 —— 队列支持

3.有实际用途的数据结构

1.线性结构
  1. 顺序表:同上面的顺序表
  2. 链表:同上面的链表
  3. 栈:

FILO | LIFO
回溯场景下使用 (原路返回)

  1. 树、图的深度优先遍历
  2. 颠倒某个顺序时
  3. 括号匹配
  4. 后缀表达式
  5. 暴力走迷宫
  1. 队列:

FIFO
确保公平、维持原始顺序

  1. 树、图的广度优先遍历
  2. 任务、数据传递
  1. 双端队列
2.动态数据集中找最值

二叉堆

动态数据集:数据集中的数据存在频繁地添加、删除操作
在这个前提下,查找数据集中的最值(最大值|最小值)
堆的特点:任意位置的父结点的值 >= 两个孩子的值 —— 大堆
堆的表示:

  1. 逻辑上是完全二叉树
  2. 物理上是一个数组(可以带长度用来约束部分区间)
  3. 下标公式:
    left = 2 * parent + 1
    right = 2 * parent + 2 = left + 1
    parent = (child - 1) / 2

核心操作 —— 向下调整操作:O(log(n))

  1. 成立的前提:除了要调整的地方,其他位置已经满足堆的性质
  2. 调整的时候两个出口:
    1. 要调整的位置满足堆的性质
    2. 调整到叶子上
  3. 具体操作:
    1. 先找左右孩子中最大的一个(右孩子可能不存在)
    2. 将最大的一个孩子结点和父结点对比
    3. 如果父结点大于最大的一个孩子结点,则退出
    4. 如果父结点小于最大的一个孩子结点,则交换
    5. 交换之后再次以最大的孩子结点那个位置不断向下调整

其他的两个操作:

  1. 建堆 —— 时间复杂度O(n)
  2. 向上调整 —— 时间复杂度O(log(n))

应用:
优先级队列

  1. 插入 —— 向上调整
  2. 删除 —— 交换 + 向下调整

堆排序(排升序,建大堆)

  1. 建堆
  2. 向下调整

Top-K 问题:找最小的 K 个,建大堆(size == k)

3.动态数据集中搜索

除了动态数据集的搜索之外,还有:

  1. 静态的数据集 —— 维护成有序数组做二分查找
  2. 数据量较少(低于 1W 个) —— 暴力搜索(遍历查找)

主要的模型:纯 key 模型 vs key-value 模型
纯 key 模型 —— Set —— 判断是否存在
key-value 模型 —— Map —— 根据 key 查找对应 value
备注:key 一定不允许重复

对比 BST vs HashTable
搜索树优点:key 有序、时间复杂度恒定(永远不会有意外)
哈希表:实现简单、性能略高

二叉搜索树

  1. 性质:任意位置,父亲的 key 小于 左子树的所有 key;大于右子树的所有 key
  2. 中序遍历是有序 :按照 key 的大小顺序(而不是插入顺序)
  3. 要使用搜索树,前提是 key 必须具备可以比较大小的能力

主要操作:

  1. 查找(核心)
  2. 插入(记住插入的位置是 null 的地方)
  3. 删除

搜索树的时间复杂度: O(树的高度) : O(log(n)) ~ O(n)
但以后一般提到搜索树都是指的平衡搜索树,所以,一般认为就是 O(log(n))

常见的平衡搜索树:

  1. 二叉树平衡搜索树:AVL树、红黑树
  2. 非二叉树平衡搜索树:B-树家族

哈希表

核心思想: 将 key 归类存储、查找
分类方式:以 hash 函数形式体现
hash 函数的要求:

  1. 硬性要求:同一个 key,多次 hash,结果应该是一致的
  2. 软性要求:哈希后的结果尽可能的均匀

用哈希桶的方式来看哈希表:一个数组 + 多条链表
冲突【核心】: k1 != k2 && hash(k1) == hash(k2)

围绕冲突:

  1. 冲突不好
  2. 冲突无法完全避免
  3. 如何减少冲突率
  4. 真的遇到冲突怎么办

常见操作: 查找、插入、删除

插入:

  1. 对象(key)-> 整型
  2. 整型 -> 合法下标
  3. 根据下标在对应的链表中查找是否存在
  4. 真正进行插入

时间复杂度(一切都是基于冲突率不高的情况下):O(1)

哈希表的扩容

触发时机: 是因为对应冲突率无法忍受了
扩容的时候,需要将所有 key 重新计算新的位置

哈希表解决冲突的办法

1.泊松分布
2.只要维护的够好,基本认为冲突的 key 的个数在 8 个以内。如果真的超过 8 个了,说明遇到特殊情况了(一般被恶意用户攻击了 —— 哈希攻击),就要想办法解决,常见的解决办法:

  1. 提升hash算法,不再公开
  2. 再哈希
  3. 使用红黑树替代链表维护冲突的元素

1.闭散列:成功、失败时的平均查找次数
2.开散列(哈希桶)

二.和Java语言相关知识

1.语法:泛型(GenericType)

  1. 代码中泛型的标志: 一对尖括号 <>
  2. 数据类型变化起来
  3. 使用过程
    3.1 泛型的上下级关系
    3.2 泛型的通配符:PECS

重点掌握使用别人写好的泛型类即可。

2.常见类:包装类

出现背景:
Java 中有一类数据公民不是对象(基本类型),所以,很多为对象准备的规则(比如泛型)就无法适用于基本类型,为了解决该问题,引入了包装类
int <-> Integer
char <-> Character

包装类除了作为包装基本类型称为对象之外,还经常是一些常见方法的组织类,比如:Integer.valueOf(…_) Integer.MAX_VALUE
Character.isDigit(…)
装箱 、拆箱 + 自动装箱、自动拆箱

3.Java 中的赋值操作,等号两边允许的规则

a = b; 包含隐式赋值
a 和 b 是什么情况下,语法成立:

  1. a 和 b 具有完全相同的类型
  2. 都是基本类型的前提下:a 表示的范围大于 b 表示的范围
    double > float > long > int > short > byte
  3. 反过来,如果表示范围小于,强制类型转换后同样成立
  4. 都是引用的前提下:a 的类型是 b 的类型的父类(含接口)
  5. 反过来,如果 a 的类型在下,强制类型转换后同样成立。注意:语法正确,但运行时是否会有类型转换异常不保证
  6. a 是基本类型,b 相对应的包装引用类型是,以自动拆箱的形式成立。 int a = new Integer(1); <-> int a = new Integer(1).intValue();
  7. 反过来是,自动装箱; Integer a = 1; Integer a = Integer.valueOf(1);

4.对象的比较

相等 不相等的比较

同一性、相等性
== vs equals
什么叫做正确的重写了 equals 方法
Collection.contains(…)
List.indexOf(…)
List.lastIndexOf(…)
HashMap.get(…)
背后都是相等性的判断 -> 元素类型必须正确地重写过 equals 方法,否则可能无法得到正确结果

大于、小于、等于

自然顺序:java.lang.Comparable
外部比较器: java.util.Comparator
-1: 前面"小"
0: 相等
1: 前面"大"
搜索树\排序\优先级队列

5.Java 集合类框架体系(Java Collection Framework)

请添加图片描述

1.Iterable + Iterator

Iterator iterator();
加强 for : foreach

Iterator:
hasNext()
next()

2.Collection 的常见方法

List vs Set
外在表现:
List 有位置的概念,元素允许重复
Set 没有位置的概念,元素不允许重复

List

掌握每个方法的含义 + 时间复杂度 + 背后的基本过程
add(e)
add(index, e)
remove(e)
remove(index)
get(index)
set(index, e)
indexOf(e)
lastIndexOf(e)
contains(e)
sort(comparator) vs Collections.sort(…)
size()
isEmpty()
clear()

Set

add(e)
contains(e)
remove(e)

3.Deque

队列(Queue)

offer(e)
poll()
peek()

栈(Deque)

push(e)
pop()
peek()

4.Map

get(key)
put(key, value)
遍历 key\values\kvs

5.Arrays 关于数组的常见操作
6.Collections 下面关于 Collection 的常见操作

6.HashMap、HashSet 的特殊之处:hashCode() + equals()

作为 HashMap 的 key 或者 HashSet 的元素,必须正确地重写 hashCode 和 equals 方法

equals 的正确性:
自反性: a.equals(a) == true
传递性:a.equals(b) == true && b.equals© == true
-> a.equals© == true
恒定性: 固定的两个元素每次调用equals方法结果是唯一的

hashCode 的正确性:
恒定性: 满足 a.equals(b) == true -> a.hashCode() == b.hashCode()

一般不用自己去写,都是让 IDEA 自动生成(以后由 lombok 插件生成)

三.代码编写

1.面向对象

顺序表对象(MyArrayList)
链表对象(MyLinkedList)
栈对象(MyStack)
队列对象(MyQueue)
优先级对象(MyPriorityQueue)
搜索树对象(MyBSTree)
哈希表对象(MyHashMap)

  1. 封装的特性:区分内部、外部
  2. 每种对象都有其对应的正确地一致性

2.链表 —— 引用/对象的操作

如何理解引用!
a == null 的理解
a == b 的理解
a != b 的理解
a = b 的理解
在链表中主要表现的:
p = q
p.next = q;
p = q.next;
p.next = q.next;

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值