堆的封装,进行插入,调整,删除堆顶以完成堆排序实例

简介


堆对于排序算法是一个比较常用的数据结构,下面我就使用Java语言来实现这一算法


首先,我们需要知道堆的数据结构的形式,其实就是一个特殊的二叉树。但是这个二叉树有一定的特点,除了是完全二叉树以外,对于最大堆而言,堆顶元素的值是最大的,而且对于堆的每一个子树也是一个小一号的最大堆;同样对于最小堆,性质相反就可以了。


我以最大堆为例:
要实现堆的初始化操作,就是先按照给定的元素创建一棵完全二叉树,然后从末尾节点进行不断地调整的过程。调整的原则是:比较要进行放置的当前节点与其父节点的数值的大小,若要进行放置的当前节点的值小于其父节点,那么当前节点所在位置符合最大堆的定义,要进行放置的当前节点放在此处是比较合适的;如果要进行放置的当前节点的值大于其父节点的值,那说明放在当前节点是不合适的,那么就需要将当前节点的值与其父节点的值进行交换,然后原父节点变为新的要进行放置的当前节点。循环比较;终止条件就是当前节点没有父节点,但此时的调整也许并没有结束,我们只需要让堆顶元素为要插入的值即可。至此,最大堆的插入和调整过程结束。
代码如下:

public boolean insert(int x){
		if(currentSize==MAXSIZE){
			System.out.println("Sorry,this heap is full!");
			return false;
		}
		//如果堆不满的话
		currentSize++;
		int flag=currentSize-1;
		while(flag>0){
			int parent=(flag-1)/2;
			if(heap[parent]>x){
				heap[flag]=x;
				return true;
			}else{
				heap[flag]=heap[parent];
				flag=parent;
			}
		}
		heap[0]=x;
		return true;
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

siftDown过程:给定一个节点的位置,对其进行调整,使之符合最大堆的定义,这个过程就是我们要实现的过程。调整原则如下:
对于当前节点i而言,其孩子节点的下标满足左节点为2i+1,右节点为2i+2;在进行调整的过程中,只需要比较当前节点与其子节点中最大的节点进行调整即可。具体的代码逻辑可在代码中看到:

public void siftDown(int flag){
		int want=flag;
		int x=heap[flag];
		
		while(want<currentSize){
			int lChild=2*want+1;
			int rChild=2*want+2;
			int MAXChildNumber;
			if(lChild>currentSize){  //没有孩子节点
				heap[want]=x;
			}else{                   //有两个孩子节点
				if(lChild<currentSize){
					MAXChildNumber=heap[lChild]>heap[rChild]?lChild:rChild;
				}else{
					MAXChildNumber=lChild;
				}
				if(heap[MAXChildNumber]<x){
					heap[want]=x;return;
				}else{
					heap[want]=heap[MAXChildNumber];
					want=MAXChildNumber;
				}
			}
		}
		
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

堆顶元素的删除,我们对堆的操作基本桑就是为了获得这个堆的最值,那么毫无疑问,堆顶元素就是我们要研究的对象。下面是代码逻辑:

public int deleteTop(){
		if(currentSize<0){
			System.out.println("Sorry, this heap is empty!");
			return -1;
		}
		int target=heap[0];
		int substitute=heap[currentSize-1];
		this.currentSize--;
		heap[0]=substitute;
		siftDown(0);
		return target;
	}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

下面是详细的代码

package test.maxHeap;

public class MaxHeap {
	
	private int []heap ;
	private int currentSize;
	private static int MAXSIZE ;
	
	public MaxHeap(int n){
		heap=new int[n];
		currentSize=0;
		MAXSIZE=n;
	}
	
	public boolean insert(int x){
		if(currentSize==MAXSIZE){
			System.out.println("Sorry,this heap is full!");
			return false;
		}
		//如果堆不满的话
		currentSize++;
		int flag=currentSize-1;
		while(flag>0){
			int parent=(flag-1)/2;
			if(heap[parent]>x){
				heap[flag]=x;
				return true;
			}else{
				heap[flag]=heap[parent];
				flag=parent;
			}
		}
		heap[0]=x;
		return true;
	}
	
	public void siftDown(int flag){
		int want=flag;
		int x=heap[flag];
		
		while(want<currentSize){
			int lChild=2*want+1;
			int rChild=2*want+2;
			int MAXChildNumber;
			if(lChild>currentSize){  //没有孩子节点
				heap[want]=x;
			}else{                   //有两个孩子节点
				if(lChild<currentSize){
					MAXChildNumber=heap[lChild]>heap[rChild]?lChild:rChild;
				}else{
					MAXChildNumber=lChild;
				}
				if(heap[MAXChildNumber]<x){
					heap[want]=x;return;
				}else{
					heap[want]=heap[MAXChildNumber];
					want=MAXChildNumber;
				}
			}
		}
		
	}

	public int deleteTop(){
		if(currentSize<0){
			System.out.println("Sorry, this heap is empty!");
			return -1;
		}
		int target=heap[0];
		int substitute=heap[currentSize-1];
		this.currentSize--;
		heap[0]=substitute;
		siftDown(0);
		return target;
	}

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78

好了,编码已经完成。下面我们就要检验一下是否正确吧。

public class MaxHeapTest {

	public static void main(String []args){
		MaxHeap maxHeap=new MaxHeap(7);
		for(int i=1;i<=7;i++){
			maxHeap.insert(i);
		}
		for(int i=0;i<7;i++){
			System.out.print(maxHeap.deleteTop()+"   ");
		}
		System.out.println("\n");
	}
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

接下来是程序的运行结果:

7   6   5   4   3   2   1   
//可见,对于最大堆,删除堆顶的操作实际上就是完成了对堆的排序任务,也证明了我们的代码是正确的
  • 1
  • 2

总结:
堆的操作很重要,我们更要学会对于堆的应用,这样的数据结构才能使得程序的运行更加的高效和流畅。对于最小堆,我们只需要在插入方法,sift方法内稍加修改即可(也就是将值的代销变换关系进行调整)。这样就同样能实现最小堆的创建和相关的操作了。
代码中可能存在不太恰当地地方,希望大家予以批评指正,期待与你们共同进步!


果然时间是最好的庸医,这不忘得差不多了,于是来复习下。
update at 2018年12月10日22:54:44

最小堆

#coding: utf8
__author__ = "郭 璞"
__email__ = "[email protected]"
# list 实现的最小堆
class MinHeap(object):
    def __init__(self, capacity):
        self.elements = [None for i in range(capacity)]
        self.count = 0

    def capacity(self):
        return len(self.elements)

    def insert(self, value):
        self.elements[self.count] = value
        self.count += 1
        self.siteup(self.count-1)

    def siteup(self, index):
        if index > 0:
            parent = index // 2
            if self.elements[parent] > self.elements[index]:
                self.elements[index], self.elements[parent] = self.elements[parent], self.elements[index]
                self.siteup(parent)

    def pop(self):
        if self.count > 0 :
            value = self.elements[0]
            self.elements[0] = self.elements[self.count -1]
            self.count -= 1
            self.sitedown(0)
            return value

    def sitedown(self, index):
        left = 2 * index + 1
        right = 2 * index + 2
        smaller = index
        if left < self.count and self.elements[smaller] > self.elements[left]:
            smaller = left
        if right < self.count and self.elements[smaller] > self.elements[right]:
            smaller = right
        if smaller != index:
            self.elements[smaller], self.elements[index] = self.elements[index], self.elements[smaller]
            self.sitedown(smaller)

if __name__ == "__main__":
    minheap = MinHeap(5)
    minheap.insert(3)
    minheap.insert(4)
    minheap.insert(2)
    minheap.insert(1)
    minheap.insert(7)

    for i in range(5):
        print(minheap.pop())


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

运行结果

/usr/local/bin/python3.7 /Users/biao/PycharmProjects/algorithm/heapdemo/minheap.py
1
2
3
4
7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

发现Java实现的这篇博文没有解释,为什么要这么做,这对于后续的复习是极其不好的,所以原理上的东西还是要写清楚的。

总的来说,最小堆包含两个步骤:

  • 建立最小堆: 自下而上
  • 调整最小堆: 自上而下

建立最小堆的时候,我们首先把节点放到数组的最后一位,也即是完全二叉树的最后一个节点上,然后根据下标来不断的与其父节点进行比较,最终实现堆的建立。

调整堆一般是pop了最顶节点触发的。pop之后,需要把目前堆上的最后一个节点放到顶点的位置上,然后让其与对应的子节点进行比较,直到当前节点为最后一个节点。

温故而知新,老话诚不欺我。

再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow

猜你喜欢

转载自www.cnblogs.com/djuwcnhwbx/p/10325683.html