java_堆排序

堆排序

采用完全二叉树

由下至上的堆有序化(上浮)

如果堆的有序化,因为某个节点比它的父节点还要大而打破堆的有序化,那么该节点就要上浮,上浮的关键是,节点k的父节点为k/2。

 

由上至下的堆有序化(下沉)

如果某个节点因为变得比它的两个子节点之一要小,那么该节点就要下沉。将该节点与两个子节点中较大的节点进行交换来恢复。下沉的关键是将节点k与它的子节点2k或2k+1交换。

 

插入元素:

将元素添加到堆的末尾,之后进行上浮操作,使堆有序化。

删除最大元素:

将堆顶元素和堆的末尾元素交换,之后将堆顶元素下沉操作使堆有序化。

对于一个含有N个元素的基于堆的优先队列,插入元素需要lgN+1次比较,删除最大元素操作需要不超过2lgN次比较。

删除最大元素需要两次比较,一次用来找出较大的子节点,一次用来确定该节点是否需要下沉。采用的是下沉操作,(需要比较左右子节点中较大的节点,以及父节点是否需要和子节点进行交换。)

 

堆排序:分为两个阶段:一是堆有序:在堆的构建阶段中,将原始数组重新组织安排进一个堆中。而是下沉排序,从堆顶中不断取出最大元素。删除最大元素,并怡然是删除后的堆有序。

 

堆的构造:由N个给定的元素构造一个堆,用下沉操作由右至左构造子堆。数组的每个位置都是一个堆的根节点。如果一个节点的两个子节点都已经是堆了,开始时只需要扫描数组中一半的元素,可以跳过大小为1的子堆,直接从倒数第二层开始扫描。直到扫描到堆顶位置1的元素调用sink()方法,就能保证整个堆都是有序的。

用下沉操作由N个元素构造堆只需少于2N次比较以及少于N次交换。

 

下层排序:堆排序的主要工作都是第二阶段完成的,将堆的最大元素删除,之后放入到堆缩小的数组空出的位置。

堆排序总体:

将N个元素排序,对排序只需少于(2NlogN+2N)次比较,以及NlogN+N次交换。2N来自于堆的构造,2NlogN来自于每次下沉操作最大可能需要2logN次比较。


package Suanfa;
import java.util.*;
//使用堆来实现优先队列
public class heap {
	
}
class MaxPQ<Key extends Comparable<Key>>{//泛型Key
	private Key[] pq;//定义一个数组,用来存储基于堆的完全二叉树来实现优先队列
	private int N=0;
	public MaxPQ(int maxN) {//构造函数,用maxN来创建一个最大优先队列
		pq=(Key[]) new Comparable[maxN+1];
	}
	public boolean isEmpty() {return N==0;}
	public int size() {return N;}
	public void insert(Key v) {
		pq[++N]=v;
		swim(N);
	}
	public Key delMax() {
		Key max=pq[1];
		exch(1,N--);//这样使得长度N减一,并接下来将原N位置置null,避免游离。
		pq[N+1]=null;
		sink(1);//将顶点下沉
		return max;
	}
	private boolean less(int i,int j) {
		return pq[i].compareTo(pq[j])<0;//compareTo比较小于0
	}
	private void exch(int i,int j) {
		Key t=pq[j];
		pq[i]=pq[j];
		pq[j]=t;
	}
	//上浮,由下至上有序化,插入某个节点比父节点大。
	private void swim(int k) {
		while(k>1&&less(k/2,k))//将条件写到while中不断循环
		{
			exch(k/2,k);
			k=k/2;
		}
	}
	//下沉,由上至下有序化,将小于子节点的元素下沉
	private void sink(int k) {
		while(2*k<=N) {
			int j=2*k;
			if(j<N&&less(j,j+1)) j++;//这样j位置或j+1位置就是最大元素位置
			if(!less(k,j)) break;
			exch(k,j);
			k=j;
		}
	}
	
	//堆排序
	public static void sort(Comparable[] a) {
		int N=a.length;
		for(int k=N/2;k>=1;k--) {
			sink(a,k,N);
		}
		while(N>1) {
			exch(a,1,N--);
			sink(a,1,N);
		}
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值