比较器与堆

堆结构

思想

  • 堆结构就是用数组实现的完全二叉树结构
  • 完全二叉树中,如果每颗子数的最大值都在顶部就是大根堆
  • 完全二叉树中,如果每颗子数的最小值都在底部就是小根堆
  • 堆结构的heapInsert与heapify操作
  • 优先级队列就是堆结构。

优先级队列默认小根堆

package sort;
import java.util.PriorityQueue;
public class heapPriorityQueue {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		PriorityQueue<Integer> heap=new PriorityQueue<>();
		//优先级队列默认小根堆
		heap.add(5);
		heap.add(7);
		heap.add(3);
		heap.add(0);
		heap.add(2);
		heap.add(5);
		while(!heap.isEmpty()){
			System.out.print(heap.poll()+" ");
			//0 2 3 5 5 7 
		}
	}
}

大根堆

思想:
父节点的值大于或等于子节点的值
部分代码理解

   public static class Mymaxheap{
	   private int[]  heap;
	   private final int limit;
	   private int heapSize;//已经收集的堆的大小,和下一个数字应该所处的位置。
	   
	   public Mymaxheap(int limit){
		   heap=new int[limit];
		   this.limit=limit;
		   heapSize=0;
	   }
	   public boolean isEmpty() {
			return heapSize == 0;
		}

		public boolean isFull() {
			return heapSize == limit;
		}

	   //用户此时让你返回最大值,并且让你在大根堆中,把最大值删掉
	   //剩下的数继续保持大根数
	   public int  pop(){
		   int ans=heap[0];
		   swap(heap, 0, --heapSize);
		   heapfiy(heap,0,heapSize);
		   return ans;
	   }
	   
	   public void push(int value){
		   if (heapSize==limit) {
			   throw  new RuntimeException("堆满了");
			   }
		   heap[heapSize]=value;
			   // value  heapSize
		   heapInsert(heap, heapSize++);
	   }
	   
	   public void heapInsert(int[] arr,int index){
		 //arr[index]  arr[index-1]/2
		 //arr[index]不比arr[父]大,停
		while(arr[index]>arr[index-1]/2){
			swap(arr, index, (index-1)/2);
			index=(index-1)/2;
		}
	}
	   private void swap(int[] arr, int i, int j) {
			int tmp = arr[i];
			arr[i] = arr[j];
			arr[j] = tmp;
		}
	   
	   //从index位置,往下看,不断的下沉
	   //停:我的孩子都不再比我大了;已经没有孩子了。
	   public void heapfiy(int[] arr,int index,int heapsize){
		   int left=index*2+1;
		   while(left<heapsize){
			   //左右两个孩子中,谁大,谁把自己的下标给largest。
			   //右:有右孩子,并且右孩子比左孩子大才行
			   //否则左孩子
			   int largest=left+1<heapsize && arr[left+1]>arr[left] ?left+1:left;
			   largest=arr[largest]>arr[index]?largest:index;
			   if (largest==index) {
				break;
			}
			   swap(arr,largest,index);
			   index=largest;
			   left=index*2+1;
		   }
	   }
   }

堆排序

  • 先让整个数组都变成大根堆结构,建立堆的过程
    • 从上到下的方法,时间复杂度是O(N*logN)
    • 从下到上的方法,时间复杂度是O(N)
  • 把堆的最大值和堆末尾的值交换,然后减少堆的大小之后,再去调整堆,一直周而复始,时间复杂度为O(N*logN)
  • 堆的大小减小成0之后,排序结束
package sort;
import java.util.PriorityQueue;
public class heap1 {
 	// 堆排序额外空间复杂度O(1)
 	public static void heapSort(int[] arr) {
 		if (arr == null || arr.length < 2) {
 			return;
 		}
 		// O(N*logN)
//			for (int i = 0; i < arr.length; i++) { // O(N)
//				heapInsert(arr, i); // O(logN)
//			}
 		// O(N)
 		for (int i = arr.length - 1; i >= 0; i--) {
 			heapify(arr, i, arr.length);
 		}
 		int heapSize = arr.length;
 		swap(arr, 0, --heapSize);
 		// O(N*logN)
 		while (heapSize > 0) { // O(N)
 			heapify(arr, 0, heapSize); // O(logN)
 			swap(arr, 0, --heapSize); // O(1)
 		}
 	}

