堆
堆是计算机科学中一类特殊的数据结构的统称。堆可以被看出是一个近似的完全二叉树,一般使用数组去存储堆数据。
如何去构造一个堆(以最大堆为例)?
1、定义一个堆的数据结构
private int data[];
private int size;
private int capacity;
data用来存储数据,size是堆中元素的个数,capacity是堆的最大容量
2、存储堆的数据
堆中数据是用数组去存的,由于最大堆是一个近似的完全二叉树,所以可以用数组去存储它的数据,如下:
a是一个堆的二叉树存储标识,b是堆在数组中的存储表示
构造堆及堆的各种操作
1、在实现堆的时候有两个操作特别重要,后面的内容也会以此为基础,那就是上移操作和下移操作
上移操作
//元素上移动,维护堆的性质
public void shiftUp(int i){
while(i > 1){
int parent = i / 2;
if(data[parent] < data[i]){
swap(parent,i);
i = parent;
}else{
break;
}
}
}
parent指向当前元素的父元素,比较当前元素与父元素,若当前元素大于父元素,说明堆的序列不对,需要将当前元素上移,然后将上移后的元素与它的父元素再次比较决定是否上移,知道当前元素是第一个元素,退出循环。
下移操作
//元素下移,维护堆的性质,将数组中索引为i的元素下移
public void shiftDown(int i){
while(2*i <= size){
int left = 2*i; //ledt
int right = 2*i+1;
//largest指向最大的子节点
int largest = i;
if(left <= size && data[largest] < data[left]){
largest = left;
}
if(right <= size && data[largest] < data[right]){
largest = right;
}
if(largest != i){
swap(largest,i); //将data[largest]与data[i]交换
i = largest;
}else{
break; //i已经是最大的了,不需要下移了
}
}
}
shiftDown(i)是将数组索引为i的元素向下移动,每次下移时选择孩子节点中最大的那个作为交换。left = 2*i指向当前元素的左孩子,right = 2*i+1指向当前元素的右孩子。
2、构建堆
//建堆
public MaxHeap(int arr[],int maxSize){
//data[0]存放一个哨兵值,不存放实际的元素
data = new int[maxSize+1];
System.arraycopy(arr, 0, data, 1, arr.length);
this.size = arr.length;
data[0] = Integer.MAX_VALUE;
this.capacity = maxSize;
//采用自底向上的建堆方法,时间复杂度为O(n)
for(int i = size / 2; i >= 1; i--){
shiftDown(i);
}
}
上面的方法将无序数组arr中的元素建立成堆,申明堆的最大容量为maxSize。建立堆的时候才用了自底向上的建堆的方式,从最后一个有孩子的节点开始,依次堆元素执行下移操作,可以在O(n)的时间复杂度情况下建立堆(证明见算法导论第三版P88)
3、向堆中插入元素
//向堆中插入元素,先把这个元素插入在数组最后面,然后执行上移操作就行了
public void insert(int x){
if(capacity == size){
System.out.println("堆满!");
return;
}
data[++size] = x;
shiftUp(size);
}
堆中插入元素步骤是这样的:先把该元素插在数组最后面,然后执行上移操作,把该元素上移到合适的位置即可。
4、删除堆中的元素
//堆中删除元素,将第一个和最后一个元素互换,然后将第一个元素shiftDown,size--就相当于最后一个元素不要了
public int deleteMax(){
if(size == 0){
System.out.println("堆空!");
//返回最小的int值代表堆空
return Integer.MIN_VALUE;
}
int t = data[1];
swap(1,size--);
shiftDown(1);
return t;
}
堆删除操作是删除堆顶元素,首先记录堆顶部元素,然后把堆最后的元素和堆顶元素交换,为了维护最大堆的性质,再将此时的堆顶元素下移就行了。
5、堆排序
public int[] heap_sort(){
int res[] = new int[size];
for(int i = size; i >= 1; i--){
res[i-1] = deleteMax();
}
return res;
}
使用堆的删除操作(每次删除最大元素),可以很容易的对堆进行排序。
堆的删除操作需要执行下移操作,每次下移的时间复杂度都是O(lgn),为树的高度,对于规模为n的堆,堆排序的时间复杂度是O(nlgn)。另由于堆排序的时候原来元素的次序无法保证,所以堆排序不是稳定的。
关于堆序列的一个题目
判断一个堆序列是否正确?
(1)、堆序列是近似的完全二叉树,按照完全二叉树的顺序去画堆就行了。
(2)、堆要么是最大堆(堆顶元素>=孩子元素),要么是最小堆(堆顶元素<=孩子元素)。
如有以下序列,哪个是堆序列
100,60,70,50,32,65
60,70,65,50,32,100
65,100,70,32,50,60
A:根据序列画最大堆,A是堆序列
B:根据序列画最小堆,但50比父元素70小,B不是堆序列
C:同B,C也不是
堆的实现示例代码
package com.lcl.javase;
/**
* 手写堆排序
* @author lcl
*
*/
public class MaxHeap {
private int data[];
private int size;
private int capacity;
//建堆
public MaxHeap(int arr[],int maxSize){
//data[0]存放一个哨兵值,不存放实际的元素
data = new int[maxSize+1];
System.arraycopy(arr, 0, data, 1, arr.length);
this.size = arr.length;
data[0] = Integer.MAX_VALUE;
this.capacity = maxSize;
//采用自底向上的建堆方法,时间复杂度为O(n)
for(int i = size / 2; i >= 1; i--){
shiftDown(i);
}
}
//向堆中插入元素,先把这个元素插入在数组最后面,然后执行上移操作就行了
public void insert(int x){
if(capacity == size){
System.out.println("堆满!");
return;
}
data[++size] = x;
shiftUp(size);
}
//堆中删除元素,将第一个和最后一个元素互换,然后将第一个元素shiftDown,size--就相当于最后一个元素不要了
public int deleteMax(){
if(size == 0){
System.out.println("堆空!");
//返回最小的int值代表堆空
return Integer.MIN_VALUE;
}
int t = data[1];
swap(1,size--);
shiftDown(1);
return t;
}
public int[] heap_sort(){
int res[] = new int[size];
for(int i = size; i >= 1; i--){
res[i-1] = deleteMax();
}
return res;
}
//元素下移,维护堆的性质
public void shiftDown(int i){
while(2*i <= size){
int left = 2*i;
int right = 2*i+1;
//largest指向最大的子节点
int largest = i;
if(left <= size && data[largest] < data[left]){
largest = left;
}
if(right <= size && data[largest] < data[right]){
largest = right;
}
if(largest != i){
swap(largest,i);
i = largest;
}else{
break; //i已经是最大的了,不需要下移了
}
}
}
//元素上移动,维护堆的性质
public void shiftUp(int i){
while(i > 1){
int parent = i / 2;
if(data[parent] < data[i]){
swap(parent,i);
i = parent;
}else{
break;
}
}
}
public void swap(int i,int j){
int t = data[i];
data[i] = data[j];
data[j] = t;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
int arr[] = {100,23,45,67,89,43,1,54,56,88,66};
MaxHeap heap = new MaxHeap(arr,100);
for(int i : heap.heap_sort()){
System.out.print(i+" ");
}
}
}