(一)需求
记得在很久之前,学习的《软件开发》书中有记载到,软件开发=数据结构+算法。 一直以来,做业务多点,对于数据结构和算法的优化有些忽视。今儿开始学习
(二)数据结构
1、常见的数据结构
- 队列
- 列表
- 栈
- 堆
- 树
- 图
(1)线性表
线性表是最常用且最简单的一种数据结构,它是n个数据元素的有限序列。
实现线性表的方式一般有两种,一种是使用数组存储线性表的元素,即用一组连续的存储单元依次存储线性表的数据元素。另一种是使用链表存储线性表的元素,即用一组任意的存储单元存储线性表的数据元素。
(2)栈与队列
栈和队列也是比较常见的数据结构,它们是比较特殊的线性表,因为对于栈来说,访问、插入和删除元素只能在栈顶进行,对于队列来说,元素只能从队列尾插入,从队列头访问和删除。
(3)栈
栈是限制插入和删除只能在一个位置上进行的表,该位置是表的末端,叫作栈顶,对栈的基本操作有push(进栈)和pop(出栈),前者相当于插入,后者相当于删除最后一个元素。栈有时又叫作LIFO(Last In First Out)表,即后进先出。
应用场景:函数调用栈、表达式求值、括号匹配。
(4)队列
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。
应用场景:
- 阻塞队列:生产者和消费者模型
- 并发队列:利用CAS实现高效的并发队列。
- 树与二叉树
- 树型结构是一类非常重要的非线性数据结构,其中以树和二叉树最为常用。
(5)树
树 是由n(n>=1)个有限节点组成一个具有层次关系的集合。它具有以下特点:每个节点有零个或多个子节点;没有父节点的节点称为 根 节点;每一个非根节点有且只有一个 父节点 。
二叉树 二叉树的每个结点至多只有2棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒。
二叉树的第i层至多有2^(i-1)个结点;深度为k的二叉树至多有2^k-1个结点。
一棵深度为k,且有2^k-1个节点的二叉树称之为 满二叉树。
深度为k,有n个节点的二叉树,当且仅当其每一个节点都与深度为k的满二叉树中,序号为1至n的节点对应时,称之为完全二叉树。
2、数据结构的存储方式
数据结构的存储方式只有两种:数组(顺序存储)和链表(链式存储)。
(1)数组(顺序结构)
数组由于是紧凑连续存储,可以随机访问,通过索引快速找到对应元素,而且相对节约存储空间。但正因为连续存储,内存空间必须一次性分配够,所以说数组如果要扩容,需要重新分配一块更大的空间,再把数据全部复制过去,时间复杂度 O(N);而且你如果想在数组中间进行插入和删除,每次必须搬移后面的所有数据以保持连续,时间复杂度 O(N)。
(2)链表(链式存储)
链表因为元素不连续,而是靠指针指向下一个元素的位置,所以不存在数组的扩容问题;如果知道某一元素的前驱和后驱,操作指针即可删除该元素或者插入新元素,时间复杂度 O(1)。但是正因为存储空间不连续,你无法根据一个索引算出对应元素的地址,所以不能随机访问;而且由于每个元素必须存储指向前后元素位置的指针,会消耗相对更多的储存空间。
3、数据结构的基本操作
对于任何数据结构,基本操作是遍历和访问——增删改查。
数据结构存在的目的是在不同场景下,尽可能高效的实现增删改查。
1、基本操作
- 遍历
- 访问
2、实现形式
- 线性—— For/while
void traverse(int[] arr) {
for (int i = 0; i < arr.length; i++) {
// 迭代访问 arr[i]
}
}
- 非线性结构使用——递归
/* 基本的单链表节点 */
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)
}
以上
参考链接
Github
写在最后的话
学习路上,常常会懈怠
《有想学技术需要监督的同学嘛~》 mp.weixin.qq.com/s/FyuddlwRY…