1.概念:二叉堆是经常被提到的堆, 堆满足: 堆中某个节点的值总是不大于或不小于其父节点的值; 堆总是一棵完全二叉树
2.分类:堆具体分为大根堆和小根堆。大根堆的父节点一定是比子节点的value大的,小根堆的父节点一定是比子节点的value小的。 父节点和子节点的关系,已知父节点为k,则左孩子为2k+1,右孩子为2k+2,且都应该小于等于总结点个数。根节点从0开始,因为底层是数组。 已知子节点位置为k,则父节点位置为(k-1)/2,向下取整 (以大根堆为例) 向下调整: 整体思想: 从最后一个子树开始调整。如果字节比父节点大,则交换。每一棵树都是向下调整。 3.创建堆
public class Heap { public int []elem;//底层是一个数组 public int usedSize;//存放数据个数 public Heap() { this.elem = new int[10]; } public void creatHeap(int[] arr){ for (int i = 0; i <arr.length; i++) { this.elem[i]=arr[i]; this.usedSize++; } //从最后一个子树开始调整 for (int i = (this.usedSize-1-1)/2; i >=0 ; i--) {//this.usedSize-1代表最后一个节点,(this.usedSize-1-1)/2每颗子树的根节点 adjustDown(i,this.usedSize); } } public void adjustDown(int root,int len){//root代表每个子树的开始位置,len代表的是结束位置。 int parent=root; int child=2*parent+1;//左孩子 while (child<len){//孩子节点的下标一定会小于等总节点的数量-1。 if(child+1<len){ //有右孩子 if (elem[child]<elem[child+1]){//找到左右孩子的最大值,child保存最大孩子的坐标 child=child+1; } } //比较child节点和父节点的值,如果大则交换 if (elem[child]>elem[parent]){ int tmp=elem[child]; elem[child]=elem[parent]; elem[parent]=tmp; parent=child; child=2*parent+1; }else {//如果小则不用交换,说明已经调整好了 break; } } } public void show(){ for (int i = 0; i <this.usedSize ; i++) { System.out.print(elem[i]+" "); } } }最终的堆:
创建堆的时间复杂度:O(n),调整一次堆的时间复杂度O(log n) 4.给堆里插入元素
新增时,在数组尾部增添元素,并且调整堆。向上调整
public void push(int value){ //0.判断是否扩容 if (this.elem.length==this.usedSize){ this.elem= Arrays.copyOf(this.elem,elem.length*2); } elem[this.usedSize]=value; this.usedSize++; adjustUp(this.usedSize-1); } private void adjustUp(int child) { int parent=(child-1)/2; while (child>0){ if (elem[parent]<elem[child]){ int tmp=elem[parent]; elem[parent]=elem[child]; elem[child]=tmp; child=parent; parent=(child-1)/2; }else { break; } } }5.堆删除元素
public void pop(){//每次移除的都是堆的元素 if (this.usedSize==0){//是否为空 return; } int tmp=this.elem[this.usedSize-1]; this.elem[this.usedSize-1]=this.elem[0]; this.elem[0]=tmp; this.usedSize--;//删除这个元素 adjustDown(0,this.usedSize); }