实验四 查找和排序算法实现
一、实验目的:
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程序完成以下功能。
- 由关键字序列(4,9,0,1,8,6,3,5,2,7)创建一棵二叉排序bt并以括号表示法输出。
- 判断bt是否为一棵二叉排序树。
- 采用递归和非递归两种方法查找关键字为6的结点,并输出其查找路径。
- 分别删除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); // 打印排序后顺序表序列
}