数据结构学习——堆

1 基本介绍

堆数据结构是一种数组对象,它可以被视为一颗完全二叉树。堆的访问可以通过三个函数来进行即,

parent(i)
    return floor(i/2);
left(i)
    return 2i;
right(i)
    return 2i + 1;

left操作可以通过一步左移操作完成,right操作可以通过左移并在地位+1实现,parent操作则可以通过把i右移一位得到。在实现中通常会使用宏或者内联函数来实现这三个操作。

二叉堆有两种,最大堆和最小堆。对于最大堆有

A[i] >= A[left(i)] && A[i] >= A[right(i)]

最小堆则是

A[i] <= A[left(i)] && A[i]<= A[right(i)]

最堆排序算法中,使用的是最大堆,最小堆通常在构造优先队列时使用。堆可以被看成是一棵树,结点在堆中的高度定义为从本结点到叶子的最长简单下降路径上边的数目;定义堆的高度为树根的高度。

2 原理部分

下面就关于堆的几个基本函数做一下说明:

maxHeapify(),运行时间为O(lgn)用于保持堆的性质;

bulidMaxHeap(),以线性时间运行,可以在无序的输入数组基础上构造出最大堆;

heapSort(),运行时间为O(nlgn),对一个数组原地进行排序。

2.1 保持堆的性质

其输入为一个数组A和下标i,当maxHeapify被调用时,我们假定以leftIi)和right(i)为根的两棵二叉树都是最大堆,但这时A[i]可能小于其子女,这样就违反了最大堆的性质。maxHeapify让A[i]在最大堆中“下降”使以i为根的子树成为最大堆。伪代码如下所示。

maxHeapify(A,i)
    l <- left(i)
    r <- right(i)
    if l <= heap-size[A] and A[l] > A[i]
        then largest <- l
        else largest <- i
    if r <= heap-size[A] and A[r] > A[largest]
        then largest <- r
    if largest != i
        then exchange A[i] <-> A[largest]  // 交换i和比它大的那个子结点
            maxHeapify(A,largest);         // 递归调用

2.2 建堆

我们可以自底向上用maxHeapify来将一个数组A[1...n](此处 n = length[A])变成一个最大堆。子数组A[floor(n/2) + 1)..n]中的元素都是树中的叶子节点,因此每个都可以看作是一个只含一个元素的堆。buildMAXHeap对树中每一个其他节点都调用一次maxHeapify。伪代码如下所示。

buildMaxHeap(A)
    heap-size[A] <- length[A]
    for i <- floor(length[A] / 2) downto 1
        do max-heapify(A,i)

2.3 堆排序算法

开始时,堆排序算法先用buildMaxHeap将输入数组A[1..n](此处 n = length[A])构造成一个最大堆,因为数组中的最大元素在根A[1],则可以通过把它与A[n]互换来达到最终正确的位置。现在,如果从堆中“去掉”节点n(通过减小heap-size[A]),可以很容易的将A[1..n-1]建成最大堆,原来根的子女仍是最大堆,而新的根元素可能违背了最大堆性质。这是调用maxHeapify(A,1)就可以保持这一性质,在A[1..(n-1)]构造出最大堆。堆排序算法不断重复这个过程,堆的大小由n-1一直降到2.

heapSort(A)
    buildMaxHeap(A)
    for i <- length[A] downto 2
        do exchange A[1] <-> A[i]
            heap-size[A] <- heap-size[A] - 1
            maxHeapify(A,1)

虽然堆排序算法是一个很nice的算法,但是在实际应用中,快速排序的一个好的实现往往优于堆排序。

3 java实现

3.1 数据结构

Heap.java

import java.io.Serializable;

/**
 * Date: 2014/8/17
 * Time: 16:02
 */
public class Heap implements Serializable{
    private int heapLength;
    private int [] data;

    public int getHeapLength() {
        return heapLength;
    }

    public void setHeapLength(int heapLength) {
        this.heapLength = heapLength;
    }

