diff --git a/README.md b/README.md index 365fc6e..af71637 100644 --- a/README.md +++ b/README.md @@ -18,15 +18,15 @@ build - + code style

-

ALGORITHM-TUTORIAL

+

ALGORITHM

-> 💾 algorithm-tutorial 是一个数据结构与算法教程。 +> 💾 algorithm 是一个数据结构与算法学习笔记。 > > 掌握数据结构与算法,你看待问题的深度,解决问题的角度就会完全不一样。 > @@ -58,143 +58,540 @@ ## 💻 刷题 -### 数组 +### 链表 -- [三数之和](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/三数之和.java) -- [两数之和](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/两数之和.java) -- [二维数组](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/二维数组.java) -- [删除排序数组中的重复项](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/删除排序数组中的重复项.java) -- [加一](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/加一.java) -- [在排序数组中查找元素的第一个和最后一个位置](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/在排序数组中查找元素的第一个和最后一个位置.java) -- [在排序数组中查找数字 I](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/在排序数组中查找数字I.java) -- [存在重复元素](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/存在重复元素.java) -- [对角线遍历](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/对角线遍历.java) -- [寻找数组的中心索引](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/寻找数组的中心索引.java) -- [将数组分成和相等的三个部分](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/将数组分成和相等的三个部分.java) -- [数组二分查找](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/数组二分查找.java) -- [数组拆分 1](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/数组拆分1.java) -- [旋转数组](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/旋转数组.java) -- [旋转矩阵](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/旋转矩阵.java) -- [最大连续 1 的个数](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/最大连续1的个数.java) -- [杨辉三角](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/杨辉三角.java) -- [杨辉三角 2](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/杨辉三角2.java) -- [模拟 ArrayList1](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/模拟ArrayList1.java) -- [模拟 ArrayList2](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/模拟ArrayList2.java) -- [移动零](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/移动零.java) -- [移除元素](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/移除元素.java) -- [至少是其他数字两倍的最大数](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/至少是其他数字两倍的最大数.java) -- [螺旋矩阵](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/螺旋矩阵.java) -- [长度最小的子数组](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/长度最小的子数组.java) -- [零矩阵](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/零矩阵.java) +#### 基础操作 + +| 题目 | 难度 | 掌握度 | +| --------------------------------------------------------------------------------------------------------- | ---- | ------ | +| [1290. 二进制链表转整数](https://leetcode.cn/problems/convert-binary-number-in-a-linked-list-to-integer/) | 💚 | ✔️ | + +#### 双指针技巧 + +| 题目 | 难度 | 掌握度 | +| ------------------------------------------------------------------------------------------------------ | ---- | ------ | +| [141. 环形链表](https://leetcode.cn/problems/linked-list-cycle/) | 💚 | ✔️ | +| [142. 环形链表 II](https://leetcode.cn/problems/linked-list-cycle-ii/) | 💛 | ✔️ | +| [160. 相交链表](https://leetcode.cn/problems/intersection-of-two-linked-lists/) | 💚 | ✔️ | +| [19. 删除链表的倒数第 N 个结点](https://leetcode.cn/problems/remove-nth-node-from-end-of-list/) | 💛 | ✔️ | +| [21. 合并两个有序链表](https://leetcode.cn/problems/merge-two-sorted-lists/) | 💚 | ✔️ | +| [23. 合并 K 个升序链表](https://leetcode.cn/problems/merge-k-sorted-lists/) | ❤️ | ✔️ | +| [86. 分隔链表](https://leetcode.cn/problems/partition-list/) | 💛 | ✔️ | +| [876. 链表的中间结点](https://leetcode.cn/problems/middle-of-the-linked-list/) | 💚 | ✔️ | +| [面试题 02. 返回倒数第 k 个节点](https://leetcode.cn/problems/kth-node-from-end-of-list-lcci/) | 💚 | ✔️ | +| [面试题 02.01. 移除重复节点](https://leetcode.cn/problems/remove-duplicate-node-lcci/) | 💚 | ✔️ | +| [203. 移除链表元素](https://leetcode.cn/problems/remove-linked-list-elements/) | 💚 | ✔️ | +| [328. 奇偶链表](https://leetcode.cn/problems/odd-even-linked-list/) | 💛 | ✔️ | +| [LCR 136. 删除链表的节点](https://leetcode.cn/problems/shan-chu-lian-biao-de-jie-dian-lcof/) | 💚 | ✔️ | +| [83. 删除排序链表中的重复元素](https://leetcode.cn/problems/remove-duplicates-from-sorted-list/) | 💚 | ✔️ | +| [82. 删除排序链表中的重复元素 II](https://leetcode.cn/problems/remove-duplicates-from-sorted-list-ii/) | 💛 | ✔️ | +| [2. 两数相加](https://leetcode.cn/problems/add-two-numbers/) | 💛 | ✔️ | +| [445. 两数相加 II](https://leetcode.cn/problems/add-two-numbers-ii/) | 💛 | ✔️ | + +#### 单链表反转 + +| 题目 | 难度 | 掌握度 | +| ------------------------------------------------------------------------------ | ---- | ------ | +| [61. 旋转链表](https://leetcode.cn/problems/rotate-list/) | 💛 | ✔️ | +| [206. 反转链表](https://leetcode.cn/problems/reverse-linked-list/) | 💚 | ✔️ | +| [92. 反转链表 II](https://leetcode.cn/problems/reverse-linked-list-ii/) | 💛 | ✔️ | +| [25. K 个一组翻转链表](https://leetcode.cn/problems/reverse-nodes-in-k-group/) | ❤️ | ✔️ | + +#### 分治 + +| 题目 | 难度 | 掌握度 | +| -------------------------------------------------------- | ---- | ------ | +| [148. 排序链表](https://leetcode.cn/problems/sort-list/) | 💛 | ❌ | + +#### 回文链表 + +| 题目 | 难度 | 掌握度 | +| --------------------------------------------------------------------- | ---- | ------ | +| [234. 回文链表](https://leetcode.cn/problems/palindrome-linked-list/) | 💚 | ✔️ | -### 链表 +### 数组 -- [两数相加](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/两数相加.java) -- [二进制链表转整数](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/二进制链表转整数.java) -- [删除排序链表中的重复元素](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/删除排序链表中的重复元素.java) -- [单链表示例](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/单链表示例.java) -- [双链表示例](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/双链表示例.java) -- [反转链表](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/反转链表.java) -- [合并 K 个排序链表](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/合并K个排序链表.java) -- [合并 K 个排序链表解法 2](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/合并K个排序链表解法2.java) -- [合并两个有序链表](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/合并两个有序链表.java) -- [回文链表](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/回文链表.java) -- [排序链表](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/排序链表.java) -- [环形链表](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/环形链表.java) -- [相交链表](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/相交链表.java) -- [移除重复节点](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/移除重复节点.java) -- [移除链表元素](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/移除链表元素.java) -- [返回倒数第 k 个节点](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/返回倒数第k个节点.java) -- [链表的中间结点](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/链表的中间结点.java) - -### 栈 - -- [三合一](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/三合一.java) -- [基本计算器](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/基本计算器.java) -- [最小栈](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/最小栈.java) -- [最小栈 2](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/最小栈2.java) -- [有效的括号](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/有效的括号.java) -- [栈排序](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/栈排序.java) -- [棒球比赛](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/棒球比赛.java) -- [比较含退格的字符串](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/比较含退格的字符串.java) -- [用栈实现队列](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/用栈实现队列.java) -- [用队列实现栈](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/用队列实现栈.java) - -### 队列 - -- [动态扩容数组实现的队列](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/动态扩容数组实现的队列.java) -- [数组实现的队列](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/数组实现的队列.java) -- [最近的请求次数](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/最近的请求次数.java) -- [设计循环队列](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/设计循环队列.java) -- [链表实现的队列](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/链表实现的队列.java) - -### 字符串 - -- [二进制求和](https://github.com/dunwu/algorithm/blob/master/codes/data-structure/src/main/java/io/github/dunwu/ds/str/AddBinary.java) -- [实现 strStr()](https://github.com/dunwu/algorithm/blob/master/codes/data-structure/src/main/java/io/github/dunwu/ds/str/ImplementStrstr.java) -- [最长公共前缀](https://github.com/dunwu/algorithm/blob/master/codes/data-structure/src/main/java/io/github/dunwu/ds/str/LongestCommonPrefix.java) -- [反转字符串](https://github.com/dunwu/algorithm/blob/master/codes/data-structure/src/main/java/io/github/dunwu/ds/str/ReverseString.java) -- [反转字符串中的单词](https://github.com/dunwu/algorithm/blob/master/codes/data-structure/src/main/java/io/github/dunwu/ds/str/ReverseWordsInAString.java) -- [反转字符串中的单词 III](https://github.com/dunwu/algorithm/blob/master/codes/data-structure/src/main/java/io/github/dunwu/ds/str/ReverseWordsInAString3.java) +#### 基础 + +| 题目 | 难度 | 掌握度 | +| -------------------------------------------------------------------------------------------------------- | ---- | ------ | +| [485. 最大连续 1 的个数](https://leetcode.cn/problems/max-consecutive-ones/) | 💚 | ✔️ | +| [747. 至少是其他数字两倍的最大数](https://leetcode.cn/problems/largest-number-at-least-twice-of-others/) | 💚 | ✔️ | + +#### 双指针技巧 + +| 题目 | 难度 | 掌握度 | +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---- | ------ | +| [27. 移除元素](https://leetcode.cn/problems/remove-element/) | 💚 | ✔️ | +| [283. 移动零](https://leetcode.cn/problems/move-zeroes/) | 💚 | ✔️ | +| [LCR 179. 查找总价格为目标值的两个商品](https://leetcode.cn/problems/he-wei-sde-liang-ge-shu-zi-lcof/) | 💚 | ✔️ | +| [1. 两数之和](https://leetcode.cn/problems/two-sum/) | 💚 | ✔️ | +| [67. 二进制求和](https://leetcode.cn/problems/add-binary/) | 💚 | ✔️ | +| [167. 两数之和 II - 输入有序数组](https://leetcode.cn/problems/two-sum-ii-input-array-is-sorted/)
[LCR 006. 两数之和 II - 输入有序数组](https://leetcode.cn/problems/kLl5u1/) | 💛 | ✔️ | +| [26. 删除有序数组中的重复项](https://leetcode.cn/problems/remove-duplicates-from-sorted-array/) | 💚 | ✔️ | +| [80. 删除有序数组中的重复项 II](https://leetcode.cn/problems/remove-duplicates-from-sorted-array-ii/) | 💛 | ✔️ | +| [344. 反转字符串](https://leetcode.cn/problems/reverse-string/) | 💚 | ✔️ | +| [125. 验证回文串](https://leetcode.cn/problems/valid-palindrome/) | 💚 | ✔️ | +| [5. 最长回文子串](https://leetcode.cn/problems/longest-palindromic-substring/) | 💛 | ✔️ | +| [75. 颜色分类](https://leetcode.cn/problems/sort-colors/) | 💛 | ✔️ | +| [88. 合并两个有序数组](https://leetcode.cn/problems/merge-sorted-array/) | 💚 | ✔️ | +| [977. 有序数组的平方](https://leetcode.cn/problems/squares-of-a-sorted-array/) | 💚 | ✔️ | +| [1329. 将矩阵按对角线排序](https://leetcode.cn/problems/sort-the-matrix-diagonally/) | 💛 | ✔️ | +| [1260. 二维网格迁移](https://leetcode.cn/problems/shift-2d-grid/) | 💚 | ✔️ | +| [867. 转置矩阵](https://leetcode.cn/problems/transpose-matrix/) | 💚 | ✔️ | +| [14. 最长公共前缀](https://leetcode.cn/problems/longest-common-prefix/) | 💚 | ✔️ | +| [15. 三数之和](https://leetcode.cn/problems/3sum/) | 💛 | ❗ | +| [56. 合并区间](https://leetcode.cn/problems/merge-intervals/) | 💛 | ✔️ | + +#### 二维数组遍历 + +| 题目 | 难度 | 掌握度 | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---- | ------ | +| [151. 反转字符串中的单词](https://leetcode.cn/problems/reverse-words-in-a-string/) | 💛 | ✔️ | +| [48. 旋转图像](https://leetcode.cn/problems/rotate-image/) | 💛 | ✔️ | +| [54. 螺旋矩阵](https://leetcode.cn/problems/spiral-matrix/)
[LCR 146. 螺旋遍历二维数组](https://leetcode.cn/problems/shun-shi-zhen-da-yin-ju-zhen-lcof/) | 💛 | ✔️ | +| [59. 螺旋矩阵 II](https://leetcode.cn/problems/spiral-matrix-ii/) | 💛 | ✔️ | +| [498. 对角线遍历](https://leetcode.cn/problems/diagonal-traverse/) | 💛 | ❌ | +| [面试题 01.08. 零矩阵](https://leetcode.cn/problems/zero-matrix-lcci/) | 💛 | ✔️ | + +#### 滑动窗口算法 + +| 题目 | 难度 | 掌握度 | +| -------------------------------------------------------------------------------------------------------------------------- | ---- | ------ | +| [3. 无重复字符的最长子串](https://leetcode.cn/problems/longest-substring-without-repeating-characters/) | 💛 | ✔️ | +| [438. 找到字符串中所有字母异位词](https://leetcode.cn/problems/find-all-anagrams-in-a-string/) | 💛 | ✔️ | +| [567. 字符串的排列](https://leetcode.cn/problems/permutation-in-string/) | 💛 | ✔️ | +| [76. 最小覆盖子串](https://leetcode.cn/problems/minimum-window-substring/) | ❤️ | ✔️ | +| [1658. 将 x 减到 0 的最小操作数](https://leetcode.cn/problems/minimum-operations-to-reduce-x-to-zero/) | 💛 | ✔️ | +| [713. 乘积小于 K 的子数组](https://leetcode.cn/problems/subarray-product-less-than-k/) | 💛 | ✔️ | +| [1004. 最大连续 1 的个数 III](https://leetcode.cn/problems/max-consecutive-ones-iii/) | 💛 | ✔️ | +| [424. 替换后的最长重复字符](https://leetcode.cn/problems/longest-repeating-character-replacement/) | 💛 | ✔️ | +| [217. 存在重复元素](https://leetcode.cn/problems/contains-duplicate/) | 💚 | ✔️ | +| [219. 存在重复元素 II](https://leetcode.cn/problems/contains-duplicate-ii/) | 💛 | ✔️ | +| [220. 存在重复元素 III](https://leetcode.cn/problems/contains-duplicate-iii/) | ❤️ | ❌ | +| [209. 长度最小的子数组](https://leetcode.cn/problems/minimum-size-subarray-sum/) | 💛 | ✔️ | +| [395. 至少有 K 个重复字符的最长子串](https://leetcode.cn/problems/longest-substring-with-at-least-k-repeating-characters/) | 💛 | ❌ | + +#### 二分查找算法 + +| 题目 | 难度 | 掌握度 | +| :-------------------------------------------------------------------------------------------------------------------------------------- | :--- | ------ | +| [34. 在排序数组中查找元素的第一个和最后一个位置](https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/) | 💛 | ✔️ | +| [35. 搜索插入位置](https://leetcode.cn/problems/search-insert-position/) | 💚 | ✔️ | +| [704. 二分查找](https://leetcode.cn/problems/binary-search/) | 💚 | ✔️ | +| [LCR 172. 统计目标成绩的出现次数](https://leetcode.cn/problems/zai-pai-xu-shu-zu-zhong-cha-zhao-shu-zi-lcof/) | 💚 | ✔️ | +| [875. 爱吃香蕉的珂珂](https://leetcode.cn/problems/koko-eating-bananas/) | 💛 | ❗ | +| [1011. 在 D 天内送达包裹的能力](https://leetcode.cn/problems/capacity-to-ship-packages-within-d-days/) | 💛 | ✔️ | +| [410. 分割数组的最大值](https://leetcode.cn/problems/split-array-largest-sum/) | ❤️ | ❌ | + +#### 前缀和数组 + +| 题目 | 难度 | 掌握度 | +| ----------------------------------------------------------------------------------------------------------------- | ---- | ------ | +| [303. 区域和检索 - 数组不可变](https://leetcode.cn/problems/range-sum-query-immutable/) | 💚 | ✔️ | +| [724. 寻找数组的中心下标](https://leetcode.cn/problems/find-pivot-index/) | 💚 | ✔️ | +| [1013. 将数组分成和相等的三个部分](https://leetcode.cn/problems/partition-array-into-three-parts-with-equal-sum/) | 💚 | ✔️ | +| [304. 二维区域和检索 - 矩阵不可变](https://leetcode.cn/problems/range-sum-query-2d-immutable/) | 💛 | ❌ | + +#### 差分数组 + +| 题目 | 难度 | 掌握度 | +| ----------------------------------------------------------------------------- | ---- | ------ | +| [1094. 拼车](https://leetcode.cn/problems/car-pooling/) | 💛 | ✔️ | +| [1109. 航班预订统计](https://leetcode.cn/problems/corporate-flight-bookings/) | 💛 | ✔️ | + +### 栈和队列 + +#### 队列 + +| 题目 | 难度 | 掌握度 | +| ----------------------------------------------------------------------------------------------------- | ---- | ------ | +| [225. 用队列实现栈](https://leetcode.cn/problems/implement-stack-using-queues/) | 💚 | ✔️ | +| [933. 最近的请求次数](https://leetcode.cn/problems/number-of-recent-calls/) | 💚 | ❗ | +| [622. 设计循环队列](https://leetcode.cn/problems/design-circular-queue/) | 💛 | ❌ | +| [641. 设计循环双端队列](https://leetcode.cn/problems/design-circular-deque/) | 💛 | ❌ | +| [1670. 设计前中后队列](https://leetcode.cn/problems/design-front-middle-back-queue/) | 💛 | ❌ | +| [2073. 买票需要的时间](https://leetcode.cn/problems/time-needed-to-buy-tickets/) | 💚 | ✔️ | +| [373. 查找和最小的 K 对数字](https://leetcode.cn/problems/find-k-pairs-with-smallest-sums/) | 💛 | ❌ | +| [378. 有序矩阵中第 K 小的元素](https://leetcode.cn/problems/kth-smallest-element-in-a-sorted-matrix/) | 💛 | ❌ | + +#### 栈 + +| 题目 | 难度 | 掌握度 | +| --------------------------------------------------------------------------------------- | ---- | ------ | +| [20. 有效的括号](https://leetcode.cn/problems/valid-parentheses/) | 💚 | ✔️ | +| [232. 用栈实现队列](https://leetcode.cn/problems/implement-queue-using-stacks/) | 💚 | ✔️ | +| [682. 棒球比赛](https://leetcode.cn/problems/baseball-game/) | 💚 | ✔️ | +| [844. 比较含退格的字符串](https://leetcode.cn/problems/backspace-string-compare/) | 💚 | ✔️ | +| [71. 简化路径](https://leetcode.cn/problems/simplify-path/) | 💛 | ✔️ | +| [143. 重排链表](https://leetcode.cn/problems/reorder-list/) | 💛 | ✔️ | +| [150. 逆波兰表达式求值](https://leetcode.cn/problems/evaluate-reverse-polish-notation/) | 💛 | ✔️ | +| [388. 文件的最长绝对路径](https://leetcode.cn/problems/longest-absolute-file-path/) | 💛 | ❌ | +| [155. 最小栈](https://leetcode.cn/problems/min-stack/) | 💛 | ✔️ | +| [面试题 03.05. 栈排序](https://leetcode.cn/problems/sort-of-stacks-lcci/) | 💛 | ✔️ | +| [895. 最大频率栈](https://leetcode.cn/problems/maximum-frequency-stack/) | ❤️ | ❌ | + +#### 单调栈 + +| 题目 | 难度 | 掌握度 | +| ----------------------------------------------------------------------------------------------------------------------------------------- | ---- | ------ | +| [496. 下一个更大元素 I](https://leetcode.cn/problems/next-greater-element-i/) | 💚 | ✔️ | +| [503. 下一个更大元素 II](https://leetcode.cn/problems/next-greater-element-ii/) | 💛 | ✔️ | +| [739. 每日温度](https://leetcode.cn/problems/daily-temperatures/)
[剑指 Offer II 038. 每日温度](https://leetcode.cn/problems/iIQa4I/) | 💛 | ✔️ | +| [1019. 链表中的下一个更大节点](https://leetcode.cn/problems/next-greater-node-in-linked-list/) | 💛 | ✔️ | +| [1944. 队列中可以看到的人数](https://leetcode.cn/problems/number-of-visible-people-in-a-queue/) | ❤️ | ❌ | +| [1475. 商品折扣后的最终价格](https://leetcode.cn/problems/final-prices-with-a-special-discount-in-a-shop/) | 💛 | ✔️ | +| [901. 股票价格跨度](https://leetcode.cn/problems/online-stock-span/) | 💛 | ❌ | +| [402. 移掉 K 位数字](https://leetcode.cn/problems/remove-k-digits/) | 💛 | ❌ | +| [853. 车队](https://leetcode.cn/problems/car-fleet/) | 💛 | ❌ | +| [581. 最短无序连续子数组](https://leetcode.cn/problems/shortest-unsorted-continuous-subarray/) | 💛 | ❌ | + +#### 单调队列 + +| 题目 | 难度 | 掌握度 | +| -------------------------------------------------------------------------------------------------------------------------------------------------- | ---- | ------ | +| [LCR 184. 设计自助结算系统](https://leetcode.cn/problems/dui-lie-de-zui-da-zhi-lcof/) | 💛 | ❌ | +| [239. 滑动窗口最大值](https://leetcode.cn/problems/sliding-window-maximum/) | ❤️ | ❌ | +| [1438. 绝对差不超过限制的最长连续子数组](https://leetcode.cn/problems/longest-continuous-subarray-with-absolute-diff-less-than-or-equal-to-limit/) | 💛 | ❌ | +| [862. 和至少为 K 的最短子数组](https://leetcode.cn/problems/shortest-subarray-with-sum-at-least-k/) | ❤️ | ❌ | +| [918. 环形子数组的最大和](https://labuladong.online/algo/problem-set/monotonic-queue/#slug_maximum-sum-circular-subarray) | 💛 | ❌ | ### 树 -- [N 叉树的最大深度](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/N叉树的最大深度.java) - #### 二叉树 -- [二叉树中的最大路径和](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/二叉树中的最大路径和.java) -- [二叉树的中序遍历](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/二叉树的中序遍历.java) -- [二叉树的前序遍历](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/二叉树的前序遍历.java) -- [二叉树的后序遍历](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/二叉树的后序遍历.java) -- [二叉树的层次遍历](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/二叉树的层次遍历.java) -- [二叉树的层次遍历 2](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/二叉树的层次遍历2.java) -- [二叉树的序列化与反序列化](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/二叉树的序列化与反序列化.java) -- [二叉树的所有路径](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/二叉树的所有路径.java) -- [二叉树的最大深度](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/二叉树的最大深度.java) -- [二叉树的最小深度](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/二叉树的最小深度.java) -- [二叉树的最近公共祖先](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/二叉树的最近公共祖先.java) -- [二叉树的锯齿形层次遍历](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/二叉树的锯齿形层次遍历.java) -- [从先序遍历还原二叉树](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/从先序遍历还原二叉树.java) -- [叶子相似的树](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/叶子相似的树.java) -- [填充每个节点的下一个右侧节点指针](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/填充每个节点的下一个右侧节点指针.java) -- [填充每个节点的下一个右侧节点指针 II](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/填充每个节点的下一个右侧节点指针II.java) -- [对称二叉树](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/对称二叉树.java) -- [平衡二叉树](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/平衡二叉树.java) -- [相同的树](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/相同的树.java) -- [翻转二叉树](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/翻转二叉树.java) -- [路径总和](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/路径总和.java) +| 题目 | 难度 | 掌握度 | +| ---------------------------------------------------------------------------------------------------- | ---- | ------ | +| [104. 二叉树的最大深度](https://leetcode.cn/problems/maximum-depth-of-binary-tree/) | 💚 | ✔️ | +| [111. 二叉树的最小深度](https://leetcode.cn/problems/minimum-depth-of-binary-tree/) | 💚 | ✔️ | +| [543. 二叉树的直径](https://leetcode.cn/problems/diameter-of-binary-tree/) | 💚 | ✔️ | +| [114. 二叉树展开为链表](https://leetcode.cn/problems/flatten-binary-tree-to-linked-list/) | 💛 | ✔️ | +| [226. 翻转二叉树](https://leetcode.cn/problems/invert-binary-tree/) | 💚 | ✔️ | +| [654. 最大二叉树](https://leetcode.cn/problems/maximum-binary-tree/) | 💛 | ✔️ | +| [297. 二叉树的序列化与反序列化](https://leetcode.cn/problems/serialize-and-deserialize-binary-tree/) | ❤️ | ❗ | +| [222. 完全二叉树的节点个数](https://leetcode.cn/problems/count-complete-tree-nodes/) | 💚 | ✔️ | + +#### DFS + +| 题目 | 难度 | 掌握度 | +| -------------------------------------------------------------------------------------- | ---- | ------ | +| [144. 二叉树的前序遍历](https://leetcode.cn/problems/binary-tree-preorder-traversal/) | 💚 | ✔️ | +| [94. 二叉树的中序遍历](https://leetcode.cn/problems/binary-tree-inorder-traversal/) | 💚 | ✔️ | +| [145. 二叉树的后序遍历](https://leetcode.cn/problems/binary-tree-postorder-traversal/) | 💚 | ✔️ | +| [872. 叶子相似的树](https://leetcode.cn/problems/leaf-similar-trees/) | 💚 | ✔️ | + +#### 用「遍历」思维解题 + +| 题目 | 难度 | 掌握度 | +| ----------------------------------------------------------------------------------------------------- | ---- | ------ | +| [257. 二叉树的所有路径](https://leetcode.cn/problems/binary-tree-paths/) | 💚 | ✔️ | +| [129. 求根节点到叶节点数字之和](https://leetcode.cn/problems/sum-root-to-leaf-numbers/) | 💛 | ✔️ | +| [199. 二叉树的右视图](https://leetcode.cn/problems/binary-tree-right-side-view/) | 💛 | ✔️ | +| [988. 从叶结点开始的最小字符串](https://leetcode.cn/problems/smallest-string-starting-from-leaf/) | 💛 | ✔️ | +| [1022. 从根到叶的二进制数之和](https://leetcode.cn/problems/sum-of-root-to-leaf-binary-numbers/) | 💚 | ✔️ | +| [1457. 二叉树中的伪回文路径](https://leetcode.cn/problems/pseudo-palindromic-paths-in-a-binary-tree/) | 💛 | ✔️ | +| [404. 左叶子之和](https://leetcode.cn/problems/sum-of-left-leaves/) | 💚 | ✔️ | +| [623. 在二叉树中增加一行](https://leetcode.cn/problems/add-one-row-to-tree/) | 💛 | ✔️ | +| [508. 出现次数最多的子树元素和](https://leetcode.cn/problems/most-frequent-subtree-sum/) | 💛 | ✔️ | +| [563. 二叉树的坡度](https://leetcode.cn/problems/binary-tree-tilt/) | 💚 | ✔️ | +| [814. 二叉树剪枝](https://leetcode.cn/problems/binary-tree-pruning/) | 💛 | ✔️ | +| [1325. 删除给定值的叶子节点](https://leetcode.cn/problems/delete-leaves-with-a-given-value/) | 💛 | ✔️ | + +#### 用「分解」思维解题 + +| 题目 | 难度 | 掌握度 | +| ------------------------------------------------------------------------------------------------------------------------------- | ---- | ------ | +| [105. 从前序与中序遍历序列构造二叉树](https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal/) | 💛 | ✔️ | +| [106. 从中序与后序遍历序列构造二叉树](https://leetcode.cn/problems/construct-binary-tree-from-inorder-and-postorder-traversal/) | 💛 | ✔️ | +| [889. 根据前序和后序遍历构造二叉树](https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-postorder-traversal/) | 💛 | ✔️ | +| [331. 验证二叉树的前序序列化](https://leetcode.cn/problems/verify-preorder-serialization-of-a-binary-tree/) | 💛 | ❌ | +| [894. 所有可能的真二叉树](https://leetcode.cn/problems/all-possible-full-binary-trees/) | 💛 | ❌ | +| [998. 最大二叉树 II](https://leetcode.cn/problems/maximum-binary-tree-ii/) | 💛 | ❌ | +| [1110. 删点成林](https://leetcode.cn/problems/delete-nodes-and-return-forest/) | 💛 | ❌ | +| [100. 相同的树](https://leetcode.cn/problems/same-tree/) | 💛 | ✔️ | +| [101. 对称二叉树](https://leetcode.cn/problems/symmetric-tree/) | 💛 | ✔️ | +| [951. 翻转等价二叉树](https://leetcode.cn/problems/flip-equivalent-binary-trees/) | 💛 | ✔️ | +| [124. 二叉树中的最大路径和](https://leetcode.cn/problems/binary-tree-maximum-path-sum/) | ❤️ | ❌ | +| [236. 二叉树的最近公共祖先](https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/) | 💛 | ❌ | + +#### 用「层序遍历」思维解题 + +| 题目 | 难度 | 掌握度 | +| ------------------------------------------------------------------------------------------------------------------------ | ---- | ------ | +| [102. 二叉树的层序遍历](https://leetcode.cn/problems/binary-tree-level-order-traversal/) | 💛 | ✔️ | +| [107. 二叉树的层序遍历 II](https://leetcode.cn/problems/binary-tree-level-order-traversal-ii/) | 💛 | ✔️ | +| [103. 二叉树的锯齿形层序遍历](https://leetcode.cn/problems/binary-tree-zigzag-level-order-traversal/) | 💛 | ✔️ | +| [116. 填充每个节点的下一个右侧节点指针](https://leetcode.cn/problems/populating-next-right-pointers-in-each-node/) | 💛 | ✔️ | +| [117. 填充每个节点的下一个右侧节点指针 II](https://leetcode.cn/problems/populating-next-right-pointers-in-each-node-ii/) | 💛 | ✔️ | +| [662. 二叉树最大宽度](https://leetcode.cn/problems/maximum-width-of-binary-tree/) | 💛 | ✔️ | +| [515. 在每个树行中找最大值](https://leetcode.cn/problems/find-largest-value-in-each-tree-row/) | 💛 | ✔️ | +| [637. 二叉树的层平均值](https://leetcode.cn/problems/average-of-levels-in-binary-tree/) | 💚 | ✔️ | +| [958. 二叉树的完全性检验](https://leetcode.cn/problems/check-completeness-of-a-binary-tree/) | 💛 | ✔️ | +| [1161. 最大层内元素和](https://leetcode.cn/problems/maximum-level-sum-of-a-binary-tree/) | 💛 | ✔️ | +| [1302. 层数最深叶子节点的和](https://leetcode.cn/problems/deepest-leaves-sum/) | 💛 | ✔️ | +| [1609. 奇偶树](https://leetcode.cn/problems/even-odd-tree/) | 💛 | ✔️ | +| [919. 完全二叉树插入器](https://leetcode.cn/problems/complete-binary-tree-inserter/) | 💛 | ✔️ | +| [863. 二叉树中所有距离为 K 的结点](https://leetcode.cn/problems/all-nodes-distance-k-in-binary-tree/) | 💛 | ❌ | +| [LCR 149. 彩灯装饰记录 I](https://leetcode.cn/problems/cong-shang-dao-xia-da-yin-er-cha-shu-lcof/) | 💛 | ✔️ | +| [LCR 150. 彩灯装饰记录 II](https://leetcode.cn/problems/cong-shang-dao-xia-da-yin-er-cha-shu-ii-lcof/) | 💚 | ✔️ | +| [LCR 151. 彩灯装饰记录 III](https://leetcode.cn/problems/cong-shang-dao-xia-da-yin-er-cha-shu-iii-lcof/) | 💛 | ✔️ | #### 二叉搜索树 -- [二叉搜索树中的插入操作](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/二叉搜索树中的插入操作.java) -- [二叉搜索树的最近公共祖先](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/二叉搜索树的最近公共祖先.java) -- [二叉搜索树节点最小距离](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/二叉搜索树节点最小距离.java) -- [将有序数组转换为二叉搜索树](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/将有序数组转换为二叉搜索树.java) -- [验证二叉搜索树](https://github.com/dunwu/algorithm-tutorial/blob/master/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/验证二叉搜索树.java) +| 题目 | 难度 | 掌握度 | +| ------------------------------------------------------------------------------------------------------------- | ---- | ------ | +| [1038. 从二叉搜索树到更大和树](https://leetcode.cn/problems/binary-search-tree-to-greater-sum-tree/) | 💛 | ✔️ | +| [230. 二叉搜索树中第 K 小的元素](https://leetcode.cn/problems/kth-smallest-element-in-a-bst/) | 💛 | ✔️ | +| [538. 把二叉搜索树转换为累加树](https://leetcode.cn/problems/convert-bst-to-greater-tree/) | 💛 | ✔️ | +| [450. 删除二叉搜索树中的节点](https://leetcode.cn/problems/delete-node-in-a-bst/) | 💛 | ✔️ | +| [700. 二叉搜索树中的搜索](https://leetcode.cn/problems/search-in-a-binary-search-tree/) | 💚 | ✔️ | +| [701. 二叉搜索树中的插入操作](https://leetcode.cn/problems/insert-into-a-binary-search-tree/) | 💛 | ✔️ | +| [98. 验证二叉搜索树](https://leetcode.cn/problems/validate-binary-search-tree/) | 💛 | ✔️ | +| [96. 不同的二叉搜索树](https://leetcode.cn/problems/unique-binary-search-trees/) | 💛 | ❌ | +| [95. 不同的二叉搜索树 II](https://leetcode.cn/problems/unique-binary-search-trees-ii/) | 💛 | ❌ | +| [108. 将有序数组转换为二叉搜索树](https://leetcode.cn/problems/convert-sorted-array-to-binary-search-tree/) | 💚 | ✔️ | +| [783. 二叉搜索树节点最小距离](https://leetcode.cn/problems/minimum-distance-between-bst-nodes/) | 💚 | ✔️ | +| [235. 二叉搜索树的最近公共祖先](https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-search-tree/) | 💛 | ❌ | +| [1373. 二叉搜索子树的最大键值和](https://leetcode.cn/problems/maximum-sum-bst-in-binary-tree/) | ❤️ | ❌ | + +#### N 叉树 + +| 题目 | 难度 | 掌握度 | +| :-------------------------------------------------------------------------------------- | :--: | ------ | +| [429. N 叉树的层序遍历](https://leetcode.cn/problems/n-ary-tree-level-order-traversal/) | 💛 | ✔️ | +| [559. N 叉树的最大深度](https://leetcode.cn/problems/maximum-depth-of-n-ary-tree/) | 💚 | ✔️ | +| [589. N 叉树的前序遍历](https://leetcode.cn/problems/n-ary-tree-preorder-traversal/) | 💚 | ✔️ | +| [590. N 叉树的后序遍历](https://leetcode.cn/problems/n-ary-tree-postorder-traversal/) | 💚 | ✔️ | + +### 图 + +#### BFS/DFS + +| 题目 | 难度 | 掌握度 | +| ------------------------------------------------------------------------------------ | ---- | ------ | +| [797. 所有可能的路径](https://leetcode.cn/problems/all-paths-from-source-to-target/) | 💛 | ❗ | + +#### 环检测及拓扑排序算法 + +| 题目 | 难度 | 掌握度 | +| :----------------------------------------------------------------- | ---- | ------ | +| [207. 课程表](https://leetcode.cn/problems/course-schedule/) | 💛 | ❌ | +| [210. 课程表 II](https://leetcode.cn/problems/course-schedule-ii/) | 💛 | ❌ | + +#### 二分图判定算法 + +| 题目 | 难度 | 掌握度 | +| :---------------------------------------------------------------------------------------------------------------------------------- | ---- | ------ | +| [785. 判断二分图](https://leetcode.cn/problems/is-graph-bipartite/)
[LCR 106. 判断二分图](https://leetcode.cn/problems/vEAB3K/) | 💛 | ❌ | +| [886. 可能的二分法](https://leetcode.cn/problems/possible-bipartition/) | 💛 | ❗ | + +#### 并查集算法 + +| 题目 | 难度 | 掌握度 | +| :-------------------------------------------------------------------------------------------- | ---- | ------ | +| [130. 被围绕的区域](https://leetcode.cn/problems/surrounded-regions/) | 💛 | ❌ | +| [684. 冗余连接](https://leetcode.cn/problems/redundant-connection/) | 💛 | ✔️ | +| [990. 等式方程的可满足性](https://leetcode.cn/problems/satisfiability-of-equality-equations/) | 💛 | ✔️ | + +#### Dijkstra 算法 + +| 题目 | 难度 | 掌握度 | +| :--------------------------------------------------------------------------------------------------------------------------------- | ---- | ------ | +| [743. 网络延迟时间](https://leetcode.cn/problems/network-delay-time/) | 💛 | ❌ | +| [1631. 最小体力消耗路径](https://leetcode.cn/problems/path-with-minimum-effort/) | 💛 | ❌ | +| [1514. 概率最大的路径](https://leetcode.cn/problems/path-with-maximum-probability/) | 💛 | ❌ | +| [787. K 站中转内最便宜的航班](https://leetcode.cn/problems/cheapest-flights-within-k-stops/) | 💛 | ❌ | +| [1368. 使网格图至少有一条有效路径的最小代价](https://leetcode.cn/problems/minimum-cost-to-make-at-least-one-valid-path-in-a-grid/) | ❤️ | ❌ | + +### DFS / 回溯算法 + +#### 排列、组合、子集问题 + +子集、组合、排列相关问题,都可以考虑使用回溯算法求解。 + +| 题目 | 难度 | 掌握度 | +| :--------------------------------------------------------------------- | ---- | ------ | +| [46. 全排列](https://leetcode.cn/problems/permutations/) | 💛 | ✔️ | +| [47. 全排列 II](https://leetcode.cn/problems/permutations-ii/) | 💛 | ✔️ | +| [78. 子集](https://leetcode.cn/problems/subsets/) | 💛 | ✔️ | +| [90. 子集 II](https://leetcode.cn/problems/subsets-ii/) | 💛 | ✔️ | +| [77. 组合](https://leetcode.cn/problems/combinations/) | 💛 | ✔️ | +| [39. 组合总和](https://leetcode.cn/problems/combination-sum/) | 💛 | ✔️ | +| [40. 组合总和 II](https://leetcode.cn/problems/combination-sum-ii/) | 💛 | ✔️ | +| [216. 组合总和 III](https://leetcode.cn/problems/combination-sum-iii/) | 💛 | ✔️ | + +#### 岛屿问题 + +| 题目 | 难度 | 掌握度 | +| :--------------------------------------------------------------------------------- | ---- | ------ | +| [200. 岛屿数量](https://leetcode.cn/problems/number-of-islands/) | 💛 | ❗ | +| [1254. 统计封闭岛屿的数目](https://leetcode.cn/problems/number-of-closed-islands/) | 💛 | ❗ | +| [1020. 飞地的数量](https://leetcode.cn/problems/number-of-enclaves/) | 💛 | ❗ | +| [695. 岛屿的最大面积](https://leetcode.cn/problems/max-area-of-island/) | 💛 | ❗ | +| [1905. 统计子岛屿](https://leetcode.cn/problems/count-sub-islands/) | 💛 | ❌ | + +#### 数独、N 皇后问题 + +| 题目 | 难度 | 掌握度 | +| :-------------------------------------------------------- | ---- | ------ | +| [37. 解数独](https://leetcode.cn/problems/sudoku-solver/) | ❤️ | ❌ | +| [51. N 皇后](https://leetcode.cn/problems/n-queens/) | ❤️ | ❌ | +| [52. N皇后 II](https://leetcode.cn/problems/n-queens-ii/) | ❤️ | ❌ | + +#### 练习 + +| 题目 | 难度 | 掌握度 | +| :----------------------------------------------------------------------------------------------- | ---- | ------ | +| [967. 连续差相同的数字](https://leetcode.cn/problems/numbers-with-same-consecutive-differences/) | 💛 | ❌ | +| [491. 非递减子序列](https://leetcode.cn/problems/non-decreasing-subsequences/) | 💛 | ❌ | +| [980. 不同路径 III](https://leetcode.cn/problems/unique-paths-iii/) | ❤️ | ❌ | +| [526. 优美的排列](https://leetcode.cn/problems/beautiful-arrangement/) | 💛 | ❌ | +| [131. 分割回文串](https://leetcode.cn/problems/palindrome-partitioning/) | 💛 | ❌ | +| [93. 复原 IP 地址](https://leetcode.cn/problems/restore-ip-addresses/) | 💛 | ❌ | +| [89. 格雷编码](https://leetcode.cn/problems/gray-code/) | 💛 | ❌ | +| [17. 电话号码的字母组合](https://leetcode.cn/problems/letter-combinations-of-a-phone-number/) | 💛 | ❌ | +| [79. 单词搜索](https://leetcode.cn/problems/word-search/) | 💛 | ❌ | + +### BFS + +| 题目 | 难度 | 掌握度 | +| :----------------------------------------------------------------------------------------------- | :--: | ------ | +| [752. 打开转盘锁](https://leetcode.cn/problems/open-the-lock/) | 💛 | ❌ | +| [773. 滑动谜题](https://leetcode.cn/problems/sliding-puzzle/) | ❤️ | ❌ | +| [919. 完全二叉树插入器](https://leetcode.cn/problems/complete-binary-tree-inserter/) | 💛 | ✔️ | +| [841. 钥匙和房间](https://leetcode.cn/problems/keys-and-rooms/) | 💛 | ✔️ | +| [433. 最小基因变化](https://leetcode.cn/problems/minimum-genetic-mutation/) | 💛 | ❗ | +| [1926. 迷宫中离入口最近的出口](https://leetcode.cn/problems/nearest-exit-from-entrance-in-maze/) | 💛 | ✔️ | +| [1091. 二进制矩阵中的最短路径](https://leetcode.cn/problems/shortest-path-in-binary-matrix/) | 💛 | ✔️ | +| [994. 腐烂的橘子](https://leetcode.cn/problems/rotting-oranges/) | 💛 | ✔️ | +| [365. 水壶问题](https://leetcode.cn/problems/water-and-jug-problem/) | 💛 | ❌ | +| [721. 账户合并](https://leetcode.cn/problems/accounts-merge/) | 💛 | ❌ | +| [127. 单词接龙](https://leetcode.cn/problems/word-ladder/) | ❤️ | ❌ | + +### 动态规划 + +#### 斐波那契 + +| 题目 | 难度 | 掌握度 | +| --------------------------------------------------------------------------------- | :--: | :----: | +| [509. 斐波那契数](https://leetcode.cn/problems/fibonacci-number/) | 💚 | ✔️ | +| [1137. 第 N 个泰波那契数](https://leetcode.cn/problems/n-th-tribonacci-number/) | 💚 | ✔️ | +| [70. 爬楼梯](https://leetcode.cn/problems/climbing-stairs/) | 💚 | ✔️ | +| [746. 使用最小花费爬楼梯](https://leetcode.cn/problems/min-cost-climbing-stairs/) | 💚 | ✔️ | +| [198. 打家劫舍](https://leetcode.cn/problems/house-robber/) | 💛 | ✔️ | +| [740. 删除并获得点数](https://leetcode.cn/problems/delete-and-earn/) | 💛 | ✔️ | + +#### 一维 + +| 题目 | 难度 | 掌握度 | +| ------------------------------------------------------------------------------------------------ | :--: | :----: | +| [2140. 解决智力问题](https://leetcode.cn/problems/solving-questions-with-brainpower/) | 💛 | ❌ | +| [2466. 统计构造好字符串的方案数](https://leetcode.cn/problems/count-ways-to-build-good-strings/) | 💛 | ❌ | +| [91. 解码方法](https://leetcode.cn/problems/decode-ways/) | 💛 | ❌ | +| [983. 最低票价](https://leetcode.cn/problems/minimum-cost-for-tickets/) | 💛 | ❌ | +| [264. 丑数 II](https://leetcode.cn/problems/ugly-number-ii/) | 💛 | ❗ | +| [1201. 丑数 III](https://leetcode.cn/problems/ugly-number-iii/) | 💛 | ❌ | +| [313. 超级丑数](https://leetcode.cn/problems/super-ugly-number/) | 💛 | ❌ | + +#### 矩阵 + +| 题目 | 难度 | 掌握度 | +| ----------------------------------------------------------------------------- | :--: | :----: | +| [118. 杨辉三角](https://leetcode.cn/problems/pascals-triangle/) | 💚 | ✔️ | +| [119. 杨辉三角 II](https://leetcode.cn/problems/pascals-triangle-ii/) | 💚 | ✔️ | +| [62. 不同路径](https://leetcode.cn/problems/unique-paths/) | 💛 | ✔️ | +| [63. 不同路径 II](https://leetcode.cn/problems/unique-paths-ii/) | 💛 | ✔️ | +| [64. 最小路径和](https://leetcode.cn/problems/minimum-path-sum/) | 💛 | ✔️ | +| [120. 三角形最小路径和](https://leetcode.cn/problems/triangle/) | 💛 | ✔️ | +| [931. 下降路径最小和](https://leetcode.cn/problems/minimum-falling-path-sum/) | 💛 | ✔️ | +| [221. 最大正方形](https://leetcode.cn/problems/maximal-square/) | 💛 | ✔️ | + +#### 字符串 + +| 题目 | 难度 | 掌握度 | +| ---------------------------------------------------------------------------------------------------------- | :--: | :----: | +| [5. 最长回文子串](https://leetcode.cn/problems/longest-palindromic-substring/) | 💛 | ✔️ | +| [139. 单词拆分](https://leetcode.cn/problems/word-break/) | 💛 | ❌ | +| [72. 编辑距离](https://leetcode.cn/problems/edit-distance/) | 💛 | ❗ | +| [583. 两个字符串的删除操作](https://leetcode.cn/problems/delete-operation-for-two-strings/) | 💛 | ❌ | +| [712. 两个字符串的最小ASCII删除和](https://leetcode.cn/problems/minimum-ascii-delete-sum-for-two-strings/) | 💛 | ❌ | +| [516. 最长回文子序列](https://leetcode.cn/problems/longest-palindromic-subsequence/) | 💛 | ❌ | +| [115. 不同的子序列](https://leetcode.cn/problems/distinct-subsequences/) | ❤️ | ❌ | + +#### 最长递增/公共子序列 + +| 题目 | 难度 | 掌握度 | +| --------------------------------------------------------------------------------------------------------------------------- | :--: | :----: | +| [300. 最长递增子序列](https://leetcode.cn/problems/longest-increasing-subsequence/) | 💛 | ❌ | +| [673. 最长递增子序列的个数](https://leetcode.cn/problems/number-of-longest-increasing-subsequence/) | 💛 | ❌ | +| [646. 最长数对链](https://leetcode.cn/problems/maximum-length-of-pair-chain/) | 💛 | ✔️ | +| [1218. 最长定差子序列](https://leetcode.cn/problems/longest-arithmetic-subsequence-of-given-difference/) | 💛 | ❌ | +| [1027. 最长等差数列](https://leetcode.cn/problems/longest-arithmetic-subsequence/) | 💛 | ❌ | +| [1143. 最长公共子序列](https://leetcode.cn/problems/longest-common-subsequence/) | 💛 | ❗ | +| [1035. 不相交的线](https://leetcode.cn/problems/uncrossed-lines/) | 💛 | ❌ | +| [1312. 让字符串成为回文串的最少插入次数](https://leetcode.cn/problems/minimum-insertion-steps-to-make-a-string-palindrome/) | ❤️ | ❌ | + +#### 背包问题 + +| 题目 | 难度 | 掌握度 | +| ----------------------------------------------------------------------------- | ---- | ------ | +| [416. 分割等和子集](https://leetcode.cn/problems/partition-equal-subset-sum/) | 💛 | ❌ | +| [322. 零钱兑换](https://leetcode.cn/problems/coin-change/) | 💛 | ❌ | +| [518. 零钱兑换 II](https://leetcode.cn/problems/coin-change-ii/) | 💛 | ❌ | + +#### 买卖股票的最佳时间/状态机 + +#### 其他 + +| 题目 | 难度 | 掌握度 | +| ------------------------------------------------------------------------------- | ---- | ------ | +| [53. 最大子数组和](https://leetcode.cn/problems/maximum-subarray/) | 💛 | ❌ | +| [354. 俄罗斯套娃信封问题](https://leetcode.cn/problems/russian-doll-envelopes/) | ❤️ | ❌ | + +### 贪心算法 + +| 题目 | 难度 | 掌握度 | +| -------------------------------------------------------------- | ---- | ------ | +| [561. 数组拆分](https://leetcode.cn/problems/array-partition/) | 💚 | ❌ | +| [55. 跳跃游戏](https://leetcode.cn/problems/jump-game/) | 💛 | ❌ | +| [45. 跳跃游戏 II](https://leetcode.cn/problems/jump-game-ii/) | 💛 | ❌ | + +### 分治算法 + +| 题目 | 掌握度 | +| --------------------------------------------------------------------------- | ------ | +| [23. 合并 K 个升序链表](https://leetcode.cn/problems/merge-k-sorted-lists/) | ✔️ | + +### 数学 + +| 题目 | 难度 | 掌握度 | +| ------------------------------------------------------ | ---- | ------ | +| [66. 加一](https://leetcode.cn/problems/plus-one/) | 💚 | ✔️ | +| [263. 丑数](https://leetcode.cn/problems/ugly-number/) | 💚 | ✔️ | ## 📚 资料 - **书籍** - - 刷题必备 + - **刷题必备** - 《剑指 offer》 - 《编程之美》 - - 《编程之法:面试和算法心得》 + - 《编程之法:面试和算法心得》 - 《算法谜题》 都是思维题 - - 基础 - - 《[编程珠玑(第 2 版)](https://www.amazon.cn/gp/product/B00SFZH0DC/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=536&creative=3200&creativeASIN=B00SFZH0DC&linkCode=as2&tag=vastwork-23)》 - - 《[编程珠玑(续)](https://www.amazon.cn/gp/product/B0150BMQDM/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=536&creative=3200&creativeASIN=B0150BMQDM&linkCode=as2&tag=vastwork-23)》 - - 《[数据结构与算法分析 : C++描述(第 4 版)](https://www.amazon.cn/gp/product/B01LDG2DSG/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=536&creative=3200&creativeASIN=B01LDG2DSG&linkCode=as2&tag=vastwork-23)》 - - 《[数据结构与算法分析 : C 语言描述(第 2 版)](https://www.amazon.cn/gp/product/B002WC7NGS/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=536&creative=3200&creativeASIN=B002WC7NGS&linkCode=as2&tag=vastwork-23)》 - - 《[数据结构与算法分析 : Java 语言描述(第 2 版)](https://www.amazon.cn/gp/product/B01CNP0CG6/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=536&creative=3200&creativeASIN=B01CNP0CG6&linkCode=as2&tag=vastwork-23)》 - - 《[算法(第 4 版)](https://www.amazon.cn/gp/product/B009OCFQ0O/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=536&creative=3200&creativeASIN=B009OCFQ0O&linkCode=as2&tag=vastwork-23)》 - - 算法设计 - - 《[算法设计与分析基础(第 3 版)](https://www.amazon.cn/gp/product/B00S4HCQUI/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=536&creative=3200&creativeASIN=B00S4HCQUI&linkCode=as2&tag=vastwork-23)》 + - **基础** + - [《编程珠玑(第 2 版)》](https://www.amazon.cn/gp/product/B00SFZH0DC/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=536&creative=3200&creativeASIN=B00SFZH0DC&linkCode=as2&tag=vastwork-23) + - [《编程珠玑(续)》](https://www.amazon.cn/gp/product/B0150BMQDM/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=536&creative=3200&creativeASIN=B0150BMQDM&linkCode=as2&tag=vastwork-23) + - [《数据结构与算法分析 : C++描述(第 4 版)》](https://www.amazon.cn/gp/product/B01LDG2DSG/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=536&creative=3200&creativeASIN=B01LDG2DSG&linkCode=as2&tag=vastwork-23) + - [《数据结构与算法分析 : C 语言描述(第 2 版)》](https://www.amazon.cn/gp/product/B002WC7NGS/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=536&creative=3200&creativeASIN=B002WC7NGS&linkCode=as2&tag=vastwork-23) + - [《数据结构与算法分析 : Java 语言描述(第 2 版)》](https://www.amazon.cn/gp/product/B01CNP0CG6/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=536&creative=3200&creativeASIN=B01CNP0CG6&linkCode=as2&tag=vastwork-23) + - [《算法(第 4 版)》](https://www.amazon.cn/gp/product/B009OCFQ0O/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=536&creative=3200&creativeASIN=B009OCFQ0O&linkCode=as2&tag=vastwork-23) + - **算法设计** + - [《算法设计与分析基础(第 3 版)》](https://www.amazon.cn/gp/product/B00S4HCQUI/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=536&creative=3200&creativeASIN=B00S4HCQUI&linkCode=as2&tag=vastwork-23) - 《Algorithm Design Manual》 - 算法设计手册 红皮书 - [《算法导论》](https://www.amazon.cn/gp/product/B00AK7BYJY/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=536&creative=3200&creativeASIN=B00AK7BYJY&linkCode=as2&tag=vastwork-23) - 是一本对算法介绍比较全面的经典书籍 - 《Algorithms on Strings,Trees and Sequences》 - 《Advanced Data Structures》 - 各种诡异高级的数据结构和算法 如元胞自动机、斐波纳契堆、线段树 600 块 - **学习网站** + - https://labuladong.online/algo/ - https://github.com/TheAlgorithms/Java - https://github.com/nonstriater/Learn-Algorithms - https://github.com/trekhleb/javascript-algorithms diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/Parklot.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/Parklot.java deleted file mode 100644 index 2451364..0000000 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/Parklot.java +++ /dev/null @@ -1,157 +0,0 @@ -package io.github.dunwu.algorithm; - -import java.time.LocalDateTime; -import java.time.ZoneOffset; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; - -/** - * @author Zhang Peng - * @since 2020-05-13 - */ -public class Parklot { - - private volatile AtomicInteger space; - private volatile AtomicLong sum; - private volatile int MAX = 1000; - - public boolean enter(Car car) { - if (space.get() >= MAX) { - // reject - return false; - } - - // TODO 判断车类型 - - car.enter(); - space.getAndIncrement(); - return true; - } - - public long exit(Car car) { - if (space.get() >= MAX) { - // reject - return 0L; - } - - // TODO 判断车类型 - car.exit(); - long money = car.money(); - // 扣费 - space.getAndDecrement(); - sum.getAndAdd(money); - return money; - } - - public void getSum() { - - } - - public interface Parking { - - enum Type { - car, - truck - } - - int getPrice(); - - int getMax(); - - void enter(); - - void exit(); - - LocalDateTime getBeginTime(); - - LocalDateTime getEndTime(); - - default long money() { - if (getEndTime() == null) { - return 0; - } - long l2 = getEndTime().toEpochSecond(ZoneOffset.UTC); - long l1 = getBeginTime().toEpochSecond(ZoneOffset.UTC); - long time = l2 - l1; - long hours = TimeUnit.NANOSECONDS.toHours(time); - long total = getPrice() * hours; - return Math.min(total, getMax()); - } - - } - - public abstract class Car implements Parking { - - private static final int price = 5; - private static final int max = 60; - private LocalDateTime beginTime; - private LocalDateTime endTime; - - @Override - public int getPrice() { - return price; - } - - @Override - public int getMax() { - return max; - } - - @Override - public void enter() { - beginTime = LocalDateTime.now(); - } - - @Override - public void exit() { - endTime = LocalDateTime.now(); - } - - @Override - public LocalDateTime getBeginTime() { - return beginTime; - } - - @Override - public LocalDateTime getEndTime() { - return endTime; - } - - } - - public class LittleCar extends Car { - - private static final int price = 5; - private static final int max = 60; - - @Override - public int getPrice() { - return price; - } - - @Override - public int getMax() { - return max; - } - - } - - public class Truck extends Car implements Parking { - - private static final int price = 10; - private static final int max = 120; - - @Override - public int getPrice() { - return price; - } - - @Override - public int getMax() { - return max; - } - - } - -} diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/Test2.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/Test2.java deleted file mode 100644 index e918626..0000000 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/Test2.java +++ /dev/null @@ -1,123 +0,0 @@ -package io.github.dunwu.algorithm; - -/** - * @author Zhang Peng - * @since 2020-05-13 - */ -public class Test2 { - - public static void main(String[] args) { - ListNode l1 = new ListNode(2); - l1.next = new ListNode(4); - l1.next.next = new ListNode(3); - - ListNode l2 = new ListNode(5); - l2.next = new ListNode(6); - l2.next.next = new ListNode(4); - - ListNode result = add(l1, l2); - ListNode temp = result; - while (temp != null) { - System.out.println(temp.val); - temp = temp.next; - } - - System.out.println("result = " + listNodeToNum(result)); - } - - public static ListNode add(ListNode l1, ListNode l2) { - ListNode n1 = l1; - ListNode n2 = l2; - ListNode resultNode = new ListNode(-1); - ListNode temp = resultNode; - boolean flag = false; - while (n1 != null && n2 != null) { - int value = n1.val + n2.val; - if (flag) { - value++; - } - int num = 0; - if (value >= 10) { - num = value % 10; - flag = true; - } else { - num = value; - flag = false; - } - - n1 = n1.next; - n2 = n2.next; - temp.next = new ListNode(num); - temp = temp.next; - } - - if (n1 != null) { - while (n1 != null) { - int num = 0; - if (flag) { - num = 1 + n1.val; - } else { - num = n1.val; - } - n1 = n1.next; - temp = new ListNode(num); - temp = temp.next; - } - } - - if (n2 != null) { - while (n2 != null) { - int num = 0; - if (flag) { - num = 1 + n2.val; - } else { - num = n2.val; - } - n2 = n2.next; - temp = new ListNode(num); - temp = temp.next; - } - } - - return resultNode.next; - } - - public static int listNodeToNum(ListNode head) { - if (head == null) { - return 0; - } - - int result = 0; - int pos = 0; - ListNode node = head; - while (node != null) { - result += getBase(pos) * node.val; - node = node.next; - pos++; - } - - return result; - } - - public static int getBase(int pos) { - if (pos <= 0) { - return 1; - } else { - pos--; - return 10 * getBase(pos); - } - } - - public static class ListNode { - - public int val; - public ListNode next; - - public ListNode(int val) { - this.val = val; - this.next = null; - } - - } - -} diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/ArrayDemo.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/ArrayDemo.java deleted file mode 100644 index 62c1db5..0000000 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/ArrayDemo.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.dunwu.algorithm.array; - -/** - * @author Zhang Peng - * @since 2020-01-20 - */ -public class ArrayDemo { - - public static int maxSubArray(int[] nums) { - int len = nums.length; - - int maxSum = nums[0]; - for (int i = 1; i < len; i++) { - if (nums[i - 1] > 0) nums[i] += nums[i - 1]; - maxSum = Math.max(nums[i], maxSum); - } - return maxSum; - } - - public static void main(String[] args) { - int max = maxSubArray(new int[] { -2, 1, -3, 4, -1, 2, 1, -5, 4 }); - System.out.println("max = " + max); - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/base/\346\234\200\345\244\247\350\277\236\347\273\2551\347\232\204\344\270\252\346\225\260.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/base/\346\234\200\345\244\247\350\277\236\347\273\2551\347\232\204\344\270\252\346\225\260.java" new file mode 100644 index 0000000..7e1cb56 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/base/\346\234\200\345\244\247\350\277\236\347\273\2551\347\232\204\344\270\252\346\225\260.java" @@ -0,0 +1,37 @@ +package io.github.dunwu.algorithm.array.base; + +import org.junit.jupiter.api.Assertions; + +/** + * 485. 最大连续 1 的个数 + * + * @author Zhang Peng + * @since 2018-11-05 + */ +public class 最大连续1的个数 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(3, s.findMaxConsecutiveOnes(new int[] { 1, 1, 0, 1, 1, 1 })); + Assertions.assertEquals(2, s.findMaxConsecutiveOnes(new int[] { 1, 0, 1, 1, 0, 1 })); + } + + static class Solution { + + public int findMaxConsecutiveOnes(int[] nums) { + int max = 0; + int cnt = 0; + for (int num : nums) { + if (num == 1) { + cnt++; + max = Math.max(max, cnt); + } else { + cnt = 0; + } + } + return max; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/base/\350\207\263\345\260\221\346\230\257\345\205\266\344\273\226\346\225\260\345\255\227\344\270\244\345\200\215\347\232\204\346\234\200\345\244\247\346\225\260.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/base/\350\207\263\345\260\221\346\230\257\345\205\266\344\273\226\346\225\260\345\255\227\344\270\244\345\200\215\347\232\204\346\234\200\345\244\247\346\225\260.java" new file mode 100644 index 0000000..cd5ceb9 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/base/\350\207\263\345\260\221\346\230\257\345\205\266\344\273\226\346\225\260\345\255\227\344\270\244\345\200\215\347\232\204\346\234\200\345\244\247\346\225\260.java" @@ -0,0 +1,37 @@ +package io.github.dunwu.algorithm.array.base; + +import org.junit.jupiter.api.Assertions; + +/** + * 747. 至少是其他数字两倍的最大数 + * + * @author Zhang Peng + * @since 2018-11-04 + */ +public class 至少是其他数字两倍的最大数 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(1, s.dominantIndex(new int[] { 3, 6, 1, 0 })); + Assertions.assertEquals(-1, s.dominantIndex(new int[] { 1, 2, 3, 4 })); + Assertions.assertEquals(0, s.dominantIndex(new int[] { 1, 0 })); + } + + static class Solution { + + public int dominantIndex(int[] nums) { + int second = -1, max = 0; + for (int i = 1; i < nums.length; i++) { + if (nums[i] > nums[max]) { + second = max; + max = i; + } else if (second == -1 || nums[i] > nums[second]) { + second = i; + } + } + return nums[max] >= 2 * nums[second] ? max : -1; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/bsearch/\344\272\214\345\210\206\346\237\245\346\211\276.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/bsearch/\344\272\214\345\210\206\346\237\245\346\211\276.java" new file mode 100644 index 0000000..1a47983 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/bsearch/\344\272\214\345\210\206\346\237\245\346\211\276.java" @@ -0,0 +1,38 @@ +package io.github.dunwu.algorithm.array.bsearch; + +import org.junit.jupiter.api.Assertions; + +/** + * 704. 二分查找 + * + * @author Zhang Peng + * @since 2020-06-05 + */ +public class 二分查找 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(4, s.search(new int[] { -1, 0, 3, 5, 9, 12 }, 9)); + Assertions.assertEquals(-1, s.search(new int[] { -1, 0, 3, 5, 9, 12 }, 2)); + } + + static class Solution { + + public int search(int[] nums, int target) { + int left = 0, right = nums.length - 1; + while (left <= right) { + int mid = left + (right - left) / 2; + if (nums[mid] == target) { + return mid; + } else if (nums[mid] < target) { + left = mid + 1; + } else { + right = mid - 1; + } + } + return -1; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/bsearch/\345\210\206\345\211\262\346\225\260\347\273\204\347\232\204\346\234\200\345\244\247\345\200\274.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/bsearch/\345\210\206\345\211\262\346\225\260\347\273\204\347\232\204\346\234\200\345\244\247\345\200\274.java" new file mode 100644 index 0000000..0c3678b --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/bsearch/\345\210\206\345\211\262\346\225\260\347\273\204\347\232\204\346\234\200\345\244\247\345\200\274.java" @@ -0,0 +1,66 @@ +package io.github.dunwu.algorithm.array.bsearch; + +import org.junit.jupiter.api.Assertions; + +/** + * 410. 分割数组的最大值 + * + * @author Zhang Peng + * @date 2025-10-16 + */ +public class 分割数组的最大值 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(18, s.splitArray(new int[] { 7, 2, 5, 10, 8 }, 2)); + Assertions.assertEquals(9, s.splitArray(new int[] { 1, 2, 3, 4, 5 }, 2)); + Assertions.assertEquals(4, s.splitArray(new int[] { 1, 4, 4 }, 3)); + } + + static class Solution { + + public int splitArray(int[] nums, int k) { + return shipWithinDays(nums, k); + } + + public int shipWithinDays(int[] weights, int days) { + int max = 0, sum = 0; + for (int weight : weights) { + max = Math.max(max, weight); + sum += weight; + } + + int left = max, right = sum; + while (left <= right) { + int mid = left + (right - left) / 2; + if (f(weights, mid) <= days) { + // 需要让 f(x) 的返回值大一些 + right = mid - 1; + } else if (f(weights, mid) > days) { + // 需要让 f(x) 的返回值小一些 + left = mid + 1; + } + } + return left; + } + + // 定义:当运载能力为 x 时,需要 f(x) 天运完所有货物 + // f(x) 随着 x 的增加单调递减 + int f(int[] weights, int x) { + int days = 0; + for (int i = 0; i < weights.length; ) { + // 尽可能多装货物 + int cap = x; + while (i < weights.length) { + if (cap < weights[i]) break; + else cap -= weights[i]; + i++; + } + days++; + } + return days; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/bsearch/\345\234\250D\345\244\251\345\206\205\351\200\201\350\276\276\345\214\205\350\243\271\347\232\204\350\203\275\345\212\233.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/bsearch/\345\234\250D\345\244\251\345\206\205\351\200\201\350\276\276\345\214\205\350\243\271\347\232\204\350\203\275\345\212\233.java" new file mode 100644 index 0000000..04d948d --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/bsearch/\345\234\250D\345\244\251\345\206\205\351\200\201\350\276\276\345\214\205\350\243\271\347\232\204\350\203\275\345\212\233.java" @@ -0,0 +1,62 @@ +package io.github.dunwu.algorithm.array.bsearch; + +import org.junit.jupiter.api.Assertions; + +/** + * 1011. 在 D 天内送达包裹的能力 + * + * @author Zhang Peng + * @since 2025-08-06 + */ +public class 在D天内送达包裹的能力 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(15, s.shipWithinDays(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, 5)); + Assertions.assertEquals(6, s.shipWithinDays(new int[] { 3, 2, 2, 4, 1, 4 }, 3)); + Assertions.assertEquals(3, s.shipWithinDays(new int[] { 1, 2, 3, 1, 1 }, 4)); + } + + static class Solution { + + public int shipWithinDays(int[] weights, int days) { + int max = 0, sum = 0; + for (int weight : weights) { + max = Math.max(max, weight); + sum += weight; + } + + int left = max, right = sum; + while (left <= right) { + int mid = left + (right - left) / 2; + if (f(weights, mid) <= days) { + // 需要让 f(x) 的返回值大一些 + right = mid - 1; + } else if (f(weights, mid) > days) { + // 需要让 f(x) 的返回值小一些 + left = mid + 1; + } + } + return left; + } + + // 定义:当运载能力为 x 时,需要 f(x) 天运完所有货物 + // f(x) 随着 x 的增加单调递减 + int f(int[] weights, int x) { + int days = 0; + for (int i = 0; i < weights.length; ) { + // 尽可能多装货物 + int cap = x; + while (i < weights.length) { + if (cap < weights[i]) break; + else cap -= weights[i]; + i++; + } + days++; + } + return days; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/bsearch/\345\234\250\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\346\237\245\346\211\276\345\205\203\347\264\240\347\232\204\347\254\254\344\270\200\344\270\252\345\222\214\346\234\200\345\220\216\344\270\200\344\270\252\344\275\215\347\275\256.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/bsearch/\345\234\250\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\346\237\245\346\211\276\345\205\203\347\264\240\347\232\204\347\254\254\344\270\200\344\270\252\345\222\214\346\234\200\345\220\216\344\270\200\344\270\252\344\275\215\347\275\256.java" new file mode 100644 index 0000000..fb14865 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/bsearch/\345\234\250\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\346\237\245\346\211\276\345\205\203\347\264\240\347\232\204\347\254\254\344\270\200\344\270\252\345\222\214\346\234\200\345\220\216\344\270\200\344\270\252\344\275\215\347\275\256.java" @@ -0,0 +1,79 @@ +package io.github.dunwu.algorithm.array.bsearch; + +import org.junit.jupiter.api.Assertions; + +/** + * 34.在排序数组中查找元素的第一个和最后一个位置 + * + * @author Zhang Peng + * @since 2020-06-05 + */ +public class 在排序数组中查找元素的第一个和最后一个位置 { + + public static void main(String[] args) { + Solution s = new Solution(); + + Assertions.assertEquals(-1, s.searchLeft(new int[] { 5, 7, 7, 8, 8, 10 }, 3)); + Assertions.assertEquals(0, s.searchLeft(new int[] { 5, 7, 7, 8, 8, 10 }, 5)); + Assertions.assertEquals(5, s.searchLeft(new int[] { 5, 7, 7, 8, 8, 10 }, 10)); + Assertions.assertEquals(-1, s.searchLeft(new int[] { 5, 7, 7, 8, 8, 10 }, 12)); + Assertions.assertEquals(1, s.searchLeft(new int[] { 5, 7, 7, 8, 8, 10 }, 7)); + + Assertions.assertEquals(-1, s.searchRight(new int[] { 5, 7, 7, 8, 8, 10 }, 3)); + Assertions.assertEquals(0, s.searchRight(new int[] { 5, 7, 7, 8, 8, 10 }, 5)); + Assertions.assertEquals(5, s.searchRight(new int[] { 5, 7, 7, 8, 8, 10 }, 10)); + Assertions.assertEquals(-1, s.searchRight(new int[] { 5, 7, 7, 8, 8, 10 }, 12)); + Assertions.assertEquals(2, s.searchRight(new int[] { 5, 7, 7, 8, 8, 10 }, 7)); + + Assertions.assertArrayEquals(new int[] { 3, 4 }, s.searchRange(new int[] { 5, 7, 7, 8, 8, 10 }, 8)); + Assertions.assertArrayEquals(new int[] { -1, -1 }, s.searchRange(new int[] { 5, 7, 7, 8, 8, 10 }, 6)); + Assertions.assertArrayEquals(new int[] { -1, -1 }, s.searchRange(new int[] {}, 0)); + Assertions.assertArrayEquals(new int[] { 0, 0 }, s.searchRange(new int[] { 1 }, 1)); + } + + static class Solution { + + public int[] searchRange(int[] nums, int target) { + int left = searchLeft(nums, target); + int right = searchRight(nums, target); + return new int[] { left, right }; + } + + public int searchLeft(int[] nums, int target) { + int res = -1; + int left = 0, right = nums.length - 1; + while (left <= right) { + int mid = left + (right - left) / 2; + if (nums[mid] < target) { + left = mid + 1; + } else if (nums[mid] > target) { + right = mid - 1; + } else if (nums[mid] == target) { + res = mid; + right = mid - 1; + } + } + return res; + } + + public int searchRight(int[] nums, int target) { + int res = -1; + int left = 0, right = nums.length - 1; + while (left <= right) { + int mid = left + (right - left) / 2; + if (nums[mid] > target) { + right = mid - 1; + } else if (nums[mid] < target) { + left = mid + 1; + } else if (nums[mid] == target) { + res = mid; + left = mid + 1; + } + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/bsearch/\346\220\234\347\264\242\346\217\222\345\205\245\344\275\215\347\275\256.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/bsearch/\346\220\234\347\264\242\346\217\222\345\205\245\344\275\215\347\275\256.java" new file mode 100644 index 0000000..bdc2bb9 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/bsearch/\346\220\234\347\264\242\346\217\222\345\205\245\344\275\215\347\275\256.java" @@ -0,0 +1,41 @@ +package io.github.dunwu.algorithm.array.bsearch; + +import org.junit.jupiter.api.Assertions; + +/** + * 35. 搜索插入位置 + * + * @author Zhang Peng + * @since 2020-07-29 + */ +public class 搜索插入位置 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(0, s.searchInsert(new int[] { 1 }, 1)); + Assertions.assertEquals(2, s.searchInsert(new int[] { 1, 3, 5, 6 }, 5)); + Assertions.assertEquals(1, s.searchInsert(new int[] { 1, 3, 5, 6 }, 2)); + Assertions.assertEquals(4, s.searchInsert(new int[] { 1, 3, 5, 6 }, 7)); + Assertions.assertEquals(0, s.searchInsert(new int[] { 1, 3, 5, 6 }, 0)); + } + + static class Solution { + + public int searchInsert(int[] nums, int target) { + int left = 0, right = nums.length - 1; + while (left <= right) { + int mid = left + (right - left) / 2; + if (nums[mid] == target) { + return mid; + } else if (nums[mid] < target) { + left = mid + 1; + } else if (nums[mid] > target) { + right = mid - 1; + } + } + return left; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/bsearch/\347\210\261\345\220\203\351\246\231\350\225\211\347\232\204\347\217\202\347\217\202.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/bsearch/\347\210\261\345\220\203\351\246\231\350\225\211\347\232\204\347\217\202\347\217\202.java" new file mode 100644 index 0000000..c7b5f17 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/bsearch/\347\210\261\345\220\203\351\246\231\350\225\211\347\232\204\347\217\202\347\217\202.java" @@ -0,0 +1,55 @@ +package io.github.dunwu.algorithm.array.bsearch; + +import org.junit.jupiter.api.Assertions; + +/** + * 875. 爱吃香蕉的珂珂 + * + * @author Zhang Peng + * @since 2025-08-06 + */ +public class 爱吃香蕉的珂珂 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(4, s.minEatingSpeed(new int[] { 3, 6, 7, 11 }, 8)); + Assertions.assertEquals(30, s.minEatingSpeed(new int[] { 30, 11, 23, 4, 20 }, 5)); + Assertions.assertEquals(23, s.minEatingSpeed(new int[] { 30, 11, 23, 4, 20 }, 6)); + Assertions.assertEquals(2, s.minEatingSpeed(new int[] { 312884470 }, 312884469)); + Assertions.assertEquals(3, s.minEatingSpeed(new int[] { 805306368, 805306368, 805306368 }, 1000000000)); + Assertions.assertEquals(14, s.minEatingSpeed( + new int[] { 332484035, 524908576, 855865114, 632922376, 222257295, 690155293, 112677673, 679580077, + 337406589, 290818316, 877337160, 901728858, 679284947, 688210097, 692137887, 718203285, 629455728, + 941802184 }, 823855818)); + } + + static class Solution { + + public int minEatingSpeed(int[] piles, int h) { + int left = 1, right = 1_000_000_000; + + // right 是闭区间,所以这里改成 <= + while (left <= right) { + int mid = left + (right - left) / 2; + if (f(piles, mid) <= h) { + // right 是闭区间,所以这里用 mid - 1 + right = mid - 1; + } else if (f(piles, mid) > h) { + left = mid + 1; + } + } + return left; + } + + long f(int[] nums, int x) { + long h = 0; + for (int num : nums) { + h += num / x; + if (num % x > 0) { h++; } + } + return h; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/bsearch/\347\273\237\350\256\241\347\233\256\346\240\207\346\210\220\347\273\251\347\232\204\345\207\272\347\216\260\346\254\241\346\225\260.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/bsearch/\347\273\237\350\256\241\347\233\256\346\240\207\346\210\220\347\273\251\347\232\204\345\207\272\347\216\260\346\254\241\346\225\260.java" new file mode 100644 index 0000000..c0266ae --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/bsearch/\347\273\237\350\256\241\347\233\256\346\240\207\346\210\220\347\273\251\347\232\204\345\207\272\347\216\260\346\254\241\346\225\260.java" @@ -0,0 +1,52 @@ +package io.github.dunwu.algorithm.array.bsearch; + +import org.junit.jupiter.api.Assertions; + +/** + * LCR 172. 统计目标成绩的出现次数 + * + * @author Zhang Peng + * @date 2025-10-15 + */ +public class 统计目标成绩的出现次数 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(3, s.countTarget(new int[] { 2, 2, 3, 4, 4, 4, 5, 6, 6, 8 }, 4)); + Assertions.assertEquals(0, s.countTarget(new int[] { 1, 2, 3, 5, 7, 9 }, 6)); + } + + static class Solution { + + public int countTarget(int[] scores, int target) { + int leftBound = searchLeft(scores, target); + if (leftBound == -1) { return 0; } + int cnt = 1; + for (int i = leftBound + 1; i < scores.length; i++) { + if (scores[i] == target) { + cnt++; + } + } + return cnt; + } + + public int searchLeft(int[] nums, int target) { + int res = -1; + int left = 0, right = nums.length - 1; + while (left <= right) { + int mid = left + (right - left) / 2; + if (nums[mid] < target) { + left = mid + 1; + } else if (nums[mid] > target) { + right = mid - 1; + } else if (nums[mid] == target) { + right = mid - 1; + res = mid; + } + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\250\241\346\213\237ArrayList1.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/demo/\346\250\241\346\213\237ArrayList1.java" similarity index 98% rename from "codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\250\241\346\213\237ArrayList1.java" rename to "codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/demo/\346\250\241\346\213\237ArrayList1.java" index 841235f..1787652 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\250\241\346\213\237ArrayList1.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/demo/\346\250\241\346\213\237ArrayList1.java" @@ -1,4 +1,4 @@ -package io.github.dunwu.algorithm.array; +package io.github.dunwu.algorithm.array.demo; import java.util.Arrays; diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\250\241\346\213\237ArrayList2.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/demo/\346\250\241\346\213\237ArrayList2.java" similarity index 99% rename from "codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\250\241\346\213\237ArrayList2.java" rename to "codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/demo/\346\250\241\346\213\237ArrayList2.java" index 22250c6..d38d8d9 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\250\241\346\213\237ArrayList2.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/demo/\346\250\241\346\213\237ArrayList2.java" @@ -1,4 +1,4 @@ -package io.github.dunwu.algorithm.array; +package io.github.dunwu.algorithm.array.demo; public class 模拟ArrayList2 { diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/matrix/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\345\215\225\350\257\215.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/matrix/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\345\215\225\350\257\215.java" new file mode 100644 index 0000000..2a10b4a --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/matrix/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\345\215\225\350\257\215.java" @@ -0,0 +1,65 @@ +package io.github.dunwu.algorithm.array.matrix; + +import org.junit.jupiter.api.Assertions; + +/** + * 151. 反转字符串中的单词 + * + * @author Zhang Peng + * @since 2020-06-05 + */ +public class 反转字符串中的单词 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals("blue is sky the", s.reverseWords("the sky is blue")); + Assertions.assertEquals("world hello", s.reverseWords(" hello world ")); + Assertions.assertEquals("example good a", s.reverseWords("a good example")); + + Solution2 s2 = new Solution2(); + Assertions.assertEquals("blue is sky the", s2.reverseWords("the sky is blue")); + Assertions.assertEquals("world hello", s2.reverseWords(" hello world ")); + Assertions.assertEquals("example good a", s2.reverseWords("a good example")); + } + + // 利用库函数 + static class Solution { + + public String reverseWords(String s) { + String[] arr = s.trim().split(" "); + StringBuilder sb = new StringBuilder(); + for (int i = arr.length - 1; i >= 0; i--) { + if (arr[i].equals("")) { + continue; + } + sb.append(arr[i]).append(" "); + } + return sb.toString().trim(); + } + + } + + // 双指针 + static class Solution2 { + + public String reverseWords(String s) { + // 删除首尾空格 + s = s.trim(); + int l = s.length() - 1, r = l; + StringBuilder res = new StringBuilder(); + while (l >= 0) { + // 左指针偏移,直到遇到空格 + while (l >= 0 && s.charAt(l) != ' ') { l--; } + // 添加单词 + res.append(s.substring(l + 1, r + 1)).append(' '); + // 左指针偏移,直到遇到非空格 + while (l >= 0 && s.charAt(l) == ' ') { l--; } + // 右指针对齐左指针 + r = l; + } + return res.toString().trim(); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/matrix/\345\257\271\350\247\222\347\272\277\351\201\215\345\216\206.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/matrix/\345\257\271\350\247\222\347\272\277\351\201\215\345\216\206.java" new file mode 100644 index 0000000..9d3c672 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/matrix/\345\257\271\350\247\222\347\272\277\351\201\215\345\216\206.java" @@ -0,0 +1,64 @@ +package io.github.dunwu.algorithm.array.matrix; + +import org.junit.jupiter.api.Assertions; + +/** + * 498. 对角线遍历 + * + * @author Zhang Peng + * @since 2018-11-04 + */ +public class 对角线遍历 { + + public static void main(String[] args) { + + Solution s = new Solution(); + + int[][] input = { { 1, 2 }, { 3, 4 } }; + int[] expect = { 1, 2, 3, 4 }; + + int[][] input2 = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } }; + int[] expect2 = { 1, 2, 4, 7, 5, 3, 6, 8, 9 }; + + int[][] input3 = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 }, { 13, 14, 15, 16 } }; + int[] expect3 = { 1, 2, 5, 9, 6, 3, 4, 7, 10, 13, 14, 11, 8, 12, 15, 16 }; + + Assertions.assertArrayEquals(expect, s.findDiagonalOrder(input)); + Assertions.assertArrayEquals(expect2, s.findDiagonalOrder(input2)); + Assertions.assertArrayEquals(expect3, s.findDiagonalOrder(input3)); + } + + static class Solution { + + // 1. 同一对角线上的元素,满足 i + j = k + // 2. k 的大小,满足递增,从 0 到 m + n - 2 + // 3. 由于,i + j = k -> i = k - j + // i = m - 1 时最大,j 最小;而 k - (m - 1) 必须大于 0 => minJ = max(0, k - (m - 1)) + // i = 0 时最小,j 最大,但不能超过 n - 1 => maxJ = Math.max(k, n -1) + public int[] findDiagonalOrder(int[][] mat) { + + // base case + if (mat == null || mat.length == 0) { return new int[0]; } + + int idx = 0; + int m = mat.length, n = mat[0].length; + int[] res = new int[m * n]; + for (int k = 0; k < m + n - 1; k++) { + int minJ = Math.max(k - (m - 1), 0); + int maxJ = Math.min(k, n - 1); + if (k % 2 == 0) { + for (int j = minJ; j <= maxJ; j++) { + res[idx++] = mat[k - j][j]; + } + } else { + for (int j = maxJ; j >= minJ; j--) { + res[idx++] = mat[k - j][j]; + } + } + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/matrix/\346\227\213\350\275\254\345\233\276\345\203\217.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/matrix/\346\227\213\350\275\254\345\233\276\345\203\217.java" new file mode 100644 index 0000000..b4bdd6d --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/matrix/\346\227\213\350\275\254\345\233\276\345\203\217.java" @@ -0,0 +1,55 @@ +package io.github.dunwu.algorithm.array.matrix; + +import cn.hutool.core.util.ArrayUtil; +import org.junit.jupiter.api.Assertions; + +/** + * LCR 006. 两数之和 II - 输入有序数组 + * + * @author Zhang Peng + * @since 2025-08-06 + */ +public class 旋转图像 { + + public static void main(String[] args) { + Solution s = new Solution(); + int[][] matrix = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } }; + s.rotate(matrix); + int[][] expect = { { 7, 4, 1 }, { 8, 5, 2 }, { 9, 6, 3 } }; + Assertions.assertTrue(ArrayUtil.equals(expect, matrix)); + + int[][] matrix2 = { { 5, 1, 9, 11 }, { 2, 4, 8, 10 }, { 13, 3, 6, 7 }, { 15, 14, 12, 16 } }; + s.rotate(matrix2); + int[][] expect2 = { { 15, 13, 2, 5 }, { 14, 3, 4, 1 }, { 12, 6, 8, 9 }, { 16, 7, 10, 11 } }; + Assertions.assertTrue(ArrayUtil.equals(expect2, matrix2)); + } + + static class Solution { + + public void rotate(int[][] matrix) { + if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { return; } + int n = matrix.length; + // 沿对角线置换 + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + int temp = matrix[i][j]; + matrix[i][j] = matrix[j][i]; + matrix[j][i] = temp; + } + } + + for (int i = 0; i < n; i++) { + int left = 0, right = n - 1; + while (left < right) { + int temp = matrix[i][left]; + matrix[i][left] = matrix[i][right]; + matrix[i][right] = temp; + left++; + right--; + } + } + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/matrix/\350\236\272\346\227\213\347\237\251\351\230\265.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/matrix/\350\236\272\346\227\213\347\237\251\351\230\265.java" new file mode 100644 index 0000000..e0085df --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/matrix/\350\236\272\346\227\213\347\237\251\351\230\265.java" @@ -0,0 +1,74 @@ +package io.github.dunwu.algorithm.array.matrix; + +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; +import java.util.List; + +/** + * 54. 螺旋矩阵 + * + * @author Zhang Peng + * @since 2018-11-04 + */ +public class 螺旋矩阵 { + + public static void main(String[] args) { + Solution s = new Solution(); + List output = s.spiralOrder(new int[][] { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } }); + Assertions.assertArrayEquals(new Integer[] { 1, 2, 3, 6, 9, 8, 7, 4, 5 }, output.toArray()); + List output2 = s.spiralOrder(new int[][] { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } }); + Assertions.assertArrayEquals(new Integer[] { 1, 2, 3, 4, 8, 12, 11, 10, 9, 5, 6, 7 }, output2.toArray()); + } + + static class Solution { + + public List spiralOrder(int[][] matrix) { + if (matrix == null || matrix.length == 0 || matrix[0] == null || matrix[0].length == 0) { + return new LinkedList<>(); + } + + int m = matrix.length, n = matrix[0].length; + int up = 0, down = m - 1, left = 0, right = n - 1; + List res = new LinkedList<>(); + while (res.size() < m * n) { + // 向右 + if (up <= down) { + for (int i = left; i <= right; i++) { + res.add(matrix[up][i]); + } + up++; + } + // System.out.printf("\t [right] up: %d, down: %d, left: %d, right: %d\n", up, down, left, right); + // 向下 + if (left <= right) { + for (int i = up; i <= down; i++) { + res.add(matrix[i][right]); + } + right--; + } + // System.out.printf("\t [down] up: %d, down: %d, left: %d, right: %d\n", up, down, left, right); + // 向左 + if (up <= down) { + for (int i = right; i >= left; i--) { + res.add(matrix[down][i]); + } + down--; + } + // System.out.printf("\t [left] up: %d, down: %d, left: %d, right: %d\n", up, down, left, right); + // 向上 + if (left <= right) { + for (int i = down; i >= up; i--) { + res.add(matrix[i][left]); + } + left++; + } + // System.out.printf("\t [up] up: %d, down: %d, left: %d, right: %d\n", up, down, left, right); + // System.out.printf("res: %s\n", JSONUtil.toJsonStr(res)); + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/matrix/\350\236\272\346\227\213\347\237\251\351\230\2652.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/matrix/\350\236\272\346\227\213\347\237\251\351\230\2652.java" new file mode 100644 index 0000000..785f236 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/matrix/\350\236\272\346\227\213\347\237\251\351\230\2652.java" @@ -0,0 +1,66 @@ +package io.github.dunwu.algorithm.array.matrix; + +import org.junit.jupiter.api.Assertions; + +/** + * 54. 螺旋矩阵 + * + * @author Zhang Peng + * @since 2018-11-04 + */ +public class 螺旋矩阵2 { + + public static void main(String[] args) { + Solution s = new Solution(); + int[][] output = s.generateMatrix(3); + Assertions.assertArrayEquals(new int[][] { { 1, 2, 3 }, { 8, 9, 4 }, { 7, 6, 5 } }, output); + int[][] output2 = s.generateMatrix(1); + Assertions.assertArrayEquals(new int[][] { { 1 } }, output2); + } + + static class Solution { + + public int[][] generateMatrix(int n) { + int cnt = 0; + int[][] res = new int[n][n]; + int left = 0, right = n - 1, top = 0, bottom = n - 1; + while (cnt < n * n) { + + // 向右 + if (top <= bottom) { + for (int i = left; i <= right; i++) { + res[top][i] = ++cnt; + } + top++; + } + + // 向下 + if (left <= right) { + for (int i = top; i <= bottom; i++) { + res[i][right] = ++cnt; + } + right--; + } + + // 向左 + if (top <= bottom) { + for (int i = right; i >= left; i--) { + res[bottom][i] = ++cnt; + } + bottom--; + } + + // 向上 + if (left <= right) { + for (int i = bottom; i >= top; i--) { + res[i][left] = ++cnt; + } + left++; + } + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/matrix/\350\236\272\346\227\213\351\201\215\345\216\206\344\272\214\347\273\264\346\225\260\347\273\204.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/matrix/\350\236\272\346\227\213\351\201\215\345\216\206\344\272\214\347\273\264\346\225\260\347\273\204.java" new file mode 100644 index 0000000..c39ed1a --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/matrix/\350\236\272\346\227\213\351\201\215\345\216\206\344\272\214\347\273\264\346\225\260\347\273\204.java" @@ -0,0 +1,84 @@ +package io.github.dunwu.algorithm.array.matrix; + +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; +import java.util.List; + +/** + * 54. 螺旋矩阵 + * + * @author Zhang Peng + * @since 2018-11-04 + */ +public class 螺旋遍历二维数组 { + + public static void main(String[] args) { + Solution s = new Solution(); + int[] output = s.spiralArray(new int[][] { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } }); + Assertions.assertArrayEquals(new int[] { 1, 2, 3, 6, 9, 8, 7, 4, 5 }, output); + int[] output2 = s.spiralArray(new int[][] { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } }); + Assertions.assertArrayEquals(new int[] { 1, 2, 3, 4, 8, 12, 11, 10, 9, 5, 6, 7 }, output2); + } + + static class Solution { + + public int[] spiralArray(int[][] array) { + List list = spiralOrder(array); + int[] res = new int[list.size()]; + for (int i = 0; i < list.size(); i++) { + res[i] = list.get(i); + } + return res; + } + + public List spiralOrder(int[][] matrix) { + if (matrix == null || matrix.length == 0 || matrix[0] == null || matrix[0].length == 0) { + return new LinkedList<>(); + } + + int m = matrix.length, n = matrix[0].length; + int up = 0, down = m - 1; + int left = 0, right = n - 1; + List res = new LinkedList<>(); + while (res.size() < m * n) { + // 向右 + if (up <= down) { + for (int i = left; i <= right; i++) { + res.add(matrix[up][i]); + } + up++; + } + // System.out.printf("\t [right] up: %d, down: %d, left: %d, right: %d\n", up, down, left, right); + // 向下 + if (left <= right) { + for (int i = up; i <= down; i++) { + res.add(matrix[i][right]); + } + right--; + } + // System.out.printf("\t [down] up: %d, down: %d, left: %d, right: %d\n", up, down, left, right); + // 向左 + if (up <= down) { + for (int i = right; i >= left; i--) { + res.add(matrix[down][i]); + } + down--; + } + // System.out.printf("\t [left] up: %d, down: %d, left: %d, right: %d\n", up, down, left, right); + // 向上 + if (left <= right) { + for (int i = down; i >= up; i--) { + res.add(matrix[i][left]); + } + left++; + } + // System.out.printf("\t [up] up: %d, down: %d, left: %d, right: %d\n", up, down, left, right); + // System.out.printf("res: %s\n", JSONUtil.toJsonStr(res)); + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/matrix/\351\233\266\347\237\251\351\230\265.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/matrix/\351\233\266\347\237\251\351\230\265.java" new file mode 100644 index 0000000..6c7d535 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/matrix/\351\233\266\347\237\251\351\230\265.java" @@ -0,0 +1,54 @@ +package io.github.dunwu.algorithm.array.matrix; + +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; + +/** + * 08. 零矩阵 + * + * @author Zhang Peng + * @since 2020-06-05 + */ +public class 零矩阵 { + + public static void main(String[] args) { + Solution s = new Solution(); + int[][] input = { + { 1, 1, 1 }, + { 1, 0, 1 }, + { 1, 1, 1 } + }; + int[][] expect = { + { 1, 0, 1 }, + { 0, 0, 0 }, + { 1, 0, 1 } + }; + s.setZeroes(input); + Assertions.assertArrayEquals(expect, input); + } + + static class Solution { + + public void setZeroes(int[][] matrix) { + int m = matrix.length, n = matrix[0].length; + LinkedList queue = new LinkedList<>(); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (matrix[i][j] == 0) { + queue.offer(new int[] { i, j }); + } + } + } + + while (!queue.isEmpty()) { + int[] point = queue.poll(); + int x = point[0], y = point[1]; + for (int i = 0; i < n; i++) { matrix[x][i] = 0; } + for (int i = 0; i < m; i++) { matrix[i][y] = 0; } + } + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/range/\344\272\214\347\273\264\345\214\272\345\237\237\345\222\214\346\243\200\347\264\242_\347\237\251\351\230\265\344\270\215\345\217\257\345\217\230.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/range/\344\272\214\347\273\264\345\214\272\345\237\237\345\222\214\346\243\200\347\264\242_\347\237\251\351\230\265\344\270\215\345\217\257\345\217\230.java" new file mode 100644 index 0000000..61ece6f --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/range/\344\272\214\347\273\264\345\214\272\345\237\237\345\222\214\346\243\200\347\264\242_\347\237\251\351\230\265\344\270\215\345\217\257\345\217\230.java" @@ -0,0 +1,52 @@ +package io.github.dunwu.algorithm.array.range; + +import org.junit.jupiter.api.Assertions; + +/** + * 303. 区域和检索 - 数组不可变 + * + * @author Zhang Peng + * @since 2025-08-06 + */ +public class 二维区域和检索_矩阵不可变 { + + public static void main(String[] args) { + NumMatrix numMatrix = new NumMatrix(new int[][] { + { 3, 0, 1, 4, 2 }, + { 5, 6, 3, 2, 1 }, + { 1, 2, 0, 1, 5 }, + { 4, 1, 0, 1, 7 }, + { 1, 0, 3, 0, 5 } + }); + Assertions.assertEquals(8, numMatrix.sumRegion(2, 1, 4, 3)); + Assertions.assertEquals(11, numMatrix.sumRegion(1, 1, 2, 2)); + Assertions.assertEquals(12, numMatrix.sumRegion(1, 2, 2, 4)); + } + + static class NumMatrix { + + // preSum[i][j] 记录矩阵 [0, 0, i-1, j-1] 的元素和 + private int[][] preSum; + + public NumMatrix(int[][] matrix) { + int m = matrix.length, n = matrix[0].length; + if (m == 0 || n == 0) return; + // 构造前缀和矩阵 + preSum = new int[m + 1][n + 1]; + for (int i = 1; i <= m; i++) { + for (int j = 1; j <= n; j++) { + // 计算每个矩阵 [0, 0, i, j] 的元素和 + preSum[i][j] = preSum[i - 1][j] + preSum[i][j - 1] + matrix[i - 1][j - 1] - preSum[i - 1][j - 1]; + } + } + } + + // 计算子矩阵 [x1, y1, x2, y2] 的元素和 + public int sumRegion(int x1, int y1, int x2, int y2) { + // 目标矩阵之和由四个相邻矩阵运算获得 + return preSum[x2 + 1][y2 + 1] - preSum[x1][y2 + 1] - preSum[x2 + 1][y1] + preSum[x1][y1]; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/range/\345\214\272\345\237\237\345\222\214\346\243\200\347\264\242_\346\225\260\347\273\204\344\270\215\345\217\257\345\217\230.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/range/\345\214\272\345\237\237\345\222\214\346\243\200\347\264\242_\346\225\260\347\273\204\344\270\215\345\217\257\345\217\230.java" new file mode 100644 index 0000000..276664c --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/range/\345\214\272\345\237\237\345\222\214\346\243\200\347\264\242_\346\225\260\347\273\204\344\270\215\345\217\257\345\217\230.java" @@ -0,0 +1,37 @@ +package io.github.dunwu.algorithm.array.range; + +import org.junit.jupiter.api.Assertions; + +/** + * 303. 区域和检索 - 数组不可变 + * + * @author Zhang Peng + * @since 2025-08-06 + */ +public class 区域和检索_数组不可变 { + + public static void main(String[] args) { + NumArray numArray = new NumArray(new int[] { -2, 0, 3, -5, 2, -1 }); + Assertions.assertEquals(1, numArray.sumRange(0, 2)); + Assertions.assertEquals(-1, numArray.sumRange(2, 5)); + Assertions.assertEquals(-3, numArray.sumRange(0, 5)); + } + + static class NumArray { + + private int[] preSum; + + public NumArray(int[] nums) { + preSum = new int[nums.length + 1]; + for (int i = 1; i <= nums.length; i++) { + preSum[i] = preSum[i - 1] + nums[i - 1]; + } + } + + public int sumRange(int left, int right) { + return preSum[right + 1] - preSum[left]; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/range/\345\257\273\346\211\276\346\225\260\347\273\204\347\232\204\344\270\255\345\277\203\347\264\242\345\274\225.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/range/\345\257\273\346\211\276\346\225\260\347\273\204\347\232\204\344\270\255\345\277\203\347\264\242\345\274\225.java" new file mode 100644 index 0000000..c7d8c6b --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/range/\345\257\273\346\211\276\346\225\260\347\273\204\347\232\204\344\270\255\345\277\203\347\264\242\345\274\225.java" @@ -0,0 +1,66 @@ +package io.github.dunwu.algorithm.array.range; + +import org.junit.jupiter.api.Assertions; + +/** + * 724. 寻找数组的中心索引 + * + * @author Zhang Peng + * @since 2018-11-04 + */ +public class 寻找数组的中心索引 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(3, s.pivotIndex(new int[] { 1, 7, 3, 6, 5, 6 })); + Assertions.assertEquals(-1, s.pivotIndex(new int[] { 1, 2, 3 })); + Assertions.assertEquals(0, s.pivotIndex(new int[] { 2, 1, -1 })); + + Solution2 s2 = new Solution2(); + Assertions.assertEquals(3, s2.pivotIndex(new int[] { 1, 7, 3, 6, 5, 6 })); + Assertions.assertEquals(-1, s2.pivotIndex(new int[] { 1, 2, 3 })); + Assertions.assertEquals(0, s2.pivotIndex(new int[] { 2, 1, -1 })); + } + + static class Solution { + + public int pivotIndex(int[] nums) { + for (int mid = 0; mid < nums.length; mid++) { + int leftSum = 0, rightSum = 0; + for (int i = 0; i < mid; i++) { + leftSum += nums[i]; + } + for (int i = mid + 1; i < nums.length; i++) { + rightSum += nums[i]; + } + if (leftSum == rightSum) { + return mid; + } + } + return -1; + } + + } + + static class Solution2 { + + public int pivotIndex(int[] nums) { + int total = 0; + for (int num : nums) { + total += num; + } + + int leftSum = 0; + for (int i = 0; i < nums.length; i++) { + int rightSum = total - leftSum - nums[i]; + if (leftSum == rightSum) { + return i; + } + leftSum += nums[i]; + } + return -1; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/range/\345\260\206\346\225\260\347\273\204\345\210\206\346\210\220\345\222\214\347\233\270\347\255\211\347\232\204\344\270\211\344\270\252\351\203\250\345\210\206.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/range/\345\260\206\346\225\260\347\273\204\345\210\206\346\210\220\345\222\214\347\233\270\347\255\211\347\232\204\344\270\211\344\270\252\351\203\250\345\210\206.java" new file mode 100644 index 0000000..43dca82 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/range/\345\260\206\346\225\260\347\273\204\345\210\206\346\210\220\345\222\214\347\233\270\347\255\211\347\232\204\344\270\211\344\270\252\351\203\250\345\210\206.java" @@ -0,0 +1,58 @@ +package io.github.dunwu.algorithm.array.range; + +import org.junit.jupiter.api.Assertions; + +/** + * 1013.将数组分成和相等的三个部分 + * + * @author Zhang Peng + * @since 2020-06-05 + */ +public class 将数组分成和相等的三个部分 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertTrue(s.canThreePartsEqualSum(new int[] { 0, 2, 1, -6, 6, -7, 9, 1, 2, 0, 1 })); + Assertions.assertTrue(s.canThreePartsEqualSum(new int[] { 3, 3, 6, 5, -2, 2, 5, 1, -9, 4 })); + Assertions.assertFalse(s.canThreePartsEqualSum(new int[] { 0, 2, 1, -6, 6, 7, 9, -1, 2, 0, 1 })); + } + + static class Solution { + + public boolean canThreePartsEqualSum(int[] arr) { + int n = arr.length; + NumArray preSum = new NumArray(arr); + for (int i = 1; i < n; i++) { + for (int j = i + 1; j < n; j++) { + int leftSum = preSum.sumRange(0, i - 1); + int midSum = preSum.sumRange(i, j - 1); + int rightSum = preSum.sumRange(j, n - 1); + if (leftSum == midSum && midSum == rightSum) { + return true; + } + } + } + return false; + } + + static class NumArray { + + private final int[] preSum; + + public NumArray(int[] arr) { + preSum = new int[arr.length + 1]; + preSum[0] = 0; + for (int i = 1; i <= arr.length; i++) { + preSum[i] = preSum[i - 1] + arr[i - 1]; + } + } + + public int sumRange(int left, int right) { + return preSum[right + 1] - preSum[left]; + } + + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/range/\346\213\274\350\275\246.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/range/\346\213\274\350\275\246.java" new file mode 100644 index 0000000..6ea1646 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/range/\346\213\274\350\275\246.java" @@ -0,0 +1,94 @@ +package io.github.dunwu.algorithm.array.range; + +import org.junit.jupiter.api.Assertions; + +/** + * 1094. 屁车 + * + * @author Zhang Peng + * @date 2025-10-17 + */ +public class 拼车 { + + public static void main(String[] args) { + Solution s = new Solution(); + int[][] input = { { 2, 1, 5 }, { 3, 3, 7 } }; + Assertions.assertFalse(s.carPooling(input, 3)); + int[][] input2 = { { 1, 2, 10 }, { 2, 2, 15 } }; + Assertions.assertTrue(s.carPooling(input2, 5)); + int[][] input3 = { { 2, 1, 5 }, { 3, 5, 7 } }; + Assertions.assertTrue(s.carPooling(input3, 3)); + } + + static class Solution { + + public boolean carPooling(int[][] trips, int capacity) { + // 最多有 1000 个车站 + int[] nums = new int[1001]; + // 构造差分解法 + Difference df = new Difference(nums); + + for (int[] trip : trips) { + // 乘客数量 + int val = trip[0]; + // 第 trip[1] 站乘客上车 + int i = trip[1]; + // 第 trip[2] 站乘客已经下车, + // 即乘客在车上的区间是 [trip[1], trip[2] - 1] + int j = trip[2] - 1; + // 进行区间操作 + df.increment(i, j, val); + } + + int[] res = df.result(); + + // 客车自始至终都不应该超载 + for (int i = 0; i < res.length; i++) { + if (capacity < res[i]) { + return false; + } + } + return true; + } + + // 差分数组工具类 + class Difference { + + // 差分数组 + private int[] diff; + + // 输入一个初始数组,区间操作将在这个数组上进行 + public Difference(int[] nums) { + assert nums.length > 0; + diff = new int[nums.length]; + // 根据初始数组构造差分数组 + diff[0] = nums[0]; + for (int i = 1; i < nums.length; i++) { + diff[i] = nums[i] - nums[i - 1]; + } + } + + // 给闭区间 [i, j] 增加 val(可以是负数) + public void increment(int i, int j, int val) { + diff[i] += val; + if (j + 1 < diff.length) { + diff[j + 1] -= val; + } + } + + // 返回结果数组 + public int[] result() { + int[] res = new int[diff.length]; + // 根据差分数组构造结果数组 + res[0] = diff[0]; + for (int i = 1; i < diff.length; i++) { + res[i] = res[i - 1] + diff[i]; + } + return res; + } + + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/range/\350\210\252\347\217\255\351\242\204\350\256\242\347\273\237\350\256\241.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/range/\350\210\252\347\217\255\351\242\204\350\256\242\347\273\237\350\256\241.java" new file mode 100644 index 0000000..bb51080 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/range/\350\210\252\347\217\255\351\242\204\350\256\242\347\273\237\350\256\241.java" @@ -0,0 +1,76 @@ +package io.github.dunwu.algorithm.array.range; + +import org.junit.jupiter.api.Assertions; + +/** + * 1109. 航班预订统计 + * + * @author Zhang Peng + * @date 2025-10-17 + */ +public class 航班预订统计 { + + public static void main(String[] args) { + + Solution s = new Solution(); + + int[][] bookings = { { 1, 2, 10 }, { 2, 3, 20 }, { 2, 5, 25 } }; + Assertions.assertArrayEquals(new int[] { 10, 55, 45, 25, 25 }, s.corpFlightBookings(bookings, 5)); + + int[][] bookings2 = { { 1, 2, 10 }, { 2, 2, 15 } }; + Assertions.assertArrayEquals(new int[] { 10, 25 }, s.corpFlightBookings(bookings2, 2)); + } + + static class Solution { + + public int[] corpFlightBookings(int[][] bookings, int n) { + int[] nums = new int[n]; + Difference df = new Difference(nums); + for (int[] booking : bookings) { + int first = booking[0], last = booking[1], seat = booking[2]; + df.increment(first - 1, last - 1, seat); + } + return df.result(); + } + + // 差分数组工具类 + static class Difference { + + // 差分数组 + private final int[] diff; + + // 输入一个初始数组,区间操作将在这个数组上进行 + public Difference(int[] nums) { + assert nums.length > 0; + diff = new int[nums.length]; + // 根据初始数组构造差分数组 + diff[0] = nums[0]; + for (int i = 1; i < nums.length; i++) { + diff[i] = nums[i] - nums[i - 1]; + } + } + + // 给闭区间 [i, j] 增加 val(可以是负数) + public void increment(int i, int j, int val) { + diff[i] += val; + if (j + 1 < diff.length) { + diff[j + 1] -= val; + } + } + + // 返回结果数组 + public int[] result() { + int[] res = new int[diff.length]; + // 根据差分数组构造结果数组 + res[0] = diff[0]; + for (int i = 1; i < diff.length; i++) { + res[i] = res[i - 1] + diff[i]; + } + return res; + } + + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/template/\344\272\214\345\210\206\346\237\245\346\211\276\346\250\241\346\235\277.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/template/\344\272\214\345\210\206\346\237\245\346\211\276\346\250\241\346\235\277.java" new file mode 100644 index 0000000..7f21032 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/template/\344\272\214\345\210\206\346\237\245\346\211\276\346\250\241\346\235\277.java" @@ -0,0 +1,70 @@ +package io.github.dunwu.algorithm.array.template; + +/** + * 二分查找模板 + * + * @author Zhang Peng + * @date 2025-12-08 + */ +public class 二分查找模板 { + + int binary_search(int[] nums, int target) { + int left = 0, right = nums.length - 1; + while (left <= right) { + int mid = left + (right - left) / 2; + if (nums[mid] < target) { + left = mid + 1; + } else if (nums[mid] > target) { + right = mid - 1; + } else if (nums[mid] == target) { + // 直接返回 + return mid; + } + } + // 直接返回 + return -1; + } + + int left_bound(int[] nums, int target) { + int left = 0, right = nums.length - 1; + while (left <= right) { + int mid = left + (right - left) / 2; + if (nums[mid] < target) { + left = mid + 1; + } else if (nums[mid] > target) { + right = mid - 1; + } else if (nums[mid] == target) { + // 别返回,锁定左侧边界 + right = mid - 1; + } + } + // 判断 target 是否存在于 nums 中 + if (left < 0 || left >= nums.length) { + return -1; + } + // 判断一下 nums[left] 是不是 target + return nums[left] == target ? left : -1; + } + + int right_bound(int[] nums, int target) { + int left = 0, right = nums.length - 1; + while (left <= right) { + int mid = left + (right - left) / 2; + if (nums[mid] < target) { + left = mid + 1; + } else if (nums[mid] > target) { + right = mid - 1; + } else if (nums[mid] == target) { + // 别返回,锁定右侧边界 + left = mid + 1; + } + } + // 由于 while 的结束条件是 right == left - 1,且现在在求右边界 + // 所以用 right 替代 left - 1 更好记 + if (right < 0 || right >= nums.length) { + return -1; + } + return nums[right] == target ? right : -1; + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/template/\345\211\215\347\274\200\345\222\214\346\225\260\347\273\204\344\273\243\347\240\201\346\250\241\346\235\277.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/template/\345\211\215\347\274\200\345\222\214\346\225\260\347\273\204\344\273\243\347\240\201\346\250\241\346\235\277.java" new file mode 100644 index 0000000..7c37f55 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/template/\345\211\215\347\274\200\345\222\214\346\225\260\347\273\204\344\273\243\347\240\201\346\250\241\346\235\277.java" @@ -0,0 +1,65 @@ +package io.github.dunwu.algorithm.array.template; + +/** + * 前缀和数组代码模板 + * + * @author Zhang Peng + * @date 2025-10-20 + */ +public class 前缀和数组代码模板 { + + /** + * 一维前缀和 + */ + static class NumArray { + + // 前缀和数组 + private final int[] preSum; + + // 输入一个数组,构造前缀和 + public NumArray(int[] nums) { + // preSum[0] = 0,便于计算累加和 + preSum = new int[nums.length + 1]; + // 计算 nums 的累加和 + for (int i = 1; i < preSum.length; i++) { + preSum[i] = preSum[i - 1] + nums[i - 1]; + } + } + + // 查询闭区间 [left, right] 的累加和 + public int sumRange(int left, int right) { + return preSum[right + 1] - preSum[left]; + } + + } + + /** + * 二维前缀和 + */ + static class NumMatrix { + + // preSum[i][j] 记录矩阵 [0, 0, i-1, j-1] 的元素和 + private int[][] preSum; + + public NumMatrix(int[][] matrix) { + int m = matrix.length, n = matrix[0].length; + if (m == 0 || n == 0) return; + // 构造前缀和矩阵 + preSum = new int[m + 1][n + 1]; + for (int i = 1; i <= m; i++) { + for (int j = 1; j <= n; j++) { + // 计算每个矩阵 [0, 0, i, j] 的元素和 + preSum[i][j] = preSum[i - 1][j] + preSum[i][j - 1] + matrix[i - 1][j - 1] - preSum[i - 1][j - 1]; + } + } + } + + // 计算子矩阵 [x1, y1, x2, y2] 的元素和 + public int sumRegion(int x1, int y1, int x2, int y2) { + // 目标矩阵之和由四个相邻矩阵运算获得 + return preSum[x2 + 1][y2 + 1] - preSum[x1][y2 + 1] - preSum[x2 + 1][y1] + preSum[x1][y1]; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/template/\345\267\256\345\210\206\346\225\260\347\273\204\344\273\243\347\240\201\346\250\241\346\235\277.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/template/\345\267\256\345\210\206\346\225\260\347\273\204\344\273\243\347\240\201\346\250\241\346\235\277.java" new file mode 100644 index 0000000..bb6a01c --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/template/\345\267\256\345\210\206\346\225\260\347\273\204\344\273\243\347\240\201\346\250\241\346\235\277.java" @@ -0,0 +1,49 @@ +package io.github.dunwu.algorithm.array.template; + +/** + * 差分数组代码模板 + * + * @author Zhang Peng + * @date 2025-10-20 + */ +public class 差分数组代码模板 { + + // 差分数组工具类 + static class Difference { + + // 差分数组 + private final int[] diff; + + // 输入一个初始数组,区间操作将在这个数组上进行 + public Difference(int[] nums) { + assert nums.length > 0; + diff = new int[nums.length]; + // 根据初始数组构造差分数组 + diff[0] = nums[0]; + for (int i = 1; i < nums.length; i++) { + diff[i] = nums[i] - nums[i - 1]; + } + } + + // 给闭区间 [i, j] 增加 val(可以是负数) + public void increment(int i, int j, int val) { + diff[i] += val; + if (j + 1 < diff.length) { + diff[j + 1] -= val; + } + } + + // 返回结果数组 + public int[] result() { + int[] res = new int[diff.length]; + // 根据差分数组构造结果数组 + res[0] = diff[0]; + for (int i = 1; i < diff.length; i++) { + res[i] = res[i - 1] + diff[i]; + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/template/\346\273\221\345\212\250\347\252\227\345\217\243\346\250\241\346\235\277.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/template/\346\273\221\345\212\250\347\252\227\345\217\243\346\250\241\346\235\277.java" new file mode 100644 index 0000000..66a46d4 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/template/\346\273\221\345\212\250\347\252\227\345\217\243\346\250\241\346\235\277.java" @@ -0,0 +1,46 @@ +package io.github.dunwu.algorithm.array.template; + +/** + * 滑动窗口模板 + * + * @author Zhang Peng + * @date 2025-11-20 + */ +public class 滑动窗口模板 { + + // 滑动窗口算法伪码框架 + // void slidingWindow(String s) { + // // 用合适的数据结构记录窗口中的数据,根据具体场景变通 + // // 比如说,我想记录窗口中元素出现的次数,就用 map + // // 如果我想记录窗口中的元素和,就可以只用一个 int + // Object window = ... + // + // int left = 0, right = 0; + // while (right < s.length()) { + // // c 是将移入窗口的字符 + // char c = s[right]; + // window.add(c) + // // 增大窗口 + // right++; + // // 进行窗口内数据的一系列更新 + // // ... + // + // // *** debug 输出的位置 *** + // // 注意在最终的解法代码中不要 print + // // 因为 IO 操作很耗时,可能导致超时 + // printf("window: [%d, %d)\n", left, right); + // // *********************** + // + // // 判断左侧窗口是否要收缩 + // while (left < right && window needs shrink){ + // // d 是将移出窗口的字符 + // char d = s[left]; + // window.remove(d) + // // 缩小窗口 + // left++; + // // 进行窗口内数据的一系列更新 + // ... + // } + // } + // } +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\344\270\211\346\225\260\344\271\213\345\222\214.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\344\270\211\346\225\260\344\271\213\345\222\214.java" new file mode 100644 index 0000000..bb147b7 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\344\270\211\346\225\260\344\271\213\345\222\214.java" @@ -0,0 +1,66 @@ +package io.github.dunwu.algorithm.array.two_pointer; + +import org.junit.jupiter.api.Assertions; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * + * 三数之和 + * + * @author Zhang Peng + * @since 2020-01-18 + */ +public class 三数之和 { + + public static void main(String[] args) { + Solution s = new Solution(); + List> input = s.threeSum(new int[] { -1, 0, 1, 2, -1, -4 }); + List> expect = new ArrayList<>(); + expect.add(Arrays.asList(-1, -1, 2)); + expect.add(Arrays.asList(-1, 0, 1)); + Assertions.assertArrayEquals(expect.toArray(), input.toArray()); + } + + static class Solution { + + public List> threeSum(int[] nums) { + if (nums == null || nums.length < 3) { return new ArrayList<>(); } + + // 数组排序 + Arrays.sort(nums); + + List> res = new ArrayList<>(); + for (int i = 0; i < nums.length; i++) { + + // 跳过重复元素 + if (i > 0 && nums[i] == nums[i - 1]) { continue; } + + // 双指针,目标是找到 nums[l] + nums[r] = -nums[i] + int target = -nums[i]; + int l = i + 1, r = nums.length - 1; + + while (l < r) { + int sum = nums[l] + nums[r]; + if (sum == target) { + res.add(Arrays.asList(nums[i], nums[l], nums[r])); + l++; + r--; + // 跳过重复元素 + while (l < r && nums[l] == nums[l - 1]) l++; + while (l < r && nums[r] == nums[r + 1]) r--; + } else if (sum > target) { + r--; + } else if (sum < target) { + l++; + } + } + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\344\270\244\346\225\260\344\271\213\345\222\214.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\344\270\244\346\225\260\344\271\213\345\222\214.java" new file mode 100644 index 0000000..27e751f --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\344\270\244\346\225\260\344\271\213\345\222\214.java" @@ -0,0 +1,67 @@ +package io.github.dunwu.algorithm.array.two_pointer; + +import org.junit.jupiter.api.Assertions; + +import java.util.HashMap; +import java.util.Map; + +/** + * 1. 两数之和 + * + * @author Zhang Peng + * @since 2020-06-05 + */ +public class 两数之和 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertArrayEquals(new int[] { 0, 1 }, s.twoSum(new int[] { 2, 7, 11, 15 }, 9)); + Assertions.assertArrayEquals(new int[] { 1, 2 }, s.twoSum(new int[] { 3, 2, 4 }, 6)); + Assertions.assertArrayEquals(new int[] { 0, 1 }, s.twoSum(new int[] { 3, 3 }, 6)); + + Solution2 s2 = new Solution2(); + Assertions.assertArrayEquals(new int[] { 0, 1 }, s2.twoSum(new int[] { 2, 7, 11, 15 }, 9)); + Assertions.assertArrayEquals(new int[] { 1, 2 }, s2.twoSum(new int[] { 3, 2, 4 }, 6)); + Assertions.assertArrayEquals(new int[] { 0, 1 }, s2.twoSum(new int[] { 3, 3 }, 6)); + } + + /** + * 两次 for 循环暴力求解,时间复杂度 o(n^2) + */ + static class Solution { + + public int[] twoSum(int[] nums, int target) { + int n = nums.length; + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + if (nums[i] + nums[j] == target) { + return new int[] { i, j }; + } + } + } + return new int[0]; + } + + } + + /** + * Hash 存值、下标,一次 for 循环,每次判断 map 中是否有值和当前下标的值凑成 target + */ + static class Solution2 { + + public int[] twoSum(int[] nums, int target) { + Map map = new HashMap<>(nums.length); + for (int i = 0; i < nums.length; i++) { + int diff = target - nums[i]; + if (map.containsKey(diff)) { + return new int[] { map.get(diff), i }; + } else { + map.put(nums[i], i); + } + } + return new int[0]; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\344\270\244\346\225\260\344\271\213\345\222\2142.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\344\270\244\346\225\260\344\271\213\345\222\2142.java" new file mode 100644 index 0000000..2585dda --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\344\270\244\346\225\260\344\271\213\345\222\2142.java" @@ -0,0 +1,69 @@ +package io.github.dunwu.algorithm.array.two_pointer; + +import org.junit.jupiter.api.Assertions; + +import java.util.HashMap; +import java.util.Map; + +/** + * 167. 两数之和 II - 输入有序数组 + * + * @author Zhang Peng + * @since 2020-06-05 + */ +public class 两数之和2 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertArrayEquals(new int[] { 1, 2 }, s.twoSum(new int[] { 2, 7, 11, 15 }, 9)); + Assertions.assertArrayEquals(new int[] { 1, 3 }, s.twoSum(new int[] { 2, 3, 4 }, 6)); + Assertions.assertArrayEquals(new int[] { 1, 2 }, s.twoSum(new int[] { -1, 0 }, -1)); + + Solution2 s2 = new Solution2(); + Assertions.assertArrayEquals(new int[] { 1, 2 }, s2.twoSum(new int[] { 2, 7, 11, 15 }, 9)); + Assertions.assertArrayEquals(new int[] { 1, 3 }, s2.twoSum(new int[] { 2, 3, 4 }, 6)); + Assertions.assertArrayEquals(new int[] { 1, 2 }, s2.twoSum(new int[] { -1, 0 }, -1)); + } + + /** + * Hash 存值、下标,一次 for 循环,每次判断 map 中是否有值和当前下标的值凑成 target + */ + static class Solution { + + public int[] twoSum(int[] nums, int target) { + Map map = new HashMap<>(nums.length); + for (int i = 0; i < nums.length; i++) { + int diff = target - nums[i]; + if (map.containsKey(diff)) { + return new int[] { map.get(diff), i + 1 }; + } else { + map.put(nums[i], i + 1); + } + } + return new int[0]; + } + + } + + /** + * 双指针 + */ + static class Solution2 { + + public int[] twoSum(int[] nums, int target) { + int left = 0, right = nums.length - 1; + while (left < right) { + if (nums[left] + nums[right] == target) { + return new int[] { left + 1, right + 1 }; + } else if (nums[left] + nums[right] < target) { + left++; + } else { + right--; + } + } + return new int[0]; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\344\272\214\347\273\264\347\275\221\346\240\274\350\277\201\347\247\273.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\344\272\214\347\273\264\347\275\221\346\240\274\350\277\201\347\247\273.java" new file mode 100644 index 0000000..1e7c9e1 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\344\272\214\347\273\264\347\275\221\346\240\274\350\277\201\347\247\273.java" @@ -0,0 +1,92 @@ +package io.github.dunwu.algorithm.array.two_pointer; + +import org.junit.jupiter.api.Assertions; + +import java.util.ArrayList; +import java.util.List; + +/** + * 1260. 二维网格迁移 + * + * @author Zhang Peng + * @date 2025-10-14 + */ +public class 二维网格迁移 { + + public static void main(String[] args) { + + Solution s = new Solution(); + + int[][] grid1 = new int[][] { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } }; + List> res1 = s.shiftGrid(grid1, 1); + Assertions.assertNotNull(res1); + Assertions.assertArrayEquals(new Integer[] { 9, 1, 2 }, res1.get(0).toArray(new Integer[0])); + Assertions.assertArrayEquals(new Integer[] { 3, 4, 5 }, res1.get(1).toArray(new Integer[0])); + Assertions.assertArrayEquals(new Integer[] { 6, 7, 8 }, res1.get(2).toArray(new Integer[0])); + + int[][] grid2 = new int[][] { { 3, 8, 1, 9 }, { 19, 7, 2, 5 }, { 4, 6, 11, 10 }, { 12, 0, 21, 13 } }; + List> res2 = s.shiftGrid(grid2, 4); + Assertions.assertNotNull(res2); + Assertions.assertArrayEquals(new Integer[] { 12, 0, 21, 13 }, res2.get(0).toArray(new Integer[0])); + Assertions.assertArrayEquals(new Integer[] { 3, 8, 1, 9 }, res2.get(1).toArray(new Integer[0])); + Assertions.assertArrayEquals(new Integer[] { 19, 7, 2, 5 }, res2.get(2).toArray(new Integer[0])); + Assertions.assertArrayEquals(new Integer[] { 4, 6, 11, 10 }, res2.get(3).toArray(new Integer[0])); + + int[][] grid3 = new int[][] { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } }; + List> res3 = s.shiftGrid(grid3, 9); + Assertions.assertNotNull(res3); + Assertions.assertArrayEquals(new Integer[] { 1, 2, 3 }, res3.get(0).toArray(new Integer[0])); + Assertions.assertArrayEquals(new Integer[] { 4, 5, 6 }, res3.get(1).toArray(new Integer[0])); + Assertions.assertArrayEquals(new Integer[] { 7, 8, 9 }, res3.get(2).toArray(new Integer[0])); + + int[][] grid4 = new int[][] { { 1 }, { 2 }, { 3 }, { 4 }, { 7 }, { 6 }, { 5 } }; + List> res4 = s.shiftGrid(grid4, 23); + Assertions.assertNotNull(res4); + } + + static class Solution { + + public List> shiftGrid(int[][] grid, int k) { + for (int i = 0; i < k; i++) { + shift(grid); + } + + int m = grid.length, n = grid[0].length; + List> res = new ArrayList<>(); + for (int i = 0; i < m; i++) { + List list = new ArrayList<>(); + res.add(list); + for (int j = 0; j < n; j++) { + list.add(grid[i][j]); + } + } + return res; + } + + public void shift(int[][] grid) { + int m = grid.length, n = grid[0].length; + int last = get(grid, m * n - 1); + for (int i = m * n - 1; i > 0; i--) { + int prev = get(grid, i - 1); + set(grid, i, prev); + } + set(grid, 0, last); + } + + public int get(int[][] grid, int index) { + int n = grid[0].length; + int i = index / n; + int j = index % n; + return grid[i][j]; + } + + public void set(int[][] grid, int index, int val) { + int n = grid[0].length; + int i = index / n; + int j = index % n; + grid[i][j] = val; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\344\272\214\350\277\233\345\210\266\346\261\202\345\222\214.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\344\272\214\350\277\233\345\210\266\346\261\202\345\222\214.java" new file mode 100644 index 0000000..812604b --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\344\272\214\350\277\233\345\210\266\346\261\202\345\222\214.java" @@ -0,0 +1,46 @@ +package io.github.dunwu.algorithm.array.two_pointer; + +import org.junit.jupiter.api.Assertions; + +/** + * 67. 二进制求和 + * + * @author Zhang Peng + * @date 2025-01-21 + */ +public class 二进制求和 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals("100", s.addBinary("11", "1")); + Assertions.assertEquals("10101", s.addBinary("1010", "1011")); + } + + static class Solution { + + public String addBinary(String a, String b) { + int i = a.length() - 1; + int j = b.length() - 1; + int carry = 0; + StringBuilder sb = new StringBuilder(); + while (i >= 0 || j >= 0) { + int numA = i < 0 ? 0 : a.charAt(i--) - '0'; + int numB = j < 0 ? 0 : b.charAt(j--) - '0'; + int sum = numA + numB + carry; + if (sum > 1) { + carry = 1; + sb.append(sum % 2); + } else { + carry = 0; + sb.append(sum); + } + } + if (carry > 0) { + sb.append(carry); + } + return sb.reverse().toString(); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271.java" new file mode 100644 index 0000000..ce195a8 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271.java" @@ -0,0 +1,37 @@ +package io.github.dunwu.algorithm.array.two_pointer; + +import org.junit.jupiter.api.Assertions; + +/** + * 26. 删除有序数组中的重复项 + * + * @author Zhang Peng + * @since 2018-11-05 + */ +public class 删除排序数组中的重复项 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(2, s.removeDuplicates(new int[] { 1, 1, 2 })); + Assertions.assertEquals(5, s.removeDuplicates(new int[] { 0, 0, 1, 1, 1, 2, 2, 3, 3, 4 })); + Assertions.assertEquals(2, s.removeDuplicates(new int[] { 1, 2 })); + Assertions.assertEquals(1, s.removeDuplicates(new int[] { 2, 2 })); + } + + static class Solution { + + public int removeDuplicates(int[] nums) { + int slow = 0, fast = 1; + while (fast < nums.length) { + if (nums[fast] != nums[slow]) { + slow++; + nums[slow] = nums[fast]; + } + fast++; + } + return slow + 1; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\2712.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\2712.java" new file mode 100644 index 0000000..ad47707 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\2712.java" @@ -0,0 +1,45 @@ +package io.github.dunwu.algorithm.array.two_pointer; + +import org.junit.jupiter.api.Assertions; + +/** + * 80. 删除有序数组中的重复项 II + * + * @author Zhang Peng + * @since 2018-11-05 + */ +public class 删除排序数组中的重复项2 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(5, s.removeDuplicates(new int[] { 1, 1, 1, 2, 2, 3 })); + Assertions.assertEquals(7, s.removeDuplicates(new int[] { 0, 0, 1, 1, 1, 1, 2, 3, 3 })); + } + + static class Solution { + + public int removeDuplicates(int[] nums) { + int slow = 0, fast = 1; + int cnt = 1; + while (fast < nums.length) { + if (nums[fast] != nums[slow]) { + cnt = 1; + slow++; + nums[slow] = nums[fast]; + } else { + if (cnt < 2) { + slow++; + nums[slow] = nums[fast]; + } + cnt++; + } + fast++; + } + // System.out.printf("slow: %d, fast: %d, nums: %s\n", slow, fast, + // JSONUtil.toJsonStr(ArrayUtil.sub(nums, 0, slow + 1))); + return slow + 1; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262.java" new file mode 100644 index 0000000..75e2bc1 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262.java" @@ -0,0 +1,39 @@ +package io.github.dunwu.algorithm.array.two_pointer; + +import org.junit.jupiter.api.Assertions; + +/** + * 344. 反转字符串 + * + * @author Zhang Peng + * @since 2020-06-05 + */ +public class 反转字符串 { + + public static void main(String[] args) { + Solution s = new Solution(); + char[] arr1 = new char[] { 'h', 'e', 'l', 'l', 'o' }; + s.reverseString(arr1); + Assertions.assertArrayEquals(new char[] { 'o', 'l', 'l', 'e', 'h' }, arr1); + + char[] arr2 = new char[] { 'H', 'a', 'n', 'n', 'a', 'h' }; + s.reverseString(arr2); + Assertions.assertArrayEquals(new char[] { 'h', 'a', 'n', 'n', 'a', 'H' }, arr2); + } + + static class Solution { + + public void reverseString(char[] s) { + int left = 0, right = s.length - 1; + while (left < right) { + char temp = s[left]; + s[left] = s[right]; + s[right] = temp; + left++; + right--; + } + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204.java" new file mode 100644 index 0000000..7465ea6 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204.java" @@ -0,0 +1,67 @@ +package io.github.dunwu.algorithm.array.two_pointer; + +import org.junit.jupiter.api.Assertions; + +/** + * 88. 合并两个有序数组 + * + * @author Zhang Peng + * @since 2025-08-06 + */ +public class 合并两个有序数组 { + + public static void main(String[] args) { + + Solution s = new Solution(); + + int[] nums1 = new int[] { 1, 2, 3, 0, 0, 0 }; + int[] nums2 = new int[] { 2, 5, 6 }; + s.merge(nums1, 3, nums2, 3); + Assertions.assertArrayEquals(new int[] { 1, 2, 2, 3, 5, 6 }, nums1); + + int[] nums3 = new int[] { 1 }; + int[] nums4 = new int[] {}; + s.merge(nums3, 1, nums4, 0); + Assertions.assertArrayEquals(new int[] { 1 }, nums3); + + int[] nums5 = new int[] { 0 }; + int[] nums6 = new int[] { 1 }; + s.merge(nums5, 0, nums6, 1); + Assertions.assertArrayEquals(new int[] { 1 }, nums5); + + int[] nums7 = new int[] { 4, 5, 6, 0, 0, 0 }; + int[] nums8 = new int[] { 1, 2, 3 }; + s.merge(nums7, 3, nums8, 3); + Assertions.assertArrayEquals(new int[] { 1, 2, 3, 4, 5, 6 }, nums7); + } + + static class Solution { + + public void merge(int[] nums1, int m, int[] nums2, int n) { + // 两个指针分别初始化在两个数组的最后一个元素(类似拉链两端的锯齿) + int i = m - 1, j = n - 1; + // 生成排序的结果(类似拉链的拉锁) + int p = nums1.length - 1; + // 从后向前生成结果数组,类似合并两个有序链表的逻辑 + while (i >= 0 && j >= 0) { + if (nums1[i] >= nums2[j]) { + nums1[p] = nums1[i]; + i--; + } else { + nums1[p] = nums2[j]; + j--; + } + p--; + } + // 可能其中一个数组的指针走到尽头了,而另一个还没走完 + while (i >= 0) { + nums1[p--] = nums1[i--]; + } + while (j >= 0) { + nums1[p--] = nums2[j--]; + } + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\345\220\210\345\271\266\345\214\272\351\227\264.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\345\220\210\345\271\266\345\214\272\351\227\264.java" new file mode 100644 index 0000000..d424a3e --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\345\220\210\345\271\266\345\214\272\351\227\264.java" @@ -0,0 +1,62 @@ +package io.github.dunwu.algorithm.array.two_pointer; + +import org.junit.jupiter.api.Assertions; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * 56. 合并区间 + * + * @author Zhang Peng + * @since 2020-07-29 + */ +public class 合并区间 { + + public static void main(String[] args) { + + Solution s = new Solution(); + + int[][] input = new int[][] { { 1, 4 }, { 2, 3 } }; + int[][] expect = new int[][] { { 1, 4 } }; + Assertions.assertArrayEquals(expect, s.merge(input)); + + int[][] input2 = new int[][] { { 1, 3 }, { 2, 6 }, { 8, 10 }, { 15, 18 } }; + int[][] expect2 = new int[][] { { 1, 6 }, { 8, 10 }, { 15, 18 } }; + Assertions.assertArrayEquals(expect2, s.merge(input2)); + + int[][] input3 = new int[][] { { 1, 4 }, { 4, 5 } }; + int[][] expect3 = new int[][] { { 1, 5 } }; + Assertions.assertArrayEquals(expect3, s.merge(input3)); + } + + static class Solution { + + public int[][] merge(int[][] intervals) { + + // base case + if (intervals == null || intervals.length <= 1) { return intervals; } + + // 先按区间下限排序 + Arrays.sort(intervals, (a, b) -> a[0] - b[0]); + + // 设置双指针,扫描 intervals + List merged = new ArrayList<>(); + for (int[] interval : intervals) { + int l = interval[0], r = interval[1]; + int last = merged.size() - 1; + if (last == -1 || merged.get(last)[1] < l) { + merged.add(new int[] { l, r }); + } else { + l = merged.get(last)[0]; + r = Math.max(merged.get(last)[1], r); + merged.set(last, new int[] { l, r }); + } + } + return merged.toArray(new int[merged.size()][2]); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\345\260\206\347\237\251\351\230\265\346\214\211\345\257\271\350\247\222\347\272\277\346\216\222\345\272\217.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\345\260\206\347\237\251\351\230\265\346\214\211\345\257\271\350\247\222\347\272\277\346\216\222\345\272\217.java" new file mode 100644 index 0000000..cabacab --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\345\260\206\347\237\251\351\230\265\346\214\211\345\257\271\350\247\222\347\272\277\346\216\222\345\272\217.java" @@ -0,0 +1,70 @@ +package io.github.dunwu.algorithm.array.two_pointer; + +import org.junit.jupiter.api.Assertions; + +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.PriorityQueue; + +/** + * 1329. 将矩阵按对角线排序 + * + * @author Zhang Peng + * @since 2025-08-06 + */ +public class 将矩阵按对角线排序 { + + public static void main(String[] args) { + + Solution s = new Solution(); + + int[][] input1 = { { 3, 3, 1, 1 }, { 2, 2, 1, 2 }, { 1, 1, 1, 2 } }; + int[][] expected1 = { { 1, 1, 1, 1 }, { 1, 2, 2, 2 }, { 1, 2, 3, 3 } }; + int[][] output1 = s.diagonalSort(input1); + Assertions.assertArrayEquals(expected1, output1); + + int[][] input2 = { { 11, 25, 66, 1, 69, 7 }, { 23, 55, 17, 45, 15, 52 }, { 75, 31, 36, 44, 58, 8 }, + { 22, 27, 33, 25, 68, 4 }, { 84, 28, 14, 11, 5, 50 } }; + int[][] expected2 = { { 5, 17, 4, 1, 52, 7 }, { 11, 11, 25, 45, 8, 69 }, { 14, 23, 25, 44, 58, 15 }, + { 22, 27, 31, 36, 50, 66 }, { 84, 28, 75, 33, 55, 68 } }; + int[][] output2 = s.diagonalSort(input2); + Assertions.assertArrayEquals(expected2, output2); + } + + static class Solution { + + public int[][] diagonalSort(int[][] mat) { + + int m = mat.length, n = mat[0].length; + + // 在同一个对角线上的元素,其横纵坐标之差是相同的 + // 存储所有对角线的元素列表,利用 PriorityQueue 自动对对角线元素排序 + Map> map = new HashMap<>(); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + // 横纵坐标之差可以作为一条对角线的 ID + int diff = i - j; + if (!map.containsKey(diff)) { + map.put(diff, new PriorityQueue<>(Comparator.comparingInt(a -> mat[a[0]][a[1]]))); + } + map.get(diff).add(new int[] { i, j }); + } + } + + // 把排序结果回填二维矩阵 + int[][] res = new int[m][n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + int diff = i - j; + PriorityQueue queue = map.get(diff); + int[] point = queue.poll(); + res[i][j] = mat[point[0]][point[1]]; + } + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\346\234\200\351\225\277\345\205\254\345\205\261\345\211\215\347\274\200.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\346\234\200\351\225\277\345\205\254\345\205\261\345\211\215\347\274\200.java" new file mode 100644 index 0000000..008fb47 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\346\234\200\351\225\277\345\205\254\345\205\261\345\211\215\347\274\200.java" @@ -0,0 +1,49 @@ +package io.github.dunwu.algorithm.array.two_pointer; + +import org.junit.jupiter.api.Assertions; + +/** + * 14. 最长公共前缀 + * + * @author Zhang Peng + * @since 2025-08-06 + */ +public class 最长公共前缀 { + + public static void main(String[] args) { + + Solution s = new Solution(); + String[] input1 = { "flower", "flow", "flight" }; + String expect1 = "fl"; + String output1 = s.longestCommonPrefix(input1); + Assertions.assertEquals(expect1, output1); + + String[] input2 = { "dog", "racecar", "car" }; + String expect2 = ""; + String output2 = s.longestCommonPrefix(input2); + Assertions.assertEquals(expect2, output2); + } + + static class Solution { + + public String longestCommonPrefix(String[] strs) { + int m = strs.length; + // 以第一行的列数为基准 + int n = strs[0].length(); + for (int col = 0; col < n; col++) { + for (int row = 1; row < m; row++) { + String cur = strs[row], prev = strs[row - 1]; + // 判断每个字符串的 col 索引是否都相同 + if (col >= cur.length() || col >= prev.length() || + cur.charAt(col) != prev.charAt(col)) { + // 发现不匹配的字符,只有 strs[row][0..col-1] 是公共前缀 + return strs[row].substring(0, col); + } + } + } + return strs[0]; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\346\234\200\351\225\277\345\233\236\346\226\207\345\255\220\344\270\262.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\346\234\200\351\225\277\345\233\236\346\226\207\345\255\220\344\270\262.java" new file mode 100644 index 0000000..c42a437 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\346\234\200\351\225\277\345\233\236\346\226\207\345\255\220\344\270\262.java" @@ -0,0 +1,84 @@ +package io.github.dunwu.algorithm.array.two_pointer; + +import org.junit.jupiter.api.Assertions; + +/** + * 5. 最长回文子串 + * + * @author Zhang Peng + * @date 2025-11-10 + */ +public class 最长回文子串 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals("bab", s.longestPalindrome("babad")); + Assertions.assertEquals("bb", s.longestPalindrome("cbbd")); + Assertions.assertEquals("a", s.longestPalindrome("a")); + Assertions.assertEquals("bb", s.longestPalindrome("bb")); + + Solution2 s2 = new Solution2(); + Assertions.assertEquals("aba", s2.longestPalindrome("babad")); + Assertions.assertEquals("bb", s2.longestPalindrome("cbbd")); + Assertions.assertEquals("a", s2.longestPalindrome("a")); + Assertions.assertEquals("bb", s2.longestPalindrome("bb")); + } + + /** + * 双指针判断回文串 + 暴力解决,时间复杂度 o(n^2) + */ + static class Solution { + + public String longestPalindrome(String s) { + String res = s.substring(0, 1); + for (int i = 0; i < s.length(); i++) { + for (int j = i + 1; j < s.length(); j++) { + if (isPalindrome(s, i, j)) { + int len = j - i + 1; + if (len > res.length()) { + res = s.substring(i, j + 1); + } + } + } + } + return res; + } + + public boolean isPalindrome(String s, int left, int right) { + while (left < right) { + if (s.charAt(left) != s.charAt(right)) { + return false; + } + left++; + right--; + } + return true; + } + + } + + static class Solution2 { + + public String longestPalindrome(String s) { + String res = ""; + for (int i = 0; i < s.length(); i++) { + String s1 = palindrome(s, i, i); + String s2 = palindrome(s, i, i + 1); + res = res.length() > s1.length() ? res : s1; + res = res.length() > s2.length() ? res : s2; + } + return res; + } + + public String palindrome(String s, int l, int r) { + while (l >= 0 && r < s.length() + && s.charAt(l) == s.charAt(r)) { + l--; + r++; + } + return s.substring(l + 1, r); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\346\234\211\345\272\217\346\225\260\347\273\204\347\232\204\345\271\263\346\226\271.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\346\234\211\345\272\217\346\225\260\347\273\204\347\232\204\345\271\263\346\226\271.java" new file mode 100644 index 0000000..e9324ca --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\346\234\211\345\272\217\346\225\260\347\273\204\347\232\204\345\271\263\346\226\271.java" @@ -0,0 +1,49 @@ +package io.github.dunwu.algorithm.array.two_pointer; + +import org.junit.jupiter.api.Assertions; + +/** + * 977. 有序数组的平方 + * + * @author Zhang Peng + * @since 2025-08-06 + */ +public class 有序数组的平方 { + + public static void main(String[] args) { + + Solution s = new Solution(); + + int[] input1 = { -4, -1, 0, 3, 10 }; + int[] expect1 = { 0, 1, 9, 16, 100 }; + int[] output1 = s.sortedSquares(input1); + Assertions.assertArrayEquals(expect1, output1); + + int[] input2 = { -7, -3, 2, 3, 11 }; + int[] expect2 = { 4, 9, 9, 49, 121 }; + int[] output2 = s.sortedSquares(input2); + Assertions.assertArrayEquals(expect2, output2); + } + + public static class Solution { + + public int[] sortedSquares(int[] nums) { + int p = nums.length - 1; + int i = 0, j = nums.length - 1; + int[] res = new int[nums.length]; + while (i <= j) { + if (Math.abs(nums[i]) > Math.abs(nums[j])) { + res[p] = nums[i] * nums[i]; + i++; + } else { + res[p] = nums[j] * nums[j]; + j--; + } + p--; + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\346\234\211\345\272\217\347\237\251\351\230\265\344\270\255\347\254\254K\345\260\217\347\232\204\345\205\203\347\264\240.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\346\234\211\345\272\217\347\237\251\351\230\265\344\270\255\347\254\254K\345\260\217\347\232\204\345\205\203\347\264\240.java" new file mode 100644 index 0000000..59232f3 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\346\234\211\345\272\217\347\237\251\351\230\265\344\270\255\347\254\254K\345\260\217\347\232\204\345\205\203\347\264\240.java" @@ -0,0 +1,64 @@ +package io.github.dunwu.algorithm.array.two_pointer; + +import org.junit.jupiter.api.Assertions; + +import java.util.PriorityQueue; + +/** + * 378. 有序矩阵中第 K 小的元素 + * + * @author Zhang Peng + * @date 2025-01-21 + */ +public class 有序矩阵中第K小的元素 { + + public static void main(String[] args) { + + Solution s = new Solution(); + + int[][] matrix = { { 1, 5, 9 }, { 10, 11, 13 }, { 12, 13, 15 } }; + Assertions.assertEquals(13, s.kthSmallest(matrix, 8)); + + int[][] matrix2 = { { -5 } }; + Assertions.assertEquals(-5, s.kthSmallest(matrix2, 1)); + + int[][] matrix3 = { { 1, 2 }, { 1, 3 } }; + Assertions.assertEquals(1, s.kthSmallest(matrix3, 2)); + + int[][] matrix4 = { { 1, 2, 3 }, { 1, 2, 3 }, { 1, 2, 3 } }; + Assertions.assertEquals(3, s.kthSmallest(matrix4, 8)); + } + + static class Solution { + + public int kthSmallest(int[][] matrix, int k) { + // 存储二元组 (matrix[i][j], i, j) + // i, j 记录当前元素的索引位置,用于生成下一个节点 + PriorityQueue pq = new PriorityQueue<>((a, b) -> { + // 按照元素大小升序排序 + return a[0] - b[0]; + }); + + // 初始化优先级队列,把每一行的第一个元素装进去 + for (int i = 0; i < matrix.length; i++) { + pq.offer(new int[] { matrix[i][0], i, 0 }); + } + + int res = -1; + while (!pq.isEmpty() && k > 0) { + int[] cur = pq.poll(); + res = cur[0]; + k--; + + // 链表中的下一个节点加入优先级队列 + int i = cur[1], j = cur[2]; + if (j + 1 < matrix[i].length) { + pq.add(new int[] { matrix[i][j + 1], i, j + 1 }); + } + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\346\237\245\346\211\276\345\222\214\346\234\200\345\260\217\347\232\204K\345\257\271\346\225\260\345\255\227.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\346\237\245\346\211\276\345\222\214\346\234\200\345\260\217\347\232\204K\345\257\271\346\225\260\345\255\227.java" new file mode 100644 index 0000000..89cfd6a --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\346\237\245\346\211\276\345\222\214\346\234\200\345\260\217\347\232\204K\345\257\271\346\225\260\345\255\227.java" @@ -0,0 +1,52 @@ +package io.github.dunwu.algorithm.array.two_pointer; + +import cn.hutool.json.JSONUtil; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.PriorityQueue; + +/** + * 373. 查找和最小的 K 对数字 + * + * @author Zhang Peng + * @date 2025-01-21 + */ +public class 查找和最小的K对数字 { + + public static void main(String[] args) { + Solution s = new Solution(); + List> expectList1 = new ArrayList<>(); + expectList1.add(Arrays.asList(1, 2)); + expectList1.add(Arrays.asList(1, 4)); + expectList1.add(Arrays.asList(1, 6)); + List> list1 = s.kSmallestPairs(new int[] { 1, 7, 11 }, new int[] { 2, 4, 6 }, 3); + System.out.println(JSONUtil.toJsonStr(list1)); + + List> list2 = s.kSmallestPairs(new int[] { 1, 1, 2 }, new int[] { 1, 2, 3 }, 2); + System.out.println(JSONUtil.toJsonStr(list2)); + } + + static class Solution { + + public List> kSmallestPairs(int[] nums1, int[] nums2, int k) { + PriorityQueue queue = new PriorityQueue<>(Comparator.comparingInt(a -> (a[0] + a[1]))); + for (int i = 0; i < nums1.length; i++) { + for (int j = 0; j < nums2.length; j++) { + queue.offer(new int[] { nums1[i], nums2[j] }); + } + } + + List> res = new ArrayList<>(); + for (int i = 0; i < k; i++) { + int[] element = queue.poll(); + res.add(Arrays.asList(element[0], element[1])); + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\346\237\245\346\211\276\346\200\273\344\273\267\346\240\274\344\270\272\347\233\256\346\240\207\345\200\274\347\232\204\344\270\244\344\270\252\345\225\206\345\223\201.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\346\237\245\346\211\276\346\200\273\344\273\267\346\240\274\344\270\272\347\233\256\346\240\207\345\200\274\347\232\204\344\270\244\344\270\252\345\225\206\345\223\201.java" new file mode 100644 index 0000000..626bd5b --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\346\237\245\346\211\276\346\200\273\344\273\267\346\240\274\344\270\272\347\233\256\346\240\207\345\200\274\347\232\204\344\270\244\344\270\252\345\225\206\345\223\201.java" @@ -0,0 +1,39 @@ +package io.github.dunwu.algorithm.array.two_pointer; + +import org.junit.jupiter.api.Assertions; + +import java.util.HashSet; +import java.util.Set; + +/** + * LCR 179. 查找总价格为目标值的两个商品 + * + * @author Zhang Peng + * @since 2020-06-05 + */ +public class 查找总价格为目标值的两个商品 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertArrayEquals(new int[] { 3, 15 }, s.twoSum(new int[] { 3, 9, 12, 15 }, 18)); + Assertions.assertArrayEquals(new int[] { 27, 34 }, s.twoSum(new int[] { 8, 21, 27, 34, 52, 66 }, 61)); + } + + static class Solution { + + public int[] twoSum(int[] nums, int target) { + Set set = new HashSet<>(); + for (int num : nums) { + int diff = target - num; + if (set.contains(diff)) { + return new int[] { num, diff }; + } else { + set.add(num); + } + } + return new int[0]; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\347\247\273\345\212\250\351\233\266.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\347\247\273\345\212\250\351\233\266.java" new file mode 100644 index 0000000..b682999 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\347\247\273\345\212\250\351\233\266.java" @@ -0,0 +1,50 @@ +package io.github.dunwu.algorithm.array.two_pointer; + +import org.junit.jupiter.api.Assertions; + +/** + * 283. 移动零 + * + * @author Zhang Peng + * @since 2018-11-05 + */ +public class 移动零 { + + public static void main(String[] args) { + + Solution s = new Solution(); + + int[] arr1 = { 0, 1, 0, 3, 12 }; + s.moveZeroes(arr1); + Assertions.assertArrayEquals(new int[] { 1, 3, 12, 0, 0 }, arr1); + + int[] arr2 = { 0, 0, 1 }; + s.moveZeroes(arr2); + Assertions.assertArrayEquals(new int[] { 1, 0, 0 }, arr2); + + int[] arr3 = { 0 }; + s.moveZeroes(arr3); + Assertions.assertArrayEquals(new int[] { 0 }, arr3); + } + + public static class Solution { + + public void moveZeroes(int[] nums) { + // slow 指针维护所有不为 0 的元素 + int slow = 0, fast = 0; + while (fast < nums.length) { + if (nums[fast] != 0) { + nums[slow] = nums[fast]; + slow++; + } + fast++; + } + // 后续补零 + for (int i = slow; i < nums.length; i++) { + nums[i] = 0; + } + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\347\247\273\351\231\244\345\205\203\347\264\240.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\347\247\273\351\231\244\345\205\203\347\264\240.java" new file mode 100644 index 0000000..52992a1 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\347\247\273\351\231\244\345\205\203\347\264\240.java" @@ -0,0 +1,43 @@ +package io.github.dunwu.algorithm.array.two_pointer; + +import org.junit.jupiter.api.Assertions; + +/** + * 27. 移除元素 + * + * @author Zhang Peng + * @since 2018-11-05 + */ +public class 移除元素 { + + public static void main(String[] args) { + + Solution s = new Solution(); + + int[] arr1 = { 3, 2, 2, 3 }; + Assertions.assertEquals(2, s.removeElement(arr1, 3)); + + int[] arr2 = { 0, 1, 2, 2, 3, 0, 4, 2 }; + Assertions.assertEquals(5, s.removeElement(arr2, 2)); + + int[] arr3 = { 1 }; + Assertions.assertEquals(0, s.removeElement(arr3, 1)); + } + + static class Solution { + + public int removeElement(int[] nums, int val) { + int slow = 0, fast = 0; + while (fast < nums.length) { + if (nums[fast] != val) { + nums[slow] = nums[fast]; + slow++; + } + fast++; + } + return slow; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\350\275\254\347\275\256\347\237\251\351\230\265.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\350\275\254\347\275\256\347\237\251\351\230\265.java" new file mode 100644 index 0000000..92104fe --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\350\275\254\347\275\256\347\237\251\351\230\265.java" @@ -0,0 +1,41 @@ +package io.github.dunwu.algorithm.array.two_pointer; + +import org.junit.jupiter.api.Assertions; + +/** + * 1329. 将矩阵按对角线排序 + * + * @author Zhang Peng + * @since 2025-08-06 + */ +public class 转置矩阵 { + + public static void main(String[] args) { + Solution s = new Solution(); + int[][] input1 = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } }; + int[][] expect1 = { { 1, 4, 7 }, { 2, 5, 8 }, { 3, 6, 9 } }; + int[][] output1 = s.transpose(input1); + Assertions.assertArrayEquals(expect1, output1); + + int[][] input2 = { { 1, 2, 3 }, { 4, 5, 6 } }; + int[][] expect2 = { { 1, 4 }, { 2, 5 }, { 3, 6 } }; + int[][] output2 = s.transpose(input2); + Assertions.assertArrayEquals(expect2, output2); + } + + public static class Solution { + + public int[][] transpose(int[][] matrix) { + int m = matrix.length, n = matrix[0].length; + int[][] res = new int[n][m]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + res[j][i] = matrix[i][j]; + } + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\351\242\234\350\211\262\345\210\206\347\261\273.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\351\242\234\350\211\262\345\210\206\347\261\273.java" new file mode 100644 index 0000000..d8d6d83 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\351\242\234\350\211\262\345\210\206\347\261\273.java" @@ -0,0 +1,53 @@ +package io.github.dunwu.algorithm.array.two_pointer; + +import cn.hutool.json.JSONUtil; +import org.junit.jupiter.api.Assertions; + +/** + * 75. 颜色分类 + * + * @author Zhang Peng + * @since 2025-08-06 + */ +public class 颜色分类 { + + public static void main(String[] args) { + + Solution s = new Solution(); + + int[] nums1 = { 2, 0, 2, 1, 1, 0 }; + s.sortColors(nums1); + Assertions.assertArrayEquals(new int[] { 0, 0, 1, 1, 2, 2 }, nums1); + + int[] nums2 = { 2, 0, 1 }; + s.sortColors(nums2); + Assertions.assertArrayEquals(new int[] { 0, 1, 2 }, nums2); + } + + static class Solution { + + public void sortColors(int[] nums) { + moveToTail(nums, 1); + System.out.println("nums = " + JSONUtil.toJsonStr(nums)); + moveToTail(nums, 2); + System.out.println("nums = " + JSONUtil.toJsonStr(nums)); + } + + public void moveToTail(int[] nums, int val) { + if (nums == null || nums.length == 0) { return; } + int slow = 0, fast = 0; + while (fast < nums.length) { + if (nums[fast] != val) { + nums[slow] = nums[fast]; + slow++; + } + fast++; + } + for (int i = slow; i < nums.length; i++) { + nums[i] = val; + } + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\351\252\214\350\257\201\345\233\236\346\226\207\344\270\262.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\351\252\214\350\257\201\345\233\236\346\226\207\344\270\262.java" new file mode 100644 index 0000000..4c1597e --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/two_pointer/\351\252\214\350\257\201\345\233\236\346\226\207\344\270\262.java" @@ -0,0 +1,74 @@ +package io.github.dunwu.algorithm.array.two_pointer; + +import org.junit.jupiter.api.Assertions; + +/** + * 125. 验证回文串 + * + * @author Zhang Peng + * @since 2025-08-06 + */ +public class 验证回文串 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertTrue(s.isPalindrome("A man, a plan, a canal: Panama")); + Assertions.assertFalse(s.isPalindrome("race a car")); + Assertions.assertTrue(s.isPalindrome(" ")); + Assertions.assertTrue(s.isPalindrome("ab_a")); + + Solution2 s2 = new Solution2(); + Assertions.assertTrue(s2.isPalindrome("A man, a plan, a canal: Panama")); + Assertions.assertFalse(s2.isPalindrome("race a car")); + Assertions.assertTrue(s2.isPalindrome(" ")); + Assertions.assertTrue(s2.isPalindrome("ab_a")); + } + + static class Solution { + + public boolean isPalindrome(String s) { + String format = s.replaceAll("[^a-zA-Z0-9]", "").toLowerCase(); + return isPalindrome(format, 0, format.length() - 1); + } + + public boolean isPalindrome(String s, int left, int right) { + while (left < right) { + if (s.charAt(left) != s.charAt(right)) { + return false; + } + left++; + right--; + } + return true; + } + + } + + static class Solution2 { + + public boolean isPalindrome(String s) { + int left = 0, right = s.length() - 1; + while (left < right) { + if (!Character.isLetterOrDigit(s.charAt(left))) { + left++; + continue; + } + if (!Character.isLetterOrDigit(s.charAt(right))) { + right--; + continue; + } + + char l = Character.toLowerCase(s.charAt(left)); + char r = Character.toLowerCase(s.charAt(right)); + if (l != r) { + return false; + } + left++; + right--; + } + return true; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\344\271\230\347\247\257\345\260\217\344\272\216K\347\232\204\345\255\220\346\225\260\347\273\204.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\344\271\230\347\247\257\345\260\217\344\272\216K\347\232\204\345\255\220\346\225\260\347\273\204.java" new file mode 100644 index 0000000..6a6d857 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\344\271\230\347\247\257\345\260\217\344\272\216K\347\232\204\345\255\220\346\225\260\347\273\204.java" @@ -0,0 +1,50 @@ +package io.github.dunwu.algorithm.array.window; + +import org.junit.jupiter.api.Assertions; + +/** + * 713. 乘积小于 K 的子数组 + * + * @author Zhang Peng + * @date 2025-10-14 + */ +public class 乘积小于K的子数组 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(8, s.numSubarrayProductLessThanK(new int[] { 10, 5, 2, 6 }, 100)); + Assertions.assertEquals(0, s.numSubarrayProductLessThanK(new int[] { 1, 2, 3 }, 0)); + } + + static class Solution { + + public int numSubarrayProductLessThanK(int[] nums, int k) { + int left = 0, right = 0; + // 滑动窗口,初始化为乘法单位元 + int windowProduct = 1; + // 记录符合条件的子数组个数 + int count = 0; + + while (right < nums.length) { + // 扩大窗口,并更新窗口数据 + windowProduct = windowProduct * nums[right]; + right++; + + while (left < right && windowProduct >= k) { + // 缩小窗口,并更新窗口数据 + windowProduct = windowProduct / nums[left]; + left++; + } + // 现在必然是一个合法的窗口,但注意思考这个窗口中的子数组个数怎么计算: + // 比方说 left = 1, right = 4 划定了 [1, 2, 3] 这个窗口(right 是开区间) + // 但不止 [left..right] 是合法的子数组,[left+1..right], [left+2..right] 等都是合法子数组 + // 所以我们需要把 [3], [2,3], [1,2,3] 这 right - left 个子数组都加上 + count += right - left; + } + + return count; + } + + } + +} \ No newline at end of file diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\345\255\227\347\254\246\344\270\262\347\232\204\346\216\222\345\210\227.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\345\255\227\347\254\246\344\270\262\347\232\204\346\216\222\345\210\227.java" new file mode 100644 index 0000000..22f6ea9 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\345\255\227\347\254\246\344\270\262\347\232\204\346\216\222\345\210\227.java" @@ -0,0 +1,59 @@ +package io.github.dunwu.algorithm.array.window; + +import org.junit.jupiter.api.Assertions; + +import java.util.HashMap; +import java.util.Map; + +/** + * 567. 字符串的排列 + * + * @author Zhang Peng + * @since 2025-08-06 + */ +public class 字符串的排列 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertTrue(s.checkInclusion("ab", "eidbaooo")); + Assertions.assertFalse(s.checkInclusion("ab", "eidboaoo")); + } + + static class Solution { + + public boolean checkInclusion(String t, String s) { + Map need = new HashMap<>(); + Map window = new HashMap<>(); + for (char c : t.toCharArray()) need.put(c, need.getOrDefault(c, 0) + 1); + + int left = 0, right = 0; + int valid = 0; + while (right < s.length()) { + char c = s.charAt(right); + right++; + // 进行窗口内数据的一系列更新 + if (need.containsKey(c)) { + window.put(c, window.getOrDefault(c, 0) + 1); + if (window.get(c).equals(need.get(c))) { valid++; } + } + + // 判断左侧窗口是否要收缩 + while (right - left >= t.length()) { + // 在这里判断是否找到了合法的子串 + if (valid == need.size()) { return true; } + char d = s.charAt(left); + left++; + // 进行窗口内数据的一系列更新 + if (need.containsKey(d)) { + if (window.get(d).equals(need.get(d))) { valid--; } + window.put(d, window.get(d) - 1); + } + } + } + // 未找到符合条件的子串 + return false; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\240.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\240.java" new file mode 100644 index 0000000..8bf319d --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\240.java" @@ -0,0 +1,38 @@ +package io.github.dunwu.algorithm.array.window; + +import org.junit.jupiter.api.Assertions; + +import java.util.HashSet; +import java.util.Set; + +/** + * 217. 存在重复元素 + * + * @author Zhang Peng + * @since 2020-06-05 + */ +public class 存在重复元素 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertTrue(s.containsDuplicate(new int[] { 1, 2, 3, 1 })); + Assertions.assertFalse(s.containsDuplicate(new int[] { 1, 2, 3, 4 })); + Assertions.assertTrue(s.containsDuplicate(new int[] { 1, 1, 1, 3, 3, 4, 3, 2, 4, 2 })); + } + + static class Solution { + + public boolean containsDuplicate(int[] nums) { + Set set = new HashSet<>(); + for (int num : nums) { + if (set.contains(num)) { + return true; + } + set.add(num); + } + return false; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\2402.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\2402.java" new file mode 100644 index 0000000..2e6b279 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\2402.java" @@ -0,0 +1,50 @@ +package io.github.dunwu.algorithm.array.window; + +import org.junit.jupiter.api.Assertions; + +import java.util.HashSet; + +/** + * 219. 存在重复元素 II + * + * @author Zhang Peng + * @date 2025-10-15 + */ +public class 存在重复元素2 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertTrue(s.containsNearbyDuplicate(new int[] { 1, 2, 3, 1 }, 3)); + Assertions.assertTrue(s.containsNearbyDuplicate(new int[] { 1, 0, 1, 1 }, 1)); + Assertions.assertFalse(s.containsNearbyDuplicate(new int[] { 1, 2, 3, 1, 2, 3 }, 2)); + Assertions.assertTrue(s.containsNearbyDuplicate(new int[] { 99, 99 }, 2)); + } + + static class Solution { + + public boolean containsNearbyDuplicate(int[] nums, int k) { + + // base case + if (nums == null || nums.length < 2) { return false; } + + int left = 0, right = 0; + HashSet window = new HashSet<>(); + // 滑动窗口算法框架,维护一个大小为 k 的窗口 + while (right < nums.length) { + // 扩大窗口 + if (window.contains(nums[right])) { return true; } + window.add(nums[right]); + right++; + + if (right - left > k) { + // 当窗口的大小大于 k 时,缩小窗口 + window.remove(nums[left]); + left++; + } + } + return false; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\2403.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\2403.java" new file mode 100644 index 0000000..8265957 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\2403.java" @@ -0,0 +1,55 @@ +package io.github.dunwu.algorithm.array.window; + +import org.junit.jupiter.api.Assertions; + +import java.util.TreeSet; + +/** + * 220. 存在重复元素 III + * + * @author Zhang Peng + * @date 2025-10-15 + */ +public class 存在重复元素3 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertTrue(s.containsNearbyAlmostDuplicate(new int[] { 1, 2, 3, 1 }, 3, 0)); + Assertions.assertFalse(s.containsNearbyAlmostDuplicate(new int[] { 1, 5, 9, 1, 5, 9 }, 2, 3)); + Assertions.assertTrue(s.containsNearbyAlmostDuplicate(new int[] { 1, 2, 2, 3, 4, 5 }, 3, 0)); + } + + static class Solution { + + public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) { + TreeSet window = new TreeSet<>(); + int left = 0, right = 0; + while (right < nums.length) { + // 为了防止 i == j,所以在扩大窗口之前先判断是否有符合题意的索引对 (i, j) + // 查找略大于 nums[right] 的那个元素 + Integer ceiling = window.ceiling(nums[right]); + if (ceiling != null && (long) ceiling - nums[right] <= t) { + return true; + } + // 查找略小于 nums[right] 的那个元素 + Integer floor = window.floor(nums[right]); + if (floor != null && (long) nums[right] - floor <= t) { + return true; + } + + // 扩大窗口 + window.add(nums[right]); + right++; + + if (right - left > k) { + // 缩小窗口 + window.remove(nums[left]); + left++; + } + } + return false; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\345\260\206x\345\207\217\345\210\2600\347\232\204\346\234\200\345\260\217\346\223\215\344\275\234\346\225\260.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\345\260\206x\345\207\217\345\210\2600\347\232\204\346\234\200\345\260\217\346\223\215\344\275\234\346\225\260.java" new file mode 100644 index 0000000..d39d1c8 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\345\260\206x\345\207\217\345\210\2600\347\232\204\346\234\200\345\260\217\346\223\215\344\275\234\346\225\260.java" @@ -0,0 +1,65 @@ +package io.github.dunwu.algorithm.array.window; + +import org.junit.jupiter.api.Assertions; + +/** + * 1658. 将 x 减到 0 的最小操作数 + * + * @author Zhang Peng + * @date 2025-10-14 + */ +public class 将x减到0的最小操作数 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(2, s.minOperations(new int[] { 1, 1, 4, 2, 3 }, 5)); + Assertions.assertEquals(-1, s.minOperations(new int[] { 5, 6, 7, 8, 9 }, 4)); + Assertions.assertEquals(5, s.minOperations(new int[] { 3, 2, 20, 1, 1, 3 }, 10)); + Assertions.assertEquals(16, s.minOperations(new int[] { 8828, 9581, 49, 9818, 9974, 9869, 9991, + 10000, 10000, 10000, 9999, 9993, 9904, 8819, 1231, 6309 }, 134365)); + } + + static class Solution { + + // 【思路】 + // 从边缘删除掉和为 x 的元素,那剩下来的是什么?剩下来的是不是就是 nums 中的一个子数组? + // 让你尽可能少地从边缘删除元素说明什么?是不是就是说剩下来的这个子数组大小尽可能的大? + // 所以,这道题等价于让你寻找 nums 中元素和为 sum(nums) - x 的最长子数组。 + + // 1、当窗口内元素之和小于目标和 target 时,扩大窗口,让窗口内元素和增加。 + // 2、当窗口内元素之和大于目标和 target 时,缩小窗口,让窗口内元素和减小。 + // 3、当窗口内元素之和等于目标和 target 时,找到一个符合条件的子数组,我们想找的是最长的子数组长度。 + public int minOperations(int[] nums, int x) { + int n = nums.length, sum = 0; + for (int num : nums) { sum += num; } + // 滑动窗口需要寻找的子数组目标和 + int target = sum - x; + + int left = 0, right = 0; + // 记录窗口内所有元素和 + int windowSum = 0; + // 记录目标子数组的最大长度 + int maxLen = Integer.MIN_VALUE; + // 开始执行滑动窗口框架 + while (right < nums.length) { + // 扩大窗口 + windowSum += nums[right]; + right++; + + while (windowSum > target && left < right) { + // 缩小窗口 + windowSum -= nums[left]; + left++; + } + // 寻找目标子数组 + if (windowSum == target) { + maxLen = Math.max(maxLen, right - left); + } + } + // 目标子数组的最大长度可以推导出需要删除的字符数量 + return maxLen == Integer.MIN_VALUE ? -1 : n - maxLen; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\346\211\276\345\210\260\345\255\227\347\254\246\344\270\262\344\270\255\346\211\200\346\234\211\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\346\211\276\345\210\260\345\255\227\347\254\246\344\270\262\344\270\255\346\211\200\346\234\211\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215.java" new file mode 100644 index 0000000..2a6bb20 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\346\211\276\345\210\260\345\255\227\347\254\246\344\270\262\344\270\255\346\211\200\346\234\211\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215.java" @@ -0,0 +1,69 @@ +package io.github.dunwu.algorithm.array.window; + +import org.junit.jupiter.api.Assertions; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 438. 找到字符串中所有字母异位词 + * + * @author Zhang Peng + * @date 2025-10-14 + */ +public class 找到字符串中所有字母异位词 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertArrayEquals(new Integer[] { 0, 6 }, s.findAnagrams("cbaebabacd", "abc").toArray()); + Assertions.assertArrayEquals(new Integer[] { 0, 1, 2 }, s.findAnagrams("abab", "ab").toArray()); + } + + static class Solution { + + public List findAnagrams(String s, String t) { + Map need = new HashMap<>(); + Map window = new HashMap<>(); + for (char c : t.toCharArray()) { + need.put(c, need.getOrDefault(c, 0) + 1); + } + + int left = 0, right = 0; + int valid = 0; + // 记录结果 + List res = new ArrayList<>(); + while (right < s.length()) { + char c = s.charAt(right); + right++; + // 进行窗口内数据的一系列更新 + if (need.containsKey(c)) { + window.put(c, window.getOrDefault(c, 0) + 1); + if (window.get(c).equals(need.get(c))) { + valid++; + } + } + // 判断左侧窗口是否要收缩 + while (right - left >= t.length()) { + // 当窗口符合条件时,把起始索引加入 res + if (valid == need.size()) { + res.add(left); + } + char d = s.charAt(left); + left++; + // 进行窗口内数据的一系列更新 + if (need.containsKey(d)) { + if (window.get(d).equals(need.get(d))) { + valid--; + } + window.put(d, window.get(d) - 1); + } + } + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\346\227\240\351\207\215\345\244\215\345\255\227\347\254\246\347\232\204\346\234\200\351\225\277\345\255\220\344\270\262.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\346\227\240\351\207\215\345\244\215\345\255\227\347\254\246\347\232\204\346\234\200\351\225\277\345\255\220\344\270\262.java" new file mode 100644 index 0000000..52f0955 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\346\227\240\351\207\215\345\244\215\345\255\227\347\254\246\347\232\204\346\234\200\351\225\277\345\255\220\344\270\262.java" @@ -0,0 +1,52 @@ +package io.github.dunwu.algorithm.array.window; + +import org.junit.jupiter.api.Assertions; + +import java.util.HashMap; +import java.util.Map; + +/** + * 3. 无重复字符的最长子串 + * + * @author Zhang Peng + * @date 2025-10-14 + */ +public class 无重复字符的最长子串 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(3, s.lengthOfLongestSubstring("abcabcbb")); + Assertions.assertEquals(1, s.lengthOfLongestSubstring("bbbbb")); + Assertions.assertEquals(3, s.lengthOfLongestSubstring("pwwkew")); + Assertions.assertEquals(2, s.lengthOfLongestSubstring("aab")); + } + + static class Solution { + + public int lengthOfLongestSubstring(String s) { + Map window = new HashMap<>(); + + int left = 0, right = 0; + // 记录结果 + int res = 0; + while (right < s.length()) { + char c = s.charAt(right); + right++; + // 进行窗口内数据的一系列更新 + window.put(c, window.getOrDefault(c, 0) + 1); + // 判断左侧窗口是否要收缩 + while (window.get(c) > 1) { + char d = s.charAt(left); + left++; + // 进行窗口内数据的一系列更新 + window.put(d, window.get(d) - 1); + } + // 在这里更新答案 + res = Math.max(res, right - left); + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\346\233\277\346\215\242\345\220\216\347\232\204\346\234\200\351\225\277\351\207\215\345\244\215\345\255\227\347\254\246.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\346\233\277\346\215\242\345\220\216\347\232\204\346\234\200\351\225\277\351\207\215\345\244\215\345\255\227\347\254\246.java" new file mode 100644 index 0000000..58f6ba0 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\346\233\277\346\215\242\345\220\216\347\232\204\346\234\200\351\225\277\351\207\215\345\244\215\345\255\227\347\254\246.java" @@ -0,0 +1,56 @@ +package io.github.dunwu.algorithm.array.window; + +import org.junit.jupiter.api.Assertions; + +/** + * 424. 替换后的最长重复字符 + * + * @author Zhang Peng + * @date 2025-10-15 + */ +public class 替换后的最长重复字符 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(4, s.characterReplacement("ABAB", 2)); + Assertions.assertEquals(4, s.characterReplacement("AABABBA", 1)); + Assertions.assertEquals(4, s.characterReplacement("AAAA", 2)); + } + + static class Solution { + + public int characterReplacement(String s, int k) { + int left = 0, right = 0; + // 统计窗口中每个字符的出现次数 + int[] windowCharCount = new int[26]; + // 记录窗口中字符的最多重复次数 + // 记录这个值的意义在于,最划算的替换方法肯定是把其他字符替换成出现次数最多的那个字符 + int windowMaxCount = 0; + // 记录结果长度 + int res = 0; + + // 开始滑动窗口模板 + while (right < s.length()) { + // 扩大窗口 + int c = s.charAt(right) - 'A'; + windowCharCount[c]++; + windowMaxCount = Math.max(windowMaxCount, windowCharCount[c]); + right++; + + // 这个 while 换成 if 也可以 + while (right - left - windowMaxCount > k) { + // 杂牌字符数量 right - left - windowMaxCount 多于 k + // 此时,k 次替换已经无法把窗口内的字符都替换成相同字符了 + // 必须缩小窗口 + windowCharCount[s.charAt(left) - 'A']--; + left++; + } + // 经过收缩后,此时一定是一个合法的窗口 + res = Math.max(res, right - left); + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\346\234\200\345\244\247\350\277\236\347\273\2551\347\232\204\344\270\252\346\225\2603.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\346\234\200\345\244\247\350\277\236\347\273\2551\347\232\204\344\270\252\346\225\2603.java" new file mode 100644 index 0000000..531e688 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\346\234\200\345\244\247\350\277\236\347\273\2551\347\232\204\344\270\252\346\225\2603.java" @@ -0,0 +1,40 @@ +package io.github.dunwu.algorithm.array.window; + +import org.junit.jupiter.api.Assertions; + +/** + * 1004. 最大连续1的个数 III + * + * @author Zhang Peng + * @date 2025-10-14 + */ +public class 最大连续1的个数3 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(6, s.longestOnes(new int[] { 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0 }, 2)); + Assertions.assertEquals(10, + s.longestOnes(new int[] { 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1 }, 3)); + } + + static class Solution { + + public int longestOnes(int[] nums, int k) { + int cnt = 0, len = 0; + int left = 0, right = 0; + while (right < nums.length) { + if (nums[right] == 0) { cnt++; } + right++; + + while (cnt > k) { + if (nums[left] == 0) { cnt--; } + left++; + } + len = Math.max(len, right - left); + } + return len; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\346\234\200\345\260\217\350\246\206\347\233\226\345\255\220\344\270\262.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\346\234\200\345\260\217\350\246\206\347\233\226\345\255\220\344\270\262.java" new file mode 100644 index 0000000..81b42a2 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\346\234\200\345\260\217\350\246\206\347\233\226\345\255\220\344\270\262.java" @@ -0,0 +1,74 @@ +package io.github.dunwu.algorithm.array.window; + +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Assertions; + +import java.util.HashMap; +import java.util.Map; + +/** + * 76. 最小覆盖子串 + * + * @author Zhang Peng + * @date 2025-01-10 + */ +@Slf4j +public class 最小覆盖子串 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals("BANC", s.minWindow("ADOBECODEBANC", "ABC")); + Assertions.assertEquals("a", s.minWindow("a", "a")); + Assertions.assertEquals("", s.minWindow("a", "aa")); + } + + static class Solution { + + public String minWindow(String s, String t) { + Map need = new HashMap<>(); + Map window = new HashMap<>(); + for (char c : t.toCharArray()) { + need.put(c, need.getOrDefault(c, 0) + 1); + } + + int valid = 0; + int left = 0, right = 0; + // 记录最小覆盖子串的起始索引及长度 + int start = 0, len = Integer.MAX_VALUE; + while (right < s.length()) { + // c 是将移入窗口的字符 + char c = s.charAt(right); + // 扩大窗口 + right++; + // 进行窗口内数据的一系列更新 + if (need.containsKey(c)) { + window.put(c, window.getOrDefault(c, 0) + 1); + if (window.get(c).equals(need.get(c))) { valid++; } + } + + // 判断左侧窗口是否要收缩 + while (valid == need.size()) { + + // 在这里更新最小覆盖子串 + if (right - left < len) { + start = left; + len = right - left; + } + // d 是将移出窗口的字符 + char d = s.charAt(left); + // 缩小窗口 + left++; + // 进行窗口内数据的一系列更新 + if (need.containsKey(d)) { + if (window.get(d).equals(need.get(d))) { valid--; } + window.put(d, window.get(d) - 1); + } + } + } + // 返回最小覆盖子串 + return len == Integer.MAX_VALUE ? "" : s.substring(start, start + len); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\350\207\263\345\260\221\346\234\211K\344\270\252\351\207\215\345\244\215\345\255\227\347\254\246\347\232\204\346\234\200\351\225\277\345\255\220\344\270\262.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\350\207\263\345\260\221\346\234\211K\344\270\252\351\207\215\345\244\215\345\255\227\347\254\246\347\232\204\346\234\200\351\225\277\345\255\220\344\270\262.java" new file mode 100644 index 0000000..5a8994b --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\350\207\263\345\260\221\346\234\211K\344\270\252\351\207\215\345\244\215\345\255\227\347\254\246\347\232\204\346\234\200\351\225\277\345\255\220\344\270\262.java" @@ -0,0 +1,86 @@ +package io.github.dunwu.algorithm.array.window; + +import org.junit.jupiter.api.Assertions; + +/** + * 395. 至少有 K + * 个重复字符的最长子串 + * + * @author Zhang Peng + * @date 2025-10-15 + */ +public class 至少有K个重复字符的最长子串 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(3, s.longestSubstring("aaabb", 3)); + Assertions.assertEquals(5, s.longestSubstring("ababbc", 2)); + Assertions.assertEquals(6, s.longestSubstring("aaabbb", 3)); + } + + public static class Solution { + + public int longestSubstring(String s, int k) { + int len = 0; + for (int i = 1; i <= 26; i++) { + // 限制窗口中只能有 i 种不同字符 + len = Math.max(len, longestKLetterSubstring(s, k, i)); + } + return len; + } + + // 寻找 s 中含有 count 种字符,且每种字符出现次数都大于 k 的子串 + public int longestKLetterSubstring(String s, int k, int count) { + + // 记录答案 + int res = 0; + // 快慢指针维护滑动窗口,左闭右开区间 + int left = 0, right = 0; + // 题目说 s 中只有小写字母,所以用大小 26 的数组记录窗口中字符出现的次数 + int[] windowCount = new int[26]; + // 记录窗口中存在几种不同的字符(字符种类) + int windowUniqueCount = 0; + // 记录窗口中有几种字符的出现次数达标(大于等于 k) + int windowValidCount = 0; + // 滑动窗口代码模板 + while (right < s.length()) { + // 移入字符,扩大窗口 + int c = s.charAt(right) - 'a'; + if (windowCount[c] == 0) { + // 窗口中新增了一种字符 + windowUniqueCount++; + } + windowCount[c]++; + if (windowCount[c] == k) { + // 窗口中新增了一种达标的字符 + windowValidCount++; + } + right++; + + // 当窗口中字符种类大于 count 时,缩小窗口 + while (windowUniqueCount > count) { + // 移出字符,缩小窗口 + int d = s.charAt(left) - 'a'; + if (windowCount[d] == k) { + // 窗口中减少了一种达标的字符 + windowValidCount--; + } + windowCount[d]--; + if (windowCount[d] == 0) { + // 窗口中减少了一种字符 + windowUniqueCount--; + } + left++; + } + + // 当窗口中字符种类为 count 且每个字符出现次数都满足 k 时,更新答案 + if (windowValidCount == count) { + res = Math.max(res, right - left); + } + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204.java" new file mode 100644 index 0000000..b636134 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/window/\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204.java" @@ -0,0 +1,44 @@ +package io.github.dunwu.algorithm.array.window; + +import org.junit.jupiter.api.Assertions; + +/** + * 209. 长度最小的子数组 + * + * @author Zhang Peng + * @date 2025-10-15 + */ +public class 长度最小的子数组 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(2, s.minSubArrayLen(7, new int[] { 2, 3, 1, 2, 4, 3 })); + Assertions.assertEquals(1, s.minSubArrayLen(4, new int[] { 1, 4, 4 })); + Assertions.assertEquals(0, s.minSubArrayLen(11, new int[] { 1, 1, 1, 1, 1, 1, 1, 1 })); + } + + public static class Solution { + + public int minSubArrayLen(int target, int[] nums) { + int left = 0, right = 0; + // 维护窗口内元素之和 + int windowSum = 0; + int res = Integer.MAX_VALUE; + + while (right < nums.length) { + // 扩大窗口 + windowSum += nums[right]; + right++; + while (windowSum >= target && left < right) { + // 已经达到 target,缩小窗口,同时更新答案 + res = Math.min(res, right - left); + windowSum -= nums[left]; + left++; + } + } + return res == Integer.MAX_VALUE ? 0 : res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\344\270\211\346\225\260\344\271\213\345\222\214.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\344\270\211\346\225\260\344\271\213\345\222\214.java" deleted file mode 100644 index 5219810..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\344\270\211\346\225\260\344\271\213\345\222\214.java" +++ /dev/null @@ -1,65 +0,0 @@ -package io.github.dunwu.algorithm.array; - -import org.junit.jupiter.api.Assertions; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * 给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。 - *

- * 注意:答案中不可以包含重复的三元组。 - *

- * 示例:给定数组 nums = [-1, 0, 1, 2, -1, -4], - *

- * 满足要求的三元组集合为: [ [-1, 0, 1], [-1, -1, 2] ] - * - * @author Zhang Peng - * @see 三数之和 - * @since 2020-01-18 - */ -public class 三数之和 { - - public static List> threeSum(int[] nums) { - List> list = new ArrayList<>(); - - if (nums == null || nums.length < 3) return list; - - int len = nums.length; - Arrays.sort(nums); - - for (int i = 0; i < len; i++) { - if (nums[i] > 0) break; - - // 去重 - if (i > 0 && nums[i] == nums[i - 1]) continue; - - int L = i + 1; - int R = len - 1; - while (L < R) { - int sum = nums[i] + nums[L] + nums[R]; - if (sum == 0) { - list.add(Arrays.asList(nums[i], nums[L], nums[R])); - while (L < R && nums[L] == nums[L + 1]) L++; - while (L < R && nums[R] == nums[R - 1]) R--; - L++; - R--; - } else if (sum < 0) { - L++; - } else if (sum > 0) { - R--; - } - } - } - - return list; - } - - public static void main(String[] args) { - List> list = threeSum(new int[] { -1, 0, 1, 2, -1, -4 }); - Assertions.assertEquals(Arrays.asList(-1, 0, 1), list.get(1)); - Assertions.assertEquals(Arrays.asList(-1, -1, 2), list.get(0)); - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\344\270\221\346\225\260I.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\344\270\221\346\225\260I.java" deleted file mode 100644 index 9fa6302..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\344\270\221\346\225\260I.java" +++ /dev/null @@ -1,30 +0,0 @@ -package io.github.dunwu.algorithm.array; - -import org.junit.jupiter.api.Assertions; - -import java.util.HashSet; -import java.util.Set; - -/** - * 263. 丑数 - * - * @author Zhang Peng - * @date 2025-01-24 - */ -public class 丑数I { - - public static void main(String[] args) { - Assertions.assertTrue(isUgly(6)); - Assertions.assertTrue(isUgly(1)); - Assertions.assertFalse(isUgly(14)); - } - - public static boolean isUgly(int n) { - while (n <= 0) return false; - while (n % 2 == 0) n /= 2; - while (n % 3 == 0) n /= 3; - while (n % 5 == 0) n /= 5; - return n == 1; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\344\270\221\346\225\260II.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\344\270\221\346\225\260II.java" deleted file mode 100644 index 8033c89..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\344\270\221\346\225\260II.java" +++ /dev/null @@ -1,51 +0,0 @@ -package io.github.dunwu.algorithm.array; - -import org.junit.jupiter.api.Assertions; - -/** - * 264. 丑数II - * - * @author Zhang Peng - * @date 2025-01-24 - */ -public class 丑数II { - - public static void main(String[] args) { - Assertions.assertEquals(12, nthUglyNumber(10)); - Assertions.assertEquals(1, nthUglyNumber(1)); - } - - public static int nthUglyNumber(int n) { - if (n == 1) { - return 1; - } - - // 可以理解为三个指向有序链表头结点的指针 - int p2 = 1, p3 = 1, p5 = 1; - // 可以理解为三个有序链表的头节点的值 - int product2 = 1, product3 = 1, product5 = 1; - // 可以理解为最终合并的有序链表(结果链表) - int[] ugly = new int[n + 1]; - // 可以理解为结果链表上的指针 - int u = 1; - - while (u <= n) { - int min = Math.min(product2, Math.min(product3, product5)); - ugly[u++] = min; - if (min == product2) { - product2 = 2 * ugly[p2]; - p2++; - } - if (min == product3) { - product3 = 3 * ugly[p3]; - p3++; - } - if (min == product5) { - product5 = 5 * ugly[p5]; - p5++; - } - } - return ugly[n]; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\344\270\221\346\225\260III.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\344\270\221\346\225\260III.java" deleted file mode 100644 index 27434b8..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\344\270\221\346\225\260III.java" +++ /dev/null @@ -1,40 +0,0 @@ -package io.github.dunwu.algorithm.array; - -import org.junit.jupiter.api.Assertions; - -/** - * 264. 丑数II - * - * @author Zhang Peng - * @date 2025-01-24 - */ -public class 丑数III { - - public static void main(String[] args) { - Assertions.assertEquals(4, nthUglyNumber(3, 2, 3, 5)); - Assertions.assertEquals(6, nthUglyNumber(4, 2, 3, 4)); - Assertions.assertEquals(10, nthUglyNumber(5, 2, 11, 13)); - Assertions.assertEquals(1999999984, nthUglyNumber(1000000000, 2, 217983653, 336916467)); - } - - public static int nthUglyNumber(int n, int a, int b, int c) { - int p = 1; - int vA = a, vB = b, vC = c; - long min = Integer.MAX_VALUE; - while (p <= n) { - min = Math.min(vA, Math.min(vB, vC)); - if (min == vA) { - vA += a; - } - if (min == vB) { - vB += b; - } - if (min == vC) { - vC += c; - } - p++; - } - return (int) min; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\344\270\244\346\225\260\344\271\213\345\222\214.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\344\270\244\346\225\260\344\271\213\345\222\214.java" deleted file mode 100644 index efad0ce..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\344\270\244\346\225\260\344\271\213\345\222\214.java" +++ /dev/null @@ -1,56 +0,0 @@ -package io.github.dunwu.algorithm.array; - -import org.junit.jupiter.api.Assertions; - -import java.util.HashMap; -import java.util.Map; - -/** - * 题目:1. 两数之和 - * - * @author Zhang Peng - * @since 2020-06-05 - */ -public class 两数之和 { - - public static void main(String[] args) { - Assertions.assertArrayEquals(new int[] { 0, 1 }, twoSumInSorted(new int[] { 2, 7, 11, 15 }, 9)); - Assertions.assertArrayEquals(new int[] { 1, 2 }, twoSumInSorted(new int[] { 3, 2, 4 }, 6)); - Assertions.assertArrayEquals(new int[] { 0, 1 }, twoSumInSorted(new int[] { 3, 3 }, 6)); - - Assertions.assertArrayEquals(new int[] { 0, 1 }, twoSumInSorted2(new int[] { 2, 7, 11, 15 }, 9)); - Assertions.assertArrayEquals(new int[] { 1, 2 }, twoSumInSorted2(new int[] { 3, 2, 4 }, 6)); - Assertions.assertArrayEquals(new int[] { 0, 1 }, twoSumInSorted2(new int[] { 3, 3 }, 6)); - } - - /** - * 时间复杂度:O(n^2) - */ - public static int[] twoSumInSorted(int[] nums, int target) { - for (int left = 0; left < nums.length; left++) { - for (int right = left + 1; right < nums.length; right++) { - if (nums[left] + nums[right] == target) { - return new int[] { left, right }; - } - } - } - return new int[] { -1, -1 }; - } - - /** - * 时间复杂度:O(n) - */ - public static int[] twoSumInSorted2(int[] nums, int target) { - Map map = new HashMap<>(nums.length); - for (int i = 0; i < nums.length; i++) { - int expectNum = target - nums[i]; - if (map.containsKey(expectNum)) { - return new int[] { map.get(expectNum), i }; - } else { - map.put(nums[i], i); - } - } - return new int[] { -1, -1 }; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\344\270\244\346\225\260\344\271\213\345\222\214II.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\344\270\244\346\225\260\344\271\213\345\222\214II.java" deleted file mode 100644 index 5c09531..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\344\270\244\346\225\260\344\271\213\345\222\214II.java" +++ /dev/null @@ -1,79 +0,0 @@ -package io.github.dunwu.algorithm.array; - -import org.junit.jupiter.api.Assertions; - -import java.util.HashMap; -import java.util.Map; - -/** - * 题目:167. 两数之和 II - 输入有序数组 - * - * @author Zhang Peng - * @since 2020-06-05 - */ -public class 两数之和II { - - public static void main(String[] args) { - Assertions.assertArrayEquals(new int[] { 1, 2 }, twoSum(new int[] { 2, 7, 11, 15 }, 9)); - Assertions.assertArrayEquals(new int[] { 1, 3 }, twoSum(new int[] { 2, 3, 4 }, 6)); - Assertions.assertArrayEquals(new int[] { 1, 2 }, twoSum(new int[] { -1, 0 }, -1)); - - Assertions.assertArrayEquals(new int[] { 1, 2 }, twoSum2(new int[] { 2, 7, 11, 15 }, 9)); - Assertions.assertArrayEquals(new int[] { 1, 3 }, twoSum2(new int[] { 2, 3, 4 }, 6)); - Assertions.assertArrayEquals(new int[] { 1, 2 }, twoSum2(new int[] { -1, 0 }, -1)); - - Assertions.assertArrayEquals(new int[] { 1, 2 }, twoSum3(new int[] { 2, 7, 11, 15 }, 9)); - Assertions.assertArrayEquals(new int[] { 1, 3 }, twoSum3(new int[] { 2, 3, 4 }, 6)); - Assertions.assertArrayEquals(new int[] { 1, 2 }, twoSum3(new int[] { -1, 0 }, -1)); - } - - /** - * 时间复杂度:O(n^2) - */ - public static int[] twoSum(int[] numbers, int target) { - for (int i = 0; i < numbers.length; i++) { - for (int j = i + 1; j < numbers.length; j++) { - if (numbers[i] + numbers[j] == target) { - return new int[] { i + 1, j + 1 }; - } - } - } - return new int[] { -1, -1 }; - } - - /** - * 时间复杂度:O(n) - */ - public static int[] twoSum2(int[] numbers, int target) { - int len = numbers.length; - Map map = new HashMap<>(len); - for (int i = 0; i < len; i++) { - int num = numbers[i]; - int diff = target - num; - if (map.containsKey(diff)) { - return new int[] { map.get(diff) + 1, i + 1 }; - } - map.put(num, i); - } - return new int[] { -1, -1 }; - } - - /** - * 时间复杂度:O(logn) - */ - public static int[] twoSum3(int[] numbers, int target) { - int left = 0, right = numbers.length - 1; - while (left < right) { - int sum = numbers[left] + numbers[right]; - if (sum == target) { - return new int[] { left + 1, right + 1 }; - } else if (sum < target) { - left++; - } else { - right--; - } - } - return new int[] { -1, -1 }; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\344\272\214\347\273\264\346\225\260\347\273\204.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\344\272\214\347\273\264\346\225\260\347\273\204.java" deleted file mode 100644 index 5bd729c..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\344\272\214\347\273\264\346\225\260\347\273\204.java" +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.dunwu.algorithm.array; - -/** - * @author Zhang Peng - * @since 2018-11-04 - */ -public class 二维数组 { - - public static void main(String[] args) { - System.out.println("Example I:"); - int[][] a = new int[2][5]; - printArray(a); - System.out.println("Example II:"); - int[][] b = new int[2][]; - printArray(b); - System.out.println("Example III:"); - b[0] = new int[3]; - b[1] = new int[5]; - printArray(b); - } - - private static void printArray(int[][] a) { - for (int i = 0; i < a.length; ++i) { - System.out.println(a[i]); - } - for (int i = 0; i < a.length; ++i) { - for (int j = 0; a[i] != null && j < a[i].length; ++j) { - System.out.print(a[i][j] + " "); - } - System.out.println(); - } - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\344\272\214\350\277\233\345\210\266\346\261\202\345\222\214.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\344\272\214\350\277\233\345\210\266\346\261\202\345\222\214.java" deleted file mode 100644 index e2fff7c..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\344\272\214\350\277\233\345\210\266\346\261\202\345\222\214.java" +++ /dev/null @@ -1,46 +0,0 @@ -package io.github.dunwu.algorithm.array; - -import org.junit.jupiter.api.Assertions; - -/** - * 67. 二进制求和 - * - * @author Zhang Peng - * @date 2025-01-21 - */ -public class 二进制求和 { - - public static void main(String[] args) { - Assertions.assertEquals("100", addBinary("11", "1")); - Assertions.assertEquals("10101", addBinary("1010", "1011")); - } - - public static String addBinary(String a, String b) { - - if (a == null || a.length() == 0) return b; - if (b == null || b.length() == 0) return a; - - char[] arrA = a.toCharArray(); - char[] arrB = b.toCharArray(); - StringBuilder sb = new StringBuilder(); - int carry = 0; - int i = arrA.length - 1, j = arrB.length - 1; - while (i >= 0 || j >= 0) { - int value = carry; - if (i >= 0) { - value += arrA[i--] - '0'; - } - if (j >= 0) { - value += arrB[j--] - '0'; - } - carry = value / 2; - value = value % 2; - sb.append(value); - } - if (carry != 0) { - sb.append(carry); - } - return sb.reverse().toString(); - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271.java" deleted file mode 100644 index 03a7e1f..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271.java" +++ /dev/null @@ -1,42 +0,0 @@ -package io.github.dunwu.algorithm.array; - -import org.junit.jupiter.api.Assertions; - -/** - * 26. 删除有序数组中的重复项 - * - * @author Zhang Peng - * @since 2018-11-05 - */ -public class 删除排序数组中的重复项 { - - public static void main(String[] args) { - int[] nums1 = { 1, 1, 2 }; - Assertions.assertEquals(2, removeDuplicates(nums1)); - - int[] nums2 = { 0, 0, 1, 1, 1, 2, 2, 3, 3, 4 }; - Assertions.assertEquals(5, removeDuplicates(nums2)); - - int[] nums3 = { 1, 2 }; - Assertions.assertEquals(2, removeDuplicates(nums3)); - - int[] nums4 = { 2, 2 }; - Assertions.assertEquals(1, removeDuplicates(nums4)); - } - - public static int removeDuplicates(int[] nums) { - if (nums.length == 0) { - return 0; - } - int slow = 0, fast = 0; - while (fast < nums.length) { - if (nums[slow] != nums[fast]) { - slow++; - nums[slow] = nums[fast]; - } - fast++; - } - return slow + 1; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\345\212\240\344\270\200.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\345\212\240\344\270\200.java" deleted file mode 100644 index dba8a10..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\345\212\240\344\270\200.java" +++ /dev/null @@ -1,62 +0,0 @@ -package io.github.dunwu.algorithm.array; - -// 【加一】 - -// -// 给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。 -// -// 最高位数字存放在数组的首位, 数组中每个元素只存储一个数字。 -// -// 你可以假设除了整数 0 之外,这个整数不会以零开头。 -// -// 示例 1: -// -// 输入: [1,2,3] -// 输出: [1,2,4] -// 解释: 输入数组表示数字 123。 -// 示例 2: -// -// 输入: [4,3,2,1] -// 输出: [4,3,2,2] -// 解释: 输入数组表示数字 4321。 - -import org.junit.jupiter.api.Assertions; - -/** - * @author Zhang Peng - * @since 2018-11-04 - */ -public class 加一 { - - public static void main(String[] args) { - int[] nums1 = { 1, 2, 3 }; - int[] nums2 = { 4, 3, 2, 1 }; - int[] nums3 = { 9, 9, 9, 9 }; - - int[] expected1 = { 1, 2, 4 }; - int[] expected2 = { 4, 3, 2, 2 }; - int[] expected3 = { 1, 0, 0, 0, 0 }; - - Assertions.assertArrayEquals(expected1, 加一.plusOne(nums1)); - Assertions.assertArrayEquals(expected2, 加一.plusOne(nums2)); - Assertions.assertArrayEquals(expected3, 加一.plusOne(nums3)); - } - - public static int[] plusOne(int[] digits) { - int n = digits.length; - for (int i = n - 1; i >= 0; i--) { - if (digits[i] < 9) { - digits[i]++; - return digits; - } - - digits[i] = 0; - } - - int[] newNumber = new int[n + 1]; - newNumber[0] = 1; - - return newNumber; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262.java" deleted file mode 100644 index 7004912..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262.java" +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.dunwu.algorithm.array; - -import org.junit.jupiter.api.Assertions; - -/** - * 题目:344. 反转字符串 - * - * @author Zhang Peng - * @since 2020-06-05 - */ -public class 反转字符串 { - - public static void main(String[] args) { - char[] s1 = new char[] { 'h', 'e', 'l', 'l', 'o' }; - reverseString(s1); - Assertions.assertArrayEquals(new char[] { 'o', 'l', 'l', 'e', 'h' }, s1); - - char[] s2 = new char[] { 'H', 'a', 'n', 'n', 'a', 'h' }; - reverseString(s2); - Assertions.assertArrayEquals(new char[] { 'h', 'a', 'n', 'n', 'a', 'H' }, s2); - } - - public static void reverseString(char[] s) { - int left = 0, right = s.length - 1; - while (left < right) { - char temp = s[left]; - s[left] = s[right]; - s[right] = temp; - left++; - right--; - } - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\345\220\210\345\271\266\345\214\272\351\227\264.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\345\220\210\345\271\266\345\214\272\351\227\264.java" deleted file mode 100644 index 4e64e04..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\345\220\210\345\271\266\345\214\272\351\227\264.java" +++ /dev/null @@ -1,68 +0,0 @@ -package io.github.dunwu.algorithm.array; - -import org.junit.jupiter.api.Assertions; - -import java.util.Arrays; - -/** - * @author Zhang Peng - * @since 2020-07-29 - */ -public class 合并区间 { - - public static void main(String[] args) { - int[][] array = new int[][] { - { 1, 4 }, { 2, 3 } - }; - int[][] exprect = new int[][] { - { 1, 4 } - }; - Assertions.assertArrayEquals(exprect, merge(array)); - - // int[][] array = new int[][] { - // { 1, 3 }, { 2, 6 }, { 8, 10 }, { 15, 18 } - // }; - // int[][] exprect = new int[][] { - // { 1, 6 }, { 8, 10 }, { 15, 18 } - // }; - // Assertions.assertArrayEquals(exprect, merge(array)); - - int[][] array2 = new int[][] { - { 1, 4 }, { 4, 5 } - }; - int[][] exprect2 = new int[][] { - { 1, 5 } - }; - Assertions.assertArrayEquals(exprect2, merge(array2)); - } - - public static int[][] merge(int[][] intervals) { - Arrays.sort(intervals, (v1, v2) -> v1[0] - v2[0]); - - int len = intervals.length; - int[][] res = new int[len][2]; - int cnt = 0; - for (int[] interval : intervals) { - boolean merged = false; - for (int i = 0; i < cnt; i++) { - if (interval[0] >= res[i][0] && interval[1] <= res[i][1]) { - merged = true; - continue; - } - if (interval[0] <= res[i][1]) { - if (interval[1] >= res[i][1]) { - res[i][1] = interval[1]; - merged = true; - continue; - } - } - } - if (!merged) { - res[cnt] = Arrays.copyOf(interval, 2); - cnt++; - } - } - return Arrays.copyOf(res, cnt); - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\345\234\250\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\346\237\245\346\211\276\345\205\203\347\264\240\347\232\204\347\254\254\344\270\200\344\270\252\345\222\214\346\234\200\345\220\216\344\270\200\344\270\252\344\275\215\347\275\256.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\345\234\250\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\346\237\245\346\211\276\345\205\203\347\264\240\347\232\204\347\254\254\344\270\200\344\270\252\345\222\214\346\234\200\345\220\216\344\270\200\344\270\252\344\275\215\347\275\256.java" deleted file mode 100644 index 9072d40..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\345\234\250\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\346\237\245\346\211\276\345\205\203\347\264\240\347\232\204\347\254\254\344\270\200\344\270\252\345\222\214\346\234\200\345\220\216\344\270\200\344\270\252\344\275\215\347\275\256.java" +++ /dev/null @@ -1,90 +0,0 @@ -package io.github.dunwu.algorithm.array; - -import org.junit.jupiter.api.Assertions; - -/** - * @author Zhang Peng - * @since 2020-06-05 - */ -public class 在排序数组中查找元素的第一个和最后一个位置 { - - public static void main(String[] args) { - Assertions.assertArrayEquals(new int[] { 3, 4 }, - searchRange(new int[] { 5, 7, 7, 8, 8, 10 }, 8)); - Assertions.assertArrayEquals(new int[] { -1, -1 }, - searchRange(new int[] { 5, 7, 7, 8, 8, 10 }, 6)); - - Assertions.assertEquals(-1, searchLeft(new int[] { 5, 7, 7, 8, 8, 10 }, 3)); - Assertions.assertEquals(0, searchLeft(new int[] { 5, 7, 7, 8, 8, 10 }, 5)); - Assertions.assertEquals(5, searchLeft(new int[] { 5, 7, 7, 8, 8, 10 }, 10)); - Assertions.assertEquals(-1, searchLeft(new int[] { 5, 7, 7, 8, 8, 10 }, 12)); - Assertions.assertEquals(1, searchLeft(new int[] { 5, 7, 7, 8, 8, 10 }, 7)); - - Assertions.assertEquals(-1, searchRight(new int[] { 5, 7, 7, 8, 8, 10 }, 3)); - Assertions.assertEquals(0, searchRight(new int[] { 5, 7, 7, 8, 8, 10 }, 5)); - Assertions.assertEquals(5, searchRight(new int[] { 5, 7, 7, 8, 8, 10 }, 10)); - Assertions.assertEquals(-1, searchRight(new int[] { 5, 7, 7, 8, 8, 10 }, 12)); - Assertions.assertEquals(2, searchRight(new int[] { 5, 7, 7, 8, 8, 10 }, 7)); - } - - /** - * 题目:34. - * 在排序数组中查找元素的第一个和最后一个位置 - *

- * 给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。 - *

- * 如果数组中不存在目标值,返回 [-1, -1]。 - */ - public static int[] searchRange(int[] nums, int target) { - final int[] notFoundResult = { -1, -1 }; - if (nums == null || nums.length == 0) { return notFoundResult; } - - int begin = searchLeft(nums, target); - if (begin == nums.length || nums[begin] != target) { return notFoundResult; } - int end = searchRight(nums, target); - return new int[] { begin, end }; - } - - public static int searchLeft(int[] nums, int target) { - if (nums == null || nums.length == 0) { return -1; } - - int left = 0, right = nums.length - 1; - while (left <= right) { - int mid = left + (right - left) / 2; - if (nums[mid] < target) { - left = mid + 1; - } else if (nums[mid] > target) { - right = mid - 1; - } else if (nums[mid] == target) { - right = mid - 1; - } - } - - if (left >= nums.length || nums[left] != target) { - return -1; - } - return left; - } - - public static int searchRight(int[] nums, int target) { - if (nums == null || nums.length == 0) { return -1; } - - int left = 0, right = nums.length - 1; - while (left <= right) { - int mid = left + (right - left) / 2; - if (nums[mid] > target) { - right = mid - 1; - } else if (nums[mid] < target) { - left = mid + 1; - } else if (nums[mid] == target) { - left = mid + 1; - } - } - - if (right < 0 || nums[right] != target) { - return -1; - } - return right; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\345\234\250\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\346\237\245\346\211\276\346\225\260\345\255\227I.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\345\234\250\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\346\237\245\346\211\276\346\225\260\345\255\227I.java" deleted file mode 100644 index d3c4f6c..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\345\234\250\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\346\237\245\346\211\276\346\225\260\345\255\227I.java" +++ /dev/null @@ -1,40 +0,0 @@ -package io.github.dunwu.algorithm.array; - -import org.junit.jupiter.api.Assertions; - -import java.util.Arrays; - -/** - * @author Zhang Peng - * @since 2020-06-05 - */ -public class 在排序数组中查找数字I { - - public static void main(String[] args) { - Assertions.assertEquals(2, count(8, new Integer[] { 7, 8, 5, 10, 7, 8 })); - Assertions.assertEquals(0, count(6, new Integer[] { 5, 7, 7, 8, 8, 10 })); - Assertions.assertEquals(2, count("abc", new String[] { "abc", "xyz", "lmn", "abc" })); - } - - /** - * 题目:面试题53 - I. - * 在排序数组中查找数字I - *

- * 统计一个元素在数组中出现的次数。 - */ - public static int count(T target, T[] array) { - Arrays.sort(array); - - int count = 0; - for (T i : array) { - if (target.equals(i)) { - count++; - continue; - } - - if (count != 0) { break; } - } - return count; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\240.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\240.java" deleted file mode 100644 index 266542f..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\240.java" +++ /dev/null @@ -1,42 +0,0 @@ -package io.github.dunwu.algorithm.array; - -import org.junit.jupiter.api.Assertions; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -/** - * @author Zhang Peng - * @since 2020-06-05 - */ -public class 存在重复元素 { - - public static void main(String[] args) { - Assertions.assertTrue(containsDuplicate(new Integer[] { 1, 2, 3, 1 })); - Assertions.assertFalse(containsDuplicate(new Integer[] { 1, 2, 3, 4 })); - Assertions.assertTrue(containsDuplicate(new Integer[] { 1, 1, 1, 3, 3, 4, 3, 2, 4, 2 })); - } - - /** - * 题目:217. 存在重复元素 - *

- * 给定一个数组,判断是否存在重复元素。 - *

- * 如果任何值在数组中出现至少两次,函数返回 true。如果数组中每个元素都不相同,则返回 false。 - * - * @param array 数组 - * @return true/false - */ - public static boolean containsDuplicate(T[] array) { - if (array == null || array.length <= 1) { - return false; - } - - Set set = new HashSet<>(); - set.addAll(Arrays.asList(array)); - - return set.size() != array.length; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\345\257\271\350\247\222\347\272\277\351\201\215\345\216\206.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\345\257\271\350\247\222\347\272\277\351\201\215\345\216\206.java" deleted file mode 100644 index eb648a8..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\345\257\271\350\247\222\347\272\277\351\201\215\345\216\206.java" +++ /dev/null @@ -1,125 +0,0 @@ -package io.github.dunwu.algorithm.array; - -// 【对角线遍历】 -// -// 给定一个含有 M x N 个元素的矩阵(M 行,N 列),请以对角线遍历的顺序返回这个矩阵中的所有元素,对角线遍历如下图所示。 -// -// 示例: -// -// 输入: -// [ -// [ 1, 2, 3 ], -// [ 4, 5, 6 ], -// [ 7, 8, 9 ] -// ] -// -// 输出: [1,2,4,7,5,3,6,8,9] -// -// 说明: -// -// 给定矩阵中的元素总数不会超过 100000 。 - -import org.junit.jupiter.api.Assertions; - -/** - * @author Zhang Peng - * @since 2018-11-04 - */ -public class 对角线遍历 { - - public static void main(String[] args) { - - int[][] matrix = { { 1, 2 }, { 3, 4 } }; - int[] expected = { 1, 2, 3, 4 }; - - int[][] matrix2 = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } }; - int[] expected2 = { 1, 2, 4, 7, 5, 3, 6, 8, 9 }; - - int[][] matrix3 = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 }, { 13, 14, 15, 16 } }; - int[] expected3 = { 1, 2, 5, 9, 6, 3, 4, 7, 10, 13, 14, 11, 8, 12, 15, 16 }; - - Assertions.assertArrayEquals(expected, 对角线遍历.findDiagonalOrder(matrix)); - Assertions.assertArrayEquals(expected2, 对角线遍历.findDiagonalOrder(matrix2)); - Assertions.assertArrayEquals(expected3, 对角线遍历.findDiagonalOrder(matrix3)); - } - - public static int[] findDiagonalOrder(int[][] matrix) { - if (matrix.length == 0) { - return new int[0]; - } - - int x = 0, y = 0; - final int M = matrix.length; - final int N = matrix[0].length; - int[] arr = new int[M * N]; - for (int i = 0; i < arr.length; i++) { - arr[i] = matrix[x][y]; - if ((x + y) % 2 == 0) { - if (y == N - 1) { - x++; - } else if (x == 0) { - y++; - } else { - x--; - y++; - } - } else { - if (x == M - 1) { - y++; - } else if (y == 0) { - x++; - } else { - x++; - y--; - } - } - } - return arr; - } - - public static int[] findDiagonalOrder2(int[][] matrix) { - final int UP = 1; - final int DOWN = 2; - final int M = matrix.length; - final int N = matrix[0].length; - int i = 0, j = 0, status = UP; - - int[] result = new int[M * N]; - // System.out.println("========================================"); - // System.out.println(JSONUtil.toJsonStr(matrix)); - // System.out.println("========================================"); - int index = 0; - while (i < M && j < N) { - result[index] = matrix[i][j]; - System.out.println(result[index]); - index++; - if (status == UP) { - if (i == 0 || j == N - 1) { - status = DOWN; - if (j == N - 1) { - i++; - } else { - j++; - } - } else { - i--; - j++; - } - } else { - if (j == 0 || i == M - 1) { - status = UP; - if (i == M - 1) { - j++; - } else { - i++; - } - } else { - i++; - j--; - } - } - } - return result; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\345\257\273\346\211\276\346\225\260\347\273\204\347\232\204\344\270\255\345\277\203\347\264\242\345\274\225.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\345\257\273\346\211\276\346\225\260\347\273\204\347\232\204\344\270\255\345\277\203\347\264\242\345\274\225.java" deleted file mode 100644 index a6d10be..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\345\257\273\346\211\276\346\225\260\347\273\204\347\232\204\344\270\255\345\277\203\347\264\242\345\274\225.java" +++ /dev/null @@ -1,101 +0,0 @@ -// 724. 寻找数组的中心下标 -// -// 给你一个整数数组nums ,请计算数组的 中心下标 。 -// -// 数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。 -// -// 如果中心下标位于数组最左端,那么左侧数之和视为 0 ,因为在下标的左侧不存在元素。这一点对于中心下标位于数组最右端同样适用。 -// -// 如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回 -1 。 -// -// -// -// 示例 1: -// -// 输入:nums = [1, 7, 3, 6, 5, 6] -// 输出:3 -// 解释: -// 中心下标是 3 。 -// 左侧数之和 sum = nums[0] + nums[1] + nums[2] = 1 + 7 + 3 = 11 , -// 右侧数之和 sum = nums[4] + nums[5] = 5 + 6 = 11 ,二者相等。 -// 示例 2: -// -// 输入:nums = [1, 2, 3] -// 输出:-1 -// 解释: -// 数组中不存在满足此条件的中心下标。 -// 示例 3: -// -// 输入:nums = [2, 1, -1] -// 输出:0 -// 解释: -// 中心下标是 0 。 -// 左侧数之和 sum = 0 ,(下标 0 左侧不存在元素), -// 右侧数之和 sum = nums[1] + nums[2] = 1 + -1 = 0 。 -// -// 提示: -// -// 1 <= nums.length <= 104 -// -1000 <= nums[i] <= 1000 -// -// 链接:https://leetcode-cn.com/problems/find-pivot-index - -package io.github.dunwu.algorithm.array; - -import org.junit.jupiter.api.Assertions; - -import java.util.Arrays; - -/** - * 724. 寻找数组的中心索引 - * - * @author Zhang Peng - * @since 2018-11-04 - */ -public class 寻找数组的中心索引 { - - public static void main(String[] args) { - Assertions.assertEquals(3, pivotIndex(new int[] { 1, 7, 3, 6, 5, 6 })); - Assertions.assertEquals(-1, pivotIndex(new int[] { 1, 2, 3 })); - Assertions.assertEquals(0, pivotIndex(new int[] { 2, 1, -1 })); - - Assertions.assertEquals(3, pivotIndex2(new int[] { 1, 7, 3, 6, 5, 6 })); - Assertions.assertEquals(-1, pivotIndex2(new int[] { 1, 2, 3 })); - Assertions.assertEquals(0, pivotIndex2(new int[] { 2, 1, -1 })); - } - - public static int pivotIndex(int[] nums) { - int sum = 0; - int total = Arrays.stream(nums).sum(); - for (int pos = 0; pos < nums.length; pos++) { - if (sum * 2 + nums[pos] == total) { - return pos; - } - sum += nums[pos]; - } - return -1; - } - - public static int pivotIndex2(int[] nums) { - for (int pos = 0; pos < nums.length; pos++) { - - // pos 左侧所有元素累加 - int leftSum = 0; - for (int left = 0; left < pos; left++) { - leftSum += nums[left]; - } - - // pos 右侧所有元素累加 - int rightSum = 0; - for (int right = nums.length - 1; right > pos; right--) { - rightSum += nums[right]; - } - - if (leftSum == rightSum) { - return pos; - } - } - return -1; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\345\260\206\346\225\260\347\273\204\345\210\206\346\210\220\345\222\214\347\233\270\347\255\211\347\232\204\344\270\211\344\270\252\351\203\250\345\210\206.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\345\260\206\346\225\260\347\273\204\345\210\206\346\210\220\345\222\214\347\233\270\347\255\211\347\232\204\344\270\211\344\270\252\351\203\250\345\210\206.java" deleted file mode 100644 index 941ac0f..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\345\260\206\346\225\260\347\273\204\345\210\206\346\210\220\345\222\214\347\233\270\347\255\211\347\232\204\344\270\211\344\270\252\351\203\250\345\210\206.java" +++ /dev/null @@ -1,30 +0,0 @@ -package io.github.dunwu.algorithm.array; - -import org.junit.jupiter.api.Assertions; - -/** - * @author Zhang Peng - * @since 2020-06-05 - */ -public class 将数组分成和相等的三个部分 { - - public static void main(String[] args) { - Assertions.assertTrue(canThreePartsEqualSum(new int[] { 0, 2, 1, -6, 6, -7, 9, 1, 2, 0, 1 })); - Assertions.assertTrue(canThreePartsEqualSum(new int[] { 3, 3, 6, 5, -2, 2, 5, 1, -9, 4 })); - Assertions.assertFalse(canThreePartsEqualSum(new int[] { 0, 2, 1, -6, 6, 7, 9, -1, 2, 0, 1 })); - } - - /** - * 题目:1013. - * 将数组分成和相等的三个部分 - *

- * 给你一个整数数组 A,只有可以将其划分为三个和相等的非空部分时才返回 true,否则返回 false。 - *

- * 形式上,如果可以找出索引 i+1 < j 且满足 (A[0] + A[1] + ... + A[i] == A[i+1] + A[i+2] + ... + A[j-1] == A[j] + A[j-1] + ... + - * A[A.length - 1]) 就可以将数组三等分。 - */ - public static boolean canThreePartsEqualSum(int[] array) { - return false; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\220\234\347\264\242\346\217\222\345\205\245\344\275\215\347\275\256.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\220\234\347\264\242\346\217\222\345\205\245\344\275\215\347\275\256.java" deleted file mode 100644 index 8a0a55e..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\220\234\347\264\242\346\217\222\345\205\245\344\275\215\347\275\256.java" +++ /dev/null @@ -1,69 +0,0 @@ -// 35. 搜索插入位置 -// -// 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 -// -// 请必须使用时间复杂度为 O(log n) 的算法。 -// -// -// -// 示例 1: -// -// 输入: nums = [1,3,5,6], target = 5 -// 输出: 2 -// 示例 2: -// -// 输入: nums = [1,3,5,6], target = 2 -// 输出: 1 -// 示例 3: -// -// 输入: nums = [1,3,5,6], target = 7 -// 输出: 4 -// 示例 4: -// -// 输入: nums = [1,3,5,6], target = 0 -// 输出: 0 -// 示例 5: -// -// 输入: nums = [1], target = 0 -// 输出: 0 -// -// -// 提示: -// -// 1 <= nums.length <= 104 -// -104 <= nums[i] <= 104 -// nums 为无重复元素的升序排列数组 -// -104 <= target <= 104 -// -// 来源:力扣(LeetCode) -// 链接:https://leetcode-cn.com/problems/search-insert-position - -package io.github.dunwu.algorithm.array; - -import org.junit.jupiter.api.Assertions; - -/** - * @author Zhang Peng - * @see 搜索插入位置 - * @since 2020-07-29 - */ -public class 搜索插入位置 { - - public static void main(String[] args) { - Assertions.assertEquals(0, searchInsert(new int[] { 1 }, 1)); - Assertions.assertEquals(2, searchInsert(new int[] { 1, 3, 5, 6 }, 5)); - Assertions.assertEquals(1, searchInsert(new int[] { 1, 3, 5, 6 }, 2)); - Assertions.assertEquals(4, searchInsert(new int[] { 1, 3, 5, 6 }, 7)); - Assertions.assertEquals(0, searchInsert(new int[] { 1, 3, 5, 6 }, 0)); - } - - public static int searchInsert(int[] nums, int target) { - for (int pos = 0; pos < nums.length; pos++) { - if (nums[pos] >= target) { - return pos; - } - } - return nums.length; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\225\260\347\273\204\344\272\214\345\210\206\346\237\245\346\211\276.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\225\260\347\273\204\344\272\214\345\210\206\346\237\245\346\211\276.java" deleted file mode 100644 index fb81cc9..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\225\260\347\273\204\344\272\214\345\210\206\346\237\245\346\211\276.java" +++ /dev/null @@ -1,40 +0,0 @@ -package io.github.dunwu.algorithm.array; - -import org.junit.jupiter.api.Assertions; - -/** - * @author Zhang Peng - * @since 2020-06-05 - */ -public class 数组二分查找 { - - public static void main(String[] args) { - Assertions.assertEquals(5, binarySearch(new int[] { 5, 7, 7, 8, 8, 10 }, 10)); - Assertions.assertEquals(0, binarySearch(new int[] { 5, 7, 7, 8, 8, 10 }, 5)); - Assertions.assertEquals(2, binarySearch(new int[] { 5, 7, 7, 8, 8, 10 }, 7)); - } - - /** - * 数组二分查找,要求传入的数组是有序排列 - *

- * 参考:https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/solution/er-fen-cha-zhao-suan-fa-xi-jie-xiang-jie-by-labula/ - */ - public static int binarySearch(int[] nums, int target) { - if (nums == null || nums.length == 0) { return -1; } - - int left = 0, right = nums.length - 1; - while (left <= right) { - int mid = left + (right - left) / 2; // 防止 mid 溢出 - if (nums[mid] == target) { - return mid; - } else if (nums[mid] < target) { - left = mid + 1; - } else if (nums[mid] > target) { - right = mid - 1; - } - } - - return -1; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\225\260\347\273\204\346\213\206\345\210\2061.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\225\260\347\273\204\346\213\206\345\210\2061.java" deleted file mode 100644 index 1f2380b..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\225\260\347\273\204\346\213\206\345\210\2061.java" +++ /dev/null @@ -1,42 +0,0 @@ -package io.github.dunwu.algorithm.array; - -// 【数组拆分 I】 -// -// 给定长度为 2n 的数组, 你的任务是将这些数分成 n 对, 例如 (a1, b1), (a2, b2), ..., (an, bn) ,使得从1 到 n 的 min(ai, bi) 总和最大。 -// -// 示例 1: -// -// 输入: [1,4,3,2] -// -// 输出: 4 -// 解释: n 等于 2, 最大总和为 4 = min(1, 2) + min(3, 4). -// 提示: -// -// n 是正整数,范围在 [1, 10000]. -// 数组中的元素范围在 [-10000, 10000]. - -import org.junit.jupiter.api.Assertions; - -import java.util.Arrays; - -/** - * @author Zhang Peng - * @since 2018-11-05 - */ -public class 数组拆分1 { - - public static void main(String[] args) { - int[] nums1 = { 1, 4, 3, 2 }; - Assertions.assertEquals(4, 数组拆分1.arrayPairSum(nums1)); - } - - public static int arrayPairSum(int[] nums) { - Arrays.sort(nums); - int result = 0; - for (int i = 0; i < nums.length; i += 2) { - result += nums[i]; - } - return result; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\227\213\350\275\254\346\225\260\347\273\204.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\227\213\350\275\254\346\225\260\347\273\204.java" deleted file mode 100644 index 7108f7a..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\227\213\350\275\254\346\225\260\347\273\204.java" +++ /dev/null @@ -1,63 +0,0 @@ -package io.github.dunwu.algorithm.array; - -// 【旋转数组】 - -// -// 给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。 -// -// 示例 1: -// -// 输入: [1,2,3,4,5,6,7] 和 k = 3 -// 输出: [5,6,7,1,2,3,4] -// 解释: -// 向右旋转 1 步: [7,1,2,3,4,5,6] -// 向右旋转 2 步: [6,7,1,2,3,4,5] -// 向右旋转 3 步: [5,6,7,1,2,3,4] -// 示例 2: -// -// 输入: [-1,-100,3,99] 和 k = 2 -// 输出: [3,99,-1,-100] -// 解释: -// 向右旋转 1 步: [99,-1,-100,3] -// 向右旋转 2 步: [3,99,-1,-100] -// 说明: -// -// 尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。 -// 要求使用空间复杂度为 O(1) 的原地算法。 - -import org.junit.jupiter.api.Assertions; - -/** - * @author Zhang Peng - * @since 2018-11-05 - */ -public class 旋转数组 { - - public static void main(String[] args) { - int[] nums1 = { 1, 2, 3, 4, 5, 6, 7 }; - int[] expected1 = { 5, 6, 7, 1, 2, 3, 4 }; - 旋转数组.rotate(nums1, 3); - Assertions.assertArrayEquals(expected1, nums1); - - int[] nums2 = { -1, -100, 3, 99 }; - int[] expected2 = { 3, 99, -1, -100 }; - 旋转数组.rotate(nums2, 2); - Assertions.assertArrayEquals(expected2, nums2); - } - - public static void rotate(int[] nums, int k) { - int i = 0; - while (i < k) { - int j = nums.length - 1; - int temp = nums[nums.length - 1]; - while (j > 0) { - nums[j] = nums[j - 1]; - j--; - } - nums[0] = temp; - // System.out.println(ArrayUtil.getArrayString(nums, 0, nums.length - 1)); - i++; - } - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\227\213\350\275\254\347\237\251\351\230\265.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\227\213\350\275\254\347\237\251\351\230\265.java" deleted file mode 100644 index d040256..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\227\213\350\275\254\347\237\251\351\230\265.java" +++ /dev/null @@ -1,45 +0,0 @@ -package io.github.dunwu.algorithm.array; - -import org.junit.jupiter.api.Assertions; - -/** - * @author Zhang Peng - * @since 2020-06-05 - */ -public class 旋转矩阵 { - - public static void main(String[] args) { - int[][] array = { - { 1, 2, 3 }, - { 4, 5, 6 }, - { 7, 8, 9 } - }; - int[][] array2 = { - { 7, 4, 1 }, - { 8, 5, 2 }, - { 9, 6, 3 } - }; - rotate(array); - Assertions.assertArrayEquals(array2, array); - } - - /** - * @see 07. 旋转矩阵 - */ - public static void rotate(int[][] matrix) { - int row = matrix.length; - int column = matrix[0].length; - int[][] array = new int[row][column]; - for (int i = 0; i < row; i++) { - for (int j = 0; j < column; j++) { - array[j][row - i - 1] = matrix[i][j]; - } - } - for (int i = 0; i < row; i++) { - for (int j = 0; j < column; j++) { - matrix[i][j] = array[i][j]; - } - } - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\234\200\345\244\247\350\277\236\347\273\2551\347\232\204\344\270\252\346\225\260.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\234\200\345\244\247\350\277\236\347\273\2551\347\232\204\344\270\252\346\225\260.java" deleted file mode 100644 index 589c482..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\234\200\345\244\247\350\277\236\347\273\2551\347\232\204\344\270\252\346\225\260.java" +++ /dev/null @@ -1,50 +0,0 @@ -package io.github.dunwu.algorithm.array; - -// 【最大连续1的个数】 - -// -// 给定一个二进制数组, 计算其中最大连续1的个数。 -// -// 示例 1: -// -// 输入: [1,1,0,1,1,1] -// 输出: 3 -// 解释: 开头的两位和最后的三位都是连续1,所以最大连续1的个数是 3. -// 注意: -// -// 输入的数组只包含 0 和1。 -// 输入数组的长度是正整数,且不超过 10,000。 - -import org.junit.jupiter.api.Assertions; - -/** - * @author Zhang Peng - * @since 2018-11-05 - */ -public class 最大连续1的个数 { - - public static void main(String[] args) { - Assertions.assertEquals(3, 最大连续1的个数.findMaxConsecutiveOnes(new int[] { 1, 1, 0, 1, 1, 1 })); - } - - public static int findMaxConsecutiveOnes(int[] nums) { - int max = 0; - int count = 0; - for (int i = 0; i < nums.length; i++) { - if (nums[i] == 1) { - count++; - } else { - if (count > max) { - max = count; - } - count = 0; - } - } - - if (count > max) { - max = count; - } - return max; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\234\211\345\272\217\347\237\251\351\230\265\344\270\255\347\254\254K\345\260\217\347\232\204\345\205\203\347\264\240.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\234\211\345\272\217\347\237\251\351\230\265\344\270\255\347\254\254K\345\260\217\347\232\204\345\205\203\347\264\240.java" deleted file mode 100644 index ef0e1ac..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\234\211\345\272\217\347\237\251\351\230\265\344\270\255\347\254\254K\345\260\217\347\232\204\345\205\203\347\264\240.java" +++ /dev/null @@ -1,63 +0,0 @@ -package io.github.dunwu.algorithm.array; - -import org.junit.jupiter.api.Assertions; - -/** - * @author Zhang Peng - * @date 2025-01-21 - */ -public class 有序矩阵中第K小的元素 { - - public static void main(String[] args) { - - int[][] matrix = { { 1, 5, 9 }, { 10, 11, 13 }, { 12, 13, 15 } }; - Assertions.assertEquals(13, kthSmallest(matrix, 8)); - - int[][] matrix2 = { { -5 } }; - Assertions.assertEquals(-5, kthSmallest(matrix2, 1)); - - int[][] matrix3 = { { 1, 2 }, { 1, 3 } }; - Assertions.assertEquals(1, kthSmallest(matrix3, 2)); - - int[][] matrix4 = { { 1, 2, 3 }, { 1, 2, 3 }, { 1, 2, 3 } }; - Assertions.assertEquals(3, kthSmallest(matrix4, 8)); - } - - public static int kthSmallest(int[][] matrix, int n) { - int row = matrix.length; - if (row == 1) { - return matrix[0][n - 1]; - } - int i = 1; - int[] arr = matrix[0]; - while (i < row) { - arr = merge(matrix[i], arr); - i++; - } - return arr[n - 1]; - } - - public static int[] merge(int[] arr1, int[] arr2) { - int i = 0, j = 0, k = 0; - int[] merge = new int[arr1.length + arr2.length]; - while (i < arr1.length && j < arr2.length) { - if (arr1[i] <= arr2[j]) { - merge[k++] = arr1[i++]; - } else { - merge[k++] = arr2[j++]; - } - } - if (i < arr1.length) { - while (i < arr1.length) { - merge[k++] = arr1[i++]; - } - } - if (j < arr2.length) { - while (j < arr2.length) { - merge[k++] = arr2[j++]; - } - } - return merge; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\235\250\350\276\211\344\270\211\350\247\222.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\235\250\350\276\211\344\270\211\350\247\222.java" deleted file mode 100644 index 51ca60f..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\235\250\350\276\211\344\270\211\350\247\222.java" +++ /dev/null @@ -1,83 +0,0 @@ -package io.github.dunwu.algorithm.array; - -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -// 【杨辉三角】 -// -// 给定一个非负整数 numRows,生成杨辉三角的前 numRows 行。 -// -// 在杨辉三角中,每个数是它左上方和右上方的数的和。 -// -// 示例: -// -// 输入: 5 -// 输出: -// [ -// [1], -// [1,1], -// [1,2,1], -// [1,3,3,1], -// [1,4,6,4,1] -// ] - -/** - * @author Zhang Peng - * @since 2018-11-04 - */ -public class 杨辉三角 { - - public static void main(String[] args) { - List> lists = 杨辉三角.generate(5); - printPascalsTriangle(lists); - } - - public static List> generate(int numRows) { - List> result = new ArrayList<>(); - - if (numRows <= 0) { - - } else if (numRows == 1) { - result.add(Arrays.asList(1)); - } else if (numRows == 2) { - result.add(Arrays.asList(1)); - result.add(Arrays.asList(1, 1)); - } else { - result.add(Arrays.asList(1)); - result.add(Arrays.asList(1, 1)); - for (int i = 2; i < numRows; i++) { - List current = result.get(i - 1); - List next = new ArrayList<>(); - - for (int j = 0; j <= i; j++) { - if (j == 0 || j == i) { - next.add(1); - } else { - int x = current.get(j - 1); - int y = current.get(j); - next.add(x + y); - } - } - - result.add(next); - } - } - - return result; - } - - static void printPascalsTriangle(List> lists) { - System.out.printf("【%d层杨辉三角】\n", lists.size()); - for (List list : lists) { - for (Integer num : list) { - System.out.print(num + "\t"); - } - System.out.println(); - } - System.out.println(); - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\235\250\350\276\211\344\270\211\350\247\2222.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\235\250\350\276\211\344\270\211\350\247\2222.java" deleted file mode 100644 index da70a72..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\235\250\350\276\211\344\270\211\350\247\2222.java" +++ /dev/null @@ -1,69 +0,0 @@ -package io.github.dunwu.algorithm.array; - -import io.github.dunwu.algorithm.util.ArrayUtil; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -// 【杨辉三角 II】 -// -// 给定一个非负索引 k,其中 k ≤ 33,返回杨辉三角的第 k 行。 -// -// 在杨辉三角中,每个数是它左上方和右上方的数的和。 -// -// 示例: -// -// 输入: 3 -// 输出: [1,3,3,1] -// 进阶: -// -// 你可以优化你的算法到 O(k) 空间复杂度吗? - -/** - * @author Zhang Peng - * @since 2018-11-05 - */ -public class 杨辉三角2 { - - public static void main(String[] args) { - List list = 杨辉三角2.getRow(3); - System.out.println(ArrayUtil.getArrayString(list.toArray(), 0, list.size() - 1)); - } - - public static List getRow(int rowIndex) { - List> result = new ArrayList<>(); - - int rows = rowIndex + 1; - if (rows <= 0) { - - } else if (rows == 1) { - result.add(Arrays.asList(1)); - } else if (rows == 2) { - result.add(Arrays.asList(1)); - result.add(Arrays.asList(1, 1)); - } else { - result.add(Arrays.asList(1)); - result.add(Arrays.asList(1, 1)); - for (int i = 2; i < rows; i++) { - List current = result.get(i - 1); - List next = new ArrayList<>(); - - for (int j = 0; j <= i; j++) { - if (j == 0 || j == i) { - next.add(1); - } else { - int x = current.get(j - 1); - int y = current.get(j); - next.add(x + y); - } - } - - result.add(next); - } - } - - return result.get(rowIndex); - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\237\245\346\211\276\345\222\214\346\234\200\345\260\217\347\232\204K\345\257\271\346\225\260\345\255\227.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\237\245\346\211\276\345\222\214\346\234\200\345\260\217\347\232\204K\345\257\271\346\225\260\345\255\227.java" deleted file mode 100644 index e7a6a02..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\237\245\346\211\276\345\222\214\346\234\200\345\260\217\347\232\204K\345\257\271\346\225\260\345\255\227.java" +++ /dev/null @@ -1,85 +0,0 @@ -package io.github.dunwu.algorithm.array; - -import cn.hutool.json.JSONUtil; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.TreeMap; - -/** - * 373. 查找和最小的 K 对数字 - * - * @author Zhang Peng - * @date 2025-01-21 - */ -public class 查找和最小的K对数字 { - - public static void main(String[] args) { - List> expectList1 = new ArrayList<>(); - expectList1.add(Arrays.asList(1, 2)); - expectList1.add(Arrays.asList(1, 4)); - expectList1.add(Arrays.asList(1, 6)); - List> list1 = kSmallestPairs2(new int[] { 1, 7, 11 }, new int[] { 2, 4, 6 }, 3); - System.out.println(JSONUtil.toJsonStr(list1)); - - List> list2 = kSmallestPairs2(new int[] { 1, 1, 2 }, new int[] { 1, 2, 3 }, 2); - System.out.println(JSONUtil.toJsonStr(list2)); - } - - public static List> kSmallestPairs(int[] nums1, int[] nums2, int k) { - List> list = new ArrayList<>(); - list.add(Arrays.asList(0, 0)); - TreeMap map = new TreeMap<>(); - int i = 0, j = 0; - while (i < nums1.length && j < nums2.length) { - - if (i == nums1.length - 1 && j != nums2.length - 1) { - i = 0; - j++; - } else if (i != nums1.length - 1 && j == nums2.length - 1) { - j = 0; - i++; - } - if (i == nums1.length - 1 && j == nums2.length - 1) { - break; - } - - if (nums1[i] + nums2[j + 1] <= nums1[i + 1] + nums2[j]) { - list.add(Arrays.asList(i, j + 1)); - j++; - } else { - list.add(Arrays.asList(i + 1, j)); - i++; - } - - if (i + 1 >= nums1.length && j + 1 >= nums2.length) { } - } - return list; - } - - public static List> kSmallestPairs2(int[] nums1, int[] nums2, int k) { - int i = 0, j = 0; - List> result = new ArrayList<>(); - result.add(Arrays.asList(nums1[i], nums2[j])); - while (i < nums1.length - 1 && j < nums2.length - 1) { - if (nums1[i] + nums2[j + 1] <= nums1[i + 1] + nums2[j]) { - j++; - } else { - i++; - } - result.add(Arrays.asList(nums1[i], nums2[j])); - if (i == nums1.length - 1 && j != nums2.length - 1) { - i = 0; - j++; - } - if (i != nums1.length - 1 && j == nums2.length - 1) { - j = 0; - i++; - } - } - System.out.println(JSONUtil.toJsonStr(result)); - return null; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\237\245\346\211\276\346\200\273\344\273\267\346\240\274\344\270\272\347\233\256\346\240\207\345\200\274\347\232\204\344\270\244\344\270\252\345\225\206\345\223\201.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\237\245\346\211\276\346\200\273\344\273\267\346\240\274\344\270\272\347\233\256\346\240\207\345\200\274\347\232\204\344\270\244\344\270\252\345\225\206\345\223\201.java" deleted file mode 100644 index 1e98ab3..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\346\237\245\346\211\276\346\200\273\344\273\267\346\240\274\344\270\272\347\233\256\346\240\207\345\200\274\347\232\204\344\270\244\344\270\252\345\225\206\345\223\201.java" +++ /dev/null @@ -1,36 +0,0 @@ -package io.github.dunwu.algorithm.array; - -import org.junit.jupiter.api.Assertions; - -/** - * LCR 179. 查找总价格为目标值的两个商品 - * - * @author Zhang Peng - * @date 2025-01-13 - */ -public class 查找总价格为目标值的两个商品 { - - public static void main(String[] args) { - Assertions.assertArrayEquals(new int[] { 3, 15 }, twoSum(new int[] { 3, 9, 12, 15 }, 18)); - Assertions.assertArrayEquals(new int[] { 27, 34 }, twoSum(new int[] { 8, 21, 27, 34, 52, 66 }, 61)); - } - - /** - * 时间复杂度:O(N) - */ - public static int[] twoSum(int[] price, int target) { - int left = 0, right = price.length - 1; - while (left < right) { - int sum = price[left] + price[right]; - if (sum == target) { - return new int[] { price[left], price[right] }; - } else if (sum < target) { - left++; - } else { - right--; - } - } - return new int[] { -1, -1 }; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\347\247\273\345\212\250\351\233\266.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\347\247\273\345\212\250\351\233\266.java" deleted file mode 100644 index 4528b3f..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\347\247\273\345\212\250\351\233\266.java" +++ /dev/null @@ -1,88 +0,0 @@ -package io.github.dunwu.algorithm.array; - -// 【移动零】 - -// -// 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。 -// -// 示例: -// -// 输入: [0,1,0,3,12] -// 输出: [1,3,12,0,0] -// 说明: -// -// 必须在原数组上操作,不能拷贝额外的数组。 -// 尽量减少操作次数。 - -import org.junit.jupiter.api.Assertions; - -/** - * @author Zhang Peng - * @since 2018-11-05 - */ -public class 移动零 { - - public static void main(String[] args) { - int[] nums1 = { 0, 1, 0, 3, 12 }; - moveZeroes2(nums1); - Assertions.assertArrayEquals(new int[] { 1, 3, 12, 0, 0 }, nums1); - - int[] nums2 = { 0, 0, 1 }; - moveZeroes2(nums2); - Assertions.assertArrayEquals(new int[] { 1, 0, 0 }, nums2); - - int[] nums3 = { 0 }; - moveZeroes2(nums3); - Assertions.assertArrayEquals(new int[] { 0 }, nums3); - } - - /** - * 时间复杂度:O(N^2) - */ - public static void moveZeroes(int[] nums) { - int left = 0, right = nums.length - 1; - while (left < right) { - if (nums[left] == 0) { - move(nums, left); - left = 0; - right--; - } else { - left++; - } - } - } - - private static void move(int[] nums, int pos) { - for (int i = pos; i < nums.length - 1; i++) { - int temp = nums[i]; - nums[i] = nums[i + 1]; - nums[i + 1] = temp; - } - } - - /** - * 时间复杂度:O(N) - */ - public static void moveZeroes2(int[] nums) { - int count = removeElement(nums, 0); - while (count < nums.length) { - nums[count++] = 0; - } - } - - public static int removeElement(int[] nums, int val) { - if (nums.length == 0) { - return 0; - } - int slow = 0, fast = 0; - while (fast < nums.length) { - if (nums[fast] != val) { - nums[slow] = nums[fast]; - slow++; - } - fast++; - } - return slow; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\347\247\273\351\231\244\345\205\203\347\264\240.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\347\247\273\351\231\244\345\205\203\347\264\240.java" deleted file mode 100644 index 907341f..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\347\247\273\351\231\244\345\205\203\347\264\240.java" +++ /dev/null @@ -1,76 +0,0 @@ -package io.github.dunwu.algorithm.array; - -// 【移除元素】 - -// -// 给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度。 -// -// 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。 -// -// 元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。 -// -// 示例 1: -// -// 给定 nums = [3,2,2,3], val = 3, -// -// 函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。 -// -// 你不需要考虑数组中超出新长度后面的元素。 -// 示例 2: -// -// 给定 nums = [0,1,2,2,3,0,4,2], val = 2, -// -// 函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。 -// -// 注意这五个元素可为任意顺序。 -// -// 你不需要考虑数组中超出新长度后面的元素。 -// 说明: -// -// 为什么返回数值是整数,但输出的答案是数组呢? -// -// 请注意,输入数组是以“引用”方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。 -// -// 你可以想象内部操作如下: -// -// // nums 是以“引用”方式传递的。也就是说,不对实参作任何拷贝 -// int len = removeElement(nums, val); -// -// // 在函数里修改输入数组对于调用者是可见的。 -// // 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。 -// for (int i = 0; i < len; i++) { -// print(nums[i]); -// } - -import org.junit.jupiter.api.Assertions; - -/** - * @author Zhang Peng - * @since 2018-11-05 - */ -public class 移除元素 { - - public static void main(String[] args) { - int[] nums1 = { 3, 2, 2, 3 }; - Assertions.assertEquals(2, removeElement(nums1, 3)); - - int[] nums2 = { 0, 1, 2, 2, 3, 0, 4, 2 }; - Assertions.assertEquals(5, removeElement(nums2, 2)); - } - - public static int removeElement(int[] nums, int val) { - if (nums.length == 0) { - return 0; - } - int slow = 0, fast = 0; - while (fast < nums.length) { - if (nums[fast] != val) { - nums[slow] = nums[fast]; - slow++; - } - fast++; - } - return slow; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\350\207\263\345\260\221\346\230\257\345\205\266\344\273\226\346\225\260\345\255\227\344\270\244\345\200\215\347\232\204\346\234\200\345\244\247\346\225\260.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\350\207\263\345\260\221\346\230\257\345\205\266\344\273\226\346\225\260\345\255\227\344\270\244\345\200\215\347\232\204\346\234\200\345\244\247\346\225\260.java" deleted file mode 100644 index 332bf0c..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\350\207\263\345\260\221\346\230\257\345\205\266\344\273\226\346\225\260\345\255\227\344\270\244\345\200\215\347\232\204\346\234\200\345\244\247\346\225\260.java" +++ /dev/null @@ -1,67 +0,0 @@ -package io.github.dunwu.algorithm.array; - -import org.junit.jupiter.api.Assertions; - -// 【至少是其他数字两倍的最大数】 -// -// 在一个给定的数组nums中,总是存在一个最大元素 。 -// -// 查找数组中的最大元素是否至少是数组中每个其他数字的两倍。 -// -// 如果是,则返回最大元素的索引,否则返回-1。 -// -// 示例 1: -// -// 输入: nums = [3, 6, 1, 0] -// 输出: 1 -// 解释: 6是最大的整数, 对于数组中的其他整数, -// 6大于数组中其他元素的两倍。6的索引是1, 所以我们返回1. -// -// -// 示例 2: -// -// 输入: nums = [1, 2, 3, 4] -// 输出: -1 -// 解释: 4没有超过3的两倍大, 所以我们返回 -1. -// -// -// 提示: -// -// nums 的长度范围在[1, 50]. -// 每个 nums[i] 的整数范围在 [0, 99]. - -/** - * @author Zhang Peng - * @since 2018-11-04 - */ -public class 至少是其他数字两倍的最大数 { - - public static void main(String[] args) { - int[] nums1 = { 3, 6, 1, 0 }; - int[] nums2 = { 1, 2, 3, 4 }; - - Assertions.assertEquals(1, 至少是其他数字两倍的最大数.dominantIndex(nums1)); - Assertions.assertEquals(-1, 至少是其他数字两倍的最大数.dominantIndex(nums2)); - } - - public static int dominantIndex(int[] nums) { - int index = 0; - while (index < nums.length) { - boolean isMatch = true; - int max = nums[index]; - for (int i = 0; i < nums.length; i++) { - if (index != i && max < nums[i] * 2) { - isMatch = false; - break; - } - } - if (isMatch) { - return index; - } else { - index++; - } - } - return -1; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\350\236\272\346\227\213\347\237\251\351\230\265.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\350\236\272\346\227\213\347\237\251\351\230\265.java" deleted file mode 100644 index 95a3576..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\350\236\272\346\227\213\347\237\251\351\230\265.java" +++ /dev/null @@ -1,85 +0,0 @@ -package io.github.dunwu.algorithm.array; - -import org.junit.jupiter.api.Assertions; - -import java.util.ArrayList; -import java.util.List; - -// 【螺旋矩阵】 -// -// 给定一个包含 m x n 个元素的矩阵(m 行, n 列),请按照顺时针螺旋顺序,返回矩阵中的所有元素。 -// -// 示例 1: -// -// 输入: -// [ -// [ 1, 2, 3 ], -// [ 4, 5, 6 ], -// [ 7, 8, 9 ] -// ] -// 输出: [1,2,3,6,9,8,7,4,5] -// 示例 2: -// -// 输入: -// [ -// [1, 2, 3, 4], -// [5, 6, 7, 8], -// [9,10,11,12] -// ] -// 输出: [1,2,3,4,8,12,11,10,9,5,6,7] - -/** - * @author Zhang Peng - * @since 2018-11-04 - */ -public class 螺旋矩阵 { - - public static void main(String[] args) { - int[] nums1 = { 1, 2, 3, 4, 5, 6, 7 }; - int[] expected1 = { 5, 6, 7, 1, 2, 3, 4 }; - 旋转数组.rotate(nums1, 3); - Assertions.assertArrayEquals(expected1, nums1); - - int[] nums2 = { -1, -100, 3, 99 }; - int[] expected2 = { 3, 99, -1, -100 }; - 旋转数组.rotate(nums2, 2); - Assertions.assertArrayEquals(expected2, nums2); - } - - public static List spiralOrder(int[][] matrix) { - ArrayList list = new ArrayList<>(); - if (matrix.length == 0) { - return list; - } - - final int M = matrix.length; - final int N = matrix[0].length; - final int MAX = M * N; - int x = 0, y = 0; - int XMIN = 0, YMIN = 0; - int XMAX = M - 1, YMAX = N - 1; - for (int index = 0; index < MAX; index++) { - list.add(matrix[x][y]); - - if (x == XMIN && y != YMAX) { - y++; - } else if (y == YMAX && x != XMAX) { - x++; - } else if (x == XMAX && y != YMIN) { - y--; - } else if (y == YMIN && x != XMIN + 1) { - x--; - } else if (x == XMIN + 1 && y == YMIN) { - XMIN = XMIN + 1; - YMIN = YMIN + 1; - XMAX = XMAX - 1; - YMAX = YMAX - 1; - x = XMIN; - y = YMIN; - } - } - - return list; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\350\266\205\347\272\247\344\270\221\346\225\260.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\350\266\205\347\272\247\344\270\221\346\225\260.java" deleted file mode 100644 index c372b91..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\350\266\205\347\272\247\344\270\221\346\225\260.java" +++ /dev/null @@ -1,45 +0,0 @@ -package io.github.dunwu.algorithm.array; - -import org.junit.jupiter.api.Assertions; - -/** - * 313. 超级丑数 - * - * @author Zhang Peng - * @date 2025-01-24 - */ -public class 超级丑数 { - - public static void main(String[] args) { - Assertions.assertEquals(32, nthSuperUglyNumber(12, new int[] { 2, 7, 13, 19 })); - Assertions.assertEquals(1, nthSuperUglyNumber(1, new int[] { 2, 3, 5 })); - } - - public static int nthSuperUglyNumber(int n, int[] primes) { - int len = primes.length; - int[] offsets = new int[len]; - long[] values = new long[len]; - long[] ugly = new long[n + 1]; - int u = 1; - for (int i = 0; i < len; i++) { - offsets[i] = 1; - values[i] = 1; - } - while (u <= n) { - long min = Integer.MAX_VALUE; - for (int i = 0; i < len; i++) { - min = Math.min(values[i], min); - } - ugly[u++] = min; - - for (int i = 0; i < len; i++) { - if (values[i] == min) { - values[i] = primes[i] * ugly[offsets[i]]; - offsets[i] = offsets[i] + 1; - } - } - } - return (int) ugly[n]; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204.java" deleted file mode 100644 index 6664fa0..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204.java" +++ /dev/null @@ -1,47 +0,0 @@ -package io.github.dunwu.algorithm.array; - -// 【长度最小的子数组】 - -// -// 给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组。如果不存在符合条件的连续子数组,返回 0。 -// -// 示例: -// -// 输入: s = 7, nums = [2,3,1,2,4,3] -// 输出: 2 -// 解释: 子数组 [4,3] 是该条件下的长度最小的连续子数组。 -// 进阶: -// -// 如果你已经完成了O(n) 时间复杂度的解法, 请尝试 O(n log n) 时间复杂度的解法。 - -/** - * @author Zhang Peng - * @since 2018-11-05 - */ -public class 长度最小的子数组 { - - public static void main(String[] args) { - 长度最小的子数组.minSubArrayLen(7, new int[] { 2, 3, 1, 2, 4, 3 }); - 长度最小的子数组.minSubArrayLen(11, new int[] { 2, 3, 1, 2, 4, 3 }); - } - - public static int minSubArrayLen(int s, int[] nums) { - if (nums == null || nums.length == 0) { - return 0; - } - - int j = 0, i = 0, sum = 0, min = Integer.MAX_VALUE; - - while (i < nums.length) { - sum += nums[i++]; - - while (sum >= s) { - min = Math.min(min, i - j); - sum -= nums[j++]; - } - } - - return min == Integer.MAX_VALUE ? 0 : min; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\351\233\266\347\237\251\351\230\265.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\351\233\266\347\237\251\351\230\265.java" deleted file mode 100644 index be09756..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/array/\351\233\266\347\237\251\351\230\265.java" +++ /dev/null @@ -1,73 +0,0 @@ -package io.github.dunwu.algorithm.array; - -import org.junit.jupiter.api.Assertions; - -import java.util.ArrayList; -import java.util.List; - -/** - * @author Zhang Peng - * @since 2020-06-05 - */ -public class 零矩阵 { - - public static void main(String[] args) { - int[][] array = { - { 1, 1, 1 }, - { 1, 0, 1 }, - { 1, 1, 1 } - }; - int[][] array2 = { - { 1, 0, 1 }, - { 0, 0, 0 }, - { 1, 0, 1 } - }; - setZeroes(array); - // setZeroForElement(array, 1, 1); - Assertions.assertArrayEquals(array2, array); - } - - /** - * @see 08. 零矩阵 - */ - public static void setZeroes(int[][] matrix) { - int row = matrix.length; - int column = matrix[0].length; - List list = new ArrayList<>(); - for (int i = 0; i < row; i++) { - for (int j = 0; j < column; j++) { - if (matrix[i][j] == 0) { - list.add(new Point(i, j)); - } - } - } - - list.forEach(p -> { - setZeroForElement(matrix, p.i, p.j); - }); - } - - public static void setZeroForElement(int[][] matrix, int x, int y) { - int row = matrix.length; - int column = matrix[0].length; - for (int i = 0; i < row; i++) { - matrix[i][y] = 0; - } - for (int j = 0; j < column; j++) { - matrix[x][j] = 0; - } - } - - static class Point { - - public int i; - public int j; - - public Point(int i, int j) { - this.i = i; - this.j = j; - } - - } - -} diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/bfs/package-info.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/bfs/package-info.java new file mode 100644 index 0000000..04a66a6 --- /dev/null +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/bfs/package-info.java @@ -0,0 +1,7 @@ +/** + * 通过 BFS 解最短路径类型问题 + * + * @author Zhang Peng + * @date 2025-12-15 + */ +package io.github.dunwu.algorithm.bfs; \ No newline at end of file diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/bfs/template/BFS\347\256\227\346\263\225\346\250\241\346\235\277.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/bfs/template/BFS\347\256\227\346\263\225\346\250\241\346\235\277.java" new file mode 100644 index 0000000..0448d03 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/bfs/template/BFS\347\256\227\346\263\225\346\250\241\346\235\277.java" @@ -0,0 +1,51 @@ +package io.github.dunwu.algorithm.bfs.template; + +import io.github.dunwu.algorithm.graph.Edge; +import io.github.dunwu.algorithm.graph.Graph; + +import java.util.LinkedList; +import java.util.Queue; + +/** + * BFS 算法模板 + * + * @author Zhang Peng + * @date 2025-12-15 + */ +public class BFS算法模板 { + + private Graph graph; + + // 从 s 开始 BFS 遍历图的所有节点,且记录遍历的步数 + // 当走到目标节点 target 时,返回步数 + int bfs(int s, int target) { + boolean[] visited = new boolean[graph.size()]; + Queue q = new LinkedList<>(); + q.offer(s); + visited[s] = true; + // 记录从 s 开始走到当前节点的步数 + int step = 0; + while (!q.isEmpty()) { + int sz = q.size(); + for (int i = 0; i < sz; i++) { + int cur = q.poll(); + System.out.println("visit " + cur + " at step " + step); + // 判断是否到达终点 + if (cur == target) { + return step; + } + // 将邻居节点加入队列,向四周扩散搜索 + for (Edge e : graph.neighbors(cur)) { + if (!visited[e.to]) { + q.offer(e.to); + visited[e.to] = true; + } + } + } + step++; + } + // 如果走到这里,说明在图中没有找到目标节点 + return -1; + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/bfs/\344\272\214\350\277\233\345\210\266\347\237\251\351\230\265\344\270\255\347\232\204\346\234\200\347\237\255\350\267\257\345\276\204.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/bfs/\344\272\214\350\277\233\345\210\266\347\237\251\351\230\265\344\270\255\347\232\204\346\234\200\347\237\255\350\267\257\345\276\204.java" new file mode 100644 index 0000000..ca60ee9 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/bfs/\344\272\214\350\277\233\345\210\266\347\237\251\351\230\265\344\270\255\347\232\204\346\234\200\347\237\255\350\267\257\345\276\204.java" @@ -0,0 +1,70 @@ +package io.github.dunwu.algorithm.bfs; + +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; + +/** + * 1091. 二进制矩阵中的最短路径 + * + * @author Zhang Peng + * @date 2025-12-15 + */ +public class 二进制矩阵中的最短路径 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(2, s.shortestPathBinaryMatrix(new int[][] { { 0, 1 }, { 1, 0 } })); + Assertions.assertEquals(4, s.shortestPathBinaryMatrix(new int[][] { { 0, 0, 0 }, { 1, 1, 0 }, { 1, 1, 0 } })); + Assertions.assertEquals(-1, s.shortestPathBinaryMatrix(new int[][] { { 1, 0, 0 }, { 1, 1, 0 }, { 1, 1, 0 } })); + } + + static class Solution { + + // 八个方向偏移量(上、下、左、右、左上、右下、左下、右上) + private final int[][] directions = { + { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 }, + { -1, -1 }, { 1, 1 }, { -1, 1 }, { 1, -1 } + }; + + public int shortestPathBinaryMatrix(int[][] grid) { + + int m = grid.length, n = grid[0].length; + if (grid[0][0] == 1 || grid[m - 1][n - 1] == 1) { + return -1; + } + + // 需要记录走过的路径,避免死循环 + boolean[][] visited = new boolean[m][n]; + LinkedList queue = new LinkedList<>(); + + // 初始化队列,从 (0, 0) 出发 + visited[0][0] = true; + queue.offer(new int[] { 0, 0 }); + + int step = 1; + while (!queue.isEmpty()) { + int size = queue.size(); + for (int i = 0; i < size; i++) { + int[] cur = queue.poll(); + int x = cur[0], y = cur[1]; + if (grid[x][y] != 0) { return -1; } + // 到达底部,返回步骤数 + if (x == m - 1 && y == n - 1) { return step; } + + for (int[] d : directions) { + int nextX = x + d[0], nextY = y + d[1]; + if (nextX < 0 || nextX >= m || nextY < 0 || nextY >= n) { continue; } + if (visited[nextX][nextY] || grid[nextX][nextY] != 0) { continue; } + visited[nextX][nextY] = true; + queue.offer(new int[] { nextX, nextY }); + } + } + step++; + } + return -1; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/bfs/\345\256\214\345\205\250\344\272\214\345\217\211\346\240\221\346\217\222\345\205\245\345\231\250.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/bfs/\345\256\214\345\205\250\344\272\214\345\217\211\346\240\221\346\217\222\345\205\245\345\231\250.java" new file mode 100644 index 0000000..77990db --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/bfs/\345\256\214\345\205\250\344\272\214\345\217\211\346\240\221\346\217\222\345\205\245\345\231\250.java" @@ -0,0 +1,68 @@ +package io.github.dunwu.algorithm.bfs; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; + +/** + * 919. 完全二叉树插入器 + * + * @author Zhang Peng + * @date 2025-11-07 + */ +public class 完全二叉树插入器 { + + public static void main(String[] args) { + CBTInserter c = new CBTInserter(TreeNode.buildTree(1, 2)); + Assertions.assertEquals(1, c.insert(3)); + Assertions.assertEquals(2, c.insert(4)); + Assertions.assertEquals(TreeNode.buildTree(1, 2, 3, 4), c.get_root()); + } + + static class CBTInserter { + + private final TreeNode root; + // 这个队列只记录完全二叉树底部可以进行插入的节点 + private final LinkedList queue; + + public CBTInserter(TreeNode root) { + this.root = root; + this.queue = new LinkedList<>(); + LinkedList tmp = new LinkedList<>(); + tmp.offer(root); + while (!tmp.isEmpty()) { + int size = tmp.size(); + for (int i = 0; i < size; i++) { + TreeNode node = tmp.poll(); + if (node == null) { continue; } + if (node.left != null) { tmp.offer(node.left); } + if (node.right != null) { tmp.offer(node.right); } + if (node.left == null || node.right == null) { + // 找到完全二叉树底部可以进行插入的节点 + queue.offer(node); + } + } + } + } + + public int insert(int val) { + TreeNode node = new TreeNode(val); + TreeNode cur = queue.peek(); + queue.offer(node); + if (cur.left == null) { + cur.left = node; + } else if (cur.right == null) { + cur.right = node; + queue.poll(); + } + return cur.val; + } + + public TreeNode get_root() { + return this.root; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/bfs/\346\211\223\345\274\200\350\275\254\347\233\230\351\224\201.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/bfs/\346\211\223\345\274\200\350\275\254\347\233\230\351\224\201.java" new file mode 100644 index 0000000..8a4c291 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/bfs/\346\211\223\345\274\200\350\275\254\347\233\230\351\224\201.java" @@ -0,0 +1,100 @@ +package io.github.dunwu.algorithm.bfs; + +import org.junit.jupiter.api.Assertions; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +/** + * 297. 二叉树的序列化与反序列化 + * + * @author Zhang Peng + * @date 2025-11-06 + */ +public class 打开转盘锁 { + + public static void main(String[] args) { + + Solution s = new Solution(); + + String[] deadends = new String[] { "0201", "0101", "0102", "1212", "2002" }; + Assertions.assertEquals(6, s.openLock(deadends, "0202")); + + String[] deadends2 = new String[] { "8888" }; + Assertions.assertEquals(1, s.openLock(deadends2, "0009")); + + String[] deadends3 = new String[] { "8887", "8889", "8878", "8898", "8788", "8988", "7888", "9888" }; + Assertions.assertEquals(-1, s.openLock(deadends3, "8888")); + } + + static class Solution { + + public int openLock(String[] deadends, String target) { + int step = 0; + + Set blackSet = new HashSet<>(); + Collections.addAll(blackSet, deadends); + + if (blackSet.contains("0000")) { return -1; } + + Set visited = new HashSet<>(); + LinkedList queue = new LinkedList<>(); + visited.add("0000"); + queue.offer("0000"); + + while (!queue.isEmpty()) { + int size = queue.size(); + for (int i = 0; i < size; i++) { + String cur = queue.poll(); + if (cur.equals(target)) { + return step; + } + + for (String neighbour : neighbours(cur)) { + if (!visited.contains(neighbour) && !blackSet.contains(neighbour)) { + visited.add(neighbour); + queue.offer(neighbour); + } + } + } + step++; + } + return -1; + } + + public String plus(String s, int i) { + char[] ch = s.toCharArray(); + if (ch[i] == '9') { + ch[i] = '0'; + } else { + ch[i] += 1; + } + return new String(ch); + } + + public String minus(String s, int i) { + char[] ch = s.toCharArray(); + if (ch[i] == '0') { + ch[i] = '9'; + } else { + ch[i] -= 1; + } + return new String(ch); + } + + public List neighbours(String s) { + List neighbours = new ArrayList<>(); + for (int i = 0; i < s.length(); i++) { + neighbours.add(plus(s, i)); + neighbours.add(minus(s, i)); + } + return neighbours; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/bfs/\346\234\200\345\260\217\345\237\272\345\233\240\345\217\230\345\214\226.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/bfs/\346\234\200\345\260\217\345\237\272\345\233\240\345\217\230\345\214\226.java" new file mode 100644 index 0000000..c52c342 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/bfs/\346\234\200\345\260\217\345\237\272\345\233\240\345\217\230\345\214\226.java" @@ -0,0 +1,82 @@ +package io.github.dunwu.algorithm.bfs; + +import org.junit.jupiter.api.Assertions; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +/** + * 433. 最小基因变化 + * + * @author Zhang Peng + * @date 2025-11-07 + */ +public class 最小基因变化 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(1, + s.minMutation("AACCGGTT", "AACCGGTA", new String[] { "AACCGGTA" })); + Assertions.assertEquals(2, + s.minMutation("AACCGGTT", "AAACGGTA", new String[] { "AACCGGTA", "AACCGCTA", "AAACGGTA" })); + Assertions.assertEquals(3, + s.minMutation("AAAAACCC", "AACCCCCC", new String[] { "AAAACCCC", "AAACCCCC", "AACCCCCC" })); + Assertions.assertEquals(-1, + s.minMutation("AACCGGTT", "AACCGGTA", new String[] {})); + Assertions.assertEquals(-1, + s.minMutation("AAAAAAAA", "CCCCCCCC", + new String[] { "AAAAAAAA", "AAAAAAAC", "AAAAAACC", "AAAAACCC", "AAAACCCC", "AACACCCC", "ACCACCCC", + "ACCCCCCC", "CCCCCCCA" })); + } + + static class Solution { + + final char[] AGCT = new char[] { 'A', 'C', 'G', 'T' }; + + public int minMutation(String startGene, String endGene, String[] bank) { + if (startGene.equals(endGene)) { return 0; } + + int step = 0; + Set banks = new HashSet<>(Arrays.asList(bank)); + Set visited = new HashSet<>(); + LinkedList queue = new LinkedList<>(); + queue.offer(startGene); + + while (!queue.isEmpty()) { + int size = queue.size(); + for (int i = 0; i < size; i++) { + String curGene = queue.poll(); + if (curGene.equals(endGene)) { return step; } + for (String newGene : neighbours(curGene)) { + if (!visited.contains(newGene) && banks.contains(newGene)) { + queue.offer(newGene); + visited.add(newGene); + } + } + } + step++; + } + return -1; + } + + // 当前基因的每个位置都可以变异为 A/G/C/T,穷举所有可能的结构 + public List neighbours(String gene) { + List res = new LinkedList<>(); + char[] ch = gene.toCharArray(); + for (int i = 0; i < ch.length; i++) { + char c = ch[i]; + for (char option : AGCT) { + ch[i] = option; + res.add(new String(ch)); + } + ch[i] = c; + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/bfs/\346\260\264\345\243\266\351\227\256\351\242\230.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/bfs/\346\260\264\345\243\266\351\227\256\351\242\230.java" new file mode 100644 index 0000000..a70a284 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/bfs/\346\260\264\345\243\266\351\227\256\351\242\230.java" @@ -0,0 +1,83 @@ +package io.github.dunwu.algorithm.bfs; + +import org.junit.jupiter.api.Assertions; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; + +/** + * 365. 水壶问题 + * + * @author Zhang Peng + * @date 2025-12-15 + */ +public class 水壶问题 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertTrue(s.canMeasureWater(3, 5, 4)); + Assertions.assertFalse(s.canMeasureWater(2, 6, 5)); + Assertions.assertTrue(s.canMeasureWater(1, 2, 3)); + } + + static class Solution { + + public boolean canMeasureWater(int x, int y, int t) { + // BFS 算法的队列 + LinkedList q = new LinkedList<>(); + // 用来记录已经遍历过的状态,把元组转化成数字方便存储哈希集合 + // 转化方式是 (x, y) -> (x * (y + 1) + y),和二维数组坐标转一维坐标是一样的原理 + // 因为水桶 2 的取值是 [0, y],所以需要额外加一,请类比二维数组坐标转一维坐标 + // 且考虑到题目输入的数据规模较大,相乘可能导致 int 溢出,所以使用 long 类型 + HashSet visited = new HashSet<>(); + // 添加初始状态,两个桶都没有水 + q.offer(new int[] { 0, 0 }); + visited.add((long) 0 * (0 + 1) + 0); + + while (!q.isEmpty()) { + int[] curState = q.poll(); + if (curState[0] == t || curState[1] == t + || curState[0] + curState[1] == t) { + // 如果任意一个桶的水量等于目标水量,就返回 true + return true; + } + // 计算出所有可能的下一个状态 + List nextStates = new LinkedList<>(); + // 把 1 桶灌满 + nextStates.add(new int[] { x, curState[1] }); + // 把 2 桶灌满 + nextStates.add(new int[] { curState[0], y }); + // 把 1 桶倒空 + nextStates.add(new int[] { 0, curState[1] }); + // 把 2 桶倒空 + nextStates.add(new int[] { curState[0], 0 }); + // 把 1 桶的水灌进 2 桶,直到 1 桶空了或者 2 桶满了 + nextStates.add(new int[] { + curState[0] - Math.min(curState[0], y - curState[1]), + curState[1] + Math.min(curState[0], y - curState[1]) + }); + // 把 2 桶的水灌进 1 桶,直到 2 桶空了或者 1 桶满了 + nextStates.add(new int[] { + curState[0] + Math.min(curState[1], x - curState[0]), + curState[1] - Math.min(curState[1], x - curState[0]) + }); + + // 把所有可能的下一个状态都放进队列里 + for (int[] nextState : nextStates) { + // 把二维坐标转化为数字,方便去重 + long hash = (long) nextState[0] * (y + 1) + nextState[1]; + if (visited.contains(hash)) { + // 如果这个状态之前遍历过,就跳过,避免队列永远不空陷入死循环 + continue; + } + q.offer(nextState); + visited.add(hash); + } + } + return false; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/bfs/\350\205\220\347\203\202\347\232\204\346\251\230\345\255\220.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/bfs/\350\205\220\347\203\202\347\232\204\346\251\230\345\255\220.java" new file mode 100644 index 0000000..0b5751b --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/bfs/\350\205\220\347\203\202\347\232\204\346\251\230\345\255\220.java" @@ -0,0 +1,93 @@ +package io.github.dunwu.algorithm.bfs; + +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; + +/** + * 994. 腐烂的橘子 + * + * @author Zhang Peng + * @date 2025-11-07 + */ +public class 腐烂的橘子 { + + public static void main(String[] args) { + Solution s = new Solution(); + + int[][] input1 = { { 2, 1, 1 }, { 1, 1, 0 }, { 0, 1, 1 } }; + Assertions.assertEquals(4, s.orangesRotting(input1)); + + int[][] input2 = { { 2, 1, 1 }, { 0, 1, 1 }, { 1, 0, 1 } }; + Assertions.assertEquals(-1, s.orangesRotting(input2)); + + int[][] input3 = { { 0, 2 } }; + Assertions.assertEquals(0, s.orangesRotting(input3)); + + int[][] input4 = { { 1 } }; + Assertions.assertEquals(-1, s.orangesRotting(input4)); + + int[][] input5 = { { 1, 2 } }; + Assertions.assertEquals(1, s.orangesRotting(input5)); + } + + static class Solution { + + // 四个方向偏移量(上、下、左、右) + private static final int[][] directions = { { 0, 1 }, { 0, -1 }, { -1, 0 }, { 1, 0 } }; + + public int orangesRotting(int[][] grid) { + + int m = grid.length, n = grid[0].length; + + int freshCount = 0; + boolean[][] visited = new boolean[m][n]; + LinkedList queue = new LinkedList<>(); + + // 把所有腐烂的橘子加入队列,作为 BFS 的起点 + for (int x = 0; x < m; x++) { + for (int y = 0; y < n; y++) { + if (grid[x][y] == 1) { + freshCount++; + } else if (grid[x][y] == 2) { + queue.offer(new int[] { x, y }); + visited[x][y] = true; + } + } + } + if (freshCount == 0) { return 0; } + if (queue.isEmpty()) { return -1; } + + int step = 1; + while (!queue.isEmpty()) { + int size = queue.size(); + for (int i = 0; i < size; i++) { + int[] point = queue.poll(); + int x = point[0], y = point[1]; + for (int[] d : directions) { + int nextX = x + d[0], nextY = y + d[1]; + // 超出边界,跳过 + if (nextX < 0 || nextX >= m || nextY < 0 || nextY >= n) { continue; } + // 已访问,跳过(避免死循环) + if (visited[nextX][nextY]) { continue; } + // 遇到空格,跳过 + if (grid[nextX][nextY] == 0) { continue; } + // 遇到新鲜橘子,被传播腐烂 + if (grid[nextX][nextY] == 1) { + grid[nextX][nextY] = 2; + freshCount--; + // 新鲜橘子数为 0,返回结果 + if (freshCount == 0) { return step; } + } + visited[nextX][nextY] = true; + queue.offer(new int[] { nextX, nextY }); + } + } + step++; + } + return -1; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/bfs/\350\264\246\346\210\267\345\220\210\345\271\266.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/bfs/\350\264\246\346\210\267\345\220\210\345\271\266.java" new file mode 100644 index 0000000..3afee28 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/bfs/\350\264\246\346\210\267\345\220\210\345\271\266.java" @@ -0,0 +1,112 @@ +package io.github.dunwu.algorithm.bfs; + +import io.github.dunwu.algorithm.util.ArrayUtil; +import org.junit.jupiter.api.Assertions; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; + +/** + * 721. 账户合并 + * + * @author Zhang Peng + * @date 2025-11-07 + */ +public class 账户合并 { + + public static void main(String[] args) { + + Solution solution = new Solution(); + + String[][] input1 = { + { "John", "johnsmith@mail.com", "john00@mail.com" }, + { "John", "johnnybravo@mail.com" }, + { "John", "johnsmith@mail.com", "john_newyork@mail.com" }, + { "Mary", "mary@mail.com" } + }; + String[][] expect1 = { + { "John", "johnnybravo@mail.com" }, + { "John", "john00@mail.com", "john_newyork@mail.com", "johnsmith@mail.com" }, + { "Mary", "mary@mail.com" } + }; + List> output1 = solution.accountsMerge(ArrayUtil.toStringMatrixList(input1)); + Assertions.assertArrayEquals(expect1, ArrayUtil.toStringMatrixArray(output1)); + + String[][] input2 = { + { "Gabe", "Gabe0@m.co", "Gabe3@m.co", "Gabe1@m.co" }, + { "Kevin", "Kevin3@m.co", "Kevin5@m.co", "Kevin0@m.co" }, + { "Ethan", "Ethan5@m.co", "Ethan4@m.co", "Ethan0@m.co" }, + { "Hanzo", "Hanzo3@m.co", "Hanzo1@m.co", "Hanzo0@m.co" }, + { "Fern", "Fern5@m.co", "Fern1@m.co", "Fern0@m.co" } + }; + String[][] expect2 = { + { "Hanzo", "Hanzo0@m.co", "Hanzo1@m.co", "Hanzo3@m.co" }, + { "Fern", "Fern0@m.co", "Fern1@m.co", "Fern5@m.co" }, + { "Gabe", "Gabe0@m.co", "Gabe1@m.co", "Gabe3@m.co" }, + { "Kevin", "Kevin0@m.co", "Kevin3@m.co", "Kevin5@m.co" }, + { "Ethan", "Ethan0@m.co", "Ethan4@m.co", "Ethan5@m.co" } + }; + List> output2 = solution.accountsMerge(ArrayUtil.toStringMatrixList(input2)); + Assertions.assertArrayEquals(expect2, ArrayUtil.toStringMatrixArray(output2)); + } + + static class Solution { + + public List> accountsMerge(List> accounts) { + // key: email, value: 出现该 email 的 account 的索引列表 + HashMap> emailToIdx = new HashMap<>(); + for (int i = 0; i < accounts.size(); i++) { + List account = accounts.get(i); + for (int j = 1; j < account.size(); j++) { + String email = account.get(j); + List indexes = emailToIdx.getOrDefault(email, new ArrayList<>()); + indexes.add(i); + emailToIdx.put(email, indexes); + } + } + + // 计算合并后的账户 + List> res = new ArrayList<>(); + HashSet visitedEmails = new HashSet<>(); + + for (String email : emailToIdx.keySet()) { + if (visitedEmails.contains(email)) { + continue; + } + // 合并账户,用 BFS 算法穷举所有和 email 相关联的邮箱 + LinkedList mergedEmail = new LinkedList<>(); + LinkedList queue = new LinkedList<>(); + queue.offer(email); + visitedEmails.add(email); + // BFS 算法框架 + while (!queue.isEmpty()) { + String curEmail = queue.poll(); + mergedEmail.addLast(curEmail); + List indexes = emailToIdx.get(curEmail); + for (int index : indexes) { + List account = accounts.get(index); + for (int j = 1; j < account.size(); j++) { + String nextEmail = account.get(j); + if (!visitedEmails.contains(nextEmail)) { + queue.offer(nextEmail); + visitedEmails.add(nextEmail); + } + } + } + } + String userName = accounts.get(emailToIdx.get(email).get(0)).get(0); + // mergedEmail 是 userName 的所有邮箱 + Collections.sort(mergedEmail); + mergedEmail.addFirst(userName); + res.add(mergedEmail); + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/bfs/\350\277\267\345\256\253\344\270\255\347\246\273\345\205\245\345\217\243\346\234\200\350\277\221\347\232\204\345\207\272\345\217\243.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/bfs/\350\277\267\345\256\253\344\270\255\347\246\273\345\205\245\345\217\243\346\234\200\350\277\221\347\232\204\345\207\272\345\217\243.java" new file mode 100644 index 0000000..4782ed7 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/bfs/\350\277\267\345\256\253\344\270\255\347\246\273\345\205\245\345\217\243\346\234\200\350\277\221\347\232\204\345\207\272\345\217\243.java" @@ -0,0 +1,79 @@ +package io.github.dunwu.algorithm.bfs; + +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; + +/** + * 1926. 迷宫中离入口最近的出口 + * + * @author Zhang Peng + * @date 2025-11-07 + */ +public class 迷宫中离入口最近的出口 { + + public static void main(String[] args) { + + Solution s = new Solution(); + + char[][] maze1 = { { '+', '+', '.', '+' }, { '.', '.', '.', '+' }, { '+', '+', '+', '.' } }; + int[] entrance1 = { 1, 2 }; + Assertions.assertEquals(1, s.nearestExit(maze1, entrance1)); + + char[][] maze2 = { { '+', '+', '+' }, { '.', '.', '.' }, { '+', '+', '+' } }; + int[] entrance2 = { 1, 0 }; + Assertions.assertEquals(2, s.nearestExit(maze2, entrance2)); + + char[][] maze3 = { { '.', '+' } }; + int[] entrance3 = { 0, 0 }; + Assertions.assertEquals(-1, s.nearestExit(maze3, entrance3)); + + char[][] maze4 = { + { '+', '.', '+', '+', '+', '+', '+' }, + { '+', '.', '+', '.', '.', '.', '+' }, + { '+', '.', '+', '.', '+', '.', '+' }, + { '+', '.', '.', '.', '+', '.', '+' }, + { '+', '+', '+', '+', '+', '+', '.' } + }; + int[] entrance4 = { 0, 1 }; + Assertions.assertEquals(-1, s.nearestExit(maze4, entrance4)); + } + + static class Solution { + + public int nearestExit(char[][] maze, int[] entrance) { + + int m = maze.length, n = maze[0].length; + final int[][] directions = { { 0, 1 }, { 0, -1 }, { 1, 0 }, { -1, 0 } }; + + // BFS 算法的队列和 visited 数组 + LinkedList queue = new LinkedList<>(); + boolean[][] visited = new boolean[m][n]; + queue.offer(entrance); + visited[entrance[0]][entrance[1]] = true; + // 启动 BFS 算法从 entrance 开始像四周扩散 + int step = 0; + while (!queue.isEmpty()) { + int size = queue.size(); + step++; + // 扩散当前队列中的所有节点 + for (int i = 0; i < size; i++) { + int[] point = queue.poll(); + // 每个节点都会尝试向上下左右四个方向扩展一步 + for (int[] d : directions) { + int x = point[0] + d[0], y = point[1] + d[1]; + if (x < 0 || x >= m || y < 0 || y >= n) { continue; } + if (visited[x][y] || maze[x][y] == '+') { continue; } + // 走到边界(出口) + if (x == 0 || x == m - 1 || y == 0 || y == n - 1) { return step; } + visited[x][y] = true; + queue.offer(new int[] { x, y }); + } + } + } + return -1; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/bfs/\351\222\245\345\214\231\345\222\214\346\210\277\351\227\264.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/bfs/\351\222\245\345\214\231\345\222\214\346\210\277\351\227\264.java" new file mode 100644 index 0000000..3464b06 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/bfs/\351\222\245\345\214\231\345\222\214\346\210\277\351\227\264.java" @@ -0,0 +1,58 @@ +package io.github.dunwu.algorithm.bfs; + +import org.junit.jupiter.api.Assertions; + +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +/** + * 841. 钥匙和房间 + * + * @author Zhang Peng + * @date 2025-11-07 + */ +public class 钥匙和房间 { + + public static void main(String[] args) { + Solution s = new Solution(); + + List> input1 = new LinkedList<>(); + input1.add(Collections.singletonList(1)); + input1.add(Collections.singletonList(2)); + input1.add(Collections.singletonList(3)); + input1.add(new LinkedList<>()); + Assertions.assertTrue(s.canVisitAllRooms(input1)); + } + + static class Solution { + + public boolean canVisitAllRooms(List> rooms) { + // base case + if (rooms == null || rooms.size() == 0) { return true; } + + // 记录访问过的房间 + Set visited = new HashSet<>(); + LinkedList queue = new LinkedList<>(); + // 在队列中加入起点,启动 BFS + queue.offer(0); + while (!queue.isEmpty()) { + int size = queue.size(); + for (int i = 0; i < size; i++) { + Integer cur = queue.poll(); + if (!visited.contains(cur)) { + visited.add(cur); + for (int room : rooms.get(cur)) { + queue.offer(room); + } + } + } + } + return visited.size() == rooms.size(); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/design/LRU\347\274\223\345\255\230.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/design/LRU\347\274\223\345\255\230.java" new file mode 100644 index 0000000..7d4ebd1 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/design/LRU\347\274\223\345\255\230.java" @@ -0,0 +1,63 @@ +package io.github.dunwu.algorithm.design; + +import org.junit.jupiter.api.Assertions; + +import java.util.Iterator; +import java.util.LinkedHashMap; + +/** + * 146. LRU 缓存 + * + * @author Zhang Peng + * @date 2025-10-31 + */ +public class LRU缓存 { + + public static void main(String[] args) { + + LRUCache lRUCache = new LRUCache(2); + lRUCache.put(1, 1); // 缓存是 {1=1} + lRUCache.put(2, 2); // 缓存是 {1=1, 2=2} + Assertions.assertEquals(1, lRUCache.get(1)); + lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3} + Assertions.assertEquals(-1, lRUCache.get(2)); + lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3} + Assertions.assertEquals(-1, lRUCache.get(1)); + Assertions.assertEquals(3, lRUCache.get(3)); + Assertions.assertEquals(4, lRUCache.get(4)); + } + + static class LRUCache { + + private int capacity = 0; + private LinkedHashMap cache = null; + + public LRUCache(int capacity) { + this.capacity = capacity; + this.cache = new LinkedHashMap<>(capacity); + } + + public int get(int key) { + Integer val = cache.get(key); + if (val != null) { + cache.remove(key); + cache.put(key, val); + } + return val == null ? -1 : val; + } + + public void put(int key, int value) { + if (cache.containsKey(key)) { + cache.remove(key); + } else { + if (capacity <= cache.size()) { + Iterator iterator = cache.keySet().iterator(); + cache.remove(iterator.next()); + } + } + cache.put(key, value); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/design/\345\237\272\346\234\254\350\256\241\347\256\227\345\231\250.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/design/\345\237\272\346\234\254\350\256\241\347\256\227\345\231\250.java" new file mode 100644 index 0000000..67de9b1 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/design/\345\237\272\346\234\254\350\256\241\347\256\227\345\231\250.java" @@ -0,0 +1,89 @@ +package io.github.dunwu.algorithm.design; + +import org.junit.jupiter.api.Assertions; + +import java.util.HashMap; +import java.util.Map; +import java.util.Stack; + +/** + * 224. 基本计算器 + * + * @author Zhang Peng + * @since 2020-06-09 + */ +public class 基本计算器 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(23, s.calculate("(1+(4+5+2)-3)+(6+8)")); + Assertions.assertEquals(12, s.calculate("1+(4+5+2)")); + Assertions.assertEquals(2147483647, s.calculate("2147483647")); + Assertions.assertEquals(2, s.calculate("1 + 1")); + Assertions.assertEquals(3, s.calculate("2 - 1 + 2")); + } + + static class Solution { + + public int calculate(String s) { + Stack stack = new Stack<>(); + Map map = new HashMap<>(); + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) == '(') { + stack.push(i); + } else if (s.charAt(i) == ')') { + map.put(stack.pop(), i); + } + } + return calculate(s, 0, s.length() - 1, map); + } + + public int calculate(String s, int start, int end, Map map) { + int num = 0; + char sign = '+'; + Stack stack = new Stack<>(); + for (int i = start; i <= end; i++) { + char c = s.charAt(i); + if (Character.isDigit(c)) { + num = num * 10 + (c - '0'); + } + if (c == '(') { + num = calculate(s, i + 1, map.get(i) - 1, map); + i = map.get(i); + } + + if (c == '+' || c == '-' || c == '*' || c == '/' || i == end) { + int pre = 0; + switch (sign) { + case '+': + stack.push(num); + break; + case '-': + stack.push(-num); + break; + case '*': + pre = stack.pop(); + stack.push(pre * num); + break; + case '/': + pre = stack.pop(); + stack.push(pre / num); + break; + default: + break; + } + sign = c; + num = 0; + } + } + + int result = 0; + while (!stack.isEmpty()) { + result += stack.pop(); + } + return result; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/design/\345\237\272\346\234\254\350\256\241\347\256\227\345\231\2502.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/design/\345\237\272\346\234\254\350\256\241\347\256\227\345\231\2502.java" new file mode 100644 index 0000000..6fe15e1 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/design/\345\237\272\346\234\254\350\256\241\347\256\227\345\231\2502.java" @@ -0,0 +1,72 @@ +package io.github.dunwu.algorithm.design; + +import org.junit.jupiter.api.Assertions; + +import java.util.Stack; + +/** + * 227. 基本计算器 II + * + * @author Zhang Peng + * @since 2020-06-09 + */ +public class 基本计算器2 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(2147483647, s.calculate("2147483647")); + Assertions.assertEquals(2, s.calculate("1 + 1")); + Assertions.assertEquals(3, s.calculate("2 - 1 + 2")); + Assertions.assertEquals(7, s.calculate("3+2*2")); + } + + static class Solution { + + public int calculate(String s) { + return calculate(s, 0, s.length() - 1); + } + + public int calculate(String s, int start, int end) { + int num = 0; + char sign = '+'; + Stack stack = new Stack<>(); + for (int i = start; i <= end; i++) { + char c = s.charAt(i); + if (Character.isDigit(c)) { + num = num * 10 + (c - '0'); + } + if (c == '+' || c == '-' || c == '*' || c == '/' || i == s.length() - 1) { + int pre = 0; + switch (sign) { + case '+': + stack.push(num); + break; + case '-': + stack.push(-num); + break; + case '*': + pre = stack.pop(); + stack.push(pre * num); + break; + case '/': + pre = stack.pop(); + stack.push(pre / num); + break; + default: + break; + } + sign = c; + num = 0; + } + } + + int result = 0; + while (!stack.isEmpty()) { + result += stack.pop(); + } + return result; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/design/\346\210\221\347\232\204\346\227\245\347\250\213\345\256\211\346\216\222\350\241\250.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/design/\346\210\221\347\232\204\346\227\245\347\250\213\345\256\211\346\216\222\350\241\250.java" new file mode 100644 index 0000000..58424c4 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/design/\346\210\221\347\232\204\346\227\245\347\250\213\345\256\211\346\216\222\350\241\250.java" @@ -0,0 +1,45 @@ +package io.github.dunwu.algorithm.design; + +import org.junit.jupiter.api.Assertions; + +import java.util.TreeMap; + +/** + * 729. 我的日程安排表 I + * + * @author Zhang Peng + * @date 2025-11-03 + */ +public class 我的日程安排表 { + + public static void main(String[] args) { + MyCalendar s = new MyCalendar(); + Assertions.assertTrue(s.book(10, 20)); + Assertions.assertFalse(s.book(15, 25)); + Assertions.assertTrue(s.book(20, 30)); + } + + static class MyCalendar { + + private TreeMap calendar = null; + + public MyCalendar() { + calendar = new TreeMap<>(); + } + + public boolean book(int start, int end) { + Integer earlier = calendar.floorKey(start); + Integer later = calendar.ceilingKey(start); + if (later != null && later < end) { + return false; + } + if (earlier != null && start < calendar.get(earlier)) { + return false; + } + calendar.put(start, end); + return true; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/design/\346\214\211\351\200\222\345\242\236\351\241\272\345\272\217\346\230\276\347\244\272\345\215\241\347\211\214.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/design/\346\214\211\351\200\222\345\242\236\351\241\272\345\272\217\346\230\276\347\244\272\345\215\241\347\211\214.java" new file mode 100644 index 0000000..e1f68e8 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/design/\346\214\211\351\200\222\345\242\236\351\241\272\345\272\217\346\230\276\347\244\272\345\215\241\347\211\214.java" @@ -0,0 +1,43 @@ +package io.github.dunwu.algorithm.design; + +import org.junit.jupiter.api.Assertions; + +import java.util.Arrays; +import java.util.LinkedList; + +/** + * 950. 按递增顺序显示卡牌 + * + * @author Zhang Peng + * @date 2025-11-03 + */ +public class 按递增顺序显示卡牌 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertArrayEquals(new int[] { 2, 13, 3, 11, 5, 17, 7 } + , s.deckRevealedIncreasing(new int[] { 17, 13, 11, 2, 3, 5, 7 })); + } + + static class Solution { + + public int[] deckRevealedIncreasing(int[] deck) { + int n = deck.length; + LinkedList res = new LinkedList<>(); + Arrays.sort(deck); + for (int i = n - 1; i >= 0; i--) { + if (!res.isEmpty()) { + res.addFirst(res.removeLast()); + } + res.addFirst(deck[i]); + } + int[] arr = new int[n]; + for (int i = 0; i < res.size(); i++) { + arr[i] = res.get(i); + } + return arr; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/design/\346\227\240\346\263\225\345\220\203\345\215\210\351\244\220\347\232\204\345\255\246\347\224\237\346\225\260\351\207\217.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/design/\346\227\240\346\263\225\345\220\203\345\215\210\351\244\220\347\232\204\345\255\246\347\224\237\346\225\260\351\207\217.java" new file mode 100644 index 0000000..2f0b46d --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/design/\346\227\240\346\263\225\345\220\203\345\215\210\351\244\220\347\232\204\345\255\246\347\224\237\346\225\260\351\207\217.java" @@ -0,0 +1,52 @@ +package io.github.dunwu.algorithm.design; + +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; + +/** + * 1700. 无法吃午餐的学生数量 + * + * @author Zhang Peng + * @date 2025-11-03 + */ +public class 无法吃午餐的学生数量 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(0, s.countStudents(new int[] { 1, 1, 0, 0 }, new int[] { 0, 1, 0, 1 })); + Assertions.assertEquals(3, s.countStudents(new int[] { 1, 1, 1, 0, 0, 1 }, new int[] { 1, 0, 0, 0, 1, 1 })); + } + + static class Solution { + + public int countStudents(int[] students, int[] sandwiches) { + int total = students.length; + LinkedList studentQueue = new LinkedList<>(); + for (int s : students) { + studentQueue.addLast(s); + } + int matchNum = 0; + while (matchNum < sandwiches.length) { + int notMatchNum = 0; + int size = studentQueue.size(); + while (notMatchNum < size) { + Integer s = studentQueue.removeFirst(); + if (s == sandwiches[matchNum]) { + matchNum++; + break; + } else { + studentQueue.addLast(s); + notMatchNum++; + } + } + if (notMatchNum == size) { + break; + } + } + return total - matchNum; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/design/\350\256\241\347\256\227\345\231\250.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/design/\350\256\241\347\256\227\345\231\250.java" new file mode 100644 index 0000000..5e32109 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/design/\350\256\241\347\256\227\345\231\250.java" @@ -0,0 +1,31 @@ +package io.github.dunwu.algorithm.design; + +import java.util.Arrays; + +/** + * 计数器模板 + * + * @author Zhang Peng + * @date 2025-10-31 + */ +public class 计算器 { + + public static void main(String[] args) { + Solution s = new Solution(); + System.out.println("args = " + Arrays.toString(args)); + } + + static class Solution { + + public int toNum(String s) { + if (s == null || s.length() == 0) { return 0; } + int num = 0; + for (char c : s.toCharArray()) { + num = num * 10 + (c - '0'); + } + return num; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/N\347\232\207\345\220\216.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/N\347\232\207\345\220\216.java" deleted file mode 100644 index 60f82e0..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/N\347\232\207\345\220\216.java" +++ /dev/null @@ -1,72 +0,0 @@ -package io.github.dunwu.algorithm.dfs; - -import java.util.ArrayList; -import java.util.List; - -/** - * @author Zhang Peng - * @since 2020-07-04 - * @see 51. N皇后 - */ -public class N皇后 { - - int[] cols; - int[] first; - int[] second; - int[] queens; - List> output = new ArrayList<>(); - - public static void main(String[] args) { - N皇后 demo = new N皇后(); - List> result = demo.solveNQueens(5); - result.forEach(System.out::println); - } - - public List> solveNQueens(int n) { - queens = new int[n]; - cols = new int[n]; - first = new int[2 * n]; - second = new int[2 * n]; - backtrack(n, 0); - return output; - } - - public void backtrack(int n, int row) { - if (row >= n) { return; } - for (int col = 0; col < n; col++) { - if (cols[col] == 1 || first[row + col] == 1 || second[row - col + n - 1] == 1) { continue;} - - queens[row] = col; - cols[col] = 1; - first[row + col] = 1; - second[row - col + n - 1] = 1; - - backtrack(n, row + 1); - if (row == n - 1) { - output.add(addSolution(n)); - } - - queens[row] = 0; - cols[col] = 0; - first[row + col] = 0; - second[row - col + n - 1] = 0; - } - } - - public List addSolution(int n) { - List res = new ArrayList<>(); - for (int i = 0; i < n; i++) { - StringBuilder sb = new StringBuilder(); - for (int j = 0; j < n; j++) { - if (i == queens[j]) { - sb.append("Q"); - } else { - sb.append("."); - } - } - res.add(sb.toString()); - } - return res; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/N\347\232\207\345\220\216II.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/N\347\232\207\345\220\216II.java" deleted file mode 100644 index 181842a..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/N\347\232\207\345\220\216II.java" +++ /dev/null @@ -1,79 +0,0 @@ -package io.github.dunwu.algorithm.dfs; - -import org.junit.jupiter.api.Assertions; - -import java.util.ArrayList; -import java.util.List; - -/** - * @author Zhang Peng - * @see 52. N皇后II - * @since 2020-07-04 - */ -public class N皇后II { - - int[] cols; - int[] first; - int[] second; - int[] queens; - List> output = new ArrayList<>(); - - public static void main(String[] args) { - N皇后II demo = new N皇后II(); - int result = demo.totalNQueens(4); - Assertions.assertEquals(2, result); - } - - public int totalNQueens(int n) { - List> lists = solveNQueens(n); - return lists.size(); - } - - public List> solveNQueens(int n) { - queens = new int[n]; - cols = new int[n]; - first = new int[2 * n]; - second = new int[2 * n]; - backtrack(n, 0); - return output; - } - - public void backtrack(int n, int row) { - if (row >= n) { return; } - for (int col = 0; col < n; col++) { - if (cols[col] == 1 || first[row + col] == 1 || second[row - col + n - 1] == 1) { continue;} - - queens[row] = col; - cols[col] = 1; - first[row + col] = 1; - second[row - col + n - 1] = 1; - - backtrack(n, row + 1); - if (row == n - 1) { - output.add(addSolution(n)); - } - - queens[row] = 0; - cols[col] = 0; - first[row + col] = 0; - second[row - col + n - 1] = 0; - } - } - - public List addSolution(int n) { - List res = new ArrayList<>(); - for (int i = 0; i < n; i++) { - StringBuilder sb = new StringBuilder(); - for (int j = 0; j < n; j++) { - if (i == queens[j]) { - sb.append("Q"); - } else { - sb.append("."); - } - } - res.add(sb.toString()); - } - return res; - } - -} diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/island/package-info.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/island/package-info.java new file mode 100644 index 0000000..6b44e2d --- /dev/null +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/island/package-info.java @@ -0,0 +1,7 @@ +/** + * DFS 解岛屿数类型问题 + * + * @author Zhang Peng + * @date 2025-12-15 + */ +package io.github.dunwu.algorithm.dfs.island; \ No newline at end of file diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/island/\345\262\233\345\261\277\346\225\260\351\207\217.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/island/\345\262\233\345\261\277\346\225\260\351\207\217.java" new file mode 100644 index 0000000..c6bafac --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/island/\345\262\233\345\261\277\346\225\260\351\207\217.java" @@ -0,0 +1,69 @@ +package io.github.dunwu.algorithm.dfs.island; + +import org.junit.jupiter.api.Assertions; + +/** + * 200. 岛屿数量 + * + * @author Zhang Peng + * @date 2025-11-04 + */ +public class 岛屿数量 { + + public static void main(String[] args) { + Solution s = new Solution(); + + char[][] input = { + { '1', '1', '1', '1', '0' }, + { '1', '1', '0', '1', '0' }, + { '1', '1', '0', '0', '0' }, + { '0', '0', '0', '0', '0' } + }; + Assertions.assertEquals(1, s.numIslands(input)); + + char[][] input2 = { + { '1', '1', '0', '0', '0' }, + { '1', '1', '0', '0', '0' }, + { '0', '0', '1', '0', '0' }, + { '0', '0', '0', '1', '1' } + }; + Assertions.assertEquals(3, s.numIslands(input2)); + } + + static class Solution { + + private final int[][] directions = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } }; + + public int numIslands(char[][] grid) { + if (grid == null || grid.length == 0) { return 0; } + int res = 0; + int m = grid.length, n = grid[0].length; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == '1') { + // 每发现一个岛屿,岛屿数量加一 + res++; + // 然后使用 DFS 将岛屿淹了 + dfs(grid, i, j); + } + } + } + return res; + } + + // 淹没与 (x, y) 相邻的陆地,并返回淹没的陆地面积 + public void dfs(char[][] grid, int x, int y) { + int m = grid.length, n = grid[0].length; + if (x < 0 || x >= m || y < 0 || y >= n) { return; } + if (grid[x][y] == '0') { return; } + + grid[x][y] = '0'; + for (int[] d : directions) { + int i = x + d[0], j = y + d[1]; + dfs(grid, i, j); + } + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/island/\345\262\233\345\261\277\347\232\204\346\234\200\345\244\247\351\235\242\347\247\257.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/island/\345\262\233\345\261\277\347\232\204\346\234\200\345\244\247\351\235\242\347\247\257.java" new file mode 100644 index 0000000..df3719e --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/island/\345\262\233\345\261\277\347\232\204\346\234\200\345\244\247\351\235\242\347\247\257.java" @@ -0,0 +1,71 @@ +package io.github.dunwu.algorithm.dfs.island; + +import org.junit.jupiter.api.Assertions; + +/** + * 695. 岛屿的最大面积 + * + * @author Zhang Peng + * @date 2025-11-05 + */ +public class 岛屿的最大面积 { + + public static void main(String[] args) { + Solution s = new Solution(); + + int[][] input = { + { 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0 }, + { 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0 }, + { 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0 } + }; + Assertions.assertEquals(6, s.maxAreaOfIsland(input)); + + int[][] input2 = { { 0, 0, 0, 0, 0, 0, 0, 0 } }; + Assertions.assertEquals(0, s.maxAreaOfIsland(input2)); + } + + static class Solution { + + private final int[][] directions = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } }; + + public int maxAreaOfIsland(int[][] grid) { + + // base case + if (grid == null || grid.length == 0) return 0; + + int res = 0; + int m = grid.length, n = grid[0].length; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == 1) { + int size = dfs(grid, i, j); + res = Math.max(res, size); + } + } + } + return res; + } + + // 淹没与 (x, y) 相邻的陆地,并返回淹没的陆地面积 + public int dfs(int[][] grid, int x, int y) { + int m = grid.length, n = grid[0].length; + if (x < 0 || x >= m || y < 0 || y >= n) { return 0; } + if (grid[x][y] == 0) { return 0; } + + int cnt = 1; + grid[x][y] = 0; + for (int[] d : directions) { + int i = x + d[0], j = y + d[1]; + cnt += dfs(grid, i, j); + } + return cnt; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/island/\347\273\237\350\256\241\345\255\220\345\262\233\345\261\277.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/island/\347\273\237\350\256\241\345\255\220\345\262\233\345\261\277.java" new file mode 100644 index 0000000..535ae08 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/island/\347\273\237\350\256\241\345\255\220\345\262\233\345\261\277.java" @@ -0,0 +1,93 @@ +package io.github.dunwu.algorithm.dfs.island; + +import org.junit.jupiter.api.Assertions; + +/** + * 1905. 统计子岛屿 + * + * @author Zhang Peng + * @date 2025-11-05 + */ +public class 统计子岛屿 { + + public static void main(String[] args) { + Solution s = new Solution(); + + int[][] gridA1 = { + { 1, 1, 1, 0, 0 }, + { 0, 1, 1, 1, 1 }, + { 0, 0, 0, 0, 0 }, + { 1, 0, 0, 0, 0 }, + { 1, 1, 0, 1, 1 } + }; + int[][] gridB1 = { + { 1, 1, 1, 0, 0 }, + { 0, 0, 1, 1, 1 }, + { 0, 1, 0, 0, 0 }, + { 1, 0, 1, 1, 0 }, + { 0, 1, 0, 1, 0 } + }; + Assertions.assertEquals(3, s.countSubIslands(gridA1, gridB1)); + + int[][] gridA2 = { + { 1, 0, 1, 0, 1 }, + { 1, 1, 1, 1, 1 }, + { 0, 0, 0, 0, 0 }, + { 1, 1, 1, 1, 1 }, + { 1, 0, 1, 0, 1 } + }; + int[][] gridB2 = { + { 0, 0, 0, 0, 0 }, + { 1, 1, 1, 1, 1 }, + { 0, 1, 0, 1, 0 }, + { 0, 1, 0, 1, 0 }, + { 1, 0, 0, 0, 1 } + }; + Assertions.assertEquals(2, s.countSubIslands(gridA2, gridB2)); + } + + static class Solution { + + private final int[][] directions = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } }; + + public int countSubIslands(int[][] grid1, int[][] grid2) { + int m = grid1.length, n = grid1[0].length; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid1[i][j] == 0 && grid2[i][j] == 1) { + // 这个岛屿肯定不是子岛,淹掉 + dfs(grid2, i, j); + } + } + } + + int res = 0; + // 现在 grid2 中剩下的岛屿都是子岛,计算岛屿数量 + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid2[i][j] == 1) { + res++; + dfs(grid2, i, j); + } + } + } + return res; + } + + // 淹没与 (x, y) 相邻的陆地,并返回淹没的陆地面积 + public void dfs(int[][] grid, int x, int y) { + // base case + int m = grid.length, n = grid[0].length; + if (x < 0 || x >= m || y < 0 || y >= n) { return; } + if (grid[x][y] == 0) { return; } + + grid[x][y] = 0; + for (int[] d : directions) { + int i = x + d[0], j = y + d[1]; + dfs(grid, i, j); + } + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/island/\347\273\237\350\256\241\345\260\201\351\227\255\345\262\233\345\261\277\347\232\204\346\225\260\347\233\256.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/island/\347\273\237\350\256\241\345\260\201\351\227\255\345\262\233\345\261\277\347\232\204\346\225\260\347\233\256.java" new file mode 100644 index 0000000..20975ca --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/island/\347\273\237\350\256\241\345\260\201\351\227\255\345\262\233\345\261\277\347\232\204\346\225\260\347\233\256.java" @@ -0,0 +1,86 @@ +package io.github.dunwu.algorithm.dfs.island; + +import org.junit.jupiter.api.Assertions; + +/** + * 1254. 统计封闭岛屿的数目 + * + * @author Zhang Peng + * @date 2025-11-04 + */ +public class 统计封闭岛屿的数目 { + + public static void main(String[] args) { + Solution s = new Solution(); + + int[][] input = { + { 1, 1, 1, 1, 1, 1, 1, 0 }, + { 1, 0, 0, 0, 0, 1, 1, 0 }, + { 1, 0, 1, 0, 1, 1, 1, 0 }, + { 1, 0, 0, 0, 0, 1, 0, 1 }, + { 1, 1, 1, 1, 1, 1, 1, 0 } + }; + Assertions.assertEquals(2, s.closedIsland(input)); + + int[][] input2 = { + { 1, 1, 1, 1, 1, 1, 1 }, + { 1, 0, 0, 0, 0, 0, 1 }, + { 1, 0, 1, 1, 1, 0, 1 }, + { 1, 0, 1, 0, 1, 0, 1 }, + { 1, 0, 1, 1, 1, 0, 1 }, + { 1, 0, 0, 0, 0, 0, 1 }, + { 1, 1, 1, 1, 1, 1, 1 } + }; + Assertions.assertEquals(2, s.closedIsland(input2)); + } + + static class Solution { + + private final int[][] directions = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } }; + + public int closedIsland(int[][] grid) { + + // base case + if (grid == null || grid.length == 0) { return 0; } + + // 将靠边的岛屿淹没 + int m = grid.length, n = grid[0].length; + for (int j = 0; j < n; j++) { + dfs(grid, 0, j); + dfs(grid, m - 1, j); + } + for (int i = 0; i < m; i++) { + dfs(grid, i, 0); + dfs(grid, i, n - 1); + } + + int res = 0; + // 遍历 grid,剩下的岛屿都是封闭岛屿 + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == 0) { + res++; + dfs(grid, i, j); + } + } + } + return res; + } + + // 淹没与 (x, y) 相邻的陆地,并返回淹没的陆地面积 + public void dfs(int[][] grid, int x, int y) { + // base case + int m = grid.length, n = grid[0].length; + if (x < 0 || x >= m || y < 0 || y >= n) { return; } + if (grid[x][y] == 1) { return; } + + grid[x][y] = 1; + for (int[] d : directions) { + int i = x + d[0], j = y + d[1]; + dfs(grid, i, j); + } + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/island/\351\243\236\345\234\260\347\232\204\346\225\260\351\207\217.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/island/\351\243\236\345\234\260\347\232\204\346\225\260\351\207\217.java" new file mode 100644 index 0000000..7bdaa0c --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/island/\351\243\236\345\234\260\347\232\204\346\225\260\351\207\217.java" @@ -0,0 +1,80 @@ +package io.github.dunwu.algorithm.dfs.island; + +import org.junit.jupiter.api.Assertions; + +/** + * 1020. 飞地的数量/a> + * + * @author Zhang Peng + * @date 2025-11-04 + */ +public class 飞地的数量 { + + public static void main(String[] args) { + Solution s = new Solution(); + + int[][] input = { + { 0, 0, 0, 0 }, + { 1, 0, 1, 0 }, + { 0, 1, 1, 0 }, + { 0, 0, 0, 0 } + }; + Assertions.assertEquals(3, s.numEnclaves(input)); + + int[][] input2 = { + { 0, 1, 1, 0 }, + { 0, 0, 1, 0 }, + { 0, 0, 1, 0 }, + { 0, 0, 0, 0 } + }; + Assertions.assertEquals(0, s.numEnclaves(input2)); + } + + static class Solution { + + private final int[][] directions = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } }; + + public int numEnclaves(int[][] grid) { + + // base case + if (grid == null || grid.length == 0) { return 0; } + + int m = grid.length, n = grid[0].length; + for (int j = 0; j < n; j++) { + dfs(grid, 0, j); + dfs(grid, m - 1, j); + } + for (int i = 0; i < m; i++) { + dfs(grid, i, 0); + dfs(grid, i, n - 1); + } + + int res = 0; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == 1) { + res += dfs(grid, i, j); + } + } + } + return res; + } + + // 淹没与 (x, y) 相邻的陆地,并返回淹没的陆地面积 + public int dfs(int[][] grid, int x, int y) { + int m = grid.length, n = grid[0].length; + if (x < 0 || x >= m || y < 0 || y >= n) { return 0; } + if (grid[x][y] == 0) { return 0; } + + int cnt = 1; + grid[x][y] = 0; + for (int[] d : directions) { + int i = x + d[0], j = y + d[1]; + cnt += dfs(grid, i, j); + } + return cnt; + } + + } + +} diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/permutation_combination/package-info.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/permutation_combination/package-info.java new file mode 100644 index 0000000..3192edb --- /dev/null +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/permutation_combination/package-info.java @@ -0,0 +1,7 @@ +/** + * 通过回溯算法解决排列、组合、子集类型的问题 + * + * @author Zhang Peng + * @date 2025-12-13 + */ +package io.github.dunwu.algorithm.dfs.permutation_combination; \ No newline at end of file diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/permutation_combination/\345\205\250\346\216\222\345\210\227.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/permutation_combination/\345\205\250\346\216\222\345\210\227.java" new file mode 100644 index 0000000..6062839 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/permutation_combination/\345\205\250\346\216\222\345\210\227.java" @@ -0,0 +1,77 @@ +package io.github.dunwu.algorithm.dfs.permutation_combination; + +import cn.hutool.core.collection.CollectionUtil; +import io.github.dunwu.algorithm.util.ArrayUtil; +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; +import java.util.List; + +/** + * 46. 全排列 + * + * @author Zhang Peng + * @date 2025-11-03 + */ +public class 全排列 { + + public static void main(String[] args) { + Solution s = new Solution(); + int[][] expect = { { 1, 2, 3 }, { 1, 3, 2 }, { 2, 1, 3 }, { 2, 3, 1 }, { 3, 1, 2 }, { 3, 2, 1 } }; + int[][] expect2 = { { 0, 1 }, { 1, 0 } }; + Assertions.assertArrayEquals(expect, ArrayUtil.toIntMatrixArray(s.permute(new int[] { 1, 2, 3 }))); + Assertions.assertArrayEquals(expect2, ArrayUtil.toIntMatrixArray(s.permute(new int[] { 0, 1 }))); + Assertions.assertArrayEquals(new int[][] { { 1 } }, ArrayUtil.toIntMatrixArray(s.permute(new int[] { 1 }))); + } + + static class Solution { + + // 「路径」中的元素会被标记为 true,避免重复使用 + boolean[] visited; + // 记录「路径」 + LinkedList path; + List> res; + + // 主函数,输入一组不重复的数字,返回它们的全排列 + List> permute(int[] nums) { + visited = new boolean[nums.length]; + path = new LinkedList<>(); + res = new LinkedList<>(); + backtrack(nums); + return res; + } + + // 路径:记录在 path 中 + // 选择列表:nums 中不存在于 path 的那些元素(visited[i] 为 false) + // 结束条件:nums 中的元素全都在 path 中出现 + void backtrack(int[] nums) { + // 【结束】【前序】到达决策树叶子节点,可以记录结果 + if (path.size() == nums.length) { + res.add(new LinkedList<>(path)); + System.out.printf("【结果】 %s\n\n", CollectionUtil.join(path, " -> ")); + return; + } + + for (int i = 0; i < nums.length; i++) { + + // 排除不合法的选择 + // nums[i] 已经在 path 中,跳过 + if (visited[i]) { continue; } + + // 【选择】 + path.add(nums[i]); + visited[i] = true; + System.out.printf("\t\t%s\n", CollectionUtil.join(path, " -> ")); + + // 【回溯】 + backtrack(nums); + + // 【取消选择】 + path.removeLast(); + visited[i] = false; + } + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/permutation_combination/\345\205\250\346\216\222\345\210\2272.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/permutation_combination/\345\205\250\346\216\222\345\210\2272.java" new file mode 100644 index 0000000..038cdda --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/permutation_combination/\345\205\250\346\216\222\345\210\2272.java" @@ -0,0 +1,76 @@ +package io.github.dunwu.algorithm.dfs.permutation_combination; + +import cn.hutool.core.collection.CollectionUtil; +import io.github.dunwu.algorithm.util.ArrayUtil; +import org.junit.jupiter.api.Assertions; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +/** + * 47. 全排列 II + * LCR 084. 全排列 II + * + * @author Zhang Peng + * @date 2025-11-03 + */ +public class 全排列2 { + + public static void main(String[] args) { + Solution s = new Solution(); + + int[][] expect = { { 1, 1, 2 }, { 1, 2, 1 }, { 2, 1, 1 } }; + Assertions.assertArrayEquals(expect, ArrayUtil.toIntMatrixArray(s.permuteUnique(new int[] { 1, 1, 2 }))); + + int[][] expect2 = { { 1, 2, 3 }, { 1, 3, 2 }, { 2, 1, 3 }, { 2, 3, 1 }, { 3, 1, 2 }, { 3, 2, 1 } }; + Assertions.assertArrayEquals(expect2, ArrayUtil.toIntMatrixArray(s.permuteUnique(new int[] { 1, 2, 3 }))); + } + + static class Solution { + + private boolean[] visited; + private List path; + private List> res; + + public List> permuteUnique(int[] nums) { + visited = new boolean[nums.length]; + path = new LinkedList<>(); + res = new LinkedList<>(); + Arrays.sort(nums); + backtrack(nums); + return res; + } + + public void backtrack(int[] nums) { + + // 【结束】【前序】到达决策树叶子节点,可以记录结果 + if (path.size() == nums.length) { + res.add(new LinkedList<>(path)); + System.out.printf("【结果】 %s\n\n", CollectionUtil.join(path, " -> ")); + } + + for (int i = 0; i < nums.length; i++) { + + // 排除不合法的选择 + if (visited[i]) { continue; } + // 剪枝逻辑,固定相同的元素在排列中的相对位置 + if (i > 0 && nums[i] == nums[i - 1] && !visited[i - 1]) { continue; } + + // 【选择】 + visited[i] = true; + path.add(nums[i]); + System.out.printf("\t\t%s\n", CollectionUtil.join(path, " -> ")); + + // 【回溯】 + backtrack(nums); + + // 【取消选择】 + path.remove(path.size() - 1); + visited[i] = false; + } + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/permutation_combination/\345\255\220\351\233\206.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/permutation_combination/\345\255\220\351\233\206.java" new file mode 100644 index 0000000..ce2377e --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/permutation_combination/\345\255\220\351\233\206.java" @@ -0,0 +1,70 @@ +package io.github.dunwu.algorithm.dfs.permutation_combination; + +import cn.hutool.core.collection.CollectionUtil; +import io.github.dunwu.algorithm.util.ArrayUtil; +import org.junit.jupiter.api.Assertions; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +/** + * 78. 子集 + * + * @author Zhang Peng + * @date 2025-11-04 + */ +public class 子集 { + + public static void main(String[] args) { + Solution s = new Solution(); + int[][] expect = { {}, { 1 }, { 1, 2 }, { 1, 2, 3 }, { 1, 3 }, { 2 }, { 2, 3 }, { 3 } }; + Assertions.assertArrayEquals(expect, ArrayUtil.toIntMatrixArray(s.subsets(new int[] { 1, 2, 3 }))); + Assertions.assertArrayEquals(new int[][] { {}, { 0 } }, ArrayUtil.toIntMatrixArray(s.subsets(new int[] { 0 }))); + } + + static class Solution { + + private boolean[] visited; + private List path; + private List> res; + + // 主函数 + public List> subsets(int[] nums) { + visited = new boolean[nums.length]; + path = new LinkedList<>(); + res = new LinkedList<>(); + Arrays.sort(nums); + backtrack(nums, 0); + return res; + } + + // 回溯算法核心函数,遍历子集问题的回溯树 + public void backtrack(int[] nums, int start) { + + // 【结束】【前序】到达决策树叶子节点,可以记录结果 + res.add(new LinkedList<>(path)); + System.out.printf("【结果】 %s\n\n", CollectionUtil.join(path, " -> ")); + + for (int i = start; i < nums.length; i++) { + + // 排除不合法的选择 + if (visited[i]) { continue; } + + // 【选择】 + visited[i] = true; + path.add(nums[i]); + System.out.printf("\t\t%s\n", CollectionUtil.join(path, " -> ")); + + // 【回溯】 + backtrack(nums, i + 1); + + // 【取消选择】 + path.remove(path.size() - 1); + visited[i] = false; + } + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/permutation_combination/\345\255\220\351\233\2062.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/permutation_combination/\345\255\220\351\233\2062.java" new file mode 100644 index 0000000..d09fb5e --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/permutation_combination/\345\255\220\351\233\2062.java" @@ -0,0 +1,71 @@ +package io.github.dunwu.algorithm.dfs.permutation_combination; + +import cn.hutool.core.collection.CollectionUtil; +import io.github.dunwu.algorithm.util.ArrayUtil; +import org.junit.jupiter.api.Assertions; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +/** + * 90. 子集 II + *

+ * 元素可重复,不可复选 + * + * @author Zhang Peng + * @date 2025-11-04 + */ +public class 子集2 { + + public static void main(String[] args) { + + Solution s = new Solution(); + + int[][] expect = { {}, { 1 }, { 1, 2 }, { 1, 2, 2 }, { 2 }, { 2, 2 } }; + Assertions.assertArrayEquals(expect, ArrayUtil.toIntMatrixArray(s.subsetsWithDup(new int[] { 1, 2, 2 }))); + + int[][] expect2 = { {}, { 0 } }; + Assertions.assertArrayEquals(expect2, ArrayUtil.toIntMatrixArray(s.subsetsWithDup(new int[] { 0 }))); + } + + static class Solution { + + private List> res; + private LinkedList path; + + public List> subsetsWithDup(int[] nums) { + path = new LinkedList<>(); + res = new LinkedList<>(); + // 先排序,让相同的元素靠在一起 + Arrays.sort(nums); + backtrack(nums, 0); + return res; + } + + public void backtrack(int[] nums, int start) { + + // 【结束】 + res.add(new LinkedList<>(path)); + System.out.printf("【结果】 %s\n\n", CollectionUtil.join(path, " -> ")); + + for (int i = start; i < nums.length; i++) { + + // 剪枝逻辑,值相同的相邻树枝,只遍历第一条 + if (i > start && nums[i] == nums[i - 1]) continue; + + // 【选择】 + path.add(nums[i]); + System.out.printf("\t\t%s\n", CollectionUtil.join(path, " -> ")); + + // 【回溯】 + backtrack(nums, i + 1); + + // 【取消选择】 + path.remove(path.size() - 1); + } + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/permutation_combination/\347\273\204\345\220\210.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/permutation_combination/\347\273\204\345\220\210.java" new file mode 100644 index 0000000..c29d190 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/permutation_combination/\347\273\204\345\220\210.java" @@ -0,0 +1,61 @@ +package io.github.dunwu.algorithm.dfs.permutation_combination; + +import cn.hutool.core.collection.CollectionUtil; +import io.github.dunwu.algorithm.util.ArrayUtil; +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; +import java.util.List; + +/** + * 77. 组合 + * + * @author Zhang Peng + * @date 2025-11-04 + */ +public class 组合 { + + public static void main(String[] args) { + Solution s = new Solution(); + int[][] expect = { { 1, 2 }, { 1, 3 }, { 1, 4 }, { 2, 3 }, { 2, 4 }, { 3, 4 } }; + Assertions.assertArrayEquals(expect, ArrayUtil.toIntMatrixArray(s.combine(4, 2))); + Assertions.assertArrayEquals(new int[][] { { 1 } }, ArrayUtil.toIntMatrixArray(s.combine(1, 1))); + } + + static class Solution { + + private List path; + private List> res; + + public List> combine(int n, int k) { + path = new LinkedList<>(); + res = new LinkedList<>(); + backtrack(n, k, 1); + return res; + } + + public void backtrack(int n, int k, int s) { + + // 【结束】 + if (path.size() == k) { + res.add(new LinkedList<>(path)); + System.out.printf("【结果】 %s\n\n", CollectionUtil.join(path, " -> ")); + } + + for (int i = s; i <= n; i++) { + // 【选择】 + path.add(i); + System.out.printf("\t\t%s\n", CollectionUtil.join(path, " -> ")); + + // 【回溯】 + // 通过 start 参数控制树枝的遍历,避免产生重复的子集 + backtrack(n, k, i + 1); + + // 【取消选择】 + path.remove(path.size() - 1); + } + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/permutation_combination/\347\273\204\345\220\210\346\200\273\345\222\214.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/permutation_combination/\347\273\204\345\220\210\346\200\273\345\222\214.java" new file mode 100644 index 0000000..8deb466 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/permutation_combination/\347\273\204\345\220\210\346\200\273\345\222\214.java" @@ -0,0 +1,74 @@ +package io.github.dunwu.algorithm.dfs.permutation_combination; + +import cn.hutool.core.collection.CollectionUtil; +import io.github.dunwu.algorithm.util.ArrayUtil; +import org.junit.jupiter.api.Assertions; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +/** + * 39. 组合总和 + * + * @author Zhang Peng + * @date 2025-11-04 + */ +public class 组合总和 { + + public static void main(String[] args) { + Solution s = new Solution(); + + List> output = s.combinationSum(new int[] { 2, 3, 6, 7 }, 7); + Assertions.assertArrayEquals(new int[][] { { 2, 2, 3 }, { 7 } }, ArrayUtil.toIntMatrixArray(output)); + + List> output2 = s.combinationSum(new int[] { 2, 3, 5 }, 8); + Assertions.assertArrayEquals(new int[][] { { 2, 2, 2, 2 }, { 2, 3, 3 }, { 3, 5 } }, + ArrayUtil.toIntMatrixArray(output2)); + } + + static class Solution { + + private int sum; + private List path; + private List> res; + + public List> combinationSum(int[] candidates, int target) { + sum = 0; + path = new LinkedList<>(); + res = new LinkedList<>(); + Arrays.sort(candidates); + backtrack(candidates, target, 0); + return res; + } + + public void backtrack(int[] nums, int target, int start) { + + // 【结束】【前序】找到目标和,记录结果 + if (sum == target) { + res.add(new LinkedList<>(path)); + System.out.printf("【结果】 %s\n\n", CollectionUtil.join(path, " -> ")); + return; + } + // base case,超过目标和,停止向下遍历 + if (sum > target) { return; } + + for (int i = start; i < nums.length; i++) { + // 【选择】 + sum += nums[i]; + path.add(nums[i]); + System.out.printf("\t\t%s\n", CollectionUtil.join(path, " -> ")); + + // 【回溯】 + // 同一元素可重复使用,注意参数 + backtrack(nums, target, i); + + // 【取消选择】 + path.remove(path.size() - 1); + sum -= nums[i]; + } + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/permutation_combination/\347\273\204\345\220\210\346\200\273\345\222\2142.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/permutation_combination/\347\273\204\345\220\210\346\200\273\345\222\2142.java" new file mode 100644 index 0000000..e57c58c --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/permutation_combination/\347\273\204\345\220\210\346\200\273\345\222\2142.java" @@ -0,0 +1,86 @@ +package io.github.dunwu.algorithm.dfs.permutation_combination; + +import cn.hutool.core.collection.CollectionUtil; +import io.github.dunwu.algorithm.util.ArrayUtil; +import org.junit.jupiter.api.Assertions; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +/** + * 40. 组合总和 II + * LCR 082. 组合总和 II + * + * @author Zhang Peng + * @date 2025-11-04 + */ +public class 组合总和2 { + + public static void main(String[] args) { + + Solution s = new Solution(); + + List> output = s.combinationSum2(new int[] { 10, 1, 2, 7, 6, 1, 5 }, 8); + int[][] expect = { { 1, 1, 6 }, { 1, 2, 5 }, { 1, 7 }, { 2, 6 } }; + Assertions.assertArrayEquals(expect, ArrayUtil.toIntMatrixArray(output)); + + List> output2 = s.combinationSum2(new int[] { 2, 5, 2, 1, 2 }, 5); + int[][] expect2 = { { 1, 2, 2 }, { 5 } }; + Assertions.assertArrayEquals(expect2, ArrayUtil.toIntMatrixArray(output2)); + } + + static class Solution { + + private int sum; + private boolean[] visited; + private List path; + private List> res; + + public List> combinationSum2(int[] candidates, int target) { + sum = 0; + visited = new boolean[candidates.length]; + path = new ArrayList<>(); + res = new ArrayList<>(); + Arrays.sort(candidates); + backtrack(candidates, target, 0); + return res; + } + + public void backtrack(int[] nums, int target, int start) { + + // 【结束】【前序】找到目标和,记录结果 + if (sum == target) { + res.add(new LinkedList<>(path)); + System.out.printf("【结果】 %s\n\n", CollectionUtil.join(path, " -> ")); + return; + } + // base case,超过目标和,停止向下遍历 + if (sum > target) { return; } + + for (int i = start; i < nums.length; i++) { + + // 剪枝逻辑 + if (visited[i]) { continue; } + if (i > start && nums[i] == nums[i - 1] && !visited[i - 1]) { continue; } + + // 【选择】 + sum += nums[i]; + visited[i] = true; + path.add(nums[i]); + System.out.printf("\t\t%s\n", CollectionUtil.join(path, " -> ")); + + // 【回溯】 + backtrack(nums, target, i + 1); + + // 【取消选择】 + path.remove(path.size() - 1); + visited[i] = false; + sum -= nums[i]; + } + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/permutation_combination/\347\273\204\345\220\210\346\200\273\345\222\2143.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/permutation_combination/\347\273\204\345\220\210\346\200\273\345\222\2143.java" new file mode 100644 index 0000000..c0128a6 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/permutation_combination/\347\273\204\345\220\210\346\200\273\345\222\2143.java" @@ -0,0 +1,83 @@ +package io.github.dunwu.algorithm.dfs.permutation_combination; + +import cn.hutool.core.collection.CollectionUtil; +import io.github.dunwu.algorithm.util.ArrayUtil; +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; +import java.util.List; + +/** + * 216. 组合总和 III + * + * @author Zhang Peng + * @date 2025-11-04 + */ +public class 组合总和3 { + + public static void main(String[] args) { + Solution s = new Solution(); + + Assertions.assertArrayEquals(new int[][] { { 1, 2, 4 } }, ArrayUtil.toIntMatrixArray(s.combinationSum3(3, 7))); + + int[][] expect2 = { { 1, 2, 6 }, { 1, 3, 5 }, { 2, 3, 4 } }; + Assertions.assertArrayEquals(expect2, ArrayUtil.toIntMatrixArray(s.combinationSum3(3, 9))); + + Assertions.assertArrayEquals(new int[][] {}, ArrayUtil.toIntMatrixArray(s.combinationSum3(4, 1))); + } + + static class Solution { + + private int sum; + private boolean[] visited; + private List path; + private List> res; + + public List> combinationSum3(int k, int n) { + sum = 0; + visited = new boolean[9]; + path = new LinkedList<>(); + res = new LinkedList<>(); + int[] nums = new int[9]; + for (int i = 0; i < 9; i++) { + nums[i] = i + 1; + } + backtrack(nums, n, k, 0); + return res; + } + + public void backtrack(int[] nums, int target, int k, int s) { + + // 【结束】【前序】找到目标和,记录结果 + if (sum == target && path.size() == k) { + res.add(new LinkedList<>(path)); + System.out.printf("【结果】 %s\n\n", CollectionUtil.join(path, " -> ")); + return; + } + // base case,超过目标和,停止向下遍历 + if (sum > target || path.size() > k) { return; } + + for (int i = s; i < nums.length; i++) { + + if (visited[i]) { continue; } + + // 【选择】 + sum += nums[i]; + visited[i] = true; + path.add(nums[i]); + System.out.printf("\t\t%s\n", CollectionUtil.join(path, " -> ")); + + // 【回溯】 + // 同一元素可重复使用,注意参数 + backtrack(nums, target, k, i + 1); + + // 【取消选择】 + path.remove(path.size() - 1); + visited[i] = false; + sum -= nums[i]; + } + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/sudoku/N\347\232\207\345\220\216.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/sudoku/N\347\232\207\345\220\216.java" new file mode 100644 index 0000000..9e15542 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/sudoku/N\347\232\207\345\220\216.java" @@ -0,0 +1,96 @@ +package io.github.dunwu.algorithm.dfs.sudoku; + +import io.github.dunwu.algorithm.util.ArrayUtil; +import org.junit.jupiter.api.Assertions; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * 51. N 皇后 + * + * @author Zhang Peng + * @since 2020-07-04 + */ +public class N皇后 { + + public static void main(String[] args) { + Solution s = new Solution(); + List> output = s.solveNQueens(4); + String[][] expect = { { ".Q..", "...Q", "Q...", "..Q." }, { "..Q.", "Q...", "...Q", ".Q.." } }; + Assertions.assertArrayEquals(expect, ArrayUtil.toStringMatrixArray(output)); + + List> output2 = s.solveNQueens(1); + String[][] expect2 = { { "Q" } }; + Assertions.assertArrayEquals(expect2, ArrayUtil.toStringMatrixArray(output2)); + } + + static class Solution { + + private List> res; + + // 输入棋盘边长 n,返回所有合法的放置 + public List> solveNQueens(int n) { + res = new ArrayList<>(); + // '.' 表示空,'Q' 表示皇后,初始化空棋盘。 + char[] arr = new char[n]; + Arrays.fill(arr, '.'); + String str = new String(arr); + List board = new ArrayList<>(); + for (int i = 0; i < n; i++) { + board.add(str); + } + backtrack(board, 0); + return res; + } + + // 路径:board 中小于 row 的那些行都已经成功放置了皇后 + // 选择列表:第 row 行的所有列都是放置皇后的选择 + // 结束条件:row 超过 board 的最后一行 + public void backtrack(List board, int row) { + // 触发结束条件 + if (row == board.size()) { + res.add(new ArrayList<>(board)); + return; + } + + int n = board.get(row).length(); + for (int col = 0; col < n; col++) { + // 排除不合法选择 + if (!isValid(board, row, col)) { + continue; + } + // 做选择 + char[] newRow = board.get(row).toCharArray(); + newRow[col] = 'Q'; + board.set(row, new String(newRow)); + // 进入下一行决策 + backtrack(board, row + 1); + // 撤销选择 + newRow[col] = '.'; + board.set(row, new String(newRow)); + } + } + + // 是否可以在 board[row][col] 放置皇后? + public boolean isValid(List board, int row, int col) { + int n = board.size(); + // 检查列是否有皇后互相冲突 + for (int i = 0; i < row; i++) { + if (board.get(i).charAt(col) == 'Q') { return false; } + } + // 检查右上方是否有皇后互相冲突 + for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) { + if (board.get(i).charAt(j) == 'Q') { return false; } + } + // 检查左上方是否有皇后互相冲突 + for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) { + if (board.get(i).charAt(j) == 'Q') { return false; } + } + return true; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/sudoku/N\347\232\207\345\220\2162.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/sudoku/N\347\232\207\345\220\2162.java" new file mode 100644 index 0000000..c9a72cf --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/sudoku/N\347\232\207\345\220\2162.java" @@ -0,0 +1,94 @@ +package io.github.dunwu.algorithm.dfs.sudoku; + +import org.junit.jupiter.api.Assertions; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * 52. N皇后II + * + * @author Zhang Peng + * @since 2020-07-04 + */ +public class N皇后2 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(2, s.totalNQueens(4)); + Assertions.assertEquals(1, s.totalNQueens(1)); + } + + static class Solution { + + private List> res; + + public int totalNQueens(int n) { + return solveNQueens(n).size(); + } + + // 输入棋盘边长 n,返回所有合法的放置 + public List> solveNQueens(int n) { + res = new ArrayList<>(); + // '.' 表示空,'Q' 表示皇后,初始化空棋盘。 + char[] arr = new char[n]; + Arrays.fill(arr, '.'); + String str = new String(arr); + List board = new ArrayList<>(); + for (int i = 0; i < n; i++) { + board.add(str); + } + backtrack(board, 0); + return res; + } + + // 路径:board 中小于 row 的那些行都已经成功放置了皇后 + // 选择列表:第 row 行的所有列都是放置皇后的选择 + // 结束条件:row 超过 board 的最后一行 + public void backtrack(List board, int row) { + // 触发结束条件 + if (row == board.size()) { + res.add(new ArrayList<>(board)); + return; + } + + int n = board.get(row).length(); + for (int col = 0; col < n; col++) { + // 排除不合法选择 + if (!isValid(board, row, col)) { + continue; + } + // 做选择 + char[] newRow = board.get(row).toCharArray(); + newRow[col] = 'Q'; + board.set(row, new String(newRow)); + // 进入下一行决策 + backtrack(board, row + 1); + // 撤销选择 + newRow[col] = '.'; + board.set(row, new String(newRow)); + } + } + + // 是否可以在 board[row][col] 放置皇后? + public boolean isValid(List board, int row, int col) { + int n = board.size(); + // 检查列是否有皇后互相冲突 + for (int i = 0; i < row; i++) { + if (board.get(i).charAt(col) == 'Q') { return false; } + } + // 检查右上方是否有皇后互相冲突 + for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) { + if (board.get(i).charAt(j) == 'Q') { return false; } + } + // 检查左上方是否有皇后互相冲突 + for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) { + if (board.get(i).charAt(j) == 'Q') { return false; } + } + return true; + } + + } + +} diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/sudoku/package-info.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/sudoku/package-info.java new file mode 100644 index 0000000..34ca619 --- /dev/null +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/sudoku/package-info.java @@ -0,0 +1,7 @@ +/** + * 回溯算法解数独、N 皇后问题 + * + * @author Zhang Peng + * @date 2025-12-13 + */ +package io.github.dunwu.algorithm.dfs.sudoku; \ No newline at end of file diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/sudoku/\350\247\243\346\225\260\347\213\254.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/sudoku/\350\247\243\346\225\260\347\213\254.java" new file mode 100644 index 0000000..3a198c3 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/sudoku/\350\247\243\346\225\260\347\213\254.java" @@ -0,0 +1,249 @@ +package io.github.dunwu.algorithm.dfs.sudoku; + +import org.junit.jupiter.api.Assertions; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * 37. 解数独 + * + * @author Zhang Peng + * @date 2025-11-03 + */ +public class 解数独 { + + public static void main(String[] args) { + + char[][] expect = { + { '5', '3', '4', '6', '7', '8', '9', '1', '2' }, + { '6', '7', '2', '1', '9', '5', '3', '4', '8' }, + { '1', '9', '8', '3', '4', '2', '5', '6', '7' }, + { '8', '5', '9', '7', '6', '1', '4', '2', '3' }, + { '4', '2', '6', '8', '5', '3', '7', '9', '1' }, + { '7', '1', '3', '9', '2', '4', '8', '5', '6' }, + { '9', '6', '1', '5', '3', '7', '2', '8', '4' }, + { '2', '8', '7', '4', '1', '9', '6', '3', '5' }, + { '3', '4', '5', '2', '8', '6', '1', '7', '9' } + }; + char[][] input = { + { '5', '3', '.', '.', '7', '.', '.', '.', '.' }, + { '6', '.', '.', '1', '9', '5', '.', '.', '.' }, + { '.', '9', '8', '.', '.', '.', '.', '6', '.' }, + { '8', '.', '.', '.', '6', '.', '.', '.', '3' }, + { '4', '.', '.', '8', '.', '3', '.', '.', '1' }, + { '7', '.', '.', '.', '2', '.', '.', '.', '6' }, + { '.', '6', '.', '.', '.', '.', '2', '8', '.' }, + { '.', '.', '.', '4', '1', '9', '.', '.', '5' }, + { '.', '.', '.', '.', '8', '.', '.', '7', '9' } + }; + char[][] input2 = { + { '5', '3', '.', '.', '7', '.', '.', '.', '.' }, + { '6', '.', '.', '1', '9', '5', '.', '.', '.' }, + { '.', '9', '8', '.', '.', '.', '.', '6', '.' }, + { '8', '.', '.', '.', '6', '.', '.', '.', '3' }, + { '4', '.', '.', '8', '.', '3', '.', '.', '1' }, + { '7', '.', '.', '.', '2', '.', '.', '.', '6' }, + { '.', '6', '.', '.', '.', '.', '2', '8', '.' }, + { '.', '.', '.', '4', '1', '9', '.', '.', '5' }, + { '.', '.', '.', '.', '8', '.', '.', '7', '9' } + }; + + Solution s = new Solution(); + s.solveSudoku(input); + Assertions.assertArrayEquals(expect, input); + + Solution2 s2 = new Solution2(); + s2.solveSudoku(input2); + Assertions.assertArrayEquals(expect, input2); + } + + static class Solution { + + private int n; + // 标记是否已经找到可行解 + boolean found = false; + + public void solveSudoku(char[][] board) { + n = board.length; + backtrack(board, 0); + } + + // 路径:board 中小于 index 的位置所填的数字 + // 选择列表:数字 1~9 + // 结束条件:整个 board 都填满数字 + void backtrack(char[][] board, int index) { + if (found) { + // 已经找到一个可行解,立即结束 + return; + } + + int[] point = point(index); + int row = point[0], col = point[1]; + if (index == n * n) { + // 找到一个可行解,触发 base case + found = true; + return; + } + + if (board[row][col] != '.') { + // 如果有预设数字,不用我们穷举 + backtrack(board, index + 1); + return; + } + + for (char ch = '1'; ch <= '9'; ch++) { + // 剪枝:如果遇到不合法的数字,就跳过 + if (!isValid(board, row, col, ch)) { continue; } + + // 做选择 + board[row][col] = ch; + + backtrack(board, index + 1); + if (found) { + // 如果找到一个可行解,立即结束 + // 不要撤销选择,否则 board[i][j] 会被重置为 '.' + return; + } + + // 撤销选择 + board[row][col] = '.'; + } + } + + // 判断是否可以在 (r, c) 位置放置数字 num + boolean isValid(char[][] board, int row, int col, char num) { + for (int i = 0; i < 9; i++) { + // 判断行是否存在重复 + if (board[row][i] == num) return false; + // 判断列是否存在重复 + if (board[i][col] == num) return false; + // 判断 3 x 3 方框是否存在重复 + if (board[(row / 3) * 3 + i / 3][(col / 3) * 3 + i % 3] == num) { return false; } + } + return true; + } + + public int index(int x, int y) { + return x * n + y; + } + + public int[] point(int index) { + int x = index / n; + int y = index % n; + return new int[] { x, y }; + } + + } + + static class Solution2 { + + private int n; + + // 标记是否已经找到可行解 + private boolean found; + + // 记录每行已经出现的数字 + // 比如 rows[0] = {1, 2, 3} 表示第 0 行已经出现了数字 1, 2, 3 + private final List> rows; + + // 记录每列已经出现的数字 + private final List> cols; + + // 记录每个九宫格已经出现的数字 + private final List> boxes; + + public Solution2() { + found = false; + rows = new ArrayList<>(9); + cols = new ArrayList<>(9); + boxes = new ArrayList<>(9); + for (int i = 0; i < 9; i++) { + rows.add(new HashSet<>()); + cols.add(new HashSet<>()); + boxes.add(new HashSet<>()); + } + } + + public void solveSudoku(char[][] board) { + n = board.length; + // 将预设数字加入集合 + for (int i = 0; i < 9; i++) { + for (int j = 0; j < 9; j++) { + if (board[i][j] != '.') { + rows.get(i).add(board[i][j]); + cols.get(j).add(board[i][j]); + boxes.get(getBoxIndex(i, j)).add(board[i][j]); + } + } + } + backtrack(board, 0); + } + + // 路径:board 中小于 index 的位置所填的数字 + // 选择列表:数字 1~9 + // 结束条件:整个 board 都填满数字 + public void backtrack(char[][] board, int index) { + + // 已经找到一个可行解,立即结束 + if (found) { return; } + + // 找到一个可行解,触发 base case + if (index == n * n) { + found = true; + return; + } + + int row = index / n; + int col = index % n; + // 如果有预设数字,无需穷举 + if (board[row][col] != '.') { + backtrack(board, index + 1); + return; + } + + for (char ch = '1'; ch <= '9'; ch++) { + + // 【剪枝】如果遇到不合法的数字,就跳过 + if (!isValid(row, col, ch)) { continue; } + + // 【选择】把 ch 填入 board[i][j] + board[row][col] = ch; + rows.get(row).add(ch); + cols.get(col).add(ch); + boxes.get(getBoxIndex(row, col)).add(ch); + + backtrack(board, index + 1); + if (found) { + // 如果找到一个可行解,立即结束 + // 不要撤销选择,否则 board[i][j] 会被重置为 '.' + return; + } + + // 【取消选择】把 board[i][j] 重置为 '.' + board[row][col] = '.'; + rows.get(row).remove(ch); + cols.get(col).remove(ch); + boxes.get(getBoxIndex(row, col)).remove(ch); + } + } + + // 获取 (row, col) 所在的九宫格索引 + public int getBoxIndex(int row, int col) { + return (row / 3) * 3 + (col / 3); + } + + // 判断是否可以在 (row, col) 位置放置数字 num + public boolean isValid(int row, int col, char num) { + // 现在只需要查询三次哈希表即可 + if (rows.get(row).contains(num)) { return false; } + if (cols.get(col).contains(num)) { return false; } + if (boxes.get(getBoxIndex(row, col)).contains(num)) { return false; } + return true; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/template/\345\233\236\346\272\257\347\256\227\346\263\225\346\250\241\346\235\277.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/template/\345\233\236\346\272\257\347\256\227\346\263\225\346\250\241\346\235\277.java" new file mode 100644 index 0000000..24b5dd5 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/template/\345\233\236\346\272\257\347\256\227\346\263\225\346\250\241\346\235\277.java" @@ -0,0 +1,20 @@ +package io.github.dunwu.algorithm.dfs.template; + +/** + * 回溯算法模板 + * + * @author Zhang Peng + * @date 2025-12-11 + */ +public class 回溯算法模板 { + + // 【回溯算法伪代码模板】 + // for 选择 in 选择列表: + // # 做选择 + // 将该选择从选择列表移除 + // 路径.add(选择) + // backtrack(路径, 选择列表) + // # 撤销选择 + // 路径.remove(选择) + // 将该选择再加入选择列表 +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/\344\274\230\347\276\216\347\232\204\346\216\222\345\210\227.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/\344\274\230\347\276\216\347\232\204\346\216\222\345\210\227.java" new file mode 100644 index 0000000..cb4aff8 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/\344\274\230\347\276\216\347\232\204\346\216\222\345\210\227.java" @@ -0,0 +1,69 @@ +package io.github.dunwu.algorithm.dfs; + +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; + +/** + * 526. 优美的排列 + * + * @author Zhang Peng + * @date 2025-12-12 + */ +public class 优美的排列 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(1, s.countArrangement(1)); + Assertions.assertEquals(2, s.countArrangement(2)); + } + + static class Solution { + + // 记录所有的「优美排列」的个数 + private int res = 0; + // track 中的元素会被标记为 true,避免重复选择 + private boolean[] visited; + // 记录回溯算法的递归路径,即每个索引选择的元素 + private LinkedList path; + + public int countArrangement(int n) { + res = 0; + visited = new boolean[n + 1]; + path = new LinkedList<>(); + dfs(n, 1); + return res; + } + + // 回溯算法标准框架,站在索引的视角选择元素 + void dfs(int n, int index) { + // base case,到达叶子节点 + if (index > n) { + // 找到一个结果 + res += 1; + return; + } + + // 索引 index 开始选择元素 + for (int val = 1; val <= n; val++) { + // 已经被其他索引选过的元素,不能重复选择 + if (visited[val]) { + continue; + } + if (!(index % val == 0 || val % index == 0)) { + continue; + } + // 【选择】index 选择元素 elem + visited[val] = true; + path.addLast(val); + // 【回溯】 + dfs(n, index + 1); + // 【取消选择】 + path.removeLast(); + visited[val] = false; + } + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/\345\210\206\345\211\262\345\233\236\346\226\207\344\270\262.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/\345\210\206\345\211\262\345\233\236\346\226\207\344\270\262.java" new file mode 100644 index 0000000..7c63a40 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/\345\210\206\345\211\262\345\233\236\346\226\207\344\270\262.java" @@ -0,0 +1,72 @@ +package io.github.dunwu.algorithm.dfs; + +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; +import java.util.List; + +/** + * 131. 分割回文串 + * + * @author Zhang Peng + * @date 2025-12-12 + */ +public class 分割回文串 { + + public static void main(String[] args) { + Solution s = new Solution(); + + String[][] expect = new String[][] { { "a", "a", "b" }, { "aa", "b" } }; + List> output = s.partition("aab"); + Assertions.assertEquals(expect.length, output.size()); + + List> output2 = s.partition("a"); + Assertions.assertEquals(1, output2.size()); + } + + static class Solution { + + private List path; + private List> res; + + public List> partition(String s) { + path = new LinkedList<>(); + res = new LinkedList<>(); + dfs(s, 0); + return res; + } + + public void dfs(String s, int start) { + if (start == s.length()) { + // base case,走到叶子节点 + // 即整个 s 被成功分割为若干个回文子串,记下答案 + res.add(new LinkedList(path)); + } + for (int i = start; i < s.length(); i++) { + if (!isPalindrome(s, start, i)) { + // s[start..i] 不是回文串,不能分割 + continue; + } + // s[start..i] 是一个回文串,可以进行分割 + // 做选择,把 s[start..i] 放入路径列表中 + path.add(s.substring(start, i + 1)); + // 进入回溯树的下一层,继续切分 s[i+1..] + dfs(s, i + 1); + // 撤销选择 + path.remove(path.size() - 1); + } + } + + // 用双指针技巧判断 s[low..high] 是否是一个回文串 + public boolean isPalindrome(String s, int low, int high) { + while (low < high) { + if (s.charAt(low) != s.charAt(high)) { return false; } + low++; + high--; + } + return true; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/\345\215\225\350\257\215\346\220\234\347\264\242.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/\345\215\225\350\257\215\346\220\234\347\264\242.java" new file mode 100644 index 0000000..ef11cfe --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/\345\215\225\350\257\215\346\220\234\347\264\242.java" @@ -0,0 +1,70 @@ +package io.github.dunwu.algorithm.dfs; + +import org.junit.jupiter.api.Assertions; + +/** + * 79. 单词搜索 + * + * @author Zhang Peng + * @date 2025-12-12 + */ +public class 单词搜索 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertTrue( + s.exist(new char[][] { { 'A', 'B', 'C', 'E' }, { 'S', 'F', 'C', 'S' }, { 'A', 'D', 'E', 'E' } }, "ABCCED")); + Assertions.assertTrue( + s.exist(new char[][] { { 'A', 'B', 'C', 'E' }, { 'S', 'F', 'C', 'S' }, { 'A', 'D', 'E', 'E' } }, "SEE")); + Assertions.assertFalse( + s.exist(new char[][] { { 'A', 'B', 'C', 'E' }, { 'S', 'F', 'C', 'S' }, { 'A', 'D', 'E', 'E' } }, "ABCB")); + } + + static class Solution { + + boolean found = false; + + public boolean exist(char[][] board, String word) { + found = false; + int m = board.length, n = board[0].length; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + dfs(board, i, j, word, 0); + if (found) { return true; } + } + } + return false; + } + + // 从 (i, j) 开始向四周搜索,试图匹配 word[p..] + void dfs(char[][] board, int i, int j, String word, int p) { + if (p == word.length()) { + // 整个 word 已经被匹配完,找到了一个答案 + found = true; + return; + } + if (found) { + // 已经找到了一个答案,不用再搜索了 + return; + } + int m = board.length, n = board[0].length; + if (i < 0 || j < 0 || i >= m || j >= n) { + return; + } + if (board[i][j] != word.charAt(p)) { + return; + } + + // 已经匹配过的字符,我们给它添一个负号作为标记,避免走回头路 + board[i][j] = (char) (-board[i][j]); + // word[p] 被 board[i][j] 匹配,开始向四周搜索 word[p+1..] + dfs(board, i + 1, j, word, p + 1); + dfs(board, i, j + 1, word, p + 1); + dfs(board, i - 1, j, word, p + 1); + dfs(board, i, j - 1, word, p + 1); + board[i][j] = (char) (-board[i][j]); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/recursive/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262.java" similarity index 95% rename from "codes/algorithm/src/main/java/io/github/dunwu/algorithm/recursive/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262.java" rename to "codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262.java" index 06cd60a..9799f09 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/recursive/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262.java" @@ -1,4 +1,4 @@ -package io.github.dunwu.algorithm.recursive; +package io.github.dunwu.algorithm.dfs; import org.junit.jupiter.api.Assertions; diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/\345\244\215\345\216\237IP\345\234\260\345\235\200.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/\345\244\215\345\216\237IP\345\234\260\345\235\200.java" new file mode 100644 index 0000000..ce16a6a --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/\345\244\215\345\216\237IP\345\234\260\345\235\200.java" @@ -0,0 +1,88 @@ +package io.github.dunwu.algorithm.dfs; + +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; +import java.util.List; + +/** + * 93. 复原 IP 地址 + * + * @author Zhang Peng + * @date 2025-12-12 + */ +public class 复原IP地址 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertArrayEquals(new String[] { "255.255.11.135", "255.255.111.35" }, + s.restoreIpAddresses("25525511135").toArray()); + Assertions.assertArrayEquals(new String[] { "0.0.0.0" }, s.restoreIpAddresses("0000").toArray()); + Assertions.assertArrayEquals(new String[] { "1.0.10.23", "1.0.102.3", "10.1.0.23", "10.10.2.3", "101.0.2.3" }, + s.restoreIpAddresses("101023").toArray()); + } + + static class Solution { + + private LinkedList path; + private LinkedList res; + + public List restoreIpAddresses(String s) { + path = new LinkedList<>(); + res = new LinkedList<>(); + dfs(s, 0); + return res; + } + + public void dfs(String s, int start) { + + // base case,走到叶子节点 + // 即整个 s 被成功分割为合法的四部分,记下答案 + if (start == s.length() && path.size() == 4) { + String ip = String.join(".", path); + res.add(ip); + } + + for (int i = start; i < s.length(); i++) { + + // s[start..i] 不是合法的 ip 数字,不能分割 + if (!isValid(s, start, i)) { continue; } + + // 已经分解成 4 部分了,不能再分解了 + if (path.size() >= 4) { continue; } + + // 【选择】 + // s[start..i] 是一个合法的 ip 数字,可以进行分割 + // 做选择,把 s[start..i] 放入路径列表中 + path.add(s.substring(start, i + 1)); + + // 【回溯】 + dfs(s, i + 1); + + // 【取消选择】 + path.removeLast(); + } + } + + public boolean isValid(String s, int start, int end) { + + int length = end - start + 1; + + if (length == 0 || length > 3) { return false; } + + // 如果只有一位数字,肯定是合法的 + if (length == 1) { return true; } + + // 多于一位数字,但开头是 0,肯定不合法 + if (s.charAt(start) == '0') { return false; } + + // 排除了开头是 0 的情况,那么如果是两位数,肯定是合法的 + if (length <= 2) { return true; } + + // 现在输入的一定是三位数,不能大于 255 + return Integer.parseInt(s.substring(start, start + length)) <= 255; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/\346\240\274\351\233\267\347\274\226\347\240\201.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/\346\240\274\351\233\267\347\274\226\347\240\201.java" new file mode 100644 index 0000000..92162c4 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/\346\240\274\351\233\267\347\274\226\347\240\201.java" @@ -0,0 +1,64 @@ +package io.github.dunwu.algorithm.dfs; + +import io.github.dunwu.algorithm.util.ArrayUtil; +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; +import java.util.List; + +/** + * 526. 优美的排列 + * + * @author Zhang Peng + * @date 2025-12-12 + */ +public class 格雷编码 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertArrayEquals(new int[] { 0, 1 }, ArrayUtil.toIntArray(s.grayCode(1))); + Assertions.assertArrayEquals(new int[] { 0, 1, 3, 2 }, ArrayUtil.toIntArray(s.grayCode(2))); + } + + static class Solution { + + private boolean[] visited; + private LinkedList path; + private LinkedList res; + + public List grayCode(int n) { + visited = new boolean[n]; + path = new LinkedList<>(); + res = null; + dfs(n, 0); + return res; + } + + public void dfs(int n, int root) { + if (res != null) return; + if (path.size() == (1 << n)) { + res = new LinkedList<>(path); + return; + } + if (visited[root]) return; + + visited[root] = true; + path.addLast(root); + + for (int i = 0; i < n; i++) { + int next = flipBit(root, i); + dfs(n, next); + } + + path.removeLast(); + visited[root] = false; + } + + // 把第 i 位取反(0 变 1,1 变 0) + int flipBit(int x, int i) { + return x ^ (1 << i); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/\347\224\265\350\257\235\345\217\267\347\240\201\347\232\204\345\255\227\346\257\215\347\273\204\345\220\210.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/\347\224\265\350\257\235\345\217\267\347\240\201\347\232\204\345\255\227\346\257\215\347\273\204\345\220\210.java" new file mode 100644 index 0000000..def2aea --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/\347\224\265\350\257\235\345\217\267\347\240\201\347\232\204\345\255\227\346\257\215\347\273\204\345\220\210.java" @@ -0,0 +1,60 @@ +package io.github.dunwu.algorithm.dfs; + +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; +import java.util.List; + +/** + * 17. 电话号码的字母组合 + * + * @author Zhang Peng + * @date 2025-11-05 + */ +public class 电话号码的字母组合 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertArrayEquals(new String[] { "ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf" }, + s.letterCombinations("23").toArray()); + Assertions.assertArrayEquals(new String[] { "a", "b", "c" }, s.letterCombinations("2").toArray()); + Assertions.assertArrayEquals(new String[] { "t", "u", "v" }, s.letterCombinations("8").toArray()); + } + + static class Solution { + + private StringBuilder sb; + private LinkedList res; + private final String[] options = new String[] { + "", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz" + }; + + public List letterCombinations(String digits) { + if (digits.isEmpty()) { return new LinkedList<>(); } + sb = new StringBuilder(); + res = new LinkedList<>(); + // 从 digits[0] 开始进行回溯 + dfs(digits, 0); + return res; + } + + // 回溯算法主函数 + public void dfs(String digits, int start) { + // 到达回溯树底部 + if (sb.length() == digits.length()) { + res.add(sb.toString()); + return; + } + + // 回溯算法框架 + int digit = digits.charAt(start) - '0'; + for (char c : options[digit].toCharArray()) { + sb.append(c); + dfs(digits, start + 1); + sb.deleteCharAt(sb.length() - 1); + } + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/\350\277\236\347\273\255\345\267\256\347\233\270\345\220\214\347\232\204\346\225\260\345\255\227.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/\350\277\236\347\273\255\345\267\256\347\233\270\345\220\214\347\232\204\346\225\260\345\255\227.java" new file mode 100644 index 0000000..d9a0551 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/\350\277\236\347\273\255\345\267\256\347\233\270\345\220\214\347\232\204\346\225\260\345\255\227.java" @@ -0,0 +1,78 @@ +package io.github.dunwu.algorithm.dfs; + +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; +import java.util.List; + +/** + * 967. 连续差相同的数字 + * + * @author Zhang Peng + * @date 2025-11-05 + */ +public class 连续差相同的数字 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertArrayEquals(new int[] { 181, 292, 707, 818, 929 }, s.numsSameConsecDiff(3, 7)); + Assertions.assertArrayEquals(new int[] { 10, 12, 21, 23, 32, 34, 43, 45, 54, 56, 65, 67, 76, 78, 87, 89, 98 }, + s.numsSameConsecDiff(2, 1)); + Assertions.assertArrayEquals(new int[] { 11, 22, 33, 44, 55, 66, 77, 88, 99 }, s.numsSameConsecDiff(2, 0)); + Assertions.assertArrayEquals(new int[] { 13, 20, 24, 31, 35, 42, 46, 53, 57, 64, 68, 75, 79, 86, 97 }, + s.numsSameConsecDiff(2, 2)); + } + + static class Solution { + + // 记录当前路径组成的数字的值 + private int num = 0; + // 记录当前数字的位数 + private int digit = 0; + private List res; + + public int[] numsSameConsecDiff(int n, int k) { + + num = 0; + digit = 0; + res = new LinkedList<>(); + + dfs(n, k); + + int[] arr = new int[res.size()]; + for (int i = 0; i < res.size(); i++) { + arr[i] = res.get(i); + } + return arr; + } + + // 回溯算法核心函数 + void dfs(int n, int k) { + // base case,到达叶子节点 + if (digit == n) { + // 找到一个合法的 n 位数 + res.add(num); + return; + } + + // 回溯算法标准框架 + for (int i = 0; i <= 9; i++) { + // 本题的剪枝逻辑 1,第一个数字不能是 0 + if (digit == 0 && i == 0) continue; + // 本题的剪枝逻辑 2,相邻两个数字的差的绝对值必须等于 k + if (digit > 0 && Math.abs(i - num % 10) != k) continue; + + // 做选择,在 track 尾部追加数字 i + digit++; + num = 10 * num + i; + // 进入下一层回溯树 + dfs(n, k); + // 取消选择,删除 track 尾部数字 + num = num / 10; + digit--; + } + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/\351\235\236\351\200\222\345\207\217\345\255\220\345\272\217\345\210\227.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/\351\235\236\351\200\222\345\207\217\345\255\220\345\272\217\345\210\227.java" new file mode 100644 index 0000000..0c5494d --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dfs/\351\235\236\351\200\222\345\207\217\345\255\220\345\272\217\345\210\227.java" @@ -0,0 +1,76 @@ +package io.github.dunwu.algorithm.dfs; + +import io.github.dunwu.algorithm.util.ArrayUtil; +import org.junit.jupiter.api.Assertions; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; + +/** + * 491. 非递减子序列 + * + * @author Zhang Peng + * @date 2025-12-12 + */ +public class 非递减子序列 { + + public static void main(String[] args) { + Solution s = new Solution(); + + int[][] expect = new int[][] { + { 4, 6 }, { 4, 6, 7 }, { 4, 6, 7, 7 }, { 4, 7 }, { 4, 7, 7 }, { 6, 7 }, { 6, 7, 7 }, { 7, 7 } + }; + List> output = s.findSubsequences(new int[] { 4, 6, 7, 7 }); + Assertions.assertArrayEquals(expect, ArrayUtil.toIntMatrixArray(output)); + + List> output2 = s.findSubsequences(new int[] { 4, 4, 3, 2, 1 }); + Assertions.assertArrayEquals(new int[][] { { 4, 4 } }, ArrayUtil.toIntMatrixArray(output2)); + } + + static class Solution { + + private List path; + private List> res; + + public List> findSubsequences(int[] nums) { + + // base case + if (nums == null || nums.length == 0) { return new LinkedList<>(); } + + path = new LinkedList<>(); + res = new LinkedList<>(); + + dfs(nums, 0); + return res; + } + + public void dfs(int[] nums, int start) { + if (path.size() >= 2) { + res.add(new LinkedList<>(path)); + } + + // 用哈希集合防止重复选择相同元素 + HashSet visited = new HashSet<>(); + + for (int i = start; i < nums.length; i++) { + + if (!path.isEmpty() && nums[i] < path.get(path.size() - 1)) { continue; } + // 保证不要重复使用相同的元素 + if (visited.contains(nums[i])) { continue; } + + // 【选择】 + visited.add(nums[i]); + path.add(nums[i]); + + // 【递归】 + dfs(nums, i + 1); + + // 【取消选择】 + path.remove(path.size() - 1); + } + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/array/\344\270\221\346\225\2602.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/array/\344\270\221\346\225\2602.java" new file mode 100644 index 0000000..dcb4260 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/array/\344\270\221\346\225\2602.java" @@ -0,0 +1,42 @@ +package io.github.dunwu.algorithm.dp.array; + +import org.junit.jupiter.api.Assertions; + +/** + * 264. 丑数II + * + * @author Zhang Peng + * @date 2025-01-24 + */ +public class 丑数2 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(12, s.nthUglyNumber(10)); + Assertions.assertEquals(1, s.nthUglyNumber(1)); + Assertions.assertEquals(15, s.nthUglyNumber(11)); + } + + static class Solution { + + public int nthUglyNumber(int n) { + int[] dp = new int[n + 1]; + dp[1] = 1; + int p2 = 1, p3 = 1, p5 = 1; + for (int index = 2; index <= n; index++) { + int n2 = dp[p2] * 2, n3 = dp[p3] * 3, n5 = dp[p5] * 5; + dp[index] = min(n2, n3, n5); + if (dp[index] == n2) { p2++; } + if (dp[index] == n3) { p3++; } + if (dp[index] == n5) { p5++; } + } + return dp[n]; + } + + public int min(int a, int b, int c) { + return Math.min(a, Math.min(b, c)); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/array/\344\270\221\346\225\2603.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/array/\344\270\221\346\225\2603.java" new file mode 100644 index 0000000..1ca3fb6 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/array/\344\270\221\346\225\2603.java" @@ -0,0 +1,42 @@ +package io.github.dunwu.algorithm.dp.array; + +import org.junit.jupiter.api.Assertions; + +/** + * 1201. 丑数 III + * + * @author Zhang Peng + * @date 2025-01-24 + */ +public class 丑数3 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(4, s.nthUglyNumber(3, 2, 3, 5)); + Assertions.assertEquals(6, s.nthUglyNumber(4, 2, 3, 4)); + Assertions.assertEquals(10, s.nthUglyNumber(5, 2, 11, 13)); + } + + static class Solution { + + public int nthUglyNumber(int n, int a, int b, int c) { + int[] dp = new int[n + 1]; + int pa = 1, pb = 1, pc = 1; + dp[0] = 1; + for (int i = 1; i <= n; i++) { + int na = pa * a, nb = pb * b, nc = pc * c; + dp[i] = min(na, nb, nc); + if (dp[i] == na) { pa++; } + if (dp[i] == nb) { pb++; } + if (dp[i] == nc) { pc++; } + } + return dp[n]; + } + + int min(int a, int b, int c) { + return Math.min(a, Math.min(b, c)); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/array/\346\234\200\344\275\216\347\245\250\344\273\267.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/array/\346\234\200\344\275\216\347\245\250\344\273\267.java" new file mode 100644 index 0000000..80c922b --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/array/\346\234\200\344\275\216\347\245\250\344\273\267.java" @@ -0,0 +1,58 @@ +package io.github.dunwu.algorithm.dp.array; + +import org.junit.jupiter.api.Assertions; + +/** + * 983. 最低票价 + * + * @author Zhang Peng + * @since 2025-11-17 + */ +public class 最低票价 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(11, s.mincostTickets(new int[] { 1, 4, 6, 7, 8, 20 }, new int[] { 2, 7, 15 })); + Assertions.assertEquals(17, + s.mincostTickets(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 30, 31 }, new int[] { 2, 7, 15 })); + } + + // 动态规划 + static class Solution { + + public int mincostTickets(int[] days, int[] costs) { + int n = days.length; + int lastDay = days[n - 1]; + boolean[] isTravel = new boolean[lastDay + 1]; + for (int d : days) { isTravel[d] = true; } + + // dp[i] 表示 1 到 i 天的最小花费 + int[] dp = new int[lastDay + 1]; + dp[0] = 0; + for (int i = 1; i <= lastDay; i++) { + if (!isTravel[i]) { + // 如果第 i 天不在 days 中,则第 i 天和第 i - 1 天花费相同 + dp[i] = dp[i - 1]; + } else { + // 如果第 i 天在 days 中 + // 则求三种不同方案最小值: + dp[i] = min( + // 在第 i 天购买为期 1 天的通行证的最小花费 + costs[0] + dp[i - 1], + // 在第 i - 7 天购买为期 7 天的通行证的最小花费(如果 i - 7 < 0,视为 0,f[0] 花费为 0) + costs[1] + dp[Math.max(0, i - 7)], + // 在第 i - 30 天购买为期 30 天的通行证的最小花费(如果 i - 30 < 0,视为 0,f[0] 花费为 0) + costs[2] + dp[Math.max(0, i - 30)] + ); + } + } + return dp[lastDay]; + } + + public int min(int a, int b, int c) { + return Math.min(a, Math.min(b, c)); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/array/\347\273\237\350\256\241\346\236\204\351\200\240\345\245\275\345\255\227\347\254\246\344\270\262\347\232\204\346\226\271\346\241\210\346\225\260.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/array/\347\273\237\350\256\241\346\236\204\351\200\240\345\245\275\345\255\227\347\254\246\344\270\262\347\232\204\346\226\271\346\241\210\346\225\260.java" new file mode 100644 index 0000000..107feec --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/array/\347\273\237\350\256\241\346\236\204\351\200\240\345\245\275\345\255\227\347\254\246\344\270\262\347\232\204\346\226\271\346\241\210\346\225\260.java" @@ -0,0 +1,38 @@ +package io.github.dunwu.algorithm.dp.array; + +import org.junit.jupiter.api.Assertions; + +/** + * 2466. 统计构造好字符串的方案数 + * + * @author Zhang Peng + * @since 2025-11-17 + */ +public class 统计构造好字符串的方案数 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(8, s.countGoodStrings(3, 3, 1, 1)); + Assertions.assertEquals(5, s.countGoodStrings(2, 3, 1, 2)); + } + + static class Solution { + + public int countGoodStrings(int low, int high, int zero, int one) { + final int MOD = 1_000_000_007; + int res = 0; + // dp[i] 表示构造长为 i 的字符串的方案数 + int[] dp = new int[high + 1]; + // 构造空串的方案数为 1 + dp[0] = 1; + for (int i = 1; i <= high; i++) { + if (i >= zero) dp[i] = dp[i - zero]; + if (i >= one) dp[i] = (dp[i] + dp[i - one]) % MOD; + if (i >= low) res = (res + dp[i]) % MOD; + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/array/\350\247\243\345\206\263\346\231\272\345\212\233\351\227\256\351\242\230.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/array/\350\247\243\345\206\263\346\231\272\345\212\233\351\227\256\351\242\230.java" new file mode 100644 index 0000000..5126e91 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/array/\350\247\243\345\206\263\346\231\272\345\212\233\351\227\256\351\242\230.java" @@ -0,0 +1,46 @@ +package io.github.dunwu.algorithm.dp.array; + +import org.junit.jupiter.api.Assertions; + +import java.util.Arrays; + +/** + * 2140. 解决智力问题 + * + * @author Zhang Peng + * @date 2025-11-17 + */ +public class 解决智力问题 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(5, s.mostPoints(new int[][] { { 3, 2 }, { 4, 3 }, { 4, 4 }, { 2, 5 } })); + Assertions.assertEquals(7, s.mostPoints(new int[][] { { 1, 1 }, { 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 } })); + } + + static class Solution { + + long[] memo; + + public long mostPoints(int[][] questions) { + if (questions == null || questions.length == 0) { return 0; } + memo = new long[questions.length + 1]; + Arrays.fill(memo, -1); + return dp(questions, 0); + } + + public long dp(int[][] questions, int i) { + if (i < 0 || i >= questions.length) { return 0L; } + if (memo[i] != -1) { return memo[i]; } + int score = questions[i][0]; + int skip = questions[i][1]; + memo[i] = Math.max( + dp(questions, i + 1), + dp(questions, i + skip + 1) + score + ); + return memo[i]; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/array/\350\247\243\347\240\201\346\226\271\346\263\225.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/array/\350\247\243\347\240\201\346\226\271\346\263\225.java" new file mode 100644 index 0000000..d813925 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/array/\350\247\243\347\240\201\346\226\271\346\263\225.java" @@ -0,0 +1,103 @@ +package io.github.dunwu.algorithm.dp.array; + +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; + +/** + * 91. 解码方法 + * + * @author Zhang Peng + * @since 2025-11-17 + */ +public class 解码方法 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(2, s.numDecodings("12")); + Assertions.assertEquals(3, s.numDecodings("226")); + Assertions.assertEquals(0, s.numDecodings("06")); + + Solution2 s2 = new Solution2(); + Assertions.assertEquals(2, s2.numDecodings("12")); + Assertions.assertEquals(3, s2.numDecodings("226")); + Assertions.assertEquals(0, s2.numDecodings("06")); + } + + // 回溯解法 + static class Solution { + + private StringBuilder sb; + private LinkedList res; + + public int numDecodings(String s) { + sb = new StringBuilder(); + res = new LinkedList<>(); + backtrack(s, 0); + return res.size(); + } + + public void backtrack(String s, int start) { + + // base case,走到叶子节点 + // 即整个 s 被成功分割为合法的四部分,记下答案 + if (start == s.length()) { + res.add(sb.toString()); + return; + } + + for (int i = start; i < s.length(); i++) { + + // s[start..i] 不是合法的 ip 数字,不能分割 + char letter = getLetter(s, start, i); + if (letter == '#') { continue; } + + // 【选择】 + // s[start..i] 是一个合法的 ip 数字,可以进行分割 + // 做选择,把 s[start..i] 放入路径列表中 + sb.append(letter); + + // 【回溯】 + backtrack(s, i + 1); + + // 【取消选择】 + sb.deleteCharAt(sb.length() - 1); + } + } + + public char getLetter(String s, int begin, int end) { + int len = end - begin + 1; + if (len <= 0 || len > 2) { return '#'; } + String numStr = s.substring(begin, begin + len); + if (numStr.startsWith("0")) { return '#'; } + int num = Integer.parseInt(numStr); + if (num < 1 || num > 26) { return '#'; } + return (char) ('A' + (num - 1)); + } + + } + + // 动态规划 + static class Solution2 { + + public int numDecodings(String s) { + int n = s.length(); + s = " " + s; + char[] ch = s.toCharArray(); + int[] dp = new int[n + 1]; + dp[0] = 1; + for (int i = 1; i <= n; i++) { + // a : 代表「当前位置」单独形成 item + // b : 代表「当前位置」与「前一位置」共同形成 item + int a = ch[i] - '0', b = (ch[i - 1] - '0') * 10 + (ch[i] - '0'); + // 如果 a 属于有效值,那么 dp[i] 可以由 dp[i - 1] 转移过来 + if (1 <= a && a <= 9) dp[i] = dp[i - 1]; + // 如果 b 属于有效值,那么 dp[i] 可以由 dp[i - 2] 或者 dp[i - 1] & dp[i - 2] 转移过来 + if (10 <= b && b <= 26) dp[i] += dp[i - 2]; + } + return dp[n]; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/array/\351\233\266\351\222\261\345\205\221\346\215\242.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/array/\351\233\266\351\222\261\345\205\221\346\215\242.java" new file mode 100644 index 0000000..feeb72a --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/array/\351\233\266\351\222\261\345\205\221\346\215\242.java" @@ -0,0 +1,87 @@ +package io.github.dunwu.algorithm.dp.array; + +import org.junit.jupiter.api.Assertions; + +import java.util.Arrays; + +/** + * 322. 零钱兑换 + * + * @author Zhang Peng + * @since 2025-11-17 + */ +public class 零钱兑换 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(3, s.coinChange(new int[] { 1, 2, 5 }, 11)); + Assertions.assertEquals(-1, s.coinChange(new int[] { 2 }, 3)); + Assertions.assertEquals(0, s.coinChange(new int[] { 1 }, 0)); + + Solution2 s2 = new Solution2(); + Assertions.assertEquals(3, s2.coinChange(new int[] { 1, 2, 5 }, 11)); + Assertions.assertEquals(-1, s2.coinChange(new int[] { 2 }, 3)); + Assertions.assertEquals(0, s2.coinChange(new int[] { 1 }, 0)); + } + + static class Solution { + + int[] memo; + + public int coinChange(int[] coins, int amount) { + memo = new int[amount + 1]; + // 备忘录初始化为一个不会被取到的特殊值,代表还未被计算 + Arrays.fill(memo, -666); + + return dp(coins, amount); + } + + int dp(int[] coins, int amount) { + if (amount == 0) return 0; + if (amount < 0) return -1; + // 查备忘录,防止重复计算 + if (memo[amount] != -666) { return memo[amount]; } + + int res = Integer.MAX_VALUE; + for (int coin : coins) { + // 计算子问题的结果 + int subProblem = dp(coins, amount - coin); + + // 子问题无解则跳过 + if (subProblem == -1) continue; + // 在子问题中选择最优解,然后加一 + res = Math.min(res, subProblem + 1); + } + // 把计算结果存入备忘录 + memo[amount] = (res == Integer.MAX_VALUE) ? -1 : res; + return memo[amount]; + } + + } + + static class Solution2 { + + public int coinChange(int[] coins, int amount) { + int[] dp = new int[amount + 1]; + // 数组大小为 amount + 1,初始值也为 amount + 1 + Arrays.fill(dp, amount + 1); + + // base case + dp[0] = 0; + // 外层 for 循环在遍历所有状态的所有取值 + for (int i = 0; i < dp.length; i++) { + // 内层 for 循环在求所有选择的最小值 + for (int coin : coins) { + // 子问题无解,跳过 + if (i - coin < 0) { + continue; + } + dp[i] = Math.min(dp[i], 1 + dp[i - coin]); + } + } + return (dp[amount] == amount + 1) ? -1 : dp[amount]; + } + + } + +} diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/fib/package-info.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/fib/package-info.java new file mode 100644 index 0000000..1e7cb09 --- /dev/null +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/fib/package-info.java @@ -0,0 +1,7 @@ +/** + * 动态规划 - 斐波那契类型 + * + * @author Zhang Peng + * @date 2025-11-12 + */ +package io.github.dunwu.algorithm.dp.fib; \ No newline at end of file diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/fib/\344\275\277\347\224\250\346\234\200\345\260\217\350\212\261\350\264\271\347\210\254\346\245\274\346\242\257.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/fib/\344\275\277\347\224\250\346\234\200\345\260\217\350\212\261\350\264\271\347\210\254\346\245\274\346\242\257.java" new file mode 100644 index 0000000..e0d3ca1 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/fib/\344\275\277\347\224\250\346\234\200\345\260\217\350\212\261\350\264\271\347\210\254\346\245\274\346\242\257.java" @@ -0,0 +1,59 @@ +package io.github.dunwu.algorithm.dp.fib; + +import org.junit.jupiter.api.Assertions; + +/** + * 746. 使用最小花费爬楼梯 + * + * @author Zhang Peng + * @date 2025-11-10 + */ +public class 使用最小花费爬楼梯 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(15, s.minCostClimbingStairs(new int[] { 10, 15, 20 })); + Assertions.assertEquals(6, s.minCostClimbingStairs(new int[] { 1, 100, 1, 1, 1, 100, 1, 1, 100, 1 })); + + Solution2 s2 = new Solution2(); + Assertions.assertEquals(15, s2.minCostClimbingStairs(new int[] { 10, 15, 20 })); + Assertions.assertEquals(6, s2.minCostClimbingStairs(new int[] { 1, 100, 1, 1, 1, 100, 1, 1, 100, 1 })); + } + + static class Solution { + + public int minCostClimbingStairs(int[] cost) { + if (cost == null || cost.length == 0) { return 0; } + int N = cost.length; + int[] dp = new int[N + 1]; + dp[0] = dp[1] = 0; + for (int i = 2; i <= N; i++) { + dp[i] = Math.min( + dp[i - 1] + cost[i - 1], + dp[i - 2] + cost[i - 2] + ); + } + return dp[N]; + } + + } + + static class Solution2 { + + public int minCostClimbingStairs(int[] cost) { + if (cost == null || cost.length == 0) { return 0; } + int pre1 = 0, pre2 = 0; + for (int i = 2; i <= cost.length; i++) { + int tmp = Math.min( + pre1 + cost[i - 1], + pre2 + cost[i - 2] + ); + pre2 = pre1; + pre1 = tmp; + } + return pre1; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/fib/\345\210\240\351\231\244\345\271\266\350\216\267\345\276\227\347\202\271\346\225\260.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/fib/\345\210\240\351\231\244\345\271\266\350\216\267\345\276\227\347\202\271\346\225\260.java" new file mode 100644 index 0000000..2442b5b --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/fib/\345\210\240\351\231\244\345\271\266\350\216\267\345\276\227\347\202\271\346\225\260.java" @@ -0,0 +1,58 @@ +package io.github.dunwu.algorithm.dp.fib; + +import org.junit.jupiter.api.Assertions; + +/** + * 740. 删除并获得点数 + * + * @author Zhang Peng + * @date 2025-11-10 + */ +public class 删除并获得点数 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(6, s.deleteAndEarn(new int[] { 3, 4, 2 })); + // 删除 4 获得 4 个点数,因此 3 也被删除。 + // 之后,删除 2 获得 2 个点数。总共获得 6 个点数。 + Assertions.assertEquals(9, s.deleteAndEarn(new int[] { 2, 2, 3, 3, 3, 4 })); + // 删除 3 获得 3 个点数,接着要删除两个 2 和 4 。 + // 之后,再次删除 3 获得 3 个点数,再次删除 3 获得 3 个点数。 + // 总共获得 9 个点数。 + } + + static class Solution { + + public int deleteAndEarn(int[] nums) { + + if (nums == null || nums.length == 0) { return 0; } + + int n = nums.length; + int max = Integer.MIN_VALUE; + for (int i = 1; i < n; i++) { + max = Math.max(max, nums[i]); + } + + int[] sums = new int[max + 1]; + for (int num : nums) { + sums[num] += num; + } + return rob(sums); + } + + public int rob(int[] nums) { + if (nums == null || nums.length == 0) { return 0; } + if (nums.length == 1) { return nums[0]; } + int N = nums.length; + int first = nums[0], second = Math.max(nums[0], nums[1]); + for (int i = 2; i < N; i++) { + int tmp = Math.max(second, first + nums[i]); + first = second; + second = tmp; + } + return second; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/fib/\346\211\223\345\256\266\345\212\253\350\210\215.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/fib/\346\211\223\345\256\266\345\212\253\350\210\215.java" new file mode 100644 index 0000000..9acda1e --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/fib/\346\211\223\345\256\266\345\212\253\350\210\215.java" @@ -0,0 +1,37 @@ +package io.github.dunwu.algorithm.dp.fib; + +import org.junit.jupiter.api.Assertions; + +/** + * 198. 打家劫舍 + * + * @author Zhang Peng + * @date 2025-11-10 + */ +public class 打家劫舍 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(4, s.rob(new int[] { 1, 2, 3, 1 })); + Assertions.assertEquals(12, s.rob(new int[] { 2, 7, 9, 3, 1 })); + Assertions.assertEquals(1, s.rob(new int[] { 1, 1 })); + } + + static class Solution { + + public int rob(int[] nums) { + if (nums == null || nums.length == 0) { return 0; } + if (nums.length == 1) { return nums[0]; } + int N = nums.length; + int first = nums[0], second = Math.max(nums[0], nums[1]); + for (int i = 2; i < N; i++) { + int tmp = Math.max(second, first + nums[i]); + first = second; + second = tmp; + } + return second; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/fib/\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/fib/\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260.java" new file mode 100644 index 0000000..a2bc7f4 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/fib/\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260.java" @@ -0,0 +1,110 @@ +package io.github.dunwu.algorithm.dp.fib; + +import org.junit.jupiter.api.Assertions; + +import java.util.Arrays; + +/** + * 509. 斐波那契数 + * + * @author Zhang Peng + * @date 2025-11-10 + */ +public class 斐波那契数 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(1, s.fib(2)); + Assertions.assertEquals(2, s.fib(3)); + Assertions.assertEquals(3, s.fib(4)); + + Solution2 s2 = new Solution2(); + Assertions.assertEquals(1, s2.fib(2)); + Assertions.assertEquals(2, s2.fib(3)); + Assertions.assertEquals(3, s2.fib(4)); + + Solution2 s3 = new Solution2(); + Assertions.assertEquals(1, s3.fib(2)); + Assertions.assertEquals(2, s3.fib(3)); + Assertions.assertEquals(3, s3.fib(4)); + } + + /** + * 使用备忘录优化动态规划问题 + */ + static class Solution { + + int fib(int n) { + // 备忘录全初始化为 -1 + // 因为斐波那契数肯定是非负整数,所以初始化为特殊值 -1 表示未计算 + + // 因为数组的索引从 0 开始,所以需要 n + 1 个空间 + // 这样才能把 `f(0) ~ f(n)` 都记录到 memo 中 + int[] memo = new int[n + 1]; + Arrays.fill(memo, -1); + + return dp(memo, n); + } + + // 带着备忘录进行递归 + int dp(int[] memo, int n) { + // base case + if (n == 0 || n == 1) { + return n; + } + // 已经计算过,不用再计算了 + if (memo[n] != -1) { + return memo[n]; + } + // 在返回结果之前,存入备忘录 + memo[n] = dp(memo, n - 1) + dp(memo, n - 2); + return memo[n]; + } + + } + + /** + * DP Table 解决动态规划问题 + */ + static class Solution2 { + + int fib(int n) { + if (n == 0 || n == 1) { + return n; + } + + int[] dp = new int[n + 1]; + dp[0] = 0; + dp[1] = 1; + for (int i = 2; i <= n; i++) { + dp[i] = dp[i - 1] + dp[i - 2]; + } + return dp[n]; + } + + } + + /** + * 在 DP Table 基础上优化空间复杂度 + */ + static class Solution3 { + + int fib(int n) { + if (n == 0 || n == 1) { + return n; + } + + // 分别代表 dp[i - 1] 和 dp[i - 2] + int dp_i_1 = 1, dp_i_2 = 0; + for (int i = 2; i <= n; i++) { + // dp[i] = dp[i - 1] + dp[i - 2]; + int dp_i = dp_i_1 + dp_i_2; + dp_i_2 = dp_i_1; + dp_i_1 = dp_i; + } + return dp_i_1; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/fib/\347\210\254\346\245\274\346\242\257.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/fib/\347\210\254\346\245\274\346\242\257.java" new file mode 100644 index 0000000..2b7f0bf --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/fib/\347\210\254\346\245\274\346\242\257.java" @@ -0,0 +1,69 @@ +package io.github.dunwu.algorithm.dp.fib; + +import org.junit.jupiter.api.Assertions; + +import java.util.Arrays; + +/** + * 70. 爬楼梯 + * + * @author Zhang Peng + * @since 2020-07-04 + */ +public class 爬楼梯 { + + public static void main(String[] args) { + + Solution s = new Solution(); + Assertions.assertEquals(1, s.climbStairs(0)); + Assertions.assertEquals(1, s.climbStairs(1)); + Assertions.assertEquals(2, s.climbStairs(2)); + Assertions.assertEquals(3, s.climbStairs(3)); + + Solution2 s2 = new Solution2(); + Assertions.assertEquals(1, s2.climbStairs(0)); + Assertions.assertEquals(1, s2.climbStairs(1)); + Assertions.assertEquals(2, s2.climbStairs(2)); + Assertions.assertEquals(3, s2.climbStairs(3)); + } + + static class Solution { + + int[] memo = null; + + public int climbStairs(int n) { + memo = new int[n + 1]; + Arrays.fill(memo, -1); + return dp(n); + } + + // 爬第n阶楼梯的方法数量,等于 2 部分之和 + // + // 爬上 n−1 阶楼梯的方法数量。因为再爬1阶就能到第n阶 + // 爬上 n−2 阶楼梯的方法数量,因为再爬2阶就能到第n阶 + public int dp(int n) { + if (n == 0 || n == 1) return 1; + if (memo[n] != -1) return memo[n]; + memo[n] = dp(n - 1) + dp(n - 2); + return memo[n]; + } + + } + + // 空间复杂度 o(1) + static class Solution2 { + + public int climbStairs(int n) { + if (n == 0 || n == 1) return 1; + int pre1 = 1, pre2 = 1; + for (int i = 2; i <= n; i++) { + int tmp = pre1 + pre2; + pre2 = pre1; + pre1 = tmp; + } + return pre1; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/fib/\347\254\254N\344\270\252\346\263\260\346\263\242\351\202\243\345\245\221\346\225\260.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/fib/\347\254\254N\344\270\252\346\263\260\346\263\242\351\202\243\345\245\221\346\225\260.java" new file mode 100644 index 0000000..87a3c49 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/fib/\347\254\254N\344\270\252\346\263\260\346\263\242\351\202\243\345\245\221\346\225\260.java" @@ -0,0 +1,37 @@ +package io.github.dunwu.algorithm.dp.fib; + +import org.junit.jupiter.api.Assertions; + +/** + * 1137. 第 N 个泰波那契数 + * + * @author Zhang Peng + * @date 2025-11-10 + */ +public class 第N个泰波那契数 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(4, s.tribonacci(4)); + Assertions.assertEquals(1389537, s.tribonacci(25)); + } + + static class Solution { + + public int tribonacci(int n) { + if (n == 0) return 0; + if (n == 1 || n == 2) return 1; + + int[] dp = new int[n + 1]; + dp[0] = 0; + dp[1] = 1; + dp[2] = 1; + for (int i = 3; i <= n; i++) { + dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3]; + } + return dp[n]; + } + + } + +} diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/matrix/package-info.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/matrix/package-info.java new file mode 100644 index 0000000..60b8318 --- /dev/null +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/matrix/package-info.java @@ -0,0 +1,7 @@ +/** + * 动态规划 - 矩阵 + * + * @author Zhang Peng + * @date 2025-11-12 + */ +package io.github.dunwu.algorithm.dp.matrix; \ No newline at end of file diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/matrix/\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/matrix/\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214.java" new file mode 100644 index 0000000..9d2e036 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/matrix/\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214.java" @@ -0,0 +1,57 @@ +package io.github.dunwu.algorithm.dp.matrix; + +import io.github.dunwu.algorithm.util.ArrayUtil; +import org.junit.jupiter.api.Assertions; + +import java.util.List; + +/** + * 120. 三角形最小路径和 + * + * @author Zhang Peng + * @since 2020-07-04 + */ +public class 三角形最小路径和 { + + public static void main(String[] args) { + Solution s = new Solution(); + List> input = ArrayUtil.toIntMatrixList(new int[][] { { 2 }, { 3, 4 }, { 6, 5, 7 }, { 4, 1, 8, 3 } }); + Assertions.assertEquals(11, s.minimumTotal(input)); + List> input2 = ArrayUtil.toIntMatrixList(new int[][] { { -10 } }); + Assertions.assertEquals(-10, s.minimumTotal(input2)); + } + + static class Solution { + + public int minimumTotal(List> triangle) { + + // base case + if (triangle == null || triangle.size() == 0) { return 0; } + if (triangle.size() == 1) { return triangle.get(0).get(0); } + + // 状态定义 + int n = triangle.size(); + int[][] dp = new int[n][n]; + + // 初始状态 + dp[0][0] = triangle.get(0).get(0); + + // 状态转移方程 + int min = Integer.MAX_VALUE; + for (int i = 1; i < n; i++) { + dp[i][0] = triangle.get(i).get(0) + dp[i - 1][0]; + for (int j = 1; j < i; j++) { + dp[i][j] = Math.min(dp[i - 1][j], dp[i - 1][j - 1]) + triangle.get(i).get(j); + } + dp[i][i] = dp[i - 1][i - 1] + triangle.get(i).get(i); + } + + for (int j = 0; j < n; j++) { + min = Math.min(min, dp[n - 1][j]); + } + return min; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/matrix/\344\270\213\351\231\215\350\267\257\345\276\204\346\234\200\345\260\217\345\222\214.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/matrix/\344\270\213\351\231\215\350\267\257\345\276\204\346\234\200\345\260\217\345\222\214.java" new file mode 100644 index 0000000..b1965bc --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/matrix/\344\270\213\351\231\215\350\267\257\345\276\204\346\234\200\345\260\217\345\222\214.java" @@ -0,0 +1,58 @@ +package io.github.dunwu.algorithm.dp.matrix; + +import org.junit.jupiter.api.Assertions; + +/** + * 931. 下降路径最小和 + * + * @author Zhang Peng + * @date 2025-11-10 + */ +public class 下降路径最小和 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(13, s.minFallingPathSum(new int[][] { { 2, 1, 3 }, { 6, 5, 4 }, { 7, 8, 9 } })); + Assertions.assertEquals(-59, s.minFallingPathSum(new int[][] { { -19, 57 }, { -40, -5 } })); + } + + static class Solution { + + public int minFallingPathSum(int[][] matrix) { + + // base case + if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { return 0; } + if (matrix.length == 1) { return matrix[0][0]; } + + // 状态定义 + int n = matrix.length; + int[][] dp = new int[n][n]; + + // 初始状态、边界状态 + for (int j = 0; j < n; j++) { + dp[0][j] = matrix[0][j]; + } + + // 状态转移 + for (int i = 1; i < n; i++) { + dp[i][0] = Math.min(dp[i - 1][0], dp[i - 1][1]) + matrix[i][0]; + for (int j = 1; j < n - 1; j++) { + dp[i][j] = min(dp[i - 1][j - 1], dp[i - 1][j], dp[i - 1][j + 1]) + matrix[i][j]; + } + dp[i][n - 1] = Math.min(dp[i - 1][n - 1], dp[i - 1][n - 2]) + matrix[i][n - 1]; + } + + int min = Integer.MAX_VALUE; + for (int j = 0; j < n; j++) { + min = Math.min(min, dp[n - 1][j]); + } + return min; + } + + public int min(int a, int b, int c) { + return Math.min(a, Math.min(b, c)); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/matrix/\344\270\215\345\220\214\350\267\257\345\276\204.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/matrix/\344\270\215\345\220\214\350\267\257\345\276\204.java" new file mode 100644 index 0000000..aa700cf --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/matrix/\344\270\215\345\220\214\350\267\257\345\276\204.java" @@ -0,0 +1,41 @@ +package io.github.dunwu.algorithm.dp.matrix; + +import org.junit.jupiter.api.Assertions; + +/** + * 62. 不同路径 + * + * @author Zhang Peng + * @date 2025-11-12 + */ +public class 不同路径 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(28, s.uniquePaths(3, 7)); + Assertions.assertEquals(3, s.uniquePaths(3, 2)); + } + + static class Solution { + + public int uniquePaths(int m, int n) { + + // 状态定义 + int[][] dp = new int[m][n]; + + // 初始状态、边界状态 + for (int i = 0; i < m; i++) { dp[i][0] = 1; } + for (int j = 0; j < n; j++) { dp[0][j] = 1; } + + // 状态转移方程 + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; + } + } + return dp[m - 1][n - 1]; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/matrix/\344\270\215\345\220\214\350\267\257\345\276\2042.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/matrix/\344\270\215\345\220\214\350\267\257\345\276\2042.java" new file mode 100644 index 0000000..50295cf --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/matrix/\344\270\215\345\220\214\350\267\257\345\276\2042.java" @@ -0,0 +1,58 @@ +package io.github.dunwu.algorithm.dp.matrix; + +import org.junit.jupiter.api.Assertions; + +/** + * 63. 不同路径 II + * + * @author Zhang Peng + * @date 2025-11-12 + */ +public class 不同路径2 { + + public static void main(String[] args) { + Solution s = new Solution(); + int[][] input1 = new int[][] { { 0, 0, 0 }, { 0, 1, 0 }, { 0, 0, 0 } }; + Assertions.assertEquals(2, s.uniquePathsWithObstacles(input1)); + int[][] input2 = new int[][] { { 0, 1 }, { 0, 0 } }; + Assertions.assertEquals(1, s.uniquePathsWithObstacles(input2)); + int[][] input3 = new int[][] { { 1, 0 } }; + Assertions.assertEquals(0, s.uniquePathsWithObstacles(input3)); + int[][] input4 = new int[][] { { 0, 1, 0, 0 } }; + Assertions.assertEquals(0, s.uniquePathsWithObstacles(input4)); + } + + static class Solution { + + public int uniquePathsWithObstacles(int[][] obstacleGrid) { + + // base case + if (obstacleGrid == null || obstacleGrid.length == 0) { return 0; } + int m = obstacleGrid.length, n = obstacleGrid[0].length; + // 起点、终点有障碍,注定无法到达 + if (obstacleGrid[0][0] == 1 || obstacleGrid[m - 1][n - 1] == 1) { return 0; } + + // 状态定义 + int[][] dp = new int[m][n]; + + // 初始状态、边界状态 + dp[0][0] = 1; + for (int i = 1; i < m; i++) { dp[i][0] = (obstacleGrid[i][0] == 1) ? 0 : dp[i - 1][0]; } + for (int j = 1; j < n; j++) { dp[0][j] = (obstacleGrid[0][j] == 1) ? 0 : dp[0][j - 1]; } + + // 状态转移方程 + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + if (obstacleGrid[i][j] == 1) { + dp[i][j] = 0; + } else { + dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; + } + } + } + return dp[m - 1][n - 1]; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/matrix/\346\234\200\345\244\247\346\255\243\346\226\271\345\275\242.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/matrix/\346\234\200\345\244\247\346\255\243\346\226\271\345\275\242.java" new file mode 100644 index 0000000..2d4926a --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/matrix/\346\234\200\345\244\247\346\255\243\346\226\271\345\275\242.java" @@ -0,0 +1,80 @@ +package io.github.dunwu.algorithm.dp.matrix; + +import org.junit.jupiter.api.Assertions; + +/** + * 221. 最大正方形 + * + * @author Zhang Peng + * @date 2025-11-10 + */ +public class 最大正方形 { + + public static void main(String[] args) { + + Solution s = new Solution(); + + char[][] input1 = new char[][] { + { '1', '0', '1', '0', '0' }, + { '1', '0', '1', '1', '1' }, + { '1', '1', '1', '1', '1' }, + { '1', '0', '0', '1', '0' } + }; + Assertions.assertEquals(4, s.maximalSquare(input1)); + + char[][] input2 = new char[][] { { '0', '1' }, { '1', '0' } }; + Assertions.assertEquals(1, s.maximalSquare(input2)); + + char[][] input3 = new char[][] { { '0' } }; + Assertions.assertEquals(0, s.maximalSquare(input3)); + + char[][] input4 = new char[][] { + { '1', '0', '1', '1', '0', '1' }, + { '1', '1', '1', '1', '1', '1' }, + { '0', '1', '1', '0', '1', '1' }, + { '1', '1', '1', '0', '1', '0' }, + { '0', '1', '1', '1', '1', '1' }, + { '1', '1', '0', '1', '1', '1' } + }; + Assertions.assertEquals(4, s.maximalSquare(input4)); + } + + static class Solution { + + public int maximalSquare(char[][] matrix) { + + // base case + if (matrix == null || matrix.length == 0 || matrix[0] == null || matrix[0].length == 0) { return 0; } + + // 状态定义 + int m = matrix.length, n = matrix[0].length; + int[][] dp = new int[m][n]; + + // 状态转移方程 + int max = 0; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (i == 0 || j == 0) { + dp[i][j] = matrix[i][j] == '1' ? 1 : 0; + } else { + if (matrix[i][j] == '1') { + dp[i][j] = min( + dp[i - 1][j], + dp[i][j - 1], + dp[i - 1][j - 1] + ) + 1; + } + } + max = Math.max(dp[i][j], max); + } + } + return max * max; + } + + public int min(int a, int b, int c) { + return Math.min(Math.min(a, b), c); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/matrix/\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/matrix/\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214.java" new file mode 100644 index 0000000..b20d79b --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/matrix/\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214.java" @@ -0,0 +1,38 @@ +package io.github.dunwu.algorithm.dp.matrix; + +import org.junit.jupiter.api.Assertions; + +/** + * 64. 最小路径和 + * + * @author Zhang Peng + * @date 2025-11-10 + */ +public class 最小路径和 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(7, s.minPathSum(new int[][] { { 1, 3, 1 }, { 1, 5, 1 }, { 4, 2, 1 } })); + Assertions.assertEquals(12, s.minPathSum(new int[][] { { 1, 2, 3 }, { 4, 5, 6 } })); + } + + static class Solution { + + public int minPathSum(int[][] grid) { + if (grid == null || grid.length == 0 || grid[0].length == 0) { return 0; } + int m = grid.length, n = grid[0].length; + int[][] dp = new int[m][n]; + dp[0][0] = grid[0][0]; + for (int i = 1; i < m; i++) { dp[i][0] = dp[i - 1][0] + grid[i][0]; } + for (int j = 1; j < n; j++) { dp[0][j] = dp[0][j - 1] + grid[0][j]; } + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + dp[i][j] = Math.min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]; + } + } + return dp[m - 1][n - 1]; + } + + } + +} diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/package-info.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/package-info.java similarity index 72% rename from codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/package-info.java rename to codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/package-info.java index 567166a..48674a2 100644 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/package-info.java +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/package-info.java @@ -4,4 +4,4 @@ * @author Zhang Peng * @since 2020-03-06 */ -package io.github.dunwu.algorithm.dynamic; +package io.github.dunwu.algorithm.dp; diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/state/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/state/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272.java" new file mode 100644 index 0000000..af5cc5f --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/state/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272.java" @@ -0,0 +1,36 @@ +package io.github.dunwu.algorithm.dp.state; + +import org.junit.jupiter.api.Assertions; + +/** + * 121. 买卖股票的最佳时机 + * + * @author Zhang Peng + * @since 2020-07-05 + */ +public class 买卖股票的最佳时机 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(5, s.maxProfit(new int[] { 7, 1, 5, 3, 6, 4 })); + Assertions.assertEquals(0, s.maxProfit(new int[] { 7, 6, 4, 3, 1 })); + } + + static class Solution { + + public int maxProfit(int[] prices) { + int min = prices[0]; + int max = 0; + for (int i = 1; i < prices.length; i++) { + if (prices[i] <= min) { + min = prices[i]; + } else { + max = Math.max(max, prices[i] - min); + } + } + return max; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272II.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/state/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\2722.java" similarity index 81% rename from "codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272II.java" rename to "codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/state/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\2722.java" index 821d79a..98b90e7 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272II.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/state/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\2722.java" @@ -1,13 +1,14 @@ -package io.github.dunwu.algorithm.dynamic; +package io.github.dunwu.algorithm.dp.state; import org.junit.jupiter.api.Assertions; /** + * 122. 买卖股票的最佳时机 II + * * @author Zhang Peng - * @see 122. 买卖股票的最佳时机 II * @since 2020-07-05 */ -public class 买卖股票的最佳时机II { +public class 买卖股票的最佳时机2 { public static void main(String[] args) { int[] prices = { 7, 1, 5, 3, 6, 4 }; diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272III.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/state/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272III.java" similarity index 97% rename from "codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272III.java" rename to "codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/state/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272III.java" index e53c7e8..916188a 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272III.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/state/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272III.java" @@ -1,4 +1,4 @@ -package io.github.dunwu.algorithm.dynamic; +package io.github.dunwu.algorithm.dp.state; import org.junit.jupiter.api.Assertions; diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272IV.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/state/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272IV.java" similarity index 97% rename from "codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272IV.java" rename to "codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/state/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272IV.java" index 63bab9e..f2bb072 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272IV.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/state/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272IV.java" @@ -1,4 +1,4 @@ -package io.github.dunwu.algorithm.dynamic; +package io.github.dunwu.algorithm.dp.state; import org.junit.jupiter.api.Assertions; diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272\345\220\253\346\211\213\347\273\255\350\264\271.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/state/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272\345\220\253\346\211\213\347\273\255\350\264\271.java" similarity index 96% rename from "codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272\345\220\253\346\211\213\347\273\255\350\264\271.java" rename to "codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/state/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272\345\220\253\346\211\213\347\273\255\350\264\271.java" index 89d7b43..134b337 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272\345\220\253\346\211\213\347\273\255\350\264\271.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/state/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272\345\220\253\346\211\213\347\273\255\350\264\271.java" @@ -1,4 +1,4 @@ -package io.github.dunwu.algorithm.dynamic; +package io.github.dunwu.algorithm.dp.state; import org.junit.jupiter.api.Assertions; diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\346\234\200\344\275\263\344\271\260\345\215\226\350\202\241\347\245\250\346\227\266\346\234\272\345\220\253\345\206\267\345\206\273\346\234\237.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/state/\346\234\200\344\275\263\344\271\260\345\215\226\350\202\241\347\245\250\346\227\266\346\234\272\345\220\253\345\206\267\345\206\273\346\234\237.java" similarity index 96% rename from "codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\346\234\200\344\275\263\344\271\260\345\215\226\350\202\241\347\245\250\346\227\266\346\234\272\345\220\253\345\206\267\345\206\273\346\234\237.java" rename to "codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/state/\346\234\200\344\275\263\344\271\260\345\215\226\350\202\241\347\245\250\346\227\266\346\234\272\345\220\253\345\206\267\345\206\273\346\234\237.java" index a06adee..9836d29 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\346\234\200\344\275\263\344\271\260\345\215\226\350\202\241\347\245\250\346\227\266\346\234\272\345\220\253\345\206\267\345\206\273\346\234\237.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/state/\346\234\200\344\275\263\344\271\260\345\215\226\350\202\241\347\245\250\346\227\266\346\234\272\345\220\253\345\206\267\345\206\273\346\234\237.java" @@ -1,4 +1,4 @@ -package io.github.dunwu.algorithm.dynamic; +package io.github.dunwu.algorithm.dp.state; import org.junit.jupiter.api.Assertions; diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/str/\344\270\244\344\270\252\345\255\227\347\254\246\344\270\262\347\232\204\345\210\240\351\231\244\346\223\215\344\275\234.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/str/\344\270\244\344\270\252\345\255\227\347\254\246\344\270\262\347\232\204\345\210\240\351\231\244\346\223\215\344\275\234.java" new file mode 100644 index 0000000..a3df26e --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/str/\344\270\244\344\270\252\345\255\227\347\254\246\344\270\262\347\232\204\345\210\240\351\231\244\346\223\215\344\275\234.java" @@ -0,0 +1,57 @@ +package io.github.dunwu.algorithm.dp.str; + +import org.junit.jupiter.api.Assertions; + +import java.util.Arrays; + +/** + * 583. 两个字符串的删除操作 + * + * @author Zhang Peng + * @date 2025-11-10 + */ +public class 两个字符串的删除操作 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(2, s.minDistance("sea", "eat")); + } + + static class Solution { + + public int minDistance(String word1, String word2) { + int lcs = longestCommonSubsequence(word1, word2); + return word1.length() + word2.length() - lcs - lcs; + } + + public int longestCommonSubsequence(String text1, String text2) { + int[][] memo = new int[text1.length()][text2.length()]; + for (int i = 0; i < text1.length(); i++) { + Arrays.fill(memo[i], -1); + } + return dp(memo, text1, 0, text2, 0); + } + + public int dp(int[][] memo, String text1, int i, String text2, int j) { + if (i < 0 || j < 0 || i >= text1.length() || j >= text2.length()) { return 0; } + if (memo[i][j] != -1) { return memo[i][j]; } + + if (text1.charAt(i) == text2.charAt(j)) { + memo[i][j] = 1 + dp(memo, text1, i + 1, text2, j + 1); + } else { + memo[i][j] = max( + dp(memo, text1, i + 1, text2, j), + dp(memo, text1, i, text2, j + 1), + dp(memo, text1, i + 1, text2, j + 1) + ); + } + return memo[i][j]; + } + + public int max(int a, int b, int c) { + return Math.max(a, Math.max(b, c)); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/str/\344\270\244\344\270\252\345\255\227\347\254\246\344\270\262\347\232\204\346\234\200\345\260\217ASCII\345\210\240\351\231\244\345\222\214.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/str/\344\270\244\344\270\252\345\255\227\347\254\246\344\270\262\347\232\204\346\234\200\345\260\217ASCII\345\210\240\351\231\244\345\222\214.java" new file mode 100644 index 0000000..18a5317 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/str/\344\270\244\344\270\252\345\255\227\347\254\246\344\270\262\347\232\204\346\234\200\345\260\217ASCII\345\210\240\351\231\244\345\222\214.java" @@ -0,0 +1,46 @@ +package io.github.dunwu.algorithm.dp.str; + +import org.junit.jupiter.api.Assertions; + +/** + * 712. 两个字符串的最小ASCII删除和 + * + * @author Zhang Peng + * @since 2020-07-06 + */ +public class 两个字符串的最小ASCII删除和 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(231, s.minimumDeleteSum("sea", "eat")); + Assertions.assertEquals(403, s.minimumDeleteSum("delete", "leet")); + } + + static class Solution { + + public int minimumDeleteSum(String s1, String s2) { + int m = s1.length(), n = s2.length(); + int[][] dp = new int[m + 1][n + 1]; + for (int i = 1; i <= m; i++) { + dp[i][0] = dp[i - 1][0] + s1.codePointAt(i - 1); + } + for (int j = 1; j <= n; j++) { + dp[0][j] = dp[0][j - 1] + s2.codePointAt(j - 1); + } + for (int i = 1; i <= m; i++) { + int code1 = s1.codePointAt(i - 1); + for (int j = 1; j <= n; j++) { + int code2 = s2.codePointAt(j - 1); + if (code1 == code2) { + dp[i][j] = dp[i - 1][j - 1]; + } else { + dp[i][j] = Math.min(dp[i - 1][j] + code1, dp[i][j - 1] + code2); + } + } + } + return dp[m][n]; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/str/\345\215\225\350\257\215\346\213\206\345\210\206.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/str/\345\215\225\350\257\215\346\213\206\345\210\206.java" new file mode 100644 index 0000000..97e11ea --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/str/\345\215\225\350\257\215\346\213\206\345\210\206.java" @@ -0,0 +1,112 @@ +package io.github.dunwu.algorithm.dp.str; + +import org.junit.jupiter.api.Assertions; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; + +/** + * 139. 单词拆分 + * + * @author Zhang Peng + * @date 2025-11-10 + */ +public class 单词拆分 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertTrue(s.wordBreak("leetcode", Arrays.asList("leet", "code"))); + Assertions.assertTrue(s.wordBreak("applepenapple", Arrays.asList("apple", "pen"))); + Assertions.assertFalse(s.wordBreak("catsandog", Arrays.asList("cats", "dog", "sand", "and", "cat"))); + + Solution2 s2 = new Solution2(); + Assertions.assertTrue(s2.wordBreak("leetcode", Arrays.asList("leet", "code"))); + Assertions.assertTrue(s2.wordBreak("applepenapple", Arrays.asList("apple", "pen"))); + Assertions.assertFalse(s2.wordBreak("catsandog", Arrays.asList("cats", "dog", "sand", "and", "cat"))); + } + + // 回溯解决方案 + static class Solution { + + // 记录是否找到一个合法的答案 + boolean found = false; + // 记录回溯算法的路径 + private LinkedList path; + + public boolean wordBreak(String s, List wordDict) { + found = false; + path = new LinkedList<>(); + backtrack(wordDict, s, 0); + return found; + } + + public void backtrack(List wordDict, String target, int start) { + + // 找到一个合法答案 + if (start == target.length()) { found = true; } + // 如果已经找到答案,就不要再递归搜索了 + if (found) { return; } + + // 回溯算法框架 + for (String word : wordDict) { + + int len = word.length(); + + // 无效情况,剪枝 + if (start + len > target.length()) { return; } + if (!target.substring(start, start + len).equals(word)) { continue; } + + // 【选择】 + path.add(word); + // 【回溯】 + backtrack(wordDict, target, start + len); + // 【取消选择】 + path.remove(path.size() - 1); + } + } + + } + + static class Solution2 { + + // 备忘录,-1 代表未计算,0 代表无法凑出,1 代表可以凑出 + private int[] memo; + // 用哈希集合方便快速判断是否存在 + HashSet wordDict; + + public boolean wordBreak(String s, List wordDict) { + this.wordDict = new HashSet<>(wordDict); + this.memo = new int[s.length()]; + Arrays.fill(memo, -1); + return dp(s, 0); + } + + public boolean dp(String s, int index) { + // base case + if (index == s.length()) { return true; } + // 避免冗余 + if (memo[index] != -1) { return memo[index] == 0 ? false : true; } + + // 遍历 s[i..] 的所有前缀 + for (int len = 1; index + len <= s.length(); len++) { + // 看看哪些前缀存在 wordDict 中 + String prefix = s.substring(index, index + len); + if (wordDict.contains(prefix)) { + // 找到一个单词匹配 s[i..i+len) + // 只要 s[i+len..] 可以被拼出,s[i..] 就能被拼出 + if (dp(s, index + len)) { + memo[index] = 1; + return true; + } + } + } + // s[i..] 无法被拼出 + memo[index] = 0; + return false; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/str/\346\234\200\351\225\277\345\233\236\346\226\207\345\255\220\345\272\217\345\210\227.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/str/\346\234\200\351\225\277\345\233\236\346\226\207\345\255\220\345\272\217\345\210\227.java" new file mode 100644 index 0000000..4104ba8 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/str/\346\234\200\351\225\277\345\233\236\346\226\207\345\255\220\345\272\217\345\210\227.java" @@ -0,0 +1,39 @@ +package io.github.dunwu.algorithm.dp.str; + +import org.junit.jupiter.api.Assertions; + +/** + * 516. 最长回文子序列 + * + * @author Zhang Peng + * @date 2025-11-10 + */ +public class 最长回文子序列 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(4, s.longestPalindromeSubseq("bbbab")); + Assertions.assertEquals(2, s.longestPalindromeSubseq("v")); + } + + static class Solution { + + public int longestPalindromeSubseq(String s) { + int n = s.length(); + int[][] dp = new int[n][n]; + for (int i = n - 1; i >= 0; i--) { + dp[i][i] = 1; + for (int j = i + 1; j < n; j++) { + if (s.charAt(i) == s.charAt(j)) { + dp[i][j] = dp[i + 1][j - 1] + 2; + } else { + dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]); + } + } + } + return dp[0][n - 1]; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/str/\347\274\226\350\276\221\350\267\235\347\246\273.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/str/\347\274\226\350\276\221\350\267\235\347\246\273.java" new file mode 100644 index 0000000..2dc83a4 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/str/\347\274\226\350\276\221\350\267\235\347\246\273.java" @@ -0,0 +1,57 @@ +package io.github.dunwu.algorithm.dp.str; + +import org.junit.jupiter.api.Assertions; + +import java.util.Arrays; + +/** + * 72. 编辑距离 + * + * @author Zhang Peng + * @since 2020-07-06 + */ +public class 编辑距离 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(3, s.minDistance("horse", "ros")); + Assertions.assertEquals(5, s.minDistance("intention", "execution")); + } + + static class Solution { + + int[][] memo; + + public int minDistance(String word1, String word2) { + memo = new int[word1.length()][word2.length()]; + for (int i = 0; i < word1.length(); i++) { + Arrays.fill(memo[i], Integer.MAX_VALUE); + } + return dp(word1, 0, word2, 0); + } + + public int dp(String word1, int i, String word2, int j) { + if (i >= word1.length()) { return word2.length() - j; } + if (j >= word2.length()) { return word1.length() - i; } + if (memo[i][j] != Integer.MAX_VALUE) { + return memo[i][j]; + } + if (word1.charAt(i) == word2.charAt(j)) { + memo[i][j] = dp(word1, i + 1, word2, j + 1); + } else { + memo[i][j] = min( + dp(word1, i + 1, word2, j), + dp(word1, i, word2, j + 1), + dp(word1, i + 1, word2, j + 1) + ) + 1; + } + return memo[i][j]; + } + + public int min(int a, int b, int c) { + return Math.min(a, Math.min(b, c)); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/subseq/\344\270\215\347\233\270\344\272\244\347\232\204\347\272\277.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/subseq/\344\270\215\347\233\270\344\272\244\347\232\204\347\272\277.java" new file mode 100644 index 0000000..290ae9d --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/subseq/\344\270\215\347\233\270\344\272\244\347\232\204\347\272\277.java" @@ -0,0 +1,53 @@ +package io.github.dunwu.algorithm.dp.subseq; + +import org.junit.jupiter.api.Assertions; + +import java.util.Arrays; + +/** + * 300. 最长递增子序列 + * + * @author Zhang Peng + * @date 2025-11-10 + */ +public class 不相交的线 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(2, s.maxUncrossedLines(new int[] { 1, 4, 2 }, new int[] { 1, 2, 4 })); + } + + static class Solution { + + int[][] memo; + + public int maxUncrossedLines(int[] nums1, int[] nums2) { + memo = new int[nums1.length + 1][nums2.length + 1]; + for (int i = 0; i <= nums1.length; i++) { + Arrays.fill(memo[i], -1); + } + return dp(nums1, 0, nums2, 0); + } + + public int dp(int[] nums1, int i, int[] nums2, int j) { + if (i < 0 || i >= nums1.length || j < 0 || j >= nums2.length) { return 0; } + if (memo[i][j] != -1) { return memo[i][j]; } + if (nums1[i] == nums2[j]) { + memo[i][j] = dp(nums1, i + 1, nums2, j + 1) + 1; + } else { + memo[i][j] = max( + dp(nums1, i, nums2, j + 1), + dp(nums1, i + 1, nums2, j), + dp(nums1, i + 1, nums2, j + 1) + ); + } + return memo[i][j]; + } + + public int max(int a, int b, int c) { + return Math.max(a, Math.max(b, c)); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/subseq/\345\210\244\346\226\255\345\255\220\345\272\217\345\210\227.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/subseq/\345\210\244\346\226\255\345\255\220\345\272\217\345\210\227.java" new file mode 100644 index 0000000..ad63c86 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/subseq/\345\210\244\346\226\255\345\255\220\345\272\217\345\210\227.java" @@ -0,0 +1,52 @@ +package io.github.dunwu.algorithm.dp.subseq; + +import org.junit.jupiter.api.Assertions; + +/** + * 392. 判断子序列 + * + * @author Zhang Peng + * @since 2020-07-06 + */ +public class 判断子序列 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertTrue(s.isSubsequence("abc", "ahbgdc")); + Assertions.assertFalse(s.isSubsequence("axc", "ahbgdc")); + Assertions.assertTrue(s.isSubsequence("", "ahbgdc")); + Assertions.assertFalse(s.isSubsequence("aaaaaa", "bbaaaa")); + } + + static class Solution { + + public boolean isSubsequence(String s, String t) { + int m = s.length(), n = t.length(); + + // dp[i][j] 表示 s 的前 i 个字符是否是 t 的前 j 个字符的子序列 + boolean[][] dp = new boolean[m + 1][n + 1]; + + // 初始化:空字符串是任何字符串的子序列 + for (int j = 0; j <= n; j++) { + dp[0][j] = true; + } + + // 动态规划填表 + for (int i = 1; i <= m; i++) { + for (int j = 1; j <= n; j++) { + if (s.charAt(i - 1) == t.charAt(j - 1)) { + // 字符匹配,取决于前一个状态 + dp[i][j] = dp[i - 1][j - 1]; + } else { + // 字符不匹配,只能尝试在 t 中继续寻找 + dp[i][j] = dp[i][j - 1]; + } + } + } + + return dp[m][n]; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\346\234\200\345\244\247\345\255\220\345\272\217\345\222\214.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/subseq/\346\234\200\345\244\247\345\255\220\345\272\217\345\222\214.java" similarity index 86% rename from "codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\346\234\200\345\244\247\345\255\220\345\272\217\345\222\214.java" rename to "codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/subseq/\346\234\200\345\244\247\345\255\220\345\272\217\345\222\214.java" index b5cb9e1..22b2183 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\346\234\200\345\244\247\345\255\220\345\272\217\345\222\214.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/subseq/\346\234\200\345\244\247\345\255\220\345\272\217\345\222\214.java" @@ -1,10 +1,11 @@ -package io.github.dunwu.algorithm.dynamic; +package io.github.dunwu.algorithm.dp.subseq; import org.junit.jupiter.api.Assertions; /** + * 53. 最大子序和 + * * @author Zhang Peng - * @see 53. 最大子序和 * @since 2020-07-06 */ public class 最大子序和 { diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\346\234\200\351\225\277\344\270\212\345\215\207\345\255\220\345\272\217\345\210\227.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/subseq/\346\234\200\351\225\277\344\270\212\345\215\207\345\255\220\345\272\217\345\210\227.java" similarity index 84% rename from "codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\346\234\200\351\225\277\344\270\212\345\215\207\345\255\220\345\272\217\345\210\227.java" rename to "codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/subseq/\346\234\200\351\225\277\344\270\212\345\215\207\345\255\220\345\272\217\345\210\227.java" index 1aef32a..d7ff0fe 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\346\234\200\351\225\277\344\270\212\345\215\207\345\255\220\345\272\217\345\210\227.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/subseq/\346\234\200\351\225\277\344\270\212\345\215\207\345\255\220\345\272\217\345\210\227.java" @@ -1,10 +1,11 @@ -package io.github.dunwu.algorithm.dynamic; +package io.github.dunwu.algorithm.dp.subseq; import org.junit.jupiter.api.Assertions; /** + * 300. 最长上升子序列 + * * @author Zhang Peng - * @see 300. 最长上升子序列 * @since 2020-07-06 */ public class 最长上升子序列 { diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/subseq/\346\234\200\351\225\277\345\205\254\345\205\261\345\255\220\345\272\217\345\210\227.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/subseq/\346\234\200\351\225\277\345\205\254\345\205\261\345\255\220\345\272\217\345\210\227.java" new file mode 100644 index 0000000..cf7c08e --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/subseq/\346\234\200\351\225\277\345\205\254\345\205\261\345\255\220\345\272\217\345\210\227.java" @@ -0,0 +1,56 @@ +package io.github.dunwu.algorithm.dp.subseq; + +import org.junit.jupiter.api.Assertions; + +import java.util.Arrays; + +/** + * 1143. 最长公共子序列 + * + * @author Zhang Peng + * @date 2025-11-10 + */ +public class 最长公共子序列 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(3, s.longestCommonSubsequence("abcde", "ace")); + Assertions.assertEquals(3, s.longestCommonSubsequence("abc", "abc")); + Assertions.assertEquals(0, s.longestCommonSubsequence("abc", "def")); + } + + static class Solution { + + private int[][] memo; + + public int longestCommonSubsequence(String text1, String text2) { + int m = text1.length(), n = text2.length(); + memo = new int[m + 1][n + 1]; + for (int i = 0; i <= m; i++) { + Arrays.fill(memo[i], -1); + } + return dp(text1, 0, text2, 0); + } + + public int dp(String text1, int i, String text2, int j) { + if (i < 0 || i >= text1.length() || j < 0 || j >= text2.length()) { return 0; } + if (memo[i][j] != -1) { return memo[i][j]; } + if (text1.charAt(i) == text2.charAt(j)) { + memo[i][j] = dp(text1, i + 1, text2, j + 1) + 1; + } else { + memo[i][j] = max( + dp(text1, i + 1, text2, j), + dp(text1, i, text2, j + 1), + dp(text1, i + 1, text2, j + 1) + ); + } + return memo[i][j]; + } + + public int max(int a, int b, int c) { + return Math.max(a, Math.max(b, c)); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/subseq/\346\234\200\351\225\277\345\256\232\345\267\256\345\255\220\345\272\217\345\210\227.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/subseq/\346\234\200\351\225\277\345\256\232\345\267\256\345\255\220\345\272\217\345\210\227.java" new file mode 100644 index 0000000..d7ae1d4 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/subseq/\346\234\200\351\225\277\345\256\232\345\267\256\345\255\220\345\272\217\345\210\227.java" @@ -0,0 +1,64 @@ +package io.github.dunwu.algorithm.dp.subseq; + +import org.junit.jupiter.api.Assertions; + +import java.util.HashMap; +import java.util.Map; + +/** + * 1218. 最长定差子序列 + * + * @author Zhang Peng + * @date 2025-11-14 + */ +public class 最长定差子序列 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(4, s.longestSubsequence(new int[] { 1, 2, 3, 4 }, 1)); + Assertions.assertEquals(1, s.longestSubsequence(new int[] { 1, 3, 5, 7 }, 1)); + Assertions.assertEquals(4, s.longestSubsequence(new int[] { 1, 5, 7, 8, 5, 3, 4, 2, 1 }, -2)); + Assertions.assertEquals(2, s.longestSubsequence(new int[] { 3, 4, -3, -2, -4 }, -5)); + + Solution2 s2 = new Solution2(); + Assertions.assertEquals(4, s2.longestSubsequence(new int[] { 1, 2, 3, 4 }, 1)); + Assertions.assertEquals(1, s2.longestSubsequence(new int[] { 1, 3, 5, 7 }, 1)); + Assertions.assertEquals(4, s2.longestSubsequence(new int[] { 1, 5, 7, 8, 5, 3, 4, 2, 1 }, -2)); + Assertions.assertEquals(2, s2.longestSubsequence(new int[] { 3, 4, -3, -2, -4 }, -5)); + } + + static class Solution { + + public int longestSubsequence(int[] arr, int diff) { + int n = arr.length; + Map map = new HashMap<>(); + int[][] dp = new int[n][2]; + dp[0][1] = 1; + map.put(arr[0], 0); + for (int i = 1; i < n; i++) { + dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1]); + dp[i][1] = 1; + int prev = arr[i] - diff; + if (map.containsKey(prev)) dp[i][1] = Math.max(dp[i][1], dp[map.get(prev)][1] + 1); + map.put(arr[i], i); + } + return Math.max(dp[n - 1][0], dp[n - 1][1]); + } + + } + + static class Solution2 { + + public int longestSubsequence(int[] arr, int diff) { + int res = 1; + Map map = new HashMap<>(); + for (int val : arr) { + map.put(val, map.getOrDefault(val - diff, 0) + 1); + res = Math.max(res, map.get(val)); + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/subseq/\346\234\200\351\225\277\346\225\260\345\257\271\351\223\276.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/subseq/\346\234\200\351\225\277\346\225\260\345\257\271\351\223\276.java" new file mode 100644 index 0000000..ae2ef0d --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/subseq/\346\234\200\351\225\277\346\225\260\345\257\271\351\223\276.java" @@ -0,0 +1,44 @@ +package io.github.dunwu.algorithm.dp.subseq; + +import org.junit.jupiter.api.Assertions; + +import java.util.Arrays; +import java.util.Comparator; + +/** + * 646. 最长数对链 + * + * @author Zhang Peng + * @date 2025-11-14 + */ +public class 最长数对链 { + + public static void main(String[] args) { + Solution s = new Solution(); + int[][] input1 = new int[][] { { 1, 2 }, { 2, 3 }, { 3, 4 } }; + Assertions.assertEquals(2, s.findLongestChain(input1)); + + int[][] input2 = new int[][] { { 1, 2 }, { 7, 8 }, { 4, 5 } }; + Assertions.assertEquals(3, s.findLongestChain(input2)); + } + + static class Solution { + + public int findLongestChain(int[][] pairs) { + Arrays.sort(pairs, Comparator.comparingInt(pair -> pair[0])); + int n = pairs.length; + int[] dp = new int[n]; + for (int i = 0; i < n; i++) { + dp[i] = 1; + for (int j = 0; j < i; j++) { + if (pairs[i][0] > pairs[j][1]) { + dp[i] = Math.max(dp[i], dp[j] + 1); + } + } + } + return dp[n - 1]; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/subseq/\346\234\200\351\225\277\347\255\211\345\267\256\346\225\260\345\210\227.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/subseq/\346\234\200\351\225\277\347\255\211\345\267\256\346\225\260\345\210\227.java" new file mode 100644 index 0000000..e0b1b49 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/subseq/\346\234\200\351\225\277\347\255\211\345\267\256\346\225\260\345\210\227.java" @@ -0,0 +1,52 @@ +package io.github.dunwu.algorithm.dp.subseq; + +import org.junit.jupiter.api.Assertions; + +import java.util.HashMap; +import java.util.Map; + +/** + * 1027. 最长等差数列 + * + * @author Zhang Peng + * @date 2025-11-10 + */ +public class 最长等差数列 { + + public static void main(String[] args) { + Solution s = new Solution(); + // Assertions.assertEquals(4, s.longestArithSeqLength(new int[] { 3, 6, 9, 12 })); + // Assertions.assertEquals(3, s.longestArithSeqLength(new int[] { 9, 4, 7, 2, 10 })); + Assertions.assertEquals(4, s.longestArithSeqLength(new int[] { 20, 1, 15, 3, 10, 5, 8 })); + } + + static class Solution { + + public int longestArithSeqLength(int[] nums) { + int min = Integer.MAX_VALUE, max = Integer.MIN_VALUE; + for (int num : nums) { + min = Math.min(min, num); + max = Math.max(max, num); + } + + int res = 1; + int maxDiff = max - min; + for (int diff = -maxDiff; diff <= maxDiff; diff++) { + res = Math.max(longestSubsequence(nums, diff), res); + } + return res; + } + + public int longestSubsequence(int[] arr, int diff) { + int res = 1; + Map map = new HashMap<>(); + for (int val : arr) { + map.put(val, map.getOrDefault(val - diff, 0) + 1); + res = Math.max(res, map.get(val)); + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/subseq/\346\234\200\351\225\277\351\200\222\345\242\236\345\255\220\345\272\217\345\210\227.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/subseq/\346\234\200\351\225\277\351\200\222\345\242\236\345\255\220\345\272\217\345\210\227.java" new file mode 100644 index 0000000..ad8cb65 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/subseq/\346\234\200\351\225\277\351\200\222\345\242\236\345\255\220\345\272\217\345\210\227.java" @@ -0,0 +1,45 @@ +package io.github.dunwu.algorithm.dp.subseq; + +import org.junit.jupiter.api.Assertions; + +/** + * 300. 最长递增子序列 + * + * @author Zhang Peng + * @date 2025-11-10 + */ +public class 最长递增子序列 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(4, s.lengthOfLIS(new int[] { 10, 9, 2, 5, 3, 7, 101, 18 })); + Assertions.assertEquals(4, s.lengthOfLIS(new int[] { 0, 1, 0, 3, 2, 3 })); + Assertions.assertEquals(1, s.lengthOfLIS(new int[] { 7, 7, 7, 7, 7, 7, 7 })); + } + + static class Solution { + + public int lengthOfLIS(int[] nums) { + int n = nums.length; + int[] dp = new int[n]; + int max = 1; + for (int i = 0; i < n; i++) { + dp[i] = 1; + for (int j = 0; j < i; j++) { + // 枚举区间 [0,i) 的所有数 nums[j],如果满足 nums[j]300. 最长递增子序列 + * + * @author Zhang Peng + * @date 2025-11-10 + */ +public class 最长递增子序列的个数 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(2, s.findNumberOfLIS(new int[] { 1, 3, 5, 4, 7 })); + Assertions.assertEquals(5, s.findNumberOfLIS(new int[] { 2, 2, 2, 2, 2 })); + Assertions.assertEquals(3, s.findNumberOfLIS(new int[] { 1, 2, 4, 3, 5, 4, 7, 2 })); + } + + static class Solution { + + public int findNumberOfLIS(int[] nums) { + + int n = nums.length; + int[] dp = new int[n]; + int[] cnt = new int[n]; + int max = 1; + for (int i = 0; i < n; i++) { + dp[i] = cnt[i] = 1; + for (int j = 0; j < i; j++) { + if (nums[j] < nums[i]) { + if (dp[i] < dp[j] + 1) { + dp[i] = dp[j] + 1; + cnt[i] = cnt[j]; + } else if (dp[i] == dp[j] + 1) { + cnt[i] += cnt[j]; + } + } + } + max = Math.max(max, dp[i]); + } + int res = 0; + for (int i = 0; i < n; i++) { + if (dp[i] == max) res += cnt[i]; + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/template/\345\212\250\346\200\201\350\247\204\345\210\222\346\250\241\346\235\277.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/template/\345\212\250\346\200\201\350\247\204\345\210\222\346\250\241\346\235\277.java" new file mode 100644 index 0000000..654e676 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/template/\345\212\250\346\200\201\350\247\204\345\210\222\346\250\241\346\235\277.java" @@ -0,0 +1,25 @@ +package io.github.dunwu.algorithm.dp.template; + +/** + * 动态规划模板 + * + * @author Zhang Peng + * @date 2025-11-10 + */ +public class 动态规划模板 { + // 自顶向下递归的动态规划 + // def dp(状态1, 状态2, ...): + // for 选择 in 所有可能的选择: + // # 此时的状态已经因为做了选择而改变 + // result = 求最值(result, dp(状态1, 状态2, ...)) + // return result + + // 自底向上迭代的动态规划 + // 初始化 base case + // dp[0][0][...] = base case + // # 进行状态转移 + // for 状态1 in 状态1的所有取值: + // for 状态2 in 状态2的所有取值: + // for ... + // dp[状态1][状态2][...] = 求最值(选择1,选择2...) +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/template/\345\212\250\346\200\201\350\247\204\345\210\222\350\247\243\350\203\214\345\214\205\351\227\256\351\242\230\346\250\241\346\235\277.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/template/\345\212\250\346\200\201\350\247\204\345\210\222\350\247\243\350\203\214\345\214\205\351\227\256\351\242\230\346\250\241\346\235\277.java" new file mode 100644 index 0000000..ded4738 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/template/\345\212\250\346\200\201\350\247\204\345\210\222\350\247\243\350\203\214\345\214\205\351\227\256\351\242\230\346\250\241\346\235\277.java" @@ -0,0 +1,22 @@ +package io.github.dunwu.algorithm.dp.template; + +/** + * 动态规划解背包问题模板 + * + * @author Zhang Peng + * @date 2025-12-17 + */ +public class 动态规划解背包问题模板 { + + // int[][] dp[N+1][W+1] + // dp[0][..] = 0 + // dp[..][0] = 0 + // + // for i in [1..N]: + // for w in [1..W]: + // dp[i][w] = max( + // 把物品 i 装进背包, + // 不把物品 i 装进背包 + // ) + // return dp[N][W] +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\344\271\230\347\247\257\346\234\200\345\244\247\345\255\220\346\225\260\347\273\204.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/\344\271\230\347\247\257\346\234\200\345\244\247\345\255\220\346\225\260\347\273\204.java" similarity index 97% rename from "codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\344\271\230\347\247\257\346\234\200\345\244\247\345\255\220\346\225\260\347\273\204.java" rename to "codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/\344\271\230\347\247\257\346\234\200\345\244\247\345\255\220\346\225\260\347\273\204.java" index 6734c61..3f7dff2 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\344\271\230\347\247\257\346\234\200\345\244\247\345\255\220\346\225\260\347\273\204.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/\344\271\230\347\247\257\346\234\200\345\244\247\345\255\220\346\225\260\347\273\204.java" @@ -1,4 +1,4 @@ -package io.github.dunwu.algorithm.dynamic; +package io.github.dunwu.algorithm.dp; import org.junit.jupiter.api.Assertions; diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/\345\210\206\345\211\262\347\255\211\345\222\214\345\255\220\351\233\206.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/\345\210\206\345\211\262\347\255\211\345\222\214\345\255\220\351\233\206.java" new file mode 100644 index 0000000..f478807 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/\345\210\206\345\211\262\347\255\211\345\222\214\345\255\220\351\233\206.java" @@ -0,0 +1,55 @@ +package io.github.dunwu.algorithm.dp; + +import org.junit.jupiter.api.Assertions; + +/** + * 416. 分割等和子集 + * + * @author Zhang Peng + * @date 2025-11-10 + */ +public class 分割等和子集 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertTrue(s.canPartition(new int[] { 1, 5, 11, 5 })); + Assertions.assertFalse(s.canPartition(new int[] { 1, 2, 3, 5 })); + } + + static class Solution { + + public boolean canPartition(int[] wights) { + + int sum = 0; + for (int weight : wights) { + sum += weight; + } + + // 和为奇数时,不可能划分成两个和相等的集合 + if (sum % 2 != 0) return false; + + // 初始化为背包问题 + int W = sum / 2; + int N = wights.length; + + // base case + boolean[][] dp = new boolean[N + 1][W + 1]; + for (int i = 0; i <= N; i++) + dp[i][0] = true; + + for (int i = 1; i <= N; i++) { + for (int w = 1; w <= W; w++) { + if (w - wights[i - 1] < 0) { + dp[i][w] = dp[i - 1][w]; + } else { + dp[i][w] = dp[i - 1][w] + || dp[i - 1][w - wights[i - 1]]; + } + } + } + return dp[N][W]; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/\346\234\200\345\244\247\345\255\220\346\225\260\347\273\204\345\222\214.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/\346\234\200\345\244\247\345\255\220\346\225\260\347\273\204\345\222\214.java" new file mode 100644 index 0000000..e7af1a6 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/\346\234\200\345\244\247\345\255\220\346\225\260\347\273\204\345\222\214.java" @@ -0,0 +1,39 @@ +package io.github.dunwu.algorithm.dp; + +import org.junit.jupiter.api.Assertions; + +/** + * 53. 最大子数组和 + * + * @author Zhang Peng + * @date 2025-11-10 + */ +public class 最大子数组和 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(6, s.maxSubArray(new int[] { -2, 1, -3, 4, -1, 2, 1, -5, 4 })); + } + + static class Solution { + + public int maxSubArray(int[] nums) { + int n = nums.length; + if (n == 0) return 0; + int[] dp = new int[n]; + // base case + // 第一个元素前面没有子数组 + dp[0] = nums[0]; + // 状态转移方程 + int res = Integer.MIN_VALUE; + for (int i = 1; i < n; i++) { + dp[i] = Math.max(nums[i], nums[i] + dp[i - 1]); + res = Math.max(res, dp[i]); + System.out.printf("nums[%d] = %d, dp[%d] = %d\n", i, nums[i], i, dp[i]); + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/\346\235\250\350\276\211\344\270\211\350\247\222.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/\346\235\250\350\276\211\344\270\211\350\247\222.java" new file mode 100644 index 0000000..4ace68d --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/\346\235\250\350\276\211\344\270\211\350\247\222.java" @@ -0,0 +1,54 @@ +package io.github.dunwu.algorithm.dp; + +import org.junit.jupiter.api.Assertions; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * 118. 杨辉三角 + * + * @author Zhang Peng + * @since 2018-11-04 + */ +public class 杨辉三角 { + + public static void main(String[] args) { + Solution s = new Solution(); + List> expect = new ArrayList<>(); + expect.add(Arrays.asList(1)); + expect.add(Arrays.asList(1, 1)); + expect.add(Arrays.asList(1, 2, 1)); + expect.add(Arrays.asList(1, 3, 3, 1)); + expect.add(Arrays.asList(1, 4, 6, 4, 1)); + List> lists = s.generate(5); + Assertions.assertArrayEquals(expect.toArray(), lists.toArray()); + } + + static class Solution { + + public List> generate(int row) { + int[][] matrix = new int[row][row]; + matrix[0][0] = 1; + List> res = new ArrayList<>(); + res.add(Collections.singletonList(1)); + for (int i = 1; i < row; i++) { + List list = new ArrayList<>(); + for (int j = 0; j <= i; j++) { + if (j == 0) { + matrix[i][j] = matrix[i - 1][j]; + } else { + matrix[i][j] = matrix[i - 1][j] + matrix[i - 1][j - 1]; + } + list.add(matrix[i][j]); + } + res.add(list); + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/\346\235\250\350\276\211\344\270\211\350\247\2222.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/\346\235\250\350\276\211\344\270\211\350\247\2222.java" new file mode 100644 index 0000000..a6a3faa --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/\346\235\250\350\276\211\344\270\211\350\247\2222.java" @@ -0,0 +1,49 @@ +package io.github.dunwu.algorithm.dp; + +import org.junit.jupiter.api.Assertions; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * 119. 杨辉三角 II + * + * @author Zhang Peng + * @since 2018-11-05 + */ +public class 杨辉三角2 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertArrayEquals(new Integer[] { 1, 3, 3, 1 }, s.getRow(3).toArray()); + Assertions.assertArrayEquals(new Integer[] { 1 }, s.getRow(0).toArray()); + Assertions.assertArrayEquals(new Integer[] { 1, 1 }, s.getRow(1).toArray()); + } + + static class Solution { + + public List getRow(int rowIndex) { + int row = rowIndex + 1; + int[][] matrix = new int[row][row]; + matrix[0][0] = 1; + List> res = new ArrayList<>(); + res.add(Collections.singletonList(1)); + for (int i = 1; i < row; i++) { + List list = new ArrayList<>(); + for (int j = 0; j <= i; j++) { + if (j == 0) { + matrix[i][j] = matrix[i - 1][j]; + } else { + matrix[i][j] = matrix[i - 1][j] + matrix[i - 1][j - 1]; + } + list.add(matrix[i][j]); + } + res.add(list); + } + return res.get(rowIndex); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/\351\233\266\351\222\261\345\205\221\346\215\2422.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/\351\233\266\351\222\261\345\205\221\346\215\2422.java" new file mode 100644 index 0000000..c0b0b76 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dp/\351\233\266\351\222\261\345\205\221\346\215\2422.java" @@ -0,0 +1,41 @@ +package io.github.dunwu.algorithm.dp; + +import org.junit.jupiter.api.Assertions; + +/** + * 518. 零钱兑换 II + * + * @author Zhang Peng + * @date 2025-11-11 + */ +public class 零钱兑换2 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(4, s.change(5, new int[] { 1, 2, 5 })); + Assertions.assertEquals(0, s.change(3, new int[] { 2 })); + Assertions.assertEquals(1, s.change(10, new int[] { 10 })); + } + + static class Solution { + + public int change(int amount, int[] coins) { + int n = coins.length; + int[][] dp = new int[n + 1][amount + 1]; + // base case + for (int i = 0; i <= n; i++) + dp[i][0] = 1; + + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= amount; j++) + if (j - coins[i - 1] >= 0) { + dp[i][j] = dp[i - 1][j] + + dp[i][j - coins[i - 1]]; + } else { dp[i][j] = dp[i - 1][j]; } + } + return dp[n][amount]; + } + + } + +} diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/MaxSubArray.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/MaxSubArray.java deleted file mode 100644 index 130756a..0000000 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/MaxSubArray.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.dunwu.algorithm.dynamic; - -import cn.hutool.core.util.ArrayUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * @author Zhang Peng - * @since 2020-03-06 - */ -public class MaxSubArray { - - private static final Logger log = LoggerFactory.getLogger(MaxSubArray.class); - - public static int maxSubArray(int[] nums) { - int[] result = new int[nums.length]; - result[0] = nums[0]; - int max = nums[0]; - for (int i = 1; i < nums.length; i++) { - result[i] = Math.max(result[i - 1] + nums[i], nums[i]); - if (max < result[i]) { - max = result[i]; - } - - if (log.isDebugEnabled()) { - log.debug(ArrayUtil.toString(result)); - } - } - return max; - } - - public static void main(String[] args) { - int[] array = new int[] { -2, 1, -3, 4, -1, 2, 1, -5, 4 }; - int max = MaxSubArray.maxSubArray(array); - System.out.println("max = " + max); - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214.java" deleted file mode 100644 index e1dd46e..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214.java" +++ /dev/null @@ -1,101 +0,0 @@ -package io.github.dunwu.algorithm.dynamic; - -import org.junit.jupiter.api.Assertions; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -/** - * @author Zhang Peng - * @see 120. 三角形最小路径和 - * @since 2020-07-04 - */ -public class 三角形最小路径和 { - - public static void main(String[] args) { - List> triangle = new ArrayList<>(); - triangle.add(Collections.singletonList(2)); - triangle.add(Arrays.asList(3, 4)); - triangle.add(Arrays.asList(6, 5, 7)); - triangle.add(Arrays.asList(4, 1, 8, 3)); - System.out.println("args = " + minimumTotal(triangle)); - Assertions.assertEquals(11, minimumTotal(triangle)); - Assertions.assertEquals(11, minimumTotal2(triangle)); - Assertions.assertEquals(11, minimumTotal3(triangle)); - Assertions.assertEquals(11, minimumTotal4(triangle)); - } - - // 回溯法,自上而下 - // 时间复杂度:O(2^(M*N)) - public static int minimumTotal(List> triangle) { - return backtrack(triangle, triangle.size(), 0, 0); - } - - private static int backtrack(List> triangle, int row, int x, int y) { - if (x == row - 1) return triangle.get(x).get(y); - int left = backtrack(triangle, row, x + 1, y); - int right = backtrack(triangle, row, x + 1, y + 1); - return triangle.get(x).get(y) + Math.min(left, right); - } - - // 回溯法 + 剪枝,自上而下 - // 针对 minimumTotal 加入记忆缓存 memory 存储计算结果,避免递归中的重复计算 - // 时间复杂度:< O(2^(M*N)) - // 空间复杂度:O(M*M) - public static int minimumTotal2(List> triangle) { - int level = triangle.size(); - int[][] memory = new int[level][level]; // 存储每个节点能得到的最优解 - return backtrack2(triangle, memory, triangle.size(), 0, 0); - } - - private static int backtrack2(List> triangle, int[][] memory, int row, int x, int y) { - if (memory[x][y] != 0) { return memory[x][y]; } - if (x == row - 1) return memory[x][y] = triangle.get(x).get(y); - int left = backtrack2(triangle, memory, row, x + 1, y); - int right = backtrack2(triangle, memory, row, x + 1, y + 1); - memory[x][y] = triangle.get(x).get(y) + Math.min(left, right); - return memory[x][y]; - } - - // 动态规划,自下而上 - // 时间复杂度:O(M^2) - // 空间复杂度:O(M^2) - public static int minimumTotal3(List> triangle) { - // 判空 - if (triangle == null || triangle.size() == 0) return 0; - int level = triangle.size(); - // 横竖维度都加1,可以不用考虑最后一行的初始化 - // 由于是三角形二维数组,可视为横竖维度都是行数 - int[][] memory = new int[level + 1][level + 1]; - for (int i = level - 1; i >= 0; i--) { - for (int j = 0; j < triangle.get(i).size(); j++) { - if (memory[i][j] == 0) { - memory[i][j] = Math.min(memory[i + 1][j], memory[i + 1][j + 1]) + triangle.get(i).get(j); - } - } - } - return memory[0][0]; - } - - // 动态规划,自下而上 + 空间优化 - // 时间复杂度:O(M^2) - // 空间复杂度:O(M^2) - public static int minimumTotal4(List> triangle) { - // 判空 - if (triangle == null || triangle.size() == 0) return 0; - int level = triangle.size(); - // 横竖维度都加1,可以不用考虑最后一行的初始化 - // 由于是三角形二维数组,可视为横竖维度都是行数 - int[] memory = new int[level + 1]; - for (int i = level - 1; i >= 0; i--) { - List rows = triangle.get(i); - for (int j = 0; j < rows.size(); j++) { - memory[j] = Math.min(memory[j], memory[j + 1]) + rows.get(j); - } - } - return memory[0]; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272.java" deleted file mode 100644 index e8723ba..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272.java" +++ /dev/null @@ -1,44 +0,0 @@ -package io.github.dunwu.algorithm.dynamic; - -import org.junit.jupiter.api.Assertions; - -/** - * @author Zhang Peng - * @see 121. 买卖股票的最佳时机 - * @since 2020-07-05 - */ -public class 买卖股票的最佳时机 { - - public static void main(String[] args) { - int[] prices = { 7, 1, 5, 3, 6, 4 }; - int[] prices2 = { 7, 6, 4, 3, 1 }; - Assertions.assertEquals(5, maxProfit(prices)); - Assertions.assertEquals(0, maxProfit(prices2)); - } - - public static int maxProfit(int[] prices) { - if (prices == null || prices.length == 0) return 0; - int n = prices.length; - int max = 0; - - // 定义二维数组 - // 一维表示第 i 天 - // 二维表示交易状态:0 表示没有买卖;1 表示买入;2 表示卖出 - int[][] mp = new int[n][3]; - mp[0][0] = 0; // 无 - mp[0][1] = -prices[0]; // 买 - mp[0][2] = 0; // 当天买进卖出,净赚0 - for (int i = 1; i < n; i++) { - mp[i][0] = mp[i - 1][0]; // 一直不买 - mp[i][1] = Math.max(mp[i - 1][1], mp[i - 1][0] - prices[i]); // 昨天买或今天买 - mp[i][2] = mp[i - 1][1] + prices[i]; // 昨天还有股,今天卖出 - for (int j = 0; j <= 2; j++) { - if (max < mp[i][j]) { - max = mp[i][j]; - } - } - } - return max; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\345\210\244\346\226\255\345\255\220\345\272\217\345\210\227.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\345\210\244\346\226\255\345\255\220\345\272\217\345\210\227.java" deleted file mode 100644 index 96d0bb2..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\345\210\244\346\226\255\345\255\220\345\272\217\345\210\227.java" +++ /dev/null @@ -1,39 +0,0 @@ -package io.github.dunwu.algorithm.dynamic; - -import org.junit.jupiter.api.Assertions; - -/** - * @author Zhang Peng - * @see 392. 判断子序列 - * @since 2020-07-06 - */ -public class 判断子序列 { - - public static void main(String[] args) { - Assertions.assertTrue(isSubsequence("abc", "ahbgdc")); - Assertions.assertFalse(isSubsequence("axc", "ahbgdc")); - Assertions.assertTrue(isSubsequence("", "ahbgdc")); - Assertions.assertFalse(isSubsequence("aaaaaa", "bbaaaa")); - } - - public static boolean isSubsequence(String s, String t) { - if (s == null || s.length() == 0) return true; - if (s.length() > t.length()) return false; - char[] source = s.toCharArray(); - char[] target = t.toCharArray(); - int i = 0, j = 0; - while (i < source.length && j < target.length) { - if (target[j] != source[i]) { - j++; - } else { - if (i == source.length - 1) { - return true; - } - i++; - j++; - } - } - return false; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\347\210\254\346\245\274\346\242\257.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\347\210\254\346\245\274\346\242\257.java" deleted file mode 100644 index 4f33ee3..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\347\210\254\346\245\274\346\242\257.java" +++ /dev/null @@ -1,63 +0,0 @@ -package io.github.dunwu.algorithm.dynamic; - -import org.junit.jupiter.api.Assertions; - -/** - * @author Zhang Peng - * @see 79. 单词搜索 - * @since 2020-07-04 - */ -public class 爬楼梯 { - - public static void main(String[] args) { - Assertions.assertEquals(1, climbStairs(0)); - Assertions.assertEquals(1, climbStairs(1)); - Assertions.assertEquals(2, climbStairs(2)); - Assertions.assertEquals(3, climbStairs(3)); - - Assertions.assertEquals(1, climbStairs2(0)); - Assertions.assertEquals(1, climbStairs2(1)); - Assertions.assertEquals(2, climbStairs2(2)); - Assertions.assertEquals(3, climbStairs2(3)); - - Assertions.assertEquals(1, climbStairs3(0)); - Assertions.assertEquals(1, climbStairs3(1)); - Assertions.assertEquals(2, climbStairs3(2)); - Assertions.assertEquals(3, climbStairs3(3)); - } - - // 使用递归(回溯方式) - // 时间复杂度:O(2^N) - public static int climbStairs(int n) { - return (n <= 1) ? 1 : climbStairs(n - 1) + climbStairs(n - 2); - } - - // 使用动态规划 - // 时间复杂度:O(N) - // 空间复杂度:O(N) - public static int climbStairs2(int n) { - if (n <= 1) return 1; - int[] mem = new int[n + 1]; - mem[0] = 1; - mem[1] = 1; - for (int i = 2; i < n + 1; i++) { - mem[i] = mem[i - 1] + mem[i - 2]; - } - return mem[n]; - } - // 优化 climbStairs2 动态规划方案 - // 时间复杂度:O(N) - // 空间复杂度:O(3) - public static int climbStairs3(int n) { - if (n <= 1) return 1; - int res = 0; - int prevStep1 = 1; - int prevStep2 = 1; - for (int i = 2; i < n + 1; i++) { - res = prevStep1 + prevStep2; - prevStep2 = prevStep1; - prevStep1 = res; - } - return res; - } -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\347\274\226\350\276\221\350\267\235\347\246\273.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\347\274\226\350\276\221\350\267\235\347\246\273.java" deleted file mode 100644 index 868165e..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\347\274\226\350\276\221\350\267\235\347\246\273.java" +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.dunwu.algorithm.dynamic; - -import org.junit.jupiter.api.Assertions; - -/** - * @author Zhang Peng - * @see 72. 编辑距离 - * @since 2020-07-06 - */ -public class 编辑距离 { - - public static void main(String[] args) { - Assertions.assertEquals(3, minDistance("horse", "ros")); - Assertions.assertEquals(5, minDistance("intention", "execution")); - } - - public static int minDistance(String word1, String word2) { - int m = word1.length(); - int n = word2.length(); - int[][] dp = new int[m + 1][n + 1]; - for (int i = 0; i < m + 1; i++) dp[i][0] = i; - for (int j = 0; j < n + 1; j++) dp[0][j] = j; - - for (int i = 1; i < m + 1; i++) { - for (int j = 1; j < n + 1; j++) { - if (word1.charAt(i - 1) == word2.charAt(j - 1)) { - dp[i][j] = dp[i - 1][j - 1]; - } else { - int m1 = Math.min(dp[i - 1][j], dp[i][j - 1]); - int m2 = Math.min(m1, dp[i - 1][j - 1]); - dp[i][j] = 1 + m2; - } - } - } - return dp[m][n]; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\351\233\266\351\222\261\345\205\221\346\215\242.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\351\233\266\351\222\261\345\205\221\346\215\242.java" deleted file mode 100644 index 20c3454..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/dynamic/\351\233\266\351\222\261\345\205\221\346\215\242.java" +++ /dev/null @@ -1,39 +0,0 @@ -package io.github.dunwu.algorithm.dynamic; - -import org.junit.jupiter.api.Assertions; - -/** - * @author Zhang Peng - * @see 322. 零钱兑换 - * @since 2020-07-06 - */ -public class 零钱兑换 { - - public static void main(String[] args) { - int[] nums = { 1, 2, 5 }; - Assertions.assertEquals(3, coinChange(nums, 11)); - Assertions.assertEquals(-1, coinChange(new int[] { 2 }, 3)); - } - - - public static int coinChange(int[] coins, int amount) { - return coinChange(coins, amount, 0); - } - - public static int coinChange(int[] coins, int amount, int idxCoin) { - if (amount == 0) { return 0; } - if (idxCoin < coins.length && amount > 0) { - int maxVal = amount / coins[idxCoin]; - int minCost = Integer.MAX_VALUE; - for (int x = 0; x <= maxVal; x++) { - if (amount >= x * coins[idxCoin]) { - int res = coinChange(coins, amount - x * coins[idxCoin], idxCoin + 1); - if (res != -1) { minCost = Math.min(minCost, res + x); } - } - } - return (minCost == Integer.MAX_VALUE) ? -1 : minCost; - } - return -1; - } - -} diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/Edge.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/Edge.java new file mode 100644 index 0000000..c4ee513 --- /dev/null +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/Edge.java @@ -0,0 +1,16 @@ +package io.github.dunwu.algorithm.graph; + +/** + * 存储相邻节点及边的权重 + */ +public class Edge { + + public int to; + public int weight; + + public Edge(int to, int weight) { + this.to = to; + this.weight = weight; + } + +} \ No newline at end of file diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/Graph.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/Graph.java new file mode 100644 index 0000000..eaf8c0c --- /dev/null +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/Graph.java @@ -0,0 +1,25 @@ +package io.github.dunwu.algorithm.graph; + +import java.util.List; + +public interface Graph { + + // 添加一条边(带权重) + void addEdge(int from, int to, int weight); + + // 删除一条边 + void removeEdge(int from, int to); + + // 判断两个节点是否相邻 + boolean hasEdge(int from, int to); + + // 返回一条边的权重 + int weight(int from, int to); + + // 返回某个节点的所有邻居节点和对应权重 + List neighbors(int v); + + // 返回节点总数 + int size(); + +} \ No newline at end of file diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/State.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/State.java new file mode 100644 index 0000000..3b78a8f --- /dev/null +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/State.java @@ -0,0 +1,17 @@ +package io.github.dunwu.algorithm.graph; + +// 图结构的 BFS 遍历,从节点 s 开始进行 BFS,且记录遍历步数(从起点 s 到当前节点的边的条数) +// 每个节点自行维护 State 类,记录从 s 走来的遍历步数 +public class State { + + // 当前节点 ID + public int node; + // 从起点 s 到当前节点的遍历步数 + public int step; + + public State(int node, int step) { + this.node = node; + this.step = step; + } + +} \ No newline at end of file diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/Vertex.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/Vertex.java new file mode 100644 index 0000000..46dec8a --- /dev/null +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/Vertex.java @@ -0,0 +1,11 @@ +package io.github.dunwu.algorithm.graph; + +/** + * 图节点 + */ +public class Vertex { + + public int id; + public Vertex[] neighbors; + +} \ No newline at end of file diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/bipartite/\345\210\244\346\226\255\344\272\214\345\210\206\345\233\276.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/bipartite/\345\210\244\346\226\255\344\272\214\345\210\206\345\233\276.java" new file mode 100644 index 0000000..a78231e --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/bipartite/\345\210\244\346\226\255\344\272\214\345\210\206\345\233\276.java" @@ -0,0 +1,139 @@ +package io.github.dunwu.algorithm.graph.bipartite; + +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; +import java.util.Queue; + +/** + * 785. 判断二分图 + * + * @author Zhang Peng + * @date 2025-11-03 + */ +public class 判断二分图 { + + public static void main(String[] args) { + + int[][] input = new int[][] { { 1, 2, 3 }, { 0, 2 }, { 0, 1, 3 }, { 0, 2 } }; + int[][] input2 = new int[][] { { 1, 3 }, { 0, 2 }, { 1, 3 }, { 0, 2 } }; + + Solution s = new Solution(); + Assertions.assertFalse(s.isBipartite(input)); + Assertions.assertFalse(s.isBipartite(input2)); + + Solution2 s2 = new Solution2(); + Assertions.assertFalse(s2.isBipartite(input)); + Assertions.assertFalse(s2.isBipartite(input2)); + } + + // 二分图算法(DFS 版本) + static class Solution { + + // 记录图是否符合二分图性质 + private boolean ok = true; + // 记录图中节点的颜色,false 和 true 代表两种不同颜色 + private boolean[] color; + // 记录图中节点是否被访问过 + private boolean[] visited; + + // 主函数,输入邻接表,判断是否是二分图 + public boolean isBipartite(int[][] graph) { + int n = graph.length; + color = new boolean[n]; + visited = new boolean[n]; + // 因为图不一定是联通的,可能存在多个子图 + // 所以要把每个节点都作为起点进行一次遍历 + // 如果发现任何一个子图不是二分图,整幅图都不算二分图 + for (int v = 0; v < n; v++) { + if (!visited[v]) { + dfs(graph, v); + } + } + return ok; + } + + // DFS 遍历框架 + private void dfs(int[][] graph, int v) { + // 如果已经确定不是二分图了,就不用浪费时间再递归遍历了 + if (!ok) return; + + visited[v] = true; + for (int w : graph[v]) { + if (!visited[w]) { + // 相邻节点 w 没有被访问过 + // 那么应该给节点 w 涂上和节点 v 不同的颜色 + color[w] = !color[v]; + // 继续遍历 w + dfs(graph, w); + } else { + // 相邻节点 w 已经被访问过 + // 根据 v 和 w 的颜色判断是否是二分图 + if (color[w] == color[v]) { + // 若相同,则此图不是二分图 + ok = false; + } + } + } + } + + } + + // 二分图算法(BFS 版本) + static class Solution2 { + + // 记录图是否符合二分图性质 + private boolean ok = true; + // 记录图中节点的颜色,false 和 true 代表两种不同颜色 + private boolean[] color; + // 记录图中节点是否被访问过 + private boolean[] visited; + + public boolean isBipartite(int[][] graph) { + int n = graph.length; + color = new boolean[n]; + visited = new boolean[n]; + + for (int v = 0; v < n; v++) { + if (!visited[v]) { + // 改为使用 BFS 函数 + bfs(graph, v); + } + } + + return ok; + } + + // 从 start 节点开始进行 BFS 遍历 + private void bfs(int[][] graph, int start) { + Queue q = new LinkedList<>(); + visited[start] = true; + q.offer(start); + + while (!q.isEmpty() && ok) { + int v = q.poll(); + // 从节点 v 向所有相邻节点扩散 + for (int w : graph[v]) { + if (!visited[w]) { + // 相邻节点 w 没有被访问过 + // 那么应该给节点 w 涂上和节点 v 不同的颜色 + color[w] = !color[v]; + // 标记 w 节点,并放入队列 + visited[w] = true; + q.offer(w); + } else { + // 相邻节点 w 已经被访问过 + // 根据 v 和 w 的颜色判断是否是二分图 + if (color[w] == color[v]) { + // 若相同,则此图不是二分图 + ok = false; + return; + } + } + } + } + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/bipartite/\345\217\257\350\203\275\347\232\204\344\272\214\345\210\206\346\263\225.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/bipartite/\345\217\257\350\203\275\347\232\204\344\272\214\345\210\206\346\263\225.java" new file mode 100644 index 0000000..1dc1d9b --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/bipartite/\345\217\257\350\203\275\347\232\204\344\272\214\345\210\206\346\263\225.java" @@ -0,0 +1,86 @@ +package io.github.dunwu.algorithm.graph.bipartite; + +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; +import java.util.List; + +/** + * 886. 可能的二分法 + * + * @author Zhang Peng + * @date 2025-11-03 + */ +public class 可能的二分法 { + + public static void main(String[] args) { + + int[][] input = new int[][] { { 1, 2 }, { 1, 3 }, { 2, 4 } }; + int[][] input2 = new int[][] { { 1, 2 }, { 1, 3 }, { 2, 3 } }; + int[][] input3 = new int[][] { { 1, 2 }, { 2, 3 }, { 3, 4 }, { 4, 5 }, { 1, 5 } }; + + Solution s = new Solution(); + Assertions.assertTrue(s.possibleBipartition(4, input)); + Assertions.assertFalse(s.possibleBipartition(3, input2)); + Assertions.assertFalse(s.possibleBipartition(5, input3)); + } + + static class Solution { + + private boolean ok = true; + private boolean[] color; + private boolean[] visited; + + public boolean possibleBipartition(int n, int[][] dislikes) { + // 图节点编号从 1 开始 + color = new boolean[n + 1]; + visited = new boolean[n + 1]; + // 转化成邻接表表示图结构 + List[] graph = buildGraph(n, dislikes); + + for (int v = 1; v <= n; v++) { + if (!visited[v]) { + dfs(graph, v); + } + } + return ok; + } + + // 建图函数 + private List[] buildGraph(int n, int[][] dislikes) { + // 图节点编号为 1...n + List[] graph = new LinkedList[n + 1]; + for (int i = 1; i <= n; i++) { + graph[i] = new LinkedList<>(); + } + for (int[] edge : dislikes) { + int v = edge[1]; + int w = edge[0]; + // 「无向图」相当于「双向图」 + // v -> w + graph[v].add(w); + // w -> v + graph[w].add(v); + } + return graph; + } + + // 和之前判定二分图的 traverse 函数完全相同 + private void dfs(List[] graph, int v) { + if (!ok) return; + visited[v] = true; + for (int w : graph[v]) { + if (!visited[w]) { + color[w] = !color[v]; + dfs(graph, w); + } else { + if (color[w] == color[v]) { + ok = false; + } + } + } + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/dfs/\346\211\200\346\234\211\345\217\257\350\203\275\347\232\204\350\267\257\345\276\204.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/dfs/\346\211\200\346\234\211\345\217\257\350\203\275\347\232\204\350\267\257\345\276\204.java" new file mode 100644 index 0000000..b111b38 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/dfs/\346\211\200\346\234\211\345\217\257\350\203\275\347\232\204\350\267\257\345\276\204.java" @@ -0,0 +1,69 @@ +package io.github.dunwu.algorithm.graph.dfs; + +import org.junit.jupiter.api.Assertions; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +/** + * 797. 所有可能的路径 + * + * @author Zhang Peng + * @date 2025-11-03 + */ +public class 所有可能的路径 { + + public static void main(String[] args) { + Solution s = new Solution(); + int[][] input = new int[][] { + { 1, 2 }, { 3 }, { 3 }, {} + }; + List> expect = new LinkedList<>(); + expect.add(Arrays.asList(0, 1, 3)); + expect.add(Arrays.asList(0, 2, 3)); + List> output = s.allPathsSourceTarget(input); + for (int i = 0; i < expect.size(); i++) { + Assertions.assertArrayEquals(expect.get(i).toArray(), output.get(i).toArray()); + } + // System.out.println("v = " + output); + } + + static class Solution { + + private List path; + private List> res; + + public List> allPathsSourceTarget(int[][] graph) { + path = new LinkedList<>(); + res = new LinkedList<>(); + dfs(graph, 0); + return res; + } + + // 图的遍历框架 + void dfs(int[][] graph, int s) { + + // 添加节点 s 到路径 + path.add(s); + + int n = graph.length; + if (s == n - 1) { + // 到达终点 + res.add(new LinkedList<>(path)); + path.remove(path.size() - 1); + return; + } + + // 递归每个相邻节点 + for (int v : graph[s]) { + dfs(graph, v); + } + + // 从路径移出节点 s + path.remove(path.size() - 1); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/template/BFS\351\201\215\345\216\206\345\233\276.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/template/BFS\351\201\215\345\216\206\345\233\276.java" new file mode 100644 index 0000000..74141ec --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/template/BFS\351\201\215\345\216\206\345\233\276.java" @@ -0,0 +1,87 @@ +package io.github.dunwu.algorithm.graph.template; + +import io.github.dunwu.algorithm.graph.Edge; +import io.github.dunwu.algorithm.graph.Graph; +import io.github.dunwu.algorithm.graph.State; + +import java.util.LinkedList; +import java.util.Queue; + +/** + * 图的遍历框架 + * + * @author Zhang Peng + * @date 2025-11-03 + */ +public class BFS遍历图 { + + // 图结构的 BFS 遍历,从节点 s 开始进行 BFS + void bfs(Graph graph, int s) { + boolean[] visited = new boolean[graph.size()]; + Queue q = new LinkedList<>(); + q.offer(s); + visited[s] = true; + + while (!q.isEmpty()) { + int cur = q.poll(); + System.out.println("visit " + cur); + for (Edge e : graph.neighbors(cur)) { + if (visited[e.to]) { + continue; + } + q.offer(e.to); + visited[e.to] = true; + } + } + } + + // 从 s 开始 BFS 遍历图的所有节点,且记录遍历的步数 + void bfs2(Graph graph, int s) { + boolean[] visited = new boolean[graph.size()]; + Queue q = new LinkedList<>(); + q.offer(s); + visited[s] = true; + // 记录从 s 开始走到当前节点的步数 + int step = 0; + while (!q.isEmpty()) { + int sz = q.size(); + for (int i = 0; i < sz; i++) { + int cur = q.poll(); + System.out.println("visit " + cur + " at step " + step); + for (Edge e : graph.neighbors(cur)) { + if (visited[e.to]) { + continue; + } + q.offer(e.to); + visited[e.to] = true; + } + } + step++; + } + } + + // 图结构的 BFS 遍历,从节点 s 开始进行 BFS,且记录遍历步数(从起点 s 到当前节点的边的条数) + // 每个节点自行维护 State 类,记录从 s 走来的遍历步数 + void bfs3(Graph graph, int s) { + boolean[] visited = new boolean[graph.size()]; + Queue q = new LinkedList<>(); + + q.offer(new State(s, 0)); + visited[s] = true; + + while (!q.isEmpty()) { + State state = q.poll(); + int node = state.node; + int step = state.step; + System.out.println("visit " + node + " with step " + step); + for (Edge e : graph.neighbors(node)) { + if (visited[e.to]) { + continue; + } + q.offer(new State(e.to, step + 1)); + visited[e.to] = true; + } + } + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/template/DFS\351\201\215\345\216\206\345\233\276\347\232\204\346\211\200\346\234\211\350\212\202\347\202\271.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/template/DFS\351\201\215\345\216\206\345\233\276\347\232\204\346\211\200\346\234\211\350\212\202\347\202\271.java" new file mode 100644 index 0000000..86b8925 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/template/DFS\351\201\215\345\216\206\345\233\276\347\232\204\346\211\200\346\234\211\350\212\202\347\202\271.java" @@ -0,0 +1,47 @@ +package io.github.dunwu.algorithm.graph.template; + +import io.github.dunwu.algorithm.graph.Edge; +import io.github.dunwu.algorithm.graph.Graph; +import io.github.dunwu.algorithm.graph.Vertex; + +/** + * 图的遍历框架 + * + * @author Zhang Peng + * @date 2025-11-03 + */ +public class DFS遍历图的所有节点 { + + // 遍历图的所有节点 + void traverse(Graph graph, int s, boolean[] visited) { + // base case + if (s < 0 || s >= graph.size()) { return; } + // 防止死循环 + if (visited[s]) { return; } + // 前序位置 + visited[s] = true; + System.out.println("visit " + s); + for (Edge e : graph.neighbors(s)) { + traverse(graph, e.to, visited); + } + // 后序位置 + } + + // 图的遍历框架 + // 需要一个 visited 数组记录被遍历过的节点 + // 避免走回头路陷入死循环 + void traverse(Vertex v, boolean[] visited) { + // base case + if (v == null) { return; } + // 防止死循环 + if (visited[v.id]) { return; } + // 前序位置 + visited[v.id] = true; + System.out.println("visit " + v.id); + for (Vertex neighbor : v.neighbors) { + traverse(neighbor, visited); + } + // 后序位置 + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/template/DFS\351\201\215\345\216\206\345\233\276\347\232\204\346\211\200\346\234\211\350\267\257\345\276\204.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/template/DFS\351\201\215\345\216\206\345\233\276\347\232\204\346\211\200\346\234\211\350\267\257\345\276\204.java" new file mode 100644 index 0000000..0a53f86 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/template/DFS\351\201\215\345\216\206\345\233\276\347\232\204\346\211\200\346\234\211\350\267\257\345\276\204.java" @@ -0,0 +1,64 @@ +package io.github.dunwu.algorithm.graph.template; + +import io.github.dunwu.algorithm.graph.Edge; +import io.github.dunwu.algorithm.graph.Graph; +import io.github.dunwu.algorithm.tree.Node; + +import java.util.LinkedList; + +/** + * DFS遍历图的所有路径 + * + * @author Zhang Peng + * @date 2025-12-02 + */ +public class DFS遍历图的所有路径 { + + // onPath 和 path 记录当前递归路径上的节点 + boolean[] onPath = null; + // 多叉树的遍历框架,寻找从根节点到目标节点的路径 + LinkedList path = new LinkedList<>(); + + void traverse(Node root, Node targetNode) { + // base case + if (root == null) { + return; + } + if (root.val == targetNode.val) { + // 找到目标节点 + System.out.println("find path: " + String.join("->", path) + "->" + targetNode); + return; + } + // 前序位置 + path.addLast(String.valueOf(root.val)); + for (Node child : root.children) { + traverse(child, targetNode); + } + // 后序位置 + path.removeLast(); + } + + void traverse(Graph graph, int from, int to) { + if (onPath == null) { onPath = new boolean[graph.size()]; } + // base case + if (from < 0 || from >= graph.size()) { return; } + // 防止死循环(成环) + if (onPath[from]) { return; } + if (from == to) { + // 找到目标节点 + System.out.println("find path: " + String.join("->", path) + "->" + to); + return; + } + + // 前序位置 + onPath[from] = true; + path.add(String.valueOf(from)); + for (Edge e : graph.neighbors(from)) { + traverse(graph, e.to, to); + } + // 后序位置 + path.remove(path.size() - 1); + onPath[from] = false; + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/template/DFS\351\201\215\345\216\206\345\233\276\347\232\204\346\211\200\346\234\211\350\276\271.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/template/DFS\351\201\215\345\216\206\345\233\276\347\232\204\346\211\200\346\234\211\350\276\271.java" new file mode 100644 index 0000000..8081142 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/template/DFS\351\201\215\345\216\206\345\233\276\347\232\204\346\211\200\346\234\211\350\276\271.java" @@ -0,0 +1,55 @@ +package io.github.dunwu.algorithm.graph.template; + +import io.github.dunwu.algorithm.graph.Edge; +import io.github.dunwu.algorithm.graph.Graph; +import io.github.dunwu.algorithm.graph.Vertex; +import io.github.dunwu.algorithm.tree.Node; + +/** + * DFS遍历图的所有边 + * + * @author Zhang Peng + * @date 2025-11-06 + */ +public class DFS遍历图的所有边 { + + // 遍历多叉树的树枝 + void traverseBranch(Node root) { + // base case + if (root == null) { return; } + for (Node child : root.children) { + System.out.println("visit branch: " + root.val + " -> " + child.val); + traverseBranch(child); + } + } + + // 遍历图的边 + // 需要一个二维 visited 数组记录被遍历过的边,visited[from][to] 表示边 from->to 已经被遍历过 + void traverseEdges(Vertex v, boolean[][] visited) { + // base case + if (v == null) { return; } + for (Vertex neighbor : v.neighbors) { + // 如果边已经被遍历过,则跳过 + if (visited[v.id][neighbor.id]) { continue; } + // 标记并访问边 + visited[v.id][neighbor.id] = true; + System.out.println("visit edge: " + v.id + " -> " + neighbor.id); + traverseEdges(neighbor, visited); + } + } + + // 从起点 s 开始遍历图的所有边 + void traverseEdges(Graph graph, int s, boolean[][] visited) { + // base case + if (s < 0 || s >= graph.size()) { return; } + for (Edge e : graph.neighbors(s)) { + // 如果边已经被遍历过,则跳过 + if (visited[s][e.to]) { continue; } + // 标记并访问边 + visited[s][e.to] = true; + System.out.println("visit edge: " + s + " -> " + e.to); + traverseEdges(graph, e.to, visited); + } + } + +} diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/template/Dijkstra.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/template/Dijkstra.java new file mode 100644 index 0000000..518770b --- /dev/null +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/template/Dijkstra.java @@ -0,0 +1,78 @@ +package io.github.dunwu.algorithm.graph.template; + +import io.github.dunwu.algorithm.graph.Edge; +import io.github.dunwu.algorithm.graph.Graph; + +import java.util.Arrays; +import java.util.PriorityQueue; + +/** + * Dijkstra 算法模板 + * + * @author Zhang Peng + * @date 2025-12-03 + */ +public class Dijkstra { + + // 输入不包含负权重边的加权图 graph 和起点 src + // 返回从起点 src 到其他节点的最小路径权重和 + public int[] dijkstra(Graph graph, int src) { + // 记录从起点 src 到其他节点的最小路径权重和 + // distTo[i] 表示从起点 src 到节点 i 的最小路径权重和 + int[] distTo = new int[graph.size()]; + // 都初始化为正无穷,表示未计算 + Arrays.fill(distTo, Integer.MAX_VALUE); + + // 优先级队列,distFromStart 较小的节点排在前面 + PriorityQueue pq = new PriorityQueue<>((a, b) -> { + return a.distFromStart - b.distFromStart; + }); + + // 从起点 src 开始进行 BFS + pq.offer(new State(src, 0)); + distTo[src] = 0; + + while (!pq.isEmpty()) { + State state = pq.poll(); + int curNode = state.node; + int curDistFromStart = state.distFromStart; + + if (distTo[curNode] < curDistFromStart) { + // 在 Dijkstra 算法中,队列中可能存在重复的节点 state + // 所以要在元素出队时进行判断,去除较差的重复节点 + continue; + } + + for (Edge e : graph.neighbors(curNode)) { + int nextNode = e.to; + int nextDistFromStart = curDistFromStart + e.weight; + + if (distTo[nextNode] <= nextDistFromStart) { + continue; + } + + // 将 nextNode 节点加入优先级队列 + pq.offer(new State(nextNode, nextDistFromStart)); + // 记录 nextNode 节点到起点的最小路径权重和 + distTo[nextNode] = nextDistFromStart; + } + } + + return distTo; + } + + static class State { + + // 当前节点 ID + int node; + // 从起点 s 到当前 node 节点的最小路径权重和 + int distFromStart; + + public State(int node, int distFromStart) { + this.node = node; + this.distFromStart = distFromStart; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/template/\345\271\266\346\237\245\351\233\206.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/template/\345\271\266\346\237\245\351\233\206.java" new file mode 100644 index 0000000..bc15937 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/template/\345\271\266\346\237\245\351\233\206.java" @@ -0,0 +1,60 @@ +package io.github.dunwu.algorithm.graph.template; + +/** + * 并查集 + * + * @author Zhang Peng + * @date 2025-12-03 + */ +public class 并查集 { + + static class UF { + + // 连通分量个数 + private int count; + // 存储每个节点的父节点 + private int[] parent; + + // n 为图中节点的个数 + public UF(int n) { + this.count = n; + parent = new int[n]; + for (int i = 0; i < n; i++) { + parent[i] = i; + } + } + + // 将节点 p 和节点 q 连通 + public void union(int p, int q) { + int rootP = find(p); + int rootQ = find(q); + + if (rootP == rootQ) { return; } + + parent[rootQ] = rootP; + // 两个连通分量合并成一个连通分量 + count--; + } + + // 判断节点 p 和节点 q 是否连通 + public boolean connected(int p, int q) { + int rootP = find(p); + int rootQ = find(q); + return rootP == rootQ; + } + + public int find(int x) { + if (parent[x] != x) { + parent[x] = find(parent[x]); + } + return parent[x]; + } + + // 返回图中的连通分量个数 + public int count() { + return count; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/template/\351\202\273\346\216\245\347\237\251\351\230\265\345\256\236\347\216\260\345\233\276.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/template/\351\202\273\346\216\245\347\237\251\351\230\265\345\256\236\347\216\260\345\233\276.java" new file mode 100644 index 0000000..140b44c --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/template/\351\202\273\346\216\245\347\237\251\351\230\265\345\256\236\347\216\260\345\233\276.java" @@ -0,0 +1,138 @@ +package io.github.dunwu.algorithm.graph.template; + +import io.github.dunwu.algorithm.graph.Edge; + +import java.util.ArrayList; +import java.util.List; + +/** + * 邻接矩阵实现图 + * + * @author Zhang Peng + * @date 2025-11-06 + */ +public class 邻接矩阵实现图 { + + // 加权有向图的通用实现(邻接矩阵) + static class WeightedDigraph { + + // 邻接矩阵,matrix[from][to] 存储从节点 from 到节点 to 的边的权重 + // 0 表示没有连接 + private int[][] matrix; + + public WeightedDigraph(int n) { + matrix = new int[n][n]; + } + + // 增,添加一条带权重的有向边,复杂度 O(1) + public void addEdge(int from, int to, int weight) { + matrix[from][to] = weight; + } + + // 删,删除一条有向边,复杂度 O(1) + public void removeEdge(int from, int to) { + matrix[from][to] = 0; + } + + // 查,判断两个节点是否相邻,复杂度 O(1) + public boolean hasEdge(int from, int to) { + return matrix[from][to] != 0; + } + + // 查,返回一条边的权重,复杂度 O(1) + public int weight(int from, int to) { + return matrix[from][to]; + } + + // 查,返回某个节点的所有邻居节点,复杂度 O(V) + public List neighbors(int v) { + List res = new ArrayList<>(); + for (int i = 0; i < matrix[v].length; i++) { + if (matrix[v][i] != 0) { + res.add(new Edge(i, matrix[v][i])); + } + } + return res; + } + + public static void main(String[] args) { + WeightedDigraph graph = new WeightedDigraph(3); + graph.addEdge(0, 1, 1); + graph.addEdge(1, 2, 2); + graph.addEdge(2, 0, 3); + graph.addEdge(2, 1, 4); + + System.out.println(graph.hasEdge(0, 1)); // true + System.out.println(graph.hasEdge(1, 0)); // false + + graph.neighbors(2).forEach(edge -> { + System.out.println(2 + " -> " + edge.to + ", wight: " + edge.weight); + }); + // 2 -> 0, wight: 3 + // 2 -> 1, wight: 4 + + graph.removeEdge(0, 1); + System.out.println(graph.hasEdge(0, 1)); // false + } + + } + + // 无向加权图的通用实现 + static class WeightedUndigraph { + + private WeightedDigraph graph; + + public WeightedUndigraph(int n) { + graph = new WeightedDigraph(n); + } + + // 增,添加一条带权重的无向边 + public void addEdge(int from, int to, int weight) { + graph.addEdge(from, to, weight); + graph.addEdge(to, from, weight); + } + + // 删,删除一条无向边 + public void removeEdge(int from, int to) { + graph.removeEdge(from, to); + graph.removeEdge(to, from); + } + + // 查,判断两个节点是否相邻 + public boolean hasEdge(int from, int to) { + return graph.hasEdge(from, to); + } + + // 查,返回一条边的权重 + public int weight(int from, int to) { + return graph.weight(from, to); + } + + // 查,返回某个节点的所有邻居节点 + public List neighbors(int v) { + return graph.neighbors(v); + } + + public static void main(String[] args) { + WeightedUndigraph graph = new WeightedUndigraph(3); + graph.addEdge(0, 1, 1); + graph.addEdge(2, 0, 3); + graph.addEdge(2, 1, 4); + + System.out.println(graph.hasEdge(0, 1)); // true + System.out.println(graph.hasEdge(1, 0)); // true + + graph.neighbors(2).forEach(edge -> { + System.out.println(2 + " <-> " + edge.to + ", wight: " + edge.weight); + }); + // 2 <-> 0, wight: 3 + // 2 <-> 1, wight: 4 + + graph.removeEdge(0, 1); + System.out.println(graph.hasEdge(0, 1)); // false + System.out.println(graph.hasEdge(1, 0)); // false + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/template/\351\202\273\346\216\245\350\241\250\345\256\236\347\216\260\345\233\276.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/template/\351\202\273\346\216\245\350\241\250\345\256\236\347\216\260\345\233\276.java" new file mode 100644 index 0000000..2372c4c --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/template/\351\202\273\346\216\245\350\241\250\345\256\236\347\216\260\345\233\276.java" @@ -0,0 +1,159 @@ +package io.github.dunwu.algorithm.graph.template; + +import io.github.dunwu.algorithm.graph.Edge; + +import java.util.ArrayList; +import java.util.List; + +/** + * 邻接表实现图 + * + * @author Zhang Peng + * @date 2025-11-06 + */ +public class 邻接表实现图 { + + /** + * 加权有向图的通用实现(邻接表) + */ + static class WeightedDigraph { + + // 邻接表,graph[v] 存储节点 v 的所有邻居节点及对应权重 + private List[] graph; + + public WeightedDigraph(int n) { + // 我们这里简单起见,建图时要传入节点总数,这其实可以优化 + // 比如把 graph 设置为 Map>,就可以动态添加新节点了 + graph = new List[n]; + for (int i = 0; i < n; i++) { + graph[i] = new ArrayList<>(); + } + } + + // 增,添加一条带权重的有向边,复杂度 O(1) + public void addEdge(int from, int to, int weight) { + graph[from].add(new Edge(to, weight)); + } + + // 删,删除一条有向边,复杂度 O(V) + public void removeEdge(int from, int to) { + for (int i = 0; i < graph[from].size(); i++) { + if (graph[from].get(i).to == to) { + graph[from].remove(i); + break; + } + } + } + + // 查,判断两个节点是否相邻,复杂度 O(V) + public boolean hasEdge(int from, int to) { + for (Edge e : graph[from]) { + if (e.to == to) { + return true; + } + } + return false; + } + + // 查,返回一条边的权重,复杂度 O(V) + public int weight(int from, int to) { + for (Edge e : graph[from]) { + if (e.to == to) { + return e.weight; + } + } + throw new IllegalArgumentException("No such edge"); + } + + // 上面的 hasEdge、removeEdge、weight 方法遍历 List 的行为是可以优化的 + // 比如用 Map> 存储邻接表 + // 这样就可以避免遍历 List,复杂度就能降到 O(1) + + // 查,返回某个节点的所有邻居节点,复杂度 O(1) + public List neighbors(int v) { + return graph[v]; + } + + public static void main(String[] args) { + WeightedDigraph graph = new WeightedDigraph(3); + graph.addEdge(0, 1, 1); + graph.addEdge(1, 2, 2); + graph.addEdge(2, 0, 3); + graph.addEdge(2, 1, 4); + + System.out.println(graph.hasEdge(0, 1)); // true + System.out.println(graph.hasEdge(1, 0)); // false + + graph.neighbors(2).forEach(edge -> { + System.out.println(2 + " -> " + edge.to + ", wight: " + edge.weight); + }); + // 2 -> 0, wight: 3 + // 2 -> 1, wight: 4 + + graph.removeEdge(0, 1); + System.out.println(graph.hasEdge(0, 1)); // false + } + + } + + /** + * 无向加权图的通用实现 + */ + static class WeightedUndigraph { + + private WeightedDigraph graph; + + public WeightedUndigraph(int n) { + graph = new WeightedDigraph(n); + } + + // 增,添加一条带权重的无向边 + public void addEdge(int from, int to, int weight) { + graph.addEdge(from, to, weight); + graph.addEdge(to, from, weight); + } + + // 删,删除一条无向边 + public void removeEdge(int from, int to) { + graph.removeEdge(from, to); + graph.removeEdge(to, from); + } + + // 查,判断两个节点是否相邻 + public boolean hasEdge(int from, int to) { + return graph.hasEdge(from, to); + } + + // 查,返回一条边的权重 + public int weight(int from, int to) { + return graph.weight(from, to); + } + + // 查,返回某个节点的所有邻居节点 + public List neighbors(int v) { + return graph.neighbors(v); + } + + public static void main(String[] args) { + WeightedUndigraph graph = new WeightedUndigraph(3); + graph.addEdge(0, 1, 1); + graph.addEdge(2, 0, 3); + graph.addEdge(2, 1, 4); + + System.out.println(graph.hasEdge(0, 1)); // true + System.out.println(graph.hasEdge(1, 0)); // true + + graph.neighbors(2).forEach(edge -> { + System.out.println(2 + " <-> " + edge.to + ", wight: " + edge.weight); + }); + // 2 <-> 0, wight: 3 + // 2 <-> 1, wight: 4 + + graph.removeEdge(0, 1); + System.out.println(graph.hasEdge(0, 1)); // false + System.out.println(graph.hasEdge(1, 0)); // false + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/topological_sort/\350\257\276\347\250\213\350\241\250.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/topological_sort/\350\257\276\347\250\213\350\241\250.java" new file mode 100644 index 0000000..62c8c0d --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/topological_sort/\350\257\276\347\250\213\350\241\250.java" @@ -0,0 +1,139 @@ +package io.github.dunwu.algorithm.graph.topological_sort; + +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +/** + * 207. 课程表 + * + * @author Zhang Peng + * @date 2025-11-03 + */ +public class 课程表 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertTrue(s.canFinish(2, new int[][] { { 1, 0 } })); + Assertions.assertFalse(s.canFinish(2, new int[][] { { 1, 0 }, { 0, 1 } })); + + Solution2 s2 = new Solution2(); + Assertions.assertTrue(s2.canFinish(2, new int[][] { { 1, 0 } })); + Assertions.assertFalse(s2.canFinish(2, new int[][] { { 1, 0 }, { 0, 1 } })); + } + + // 环检测算法(DFS 版本) + static class Solution { + + // 记录一次递归堆栈中的节点 + boolean[] onPath; + // 记录节点是否被遍历过 + boolean[] visited; + // 记录图中是否有环 + boolean hasCycle = false; + + public boolean canFinish(int numCourses, int[][] prerequisites) { + List[] graph = buildGraph(numCourses, prerequisites); + visited = new boolean[numCourses]; + onPath = new boolean[numCourses]; + + // 遍历图中的所有节点 + for (int i = 0; i < numCourses; i++) { + dfs(graph, i); + } + // 只要没有循环依赖可以完成所有课程 + return !hasCycle; + } + + public void dfs(List[] graph, int s) { + // 找到环,或已访问,则无需再遍历 + if (onPath[s]) { hasCycle = true; } + if (hasCycle || visited[s]) { return; } + + // 【前序】 + visited[s] = true; + onPath[s] = true; + for (int t : graph[s]) { + dfs(graph, t); + } + // 【后序】 + onPath[s] = false; + } + + public List[] buildGraph(int n, int[][] data) { + List[] graph = new LinkedList[n]; + for (int i = 0; i < n; i++) { + graph[i] = new LinkedList<>(); + } + + for (int[] edge : data) { + int from = edge[1], to = edge[0]; + graph[from].add(to); + } + return graph; + } + + } + + // 环检测算法(BFS 版本) + static class Solution2 { + + public boolean canFinish(int numCourses, int[][] prerequisites) { + // 建图,有向边代表「被依赖」关系 + List[] graph = buildGraph(numCourses, prerequisites); + // 构建入度数组 + int[] indegree = new int[numCourses]; + for (int[] edge : prerequisites) { + int from = edge[1], to = edge[0]; + // 节点 to 的入度加一 + indegree[to]++; + } + + // 根据入度初始化队列中节点 + Queue q = new LinkedList<>(); + for (int i = 0; i < numCourses; i++) { + if (indegree[i] == 0) { + // 节点 i 没有入度,即没有依赖的节点 + // 可以作为拓扑排序的起点,加入队列 + q.offer(i); + } + } + + // 记录遍历的节点个数 + int count = 0; + // 开始执行 BFS 遍历 + while (!q.isEmpty()) { + // 弹出节点 cur,并将它指向的节点的入度减一 + int cur = q.poll(); + count++; + for (int next : graph[cur]) { + indegree[next]--; + if (indegree[next] == 0) { + // 如果入度变为 0,说明 next 依赖的节点都已被遍历 + q.offer(next); + } + } + } + + // 如果所有节点都被遍历过,说明不成环 + return count == numCourses; + } + + public List[] buildGraph(int n, int[][] data) { + List[] graph = new LinkedList[n]; + for (int i = 0; i < n; i++) { + graph[i] = new LinkedList<>(); + } + + for (int[] edge : data) { + int from = edge[1], to = edge[0]; + graph[from].add(to); + } + return graph; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/topological_sort/\350\257\276\347\250\213\350\241\2502.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/topological_sort/\350\257\276\347\250\213\350\241\2502.java" new file mode 100644 index 0000000..998b686 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/topological_sort/\350\257\276\347\250\213\350\241\2502.java" @@ -0,0 +1,156 @@ +package io.github.dunwu.algorithm.graph.topological_sort; + +import org.junit.jupiter.api.Assertions; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +/** + * 210. 课程表 II + * + * @author Zhang Peng + * @date 2025-11-03 + */ +public class 课程表2 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertArrayEquals(new int[] { 0, 1 }, s.findOrder(2, new int[][] { { 1, 0 } })); + Assertions.assertArrayEquals(new int[] { 0, 2, 1, 3 }, + s.findOrder(4, new int[][] { { 1, 0 }, { 2, 0 }, { 3, 1 }, { 3, 2 } })); + + Solution2 s2 = new Solution2(); + Assertions.assertArrayEquals(new int[] { 0, 1 }, s2.findOrder(2, new int[][] { { 1, 0 } })); + Assertions.assertArrayEquals(new int[] { 0, 2, 1, 3 }, + s2.findOrder(4, new int[][] { { 1, 0 }, { 2, 0 }, { 3, 1 }, { 3, 2 } })); + } + + // 拓扑排序算法(DFS 版本) + static class Solution { + + // 记录后序遍历结果 + private List preorder; + // 记录一次递归堆栈中的节点 + boolean[] onPath; + // 记录节点是否被遍历过 + boolean[] visited; + // 记录图中是否有环 + boolean hasCycle = false; + + public int[] findOrder(int numCourses, int[][] prerequisites) { + List[] graph = buildGraph(numCourses, prerequisites); + preorder = new LinkedList<>(); + visited = new boolean[numCourses]; + onPath = new boolean[numCourses]; + + for (int i = 0; i < numCourses; i++) { + dfs(graph, i); + } + + // 有环图无法进行拓扑排序 + if (hasCycle) { return new int[0]; } + + // 逆后序遍历结果即为拓扑排序结果 + Collections.reverse(preorder); + int[] order = new int[numCourses]; + for (int i = 0; i < numCourses; i++) { + order[i] = preorder.get(i); + } + return order; + } + + public void dfs(List[] graph, int s) { + // 找到环,或已访问,则无需再遍历 + if (onPath[s]) { hasCycle = true; } + if (hasCycle || visited[s]) { return; } + + // 【前序】 + visited[s] = true; + onPath[s] = true; + for (int t : graph[s]) { + dfs(graph, t); + } + // 【后序】 + preorder.add(s); + onPath[s] = false; + } + + public List[] buildGraph(int n, int[][] data) { + List[] graph = new LinkedList[n]; + for (int i = 0; i < n; i++) { + graph[i] = new LinkedList<>(); + } + + for (int[] edge : data) { + int from = edge[1], to = edge[0]; + graph[from].add(to); + } + return graph; + } + + } + + // 拓扑排序算法(BFS 版本) + static class Solution2 { + + public int[] findOrder(int numCourses, int[][] prerequisites) { + // 建图,和环检测算法相同 + List[] graph = buildGraph(numCourses, prerequisites); + // 计算入度,和环检测算法相同 + int[] indegree = new int[numCourses]; + for (int[] edge : prerequisites) { + int from = edge[1], to = edge[0]; + indegree[to]++; + } + + // 根据入度初始化队列中的节点,和环检测算法相同 + Queue q = new LinkedList<>(); + for (int i = 0; i < numCourses; i++) { + if (indegree[i] == 0) { + q.offer(i); + } + } + + // 记录拓扑排序结果 + int[] res = new int[numCourses]; + // 记录遍历节点的顺序(索引) + int count = 0; + // 开始执行 BFS 算法 + while (!q.isEmpty()) { + int cur = q.poll(); + // 弹出节点的顺序即为拓扑排序结果 + res[count] = cur; + count++; + for (int next : graph[cur]) { + indegree[next]--; + if (indegree[next] == 0) { + q.offer(next); + } + } + } + + // 存在环,拓扑排序不存在 + if (count != numCourses) { + return new int[0]; + } + return res; + } + + public List[] buildGraph(int n, int[][] data) { + List[] graph = new LinkedList[n]; + for (int i = 0; i < n; i++) { + graph[i] = new LinkedList<>(); + } + + for (int[] edge : data) { + int from = edge[1], to = edge[0]; + graph[from].add(to); + } + return graph; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/union_find/\345\206\227\344\275\231\350\277\236\346\216\245.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/union_find/\345\206\227\344\275\231\350\277\236\346\216\245.java" new file mode 100644 index 0000000..8619903 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/union_find/\345\206\227\344\275\231\350\277\236\346\216\245.java" @@ -0,0 +1,82 @@ +package io.github.dunwu.algorithm.graph.union_find; + +import org.junit.jupiter.api.Assertions; + +/** + * 684. 冗余连接 + * + * @author Zhang Peng + * @date 2025-11-03 + */ +public class 冗余连接 { + + public static void main(String[] args) { + Solution s = new Solution(); + int[][] input = new int[][] { { 1, 2 }, { 1, 3 }, { 2, 3 } }; + int[][] input2 = new int[][] { { 1, 2 }, { 2, 3 }, { 3, 4 }, { 1, 4 }, { 1, 5 } }; + Assertions.assertArrayEquals(new int[] { 2, 3 }, s.findRedundantConnection(input)); + Assertions.assertArrayEquals(new int[] { 1, 4 }, s.findRedundantConnection(input2)); + } + + static class Solution { + + public int[] findRedundantConnection(int[][] edges) { + UF uf = new UF(edges.length + 1); + for (int[] edge : edges) { + int p = edge[0], q = edge[1]; + if (uf.connected(p, q)) { + return new int[] { p, q }; + } else { + uf.union(p, q); + } + } + return new int[0]; + } + + static class UF { + + // 连通分量个数 + private int count; + // 存储每个节点的父节点 + private int[] parent; + + public UF(int n) { + this.count = n; + parent = new int[n]; + for (int i = 0; i < n; i++) { + parent[i] = i; + } + } + + public void union(int p, int q) { + int rootP = find(p); + int rootQ = find(q); + + if (rootP == rootQ) { return; } + + parent[rootQ] = rootP; + count--; + } + + public boolean connected(int p, int q) { + int rootP = find(p); + int rootQ = find(q); + return rootP == rootQ; + } + + public int find(int x) { + if (parent[x] != x) { + parent[x] = find(parent[x]); + } + return parent[x]; + } + + public int count() { + return count; + } + + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/union_find/\347\255\211\345\274\217\346\226\271\347\250\213\347\232\204\345\217\257\346\273\241\350\266\263\346\200\247.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/union_find/\347\255\211\345\274\217\346\226\271\347\250\213\347\232\204\345\217\257\346\273\241\350\266\263\346\200\247.java" new file mode 100644 index 0000000..c78af57 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/union_find/\347\255\211\345\274\217\346\226\271\347\250\213\347\232\204\345\217\257\346\273\241\350\266\263\346\200\247.java" @@ -0,0 +1,94 @@ +package io.github.dunwu.algorithm.graph.union_find; + +import org.junit.jupiter.api.Assertions; + +/** + * 990. 等式方程的可满足性 + * + * @author Zhang Peng + * @date 2025-11-03 + */ +public class 等式方程的可满足性 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertFalse(s.equationsPossible(new String[] { "a==b", "b!=a" })); + Assertions.assertTrue(s.equationsPossible(new String[] { "b==a", "a==b" })); + Assertions.assertTrue(s.equationsPossible(new String[] { "a==b", "b==c", "a==c" })); + Assertions.assertFalse(s.equationsPossible(new String[] { "a==b", "b!=c", "c==a" })); + Assertions.assertTrue(s.equationsPossible(new String[] { "c==c", "b==d", "x!=z" })); + } + + static class Solution { + + public boolean equationsPossible(String[] equations) { + UF uf = new UF(26); + for (String exp : equations) { + if (exp.contains("==")) { + String[] vals = exp.split("=="); + int a = vals[0].charAt(0) - 'a'; + int b = vals[1].charAt(0) - 'a'; + uf.union(a, b); + } + } + + for (String exp : equations) { + if (exp.contains("!=")) { + String[] vals = exp.split("!="); + int a = vals[0].charAt(0) - 'a'; + int b = vals[1].charAt(0) - 'a'; + if (uf.connected(a, b)) { + return false; + } + } + } + return true; + } + + static class UF { + + // 连通分量个数 + private int count; + // 存储每个节点的父节点 + private int[] parent; + + public UF(int n) { + this.count = n; + parent = new int[n]; + for (int i = 0; i < n; i++) { + parent[i] = i; + } + } + + public void union(int p, int q) { + int rootP = find(p); + int rootQ = find(q); + + if (rootP == rootQ) { return; } + + parent[rootQ] = rootP; + count--; + } + + public boolean connected(int p, int q) { + int rootP = find(p); + int rootQ = find(q); + return rootP == rootQ; + } + + public int find(int x) { + if (parent[x] != x) { + parent[x] = find(parent[x]); + } + return parent[x]; + } + + public int count() { + return count; + } + + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/union_find/\350\242\253\345\233\264\347\273\225\347\232\204\345\214\272\345\237\237.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/union_find/\350\242\253\345\233\264\347\273\225\347\232\204\345\214\272\345\237\237.java" new file mode 100644 index 0000000..ae1114a --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/graph/union_find/\350\242\253\345\233\264\347\273\225\347\232\204\345\214\272\345\237\237.java" @@ -0,0 +1,136 @@ +package io.github.dunwu.algorithm.graph.union_find; + +import org.junit.jupiter.api.Assertions; + +/** + * 130. 被围绕的区域 + * + * @author Zhang Peng + * @date 2025-11-03 + */ +public class 被围绕的区域 { + + public static void main(String[] args) { + Solution s = new Solution(); + + char[][] input = new char[][] { + { 'X', 'X', 'X', 'X' }, + { 'X', 'O', 'O', 'X' }, + { 'X', 'X', 'O', 'X' }, + { 'X', 'O', 'X', 'X' } + }; + char[][] expect = new char[][] { + { 'X', 'X', 'X', 'X' }, + { 'X', 'X', 'X', 'X' }, + { 'X', 'X', 'X', 'X' }, + { 'X', 'O', 'X', 'X' } + }; + s.solve(input); + Assertions.assertArrayEquals(expect, input); + } + + static class Solution { + + private int m; + private int n; + int[][] direct = new int[][] { { 1, 0 }, { 0, 1 }, { 0, -1 }, { -1, 0 } }; + + public void solve(char[][] board) { + + if (board == null || board.length == 0) return; + m = board.length; + n = board[0].length; + + // 给 dummy 留一个额外位置 + UF uf = new UF(m * n + 1); + int dummy = m * n; + + // 将首列和末列的 O 与 dummy 连通 + for (int i = 0; i < m; i++) { + if (board[i][0] == 'O') { uf.union(index(i, 0), dummy); } + if (board[i][n - 1] == 'O') { uf.union(index(i, n - 1), dummy); } + } + + // 将首行和末行的 O 与 dummy 连通 + for (int j = 0; j < n; j++) { + if (board[0][j] == 'O') { uf.union(index(0, j), dummy); } + if (board[m - 1][j] == 'O') { uf.union(index(m - 1, j), dummy); } + } + + // 方向数组 d 是上下左右搜索的常用手法 + for (int i = 1; i < m - 1; i++) { + for (int j = 1; j < n - 1; j++) { + if (board[i][j] == 'O') { + // 将此 O 与上下左右的 O 连通 + for (int[] d : direct) { + int x = i + d[0], y = j + d[1]; + if (board[x][y] == 'O') { + uf.union(index(x, y), index(i, j)); + } + } + } + } + } + + // 所有不和 dummy 连通的 O,都要被替换 + for (int i = 1; i < m - 1; i++) { + for (int j = 1; j < n - 1; j++) { + int index = index(i, j); + if (!uf.connected(index, dummy)) { + board[i][j] = 'X'; + } + } + } + } + + public int index(int row, int col) { + return row * n + col; + } + + static class UF { + + // 连通分量个数 + private int count; + // 存储每个节点的父节点 + private int[] parent; + + public UF(int n) { + this.count = n; + parent = new int[n]; + for (int i = 0; i < n; i++) { + parent[i] = i; + } + } + + public void union(int p, int q) { + int rootP = find(p); + int rootQ = find(q); + + if (rootP == rootQ) { return; } + + parent[rootQ] = rootP; + count--; + } + + public boolean connected(int p, int q) { + int rootP = find(p); + int rootQ = find(q); + return rootP == rootQ; + } + + public int find(int x) { + if (parent[x] != x) { + parent[x] = find(parent[x]); + } + return parent[x]; + } + + public int count() { + return count; + } + + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/greedy/\346\225\260\347\273\204\346\213\206\345\210\206.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/greedy/\346\225\260\347\273\204\346\213\206\345\210\206.java" new file mode 100644 index 0000000..88d7ee5 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/greedy/\346\225\260\347\273\204\346\213\206\345\210\206.java" @@ -0,0 +1,35 @@ +package io.github.dunwu.algorithm.greedy; + +import org.junit.jupiter.api.Assertions; + +import java.util.Arrays; + +/** + * 561. 数组拆分 + * + * @author Zhang Peng + * @since 2018-11-05 + */ +public class 数组拆分 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(4, s.arrayPairSum(new int[] { 1, 4, 3, 2 })); + Assertions.assertEquals(9, s.arrayPairSum(new int[] { 6, 2, 6, 5, 1, 2 })); + } + + static class Solution { + + + public int arrayPairSum(int[] nums) { + Arrays.sort(nums); + int sum = 0; + for (int i = 0; i < nums.length; i+=2) { + sum += nums[i]; + } + return sum; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/greedy/\350\267\263\350\267\203\346\270\270\346\210\217.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/greedy/\350\267\263\350\267\203\346\270\270\346\210\217.java" new file mode 100644 index 0000000..bae6a44 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/greedy/\350\267\263\350\267\203\346\270\270\346\210\217.java" @@ -0,0 +1,57 @@ +package io.github.dunwu.algorithm.greedy; + +import org.junit.jupiter.api.Assertions; + +import java.util.Arrays; + +/** + * 55. 跳跃游戏 + * + * @author Zhang Peng + * @date 2025-11-10 + */ +public class 跳跃游戏 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(2, s.jump(new int[] { 2, 3, 1, 1, 4 })); + } + + static class Solution { + + int[] memo; + + public int jump(int[] nums) { + if (nums.length <= 1) { + return 0; + } + int n = nums.length; + // 备忘录都初始化为 n,相当于 INT_MAX + // 因为从 0 跳到 n - 1 最多 n - 1 步 + memo = new int[n]; + Arrays.fill(memo, n); + + return dp(nums, 0); + } + + int dp(int[] nums, int p) { + int n = nums.length; + if (p >= n - 1) { + return 0; + } + + int steps = nums[p]; + // 你可以选择跳 1 步,2 步... + for (int i = 1; i <= steps; i++) { + // 穷举每一个选择 + // 计算每一个子问题的结果 + int sub = dp(nums, p + i); + // 取其中最小的作为最终结果 + memo[p] = Math.min(memo[p], sub + 1); + } + return memo[p]; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/greedy/\350\267\263\350\267\203\346\270\270\346\210\2172.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/greedy/\350\267\263\350\267\203\346\270\270\346\210\2172.java" new file mode 100644 index 0000000..d1413bb --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/greedy/\350\267\263\350\267\203\346\270\270\346\210\2172.java" @@ -0,0 +1,31 @@ +package io.github.dunwu.algorithm.greedy; + +import org.junit.jupiter.api.Assertions; + +/** + * 53. 最大子数组和 + * + * @author Zhang Peng + * @date 2025-11-10 + */ +public class 跳跃游戏2 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertTrue(s.canJump(new int[] { 2, 3, 1, 1, 4 })); + } + + static class Solution { + + public boolean canJump(int[] nums) { + int farthest = 0; + for (int i = 0; i < nums.length; i++) { + farthest = Math.max(farthest, i + nums[i]); + if (farthest <= i) return false; + } + return farthest >= nums.length - 1; + } + + } + +} diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/hashtable/JewelsAndStones.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/hash/JewelsAndStones.java similarity index 96% rename from codes/algorithm/src/main/java/io/github/dunwu/algorithm/hashtable/JewelsAndStones.java rename to codes/algorithm/src/main/java/io/github/dunwu/algorithm/hash/JewelsAndStones.java index 788329f..57f5933 100644 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/hashtable/JewelsAndStones.java +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/hash/JewelsAndStones.java @@ -1,4 +1,4 @@ -package io.github.dunwu.algorithm.hashtable; +package io.github.dunwu.algorithm.hash; import java.util.HashSet; diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/hashtable/SubdomainVisitCount.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/hash/SubdomainVisitCount.java similarity index 98% rename from codes/algorithm/src/main/java/io/github/dunwu/algorithm/hashtable/SubdomainVisitCount.java rename to codes/algorithm/src/main/java/io/github/dunwu/algorithm/hash/SubdomainVisitCount.java index 2497364..7d7f733 100644 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/hashtable/SubdomainVisitCount.java +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/hash/SubdomainVisitCount.java @@ -1,4 +1,4 @@ -package io.github.dunwu.algorithm.hashtable; +package io.github.dunwu.algorithm.hash; import java.util.ArrayList; import java.util.HashMap; diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/hashtable/ToLowerCase.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/hash/ToLowerCase.java similarity index 95% rename from codes/algorithm/src/main/java/io/github/dunwu/algorithm/hashtable/ToLowerCase.java rename to codes/algorithm/src/main/java/io/github/dunwu/algorithm/hash/ToLowerCase.java index 885f1df..97af3d7 100644 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/hashtable/ToLowerCase.java +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/hash/ToLowerCase.java @@ -1,4 +1,4 @@ -package io.github.dunwu.algorithm.hashtable; +package io.github.dunwu.algorithm.hash; /* https://leetcode.com/problems/to-lower-case/ diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/ListNode.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/ListNode.java new file mode 100644 index 0000000..f8868a7 --- /dev/null +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/ListNode.java @@ -0,0 +1,114 @@ +package io.github.dunwu.algorithm.linkedlist; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public final class ListNode { + + public int val; + public ListNode next; + + public ListNode(int val) { this.val = val; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ListNode)) return false; + ListNode listNode = (ListNode) o; + return val == listNode.val && + Objects.equals(next, listNode.next); + } + + @Override + public int hashCode() { + return Objects.hash(val, next); + } + + public List toList() { + return ListNode.toList(this); + } + + public static ListNode createLinkedList(int[] arr) { + if (arr == null || arr.length == 0) { + return null; + } + ListNode head = new ListNode(arr[0]); + ListNode cur = head; + for (int i = 1; i < arr.length; i++) { + cur.next = new ListNode(arr[i]); + cur = cur.next; + } + return head; + } + + public static void addLast() { + + } + + public static ListNode buildList(int... list) { + ListNode head = new ListNode(-1); + ListNode node = head; + for (int val : list) { + node.next = new ListNode(val); + node = node.next; + } + return head.next; + } + + public static ListNode buildCycleList(int cyclePoint, int[] list) { + ListNode head = new ListNode(-1); + ListNode node = head; + ListNode cycleBeginNode = null; + for (int val : list) { + ListNode item = new ListNode(val); + if (cyclePoint == 0 && cycleBeginNode == null) { + cycleBeginNode = item; + } else { + cyclePoint--; + } + node.next = item; + node = node.next; + } + if (cycleBeginNode != null) { + node.next = cycleBeginNode; + } + return head.next; + } + + public static List toList(ListNode listNode) { + List list = new ArrayList<>(); + while (listNode != null) { + list.add(listNode.val); + listNode = listNode.next; + } + return list; + } + + public static void buildMetPot(ListNode listA, ListNode listB, int skipA, int skipB) { + ListNode pA = listA; + for (int i = 0; i < skipA; i++) { + pA = pA.next; + } + ListNode pB = listB; + for (int i = 0; i < skipB - 1; i++) { + pB = pB.next; + } + pB.next = pA; + } + + public static void main(String[] args) { + int[] arr = { 1, 2, 3, 4, 5 }; + ListNode head = createLinkedList(arr); + ListNode p = head; + while (p.next != null) { + p = p.next; + } + p.next = new ListNode(6); + while (head != null) { + System.out.println(head.val); + head = head.next; + } + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/base/\344\272\214\350\277\233\345\210\266\351\223\276\350\241\250\350\275\254\346\225\264\346\225\260.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/base/\344\272\214\350\277\233\345\210\266\351\223\276\350\241\250\350\275\254\346\225\264\346\225\260.java" new file mode 100644 index 0000000..2afe676 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/base/\344\272\214\350\277\233\345\210\266\351\223\276\350\241\250\350\275\254\346\225\264\346\225\260.java" @@ -0,0 +1,34 @@ +package io.github.dunwu.algorithm.linkedlist.base; + +import io.github.dunwu.algorithm.linkedlist.ListNode; +import org.junit.jupiter.api.Assertions; + +/** + * 二进制链表转整数 + * + * @author Zhang Peng + * @since 2020-06-09 + */ +public class 二进制链表转整数 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(5, s.getDecimalValue(ListNode.buildList(1, 0, 1))); + Assertions.assertEquals(0, s.getDecimalValue(ListNode.buildList(0))); + } + + static class Solution { + + public int getDecimalValue(ListNode head) { + int res = 0; + ListNode p = head; + while (p != null) { + res = res * 2 + p.val; + p = p.next; + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/base/\345\210\240\351\231\244\351\223\276\350\241\250\347\232\204\350\212\202\347\202\271.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/base/\345\210\240\351\231\244\351\223\276\350\241\250\347\232\204\350\212\202\347\202\271.java" new file mode 100644 index 0000000..97faea6 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/base/\345\210\240\351\231\244\351\223\276\350\241\250\347\232\204\350\212\202\347\202\271.java" @@ -0,0 +1,42 @@ +package io.github.dunwu.algorithm.linkedlist.base; + +import io.github.dunwu.algorithm.linkedlist.ListNode; +import org.junit.jupiter.api.Assertions; + +/** + * LCR 136. 删除链表的节点 + * + * @author Zhang Peng + * @date 2025-12-18 + */ +public class 删除链表的节点 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(ListNode.buildList(4, 5, 1), s.deleteNode(ListNode.buildList(4, 5, 1, 9), 9)); + Assertions.assertEquals(ListNode.buildList(4, 1, 9), s.deleteNode(ListNode.buildList(4, 5, 1, 9), 5)); + Assertions.assertEquals(ListNode.buildList(4, 5, 9), s.deleteNode(ListNode.buildList(4, 5, 1, 9), 1)); + Assertions.assertEquals(ListNode.buildList(5, 1, 9), s.deleteNode(ListNode.buildList(4, 5, 1, 9), 4)); + } + + static class Solution { + + public ListNode deleteNode(ListNode head, int val) { + ListNode dummy = new ListNode(-1); + dummy.next = head; + ListNode pre = dummy; + while (pre != null && pre.next != null) { + ListNode cur = pre.next; + if (cur.val == val) { + pre.next = cur.next; + pre = cur.next; + } else { + pre = pre.next; + } + } + return dummy.next; + } + + } + +} diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/DoublyLinkedList.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/demo/DoublyLinkedList.java similarity index 99% rename from codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/DoublyLinkedList.java rename to codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/demo/DoublyLinkedList.java index a5e05f8..9b4c126 100644 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/DoublyLinkedList.java +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/demo/DoublyLinkedList.java @@ -1,4 +1,4 @@ -package io.github.dunwu.algorithm.list; +package io.github.dunwu.algorithm.linkedlist.demo; import lombok.Getter; import lombok.Setter; diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/LRUBaseLinkedList.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/demo/LRUBaseLinkedList.java similarity index 98% rename from codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/LRUBaseLinkedList.java rename to codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/demo/LRUBaseLinkedList.java index 126fe30..de8e751 100644 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/LRUBaseLinkedList.java +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/demo/LRUBaseLinkedList.java @@ -1,4 +1,4 @@ -package io.github.dunwu.algorithm.list; +package io.github.dunwu.algorithm.linkedlist.demo; import java.util.Scanner; diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/LRUBasedArray.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/demo/LRUBasedArray.java similarity index 98% rename from codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/LRUBasedArray.java rename to codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/demo/LRUBasedArray.java index e7aa484..cd909e4 100644 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/LRUBasedArray.java +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/demo/LRUBasedArray.java @@ -1,4 +1,4 @@ -package io.github.dunwu.algorithm.list; +package io.github.dunwu.algorithm.linkedlist.demo; import java.util.HashMap; import java.util.Map; diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/MyLinkedList.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/demo/MyLinkedList.java similarity index 99% rename from codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/MyLinkedList.java rename to codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/demo/MyLinkedList.java index 6601d2e..be76a54 100644 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/MyLinkedList.java +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/demo/MyLinkedList.java @@ -1,4 +1,4 @@ -package io.github.dunwu.algorithm.list; +package io.github.dunwu.algorithm.linkedlist.demo; import java.util.NoSuchElementException; diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/SinglyLinkedList.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/demo/SinglyLinkedList.java similarity index 99% rename from codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/SinglyLinkedList.java rename to codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/demo/SinglyLinkedList.java index e661110..d519205 100644 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/SinglyLinkedList.java +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/demo/SinglyLinkedList.java @@ -1,4 +1,4 @@ -package io.github.dunwu.algorithm.list; +package io.github.dunwu.algorithm.linkedlist.demo; import lombok.Getter; import lombok.Setter; diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\215\225\351\223\276\350\241\250\347\244\272\344\276\213.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/demo/\345\215\225\351\223\276\350\241\250\347\244\272\344\276\213.java" similarity index 95% rename from "codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\215\225\351\223\276\350\241\250\347\244\272\344\276\213.java" rename to "codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/demo/\345\215\225\351\223\276\350\241\250\347\244\272\344\276\213.java" index 6e656a6..b3ba9ec 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\215\225\351\223\276\350\241\250\347\244\272\344\276\213.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/demo/\345\215\225\351\223\276\350\241\250\347\244\272\344\276\213.java" @@ -1,4 +1,4 @@ -package io.github.dunwu.algorithm.list; +package io.github.dunwu.algorithm.linkedlist.demo; import java.util.ArrayList; import java.util.List; @@ -65,7 +65,7 @@ public void remove(ListNode node) { * 删除首个值为 value 的节点 * * @param value 数据值 - * @return {@link io.github.dunwu.algorithm.list.单链表示例.ListNode} + * @return {@link ListNode} */ public E removeFirst(E value) { ListNode prev = this.head; @@ -124,7 +124,7 @@ public void clear() { * 从头开始查找,一旦发现有数值与查找值相等的节点,直接返回此节点。如果遍历结束,表明未找到节点,返回 null。 * * @param value 数据值 - * @return {@link io.github.dunwu.algorithm.list.单链表示例.ListNode} + * @return {@link ListNode} */ public ListNode find(E value) { ListNode node = this.head.next; diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\217\214\351\223\276\350\241\250\347\244\272\344\276\213.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/demo/\345\217\214\351\223\276\350\241\250\347\244\272\344\276\213.java" similarity index 98% rename from "codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\217\214\351\223\276\350\241\250\347\244\272\344\276\213.java" rename to "codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/demo/\345\217\214\351\223\276\350\241\250\347\244\272\344\276\213.java" index 02ede0a..f6db8af 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\217\214\351\223\276\350\241\250\347\244\272\344\276\213.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/demo/\345\217\214\351\223\276\350\241\250\347\244\272\344\276\213.java" @@ -1,4 +1,4 @@ -package io.github.dunwu.algorithm.list; +package io.github.dunwu.algorithm.linkedlist.demo; /** * @author Zhang Peng diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/divide/\346\216\222\345\272\217\351\223\276\350\241\250.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/divide/\346\216\222\345\272\217\351\223\276\350\241\250.java" new file mode 100644 index 0000000..9c6a30e --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/divide/\346\216\222\345\272\217\351\223\276\350\241\250.java" @@ -0,0 +1,72 @@ +package io.github.dunwu.algorithm.linkedlist.divide; + +import io.github.dunwu.algorithm.linkedlist.ListNode; +import org.junit.jupiter.api.Assertions; + +/** + * 148.排序链表 + * + * @author Zhang Peng + * @since 2020-06-09 + */ +public class 排序链表 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(ListNode.buildList(1, 2, 3, 4), s.sortList(ListNode.buildList(4, 2, 1, 3))); + Assertions.assertEquals(ListNode.buildList(-1, 0, 3, 4, 5), s.sortList(ListNode.buildList(-1, 5, 3, 4, 0))); + Assertions.assertEquals(ListNode.buildList(), s.sortList(ListNode.buildList())); + } + + public static class Solution { + + public ListNode sortList(ListNode head) { + // 如果链表为空或者只有一个节点,无需排序 + if (head == null || head.next == null) { + return head; + } + // 找到中间节点 head2,并断开 head2 与其前一个节点的连接 + // 比如 head=[4,2,1,3],那么 middleNode 调用结束后 head=[4,2] head2=[1,3] + ListNode mid = middleNode(head); + // 分治 + head = sortList(head); + mid = sortList(mid); + // 合并 + return mergeTwoLists(head, mid); + } + + // 876. 链表的中间结点(快慢指针) + private ListNode middleNode(ListNode head) { + ListNode pre = head; + ListNode slow = head; + ListNode fast = head; + while (fast != null && fast.next != null) { + pre = slow; // 记录 slow 的前一个节点 + slow = slow.next; + fast = fast.next.next; + } + pre.next = null; // 断开 slow 的前一个节点和 slow 的连接 + return slow; + } + + // 21. 合并两个有序链表(双指针) + private ListNode mergeTwoLists(ListNode list1, ListNode list2) { + ListNode dummy = new ListNode(-1); // 用哨兵节点简化代码逻辑 + ListNode cur = dummy; // cur 指向新链表的末尾 + while (list1 != null && list2 != null) { + if (list1.val < list2.val) { + cur.next = list1; // 把 list1 加到新链表中 + list1 = list1.next; + } else { // 注:相等的情况加哪个节点都是可以的 + cur.next = list2; // 把 list2 加到新链表中 + list2 = list2.next; + } + cur = cur.next; + } + cur.next = list1 != null ? list1 : list2; // 拼接剩余链表 + return dummy.next; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/palindrome/\345\233\236\346\226\207\351\223\276\350\241\250.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/palindrome/\345\233\236\346\226\207\351\223\276\350\241\250.java" new file mode 100644 index 0000000..dd4a500 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/palindrome/\345\233\236\346\226\207\351\223\276\350\241\250.java" @@ -0,0 +1,49 @@ +package io.github.dunwu.algorithm.linkedlist.palindrome; + +import io.github.dunwu.algorithm.linkedlist.ListNode; +import org.junit.jupiter.api.Assertions; + +import java.util.ArrayList; +import java.util.List; + +/** + * 234. 回文链表 + * 面试题 02.06. 回文链表 + * + * @author Zhang Peng + * @since 2020-06-09 + */ +public class 回文链表 { + + public static void main(String[] args) { + Solution s = new Solution(); + ListNode input = ListNode.buildList(1, 2, 2, 1); + Assertions.assertTrue(s.isPalindrome(input)); + ListNode input2 = ListNode.buildList(1, 2); + Assertions.assertFalse(s.isPalindrome(input2)); + } + + static class Solution { + + public boolean isPalindrome(ListNode head) { + List list = new ArrayList<>(); + ListNode p = head; + while (p != null) { + list.add(p.val); + p = p.next; + } + + int left = 0, right = list.size() - 1; + while (left < right) { + if (!list.get(left).equals(list.get(right))) { + return false; + } + left++; + right--; + } + return true; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/reverse/K\344\270\252\344\270\200\347\273\204\347\277\273\350\275\254\351\223\276\350\241\250.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/reverse/K\344\270\252\344\270\200\347\273\204\347\277\273\350\275\254\351\223\276\350\241\250.java" new file mode 100644 index 0000000..18143c9 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/reverse/K\344\270\252\344\270\200\347\273\204\347\277\273\350\275\254\351\223\276\350\241\250.java" @@ -0,0 +1,52 @@ +package io.github.dunwu.algorithm.linkedlist.reverse; + +import io.github.dunwu.algorithm.linkedlist.ListNode; +import org.junit.jupiter.api.Assertions; + +/** + * 25. K 个一组翻转链表 + * + * @author Zhang Peng + * @since 2020-06-09 + */ +public class K个一组翻转链表 { + + public static void main(String[] args) { + Solution s = new Solution(); + ListNode output = s.reverseKGroup(ListNode.buildList(1, 2, 3, 4, 5), 2); + Assertions.assertEquals(ListNode.buildList(2, 1, 4, 3, 5), output); + ListNode output2 = s.reverseKGroup(ListNode.buildList(1, 2, 3, 4, 5), 3); + Assertions.assertEquals(ListNode.buildList(3, 2, 1, 4, 5), output2); + } + + static class Solution { + + public ListNode reverseKGroup(ListNode head, int k) { + if (head == null || head.next == null) { return head; } + ListNode p = head; + for (int i = 0; i < k; i++) { + if (p == null) { return head; } + p = p.next; + } + ListNode newHead = reverseN(head, k); + head.next = reverseKGroup(p, k); + return newHead; + } + + public ListNode reverseN(ListNode head, int len) { + if (head == null) { return null; } + ListNode pre = null, cur = head; + for (int i = 0; i < len; i++) { + if (cur == null) { break; } + ListNode next = cur.next; + cur.next = pre; + pre = cur; + cur = next; + } + head.next = cur; + return pre; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/reverse/\345\217\215\350\275\254\351\223\276\350\241\250.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/reverse/\345\217\215\350\275\254\351\223\276\350\241\250.java" new file mode 100644 index 0000000..ac54c4c --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/reverse/\345\217\215\350\275\254\351\223\276\350\241\250.java" @@ -0,0 +1,44 @@ +package io.github.dunwu.algorithm.linkedlist.reverse; + +import io.github.dunwu.algorithm.linkedlist.ListNode; +import org.junit.jupiter.api.Assertions; + +/** + * 206. 反转链表 + * + * @author Zhang Peng + * @since 2020-06-09 + */ +public class 反转链表 { + + public static void main(String[] args) { + + Solution s = new Solution(); + + ListNode result = s.reverseList(ListNode.buildList(1, 2, 3, 4)); + Assertions.assertEquals(ListNode.buildList(4, 3, 2, 1), result); + + ListNode result2 = s.reverseList(ListNode.buildList(1, 2)); + Assertions.assertEquals(ListNode.buildList(2, 1), result2); + + ListNode result3 = s.reverseList(ListNode.buildList()); + Assertions.assertEquals(ListNode.buildList(), result3); + } + + static class Solution { + + public ListNode reverseList(ListNode head) { + if (head == null || head.next == null) { return head; } + ListNode pre = null, cur = head; + while (cur != null) { + ListNode next = cur.next; + cur.next = pre; + pre = cur; + cur = next; + } + return pre; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/reverse/\345\217\215\350\275\254\351\223\276\350\241\2502.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/reverse/\345\217\215\350\275\254\351\223\276\350\241\2502.java" new file mode 100644 index 0000000..74aca5d --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/reverse/\345\217\215\350\275\254\351\223\276\350\241\2502.java" @@ -0,0 +1,64 @@ +package io.github.dunwu.algorithm.linkedlist.reverse; + +import io.github.dunwu.algorithm.linkedlist.ListNode; +import org.junit.jupiter.api.Assertions; + +/** + * 92. 反转链表 II + * + * @author Zhang Peng + * @date 2025-01-20 + */ +public class 反转链表2 { + + public static void main(String[] args) { + Solution s = new Solution(); + ListNode result = s.reverseBetween(ListNode.buildList(1, 2, 3, 4, 5), 2, 4); + Assertions.assertEquals(ListNode.buildList(1, 4, 3, 2, 5), result); + ListNode result2 = s.reverseBetween(ListNode.buildList(3, 5), 1, 2); + Assertions.assertEquals(ListNode.buildList(5, 3), result2); + } + + static class Solution { + + public ListNode reverseBetween(ListNode head, int m, int n) { + if (m == 1) { + return reverseN(head, n); + } + // 找到第 m 个节点的前驱 + ListNode pre = head; + for (int i = 1; i < m - 1; i++) { + pre = pre.next; + } + // 从第 m 个节点开始反转 + pre.next = reverseN(pre.next, n - m + 1); + return head; + } + + ListNode reverseN(ListNode head, int n) { + if (head == null || head.next == null) { + return head; + } + ListNode pre, cur, nxt; + pre = null; + cur = head; + nxt = head.next; + while (n > 0) { + cur.next = pre; + pre = cur; + cur = nxt; + if (nxt != null) { + nxt = nxt.next; + } + n--; + } + // 此时的 cur 是第 n + 1 个节点,head 是反转后的尾结点 + head.next = cur; + + // 此时的 pre 是反转后的头结点 + return pre; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/reverse/\346\227\213\350\275\254\351\223\276\350\241\250.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/reverse/\346\227\213\350\275\254\351\223\276\350\241\250.java" new file mode 100644 index 0000000..a27b82a --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/reverse/\346\227\213\350\275\254\351\223\276\350\241\250.java" @@ -0,0 +1,82 @@ +package io.github.dunwu.algorithm.linkedlist.reverse; + +import io.github.dunwu.algorithm.linkedlist.ListNode; +import org.junit.jupiter.api.Assertions; + +/** + * 61. 旋转链表 + * + * @author Zhang Peng + * @since 2025-11-20 + */ +public class 旋转链表 { + + public static void main(String[] args) { + Solution s = new Solution(); + + ListNode input = ListNode.buildList(1, 2, 3, 4, 5); + ListNode output = s.rotateRight(input, 2); + Assertions.assertEquals(ListNode.buildList(4, 5, 1, 2, 3), output); + + ListNode input2 = ListNode.buildList(0, 1, 2); + ListNode output2 = s.rotateRight(input2, 4); + Assertions.assertEquals(ListNode.buildList(2, 0, 1), output2); + + ListNode input3 = ListNode.buildList(1, 2); + ListNode output3 = s.rotateRight(input3, 1); + Assertions.assertEquals(ListNode.buildList(2, 1), output3); + + ListNode input4 = ListNode.buildList(1, 2); + ListNode output4 = s.rotateRight(input4, 3); + Assertions.assertEquals(ListNode.buildList(2, 1), output4); + } + + static class Solution { + + public ListNode rotateRight(ListNode head, int k) { + if (head == null || head.next == null) { + return head; + } + + ListNode dummy = new ListNode(-1); + dummy.next = head; + + ListNode newLast = lastFromEnd(head, k + 1); + ListNode last = newLast; + while (last.next != null) { + last = last.next; + } + + last.next = head; + dummy.next = newLast.next; + newLast.next = null; + + return dummy.next; + } + + public ListNode lastFromEnd(ListNode head, int k) { + + if (head == null || head.next == null) { + return null; + } + + int i = 0; + ListNode slow = head, fast = head; + while (i < k) { + i++; + if (fast == null) { + fast = head; + } + fast = fast.next; + } + + // fast 先走 k 步后,slow 从 head 开始出发,当 fast 到底,slow 正好是倒数第 k 个节点 + while (fast != null) { + slow = slow.next; + fast = fast.next; + } + return slow; + } + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\344\270\244\346\225\260\347\233\270\345\212\240.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\344\270\244\346\225\260\347\233\270\345\212\240.java" new file mode 100644 index 0000000..69de0e0 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\344\270\244\346\225\260\347\233\270\345\212\240.java" @@ -0,0 +1,65 @@ +package io.github.dunwu.algorithm.linkedlist.two_pointer; + +import io.github.dunwu.algorithm.linkedlist.ListNode; +import org.junit.jupiter.api.Assertions; + +/** + * 2. 两数相加 + * + * @author Zhang Peng + * @since 2020-06-09 + */ +public class 两数相加 { + + public static void main(String[] args) { + + Solution s = new Solution(); + + ListNode output1 = s.addTwoNumbers(ListNode.buildList(2, 4, 3), ListNode.buildList(5, 6, 4)); + Assertions.assertEquals(ListNode.buildList(7, 0, 8), output1); + + ListNode output2 = s.addTwoNumbers(ListNode.buildList(0), ListNode.buildList(0)); + Assertions.assertEquals(ListNode.buildList(0), output2); + + ListNode output3 = s.addTwoNumbers(ListNode.buildList(9, 9, 9, 9, 9, 9, 9), ListNode.buildList(9, 9, 9, 9)); + Assertions.assertEquals(ListNode.buildList(8, 9, 9, 9, 0, 0, 0, 1), output3); + } + + static class Solution { + + public ListNode addTwoNumbers(ListNode l1, ListNode l2) { + // 在两条链表上的指针 + ListNode p1 = l1, p2 = l2; + // 虚拟头结点(构建新链表时的常用技巧) + ListNode dummy = new ListNode(-1); + // 指针 p 负责构建新链表 + ListNode p = dummy; + // 记录进位 + int carry = 0; + // 开始执行加法,两条链表走完且没有进位时才能结束循环 + while (p1 != null || p2 != null || carry > 0) { + + int val = 0; + if (p1 != null) { + val += p1.val; + p1 = p1.next; + } + if (p2 != null) { + val += p2.val; + p2 = p2.next; + } + if (carry > 0) { + val += carry; + } + + carry = val / 10; + val = val % 10; + p.next = new ListNode(val); + p = p.next; + } + return dummy.next; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\344\270\244\346\225\260\347\233\270\345\212\2402.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\344\270\244\346\225\260\347\233\270\345\212\2402.java" new file mode 100644 index 0000000..445b9d7 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\344\270\244\346\225\260\347\233\270\345\212\2402.java" @@ -0,0 +1,79 @@ +package io.github.dunwu.algorithm.linkedlist.two_pointer; + +import io.github.dunwu.algorithm.linkedlist.ListNode; +import org.junit.jupiter.api.Assertions; + +/** + * 445. 两数相加 II + * + * @author Zhang Peng + * @date 2025-01-21 + */ +public class 两数相加2 { + + public static void main(String[] args) { + Solution s = new Solution(); + ListNode result = s.addTwoNumbers(ListNode.buildList(7, 2, 4, 3), ListNode.buildList(5, 6, 4)); + Assertions.assertEquals(ListNode.buildList(7, 8, 0, 7), result); + ListNode result2 = s.addTwoNumbers(ListNode.buildList(2, 4, 3), ListNode.buildList(5, 6, 4)); + Assertions.assertEquals(ListNode.buildList(8, 0, 7), result2); + ListNode result3 = s.addTwoNumbers(ListNode.buildList(0), ListNode.buildList(0)); + Assertions.assertEquals(ListNode.buildList(0), result3); + } + + public static class Solution { + + public ListNode addTwoNumbers(ListNode l1, ListNode l2) { + ListNode r1 = reverse(l1); + ListNode r2 = reverse(l2); + ListNode res = doAddTwoNumbers(r1, r2); + return reverse(res); + } + + public ListNode reverse(ListNode head) { + ListNode pre = null, cur = head; + while (cur != null) { + ListNode next = cur.next; + cur.next = pre; + pre = cur; + cur = next; + } + return pre; + } + + public ListNode doAddTwoNumbers(ListNode l1, ListNode l2) { + // 在两条链表上的指针 + ListNode p1 = l1, p2 = l2; + // 虚拟头结点(构建新链表时的常用技巧) + ListNode dummy = new ListNode(-1); + // 指针 p 负责构建新链表 + ListNode p = dummy; + // 记录进位 + int carry = 0; + // 开始执行加法,两条链表走完且没有进位时才能结束循环 + while (p1 != null || p2 != null || carry > 0) { + + int val = 0; + if (p1 != null) { + val += p1.val; + p1 = p1.next; + } + if (p2 != null) { + val += p2.val; + p2 = p2.next; + } + if (carry > 0) { + val += carry; + } + + carry = val / 10; + val = val % 10; + p.next = new ListNode(val); + p = p.next; + } + return dummy.next; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\345\210\206\351\232\224\351\223\276\350\241\250.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\345\210\206\351\232\224\351\223\276\350\241\250.java" new file mode 100644 index 0000000..2ab09c3 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\345\210\206\351\232\224\351\223\276\350\241\250.java" @@ -0,0 +1,101 @@ +package io.github.dunwu.algorithm.linkedlist.two_pointer; + +import io.github.dunwu.algorithm.linkedlist.ListNode; +import org.junit.jupiter.api.Assertions; + +/** + * 86. 分隔链表 + * + * @author Zhang Peng + * @since 2020-07-06 + */ +public class 分隔链表 { + + public static void main(String[] args) { + + Solution s = new Solution(); + + Assertions.assertEquals(ListNode.buildList(1, 2, 2, 4, 3, 5), + s.partition(ListNode.buildList(1, 4, 3, 2, 5, 2), 3)); + Assertions.assertEquals(ListNode.buildList(1, 2), s.partition(ListNode.buildList(2, 1), 2)); + Assertions.assertEquals(ListNode.buildList(1, 2, 3), s.partition(ListNode.buildList(3, 1, 2), 3)); + Assertions.assertEquals(ListNode.buildList(1, 0, 4, 3, 5, 2), + s.partition(ListNode.buildList(1, 4, 3, 0, 5, 2), 2)); + + Solution2 s2 = new Solution2(); + Assertions.assertEquals(ListNode.buildList(1, 2, 2, 4, 3, 5), + s2.partition(ListNode.buildList(1, 4, 3, 2, 5, 2), 3)); + Assertions.assertEquals(ListNode.buildList(1, 2), s2.partition(ListNode.buildList(2, 1), 2)); + Assertions.assertEquals(ListNode.buildList(1, 2, 3), s2.partition(ListNode.buildList(3, 1, 2), 3)); + Assertions.assertEquals(ListNode.buildList(1, 0, 4, 3, 5, 2), + s2.partition(ListNode.buildList(1, 4, 3, 0, 5, 2), 2)); + } + + static class Solution { + + public ListNode partition(ListNode head, int x) { + + if (head == null) { return null; } + + ListNode dummy = new ListNode(-1); + dummy.next = head; + + // 找到大于等于 x 的节点的前一个节点 + ListNode l = dummy, r = dummy.next; + while (r != null) { + + while (l.next != null && l.next.val < x) { + l = l.next; + } + if (l.next == null) { + break; + } + + r = l.next; + while (r.next != null && r.next.val >= x) { + r = r.next; + } + if (r.next == null) { + break; + } + + // 替换节点 + ListNode tmp = r.next; + r.next = tmp.next; + tmp.next = l.next; + l.next = tmp; + + l = l.next; + r = r.next; + } + + return dummy.next; + } + + } + + static class Solution2 { + + public ListNode partition(ListNode head, int x) { + ListNode dummy1 = new ListNode(-1); + ListNode dummy2 = new ListNode(-1); + + ListNode d1 = dummy1, d2 = dummy2, p = head; + while (p != null) { + if (p.val < x) { + d1.next = p; + d1 = d1.next; + } else { + d2.next = p; + d2 = d2.next; + } + p = p.next; + } + d2.next = null; + d1.next = dummy2.next; + return dummy1.next; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\345\210\240\351\231\244\346\216\222\345\272\217\351\223\276\350\241\250\344\270\255\347\232\204\351\207\215\345\244\215\345\205\203\347\264\240.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\345\210\240\351\231\244\346\216\222\345\272\217\351\223\276\350\241\250\344\270\255\347\232\204\351\207\215\345\244\215\345\205\203\347\264\240.java" new file mode 100644 index 0000000..8d17469 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\345\210\240\351\231\244\346\216\222\345\272\217\351\223\276\350\241\250\344\270\255\347\232\204\351\207\215\345\244\215\345\205\203\347\264\240.java" @@ -0,0 +1,36 @@ +package io.github.dunwu.algorithm.linkedlist.two_pointer; + +import io.github.dunwu.algorithm.linkedlist.ListNode; +import org.junit.jupiter.api.Assertions; + +/** + * 83. 删除排序链表中的重复元素 + * + * @author Zhang Peng + * @since 2020-06-09 + */ +public class 删除排序链表中的重复元素 { + + public static void main(String[] args) { + Solution s = new Solution(); + ListNode input = ListNode.buildList(1, 1, 2); + Assertions.assertEquals(ListNode.buildList(1, 2), s.deleteDuplicates(input)); + ListNode input2 = ListNode.buildList(1, 1, 2, 3, 3); + Assertions.assertEquals(ListNode.buildList(1, 2, 3), s.deleteDuplicates(input2)); + } + + static class Solution { + + public ListNode deleteDuplicates(ListNode head) { + ListNode pre = head, cur = head.next; + while (cur != null) { + pre.next = cur.next; + pre = cur; + cur = cur.next; + } + return head; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\345\210\240\351\231\244\346\216\222\345\272\217\351\223\276\350\241\250\344\270\255\347\232\204\351\207\215\345\244\215\345\205\203\347\264\2402.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\345\210\240\351\231\244\346\216\222\345\272\217\351\223\276\350\241\250\344\270\255\347\232\204\351\207\215\345\244\215\345\205\203\347\264\2402.java" new file mode 100644 index 0000000..94cc3af --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\345\210\240\351\231\244\346\216\222\345\272\217\351\223\276\350\241\250\344\270\255\347\232\204\351\207\215\345\244\215\345\205\203\347\264\2402.java" @@ -0,0 +1,62 @@ +package io.github.dunwu.algorithm.linkedlist.two_pointer; + +import io.github.dunwu.algorithm.linkedlist.ListNode; +import org.junit.jupiter.api.Assertions; + +/** + * 82. 删除排序链表中的重复元素 II + * + * @author Zhang Peng + * @since 2020-06-09 + */ +public class 删除排序链表中的重复元素2 { + + public static void main(String[] args) { + + Solution s = new Solution(); + + ListNode input = ListNode.buildList(1, 2, 3, 3, 4, 4, 5); + Assertions.assertEquals(ListNode.buildList(1, 2, 5), s.deleteDuplicates(input)); + + ListNode input2 = ListNode.buildList(1, 1, 1, 2, 3); + Assertions.assertEquals(ListNode.buildList(2, 3), s.deleteDuplicates(input2)); + + ListNode input3 = ListNode.buildList(1, 2, 2); + Assertions.assertEquals(ListNode.buildList(1), s.deleteDuplicates(input3)); + } + + public static class Solution { + + public ListNode deleteDuplicates(ListNode head) { + // 将原链表分解为两条链表 + // 一条链表存放不重复的节点,另一条链表存放重复的节点 + // 运用虚拟头结点技巧,题目说了 node.val <= 100,所以用 101 作为虚拟头结点 + ListNode dummyUniq = new ListNode(101); + ListNode dummyDup = new ListNode(101); + + ListNode pUniq = dummyUniq, pDup = dummyDup; + ListNode p = head; + + while (p != null) { + if ((p.next != null && p.val == p.next.val) || p.val == pDup.val) { + // 发现重复节点,接到重复链表后面 + pDup.next = p; + pDup = pDup.next; + } else { + // 不是重复节点,接到不重复链表后面 + pUniq.next = p; + pUniq = pUniq.next; + } + + p = p.next; + // 将原链表和新链表断开 + pUniq.next = null; + pDup.next = null; + } + + return dummyUniq.next; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\345\210\240\351\231\244\351\223\276\350\241\250\347\232\204\345\200\222\346\225\260\347\254\254N\344\270\252\347\273\223\347\202\271.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\345\210\240\351\231\244\351\223\276\350\241\250\347\232\204\345\200\222\346\225\260\347\254\254N\344\270\252\347\273\223\347\202\271.java" new file mode 100644 index 0000000..d3e6e42 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\345\210\240\351\231\244\351\223\276\350\241\250\347\232\204\345\200\222\346\225\260\347\254\254N\344\270\252\347\273\223\347\202\271.java" @@ -0,0 +1,59 @@ +package io.github.dunwu.algorithm.linkedlist.two_pointer; + +import io.github.dunwu.algorithm.linkedlist.ListNode; +import org.junit.jupiter.api.Assertions; + +/** + * 19. 删除链表的倒数第 N 个结点 + * + * @author Zhang Peng + * @since 2020-06-09 + */ +public class 删除链表的倒数第N个结点 { + + public static void main(String[] args) { + + Solution s = new Solution(); + + ListNode input1 = ListNode.buildList(1, 2, 3, 4, 5); + ListNode output1 = s.removeNthFromEnd(input1, 2); + Assertions.assertEquals(ListNode.buildList(1, 2, 3, 5), output1); + + ListNode input2 = ListNode.buildList(1); + ListNode output2 = s.removeNthFromEnd(input2, 1); + Assertions.assertEquals(ListNode.buildList(), output2); + + ListNode input3 = ListNode.buildList(1, 2); + ListNode output3 = s.removeNthFromEnd(input3, 1); + Assertions.assertEquals(ListNode.buildList(1), output3); + } + + static class Solution { + + public ListNode removeNthFromEnd(ListNode head, int n) { + ListNode dummy = new ListNode(-1); + dummy.next = head; + ListNode node = findFromEnd(dummy, n + 1); + node.next = node.next.next; + return dummy.next; + } + + public ListNode findFromEnd(ListNode head, int k) { + ListNode p1 = head; + // p1 先走 k 步 + for (int i = 0; i < k; i++) { + p1 = p1.next; + } + ListNode p2 = head; + // p1 和 p2 同时走 n - k 步 + while (p1 != null) { + p2 = p2.next; + p1 = p1.next; + } + // p2 现在指向第 n - k + 1 个节点,即倒数第 k 个节点 + return p2; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\345\220\210\345\271\266K\344\270\252\345\215\207\345\272\217\351\223\276\350\241\250.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\345\220\210\345\271\266K\344\270\252\345\215\207\345\272\217\351\223\276\350\241\250.java" new file mode 100644 index 0000000..419f5e2 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\345\220\210\345\271\266K\344\270\252\345\215\207\345\272\217\351\223\276\350\241\250.java" @@ -0,0 +1,67 @@ +package io.github.dunwu.algorithm.linkedlist.two_pointer; + +import io.github.dunwu.algorithm.linkedlist.ListNode; +import org.junit.jupiter.api.Assertions; + +/** + * 23. 合并 K 个升序链表 + * + * @author Zhang Peng + * @since 2020-06-09 + */ +public class 合并K个升序链表 { + + public static void main(String[] args) { + + Solution s = new Solution(); + ListNode head1 = ListNode.buildList(1, 4, 5); + ListNode head2 = ListNode.buildList(1, 3, 4); + ListNode head3 = ListNode.buildList(2, 6); + ListNode result = s.mergeKLists(new ListNode[] { head1, head2, head3 }); + Assertions.assertEquals(ListNode.buildList(1, 1, 2, 3, 4, 4, 5, 6), result); + + ListNode[] array2 = new ListNode[] {}; + ListNode result2 = s.mergeKLists(array2); + Assertions.assertEquals(ListNode.buildList(), result2); + } + + static class Solution { + + public ListNode mergeKLists(ListNode[] lists) { + if (lists == null || lists.length == 0) return null; + ListNode l1 = lists[0]; + for (int i = 1; i < lists.length; i++) { + ListNode l2 = lists[i]; + l1 = mergeTwoLists(l1, l2); + } + return l1; + } + + public ListNode mergeTwoLists(ListNode l1, ListNode l2) { + // 虚拟头结点 + ListNode dummy = new ListNode(-1), p = dummy; + ListNode p1 = l1, p2 = l2; + + while (p1 != null && p2 != null) { + // 比较 p1 和 p2 两个指针 + // 将值较小的的节点接到 p 指针 + if (p1.val > p2.val) { + p.next = p2; + p2 = p2.next; + } else { + p.next = p1; + p1 = p1.next; + } + // p 指针不断前进 + p = p.next; + } + + if (p1 != null) { p.next = p1; } + if (p2 != null) { p.next = p2; } + + return dummy.next; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\351\223\276\350\241\250.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\351\223\276\350\241\250.java" new file mode 100644 index 0000000..8da3bd1 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\351\223\276\350\241\250.java" @@ -0,0 +1,51 @@ +package io.github.dunwu.algorithm.linkedlist.two_pointer; + +import io.github.dunwu.algorithm.linkedlist.ListNode; +import org.junit.jupiter.api.Assertions; + +/** + * 21. 合并两个有序链表 + * + * @author Zhang Peng + * @since 2020-06-09 + */ +public class 合并两个有序链表 { + + public static void main(String[] args) { + Solution s = new Solution(); + ListNode h1 = ListNode.buildList(1, 2, 4); + ListNode h2 = ListNode.buildList(1, 3, 4); + ListNode result = s.mergeTwoLists(h1, h2); + Assertions.assertEquals(ListNode.buildList(1, 1, 2, 3, 4, 4), result); + } + + static class Solution { + + public ListNode mergeTwoLists(ListNode l1, ListNode l2) { + // 虚拟头结点 + ListNode dummy = new ListNode(-1), p = dummy; + ListNode p1 = l1, p2 = l2; + + while (p1 != null && p2 != null) { + // 比较 p1 和 p2 两个指针 + // 将值较小的的节点接到 p 指针 + if (p1.val > p2.val) { + p.next = p2; + p2 = p2.next; + } else { + p.next = p1; + p1 = p1.next; + } + // p 指针不断前进 + p = p.next; + } + + if (p1 != null) { p.next = p1; } + if (p2 != null) { p.next = p2; } + + return dummy.next; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\345\245\207\345\201\266\351\223\276\350\241\250.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\345\245\207\345\201\266\351\223\276\350\241\250.java" new file mode 100644 index 0000000..5d3c5be --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\345\245\207\345\201\266\351\223\276\350\241\250.java" @@ -0,0 +1,46 @@ +package io.github.dunwu.algorithm.linkedlist.two_pointer; + +import io.github.dunwu.algorithm.linkedlist.ListNode; +import org.junit.jupiter.api.Assertions; + +/** + * 328. 奇偶链表 + * + * @author Zhang Peng + * @since 2020-07-08 + */ +public class 奇偶链表 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(ListNode.buildList(1, 3, 5, 2, 4), s.oddEvenList(ListNode.buildList(1, 2, 3, 4, 5))); + Assertions.assertEquals(ListNode.buildList(2, 3, 6, 7, 1, 5, 4), + s.oddEvenList(ListNode.buildList(2, 1, 3, 5, 6, 4, 7))); + } + + static class Solution { + + public ListNode oddEvenList(ListNode head) { + ListNode oddDummy = new ListNode(-1); + ListNode evenDummy = new ListNode(-1); + ListNode p = head, o = oddDummy, e = evenDummy; + int i = 1; + while (p != null) { + if (i % 2 == 0) { + e.next = p; + e = e.next; + } else { + o.next = p; + o = o.next; + } + p = p.next; + i++; + } + e.next = null; + o.next = evenDummy.next; + return oddDummy.next; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\347\216\257\345\275\242\351\223\276\350\241\250.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\347\216\257\345\275\242\351\223\276\350\241\250.java" new file mode 100644 index 0000000..f73021e --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\347\216\257\345\275\242\351\223\276\350\241\250.java" @@ -0,0 +1,50 @@ +package io.github.dunwu.algorithm.linkedlist.two_pointer; + +import io.github.dunwu.algorithm.linkedlist.ListNode; +import org.junit.jupiter.api.Assertions; + +/** + * 141. 环形链表 + * + * @author Zhang Peng + * @since 2020-06-09 + */ +public class 环形链表 { + + public static void main(String[] args) { + + Solution s = new Solution(); + ListNode head = ListNode.buildList(3, 2, 0, -4); + Assertions.assertFalse(s.hasCycle(head)); + + ListNode head2 = ListNode.buildCycleList(1, new int[] { 3, 2, 0, -4 }); + Assertions.assertTrue(s.hasCycle(head2)); + + ListNode head3 = ListNode.buildCycleList(0, new int[] { 1, 2 }); + Assertions.assertTrue(s.hasCycle(head3)); + + ListNode head4 = ListNode.buildCycleList(1, new int[] { 1 }); + Assertions.assertFalse(s.hasCycle(head4)); + } + + static class Solution { + + public boolean hasCycle(ListNode head) { + // 快慢指针初始化指向 head + ListNode slow = head, fast = head; + // 快指针走到末尾时停止 + while (fast != null && fast.next != null) { + // 慢指针走一步,快指针走两步 + slow = slow.next; + fast = fast.next.next; + // 快慢指针相遇,说明含有环 + if (slow == fast) { + return true; + } + } + return false; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\347\216\257\345\275\242\351\223\276\350\241\2502.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\347\216\257\345\275\242\351\223\276\350\241\2502.java" new file mode 100644 index 0000000..1c6abd9 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\347\216\257\345\275\242\351\223\276\350\241\2502.java" @@ -0,0 +1,60 @@ +package io.github.dunwu.algorithm.linkedlist.two_pointer; + +import io.github.dunwu.algorithm.linkedlist.ListNode; +import org.junit.jupiter.api.Assertions; + +/** + * 142. 环形链表 II + * + * @author Zhang Peng + * @since 2020-07-08 + */ +public class 环形链表2 { + + public static void main(String[] args) { + + Solution s = new Solution(); + + ListNode input = ListNode.buildList(3, 2, 0, -4); + Assertions.assertNull(s.detectCycle(input)); + + ListNode input2 = ListNode.buildList(1); + Assertions.assertNull(s.detectCycle(input2)); + + ListNode input3 = ListNode.buildCycleList(1, new int[] { 3, 2, 0, -4 }); + Assertions.assertEquals(2, s.detectCycle(input3).val); + + ListNode input4 = ListNode.buildCycleList(0, new int[] { 1, 2 }); + Assertions.assertEquals(1, s.detectCycle(input4).val); + } + + static class Solution { + + public ListNode detectCycle(ListNode head) { + ListNode fast, slow; + fast = slow = head; + while (fast != null && fast.next != null) { + fast = fast.next.next; + slow = slow.next; + if (fast == slow) break; + } + + // fast 遇到空指针说明没有环 + if (fast == null || fast.next == null) { + return null; + } + + // 重新指向头结点 + slow = head; + + // 快慢指针同步前进,相交点就是环起点 + while (slow != fast) { + fast = fast.next; + slow = slow.next; + } + return slow; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\347\233\270\344\272\244\351\223\276\350\241\250.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\347\233\270\344\272\244\351\223\276\350\241\250.java" new file mode 100644 index 0000000..e222dfe --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\347\233\270\344\272\244\351\223\276\350\241\250.java" @@ -0,0 +1,60 @@ +package io.github.dunwu.algorithm.linkedlist.two_pointer; + +import io.github.dunwu.algorithm.linkedlist.ListNode; +import org.junit.jupiter.api.Assertions; + +/** + * 相交链表 + * + * @author Zhang Peng + * @since 2020-06-09 + */ +public class 相交链表 { + + public static void main(String[] args) { + + Solution s = new Solution(); + + ListNode listA = ListNode.buildList(4, 1, 8, 4, 5); + ListNode listB = ListNode.buildList(5, 6, 1, 8, 4, 5); + ListNode.buildMetPot(listA, listB, 2, 3); + ListNode result = s.getIntersectionNode(listA, listB); + Assertions.assertEquals(8, result.val); + + ListNode listA2 = ListNode.buildList(1, 9, 1, 2, 4); + ListNode listB2 = ListNode.buildList(3, 2, 4); + ListNode.buildMetPot(listA2, listB2, 3, 1); + ListNode result2 = s.getIntersectionNode(listA2, listB2); + Assertions.assertEquals(2, result2.val); + + ListNode listA3 = ListNode.buildList(2, 6, 4); + ListNode listB3 = ListNode.buildList(1, 5); + ListNode result3 = s.getIntersectionNode(listA3, listB3); + Assertions.assertNull(result3); + } + + static class Solution { + + public ListNode getIntersectionNode(ListNode headA, ListNode headB) { + // pA 指向 A 链表头结点,pB 指向 B 链表头结点 + ListNode pA = headA, pB = headB; + while (pA != pB) { + // pA 走一步,如果走到 A 链表末尾,转到 B 链表 + if (pA == null) { + pA = headB; + } else { + pA = pA.next; + } + // pB 走一步,如果走到 B 链表末尾,转到 A 链表 + if (pB == null) { + pB = headA; + } else { + pB = pB.next; + } + } + return pA; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\347\247\273\351\231\244\351\207\215\345\244\215\350\212\202\347\202\271.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\347\247\273\351\231\244\351\207\215\345\244\215\350\212\202\347\202\271.java" new file mode 100644 index 0000000..0bba21c --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\347\247\273\351\231\244\351\207\215\345\244\215\350\212\202\347\202\271.java" @@ -0,0 +1,45 @@ +package io.github.dunwu.algorithm.linkedlist.two_pointer; + +import io.github.dunwu.algorithm.linkedlist.ListNode; +import org.junit.jupiter.api.Assertions; + +import java.util.HashSet; +import java.util.Set; + +/** + * 面试题 02.01. 移除重复节点 + * + * @author Zhang Peng + * @since 2020-06-09 + */ +public class 移除重复节点 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(ListNode.buildList(1, 2, 3), + s.removeDuplicateNodes(ListNode.buildList(1, 2, 3, 3, 2, 1))); + Assertions.assertEquals(ListNode.buildList(1, 2), + s.removeDuplicateNodes(ListNode.buildList(1, 1, 1, 1, 2))); + } + + static class Solution { + + public ListNode removeDuplicateNodes(ListNode head) { + Set set = new HashSet<>(); + ListNode dummy = new ListNode(-1); + ListNode p = head, n = dummy; + while (p != null) { + if (!set.contains(p.val)) { + n.next = p; + n = n.next; + set.add(p.val); + } + p = p.next; + } + n.next = null; + return dummy.next; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\347\247\273\351\231\244\351\223\276\350\241\250\345\205\203\347\264\240.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\347\247\273\351\231\244\351\223\276\350\241\250\345\205\203\347\264\240.java" new file mode 100644 index 0000000..6cb6000 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\347\247\273\351\231\244\351\223\276\350\241\250\345\205\203\347\264\240.java" @@ -0,0 +1,40 @@ +package io.github.dunwu.algorithm.linkedlist.two_pointer; + +import io.github.dunwu.algorithm.linkedlist.ListNode; +import org.junit.jupiter.api.Assertions; + +/** + * 203. 移除链表元素 + * + * @author Zhang Peng + * @since 2020-06-09 + */ +public class 移除链表元素 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(ListNode.buildList(1, 2, 3, 4, 5), + s.removeElements(ListNode.buildList(1, 2, 6, 3, 4, 5, 6), 6)); + Assertions.assertEquals(ListNode.buildList(), s.removeElements(ListNode.buildList(), 1)); + Assertions.assertEquals(ListNode.buildList(), s.removeElements(ListNode.buildList(7, 7, 7, 7, 7), 7)); + } + + static class Solution { + + public ListNode removeElements(ListNode head, int val) { + ListNode dummy = new ListNode(0); + ListNode p = head, q = dummy; + while (p != null) { + if (p.val != val) { + q.next = p; + q = q.next; + } + p = p.next; + } + q.next = null; + return dummy.next; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\350\277\224\345\233\236\345\200\222\346\225\260\347\254\254k\344\270\252\350\212\202\347\202\271.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\350\277\224\345\233\236\345\200\222\346\225\260\347\254\254k\344\270\252\350\212\202\347\202\271.java" new file mode 100644 index 0000000..808ba2b --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\350\277\224\345\233\236\345\200\222\346\225\260\347\254\254k\344\270\252\350\212\202\347\202\271.java" @@ -0,0 +1,37 @@ +package io.github.dunwu.algorithm.linkedlist.two_pointer; + +import io.github.dunwu.algorithm.linkedlist.ListNode; +import org.junit.jupiter.api.Assertions; + +/** + * 面试题 02. 返回倒数第 k 个节点 + * LCR 140. 训练计划 II + * + * @author Zhang Peng + * @since 2020-06-09 + */ +public class 返回倒数第k个节点 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(4, s.kthToLast(ListNode.buildList(1, 2, 3, 4, 5), 2)); + Assertions.assertEquals(1, s.kthToLast(ListNode.buildList(1), 1)); + } + + static class Solution { + + public int kthToLast(ListNode head, int k) { + ListNode slow = head, fast = head; + for (int i = 0; i < k && fast != null; i++) { + fast = fast.next; + } + while (fast != null) { + fast = fast.next; + slow = slow.next; + } + return slow == null ? -1 : slow.val; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\351\223\276\350\241\250\347\232\204\344\270\255\351\227\264\347\273\223\347\202\271.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\351\223\276\350\241\250\347\232\204\344\270\255\351\227\264\347\273\223\347\202\271.java" new file mode 100644 index 0000000..f93a67c --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/linkedlist/two_pointer/\351\223\276\350\241\250\347\232\204\344\270\255\351\227\264\347\273\223\347\202\271.java" @@ -0,0 +1,35 @@ +package io.github.dunwu.algorithm.linkedlist.two_pointer; + +import io.github.dunwu.algorithm.linkedlist.ListNode; +import org.junit.jupiter.api.Assertions; + +/** + * 876. 链表的中间结点 + * + * @author Zhang Peng + * @since 2020-06-09 + */ +public class 链表的中间结点 { + + public static void main(String[] args) { + Solution s = new Solution(); + ListNode input = ListNode.buildList(1, 2, 3, 4, 5); + Assertions.assertEquals(ListNode.buildList(3, 4, 5), s.middleNode(input)); + ListNode input2 = ListNode.buildList(1, 2, 3, 4, 5, 6); + Assertions.assertEquals(ListNode.buildList(4, 5, 6), s.middleNode(input2)); + } + + static class Solution { + + public ListNode middleNode(ListNode head) { + ListNode slow = head, fast = head; + while (fast != null && fast.next != null) { + slow = slow.next; + fast = fast.next.next; + } + return slow; + } + + } + +} diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/ListNode.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/ListNode.java deleted file mode 100644 index fc8db7f..0000000 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/ListNode.java +++ /dev/null @@ -1,57 +0,0 @@ -package io.github.dunwu.algorithm.list; - -import java.util.Objects; - -public final class ListNode { - - int val; - ListNode next; - - ListNode(int val) { this.val = val; } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof ListNode)) return false; - ListNode listNode = (ListNode) o; - return val == listNode.val && - Objects.equals(next, listNode.next); - } - - @Override - public int hashCode() { - return Objects.hash(val, next); - } - - public static ListNode createLinkedList(int[] arr) { - if (arr == null || arr.length == 0) { - return null; - } - ListNode head = new ListNode(arr[0]); - ListNode cur = head; - for (int i = 1; i < arr.length; i++) { - cur.next = new ListNode(arr[i]); - cur = cur.next; - } - return head; - } - - public static void addLast() { - - } - - public static void main(String[] args) { - int[] arr = { 1, 2, 3, 4, 5 }; - ListNode head = createLinkedList(arr); - ListNode p = head; - while (p.next != null) { - p = p.next; - } - p.next = new ListNode(6); - while (head != null) { - System.out.println(head.val); - head = head.next; - } - } - -} diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/ListUtil.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/ListUtil.java deleted file mode 100644 index 6176f9f..0000000 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/ListUtil.java +++ /dev/null @@ -1,63 +0,0 @@ -package io.github.dunwu.algorithm.list; - -import java.util.ArrayList; -import java.util.List; - -/** - * @author Zhang Peng - * @since 2020-06-09 - */ -public class ListUtil { - - private ListUtil() { } - - public static ListNode buildList(int... list) { - ListNode head = new ListNode(-1); - ListNode node = head; - for (int val : list) { - node.next = new ListNode(val); - node = node.next; - } - return head.next; - } - - public static List toList(ListNode result) { - List list = new ArrayList<>(); - while (result != null) { - list.add(result.val); - result = result.next; - } - return list; - } - - public static List getValues(ListNode listNode) { - List list = new ArrayList<>(); - ListNode item = listNode; - while (item != null) { - list.add(item.val); - item = item.next; - } - return list; - } - - public static ListNode buildCycleList(int pos, int[] list) { - ListNode head = new ListNode(-1); - ListNode node = head; - ListNode cycleBeginNode = null; - for (int val : list) { - ListNode item = new ListNode(val); - if (pos == 0 && cycleBeginNode == null) { - cycleBeginNode = item; - } else { - pos--; - } - node.next = item; - node = node.next; - } - if (cycleBeginNode != null) { - node.next = cycleBeginNode; - } - return head.next; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\344\270\244\346\225\260\347\233\270\345\212\240.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\344\270\244\346\225\260\347\233\270\345\212\240.java" deleted file mode 100644 index c2128a4..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\344\270\244\346\225\260\347\233\270\345\212\240.java" +++ /dev/null @@ -1,70 +0,0 @@ -package io.github.dunwu.algorithm.list; - -import org.junit.jupiter.api.Assertions; - -import java.util.List; - -/** - * 2. 两数相加 - * - * @author Zhang Peng - * @since 2020-06-09 - */ -public class 两数相加 { - - public static void main(String[] args) { - ListNode result = addTwoNumbers(ListUtil.buildList(2, 4, 3), ListUtil.buildList(5, 6, 4)); - List list = ListUtil.toList(result); - System.out.println(list); - Assertions.assertArrayEquals(new Integer[] { 7, 0, 8 }, list.toArray()); - - ListNode result2 = addTwoNumbers(ListUtil.buildList(0), ListUtil.buildList(0)); - List list2 = ListUtil.toList(result2); - System.out.println(list2); - Assertions.assertArrayEquals(new Integer[] { 0 }, list2.toArray()); - - ListNode result3 = addTwoNumbers(ListUtil.buildList(9, 9, 9, 9, 9, 9, 9), ListUtil.buildList(9, 9, 9, 9)); - List list3 = ListUtil.toList(result3); - System.out.println(list3); - Assertions.assertArrayEquals(new Integer[] { 8, 9, 9, 9, 0, 0, 0, 1 }, list3.toArray()); - } - - public static ListNode addTwoNumbers(ListNode l1, ListNode l2) { - - // 如果任意一个表示数的链表为空,直接返回另一个链表 - if (l1 == null) return l2; - if (l2 == null) return l1; - - // 初始化 - int carry = 0; - ListNode x = l1; - ListNode y = l2; - ListNode dummy = new ListNode(-1); - ListNode p = dummy; - - // 同时遍历两个操作数链表,任意操作数链表的当前位数所对应元素不为 null 则累加 - while (x != null || y != null) { - int value = carry; - - if (x != null) { - value += x.val; - x = x.next; - } - - if (y != null) { - value += y.val; - y = y.next; - } - - carry = value / 10; - p.next = new ListNode(value % 10); - p = p.next; - } - - if (carry != 0) { - p.next = new ListNode(carry); - } - return dummy.next; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\344\270\244\346\225\260\347\233\270\345\212\240II.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\344\270\244\346\225\260\347\233\270\345\212\240II.java" deleted file mode 100644 index 0df454e..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\344\270\244\346\225\260\347\233\270\345\212\240II.java" +++ /dev/null @@ -1,51 +0,0 @@ -package io.github.dunwu.algorithm.list; - -import org.junit.jupiter.api.Assertions; - -import java.util.List; - -/** - * LCR 025. 两数相加II - * - * @author Zhang Peng - * @date 2025-01-21 - */ -public class 两数相加II { - - public static void main(String[] args) { - ListNode result = addTwoNumbers(ListUtil.buildList(7, 2, 4, 3), ListUtil.buildList(5, 6, 4)); - List list = ListUtil.toList(result); - System.out.println(list); - Assertions.assertArrayEquals(new Integer[] { 7, 8, 0, 7 }, list.toArray()); - - ListNode result2 = addTwoNumbers(ListUtil.buildList(2, 4, 3), ListUtil.buildList(5, 6, 4)); - List list2 = ListUtil.toList(result2); - System.out.println(list2); - Assertions.assertArrayEquals(new Integer[] { 8, 0, 7 }, list2.toArray()); - - ListNode result3 = addTwoNumbers(ListUtil.buildList(0), ListUtil.buildList(0)); - List list3 = ListUtil.toList(result3); - System.out.println(list3); - Assertions.assertArrayEquals(new Integer[] { 0 }, list3.toArray()); - } - - public static ListNode addTwoNumbers(ListNode l1, ListNode l2) { - // 将两个链表倒置,方便先从低位到高位,逐次相加 - ListNode r1 = reverse(l1); - ListNode r2 = reverse(l2); - ListNode result = 两数相加.addTwoNumbers(r1, r2); - return reverse(result); - } - - public static ListNode reverse(ListNode head) { - ListNode pre = null, cur = head; - while (cur != null) { - ListNode next = cur.next; - cur.next = pre; - pre = cur; - cur = next; - } - return pre; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\344\272\214\350\277\233\345\210\266\351\223\276\350\241\250\350\275\254\346\225\264\346\225\260.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\344\272\214\350\277\233\345\210\266\351\223\276\350\241\250\350\275\254\346\225\264\346\225\260.java" deleted file mode 100644 index 4348aad..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\344\272\214\350\277\233\345\210\266\351\223\276\350\241\250\350\275\254\346\225\264\346\225\260.java" +++ /dev/null @@ -1,89 +0,0 @@ -package io.github.dunwu.algorithm.list; - -import org.junit.jupiter.api.Assertions; - -/** - * @author Zhang Peng - * @since 2020-06-09 - */ -public class 二进制链表转整数 { - - public static void main(String[] args) { - ListNode head = ListUtil.buildList(1, 0, 1); - System.out.println(ListUtil.toList(head)); - int result = getDecimalValue(head); - Assertions.assertEquals(5, result); - - head = new ListNode(0); - System.out.println(ListUtil.toList(head)); - result = getDecimalValue(head); - Assertions.assertEquals(0, result); - - head = new ListNode(1); - System.out.println(ListUtil.toList(head)); - result = getDecimalValue(head); - Assertions.assertEquals(1, result); - - head = ListUtil.buildList(1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0); - System.out.println(ListUtil.toList(head)); - result = getDecimalValue(head); - Assertions.assertEquals(18880, result); - } - - /** - * 二进制链表转整数 算法实现 - *

- * 给你一个单链表的引用结点 head。链表中每个结点的值不是 0 就是 1。已知此链表是一个整数数字的二进制表示形式。 - *

- * 请你返回该链表所表示数字的 十进制值 。 - *

- * 示例 1: - *

-     * 输入:head = [1,0,1]
-     * 输出:5
-     * 解释:二进制数 (101) 转化为十进制数 (5)
-     * 
- *

- * 示例 2: - *

-     * 输入:head = [0]
-     * 输出:0
-     * 
- *

- * 示例 3: - *

-     * 输入:head = [1]
-     * 输出:1
-     * 
- *

- * 示例 4: - *

-     * 输入:head = [1,0,0,1,0,0,1,1,1,0,0,0,0,0,0]
-     * 输出:18880
-     * 
- *

- * 示例 5: - *

-     * 输入:head = [0,0]
-     * 输出:0
-     * 
- *

- * 提示: - *

-     * 链表不为空。
-     * 链表的结点总数不超过 30。
-     * 每个结点的值不是 0 就是 1。
-     * 
- * - * @see 二进制链表转整数 - */ - public static int getDecimalValue(ListNode head) { - int sum = 0; - while (head != null) { - sum = sum * 2 + head.val; - head = head.next; - } - return sum; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\344\273\216\346\234\252\346\216\222\345\272\217\347\232\204\351\223\276\350\241\250\344\270\255\347\247\273\351\231\244\351\207\215\345\244\215\345\205\203\347\264\240.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\344\273\216\346\234\252\346\216\222\345\272\217\347\232\204\351\223\276\350\241\250\344\270\255\347\247\273\351\231\244\351\207\215\345\244\215\345\205\203\347\264\240.java" deleted file mode 100644 index d4a69af..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\344\273\216\346\234\252\346\216\222\345\272\217\347\232\204\351\223\276\350\241\250\344\270\255\347\247\273\351\231\244\351\207\215\345\244\215\345\205\203\347\264\240.java" +++ /dev/null @@ -1,94 +0,0 @@ -package io.github.dunwu.algorithm.list; - -import org.junit.jupiter.api.Assertions; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * 83. 删除排序链表中的重复元素 - * - * @author Zhang Peng - * @since 2020-06-09 - */ -public class 从未排序的链表中移除重复元素 { - - public static void main(String[] args) { - ListNode head = ListUtil.buildList(1, 2, 3, 2); - System.out.println(ListUtil.toList(head)); - ListNode result = deleteDuplicates2(head); - List list = ListUtil.toList(result); - System.out.println(list); - Assertions.assertArrayEquals(new Integer[] { 1, 3 }, list.toArray(new Integer[0])); - - ListNode head2 = ListUtil.buildList(2, 1, 1, 2); - System.out.println(ListUtil.toList(head2)); - ListNode result2 = deleteDuplicates2(head2); - List list2 = ListUtil.toList(result2); - System.out.println(list2); - Assertions.assertArrayEquals(new Integer[] {}, list2.toArray(new Integer[0])); - - ListNode head3 = ListUtil.buildList(3, 2, 2, 1, 3, 2, 4); - System.out.println(ListUtil.toList(head3)); - ListNode result3 = deleteDuplicates2(head3); - List list3 = ListUtil.toList(result3); - System.out.println(list3); - Assertions.assertArrayEquals(new Integer[] { 1, 4 }, list3.toArray(new Integer[0])); - } - - public static ListNode deleteDuplicates(ListNode head) { - Map map = new HashMap<>(); - ListNode p = head; - while (p != null) { - map.put(p.val, map.getOrDefault(p.val, 0) + 1); - p = p.next; - } - - ListNode dup = new ListNode(101); - ListNode nodup = new ListNode(101); - ListNode pDup = dup, pNodup = nodup; - p = head; - while (p != null) { - if (map.get(p.val) > 1) { - pDup.next = p; - pDup = pDup.next; - } else { - pNodup.next = p; - pNodup = pNodup.next; - } - - p = p.next; - pDup.next = null; - pNodup.next = null; - } - return nodup.next; - } - - public static ListNode deleteDuplicates2(ListNode head) { - ListNode dupList = new ListNode(0); - ListNode nodupList = new ListNode(0); - ListNode dup = dupList, nodup = nodupList; - ListNode p = head; - Map map = new HashMap<>(); - while (p != null) { - map.put(p.val, map.getOrDefault(p.val, 0) + 1); - p = p.next; - } - p = head; - while (p != null) { - if (map.get(p.val) > 1) { - dup.next = p; - dup = dup.next; - } else { - nodup.next = p; - nodup = nodup.next; - } - p = p.next; - dup.next = null; - nodup.next = null; - } - return nodupList.next; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\210\206\351\232\224\351\223\276\350\241\250.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\210\206\351\232\224\351\223\276\350\241\250.java" deleted file mode 100644 index d75b224..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\210\206\351\232\224\351\223\276\350\241\250.java" +++ /dev/null @@ -1,54 +0,0 @@ -package io.github.dunwu.algorithm.list; - -import org.junit.jupiter.api.Assertions; - -import java.util.List; - -/** - * @author Zhang Peng - * @see 86. 分隔链表 - * @since 2020-07-06 - */ -public class 分隔链表 { - - public static void main(String[] args) { - ListNode head = ListUtil.buildList(1, 4, 3, 2, 5, 2); - ListNode result = partition(head, 3); - List list = ListUtil.toList(result); - Assertions.assertArrayEquals(new Integer[] { 1, 2, 2, 4, 3, 5 }, list.toArray(new Integer[0])); - - ListNode head2 = ListUtil.buildList(2, 1); - ListNode result2 = partition(head2, 2); - List list2 = ListUtil.toList(result2); - Assertions.assertArrayEquals(new Integer[] { 1, 2 }, list2.toArray(new Integer[0])); - - ListNode head3 = ListUtil.buildList(3, 1, 2); - ListNode result3 = partition(head3, 3); - List list3 = ListUtil.toList(result3); - Assertions.assertArrayEquals(new Integer[] { 1, 2, 3 }, list3.toArray(new Integer[0])); - } - - public static ListNode partition(ListNode head, int x) { - ListNode dummy1 = new ListNode(-1); - ListNode dummy2 = new ListNode(-1); - ListNode d1 = dummy1; - ListNode d2 = dummy2; - ListNode p = head; - while (p != null) { - if (p.val < x) { - d1.next = p; - d1 = d1.next; - } else { - d2.next = p; - d2 = d2.next; - } - ListNode temp = p.next; - p.next = null; - p = temp; - } - d1.next = dummy2.next; - d2.next = null; - return dummy1.next; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\210\240\351\231\244\346\216\222\345\272\217\351\223\276\350\241\250\344\270\255\347\232\204\351\207\215\345\244\215\345\205\203\347\264\240.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\210\240\351\231\244\346\216\222\345\272\217\351\223\276\350\241\250\344\270\255\347\232\204\351\207\215\345\244\215\345\205\203\347\264\240.java" deleted file mode 100644 index e38379b..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\210\240\351\231\244\346\216\222\345\272\217\351\223\276\350\241\250\344\270\255\347\232\204\351\207\215\345\244\215\345\205\203\347\264\240.java" +++ /dev/null @@ -1,47 +0,0 @@ -package io.github.dunwu.algorithm.list; - -import org.junit.jupiter.api.Assertions; - -import java.util.List; - -/** - * 83. 删除排序链表中的重复元素 - * - * @author Zhang Peng - * @since 2020-06-09 - */ -public class 删除排序链表中的重复元素 { - - public static void main(String[] args) { - ListNode head = ListUtil.buildList(1, 1, 2); - System.out.println(ListUtil.toList(head)); - ListNode result = deleteDuplicates(head); - List list = ListUtil.toList(result); - System.out.println(list); - Assertions.assertArrayEquals(new Integer[] { 1, 2 }, list.toArray(new Integer[0])); - - ListNode head2 = ListUtil.buildList(1, 1, 2, 3, 3); - System.out.println(ListUtil.toList(head2)); - ListNode result2 = deleteDuplicates(head2); - List list2 = ListUtil.toList(result2); - System.out.println(list2); - Assertions.assertArrayEquals(new Integer[] { 1, 2, 3 }, list2.toArray(new Integer[0])); - } - - public static ListNode deleteDuplicates(ListNode head) { - if (head == null || head.next == null) { - return head; - } - ListNode slow = head, fast = head.next; - while (fast != null) { - if (slow.val == fast.val) { - slow.next = fast.next; - } else { - slow = slow.next; - } - fast = fast.next; - } - return head; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\210\240\351\231\244\346\216\222\345\272\217\351\223\276\350\241\250\344\270\255\347\232\204\351\207\215\345\244\215\345\205\203\347\264\240II.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\210\240\351\231\244\346\216\222\345\272\217\351\223\276\350\241\250\344\270\255\347\232\204\351\207\215\345\244\215\345\205\203\347\264\240II.java" deleted file mode 100644 index a037bb1..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\210\240\351\231\244\346\216\222\345\272\217\351\223\276\350\241\250\344\270\255\347\232\204\351\207\215\345\244\215\345\205\203\347\264\240II.java" +++ /dev/null @@ -1,79 +0,0 @@ -package io.github.dunwu.algorithm.list; - -import org.junit.jupiter.api.Assertions; - -import java.util.List; - -/** - * 83. 删除排序链表中的重复元素 - * - * @author Zhang Peng - * @since 2020-06-09 - */ -public class 删除排序链表中的重复元素II { - - public static void main(String[] args) { - // ListNode head = ListUtil.buildList(1, 2, 3, 3, 4, 4, 5); - // System.out.println(ListUtil.toList(head)); - // ListNode result = deleteDuplicates2(head); - // List list = ListUtil.toList(result); - // System.out.println(list); - // Assertions.assertArrayEquals(new Integer[] { 1, 2, 5 }, list.toArray(new Integer[0])); - // - // ListNode head2 = ListUtil.buildList(1, 1, 1, 2, 3); - // System.out.println(ListUtil.toList(head2)); - // ListNode result2 = deleteDuplicates2(head2); - // List list2 = ListUtil.toList(result2); - // System.out.println(list2); - // Assertions.assertArrayEquals(new Integer[] { 2, 3 }, list2.toArray(new Integer[0])); - - ListNode head3 = ListUtil.buildList(1, 2, 2); - System.out.println(ListUtil.toList(head3)); - ListNode result3 = deleteDuplicates2(head3); - List list3 = ListUtil.toList(result3); - System.out.println(list3); - Assertions.assertArrayEquals(new Integer[] { 1 }, list3.toArray(new Integer[0])); - } - - public static ListNode deleteDuplicates(ListNode head) { - ListNode dupList = new ListNode(101); - ListNode nodupList = new ListNode(101); - - ListNode dup = dupList, nodup = nodupList; - ListNode cur = head; - while (cur != null) { - if ((cur.next != null && cur.val == cur.next.val) || cur.val == dup.val) { - dup.next = cur; - dup = dup.next; - } else { - nodup.next = cur; - nodup = nodup.next; - } - cur = cur.next; - dup.next = null; - nodup.next = null; - } - return nodupList.next; - } - - public static ListNode deleteDuplicates2(ListNode head) { - ListNode dupList = new ListNode(101); - ListNode nodupList = new ListNode(101); - ListNode dup = dupList, nodup = nodupList; - ListNode p = head; - while (p != null) { - if ((p.next != null && p.val == p.next.val) || p.val == dup.val) { - dup.next = p; - dup = dup.next; - } else { - nodup.next = p; - nodup = nodup.next; - } - p = p.next; - dup.next = null; - nodup.next = null; - } - return nodupList.next; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\210\240\351\231\244\351\223\276\350\241\250\347\232\204\345\200\222\346\225\260\347\254\254N\344\270\252\347\273\223\347\202\271.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\210\240\351\231\244\351\223\276\350\241\250\347\232\204\345\200\222\346\225\260\347\254\254N\344\270\252\347\273\223\347\202\271.java" deleted file mode 100644 index 43e81d7..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\210\240\351\231\244\351\223\276\350\241\250\347\232\204\345\200\222\346\225\260\347\254\254N\344\270\252\347\273\223\347\202\271.java" +++ /dev/null @@ -1,54 +0,0 @@ -package io.github.dunwu.algorithm.list; - -import org.junit.jupiter.api.Assertions; - -import java.util.List; - -/** - * 19. 删除链表的倒数第 N 个结点 - * - * @author Zhang Peng - * @since 2020-06-09 - */ -public class 删除链表的倒数第N个结点 { - - public static void main(String[] args) { - ListNode head1 = ListUtil.buildList(1, 2, 3, 4, 5); - ListNode result = removeNthFromEnd(head1, 2); - List list = ListUtil.toList(result); - System.out.println(list); - Assertions.assertArrayEquals(new Integer[] { 1, 2, 3, 5 }, list.toArray(new Integer[0])); - - ListNode head2 = ListUtil.buildList(1); - ListNode result2 = removeNthFromEnd(head2, 1); - List list2 = ListUtil.toList(result2); - System.out.println(list2); - Assertions.assertArrayEquals(new Integer[] {}, list2.toArray(new Integer[0])); - - ListNode head3 = ListUtil.buildList(1, 2); - ListNode result3 = removeNthFromEnd(head3, 1); - List list3 = ListUtil.toList(result3); - System.out.println(list3); - Assertions.assertArrayEquals(new Integer[] { 1 }, list3.toArray(new Integer[0])); - } - - public static ListNode removeNthFromEnd(ListNode head, int n) { - ListNode dummy = new ListNode(-1); - dummy.next = head; - - ListNode fast = dummy; - for (int i = 0; i < n + 1; i++) { - fast = fast.next; - } - - ListNode slow = dummy; - while (fast != null) { - fast = fast.next; - slow = slow.next; - } - - slow.next = slow.next.next; - return dummy.next; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\217\215\350\275\254\351\223\276\350\241\250.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\217\215\350\275\254\351\223\276\350\241\250.java" deleted file mode 100644 index 47963f5..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\217\215\350\275\254\351\223\276\350\241\250.java" +++ /dev/null @@ -1,98 +0,0 @@ -package io.github.dunwu.algorithm.list; - -import org.junit.jupiter.api.Assertions; - -import java.util.List; -import java.util.Stack; - -/** - * @author Zhang Peng - * @see 206. 反转链表 - * @since 2020-06-09 - */ -public class 反转链表 { - - public static void main(String[] args) { - ListNode head = ListUtil.buildList(1, 2, 3, 4); - System.out.println(ListUtil.toList(head)); - ListNode result = reverseList3(head); - List list = ListUtil.toList(result); - System.out.println(list); - Assertions.assertArrayEquals(new Integer[] { 4, 3, 2, 1 }, list.toArray(new Integer[0])); - - ListNode head2 = ListUtil.buildList(1, 2); - System.out.println(ListUtil.toList(head2)); - ListNode result2 = reverseList3(head2); - List list2 = ListUtil.toList(result2); - System.out.println(list2); - Assertions.assertArrayEquals(new Integer[] { 2, 1 }, list2.toArray(new Integer[0])); - - ListNode head3 = ListUtil.buildList(); - System.out.println(ListUtil.toList(head3)); - ListNode result3 = reverseList3(head3); - List list3 = ListUtil.toList(result3); - System.out.println(list3); - Assertions.assertArrayEquals(new Integer[] {}, list3.toArray(new Integer[0])); - } - - /** - * 借助栈来实现,时间复杂度:O(2N) - */ - public static ListNode reverseList(ListNode head) { - if (head == null) { - return head; - } - - Stack stack = new Stack<>(); - ListNode node = head; - while (node != null) { - stack.push(node); - node = node.next; - } - - ListNode dummy = new ListNode(-5001); - ListNode p = dummy; - while (!stack.isEmpty()) { - ListNode top = stack.pop(); - top.next = null; - p.next = top; - p = p.next; - } - return dummy.next; - } - - /** - * 双指针,时间复杂度:O(N) - */ - public static ListNode reverseList2(ListNode head) { - - if (head == null) { - return head; - } - - ListNode pre = null, cur = head; - while (cur != null) { - ListNode next = cur.next; - cur.next = pre; - pre = cur; - cur = next; - } - return pre; - } - - /** - * 递归 - */ - public static ListNode reverseList3(ListNode head) { - - if (head == null || head.next == null) { - return head; - } - - ListNode last = reverseList3(head.next); - head.next.next = head; - head.next = null; - return last; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\217\215\350\275\254\351\223\276\350\241\250II.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\217\215\350\275\254\351\223\276\350\241\250II.java" deleted file mode 100644 index 6923236..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\217\215\350\275\254\351\223\276\350\241\250II.java" +++ /dev/null @@ -1,57 +0,0 @@ -package io.github.dunwu.algorithm.list; - -import org.junit.jupiter.api.Assertions; - -import java.util.List; - -/** - * 92. 反转链表 II - * - * @author Zhang Peng - * @date 2025-01-20 - */ -public class 反转链表II { - - public static void main(String[] args) { - ListNode head = ListUtil.buildList(1, 2, 3, 4, 5); - System.out.println(ListUtil.toList(head)); - ListNode result = reverseList(head, 2, 4); - List list = ListUtil.toList(result); - System.out.println(list); - Assertions.assertArrayEquals(new Integer[] { 1, 4, 3, 2, 5 }, list.toArray(new Integer[0])); - } - - /** - * 借助栈来实现,时间复杂度:O(2N) - */ - public static ListNode reverseList(ListNode head, int m, int n) { - if (m == 1) { - return reverseN(head, n); - } - ListNode cur = head; - for (int i = 1; i < m - 1; i++) { - cur = cur.next; - } - cur.next = reverseN(cur.next, n - m + 1); - return head; - } - - public static ListNode reverseN(ListNode head, int n) { - if (head == null || head.next == null) { - return head; - } - ListNode pre = null, cur = head; - while (cur != null && n > 0) { - ListNode next = cur.next; - cur.next = pre; - pre = cur; - cur = next; - n--; - } - if (head != null) { - head.next = cur; - } - return pre; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\220\210\345\271\266K\344\270\252\346\216\222\345\272\217\351\223\276\350\241\250.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\220\210\345\271\266K\344\270\252\346\216\222\345\272\217\351\223\276\350\241\250.java" deleted file mode 100644 index af0644a..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\220\210\345\271\266K\344\270\252\346\216\222\345\272\217\351\223\276\350\241\250.java" +++ /dev/null @@ -1,76 +0,0 @@ -package io.github.dunwu.algorithm.list; - -import org.junit.jupiter.api.Assertions; - -import java.util.List; - -/** - * 23. 合并K个排序链表 - * - * @author Zhang Peng - * @since 2020-06-09 - */ -public class 合并K个排序链表 { - - public static void main(String[] args) { - ListNode head1 = ListUtil.buildList(1, 4, 5); - ListNode head2 = ListUtil.buildList(1, 3, 4); - ListNode head3 = ListUtil.buildList(2, 6); - ListNode[] array = new ListNode[] { head1, head2, head3 }; - ListNode result = mergeKLists2(array); - List list = ListUtil.toList(result); - System.out.println(list); - Assertions.assertArrayEquals(new Integer[] { 1, 1, 2, 3, 4, 4, 5, 6 }, list.toArray(new Integer[0])); - - ListNode[] array2 = new ListNode[] {}; - ListNode result2 = mergeKLists2(array2); - List list2 = ListUtil.toList(result2); - System.out.println(list2); - Assertions.assertArrayEquals(new Integer[] {}, list2.toArray(new Integer[0])); - } - - public static ListNode mergeKLists(ListNode[] lists) { - if (lists == null || lists.length == 0) { - return null; - } - - ListNode root = new ListNode(-1); - ListNode resultHead = root; - while (true) { - Integer minIndex = null; - Integer minVal = null; - for (int i = 0; i < lists.length; i++) { - if (lists[i] == null) { - continue; - } - - if (minVal == null || lists[i].val < minVal) { - minIndex = i; - minVal = lists[i].val; - } - } - - if (minIndex != null) { - resultHead.next = new ListNode(lists[minIndex].val); - resultHead = resultHead.next; - lists[minIndex] = lists[minIndex].next; - } else { - break; - } - } - - return root.next; - } - - public static ListNode mergeKLists2(ListNode[] lists) { - if (lists == null || lists.length == 0) { - return null; - } - ListNode result = lists[0]; - for (int i = 1; i < lists.length; i++) { - result = 合并两个有序链表.mergeTwoLists(result, lists[i]); - } - return result; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\351\223\276\350\241\250.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\351\223\276\350\241\250.java" deleted file mode 100644 index 4b24ebd..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\351\223\276\350\241\250.java" +++ /dev/null @@ -1,41 +0,0 @@ -package io.github.dunwu.algorithm.list; - -import org.junit.jupiter.api.Assertions; - -import java.util.List; - -/** - * @author Zhang Peng - * @see 合并两个有序链表 - * @since 2020-06-09 - */ -public class 合并两个有序链表 { - - public static void main(String[] args) { - ListNode head1 = ListUtil.buildList(1, 2, 4); - ListNode head2 = ListUtil.buildList(1, 3, 4); - ListNode result = mergeTwoLists(head1, head2); - List list = ListUtil.toList(result); - System.out.println(list); - Assertions.assertArrayEquals(new Integer[] { 1, 1, 2, 3, 4, 4 }, list.toArray(new Integer[0])); - } - - public static ListNode mergeTwoLists(ListNode l1, ListNode l2) { - ListNode dummy = new ListNode(-1); - ListNode n = dummy; - while (l1 != null && l2 != null) { - if (l1.val <= l2.val) { - n.next = l1; - l1 = l1.next; - } else { - n.next = l2; - l2 = l2.next; - } - n = n.next; - } - - n.next = (l1 != null) ? l1 : l2; - return dummy.next; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\233\236\346\226\207\351\223\276\350\241\250.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\233\236\346\226\207\351\223\276\350\241\250.java" deleted file mode 100644 index 8e3e249..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\233\236\346\226\207\351\223\276\350\241\250.java" +++ /dev/null @@ -1,65 +0,0 @@ -package io.github.dunwu.algorithm.list; - -import org.junit.jupiter.api.Assertions; - -import java.util.ArrayList; -import java.util.List; - -/** - * @author Zhang Peng - * @see 234. 回文链表 - * @see 面试题 02.06. 回文链表 - * @since 2020-06-09 - */ -public class 回文链表 { - - public static void main(String[] args) { - ListNode head = ListUtil.buildList(1, 2, 2, 1); - Assertions.assertTrue(isPalindrome2(head)); - - head = ListUtil.buildList(1, 2); - Assertions.assertFalse(isPalindrome2(head)); - } - - public static boolean isPalindrome(ListNode head) { - List list = new ArrayList<>(); - ListNode node = head; - while (node != null) { - list.add(node.val); - node = node.next; - } - - // int i = 0, j = list.size() - 1; - for (int i = 0, j = list.size() - 1; i < j; i++, j--) { - if (!list.get(i).equals(list.get(j))) { - return false; - } - } - return true; - } - - public static boolean isPalindrome2(ListNode head) { - ListNode left = head; - ListNode right = reverse(head); - while (left != null && right != null) { - if (left.val != right.val) { - return false; - } - left = left.next; - right = right.next; - } - return true; - } - - public static ListNode reverse(ListNode head) { - ListNode pre = null, cur = head; - while (cur != null) { - ListNode next = cur.next; - cur.next = pre; - pre = cur; - cur = next; - } - return pre; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\245\207\345\201\266\351\223\276\350\241\250.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\245\207\345\201\266\351\223\276\350\241\250.java" deleted file mode 100644 index 3ab1ae7..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\345\245\207\345\201\266\351\223\276\350\241\250.java" +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.dunwu.algorithm.list; - -import java.util.Arrays; -import java.util.List; - -/** - * @author Zhang Peng - * @since 2020-07-08 - */ -public class 奇偶链表 { - - public static void main(String[] args) { - ListNode head = ListUtil.buildList(1, 2, 3, 4, 5); - List list = ListUtil.toList(oddEvenList(head)); - System.out.println(list); - // Assertions.assertFalse(); - } - - public static ListNode oddEvenList(ListNode head) { - if (head == null || head.next == null) return head; - - ListNode odd = head, even = head.next, evenHead = even; - - while (even != null && even.next != null) { - odd.next = even.next; - odd = odd.next; - even.next = odd.next; - even = even.next; - } - odd.next = evenHead; - return head; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\346\216\222\345\272\217\351\223\276\350\241\250.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\346\216\222\345\272\217\351\223\276\350\241\250.java" deleted file mode 100644 index fa65a28..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\346\216\222\345\272\217\351\223\276\350\241\250.java" +++ /dev/null @@ -1,96 +0,0 @@ -package io.github.dunwu.algorithm.list; - -import org.junit.jupiter.api.Assertions; - -import java.util.List; - -/** - * @author Zhang Peng - * @see 148.排序链表 - * @since 2020-06-09 - */ -public class 排序链表 { - - public static void main(String[] args) { - ListNode head = ListUtil.buildList(4, 2, 1, 3); - System.out.println(ListUtil.toList(head)); - ListNode result = sortList(head); - List list = ListUtil.toList(result); - System.out.println(list); - Assertions.assertArrayEquals(new Integer[] { 1, 2, 3, 4 }, list.toArray(new Integer[0])); - - head = ListUtil.buildList(-1, 5, 3, 4, 0); - System.out.println(ListUtil.toList(head)); - result = sortList(head); - list = ListUtil.toList(result); - System.out.println(list); - Assertions.assertArrayEquals(new Integer[] { -1, 0, 3, 4, 5 }, list.toArray(new Integer[0])); - } - - public static ListNode sortList(ListNode head) { - if (head == null) {return head;} - return mergeSort(head); - } - - static ListNode mergeSort(ListNode head) { - //回归条件 - if (head.next == null) { - return head; - } - //快指针,考虑到链表为2时的情况,fast比slow早一格 - ListNode fast = head.next; - //慢指针 - ListNode slow = head; - //快慢指针开跑 - while (fast != null && fast.next != null) { - fast = fast.next.next; - slow = slow.next; - } - //找到右子链表头元素,复用fast引用 - fast = slow.next; - //将中点后续置空,切割为两个子链表 - slow.next = null; - //递归分解左子链表,得到新链表起点 - head = mergeSort(head); - //递归分解右子链表,得到新链表起点 - fast = mergeSort(fast); - //并归两个子链表 - return merge(head, fast); - } - - static ListNode merge(ListNode left, ListNode right) { - //维护临时序列的头元素 - ListNode head; - if (left.val <= right.val) { - head = left; - left = left.next; - } else { - head = right; - right = right.next; - } - //两个子链表均存在剩余元素 - ListNode temp = head; - while (left != null && right != null) { - //将较小的元素加入临时序列 - if (left.val <= right.val) { - temp.next = left; - left = left.next; - temp = temp.next; - } else { - temp.next = right; - right = right.next; - temp = temp.next; - } - } - //左子序列用完将右子序列余下元素加入临时序列 - if (left == null) { - temp.next = right; - } - //右子序列用完将左子序列余下元素加入临时序列 - if (right == null) { - temp.next = left; - } - return head; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\347\216\257\345\275\242\351\223\276\350\241\250.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\347\216\257\345\275\242\351\223\276\350\241\250.java" deleted file mode 100644 index 2d10a78..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\347\216\257\345\275\242\351\223\276\350\241\250.java" +++ /dev/null @@ -1,40 +0,0 @@ -package io.github.dunwu.algorithm.list; - -import org.junit.jupiter.api.Assertions; - -/** - * 141. 环形链表 - * - * @author Zhang Peng - * @since 2020-06-09 - */ -public class 环形链表 { - - public static void main(String[] args) { - ListNode head = ListUtil.buildList(3, 2, 0, -4); - Assertions.assertFalse(hasCycle(head)); - - ListNode head2 = ListUtil.buildCycleList(1, new int[] { 3, 2, 0, -4 }); - Assertions.assertTrue(hasCycle(head2)); - - ListNode head3 = ListUtil.buildCycleList(0, new int[] { 1, 2 }); - Assertions.assertTrue(hasCycle(head3)); - - ListNode head4 = ListUtil.buildCycleList(1, new int[] { 1 }); - Assertions.assertFalse(hasCycle(head4)); - } - - public static boolean hasCycle(ListNode head) { - if (head == null || head.next == null) return false; - ListNode slow = head, fast = head.next; - while (fast != null && fast.next != null) { - if (slow == fast) { - return true; - } - slow = slow.next; - fast = fast.next.next; - } - return false; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\347\216\257\345\275\242\351\223\276\350\241\250II.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\347\216\257\345\275\242\351\223\276\350\241\250II.java" deleted file mode 100644 index ed79e53..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\347\216\257\345\275\242\351\223\276\350\241\250II.java" +++ /dev/null @@ -1,68 +0,0 @@ -package io.github.dunwu.algorithm.list; - -import org.junit.jupiter.api.Assertions; - -/** - * 142. 环形链表 II - * - * @author Zhang Peng - * @since 2020-07-08 - */ -public class 环形链表II { - - public static void main(String[] args) { - // ListNode head = ListUtil.buildList(3, 2, 0, -4); - // Assertions.assertEquals(null, detectCycle(head)); - - ListNode head2 = ListUtil.buildCycleList(1, new int[] { 3, 2, 0, -4 }); - Assertions.assertEquals(2, detectCycle2(head2).val); - - ListNode head3 = ListUtil.buildCycleList(0, new int[] { 1, 2 }); - Assertions.assertEquals(1, detectCycle2(head3).val); - - ListNode head4 = ListUtil.buildCycleList(1, new int[] { 1 }); - Assertions.assertEquals(null, detectCycle2(head4)); - } - - public static ListNode detectCycle(ListNode head) { - ListNode fast = head, slow = head; - while (fast != null && fast.next != null) { - fast = fast.next.next; - slow = slow.next; - if (fast == slow) { - break; - } - } - - if (fast == null || fast.next == null) { - return null; - } - - fast = head; - while (fast != slow) { - fast = fast.next; - slow = slow.next; - } - return fast; - } - public static ListNode detectCycle2(ListNode head) { - ListNode slow = head, fast = head; - while (fast != null && fast.next != null) { - slow = slow.next; - fast = fast.next.next; - if (slow == fast) { - break; - } - } - if (fast == null || fast.next == null) { - return null; - } - slow = head; - while (slow != fast) { - slow = slow.next; - fast = fast.next; - } - return slow; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\347\233\270\344\272\244\351\223\276\350\241\250.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\347\233\270\344\272\244\351\223\276\350\241\250.java" deleted file mode 100644 index ebd64e2..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\347\233\270\344\272\244\351\223\276\350\241\250.java" +++ /dev/null @@ -1,49 +0,0 @@ -package io.github.dunwu.algorithm.list; - -import org.junit.jupiter.api.Assertions; - -/** - * 相交链表 - * - * @author Zhang Peng - * @since 2020-06-09 - */ -public class 相交链表 { - - public static void main(String[] args) { - ListNode listA = ListUtil.buildList(4, 1, 8, 4, 5); - ListNode listB = ListUtil.buildList(5, 6, 1, 8, 4, 5); - buildMetPot(listA, listB, 2, 3); - ListNode result = getIntersectionNode(listA, listB); - Assertions.assertEquals(8, result.val); - - ListNode listA2 = ListUtil.buildList(1, 9, 1, 2, 4); - ListNode listB2 = ListUtil.buildList(3, 2, 4); - buildMetPot(listA2, listB2, 3, 1); - ListNode result2 = getIntersectionNode(listA2, listB2); - Assertions.assertEquals(2, result2.val); - } - - public static void buildMetPot(ListNode listA, ListNode listB, int skipA, int skipB) { - ListNode pA = listA; - for (int i = 0; i < skipA; i++) { - pA = pA.next; - } - ListNode pB = listB; - for (int i = 0; i < skipB - 1; i++) { - pB = pB.next; - } - pB.next = pA; - } - - public static ListNode getIntersectionNode(ListNode headA, ListNode headB) { - if (headA == null || headB == null) return null; - ListNode pA = headA, pB = headB; - while (pA != pB) { - pA = pA == null ? headB : pA.next; - pB = pB == null ? headA : pB.next; - } - return pA; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\347\247\273\351\231\244\351\207\215\345\244\215\350\212\202\347\202\271.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\347\247\273\351\231\244\351\207\215\345\244\215\350\212\202\347\202\271.java" deleted file mode 100644 index cd353d7..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\347\247\273\351\231\244\351\207\215\345\244\215\350\212\202\347\202\271.java" +++ /dev/null @@ -1,63 +0,0 @@ -package io.github.dunwu.algorithm.list; - -import org.junit.jupiter.api.Assertions; - -import java.util.List; - -/** - * @author Zhang Peng - * @since 2020-06-09 - */ -public class 移除重复节点 { - - public static void main(String[] args) { - ListNode head = ListUtil.buildList(1, 2, 3, 3, 2, 1); - ListNode listNode = removeDuplicateNodes(head); - List result = ListUtil.getValues(listNode); - System.out.println(result); - Assertions.assertArrayEquals(new Integer[] { 1, 2, 3 }, result.toArray()); - } - - /** - * @see 面试题 02.01. 移除重复节点 - */ - public static ListNode removeDuplicateNodes(ListNode head) { - if (head == null) { - return null; - } - - ListNode list = new ListNode(-1); - list.next = null; - - ListNode node = head; - while (node != null) { - if (!exists(list, node.val)) { - addToTail(list, node.val); - } - node = node.next; - } - return list.next; - } - - private static boolean exists(ListNode head, int val) { - ListNode node = head; - while (node != null) { - if (node.val == val) { return true; } - node = node.next; - } - return false; - } - - private static void addToTail(ListNode head, int val) { - if (head == null) { - return; - } - ListNode node = head; - while (node.next != null) { - node = node.next; - } - ListNode newNode = new ListNode(val); - node.next = newNode; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\347\247\273\351\231\244\351\223\276\350\241\250\345\205\203\347\264\240.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\347\247\273\351\231\244\351\223\276\350\241\250\345\205\203\347\264\240.java" deleted file mode 100644 index deccb7f..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\347\247\273\351\231\244\351\223\276\350\241\250\345\205\203\347\264\240.java" +++ /dev/null @@ -1,81 +0,0 @@ -package io.github.dunwu.algorithm.list; - -import org.junit.jupiter.api.Assertions; - -import java.util.List; - -/** - * @author Zhang Peng - * @see 203. 移除链表元素 - * @since 2020-06-09 - */ -public class 移除链表元素 { - - public static void main(String[] args) { - ListNode head = ListUtil.buildList(1, 2); - System.out.println(ListUtil.toList(head)); - ListNode result = removeElementByValue(head, 1); - List list = ListUtil.toList(result); - System.out.println(list); - Assertions.assertArrayEquals(new Integer[] { 2 }, list.toArray(new Integer[0])); - - head = new ListNode(1); - System.out.println(ListUtil.toList(head)); - result = removeElementByValue(head, 1); - list = ListUtil.toList(result); - System.out.println(list); - Assertions.assertArrayEquals(new Integer[] {}, list.toArray(new Integer[0])); - - head = ListUtil.buildList(1, 1); - System.out.println(ListUtil.toList(head)); - result = removeElementByValue(head, 1); - list = ListUtil.toList(result); - System.out.println(list); - Assertions.assertArrayEquals(new Integer[] {}, list.toArray(new Integer[0])); - - head = ListUtil.buildList(1, 2, 6, 3, 4, 5, 6); - System.out.println(ListUtil.toList(head)); - result = removeElementByValue(head, 6); - list = ListUtil.toList(result); - System.out.println(list); - Assertions.assertArrayEquals(new Integer[] { 1, 2, 3, 4, 5 }, list.toArray(new Integer[0])); - } - - public static ListNode removeElementByValue(ListNode head, int val) { - if (head == null) return null; - - ListNode root = new ListNode(-1); - root.next = head; - ListNode prev = root; - while (prev.next != null) { - if (prev.next.val == val) { - prev.next = prev.next.next; - } else { - prev = prev.next; - } - } - return root.next; - } - - public static ListNode removeElementByIndex(ListNode head, int index) { - if (head == null) { - return null; - } - - ListNode root = new ListNode(-1); - root.next = head; - ListNode node = root; - int pos = 0; - while (node.next != null && pos != index) { - node = node.next; - pos++; - } - - if (node.next != null) { - node.next = node.next.next; - } - - return root.next; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\350\256\276\350\256\241\351\223\276\350\241\250.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\350\256\276\350\256\241\351\223\276\350\241\250.java" deleted file mode 100644 index 11e02a1..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\350\256\276\350\256\241\351\223\276\350\241\250.java" +++ /dev/null @@ -1,132 +0,0 @@ -package io.github.dunwu.algorithm.list; - -import org.junit.jupiter.api.Assertions; - -/** - * @author Zhang Peng - * @since 2020-07-08 - */ -public class 设计链表 { - - public static void main(String[] args) { - MyLinkedList list = new MyLinkedList(); - list.addAtHead(1); - list.print(); - list.addAtTail(3); - list.print(); - list.addAtIndex(1, 2); //链表变为1-> 2-> 3 - list.print(); - System.out.println(list.get(1));//返回2 - Assertions.assertEquals(2, list.get(1)); - list.deleteAtIndex(1); //现在链表是1-> 3 - list.print(); - Assertions.assertEquals(3, list.get(1)); - } - - static class MyLinkedList { - - private Node head; - - /** - * Initialize your data structure here. - */ - public MyLinkedList() { - head = new Node(-1); - } - - /** - * Get the value of the index-th node in the linked list. If the index is invalid, return -1. - */ - public int get(int index) { - int i = 0; - Node p = head.next; - while (p.next != null && i < index) { - p = p.next; - i++; - } - return p.val; - } - - /** - * Add a node of value val before the first element of the linked list. After the insertion, the new node will - * be the first node of the linked list. - */ - public void addAtHead(int val) { - Node node = new Node(val); - if (head.next == null) { - head.next = node; - } else { - node.next = head.next; - head.next = node; - } - } - - /** - * Append a node of value val to the last element of the linked list. - */ - public void addAtTail(int val) { - Node p = head; - while (p.next != null) { - p = p.next; - } - p.next = new Node(val); - } - - /** - * Add a node of value val before the index-th node in the linked list. If index equals to the length of linked - * list, the node will be appended to the end of linked list. If index is greater than the length, the node will - * not be inserted. - */ - public void addAtIndex(int index, int val) { - int i = 0; - Node p = head.next; - while (p.next != null && i < index - 1) { - p = p.next; - } - - Node node = new Node(val); - node.next = p.next; - p.next = node; - } - - /** - * Delete the index-th node in the linked list, if the index is valid. - */ - public void deleteAtIndex(int index) { - int i = 0; - Node p = head.next; - while (p.next != null && i < index - 1) { - p = p.next; - } - - if (p.next != null) { - p.next = p.next.next; - } else { - p.next = null; - } - } - - public void print() { - Node p = head; - while (p.next != null) { - p = p.next; - System.out.print(p.val + "\t"); - } - System.out.println(); - } - - static class Node { - - int val; - Node next; - - public Node(int val) { - this.val = val; - next = null; - } - - } - - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\350\277\224\345\233\236\345\200\222\346\225\260\347\254\254k\344\270\252\350\212\202\347\202\271.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\350\277\224\345\233\236\345\200\222\346\225\260\347\254\254k\344\270\252\350\212\202\347\202\271.java" deleted file mode 100644 index fcd5f98..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\350\277\224\345\233\236\345\200\222\346\225\260\347\254\254k\344\270\252\350\212\202\347\202\271.java" +++ /dev/null @@ -1,40 +0,0 @@ -package io.github.dunwu.algorithm.list; - -import org.junit.jupiter.api.Assertions; - -/** - * 面试题 02. 返回倒数第 k 个节点 - * LCR 140. 训练计划 II - * - * @author Zhang Peng - * @since 2020-06-09 - */ -public class 返回倒数第k个节点 { - - public static void main(String[] args) { - ListNode head = ListUtil.buildList(1, 2, 3, 4, 5); - int val = kthToLast(head, 2); - Assertions.assertEquals(4, val); - - ListNode head2 = ListUtil.buildList(1); - int val2 = kthToLast(head2, 1); - Assertions.assertEquals(1, val2); - } - - public static int kthToLast(ListNode head, int k) { - ListNode fast = head; - // fast 指针先走 k 步 - for (int i = 0; i < k; i++) { - fast = fast.next; - } - // fast、slow 同时走,直到结束 - ListNode slow = head; - while (fast != null) { - fast = fast.next; - slow = slow.next; - } - // slow 现在指向第 n - k + 1 个节点,即倒数第 k 个节点 - return slow.val; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\351\223\276\350\241\250\347\232\204\344\270\255\351\227\264\347\273\223\347\202\271.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\351\223\276\350\241\250\347\232\204\344\270\255\351\227\264\347\273\223\347\202\271.java" deleted file mode 100644 index 47b9b1e..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/list/\351\223\276\350\241\250\347\232\204\344\270\255\351\227\264\347\273\223\347\202\271.java" +++ /dev/null @@ -1,41 +0,0 @@ -package io.github.dunwu.algorithm.list; - -import org.junit.jupiter.api.Assertions; - -import java.util.List; - -/** - * 876. 链表的中间结点 - * - * @author Zhang Peng - * @since 2020-06-09 - */ -public class 链表的中间结点 { - - public static void main(String[] args) { - ListNode head = ListUtil.buildList(1, 2, 3, 4, 5); - System.out.println(ListUtil.toList(head)); - ListNode result = middleNode(head); - List list = ListUtil.toList(result); - System.out.println(list); - Assertions.assertArrayEquals(new Integer[] { 3, 4, 5 }, list.toArray(new Integer[0])); - - ListNode head2 = ListUtil.buildList(1, 2, 3, 4, 5, 6); - System.out.println(ListUtil.toList(head2)); - ListNode result2 = middleNode(head2); - List list2 = ListUtil.toList(result2); - System.out.println(list2); - Assertions.assertArrayEquals(new Integer[] { 4, 5, 6 }, list2.toArray(new Integer[0])); - } - - public static ListNode middleNode(ListNode head) { - // 利用快慢指针,慢指针每次偏移一个节点,快指针每次偏移两个节点 - ListNode slow = head, fast = head; - while (fast != null && fast.next != null) { - slow = slow.next; - fast = fast.next.next; - } - return slow; - } - -} diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/map/LRUCache.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/map/LRUCache.java deleted file mode 100644 index 6723dd3..0000000 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/map/LRUCache.java +++ /dev/null @@ -1,45 +0,0 @@ -package io.github.dunwu.algorithm.map; - -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * @author Zhang Peng - * @since 2020-01-18 - */ -class LRUCache { - - private int capacity; - - // 保持插入顺序 - private Map map; - - public LRUCache(int capacity) { - this.capacity = capacity; - map = new LinkedHashMap<>(capacity); - } - - public int get(int key) { - if (map.containsKey(key)) { - int value = map.get(key); - map.remove(key); - // 保证每次查询后,都在末尾 - map.put(key, value); - return value; - } - return -1; - } - - public void put(int key, int value) { - if (map.containsKey(key)) { - map.remove(key); - } else if (map.size() == capacity) { - Iterator> iterator = map.entrySet().iterator(); - iterator.next(); - iterator.remove(); - } - map.put(key, value); - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/math/\344\270\221\346\225\260.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/math/\344\270\221\346\225\260.java" new file mode 100644 index 0000000..dace497 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/math/\344\270\221\346\225\260.java" @@ -0,0 +1,32 @@ +package io.github.dunwu.algorithm.math; + +import org.junit.jupiter.api.Assertions; + +/** + * 263. 丑数 + * + * @author Zhang Peng + * @date 2025-01-24 + */ +public class 丑数 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertTrue(s.isUgly(6)); + Assertions.assertTrue(s.isUgly(1)); + Assertions.assertFalse(s.isUgly(14)); + } + + static class Solution { + + public boolean isUgly(int n) { + if (n <= 0) { return false; } + while (n % 2 == 0) { n /= 2; } + while (n % 3 == 0) { n /= 3; } + while (n % 5 == 0) { n /= 5; } + return n == 1; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/math/\345\212\240\344\270\200.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/math/\345\212\240\344\270\200.java" new file mode 100644 index 0000000..334b604 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/math/\345\212\240\344\270\200.java" @@ -0,0 +1,38 @@ +package io.github.dunwu.algorithm.math; + +import org.junit.jupiter.api.Assertions; + +/** + * 66. 加一 + * + * @author Zhang Peng + * @since 2018-11-04 + */ +public class 加一 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertArrayEquals(new int[] { 1, 2, 4 }, s.plusOne(new int[] { 1, 2, 3 })); + Assertions.assertArrayEquals(new int[] { 4, 3, 2, 2 }, s.plusOne(new int[] { 4, 3, 2, 1 })); + Assertions.assertArrayEquals(new int[] { 1, 0, 0, 0, 0 }, s.plusOne(new int[] { 9, 9, 9, 9 })); + } + + static class Solution { + + public int[] plusOne(int[] digits) { + for (int i = digits.length - 1; i >= 0; i--) { + if (digits[i] == 9) { + digits[i] = 0; + } else { + digits[i]++; + return digits; + } + } + int[] res = new int[digits.length + 1]; + res[0] = 1; + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/\345\212\250\346\200\201\346\211\251\345\256\271\346\225\260\347\273\204\345\256\236\347\216\260\347\232\204\351\230\237\345\210\227.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/demo/\345\212\250\346\200\201\346\211\251\345\256\271\346\225\260\347\273\204\345\256\236\347\216\260\347\232\204\351\230\237\345\210\227.java" similarity index 97% rename from "codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/\345\212\250\346\200\201\346\211\251\345\256\271\346\225\260\347\273\204\345\256\236\347\216\260\347\232\204\351\230\237\345\210\227.java" rename to "codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/demo/\345\212\250\346\200\201\346\211\251\345\256\271\346\225\260\347\273\204\345\256\236\347\216\260\347\232\204\351\230\237\345\210\227.java" index dc4f3f8..f476f99 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/\345\212\250\346\200\201\346\211\251\345\256\271\346\225\260\347\273\204\345\256\236\347\216\260\347\232\204\351\230\237\345\210\227.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/demo/\345\212\250\346\200\201\346\211\251\345\256\271\346\225\260\347\273\204\345\256\236\347\216\260\347\232\204\351\230\237\345\210\227.java" @@ -1,4 +1,4 @@ -package io.github.dunwu.algorithm.queue; +package io.github.dunwu.algorithm.queue.demo; import java.util.Arrays; diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/\346\225\260\347\273\204\345\256\236\347\216\260\347\232\204\351\230\237\345\210\227.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/demo/\346\225\260\347\273\204\345\256\236\347\216\260\347\232\204\351\230\237\345\210\227.java" similarity index 97% rename from "codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/\346\225\260\347\273\204\345\256\236\347\216\260\347\232\204\351\230\237\345\210\227.java" rename to "codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/demo/\346\225\260\347\273\204\345\256\236\347\216\260\347\232\204\351\230\237\345\210\227.java" index 1c55892..0e321f7 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/\346\225\260\347\273\204\345\256\236\347\216\260\347\232\204\351\230\237\345\210\227.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/demo/\346\225\260\347\273\204\345\256\236\347\216\260\347\232\204\351\230\237\345\210\227.java" @@ -1,4 +1,4 @@ -package io.github.dunwu.algorithm.queue; +package io.github.dunwu.algorithm.queue.demo; /** * 用数组实现的队列 diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/\351\223\276\350\241\250\345\256\236\347\216\260\347\232\204\351\230\237\345\210\227.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/demo/\351\223\276\350\241\250\345\256\236\347\216\260\347\232\204\351\230\237\345\210\227.java" similarity index 97% rename from "codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/\351\223\276\350\241\250\345\256\236\347\216\260\347\232\204\351\230\237\345\210\227.java" rename to "codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/demo/\351\223\276\350\241\250\345\256\236\347\216\260\347\232\204\351\230\237\345\210\227.java" index 5237442..3117ad2 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/\351\223\276\350\241\250\345\256\236\347\216\260\347\232\204\351\230\237\345\210\227.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/demo/\351\223\276\350\241\250\345\256\236\347\216\260\347\232\204\351\230\237\345\210\227.java" @@ -1,4 +1,4 @@ -package io.github.dunwu.algorithm.queue; +package io.github.dunwu.algorithm.queue.demo; /** * 基于链表实现的队列 diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/monotonic/MonotonicQueue.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/monotonic/MonotonicQueue.java new file mode 100644 index 0000000..a13cdb2 --- /dev/null +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/monotonic/MonotonicQueue.java @@ -0,0 +1,66 @@ +package io.github.dunwu.algorithm.queue.monotonic; + +import java.util.LinkedList; + +// 单调队列的实现,可以高效维护最大值和最小值 +class MonotonicQueue> { + + // 常规队列,存储所有元素 + LinkedList q = new LinkedList<>(); + // 元素降序排列的单调队列,头部是最大值 + LinkedList maxq = new LinkedList<>(); + // 元素升序排列的单调队列,头部是最小值 + LinkedList minq = new LinkedList<>(); + + public void push(E elem) { + // 维护常规队列,直接在队尾插入元素 + q.addLast(elem); + + // 维护 maxq,将小于 elem 的元素全部删除 + while (!maxq.isEmpty() && maxq.getLast().compareTo(elem) < 0) { + maxq.pollLast(); + } + maxq.addLast(elem); + + // 维护 minq,将大于 elem 的元素全部删除 + while (!minq.isEmpty() && minq.getLast().compareTo(elem) > 0) { + minq.pollLast(); + } + minq.addLast(elem); + } + + public E max() { + // maxq 的头部是最大元素 + return maxq.getFirst(); + } + + public E min() { + // minq 的头部是最大元素 + return minq.getFirst(); + } + + public E pop() { + // 从标准队列头部弹出需要删除的元素 + E deleteVal = q.pollFirst(); + assert deleteVal != null; + + // 由于 push 的时候会删除元素,deleteVal 可能已经被删掉了 + if (deleteVal.equals(maxq.getFirst())) { + maxq.pollFirst(); + } + if (deleteVal.equals(minq.getFirst())) { + minq.pollFirst(); + } + return deleteVal; + } + + public int size() { + // 标准队列的大小即是当前队列的大小 + return q.size(); + } + + public boolean isEmpty() { + return q.isEmpty(); + } + +} \ No newline at end of file diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/monotonic/\346\273\221\345\212\250\347\252\227\345\217\243\346\234\200\345\244\247\345\200\274.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/monotonic/\346\273\221\345\212\250\347\252\227\345\217\243\346\234\200\345\244\247\345\200\274.java" new file mode 100644 index 0000000..3b9aaf2 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/monotonic/\346\273\221\345\212\250\347\252\227\345\217\243\346\234\200\345\244\247\345\200\274.java" @@ -0,0 +1,74 @@ +package io.github.dunwu.algorithm.queue.monotonic; + +import org.junit.jupiter.api.Assertions; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +/** + * 239. 滑动窗口最大值 + * + * @author Zhang Peng + * @date 2025-11-26 + */ +public class 滑动窗口最大值 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertArrayEquals(new int[] { 3, 3, 5, 5, 6, 7 }, + s.maxSlidingWindow(new int[] { 1, 3, -1, -3, 5, 3, 6, 7 }, 3)); + Assertions.assertArrayEquals(new int[] { 1 }, s.maxSlidingWindow(new int[] { 1 }, 1)); + } + + static class Solution { + + public int[] maxSlidingWindow(int[] nums, int k) { + MonotonicQueue window = new MonotonicQueue(); + List res = new ArrayList<>(); + + for (int i = 0; i < nums.length; i++) { + if (i < k - 1) { + window.push(nums[i]); + } else { + window.push(nums[i]); + res.add(window.max()); + window.pop(nums[i - (k - 1)]); + } + } + + int[] arr = new int[res.size()]; + for (int i = 0; i < res.size(); i++) { + arr[i] = res.get(i); + } + return arr; + } + + static class MonotonicQueue { + + LinkedList q = new LinkedList<>(); + + public void push(int n) { + // 将小于 n 的元素全部删除 + while (!q.isEmpty() && q.getLast() < n) { + q.pollLast(); + } + // 然后将 n 加入尾部 + q.addLast(n); + } + + public int max() { + return q.getFirst(); + } + + public void pop(int n) { + if (n == q.getFirst()) { + q.pollFirst(); + } + } + + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/monotonic/\350\256\276\350\256\241\350\207\252\345\212\251\347\273\223\347\256\227\347\263\273\347\273\237.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/monotonic/\350\256\276\350\256\241\350\207\252\345\212\251\347\273\223\347\256\227\347\263\273\347\273\237.java" new file mode 100644 index 0000000..69586cd --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/monotonic/\350\256\276\350\256\241\350\207\252\345\212\251\347\273\223\347\256\227\347\263\273\347\273\237.java" @@ -0,0 +1,44 @@ +package io.github.dunwu.algorithm.queue.monotonic; + +import org.junit.jupiter.api.Assertions; + +/** + * 739. 每日温度 + * + * @author Zhang Peng + * @date 2025-11-26 + */ +public class 设计自助结算系统 { + + public static void main(String[] args) { + Checkout c = new Checkout(); + c.add(4); + c.add(7); + Assertions.assertEquals(7, c.get_max()); + Assertions.assertEquals(4, c.remove()); + Assertions.assertEquals(7, c.get_max()); + } + + static class Checkout { + + private MonotonicQueue q; + public Checkout() { + q = new MonotonicQueue<>(); + } + + public int get_max() { + return q.isEmpty() ? -1 : q.max(); + } + + public void add(int value) { + q.push(value); + } + + public int remove() { + if (q.isEmpty()) { return -1; } + return q.pop(); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/\344\271\260\347\245\250\351\234\200\350\246\201\347\232\204\346\227\266\351\227\264.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/\344\271\260\347\245\250\351\234\200\350\246\201\347\232\204\346\227\266\351\227\264.java" new file mode 100644 index 0000000..e350c5c --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/\344\271\260\347\245\250\351\234\200\350\246\201\347\232\204\346\227\266\351\227\264.java" @@ -0,0 +1,36 @@ +package io.github.dunwu.algorithm.queue; + +import org.junit.jupiter.api.Assertions; + +/** + * 2073. 买票需要的时间 + * + * @author Zhang Peng + * @since 2025-11-26 + */ +public class 买票需要的时间 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(6, s.timeRequiredToBuy(new int[] { 2, 3, 2 }, 2)); + Assertions.assertEquals(8, s.timeRequiredToBuy(new int[] { 5, 1, 1, 1 }, 0)); + } + + static class Solution { + + public int timeRequiredToBuy(int[] tickets, int k) { + int i = 0; + int seconds = 0; + while (tickets[k] != 0) { + if (tickets[i] != 0) { + tickets[i]--; + seconds++; + } + i = (i + 1) % tickets.length; + } + return seconds; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/\346\234\200\350\277\221\347\232\204\350\257\267\346\261\202\346\254\241\346\225\260.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/\346\234\200\350\277\221\347\232\204\350\257\267\346\261\202\346\254\241\346\225\260.java" index ee1d7e7..00e74d9 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/\346\234\200\350\277\221\347\232\204\350\257\267\346\261\202\346\254\241\346\225\260.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/\346\234\200\350\277\221\347\232\204\350\257\267\346\261\202\346\254\241\346\225\260.java" @@ -4,22 +4,37 @@ import java.util.Queue; /** + * 933. 最近的请求次数 + * * @author Zhang Peng - * @see 933. 最近的请求次数 * @since 2020-06-10 */ public class 最近的请求次数 { - Queue queue; - - public 最近的请求次数() { - queue = new LinkedList<>(); + public static void main(String[] args) { + RecentCounter recentCounter = new RecentCounter(); + recentCounter.ping(1); // requests = [1],范围是 [-2999,1],返回 1 + recentCounter.ping(100); // requests = [1, 100],范围是 [-2900,100],返回 2 + recentCounter.ping(3001); // requests = [1, 100, 3001],范围是 [1,3001],返回 3 + recentCounter.ping(3002); // requests = [1, 100, 3001, 3002],范围是 [2,3002],返回 3 } - public int ping(int t) { - queue.add(t); - while (queue.peek() < t - 3000) { queue.poll(); } - return queue.size(); + static class RecentCounter { + + private Queue queue; + + public RecentCounter() { + queue = new LinkedList<>(); + } + + public int ping(int t) { + queue.offer(t); + while (queue.peek() < t - 3000) { + queue.poll(); + } + return queue.size(); + } + } } diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/\347\224\250\351\230\237\345\210\227\345\256\236\347\216\260\346\240\210.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/\347\224\250\351\230\237\345\210\227\345\256\236\347\216\260\346\240\210.java" new file mode 100644 index 0000000..7628222 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/\347\224\250\351\230\237\345\210\227\345\256\236\347\216\260\346\240\210.java" @@ -0,0 +1,69 @@ +package io.github.dunwu.algorithm.queue; + +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; +import java.util.Queue; + +/** + * 225. 用队列实现栈 + * + * @author Zhang Peng + * @since 2020-01-18 + */ +public class 用队列实现栈 { + + public static void main(String[] args) { + MyStack s1 = new MyStack(); + s1.push(1); + s1.push(2); + Assertions.assertEquals(2, s1.top()); + Assertions.assertEquals(2, s1.pop()); + Assertions.assertFalse(s1.empty()); + + int max = 10; + MyStack stack = new MyStack(); + for (int i = 1; i <= max; i++) { + stack.push(i); + } + for (int i = 1; i <= max; i++) { + Assertions.assertEquals(max - i + 1, stack.top()); + Assertions.assertEquals(max - i + 1, stack.pop()); + } + } + + static class MyStack { + + private Integer top; + Queue q; + + public MyStack() { + q = new LinkedList<>(); + } + + public void push(int x) { + q.offer(x); + top = x; + } + + public int pop() { + int size = q.size(); + for (int i = 1; i < size; i++) { + Integer val = q.poll(); + q.offer(val); + top = val; + } + return q.poll(); + } + + public int top() { + return top == null ? 0 : top; + } + + public boolean empty() { + return q.isEmpty(); + } + + } + +} diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/MyCircularDeque.java "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/\350\256\276\350\256\241\345\276\252\347\216\257\345\217\214\347\253\257\351\230\237\345\210\227.java" similarity index 93% rename from codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/MyCircularDeque.java rename to "codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/\350\256\276\350\256\241\345\276\252\347\216\257\345\217\214\347\253\257\351\230\237\345\210\227.java" index 8d4bed9..1db2d49 100644 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/MyCircularDeque.java +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/\350\256\276\350\256\241\345\276\252\347\216\257\345\217\214\347\253\257\351\230\237\345\210\227.java" @@ -1,14 +1,15 @@ package io.github.dunwu.algorithm.queue; /** + * 641. 设计循环双端队列 + * * @author Zhang Peng - * @see 641. 设计循环双端队列 * @since 2020-06-10 */ -public class MyCircularDeque { +public class 设计循环双端队列 { public static void main(String[] args) { - MyCircularDeque queue = new MyCircularDeque(3); + 设计循环双端队列 queue = new 设计循环双端队列(3); queue.insertFront(1); queue.insertFront(2); queue.insertFront(3); @@ -43,7 +44,7 @@ public static void main(String[] args) { private int capacity; /** Initialize your data structure here. Set the size of the deque to be k. */ - public MyCircularDeque(int k) { + public 设计循环双端队列(int k) { this.capacity = k + 1; this.data = new int[this.capacity]; } diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/\350\256\276\350\256\241\345\276\252\347\216\257\351\230\237\345\210\227.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/\350\256\276\350\256\241\345\276\252\347\216\257\351\230\237\345\210\227.java" deleted file mode 100644 index 380f53f..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/\350\256\276\350\256\241\345\276\252\347\216\257\351\230\237\345\210\227.java" +++ /dev/null @@ -1,111 +0,0 @@ -package io.github.dunwu.algorithm.queue; - -/** - * @author Zhang Peng - * @see 622. 设计循环队列 - * @since 2020-06-10 - */ -public class 设计循环队列 { - - public static void main(String[] args) { - 设计循环队列 queue = new 设计循环队列(3); - queue.enQueue(1); - queue.enQueue(2); - queue.enQueue(3); - queue.enQueue(4); - queue.printAll(); - System.out.println("rear: " + queue.Rear()); - System.out.println("full: " + queue.isFull()); - queue.deQueue(); - queue.enQueue(4); - queue.printAll(); - System.out.println("rear: " + queue.Rear()); - } - - private int[] data; - private int head; - // head表示队头下标,tail表示队尾下标 - private int tail; - private int capacity; - - // 申请一个大小为capacity的数组(由于循环队列会浪费一个空间,所以如果想要存储元素数为capacity,数组维度n=capacity+1) - public 设计循环队列(int k) { - this.capacity = k + 1; - this.data = new int[capacity]; - this.head = 0; - this.tail = 0; - } - - /** Insert an element into the circular queue. Return true if the operation is successful. */ - public boolean enQueue(int value) { - if (isFull()) { - return false; - } - - this.data[tail] = value; - tail = (tail + 1) % capacity; - return true; - } - - /** Delete an element from the circular queue. Return true if the operation is successful. */ - public boolean deQueue() { - if (isEmpty()) { - return false; - } - - head = (head + 1) % capacity; - return true; - } - - /** Get the front item from the queue. */ - public int Front() { - if (isEmpty()) { - return -1; - } - - return data[head]; - } - - /** Get the last item from the queue. */ - public int Rear() { - if (isEmpty()) { - return -1; - } - - int temp = (tail - 1 + capacity) % capacity; - return data[temp]; - } - - /** Checks whether the circular queue is empty or not. */ - public boolean isEmpty() { - if (head == tail) { - return true; - } - return false; - } - - /** Checks whether the circular queue is full or not. */ - public boolean isFull() { - if ((tail + 1) % capacity == head) { - return true; - } - return false; - } - - public void printAll() { - if (head == tail) { - System.out.println("队列已空"); - return; - } - for (int i = head; i != tail; ) { - System.out.print(data[i] + "\t"); - if (i == capacity - 1) { - i = 0; - } else { - i++; - } - } - System.out.println(); - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/\350\266\205\347\272\247\344\270\221\346\225\260.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/\350\266\205\347\272\247\344\270\221\346\225\260.java" new file mode 100644 index 0000000..ab5b321 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/queue/\350\266\205\347\272\247\344\270\221\346\225\260.java" @@ -0,0 +1,73 @@ +package io.github.dunwu.algorithm.queue; + +import org.junit.jupiter.api.Assertions; + +import java.util.HashSet; +import java.util.PriorityQueue; +import java.util.Set; + +/** + * 313. 超级丑数 + * + * @author Zhang Peng + * @date 2025-01-24 + */ +public class 超级丑数 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(32, s.nthSuperUglyNumber(12, new int[] { 2, 7, 13, 19 })); + Assertions.assertEquals(1, s.nthSuperUglyNumber(1, new int[] { 2, 3, 5 })); + + Solution2 s2 = new Solution2(); + Assertions.assertEquals(32, s2.nthSuperUglyNumber(12, new int[] { 2, 7, 13, 19 })); + Assertions.assertEquals(1, s2.nthSuperUglyNumber(1, new int[] { 2, 3, 5 })); + } + + // 优先队列(堆)方案 + static class Solution { + + public int nthSuperUglyNumber(int n, int[] primes) { + Set set = new HashSet<>(); + PriorityQueue queue = new PriorityQueue<>(); + set.add(1L); + queue.add(1L); + for (int i = 1; i <= n; i++) { + long curVal = queue.poll(); + if (i == n) { return (int) curVal; } + for (int prime : primes) { + long nextVal = curVal * prime; + if (!set.contains(nextVal)) { + set.add(nextVal); + queue.add(nextVal); + } + } + } + return -1; + } + + } + + // 多路归并方案 + static class Solution2 { + + public int nthSuperUglyNumber(int n, int[] nums) { + int m = nums.length; + PriorityQueue q = new PriorityQueue<>((a, b) -> a[0] - b[0]); + for (int i = 0; i < m; i++) { + q.add(new int[] { nums[i], i, 0 }); + } + int[] dp = new int[n]; + dp[0] = 1; + for (int j = 1; j < n; ) { + int[] poll = q.poll(); + int num = poll[0], i = poll[1], idx = poll[2]; + if (num != dp[j - 1]) dp[j++] = num; + q.add(new int[] { dp[idx + 1] * nums[i], i, idx + 1 }); + } + return dp[n - 1]; + } + + } + +} diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/Sort.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/Sort.java index d2c28ca..bfbe8ca 100644 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/Sort.java +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/Sort.java @@ -1,6 +1,8 @@ package io.github.dunwu.algorithm.sort; /** + * 排序通用泛型接口 + * * @author Zhang Peng */ public interface Sort { diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/SortStrategy.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/SortStrategy.java index 3398632..5e2f7ef 100644 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/SortStrategy.java +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/SortStrategy.java @@ -1,29 +1,27 @@ package io.github.dunwu.algorithm.sort; import io.github.dunwu.algorithm.util.ArrayUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; /** * 使用策略模式,对算法进行包装 * * @author Zhang Peng */ +@Slf4j public class SortStrategy { - private static final Logger logger = LoggerFactory.getLogger(SortStrategy.class); - - private Sort sort; + private final Sort sort; public SortStrategy(Sort sort) { this.sort = sort; } public void sort(Integer[] list) { - logger.info(this.sort.getClass().getSimpleName() + " 排序开始:"); - logger.info("排序前: {}", ArrayUtil.getArrayString(list, 0, list.length - 1)); + System.out.printf("=================== %s 排序开始 ===================\n", this.sort.getClass().getSimpleName()); + System.out.printf("【排序前】\n%s\n", ArrayUtil.getArrayString(list, 0, list.length - 1)); this.sort.sort(list); - logger.info("排序后: {}", ArrayUtil.getArrayString(list, 0, list.length - 1)); + System.out.printf("【排序后】\n%s\n", ArrayUtil.getArrayString(list, 0, list.length - 1)); } } diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/strategy/BubbleSort.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/strategy/BubbleSort.java index be160ef..3c10699 100644 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/strategy/BubbleSort.java +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/strategy/BubbleSort.java @@ -23,8 +23,7 @@ public > void sort(T[] list) { list[j] = temp; } } - - ArrayUtil.debugLogArray(list, 0, list.length - 1, String.format("第 %d 趟:", i + 1)); + ArrayUtil.printArray(list, 0, list.length - 1, String.format("第 %02d 趟", i + 1)); } } diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/strategy/BubbleSort2.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/strategy/BubbleSort2.java index 6452573..7bebb12 100644 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/strategy/BubbleSort2.java +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/strategy/BubbleSort2.java @@ -12,12 +12,11 @@ public class BubbleSort2 implements Sort { @Override public > void sort(T[] list) { - // 交换标志 - boolean bChange = false; // 要遍历的次数 for (int i = 0; i < list.length - 1; i++) { - bChange = false; + // 交换标志 + boolean changed = false; // 从后向前依次的比较相邻两个数的大小,遍历一次后,把数组中第i小的数放在第i个位置上 for (int j = list.length - 1; j > i; j--) { // 比较相邻的元素,如果前面的数大于后面的数,则交换 @@ -25,16 +24,16 @@ public > void sort(T[] list) { T temp = list[j - 1]; list[j - 1] = list[j]; list[j] = temp; - bChange = true; + changed = true; } } // 如果标志为false,说明本轮遍历没有交换,已经是有序数列,可以结束排序 - if (false == bChange) { + if (!changed) { break; } - ArrayUtil.debugLogArray(list, 0, list.length - 1, String.format("第 %d 趟:", i + 1)); + ArrayUtil.printArray(list, 0, list.length - 1, String.format("第 %02d 趟", i + 1)); } } diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/strategy/HeapSort.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/strategy/HeapSort.java index a5fc017..d403207 100644 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/strategy/HeapSort.java +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/strategy/HeapSort.java @@ -27,7 +27,7 @@ public > void sort(T[] list) { // 筛选 R[0] 结点,得到i-1个结点的堆 adjustHeat(list, 0, i); - ArrayUtil.debugLogArray(list, 0, list.length - 1, String.format("第 %d 趟:", list.length - i)); + ArrayUtil.printArray(list, 0, list.length - 1, String.format("第 %02d 趟", list.length - i)); } } diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/strategy/InsertSort.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/strategy/InsertSort.java index 4847fcb..9d1d49b 100644 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/strategy/InsertSort.java +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/strategy/InsertSort.java @@ -12,9 +12,6 @@ public class InsertSort implements Sort { @Override public > void sort(T[] list) { - // 打印第一个元素 - ArrayUtil.debugLogArray(list, 0, 0, String.format("i = %d:\t", 0)); - // 第1个数肯定是有序的,从第2个数开始遍历,依次插入有序序列 for (int i = 1; i < list.length; i++) { int j = 0; @@ -27,7 +24,7 @@ public > void sort(T[] list) { } list[j + 1] = temp; - ArrayUtil.debugLogArray(list, 0, list.length - 1, String.format("i = %d:\t", i)); + ArrayUtil.printArray(list, 0, list.length - 1, String.format("第 %02d 趟", i)); } } diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/strategy/MergeSort.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/strategy/MergeSort.java index 5ac1e6c..56d36ac 100644 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/strategy/MergeSort.java +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/strategy/MergeSort.java @@ -9,7 +9,7 @@ public class MergeSort implements Sort { public > void sort(T[] list) { for (int gap = 1; gap < list.length; gap = 2 * gap) { mergeSort(list, gap, list.length); - ArrayUtil.debugLogArray(list, 0, list.length - 1, String.format("gap = %d", gap)); + ArrayUtil.printArray(list, 0, list.length - 1, String.format("gap = %d", gap)); } } diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/strategy/QuickSort.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/strategy/QuickSort.java index e821cb6..03ee815 100644 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/strategy/QuickSort.java +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/strategy/QuickSort.java @@ -42,7 +42,7 @@ private > void quickSort(T[] list, int left, int right) // 对数组进行分割,取出下次分割的基准标号 int base = division(list, left, right); - ArrayUtil.debugLogArray(list, left, right, String.format("base = %d: ", list[base])); + ArrayUtil.printArray(list, left, right, String.format("base = %d: ", list[base])); // 对“基准标号“左侧的一组数值进行递归的切割,以至于将这些数值完整的排序 quickSort(list, left, base - 1); diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/strategy/SelectionSort.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/strategy/SelectionSort.java index 128b255..319cc81 100644 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/strategy/SelectionSort.java +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/strategy/SelectionSort.java @@ -30,7 +30,7 @@ public > void sort(T[] list) { list[index] = list[i]; list[i] = temp; - ArrayUtil.debugLogArray(list, 0, list.length - 1, String.format("第 %d 趟:", i + 1)); + ArrayUtil.printArray(list, 0, list.length - 1, String.format("第 %02d 趟:", i + 1)); } } diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/strategy/ShellSort.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/strategy/ShellSort.java index 08c9f5f..7b32584 100644 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/strategy/ShellSort.java +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/sort/strategy/ShellSort.java @@ -27,7 +27,7 @@ public > void sort(T[] list) { list[j + gap] = temp; } - ArrayUtil.debugLogArray(list, 0, list.length - 1, String.format("gap = %d:", gap)); + ArrayUtil.printArray(list, 0, list.length - 1, String.format("gap = %d:", gap)); // 减小增量 gap = gap / 2; } diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/StackBasedOnLinkedList.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/StackBasedOnLinkedList.java deleted file mode 100644 index f45a3de..0000000 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/StackBasedOnLinkedList.java +++ /dev/null @@ -1,68 +0,0 @@ -package io.github.dunwu.algorithm.stack; - -/** - * 基于链表实现的栈。 - *

- * Author: Zheng - */ -public class StackBasedOnLinkedList { - - public static void main(String[] args) { - StackBasedOnLinkedList stack = new StackBasedOnLinkedList(); - stack.push(1); - stack.push(2); - stack.push(3); - stack.printAll(); - System.out.println("pop " + stack.pop()); - System.out.println("pop " + stack.pop()); - System.out.println("pop " + stack.pop()); - } - - private Node top = null; - - public void push(int value) { - Node node = new Node(value, null); - if (top == null) { - top = node; - } else { - node.next = top; - top = node; - } - } - - /** - * 我用-1表示栈中没有数据。 - */ - public int pop() { - if (top == null) return -1; - int val = top.data; - top = top.next; - return val; - } - - public void printAll() { - Node p = top; - while (p != null) { - System.out.print(p.data + " "); - p = p.next; - } - System.out.println(); - } - - private static class Node { - - private int data; - private Node next; - - public Node(int data, Node next) { - this.data = data; - this.next = next; - } - - public int getData() { - return data; - } - - } - -} diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/monotonic/package-info.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/monotonic/package-info.java new file mode 100644 index 0000000..6a2fefd --- /dev/null +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/monotonic/package-info.java @@ -0,0 +1,7 @@ +/** + * 通过「单调栈」解决「下一个更大元素」,「上一个更小元素」等类型问题 + * + * @author Zhang Peng + * @date 2025-11-26 + */ +package io.github.dunwu.algorithm.stack.monotonic; \ No newline at end of file diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/monotonic/\344\270\213\344\270\200\344\270\252\346\233\264\345\244\247\345\205\203\347\264\240.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/monotonic/\344\270\213\344\270\200\344\270\252\346\233\264\345\244\247\345\205\203\347\264\240.java" new file mode 100644 index 0000000..d18dd54 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/monotonic/\344\270\213\344\270\200\344\270\252\346\233\264\345\244\247\345\205\203\347\264\240.java" @@ -0,0 +1,48 @@ +package io.github.dunwu.algorithm.stack.monotonic; + +import org.junit.jupiter.api.Assertions; + +import java.util.HashMap; +import java.util.Map; +import java.util.Stack; + +/** + * 496. 下一个更大元素 I + * + * @author Zhang Peng + * @date 2025-08-11 + */ +public class 下一个更大元素 { + + public static void main(String[] args) { + Solution s = new Solution(); + int[] output1 = s.nextGreaterElement(new int[] { 4, 1, 2 }, new int[] { 1, 3, 4, 2 }); + Assertions.assertArrayEquals(new int[] { -1, 3, -1 }, output1); + int[] output2 = s.nextGreaterElement(new int[] { 2, 4 }, new int[] { 1, 2, 3, 4 }); + Assertions.assertArrayEquals(new int[] { 3, -1 }, output2); + } + + // 采用单调栈解决问题,算法复杂度:O(n) + static class Solution { + + public int[] nextGreaterElement(int[] nums1, int[] nums2) { + Stack stack = new Stack<>(); + Map map = new HashMap<>(); + for (int i = nums2.length - 1; i >= 0; i--) { + while (!stack.isEmpty() && stack.peek() <= nums2[i]) { + stack.pop(); + } + int largerVal = stack.isEmpty() ? -1 : stack.peek(); + map.put(nums2[i], largerVal); + stack.push(nums2[i]); + } + + for (int i = 0; i < nums1.length; i++) { + nums1[i] = map.get(nums1[i]); + } + return nums1; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/monotonic/\344\270\213\344\270\200\344\270\252\346\233\264\345\244\247\345\205\203\347\264\2402.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/monotonic/\344\270\213\344\270\200\344\270\252\346\233\264\345\244\247\345\205\203\347\264\2402.java" new file mode 100644 index 0000000..b2fe027 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/monotonic/\344\270\213\344\270\200\344\270\252\346\233\264\345\244\247\345\205\203\347\264\2402.java" @@ -0,0 +1,42 @@ +package io.github.dunwu.algorithm.stack.monotonic; + +import org.junit.jupiter.api.Assertions; + +import java.util.Stack; + +/** + * 503. 下一个更大元素 II + * + * @author Zhang Peng + * @date 2025-11-26 + */ +public class 下一个更大元素2 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertArrayEquals(new int[] { 2, -1, 2 }, s.nextGreaterElements(new int[] { 1, 2, 1 })); + Assertions.assertArrayEquals(new int[] { 2, 3, 4, -1, 4 }, s.nextGreaterElements(new int[] { 1, 2, 3, 4, 3 })); + } + + static class Solution { + + public int[] nextGreaterElements(int[] nums) { + int n = nums.length; + int[] res = new int[n]; + Stack s = new Stack<>(); + for (int i = 2 * n - 1; i >= 0; i--) { + int index = i % n; + // 遍历栈,将小于当前元素的值都踢了 + while (!s.isEmpty() && s.peek() <= nums[index]) { + s.pop(); + } + // nums[i] 下一个更大元素在栈顶 + res[index] = s.isEmpty() ? -1 : s.peek(); + s.push(nums[index]); + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/monotonic/\345\225\206\345\223\201\346\212\230\346\211\243\345\220\216\347\232\204\346\234\200\347\273\210\344\273\267\346\240\274.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/monotonic/\345\225\206\345\223\201\346\212\230\346\211\243\345\220\216\347\232\204\346\234\200\347\273\210\344\273\267\346\240\274.java" new file mode 100644 index 0000000..9243ec1 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/monotonic/\345\225\206\345\223\201\346\212\230\346\211\243\345\220\216\347\232\204\346\234\200\347\273\210\344\273\267\346\240\274.java" @@ -0,0 +1,39 @@ +package io.github.dunwu.algorithm.stack.monotonic; + +import org.junit.jupiter.api.Assertions; + +import java.util.Stack; + +/** + * 1475. 商品折扣后的最终价格 + * + * @author Zhang Peng + * @date 2025-11-26 + */ +public class 商品折扣后的最终价格 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertArrayEquals(new int[] { 4, 2, 4, 2, 3 }, s.finalPrices(new int[] { 8, 4, 6, 2, 3 })); + Assertions.assertArrayEquals(new int[] { 1, 2, 3, 4, 5 }, s.finalPrices(new int[] { 1, 2, 3, 4, 5 })); + Assertions.assertArrayEquals(new int[] { 9, 0, 1, 6 }, s.finalPrices(new int[] { 10, 1, 1, 6 })); + } + + static class Solution { + + public int[] finalPrices(int[] prices) { + int[] res = new int[prices.length]; + Stack stack = new Stack<>(); + for (int i = prices.length - 1; i >= 0; i--) { + while (!stack.isEmpty() && stack.peek() > prices[i]) { + stack.pop(); + } + res[i] = stack.isEmpty() ? prices[i] : prices[i] - stack.peek(); + stack.push(prices[i]); + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/monotonic/\346\234\200\347\237\255\346\227\240\345\272\217\350\277\236\347\273\255\345\255\220\346\225\260\347\273\204.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/monotonic/\346\234\200\347\237\255\346\227\240\345\272\217\350\277\236\347\273\255\345\255\220\346\225\260\347\273\204.java" new file mode 100644 index 0000000..077f1c8 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/monotonic/\346\234\200\347\237\255\346\227\240\345\272\217\350\277\236\347\273\255\345\255\220\346\225\260\347\273\204.java" @@ -0,0 +1,89 @@ +package io.github.dunwu.algorithm.stack.monotonic; + +import org.junit.jupiter.api.Assertions; + +import java.util.Arrays; +import java.util.Stack; + +/** + * 581. 最短无序连续子数组 + * + * @author Zhang Peng + * @date 2025-11-26 + */ +public class 最短无序连续子数组 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(5, s.findUnsortedSubarray(new int[] { 2, 6, 4, 8, 10, 9, 15 })); + Assertions.assertEquals(0, s.findUnsortedSubarray(new int[] { 1, 2, 3, 4 })); + Assertions.assertEquals(0, s.findUnsortedSubarray(new int[] { 1 })); + + Solution2 s2 = new Solution2(); + Assertions.assertEquals(5, s2.findUnsortedSubarray(new int[] { 2, 6, 4, 8, 10, 9, 15 })); + Assertions.assertEquals(0, s2.findUnsortedSubarray(new int[] { 1, 2, 3, 4 })); + Assertions.assertEquals(0, s2.findUnsortedSubarray(new int[] { 1 })); + } + + // 排序解法 + static class Solution { + + public int findUnsortedSubarray(int[] nums) { + int[] temp = Arrays.copyOf(nums, nums.length); + Arrays.sort(temp); + int left = Integer.MAX_VALUE, right = Integer.MIN_VALUE; + for (int i = 0; i < nums.length; i++) { + if (temp[i] != nums[i]) { + left = i; + break; + } + } + for (int i = nums.length - 1; i >= 0; i--) { + if (temp[i] != nums[i]) { + right = i; + break; + } + } + if (left == Integer.MAX_VALUE && right == Integer.MIN_VALUE) { + // nums 本来就是有序的 + return 0; + } + return right - left + 1; + } + + } + + // 单调栈解法 + static class Solution2 { + + public int findUnsortedSubarray(int[] nums) { + int n = nums.length; + int left = Integer.MAX_VALUE, right = Integer.MIN_VALUE; + // 递增栈,存储元素索引 + Stack incrStk = new Stack<>(); + for (int i = 0; i < n; i++) { + while (!incrStk.isEmpty() && nums[incrStk.peek()] > nums[i]) { + // 弹出的元素都是乱序元素,其中最小的索引就是乱序子数组的左边界 + left = Math.min(left, incrStk.pop()); + } + incrStk.push(i); + } + // 递减栈,存储元素索引 + Stack decrStk = new Stack<>(); + for (int i = n - 1; i >= 0; i--) { + while (!decrStk.isEmpty() && nums[decrStk.peek()] < nums[i]) { + // 弹出的元素都是乱序元素,其中最大的索引就是乱序子数组的右边界 + right = Math.max(right, decrStk.pop()); + } + decrStk.push(i); + } + if (left == Integer.MAX_VALUE && right == Integer.MIN_VALUE) { + // 说明单调栈没有弹出任何元素,即 nums 本来就是有序的 + return 0; + } + return right - left + 1; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/monotonic/\346\257\217\346\227\245\346\270\251\345\272\246.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/monotonic/\346\257\217\346\227\245\346\270\251\345\272\246.java" new file mode 100644 index 0000000..774fe37 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/monotonic/\346\257\217\346\227\245\346\270\251\345\272\246.java" @@ -0,0 +1,40 @@ +package io.github.dunwu.algorithm.stack.monotonic; + +import org.junit.jupiter.api.Assertions; + +import java.util.Stack; + +/** + * 739. 每日温度 + * + * @author Zhang Peng + * @date 2025-11-26 + */ +public class 每日温度 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertArrayEquals(new int[] { 1, 1, 4, 2, 1, 1, 0, 0 }, + s.dailyTemperatures(new int[] { 73, 74, 75, 71, 69, 72, 76, 73 })); + Assertions.assertArrayEquals(new int[] { 1, 1, 1, 0 }, + s.dailyTemperatures(new int[] { 30, 40, 50, 60 })); + } + + static class Solution { + + public int[] dailyTemperatures(int[] t) { + int[] res = new int[t.length]; + Stack s = new Stack<>(); + for (int i = t.length - 1; i >= 0; i--) { + while (!s.isEmpty() && s.peek()[1] <= t[i]) { + s.pop(); + } + res[i] = s.isEmpty() ? 0 : s.peek()[0] - i; + s.push(new int[] { i, t[i] }); + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/monotonic/\347\247\273\346\216\211K\344\275\215\346\225\260\345\255\227.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/monotonic/\347\247\273\346\216\211K\344\275\215\346\225\260\345\255\227.java" new file mode 100644 index 0000000..aeb64a5 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/monotonic/\347\247\273\346\216\211K\344\275\215\346\225\260\345\255\227.java" @@ -0,0 +1,53 @@ +package io.github.dunwu.algorithm.stack.monotonic; + +import org.junit.jupiter.api.Assertions; + +import java.util.Stack; + +/** + * 402. 移掉 K 位数字 + * + * @author Zhang Peng + * @date 2025-11-26 + */ +public class 移掉K位数字 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals("1219", s.removeKdigits("1432219", 3)); + Assertions.assertEquals("200", s.removeKdigits("10200", 1)); + Assertions.assertEquals("0", s.removeKdigits("10", 2)); + } + + static class Solution { + + public String removeKdigits(String num, int k) { + Stack s = new Stack<>(); + for (char c : num.toCharArray()) { + // 单调栈代码模板 + while (!s.isEmpty() && c < s.peek() && k > 0) { + s.pop(); + k--; + } + // 防止 0 作为数字的开头 + if (s.isEmpty() && c == '0') { continue; } + s.push(c); + } + + // 此时栈中元素单调递增,若 k 还没用完的话删掉栈顶元素 + while (!s.isEmpty() && k > 0) { + s.pop(); + k--; + } + // 若最后没剩下数字,就是 0 + if (s.isEmpty()) { return "0"; } + // 将栈中字符转化成字符串 + StringBuilder sb = new StringBuilder(); + while (!s.isEmpty()) { sb.append(s.pop()); } + // 出栈顺序和字符串顺序是反的 + return sb.reverse().toString(); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/monotonic/\350\202\241\347\245\250\344\273\267\346\240\274\350\267\250\345\272\246.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/monotonic/\350\202\241\347\245\250\344\273\267\346\240\274\350\267\250\345\272\246.java" new file mode 100644 index 0000000..4852eb3 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/monotonic/\350\202\241\347\245\250\344\273\267\346\240\274\350\267\250\345\272\246.java" @@ -0,0 +1,83 @@ +package io.github.dunwu.algorithm.stack.monotonic; + +import org.junit.jupiter.api.Assertions; + +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +/** + * 901. 股票价格跨度 + * + * @author Zhang Peng + * @date 2025-11-26 + */ +public class 股票价格跨度 { + + public static void main(String[] args) { + StockSpanner stock = new StockSpanner(); + Assertions.assertEquals(1, stock.next(100)); + Assertions.assertEquals(1, stock.next(80)); + Assertions.assertEquals(1, stock.next(60)); + Assertions.assertEquals(2, stock.next(70)); + Assertions.assertEquals(1, stock.next(60)); + Assertions.assertEquals(4, stock.next(75)); + Assertions.assertEquals(6, stock.next(85)); + + StockSpanner2 stock2 = new StockSpanner2(); + Assertions.assertEquals(1, stock2.next(100)); + Assertions.assertEquals(1, stock2.next(80)); + Assertions.assertEquals(1, stock2.next(60)); + Assertions.assertEquals(2, stock2.next(70)); + Assertions.assertEquals(1, stock2.next(60)); + Assertions.assertEquals(4, stock2.next(75)); + Assertions.assertEquals(6, stock2.next(85)); + } + + static class StockSpanner { + + private final List l; + + public StockSpanner() { + l = new ArrayList<>(); + } + + public int next(int price) { + int count = 1; + for (int i = l.size() - 1; i >= 0; i--) { + if (l.get(i) > price) { + break; + } + count++; + } + l.add(price); + return count; + } + + } + + static class StockSpanner2 { + + // int[] 记录 {价格,小于等于该价格的天数} 二元组 + private final Stack s; + + public StockSpanner2() { + s = new Stack<>(); + } + + public int next(int price) { + // 算上当天 + int count = 1; + // 单调栈模板 + while (!s.isEmpty() && s.peek()[0] <= price) { + // 挤掉价格低于 price 的记录 + int[] prev = s.pop(); + count += prev[1]; + } + s.push(new int[] { price, count }); + return count; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/monotonic/\350\275\246\344\275\215.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/monotonic/\350\275\246\344\275\215.java" new file mode 100644 index 0000000..8005fcc --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/monotonic/\350\275\246\344\275\215.java" @@ -0,0 +1,70 @@ +package io.github.dunwu.algorithm.stack.monotonic; + +import org.junit.jupiter.api.Assertions; + +import java.util.Arrays; +import java.util.Stack; + +/** + * 853. 车队 + * + * @author Zhang Peng + * @date 2025-11-26 + */ +public class 车位 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(3, s.carFleet(12, new int[] { 10, 8, 0, 5, 3 }, new int[] { 2, 4, 1, 1, 3 })); + Assertions.assertEquals(1, s.carFleet(10, new int[] { 3 }, new int[] { 3 })); + Assertions.assertEquals(1, s.carFleet(100, new int[] { 0, 2, 4 }, new int[] { 4, 2, 1 })); + } + + static class Solution { + + public int carFleet(int target, int[] position, int[] speed) { + + int n = position.length; + int[][] cars = new int[n][2]; + for (int i = 0; i < n; i++) { + cars[i][0] = position[i]; + cars[i][1] = speed[i]; + } + + // 按照初始位置,从小到大排序 + Arrays.sort(cars, (int[] a, int[] b) -> { + return Integer.compare(a[0], b[0]); + }); + + // 计算每辆车到达终点的时间 + double[] times = new double[n]; + for (int i = 0; i < n; i++) { + int[] car = cars[i]; + times[i] = (double) (target - car[0]) / car[1]; + } + + // 使用单调栈计算车队的数量 + Stack s = new Stack<>(); + for (double t : times) { + while (!s.isEmpty() && t >= s.peek()) { + s.pop(); + } + s.push(t); + } + return s.size(); + + // 避免使用栈模拟,倒序遍历取递增序列就是答案 + // int res = 0; + // double maxTime = 0; + // for (int i = n - 1; i >= 0; i--) { + // if (time[i] > maxTime) { + // maxTime = time[i]; + // res++; + // } + // } + // return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/monotonic/\351\223\276\350\241\250\344\270\255\347\232\204\344\270\213\344\270\200\344\270\252\346\233\264\345\244\247\350\212\202\347\202\271.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/monotonic/\351\223\276\350\241\250\344\270\255\347\232\204\344\270\213\344\270\200\344\270\252\346\233\264\345\244\247\350\212\202\347\202\271.java" new file mode 100644 index 0000000..cd746cc --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/monotonic/\351\223\276\350\241\250\344\270\255\347\232\204\344\270\213\344\270\200\344\270\252\346\233\264\345\244\247\350\212\202\347\202\271.java" @@ -0,0 +1,53 @@ +package io.github.dunwu.algorithm.stack.monotonic; + +import io.github.dunwu.algorithm.linkedlist.ListNode; +import org.junit.jupiter.api.Assertions; + +import java.util.ArrayList; +import java.util.Stack; + +/** + * 1019. 链表中的下一个更大节点 + * + * @author Zhang Peng + * @date 2025-11-26 + */ +public class 链表中的下一个更大节点 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertArrayEquals(new int[] { 5, 5, 0 }, + s.nextLargerNodes(ListNode.buildList(2, 1, 5))); + Assertions.assertArrayEquals(new int[] { 7, 0, 5, 5, 0 }, + s.nextLargerNodes(ListNode.buildList(2, 7, 4, 3, 5))); + } + + static class Solution { + + public int[] nextLargerNodes(ListNode head) { + // 把单链表转化成数组,方便通过索引访问 + ArrayList nums = new ArrayList<>(); + ListNode p = head; + while (p != null) { + nums.add(p.val); + p = p.next; + } + + // 存放答案的数组 + int[] res = new int[nums.size()]; + Stack stack = new Stack<>(); + // 单调栈模板,求下一个更大元素,从后往前遍历 + for (int i = nums.size() - 1; i >= 0; i--) { + while (!stack.isEmpty() && stack.peek() <= nums.get(i)) { + stack.pop(); + } + // 本题要求没有下一个更大元素时返回 0 + res[i] = stack.isEmpty() ? 0 : stack.peek(); + stack.push(nums.get(i)); + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/monotonic/\351\230\237\345\210\227\344\270\255\345\217\257\344\273\245\347\234\213\345\210\260\347\232\204\344\272\272\346\225\260.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/monotonic/\351\230\237\345\210\227\344\270\255\345\217\257\344\273\245\347\234\213\345\210\260\347\232\204\344\272\272\346\225\260.java" new file mode 100644 index 0000000..8257b94 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/monotonic/\351\230\237\345\210\227\344\270\255\345\217\257\344\273\245\347\234\213\345\210\260\347\232\204\344\272\272\346\225\260.java" @@ -0,0 +1,45 @@ +package io.github.dunwu.algorithm.stack.monotonic; + +import org.junit.jupiter.api.Assertions; + +import java.util.Stack; + +/** + * 1944. 队列中可以看到的人数 + * + * @author Zhang Peng + * @date 2025-12-19 + */ +public class 队列中可以看到的人数 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(new int[] { 3, 1, 2, 1, 1, 0 }, s.canSeePersonsCount(new int[] { 10, 6, 8, 5, 11, 9 })); + Assertions.assertEquals(new int[] { 4, 1, 1, 1, 0 }, s.canSeePersonsCount(new int[] { 5, 1, 2, 3, 10 })); + } + + static class Solution { + + public int[] canSeePersonsCount(int[] heights) { + int n = heights.length; + int[] res = new int[n]; + // int[] 记录 {身高,小于等于该身高的人数} 二元组 + Stack stk = new Stack<>(); + for (int i = n - 1; i >= 0; i--) { + // 记录右侧比自己矮的人 + int count = 0; + // 单调栈模板,计算下一个更大或相等元素(身高) + while (!stk.isEmpty() && heights[i] > stk.peek()) { + stk.pop(); + count++; + } + // 不仅可以看到比自己矮的人,如果后面存在更高的的人,也可以看到这个高人 + res[i] = stk.isEmpty() ? count : count + 1; + stk.push(heights[i]); + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/template/\345\215\225\350\260\203\346\240\210\347\256\227\346\263\225\346\250\241\346\235\277.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/template/\345\215\225\350\260\203\346\240\210\347\256\227\346\263\225\346\250\241\346\235\277.java" new file mode 100644 index 0000000..99192ab --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/template/\345\215\225\350\260\203\346\240\210\347\256\227\346\263\225\346\250\241\346\235\277.java" @@ -0,0 +1,172 @@ +package io.github.dunwu.algorithm.stack.template; + +import java.util.Stack; + +/** + * 单调栈算法模板 + * + * @author Zhang Peng + * @date 2025-12-19 + */ +public class 单调栈算法模板 { + + /** + * 下一个更大的元素:计算 nums 中每个元素的下一个更大元素 + */ + int[] nextGreaterElement(int[] nums) { + int n = nums.length; + // 存放答案的数组 + int[] res = new int[n]; + Stack s = new Stack<>(); + // 因为是求 nums[i] 后面的元素,所以倒着往栈里放 + for (int i = n - 1; i >= 0; i--) { + // 删掉 nums[i] 后面较小的元素 + while (!s.isEmpty() && s.peek() <= nums[i]) { + s.pop(); + } + // 现在栈顶就是 nums[i] 身后的更大元素 + res[i] = s.isEmpty() ? -1 : s.peek(); + s.push(nums[i]); + } + return res; + } + + /** + * 下一个更大或相等的元素:计算 nums 中每个元素的下一个更大或相等的元素 + */ + int[] nextGreaterOrEqualElement(int[] nums) { + int n = nums.length; + int[] res = new int[n]; + Stack s = new Stack<>(); + for (int i = n - 1; i >= 0; i--) { + // 把这里改成 < 号 + while (!s.isEmpty() && s.peek() < nums[i]) { + s.pop(); + } + // 现在栈顶就是 nums[i] 身后的大于等于 nums[i] 的元素 + res[i] = s.isEmpty() ? -1 : s.peek(); + s.push(nums[i]); + } + return res; + } + + /** + * 下一个更小的元素:计算 nums 中每个元素的下一个更小的元素 + */ + int[] nextLessElement(int[] nums) { + int n = nums.length; + // 存放答案的数组 + int[] res = new int[n]; + Stack s = new Stack<>(); + // 倒着往栈里放 + for (int i = n - 1; i >= 0; i--) { + // 删掉 nums[i] 后面较大的元素 + while (!s.isEmpty() && s.peek() >= nums[i]) { + s.pop(); + } + // 现在栈顶就是 nums[i] 身后的更小元素 + res[i] = s.isEmpty() ? -1 : s.peek(); + s.push(nums[i]); + } + return res; + } + + /** + * 下一个更小或相等的元素:计算 nums 中每个元素的下一个更小或相等的元素 + */ + int[] nextLessOrEqualElement(int[] nums) { + int n = nums.length; + // 存放答案的数组 + int[] res = new int[n]; + Stack s = new Stack<>(); + // 倒着往栈里放 + for (int i = n - 1; i >= 0; i--) { + // 删掉 nums[i] 后面较大的元素 + while (!s.isEmpty() && s.peek() > nums[i]) { + s.pop(); + } + // 现在栈顶就是 nums[i] 身后的更小或相等元素 + res[i] = s.isEmpty() ? -1 : s.peek(); + s.push(nums[i]); + } + return res; + } + + /** + * 上一个更大元素:计算 nums 中每个元素的上一个更大元素 + */ + int[] prevGreaterElement(int[] nums) { + int n = nums.length; + int[] res = new int[n]; + Stack s = new Stack<>(); + // 因为是求 nums[i] 前面的元素,所以正着往栈里放 + for (int i = 0; i < n; i++) { + // 删掉 nums[i] 前面较小的元素 + while (!s.isEmpty() && s.peek() <= nums[i]) { + s.pop(); + } + // 现在栈顶就是 nums[i] 前面的更大元素 + res[i] = s.isEmpty() ? -1 : s.peek(); + s.push(nums[i]); + } + return res; + } + + /** + * 上一个更大或相等的元素:计算 nums 中每个元素的上一个更大或相等元素 + */ + int[] prevGreaterOrEqualElement(int[] nums) { + int n = nums.length; + int[] res = new int[n]; + Stack s = new Stack<>(); + for (int i = 0; i < n; i++) { + // 注意不等号 + while (!s.isEmpty() && s.peek() < nums[i]) { + s.pop(); + } + // 现在栈顶就是 nums[i] 前面的更大或相等元素 + res[i] = s.isEmpty() ? -1 : s.peek(); + s.push(nums[i]); + } + return res; + } + + /** + * 上一个更小的元素:计算 nums 中每个元素的上一个更小的元素 + */ + int[] prevLessElement(int[] nums) { + int n = nums.length; + int[] res = new int[n]; + Stack s = new Stack<>(); + for (int i = 0; i < n; i++) { + // 把 nums[i] 之前的较大元素删除 + while (!s.isEmpty() && s.peek() >= nums[i]) { + s.pop(); + } + // 现在栈顶就是 nums[i] 前面的更小元素 + res[i] = s.isEmpty() ? -1 : s.peek(); + s.push(nums[i]); + } + return res; + } + + /** + * 上一个更小或相等的元素:计算 nums 中每个元素的上一个更小或相等元素 + */ + int[] prevLessOrEqualElement(int[] nums) { + int n = nums.length; + int[] res = new int[n]; + Stack s = new Stack<>(); + for (int i = 0; i < n; i++) { + // 注意不等号 + while (!s.isEmpty() && s.peek() > nums[i]) { + s.pop(); + } + // 现在栈顶就是 nums[i] 前面的更小或相等元素 + res[i] = s.isEmpty() ? -1 : s.peek(); + s.push(nums[i]); + } + return res; + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\344\270\211\345\220\210\344\270\200.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\344\270\211\345\220\210\344\270\200.java" deleted file mode 100644 index b38b134..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\344\270\211\345\220\210\344\270\200.java" +++ /dev/null @@ -1,56 +0,0 @@ -package io.github.dunwu.algorithm.stack; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; - -/** - * @author Zhang Peng - * @since 2020-06-09 - */ -public class 三合一 { - - int stackSize; - List> stacks; - - public 三合一(int stackSize) { - this.stackSize = stackSize; - this.stacks = new ArrayList<>(); - for (int i = 0; i < 3; i++) { - LinkedList list = new LinkedList<>(); - this.stacks.add(list); - } - } - - public void push(int stackNum, int value) { - LinkedList list = stacks.get(stackNum); - if (list.size() < stackSize) { - list.addLast(value); - } - } - - public int pop(int stackNum) { - LinkedList list = stacks.get(stackNum); - int value = -1; - if (list.size() > 0) { - value = list.getLast(); - list.removeLast(); - } - return value; - } - - public int peek(int stackNum) { - LinkedList list = stacks.get(stackNum); - int value = -1; - if (list.size() > 0) { - value = list.getLast(); - } - return value; - } - - public boolean isEmpty(int stackNum) { - LinkedList list = stacks.get(stackNum); - return list.size() <= 0; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\345\237\272\346\234\254\350\256\241\347\256\227\345\231\250.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\345\237\272\346\234\254\350\256\241\347\256\227\345\231\250.java" deleted file mode 100644 index 1e41699..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\345\237\272\346\234\254\350\256\241\347\256\227\345\231\250.java" +++ /dev/null @@ -1,65 +0,0 @@ -package io.github.dunwu.algorithm.stack; - -import org.junit.jupiter.api.Assertions; - -/** - * @author Zhang Peng - * @see 224. 基本计算器 - * @since 2020-06-09 - */ -public class 基本计算器 { - - public static void main(String[] args) { - Assertions.assertEquals(23, calculate("(1+(4+5+2)-3)+(6+8)")); - Assertions.assertEquals(3, calculate("2-(5-6)")); - Assertions.assertEquals(12, calculate("1+(4+5+2)")); - Assertions.assertEquals(2147483647, calculate("2147483647")); - Assertions.assertEquals(2, calculate("1 + 1")); - Assertions.assertEquals(3, calculate("2 - 1 + 2")); - } - - public static int calculate(String s) { - int sign = 1; - int current = 0; - int result = 0; - GenericStack stack = new GenericStack<>(); - for (int i = 0; i < s.length(); i++) { - char c = s.charAt(i); - if (Character.isDigit(c)) { - current = current * 10 + (c - '0'); - } else if (c == '+') { - // 累加上一个操作数并重置 - result = result + sign * current; - current = 0; - // 设置下一个操作数的正负号 - sign = 1; - } else if (c == '-') { - // 累加上一个操作数并重置 - result = result + sign * current; - current = 0; - // 设置下一个操作数的正负号 - sign = -1; - } else if (c == '(') { - stack.push(result); - stack.push(sign); - sign = 1; - result = 0; - } else if (c == ')') { - // 累加上一个操作数并重置 - result = result + sign * current; - current = 0; - // 依次取出暂存栈中的正负号和操作数 - sign = stack.pop(); - int temp = stack.pop(); - // 累加 - result = temp + sign * result; - } - } - - if (current != 0) { - result = result + sign * current; - } - return result; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\346\226\207\344\273\266\347\232\204\346\234\200\351\225\277\347\273\235\345\257\271\350\267\257\345\276\204.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\346\226\207\344\273\266\347\232\204\346\234\200\351\225\277\347\273\235\345\257\271\350\267\257\345\276\204.java" new file mode 100644 index 0000000..e0bde55 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\346\226\207\344\273\266\347\232\204\346\234\200\351\225\277\347\273\235\345\257\271\350\267\257\345\276\204.java" @@ -0,0 +1,51 @@ +package io.github.dunwu.algorithm.stack; + +import org.junit.jupiter.api.Assertions; + +import java.util.Deque; +import java.util.LinkedList; + +/** + * 150. 逆波兰表达式求值 + * + * @author Zhang Peng + * @date 2025-08-11 + */ +public class 文件的最长绝对路径 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(20, s.lengthLongestPath("dir\\n\\tsubdir1\\n\\tsubdir2\\n\\t\\tfile.ext")); + Assertions.assertEquals(32, s.lengthLongestPath( + "dir\\n\\tsubdir1\\n\\t\\tfile1.ext\\n\\t\\tsubsubdir1\\n\\tsubdir2\\n\\t\\tsubsubdir2\\n\\t\\t\\tfile2.ext")); + Assertions.assertEquals(0, s.lengthLongestPath("a")); + Assertions.assertEquals(12, s.lengthLongestPath("file1.txt\\nfile2.txt\\nlongfile.txt")); + } + + static class Solution { + + public int lengthLongestPath(String input) { + // 这个栈存储之前的父路径。实际上这里只用存父路径的长度就够了,这个优化留给你吧 + Deque stack = new LinkedList<>(); + int maxLen = 0; + for (String part : input.split("\n")) { + int level = part.lastIndexOf("\t") + 1; + // 让栈中只保留当前目录的父路径 + while (level < stack.size()) { + stack.removeLast(); + } + stack.addLast(part.substring(level)); + // 如果是文件,就计算路径长度 + if (part.contains(".")) { + int sum = stack.stream().mapToInt(String::length).sum(); + // 加上父路径的分隔符 + sum += stack.size() - 1; + maxLen = Math.max(maxLen, sum); + } + } + return maxLen; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\346\234\200\345\244\247\351\242\221\347\216\207\346\240\210.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\346\234\200\345\244\247\351\242\221\347\216\207\346\240\210.java" new file mode 100644 index 0000000..9f6ec25 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\346\234\200\345\244\247\351\242\221\347\216\207\346\240\210.java" @@ -0,0 +1,59 @@ +package io.github.dunwu.algorithm.stack; + +import org.junit.jupiter.api.Assertions; + +import java.util.HashMap; +import java.util.Stack; +import java.util.TreeMap; + +/** + * 895. 最大频率栈 + * + * @author Zhang Peng + * @date 2025-08-11 + */ +public class 最大频率栈 { + + public static void main(String[] args) { + FreqStack s1 = new FreqStack(); + s1.push(5);//堆栈为 [5] + s1.push(7);//堆栈是 [5,7] + s1.push(5);//堆栈是 [5,7,5] + s1.push(7);//堆栈是 [5,7,5,7] + s1.push(4);//堆栈是 [5,7,5,7,4] + s1.push(5);//堆栈是 [5,7,5,7,4,5] + Assertions.assertEquals(5, s1.pop()); //返回 5 ,因为 5 出现频率最高。堆栈变成 [5,7,5,7,4] + Assertions.assertEquals(7, s1.pop()); //返回 7 ,因为 5 和 7 出现频率最高,但7最接近顶部。堆栈变成 [5,7,5,4]。 + Assertions.assertEquals(5, s1.pop()); //返回 5 ,因为 5 出现频率最高。堆栈变成 [5,7,4]。 + Assertions.assertEquals(4, s1.pop()); //返回 4 ,因为 4, 5 和 7 出现频率最高,但 4 是最接近顶部的。堆栈变成 [5,7]。 + } + + static class FreqStack { + + private HashMap valToFreq; + private TreeMap> freqToValStack; + + public FreqStack() { + valToFreq = new HashMap<>(); + freqToValStack = new TreeMap<>(); + } + + public void push(int val) { + valToFreq.put(val, valToFreq.getOrDefault(val, 0) + 1); + Integer freq = this.valToFreq.get(val); + freqToValStack.putIfAbsent(freq, new Stack<>()); + freqToValStack.get(freq).push(val); + } + + public int pop() { + Integer maxFreq = freqToValStack.lastKey(); + Stack stack = freqToValStack.get(maxFreq); + Integer val = stack.pop(); + if (stack.empty()) { freqToValStack.remove(maxFreq); } + valToFreq.put(val, valToFreq.getOrDefault(val, 0) - 1); + return val; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\346\234\200\345\260\217\346\240\210.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\346\234\200\345\260\217\346\240\210.java" index 9a4f5ac..6775a69 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\346\234\200\345\260\217\346\240\210.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\346\234\200\345\260\217\346\240\210.java" @@ -1,63 +1,60 @@ package io.github.dunwu.algorithm.stack; -import java.util.LinkedList; +import org.junit.jupiter.api.Assertions; + +import java.util.Stack; /** - * @see 面试题 03.02. 栈的最小值 + * 面试题 03.02. 栈的最小值 + * + * @author Zhang Peng + * @date 2025-10-20 */ public class 最小栈 { public static void main(String[] args) { - 最小栈 stack = new 最小栈(); - stack.push(9); - stack.push(2); - stack.push(5); - stack.push(6); - stack.push(3); - stack.push(1); - System.out.println("min = " + stack.getMin()); - System.out.println("pop " + stack.pop()); - System.out.println("pop " + stack.pop()); - System.out.println("pop " + stack.pop()); + MinStack minStack = new MinStack(); + minStack.push(-2); + minStack.push(0); + minStack.push(-3); + Assertions.assertEquals(-3, minStack.getMin()); + minStack.pop(); + Assertions.assertEquals(0, minStack.top()); + Assertions.assertEquals(-2, minStack.getMin()); } - private final LinkedList stack; - private final LinkedList minStack; + static class MinStack { - public 最小栈() { - stack = new LinkedList<>(); - minStack = new LinkedList<>(); - } + Stack stack; + Stack minStack; - public void push(int x) { - if (!minStack.isEmpty()) { - Integer first = minStack.getFirst(); - if (x < first) { - minStack.push(x); + public MinStack() { + stack = new Stack<>(); + minStack = new Stack<>(); + } + + public void push(int val) { + stack.push(val); + if (minStack.isEmpty() || val < minStack.peek()) { + minStack.push(val); + } else { + minStack.push(minStack.peek());; } - stack.push(x); } - } - public int pop() { - int top = stack.pop(); - int val = minStack.peek() ; - if (val == val) { + public void pop() { + stack.pop(); minStack.pop(); } - return val; - } - public int top() { - return stack.getFirst(); - } + public int top() { + return stack.peek(); + } - public int getMin() { - if (minStack.isEmpty()) { - return -1; - } else { - return minStack.getFirst(); + public int getMin() { + return minStack.peek(); } + } } diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\346\234\200\345\260\217\346\240\2102.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\346\234\200\345\260\217\346\240\2102.java" deleted file mode 100644 index f96f802..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\346\234\200\345\260\217\346\240\2102.java" +++ /dev/null @@ -1,58 +0,0 @@ -package io.github.dunwu.algorithm.stack; - -import java.util.LinkedList; - -/** - * @author Zhang Peng - * @since 2020-01-18 - */ -public class 最小栈2 { - - // 数据栈 - private LinkedList data; - - // 辅助栈 - private LinkedList helper; - - /** - * initialize your data structure here. - */ - public 最小栈2() { - data = new LinkedList<>(); - helper = new LinkedList<>(); - } - - // 思路 1:数据栈和辅助栈在任何时候都同步 - public void push(int x) { - // 数据栈和辅助栈一定会增加元素 - data.push(x); - if (helper.isEmpty() || helper.peek() >= x) { - helper.push(x); - } else { - helper.push(helper.peek()); - } - } - - public void pop() { - // 两个栈都得 pop - if (!data.isEmpty()) { - helper.pop(); - data.pop(); - } - } - - public int top() { - if (!data.isEmpty()) { - return data.peek(); - } - throw new RuntimeException("栈中元素为空,此操作非法"); - } - - public int getMin() { - if (!helper.isEmpty()) { - return helper.peek(); - } - throw new RuntimeException("栈中元素为空,此操作非法"); - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\346\234\211\346\225\210\347\232\204\346\213\254\345\217\267.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\346\234\211\346\225\210\347\232\204\346\213\254\345\217\267.java" index b4fb51a..1601e5f 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\346\234\211\346\225\210\347\232\204\346\213\254\345\217\267.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\346\234\211\346\225\210\347\232\204\346\213\254\345\217\267.java" @@ -2,52 +2,52 @@ import org.junit.jupiter.api.Assertions; +import java.util.Stack; + /** - * @see 20. 有效的括号 + * 20. 有效的括号 + * * @author Zhang Peng * @since 2020-06-09 */ public class 有效的括号 { public static void main(String[] args) { - Assertions.assertTrue(isValid("()")); - Assertions.assertTrue(isValid("{[]}")); - Assertions.assertFalse(isValid("([)]")); - Assertions.assertFalse(isValid("([)")); + Solution s = new Solution(); + Assertions.assertTrue(s.isValid("()")); + Assertions.assertTrue(s.isValid("{[]}")); + Assertions.assertFalse(s.isValid("([)]")); + Assertions.assertFalse(s.isValid("([)")); + Assertions.assertFalse(s.isValid("((")); + Assertions.assertTrue(s.isValid("(())")); } - public static boolean isValid(String s) { - if (s == null) { - return true; - } - - int length = s.length(); - if (length == 0) return true; - if (length % 2 != 0) return false; - - GenericStack stack = new GenericStack<>(); - for (char c : s.toCharArray()) { - Character top = stack.peek(); - if (top == null) { - stack.push(c); - continue; - } - - if (top == '(' && c == ')') { - stack.pop(); - } else if (top == '[' && c == ']') { - stack.pop(); - } else if (top == '{' && c == '}') { - stack.pop(); - } else { - stack.push(c); + static class Solution { + + public boolean isValid(String s) { + Stack stack = new Stack<>(); + for (char c : s.toCharArray()) { + switch (c) { + case ')': + if (stack.isEmpty()) { return false; } + if (stack.pop() != '(') { return false; } + break; + case ']': + if (stack.isEmpty()) { return false; } + if (stack.pop() != '[') { return false; } + break; + case '}': + if (stack.isEmpty()) { return false; } + if (stack.pop() != '{') { return false; } + break; + default: + stack.push(c); + break; + } } + return stack.isEmpty(); } - if (stack.getSize() == 0) { - return true; - } - return false; } } diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\346\240\210\346\216\222\345\272\217.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\346\240\210\346\216\222\345\272\217.java" index 8e9e1ef..40e5a99 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\346\240\210\346\216\222\345\272\217.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\346\240\210\346\216\222\345\272\217.java" @@ -1,73 +1,71 @@ package io.github.dunwu.algorithm.stack; -import java.util.LinkedList; +import org.junit.jupiter.api.Assertions; + +import java.util.Stack; /** - * @see 面试题 03.05. 栈排序 + * 面试题 03.05. 栈排序 + * + * @author Zhang Peng + * @date 2025-11-26 */ public class 栈排序 { public static void main(String[] args) { - 栈排序 demo = new 栈排序(); - demo.push(1); - System.out.println(demo.stack1); - demo.push(2); - System.out.println(demo.stack1); - } - - public LinkedList stack1; - public LinkedList stack2; + SortedStack s = new SortedStack(); + s.push(1); + s.push(2); + Assertions.assertEquals(1, s.peek()); + s.pop(); + Assertions.assertEquals(2, s.peek()); - public 栈排序() { - stack1 = new LinkedList<>(); - stack2 = new LinkedList<>(); + SortedStack s2 = new SortedStack(); + s2.pop(); + s2.pop(); + s2.push(1); + s2.pop(); + Assertions.assertTrue(s2.isEmpty()); } - public void push(int val) { - if (isEmpty()) { - stack1.push(val); - return; - } + static class SortedStack { - if (!stack1.isEmpty()) { - move(val); - } + private Stack s; + private Stack t; - stack1.push(val); - while (!stack2.isEmpty()) { - Integer top = stack2.pop(); - stack1.push(top); + public SortedStack() { + s = new Stack<>(); + t = new Stack<>(); } - } - private void move(int val) { - if (stack1.isEmpty()) { - return; + public void push(int val) { + if (s.isEmpty()) { + s.push(val); + return; + } + while (!s.isEmpty() && s.peek() < val) { + t.push(s.pop()); + } + s.push(val); + while (!t.isEmpty()) { + s.push(t.pop()); + } } - int top = peek(); - if (top < val) { - stack2.push(stack1.pop()); - move(val); + public void pop() { + if (!s.isEmpty()) { + s.pop(); + } } - } - public int pop() { - if (stack1.isEmpty()) { - return -1; + public int peek() { + return s.isEmpty() ? -1 : s.peek(); } - return stack1.pop(); - } - public int peek() { - if (stack1.isEmpty()) { - return -1; + public boolean isEmpty() { + return s.isEmpty(); } - return stack1.peek(); - } - public boolean isEmpty() { - return stack1.isEmpty() && stack2.isEmpty(); } } diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\346\243\222\347\220\203\346\257\224\350\265\233.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\346\243\222\347\220\203\346\257\224\350\265\233.java" index 57d7263..3ae17cf 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\346\243\222\347\220\203\346\257\224\350\265\233.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\346\243\222\347\220\203\346\257\224\350\265\233.java" @@ -1,42 +1,56 @@ package io.github.dunwu.algorithm.stack; +import org.junit.jupiter.api.Assertions; + +import java.util.Stack; + /** + * 682. 棒球比赛 + * * @author Zhang Peng - * @see 682. 棒球比赛 * @since 2020-06-09 */ public class 棒球比赛 { public static void main(String[] args) { - System.out.println(calPoints("5", "2", "C", "D", "+")); - System.out.println(calPoints("5", "-2", "4", "C", "D", "9", "+", "+")); + Solution s = new Solution(); + Assertions.assertEquals(30, s.calPoints(new String[] { "5", "2", "C", "D", "+" })); + Assertions.assertEquals(27, s.calPoints(new String[] { "5", "-2", "4", "C", "D", "9", "+", "+" })); } - public static int calPoints(String... ops) { - int total = 0; - GenericStack stack = new GenericStack<>(); - for (String s : ops) { - if (s.equals("+")) { - int num1 = stack.pop(); - int num2 = stack.pop(); - int num = num1 + num2; - stack.push(num2); - stack.push(num1); - stack.push(num); - } else if (s.equals("D")) { - stack.push(stack.peek() * 2); - } else if (s.equals("C")) { - stack.pop(); - } else { - stack.push(Integer.valueOf(s)); + static class Solution { + + public int calPoints(String[] operations) { + Stack stack = new Stack<>(); + for (String op : operations) { + switch (op) { + case "C": + stack.pop(); + break; + case "D": + stack.push(stack.peek() * 2); + break; + case "+": + int cur = stack.pop(); + int prev = stack.pop(); + int next = prev + cur; + stack.push(prev); + stack.push(cur); + stack.push(next); + break; + default: + stack.push(Integer.valueOf(op)); + break; + } } - } - while (stack.getSize() != 0) { - total += stack.pop(); + int res = 0; + while (!stack.isEmpty()) { + res += stack.pop(); + } + return res; } - return total; } } diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\346\257\224\350\276\203\345\220\253\351\200\200\346\240\274\347\232\204\345\255\227\347\254\246\344\270\262.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\346\257\224\350\276\203\345\220\253\351\200\200\346\240\274\347\232\204\345\255\227\347\254\246\344\270\262.java" index 6315118..5fee00b 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\346\257\224\350\276\203\345\220\253\351\200\200\346\240\274\347\232\204\345\255\227\347\254\246\344\270\262.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\346\257\224\350\276\203\345\220\253\351\200\200\346\240\274\347\232\204\345\255\227\347\254\246\344\270\262.java" @@ -2,40 +2,51 @@ import org.junit.jupiter.api.Assertions; +import java.util.Stack; + /** + * 844. 比较含退格的字符串 + * * @author Zhang Peng - * @see 844. 比较含退格的字符串 * @since 2020-06-09 */ public class 比较含退格的字符串 { public static void main(String[] args) { - Assertions.assertTrue(backspaceCompare("ab#c", "ad#c")); - Assertions.assertTrue(backspaceCompare("ab##", "c#d#")); - Assertions.assertTrue(backspaceCompare("a##c", "#a#c")); - Assertions.assertFalse(backspaceCompare("a#c", "b")); - } - - public static boolean backspaceCompare(String S, String T) { - return getFinalStr(S).equals(getFinalStr(T)); + Solution s = new Solution(); + Assertions.assertTrue(s.backspaceCompare("ab#c", "ad#c")); + Assertions.assertTrue(s.backspaceCompare("ab##", "c#d#")); + Assertions.assertTrue(s.backspaceCompare("a##c", "#a#c")); + Assertions.assertFalse(s.backspaceCompare("a#c", "b")); } - public static String getFinalStr(String S) { - GenericStack stack = new GenericStack<>(); - for (char c : S.toCharArray()) { - if (c == '#') { - stack.pop(); - } else { - stack.push(c); + static class Solution { + + public boolean backspaceCompare(String s, String t) { + Stack a = new Stack<>(); + Stack b = new Stack<>(); + for (char c : s.toCharArray()) { + if (c == '#') { + if (!a.isEmpty()) { a.pop(); } + } else { + a.push(c); + } + } + for (char c : t.toCharArray()) { + if (c == '#') { + if (!b.isEmpty()) { b.pop(); } + } else { + b.push(c); + } } - } - StringBuilder sb = new StringBuilder(); - while (stack.getSize() > 0) { - sb.append(stack.pop()); + if (a.size() != b.size()) { return false; } + while (!a.isEmpty()) { + if (a.pop() != b.pop()) { return false; } + } + return true; } - return sb.reverse().toString(); } } diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/SampleBrowser.java "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\347\224\250\346\240\210\345\256\236\347\216\260\346\265\217\350\247\210\345\231\250\347\232\204\345\211\215\350\277\233\345\220\216\351\200\200.java" similarity index 96% rename from codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/SampleBrowser.java rename to "codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\347\224\250\346\240\210\345\256\236\347\216\260\346\265\217\350\247\210\345\231\250\347\232\204\345\211\215\350\277\233\345\220\216\351\200\200.java" index ac44ef7..c12f789 100644 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/SampleBrowser.java +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\347\224\250\346\240\210\345\256\236\347\216\260\346\265\217\350\247\210\345\231\250\347\232\204\345\211\215\350\277\233\345\220\216\351\200\200.java" @@ -5,10 +5,10 @@ * * @author chinalwb */ -public class SampleBrowser { +public class 用栈实现浏览器的前进后退 { public static void main(String[] args) { - SampleBrowser browser = new SampleBrowser(); + 用栈实现浏览器的前进后退 browser = new 用栈实现浏览器的前进后退(); browser.open("http://www.baidu.com"); browser.open("http://news.baidu.com/"); browser.open("http://news.baidu.com/ent"); @@ -30,7 +30,7 @@ public static void main(String[] args) { private LinkedListBasedStack backStack; private LinkedListBasedStack forwardStack; - public SampleBrowser() { + public 用栈实现浏览器的前进后退() { this.backStack = new LinkedListBasedStack(); this.forwardStack = new LinkedListBasedStack(); } diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\347\224\250\346\240\210\345\256\236\347\216\260\351\230\237\345\210\227.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\347\224\250\346\240\210\345\256\236\347\216\260\351\230\237\345\210\227.java" index c340e5b..3051ecf 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\347\224\250\346\240\210\345\256\236\347\216\260\351\230\237\345\210\227.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\347\224\250\346\240\210\345\256\236\347\216\260\351\230\237\345\210\227.java" @@ -1,55 +1,81 @@ package io.github.dunwu.algorithm.stack; -import java.util.LinkedList; +import org.junit.jupiter.api.Assertions; + +import java.util.Stack; /** - * @see 232. 用栈实现队列 + * 232. 用栈实现队列 + * + * @author Zhang Peng + * @since 2020-01-18 */ public class 用栈实现队列 { public static void main(String[] args) { - 用栈实现队列 queue = new 用栈实现队列(); - queue.push(1); - queue.push(2); - System.out.println(queue.peek()); // 返回 1 - System.out.println(queue.pop()); // 返回 1 - System.out.println(queue.empty()); // 返回 false - } - private LinkedList stack1; - private LinkedList stack2; + MyQueue q1 = new MyQueue(); + q1.push(1); // queue is: [1] + q1.push(2); // queue is: [1, 2] (leftmost is front of the queue) + Assertions.assertEquals(1, q1.peek()); + Assertions.assertEquals(1, q1.pop()); + Assertions.assertFalse(q1.empty()); + Assertions.assertEquals(2, q1.pop()); + Assertions.assertTrue(q1.empty()); - /** Initialize your data structure here. */ - public 用栈实现队列() { - stack1 = new LinkedList<>(); - stack2 = new LinkedList<>(); - } + MyQueue q2 = new MyQueue(); + q2.push(1); + q2.push(2); + Assertions.assertEquals(1, q2.pop()); + q2.push(3); + q2.push(4); + Assertions.assertEquals(2, q2.pop()); + Assertions.assertEquals(3, q2.peek()); - /** Push element x to the back of queue. */ - public void push(int x) { - stack1.push(x); + MyQueue q3 = new MyQueue(); + int max = 10; + for (int i = 1; i <= max; i++) { + q3.push(i); + } + for (int i = 1; i <= max; i++) { + Assertions.assertEquals(i, q3.peek()); + Assertions.assertEquals(i, q3.pop()); + } } - /** Removes the element from in front of queue and returns that element. */ - public int pop() { - peek(); - return stack2.pop(); - } + static class MyQueue { - /** Get the front element. */ - public int peek() { - if (stack2.size() > 0) { - return stack2.peek(); + private Stack s1; + private Stack s2; + + public MyQueue() { + s1 = new Stack<>(); + s2 = new Stack<>(); } - while (!stack1.isEmpty()) { - stack2.push(stack1.pop()); + + public void push(int x) { + s1.push(x); + } + + public int pop() { + peek(); + Integer top = s2.pop(); + return top == null ? 0 : top; + } + + public int peek() { + if (s2.isEmpty()) { + while (!s1.isEmpty()) { + s2.push(s1.pop()); + } + } + return s2.isEmpty() ? 0 : s2.peek(); + } + + public boolean empty() { + return s1.isEmpty() && s2.isEmpty(); } - return stack2.peek(); - } - /** Returns whether the queue is empty. */ - public boolean empty() { - return stack1.isEmpty() && stack2.isEmpty(); } } diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\347\224\250\351\230\237\345\210\227\345\256\236\347\216\260\346\240\210.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\347\224\250\351\230\237\345\210\227\345\256\236\347\216\260\346\240\210.java" deleted file mode 100644 index c350aec..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\347\224\250\351\230\237\345\210\227\345\256\236\347\216\260\346\240\210.java" +++ /dev/null @@ -1,63 +0,0 @@ -package io.github.dunwu.algorithm.stack; - -import java.util.LinkedList; -import java.util.Queue; - -/** - * 基于队列实现的栈 - * - * @author Zhang Peng - * @see 225. 用队列实现栈 - * @since 2020-01-18 - */ -public class 用队列实现栈 { - - public static void main(String[] args) { - 用队列实现栈 stack = new 用队列实现栈<>(); - stack.push(1); - stack.push(2); - System.out.println(stack.pop()); - System.out.println(stack.pop()); - } - - private Queue q1 = new LinkedList<>(); - - /** - * Initialize your data structure here. - */ - public 用队列实现栈() { } - - /** - * Push element x onto stack. - */ - public void push(T x) { - q1.add(x); - int sz = q1.size(); - while (sz > 1) { - q1.add(q1.remove()); - sz--; - } - } - - /** - * Removes the element on top of the stack and returns that element. - */ - public T pop() { - return q1.poll(); - } - - /** - * Get the top element. - */ - public T top() { - return q1.peek(); - } - - /** - * Returns whether the stack is empty. - */ - public boolean empty() { - return q1.isEmpty(); - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\347\256\200\345\214\226\350\267\257\345\276\204.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\347\256\200\345\214\226\350\267\257\345\276\204.java" new file mode 100644 index 0000000..aa7357b --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\347\256\200\345\214\226\350\267\257\345\276\204.java" @@ -0,0 +1,50 @@ +package io.github.dunwu.algorithm.stack; + +import org.junit.jupiter.api.Assertions; + +import java.util.Stack; + +/** + * 71. 简化路径 + * + * @author Zhang Peng + * @since 2025-08-08 + */ +public class 简化路径 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals("/home", s.simplifyPath("/home/")); + Assertions.assertEquals("/home/foo", s.simplifyPath("/home//foo/")); + Assertions.assertEquals("/home/user/Pictures", s.simplifyPath("/home/user/Documents/../Pictures")); + Assertions.assertEquals("/", s.simplifyPath("/../")); + Assertions.assertEquals("/.../b/d", s.simplifyPath("/.../a/../b/c/../d/./")); + } + + static class Solution { + + public String simplifyPath(String path) { + String[] parts = path.split("/"); + Stack stk = new Stack<>(); + // 借助栈计算最终的文件夹路径 + for (String part : parts) { + if (part.isEmpty() || part.equals(".")) { + continue; + } + if (part.equals("..")) { + if (!stk.isEmpty()) stk.pop(); + continue; + } + stk.push(part); + } + // 栈中存储的文件夹组成路径 + String res = ""; + while (!stk.isEmpty()) { + res = "/" + stk.pop() + res; + } + return res.isEmpty() ? "/" : res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\351\200\206\346\263\242\345\205\260\350\241\250\350\276\276\345\274\217\346\261\202\345\200\274.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\351\200\206\346\263\242\345\205\260\350\241\250\350\276\276\345\274\217\346\261\202\345\200\274.java" new file mode 100644 index 0000000..e83ae8f --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\351\200\206\346\263\242\345\205\260\350\241\250\350\276\276\345\274\217\346\261\202\345\200\274.java" @@ -0,0 +1,53 @@ +package io.github.dunwu.algorithm.stack; + +import org.junit.jupiter.api.Assertions; + +import java.util.Stack; + +/** + * 150. 逆波兰表达式求值 + * + * @author Zhang Peng + * @date 2025-08-11 + */ +public class 逆波兰表达式求值 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(9, s.evalRPN(new String[] { "2", "1", "+", "3", "*" })); + Assertions.assertEquals(6, s.evalRPN(new String[] { "4", "13", "5", "/", "+" })); + Assertions.assertEquals(22, + s.evalRPN(new String[] { "10", "6", "9", "3", "+", "-11", "*", "/", "*", "17", "+", "5", "+" })); + } + + static class Solution { + + public int evalRPN(String[] tokens) { + Stack stack = new Stack<>(); + for (String token : tokens) { + if ("+".equals(token)) { + Integer numB = stack.pop(); + Integer numA = stack.pop(); + stack.push(numA + numB); + } else if ("-".equals(token)) { + Integer numB = stack.pop(); + Integer numA = stack.pop(); + stack.push(numA - numB); + } else if ("*".equals(token)) { + Integer numB = stack.pop(); + Integer numA = stack.pop(); + stack.push(numA * numB); + } else if ("/".equals(token)) { + Integer numB = stack.pop(); + Integer numA = stack.pop(); + stack.push(numA / numB); + } else { + stack.push(Integer.parseInt(token)); + } + } + return stack.pop(); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\351\207\215\346\216\222\351\223\276\350\241\250.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\351\207\215\346\216\222\351\223\276\350\241\250.java" new file mode 100644 index 0000000..48cdd0e --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/stack/\351\207\215\346\216\222\351\223\276\350\241\250.java" @@ -0,0 +1,60 @@ +package io.github.dunwu.algorithm.stack; + +import io.github.dunwu.algorithm.linkedlist.ListNode; +import org.junit.jupiter.api.Assertions; + +import java.util.List; +import java.util.Stack; + +/** + * 143. 重排链表 + * + * @author Zhang Peng + * @date 2025-08-11 + */ +public class 重排链表 { + + public static void main(String[] args) { + + Solution s = new Solution(); + ListNode input = ListNode.buildList(1, 2, 3, 4); + s.reorderList(input); + List list = ListNode.toList(input); + Assertions.assertArrayEquals(new Integer[] { 1, 4, 2, 3 }, list.toArray()); + + ListNode input2 = ListNode.buildList(1, 2, 3, 4, 5); + s.reorderList(input2); + List list2 = ListNode.toList(input2); + Assertions.assertArrayEquals(new Integer[] { 1, 5, 2, 4, 3 }, list2.toArray()); + } + + static class Solution { + + public void reorderList(ListNode head) { + Stack stack = new Stack<>(); + // 先把所有节点装进栈里,得到倒序结果 + ListNode p = head; + while (p != null) { + stack.push(p); + p = p.next; + } + + p = head; + while (p != null) { + // 链表尾部的节点 + ListNode last = stack.pop(); + ListNode next = p.next; + if (last == next || last.next == next) { + // 结束条件,链表节点数为奇数或偶数时均适用 + last.next = null; + break; + } + p.next = last; + last.next = next; + p = next; + } + } + + } + +} diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/str/StringAlgorithm.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/str/StringAlgorithm.java index ade2285..b0a3d12 100644 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/str/StringAlgorithm.java +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/str/StringAlgorithm.java @@ -1,226 +1,293 @@ package io.github.dunwu.algorithm.str; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; -import java.util.HashSet; +import java.util.List; import java.util.Map; -import java.util.Set; /** * @author Zhang Peng - * @since 2020-05-12 + * @since 2020-01-18 */ public class StringAlgorithm { /** - * @see 01. 判定字符是否唯一 + * 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。 + *

+ * 示例 1: + *

+ * 输入: "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。 示例 2: + *

+ * 输入: "bbbbb" 输出: 1 解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。 示例 3: + *

+ * 输入: "pwwkew" 输出: 3 解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。 + *

+ * 请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。 + * + * @see 无重复字符的最长子串 */ - public static boolean isUnique(String str) { - if (str == null || str.length() <= 1) { return true; } - Set set = new HashSet<>(); - for (char c : str.toCharArray()) { - if (set.contains(c)) { - return false; + public static int lengthOfLongestSubstring(String s) { + if (null == s || s.length() == 0) { + return 0; + } + + int max = 0; + int left = 0; + Map map = new HashMap<>(); + for (int i = 0; i < s.length(); i++) { + if (map.containsKey(s.charAt(i))) { + left = Math.max(left, map.get(s.charAt(i)) + 1); } - set.add(c); + map.put(s.charAt(i), i); + max = Math.max(max, i - left + 1); } - return true; + return max; } /** - * @see 02. 判定是否互为字符重排 + * 编写一个函数来查找字符串数组中的最长公共前缀。 + *

+ * 如果不存在公共前缀,返回空字符串 ""。 + *

+ * 示例 1: + *

+ * 输入: ["flower","flow","flight"] 输出: "fl" 示例 2: + *

+ * 输入: ["dog","racecar","car"] 输出: "" 解释: 输入不存在公共前缀。 说明: + *

+ * 所有输入只包含小写字母 a-z 。 + * + * @see 最长公共前缀 */ - public static boolean checkPermutation(String s1, String s2) { - if (!(s1 != null && s2 != null)) { - return false; + public static String longestCommonPrefix(String[] array) { + if (array == null || array.length == 0) { + return ""; + } else if (array.length == 1) { + return array[0]; + } else { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < array[0].length(); i++) { + char c = array[0].charAt(i); + boolean end = false; + for (int index = 1; index < array.length; index++) { + if (array[index].length() - 1 < i) { + end = true; + break; + } + + if (array[index].charAt(i) != c) { + end = true; + break; + } + } + if (end) { + break; + } else { + sb.append(c); + } + } + return sb.toString(); } + } - if (s1.length() != s2.length()) { + /** + * 给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的排列。 + *

+ * 换句话说,第一个字符串的排列之一是第二个字符串的子串。 + *

+ * 示例1: 输入: s1 = "ab" s2 = "eidbaooo" 输出: True 解释: s2 包含 s1 的排列之一 ("ba"). + *

+ * 示例2: 输入: s1= "ab" s2 = "eidboaoo" 输出: False + *

+ * 注意:输入的字符串只包含小写字母,两个字符串的长度都在 [1, 10,000] 之间 + * + * @see 字符串的排列 + */ + public static boolean checkInclusion(String s1, String s2) { + if (s1 == null || s1.length() == 0 || s2 == null || s2.length() == 0) { return false; } - Map countMap1 = new HashMap<>(); - Map countMap2 = new HashMap<>(); - for (char c : s1.toCharArray()) { - if (countMap1.containsKey(c)) { - Integer cnt = countMap1.get(c); - cnt++; - } else { - countMap1.put(c, 1); - } - } - for (char c : s2.toCharArray()) { - if (countMap2.containsKey(c)) { - Integer cnt = countMap2.get(c); - cnt++; - } else { - countMap2.put(c, 1); - } - } + int len1 = s1.length(); + int len2 = s2.length(); - Set keySet1 = countMap1.keySet(); - Set keySet2 = countMap2.keySet(); - if (keySet1.size() != keySet2.size()) { - return false; + // 字母命中数统计 + int[] count1 = new int[26]; + int[] count2 = new int[26]; + + for (char c : s1.toCharArray()) { + count1[c - 'a']++; } - for (Character key : keySet1) { - if (!countMap2.containsKey(key)) { - return false; + for (int i = 0; i < len2; i++) { + if (i >= len1) { + count2[s2.charAt(i - len1) - 'a']--; } - if (countMap2.get(key).intValue() != countMap1.get(key).intValue()) { - return false; + count2[s2.charAt(i) - 'a']++; + if (Arrays.equals(count1, count2)) { + return true; } } - return true; + return false; } /** - * @see 03. URL化 + * 给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。 + *

+ * 示例 1: + *

+ * 输入: num1 = "2", num2 = "3" 输出: "6" 示例 2: + *

+ * 输入: num1 = "123", num2 = "456" 输出: "56088" + *

+ * 说明:num1 和 num2 的长度小于110。 num1 和 num2 只包含数字 0-9。 num1 和 num2 均不以零开头,除非是数字 0 本身。 + *

+ * 不能使用任何标准库的大数类型(比如 BigInteger)或直接将输入转换为整数来处理。 + * + * @see 字符串相乘 */ - public static String replaceSpaces(String str, int length) { - int realLength = str.length(); - int min = Math.min(length, realLength); - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < min; i++) { - char c = str.charAt(i); - if (str.charAt(i) == ' ') { - sb.append("%20"); - } else { - sb.append(c); - } + public static String multiply(String num1, String num2) { + if (num1.equals("0") || num2.equals("0")) { + return "0"; } - return sb.toString(); - } - /** - * @see 04. 回文排列 - */ - public static boolean canPermutePalindrome(String s) { - int length = s.length(); - boolean isEven = (length % 2) == 0; - Map map = new HashMap<>(length); - for (char c : s.toCharArray()) { - if (map.containsKey(c)) { - Integer cnt = map.get(c); - cnt++; - map.put(c, cnt); - } else { - map.put(c, 1); - } - } + String result = "0"; + for (int i = num1.length() - 1; i >= 0; i--) { - int oddCount = 0; - for (char c : map.keySet()) { - int count = map.get(c); + int carry = 0; - if (isEven && (count % 2) != 0) { return false; } - if (!isEven && (count % 2) != 0) { - if (oddCount > 1) { - return false; - } - oddCount++; + StringBuilder tempBuilder = new StringBuilder(); + int value1 = num1.charAt(i) - '0'; + + for (int temp = i; temp < num1.length() - 1; temp++) { + tempBuilder.append("0"); } - } - return true; - } - /** - * @see 05. 一次编辑 - */ - public static boolean oneEditAway(String first, String second) { - if (first == null || second == null) { - return false; - } - int len1 = first.length(); - int len2 = second.length(); - if (Math.abs(len1 - len2) > 1) { - return false; - } - if (len2 > len1) { return oneEditAway(second, first); } + for (int j = num2.length() - 1; j >= 0; j--) { + int value2 = num2.charAt(j) - '0'; + int value = value1 * value2 + carry; + int current = value % 10; + carry = value / 10; + tempBuilder.append(current); + } - for (int i = 0; i < len2; i++) { - if (first.charAt(i) != second.charAt(i)) { - return first.substring(i + 1).equals(second.substring(len1 == len2 ? i + 1 : i)); + if (carry > 0) { + tempBuilder.append(carry); } + + result = add(result, tempBuilder.reverse().toString()); } - return true; + + return result; } - /** - * @see 06. 字符串压缩 - */ - public static String compressString(String str) { - if (str == null) { return null; } + public static String add(String num1, String num2) { + StringBuilder builder = new StringBuilder(); + int carry = 0; - int originLen = str.length(); - if (str.length() <= 1) { - return str; - } + for (int i = num1.length() - 1, j = num2.length() - 1; + i >= 0 || j >= 0; + i--, j--) { - int cnt = 0; - char mark = str.charAt(0); - StringBuilder sb = new StringBuilder(); - for (char c : str.toCharArray()) { - if (mark == c) { - cnt++; - } else { - sb.append(mark).append(cnt); - // 设置新字符 - mark = c; - cnt = 1; + int result = carry; + if (i >= 0) { + result += num1.charAt(i) - '0'; + } + if (j >= 0) { + result += num2.charAt(j) - '0'; } + carry = result / 10; + int current = result % 10; + builder.append(current); } - sb.append(mark).append(cnt); - - String newStr = sb.toString(); - if (newStr.length() >= originLen) { - return str; - } else { - return newStr; + if (carry > 0) { + builder.append(carry); } + return builder.reverse().toString(); } /** - * @see 09. 字符串轮转 + * 给定一个字符串,逐个翻转字符串中的每个单词。 + *

+ * 示例 1: 输入: "the sky is blue" 输出: "blue is sky the" + *

+ * 示例 2: 输入: " hello world! " 输出: "world! hello" 解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。 + *

+ * 示例 3: 输入: "a good example" 输出: "example good a" 解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。 + *

+ * 说明: 无空格字符构成一个单词。 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。 + *

+ * 进阶: 请选用 C 语言的用户尝试使用 O(1) 额外空间复杂度的原地解法。 + * + * @see 翻转字符串里的单词 */ - public static boolean isFlipedString(String s1, String s2) { - if (s1 == null || s2 == null) { return false; } - - int len1 = s1.length(), len2 = s2.length(); - if (len1 != len2) { - return false; + public static String reverseWords(String s) { + StringBuilder builder = new StringBuilder(); + List list = new ArrayList<>(); + for (char c : s.toCharArray()) { + if (c != ' ') { + builder.append(c); + } else { + if (!builder.toString().equals("")) { + list.add(builder.toString()); + } + builder = new StringBuilder(); + } } - - if (s1.equals(s2)) { - return true; + if (!builder.toString().equals("")) { + list.add(builder.toString()); } - if (len1 == 1) { - return false; - } + builder = new StringBuilder(); + for (int i = list.size() - 1; i >= 0; i--) { - int begin = s1.indexOf(s2.charAt(0)) - 1; - for (int i = begin; i < len2 - 1; i++) { - String temp = leftMove(s1, i + 1); - if (s2.equals(temp)) { - return true; + builder.append(list.get(i)); + if (i != 0) { + builder.append(" "); } } - return false; + return builder.toString(); } /** - * 字符串整体向左偏移 + * 以 Unix 风格给出一个文件的绝对路径,你需要简化它。或者换句话说,将其转换为规范路径。 + *

+ * 在 Unix 风格的文件系统中,一个点(.)表示当前目录本身;此外,两个点 (..) 表示将目录切换到上一级(指向父目录);两者都可以是复杂相对路径的组成部分。更多信息请参阅:Linux / Unix中的绝对路径 vs + * 相对路径 + *

+ * 请注意,返回的规范路径必须始终以斜杠 / 开头,并且两个目录名之间必须只有一个斜杠 /。最后一个目录名(如果存在)不能以 / 结尾。此外,规范路径必须是表示绝对路径的最短字符串。 + *

+ * 示例 1: 输入:"/home/" 输出:"/home" 解释:注意,最后一个目录名后面没有斜杠。 + *

+ * 示例 2: 输入:"/../" 输出:"/" 解释:从根目录向上一级是不可行的,因为根是你可以到达的最高级。 + *

+ * 示例 3: 输入:"/home//foo/" 输出:"/home/foo" 解释:在规范路径中,多个连续斜杠需要用一个斜杠替换。 + *

+ * 示例 4: 输入:"/a/./b/../../c/" 输出:"/c" + *

+ * 示例 5: 输入:"/a/../../b/../c//.//" 输出:"/c" + *

+ * 示例 6: 输入:"/a//b////c/d//././/.." 输出:"/a/b/c" + * + * @see 简化路径 */ - private static String leftMove(String str, int pos) { - if (str == null || str.length() <= 1 || pos <= 0) { - return str; + public static String simplifyPath(String path) { + if (path.equals("/")) { + return path; + } + + if (path.endsWith("/")) { + path = path.substring(0, path.length() - 1); + } + + if (path.startsWith("/../")) { + path = path.replaceFirst("/../", "/"); } - String temp = str.substring(pos); - temp = temp + str.substring(0, pos); - return temp; + path = path.replaceAll("//", "/"); + return path; } } diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/string/ValidAnagram.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/str/ValidAnagram.java similarity index 98% rename from codes/algorithm/src/main/java/io/github/dunwu/algorithm/string/ValidAnagram.java rename to codes/algorithm/src/main/java/io/github/dunwu/algorithm/str/ValidAnagram.java index 60dc9fc..1dd91de 100644 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/string/ValidAnagram.java +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/str/ValidAnagram.java @@ -1,4 +1,4 @@ -package io.github.dunwu.algorithm.string; +package io.github.dunwu.algorithm.str; import java.util.HashMap; import java.util.Map; diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/string/StringAlgorithm.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/string/StringAlgorithm.java deleted file mode 100644 index b02ed5b..0000000 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/string/StringAlgorithm.java +++ /dev/null @@ -1,289 +0,0 @@ -package io.github.dunwu.algorithm.string; - -import java.util.*; - -/** - * @author Zhang Peng - * @since 2020-01-18 - */ -public class StringAlgorithm { - - /** - * 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。 - *

- * 示例 1: - *

- * 输入: "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。 示例 2: - *

- * 输入: "bbbbb" 输出: 1 解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。 示例 3: - *

- * 输入: "pwwkew" 输出: 3 解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。 - *

- * 请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。 - * - * @see 无重复字符的最长子串 - */ - public static int lengthOfLongestSubstring(String s) { - if (null == s || s.length() == 0) { - return 0; - } - - int max = 0; - int left = 0; - Map map = new HashMap<>(); - for (int i = 0; i < s.length(); i++) { - if (map.containsKey(s.charAt(i))) { - left = Math.max(left, map.get(s.charAt(i)) + 1); - } - map.put(s.charAt(i), i); - max = Math.max(max, i - left + 1); - } - return max; - } - - /** - * 编写一个函数来查找字符串数组中的最长公共前缀。 - *

- * 如果不存在公共前缀,返回空字符串 ""。 - *

- * 示例 1: - *

- * 输入: ["flower","flow","flight"] 输出: "fl" 示例 2: - *

- * 输入: ["dog","racecar","car"] 输出: "" 解释: 输入不存在公共前缀。 说明: - *

- * 所有输入只包含小写字母 a-z 。 - * - * @see 最长公共前缀 - */ - public static String longestCommonPrefix(String[] array) { - if (array == null || array.length == 0) { - return ""; - } else if (array.length == 1) { - return array[0]; - } else { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < array[0].length(); i++) { - char c = array[0].charAt(i); - boolean end = false; - for (int index = 1; index < array.length; index++) { - if (array[index].length() - 1 < i) { - end = true; - break; - } - - if (array[index].charAt(i) != c) { - end = true; - break; - } - } - if (end) { - break; - } else { - sb.append(c); - } - } - return sb.toString(); - } - } - - /** - * 给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的排列。 - *

- * 换句话说,第一个字符串的排列之一是第二个字符串的子串。 - *

- * 示例1: 输入: s1 = "ab" s2 = "eidbaooo" 输出: True 解释: s2 包含 s1 的排列之一 ("ba"). - *

- * 示例2: 输入: s1= "ab" s2 = "eidboaoo" 输出: False - *

- * 注意:输入的字符串只包含小写字母,两个字符串的长度都在 [1, 10,000] 之间 - * - * @see 字符串的排列 - */ - public static boolean checkInclusion(String s1, String s2) { - if (s1 == null || s1.length() == 0 || s2 == null || s2.length() == 0) { - return false; - } - - int len1 = s1.length(); - int len2 = s2.length(); - - // 字母命中数统计 - int[] count1 = new int[26]; - int[] count2 = new int[26]; - - for (char c : s1.toCharArray()) { - count1[c - 'a']++; - } - - for (int i = 0; i < len2; i++) { - if (i >= len1) { - count2[s2.charAt(i - len1) - 'a']--; - } - - count2[s2.charAt(i) - 'a']++; - if (Arrays.equals(count1, count2)) { - return true; - } - } - return false; - } - - /** - * 给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。 - *

- * 示例 1: - *

- * 输入: num1 = "2", num2 = "3" 输出: "6" 示例 2: - *

- * 输入: num1 = "123", num2 = "456" 输出: "56088" - *

- * 说明:num1 和 num2 的长度小于110。 num1 和 num2 只包含数字 0-9。 num1 和 num2 均不以零开头,除非是数字 0 本身。 - *

- * 不能使用任何标准库的大数类型(比如 BigInteger)或直接将输入转换为整数来处理。 - * - * @see 字符串相乘 - */ - public static String multiply(String num1, String num2) { - if (num1.equals("0") || num2.equals("0")) { - return "0"; - } - - String result = "0"; - for (int i = num1.length() - 1; i >= 0; i--) { - - int carry = 0; - - StringBuilder tempBuilder = new StringBuilder(); - int value1 = num1.charAt(i) - '0'; - - for (int temp = i; temp < num1.length() - 1; temp++) { - tempBuilder.append("0"); - } - - for (int j = num2.length() - 1; j >= 0; j--) { - int value2 = num2.charAt(j) - '0'; - int value = value1 * value2 + carry; - int current = value % 10; - carry = value / 10; - tempBuilder.append(current); - } - - if (carry > 0) { - tempBuilder.append(carry); - } - - result = add(result, tempBuilder.reverse().toString()); - } - - return result; - } - - public static String add(String num1, String num2) { - StringBuilder builder = new StringBuilder(); - int carry = 0; - - for (int i = num1.length() - 1, j = num2.length() - 1; - i >= 0 || j >= 0; - i--, j--) { - - int result = carry; - if (i >= 0) { - result += num1.charAt(i) - '0'; - } - if (j >= 0) { - result += num2.charAt(j) - '0'; - } - carry = result / 10; - int current = result % 10; - builder.append(current); - } - if (carry > 0) { - builder.append(carry); - } - return builder.reverse().toString(); - } - - /** - * 给定一个字符串,逐个翻转字符串中的每个单词。 - *

- * 示例 1: 输入: "the sky is blue" 输出: "blue is sky the" - *

- * 示例 2: 输入: " hello world! " 输出: "world! hello" 解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。 - *

- * 示例 3: 输入: "a good example" 输出: "example good a" 解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。 - *

- * 说明: 无空格字符构成一个单词。 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。 - *

- * 进阶: 请选用 C 语言的用户尝试使用 O(1) 额外空间复杂度的原地解法。 - * - * @see 翻转字符串里的单词 - */ - public static String reverseWords(String s) { - StringBuilder builder = new StringBuilder(); - List list = new ArrayList<>(); - for (char c : s.toCharArray()) { - if (c != ' ') { - builder.append(c); - } else { - if (!builder.toString().equals("")) { - list.add(builder.toString()); - } - builder = new StringBuilder(); - } - } - if (!builder.toString().equals("")) { - list.add(builder.toString()); - } - - builder = new StringBuilder(); - for (int i = list.size() - 1; i >= 0; i--) { - - builder.append(list.get(i)); - if (i != 0) { - builder.append(" "); - } - } - return builder.toString(); - } - - /** - * 以 Unix 风格给出一个文件的绝对路径,你需要简化它。或者换句话说,将其转换为规范路径。 - *

- * 在 Unix 风格的文件系统中,一个点(.)表示当前目录本身;此外,两个点 (..) 表示将目录切换到上一级(指向父目录);两者都可以是复杂相对路径的组成部分。更多信息请参阅:Linux / Unix中的绝对路径 vs - * 相对路径 - *

- * 请注意,返回的规范路径必须始终以斜杠 / 开头,并且两个目录名之间必须只有一个斜杠 /。最后一个目录名(如果存在)不能以 / 结尾。此外,规范路径必须是表示绝对路径的最短字符串。 - *

- * 示例 1: 输入:"/home/" 输出:"/home" 解释:注意,最后一个目录名后面没有斜杠。 - *

- * 示例 2: 输入:"/../" 输出:"/" 解释:从根目录向上一级是不可行的,因为根是你可以到达的最高级。 - *

- * 示例 3: 输入:"/home//foo/" 输出:"/home/foo" 解释:在规范路径中,多个连续斜杠需要用一个斜杠替换。 - *

- * 示例 4: 输入:"/a/./b/../../c/" 输出:"/c" - *

- * 示例 5: 输入:"/a/../../b/../c//.//" 输出:"/c" - *

- * 示例 6: 输入:"/a//b////c/d//././/.." 输出:"/a/b/c" - * - * @see 简化路径 - */ - public static String simplifyPath(String path) { - if (path.equals("/")) { - return path; - } - - if (path.endsWith("/")) { - path = path.substring(0, path.length() - 1); - } - - if (path.startsWith("/../")) { - path = path.replaceFirst("/../", "/"); - } - - path = path.replaceAll("//", "/"); - return path; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/string/\346\234\200\345\260\217\350\246\206\347\233\226\345\255\220\344\270\262.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/string/\346\234\200\345\260\217\350\246\206\347\233\226\345\255\220\344\270\262.java" deleted file mode 100644 index b2b4702..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/string/\346\234\200\345\260\217\350\246\206\347\233\226\345\255\220\344\270\262.java" +++ /dev/null @@ -1,83 +0,0 @@ -package io.github.dunwu.algorithm.string; - -import lombok.extern.slf4j.Slf4j; -import org.junit.jupiter.api.Assertions; - -import java.util.HashMap; - -/** - * 76. 最小覆盖子串 - * - * @author Zhang Peng - * @date 2025-01-10 - */ -@Slf4j -public class 最小覆盖子串 { - - public static void main(String[] args) { - Assertions.assertEquals("BANC", minWindow("ADOBECODEBANC", "ABC")); - Assertions.assertEquals("a", minWindow("a", "a")); - Assertions.assertEquals("", minWindow("a", "aa")); - } - - public static String minWindow(String s, String t) { - // 用合适的数据结构记录窗口中的数据,根据具体场景变通 - // 比如说,我想记录窗口中元素出现的次数,就用 map - // 如果我想记录窗口中的元素和,就可以只用一个 int - - // 记录 window 中的字符出现次数 - HashMap window = new HashMap<>(); - // 记录所需的字符出现次数 - HashMap need = new HashMap<>(); - for (int i = 0; i < t.length(); i++) { - char c = t.charAt(i); - need.put(c, need.getOrDefault(c, 0) + 1); - } - - int valid = 0; - int left = 0, right = 0; - // 记录最小覆盖子串的起始索引及长度 - int start = 0, len = Integer.MAX_VALUE; - while (right < s.length()) { - // c 是将移入窗口的字符 - char c = s.charAt(right); - // 增大窗口 - right++; - // 进行窗口内数据的一系列更新 - if (need.containsKey(c)) { - window.put(c, window.getOrDefault(c, 0) + 1); - if (window.get(c).equals(need.get(c))) { - valid++; - } - } - - // *** debug 输出的位置 *** - // 注意在最终的解法代码中不要 print - // 因为 IO 操作很耗时,可能导致超时 - log.info("window: [{}, {})", left, right); - - // 判断左侧窗口是否要收缩 - while (left < right && valid == need.size()) { - // 在这里更新最小覆盖子串 - if (right - left < len) { - start = left; - len = right - left; - } - - // d 是将移出窗口的字符 - char d = s.charAt(left); - // 缩小窗口 - left++; - // 进行窗口内数据的一系列更新 - if (need.containsKey(d)) { - if (window.get(d).equals(need.get(d))) { - valid--; - } - window.put(d, window.getOrDefault(d, 0) - 1); - } - } - } - return len == Integer.MAX_VALUE ? "" : s.substring(start, start + len); - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/string/\346\234\200\351\225\277\345\233\236\346\226\207\345\255\220\344\270\262.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/string/\346\234\200\351\225\277\345\233\236\346\226\207\345\255\220\344\270\262.java" deleted file mode 100644 index afcb0a9..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/string/\346\234\200\351\225\277\345\233\236\346\226\207\345\255\220\344\270\262.java" +++ /dev/null @@ -1,45 +0,0 @@ -package io.github.dunwu.algorithm.string; - -import org.junit.jupiter.api.Assertions; - -/** - * @author Zhang Peng - * @date 2025-01-10 - */ -public class 最长回文子串 { - - public static void main(String[] args) { - Assertions.assertEquals("bab", longestPalindrome("babad")); - Assertions.assertEquals("bb", longestPalindrome("cbbd")); - Assertions.assertEquals("aca", longestPalindrome("aacabdkacaa")); - } - - public static String longestPalindrome(String s) { - char[] chars = s.toCharArray(); - String max = s.substring(0, 1); - for (int i = 0; i < chars.length; i++) { - for (int j = chars.length - 1; j > i; j--) { - if (check(chars, i, j)) { - String temp = s.substring(i, j + 1); - if (temp.length() > max.length()) { - max = temp; - } - } - } - } - return max; - } - - public static boolean check(char[] chars, int begin, int end) { - int left = begin, right = end; - while (left < right) { - if (chars[left] != chars[right]) { - return false; - } - left++; - right--; - } - return true; - } - -} diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/BTree.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/BTree.java index d953673..cac20fd 100644 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/BTree.java +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/BTree.java @@ -1,6 +1,11 @@ package io.github.dunwu.algorithm.tree; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; +import java.util.Queue; /** * 二叉树 @@ -23,7 +28,7 @@ public BTree(TreeNode root) { this.root = root; } - public static > BTree buildTree(T... array) { + public static > BTree build(T... array) { BTree tree = new BTree<>(); List> list = new ArrayList<>(); @@ -79,32 +84,7 @@ public static > BTree buildTree(T... array) { * @return true / false */ public static > boolean isEquals(final BTree tree1, final BTree tree2) { - return isEquals(tree1.root, tree2.root); - } - - /** - * 判断两颗二叉树是否完全一致 - * - * @param root1 二叉树根节点,类型:{@link BTree#root} - * @param root2 二叉树根节点,类型:{@link BTree#root} - * @param 元素类型 - * @return true / false - * @see 相同的树 - */ - private static > boolean isEquals(TreeNode root1, TreeNode root2) { - if (root1 == null && root2 == null) { - return true; - } - - if (root1 == null || root2 == null) { - return false; - } - - if (!root1.value.equals(root2.value)) { - return false; - } - - return isEquals(root1.left, root2.left) && isEquals(root1.right, root2.right); + return TreeNode.isEquals(tree1.root, tree2.root); } /** @@ -116,37 +96,16 @@ private static > boolean isEquals(TreeNode root1, Tre * @see 叶子相似的树 */ public static > boolean isLeafSimilar(final BTree tree1, final BTree tree2) { - List leafs1 = new LinkedList<>(); - List leafs2 = new LinkedList<>(); - getLeafNodes(tree1, leafs1); - getLeafNodes(tree2, leafs2); + List leafs1 = TreeNode.getLeafNodes(tree1.root); + List leafs2 = TreeNode.getLeafNodes(tree2.root); return Arrays.equals(leafs1.toArray(), leafs2.toArray()); } /** * 获取叶子节点 - * - * @param tree {@link BTree} - * @param leafs [出参]叶子节点列表{@link List} - * @param 元素类型 - */ - public static > void getLeafNodes(BTree tree, List leafs) { - getLeafNodes(tree.root, leafs); - } - - /** - * 获取叶子节点 - * - * @param root {@link TreeNode} - * @param leafs [出参]叶子节点列表{@link List} - * @param 元素类型 */ - private static > void getLeafNodes(TreeNode root, List leafs) { - if (root == null) { return; } - - if (root.left == null && root.right == null) { leafs.add(root.value); } - getLeafNodes(root.left, leafs); - getLeafNodes(root.right, leafs); + public List getLeafNodes() { + return TreeNode.getLeafNodes(this.root); } /** @@ -155,24 +114,7 @@ private static > void getLeafNodes(TreeNode root, Lis * @return 二叉树的最大深度 */ public int maxDepth() { - return maxDepth(this.root); - } - - /** - * 采用递归方法获取二叉树的最大深度 - * - * @param root 二叉树根节点,类型:{@link BTree#root} - * @return 二叉树的最大深度 - * @see 二叉树的最大深度 - */ - private int maxDepth(TreeNode root) { - if (root == null) return 0; - - int left = maxDepth(root.left); - - int right = maxDepth(root.right); - - return Math.max(left, right) + 1; + return TreeNode.maxDepth(this.root); } /** @@ -181,86 +123,246 @@ private int maxDepth(TreeNode root) { * @return 二叉树的最小深度 */ public int minDepth() { - return minDepth(this.root); + return TreeNode.minDepth(this.root); } + // ------------------------------------------------------------- 遍历元素 + /** - * 采用递归方法获取二叉树的最小深度 + * 将二叉树按层次遍历顺序转换为列表,即广度优先搜索(BFS) * - * @param root 二叉树根节点,类型:{@link BTree#root} - * @return 二叉树的最小深度 - * @see 二叉树的最小深度 + * @return {@link List>} */ - private int minDepth(TreeNode root) { - if (root == null) { return 0; } + public List> levelOrderLists() { + return TreeNode.levelOrderLists(this.root); + } + + public static class TreeNode> { - int left = minDepth(root.left); + T val; - int right = minDepth(root.right); + TreeNode left; + + TreeNode right; - if (left == 0 || right == 0) { - return left + right + 1; + public TreeNode(T val) { + this.val = val; } - return Math.min(left, right) + 1; - } + public TreeNode(T val, TreeNode left, TreeNode right) { + this.val = val; + this.left = left; + this.right = right; + } - // ------------------------------------------------------------- 遍历元素 + @Override + public String toString() { + return String.valueOf(val); + } - /** - * 将二叉树按层次遍历顺序转换为列表,即广度优先搜索(BFS) - * - * @return {@link List>} - * @see 二叉树的层次遍历 II - */ - public List> levelOrderLists() { - List> lists = new ArrayList<>(); - if (root == null) { return lists; } - Queue> queue = new LinkedList<>(); - queue.offer(root); - while (!queue.isEmpty()) { - int size = queue.size(); - List temp = new ArrayList<>(); - for (int i = 0; i < size; i++) { + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof TreeNode)) return false; + TreeNode treeNode = (TreeNode) o; + return Objects.equals(val, treeNode.val) && + Objects.equals(left, treeNode.left) && + Objects.equals(right, treeNode.right); + } + + @Override + public int hashCode() { + return Objects.hash(val, left, right); + } + + public static > TreeNode build(T... values) { + + if (values == null || values.length == 0 || values[0] == null) { + return null; + } + + Queue> queue = new LinkedList<>(); + TreeNode root = new TreeNode<>(values[0]); + queue.offer(root); + + int i = 1; + while (!queue.isEmpty()) { + TreeNode current = queue.poll(); + + // 处理左子节点 + if (i < values.length && values[i] != null) { + current.left = new TreeNode(values[i]); + queue.offer(current.left); + } + i++; + + // 处理右子节点 + if (i < values.length && values[i] != null) { + current.right = new TreeNode(values[i]); + queue.offer(current.right); + } + i++; + } + + return root; + } + + public static > TreeNode find(TreeNode root, T val) { + if (root == null || Objects.equals(root.val, val)) { return root; } + TreeNode left = find(root.left, val); + if (left != null) return left; + return find(root.right, val); + } + + public static > List> toList(TreeNode root) { + List> list = new ArrayList<>(); + if (root == null) { + return list; + } + + Queue> queue = new LinkedList<>(); + queue.add(root); + while (!queue.isEmpty()) { TreeNode node = queue.poll(); - temp.add(node.value); - if (node.left != null) { queue.offer(node.left); } - if (node.right != null) { queue.offer(node.right); } + list.add(node); + if (node == null) continue; + queue.add(node.left); + queue.add(node.right); + } + + // 删除队列尾部的所有 null + int last = list.size() - 1; + while (last > 0 && list.get(last) == null) { + last--; } - lists.add(temp); + return list.subList(0, last + 1); } - return lists; - } - public List levelOrderList() { - List list = new ArrayList<>(); - if (root == null) { return list; } - Queue> queue = new LinkedList<>(); - queue.offer(root); - while (!queue.isEmpty()) { - int size = queue.size(); - for (int i = 0; i < size; i++) { + public static > List toValueList(TreeNode root) { + List list = new ArrayList<>(); + if (root == null) { + return list; + } + + Queue> queue = new LinkedList<>(); + queue.add(root); + while (!queue.isEmpty()) { TreeNode node = queue.poll(); - list.add(node.value); - if (node.left != null) { queue.offer(node.left); } - if (node.right != null) { queue.offer(node.right); } + if (node == null) { + list.add(null); + continue; + } else { + list.add(node.val); + } + + queue.add(node.left); + queue.add(node.right); + } + + // 删除队列尾部的所有 null + int last = list.size() - 1; + while (last > 0 && list.get(last) == null) { + last--; } + return list.subList(0, last + 1); + } + + /** + * 判断两颗二叉树是否完全一致 + * + * @param root1 二叉树根节点,类型:{@link BTree#root} + * @param root2 二叉树根节点,类型:{@link BTree#root} + * @param 元素类型 + * @return true / false + * @see 相同的树 + */ + private static > boolean isEquals(TreeNode root1, TreeNode root2) { + if (root1 == null && root2 == null) { return true; } + if (root1 == null || root2 == null) { return false; } + if (!root1.val.equals(root2.val)) { return false; } + return isEquals(root1.left, root2.left) && isEquals(root1.right, root2.right); } - return list; - } - static class TreeNode> { + /** + * 获取叶子节点 + * + * @param root {@link TreeNode} + * @param 元素类型 + */ + public static > List getLeafNodes(TreeNode root) { + List res = new ArrayList<>(); + getLeafNodes(root, res); + return res; + } - T value; + /** + * 获取叶子节点 + * + * @param root {@link TreeNode} + * @param leafs [出参]叶子节点列表{@link List} + * @param 元素类型 + */ + private static > void getLeafNodes(TreeNode root, List leafs) { + if (root == null) { return; } + if (root.left == null && root.right == null) { leafs.add(root.val); } + getLeafNodes(root.left, leafs); + getLeafNodes(root.right, leafs); + } - TreeNode left; + /** + * 采用递归方法获取二叉树的最大深度 + * + * @param root 二叉树根节点,类型:{@link BTree#root} + * @return 二叉树的最大深度 + * @see 二叉树的最大深度 + */ + public static > int maxDepth(TreeNode root) { + if (root == null) { return 0; } + int left = maxDepth(root.left); + int right = maxDepth(root.right); + return Math.max(left, right) + 1; + } - TreeNode right; + /** + * 采用递归方法获取二叉树的最小深度 + * + * @param root 二叉树根节点,类型:{@link BTree#root} + * @return 二叉树的最小深度 + * @see 二叉树的最小深度 + */ + public static > int minDepth(TreeNode root) { + if (root == null) { return 0; } + int left = minDepth(root.left); + int right = minDepth(root.right); + if (left == 0 || right == 0) { + return left + right + 1; + } + return Math.min(left, right) + 1; + } - public TreeNode(T value, TreeNode left, TreeNode right) { - this.value = value; - this.left = left; - this.right = right; + /** + * 将二叉树按层次遍历顺序转换为列表,即广度优先搜索(BFS) + * + * @return {@link List>} + * @see 二叉树的层次遍历 II + */ + public static > List> levelOrderLists(TreeNode root) { + List> lists = new ArrayList<>(); + if (root == null) { return lists; } + Queue> queue = new LinkedList<>(); + queue.offer(root); + while (!queue.isEmpty()) { + int size = queue.size(); + List list = new ArrayList<>(); + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + list.add(node.val); + if (node.left != null) { queue.offer(node.left); } + if (node.right != null) { queue.offer(node.right); } + } + lists.add(list); + } + return lists; } } diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/BaseCase.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/BaseCase.java new file mode 100644 index 0000000..8da17c6 --- /dev/null +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/BaseCase.java @@ -0,0 +1,25 @@ +package io.github.dunwu.algorithm.tree; + +import java.util.List; + +/** + * 基本示例 + * + * @author Zhang Peng + * @date 2025-10-27 + */ +public class BaseCase { + + public static class Node extends NTree { + + public Node(int val) { + super(val); + } + + public Node(int val, List children) { + super(val, children); + } + + } + +} diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/BinaryTree.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/BinaryTree.java deleted file mode 100644 index 84fbd3d..0000000 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/BinaryTree.java +++ /dev/null @@ -1,937 +0,0 @@ -package io.github.dunwu.algorithm.tree; - -import io.github.dunwu.algorithm.common.ITree; - -import java.util.ArrayDeque; -import java.util.Arrays; -import java.util.Comparator; -import java.util.Deque; - -/** - * B树是一种树数据结构,可以对数据进行排序,并允许以对数时间进行搜索,顺序访问,插入和删除。 - *

- * B树是二叉搜索树的一般化,因为节点可以有两个以上的子节点。 - *

- * 与自平衡二进制搜索树不同,B树针对读取和写入大块数据的系统进行了优化。 - *

- * 它通常用于数据库和文件系统。 - *

- * - * @author Justin Wetherell - * @see B-Tree (Wikipedia) - */ -@SuppressWarnings("ALL") -public class BinaryTree> implements ITree { - - private int minKeySize = 1; - - private int minChildrenSize = minKeySize + 1; // 2 - - private int maxKeySize = 2 * minKeySize; // 2 - - private int maxChildrenSize = maxKeySize + 1; // 3 - - private Node root = null; - - private int size = 0; - - /** - * Constructor for B-Tree which defaults to a 2-3 B-Tree. - */ - public BinaryTree() { - } - - /** - * Constructor for B-Tree of ordered parameter. Order here means minimum number of keys in a non-root node. - * - * @param order of the B-Tree. - */ - public BinaryTree(int order) { - this.minKeySize = order; - this.minChildrenSize = minKeySize + 1; - this.maxKeySize = 2 * minKeySize; - this.maxChildrenSize = maxKeySize + 1; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean add(T value) { - if (root == null) { - root = new Node(null, maxKeySize, maxChildrenSize); - root.addKey(value); - } else { - Node node = root; - while (node != null) { - if (node.numberOfChildren() == 0) { - node.addKey(value); - if (node.numberOfKeys() <= maxKeySize) { - // A-OK - break; - } - // Need to split up - split(node); - break; - } - // Navigate - - // Lesser or equal - T lesser = node.getKey(0); - if (value.compareTo(lesser) <= 0) { - node = node.getChild(0); - continue; - } - - // Greater - int numberOfKeys = node.numberOfKeys(); - int last = numberOfKeys - 1; - T greater = node.getKey(last); - if (value.compareTo(greater) > 0) { - node = node.getChild(numberOfKeys); - continue; - } - - // Search internal nodes - for (int i = 1; i < node.numberOfKeys(); i++) { - T prev = node.getKey(i - 1); - T next = node.getKey(i); - if (value.compareTo(prev) > 0 && value.compareTo(next) <= 0) { - node = node.getChild(i); - break; - } - } - } - } - - size++; - - return true; - } - - /** - * {@inheritDoc} - */ - @Override - public T remove(T value) { - T removed = null; - Node node = this.getNode(value); - removed = remove(value, node); - return removed; - } - - /** - * {@inheritDoc} - */ - @Override - public void clear() { - root = null; - size = 0; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean contains(T value) { - Node node = getNode(value); - return (node != null); - } - - /** - * Get the node with value. - * - * @param value to find in the tree. - * @return Node with value. - */ - private Node getNode(T value) { - Node node = root; - while (node != null) { - T lesser = node.getKey(0); - if (value.compareTo(lesser) < 0) { - if (node.numberOfChildren() > 0) { - node = node.getChild(0); - } else { - node = null; - } - continue; - } - - int numberOfKeys = node.numberOfKeys(); - int last = numberOfKeys - 1; - T greater = node.getKey(last); - if (value.compareTo(greater) > 0) { - if (node.numberOfChildren() > numberOfKeys) { - node = node.getChild(numberOfKeys); - } else { - node = null; - } - continue; - } - - for (int i = 0; i < numberOfKeys; i++) { - T currentValue = node.getKey(i); - if (currentValue.compareTo(value) == 0) { - return node; - } - - int next = i + 1; - if (next <= last) { - T nextValue = node.getKey(next); - if (currentValue.compareTo(value) < 0 && nextValue.compareTo(value) > 0) { - if (next < node.numberOfChildren()) { - node = node.getChild(next); - break; - } - return null; - } - } - } - } - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public int size() { - return size; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean validate() { - if (root == null) { - return true; - } - return validateNode(root); - } - - /** - * {@inheritDoc} - */ - @Override - public java.util.Collection toCollection() { - return (new JavaCompatibleBinaryTree(this)); - } - - /** - * The node's key size is greater than maxKeySize, split down the middle. - * - * @param nodeToSplit to split. - */ - private void split(Node nodeToSplit) { - Node node = nodeToSplit; - int numberOfKeys = node.numberOfKeys(); - int medianIndex = numberOfKeys / 2; - T medianValue = node.getKey(medianIndex); - - Node left = new Node(null, maxKeySize, maxChildrenSize); - for (int i = 0; i < medianIndex; i++) { - left.addKey(node.getKey(i)); - } - if (node.numberOfChildren() > 0) { - for (int j = 0; j <= medianIndex; j++) { - Node c = node.getChild(j); - left.addChild(c); - } - } - - Node right = new Node(null, maxKeySize, maxChildrenSize); - for (int i = medianIndex + 1; i < numberOfKeys; i++) { - right.addKey(node.getKey(i)); - } - if (node.numberOfChildren() > 0) { - for (int j = medianIndex + 1; j < node.numberOfChildren(); j++) { - Node c = node.getChild(j); - right.addChild(c); - } - } - - if (node.parent == null) { - // new root, height of tree is increased - Node newRoot = new Node(null, maxKeySize, maxChildrenSize); - newRoot.addKey(medianValue); - node.parent = newRoot; - root = newRoot; - node = root; - node.addChild(left); - node.addChild(right); - } else { - // Move the median value up to the parent - Node parent = node.parent; - parent.addKey(medianValue); - parent.removeChild(node); - parent.addChild(left); - parent.addChild(right); - - if (parent.numberOfKeys() > maxKeySize) { - split(parent); - } - } - } - - /** - * Remove the value from the Node and check invariants - * - * @param value T to remove from the tree - * @param node Node to remove value from - * @return True if value was removed from the tree. - */ - private T remove(T value, Node node) { - if (node == null) { - return null; - } - - T removed = null; - int index = node.indexOf(value); - removed = node.removeKey(value); - if (node.numberOfChildren() == 0) { - // leaf node - if (node.parent != null && node.numberOfKeys() < minKeySize) { - this.combined(node); - } else if (node.parent == null && node.numberOfKeys() == 0) { - // Removing root node with no keys or children - root = null; - } - } else { - // internal node - Node lesser = node.getChild(index); - Node greatest = this.getGreatestNode(lesser); - T replaceValue = this.removeGreatestValue(greatest); - node.addKey(replaceValue); - if (greatest.parent != null && greatest.numberOfKeys() < minKeySize) { - this.combined(greatest); - } - if (greatest.numberOfChildren() > maxChildrenSize) { - this.split(greatest); - } - } - - size--; - - return removed; - } - - /** - * Remove greatest valued key from node. - * - * @param node to remove greatest value from. - * @return value removed; - */ - private T removeGreatestValue(Node node) { - T value = null; - if (node.numberOfKeys() > 0) { - value = node.removeKey(node.numberOfKeys() - 1); - } - return value; - } - - /** - * Get the greatest valued child from node. - * - * @param nodeToGet child with the greatest value. - * @return Node child with greatest value. - */ - private Node getGreatestNode(Node nodeToGet) { - Node node = nodeToGet; - while (node.numberOfChildren() > 0) { - node = node.getChild(node.numberOfChildren() - 1); - } - return node; - } - - /** - * Combined children keys with parent when size is less than minKeySize. - * - * @param node with children to combined. - * @return True if combined successfully. - */ - private boolean combined(Node node) { - Node parent = node.parent; - int index = parent.indexOf(node); - int indexOfLeftNeighbor = index - 1; - int indexOfRightNeighbor = index + 1; - - Node rightNeighbor = null; - int rightNeighborSize = -minChildrenSize; - if (indexOfRightNeighbor < parent.numberOfChildren()) { - rightNeighbor = parent.getChild(indexOfRightNeighbor); - rightNeighborSize = rightNeighbor.numberOfKeys(); - } - - // Try to borrow neighbor - if (rightNeighbor != null && rightNeighborSize > minKeySize) { - // Try to borrow from right neighbor - T removeValue = rightNeighbor.getKey(0); - int prev = getIndexOfPreviousValue(parent, removeValue); - T parentValue = parent.removeKey(prev); - T neighborValue = rightNeighbor.removeKey(0); - node.addKey(parentValue); - parent.addKey(neighborValue); - if (rightNeighbor.numberOfChildren() > 0) { - node.addChild(rightNeighbor.removeChild(0)); - } - } else { - Node leftNeighbor = null; - int leftNeighborSize = -minChildrenSize; - if (indexOfLeftNeighbor >= 0) { - leftNeighbor = parent.getChild(indexOfLeftNeighbor); - leftNeighborSize = leftNeighbor.numberOfKeys(); - } - - if (leftNeighbor != null && leftNeighborSize > minKeySize) { - // Try to borrow from left neighbor - T removeValue = leftNeighbor.getKey(leftNeighbor.numberOfKeys() - 1); - int prev = getIndexOfNextValue(parent, removeValue); - T parentValue = parent.removeKey(prev); - T neighborValue = leftNeighbor.removeKey(leftNeighbor.numberOfKeys() - 1); - node.addKey(parentValue); - parent.addKey(neighborValue); - if (leftNeighbor.numberOfChildren() > 0) { - node.addChild(leftNeighbor.removeChild(leftNeighbor.numberOfChildren() - 1)); - } - } else if (rightNeighbor != null && parent.numberOfKeys() > 0) { - // Can't borrow from neighbors, try to combined with right neighbor - T removeValue = rightNeighbor.getKey(0); - int prev = getIndexOfPreviousValue(parent, removeValue); - T parentValue = parent.removeKey(prev); - parent.removeChild(rightNeighbor); - node.addKey(parentValue); - for (int i = 0; i < rightNeighbor.keysSize; i++) { - T v = rightNeighbor.getKey(i); - node.addKey(v); - } - for (int i = 0; i < rightNeighbor.childrenSize; i++) { - Node c = rightNeighbor.getChild(i); - node.addChild(c); - } - - if (parent.parent != null && parent.numberOfKeys() < minKeySize) { - // removing key made parent too small, combined up tree - this.combined(parent); - } else if (parent.numberOfKeys() == 0) { - // parent no longer has keys, make this node the new root - // which decreases the height of the tree - node.parent = null; - root = node; - } - } else if (leftNeighbor != null && parent.numberOfKeys() > 0) { - // Can't borrow from neighbors, try to combined with left neighbor - T removeValue = leftNeighbor.getKey(leftNeighbor.numberOfKeys() - 1); - int prev = getIndexOfNextValue(parent, removeValue); - T parentValue = parent.removeKey(prev); - parent.removeChild(leftNeighbor); - node.addKey(parentValue); - for (int i = 0; i < leftNeighbor.keysSize; i++) { - T v = leftNeighbor.getKey(i); - node.addKey(v); - } - for (int i = 0; i < leftNeighbor.childrenSize; i++) { - Node c = leftNeighbor.getChild(i); - node.addChild(c); - } - - if (parent.parent != null && parent.numberOfKeys() < minKeySize) { - // removing key made parent too small, combined up tree - this.combined(parent); - } else if (parent.numberOfKeys() == 0) { - // parent no longer has keys, make this node the new root - // which decreases the height of the tree - node.parent = null; - root = node; - } - } - } - - return true; - } - - /** - * Get the index of previous key in node. - * - * @param node to find the previous key in. - * @param value to find a previous value for. - * @return index of previous key or -1 if not found. - */ - private int getIndexOfPreviousValue(Node node, T value) { - for (int i = 1; i < node.numberOfKeys(); i++) { - T t = node.getKey(i); - if (t.compareTo(value) >= 0) { - return i - 1; - } - } - return node.numberOfKeys() - 1; - } - - /** - * Get the index of next key in node. - * - * @param node to find the next key in. - * @param value to find a next value for. - * @return index of next key or -1 if not found. - */ - private int getIndexOfNextValue(Node node, T value) { - for (int i = 0; i < node.numberOfKeys(); i++) { - T t = node.getKey(i); - if (t.compareTo(value) >= 0) { - return i; - } - } - return node.numberOfKeys() - 1; - } - - /** - * Validate the node according to the B-Tree invariants. - * - * @param node to validate. - * @return True if valid. - */ - private boolean validateNode(Node node) { - int keySize = node.numberOfKeys(); - if (keySize > 1) { - // Make sure the keys are sorted - for (int i = 1; i < keySize; i++) { - T p = node.getKey(i - 1); - T n = node.getKey(i); - if (p.compareTo(n) > 0) { - return false; - } - } - } - int childrenSize = node.numberOfChildren(); - if (node.parent == null) { - // root - if (keySize > maxKeySize) { - // check max key size. root does not have a min key size - return false; - } else if (childrenSize == 0) { - // if root, no children, and keys are valid - return true; - } else if (childrenSize < minChildrenSize) { - // root should have zero or at least two children - return false; - } else if (childrenSize > maxChildrenSize) { - return false; - } - } else { - // non-root - if (keySize < minKeySize) { - return false; - } else if (keySize > maxKeySize) { - return false; - } else if (childrenSize == 0) { - return true; - } else if (keySize != (childrenSize - 1)) { - // If there are chilren, there should be one more child then - // keys - return false; - } else if (childrenSize < minChildrenSize) { - return false; - } else if (childrenSize > maxChildrenSize) { - return false; - } - } - - Node first = node.getChild(0); - // The first child's last key should be less than the node's first key - if (first.getKey(first.numberOfKeys() - 1).compareTo(node.getKey(0)) > 0) { - return false; - } - - Node last = node.getChild(node.numberOfChildren() - 1); - // The last child's first key should be greater than the node's last key - if (last.getKey(0).compareTo(node.getKey(node.numberOfKeys() - 1)) < 0) { - return false; - } - - // Check that each node's first and last key holds it's invariance - for (int i = 1; i < node.numberOfKeys(); i++) { - T p = node.getKey(i - 1); - T n = node.getKey(i); - Node c = node.getChild(i); - if (p.compareTo(c.getKey(0)) > 0) { - return false; - } - if (n.compareTo(c.getKey(c.numberOfKeys() - 1)) < 0) { - return false; - } - } - - for (int i = 0; i < node.childrenSize; i++) { - Node c = node.getChild(i); - boolean valid = this.validateNode(c); - if (!valid) { - return false; - } - } - - return true; - } - - /** - * {@inheritDoc} - */ - @Override - public String toString() { - return TreePrinter.getString(this); - } - - private static class Node> { - - protected Node parent = null; - - private T[] keys = null; - - private int keysSize = 0; - - private Node[] children = null; - - private int childrenSize = 0; - - private Comparator> comparator = new Comparator>() { - @Override - public int compare(Node arg0, Node arg1) { - return arg0.getKey(0).compareTo(arg1.getKey(0)); - } - }; - - private Node(Node parent, int maxKeySize, int maxChildrenSize) { - this.parent = parent; - this.keys = (T[]) new Comparable[maxKeySize + 1]; - this.keysSize = 0; - this.children = new Node[maxChildrenSize + 1]; - this.childrenSize = 0; - } - - private int indexOf(T value) { - for (int i = 0; i < keysSize; i++) { - if (keys[i].equals(value)) { - return i; - } - } - return -1; - } - - private void addKey(T value) { - keys[keysSize++] = value; - Arrays.sort(keys, 0, keysSize); - } - - private T removeKey(T value) { - T removed = null; - boolean found = false; - if (keysSize == 0) { - return null; - } - for (int i = 0; i < keysSize; i++) { - if (keys[i].equals(value)) { - found = true; - removed = keys[i]; - } else if (found) { - // shift the rest of the keys down - keys[i - 1] = keys[i]; - } - } - if (found) { - keysSize--; - keys[keysSize] = null; - } - return removed; - } - - private T removeKey(int index) { - if (index >= keysSize) { - return null; - } - T value = keys[index]; - for (int i = index + 1; i < keysSize; i++) { - // shift the rest of the keys down - keys[i - 1] = keys[i]; - } - keysSize--; - keys[keysSize] = null; - return value; - } - - private Node getChild(int index) { - if (index >= childrenSize) { - return null; - } - return children[index]; - } - - private int indexOf(Node child) { - for (int i = 0; i < childrenSize; i++) { - if (children[i].equals(child)) { - return i; - } - } - return -1; - } - - private boolean addChild(Node child) { - child.parent = this; - children[childrenSize++] = child; - Arrays.sort(children, 0, childrenSize, comparator); - return true; - } - - private boolean removeChild(Node child) { - boolean found = false; - if (childrenSize == 0) { - return found; - } - for (int i = 0; i < childrenSize; i++) { - if (children[i].equals(child)) { - found = true; - } else if (found) { - // shift the rest of the keys down - children[i - 1] = children[i]; - } - } - if (found) { - childrenSize--; - children[childrenSize] = null; - } - return found; - } - - private Node removeChild(int index) { - if (index >= childrenSize) { - return null; - } - Node value = children[index]; - children[index] = null; - for (int i = index + 1; i < childrenSize; i++) { - // shift the rest of the keys down - children[i - 1] = children[i]; - } - childrenSize--; - children[childrenSize] = null; - return value; - } - - /** - * {@inheritDoc} - */ - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - - builder.append("keys=["); - for (int i = 0; i < numberOfKeys(); i++) { - T value = getKey(i); - builder.append(value); - if (i < numberOfKeys() - 1) { - builder.append(", "); - } - } - builder.append("]\n"); - - if (parent != null) { - builder.append("parent=["); - for (int i = 0; i < parent.numberOfKeys(); i++) { - T value = parent.getKey(i); - builder.append(value); - if (i < parent.numberOfKeys() - 1) { - builder.append(", "); - } - } - builder.append("]\n"); - } - - if (children != null) { - builder.append("keySize=").append(numberOfKeys()).append(" children=").append(numberOfChildren()) - .append("\n"); - } - - return builder.toString(); - } - - private int numberOfKeys() { - return keysSize; - } - - private T getKey(int index) { - return keys[index]; - } - - private int numberOfChildren() { - return childrenSize; - } - - } - - private static class TreePrinter { - - public static > String getString(BinaryTree tree) { - if (tree.root == null) { - return "Tree has no nodes."; - } - return getString(tree.root, "", true); - } - - private static > String getString(Node node, String prefix, boolean isTail) { - StringBuilder builder = new StringBuilder(); - - builder.append(prefix).append((isTail ? "└── " : "├── ")); - for (int i = 0; i < node.numberOfKeys(); i++) { - T value = node.getKey(i); - builder.append(value); - if (i < node.numberOfKeys() - 1) { - builder.append(", "); - } - } - builder.append("\n"); - - if (node.children != null) { - for (int i = 0; i < node.numberOfChildren() - 1; i++) { - Node obj = node.getChild(i); - builder.append(getString(obj, prefix + (isTail ? " " : "│ "), false)); - } - if (node.numberOfChildren() >= 1) { - Node obj = node.getChild(node.numberOfChildren() - 1); - builder.append(getString(obj, prefix + (isTail ? " " : "│ "), true)); - } - } - - return builder.toString(); - } - - } - - public static class JavaCompatibleBinaryTree> extends java.util.AbstractCollection { - - private BinaryTree tree = null; - - public JavaCompatibleBinaryTree(BinaryTree tree) { - this.tree = tree; - } - - /** - * {@inheritDoc} - */ - @Override - public java.util.Iterator iterator() { - return (new BinaryTreeIterator(this.tree)); - } - - /** - * {@inheritDoc} - */ - @Override - public int size() { - return tree.size(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean contains(Object value) { - return tree.contains((T) value); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean add(T value) { - return tree.add(value); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean remove(Object value) { - return (tree.remove((T) value) != null); - } - - private static class BinaryTreeIterator> implements java.util.Iterator { - - private BinaryTree tree = null; - - private Node lastNode = null; - - private C lastValue = null; - - private int index = 0; - - private Deque> toVisit = new ArrayDeque>(); - - protected BinaryTreeIterator(BinaryTree tree) { - this.tree = tree; - if (tree.root != null && tree.root.keysSize > 0) { - toVisit.add(tree.root); - } - } - - /** - * {@inheritDoc} - */ - @Override - public boolean hasNext() { - boolean toVisitSizeNotZero = toVisit.size() > 0; - boolean lastNodeNotZero = lastNode != null && index < lastNode.keysSize; - if (lastNodeNotZero || toVisitSizeNotZero) { - return true; - } - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public C next() { - if (lastNode != null && (index < lastNode.keysSize)) { - lastValue = lastNode.getKey(index++); - return lastValue; - } - while (toVisit.size() > 0) { - // Go thru the current nodes - Node n = toVisit.pop(); - - // Add non-null children - for (int i = 0; i < n.childrenSize; i++) { - toVisit.add(n.getChild(i)); - } - - // Update last node (used in remove method) - index = 0; - lastNode = n; - lastValue = lastNode.getKey(index++); - return lastValue; - } - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public void remove() { - if (lastNode != null && lastValue != null) { - // On remove, reset the iterator (very inefficient, I know) - tree.remove(lastValue, lastNode); - - lastNode = null; - lastValue = null; - index = 0; - toVisit.clear(); - if (tree.root != null && tree.root.keysSize > 0) { - toVisit.add(tree.root); - } - } - } - - } - - } - -} diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/IntBTree.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/IntBTree.java deleted file mode 100644 index 00bac67..0000000 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/IntBTree.java +++ /dev/null @@ -1,175 +0,0 @@ -package io.github.dunwu.algorithm.tree; - -import java.util.LinkedList; -import java.util.Queue; -import java.util.Stack; - -/** - * @author Zhang Peng - * @since 2020-01-20 - */ -public class IntBTree { - - /** - * 前序遍历递归方法 - * - * @param root {@link TreeNode} - */ - public static void preOrder(TreeNode root) { - TreeNode node = root; - if (node != null) { - System.out.print(node.val + " "); - preOrder(node.left); - preOrder(node.right); - } - } - - /** - * 前序遍历非递归方法 - * - * @param root {@link TreeNode} - */ - public static void preOrder2(TreeNode root) { - if (root == null) return; - Stack stack = new Stack<>(); - while (!stack.isEmpty() || root != null) { - while (root != null) { - System.out.print(root.val + " "); - stack.push(root); - root = root.left; - } - if (!stack.isEmpty()) { - TreeNode t = stack.pop(); - root = t.right; - } - } - } - - /** - * 中序遍历递归方法 - * - * @param root {@link TreeNode} - */ - public static void inOrder(TreeNode root) { - if (root != null) { - preOrder(root.left); - System.out.print(root.val + " "); - preOrder(root.right); - } - } - - /** - * 中序遍历非递归方法 - * - * @param root {@link TreeNode} - */ - public static void inOrder2(TreeNode root) { - if (root == null) { - return; - } - - Stack stack = new Stack<>(); - while (!stack.isEmpty() || root != null) { - while (root != null) { - stack.push(root); - root = root.left; - } - if (!stack.isEmpty()) { - TreeNode t = stack.pop(); - System.out.print(t.val + " "); - root = t.right; - } - } - } - - public static void postOrder(TreeNode root) { - if (root != null) { - postOrder(root.left); - postOrder(root.right); - System.out.print(root.val + " "); - } - } - - /** - * 中序遍历非递归方法 - * - * @param root {@link TreeNode} - */ - public static void postOrder2(TreeNode root) { - if (root == null) { - return; - } - - Stack stack = new Stack<>(); - while (!stack.isEmpty() || root != null) { - while (root != null) { - stack.push(root); - root = root.left; - } - if (!stack.isEmpty()) { - TreeNode t = stack.pop(); - System.out.print(t.val + " "); - root = t.left; - } - } - } - - public static void levelTraverse(TreeNode root) { - if (root == null) { - return; - } - Queue queue = new LinkedList<>(); - queue.add(root); - while (!queue.isEmpty()) { - TreeNode node = queue.poll(); - System.out.print(node.val + " "); - if (node.left != null) queue.add(node.left); - if (node.right != null) queue.add(node.right); - } - } - - public static void depthOrderTraverse(TreeNode root) { - if (root == null) { - return; - } - LinkedList stack = new LinkedList<>(); - stack.push(root); - while (!stack.isEmpty()) { - TreeNode node = stack.pop(); - System.out.print(node.val + " "); - if (node.left != null) stack.push(node.left); - if (node.right != null) stack.push(node.right); - } - } - - public static TreeNode sortedArrayToBST(int[] nums) { - if (nums == null || nums.length == 0) return null; - return _sortedArrayToBST(nums, 0, nums.length - 1); - } - - public static TreeNode _sortedArrayToBST(int[] nums, int left, int right) { - if (left > right) return null; - - // always choose left middle node as a root - int p = (left + right) / 2; - - // inorder traversal: left -> node -> right - TreeNode root = new TreeNode(nums[p]); - root.left = _sortedArrayToBST(nums, left, p - 1); - root.right = _sortedArrayToBST(nums, p + 1, right); - return root; - } - - public static class TreeNode { - - public int val; - - public TreeNode left; - - public TreeNode right; - - public TreeNode(int val) { this.val = val; } - - } - -} diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/NTree.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/NTree.java new file mode 100644 index 0000000..664e896 --- /dev/null +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/NTree.java @@ -0,0 +1,32 @@ +package io.github.dunwu.algorithm.tree; + +import java.util.LinkedList; +import java.util.List; + +/** + * N 叉树 + * + * @author Zhang Peng + * @date 2025-10-27 + */ +public class NTree> { + + public int val; + public List children; + + public NTree() { + val = -1; + children = new LinkedList<>(); + } + + public NTree(int val) { + this.val = val; + this.children = new LinkedList<>(); + } + + public NTree(int val, List children) { + this.val = val; + this.children = children; + } + +} diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/Node.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/Node.java new file mode 100644 index 0000000..8c295ec --- /dev/null +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/Node.java @@ -0,0 +1,27 @@ +package io.github.dunwu.algorithm.tree; + +import java.util.LinkedList; +import java.util.List; + +// 多叉树节点 +public class Node { + + public int val; + public List children; + + public Node() { + val = -1; + children = new LinkedList<>(); + } + + public Node(int val) { + this.val = val; + this.children = new LinkedList<>(); + } + + public Node(int val, List children) { + this.val = val; + this.children = children; + } + +} \ No newline at end of file diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/N\345\217\211\346\240\221\347\232\204\346\234\200\345\244\247\346\267\261\345\272\246.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/N\345\217\211\346\240\221\347\232\204\346\234\200\345\244\247\346\267\261\345\272\246.java" deleted file mode 100644 index e2ce861..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/N\345\217\211\346\240\221\347\232\204\346\234\200\345\244\247\346\267\261\345\272\246.java" +++ /dev/null @@ -1,55 +0,0 @@ -package io.github.dunwu.algorithm.tree; - -import java.util.List; - -// 559. N叉树的最大深度 -// -// https://leetcode-cn.com/problems/maximum-depth-of-n-ary-tree/ -// -// 给定一个 N 叉树,找到其最大深度。 -// -// 最大深度是指从根节点到最远叶子节点的最长路径上的节点总数。 -// -// 例如,给定一个 3叉树 : -// -// 我们应返回其最大深度,3。 -// -// 说明: -// -// 树的深度不会超过 1000。 -// 树的节点总不会超过 5000。 -public class N叉树的最大深度 { - - public static int maxDepth(Node root) { - if (root == null) return 0; - if (root.children == null || root.children.size() == 0) return 1; - int max = 0; - for (Node node : root.children) { - int temp = maxDepth(node); - if (temp > max) { - max = temp; - } - } - return max + 1; - } - - static class Node { - - public int val; - - public List children; - - public Node() {} - - public Node(int val) { - this.val = val; - } - - public Node(int val, List children) { - this.val = val; - this.children = children; - } - - } - -} diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/State.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/State.java new file mode 100644 index 0000000..9f19b02 --- /dev/null +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/State.java @@ -0,0 +1,15 @@ +package io.github.dunwu.algorithm.tree; + +// 多叉树的层序遍历 +// 每个节点自行维护 State 类,记录深度等信息 +public class State { + + public Node node; + public int depth; + + public State(Node node, int depth) { + this.node = node; + this.depth = depth; + } + +} \ No newline at end of file diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/TreeNode.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/TreeNode.java index a552ff1..632d59e 100644 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/TreeNode.java +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/TreeNode.java @@ -1,8 +1,15 @@ package io.github.dunwu.algorithm.tree; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; import java.util.Objects; +import java.util.Queue; /** + * 二叉树节点 + * * @author Zhang Peng * @since 2020-01-28 */ @@ -42,4 +49,139 @@ public int hashCode() { return Objects.hash(val, left, right); } + public static String serialize(TreeNode root) { + return serialize(root, "NULL", ","); + } + + public static String serialize(TreeNode root, String nullFlag, String sepFlag) { + StringBuilder sb = new StringBuilder(); + doSerialize(root, sb, nullFlag, sepFlag); + return sb.toString(); + } + + static void doSerialize(TreeNode root, StringBuilder sb, String nullFlag, String sepFlag) { + if (root == null) { + sb.append(nullFlag).append(sepFlag); + return; + } + sb.append(root.val).append(sepFlag); + doSerialize(root.left, sb, nullFlag, sepFlag); + doSerialize(root.right, sb, nullFlag, sepFlag); + } + + public static TreeNode deserialize(String data) { + return deserialize(data, "NULL", ","); + } + + public static TreeNode deserialize(String data, String nullFlag, String sepFlag) { + LinkedList nodes = new LinkedList<>(Arrays.asList(data.split(sepFlag))); + return doDeserialize(nodes, nullFlag); + } + + static TreeNode doDeserialize(LinkedList nodes, String nullFlag) { + if (nodes.isEmpty()) return null; + + // =============== 前序遍历处理 =============== + String val = nodes.removeFirst(); + if (nullFlag.equals(val)) { return null; } + TreeNode root = new TreeNode(Integer.parseInt(val)); + // ========================================== + + root.left = doDeserialize(nodes, nullFlag); + root.right = doDeserialize(nodes, nullFlag); + return root; + } + + public static TreeNode buildTree(Integer... values) { + + if (values == null || values.length == 0 || values[0] == null) { + return null; + } + + Queue queue = new LinkedList<>(); + TreeNode root = new TreeNode(values[0]); + queue.offer(root); + + int i = 1; + while (!queue.isEmpty() && i < values.length) { + TreeNode current = queue.poll(); + + // 处理左子节点 + if (i < values.length && values[i] != null) { + current.left = new TreeNode(values[i]); + queue.offer(current.left); + } + i++; + + // 处理右子节点 + if (i < values.length && values[i] != null) { + current.right = new TreeNode(values[i]); + queue.offer(current.right); + } + i++; + } + + return root; + } + + public static TreeNode find(TreeNode root, int val) { + if (root == null || root.val == val) { return root; } + TreeNode left = find(root.left, val); + if (left != null) return left; + return find(root.right, val); + } + + public static List toList(TreeNode root) { + List list = new ArrayList<>(); + if (root == null) { + return list; + } + + Queue queue = new LinkedList<>(); + queue.add(root); + while (!queue.isEmpty()) { + TreeNode node = queue.poll(); + list.add(node); + if (node == null) continue; + queue.add(node.left); + queue.add(node.right); + } + + // 删除队列尾部的所有 null + int last = list.size() - 1; + while (last > 0 && list.get(last) == null) { + last--; + } + return list.subList(0, last + 1); + } + + public static List toValueList(TreeNode root) { + List list = new ArrayList<>(); + if (root == null) { + return list; + } + + Queue queue = new LinkedList<>(); + queue.add(root); + while (!queue.isEmpty()) { + TreeNode node = queue.poll(); + if (node == null) { + list.add(null); + continue; + } else { + list.add(node.val); + } + + queue.add(node.left); + queue.add(node.right); + } + + // 删除队列尾部的所有 null + int last = list.size() - 1; + while (last > 0 && list.get(last) == null) { + last--; + } + return list.subList(0, last + 1); + } + } diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/TreeUtils.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/TreeUtils.java deleted file mode 100644 index 2395dbf..0000000 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/TreeUtils.java +++ /dev/null @@ -1,213 +0,0 @@ -package io.github.dunwu.algorithm.tree; - -import java.util.*; - -/** - * @author Zhang Peng - * @since 2020-01-28 - */ -public class TreeUtils { - - public static TreeNode buildTree(Integer[] array) { - List list = new ArrayList<>(); - - for (Integer value : array) { - // 创建结点,每一个结点的左结点和右结点为null - TreeNode node; - if (value == null) { - node = null; - } else { - node = new TreeNode(value, null, null); - } - // list中存着每一个结点 - list.add(node); - } - - // 构建二叉树 - if (list.size() > 0) { - // i表示的是根节点的索引,从0开始 - for (int i = 0; i < array.length / 2 - 1; i++) { - if (list.get(2 * i + 1) != null) { - // 左结点 - list.get(i).left = list.get(2 * i + 1); - } - if (list.get(2 * i + 2) != null) { - // 右结点 - list.get(i).right = list.get(2 * i + 2); - } - } - // 判断最后一个根结点:因为最后一个根结点可能没有右结点,所以单独拿出来处理 - int lastIndex = array.length / 2 - 1; - - // 左结点 - list.get(lastIndex).left = list.get(lastIndex * 2 + 1); - // 右结点,如果数组的长度为奇数才有右结点 - if (array.length % 2 == 1) { - list.get(lastIndex).right = list.get(lastIndex * 2 + 2); - } - - return list.get(0); - } else { - return null; - } - } - - public static TreeNode asTree(Integer... array) { - return buildTree(array); - } - - public static TreeNode find(TreeNode root, int val) { - if (root == null || root.val == val) { return root;} - TreeNode left = find(root.left, val); - if (left != null) return left; - return find(root.right, val); - } - - public static void depthOrderTraverse(TreeNode root) { - if (root == null) { - return; - } - LinkedList stack = new LinkedList<>(); - stack.push(root); - while (!stack.isEmpty()) { - TreeNode node = stack.pop(); - System.out.print(node.val + " "); - if (node.left != null) stack.push(node.left); - if (node.right != null) stack.push(node.right); - } - } - - public static List toBfsList(TreeNode root) { - List list = new ArrayList<>(); - if (root == null) { - return list; - } - - Queue queue = new LinkedList<>(); - queue.add(root); - while (!queue.isEmpty()) { - TreeNode node = queue.poll(); - list.add(node); - if (node == null) continue; - queue.add(node.left); - queue.add(node.right); - } - - // 删除队列尾部的所有 null - int last = list.size() - 1; - while (last > 0 && list.get(last) == null) { - last--; - } - return list.subList(0, last + 1); - } - - public static List toBfsValueList(TreeNode root) { - List list = new ArrayList<>(); - if (root == null) { - return list; - } - - Queue queue = new LinkedList<>(); - queue.add(root); - while (!queue.isEmpty()) { - TreeNode node = queue.poll(); - if (node == null) { - list.add(null); - continue; - } else { - list.add(node.val); - } - - queue.add(node.left); - queue.add(node.right); - } - - // 删除队列尾部的所有 null - int last = list.size() - 1; - while (last > 0 && list.get(last) == null) { - last--; - } - return list.subList(0, last + 1); - } - - public static String rserialize(TreeNode root, String str) { - if (root == null) { - str += "null,"; - } else { - str += str.valueOf(root.val) + ","; - str = rserialize(root.left, str); - str = rserialize(root.right, str); - } - return str; - } - - public static String serialize(TreeNode root) { - String text = rserialize(root, ""); - while (text.endsWith("null,")) { - int index = text.lastIndexOf("null,"); - text = text.substring(0, index); - } - if (text.endsWith(",")) { - text = text.substring(0, text.length() - 1); - } - return text; - } - - public static TreeNode rdeserialize(List list) { - List nodes = new ArrayList<>(); - - for (String value : list) { - // 创建结点,每一个结点的左结点和右结点为null - TreeNode node; - if (value == null || value.equalsIgnoreCase("null")) { - node = null; - } else { - node = new TreeNode(Integer.parseInt(value), null, null); - } - // list中存着每一个结点 - nodes.add(node); - } - - // 构建二叉树 - if (nodes.size() > 0) { - // i表示的是根节点的索引,从0开始 - for (int i = 0; i < list.size() / 2 - 1; i++) { - if (nodes.get(2 * i + 1) != null) { - // 左结点 - nodes.get(i).left = nodes.get(2 * i + 1); - } - if (nodes.get(2 * i + 2) != null) { - // 右结点 - nodes.get(i).right = nodes.get(2 * i + 2); - } - } - // 判断最后一个根结点:因为最后一个根结点可能没有右结点,所以单独拿出来处理 - int lastIndex = list.size() / 2 - 1; - - // 左结点 - nodes.get(lastIndex).left = nodes.get(lastIndex * 2 + 1); - // 右结点,如果数组的长度为奇数才有右结点 - if (list.size() % 2 == 1) { - nodes.get(lastIndex).right = nodes.get(lastIndex * 2 + 2); - } - - return nodes.get(0); - } else { - return null; - } - } - - public static TreeNode deserialize(String data) { - data = data.substring(1, data.length() - 1); - String[] nums = data.split(","); - List list = new LinkedList<>(Arrays.asList(nums)); - return rdeserialize(list); - } - - public static void main(String[] args) { - Integer[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - TreeNode head = TreeUtils.asTree(array); - toBfsList(head); - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\344\270\215\345\220\214\347\232\204\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\344\270\215\345\220\214\347\232\204\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221.java" new file mode 100644 index 0000000..c2d9a86 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\344\270\215\345\220\214\347\232\204\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221.java" @@ -0,0 +1,52 @@ +package io.github.dunwu.algorithm.tree.bstree; + +import org.junit.jupiter.api.Assertions; + +/** + * 96. 不同的二叉搜索树 + * + * @author Zhang Peng + * @date 2025-10-22 + */ +public class 不同的二叉搜索树 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(5, s.numTrees(3)); + Assertions.assertEquals(1, s.numTrees(1)); + } + + static class Solution { + + // 备忘录 + int[][] memo; + + // 主函数 + public int numTrees(int n) { + // 备忘录的值初始化 + memo = new int[n + 1][n + 1]; + // 计算闭区间 [1, n] 组成的 BST 个数 + return count(1, n); + } + + // 计算闭区间 [lo, hi] 组成的 BST 个数 + int count(int low, int high) { + // base case + if (low > high) { return 1; } + if (memo[low][high] != 0) { return memo[low][high]; } + + int res = 0; + for (int i = low; i <= high; i++) { + // i 的值作为根节点 root + int left = count(low, i - 1); + int right = count(i + 1, high); + // 左右子树的组合数乘积是 BST 的总数 + res += left * right; + } + memo[low][high] = res; + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\344\270\215\345\220\214\347\232\204\344\272\214\345\217\211\346\220\234\347\264\242\346\240\2212.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\344\270\215\345\220\214\347\232\204\344\272\214\345\217\211\346\220\234\347\264\242\346\240\2212.java" new file mode 100644 index 0000000..19d183b --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\344\270\215\345\220\214\347\232\204\344\272\214\345\217\211\346\220\234\347\264\242\346\240\2212.java" @@ -0,0 +1,85 @@ +package io.github.dunwu.algorithm.tree.bstree; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +/** + * 95. 不同的二叉搜索树 II + * + * @author Zhang Peng + * @date 2025-10-22 + */ +public class 不同的二叉搜索树2 { + + public static void main(String[] args) { + + Solution s = new Solution(); + + List output1 = s.generateTrees(3); + LinkedList> expectList1 = new LinkedList<>(); + expectList1.add(new LinkedList<>(Arrays.asList(1, null, 2, null, 3))); + expectList1.add(new LinkedList<>(Arrays.asList(1, null, 3, 2))); + expectList1.add(new LinkedList<>(Arrays.asList(2, 1, 3))); + expectList1.add(new LinkedList<>(Arrays.asList(3, 1, null, null, 2))); + expectList1.add(new LinkedList<>(Arrays.asList(3, 2, null, 1))); + Assertions.assertEquals(expectList1.size(), output1.size()); + output1.forEach(tree -> { + List expect = expectList1.poll(); + Assertions.assertArrayEquals(expect.toArray(), TreeNode.toValueList(tree).toArray()); + }); + + List output2 = s.generateTrees(1); + LinkedList> expectList2 = new LinkedList<>(); + expectList2.add(new LinkedList<>(Collections.singletonList(1))); + Assertions.assertEquals(expectList2.size(), output2.size()); + output2.forEach(tree -> { + List expect = expectList2.poll(); + Assertions.assertArrayEquals(expect.toArray(), TreeNode.toValueList(tree).toArray()); + }); + } + + static class Solution { + + // 主函数 + public List generateTrees(int n) { + if (n == 0) return new LinkedList<>(); + // 构造闭区间 [1, n] 组成的 BST + return build(1, n); + } + + // 构造闭区间 [low, high] 组成的 BST + List build(int low, int high) { + List res = new LinkedList<>(); + // base case + if (low > high) { + res.add(null); + return res; + } + + // 1、穷举 root 节点的所有可能。 + for (int i = low; i <= high; i++) { + // 2、递归构造出左右子树的所有合法 BST。 + List leftTree = build(low, i - 1); + List rightTree = build(i + 1, high); + // 3、给 root 节点穷举所有左右子树的组合。 + for (TreeNode left : leftTree) { + for (TreeNode right : rightTree) { + // i 作为根节点 root 的值 + TreeNode root = new TreeNode(i); + root.left = left; + root.right = right; + res.add(root); + } + } + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\344\272\214\345\217\211\346\220\234\347\264\242\345\255\220\346\240\221\347\232\204\346\234\200\345\244\247\351\224\256\345\200\274\345\222\214.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\344\272\214\345\217\211\346\220\234\347\264\242\345\255\220\346\240\221\347\232\204\346\234\200\345\244\247\351\224\256\345\200\274\345\222\214.java" new file mode 100644 index 0000000..42aa113 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\344\272\214\345\217\211\346\220\234\347\264\242\345\255\220\346\240\221\347\232\204\346\234\200\345\244\247\351\224\256\345\200\274\345\222\214.java" @@ -0,0 +1,76 @@ +package io.github.dunwu.algorithm.tree.bstree; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +/** + * 96. 不同的二叉搜索树 + * + * @author Zhang Peng + * @date 2025-10-22 + */ +public class 二叉搜索子树的最大键值和 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(20, + s.maxSumBST(TreeNode.buildTree(1, 4, 3, 2, 4, 2, 5, null, null, null, null, null, null, 4, 6))); + Assertions.assertEquals(2, s.maxSumBST(TreeNode.buildTree(4, 3, null, 1, 2))); + Assertions.assertEquals(0, s.maxSumBST(TreeNode.buildTree(-4, -2, -5))); + Assertions.assertEquals(6, s.maxSumBST(TreeNode.buildTree(2, 1, 3))); + Assertions.assertEquals(7, s.maxSumBST(TreeNode.buildTree(5, 4, 8, 3, null, 6, 3))); + } + + static class Solution { + + // 记录 BST 最大节点之和 + private int maxSum = 0; + + public int maxSumBST(TreeNode root) { + maxSum = 0; + findMaxMinSum(root); + return maxSum; + } + + // 计算以 root 为根的二叉树的最大值、最小值、节点和 + int[] findMaxMinSum(TreeNode root) { + // base case + if (root == null) { + return new int[] { + 1, Integer.MAX_VALUE, Integer.MIN_VALUE, 0 + }; + } + + // 递归计算左右子树 + int[] left = findMaxMinSum(root.left); + int[] right = findMaxMinSum(root.right); + + // ******* 后序位置 ******* + // 通过 left 和 right 推导返回值 + // 并且正确更新 maxSum 变量 + int[] res = new int[4]; + // 这个 if 在判断以 root 为根的二叉树是不是 BST + if (left[0] == 1 && right[0] == 1 && + root.val > left[2] && root.val < right[1]) { + // 以 root 为根的二叉树是 BST + res[0] = 1; + // 计算以 root 为根的这棵 BST 的最小值 + res[1] = Math.min(left[1], root.val); + // 计算以 root 为根的这棵 BST 的最大值 + res[2] = Math.max(right[2], root.val); + // 计算以 root 为根的这棵 BST 所有节点之和 + res[3] = left[3] + right[3] + root.val; + // 更新全局变量 + maxSum = Math.max(maxSum, res[3]); + } else { + // 以 root 为根的二叉树不是 BST + res[0] = 0; + // 其他的值都没必要计算了,因为用不到 + } + + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\344\270\255\347\232\204\346\217\222\345\205\245\346\223\215\344\275\234.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\344\270\255\347\232\204\346\217\222\345\205\245\346\223\215\344\275\234.java" index 528305b..9a6925c 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\344\270\255\347\232\204\346\217\222\345\205\245\346\223\215\344\275\234.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\344\270\255\347\232\204\346\217\222\345\205\245\346\223\215\344\275\234.java" @@ -1,36 +1,38 @@ package io.github.dunwu.algorithm.tree.bstree; import io.github.dunwu.algorithm.tree.TreeNode; -import io.github.dunwu.algorithm.tree.TreeUtils; - -import java.util.List; +import org.junit.jupiter.api.Assertions; /** + * 701. 二叉搜索树中的插入操作 + * * @author Zhang Peng - * @see 701. 二叉搜索树中的插入操作 * @since 2020-07-06 */ public class 二叉搜索树中的插入操作 { public static void main(String[] args) { - TreeNode tree = TreeUtils.asTree(4, 2, 7, 1, 3); - insertIntoBST(tree, 5); - List treeNodes = TreeUtils.toBfsList(tree); - System.out.println(treeNodes); + TreeNode input1 = TreeNode.buildTree(4, 2, 7, 1, 3); + TreeNode output1 = insertIntoBST(input1, 5); + Assertions.assertArrayEquals(new Integer[] { 4, 2, 7, 1, 3, 5 }, TreeNode.toValueList(output1).toArray()); + + TreeNode input2 = TreeNode.buildTree(40, 20, 60, 10, 30, 50, 70); + TreeNode output2 = insertIntoBST(input2, 25); + Assertions.assertArrayEquals(new Integer[] { 40, 20, 60, 10, 30, 50, 70, null, null, 25 }, + TreeNode.toValueList(output2).toArray()); + + TreeNode input3 = TreeNode.buildTree(4, 2, 7, 1, 3, null, null, null, null, null, null); + TreeNode output3 = insertIntoBST(input3, 5); + Assertions.assertArrayEquals(new Integer[] { 4, 2, 7, 1, 3, 5 }, + TreeNode.toValueList(output3).toArray()); } public static TreeNode insertIntoBST(TreeNode root, int val) { - if (root == null) return new TreeNode(val); - - TreeNode node = root; - if (val > node.val) { - if (node.right == null) { - node.right = new TreeNode(val); - } else { insertIntoBST(node.right, val); } + if (root == null) { return new TreeNode(val); } + if (root.val < val) { + root.right = (root.right == null) ? new TreeNode(val) : insertIntoBST(root.right, val); } else { - if (node.left == null) { - node.left = new TreeNode(val); - } else { insertIntoBST(node.left, val); } + root.left = (root.left == null) ? new TreeNode(val) : insertIntoBST(root.left, val); } return root; } diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\344\270\255\347\232\204\346\220\234\347\264\242.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\344\270\255\347\232\204\346\220\234\347\264\242.java" new file mode 100644 index 0000000..9650df6 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\344\270\255\347\232\204\346\220\234\347\264\242.java" @@ -0,0 +1,41 @@ +package io.github.dunwu.algorithm.tree.bstree; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +/** + * 538. 把二叉搜索树转换为累加树 + * + * @author Zhang Peng + * @date 2025-10-22 + */ +public class 二叉搜索树中的搜索 { + + public static void main(String[] args) { + + Solution s = new Solution(); + + TreeNode input1 = TreeNode.buildTree(4, 2, 7, 1, 3); + TreeNode output1 = s.searchBST(input1, 2); + Assertions.assertArrayEquals(new Integer[] { 2, 1, 3 }, TreeNode.toValueList(output1).toArray()); + + TreeNode output2 = s.searchBST(input1, 5); + Assertions.assertNull(output2); + } + + static class Solution { + + public TreeNode searchBST(TreeNode root, int val) { + if (root == null) { return null; } + if (root.val == val) { + return root; + } else if (root.val < val) { + return searchBST(root.right, val); + } else { + return searchBST(root.left, val); + } + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\344\270\255\347\254\254K\345\260\217\347\232\204\345\205\203\347\264\240.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\344\270\255\347\254\254K\345\260\217\347\232\204\345\205\203\347\264\240.java" new file mode 100644 index 0000000..5ce506e --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\344\270\255\347\254\254K\345\260\217\347\232\204\345\205\203\347\264\240.java" @@ -0,0 +1,44 @@ +package io.github.dunwu.algorithm.tree.bstree; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +/** + * 98. 验证二叉搜索树 + * + * @author Zhang Peng + * @date 2025-10-22 + */ +public class 二叉搜索树中第K小的元素 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(1, s.kthSmallest(TreeNode.buildTree(3, 1, 4, null, 2), 1)); + Assertions.assertEquals(3, s.kthSmallest(TreeNode.buildTree(5, 3, 6, 2, 4, null, null, 1), 3)); + } + + static class Solution { + + private int rank = 1; + private int res = 0; + + public int kthSmallest(TreeNode root, int k) { + rank = 1; + res = 0; + dfs(root, k); + return res; + } + + void dfs(TreeNode root, int k) { + if (root == null) { return; } + dfs(root.left, k); + if (rank++ == k) { + res = root.val; + return; + } + dfs(root.right, k); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210.java" index 95f3b32..5b8039e 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210.java" @@ -1,82 +1,53 @@ package io.github.dunwu.algorithm.tree.bstree; import io.github.dunwu.algorithm.tree.TreeNode; -import io.github.dunwu.algorithm.tree.TreeUtils; -import io.github.dunwu.algorithm.tree.btree.二叉树的最近公共祖先; import org.junit.jupiter.api.Assertions; /** - * 235. 二叉搜索树的最近公共祖先 算法实现 + * 235. 二叉搜索树的最近公共祖先 * - * @see 二叉树的最近公共祖先 可以使用二叉树的最近公共祖先,但没有利用二叉搜索树特性,性能略差 - * @see 235. 二叉搜索树的最近公共祖先 + * @author Zhang Peng + * @date 2025-10-22 */ public class 二叉搜索树的最近公共祖先 { public static void main(String[] args) { - TreeNode root = TreeUtils.asTree(6, 2, 8, 0, 4, 7, 9, null, null, 3, 5); - TreeNode p = TreeUtils.find(root, 2); - TreeNode q = TreeUtils.find(root, 8); - // TreeNode treeNode = lowestCommonAncestor(root, p, q); - TreeNode treeNode = lowestCommonAncestor2(root, p, q); - Assertions.assertNotNull(treeNode); - Assertions.assertEquals(6, treeNode.val); - System.out.println("公共祖先节点 = " + treeNode.val); - TreeNode root2 = TreeUtils.asTree(6, 2, 8, 0, 4, 7, 9, null, null, 3, 5); - TreeNode p2 = TreeUtils.find(root2, 2); - TreeNode q2 = TreeUtils.find(root2, 4); - // TreeNode treeNode2 = lowestCommonAncestor(root2, p2, q2); - TreeNode treeNode2 = lowestCommonAncestor2(root2, p2, q2); - Assertions.assertNotNull(treeNode2); - Assertions.assertEquals(2, treeNode2.val); - System.out.println("公共祖先节点 = " + treeNode2.val); - } + Solution s = new Solution(); - /** - * 递归方式求解 - *

- * 时间复杂度:O(N) 线性级 - *

- * 空间复杂度:O(2) 常数级 - */ - public static TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { - // 如果当前节点为空,直接返回 - // 或当前节点就是 p 或 q 其中一个,显然就是要找的最近公共祖先,直接返回 - if (root == null || root == p || root == q) return root; + TreeNode root = TreeNode.buildTree(6, 2, 8, 0, 4, 7, 9, null, null, 3, 5); - if (root.val > p.val && root.val > q.val) { // 如果当前节点值同时大于 p、q 的值,说明 p、q 肯定都在左子树 - return lowestCommonAncestor(root.left, p, q); - } else if (root.val < p.val && root.val < q.val) { // 如果当前节点值同时小于 p、q 的值,说明 p、q 肯定都在右子树 - return lowestCommonAncestor(root.right, p, q); - } else { - return root; - } + TreeNode node1 = s.lowestCommonAncestor(root, TreeNode.find(root, 2), TreeNode.find(root, 8)); + Assertions.assertNotNull(node1); + Assertions.assertEquals(6, node1.val); + + TreeNode node2 = s.lowestCommonAncestor(root, TreeNode.find(root, 2), TreeNode.find(root, 4)); + Assertions.assertNotNull(node2); + Assertions.assertEquals(2, node2.val); } - /** - * 非递归方式求解 - *

- * 时间复杂度:O(N) 线性级 - *

- * 空间复杂度:O(2) 常数级 - */ - public static TreeNode lowestCommonAncestor2(TreeNode root, TreeNode p, TreeNode q) { - // 如果当前节点为空,直接返回 - // 或当前节点就是 p 或 q 其中一个,显然就是要找的最近公共祖先,直接返回 - if (root == null || root == p || root == q) return root; + static class Solution { - TreeNode curr = root; - while (curr != null) { - if (curr.val > p.val && curr.val > q.val) { // 如果当前节点值同时大于 p、q 的值,说明 p、q 肯定都在左子树 - curr = curr.left; - } else if (curr.val < p.val && curr.val < q.val) { // 如果当前节点值同时小于 p、q 的值,说明 p、q 肯定都在右子树 - curr = curr.right; + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + if (root == null) return null; + if (p.val > q.val) { + // 保证 p.val <= q.val,便于后续情况讨论 + return lowestCommonAncestor(root, q, p); + } + if (root.val >= p.val && root.val <= q.val) { + // p <= root <= q + // 即 p 和 q 分别在 root 的左右子树,那么 root 就是 LCA + return root; + } + if (root.val > q.val) { + // p 和 q 都在 root 的左子树,那么 LCA 在左子树 + return lowestCommonAncestor(root.left, p, q); } else { - return curr; + // p 和 q 都在 root 的右子树,那么 LCA 在右子树 + return lowestCommonAncestor(root.right, p, q); } } - return curr; + } } diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\350\212\202\347\202\271\346\234\200\345\260\217\350\267\235\347\246\273.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\350\212\202\347\202\271\346\234\200\345\260\217\350\267\235\347\246\273.java" index b543c50..884b648 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\350\212\202\347\202\271\346\234\200\345\260\217\350\267\235\347\246\273.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\350\212\202\347\202\271\346\234\200\345\260\217\350\267\235\347\246\273.java" @@ -1,90 +1,51 @@ package io.github.dunwu.algorithm.tree.bstree; import io.github.dunwu.algorithm.tree.TreeNode; -import io.github.dunwu.algorithm.tree.TreeUtils; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import org.junit.jupiter.api.Assertions; /** - * 二叉搜索树结点最小距离 算法实现 - * - *

- * 给定一个二叉搜索树的根结点 root, 返回树中任意两节点的差的最小值。
- *
- * 示例:
- *
- * 输入: root = [4,2,6,1,3,null,null]
- * 输出: 1
- * 解释:
- * 注意,root是树结点对象(TreeNode object),而不是数组。
- *
- * 给定的树 [4,2,6,1,3,null,null] 可表示为下图:
- *
- *           4
- *         /   \
- *       2      6
- *      / \
- *     1   3
- *
- * 最小的差值是 1, 它是节点1和节点2的差值, 也是节点3和节点2的差值。
- * 
+ * 783. 二叉搜索树节点最小距离 * - * @see 二叉搜索树结点最小距离 + * @author Zhang Peng + * @date 2020-06-18 */ public class 二叉搜索树节点最小距离 { public static void main(String[] args) { - TreeNode tree = TreeUtils.asTree(4, 2, 6, 1, 3, null, null); - System.out.println("result = " + minDiffInBST2(tree)); + Solution s = new Solution(); + Assertions.assertEquals(1, s.minDiffInBST(TreeNode.buildTree(4, 2, 6, 1, 3))); + Assertions.assertEquals(1, s.minDiffInBST(TreeNode.buildTree(1, 0, 48, null, null, 12, 49))); } - // ------------------------------------------------------------------------------------------------- + static class Solution { - // 方法一:排序【通过】 - // 思路和算法:将树中所有节点的值写入数组,之后将数组排序。依次计算相邻数之间的差值,找出其中最小的值。 + private int pre; + private int res; - public static int minDiffInBST(TreeNode root) { - List list = new ArrayList(); - dfs(root, list); - Collections.sort(list); - - int min = Integer.MAX_VALUE; - for (int i = 0; i < list.size() - 1; ++i) { - min = Math.min(min, list.get(i + 1) - list.get(i)); + public int minDiffInBST(TreeNode root) { + pre = -1; + res = Integer.MAX_VALUE; + dfs(root); + return res; } - return min; - } - - public static void dfs(TreeNode node, List list) { - if (node == null) { return; } - list.add(node.val); - dfs(node.left, list); - dfs(node.right, list); - } + public void dfs(TreeNode root) { + // base case + if (root == null) { return; } - // ------------------------------------------------------------------------------------------------- + // 【前序】 + dfs(root.left); - // 方法二:中序遍历【通过】 - // 思路和算法:在二叉搜索树中,中序遍历会将树中节点按数值大小顺序输出。只需要遍历计算相邻数的差值,取其中最小的就可以了。 + // 【中序】中序保证递增有序 + if (pre != -1) { + res = Math.min(res, Math.abs(root.val - pre)); + } + pre = root.val; - public static Integer prev = null; - public static Integer min = Integer.MAX_VALUE; - - public static int minDiffInBST2(TreeNode root) { - if (root == null) return 0; - dfs2(root); - return min; - } + // 【后序】 + dfs(root.right); + } - public static void dfs2(TreeNode node) { - if (node == null) { return; } - dfs2(node.left); - if (prev != null) min = Math.min(min, node.val - prev); - prev = node.val; - dfs2(node.right); } } diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\344\273\216\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\345\210\260\346\233\264\345\244\247\345\222\214\346\240\221.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\344\273\216\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\345\210\260\346\233\264\345\244\247\345\222\214\346\240\221.java" new file mode 100644 index 0000000..e76ff50 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\344\273\216\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\345\210\260\346\233\264\345\244\247\345\222\214\346\240\221.java" @@ -0,0 +1,47 @@ +package io.github.dunwu.algorithm.tree.bstree; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +/** + * 538. 把二叉搜索树转换为累加树 + * + * @author Zhang Peng + * @date 2025-10-22 + */ +public class 从二叉搜索树到更大和树 { + + public static void main(String[] args) { + + Solution s = new Solution(); + + TreeNode input1 = TreeNode.buildTree(4, 1, 6, 0, 2, 5, 7, null, null, null, 3, null, null, null, 8); + TreeNode output1 = s.bstToGst(input1); + TreeNode expect1 = TreeNode.buildTree(30, 36, 21, 36, 35, 26, 15, null, null, null, 33, null, null, null, 8); + Assertions.assertEquals(expect1, output1); + + Assertions.assertEquals(TreeNode.buildTree(1, null, 1), s.bstToGst(TreeNode.buildTree(0, null, 1))); + } + + static class Solution { + + private int sum = 0; + + public TreeNode bstToGst(TreeNode root) { + sum = 0; + dfs(root); + return root; + } + + public void dfs(TreeNode root) { + if (root == null) { return; } + dfs(root.right); + sum += root.val; + root.val = sum; + // System.out.printf("%s\n", root.val); + dfs(root.left); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\345\210\240\351\231\244\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\344\270\255\347\232\204\350\212\202\347\202\271.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\345\210\240\351\231\244\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\344\270\255\347\232\204\350\212\202\347\202\271.java" new file mode 100644 index 0000000..e088862 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\345\210\240\351\231\244\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\344\270\255\347\232\204\350\212\202\347\202\271.java" @@ -0,0 +1,66 @@ +package io.github.dunwu.algorithm.tree.bstree; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +/** + * 701. 二叉搜索树中的插入操作 + * + * @author Zhang Peng + * @since 2020-07-06 + */ +public class 删除二叉搜索树中的节点 { + + public static void main(String[] args) { + + Solution s = new Solution(); + + TreeNode output1 = s.deleteNode(TreeNode.buildTree(5, 3, 6, 2, 4, null, 7), 3); + Assertions.assertEquals(TreeNode.buildTree(5, 4, 6, 2, null, null, 7), output1); + + TreeNode output2 = s.deleteNode(TreeNode.buildTree(5, 3, 6, 2, 4, null, 7), 0); + Assertions.assertEquals(TreeNode.buildTree(5, 3, 6, 2, 4, null, 7), output2); + + TreeNode output3 = s.deleteNode(TreeNode.buildTree(5, 3, 6, 2, 4, null, 7), 5); + Assertions.assertEquals(TreeNode.buildTree(6, 3, 7, 2, 4), output3); + + Assertions.assertEquals(TreeNode.buildTree(1), s.deleteNode(TreeNode.buildTree(2, 1), 2)); + + Assertions.assertNull(s.deleteNode(TreeNode.buildTree(), 0)); + Assertions.assertNull(s.deleteNode(TreeNode.buildTree(0), 0)); + } + + static class Solution { + + public TreeNode deleteNode(TreeNode root, int key) { + if (root == null) { return null; } + if (root.val == key) { + if (root.left == null) { return root.right; } + if (root.right == null) { return root.left; } + // 获得右子树最小的节点 + TreeNode minRightNode = getMin(root.right); + // 删除右子树最小的节点 + root.right = deleteNode(root.right, minRightNode.val); + // 用右子树最小的节点替换 root 节点 + minRightNode.left = root.left; + minRightNode.right = root.right; + root = minRightNode; + } else if (root.val > key) { + // 去左子树找 + root.left = deleteNode(root.left, key); + } else if (root.val < key) { + // 去右子树找 + root.right = deleteNode(root.right, key); + } + return root; + } + + public TreeNode getMin(TreeNode root) { + if (root == null) { return null; } + if (root.left != null) { return getMin(root.left); } + return root; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\345\260\206\346\234\211\345\272\217\346\225\260\347\273\204\350\275\254\346\215\242\344\270\272\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\345\260\206\346\234\211\345\272\217\346\225\260\347\273\204\350\275\254\346\215\242\344\270\272\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221.java" index e40f46e..2ba0983 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\345\260\206\346\234\211\345\272\217\346\225\260\347\273\204\350\275\254\346\215\242\344\270\272\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\345\260\206\346\234\211\345\272\217\346\225\260\347\273\204\350\275\254\346\215\242\344\270\272\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221.java" @@ -1,32 +1,46 @@ package io.github.dunwu.algorithm.tree.bstree; import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; /** + * 108. 将有序数组转换为二叉搜索树 + * * @author Zhang Peng * @since 2020-07-07 */ public class 将有序数组转换为二叉搜索树 { public static void main(String[] args) { - System.out.println("result = " + sortedArrayToBST(new int[] { -10, -3, 0, 5, 9 })); + Solution s = new Solution(); + Assertions.assertEquals(TreeNode.buildTree(0, -3, 9, -10, null, 5), + s.sortedArrayToBST(new int[] { -10, -3, 0, 5, 9 })); + Assertions.assertEquals(TreeNode.buildTree(3, 1), s.sortedArrayToBST(new int[] { 1, 3 })); } - public static TreeNode sortedArrayToBST(int[] nums) { - if (nums == null || nums.length == 0) return null; - return backtrack(nums, 0, nums.length - 1); - } + static class Solution { + + public TreeNode sortedArrayToBST(int[] nums) { + return sortedArrayToBST(nums, 0, nums.length - 1); + } - public static TreeNode backtrack(int[] nums, int left, int right) { - if (left > right) return null; - // always choose left middle node as a root - int p = (left + right) / 2; + // 将闭区间 [left, right] 中的元素转化成 BST,返回根节点 + TreeNode sortedArrayToBST(int[] nums, int left, int right) { + if (left > right) { + // 区间为空 + return null; + } + // 构造根节点 + // BST 节点左小右大,中间的元素就是根节点 + int mid = (left + right) / 2; + TreeNode root = new TreeNode(nums[mid]); + // 递归构建左子树 + root.left = sortedArrayToBST(nums, left, mid - 1); + // 递归构造右子树 + root.right = sortedArrayToBST(nums, mid + 1, right); + return root; + } - // inorder traversal: left -> node -> right - TreeNode root = new TreeNode(nums[p]); - root.left = backtrack(nums, left, p - 1); - root.right = backtrack(nums, p + 1, right); - return root; } } diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\346\212\212\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\350\275\254\346\215\242\344\270\272\347\264\257\345\212\240\346\240\221.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\346\212\212\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\350\275\254\346\215\242\344\270\272\347\264\257\345\212\240\346\240\221.java" new file mode 100644 index 0000000..2b0c161 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\346\212\212\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\350\275\254\346\215\242\344\270\272\347\264\257\345\212\240\346\240\221.java" @@ -0,0 +1,58 @@ +package io.github.dunwu.algorithm.tree.bstree; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +/** + * 538. 把二叉搜索树转换为累加树 + * + * @author Zhang Peng + * @date 2025-10-22 + */ +public class 把二叉搜索树转换为累加树 { + + public static void main(String[] args) { + + Solution s = new Solution(); + + TreeNode input1 = TreeNode.buildTree(4, 1, 6, 0, 2, 5, 7, null, null, null, 3, null, null, null, 8); + TreeNode output1 = s.convertBST(input1); + Assertions.assertArrayEquals( + new Integer[] { 30, 36, 21, 36, 35, 26, 15, null, null, null, 33, null, null, null, 8 }, + TreeNode.toValueList(output1).toArray()); + + TreeNode input2 = TreeNode.buildTree(0, null, 1); + TreeNode output2 = s.convertBST(input2); + Assertions.assertArrayEquals(new Integer[] { 1, null, 1 }, TreeNode.toValueList(output2).toArray()); + + TreeNode input3 = TreeNode.buildTree(1, 0, 2); + TreeNode output3 = s.convertBST(input3); + Assertions.assertArrayEquals(new Integer[] { 3, 3, 2 }, TreeNode.toValueList(output3).toArray()); + + TreeNode input4 = TreeNode.buildTree(3, 2, 4, 1); + TreeNode output4 = s.convertBST(input4); + Assertions.assertArrayEquals(new Integer[] { 7, 9, 4, 10 }, TreeNode.toValueList(output4).toArray()); + } + + static class Solution { + + int sum = 0; + + public TreeNode convertBST(TreeNode root) { + sum = 0; // 重置 + traverse(root); + return root; + } + + public void traverse(TreeNode root) { + if (root == null) return; + traverse(root.right); + sum += root.val; + root.val = sum; + // System.out.printf("val: %d\n", root.val); + traverse(root.left); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\351\252\214\350\257\201\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\351\252\214\350\257\201\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221.java" index 1974f3a..1177d66 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\351\252\214\350\257\201\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/bstree/\351\252\214\350\257\201\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221.java" @@ -1,69 +1,43 @@ package io.github.dunwu.algorithm.tree.bstree; import io.github.dunwu.algorithm.tree.TreeNode; -import io.github.dunwu.algorithm.tree.TreeUtils; import org.junit.jupiter.api.Assertions; -import java.util.*; - /** - * 98. 验证二叉搜索树 算法实现 + * 98. 验证二叉搜索树 * * @author Zhang Peng - * @see 98. 验证二叉搜索树 * @since 2020-07-02 */ public class 验证二叉搜索树 { public static void main(String[] args) { - TreeNode root = TreeUtils.asTree(2, 1, 3); - TreeNode root2 = TreeUtils.asTree(5, 1, 4, null, null, 3, 6); - TreeNode root3 = TreeUtils.asTree(1, 1); - - Assertions.assertTrue(isValidBST(root)); - Assertions.assertFalse(isValidBST(root2)); - Assertions.assertFalse(isValidBST(root3)); - - Assertions.assertTrue(isValidBST2(root)); - Assertions.assertFalse(isValidBST2(root2)); - Assertions.assertFalse(isValidBST2(root3)); + Solution s = new Solution(); + Assertions.assertTrue(s.isValidBST(TreeNode.buildTree(2, 1, 3))); + Assertions.assertFalse(s.isValidBST(TreeNode.buildTree(5, 1, 4, null, null, 3, 6))); + Assertions.assertFalse(s.isValidBST(TreeNode.buildTree(2, 2, 2))); + Assertions.assertFalse(s.isValidBST(TreeNode.buildTree(5, 4, 6, null, null, 3, 7))); + Assertions.assertTrue(s.isValidBST(TreeNode.buildTree(3, 1, 5, 0, 2, 4, 6))); } - public static boolean isValidBST(TreeNode root) { - return help(root, null, null); - } + static class Solution { - public static boolean help(TreeNode root, Integer min, Integer max) { - if (root == null) return true; - if (min != null && root.val <= min) return false; - if (max != null && root.val >= max) return false; - return help(root.left, min, root.val) && help(root.right, root.val, max); - } - - /** - * 中序遍历二叉搜索树获取到的一定是有序数组。符合这个条件的就是有效二叉搜索树 - *

- * 遍历二叉搜索树的时间复杂度:O(N) 但是对 List 进行排序,也要耗费时间,所以综合来看,应该是 O(N) + O(log N) - *

- * 空间复杂度:用两个链表进行比较,占用了 O(2N),比较耗费空间,性能并不好 - */ - public static boolean isValidBST2(TreeNode root) { - if (root == null) { return true; } - List list = new LinkedList<>(); - inOrder2(root, list); + public boolean isValidBST(TreeNode root) { + return isValidBST(root, null, null); + } - // 这里使用 TreeSet,基于两个目的:(1)去重 (2)根据值排序 - Set set2 = new TreeSet<>(); - set2.addAll(list); - return Arrays.equals(list.toArray(), set2.toArray()); - } + // 限定以 root 为根的子树节点必须满足 max.val > root.val > min.val + boolean isValidBST(TreeNode root, TreeNode min, TreeNode max) { + // base case + if (root == null) return true; + // 若 root.val 不符合 max 和 min 的限制,说明不是合法 BST + if (min != null && root.val <= min.val) return false; + if (max != null && root.val >= max.val) return false; + // 限定左子树的最大值是 root.val,右子树的最小值是 root.val + return isValidBST(root.left, min, root) + && isValidBST(root.right, root, max); + } - // 单纯的中序遍历 - public static void inOrder2(TreeNode root, List list) { - if (root == null) return; - inOrder2(root.left, list); - list.add(root.val); - inOrder2(root.right, list); } } diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\344\272\214\345\217\211\346\240\221\344\270\255\346\211\200\346\234\211\350\267\235\347\246\273\344\270\272K\347\232\204\347\273\223\347\202\271.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\344\272\214\345\217\211\346\240\221\344\270\255\346\211\200\346\234\211\350\267\235\347\246\273\344\270\272K\347\232\204\347\273\223\347\202\271.java" new file mode 100644 index 0000000..c9c6a89 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\344\272\214\345\217\211\346\240\221\344\270\255\346\211\200\346\234\211\350\267\235\347\246\273\344\270\272K\347\232\204\347\273\223\347\202\271.java" @@ -0,0 +1,87 @@ +package io.github.dunwu.algorithm.tree.btree.bfs; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; + +/** + * 863. 二叉树中所有距离为 K 的结点 + * + * @author Zhang Peng + * @date 2025-12-01 + */ +public class 二叉树中所有距离为K的结点 { + + public static void main(String[] args) { + Solution s = new Solution(); + TreeNode input = TreeNode.buildTree(1, 2, 3, 4, 5, 6, 7); + TreeNode target = TreeNode.find(input, 5); + Assertions.assertArrayEquals(new Integer[] { 7, 4, 1 }, s.distanceK(input, target, 2).toArray()); + } + + static class Solution { + + // 记录父节点:node.val -> parentNode + // 题目说了树中所有节点值都是唯一的,所以可以用 node.val 代表 TreeNode + HashMap parent = new HashMap<>(); + + public List distanceK(TreeNode root, TreeNode target, int k) { + // 遍历所有节点,记录每个节点的父节点 + traverse(root, null); + + // 开始从 target 节点施放 BFS 算法,找到距离为 k 的节点 + LinkedList q = new LinkedList<>(); + HashSet visited = new HashSet<>(); + q.offer(target); + visited.add(target.val); + // 记录离 target 的距离 + int dist = 0; + List res = new LinkedList<>(); + + while (!q.isEmpty()) { + int sz = q.size(); + for (int i = 0; i < sz; i++) { + TreeNode cur = q.poll(); + if (dist == k) { + // 找到距离起点 target 距离为 k 的节点 + res.add(cur.val); + } + // 向父节点、左右子节点扩散 + TreeNode parentNode = parent.get(cur.val); + if (parentNode != null && !visited.contains(parentNode.val)) { + visited.add(parentNode.val); + q.offer(parentNode); + } + if (cur.left != null && !visited.contains(cur.left.val)) { + visited.add(cur.left.val); + q.offer(cur.left); + } + if (cur.right != null && !visited.contains(cur.right.val)) { + visited.add(cur.right.val); + q.offer(cur.right); + } + } + // 向外扩展一圈 + dist++; + } + + return res; + } + + private void traverse(TreeNode root, TreeNode parentNode) { + if (root == null) { + return; + } + parent.put(root.val, parentNode); + // 二叉树递归框架 + traverse(root.left, root); + traverse(root.right, root); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\344\272\214\345\217\211\346\240\221\347\232\204\345\256\214\345\205\250\346\200\247\346\243\200\351\252\214.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\344\272\214\345\217\211\346\240\221\347\232\204\345\256\214\345\205\250\346\200\247\346\243\200\351\252\214.java" new file mode 100644 index 0000000..d27f609 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\344\272\214\345\217\211\346\240\221\347\232\204\345\256\214\345\205\250\346\200\247\346\243\200\351\252\214.java" @@ -0,0 +1,71 @@ +package io.github.dunwu.algorithm.tree.btree.bfs; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; + +/** + * 958. 二叉树的完全性检验 + * + * @author Zhang Peng + * @date 2025-08-18 + */ +public class 二叉树的完全性检验 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertTrue(s.isCompleteTree(TreeNode.buildTree(1, 2, 3, 4, 5, 6))); + Assertions.assertTrue(s.isCompleteTree( + TreeNode.buildTree(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33))); + Assertions.assertFalse(s.isCompleteTree(TreeNode.buildTree(1, 2, 3, 4, 5, null, 7))); + Assertions.assertFalse(s.isCompleteTree(TreeNode.buildTree(1, 2, 3, 5, null, 7, 8))); + Assertions.assertFalse(s.isCompleteTree(TreeNode.buildTree(1, null, 7))); + } + + static class Solution { + + static class NodeInfo { + + public int id; + public TreeNode node; + + public NodeInfo(int id, TreeNode node) { + this.id = id; + this.node = node; + } + + } + + public boolean isCompleteTree(TreeNode root) { + + if (root == null) { return false; } + + int expect = 1; + LinkedList queue = new LinkedList<>(); + queue.offer(new NodeInfo(1, root)); + while (!queue.isEmpty()) { + int size = queue.size(); + for (int i = 0; i < size; i++) { + NodeInfo info = queue.poll(); + if (expect != info.id) { return false; } + if (info.node.left == null && info.node.right != null) { + return false; + } + if (info.node.left != null) { + queue.offer(new NodeInfo(info.id * 2, info.node.left)); + } + if (info.node.right != null) { + queue.offer(new NodeInfo(info.id * 2 + 1, info.node.right)); + } + expect++; + } + } + + return true; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\345\271\263\345\235\207\345\200\274.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\345\271\263\345\235\207\345\200\274.java" new file mode 100644 index 0000000..71b0a95 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\345\271\263\345\235\207\345\200\274.java" @@ -0,0 +1,49 @@ +package io.github.dunwu.algorithm.tree.btree.bfs; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; +import java.util.List; + +/** + * 637. 二叉树的层平均值 + * + * @author Zhang Peng + * @date 2025-08-18 + */ +public class 二叉树的层平均值 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertArrayEquals(new Double[] { 3.00000, 14.50000, 11.00000 }, + s.averageOfLevels(TreeNode.buildTree(3, 9, 20, null, null, 15, 7)).toArray()); + Assertions.assertArrayEquals(new Double[] { 3.00000, 14.50000, 11.00000 }, + s.averageOfLevels(TreeNode.buildTree(3, 9, 20, 15, 7)).toArray()); + } + + static class Solution { + + public List averageOfLevels(TreeNode root) { + if (root == null) { return new LinkedList<>(); } + + LinkedList res = new LinkedList<>(); + LinkedList queue = new LinkedList<>(); + queue.offer(root); + while (!queue.isEmpty()) { + int size = queue.size(); + double sum = 0.0; + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + sum += node.val; + if (node.left != null) queue.offer(node.left); + if (node.right != null) queue.offer(node.right); + } + res.add(sum / size); + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\346\254\241\351\201\215\345\216\206.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\346\254\241\351\201\215\345\216\206.java" new file mode 100644 index 0000000..dd133f3 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\346\254\241\351\201\215\345\216\206.java" @@ -0,0 +1,67 @@ +package io.github.dunwu.algorithm.tree.btree.bfs; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +/** + * 二叉树的层次遍历 + * + * @author Zhang Peng + * @since 2020-06-18 + */ +public class 二叉树的层次遍历 { + + public static void main(String[] args) { + Solution s = new Solution(); + TreeNode root = TreeNode.buildTree(3, 9, 20, null, null, 15, 7); + List> expectList = new LinkedList<>(); + expectList.add(Arrays.asList(3)); + expectList.add(Arrays.asList(9, 20)); + expectList.add(Arrays.asList(15, 7)); + Assertions.assertArrayEquals(expectList.toArray(), s.levelOrder(root).toArray()); + + Solution s2 = new Solution(); + TreeNode root2 = TreeNode.buildTree(1); + List> expectList2 = new LinkedList<>(); + expectList2.add(Arrays.asList(1)); + Assertions.assertArrayEquals(expectList2.toArray(), s2.levelOrder(root2).toArray()); + + Solution s3 = new Solution(); + TreeNode root3 = TreeNode.buildTree(); + Assertions.assertArrayEquals(new LinkedList<>().toArray(), s3.levelOrder(root3).toArray()); + } + + static class Solution { + + public List> levelOrder(TreeNode root) { + + LinkedList> res = new LinkedList<>(); + if (root == null) { return res; } + + Queue queue = new LinkedList<>(); + queue.offer(root); + // while 循环控制从上向下一层层遍历 + while (!queue.isEmpty()) { + int size = queue.size(); + // 记录这一层的节点值 + List level = new LinkedList<>(); + // for 循环控制每一层从左向右遍历 + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + level.add(node.val); + if (node.left != null) { queue.offer(node.left); } + if (node.right != null) { queue.offer(node.right); } + } + res.add(level); + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\346\254\241\351\201\215\345\216\2062.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\346\254\241\351\201\215\345\216\2062.java" new file mode 100644 index 0000000..dae1cfc --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\346\254\241\351\201\215\345\216\2062.java" @@ -0,0 +1,73 @@ +package io.github.dunwu.algorithm.tree.btree.bfs; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +/** + * 二叉树的层次遍历 II + * + * @author Zhang Peng + * @since 2020-06-18 + */ +public class 二叉树的层次遍历2 { + + public static void main(String[] args) { + Solution s = new Solution(); + TreeNode root = TreeNode.buildTree(3, 9, 20, null, null, 15, 7); + List> expectList = new LinkedList<>(); + expectList.add(Arrays.asList(15, 7)); + expectList.add(Arrays.asList(9, 20)); + expectList.add(Arrays.asList(3)); + Assertions.assertArrayEquals(expectList.toArray(), s.levelOrderBottom(root).toArray()); + + Solution s2 = new Solution(); + TreeNode root2 = TreeNode.buildTree(1); + List> expectList2 = new LinkedList<>(); + expectList2.add(Arrays.asList(1)); + Assertions.assertArrayEquals(expectList2.toArray(), s2.levelOrderBottom(root2).toArray()); + + Solution s3 = new Solution(); + TreeNode root3 = TreeNode.buildTree(); + Assertions.assertArrayEquals(new LinkedList<>().toArray(), s3.levelOrderBottom(root3).toArray()); + } + + static class Solution { + + public List> levelOrderBottom(TreeNode root) { + + if (root == null) { + return new ArrayList(); + } + + LinkedList queue = new LinkedList<>(); + List> result = new LinkedList<>(); + queue.add(root); + + while (!queue.isEmpty()) { + int size = queue.size(); + List currentLevel = new LinkedList<>(); + result.add(currentLevel); + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + currentLevel.add(node.val); + if (node.left != null) { + queue.offer(node.left); + } + if (node.right != null) { + queue.offer(node.right); + } + } + } + Collections.reverse(result); + return result; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\244\247\345\256\275\345\272\246.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\244\247\345\256\275\345\272\246.java" new file mode 100644 index 0000000..df94b8e --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\244\247\345\256\275\345\272\246.java" @@ -0,0 +1,77 @@ +package io.github.dunwu.algorithm.tree.btree.bfs; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; + +/** + * 104. 二叉树的最大深度 + * + * @author Zhang Peng + * @date 2025-08-18 + */ +public class 二叉树的最大宽度 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(4, s.widthOfBinaryTree(TreeNode.buildTree(1, 3, 2, 5, 3, null, 9))); + Assertions.assertEquals(7, s.widthOfBinaryTree(TreeNode.buildTree(1, 3, 2, 5, null, null, 9, 6, null, 7))); + } + + static class Solution { + + public static class NodeInfo { + + public int id; + public TreeNode node; + + public NodeInfo(int id, TreeNode node) { + this.id = id; + this.node = node; + } + + @Override + public String toString() { + return "NodeInfo{" + + "id=" + id + + ", node=" + node.val + + '}'; + } + + } + + public int widthOfBinaryTree(TreeNode root) { + + if (root == null) return 0; + + int level = 0; + int maxWidth = 0; + LinkedList queue = new LinkedList<>(); + queue.offer(new NodeInfo(1, root)); + while (!queue.isEmpty()) { + level++; + int size = queue.size(); + NodeInfo left = null, right = null; + for (int i = 0; i < size; i++) { + NodeInfo info = queue.poll(); + if (info == null) { continue; } + if (left == null) { left = info; } + right = info; + if (info.node.left != null) { + queue.offer(new NodeInfo(2 * info.id, info.node.left)); + } + if (info.node.right != null) { + queue.offer(new NodeInfo(2 * info.id + 1, info.node.right)); + } + } + // System.out.printf("level: %d, left: %s, right: %s\n", level, left.toString(), right.toString()); + int width = right.id - left.id + 1; + maxWidth = Math.max(maxWidth, width); + } + return maxWidth; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\344\272\214\345\217\211\346\240\221\347\232\204\351\224\257\351\275\277\345\275\242\345\261\202\345\272\217\351\201\215\345\216\206.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\344\272\214\345\217\211\346\240\221\347\232\204\351\224\257\351\275\277\345\275\242\345\261\202\345\272\217\351\201\215\345\216\206.java" new file mode 100644 index 0000000..0d458c6 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\344\272\214\345\217\211\346\240\221\347\232\204\351\224\257\351\275\277\345\275\242\345\261\202\345\272\217\351\201\215\345\216\206.java" @@ -0,0 +1,65 @@ +package io.github.dunwu.algorithm.tree.btree.bfs; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +/** + * 103. 二叉树的锯齿形层序遍历 + * + * @author Zhang Peng + * @date 2025-10-21 + */ +public class 二叉树的锯齿形层序遍历 { + + public static void main(String[] args) { + Solution s = new Solution(); + TreeNode root = TreeNode.buildTree(3, 9, 20, null, null, 15, 7); + List> expectList = new LinkedList<>(); + expectList.add(Arrays.asList(3)); + expectList.add(Arrays.asList(20, 9)); + expectList.add(Arrays.asList(15, 7)); + Assertions.assertArrayEquals(expectList.toArray(), s.zigzagLevelOrder(root).toArray()); + + TreeNode root2 = TreeNode.buildTree(1, 2, 3, 4, null, null, 5); + List> expectList2 = new LinkedList<>(); + expectList2.add(Arrays.asList(1)); + expectList2.add(Arrays.asList(3, 2)); + expectList2.add(Arrays.asList(4, 5)); + Assertions.assertArrayEquals(expectList2.toArray(), s.zigzagLevelOrder(root2).toArray()); + } + + static class Solution { + + public List> zigzagLevelOrder(TreeNode root) { + List> result = new LinkedList<>(); + if (root == null) return result; + + boolean reverse = false; + LinkedList queue = new LinkedList<>(); + queue.addLast(root); + while (!queue.isEmpty()) { + int size = queue.size(); + LinkedList layer = new LinkedList<>(); + for (int i = 0; i < size; i++) { + TreeNode node = queue.removeFirst(); + if (reverse) { + layer.addFirst(node.val); + } else { + layer.addLast(node.val); + } + if (node.left != null) queue.addLast(node.left); + if (node.right != null) queue.addLast(node.right); + } + reverse = !reverse; + result.add(layer); + } + return result; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\345\234\250\346\257\217\344\270\252\346\240\221\350\241\214\344\270\255\346\211\276\346\234\200\345\244\247\345\200\274.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\345\234\250\346\257\217\344\270\252\346\240\221\350\241\214\344\270\255\346\211\276\346\234\200\345\244\247\345\200\274.java" new file mode 100644 index 0000000..c9bdb3c --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\345\234\250\346\257\217\344\270\252\346\240\221\350\241\214\344\270\255\346\211\276\346\234\200\345\244\247\345\200\274.java" @@ -0,0 +1,51 @@ +package io.github.dunwu.algorithm.tree.btree.bfs; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; +import java.util.List; + +/** + * 515. 在每个树行中找最大值 + * + * @author Zhang Peng + * @date 2025-08-18 + */ +public class 在每个树行中找最大值 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertArrayEquals(new Integer[] { 1, 3, 9 }, + s.largestValues(TreeNode.buildTree(1, 3, 2, 5, 3, null, 9)).toArray()); + Assertions.assertArrayEquals(new Integer[] { 1, 3 }, + s.largestValues(TreeNode.buildTree(1, 2, 3)).toArray()); + } + + static class Solution { + + public List largestValues(TreeNode root) { + + if (root == null) { return new LinkedList<>(); } + + LinkedList res = new LinkedList<>(); + LinkedList queue = new LinkedList<>(); + queue.offer(root); + while (!queue.isEmpty()) { + int size = queue.size(); + int max = Integer.MIN_VALUE; + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + max = Math.max(max, node.val); + if (node.left != null) { queue.offer(node.left); } + if (node.right != null) { queue.offer(node.right); } + } + res.add(max); + } + + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\345\241\253\345\205\205\346\257\217\344\270\252\350\212\202\347\202\271\347\232\204\344\270\213\344\270\200\344\270\252\345\217\263\344\276\247\350\212\202\347\202\271\346\214\207\351\222\210.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\345\241\253\345\205\205\346\257\217\344\270\252\350\212\202\347\202\271\347\232\204\344\270\213\344\270\200\344\270\252\345\217\263\344\276\247\350\212\202\347\202\271\346\214\207\351\222\210.java" new file mode 100644 index 0000000..061e188 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\345\241\253\345\205\205\346\257\217\344\270\252\350\212\202\347\202\271\347\232\204\344\270\213\344\270\200\344\270\252\345\217\263\344\276\247\350\212\202\347\202\271\346\214\207\351\222\210.java" @@ -0,0 +1,64 @@ +package io.github.dunwu.algorithm.tree.btree.bfs; + +import cn.hutool.json.JSONUtil; +import io.github.dunwu.algorithm.tree.TreeNode; + +import java.util.LinkedList; + +/** + * 116. 填充每个节点的下一个右侧节点指针 + * + * @author Zhang Peng + * @date 2025-08-11 + */ +public class 填充每个节点的下一个右侧节点指针 { + + public static void main(String[] args) { + Solution s = new Solution(); + TreeNode treeNode = TreeNode.buildTree(1, 2, 3, 4, 5, 6, 7); + Node root = JSONUtil.toBean(JSONUtil.toJsonStr(treeNode), Node.class); + s.connect(root); + System.out.println(root); + } + + static class Solution { + + public Node connect(Node root) { + if (root == null) return root; + LinkedList queue = new LinkedList<>(); + queue.offer(root); + while (!queue.isEmpty()) { + int size = queue.size(); + Node prev = queue.poll(); + if (prev.left != null) queue.offer(prev.left); + if (prev.right != null) queue.offer(prev.right); + for (int i = 1; i < size; i++) { + Node next = queue.poll(); + prev.next = next; + prev = next; + if (next.left != null) queue.offer(next.left); + if (next.right != null) queue.offer(next.right); + } + } + return root; + } + + } + + static class Node extends TreeNode { + + public Node next; + public Node left; + public Node right; + + public Node(int val) { + super(val); + } + + public Node(int val, TreeNode left, TreeNode right) { + super(val, left, right); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\345\241\253\345\205\205\346\257\217\344\270\252\350\212\202\347\202\271\347\232\204\344\270\213\344\270\200\344\270\252\345\217\263\344\276\247\350\212\202\347\202\271\346\214\207\351\222\2102.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\345\241\253\345\205\205\346\257\217\344\270\252\350\212\202\347\202\271\347\232\204\344\270\213\344\270\200\344\270\252\345\217\263\344\276\247\350\212\202\347\202\271\346\214\207\351\222\2102.java" new file mode 100644 index 0000000..187eae0 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\345\241\253\345\205\205\346\257\217\344\270\252\350\212\202\347\202\271\347\232\204\344\270\213\344\270\200\344\270\252\345\217\263\344\276\247\350\212\202\347\202\271\346\214\207\351\222\2102.java" @@ -0,0 +1,68 @@ +package io.github.dunwu.algorithm.tree.btree.bfs; + +import cn.hutool.json.JSONUtil; +import io.github.dunwu.algorithm.tree.TreeNode; + +import java.util.LinkedList; + +/** + * 117. 填充每个节点的下一个右侧节点指针 II + * + * @author Zhang Peng + * @date 2025-08-11 + */ +public class 填充每个节点的下一个右侧节点指针2 { + + public static void main(String[] args) { + Solution s = new Solution(); + TreeNode treeNode = TreeNode.buildTree(1, 2, 3, 4, 5, null, 7); + Node root = JSONUtil.toBean(JSONUtil.toJsonStr(treeNode), Node.class); + s.connect(root); + System.out.println(root); + } + + static class Solution { + + public Node connect(Node root) { + if (root == null) return null; + traverse(root); + return root; + } + + public void traverse(Node root) { + if (root == null) return; + LinkedList q = new LinkedList<>(); + q.offer(root); + + while (!q.isEmpty()) { + int size = q.size(); + Node prev = null; + for (int i = 0; i < size; i++) { + Node cur = q.poll(); + if (prev != null) { prev.next = cur; } + if (cur.left != null) q.offer(cur.left); + if (cur.right != null) q.offer(cur.right); + prev = cur; + } + } + } + + } + + static class Node extends TreeNode { + + public Node next; + public Node left; + public Node right; + + public Node(int val) { + super(val); + } + + public Node(int val, TreeNode left, TreeNode right) { + super(val, left, right); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\345\245\207\345\201\266\346\240\221.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\345\245\207\345\201\266\346\240\221.java" new file mode 100644 index 0000000..8d58a76 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\345\245\207\345\201\266\346\240\221.java" @@ -0,0 +1,59 @@ +package io.github.dunwu.algorithm.tree.btree.bfs; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; + +/** + * 1609. 奇偶树 + * + * @author Zhang Peng + * @date 2025-08-18 + */ +public class 奇偶树 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertTrue(s.isEvenOddTree(TreeNode.buildTree(1, 10, 4, 3, null, 7, 9, 12, 8, 6, null, null, 2))); + Assertions.assertFalse(s.isEvenOddTree(TreeNode.buildTree(5, 4, 2, 3, 3, 7))); + Assertions.assertFalse(s.isEvenOddTree(TreeNode.buildTree(5, 9, 1, 3, 5, 7))); + Assertions.assertTrue(s.isEvenOddTree(TreeNode.buildTree(1))); + Assertions.assertTrue( + s.isEvenOddTree(TreeNode.buildTree(11, 8, 6, 1, 3, 9, 11, 30, 20, 18, 16, 12, 10, 4, 2, 17))); + } + + static class Solution { + + public boolean isEvenOddTree(TreeNode root) { + if (root == null) return false; + int level = 0; + LinkedList queue = new LinkedList<>(); + queue.offer(root); + while (!queue.isEmpty()) { + int size = queue.size(); + boolean odd = level % 2 != 0; + Integer lastVal = null; + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + if (odd) { // 奇数层 + // 奇数下标 层上的所有节点的值都是 偶 整数,从左到右按顺序 严格递减 + if (node.val % 2 != 0) { return false; } + if (lastVal != null && node.val >= lastVal) { return false; } + } else { // 偶数层 + // 偶数下标 层上的所有节点的值都是 奇 整数,从左到右按顺序 严格递增 + if (node.val % 2 == 0) { return false; } + if (lastVal != null && node.val <= lastVal) { return false; } + } + lastVal = node.val; + if (node.left != null) { queue.offer(node.left); } + if (node.right != null) { queue.offer(node.right); } + } + level++; + } + return true; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\345\256\214\345\205\250\344\272\214\345\217\211\346\240\221\346\217\222\345\205\245\345\231\250.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\345\256\214\345\205\250\344\272\214\345\217\211\346\240\221\346\217\222\345\205\245\345\231\250.java" new file mode 100644 index 0000000..740f4ea --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\345\256\214\345\205\250\344\272\214\345\217\211\346\240\221\346\217\222\345\205\245\345\231\250.java" @@ -0,0 +1,74 @@ +package io.github.dunwu.algorithm.tree.btree.bfs; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; + +/** + * 919. 完全二叉树插入器 + * + * @author Zhang Peng + * @date 2025-08-18 + */ +public class 完全二叉树插入器 { + + public static void main(String[] args) { + CBTInserter c = new CBTInserter(TreeNode.buildTree(1, 2)); + Assertions.assertEquals(1, c.insert(3)); + Assertions.assertEquals(2, c.insert(4)); + Assertions.assertEquals(TreeNode.buildTree(1, 2, 3, 4), c.get_root()); + } + + static class CBTInserter { + + private TreeNode root; + private final LinkedList unfull; + + public CBTInserter(TreeNode root) { + this.root = root; + this.unfull = new LinkedList<>(); + + LinkedList q = new LinkedList<>(); + q.offer(this.root); + while (!q.isEmpty()) { + for (int i = 0; i < q.size(); i++) { + TreeNode node = q.poll(); + if (node.left != null) { + q.offer(node.left); + } + if (node.right != null) { + q.offer(node.right); + } + if (node.left == null || node.right == null) { + unfull.offer(node); + } + } + } + } + + public int insert(int val) { + if (root == null) { + root = new TreeNode(val); + return 0; + } + + TreeNode parent = unfull.peek(); + TreeNode node = new TreeNode(val); + if (parent.left == null) { + parent.left = node; + } else if (parent.right == null) { + parent.right = node; + unfull.poll(); + } + unfull.offer(node); + return parent.val; + } + + public TreeNode get_root() { + return this.root; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\345\261\202\346\225\260\346\234\200\346\267\261\345\217\266\345\255\220\350\212\202\347\202\271\347\232\204\345\222\214.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\345\261\202\346\225\260\346\234\200\346\267\261\345\217\266\345\255\220\350\212\202\347\202\271\347\232\204\345\222\214.java" new file mode 100644 index 0000000..0992a9a --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\345\261\202\346\225\260\346\234\200\346\267\261\345\217\266\345\255\220\350\212\202\347\202\271\347\232\204\345\222\214.java" @@ -0,0 +1,48 @@ +package io.github.dunwu.algorithm.tree.btree.bfs; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; + +/** + * 1302. 层数最深叶子节点的和 + * + * @author Zhang Peng + * @date 2025-08-18 + */ +public class 层数最深叶子节点的和 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(15, + s.deepestLeavesSum(TreeNode.buildTree(1, 2, 3, 4, 5, null, 6, 7, null, null, null, null, 8))); + Assertions.assertEquals(19, + s.deepestLeavesSum(TreeNode.buildTree(6, 7, 8, 2, 7, 1, 3, 9, null, 1, 4, null, null, null, 5))); + } + + static class Solution { + + public int deepestLeavesSum(TreeNode root) { + + if (root == null) { return 0; } + + int sum = 0; + LinkedList queue = new LinkedList<>(); + queue.offer(root); + while (!queue.isEmpty()) { + sum = 0; + int size = queue.size(); + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + sum += node.val; + if (node.left != null) { queue.offer(node.left); } + if (node.right != null) { queue.offer(node.right); } + } + } + return sum; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\345\275\251\347\201\257\350\243\205\351\245\260\350\256\260\345\275\225.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\345\275\251\347\201\257\350\243\205\351\245\260\350\256\260\345\275\225.java" new file mode 100644 index 0000000..37abde6 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\345\275\251\347\201\257\350\243\205\351\245\260\350\256\260\345\275\225.java" @@ -0,0 +1,41 @@ +package io.github.dunwu.algorithm.tree.btree.bfs; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +import java.util.ArrayList; +import java.util.LinkedList; + +/** + * LCR 149. 彩灯装饰记录 I + * + * @author Zhang Peng + * @since 2025-10-28 + */ +public class 彩灯装饰记录 { + + public static void main(String[] args) { + Solution s = new Solution(); + TreeNode root = TreeNode.buildTree(8, 17, 21, 18, null, null, 6); + Assertions.assertArrayEquals(new int[] { 8, 17, 21, 18, 6 }, s.decorateRecord(root)); + } + + static class Solution { + + public int[] decorateRecord(TreeNode root) { + ArrayList list = new ArrayList<>(); + LinkedList queue = new LinkedList<>(); + queue.offer(root); + while (!queue.isEmpty()) { + TreeNode node = queue.poll(); + if (node == null) { continue; } + list.add(node.val); + if (node.left != null) { queue.offer(node.left); } + if (node.right != null) { queue.offer(node.right); } + } + return list.stream().mapToInt(Integer::intValue).toArray(); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\345\275\251\347\201\257\350\243\205\351\245\260\350\256\260\345\275\2252.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\345\275\251\347\201\257\350\243\205\351\245\260\350\256\260\345\275\2252.java" new file mode 100644 index 0000000..26ba27f --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\345\275\251\347\201\257\350\243\205\351\245\260\350\256\260\345\275\2252.java" @@ -0,0 +1,57 @@ +package io.github.dunwu.algorithm.tree.btree.bfs; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +/** + * LCR 150. 彩灯装饰记录 II + * + * @author Zhang Peng + * @since 2025-10-28 + */ +public class 彩灯装饰记录2 { + + public static void main(String[] args) { + Solution s = new Solution(); + TreeNode root = TreeNode.buildTree(8, 17, 21, 18, null, null, 6); + List> expectList = new ArrayList<>(); + expectList.add(Arrays.asList(8)); + expectList.add(Arrays.asList(17, 21)); + expectList.add(Arrays.asList(18, 6)); + List> output = s.decorateRecord(root); + for (int i = 0; i < expectList.size(); i++) { + Assertions.assertArrayEquals(expectList.get(i).toArray(), output.get(i).toArray()); + } + } + + static class Solution { + + public List> decorateRecord(TreeNode root) { + List> res = new ArrayList<>(); + LinkedList queue = new LinkedList<>(); + queue.offer(root); + while (!queue.isEmpty()) { + List list = new ArrayList<>(); + int size = queue.size(); + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + if (node == null) { continue; } + list.add(node.val); + if (node.left != null) { queue.offer(node.left); } + if (node.right != null) { queue.offer(node.right); } + } + if (list.size() != 0) { + res.add(list); + } + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\345\275\251\347\201\257\350\243\205\351\245\260\350\256\260\345\275\2253.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\345\275\251\347\201\257\350\243\205\351\245\260\350\256\260\345\275\2253.java" new file mode 100644 index 0000000..7985e9f --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\345\275\251\347\201\257\350\243\205\351\245\260\350\256\260\345\275\2253.java" @@ -0,0 +1,63 @@ +package io.github.dunwu.algorithm.tree.btree.bfs; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +/** + * LCR 151. 彩灯装饰记录 III + * + * @author Zhang Peng + * @since 2025-10-28 + */ +public class 彩灯装饰记录3 { + + public static void main(String[] args) { + Solution s = new Solution(); + TreeNode root = TreeNode.buildTree(8, 17, 21, 18, null, null, 6); + List> expectList = new ArrayList<>(); + expectList.add(Arrays.asList(8)); + expectList.add(Arrays.asList(21, 17)); + expectList.add(Arrays.asList(18, 6)); + List> output = s.decorateRecord(root); + for (int i = 0; i < expectList.size(); i++) { + Assertions.assertArrayEquals(expectList.get(i).toArray(), output.get(i).toArray()); + } + } + + static class Solution { + + public List> decorateRecord(TreeNode root) { + List> res = new LinkedList<>(); + LinkedList queue = new LinkedList<>(); + queue.offer(root); + boolean reverse = false; + while (!queue.isEmpty()) { + LinkedList list = new LinkedList<>(); + int size = queue.size(); + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + if (node == null) { continue; } + if (reverse) { + list.addFirst(node.val); + } else { + list.addLast(node.val); + } + if (node.left != null) { queue.offer(node.left); } + if (node.right != null) { queue.offer(node.right); } + } + if (list.size() != 0) { + res.add(list); + } + reverse = !reverse; + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\346\234\200\345\244\247\345\261\202\345\206\205\345\205\203\347\264\240\345\222\214.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\346\234\200\345\244\247\345\261\202\345\206\205\345\205\203\347\264\240\345\222\214.java" new file mode 100644 index 0000000..d0bd78b --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/bfs/\346\234\200\345\244\247\345\261\202\345\206\205\345\205\203\347\264\240\345\222\214.java" @@ -0,0 +1,55 @@ +package io.github.dunwu.algorithm.tree.btree.bfs; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; + +/** + * 1161. 最大层内元素和 + * + * @author Zhang Peng + * @date 2025-08-18 + */ +public class 最大层内元素和 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(2, + s.maxLevelSum(TreeNode.buildTree(1, 7, 0, 7, -8, null, null))); + Assertions.assertEquals(2, + s.maxLevelSum(TreeNode.buildTree(989, null, 10250, 98693, -89388, null, null, null, -32127))); + Assertions.assertEquals(3, + s.maxLevelSum(TreeNode.buildTree(-100, -200, -300, -20, -5, -10, null))); + } + + static class Solution { + + public int maxLevelSum(TreeNode root) { + if (root == null) { return 0; } + int depth = 1; + int max = Integer.MIN_VALUE; + int maxDepth = 1; + LinkedList queue = new LinkedList<>(); + queue.offer(root); + while (!queue.isEmpty()) { + int sum = 0; + int size = queue.size(); + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + sum += node.val; + if (node.left != null) { queue.offer(node.left); } + if (node.right != null) { queue.offer(node.right); } + } + if (sum > max) { + max = sum; + maxDepth = depth; + } + depth++; + } + return maxDepth; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/dfs/\344\272\214\345\217\211\346\240\221\345\211\252\346\236\235.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/dfs/\344\272\214\345\217\211\346\240\221\345\211\252\346\236\235.java" new file mode 100644 index 0000000..ca687c2 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/dfs/\344\272\214\345\217\211\346\240\221\345\211\252\346\236\235.java" @@ -0,0 +1,40 @@ +package io.github.dunwu.algorithm.tree.btree.dfs; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +/** + * 814. 二叉树剪枝 + * + * @author Zhang Peng + * @since 2025-10-30 + */ +public class 二叉树剪枝 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(TreeNode.buildTree(1, null, 0, null, 1), + s.pruneTree(TreeNode.buildTree(1, null, 0, 0, 1))); + Assertions.assertEquals(TreeNode.buildTree(1, null, 1, null, 1), + s.pruneTree(TreeNode.buildTree(1, 0, 1, 0, 0, 0, 1))); + Assertions.assertEquals(TreeNode.buildTree(1, 1, 0, 1, 1, null, 1), + s.pruneTree(TreeNode.buildTree(1, 1, 0, 1, 1, 0, 1, 0))); + } + + static class Solution { + + public TreeNode pruneTree(TreeNode root) { + if (root == null) { return null; } + + root.left = pruneTree(root.left); + root.right = pruneTree(root.right); + + if (root.left == null && root.right == null && root.val == 0) { + return null; + } + return root; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/dfs/\344\272\214\345\217\211\346\240\221\347\232\204\344\270\255\345\272\217\351\201\215\345\216\206.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/dfs/\344\272\214\345\217\211\346\240\221\347\232\204\344\270\255\345\272\217\351\201\215\345\216\206.java" new file mode 100644 index 0000000..0572005 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/dfs/\344\272\214\345\217\211\346\240\221\347\232\204\344\270\255\345\272\217\351\201\215\345\216\206.java" @@ -0,0 +1,53 @@ +package io.github.dunwu.algorithm.tree.btree.dfs; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +import java.util.ArrayList; +import java.util.List; + +/** + * 94. 二叉树的中序遍历 + * + * @author Zhang Peng + * @since 2020-07-06 + */ +public class 二叉树的中序遍历 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertArrayEquals(new Integer[] { 1, 3, 2 }, + s.inorderTraversal(TreeNode.buildTree(1, null, 2, 3)).toArray()); + Assertions.assertArrayEquals(new Integer[] {}, + s.inorderTraversal(TreeNode.buildTree()).toArray()); + Assertions.assertArrayEquals(new Integer[] { 1 }, + s.inorderTraversal(TreeNode.buildTree(1)).toArray()); + } + + private static class Solution { + + List values; + + public List inorderTraversal(TreeNode root) { + values = new ArrayList<>(); + traverse(root); + return values; + } + + public void traverse(TreeNode root) { + if (root == null) return; + // 【前序】 + // System.out.printf("[node -> left]从节点 %s 进入节点 %s\n", root, root.left); + traverse(root.left); + // 【中序】 + // System.out.printf("\t[left -> node]从节点 %s 回到节点 %s\n", root.left, root); + // System.out.printf("\t[node -> right]从节点 %s 进入节点 %s\n", root, root.right); + values.add(root.val); + traverse(root.right); + // 【后序】 + // System.out.printf("\t[right -> node]从节点 %s 回到节点 %s\n", root.right, root); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/dfs/\344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/dfs/\344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.java" new file mode 100644 index 0000000..ba15417 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/dfs/\344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.java" @@ -0,0 +1,79 @@ +package io.github.dunwu.algorithm.tree.btree.dfs; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +import java.util.ArrayList; +import java.util.List; + +/** + * 144. 二叉树的前序遍历 + * + * @author Zhang Peng + * @date 2025-08-11 + */ +public class 二叉树的前序遍历 { + + public static void main(String[] args) { + + Solution s1 = new Solution(); + Assertions.assertArrayEquals(new Integer[] { 1, 2, 3 }, + s1.preorderTraversal(TreeNode.buildTree(1, null, 2, 3)).toArray()); + Assertions.assertArrayEquals(new Integer[] {}, + s1.preorderTraversal(TreeNode.buildTree()).toArray()); + Assertions.assertArrayEquals(new Integer[] { 1 }, + s1.preorderTraversal(TreeNode.buildTree(1)).toArray(new Integer[0])); + + Solution2 s2 = new Solution2(); + Assertions.assertArrayEquals(new Integer[] { 1, 2, 3 }, + s2.preorderTraversal(TreeNode.buildTree(1, null, 2, 3)).toArray()); + Assertions.assertArrayEquals(new Integer[] {}, + s2.preorderTraversal(TreeNode.buildTree()).toArray()); + Assertions.assertArrayEquals(new Integer[] { 1 }, + s2.preorderTraversal(TreeNode.buildTree(1)).toArray(new Integer[0])); + } + + /** + * 【分解】思路解法 + */ + private static class Solution { + + public List preorderTraversal(TreeNode root) { + List res = new ArrayList<>(); + if (root == null) { + return res; + } + // 前序遍历的结果,root.val 在第一个 + res.add(root.val); + // 利用函数定义,后面接着左子树的前序遍历结果 + res.addAll(preorderTraversal(root.left)); + // 利用函数定义,最后接着右子树的前序遍历结果 + res.addAll(preorderTraversal(root.right)); + return res; + } + + } + + /** + * 【遍历】思路解法 + */ + private static class Solution2 { + + List res; + + public List preorderTraversal(TreeNode root) { + res = new ArrayList<>(); + traverse(root); + return res; + } + + public void traverse(TreeNode root) { + if (root == null) { return; } + res.add(root.val); + traverse(root.left); + traverse(root.right); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/dfs/\344\272\214\345\217\211\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/dfs/\344\272\214\345\217\211\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206.java" new file mode 100644 index 0000000..cdbcba2 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/dfs/\344\272\214\345\217\211\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206.java" @@ -0,0 +1,55 @@ +package io.github.dunwu.algorithm.tree.btree.dfs; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +import java.util.ArrayList; +import java.util.List; + +/** + * 145. 二叉树的后序遍历 + * + * @author Zhang Peng + * @since 2020-07-06 + */ +public class 二叉树的后序遍历 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertArrayEquals(new Integer[] { 3, 2, 1 }, + s.postorderTraversal(TreeNode.buildTree(1, null, 2, 3)).toArray()); + Assertions.assertArrayEquals(new Integer[] {}, + s.postorderTraversal(TreeNode.buildTree()).toArray()); + Assertions.assertArrayEquals(new Integer[] { 1 }, + s.postorderTraversal(TreeNode.buildTree(1)).toArray()); + Assertions.assertArrayEquals(new Integer[] { 4, 6, 7, 5, 2, 9, 8, 3, 1 }, + s.postorderTraversal(TreeNode.buildTree(1, 2, 3, 4, 5, null, 8, null, null, 6, 7, 9)).toArray()); + } + + private static class Solution { + + List values = null; + + public List postorderTraversal(TreeNode root) { + values = new ArrayList<>(); + traverse(root); + return values; + } + + public void traverse(TreeNode root) { + if (root == null) return; + // 【前序】 + System.out.printf("[node -> left]从节点 %s 进入节点 %s\n", root, root.left); + traverse(root.left); + // 【中序】 + System.out.printf("\t[left -> node]从节点 %s 回到节点 %s\n", root.left, root); + System.out.printf("\t[node -> right]从节点 %s 进入节点 %s\n", root, root.right); + traverse(root.right); + // 【后序】 + System.out.printf("\t[right -> node]从节点 %s 回到节点 %s\n", root.right, root); + values.add(root.val); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/dfs/\344\272\214\345\217\211\346\240\221\347\232\204\345\235\241\345\272\246.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/dfs/\344\272\214\345\217\211\346\240\221\347\232\204\345\235\241\345\272\246.java" new file mode 100644 index 0000000..2d5500b --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/dfs/\344\272\214\345\217\211\346\240\221\347\232\204\345\235\241\345\272\246.java" @@ -0,0 +1,41 @@ +package io.github.dunwu.algorithm.tree.btree.dfs; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +/** + * 110. 平衡二叉树 + * + * @author Zhang Peng + * @since 2025-10-30 + */ +public class 二叉树的坡度 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(1, s.findTilt(TreeNode.buildTree(1, 2, 3))); + Assertions.assertEquals(15, s.findTilt(TreeNode.buildTree(4, 2, 9, 3, 5, null, 7))); + Assertions.assertEquals(9, s.findTilt(TreeNode.buildTree(21, 7, 14, 1, 1, 2, 2, 3, 3))); + } + + static class Solution { + + int res = 0; + + public int findTilt(TreeNode root) { + res = 0; + sum(root); + return res; + } + + public int sum(TreeNode root) { + if (root == null) { return 0; } + int left = sum(root.left); + int right = sum(root.right); + res += Math.abs(left - right); + return left + right + root.val; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/dfs/\345\207\272\347\216\260\346\254\241\346\225\260\346\234\200\345\244\232\347\232\204\345\255\220\346\240\221\345\205\203\347\264\240\345\222\214.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/dfs/\345\207\272\347\216\260\346\254\241\346\225\260\346\234\200\345\244\232\347\232\204\345\255\220\346\240\221\345\205\203\347\264\240\345\222\214.java" new file mode 100644 index 0000000..550c275 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/dfs/\345\207\272\347\216\260\346\254\241\346\225\260\346\234\200\345\244\232\347\232\204\345\255\220\346\240\221\345\205\203\347\264\240\345\222\214.java" @@ -0,0 +1,65 @@ +package io.github.dunwu.algorithm.tree.btree.dfs; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 508. 出现次数最多的子树元素和 + * + * @author Zhang Peng + * @since 2020-07-06 + */ +public class 出现次数最多的子树元素和 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertArrayEquals(new int[] { 2, -3, 4 }, s.findFrequentTreeSum(TreeNode.buildTree(5, 2, -3))); + Assertions.assertArrayEquals(new int[] { 2 }, s.findFrequentTreeSum(TreeNode.buildTree(5, 2, -5))); + } + + static class Solution { + + private Map map; + + public int[] findFrequentTreeSum(TreeNode root) { + map = new HashMap<>(); + sum(root); + + int max = Integer.MIN_VALUE; + List list = new ArrayList<>(); + for (Map.Entry entry : map.entrySet()) { + Integer k = entry.getKey(); + Integer v = entry.getValue(); + if (v > max) { + max = v; + list.clear(); + list.add(k); + } else if (v == max) { + list.add(k); + } + } + + int[] res = new int[list.size()]; + for (int i = 0; i < list.size(); i++) { + res[i] = list.get(i); + } + return res; + } + + public int sum(TreeNode root) { + if (root == null) { return 0; } + int left = sum(root.left); + int right = sum(root.right); + int sum = left + right + root.val; + map.put(sum, map.getOrDefault(sum, 0) + 1); + return sum; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/dfs/\345\210\240\351\231\244\347\273\231\345\256\232\345\200\274\347\232\204\345\217\266\345\255\220\350\212\202\347\202\271.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/dfs/\345\210\240\351\231\244\347\273\231\345\256\232\345\200\274\347\232\204\345\217\266\345\255\220\350\212\202\347\202\271.java" new file mode 100644 index 0000000..f273bae --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/dfs/\345\210\240\351\231\244\347\273\231\345\256\232\345\200\274\347\232\204\345\217\266\345\255\220\350\212\202\347\202\271.java" @@ -0,0 +1,38 @@ +package io.github.dunwu.algorithm.tree.btree.dfs; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +/** + * 814. 二叉树剪枝 + * + * @author Zhang Peng + * @since 2025-10-30 + */ +public class 删除给定值的叶子节点 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(TreeNode.buildTree(1, null, 3, null, 4), + s.removeLeafNodes(TreeNode.buildTree(1, 2, 3, 2, null, 2, 4), 2)); + Assertions.assertEquals(TreeNode.buildTree(1, 3, null, null, 2), + s.removeLeafNodes(TreeNode.buildTree(1, 3, 3, 3, 2), 3)); + Assertions.assertEquals(TreeNode.buildTree(1), + s.removeLeafNodes(TreeNode.buildTree(1, 2, null, 2, null, 2), 2)); + } + + static class Solution { + + public TreeNode removeLeafNodes(TreeNode root, int target) { + if (root == null) { return null; } + root.left = removeLeafNodes(root.left, target); + root.right = removeLeafNodes(root.right, target); + if (root.left == null && root.right == null && root.val == target) { + return null; + } + return root; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/dfs/\345\217\266\345\255\220\347\233\270\344\274\274\347\232\204\346\240\221.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/dfs/\345\217\266\345\255\220\347\233\270\344\274\274\347\232\204\346\240\221.java" new file mode 100644 index 0000000..e1463ce --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/dfs/\345\217\266\345\255\220\347\233\270\344\274\274\347\232\204\346\240\221.java" @@ -0,0 +1,55 @@ +package io.github.dunwu.algorithm.tree.btree.dfs; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * LCR 149. 彩灯装饰记录 I + * + * @author Zhang Peng + * @since 2025-10-28 + */ +public class 叶子相似的树 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertTrue(s.leafSimilar(TreeNode.buildTree(3, 5, 1, 6, 2, 9, 8, null, null, 7, 4), + TreeNode.buildTree(3, 5, 1, 6, 7, 4, 2, null, null, null, null, null, null, 9, 8))); + } + + static class Solution { + + public boolean leafSimilar(TreeNode root1, TreeNode root2) { + List root1Leafs = new ArrayList<>(); + List root2Leafs = new ArrayList<>(); + getLeafs(root1, root1Leafs); + getLeafs(root2, root2Leafs); + if (root1Leafs.size() != root2Leafs.size()) { + return false; + } + for (int i = 0; i < root1Leafs.size(); i++) { + if (!Objects.equals(root1Leafs.get(i), root2Leafs.get(i))) { + return false; + } + } + return true; + } + + public void getLeafs(TreeNode root, List leafs) { + if (root == null) { + return; + } + if (root.left == null && root.right == null) { + leafs.add(root.val); + } + getLeafs(root.left, leafs); + getLeafs(root.right, leafs); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/dfs/\345\271\263\350\241\241\344\272\214\345\217\211\346\240\221.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/dfs/\345\271\263\350\241\241\344\272\214\345\217\211\346\240\221.java" new file mode 100644 index 0000000..b67c5f4 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/dfs/\345\271\263\350\241\241\344\272\214\345\217\211\346\240\221.java" @@ -0,0 +1,44 @@ +package io.github.dunwu.algorithm.tree.btree.dfs; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +/** + * 110. 平衡二叉树 + * + * @author Zhang Peng + * @since 2020-07-06 + */ +public class 平衡二叉树 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertTrue(s.isBalanced(TreeNode.buildTree(3, 9, 20, null, null, 15, 7))); + Assertions.assertFalse(s.isBalanced(TreeNode.buildTree(1, 2, 2, 3, 3, null, null, 4, 4))); + Assertions.assertTrue(s.isBalanced(TreeNode.buildTree())); + } + + static class Solution { + + boolean isOk = true; + + public boolean isBalanced(TreeNode root) { + isOk = true; + maxDepth(root); + return isOk; + } + + public int maxDepth(TreeNode root) { + if (root == null) { return 0; } + if (root.left == null && root.right == null) { return 1; } + int leftDepth = maxDepth(root.left); + int rightDepth = maxDepth(root.right); + if (Math.abs(leftDepth - rightDepth) > 1) { + isOk = false; + } + return Math.max(leftDepth, rightDepth) + 1; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/divide/\344\272\214\345\217\211\346\240\221\344\270\255\347\232\204\346\234\200\345\244\247\350\267\257\345\276\204\345\222\214.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/divide/\344\272\214\345\217\211\346\240\221\344\270\255\347\232\204\346\234\200\345\244\247\350\267\257\345\276\204\345\222\214.java" new file mode 100644 index 0000000..820c810 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/divide/\344\272\214\345\217\211\346\240\221\344\270\255\347\232\204\346\234\200\345\244\247\350\267\257\345\276\204\345\222\214.java" @@ -0,0 +1,41 @@ +package io.github.dunwu.algorithm.tree.btree.divide; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +/** + * 124. 二叉树中的最大路径和 + * + * @author Zhang Peng + * @since 2020-07-06 + */ +public class 二叉树中的最大路径和 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(6, s.maxPathSum(TreeNode.buildTree(1, 2, 3))); + Assertions.assertEquals(42, s.maxPathSum(TreeNode.buildTree(-10, 9, 20, null, null, 15, 7))); + } + + static class Solution { + + int res = Integer.MIN_VALUE; + + public int maxPathSum(TreeNode root) { + if (root == null) { return 0; } + oneSideMax(root); + return res; + } + + int oneSideMax(TreeNode root) { + if (root == null) { return 0; } + int left = Math.max(oneSideMax(root.left), 0); + int right = Math.max(oneSideMax(root.right), 0); + int pathMaxSum = root.val + left + right; + res = Math.max(res, pathMaxSum); + return Math.max(left, right) + root.val; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/divide/\344\273\216\344\270\255\345\272\217\344\270\216\345\220\216\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/divide/\344\273\216\344\270\255\345\272\217\344\270\216\345\220\216\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221.java" new file mode 100644 index 0000000..b0b70bf --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/divide/\344\273\216\344\270\255\345\272\217\344\270\216\345\220\216\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221.java" @@ -0,0 +1,58 @@ +package io.github.dunwu.algorithm.tree.btree.divide; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +import java.util.HashMap; +import java.util.Map; + +/** + * 106. + * 从中序与后序遍历序列构造二叉树 + * + * @author Zhang Peng + * @date 2025-08-11 + */ +public class 从中序与后序遍历序列构造二叉树 { + + public static void main(String[] args) { + + Solution s = new Solution(); + + TreeNode output1 = s.buildTree(new int[] { 9, 3, 15, 20, 7 }, new int[] { 9, 15, 7, 20, 3 }); + Assertions.assertEquals(TreeNode.buildTree(3, 9, 20, null, null, 15, 7), output1); + + TreeNode output2 = s.buildTree(new int[] { -1 }, new int[] { -1 }); + Assertions.assertEquals(TreeNode.buildTree(-1), output2); + } + + static class Solution { + + Map map = null; + + public TreeNode buildTree(int[] inorder, int[] postorder) { + if (inorder == null || postorder == null) { return null; } + map = new HashMap<>(inorder.length); + for (int i = 0; i < inorder.length; i++) { + map.put(inorder[i], i); + } + return build(inorder, 0, inorder.length - 1, + postorder, 0, postorder.length - 1); + } + + public TreeNode build(int[] inorder, int inLow, int inHigh, + int[] postorder, int postLow, int postHigh) { + if (postLow > postHigh) { return null; } + int inMid = map.get(postorder[postHigh]); + int leftLen = inMid - inLow; + TreeNode root = new TreeNode(postorder[postHigh]); + root.left = build(inorder, inLow, inMid - 1, + postorder, postLow, postLow + leftLen - 1); + root.right = build(inorder, inMid + 1, inHigh, + postorder, postLow + leftLen, postHigh - 1); + return root; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/divide/\344\273\216\345\211\215\345\272\217\344\270\216\344\270\255\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/divide/\344\273\216\345\211\215\345\272\217\344\270\216\344\270\255\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221.java" new file mode 100644 index 0000000..af00d0e --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/divide/\344\273\216\345\211\215\345\272\217\344\270\216\344\270\255\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221.java" @@ -0,0 +1,58 @@ +package io.github.dunwu.algorithm.tree.btree.divide; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +import java.util.HashMap; +import java.util.Map; + +/** + * 105. + * 从前序与中序遍历序列构造二叉树 + * + * @author Zhang Peng + * @date 2025-08-11 + */ +public class 从前序与中序遍历序列构造二叉树 { + + public static void main(String[] args) { + + Solution s = new Solution(); + + TreeNode output1 = s.buildTree(new int[] { 3, 9, 20, 15, 7 }, new int[] { 9, 3, 15, 20, 7 }); + Assertions.assertEquals(TreeNode.buildTree(3, 9, 20, null, null, 15, 7), output1); + + TreeNode output2 = s.buildTree(new int[] { -1 }, new int[] { -1 }); + Assertions.assertEquals(TreeNode.buildTree(-1), output2); + } + + static class Solution { + + Map map = null; + + public TreeNode buildTree(int[] preorder, int[] inorder) { + if (inorder == null || preorder == null) { return null; } + map = new HashMap<>(inorder.length); + for (int i = 0; i < inorder.length; i++) { + map.put(inorder[i], i); + } + return build(preorder, 0, preorder.length - 1, + inorder, 0, inorder.length - 1); + } + + public TreeNode build(int[] preorder, int preLow, int preHigh, + int[] inorder, int inLow, int inHigh) { + if (preLow > preHigh) { return null; } + int inMid = map.get(preorder[preLow]); + int leftLen = inMid - inLow; + TreeNode root = new TreeNode(preorder[preLow]); + root.left = build(preorder, preLow + 1, preLow + leftLen, + inorder, inLow, inMid - 1); + root.right = build(preorder, preLow + leftLen + 1, preHigh, + inorder, inMid + 1, inHigh); + return root; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/divide/\345\210\240\347\202\271\346\210\220\346\236\227.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/divide/\345\210\240\347\202\271\346\210\220\346\236\227.java" new file mode 100644 index 0000000..c3a3959 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/divide/\345\210\240\347\202\271\346\210\220\346\236\227.java" @@ -0,0 +1,67 @@ +package io.github.dunwu.algorithm.tree.btree.divide; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * 1110. 删点成林 + * + * @author Zhang Peng + * @date 2025-08-18 + */ +public class 删点成林 { + + public static void main(String[] args) { + Solution s = new Solution(); + + List output = s.delNodes(TreeNode.buildTree(1, 2, 3, 4, 5, 6, 7), new int[] { 3, 5 }); + List expect = new ArrayList<>(); + expect.add(TreeNode.buildTree(1, 2, null, 4)); + expect.add(TreeNode.buildTree(6)); + expect.add(TreeNode.buildTree(7)); + for (int i = 0; i < output.size(); i++) { + Assertions.assertEquals(expect.get(i), output.get(i)); + } + + List output2 = s.delNodes(TreeNode.buildTree(1, 2, 4, null, 3), new int[] { 3 }); + List expect2 = new ArrayList<>(); + expect2.add(TreeNode.buildTree(1, 2, 4)); + for (int i = 0; i < output2.size(); i++) { + Assertions.assertEquals(expect2.get(i), output2.get(i)); + } + } + + static class Solution { + + private List res; + + public List delNodes(TreeNode root, int[] to_delete) { + res = new ArrayList<>(); + Set set = new HashSet<>(); + for (int val : to_delete) { + set.add(val); + } + delNodes(root, set, false); + return res; + } + + public TreeNode delNodes(TreeNode root, Set set, boolean hasParent) { + if (root == null) { return null; } + boolean deleted = set.contains(root.val); + if (!deleted && !hasParent) { + res.add(root); + } + + root.left = delNodes(root.left, set, !deleted); + root.right = delNodes(root.right, set, !deleted); + return deleted ? null : root; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/divide/\345\257\271\347\247\260\344\272\214\345\217\211\346\240\221.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/divide/\345\257\271\347\247\260\344\272\214\345\217\211\346\240\221.java" new file mode 100644 index 0000000..0daa7f4 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/divide/\345\257\271\347\247\260\344\272\214\345\217\211\346\240\221.java" @@ -0,0 +1,38 @@ +package io.github.dunwu.algorithm.tree.btree.divide; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +/** + * 101. 对称二叉树 + * + * @author Zhang Peng + * @date 2020-01-28 + */ +public class 对称二叉树 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertTrue(s.isSymmetric(TreeNode.buildTree(1, 2, 2, 3, 4, 4, 3))); + Assertions.assertFalse(s.isSymmetric(TreeNode.buildTree(1, 2, 2, null, 3, null, 3))); + } + + static class Solution { + + public boolean isSymmetric(TreeNode root) { + if (root == null) { return true; } + return isSymmetric(root.left, root.right); + } + + boolean isSymmetric(TreeNode left, TreeNode right) { + if (left == null && right == null) { return true; } + if (left == null || right == null) { return false; } + // 两个根节点需要相同 + if (left.val != right.val) { return false; } + // 左右子树也需要镜像对称 + return isSymmetric(left.left, right.right) && isSymmetric(left.right, right.left); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/divide/\346\211\200\346\234\211\345\217\257\350\203\275\347\232\204\347\234\237\344\272\214\345\217\211\346\240\221.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/divide/\346\211\200\346\234\211\345\217\257\350\203\275\347\232\204\347\234\237\344\272\214\345\217\211\346\240\221.java" new file mode 100644 index 0000000..e943be3 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/divide/\346\211\200\346\234\211\345\217\257\350\203\275\347\232\204\347\234\237\344\272\214\345\217\211\346\240\221.java" @@ -0,0 +1,77 @@ +package io.github.dunwu.algorithm.tree.btree.divide; + +import io.github.dunwu.algorithm.tree.TreeNode; + +import java.util.LinkedList; +import java.util.List; + +/** + * 894. 所有可能的真二叉树 + * + * @author Zhang Peng + * @date 2025-10-21 + */ +public class 所有可能的真二叉树 { + + public static void main(String[] args) { + Solution s = new Solution(); + List trees = s.allPossibleFBT(7); + trees.forEach(tree -> { + System.out.println(TreeNode.serialize(tree)); + }); + } + + static class Solution { + + // 备忘录,记录 n 个节点能够组合成的所有可能二叉树 + List[] memo; + + public List allPossibleFBT(int n) { + if (n % 2 == 0) { + // 题目描述的满二叉树不可能是偶数个节点 + return new LinkedList<>(); + } + memo = new LinkedList[n + 1]; + return build(n); + } + + // 定义:输入一个 n,生成节点树为 n 的所有可能的满二叉树 + public List build(int n) { + List res = new LinkedList<>(); + // base case + if (n == 1) { + res.add(new TreeNode(0)); + return res; + } + if (memo[n] != null) { + // 避免冗余计算 + return memo[n]; + } + + // 递归生成所有符合条件的左右子树 + for (int i = 1; i < n; i += 2) { + int j = n - i - 1; + // 利用函数定义,生成左右子树 + List leftSubTrees = build(i); + List rightSubTrees = build(j); + // 左右子树的不同排列也能构成不同的二叉树 + for (TreeNode left : leftSubTrees) { + for (TreeNode right : rightSubTrees) { + // 生成根节点 + TreeNode root = new TreeNode(0); + // 组装出一种可能的二叉树形状 + root.left = left; + root.right = right; + // 加入结果列表 + res.add(root); + } + } + } + // 存入备忘录 + memo[n] = res; + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/divide/\346\234\200\345\244\247\344\272\214\345\217\211\346\240\2212.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/divide/\346\234\200\345\244\247\344\272\214\345\217\211\346\240\2212.java" new file mode 100644 index 0000000..4cac6df --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/divide/\346\234\200\345\244\247\344\272\214\345\217\211\346\240\2212.java" @@ -0,0 +1,48 @@ +package io.github.dunwu.algorithm.tree.btree.divide; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +/** + * 998. 最大二叉树 II + * + * @author Zhang Peng + * @date 2025-08-18 + */ +public class 最大二叉树2 { + + public static void main(String[] args) { + + Solution s = new Solution(); + + Assertions.assertEquals(TreeNode.buildTree(5, 4, null, 1, 3, null, null, 2), + s.insertIntoMaxTree(TreeNode.buildTree(4, 1, 3, null, null, 2), 5)); + + Assertions.assertEquals(TreeNode.buildTree(5, 2, 4, null, 1, null, 3), + s.insertIntoMaxTree(TreeNode.buildTree(5, 2, 4, null, 1), 3)); + + Assertions.assertEquals(TreeNode.buildTree(5, 2, 4, null, 1, 3), + s.insertIntoMaxTree(TreeNode.buildTree(5, 2, 3, null, 1), 4)); + } + + static class Solution { + + public TreeNode insertIntoMaxTree(TreeNode root, int val) { + if (root == null) { return new TreeNode(val); } + if (root.val < val) { + // 如果 val 是整棵树最大的,那么原来的这棵树应该是 val 节点的左子树, + // 因为 val 节点是接在原始数组 a 的最后一个元素 + TreeNode node = new TreeNode(val); + node.left = root; + return node; + } else { + // 如果 val 不是最大的,那么就应该在右子树上, + // 因为 val 节点是接在原始数组 a 的最后一个元素 + root.right = insertIntoMaxTree(root.right, val); + return root; + } + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/divide/\346\240\271\346\215\256\345\211\215\345\272\217\345\222\214\345\220\216\345\272\217\351\201\215\345\216\206\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/divide/\346\240\271\346\215\256\345\211\215\345\272\217\345\222\214\345\220\216\345\272\217\351\201\215\345\216\206\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221.java" new file mode 100644 index 0000000..5bb6c8f --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/divide/\346\240\271\346\215\256\345\211\215\345\272\217\345\222\214\345\220\216\345\272\217\351\201\215\345\216\206\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221.java" @@ -0,0 +1,59 @@ +package io.github.dunwu.algorithm.tree.btree.divide; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +import java.util.HashMap; +import java.util.Map; + +/** + * 106. + * 从中序与后序遍历序列构造二叉树 + * + * @author Zhang Peng + * @date 2025-08-11 + */ +public class 根据前序和后序遍历构造二叉树 { + + public static void main(String[] args) { + + Solution s = new Solution(); + TreeNode output1 = s.constructFromPrePost(new int[] { 1, 2, 4, 5, 3, 6, 7 }, + new int[] { 4, 5, 2, 6, 7, 3, 1 }); + Assertions.assertEquals(TreeNode.buildTree(1, 2, 3, 4, 5, 6, 7), output1); + + TreeNode output2 = s.constructFromPrePost(new int[] { 1 }, new int[] { 1 }); + Assertions.assertEquals(TreeNode.buildTree(1), output2); + } + + static class Solution { + + Map map = new HashMap<>(); + + public TreeNode constructFromPrePost(int[] preorder, int[] postorder) { + if (preorder.length == 0 || postorder.length == 0) { return null; } + for (int i = 0; i < postorder.length; i++) { + map.put(postorder[i], i); + } + return build(preorder, 0, preorder.length - 1, + postorder, 0, postorder.length - 1); + } + + public TreeNode build(int[] preorder, int preLow, int preHigh, + int[] postorder, int postLow, int postHigh) { + if (preLow > preHigh) { return null; } + if (preLow == preHigh) { return new TreeNode(preorder[preLow]); } + int leftRootVal = preorder[preLow + 1]; + int leftPostHigh = map.get(leftRootVal); + int leftLen = leftPostHigh - postLow + 1; + TreeNode root = new TreeNode(preorder[preLow]); + root.left = build(preorder, preLow + 1, preLow + leftLen, + postorder, postLow, leftPostHigh); + root.right = build(preorder, preLow + leftLen + 1, preHigh, + postorder, leftPostHigh + 1, postHigh - 1); + return root; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/divide/\347\233\270\345\220\214\347\232\204\346\240\221.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/divide/\347\233\270\345\220\214\347\232\204\346\240\221.java" new file mode 100644 index 0000000..04205a8 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/divide/\347\233\270\345\220\214\347\232\204\346\240\221.java" @@ -0,0 +1,32 @@ +package io.github.dunwu.algorithm.tree.btree.divide; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +/** + * 100. 相同的树 + * + * @author Zhang Peng + * @date 2020-01-28 + */ +public class 相同的树 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertTrue(s.isSameTree(TreeNode.buildTree(1, 2, 3), TreeNode.buildTree(1, 2, 3))); + Assertions.assertFalse(s.isSameTree(TreeNode.buildTree(1, 2), TreeNode.buildTree(1, 2, 3))); + Assertions.assertFalse(s.isSameTree(TreeNode.buildTree(1, 2, 1), TreeNode.buildTree(1, 1, 2))); + } + + static class Solution { + + public boolean isSameTree(TreeNode p, TreeNode q) { + if (p == null && q == null) { return true; } + if (p == null || q == null) { return false; } + if (p.val != q.val) { return false; } + return isSameTree(p.left, q.left) && isSameTree(p.right, q.right); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/divide/\347\277\273\350\275\254\347\255\211\344\273\267\344\272\214\345\217\211\346\240\221.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/divide/\347\277\273\350\275\254\347\255\211\344\273\267\344\272\214\345\217\211\346\240\221.java" new file mode 100644 index 0000000..f3d1c1f --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/divide/\347\277\273\350\275\254\347\255\211\344\273\267\344\272\214\345\217\211\346\240\221.java" @@ -0,0 +1,39 @@ +package io.github.dunwu.algorithm.tree.btree.divide; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +/** + * 951. 翻转等价二叉树 + * + * @author Zhang Peng + * @date 2025-10-29 + */ +public class 翻转等价二叉树 { + + public static void main(String[] args) { + Solution s = new Solution(); + TreeNode p = TreeNode.buildTree(1, 2, 3, 4, 5, 6, null, null, null, 7, 8); + TreeNode q = TreeNode.buildTree(1, 3, 2, null, 6, 4, 5, null, null, null, null, 8, 7); + Assertions.assertTrue(s.flipEquiv(p, q)); + Assertions.assertTrue(s.flipEquiv(TreeNode.buildTree(), TreeNode.buildTree())); + Assertions.assertFalse(s.flipEquiv(TreeNode.buildTree(), TreeNode.buildTree(1))); + } + + static class Solution { + + // 定义:输入两棵二叉树,判断这两棵二叉树是否是翻转等价的 + public boolean flipEquiv(TreeNode root1, TreeNode root2) { + // 判断 root1 和 root2 两个节点是否能够匹配 + if (root1 == null && root2 == null) { return true; } + if (root1 == null || root2 == null) { return false; } + if (root1.val != root2.val) { return false; } + // 根据函数定义,判断子树是否能够匹配 + // 不翻转、翻转两种情况满足一种即可算是匹配 + return (flipEquiv(root1.left, root2.left) && flipEquiv(root1.right, root2.right)) // 不翻转子树 + || (flipEquiv(root1.left, root2.right) && flipEquiv(root1.right, root2.left)); // 翻转子树 + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/divide/\351\252\214\350\257\201\344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\345\272\217\345\210\227\345\214\226.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/divide/\351\252\214\350\257\201\344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\345\272\217\345\210\227\345\214\226.java" new file mode 100644 index 0000000..6165bf4 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/divide/\351\252\214\350\257\201\344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\345\272\217\345\210\227\345\214\226.java" @@ -0,0 +1,72 @@ +package io.github.dunwu.algorithm.tree.btree.divide; + +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; + +/** + * 331. 验证二叉树的前序序列化 + * + * @author Zhang Peng + * @date 2025-08-15 + */ +public class 验证二叉树的前序序列化 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertTrue(s.isValidSerialization("9,3,4,#,#,1,#,#,2,#,6,#,#")); + Assertions.assertFalse(s.isValidSerialization("1,#")); + Assertions.assertFalse(s.isValidSerialization("9,#,#,1")); + + Solution2 s2 = new Solution2(); + Assertions.assertTrue(s2.isValidSerialization("9,3,4,#,#,1,#,#,2,#,6,#,#")); + Assertions.assertFalse(s2.isValidSerialization("1,#")); + Assertions.assertFalse(s2.isValidSerialization("9,#,#,1")); + } + + static class Solution { + + /** + * 参考题解:https://leetcode.cn/problems/verify-preorder-serialization-of-a-binary-tree/solutions/651132/pai-an-jiao-jue-de-liang-chong-jie-fa-zh-66nt + */ + public boolean isValidSerialization(String preorder) { + LinkedList stack = new LinkedList<>(); + for (String s : preorder.split(",")) { + stack.push(s); + while (stack.size() >= 3 + && stack.get(0).equals("#") + && stack.get(1).equals("#") + && !stack.get(2).equals("#")) { + stack.pop(); + stack.pop(); + stack.pop(); + stack.push("#"); + } + } + return stack.size() == 1 && stack.pop().equals("#"); + } + + } + + static class Solution2 { + + /** + * 参考题解:https://leetcode.cn/problems/verify-preorder-serialization-of-a-binary-tree/solutions/651132/pai-an-jiao-jue-de-liang-chong-jie-fa-zh-66nt + */ + public boolean isValidSerialization(String preorder) { + int diff = 1; + for (String s : preorder.split(",")) { + diff -= 1; + if (diff < 0) { + return false; + } + if (!s.equals("#")) { + diff += 2; + } + } + return diff == 0; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/traverse/\344\272\214\345\217\211\346\240\221\344\270\255\347\232\204\344\274\252\345\233\236\346\226\207\350\267\257\345\276\204.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/traverse/\344\272\214\345\217\211\346\240\221\344\270\255\347\232\204\344\274\252\345\233\236\346\226\207\350\267\257\345\276\204.java" new file mode 100644 index 0000000..908fd91 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/traverse/\344\272\214\345\217\211\346\240\221\344\270\255\347\232\204\344\274\252\345\233\236\346\226\207\350\267\257\345\276\204.java" @@ -0,0 +1,68 @@ +package io.github.dunwu.algorithm.tree.btree.traverse; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +/** + * 1457. 二叉树中的伪回文路径 + * + * @author Zhang Peng + * @date 2025-08-15 + */ +public class 二叉树中的伪回文路径 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(2, + s.pseudoPalindromicPaths(TreeNode.buildTree(2, 3, 1, 3, 1, null, 1))); + Assertions.assertEquals(1, + s.pseudoPalindromicPaths(TreeNode.buildTree(2, 1, 1, 1, 3, null, null, null, null, null, 1))); + Assertions.assertEquals(1, + s.pseudoPalindromicPaths(TreeNode.buildTree(9))); + } + + static class Solution { + + int res = 0; + // 计数数组,题目说了 1 <= root.val <= 9 + int[] count; + + public int pseudoPalindromicPaths(TreeNode root) { + res = 0; + count = new int[10]; + traverse(root); + return res; + } + + public void traverse(TreeNode root) { + if (root == null) { return; } + + // 选择 + if (root.left == null && root.right == null) { + count[root.val]++; + int odd = 0; + for (int cnt : count) { + if (cnt % 2 != 0) { + odd++; + } + } + if (odd <= 1) { + res++; + } + count[root.val]--; + return; + } + + // 选择 + count[root.val]++; + + traverse(root.left); + traverse(root.right); + + // 取消选择 + count[root.val]--; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/traverse/\344\272\214\345\217\211\346\240\221\347\232\204\345\217\263\350\247\206\345\233\276.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/traverse/\344\272\214\345\217\211\346\240\221\347\232\204\345\217\263\350\247\206\345\233\276.java" new file mode 100644 index 0000000..58ab169 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/traverse/\344\272\214\345\217\211\346\240\221\347\232\204\345\217\263\350\247\206\345\233\276.java" @@ -0,0 +1,96 @@ +package io.github.dunwu.algorithm.tree.btree.traverse; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; + +/** + * 求根节点到叶节点数字之和 + * + * @author Zhang Peng + * @date 2025-08-15 + */ +public class 二叉树的右视图 { + + public static void main(String[] args) { + + Solution s = new Solution(); + Assertions.assertArrayEquals(new Integer[] { 1, 3, 4 }, + s.rightSideView(TreeNode.buildTree(1, 2, 3, null, 5, null, 4)).toArray()); + Assertions.assertArrayEquals(new Integer[] { 1, 3, 4, 5 }, + s.rightSideView(TreeNode.buildTree(1, 2, 3, 4, null, null, null, 5)).toArray()); + Assertions.assertArrayEquals(new Integer[] { 1, 3 }, + s.rightSideView(TreeNode.buildTree(1, null, 3)).toArray()); + Assertions.assertArrayEquals(new Integer[] {}, + s.rightSideView(TreeNode.buildTree()).toArray()); + + Solution2 s2 = new Solution2(); + Assertions.assertArrayEquals(new Integer[] { 1, 3, 4 }, + s2.rightSideView(TreeNode.buildTree(1, 2, 3, null, 5, null, 4)).toArray()); + Assertions.assertArrayEquals(new Integer[] { 1, 3, 4, 5 }, + s2.rightSideView(TreeNode.buildTree(1, 2, 3, 4, null, null, null, 5)).toArray()); + Assertions.assertArrayEquals(new Integer[] { 1, 3 }, + s2.rightSideView(TreeNode.buildTree(1, null, 3)).toArray()); + Assertions.assertArrayEquals(new Integer[] {}, + s2.rightSideView(TreeNode.buildTree()).toArray()); + + } + + // 【层序遍历】思路 + static class Solution { + + LinkedList res = null; + + public List rightSideView(TreeNode root) { + if (root == null) { return new LinkedList<>(); } + + res = new LinkedList<>(); + LinkedList queue = new LinkedList<>(); + queue.offer(root); + + while (!queue.isEmpty()) { + int size = queue.size(); + // 每层将最后一个元素加入结果集 + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + if (node.left != null) { queue.offer(node.left); } + if (node.right != null) { queue.offer(node.right); } + if (i == size - 1) { + res.add(node.val); + } + } + } + return res; + } + + } + + // 【遍历递归】思路 + static class Solution2 { + + int depth = 0; + LinkedHashMap map = null; + + public List rightSideView(TreeNode root) { + map = new LinkedHashMap<>(); + traverse(root); + return new LinkedList<>(map.values()); + } + + public void traverse(TreeNode root) { + if (root == null) { return; } + depth++; + if (!map.containsKey(depth)) { + map.put(depth, root.val); + } + traverse(root.right); + traverse(root.left); + depth--; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/traverse/\344\272\214\345\217\211\346\240\221\347\232\204\346\211\200\346\234\211\350\267\257\345\276\204.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/traverse/\344\272\214\345\217\211\346\240\221\347\232\204\346\211\200\346\234\211\350\267\257\345\276\204.java" new file mode 100644 index 0000000..8da62a1 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/traverse/\344\272\214\345\217\211\346\240\221\347\232\204\346\211\200\346\234\211\350\267\257\345\276\204.java" @@ -0,0 +1,66 @@ +package io.github.dunwu.algorithm.tree.btree.traverse; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +/** + * 二叉树的所有路径 + * + * @author Zhang Peng + * @since 2025-08-15 + */ +public class 二叉树的所有路径 { + + public static void main(String[] args) { + + Solution s = new Solution(); + Assertions.assertArrayEquals(Arrays.asList("1->2->5", "1->3").toArray(), + s.binaryTreePaths(TreeNode.buildTree(1, 2, 3, 5)).toArray()); + Assertions.assertArrayEquals(Collections.singletonList("1").toArray(), + s.binaryTreePaths(TreeNode.buildTree(1)).toArray()); + } + + static class Solution { + + LinkedList res = null; + LinkedList paths = null; + + public List binaryTreePaths(TreeNode root) { + res = new LinkedList<>(); + paths = new LinkedList<>(); + traverse(root); + return res; + } + + public void traverse(TreeNode root) { + if (root == null) { return; } + + // 选择 + paths.addLast(String.valueOf(root.val)); + if (root.left == null && root.right == null) { + res.addLast(String.join("->", paths)); + // System.out.printf("res: %s\n", JSONUtil.toJsonStr(res)); + } + + // 【前序】 + // System.out.format("root: %s -> root.left: %s\n", root.val, root.left.val); + traverse(root.left); + // 【中序】 + // System.out.format("root.left: %s -> root: %s\n", root.left.val, root.val); + // System.out.format("root: %s -> root.right: %s\n", root.val, root.right.val); + traverse(root.right); + // 【后序】 + // System.out.format("root.right: %s -> root: %s\n", root.right.val, root.val); + + // 取消选择 + paths.removeLast(); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/traverse/\344\273\216\345\217\266\347\273\223\347\202\271\345\274\200\345\247\213\347\232\204\346\234\200\345\260\217\345\255\227\347\254\246\344\270\262.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/traverse/\344\273\216\345\217\266\347\273\223\347\202\271\345\274\200\345\247\213\347\232\204\346\234\200\345\260\217\345\255\227\347\254\246\344\270\262.java" new file mode 100644 index 0000000..cabcee9 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/traverse/\344\273\216\345\217\266\347\273\223\347\202\271\345\274\200\345\247\213\347\232\204\346\234\200\345\260\217\345\255\227\347\254\246\344\270\262.java" @@ -0,0 +1,71 @@ +package io.github.dunwu.algorithm.tree.btree.traverse; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; + +/** + * 988. 从叶结点开始的最小字符串 + * + * @author Zhang Peng + * @date 2025-08-15 + */ +public class 从叶结点开始的最小字符串 { + + public static void main(String[] args) { + + Solution s = new Solution(); + Assertions.assertEquals("dba", + s.smallestFromLeaf(TreeNode.buildTree(0, 1, 2, 3, 4, 3, 4))); + Assertions.assertEquals("adz", + s.smallestFromLeaf(TreeNode.buildTree(25, 1, 3, 1, 3, 0, 2))); + Assertions.assertEquals("abc", + s.smallestFromLeaf(TreeNode.buildTree(2, 2, 1, null, 1, 0, null, 0))); + } + + static class Solution { + + String res = null; + LinkedList nodes = null; + + public String smallestFromLeaf(TreeNode root) { + res = null; + nodes = new LinkedList<>(); + traverse(root); + return res; + } + + public void traverse(TreeNode root) { + // 校验 + if (root == null) { return; } + + // 选择 + nodes.addLast(root.val); + if (root.left == null && root.right == null) { + String str = toString(nodes); + // System.out.printf("\tstr: %s\n", str); + if (res == null || str.compareTo(res) < 0) { + res = str; + } + } + + traverse(root.left); + traverse(root.right); + + // 取消选择 + nodes.removeLast(); + } + + public String toString(LinkedList nodes) { + StringBuilder sb = new StringBuilder(); + for (int node : nodes) { + char c = (char) (node + 'a'); + sb.insert(0, c); + } + return sb.toString(); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/traverse/\344\273\216\346\240\271\345\210\260\345\217\266\347\232\204\344\272\214\350\277\233\345\210\266\346\225\260\344\271\213\345\222\214.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/traverse/\344\273\216\346\240\271\345\210\260\345\217\266\347\232\204\344\272\214\350\277\233\345\210\266\346\225\260\344\271\213\345\222\214.java" new file mode 100644 index 0000000..6b45eec --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/traverse/\344\273\216\346\240\271\345\210\260\345\217\266\347\232\204\344\272\214\350\277\233\345\210\266\346\225\260\344\271\213\345\222\214.java" @@ -0,0 +1,63 @@ +package io.github.dunwu.algorithm.tree.btree.traverse; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; + +/** + * 1022. 从根到叶的二进制数之和 + * + * @author Zhang Peng + * @date 2025-08-15 + */ +public class 从根到叶的二进制数之和 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(22, s.sumRootToLeaf(TreeNode.buildTree(1, 0, 1, 0, 1, 0, 1))); + Assertions.assertEquals(0, s.sumRootToLeaf(TreeNode.buildTree(0))); + } + + static class Solution { + + int sum = 0; + LinkedList nodes; + + public int sumRootToLeaf(TreeNode root) { + sum = 0; + nodes = new LinkedList<>(); + traverse(root); + return sum; + } + + public void traverse(TreeNode node) { + // 校验 + if (node == null) return; + + // 选择 + nodes.addLast(node.val); + if (node.left == null && node.right == null) { + Integer num = toNum(nodes); + System.out.printf("\tnum: %d\n", num); + sum += num; + } + + traverse(node.left); + traverse(node.right); + + // 取消选择 + nodes.removeLast(); + } + + Integer toNum(LinkedList nodes) { + int num = 0; + for (int node : nodes) { + num = num * 2 + node; + } + return num; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/traverse/\345\234\250\344\272\214\345\217\211\346\240\221\344\270\255\345\242\236\345\212\240\344\270\200\350\241\214.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/traverse/\345\234\250\344\272\214\345\217\211\346\240\221\344\270\255\345\242\236\345\212\240\344\270\200\350\241\214.java" new file mode 100644 index 0000000..b04a5ff --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/traverse/\345\234\250\344\272\214\345\217\211\346\240\221\344\270\255\345\242\236\345\212\240\344\270\200\350\241\214.java" @@ -0,0 +1,76 @@ +package io.github.dunwu.algorithm.tree.btree.traverse; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; +import java.util.List; + +/** + * 623. 在二叉树中增加一行 + * + * @author Zhang Peng + * @date 2025-08-15 + */ +public class 在二叉树中增加一行 { + + public static void main(String[] args) { + TreeNode root = TreeNode.buildTree(4, 2, 6, 3, 1, 5); + TreeNode newRoot = new Solution().addOneRow(root, 1, 2); + List list = TreeNode.toValueList(newRoot); + Assertions.assertArrayEquals(new Integer[] { 4, 1, 1, 2, null, null, 6, 3, 1, 5 }, list.toArray()); + + TreeNode root2 = TreeNode.buildTree(4, 2, 6, 3, 1, 5); + TreeNode newRoot2 = new Solution().addOneRow(root2, 1, 1); + List list2 = TreeNode.toValueList(newRoot2); + Assertions.assertArrayEquals(new Integer[] { 1, 4, null, 2, 6, 3, 1, 5 }, list2.toArray()); + } + + static class Solution { + + public TreeNode addOneRow(TreeNode root, int val, int depth) { + if (root == null) { return root; } + if (depth == 1) { + TreeNode newRoot = new TreeNode(val); + newRoot.left = root; + return newRoot; + } + LinkedList q = new LinkedList<>(); + int level = 1; + q.offer(root); + while (!q.isEmpty()) { + + int size = q.size(); + if (level == depth - 1) { + for (int i = 0; i < size; i++) { + TreeNode node = q.poll(); + TreeNode left = node.left; + node.left = new TreeNode(val); + node.left.left = left; + + TreeNode right = node.right; + node.right = new TreeNode(val); + node.right.right = right; + } + break; + } + + // 层序遍历 + for (int i = 0; i < size; i++) { + TreeNode node = q.poll(); + if (node.left != null) { + q.offer(node.left); + } + if (node.right != null) { + q.offer(node.right); + } + } + + level++; + } + return root; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/traverse/\345\267\246\345\217\266\345\255\220\344\271\213\345\222\214.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/traverse/\345\267\246\345\217\266\345\255\220\344\271\213\345\222\214.java" new file mode 100644 index 0000000..7d24178 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/traverse/\345\267\246\345\217\266\345\255\220\344\271\213\345\222\214.java" @@ -0,0 +1,46 @@ +package io.github.dunwu.algorithm.tree.btree.traverse; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +/** + * 404. 左叶子之和 + * + * @author Zhang Peng + * @date 2025-08-15 + */ +public class 左叶子之和 { + + public static void main(String[] args) { + Assertions.assertEquals(24, + new Solution().sumOfLeftLeaves(TreeNode.buildTree(3, 9, 20, null, null, 15, 7))); + + Assertions.assertEquals(4, + new Solution().sumOfLeftLeaves(TreeNode.buildTree(1, 2, 3, 4, 5))); + + Assertions.assertEquals(0, + new Solution().sumOfLeftLeaves(TreeNode.buildTree(1))); + } + + static class Solution { + + int sum = 0; + + public int sumOfLeftLeaves(TreeNode root) { + traverse(root); + return sum; + } + + public void traverse(TreeNode root) { + if (root == null) { return; } + if (root.left != null && + root.left.left == null && root.left.right == null) { + sum += root.left.val; + } + traverse(root.left); + traverse(root.right); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/traverse/\346\261\202\346\240\271\350\212\202\347\202\271\345\210\260\345\217\266\350\212\202\347\202\271\346\225\260\345\255\227\344\271\213\345\222\214.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/traverse/\346\261\202\346\240\271\350\212\202\347\202\271\345\210\260\345\217\266\350\212\202\347\202\271\346\225\260\345\255\227\344\271\213\345\222\214.java" new file mode 100644 index 0000000..737d9b7 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/traverse/\346\261\202\346\240\271\350\212\202\347\202\271\345\210\260\345\217\266\350\212\202\347\202\271\346\225\260\345\255\227\344\271\213\345\222\214.java" @@ -0,0 +1,60 @@ +package io.github.dunwu.algorithm.tree.btree.traverse; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +import java.util.LinkedList; + +/** + * 求根节点到叶节点数字之和 + * + * @author Zhang Peng + * @date 2025-08-15 + */ +public class 求根节点到叶节点数字之和 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(25, s.sumNumbers(TreeNode.buildTree(1, 2, 3))); + Assertions.assertEquals(1026, s.sumNumbers(TreeNode.buildTree(4, 9, 0, 5, 1))); + } + + static class Solution { + + int res = 0; + LinkedList paths = null; + + public int sumNumbers(TreeNode root) { + res = 0; + paths = new LinkedList<>(); + traverse(root); + return res; + } + + public void traverse(TreeNode root) { + // 【校验】 + if (root == null) { return; } + + // 选择 + paths.addLast(root.val); + if (root.left == null && root.right == null) { + int num = 0; + for (Integer path : paths) { + num = num * 10 + path; + } + res += num; + } + + // 【前序】 + traverse(root.left); + // 【中序】 + traverse(root.right); + // 【后序】 + + // 取消选择 + paths.removeLast(); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\344\270\255\347\232\204\346\234\200\345\244\247\350\267\257\345\276\204\345\222\214.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\344\270\255\347\232\204\346\234\200\345\244\247\350\267\257\345\276\204\345\222\214.java" deleted file mode 100644 index 748279f..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\344\270\255\347\232\204\346\234\200\345\244\247\350\267\257\345\276\204\345\222\214.java" +++ /dev/null @@ -1,54 +0,0 @@ -package io.github.dunwu.algorithm.tree.btree; - -import io.github.dunwu.algorithm.tree.TreeNode; -import io.github.dunwu.algorithm.tree.TreeUtils; -import org.junit.jupiter.api.Assertions; - -/** - * @author Zhang Peng - * @see 124. 二叉树中的最大路径和 - * @since 2020-07-06 - */ -public class 二叉树中的最大路径和 { - - public static void main(String[] args) { - 二叉树中的最大路径和 demo = new 二叉树中的最大路径和(); - TreeNode tree = TreeUtils.asTree(1, 2, 3); - Assertions.assertEquals(6, demo.maxPathSum(tree)); - TreeNode tree2 = TreeUtils.asTree(-10, 9, 20, null, null, 15, 7); - Assertions.assertEquals(42, demo.maxPathSum(tree2)); - TreeNode tree3 = TreeUtils.asTree(2, -1); - Assertions.assertEquals(2, demo.maxPathSum(tree3)); - TreeNode tree4 = TreeUtils.asTree(-2, -1); - Assertions.assertEquals(-1, demo.maxPathSum(tree4)); - } - - int maxSum; - - public int maxPathSum(TreeNode root) { - maxSum = Integer.MIN_VALUE; - maxGain(root); - return maxSum; - } - - public int maxGain(TreeNode node) { - if (node == null) { - return 0; - } - - // 递归计算左右子节点的最大贡献值 - // 只有在最大贡献值大于 0 时,才会选取对应子节点 - int left = Math.max(maxGain(node.left), 0); - int right = Math.max(maxGain(node.right), 0); - - // 节点的最大路径和取决于该节点的值与该节点的左右子节点的最大贡献值 - int current = node.val + left + right; - - // 更新答案 - maxSum = Math.max(maxSum, current); - - // 返回节点的最大贡献值 - return node.val + Math.max(left, right); - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\345\261\225\345\274\200\344\270\272\351\223\276\350\241\250.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\345\261\225\345\274\200\344\270\272\351\223\276\350\241\250.java" new file mode 100644 index 0000000..7eb9b65 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\345\261\225\345\274\200\344\270\272\351\223\276\350\241\250.java" @@ -0,0 +1,61 @@ +package io.github.dunwu.algorithm.tree.btree; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +/** + * 114. 二叉树展开为链表 + * + * @author Zhang Peng + * @date 2025-08-11 + */ +public class 二叉树展开为链表 { + + public static void main(String[] args) { + + Solution s = new Solution(); + + TreeNode root = TreeNode.buildTree(1, 2, 5, 3, 4, null, 6); + s.flatten(root); + Assertions.assertArrayEquals(new Integer[] { 1, null, 2, null, 3, null, 4, null, 5, null, 6 }, + TreeNode.toValueList(root).toArray()); + + TreeNode root2 = TreeNode.buildTree(0); + s.flatten(root2); + Assertions.assertArrayEquals(new Integer[] { 0 }, TreeNode.toValueList(root2).toArray()); + + TreeNode root3 = TreeNode.buildTree(); + s.flatten(root3); + Assertions.assertArrayEquals(new Integer[] {}, TreeNode.toValueList(root3).toArray()); + } + + static class Solution { + + public void flatten(TreeNode root) { + // base case + if (root == null) { return; } + + // 利用定义,把左右子树拉平 + flatten(root.left); + flatten(root.right); + + // *** 后序遍历位置 *** + // 1、左右子树已经被拉平成一条链表 + TreeNode left = root.left; + TreeNode right = root.right; + + // 2、将左子树作为右子树 + root.left = null; + root.right = left; + + // 3、将原先的右子树接到当前右子树的末端 + TreeNode p = root; + while (p.right != null) { + p = p.right; + } + p.right = right; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\344\270\255\345\272\217\351\201\215\345\216\206.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\344\270\255\345\272\217\351\201\215\345\216\206.java" deleted file mode 100644 index fc5581f..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\344\270\255\345\272\217\351\201\215\345\216\206.java" +++ /dev/null @@ -1,28 +0,0 @@ -package io.github.dunwu.algorithm.tree.btree; - -import io.github.dunwu.algorithm.tree.TreeNode; - -import java.util.ArrayList; -import java.util.List; - -/** - * @author Zhang Peng - * @since 2020-07-06 - */ -public class 二叉树的中序遍历 { - - public static List inorderTraversal(TreeNode root) { - List list = new ArrayList<>(); - if (root == null) return list; - backtrack(root, list); - return list; - } - - public static void backtrack(TreeNode root, List list) { - if (root == null) return; - if (root.left != null) backtrack(root.left, list); - list.add(root.val); - if (root.right != null) backtrack(root.right, list); - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.java" deleted file mode 100644 index d368ddf..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.java" +++ /dev/null @@ -1,36 +0,0 @@ -package io.github.dunwu.algorithm.tree.btree; - -import io.github.dunwu.algorithm.tree.TreeNode; -import io.github.dunwu.algorithm.tree.TreeUtils; -import org.junit.jupiter.api.Assertions; - -import java.util.ArrayList; -import java.util.List; - -/** - * @author Zhang Peng - * @since 2020-07-06 - */ -public class 二叉树的前序遍历 { - - public static void main(String[] args) { - TreeNode tree = TreeUtils.buildTree(new Integer[] { 1, null, 2, 3 }); - List list = preorderTraversal(tree); - Assertions.assertArrayEquals(new Integer[] { 1, 2, 3 }, list.toArray(new Integer[0])); - } - - public static List preorderTraversal(TreeNode root) { - List list = new ArrayList<>(); - if (root == null) return list; - backtrack(root, list); - return list; - } - - public static void backtrack(TreeNode root, List list) { - if (root == null) return; - list.add(root.val); - if (root.left != null) backtrack(root.left, list); - if (root.right != null) backtrack(root.right, list); - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206.java" deleted file mode 100644 index 742464c..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206.java" +++ /dev/null @@ -1,28 +0,0 @@ -package io.github.dunwu.algorithm.tree.btree; - -import io.github.dunwu.algorithm.tree.TreeNode; - -import java.util.ArrayList; -import java.util.List; - -/** - * @author Zhang Peng - * @since 2020-07-06 - */ -public class 二叉树的后序遍历 { - - public List postorderTraversal(TreeNode root) { - List list = new ArrayList<>(); - if (root == null) return list; - backtrack(root, list); - return list; - } - - public static void backtrack(TreeNode root, List list) { - if (root == null) return; - if (root.left != null) backtrack(root.left, list); - if (root.right != null) backtrack(root.right, list); - list.add(root.val); - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\346\254\241\351\201\215\345\216\206.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\346\254\241\351\201\215\345\216\206.java" deleted file mode 100644 index c690a6a..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\346\254\241\351\201\215\345\216\206.java" +++ /dev/null @@ -1,50 +0,0 @@ -package io.github.dunwu.algorithm.tree.btree; - -import io.github.dunwu.algorithm.tree.TreeNode; -import io.github.dunwu.algorithm.tree.TreeUtils; -import org.junit.jupiter.api.Assertions; - -import java.util.*; - -/** - * @author Zhang Peng - * @see 二叉树的层次遍历 - * @since 2020-06-18 - */ -public class 二叉树的层次遍历 { - - public static void main(String[] args) { - TreeNode tree = TreeUtils.asTree(3, 9, 20, null, null, 15, 7); - List> resultList = levelOrder(tree); - List> expectList = new LinkedList<>(); - expectList.add(Arrays.asList(3)); - expectList.add(Arrays.asList(9, 20)); - expectList.add(Arrays.asList(15, 7)); - Assertions.assertArrayEquals(expectList.toArray(), resultList.toArray()); - } - - /** - * 基于 BFS 实现二叉树层次遍历。关键在于使用一个队列存储 - */ - public static List> levelOrder(TreeNode root) { - List> result = new ArrayList<>(); - if (root == null) return result; - - Queue queue = new LinkedList<>(); - queue.offer(root); - while (!queue.isEmpty()) { - int size = queue.size(); - List list = new ArrayList<>(); - for (int i = 0; i < size; i++) { - TreeNode node = queue.poll(); - if (node.left != null) queue.offer(node.left); - if (node.right != null) queue.offer(node.right); - list.add(node.val); - } - result.add(list); - } - - return result; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\346\254\241\351\201\215\345\216\2062.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\346\254\241\351\201\215\345\216\2062.java" deleted file mode 100644 index 1bc152a..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\346\254\241\351\201\215\345\216\2062.java" +++ /dev/null @@ -1,71 +0,0 @@ -package io.github.dunwu.algorithm.tree.btree; - -import io.github.dunwu.algorithm.tree.TreeNode; -import io.github.dunwu.algorithm.tree.TreeUtils; -import org.junit.jupiter.api.Assertions; - -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; - -/** - * 二叉树的层次遍历 II 算法实现 - * - *

- * 给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
- *
- * 例如:
- * 给定二叉树 [3,9,20,null,null,15,7],
- *
- *     3
- *    / \
- *   9  20
- *     /  \
- *    15   7
- * 返回其自底向上的层次遍历为:
- *
- * [
- *   [15,7],
- *   [9,20],
- *   [3]
- * ]
- * 
- * - * @see 二叉树的层次遍历 II - */ -public class 二叉树的层次遍历2 { - - public static void main(String[] args) { - TreeNode tree = TreeUtils.asTree(3, 9, 20, null, null, 15, 7); - List> resultList = levelOrderBottom(tree); - List> expectList = new LinkedList<>(); - expectList.add(Arrays.asList(15, 7)); - expectList.add(Arrays.asList(9, 20)); - expectList.add(Arrays.asList(3)); - Assertions.assertArrayEquals(expectList.toArray(), resultList.toArray()); - } - - public static List> levelOrderBottom(TreeNode root) { - List> result = new LinkedList<>(); - LinkedList queue = new LinkedList<>(); - if (root == null) return result; - queue.offer(root); - while (!queue.isEmpty()) { - int size = queue.size(); - List list = new LinkedList<>(); - for (int i = 0; i < size; i++) { - TreeNode node = queue.poll(); - if (node != null) { - list.add(node.val); - if (node.left != null) queue.add(node.left); - if (node.right != null) queue.add(node.right); - } - } - result.add(list); - } - Collections.reverse(result); - return result; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\345\272\217\345\210\227\345\214\226\344\270\216\345\217\215\345\272\217\345\210\227\345\214\226.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\345\272\217\345\210\227\345\214\226\344\270\216\345\217\215\345\272\217\345\210\227\345\214\226.java" index f194694..4eac5e3 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\345\272\217\345\210\227\345\214\226\344\270\216\345\217\215\345\272\217\345\210\227\345\214\226.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\345\272\217\345\210\227\345\214\226\344\270\216\345\217\215\345\272\217\345\210\227\345\214\226.java" @@ -1,67 +1,88 @@ package io.github.dunwu.algorithm.tree.btree; import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; -import java.util.Arrays; import java.util.LinkedList; -import java.util.List; /** + * 297. 二叉树的序列化与反序列化 + * * @author Zhang Peng * @since 2020-07-06 */ public class 二叉树的序列化与反序列化 { public static void main(String[] args) { - TreeNode tree = deserialize("[1,2,3,null,null,4,5]"); - System.out.println("args = " + serialize(tree)); - } - public static String rserialize(TreeNode root, String str) { - if (root == null) { - str += "null,"; - } else { - str += str.valueOf(root.val) + ","; - str = rserialize(root.left, str); - str = rserialize(root.right, str); - } - return str; + String input1 = "1,2,3,null,null,4,5,null,null,null,null,"; + String input2 = "null,"; + String input3 = "1,null,null,"; + String input4 = "1,2,null,null,null,"; + + Solution s = new Solution(); + Assertions.assertEquals(input1, s.serialize(s.deserialize(input1))); + Assertions.assertEquals(input2, s.serialize(s.deserialize(input2))); + Assertions.assertEquals(input3, s.serialize(s.deserialize(input3))); + Assertions.assertEquals(input4, s.serialize(s.deserialize(input4))); } - public static String serialize(TreeNode root) { - String text = rserialize(root, ""); - while (text.endsWith("null,")) { - int index = text.lastIndexOf("null,"); - text = text.substring(0, index); - } - if (text.endsWith(",")) { - text = text.substring(0, text.length() - 1); + static class Solution { + + public static final String SEP = ","; + public static final String NULL = "null"; + + // 主函数,将二叉树序列化为字符串 + public String serialize(TreeNode root) { + StringBuilder sb = new StringBuilder(); + serialize(root, sb); + return sb.toString(); } - return text; - } - public static TreeNode rdeserialize(List list) { - if (list == null || list.size() == 0) { - return null; + // 辅助函数,将二叉树存入 StringBuilder + void serialize(TreeNode root, StringBuilder sb) { + if (root == null) { + sb.append(NULL).append(SEP); + return; + } + + // ****** 前序位置 ******** + sb.append(root.val).append(SEP); + + // *********************** + + serialize(root.left, sb); + serialize(root.right, sb); } - if (list.get(0).equalsIgnoreCase("null")) { - list.remove(0); - return null; + + // 主函数,将字符串反序列化为二叉树结构 + public TreeNode deserialize(String data) { + // 将字符串转化成列表 + LinkedList nodes = new LinkedList<>(); + for (String s : data.split(SEP)) { + nodes.addLast(s); + } + return deserialize(nodes); } - TreeNode root = new TreeNode(Integer.valueOf(list.get(0))); - list.remove(0); - root.left = rdeserialize(list); - root.right = rdeserialize(list); + // 辅助函数,通过 nodes 列表构造二叉树 + TreeNode deserialize(LinkedList nodes) { + if (nodes.isEmpty()) return null; - return root; - } + // ****** 前序位置 ******** + // 列表最左侧就是根节点 + String first = nodes.removeFirst(); + if (first.equals(NULL)) return null; + TreeNode root = new TreeNode(Integer.parseInt(first)); + + // ********************* + + root.left = deserialize(nodes); + root.right = deserialize(nodes); + + return root; + } - public static TreeNode deserialize(String data) { - data = data.substring(1, data.length() - 1); - String[] nums = data.split(","); - List list = new LinkedList(Arrays.asList(nums)); - return rdeserialize(list); } } diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\346\211\200\346\234\211\350\267\257\345\276\204.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\346\211\200\346\234\211\350\267\257\345\276\204.java" deleted file mode 100644 index 483939b..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\346\211\200\346\234\211\350\267\257\345\276\204.java" +++ /dev/null @@ -1,63 +0,0 @@ -package io.github.dunwu.algorithm.tree.btree; - -import io.github.dunwu.algorithm.tree.TreeNode; -import io.github.dunwu.algorithm.tree.TreeUtils; -import org.junit.jupiter.api.Assertions; - -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; - -/** - * 二叉树的所有路径 算法实现 - * - *
- * 给定一个二叉树,返回所有从根节点到叶子节点的路径。
- *
- * 说明: 叶子节点是指没有子节点的节点。
- *
- * 示例:
- *
- * 输入:
- *
- *    1
- *  /   \
- * 2     3
- *  \
- *   5
- *
- * 输出: ["1->2->5", "1->3"]
- *
- * 解释: 所有根节点到叶子节点的路径为: 1->2->5, 1->3
- * 
- * - * @see 二叉树的所有路径 - */ -public class 二叉树的所有路径 { - - public static void main(String[] args) { - TreeNode tree = TreeUtils.asTree(1, 2, 3, 5); - System.out.println("result = " + binaryTreePaths(tree)); - Assertions.assertArrayEquals(Arrays.asList("1->2->5", "1->3").toArray(), - binaryTreePaths(tree).toArray(new String[0])); - } - - public static List binaryTreePaths(TreeNode root) { - List paths = new LinkedList<>(); - recordPath(root, "", paths); - return paths; - } - - private static void recordPath(TreeNode node, String path, List paths) { - if (node == null) return; - path += node.val; - if (node.left == null && node.right == null) { - paths.add(path); - } else { - path += "->"; - recordPath(node.left, path, paths); - recordPath(node.right, path, paths); - } - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\244\247\346\267\261\345\272\246.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\244\247\346\267\261\345\272\246.java" index fea75ee..c01f06a 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\244\247\346\267\261\345\272\246.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\244\247\346\267\261\345\272\246.java" @@ -1,55 +1,82 @@ package io.github.dunwu.algorithm.tree.btree; import io.github.dunwu.algorithm.tree.TreeNode; -import io.github.dunwu.algorithm.tree.TreeUtils; import org.junit.jupiter.api.Assertions; -import java.util.LinkedList; -import java.util.Queue; - /** - * 104. 二叉树的最大深度 算法实现 + * 104. 二叉树的最大深度 * - * @see 104. 二叉树的最大深度 + * @author Zhang Peng + * @date 2025-08-11 */ public class 二叉树的最大深度 { public static void main(String[] args) { - TreeNode tree = TreeUtils.deserialize("[3,9,20,null,null,15,7]"); - System.out.println("result = " + maxDepthInDFS(tree)); - Assertions.assertEquals(3, maxDepthInDFS(tree)); - Assertions.assertEquals(3, maxDepthInBFS(tree)); + + Solution s = new Solution(); + Assertions.assertEquals(3, s.maxDepth(TreeNode.buildTree(3, 9, 20, null, null, 15, 7))); + Assertions.assertEquals(2, s.maxDepth(TreeNode.buildTree(1, null, 2))); + + Solution2 s2 = new Solution2(); + Assertions.assertEquals(3, s2.maxDepth(TreeNode.buildTree(3, 9, 20, null, null, 15, 7))); + Assertions.assertEquals(2, s2.maxDepth(TreeNode.buildTree(1, null, 2))); } - // 基于 DFS 实现 - // 时间复杂度 O(N) - public static int maxDepthInDFS(TreeNode root) { - if (root == null) return 0; - return 1 + Math.max(maxDepthInDFS(root.left), maxDepthInDFS(root.right)); + /** + * 【分解】思路解法 + */ + static class Solution { + + public int maxDepth(TreeNode root) { + if (root == null) { return 0; } + + // 计算左右子树的最大深度 + int left = maxDepth(root.left); + int right = maxDepth(root.right); + + // 根据左右子树的最大深度推出原二叉树的最大深度 + // 整棵树的最大深度等于左右子树的最大深度取最大值, + // 然后再加上根节点自己 + return Math.max(left, right) + 1; + } + } - // 基于 BFS 实现 - // 逐层扫描,只要每层有节点,层级数+1 - // 时间复杂度 O(N) - public static int maxDepthInBFS(TreeNode root) { - - if (root == null) return 0; - - int level = 0; - Queue queue = new LinkedList<>(); - queue.offer(root); - while (!queue.isEmpty()) { - level++; - int size = queue.size(); - for (int i = 0; i < size; i++) { - TreeNode node = queue.poll(); - if (node == null) continue; - if (node.left != null) queue.add(node.left); - if (node.right != null) queue.add(node.right); + /** + * 【遍历】思路解法 + */ + static class Solution2 { + + // 记录最大深度 + int res = 0; + // 记录遍历到的节点的深度 + int depth = 0; + + public int maxDepth(TreeNode root) { + // 重置全局变量 + res = 0; + depth = 0; + traverse(root); + return res; + } + + // 遍历二叉树 + void traverse(TreeNode root) { + if (root == null) { return; } + + // 【前序遍历位置】(进入节点)增加深度 + depth++; + // 遍历到叶子节点时记录最大深度 + if (root.left == null && root.right == null) { + res = Math.max(res, depth); } + traverse(root.left); + // 【中序遍历位置】 + traverse(root.right); + // 【后序遍历位置】(离开节点)减少深度 + depth--; } - return level; } } diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\260\217\346\267\261\345\272\246.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\260\217\346\267\261\345\272\246.java" index a2333fb..38bfbb8 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\260\217\346\267\261\345\272\246.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\260\217\346\267\261\345\272\246.java" @@ -1,80 +1,36 @@ package io.github.dunwu.algorithm.tree.btree; import io.github.dunwu.algorithm.tree.TreeNode; -import io.github.dunwu.algorithm.tree.TreeUtils; import org.junit.jupiter.api.Assertions; -import java.util.LinkedList; -import java.util.Queue; - /** - * 二叉树的最小深度 算法实现 - * - *
- * 给定一个二叉树,找出其最小深度。
- *
- * 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
- *
- * 说明: 叶子节点是指没有子节点的节点。
- *
- * 示例:
+ * 二叉树的最小深度
  *
- * 给定二叉树 [3,9,20,null,null,15,7],
- *
- *     3
- *    / \
- *   9  20
- *     /  \
- *    15   7
- * 返回它的最小深度  2.
- * 
- * - * @see 二叉树的最小深度 + * @author Zhang Peng + * @date 2025-08-11 */ public class 二叉树的最小深度 { public static void main(String[] args) { - TreeNode tree = TreeUtils.asTree(3, 9, 20, null, null, 15, 7); - System.out.println("result = " + minDepthInDFS(tree)); - Assertions.assertEquals(2, minDepthInDFS(tree)); - Assertions.assertEquals(2, minDepthInBFS(tree)); - } + Solution s = new Solution(); - // 基于 DFS 实现 - // 时间复杂度 O(N) - public static int minDepthInDFS(TreeNode root) { - if (root == null) return 0; - if (root.left == null) return 1 + minDepthInDFS(root.right); - if (root.right == null) return 1 + minDepthInDFS(root.left); - return 1 + Math.min(minDepthInDFS(root.left), minDepthInDFS(root.right)); + TreeNode root = TreeNode.buildTree(3, 9, 20, null, null, 15, 7); + Assertions.assertEquals(2, s.minDepth(root)); + + TreeNode root2 = TreeNode.buildTree(2, null, 3, null, 4, null, 5, null, 6); + Assertions.assertEquals(5, s.minDepth(root2)); } - // 基于 BFS 实现 - // 时间复杂度 O(N) - public static int minDepthInBFS(TreeNode root) { - if (root == null) return 0; - int level = 0; - int min = -1; - Queue queue = new LinkedList<>(); - queue.offer(root); - while (!queue.isEmpty()) { - level++; - int size = queue.size(); - for (int i = 0; i < size; i++) { - TreeNode node = queue.poll(); - if (node.left == null && node.right == null) { - if (min == -1) { - min = level; - } else { - min = Math.min(min, level); - } - } - if (node.left != null) queue.offer(node.left); - if (node.right != null) queue.offer(node.right); - } + static class Solution { + + public int minDepth(TreeNode root) { + if (root == null) { return 0; } + int left = minDepth(root.left); + int right = minDepth(root.right); + if (root.left == null || root.right == null) { return left + right + 1; } + return Math.min(left, right) + 1; } - return min; } } diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210.java" index da64616..976393b 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210.java" @@ -1,57 +1,82 @@ package io.github.dunwu.algorithm.tree.btree; import io.github.dunwu.algorithm.tree.TreeNode; -import io.github.dunwu.algorithm.tree.TreeUtils; import org.junit.jupiter.api.Assertions; /** - * 236. 二叉树的最近公共祖先 算法实现 + * 236. 二叉树的最近公共祖先 + * 解题思路 * - * @see 236. 二叉树的最近公共祖先 - * @see 解题思路 + * @author Zhang Peng + * @date 2020-01-28 */ public class 二叉树的最近公共祖先 { public static void main(String[] args) { - TreeNode root = TreeUtils.asTree(3, 5, 1, 6, 2, 0, 8, null, null, 7, 4); - TreeNode p = TreeUtils.find(root, 5); - TreeNode q = TreeUtils.find(root, 1); - TreeNode treeNode = lowestCommonAncestor(root, p, q); - Assertions.assertNotNull(treeNode); - Assertions.assertEquals(3, treeNode.val); - System.out.println("公共祖先节点 = " + treeNode.val); - - TreeNode p2 = TreeUtils.find(root, 5); - TreeNode q2 = TreeUtils.find(root, 4); - TreeNode treeNode2 = lowestCommonAncestor(root, p2, q2); - Assertions.assertNotNull(treeNode2); - Assertions.assertEquals(5, treeNode2.val); - System.out.println("公共祖先节点 = " + treeNode2.val); + + Solution s = new Solution(); + TreeNode input = TreeNode.buildTree(6, 2, 8, 0, 4, 7, 9, null, null, 3, 5); + TreeNode output1 = s.lowestCommonAncestor(input, TreeNode.find(input, 2), TreeNode.find(input, 8)); + Assertions.assertNotNull(output1); + Assertions.assertEquals(6, output1.val); + TreeNode output2 = s.lowestCommonAncestor(input, TreeNode.find(input, 2), TreeNode.find(input, 4)); + Assertions.assertNotNull(output2); + Assertions.assertEquals(2, output2.val); + + Solution2 s2 = new Solution2(); + TreeNode input2 = TreeNode.buildTree(6, 2, 8, 0, 4, 7, 9, null, null, 3, 5); + TreeNode output3 = s2.lowestCommonAncestor(input2, TreeNode.find(input2, 2), TreeNode.find(input2, 8)); + Assertions.assertNotNull(output3); + Assertions.assertEquals(6, output3.val); + TreeNode output4 = s2.lowestCommonAncestor(input2, TreeNode.find(input2, 2), TreeNode.find(input2, 4)); + Assertions.assertNotNull(output4); + Assertions.assertEquals(2, output4.val); + } + + static class Solution { + + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + + if (root == null) return null; + if (root == p || root == q) return root; + + TreeNode left = lowestCommonAncestor(root.left, p, q); + TreeNode right = lowestCommonAncestor(root.right, p, q); + + if (left != null && right != null) { return root; } + return left != null ? left : right; + } + } - /** - * 递归方式求解 - *

- * 时间复杂度:O(N) 线性级 - *

- * 空间复杂度:O(2) 常数级 - */ - public static TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { - // 如果当前节点为空,直接返回 - // 或当前节点就是 p 或 q 其中一个,显然就是要找的最近公共祖先,直接返回 - if (root == null || root == p || root == q) return root; - - TreeNode left = lowestCommonAncestor(root.left, p, q); - TreeNode right = lowestCommonAncestor(root.right, p, q); - - if (left == null) { // p、q 都不在左子树,查找右子树 - return right; - } else if (right == null) { // p、q 都不在右子树,查找左子树 - return left; - } else { - // p、q 既不在左子树,又不在右子树,直接返回当前节点 - return root; + static class Solution2 { + + private TreeNode res = null; + + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + res = null; + return find(root, p.val, q.val); } + + TreeNode find(TreeNode root, int p, int q) { + if (root == null) { return null; } + // 如果已经找到 LCA 节点,直接返回 + if (res != null) { return res; } + + if (root.val == p || root.val == q) return root; + + TreeNode left = find(root.left, p, q); + TreeNode right = find(root.right, p, q); + + if (left != null && right != null) { + // 当前节点是 LCA 节点,记录下来 + res = root; + return res; + } + return left != null ? left : right; + } + } } diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\347\233\264\345\276\204.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\347\233\264\345\276\204.java" new file mode 100644 index 0000000..c55a9d6 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\347\233\264\345\276\204.java" @@ -0,0 +1,43 @@ +package io.github.dunwu.algorithm.tree.btree; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +/** + * 543. 二叉树的直径 + * + * @author Zhang Peng + * @date 2025-08-11 + */ +public class 二叉树的直径 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(3, s.diameterOfBinaryTree(TreeNode.buildTree(1, 2, 3, 4, 5))); + Assertions.assertEquals(1, s.diameterOfBinaryTree(TreeNode.buildTree(1, 2))); + Assertions.assertEquals(0, s.diameterOfBinaryTree(TreeNode.buildTree(1))); + Assertions.assertEquals(2, s.diameterOfBinaryTree(TreeNode.buildTree(2, 3, null, 1))); + } + + static class Solution { + + private int max = 0; + + public int diameterOfBinaryTree(TreeNode root) { + max = 0; + depth(root); + return max; + } + + public int depth(TreeNode root) { + if (root == null) { return 0; } + int left = depth(root.left); + int right = depth(root.right); + int depth = Math.max(left, right) + 1; + max = Math.max(max, left + right); + return depth; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\351\224\257\351\275\277\345\275\242\345\261\202\346\254\241\351\201\215\345\216\206.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\351\224\257\351\275\277\345\275\242\345\261\202\346\254\241\351\201\215\345\216\206.java" index 7bd0b78..1b91f8f 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\351\224\257\351\275\277\345\275\242\345\261\202\346\254\241\351\201\215\345\216\206.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\272\214\345\217\211\346\240\221\347\232\204\351\224\257\351\275\277\345\275\242\345\261\202\346\254\241\351\201\215\345\216\206.java" @@ -1,74 +1,73 @@ package io.github.dunwu.algorithm.tree.btree; import io.github.dunwu.algorithm.tree.TreeNode; -import io.github.dunwu.algorithm.tree.TreeUtils; import org.junit.jupiter.api.Assertions; -import java.util.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; /** - * 103. 二叉树的锯齿形层次遍历 算法实现 - *

- * 给定一个二叉树,返回其节点值的锯齿形层次遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。 - *

- * 例如:给定二叉树 [3,9,20,null,null,15,7], - *

- *     3
- *    / \
- *   9  20
- *     /  \
- *    15   7
- * 
- * 返回锯齿形层次遍历如下: - *
- * [
- *   [3],
- *   [20,9],
- *   [15,7]
- * ]
- * 
+ * 103. 二叉树的锯齿形层次遍历 * - * @see 103. 二叉树的锯齿形层次遍历 + * @author Zhang Peng + * @date 2025-08-18 */ public class 二叉树的锯齿形层次遍历 { public static void main(String[] args) { - TreeNode tree = TreeUtils.asTree(3, 9, 20, null, null, 15, 7); - List> resultList = zigzagLevelOrder(tree); - System.out.println(resultList); - List> expectList = new LinkedList<>(); - expectList.add(Arrays.asList(3)); - expectList.add(Arrays.asList(20, 9)); - expectList.add(Arrays.asList(15, 7)); - Assertions.assertArrayEquals(expectList.toArray(), resultList.toArray()); + + Solution s = new Solution(); + + TreeNode root = TreeNode.buildTree(3, 9, 20, null, null, 15, 7); + List> expect = new LinkedList<>(); + expect.add(Arrays.asList(3)); + expect.add(Arrays.asList(20, 9)); + expect.add(Arrays.asList(15, 7)); + Assertions.assertArrayEquals(expect.toArray(), s.zigzagLevelOrder(root).toArray()); + + TreeNode root2 = TreeNode.buildTree(1); + List> expect2 = new LinkedList<>(); + expect2.add(Arrays.asList(1)); + Assertions.assertArrayEquals(expect2.toArray(), s.zigzagLevelOrder(root2).toArray()); + + TreeNode root3 = TreeNode.buildTree(); + List> expect3 = new LinkedList<>(); + Assertions.assertArrayEquals(expect3.toArray(), s.zigzagLevelOrder(root3).toArray()); } - public static List> zigzagLevelOrder(TreeNode root) { - List> result = new LinkedList<>(); - LinkedList queue = new LinkedList<>(); - if (root == null) return result; - queue.offer(root); - boolean reverse = false; - while (!queue.isEmpty()) { - int size = queue.size(); - List list = new ArrayList<>(); - for (int i = 0; i < size; i++) { - TreeNode node = queue.poll(); - if (node != null) { - list.add(node.val); - if (node.left != null) queue.offer(node.left); - if (node.right != null) queue.offer(node.right); + static class Solution { + + public List> zigzagLevelOrder(TreeNode root) { + + if (root == null) { return new LinkedList<>(); } + + LinkedList q = new LinkedList<>(); + LinkedList> res = new LinkedList<>(); + q.offer(root); + + boolean reverse = false; + while (!q.isEmpty()) { + int size = q.size(); + List cur = new LinkedList<>(); + + for (int i = 0; i < size; i++) { + TreeNode node = q.poll(); + cur.add(node.val); + if (node.left != null) { q.offer(node.left); } + if (node.right != null) { q.offer(node.right); } } + if (reverse) { + Collections.reverse(cur); + } + res.add(cur); + reverse = !reverse; } - if (reverse) { - Collections.reverse(list); - result.add(list); - } else { - result.add(list); - } - reverse = !reverse; + + return res; } - return result; + } } diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\273\216\344\270\255\345\272\217\344\270\216\345\220\216\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\273\216\344\270\255\345\272\217\344\270\216\345\220\216\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221.java" deleted file mode 100644 index 78bf624..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\273\216\344\270\255\345\272\217\344\270\216\345\220\216\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221.java" +++ /dev/null @@ -1,71 +0,0 @@ -package io.github.dunwu.algorithm.tree.btree; - -import io.github.dunwu.algorithm.tree.TreeNode; -import io.github.dunwu.algorithm.tree.TreeUtils; -import org.junit.jupiter.api.Assertions; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * @author Zhang Peng - * @since 2020-07-07 - */ -public class 从中序与后序遍历序列构造二叉树 { - - public static void main(String[] args) { - int[] postorder = { 9, 15, 7, 20, 3 }; - int[] inorder = { 9, 3, 15, 20, 7 }; - 从中序与后序遍历序列构造二叉树 demo = new 从中序与后序遍历序列构造二叉树(); - TreeNode root = demo.buildTree(inorder, postorder); - List list = TreeUtils.toBfsValueList(root); - System.out.println(list); - Assertions.assertArrayEquals(Arrays.asList(3, 9, 20, null, null, 15, 7).toArray(), list.toArray()); - } - - private Map map; - - public TreeNode backtrack(int[] postorder, int[] inorder, int postLeft, int postRight, int inLeft, int inRight) { - if (postLeft > postRight) return null; - - // 后序遍历中的最后一个节点就是根节点 - // 在中序遍历中定位根节点 - int inRoot = map.get(postorder[postRight]); - - // 先把根节点建立出来 - TreeNode root = new TreeNode(postorder[postRight]); - - // 得到右子树中的节点数目 - int rightTreeSize = inRight - inRoot; - - // System.out.printf("左子树:postLeft = %s, postRight = %s, inLeft = %s, inRight = %s\n", - // postorder[postLeft], postorder[postRight - rightTreeSize - 1], inorder[inLeft], inorder[inRoot - 1]); - // System.out.printf("右子树:postLeft = %s, postRight = %s, inLeft = %s, inRight = %s\n", - // postorder[postRight - rightTreeSize], postorder[postRight - 1], inorder[inRoot + 1], inorder[inRight]); - - // 递归地构造左子树,并连接到根节点 - // 先序遍历中「从 左边界+1 开始的 leftTreeSize」个元素就对应了 - // 中序遍历中「从 左边界 开始到 根节点定位-1」的元素 - root.left = backtrack(postorder, inorder, postLeft, postRight - rightTreeSize - 1, inLeft, inRoot - 1); - - // 递归地构造右子树,并连接到根节点 - // 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了 - // 中序遍历中「从 根节点定位+1 到 右边界」的元素 - root.right = backtrack(postorder, inorder, postRight - rightTreeSize, postRight - 1, inRoot + 1, inRight); - - return root; - } - - public TreeNode buildTree(int[] inorder, int[] postorder) { - if (postorder == null || inorder == null) { return null;} - int n = inorder.length; - map = new HashMap<>(n); - for (int i = 0; i < n; i++) { - map.put(inorder[i], i); - } - return backtrack(postorder, inorder, 0, n - 1, 0, n - 1); - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\273\216\345\205\210\345\272\217\351\201\215\345\216\206\350\277\230\345\216\237\344\272\214\345\217\211\346\240\221.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\273\216\345\205\210\345\272\217\351\201\215\345\216\206\350\277\230\345\216\237\344\272\214\345\217\211\346\240\221.java" index 74a4935..729b3c0 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\273\216\345\205\210\345\272\217\351\201\215\345\216\206\350\277\230\345\216\237\344\272\214\345\217\211\346\240\221.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\273\216\345\205\210\345\272\217\351\201\215\345\216\206\350\277\230\345\216\237\344\272\214\345\217\211\346\240\221.java" @@ -1,7 +1,6 @@ package io.github.dunwu.algorithm.tree.btree; import io.github.dunwu.algorithm.tree.TreeNode; -import io.github.dunwu.algorithm.tree.TreeUtils; import java.util.Stack; @@ -13,7 +12,7 @@ public class 从先序遍历还原二叉树 { public static void main(String[] args) { TreeNode result = recoverFromPreorder("1-2--3--4-5--6--7"); - System.out.println(TreeUtils.toBfsList(result)); + System.out.println(TreeNode.toList(result)); } public static TreeNode recoverFromPreorder(String S) { diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\273\216\345\211\215\345\272\217\344\270\216\344\270\255\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\273\216\345\211\215\345\272\217\344\270\216\344\270\255\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221.java" deleted file mode 100644 index ec1944b..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\344\273\216\345\211\215\345\272\217\344\270\216\344\270\255\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221.java" +++ /dev/null @@ -1,69 +0,0 @@ -package io.github.dunwu.algorithm.tree.btree; - -import io.github.dunwu.algorithm.tree.TreeNode; -import io.github.dunwu.algorithm.tree.TreeUtils; -import org.junit.jupiter.api.Assertions; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * @author Zhang Peng - * @since 2020-07-07 - */ -public class 从前序与中序遍历序列构造二叉树 { - - public static void main(String[] args) { - int[] preorder = { 3, 9, 20, 15, 7 }; - int[] inorder = { 9, 3, 15, 20, 7 }; - 从前序与中序遍历序列构造二叉树 demo = new 从前序与中序遍历序列构造二叉树(); - TreeNode root = demo.buildTree(preorder, inorder); - List list = TreeUtils.toBfsValueList(root); - System.out.println(list); - Assertions.assertArrayEquals(Arrays.asList(3, 9, 20, null, null, 15, 7).toArray(), list.toArray()); - } - - // 中序遍历结构,key 是值,value 是索引 - private Map map; - - public TreeNode backtrack(int[] preorder, int preLeft, int preRight, int inLeft, int inRight) { - if (preLeft > preRight) { - return null; - } - - // 前序遍历中的第一个节点就是根节点 - // 在中序遍历中定位根节点 - int inRoot = map.get(preorder[preLeft]); - - // 先把根节点建立出来 - TreeNode root = new TreeNode(preorder[preLeft]); - - // 得到左子树中的节点数目 - int leftTreeSize = inRoot - inLeft; - - // 递归地构造左子树,并连接到根节点 - // 先序遍历中「从 左边界+1 开始的 leftTreeSize」个元素就对应了 - // 中序遍历中「从 左边界 开始到 根节点定位-1」的元素 - root.left = backtrack(preorder, preLeft + 1, preLeft + leftTreeSize, inLeft, inRoot - 1); - - // 递归地构造右子树,并连接到根节点 - // 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了 - // 中序遍历中「从 根节点定位+1 到 右边界」的元素 - root.right = backtrack(preorder, preLeft + leftTreeSize + 1, preRight, inRoot + 1, inRight); - return root; - } - - public TreeNode buildTree(int[] preorder, int[] inorder) { - if (preorder == null || inorder == null) { return null;} - int n = preorder.length; - // 构造哈希映射,帮助我们快速定位根节点 - map = new HashMap<>(n); - for (int i = 0; i < n; i++) { - map.put(inorder[i], i); - } - return backtrack(preorder, 0, n - 1, 0, n - 1); - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\345\217\266\345\255\220\347\233\270\344\274\274\347\232\204\346\240\221.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\345\217\266\345\255\220\347\233\270\344\274\274\347\232\204\346\240\221.java" index d6e0219..03dc2b9 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\345\217\266\345\255\220\347\233\270\344\274\274\347\232\204\346\240\221.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\345\217\266\345\255\220\347\233\270\344\274\274\347\232\204\346\240\221.java" @@ -1,7 +1,6 @@ package io.github.dunwu.algorithm.tree.btree; import io.github.dunwu.algorithm.tree.TreeNode; -import io.github.dunwu.algorithm.tree.TreeUtils; import org.junit.jupiter.api.Assertions; import java.util.Arrays; @@ -32,8 +31,8 @@ public class 叶子相似的树 { public static void main(String[] args) { - TreeNode tree1 = TreeUtils.asTree(3, 5, 1, 6, 2, 9, 8, null, null, 7, 4); - TreeNode tree2 = TreeUtils.asTree(3, 5, 1, 6, 7, 4, 2, null, null, null, null, null, null, 9, 8); + TreeNode tree1 = TreeNode.buildTree(3, 5, 1, 6, 2, 9, 8, null, null, 7, 4); + TreeNode tree2 = TreeNode.buildTree(3, 5, 1, 6, 7, 4, 2, null, null, null, null, null, null, 9, 8); Assertions.assertTrue(leafSimilar(tree1, tree2)); } diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\345\241\253\345\205\205\346\257\217\344\270\252\350\212\202\347\202\271\347\232\204\344\270\213\344\270\200\344\270\252\345\217\263\344\276\247\350\212\202\347\202\271\346\214\207\351\222\210.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\345\241\253\345\205\205\346\257\217\344\270\252\350\212\202\347\202\271\347\232\204\344\270\213\344\270\200\344\270\252\345\217\263\344\276\247\350\212\202\347\202\271\346\214\207\351\222\210.java" deleted file mode 100644 index 65d625a..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\345\241\253\345\205\205\346\257\217\344\270\252\350\212\202\347\202\271\347\232\204\344\270\213\344\270\200\344\270\252\345\217\263\344\276\247\350\212\202\347\202\271\346\214\207\351\222\210.java" +++ /dev/null @@ -1,61 +0,0 @@ -package io.github.dunwu.algorithm.tree.btree; - -import java.util.LinkedList; - -/** - * @author Zhang Peng - * @since 2020-07-06 - */ -public class 填充每个节点的下一个右侧节点指针 { - - public Node connect(Node root) { - if (root == null) return null; - bfs(root); - return root; - } - - /** - * 基于 BFS 实现二叉树层次遍历。关键在于使用一个队列存储 - */ - public void bfs(Node root) { - LinkedList queue = new LinkedList<>(); - queue.offer(root); - while (!queue.isEmpty()) { - int size = queue.size(); - for (int i = 1; i < size; i++) { - queue.get(i - 1).next = queue.get(i); - } - - for (int i = 0; i < size; i++) { - Node node = queue.poll(); - if (node.left != null) queue.offer(node.left); - if (node.right != null) queue.offer(node.right); - } - } - } - - private static class Node { - - public int val; - public Node left; - public Node right; - public Node next; - - public Node(int val) { this.val = val; } - - public Node(int val, Node left, Node right) { - this.val = val; - this.left = left; - this.right = right; - } - - @Override - public String toString() { - return "Node{" + - "val=" + val + - '}'; - } - - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\345\241\253\345\205\205\346\257\217\344\270\252\350\212\202\347\202\271\347\232\204\344\270\213\344\270\200\344\270\252\345\217\263\344\276\247\350\212\202\347\202\271\346\214\207\351\222\210II.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\345\241\253\345\205\205\346\257\217\344\270\252\350\212\202\347\202\271\347\232\204\344\270\213\344\270\200\344\270\252\345\217\263\344\276\247\350\212\202\347\202\271\346\214\207\351\222\210II.java" deleted file mode 100644 index 04f2e67..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\345\241\253\345\205\205\346\257\217\344\270\252\350\212\202\347\202\271\347\232\204\344\270\213\344\270\200\344\270\252\345\217\263\344\276\247\350\212\202\347\202\271\346\214\207\351\222\210II.java" +++ /dev/null @@ -1,61 +0,0 @@ -package io.github.dunwu.algorithm.tree.btree; - -import java.util.LinkedList; - -/** - * @author Zhang Peng - * @since 2020-07-06 - */ -public class 填充每个节点的下一个右侧节点指针II { - - public Node connect(Node root) { - if (root == null) return null; - bfs(root); - return root; - } - - /** - * 基于 BFS 实现二叉树层次遍历。关键在于使用一个队列存储 - */ - public void bfs(Node root) { - LinkedList queue = new LinkedList<>(); - queue.offer(root); - while (!queue.isEmpty()) { - int size = queue.size(); - for (int i = 1; i < size; i++) { - queue.get(i - 1).next = queue.get(i); - } - - for (int i = 0; i < size; i++) { - Node node = queue.poll(); - if (node.left != null) queue.offer(node.left); - if (node.right != null) queue.offer(node.right); - } - } - } - - private static class Node { - - public int val; - public Node left; - public Node right; - public Node next; - - public Node(int val) { this.val = val; } - - public Node(int val, Node left, Node right) { - this.val = val; - this.left = left; - this.right = right; - } - - @Override - public String toString() { - return "Node{" + - "val=" + val + - '}'; - } - - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\345\256\214\345\205\250\344\272\214\345\217\211\346\240\221\347\232\204\350\212\202\347\202\271\344\270\252\346\225\260.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\345\256\214\345\205\250\344\272\214\345\217\211\346\240\221\347\232\204\350\212\202\347\202\271\344\270\252\346\225\260.java" new file mode 100644 index 0000000..e6aeaec --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\345\256\214\345\205\250\344\272\214\345\217\211\346\240\221\347\232\204\350\212\202\347\202\271\344\270\252\346\225\260.java" @@ -0,0 +1,30 @@ +package io.github.dunwu.algorithm.tree.btree; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +/** + * 222. 完全二叉树的节点个数 + * + * @author Zhang Peng + * @since 2020-07-06 + */ +public class 完全二叉树的节点个数 { + + public static void main(String[] args) { + Solution s = new Solution(); + Assertions.assertEquals(6, s.countNodes(TreeNode.buildTree(1, 2, 3, 4, 5, 6))); + Assertions.assertEquals(0, s.countNodes(TreeNode.buildTree())); + Assertions.assertEquals(1, s.countNodes(TreeNode.buildTree(1))); + } + + static class Solution { + + public int countNodes(TreeNode root) { + if (root == null) { return 0; } + return countNodes(root.left) + countNodes(root.right) + 1; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\345\257\271\347\247\260\344\272\214\345\217\211\346\240\221.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\345\257\271\347\247\260\344\272\214\345\217\211\346\240\221.java" deleted file mode 100644 index a87d430..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\345\257\271\347\247\260\344\272\214\345\217\211\346\240\221.java" +++ /dev/null @@ -1,52 +0,0 @@ -package io.github.dunwu.algorithm.tree.btree; - -import io.github.dunwu.algorithm.tree.TreeNode; -import io.github.dunwu.algorithm.tree.TreeUtils; - -/** - * 101. 对称二叉树 算法实现 - *

- * 给定一个二叉树,检查它是否是镜像对称的。 - *

- * 例如,二叉树 [1,2,2,3,4,4,3] 是对称的。 - *

- *     1
- *    / \
- *   2   2
- *  / \ / \
- * 3  4 4  3
- * 
- * 但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的: - *
- *     1
- *    / \
- *   2   2
- *    \   \
- *    3    3
- * 
- * 说明:如果你可以运用递归和迭代两种方法解决这个问题,会很加分。 - * - * @see 101. 对称二叉树 - */ -public class 对称二叉树 { - - public static void main(String[] args) { - TreeNode tree = TreeUtils.asTree(1, 2, 2, 3, 4, 4, 3); - System.out.println("result = " + isSymmetric(tree)); - - tree = TreeUtils.asTree(1, 2, 2, null, 3, null, 3); - System.out.println("result = " + isSymmetric(tree)); - } - - public static boolean isSymmetric(TreeNode root) { - return isMirror(root, root); - } - - private static boolean isMirror(TreeNode tree1, TreeNode tree2) { - if (tree1 == null && tree2 == null) return true; - if (tree1 == null || tree2 == null) return false; - if (tree1.val != tree2.val) return false; - return isMirror(tree1.left, tree2.right) && isMirror(tree1.right, tree2.left); - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\345\271\263\350\241\241\344\272\214\345\217\211\346\240\221.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\345\271\263\350\241\241\344\272\214\345\217\211\346\240\221.java" deleted file mode 100644 index 1a16951..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\345\271\263\350\241\241\344\272\214\345\217\211\346\240\221.java" +++ /dev/null @@ -1,43 +0,0 @@ -package io.github.dunwu.algorithm.tree.btree; - -import io.github.dunwu.algorithm.tree.TreeNode; -import io.github.dunwu.algorithm.tree.TreeUtils; -import org.junit.jupiter.api.Assertions; - -/** - * @author Zhang Peng - * @since 2020-07-06 - */ -public class 平衡二叉树 { - - public static void main(String[] args) { - TreeNode tree = TreeUtils.asTree(3, 9, 20, null, null, 15, 7); - TreeNode tree2 = TreeUtils.asTree(1, 2, 2, 3, 3, null, null, 4, 4); - TreeNode tree3 = TreeUtils.asTree(null); - 平衡二叉树 demo = new 平衡二叉树(); - Assertions.assertTrue(demo.isBalanced(tree)); - Assertions.assertFalse(demo.isBalanced(tree2)); - Assertions.assertTrue(demo.isBalanced(tree3)); - } - - private boolean flag = true; - - public boolean isBalanced(TreeNode root) { - if (root == null) return true; - backtrack(root); - return flag; - } - - public int backtrack(TreeNode root) { - if (root == null) return 0; - if (root.left == null && root.right == null) return 1; - int left = backtrack(root.left); - int right = backtrack(root.right); - int temp = left - right; - if (temp > 1 || temp < -1) { - flag = false; - } - return Math.max(left, right) + 1; - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\346\234\200\345\244\247\344\272\214\345\217\211\346\240\221.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\346\234\200\345\244\247\344\272\214\345\217\211\346\240\221.java" new file mode 100644 index 0000000..9ac1276 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\346\234\200\345\244\247\344\272\214\345\217\211\346\240\221.java" @@ -0,0 +1,46 @@ +package io.github.dunwu.algorithm.tree.btree; + +import io.github.dunwu.algorithm.tree.TreeNode; +import org.junit.jupiter.api.Assertions; + +/** + * 654. 最大二叉树 + * + * @author Zhang Peng + * @date 2025-08-11 + */ +public class 最大二叉树 { + + public static void main(String[] args) { + Solution s = new Solution(); + TreeNode output = s.constructMaximumBinaryTree(new int[] { 3, 2, 1, 6, 0, 5 }); + Assertions.assertEquals(TreeNode.buildTree(6, 3, 5, null, 2, 0, null, null, 1), output); + TreeNode output2 = s.constructMaximumBinaryTree(new int[] { 3, 2, 1 }); + Assertions.assertEquals(TreeNode.buildTree(3, null, 2, null, 1), output2); + } + + static class Solution { + + public TreeNode constructMaximumBinaryTree(int[] nums) { + return dfs(nums, 0, nums.length - 1); + } + + public TreeNode dfs(int[] nums, int start, int end) { + if (start > end) { return null; } + int max = -1; + int maxIndex = start; + for (int i = start; i <= end; i++) { + if (nums[i] > max) { + maxIndex = i; + max = nums[i]; + } + } + TreeNode root = new TreeNode(nums[maxIndex]); + root.left = dfs(nums, start, maxIndex - 1); + root.right = dfs(nums, maxIndex + 1, end); + return root; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\347\233\270\345\220\214\347\232\204\346\240\221.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\347\233\270\345\220\214\347\232\204\346\240\221.java" deleted file mode 100644 index 405373a..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\347\233\270\345\220\214\347\232\204\346\240\221.java" +++ /dev/null @@ -1,72 +0,0 @@ -package io.github.dunwu.algorithm.tree.btree; - -import io.github.dunwu.algorithm.tree.TreeNode; -import io.github.dunwu.algorithm.tree.TreeUtils; - -/** - * 100. 相同的树 算法实现 - *

- * 给定两个二叉树,编写一个函数来检验它们是否相同。 - *

- * 如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。 - *

- * 示例 1: - *

- * 输入:       1         1
- *           / \       / \
- *          2   3     2   3
- *
- *         [1,2,3],   [1,2,3]
- *
- * 输出: true
- * 
- * 示例 2: - *
- * 输入:      1          1
- *           /           \
- *          2             2
- *
- *         [1,2],     [1,null,2]
- *
- * 输出: false
- * 
- * 示例 3: - *
- * 输入:       1         1
- *           / \       / \
- *          2   1     1   2
- *
- *         [1,2,1],   [1,1,2]
- *
- * 输出: false
- * 
- * - * @see 100. 相同的树 - */ -public class 相同的树 { - - public static void main(String[] args) { - TreeNode tree1 = TreeUtils.asTree(1, 2, 3); - TreeNode tree2 = TreeUtils.asTree(1, 2, 3); - System.out.println("result = " + isSameTree(tree1, tree2)); - - tree1 = TreeUtils.asTree(1, 2); - tree2 = TreeUtils.asTree(1, 2, 3); - System.out.println("result = " + isSameTree(tree1, tree2)); - - tree1 = TreeUtils.asTree(1, 2, 1); - tree2 = TreeUtils.asTree(1, 1, 2); - System.out.println("result = " + isSameTree(tree1, tree2)); - } - - public static boolean isSameTree(TreeNode p, TreeNode q) { - if (p == null && q == null) return true; - - if (p == null || q == null) return false; - - if (p.val != q.val) return false; - - return isSameTree(p.left, q.left) && isSameTree(p.right, q.right); - } - -} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\347\277\273\350\275\254\344\272\214\345\217\211\346\240\221.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\347\277\273\350\275\254\344\272\214\345\217\211\346\240\221.java" index 8328d06..30885c1 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\347\277\273\350\275\254\344\272\214\345\217\211\346\240\221.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\347\277\273\350\275\254\344\272\214\345\217\211\346\240\221.java" @@ -1,52 +1,79 @@ package io.github.dunwu.algorithm.tree.btree; import io.github.dunwu.algorithm.tree.TreeNode; -import io.github.dunwu.algorithm.tree.TreeUtils; +import org.junit.jupiter.api.Assertions; /** - * 翻转二叉树 算法实现 + * 226. 翻转二叉树 * - *
- * 翻转一棵二叉树。
- *
- * 示例:
- *
- * 输入:
- *
- *      4
- *    /   \
- *   2     7
- *  / \   / \
- * 1   3 6   9
- * 输出:
- *
- *      4
- *    /   \
- *   7     2
- *  / \   / \
- * 9   6 3   1
- * 备注:
- * 这个问题是受到 Max Howell 的 原问题 启发的 :
- * 
- * - * @see 翻转二叉树 + * @author Zhang Peng + * @date 2025-08-11 */ public class 翻转二叉树 { public static void main(String[] args) { - TreeNode tree = TreeUtils.asTree(4, 2, 7, 1, 3, 6, 9); - System.out.println("result = " + invertTree(tree)); + Solution s = new Solution(); + Assertions.assertEquals(TreeNode.buildTree(4, 2, 7, 1, 3, 6, 9), + s.invertTree(TreeNode.buildTree(4, 7, 2, 9, 6, 3, 1))); + Assertions.assertEquals(TreeNode.buildTree(2, 3, 1), + s.invertTree(TreeNode.buildTree(2, 1, 3))); + Assertions.assertEquals(TreeNode.buildTree(), + s.invertTree(TreeNode.buildTree())); + + Solution2 s2 = new Solution2(); + Assertions.assertEquals(TreeNode.buildTree(4, 2, 7, 1, 3, 6, 9), + s2.invertTree(TreeNode.buildTree(4, 7, 2, 9, 6, 3, 1))); + Assertions.assertEquals(TreeNode.buildTree(2, 3, 1), + s2.invertTree(TreeNode.buildTree(2, 1, 3))); + Assertions.assertEquals(TreeNode.buildTree(), + s2.invertTree(TreeNode.buildTree())); } - public static TreeNode invertTree(TreeNode root) { - if (root == null) { return null; } + /** + * 【分解】思路解法 + */ + static class Solution { + + public TreeNode invertTree(TreeNode root) { + if (root == null) { return root; } + TreeNode left = invertTree(root.left); + TreeNode right = invertTree(root.right); + root.right = left; + root.left = right; + return root; + } + + } + + /** + * 【遍历】思路解法 + */ + static class Solution2 { + + public TreeNode invertTree(TreeNode root) { + traverse(root); + return root; + } + + // 遍历二叉树 + void traverse(TreeNode root) { + if (root == null) { return; } + + // 【前序】 + // System.out.printf("[node -> left]从节点 %s 进入节点 %s\n", root, root.left); + traverse(root.left); + // 【中序】 + // System.out.printf("\t[left -> node]从节点 %s 回到节点 %s\n", root.left, root); + // System.out.printf("\t[node -> right]从节点 %s 进入节点 %s\n", root, root.right); + traverse(root.right); + // 【后序】 + // System.out.printf("\t[right -> node]从节点 %s 回到节点 %s\n", root.right, root); - TreeNode right = invertTree(root.right); - TreeNode left = invertTree(root.left); + TreeNode temp = root.left; + root.left = root.right; + root.right = temp; + } - root.left = right; - root.right = left; - return root; } } diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\350\267\257\345\276\204\346\200\273\345\222\214.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\350\267\257\345\276\204\346\200\273\345\222\214.java" index 28cc9b1..28ae963 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\350\267\257\345\276\204\346\200\273\345\222\214.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/btree/\350\267\257\345\276\204\346\200\273\345\222\214.java" @@ -1,47 +1,33 @@ package io.github.dunwu.algorithm.tree.btree; import io.github.dunwu.algorithm.tree.TreeNode; -import io.github.dunwu.algorithm.tree.TreeUtils; import org.junit.jupiter.api.Assertions; /** - * 路径总和 算法实现 + * 112. 路径总和 * - *
- * 给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。
- *
- * 说明: 叶子节点是指没有子节点的节点。
- *
- * 示例: 
- * 给定如下二叉树,以及目标和 sum = 22,
- *
- *               5
- *              / \
- *             4   8
- *            /   / \
- *           11  13  4
- *          /  \      \
- *         7    2      1
- * 返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2。
- * 
- * - * @see 112. 路径总和 + * @author Zhang Peng + * @date 2020-01-29 */ public class 路径总和 { public static void main(String[] args) { - TreeNode - tree = TreeUtils.asTree(5, 4, 8, 11, null, 13, 4, 7, 2, null, null, null, null, null, 1); - Assertions.assertTrue(hasPathSum(tree, 22)); - TreeNode tree2 = TreeUtils.asTree(1, 2); - Assertions.assertFalse(hasPathSum(tree2, 1)); + Solution s = new Solution(); + TreeNode tree = TreeNode.buildTree(5, 4, 8, 11, null, 13, 4, 7, 2, null, null, null, null, null, 1); + Assertions.assertTrue(s.hasPathSum(tree, 22)); + TreeNode tree2 = TreeNode.buildTree(1, 2); + Assertions.assertFalse(s.hasPathSum(tree2, 1)); } - public static boolean hasPathSum(TreeNode root, int sum) { - if (root == null) { return false; } - sum -= root.val; - if (root.left == null && root.right == null) { return sum == 0; } - return hasPathSum(root.left, sum) || hasPathSum(root.right, sum); + static class Solution { + + public boolean hasPathSum(TreeNode root, int targetSum) { + if (root == null) { return false; } + if (root.left == null && root.right == null && root.val == targetSum) { return true; } + return hasPathSum(root.left, targetSum - root.val) + || hasPathSum(root.right, targetSum - root.val); + } + } } diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/ntree/N\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/ntree/N\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.java" new file mode 100644 index 0000000..3f2138f --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/ntree/N\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.java" @@ -0,0 +1,47 @@ +package io.github.dunwu.algorithm.tree.ntree; + +import io.github.dunwu.algorithm.tree.Node; +import org.junit.jupiter.api.Assertions; + +import java.util.ArrayList; +import java.util.List; + +/** + * 589. N 叉树的前序遍历 + * + * @author Zhang Peng + * @date 2025-10-27 + */ +public class N叉树的前序遍历 { + + public static void main(String[] args) { + Solution s = new Solution(); + Node node3 = new Node(3); + node3.children.add(new Node(5)); + node3.children.add(new Node(6)); + Node root = new Node(1); + root.children.add(node3); + root.children.add(new Node(2)); + root.children.add(new Node(4)); + Assertions.assertArrayEquals(new Integer[] { 1, 3, 5, 6, 2, 4 }, s.preorder(root).toArray()); + } + + static class Solution { + + public List preorder(Node root) { + List res = new ArrayList<>(); + dfs(root, res); + return res; + } + + public void dfs(Node root, List res) { + if (root == null) { return; } + res.add(root.val); + for (int i = 0; i < root.children.size(); i++) { + dfs(root.children.get(i), res); + } + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/ntree/N\345\217\211\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/ntree/N\345\217\211\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206.java" new file mode 100644 index 0000000..17db4ac --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/ntree/N\345\217\211\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206.java" @@ -0,0 +1,47 @@ +package io.github.dunwu.algorithm.tree.ntree; + +import io.github.dunwu.algorithm.tree.Node; +import org.junit.jupiter.api.Assertions; + +import java.util.ArrayList; +import java.util.List; + +/** + * 589. N 叉树的前序遍历 + * + * @author Zhang Peng + * @date 2025-10-27 + */ +public class N叉树的后序遍历 { + + public static void main(String[] args) { + Solution s = new Solution(); + Node node3 = new Node(3); + node3.children.add(new Node(5)); + node3.children.add(new Node(6)); + Node root = new Node(1); + root.children.add(node3); + root.children.add(new Node(2)); + root.children.add(new Node(4)); + Assertions.assertArrayEquals(new Integer[] { 5, 6, 3, 2, 4, 1 }, s.postorder(root).toArray()); + } + + static class Solution { + + public List postorder(Node root) { + List res = new ArrayList<>(); + dfs(root, res); + return res; + } + + public void dfs(Node root, List res) { + if (root == null) { return; } + for (int i = 0; i < root.children.size(); i++) { + dfs(root.children.get(i), res); + } + res.add(root.val); + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/ntree/N\345\217\211\346\240\221\347\232\204\345\261\202\345\272\217\351\201\215\345\216\206.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/ntree/N\345\217\211\346\240\221\347\232\204\345\261\202\345\272\217\351\201\215\345\216\206.java" new file mode 100644 index 0000000..ca4152f --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/ntree/N\345\217\211\346\240\221\347\232\204\345\261\202\345\272\217\351\201\215\345\216\206.java" @@ -0,0 +1,61 @@ +package io.github.dunwu.algorithm.tree.ntree; + +import io.github.dunwu.algorithm.tree.Node; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +/** + * 429. N 叉树的层序遍历 + * + * @author Zhang Peng + * @date 2025-10-27 + */ +public class N叉树的层序遍历 { + + public static void main(String[] args) { + Solution s = new Solution(); + Node node3 = new Node(3); + node3.children.add(new Node(5)); + node3.children.add(new Node(6)); + Node root = new Node(1); + root.children.add(node3); + root.children.add(new Node(2)); + root.children.add(new Node(4)); + List> res = s.levelOrder(root); + System.out.printf("res: %s\n", res); + } + + static class Solution { + + public List> levelOrder(Node root) { + List> res = new ArrayList<>(); + LinkedList queue = new LinkedList<>(); + queue.offer(root); + + while (!queue.isEmpty()) { + int size = queue.size(); + LinkedList list = new LinkedList<>(); + for (int i = 0; i < size; i++) { + Node node = queue.poll(); + if (node == null) { + continue; + } + list.add(node.val); + if (!node.children.isEmpty()) { + for (Node child : node.children) { + queue.offer(child); + } + } + } + if (!list.isEmpty()) { + res.add(list); + } + } + return res; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/ntree/N\345\217\211\346\240\221\347\232\204\346\234\200\345\244\247\346\267\261\345\272\246.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/ntree/N\345\217\211\346\240\221\347\232\204\346\234\200\345\244\247\346\267\261\345\272\246.java" new file mode 100644 index 0000000..b4ab98a --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/ntree/N\345\217\211\346\240\221\347\232\204\346\234\200\345\244\247\346\267\261\345\272\246.java" @@ -0,0 +1,49 @@ +package io.github.dunwu.algorithm.tree.ntree; + +import io.github.dunwu.algorithm.tree.Node; +import org.junit.jupiter.api.Assertions; + +/** + * 559. N叉树的最大深度 + * + * @author Zhang Peng + * @date 2020-03-23 + */ +public class N叉树的最大深度 { + + public static void main(String[] args) { + Solution s = new Solution(); + io.github.dunwu.algorithm.tree.Node node3 = new io.github.dunwu.algorithm.tree.Node(3); + node3.children.add(new io.github.dunwu.algorithm.tree.Node(5)); + node3.children.add(new io.github.dunwu.algorithm.tree.Node(6)); + io.github.dunwu.algorithm.tree.Node root = new io.github.dunwu.algorithm.tree.Node(1); + root.children.add(node3); + root.children.add(new io.github.dunwu.algorithm.tree.Node(2)); + root.children.add(new io.github.dunwu.algorithm.tree.Node(4)); + Assertions.assertEquals(3, s.maxDepth(root)); + } + + static class Solution { + + private int max = 0; + + public int maxDepth(Node root) { + max = 0; + dfs(root); + return max; + } + + public int dfs(Node root) { + if (root == null) { return 0; } + + int depth = 0; + for (int i = 0; i < root.children.size(); i++) { + depth = Math.max(depth, dfs(root.children.get(i))); + } + max = Math.max(max, depth + 1); + return depth + 1; + } + + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/template/\344\272\214\345\217\211\346\240\221\351\201\215\345\216\206\346\241\206\346\236\266.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/template/\344\272\214\345\217\211\346\240\221\351\201\215\345\216\206\346\241\206\346\236\266.java" new file mode 100644 index 0000000..7fbb430 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/template/\344\272\214\345\217\211\346\240\221\351\201\215\345\216\206\346\241\206\346\236\266.java" @@ -0,0 +1,30 @@ +package io.github.dunwu.algorithm.tree.template; + +import io.github.dunwu.algorithm.tree.TreeNode; + +/** + * 二叉树递归遍历框架 + * + * @author Zhang Peng + * @date 2025-10-23 + */ +public class 二叉树遍历框架 { + + /** + * 二叉树的遍历框架 + */ + public void traverse(TreeNode root) { + // 【校验】 + if (root == null) { return; } + // 【前序】 + System.out.printf("[node -> left]从节点 %s 进入节点 %s\n", root, root.left); + traverse(root.left); + // 【中序】 + System.out.printf("\t[left -> node]从节点 %s 回到节点 %s\n", root.left, root); + System.out.printf("\t[node -> right]从节点 %s 进入节点 %s\n", root, root.right); + traverse(root.right); + // 【后序】 + System.out.printf("\t[right -> node]从节点 %s 回到节点 %s\n", root.right, root); + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/template/\345\244\232\345\217\211\346\240\221\351\201\215\345\216\206\346\241\206\346\236\266.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/template/\345\244\232\345\217\211\346\240\221\351\201\215\345\216\206\346\241\206\346\236\266.java" new file mode 100644 index 0000000..a8154e0 --- /dev/null +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/tree/template/\345\244\232\345\217\211\346\240\221\351\201\215\345\216\206\346\241\206\346\236\266.java" @@ -0,0 +1,97 @@ +package io.github.dunwu.algorithm.tree.template; + +import io.github.dunwu.algorithm.tree.Node; +import io.github.dunwu.algorithm.tree.State; + +import java.util.LinkedList; +import java.util.Queue; + +/** + * 多叉树遍历框架 + * + * @author Zhang Peng + * @date 2025-11-03 + */ +public class 多叉树遍历框架 { + + // 多叉树的遍历框架 + void dfs(Node root) { + // base case + if (root == null) { + return; + } + // 前序位置 + System.out.println("visit " + root.val); + for (Node child : root.children) { + dfs(child); + } + // 后序位置 + } + + void bfs(Node root) { + // base case + if (root == null) { + return; + } + Queue q = new LinkedList<>(); + q.offer(root); + while (!q.isEmpty()) { + Node node = q.poll(); + // 访问 cur 节点 + System.out.println(node.val); + + // 把 cur 的所有子节点加入队列 + for (Node child : node.children) { + q.offer(child); + } + } + } + + // 记录遍历步数的写法 + void bfs2(Node root) { + if (root == null) { + return; + } + Queue q = new LinkedList<>(); + q.offer(root); + // 记录当前遍历到的层数(根节点视为第 1 层) + int depth = 1; + + while (!q.isEmpty()) { + int size = q.size(); + for (int i = 0; i < size; i++) { + Node node = q.poll(); + // 访问 cur 节点,同时知道它所在的层数 + System.out.println("depth = " + depth + ", val = " + node.val); + + for (Node child : node.children) { + q.offer(child); + } + } + depth++; + } + } + + // 每个节点自行维护 State 类,记录深度等信息 + void bfs3(Node root) { + if (root == null) { + return; + } + Queue q = new LinkedList<>(); + // 记录当前遍历到的层数(根节点视为第 1 层) + q.offer(new State(root, 1)); + + while (!q.isEmpty()) { + State state = q.poll(); + Node node = state.node; + int depth = state.depth; + // 访问 cur 节点,同时知道它所在的层数 + System.out.println("depth = " + depth + ", val = " + node.val); + + for (Node child : node.children) { + q.offer(new State(child, depth + 1)); + } + } + } + +} diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/trie/\345\215\225\350\257\215\346\220\234\347\264\242II.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/trie/\345\215\225\350\257\215\346\220\234\347\264\242II.java" index 2f9d3d1..3ad1f6e 100644 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/trie/\345\215\225\350\257\215\346\220\234\347\264\242II.java" +++ "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/trie/\345\215\225\350\257\215\346\220\234\347\264\242II.java" @@ -2,7 +2,11 @@ import org.junit.jupiter.api.Assertions; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; /** * @author Zhang Peng diff --git a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/util/ArrayUtil.java b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/util/ArrayUtil.java index f6ff852..117d1b1 100644 --- a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/util/ArrayUtil.java +++ b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/util/ArrayUtil.java @@ -1,60 +1,98 @@ package io.github.dunwu.algorithm.util; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Random; /** + * 数组工具类 + * * @author Zhang Peng */ +@Slf4j public class ArrayUtil { - private static final Logger logger = LoggerFactory.getLogger(ArrayUtil.class); + public static int[] toIntArray(List list) { + if (list == null || list.isEmpty()) { return new int[0]; } + int[] res = new int[list.size()]; + for (int i = 0; i < list.size(); i++) { + res[i] = list.get(i); + } + return res; + } - public static void debugLogArray(T[] list, int begin, int end, String tip) { - String content = tip + getArrayString(list, begin, end); - if (logger.isDebugEnabled()) { - logger.debug(content); + public static List> toIntMatrixList(int[][] arr) { + if (arr == null || arr.length == 0) { return new ArrayList<>(); } + List> listlist = new ArrayList<>(); + for (int i = 0; i < arr.length; i++) { + List list = new ArrayList<>(); + listlist.add(list); + for (int j = 0; j < arr[i].length; j++) { + list.add(arr[i][j]); + } } + return listlist; } - public static String getArrayString(T[] list) { - return getArrayString(list, 0, list.length); + public static int[][] toIntMatrixArray(List> listlist) { + if (listlist == null || listlist.size() == 0) { return new int[0][0]; } + List arrList = new ArrayList<>(); + for (List list : listlist) { + arrList.add(toIntArray(list)); + } + return arrList.toArray(new int[listlist.size()][]); } - public static String getArrayString(T[] list, int begin, int end) { - StringBuilder sb = new StringBuilder(); - sb.append("\n"); - for (int i = 0; i < begin; i++) { - sb.append("\t"); + public static String[] toStringArray(List list) { + if (list == null || list.isEmpty()) { return new String[0]; } + String[] res = new String[list.size()]; + for (int i = 0; i < list.size(); i++) { + res[i] = list.get(i); } - int count = 0; - for (int i = begin; i <= end; i++) { - sb.append(list[i] + "\t"); - if (++count == 10) { - sb.append("\n"); - count = 0; - } + return res; + } + + public static List> toStringMatrixList(String[][] arr) { + if (arr == null || arr.length == 0) { return new ArrayList<>(); } + List> listlist = new ArrayList<>(); + for (String[] strings : arr) { + List list = new ArrayList<>(); + listlist.add(list); + Collections.addAll(list, strings); } + return listlist; + } - return sb.toString(); + public static String[][] toStringMatrixArray(List> listlist) { + if (listlist == null || listlist.size() == 0) { return new String[0][0]; } + List arrList = new ArrayList<>(); + for (List list : listlist) { + arrList.add(toStringArray(list)); + } + return arrList.toArray(new String[listlist.size()][]); + } + + public static void printArray(T[] arr, int begin, int end, String tip) { + System.out.printf("%s -> %s\n", tip, getArrayString(arr, begin, end)); + } + + public static String getArrayString(T[] arr) { + return getArrayString(arr, 0, arr.length); } - public static String getArrayString(int[] list, int begin, int end) { + public static String getArrayString(T[] arr, int begin, int end) { StringBuilder sb = new StringBuilder(); - for (int i = 0; i < begin; i++) { - sb.append("\t"); - } int count = 0; - for (int i = begin; i < end; i++) { - sb.append(list[i] + "\t"); - if (++count == 10) { + for (int i = begin; i <= end; i++) { + if (count != 0 && count % 10 == 0) { sb.append("\n"); - count = 0; } + sb.append("\t" + arr[i]); + count++; } - sb.append(list[end]); return sb.toString(); } diff --git "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/\346\213\254\345\217\267\347\224\237\346\210\220.java" "b/codes/algorithm/src/main/java/io/github/dunwu/algorithm/\346\213\254\345\217\267\347\224\237\346\210\220.java" deleted file mode 100644 index 0b79ae4..0000000 --- "a/codes/algorithm/src/main/java/io/github/dunwu/algorithm/\346\213\254\345\217\267\347\224\237\346\210\220.java" +++ /dev/null @@ -1,42 +0,0 @@ -package io.github.dunwu.algorithm; - -import org.junit.jupiter.api.Assertions; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * @author Zhang Peng - * @since 2020-07-03 - */ -public class 括号生成 { - - public static void main(String[] args) { - List list1 = Collections.singletonList("()"); - List list2 = new ArrayList<>(); - list2.add("(())"); - list2.add("()()"); - Assertions.assertArrayEquals(list1.toArray(), generateParenthesis(1).toArray()); - Assertions.assertArrayEquals(list2.toArray(), generateParenthesis(2).toArray()); - } - - public static List generateParenthesis(int n) { - List list = new ArrayList<>(); - generateOneByOne(list, 0, 0, n, ""); - return list; - } - - private static void generateOneByOne(List list, int left, int right, int n, String str) { - // 因为括号必然成对出现,所以左括号数和右括号都等于 N,即符合条件 - if (left == n && right == n) { - list.add(str); - return; - } - // 左括号数小于 N,就累加,将其 ( 加入字符串 - if (left < n) generateOneByOne(list, left + 1, right, n, str + "("); - // 右括号数小于 N 并且小于左括号数(右括号数多于左括号数,则语义不合法),就累加,将其 ) 加入字符串 - if (right < n && right < left) generateOneByOne(list, left, right + 1, n, str + ")"); - } - -} diff --git a/codes/algorithm/src/test/java/io/github/dunwu/algorithm/list/DoubleLinkListTests.java b/codes/algorithm/src/test/java/io/github/dunwu/algorithm/list/DoubleLinkListTests.java index a89cfb7..89c3766 100644 --- a/codes/algorithm/src/test/java/io/github/dunwu/algorithm/list/DoubleLinkListTests.java +++ b/codes/algorithm/src/test/java/io/github/dunwu/algorithm/list/DoubleLinkListTests.java @@ -1,5 +1,6 @@ package io.github.dunwu.algorithm.list; +import io.github.dunwu.algorithm.linkedlist.demo.DoublyLinkedList; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; diff --git a/codes/algorithm/src/test/java/io/github/dunwu/algorithm/list/SingleLinkListTests.java b/codes/algorithm/src/test/java/io/github/dunwu/algorithm/list/SingleLinkListTests.java index 06cf157..15020fd 100644 --- a/codes/algorithm/src/test/java/io/github/dunwu/algorithm/list/SingleLinkListTests.java +++ b/codes/algorithm/src/test/java/io/github/dunwu/algorithm/list/SingleLinkListTests.java @@ -1,5 +1,6 @@ package io.github.dunwu.algorithm.list; +import io.github.dunwu.algorithm.linkedlist.demo.SinglyLinkedList; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; diff --git a/codes/algorithm/src/test/java/io/github/dunwu/algorithm/map/LRUCacheTest.java b/codes/algorithm/src/test/java/io/github/dunwu/algorithm/map/LRUCacheTest.java deleted file mode 100644 index b1b3272..0000000 --- a/codes/algorithm/src/test/java/io/github/dunwu/algorithm/map/LRUCacheTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.dunwu.algorithm.map; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -/** - * @author Zhang Peng - * @since 2020-01-18 - */ -public class LRUCacheTest { - - @Test - public void test() { - LRUCache cache = new LRUCache(3); - Assertions.assertEquals(-1, cache.get(2)); - cache.put(2, 6); - Assertions.assertEquals(-1, cache.get(1)); - cache.put(1, 5); - cache.put(1, 2); - Assertions.assertEquals(2, cache.get(1)); - Assertions.assertEquals(6, cache.get(2)); - } - -} diff --git a/codes/algorithm/src/test/java/io/github/dunwu/algorithm/sort/SortStrategyTest.java b/codes/algorithm/src/test/java/io/github/dunwu/algorithm/sort/SortStrategyTest.java index d1a04f4..2783f81 100644 --- a/codes/algorithm/src/test/java/io/github/dunwu/algorithm/sort/SortStrategyTest.java +++ b/codes/algorithm/src/test/java/io/github/dunwu/algorithm/sort/SortStrategyTest.java @@ -1,6 +1,13 @@ package io.github.dunwu.algorithm.sort; -import io.github.dunwu.algorithm.sort.strategy.*; +import io.github.dunwu.algorithm.sort.strategy.BubbleSort; +import io.github.dunwu.algorithm.sort.strategy.BubbleSort2; +import io.github.dunwu.algorithm.sort.strategy.HeapSort; +import io.github.dunwu.algorithm.sort.strategy.InsertSort; +import io.github.dunwu.algorithm.sort.strategy.MergeSort; +import io.github.dunwu.algorithm.sort.strategy.QuickSort; +import io.github.dunwu.algorithm.sort.strategy.SelectionSort; +import io.github.dunwu.algorithm.sort.strategy.ShellSort; import io.github.dunwu.algorithm.util.ArrayUtil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; @@ -21,29 +28,23 @@ public class SortStrategyTest { /** * 随机样本一 */ - private static Integer[] origin01; - - private static Integer[] target01; - - private static Integer[] expected01; + private static Integer[] s1; + private static Integer[] t1; + private static Integer[] e1; /** * 随机样本二 */ - private static Integer[] origin02; - - private static Integer[] target02; - - private static Integer[] expected02; + private static Integer[] s2; + private static Integer[] t2; + private static Integer[] e2; /** * 随机样本三 */ - private static Integer[] origin03; - - private static Integer[] target03; - - private static Integer[] expected03; + private static Integer[] s3; + private static Integer[] t3; + private static Integer[] e3; /** * 生成随机数组样本,并调用 JDK api 生成期望的有序数组 @@ -51,19 +52,31 @@ public class SortStrategyTest { @BeforeAll public static void beforeClass() { // 在 [0, 100] 间生成长度为 10 的存在重复的随机数组 - origin01 = ArrayUtil.randomRepeatIntegerArray(0, 10, 9); - expected01 = Arrays.copyOf(origin01, origin01.length); - Arrays.sort(expected01); + s1 = ArrayUtil.randomRepeatIntegerArray(0, 10, 5); + e1 = Arrays.copyOf(s1, s1.length); + Arrays.sort(e1); // 在 [0, 100] 间生成长度为 17 的不重复的随机数组 - origin02 = ArrayUtil.randomNoRepeatIntegerArray(0, 100, 17); - expected02 = Arrays.copyOf(origin02, origin02.length); - Arrays.sort(expected02); + s2 = ArrayUtil.randomNoRepeatIntegerArray(0, 100, 10); + e2 = Arrays.copyOf(s2, s2.length); + Arrays.sort(e2); // 在 [0, 100] 间生成长度为 100 的不重复的随机数组 - origin03 = ArrayUtil.randomNoRepeatIntegerArray(0, 100, 100); - expected03 = Arrays.copyOf(origin03, origin03.length); - Arrays.sort(expected03); + s3 = ArrayUtil.randomNoRepeatIntegerArray(0, 100, 30); + e3 = Arrays.copyOf(s3, s3.length); + Arrays.sort(e3); + } + + /** + * 注入 SortStrategy,执行对三个样本的排序测试 + */ + private void executeSort(SortStrategy strategy) { + strategy.sort(t1); + Assertions.assertArrayEquals(e1, t1); + strategy.sort(t2); + Assertions.assertArrayEquals(e2, t2); + strategy.sort(t3); + Assertions.assertArrayEquals(e3, t3); } /** @@ -71,9 +84,9 @@ public static void beforeClass() { */ @BeforeEach public void before() { - target01 = Arrays.copyOf(origin01, origin01.length); - target02 = Arrays.copyOf(origin02, origin02.length); - target03 = Arrays.copyOf(origin03, origin03.length); + t1 = Arrays.copyOf(s1, s1.length); + t2 = Arrays.copyOf(s2, s2.length); + t3 = Arrays.copyOf(s3, s3.length); } @Test @@ -82,18 +95,6 @@ public void testBubbleSort() { executeSort(strategy); } - /** - * 注入 SortStrategy,执行对三个样本的排序测试 - */ - private void executeSort(SortStrategy strategy) { - strategy.sort(target01); - Assertions.assertArrayEquals(expected01, target01); - strategy.sort(target02); - Assertions.assertArrayEquals(expected02, target02); - strategy.sort(target03); - Assertions.assertArrayEquals(expected03, target03); - } - @Test public void testBubbleSort2() { SortStrategy strategy = new SortStrategy(new BubbleSort2()); diff --git a/codes/algorithm/src/test/java/io/github/dunwu/algorithm/str/StringAlgorithmTest.java b/codes/algorithm/src/test/java/io/github/dunwu/algorithm/str/StringAlgorithmTest.java deleted file mode 100644 index b68a837..0000000 --- a/codes/algorithm/src/test/java/io/github/dunwu/algorithm/str/StringAlgorithmTest.java +++ /dev/null @@ -1,63 +0,0 @@ -package io.github.dunwu.algorithm.str; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -/** - * @author Zhang Peng - * @since 2020-05-12 - */ -public class StringAlgorithmTest { - - @Test - public void isUniqueTest() { - Assertions.assertTrue(StringAlgorithm.isUnique("")); - Assertions.assertTrue(StringAlgorithm.isUnique("abc")); - Assertions.assertFalse(StringAlgorithm.isUnique("leetcode")); - } - - @Test - public void checkPermutationTest() { - Assertions.assertTrue(StringAlgorithm.checkPermutation("abc", "bca")); - Assertions.assertFalse(StringAlgorithm.checkPermutation("abc", "bad")); - Assertions.assertFalse(StringAlgorithm.checkPermutation( - "krqdpwdvgfuogtobtyylexrebrwzynzlpkotoqmokfpqeibbhzdjcwpgprzpqersmmdxdmwssfbfwmmvrxkjyjteirloxpbiopso", - "pyymgxtdqzqxxkmirptmbewjobpslwkbmmzfbwzmltowevsofkydrejdpcoripjlewoqzgusvypotrdkepbqspxdmoyrfnyrbrof")); - Assertions.assertFalse(StringAlgorithm.checkPermutation( - "jzvthzihsvghjhbrpfhdwixmyaxjrdzfvnhpmyrbqjpdffykqgahgzpjwvouurr", - "hhqhxjyrghjjsmduaxppwrqkikqnfdrzjowapehtbyrgrfyprrfrebzduxvvhhu")); - } - - @Test - public void replaceSpacesTest() { - Assertions.assertEquals("Mr%20John%20Smith", StringAlgorithm.replaceSpaces("Mr John Smith ", 13)); - } - - @Test - public void canPermutePalindromeTest() { - Assertions.assertTrue(StringAlgorithm.canPermutePalindrome("tactcoa")); - } - - @Test - public void oneEditAwayTest() { - Assertions.assertTrue(StringAlgorithm.oneEditAway("pale", "ple")); - Assertions.assertFalse(StringAlgorithm.oneEditAway("pales", "pal")); - } - - @Test - public void compressStringTest() { - Assertions.assertEquals("a2b1c5a3", StringAlgorithm.compressString("aabcccccaaa")); - Assertions.assertEquals("abbccd", StringAlgorithm.compressString("abbccd")); - } - - @Test - public void isFlipedStringTest() { - Assertions.assertTrue(StringAlgorithm.isFlipedString("waterbottle", "erbottlewat")); - Assertions.assertTrue(StringAlgorithm.isFlipedString("ab", "ba")); - Assertions.assertFalse(StringAlgorithm.isFlipedString("aa", "aba")); - Assertions.assertTrue(StringAlgorithm.isFlipedString( - "LmMoLrUxaeSgUhqFsicojxzsbbBobkzkigNyzreunviUECVpPaWKUTMfgskTiirzDkLQFQcTmvdeKpeAvypDMTJTGZRcGOlbJDVFlXNmORIJjhhGyMdGnGpefjLinqwESmCexewgloibkZxeTydRnyFRyDPMyPumFjhjuGnNmCKhOnWPYfUBmlppeBcqhoggzPqXcpYRYIuFbCuxpbaScUGYZWZVQxnOChBPIbozFagLalTGGjjAJJnPiMPcMJMxlYfBJBqtejxqXqgHgcoLGqqIaORJrEQMbqlbzddjOWJOyFkksYvdxUBPbzYVrxMKOYPigNwtXWGBqtXxUOCuIpGigRErIkkYbdDKuXmHoIXXWIiisBLwWqdKporlfbcHZicevgpAUOkeiFIeaSoaeXlXGfHFzImzCYunHleAEkzfmAirrMeUGczkBfzHBnrrBoiXVXELXMjOEXkQYJWkzRfTMeAyEKJstkUAuwkywhpDreIfrwQJLWLdAmaSlCLKdGcCjbaPPyHcGNbskyZgRahgieOqmztbakPRBOUHLRSfquGjUtlfbRJfMlOiFKFQcYDJaMgOGlnEQmHtaYDRpcGamZlZqLrAIDdMRPvelfBykZWNCCspjmLPczMQuTSxviiLtOMvdmLXDsffTAYMeVQReYRGoNVViteGksLdYyWLOxTpwqknfysBLASGgPoyGSRsAdXIXmKuWYIUjZeeRKfarTRTFlQDwvzFPFpcEMCxRMWyuySyMPCFFmAnCONFxTsIzQIMhApfVifcxlUTXdCKEKdDeJCfPnmRXTsNoCllqjRMtXBISwfeUMzeLwQwgQbbXvMzGctiBIQciIljKIkzMmnedtLCxaVnfBXEimIlrBqmsvWDEIoWiUFSMxeVgzqkqFJLdywojNkLwWVkIrzneqSPIsIPvwNaXbUfpxegkVhhIKOAdCtmmesmyYGhfJtqadOsGQIBuTOZxHINAMwKuBWikjwEdFDTksuICVTCDHEqvrUWOpgNONVkNkERATbHBonoAbjPFkthfgOTffCzgQrcaEYEkKyFNbBNmwljQFeKIyEPSiHZhlFUaEdYGDcYJgIYpwrevmFKEoqNtRhKSFrQNRJNkNOqjgKCMGYCCbDMMgerlvjanGrovokGfUUWDINbGCsPNINPBFygvDoynpxDZHQVbsXLFNjKmuFlYOhstBGdTsNSgsZVRXUQKrLqSTMCMHobsTZGVYOfGxkVPGbtaiAJostQbRctXJgysaWQuSfvYxwidQuLbkxIaJXIzbWvNDphIbeYBRBGlChLmxVpTezcjYmBkBslMqkEaPYrLROcZrxXiEDiBuLRcqlyNZAfVTlQawPVcqxadDcWKGXuDJUBxsRqoXXqqkzchsqgrguPpJDXrcrbXLIfmucbgWaIwqxwZNyuKJrqcEisRpVeuEdznTJsbQimFLfUQriOYYqaychChJogAZjvLjuDPONzyNzGnnaFyhCVLsxTmRMVVOpfhQqgPArwwOSOIdeiroBBhWUFWvTsdRZYWzTrXpRIWGFgKMaODDnOtLMZmzwxOJfoBlTmngxpBhsDYzUqpMtpknioFZeSBYqiSAVkZqifBQyqYuFoOPUIqdTWtQsotGLhVWKhesGgTVJPXmXqUeFpIpFeVVyOUmJxjiQfppSUKLdwNSoWVPIdzHXbIQvzyhiTUGomjTcVWgxSXyEznahWANjwhalYNnchohNAkAXWkWIVdiKPaAPnkWprRIHmqWfnLnOgUfNhThPmwJymURQHejsnqgdWKRUJZqEwfVnSDMyuHWKTPoNNnTrQhytdYWBsiKfAqWUfgjGQyxpQsRZxuUyaXerBDsJANuEhpNNejoaXrkVDqGrkQMbTogtdHsOHrhmIZajoMZjNwYGlBXrkTOphhvNWArIeyEMYrEkGofZIKQkaMyTLrpuWpjClEsbCEVjmwCEPmdDfFELazsIDopLmsrmXgEROgitmYUDqWqrpNtnhtEcxKsccAYKlhFGtzqwLSxgfXCQykyUEpIqpoVwPizirScwwQSbnhfBDYzVriWYpKZhxLwrHBtrcXsiJgTvRRNIkwJfIRfqZramufpeCQxMTZAhGrQLelrJQhScdqPyKUdNVZTCMdwZzydnInkQgyOiMxkAGqJfKakOOwsbNvIffJxFuGbtIyonefNHCCPAonrPaZihkEeMGZyTembSLsBpLeERBFFwnPhrTXTVvoNRTOwDwIKoBrMAGwPvhHOWlVkZcvSIjjUTuArbxnjkCnqmyQpIxqMqLlXxTKMztlQFxUDWhbpYCexaSyVvtGfwbCMcZgovtHslmazhQJTNQpmomjPYzrRiPGpodFHTiFSjQijeXEeBUEVaggRjTdAyMViqcwKDkUxXtSXuXOKRkYHTgZKyRqBJAcmmhXVyiBvceeOyfGauHXnnPAWOrNylLbPBbuxRfVTwOXJxQslgmldRKAICHRgOxvuaAPOtgDYBWFXABExfUyvuuxpMBWHFSyWCLzWcKQfntdciWKBfLTxYxWtVVYoNiJbFOawEEJChUCEoWLkXQCjEnXmOYOBTnXNxgCBcyKUuftmPyQgByuuDSOUMSbQFjuYOrQmLRVqYODLxhJyuhJnoM", - "xzsbbBobkzkigNyzreunviUECVpPaWKUTMfgskTiirzDkLQFQcTmvdeKpeAvypDMTJTGZRcGOlbJDVFlXNmORIJjhhGyMdGnGpefjLinqwESmCexewgloibkZxeTydRnyFRyDPMyPumFjhjuGnNmCKhOnWPYfUBmlppeBcqhoggzPqXcpYRYIuFbCuxpbaScUGYZWZVQxnOChBPIbozFagLalTGGjjAJJnPiMPcMJMxlYfBJBqtejxqXqgHgcoLGqqIaORJrEQMbqlbzddjOWJOyFkksYvdxUBPbzYVrxMKOYPigNwtXWGBqtXxUOCuIpGigRErIkkYbdDKuXmHoIXXWIiisBLwWqdKporlfbcHZicevgpAUOkeiFIeaSoaeXlXGfHFzImzCYunHleAEkzfmAirrMeUGczkBfzHBnrrBoiXVXELXMjOEXkQYJWkzRfTMeAyEKJstkUAuwkywhpDreIfrwQJLWLdAmaSlCLKdGcCjbaPPyHcGNbskyZgRahgieOqmztbakPRBOUHLRSfquGjUtlfbRJfMlOiFKFQcYDJaMgOGlnEQmHtaYDRpcGamZlZqLrAIDdMRPvelfBykZWNCCspjmLPczMQuTSxviiLtOMvdmLXDsffTAYMeVQReYRGoNVViteGksLdYyWLOxTpwqknfysBLASGgPoyGSRsAdXIXmKuWYIUjZeeRKfarTRTFlQDwvzFPFpcEMCxRMWyuySyMPCFFmAnCONFxTsIzQIMhApfVifcxlUTXdCKEKdDeJCfPnmRXTsNoCllqjRMtXBISwfeUMzeLwQwgQbbXvMzGctiBIQciIljKIkzMmnedtLCxaVnfBXEimIlrBqmsvWDEIoWiUFSMxeVgzqkqFJLdywojNkLwWVkIrzneqSPIsIPvwNaXbUfpxegkVhhIKOAdCtmmesmyYGhfJtqadOsGQIBuTOZxHINAMwKuBWikjwEdFDTksuICVTCDHEqvrUWOpgNONVkNkERATbHBonoAbjPFkthfgOTffCzgQrcaEYEkKyFNbBNmwljQFeKIyEPSiHZhlFUaEdYGDcYJgIYpwrevmFKEoqNtRhKSFrQNRJNkNOqjgKCMGYCCbDMMgerlvjanGrovokGfUUWDINbGCsPNINPBFygvDoynpxDZHQVbsXLFNjKmuFlYOhstBGdTsNSgsZVRXUQKrLqSTMCMHobsTZGVYOfGxkVPGbtaiAJostQbRctXJgysaWQuSfvYxwidQuLbkxIaJXIzbWvNDphIbeYBRBGlChLmxVpTezcjYmBkBslMqkEaPYrLROcZrxXiEDiBuLRcqlyNZAfVTlQawPVcqxadDcWKGXuDJUBxsRqoXXqqkzchsqgrguPpJDXrcrbXLIfmucbgWaIwqxwZNyuKJrqcEisRpVeuEdznTJsbQimFLfUQriOYYqaychChJogAZjvLjuDPONzyNzGnnaFyhCVLsxTmRMVVOpfhQqgPArwwOSOIdeiroBBhWUFWvTsdRZYWzTrXpRIWGFgKMaODDnOtLMZmzwxOJfoBlTmngxpBhsDYzUqpMtpknioFZeSBYqiSAVkZqifBQyqYuFoOPUIqdTWtQsotGLhVWKhesGgTVJPXmXqUeFpIpFeVVyOUmJxjiQfppSUKLdwNSoWVPIdzHXbIQvzyhiTUGomjTcVWgxSXyEznahWANjwhalYNnchohNAkAXWkWIVdiKPaAPnkWprRIHmqWfnLnOgUfNhThPmwJymURQHejsnqgdWKRUJZqEwfVnSDMyuHWKTPoNNnTrQhytdYWBsiKfAqWUfgjGQyxpQsRZxuUyaXerBDsJANuEhpNNejoaXrkVDqGrkQMbTogtdHsOHrhmIZajoMZjNwYGlBXrkTOphhvNWArIeyEMYrEkGofZIKQkaMyTLrpuWpjClEsbCEVjmwCEPmdDfFELazsIDopLmsrmXgEROgitmYUDqWqrpNtnhtEcxKsccAYKlhFGtzqwLSxgfXCQykyUEpIqpoVwPizirScwwQSbnhfBDYzVriWYpKZhxLwrHBtrcXsiJgTvRRNIkwJfIRfqZramufpeCQxMTZAhGrQLelrJQhScdqPyKUdNVZTCMdwZzydnInkQgyOiMxkAGqJfKakOOwsbNvIffJxFuGbtIyonefNHCCPAonrPaZihkEeMGZyTembSLsBpLeERBFFwnPhrTXTVvoNRTOwDwIKoBrMAGwPvhHOWlVkZcvSIjjUTuArbxnjkCnqmyQpIxqMqLlXxTKMztlQFxUDWhbpYCexaSyVvtGfwbCMcZgovtHslmazhQJTNQpmomjPYzrRiPGpodFHTiFSjQijeXEeBUEVaggRjTdAyMViqcwKDkUxXtSXuXOKRkYHTgZKyRqBJAcmmhXVyiBvceeOyfGauHXnnPAWOrNylLbPBbuxRfVTwOXJxQslgmldRKAICHRgOxvuaAPOtgDYBWFXABExfUyvuuxpMBWHFSyWCLzWcKQfntdciWKBfLTxYxWtVVYoNiJbFOawEEJChUCEoWLkXQCjEnXmOYOBTnXNxgCBcyKUuftmPyQgByuuDSOUMSbQFjuYOrQmLRVqYODLxhJyuhJnoMLmMoLrUxaeSgUhqFsicoj")); - } - -} diff --git a/codes/algorithm/src/test/java/io/github/dunwu/algorithm/string/StringAlgorithmTest.java b/codes/algorithm/src/test/java/io/github/dunwu/algorithm/string/StringAlgorithmTest.java index dcb56d4..7aa1f30 100644 --- a/codes/algorithm/src/test/java/io/github/dunwu/algorithm/string/StringAlgorithmTest.java +++ b/codes/algorithm/src/test/java/io/github/dunwu/algorithm/string/StringAlgorithmTest.java @@ -1,5 +1,6 @@ package io.github.dunwu.algorithm.string; +import io.github.dunwu.algorithm.str.StringAlgorithm; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; diff --git a/codes/algorithm/src/test/java/io/github/dunwu/algorithm/tree/BTreeDemoTests.java b/codes/algorithm/src/test/java/io/github/dunwu/algorithm/tree/BTreeDemoTests.java deleted file mode 100644 index 72c3450..0000000 --- a/codes/algorithm/src/test/java/io/github/dunwu/algorithm/tree/BTreeDemoTests.java +++ /dev/null @@ -1,75 +0,0 @@ -package io.github.dunwu.algorithm.tree; - -import org.junit.jupiter.api.Test; - -/** - * @author Zhang Peng - * @since 2020-01-18 - */ -public class BTreeDemoTests { - - public IntBTree.TreeNode initBTree() { - IntBTree.TreeNode root = new IntBTree.TreeNode(3); - root.left = new IntBTree.TreeNode(9); - root.right = new IntBTree.TreeNode(20); - root.left.left = null; - root.left.right = null; - root.right.left = new IntBTree.TreeNode(15); - root.right.right = new IntBTree.TreeNode(17); - return root; - } - - @Test - public void preOrderTest() { - IntBTree.TreeNode root = new IntBTree.TreeNode(3); - root.left = new IntBTree.TreeNode(9); - root.right = new IntBTree.TreeNode(20); - root.left.left = null; - root.left.right = null; - root.right.left = new IntBTree.TreeNode(15); - root.right.right = new IntBTree.TreeNode(17); - IntBTree.preOrder(root); - System.out.println(); - IntBTree.preOrder2(root); - System.out.println(); - } - - @Test - public void inOrderTest() { - IntBTree.TreeNode root = new IntBTree.TreeNode(3); - root.left = new IntBTree.TreeNode(9); - root.right = new IntBTree.TreeNode(20); - root.left.left = null; - root.left.right = null; - root.right.left = new IntBTree.TreeNode(15); - root.right.right = new IntBTree.TreeNode(17); - IntBTree.inOrder(root); - System.out.println(); - IntBTree.inOrder2(root); - System.out.println(); - } - - @Test - public void postOrderTest() { - IntBTree.TreeNode root = initBTree(); - IntBTree.postOrder(root); - System.out.println(); - IntBTree.postOrder2(root); - System.out.println(); - } - - @Test - public void levelTraverseTest() { - IntBTree.TreeNode root = initBTree(); - IntBTree.levelTraverse(root); - System.out.println(); - } - - @Test - public void depthOrderTraverseTest() { - IntBTree.TreeNode root = initBTree(); - IntBTree.depthOrderTraverse(root); - System.out.println(); - } - -} diff --git a/codes/algorithm/src/test/java/io/github/dunwu/algorithm/tree/BTreeTests.java b/codes/algorithm/src/test/java/io/github/dunwu/algorithm/tree/BTreeTests.java index 9605eed..f53c916 100644 --- a/codes/algorithm/src/test/java/io/github/dunwu/algorithm/tree/BTreeTests.java +++ b/codes/algorithm/src/test/java/io/github/dunwu/algorithm/tree/BTreeTests.java @@ -18,36 +18,36 @@ public class BTreeTests { @Test @DisplayName("二叉树的最大深度") public void maxDepthTest() { - BTree tree = BTree.buildTree(1, 2, 3, 4, 5); + BTree tree = BTree.build(1, 2, 3, 4, 5); Assertions.assertEquals(3, tree.maxDepth()); } @Test @DisplayName("二叉树的最小深度") public void minDepthTest() { - BTree tree = BTree.buildTree(3, 9, 20, null, null, 15, 7); + BTree tree = BTree.build(3, 9, 20, null, null, 15, 7); Assertions.assertEquals(2, tree.minDepth()); - tree = BTree.buildTree(1, 2); + tree = BTree.build(1, 2); Assertions.assertEquals(2, tree.minDepth()); } @Test @DisplayName("判断两颗二叉树是否完全一致") public void isEqualsTest() { - BTree tree1 = BTree.buildTree(1, 2, 3); - BTree tree2 = BTree.buildTree(1, 2, 3); + BTree tree1 = BTree.build(1, 2, 3); + BTree tree2 = BTree.build(1, 2, 3); Assertions.assertTrue(BTree.isEquals(tree1, tree2)); - tree1 = BTree.buildTree(1, 2, 1); - tree2 = BTree.buildTree(1, 1, 2); + tree1 = BTree.build(1, 2, 1); + tree2 = BTree.build(1, 1, 2); Assertions.assertFalse(BTree.isEquals(tree1, tree2)); } @Test @DisplayName("广度优先搜索(BFS)") public void levelOrderBottomTest() { - BTree tree = BTree.buildTree(3, 9, 20, null, null, 15, 7); + BTree tree = BTree.build(3, 9, 20, null, null, 15, 7); List> lists = new ArrayList<>(); lists.add(Collections.singletonList(3)); lists.add(Arrays.asList(9, 20)); @@ -58,8 +58,8 @@ public void levelOrderBottomTest() { @Test @DisplayName("判断两颗二叉树的叶子节点是否相似") public void isLeafSimilarTest() { - BTree tree1 = BTree.buildTree(3, 5, 1, 6, 2, 9, 8, null, null, 7, 4); - BTree tree2 = BTree.buildTree(3, 5, 1, 6, 7, 4, 2, null, null, null, null, null, null, 9, 8); + BTree tree1 = BTree.build(3, 5, 1, 6, 2, 9, 8, null, null, 7, 4); + BTree tree2 = BTree.build(3, 5, 1, 6, 7, 4, 2, null, null, null, null, null, null, 9, 8); Assertions.assertTrue(BTree.isLeafSimilar(tree1, tree2)); } diff --git a/codes/algorithm/src/test/java/io/github/dunwu/algorithm/tree/BinaryTreeTests.java b/codes/algorithm/src/test/java/io/github/dunwu/algorithm/tree/BinaryTreeTests.java deleted file mode 100644 index 140f3f2..0000000 --- a/codes/algorithm/src/test/java/io/github/dunwu/algorithm/tree/BinaryTreeTests.java +++ /dev/null @@ -1,27 +0,0 @@ -package io.github.dunwu.algorithm.tree; - -import io.github.dunwu.algorithm.common.JavaCollectionTest; -import io.github.dunwu.algorithm.common.TreeTest; -import io.github.dunwu.algorithm.common.Utils; -import org.junit.jupiter.api.Test; - -import java.util.Collection; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class BinaryTreeTests { - - @Test - public void testBTree() { - Utils.TestData data = Utils.generateTestData(1000); - - String bstName = "B-Tree"; - BinaryTree bst = new BinaryTree(2); - Collection bstCollection = bst.toCollection(); - - assertTrue(TreeTest.testTree(bst, Integer.class, bstName, data.unsorted, data.invalid)); - assertTrue(JavaCollectionTest.testCollection(bstCollection, Integer.class, bstName, data.unsorted, data.sorted, - data.invalid)); - } - -}