数据结构实验四 查找和排序算法实现

实验四  查找和排序算法实现

一、实验目的:

1、领会折半查找的过程和算法设计;

2、领会二叉排序树的定义,二叉树排序树的创建、查找和删除过程及其算法设计;

3、领会快速排序的过程和算法设计;

4、领会堆排序的过程和算法设计;

5、掌握二路归并排序算法及其应用。

二、实验类型: 验证性/设计性

三、实验学时:4学时

四、实验教学的重点和难点

重点:查找和内排序的重要算法

难点查找和排序算法的比较及应用

五、实验内容

1、教材P362实验题2:实现折半查找的算法

编写一个程序exp9-2.cpp,输出在顺序表(1,2,3,4,5,6,7,8,9,10)中采用折半查找方法查找关键字9的过程。

#include<iostream>
#include<vector>
using namespace std;
typedef int Keytype;	// 定义关键字类型为int
struct RecType {
	Keytype key;	// 关键字
};
class solution {
public:
	void BinarySearch(vector<RecType>& R, Keytype k) {
		int left = 0, right = R.size() - 1, middle;
		while (left <= right) {	// 在[left,right]中寻找
			middle = left + (right - left) / 2;	// 防止溢出(一般不担心)
			if (R[middle].key > k) {
				cout << k << "可能在顺序数组下标" << left << "-" << middle - 1 << "之间\n";
				right = middle - 1;	// k在左区间,所以[left,middle-1]
			}
			else if (R[middle].key < k) {
				cout << k << "可能在顺序表下标" << middle + 1 << "-" << right << "之间\n";
				left = middle + 1;	// k在右区间,所以[middle+1,right]
			}
			else {
				cout << k << "在顺序中" << middle << "的位置\n";
				return;
			}
		}
		cout << k << "不在顺序表中\n";
	}
};
int main() {
	vector<RecType>T(10);
	for (int i = 1; i < 11; i++)T[i - 1].key = i;	// 给顺序表赋值
	solution s;
	s.BinarySearch(T, 9);
}

2、教材P362实验题4:实现二叉排序树的基本运算算法

编写一个程序bst.cpp,包含二叉排序树的创建、查找和删除算法,再次基础上编写exp9-4.cpp程序完成以下功能。

  1. 由关键字序列(4,9,0,1,8,6,3,5,2,7)创建一棵二叉排序bt并以括号表示法输出。
  2. 判断bt是否为一棵二叉排序树。
  3. 采用递归和非递归两种方法查找关键字为6的结点,并输出其查找路径。
  4. 分别删除bt中关键字为4和5的结点,并输出删除后的二叉排序。
