必威体育Betway必威体育官网
当前位置:首页 > IT技术

堆排序

时间:2019-10-17 07:44:26来源:IT技术作者:seo实验室小编阅读:71次「手机版」
 

堆排序

首先我们有一个数组arr,他的里面有一些元素,如果要我们找到这里面的最小值,我们是不是要去扫描一遍这个数组?如果这个时候他要求我们去找一个次小值,或者删除这个数且加一个数进来问你最小值,那我们是不是得又扫描一遍,这样重复n次就扫描了n次也就是0[n]的效率,n一大就gg,那这个时候怎么办呢?堆出现了。

堆是一个完全二叉树,所有顶点的左子树和右子树都比他本身要小,这就叫最大堆(大根堆)因为这样一来他的根就是就是最大值,所有顶点的左子树和右子树都比他本身要大这就是最小堆(小根堆)。这样我们求这个数组的最小值就可以访问堆顶元素即可。

堆的应用,求第n个数里第k小,第k大的数,排序;

一个问题

为什么堆排序(升序)时用最大堆优于最小堆,那是不是降序的时候最小堆优于最大堆,他妈的害我设计了一个自动化的实验,程序都写出来了才发现这个问题根本就是我记错了导致的!仔细思考就能发现,无论是最小堆还是最大堆,pop操作都是需要swap(1,n)然后向下调整,向下调整的时间都是logN有什么区别啊靠。

其实这里说的是在找第k小的时候,建立一个大小为k的最大堆比较方便(遍历完了堆顶就是他了),找第k大的时候建立一个大小为k的最小堆,遍历完堆顶就是他了。mmp我居然搞了大概两个小时。

堆的手写实现(可能有点搓)

手写之前注意四个点

  1. 如图

在这里插入图片描述

  1. this.papa.index=[this.index/2];(向下取整)

  2. 如何维护一个堆的特征,从两个方面考虑(i=this.index)

    • 元素在数组尾部,需要向上调整( if(arr[i]<arr[papa(i)])swap(i,pre(i)) ;)
    • 元素在数组头部,需要向下调整(类比上面的就行了)
  3. 如何创建一个堆?可以从零开始一个一个的加入但是这样太慢了,我们可以直接把无序的数组直接给他,然后从arr.length/2这个地方,也就是第二排(arr.length/2一定对应第二排最后一个元素)每个元素向下调整即可在这里插入图片描述

    建立堆的时间复杂度在这里插入图片描述

class heap{
	public:
		int a[Max + 1];
		int length;
		int pre(int nowI);//返回papa坐标
		int l(int nowI);//返回左子树坐标
		int r(int nowI);//返回右子树坐标
		bool creat();
		bool print();
		void shiftDownMin(int nowI);//向下调整
		void shiftUpMin(int nowI);//向上调整
		int deletBestMin();//pop最小的元素
		void addMin(int val);
};
void heap::addMin(int val) {//增加一个元素,在尾部加然后向上调整
	a[++length] = val;
	shiftUpMin(length);
}
int heap::deletBestMin() {
	int t = a[1];//把根保留一下以便返回
	a[1] = a[length--];//把最后一个元素拿到前面来这里也没法拿第别的,不然结构变了
	shiftDownMin(1);//向下调整
	return t;
}
int heap :: pre(int nowI) {
	return nowI / 2;//直接向下取整没有问题
}
int heap::l(int nowI) {
	if(nowI * 2 <= length)
	return nowI*2;
	return nowI;
}
int heap::r(int nowI) {
	if (nowI * 2 + 1 <= length)
	return nowI*2+1;
	return nowI;
}
void heap::shiftDownMin(int nowI) {
	if (a[nowI] > a[l(nowI)] || a[nowI] > a[r(nowI)]) {
		if (a[l(nowI)] < a[r(nowI)]) {
			swap(&a[nowI], &a[l(nowI)]);
			shiftDownMin(l(nowI));
		}else {
			swap(&a[nowI], &a[r(nowI)]);
			shiftDownMin(r(nowI));
		}
	}
}
void heap::shiftUpMin(int nowI) {
	if (a[nowI] < a[pre(nowI)]) {
		swap(&a[nowI], &a[pre(nowI)]);
		shiftUpMin(pre(nowI));
	}
}
bool heap::creat() {
	cin >> length;
	for (int i = 1; i <= length; i++) {
		scanf("%d", &a[i]);
	}
	for (int i = length/2; i >= 1; i--){
		shiftDownMin(i);
	}
	return 1;
}

堆的stl实现

#include <queue>//需要这个头文件
using namespace std;//以及这个命名空间
typedef struct {
	int i, v;
}node;
inline bool operator < (const node &n1, const node &n2) {//用结构体的话需要重载运算符
	return n1.v < n2.v;
}
priority_queue<node, vector<node>, greater<node>> hep;//最小堆的声明
priority_queue<int, vector<int>, less<int>> maxHeap; //这是最大堆
//priority_queue<Type, Container, Functional>

hep.size():返回堆内元素个数。

hep.empty():如果堆为空,返回真,否则返回假。

hep.top():返回堆顶元素。

hep.pop():删除堆顶元素,自动整理。

hep.push(int x):插入一个元素,自动整理。

这是一种方式,另外一种用algorithm库

void put(int x){//用于插入元素
    hep[++len]=x;//len表示堆中元素个数
    push_heap(hep+1,hep+1+len);//就像排序一样
}
int get(){//用于得到堆顶元素
    pop_heap(hep+1,hep+1+len);
    //这个函数会将堆顶放到堆尾,所以后面是hep[len--];
    return hep[len--];
}
//创建一个堆
vector<int> v2{6, 1, 2, 5, 3, 4};
make_heap(v2.begin(), v2.end(), greater<int>());//greater是最小堆,这里也可以是自己的比较函数

v1.push_back(200);//push_back仅用于在尾部增加元素
push_heap(v1.begin(), v1.end());//仅用于把最后一个元素向上调整,push_heap(1,n)前需要确保[1,n-1]已经是个堆
//printcontainer(v1, "after push_heap: ");         // after push_heap: 200 5 6 1 3 2 4

pop_heap();//它的作用是:交换*first和*(last-1), 然后把[first, last-1)建成一个max heap. 也就是说把堆顶的最大元素交换到区间尾,然后把除了尾部的元素的剩余区间重新调整成堆。 
pop_back();//上面的只是交换,并没有取走,也就是end迭代器没有减减;

sort_heap( RandomIt first, RandomIt last, Compare comp );//即经典的堆排序算法,通过每次弹出堆顶直到堆为空,依次被弹出的元素就组成了有序的序列了。STL中的priority_queue即使用heap的这个特性来实现。

相关阅读

八大排序算法--堆排序

序言 对于堆排序的学习,实际上就是对于 堆 这一种数据结构的学习,把堆学会了,堆排序自然也就学会了。1、为什么使用堆这种数据结构

堆排序算法详解

一、堆排序算法原理和动态图解 将待排序的序列构造成一个大顶堆。此时,整个序列的最大值就是堆顶的根节点。将它移走(其实就是将

选择排序(简单选择排序和堆排序)

选择排序的基本思想是:每一趟(例如第i趟)在后面的 n-i+1 个待排序元素中选取关键字最小的元素,作为有序子序列的第i个元素,直到第 n-1

排序算法之堆排序(Heap Sort)——C语言实现

堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。 算法分析 在学习堆排序之前我们要先

分享到:

栏目导航

推荐阅读

热门阅读