【堆排序】

  1. 冒泡排序
  2. 选择排序
  3. 快速排序
  4. 插入排序
  5. 希尔排序
  6. 归并排序
  7. 计数排序
  8. 桶排序
  9. 基数排序

推荐视频:哔站堆排序

1. 堆的概念

  • 堆:堆是一种叫做完全二叉树的数据结构,可以分为大根堆,小根堆,而堆排序就是基于这种结构而产生的一种程序算法。
  • 大根堆:每个节点的值都大于或等于其左孩子和右孩子节点的值;
  • 小根堆:每个节点的值都小于或等于其左孩子和右孩子节点的值。

2. 算法步骤

  1. 首先将待排序的数组构造成一个大根堆,此时,整个数组的最大值就是堆结构的顶端;
  2. 将顶端的数与末尾的数交换,此时,末尾的数为最大值,剩余待排序数组个数为n-1;
  3. 将剩余的n-1个数再构造成大根堆,再将顶端数与n-1位置的数交换,如此反复执行,便能得到有序数组。
  4. 升序用大根堆,降序就用小根堆(默认为升序)。

3. 代码实现

3.1 C++版

#include<iostream>
#include<vector>
using namespace std;

void printVector(vector<int>& nums)
{
	for (auto num : nums)
	{
		cout << num << " ";
	}
	cout << endl;
}

void swap(vector<int>& arr, int i, int j)
{
	int temp = arr[i];
	arr[i] = arr[j];
	arr[j] = temp;
}

void heapify(vector<int>& tree, int n, int i)
{
	if (i >= n) return; //递归出口
	int c1 = 2 * i + 1;
	int c2 = 2 * i + 2;
	int max = i;
	if (c1 < n && tree[c1] > tree[max])
	{
		max = c1;
	}
	if (c2 < n && tree[c2] > tree[max])
	{
		max = c2;
	}

	if (max != i)
	{
		swap(tree, max, i);
		heapify(tree, n, max);
	}
}

void build_heap(vector<int>& tree, int n)
{
	int last_node = n - 1;
	int parent = (last_node - 1) / 2;
	for (int i = parent; i >= 0; --i)
	{
		heapify(tree, n, i);
	}
}

void heapSort(vector<int>& nums, int n)
{
	build_heap(nums, n);
	for (int i = n - 1; i >= 0; --i)
	{
		swap(nums, i, 0);
		heapify(nums, i, 0);
	}
}

int main()
{
	vector<int>nums = { 3,6,9,-1,2,5,10,4,33 };
	int n = nums.size();

	cout << "排序前:" << endl;
	printVector(nums);

	heapSort(nums, n);

	cout << "排序后:" << endl;
	printVector(nums);

	//system("pause"); //按任意键继续
	return 0;
}

3.2 C语言版

#include<stdio.h>

void swap(int arr[], int i, int j)
{
	int temp = arr[i];
	arr[i] = arr[j];
	arr[j] = temp;
}

void heapify(int tree[], int n, int i)
{
	if(i >= n) //递归出口 
	{
		return;
	}
	int c1 = 2 * i + 1;
	int c2 = 2 * i + 2;
	int max = i;
	if(c1 < n && tree[c1] > tree[max])
	{
		max = c1;
	}
	if(c2 < n && tree[c2] > tree[max])
	{
		max = c2;
	}
	
	if(max != i)
	{
		swap(tree, max, i);
		heapify(tree, n, max);
	}
}

void build_heap(int tree[], int n)
{
	int last_node = n - 1;
	int parent = (last_node - 1) / 2;
	int i;
	for(i = parent; i >= 0; --i)
	{
		heapify(tree, n, i);
	}
}

void heapSort(int tree[], int n)
{
	build_heap(tree, n);
	int i;
	for(i = n - 1; i >= 0; --i)
	{
		swap(tree, i, 0);
		heapify(tree, i, 0);
	}
}

int main()
{
	int tree[] = {2, 5, 3, 1, 10, 4};
	int n = 6; //元素个数
	heapSort(tree, n);
	
	int i; 
	for(i = 0; i < 6; ++i)
	{
		printf("%d ", tree[i]);
	}
	printf("\n");
	
	return 0;
}

4. 总结

平均时间复杂度: O(n * log n)
最好情况: O(n * log n)
最坏情况:O(n * log n)
排序方式:原地排序
稳定性:非稳定
空间复杂度: O(1)

5. 补充知识

  • 稳定排序:如果 a 原本在 b 的前面,且 a == b,排序之后 a 仍然在 b 的前面,则为稳定排序;
  • 非稳定排序:如果 a 原本在 b 的前面,且 a == b,排序之后 a 可能不在 b 的前面,则为非稳定排序。
  • 原地排序:原地排序就是指在排序过程中不申请多余的存储空间,只利用原来存储待排数据的存储空间进行比较和交换的数据排序;
  • 非原地排序:需要利用额外的数组来辅助排序。
  • 时间复杂度:一个算法执行所消耗的时间(次数,因为同一程序在不同系统的执行时间可能不同);
  • 空间复杂度:运行完一个算法所需的内存大小。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值