数据结构考试
三、综合题
8.直接插入排序 和 简单选择排序 算法的执行过程
- 直接插入排序
void InsertSort(SqList &L){
//对顺序表L作直接插入排序
for( i = 2; i <= L.length; ++i ){
if( LT( L.r[i].key, L.r[i-1].key )){
L.r[0] = L.r[i]; //复制为哨兵
L.r[i] = L.r[i-1]; //将 i-1 记录后移
for( j = i - 2; LT( L.r[0].key, L.r[j].key ); --j){
L.r[j+1] = L.r[j]; //将 i-1 之前的记录后移
}
L.r[j+1] = L.r[0]; //插入到正确位置
}
}
}
直接插入排序的简单版,利于理解,但效率次于上述代码
void InsertSort(SqList &L){
for( i = 2; i <= T.length; i++ ){
if(L.[i] < L.[i-1]){
L.[0] = L.[i]; //复制为哨兵
int j = i;
for( ; L.[j-1] > L.[0]; j-- ){ //从i-1起,进行记录后移
L.[j] = L.[j-1];
}
L.[j] = L.[0]; //插入到正确位置
}
}
}
- 简单选择排序
void SelectSort(SqList &L){
for( i = 1; i < L.length; i++ ){
j = SelectMinKey(L,i);
if( i != j ){ //在L.r[i...L.length]中选择 key 最小的记录
L.r[i]←→L.r[j];
}
}
}
四、算法题
1.线性链表 Create
- 逆序建立(头插法):先建立头节点,再从后往前添加元素。
void List_Create(LinkList &L,int n){
//逆位序输入 n 个元素的值,建立单链表L。
L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;//先建立 头节点
for( i = n; i > 0; --i ){//循环生成其他结点
p = (LinkList)malloc(sizeof(LNode));
scanf(&p->data);
p->next = L->next;
L->next = p;
}
}
- 顺序建立(尾插法):
void CreateList2(LinkList& L,int n){
L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;
q = L;
for( i = 1; i <= n; i++ ){
p = (LinkList)malloc(sizeof(LNode));
scanf(&p->data);
q->next = p;
q = q->next;
}
p->next = NULL;//next域置空。
}
2.线性链表 Delete
Status List_Delete(LinkList &L,int i,ElemType &e){
//在单链表L中,删除第 i 个元素,并由 e 返回其值。
p = L;
j = 0;
while( p->next && j < i-1 ){//让 p 指向待删除结点的前驱,即 i-1
p = p->next;
++j;
}
if( !( p->next ) || j > i-1 ){//p 指向了最后一个元素 || j 超出 待删除的位置i (在数组中的位置i-1)
return ERROR;
}
q = p->next;
p->next = q->next;
e = q->data;
free(q);
return OK;
}
free(q);
:由系统回收一个 LNode
型的结点,回收后的空间可以备作再次生成结点时使用。
3.线性链表 Insert
Status List_insert(LinkList &L,int i,ElemType e){
//在单链表L中,插入元素 e 到第 i 个位置。原第 i 个元素的位置变为 i+1。
p = L;
j = 0;
while( p && j < i-1 ){//查找第 i-1 个值
p = p->next;
++j;
}
if( !p || j > i-1 ){//指针 p 为空 || j 超出 目的插入位置i (在数组中的下标i-1)
return ERROR;
}
s = (LinkList)malloc(sizeof(LNode));
s->data = e;
s->next = p->next;
p->next = s;
return OK;
}
s = (LinkList)malloc(sizeof(LNode));
:由系统生成一个 LNode
型的结点,同时将该结点的起始位置赋给指针变量 s
。
4.求二叉树的深度
int BiTreeDepth(BiTree T){
if (!T){
return 0;
}else
{
l = BiTreeDepth(T->lchild);
r = BiTreeDepth(T->rchild);
return (l > r) ? (l + 1) : (r + 1);
}
}
5. 折半查找算法 p220
int Search_Bin(SSTable ST, KeyType key){
low = 1;
high = ST.length;
while(low <= high){
mid = (low + high) / 2;
if( EQ( key,ST.elem[mid].key ) ){//找到带查找元素
return mid;
}else if( LT( key,ST.elem[mid].key ) ){
high = mid -1;
}else{
low = mid +1;
}
}
return 0;
}
6.统计二叉树中叶子结点的个数
int CountLeaf(BiTree T,int &count){
if(T){
if( !T->lchild && !T->rchild ){
count++;
}
CountLeaf(T->lchild, count);
CountLeaf(T->rchild, count);
}
}
叶子结点的特征是左孩子和右孩子都为空,若有一个不为空,那就再遍历一次。
7.循环队列的入队算法 p65
Status EnQueue(SqQueue &Q, QElemType e){
// 插入新的队尾元素 e
if( ( Q.rear+1 ) % MAXQSIZE == Q.front ){
return ERROR;
}
Q.base[Q.rear] = e;
Q.rear = (Q.rear + 1) % MAXQSIZE;
return OK;
}
出队(不在老师给考试的范围内):
Status DeQueue(SqQueue &Q, QElemType &e){
//
if( Q.rear == Q.front ){
return ERROR;
}
e = Q.base[Q.front];
Q.front = (Q.front + 1) % MAXQSIZE;
return OK;
}
8.顺序栈的出栈算法 p47
Status Pop(SqStack &S, SElemType &e){
//栈不为空,则删除 S 的栈顶元素
if(S.top == S.base){
return ERROR;
}
e = * --S.top;
return OK;
}
知识点
chap 1 绪论
基本概念和术语
- 数据:data 所有能输入到计算机中并被计算机程序处理的符号的总称。
数据元素
:data element 是数据
的基本单位
,一个数据元素
可由若干
个数据项 data item
组成,数据项
是数据
的不可分割
的最小单位
。- 数据对象:data object 是性质相同的
数据元素
的集合
,是数据
的一个子集
。 - 数据结构:data structure 是
相互之间存在
一种或多种特定关系
的数据元素
的集合
。
数据元素
相互之间的关系
称为结构 structure
。根据数据元素之间关系的不同特性,通常有以下4类基本结构:- 集合:结构中的数据元素之间除了“同属于一个集合”的关系外,别无其它关系。
- 线性结构:结构中的
数据元素
之间存在一个对一个
的关系。 - 树形结构:结构中的
数据元素
之间存在一个对多个
的关系。 - 图状结构 or 网状结构:结构中的
数据元素
之间存在多个对多个
的关系。
结构
定义中的“关系”
描述的是数据元素
之间的逻辑关系
。
衡量一个算法好坏的量度:
- 时间复杂度:衡量算法执行的时间量级。
- 空间复杂度:衡量算法的数据结构所占存储以及大量的附加存储。
- 算法的其他性能:
chap 2 线性表
线性表的顺序表示:用一组地址连续的存储单元依次存储线性表的数据元素。
LOC(ai)=LOC(a1) + (i-1) * L
以元素
在计算机内“物理位置相邻”
来表示线性表
中数据元素
之间的逻辑关系
。
只要确定了存储线性表
的起始位置
,线性表
中任一数据元素
都可以随机存取
,所以线性表的顺序存储结构
是一种随机存取
的存储结构
。
chap 3 栈
stack 是限定仅在表尾
进行插入或删除
操作的线性表
。表尾
为栈顶
(top),表头
为栈底
(bottom)。
遵循后进先出原则。可类比铁路调度站。
base
栈底指针:top
栈顶指针:
top = base;
可以作为栈空的标记。非空栈的栈顶指针
始终在栈顶元素
的下一个位置上。stacksize
已分配的存储空间
条件:
- 栈满条件:
S.top - S.base >= S.stacksize
- 栈空条件:
S.top == S.base
chap 3 队列
队列:
queue
是一种先进先出
的线性表
。
它只允许在表的一端进行插入,而在另一端删除元素。这和我们日常生活中的排队是一致的。
在队列中,允许插入
到一端叫做队尾 rear
,允许删除
的一端称为队头 front
。
循环队列:
front
队列头指针:删除头元素时,头指针 +1,头指针
始终指向队头元素
。rear
队列尾指针:插入尾元素时,尾指针 +1,尾指针
始终指向队尾元素
的下一个位置
。
只凭 Q.front == Q.rear
无法判断队列空间是“空”还是“满”。
处理方法:少用一个元素空间,约定以 “front 在 rear 的下一位置上” 作为队列呈“满”状态的标志。
- 队满条件:
(Q.rear + 1) % MAXQSIZE == Q.front
- 队空条件:
Q.front == Q.rear
chap 6 树和二叉树
树的定义
树型结构
是非线性数据结构
。树
是以分支关系
定义的层次结构。
树的结点
包含一个数据元素
及若干指向其子树的分支
。结点
拥有的子树数
成为节点的度
(Degree)。
- 度为 0 的结点称为
叶子结点(Leaf)
或终端结点
。 - 度不为 0 的结点称为
分支节点
或非终端节点
。
分支结点
和根结点
都是内部节点
。
树的度
是树内各结点的度
的最大值
。
结点的层次(Level)
从根开始定义起,根为第一层,根的孩子为第二层。
树中结点的最大层次
称为树的深度
。
森林(Forest)
是 m 棵互不相交的树的集合。对树中每个结点而言,其子树的集合即为森林 。
二叉树
二叉树的性质:
- 在二叉树的
第 i 层
上,至多有 2i-1 个结点。 - 深度为 k 的二叉树,至多有 2k-1 个结点。
- 任何一颗二叉树,如果
叶子节点
数 = n0 ,度为 2 的结点
数 = n2 ,则 n0 = n2 + 1 。
证明步骤:- 结点总数:n = n0 +n1 +n2
- 分支数:B = n - 1 且 B = n1 + 2n2
满二叉树
:深度为 k 且有 2k-1 个结点的二叉树。
特点:每一层上的结点数都是最大节点数。
完全二叉树
:深度为 k ,有 n 个节点,其每一个结点
都与深度为 k 的满二叉树
中编号从 1 至 n 的结点一一对应
的二叉树。
特点:叶子结点
只可能在层次最大的两层上
出现;对任一结点,若其右分支下
的子孙的最大层次为 j,则其左分支下
的子孙的最大层次必为 j 或 j+1。
- 具有 n 个结点的
完全二叉树
的深度为 (log2n) +1。 完全二叉树
子结点/2=双亲结点;双亲结点为 i ,则左孩子结点为 2i ,右孩子结点为 2i+1 。
二叉树的存储结构:
- 顺序存储结构
- 链式存储结构:
二叉链表:n+1个空链域,n-1个非空链域。(总共 2n)
三叉链表:n+2个空链域,2n-2个非空链域。(总共 3n)
森林
二叉树
和树
都可以用二叉链表
作为存储结构。则通过二叉链表
作为媒介,可导出树
与二叉树
之间的一个对应关系
。
也就是说,给定一棵树
,可以找到唯一的一棵二叉树
与之对应。
chap 9 静态查找
通常以“其关键字
和给定值
进行过比较的记录个数
的平均值
”作为衡量查找算法好坏的依据。
等概率下顺序查找的平均查找长度:ASL = (n+1)/2
顺序表的查找:
- 优点:算法简单,适应面广。对表的结构无任何要求,无论记录是否按
关键字有序
均可应用。 - 缺点:平均查找长度较大,特别是当n很大时,查找效率较低。
有序表的查找:(折半查找)
low
mid
= (low + high) / 2high
- 折半查找过程:以
处于区间中间位置记录
的关键字
和给定值
比较,若相等,则查找成功,若不等,则缩小范围。 - 性能分析:
折半查找过程可以用二叉树叙述,也叫判定树
。
∵ 折半查找在查找成功时,进行比较的关键字个数
最多不超过树的深度h
。
∵ 而具有n个结点的判定树的深度为 h=(log2n)+1。
∴ 折半查找法在查找成功时,和给定值进行比较的关键字个数至多为 h=(log2n)+1。 - 折半查找的平均查找长度:
ASLbs = log2(n+1) - 1 - 优缺点分析:
优点:折半查找
的效率比顺序查找
高。
缺点:只适用于有序表
,且限于顺序存储结构
。对线性链表
无法有效的进行折半查找。