堆的动态调整(小根堆)

堆排序的应用很广泛,但是当我们往堆中添加完元素,然后更改这些元素的属性,更改后的元素并不会重新形成堆,这时候需要我们自己去实现堆的动态调整了,因为很多语言并未实现这一步骤,例如java中的PriorityQueue就没有动态调整。


思路:要重新调整其实不难,对调整后的元素分别进行向上和向下的堆调整,它能上则上,不能上则下,两条路二选一,当它来到合适的位置则调整完毕。问题是我们怎么知道它的索引呢?直接根据ArrayList的indexOf就进行了O(n)的循环,所以我们加了一个Map来同步更新索引,这样就直接通过map就get到下标了,避免了O(n)的一步,具体代码如下:

package class04;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;

/**
 * @Description 堆的动态调整
 * @Package: class04
 * @Author: Ray
 * @CreateTime: 2020/7/14 22:36
 * @E-mail: 634302021@qq.com
 */
public class Test4 {
	
	public static class Student {
		public int classNo;
		public int age;
		public int id;
		
		public Student(int c, int a, int i) {
			classNo = c;
			age = a;
			id = i;
		}
	}
	
	public static class MyHeap<T> {
		public ArrayList<T> heap;
		public Comparator<? super T> comparator;
		public int heapSize;    //堆的大小,动态维护,同时是indexMap的value
		public HashMap<T, Integer> indexMap;
		
		public MyHeap(Comparator<T> c) {
			this.heap = new ArrayList<>();
			this.comparator = c;
			this.heapSize = 0;
			this.indexMap = new HashMap<>();
		}
		
		/**
		 * 堆添加元素
		 * @param o
		 */
		public void push(T o) {
			heap.add(o);
			//每加一个,同步添加到indexMap
			indexMap.put(o, heapSize);
			//每加一个,做向上调整
			heapUp(heapSize);
			heapSize += 1;
		}
		
		/**
		 * 取堆顶返回,并将堆底放置堆顶,重新形成小根堆
		 * @return
		 */
		public T pop() {
			T ans = heap.get(0);
			//首尾交换
			swap(0, heapSize - 1);
			//indexMap和heap移除最后一个(原来的首)
			indexMap.remove(ans);
			heap.remove(ans);
			//向下调整
			heapDown(0, --heapSize);
			return ans;
		}
		
		/**
		 * 堆动态调整
		 * @param o
		 */
		public void resign(T o) {
			int index = indexMap.get(o);
			heapUp(index);
			heapDown(index, heapSize);
		}
		
		/**
		 * 堆向上调整
		 * @param index
		 */
		public void heapUp(int index) {
			//如果下面小,上面大,就交换,形成小根堆
			while (comparator.compare(heap.get(index), heap.get((index - 1) / 2)) < 0) {
				swap(index, (index - 1) / 2);
				index = (index - 1) / 2;
			}
		}
		
		/**
		 * 堆向下调整
		 * @param index
		 * @param heapSize
		 */
		public void heapDown(int index, int heapSize) {
			int left = index * 2 + 1;
			//如果下方有左树,没越界
			while (left < heapSize) {
				//存在右孩子且右<左,取右孩子下标给smaller,否则把左孩子下标给smaller
				int smaller = left + 1 < heapSize && comparator.compare(heap.get(left + 1), heap.get(left)) < 0 ? left + 1 : left;
				//如果上大下小,那么交换,否则已经是小根堆了无需再调整
				if (comparator.compare(heap.get(index), heap.get(smaller)) > 0) {
					swap(index, smaller);
				} else {
					break;
				}
				index = smaller;
				left = index * 2 + 1;
			}
		}
		
		/**
		 * 交换位置
		 * @param i
		 * @param j
		 */
		public void swap(int i, int j) {
			T o1 = heap.get(i);
			T o2 = heap.get(j);
			heap.set(i, o2);
			heap.set(j, o1);
			indexMap.put(o1, j);
			indexMap.put(o2, i);
		}
		
		/**
		 * 是否为空
		 * @return
		 */
		public boolean isEmpty() {
			return heapSize == 0;
		}
	}
	
	/**
	 * 根据年龄比较
	 */
	public static class StudentComparator implements Comparator<Student> {
		@Override
		public int compare(Student o1, Student o2) {
			return o1.age - o2.age;
		}
	}
	
	public static void main(String[] args) {
		Student s1 = null;
		Student s2 = null;
		Student s3 = null;
		Student s4 = null;
		Student s5 = null;
		Student s6 = null;
		s1 = new Student(2, 10, 11111);
		s2 = new Student(1, 20, 22222);
		s3 = new Student(6, 30, 33333);
		s4 = new Student(3, 40, 44444);
		s5 = new Student(7, 50, 55555);
		s6 = new Student(1, 60, 66666);
		MyHeap<Student> myHeap = new MyHeap<>(new StudentComparator());
		myHeap.push(s1);
		myHeap.push(s2);
		myHeap.push(s3);
		myHeap.push(s4);
		myHeap.push(s5);
		myHeap.push(s6);
		s2.age = 80;
		myHeap.resign(s2);
		s4.age = 13;
		myHeap.resign(s4);
		s5.age = 14;
		myHeap.resign(s5);
		s6.age = 15;
		myHeap.resign(s6);
		
		while (!myHeap.isEmpty()) {
			Student cur = myHeap.pop();
			System.out.println(cur.classNo + "," + cur.age + "," + cur.id);
		}
		
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值