- 目录
第一章绪论练习
第二章线性表
第三章栈和队列
第四章串
第五章数组和广义表
第六章树和二叉树
第七章图
第九章查找
第十章排序
第一章绪论练习
1-8 数据结构的抽象操作的定义与具体实现有关。 (1分)
- T
- F
1-14 数据结构包括数据对象集以及它们的逻辑结构和物理结构,还包括与数据对象相关联的操作集,以及实现这些操作的高效的算法。 (1分)
- T
- F
2-10 下面关于抽象数据类型的描述,不正确的是( )。 (2分)
- A. 数据封装
- B. 使用与实现分离
- C. 信息隐藏
- D. 用例驱动
2-12 以下关于数据结构的说法中正确的是____。 (2分)
- A 数据结构的逻辑结构独立于其存储结构
- B 数据结构的存储结构独立于该数据结构的逻辑结构
- C 数据结构的逻辑结构唯一地决定了该数据结构的存储结构
- D 数据结构仅由其逻辑结构和存储结构决定
4-2 存储结构* 数据的存储结构包含两个方面:
____的表示,是数据元素在计算机中的映像;
___ 的表示,是指数据元素之间的关系在计算机中的表示方法。
4-5 基本概念*
_____ 一般指由用户定义的、表示应用问题的数学模型,以及定义在该模型上的一组操作。4-9 数据结构由___、___和___三部分组成。
4-14 基本概念* 按值的不同特性,在高级语言中的数据类型可分为两大类:
(1) ____:值不可分解;
(2) ____:值由若干成分组成,可分解,其成分可是以 (1),也可以是 (2)。
**绪论答案:1-8F 1-14T 2-10D 2-12A 4-2数据元素 关系 4-5抽象数据类型 4-9逻辑结构 存储结构 运算 4-14原子类型 结构类型**
第二章线性表
pta
1-5 将长度分别为m,n的两个单链表合并为一个单链表的时间复杂度为O(m+n)。
- T
- F
时间复杂度为O(1),如果是两个有序链表合成一个有序链表的时间复杂度为O(M + N)1-15 所谓“循环队列”是指用单向循环链表或者循环数组表示的队列。
- T
- F
所谓“循环队列”是指用循环数组表示的队列1-16在具有头结点的链式存储结构中,头指针指向链表中的第一个元素结点。
- T
- F
头指针指向头结点2-12 顺序表中第一个元素的存储地址是100,每个元素的长度为2,则第5个元素的地址是( )。
- A 100
- B 105
- C 108
- D 110
答案:1-5F 1-15 F 1-16F 2-12C
课后题
1、编写算法,实现带头结点单链表的逆置算法。
void ReverseList( LinkList &Head )
{// 将Head 所指的带头结点的单链表逆置
if( Head->next && Head->next->next){//当链表不是空表或单结点时p=Head->next;q=p->next; p -> next=NULL;//将开始结点变成终端结点while (q) {//每次循环将后一个结点变成开始结点p=q; q=q->next ;//先将指针同步后移一位p->next = Head-> next;Head->next = p;//然后将p指针提到第一个位置去}
}
第三章栈和队列
pta
2-19
如果循环队列用大小为m的数组表示,队头位置为front、队列元素个数为size,那么队尾元素位置rear为: (2分)
- A front+size
- B front+size-1
- C (front+size)%m
- D (front+size-1)%m
答案:2-19D
应用举例
1、数制转换
//数制转换
void conversion( ) {initstack(S);scanf (“%”,N);while(N){push(S,N%8)N=N/8;}while(! Stackempty(S)){pop(S,e);printf(“%d”,e);}
}
2.括号匹配的检验
假设表达式中允许包含两种括号:圆括号和方括号,其嵌套顺序随意,例:
( [ ] ( ) )或[ ( [ ] [ ] ) ]等为正确的格式
[ ( ] )或( [ ( ) )或 ( ( ) ] )均为不正确的格式。
思路:1)凡出现左括弧,则进栈;
2)凡出现右括弧,首先检查栈是否空?
若栈空,则表明该“右括弧”多余
否则和栈顶元素比较,
若相匹配,则“左括弧出栈”
否则表明不匹配
3)表达式检验结束时,
若栈空,则表明表达式中匹配正确
否则表明“左括弧”有余。
课后习题
1、假设以带头结点的循环链表表示队列,并且只设一个指针指向队尾元素(注意不设头指针), 试编写相应的队列初始化、入队列和出队列的算法。
typedef struct QNode {QElemType data; struct QNode *next;
}QNode,*QueuePtr;
typedef struct { //链队指针类型 QueuePtr tail; //队尾指针
}LinkQueue;
void InitQueue(Queue &Q)
{//初始化循环链表表示的队列QQ.tail=(QueuePtr)malloc(sizeof(QNode));Q.tail->next=Q.tail;}//InitQueue
Status EnQueue(QueuePtr &tail,QElemType e ){q = (QueuePtr)malloc(sizeof(QNode));//分配空间,构造入队列元素结点 q->data = e; //元素赋值 q->next = tail ->next; //入队列 tail ->next = q;tail = tail ->next; //队尾指针后移 return OK;
}
Status DeQueue(QueuePtr & tail,QElemType &e){if(tail == tail ->next) return ERROR; //队列已空q = tail ->next->next;//q指向带头结点链表的第一个元素e = q->data; //返回出队列元素 tail ->next->next = q ->next; //元素出队列 free(q); //释放空间 return OK;
}
3-31 判别输入的字符序列是否为“回文”。 例如 abcdedcba 或 abccba是回文,两头读过来都相同。
试写一个算法判别输入的一个以“@”为结束符的字符序列是否为回文。分析:由于回文的字符序列中的分界线不明确,因此算法中,除了需要用一个栈外,还需要用一个队列,否则无法判别。
算法的基本思想是: 将依次读入的字符分别插入栈和队列,然后依次进行比较“栈顶”和“队头”的字符。
注意:循环队列队满的条件(rear+1)/maxsize == front
第四章串
2-2 (neuDS_C++)串是一种特殊的线性表,其特殊性体现在( )。
- A 可以顺序存储
- B 数据元素是一个字符
- C 可以链接存储
- D 数据元素可以是多个字符
2-10 (neuDS)设主串的长度为n,模式串的长度为m,则串匹配的KMP算法时间复杂度是( )。
- A O(m)
- B O(n)
- C O(n + m)
- D O(n×m)
2-11 若串S=“software”,其子串的数目是 (2分)
- A 8
- B 37
- C 36
- D 9
子串数目 = (∑n)+1 = n*(n+1)/2 + 1
注意:定长顺序存储即将字符存于数组中且总下标1开始,而0号单元存储长度
串的模式匹配:子串的定位操作
int Index(SString S,SString T, int pos){
//返回子串T在主串S中第pos个字符之后的位置,若不存在,则函数值为0,其中,T非空,(1≦pos≦Strlength(S))。i=pos; //指向串s的第pos个字符j=1; //指向串t的第1个字符while((i<=S[0])&&(j<=T[0])){if(S[i]==T[j]) { ++i; ++j; } //继续比较后继字符else {i=i-j+2; j=1;} //串S指针回溯重新开始匹配}if(j>T[0]) return i-T[0]; //匹配成功,返回模式串t在串s中的起始位置else return 0; //匹配失败返回0
} //Index
串答案:2-2B 2-10C 2-11B
第五章数组和广义表
知识点
广义表的分类
(1)线性表:元素全部是原子的广义表。
(2)纯表:与树对应的广义表.
(3)再入表:与图对应的广义表(允许结点共享),
(4)递归表:允许有递归关系的广义表,例如E=(a,E)。
这四种表的关系满足:递归表再入表 纯表 线性表
LS = ( 1, 2, …, n)均可分解为
表头 Head(LS) = 1 和
表尾 Tail(LS) = ( 2, …, n) 两部分
pta
2-3 稀疏矩阵在计算机中通常采用()来表示。 三元组线性表
2-4广义表是一种()数据结构。 递归的
2-7在定义稀疏矩阵的十字链接存储结构时,每个结点结构需包含()个域。 5
十字链表标志稀疏矩阵:在链表中,每个非零元可用一个含有5个域的结点表示,其中i,j,e这三个域分别表示非零元所在行、列以及值,向右域right和向下域down分别表示同行的下一个非零元和同列的下一个非零元
第六章树和二叉树
必会知识点
性质1:在二叉树的第i层上至多有2i-1个结点。(i≥1)
性质 2 :深度为 k 的二叉树上至多含 2k-1 个结点(k≥1)
性质 3 :具有 n 个结点的完全二叉树的深度为log2n +1
性质 4 :若对含 n 个结点的完全二叉树从上到下且从左至右进行 1 至 n 的编号,则对完全二叉树中任意一个编号为 i 的结点:
(1) 若 i=1,则该结点是二叉树的根,无双亲,
否则,编号为i/2的结点为其双亲结点;
(2) 若 2i>n,则该结点无左孩子,否则,编号为 2i 的结点为其左孩子结点;
(3) 若 2i+1>n,则该结点无右孩子结点, 否则,编号为2i+1 的结点为其右孩子结点。
树 | 二叉树 |
---|---|
先序遍历 | 先序遍历 |
后序遍历 | 中序遍历 |
pta
2-7 设高为h的二叉树(规定叶子结点的高度为1)只有度为0和2的结点,则此类二叉树的最少结点数和最多结点数分别为: (3分)
- A 2h, 2h-1
- B 2h−1, 2h-1
- C 2h−1, 2h-1-1
- D 2h-1+1, 2h-1
2-23 设 T 是非空二叉树,若 T 的先序遍历和后序遍历序列相同,则 T 的形态是 __ (2分)
- A 只有一个根结点
- B 没有度为 1 的结点
- C 所有结点只有左孩子
- D 所有结点只有右孩子
答案:2-7B 2-23A
5-1 下列代码的功能是将二叉树T中的结点按照层序遍历的顺序输出。
typedef struct TreeNode *Tree;
struct TreeNode
{int Key;Tree Left;Tree Right;
};void Level_order ( Tree T )
{Queue Q;if ( !T ) return; Q = CreateQueue( MaxElements ); Enqueue( T, Q ); while ( !IsEmpty( Q ) ){T = Front_Dequeue ( Q ); /* return the front element and delete it from Q */printf("%d ", T->Key);if ( T->Left ) Enqueue( T->Left, Q )
(3分);if (
T->Right
(3分) ) Enqueue( T->Right, Q )
(3分);}
}
5-4
已知先序遍历序列和中序遍历序列建立二叉树。 例如
输入先序遍历序列: ABDFGC, 再输入中序遍历序列: BFDGAC,则 输出该二叉树的后序遍历序列: FGDBCA。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef char ElementType;
typedef struct BiTNode{ElementType data;struct BiTNode *lchild;struct BiTNode *rchild;
}BiTNode,*BiTree;BiTree CreatBinTree(char *pre,char*in,int n );
void postorder( BiTree T );int main()
{BiTree T;char prelist[100];char inlist[100];int length;scanf("%s",prelist);scanf("%s",inlist);length=strlen(prelist);T=CreatBinTree(prelist,inlist, length);postorder( T );return 0;
}
void postorder( BiTree T )
{if(T){postorder(T->lchild);postorder(T->rchild);printf("%c",T->data);}
}
BiTree CreatBinTree(char *pre,char*in,int n)
{BiTree T;int i;if(n<=0) return NULL;T=(BiTree)malloc(sizeof(BiTNode));T->data=pre[0];for(i=0;in[i]!=pre[0];i++);T->lchild=
CreatBinTree(pre+1,in, i)//i为左子树的长度,若<=0,则无左子树
(3分);T->rchild=
CreatBinTree(pre+i+1,in+i+1,n-1-i )//n-i-1为右子树长度
(3分);return T;
}
课后习题
1.深度为k的完全二叉树至少有____个结点。至多有____个结点,若按自上而下,从左到右次序给结点编号(从1开始),则编号最小的叶子结点的编号是____。 2k-1 2k-1 2k-1+1
2.已知一棵二叉树的前序遍历的结果是ABECDFGHIJ, 中序遍历的结果是EBCDAFHIGJ, 试画出这棵二叉树。
中序非递归算法
void InOrderTraverse(BiTree T, Status (*Visit)(TElemType e)){InitStack(S); Push(S,T);while(!StackEmpty(S)){ while(GetTop(S,p) && p) Push(S,p->lchild);Pop(S,p); if(!StackEmpty(S)){ Pop(S,p); if(! Visit(p ->data)) return ERROR;Push(S,p->rchild);}}return OK;
}
未掌握:5-4
已知后序序遍历序列和中序遍历序列建立二叉树。 例如
> 输入后序遍历序列: FGDBCA, 再输入中序遍历序列: BFDGAC,则 输出该二叉树的先序遍历序列: ABDFGC。
> 中序非递归算法
第七章图
知识点
深度优先遍历
//连通图
void DFS(Graph G, int v) {// 从顶点v出发,深度优先搜索遍历连通图 G,需要设置一个辅助数组visited[v] = TRUE; VisitFunc(v);for(w=FirstAdjVex(G,v);w!=0; w=NextAdjVex(G,v,w))if (!visited[w]) DFS(G,w); // 对v的尚未访问的邻接顶点w// 递归调用DFS
} // DFS//非连通图
void DFSTraverse(Graph G, Status (*Visit)(int v)) {// 对图 G 作深度优先遍历。VisitFunc = Visit; for (v=0; v<G.vexnum; ++v) visited[v] = FALSE; // 访问标志数组初始化for (v=0; v<G.vexnum; ++v) if (!visited[v]) DFS(G, v);// 对尚未访问的顶点调用DFS
}
广度优先搜索
//使用队列
void DFSTraverse(Graph G,Status (*Visit)(int v)){for(v=0;v<G.vexnum;++v) visited[v]=FALSE; //访问标志数组初始化InitQueue(Q);//置空的辅助队列Qfor (v=0;v<G.vexnum;++v)if (!visited[v]) {//v尚未访问visited[v]=TRUE; Visit(v);EnQueue(Q,v);while(!QueueEmpty(Q)){DelQueue(Q,u);//队头元素出队并置为u,访问ufor(w=FirstAdiVex(G,u);w!=0;w=NextAdjVex(G,u,w))if (!visited[w]){visited[v]=TRUE; Visit(w);EnQueue(Q,v); //u的尚未访问的邻接顶点w入队列}//if}//while} // if
}// DFSTraverse
最小生成树
//普里姆算法(按逐个将顶点连通的方式来构造最小生成树。)
//需要设置一个辅助数组记录最小生成树元素,先遍历顶点判断顶点是否已属于最小生成树,
//若是则遍历其邻接点,若其临界点不含于最小生成树中则参与比较大小
int U;
int V[1001];
typedef struct Adjacency{//表结点int leftAdjvex;int rightAdjvex; //邻接点的位置int weight;struct Adjacency * nextarc; //指向下一个表结点的指针
}Adjacency;
typedef struct VNode{int data; //顶点信息Adjacency * firstarc; //指向第一个表结点的指针
}VNode, VexList[2000]; //AdjList表示邻接表类型
typedef struct{VexList vexList; //头结点数组int vexNumber, edgeNumber; //图的当前顶点数和边数
}ALGraph;
/---找最小边---
Adjacency* findMinEdge(ALGraph alGraph){//先遍历顶点判断顶点是否已属于最小生成树,若是则遍历其邻接点,若其临界点不含于最小生成树中则参与比较大小Adjacency *b = (Adjacency*)malloc(sizeof(Adjacency));b = NULL;for (int i = 1; i <= alGraph.vexNumber; ++i) {//遍历顶点判断顶点if(V[i] != 0){//是否已属于最小生成树Adjacency *a = (Adjacency*)malloc(sizeof(Adjacency));a = alGraph.vexList[i].firstarc;while (a != NULL) {//当邻接点含于最小生成树时,a=下一个邻接点if(V[a->rightAdjvex] != 0){a = a->nextarc;}else{if(b == NULL){b = a;}else{if(a->weight < b->weight){b = a;}}a = a->nextarc;}}}else{continue;}}return b;
}
int prim(ALGraph alGraph,Adjacency *c){int w;V[c->leftAdjvex] = V[c->rightAdjvex] = 1;U = 2;w = c->weight;Adjacency *b = (Adjacency*)malloc(sizeof(Adjacency));b = findMinEdge(alGraph);while (b != NULL){V[b->rightAdjvex] = 1;w += b->weight;U++;b = findMinEdge(alGraph);}if(U != alGraph.vexNumber)return -1;return w;
}
克鲁斯卡尔(Kruskal)算法
先构造一个只含n个顶点的子图SG,然后从权值最小的边开始,若它的添加不使SG中产生回路,则在SG上加上这条边,如此重复,直至加上n-1 条边为止。
拓扑排序
用DFS遍历一个无环有向图,并在DFS算法退栈返回时打印相应的顶点,则输出的顶点序列是
求最短路径的迪杰斯特拉算法
pta
2-4 在N个顶点的无向图中,所有顶点的度之和不会超过顶点数的多少倍? (2分)
- A 1
- B 2
- C (N−1)/2
- D N−1
2-18以下哪个命题是正确的? (3分)
- A 对于带权无向图G = (V, E),M是G的最小生成树,则M中任意两点V1到V2的路径一定是它们之间的最短路径
- B P是顶点S到T的最短路径,如果该图中的所有路径的权值都加1,P仍然是S到T的最短路径
- C 深度优先遍历也可用于完成拓扑排序
- D 以上都不是
2-24 用DFS遍历一个无环有向图,并在DFS算法退栈返回时打印相应的顶点,则输出的顶点序列是? (2分)
- A 无序的
- B 拓扑有序
- C 逆拓扑有序
- D 以上都不对
第九章查找
知识点
二叉排序树的删除操作
(1) 若待删除的结点是叶子结点,直接删去该结点。如图(a)所示,直接删除结点9。
(2) 若待删除的结点只有左子树而无右子树。根据二叉排序树的特点,可以直接将其左子树的根结点放在被删结点的位置。如图(b)所示,p作为q的右子树根结点,要删除p结点,只需将p的左子树(其根结点为3)作为q结点的右子树。
(3) 若待删除的结点只有右子树而无左子树。与(2)情况类似,可以直接将其右子树的根结点放在被删结点的位置。如图©所示,p作为q的右子树根结点,要删除p结点,只需将p的右子树(其根结点为8)作为q结点的右子树。
(4) 若左右子树均存在则找左树最大的结点或者右树最小的结点取代此节点并转至(2)或(3)删除原来位置的次节点
pta
2-14给定散列表大小为11,散列函数为H(Key)=Key%11。按照线性探测冲突解决策略连续插入散列值相同的4个元素。问:此时该散列表的平均不成功查找次数是多少?
- A 1
- B 4/11
- C 21/11
- D 不确定
平均不成功查找长度,顾名思义即对N(N为表长)种查找不成功的不同情况的探测次数求和并取平均,对于表中的空格只探测一次便能证明查找失败所以m个空格直接加m,对于非空,则需要比较并继续探测下一个空格是否为空。
此题 = ((11-4)+(5+4+3+2))/11
第十章排序
堆排序
1、建堆
把待排序记录R[1…n]看作一棵二叉树,将R[1]作为二叉树的根,R[2…n]依次逐层从左到右顺序排列,构成一棵完全二叉树,任意结点R[i]的左孩子是R[2i],右孩子是R[2i+1],双亲是R[i/2]。(注意:这时的完全二叉树并不具备堆的特征)。此时所有i>n/2」的结点R[i]都没有孩子结点,因此以R[i]为根的子树已经是堆。从i=n/2的结点R[i]开始,比较根结点与左、右孩子的关键字
例如此例,从12即R[5]开始一直筛选R[4]、R[3]、R[2]、R[1]