一、排序分析:
5、快速排序
时间复杂度:O(nlog2n)–有序–>O(n^2)
空间复杂度:O(log2n)
算法稳定性:不稳定
//快速排序(分区、递归操作):固定位置选取基准法low、随机选取(有序数据的优化)、三分选取
//优化: 1、直接插入(少于100各元素) 2、聚集基准位置法
//取基准(分数据段):
//(1)low、high 对应元素比较交换(初始par 为首元素,l 为首元素,h为尾部元素),tmp存放l值(默认其左侧均小于自身,右侧均大于自身)
//(2)low++寻找大于基准的覆盖high,--high寻找小于基准的覆盖low
//(3)相遇点即基准位置,每趟l、h相遇即结束,将tmp值覆盖相遇点,此时基准tmp之前均小于,之后均大于
//
//一趟快速排序的过程,结果得到新基准位置
int Partion(int *arr, int low, int high)//基准位置
{
int tmp = arr[low];
while(low < high)
{
while(low < high && arr[high] >= tmp)
{
--high;
}
if(low < high)
{
arr[low] = arr[high];
}//找到小于tmp的元素覆盖arr[low]
else
{
break;
}
while(low < high && arr[low] <= tmp)
{
low++;
}
if(low < high)
{
arr[high] = arr[low];
}//找到大于tmp的元素覆盖arr[high]
else
{
break;
}
}
arr[low] = tmp;//每趟结束(low、high相遇),此时为基准位置,重新恢复当前基准数值
return low;
}
void Quick(int *arr, int start, int end)//递归
{
int par = Partion(arr,start, end);//第一趟排序(初始par 为首元素,l 为首元素,h为尾部元素,结果将数据段分成左右两区),其余趟则依据上一趟得到基准进行排序
if(par > start+1)
{
Quick(arr,start, par-1);
}//左边递归排序
if(par < end-1)
{
Quick(arr,par+1, end);
}//右边递归排序
}
void QuickSort(int *arr, int len)
{
Quick(arr, 0, len-1);
}
//将每次找基准的数对存放在栈中
void QuickSort_Stack(int *arr, int len)//栈(用于存放数对,即每趟low、high的值)
{
int *s = (int *)malloc(sizeof(int) * len);
assert(s != NULL);
int top = 0;
int low= 0;
int high = len-1;
//第一趟快排
int par = Partion(arr,low, high);
//第一次数对入栈(先low后high)
if(par > low+1)
{
s[top++] = low;
s[top++] = par-1;
}//左
if(par < high-1)
{
s[top++] = par+1;
s[top++] = high;
}//右
//出栈、第二次及以上数对入栈出栈
while(top > 0)
{
//第一次数对出栈
high = s[--top];
low = s[--top];
//下一趟快排
int par = Partion(arr,low, high);
//第二次及以上数对入栈出栈
if(par > low+1)
{
s[top++] = low;
s[top++] = par-1;
}//左
if(par < high-1)
{
s[top++] = par+1;
s[top++] = high;
}//右
}
}
6、堆排序
时间复杂度:O(nlog2n)
空间复杂度:O(1)
算法稳定性:不稳定
//堆排序:大根堆
//(1)默认无序数组为完全二叉树
//(2)构造大根堆:每次0号下标的元素即根节点确定为无序数组中的最大值
// 子为n,求父:(n-1)/2
// 父为n,求子:左孩子2n+1;右孩子2n+2
//(3)交换根节点,再次调整构造大根堆
//整个排序过程由后至前开始有序排列
void Adjust(int *arr, int start, int end)//一次调整(根节点为最大元素)
{
int tmp = arr[start];//最后一个父节点
//从当前父节点开始向下遍历比较子节点以及子节点的子节点...
for(int i = 2*start+1; i <= end; i = 2*i+1)
{
if(i < end && arr[i] < arr[i+1])
{
i = i + 1;
}//存在子节点,并得到两个子节点的最大值
if(arr[i] > tmp)
{
arr[start] = arr[i];
start = i;
}//子节点的最大值也大于父节点时,将最大值赋给父节点
else
{
break;
}
}
arr[start] = tmp;//遍历结束后将tmp放置合适位置
}
void HeapSort(int *arr, int len)//交换堆排序
{
//得到大根堆(一个有序元素)
for(int i = (len-1-1)/2; i >= 0; i--)
{
Adjust(arr, i, len-1);
}
//交换
int tmp = 0;
for(int i = 0; i < len-1; i++)
{
Swap(arr,0, len-1-i);//得到大根堆的同时,0号下标元素已经确定为最大值,则直接交换成为最后一个元素有序
Adjust(arr, 0, (len-1)-1-i);//再需调整(len-1)-1次,再将0号下标元素确定为无序数组中的最大元素
}
}
二、代码实现
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
static void Swap(int *arr, int i, int j)
{
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
//快速排序
int Partion(int *arr, int low, int high)//基准位置
{
int tmp = arr[low];
while(low < high)
{
while(low < high && arr[high] >= tmp)
{
--high;
}
if(low < high)
{
arr[low] = arr[high];
}//找到小于tmp的元素覆盖arr[low]
else
{
break;
}
while(low < high && arr[low] <= tmp)
{
low++;
}
if(low < high)
{
arr[high] = arr[low];
}//找到大于tmp的元素覆盖arr[high]
else
{
break;
}
}
arr[low] = tmp;//每趟结束(low、high相遇),此时为基准位置,重新恢复当前基准数值
return low;
}
void Quick(int *arr, int start, int end)//递归
{
int par = Partion(arr,start, end);//第一趟排序(初始par 为首元素,l 为首元素,h为尾部元素,结果将数据段分成左右两区),其余趟则依据上一趟得到基准进行排序
if(par > start+1)
{
Quick(arr,start, par-1);
}//左边递归排序
if(par < end-1)
{
Quick(arr,par+1, end);
}//右边递归排序
}
void QuickSort(int *arr, int len)
{
Quick(arr, 0, len-1);
}
//将每次找基准的数对存放在栈中
void QuickSort_Stack(int *arr, int len)//栈(用于存放数对,即每趟low、high的值)
{
int *s = (int *)malloc(sizeof(int) * len);
assert(s != NULL);
int top = 0;
int low= 0;
int high = len-1;
//第一趟快排
int par = Partion(arr,low, high);
//第一次数对入栈(先low后high)
if(par > low+1)
{
s[top++] = low;
s[top++] = par-1;
}//左
if(par < high-1)
{
s[top++] = par+1;
s[top++] = high;
}//右
//出栈、第二次及以上数对入栈出栈
while(top > 0)
{
//第一次数对出栈
high = s[--top];
low = s[--top];
//下一趟快排
int par = Partion(arr,low, high);
//第二次及以上数对入栈出栈
if(par > low+1)
{
s[top++] = low;
s[top++] = par-1;
}//左
if(par < high-1)
{
s[top++] = par+1;
s[top++] = high;
}//右
}
}
//堆排序
void Adjust(int *arr, int start, int end)//一次调整(根节点为最大元素)
{
int tmp = arr[start];//最后一个父节点
//从当前父节点开始向下遍历比较子节点以及子节点的子节点...
for(int i = 2*start+1; i <= end; i = 2*i+1)
{
if(i < end && arr[i] < arr[i+1])
{
i = i + 1;
}//存在子节点,并得到两个子节点的最大值
if(arr[i] > tmp)
{
arr[start] = arr[i];
start = i;
}//子节点的最大值也大于父节点时,将最大值赋给父节点
else
{
break;
}
}
arr[start] = tmp;//遍历结束后将tmp放置合适位置
}
void HeapSort(int *arr, int len)//交换堆排序
{
//得到大根堆(一个有序元素)
for(int i = (len-1-1)/2; i >= 0; i--)
{
Adjust(arr, i, len-1);
}
//交换
int tmp = 0;
for(int i = 0; i < len-1; i++)
{
Swap(arr,0, len-1-i);//得到大根堆的同时,0号下标元素已经确定为最大值,则直接交换成为最后一个元素有序
Adjust(arr, 0, (len-1)-1-i);//再需调整(len-1)-1次,再将0号下标元素确定为无序数组中的最大元素
}
}
void Show(int *arr, int len)
{
for(int i = 0; i < len; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
int main()
{
int arr[] = {4,1,3,2,5};
int len = sizeof(arr) / sizeof(arr[0]);
HeapSort(arr, len);
//QuickSort(arr, len);
//QuickSort_Stack(arr, len);
Show(arr, len);
getchar();
return 0;
}