广州大学数据结构实验四
实验四 查找和排序算法实现
开课实验室:计算机科学与工程实验(电子楼) 2020年12月31日
学院 |
计算机科学与网络工程学院 |
年级、专业、班 |
网络工程194 |
姓名 |
jwt |
学号 |
||
实验课程 |
数据结构实验 |
成绩 |
||||||
实验项目 |
实验四 查找和排序算法实现 |
指导老师 |
一、实验目的
1、各种排序算法的实现
2、各种查找算法实现
二、使用仪器、器材
操作系统:Win10
编程软件:C++
三、实验内容及原理
1、各种排序算法的实现
用随机函数生成 16 个 2 位正整数(10~99),实现插入排序、希尔排序、冒泡排序、
快速排序、选择排序、堆排序、二路归并排序等多种排序算法,输出排序中间过程、统计关键字的比较次数和记录的移动次数。
思路:
直接插入排序:
假设把数组分为两部分,一部分是初始时是R1[1],另一部分是R2[2……n-1],每次将R2的第一个元素抽出来与R1中的数进行比较,找到合适的位置插入,然后把这个元素加入R1,依次循环直到R2的数组为空。
折半插入排序:
与直接插入类似,只不过在寻找插入位置时采用了折半方法。
希尔排序:
设置初始增量为n/2(n为数组长度),将数组划分为n/2个小数组,然后对每个小数组进行直接插入排序,一轮结束后,再将增量/2,重复上述步骤,直至增量=0,即排序完成。
冒泡排序:
设置双重循环,相邻元素之间比较大小根据大小进行移动。并添加标志,如果剩下的序列中没有元素发生移动,则表明数组元素已排序完成,可以提前退出。
快速排序:
初始时,将数组的第一个元素作为标记,然后扫描数组,将比标记小的数移到标记左边,比数组大的数移到标记右边,最后返回标记的位置;所以以标记为中心,数组被划分为了两部分,假设为左右子表,接着对左右子表用递归方式重复上述步骤即可完成排序。
选择排序:
设置双重循环,初始时假设第一个元素是最小值,向后扫描找是否有比第一个更小的元素,如果有则记录它的下标,直到找到真正的最小值,然后把最小值和第一个元素交换位置;第二趟排序则设第二个元素是最小值,重复上述步骤,即可完成排序。
堆排序:
将数组看作是一棵完全二叉树的顺序存储结构。初始时,需要构建大根堆。由完全二叉树性质可得,序号大于n/2的结点都是叶子结点,叶子结点满足堆,所以只需要对序号小于n/2的这部分结点进行筛选调整。之后再反复重建堆即可完成排序。
归并排序:
初始时,设数组分为n个 部分,所以每个部分都是有序的;每一趟排序将两两小数组合并(合并时要比较大小进行排序);最后合并得到一个数组即为有序数组,完成排序。
2、各种查找算法实现
(1) 顺序查找:使用数组或链表结构。用随机函数生成 16 个不重复的字母(’a’~’z’),
键盘输入待查找的字母,返回查找成功与否,若成功则返回该字母所在的位置(序号),
并计算比较次数。
(2) 折半查找:用数组实现,查找前元素先排序。计算比较次数。分别用查找成功、
不成功进行测试。
(3) 二叉查找树:手工输入 10 个字母,生成一棵二叉查找树,用递归算法打印树结
构或分别输出先序和中序遍历序列以确认其结构。键盘输入待查找的字母,计算比较次数,并删除该字母。分别用查找成功、不成功进行测试。
思路:
顺序查找:
要获得16个随机不重复的字母,可以先定义一个数组存放26个英文字母(顺序从a-z),然后生成随机下标,再定义一个标记数组,已访问过的元素进行标记,避免产生重复下标,把随机下标得到的字母依次赋予一个新数组,即可得到16个不重复的随机字母。生成数组后,查找即从数组第一个元素开始逐个比较,找到后返回相应下标。
折半查找:
因为上边定义了26个字母的有序数组,所以可以直接用来做折半查找。折半字面意思,对半折,从数组中间开始比较,如果需查找值比中间值大,则在中间值右边继续找,重复上述步骤,直至找到该元素;如果需查找值比中间值小,则在中间值左边继续找,重复上述步骤。
二叉查找树:
利用递归方式构建一棵二叉查找树,使得左子树所有结点都比根节点小,右子树所有结点都比根节点大。再用递归方式先序和中序遍历确认树结构。查找时,通过与根节点比较大小即可分别对应进入左右子树,依次递归,直至找到该元素。删除操作:要判断删除结点的子树情况:若左右子树均不为空,则在被删结点的左子树中找到最右边的结点,把该结点提上来(实际操作中将被删结点赋予新值,删除左子树最右边的结点即可);若只有左子树,直接把左子树提上来;若只有右子树,直接把右子树提上来(还需要判断被删结点是前驱结点的左孩子还是右孩子,若为根节点则直接挂接子树)然后删除结点。
四、实验过程原始数据记录
直接插入排序
void InsertSort(int arr[], int n) //直接插入排序
{int i, j;compare_count = 0; //关键字比较次数remove_count = 0; //关键字移动次数process = 1; //中间过程for (i = 2; i <n; ++i){++compare_count;if (arr[i] < arr[i - 1]){arr[0] = arr[i]; //将待插入的记录暂存到监视哨中arr[i] = arr[i - 1]; //r[i-1]后移remove_count += 2;for (j = i - 2; arr[0] < arr[j]; --j) //从后向前寻找插入位置,逐个后移,空出插入位置{++compare_count;arr[j + 1] = arr[j];++remove_count;}++compare_count; //上一个for循环最后一步判断条件不成立后不执行++compare_count,这里补上一次比较arr[j + 1] = arr[0]; //插入++remove_count;}cout << "第" << process++ << "次排序的中间过程: ";DisArr(arr); //输出数组元素cout << endl;}cout << "关键字比较次数: " << compare_count << "\t"<< "记录移动次数: " << remove_count << endl;
}
折半插入排序
void BInsertSort(int arr[],int n) //折半插入排序
{int i, j, low, high, m;compare_count = 0; //关键字比较次数remove_count = 0; //关键字移动次数process = 1; //中间过程for (i = 2; i < n; ++i){++compare_count;if (arr[i] < arr[i - 1]){arr[0] = arr[i]; //将待插入的记录暂存到监视哨中++remove_count;low = 1; high = i; //置查找区间初值while (low <= high){ //在r[low..high]中折半查找插入的位置m = (low + high) / 2; //折半++compare_count;if (arr[0] < arr[m]) //插入点在前一子表high = m - 1;else //插入点在后一子表low = m + 1;}for (j = i - 1; j >= high + 1; --j) //记录后移{arr[j + 1] = arr[j];++remove_count;}arr[high + 1] = arr[0]; //将r[0]即原r[i],插入到正确位置++remove_count;}cout << "第" << process++ << "次排序的中间过程: ";DisArr(arr); //输出数组元素cout << endl;}cout << "关键字比较次数: " << compare_count << "\t" << "记录移动次数: " << remove_count << endl;
}
希尔排序
void ShellSort(int arr[], int n) //希尔排序
{int i, j, d;d = n / 2; //增量初始值compare_count = 0; //关键字比较次数remove_count = 0; //关键字移动次数process = 1; //中间过程while (d > 0){for (i = d+1; i < n; ++i) //对所有组进行直接插入排序{++compare_count;if (arr[i] < arr[i - d]){arr[0] = arr[i]; //将待插入的记录暂存到监视哨中++remove_count;j = i - d; for (j; (arr[0] < arr[j])&&(j>0) ; j = j - d) //从后向前寻找插入位置,逐个后移,空出插入位置{++compare_count;arr[j + d] = arr[j];++remove_count;}++compare_count; //上一个for循环最后一步判断条件不成立后不执行++compare_count,这里补上一次比较arr[j + d] = arr[0]; //插入正确位置++remove_count;}}d = d / 2; //减小增量cout << "第" << process++ << "次排序的中间过程: ";DisArr(arr); //输出数组元素cout << endl;}cout << "关键字比较次数: " << compare_count << "\t" << "记录移动次数: " << remove_count << endl;
}
冒泡排序
void BubbleSort(int arr[],int n) //冒泡排序
{//对顺序表L做冒泡排序int j,m, flag;m = n - 1;flag = 1; //flag用来标记某一趟排序是否发生交换compare_count = 0; //关键字比较次数remove_count = 0; //关键字移动次数process = 1; //中间过程while(m&&flag){flag = 0; //flag置为0,如果本趟排序没有发生交换,则不会执行下一趟排序for (j = 1; j < m; ++j){++compare_count;if (arr[j] > arr[j + 1]){flag = 1; //flag置为1,表示本趟排序发生了交换arr[0] = arr[j];arr[j] = arr[j + 1];arr[j + 1] = arr[0]; //交换前后两个记录remove_count += 3;}}--m;if (flag == 0){cout << "关键字比较次数: " << compare_count << "\t" << "记录移动次数: " << remove_count << endl;return;}else{cout << "第" << process++ << "次排序的中间过程: ";DisArr(arr);cout << endl;}}
}
快速排序
int Partition(int arr[], int low, int high)//划分
{//对顺序表L中的子表r[low..high]进行一趟排序,返回枢轴位置arr[0] = arr[low]; //用子表的第一个记录做标记++remove_count;while (low < high){ //从表的两端交替地向中间扫描while (low < high && arr[high] >= arr[0]){++compare_count;--high;}++compare_count; //上一个while循环最后一次不满足条件时也进行了比较但没有执行++compare_count,这里补上一次arr[low] = arr[high]; //将比标记记录小的记录移到左边++remove_count;while (low < high && arr[low] <= arr[0]){++compare_count;++low;}++compare_count; //上一个while循环最后一次不满足条件时也进行了比较但没有执行++compare_count,这里补上一次arr[high] = arr[low]; //将比标记记录大的记录移到右边++remove_count;}arr[low] = arr[0]; //标记记录到位++remove_count;cout << "第" << process++ << "次排序的中间过程: ";DisArr(arr);cout << endl;return low; //返回标记位置
}
void QuickSort(int arr[], int low, int high) //快速排序
{ int i;if (low < high){ i = Partition(arr, low, high); //将arr[low..high]一分为二,i是标记位置QuickSort(arr, low, i - 1); //对左子表递归排序QuickSort(arr, i + 1, high); //对右子表递归排序}
}
选择排序
void SelectSort(int arr[],int n) //选择排序
{//对顺序表L做简单选择排序int i, j, temp; //假设arr[temp]为最小compare_count = 0; //关键字比较次数remove_count = 0; //关键字移动次数process = 1; //中间过程for (i = 1; i < n-1; ++i){ //在arr[] 中选择关键字最小的记录temp = i;for (j = i + 1; j <=n-1; ++j){++compare_count;if (arr[j] < arr[temp])temp = j; //temp指向此趟排序中关键字最小的记录}if (temp != i) { arr[0] = arr[i]; arr[i] = arr[temp]; arr[temp] = arr[0]; remove_count += 3;} //交换r[i]与r[k] cout << "第" << process++ << "次排序的中间过程: ";DisArr(arr);cout << endl;}cout << "关键字比较次数: " << compare_count << "\t" << "记录移动次数: " << remove_count << endl;
}
堆排序
void sift(int arr[], int low, int high) //筛选
{int i = low, j = 2 * i; //根据完全二叉树性质,i的孩子为2*i和2*i+1arr[0] = arr[i];++remove_count;while (j <= high){++compare_count;if (j < high && arr[j] < arr[j + 1]) //若右孩子比较大 ++j;++compare_count;if (arr[0] < arr[j]) //将arr[j]调整到双亲结点位置上{arr[i] = arr[j];++remove_count;i = j; //修改i和j,以便向下筛选j = 2 * i;}elsebreak;}arr[i] = arr[0]; //被筛选结点放入最终位置上++remove_count;
}void HeapSort(int arr[], int n) //堆排序
{int i;compare_count = 0; //关键字比较次数remove_count = 0; //关键字移动次数process = 1; //中间过程for (i = n / 2; i >= 1; --i) //建立初始堆,调用sift算法(n/2)(向下取整)次sift(arr, i, n);for (i = n; i >= 2; i--){arr[0] = arr[i];arr[i] = arr[1];arr[1] = arr[0];remove_count += 3;sift(arr, 1, i - 1);cout << "第" << process++ << "次排序的中间过程: ";DisArr(arr);cout << endl;}cout << "关键字比较次数: " << compare_count << "\t" << "记录移动次数: " << remove_count << endl;
}
二分归并排序
int ct = 0,length=16;//想着用来判断合并次数和长度的关系输出排序过程,结果不行……
void Merge(int arr[], int temp[], int low,int mid, int high) //合并
{int i = low,j=mid+1,k=low;while (i <= mid && j <= high) //将arr中的记录从小到达放入temp中{++compare_count;if (arr[i] <= arr[j]){++remove_count;temp[k++] = arr[i++];}else{++remove_count;temp[k++] = arr[j++]; }}while (i <= mid) //若arr[j……high]已遍历完,则将arr[i,mid]复制到temp中{++remove_count;temp[k++] = arr[i++];}while (j <= high) //若arr[i,mid]已遍历完,则将arr[j……high]复制到temp中{++remove_count;temp[k++] = arr[j++];}++ct;
}void MergeSort(int arr[], int temp[], int low, int high) //二分归并排序
{int s[17];if (low == high)temp[low] = arr[low];else{int mid = (low + high) / 2; //将当前序列一分为二MergeSort(arr, s, low, mid); //对分裂点的左边序列递归归并排序,结果放入S[low……mid]MergeSort(arr, s, mid + 1, high); //对分裂点的右边序列递归归并排序,结果放入S[mid+1……high]Merge(s, temp, low, mid, high); //合并S[low……mid]和S[mid+1……high]if (ct==length/2 ){cout << "第" << process++ << "次排序的中间过程: ";DisArr(temp);cout << endl;length /= 2;ct = 0;}}
}
顺序查找
int SeqSearch(char str[], int n, char ch)//顺序表查找
{int i = 0;//ct统计比较次数for (i; i < n; ++i){++ct;//次数+1if (str[i] == ch)//找到该元素,返回下标return i + 1;//设数组元素位置从1开始}cout << "数组中没有该元素!" << endl;return -1;//若没找到则返回-1
}
折半查找
int BinSearch(char str[], int n, char ch)
{ct = 0;int low = 0, high = n - 1, mid;while (low <= high){++ct;mid = (low + high) / 2;if (str[mid] == ch)return mid + 1;else if (str[mid] > ch) //如果比中间值小,则在中间值左边查找high = mid - 1; //更新highelse //如果比中间值大,则在中间值右边查找low = mid + 1; //更新low}cout << "数组中没有该元素!" << endl;return -1;//若没找到则返回-1
}
二叉查找树
bool InsertBST(BiTree& bt, char ch)//在二叉查找树中插入新结点
{if (bt == NULL) //创建新结点{bt = new BiNode;bt->c = ch;bt->lchild = bt->rchild = NULL;return true;}else if (bt->c == ch){cout << "已存在该元素,请重新输入!" << endl;return false;}else if (ch < bt->c)return InsertBST(bt->lchild, ch);elsereturn InsertBST(bt->rchild, ch);
}
BiTree CreateBST(BiTree& bt)
{bt = NULL;char ch;for (int i = 0; i < 10; ++i){cout << "请输入第" << i + 1 << "个字母: ";cin >> ch;if (!InsertBST(bt, ch))//如果输入的数据重复,--i使得可以重新输出第i个字母;不重复则正常插入--i;}cout << endl;return bt;
}
BiTree SearchBST(BiTree bt, char ch)//二叉排序树的查找
{++ct;if (bt == NULL || bt->c == ch)return bt;if (ch < bt->c) //ch比根节点值小return SearchBST(bt->lchild, ch);else //ch比根节点大return SearchBST(bt->rchild, ch);
}
bool DeleteBST(BiTree& bt, char ch)
{//从二叉排序树T中删除关键字等于ch的结点BiTree p = bt;BiTree f = NULL; //初始化BiTree q, s;while (p){if (p->c == ch) break; //找到关键字等于ch的结点*p,结束循环f = p; //*f为*p的双亲结点if (ch < p->c) p = p->lchild; //在*p的左子树中继续查找else p = p->rchild; //在*p的右子树中继续查找}//whileif (!p)return false; //找不到被删结点则返回q = p;if ((p->lchild) && (p->rchild)){ //被删结点*p左右子树均不空s = p->lchild;while (s->rchild) //在*p的左子树中继续查找其前驱结点,即最右下结点{q = s;s = s->rchild;} //向右到尽头p->c = s->c; //q指向被删结点的“前驱”if (q != p) //如果p的左孩子有右子树q->rchild = s->lchild; //重接*q的右子树elseq->lchild = s->lchild; //重接*q的左子树delete s;return true;}//ifelse if (!p->rchild) //被删结点*p无右子树,只需重接其左子树p = p->lchild;else if (!p->lchild) //被删结点*p无左子树,只需重接其右子树p = p->rchild;if (!f)bt = p; //被删结点为根结点else if (q == f->lchild)f->lchild = p; //挂接到*f的左子树位置elsef->rchild = p; //挂接到*f的右子树位置delete q;return true;
}
五、实验结果及分析
定义两个数组,数组1用于固定存储随机值,数组2复制数组1,然后用数组2进行排序测试。
先显示随机数组(方便比对),调用直接插入排序函数,每一趟排序都输出一次数组(即中间过程),最后再输出关键字比较次数、记录的移动次数以及排序后的数组。后边几幅图类似(最后一行有显示用了何种排序)
每次排序前重新对数组2赋值(复制数组1),为了可以比对各个排序算法的差别以及关键字比较次数、记录移动次数等。测试结果均正确。
归并排序用了递归归并的方法,所以中间过程前两趟输出有问题,因为它先对左子序列进行归并,再对右子序列进行归并,然后得到长度各一半的左右有序序列,最后一趟将两序列归并得到有序序列。递归方式的中间过程想不出来如何输出了……但比较次数和记录移动次数都正确,数组也能排出正确的序列。
先输出生成的16个随机不重复的字母,然后输入要查询的字母,调用顺序查找函数,输出该字母所在位置,并输出比较次数
折半查找用的是26个有序的字母表。输入要查询的字母,调用折半查找函数,输出该字母所在位置,并输出比较次数
先创建一个二叉查找树,输入10个不同的字母。然后先序遍历、中序遍历二叉树确定二叉树的结构。输入要查找的字母,调用查找函数,成功了,输出比较次数。然后输入要删除的字母,调用删除函数,提示删除成功,再次先序、中序遍历二叉树,确认已删除成功。
总结
- 因为我用的是普通数组,所以在循环语句边界时要注意,防止数据溢出导致输出异常。只要调整好循环次数即可。
- 输出中间过程、统计关键字的比较次数以及记录的移动次数要注意统计语句放到哪里,否则统计不正确。
- 二叉查找树删除结点要注意判断被删除结点的孩子情况,并且要有指针指向前驱结点和被替换结点的前驱。
- 遇到的问题:二分归并排序用了递归方式,暂时想不到方法出输出中间排序过程……
Pass:仅作为实验参考,有些地方可能不够完善,见谅一下。转载请注明出处!
广州大学数据结构实验四相关推荐
- 数据结构实验四 :字符串和多维数组
数据结构实验四 :字符串和多维数组 本次实验内容: 1.从键盘输入一些文本,统计文本单词的个数. 2.写程序输出一个5阶幻方:每一行每一列每一个对角线和均相等. 3.自己设计一个字符加密算法,并设计程 ...
- 数据结构实验四 排序算法的实现
广州大学学生实验报告 开课实验室:计算机科学与工程实验(电子楼416) 2019年6月4日 学院 计算机科学与教育软件学院 年级.专业.班 姓名 学号 实验课程名称 数据结构实验 成绩 实验项 ...
- 广州大学 数据结构实验报告
数据结构 实验一 一.实验目的: 1.线性表的链表实现:遍历.查找.插入.删除.翻转 2.栈的链式存储结构实现:入栈.出栈 3.队列的链式存储结构的实现:入队.出队 4.线性表.栈和队列的应用实现 二 ...
- 广州大学数据结构实验一
实验一 线性表.堆栈和队列的操作与实现 开课实验室:计算机科学与工程实验(电子楼) 2020年11月22日 学院 计算机科学与网络工程学院 年级.专业.班 网络工程194 姓名 jwt 学 ...
- 南京邮电大学数据结构实验四(各种排序算法)
南邮数据结构实验报告四----各种排序算法 一.各类算法 (一)简单选择排序 (二)直接插入排序 (三)冒泡排序 (四)快速排序 (五)两路合并排序 (六)堆排序 二.全部排序整合+时间测试 三.算法 ...
- 数据结构实验四 约瑟夫生死游戏
实验四 约瑟夫生死游戏 1.实验目的: 利用线性表解决实际问题. 2.实验环境与设备: 已安装Visual Studio 2010(或其以上版本)集成开发环境的计算机. 3.实验原理: (1)利用线性 ...
- 数据结构实验 四色地图染色 c语言实现
main.c #include"map.h" //#include"stack.h" #include<stdio.h> //一个邻接矩阵 int ...
- 广州大学计算机视觉实验一:图像处理入门
相关资料 广州大学计算机视觉实验一:图像处理入门 广州大学计算机视觉实验二:摄像机几何 广州大学计算机视觉实验三:图像滤波 广州大学计算机视觉实验四:图像分割 广州大学计算机视觉实验五:简易数字识别 ...
- 数据结构实验三 图的操作与实现
系列文章: 数据结构实验一 线性表.堆栈和队列的操作与实现 数据结构实验二 二叉树的操作与实现 数据结构实验三 图的操作与实现 数据结构实验四 查找和排序算法实现 一.实验目的: 1.领会图的两种主要 ...
最新文章
- 【IEEE】2020 年AI's 10 To Watch名单新鲜出炉!MIT韩松、方飞、张含望等华人入选!...
- Java的Json解析包FastJson使用
- 如何在用户控件中操作页面中的控件?
- memcached基本操作和语法
- php 匹配标记,php – 正则表达式匹配没有标记的链接
- linux centos lamp,Centos下搭建LAMP
- php 去掉多维数组的键名,去除多维数组的最外层key 保留值
- 20180828 上课截图
- Android Studio 如何修改项目名称
- 英雄启动出错解决方法_超纯水设备高压泵不启动解决方法
- [C] static和extern的作用
- CSS基础part1
- 中国建设银行信息技术类校招笔试心得
- DLang、Rust 以及 Golang 对比数据库操作方式
- Redis数据库及五种数据类型的常用命令详解
- python 爬取微信朋友圈的一些信息
- 【uniapp】根据身份证号获取生日日期
- forever warn: --minUptime not set. Defaulting to: 1000ms
- Win8快速关机命令
- 在家办公可摸鱼?屁,忙到怀疑人生!
热门文章
- terraform import-导入已有资源到Terraform
- 回归初心——读《Deep Big Simple Neural Nets Excel on Hand-written Digit Recognition》
- (毕设1)爬虫+mysql+flask+echarts实现网站数据可视化(附源码)
- Post/Get方式获取Web页面
- NVIDIA官网页面加载慢
- 放大镜 讲课_《放大镜》教学设计
- 如何处理SOLIDWORKS警告提示无法创建日志文件
- 三星折叠手机出现故障显示该项技术尚未足够成熟
- 字段包括id、mmsi、longitude、latitude的csv导入pgadmin4并使用postgis去分析的具体步骤...
- 游戏运营活动设计完整攻略