#include<iostream>
#include<vector>
using namespace std;
typedef int Keytype;
struct BSTNode {	// 二叉排序树结点
	Keytype key;	// 关键字
	BSTNode* lchild;	// 左孩子
	BSTNode* rchild;	// 右孩子
	BSTNode(Keytype x) :key(x), lchild(nullptr), rchild(nullptr) {}
};
class solution {
public:
	vector<Keytype>path;
	BSTNode* CreateBST(vector<Keytype>p) {	// 创建二叉排序树
		BSTNode* root = nullptr;	// 一开始根节点为空
		int i = 0;
		while (i < p.size()) {
			root = InsertBST(root, p[i]);	// 将关键字p[i]插入二叉排序树root中
			i++;
		}
		return root;
	}
	BSTNode* InsertBST(BSTNode* root, Keytype k) {	// 二叉排序树的插入
		if (!root)root = new BSTNode(k);	// 原(子)树为空,创建新结点	二叉排序树无重复关键字
		else if (k < root->key)root->lchild = InsertBST(root->lchild, k);	// 比当前结点小则插入当前结点的左子树中
		else if (k > root->key)root->rchild = InsertBST(root->rchild, k);	// 比当前结点大则插入当前结点的右子树中
		return root;	// 返回插入后的根节点
	}
	void PrintfBST(BSTNode* root) {	// 括号表示法输出二叉排序树
		if (root) {
			cout << root->key;
			if (root->lchild || root->rchild) {	// 根结点存在子树则输出括号
				cout << "(";
				PrintfBST(root->lchild);	// 递归左子树
				if (root->rchild)cout << ",";	// 有右子树才有逗号
				PrintfBST(root->rchild);	// 递归右子树
				cout << ")";
			}
		}
	}
	bool LikeBST(BSTNode* root, int min = 0x8000, int max = 0x7fff) {	// 判断是否为二叉排序树
		if (!root)return 1;	// 空树也是二叉排序树
		if (root->key<=min || root->key>=max)return 0;	// 取等于是因为二叉排序树关键字不重复,超出范围说明不是二叉排序树
		return LikeBST(root->lchild, min, root->key) && (root->rchild, root->key, max);
	}
	void SearchBST1(BSTNode* root, Keytype k, vector<Keytype>path) {	// 递归查找二叉树中关键字为k的结点
		if (!root) {
			cout << "\n查找失败\n";
			return;
		}
		if (k == root->key) {	// 找到,输出路径
			path.push_back(root->key);	// 存入路径
			cout << "\n查找成功,路径:";
			for (int i = 0; i < path.size(); i++)cout << path[i] << " \n"[i == path.size() - 1];	// 输出路径
			path.clear();	// 清空,下次继续用
			return;
		}
		if (k < root->key) {	// 小于当前结点值则在左区间
			path.push_back(root->key);	// 存入路径
			SearchBST1(root->lchild, k, path);	// 进入左子树找
		}
		else {	// 大于当前结点值则在右区间
			path.push_back(root->key);	//存入路径
			SearchBST1(root->rchild, k, path);	// 进入柚子树找
		}
	}
	void SearchBST2(BSTNode* root, Keytype k, vector<Keytype>path) {	// 非递归查找二叉树中关键字为k的结点
		BSTNode* p = root;	// 工作指针
		while (p) {
			if (p->key == k) {
				path.push_back(p->key);	// 存入路径
				cout << "\n查找成功,路径:";
				for (int i = 0; i < path.size(); i++)cout << path[i] << " \n"[i == path.size() - 1];	// 输出路径
				path.clear();	// 清空,下次继续用
				return;
			}
			if (k < p->key) {	// 小于当前结点值则在左区间
				path.push_back(p->key);	// 存入路径
				p = p->lchild;	// 进入左子树找
			}
			else {	// 大于当前结点值则在右区间
				path.push_back(p->key);	//存入路径
				p = p->rchild;	// 进入柚子树找
			}
		}
		cout << "\n查找失败\n";
	}
	void DeleteBST(BSTNode* root, Keytype k) {	// 删除关键字为k的结点
		BSTNode* p = root, * f = root;	// f指向被删结点的双亲
		while(p){	// 直至找到被删节点p时退出循环
			if (p->key == k)break;
			f = p;
			if (k < p->key)p = p->lchild;	// 小于当前结点,进入左子树
			else p = p->rchild;	// 大于当前结点,进入右子树
		}
		if (!p)return ;	// 没有找到被删结点p
		BSTNode* tem = p;	// 开始删除
		if (!p->lchild && !p->rchild) {	// 叶子结点
			if (p == root)root = nullptr;	// 如果是根节点
			else {
				if (f->lchild = p)f->lchild = nullptr;	// 结点p是f的左孩子
				else f->rchild = nullptr;	// 结点p是f的右孩子
			}
			delete p;
			return;
		}
		else if (!p->rchild)p = p->lchild;	// 无柚子树,只用重接左子树;先指向,最后再接
		else if (!p->lchild)p = p->rchild;	// 无左子树,只用重接柚子树;先指向,最后再接 
		else {	// 有左右子树
			BSTNode* q = p->lchild;	// q指向p的左孩子
			while (q->rchild) {	// 在*p的左子树中继续查找其前驱结点,即最右下结点
				f = q;
				q = q->rchild;	// 右到尽头
			}
			p->key = q->key;	// q指向被删结点的“前驱”
			if (q == f->lchild)f->lchild = q->lchild; // q.rchild不存在没发生移动,则直接重接左子树
			else f->rchild = q->lchild;	// q.rchild存在,发生移动,q无rchild因为q是最右下节点,则将q的左子树重接到q父母指向下的q的位置
			delete q;
			return;
		}
		if (f->lchild == tem)f->lchild = p;	// 挂到*f左子树上面
		else f->rchild = p;	// 挂到*f右子树上面
		delete tem;
	}
};
int main() {
	solution s;
	vector<int>p{ 4,9,0,1,8,6,3,5,2,7 };	// 关键字序列
	BSTNode* root = s.CreateBST(p);	// 创建一棵二叉排序树
	s.PrintfBST(root);	// 括号法输出二叉排序树	
	s.SearchBST1(root, 6, s.path);	// 递归查找关键字为6的结点,并输出其查找路径
	s.SearchBST2(root, 6, s.path);	// 非递归查找关键字为6的结点,并输出其查找路径
	s.DeleteBST(root, 4);	// 删除关键字为4的结点
	s.DeleteBST(root, 5);	// 删除关键字为5的结点
	s.PrintfBST(root);	// 括号法输出二叉排序树	
}

