数据结构第一到六章
期末整理汇总:
写在前面:害这段时间复习周反正就是看PPT然后总结一些伪代码啊算法啊之类的,自己方便看。期末冲鸭!
第一章
数据元素:是数据的基本单位
数据项:是最小单位,数据元素由数据项组成
数据对象:性质相同的数据元素的集合
数据结构:相互之间具有一种或多种特定的关系的数据元素的集合
数据元素有三种结构:集合、线性结构、非线性结构
(D,S):D是数据元素的有限集,S是D上关系的有限集
“关系”描述的是数据元素之间的逻辑关系,因此又叫逻辑结构
数据结构在计算机中的表示叫做存储结构
元素和结点是数据元素在计算机中 的映像
数据结构的存储方式:顺序存储结构和链式存储结构。顺序通过借助元素在存储器中的相对位置来表示数据元素之间的逻辑关系;链式通过指针来表示数据元素之间的逻辑关系
数据类型:是一个值的集合和定义在该值集上的一组操作的总称。按值是否可分可将数据类型分为两类:原子类型和结构类型
抽象数据类型(ADT)是一个数学模型以及定义在该模型上的一组操作。“抽象”在于数据类型的数学抽象特性。(D,S,P):D是数据对象,S是D上的关系集,P是对D的基本操作集
基本操作有两种参数:1.赋值参数只为操作提供输入值,2.引用参数以&打头,除了可以提供输入值以为还能返回操作结果
异常结束语句:exit(异常代码)
算法是为了解决某个特定问题而规定的一个有限长的操作序列,是指令的有限序列:有5个重要特性——又穷性、确定性、可行性、输入、输出
算法的设计应该考虑:正确性、可读性、健壮性、效率与低存储量需求
同一个算法,实现它的语言越高级,执行效率越低
一个算法是由控制结构(顺序、分支、循环)和原操作(固有数据类型的操作)
我们应该尽可能选用多项式阶的算法,而不希望用指数阶的算法
算法的时间复杂度取决于:问题的规模和待处理的数据初态
第二章
线性表的链式存储:带头结点的结构中,头结点的作用:1.使得对空表和非空表的操作一致;2.插入和删除首元素的时候不必做特殊处理
静态链表:数组下标表示元素的地址。定义的时候是定义一个数据结构数组,数据结构包括存储的数据类型和表示下标的Int型变量名
循环链表:表中最后一个节点的指针域指向头结点,因此可从表中任意结点出发找到表中其他结点
双向链表:有prior和next两个指针域
第三章
栈:表尾叫做栈顶,表头叫做栈底。只能从栈顶进行插入和删除操作。
顺序栈:
typedef struct{
SElemType *base;
SElmType *top;
int stacksize;
}SqStack;
栈空的时候是top=base;栈不存在是base=NULL;
非空栈的栈顶指针始终在栈顶元素的下一个元素的位置。
*S.top++=e;//先在该位置放上e,然后指针上移
e=*--S.top;//先下移找到栈顶元素然后取值
链式栈:
队列:
链队列:
队列空的条件是:链的头指针和尾指针都指向头结点。
typedef struct QNode{
QElemType data;
struct QNode *next;
}QNode,*QueuePtr;
typedef struct{
QueuePtr front;
QueuePtr rear;
}LinkQueue;
Q.front->next是队头,Q.rear是队尾.一开始是Q.front=Q.rear=(...)malloc(...);后面的时候若是进队则先是Q.rear->next=e,然后Q.rear=e;而若是出栈则p=Q.front->next,Q.front->next=p->next,Q.front本身是不能动的!它是该栈的地址
顺序循环队列:对尾处的元素是一开始没有的,赋值之后再将其后移一位;但队头元素一开始就是有的,取值之后再后移
typedef struct{
QElemType *base;
int front;
int rear; //front和rear都是数字,代表着的是数组下标!
}SqQueue;
Status InitQueue(SqQueue &Q){
Q.base=(....)malloc(......);
Q.friont=Q.rear=0;
................
}
插入元素e:if((Q.rear+1)%MAXQSIZE==Q.front)return ERROR;//队列满了!!!队空的条件是:Q.front=Q.rear
Q.base[Q.rear]=e;
Q.rear=(Q.rear+1)%MAXSIZE;//因为是循环队列
删除队头元素:就是要使得Q.front=(Q.front+1)%MAXSIZE;
第四章:串
串是由多个或多个字符组成的有限序列
串的逻辑结构和线性表极为相似,区别仅在于串的数据对象约束为字符集。
基本操作:
Concat(&T,S1,S2)返回由S1,S2链接成的新串
SubString(&Sub,S,pos,len)用 Sub 返回串 S 的第 pos 个字符起 长度为 len 的子串
StrLength(S)求S的长度
Replace(&S,T,V)用V替换主串S中出现的所有与(模式串)T相等的不重叠的子串
Index(S,T,pos)返回T在S中第pos个字符之后第一次出现的位置
StrInsert(&S,pos,T)在串的第pos个字符之前插入串T
StrDelete(&S,pos,len)从串中删除第pos个字符起长度为len的字串
StrCompare(S,T)比较两个字符串的大小(是按照字符串的比较方法,不是看长度比的!)
StrAssign(&T,chars)生成一个其值为chars的串T
空格串不是空串,用符号Ø来表示空串。
串的定长存储 表示:规定好串的长度,超过部分会被截断。typedef unsigned char Sstring [MAXSTRLEN + 1];Sstring[0]里存储串的长度
串的堆分配存储 表示:地址连续,但是动态分配存储空间,因此不会有溢出的情况
串的块链式存储结构 表示:用链表表示串,每个结点里面可以放置多个字符,即块的大小。然后除了头指针还能设置一个尾指针,以及定义串的链表长
第五章 数组
二维数组A(b1, b2)表示该数组的行数是b1,列数是b2
压缩存储:为多个值相同的元只分配一个存储空间,为零单元不分配空间
对称矩阵:
存储单元从n^2缩小到n(n+1)/2.
这式子里面的-1是因为S的下标从0开始,因此求到的值需要减去1
如果遇到了i<j,则直接访问aji,即要根据aji确定k然后找到S[k]。
三角矩阵:
跟对称矩阵类似,只是对角线上方的元素全部为常数c或0,因此所需存储空间为n(n+1)/2+1,多出来的那个地方,就是S[n(n+1)/2]的值为常数c或0。
对角矩阵:
在这种矩阵中,除了对角线上和直接在对角线上、下方若干条对角线上的元素外,所有其他的元素皆为0。
稀疏矩阵:有很多个0的矩阵,可用三元组表示非零元素,如(1,2,18)表示的是1行2列的非零元18
系数矩阵存储方式:三元组顺序表、行逻辑链接的顺序表、十字链表
三元组顺序表:将三元组按行优先,同一行列号从小到大的规律排成一个线性表。采用顺序存储方法存储该表
转置算法:将M的行和列对换然后用T表示(M,T都是三元组顺序存储表)
for (col=1;col <=M.nu; ++col) num[col]=0;//先初始都是0
for (t=1;t<=M.tu; ++t) ++num[M.data[t].j];//求M中每一列含非零元个数
cpot[1] = 1;//求第col列中第一个非零元在b.data中的序号
for (col=2; col<=M.nu; ++col) cpot[col] = cpot[col-1] + num[col-1];//求col列第一非零元位置
注意红色部分:cpot[col]始终存的是col列下一个要存的元素在T中的位置!!
这个算法的时间复杂度为: O(M.nu+M.tu)
行逻辑链接的顺序表:
将指示行信息的辅助数组cpot固定在 稀疏矩阵的存储结构中
两个稀疏矩阵相乘:
十字链表:
每行和每列的非零元链成循环链表,每个非零元用一个含五个域的结点表示:
广义表:是n(n≥0)个数据元素a1, a2, …, an的有序序列,记做LS(a1,a2,a3......an)
当广义表非空的时候,将a1称为表头,(a2,a3......an)组成的表称为LS的表尾
广义表的长度定义为最外层所包含的元素个数(所以最好的方法就是根据最外层的逗号来数)
广义表的深度定义为所含括弧的重数; 注意:“原子”的深度为 0 , “空表”的深度为 1
基本操作:取表头操作(GetHead)和取表尾操作(GetTail)
小窍门:写表尾的时候就先放个()然后把第一个逗号之后的东西照搬进去就好
广义表的链式存储结构可以分为:头尾表示法和孩子兄弟表示法
头尾表示法:表结点和原子节点分别表示列表和原子
表结点中:tag是标志域,hp是指向表头的,tp是指向表尾的
原子结点中:tag是标志域,atom是值域
tag=1是表结点,=0是原子结点
孩子兄弟表示法:
其中原子的tp是指向跟它同级的第一个元素,可能是表节点也可能是原子结点
第六章:树和二叉树
专业术语:
结点的度数:表示它有几个子树
树的度:表示结点度数的最大值
结点的层数:默认将根的层次设为1
树的深度:树种结点最大的层次
森林:对树的每个结点而言,其子树的集合就是森林
二叉树:二叉树每个结点至多有两棵子树,且有左右之分,次序不能颠倒。
满二叉树:每层结点都满了,深度为K的话那么结点总数为(2^K-1)。
完全二叉树:深度为k,有n个结点的二叉树,当且仅当其每一个结点都与深度为k的满二叉树中编号从1至n的结点一一对应,称为完全二叉树,其特点是: 叶子结点只可能在层次最大的两层上出现,即最下层和次最下层; 对任一结点,若其右分支下的子孙的最大层次为 L,则起左分支下的子孙的最大层次必为 L 或 L+1 ,即最下层的 叶子结点集中在树的左部。
二叉树的性质:
在二叉数的第i层上至多 有2i-1个结点(i>=1);
深度为k的二叉树至多有2k-1个结点(k>=1);
对任何一棵二叉树T,如果其终端结点数为n0,度为2的 结点数为n2,则n0=n2+1(树中,终端结点出度为0,树的总入度为总结点数-1,非终端结点出度为1或2)
具有n个结点的完全二叉树的深度为|_log2n_| +1
个
二叉树的表示方法:
链式存储表示 :
对结点有二个指针域的存储方式有以下表示方法:
typedef struct BiTNode{
TElemType data;
struct BitNode *lchild, *rchild; //左右孩子指针
} BiTNode,*BiTree;
二叉树的遍历:
可以用栈来实现非递归遍历算法。
以中序为例:
建立二叉树也是可以用递归实现,先序、中序、后序遍历都可
遍历二叉树的时间复杂度:不论以何种方式进行遍历,最终时间复杂度都是O(n)
完全二叉树的顺序存储:在完全二叉树的顺序存储结构中,对于任意一个结点BT[k],其左孩子BT[2k],右孩子为BT[2k+1]。
r是一个BitTree类型的根节点
线索二叉树:
利用二叉树的二叉链表存储结构中那些空指针域来保留结点在某种遍历序列中直接前驱和后继的位置信息,称这种二叉树的存储结构为线索链表, 其中指向结点的前驱和后继的指针域被称为线索。加上线索的二叉树称之为线索二叉树
以中序为例代码如下:
中序遍历建立一个线索二叉树:
树和森林:
树的存储有三种链表结构:
1.双亲表示法:
2.孩子表示法:
3.孩子兄弟表示法:(也叫二叉树表示法)
森林和二叉树:
1.树转换成二叉树:树和二叉树都可以用二叉链表存储,给了一棵树,有唯一一个二叉树与之对应
转换方法:
(1)在树中的所有兄弟结点之间加一条连线;
(2)树中的每一个结点只保留它与第一个孩子结点之间的连线,全部删除与其它孩子结点之间的连线;
(3)以树根为轴心,对结点进行旋转整理,即得到二叉树。
2.森林转换成二叉树:
森林转换化为二叉树的步骤:
(1)将森林中每一棵树转换成对应的二叉树;
(2)从第一棵二叉树开始,依次把森林中后一棵二叉树的根结点作为前一棵二叉树根结点的右孩子,
把所有的二叉树全部用连线连起来后,最后所得到的二叉树就是由森林转换得到的二叉树。
3.二叉树转化成森林:
注意:若B非空,则F中第一棵树T1的根ROOT(T1)即为二叉树B的根root; T1中根结点的子树森林F1是由B的左子树LB转换而成的森林;F中除T1 之外其余树组成的森林F’={T2, T3,…, Tm}是B的右子树RB转换而成的森林。
树的遍历:
先根遍历:ABCDE
后根遍历:BDCEA
森林的遍历:
先序:ABCDEFGHIJ
中序:BCDAFEHJIG
当以二叉链表的存储结构时,树的先根遍历和后根遍历可借用二叉树的先序遍历和中序遍历的算法实现之。
哈夫曼树:
结点的带权路径长度:从结点到根之间的路径长度与结点上权值的乘积。
树的带权路径长度:树中所有叶子结点的带权路径长度之和
应用:Huffman树可用来构造前缀编码。
规定Huffman树中的左分支代表“0”,右分支代表“1”
本题需要的二进制位数为: 2*(17+28+35)+3*12+4*(3+5)=228 则,共需要的字节数为:228/8=28.5, 即需要29个字节。
哈夫曼树的构造:
哈夫曼树的编码: