1.堆排
小顶堆:父节点的值<孩子节点的值【从大到小】
大顶堆:父节点的值>孩子节点的值【从小到大】
如果需要升序排序,则需要大顶堆,则将去数据调整为大顶堆,再将根节点和最后一个已知节点交换,然后重复此操作,直到剩余一个节点。
时间复杂度:O(nlog2 n) 空间复杂度:O(1)
稳定性:不稳定【存在跳跃交换】
//这个函数:调整一次大顶堆 时间复杂度O(log2n)
void AdjustHeap(int arr[], int start, int end)
{
assert(arr!=NULL);
int tmp = arr[start];
int i = start;
int j = 2*i+1;
for(j; j<=end; j=2*i+1)
{
if(j<end && arr[j+1]>arr[j])//如果右孩子存在,并且右孩子的值大于左孩子的值
{
j++;
}
if(arr[j] > tmp)
{
arr[i] = arr[j];
i = j;
}
else
{
break;
}
}
arr[i] = tmp;
}
//时间复杂度O(nlogn) 空间复杂度O(1)
void HeapSort(int *arr, int len)
{
assert(arr!=NULL);
//最后一个叶子结点是len-1 所以子推父 得到最后一个非叶子节点的下标为((len-1)-1)/2
//初始状态下,调整大顶堆需要,从最后一个非叶子节点开始,从后向前,从下向上调整
for(int i=(len-1-1)/2; i>=0; i--)
{
AdjustHeap(arr, i, len-1);//第三个参数,找不到规则,直接填最大下标位置即可(饱和式救援)
}
//当此时for循环 执行结束, 初始状态下最繁琐的调整大顶堆搞定了
for(int i=0; i<len-1; i++)//轮数
{
int tmp = arr[0];//交换个是根节点arr[0] 而不是arr[i]
arr[0] = arr[len-1-i];
arr[len-1-i] = tmp;
AdjustHeap(arr, 0, (len-1-i)-1);// (len-1-i)-1
}
}
2.快排
重复划分,找基准值,将小于基准值的值全部放在左边,大于基准值的值全部放在右边:从右向左找比arr[left]更小的值,再从左向右找比arr[right]更大的值放过去,重复此操作,只能left>=right 退出
综合情况来看,是最快的,数组越乱越有序
时间复杂度:O(nlog2 n) 空间复杂度:O(log2 n)
稳定性:不稳定
int Partition(int *arr, int left, int right)
{
assert(arr!=NULL);
int tmp = arr[left];
while(left<right)
{
while(left<right && arr[right]>tmp)
right--;
if(left == right)
{
break;
}
arr[left] = arr[right];
while(left<right && arr[left]<=tmp)
left++;
if(left == right)
{
break;
}
arr[right] = arr[left];
}
arr[left] = tmp;//arr[left] == arr[right]
return left;//return right;
}
void Quick(int *arr, int left, int right)
{
assert(arr!=NULL);
if(left < right)
{
int index = Partition(arr, left, right);
if(left < index-1)//保证基准值左边至少有两个值
{
Quick(arr, left, index-1);
}
if(index+1 < right)//保证基准值右边至少有两个值
{
Quick(arr, index+1, right);
}
}
}
void QuickSort(int *arr, int len)
{
assert(arr!=NULL);
Quick(arr, 0, len-1);
}