排序是计算机程序设计中的一种重要操作,它的功能是将一个数据元素的序列。重新排列成有序的序列。
排序的时间复杂度,空间复杂度,还有稳定性往往是选取的目标。时间复杂度,空间复杂度越低越好,稳定性越稳定越好。时间,空间即消耗的越小越好。稳定性则是看数据是否有跳跃交换,跳跃的话就不稳定,不跳跃(相邻交换)就稳定。
常见的排序算法有八种。现就这八种展开描述:
1.堆排序
/*
1.建立大根堆:把所有数据按照完全二叉树建立堆,规则如下: 每个根结点下标为i,左孩子下标为(2*i+1),右孩子下标为 (2*+2)。大根堆就是每个子树的根结点数值都要大于孩子结点数值。所以要从最后一个数开始比较,把最大的数放到 所有树的根结点。
2.把大根堆的根结点的值和当前最后子结点的值进行交换,即把大的数据放到后面,即这个数视为有序,除去当前数据序列。再 次建立大根堆,再次交换,依次进行。直到只剩根结点。
//这里使用封装函数,思路更清晰
//时间复杂度为O(nlogn), 空间复杂度O(1),不稳定
*/
void Swap(int* a,int* b)//交换函数
{
int c = *a;
*a = *b;
*b = c;
}
void OneAdjust(int* arr,int len,int root)///排序,使树有序,即根结点大于子结点
{
int tmp = arr[root];
int left = 2 * root + 1;
int right = 2 * root + 2;
int max;
while(left < len)//左孩子下标要小于数据长度
{
max = left;
if(right < len && arr[left] < arr[right])//右孩子下标小于数据长度,且做孩子的值小于右孩子的值
{
max = right;
}
if(tmp > arr[max])
{
break;
}
arr[root] = arr[max];
root = max;
left = 2 * root + 1;
right = 2 * root + 2;
}
arr[root] = tmp;
}
void GreatHeap(int* arr,int len)//建立大根堆
{
int root = (len - 1 - 1) / 2;
for(;root >= 0;root--)
{
OneAdjust(arr,len,root);
}
}
void HeapSort(int* arr,int len)
{
GreatHeap(arr,len);
for(int i = 0;i < len - 1;i++)
{
Swap(&arr[0],&arr[len-1-i]);//交换值
OneAdjust(arr,len-1-i,0);
}
}
#define N 10
int main()
{
srand((unsigned int)time(NULL));//随机函数,生成随机数组
int arr[N] = {0};
int i = 0;
for(;i < N;i++)
{
arr[i] = rand() % 100;//取100以内的值
printf("%5d",arr[i]);
}
printf("\n");
printf("排序如下:\n");
HeapSort(arr,N);
for(i = 0;i < N;i++)
{
printf("%5d",arr[i]);
}
printf("\n");
}
2.快速排序:
/*
1.找数据序列一个数据为基准,设立两个下标i,j.分别放在数据序列的头和尾.j从后往前走,找比基准小的第一个数,并把这个数据放到i的位置。然后i从前往后走,找比基准大的第一个数,放到j的位置。然后依次进行。直到ij相遇。以这个数为中间数,把数据序列分开,前面的数列为比次数小的数,后面的数为比此数列大的数。
2.可以用递归,把两边的数列分别依次排序,调整成有 序数列。思想简单,但操作数过大,会造成内存溢出。
3.也可以用栈的方法求解。
// 时间复杂度(O(nlogn)) 空间复杂度O(1) 不稳定
*/
int Quick(int* arr,int start,int end)//先遍历一遍,划分数据
{
int tmp = arr[start];
while(start < end)
{
while(start < end)
{
if(arr[end] < tmp) break;
end--;
}
arr[start] = arr[end];
while(start < end)
{
if(arr[start] > tmp)break;
start++;
}
arr[end] = arr[start];
}
arr[start] = tmp;
return start;
}
void OneQuick(int* arr,int start,int end)//递归遍历mod两边的数据
{
int mod = Quick(arr,start,end);
if(mod - start > 1)
{
OneQuick(arr,start,mod -1);
}
if(end - mod > 1)
{
OneQuick(arr,mod+1,end);
}
}
void QuickSort(int* arr,int len)
{
OneQuick(arr,0,len-1);
}
3.直接插入:
/*
一列数据,定义两个下标 i 和 j,i的下标为数据序列第二个数,j的下标为数据i的前一个。因为我们可以把一个数据看成有序,两个及以上的数据序无序,所以把i定义为第二个数的下标,并且我们把i下标之前的数据看成有序,把i插入i之前的序列。直到插完结束。
//时间复杂度O(n^2),空间复杂度O(1),稳定
*/
void InsertSort(int* arr,int len)
{
int i,j;
for(i = 1;i < len;i++)
{
int tmp = arr[i];//存arr[i]到tmp
for(j = i-1;j >= 0;j--)
{
if(arr[j] < tmp)
{
break;
}
arr[j+1] = arr[j];//arr[j] > tmp就执行
}
arr[j+1] = tmp;
}
}
4.希尔排序:
/*
其实也是插入排序,在插入排序的基础上分组,先对每组的数据进行排序,使每组数据有序,然后将有序的组进行排序,分组也不固定,视情况而定。一般就5组,3组,1组。以这样的顺序。依次使整个数据序列趋于有序。最终使整个数据序列有序。
//时间复杂度O(n^1.3)~O(n^1.5),空间O(1),不稳定
*/
void ShellGroup(int* arr,int len,int group)
{
int i = 0;
int j = 0;
for(i = group;i < len;i++)
{
int tmp = arr[i];
for(j = i-group;j >= 0;j -= group)
{
if(arr[j] < tmp)
{
break;
}
arr[j+group] = arr[j];
}
arr[j+group] = tmp;
}
}
void ShellSort(int* arr,int len)
{
int str[] = {5,3,1};
for(int i = 0;i < sizeof(str)/sizeof(str[0]);i++)
{
ShellGroup(arr,len,str[i]);
}
}
5.选择排序:
/*
一列数据,把最小的放在第一个,第二个数与剩下的数进行比较 再把最小的放在前面。依次进行,直到最后一个数,排列完毕。
//时间复杂度O(n^2),空间复杂度O(1),不稳定
*/
void SelectSort(int*arr,int len)
{
int i = 0;
int j = 0;
int tmp;
for(int i = 0;i < len-1;i++)
{
int min = arr[i];
int j;
for(j = i+1;j < len;j++)
{
if(arr[j] < min)
{
min = arr[j];
tmp = j;
}
}
if(min != arr[i])
{
Swap(&arr[i],&arr[tmp]);//交换
}
}
}
6.冒泡排序:
/*
挨个比较,小的放前,大的排后。一个数视为有序一趟只能确定一个数,所以n个数需要(n-1)趟。
时间复杂度为O(n^2),空间复杂度为O(1),稳定
*/
void BubbleSort(int* arr,int len)
{
for(int i = 0;i < len-1;i++)//n-1趟
{
for(int j = 0;j < len-1-i;j++)//每走一趟,多一个数据有序,即有序序列数增多,suoyi
{
if(arr[j] > arr[j+1])
{
Swap(&arr[j],&arr[j+1]);//交换
}
}
}
}
基数排序见链接:基数排序_Victor_psl的博客-CSDN博客
归并排序见链接:归并排序--C语言_Victor_psl的博客-CSDN博客