堆(创建、删除、插入)及堆排序 Java实现

堆以及堆排序

一、堆(大根堆)

堆可以定义为一棵具有父母优势的完全二叉树。

堆也继承了一些完全二叉树的性质。

父母优势,即每一个结点的键值总是要大于其子结点的。

于是我们可以认为,从任意一个结点到某个叶子(当然这个叶子是该结点的子孙)的路径上,键值总是递减,或者非增的(当允许子结点键值与父节点相等时)。

堆的特性:

1、n个节点的完全二叉树,其高度为floor(log2(n))

2、堆的根总是最大元素

3、其子树也是一个堆

4、用数组实现堆时,可预留数组头的元素,使得n个节点分别占据1-n的位置,这样以来,数组应该被初始化为n+1的长度,该数组应该有如下的性质:

          4.1、父母节点总是占据了前floor(n/2)个位置,叶子节点将占据后ceil(n/2)个位置,因为继承了完全二叉树的性质,所以该条规则对于最后一层右侧不满的堆也是成立的。

          4.2、如果一个父节点的位置为i (1≤i≤floor(n/2)),则它的子节点(如果存在的话)位置应为2i和2i+1 ; 与之对应的,子节点的位置为i时,其父节点的位置应为floor(i/2)

一个数组调整为一个堆,4.1和4.2两条规则对调整堆的算法起到了重要作用。

堆的Java实现,包含创建堆、堆的任意索引位置删除、堆的插入:

package com.ryo.algorithm.heap;

import com.ryo.algorithm.util.Util;

/**
 * 采用插入排序的思想调整堆
 * @author shiin
 */
public class InsertHeap implements Heap{
	
	private int[] heap;
	private int size;
	
	public InsertHeap(int[] arr) {
		if(arr != null) {
			heap = new int[arr.length+1];
			size = arr.length;
			if(size > 1) {
				for(int i=0 ;i<size ;i++) {
					heap[i+1] = arr[i];
				}
				buildHeap();
			}
		}
	}
	
	@Override
	public void buildHeap() {
		int floor = (heap.length-1)/2;//下界  最后一个父母节点的索引
		int i = floor;
		int j ,temp ,nextindex;
		while(i > 0) {
			temp = heap[i];
			j = i;
			while(j <= floor) {
				nextindex = getBigerIndex(j);
				if(temp < heap[nextindex]) {
					heap[j] = heap[nextindex];
				}
				else
					break;
				j = nextindex;
			}
			heap[j] = temp;
			i--;
		}
	}
	
	/**
	 * 插入一个int并放到合适的位置
	 * @param newint 新元素
	 */
	@Override
	public void insert(int newint) {
		int[] temp = new int[heap.length+1];
		for(int i=0 ;i<temp.length-1 ;i++) {
			temp[i] = heap[i];
		}
		int index = heap.length;
		while(index > 1) {
			if(newint > temp[index/2]) {
				temp[index] = temp[index/2];
				index = index/2;
			}
			else
				break;
		}
		temp[index] = newint;
		heap = temp;
	}

	/**
	 * 删除指定索引的元素
	 * 基本思想是用末尾的元素取替代删除位置的元素再调整堆
	 * @param index 目标索引,从1开始为根
	 */
	@Override
	public void delete(int index) {
		heap[index] = heap[heap.length-1];
		int[] temp = new int[heap.length-1];
		for(int i=0 ;i<temp.length ;i++) {
			temp[i] = heap[i];
		}
		heap = temp;
		buildHeap();
	}
	
	/**
	 * 比较两个子节点的大小,返回较大节点的索引
	 * @param root	根节点位置
	 * @return	较大子节点的索引
	 */
	private int getBigerIndex(int root) {
		if(2*root + 1 > size) {
			return 2*root;
		}
		else {
			if(heap[2*root] > heap[2*root+1])
				return 2*root;
			else
				return 2*root+1;
		}	
	}
	

	@Override
	public int[] getHeap() {
		return this.heap;
	}

	@Override
	public int size() {
		return this.size;
	}

}

堆排序Java实现:

package com.ryo.algorithm.sort;

import com.ryo.alogorithm.heap.InsertHeap;

public class HeapSort implements Sort{

	@Override
	public int[] sort(int[] arr) {
		int[] heap = new InsertHeap(arr).getHeap();
		return heapSort(heap);
	}
	
	private int[] heapSort(int[] heap) {
		int cursor = heap.length-1;
		int[] result = new int[heap.length-1];
		int i = result.length-1;
		int nextindex,temp,j;
		while(cursor > 0) {
			result[i] = heap[1];
			j = 1;
			temp = heap[cursor];
			while(j*2 < cursor) {
				nextindex = getBigerChildIndex(heap ,j);	
				if(!compareAndSet(heap ,temp ,nextindex))
					break;		
				j = nextindex;
			}
			heap[j] = temp;
			i--;
			cursor--;
		}
		return result;	
	}
	
	/**
	 * 返回较大的子节点索引
	 * @param heap	目标数组
	 * @param index	父母节点索引
	 * @return	较大的子节点索引
	 */
	public int getBigerChildIndex(int[] heap ,int index) {
		if(heap[2*index] > heap[2*index+1]) {
			return 2*index;
		}
		else
			return 2*index+1;
	}
	
	/**
	 * 基于一种插入排序的思想
	 * 若child大于value,只设置该child的父母节点的值为该child的值,不对child做改变
	 * @param heap	目标数组
	 * @param value	待比较的值
	 * @param child	较大的子节点索引,该值应由getBigerChildIndex方法传入
	 * @return	是否需要继续向下比较
	 */
	public boolean compareAndSet(int[] heap ,int value ,int child) {
		if(value < heap[child]) {
			heap[child/2] = heap[child];
			return true;
		}
		else
			return false;
	}
}

 

猜你喜欢

转载自blog.csdn.net/my_dearest_/article/details/80001493