美文网首页
二叉堆和堆排序

二叉堆和堆排序

作者: thebigsilly | 来源:发表于2018-02-03 23:41 被阅读0次
  1. 简介

堆是优先队列最高效的一种数据结构,堆又分为最大堆最小堆。最大堆的孩子节点的key小于或者等于父亲节点的key,而最小堆则是孩子节点key大于或者等于父亲节点key。堆通常的实现是二叉堆。

  1. ADT

insert:插入一个元素。
delete:删除一个元素。
replace:替换一个元素。
findMax(findMin):查找最大或者最小元素。
deleteMax(deleteMin):删除最大或者最小元素。
heapify:将非二叉堆转化为二叉堆。
size:二叉堆的大小。
isEmpty:堆是否为空。
siftDown:查看当前节点是否需要和当前节点的子节点互换,如需要则互换位置。
siftUp:查看当前节点是否需要和当前节点的父节点互换,如需要则互换位置。
heapSort:堆排序

其中核心的操作为heapify、siftDown、siftUp。

  1. 实现思路(以最大二叉堆为例)

Ex:数组A = {20,30,10,50,30,40};
将次数组绘制为二叉树为:


二叉树

很明显这个数组不符合最大二叉堆的定义也即是孩子节点的key小于或者等于父亲节点的key。
我们将每个节点从左到右,从上到下,以此标上1,2,3,4,5,6的序号。
我们可以很明显的看出来两个二叉树的性质:

  1. 最后一个有孩子节点的序号为n / 2。
  2. 若其中n/2 ≥ i ≥ 0,那么A[i]的左右孩子节点分别为A[2 * i + 1]和A[2 * i + 2]。
  3. 如n > i > 0,那么A[i]的父亲节点为A[(i + 1) >> 1 - 1]

siftDown:利用性质2从当前元素的位置找到两个子节点的位置,然后依次两个子节点依次与父节点比较大小来判断是否需要互换位置。
siftUp:利用性质2和性质3获取三个节点的位置,然后依次两个子节点依次与父节点比较大小来判断是否需要互换位置。
heapify:根据堆的定义我们可知道,父亲节点的key大于或等于孩子节点的key。因此我们可以从最后一个含有孩子节点的位置开始从右到左,从下到上依次进行siftDown操作。则可以将数组转化为最大二叉堆。
heapSoft:heapSoft只能使用在已经为最大二叉堆或者最小二叉堆上。1#我们将A[0]和A[n]的元素互换,A[n]则为数组中最大的元素,随后将A[0]到A[n-1]的元素进行heapify运算后那么A[0]到a[n-1]则为最大二叉堆。然后重复1的步骤将第一位和最后一位互换直到互换到根节点,之后就会得到排好序的数组。

  1. 伪代码

siftUp伪代码描述:

siftUp(A, index) //A为数组,index为当前元素的下标
     parentNodeIndex = ((index + 1) >> 1) - 1;
     leftNodeIndex = 2 * parentNodeIndex + 1;
     rightNodeIndex = leftNodeIndex + 1;
     if (A[parentNodeIndex] < A[leftNodeIndex])
            swap(A,parentNodeIndex, leftNodeIndex);//互换位置
     if (A[parentNodeIndex] < A[rightNodeIndex])
            swap(A,parentNodeIndex, rightNodeIndex);//互换位置

heapify伪代码描述:

heapify(A, index) //A为数组,index为当前元素的下标
     for a = A.length >> 1 to 1
            siftDown(a - 1);

sort伪代码描述:

sort(A)
     for i = A.length - 1 to 1 {
            swap(0, i);
            heapify(i); //将i之前的元素构建最大二叉堆
      }
  1. Java实现

public class BinaryHeap{

        private Integer[] array;

        public void heapify(Integer[] target) {
            if (target == null)
               return;

            array = new Integer[target.length];
            System.arraycopy(target, 0, array, 0, target.length); //拷贝数组
            heapifyRecursion(0); //递归版本
    //        if (target.length != 0)  //非递归版本
    //            for (int a = array.length >> 1; a > 0; a--) 
    //                siftDown(a - 1);
        }

