1.数据结构的概念
- 数据:是描述客观事物的数、字符以及所有能输入到计算机中并被计算机程序处理的符号的集合。
- 数据元素:是数据的基本单位
- 数据项:是具有独立含义的数据最小单位,也称为域(如一个班中的学号、姓名等)
- 数据对象:是具有性质相同的有限个数据元素的集合,是数据的一个子集
- 数据结构=数据对象+结构
1.1数据结构的逻辑结构
- 线性结构:若结构是非空的,则有且只有一个开始元素和一个终端元素;最多只有一个前驱元素和一个后继元素
- 树形结构:若结构非空,则有且只有一个开始元素(也称根结点),但有多个终端元素。每个元素有0个或多个后继元素,除开始元素外,有且只有一个前驱元素
- 图形结构:若结构是非空的,每个元素有多个前驱元素和多个后继元素
1.2数据的逻辑结构
- 集合
- 线性结构
- 树形结构
- 图状结构或网状结构
1.3数据的存储结构
- 顺序存储结构:所有元素放在一片连续的地址连续的存储单元中逻辑上相邻的元素在物理位置上也是相邻的,所以不需要额外空间表示元素之间的逻辑关系。
- 链式存储结构:数据元素存放在任意的存储单元中,这组存储单元可以是连续的也可以是不连续的
- 索引存储
- 散列存储
1.4算法的5个特性
- 1)有穷性
- 2)可行性
- 3)确定性
- 4)输入性
- 5) 输出性
1.5算法的设计目标
- 1.正确性
- 2.可使用性
- 3.可读性
- 4.健壮性
- 5.高时间性能低存储量需求
2.线性表(顺序表和链表)
2.1线性表的定义
- 1.所有数据元素类型相同
- 2.线性表是有限个元素构成的
- 3.线性表中数据元素与存储的位置相关,每个数据元素有唯一的序号
2.1.1顺序表的插入
- 线性表中需要移动data[i..n-1]的元素,移动次数为(n-1)-i+1=n-i
- 插入的时间复杂度为o(n)
2.1.2顺序表的删除
- 线性表中需要移动data[i+1..n-1]的元素,移动次数为(n-1)-(i+1)+1=n-i-1
- 删除的时间复杂度为o(n)
3.1链式存储结构--链表(动态分布内存,不连续)
- 结点:包括元素值和后继结点的地址
3.1.1链表分为两个
- 1.单链表(结点中有一个指针,指向其后继结点)
- 2.双链表(结点中有两个指针,一个指向前驱结点一个指向后继结点)
3.1.2链表基本操作
- 1.插入操作
- 2.删除操作
- 3.头插法
- 4.尾插法
3.1.3循环链表
- 尾结点满足的条件是p.next==head而不是p.next==null
4.1总结
- 顺序表具有随机存取特性,给定序号查找对应的元素值的时间为O(1),而链表不具有随机存取特性,只能顺序访问,给定序号查找对应的元素值的时间为O(n)。
- 但如果是插入或删除操作时顺序表的时间复杂度是O(n),链表是O(1)
- 线性表包括顺序结构和链式结构,顺序结构逻辑相邻且物理位置相邻,链式结构逻辑相邻,物理不一定相邻
5.串
5.1串的基本概念
- 1串是由一个或多个字符组成的有限序列
- 串中所包含的字符个数是串的长度,当n=0是称为空串
- 一个串中任意连续的字符组成的字序列成为:字串
- 包含字串的串称为:主串
- 若两个串的长度相等且对应的字符相等则称两个:串相等
5.2子串的个数计算方法
- (n+1)*n/2+1(n为串的长度)
- 求串的排列方式有多少种(n*(n-1)*(n-2)*(n-3)*1),如果有重复的则除以i(i个相同的元素)
- "=="可以用于基本数据类型的比较,当用于引用对象比较时是判断引用是否指向堆内存的同一块地址
- equals方法的作用是用于判断两个变量是否是同一个对象的引用,即堆中的内容是否相同,返回值为布尔类型
5.3串的模式匹配
- 设有两个串s串和t串,在s串中查找与字串t相等的字串
- 称s为目标串,t为模式串
5.3.1著名的算法
- 1)BF
- 2)KMP
- 在KMP算法中:定义其中最长的相同前、后缀为M串,则next[j]就是该M串的长度
- 在KMP算法中求next数组的时间复杂度为O(m)
- KMP算法总的平均时间复杂度为O(n+m)
例子:模式串t="abcac"
j | t[j] | t[j]前面的子串 | 前缀 | 后缀 | M串 | next[j] |
0 | a | -1 | ||||
1 | b | a | 0 | |||
2 | c | ab | a | b | 0 | |
3 | a | abc | a,ab | c,bc | 0 | |
4 | c | abca | a,ab,abc | a,ca,bca | a | 1 |
6.数组(静态分布内存,且连续)和稀疏矩阵
- int [][]number={}注:前可空后不可空
- 1.1d(d≥1)维数组中的非边界元素具有d个前驱元素和d个后继元素
- 1.2每个有意义的下标都存在一个与其相对应的数组元素值
- 1.3其起始地址为第一个元素A0的地址,则任意元素Ai的存储地址为LOC(Ai)=LOC(A0)+i*k 注:K表示每个元素占多少个单元格
6.1数组的计算
例子:以m行n列的二维数组Am×n=(ai,j)为例讨论(二维数组也称为矩阵)
- 按行优先存储:LOC(Ai,j)=LOC(Ao,o)+(i*n+j)*k 注:n表示一行有几个元素
- 按列优先存储:LOC(ai,j)=LOC(a0,0) + (j×m + i)×k
6.2特殊矩阵的压缩存储
- 若一个n阶方阵A的元素满足ai,j=aj,i(0≤i,j≤n-1),则称其为n阶对称矩阵
- 上三角矩阵,下三角矩阵
- 对角矩阵(主要的是三对角矩阵)
7.栈(编译器自动分配释放)
7.1栈的定义
- 是一种只能在同一端进行插入和删除操作的线性表
- 表中允许插入和删除操作的一端称为栈顶(top),另一端成为栈底(bottom)
- 栈的插入操作称为进栈(push),删除操作称为出栈(pop)
7.2栈的主要特点
- 后进先出(先进后出),每次进栈的元素都称为栈顶元素,每次出栈的元素只能是当前的栈顶元素
7.3顺序栈
- 初始时置栈顶空的条件为top==-1
- 元素进栈时先将top指针增1,再将元素e放入到栈顶指针处。出栈则与此相反
例题: 若一个栈用数组data[1..n]存储,初始栈顶指针top为n+1,则以下元素x进栈的正确操作是( C )
A.top++; data[top]=x; B.data[top]=x; top++;
C.top--; data[top]=x; D.data[top]=x; top--;
8.队列
8.1队列的概念
- 队列是一种只能在不同端进行插入和删除操作的线性表
- 进行插入的一端叫做队尾(rear),进行删除的一端叫做对头(front)
8.2队列的特点
- 先进先出,每次进去的元素都作为新队尾元素,每次出队的都是队头元素
- 初始化时置front和rear都为-1,元素进队rear增1,元素出队front增1
- 进队:先将尾指针增1,然后再将e放在该位置
- 出队:先将头指针增1,然后再将e放在该位置
- 队列的空 rear==front
- 在循环队列中判断队满的条件:(rear+1)%Maxsize==front
- 元素进队:(rear+1)%Maxsize==rear
- 元素出队:(front+1)%Maxsize==front
9.树和二叉树
9.1树
9.1.1二叉树的空链
- 在二叉树的链表中值为空的链域个数为:n+1;
- 链为非空的个数为:n-1;其中n表示树的结点
9.1.2树的定义
- 树是由n个结点组成的有限集合
- 如果n=0,那就是一颗空树,是特例
- 树是一种非线性关系,可以有多个或0个后继结点,但只有一个前驱结点(根结点除外)
9.1.3树的逻辑结构表示方法
- 1.树形表示法
- 2.文氏图表示法
- 3.括号表示法
- 4.凹入表示法
9.1.4度的概念
- 结点的度:每个结点拥有的后继结点或者子树的数称为度
- 树的度:树中所有结点的度的最大值称之为树的度
9.1.5树的三种遍历形式
- 先根遍历
- 后根遍历
- 层次遍历
9.2二叉树(最佳二叉树是:哈夫曼树)
9.2.1概念
- 二分树由一个根结点和两个不相交的左子树和右子树的二叉树组成
9.3遍历的概念
9.3.1.先序遍历
- 1.1:访问根结点
- 1.2:访问左子树
- 1.3:访问右子树
9.3.2.中序遍历
- 2.1:访问左子树
- 2.2:访问根结点
- 2.3:访问右子树
9.3.3.后序遍历
- 3.1:访问左子树
- 3.2:访问右子树
- 3.3:访问根结点
9.3.4知道一颗二叉树的条件
- 中序+先序
- 中序+后序
- 层次+中序
9.3.5二叉树转换为深林
- 1.给右孩子去线(是右孩子的右孩子)
- 2.将二叉树转化为树(将所有的右孩子结点和根结点相连接)
9.3.6深林转化为二叉树
- 1.兄弟之间加线
- 2.除了第一个孩子右孩子的线去掉
- 3.后一颗树的根结点作为前一个树的右孩子
9.4满二叉树
9.4.1满二叉树的概念
- 如果所有分支结点都有左孩子结点和右孩子结点,并且叶子结点都集中在二叉树的最下一层,这样的二叉树称为满二叉树
- 只有度为0或度为2的结点
9.5完全二叉树概念
- 若二叉树中最多只有最下面两层的结点的度数可以小于2,并且最下面的一层叶子结点都依次排列在该层最左边的位置上,称为完全二叉树
9.6二叉排序树(二叉搜索树)
- 二叉排序树首先要求该树是一棵二叉树,之后要求在这棵树中,所有子树的根节点的左子树上的节点值或者说是权重要均小于(或大于)根节点,而右子树上的所有节点值或者说是权重均大于(或小于)根节点。
9.7平衡二叉树
- 在二叉排序树的前提下,左右高度差小于等于1
9.8哈夫曼树(WPL所有叶子结点的权值)
- 从树根结点到某个结点的路径长度与该结点的权的乘积称为结点的带权路径长度
- 方法:从森林中选取两个结点最小的权值作为左孩子和右孩子结点,构成一颗新的二叉树,以此类推
- 对于拥有n个叶子结点的二叉树有2n-1个结点
哈夫曼编码:
注意:规定哈夫曼的左孩子为0,右孩子为1
提醒:在一组哈夫曼编码中,任意字符的哈夫曼编码不可能是另一字符哈夫曼编码的前缀
例子:5个字符有如下4种编码方案,不是前缀编码的是(D )。
A.01,0000,0001,001,1
B.011,000,001,010,1
C.000,001,010,011,100
D.0,100,110,1110,1100
例子总结
- 在含n个结点的二叉树中,所有结点的度小于等于2,通常用n0表示叶子结点个数,n1表示单分支结点个数,n2表示双分支结点个数
- 总结点个数:n=no+n1+n2;
10.图
10.1生成树概念
- 生成树:是一个极小联通子图,包含树的全部顶点(n),但只包含构成一棵树的(n-1)条边,如果在生成树上加上一条边,必定会构成一个环
10.1.2.有两种遍历方法产生的生成树
- 1)深度优先生成树
- 2)广度优先生成树
10.2最小生成树的方法
- 普里姆算法:首先将顶点分为两类(A类和B类),初始状态下将顶点都存放在B类中,任意选着一个顶点将其从B类移到A类中,从B类的顶点出发找到一个与A类相连且权值最小的,放进A类中,以此类推最后会得到(n-1)条边
- 克鲁斯卡尔算法:先按边权从小到大排,依次看每条边,如果发现某一条边的所依附点已经连通了,就直接跳过,知道n个点连通
10.3最短路径
- 最短路径:从一个顶点到另一个顶点存在着一条路径时,称该路径长度为经过的边的数目,路径最短的称作最短路径
10.3.1最短路径的方法
- 狄克斯特拉算法:首先将顶点都置为false,将第一个元素寻找最短路径,然后找到了(打通了这个点),可以在找第一个元素与其他元素的最短路径,以此类推,会有一个最短路径表和一个经过路径表
- 弗洛伊德算法:首先将顶点(n)的个数弄成n*n的矩阵,求出点到各点的路径,自己和自己则是0,无路则用无穷表示,可以求出最短路径,同时经过路径如是0或无穷则用-1代表,有路则则用经过的顶点作为路径
11.排序
11.1排序的两种
- 内排序:整个表放在内存中处理,排序不涉及数据的内,外存交换
- 外排序:反之,要进行数据的内,外存之间的交换
11.2内排序的分类
11.2.1.基于比较的排序
插入排序
- 直接插入排序:逐次将n个元素放到(n-1)个有序序列中带哨兵 平均复杂度为O(n^2)且稳定,需要比较的次数为n(n-1)/2(稳定)
- 折半插入排序:在直接插入的基础上,对有序序列进行减半查找(稳定)
- 希尔排序:将待排序的序列按照增量分为子序列,依次缩小增量,直至增量大小为1,最终的序列是有序序列 时间复杂度O(n^1.5)不稳定
选择排序
- 简单选择排序:首先在一组数中找出最大数和最小数,然后将最大数放在最右边,而将最小的数放在最左边,然后将排序范围缩小到最大数和最小数之间,依次排序之后,最后排序范围指针 begin 和 end 指向一个数时,或者当 begin 大于 end,说明排序完成时间复杂的为O(n^2)且不稳定
- 堆排序:分为大根堆和小根堆(用完全二叉树表示)
交换排序
- 冒泡排序 :内部的循环,比较连续的2连个字符,小的在左边大的在右边,直至序列排好,时间复杂度O(n^2)且稳定
- 快速排序:内部的循环,将j从后面开始往前递减,i从前面往后面j要找到比哨兵小的值,i要找到比哨兵大的值找到了进行交换,直至i=j,将i的值与哨兵交换 时间复杂度O(nlog2n)不稳定
归并排序(外部排序)
- 二路归并排序:设数组a中存放了n个数据元素,初始时把它们看成是n个长度为1的有序子数组,然后从第一个有序子数组开始,把相邻的有序子数组两两合并 时间复杂度O(nlog2n)且稳定
- m个元素k路归并的归并趟数s=logk(m),代入数据:logk(100)≦3
11.2.2不基于比较的排序
-
基数排序
11.3排序的稳定性
- 1.稳定的:如果排序的表中有多个关键字相同的元素,经过排序后这些关键字相同的元素的相对次序保持不变
- 2.不稳定:如果待排序的表中有多个关键子相同的元素,经过排序后这些关键字相同的元素的相对次序发生改变