 	// arr[index]刚来的数,往上
 	public static void heapInsert(int[] arr, int index) {
 		while (arr[index] > arr[(index - 1) / 2]) {
 			swap(arr, index, (index - 1) / 2);
 			index = (index - 1) / 2;
 		}
 	}

 	// arr[index]位置的数,能否往下移动
 	public static void heapify(int[] arr, int index, int heapSize) {
 		int left = index * 2 + 1; // 左孩子的下标
 		while (left < heapSize) { // 下方还有孩子的时候
 			// 两个孩子中,谁的值大,把下标给largest
 			// 1)只有左孩子,left -> largest
 			// 2) 同时有左孩子和右孩子,右孩子的值<= 左孩子的值,left -> largest
 			// 3) 同时有左孩子和右孩子并且右孩子的值> 左孩子的值, right -> largest
 			int largest = left + 1 < heapSize && arr[left + 1] > arr[left] ? left + 1 : left;
 			// 父和较大的孩子之间,谁的值大,把下标给largest
 			largest = arr[largest] > arr[index] ? largest : index;
 			if (largest == index) {
 				break;
 			}
 			swap(arr, largest, index);
 			index = largest;
 			left = index * 2 + 1;
 		}
 	}

 	public static void swap(int[] arr, int i, int j) {
 		int tmp = arr[i];
 		arr[i] = arr[j];
 		arr[j] = tmp;
 	}
 	public static void printArray(int[] arr) {
 		if (arr == null) {
 			return;
 		}
 		for (int i = 0; i < arr.length; i++) {
 			System.out.print(arr[i] + " ");
 		}
 		System.out.println();
 	}
 	
 	public static void main(String[] args) {
 		// 默认小根堆
 		PriorityQueue<Integer> heap = new PriorityQueue<>();
 		heap.add(6);
 		heap.add(8);
 		heap.add(0);
 		heap.add(2);
 		heap.add(9);
 		heap.add(1);
 		while (!heap.isEmpty()) {
 			System.out.println(heap.poll()+" ");
 		}
 		int[] arr ={6,8,0,2,9,1};
 		printArray(arr);
 		heapSort(arr);
 		printArray(arr);
 	}
 }

与堆有关的题目

已知一个几乎有序的数组,几乎有序的意思是,如果把数组排好顺序的话,每个元素移动的距离一定不超过k,并且k相对于数组长度来说是比较小的。
请选择一个合适的排序序列对数组进行排序

解题思路,先取0-k个数字,也就是k+1个数字放进小根堆,然后接下来,先弹出最小值再加一个数进入小根堆,或者先加一个数再弹出最小值进入小根堆,周而复始。

package question;
import java.util.PriorityQueue;
public class SortArryDistance {
	public static void sorArryDistance(int[] arr,int k){
		//默认小根堆
		PriorityQueue<Integer> heap=new PriorityQueue<>();
		int index=0;
		for (; index <=Math.min(arr.length-1, k); index++) {
			heap.add(arr[index]);
		}
		int i=0;
		for (; index < arr.length; i++,index++) {
			heap.add(arr[index]);//这里先加后弹或者先弹再加,都可以。
			arr[i]=heap.poll();
		}
		//依次把最后的数去掉。
		while(!heap.isEmpty()){
			arr[i++]=heap.poll();
		}	
	}
	public static void printArray(int[] arr) {
		if (arr == null) {
			return;
		}
		for (int i = 0; i < arr.length; i++) {
			System.out.print(arr[i] + " ");
		}
		System.out.println();
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int maxSize = 100;
		int k = (int) (Math.random() * maxSize) + 1;
		PriorityQueue<Integer> heap=new PriorityQueue<>();
		heap.add(8);
		heap.add(4);
		heap.add(4);
		heap.add(5);
		heap.add(7);
		heap.add(3);
		heap.add(0);
		heap.add(2);
		heap.add(5);
		while(!heap.isEmpty()){
			System.out.print(heap.poll()+" ");
		}
		System.out.println();
		int[] arr={8,4,4,5,7,3,0,2,5};
		sorArryDistance(arr, k);
		printArray(arr);
	}
}