    public int[] getData() {
        return data;
    }

    public void setData(int[] data) {
        this.data = data;
    }
}

3.2 操作类

HeapSort.java

/**
 * Created with IntelliJ IDEA.
 * Date: 2014/8/17
 * Time: 15:39
 */
public class HeapSort {
    public final static int getLeft(int i) {
        return i << 1;
    }

    public final static int getRight(int i) {
        return (i << 1) + 1;
    }

    public final static int getParent(int i) {
        return i >> 1;
    }

    /**
     * 保持堆的性质
     *
     * @param heap
     * @param i
     */
    public static void maxHeapify(Heap heap, int i) {
        if (null == heap || null == heap.getData() || heap.getData().length <= 0 || i < 0)
            return;
        int l = getLeft(i);
        int r = getRight(i);
        int largest = 0;
        if (l < heap.getHeapLength() && heap.getData()[l] > heap.getData()[i])
            largest = l;
        else
            largest = i;
        if (r < heap.getHeapLength() && heap.getData()[r] > heap.getData()[largest])
            largest = r;
        if (largest != i) {
            int tmp = heap.getData()[i];
            heap.getData()[i] = heap.getData()[largest];
            heap.getData()[largest] = tmp;
            maxHeapify(heap, largest);
        }
    }

    /**
     * 建立最大堆
     *
     * @param array
     * @return
     */
    public static Heap bulidMaxHeap(int[] array) {
        if (null == array || array.length <= 0)
            return null;
        Heap heap = new Heap();
        heap.setData(array);
        heap.setHeapLength(array.length);
        for (int i = (array.length >> 1); i >= 0; i--)
            maxHeapify(heap, i);
        return heap;
    }

    /**
     * 堆排序
     *
     * @param array
     */
    public static void heapSort(int[] array) {
        if (null == array || array.length <= 0)
            return;
        Heap heap = bulidMaxHeap(array);
        if (null == heap)
            return;
        for (int i = heap.getHeapLength() - 1; i > 0; i--) {
            int tmp = heap.getData()[0];
            heap.getData()[0] = heap.getData()[i];
            heap.getData()[i] = tmp;
            heap.setHeapLength(heap.getHeapLength() - 1);
            maxHeapify(heap, 0);
        }
    }
}

3.3 测试类

Main.java

public class Main {

    public static void main(String[] args) {
        int a[] = {9, 3, 4, 1, 5, 10, 7};
        System.out.println("Hello World!");
        Sort sort = new Sort();
//        sort.bubbleSort(a);
//        sort.selectSort(a);
//        sort.insertionSort(a);
        HeapSort.heapSort(a);
        for (int i = 0; i < a.length; i++)
            System.out.println(a[i]);
    }
}

数据结构学习——堆

时间: 2024-10-13 17:57:41

数据结构学习——堆的相关文章

[数据结构学习备忘录]堆及其堆排序

[数据结构学习备忘录] 堆 一种数据结构,物理存储方式:数组 逻辑存储方式:近似于完全二叉树,假定i为堆元素的序数[Index],那么i/2就是该元素的左子树,(i/2 + 1)就是该元素的右子树,分为两种堆:大根堆.小根堆:这两种堆的区别是:大根堆的根节点元素的值比左右子树的值都要大,小根堆则相反. 可用这种数据结构进行排序,称为堆排序. 与该数据结构相关的关键算法: ①   MaxHeaplfy 当堆的元素顺序出现错误时的调整函数 ②   BulidMax[min]Heap 建立大[小]根堆

数据结构学习之二叉排序树

