这次,我们来讲解一下关于计数排序,和三路划分及排序的总结。
一:计数排序
原理:
以上面一个数组a为例子:
1.先记录下来每个数字出现的次数------遍历一遍数组
2.用另一个数组来记录:
3.进行排序:
但是呢?这只是0-9的数字,那么数字大的话:eg:100-110的话,难道也是要开0-110的数组吗?这是不是就显得非常地浪费空间啊?所以,我们这里使用一种 相对位置映射计数。
比如:
代码:
void CountSort(int* a, int n)
{
//找最大和最小
int min =a[0], max = a[0];
for (int i = 0; i < n; i++)
{
if (a[i] < min)
{
min = a[i];
}
if (a[i] >max)
{
max = a[i];
}
}
//创建计数数组
int* CountA = (int*)malloc(sizeof(int) * n);
if (CountA == NULL)
{
perror("malloc fail");
return;
}
//数组初始化为0
memset(CountA, 0, sizeof(int) * n);
//算范围+计算次数
int range =max-min+1;
for (int i = 0; i <n; i++)
{
CountA[a[i] - min]++;
}
int j = 0;
for (int i = 0; i < range; i++)
{
while (CountA[i]--)
{
a[j++] = i + min;
}
}
free(CountA);
}
三路划分:
之前我们的快速排序是分成两半,但是呢?当我们出现这样的情况时,会出现效率变低。
当出现大量重复的数字时,那么key是这个重复的数字时,那么就会存在性能下降的情况。
所以呢?针对这种情况,我们来学习一下三路划分的思路。
三路划分:顾名思义,就是分成三路。
思想:
1.当跟key值相等时,往后退
2.比key值小的话,甩到左边。
3.比key值大的话,甩到右边。
4.跟key值相等的话,放中间。
步骤:
这道题要求的时间复杂度有一定要求,像插入排序,选择排序,冒泡排序都是O(n^2)的时间复杂度,所以这三种排序都跑不过。
如果使用的是快速排序得到话,用之前我们所说的方法也是跑不过的,这题就可以使用三路划分的方法了。
代码:
//三数取中
int GetMidNum(int*nums,int left,int right)
{
//int mid=(left+right)/2 ;
int mid=left+rand()%(right-left);
if(nums[left]<nums[mid])
{
if(nums[mid]<nums[right])
{
return mid;
}
else if(nums[left]<nums[right])
{
return right;
}
else{
return left;
}
}
else{
if(nums[left]<nums[right])
{
return left;
}
else if(nums[mid]>nums[right])
{
return mid;
}
else
{
return right;
}
}
}
void Swap(int*p1,int* p2)
{
int x=*p1;
*p1=*p2;
*p2=x;
}
void QuickSort(int*nums,int begin,int end)
{
if(begin>=end)
return;
int midi=GetMidNum(nums,begin,end);
if(midi !=begin)
{
Swap(&nums[begin],&nums[midi]);
}
int key=nums[begin];
int left=begin,right=end;
int cur=begin+1;
while(cur<=right)
{
if(nums[cur]<key)
{
Swap(&nums[left],&nums[cur]);
cur++;
left++;
}
else if(nums[cur]>key)
{
Swap(&nums[cur],&nums[right]);
right--;
}
else{
cur++;
}
}
//[begin,left-1][left,right][right+1,end];
QuickSort(nums,begin,left-1);
QuickSort(nums,right+1,end);
}
int* sortArray(int* nums, int numsSize, int* returnSize) {
srand(time(NULL));
*returnSize=numsSize;
QuickSort(nums,0,numsSize-1);
return nums;
}
//int mid=(left+right)/2 ;
int mid=left+rand()%(right-left);
各大排序的总结:
稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。
简单来说,
现在我们来逐一用具体的例子来说明各个排序的稳定性:
冒泡排序:稳定(相等之后不交换位置就做到稳定了)。
简单选择排序:不稳定:
你看当它有两个数字相同时,是不是会将1换到第一个2中,这是不是就2的相对位置就变了。
直接插入:稳定
希尔排序:不稳定
情况:相同的数据可能会分到不同的组预排。
堆排序:不稳定
归并排序:稳定
快速排序:不稳定
![]()
好了,写了那么久的排序,今天算是完结散花排序部分的啦。(有点多哈哈哈哈哈哈哈幸好坚持写下来了)
到了我们本次的鸡汤部分:
向阳生长,扎根梦想,相信自己,词典中只有胜利没有失败。向往的地方那是心生的起源,留下的伤痛给了弱小,只有勇气才传给了不怕失败的人。