├── .gitignore ├── Nine-Chapters.iml ├── README-Noseparte.md ├── doc └── study │ ├── directory.md │ ├── Data-Structures │ ├── 02.md │ ├── 01.md │ ├── leetcode │ │ └── array │ │ │ └── SingleNumber.md │ └── 03.md │ └── interview │ └── HUAWEI │ └── 01.md ├── src └── main │ └── java │ └── com │ └── awaken │ ├── leetcode │ ├── array │ │ ├── MatrixTransepose.java │ │ ├── FindDuplicate.java │ │ ├── Read4Buf.java │ │ ├── Sum3Closest.java │ │ ├── OddCells.java │ │ ├── TwoSum.java │ │ ├── FlipAndInvertImage.java │ │ ├── SpiralOrder.java │ │ ├── CombinationSum3.java │ │ ├── YangHuiTrigonometric.java │ │ ├── MaxNumberOfFamilies.java │ │ ├── LargestRectangleArea.java │ │ ├── SearchRepetitionNumber.java │ │ ├── Sum3Nums.java │ │ ├── SingleNumber.java │ │ ├── MinimumTotal.java │ │ ├── RotateSorted.java │ │ ├── ThreeSum.java │ │ ├── FindLadders.java │ │ ├── KidsWithCandies.java │ │ ├── ShipWithinDays.java │ │ └── CombinationSum.java │ ├── dynamic │ │ ├── New21Game.java │ │ └── MinCostTickets.java │ ├── hash │ │ ├── SubArraysDivByK.java │ │ └── HappyNumber.java │ ├── string │ │ ├── ReverseLeftWords.java │ │ ├── LongestPalindrome.java │ │ ├── FindTheLongestSubstring.java │ │ └── ValidPalindrome.java │ ├── graph │ │ ├── GraphDeepClone.java │ │ └── EquationsPossible.java │ ├── multithreading │ │ └── FizzBuzz.java │ └── design │ │ └── LRUCache.java │ └── interview │ └── huawei │ └── Sync_LockSupport.java ├── pom.xml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .gitignore 3 | *.iml 4 | target -------------------------------------------------------------------------------- /Nine-Chapters.iml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /README-Noseparte.md: -------------------------------------------------------------------------------- 1 | # Noseparte的数据结构与算法学习笔记 2 | 3 | ## 数据的逻辑结构 4 | 5 | - 线性结构 6 | 7 | 除第一个和最后一个数据外每个数据元素只有一个前驱数据元素和一个后继元素。 8 | 9 | - 树状结构 10 | 11 | 除根节点外每个数据元素只有一个前驱数据元素,可有零个或若干个后继元素。 12 | 13 | - 图状结构 14 | 15 | 每个数据元素可有零个或若干个前驱数据元素和零个 或 若干个后继元素。 -------------------------------------------------------------------------------- /doc/study/directory.md: -------------------------------------------------------------------------------- 1 | ## Java基础算法 2 | 3 | * 1、[DoWhile实现特殊累加值](Data-Structures/01.md) 4 | * 2、[三目运算符美观打印ASCII码](Data-Structures/02.md) 5 | * 3、[枚举求就业率问题](Data-Structures/03.md) 6 | 7 | ## Java面试-算法题 8 | 9 | ### 华为科技: 10 | 11 | * 1、[使用俩个线程交替打印数字与字符串](interview/HUAWEI/01.md) 12 | -------------------------------------------------------------------------------- /doc/study/Data-Structures/02.md: -------------------------------------------------------------------------------- 1 | > 贡献者:[猫叔](https://github.com/UncleCatMySelf) 2 | 3 | ##题目 4 | 将ASCII码位于32-126之间的95个字符显示在屏幕上,为了美观,要求小于100的码值前填充一个0,每打印8个字符后换行。 5 | 6 | ##分析 7 | 这里先注意8个字符后换行,即需要对打印数做判断之后再打印回车,接着循环的头尾是从32-126,且加了一个特殊的条件是没到100的数值需要在前面加“0”,那么我们可以用简单的三目运算符去判断循环的次数 8 | 9 | ##编码 10 | 11 | ```java 12 | public class Main { 13 | public static void main(String[] args) { 14 | String temp; 15 | for (int i = 32; i <= 126;i++){ 16 | temp = i < 100 ? ("0"+i):(""+i); 17 | System.out.print(temp + "=" + (char)i + " "); 18 | if ((i-31)%8 == 0){ 19 | System.out.println(); 20 | } 21 | } 22 | } 23 | } 24 | ``` -------------------------------------------------------------------------------- /doc/study/Data-Structures/01.md: -------------------------------------------------------------------------------- 1 | > 贡献者:[猫叔](https://github.com/UncleCatMySelf) 2 | 3 | ## 题目 4 | 计算1+1/3+1/5+1/7+···+1/(2×n+1)的值,要求使用while循环,且必须计算到1/(2×n+1)小于0.00001是为止。当循环结束时,显示上述表达式中的n的值,以及表达式的计算结果。 5 | 6 | ## 分析 7 | 我们需要一个可变量来作为n,题目要求用while,则判断就是1/(2×n+1)小于0.00001时推出循环,一开始的n就给它为1吧,0是乘不起来的,那么最后需要n-1来得到真正的n值 8 | 9 | ## 编码 10 | 11 | ```java 12 | public class Main { 13 | 14 | public static void main(String[] args) { 15 | //初始化n 16 | int n = 1; 17 | //给定总值的初始值,还有累加的过度局部变量 18 | double dSum = 1.0,dTemp; 19 | do { 20 | //计算 21 | dTemp = 1.0/(2*n+1); 22 | //累加 23 | dSum += dTemp; 24 | n++; 25 | }while (dTemp>=0.00001); 26 | //打印n时记得要减去1 27 | System.out.println("循环结束时n的值是:"+(n-1)); 28 | System.out.println("计算出的结果是:"+dSum); 29 | } 30 | 31 | } 32 | ``` -------------------------------------------------------------------------------- /src/main/java/com/awaken/leetcode/array/MatrixTransepose.java: -------------------------------------------------------------------------------- 1 | package com.awaken.leetcode.array; 2 | 3 | /** 4 | * @author Kirago 5 | * @date 2020/8/26 14:30 6 | * @description https://leetcode-cn.com/problems/transpose-matrix/ 7 | * @version version-1.0 8 | */ 9 | 10 | public class MatrixTransepose { 11 | /** 12 | * @author Kirago 13 | * @date 2020/8/26 14:32 14 | * @description 利用空间换时间,通过复制一个原空间大小的矩阵进行赋值 15 | * @param A 16 | * @return 17 | * @version version-1.0 18 | */ 19 | 20 | 21 | public int[][] transpose(int[][] A){ 22 | int i=A.length; 23 | int j=A[0].length; 24 | int[][] result = new int[j][i]; 25 | for(int x=0;x 2 | 5 | 4.0.0 6 | 7 | 8 | com.awaken 9 | Nine-Chapters 10 | 1.0-SNAPSHOT 11 | 12 | 13 | 14 | UTF-8 15 | 11 16 | 11 17 | 11 18 | 19 | 20 | 21 | Data Structures and Algorithms 22 | LeetCode Algorithm solution 23 | 剑指 Offer 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/main/java/com/awaken/leetcode/dynamic/New21Game.java: -------------------------------------------------------------------------------- 1 | package com.awaken.leetcode.dynamic; 2 | 3 | /** 4 | * @Auther: Noseparte 5 | * @Date: 2020/6/3 4:05 下午 6 | * @Description: 7 | * 8 | *

837. 新21点