        private void heapifyRecursion(int parent) {
            if (parent < array.length >> 1) {
                heapify(2 * parent + 1); //left
                heapify(2 * parent + 2); //right
                siftDown(parent);
            }
        }

        private void siftUp(int index) {
            int parentNodeIndex = ((index + 1) >> 1) - 1;
            int leftNodeIndex = 2 * parentNodeIndex + 1;
            int rightNodeIndex = leftNodeIndex + 1;
            if (array[parentNodeIndex] < array[leftNodeIndex])
                swap(parentNodeIndex, leftNodeIndex);

            if (rightNodeIndex < array.length && array[parentNodeIndex] < array[rightNodeIndex])
                swap(parentNodeIndex, rightNodeIndex);
        }

        private void siftDown(int index) {
            int leftNodeIndex = 2 * index + 1;
            int rightNodeIndex = leftNodeIndex + 1;
            if (array[index] < array[leftNodeIndex])
                swap(index, leftNodeIndex);

            if (rightNodeIndex < array.length && array[index] < array[rightNodeIndex])
                swap(index, rightNodeIndex);
        }

       private void siftDown(int index, int sort) {  //sort为已经排好序的位置
            int leftNodeIndex = 2 * index + 1;
            int rightNodeIndex = leftNodeIndex + 1;
            if (array[index] < array[leftNodeIndex])
                swap(index, leftNodeIndex);

            if (rightNodeIndex < sort && array[index] < array[rightNodeIndex])
                swap(index, rightNodeIndex);
        }

        private void swap(int index, int index2) {
            int a = array[index];
            array[index] = array[index2];
            array[index2] = a;
        }

        private void heapify(int sort) {
            for (int a = sort >> 1; a > 0; a--)
                siftDown(a - 1, sort);
        }

        public void sort() {
            if (array == null || array.length == 0)
                return;

            for (int i = array.length - 1; i > 0; i--) {
                swap(0, i);
                heapify(i);
            }
        }
}

相关文章

  • 二叉树的应用

    1 排序二叉树和堆 用途树结构关系存储方式应用(大根)堆排序完全二叉树根>左子树,根>右子树数组堆排序,取topk...

  • 堆排序

    堆排序 堆:heap 一种数据结构 完全二叉树(每个父节点最多只有两个子节点左节点和右结点) 常用来做堆排序和二叉...

  • PHP算法系列教程(三)-堆排序

    PHP算法系列教程(三)-堆排序 介绍 要介绍堆排序我们就要先了解什么是堆. 什么是堆 堆(二叉堆)可以视为一棵完...

  • java堆排序

    什么是堆排序:图解堆排序堆排序:利用了堆这种数据结构堆数据结构:特殊的完全二叉树,因为具有以下的特点:1)每个结点...

  • 堆排序

    堆 堆排序中用到的是二叉堆,它其实就是一棵近似于完全二叉树树按照层次遍历得到的数组。而堆排序中只要是利用最大(小)...

  • 基于jdk PriorityQueue的思考

    前言 众所周知堆排序就是个二叉堆,其实本质上就是个完全二叉树,我其实是想讲堆排序的,可为什么会和优先队列扯上关系呢...

  • 二叉堆和堆排序

    简介 堆是优先队列最高效的一种数据结构,堆又分为最大堆最小堆。最大堆的孩子节点的key小于或者等于父亲节点的key...

  • 编程马拉松 Day05 堆、二叉堆、堆排序

    堆 堆排序需要用到二叉堆,在开始之前,我们先来了解一下什么是二叉堆。 当二叉树满足满足如下条件时,我们说这个二叉树...

  • HeapSort学习笔记

    完全二叉树 堆排序 什么是堆(Heap)? 堆本质上是一棵二叉树,而且是完全二叉树。 (注:从严格意义上讲,堆可以...

  • 堆排序

    在上篇文章中已经讲过了什么是二叉堆。那么这个二叉堆怎样使用呢?so,这篇文章讲讲堆排序。 首先回顾一下二叉堆的特性...

网友评论

      本文标题:二叉堆和堆排序

      本文链接:https://www.haomeiwen.com/subject/wljpzxtx.html