做题笔记:数据结构

数据结构的一些练习题

记录一下做的一点练习题

1.假设在构造一个无向网的最小生成树的过程中,得到如下边权值序列(以 链表方式存储):
5 → 3 → 4 → 2 → 5 → 4 → 1 → 3 → null
请删除该单链表中的重复项,并将其保存到如下所示的顺序数组中。要求:只能遍历一 次单链表,实现其中重复项的删除,补充如下函数的代码实现。
5 3 4 2 1

// 从链表 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得到的数组,按照从小到大进行排序,得 到如下所示的有序数组。请按照提示补充如下函数的代码实现部分。
1 2 3 4 5
// 带有哨兵的插入排序,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;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值