3、实现快速排序算法

编写一个程序exp4-3.cpp实现快速排序算法,用相关数据进行测试并输出各趟的排序结果。

#include<iostream>
#include<vector>
using namespace std;
typedef int KeyType;
struct RecType {
	KeyType key;	// 关键字
	RecType(KeyType x) :key(x) {}
};
struct	Sqlist {
	vector<RecType>p;	// 顺序表
};
class solution {
public:
	Sqlist* CreateList(Sqlist*& q, vector<KeyType>list) {	// 创建顺序表
		for (int i = 0; i < list.size(); i++)q->p.push_back(list[i]);	// 一个个存进去
		return q;
	}
	int Once(Sqlist* q, int left, int right) {	// 划分一次
		while (left < right) {
			while (left < right && q->p[left].key <= q->p[right].key)right--;	// 直到找到比left小的
			if (left < right)swap(q->p[left++], q->p[right]);	// 防止超越了还要判断一下,交换这两个的位置,交换完之后记得往中间走一步
			while (left < right && q->p[left].key <= q->p[right].key)left++;	// 直到找到比right大的
			if (left < right)swap(q->p[left], q->p[right--]);	// 防止超越了还要判断一下,交换这两个的位置,交换完之后记得往中间走一步
		}
		return left;	// 返回他们两个相等的位置
	}
	void QuickSort(Sqlist* q, int left, int right) {	//快速排序
		if (left < right) {	// 至少两元素(只靠近不超越)	其实是分治思想,划分成更小的子题日
			QuickSort(q, left, Once(q, left, right) - 1);	// 划分成左右两块分别更小的
			QuickSort(q, Once(q, left, right) + 1, right);
		}
	}
	void PrintfSL(Sqlist* q) {	// 输出顺序表
		for (int i = 0; i < q->p.size(); i++)cout << q->p[i].key << " \n"[i == q->p.size() - 1];
	}
};
int main() {
	vector<KeyType>list{ 4,9,0,1,8,6,3,5,2,7 };	// 测试用的初始序列
	Sqlist* p = new Sqlist;
	solution s;
	p=s.CreateList(p, list);	// 创建顺序表
	cout << "初始序列\n";
	s.PrintfSL(p);	// 打印顺序表初始序列
	s.QuickSort(p, 0, p->p.size()-1);	// 快速排序
	cout << "快速排序后的序列\n";
	s.PrintfSL(p);	// 打印排序后顺序表序列
}

4、教材P397实验题7:实现堆排序算法

编写一个程序exp10-7.cpp实现堆排序算法,用相关数据进行测试并输出各趟的序结果。

#include<iostream>
#include<vector>
using namespace std;
typedef int KeyType;
struct RecType {
	KeyType key;	// 关键字
	RecType(KeyType x) :key(x) {}
};
struct	Sqlist {
	vector<RecType>p;	// 顺序表
};
class solution {
public:
	Sqlist* CreateList(Sqlist*& q, vector<KeyType>list) {	// 创建顺序表
		q->p.push_back(NULL);	// 先存个空的元素进去,方便后续操作
		for (int i = 0; i < list.size(); i++)q->p.push_back(list[i]);	// 一个个存进去
		return q;
	}
	void sift(Sqlist* q, int low, int high) {	// 堆排序向下筛选算法
		int i = low;
		int j = 2 * i;	// p[j]是p[i]的左孩子
		RecType tem = q->p[i];
		while (j <= high) {
			if (j < high && q->p[j].key < q->p[j + 1].key)j++;	// 将j指向俩孩子之中较大的值
			if (tem.key < q->p[j].key) {	// 根节点小于最大孩子的关键字
				swap(q->p[i], q->p[j]);	// 将p[j]调整到双亲结点位置上
				i = j;	// 向下深入要筛选的子结点
				j = 2 * i;	// 修改i和j的值,以便继续向下筛选
			}
			else break;	// 根结点大于等于最大孩子的关键字,筛选结束
		}
	}
	void HeapSort(Sqlist* q, int n) {	// 实现堆排序,n为结点个数q.p.size()-1(从1开始,方便操作,舍弃0号位)
		for (int i = n / 2; i >= 1; i--)sift(q, i, n);	// 循环建立初始堆,调用sift算法⌊n/2⌋次
		cout << "初始堆\t";
		PrintfSL(q);	// 打印初始堆
		cout << "每一趟\n";
		for (int i = n; i >= 2; i--) {	// 进行n-1趟完成堆排序,每一趟堆中的元素个数减1
			swap(q->p[1], q->p[i]);	//将最后一个未排好序的元素与根p[i]交换 
			sift(q, 1, i - 1);	// 对q.p[[1..i-1]进行筛选,得到i-1个结点的堆
			PrintfSL(q);	// 打印每一趟
		}
	}
	void PrintfSL(Sqlist* q) {	// 输出顺序表
		for (int i = 1; i < q->p.size(); i++)cout << q->p[i].key << " \n"[i == q->p.size() - 1];	//	从第二个开始输出(第一个为空元素)
	}
};
int main() {
	vector<KeyType>list{ 4,9,0,1,8,6,3,5,2,7 };	// 测试用的初始序列
	Sqlist* p = new Sqlist;
	solution s;
	p=s.CreateList(p, list);	// 创建顺序表
	cout << "初始序列\n";
	s.PrintfSL(p);	// 打印顺序表初始序列
	s.HeapSort(p,p->p.size()-1);	// 进行堆排序
	cout << "最终序列\n";
	s.PrintfSL(p);	// 打印排序后顺序表序列
}

