数据结构的复习文档格式.docx
《数据结构的复习文档格式.docx》由会员分享,可在线阅读,更多相关《数据结构的复习文档格式.docx(14页珍藏版)》请在冰豆网上搜索。
基本操作的定义
}ADT抽象数据类型名
随着问题规模n的增长,算法执行时间的度量和f(n)的增长率相同,记作:
T(n)=O(f(n))
补:
若limT(n)/f(n)=c,则T(n)和f(n)是同阶无穷小,表示为T(n)=O(f(n));
其中:
n:
算法的计算量或称规模;
f(n):
运算时间随n增大时的增长率;
O(f(n)算法时间特征的度量。
线性表:
n个数据元素的有限序列。
⏹线性表中数据元素的个数n(n≥0)称为线性表的长度。
⏹当n=0时,称为空表。
⏹非空表的特性:
(1)有且仅有一个表头结点a1,它没有前驱,而仅有一个后继a2;
(2)有且仅有一个表尾结点an,它没有后继,而仅有一个前驱an-1;
(3)其余的结点ai(2≤i≤n1)都有且仅有一个前驱ai-1和一个后继ai+1。
这是定义线性表的结构体:
#defineLIST_INIT_SIZE100//初始分配空间
#defineLISTINCREMENT10//空间分配增量
typedefstruct{
ElemType*elem;
//顺序表数组的基址
intlength;
//当前长度
intlistsize;
//当前分配的存储容量
}SqList
线性表的初始化:
StatusInitlist_sq(sqlist&
L)
{
L.elem=(ElemType*)malloc(LIST_INIT_SIZE*
sizeof(ElemType));
if(!
L.elem)exit(OVERFLOW);
L.length=0;
L.listsize=LIST_INIT_SIZE;
ReturnOK;
}
线性表的插入:
StatusListInsert_sq(sqlist&
L,inti,ElemTypee)
{
if(i<
1||i>
L.length+1)returnERROR;
if(L.length>
=L.listsize)exit(OVERFLOW);
for(j=L.length-1;
j>
=i-1;
--j)
L.elem[j+1]=L.elem[j];
L.elem[i-1]=e;
++L.length;
returnOK;
}//ListInsert_sq最坏情况,其时间复杂度为O(n);
线性表的删除操作:
Statuslistdelete_sq(Sqlist&
L,inti,Elemtype&
e)
{if(i<
L.length)returnERROR;
e=L.elem[i-1];
for(j=i;
j<
=L.length-1;
++j)
L.elem[j-1]=L.elem[j];
--L.length;
returnOK;
}时间复杂度为O(n);
判断线性表L是否为空
statusListEmpty_sq(sqListL)
If(L.length==0)
returnTRUE;
else
returnFALSE;
}
返回线性表L的长度
statusListLength_sq(sqListL)
returnL.length;
}
取线性表的第i个元素算法
StatusGetElem_sq(SqListL,inti,ElemType&
{
if(i<
1||i>
两个线性表的合并:
Voidmergelist_sq(sqlistLa,sqlistLb,sqlist&
Lc)
{pa=La.elem;
pb=Lb.elem;
Lc.listsize=Lc.length=La.length+Lb.length;
pc=Lc.elem=(Elemtype*)malloc(Lc.listsize*sizeof(elemtype));
if(!
Lc.elem)exit(OVERFLOW);
pa_last=La.elem+La.length-1;
pb_last=Lb.elem+Lb.length-1;
While(pa<
=pa_last&
&
pb<
=pb_last)
{if(*pa<
=*pb)*pc++=*pa++;
else*pc++=*pb++;
while(pa<
=pa_last)*pc++=*pa++;
while(pb<
=pb_last)*pc++=*pb++;
⏹线性链表:
用一组任意的存储单元(可以不连续)存储线性表的数据元素。
注:
不连续---可零散地分布在内存中的任何位置上。
⏹线性链表的特点:
1链表中数据元素的逻辑次序和物理次序不一定相同。
即:
逻辑上相邻未必在物理上相邻。
2数据元素在存储器中的存储位置是随意的。
数据域——存储数据元素的信息。
指针域——存储直接后继的存储地址
单链表:
每个结点只有一个指针域。
带头结点的单链表:
⏹在线性链表的第一个元素结点之前附设一个结点,称为头结点。
⏹头结点的数据域不存储任何信息,其指针域存储第一个元素结点的存储位置。
⏹头指针L指向该头结点。
开始结点---用头指针指向之。
最后一个结点(尾结点)---指针域为空(无后继),用^或NULL表示。
表中其它结点---由其前趋的指针域指向之。
⏹线性链表(单链表)的定义描述:
typedefstructLNode{
ElemTypedata;
structLNode*next;
}LNode,*Linklist;
线性链表的初始化——建一个空链表
statusInitlist_L(Linklist&
L){
//建立头结点,其next为空
L=(Lnode*)malloc(sizeof(Lnode));
L->
next=null;
单链表的查找操作:
StatusGetelem_L(LinklistL,inti,ElemType&
{p=L->
next;
j=1;
while(p&
i){//控制L不能是空表,和j<
i
p=p->
++j;
p‖j>
i)returnerror;
//找不到,返回ERROR
e=p->
data;
//找到第i个结点,返回OK
单链表的插入操作:
Statuslistinsert_L(Linklist&
L,inti,elemtypee)
{p=L;
j=0;
while(p&
i-1){p=p->
i-1)returnerror;
s=(Lnode*)malloc(sizeof(Lnode));
s->
data=e;
next=p->
p->
next=s;
单链表的删除操作:
Statuslistdelete_L(Linklist&
L,inti,ElemType&
while(p->
next&
i-1){p=p->
(p->
next)‖j>
i-1)returnERROR;
q=p->
next=q->
e=q->
free(q);
(1)在初值设置上,删除算法从头结点开始,删除范围为[1,表长],指针范围为[0,n-1];
查找算法查找范围[1,表长],指针范围为[1,n];
插入算法插入范围[1,表长+1],指针范围为[0,n];
(2)在循环定位上,删除算法定位在i-1;
(3)在核心条件上,切记“删除算法”的书写。
建立一个带有n个元素的单链表
算法实现:
L,intn){
L=(Lnode*)malloc(sizeof(Lnode));
for(i=n;
i>
0;
--i){
p=(Lnode*)malloc(sizeof(Lnode));
scanf(&
p->
data);
next=L->
next=p;
算法的时间复杂度:
for语句循环n次,为T(n)=O(n)
单链表的合并算法实现:
voidMergeList_L(LinkList&
La,LinkList&
Lb,LinkList&
Lc){
pa=La->
next;
pb=Lb->
Lc=pc=La;
//用La的头结点作为Lc的头结点
while(pa&
pb){
if(pa->
data<
=pb->
data){
pc->
next=pa;
pc=pa;
pa=pa->
next;
else{
pc->
next=pb;
pc=pb;
pb=pb->
if(pa)pc->
elsepc->
next=pb;
free(Lb);
循环链表:
线性表的另一种形式的链式存储结构。
令表中最后一个结点的指针域指向头结点,整个链表形成一个环。
双向链表:
每一个结点有两个指针域——一个指向直接后继,一个指向直接前驱
栈:
栈——限制仅在表尾进行插入或删除操作的线性表。
栈顶(top,表尾)——允许插入和删除的一端。
栈底(bottom,表头)——不允许插入和删除的一端。
空栈——表中没有数据元素。
⏹栈的特点:
后进先出(LIFO)或先进后出(FILO)
顺序栈的结构体定义:
定义:
采用C语言中分配的一维数组表示顺序表。
#definestack_init_size100//初始分配量
#definestackincrement10//分配增量
Typedefstruct{
selemtype*base;
//存储空间基址
selemtype*top;
//栈顶指针
intstacksize;
//当前已分配的存储空间(元
素为单位)
}sqstack;
初始化一个栈——创建栈
StatusInitstack(sqstack&
S){
s.base=(SElemtype*)
malloc(STACK_INIT_SIZE*sizeof(SElemtype));
s.base)exit(OVERFLOW);
s.top=s.base;
s.stacksize=STACK_INIT_SIZE;
}//initstack
进栈(插入新元素)
算法思想:
若栈满,返回OVERFLOW;
否则将新元素e入栈,并返回OK。
Statuspush(sqstack&
s,selemtypee){
if(s.top-s.base>
=s.stacksize)
exit(OVERFLOW);
*S.top++=e;
}//push
出栈(删除栈顶元素)
若栈空,返回ERROR;
否则将删除S的栈顶元素,并用e返回其值。
statuspop(sqstack&
s,selemtype&
e){
if(s.top==s.base)returnERROR;
e=*--s.top;
}//pop
取栈顶元素
否则将用e返回栈顶元素。
statusgettop(sqstacks,selemtype&
if(s.top==s.base)returnERROR;
e=*(s.top-1);
}//getpop
队列:
一个只能在队首进行删除、队尾进行插入的线性表。
队尾(rear):
允许插入的一端。
队头(front):
允许删除的一端。
特征:
先进先出(FIFO)。
树:
•结点(node)一个数据元素及若干指向其子树的分支;
•结点的度(degree)结点的子树个数;
•树的度(degree)树内各结点度的最大值;
•分支(branch)结点度不为0的结点;
•叶(leaf)结点度为0的结点;
•孩子(child)结点某结点的子树;
双亲(parent)结点相应的,该结点称为孩子的双亲
•兄弟(sibling)结点具有同一双亲的所有结点;
•祖先(ancestor)结点从根到该结点所经分支上的所有结点;
•子孙(descendant)结点以某结点为根的子树中的任一结点。
•结点的层次(level)根结点的层数为1,其余结点的层数为双亲结点的层数加1;
•树的深度(depth)树中结点的最大层数;
•有序树子树的次序不能互换;
•无序树子树的次序可以互换;
•森林m(m≥0)棵互不相交的树的集合,对于树中每个结点而言,其子树的集合即为森林。
二叉树是结点数为0或每个结点最多只有左右两棵子树的树。
二叉树是一种特殊的树,它的特点有:
(1)每个结点最多只有两棵子树,即不存在结点的度大于2的结点;
(2)子树有左右之分,不能颠倒。
二叉树的性质
性质1二叉树的第i层最多有2i-1个结点。
(i1)
性质2深度为k的二叉树最多有2k-1个结点。
(k1)
性质3对任何一棵二叉树,如果其叶结点个数为n0,度为2的非叶结点个数为n2,则有n0=n2+1。
满二叉树(FullBinaryTree)
一棵深度为k,且有2k-1个结点的二叉树。
满二叉树的特点:
每一层都取最大结点数;
结点层序编号方法:
从根结点起从上到下逐层,层内从左到右,对二叉树的结点进行连续编号。
⏹完全二叉树(CompleteBinaryTree)
深度为k,有n个结点的二叉树是一棵完全二叉树,当且仅当其每个结点都与深度为k的满二叉树中层次编号1—n相对应。
完全二叉树的特点
(1)除最后一层外,每一层都取最大结点数,最后一层结点都有集中在该层最左边的若干位置。
(2)叶子结点只可能在层次最大的两层出现。
(3)对任一结点,若其右分支下的子孙的最大层次为L,则其左分支下的子孙的最大层次为L或L+1。
性质4具有n个结点的完全二叉树的深度
为+1。
性质5如果将一棵有n个结点的完全二叉树自顶向下,同一层自左向右连续给结点编号1,2,…,
n-1,n,则对任一结点i(1≤i≤n)有:
(1)若i=1,则结点i是二叉树的根,无双亲;
若i>
1,则i的双亲为i/2
(2)如果2i>
n,则结点无左孩子,否则其左孩子
lchild(i)是结点2i
(3)如果2i+1>
n,则结点无右孩子,否则其右孩
子rchild(i)是结点2i+1
二叉树的存储结构
一、顺序存储结构
二、链式存储结构
设计不同的结点结构,可以构成不同的链式存储结构,常用的有:
二叉链表、三叉链表和线索链表
遍历二叉树(traversingbinarytree)
按某条搜索路径访问树中每一个结点,使得每个结点均被访问一次,且仅被访问一次。
先序(根)遍历:
DLR
中序(根)遍历:
LDR
后序(根)遍历:
LRD
二叉树的遍历算法主要有两种:
递归算法;
非递归算法。
先序遍历算法:
StatusPreOrderTraverse(BitreeT){
if(T){
visit(T->
data);
PreOrderTraverse(T->
lchild);
rchild);
returnok;
}
returnerror;
中序遍历算法:
voidInOrderTraverse(BiTreeT){
if(T){
InOrderTraverse(T->
Visit(T->
后序遍历算法:
StatusPostOrderTraverse(BitreeT){
if(T){
PostOrderTraverse(T->
visit(T->
先序遍历——非递归算法:
StatusPreOrderTraverse(BitreeT,status(*visit)(TelemTypee)){
Initstack(s);
p=T;
while(p||!
StackEmpty(s)){
if(p){visit(p->
push(s,p);
lchild;
else{
pop(s,p);
rchild;
中序遍历——非递归算法:
StatusInOrderTraverse(BitreeT,status(*visit)(TelemTypee)){
if(p){push(s,p);
visit(p->
线索二叉树
☐可利用二叉链表结点结构中的空指针域(共(n+1)个),在空指针域中存放结点在某种遍历次序下的前趋和后继结点信息,这种附加的指针称为“线索”。
☐为避免混淆,需改变结点结构,即增加两个标志域。
☐若结点有左子树,则标志位ltag=0,表示左链域指针lchild指向其左孩子结点;
否则,令其左链域指示其前驱,ltag=1。
☐若结点有右子树,则标志位rtag=0,表示右链域指针rchild指向其右孩子结点;
否则,令其右链域指示其后继,rtag=1。
☐指向结点前驱或后继的指针叫做线索。
☐加上线索的二叉树叫线索二叉树。
☐对二叉树以某种次序遍历使其变为线索二叉树的过程叫做线索化。
☐按先序遍历得到的线索二叉树称为先序线索二叉树;
☐按中序遍历得到的线索二叉树称为中序线索二叉树;
☐按后序遍历得到的线索二叉树称为后序线索二叉树。
树的存储表示
1.双亲表示法
用一组地址连续的存储单元来存放树的结点,每一个结点有两个域:
Data域——存放结点的信息;
Parent域——存放该结点的双亲结点的位置;
.孩子表示法:
◆每个结点的孩子用单链表存储,称为孩子链表;
◆N个结点可以有n个孩子链表;
◆N个孩子链表的头指针可以组成一个顺序表。
4.孩子-兄弟表示法:
用二叉链表作为树的存储结构,转换规则:
每个结点的左链域指向该结点的第一个孩子,
每个结点的右链域指向下一个兄弟结点。
•简单来说,哈希表就是基于哈希函数建立的一张查找表。
•哈希函数是一个映射,其设定可以很灵活,只要使得任何关键字的哈希函数值都落在表长允许范围内即可。
对不同关键字可能得到同一哈希地址,这一现象称为“冲突”,即key1≠key2,而f(key1)=f(key2)。
并且,改进哈希函数只能减少冲突,而不能避免冲突。
•因此,在设计哈希函数时:
一方面,要考虑选择一个“好”的哈希函数;
另一方面,要选择一种处理冲突的方法。
查找的分类:
静态查找:
不涉及插入和删除操作的查找。
动态查找:
涉及插入和删除操作的查找。