一、数据的存储结构
1、顺序存储
-
定义:
数据元素存储在连续的内存空间中,用元素的相对位置来表示关系。
-
优点:
1、支持随机访问
2、修改、访问效率高
3、存储空间连续,不易产生内存碎片
-
缺点:
1、内存空间利用率低
2、插入删除不方便
2、链式存储
-
定义:
数据存储在彼此独立的内存空间中,通过指针域存储元素地址表示元素间的关系。
-
优点:
1、空间利用率高
2、插入删除方便,适合频繁的增删数据
-
缺点:
1、不支持随机访问
2、内存不连续容易产生内存碎片
二、逻辑结构
1、集合
元素之间同属于一个集合但是没有任何关系
2、线性结构
数据元素之间存在一对一的关系
1)线性表
-
顺序表(数组)Array
- 数据项:存储元素内存首地址、表的容量、元素的数量
- 注意:不要越界,时刻保持元素连续性
-
链式表 List
- 节点的数据项:数据域、指针域(指向下一个节点)
两种类型:带头节点和不带头节点
-
不带头节点:
第一个节点的数据域存储的是有效的数据;
添加、删除时有可能会改变第一个节点的指针指向,
参数需要传递二级指针,而且删除第一个节点时,还需要额外处理。
-
带头节点:
第一个节点不使用,仅仅只是用来指向第一个数据域有效的节点
注意:操作需要从第二个节点(也就是第一个有效数据节点)开始
注意:下标从第一个有效数据开始
-
封装链表的几种类型
-
单链表(由于尾添加效率低,加入尾指针)
节点数据项:数据域、指针域
单链表数据项:头指针、尾指针、节点数量
-
双向链表(提高链表删除效率)
节点数据项:前驱指针、后驱指针、数据域
双链表数据项:头节点、节点数量
-
循环链表(它的好处是可以通过任意节点遍历整个链表)
链表的最后一个节点的next不再指向NULL,而是指向头节点,这种链表叫做单向循环链表,简称循环链表
-
静态链表(牺牲了随机访问的功能,主要是提供给没有指针的编程语言实现单链表的一种方法)
节点数据域:数据域、游标
静态链表的节点存储在连续的内存中,通过游标访问下一个节点
这种链表在插入删除时只需要修改游标的值,而不用重新申请、释放内存从而达到链式表结构的效果
-
Linux内核链表
既然链表不能包含万物,那就让万物包含链表
-
通用链表
节点数据项:指针域、void* ptr
链表数据项:头节点指针、节点数量
运算:常规功能 + 回调函数
-
关于链式表的常见题目:
1、反转链表、单链表逆序
https://www.nowcoder.com/practice/75e878df47f24fdc9dc3e400ec6058ca2、每K个一组反转链表
https://www.nowcoder.com/practice/a632ec91a4524773b8af8694a51109e7
3、从尾到头打印链表
https://www.nowcoder.com/practice/d0267f7f55b3412ba93bd35cfa8e80354、找出链表倒数第n个节点
https://www.nowcoder.com/practice/886370fe658f41b498d40fb34ae76ff9
5、判断链表中是否有环及求出环形链表的入口
https://www.nowcoder.com/practice/253d2c59ec3e4bc68da16833f79a38e46、合并两个有序链表,合并后依旧有序
https://www.nowcoder.com/practice/d8b6b4358f774294a89de2a6ac4d93377、删除链表中重复节点
https://www.nowcoder.com/practice/fc533c45b73a41b0b44ccba763f866ef8、判断两个链表是否是Y型链表
2)功能受限的线性表
-
栈:只有一个进出的出入口表结构,先进后出FILO
-
顺序栈
数据项:存储元素的内存首地址、栈的容量、栈顶的位置
运算:创建、销毁、入栈、出栈、栈满、栈空、栈顶
顺序栈有四种:
top初值:0 入栈 top++ 空增栈
top初值:-1 top++ 入栈 满增栈
top初值:cal-1 出栈 top-- 空减栈
top初值:cal top-- 出栈 满减栈
-
链式栈
数据项:栈顶、节点数量
-
栈的应用:
1、函数的调用(栈内存)
2、表达式的解析
3、生产者和消费者的模型(栈作为仓库)
-
-
队列:一个端口进一个端口出,先进先出FIFO
-
顺序队列
数据项:存储元素内存首地址、容量、队头、队尾
运算:创建、销毁、入队、出队、队空、队满、队头、队尾、元素个数
顺序队列是由一位数组+队头位置front+队尾rear组成,入队时rear+1,出队时front+1,为了能让队列反复使用,我们要把一位数组想象成一个环,因此rear\front加1后要用队列的容量求余:
rear = (rear+1)%cal;
front = (front+1)%cal;
如何判断队空: front == rear
如何判断队满: front == (rear+1)%cal
1、代价是空一个位置不能使用
2、添加一个数据项标记队列是空或者满(元素个数)
如何计算元素的数量: (rear-front+cal)%cal
-
链式队列
数据项:队头指针、队尾指针、节点数量
-
队列的应用:
1、树的层序遍历
2、图的广度优先遍历
3、消息队列
4、封装线程池、数据池
-
关于栈和队列的题目:
1、用两个栈实现队列
https://www.nowcoder.com/practice/54275ddae22f475981afa2244dd448c62、按之字型打印二叉树(栈)
https://www.nowcoder.com/practice/91b69814117f4e8097390d107d2efbe03、从上往下打印二叉树、二叉树的层序遍历(队列)
https://www.nowcoder.com/practice/7fe2212963db4790b57431d9ed2597014、判断序列A是否可能为序列B的栈弹出顺序 https://www.nowcoder.com/practice/d77d11405cc7470d82554cb392585106
3、非线性结构
1)树
数据之间存在一对多的关系
-
相关名词概念
节点的度:该节点子树的数量
节点的层次:根节点层次为1,它的孩子层次为2,以此类推
树的深度:树的最大层次数
叶子节点:节点的度为0的节点
双亲和孩子、兄弟、祖先、子孙、堂兄弟
森林:由n个互不相交的数的集合
-
数的存储
双亲表示法(顺序)
孩子表示法(顺序、顺序+链式)
兄弟表示法
-
二叉树
-
特殊二叉树
满二叉树、完全二叉树
有序二叉树(搜索二叉树):左子树的数据小于根,右子树大于等于根;有序二叉树的中序遍历,一定是从小到大,所以有序二叉树也是一种排序算法,它的查找天然又是二分查找,所以经常考。
线索二叉树:在有n个节点的链式二叉树中,必定有n+1个空指针域;链式二叉树中有很多的空指针,可以让这些指针的一部分指向下一个节点,这样遍历树时可以不用递归而是使用循环,提高树的遍历速度
堆:是一种完全二叉树,不适合链式存储;大顶堆(大根堆):根节点比左右子树大;小顶堆(小根堆):根节点比左右子树小;使用堆可以实现优先队列
平衡二叉树:前提是有序的二叉树,它的左右子树的高度差不超过1,而且它的所有子树也要满足这个条件。
-
二叉树5性质
1、满二叉树:每层的节点数都是2^(i-1)
2、深度为h的二叉树中至多含有2^h-1个节点
3、若在任意一棵二叉树中,有n0个叶子节点,有n2个度为2的节点,则必有n0=n2+1
4、具有n个节点的完全二叉树深为log2^n+1
5、若对一棵有n个节点的完全二叉树进行顺序编号(1≤i≤n),那么,对于编号为i(i≥1)的节点:
当i=1时,该节点为根,它无双亲节点 。
当i>1时,该节点的双亲节点的编号为i/2 。
若2i≤n,则有编号为2i的左节点,否则没有左节点 。
若2i+1≤n,则有编号为2i+1的右节点,否则没有右节点 。
-
二叉树的操作:构建、销毁、插入、遍历、高度、密度、删除、求根、求左、求右
-
二叉树的遍历:前序、中序、后序、层序
-
二叉树的存储:
顺序:必须按照完全二叉树的格式存储节点,空位置使用特殊数据替代
数据项:存储节点的首地址、容量
注意:通过编号的关系来找到双亲(i/2)和孩子节点(2i\2i-1)
链式:由一个个节点组成,通过左右子树指针指向自己左右子树
数据项:数据、左子树指针、右子树指针
-
关于二叉树的常考题目:
4、给定一个二叉树和一个值\ sum sum,请找出所有的根节点到叶子节点的节点值之和等于\ sum sum 的路径
5、把一棵二叉树按照之字型打印
6、判断该二叉树是否是平衡二叉树9、给出一个升序排序的数组,将其转化为平衡二叉搜索树(BST)
11、求给定二叉树的最大深度
12、给定一棵二叉搜索树,请找出其中的第k小的TreeNode结点。
13、输入两棵二叉树A,B,判断B是不是A的子结构。