算法基础4

    科技2022-07-15  119

    算法基础4

    一、堆结构

    1.概念

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

    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 = arr[largest] > arr[index] ? largest : 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; } } 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!"); } }

    2.堆排序

    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) // } 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); } }

    3.语言提供的堆结构vs手写的堆结构

    取决于,你有没有动态改信息的需求! 语言提供的堆结构,如果你动态改数据,不保证依然有序 手写堆结构,因为增加了对象的位置表,所以能够满足动态改信息的需求

    二、与堆有关的题目

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

    请选择一个合适的排序策略,对这个数组进行排序。

    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; 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); indexMap.put(value, heapSize); heapInsert(heapSize++); } 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(valueIndex); heapify(valueIndex, heapSize); } 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"); } } 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!"); } }

    三、比较器

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

    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("==========================="); } }
    Processed: 0.014, SQL: 8