p=p->next;++j;
}
if(!
(p->next)‖j>i-1)
returnERROR;//删除位置不合理q
=p->next;
p->next=q->next;free(q);//删除并释放结点
returnOK;
}//ListDelete_L
7.循环链表:
表中最后一个结点的指针域指向头结点,整个链表形成一个环。
循环链表的操作和单链表基本一致,差别仅在于,判别链表中最后一个结点的条件不再是"后继是否为空",而是"后继是否为头结点"。
(1)单链表p或p->next==NULL
(2)循环链表p->next==L
8.双向链表有两个指针域,一个指向直接前驱,一个指向直接后继。
1)向双向链表中插入一个结点:
s->prior=p->prior;
p->prior->next=s;
s->next=p;
p->prior=s;
2)向双向链表中插入一个结点:
:
s->prior=p;
s->next=p->next;
p->next->prior=s;
p->next=s;
3)从双向链表中删除一个结点
①p->prior->next=p->next;
②p->next->prior=p->prior;
第三章栈和队列
3.1栈和队列的逻辑结构特征
1.栈(stack)和队列(queue)是两种重要的线性结构,特殊性在于其基本操作是线性表操作的子集,是操作受限的线性表(操作限定在两个端点进行),为具有限定性的数据结构。
栈按“后进先出”的规则进行操作,队列按“先进先出”的规则进行操作。
2.栈是限定在表尾进行插入和删除操作的线性表。
允许插入,删除的一端称为栈顶(top),另一端
称为栈底(bottom)。
3.栈的基本运算主要有两个:
Push(S,e),进栈,插入(压入)元素e为新的栈顶元素,Pop(S),
出栈,删除(弹出)S的栈顶元素。
如:
若元素入栈的顺序为1234,为了得到1342出栈顺序,操作序列为:
Push(S,1),Pop(S),Push(S,2),Push(S,3),Pop(S),Push(S,4),Pop(S),Pop(S)。
3.2栈的顺序存储结构
1.顺序栈:
利用一组地址连续的存储单元一次存放从栈底到栈顶的数据元素,用指针top指示栈顶元素在顺序栈中的位置。
top指向栈顶元素的下一个位置
top指向栈顶元素
初始化:
S.top=S.base
S.top=S.base-1
判栈空:
S.base==S.top
S.base==S.top-1
入栈:
*s->top++=e(先压后加)
*++s->top=e(先加后压)
栈满:
S.top-S.base>=S.stacksize
S.top-S.base>=S.stacksize-1
出栈:
e=*--S.top(先减后弹)
e=*S.top--(先弹后减)
Ø入栈时,首先判栈是否满了,栈满的条件为:
S.top-S.base>=S.stacksize,栈满时,不
能入栈;否则出现空间溢出,引起错误,这种现象称为上溢。
Ø出栈和读栈顶元素操作,先判栈是否为空,为空时不能操作,否则产生错误。
通常栈空时
常作为一种控制转移的条件。
2.用数组的索引值表示栈底和栈顶
top指向栈顶元素的下一个位置
top指向栈顶元素
初始化:
top=0
top=-1
判栈空:
top==0
top==-1
入栈:
v[top++]=x(先压后加)
v[++top]=x(先加后压)
栈满:
top-base>=stacksize;
top-base>=stacksize-1;
出栈:
y=v[--top])(先减后弹)
y=v[top--])(先弹后减)
3.两栈共享空间:
top[0]表示第一个栈的栈顶;top[1]表示第二个栈的栈顶
栈空:
top[0]=-1;top[1]=n
入栈:
a[++top[0]]=e;a[--top[1]]=e
栈满:
top[0]+1=top[1]
出栈:
e=a[top[0]--];e=a[top[1]++]
4.关于顺序栈的说明:
入栈时,首先判栈是否满了,栈满时,不能入栈;否则出现空间溢出,引起错误,这种现象称为上溢。
出栈和读栈顶元素操作,先判栈是否为空,为空时不能操作,否则产生错误。
通常栈空时常作为一种控制转移的条件。
3.3栈的顺序链式存储
入栈:
p=newLNode;//建新的结点
if(!
p)exit
(1);//存储分配失败
p->data=e;p->next=S->top;//链接到原来的栈顶
S->top=p;//移动栈顶指针
出栈:
if(!
S->top)returnNULL;else
{e=S->top->data;//返回栈顶元素
q=S->top;
S->top=S->top->next;//修改栈顶指针
free(q);//释放被删除的结点空间
returne;
}
3.4栈的应用举例
1.数制转换
#defineNUM10
voidconversion(intN,intr){
ints[NUM],top;/*定义一个顺序栈*/
intx;
top=-1;/*初始化栈*/
while(N){
s[++top]=N%r;/*余数入栈*/
N=N/r;/*商作为被除数继续*/
}
while(top!
=-1){
x=s[top--];
printf(“%d”,x);
}
}
2.括号匹配的检验:
3.表达式求值:
熟悉前缀、中缀和后缀表达式,表达式求值时栈的状态变化。
4.栈与递归的实现:
熟悉使用递归解决
3.5队列的逻辑结构特征
队列:
只允许在一端进行插入,而在另一端删除元素。
允许插入的一端为队尾(rear),允
许删除的一端为队头(front)。
3.6队列的顺序存储结构
1.循环队列的顺序存储结构:
队列存放数组被当作首尾相接的表处理。
队头、队尾指针加1时
用语言的取模(余数)运算实现。
队列初始化:
front=rear=0;
队空条件:
front==rear;
队满条件:
(rear+1)%MAXQSIZE==front
队头指针进1:
front=(front+1)%MAXQSIZE;
队尾指针进1:
rear=(rear+1)%MAXQSIZE;
队中元素个数:
(rear-front+MAXQSIZE)%MAXQSIZE
2.链式队列:
进队:
p=(QueuePtr)malloc(sizeof(QNode));
if(!
p)return0;//存储分配失败
p->data=e;p->next=NULL;
Q->rear->next=p;Q.rear=p;
出队:
if(Q->front==Q->rear)returnNULL;
p=Q->front->next;e=p->data;
Q->front->next=p->next;
if(Q->rear==p)Q->rear=Q->front;
free(p);returne;
第四章串、数组和广义表
4.1串相关术语
串即字符串,是由零个或多个字符组成的有限序列,是数据元素为单个字符的特殊线性表。
串长:
串中字符个数(n≥0).n=0时称为空串Æ。
空白串:
由一个或多个空格符组成的串。
子串:
串s中任意个连续的字符序列叫s的子串;s叫主串。
子串位置:
子串的第一个字符的序号。
字符位置:
字符在串中的序号
串相等:
串长度相等,且对应位置上字符相等。
串的逻辑结构和线性表极为相似,区别仅在于串的数据对象约束为字符集;串的基本操作和线性表有很大差别。
在线性表的基本操作中,大多以“单个元素”作为操作对象;在串的基本操作中,通常以“串的整体”作为操作对象。
4.2串的基本操作
熟悉以下操作的意义:
StrAssign(&T,chars)
StrCopy(&T,S)
DestroyString(&S)
StrEmpty(S)
StrCompare(S,T)
StrLength(S)
Concat(&T,S1,S2)
SubString(&Sub,S,pos,len)
Index(S,T,pos)
Replace(&S,T,V)
StrInsert(&S,pos,T)
StrDelete(&S,pos,len)
ClearString(&S)
4.3数组
1.二维数组的顺序存储结构及地址计算方式。
设一般的二维数组是A[c1..d1,c2..d2],这里c1,c2不一定是0。
L:
单个元素长度
则行优先存储时的地址公式为:
LOC(aij)=LOC(c1,c2)+[(i-c1)*(d2-c2+1)+(j-c2)]*L
二维数组列优先存储的通式为:
LOC(aij)=LOC(ac1,c2)+[(j-c2)*(d1-c1+1)+(i-c1)]*L
2.对称矩阵的压缩存储:
在对称矩阵中,只需存储对称矩阵的下半部分。
所需空间数为:
n×(n+1)/2。
设一般的二维数组是A[c1..d1,c2..d2],这里c1,c2不一定是0,对应一维存储空间SA的起始
值是C3。
则行优先存储时的地址公式为:
3.三角矩阵:
若n阶方阵中下(上)三角(不包括对角线)中的元均为常量c或0,则称为上(下)
三角矩阵;下三角矩阵:
主队角线以上均为同一个常数;上三角矩阵,主队角线以下均为同一个常数。
与对称矩阵类似,不同之处在于存完下三角中的元素之后,紧接着存储对角线上方的常量,因为是同一个常数,所以存一个即可,这样一共存储了n*(n+1)/2+1个元素,设存入数组:
SA[n*(n+1)/2+1]中,这种的存储方式可节约n*(n-1)/2个存储单元。
4.理解下、上三角矩阵:
SAk与ai,j的对应关系。
5.稀疏矩阵:
将每个非零元素用一个三元组(i,j,aij)来表示,将三元组按行优先的顺序,同一行中列号从小到大的规律排列成一个线性表,称为三元组表,每个稀疏矩阵可用一个三元组表来表示。
4.4广义表
1.广义表是递归定义的线性结构,是线性表的推广,也称为列表(lists)
记为:
LS=(1,2,...,n)。
2.广义表与线性表的区别和联系:
广义表中元素既可以是原子类型,也可以是列表;当每个元素都为原子且类型相同时,就是线性表。
3.广义表LS=(a1,a2,…,an)的的性质:
1)广义表中的数据元素有相对次序;
2)广义表的长度定义为最外层包含元素个数;
3)广义表的深度定义为所含括弧的最大重数;
注意:
“原子”的深度为0;“空表”的深度为1
4)广义表是一种多层次的数据结构。
广义表的元素可以是单元素,也可以是子表,而子表的元素还可以是子表,…。
5)广义表可以是递归的表。
广义表的定义并没有限制元素的递归,即广义表也可以是其自身的子表。
6)广义表可以为其他表所共享。
7)任何一个非空广义表LS=(1,2,…,n)
均可分解为:
表头Head(LS)=1和表尾Tail(LS)=(2,…,n)两部分.
任何一个非空表,表头可能是原子,也可能是列表;但表尾一定是列表
4.广义表的基本运算:
广义表有两个重要的基本操作,即取头操作(Head)和取尾操作(Tail)。
要熟悉这个两个操作,正确给出一个广义表的这两个操作的结果。
第五章树及二叉树
5.1树结构及基本概念
1.树具有下面两个特点:
树的根结点没有前驱结点,除根结点之外的所有结点有且只有一个前驱结点。
树中所有结点可以有零个或多个后继结点。
2.基本术语:
结点(node):
表示树中的元素,包括数据项及若干指向其子树的分支
结点的度(degree):
结点拥有的子树数称为~
叶子(leaf):
度为0的结点
孩子(child):
结点子树的根称为该结点的孩子
双亲(parents):
孩子结点的上层结点
兄弟(sibling):
同一双亲的孩子
树的度:
一棵树中最大的结点度数
结点的层次(level):
从根结点算起,根为第一层,它的孩子为第二层……
深度(depth):
树中结点的最大层次数
森林(forest):
m(m³0)棵互不相交的树的集合
5.2二叉树结构
1.定义:
二叉树是n(n0)个结点的有限集,它或为空树(n=0),或由一个根结点和两棵分别
称为左子树和右子树的互不相交的二叉树构成
2.特点:
每个结点至多有二棵子树(即不存在度大于2的结点)二叉树的子树有左、右之分,且
其次序不能任意颠倒
3.基本形态:
五种
4.二叉树的性质
性质1:
在二叉树的第i层上至多有2i-1个结点。
性质2:
深度为k的二叉树,至多有2k-1个结点。
性质3:
对任意二叉树BT,若叶结点数为n0,度为2的结点数为n2,则:
n0=n2+1
性质4:
具有n个结点的完全二叉树的深度为ëlog2nû+1
性质5:
如果对一棵有n个结点的完全二叉树的结点按层序编号,则对任一结点i(1£i£n),
有:
1)如果i=1,则结点i是二叉树的根,无双亲;如果i>1,则其双亲是结点ëi/2û
2)如果2i>n,则结点i无左孩子;如果2i£n,则其左孩子是结点2i
3)如果2i+1>n,则结点i无右孩子;如果2i+1£n,则其右孩子是结点2i+1
5.几种特殊形式的二叉树:
满二叉树:
一棵深度为k且有2k-1个结点的二叉树称为~
特点:
每一层上的结点数都是最大结点数
完全二叉树:
深度为k,有n个结点的二叉树当且仅当其每一个结点都与深度为k的满二叉
树中编号从1至n的结点一一对应时,称为~
特点:
叶子结点只可能在层次最大的两层上出现;对任一结点,若其右分支下子孙的最大层次为l,则其左分支下子孙的最大层次必为l或l+1
5.3二叉树存储
1.二叉树的顺序存储结构:
按满二叉树的结点层次编号,依次存放二叉树中的数据元素
特点:
结点间关系蕴含在其存储位置中;浪费空间,适于存满二叉树和完全二叉树
2.二叉树的链式存储结构(二叉链表):
在n个结点的二叉链表中,有n+1个空指针域
3.二叉树的链式存储结构(三叉链表)
5.4二叉树遍历
1.二叉树的遍历:
先序遍历(DLR):
先访问根结点,然后分别先序遍历左子树、右子树
中序遍历(LDR):
先中序遍历左子树,然后访问根结点,最后中序遍历右子树
后序遍历(LRD):
先后序遍历左、右子树,然后访问根结点
2.遍历的递归算法:
voidpreOrder(bt){/*先序遍历二叉树bt*/
if(bt){/*递归调用的结束条件为bt为空*/
visit(bt->data);/*访问结点的数据域*/
preorder(bt->lchild);/*先序递归遍历bt的左子树*/
preorder(bt->rchild);/*先序递归遍历bt的右子树*/
}
}
voidinOrder(bt){/*中序遍历二叉树bt*/
if(bt){/*递归调用的结束条件为bt为空*/
inOrder(bt->lchild);/*中序递归遍历bt的左子树*/
visit(bt->data);/*访问结点的数据域*/
inOrder(bt->rchild);/*中序递归遍历bt的右子树*/
}
}
voidpostOrder(bt){/*后序遍历二叉树bt*/
if(bt){/*递归调用的结束条件为bt为空*/
postOrder(bt->lchild);/*后序递归遍历bt的右子树*/
postOrder(bt->rchild);/*后序递归遍历bt的右子树*/
visit(bt->data);/*访问结点的数据域*/
}
}
5.5线索二叉树
1.线索二叉树的定义
前驱与后继:
在二叉树的先序、中序或后序遍历序列中两个相邻的结点互称为~
线索:
指向前驱或后继结点的指针称为~
线索二叉树:
加上线索的二叉链表表示的二叉树叫~
线索化:
对二叉树按某种遍历次序使其变为线索二叉树的过程叫~
2.线索二叉树的实现
在有n个结点的二叉链表中必定有n+1个空链域。
在线索二叉树的结点中增加两个标志域
ltag:
若ltag=0,lchild域指向左孩子;若ltag=1,lchild域指向其前驱
rtag:
若rtag=0,rchild域指向右孩子;若rtag=1,rchild域指向其后继
3.在中序线索二叉树中找结点后继的方法
rt=1,则rc域直接指向其后继
rt=0,则结点的后继应是其右子树的左链尾(lt=1)的结点
4.在中序线索二叉树中找结点前驱的方法:
lt=1,则lc域直接指向其前驱
lt=0,则结点的前驱应是其左子树的右链尾(rt=1)的结点
5.6树和森林
1.树和森林与二叉树之间的转换方法:
孩子兄弟表示法
5.7赫夫曼树
1.赫夫曼树(Huffman)——带权路径长度最短的树
2.赫夫曼算法
1)根据给定的n个权值构成n棵二叉树的集合F,其中每棵二叉树中只有一个带权值的结点;
2)