题目要求:初始无序数组{3, 8, 20, -5, 2, -9, 7, 1, -12, 18},按从小到大排序
一、堆排序
基本概念:
大根堆:一个二叉树,它的每个结点的值都大于或等于其左右孩子结点的值。
小根堆:一个二叉树,它的每个结点的值都小于或等于其左右孩子结点的值。
算法简介:
首先将待排序序列构造成一个大根堆,此时,整个序列的最大值就是堆顶的根节点。将其与数组末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。
代码实现:
// 调整堆,其子树必须已经是大根堆
// 参数 array[]:待排序数组,len:数组长度,pidx:待调整堆的根节点索引(数组中的索引)
// return -1:参数错误,0:调整成功
int adjust_heap(int array[], int len, int pidx)
{
if(NULL == array || len < 1 || pidx < 0 || pidx >= len)
{
printf("params error!\n");
return -1;
}
int tmp = 0;
int max_idx = pidx;
int lidx = 2*pidx + 1;//左孩子索引值
int ridx = 2*pidx + 2;//右孩子索引值
while(lidx < len)
{
if(array[lidx] > array[max_idx])
{
max_idx = lidx;
}
if(array[ridx] > array[max_idx] && ridx < len) // 取二叉树中最大值索引
{
max_idx = ridx;
}
if(max_idx != pidx) //最大值放到根节点
{
tmp = array[max_idx];
array[max_idx] = array[pidx];
array[pidx] = tmp;
pidx = max_idx; //递归调整其子树,只需调整最大值对应的子树
lidx = 2*pidx + 1;
ridx = 2*pidx + 2;
}
else
{
break;
}
}
return 0;
}
// 创建大根堆 O(logn)
// 参数 array[]:待排序数组,len:数组长度
// return -1:参数错误,0:创建成功
int create_heap(int array[], int len)
{
if(NULL == array || len < 1)
{
printf("params error!\n");
return -1;
}
int i = len/2 - 1;//最后一个非叶子节点的索引值
for(; i >= 0; i--) //从最后一棵非叶子子树进行调整,向前逐一进行
{
adjust_heap(array, len, i);
}
return 0;
}
// 堆排序 O(nlogn)
// 参数 array[]:待排序数组,len:数组长度
// return -1:参数错误,0:排序成功
int heap_sort(int array[], int len)
{
if(NULL == array || len < 1)
{
printf("params error!\n");
return -1;
}
int result = create_heap(array, len);//首先创建大根堆
if(result != 0)
{
return -1;
}
int i = len - 1;
int tmp = 0;
for(; i > 0; i--)
{
tmp = array[0]; // 最大值放到数组末尾
array[0] = array[i];
array[i] = tmp;
adjust_heap(array, i, 0); //前len-1个数调整为大根堆,取次大值
}
for(i = 0; i < len; i++)
{
printf("%d ", array[i]);
}
printf("\n");
return 0;
}
int main(int argc, char *argv[])
{
int origin[] = {3, 8, 20, -5, 2, -9, 7, 1, -12, 18};
int result = heap_sort(origin, 10);
if(result != 0)
{
return -1;
}
return 0;
}
二、快速排序
算法简介:
(1)在数据集之中,选择一个元素作为"枢轴"(pivot)。
(2)所有小于"枢轴"的元素,都移到"枢轴"的左边;所有大于"枢轴"的元素,都移到"枢轴"的右边。
(3)对"枢轴"左边和右边的两个子集,不断重复第一步和第二步,直到所有子集只剩下一个元素为止。
代码实现:
int quick_sort(int array[], int left, int right)
{
if(NULL == array || left < 0 || right < left)
{
printf("params error!\n");
return -1;
}
int l = left, r = right;
int pivot = array[l]; //选取数组中最左侧的数作为枢轴
while(l < r)
{
while((l < r) && array[r] > pivot) //右侧比“枢轴”值大的数原地不动
{
r--;
}
if(l < r) //比“枢轴”值赋值到原“枢轴”所在位置
{
array[l++] = array[r];
}
while((l < r) && array[l] < pivot) //左侧比“枢轴”值小的数原地不动
{
l++;
}
if(l < r) // 比“枢轴”值大的数放到刚才右侧比“枢轴”大的数位置
{
array[r--] = array[l];
}
} // 第一遍循环结束,此时 l == r ,左侧全是比“枢轴”值小的数,右侧全是大的数
array[l] = pivot;//“枢轴”放到索引 l 的位置
//但此时并不能保证左右两侧的数已排好序
if(l > left)// 如果“枢轴”左侧有多于一个数,则对左侧子数组递归上面的过程
{
quick_sort_core(array, left, l-1);
}
if(r < right) // 如果“枢轴”右侧有多于一个数,则对右侧子数组递归上面的过程
{
quick_sort_core(array, r+1, right);
}
return 0;
}