比较器

  • 比较器的实质是重载比较运算符
  • 比较器可以很好的应用在特殊标准的排序上面
  • 比较器可以很好的应用在特殊标准排序的结构上
  • 写代码变得异常容易,还适合范型编程

任何比较器:
compare方法里,遵循一个统一的规范:
返回负数的时候,认为第一个参数应该排在前面
返回正数的时候,认为第二个参数应该排在前面
返回0的时候,认为无所谓谁放前面

应用在特殊标准的排序上面

代码如下

package sort;
import java.util.Arrays;
public class Comparator {
	public static  class Student{
		public String name;
		public int age;
		public int id;
		public Student(String name,int id,int age){
			this.name=name;
			this.id=id;
			this.age=age;
		}
	}
	public static class  idascendingagejiangodrdercomparator implements  java.util.Comparator<Student>{
		public int compare(Student o1,Student o2){
			//先按照id升序,再按照age降序
			return o1.id!=o2.id? o1.id-o2.id:o2.age-o1.age;
		}
	}
	public static void main(String[] args) {
		Student student1 = new Student("A", 4, 40);
		Student student2 = new Student("B", 4, 21);
		Student student3 = new Student("C", 3, 12);
		Student student4 = new Student("D", 3, 62);
		Student student5 = new Student("E", 3, 42);
		Student [] students=new Student[]{student1,student2,student3,student4,student5};
		Arrays.sort(students, new idascendingagejiangodrdercomparator());
		for (int i = 0; i < students.length; i++) {
			Student s = students[i];
			System.out.println(s.name + "," + s.id + "," + s.age);
			}
	}
}
/*D,3,62
E,3,42
C,3,12
A,4,40
B,4,21*/

应用在特殊标准排序的结构上

例如把系统默认的小根堆变成大根堆

package sort;
import java.util.Comparator;
import java.util.PriorityQueue;
public class Comparatorheap {
   public static class MyComp implements Comparator<Integer>{
   	@Override
   	public int compare(Integer o1, Integer o2) {
   		return o2-o1;
   	}	 
   }
   public static void main(String[] args) {
   	// 把系统默认的小根堆变成大根堆
   		PriorityQueue<Integer> heap = new PriorityQueue<>(new MyComp());
   		heap.add(5);
   		heap.add(6);
   		heap.add(8);
   		heap.add(0);
   		heap.add(2);
   		heap.add(9);
   		heap.add(1);
   		while(!heap.isEmpty()) {
   			System.out.print(heap.poll()+" ");
   		}			
   }
}
//9 8 6 5 2 1 0 

特点

语言提供的堆结构手写的堆结构
动态改数据,不保证依然有序增加了动态的位置表,能够满足动态改信息的需求

动态修改数据

package sort;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.PriorityQueue;
import sort.Comparator.Student;
import sort.Comparator.idascendingagejiangodrdercomparator;
public class HeapGreat {
	public static class MyHeap<T>{
		//动态数组做的堆结构
		private ArrayList<T> heap;
		//任何一个样本记录着在堆上面的什么位置
		private HashMap<T, Integer> indexMap;
		//堆的大小,初始化为0
		private int heapSize;
		//关于样本类型的比较器,根据比较器,就知道怎么比大小
		private Comparator<? super T>  comparator;
		