9 | */ 10 | public class New21Game { 11 | 12 | public double new21Game(int N, int K, int W) { 13 | if (K == 0) { 14 | return 1.0; 15 | } 16 | double[] dp = new double[K + W]; 17 | for (int i = K; i <= N && i < K + W; i++) { 18 | dp[i] = 1.0; 19 | } 20 | dp[K - 1] = 1.0 * Math.min(N - K + 1, W) / W; 21 | for (int i = K - 2; i >= 0; i--) { 22 | dp[i] = dp[i + 1] - (dp[i + W + 1] - dp[i + 1]) / W; 23 | } 24 | return dp[0]; 25 | } 26 | 27 | public static void main(String[] args) { 28 | double probability = new New21Game().new21Game(6, 1, 10); 29 | System.out.println(probability); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/awaken/interview/huawei/Sync_LockSupport.java: -------------------------------------------------------------------------------- 1 | package com.awaken.interview.huawei; 2 | 3 | import java.util.concurrent.locks.LockSupport; 4 | 5 | public class Sync_LockSupport { 6 | 7 | private static Thread t1 = null, t2 = null; 8 | 9 | public static void main(String[] args) { 10 | 11 | char[] arr1 = "1234567".toCharArray(); 12 | char[] arr2 = "ABCDEFG".toCharArray(); 13 | 14 | t1 = new Thread(() -> { 15 | for (char a : arr1) { 16 | System.out.print(a); 17 | LockSupport.unpark(t2); 18 | LockSupport.park(); 19 | } 20 | }, "t1"); 21 | 22 | t2 = new Thread(() -> { 23 | for (char b : arr2){ 24 | LockSupport.park(); 25 | System.out.print(b); 26 | LockSupport.unpark(t1); 27 | } 28 | }, "t2"); 29 | 30 | t1.start(); 31 | t2.start(); 32 | 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/awaken/leetcode/array/FindDuplicate.java: -------------------------------------------------------------------------------- 1 | package com.awaken.leetcode.array; 2 | 3 | /** 4 | * @Auther: Noseparte 5 | * @Date: 2020/5/26 7:42 下午 6 | * @Description: 7 | * 8 | *

287. 寻找重复数

9 | */ 10 | public class FindDuplicate { 11 | 12 | public int findDuplicate(int[] nums) { 13 | int n = nums.length; 14 | int l = 1, r = n - 1, ans = -1; 15 | while (l <= r) { 16 | int mid = (l + r) >> 1; 17 | int cnt = 0; 18 | for (int num : nums) { 19 | if (num <= mid) { 20 | cnt++; 21 | } 22 | } 23 | if (cnt <= mid) { 24 | l = mid + 1; 25 | } else { 26 | r = mid - 1; 27 | ans = mid; 28 | } 29 | } 30 | return ans; 31 | 32 | } 33 | 34 | 35 | public static void main(String[] args) { 36 | int b = 3 << 2; 37 | System.out.println(b); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/awaken/leetcode/array/Read4Buf.java: -------------------------------------------------------------------------------- 1 | package com.awaken.leetcode.array; 2 | 3 | /** 4 | * @author Kirago 5 | * @date 2020/8/26 11:36 6 | * @description https://leetcode-cn.com/problems/read-n-characters-given-read4/ 7 | * @version version-1.0 8 | */ 9 | /** 10 | * The read4 API is defined in the parent class Reader4. 11 | * int read4(char[] buf); 12 | */ 13 | public class Read4Buf { 14 | 15 | // 此函数忽略 只是为了保证工程项目编译无问题, read4为题目中的API接口 不需要实现 16 | protected int read4(char[] buf){ 17 | return 0; 18 | } 19 | 20 | public int read(char[] buf, int n){ 21 | // 用于存放 read4 读取到的 char 数组的有效长度 22 | int tmp; 23 | // 有效计数位 24 | int length = 0; 25 | // 初始化 bufinfo 供 read4使用 26 | char[] bufTmp = new char[4]; 27 | while( (tmp=read4(bufTmp)) != 0){ 28 | for(int i=0;i Math.abs(sum -target)){ 22 | ans = sum; 23 | } 24 | if(ans - target < 0){ 25 | l++; 26 | }else if(ans - target > 0){ 27 | r--; 28 | }else return ans; 29 | } 30 | } 31 | return ans; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/awaken/leetcode/hash/SubArraysDivByK.java: -------------------------------------------------------------------------------- 1 | package com.awaken.leetcode.hash; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * @Auther: Noseparte 8 | * @Date: 2020/5/27 8:37 下午 9 | * @Description: 10 | * 11 | *

974. 和可被 K 整除的子数组

12 | */ 13 | public class SubArraysDivByK { 14 | 15 | public int subArraysDivByK(int[] A, int K) { 16 | Map record = new HashMap<>(); 17 | record.put(0, 1); 18 | int sum = 0, ans = 0; 19 | for (int elem : A) { 20 | sum += elem; 21 | // 注意 Java 取模的特殊性,当被除数为负数时取模结果为负数,需要纠正 22 | int modulus = (sum % K + K) % K; 23 | int same = record.getOrDefault(modulus, 0); 24 | ans += same; 25 | record.put(modulus, same + 1); 26 | } 27 | return ans; 28 | } 29 | 30 | public static void main(String[] args) { 31 | int byK = new SubArraysDivByK().subArraysDivByK(new int[]{4, 5, 0, -2, -3, 1}, 5); 32 | System.out.println(byK); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /doc/study/interview/HUAWEI/01.md: -------------------------------------------------------------------------------- 1 | > 贡献者:[Almost-Famous](https://github.com/noseparte) 2 | 3 | ##题目 4 | 使用俩个线程交替打印数字与字符串 1A2B3C4D5E6F7G...27Z 5 | 6 | ##分析 7 | 打印完当前任务后,唤醒另外一个线程,阻塞当前线程,另一个线程执行完毕后再唤醒第一个线程执行,保持阻塞。 8 | 9 | ##编码 10 | 11 | ```java 12 | public class Sync_LockSupport { 13 | 14 | private static Thread t1 = null, t2 = null; 15 | 16 | public static void main(String[] args) { 17 | 18 | char[] arr1 = "1234567".toCharArray(); 19 | char[] arr2 = "ABCDEFG".toCharArray(); 20 | 21 | t1 = new Thread(() -> { 22 | for (char a : arr1) { 23 | System.out.print(a); 24 | LockSupport.unpark(t2); 25 | LockSupport.park(); 26 | } 27 | }, "t1"); 28 | 29 | t2 = new Thread(() -> { 30 | for (char b : arr2){ 31 | LockSupport.park(); 32 | System.out.print(b); 33 | LockSupport.unpark(t1); 34 | } 35 | }, "t2"); 36 | 37 | t1.start(); 38 | t2.start(); 39 | 40 | } 41 | 42 | } 43 | ``` 44 | 45 | ##结尾 46 | -------------------------------------------------------------------------------- /src/main/java/com/awaken/leetcode/array/OddCells.java: -------------------------------------------------------------------------------- 1 | package com.awaken.leetcode.array; 2 | 3 | /** 4 | * @Auther: Noseparte 5 | * @Date: 2020/5/15 8:09 下午 6 | * @Description: 7 | * 8 | *

1252. 奇数值单元格的数目

9 | */ 10 | public class OddCells { 11 | 12 | public int oddCells(int n, int m, int[][] indices) { 13 | int[] row = new int[n]; 14 | int[] col = new int[m]; 15 | 16 | for (int i = 0; i < indices.length; i++) { 17 | row[indices[i][0]]++; 18 | col[indices[i][1]]++; 19 | } 20 | 21 | int ans = 0; 22 | for (int i = 0; i < n; i++) 23 | for (int j = 0; j < m; j++) { 24 | if ((row[i] + col[j]) % 2 > 0) 25 | ans++; 26 | } 27 | return ans; 28 | } 29 | 30 | public static void main(String[] args) { 31 | int cells1 = new OddCells().oddCells(2, 3, new int[][]{{0, 1}, {1, 1}}); 32 | System.out.println(cells1); 33 | int cells = new OddCells().oddCells(2, 2, new int[][]{{1, 1}, {0, 0}}); 34 | System.out.println(cells); 35 | } 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /doc/study/Data-Structures/leetcode/array/SingleNumber.md: -------------------------------------------------------------------------------- 1 | # 面试题56 - I. 数组中数字出现的次数 2 | 3 | 解法选择 4 | 看到题目第一眼就觉得应该是需要用到异或来做的。 5 | 题目又要求了:时间复杂度为O(n),空间复杂度为O(1)。 6 | 因此不能用map(空间复杂度为O(n))与双重循环嵌套(空间复杂度为O(n^2))。 7 | 8 | 解析题目 9 | 由于数组中存在着两个数字不重复的情况,我们将所有的数字异或操作起来,最终得到的结果是这两个数字的异或结果:(相同的两个数字相互异或,值为0)) 最后结果一定不为0,因为有两个数字不重复。 10 | 11 | 演示: 12 | 13 | ``` 14 | 4 ^ 1 ^ 4 ^ 6 => 1 ^ 6 15 | 16 | 6 对应的二进制: 110 17 | 1 对应的二进制: 001 18 | 1 ^ 6 二进制: 111 19 | ``` 20 | 此时我们无法通过 111(二进制),去获得 110 和 001。 21 | 那么当我们可以把数组分为两组进行异或,那么就可以知道是哪两个数字不同了。 22 | 我们可以想一下如何分组: 23 | 24 | 重复的数字进行分组,很简单,只需要有一个统一的规则,就可以把相同的数字分到同一组了。例如:奇偶分组。因为重复的数字,数值都是一样的,所以一定会分到同一组! 25 | 此时的难点在于,对两个不同数字的分组。 26 | 此时我们要找到一个操作,让两个数字进行这个操作后,分为两组。 27 | 我们最容易想到的就是 & 1 操作, 当我们对奇偶分组时,容易地想到 & 1,即用于判断最后一位二进制是否为1。来辨别奇偶。 28 | 你看,通过 & 运算来判断一位数字不同即可分为两组,那么我们随便两个不同的数字至少也有一位不同吧! 29 | 我们只需要找出那位不同的数字mask,即可完成分组( & mask )操作。 30 | 31 | 为了操作方便,我们只去找最低位的mask: 32 | 33 | | | | | | 34 | | :---: | :---: | :---: | :---: | 35 | |num1: | 101110 | 110 | 1111 | 36 | |num2: | 111110 | 001 | 1001 | 37 | |mask: | 010000 | 001 | 0010 | 38 | 由于两个数异或的结果就是两个数数位不同结果的直观表现,所以我们可以通过异或后的结果去找最低位的mask! 39 | 40 | -------------------------------------------------------------------------------- /src/main/java/com/awaken/leetcode/array/TwoSum.java: -------------------------------------------------------------------------------- 1 | package com.awaken.leetcode.array; 2 | 3 | import java.util.Arrays; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | /** 8 | * @Auther: Noseparte 9 | * @Date: 2020/6/3 4:09 下午 10 | * @Description: 11 | * 12 | *

13 | */ 14 | public class TwoSum { 15 | 16 | public int[] twoSum(int[] nums, int target) { 17 | int length = nums.length; 18 | Map map = new HashMap<>(); 19 | for (int i = 0; i < length; i++) { 20 | map.put(nums[i], i); 21 | } 22 | for (int i = 0; i < nums.length; i++) { 23 | int complement = target - nums[i]; 24 | if (map.containsKey(complement) && map.get(complement) != i) { 25 | return new int[] { i, map.get(complement) }; 26 | } 27 | } 28 | throw new IllegalArgumentException("No two sum solution"); 29 | } 30 | 31 | public static void main(String[] args) { 32 | int[] sum = new TwoSum().twoSum(new int[]{3,2,4}, 6); 33 | System.out.println(Arrays.toString(sum)); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/awaken/leetcode/array/FlipAndInvertImage.java: -------------------------------------------------------------------------------- 1 | package com.awaken.leetcode.array; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * @Auther: Noseparte 7 | * @Date: 2020/5/13 7:37 下午 8 | * @Description: 9 | * 10 | *

832. 翻转图像

11 | */ 12 | public class FlipAndInvertImage { 13 | 14 | public int[][] flipAndInvertImage(int[][] A) { 15 | 16 | int length = A.length; 17 | 18 | int[][] result = new int[A.length][]; 19 | 20 | for (int j = 0; j < length; j++) { 21 | int[] a = A[j]; 22 | int n = a.length; 23 | int[] b = new int[n]; 24 | 25 | for (int i = 0; i < n; i++) { 26 | int image = a[n - 1 - i] == 0 ? 1 : 0; 27 | b[i] = image; 28 | } 29 | result[j] = b; 30 | } 31 | 32 | return result; 33 | } 34 | 35 | public static void main(String[] args) { 36 | int[][] image = new FlipAndInvertImage().flipAndInvertImage(new int[][]{{1, 1, 0, 0}, {1, 0, 0, 1}, {1, 0, 0, 1}, {1, 0, 0, 1}}); 37 | for (int[] a : image) { 38 | System.out.println(Arrays.toString(a)); 39 | } 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/awaken/leetcode/string/ReverseLeftWords.java: -------------------------------------------------------------------------------- 1 | package com.awaken.leetcode.string; 2 | 3 | /** 4 | * @Auther: Noseparte 5 | * @Date: 2020/4/29 1:19 下午 6 | * @Description: 7 | * 8 | *

9 | * 面试题58 - II. 左旋转字符串 10 | * 字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。 11 | * 比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。 12 | *

13 | */ 14 | class ReverseLeftWord { 15 | 16 | //解法一 最简单的 17 | public static String reverseLeftWords(String s, int n) { 18 | return s.substring(n) + s.substring(0, n); 19 | } 20 | 21 | //解法二 要求不能使用切片函数 22 | public static String reverseLeftWords2(String s, int n) { 23 | StringBuilder res = new StringBuilder(); 24 | for (int i = n; i < s.length(); i++) { 25 | res.append(s.charAt(i)); 26 | } 27 | for (int i = 0; i < n; i++) { 28 | res.append(s.charAt(i)); 29 | } 30 | return res.toString(); 31 | } 32 | 33 | public static void main(String[] args) { 34 | String s = ReverseLeftWord.reverseLeftWords("helloWorld", 5); 35 | String s2 = ReverseLeftWord.reverseLeftWords2("helloChina", 5); 36 | System.out.println(s); 37 | System.out.println(s2); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /doc/study/Data-Structures/03.md: -------------------------------------------------------------------------------- 1 | > 贡献者:[猫叔](https://github.com/UncleCatMySelf) 2 | 3 | ##题目 4 | 已知某大学某专业本年度毕业生的人数为三百人左右,且学生就业率为82.23%。通过for循环,来求出最可能的学生人数及已就业人数。假定总人数三百人左右的描述,是指人数上下波动在20以内。 5 | 6 | ##分析 7 | 我们需要将已知的值传入到一个函数中,且这个函数是有一个重要的for循环,20则是循环的上下值。 8 | 9 | ##编码 10 | 11 | ```java 12 | public class Main { 13 | 14 | private void calcByEnum(int iNum,int iOff,float fPercent){ 15 | float fMinDiff = 1.0f,fTmp; 16 | int iReadNum = 300; 17 | for (int i = iNum-iOff+1;i<=iNum+iOff;i++){ 18 | fTmp = Math.abs(Math.round(i*fPercent)/(float)i-fPercent); 19 | if (fTmp5. 最长回文子串

9 | */ 10 | public class LongestPalindrome { 11 | 12 | public String longestPalindrome(String s) { 13 | if (s == null || s.length() < 1) return ""; 14 | int start = 0, end = 0; 15 | for (int i = 0; i < s.length(); i++) { 16 | int len1 = expandAroundCenter(s, i, i); 17 | int len2 = expandAroundCenter(s, i, i + 1); 18 | int len = Math.max(len1, len2); 19 | if (len > end - start) { 20 | start = i - (len - 1) / 2; 21 | end = i + len / 2; 22 | } 23 | } 24 | return s.substring(start, end + 1); 25 | } 26 | 27 | private int expandAroundCenter(String s, int left, int right) { 28 | int L = left, R = right; 29 | while (L >= 0 && R < s.length() && s.charAt(L) == s.charAt(R)) { 30 | L--; 31 | R++; 32 | } 33 | return R - L - 1; 34 | } 35 | 36 | public static void main(String[] args) { 37 | String dabad = new LongestPalindrome().longestPalindrome("babad"); 38 | System.out.println(dabad); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/awaken/leetcode/array/SpiralOrder.java: -------------------------------------------------------------------------------- 1 | package com.awaken.leetcode.array; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * @Auther: Noseparte 7 | * @Date: 2020/5/21 8:33 下午 8 | * @Description: 9 | * 10 | *

面试题29. 顺时针打印矩阵

11 | */ 12 | public class SpiralOrder { 13 | 14 | public int[] spiralOrder(int[][] matrix) { 15 | if (matrix.length == 0) return new int[0]; 16 | int l = 0, r = matrix[0].length - 1, t = 0, b = matrix.length - 1, x = 0; 17 | int[] res = new int[(r + 1) * (b + 1)]; 18 | while (true) { 19 | for (int i = l; i <= r; i++) res[x++] = matrix[t][i]; // left to right. 20 | if (++t > b) break; 21 | for (int i = t; i <= b; i++) res[x++] = matrix[i][r]; // top to bottom. 22 | if (l > --r) break; 23 | for (int i = r; i >= l; i--) res[x++] = matrix[b][i]; // right to left. 24 | if (t > --b) break; 25 | for (int i = b; i >= t; i--) res[x++] = matrix[i][l]; // bottom to top. 26 | if (++l > r) break; 27 | } 28 | return res; 29 | } 30 | 31 | public static void main(String[] args) { 32 | int[] order = new SpiralOrder().spiralOrder(new int[][]{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); 33 | System.out.println(Arrays.toString(order)); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/awaken/leetcode/array/CombinationSum3.java: -------------------------------------------------------------------------------- 1 | package com.awaken.leetcode.array; 2 | 3 | import java.util.ArrayDeque; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | import java.util.Queue; 7 | 8 | /** 9 | * @author Kirago 10 | * @date 2020/9/11 9:02 11 | * @description https://leetcode-cn.com/problems/combination-sum-iii/ 12 | * @version version-1.0 13 | */ 14 | 15 | 16 | public class CombinationSum3 { 17 | private Queue queue = new ArrayDeque<>(); 18 | 19 | private List> res = new ArrayList<>(); 20 | public List> combinationSum3(int k, int n) { 21 | if(n<0) return null; 22 | inner(1, k, n); 23 | return res; 24 | } 25 | 26 | private void inner(int index ,int nums, int reduction){ 27 | 28 | if(reduction < 0 || nums < 0) return; 29 | if( reduction == 0 && nums == 0 ){ 30 | res.add(new ArrayList<>(queue)); 31 | return; 32 | } 33 | for(int i=index;i<10;i++){ 34 | queue.add(i); 35 | inner(++index, nums-1, reduction-i); 36 | queue.remove(i); 37 | } 38 | } 39 | 40 | public static void main(String[] args){ 41 | CombinationSum3 combinationSum3 = new CombinationSum3(); 42 | combinationSum3.combinationSum3(3,7); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/awaken/leetcode/array/YangHuiTrigonometric.java: -------------------------------------------------------------------------------- 1 | package com.awaken.leetcode.array; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * @Auther: Noseparte 8 | * @Date: 2020/5/12 7:58 下午 9 | * @Description: 10 | * 11 | *

118. 杨辉三角

12 | */ 13 | public class YangHuiTrigonometric { 14 | 15 | public List> generate(int numRows) { 16 | 17 | List> triangle = new ArrayList<>(); 18 | 19 | if (numRows <= 0) { 20 | return triangle; 21 | } 22 | 23 | triangle.add(new ArrayList<>()); 24 | triangle.get(0).add(1); 25 | 26 | for (int rowNum = 1; rowNum < numRows; rowNum++) { 27 | List row = new ArrayList<>(); 28 | List prevRow = triangle.get(rowNum - 1); 29 | 30 | row.add(1); 31 | 32 | for (int j = 1; j < rowNum; j++) { 33 | row.add(prevRow.get(j - 1) + prevRow.get(j)); 34 | } 35 | 36 | row.add(1); 37 | 38 | triangle.add(row); 39 | } 40 | 41 | return triangle; 42 | } 43 | 44 | public static void main(String[] args) { 45 | List> lists = new YangHuiTrigonometric().generate(5); 46 | lists.forEach(System.out::println); 47 | // System.out.println(lists); 48 | 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/awaken/leetcode/array/MaxNumberOfFamilies.java: -------------------------------------------------------------------------------- 1 | package com.awaken.leetcode.array; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * @Auther: Noseparte 8 | * @Date: 2020/5/11 11:09 上午 9 | * @Description: 10 | * 11 | *

1386. 安排电影院座位

12 | */ 13 | public class MaxNumberOfFamilies { 14 | 15 | public int maxNumberOfFamilies(int n, int[][] reservedSeats) { 16 | 17 | int left = 0b11110000; 18 | int middle = 0b11000011; 19 | int right = 0b00001111; 20 | 21 | Map occupied = new HashMap<>(); 22 | for (int[] seat : reservedSeats) { 23 | if (seat[1] >= 2 && seat[1] <= 9) { 24 | int origin = occupied.getOrDefault(seat[0], 0); 25 | int value = origin | (1 << seat[1] - 2); 26 | occupied.put(seat[0], value); 27 | } 28 | } 29 | 30 | int nas = (n - occupied.size()) * 2; 31 | for (Map.Entry entry : occupied.entrySet()) { 32 | int bitmask = entry.getValue(); 33 | if (((bitmask | left) == left) || ((bitmask | middle) == middle) || ((bitmask | right) == right)) { 34 | ++nas; 35 | } 36 | } 37 | return nas; 38 | 39 | } 40 | 41 | public static void main(String[] args) { 42 | int number = new MaxNumberOfFamilies().maxNumberOfFamilies(2, new int[][]{{2, 1}, {1, 8}, {2, 6}}); 43 | System.out.println(number); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/awaken/leetcode/string/FindTheLongestSubstring.java: -------------------------------------------------------------------------------- 1 | package com.awaken.leetcode.string; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * @Auther: Noseparte 7 | * @Date: 2020/5/20 2:52 下午 8 | * @Description: 9 | * 10 | *

1371. 每个元音包含偶数次的最长子字符串

11 | */ 12 | public class FindTheLongestSubstring { 13 | 14 | public int findTheLongestSubstring(String s) { 15 | int n = s.length(); 16 | int[] pos = new int[1 << 5]; 17 | Arrays.fill(pos, -1); 18 | int ans = 0, status = 0; 19 | pos[0] = 0; 20 | for (int i = 0; i < n; i++) { 21 | char ch = s.charAt(i); 22 | if (ch == 'a') { 23 | //1 << 0 24 | status ^= (1); 25 | } else if (ch == 'e') { 26 | status ^= (1 << 1); 27 | } else if (ch == 'i') { 28 | status ^= (1 << 2); 29 | } else if (ch == 'o') { 30 | status ^= (1 << 3); 31 | } else if (ch == 'u') { 32 | status ^= (1 << 4); 33 | } 34 | if (pos[status] >= 0) { 35 | ans = Math.max(ans, i + 1 - pos[status]); 36 | } else { 37 | pos[status] = i + 1; 38 | } 39 | } 40 | return ans; 41 | } 42 | 43 | public static void main(String[] args) { 44 | int substring = new FindTheLongestSubstring().findTheLongestSubstring("sdfrsgsfsfweqwsdc"); 45 | System.out.println(substring); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/awaken/leetcode/array/LargestRectangleArea.java: -------------------------------------------------------------------------------- 1 | package com.awaken.leetcode.array; 2 | 3 | import java.util.ArrayDeque; 4 | import java.util.Deque; 5 | 6 | /** 7 | * @Auther: Noseparte 8 | * @Date: 2020/5/30 8:27 下午 9 | * @Description: 10 | * 11 | *

84. 柱状图中最大的矩形

12 | */ 13 | public class LargestRectangleArea { 14 | 15 | public int largestRectangleArea(int[] heights) { 16 | int len = heights.length; 17 | if (len == 0) { 18 | return 0; 19 | } 20 | if (len == 1) { 21 | return heights[0]; 22 | } 23 | 24 | int area = 0; 25 | int[] newHeights = new int[len + 2]; 26 | for (int i = 0; i < len; i++) { 27 | newHeights[i + 1] = heights[i]; 28 | } 29 | len += 2; 30 | heights = newHeights; 31 | 32 | Deque stack = new ArrayDeque<>(); 33 | stack.addLast(0); 34 | 35 | for (int i = 1; i < len; i++) { 36 | while (heights[stack.peekLast()] > heights[i]) { 37 | int height = heights[stack.removeLast()]; 38 | int width = i - stack.peekLast() - 1; 39 | area = Math.max(area, width * height); 40 | } 41 | stack.addLast(i); 42 | } 43 | return area; 44 | } 45 | 46 | public static void main(String[] args) { 47 | int area = new LargestRectangleArea().largestRectangleArea(new int[]{2, 1, 5, 6, 2, 3}); 48 | System.out.println(area); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/awaken/leetcode/array/SearchRepetitionNumber.java: -------------------------------------------------------------------------------- 1 | package com.awaken.leetcode.array; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * @Auther: Noseparte 8 | * @Date: 2020/5/20 4:52 下午 9 | * @Description: 10 | * 11 | *

面试题53 - I. 在排序数组中查找数字 I

12 | */ 13 | public class SearchRepetitionNumber { 14 | 15 | public int search1(int[] nums, int target) { 16 | int n = nums.length; 17 | if (n <= 0) { 18 | return 0; 19 | } 20 | 21 | Map map = new HashMap<>(); 22 | for (Integer bun : nums) { 23 | if (!map.containsKey(bun)) { 24 | map.put(bun, 1); 25 | } else { 26 | map.put(bun, map.get(bun) + 1); 27 | } 28 | } 29 | 30 | return map.getOrDefault(target, 0); 31 | } 32 | 33 | //官网优化后的方案 34 | public int search(int[] nums, int target) { 35 | return helper(nums, target) - helper(nums, target - 1); 36 | } 37 | int helper(int[] nums, int tar) { 38 | int i = 0, j = nums.length - 1; 39 | while(i <= j) { 40 | int m = (i + j) / 2; 41 | if(nums[m] <= tar) i = m + 1; 42 | else j = m - 1; 43 | } 44 | return i; 45 | } 46 | 47 | public static void main(String[] args) { 48 | int[] nums = new int[]{5, 7, 7, 8, 8, 10}; 49 | int time = new SearchRepetitionNumber().search(nums, 8); 50 | System.out.println(time); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/awaken/leetcode/array/Sum3Nums.java: -------------------------------------------------------------------------------- 1 | package com.awaken.leetcode.array; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | 7 | /** 8 | * @Description: java类作用描述,https://leetcode-cn.com/problems/3sum/ 9 | * @Author: kirago 10 | * @CreateDate: 2020/8/26 9:54 下午 11 | * @UpdateRemark: 修改内容 12 | * @Version: 1.0 13 | */ 14 | public class Sum3Nums { 15 | 16 | public List> threeSum(int[] nums){ 17 | List> res = new ArrayList<>(); 18 | Arrays.sort(nums); 19 | int sum=0,length=nums.length; 20 | for(int i=0;i0) return res; 22 | if(i>0 && nums[i-1] == nums[i]) continue; 23 | int left=i+1,right=length-1; 24 | while (left list = Arrays.asList(nums[i], nums[left], nums[right]); 28 | res.add(list); 29 | while (left < right && nums[left] == nums[left+1]) left++; 30 | while (left < right && nums[right-1] == nums[right]) right--; 31 | left++; 32 | right--; 33 | }else if(sum < 0){ 34 | left++; 35 | }else { 36 | right--; 37 | } 38 | } 39 | } 40 | return res; 41 | } 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/awaken/leetcode/string/ValidPalindrome.java: -------------------------------------------------------------------------------- 1 | package com.awaken.leetcode.string; 2 | 3 | /** 4 | * @Auther: Noseparte 5 | * @Date: 2020/5/19 3:06 下午 6 | * @Description: 7 | * 8 | *

680. 验证回文字符串 Ⅱ

9 | */ 10 | public class ValidPalindrome { 11 | 12 | public boolean validPalindrome(String s) { 13 | int low = 0, high = s.length() - 1; 14 | while (low < high) { 15 | char c1 = s.charAt(low), c2 = s.charAt(high); 16 | if (c1 == c2) { 17 | low++; 18 | high--; 19 | } else { 20 | boolean flag1 = true, flag2 = true; 21 | for (int i = low, j = high - 1; i < j; i++, j--) { 22 | char c3 = s.charAt(i), c4 = s.charAt(j); 23 | if (c3 != c4) { 24 | flag1 = false; 25 | break; 26 | } 27 | } 28 | for (int i = low + 1, j = high; i < j; i++, j--) { 29 | char c3 = s.charAt(i), c4 = s.charAt(j); 30 | if (c3 != c4) { 31 | flag2 = false; 32 | break; 33 | } 34 | } 35 | return flag1 || flag2; 36 | } 37 | } 38 | return true; 39 | } 40 | 41 | public static void main(String[] args) { 42 | boolean validPalindrome = new ValidPalindrome().validPalindrome("aba"); 43 | System.out.println(validPalindrome); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nine-Chapters 2 | 3 | ## 前言 4 | 5 | AwakenCN组织的数据结构与算法篇,强化数据结构与算法的基础知识,从而达到学以致用的目的,其次就是在跳槽可以拿到更好的offer。 6 | 7 | 8 | ## 算法宝典 9 | 10 | * [LeetCode中文网](https://leetcode-cn.com/problemset/all/) 11 | * [LeetCode官网](https://leetcode.com/) 12 | * [面试](https://leetcode-cn.com/interview) 13 | * [Java基础算法](doc/study/directory.md) 14 | 15 | 16 | ## 数据结构 17 | 18 | - 数组(Array) : 19 | 数组是一种聚合数据类型,它是将具有相同类型的若干变量有序地组织在一起的集合。数组可以说是最基本的数据结构,在各种编程语言中都有对应。一个数组可以分解为多个数组元素,按照数据元素的类型,数组可以分为整型数组、字符型数组、浮点型数组、指针数组和结构数组等。数组还可以有一维、二维以及多维等表现形式。 20 | - 栈(Stack) : 21 | 栈是一种特殊的线性表,它只能在一个表的一个固定端进行数据结点的插入和删除操作。栈按照后进先出的原则来存储数据,也就是说,先插入的数据将被压入栈底,最后插入的数据在栈顶,读出数据时,从栈顶开始逐个读出。栈在汇编语言程序中,经常用于重要数据的现场保护。栈中没有数据时,称为空栈。 22 | - 队列(Queue) : 23 | 队列和栈类似,也是一种特殊的线性表。和栈不同的是,队列只允许在表的一端进行插入操作,而在另一端进行删除操作。一般来说,进行插入操作的一端称为队尾,进行删除操作的一端称为队头。队列中没有元素时,称为空队列。 24 | - 链表(Linked List) : 25 | 链表是一种数据元素按照链式存储结构进行存储的数据结构,这种存储结构具有在物理上存在非连续的特点。链表由一系列数据结点构成,每个数据结点包括数据域和指针域两部分。其中,指针域保存了数据结构中下一个元素存放的地址。链表结构中数据元素的逻辑顺序是通过链表中的指针链接次序来实现的。 26 | - 树(Tree) : 27 | 树是典型的非线性结构,它是包括,2个结点的有穷集合K。在树结构中,有且仅有一个根结点,该结点没有前驱结点。在树结构中的其他结点都有且仅有一个前驱结点,而且可以有两个后继结点,m≥0。 28 | - 图(Graph) : 29 | 图是另一种非线性数据结构。在图结构中,数据结点一般称为顶点,而边是顶点的有序偶对。如果两个顶点之间存在一条边,那么就表示这两个顶点具有相邻关系。 30 | - 堆(Heap) : 31 | 堆是一种特殊的树形数据结构,一般讨论的堆都是二叉堆。堆的特点是根结点的值是所有结点中最小的或者最大的,并且根结点的两个子树也是一个堆结构。 32 | - 散列表(Hash) : 33 | 散列表源自于散列函数(Hash function),其思想是如果在结构中存在关键字和T相等的记录,那么必定在F(T)的存储位置可以找到该记录,这样就可以不用进行比较操作而直接取得所查记录。 34 | 35 | ## 时间、空间复杂度 36 | 37 | 38 | 39 | 40 | ## 算法题目最优解 41 | 42 | - [面试题56 - I. 数组中数字出现的次数](doc/study/Data-Structures/leetcode/array/SingleNumber.md) 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/main/java/com/awaken/leetcode/array/SingleNumber.java: -------------------------------------------------------------------------------- 1 | package com.awaken.leetcode.array; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * @Auther: Noseparte 7 | * @Date: 2020/4/28 1:09 下午 8 | * @Description: 9 | * 10 | *

面试题56 - I. 数组中数字出现的次数

11 | *

一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。 12 | * 要求时间复杂度是O(n),空间复杂度是O(1)。

13 | * 14 | *

示例 1:

15 | *

输入:nums = [4,1,4,6]

16 | *

输出:[1,6] 或 [6,1]

17 | * 18 | *

示例 2:

19 | *

输入:nums = [1,2,10,4,1,4,3,3]

20 | *

输出:[2,10] 或 [10,2]

21 | * 22 | */ 23 | public class SingleNumber { 24 | 25 | public static int[] singleNumbers(int[] nums) { 26 | //用于将所有的数异或起来 27 | int k = 0; 28 | 29 | for (int num : nums) { 30 | k ^= num; 31 | } 32 | 33 | //获得k中最低位的 34 | int mask = 1; 35 | 36 | while ((k & mask) == 0) { 37 | mask <<= 1; 38 | } 39 | 40 | int a = 0; 41 | int b = 0; 42 | 43 | //mask = k & (-k) 这种方法也可以得到mask,具体原因百度 哈哈哈哈哈 44 | for (int num : nums) { 45 | if ((num & mask) == 0) { 46 | a ^= num; 47 | } else { 48 | b ^= num; 49 | } 50 | } 51 | 52 | return new int[]{a, b}; 53 | } 54 | 55 | public static void main(String[] args) { 56 | int[] numbers = SingleNumber.singleNumbers(new int[]{4, 1, 4, 6}); 57 | System.out.println(Arrays.toString(numbers)); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/awaken/leetcode/array/MinimumTotal.java: -------------------------------------------------------------------------------- 1 | package com.awaken.leetcode.array; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * @Auther: Noseparte 8 | * @Date: 2020/5/16 5:54 下午 9 | * @Description: 10 | * 11 | *

leetcode 120. 三角形最小路径和

12 | */ 13 | public class MinimumTotal { 14 | 15 | public int minimumTotal(List> triangle) { 16 | int row = triangle.size(); 17 | int[] minLen = new int[row + 1]; 18 | for (int level = row - 1; level >= 0; level--) { 19 | for (int i = 0; i <= level; i++) { //第i行有i+1个数字 20 | minLen[i] = Math.min(minLen[i], minLen[i + 1]) + triangle.get(level).get(i); 21 | } 22 | } 23 | return minLen[0]; 24 | } 25 | 26 | public static void main(String[] args) { 27 | List> triangle = new ArrayList<>(); 28 | List list = new ArrayList<>(); 29 | list.add(2); 30 | List list1 = new ArrayList<>(); 31 | list1.add(3); 32 | list1.add(4); 33 | List list2 = new ArrayList<>(); 34 | list2.add(6); 35 | list2.add(5); 36 | list2.add(7); 37 | List list3 = new ArrayList<>(); 38 | list3.add(4); 39 | list3.add(1); 40 | list3.add(8); 41 | list3.add(3); 42 | triangle.add(list); 43 | triangle.add(list1); 44 | triangle.add(list2); 45 | triangle.add(list3); 46 | 47 | int minimumTotal = new MinimumTotal().minimumTotal(triangle); 48 | System.out.println(minimumTotal); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/awaken/leetcode/graph/GraphDeepClone.java: -------------------------------------------------------------------------------- 1 | package com.awaken.leetcode.graph; 2 | 3 | 4 | import java.util.ArrayList; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | 8 | /** 9 | * @Description: 133、图的深度克隆,https://leetcode-cn.com/problems/clone-graph/ 10 | * @Author: kirago 11 | * @CreateDate: 2020/8/12 11:47 下午 12 | * @UpdateRemark: 修改内容 13 | * @Version: 1.0 14 | */ 15 | public class GraphDeepClone { 16 | class Node { 17 | public int val; 18 | public List neighbors; 19 | 20 | public Node() { 21 | val = 0; 22 | neighbors = new ArrayList(); 23 | } 24 | 25 | public Node(int _val) { 26 | val = _val; 27 | neighbors = new ArrayList(); 28 | } 29 | 30 | public Node(int _val, ArrayList _neighbors) { 31 | val = _val; 32 | neighbors = _neighbors; 33 | } 34 | } 35 | 36 | /** 37 | * 通过构造 HashMap 来记录已经遍历的元素,结合 BFS 的思路实现完全遍历 38 | */ 39 | private HashMap visited = new HashMap<>(); 40 | 41 | /** 42 | * @Description: java方法描述 43 | * @Param: Node 44 | * @return: Node 45 | **/ 46 | public Node cloneGraph(Node node) { 47 | if(node == null) return null; 48 | if(visited.containsKey(node)) return visited.get(node); 49 | Node cloneNode = new Node(node.val, new ArrayList<>()); 50 | visited.put(node, cloneNode); 51 | for(Node neighbor:node.neighbors){ 52 | cloneNode.neighbors.add(cloneGraph(neighbor)); 53 | } 54 | return cloneNode; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/awaken/leetcode/array/RotateSorted.java: -------------------------------------------------------------------------------- 1 | package com.awaken.leetcode.array; 2 | 3 | /** 4 | * @Auther: Noseparte 5 | * @Date: 2020/4/27 7:21 下午 6 | * @Description: 7 | * 8 | *

9 | 假设按照升序排序的数组在预先未知的某个点上进行了旋转。 10 | 11 | ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。 12 | 13 | 搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。 14 | 15 | 你可以假设数组中不存在重复的元素。 16 | 17 | 你的算法时间复杂度必须是 O(log n) 级别。 18 | *

19 | */ 20 | public class RotateSorted { 21 | 22 | public static int search(int[] nums, int target) { 23 | if (nums == null || nums.length == 0) { 24 | return -1; 25 | } 26 | 27 | int start = 0; 28 | int end = nums.length - 1; 29 | int mid; 30 | while (start <= end) { 31 | mid = start + (end - start) / 2; 32 | if (nums[mid] == target) { 33 | return mid; 34 | } 35 | //前半部分有序 36 | if (nums[start] <= nums[mid]) { 37 | //target在前半部分 38 | if (target >= nums[start] && target <= nums[mid]) { 39 | end = mid - 1; 40 | } else { 41 | start = mid + 1; 42 | } 43 | } else { 44 | //target在后半部分 45 | if (target > nums[mid] && target <= nums[end]) { 46 | start = mid + 1; 47 | } else { 48 | end = mid - 1; 49 | } 50 | } 51 | } 52 | 53 | return -1; 54 | } 55 | 56 | public static void main(String[] args) { 57 | int result = RotateSorted.search(new int[]{4, 5, 6, 7, 0, 1, 2}, 0); 58 | System.out.println(result); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/awaken/leetcode/graph/EquationsPossible.java: -------------------------------------------------------------------------------- 1 | package com.awaken.leetcode.graph; 2 | 3 | /** 4 | * @Auther: Noseparte 5 | * @Date: 2020/6/8 5:44 下午 6 | * @Description: 7 | * 8 | *

990. 等式方程的可满足性

9 | */ 10 | public class EquationsPossible { 11 | 12 | //给定一个由表示变量之间关系的字符串方程组成的数组,每个字符串方程 equations[i] 的长度为 4, 13 | // 并采用两种不同的形式之一:"a==b" 或 "a!=b"。在这里,a 和 b 是小写字母(不一定不同),表示单字母变量名。 14 | 15 | //只有当可以将整数分配给变量名,以便满足所有给定的方程时才返回 true,否则返回 false。  16 | public boolean equationsPossible(String[] equations) { 17 | int length = equations.length; 18 | int[] parent = new int[26]; 19 | for (int i = 0; i < 26; i++) { 20 | parent[i] = i; 21 | } 22 | for (String str : equations) { 23 | if (str.charAt(1) == '=') { 24 | int index1 = str.charAt(0) - 'a'; 25 | int index2 = str.charAt(3) - 'a'; 26 | union(parent, index1, index2); 27 | } 28 | } 29 | for (String str : equations) { 30 | if (str.charAt(1) == '!') { 31 | int index1 = str.charAt(0) - 'a'; 32 | int index2 = str.charAt(3) - 'a'; 33 | if (find(parent, index1) == find(parent, index2)) { 34 | return false; 35 | } 36 | } 37 | } 38 | return true; 39 | } 40 | 41 | public void union(int[] parent, int index1, int index2) { 42 | parent[find(parent, index1)] = find(parent, index2); 43 | } 44 | 45 | public int find(int[] parent, int index) { 46 | while (parent[index] != index) { 47 | parent[index] = parent[parent[index]]; 48 | index = parent[index]; 49 | } 50 | return index; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/awaken/leetcode/array/ThreeSum.java: -------------------------------------------------------------------------------- 1 | package com.awaken.leetcode.array; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | 7 | /** 8 | * @Auther: Noseparte 9 | * @Date: 2020/4/26 5:03 下午 10 | * @Description: 11 | * 12 | *

leetcode 15.三数之和

13 | * 14 | * 给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。 15 | * 注意:答案中不可以包含重复的三元组。 16 | * 17 | * 示例: 18 | * 19 | * 给定数组 nums = [-1, 0, 1, 2, -1, -4], 20 | * 21 | * 满足要求的三元组集合为: 22 | * [ 23 | * [-1, 0, 1], 24 | * [-1, -1, 2] 25 | * ] 26 | * 27 | */ 28 | public class ThreeSum { 29 | 30 | public static List> threeSum(int[] nums) { 31 | List> result = new ArrayList>(); 32 | Arrays.sort(nums); 33 | if (nums.length == 0) { 34 | return result; 35 | } 36 | 37 | for (int i = 0; i < nums.length; i++) { 38 | if (i > 0 && nums[i] == nums[i - 1]) continue; 39 | int target = -nums[i]; 40 | int j = i + 1; // left pointer 41 | int k = nums.length - 1; //right pointer 42 | 43 | while (j < k) { 44 | if (nums[j] + nums[k] == target) { 45 | List curr = new ArrayList(); 46 | curr.add(nums[i]); 47 | curr.add(nums[j]); 48 | curr.add(nums[k]); 49 | result.add(curr); 50 | j++; 51 | k--; 52 | while (j < nums.length && nums[j] == nums[j - 1]) j++; 53 | while (k > j && nums[k] == nums[k + 1]) j--; 54 | } else if (nums[j] + nums[k] > target) { 55 | k--; 56 | } else { 57 | j++; 58 | } 59 | } 60 | } 61 | return result; 62 | 63 | } 64 | 65 | public static void main(String[] args) { 66 | System.out.println(ThreeSum.threeSum(new int[] {-1, 0, 1, 2, -1, -4})); 67 | } 68 | 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/awaken/leetcode/array/FindLadders.java: -------------------------------------------------------------------------------- 1 | package com.awaken.leetcode.array; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * @Auther: Noseparte 7 | * @Date: 2020/5/18 5:56 下午 8 | * @Description: 9 | * 10 | *

leetcode 面试题 17.22. 单词转换

11 | */ 12 | public class FindLadders { 13 | 14 | List wordList; 15 | boolean[] marked; 16 | List output; 17 | String endWord; 18 | List result; 19 | 20 | public List findLadders(String beginWord, String endWord, List wordList) { 21 | this.wordList = wordList; 22 | output = new ArrayList(); 23 | marked = new boolean[wordList.size()]; 24 | result = new ArrayList(); 25 | this.endWord = endWord; 26 | dfs(beginWord); 27 | return result; 28 | } 29 | 30 | public void dfs(String s) { 31 | output.add(s); 32 | Queue queue = oneCharDiff(s); 33 | for (String str : queue) { 34 | if (str.equals(endWord)) { 35 | output.add(str); 36 | result = new ArrayList(output); 37 | return; 38 | } 39 | dfs(str); 40 | output.remove(output.size() - 1); 41 | } 42 | 43 | } 44 | 45 | public Queue oneCharDiff(String s) { 46 | Queue queue = new LinkedList(); 47 | for (int j = 0; j < wordList.size(); j++) { 48 | String str = wordList.get(j); 49 | int diffNum = 0; 50 | if (str.length() != s.length() || marked[j]) continue; 51 | for (int i = 0; i < str.length(); i++) { 52 | if (diffNum >= 2) break; 53 | if (str.charAt(i) != s.charAt(i)) diffNum++; 54 | } 55 | if (diffNum == 1) { 56 | queue.add(str); 57 | marked[j] = true; 58 | } 59 | } 60 | return queue; 61 | } 62 | 63 | public static void main(String[] args) { 64 | List ladders = new FindLadders().findLadders("hit", "cog", Arrays.asList("hot", "dot", "dog", "lot", "log", "cog")); 65 | System.out.println(ladders); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/awaken/leetcode/hash/HappyNumber.java: -------------------------------------------------------------------------------- 1 | package com.awaken.leetcode.hash; 2 | 3 | import java.util.Arrays; 4 | import java.util.HashSet; 5 | import java.util.Set; 6 | 7 | /** 8 | * @Auther: Noseparte 9 | * @Date: 2020/4/30 12:28 下午 10 | * @Description: 11 | * 12 | *

leetcode 202.快乐数

13 | * 14 | *

编写一个算法来判断一个数 n 是不是快乐数。

15 | *

「快乐数」定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1, 16 | * 也可能是 无限循环 但始终变不到 1。如果 可以变为  1,那么这个数就是快乐数。

17 | *

如果 n 是快乐数就返回 True ;不是,则返回 False 。

18 | * 19 | * 示例: 20 | * 输入:19 21 | * 输出:true 22 | * 解释: 23 | * 12 + 92 = 82 24 | * 82 + 22 = 68 25 | * 62 + 82 = 100 26 | * 12 + 02 + 02 = 1 27 | */ 28 | public class HappyNumber { 29 | 30 | //方法一、用HashSet检测循环 31 | public static boolean getHappyNumber(int num) { 32 | Set seen = new HashSet<>(); 33 | while (num != 1 && !seen.contains(num)) { 34 | seen.add(num); 35 | num = getNext(num); 36 | } 37 | return num == 1; 38 | } 39 | 40 | //方法二、单向链表法 41 | //快慢指针法 42 | public boolean isHappy(int n) { 43 | int slowRunner = n; 44 | int fastRunner = getNext(n); 45 | while (fastRunner != 1 && slowRunner != fastRunner) { 46 | slowRunner = getNext(slowRunner); 47 | fastRunner = getNext(getNext(fastRunner)); 48 | } 49 | return fastRunner == 1; 50 | } 51 | 52 | private static final Set cycleMembers = new HashSet<>(Arrays.asList(4, 16, 37, 58, 89, 145, 42, 20)); 53 | //方法三、数学法 54 | public boolean isHappyMath(int n) { 55 | while (n != 1 && !cycleMembers.contains(n)) { 56 | n = getNext(n); 57 | } 58 | return n == 1; 59 | } 60 | 61 | 62 | private static int getNext(int n) { 63 | int next = 0; 64 | while (n > 0) { 65 | int d = n % 10;//个位数 66 | n = n / 10; 67 | next += d ^ 2; 68 | } 69 | return next; 70 | } 71 | 72 | public static void main(String[] args) { 73 | boolean isHappy = HappyNumber.getHappyNumber(14); 74 | System.out.println(isHappy); 75 | } 76 | 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/awaken/leetcode/multithreading/FizzBuzz.java: -------------------------------------------------------------------------------- 1 | package com.awaken.leetcode.multithreading; 2 | 3 | import java.util.concurrent.BrokenBarrierException; 4 | import java.util.concurrent.CyclicBarrier; 5 | import java.util.function.IntConsumer; 6 | 7 | public class FizzBuzz { 8 | 9 | private int n; 10 | 11 | public FizzBuzz(int n) { 12 | this.n = n; 13 | } 14 | 15 | private static CyclicBarrier barrier = new CyclicBarrier(4); 16 | 17 | // printFizz.run() outputs "fizz". 18 | public void fizz(Runnable printFizz) throws InterruptedException { 19 | for (int i = 1; i <= n; i++) { 20 | if (i % 3 == 0 && i % 5 != 0) { 21 | printFizz.run(); 22 | } 23 | try { 24 | barrier.await(); 25 | } catch (BrokenBarrierException e) { 26 | e.printStackTrace(); 27 | } 28 | } 29 | } 30 | 31 | // printBuzz.run() outputs "buzz". 32 | public void buzz(Runnable printBuzz) throws InterruptedException { 33 | for (int i = 1; i <= n; i++) { 34 | if (i % 3 != 0 && i % 5 == 0) { 35 | printBuzz.run(); 36 | } 37 | try { 38 | barrier.await(); 39 | } catch (BrokenBarrierException e) { 40 | e.printStackTrace(); 41 | } 42 | } 43 | 44 | } 45 | 46 | // printFizzBuzz.run() outputs "fizzbuzz". 47 | public void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException { 48 | for (int i = 1; i <= n; i++) { 49 | if (i % 3 == 0 && i % 5 == 0) { 50 | printFizzBuzz.run(); 51 | } 52 | try { 53 | barrier.await(); 54 | } catch (BrokenBarrierException e) { 55 | e.printStackTrace(); 56 | } 57 | } 58 | 59 | } 60 | 61 | // printNumber.accept(x) outputs "x", where x is an integer. 62 | public void number(IntConsumer printNumber) throws InterruptedException { 63 | for (int i = 1; i <= n; i++) { 64 | if (i % 3 != 0 && i % 5 != 0) { 65 | printNumber.accept(i); 66 | } 67 | try { 68 | barrier.await(); 69 | } catch (BrokenBarrierException e) { 70 | e.printStackTrace(); 71 | } 72 | } 73 | } 74 | 75 | 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/awaken/leetcode/array/KidsWithCandies.java: -------------------------------------------------------------------------------- 1 | package com.awaken.leetcode.array; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * @Auther: Noseparte 8 | * @Date: 2020/5/8 1:08 下午 9 | * @Description: 10 | * 11 | *

12 | * 1431. 拥有最多糖果的孩子 13 | * 14 | * 给你一个数组 candies 和一个整数 extraCandies ,其中 candies[i] 代表第 i 个孩子拥有的糖果数目。 15 | * 对每一个孩子,检查是否存在一种方案,将额外的 extraCandies 个糖果分配给孩子们之后,此孩子有 最多 的糖果。 16 | * 注意,允许有多个孩子同时拥有 最多 的糖果数目。 17 | *

18 | * 19 | *

20 | * 示例 1: 21 | * 22 | * 输入:candies = [2,3,5,1,3], extraCandies = 3 23 | * 输出:[true,true,true,false,true] 24 | * 解释: 25 | * 孩子 1 有 2 个糖果,如果他得到所有额外的糖果(3个),那么他总共有 5 个糖果,他将成为拥有最多糖果的孩子。 26 | * 孩子 2 有 3 个糖果,如果他得到至少 2 个额外糖果,那么他将成为拥有最多糖果的孩子。 27 | * 孩子 3 有 5 个糖果,他已经是拥有最多糖果的孩子。 28 | * 孩子 4 有 1 个糖果,即使他得到所有额外的糖果,他也只有 4 个糖果,无法成为拥有糖果最多的孩子。 29 | * 孩子 5 有 3 个糖果,如果他得到至少 2 个额外糖果,那么他将成为拥有最多糖果的孩子。 30 | * 31 | * 示例 2: 32 | * 33 | * 输入:candies = [4,2,1,1,2], extraCandies = 1 34 | * 输出:[true,false,false,false,false] 35 | * 解释:只有 1 个额外糖果,所以不管额外糖果给谁,只有孩子 1 可以成为拥有糖果最多的孩子。 36 | * 37 | * 示例 3: 38 | * 39 | * 输入:candies = [12,1,12], extraCandies = 10 40 | * 输出:[true,false,true] 41 | * 42 | * 提示: 43 | * 44 | * 2 <= candies.length <= 100 45 | * 1 <= candies[i] <= 100 46 | * 1 <= extraCandies <= 50 47 | * 48 | *

49 | */ 50 | public class KidsWithCandies { 51 | 52 | public List kidsWithCandies(int[] candies, int extraCandies) { 53 | 54 | List results = new ArrayList<>(); 55 | 56 | int max = candies[0]; 57 | 58 | for (int i = 1; i < candies.length; i++) { 59 | if (candies[i] > max) { 60 | max = candies[i]; 61 | } 62 | } 63 | 64 | for (int candy : candies) { 65 | results.add(extraCandies + candy >= max); 66 | } 67 | 68 | return results; 69 | } 70 | 71 | public static void main(String[] args) { 72 | List candies = new KidsWithCandies().kidsWithCandies(new int[]{4, 2, 1, 1, 2}, 1); 73 | System.out.println(candies); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/awaken/leetcode/array/ShipWithinDays.java: -------------------------------------------------------------------------------- 1 | package com.awaken.leetcode.array; 2 | 3 | /** 4 | * @Auther: Noseparte 5 | * @Date: 2020/5/7 7:40 下午 6 | * @Description: 7 | * 8 | *

9 | * 1011. 在 D 天内送达包裹的能力 10 | * 11 | * 传送带上的包裹必须在 D 天内从一个港口运送到另一个港口。 12 | * 传送带上的第 i 个包裹的重量为 weights[i]。每一天,我们都会按给出重量的顺序往传送带上装载包裹。我们装载的重量不会超过船的最大运载重量。 13 | * 返回能在 D 天内将传送带上的所有包裹送达的船的最低运载能力。 14 | *

15 | * 16 | */ 17 | public class ShipWithinDays { 18 | 19 | 20 | public int shipWithinDays(int[] weights, int D) { 21 | int lo = 0, hi = Integer.MAX_VALUE; 22 | while (lo < hi) { 23 | int mid = lo + (hi - lo) / 2; 24 | if (canShip(weights, D, mid)) { 25 | hi = mid; 26 | } else { 27 | lo = mid+1; 28 | } 29 | } 30 | return lo; 31 | } 32 | 33 | /** 34 | * 定义函数canShip(D, K),来判断在最低承载力为K的情形下能否在DD天内送达所有包裹。我们所要做的就是按照传送带上货物的顺序,依次且尽可能多地往船上装载货物,当该艘船无法装下更多货物时,我们换一搜船,同时将天数加1。当运输完所有货物后,我们判断所用的天数是否小于等于D。 35 | * 用二分查找的方式,来查找这个最低承载力,如果midmid可以完成任务,我们把查找范围缩减至[lo, mid][lo,mid](注意不是mid+1mid+1,因为mid可能是我们所求的解),否则我们去[mid+1, hi][mid+1,hi]区间中继续查找,详情见代码。 36 | * 37 | */ 38 | private boolean canShip(int[] weights, int D, int K) { 39 | int cur = K; // cur 表示当前船的可用承载量 40 | for (int weight: weights) { 41 | if (weight > K) return false; 42 | if (cur < weight) { 43 | cur = K; 44 | D--; 45 | } 46 | cur -= weight; 47 | } 48 | return D > 0; // 能否在D天内运完 49 | } 50 | 51 | /** 52 | * 示例 1: 53 | * 54 | * 输入:weights = [1,2,3,4,5,6,7,8,9,10], D = 5 55 | * 输出:15 56 | * 解释: 57 | * 船舶最低载重 15 就能够在 5 天内送达所有包裹,如下所示: 58 | * 第 1 天:1, 2, 3, 4, 5 59 | * 第 2 天:6, 7 60 | * 第 3 天:8 61 | * 第 4 天:9 62 | * 第 5 天:10 63 | * 64 | * 请注意,货物必须按照给定的顺序装运,因此使用载重能力为 14 的船舶并将包装分成 (2, 3, 4, 5), (1, 6, 7), (8), (9), (10) 是不允许的。 65 | * 66 | * 67 | * 示例 2: 68 | * 输入:weights = [3,2,2,4,1,4], D = 3 69 | * 输出:6 70 | * 解释: 71 | * 船舶最低载重 6 就能够在 3 天内送达所有包裹,如下所示: 72 | * 第 1 天:3, 2 73 | * 第 2 天:2, 4 74 | * 第 3 天:1, 4 75 | * 76 | * 77 | * 示例 3: 78 | * 79 | * 输入:weights = [1,2,3,1,1], D = 4 80 | * 输出:3 81 | * 解释: 82 | * 第 1 天:1 83 | * 第 2 天:2 84 | * 第 3 天:3 85 | * 第 4 天:1, 1 86 | *   87 | * 88 | * 提示: 89 | * 90 | * 1 <= D <= weights.length <= 50000 91 | * 1 <= weights[i] <= 500 92 | * 93 | */ 94 | public static void main(String[] args) { 95 | int ship = new ShipWithinDays().shipWithinDays(new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 5); 96 | System.out.println(ship); 97 | } 98 | 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/com/awaken/leetcode/design/LRUCache.java: -------------------------------------------------------------------------------- 1 | package com.awaken.leetcode.design; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * @Auther: Noseparte 8 | * @Date: 2020/5/25 6:33 下午 9 | * @Description: 10 | * 11 | *

146. LRU缓存机制

12 | */ 13 | public class LRUCache { 14 | 15 | /** 16 | * Your LRUCache object will be instantiated and called as such: 17 | * LRUCache obj = new LRUCache(capacity); 18 | * int param_1 = obj.get(key); 19 | * obj.put(key,value); 20 | */ 21 | static class DLinkedNode { 22 | int key; 23 | int value; 24 | DLinkedNode prev; 25 | DLinkedNode next; 26 | public DLinkedNode() {} 27 | public DLinkedNode(int _key, int _value) {key = _key; value = _value;} 28 | } 29 | 30 | private Map cache = new HashMap(); 31 | private int size; 32 | private int capacity; 33 | private DLinkedNode head, tail; 34 | 35 | public LRUCache(int capacity) { 36 | this.size = 0; 37 | this.capacity = capacity; 38 | // 使用伪头部和伪尾部节点 39 | head = new DLinkedNode(); 40 | tail = new DLinkedNode(); 41 | head.next = tail; 42 | tail.prev = head; 43 | } 44 | 45 | public int get(int key) { 46 | DLinkedNode node = cache.get(key); 47 | if (node == null) { 48 | return -1; 49 | } 50 | // 如果 key 存在,先通过哈希表定位,再移到头部 51 | moveToHead(node); 52 | return node.value; 53 | } 54 | 55 | public void put(int key, int value) { 56 | DLinkedNode node = cache.get(key); 57 | if (node == null) { 58 | // 如果 key 不存在,创建一个新的节点 59 | DLinkedNode newNode = new DLinkedNode(key, value); 60 | // 添加进哈希表 61 | cache.put(key, newNode); 62 | // 添加至双向链表的头部 63 | addToHead(newNode); 64 | ++size; 65 | if (size > capacity) { 66 | // 如果超出容量,删除双向链表的尾部节点 67 | DLinkedNode tail = removeTail(); 68 | // 删除哈希表中对应的项 69 | cache.remove(tail.key); 70 | --size; 71 | } 72 | } 73 | else { 74 | // 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部 75 | node.value = value; 76 | moveToHead(node); 77 | } 78 | } 79 | 80 | private void addToHead(DLinkedNode node) { 81 | node.prev = head; 82 | node.next = head.next; 83 | head.next.prev = node; 84 | head.next = node; 85 | } 86 | 87 | private void removeNode(DLinkedNode node) { 88 | node.prev.next = node.next; 89 | node.next.prev = node.prev; 90 | } 91 | 92 | private void moveToHead(DLinkedNode node) { 93 | removeNode(node); 94 | addToHead(node); 95 | } 96 | 97 | private DLinkedNode removeTail() { 98 | DLinkedNode res = tail.prev; 99 | removeNode(res); 100 | return res; 101 | } 102 | 103 | 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/com/awaken/leetcode/array/CombinationSum.java: -------------------------------------------------------------------------------- 1 | package com.awaken.leetcode.array; 2 | 3 | import java.util.*; 4 | 5 | 6 | /** 7 | * @author Kirago 8 | * @date 2020/9/10 16:44 9 | * @description https://leetcode-cn.com/problems/combination-sum-ii/ 10 | * @version version-1.0 11 | */ 12 | 13 | 14 | public class CombinationSum { 15 | 16 | // private int[] candinates; 17 | // 18 | // private List> res = new ArrayList<>(); 19 | // 20 | // private int target; 21 | // 22 | // 23 | // public List> combinationSum2(int[] candinates, int target){ 24 | // this.candinates = candinates; 25 | // this.target = target; 26 | // 27 | // if(candinates == null || candinates.length == 0) return res; 28 | // 29 | // Arrays.sort(candinates); 30 | // 31 | // List list = new ArrayList<>(); 32 | // 33 | // int len = candinates.length; 34 | // 35 | // inner(0, len, 0, list); 36 | // return res; 37 | // } 38 | // 39 | // private void inner(int index, int len, int sum, List list){ 40 | // if(sum == target){ 41 | // List item = new ArrayList<>(); 42 | // item.addAll(list); 43 | // res.add(item); 44 | // }else if(sum < target && index < len){ 45 | // for(int i=index;i path, List> res) { 59 | if (residue == 0) { 60 | res.add(new ArrayList<>(path)); 61 | return; 62 | } 63 | for (int i = begin; i < len; i++) { 64 | // 大剪枝 65 | if (residue - candidates[i] < 0) { 66 | break; 67 | } 68 | 69 | // 小剪枝 70 | if (i > begin && candidates[i] == candidates[i - 1]) { 71 | continue; 72 | } 73 | 74 | path.addLast(candidates[i]); 75 | 76 | // 因为元素不可以重复使用,这里递归传递下去的是 i + 1 而不是 i 77 | dfs(candidates, len, i + 1, residue - candidates[i], path, res); 78 | 79 | path.removeLast(); 80 | } 81 | } 82 | 83 | public List> combinationSum2(int[] candidates, int target) { 84 | int len = candidates.length; 85 | List> res = new ArrayList<>(); 86 | if (len == 0) { 87 | return res; 88 | } 89 | 90 | // 先将数组排序,这一步很关键 91 | Arrays.sort(candidates); 92 | 93 | Deque path = new ArrayDeque<>(len); 94 | dfs(candidates, len, 0, target, path, res); 95 | return res; 96 | } 97 | 98 | public static void main(String[] args){ 99 | int[] nums = new int[]{2,5,2,1,2}; 100 | CombinationSum combinationSum = new CombinationSum(); 101 | combinationSum.combinationSum2(nums, 5); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/com/awaken/leetcode/dynamic/MinCostTickets.java: -------------------------------------------------------------------------------- 1 | package com.awaken.leetcode.dynamic; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | /** 7 | * @Auther: Noseparte 8 | * @Date: 2020/5/6 1:47 下午 9 | * @Description: 10 | * 11 | *

12 | * 在一个火车旅行很受欢迎的国度,你提前一年计划了一些火车旅行。在接下来的一年里,你要旅行的日子将以一个名为 days 的数组给出。 13 | * 每一项是一个从 1 到 365 的整数。 14 | * 15 | * 火车票有三种不同的销售方式: 16 | * * 一张为期一天的通行证售价为 costs[0] 美元; 17 | * * 一张为期七天的通行证售价为 costs[1] 美元; 18 | * * 一张为期三十天的通行证售价为 costs[2] 美元。 19 | * 20 | * 通行证允许数天无限制的旅行。 例如,如果我们在第 2 天获得一张为期 7 天的通行证,那么我们可以连着旅行 7 天:第 2 天、第 3 天、第 4 天、第 5 天、第 6 天、第 7 天和第 8 天。 21 | * 22 | * 返回你想要完成在给定的列表 days 中列出的每一天的旅行所需要的最低消费。 23 | * 24 | *

25 | * 26 | * 示例 1: 27 | * 28 | * 输入:days = [1,4,6,7,8,20], costs = [2,7,15] 29 | * 输出:11 30 | * 解释: 31 | * 例如,这里有一种购买通行证的方法,可以让你完成你的旅行计划: 32 | * 在第 1 天,你花了 costs[0] = $2 买了一张为期 1 天的通行证,它将在第 1 天生效。 33 | * 在第 3 天,你花了 costs[1] = $7 买了一张为期 7 天的通行证,它将在第 3, 4, ..., 9 天生效。 34 | * 在第 20 天,你花了 costs[0] = $2 买了一张为期 1 天的通行证,它将在第 20 天生效。 35 | * 你总共花了 $11,并完成了你计划的每一天旅行。 36 | * 37 | * 示例 2: 38 | * 39 | * 输入:days = [1,2,3,4,5,6,7,8,9,10,30,31], costs = [2,7,15] 40 | * 输出:17 41 | * 解释: 42 | * 例如,这里有一种购买通行证的方法,可以让你完成你的旅行计划: 43 | * 在第 1 天,你花了 costs[2] = $15 买了一张为期 30 天的通行证,它将在第 1, 2, ..., 30 天生效。 44 | * 在第 31 天,你花了 costs[0] = $2 买了一张为期 1 天的通行证,它将在第 31 天生效。 45 | * 你总共花了 $17,并完成了你计划的每一天旅行。 46 | * 47 | */ 48 | public class MinCostTickets { 49 | 50 | int[] costs; 51 | Integer[] memo; 52 | Set daySet; 53 | 54 | /** 55 | * 方法一:记忆化搜索(日期变量型) 56 | * 思路和算法 57 | * 58 | * 我们用 \textit{dp}(i)dp(i) 来表示从第 ii 天开始到一年的结束,我们需要花的钱。考虑到一张通行证可以让我们在「接下来」的若干天进行旅行,所以我们「从后往前」倒着进行动态规划。 59 | * 60 | * 对于一年中的任意一天: 61 | * 62 | * 如果这一天不是必须出行的日期,那我们可以贪心地选择不买。这是因为如果今天不用出行,那么也不必购买通行证,并且通行证越晚买越好。所以有 \textit{dp}(i) = \textit{dp}(i + 1)dp(i)=dp(i+1); 63 | * 64 | * 如果这一天是必须出行的日期,我们可以选择买 11,77 或 3030 天的通行证。若我们购买了 jj 天的通行证,那么接下来的 j - 1j−1 天,我们都不再需要购买通行证,只需要考虑第 i + ji+j 天及以后即可。因此,我们有 65 | * 66 | * \textit{dp}(i) = \min\{\textit{cost}(j) + \textit{dp}(i + j)\}, \quad j \in \{1, 7, 30\} 67 | * dp(i)=min{cost(j)+dp(i+j)},j∈{1,7,30} 68 | * 69 | * 其中 \textit{cost}(j)cost(j) 表示 jj 天通行证的价格。为什么我们只需要考虑第 i+ji+j 天及以后呢?这里和第一条的贪心思路是一样的,如果我们需要购买通行证,那么一定越晚买越好,在握着一张有效的通行证的时候购买其它的通行证显然是不划算的。 70 | * 71 | * 由于我们是倒着进行动态规划的,因此我们可以使用记忆化搜索,减少代码的编写难度。我们使用一个长度为 366366 的数组(因为天数是 [1, 365][1,365],而数组的下标是从 00 开始的)存储所有的动态规划结果,这样所有的 \textit{dp}(i)dp(i) 只会被计算一次(和普通的动态规划相同),时间复杂度不会增大。 72 | * 73 | * 最终的答案记为 \textit{dp}(1)dp(1)。 74 | * 75 | * @param days 76 | * @param costs 77 | * @return 78 | */ 79 | public int minCostTickets(int[] days, int[] costs) { 80 | this.costs = costs; 81 | memo = new Integer[366]; 82 | daySet = new HashSet(); 83 | for (int d : days) { 84 | daySet.add(d); 85 | } 86 | return dp(1); 87 | } 88 | 89 | public int dp(int i) { 90 | if (i > 365) { 91 | return 0; 92 | } 93 | if (memo[i] != null) { 94 | return memo[i]; 95 | } 96 | if (daySet.contains(i)) { 97 | memo[i] = Math.min(Math.min(dp(i + 1) + costs[0], dp(i + 7) + costs[1]), dp(i + 30) + costs[2]); 98 | } else { 99 | memo[i] = dp(i + 1); 100 | } 101 | return memo[i]; 102 | } 103 | 104 | /** 105 | * 复杂度分析 106 | * 107 | * 时间复杂度:O(W)O(W),其中 W = 365W=365 是旅行计划中日期的最大值,我们需要计算 WW 个解,而每个解最多需要查询 33 个其他的解,因此计算量为 O(3 * W)=O(W)O(3∗W)=O(W)。 108 | * 109 | * 空间复杂度:O(W)O(W),我们需要长度为 O(W)O(W) 的数组来存储所有的解。 110 | */ 111 | 112 | public static void main(String[] args) { 113 | int minCostTickets = new MinCostTickets().minCostTickets(new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 30, 31}, new int[]{2, 7, 15}); 114 | System.out.println(minCostTickets); 115 | } 116 | 117 | 118 | } 119 | --------------------------------------------------------------------------------