考研计算机874数据结构知识整理Apare_xzc
for zcy勇哥
2020.11.17
一、题目类型
10道填空题
4道综合题
2道算法题(出自历年期末考试,主要考链表和二叉树)
二、综合题的考点
- 二叉树的遍历(给出先序中序还原二叉树,求后序)
- 线索二叉树(画出)
- 排序算法的比较(最好最坏时间空间复杂度,稳定性)
- 哈夫曼编码(给定字母出现概率,生成哈夫曼树,求每个字母的编码,以及带权路径总长度WPL)
- 最小生成树(Prim, Kruskal)
- 堆排序相关(初始建堆的结果,每一趟的结果)
- 哈希表(给出待散列的序列,除留取余法,链地址法处理冲突,求出每个元素的散列地址,求平均查找长度)
- 最短路径(Dijkstra)
- 折半查找(画出判定树,求平均查找长度(成功/失败),求查找某个值得过程)
- 图的综合题(给一个图,求邻接表,求深度优先遍历的结果,根据Prim构造最小生成树)
- 二叉排序树(给一个线性表序列,插入到空的二叉排序树中,求出其平均查找长度)
三、算法编程题
2020年:统计字符串中单词出现的个数(直接C++map,java Hashmap, 或者用有序链表维护)
2019年:求二叉树的宽度,删除有序链表在(mink,maxk)范围的结点
2018年:在用双向链表实现的有序表中检索具有关键字key的结点, 二叉排序树的查找、插入(非递归)
2017年:有序链表中插入元素,二叉排序树的查找、插入(非递归)
2016年:求链表中结点为值为偶数的结点之和,求二叉树叶子结点值之和
2015,2014年:求链表结点值能被5整除/能被2整除的个数(链表的遍历)
2013年:送分水题,输入一行字符,统计其中数字,字母,空格和其它字符的个数。
四、填空选择考点
广义表
二叉树的遍历
图的广度优先遍历
哈希表
归并排序的趟数
中缀,后缀表达式,表达式树
单链表的插入,删除,遍历
循环队列的实现,栈的实现(top,head,tail如何赋值)
拓扑排序
二分查找,插入二叉排序树,插入堆的复杂度O(log2n)
有向图、无向图的最大边数,顶点度数
顺序存储,链式存储
二叉树叶子结点的个数
可能的出栈顺序
初始堆
快排时间复杂度,第一次划分后的结果
算法题详解:
1. [2020年真题]统计字符串中单词出现的个数,按照字典序输出。
可以用有序链表维护。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct ListNode{
//链表结点的定义
int cnt; //这个单词出现的次数
char word[20]; //记录单词
struct ListNode * next;
}ListNode;
int main(void) {
char str[20];
ListNode * head = (ListNode*)malloc(sizeof(ListNode)); //头结点
head->next = NULL;
ListNode * p = head;
while(scanf("%s",str)!=EOF) {
p = head;
int flag = 0;
while(p->next) {
//从前往后遍历链表
if(strcmp(p->next->word,str)==0) {
p->next->cnt += 1;
flag = 1;
break;
}
else if(strcmp(p->next->word,str)>0) break;
else p = p->next;
}
if(!flag) {
p = head;
while(p->next&&strcmp(p->next->word,str)<0) //找到插入的位置
p = p->next;
ListNode * tmp = (ListNode*)malloc(sizeof(ListNode));
tmp->cnt = 1;
strcpy(tmp->word,str);
tmp->next = p->next;
p->next = tmp;
}
}
p = head;
while(p->next) {
printf("%s\t%d\n",p->next->word,p->next->cnt);
p = p->next;
}
return 0;
}
2. [2019年真题]求二叉树的宽度
维护一个CurCnt变量,记录上一层在队列中的结点数。当CurCnt为0时,说明此时队列中都是这一层的。于是用Q.size()更新答案
核心代码:
int GetWidthOfBinaryTree(TreeNode * root) {
if(!root) return 0;
std::queue<TreeNode*> Q;
Q.push(root);
int CurCnt = 1,MaxWidth=0;
while(!Q.empty()) {
TreeNode * tp = Q.front(); Q.pop();
if(tp->lson) Q.push(tp->lson);
if(tp->rson) Q.push(tp->rson);
if(--CurCnt==0) {
MaxWidth = max(MaxWidth,(int)Q.size());
CurCnt = Q.size();
}
}
return MaxWidth;
}
#include <stdio.h>
#include <stdlib.h>
#include <queue>
#define maxn 100
#define max(a,b) (a>b?a:b)
using namespace std;
struct TreeNode{
char val;
struct TreeNode * lson;
struct TreeNode * rson;
};
char sequence[maxn] = "ABD#G##EH##I##C#FJ###";
TreeNode * buildTree(char * str,int& x) {
if(str[x]=='#') {
x++;
return NULL;
}
TreeNode * pNode = (TreeNode*)malloc(sizeof(TreeNode));
pNode->val = str[x++];
pNode->lson = buildTree(str,x);
pNode->rson = buildTree(str,x);
return pNode;
}
int GetWidthOfBinaryTree(TreeNode * root) {
if(!root) return 0;
std::queue<TreeNode*> Q;
Q.push(root);
int CurCnt = 1,MaxWidth=0;
while(!Q.empty()) {
TreeNode * tp = Q.front(); Q.pop();
if(tp->lson) Q.push(tp->lson);
if(tp->rson) Q.push(tp->rson);
if(--CurCnt==0) {
MaxWidth = max(MaxWidth,(int)Q.size());
CurCnt = Q.size();
}
}
return MaxWidth;
}
int main(void) {
int idx = 0;
TreeNode * root = buildTree(sequence,idx);
printf("二叉树的宽度为:%d\n",GetWidthOfBinaryTree(root));
return 0;
}
3. [2019年真题]删除有序链表在(mink,maxk)范围的结点
核心代码如下:
void DeleteNode(ListNode * head,int mink,int maxk) {
//head为不记录数据的头结点指针
ListNode * p = head;
while(p->next) {
if(p->next->val>mink && p->next->val<maxk) {
ListNode * tmp = p->next;
p->next = tmp->next;
free(tmp);
}
else p = p->next;
}
}
#include <stdio.h>
#include <stdlib.h>
struct ListNode{
int val;
struct ListNode * next;
};
void InsertNode(ListNode * head,int v) {
ListNode * p = head;
while(p->next&&p->next->val<=v) p = p->next;
ListNode * tmp = (ListNode*)malloc(sizeof(ListNode));
tmp->val = v;
tmp->next = p->next;
p->next = tmp;
}
void ShowList(ListNode * p) {
while(p) printf("%d ",p->val),p = p->next;
printf("\n");
}
void DeleteNode(ListNode * head,int mink,int maxk) {
ListNode * p = head;
while(p->next) {
if(p->next->val>mink && p->next->val<maxk) {
ListNode * tmp = p->next;
p->next = tmp->next;
free(tmp);
}
else p = p->next;
}
}
int main(void) {
ListNode * head = (ListNode*)malloc(sizeof(ListNode));
head->next = NULL;
InsertNode(head,5);
InsertNode(head,5);
InsertNode(head,6);
InsertNode(head,1);
InsertNode(head,4);
InsertNode(head,7);
InsertNode(head,2);
InsertNode(head,3);
InsertNode(head,9);
InsertNode(head,0);
ShowList(head->next);
DeleteNode(head,2,6);
ShowList(head->next);
return 0;
}
4. [2013年真题]输入一行字符,统计数字,英文字母,空格,其它字符的个数
#include <stdio.h>
#include <algorithm>
char str[200];
int main(void) {
int space=0,letter=0,digit=0,other=0;
gets(str); //std::cin.getline(str,200) <iostream>
for(int i=0;str[i];++i) {
if(str[i]==' ') space++;
else if(isdigit(str[i])) digit++;
else if(isalpha(str[i])) letter++;
else other++;
}
printf("空格:%d个\n",space);
printf("字母:%d个\n",letter);
printf("数字:%d个\n",digit);
printf("其它:%d个\n",other);
return 0;
}
六、综合题要点
1. 堆排序,建堆,每一趟的过程
[2018年第13题] 从空堆开始插入{64,52,12…} 每趟的结果
#include <stdio.h>
int a[100];
int b[] = {
0,64,52,12,48,45,26};
//int b[] = {0,1,4,5,2,3,8,100,6,7,4,4,3};
//小顶堆
void AdjustToHeap(int * a,int n) {
for(int i=n/2;i>=1;--i) {
int j = i,tmp = a[j];
while(j*2<=n) {
int son = j*2;
if(son+1<=n&&a[son+1]<a[son]) son = son+1;
if(a[son]<tmp) a[j] = a[son], j = son;
else break;
}
a[j] = tmp;
}
}
int main(void) {
int n = 6;
// n = 12;
for(int i=1;i<=n;++i) {
a[i] = b[i];
AdjustToHeap(a,i);
for(int j=1;j<=i;++j)
printf("%d ",a[j]);printf("\n");
}
return 0;
}
答案:
64
52 64
12 64 52
12 48 52 64
12 45 52 64 48
12 45 26 64 48 52
堆排序的实现:
#include <stdio.h>
const int maxn = 200;
int a[maxn] = {
0,1,4,5,2,3,8,100,6,7,4,4,3};
//int a[maxn] = {0,8,7,4,2,5,1};
//下表都从1开始
//从小到大排序,维护一个大顶堆
void AdjustOnce(int * a,int pos,int n) {
int tmp = a[pos];
int j = pos;
while(j*2<=n) {
int son = j*2;
if(son+1<=n&&a[son+1]>a[son]) son = son+1;
if(a[son]>tmp) a[j] = a[son], j = son;
else break;
}
a[j] = tmp;
}
void toHeap(int *a,int n) {
for(int i=n/2;i>=1;--i) {
AdjustOnce(a,i,n);
}
}
void HeapSort(int *a,int n) {
//a[1]到a[n]排序
toHeap(a,n);
int m = n;
for(int i=1;i<n;++i) {
int tmp = a[m]; a[m] = a[1]; a[1] = tmp; //把堆顶的最大元素换到最后一个位置
AdjustOnce(a,1,--m);
}
for(int i=1;i<=n;++i)
printf("%d ",a[i]);printf("\n");
}
int main(void) {
int n = 12;
// n = 6;
// scanf("%d",&n);
// for(int i=1;i<=n;++i)
// scanf("%d",&a[i]);
HeapSort(a,n);
return 0;
}
2. Huffman数
[2016年,第13题] abcdefgh的概率为[9,16,22,18,3,15,12,5]写出哈夫曼编码
#include <stdio.h>
#include <queue>
const int maxn = 100;
char str[] = "abcdefgh";
int Lson[maxn];
int Rson[maxn];
bool vis[maxn];
//int a[maxn] = {5,29,7,8,14,23,3,11};
//int a[maxn] = {10,25,20,30,15};
//int a[maxn] = {3,7,8,2,6,10,14};
int a[maxn] = {
9,16,22,18,3,15,12,5};
char record[maxn];
void dfs(int rt,int x,int& wpl) {
if(rt==-1) return;
if(Lson[rt]==-1 && Rson[rt]==-1) {
//叶子结点
wpl += a[rt]*x;
record[x] = '\0';
printf("%c: %s\n",str[rt],record);
return;
}
if(Lson[rt]!=-1) {
record[x] = '0';
dfs(Lson[rt],x+1,wpl);
}
if(Rson[rt]!=-1) {
record[x] = '1';
dfs(Rson[rt],x+1,wpl);
}
}
void getHuffmanCommon() {
int n = 8;
int m = n;
for(int i=0;i<n;++i)
vis[i] = false, Lson[i] = -1, Rson[i] = -1;
for(int i=1;i<n;++i) {
int minId1 = -1, minId2 = -1;
for(int j=0;j<m;++j) {
if(!vis[j]&&(minId1==-1||a[j]<a[minId1])) minId1 = j;
}
vis[minId1] = true;
for(int j=0;j<m;++j) {
if(!vis[j]&&(minId2==-1||a[j]<a[minId2])) minId2 = j;
}
vis[minId2] = true;
a[m] = a[minId1]+a[minId2];
Lson[m] = minId1;
Rson[m] = minId2;
m++;
}
int WPL = 0;
dfs(m-1,0,WPL);
printf("带权路径长度WPL为:%d\n",WPL);
}
int main(void) {
// getHuffmanUsingHeap();
getHuffmanCommon();
return 0;
}
拓展:用堆优化Huffman
void getHuffmanUsingHeap() {
int n = 8,m = n;
using std::pair;
using std::vector;
using std::priority_queue;
using std::greater;
using std::make_pair;
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > Q;
for(int i=0;i<n;++i)
vis[i] = false, Lson[i] = -1, Rson[i] = -1, Q.push(make_pair(a[i],i));
for(int i=1;i<n;++i) {
int minId1 = Q.top().second; Q.pop();
int minId2 = Q.top().second; Q.pop();
a[m] = a[minId1]+a[minId2];
Lson[m] = minId1;
Rson[m] = minId2;
Q.push(make_pair(a[m],m));
m++;
}
int WPL = 0;
dfs(m-1,0,WPL);
for(int i=0;i<m;++i)
{
printf("%2d %d %d\n",a[i],Lson[i],Rson[i]);
}
printf("带权路径长度WPL为:%d\n",WPL);
}
最小生成树
- Kruskal求MST
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
const int maxn = 200;
struct Edge{
int u,v,dis;
bool operator < (const Edge& rhs)const{
return dis < rhs.dis;
}
}edge[maxn];
struct UFSet{
int father[maxn];
void init() {
for(int i=0;i<maxn;++i)
father[i] = i;
}
int getFather(int x) {
return x==father[x]?x:father[x]=getFather(father[x]);
}
bool join(int x,int y) {
int fx = getFather(x), fy = getFather(y);
if(fx==fy) return false;
father[fy] = fx;
return true;
}
}ufset;
int main(void) {
int n;
scanf("%d",&n);
for(int i=0;i<n;++i)
scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].dis);
std::sort(edge,edge+n);
int cnt = 0;
int ans = 0;
ufset.init();
for(int i=0;i<n&&cnt<n-1;++i) {
if(ufset.join(edge[i].u,edge[i].v)) {
cnt += 1;
ans += edge[i].dis;
}
}
printf("%d\n",ans);
return 0;
}
/*
5
1 2 1
2 3 2
3 4 4
4 5 8
5 1 16
*/
未完待续…