堆排序、归并排序、快速排序

堆排序
/*
算法步骤:

1)利用给定数组创建一个堆H[0..n-1](我们这里使用最小堆),输出堆顶元素

2)以最后一个元素代替堆顶,调整成堆,输出堆顶元素

3)把堆的尺寸缩小1

4) 重复步骤2,直到堆的尺寸为1
*/
 
#include <iostream>
#include <vector>

// 分类 -------------- 内部比较排序
// 数据结构 ---------- 数组
// 最差时间复杂度 ---- O(nlogn)
// 最优时间复杂度 ---- O(nlogn)
// 平均时间复杂度 ---- O(nlogn)
// 所需辅助空间 ------ O(1)
// 稳定性 ------------ 不稳定

using namespace std;
 
//辅助交换函数
void Swap(int &a, int &b)
{
	int temp = a;
	a = b;
	b = temp;
}
 
//堆排序的核心是建堆,传入参数为数组,根节点位置,数组长度
void Heap_build(int a[],int root,int length)
{
	int lchild = root * 2 + 1;//根节点的左子结点下标
	if (lchild < length)//左子结点下标不能超出数组的长度
	{
		int flag = lchild;//flag保存左右节点中最大值的下标
		int rchild = lchild + 1;//根节点的右子结点下标
		if (rchild < length)//右子结点下标不能超出数组的长度(如果有的话)
		{
			if (a[rchild] > a[flag])//找出左右子结点中的最大值
			{
				flag = rchild;
			}
		}
		if (a[root] < a[flag])
		{
			//交换父结点和比父结点大的最大子节点
			Swap(a[root], a[flag]);
			//从此次最大子节点的那个位置开始递归建堆
			Heap_build(a, flag, length);
		}
	}
}
 
void Heap_sort(int a[], int len){
	//从最后一个非叶子节点的父结点开始建堆
	for (int i = len / 2; i >= 0; --i)  {
		Heap_build(a, i, len);
	}
 	//j表示数组此时的长度,因为len长度已经建过了,从len-1开始
	for (int j = len-1; j > 0; --j)  {
		//交换首尾元素,将最大值交换到数组的最后位置保存
		Swap(a[0], a[j]);  
		//去除最后位置的元素重新建堆,此处j表示数组的长度,最后一个位置下标变为len-2
		Heap_build(a, 0, j);  
	}
}
int main()
{
	int a[10] = {12, 45, 748, 12, 56, 3, 89, 4, 48, 2};
	Heap_sort(a, 10);
 	for (size_t i = 0; i != 10; ++i){
 		cout << a[i] << " ";
 	}
	return 0;
}
//2 3 4 12 12 45 48 56 89 748 
归并排序
#include <stdio.h>

// 分类 -------------- 内部比较排序
// 数据结构 ---------- 数组
// 最差时间复杂度 ---- O(nlogn)
// 最优时间复杂度 ---- O(nlogn)
// 平均时间复杂度 ---- O(nlogn)
// 所需辅助空间 ------ O(n)
// 稳定性 ------------ 稳定


void Merge(int A[], int left, int mid, int right)// 合并两个已排好序的数组A[left...mid]和A[mid+1...right]
{
    int len = right - left + 1;
    int temp[len];       // 辅助空间O(n)
    int index = 0;
    int i = left;                   // 前一数组的起始元素
    int j = mid + 1;                // 后一数组的起始元素
    while (i <= mid && j <= right)
    {
        temp[index++] = A[i] <= A[j] ? A[i++] : A[j++];  // 带等号保证归并排序的稳定性
    }
    while (i <= mid)
    {
        temp[index++] = A[i++];
    }
    while (j <= right)
    {
        temp[index++] = A[j++];
    }
    for (int k = 0; k < len; k++)
    {
        A[left++] = temp[k];
    }
}

