数据结构的一些练习题
记录一下做的一点练习题
1.假设在构造一个无向网的最小生成树的过程中,得到如下边权值序列(以 链表方式存储):
5 → 3 → 4 → 2 → 5 → 4 → 1 → 3 → null
请删除该单链表中的重复项,并将其保存到如下所示的顺序数组中。要求:只能遍历一 次单链表,实现其中重复项的删除,补充如下函数的代码实现。
// 从链表 head 中删除重复节点,并将其存入顺序数组 s 中。其中,n 为数组的长度
void RemoveDuplicates(struct Node* head, int s[], int n)
{
}
一种解答如下:
#include<stdio.h>
int a[100000], b[100000],c[100000],d[100000];
typedef struct Node //定义单链表结点类型
{
int data;
struct Node* next;
}LinkNode;
template<class ElemType>
void CreateListR(LinkNode*& L, ElemType a[], int n)//尾插法
{
LinkNode* s, * r;
int i;
L = new LinkNode; //创建头结点
r = L; //r始终指向尾结点,开始时指向头结点
for (i = 0; i < n; i++) //循环建立数据结点
{
LinkNode* s = new LinkNode;
s->data = a[i]; //创建数据结点*s
r->next = s; //将*s插入*r之后
r = s;
}
r->next = NULL; //尾结点next域置为NULL
}
void DispList(LinkNode*L)
{
LinkNode* p = L->next; //p指向开始结点
while (p != nullptr) //p不为NULL,输出*p结点的data域
{
printf("%d->", p->data);
p = p->next; //p移向下一个结点
}
printf("null\n");
}
void RemoveDuplicates(struct Node* head, int s[], int n)
{
struct Node* Head = head->next; //Head指向第一个有数据的结点
s[0] = Head->data; //data代表权值
for (int i=1; Head->next != nullptr;)
{
d[Head->data] = 1;//通过数组d[]记录该值是否出现过
if (d[Head->next->data] == 1)//判断下一个结点的数据是否出现过,是否重复,如果重复就删掉,指向下一个
{
struct Node* p = Head->next;//记录后继节点
Head->next = Head->next->next;
p->next = nullptr;//将指针置为null
delete(p);//删除内存
}
else//如果没重复就下一个
{
Head = Head->next;
s[i++] = Head->data;
}
}
}
void DestroyListY(LinkNode*& L)
{
LinkNode* p = L,*q; //q指向*p的后继结点
while(p != nullptr) //扫描单链表L
{
q = p->next;
delete(p); //释放*p结点
p = q; //p后一一个结点
} //循环结束时,所有结点已经释放干净
}
int main(void)
{
printf("请输入一个无序链表的结点个数及各个数据(Ex:8 5 3 4 2 5 4 1 3):");
int n2;
scanf_s("%d", &n2);
for (int i = 0; i < n2; i++)
{
scanf_s("%d", &c[i]);
}
LinkNode* L2;
CreateListR(L2, c, n2);
printf("所创建的链表为:");
DispList(L2);
printf("删除重复元素后的链表为:");
//Listdeleteunsorted(L2);
int s[100] = {0};
RemoveDuplicates(L2, s, 100);
DispList(L2);
for (int i=0;i<8;i++)
{
printf("%d ", s[i]);
}
DestroyListY(L2);
}
输出结果:
2.利用带有哨兵的插入排序,对题 1得到的数组,按照从小到大进行排序,得 到如下所示的有序数组。请按照提示补充如下函数的代码实现部分。
// 带有哨兵的插入排序,s[0]做监视哨
void InsertSort(int s[], int n)
{
}
一种解答如下:
#include <iostream>
using namespace std;
void InsertSort(int s[], int n)
{
int i, j;
for (i = 2; i <= n; i++)
{
if (s[i] < s[i - 1])
{
s[0] = s[i];//将要插入的值赋值给哨兵
s[i] = s[i - 1]; //小于哨兵的向后挪一位
for (j = i - 2; s[j] > s[0]; j--) //纪录后移
{
s[j + 1] = s[j];
}
s[j + 1] = s[0]; //插入到正确位置
}
}
}
int main() {
int n=6;
int s[100] = {0,5,3,4,2,1};
//s[0]空出来设置哨兵
//调用插入排序函数
InsertSort(s, n);
//输出排序后的数列
for (int i = 1; i <= n; i++)
cout << s[i] << " ";
return 0;
}
输出结果:
3.赫夫曼树
假设题 2 得到的有序序列分别对应字符 “A”, “B”, “C”, “D”, “E” 的发生频率,请据此频率分布,编程实现赫夫曼树的构建,得到一棵赫夫曼树 T。 请按照提示补充如下函数的代码实现部分。
// 不同字符对应的发生频率存在 s 数组中,n 为字符数,要求构造赫夫曼树 T
void HuffmanConstructing(HuffmanTree &T, int *s, int n)
{
}
这题直接权值已经排好序了,权值也已经统计了,如果没给的话需要自己统计,然后排个序比较好。
对于此题如果没有下面第四题按理可以用结构体数组做,当然也是我不会用数组做第四题。
结构体数组解答如下:
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
using namespace std;
//HuffmanTree的结构
typedef struct Node
{
int weight;//叶子结点权值
int parent;//指向双亲,和孩子结点的指针
int lchild;
int rchild;
}HTNode, * HuffmanTree;
typedef char* HuffmanCode; //动态分配数组,存储哈夫曼编码表
//选择两个parent为0,且weight最小的结点s1和s2
//n 为叶子结点的总数,s1和 s2两个指针参数指向要选取出来的两个权值最小的结点
void select(HuffmanTree T, int n, int* s1, int* s2)
{
int i = 0;
int min=0; //最小权值
for (i = 1; i <= n; i++)//遍历全部结点,找出单节点
{
//如果此结点的父亲没有,那么把结点号赋值给 min,跳出循环
if (T[i].parent == 0)
{
min = i;
break;
}
}
//继续遍历全部结点,找出权值最小的单节点
for (i = 1; i <= n; i++)
{
//如果此结点的父亲为空
if (T[i].parent == 0)
{
//如果此结点的权值比 min 结点的权值小,那么更新 min 结点,否则就是最开始的 min
if (T[i].weight < T[min].weight)
{
min = i;
}
}
}
*s1 = min;//找到了第一个最小权值的结点,s1指向
//遍历全部结点
for (i = 1; i <= n; i++)
{
//找出下一个单节点,且没有被 s1指向,那么i 赋值给 min,跳出循环
if (T[i].parent == 0 && i != (*s1))
{
min = i;
break;
}
}
//继续遍历全部结点,找到权值最小的那一个
for (i = 1; i <= n; i++)
{
if (T[i].parent == 0 && i != (*s1))
{
//如果此结点的权值比 min 结点的权值小,那么更新 min 结点,否则就是最开始的 min
if (T[i].weight < T[min].weight)
{
min = i;
}
}
}
//找到第二个权值最小的叶子结点
*s2 = min;
}
//s数组存放权值
void HuffmanTreeConstructing(HuffmanTree& T, int* s, int n)
{
if (n <= 1) return; // 如果只有一个编码就相当于0
int m = 2 * n - 1;//m:总结点数,n :叶子结点数
//s1 和 s2 为两个当前结点里,要选取的最小权值的结点
int s1;
int s2;
int i=0;
T = new HTNode[m + 1]; //0号单元未用 当指向0号时表示没有结点
for(i = 1; i <= n; i++,s++)//1--n号存放叶子结点,初始化叶子结点,结构数组来初始化每个叶子结点,
{
T[i].weight = *s;
T[i].lchild = 0;
T[i].parent = 0;
T[i].rchild = 0;
}
for (i = n + 1; i <= m; i++)//非叶子结点的初始化
{
T[i].weight = 0;
T[i].lchild = 0;
T[i].parent = 0;
T[i].rchild = 0;
}
for (i = n + 1; i <= m; i++)//建哈夫曼树
{
//在T[1]~T[i-1]的范围内选择两个parent为0且weight最小的两个结点,其序号分别为s1、s2
select(T, i - 1, &s1, &s2);
//选出的两个权值最小的叶子结点,组成一个新的二叉树,根为 i 结点
T[s1].parent = i;
T[s2].parent = i;
T[i].lchild = s1;
T[i].rchild = s2;
//新的结点 i 的权值
T[i].weight = T[s1].weight + T[s2].weight;
printf("%d (%d, %d)\n", T[i].weight, T[s1].weight, T[s2].weight);
}
}
int cmp(const void* a, const void* b)
{
struct Node* t1 = (struct Node*)a, * t2 = (struct Node*)b;
return (t1->weight <= t2->weight);
}
int main(void)
{
HuffmanCode HC;
int s[5] = { 1,2,3,4,5 };
int n = 5;
HuffmanTree T;
HuffmanTreeConstructing(T,s, n);
return 0;
}
链表的方式存储树的解答:
#include <iostream>
using namespace std;
#define N 20
#define INF 99999
int visted[100] = { 0 }; //用来判断节点是否被选取过
//建立哈夫曼树的结构体
typedef struct node
{
int data; //存放权值
struct node* lchild; //定义各节点
struct node* rchild;
struct node* parent;
} BTNode,*HuffmanTree;
void select(HuffmanTree T, int end, int& min1, int& min2)
//选择两个最小的权值,序号由min1,min2表示
{
int min = INF; //先将最小值设定足够大
for (int j = 0; j <= end; j++) //选取最小值
{
if (T[j].data < min && visted[j] == 0)
{
min = T[j].data;
min1 = j;
}
}
min =INF;
for (int j = 0; j <= end; j++) //选取次小值
{
if (T[j].data < min && j != min1 && visted[j] == 0)
{
min = T[j].data;
min2 = j;
}
}
}
void HuffmanTreeConstructing(HuffmanTree& T, int*s,int n) //创建哈夫曼树
{
for (int i = 0; i < n; i++) //n个权值的二叉树的哈夫曼树有2n-1个结点
{
T[i].data = s[i]; //初始化权重
}
for (int i = 0; i < 2 * n - 1; i++) //n个权值的二叉树的哈夫曼树有2n-1个结点
{
T[i].lchild = T[i].rchild = T[i].parent = nullptr; //初始化各指针
}
int min1 = 0; //排序后最小的两个权值的编号为0和1
int min2 = 1;
for (int i = n; i < 2 * n - 1; i++)
{
visted[min1] = visted[min2] = 1; //已经用于构造了
T[i].lchild = &T[min1];
T[i].rchild = &T[min2];
T[i].data = T[min1].data + T[min2].data;
select(T, i, min1, min2); //选择最小的两个权值
}
T= &T[2*n-2];//将其指向根节点
}
void DispBTNode(BTNode* T) //遍历
{
if (T != nullptr) //树不为空
{
cout << T->data;
if (T->lchild != NULL || T->rchild != NULL)
{
cout << "("; //有左子树输出左括号
DispBTNode(T->lchild);
if (T->rchild != NULL) //有右子树 输出右括号
cout << ",";
DispBTNode(T->rchild);
cout << ")";
}
}
}
int ComputeBiTreeDepth(HuffmanTree T)
{
if (T == nullptr)return 0;
int ldepth = ComputeBiTreeDepth(T->lchild);
int rdepth = ComputeBiTreeDepth(T->rchild);
return ldepth > rdepth ? ldepth+1 : rdepth+1;
}
int main()
{
int n;
cout << "*** 哈夫曼树的建立 ***" << endl;
n = 5;
//cin >>n;
BTNode w[2 * N - 1];
BTNode* m = &w[0];
int s[5] = { 1,2,3,4,5 };
HuffmanTreeConstructing(m,s, n);
cout << endl;
cout << "该哈夫曼树为:" << endl;
DispBTNode(m);
cout <<"\n树的高度为:"<< ComputeBiTreeDepth(m);
return 0;
}
输出结果如下:
4.请用递归方式,编程求解题3得到的二叉树T深度的函数ComputeBiTreeDepth。 其中,
/二叉树 T, T.lchild 为 T 的左子树,T.rchild 为 T 的右子树,操作结果: 返回 T 的深度/
int ComputeBiTreeDepth(HuffmanTree T)
{
}
解答同上:
int ComputeBiTreeDepth(HuffmanTree T)
{
if (T == nullptr)return 0;
int ldepth = ComputeBiTreeDepth(T->lchild);
int rdepth = ComputeBiTreeDepth(T->rchild);
return ldepth > rdepth ? ldepth+1 : rdepth+1;
}