快速排序
概念
快速排序采用的是分治思想,即在一个无序的序列中选取一个任意的基准元素pivot,利用pivot将待排序的序列分成两部分,前面部分元素均小于或等于基准元素,后面部分均大于或等于基准元素,然后采用递归的方法分别对前后两部分重复上述操作,直到将无序序列排列成有序序列。
code:
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
int partion(int *nums, int left, int right){
int pivot = nums[left];
while(left < right){
while(left < right && nums[right] >= pivot){
right--;
}
nums[left]=nums[right];
while(left < right && nums[left] <= pivot){
left++;
}
nums[right]=nums[left];
}
nums[left] = pivot;
return left;
}
void QuickSort(int *nums, int left, int right){
if(left>=right){
return;
}
int mid = partion(nums, left, right);
QuickSort(nums, left, mid-1);
QuickSort(nums, mid+1, right);
}
void Print(int *nums, int size){
for(int i = 0; i<size; ++i){
printf("%d ", nums[i]);
}
}
int main()
{
int size;
scanf("%d", &size);
int left = 0, right;
int *nums=(int *)malloc(sizeof(int) * size);
right = size - 1;
for(int i = 0; i < size; ++i){
scanf("%d", &nums[i]);
}
QuickSort(nums, left, right);
Print(nums, size);
free(nums);
return 0;
}
#include <iostream>
using namespace std;
const int N = 1e6 + 10;
void quick_sort(int nums[], int l, int r){
if(l >= r)return;
int x = nums[l + r >> 1];
int i = l - 1, j = r + 1;
while(i < j){
do i++; while(nums[i] < x);
do j--; while(nums[j] > x);
if(i < j)swap(nums[i], nums[j]);
}
quick_sort(nums, l, j);
quick_sort(nums, j + 1, r);
}
int main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int n;
cin >> n;
int nums[N];
for(int i = 0; i < n; i++){
cin >> nums[i];
}
quick_sort(nums, 0, n - 1);
for(int i = 0; i < n; i++){
cout << nums[i] << ' ';
}
return 0;
}
堆排序
调整堆(AdjustHeap)。调整堆的目的是确保以某个节点为根的子树满足堆的性质(最大堆或最小堆)。这里我们假设要实现的是最大堆。在堆(无论是最大堆还是最小堆)的数据结构中,左子节点和右子节点与它们的父节点之间存在特定的关系。这些关系确保了堆的性质得以维持。对于任何给定的父节点,其左子节点的索引通常可以通过公式 2 * parent_index + 1 计算得出,而右子节点的索引则通过公式 2 * parent_index + 2 计算得出。这里,parent_index 是父节点在数组中的索引。
构建堆
void AdjustHeap(int *nums, int parent, int size){
int temp = nums[parent];
int child = 2 * parent + 1;
while(child < size){
if(child + 1 < size && nums[child] < nums[child + 1]){
child++;
}
if(temp >= nums[child]){
break;
}
nums[parent] = nums[child];
parent = child;
child = 2 * parent + 1;
}
nums[parent] = temp;
}
代码详解如下:
- 函数定义:
void AdjustHeap(int *nums, int parent, int size)
这个函数有三个参数:
nums:指向整数数组的指针。parent:当前需要调整的子树的根节点在数组中的索引。size:数组的大小。
- 取出当前元素:
int temp = nums[parent];
temp存储了当前父节点的值,接下来我们将尝试将这个值放到正确的位置,以确保以该父节点为根的子树是一个最大堆。
- 初始化子节点索引:
int child = 2 * parent + 1;
child初始化为父节点的左子节点在数组中的索引。
- 循环调整子节点:
while(child < size)
只要子节点的索引还在数组范围内,就持续调整。
- 找到左右子节点中的较大值:
if( child + 1 < size && nums[child] < nums[ child + 1 ] ){
child++;
}
如果右子节点存在且其值大于左子节点的值,则更新 child 为右子节点的索引。
- 判断父节点是否需要移动:
if (temp >= nums[child]){
break;
}
如果父节点的值已经大于或等于子节点的值,说明父节点已经在正确的位置,跳出循环。
- 移动父节点到子节点的位置:
nums[parent] = nums[child];
将子节点的值赋给父节点,这样父节点就暂时占据了子节点的位置。
- 更新父节点和子节点的索引:
parent = child;
child = 2 * parent + 1;
将父节点更新为刚刚赋值的子节点,并重新计算新的子节点的索引。
- 将 temp 值放到最终的位置:
nums[parent] = temp;
循环结束后,将 temp(原始父节点的值)放到最终的正确位置。
通过这个过程,我们可以确保以 parent 为根的子树满足最大堆的性质。
交换函数
void swap(int *a, int *b){
int temp = *a;
*a = *b;
*b = temp;
}
排序
void HeapSort(int *nums, int size){
// 1. 构建初始堆
for(int i = size / 2 - 1; i >= 0; i--){
AdjustHeap(nums, i, size);
}
// 2. 堆排序过程
for(int i = size - 1;i >= 0; i--){
// 将堆顶元素与当前未排序部分的最后一个元素交换
swap(&nums[0], &nums[i]);
// 重新调整堆(除了已经排序好的最后一个元素)
AdjustHeap(nums, 0, i);
}
}
- 这段代码是堆排序算法中构建初始堆的部分。在堆排序中,我们首先需要把一个无序的数组转换成一个大顶堆(或小顶堆,取决于排序需求)。
for (int i = size / 2 - 1; i >= 0; i--) {
adjustHeap(arr, i, size);
}
- 构建初始堆
for(int i = size / 2 - 1; i >= 0; i--){
AdjustHeap(nums, i, size);
}
- 堆排序过程
for(int i = size - 1;i >= 0; i--){
// 将堆顶元素与当前未排序部分的最后一个元素交换
swap(&nums[0], &nums[i]);
// 重新调整堆(除了已经排序好的最后一个元素)
AdjustHeap(nums, 0, i);
}
打印函数
void Print(int *nums, int size){
for(int i = 0;i < size; ++i){
printf("%d ",nums[i]);
}
}
完整代码
#include <stdio.h>
void swap(int *a, int *b){
int temp = *a;
*a = *b;
*b = temp;
}
void AdjustHeap(int *nums, int parent, int size){
int temp = nums[parent];
int child = 2 * parent + 1;
while(child < size){
if(child + 1 < size && nums[child] < nums[child + 1]){
child++;
}
if(temp >= nums[child]){
break;
}
nums[parent] = nums[child];
parent = child;
child = 2 * parent + 1;
}
nums[parent] = temp;
}
void HeapSort(int *nums, int size){
for(int i = size / 2 - 1; i >= 0; i--){
AdjustHeap(nums, i, size);
}
for(int i = size - 1;i >= 0; i--){
swap(&nums[0], &nums[i]);
AdjustHeap(nums, 0, i);
}
}
void Print(int *nums, int size){
for(int i = 0;i < size; ++i){
printf("%d ",nums[i]);
}
}
int main()
{
int nums[]={5,2,8,9,0,3,7};
int size = sizeof(nums) / sizeof(nums[0]);
HeapSort(nums, size);
Print(nums, size);
return 0;
}
本文详细解析了快速排序和堆排序两种常用排序算法,包括它们的分治思想、基准元素的选择与交换、调整堆的过程,以及C语言的代码实现。

被折叠的 条评论
为什么被折叠?