介绍:二叉排序树是以一定的规则排列树中元素,因而可以进行快速的排序和查询的树状数据结构,一般规则是:对于树中任意一个节点,左孩子严格小于根,根严格小于右孩子,有点像大根堆.(只是大根堆中左右孩子关系并不确定,且和根的关系是统一的,而且有上浮和下沉操作使得大根堆总是一棵完全二叉树,其不断弹出堆顶形成有序列的过程叫做堆排序.虽然二叉排序树中也有旋转操作使得树尽量平衡,但是由于数值大小分明的左右孩子,在进行平衡操作时远不如大根堆方便快捷.)对于一棵已经构造完成的排序二叉树,它的中序遍历序列即为升序排列

小猪的数据结构学习笔记(四)

小猪的数据结构学习笔记(四) 线性表之静态链表 --转载请注明出处:coder-pig 本章引言: 在二,三中中我们分别学习了顺序表中的线性表与单链表,线性表有点类似于 我们前面所学的数组,而单链表使用的最多的是指针,这里问个简单的问题, 如果是在以前没有指针的话,前辈先人们怎么实现单链表呢?大家思考下! 没有指针,那么用什么来代替呢?前辈先人们非常机智,想出了使用下标+游标的方式 来实现单链表的效果!也就是今天要讲的--静态链表! 当然你也可以直接跳过本章,因为有了单链表就没有必要用静态链表了

小猪的数据结构学习笔记(二)

小猪的数据结构学习笔记(二) 线性表中的顺序表 本节引言: 在上个章节中,我们对数据结构与算法的相关概念进行了了解,知道数据结构的 逻辑结构与物理结构的区别,算法的特性以及设计要求;还学了如何去衡量一个算法 的好坏,以及时间复杂度的计算!在本节中我们将接触第一个数据结构--线性表; 而线性表有两种表现形式,分别是顺序表和链表;学好这一章很重要,是学习后面的基石; 这一节我们会重点学习下顺序表,在这里给大家一个忠告,学编程切忌眼高手低,看懂不代表自己 写得出来,给出的实现代码,自己要理解思路,自己

数据结构学习日记2:实现一个简单的线性表功能(链式存储方式)

数据结构学习日记,此次是用链表来实现一个线性表的功能 // ListTable1.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include<iostream> using namespace std; #define ERROR 0; #define OK 1; typedef int Status; typedef int ElemType; //声明一个节点 typedef struct Node { ElemType d

python数据结构学习笔记(九)

Priority Queues 9.1 ADT 9.2 implementing a priority queue 用无序的list来实现 用有序的列表来实现优先队列 9.3 heaps heap数据结构 使用堆来实现优先队列 基于数组实现的完全二叉树 使用最小优先队列来进行排序 adaptable priority queue Locators Priority Queues 9.1 ADT P.add(k, v) P.min(): return (k, v) P.remove_min():

文章分享:简单数据结构学习:单向链表

文章分享:简单数据结构学习:单向链表:https://www.textarea.com/aprikyb/jiandan-shujujiegou-xuexi-danxiang-lianbiao-252/

数据结构学习系列之线性表(三)

前言数据结构学习,发现坚持下来比较难,本次学习与上次学习间隔了几个月,不管怎样还是要强迫自己坚持下来. 静态链表 用数组模拟链式结构的线性表,对于没有指针的编程语言,只能使用这种方式来模拟.这是大师们想出来的实现方法,体会大师们的编程思路,站在大师们的肩膀上解决一个又一个的难题.每个节点包含一个游标(数组数字索引),用于指向下个节点,所以静态链表也称为游标链表.规定数组第一个元素为备用链表(未被使用的数组游标,静态链表初始化时,会生成备用链表)的头节点,数组最后一个元素为链表的头节点.当需要插入

小猪的数据结构学习笔记(五)

小猪的数据结构学习笔记(五) 线性表之--循环链表                           --转载请注明出处:coder-pig 循环链表知识点归纳: 相关代码实现: ①判断是否为空表: ②单循环链表的存储结构 其实和单链表的结构是一样的! /*定义循环链表的存储结构*/ typedef struct Cir_List { int data; struct Cir_List *next; }Lnode; ③初始化循环单链表 代码如下: //1.循环链表的初始化 //表示一个元素,如