HeapSort堆排序原理与实现

HeapSort堆排序原理与实现

  堆排序是比较重要的数据结构,其主要优点是通过排序二叉树的特性能够记录每个数之间的大小关系,以至于不需要重复比较,对于海量数据排序问题可以减少时间复杂度。

一、基本概念

(1)大根堆(大顶堆):对于一个堆(完全二叉树),根结点的值是比所有结点的值都大,其子树中根结点的值也比子树中其他值都大。大根堆可以获得由小到大的递增序列;其定义为:
A [ i ] ≥ A [ 2 ∗ i ] A[i]\ge A[2*i] A[i]A[2i] A [ i ] ≥ A [ 2 ∗ i + 1 ] A[i]\ge A[2*i+1] A[i]A[2i+1]
(2)小根堆(小顶堆):与大根堆正好相反,定义为:
A [ i ] ≤ A [ 2 ∗ i ] A[i]\le A[2*i] A[i]A[2i] A [ i ] ≤ A [ 2 ∗ i + 1 ] A[i]\le A[2*i+1] A[i]A[2i+1]
例如:下图中,根结点是最大值,且所有子树中的根结点也都是最大值,所以这是一个大根堆
在这里插入图片描述

二、算法原理

  我们以大根堆为例。进行堆排序时,主要分为两步:构建大根堆和调整大根堆。构建大根堆时我们是自底向上的进行比较,使得每次构建的堆都是大根堆;当构建完毕后,每次则是自顶向下的进行比较,保证形成新的大根堆。给定一个无序数列,基本流程为:

一、构建大根堆
(1)按照完全二叉树的层序顺序,插入叶子结点;
(2)比较该叶子结点与其父结点,如果比父结点大,则与父结点交换,此时可以保证当前的子树是大根堆;
(3)如果(2)执行过交换,则再与上一层父结点比较,以此类推,直到不交换为止; (4)重复(1)-(3),直到所有数都添加至这个树中;
二、调整大根堆
(5)此时根结点时最大值,将其与树中最后一个结点进行交换,并将这个最大值剔除这个树;
(6)对剩下的n-1个数进行调整,自顶向下的进行判断,如果根结点比两个孩子结点的最大值要小,则把最大值与根结点交换;
(7)重复(6),直到不交换为止,此时形成了新的大根堆; (8)重复步骤(5)-(7),直到这个树为空,此时我们可以得到一个有序的数列。

  我们给出一个例子:使用堆排序对数列 4 1 16 9 10 8 7 进行递增排序。

一、构建大根堆

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
此时,所有数都插入到大根堆里
在这里插入图片描述
二、跳转大根堆
自顶向下的保证每个子树都是大根堆,蓝色表示已经剔除的结点。
在这里插入图片描述

根结点与树中最后一个节点交换,并剔除
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
我们可以得到一个有序序列: 1 4 7 8 9 10 16;
事实上,如果想获得递减序列,除了使用小根堆方法,也可以对大根堆的序列做反置。

伪代码如下图:
(1)构建大根堆
在这里插入图片描述
(2)调整大根堆
在这里插入图片描述

(3)算法的整体(先自底向上构建,再自顶向下调整)
在这里插入图片描述

三、例题

给出n个坐标点<x1,y1>, <x2, y2>……<xn, yn>,请对他们进行堆排序。坐标点的大小关系是:如果 xi> xj, 则
<xi,yi> 大于 <xj, yj>;若 xi=xj 且 yi >yj, 那么同样有<xi,yi> 大于 <xj, yj>。

在这里插入图片描述

思路:只用大根堆对坐标排序,这里只需要将上述的算法中做一点微调,即重写一下比较函数。

(1)定义结点(数据类型):

class Node {
	public:
		int x; //x坐标 
		int y; //y坐标
	public:
		int getX(){
			return x;
		}
		int getY(){
			return y;
		}
		int setXY(int x, int y){
			this->x = x;
			this->y = y;
		}
};

(2)定义堆排序类,这里面包含上述伪代码包括的三个方法。

class Heap {
	public:
		int size;
		void heapSort(Node a[], int n); //排序 
		void maxHeap(Node a[], int n); //调整大根堆 
		void buildMaxHeap(Node a[], int n); //构建大根堆 
}; 


void Heap::heapSort(Node a[], int n){
	int i;
	buildMaxHeap(a, n);
	for (i = size; i>1; i--)
	{
		swap(a[0], a[i-1]); //交换 
	    size = size - 1;
		maxHeap(a, 1);
 
 
	}
 
}
void Heap::buildMaxHeap(Node a[], int n){
	int i = n / 2;
	for (i; i > 0; i--)
		maxHeap(a, i);
 
}
void Heap::maxHeap(Node a[], int n){
	int leftChild, rightChild, largest;
	leftChild = 2 * n;
	rightChild = 2 * n + 1;
//	int q = sizeof(a);
	if (leftChild <= size && compare(a[leftChild-1], a[n-1]))
		largest = leftChild;
	else
		largest = n;
	if (rightChild <= size && compare(a[rightChild-1], a[largest-1]))
		largest = rightChild;
	if (largest != n){
		
		swap(a[n-1], a[largest-1]);
		maxHeap(a, largest);
	}
}

(3)比较函数

int compare(Node A, Node B){
	if(A.x > B.x){
		return true;
	}else if(A.x == B.x && A.y > B.y){
		return true;
	}
	return false;
}

(4)入口函数

int main(){
	Node a[100];
	Heap t;
	int size = 0;
	cin>>size;
	t.size = size;
	for(int i=0;i<size;i++){
		int x, y;
		cin>>x>>y;
		a[i].setXY(x,y);
	} 
	t.heapSort(a, size);
	int yes = 0;
	for (int i = 0; i < size; i++){
		if(yes == 0){
			yes = 1;
		}else {
			cout<<'\n';
		}
		cout << "(" << a[i].getX() << "," << a[i].getY() << ")";
	}
	return 0;
}

运行效果

在这里插入图片描述


  博客记录着学习的脚步,分享着最新的技术,非常感谢您的阅读,本博客将不断进行更新,希望能够给您在技术上带来帮助。

©️2020 CSDN 皮肤主题: 黑客帝国 设计师:上身试试 返回首页