排序目录
排序算法是计算机科学领域中的基础和重要内容,涉及到数据结构、算法、程序设计等多方面知识。排序是对大量乱序数据进行特定操作,使其有一定顺序排列的过程。
排序算法可以分为内部排序和外部排序两种类型。内部排序是指所有需要排序的数据在内存当中完成整个排序过程,外部排序则是数据太大无法全部放到内存中,需要借助外部设备(如磁盘)实现数据的读入、处理和输出。常用的几种内部排序方法包括插入排序、冒泡排序、选择排序、快速排序、希尔排序、堆排序、归并排序等。
每种排序算法都有各自的优缺点,适合不同场景下使用。比如针对特定数据规模大小,选择时间复杂度低的算法刻意降低计算成本;针对多次输入互相独立且规模小于 N (N 较小时可视为 O(1)) 的时候,排序所需时间甚至还没有O(NlogN) 大。
同时,在实际开发和应用中,选用适当的排序算法能够提升效率,并在一些特殊场景下达到事半功倍的效果例如对非数字类数据排序、字符串排序等。
总之,熟悉各种排序算法的特性与适用条件,对于提升代码效率和优化程序结构有很大的帮助。
文章目录
算法思想简介
常用的排序算法有以下 10 种:
-
冒泡排序(Bubble Sort):重复走访要排序的数列,一次比较两个元素,如果它们的顺序错误就交换位置。
-
选择排序(Selection Sort):找到数据中最小值并把它放在第一个位置,再从剩下的数据里找到最小值放在第二个位置,以此类推直到所有数据完成排序。
-
插入排序(Insertion Sort):将无序的列表分为已排序和未排序两部分,依次遍历未排序的元素,在已经排好序的部分插入该元素。
-
希尔排序(Shell Sort):改进版的插入排序,利用增量gap逐步缩小,最后gap=1回归标准插入排序。可同时处理多组不同间隔的子列表。
-
归并排序(Merge Sort):采用分治法思想,先将待排序数组拆开成若干个单独的、有序的序列,然后合并序列最终形成被排序的数组。
-
快速排序(Quick Sort):通过选择一个基准元素将问题划分为左右两个子集来解决问题,之后对子集进行快速排序。
-
堆排序(Heap Sort):借助堆这种数据结构实现的选择型排序方法,将待排序序列构成一个堆,依次将数值最大(小)的元素取出放到有序区间。
-
计数排序(Counting Sort):通过计算待排序元素中有多少个小于等于每个值并对其进行从后向前的累加来排好序列。在数据份布相对均匀的情况下耗时很快。
-
桶排序(Bucket Sort):将被排序的数组分为 n 个大小相同的子区间,则每个子区间内的元素映射到单独的桶中,对桶里面的元素进行排序。
-
基数排序(Radix Sort):一种非比较型、升级版的计数排序,能够用于整数以及其他类型的列表,按不同位数采用桶装法进行排序。
以上排序算法都有各自的特点和优缺点,在实际使用过程中需要根据需求选择适合的算法。
接下来为您简单介绍以下 10 种排序算法的思路
1. 冒泡排序(Bubble Sort)
冒泡排序是一种交换型排序方法。它重复地走访 数列,依次比较每对相邻元素,若顺序不对就交换两个元素位置,直到没有任何一对数字需要交换位置。时间复杂度 O(n²)。
2. 选择排序(Selection Sort)
选择排序也是一种交换型排序方法。通过在尚未有序的子数列中寻找最小或最大元素然后替换现在的元素,以此类推完成整个数据集合的排序。时间复杂度 O(n²)。
3. 插入排序(Insertion Sort)
插入排序是另外一组基于交换操作和步骤的排序算法, 它通过设定已经排好序的区间并逐渐扩展该区间以包括更多元素来完成排序过程。 时间复杂度 O(n²)。
4. 希尔排序(Shell Sort)
希尔排序将待排序序列按照指定增量分割成几个子序列,并 对子序列进行直接插入排序 ,实际上时优化版的插入排序,缺乏理论保证但易于实现,能够处理中等大小的数据集。时间复杂度取决于一个被称为增量序列的函数,极端情况下会退化成 O(n²)。
5. 归并排序(Merge Sort)
归并是分治策略经典应用之一。将待排数据分为左右两部分,递归进行排序,并合并排好序的子序列得到完整的已排序序列。时间复杂度总是O(nlogn),相对于快排鲁棒性更高,但代价是空间占用比较大。
6. 快速排序(Quick Sort)
在使用基准值轮流将数列无限进一步地切割为小、未排序子数组的同时完成了求解各元素正确位置的任务。时间复杂度期望为 O(nūg log n), 最坏情况下由于回退策略而导致它变成了 O(n²)。
7. 堆排序(Heap Sort)
堆排序也是选择型排序,根据构建的最大二叉堆,不断把堆顶元素与尾部元素交换位置,然后执行 heapify 操作来重建最大堆,时间复杂度 O(n log n)。
8. 计数排序(Counting Sort)
计数排序通过统计待排序集合中每个元素出现的次数,能够在特定条件下达到线性时间复杂度(以数字的范围作为常量因子)。然后按照元素大小排列输出,适用于数据范围相对较窄的;时间复杂度 O(n+k),k表示最大值-最小值+1。
9. 桶排序(Bucket Sort)
桶排序通过讲被排序数分割到不同的范围“桶”内,把每个桶中甚至可能还需要用别的排序算法进一步排好序。与计数排序类似,它的效率取决于位置参数是连续的。 具体地说,桶排序使用线性时间实现了比计数排序、基数排序更高的准确性。时间复杂度最差为O(n²)。
10. 基数排序(Radix Sort)
基数排序根据元素对应位数字来排序,在统指定有关键字类型,且各关键字位之间具有天然“首尾”次序的情况下非常高效。从低位开始,将所有待排序数字按照该位的数值进行排序,然后再处理上一个位元素。复杂度取决关键字和常量因子,可以达到O(dn) ,其中"d"是位数。
思路及其流程图
下面我将用流程图来分别描述这十种排序算法的实现思路。
请看下面对十种排序算法描述示例:
- 冒泡排序
代码:
for(i = 0; i < n; i++){
for(j = 0; j < n-i-1; j++){
if(arr[j] > arr[j+1]){
// swap
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
流程图 :
- 快速排序
代码:
int partition (int arr[], int low, int high){
pivot = arr[high];
i = (low-1);
for(j = low; j <= high-1; j++){
if(arr[j] < pivot){
i++;
// swap
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
// swap
temp = arr[i+1];
arr[i+1] = arr[high];
arr[high] = temp;
return (i+1);
}
void quick_sort(int arr[], int low, int high){
if(low < high){
pi = partition(arr, low, high);
quick_sort(arr, low, pi-1);
quick_sort(arr, pi+1, high);
}
}
流程图 :
- 插入排序
代码:
for(i = 1; i < n; i++){
key = arr[i];
j = i - 1;
while(j >= 0 && arr[j] > key){
arr[j+1] = arr[j];
j--;
}
arr[j+1] = key;
}
流程图 :
- 希尔排序
代码:
for(gap = n/2; gap > 0; gap /= 2){
for(i = gap; i < n; i++){
temp = arr[i];
for(j = i; j >= gap && arr[j-gap] > temp; j -= gap)
arr[j] = arr[j-gap];
arr[j] = temp;
}
}
流程图 :
- 归并排序
代码:
void merge(int arr[], int l, int m, int r){
int i, j, k;
int n1 = m - l + 1;
int n2 = r - m;
int L[n1], R[n2];
for(i = 0; i < n1; i++)
L[i] = arr[l + i];
for(j = 0; j < n2; j++)
R[j] = arr[m + 1 + j];
i = 0;
j = 0;
k = l;
while(i < n1 && j < n2){
if(L[i] <= R[j]){
arr[k] = L[i];
i++;
}
else{
arr[k] = R[j];
j++;
}
k++;
}
while(i < n1){
arr[k] = L[i];
i++;
k++;
}
while(j < n2){
arr[k] = R[j];
j++;
k++;
}
}
void merge_sort(int arr[], int l, int r){
if(l < r){
int m = l+(r-l)/2;
merge_sort(arr, l, m);
merge_sort(arr, m+1, r);
merge(arr, l, m ,r);
}
}
流程图 :
- 堆排序
代码:
void heapify(int arr[], int n, int i){
largest = i;
left_child = 2*i + 1;
right_child = 2*i + 2;
if(left_child < n && arr[left_child] > arr[largest])
largest = left_child;
if(right_child < n && arr[right_child] > arr[largest])
largest = right_child;
if(largest != i){
// swap
temp = arr[i];
arr[i] = arr[largest];
arr[largest] = temp;
heapify(arr, n, largest);
}
}
void heap_sort(int arr[], int n){
for(i = n/2-1; i >= 0; i--)
heapify(arr, n, i);
for(i = n-1; i >= 0; i--){
// swap
temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
heapify(arr, i, 0);
}
}
流程图 :
- 计数排序
代码:
int output[n];
int count[k+1] = {0};
for(i = 0; i < n; i++)
count[arr[i]]++;
for(i = 1; i <= k; i++)
count[i] += count[i-1];
for(i = n-1; i >= 0; i--){
output[count[arr[i]]-1] = arr[i];
count[arr[i]]--;
}
for(i = 0; i < n; i++)
arr[i] = output[i];
流程图 :
- 桶排序
代码:
vector<float> bucket[n];
for(i = 0; i < n; i++){
int bi = arr[i]*n;
bucket[bi].push_back(arr[i]);
}
for(i = 0; i < n; i++)
sort(bucket[i].begin(), bucket[i].end());
index = 0;
for(i = 0; i < n; i++)
for(j = 0; j < bucket[i].size(); j++)
arr[index++] = bucket[i][j];
流程图 :
- 基数排序
代码:
int max_digit = getMax(arr, n);
for(exp = 1; max_digit/exp > 0; exp *= 10){
count_sort(arr, n, exp);
}
流程图 :
10.选择排序
代码:
for(i = 0; i < n-1; i++){
min_index = i;
for(j = i+1; j < n; j++){
if(arr[j] < arr[min_index])
min_index = j;
}
// swap
temp = arr[min_index];
arr[min_index] = arr[i];
arr[i] = temp;
}
流程图 :
以上是十种常见排序算法的Flowchart流程图,供大家参考。
接下来是完整版,且easyx环境可视化代码:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
#include<windows.h>
#include<easyx.h>
#include<time.h>
#include<math.h>
#include<stdbool.h>
#include<graphics.h>
#include<vector>
#define NIN int
#define WIDTH 800
#define HEIGHT 800
#define SiF 100 //上下左右间距
#define MAX 600
#include <iostream>
using namespace std;
/*十大排序算法*/
/*
* 1. 冒泡排序(Bubble Sort)
* 2. 快速排序(Quick Sort)
* 3. 插入排序(Insertion Sort)
* 4. 选择排序(Selection Sort)
* 5. 归并排序(Merge Sort)
* 6. 堆排序(Heap Sort)
* 7. 桶排序(Bucket Sort)
* 8. 基数排序(Radix Sort)
* 9. 希尔排序(Shell Sort)
* 10. 计数排序(Counting Sort)
*/
class MAP {
public:
NIN map = 0;
bool i = false;
//NIN L = 0;
COLORREF color = GREEN;
};
//判断指定区域是否点击
void WaitForLeftClick(int x1, int y1, int x2, int y2) {
while (true) {
// 获取鼠标消息
MOUSEMSG m = GetMouseMsg();
if (m.uMsg == WM_LBUTTONDOWN && m.x >= x1 && m.x <= x2 && m.y >= y1 && m.y <= y2) {
break; // 如果检测到左键按下,并且在指定矩形范围内,退出循环
}
}
}
void Shuffle(MAP map[], NIN I,bool a);//随机打乱数组
void Bubble_Sort(MAP map[], NIN I);//冒泡排序
void Quick_Sort(MAP map[], NIN left, NIN right);//快速排序
void Insertion_Sort(MAP map[], NIN n);//插入排序
void Selection_Sort(MAP map[], NIN I);//选择排序
void MergeSort(MAP map[], NIN l, NIN r);//归并排序
void Heap_Sort(MAP map[], NIN I);// 堆排序
void Bucket_Sort(MAP map[], NIN I);//桶排序
void Radix_Sort(MAP arr[], NIN I);//基数排序
void Shell_Sort(MAP map[], NIN I);//希尔排序
void Count_Sort(MAP map[], NIN I);//计数排序
void DaLuan(MAP map[]);//打乱
void PrintINI(MAP map[],NIN I,const char* c);//绘制显示
//全局参数
int Lmax = WIDTH - (SiF * 2);
int Lmay = HEIGHT - (SiF * 2);
int main(int argc, char* argv[])
{
initgraph(WIDTH, HEIGHT, 0);
setbkcolor(WHITE);
cleardevice();
MAP Map[MAX] = { 0,false,GREEN };
MAP map[MAX] = {0,false,GREEN};
//初始化
//for(int a=0;a<10;a++)
WaitForLeftClick(0, 0, WIDTH, HEIGHT);//判断鼠标左键是否点击
for (int i = 0; i < MAX; i++) {
Map[i].map = i;
Map[i].i = false;
map[i].map = i;
map[i].i = false;
}
PrintINI(map, MAX,"初始化");
Sleep(2000);
DaLuan(Map);
Sleep(1000);
//NIN s = rand() % MAX;
//随机打乱
// Shuffle(map, MAX,true);//随机打乱
for (int i = 0; i < MAX; i++)map[i].map = Map[i].map, map[i].i = Map[i].i;//打乱替换
//for (NIN i = 0; i < MAX; i++) {
// int sr = rand() % MAX;
// NIN temp = map[i].map;
// map[i].map = map[sr].map;
// map[sr].map = temp;
// //设置当前线条为红色,还原上一个线条颜色
// map[i].i = true;
// map[i-1].i = false;
// PrintINI(map, MAX,"随机打乱");
// Sleep(10);
//}
map[MAX - 1].i = false;
PrintINI(map, MAX,"打乱后结果");
//_getch();
WaitForLeftClick(0, 0, WIDTH, HEIGHT);//判断鼠标左键是否点击
//冒泡排序:
Bubble_Sort(map, MAX);
WaitForLeftClick(0, 0, WIDTH, HEIGHT);//判断鼠标左键是否点击
// _getch();
for (int i = 0; i < MAX; i++)map[i].map = Map[i].map, map[i].i = Map[i].i;//打乱替换
PrintINI(map, MAX, "打乱后结果");
WaitForLeftClick(0, 0, WIDTH, HEIGHT);//判断鼠标左键是否点击
//快速排序
Quick_Sort(map, 0, MAX - 1);
WaitForLeftClick(0, 0, WIDTH, HEIGHT);//判断鼠标左键是否点击
//Sleep(1500);
Shuffle(map, MAX, true);//随机打乱
WaitForLeftClick(0, 0, WIDTH, HEIGHT);//判断鼠标左键是否点击
// 插入排序
Insertion_Sort(map, MAX);
WaitForLeftClick(0, 0, WIDTH, HEIGHT);//判断鼠标左键是否点击
//Sleep(1500);
Shuffle(map, MAX, true);//随机打乱
WaitForLeftClick(0, 0, WIDTH, HEIGHT);//判断鼠标左键是否点击
//选择排序
Selection_Sort(map, MAX);
WaitForLeftClick(0, 0, WIDTH, HEIGHT);//判断鼠标左键是否点击
//Sleep(1500);
Shuffle(map, MAX, true);//随机打乱
WaitForLeftClick(0, 0, WIDTH, HEIGHT);//判断鼠标左键是否点击
//归并排序
MergeSort(map, 0, MAX - 1);
WaitForLeftClick(0, 0, WIDTH, HEIGHT);//判断鼠标左键是否点击
//Sleep(1500);
Shuffle(map, MAX, true);//随机打乱
WaitForLeftClick(0, 0, WIDTH, HEIGHT);//判断鼠标左键是否点击
//堆排序
Heap_Sort(map, MAX);
WaitForLeftClick(0, 0, WIDTH, HEIGHT);//判断鼠标左键是否点击
Shuffle(map, MAX,true);//随机打乱
WaitForLeftClick(0, 0, WIDTH, HEIGHT);//判断鼠标左键是否点击
//桶排序
Bucket_Sort(map, MAX);
DaLuan(map);//打乱数组
WaitForLeftClick(0, 0, WIDTH, HEIGHT);//判断鼠标左键是否点击
//基数排序
Radix_Sort(map, MAX);
WaitForLeftClick(0, 0, WIDTH, HEIGHT);//判断鼠标左键是否点击
DaLuan(map);//打乱数组
WaitForLeftClick(0, 0, WIDTH, HEIGHT);//判断鼠标左键是否点击
//希尔排序
Shell_Sort(map, MAX);
WaitForLeftClick(0, 0, WIDTH, HEIGHT);//判断鼠标左键是否点击
DaLuan(map);//打乱数组
WaitForLeftClick(0, 0, WIDTH, HEIGHT);//判断鼠标左键是否点击
//计数排序
Count_Sort(map, MAX);
DaLuan(map);//打乱数组
WaitForLeftClick(0, 0, WIDTH, HEIGHT);//判断鼠标左键是否点击
_getch();
/*int DATA[MAX] = {0};
for (int i = 0; i < MAX; i++) {
DATA[i] = rand() % MAX;
}
int M2 = (int)(sqrt((double)MAX));
for (int i = 0; i < MAX; i++) {
printf("%d ", DATA[i]);
// if (0==i %M2 )printf("\n");
}
// shuffle(DATA, MAX);
for (int i = 0; i < MAX; i++) {
int n = rand() % MAX;
int a = DATA[i];
DATA[i] = DATA[n];
DATA[n] = a;
}
printf("\n打乱后...:\n");
for (int i = 0; i < MAX; i++) {
printf("%d ", DATA[i]);
// if (0 == i % (int)(sqrt((double)MAX)))printf("\n");
}
shuffle(DATA, MAX);
printf("\n在打乱后...:\n");
for (int i = 0; i < MAX; i++) {
printf("%d ", DATA[i]);
// if (0 == i % (int)(sqrt((double)MAX)))printf("\n");
}*/
system("pause");
return EXIT_SUCCESS;
}
void DaLuan(MAP map[])
{
//随机打乱数组
for (NIN i = 0; i < MAX; ++i) {
int sr = rand() % MAX;
NIN temp = map[i].map;
map[i].map = map[sr].map;
map[sr].map = temp;
map[i].i = true;
if (i > 0) {
map[i - 1].i = false;
}
PrintINI(map, MAX, "随机打乱");
Sleep(1);
}
}
void PrintINI(MAP map[], NIN I, const char* c)
{
BeginBatchDraw();//开启双缓冲
cleardevice();//刷新
setlinecolor(GREEN);
setlinestyle(50);
//画出外框
line(0, 0, WIDTH, 0);
line(WIDTH, 0, WIDTH, HEIGHT);
line(WIDTH, HEIGHT, 0, HEIGHT);
line(0, HEIGHT, 0, 0);
//绘制文字
setbkmode(1);
settextcolor(GREEN);
settextstyle(SiF / 2, 0, "宋体");
outtextxy(WIDTH / 2 - SiF / 2, SiF / 4, c);
//依次排列绘制
for (NIN i = 0; i < I; i++) {
setlinecolor(map[i].color);
int x1, x2, y1, y2;
//底部基础位置
x1 = SiF + i*(Lmay/I);
y1 = HEIGHT - SiF;
//对应顶部的位置
x2 = x1;
y2 = (y1 - map[i].map) <SiF? SiF: (y1 - map[i].map);
//划线画线
line(x1, y1, x2, y2);
//if (map[i].i) {
// setlinecolor(RED);
// line(x1, y1 + SiF / 2, x2, SiF / 2);
//}
}
EndBatchDraw();//结束双缓冲
}
//随机打乱数组
void Shuffle(MAP map[], NIN I, bool a)
{
for (NIN i = 0; i < MAX; ++i) {
int sr = rand() % MAX;
NIN temp = map[i].map;
map[i].map = map[sr].map;
map[sr].map = temp;
if (i > 0) {
map[i - 1].i = false;
}
map[i].i = true;
//if (a==true)
PrintINI(map, MAX, "随机打乱");
//Sleep(10);
}
// if(a==false)PrintINI(map, MAX, "随机打乱");
}
//冒泡排序
void Bubble_Sort(MAP map[], NIN I)
{
map[0].i= true;
for (int i = 0; i < I - 1; ++i) {
for (int j = 0; j < I - i - 1; ++j) {
if (map[j].map > map[j + 1].map) {
NIN temp = map[j].map;
bool tp = map[j].i;
map[j].map = map[j+1].map;
map[j].i = map[j + 1].i;
map[j + 1].map = temp;
map[j + 1].i = tp;
PrintINI(map, MAX, "冒泡排序");
}
}
}
WaitForLeftClick(0, 0, WIDTH, HEIGHT);//判断鼠标左键是否点击
//_getch();
}
//快速排序
void Quick_Sort(MAP map[], NIN left, NIN right) {
if (left >= right)
return;
NIN i = left, j = right;
NIN pivot = map[(i + j) / 2].map; // 中间元素作为枢轴
while (i <= j) { // 进行常规操作
while (map[i].map < pivot)
++i;
while (map[j].map > pivot)
--j;
if (i <= j) {
std::swap(map[i], map[j]);
++i;
--j;
PrintINI(map, MAX, "快速排序");
}
}
Quick_Sort(map, left, j); // 递归处理左半边
Quick_Sort(map, i, right); // 递归处理右半边
}
//插入排序
void Insertion_Sort(MAP map[], NIN n) {
for (int i = 1; i < n; ++i) {
MAP key = map[i]; // 记录当前要处理的数
int j = i - 1;
while (j >= 0 && map[j].map > key.map) { // 依次向前比较,找到合适的位置
map[j + 1] = map[j];
--j;
PrintINI(map, n, "插入排序");
}
map[j + 1] = key; // 将key(即原来的map[i])放在应该处于的位置上
PrintINI(map, n, "插入排序");
}
}
//选择排序
void Selection_Sort(MAP map[], NIN I) {
for (int i = 0; i < I - 1; i++) {
int min_idx = i;
for (int j = i + 1; j < I; j++) {
if (map[j].map < map[min_idx].map) {
min_idx = j;
}
}
if (min_idx != i) {
// swap two elements
NIN temp = map[i].map;
bool tp = map[i].i;
map[i].map = map[min_idx].map;
map[i].i = map[min_idx].i;
map[min_idx].map = temp;
map[min_idx].i = tp;
PrintINI(map, I, "选择排序");
}
}
}
//归并排序
void MergeSort(MAP map[], NIN l, NIN r)//归并排序
{
if (l >= r) {
return;
}
int mid = l + (r - l) / 2; // 计算中间位置
MergeSort(map, l, mid); // 对前半部分进行排序
MergeSort(map, mid + 1, r); // 对后半部分进行排序
// 合并两个有序数组
MAP tmp[MAX];
int i = l, j = mid + 1, k = 0;
while (i <= mid && j <= r) {
if (map[i].map < map[j].map) {
tmp[k++] = map[i++];
}
else {
tmp[k++] = map[j++];
}
}
while (i <= mid) {
tmp[k++] = map[i++];
}
while (j <= r) {
tmp[k++] = map[j++];
}
for (int p = 0; p < k; ++p) {
map[l + p] = tmp[p]; // 复制回原数组
}
PrintINI(map, MAX, "归并排序");
}
// 调整堆
void Heap_Adjust(MAP map[], NIN start, NIN end)
{
int father = start;
//left child of father node: 2 * i + 1
for (int son = father * 2 + 1; son <= end; father = son, son = father * 2 + 1) {
if (son < end && map[son].map < map[son + 1].map) {找到较大子节点
++son;//右孩子更大时,则比较右孩子和父亲
}
if (map[father].map > map[son].map) {//较大子节点比父亲小,跳出循环
break;
}
NIN temp = map[father].map;//交换位置
bool temp_i = map[father].i;
map[father].map = map[son].map;
map[father].i = map[son].i;
map[son].map = temp;
map[son].i = temp_i;
PrintINI(map, MAX, "堆排序");
}
}
//堆排序
void Heap_Sort(MAP map[], NIN I)// 堆排序
{
for (int i = I / 2 - 1; i >= 0; --i) {//建立初始的大根堆
Heap_Adjust(map, i, I - 1);
}
for (int i = I - 1; i > 0; --i) {//把最大值放到末尾再重新调整序列为堆
//将堆顶元素和末尾元素交换位置,然后重新构造成新堆来完成排序。
int tmp = map[i].map;
bool tp = map[i].i;
map[i].map = map[0].map;
map[i].i = map[0].i;
map[0].map = tmp;
map[0].i = tp;
Heap_Adjust(map, 0, i - 1);
PrintINI(map, MAX, "堆排序");//输出打印
}
_getch();
}
//桶排序
void Bucket_Sort(MAP map[], NIN I) {
const int bucketNum = MAX + 1; // 桶的数量
int bucket[bucketNum] = { 0 }; // 计数桶
for (int i = 0; i < I; ++i) {// 统计每个数字出现的个数
++bucket[map[i].map];
}
int mapItemIndex = 0;
for (int i = 0; i < bucketNum; ++i) {// 输出结果
while (bucket[i] > 0) {
map[mapItemIndex++].map = i;
--bucket[i];
PrintINI(map, MAX, "桶排序"); //打印间隔
}
}
WaitForLeftClick(0, 0, WIDTH, HEIGHT);//判断鼠标左键是否点击
}
//基数排序
void Radix_Sort(MAP map[], NIN I) {
/*int exp = 1;
//bool is_sorted = false;
//MAP temp[MAX];
//while (!is_sorted) { // 循环直到所有数的各位均已比较
// is_sorted = true;
// NIN digits_count[10] = { 0 }; // 计数数组
// for (NIN i = 0; i < I; ++i) { // 统计当前位上相同数字的数量
// int digit = (arr[i].map / exp) % 10;
// digits_count[digit]++;
// }
// for (NIN i = 1; i < 10; ++i) { // 将桶内计数加和
// digits_count[i] += digits_count[i - 1];
// }
// for (NIN i = I - 1; i >= 0; --i) { // 从临时数组中取出在当前位上需排序的数字并放入原数组中
// int digit = (arr[i].map / exp) % 10;
// temp[digits_count[digit] - 1] = arr[i];
// digits_count[digit]--;
// }
// for (NIN i = 0; i < I; ++i) { // 更新原数组并判断排序是否完成
// if (arr[i].map != temp[i].map) {
// is_sorted = false;
// }
// arr[i] = temp[i];
// }
// exp *= 10; // 进入下一位的比较
// PrintINI(arr, I, "基数排序");
//}
*/
int max_num = 0;
for (int i = 0; i < I; ++i) { // 找到数组中最大值
if (map[i].map > max_num) {
max_num = map[i].map;
}
}
int exp = 1;
while (max_num / exp > 0) { // 计算最长位数d
std::vector<MAP> count_arr(10); // 创建临时桶(可使用标准库 vector)
for (int i = 0; i < I; ++i) {
int digit = (map[i].map / exp) % 10; // 取得当前位数字,并将数字相同元素存入对应桶内
count_arr[digit].map = map[i].map;
count_arr[digit].color = BLUE;
}
int index = 0;
for (int i = 0; i < 10; ++i) { // 将桶内元素按照优先级取出
while (!count_arr[i].map == 0) {
map[index].map = count_arr[i].map;
map[index].color = GREEN;
PrintINI(map, I, "基数排序"); // 调用PrintINI函数
++index;
count_arr[i].map /= 10;
}
}
exp *= 10; // 每次循环将当前位数的进制变为10倍
}
}
//希尔排序
void Shell_Sort(MAP map[], NIN I){
for (int gap = I / 2; gap > 0; gap /= 2) { // 控制步长gap,并以此分组进行插入排序(可理解为数组拆分)
for (int i = gap; i < I; ++i) {
MAP temp = map[i]; // 暂存待插入元素
int j;
for (j = i; j >= gap && map[j - gap].map > temp.map; j -= gap) { // 根据步长控制前一个组中元素与当前组中数字的大小关系并完成有序添加操作(即插入排序)
map[j] = map[j - gap];
map[j].color = BLUE;
PrintINI(map, I, "希尔排序"); // 调用PrintINI函数
}
map[j] = temp;
map[j].color = GREEN;
}
}
}
//计数排序
void Count_Sort(MAP map[], NIN I)//计数排序
{
map[0].i = true;
int min = map[0].map, max = map[0].map;//寻找最大、最小值
for (int i = 1; i < I; ++i)
{
if (map[i].map < min)
{
min = map[i].map;
}
else if (map[i].map > max)
{
max = map[i].map;
}
}
int len = max - min + 1;
int* c = new int[len]();
for (int i = 0; i < I; ++i)//统计每个数出现的次数,存入辅助数组c
{
++c[map[i].map - min];
}
for (int i = 1; i < len; ++i)//重排原序列temp
{
c[i] += c[i - 1];
}
MAP* temp = new MAP[I]();//新建一个MAP类型的数据组
//将各元素从temp中对应位置复制到arr中,注意要倒序遍历以确保排序稳定性
for (int i = I - 1; i >= 0; --i)
{
int index = c[map[i].map - min] - 1;
temp[index] = map[i];
--c[map[i].map - min];
PrintINI(map, I, "计数排序");
PrintINI(temp, I, "计数排序");
}
memcpy(map, temp, sizeof(MAP) * I);//将排好序的结果复制回原始数组map
delete[] c;
delete[] temp;
PrintINI(map, I, "计数排序");
WaitForLeftClick(0, 0, WIDTH, HEIGHT);//判断鼠标左键是否点击
}
简单视频演示:
算法可视化演示
总结
这十种算法分别是:冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序、堆排序、计数排序、桶排序和基数排序。
首先,冒泡排序和选择排序都采用了比较的方法进行排序。冒泡排序每次比较相邻两个元素的大小,若前者大于后者则交换,类似水泡在中间逐渐升起的过程;而选择排序从待排序序列中找到最小值,放到已排好序的部分的末尾,不断重复这一过程直至所有元素有序。
其次,插入排序和希尔排序都是将输入值一步步插入一个已排序的列表中得到排序序列。插入排序通过对未排序端处理左侧部分来增加有序部分长度以实现排序,可以方便地应用于链表中或需要稳定性的场合。希尔排序是对插入排序的改进,在多趟的插入排序后引入缩小增量的策略,使之成为频率较高且常数较低的一种外部排序方法。
几乎同时诞生的归并排序和快速排序都使用了分治思想实现了内部排序。分治算法就是将问题划分成更小的问题解决,之后将结果重新组合以解决原问题。归并排序将待排序序列切分为几个子序列,对每个列表递归地进行排序后再合并;而快速排序则是通过选取一个基准元素(通常为列表的尾部),把比这个数小的放到左边,比它大的放在右边,再对左右两个区间重复上述过程直至所有元素有序。
堆排序采用了树的形式实现了内部排序,在堆中父节点的值总是大于或等于子节点的值,显然最大(或最小)的元素就是堆顶元素,所以堆可以看作是一棵完全二叉树。堆排序的过程:构造大根堆-> 交换堆顶元素和末位元素 -> 去掉末位元素重新调整大根堆…….
计数排序、桶排序和基数排序都没有使用比较的方法进行排序。计数排序是先找出数组中的最大数(假设为k),建立桶大小为 k+1 的桶数组来记录每个元素出现的次数,之后按照桶索引从 0 到 k 输出即可得到排序结果。桶排序也是类似的思路,它将每个元素都存放到相应的“桶”里,各自独立排序,最后收集起来即可。基数排序则是从低位到高位依次按照数字大小进行排序,同样需要考虑桶的问题。
总的来说,这十种算法各有特点、适用场景和复杂度等方面的差异。在实际开发中选择合适的算法进行优化可以大幅提升程序效率。
注:源代码及其解决方案完整压缩已经上传,自行下载