- 7 动态规划算法
动态规划(Dynamic Programming)是求多阶段决策过程(Multistep Decision Process)最优化的一种数学方法,它将问题的整体按时间或空间的特征分成若干个前后衔接的时空阶段,把多阶段决策问题表示为前后有关的一系列单阶段决策问题,然后逐个求解,从而求出整个问题的最有决策序列。它强调了时间和空间的连续性。
动态规划算法的核心思想就是避免子问题的重复计算,通过用空间换取时间的方法来提高算法效率。动态规划算法与分治算法和贪心算法类似,都是将问题归纳为更小的、相似的子问题,然后通过求解子问题产生一个全局最优解。但是与分治算法和贪心算法不同的是,动态规划算法允许这些子问题不独立,也允许其通过自身子问题的解做出选择。动态规划算法的一个显著特征就是其问题树中的子问题呈现大量的重复,因此动态规划算法的实质就是分治思想和解决冗余。动态规划算法的关键,就在于针对重复出现的子问题,在其第一次出现时就加以求解并保存结果,以后再出现重复的子问题时就直接引用、不必重新求解。一般动态规划算法适用的场景满足:
1.最优子结构
如果问题的最优解所包含的子问题的解也是最优的,我们就称该问题具有最优子结构性质(即满足最优化原理)。最优子结构性质为动态规划算法解决问题提供了重要线索。20世纪50年代初美国数学家R.E.Bellman等人在研究多阶段决策过程(multistep decision process)的优化问题时,提出了著名的最优化原理(principle of optimality)。
2.子问题重叠
子问题重叠性质是指在用递归算法自顶向下对问题进行求解时,每次产生的子问题并不总是新问题,有些子问题会被重复计算多次。动态规划算法正是利用了这种子问题的重叠性质,对每一个子问题只计算一次,然后将其计算结果保存在一个表格中,当再次需要计算已经计算过的子问题时,只是在表格中简单地查看一下结果,从而获得较高的效率。
3.无后效性
即子问题的解一旦确定,就不再改变,不受在这之后、包含它的更大的问题的求解决策影响。
下面以数字三角形和编辑距离问题来展示一下动态规划算法。
数字三角形问题:如图所示的数字三角形,从顶部出发,在每一结点可以选择向左走或得向右走,一直走到底层,要求找出一条路径,使路径上的值最大。以下是数字三角形问题动态规划算法的C语言程序。
/*
题目描述 Description
如图所示的数字三角形,从顶部出发,在每一结点可以选择向左走或得向右走,一直走到底层,要求找出一条路径,使路径上的值最大。
输入描述 Input Description
第一行是数塔层数N(1 <= N <= 100)。
第二行起,按数塔图形,有一个或多个的整数,表示该层节点的值,共有N行。
输出描述 Output Description
输出最大值。
样例输入 Sample Input
5
13
11 8
12 7 26
6 14 15 8
12 7 13 24 11
样例输出 Sample Output
86
*/
#include <stdio.h>
#define maxN 101
int N; //N表示数字三角形数塔层数。
int D[maxN][maxN]; //D[i][j]表示第i行第 j 个数字,其中i、j从1开始算。
int maxSum[maxN][maxN]; //maxSum[i][j]表示从D(i,j)到底边的各条路径中,最佳路径的数字之和。
//代码一:纯粹递归。当N达到100是绝对是超时的。因为复杂度是O(2^N)
int fun1(int i, int j)//返回从(i,j)到达最底层的最大路径之和
{
if (i == N) return D[i][j];
else
{
int x = fun1(i + 1, j);
int y = fun1(i + 1, j + 1);
return D[i][j] + (x > y ? x : y);
}
}
//代码二:记忆型递归,避免重复计算。时间复杂度O(n*n)
int fun2(int i, int j)//返回从(i,j)到达最底层的最大路径之和
{
if (maxSum[i][j] != -1) return maxSum[i][j];
if (i == N) maxSum[i][j] = D[i][j];
else
{
int x = fun2(i + 1, j);
int y = fun2(i + 1, j + 1);
maxSum[i][j] = D[i][j] + (x > y ? x : y);
}
return maxSum[i][j];
}
//代码三:递归变递推
int fun3()
{
int i, j,max;
for (j = 1; j <= N; j++) maxSum[N][j] = D[N][j];
for (i = N - 1; i >= 1; i--)
{
for (j = 1; j <= i; j++)
{
int max = (maxSum[i + 1][j] > maxSum[i + 1][j + 1] ? maxSum[i + 1][j] : maxSum[i + 1][j + 1]);
maxSum[i][j] = D[i][j] + max;
}
}
return maxSum[1][1];
}
//代码四:递归变递推并在空间上做优化
int fun4()
{
int i, j;
for (i = N - 1; i >= 1; i--)
{
for (j = 1; j <= i; j++)
{
int max = (D[i + 1][j] > D[i + 1][j + 1] ? D[i + 1][j] : D[i + 1][j + 1]);
D[i][j] = D[i][j] + max;
}
}
return D[1][1];
}
int main(int argc, char *argv[])
{
int i,j, sel,max;
printf("请输入数字三角形数塔层数:");
scanf_s("%d", &N);
printf("请输入数字三角形数塔数表:\n");
for (i = 1; i <= N; i++)
{
for (j = 1; j <= i; j++)
{
scanf_s("%d", &D[i][j]);
maxSum[i][j] = -1;
}
}
printf("fun1:纯粹递归\n\
fun2:记忆型递归\n\
fun3:递归变递推\n\
fun4:空间优化的递归变递推\n\
请选择计算方式:");
scanf_s("%d", &sel);
switch (sel) {
case 1:
max = fun1(1,1);
case 2:
max = fun2(1,1);
case 3:
max = fun3();
case 4:
max = fun4();
}
printf("数字三角形最佳路径的数字之和:%d\n", max);
return 0;
}
以上程序编译运行后结果如下:
请输入数字三角形数塔层数:3
请输入数字三角形数塔数表:
5
2 7
8 4 1
fun1:纯粹递归
fun2:记忆型递归
fun3:递归变递推
fun4:空间优化的递归变递推
请选择计算方式:3
数字三角形最佳路径的数字之和:16
编辑距离问题:设A和B是两个字符串,要用最少的字符操作将字符串A转换为字符串B,这里所说的字符操作包括(1):删除一个字符;(2):插入一个字符;(3):修改一个字符。将A转换为B所用的最少字符操作数称为A到B的编辑距离,记为d[A][B],d[][]中的A,B指的是A和B的长度,设计一个算法对任给的A,B,计算出d[A][B]。以下是编辑距离问题动态规划算法的C语言程序。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ROWMAX 100
#define COLMAX 100
#define MIN3(a,b,c) ((a)<(b)?((a)<(c)?(a):(c)):((b)<(c)?(b):(c)))
//#define SWAP(a,b) do{a^=b;b^=a;a^=b;}while(0)
//#define SWAP1(a,b) do{a=a+b;b=a-b;a=a-b;}while(0)
//#define SWAP2(a,b) do{a^=b;b^=a;a^=b;}while(0)
//#define SWAP3(a,b) do{typeof(a) t=(a);(a)=typeof(a)(b);(b)=t;}while(0)
void SWAP(int *a, int *b)
{
int temp = *a; //需要关注的是改变指针所指变量的值,而不是交换指针值本身
*a = *b; // 是用int temp 而不是int *temp
*b = temp;
}
int mintime_cmp_levenshtein(const char *s1, const char *s2);
int minspace_cmp_levenshtein(const char *s1, const char *s2);
int main(int argc, char *argv[])
{
char str1[] = "Today is Saturday.";
char str2[] = "Tomorrow is Sunday.";
int d,sel;
printf("Please input selection:");
scanf_s("%d", &sel);
if (1 == sel) {
d = mintime_cmp_levenshtein(str1, str2);
}
else if (2 == sel) {
d = minspace_cmp_levenshtein(str1, str2);
}
//int d = cmp_levenshtein(str1, str2);
printf("Edit distance: %d\n", d);
return 0;
}
int mintime_cmp_levenshtein(const char *s1, const char *s2)
{
int row = strlen(s1); /* s1 的长度 */
int col = strlen(s2); /* s2 的长度 */
//int mat[row][col]; /* C99 - variable-length array */
int mat[ROWMAX][COLMAX];
for (int i = 0; i < row; ++i) { /* 数组的行 */
for (int j = 0; j < col; ++j) { /* 数组的列 */
if (i == 0) {
mat[i][j] = j; /* 初始化第1行为 [ 0 1 2 ... ] */
}
else if (j == 0) {
mat[i][j] = i; /* 初始化第1列为 [ 0 1 2 ... ] */
}
else {
int cost = (s1[i - 1] == s2[j - 1]) ? 0 : 1; /* 记录s1[i-1]与s2[j-1]是否相等 */
mat[i][j] = MIN3(mat[i - 1][j] + 1, /* 取三者的最小值 */
mat[i][j - 1] + 1,
mat[i - 1][j - 1] + cost);
}
}
}
return mat[row - 1][col - 1];
}
int minspace_cmp_levenshtein(const char *s1, const char *s2)
{
// create two work vectors of integer distances
int m = strlen(s1);
int n = strlen(s2);
int *v1 = (int*)malloc((n + 1) * sizeof(int));
if (v1 == NULL) {
return -1;
}
int *v2 = (int*)malloc((n + 1) * sizeof(int));
if (v2 == NULL) {
free(v1);
return -1;
}
// initialize v1 (the previous row of distances)
// this row is A[0][i]: edit distance for an empty s1
// the distance is just the number of characters to delete from s2
for (int i = 0; i <= n; ++i) {
v1[i] = i;
}
// calculate v2 (current row distances) from the previous row v1
for (int i = 0; i < m; ++i) {
// first element of v2 is A[i+1][0]
// edit distance is delete (i+1) chars from s to match empty s2
v2[0] = i + 1;
// use formula to fill in the rest of the row
for (int j = 0; j < n; ++j) {
// calculating costs for A[i+1][j+1]
int deletionCost = v1[j + 1] + 1;
int insertionCost = v2[j] + 1;
int substitutionCost = v1[j];
if (s1[i] != s2[j]) {
++substitutionCost;
}
v2[j + 1] = MIN3(deletionCost, insertionCost, substitutionCost);
}
// copy v2 (current row) to v1 (previous row) for next iteration
SWAP(v1, v2);
//SWAP1(v1, v2);
//SWAP2(v1, v2);
//SWAP3(v1, v2);
}
// after the last swap, the results of v2 are now in v1
int retval = v1[n];
free(v1);
free(v2);
return retval;
}
以上程序编译运行后结果如下:
Please input selection:1
Edit distance: 9
- 8 模拟算法
很多算法都是有规律普通的、数值确定的、可以建立可解析的数学模型的;但是在人机交互中也会有一些算法规律特殊、数值不确定的、不能建立解析的数学模型,这时就要采用模拟算法来解决了。模拟算法就是对真实事物或者过程的虚拟。在编程时为了实现某个功能,可以用编程语言来模拟那个功能,模拟成功也就相应地表示编程成功。例如:计算机模拟抛硬币得到正反面的概率的情况。随机函数就是用来模拟自然界中发生的不可预测的情况的模拟算法,C语言中是用srand()和rand()函数来模拟生成随机数。
下面我们用掷骰子游戏和猜数游戏来说明一下模拟算法。
掷骰子游戏问题:要求根据用户输入骰子次数和参赛人数,然后由计算机随机生成每次投掷骰子的点数,再累加得到每一个选手的总点数。算法分析:(1)定义一个随机函数play,根据投掷骰子的次数随机生成每次投掷骰子的点数和总点数。(2)设置一个死循环,可以重复操作。(3)处理每个选手,调用函数play来模拟掷骰子游戏的。以下是掷骰子游戏问题模拟算法的C语言程序。
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
void play(int m);
int main()
{
int n; //骰子次数
int c; //参赛人数
int i; //循环变量
do {
srand((unsigned int)time(NULL));
printf("\n请输入投掷骰子的次数:");
scanf_s("%d", &n);
if (0 == n) {
printf("输入投掷骰子的次数不能为0!");//至少需要投掷一次
break;
}
printf("输入本轮参赛选手人数:");
scanf_s("%d", &c);
if (0 == c) {
printf("输入的参赛选手人数不能为0!");//至少一个参赛选手
break;
}
for (i = 0; i < c; i++) {
printf("第%d位参赛选手投掷的骰子情况:\n", i + 1);
play(n);
}
}while(1);
return 0;
}
void play( int m) {
int i, n = 0, s = 0;
for (i = 1; i <= m; i++)
{
n = rand() % 6 + 1;//生成骰子的六位随机面
printf("第%d次投掷骰子的点数为:%d\n", i, n);
s = s + n;
}
printf("投掷骰子的总点数为:%d\n", s);
}
以上程序编译运行后结果如下:
请输入投掷骰子的次数:3
输入本轮参赛选手人数:3
第1位参赛选手投掷的骰子情况:
第1次投掷骰子的点数为:1
第2次投掷骰子的点数为:1
第3次投掷骰子的点数为:5
投掷骰子的总点数为:7
第2位参赛选手投掷的骰子情况:
第1次投掷骰子的点数为:2
第2次投掷骰子的点数为:6
第3次投掷骰子的点数为:2
投掷骰子的总点数为:10
第3位参赛选手投掷的骰子情况:
第1次投掷骰子的点数为:1
第2次投掷骰子的点数为:5
第3次投掷骰子的点数为:6
投掷骰子的总点数为:12
请输入投掷骰子的次数:
猜数游戏问题:用计算机实现一个随机1~100之间的数字,然后由用户来猜这个数,根据用户猜测的数的大小分别给出不同的提示。算法分析:(1)先通过rand()随机生成一个1~100的数字。(2)通过循环让用户逐个输入要猜测的整数,根据输入的数据和随机数字进行比较。(3)将比较的结果输出。以下是猜数游戏问题模拟算法的C语言程序。
//生成100以内的随机数
#include<stdio.h>//包含生成随机数的函数
#include<stdlib.h>
#include<time.h>
int main()
{
int m, n, i = 0;//i在此要先赋初值0
srand(time(NULL));
n = rand() % 100 + 1;//rand生成的随机数可达65000多,这里除以100
do {
printf("请输入所猜数字:");
scanf_s("%d", &m);
i++;
if (m > n)
printf("你猜的数太大了!\n");
else if (m < n)
printf("你猜的数太小了!\n");
} while (m != n);
printf("你一共猜了%d次,", i);
if (i <= 5)
printf("你很聪明!");
else
printf("你还要多努力!");
return 0;
}
以上程序编译运行后结果如下:
请输入所猜数字:50
你猜的数太小了!
请输入所猜数字:75
你猜的数太小了!
请输入所猜数字:88
你猜的数太大了!
请输入所猜数字:80
你一共猜了4次,你很聪明!
- 9 其他算法介绍
通用基础算法也有很多其他算法,比如数值转换算法、高精度求解算法、排序算法、排列组会算法等。按问题数值类型可分为数值算法(蒙特卡洛、龙格库塔、有限差分、有限元等)、非数值算法和半数值算法;按数学建模的类型可分为分类算法(支持向量机、聚类分析、主成分分析、判别分析、典型相关分析等)、优化算法(禁忌搜索、模拟退火、遗传算法、人工神经网络等)、评价算法(理想解法、模糊综合评判法、数据包络分析法、灰色关联分析法、主成分分析法、秩和比综合评价法)和预测算法(微分方程模型、灰色预测模型、马尔科夫预测、时间序列、插值与拟合、神经网络)。
通用基础算法有很多,这里就不一一展示了,下面仅以排序算法为例总结一下算法的复杂度和稳定性(时间复杂度、空间复杂度,以及稳定性如下表所示)。十大排序算法:冒泡排序算法、选择排序算法、插入排序算法、快速排序算法、堆排序算法、希尔排序算法、桶排序算法、计数排序算法、基数排序算法。
中文名称 | 英文名称 | 平均时间复杂度 | 最坏时间复杂度 | 最好时间复杂度 | 空间复杂度 | 稳定性 |
选择排序 | Selection | n2 | n2 | n2 | 1 | 不稳定 |
冒泡排序 | Bubble | n2 | n2 | n | 1 | 稳定 |
插入排序 | Insertion | n2 | n2 | n | 1 | 稳定 |
堆排序 | Heap | nlogn | nlogn | nlogn | 1 | 不稳定 |
希尔排序 | Shell | n1.3 | n2 | n | 1 | 不稳定 |
归并排序 | Merge | nlogn | nlogn | nlogn | 1 | 稳定 |
快速排序 | Quick | nlogn | n2 | nlogn | nlogn | 不稳定 |
桶排序 | Bucket | n+k | n2 | n | n+k | 稳定 |
计数排序 | Counting | n+k | n+k | n+k | n+k | 稳定 |
基数排序 | Radix | n*k | n*k | n*k | n+k | 稳定 |
排序问题:给一个无序的数列进行重新排序(可升序或降序)。以下是十大排序算法的C++语言程序。
// sortalgo.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include<time.h>
//1——冒泡排序算法
template<typename T>
void bubblesort(T array[], int sz) {
//int sz = sizeof(array) / sizeof(array[0]);
int i = 0, j = 0;
T temp;
for (i = 0; i < sz - 1; i++) {
for (j = 0; j < sz - i - 1; j++) {
if (array[j] > array[j + 1]) {
temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
}
}
//2——选择排序算法
template<typename T>
void selectsort(T array[], int sz) {
int i = 0, j = 0, minindex = 0;
T temp;
for (i = 0; i < sz; i++) {
minindex = i;
for (j = i; j < sz; j++) {
if (array[j] < array[minindex]) {
minindex = j;
}
}
temp = array[i];
array[i] = array[minindex];
array[minindex] = temp;
}
}
//3——插入排序算法
template<typename T>
void insertsort(T array[], int sz) {
int i, j;
for (i = 1; i < sz; i++) {
T temp = array[i];//未排序的元素
for (j = i; j > 0 && temp < array[j - 1]; j--) { //将未排序的元素与已排序的元素逐一比较
array[j] = array[j - 1];//移动已排序元素位置
}
array[j] = temp; //找到插入位置了
}
}
//4——快速排序算法
template<typename T>
T partition(T array[], int left, int right) {
int pivot = left;
T key = array[pivot];
while (left < right) {
while (left < right && array[right] >= key) {
right--;
}
array[left] = array[right];
while (left < right && array[left] <= key) {
left++;
}
array[right] = array[left];
}
array[left] = key;
return left;
}
template<typename T>
void qs_recursive(T array[], int left, int right) {
T middle;
if (left < right) {
middle = partition(array, left, right);
qs_recursive(array, left, middle - 1);
qs_recursive(array, middle + 1, right);
}
}
template<typename T>
void quicksort(T array[], int sz) {
int left = 0;
int right = sz - 1;
qs_recursive(array, left, right);
}
//5——堆排序算法
template<typename T>
void swap(T *a, T *b) {
T temp;
temp = *a;
*a = *b;
*b = temp;
}
template<typename T>
void heapajust(T array[], int index, int sz) {
int max = index;
int left = 2 * index + 1;
int right = 2 * index + 2;
if (left < sz && array[left] > array[max]) {
max = left;
}
if (right < sz && array[right] > array[max]) {
max = right;
}
if (max != index) {
swap(&array[max], &array[index]);
heapajust(array, max, sz);
}
}
template<typename T>
void heapsort(T array[], int sz) {
int i, j;
//堆初始化(将数组按照堆重排)
for (i = sz / 2 - 1; i >= 0; i--) {
heapajust(array, i, sz);
}
//堆排序
for (j = sz - 1; j >= 0; j--) {
swap(&array[0], &array[j]);
heapajust(array, 0, j);
}
}
//6——希尔排序算法
template<typename T>
void shellsort(T array[], int sz) {
int gap, i, j;
for (gap = sz; gap > 0; gap /= 2) {
for (i = gap; i < sz; i++) {
T temp = array[i];//未排序的元素
for (j = i; j > 0 && temp < array[j - 1]; j--) { //将未排序的元素与已排序的元素逐一比较
array[j] = array[j - 1];//移动已排序元素位置
}
array[j] = temp; //找到插入位置了
}
}
}
7——归并排序算法
//template<typename T>
//void merge(T array[], T aux[], int left, int right, int middle) {
// //int len = right - left + 1;
// //T aux[right - left + 1];
// int i, j, k;
// for (k = left; k <= right; k++) {
// aux[k - left] = array[k];
// }
// i = left;
// j = middle + 1;
// for (k = left; k <= right; k++) {
// if (i > middle) {
// array[k] = aux[j - left];
// j++;
// }
// else if (middle > right) {
// array[k] = aux[i - left];
// i++;
// }
// else if (aux[j - left] > aux[i - left]) {
// array[k] = aux[j - left];
// j++;
// }
// else {
// array[k] = aux[i - left];
// i++;
// }
//
// }
//
//}
//
//template<typename T>
//void mergerecursive(T array[], T aux[], int left, int right) {
// if (left >= right) {
// return;
// }
// //aux = NULL;
// int middle = (left + right) / 2;
// mergerecursive(array, aux, left, middle);
// mergerecursive(array, aux, middle + 1, right);
// merge(array, aux, left, right, middle);
//}
//
//template<typename T>
//void mergesort(T array[], int sz) {
// int left = 0, right = sz - 1;
// T aux[sz];
// //T *aux = (T *)malloc(sz);
// //T aux[] = temp;
// mergerecursive(array, aux, left, right);
// //free(aux);
//}
//8——桶排序算法
//template<typename T>
//#define LMAX 100
//void bucketsort(T array[], int sz) {
// int k = 0, i = 0, j = 0;
// //根据原数组元素取值范围[min,max]确定桶的个数len
// int min = array[0], max = array[0], len = 0;
// for (k = 0; k < sz; k++) {
// if (array[k] > max)
// max = array[k];
// if (array[k] < min)
// min = array[k];
// }
// int b = 5; //计数排序 b=1,桶排序不一定
// len = (max - min) / b + 1;
// if (len > LMAX) {
// std::cout << "len > LMAX" << std::endl;
// }
//
// //T bucket[LMAX] = { 0 };
// T buckets[len][0];
// //T *bucket=new T(len);
// /*int *bucket = (int*)malloc(sizeof(int)*len);
// memset(bucket, 0, sizeof(int)*len);*/
//
// //桶计数
// for (i = 0; i < sz; i++) {
// int index = (array[i] - min) / b + 1;
// buckets[index] = arrAppend(buckets[index], array[i]);
// }
//
// //原数组重新排序
// /*for (j = min, i = 0; j < LMAX; j++) {
// while ((bucket[j]--) > 0)
// array[i++] = j;
// }
//*/
// int arrIndex = 0;
// for (i = 0; i < len; i++) {
// T bucket[] = buckets[i];
// if (sizeof(bucket) <= 0) {
// continue;
// }
// int sz = sizeof(bucket) / sizeof(bucket[0]);
// // 对每个桶进行排序,这里使用了插入排序
// insertsort(bucket, sz);
// for (j = 0; j < sz; j++) {
// array[arrIndex++] = bucket[j];
// }
// }
//}
//
//
///**
// * 自动扩容,并保存数据
// *
// * @param arr
// * @param value
// */
//template<typename T>
//T* arrAppend(T* arr, int value) {
// //arr = Arrays.copyOf(arr, arr.length + 1);
// //arr[arr.length - 1] = value;
// int length = sizeof(arr) / sizeof(arr[0]);
// memcpy(arr, arr, (length + 1) * sizeof(T));
// arr[length] = value;
// return arr;
//}
//9——计数排序算法
template<typename T>
#define LMAX 100
void countsort(T array[], int sz) {
int k = 0, i = 0, j = 0;
//根据原数组元素取值范围[min,max]确定桶的个数len
int min = array[0], max = array[0], len = 0;
for (k = 0; k < sz; k++) {
if (array[k] > max)
max = array[k];
if (array[k] < min)
min = array[k];
}
len = max - min + 1;
if (len > LMAX) {
std::cout << "len > LMAX" << std::endl;
}
T count[LMAX] = { 0 };
//桶计数
for (i = 0; i < sz; i++) {
count[array[i]]++;
}
//原数组重新排序
for (j = min, i = 0; j < len; j++) {
while ((count[j]--) > 0)
array[i++] = j;
}
}
//10——基数排序算法
template<typename T>
#define MAX 100
#define BASE 10
void radixsort(T array[], int sz) {
int i, exp = 1;
T m = array[0], temp[MAX] = { 0 };
for (i = 0; i < sz; i++) {
if (m < array[i]) {
m = array[i];
}
}
while (m / exp > 0) {
//基数桶清零
int bucket[BASE] = { 0 };
//基数桶记录
for (i = 0; i < sz; i++) {
bucket[(array[i] / exp) % BASE]++;
}
//基数桶记录叠加
for (i = 1; i < sz; i++) {
bucket[i] += bucket[i - 1];
}
//排序到临时数组中
for (i = sz - 1; i >= 0; i--) {
temp[--bucket[(array[i] / exp) % BASE]] = array[i];
}
//临时数组赋值给原数组排序
for (i = 0; i < sz; i++) {
array[i] = temp[i];
}
exp *= BASE;
}
}
#define ANUM 15
int main()
{
//std::cout << "Hello World!\n";
/*int arr[] = { 65,58,95,10,57,62,13,106,78,23,85 };*/
int arr[ANUM];
int sz = sizeof(arr) / sizeof(arr[0]);
srand((unsigned int)time(NULL));
for (int i = 0; i < sz; i++) {
arr[i] = rand() % 100;
}
std::cout << "数组大小:" << sz << std::endl;
std::cout << "排序前:" << std::endl;
for (int i = 0; i < sz; i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
std::cout << "1——冒泡排序算法" << std::endl;
std::cout << "2——选择排序算法" << std::endl;
std::cout << "3——插入排序算法" << std::endl;
std::cout << "4——快速排序算法" << std::endl;
std::cout << "5——堆排序算法" << std::endl;
std::cout << "6——希尔排序算法" << std::endl;
std::cout << "7——归并排序算法" << std::endl;
std::cout << "8——桶排序算法" << std::endl;
std::cout << "9——计数排序算法" << std::endl;
std::cout << "10——基数排序算法" << std::endl;
std::cout << "请选择排序算法:";
int sel_num;
scanf_s("%d", &sel_num);
switch (sel_num) {
case 1:
bubblesort(arr, sz);
break;
case 2:
selectsort(arr, sz);
break;
case 3:
insertsort(arr, sz);
break;
case 4:
quicksort(arr, sz);
break;
case 5:
heapsort(arr, sz);
break;
case 6:
shellsort(arr, sz);
break;
case 7:
//mergesort(arr, sz);
break;
case 8:
//bucketsort(arr, sz);
break;
case 9:
countsort(arr, sz);
break;
case 10:
radixsort(arr, sz);
break;
default:
break;
}
std::cout << "排序后:" << std::endl;
for (int i = 0; i < sz; i++) {
std::cout << arr[i] << " ";
}
return 0;
}
以上程序编译运行后结果如下:
数组大小:15
排序前:
33 29 21 47 94 27 18 55 56 89 29 20 31 29 89
1——冒泡排序算法
2——选择排序算法
3——插入排序算法
4——快速排序算法
5——堆排序算法
6——希尔排序算法
7——归并排序算法
8——桶排序算法
9——计数排序算法
10——基数排序算法
请选择排序算法:4
排序后:
18 20 21 27 29 29 29 31 33 47 55 56 89 89 94