写在前面:
本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
接上一篇:排序算法(Sorting algorithm)(一)
目录
6、快速排序(Quick Sort)
7、堆排序(Heap Sort)
8、计数排序(Counting Sort)
9、桶排序(Bucket Sort)
10、基数排序(Radix Sort)
四、十大经典排序算法 C语言总代码实现
快速排序(英语:Quick Sort),又称分区交换排序(partition-exchange sort),简称快排,是对冒泡排序的一种改进,亦是分而治之思想在排序算法上的典型应用。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列的目的。
6.1 执行流程:
快速排序使用分治法(Divide and conquer)策略来把一个序列(list)分为较小和较大的2个子序列,然后递归地排序两个子序列。
递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,但是这个算法总会退出,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。
6.2 动图演示:
6.3 代码实现:
void Quick_Sort(int *arr, int low, int high)
{
int i = low;
int j = high;
int pivot = arr[i];
if(low > high) {
return ;
}
while(i < j) {
while((arr[j] >= pivot) && (i < j)) {
j--;
}
arr[i] = arr[j];
while((arr[i] <= pivot) && (i < j)) {
i++;
}
arr[j]= arr[i];
}
arr[i] = pivot;
Quick_Sort(arr, low, i-1);
Quick_Sort(arr, j+1, high);
}
堆排序(英语:Heap Sort)是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆的性质:即子节点的键值或索引总是小于(或者大于)它的父节点。
7.1 执行流程:
假设给定无序序列结构如下(以大顶堆为例):
1) 构造大顶堆
a. 从最后一个非叶子结点开始,从左至右,从下至上进行调整。叶结点自然不用调整,第一个非叶子结点 arr.length/2-1=5/2-1=1,也就是下面的 6结点;在[6, 5, 9]这个小堆里边,父节点 97最大,所以 6和 9交换。
b. 找到第二个非叶节点 4,由于[4, 9, 8]中 9元素最大,4和 9交换。
c. 这时,交换导致了子根[4, 5, 6]结构混乱,继续调整,在[4, 5, 6]这个小堆里边,父节点 6最大,所以 4和 6交换。
至此,一个无序序列已经构造成一个大顶堆。
2) 堆排序
a. 将堆顶元素 9和末尾元素 4进行交换
b. 重新调整结构,使其继续满足堆定义
c. 再将堆顶元素 8与末尾元素 5进行交换,得到第二大元素 8
d. 后续过程,继续进行调整,交换,如此反复进行,最终使得整个序列有序
7.2 动图演示:
7.3 代码实现:
void swap(int *a, int *b)
{
int temp = *b;
*b = *a;
*a = temp;
}
void Heapify(int *arr, int start, int end)
{
#if 1
int i, temp;
temp = arr[start];
for (i = 2*start + 1; i < end; i = 2*i + 1) {// 左孩子 2*i + 1,右孩子 2*i + 2
if (i + 1 < end && arr[i] < arr[i+1]) {// 如果左子结点小于右子结点,i指向右子结点
i++;
}
if (temp < arr[i]) {
arr[start] = arr[i];// 将根节点设置为子节点的较大值
start = i;// 继续往下
} else
break;// 已经满足大根堆
}
arr[start] = temp;// 将temp值放到最终的位置
#else
// 建立父節點指標和子節點指標
int dad = start;
int son = dad * 2 + 1;
while (son < end) { // 若子節點指標在範圍內才做比較
if (son + 1 < end && arr[son] < arr[son + 1]) // 先比較兩個子節點大小,選擇最大的
son++;
if (arr[dad] >= arr[son]) //如果父節點大於子節點代表調整完畢,直接跳出函數
return;
else { // 否則交換父子內容再繼續子節點和孫節點比較
swap(&arr[dad], &arr[son]);
dad = son;
son = dad * 2 + 1;
}
}
#endif
}
void Heap_Sort(int *arr, int len)
{
int i;
// 先将数组构造成大顶堆
for (i = len / 2 - 1; i >= 0; i--)
Heapify(arr, i, len);// 从第一个非叶子结点从下至上,从右至左调整结构
// 调整堆结构+交换堆顶元素与末尾元素
for (i = len - 1; i > 0; i--) {
swap(&arr[0], &arr[i]);// 将堆顶元素与末尾元素进行交换
Heapify(arr, 0, i);// 重新对堆进行调整
}
}
计数排序(Counting Sort)是一种稳定的线性时间排序算法。该算法于1954年由 Harold H. Seward 提出。比较适合数值跨度比较小的, 也就是数组中最大值减去最小值得到的值尽量小, 同时数组元素又比较多的情况下用计数排序效率比较高。计数排序使用一个额外的数组 C,其中第 i个元素是待排序数组 A中值等于 i的元素的个数。然后根据数组 C来将 A中的元素排到正确的位置。
8.1 执行流程:
8.2 动图演示:
8.3 代码实现:
int Counting_Sort(DataType *arr, int len)
{
DataType *counts,
*temp;
int i;
int max_val = arr[0];
for(i = 1; i < len; i++)
if(arr[i] > max_val)
max_val = arr[i];
/*为计数器数组分配空间*/
counts = (DataType *)malloc((max_val+1) * sizeof(DataType));
/*为已排序元素临时存放数组分配空间*/
temp = (DataType *)malloc(len * sizeof(DataType));
if(counts == NULL)
return -1;
if(temp == NULL)
return -1;
/* 初始化计数数组 */
for(i = 0; i < max_val+1; i++)
counts[i] = 0;
/* 统计每个元素出现的次数(counts的下标索引即是要统计的元素本身)*/
for(i = 0; i < len; i++)
counts[arr[i]]++;
/* 将元素本身的次数加上它前一个元素的次数(得到元素偏移量) */
for(i = 1; i < max_val+1; i++)
counts[i] += counts[i - 1];
/* 关键代码:使用上面得到的计数数组去放置每个元素要排序的位置 */
for(i = len - 1; i >= 0; i--) {
temp[counts[arr[i]] -1] = arr[i]; /* counts的值是元素要放置到 temp中的偏移量 */
counts[arr[i]]--; /* counts的计数减 1 */
// temp[--counts[arr[i]]] = arr[i];
}
/* 将 Counting_Sort已排序的元素从 temp拷贝回 data */
memcpy(arr, temp, len * sizeof(DataType));
/*释放前面分配的空间*/
free(counts);
free(temp);
return 0;
}
桶排序(Bucket Sort)或所谓的箱排序,是一个排序算法,工作的原理是将数组分到有限数量的桶里。每个桶再个别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序)。
为了使桶排序更加高效,我们需要做到这两点:
9.1 执行流程:
9.2 动图演示:
9.3 代码实现:
int Bucket_Sort(DataType *arr, int len)
{
int i, j, n;
DataType *buckets;
int max_val = arr[0];
int min_val = arr[0];
for(i = 1; i < len; i++) {
if(arr[i] > max_val)
max_val = arr[i];
else if(arr[i] < min_val)
min_val = arr[i];
}
n = max_val - min_val + 1;
buckets = (DataType *)malloc((n) * sizeof(DataType));
if(buckets == NULL)
return -1;
memset(buckets, 0, (n) * sizeof(DataType));
for (i = 0; i < len; i++)
buckets[arr[i] - min_val]++;
for (i = 0, j = 0; i < n; i++)
while((buckets[i]--) > 0)
arr[j++] = i + min_val;
return 0;
}
基数排序(英语:Radix Sort)是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。
它是这样实现的:将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。
基数排序的方式可以采用LSD(Least significant digital)或MSD(Most significant digital),LSD的排序方式由键值的最右边开始,而MSD则相反,由键值的最左边开始。
10.1 执行流程:
10.2 动图演示:
10.3 代码实现:
#define BASE 10
int Radix_Sort(DataType *arr, int len)
{
DataType *temp,
*counts; //计数器
int i, j, k;
int radix = 1;
int max_val = arr[0];
for(i = 1; i < len; i++)
if(arr[i] > max_val)
max_val = arr[i];
/* 为计数器数组分配空间 */
counts = (DataType *)malloc((BASE+1) * sizeof(DataType));
/* 为已排序元素临时存放数组分配空间 */
temp = (DataType *)malloc(len * sizeof(DataType));
if(counts == NULL)
return -1;
if(temp == NULL)
return -1;
while (max_val / radix > 0) {
for(j = 0; j < BASE+1; j++)
counts[j] = 0; // 每次分配前清空计数器
for(j = 0; j < len; j++) {
k = (arr[j] / radix) % BASE; // 统计每个桶中的记录数
counts[k]++;
}
for(j = 1; j < BASE+1; j++)
counts[j] += counts[j - 1]; // 将 temp中的位置依次分配给每个桶
for(j = len - 1; j >= 0; j--) { // 将所有桶中记录依次收集到 temp中
k = (arr[j] / radix) % BASE;
temp[counts[k] - 1] = arr[j];
counts[k]--;
}
for(j = 0; j < len; j++) // 将临时数组的内容复制到 data中
arr[j] = temp[j];
radix *= 10;
}
/*释放前面分配的空间*/
free(counts);
free(temp);
return 0;
}
/* 排序算法 */
#include
#include
#include
typedef int DataType;
#define SIZEOF(x,y) (sizeof(x)/sizeof(y))
#define LEN SIZEOF(buffer,DataType)
int buffer[10] = {3,6,13,1,78,35,22,11,32,60};
void Bubble_Sort(DataType *arr, int len)
{
int i, j, temp;
for (i = 0; i < len - 1; i++)
for (j = 0; j < len - 1 - i; j++)
if (arr[j] > arr[j + 1]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
void Selection_Sort(DataType *arr, int len)
{
int i, j, k, temp;
for (i = 0; i < len - 1; i++) {
k = i;
for (j = i + 1; j < len; j++)
if (arr[j] < arr[k])
k = j;
if(k != i) {
temp = arr[k];
arr[k] = arr[i];
arr[i] = temp;
}
}
}
void Insertion_Sort(DataType *arr, int len)
{
int i, j, temp;
for (i = 1; i < len; i++)
if (arr[i] < arr[i-1]) {
temp = arr[i];
for (j = i - 1; j >= 0 && arr[j] > temp; j--)
arr[j+1] = arr[j];
arr[j+1] = temp;
}
}
void Shell_Sort(DataType *arr, int len)
{
int gap, i, j;
int temp;
for (gap = len >> 1; gap > 0; gap >>= 1)
for (i = gap; i < len; i++) {
temp = arr[i];
for (j = i - gap; j >= 0 && arr[j] > temp; j -= gap)
arr[j + gap] = arr[j];
arr[j + gap] = temp;
}
}
#define MIN(x,y) ((x) < (y) ? (x) : (y))
int Merge_Sort(DataType *arr, int len)
{
DataType *a = arr;
DataType *b = (DataType *) malloc(len * sizeof(DataType));
int seg, start;
if(b == NULL)
return -1;
for (seg = 1; seg < len; seg += seg) {
for (start = 0; start < len; start += seg * 2) {
int low = start, mid = MIN(start + seg, len), high = MIN(start + seg * 2, len);
int k = low;
int start1 = low, end1 = mid;
int start2 = mid, end2 = high;
while (start1 < end1 && start2 < end2)
b[k++] = a[start1] < a[start2] ? a[start1++] : a[start2++];
while (start1 < end1)
b[k++] = a[start1++];
while (start2 < end2)
b[k++] = a[start2++];
}
DataType *temp = a;
a = b;
b = temp;
}
if (a != arr) {
int i;
for (i = 0; i < len; i++)
b[i] = a[i];
b = a;
}
free(b);
return 0;
}
void Quick_Sort(DataType *arr, int low, int high)
{
int i = low;
int j = high;
int pivot = arr[i];
if(low > high) {
return ;
}
while(i < j) {
while((arr[j] >= pivot) && (i < j)) {
j--;
}
arr[i] = arr[j];
while((arr[i] <= pivot) && (i < j)) {
i++;
}
arr[j]= arr[i];
}
arr[i] = pivot;
Quick_Sort(arr, low, i-1);
Quick_Sort(arr, j+1, high);
}
void swap(DataType *a, DataType *b)
{
DataType temp = *b;
*b = *a;
*a = temp;
}
void Heapify(DataType *arr, int start, int end)
{
#if 0
int i, temp;
temp = arr[start];
for (i = 2*start + 1; i < end; i = 2*i + 1) {// 左孩子 2*i + 1,右孩子 2*i + 2
if (i + 1 < end && arr[i] < arr[i+1]) {// 如果左子结点小于右子结点,i指向右子结点
i++;
}
if (temp < arr[i]) {
arr[start] = arr[i];// 将根节点设置为子节点的较大值
start = i;// 继续往下
} else
break;// 已经满足大根堆
}
arr[start] = temp;// 将temp值放到最终的位置
#else
// 建立父節點指標和子節點指標
int dad = start;
int son = dad * 2 + 1;
while (son < end) { // 若子節點指標在範圍內才做比較
if (son + 1 < end && arr[son] < arr[son + 1]) // 先比較兩個子節點大小,選擇最大的
son++;
if (arr[dad] >= arr[son]) //如果父節點大於子節點代表調整完畢,直接跳出函數
return;
else { // 否則交換父子內容再繼續子節點和孫節點比較
swap(&arr[dad], &arr[son]);
dad = son;
son = dad * 2 + 1;
}
}
#endif
}
void Heap_Sort(DataType *arr, int len)
{
int i;
// 先将数组构造成大顶堆
for (i = len / 2 - 1; i >= 0; i--)
Heapify(arr, i, len);// 从第一个非叶子结点从下至上,从右至左调整结构
// 调整堆结构+交换堆顶元素与末尾元素
for (i = len - 1; i > 0; i--) {
swap(&arr[0], &arr[i]);// 将堆顶元素与末尾元素进行交换
Heapify(arr, 0, i);// 重新对堆进行调整
}
}
int Counting_Sort(DataType *arr, int len)
{
DataType *counts,
*temp;
int i;
int max_val = arr[0];
for(i = 1; i < len; i++)
if(arr[i] > max_val)
max_val = arr[i];
/*为计数器数组分配空间*/
counts = (DataType *)malloc((max_val+1) * sizeof(DataType));
/*为已排序元素临时存放数组分配空间*/
temp = (DataType *)malloc(len * sizeof(DataType));
if(counts == NULL)
return -1;
if(temp == NULL)
return -1;
/* 初始化计数数组 */
for(i = 0; i < max_val+1; i++)
counts[i] = 0;
/* 统计每个元素出现的次数(counts的下标索引即是要统计的元素本身)*/
for(i = 0; i < len; i++)
counts[arr[i]]++;
/* 将元素本身的次数加上它前一个元素的次数(得到元素偏移量) */
for(i = 1; i < max_val+1; i++)
counts[i] += counts[i - 1];
/* 关键代码:使用上面得到的计数数组去放置每个元素要排序的位置 */
for(i = len - 1; i >= 0; i--) {
temp[counts[arr[i]] -1] = arr[i]; /* counts的值是元素要放置到 temp中的偏移量 */
counts[arr[i]]--; /* counts的计数减 1 */
// temp[--counts[arr[i]]] = arr[i];
}
/* 将 Counting_Sort已排序的元素从 temp拷贝回 data */
memcpy(arr, temp, len * sizeof(DataType));
/*释放前面分配的空间*/
free(counts);
free(temp);
return 0;
}
int Bucket_Sort(DataType *arr, int len)
{
int i, j, n;
DataType *buckets;
int max_val = arr[0];
int min_val = arr[0];
for(i = 1; i < len; i++) {
if(arr[i] > max_val)
max_val = arr[i];
else if(arr[i] < min_val)
min_val = arr[i];
}
n = max_val - min_val + 1;
buckets = (DataType *)malloc((n) * sizeof(DataType));
if(buckets == NULL)
return -1;
memset(buckets, 0, (n) * sizeof(DataType));
for (i = 0; i < len; i++)
buckets[arr[i] - min_val]++;
for (i = 0, j = 0; i < n; i++)
while((buckets[i]--) > 0)
arr[j++] = i + min_val;
return 0;
}
#define BASE 10
int Radix_Sort(DataType *arr, int len)
{
DataType *temp,
*counts; //计数器
int i, j, k;
int radix = 1;
int max_val = arr[0];
for(i = 1; i < len; i++)
if(arr[i] > max_val)
max_val = arr[i];
/*为计数器数组分配空间*/
counts = (DataType *)malloc((BASE+1) * sizeof(DataType));
/*为已排序元素临时存放数组分配空间*/
temp = (DataType *)malloc(len * sizeof(DataType));
if(counts == NULL)
return -1;
if(temp == NULL)
return -1;
while (max_val / radix > 0) {
for(j = 0; j < BASE+1; j++)
counts[j] = 0; // 每次分配前清空计数器
for(j = 0; j < len; j++) {
k = (arr[j] / radix) % BASE; // 统计每个桶中的记录数
counts[k]++;
}
for(j = 1; j < BASE+1; j++)
counts[j] += counts[j - 1]; // 将 temp中的位置依次分配给每个桶
for(j = len - 1; j >= 0; j--) { // 将所有桶中记录依次收集到 temp中
k = (arr[j] / radix) % BASE;
temp[counts[k] - 1] = arr[j];
counts[k]--;
}
for(j = 0; j < len; j++) // 将临时数组的内容复制到 data中
arr[j] = temp[j];
radix *= 10;
}
/*释放前面分配的空间*/
free(counts);
free(temp);
return 0;
}
int main(void)
{
int i;
// Bubble_Sort(buffer, LEN);
// Selection_Sort(buffer, LEN);
// Insertion_Sort(buffer, LEN);
// Shell_Sort(buffer, LEN);
// Merge_Sort(buffer, LEN);
// Quick_Sort(buffer, 0, LEN-1);
// Heap_Sort(buffer, LEN);
// Counting_Sort(buffer, LEN);
// Bucket_Sort(buffer, LEN);
// Radix_Sort(buffer, LEN);
for(i = 0; i < LEN; i++)
printf("buffer[%d]=%3d\n", i, *(buffer+i));
return 0;
}
/* -------------------- END OF FILE -------------------- */