目录
基本情况
对应课程:程序设计与算法基础II(数据结构与算法基础)
考试时间:3小时
题型:函数题
考试年级:2021级
每个题目均会完整给出 一、已知条件 二、任务描述 三、编码要求 (包括提示) 三个部分;
第四部分为编者的答案,可能不是最优,欢迎探讨。
具体题目
1 线性表1
(题目编号:176)
一、已知条件
有线性表的存储结构表示如下:
#define MAXLEN 128
typedef struct {
int elem[MAXLEN]; //存储数据的数组。下标从0开始。
unsigned len; //数组中数据的个数
} list;
二、任务描述
请设计一个算法,其功能是:
- 在一个给定的线性表中,移除所有能被指定正整数整除的元素
- 操作完成后的线性表中保留的元素呈连续存储的状态
三、编码要求
1. 算法函数原型
void remove_elem(list *L, unsigned f);
- 功能:在线性表L中移除所有能被f整除的元素。
- 参数:
L:指向线性表的指针。线性表可能为空。
f:整除因子。测试用例保证f不小于2。 - 返回值:无
2. 编码约束
- 时间复杂度:O(L->len)
- 空间复杂度:O(1)
四、参考答案
/*
* 完成人 :***
* 完成时间:***
* 系统评分:100
*/
#include "176.h" //本题所需头文件,请勿删除!
/*
* 功能:
* 在线性表L中移除所有能被f整除的元素。
* 参数:
* L:指向线性表的指针。线性表可能为空。
* f:整除因子。测试用例保证f不小于2。
* 返回值:
* 无
*/
void remove_elem(list *L, unsigned f) {
//TODO
int tmp[1000]={0};
int cou=0;
for(int i=0;i<L->len;i++){
if(L->elem[i]%f!=0)
tmp[cou++]=L->elem[i];
}
for(int i=0;i<cou;i++){
L->elem[i]=tmp[i];
}
L->len=cou;
return;
}
2 线性表2
(题目编号:177)
一、已知条件
有单链表表示的线性表结构定义如下:
//定义节点类型
typedef struct _node {
int data; //数据域
struct _node *next; //指针域
} node;
//定义单链表类型
typedef struct {
node *head; //头指针
unsigned len; //链表中的节点数量
} list;
现已知有两个上述类型的线性表La和Lb,二者在某个结点处融合在一起。如图 q2.png 所示:
提示:该图只是一个示例,La不一定总是比Lb长;两个链表都可能为空。
二、任务描述
请设计一个算法,计算两个链表共享结点的数量。
三、编码要求
1. 算法函数原型
int list_shared(list *La, list *Lb);
- 功能:计算并返回线性表La和Lb的共享结点的数量
- 参数:La和Lb都是指向线性表的指针
- 返回值:La和Lb共享结点的数目
2. 编码约束
- 时间复杂度:O(max(La->len, Lb->len))
- 空间复杂度:O(1)
四、参考答案
/*
* 完成人 :***
* 完成时间:***
* 系统评分:100
*/
#include "177.h" //本题所需头文件,请勿删除!
/*
* 功能:
* 统计两个线性表中共享结点的个数。
* 参数:
* La和Lb:指向线性表的指针。
* 返回值:
* La和Lb共享结点的数目。
*/
//答案一(编者答案)
int list_shared(list *La, list *Lb) {
//TODO
if(La->head==NULL||Lb->head==NULL) return 0;
node* p,*q;
p=La->head;
q=Lb->head;
while(p->next!=NULL){
p=p->next;
}
while(q->next!=NULL){
q=q->next;
}
if(p!=q) return 0;
p=La->head;
q=Lb->head;
//int num=0;
while(p!=q){
if(p->next==NULL){
p=La->head;
}
else{
p=p->next;
}
if(q->next==NULL){
q=Lb->head;
}
else{
q=q->next;
}
}
int cou=1;
while(p->next!=NULL){
p=p->next;
cou++;
}
return cou;
}
//答案二(某同学答案)
//个人认为这个答案不满足时间复杂度要求,但是仍然通过
int list_shared(list *La, list *Lb) {
node *p = La->head,*q = Lb->head;
if(p==NULL||q == NULL) return 0;
int cnt = 0;
for(;p!=NULL;p = p->next)
{
for(;q!=NULL;q = q->next)
{
if(p == q)
{
cnt++;
break;
}
}
q = Lb->head;
}
return cnt;
}
3 栈
(题目编号:178)
一、已知条件
有一个递归函数如下:
void reverse() {
char c = getchar();
if (c == '.') return;
reverse();
putchar(c);
}
其功能是将输入的字符序列(以'.'结尾)倒序输出(不包括'.')。例如:
输入:abc123.
输出:321cba
另有可用的栈接口函数如下:
void push(stack *S, char x); //将字符x压入栈S。S是指向栈的指针
char pop(stack *S); //弹栈,返回S栈顶数据
bool empty(stack * S); //测试栈S是否为空。如果为空,返回true,否则返回false
二、任务描述
请设计一个算法,利用栈,消除reverse()中的递归,但功能不变。
三、编码约束
1. 算法函数原型:
void reverse(stack *S);
- 功能:将输入的字符序列倒序输出。输入以'.'结尾。
- 参数:S是指向栈的指针。栈S已经初始化。
- 返回值:无
2. 编码约束
- 时间复杂度:无特别要求
- 空间复杂度:O(1)(不计栈空间)
提示:常量空间复杂度意味着,除了使用栈和单个变量,你不能定义类似于数组这样的辅助存储。
四、参考答案
/*
* 完成人 :***
* 完成时间:***
* 系统评分:100
*/
#include "178.h" //本题所需头文件,请勿删除!
/*
* 功能:
* 将输入的字符序列(以'.'结尾)倒序输出。
* 参数:
* S是指向栈的指针。栈S已经初始化。
* 返回值:
* 无。
*/
void reverse(stack *S) {
//TODO 输入字符请用getchar()
char c = getchar();
while(c!='.'){
push(S,c);
c=getchar();
}
while(!empty(S)){
char x=pop(S);
if(x=='.') continue;
else{
putchar(x);
}
}
return;
}
4 二叉树
(题目编号:179)
一、已知条件
设有两棵二叉树t1和t2。如果t2是t1左右翻转得到,如图 q4.png 所示:
那么称二叉树t1和t2互为镜像。
二叉树的存储结构如下:
typedef struct _btree_node {
char tag; //二叉树结点的字符标签
struct _btree_node *left, *right; //左子树和右子树
} btree_node, *btree;
二、任务描述
请设计一个算法,实现一棵二叉树的镜像翻转。
三、编码约束
1. 算法函数原型:
btree mirror(btree tree);
- 功能:生成二叉树tree的镜像二叉树,返回镜像二叉树的根结点指针。
- 参数:tree是指向源二叉树根结点的指针
- 返回值:指向二叉树tree的镜像二叉树根结点的指针
2. 编码约束
- 时间复杂度:无特别要求
- 空间复杂度:无特别要求
四、参考答案
/*
* 完成人 :***
* 完成时间:***
* 系统评分:100
*/
#include "179.h" //本题所需头文件,请勿删除!
/*
* 生成二叉树tree的镜像二叉树
* 参数
* tree:源二叉树的根结点指针
* 返回值
* 镜像二叉树的根结点指针
*/
btree mirror(btree tree) {
//TODO
if (tree == NULL) return NULL;
btree left = mirro(tree->left);
btree right = mirro(tree->right);
tree->left = right;
tree->right = left;
return tree;
}
5 树
(题目编号:180)
一、已知条件
设有如图 q5-1.png 的一棵树:
那么这棵树的 层次遍历 顺序就是:ABFGCDHE
如果这样的树的存储结构用如下 孩子-兄弟 表示法:
typedef struct csnode {
char data; //结点的字符标签
struct csnode *first; //指向结点的第一个孩子结点
struct csnode *sibling; //指向结点的下一个兄弟结点
} csnode, *cstree;
利用以上存储结构的树可以形象地示意为图 q5-2.png:
其中,土黄色结点是其双亲结点的第一个孩子。
另有可用的 队列 操作的接口如下:
bool queue_enter(queue *Q, void *v); //数据v进队Q。参数Q是指向队列的指针,v的类型是任意类型指针。
void* queue_leave(queue *Q); //数据出队。该数据是函数的返回值,其类型是任意类型指针。
bool queue_empty(queue *Q); //测试队列Q是否为空,如果为空返回真,否则返回假。
再有访问树结点的函数如下:
void visit(cstree node);
其功能是输出结点node的字符标签。
二、任务描述
请设计一个算法,实现树的层次遍历。
三、编码要求
1.算法函数原型
void layer(cstree root, queue *Q);
- 功能:对树tree进行层次遍历。遍历到某个结点node时,调用visit(node)来访问该结点。
提示:如果是空树,则遍历没有输出。
- 参数:
root: 指向树根结点的指针
Q: 指向已初始化的队列的指针 - 返回值:无
2. 编码约束
- 时间复杂度:无特别要求
- 空间复杂度:无特别要求
四、参考答案
/*
* 完成人 :***
* 完成时间:***
* 系统评分:100
*/
#include "180.h" //本题所需头文件,请勿删除!
/*
* 功能
* 层次遍历树
* 参数
* root:树的根结点指针
* Q: 队列指针。队列Q已经初始化了
* 返回值
* 无
*/
void layer(cstree root, queue *Q) {
//TODO
if(root==NULL)return;
queue_enter(Q,root);
while(!queue_empty(Q)){
cstree p=(cstree)queue_leave(Q);
visit(p);
if(p->first!=NULL){
queue_enter(Q,p->first);
cstree tmp=p->first->sibling;
while(tmp!=NULL){
queue_enter(Q,tmp);
tmp=tmp->sibling;
}
}
}
return;
}
6 图
(题目编号:181)
一、已知条件
设有两张 有向图 G1和G2,如图 q6-1.png 所示。
二者有一些 字符标签相同 的顶点(例中是B和D)。
现定义两张 图的焊接(weld) 是将二者的 标签相同的顶点合并,融合成一张图,其余顶点和边不变,如图 q6-2.png 所示。
有向图 用 邻接表 表示,结构如下:
//定义最大顶点数量
#define MAX_VERTEX_NUM 20
typedef struct _arc_node {
int adjvex; //该弧指向顶点在顶点向量中的序号
struct _arc_node *nextarc; //指向下一条弧的指针
} arc_node;
typedef struct vertex_node {
char tag; //顶点字符标签
arc_node *firstarc; //指向该顶点第一条弧的指针
} vertex_node;
typedef struct Graph {
vertex_node vertex[MAX_VERTEX_NUM]; //顶点向量。下标从0开始。
int vexnum, arcnum; //图的顶点数和弧数
} adjlist;
用上述数据结构描述的有向图G2的存储结构可以用图 q6-3.png 所示意:
另有根据结点字符标签 v 定位该结点在 图G 顶点向量中的 序号 的函数:
int locate_vertex(adjlist* G, char v);
例如:locate_vertex(G2, 'F')
返回的结果是3
。
二、任务描述
焊接算法分为几个子算法,其中除了合并边(弧)的子算法,其余都已经完成。
现请设计一个算法,实现焊接算法的子算法:合并两张图的边(弧)。
三、编码要求
-
算法函数原型:
void merge_arcs(adjlist *G1, adjlist *G2);
- 功能:将图G2的边(弧)合并到G1中,即将G2中所有的边复制到G1中;G2保持不变。
如果两张图中只要有一张为空图(即顶点数为0),那么算法不做任何操作,即G1和G2保持不变。 - 参数:G1和G2都是指向图的指针。
- 返回值:无
注意:请用采用头插法处理链表的插入!
- 功能:将图G2的边(弧)合并到G1中,即将G2中所有的边复制到G1中;G2保持不变。
-
编码约束
- 时间复杂度:无特别要求
- 空间复杂度:无特别要求
提示:对图G2中顶点向量中的每一个顶点i,首先定位其在图G1顶点向量中的位置j,然后遍历i的邻接点链表,将其中每一个结点复制出一个副本(即生成新结点),再把副本(新结点)采用头插法插入到顶点j的邻接点链表中
四、参考答案
编者没做出来此题,故略
7 排序
(题目编号:182)
一、已知条件
有一个乱序的正整数数组,其长度为n,其中元素值在 1~m 之间。已知
- n和m都不太大
- n 小于 m
- 元素没有重复值,即数组中没有两个数是相同的
- 数组下标从0开始
二、任务描述
请设计一个算法,对上述数组进行从小到大升序排序。
注:1. 不能用基数排序。2. 不能为m预估一个预设值
三、编码要求
- 算法函数原型
void xsort(unsigned *a, unsigned n);
- 功能:将指定数组进行从小到大升序排序。如果数组长度为0,则函数什么都不做。
- 参数:
a:待排序数组
n:数组长度。注意:长度可能为0。 - 返回值:无
- 编码约束
- 时间复杂度:O(m)
- 空间复杂度:无特别要求
提示:利用数组中无重复值的特点,将数据散列到一个长度为m的一维数组中(这里hash(K)=K),然后再依次收集到原数组中。注意:m的值未知,须编码求得。
四、参考答案
使用的是提示的哈希排序
/*
* 完成人 :***
* 完成时间:***
* 系统评分:100
*/
/*
* 功能:
* 将指定数组进行从小到大升序排序。如果数组长度为0,则函数什么都不做。
* 参数:
* a:待排序数组
* n:数组长度。注意:长度可能为0。
* 返回值:
* 无
*/
#include<stdlib.h>
void xsort(unsigned *a, unsigned n) {
//TODO
if(n==0) return ;
unsigned m=0;
for(int i=0;i<n;i++){
if(m<a[i]) m=a[i];
}
unsigned hash[m+1];
for(int i=0;i<=m;i++){
hash[i]=0;
}
for(int i=0;i<n;i++){
hash[a[i]]=a[i];
}
int k=0;
for(int i=0;i<=m;i++){
if(hash[i]!=0){
a[k++]=hash[i];
}
}
return;
}
自我练习
1. 登录icoding,点击左侧的开始编程
2. 新建一个标签页,网址栏输入 https://icoding.run/ide/question/xxx/1
注意:上方网址的xxx更换为icoding题目编号,题目编号为上方的题目序号下面的黄帝文字,如第一题的编号为176.要练习哪个题就对应那个题目编号
3. 这个新建的标签页出现如下图像时
回到icoding,刷新,即可看到作答页面。
声明
本文章仅供学习使用。严禁作其他用途。