赞
踩
堆是一个特殊的数据结构,可以看作是用数组实现的二叉树。
它是一个完全二叉树,除了树的最后一层结点不需要是满的,其它的每一层从左到右都是满的,如果最后一层结点不 是满的,那么要求左满右不满。
数组实现的具体方法就是将二叉树的结点按照层级顺序放入数组中,根结点在位置1,它的子结点在位置2和3,而子结点的子结点则分别在位置4,5,6和7,以此类推。
堆的每个结点都大于等于它的两个子结点。这里要注意堆中仅仅规定了每个结点大于等于它的两个子结点,但这两个子结点的顺序并没有做规定,跟二叉查找树是有区别的。
怎么去确定结点位置呢?如果一个结点的位置为k,则它的父结点的位置为k/2,而它的两个子结点的位置则分别为2k和2k+1。这样在不使用指针的情况下,我们也可以通过计算数组的索引在树中上下移动:从a[k]向上一层,就令k等于k/2,向下一层就令k等于2k或2k+1。
结点的排序:在二叉查找树中,左子节点必须比父节点小,右子节点必须必比父节点大。但是在堆中并非如此,在最大堆中两个子节点都必须比父节点小,而在最小堆中,它们都必须比父节点大,就像是从大到小或者从小排序的一个数组。
内存占用:普通的树所占用的内存会更多,因为它需要指针,指向左右子结点,而堆不用,只需要储存一个数组就可以了,因为它规定了子父结点之间的关系。
平衡以及作用:普通的树更强调平衡性,只有在平衡的时候,树的快速查找才能体现出来,而堆并不需要,它更多的是利用堆排序来进行增删,它们的职能是不一样的。
- public class Heap<T extends Comparable<T>> {
- private T[] items;
- private int N;
-
- public Heap(int capacity) {
- //capacity+1是因为索引从1开始储存数据
- this.items = (T[]) new Comparable[capacity + 1];
- this.N = 0;
- }
-
- // 判断索引i处的值是否小于索引j处的值
- private boolean less(int i,int j){
- return items[i].compareTo(items[j])<0;
- }
- // 交换i处和j处的值
- private void exchange(int i,int j){
- T temp = items[i];
- items[i]=items[j];
- items[j]=temp;
- }
- // 往堆中插入元素
- public void insert(T t){
- items[++N]=t;
- //添加之后进行上浮排序
- swim(N);
- }
- // 删除堆中最大的元素,并返回这个最大元素
- public T delMax(){
- //就是将第一个元素和最后一个元素交换,再让最后索引为null,最后下沉第一个元素
- T max = items[1];
- exchange(1,N);
- items[N] = null;
- N--;
- // 通过下沉算法调整堆的排序
- sink(1);
- return max;
- }
- // 上浮算法,使得添加的元素在合适的位置
- public void swim(int k){
- // 通过循环不断比较与父结点的大小,若比父结点大,就换位置上浮
- while (k > 1) {
- if (less(k/2,k)){
- exchange(k/2,k);
- }
- k=k/2;
- }
- }
- // 下沉算法
- private void sink(int k){
- // 通过循环不断比较当前结点k与其左子结点2k和右子结点2k+1中的较大值
- while (2*k<=N){
- int max;
- if (2 * k + 1 <= N) {
- if (less(2*k,2*k+1)){
- max=2*k+1;
- }else {
- max=2*k;
- }
- }else {
- max=2*k;
- }
- if (!less(k,max)){
- break;
- }
- exchange(k,max);
- k=max;
- }
- }
- }
排序原理:堆排序分为两个过程,一个是堆有序的构建,二是堆的排序。
- public class HeapSort {
- //对source数组中的数据从小到大排序
- public static void sort(Comparable[] source) {
- //创建一个比原数组大1的数组
- Comparable[] heap = new Comparable[source.length + 1];
- //构造堆
- createHeap(source,heap);
- //堆排序
- //定义一个变量,记录heap中未排序的所有元素中最大的索引
- int N = heap.length-1;
- while(N!=1){
- //交换heap中索引1处的元素和N处的元素
- exchange(heap,1,N);
- N--;
- //对索引1处的元素在0~N范围内做下沉操作
- sink(heap,1,N);
- }
- //heap中的数据已经有序,拷贝到source中
- System.arraycopy(heap,1,source,0,source.length);
- }
-
- // 根据原数组source,构造出堆heap
- private static void createHeap(Comparable[] source,Comparable[] heap){
- System.arraycopy(source,0,heap,1,source.length);
- //2.从heap索引的一半处开始倒叙遍历,对得到的每一个元素做下沉操作
- for (int i=(heap.length)/2;i>0;i--){
- sink(heap,i,heap.length-1);
- }
- }
- //判断heap堆中索引i处的元素是否小于索引j处的元素
- private static boolean less(Comparable[] heap,int i,int j){
- return heap[i].compareTo(heap[j])<0;
- }
- //交换heap堆中i索引和j索引处的值
- private static void exchange(Comparable[] heap,int i, int j){
- Comparable tem= heap[i];
- heap[i]=heap[j];
- heap[j]=tem;
- }
- // 在堆heap中对目标元素进行下沉,范围为range
- private static void sink(Comparable[] heap,int target,int range){
- //有子结点
- while (2 * target <= range) {
- int max=0;
- //有两个子结点
- if (2 * target + 1 <= range) {
- if (less(heap,2*target,2*target+1)){
- max=2*target+1;
- }else {
- max=2*target;
- }
-
- }else {
- max=2*target;
- }
- if (less(heap,target,max)){
- exchange(heap,target,max);
- }
- // 为了继续下一个循环
- target=max;
- }
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。