基础数据结构(零) -- “零基础”数据结构入门
教材常见分类
- 数组,Array;
- 链表,Linked List;
- 队列,Queue;
- 栈,Stack;
- 散列表,Hash;
- 树,Tree;
- 堆,Heap;
- 图,Graph;
面试实际考察
- 常考
- Array
- String
- Linked List
- Tree(BT, BST)
- Stack
- Queue
- PriorityQueue(Heap)
- HashMap
- HashSet
- Trie
- 少考
- Disjoint-Set(Union Find)
- Deque
- Graph
- 不考,一题多解更快
- TreeMap
- TreeSet
- Segment Tree(zkw Tree)
- Binary Index Tree(Fenwick Tree)
Array 数组
定义:在物理存储单元中连续存储多个元素,
通过数组的下标访问数组元素,数组的下标从0开始
// 初始化
int[] nums = new int[10];
// 赋值,时间复杂度O(1)
nums[0] = 35;
// 访问,时间复杂度O(1)
nums[0];
- 优点
- 按照索引查询元素速度快;
- 按照索引遍历数组方便;
- 缺点
- 数组大小初始化后固定,无法扩容;
- 数组只能存储同类型的元素;
- 插入、删除操作慢,因为要移动之后的元素;
- 适用场景
- 存储空间要求不大;
- 频繁查询;
- 插入、删除操作少;
String 字符串
定义:字符的序列
在Java中定义了String类,用来创建字符串对象。
- Java8及之前底层是char[];
- Java9及之后底层是byte[];
String属于非基本类型
-
基本类型:boolean,int,char,double,long,byte,short,float
-
非基本类型:Arrays 数组,Classes 类,Interfaces 接口
String str1 = "古城666";
String str2 = new String("古城666");
String str3 = new String(new char[]{'古', '城', '6', '6', '6'});
str2.substring(0, 2);
str2.charAt(2);
str2.compareTo(str3);
Linked List 链表
定义:在物理存储单元中非连续、非顺序存储,
每个结点包含值域和指针域,指针指向下一个结点,
结点关联,形成链表,通过指针实现元素的逻辑顺序访问
根据指针的指向
- 单链表;
- 双向链表;
- 循环链表;
public class ListNode {
int val;
ListNode next;
public ListNode(int val) {
this.val = val;
}
}
// 初始化
ListNode head = new ListNode(0);
// 赋值,时间复杂度O(1)
head.next = new ListNode(1);
// 取值,时间复杂度O(1)
head.val;
- 优点
- 不需要初始化容量,可以任意添加/删除元素;
- 添加/删除操作快,只需要操作相邻两个结点的指针域;
- 缺点
- 包含指针域,占用空间较大;
- 查找元素,需要遍历链表,时间复杂度为O(N)
- 适用场景
- 数据量较小;
- 频繁添加/删除;
- 查询操作少;
Tree 树
由n(n>=1)个有限节点组成一个具有层次关系的集合。像一棵倒挂的树,根朝上,叶子朝下。
- 每个节点有零个或多个子节点;
- 没有子节点的节点称为叶子节点;
- 没有父节点的节点称为根节点;
- 每一个非根节点有且只有一个父节点;
- 除了根节点外,每个子节点可以分为多个不相交的子树;(子树不相交)——》没有环路
Binary Tree 二叉树是树的一种,具有以下特点
- 每个节点最多有两颗子树;
- 左子树和右子树是有顺序的;
Binary Search Tree 二叉搜索树是二叉树的一种,特点为
left < parent < right
;
Trie 前缀树
Trie 前缀树/字典树,具有以下特点
- 多叉树树结构
- 根节点不包含字符,其他节点仅包含一个字符;
- 任意节点的所有子节点锁包含的字符都不相同;
- 从根节点到某一节点路径上所经过的字符连接起来,即为该节点对应的字符串;
基础方法 | 时间复杂度 |
---|---|
addWord | word.length |
searchWord | word.length |
searchPrefix | word.length |
详解请见Trie专题PPT
Stack 栈
Stack 栈是一种特殊的线性表
- 仅允许在线性表的一端操作,栈顶允许操作,栈底不允许操作
- 入栈:从栈顶放入元素
- 出栈:从栈顶取出元素
- 后进先出,应用于实现递归功能的场景,例如DFS 深度优先遍历
Deque VS Stack
- 非并发场景下Stack synchronized的开销——》用接口Deque和实现类ArrayDeque替代Stack;
- 接口Deque为双向队列,可以打破栈只在栈顶操作的原则——》基于Deque封装,只保留栈的操作;
更复杂的单调栈,详解请见基础算法(六) -- 单调栈PPT
Queue 队列
Queue 队列是一种特殊的线性表
- 在一端添加元素,在另一端取出元素;
- 入队:在一端添加元素;
- 出队:在另一端取出元素;
- 先进先出,例如BFS 宽度优先遍历;
Return Special Value | Throw Exception | |
---|---|---|
Insert | offer(e) | add(e) |
Remove | poll() | remove() |
Examine | peek() | element() |
Deque 双端队列
Double-ended queue,两端都允许进出
ArrayDeque VS LinkedList
- LinkedList 非线性存储,CPU Cache命中率低;
- ArrayDeque随机访问为O(1)(相较LinkedList),两端进出队列都是O(1)(相较ArrayList);
- ArrayDeque不支持null;
- LinkedList在遍历中每次O(1)移除元素;
更难的单调队列,详见基础算法(七) -- 单调队列
Heap 堆
分为两种:最大堆(大根堆)和最小堆(小根堆),差别在于节点的排序方式
- 最大堆:父节点的值比每一个子节点的值都要大;
- 最小堆:父节点的值比每一个子节点的值都要小;
- 又名PriorityQueue,可以用数组实现,Java PriorityQueue默认最小堆;
考察点:Heap应用和Array手动实现Heap
更难的heap sort,详见基础数据结构(三) -- Heap
Map 散列表
又名HashMap 哈希表
- 根据关键码(key)和值 (value) 直接进行访问的数据结构;
- 通过key和value来映射到集合中的一个位置,其中通过key找到桶的位置,被称为散列函数/哈希函数f(key);
- 基础逻辑:数组 + 链表
- f(key)计算出一个整数;
- 整数对数组长度进行取余,取余结果为数组的下标(桶的位置);
- 在同一桶里,继续查找元素;
- key散列冲突
- 作为挂链表 Separate Chaining
- 开放地址法 Open Addressing
- 查找比纯链表快,插入删除比纯数组快;
Set 集合
和数学上的笛卡尔集合逻辑类似
- 不允许出现重复元素;
- 不保证集合中元素的顺序;
- HashSet允许null元素,底层基于HashMap,key为元素,value为一个固定元素;
TreeMap
-
仅Java,基于红黑树实现;
-
key按值排序,LinkedHashMap维护的值的操作顺序;;
-
时间复杂度,O(logn)
- put(key, value)
- lowerKey() <
- floorKey() <=
- higherKey() >
- ceilingKey() >=
面试不做要求,笔试解题可加速
TreeSet
与Set相比
- 元素按值排序;
- 底层基于TreeMap;
Disjoint-Set 并查集
并查集是一种树形的数据结构,用于处理不交集的合并(union)以及查询(find)问题。
union(x, y)和find(x)时间复杂度为logN
优化,详见基础数据结构(二) -- 并查集
- path compression
- union by rank
Graph 图
数据之间的关系
- 一对一:线性表;
- 一对多:树;
- 多对多:图;
无向 VS 有向
有环 VS 无环
图的表示有两种主要方式:
-
邻接表
-
邻接矩阵
图类型查考不多,详见图的基础算法系列
- 基本BFS,DFS,拓扑排序;
- 深入学习可考虑最短路径,最小生成树等;