From c171854b46bb2e6301cea38da8f47aeaed10eeae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BB=A3=E7=A0=81=E9=A3=8E=E6=B0=B4=E5=B8=88?= Date: Tue, 11 Jun 2019 18:06:29 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BA=86=E5=9F=BA?= =?UTF-8?q?=E7=A1=80=E6=8E=92=E5=BA=8F=E7=AE=97=E6=B3=95=E5=92=8C=E4=BA=8C?= =?UTF-8?q?=E5=8F=89=E6=A0=91=E7=9A=84=E9=81=8D=E5=8E=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 14 +- resource/markdown/algorithm/BasicSorting.md | 245 ++++++++++++++++++ .../markdown/algorithm/BinaryTreeTraversal.md | 120 +++++++++ 3 files changed, 376 insertions(+), 3 deletions(-) create mode 100644 resource/markdown/algorithm/BasicSorting.md create mode 100644 resource/markdown/algorithm/BinaryTreeTraversal.md diff --git a/README.md b/README.md index c9d1107..cb171ed 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,16 @@ ### :lollipop::lollipop::lollipop:全文持续更新中 ... :recycle::recycle::recycle: + + +### 零、:rocket::rocket::rocket:数据结构与算法 + +* [数据结构与算法 (第 01 篇) Java版:常见基础排序算法与复杂度](https://github.com/about-cloud/JavaCore/blob/master/resource/markdown/algorithm/BasicSorting.md) +* [数据结构与算法 (第 02 篇) Java版:二叉树遍历的n种方法](https://github.com/about-cloud/JavaCore/blob/master/resource/markdown/algorithm/BinaryTreeTraversal.md) + + ### 一、:bullettrain_side::railway_car::railway_car::railway_car:集合框架源码分析 + * [集合框架 (第 01 篇) 源码分析:Collection 框架总览](https://github.com/about-cloud/JavaCore/blob/master/resource/markdown/collection/JavaCollections.md) * [集合框架 (第 02 篇) 源码分析:Map 框架总览](https://github.com/about-cloud/JavaCore/blob/master/resource/markdown/collection/JavaMaps.md) * [集合框架 (第 03 篇) 源码分析:ArrayList](https://github.com/about-cloud/JavaCore/blob/master/resource/markdown/collection/ArrayList.md) @@ -190,6 +199,5 @@ ### 联系作者:flags:: -> :postbox:aboutcloud@163.com -> -> :dizzy:[https://www.FooVv.com](https://www.foovv.com) \ No newline at end of file +> :postbox:linkedme@qq.com +> \ No newline at end of file diff --git a/resource/markdown/algorithm/BasicSorting.md b/resource/markdown/algorithm/BasicSorting.md new file mode 100644 index 0000000..ce1fae7 --- /dev/null +++ b/resource/markdown/algorithm/BasicSorting.md @@ -0,0 +1,245 @@ +### 常见基础排序算法 + + + +#### 排序算法分类 + +![排序算法分类](https://i.loli.net/2019/06/10/5cfe3bf15a32392750.png) + + +#### 时间复杂度 + +| 排序算法 | 最好(时间复杂度) | 平均(时间复杂度) | 最坏(时间复杂度) | 稳定性 | 空间复杂度 | +| ------------ | ------------------------- | ------------------------- | ------------------------- | ------ | -------------------------------- | +| 冒泡排序 | **O**(n) | **O**(n2) | **O**(n2) | 稳定 | **O**(1) | +| **快速排序** | **O**(n*log2n) | **O**(n*log2n) | **O**(n2) | 不稳定 | **O**(log2n)~**O**(n) | +| 直接插入排序 | **O**(n) | **O**(n2) | **O**(n2) | 稳定 | **O**(1) | +| **希尔排序** | **O**(n) | **O**(n1.3) | **O**(n2) | 不稳定 | **O**(1) | +| 简单选择排序 | **O**(n) | **O**(n2) | **O**(n2) | 不稳定 | **O**(1) | +| **堆排序** | **O**(n*log2n) | **O**(n*log2n) | **O**(n*log2n) | 不稳定 | **O**(1) | +| **归并排序** | **O**(n*log2n) | **O**(n*log2n) | **O**(n*log2n) | 稳定 | **O**(n) | +| 基数排序 | **O**(d*(r+n)) | **O**(d*(r+n)) | **O**(d*(r+n)) | 稳定 | **O**(r*d+n) | + + + +#### 各种复杂度效率比较图 + +```java +O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(2^n) < O(n^3) < O(n^n) +``` + +![各种时间复杂度效率比较图](https://i.loli.net/2019/06/11/5cff235ba9b9a93703.jpg) + +**说明:** n 越大,越能体现算法效率。当 n 比较小时,复杂度会有一波小交叉,上图不考虑 n 比较小的情况。 + + + +##### 1. 冒泡排序 ★☆☆☆☆ + +```java +public void bubbleSort(int[] array) { + int temp; + // 外层移动基准元素索引 + for (int i = 0; i < array.length - 1; i++) { + // 内层对基准元素与之后的每个做比较,冒泡排序 + for (int j = i + 1; j < array.length; j++) { + // 大的值,向上冒泡 + if ((temp = array[i]) > array[j]) { + array[i] = array[j]; + array[j] = temp; + } + } + } +} +``` + + + +##### 2. 快速排序 ★★★★★ + +```java +public void quickSort(int[] array, int left, int right) { + if (left < right) { + int i = left; + int j = right; + int temp = array[i]; + + while (i < j) { + while (i < j && array[j] >= temp) { + j--; + } + if (i < j) { + array[i++] = array[j]; + } + + while (i < j && array[i] <= temp) { + i++; + } + if (i < j) { + array[j--] = array[i]; + } + + array[i] = temp; + quickSort(array, left, i - 1); + quickSort(array, i + 1, right); + } + } +} +``` + + + +##### 3. 直接插入排序 ★★★☆☆ + +```java +public void insertionSort(int[] array) { + for (int i = 0; i < array.length; i++) { + int temp = array[i]; + int j; + for (j = i; j > 0 && array[j - 1] > temp; j--) { + array[j] = array[j - 1]; + } + if (array[j] > temp) { + array[j] = temp; + } + } +} +``` + + + +##### 4. 希尔排序 ★★★☆☆ + +```java +public void shellSort(int[] array) { + // 增量 + for (int d = array.length / 2; d > 0; d /= 2) { + // 分组 + for (int x = 0; x < d; x++) { + // 直接插入排序(第 x 组的第2个元素起步) + for (int i = x + d; i < array.length; i += d) { + int temp = array[i]; + int j = i; + for (; j > d && array[j - d] > temp; j -= d) { + array[j] = array[j - d]; + } + if (array[j] > temp) { + array[j] = temp; + } + } + } + } +} +``` + + + +##### 5. 简单选择排序 ★★☆☆☆ + +```java +public void selectionSort(int[] array) { + int minIndex; + int temp; + for (int i = 0; i < array.length - 1; i++) { + minIndex = i; + for (int j = i + 1; j < array.length; j ++) { + if (array[minIndex] > array[j]) { + minIndex = j; + } + } + if (minIndex > i) { + temp = array[i]; + array[i] = array[minIndex]; + array[minIndex] = temp; + } + } +} +``` + + + +##### 6. 堆排序 ★★★★☆ + +```java +public void heapSort(int[] array) { + for (int i = array.length / 2 - 1; i >= 0; i--) { + adjustHeap(array, i, array.length); + } + + for (int i = array.length - 1; i > 0; i--) { + swap(array, 0, i); + adjustHeap(array, 0, i); + } +} + +private void adjustHeap(int[] array, int i, int length) { + int temp = array[i]; + + for (int j = i * 2 + 1; j < length; j = j * 2 + 1) { + if (j + 1 < length && array[j + 1] > array[j]) { + j++; + } + if (array[j] > temp) { + array[i] = array[j]; + i = j; + } else { + break; + } + } + array[i] = temp; +} + +private void swap(int[] array, int a, int b) { + int temp = array[a]; + array[a] = array[b]; + array[b] = temp; +} +``` + + + +##### 7. 归并排序 ★★★★☆ + +```java +public void mergeSort(int[] array) { + int[] aux = new int[array.length]; + sort(array, 0, array.length - 1, aux); +} + +private void sort(int[] array, int left, int right,int[] aux) { + if (left < right) { + int mid = (left + right) / 2; + sort(array, left , mid, aux); + sort(array, mid + 1, right, aux); + merge(array, left, mid, right, aux); + } +} + +private void merge(int[] array, int left, int mid, int right, int[] aux){ + int l = left; + int m = mid + 1; + int t = 0; + + while (l <= mid && m <= right) { + if (array[l] <= array[m]) { + aux[t++] = array[l++]; + } else { + aux[t++] = array[m++]; + } + } + + // 填充剩余元素 + while (l <= mid) { + aux[t++] = array[l++]; + } + while (m <= right) { + aux[t++] = array[m++]; + } + + t = 0; + while (left <= right) { + array[left++] = aux[t++]; + } +} +``` + diff --git a/resource/markdown/algorithm/BinaryTreeTraversal.md b/resource/markdown/algorithm/BinaryTreeTraversal.md new file mode 100644 index 0000000..e73d9e1 --- /dev/null +++ b/resource/markdown/algorithm/BinaryTreeTraversal.md @@ -0,0 +1,120 @@ +### 二叉树的遍历 ★★★★★ + + + +##### 0. TreeNode 结点 + +```java +private static class TreeNode { + T data; + TreeNode left, right; + + TreeNode(T data) { + this.data = data; + } + TreeNode() { + } +} +``` + + + +##### 1. 递归调用 (深度优先遍历) ★☆☆☆☆ + +```java +public void preorderTraversal(TreeNode node) { + if (node == null) { + return; + } + // 可调整前、中、后序遍历 + System.out.print(node.data); + preorderTraversal(node.left); + preorderTraversal(node.right); +} +``` + + + +##### 2. 非递归遍历 - 基于栈 (深度优先遍历) ★★★☆☆ + +```java +public void preorderTraversalWithStack(TreeNode root) { + Stack stack = new Stack<>(); + TreeNode node = root; + while (node != null || !stack.isEmpty()) { + // 访问结点的数据,并且移动到左子结点 + while (node != null) { + System.out.println(node.data); + stack.push(node); + node = node.left; + } + // 回溯,遍历右子结点 + if (!stack.isEmpty()) { + node = stack.pop(); + node = node.right; + } + } +} +``` + + + +##### 3. 非递归遍历 - 基于队列 (层次遍历、广度优先遍历、O(n)) ★★★★☆ + +```java +public void levelorderTraversal(TreeNode root) { + if (root == null) { + return; + } + Queue queue = new LinkedList<>(); + queue.offer(root); + TreeNode node; + // 遍历队列 + while (!queue.isEmpty()) { + // 从队列头部取出一个结点 + node = queue.poll(); + System.out.println(node.data); + // 将左右子结点放入队列尾部 + if (node.left != null) { + queue.offer(node.left); + } + if (node.right != null) { + queue.offer(node.right); + } + } +} +``` + + + +##### 4. Morris Traversal(莫里斯遍历、O(n)) ★★★★☆ + +```java +public void morrisTraversal(TreeNode root) { + TreeNode curr = root; + TreeNode prev; + while (curr != null) { + // 向左子结点遍历 + if (curr.left != null) { + prev = curr.left; + while (prev.right != null && prev.right != curr) { + prev = prev.right; + } + // 右子结点的回溯指针绑定 + if (prev.right == null) { + prev.right = curr; + curr = curr.left; + } else { + System.out.println(curr.data); + prev.right = null; + curr = curr.right; + } + // 向右子结点遍历 + } else { + System.out.print(curr.data); + curr = curr.right; + } + } +} +``` + From 91dab8d412c8600b4e87e0bdf85012728f3ad85b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BB=A3=E7=A0=81=E9=A3=8E=E6=B0=B4=E5=B8=88?= Date: Sun, 1 Sep 2019 00:28:09 +0800 Subject: [PATCH 2/3] =?UTF-8?q?1.=E4=BC=98=E5=8C=96=E4=BA=86=E5=9F=BA?= =?UTF-8?q?=E7=A1=80=E6=8E=92=E5=BA=8F=E7=AE=97=E6=B3=95=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=EF=BC=9B2.=E6=B7=BB=E5=8A=A0=E4=BA=86=E4=BA=8C=E5=8F=89?= =?UTF-8?q?=E6=A0=91=E9=81=8D=E5=8E=86=E3=80=81=E4=BA=8C=E5=8F=89=E6=A0=91?= =?UTF-8?q?=E6=B7=B1=E5=BA=A6=E3=80=81LRU=E7=BC=93=E5=AD=98=E6=9C=BA?= =?UTF-8?q?=E5=88=B6=E7=9A=84=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +- resource/markdown/algorithm/BasicSorting.md | 151 +++++++++++------ .../markdown/algorithm/BinaryTreeDepth.md | 89 ++++++++++ .../markdown/algorithm/BinaryTreeTraversal.md | 130 ++++++++++----- resource/markdown/algorithm/LRUCache.md | 153 ++++++++++++++++++ 5 files changed, 436 insertions(+), 91 deletions(-) create mode 100644 resource/markdown/algorithm/BinaryTreeDepth.md create mode 100644 resource/markdown/algorithm/LRUCache.md diff --git a/README.md b/README.md index cb171ed..bc5223b 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,10 @@ ### 零、:rocket::rocket::rocket:数据结构与算法 -* [数据结构与算法 (第 01 篇) Java版:常见基础排序算法与复杂度](https://github.com/about-cloud/JavaCore/blob/master/resource/markdown/algorithm/BasicSorting.md) +* [数据结构与算法 (第 01 篇) Java版:常见基础排序算法及复杂度](https://github.com/about-cloud/JavaCore/blob/master/resource/markdown/algorithm/BasicSorting.md) * [数据结构与算法 (第 02 篇) Java版:二叉树遍历的n种方法](https://github.com/about-cloud/JavaCore/blob/master/resource/markdown/algorithm/BinaryTreeTraversal.md) +* [数据结构与算法 (第 03 篇) Java版:二叉树深度](https://github.com/about-cloud/JavaCore/blob/master/resource/markdown/algorithm/BinaryTreeDepth.md) +* [数据结构与算法 (第 04 篇) Java版:LRU缓存机制](https://github.com/about-cloud/JavaCore/blob/master/resource/markdown/algorithm/LRUCache.md) ### 一、:bullettrain_side::railway_car::railway_car::railway_car:集合框架源码分析 diff --git a/resource/markdown/algorithm/BasicSorting.md b/resource/markdown/algorithm/BasicSorting.md index ce1fae7..c37e6a2 100644 --- a/resource/markdown/algorithm/BasicSorting.md +++ b/resource/markdown/algorithm/BasicSorting.md @@ -1,10 +1,10 @@ -### 常见基础排序算法 +### 2常见基础排序算法 #### 排序算法分类 -![排序算法分类](https://i.loli.net/2019/06/10/5cfe3bf15a32392750.png) +![排序算法分类](http://ww2.sinaimg.cn/large/006y8mN6ly1g68uopou69j30ko0dzwgb.jpg) #### 时间复杂度 @@ -38,18 +38,23 @@ O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(2^n) < O(n^3) < O(n^n) ```java public void bubbleSort(int[] array) { - int temp; - // 外层移动基准元素索引 - for (int i = 0; i < array.length - 1; i++) { - // 内层对基准元素与之后的每个做比较,冒泡排序 - for (int j = i + 1; j < array.length; j++) { - // 大的值,向上冒泡 - if ((temp = array[i]) > array[j]) { - array[i] = array[j]; - array[j] = temp; - } - } - } + if (array == null) { + return; + } + + int temp; + // 冒泡次数 + for (int i = array.length - 1; i > 0; i--) { + // 冒泡排序 + for (int j = 0; j < i; j++) { + // 将大值交换到后面 + if (array[j] > array[j + 1]) { + temp = array[j]; + array[j] = array[j + 1]; + array[j + 1] = temp; + } + } + } } ``` @@ -57,32 +62,49 @@ public void bubbleSort(int[] array) { ##### 2. 快速排序 ★★★★★ +基本思想:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。 + ```java public void quickSort(int[] array, int left, int right) { + if (array == null) { + return; + } + if (left < right) { int i = left; int j = right; - int temp = array[i]; + int temp = array[i]; // 选取一端值为基准值 while (i < j) { + // 如果 j 处值大于等于基准值,那么不用交换数据,直接将 j 向前移动, + // 直到 i 等于 j 或者 j 处值比基准值小 while (i < j && array[j] >= temp) { j--; } + // 如果 i < j,说明 j 处值比基准值小(根据上面循环判断) if (i < j) { + // 交换 j 与 i 处的值,并将 i 向后移动 array[i++] = array[j]; } + + // 如果 i 处值小于等于基准值,那么将i向后移动就可以了 while (i < j && array[i] <= temp) { i++; } + // 如果 i < j,说明 i 处值比基准值大(根据上面循环判断) if (i < j) { + // 交换 i 与 j 处的值,并将 i 向前移动 array[j--] = array[i]; } + // 最后将临时基准值填充到 i 处 array[i] = temp; - quickSort(array, left, i - 1); - quickSort(array, i + 1, right); + // 对两段各自快速排序 } + + quickSort(array, left, i - 1); + quickSort(array, i + 1, right); } } ``` @@ -93,12 +115,18 @@ public void quickSort(int[] array, int left, int right) { ```java public void insertionSort(int[] array) { + if (array == null) { + return; + } + // 和冒泡排序有些类似,这里是遍历趟数 for (int i = 0; i < array.length; i++) { - int temp = array[i]; + // 精髓是从局部有序,到整体有序 + int temp = array[i]; // 当前基准元素 int j; for (j = i; j > 0 && array[j - 1] > temp; j--) { - array[j] = array[j - 1]; + array[j] = array[j - 1]; // 下一个元素比基准元素大,下一个元素向后移动 } + // 最后比较当前元素和基准元素大小 if (array[j] > temp) { array[j] = temp; } @@ -108,19 +136,22 @@ public void insertionSort(int[] array) { -##### 4. 希尔排序 ★★★☆☆ +##### 4. 希尔排序(缩写增量-直接插入排序) ★★★☆☆ ```java public void shellSort(int[] array) { - // 增量 + if (array == null) { + return; + } + // 计算增量 for (int d = array.length / 2; d > 0; d /= 2) { // 分组 - for (int x = 0; x < d; x++) { - // 直接插入排序(第 x 组的第2个元素起步) - for (int i = x + d; i < array.length; i += d) { + for (int g = 0; g < d; g++) { + // 插入排序(第 x 组的第 d 个增量元素起步)(直接插入排序的增量是 1,这里是 d,需注意下) + for (int i = g + d; i < array.length; i += d) { int temp = array[i]; - int j = i; - for (; j > d && array[j - d] > temp; j -= d) { + int j; + for (j = i; j > d && array[j - d] > temp; j -= d) { array[j] = array[j - d]; } if (array[j] > temp) { @@ -138,19 +169,26 @@ public void shellSort(int[] array) { ```java public void selectionSort(int[] array) { - int minIndex; + if (array == null) { + return; + } + + int index; int temp; - for (int i = 0; i < array.length - 1; i++) { - minIndex = i; - for (int j = i + 1; j < array.length; j ++) { - if (array[minIndex] > array[j]) { - minIndex = j; + // 做出的选择次数 + for (int i = array.length - 1; i > 0; i--) { + index = 0; + for (int j = 1; j < i; j++) { + // 选择一个最大的值(记录索引) + if (array[j] > array[index]) { + index = j; } } - if (minIndex > i) { - temp = array[i]; - array[i] = array[minIndex]; - array[minIndex] = temp; + // 将选出的最大值换到一端 + if (array[index] > array[i]) { + temp = array[index]; + array[index] = array[i]; + array[i] = temp; } } } @@ -162,31 +200,43 @@ public void selectionSort(int[] array) { ```java public void heapSort(int[] array) { + if (array == null) { + return; + } + for (int i = array.length / 2 - 1; i >= 0; i--) { + // 先调整堆(选择一个最大值放到堆顶) adjustHeap(array, i, array.length); } for (int i = array.length - 1; i > 0; i--) { + // 将堆顶的元素与其他元素比较并交换 swap(array, 0, i); + // 再调整堆 adjustHeap(array, 0, i); } } -private void adjustHeap(int[] array, int i, int length) { - int temp = array[i]; +// 调整堆,使得堆顶元素值大于等于其子节点值 +private void adjustHeap(int[] array, int top, int length) { + int temp = array[top]; - for (int j = i * 2 + 1; j < length; j = j * 2 + 1) { - if (j + 1 < length && array[j + 1] > array[j]) { - j++; + for (int i = top * 2 + 1; i < length; i = i * 2 + 1) { + // (如果存在的化)从左右子节点找出值最大的子节点 + if (i + 1 < length && array[i + 1] > array[i]) { + i++; } - if (array[j] > temp) { - array[i] = array[j]; - i = j; + if (array[i] > temp) { + array[top] = array[i]; + top = i; } else { break; } } - array[i] = temp; + + if (array[top] > temp) { + array[top] = temp; + } } private void swap(int[] array, int a, int b) { @@ -202,6 +252,10 @@ private void swap(int[] array, int a, int b) { ```java public void mergeSort(int[] array) { + if (array == null) { + return; + } + int[] aux = new int[array.length]; sort(array, 0, array.length - 1, aux); } @@ -209,6 +263,7 @@ public void mergeSort(int[] array) { private void sort(int[] array, int left, int right,int[] aux) { if (left < right) { int mid = (left + right) / 2; + // 先分后合 sort(array, left , mid, aux); sort(array, mid + 1, right, aux); merge(array, left, mid, right, aux); @@ -216,10 +271,11 @@ private void sort(int[] array, int left, int right,int[] aux) { } private void merge(int[] array, int left, int mid, int right, int[] aux){ + int t = 0; int l = left; int m = mid + 1; - int t = 0; + // 判断元素值大小,按大小排序到辅助数组上 while (l <= mid && m <= right) { if (array[l] <= array[m]) { aux[t++] = array[l++]; @@ -228,7 +284,7 @@ private void merge(int[] array, int left, int mid, int right, int[] aux){ } } - // 填充剩余元素 + // 把剩余元素填充到辅助数组上 while (l <= mid) { aux[t++] = array[l++]; } @@ -236,6 +292,7 @@ private void merge(int[] array, int left, int mid, int right, int[] aux){ aux[t++] = array[m++]; } + // 将辅助线数组上的元素复制到需要排序的数组上 t = 0; while (left <= right) { array[left++] = aux[t++]; diff --git a/resource/markdown/algorithm/BinaryTreeDepth.md b/resource/markdown/algorithm/BinaryTreeDepth.md new file mode 100644 index 0000000..38cef68 --- /dev/null +++ b/resource/markdown/algorithm/BinaryTreeDepth.md @@ -0,0 +1,89 @@ +### 二叉树的最小深度 + + + +> 给定一个二叉树,找出其最小深度。 +> +> 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 + +**说明:** 叶子节点是指没有子节点的节点。 + + + +##### 0. 定义一个二叉树节点 + +```java +public class TreeNode { + int val; + TreeNode left; + TreeNode right; + TreeNode(int x) { + val = x; + } +} +``` + + + +##### 1. 递归 + +```java +public class Solution { + public int minDepth(TreeNode root) { + if (root == null) { + return 0; + } + + // 递归计算 + int leftMinDepth = minDepth(root.left); + int rightMinDepth = minDepth(root.right); + + // 有子节点为空的,则返回另一个节点的深度。否则返回两则最小的深度。 + return leftMinDepth == 0 || rightMinDepth == 0 + ? leftMinDepth + rightMinDepth + 1 + : Math.min(leftMinDepth, rightMinDepth) + 1; + } +} +``` + + + +##### 2. 非递归(宽度优先搜索) + +> 出现第一个无子节点的节点,则该节点的深度为树的最小深度 + +```java +public class Solution { + public int minDepth(TreeNode root) { + if (root == null) { + return 0; + } + + Queue queue = new LinkedList<>(); + queue.offer(root); + + int minDepth = 0; + while (!queue.isEmpty()) { + ++minDepth; + + // 逐层遍历,判断一层是否存在没有子节点的节点,则该节点的深度为树的最小深度 + int size = queue.size(); + for (int i = 0; i < size; i++) { + root = queue.poll(); + if (root.left == null && root.right == null) { + return minDepth; + } + if (root.left != null) { + queue.offer(root.left); + } + if (root.right != null) { + queue.offer(root.right); + } + } + } + + return minDepth; + } +} +``` + diff --git a/resource/markdown/algorithm/BinaryTreeTraversal.md b/resource/markdown/algorithm/BinaryTreeTraversal.md index e73d9e1..d55a2bb 100644 --- a/resource/markdown/algorithm/BinaryTreeTraversal.md +++ b/resource/markdown/algorithm/BinaryTreeTraversal.md @@ -2,18 +2,18 @@ -##### 0. TreeNode 结点 +##### TreeNode 节点 ```java -private static class TreeNode { - T data; - TreeNode left, right; - - TreeNode(T data) { - this.data = data; - } - TreeNode() { - } +/* Definition for a binary tree node. */ +public class TreeNode { + int val; + TreeNode left; + TreeNode right; + + TreeNode(int x) { + val = x; + } } ``` @@ -22,66 +22,106 @@ private static class TreeNode { ##### 1. 递归调用 (深度优先遍历) ★☆☆☆☆ ```java -public void preorderTraversal(TreeNode node) { - if (node == null) { +public void preorderTraversal(TreeNode root) { + if (root == null) { return; } // 可调整前、中、后序遍历 - System.out.print(node.data); - preorderTraversal(node.left); - preorderTraversal(node.right); + System.out.print(root.val); + preorderTraversal(root.left); + preorderTraversal(root.right); } ``` -##### 2. 非递归遍历 - 基于栈 (深度优先遍历) ★★★☆☆ +##### 2.1 非递归遍历 - 基于栈 (前序遍历、深度优先遍历) ★★★☆☆(不推荐) ```java -public void preorderTraversalWithStack(TreeNode root) { +public List preorderTraversalWithStack(TreeNode root) { + + LinkedList output = new LinkedList<>(); + + // 用于回溯的栈 Stack stack = new Stack<>(); - TreeNode node = root; - while (node != null || !stack.isEmpty()) { - // 访问结点的数据,并且移动到左子结点 - while (node != null) { - System.out.println(node.data); - stack.push(node); - node = node.left; + + while (root != null || !stack.isEmpty()) { + // 访问结点的数据,并且移动到左子结点,直到无左子结点 + while (root != null) { + output.add(root.val); + stack.push(root); // 将当前节点 push 到栈中,方便后续回溯 + root = root.left; // 遍历左子结点 } // 回溯,遍历右子结点 if (!stack.isEmpty()) { - node = stack.pop(); - node = node.right; + root = stack.pop().right; } } + + return output; } ``` +##### 2.2非递归遍历 - 基于栈改进版(基于队列的栈模式)(执行时间较上者提升1倍) ★★★★☆ + +```java +public List preorderTraversalWithStack(TreeNode root) { + + LinkedList output = new LinkedList<>(); + + // 用于回溯的栈(基于链表实现,不用担心栈的扩容问题) + LinkedList stack = new LinkedList<>(); + + while (root != null || !stack.isEmpty()) { + // 访问结点的数据,并且移动到左子结点,直到无左子结点 + while (root != null) { + output.add(root.val); + stack.add(root); // 将当前节点 add 到栈中,方便后续回溯 + root = root.left; // 遍历左子结点 + } + // 回溯,遍历右子结点 + if (!stack.isEmpty()) { + root = stack.pollLast().right; + } + } + + return output; +} +``` + + + + + ##### 3. 非递归遍历 - 基于队列 (层次遍历、广度优先遍历、O(n)) ★★★★☆ ```java -public void levelorderTraversal(TreeNode root) { +public List levelorderTraversal(TreeNode root) { + LinkedList output = new LinkedList<>(); if (root == null) { - return; + return output; } + Queue queue = new LinkedList<>(); - queue.offer(root); - TreeNode node; + queue.add(root); + // 遍历队列 while (!queue.isEmpty()) { // 从队列头部取出一个结点 - node = queue.poll(); - System.out.println(node.data); + root = queue.poll(); + output.add(root.val); // 将左右子结点放入队列尾部 - if (node.left != null) { - queue.offer(node.left); + if (root.left != null) { + queue.add(root.left); } - if (node.right != null) { - queue.offer(node.right); + if (root.right != null) { + queue.add(root.right); } } + + return output; } ``` @@ -90,31 +130,35 @@ public void levelorderTraversal(TreeNode root) { ##### 4. Morris Traversal(莫里斯遍历、O(n)) ★★★★☆ ```java -public void morrisTraversal(TreeNode root) { - TreeNode curr = root; +public List morrisTraversal(TreeNode root) { + LinkedList output = new LinkedList<>(); + TreeNode prev; + TreeNode curr = root; while (curr != null) { - // 向左子结点遍历 + // 向左子节点遍历 if (curr.left != null) { prev = curr.left; while (prev.right != null && prev.right != curr) { prev = prev.right; } - // 右子结点的回溯指针绑定 + // 右子节点的回溯指针绑定 if (prev.right == null) { prev.right = curr; curr = curr.left; } else { - System.out.println(curr.data); + output.add(curr.val); prev.right = null; curr = curr.right; } - // 向右子结点遍历 + // 向右子节点遍历 } else { - System.out.print(curr.data); + output.add(curr.val); curr = curr.right; } } + + return output; } ``` diff --git a/resource/markdown/algorithm/LRUCache.md b/resource/markdown/algorithm/LRUCache.md new file mode 100644 index 0000000..91f159c --- /dev/null +++ b/resource/markdown/algorithm/LRUCache.md @@ -0,0 +1,153 @@ +### LRUCache 缓存算法 + +##### 最少最近使用算法 + +```java +public class LRUCache { + /** + * 默认容量 + */ + private int DEFAULT_CAPACITY = 1024; + /** + * 缓存容量 + */ + private int capacity; + /** + * 实际存储/使用的元素大小 + */ + private int size = 0; + /** + * 高效访问的散列表 + */ + private Map> map; + + private Node head, tail; + + /** + * 自定义双向链表中的节点 + */ + private static class Node { + V value; + Node prev; + Node next; + + Node(Node prev, V value, Node next) { + this.value = value; + this.prev = prev; + this.next = next; + } + } + + public LRUCache() { + this.capacity = DEFAULT_CAPACITY; + this.map = new HashMap<>(DEFAULT_CAPACITY, 0.75F); + this.head = null; + this.tail = null; + } + + public LRUCache(int capacity) { + this.capacity = capacity; + this.map = new HashMap<>(capacity, 0.75F); + this.head = null; + this.tail = null; + } + + public V get(K key) { + Node node = this.map.get(key); + if (node != null) { + this.moveToHead(node); + return node.value; + } else { + return null; + } + } + + public V put(K key, V value) { + Node node = this.map.get(key); + if (node != null) { + node.value = value; + moveToHead(node); + return value; + } + + if (size == capacity) { + removeLast(); + map.remove(key); + } + + node = addFirst(value); + map.put(key, node); + + return value; + } + + /** + * 对于新添加的元素,应将新元素添加到链表的头部 + */ + private Node addFirst(V e) { + final Node h = head; + final Node newNode = new Node<>(null, e, h); + head = newNode; + if (h == null) { + tail = newNode; + } else { + h.prev = newNode; + } + size++; + + return newNode; + } + + /** + * 对于被访问的元素,将该元素移动到头部 + */ + private Node moveToHead(Node node) { + final Node prev = node.prev; + final Node next = node.next; + + if (prev == null) { // 如果是首节点,无需移动 + return node; + } + + prev.next = next; + if (next == null) { // 如果是尾节点,需要移动tail + tail = prev; + } else { + next.prev = prev; + } + + node.prev = null; + node.next = head; + head.prev = node; + head = node; + + return node; + } + + /** + * 缓存满时,应删除(淘汰)最后一个节点 + */ + private V removeLast() { + final Node t = tail; + if (t == null) { + return null; + } + + V element = t.value; + t.value = null; // help GC + Node prev = t.prev; + t.prev = null; // help GC + tail = prev; // 移动 tail + if (prev == null) { // 如果尾节点的前一个节点也为空,说明尾节点也是首节点 + head = null; + } else { + prev.next = null; + } + size--; + return element; + } +} +``` + + + From e0335368b7ffce0e30a170c12111d577f21440ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BB=A3=E7=A0=81=E9=A3=8E=E6=B0=B4=E5=B8=88?= Date: Sun, 1 Sep 2019 13:44:46 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BA=86map=E6=97=A0?= =?UTF-8?q?=E6=B3=95=E5=88=A0=E9=99=A4=E5=85=83=E7=B4=A0=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resource/markdown/algorithm/LRUCache.md | 51 ++++++++++++++----------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/resource/markdown/algorithm/LRUCache.md b/resource/markdown/algorithm/LRUCache.md index 91f159c..953911a 100644 --- a/resource/markdown/algorithm/LRUCache.md +++ b/resource/markdown/algorithm/LRUCache.md @@ -7,7 +7,7 @@ public class LRUCache { /** * 默认容量 */ - private int DEFAULT_CAPACITY = 1024; + private static final int DEFAULT_CAPACITY = 1024; /** * 缓存容量 */ @@ -19,19 +19,21 @@ public class LRUCache { /** * 高效访问的散列表 */ - private Map> map; + private Map> map; - private Node head, tail; + private Node head, tail; /** * 自定义双向链表中的节点 */ - private static class Node { + private static class Node { + K key; V value; - Node prev; - Node next; + Node prev; + Node next; - Node(Node prev, V value, Node next) { + Node(Node prev, K key, V value, Node next) { + this.key = key; this.value = value; this.prev = prev; this.next = next; @@ -46,6 +48,10 @@ public class LRUCache { } public LRUCache(int capacity) { + if (capacity <= 0) { + throw new IllegalArgumentException("Capacity must be positive integer"); + } + this.capacity = capacity; this.map = new HashMap<>(capacity, 0.75F); this.head = null; @@ -53,7 +59,7 @@ public class LRUCache { } public V get(K key) { - Node node = this.map.get(key); + Node node = this.map.get(key); if (node != null) { this.moveToHead(node); return node.value; @@ -63,7 +69,7 @@ public class LRUCache { } public V put(K key, V value) { - Node node = this.map.get(key); + Node node = this.map.get(key); if (node != null) { node.value = value; moveToHead(node); @@ -71,11 +77,11 @@ public class LRUCache { } if (size == capacity) { - removeLast(); - map.remove(key); + node = removeLast(); + map.remove(node.key); } - node = addFirst(value); + node = addFirst(key, value); map.put(key, node); return value; @@ -84,9 +90,9 @@ public class LRUCache { /** * 对于新添加的元素,应将新元素添加到链表的头部 */ - private Node addFirst(V e) { - final Node h = head; - final Node newNode = new Node<>(null, e, h); + private Node addFirst(K key, V value) { + final Node h = head; + final Node newNode = new Node<>(null, key, value, h); head = newNode; if (h == null) { tail = newNode; @@ -101,9 +107,9 @@ public class LRUCache { /** * 对于被访问的元素,将该元素移动到头部 */ - private Node moveToHead(Node node) { - final Node prev = node.prev; - final Node next = node.next; + private Node moveToHead(Node node) { + final Node prev = node.prev; + final Node next = node.next; if (prev == null) { // 如果是首节点,无需移动 return node; @@ -127,15 +133,14 @@ public class LRUCache { /** * 缓存满时,应删除(淘汰)最后一个节点 */ - private V removeLast() { - final Node t = tail; + private Node removeLast() { + final Node t = tail; if (t == null) { return null; } - V element = t.value; t.value = null; // help GC - Node prev = t.prev; + Node prev = t.prev; t.prev = null; // help GC tail = prev; // 移动 tail if (prev == null) { // 如果尾节点的前一个节点也为空,说明尾节点也是首节点 @@ -144,7 +149,7 @@ public class LRUCache { prev.next = null; } size--; - return element; + return t; } } ```