1、排序算法分类
内排序(所有的数据都在内存上)
①冒泡排序
②选择排序
③直接插入排序
④希尔排序
⑤堆排序
⑥快速排序
⑦归并排序
⑧基数排序
外排序:借助磁盘空间(文件)来进行排序
①冒泡排序:
基本思想:将有待排序数据的相邻两个数据进行比较,将较大的数据放在后面,遍历一次后就可以将最大的数据放在最后面。结果n-1次遍历,整个数据序列就有序了。
时间复杂度:O(n^2)
空间复杂度:O(1)
稳定性:稳定
#include<stdio.h>
#include<stdlib.h>
void BulleSort(int *arr,int len)
{
for(int i=0;i<len;i++)
{
for(int j=0;j<len-i-1;j++)
{
if(arr[j]>arr[j+1])
{
int tmp=0;
tmp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=tmp;
}
}
}
printf("finsh soet\n");
}
int main()
{
int arr[]={1,11,2,33,4,5,66};
int len=sizeof(arr)/sizeof(int);
for(int i=0;i<len;i++)
{
printf("%d ",arr[i]);
}
printf("\n");
BulleSort(arr,len);
for(int i=0;i<len;i++)
{
printf("%d ",arr[i]);
}
return 0;
}
②选择排序:
基本思想:每次从待排序中选出一个最小值放在起始位置,直至所有待排序列排完为止。
时间复杂度:O(n^2)
空间复杂度:O(1)
稳定性;不稳定
#include<stdio.h>
#include<stdlib.h>
void SelectSort(int *arr,int len)
{
for(int i=0;i<len;i++)
{
int min=i;
for(int j=i+1;j<len;j++)
{
if(arr[j]<arr[min])
{
min=j;
}
}
int tmp=0;
tmp=arr[i];
arr[i]=arr[min];
arr[min]=tmp;
}
printf("\nfinsh sort\n");
}
int main()
{
int arr[]={1,11,2,33,4,5,66};
int len=sizeof(arr)/sizeof(int);
for(int i=0;i<len;i++)
{
printf("%d ",arr[i]);
}
SelectSort(arr,len);
for(int i=0;i<len;i++)
{
printf("%d ",arr[i]);
}
return 0;
}
③直接插入排序
基本思想:将待排序序列看成两部分,左部分为有序,右部分为无序(第一次在部分只有一个数据)。循环从右部分拿出一个数据插入在部分。
时间复杂度:O(n^2)
空间复杂度:O(1)
稳定性:稳定
#include<stdio.h>
#include<stdlib.h>
void SelectSort(int *arr,int len)
{
for(int i=1;i<len;i++)
{
int tmp=arr[i];
int j=i-1;
for( ;j>=0&&arr[j]>tmp;j--)
{
arr[j+1]=arr[j];
}
arr[j+1]=tmp;
}
printf("\nfinsh sort\n");
}
int main()
{
int arr[]={1,11,2,33,4,5,66};
int len=sizeof(arr)/sizeof(int);
for(int i=0;i<len;i++)
{
printf("%d ",arr[i]);
}
SelectSort(arr,len);
for(int i=0;i<len;i++)
{
printf("%d ",arr[i]);
}
return 0;
}
④希尔排序
基本思想:先将待排序序列分成若干组,然后使用直接插入排序在组内排序。循环着将分组减到最小,执行以上过程,直到分组减为1。
希尔排序就是直接排序的优化,数据越有序,排序越快。
时间复杂度:时间复杂度是与增量序列相关的函数。 O(n^1.3~1.5)
空间复杂度:O(1)
稳定性:不稳定
#include<stdio.h>
ShellSort(int* arr,int len)
{
int gap=len;
while(gap>1)
{
gap=gap/2;
int i=0;
for(i=0;i<len-gap;i++)
{
int end=i;
int tmp=arr[gap+end];
while(end>=0)
{
if(tmp<arr[end])
{
arr[gap+end]=arr[end];
end-=gap;
}
else
{
break;
}
}
arr[end+gap]=tmp;
}
}
printf("\nfinsh sort\n");
}
int main()
{
int arr[]={11,2,4,1,99,89,45};
int len=sizeof(arr)/sizeof(int);
for(int i=0;i<len;i++)
{
printf("%d ",arr[i]);
}
ShellSort(arr,len);
for(int i=0;i<len;i++)
{
printf("%d ",arr[i]);
}
}
⑤堆排序
堆:堆通常是一个可以被看作一颗完全二叉数的数组对象。堆总是满足下列性质:
堆中某个结点的值总是不大于或不小于其父结点的值。
大根堆:每个子数的父结点都大于等于左、右孩子,
根是最大的。
小根堆:每个子数的父结点都小于等于其左、右孩子,根是最小的。
堆是一颗完全二叉树。
※完全二叉树的数组的储存性质
1、如果一个下标的结点为I,则其左孩子的下标为:2*i+1,右孩子为:2*i+2。
2、如果一个孩子的下标为i,则其父结点的下标的为:(i-1)/2。
基本思路:先将数组调整为大根堆。①从最后一颗子树开始调整。(len-2)/2 向上一颗子树调整(是-1的过程)。②每次调整过程都是从当前子数的根开始向下调整。
时间复杂度:O(nlogn)
空间复杂度:O(1)
稳定性:不稳定
#include<stdio.h>
void Swap(int *a,int *b)
{
int tmp=0;
tmp=*a;
*a=*b;
*b=tmp;
}
void oneAdjust(int *arr,int len,int root)
{
int tmp=arr[root];
int i=root;
int j=2*i+1;
while(j<len)
{
if(j+1<len&&arr[j]<arr[j+1]) j++;//j为大孩子的下标
if(arr[j]<tmp) break;
arr[i]=arr[j];
i=j;
j=2*i+1;
}
arr[i]=tmp;
}
//O(nlogn)
void CreateHeap(int *arr,int len)
{
int last_root=(len-2)/2;
for(int i=last_root;i>=0;--i)
{
oneAdjust(arr,len,i);
}
}
//O(nlogn)
void HeapSort(int *arr,int len)
{
CreateHeap(arr,len);
//O(nlogn)
for(int i=0;i<len-1;++i)
{
Swap(&arr[0],&arr[len-1-i]);
oneAdjust(arr,len-1-i,0);
}
printf("\nfinsh sort\n");
}
int main()
{
int arr[]={11,1,2,4,3,7,90};
int len=sizeof(arr)/sizeof(int);
for(int i=0;i<len;i++)
{
printf("%d ",arr[i]);
}
HeapSort(arr,len);
for(int i=0;i<len;i++)
{
printf("%d ",arr[i]);
}
}
⑥快速排序
基本思想:任取带排序元素序列中的元素作为基准值,按照该基准值讲待排序列分为两子序列,左子系列中所有元素均小于基准值,右子序列中所有元素大于基准值,然后左右序列重复该过程,直到所有元素都排列在相应位置上为止。
时间复杂度:O(nlogn)
空间复杂度:O(logn)
稳定性:不稳定
#include<stdio.h>
#include<stdlib.h>
void Swap(int *a,int *b)
{
int tmp=0;
tmp=*a;
*a=*b;
*b=tmp;
}
void QuickSort(int *arr,int begin,int end)
{
if(begin>=end)
{
return;
}
int left=begin;
int right=end;
int key=left;
while(left<right)
{
while(left<right&&arr[right]>=arr[key])
{
right--;
}
while(left<right&&arr[left]<=arr[key])
{
left++;
}
if(left<right)
{
Swap(&arr[left],&arr[right]);
}
}
int meet=left;
Swap(&arr[key],&arr[meet]);
QuickSort(arr,begin,meet-1);
QuickSort(arr,meet+1,end);
}
int main()
{
int arr[]={1,11,2,33,4,5,66};
int len=sizeof(arr)/sizeof(int);
for(int i=0;i<len;i++)
{
printf("%d ",arr[i]);
}
printf("\n");
QuickSort(arr,0,6);
for(int i=0;i<len;i++)
{
printf("%d ",arr[i]);
}
return 0;
}
⑦归并排序
基本思想:将数据划分成不同的区间段(每个区间段已经有序),初始时,每个区间段只有一个数据。将相邻两个区间段合并到一块。重复这个过程,直到只剩下一个区间段。
时间复杂度:O(nlogn)
空间复杂度:O(n)
稳定性:稳定
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
void Meger(int *arr, int len, int width, int *brr) // width每个段的数据量个数
{
int low1 = 0;
int high1 = low1 + width - 1;
int low2 = high1 + 1;
int high2 = low2 + width - 1 < len - 1 ? low2 + width - 1 : len - 1;
int index = 0;
// 有两个归并段
while(low2 < len)
{
// 两个段都有数据时
while(low1 <= high1 && low2 <= high2)
{
if(arr[low1] < arr[low2]) brr[index++] = arr[low1++];
else brr[index++] = arr[low2++];
}
// 只剩下一个段有数据
while(low1 <= high1) brr[index++] = arr[low1++];
while(low2 <= high2) brr[index++] = arr[low2++];
low1 = high2 + 1;
high1 = low1 + width - 1;
low2 = high1 + 1;
high2 = low2 + width - 1 < len - 1 ? low2 + width - 1 : len - 1;
}
// 只有一个段的情况
while(low1 < len) brr[index++] = arr[low1++];
for(int i = 0; i < len; ++i)
{
arr[i] = brr[i];
}
}
void MegerSort(int *arr, int len)
{
int *brr = (int*)malloc(sizeof(int) * len);
if(brr == NULL) exit(0);
for(int i = 1; i < len; i *= 2)
{
Meger(arr, len, i, brr);
}
free(brr);
printf("\nfinsh sort\n");
}
int main()
{
int arr[]={11,1,2,4,3,7,90};
int len=sizeof(arr)/sizeof(int);
for(int i=0;i<len;i++)
{
printf("%d ",arr[i]);
}
MegerSort(arr,len);
for(int i=0;i<len;i++)
{
printf("%d ",arr[i]);
}
}
⑧基数排序
基本思想:基数排序是针对有多个关键字(每个关键字的权重不同)的排序算法。
针对一组整形数据:按照权重划分(个位,十位,百位,千位…)。
按照从小权重到大权重进行处理。对于一个数据,拿到这一位的值,将原始数据存储到相应的队列中。一共有关键字取值范围的队列(1,2,3,4,5,6,7,8,9)。整个数据序列全部放入到队列后,按照队列顺序将其中的数据全部Pop出来,再放回到数组中。
基数排序数据之间不需要比较。
空间复杂度: O(dn) d是关键字的取值范围
时间复杂度: O(wn) w是关键字的个数
稳定性: 稳定
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
/*
基数排序:
*/
/*
队列的代码:
*/
typedef struct
{
int *data;
int rear;
int head;
int size;
}Queue;
void InitQueue(Queue *que, int init_size)
{
if(que == NULL) exit(0);
init_size = init_size > 0 ? init_size:10;
que->data = (int*)malloc(sizeof(int) * init_size);
if(que->data == NULL) exit(0);
que->head = que->rear = 0;
que->size = init_size;
}
void ClearQueue(Queue *que)
{
if(que == NULL) exit(0);
que->head = que->rear = 0;
}
void PushQue(Queue *que, int value)
{
if(que == NULL) exit(0);
if(que->rear == que->size) return;
que->data[que->rear++] = value;
}
int EmptyQue(Queue *que)
{
if(que == NULL) exit(0);
if(que->head == que->rear) return 1;
return 0;
}
int PopQue(Queue *que)
{
if(que == NULL) exit(0);
if(EmptyQue(que)) return -1;
int reval = que->data[que->head];
que->head++;
return reval;
}
void DestroyQue(Queue *que)
{
if(que == NULL) exit(0);
free(que->data);
que->data = NULL;
que->head = que->rear = 0;
}
// 获取最大数的位数
int GetMaxWidth(int *arr, int len)
{
int max_value = 0;
for(int i = 0; i < len; ++i)
{
if(arr[i] > max_value)
{
max_value = arr[i];
}
}
int width = 0;
while(max_value) // 1234 1234 % 10
{
width++;
max_value /= 10;
}
return width;
}
//根据位数获取相应位上的值
int GetWidthValue(int value, int width)
{
while(width)
{
if(value == 0) return 0;
width--;
value /= 10;
}
return value % 10;
}
void RadixSort(int *arr, int len)
{
Queue que[10];
for(int i = 0; i < 10; ++i)
{
InitQueue(&que[i], len);
}
int max_width = GetMaxWidth(arr, len);
for(int i = 0; i < max_width; ++i) // i==0 个位 i==1 十位
{
for(int j = 0; j < len; ++j) // 遍历整个待排序序列,按照i给定的位数将数据放到对应的队列中
{
int value = GetWidthValue(arr[j], i);
PushQue(&que[value], arr[j]);
}
int index = 0;
for(int k = 0; k < 10; ++k) // 遍历所有的队列,按照顺序将队列中的数据全部输出
{
while(!EmptyQue(&que[k]))
{
arr[index++] = PopQue(&que[k]);
}
ClearQueue(&que[k]);
}
}
for(int i = 0; i < 10; ++i)
{
DestroyQue(&que[i]);
}
printf("\nfinsh sort\n");
}
int main()
{
int arr[]={11,1,2,4,3,7,90};
int len=sizeof(arr)/sizeof(int);
for(int i=0;i<len;i++)
{
printf("%d ",arr[i]);
}
RadixSort(arr,len);
for(int i=0;i<len;i++)
{
printf("%d ",arr[i]);
}
}