// 递归实现的归并排序(自顶向下)
void MergeSortRecursion(int A[], int left, int right)    
{
    if (left == right)    // 当待排序的序列长度为1时,递归开始回溯,进行merge操作
        return;
    int mid = (left + right) / 2;
    MergeSortRecursion(A, left, mid);
    MergeSortRecursion(A, mid + 1, right);
    Merge(A, left, mid, right);
}

// 非递归(迭代)实现的归并排序(自底向上)
void MergeSortIteration(int A[], int len)    
{
	// 子数组索引,前一个为A[left...mid],后一个子数组为A[mid+1...right]
    int left, mid, right;
    for (int i = 1; i < len; i *= 2)        // 子数组的大小i初始为1,每轮翻倍
    {
        left = 0;
        while (left + i < len)              // 后一个子数组存在(需要归并)
        {
            mid = left + i - 1;
            right = mid + i < len ? mid + i : len - 1;// 后一个子数组大小可能不够
            Merge(A, left, mid, right);
            left = right + 1;               // 前一个子数组索引向后移动
        }
    }
}

int main()
{
    int A1[] = { 6, 5, 3, 1, 8, 7, 2, 4 };      // 从小到大归并排序
    int A2[] = { 6, 5, 3, 1, 8, 7, 2, 4 };
    int n1 = sizeof(A1) / sizeof(int);
    int n2 = sizeof(A2) / sizeof(int);
    MergeSortRecursion(A1, 0, n1 - 1);          // 递归实现
    MergeSortIteration(A2, n2);                 // 非递归实现
    printf("递归实现的归并排序结果:");
    for (int i = 0; i < n1; i++)
    {
        printf("%d ", A1[i]);
    }
    printf("\n");
    printf("非递归实现的归并排序结果:");
    for (int i = 0; i < n2; i++)
    {
        printf("%d ", A2[i]);
    }
    printf("\n");
    return 0;
}
快速排序
#include <iostream>
#include <vector>
#include <algorithm>

// 分类 ------------ 内部比较排序
// 数据结构 --------- 数组
// 最差时间复杂度 ---- 每次选取的基准都是最大(或最小)的元素,导致每次只划分出了一个分区,需要进行n-1次划分才能结束递归,时间复杂度为O(n^2)
// 最优时间复杂度 ---- 每次选取的基准都是中位数,这样每次都均匀的划分出两个分区,只需要logn次划分就能结束递归,时间复杂度为O(nlogn)
// 平均时间复杂度 ---- O(nlogn)
// 所需辅助空间 ------ 主要是递归造成的栈空间的使用(用来保存left和right等局部变量),取决于递归树的深度,一般为O(logn),最差为O(n)
// 稳定性 ---------- 不稳定

using namespace std;

int Partition(vector<int>& nums, int left, int right){
	// 这里每次都选择最后一个元素作为基准
    int pivot = nums[right];
    // tail为小于基准的子数组最后一个元素的索引
    int tail = left - 1;
    for(int i = left; i < right; ++i){
        if(nums[i] <= pivot){
            ++tail;
            // 把小于等于基准的元素放到前一个子数组末尾
            if(tail != i){
                swap(nums[tail], nums[i]);
            }
        }
    }
    // 最后把基准放到前一个子数组的后边,剩下的子数组既是大于基准的子数组
    // 该操作很有可能把后面元素的稳定性打乱,所以快速排序是不稳定的排序算法
    swap(nums[tail + 1], nums[right]);
    return tail + 1;
}

void QuickSort(vector<int>& nums, int left, int right){
    if(left > right){
        return;
    }
    int pivot_index = Partition(nums, left, right);
    QuickSort(nums, left, pivot_index - 1);
    QuickSort(nums, pivot_index + 1, right);
}

int main()
{
    int A[] = {5, 2, 9, 4, 7, 6, 1, 3, 8}; // 从小到大快速排序
    int n = sizeof(A) / sizeof(int);
    vector<int> nums(A, A + n);
    QuickSort(nums, 0, n - 1);
    cout << "快速排序结果:";
    for(int i = 0; i < nums.size(); ++i){
        cout << nums[i] << " ";
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值