一.二叉树
1.首先介绍一下二叉树
(1)二叉树:每个节点最多有两个子树的结构
(2)完全二叉树:叶节点只能出现在最下层和次下层,并且最下一层的节点集中在该层最左边的若干位置的二叉树
(3)满二叉树:叶节点只能出现在最下层
二.二叉堆(堆)
1.堆有序:当一棵二叉树的每个结点都不小于它的两个子结点时,称为堆有序
2.二叉堆:一组能够用堆有序的完全二叉树排序的元素,并且能在数组中按照层级存储。
3.说明:
(1)二叉堆可以用数组来表示,从1-N,根节点位置在a[1]
(2)二叉堆可以不使用指针而是简单的使用数学关系,来表示结构
(3)位置k的结点的父节点是(int)k/2,两个子结点的位置是2k和2k+1
(4)一颗大小为N的完全二叉树的高度是|_lgN_|
4.堆的维护
(1)上浮。当某个结点比父结点更大时,需要交换它和父结点的位置,并继续判断,直到比父结点小。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
private void swim(int k) { while(k>1 && less(k/2,k)) { exch(k,k/2); k=k/2; } }
(2)下沉。当某个结点比它的两个子结点或其中一个子结点小,需要将父结点与子结点中较大的一个进行交换,重复该过程,直到比子结点都大。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
private void sink(int k) { while(2*k<=N) { int j=2*k; //考虑到只有左结点的情况 //如果有2个结点,则取最大的结点 if(j<N && less(j,j+1)) j++; //比较子结点和k的大小 if(less(j,k)) break; exch(j,k); k=j; } }
三.优先级队列
1.能够进行删除最大元素和插入元素的操作的数据结构
2.方法1:将元素进行排序,然后就可以用普通队列的方式,从队尾插元素,从队首取元素
方法2:使用无序数组的方式,在a[N]出插入元素,删除最大元素时,需要遍历数组
方法3:利用堆来实现优先队列
3.性能比较
4.使用无序数组实现优先队列
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.cx.sort; //可变数组构建的优先级队列 public class UnorderedMaxPQ<T extends Comparable<T>> { private T[] pq; private int N; public UnorderedMaxPQ(int capacity) { pq=(T[])new Comparable[capacity]; } public boolean isEmpty() { return N==0; } public int size() { return pq.length; } public void insert(T x) { //如果pq容量不够,将pq容量变为2倍 if(N==pq.length) resize(2*N); pq[N++]=x; } public T delMax() { //初级优先队列,是遍历数组找到最大的元素,然后删除 int max=0; for(int i=1;i<N;i++) { if(less(max,i)) max=i; } exch(max,N-1); T delVal=pq[--N]; //避免对象游离 pq[N]=null; //删除完后,如果数组长度过大(1/4满),将数组长度减半 if(N>0 && N<pq.length/4) resize(pq.length/2); return delVal; } private boolean less(int i,int j) { return pq[i].compareTo(pq[j])<0; } private void exch(int i,int j) { T temp=pq[i]; pq[i]=pq[j]; pq[j]=temp; } //可变数组 private void resize(int capacity) { T[] temp=(T[])new Comparable[capacity]; for(int i=0;i<N;i++) temp[i]=pq[i]; pq=temp; } }
5.使用堆实现优先队列:充分利用上一段指出的堆的维护的特点
(1)当插入元素时,将元素放于数组的末尾,然后利用swim,即可继续维持堆的结构
(2)当删除元素时,优先级最高的元素时根的元素即a[1]。将a[1]与末尾元素交换,对刚变为根节点的元素使用sink,即可继续维持堆的结构。并且成功找到并删除了优先级最高的结点
(3)由于堆是从数组的1到N的,所以在调整数组大小的时候,有很多需要注意的地方,具体实现如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class MaxPQ <T extends Comparable<T>>{ private T[] pq; //元素存在pq[1..N] private int N=0; public MaxPQ(int capacity) { pq=(T[])new Comparable[capacity+1]; } public boolean isEmpty() { return N==0; } //pq中有效元素的个数 public int size() { return N; } //pq的真实容量 public int length() { return pq.length; } public void insert(T v) { //如果容量不够,变为2倍 if(N==pq.length-1) resize(2*N); pq[++N]=v; swim(N); } public T delMax() { T max=pq[1]; exch(1,N); pq[N--]=null; sink(1); //如果容量过大,变为一半 if(N>0 && N<pq.length/4) resize(pq.length/2); return max; } //将对象上浮 private void swim(int k) { while(k>1 && less(k/2,k)) { exch(k,k/2); k=k/2; } } //将对象下沉 private void sink(int k) { while(2*k<=N) { int j=2*k; //考虑到只有左结点的情况 //如果有2个结点,则取最大的结点 if(j<N && less(j,j+1)) j++; //比较子结点和k的大小 if(less(j,k)) break; exch(j,k); k=j; } } private boolean less(int i,int j) { return pq[i].compareTo(pq[j])<0; } private void exch(int i,int j) { T temp=pq[i]; pq[i]=pq[j]; pq[j]=temp; } //可变数组 private void resize(int capacity) { T[] temp=(T[])new Comparable[capacity+1]; //这一步注意是从1..N,容易把pq[N]变为空了 for(int i=0;i<=N;i++) temp[i]=pq[i]; pq=temp; } public void show() { for(T t:pq) { System.out.print(t+" "); } System.out.println(); } }
6.测试代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.cx.sort; public class TestPQ { public static void main(String[] args) { // UnorderedMaxPQ<String> pq1=new UnorderedMaxPQ<>(5); MaxPQ<String> pq1=new MaxPQ<>(2); pq1.insert("c"); System.out.println(pq1.size()+" "+pq1.length()); pq1.insert("a"); System.out.println(pq1.size()+" "+pq1.length()); pq1.insert("d"); System.out.println(pq1.size()+" "+pq1.length()); pq1.insert("m"); System.out.println(pq1.size()+" "+pq1.length()); pq1.insert("r"); System.out.println(pq1.size()+" "+pq1.length()); pq1.show(); pq1.insert("z"); System.out.println(pq1.size()+" "+pq1.length()); pq1.show(); pq1.insert("f"); System.out.println(pq1.size()+" "+pq1.length()); pq1.show(); while(pq1.size()>0) { System.out.println(pq1.size()+" "+pq1.length()); pq1.delMax(); pq1.show(); } } }
7.说明:
对于一个含有N个元素的基于堆的优先队列,插入元素操作只需要不超过(lgN+1)次比较,删除最大元素的操作需要不超过2lgN次比较