写在前面
时间回到2021年12月份,突如其来的期末考试打的我们猝不及防,相信也给期末出卷老师带来了不小的压力,再加上20级是如此的特殊以及优秀,所以此次数据结构机考变成了闭卷。但天无绝人之路,闭卷的同时也意味着难度可能有所降低,参考往年的机考题,现大胆进行一波预测。(纯属个人臆想,什么都会的大佬请跳过)
还有本次软工是五个题目做出三个满分,分为函数题以及编程题。且根据老师的回答,函数题占大多数,可以想到函数题大概是有三个。但我个人感觉函数题比自己完整写一道题目更难,因为所要编写的函数要满足题目片段各种奇奇怪怪的定义名,一多就容易眼花的来。
⑧说废话了,直接开始。
一、函数题
1.链表逆置(详见PTA 链表逆置 )
本题要求实现一个函数,将给定单向链表逆置,即表头置为表尾,表尾置为表头。链表结点定义如下:
struct ListNode {
int data;
struct ListNode *next;
};
函数接口定义:
struct ListNode *reverse( struct ListNode *head );
其中head是用户传入的链表的头指针;函数reverse将链表head逆置,并返回结果链表的头指针。
裁判测试程序样例:
#include <stdio.h>
#include <stdlib.h>
struct ListNode {
int data;
struct ListNode *next;
};
struct ListNode *createlist(); /*裁判实现,细节不表*/
struct ListNode *reverse( struct ListNode *head );
void printlist( struct ListNode *head )
{
struct ListNode *p = head;
while (p) {
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
int main()
{
struct ListNode *head;
head = createlist();
head = reverse(head);
printlist(head);
return 0;
}
输入样例:
1 2 3 4 5 6 -1
输出样例:
6 5 4 3 2 1
代码:
struct ListNode *reverse( struct ListNode *head ){
struct ListNode *p,*q;
p=head->next;
head->next=NULL;
while(p){
q=p;
p=p->next;
q->next=head->next; //头插
head->next=q;
}
return head->next;
}
算是线性表的入门题了,当然链表逆置有许多方法,我选择的是头插,简单易懂。更多的方法详见 链表逆置(三种方法详解)
线性表也是笔试的热门考点,双向链表节点的插入删除,重复节点的删除,链表去重等具体实现也要掌握。
2.快速排序(详见PTA 快速排序 )
给一个无序表,使用快速排序算法对它进行排序。
函数接口定义:
int Partition(SqList &L,int low,int high);
void QuickSort(SqList &L, int low, int high);
其中L是待排序表,low和high是排序的区间。
裁判测试程序样例:
#include <iostream>
using namespace std;
#define MAXSIZE 50
typedef int KeyType;
typedef struct
{ KeyType key;
} ElemType;
typedef struct
{ ElemType r[MAXSIZE+1];
int length;
} SqList;
void Create(SqList &L)
{ int i;
cin>>L.length;
for(i=1;i<=L.length;i++)
cin>>L.r[i].key;
}
void Output(SqList L)
{ int i;
for(i=1;i<=L.length;i++)
cout<<L.r[i].key<<" ";
cout<<endl;;
}
int Partition(SqList &L,int low,int high);
void QuickSort(SqList &L, int low, int high);
int main ()
{ SqList L; int low,high;
Create(L);
low=1; high=L.length;
QuickSort(L,low,high);
Output(L);
return 0;
}
输入样例:
5
3 1 9 5 7
输出样例:
1 3 5 7 9
输入样例:
5
0 -1 8 -1 2
输出样例:
-1 -1 0 2 8
代码:
int Partition(SqList &L,int low,int high){
int value=L.r[low].key;
while(low<high){
while(low<high && L.r[high].key>=value) high--;
L.r[low].key=L.r[high].key;
while(low<high && L.r[low].key<=value) low++;
L.r[high].key=L.r[low].key;
}
L.r[low].key=value;
return low;
}
void QuickSort(SqList &L, int low, int high){
if(low<high){
int newvalue=Partition(L,low,high);
QuickSort(L,low,newvalue-1);
QuickSort(L,newvalue+1,high);
}
}
也是排序最基础最典型的算法了,由于快速排序是将比首元素大的值放在它的右边,比首元素小的值放在左边,所以partition函数的作用其一就是进行一个移位操作,第二个作用就是找到首元素应该存在的位置,并将它所在位的下标返回。后面的QuickSort函数就是对左右部分分别进行快排,一直进行下去知道整个序列有序。
理解快速排序的思路,代码也就不难理解了,快排也是笔试题的一个热点,要熟练掌握。
3.二分查找(详见PTA 二分查找 )
本题要求实现二分查找算法。
函数接口定义:
Position BinarySearch( List L, ElementType X );
其中List结构定义如下:
typedef int Position;
typedef struct LNode *List;
struct LNode {
ElementType Data[MAXSIZE];
Position Last; /* 保存线性表中最后一个元素的位置 */
};
L是用户传入的一个线性表,其中ElementType元素可以通过>、==、<进行比较,并且题目保证传入的数据是递增有序的。函数BinarySearch要查找X在Data中的位置,即数组下标(注意:元素从下标1开始存储)。找到则返回下标,否则返回一个特殊的失败标记NotFound。
裁判测试程序样例:
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 10
#define NotFound 0
typedef int ElementType;
typedef int Position;
typedef struct LNode *List;
struct LNode {
ElementType Data[MAXSIZE];
Position Last; /* 保存线性表中最后一个元素的位置 */
};
List ReadInput(); /* 裁判实现,细节不表。元素从下标1开始存储 */
Position BinarySearch( List L, ElementType X );
int main()
{
List L;
ElementType X;
Position P;
L = ReadInput();
scanf("%d", &X);
P = BinarySearch( L, X );
printf("%d\n", P);
return 0;
}
输入样例1:
5
12 31 55 89 101
31
输出样例1:
2
输入样例2:
3
26 78 233
31
输出样例2:
0
代码:
Position BinarySearch( List L, ElementType X ){
int begin=1;
while(begin<=L->Last){
int mid=(begin+L->Last)/2;
if(X==L->Data[mid]) return mid;
else if(X<L->Data[mid]) L->Last=mid-1;
else begin=mid+1;
}
return NotFound;
}
遇到这题不是嘴都要笑歪,过于简单就不解释了。
4.求树高(详见PTA 求二叉树高度 )
本题要求给定二叉树的高度。
函数接口定义:
int GetHeight( BinTree BT );
其中BinTree结构定义如下:
typedef struct TNode *Position;
typedef Position BinTree;
struct TNode{
ElementType Data;
BinTree Left;
BinTree Right;
};
要求函数返回给定二叉树BT的高度值。
裁判测试程序样例:
#include <stdio.h>
#include <stdlib.h>
typedef char ElementType;
typedef struct TNode *Position;
typedef Position BinTree;
struct TNode{
ElementType Data;
BinTree Left;
BinTree Right;
};
BinTree CreatBinTree(); /* 实现细节忽略 */
int GetHeight( BinTree BT );
int main()
{
BinTree BT = CreatBinTree();
printf("%d\n", GetHeight(BT));
return 0;
}
输出样例(对于图中给出的树):
4
代码:
int GetHeight(BinTree BT){
int height=0,l_height=0,r_height=0;
if(BT){
l_height=GetHeight(BT->Left);
r_height=GetHeight(BT->Right);
if(l_height > r_height)
height=l_height+1;
else
height=r_height+1;
}
return height;
}
这里要注意 height , l_height , r_height要初始化,不然你的树会有几亿层高ovo 整体难度不大,递归求解就可以。
5.dfs(详见PTA 邻接矩阵存储图的深度优先遍历)
试实现邻接矩阵存储图的深度优先遍历。
函数接口定义:
void DFS( MGraph Graph, Vertex V, void (*Visit)(Vertex) );
其中MGraph是邻接矩阵存储的图,定义如下:
typedef struct GNode *PtrToGNode;
struct GNode{
int Nv; /* 顶点数 */
int Ne; /* 边数 */
WeightType G[MaxVertexNum][MaxVertexNum]; /* 邻接矩阵 */
};
typedef PtrToGNode MGraph; /* 以邻接矩阵存储的图类型 */
函数DFS应从第V个顶点出发递归地深度优先遍历图Graph,遍历时用裁判定义的函数Visit访问每个顶点。当访问邻接点时,要求按序号递增的顺序。题目保证V是图中的合法顶点。
裁判测试程序样例:
#include <stdio.h>
typedef enum {false, true} bool;
#define MaxVertexNum 10 /* 最大顶点数设为10 */
#define INFINITY 65535 /* ∞设为双字节无符号整数的最大值65535*/
typedef int Vertex; /* 用顶点下标表示顶点,为整型 */
typedef int WeightType; /* 边的权值设为整型 */
typedef struct GNode *PtrToGNode;
struct GNode{
int Nv; /* 顶点数 */
int Ne; /* 边数 */
WeightType G[MaxVertexNum][MaxVertexNum]; /* 邻接矩阵 */
};
typedef PtrToGNode MGraph; /* 以邻接矩阵存储的图类型 */
bool Visited[MaxVertexNum]; /* 顶点的访问标记 */
MGraph CreateGraph(); /* 创建图并且将Visited初始化为false;裁判实现,细节不表 */
void Visit( Vertex V )
{
printf(" %d", V);
}
void DFS( MGraph Graph, Vertex V, void (*Visit)(Vertex) );
int main()
{
MGraph G;
Vertex V;
G = CreateGraph();
scanf("%d", &V);
printf("DFS from %d:", V);
DFS(G, V, Visit);
return 0;
}
输入样例:给定图如下
5
输出样例:
DFS from 5: 5 1 3 0 2 4 6
代码:
void DFS( MGraph Graph, Vertex V, void (*Visit)(Vertex) ){
Visited[V]=true;
Visit(V);
for(int i=0;i<Graph->Nv;i++){
if(Graph->G[V][i]==1 && !Visited[i])
DFS(Graph,i,Visit);
}
}
我说的函数题难就是这种啦,巴拉巴拉的定义一大堆,深搜广搜大家都懂,但就是题目片段给的乱七八糟的定义太多了有时候很容易弄混。思路也不难,首先将未访问的节点标记为已经访问,并由此节点所在的行进行一列一列的判断是否有与之相邻且未被访问的点,如果有,则深搜下去。
6.bfs(详见PTA 邻接矩阵存储图的广度优先遍历)
题目和图都是和dfs那题一样的,这边就不再占用篇幅了。题目是从2开始广搜的。
输出样例:
BFS from 2: 2 0 3 5 4 1 6
代码:
void BFS ( LGraph Graph, Vertex S, void (*Visit)(Vertex) ){
int queue[10001]; //模拟队列
int begin=0,end=0; //begin是队头,end是队尾
queue[end++]=S; //S首先入队
Visited[S]=true;
Visit(S);
PtrToAdjVNode p;
while(begin!=end){
p=Graph->G[queue[begin++]].FirstEdge; //p是指向队头的指针
while (p){
Vertex node=p->AdjV; //记录队头的数据
if(!Visited[node]){
Visited[node]=true;
Visit(node);
queue[end++]=node; //未访问过就入队
}
p=p->Next; //指针后移
}
}
}
此处借用队列先进先出的特性,从一个点开始将与之相邻且未被访问的点全部压入队列,再一个个出队列重复上述操作。
二、编程大题
1.哈夫曼树的带权路径长度
这是往年的机考题,不算难,具体参考我以前发布的博客。偷个懒,顺便给自己打广告
用优先队列STL解决哈夫曼树的带权路径长度
2.链表去重(详见PTA 链表去重)
pta上的题,本题赢数组实现就可以了就是有点繁琐,地址的传递等容易搞晕,有更好的方法的话也可以jjw
这题以前也发过博客,直接参考。偷懒属实被我玩明白了
PTA 链表去重
3.二叉树的遍历问题
新手还在等问题描述和代码,老手已经翻我以前的博客去了。
由树的先序中序序列得到后序遍历序列
⑧难,函数参数的传递要注意,此方法有改良版,可以省略一个参数,具体自己查资料。
4.括号匹配
这题的题干被我弄丢了,总之就是给你几个待查括号字符串,让你看它是不是匹配,是就输出yes,不是就输出no。漏,大漏特漏
#include<bits/stdc++.h>
using namespace std;
int main(){
stack<char> st;
int n,flag=1;
char s[1000];
cin>>n;
for(int i=0;i<n;i++){
scanf("%s",s);
int len=strlen(s);
for(int j=0;j<len;j++){
if(s[j]=='['||s[j]=='(')
st.push(s[j]);
else if(s[j]==']'){
if(!st.empty()&&st.top()=='[') //一定要判断是非空啊,不然段错误
st.pop();
else{
flag=0;
break;
}
}
else if(s[j]==')'){
if(!st.empty()&&st.top()=='(')
st.pop();
else{
flag=0;
break;
}
}
}
if(flag==0||!st.empty()){ //匹配完成栈中仍有元素也是匹配不成功
printf("no\n");
while(!st.empty()){ //清空栈,以便下一个序列的检查
st.pop();
}
flag=1;
}
else
printf("yes\n");
}
return 0;
}
5.dfs和bfs
直接看吧👇
由邻接矩阵存储的图的dfs和bfs
三、小结
1.大家可以发现我总结预测的考试题目都是偏基础但是又十分重要的算法。一方面,这些算法虽然算不上难但却是是讲课的重点,西电的期末考试一年下来大家也有体会了,难度不算高主要还是考查基础知识的掌握,所以这些题目作为机考题还是有很大可能的;另一方面,数据结构如果深入学下去可以说是很难的,而一学期的学习说实话根本学不到什么,如果能掌握这些基础算法我觉得已经不错了,出题老师也不可能刁难大家,因为他要保证及格率哈哈哈。如果上课认真听讲,平时有多加练习,那机考三题满分是绝对没有问题的。
2.对于函数题,也有许多经典的算法我没有举例,比如KMP算法的next数组的求解,克鲁斯卡尔算法,prim算法等,但我觉得作为机考题的可能性不大,因为确实有一点难度。但作为笔试题的概率可以说是百分之一百。
3.最后说一下笔试题吧,笔试题难度真的不大。链表的一些操作,栈和队列的出入顺序,数组的压缩求地址,树与森林的转换,树的遍历,哈夫曼树,二叉排序树,平衡二叉树,哈希表,平均查找长度,深搜广搜,最短路径,各种排序的操作以及时间空间复杂度,各种查找的特性都要掌握,看起来东西很多,但是都不是很难。
4.最后的最后,希望大家期末都能拿一个好成绩,不只是数据结构。
5.最后的最后的最后,如果代码有任何错误,恳请大佬指正,如果有更好的意见,也欢迎交流。