5、实现二路归并排序算法。

编写一个程序exp4-5.cpp,采用二路归并排序方法,对有n个记录的待排序序列(例如8个记录的序列{8, 2, 21, 34, 12, 32, 6, 16, 11, 5})进行排序,要求(1)写出采用二路归并排序方法进行排序的过程;(2)写出二路归并排序算法程序;(3)给出二路归并排序算法的时间复杂度和稳定性。

#include<iostream>
#include<vector>
using namespace std;
typedef int KeyType;
struct RecType {
	KeyType key;	// 关键字
	RecType(KeyType x) :key(x) {}
};
struct	Sqlist {
	vector<RecType>p;	// 顺序表
};
class solution {
public:
	Sqlist* CreateList(Sqlist*& q, vector<KeyType>list) {	// 创建顺序表
		for (int i = 0; i < list.size(); i++)q->p.push_back(list[i]);	// 一个个存进去
		return q;
	}
	void Merge(Sqlist* q, int low, int mid, int high) {	// 归并p[low..high]
		Sqlist* q1 = new Sqlist;
		int i = low, j = mid + 1;	// i、j分别为第1段、第2段的下标
		while (i <= mid && j <= high) {	// 第1段和第2段均为扫描完时循环
			if (q->p[i].key <= q->p[j].key) q1->p.push_back(q->p[i++]);	// 将第1段中的元素放入q1中
			else q1->p.push_back(q->p[j++]);	// 将第2段中的元素放入q1中
		}
		while (i <= mid) q1->p.push_back(q->p[i++]);	// 将第1段余下的部分复制到q1
		while (j <= high)q1->p.push_back(q->p[j++]);	// 将第2段余下的部分复制到q1
		for (int k = 0, i = low; i <= high; i++, k++)q->p[i] = q1->p[k];	// 将q1复制到q[low..high]中
		delete q1;	//释放
	}
	void MergeSort(Sqlist* q, int low, int high) {	// 对q->p[low..high]进行自顶向下的二路归并排序
		int mid = (low + high) / 2;	// 划分点
		if (low < high) {
			MergeSort(q, low, mid);	// 递归左区间
			MergeSort(q, mid+1, high);	// 递归右区间
			Merge(q, low, mid, high);	// 归并q->p[low..high]
			PrintfSL(q);	// 输出排序过程
		}
	}
	void PrintfSL(Sqlist* q) {	// 输出顺序表
		for (int i = 0; i < q->p.size(); i++)cout << q->p[i].key << " \n"[i == q->p.size() - 1];
	}
};
int main() {
	vector<KeyType>list{ 8, 2, 21, 34, 12, 32, 6, 16, 11, 5 };	// 测试用的初始序列
	Sqlist* p = new Sqlist;
	solution s;
	p=s.CreateList(p, list);	// 创建顺序表
	cout << "初始序列\n";
	s.PrintfSL(p);	// 打印顺序表初始序列
	cout << "排序过程\n";
	s.MergeSort(p, 0, p->p.size()-1);	// 二路归并排序
	cout << "二路归并排序后的序列\n";
	s.PrintfSL(p);	// 打印排序后顺序表序列
}
  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

萌白萌白的傻白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值