基于堆的优先队列,堆排序及实现

基于堆的优先队列

基于堆的优先队列

许多应用程序要处理有序的元素,但是不一定要求他们全部有序。许多情况下我们会收集一些元素,处理当前最大的元素,再收集更多的元素。再收集更多元素,再处理当前最大值。这种情况下,一种合适的数据结构可以支持两种操作,删除最大值+插入元素,这就是我们所说的优先队列。

堆的定义

  1. 完全二叉树 ,堆的定义需要用到完全二叉树的定义,那么什么是完全二叉树呢?二叉树的深度为h,除第h层外,其他各层结点都达到最大数,第h层结点都连续集中在最左边。大小为n的完全二叉树高度为logN
    完全二叉树可以直接由数组存储。根结点的位置为1(这里为我们不用数组的位置0,方便后续编写程序)。子节点为位置2,3.子节点的子节点位置在4,5,6,7.)

  2. 堆有序 ,当一颗二叉树的每个结点都大于等于它的两个子结点时,被称为堆有序。

  3. 二叉堆 即堆 。是堆有序完全二叉树。此时从一个叶子结点向上是大概递增(可能会有某两个相邻结点相等)的。从一个父结点向下是大概递减的。

  4. 在这里插入图片描述

堆的存储结构

堆本质上是一颗特殊的完全二叉树,我们用数组按照层级存储。从位置1开始。位置k结点的父结点为 k/2(向下取整)。位置k的子结点为 2k,2k+1。移动也很简单pq[k]向上一层为pq[k/2],向下一层为pq[2k]。
在这里插入图片描述

堆的算法

对于堆的操作,我们会改变堆当前的状态,比如改变当前结点的大小,使其不再是堆有序的,我们利用上浮或下沉操作进行堆的有序化。
堆的有序化过程分为两种情况:由下至上(上浮),有上至下(下沉)

1.由下至上的堆有序化(上浮):若当前结点比它的父结点大,我们交换这两个结点,交换之后还需要判断当前结点与父结点大小,重复上述过程直至堆变得有序化。
代码实现

	private void swim(int k) {
		while(k > 1 && less(k/2, k)) {
			exchange(k, k/2);
			k = k/2;
		}
	}

2.有上至下的堆有序化(下沉):若当前结点变得比它的两个子结点之一更小了,我们就把当前结点与两个子结点中大的那一个进行交换。我们以不断相同的方式重复上述过程,直到它的子结点都比它小。

private void sink(int k) {
		while (2*k <= N) {
			int j = 2*k;
			if (j < N && less(j, j+1)) {
				j++;
			}
			if (less(k, j)) {
				exchange(k, j);
			}else {
				break;
			}
			k = j;
			
		}
	}

函数less 与exchange

	private boolean less(int i, int j) {
		
		return pq[i].compareTo(pq[j]) < 0;
	}
	private void exchange(int i,int j) {
		Key tmp = pq[i];
		pq[i] = pq[j];
		pq[j] = tmp;
	}

3.堆元素的删除最大值及插入元素
删除最大值就是将数组中第一个元素与最后一个元素交换,并将第一个元素进行下沉(sink)操作,以恢复堆的有序。

	private Key delMax() {
   	Key maxKey = pq[1];
   	//将第一个与最后一个交换,并将最后一个位置前移
   	exchange(1, N--);
   	pq[N] = null;
   	sink(1);
   	return maxKey;
   }

插入元素既在数组末尾添加元素,并将这个元素进行上浮(swim)操作

	private void insert(Key value) {
   	pq[N+1] = value;
   	N++;
   	swim(N);
   }

堆排序

堆排序分为两个阶段: 堆的构造与堆的删除最大元素过程
1.堆的构造有N个给定元素构造一个堆是一个递归过程,我们可以从右至左的用sink()函数构造子堆
2.删除最大元素我们删除最大元素后,放入堆缩小后的位置,最后形成的数组即为有序的。



public class HeapSort {
   /**
    * 
    * @param a 输入,数组
    * @param k 在堆中的第k位置,存在a[k-1]
    * @param N 数组长度,堆的大小
    */
   private static void sink(int[] a ,int k ,int N) {
   	while (2*k <= N) {
   		int p = k-1;
   		int j = 2*k-1;
   		if (a[j] < a[j+1] && j+1 < N) {
   			j++;
   		}
   		if (a[p] < a[j]) {
   			int tmp = a[p];
   			a[p] = a[j];
   			a[j] = tmp;
   		}else {
   			break;
   		}
   		k = j+1;
   	}
   	
   }
   	
   }
   public static int[] sort(int[] a) {
   	int N = a.length;
   	int tmp = 0;
   	//构造堆,从N/2开始,对堆中的每一个元素进行下沉
   	for (int k = N/2; k >= 1; k--) {
   		sink(a, k, N);
   	}
   	//处理最大元素,其实也是sink操作
   	while (N >=1) {
   		tmp = a[N-1];
   		a[N-1] = a[0];
   		a[0] = tmp ;
   		sink(a, 1, --N);
   		
   		
   	}
   	
   			
   	return a;
   }


}

3.测试

public static void main(String[] args) {
	int[] m = {15,16,9,14,10,10,7,18,4};
	int[] res = sort(m);
	for (int i : res) {
		System.out.println(i);
	}
4.**结果**
4
7
9
10
10
14
15
16
18




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值