一、排序分析:
7、归并排序
时间复杂度:O(nlog2^n)
空间复杂度:O(n)
算法稳定性:稳定
//1、申请新数组,用来保存排序结果
//2、初始start/end1/start2/end2
//3、归并排序:
// 有两个归并段时,start1与start2元素进行比较
// 小于等于,保存start1值同时后移;
// 大于,保存start2值同时后移。
// 注:当任意一个归并段start移至end后时,即另一段剩余元素同时有序;
// 更新start/end1/start2/end2。
// 有一个归并段时,仅将其按顺序保存至brr
//4、更新arr
void Merge(int *arr, int len, int gap)//一次归并排序
{
//申请新数组
int *brr = (int *)malloc(sizeof(int) * len);
assert(brr != NULL);
//初始start/end1/start2/end2
int i = 0;
int start1 = 0;
int end1 = start1 + gap - 1;
int start2 = end1 + 1;
int end2 = start2+gap-1 > len-1 ? len-1 : start2+gap-1;
//开始归并排序
while(start2 < len)//有两个归并段
{
while(start1 <= end1 && start2 <=end2)
{
if(arr[start1] <= arr[start2])//稳定性
{
brr[i++] = arr[start1++];
}
else
{
brr[i++] = arr[start2++];
}
}
while(start1 <= end1)
{
brr[i++] = arr[start1++];
}
while(start2 <= end2)
{
brr[i++] = arr[start2++];
}
//更新start/end1/start2/end2
start1 = end2 + 1;
end1 = start1 + gap -1;
start2 = end1 + 1;
end2 = start2+gap-1 > len-1 ? len-1 : start2+gap-1;
}
while(start1 < len)//有一个归并段
{
brr[i++] = arr[start1++];
}
//更新arr
for(i = 0; i < len; i++)
{
arr[i] = brr[i];
}
free(brr);
brr = NULL;
}
//按 i*2 的步进方式,每次平分为 i*2 个元素的数据段,进行二路排序
void MergeSort(int *arr, int len)//归并排序
{
for(int i = 1; i < len; i *= 2)
{
Merge(arr, len, i);
}
}
8、基数排序
时间复杂度:O(d(r+n))
空间复杂度:O(rd+n)
算法稳定性:稳定
//1、初始十个桶(0-9):单链表建立
//2、确定入桶(与出桶)次数:最大元素位数count
//3、完成每次的入桶与出桶:即figure位的排序
// 得到 figure位数字;
// 按照0-9号桶对应插入;
// 从0-9号桶依次出桶(当前桶全部出完才能开始出下一个桶)。
//4、当完成count次入桶、出桶操作后,即整体有序
void Base(int *arr, int len, int figure)//一次入桶、出桶
{
//初始十个桶
Node head[10];
int i = 0;
int tmp = 0;
for(i = 0; i < 10; i++)
{
InitList(&head[i]);
}
//入桶
for(i = 0; i < len; i++)
{
tmp = GetNum(arr[i], figure);
Insert(&head[tmp], arr[i]);
}
//出桶
i = 0;
for(int j = 0; j <10; )
{
if(Delet(&head[j], &arr[i]))
{
i++;
}
else
{
j++;
}
}
}
void BaseSort(int *arr, int len)//基数排序
{
int count = GetMaxBit(arr, len);
for(int i = 0; i <count; i++)
{
Base(arr, len, i);
}
}
二、代码实现
#include<stdio.h>
#include<assert.h>
#include<malloc.h>
//归并排序(二路归并)
//子序列有序->子序列间有序->整体有序
//1、申请新数组,用来保存排序结果
//2、初始start/end1/start2/end2
//3、归并排序:
// 有两个归并段时,start1与start2元素进行比较
// 小于等于,保存start1值同时后移;
// 大于,保存start2值同时后移。
// 注:当任意一个归并段start移至end后时,即另一段剩余元素同时有序;
// 更新start/end1/start2/end2。
// 有一个归并段时,仅将其按顺序保存至brr
//4、更新arr
void Merge(int *arr, int len, int gap)//一次归并排序
{
//申请新数组
int *brr = (int *)malloc(sizeof(int) * len);
assert(brr != NULL);
//初始start/end1/start2/end2
int i = 0;
int start1 = 0;
int end1 = start1 + gap - 1;
int start2 = end1 + 1;
int end2 = start2+gap-1 > len-1 ? len-1 : start2+gap-1;
//开始归并排序
while(start2 < len)//有两个归并段
{
while(start1 <= end1 && start2 <=end2)
{
if(arr[start1] <= arr[start2])//稳定性
{
brr[i++] = arr[start1++];
}
else
{
brr[i++] = arr[start2++];
}
}
while(start1 <= end1)
{
brr[i++] = arr[start1++];
}
while(start2 <= end2)
{
brr[i++] = arr[start2++];
}
//更新start/end1/start2/end2
start1 = end2 + 1;
end1 = start1 + gap -1;
start2 = end1 + 1;
end2 = start2+gap-1 > len-1 ? len-1 : start2+gap-1;
}
while(start1 < len)//有一个归并段
{
brr[i++] = arr[start1++];
}
//更新arr
for(i = 0; i < len; i++)
{
arr[i] = brr[i];
}
free(brr);
brr = NULL;
}
//按 i*2 的步进方式,每次平分为 i*2 个元素的数据段,进行二路排序
void MergeSort(int *arr, int len)//归并排序
{
for(int i = 1; i < len; i *= 2)
{
Merge(arr, len, i);
}
}
void Show(int *arr, int len)
{
assert(arr != NULL);
for(int i = 0; i < len; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
int main()
{
int arr[] = {7,5,0,4,3,1,2};
int len = sizeof(arr) / sizeof(arr[0]);
MergeSort(arr, len);
Show(arr, len);
getchar();
return 0;
}
#include<stdio.h>
#include<assert.h>
#include<malloc.h>
//基数排序(桶子法)
//建立单链表
typedef struct Node
{
int data;
struct Node *next;
}Node, *List;
void InitList(List plist)
{
assert(plist != NULL);
plist->next = NULL;
}
static Node *GetNode(int val)
{
Node *pGet = (Node *)malloc(sizeof(Node));
assert(pGet != NULL);
pGet->data = val;
pGet->next = NULL;
return pGet;
}
bool Insert(List plist, int val)//尾插
{
assert(plist != NULL);
Node *pGet = GetNode(val);
Node *p = plist;
while(p->next != NULL)
{
p = p->next;
}
p->next = pGet;
return true;
}
bool Delet(List plist, int *rtv)//头删
{
assert(plist != NULL);
if(plist->next == NULL)
{
return false;
}
Node *pDel = plist->next;
if(rtv != NULL)
{
*rtv = pDel->data;
}
plist->next = pDel->next;
free(pDel);
pDel = NULL;
return true;
}
//相关函数实现
int GetMaxBit(int *arr, int len)//入桶次数:最大元素位数
{
//得到最大元素
int max = arr[0];
for(int i = 1; i <len; i++)
{
if(max < arr[i])
{
max = arr[i];
}
}
//得到元素位数
int count = 0;
while(max != 0)
{
count++;
max /= 10;
}
return count;
}
int GetNum(int num, int figure)//应入桶号:figure位数字
{
for(int i = 0;i < figure;i++)
{
num/=10;
}
return num%10;
}
//低位有序->高位有序->整体有序
//1、初始十个桶(0-9):单链表建立
//2、确定入桶(与出桶)次数:最大元素位数count
//3、完成每次的入桶与出桶:即figure位的排序
// 得到 figure位数字;
// 按照0-9号桶对应插入;
// 从0-9号桶依次出桶(当前桶全部出完才能开始出下一个桶)。
//4、当完成count次入桶、出桶操作后,即整体有序
void Base(int *arr, int len, int figure)//一次入桶、出桶
{
//初始十个桶
Node head[10];
int i = 0;
int tmp = 0;
for(i = 0; i < 10; i++)
{
InitList(&head[i]);
}
//入桶
for(i = 0; i < len; i++)
{
tmp = GetNum(arr[i], figure);
Insert(&head[tmp], arr[i]);
}
//出桶
i = 0;
for(int j = 0; j <10; )
{
if(Delet(&head[j], &arr[i]))
{
i++;
}
else
{
j++;
}
}
}
void BaseSort(int *arr, int len)//基数排序
{
int count = GetMaxBit(arr, len);
for(int i = 0; i <count; i++)
{
Base(arr, len, i);
}
}
void Show(int *arr, int len)
{
assert(arr != NULL);
for(int i = 0; i <len; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
int main()
{
int arr[] = {12,3,1,8,34,21,88,54,0};
int len = sizeof(arr)/sizeof(arr[0]);
BaseSort(arr,len);
Show(arr,len);
getchar();
return 0;
}
三、八大排序总结
1、分类:
1)、插入排序:
直接插入排序———->优化:shell排序
2)、选择排序:
直接选择排序———->优化:堆排序
3)、交换排序:
冒泡排序———->优化:快速排序
4)、归并排序
5)、基数排序
2、应用场景:
整体元素个数为n,
1)n较小,直接选择排序(不考虑稳定性)/直接插入排序
2)n较小,且整体或局部有序时,直接插入排序/冒泡排序/快速排序(随机)
3)n较大,快速排序(平均时间最短)/堆排序(内存空间允许且要求稳定性)/归并排序(内存空间允许且要求稳定性)
4)数据无符号,且可分解,基数排序
5)待排序元素间隔距离跨度较大,shell排序