1.定义:
二叉堆是一颗完全二叉树
2.特点:(最大堆)
堆中某个节点的值总是不大于其父节点的值
3.数组存储二叉堆
parent(i)=(i-1)/2
left child(i)=2*i+1
right child(i)=2*i+2
4.二叉堆实现功能
(1)add
(2)extractMax取出最大元素
(3)replace 取出最大元素后,放入一个新元素,算法复杂度为O(nlogn)
(4)Heapify 将n个元素诸葛插入到一个空堆中,算法复杂度为O(nlogn)
5.最大堆的java实现
(1)实现的基本结构:动态数组
package com.DataStructures._08Heap;
/**
* Created by Administrator on 2018/12/10.
*/
public class ArrayDynamic<E> {
private E[] data; //数组
private int size; //数组中实际元素个数
//构造函数,传入数组的容量capacity
public ArrayDynamic(int capacity){
data=(E[])new Object[capacity];
size=0;
}
//无参数构造函数,默认数组的容量10
public ArrayDynamic(){
this(10);
}
//传入数组构造函数
public ArrayDynamic(E[] arr)
{
data=(E[])new Object[arr.length];
for (int i=0;i<arr.length;i++){
data[i]=arr[i];
}
size=arr.length;
}
//获取数组容量
public int getCapacity(){
return data.length;
}
//获取数组中的元素个数
public int getSize(){
return size;
}
//返回数组是否为空
public boolean isEmpty(){
return size==0;
}
//向所有元素后添加一个新元素
public void addLast(E e){
// if (size==data.length){
// throw new IllegalArgumentException("AddLast failed. Array is full.");
//
// }
// data[size] = e;
// size ++;
add(size,e);
}
//在所有元素前添加一个新元素
public void addFirst(E e){
add(0,e);
}
//在index索引位置插入一个新元素
public void add(int index,E e){
if(index<0||index>size){
throw new IllegalArgumentException("Add failed. Require index >= 0 and index <= size.");
}
if(size==data.length){
// throw new IllegalArgumentException("AddLast failed. Array is full.");
//当达到最大长度时候,实行扩容
resize(2*data.length);
}
for (int i =size-1;i>=index;i--){
data[i+1]=data[i];
}
data[index]=e;
size++;
}
@Override
public String toString(){
StringBuilder res=new StringBuilder();
res.append(String.format("Array:size=%d,capacity=%d\n",size,data.length));
res.append("[");
for (int i =0;i<size;i++){
res.append(data[i]);
if(i!=size-1){
res.append(",");
}
}
res.append("]");
return res.toString();
}
public E get(int index){
if(index < 0 || index >= size){
throw new IllegalArgumentException("Get failed. Index is illegal.");
}
return data[index];
}
public void set(int index,E e){
if(index < 0 || index >= size){
throw new IllegalArgumentException("Get failed. Index is illegal.");
}
data[index]=e;
}
//查找是否包含元素e
public boolean contains(E e){
for (int i =0;i<size;i++){
if(data[i].equals(e)){
return true;
}
}
return false;
}
//查找包含元素所在索引,不包含则返回-1
public int find(E e){
for (int i =0;i<size;i++){
if (data[i].equals(e)){
return i;
}
}
return -1;
}
//从数组中删除元素,返回删除元素
public E remove(int index){
if(index < 0 || index >= size){
throw new IllegalArgumentException("Remove failed. Index is illegal.");
}
E ret=data[index];
for (int i=index;i<size-1;i++){
data[i]=data[i+1];
}
size--;
data[size]=null;
if(size == data.length / 4 && data.length / 2 != 0)
{
resize(data.length/2);
}
return ret;
}
//从数组中删除第一个元素,返回删除元素
public E removeFirst(){
return remove(0);
}
//从数组中删除最后一个元素,返回删除元素
public E removeLast(){
return remove(size-1);
}
//从数组中删除元素e
public void removeElement(E e){
int index=find(e);
if (index!=-1){
remove(index);
}
}
//将数组容量变为newCapacity大小
private void resize(int newCapacity){
E[] newData=(E[])new Object[newCapacity];
for (int i =0;i<size;i++){
newData[i]=data[i];
}
data=newData;
}
/**
* 交换元素位置
* @param i
* @param j
*/
public void swap(int i,int j){
if(i<0||i>=size || j<0 ||j >=size){
throw new IllegalArgumentException("Index is illegal");
}
E t=data[i];
data[i]=data[j];
data[j]=t;
}
}
(2)动态数组为基本结构实现最大堆
package com.DataStructures._08Heap;
import java.util.Random;
/**
* Created by Administrator on 2018/12/19.
* 最大堆的实现:即每个节点都会大于其子节点的值
* 实现基本原理:是从数组0开始的堆
*/
public class MaxHeap <E extends Comparable<E>>{
//1.存储元素
private ArrayDynamic<E> data;
//2. 构造函数
public MaxHeap(int capacity){
data=new ArrayDynamic<E>(capacity);
}
public MaxHeap(){
data=new ArrayDynamic<E>();
}
//3.基本信息
//返回堆中的元素个数
public int size(){
return data.getSize();
}
//返回一个布尔值,表示堆中是否为空
public boolean isEmpty(){
return data.isEmpty();
}
//***********************************************************
//4.辅助函数
//返回完全二叉树的数组表示中,一个索引表示的元素的父亲节点的索引
private int parent(int index){
if(index==0){
throw new IllegalArgumentException("index-0 doesn't have parent.");
}
return (index-1)/2;
}
//返回完全二叉树的数组中,一个索引表示的元素的左孩子节点的索引
private int leftChild(int index){
return index*2+1;
}
//返回完全二叉树的数组中,一个索引表示的元素的右孩子节点的索引
private int rightChild(int index){
return index*2+2;
}
//***********************************************************
//5.向堆中添加元素
public void add(E e){
data.addLast(e);
siftUp(data.getSize()-1);
}
//把该元素调整到正确位置
private void siftUp(int k){
while (k>0&&data.get(parent(k)).compareTo(data.get(k))<0){
data.swap(k,parent(k));
k=parent(k);
}
}
//***********************************************************
// 6.看堆中的最大元素
public E findMax(){
if (data.getSize()==0){
throw new IllegalArgumentException("Can not findMax when heap is empty.");
}
return data.get(0);
}
//***********************************************************
//7.从堆中取出元素
//默认取出第一个,后续改造思路:把最后一个元素放到顶部,
public E extractMax(){
E ret=findMax();
data.swap(0,data.getSize()-1); //第一个和最后一个元素互换
data.removeLast(); //删除最后一个
siftDown(0);
return ret;
}
//下沉操作
private void siftDown(int k){
while (leftChild(k)<data.getSize()){
int j=leftChild(k);// 在此轮循环中,data[k]和data[j]交换位置
if(j+1<data.getSize()&&//说明有右孩子
data.get(j+1).compareTo(data.get(j))>0){ //这里比较左孩子j,有孩子j+1的大小,取比较大值
j++;
}
// data[j] 是 leftChild 和 rightChild 中的最大值
if(data.get(k).compareTo(data.get(j))>=0){
break;
}
data.swap(k,j);
k=j;
}
}
//********************************************
//8.替换元素
public E replace(E e){
E ret=findMax();
data.set(0,e);
siftDown(0);
return ret;
}
//********************************************
//9.Heapify的实现:一个构造函数
public MaxHeap(E[] arr){
data=new ArrayDynamic<E>(arr);
for (int i =parent(arr.length-1);i>=0;i--){
siftDown(i);
}
}
//********************************************
//测试
public static void main(String[] args) {
//测试一
// int n=1000000;
// MaxHeap<Integer> maxHeap=new MaxHeap<>();
// Random random =new Random();
// for (int i=0;i<n;i++){
// maxHeap.add(random.nextInt(Integer.MAX_VALUE));
// }
//
// int[] arr=new int[n];
// for (int i=0;i<n;i++){
// arr[i]=maxHeap.extractMax();
// }
//
// for (int i=1;i<n;i++){
// if(arr[i-1]<arr[i]){
// throw new IllegalArgumentException("Error");
// }
// }
// System.out.println("Test MaxHeap completed.");
//测试二
int n = 1000000;
Random random = new Random();
Integer[] testData = new Integer[n];
for(int i = 0 ; i < n ; i ++)
testData[i] = random.nextInt(Integer.MAX_VALUE);
double time1 = testHeap(testData, false);
System.out.println("Without heapify: " + time1 + " s");
double time2 = testHeap(testData, true);
System.out.println("With heapify: " + time2 + " s");
}
private static double testHeap(Integer[] testData, boolean isHeapify){
long startTime = System.nanoTime();
MaxHeap<Integer> maxHeap;
if(isHeapify)
maxHeap = new MaxHeap<>(testData);
else{
maxHeap = new MaxHeap<>();
for(int num: testData)
maxHeap.add(num);
}
int[] arr = new int[testData.length];
for(int i = 0 ; i < testData.length ; i ++)
arr[i] = maxHeap.extractMax();
for(int i = 1 ; i < testData.length ; i ++)
if(arr[i-1] < arr[i])
throw new IllegalArgumentException("Error");
System.out.println("Test MaxHeap completed.");
long endTime = System.nanoTime();
return (endTime - startTime) / 1000000000.0;
}
}