《labuladong算法小抄》压缩整理-第零章:框架结构之数据结构
一、存储方式
1、根本存储方式:数组(顺序存储)、链表(链式存储)
2、【队列】、【栈】
数组实现:要处理扩容缩容问题;
链表实:没有扩容缩容问题,但是需要更多的内存空间。
3、【图】
邻接表:本质,链表。优点,节省空间。缺点,很多操作效率比不上邻接矩阵。
邻接矩阵:本质,二维数组。优点,判断连通性迅速,可以实现用矩阵运算。缺点,如果图稀疏则浪费空间。
4、【散列表】:用散列函数把键映射到一个大数组
拉链法:特性,链表。优点,操作简单。缺点,需要额外的指针。
线性探查法:特性,数组。优点,不需要额外指针的存储空间。缺点,操作稍微复杂。
5、【树】
堆:本质,数组。不需要额外指针,操作简单。
树:本质,链表。衍生设计:二叉搜索树、AVL树、红黑树、区间树、B树等,对应不同问题。
6、总结
数组:特点:连续存储、随机访问、节省存储空间、内存一次分配。扩容操作:分配空间,复制数据,O(N)。插入与删除:搬移后面所有数据,O(N)。
链表:特点:不连续存储。
二、基本操作
1、基本操作:遍历+访问,具体一点—增删查改。
2、不同数据结构存在价值:应对不同场景,尽可能高效。
3、遍历+访问的两种形式:线性(for + while迭代)和非线性(递归)。
4、常用框架
4.1 数组遍历框架(线性迭代):
void traverse(int[] arr){
for(int i = 0;i < arr.length;++i){
//迭代访问 arr[i]
}
}
4.2 链表遍历框架(迭代和递归):
/*基本的单链表节点*/
class ListNode{
int val;
ListNode next;
}
void traverse(ListNode head){
for(ListNode p = head;p != null;p = p.next){
//迭代访问 p.val
}
}
void traverse(ListNode head){
//递归访问 head.val
traverse(head.next)
}
4.3 二叉树遍历框架(非线性递归):
/*基本的二叉树节点*/
class TreeNode{
int val;
TreeNode left,right;
}
void traverse(TreeNode root){
//前序遍历
traverse(root.left)
//中序遍历
traverse(root.right)
//后序遍历
}
4.4 N叉树遍历框架(非线性递归):
/*基本的N叉树节点*/
class TreeNode{
int val;
TreeNode[] children;
}
void traverse(TreeNode root){
for(TreeNode child:root.children)
traverse(child);
}