		public MyHeap(Comparator<? super T> com){
			heap=new ArrayList<>();
			indexMap=new HashMap<>();
			heapSize=0;
			comparator=com;
		}
		public boolean isEmpty(){
			return heapSize==0;
		}
		public int size(){
			return heapSize;
		}
		//是否收过某个数据
		public boolean contains(T key){
			return indexMap.containsKey(key);
		}
		 public void push(T value){
			 heap.add(value);
			 indexMap.put(value, heapSize);
			 heapInsert(heapSize++);
		 }
		 public T pop(){
			 //0位置的东西一定是要最后返回的
			T ans=heap.get(0);
			int end=heapSize-1;
			//0位置和最后一个位置做交换
			swap(0, end);
			//移除最后一个位置,不是移除样本,把end位置上的东西删掉
			//indexMap里面关于ans也要删掉,heap,indexMap需要同步
			heap.remove(end);
			indexMap.remove(ans);
			heapify(0,--heapSize);
			return ans;
			
		 }
		 public void heapInsert(int index){
			 //通过比较器给出我怎么与父比较大小
			 while(comparator.compare(heap.get(index),heap.get((index-1)/2))<0){
				 swap(index,(index-1)/2);
				 index=(index-1)/2;
			 }
		 }
		 public void heapify(int index,int heapSize){
			 int left=index*2+1;
			 while(left<heapSize){
				 int largest=left+1<heapSize && (comparator.compare(heap.get(left+1),heap.get(left))<0)? left+1:left;
				 largest=comparator.compare(heap.get(largest),heap.get(index))<0? largest:index;
				 if (largest==index) {
					break;
				}
				 swap(largest, index);
				 index=largest;
				 left=index*2+1;
			 }
		 }
		 public void resign(T value){
			 //找到修改的位置,heapInsert,heapify两个只会中一个
			 int valueindex=indexMap.get(value);
			 heapInsert(valueindex);
			 heapify(valueindex, heapSize);
		 }
		 public void swap(int i,int j){
			 //既在heap里面交换,也在indexmap里面交换
			 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);
		 }
	}
	public static  class Student{
		public int  class1;
		public int age;
		public int id;
		public Student(int class1,int id,int age){
			this.class1=class1;
			this.id=id;
			this.age=age;
		}
	}
	public static class  idascendingagejiangodrdercomparator implements  java.util.Comparator<Student>{
		public int compare(Student o1,Student o2){
			//先按照id升序,再按照age降序
			return o1.id!=o2.id? o1.id-o2.id:o2.age-o1.age;
		}
	}
	public static void main(String[] args) {
		Student student1 = new Student(1, 4, 40);
		Student student2 = new Student(2, 6, 21);
		Student student3 = new Student(2, 7, 12);
		Student student4 = new Student(1, 4, 62);
		Student student5 = new Student(5, 6, 42);
		MyHeap<Student> heap= new MyHeap<>(new idascendingagejiangodrdercomparator());
		heap.push(student1);
		heap.push(student2);
		heap.push(student3);
		heap.push(student4);
		heap.push(student5);
		//打印出来
		while(!heap.isEmpty()){
			Student cur=heap.pop();
			System.out.println(cur.class1+" "+cur.id+" "+cur.age);
		}
		System.out.println("=============");
		heap.push(student1);
		heap.push(student2);
		heap.push(student3);
		heap.push(student4);
		heap.push(student5);
		//修改数据
		student5.id = 0;
		heap.resign(student5);
		while(!heap.isEmpty()){
			Student cur=heap.pop();
			System.out.println(cur.class1+" "+cur.id+" "+cur.age);
		}
		System.out.println("=============");
		
		while(!heap.isEmpty()){
			Student cur=heap.pop();
			System.out.println(cur.class1+" "+cur.id+" "+cur.age);
		}
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
PriorityQueue是Java中的一个数据结构,它是基于优先级堆实现的。PriorityQueue可以根据元素的优先级进行排序,并且可以快速获取最小值或最大值。 在PriorityQueue中,获取最小值的方式是通过比较(Comparator)来实现的。比较是一个接口,用于定义元素之间的比较规则。在PriorityQueue中,我们可以通过自定义比较来指定元素的排序方式。 要获取最小值,我们需要创建一个实现了Comparator接口的比较,并将其传递给PriorityQueue的构造函数。比较需要实现compare方法,该方法接受两个参数,返回一个整数值表示两个元素的比较结果。 下面是一个示例代码,演示了如何使用比较获取PriorityQueue中的最小值: ```java import java.util.Comparator; import java.util.PriorityQueue; public class PriorityQueueExample { public static void main(String[] args) { // 创建一个自定义比较 Comparator<Integer> comparator = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { // 按照升序排序 return o1 - o2; } }; // 创建一个PriorityQueue,并传入比较 PriorityQueue<Integer> pq = new PriorityQueue<>(comparator); // 添加元素到PriorityQueue pq.add(5); pq.add(2); pq.add(8); pq.add(1); // 获取最小值 int min = pq.peek(); System.out.println("最小值:" + min); } } ``` 在上面的示例中,我们创建了一个自定义比较,按照升序排序。然后创建了一个PriorityQueue,传入比较。接下来,我们向PriorityQueue中添加一些元素,并使用peek方法获取最小值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值