栈
- 栈是逻辑结构中线性结构中的一种,遵循先入后出的原则。
- 最先入栈的元素称为栈底元素,最后入栈的元素称为栈顶元素。
- 栈属于逻辑结构,需要依托物理结构而存在。通过数组和链表,都可以实现一个栈。
- 栈基本操作是入栈和出栈,只从某一侧进行入或出,不论是链表实现还是数组实现,时间复杂度都是O(1)。
//入栈
var arr=[1,2,3,4]
arr.push(5)
//出栈
var arr=[1,2,3,4]
arr.pop()
队列
- 队列也是逻辑结构中线性结构中的一种,遵循先入先出的原则。
- 队列就像排队一样,有队头(出口)和队尾(入口)。
- 队列基本操作是入队和出队,只从某一侧进行入或出,不论是链表实现还是数组实现,时间复杂度都是O(1)。
//入队
var arr=[1,2,3,4]
arr.push(5)
//出队
var arr=[1,2,3,4]
arr.shift()
栈和队列的应用
先入后出的栈和先入先出的队列在特点上决定了它们的不同应用场景。
- 栈可用于历史数据的获取,是一种回溯,也能用于将递归操作转换成基于栈的非递归。
- 队列就像它的名字一样,用于排队。浏览器的事件循环中的任务队列就是按照事件注册顺序进行入队和出队。
树
之前提到的队列,栈都属于线性的逻辑结构,而实际上,还存在非线性的逻辑结构,树就是其中一种。以学校为例,下分各个院系,每个院系会有各个专业,每个专业会有各个班级,班级还会细分宿舍,每个宿舍是一个个学生,这就是一棵树的结构。
常见术语
- 根节点:树根,如下图A节点
- 叶子节点:没有孩子的末端节点,如下图的#
- 子树:符合树结构的子集,如下图以B,C为根的两棵左右子树
- 父节点:当前节点的上一级节点,对B来说,A就是其父节点
- 兄弟节点:同父节点且同级节点,如下图的B,C
- 孩子节点:当前节点的子节点,对B来说,D是孩子其节点
- 树的高度或深度:树的最大层级,下图的树高度为4
二叉树
二叉树是树的一种特殊形式,也是树结构中最常用的一种。它的结构就和名字那样,每个节点最多两个分支,但也可能一个,甚至没有。二叉树的左右节点被称为左孩子和右孩子,顺序固定,上图就是一个二叉树的结构。当然,二叉树还有满二叉树和完全二叉树的结构,前者要求每个子节点的左右孩子都存在,后者只要求最后一个节点之前的都齐全。
二叉树的实现
二叉树是逻辑结构,需要依托物理结构存在,既可以用链表实现,也可以用数组实现。
链表实现
- 每个节点分三个域,存储数据data域,分别指向左右孩子的left指针域和right域
数组实现
- 数组存储会按照层级顺序安放树的各个节点,若某个孩子节点不存在,则数组在对应位置也空出来。
- 以上图二叉树为例
- 这种存储方式很方便查找,假如当前父节点索引为index,那其左节点索引就是2index+1,右节点索引就是2index+2(不考虑下标越界)
- 以A节点为例,索引为0,左节点索引为2x0+1=1,arr[1]=“B”,右节点索引为2x0+2,arr[2]=“C”
var arr=[];
arr[0]="A";
arr[1]="B";
arr[2]="C";
arr[3]="D";
arr[4]="E";
arr[5]="F";
arr[9]="G";
arr[12]="H";
二叉搜索树/二叉查找树/二叉排序树
二叉搜索树,二叉查找树,二叉排序树这三个是同一个东西,它建立在二叉树基础上,符合二叉树特点,也有自身特点:·对于非空的左子树(也是二叉查找树)而言,其所有节点的值均小于根节点的值。对于非空的右子树(也是二叉查找树)而言,其所有节点的值均大于根节点的值。
- 这种存储方式既便于查找又能保持相对顺序,三个名字也是这样来的
- 对于一个分布均匀的二叉搜索树,其时间复杂度为O(logn),n为节点数
- 也存在分布不均匀的二叉搜索树,比如左子树10个节点,右子树一个节点,严重失衡
- 严重失衡的二叉搜索树效率会大打折扣,从O(logn)退化 到O(n)也是很可能的
- 所以也有一些常用的平衡手段(红黑树/AVL等)
二叉树的遍历
基于线性结构的遍历自然简单,但对于非线性结构的遍历,需要转换成线性的方式,曲线救国。常见的遍历方式有两大类共四种,深度优先遍历(前序/中序/后序),广度优先遍历:层序
前序遍历:根,左,右
- 7,5,2,6,9,11
中序遍历:左,根,右
- 2,5,6,7,9,11
后序遍历:左,右,根
- 2,6,5,11,9,7
层序遍历
- 7,5,9,2,6,11