比較器與堆
堆結構
完全二叉樹結構
完全二叉樹結構:要麼本層是滿的,要麼先滿左邊的,以下都是完全二叉樹
graph TD
A-->B
A-->C
graph TD
A-->B
A-->C
B-->D
B-->E
C-->F
陣列實現堆
- 堆結構就是用陣列實現的完全二叉樹結構
用陣列實現完全二叉樹結構,從陣列下標0開始,當成依次補齊二叉樹結構的資料
graph TD
0--> 1
0--> 2
1--> 3
1-->4
2-->5
2-->6
某位置i的左孩子下標為:
lchild = 2*i + 1
某位置i的右孩子的下標為:
rchild = 2*i + 2
某位置i的父節點位置為:
parent = (i-1) / 2
當我們不使用陣列的0下標,從1位置開始構建完全二叉樹時,方便使用位操作:
某位置i的左孩子下標為:
lchild = 2*i <==> i << 1
某位置i的右孩子的下標為:
rchild = 2*i + 1 <==> (i << 1) | 1
某位置i的父節點位置為:
parent = i / 2 <==> i >> 1
大根堆與小根堆
-
完全二叉樹中如果每棵子樹的最大值都在頂部就是大根堆
-
完全二叉樹中如果每顆子樹的最小值都在頂部就是小根堆
我們認為堆就是大根堆或者小根堆,既不是大根堆也不是小根堆的完全二叉樹只是完全二叉樹,不能稱之為堆
構建堆
- 堆結構的heapInsert與heapify操作
heapInsert
思路:例如我們要構建一個大根堆,我們把所有的數依次新增到一個陣列(下標從0開始)中去,每次新增一個數的時候,要去用找父親節點的公式parent = (i-1) / 2找到父節點區比較,如果比父節點大就和父節點交換向上移動,移動後再用自己當前位置和父親節點比較...,小於等於父節點不做處理。這樣使用者每加一個數,我們都能保證該結構是大根堆,對應程式碼的push方法
我們的調整代價實際上就是這顆樹的高度層數,logN
heapify
原堆結構,刪除最大值,繼續調整維持成大根堆
思路:我們刪除了最大值,也就是arr[0]位置,之後我們把堆最末尾的位置調整到arr[0]位置,堆大小減一。讓現在arr[0]位置的數找左右孩子比較...,進行hearify操作,讓其沉下去。沉到合適的位置之後,仍然是大根堆。對應程式碼的pop方法
heapify的下沉操作,仍然是樹的高度,logN
堆結構很重要很重要
package class04;
public class Code02_Heap01 {
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 void push(int value) {
if (heapSize == limit) {
throw new RuntimeException("heap is full");
}
heap[heapSize] = value;
// value heapSize
heapInsert(heap, heapSize++);
}
// 使用者此時,讓你返回最大值,並且在大根堆中,把最大值刪掉
// 剩下的數,依然保持大根堆組織
public int pop() {
int ans = heap[0];
swap(heap, 0, --heapSize);
heapify(heap, 0, heapSize);
return ans;
}
// 往堆上新增數,需要用當前位置找父節點比較
private void heapInsert(int[] arr, int index) {
// arr[index]
// arr[index] 不比 arr[index父]大了 , 停
// index = 0時也停
while (arr[index] > arr[(index - 1) / 2]) {
swap(arr, index, (index - 1) / 2);
index = (index - 1) / 2;
}
}
// 從index位置,往下看,不斷的下沉,
// 停的條件:我的孩子都不再比我大;已經沒孩子了
private void heapify(int[] arr, int index, int heapSize) {
int left = index * 2 + 1;
// 左孩子沒越界,如果左孩子越界有孩子一定也越界
while (left < heapSize) {
// 左右兩個孩子中,誰大,誰把自己的下標給largest
// 什麼請款下選擇右 -> (1) 有右孩子 && (2)右孩子的值比左孩子大才行
// 否則,左
int largest = left + 1 < heapSize && arr[left + 1] > arr[left] ? left + 1 : left;
// 左右孩子中最大值,和當前值比較,誰大誰把下標給largest(當前,左,右的最大值下標)
largest = arr[largest] > arr[index] ? largest : index;
// index位置上的數比左右孩子的數都大,已經無需下沉
if (largest == index) {
break;
}
// 交換後,繼續找左右孩子進行比較,周而復始
swap(arr, largest, index);
index = largest;
left = index * 2 + 1;
}
}
private void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
// 暴力,O(N)複雜度實現的大根堆。用來做對數器
public static class RightMaxHeap {
private int[] arr;
private final int limit;
private int size;
public RightMaxHeap(int limit) {
arr = new int[limit];
this.limit = limit;
size = 0;
}
public boolean isEmpty() {
return size == 0;
}
public boolean isFull() {
return size == limit;
}
public void push(int value) {
if (size == limit) {
throw new RuntimeException("heap is full");
}
arr[size++] = value;
}
public int pop() {
int maxIndex = 0;
for (int i = 1; i < size; i++) {
if (arr[i] > arr[maxIndex]) {
maxIndex = i;
}
}
int ans = arr[maxIndex];
arr[maxIndex] = arr[--size];
return ans;
}
}
public static void main(String[] args) {
int value = 1000;
int limit = 100;
int testTimes = 1000000;
for (int i = 0; i < testTimes; i++) {
int curLimit = (int) (Math.random() * limit) + 1;
MyMaxHeap my = new MyMaxHeap(curLimit);
RightMaxHeap test = new RightMaxHeap(curLimit);
int curOpTimes = (int) (Math.random() * limit);
for (int j = 0; j < curOpTimes; j++) {
if (my.isEmpty() != test.isEmpty()) {
System.out.println("Oops!");
}
if (my.isFull() != test.isFull()) {
System.out.println("Oops!");
}
if (my.isEmpty()) {
int curValue = (int) (Math.random() * value);
my.push(curValue);
test.push(curValue);
} else if (my.isFull()) {
if (my.pop() != test.pop()) {
System.out.println("Oops!");
}
} else {
if (Math.random() < 0.5) {
int curValue = (int) (Math.random() * value);
my.push(curValue);
test.push(curValue);
} else {
if (my.pop() != test.pop()) {
System.out.println("Oops!");
}
}
}
}
}
System.out.println("finish!");
}
}
堆排序
- 對於使用者給的所有資料,我們先讓其構建成為大根堆
- 對於0到N-1位置的數,我們依次讓N-1位置的數和0位置的數(全域性最大值)交換,此時全域性最大值來到了陣列最大位置,堆大小減一,再heapify調整成大根堆。再用N-2位置的數和調整後的0位置的數交換,相同操作。直至0位置和0位置交換。每次heapify為logN,交換調整了N次
- 所以堆排序的時間複雜度為O(NlogN)
- 堆排序額為空間複雜度為O(1),且不存在遞迴行為
package class04;
import java.util.Arrays;
import java.util.PriorityQueue;
public class Code04_HeapSort {
// 堆排序額外空間複雜度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)
// }
// 優化版本,heapInsert改為heapify。從末尾開始看是否需要heapify=》O(N)複雜度。
// 但是這只是優化了原有都是構建堆(O(NlogN)),最終的堆排序仍然是O(NlogN)
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;
}
// for test
public static void comparator(int[] arr) {
Arrays.sort(arr);
}
// for test
public static int[] generateRandomArray(int maxSize, int maxValue) {
int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
}
return arr;
}
// for test
public static int[] copyArray(int[] arr) {
if (arr == null) {
return null;
}
int[] res = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
res[i] = arr[i];
}
return res;
}
// for test
public static boolean isEqual(int[] arr1, int[] arr2) {
if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) {
return false;
}
if (arr1 == null && arr2 == null) {
return true;
}
if (arr1.length != arr2.length) {
return false;
}
for (int i = 0; i < arr1.length; i++) {
if (arr1[i] != arr2[i]) {
return false;
}
}
return true;
}
// for test
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();
}
// for test
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 testTime = 500000;
int maxSize = 100;
int maxValue = 100;
boolean succeed = true;
for (int i = 0; i < testTime; i++) {
int[] arr1 = generateRandomArray(maxSize, maxValue);
int[] arr2 = copyArray(arr1);
heapSort(arr1);
comparator(arr2);
if (!isEqual(arr1, arr2)) {
succeed = false;
break;
}
}
System.out.println(succeed ? "Nice!" : "Fucking fucked!");
int[] arr = generateRandomArray(maxSize, maxValue);
printArray(arr);
heapSort(arr);
printArray(arr);
}
}
關於上述heapInsert改為heapIfy的優化:
在我們從0到N-1進行heapInsert的時候,是O(NlogN)不做解釋,當我們從N-1到0上依次heapify的時候,整體來看,整棵樹的跟節點的heapify層數N/2,第二層為N/4且有兩個節點。那麼實質是N個不同的層數相加:
T(N) = (\frac{N}{2} * 1) + (\frac{N}{4} * 2) + (\frac{N}{8} * 3) + (\frac{N}{16} * 4) + ...
=>
2T(N) = (\frac{N}{2} * 2) + (\frac{N}{2} * 2) + (\frac{N}{4} * 3) + (\frac{N}{8} * 4) + ...
=>
T(N) = N + \frac{N}{2} + \frac{N}{4} + \frac{N}{8} + ...
=> O(N)
語言、系統提供的堆和手寫堆的選擇
系統實現的堆
系統實現的堆實質上就是優先順序佇列,雖然名稱叫優先順序佇列,底層就是堆實現的。預設是小根堆,我們可以自定義比較器把它改為大根堆
package class04;
import java.util.Comparator;
import java.util.PriorityQueue;
public class Test {
// 負數,o1 放在上面的情況
public static class MyComp implements Comparator<Integer>{
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
}
public static void main(String[] args) {
System.out.println("hello");
// 大根堆
PriorityQueue<Integer> heap = new PriorityQueue<>(new MyComp());
heap.add(5);
heap.add(7);
heap.add(3);
heap.add(0);
heap.add(2);
heap.add(5);
while(!heap.isEmpty()) {
System.out.println(heap.poll());
}
}
}
堆的相關面試題:
題目一:已知一個幾乎有序的陣列。幾乎有序是指,如果把陣列排好序的話,每個元素移動的距離一定不超過k,並且k相對於陣列長度來說是比較小的。請選擇一個合適的排序策略,對這個陣列進行排序
思路:例如給定一個陣列,k=5,那麼我們從0開始,前K+1個數也就是0到5位置的數放到小根堆,排序之後把最小的放到0位置,接下來把6位置放小根堆(此時小根堆裡面有0到6位置的數),由於0位置的數有距離限制只能從0到5上選擇,所以此時彈出最小值放到1位置,此時1位置被固定...
package class04;
import java.util.Arrays;
import java.util.PriorityQueue;
public class Code05_SortArrayDistanceLessK {
public static void sortedArrDistanceLessK(int[] arr, int k) {
if (k == 0) {
return;
}
// 預設小根堆
PriorityQueue<Integer> heap = new PriorityQueue<>();
int index = 0;
// 0...K-1
for (; index <= Math.min(arr.length - 1, k - 1); 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();
}
}
// for test
public static void comparator(int[] arr, int k) {
Arrays.sort(arr);
}
// for test
public static int[] randomArrayNoMoveMoreK(int maxSize, int maxValue, int K) {
int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
}
// 先排個序
Arrays.sort(arr);
// 然後開始隨意交換,但是保證每個數距離不超過K
// swap[i] == true, 表示i位置已經參與過交換
// swap[i] == false, 表示i位置沒有參與過交換
boolean[] isSwap = new boolean[arr.length];
for (int i = 0; i < arr.length; i++) {
int j = Math.min(i + (int) (Math.random() * (K + 1)), arr.length - 1);
if (!isSwap[i] && !isSwap[j]) {
isSwap[i] = true;
isSwap[j] = true;
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
return arr;
}
// for test
public static int[] copyArray(int[] arr) {
if (arr == null) {
return null;
}
int[] res = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
res[i] = arr[i];
}
return res;
}
// for test
public static boolean isEqual(int[] arr1, int[] arr2) {
if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) {
return false;
}
if (arr1 == null && arr2 == null) {
return true;
}
if (arr1.length != arr2.length) {
return false;
}
for (int i = 0; i < arr1.length; i++) {
if (arr1[i] != arr2[i]) {
return false;
}
}
return true;
}
// for test
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();
}
// for test
public static void main(String[] args) {
System.out.println("test begin");
int testTime = 500000;
int maxSize = 100;
int maxValue = 100;
boolean succeed = true;
for (int i = 0; i < testTime; i++) {
int k = (int) (Math.random() * maxSize) + 1;
int[] arr = randomArrayNoMoveMoreK(maxSize, maxValue, k);
int[] arr1 = copyArray(arr);
int[] arr2 = copyArray(arr);
sortedArrDistanceLessK(arr1, k);
comparator(arr2, k);
if (!isEqual(arr1, arr2)) {
succeed = false;
System.out.println("K : " + k);
printArray(arr);
printArray(arr1);
printArray(arr2);
break;
}
}
System.out.println(succeed ? "Nice!" : "Fucking fucked!");
}
}
時間複雜度O(NlogK)
系統堆和手寫堆選擇
使用系統提供的堆:如果我們只是要依次拿最大值,那麼做成大根堆,如果我們要最小值我們把堆結構做成小根堆。就是簡單的我們新增值,拿值,我們就選擇系統提供的堆
選擇手寫堆:如果已經放到系統堆中的元素,加入我們根據需求會在放入堆之後要改動這些元素的值,系統堆並不保證彈出來的東西是正確的,這個時候需要我們手動寫一個我們自定義的堆。雖然存在那種排好堆改某些元素讓其重新有序的堆結構,但是實質上它是重新掃每個元素去heapinsert,代價太高。手動改寫堆的例子例如Dijkstra演算法就存在改寫堆的優化
package class04;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.PriorityQueue;
public class Code03_Heap02 {
// 堆
public static class MyHeap<T> {
// 堆結構,陣列實現
private ArrayList<T> heap;
// 任意一個元素,我們記錄它在我們堆上的位置資訊(反向表),此時我們找到我們要改的元素的位置就O(1)
private HashMap<T, Integer> indexMap;
// 堆大小
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);
// 由於依次新增元素,新增進來的元素位置就是heapSize
indexMap.put(value, heapSize);
heapInsert(heapSize++);
}
// 彈出0號位置的元素,要同步堆和字典的操作
public T pop() {
T ans = heap.get(0);
int end = heapSize - 1;
swap(0, end);
heap.remove(end);
indexMap.remove(ans);
heapify(0, --heapSize);
return ans;
}
// 用來滿足自定義的需求,使用者要改某個元素的值,我們需要改過之後繼續維持堆結構
public void resign(T value) {
int valueIndex = indexMap.get(value);
// 改變值之後,我們不確定是值變大了還是變小了,即不確定是需要heapInsert還是heapify,但是兩個操作只會命中一個
heapInsert(valueIndex);
heapify(valueIndex, heapSize);
}
// heapInsert時,需要用我們自己的比較器進行比較
private 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;
}
}
private 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;
}
}
// 每次交換,不經要交換堆中兩個位置的元素,在我們的字典中也要要換位置
private 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);
}
}
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 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, 50, 11111);
s2 = new Student(1, 60, 22222);
s3 = new Student(6, 10, 33333);
s4 = new Student(3, 20, 44444);
s5 = new Student(7, 72, 55555);
s6 = new Student(1, 14, 66666);
PriorityQueue<Student> heap = new PriorityQueue<>(new StudentComparator());
heap.add(s1);
heap.add(s2);
heap.add(s3);
heap.add(s4);
heap.add(s5);
heap.add(s6);
while (!heap.isEmpty()) {
Student cur = heap.poll();
System.out.println(cur.classNo + "," + cur.age + "," + cur.id);
}
System.out.println("===============");
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);
while (!myHeap.isEmpty()) {
Student cur = myHeap.pop();
System.out.println(cur.classNo + "," + cur.age + "," + cur.id);
}
System.out.println("===============");
s1 = new Student(2, 50, 11111);
s2 = new Student(1, 60, 22222);
s3 = new Student(6, 10, 33333);
s4 = new Student(3, 20, 44444);
s5 = new Student(7, 72, 55555);
s6 = new Student(1, 14, 66666);
heap = new PriorityQueue<>(new StudentComparator());
heap.add(s1);
heap.add(s2);
heap.add(s3);
heap.add(s4);
heap.add(s5);
heap.add(s6);
s2.age = 6;
s4.age = 12;
s5.age = 10;
s6.age = 84;
while (!heap.isEmpty()) {
Student cur = heap.poll();
System.out.println(cur.classNo + "," + cur.age + "," + cur.id);
}
System.out.println("===============");
s1 = new Student(2, 50, 11111);
s2 = new Student(1, 60, 22222);
s3 = new Student(6, 10, 33333);
s4 = new Student(3, 20, 44444);
s5 = new Student(7, 72, 55555);
s6 = new Student(1, 14, 66666);
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 = 6;
myHeap.resign(s2);
s4.age = 12;
myHeap.resign(s4);
s5.age = 10;
myHeap.resign(s5);
s6.age = 84;
myHeap.resign(s6);
while (!myHeap.isEmpty()) {
Student cur = myHeap.pop();
System.out.println(cur.classNo + "," + cur.age + "," + cur.id);
}
// 對數器
System.out.println("test begin");
int maxValue = 100000;
int pushTime = 1000000;
int resignTime = 100;
MyHeap<Student> test = new MyHeap<>(new StudentComparator());
ArrayList<Student> list = new ArrayList<>();
for(int i = 0 ; i < pushTime; i++) {
Student cur = new Student(1,(int) (Math.random() * maxValue), 1000);
list.add(cur);
test.push(cur);
}
for(int i = 0 ; i < resignTime; i++) {
int index = (int)(Math.random() * pushTime);
list.get(index).age = (int) (Math.random() * maxValue);
test.resign(list.get(index));
}
int preAge = Integer.MIN_VALUE;
while(test.isEmpty()) {
Student cur = test.pop();
if(cur.age < preAge) {
System.out.println("Oops!");
}
preAge = cur.age;
}
System.out.println("test finish");
}
}
比較器
1、比較器的實質就是過載比較運算子
2、比較器可以很好的應用在特殊標準的排序上
3、比較器可以很好的應用在根據特殊標準排序的結構上
任何有序結構,我們可以傳入我們的比較器,自定義我們自己的排序規則,不傳它會按自己預設的規則排序
4、寫程式碼變得異常容易,還用於泛型程式設計
比較規則中o1,o2,比較器返回負數表示o1要排在前面,返回正數表示o1要排在後面,返回0表示o1和o1相等無需排序。在java中自定義的比較器(MyComparator)實現Comparator介面,實現該介面中的compare方法,自定義我們的比較規則。
使用示例:Arrays.sort(student, new MyComparator())
package class04;
import java.util.Arrays;
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.TreeSet;
public class Code01_Comparator {
// 自定義我們的排序物件
public static class Student {
public String name;
public int id;
public int age;
public Student(String name, int id, int age) {
this.name = name;
this.id = id;
this.age = age;
}
}
public static class IdAscendingComparator
implements Comparator<Student> {
// 返回負數的時候,第一個引數排在前面
// 返回正數的時候,第二個引數排在前面
// 返回0的時候,誰在前面無所謂
@Override
public int compare(Student o1, Student o2) {
return o1.id - o2.id;
}
}
public static class IdDescendingComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o2.id - o1.id;
}
}
public static class AgeAscendingComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.age - o2.age;
}
}
public static class AgeDescendingComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o2.age - o1.age;
}
}
public static class AgeShengIdSheng implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.age != o2.age ? (o1.age - o2.age)
: (o1.id - o2.id);
}
}
// 先按照id排序,id小的,放前面;
// id一樣,age大的,前面;
public static class IdInAgeDe implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.id != o2.id ? o1.id - o2.id : ( o2.age - o1.age );
}
}
public static void printStudents(Student[] students) {
for (Student student : students) {
System.out.println("Name : " + student.name + ", Id : " + student.id + ", Age : " + student.age);
}
}
public static void printArray(Integer[] arr) {
if (arr == null) {
return;
}
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static class MyComp implements Comparator<Integer> {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
}
public static class AComp implements Comparator<Integer>{
// 如果返回負數,認為第一個引數應該拍在前面
// 如果返回正數,認為第二個引數應該拍在前面
// 如果返回0,認為誰放前面都行
@Override
public int compare(Integer arg0, Integer arg1) {
return arg1 - arg0;
// return 0;
}
}
public static void main(String[] args) {
Integer[] arr = {5,4,3,2,7,9,1,0};
Arrays.sort(arr, new AComp());
for(int i = 0 ;i < arr.length;i++) {
System.out.println(arr[i]);
}
System.out.println("===========================");
Student student1 = new Student("A", 2, 20);
Student student2 = new Student("B", 3, 21);
Student student3 = new Student("C", 1, 22);
Student[] students = new Student[] { student1, student2, student3 };
System.out.println("第一條列印");
Arrays.sort(students, new IdAscendingComparator());
printStudents(students);
System.out.println("===========================");
Arrays.sort(students, new IdDescendingComparator());
printStudents(students);
System.out.println("===========================");
Arrays.sort(students, new AgeAscendingComparator());
printStudents(students);
System.out.println("===========================");
////
//// Arrays.sort(students, new AgeDescendingComparator());
//// printStudents(students);
//// System.out.println("===========================");
//
// Arrays.sort(students, new AgeShengIdSheng());
// printStudents(students);
//
// System.out.println("===========================");
// System.out.println("===========================");
// System.out.println("===========================");
//
// PriorityQueue<Student> maxHeapBasedAge = new PriorityQueue<>(new AgeDescendingComparator());
// maxHeapBasedAge.add(student1);
// maxHeapBasedAge.add(student2);
// maxHeapBasedAge.add(student3);
// while (!maxHeapBasedAge.isEmpty()) {
// Student student = maxHeapBasedAge.poll();
// System.out.println("Name : " + student.name + ", Id : " + student.id + ", Age : " + student.age);
// }
// System.out.println("===========================");
PriorityQueue<Student> minHeapBasedId
= new PriorityQueue<>(new AgeAscendingComparator());
minHeapBasedId.add(student1);
minHeapBasedId.add(student2);
minHeapBasedId.add(student3);
while (!minHeapBasedId.isEmpty()) {
Student student = minHeapBasedId.poll();
System.out.println("Name : " + student.name + ", Id : " + student.id + ", Age : " + student.age);
}
System.out.println("===========================");
System.out.println("===========================");
System.out.println("===========================");
TreeSet<Student> treeAgeDescending = new TreeSet<>(new AgeAscendingComparator());
treeAgeDescending.add(student1);
treeAgeDescending.add(student2);
treeAgeDescending.add(student3);
Student studentFirst = treeAgeDescending.first();
System.out.println("Name : " + studentFirst.name + ", Id : " + studentFirst.id + ", Age : " + studentFirst.age);
Student studentLast = treeAgeDescending.last();
System.out.println("Name : " + studentLast.name + ", Id : " + studentLast.id + ", Age : " + studentLast.age);
System.out.println("===========================");
}
}