diff --git a/src/main/java/com/bruis/algorithminjava/algorithm/huawei/Question01.java b/src/main/java/com/bruis/algorithminjava/algorithm/huawei/Question01.java deleted file mode 100644 index 6f1092a..0000000 --- a/src/main/java/com/bruis/algorithminjava/algorithm/huawei/Question01.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.bruis.algorithminjava.algorithm.huawei; - -import java.util.Scanner; - -/** - * @author LuoHaiYang - * - * 题目描述:输入一个int型的正整数,计算出该int型数据在内存中存储时1的个数。 - * - * 输入描述: 输入一个整数(int类型) - * - * 输出描述: 这个数转换成2进制后,输出1的个数 - * - * 实例1: - * - * 输入:5 - * 输出:2 - * - */ -public class Question01 { - - /** - * 笨办法 - * @param args - */ - public static void main(String[] args) { - - // 巧妙法,通过二进制位运算 - Scanner in = new Scanner(System.in); - - int count = in.nextInt(), result = 0; - while (count > 0) { - if ((count & 1) > 0) { - result++; - } - count = count >> 1; - } - System.out.println(result); -/* - 笨办法 - Scanner scanner = new Scanner(System.in); - int input = scanner.nextInt(); - char[] bytes = Integer.toBinaryString(input).toCharArray(); - int result = 0; - for (char c : bytes) { - if (c == '1') { - result++; - } - } - System.out.println(result); -*/ - } - -} diff --git a/src/main/java/com/bruis/algorithminjava/algorithm/sort/BinarySearch.java b/src/main/java/com/bruis/algorithminjava/algorithm/sort/BinarySearch.java index b6de14f..ef5cb71 100644 --- a/src/main/java/com/bruis/algorithminjava/algorithm/sort/BinarySearch.java +++ b/src/main/java/com/bruis/algorithminjava/algorithm/sort/BinarySearch.java @@ -8,22 +8,21 @@ */ public class BinarySearch { - public static int binarySearch(int[] arr, int n, int target) { - - // 在 [left, right]范围里寻找target - int left = 0, right = n - 1; - - while (left <= right) { - int mid = (right + left) / 2; - int nums = arr[mid]; - - if (nums == target) { + // 需要排序号的数组,时间复杂度是O(logN) + public static int binarySearch(int[] arr, int target) { + // [l...r] 左右闭区间 + int l = 0, r = arr.length - 1; + // l=r也要判断,例如这种场景:{5}, l=r=0,需要判断 + while (l <= r) { + // 不要(l+r)/2,防止int越界 + int mid = l + (r-l)/2; + if (arr[mid] == target) { return mid; - } else if (nums > target) { - left = mid + 1; + } + if (arr[mid] < target) { + l = mid + 1; } else { - // nums < target - right = mid - 1; + r = mid - 1; } } return -1; diff --git a/src/main/java/com/bruis/algorithminjava/algorithm/sort/BinarySearchFloorAndCeil.java b/src/main/java/com/bruis/algorithminjava/algorithm/sort/BinarySearchFloorAndCeil.java new file mode 100644 index 0000000..e592237 --- /dev/null +++ b/src/main/java/com/bruis/algorithminjava/algorithm/sort/BinarySearchFloorAndCeil.java @@ -0,0 +1,65 @@ +package com.bruis.algorithminjava.algorithm.sort; + +/** + * @Author : haiyang.luo + * @Date : 2026/6/30 11:04 + * @Description : + */ +public class BinarySearchFloorAndCeil { + /* + 功能定义: + floor(v): 返回≤v的最大元素(不存在时返回最近小值) + ceil(v): 返回≥v的最小元素(不存在时返回最近大值) + 特殊处理: 当v=42不存在时,floor返回41,ceil返回43 + + 存在多个v时,floor(v)返回v的第一个索引,ceil(v)返回v的最后一个索引 + */ + + public int floor(int[] arr, int v) { + int lowerBound = lowerBound(arr, v); + if (lowerBound < arr.length && arr[lowerBound] == v) { + return lowerBound; + } + if (lowerBound - 1 >= 0) { + return lowerBound - 1; + } + return -1; + } + + public int ceil(int[] arr, int v) { + int upperBound = upperBound(arr, v); + if (upperBound - 1 >= 0 && arr[upperBound - 1] == v) { + return upperBound - 1; + } + if (upperBound < arr.length) { + return upperBound; + } + return -1; + } + + private int lowerBound(int[] arr, int v) { + int l = 0, r = arr.length - 1; + while (l <= r) { + int mid = l + (r - l) / 2; + if (arr[mid] >= v) { + r = mid - 1; + } else { + l = mid + 1; + } + } + return l; + } + + private int upperBound(int[] arr, int v) { + int l = 0, r = arr.length - 1; + while (l <= r) { + int mid = l + (r - l) / 2; + if (arr[mid] <= v) { + l = mid + 1; + } else { + r = mid - 1; + } + } + return l; + } +} diff --git a/src/main/java/com/bruis/algorithminjava/algorithm/sort/Sort2026.java b/src/main/java/com/bruis/algorithminjava/algorithm/sort/Sort2026.java new file mode 100644 index 0000000..c25391c --- /dev/null +++ b/src/main/java/com/bruis/algorithminjava/algorithm/sort/Sort2026.java @@ -0,0 +1,334 @@ +package com.bruis.algorithminjava.algorithm.sort; + +import java.util.Arrays; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; + +/** + * @Author : haiyang.luo + * @Date : 2026/6/3 17:31 + * @Description : + */ +public class Sort2026 { + + public static void main(String[] args) { + int[] arrays = new int[]{10,9,11,10,10,10,33,2,3,1,0,5}; + twoWayQuickSort(arrays); + System.out.println(Arrays.toString(arrays)); + } + + // 1. 选择排序,时间复杂度O(n^2) + private static void selectionSort(int[] arrays) { + for (int i = 0; i < arrays.length; i++) { + int minIndex = i; + for (int j = i + 1; j < arrays.length; j++) { + if (arrays[j] < arrays[minIndex]) { + minIndex = j; + } + } + if (minIndex != i) { + int tmp = arrays[i]; + arrays[i] = arrays[minIndex]; + arrays[minIndex] = tmp; + } + } + } + + // 2. 冒泡算法,时间复杂度O(n^2) + private static void bubbleSort(int[] arr) { + if (arr == null || arr.length <= 1) { + return; + } + + for (int i = 0; i < arr.length - 1; i++) { + for (int j = 0; j < arr.length - i - 1; j++) { + swap(arr, j, j + 1); + } + } + } + + /** + * a,b,c,d,e,f,g + * ^ ^ + * 普通插入排序法 + * 时间最差复杂度: O(n^2) + */ + private static void insertSort(int[] arr) { + if (arr == null || arr.length <= 1) { + return; + } + for (int i = 1; i < arr.length; i++) { + for (int j = i; j > 0; j--) { + if (arr[j] < arr[j-1]) { + swap(arr, j, j - 1); + } else { + break; + } + } + } + } + + /** + * 不需要每次对比都交换元素 + * a,b,c,d,e,f,g + * e + * ^ + * 时间最差复杂度: O(n^2) + */ + private static void insertSortWithoutSwap(int[] arr) { + if (arr == null || arr.length <= 1) { + return; + } + for (int i = 1; i < arr.length; i++) { + int j; + int e = arr[i]; + for(j = i; j > 0 && arr[j - 1] > e; j--) { + arr[j] = arr[j-1]; + } + arr[j] = e; + } + } + + /** + * 归并排序 + * + */ + private static void mergeSort(int[] arr) { + if (arr == null || arr.length <= 1) { + return; + } + mergeSortInner(arr, 0, arr.length - 1); + } + + private static void mergeSortInner(int[] arr, int left, int right) { + // 越界结束merge + if (left >= right) { + return; + } + // TODO 【优化】会数组溢出 + int mid = (left + right) / 2; + // 这样数组一定不会溢出 +// int mid = left + (right - left) / 2; + // 继续拆左侧元素 + mergeSortInner(arr, left, mid); + // 继续拆右侧元素 + mergeSortInner(arr, mid + 1, right); + + // 走到此处,左右分半(左侧和右侧都是已经排好序了),mid就是左侧最大值;mid+1就是右侧最小值 + if (arr[mid] > arr[mid + 1]) { + doMergeSort(arr, left, mid, right); + } + } + + private static void doMergeSort(int[] arr, int left, int mid, int right) { + // 新开辟一个辅助数组:[left + 1 ... right] + int[] aux = new int[right - left + 1]; + for (int i = left; i <= right; i++) { + aux[i - left] = arr[i]; + } + int i = left, j = mid + 1; + for (int k = left; k <= right; k++) { + if (i > mid) { + arr[k] = aux[j - left]; + j++; + } else if (j > right) { + arr[k] = aux[i - left]; + i++; + } else if (aux[i - left] < aux[j - left]) { // 【TODO】优化:相等时永远左边优先,这样排序会稳定得多 aux[i - left] <= aux[j - left + arr[k] = aux[i - left]; + i++; + } else { + arr[k] = aux[j - left]; + j++; + } + } + } + + /** + * 自底向上的归并排序算法 + * + */ + private static void mergeSortByBottom(int[] arr) { + if (arr == null || arr.length <= 1) { + return; + } + int n = arr.length; + // size 表示当前要归并的子数组长度:1, 2, 4, 8... => size就是数组大小,每次要merge左侧也右侧的数组[size, size],这么好理解一点 + // 每轮merge的数组长度翻倍 + for (int size = 1; size < n; size += size) { + // 跳到下一对待merge数组 + // left + size < n,保证右侧总有数据可以merge => left + size 得到的就是右侧数组的第一个元素(mid+1),这个元素(mid+1)必须得有才能进行merge + for (int left = 0; left < n - size; left += size + size) { + int mid = left + size - 1; // => mid = left + size - 1,所以上面left + size = mid + 1 < n + // for的结束条件只能保证mid + 1 有值,但是不保证right边界不越界,所以得和n-1进行最小值比较 + int right = Math.min(left + size + size - 1, n - 1); + + if (arr[mid] > arr[mid + 1]) { + doMergeSort(arr, left, mid, right); + } + } + } + } + + // ============================ 普通快排 ============================ + + private void quickSort(int[] arr) { + if (arr == null || arr.length <= 1) { + return; + } + doQuickSort(arr, 0, arr.length - 1); + } + + private void doQuickSort(int[] arr, int left, int right) { + if (left >= right) { + return; + } + int p = partition(arr, left, right); + doQuickSort(arr, left, p - 1); + doQuickSort(arr, p + 1, right); + } + + private int partition(int[] arr, int left, int right) { + int p = arr[left]; + int j = left; + // arr[left] arr[left+1...j] < p, p < arr[j+1...i]; + for (int i = left + 1; i <= right; i++) { + if (arr[i] < p) { + swap(arr, ++j, i); + } + } + swap(arr, left, j); + return j; + } + + /** + * 取随机数,尽量将有序的数组打散 + * 生成[a,b]之间随机数,公式为:a + new Random().nextInt(b - a + 1) + */ + private int partitionWithPivot(int[] arr, int left, int right) { + int pivot = left + new Random().nextInt(right - left + 1); + swap(arr, left, pivot); + int p = arr[left]; + int j = left; + for (int i = left + 1; i <= right; i++) { + if (arr[i] < p) { + swap(arr, ++j, i); + } + } + swap(arr, left, j); + return j; + } + + // 问题分析:如果是对接近有序的数组进行排序,普通快排最坏情况下会退化成O(N^2) + // ============================ 普通快排 ============================ + + // ============================ 2路快排 ============================ + private static void twoWayQuickSort(int[] arr) { + if (arr == null || arr.length <= 1) { + return; + } + doTwoWayQuickSort(arr, 0, arr.length - 1); + } + + private static void doTwoWayQuickSort(int[] arr, int left, int right) { + if (left >= right) { + return; + } + int p = twoWayPartition(arr, left, right); + doTwoWayQuickSort(arr, left, p - 1); + doTwoWayQuickSort(arr, p + 1, right); + } + + /** + * 循环过程中: + * arr[left + 1...i - 1] <= p + * arr[j + 1...right] >= p + *
+ * 最后 swap(arr, left, j) 后: + * arr[left...j - 1] <= p + * arr[j] == p + * arr[j + 1...right] >= p + */ + private static int twoWayPartition(int[] arr, int left, int right) { + // 加随机数,避免partition极度不均匀 + int pivot = left + new Random().nextInt(right - left + 1); + swap(arr, left, pivot); + int p = arr[left], i = left + 1, j = right; + while(true) { + while(i <= right && arr[i] < p) { + i++; + } + // p = arr[left]了,没必要比较j 和 left + while(j >= left + 1 && arr[j] > p) { + j--; + } + if (i > j) break; + swap(arr, i++, j--); + } + swap(arr, left, j); + return j; + } + // ============================ 2路快排 ============================ + + // ============================ 3路快排 ============================ + private void threeWayQuickSort(int[] arr) { + if (arr == null || arr.length <= 1) { + return; + } + doThreeWayQuickSort(arr, 0, arr.length - 1); + } + + + /** + * 1) arr[l+1...lt] < p(arr[l]) + * 2) arr[lt+1...i-1] = p(arr[l]) + * 3) arr[gt...r] > p(arr[l]) + *
+ * [a, b, c, d, e, f, g, h, i, j, k]
+ * l
+ * lt
+ * i
+ * gt r
+ * while循环结束
+ * [a, b, c, d, e, f, g, h, i, j, k]
+ * l
+ * lt
+ * i
+ * gt r
+ */
+ private void doThreeWayQuickSort(int[] arr, int left, int right) {
+ if (left >= right) return;
+ // 不要每次递归都new Random()
+// int pivot = left + new Random().nextInt(right - left + 1);
+ int pivot = ThreadLocalRandom.current().nextInt(left, right + 1);
+ swap(arr, pivot, left);
+ int p = arr[left], lt = left, i = left + 1, gt = right + 1;
+ while (i < gt) {
+ if (arr[i] < p) {
+ // lt区域扩大,i得递增
+ swap(arr, ++lt, i++);
+ } else if (arr[i] > p) {
+ // gt区域扩大
+ swap(arr, --gt, i);
+ } else {
+ i++;
+ }
+ }
+ swap(arr, left, lt);
+ // lt -> gt - 1 区间都是相等的值了
+ doThreeWayQuickSort(arr, left, lt - 1);
+ doThreeWayQuickSort(arr, gt, right);
+ }
+ // ============================ 3路快排 ============================
+
+ // TODO 归并排序变种题==> 查找逆序对
+
+ // TODO 快速排序变种题==> 查找第n位数
+
+
+ public static void swap(int[] arr, int sourceIndex, int targetIndex) {
+ int temp = arr[sourceIndex];
+ arr[sourceIndex] = arr[targetIndex];
+ arr[targetIndex] = temp;
+ }
+}
diff --git a/src/main/java/com/bruis/algorithminjava/algorithm/sort/Test.java b/src/main/java/com/bruis/algorithminjava/algorithm/sort/Test.java
new file mode 100644
index 0000000..dc17c86
--- /dev/null
+++ b/src/main/java/com/bruis/algorithminjava/algorithm/sort/Test.java
@@ -0,0 +1,48 @@
+package com.bruis.algorithminjava.algorithm.sort;
+
+/**
+ * @Author : haiyang.luo
+ * @Date : 2026/6/18 12:32
+ * @Description :
+ */
+public class Test {
+ public static void main(String[] args) {
+
+ }
+
+ private void quickSort(int[] arr) {
+ if (arr == null || arr.length <= 1) {
+ return;
+ }
+ doQuickSort(arr, 0, arr.length - 1);
+ }
+
+ // arr[l+1...i-1] < v, arr[j...r] > v
+ // a, b, f, d, e, c, g, h
+ // i
+ // j
+ private void doQuickSort(int[] arr, int l, int r) {
+ if (l >= r) return;
+ int p = partition(arr, l, r);
+ doQuickSort(arr, l, p -1);
+ doQuickSort(arr, p + 1, r);
+ }
+
+ private int partition(int[] arr, int l, int r) {
+ int v = arr[l], i = l + 1, j = r;
+ while (true) {
+ while (i <= r && arr[i] < v) i++;
+ while (j >= l + 1 && arr[j] > v) j--;
+ if (i > j) break;
+ swap(arr, i++, j--);
+ }
+ swap(arr, l, j);
+ return j;
+ }
+
+ private void swap(int[] arr, int a, int b) {
+ int tmp = arr[a];
+ arr[a] = arr[b];
+ arr[b] = tmp;
+ }
+}
diff --git a/src/main/java/com/bruis/algorithminjava/datastructures/heap/HeapPrinter.java b/src/main/java/com/bruis/algorithminjava/datastructures/heap/HeapPrinter.java
new file mode 100644
index 0000000..dee8d54
--- /dev/null
+++ b/src/main/java/com/bruis/algorithminjava/datastructures/heap/HeapPrinter.java
@@ -0,0 +1,50 @@
+package com.bruis.algorithminjava.datastructures.heap;
+
+/**
+ * 打印二叉堆
+ * @Author : haiyang.luo
+ * @Date : 2026/6/20 21:23
+ * @Description :
+ */
+public class HeapPrinter {
+
+ public static void printHeap(int[] heap) {
+ if (heap == null || heap.length == 0) {
+ System.out.println("(empty)");
+ return;
+ }
+
+ int n = heap.length;
+ int height = (int) (Math.log(n) / Math.log(2)) + 1;
+ int maxWidth = (int) Math.pow(2, height) * 4;
+
+ int index = 0;
+
+ for (int level = 0; level < height; level++) {
+ int levelCount = (int) Math.pow(2, level);
+ int spaces = maxWidth / (int) Math.pow(2, level + 1);
+
+ printSpaces(spaces);
+
+ for (int i = 0; i < levelCount && index < n; i++) {
+ System.out.printf("%2d", heap[index++]);
+ printSpaces(spaces * 2 - 2);
+ }
+
+ System.out.println();
+ System.out.println();
+ }
+ }
+
+ private static void printSpaces(int count) {
+ for (int i = 0; i < count; i++) {
+ System.out.print(" ");
+ }
+ }
+
+ public static void main(String[] args) {
+ int[] heap = {100, 10, 12, 60, 70, 50, 40, 10, 20, 30};
+
+ printHeap(heap);
+ }
+}
diff --git a/src/main/java/com/bruis/algorithminjava/datastructures/heap/HeapSort.java b/src/main/java/com/bruis/algorithminjava/datastructures/heap/HeapSort.java
new file mode 100644
index 0000000..661c027
--- /dev/null
+++ b/src/main/java/com/bruis/algorithminjava/datastructures/heap/HeapSort.java
@@ -0,0 +1,47 @@
+package com.bruis.algorithminjava.datastructures.heap;
+
+import java.util.Random;
+
+/**
+ * @Author : haiyang.luo
+ * @Date : 2026/6/25 23:30
+ * @Description :
+ */
+public class HeapSort {
+
+ // 随机数组大小
+ private static final int DEFAULT_RANDOM_ARRAY_SIZE = 10;
+ // 随机范围
+ private static final int DEFAULT_RANDOM_BOUND = 100;
+
+ private int[] data;
+
+ public HeapSort() {}
+
+ public static int[] generateRandomIntArray() {
+ int[] array = new int[DEFAULT_RANDOM_ARRAY_SIZE];
+ Random random = new Random();
+ for (int i = 0; i < array.length; i++) {
+ array[i] = random.nextInt(DEFAULT_RANDOM_BOUND);
+ }
+ return array;
+ }
+
+ // 时间复杂度o(nlogn)
+ public static void heapSort(int[] arr) {
+ MaxHeapHeapify maxHeapHeapify = new MaxHeapHeapify(arr);
+ for (int i = arr.length - 1; i >= 0; i--) {
+ arr[i] = maxHeapHeapify.extractMax();
+ }
+ }
+
+ public static void main(String[] args) {
+ int[] randomArray = generateRandomIntArray();
+ heapSort(randomArray);
+ for (int i = 1; i < randomArray.length; i++) {
+ if (randomArray[i] < randomArray[i-1]) {
+ System.out.println("false");
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/bruis/algorithminjava/datastructures/heap/HeapSortInPlace.java b/src/main/java/com/bruis/algorithminjava/datastructures/heap/HeapSortInPlace.java
new file mode 100644
index 0000000..cf3ca81
--- /dev/null
+++ b/src/main/java/com/bruis/algorithminjava/datastructures/heap/HeapSortInPlace.java
@@ -0,0 +1,10 @@
+package com.bruis.algorithminjava.datastructures.heap;
+
+/**
+ * @Author : haiyang.luo
+ * @Date : 2026/6/25 23:42
+ * @Description :
+ */
+public class HeapSortInPlace {
+
+}
diff --git a/src/main/java/com/bruis/algorithminjava/datastructures/heap/IndexHeap.java b/src/main/java/com/bruis/algorithminjava/datastructures/heap/IndexHeap.java
new file mode 100644
index 0000000..625cd0f
--- /dev/null
+++ b/src/main/java/com/bruis/algorithminjava/datastructures/heap/IndexHeap.java
@@ -0,0 +1,198 @@
+package com.bruis.algorithminjava.datastructures.heap;
+
+import java.util.Arrays;
+
+/**
+ * 索引堆里,只有indexes是堆化了的,而data和reverse数组都是普通的数组。
+ *
+ * @Author : haiyang.luo
+ * @Date : 2026/6/26 00:15
+ * @Description :
+ */
+public class IndexHeap {
+
+ /**
+ * 真实数据。对外使用0开始的索引,内部转换为1开始的索引。
+ */
+ private int[] data;
+ // indexes进行了堆化!! data的索引,indexes[heapIndex]=dataIndex, data[dataIndex]=value
+ private int[] indexes;
+ // 索引数组的反向,reverse[dataIndex]=heapIndex
+ private int[] reverse;
+ private int count;
+ private int capacity;
+
+ public IndexHeap(int capacity) {
+ if (capacity <= 0) {
+ throw new IllegalArgumentException("capacity must be greater than 0.");
+ }
+ this.capacity = capacity;
+ this.data = new int[capacity + 1];
+ this.indexes = new int[capacity + 1];
+ this.reverse = new int[capacity + 1];
+ this.count = 0;
+ }
+
+ public boolean isEmpty() {
+ return count == 0;
+ }
+
+ public int size() {
+ return count;
+ }
+
+ public boolean contains(int i) {
+ checkIndex(i);
+ return reverse[i + 1] != 0;
+ }
+
+ public void insert(int i, int item) {
+ checkIndex(i);
+ if (count + 1 > capacity) {
+ throw new IllegalStateException("out of the capacity.");
+ }
+ if (contains(i)) {
+ throw new IllegalArgumentException("index already exists in heap.");
+ }
+
+ int dataIndex = i + 1;
+ data[dataIndex] = item;
+ indexes[++count] = dataIndex;
+ reverse[dataIndex] = count;
+ shiftUp(count);
+ }
+
+ public int getItem(int i) {
+ if (!contains(i)) {
+ throw new IllegalArgumentException("index does not exist in heap.");
+ }
+ return data[i + 1];
+ }
+
+ public int getMax() {
+ checkNotEmpty();
+ return data[indexes[1]];
+ }
+
+ public int getMaxIndex() {
+ checkNotEmpty();
+ return indexes[1] - 1;
+ }
+
+ public int extractMax() {
+ checkNotEmpty();
+
+ int ret = data[indexes[1]];
+ swapIndexes(1, count);
+ reverse[indexes[count]] = 0;
+ count--;
+ shiftDown(1);
+ return ret;
+ }
+
+ /**
+ * 删除堆中最大值,并返回该最大值对应的外部原始索引。
+ * 外部用户索引从0开始,索引堆内部索引从1开始。
+ *
+ * @return 最大值对应的外部原始索引
+ */
+ public int extractMaxDataIndex() {
+ checkNotEmpty();
+
+ int ret = indexes[1] - 1;
+ swapIndexes(1, count);
+ reverse[indexes[count]] = 0;
+ count--;
+ shiftDown(1);
+ return ret;
+ }
+
+ public void change(int i, int item) {
+ if (!contains(i)) {
+ throw new IllegalArgumentException("index does not exist in heap.");
+ }
+
+ int dataIndex = i + 1;
+ data[dataIndex] = item;
+ int heapIndex = reverse[dataIndex];
+ shiftUp(heapIndex);
+ // 要调用reverse重新取一遍heapIndex,在shiftUp中reverse变了
+ heapIndex = reverse[dataIndex];
+ // 为什么上浮了之后,还要下沉?因为 change(i, item) 可能是把值改大,也可能是把值改小。先试着上浮
+ // 再从当前位置试着下沉,这样无论值变大还是变小,都能恢复堆性质。
+ shiftDown(heapIndex);
+ }
+
+ public int[] toArray() {
+ int[] arr = new int[count];
+ for (int i = 0; i < count; i++) {
+ arr[i] = data[indexes[i + 1]];
+ }
+ return arr;
+ }
+
+ private void shiftUp(int k) {
+ while (k > 1 && data[indexes[k / 2]] < data[indexes[k]]) {
+ swapIndexes(k, k / 2);
+ k /= 2;
+ }
+ }
+
+ private void shiftDown(int k) {
+ while (2 * k <= count) {
+ int j = 2 * k;
+ if (j + 1 <= count && data[indexes[j + 1]] > data[indexes[j]]) {
+ j++;
+ }
+ if (data[indexes[k]] >= data[indexes[j]]) {
+ break;
+ }
+ swapIndexes(k, j);
+ k = j;
+ }
+ }
+
+ private void swapIndexes(int i, int j) {
+ int tmp = indexes[i];
+ indexes[i] = indexes[j];
+ indexes[j] = tmp;
+
+ reverse[indexes[i]] = i;
+ reverse[indexes[j]] = j;
+ }
+
+ private void checkIndex(int i) {
+ if (i < 0 || i >= capacity) {
+ throw new IllegalArgumentException("index is out of range.");
+ }
+ }
+
+ private void checkNotEmpty() {
+ if (isEmpty()) {
+ throw new IllegalStateException("heap is empty.");
+ }
+ }
+
+ public static void demoChangeByOriginalIndex() {
+ int[] scores = {62, 88, 71, 45, 93, 56};
+ IndexHeap indexHeap = new IndexHeap(scores.length);
+ for (int i = 0; i < scores.length; i++) {
+ indexHeap.insert(i, scores[i]);
+ }
+
+ System.out.println("原始数据: " + Arrays.toString(scores));
+ System.out.println("当前最大值: " + indexHeap.getMax() + ", 外部索引: " + indexHeap.getMaxIndex());
+
+ indexHeap.change(3, 99);
+ System.out.println("把外部索引3的值改成99后:");
+ System.out.println("当前最大值: " + indexHeap.getMax() + ", 外部索引: " + indexHeap.getMaxIndex());
+
+ indexHeap.change(3, 40);
+ System.out.println("再把外部索引3的值改成40后:");
+ System.out.println("当前最大值: " + indexHeap.getMax() + ", 外部索引: " + indexHeap.getMaxIndex());
+ }
+
+ public static void main(String[] args) {
+ demoChangeByOriginalIndex();
+ }
+}
diff --git a/src/main/java/com/bruis/algorithminjava/datastructures/heap/MaxHeap.java b/src/main/java/com/bruis/algorithminjava/datastructures/heap/MaxHeap.java
index 3fee523..c0f18e3 100644
--- a/src/main/java/com/bruis/algorithminjava/datastructures/heap/MaxHeap.java
+++ b/src/main/java/com/bruis/algorithminjava/datastructures/heap/MaxHeap.java
@@ -1,204 +1,61 @@
package com.bruis.algorithminjava.datastructures.heap;
-import java.util.Random;
-
/**
- * @author LuoHaiYang
+ * @Author : haiyang.luo
+ * @Date : 2026/6/25 19:16
+ * @Description :
*/
public class MaxHeap {
- /**
- * 注意,由于数组元素默认值为0,所以堆中元素不可为0
- */
private int[] data;
-
- /**
- * 记录数组中元素个数
- */
- private int size;
-
- /**
- * 数组容量(初始化大小)
- */
+ // 堆中的元素
+ private int count;
+ // 最大堆容量
private int capacity;
+ public MaxHeap() {}
+
public MaxHeap(int capacity) {
- data = new int[capacity];
+ data = new int[capacity + 1];
+ count = 0;
this.capacity = capacity;
- size = 0;
- }
- public MaxHeap() {
- data = new int[10];
- capacity = 10;
- size = 0;
- }
-
- // ================================ heapify(二叉堆化) ========================
-
- public MaxHeap(int[] data) {
- int n = data.length;
- this.data = new int[n+1];
- capacity = n;
-
- for (int i = 0; i < n; i++) {
- this.data[i+1] = data[i];
- }
- size = n;
-
- // 这里需要注意,size / 2 得到的索引是二叉堆中最后一个非叶子节点。
- // 注意!!! 这里size / 2 是因为 data 从1开始的,所以最后一个非叶子节点为:size / 2
- // 如果是从0开始,则:size - 1
- for (int i = size / 2; i > 0; i++) {
- siftDown(i);
- }
- }
-
- /**
- * 返回堆中真实存在元素个数
- * @return
- */
- public int size() {
- return size;
- }
-
- /**
- * 返回堆中是否为空
- * @return
- */
- public boolean isEmpty() {
- return size == 0;
}
- /**
- * 返回index索引其父亲节点
- * @param index
- * @return
- */
- private int parent(int index) {
- if (index == 0) {
- throw new IllegalArgumentException("index-0 doesn't have parent");
+ private void shiftUp(int a) {
+ while (a > 1 && data[a] > data[a/2]) {
+ swap(a, a/2);
+ a /= 2;
}
- return (index - 1) / 2;
}
- /**
- * 返回index索引的左子节点
- * @param index
- * @return
- */
- private int leftChild(int index) {
- return index * 2 + 1;
- }
-
- /**
- * 返回index索引的右子节点
- * @param index
- * @return
- */
- private int rightChild(int index) {
- return index * 2 + 2;
- }
-
- // ================================ 上浮 siftUp ========================
-
- /**
- * 向堆中添加元素
- */
- public void add(int i) {
- // 注意size和capacity的关系,判断是否容量已经满了
- // 向数组最后一位添加新元素
- data[size] = i;
- siftUp(size++);
- }
-
- /**
- * 上浮过程
- * @param k
- */
- private void siftUp(int k) {
- // k 索引大于0,并且k索引元素值大于k父亲节点元素值
- while (k > 0 && data[parent(k)] < data[k]) {
- // k和parent(k)互换元素
- swap(data, k, parent(k));
- // 向上移动,让k为parent(k)再进行判断
- k = parent(k);
- }
- }
-
- // ================================ 下浮 siftDown ========================
-
- /**
- * 获取堆中最大元素
- * @return
- */
- public int findMax() {
- if (size == 0) {
- throw new IllegalArgumentException("Can't find Max value!");
+ public void insert(int value) {
+ if (count + 1 > capacity) {
+ throw new IllegalStateException("out of the capacity.");
}
- return data[0];
+ data[++count] = value;
+ shiftUp(count);
}
- /**
- * 取出堆中最大元素
- * @return
- */
- public int extractMax() {
- int max = findMax();
- swap(data, 0, size - 1);
- data[size - 1] = 0;
- siftDown(0);
- return max;
+ private void swap(int a, int b) {
+ int tmp = data[a];
+ data[a] = data[b];
+ data[b] = tmp;
}
- /**
- * 下沉操作
- * @param k
- */
- private void siftDown(int k) {
- // 左子节点比数组元素小,则表示有子节点
- while (leftChild(k) < size()) {
- int j = leftChild(k);
- // 如果k的有右子节点
- if (j + 1 < size() && data[j + 1] > data[j]) {
- // 所以让j为右子节点
- // j = rightChild(k); 同
- j++;
- }
- // 此时data[k] 比leftChild和rightChild中的最大值都要大
- if (data[k] > data[j]) {
- break;
- }
- // leftChild和rightChild都比data[k]大,在互换之后,继续下一轮判断
- swap(data, k, j);
- // 互换位置,继续下一轮判断
- k = j;
+ public int[] toArray() {
+ int[] arr = new int[count];
+ for (int i = 0; i < count; i++) {
+ arr[i] = data[i + 1];
}
- }
-
- // ================================ 替换操作 ========================
-
- private void swap(int[] arr, int i, int j) {
- int tmp = arr[i];
- arr[i] = arr[j];
- arr[j] = tmp;
+ return arr;
}
public static void main(String[] args) {
- int n = 100;
- MaxHeap maxHeap = new MaxHeap(n);
- Random random = new Random();
- for (int i = 0; i < n; i++) {
- maxHeap.add(random.nextInt(1000));
- }
- int[] result = new int[n];
- for (int i = 0; i < n; i++) {
- result[i] = maxHeap.extractMax();
- }
- // 测试是否是顺序的
- for (int j = 1; j < n; j++) {
- if (result[j-1] < result[j]) {
- throw new IllegalArgumentException("Error!");
- }
+ int[] heap = {100, 10, 12, 60, 70, 50, 40, 10, 20, 30};
+ MaxHeap maxHeap = new MaxHeap(heap.length);
+ for (int i = 0; i < heap.length; i++) {
+ maxHeap.insert(heap[i]);
}
+ HeapPrinter.printHeap(maxHeap.toArray());
}
}
diff --git a/src/main/java/com/bruis/algorithminjava/datastructures/heap/MaxHeapHeapify.java b/src/main/java/com/bruis/algorithminjava/datastructures/heap/MaxHeapHeapify.java
new file mode 100644
index 0000000..1178b1b
--- /dev/null
+++ b/src/main/java/com/bruis/algorithminjava/datastructures/heap/MaxHeapHeapify.java
@@ -0,0 +1,76 @@
+package com.bruis.algorithminjava.datastructures.heap;
+
+/**
+ * @Author : haiyang.luo
+ * @Date : 2026/6/20 21:00
+ * @Description :
+ */
+public class MaxHeapHeapify {
+
+ private int[] data;
+ private int count;
+
+ public MaxHeapHeapify() {}
+
+ public MaxHeapHeapify(int[] arr) {
+ data = new int[arr.length + 1];
+ count = arr.length;
+ for (int i = 0; i < count; i++) {
+ data[i+1] = arr[i];
+ }
+ heapify();
+ }
+
+ public int extractMax() {
+ if (count == 0) {
+ throw new IllegalStateException("count == 0");
+ }
+ int max = data[1];
+ swap(1, count);
+ count--;
+ shiftDown(1);
+ return max;
+ }
+
+
+
+ private void heapify() {
+ for (int i = count/2; i >= 1; i--) {
+ shiftDown(i);
+ }
+ }
+
+ // 将指定参数位置进行下层操作
+ private void shiftDown(int a) {
+ // 探讨下,2 * a <= count有啥差别
+ while (2 * a <= count) {
+ int b = 2 * a;
+ if (2 * a + 1 <= count && data[b] < data[b+1]) {
+ b += 1;
+ }
+ if (data[b] <= data[a]) break;
+ swap(a, b);
+ a = b;
+ }
+ }
+
+ private void swap(int a, int b) {
+ int tmp = data[a];
+ data[a] = data[b];
+ data[b] = tmp;
+ }
+
+ public int[] toArray() {
+ int[] arr = new int[count];
+ for (int i = 0; i < count; i++) {
+ arr[i] = data[i + 1];
+ }
+ return arr;
+ }
+
+ public static void main(String[] args) {
+ int[] heap = {100, 10, 12, 60, 70, 50, 40, 10, 20, 30};
+ MaxHeapHeapify maxHeap = new MaxHeapHeapify(heap);
+ HeapPrinter.printHeap(maxHeap.toArray());
+ }
+}
diff --git a/src/main/java/com/bruis/algorithminjava/datastructures/list/HashMapLRUCache.java b/src/main/java/com/bruis/algorithminjava/datastructures/list/HashMapLRUCache.java
new file mode 100644
index 0000000..a653c9d
--- /dev/null
+++ b/src/main/java/com/bruis/algorithminjava/datastructures/list/HashMapLRUCache.java
@@ -0,0 +1,181 @@
+package com.bruis.algorithminjava.datastructures.list;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 基于 HashMap + 双向链表实现的 LRU 缓存。
+ *
+ * HashMap 用于 O(1) 定位节点,双向链表用于 O(1) 刷新访问顺序和淘汰尾部节点。
+ *
+ * @Author : haiyang.luo
+ * @Date : 2026/6/30 10:30
+ * @Description :
+ */
+public class HashMapLRUCache