作业描述:
分别实现希尔排序ShellSort()、快速排序QuickSort()、堆排序HeapSort()、2路归并排序MergSort()和链式基数排序RadixSort()共5个函数,要求通过键盘输入选择排序算法并动态展示数据经过每一趟排序操作之b后的数据记录序列(如果能够记录并输出整个排序过程中的数据比较次数则更佳),其中原始数据记录的数量n以及每一项数据记录的信息可以通过键盘或文本文件输入。注意:上交作业的同学必须将这5个算法都实现到位,如果缺一则倒扣一次平时作业的分数。
用opencv将排序过程展示:
完整代码
#include<iostream>
#include<stdio.h>
#include<malloc.h>
#include<math.h>
#include<string.h>
#include<opencv.hpp>
using namespace cv;
using namespace std;
Mat image(1000, 1000, CV_8UC3, Scalar(0, 0, 0));
int countsteptimes = 0;//用来记录排序的次数
int starty = 50;//starty是可视化数组开始的y坐标
//struct area
void ShellSort(int x[], int n, int d[], int dnumber);
void Draw(int sortseq[]);
void QuickSort(int list[], int left, int right);
void HeapSort(int list[], int n);
void PercDown(int A[], int i, int N);
void merge(int arr[], int L, int M, int R);
void mergeSort(int arr[], int L, int R);
void RadixSort(int* arr, int iDataNum);
int GetNumInPos(int num, int pos);
/*问题描述:分别实现希尔排序ShellSort()、快速排序QuickSort()、堆排序HeapSort()、2路归并排序MergSort()和链式基数排序RadixSort()共5个函数,
要求通过键盘输入选择排序算法并动态展示数据经过每一趟排序操作之b后的数据记录序列(如果能够记录并输出整个排序过程中的数据比较次数则更佳),
其中原始数据记录的数量n以及每一项数据记录的信息可以通过键盘或文本文件输入。注意:上交作业的同学必须将这5个算法都实现到位,如果缺一则倒扣一次平时作业的分数。
*/
//ok,我们现在来分析这个问题:首先这是一个排序问题,所以呢我们需要一个待排序的序列(其实也就是待排序的一个一维数组,为了opencv更好的演示,我们这里试用固定长度的数组
int main()
{
int needsort[30] = { 26,5,37,15,61,11,59,15,48,19};//我们这里用10个数来做演示
int needsort2[30] = { 262,542,327,155,631,111,595,165,438,179 };
int n = 10,num;
int d[3] = { 5,3,1 };
for (int i = 0; i <n ; i++)
{
printf("%d ", needsort[i]);
}
printf("\n\n请输入您要选择的排序算法:\n1.ShellSort\n2.QuickSort\n3.HeapSort\n4.mergeSort\n5.RadixSort\n");
scanf("%d", &num);
switch (num)
{
case 1:
ShellSort(needsort, n, d, 3);
break;
case 2:
QuickSort(needsort, 0, 9);
break;
case 3:
HeapSort(needsort, 10);
break;
case 4:
mergeSort(needsort, 0, 9);
break;
case 5:
RadixSort(needsort2,10);
break;
default:
break;
}
for (int i = 0; i < n; i++)
{
printf("%d ", needsort[i]);
}
}
void ShellSort(int x[], int n, int d[], int dnumber)
{
//希尔排序是对直接插入排序的一种优化,又称缩小增量排序。
int i, j, k, span, m,s;
for (i = 0; i < dnumber; i++)//增量数组有多少个就遍历多少遍
{
span = d[i];
for (j = 0; j < span; j++)
{
Draw(x);
s = x[j + span];
k = j;
while (k > -1 && s < x[k])
{
x[k + span] = x[k];
k -= span;
}
x[k + span] = s;
}
}
Draw(x);
}
void QuickSort(int list[],int left,int right)
{
Draw(list);
int pivot, i, j;
int temp;
if (left < right)
{
i = left; j = right + 1;
pivot = list[left];
do {
do i++;
while (list[i] < pivot);
do j--;
while (list[j] > pivot);
if (i < j)
{
list[i] ^= list[j];list[j] ^= list[i];list[i] ^= list[j];
}//三个^=是实现不开辟新的变量就可以实现两个数的交换,这是异或运算,可以证明。
} while (i < j);
list[left] ^= list[j];list[j] ^= list[left];list[left] ^= list[j];
QuickSort(list, left, j - 1);
QuickSort(list, j + 1, right);
}
}
void PercDown(int A[], int i, int N)
{
int child;
int Tmp;
for (Tmp = A[i]; 2 * i + 1 < N; i = child) {
child = 2 * i + 1; //注意数组下标是从0开始的,所以左孩子的求发不是2*i
if (child != N - 1 && A[child + 1] > A[child])
++child; //找到最大的儿子节点
if (Tmp < A[child])
A[i] = A[child];
else
break;
}
A[i] = Tmp;
}
void HeapSort(int A[], int N)
{
int i;
for (i = N / 2; i >= 0; --i)
PercDown(A, i, N); //构造堆
for (i = N - 1; i > 0; --i)
{
Draw(A);
A[0] ^= A[i]; A[i] ^= A[0]; A[0] ^= A[i]; //将最大元素(根)与数组末尾元素交换,从而删除最大元素,重新构造堆
PercDown(A, 0, i);
}
Draw(A);
}
void merge(int arr[], int L, int M, int R)
{
Draw(arr);
int LEFT_SIZE =M-L ;
int RIGHT_SIZE = R-M+1;
int *left=(int *)malloc(sizeof(int)*LEFT_SIZE);
int *right=(int *)malloc(sizeof(int)*RIGHT_SIZE);
if (left == NULL || right == NULL)
printf("开辟数组失败!\n");
int i,j,k;
//填到两个数组里面
for (i = L; i < M; i++)
left[i - L] = arr[i];
for (i = M; i <= R; i++)
right[i - M] = arr[i];
i = 0; j = 0; k = L;//K=L花了我半个多小时………………………还是我太菜了
while (i < LEFT_SIZE&&j < RIGHT_SIZE)
{
if (left[i]<right[j])
{
arr[k] = left[i];
i++;
k++;
}
else {
arr[k] = right[j];
k++;
j++;
}
}
while (i<LEFT_SIZE)
{
arr[k] = left[i];
i++;
k++;
}//如果right这个数组已经满了,把left这个数组剩下的内容全部填进去
while (j<RIGHT_SIZE)
{
arr[k] = right[j];
k++;
j++;
}//如果left这个数组已经满了,把right这个数组剩下的内容全部填进去
free(left); free(right);//搞完事情记得擦屁股
}
void mergeSort(int arr[], int L, int R)
{
if (L == R)return;
else {
int M = (L + R) / 2;
mergeSort(arr, L, M);
mergeSort(arr, M + 1, R);
merge(arr, L, M +1, R);
}
}
//归并排序总共有两部分组成一是分治,二是排序。
// 找到num的从低到高的第pos位的数据
int GetNumInPos(int num, int pos)
{
int temp = 1;
for (int i = 0; i < pos - 1; i++)
temp *= 10;
return (num / temp) % 10;
}
//基数排序 arr 无序数组;iDataNum为无序数据个数
void RadixSort(int* arr, int iDataNum)
{
int *radixArrays[10]; //十进制数,0~9的空间序列
for (int i = 0; i < 10; i++)
{
radixArrays[i] = (int *)malloc(sizeof(int) * (iDataNum + 1));
radixArrays[i][0] = 0; //index为0处记录这组数据的个数
}
for (int pos = 1; pos <= 10; pos++) //从个位开始到31位
{
for (int i = 0; i < iDataNum; i++) //分配过程
{
int num = GetNumInPos(arr[i], pos);
int index = ++radixArrays[num][0];
radixArrays[num][index] = arr[i];
}
for (int i = 0, j = 0; i < 10; i++) //收集
{
for (int k = 1; k <= radixArrays[i][0]; k++)
arr[j++] = radixArrays[i][k];
radixArrays[i][0] = 0; //复位
}
Draw(arr);
}
}
void Draw(int sortseq[])
{
//我们来仔细思考一下这个画图函数需要什么,首先我们将根据每一步排序后的数组来画一个数组,在这里我们的数组的每一个数据用
//长方形表示(30*50)每个rectangle之间间隔2个像素,这样的话我们就可以完成至少18rectangle长度的排序过程可视化,
//
//每画一步应该更新一次,每两步之前的可视化数组应该间隔30个像素,这样的话,整个画布可同时容纳至少10个可视化的数组,但是为避免画布
//画满,我们应该在每满10个可视化的数组时,清屏。
int n=0;
for (int i = 0; sortseq[i] != 0; i++)
{
n++;
}
//统计sortseq的个数
if ((countsteptimes + 1) % 10 == 0)
{
image=(1000, 1000, CV_8UC3, Scalar(0, 0, 0));
imshow("sort", image);
starty = 50;
}
char str[10] = { 0 };
for (int i = 0; i < n; i++)
{
rectangle(image, Point((i + 1) * 50, starty), Point((i + 1) * 50 + 50, starty+30), Scalar(0, 255, 0),1, 8);
memset(str, 0, sizeof(sortseq));
_itoa(sortseq[i], str, n);
putText(image, str, Point((i + 1) * 50 + 10, starty + 15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255, 255, 0), 1, 8);
//rectangle是opencv里面的一个画矩形的函数,百度一查就明白
//putText作用顾名思义,不懂百度一下就知道
}
imshow("sort", image);
waitKey(0);
starty += 50;
}