堆
堆结构
思想
- 堆结构就是用数组实现的完全二叉树结构
- 完全二叉树中,如果每颗子数的最大值都在顶部就是大根堆
- 完全二叉树中,如果每颗子数的最小值都在底部就是小根堆
- 堆结构的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);
}
}
}