系列文章:
数据结构实验一 线性表、堆栈和队列的操作与实现
数据结构实验二 二叉树的操作与实现
数据结构实验三 图的操作与实现
数据结构实验四 查找和排序算法实现
一、实验目的:
1、领会折半查找的过程和算法设计;
2、领会二叉排序树的定义,二叉树排序树的创建、查找和删除过程及其算法设计;
3、领会冒泡排序的过程和算法设计;
4、领会堆排序的过程和算法设计;
5、掌握基数排序算法及其应用。
二、使用仪器、器材
微机一台
操作系统:WinXP
编程软件:C/C++编程软件
三、实验内容及原理
填入自己的内容(思路或算法流程图、源代码、说明等)
1、教材P362实验题2:实现折半查找的算法
编写一个程序exp9-2.cpp,输出在顺序表(1,2,3,4,5,6,7,8,9,10)中采用折半查找方法查找关键字9的过程。
#include <iostream>
using namespace std;
typedef long long ll;
int arr[] = {1,2,3,4,5,6,7,8,9,10}; //这个是待查找的顺序表
/**
* 二分查找的算法实现
* l:下界
* r:上界
* val:要查找的值
*/
int search(int l,int r,int val){
int cnt = 1;
int mid;
while(l <= r){
mid = (l + r) >> 1;
cout << "(" << cnt++ << ")" << " 上界是" << r << ",下界是" << l << ",中间值是" << mid << endl;
if(arr[mid] == val){
cout << "(" << cnt++ << ")" << " arr[mid] == val,直接返回mid,mid的值是"<< mid << endl;
return mid;
}
if(arr[mid] > val){
cout << "(" << cnt++ << ")" << " arr[mid] > val,让上界=mid-1,下界不变" << endl;
r = mid - 1;
}else{
cout << "(" << cnt++ << ")" << " arr[mid] < val,让下界=mid+1,上界不变" << endl;
l = mid + 1;
}
}
cout << "(" << cnt++ << ")" << " 搜索不到,返回-1" << endl;
return -1;
}
int main() {
search(0,9,9); //算法实现
return 0;
}
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>
using namespace std;
typedef long long ll;
typedef struct BTree{
int val;
BTree* lchild,*rchild;
BTree(){
lchild = 0; //初始化让他的左右子树为空
rchild = 0;
}
}TreeNode;
BTree* bt = 0; //二叉搜索树的根
/**
* 括号法输出二叉树
*/
void bracketingPrint(TreeNode* t = bt){
if(!t){
return; //树空,直接返回
}
cout<<t->val; //输出这个节点的值
if(t->lchild || t->rchild){ //如果左右子树至少其中一个存在,就输出括号,并递归输出左右子树的内容
cout <<"(";
bracketingPrint(t->lchild);
if(t->rchild){ //如果右子树不存在,就不需要输出逗号了,因为输出格式是A(B)而不会是A(B,)
cout << ",";
bracketingPrint(t->rchild);
}
cout << ")";
}
}
/**
* 给二叉排序树插入一个值
*/
void insert(BTree*& bt,int val){
if(!bt){
//如果二叉排序树中不存在值为val的节点时,则插入
TreeNode* node = new TreeNode(); //新建一个节点
node->val = val;
bt = node;
return;
}
if(bt->val < val){
//如果当前节点的值比目标值小,那么就递归寻找右子树
insert(bt->rchild,val);
}else if(bt->val > val){
//如果当前节点的值比目标值大,那么就递归寻找左子树
insert(bt->lchild,val);
}
//剩余一种等于的情况,不需要处理,因为已经存在了。
}
/**
* 判断是不是二叉排序树
*/
bool checkIfBST(BTree* bt = bt,int minLimit = 0x80000000,int maxLimit = 0x7fffffff){
if(!bt){
return 1; //空树也属于二叉排序树
}
if(bt->val <= minLimit || bt->val >= maxLimit){ //取等号的原因是:二叉排序树的值必须唯一
return 0; //值不在范围里面,说明一定不是二叉排序树
}
return checkIfBST(bt->lchild,minLimit,bt->val) && checkIfBST(bt->rchild,bt->val,maxLimit);
}
/**
* 递归查找值为val的节点,并输出查找路径
*/
void recursiveSearch(BTree* bt,int val){
if(!bt){
//树为空就是没有找到
cout << "查找失败"<< endl;
return;
}
if(bt->val == val){
cout << bt->val << " -> 查找成功" << endl;
return;
}
cout << bt->val << " -> ";
if(bt->val < val){
//如果当前节点的值比目标值小,那么就递归寻找右子树
recursiveSearch(bt->rchild,val);
}else{
//否则就找左子树
recursiveSearch(bt->lchild,val);
}
}
/**
* 非递归查找值为val的节点,并输出查找路径
*/
void non_recursive_search(BTree* bt,int val){
while(bt != 0 && bt->val != val){
cout << bt->val << " -> "; //输出
if(bt->val < val){
//如果当前节点的值比目标值小,那么就寻找右子树
bt = bt->rchild;
}else{
//否则就找左子树
bt = bt->lchild;
}
}
if(bt==0){
//为空就是没有找到
cout << "查找失败"<< endl;
}else{
cout << bt->val << " -> 查找成功" << endl;
}
}
/**
* 删除指定值的节点
*/
void deleteBST(BTree* t,int val){
BTree* p = t; //当前节点
BTree* f = 0; //父节点
while(p != 0 && p->val != val){
f = p;
if(p->val < val){
//如果当前节点的值比目标值小,那么就寻找右子树
p = p->rchild;
}else{
//否则就找左子树
p = p->lchild;
}
}
if(!p){
//不存在该值的节点,直接return即可
return;
}
//删除节点时出现3种情况
//(1)要删除的节点是叶子节点
if(!p->lchild && !p->rchild){
//直接删除即可
if(f){
//如果父亲存在的话,给父节点的孩子置空
if(f->lchild == p){
f->lchild = 0;
}else{
f->rchild = 0;
}
}
delete p; //删除p
p = 0;
return;
}
//(2)要删除的节点只存在左子树或者只存在右子树
if((p->lchild && !p->rchild) || (!p->lchild && p->rchild)){
//这种情况只需要用他的左子树或右子树代替要删除的那个节点的位置就好了
if(f){
//父亲存在
if(f->lchild == p){
if(p->lchild){
f->lchild = p->lchild; //直接代替
}else{
f->lchild = p->rchild;
}
}else{
if(p->lchild){
f->rchild = p->lchild; //直接代替
}else{
f->rchild = p->rchild;
}
}
}else{
if(p->lchild){
bt = p->lchild; //直接代替
}else{
bt = p->rchild;
}
}
delete p;
p = 0;
return;
}
//(3) 要删除的顶点的左右子树都存在
if(p->lchild && p->rchild){
//这种情况下只需要用左子树中最大的那个值的顶点代替即可
BTree* q = p->lchild;
BTree* qf = p; //q的父亲
//把q定位到左子树中最大的那个值的顶点
while(q->rchild){
qf = q;
q = q->rchild;
}
if(qf != p){
qf ->rchild = q->lchild;
q ->lchild = p->lchild;
}
if(f){
if(f->lchild == p){
f->lchild = q;
q->rchild = p ->rchild;
}else{
f->rchild = q;
q->rchild = p ->rchild;
}
}else{
bt = q;
q->rchild = p ->rchild;
}
delete p;
p = 0;
}
}
int main() {
cout << "(1)由关键字序列(4,9,0,1,8,6,3,5,2,7)创建一棵二叉排序bt并以括号表示法输出。" << endl;
int arr[] = {4,9,0,1,8,6,3,5,2,7};
for(int i = 0;i<sizeof(arr)/sizeof(arr[0]);++i){
insert(bt,arr[i]); //依次插入
}
bracketingPrint();
cout << endl;
//=========================================================
cout << "(2)判断bt是否为一棵二叉排序树。" << endl;
if(checkIfBST()){
cout << "这是一颗二叉排序树" << endl;
}else{
cout << "这不是一颗二叉排序树" << endl;
}
//=========================================================
cout << "(3)采用递归和非递归两种方法查找关键字为6的结点,并输出其查找路径。" << endl;
cout << "递归查找:" << endl;
recursiveSearch(bt,6);
cout << "非递归查找:" << endl;
non_recursive_search(bt,6);
//=========================================================
cout << "(4)分别删除bt中关键字为4和5的结点,并输出删除后的二叉排序。" << endl;
deleteBST(bt,4);
deleteBST(bt,5);
bracketingPrint();
cout << endl;
return 0;
}
3、教材P396实验题4:实现冒泡排序算法
编写一个程序exp10-4.cpp实现冒泡排序算法,用相关数据进行测试并输出各趟的排序结果。
#include<iostream>
using namespace std;
int main(){
int nums[] = {1,6,5,2,4};
int n = sizeof(nums) / sizeof(nums[0]);
for(int i = 1;i< n;++i){
cout << "第(" << i << ")趟冒泡排序:"<< endl;
for(int j = 0;j < n - i;++j){
cout << "\t当前j = " << j << ", ";
if(nums[j] > nums[j + 1]){
cout << "因为nums[j] > nums[j + 1],所以交换位置,";
//交换
nums[j] = nums[j] ^ nums[j+1];
nums[j + 1] = nums[j] ^ nums[j+1];
nums[j] = nums[j] ^ nums[j+1];
}else{
cout << "因为nums[j] <= nums[j + 1],所以不需要交换位置,";
}
cout << "目前数组里面的内容为[";
for(int k = 0;k<n;++k){
cout << nums[k] << ",]"[k == n-1];
}
cout << endl;
}
}
cout << "排序完成!" << endl;
return 0;
}
4、教材P397实验题7:实现堆排序算法
编写一个程序exp10-7cpp实现堆排序算法,用相关数据进行测试并输出各趟的序结果。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int nums[] = {0,1,6,8,9,4,3}; //待排序的数组
int len = sizeof(nums) / sizeof(int); //数组的长度
int cnt = 1;
void printNums(){
cout << "(" << cnt++ << ")";
for(int i = 0;i<len;++i){
cout << nums[i] << " \n"[i==len-1];
}
}
/**
* 将s-n-1调整为大根堆模式,默认s+1-n-1已经是大根堆
*/
void heapAdjust(int s,int n){
int rc = nums[s];
for(int j = 2*s;j<n-1;j*=2){
++j; // nums[j]的左孩子是nums[2*j+1],nums[j]的右孩子是nums[2*j+2];
if(j < n-1 && nums[j+1]>nums[j]){
++j; //选择较大的,如果右孩子比较大那就选择右孩子
}
if(rc > nums[j]){
break; //根最大,已经是堆,不需要做任何调整
}
nums[s]=nums[j];
s = j;
}
nums[s] = rc;
printNums();
}
/**
* 建初堆
*/
void createHeap(){
for(int i = len / 2 - 1;i>=0;--i){
heapAdjust(i,len); //反复调用
}
}
/*
排序
*/
void heapSort(){
createHeap(); //建堆
for(int i = len-1;i>0;--i){
//交换头和尾
nums[0] = nums[0] ^ nums[i];
nums[i] = nums[0] ^ nums[i];
nums[0] = nums[0] ^ nums[i];
printNums();
heapAdjust(0,i-1); //重新将nums[0...i-1]调整为大根堆
}
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
printNums();
heapSort(); //堆排序
printNums();
return 0;
}
5、教材P397题11:实现英文单词按字典序排列的基数排序算法
编写一个程序exp10-11.cpp,采用基数排序方法将一组英文单词按字典序排列。假设单词均由小写字母或空格构成,最长的单词MaxLen个字母,用相关数据进行测试并输出各趟的排序结果。
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
#define MAX_LEN 10 //字符串数量
#define RADIX 28 //26个单词+1个空格+1个空字符
#define COUNT 10 //一共多少个字符串
char strs[COUNT][MAX_LEN + 1];
struct {
char keys[COUNT + 1][MAX_LEN + 1];
int n = 0;
}SList[RADIX]; //SList[0]是空字符,SList[1]是空格,SList[2-27]是字符a-z
//输出
void printStrs(int cnt = 0){
printf("(%2d) [",cnt);
for(int i = 0;i < COUNT; ++i){
printf("%s",strs[i]);
if(i < COUNT - 1){
printf(", ");
}
}
printf("]\n");
}
int main(){
//初始化strs
for(int i = 0;i<COUNT;++i){
for(int j = 0;j<=MAX_LEN;++j){
strs[i][j] = 0;
}
}
//给字符串赋值
strcpy(strs[0],"codeforces");
strcpy(strs[1],"leetcode");
strcpy(strs[2],"atcoder");
strcpy(strs[3],"lujiajing");
strcpy(strs[4],"good icpc");
strcpy(strs[5],"acm is nb");
strcpy(strs[6],"good ccpc");
strcpy(strs[7],"acmisgood");
strcpy(strs[8],"nowcoder");
strcpy(strs[9],"springboot");
//输出
printStrs();
//从后往前,逐趟排序
for(int j = MAX_LEN - 1;j>=0;--j){
for(int i = 0;i<COUNT;++i){
int index = 0;
if(strs[i][j] == 0){
//空字符
index = 0;
}else if(strs[i][j] == ' '){
index = 1;
}else{
index = strs[i][j] - 'a' + 2;
}
for(int k = 0;k<=MAX_LEN;++k){
//逐一拷贝字符
SList[index].keys[SList[index].n][k] = strs[i][k];
}
++SList[index].n;
}
//排序完一趟之后需要拷贝回strs
int i = 0; //strs的下标
int index = 0; //SList的下标
int k = 0; //SList[index].keys的下标
for(;index < RADIX;++index){
int n = SList[index].n;
k = 0;
while(k < n){
for(int l = 0;l<=MAX_LEN;++l){
//逐一拷贝字符
strs[i][l] = SList[index].keys[k][l];
}
++k;
++i;
}
}
//重新初始化每一个SList
for(int i = 0;i<RADIX;++i){
SList[i].n = 0;
}
//输出:
printStrs(MAX_LEN-j);
}
}