├── .gitignore ├── images ├── amazon.png ├── google.png ├── facebook.png └── microsoft.png └── src ├── Other ├── BasicAlgorithm │ ├── _15_UniqueStack │ │ └── Solution.java │ ├── _24_CopyListWithRandom │ │ └── Solution.java │ ├── _08_ArrayDivision │ │ └── Solution.java │ ├── _05_Recursive │ │ └── Solution.java │ ├── _21_FindNumInSortedMatrix │ │ └── Solution.java │ ├── _39_Factorial │ │ └── Solution.java │ ├── _40_TowerOfHanoi │ │ └── Solution.java │ ├── _47_CompletenessKnapsackProblem │ │ └── Solution.java │ ├── _46_01KnapsackProblemOptimizes │ │ └── Solution.java │ ├── _19_ReverseList │ │ └── Solution.java │ ├── _13_ArrayStack │ │ └── Solution.java │ ├── _20_ZigZagPrintMatrix │ │ └── Solution.java │ └── _35_LowestLexicography │ │ └── Solution.java ├── Other │ ├── FoldedPaper.java │ ├── DeleteMidNodeOfList.java │ ├── FlipAndInvertImage.java │ ├── ListHasCycle.java │ ├── ArrayPartition.java │ ├── GetLessIndex.java │ └── GetMaxLength.java ├── BasicInputOutput │ ├── StringSort4.java │ ├── APlusB1.java │ ├── APlusB2.java │ ├── APlusB3.java │ ├── APlusB4.java │ ├── APlusB6.java │ ├── APlusB5.java │ ├── APlusB7.java │ ├── StringSort3.java │ ├── StringSort2.java │ └── StringSort1.java ├── BasicDataStructure │ └── Knapsack.java └── AdvancedAlgorithm │ ├── _18_MaxHappy │ └── Solution.java │ ├── _14_02_LongestSumSubArrayLength │ └── Solution.java │ ├── _27_LongestSubstring │ └── Solution.java │ └── _15_MostEOR │ └── Solution.java ├── LeetCodeSolution ├── AlgorithmThought │ ├── _08_Mathematics │ │ ├── _233_NumberOfDigitOne │ │ │ └── Solution.java │ │ ├── _136_SingleNumber │ │ │ └── Solution.java │ │ ├── _9_PalindromeNumber │ │ │ └── Solution.java │ │ └── _400_NthDigit │ │ │ └── Solution.java │ ├── _03_Greedy │ │ ├── _121_BestTimeToBuyAndSellStock │ │ │ └── Solution.java │ │ ├── _122_BestTimeToBuyAndSellStockII │ │ │ └── Solution.java │ │ ├── _455_AssignCookies │ │ │ └── Solution.java │ │ ├── _392_IsSubsequence │ │ │ └── Solution.java │ │ ├── _452_MinimumNumberOfArrowsToBurstBalloons │ │ │ └── Solution.java │ │ ├── _665_NonDecreasingArray │ │ │ └── Solution.java │ │ └── _45_JumpGameII │ │ │ └── Solution.java │ ├── _01_DoublePointer │ │ ├── _141_LinkedListCycle │ │ │ └── Solution.java │ │ ├── _633_SumOfSquare │ │ │ └── Solution.java │ │ ├── _11_ContainerWithMostWater │ │ │ └── Solution.java │ │ ├── _415_AddStrings │ │ │ └── Solution.java │ │ ├── _167_TwoSumII │ │ │ └── Solution.java │ │ ├── _16_3SumClosest │ │ │ └── Solution.java │ │ └── _125_Valid_Palindrome │ │ │ └── Solution.java │ ├── _02_DP │ │ ├── _343_IntegerBreak │ │ │ └── Solution.java │ │ ├── _377_Combination_SumIV │ │ │ └── Solution.java │ │ ├── _322_CoinChange │ │ │ └── Solution.java │ │ ├── _123_BestTimeToBuyAndSellStockIII │ │ │ └── Solution.java │ │ ├── _139_WordBreak │ │ │ └── Solution.java │ │ ├── _509_FibonacciNumber │ │ │ └── Solution.java │ │ ├── _70_ClimbingStairs │ │ │ └── Solution.java │ │ └── _1143_LongestCommonSubsequence │ │ │ └── Solution.java │ ├── _04_BinarySearch │ │ ├── _278_FirstBadVersion │ │ │ └── Solution.java │ │ ├── _153_FindMinimumInRotatedSortedArray │ │ │ └── Solution.java │ │ ├── _69_Sqrt_x │ │ │ └── Solution.java │ │ ├── _744_FindSmallestLetterGreaterThanTarget │ │ │ └── Solution.java │ │ ├── _540_SingleElementInASortedArray │ │ │ └── Solution.java │ │ └── _35_SearchInserrPosition │ │ │ └── Solution.java │ ├── _06_Search │ │ ├── _78_Subsets │ │ │ └── Solution.java │ │ ├── _112_PathSum │ │ │ └── Solution.java │ │ ├── _437_PathSumIII │ │ │ └── Solution.java │ │ ├── _77_Combinations │ │ │ └── Solution.java │ │ ├── _113_PathSumII │ │ │ └── Solution.java │ │ └── _216_CombinationSumIII │ │ │ └── Solution.java │ └── _07_Sort │ │ ├── _75_SortColors │ │ └── Solution.java │ │ └── _347_TopKFrequentElements │ │ └── Solution.java └── DataStructure │ ├── _03_Tree │ ├── _538_ConvertBSTToGreaterTree │ │ └── Solution.java │ ├── _100_SameTree │ │ └── Solution.java │ ├── _617_MergeTwoBinaryTrees │ │ └── Solution.java │ ├── _112_PathSum │ │ └── Solution.java │ ├── _235_LowestCommonAncestorOfABinarySearchTree │ │ └── Solution.java │ ├── _111_MinimumDepthOfBinaryTree │ │ └── Solution.java │ ├── _108_ConvertSortedArrayToBinarySearchTree │ │ └── Solution.java │ ├── _437_PathSumIII │ │ └── Solution.java │ ├── _94_BinaryTreeInorderTraversal │ │ └── Solution.java │ ├── _543_DiameterOfBinaryTree │ │ └── Solution.java │ ├── _669_TrimABinarySearchTree │ │ └── Solution.java │ ├── _530_MinimumAbsoluteDifferenceInBST │ │ └── Solution.java │ ├── _144_BinaryTreePreorderTraversal │ │ └── Solution.java │ ├── _113_PathSumII │ │ └── Solution.java │ ├── _298_BinaryTreeLongestConsecutiveSequence │ │ └── Solution.java │ ├── _513_FindBottomLeftTreeValue │ │ └── Solution.java │ ├── _145_BinaryTreePostorderTraversal │ │ └── Solution.java │ ├── _572_SubtreeOfAnotherTree │ │ └── Solution.java │ ├── _337_HouseRobberIII │ │ └── Solution.java │ ├── _236_LowestCommonAncestorOfABinaryTree │ │ └── Solution.java │ ├── _653_TwoSumIVInputIsABST │ │ └── Solution.java │ ├── _637_AverageOfLevelsInBinaryTree │ │ └── Solution.java │ ├── _687_LongestUnivaluePath │ │ └── Solution.java │ └── _124_BinaryTreeMaximumPathSum │ │ └── Solution.java │ ├── _08_LinkedList │ ├── _83_RemoveDuplicatesFromSortedList │ │ └── Solution.java │ ├── _203_RemoveLinkedListElements │ │ └── Solution.java │ ├── _160_IntersectionOfTwoLinkedLists │ │ └── Solution.java │ ├── _142_LinkedListCycleII │ │ └── Solution.java │ ├── _92_ReverseLinkedListII │ │ └── Solution.java │ ├── _24_SwapNodesInPairs │ │ └── Solution.java │ ├── _82_RemoveDuplicatesFromSortedListII │ │ └── Solution.java │ ├── _1171_RemoveZeroSumConsecutiveNodesFromLinkedList │ │ └── Solution.java │ ├── _328_OddEvenLinkedList │ │ └── Solution.java │ └── _86_PartitionList │ │ └── Solution.java │ ├── _02_String │ ├── _9_PalindromeNumber │ │ └── Solution.java │ ├── _14_LongestCommonPrefix │ │ └── Solution.java │ ├── _409_LongestPalindrome │ │ └── Solution.java │ ├── _696_CountBinarySubstrings │ │ └── Solution.java │ ├── _205_IsomorphicStrings │ │ └── Solution.java │ └── _415_AddStrings │ │ └── Solution.java │ ├── _01_ArrayAndMatrix │ ├── _27_RemoveElement │ │ └── Solution.java │ ├── _26_RemoveDuplicatesFromSortedArray │ │ └── Solution.java │ ├── _766_ToeplitzMatrix │ │ └── Solution.java │ ├── _769_MaxChunksToMakeSorted │ │ └── Solution.java │ ├── _718_MaximumLengthOfRepeatedSubarray │ │ └── Solution.java │ ├── _252_MeetingRooms │ │ └── Solution.java │ ├── _1046_LastStoneWeight │ │ └── Solution.java │ ├── _80_RemoveDuplicatesFromSortedArrayII │ │ └── Solution.java │ ├── _154_FindMinimumInRotatedSortedArrayII │ │ └── Solution.java │ ├── _566_ReshapeTheMatrix │ │ └── Solution.java │ ├── _240_SearchA2DMatrixII │ │ └── Solution.java │ ├── _1049_LastStoneWeightII │ │ └── Solution.java │ ├── _118_PascalsTriangle │ │ └── Solution.java │ ├── _243_ShortestWordDistance │ │ └── Solution.java │ ├── _228_SummaryRanges │ │ └── Solution.java │ └── _59_SpiralMatrixII │ │ └── Solution.java │ ├── _04_HashTable │ └── _594_LongestHarmoniousSubsequence │ │ └── Solution.java │ └── _05_StackAndQueue │ ├── _232_ImplementQueueUsingStacks │ └── Solution.java │ ├── _225_ImplementStackUsingQueues │ └── Solution.java │ ├── _155_MinStack │ └── Solution.java │ └── _503_NextGreaterElementII │ └── Solution.java └── SwordToOfferSolution ├── _43_NumberOf1Between1AndN └── Solution.java ├── _18_01_DeleteNodeInList └── Solution2.java ├── _10_03_ClimbingStairsII └── Solution.java ├── _23_EntryNodeInListLoop ├── Solution.java └── Solution2.java ├── _18_02_DeleteDuplicatedNode └── Solution2.java ├── _41_02_FirstAppearingOnce └── Solution.java ├── _66_ConstuctArray └── Solution.java ├── _64_Accumulate └── Solution.java ├── _09_QueueWithTwoStacks └── Solution.java ├── _15_NumberOf1InBinary └── Solution.java ├── _45_SortArrayForMinNumber └── Solution.java ├── _44_DigitsInSequence └── Solution.java ├── _08_NextNodeInBinaryTrees └── Solution.java ├── _10_02_ClimbingStairs └── Solution.java ├── _68_01_CommonParentInTree └── Solution.java ├── _10_01_Fibonacci └── Solution.java ├── _53_03_IntegerIdenticalToIndex └── Solution.java ├── _11_01_MinNumberInRotatedArray └── Solution.java ├── _32_02_PrintTreesInLines └── Solution.java ├── _32_01_PrintTreeFromTopToBottom └── Solution.java ├── _30_MinInStack └── Solution.java └── _56_01_NumbersAppearOnce └── Solution.java /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | 3 | *.iml 4 | 5 | out 6 | 7 | .DS_Store 8 | 9 | */.DS_Store -------------------------------------------------------------------------------- /images/amazon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyfloveslife/LeetCodeAndSwordToOffer/HEAD/images/amazon.png -------------------------------------------------------------------------------- /images/google.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyfloveslife/LeetCodeAndSwordToOffer/HEAD/images/google.png -------------------------------------------------------------------------------- /images/facebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyfloveslife/LeetCodeAndSwordToOffer/HEAD/images/facebook.png -------------------------------------------------------------------------------- /images/microsoft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyfloveslife/LeetCodeAndSwordToOffer/HEAD/images/microsoft.png -------------------------------------------------------------------------------- /src/Other/BasicAlgorithm/_15_UniqueStack/Solution.java: -------------------------------------------------------------------------------- 1 | package Other.BasicAlgorithm._15_UniqueStack; 2 | 3 | /* 4 | * 实现一个特殊的栈,在 pop、push 的基础上,再实现一个返回栈中最小元素的操作,要求时间复杂度都是 O(1)。 5 | * 6 | * 思路: 7 | * https://github.com/dyfloveslife/LeetCodeAndSwordToOffer/blob/master/src/SwordToOfferSolution/_30_MinInStack/Solution.java 8 | */ 9 | public class Solution { 10 | } 11 | -------------------------------------------------------------------------------- /src/Other/Other/FoldedPaper.java: -------------------------------------------------------------------------------- 1 | package Other.Other; 2 | 3 | /** 4 | * 折纸问题 5 | */ 6 | public class FoldedPaper { 7 | public static void printAllFold(int n) { 8 | processFold(1, n, true); 9 | } 10 | 11 | private static void processFold(int i, int n, boolean down) { 12 | if (i > n) return; 13 | 14 | processFold(i + 1, n, true); 15 | System.out.println(down ? "down" : "up"); 16 | processFold(i + 1, n, false); 17 | } 18 | 19 | public static void main(String[] args) { 20 | printAllFold(3); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Other/BasicAlgorithm/_24_CopyListWithRandom/Solution.java: -------------------------------------------------------------------------------- 1 | package Other.BasicAlgorithm._24_CopyListWithRandom; 2 | 3 | /* 4 | * 复制含有随机指针节点的链表 5 | * 6 | * 问题描述: 7 | * 链表中除了有 next 指针以外,还要一个 rand 指针指向链表中任意一个节点,或者指向 null。给定一个无环单链表的头节点 head,完成 8 | * 该链表中所有结构的复制,并返回复制的新链表的头节点。 9 | * 10 | * 进阶: 11 | * 不适用额外的空间,仅使用有限几个变量,且要求时间复杂度为 O(N)。 12 | * 13 | * 思路: 14 | * https://github.com/dyfloveslife/LeetCodeAndSwordToOffer/blob/master/src/SwordToOfferSolution/_35_CopyComplexList/Solution.java 15 | */ 16 | public class Solution { 17 | } 18 | -------------------------------------------------------------------------------- /src/Other/BasicAlgorithm/_08_ArrayDivision/Solution.java: -------------------------------------------------------------------------------- 1 | package Other.BasicAlgorithm._08_ArrayDivision; 2 | 3 | /* 4 | * 数组划分问题 5 | * 给定一个数组 arr 和一个数 num,要求把小于等于 num 的数放在数组的左边,大于 num 的放在数组的右边。 6 | * 7 | * 思路: https://i.loli.net/2019/12/05/W9ZmnI2SE35PL1q.png 8 | * 1. 用 x 表示小于等于 num 的区域,初始值指向数组的 -1 位置,维护一个指针,指向数组的第一个元素; 9 | * 2. 遍历数组 arr 中的每个数,如果当前元素大于 num,则指针直接后移; 10 | * 如果当前元素小于 num,则将当前元素与 x 所表示区域的下一个元素交换,然后 x 所表示的区域就向右扩大一个位置。 11 | * 3. 直到最后,数组 arr 就会被划分成小于等于 num 的在数组左边,大于 num 的在数组右边。 12 | */ 13 | public class Solution { 14 | } 15 | -------------------------------------------------------------------------------- /src/Other/BasicAlgorithm/_05_Recursive/Solution.java: -------------------------------------------------------------------------------- 1 | package Other.BasicAlgorithm._05_Recursive; 2 | 3 | /* 4 | * 计算递归时间复杂度的 Master 公式:T(n)=aT(n/b)+O(n^d) 5 | * 表示一个规模为 n 的问题被分成规模为 n/b 的 a 个子问题,递归的求这 a 个子问题,通过对 a 个子问题的解的综合得到原问题的解。 6 | * 7 | * 对于二分法,递归所用的时间复杂度为 T(n)=2T(n/2)+O(1)。 8 | * 2 表示将原问题划分成了两个子问题; 9 | * n/2 表示子问题的规模是原问题的 n/2; 10 | * O(1) 表示除去调用子过程之外,剩下的过程。 11 | * 12 | * 有三种情况:log(b,a) 表示以 b 为底 a 的对数 13 | * 1) 若 log(b,a) > d,则复杂度为 O(N^log(b,a)) 14 | * 2) 若 log(b,a) = d,则复杂度为 O(N^d*logN) 15 | * 3) 若 log(b,a) < d,则复杂度为 O(N^d) 16 | */ 17 | public class Solution { 18 | } 19 | -------------------------------------------------------------------------------- /src/Other/Other/DeleteMidNodeOfList.java: -------------------------------------------------------------------------------- 1 | package Other.Other; 2 | 3 | /** 4 | * 删除链表的中间节点 5 | */ 6 | public class DeleteMidNodeOfList { 7 | class ListNode{ 8 | int val; 9 | ListNode next; 10 | ListNode(int val) { 11 | this.val = val; 12 | } 13 | } 14 | 15 | public static ListNode deleteMidNodeOfList(ListNode head) { 16 | if (head == null) return null; 17 | 18 | ListNode fastNode = head; 19 | ListNode slowNode = head; 20 | while (fastNode != null && fastNode.next != null) { 21 | fastNode = fastNode.next.next; 22 | slowNode = slowNode.next; 23 | } 24 | return slowNode; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Other/BasicInputOutput/StringSort4.java: -------------------------------------------------------------------------------- 1 | package Other.BasicInputOutput; 2 | 3 | import java.util.Scanner; 4 | 5 | /* 6 | * 需要注意的是:数字越界的情况 7 | * 8 | * 输入: 9 | * 输入有多组测试用例,每组空格隔开两个整数 10 | * 11 | * 输出: 12 | * 对于每组数据输出一行两个整数的和 13 | * 14 | * 示例: 15 | * 输入: 16 | * 1 1 17 | * 18 | * 输出: 19 | * 2 20 | */ 21 | public class StringSort4 { 22 | public static void main(String[] args) { 23 | Scanner sc = new Scanner(System.in); 24 | while (sc.hasNext()) { 25 | long a = sc.nextLong(); 26 | long b = sc.nextLong(); 27 | System.out.println(a + b); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Other/BasicInputOutput/APlusB1.java: -------------------------------------------------------------------------------- 1 | package Other.BasicInputOutput; 2 | 3 | import java.util.Scanner; 4 | 5 | /* 6 | * 题目描述: 7 | * 计算 a + b 8 | * 9 | * 输入: 10 | * 输入包括两个正整数 a,b(1 <= a, b <= 10^9),输入数据包括多组。 11 | * 12 | * 输出: 13 | * 输出 a + b 的结果 14 | * 15 | * 示例: 16 | * 输入: 17 | * 1 5 18 | * 10 20 19 | * 20 | * 输出: 21 | * 6 22 | * 30 23 | */ 24 | public class APlusB1 { 25 | public static void main(String[] args) { 26 | Scanner sc = new Scanner(System.in); 27 | while (sc.hasNext()) { 28 | int a = sc.nextInt(); 29 | int b = sc.nextInt(); 30 | System.out.println(a + b); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Other/Other/FlipAndInvertImage.java: -------------------------------------------------------------------------------- 1 | package Other.Other; 2 | 3 | public class FlipAndInvertImage { 4 | public static int[][] flipAndInvertImage(int[][] A) { 5 | int C = A[0].length; 6 | for (int[] row : A) 7 | for (int i = 0; i < (C + 1) / 2; ++i) { 8 | int tmp = row[i] ^ 1; 9 | row[i] = row[C - 1 - i] ^ 1; 10 | row[C - 1 - i] = tmp; 11 | } 12 | 13 | return A; 14 | } 15 | 16 | public static void main(String[] args) { 17 | int[][] arr = {{1, 1, 0}, {1, 0, 1}, {0, 0, 0}}; 18 | int[][] arr2 = flipAndInvertImage(arr); 19 | for (int[] i : arr2) { 20 | for (int n : i) { 21 | System.out.print(n); 22 | } 23 | System.out.println(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Other/BasicInputOutput/APlusB2.java: -------------------------------------------------------------------------------- 1 | package Other.BasicInputOutput; 2 | 3 | import java.util.Scanner; 4 | 5 | /* 6 | * 题目描述: 7 | * 计算 a + b 8 | * 9 | * 输入: 10 | * 输入第一行包括一个数据组数 t(1 <= t <= 100) 11 | * 接下来每行包括两个正整数 a,b(1 <= a, b <= 10^9) 12 | * 13 | * 输出: 14 | * 输出 a + b 的结果 15 | * 16 | * 示例: 17 | * 输入: 18 | * 2 19 | * 1 5 20 | * 10 20 21 | * 22 | * 输出: 23 | * 6 24 | * 30 25 | */ 26 | public class APlusB2 { 27 | public static void main(String[] args) { 28 | Scanner sc = new Scanner(System.in); 29 | int t = sc.nextInt(); 30 | for (int i = 0; i < t; i++) { 31 | int a = sc.nextInt(); 32 | int b = sc.nextInt(); 33 | System.out.println(a + b); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/Other/Other/ListHasCycle.java: -------------------------------------------------------------------------------- 1 | package Other.Other; 2 | 3 | /** 4 | * 判断链表是否有环 5 | */ 6 | 7 | 8 | public class ListHasCycle { 9 | class ListNode { 10 | int val; 11 | ListNode next = null; 12 | 13 | ListNode(int val) { 14 | this.val = val; 15 | } 16 | } 17 | 18 | public boolean hasCycle(ListNode head) { 19 | if (head == null) return false; 20 | ListNode fastNode = head; 21 | ListNode slowNode = head; 22 | while (fastNode != null && fastNode.next != null) { 23 | fastNode = fastNode.next.next; 24 | slowNode = slowNode.next; 25 | if (fastNode == slowNode) return true; 26 | } 27 | return false; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Other/BasicInputOutput/APlusB3.java: -------------------------------------------------------------------------------- 1 | package Other.BasicInputOutput; 2 | 3 | import java.util.Scanner; 4 | 5 | /* 6 | * 题目描述: 7 | * 计算 a + b 8 | * 9 | * 输入: 10 | * 输入包括两个正整数 a, b(1 <= a, b <= 10^9),输入数据有多组, 如果输入为 0 0 则结束输入 11 | * 12 | * 输出: 13 | * 输出 a + b 的结果 14 | * 15 | * 示例: 16 | * 输入: 17 | * 1 5 18 | * 10 20 19 | * 0 0 20 | * 21 | * 输出: 22 | * 6 23 | * 30 24 | */ 25 | public class APlusB3 { 26 | public static void main(String[] args) { 27 | Scanner sc = new Scanner(System.in); 28 | while (sc.hasNext()) { 29 | int a = sc.nextInt(); 30 | int b = sc.nextInt(); 31 | if (a == 0 && b == 0) { 32 | return; 33 | } 34 | System.out.println(a + b); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/Other/Other/ArrayPartition.java: -------------------------------------------------------------------------------- 1 | package Other.Other; 2 | 3 | public class ArrayPartition { 4 | public static int arrayPairSum(int[] nums) { 5 | for (int i = 0; i < nums.length - 1; i++) { 6 | for (int j = 0; j < nums.length - i - 1; j++) { 7 | if (nums[j] > nums[j + 1]) { 8 | int temp = nums[j]; 9 | nums[j] = nums[j + 1]; 10 | nums[j + 1] = temp; 11 | } 12 | } 13 | } 14 | 15 | for (int num : nums) { 16 | System.out.print(num + " "); 17 | } 18 | System.out.println(); 19 | int sum = 0; 20 | for (int k = 0; k < nums.length - 1; k++) { 21 | if ((k % 2) == 0) sum += nums[k]; 22 | } 23 | return sum; 24 | } 25 | 26 | public static void main(String[] args) { 27 | int[] arr = {1, 4, 3, 2}; 28 | int i = arrayPairSum(arr); 29 | System.out.println(i); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Other/BasicInputOutput/APlusB4.java: -------------------------------------------------------------------------------- 1 | package Other.BasicInputOutput; 2 | 3 | import java.util.Scanner; 4 | 5 | /* 6 | * 题目描述: 7 | * 计算一系列数的和 8 | * 9 | * 输入: 10 | * 输入数据包括多组。 11 | * 每组数据一行, 每行的第一个整数为整数的个数 n(1 <= n <= 100), n 为 0 的时候结束输入。 12 | * 接下来 n 个正整数, 即需要求和的每个正整数。 13 | * 14 | * 输出: 15 | * 每组数据输出求和的结果 16 | * 17 | * 示例: 18 | * 输入: 19 | * 4 1 2 3 4 20 | * 5 1 2 3 4 5 21 | * 0 22 | * 23 | * 输出: 24 | * 10 25 | * 15 26 | */ 27 | public class APlusB4 { 28 | public static void main(String[] args) { 29 | Scanner sc = new Scanner(System.in); 30 | int n; 31 | while ((n = sc.nextInt()) != 0) { 32 | int sum = 0; 33 | for (int i = 0; i < n; i++) { 34 | sum += sc.nextInt(); 35 | } 36 | System.out.println(sum); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/Other/BasicInputOutput/APlusB6.java: -------------------------------------------------------------------------------- 1 | package Other.BasicInputOutput; 2 | 3 | import java.util.Scanner; 4 | 5 | /* 6 | * 题目描述: 7 | * 计算一系列数的和 8 | * 9 | * 输入: 10 | * 输入数据有多组, 每行表示一组输入数据。 11 | * 每行的第一个整数为整数的个数 n(1 <= n <= 100)。 12 | * 接下来 n 个正整数, 即需要求和的每个正整数。 13 | * 14 | * 输出: 15 | * 每组数据输出求和的结果 16 | * 17 | * 示例: 18 | * 输入: 19 | * 4 1 2 3 4 20 | * 5 1 2 3 4 5 21 | * 22 | * 输出: 23 | * 10 24 | * 15 25 | */ 26 | public class APlusB6 { 27 | public static void main(String[] args) { 28 | Scanner sc = new Scanner(System.in); 29 | while (sc.hasNext()) { 30 | int size = sc.nextInt(); 31 | int sum = 0; 32 | for (int i = 0; i < size; i++) { 33 | sum += sc.nextInt(); 34 | } 35 | System.out.println(sum); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Other/BasicAlgorithm/_21_FindNumInSortedMatrix/Solution.java: -------------------------------------------------------------------------------- 1 | package Other.BasicAlgorithm._21_FindNumInSortedMatrix; 2 | 3 | /* 4 | * 在排好序的 N*M 的矩阵中找到指定的数 5 | * 时间复杂度:O(N+M) 6 | * 空间复杂度:O(1) 7 | * 8 | * https://github.com/dyfloveslife/LeetCodeAndSwordToOffer/blob/master/src/SwordToOfferSolution/_04_FindInPartiallySortedMatrix/Solution.java 9 | */ 10 | public class Solution { 11 | public static boolean isContains(int[][] matrix, int K) { 12 | int row = 0; 13 | int col = matrix[0].length - 1; 14 | 15 | while (row < matrix.length && col > -1) { 16 | if (matrix[row][col] == K) { 17 | return true; 18 | } else if (matrix[row][col] > K) { 19 | col--; 20 | } else { 21 | row++; 22 | } 23 | } 24 | return false; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_08_Mathematics/_233_NumberOfDigitOne/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._08_Mathematics._233_NumberOfDigitOne; 2 | 3 | /* 4 | * 数字 1 的个数 5 | * 6 | * 题目描述: 7 | * 给定一个整数 n,计算所有小于等于 n 的非负整数中数字 1 出现的个数。 8 | * 9 | * 思路: 10 | * https://dyfloveslife.github.io/2019/11/28/offer-NumberOf1Between1AndN/ 11 | */ 12 | public class Solution { 13 | 14 | public int countDigitOne(int n) { 15 | int res = 0; 16 | for (long i = 1; i <= n; i *= 10) { 17 | long a = n / i; 18 | long b = n % i; 19 | res += (a + 8) / 10 * i + (a % 10 == 1 ? b + 1 : 0); 20 | } 21 | return res; 22 | } 23 | 24 | public static void main(String[] args) { 25 | Solution solution = new Solution(); 26 | 27 | System.out.println(solution.countDigitOne(13)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Other/BasicAlgorithm/_39_Factorial/Solution.java: -------------------------------------------------------------------------------- 1 | package Other.BasicAlgorithm._39_Factorial; 2 | 3 | /* 4 | * 求 n 的阶乘 5 | */ 6 | public class Solution { 7 | 8 | // 从需要依赖到不需要依赖 9 | public static long getFactorial1(int n) { 10 | // base case 11 | if (n == 1) { 12 | return 1L; 13 | } 14 | return (long) n * getFactorial1(n - 1); 15 | } 16 | 17 | // 我知道怎么计算,直接计算就好了 18 | // 从不需要依赖的问题到依赖的问题 19 | // 即 1 * 2 * 3 ... * n,一开始的值不被依赖,则后面的值需要依赖前面的值 20 | public static long getFactorial2(int n) { 21 | long res = 1L; 22 | for (int i = 1; i <= n; i++) { 23 | res *= i; 24 | } 25 | return res; 26 | } 27 | 28 | public static void main(String[] args) { 29 | int n = 5; 30 | System.out.println(getFactorial1(n)); 31 | System.out.println(getFactorial2(n)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_08_Mathematics/_136_SingleNumber/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._08_Mathematics._136_SingleNumber; 2 | 3 | /* 4 | * 只出现一次的数字 5 | * 6 | * 题目描述: 7 | * 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。 8 | * 找出那个只出现了一次的元素。 9 | * 10 | * 思路: 11 | * 1. 一个数与自己进行异或的结果是 0; 12 | * 2. 一个数与 0 进行异或的结果是该数。 13 | */ 14 | public class Solution { 15 | public int singleNumber(int[] nums) { 16 | if (nums == null || nums.length == 0) { 17 | return -1; 18 | } 19 | 20 | int res = 0; 21 | for (int num : nums) { 22 | res = res ^ num; 23 | } 24 | 25 | return res; 26 | } 27 | 28 | public static void main(String[] args) { 29 | Solution solution = new Solution(); 30 | int[] nums = {4, 1, 2, 1, 2}; 31 | 32 | System.out.println(solution.singleNumber(nums)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Other/BasicInputOutput/APlusB5.java: -------------------------------------------------------------------------------- 1 | package Other.BasicInputOutput; 2 | 3 | import java.util.Scanner; 4 | 5 | /* 6 | * 题目描述: 7 | * 计算一系列数的和 8 | * 9 | * 输入: 10 | * 输入的第一行包括一个正整数 t(1 <= t <= 100), 表示数据组数。 11 | * 接下来 t 行, 每行一组数据。 12 | * 每行的第一个整数为整数的个数 n(1 <= n <= 100)。 13 | * 接下来 n 个正整数, 即需要求和的每个正整数。 14 | * 15 | * 输出: 16 | * 每组数据输出求和的结果 17 | * 18 | * 示例: 19 | * 输入: 20 | * 2 21 | * 4 1 2 3 4 22 | * 5 1 2 3 4 5 23 | * 24 | * 输出: 25 | * 10 26 | * 15 27 | */ 28 | public class APlusB5 { 29 | public static void main(String[] args) { 30 | Scanner sc = new Scanner(System.in); 31 | int t = sc.nextInt(); 32 | while (t-- > 0) { 33 | int sum = 0; 34 | int size = sc.nextInt(); 35 | for (int i = 0; i < size; i++) { 36 | sum += sc.nextInt(); 37 | } 38 | System.out.println(sum); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Other/BasicInputOutput/APlusB7.java: -------------------------------------------------------------------------------- 1 | package Other.BasicInputOutput; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.List; 6 | import java.util.Scanner; 7 | 8 | /* 9 | * 题目描述: 10 | * 计算一系列数的和 11 | * 12 | * 输入: 13 | * 输入数据有多组, 每行表示一组输入数据。 14 | * 每行不定有n个整数,空格隔开。(1 <= n <= 100)。 15 | * 16 | * 输出: 17 | * 每组数据输出求和的结果 18 | * 19 | * 示例: 20 | * 输入: 21 | * 1 2 3 22 | * 4 5 23 | * 0 0 0 0 0 24 | * 25 | * 输出: 26 | * 6 27 | * 9 28 | * 0 29 | */ 30 | public class APlusB7 { 31 | 32 | public static void main(String[] args) { 33 | Scanner sc = new Scanner(System.in); 34 | while (sc.hasNextLine()) { 35 | int sum = 0; 36 | String[] str = sc.nextLine().split(" "); 37 | for (String s : str) { 38 | sum += Integer.valueOf(s); 39 | } 40 | System.out.println(sum); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Other/BasicInputOutput/StringSort3.java: -------------------------------------------------------------------------------- 1 | package Other.BasicInputOutput; 2 | 3 | import java.util.Arrays; 4 | import java.util.Scanner; 5 | 6 | /* 7 | * 题目描述: 8 | * 对输入的字符串进行排序后输出 9 | * 10 | * 输入: 11 | * 多个测试用例,每个测试用例一行。 12 | * 每行通过 , 隔开,有 n 个字符,n < 100 13 | * 14 | * 输出: 15 | * 对于每组测试用例,输出一行排序过的字符串,每个字符串通过空格隔开 16 | * 17 | * 示例: 18 | * 输入: 19 | * a,c,bb 20 | * f,dddd 21 | * nowcoder 22 | * 23 | * 输出: 24 | * a,bb,c 25 | * dddd,f 26 | * nowcoder 27 | */ 28 | public class StringSort3 { 29 | public static void main(String[] args) { 30 | Scanner sc = new Scanner(System.in); 31 | while (sc.hasNext()) { 32 | String[] str = sc.nextLine().split(","); 33 | Arrays.sort(str); 34 | for (int i = 0; i < str.length - 1; i++) { 35 | System.out.print(str[i] + ","); 36 | } 37 | System.out.println(str[str.length - 1]); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/SwordToOfferSolution/_43_NumberOf1Between1AndN/Solution.java: -------------------------------------------------------------------------------- 1 | package SwordToOfferSolution._43_NumberOf1Between1AndN; 2 | 3 | /* 4 | * 从整数 1 到 n 中 1 出现的次数 5 | * 6 | * 题目描述: 7 | * 输入一个整数 n,求从 1 到 n 这 n 个整数的十进制表示中 1 出现的次数。 8 | * 例如输入 12,从 1 到 12 这些整数中包含 1 的数字有 1,10,11 和 12,其中 1 一共出现了 5 次。 9 | * 10 | * 思路: 11 | * 找数字的规律 12 | * https://dyfloveslife.github.io/2019/11/28/offer-NumberOf1Between1AndN/ 13 | */ 14 | public class Solution { 15 | public int countDigitOne(int n) { 16 | int res = 0; 17 | for (long m = 1; m <= n; m *= 10) { 18 | long a = n / m, b = n % m; 19 | res += (a + 8) / 10 * m + (a % 10 == 1 ? b + 1 : 0); 20 | } 21 | return res; 22 | } 23 | 24 | public static void main(String[] args) { 25 | Solution solution = new Solution(); 26 | 27 | System.out.println(solution.countDigitOne(12)); 28 | System.out.println(solution.countDigitOne(13)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Other/BasicInputOutput/StringSort2.java: -------------------------------------------------------------------------------- 1 | package Other.BasicInputOutput; 2 | 3 | import java.util.Arrays; 4 | import java.util.Scanner; 5 | 6 | /* 7 | * 题目描述: 8 | * 对输入的字符串进行排序后输出 9 | * 10 | * 输入: 11 | * 多个测试用例,每个测试用例一行。 12 | * 每行通过空格隔开,有 n 个字符,n < 100 13 | * 14 | * 输出: 15 | * 对于每组测试用例,输出一行排序过的字符串,每个字符串通过空格隔开 16 | * 17 | * 示例: 18 | * 输入: 19 | * a c bb 20 | * f dddd 21 | * nowcoder 22 | * 23 | * 输出: 24 | * a bb c 25 | * dddd f 26 | * nowcoder 27 | */ 28 | public class StringSort2 { 29 | public static void main(String[] args) { 30 | Scanner sc = new Scanner(System.in); 31 | while (sc.hasNext()) { 32 | String[] str = sc.nextLine().split(" "); 33 | Arrays.sort(str); 34 | for (int i = 0; i < str.length - 1; i++) { 35 | System.out.print(str[i] + " "); 36 | } 37 | // 单独处理当前行的最后一个字符串 38 | System.out.println(str[str.length - 1]); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/SwordToOfferSolution/_18_01_DeleteNodeInList/Solution2.java: -------------------------------------------------------------------------------- 1 | package SwordToOfferSolution._18_01_DeleteNodeInList; 2 | 3 | /** 4 | * 删除链表中的节点 5 | *

6 | * 题目描述: 7 | * 有一个单链表的 head,我们想删除它其中的一个节点 node。 8 | * 给你一个需要删除的节点 node 。你将无法访问第一个节点 head。 9 | * 链表的所有值都是唯一的,并且保证给定的节点 node 不是链表中的最后一个节点。 10 | *

11 | * 分析: 12 | * 1、一般情况下,如果要删除当前节点,需要设置前驱节点 pre,当前节点 cur,后继节点 cur.next,然后执行 pre.next = cur.next; 13 | * 2、但本题只传入「待删除节点」,无法找到其前驱; 14 | * 3、为了删除节点 node,可以复制其后继节点 node.next 的节点值给 node,然后再删除 node 即可。 15 | */ 16 | public class Solution2 { 17 | public static class ListNode { 18 | private int val; 19 | private ListNode next; 20 | 21 | private ListNode(int val) { 22 | this.val = val; 23 | } 24 | } 25 | 26 | public void deleteNode(ListNode node) { 27 | if (node == null) { 28 | return; 29 | } 30 | 31 | node.val = node.next.val; 32 | node.next = node.next.next; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/SwordToOfferSolution/_10_03_ClimbingStairsII/Solution.java: -------------------------------------------------------------------------------- 1 | package SwordToOfferSolution._10_03_ClimbingStairsII; 2 | 3 | /* 4 | * 爬楼梯Ⅱ 5 | * 6 | * 题目描述: 7 | * 一只青蛙一次可以跳上 1 级台阶,也可以跳上 2 级……它也可以跳上 n 级。 8 | * 求该青蛙跳上一个 n 级的台阶总共有多少种跳法。 9 | * 10 | * 思路: 11 | * 1. 对于 n 级台阶,第一步有 n 种跳法,即跳 1 级、跳 2 级、跳 3 级、...、跳 n 级; 12 | * 2. 如果跳 1 级,则还剩下 n-1 级跳法,即 f(n-1); 13 | * 3. 如果跳 2 级,则还剩下 n-2 级跳法,即 f(n-2); 14 | * 4. 如果跳 3 级,则还剩下 n-3 级跳法,即 f(n-3); 15 | * 5. ... 16 | * 6. f(n) = f(n-1) + f(n-2) + ... + f(1); 17 | * 7. 又因为 f(n-1) = f(n-2) + f(n-3) + ... + f(1); 18 | * 8. 两式相减,得到 f(n) = 2*f(n-1)。 19 | */ 20 | public class Solution { 21 | public int climbingStairsII(int n) { 22 | int res = 1; 23 | while (n-- > 1) { 24 | res *= 2; 25 | } 26 | return res; 27 | } 28 | 29 | public static void main(String[] args) { 30 | Solution solution = new Solution(); 31 | System.out.println(solution.climbingStairsII(6)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Other/BasicAlgorithm/_40_TowerOfHanoi/Solution.java: -------------------------------------------------------------------------------- 1 | package Other.BasicAlgorithm._40_TowerOfHanoi; 2 | 3 | /* 4 | * 汉诺塔问题 5 | * 6 | * 题目描述: 7 | * 打印 n 层汉诺塔从最左边移动到最后边的全部过程 8 | * 9 | * 思路: 10 | * 1. 现在不分左右中杆,只有 from、to 和 help,现在想将 from 上的 1~n 借助 help 移动到 to 上去; 11 | * 2. 第一步:将 1~n-1 从 from 移动到 help; 12 | * 3. 第二步:将 n 从 from 移动到 to; 13 | * 4. 第三步:将 1~n-1 从 help 移动到 to; 14 | * 5. 一共 (2^n)-1 步。 15 | */ 16 | public class Solution { 17 | 18 | // N 代表从 1 到 N 19 | public static void process(int N, String from, String to, String help) { 20 | if (N == 1) { 21 | System.out.println("Move 1 from " + from + " to " + to); 22 | } else { 23 | // 这里分三步走 24 | process(N - 1, from, help, to); 25 | System.out.println("Move " + N + " from " + from + " to " + to); 26 | process(N - 1, help, to, from); 27 | } 28 | } 29 | 30 | public static void main(String[] args) { 31 | process(3, "左", "右", "中"); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/SwordToOfferSolution/_23_EntryNodeInListLoop/Solution.java: -------------------------------------------------------------------------------- 1 | package SwordToOfferSolution._23_EntryNodeInListLoop; 2 | 3 | /** 4 | * 环形链表 5 | *

6 | * 题目描述: 7 | * 给你一个链表的头节点 head ,判断链表中是否有环。 8 | *

9 | * 思路: 10 | * 1、可以使用哈希表,在来到下一个节点的时候,判断是否已经出现过; 11 | * 2、使用「龟兔赛跑」的思想,兔子先跑,乌龟在后,假设链表中存在环,那么兔子会一直处于环中,在某一个时间点,兔子和乌龟一定会相遇。 12 | */ 13 | public class Solution { 14 | static class ListNode { 15 | private int val; 16 | private ListNode next; 17 | 18 | private ListNode(int val) { 19 | this.val = val; 20 | } 21 | } 22 | 23 | public boolean hasCycle(ListNode head) { 24 | if (head == null) { 25 | return false; 26 | } 27 | 28 | ListNode slow = head, fast = head; 29 | while (fast.next != null) { 30 | fast = fast.next.next; 31 | slow = slow.next; 32 | if (fast == slow) { 33 | return true; 34 | } 35 | } 36 | 37 | return false; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Other/BasicInputOutput/StringSort1.java: -------------------------------------------------------------------------------- 1 | package Other.BasicInputOutput; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.Scanner; 6 | 7 | /* 8 | * 题目描述: 9 | * 对输入的字符串进行排序后输出 10 | * 11 | * 输入: 12 | * 输入有两行,第一行 n 13 | * 第二行是 n 个空格隔开的字符串 14 | * 15 | * 输出: 16 | * 输出一行排序后的字符串,空格隔开,无结尾空格 17 | * 18 | * 示例: 19 | * 输入: 20 | * 5 21 | * c d a bb e 22 | * 23 | * 输出: 24 | * a bb c d e 25 | */ 26 | public class StringSort1 { 27 | 28 | public static void main(String[] args) { 29 | Scanner sc = new Scanner(System.in); 30 | ArrayList list = new ArrayList<>(); 31 | int n = sc.nextInt(); 32 | 33 | for (int i = 0; i < n; i++) { 34 | list.add(sc.next()); 35 | } 36 | 37 | Collections.sort(list); 38 | for (int i = 0; i < list.size() - 1; i++) { 39 | System.out.print(list.get(i) + " "); 40 | } 41 | // 单独处理最后一个字符 42 | System.out.println(list.get(list.size() - 1)); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/SwordToOfferSolution/_18_02_DeleteDuplicatedNode/Solution2.java: -------------------------------------------------------------------------------- 1 | package SwordToOfferSolution._18_02_DeleteDuplicatedNode; 2 | 3 | /** 4 | * 删除排序链表中的重复元素 5 | *

6 | * 题目描述: 7 | * 给定一个已排序的链表的头 head,删除所有重复的元素,使每个元素只出现一次。返回已排序的链表。 8 | *

9 | * 思路: 10 | * 1、使用一个指针进行判断即可,如果当前节点与其后继节点相同,则当前指针的 next 指向下一个节点,相当于把重复的节点删除了; 11 | * 2、如果当前节点与其后继节点不同,则指针后移即可。 12 | */ 13 | public class Solution2 { 14 | public static class ListNode { 15 | private int val; 16 | private ListNode next; 17 | 18 | private ListNode(int val) { 19 | this.val = val; 20 | } 21 | } 22 | 23 | public ListNode deleteDuplicates(ListNode head) { 24 | if (head == null) { 25 | return null; 26 | } 27 | 28 | ListNode cur = head; 29 | while (cur.next != null) { 30 | if (cur.val == cur.next.val) { 31 | cur.next = cur.next.next; 32 | } else { 33 | cur = cur.next; 34 | } 35 | } 36 | 37 | return head; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_03_Greedy/_121_BestTimeToBuyAndSellStock/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._03_Greedy._121_BestTimeToBuyAndSellStock; 2 | 3 | /* 4 | * 买卖股票的最佳时机 5 | * 6 | * 题目描述: 7 | * 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。 8 | * 如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。 9 | * 注意你不能在买入股票前卖出股票。 10 | * 11 | * 思路: 12 | * 1. 可以使用 DP,维护一个最小值和一个最大利润,最大利润为当前元素减去最小值; 13 | * 2. 即找到最小的谷后面的最大的峰。 14 | */ 15 | public class Solution { 16 | public static int maxProfit(int[] arr) { 17 | if (arr == null || arr.length == 0) { 18 | return 0; 19 | } 20 | 21 | int min = Integer.MAX_VALUE; 22 | int maxprofit = 0; 23 | for (int i = 0; i < arr.length; i++) { 24 | min = Math.min(min, arr[i]); 25 | maxprofit = Math.max(maxprofit, arr[i] - min); 26 | } 27 | return maxprofit; 28 | } 29 | 30 | public static void main(String[] args) { 31 | int[] arr = {7, 6, 4, 3, 1}; 32 | System.out.println(maxProfit(arr)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_03_Tree/_538_ConvertBSTToGreaterTree/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._03_Tree._538_ConvertBSTToGreaterTree; 2 | 3 | /* 4 | * 把二叉搜索树转换为累加树 5 | * 6 | * 题目描述: 7 | * 给定一个二叉搜索树(Binary Search Tree),把它转换成为累加树(Greater Tree),使得每个节点的值是原来的节点值加上所有大于它的节点值之和。 8 | * 9 | * 思路: 10 | * 1. 利用二叉搜索树的特性,左子树的值都小于根节点的值,右子树的值都大于根节点的值; 11 | * 2. 既然需要在原节点加上“所有大于它的节点值之和”,那么我们需要从最右侧的节点开始,通过递归的方式不断给父节点返回累加值; 12 | * 3. 因此,可以采用右->根->左的方式遍历二叉搜索树,在遍历的过程中不断修改当前遍历的节点即可。 13 | */ 14 | public class Solution { 15 | class TreeNode { 16 | int val; 17 | TreeNode left; 18 | TreeNode right; 19 | 20 | TreeNode(int val) { 21 | this.val = val; 22 | } 23 | } 24 | 25 | private int sum = 0; 26 | 27 | public TreeNode convertBST(TreeNode root) { 28 | if (root == null) { 29 | return null; 30 | } 31 | convertBST(root.right); 32 | sum += root.val; 33 | root.val = sum; 34 | convertBST(root.left); 35 | return root; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Other/BasicAlgorithm/_47_CompletenessKnapsackProblem/Solution.java: -------------------------------------------------------------------------------- 1 | package Other.BasicAlgorithm._47_CompletenessKnapsackProblem; 2 | 3 | import java.util.Scanner; 4 | 5 | /* 6 | * 完全背包问题 7 | * 8 | * 思路: 9 | * 1. 与之前的 0-1 背包的思路相同,但是在内层循环的时候,需要从小到大进行。 10 | */ 11 | public class Solution { 12 | 13 | public static void main(String[] args) { 14 | Scanner sc = new Scanner(System.in); 15 | // 物品数量 16 | int N = sc.nextInt(); 17 | // 背包容量 18 | int V = sc.nextInt(); 19 | // 第 i 个物品的体积和价值 20 | int[] v = new int[N + 1]; 21 | int[] w = new int[N + 1]; 22 | for (int i = 1; i <= N; i++) { 23 | v[i] = sc.nextInt(); 24 | w[i] = sc.nextInt(); 25 | } 26 | 27 | int[] dp = new int[V + 1]; 28 | for (int i = 1; i <= N; i++) { 29 | // 仅对内层 for 循环进行修改即可 30 | for (int j = v[i]; j <= V; j++) { 31 | dp[j] = Math.max(dp[j], dp[j - v[i]] + w[i]); 32 | } 33 | } 34 | System.out.println(dp[V]); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_08_Mathematics/_9_PalindromeNumber/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._08_Mathematics._9_PalindromeNumber; 2 | 3 | /* 4 | * 回文数 5 | * 6 | * 题目描述: 7 | * 判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。 8 | * 你可以不将整数转为字符串来解决这个问题吗? 9 | * 10 | * 思路: 11 | * 1. 一开始想到的是将数字转换成字符串,通过双指针来解决,但看了一下【进阶】的要求,不能转换成字符串; 12 | * 2. 然后就想到求数字的逆序,通过逆序来与原数字进行比较即可。 13 | */ 14 | public class Solution { 15 | public boolean isPalindrome(int x) { 16 | if (x < 0) { 17 | return false; 18 | } 19 | int res = 0; 20 | int num = x; 21 | 22 | while (num != 0) { 23 | // 乘以 10 是为了给个位留出空位 24 | // 取余就是求出个位 25 | res = res * 10 + num % 10; 26 | num /= 10; 27 | } 28 | 29 | return res == x; 30 | } 31 | 32 | public static void main(String[] args) { 33 | Solution solution = new Solution(); 34 | 35 | System.out.println(solution.isPalindrome(123)); 36 | System.out.println(solution.isPalindrome(121)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_08_LinkedList/_83_RemoveDuplicatesFromSortedList/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._08_LinkedList._83_RemoveDuplicatesFromSortedList; 2 | 3 | /* 4 | * 删除排序链表中的重复元素 5 | * 6 | * 题目描述: 7 | * 给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。 8 | * 9 | * 思路: 10 | * 1. 如果当前节点和它的下一个节点相等,则当前节点就指向它的下下节点,也就是将相等的节点给空出来,相当于删除了; 11 | * 2. 如果当前节点和它的下一个节点不相等,那么我们就不需要管了,而是直接移动当前节点的指针来到它的下一个节点,继续判断。 12 | */ 13 | public class Solution { 14 | class ListNode { 15 | int val; 16 | ListNode next; 17 | 18 | ListNode(int val) { 19 | this.val = val; 20 | } 21 | } 22 | 23 | public ListNode deleteDuplicates(ListNode head) { 24 | if (head == null) { 25 | return null; 26 | } 27 | 28 | ListNode cur = head; 29 | while (cur != null && cur.next != null) { 30 | if (cur.val == cur.next.val) { 31 | cur.next = cur.next.next; 32 | } else { 33 | cur = cur.next; 34 | } 35 | } 36 | return head; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_02_String/_9_PalindromeNumber/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._02_String._9_PalindromeNumber; 2 | 3 | /* 4 | * 回文数 5 | * 6 | * 题目描述: 7 | * 判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。 8 | * 9 | * 思路: 10 | * 1. 这里不将给定的整数转化成字符串进行比较,而是直接通过取余、整除的方式构建一个逆序的 x; 11 | * 2. 最后再通过逆序后的结果与 x 进行比较,相等则为回文数。 12 | */ 13 | public class Solution { 14 | 15 | public boolean isPalindrome(int x) { 16 | if (x < 0) { 17 | return false; 18 | } 19 | 20 | int num = x; 21 | int res = 0; 22 | while (num != 0) { 23 | // 每次通过乘 10 加余数的方式,将 x 逆序构建出来 24 | res = res * 10 + num % 10; 25 | num /= 10; 26 | } 27 | // 最后判断是否和给定的 x 相等,相等则为回文数 28 | return res == x; 29 | } 30 | 31 | public static void main(String[] args) { 32 | Solution solution = new Solution(); 33 | int x1 = 121; 34 | int x2 = 20; 35 | 36 | System.out.println(solution.isPalindrome(x1)); 37 | System.out.println(solution.isPalindrome(x2)); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_03_Tree/_100_SameTree/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._03_Tree._100_SameTree; 2 | 3 | /* 4 | * 相同的树 5 | * 6 | * 题目描述: 7 | * 给定两个二叉树,编写一个函数来检验它们是否相同。 8 | * 如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。 9 | * 10 | * 思路: 11 | * 1. 判断两棵树是否相同,则需要满足: 12 | * 1.1) 当前两棵树的根节点相同; 13 | * 1.2) 并且,p 的左子树和 q 的右子树相同; 14 | * 1.3) 并且,p 的右子树和 q 的右子树相同。 15 | * 2. 注意,以上是“并且”的关系。 16 | */ 17 | public class Solution { 18 | class TreeNode { 19 | int val; 20 | TreeNode left; 21 | TreeNode right; 22 | 23 | TreeNode(int val) { 24 | this.val = val; 25 | } 26 | } 27 | 28 | public boolean isSameTree(TreeNode p, TreeNode q) { 29 | if (p == null && q == null) { 30 | return true; 31 | } 32 | if (p == null || q == null) { 33 | return false; 34 | } 35 | 36 | if (p.val != q.val) { 37 | return false; 38 | } 39 | 40 | // 注意是“并且”的关系 41 | return isSameTree(p.left, q.left) && isSameTree(p.right, q.right); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_03_Tree/_617_MergeTwoBinaryTrees/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._03_Tree._617_MergeTwoBinaryTrees; 2 | 3 | /* 4 | * 合并二叉树 5 | * 6 | * 题目描述: 7 | * 给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。 8 | * 9 | * 思路: 10 | * 1. 一开始将问题想复杂了; 11 | * 2. 其实很简单,每次将两个节点的值相加,然后用这个值去创建新的节即可; 12 | * 3. 然后再分别递归处理左右节点。 13 | */ 14 | public class Solution { 15 | class TreeNode { 16 | int val; 17 | TreeNode left; 18 | TreeNode right; 19 | 20 | TreeNode(int val) { 21 | this.val = val; 22 | } 23 | } 24 | 25 | public TreeNode mergeTrees(TreeNode t1, TreeNode t2) { 26 | if (t1 == null && t2 == null) { 27 | return null; 28 | } 29 | if (t1 == null) { 30 | return t2; 31 | } 32 | if (t2 == null) { 33 | return t1; 34 | } 35 | 36 | TreeNode root = new TreeNode(t1.val + t2.val); 37 | root.left = mergeTrees(t1.left, t2.left); 38 | root.right = mergeTrees(t2.right, t2.right); 39 | 40 | return root; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Other/Other/GetLessIndex.java: -------------------------------------------------------------------------------- 1 | package Other.Other; 2 | 3 | /** 4 | * 在数组中找到一个局部最小的位置 5 | */ 6 | public class GetLessIndex { 7 | public static int getLessIndex(int[] arr) { 8 | if (arr == null || arr.length < 1) return -1; 9 | if (arr.length == 1 || arr[0] < arr[1]) return 0; 10 | if (arr[arr.length - 1] < arr[arr.length - 2]) return arr.length - 1; 11 | 12 | int left = 1, right = arr.length - 2, mid = 0; 13 | while (left < right) { 14 | mid = (left + right) / 2; 15 | if (arr[mid] > arr[mid - 1]) { // 说明局部最小在arr[left]到arr[mid-1]范围 16 | right = mid - 1; 17 | } else if (arr[mid] > arr[mid + 1]) { // 说明局部最小在arr[m+1]到arr[right]范围 18 | left = mid + 1; 19 | } else { // arr[mid] 0) { 27 | res += cur_profit; 28 | } 29 | } 30 | return res; 31 | } 32 | 33 | public static void main(String[] args) { 34 | int[] arr = {7, 1, 5, 3, 6, 4}; 35 | System.out.println(maxProfit(arr)); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Other/BasicAlgorithm/_46_01KnapsackProblemOptimizes/Solution.java: -------------------------------------------------------------------------------- 1 | package Other.BasicAlgorithm._46_01KnapsackProblemOptimizes; 2 | 3 | import java.util.Scanner; 4 | 5 | /* 6 | * 我们来考虑如何将 0-1 背包从二维 dp 优化到 一维 dp 7 | * 8 | * 1. 还是状态转移方程:dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - v[i]] + w[i]); 9 | * 2. 第二层循环需要从大到小循环,这是为了避免 i-1 的值被覆盖; 10 | * 3. 也就是先更新第 i 个的值,然后再更新第 i-1 个值。 11 | */ 12 | public class Solution { 13 | public static void main(String[] args) { 14 | Scanner sc = new Scanner(System.in); 15 | int N = sc.nextInt(); 16 | int V = sc.nextInt(); 17 | int[] v = new int[N + 1]; 18 | int[] w = new int[N + 1]; 19 | 20 | for (int i = 1; i <= N; i++) { 21 | v[i] = sc.nextInt(); 22 | w[i] = sc.nextInt(); 23 | } 24 | 25 | // 注意这里是容量再加 1 26 | int[] dp = new int[V + 1]; 27 | dp[0] = 0; 28 | for (int i = 1; i <= N; i++) { 29 | // 背包容量大于当前物品的体积的时候,才能更新 dp 数组 30 | for (int j = V; j >= v[i]; j--) { 31 | dp[j] = Math.max(dp[j], dp[j - v[i]] + w[i]); 32 | } 33 | } 34 | System.out.println(dp[V]); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_02_String/_14_LongestCommonPrefix/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._02_String._14_LongestCommonPrefix; 2 | 3 | /* 4 | * 最长公共前缀 5 | * 6 | * 题目描述: 7 | * 编写一个函数来查找字符串数组中的最长公共前缀。 8 | * 如果不存在公共前缀,返回空字符串 ""。 9 | * 10 | * 思路: 11 | * 1. 采用两两比较的方式,首先拿第一个字符串做“基准”,然后逐个与后面的字符串进行比较; 12 | * 2. 每次在比较的过程中,如果返回 -1,则说明需要将“基准”的最后一个字符删除; 13 | * 3. 通过不断的删除,那么该“基准”就会逐渐的变成要返回的最长公共前缀。 14 | */ 15 | public class Solution { 16 | public String longestCommonPrefix(String[] strs) { 17 | if (strs == null || strs.length == 0) { 18 | return ""; 19 | } 20 | 21 | String res = strs[0]; 22 | for (String str : strs) { 23 | // 如果不为 0,说明 res 不在 str 中 24 | while (str.indexOf(res) != 0) { 25 | // 通过删除最后一个字符,不断地更新 res 26 | res = res.substring(0, res.length() - 1); 27 | } 28 | } 29 | return res; 30 | } 31 | 32 | 33 | public static void main(String[] args) { 34 | Solution solution = new Solution(); 35 | String[] strs = {"flower", "flow", "flight"}; 36 | 37 | System.out.println(solution.longestCommonPrefix(strs)); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/SwordToOfferSolution/_41_02_FirstAppearingOnce/Solution.java: -------------------------------------------------------------------------------- 1 | package SwordToOfferSolution._41_02_FirstAppearingOnce; 2 | 3 | import java.util.LinkedList; 4 | import java.util.Queue; 5 | 6 | /* 7 | * 字符流中第一个不重复的字符 8 | * 9 | * 题目描述: 10 | * 请实现一个函数用来找出字符流中第一个只出现一次的字符。 11 | * 例如,当从字符流中只读出前两个字符 “go” 时,第一个只出现一次的字符是 "g"; 12 | * 当从该字符流中读出前六个字符 “google” 时,第一个只出现一次的字符是 "l"。 13 | * 如果当前字符流没有存在出现一次的字符,返回 # 字符。 14 | * 15 | * 思路: 16 | * 1. 构造一个类似于哈希数组的结果,每次 insert 的时候统计当前字符出现的次数,并将该字符加入到队列中; 17 | * 2. 只要队头字符的数量大于 1,则说明该字符是重复的,即让其出队; 18 | * 3. 由于重复的字符都出队了,所以最后返回队头元素就是第一个不重复出现的字符。 19 | */ 20 | public class Solution { 21 | private int[] count = new int[256]; 22 | private Queue queue = new LinkedList<>(); 23 | 24 | // 从字符流中插入一个字符 25 | public void insert(char ch) { 26 | // ch 就是对应的 ASCII 值 27 | // 统计每个 ch 的数量 28 | count[ch]++; 29 | // 入队 30 | queue.offer(ch); 31 | while (!queue.isEmpty() && count[queue.peek()] > 1) { 32 | // 出队 33 | queue.poll(); 34 | } 35 | } 36 | 37 | // 返回当前字符流中第一个不重复出现的字符 38 | public char firstAppearingOnce() { 39 | return queue.isEmpty() ? '#' : queue.peek(); 40 | } 41 | } -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_08_LinkedList/_160_IntersectionOfTwoLinkedLists/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._08_LinkedList._160_IntersectionOfTwoLinkedLists; 2 | 3 | /* 4 | * 相交链表 5 | * 6 | * 题目描述: 7 | * 找到两个单链表相交的起始节点 8 | * 9 | * 思路: 10 | * 1. 设链表 A 的长度的 a+c,链表 B 的长度是 b+c,这里都加上 c 表示它们的公共节点; 11 | * 2. 那么可以得到:a+c+b=b+c+a 12 | * 3. 当访问 A 链表的指针访问到链表尾部时,令它从链表 B 的头部开始访问链表 B; 13 | * 4. 同样地,当访问 B 链表的指针访问到链表尾部时,令它从链表 A 的头部开始访问链表 A; 14 | * 5. 这样就能控制访问 A 和 B 两个链表的指针能同时访问到交点。 15 | * 6. 如果不存在交点,那么 a + b = b + a,以下实现代码中 l1 和 l2 会同时为 null,从而退出循环。 16 | */ 17 | public class Solution { 18 | class ListNode { 19 | int val; 20 | ListNode next; 21 | ListNode(int val) { 22 | this.val = val; 23 | } 24 | } 25 | 26 | public ListNode getIntersectionNode(ListNode headA, ListNode headB) { 27 | if (headA == null || headB == null) { 28 | return null; 29 | } 30 | 31 | ListNode l1 = headA; 32 | ListNode l2 = headB; 33 | 34 | while (l1 != l2) { 35 | l1 = l1 == null ? headB : l1.next; 36 | l2 = l2 == null ? headA : l2.next; 37 | } 38 | return l1; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_03_Tree/_235_LowestCommonAncestorOfABinarySearchTree/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._03_Tree._235_LowestCommonAncestorOfABinarySearchTree; 2 | 3 | /* 4 | * 二叉搜索树的最近公共祖先 5 | * 6 | * 题目描述: 7 | * 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 8 | * 9 | * 思路: 10 | * 1. 同样利用二叉搜索树的性质; 11 | * 2. 如果当前节点比 p 和 q 都大,则说明当前节点需要来到它的左子树; 12 | * 3. 如果当前节点比 p 和 q 都小,则说明当前节点需要来到它的右子树; 13 | * 4. 否则,当前节点就是 p 和 q 的最近公共祖先。 14 | */ 15 | public class Solution { 16 | class TreeNode { 17 | int val; 18 | TreeNode left; 19 | TreeNode right; 20 | 21 | TreeNode(int val) { 22 | this.val = val; 23 | } 24 | } 25 | 26 | public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { 27 | if (root == null) { 28 | return null; 29 | } 30 | 31 | while (root != null) { 32 | if (root.val > p.val && root.val > q.val) { 33 | root = root.left; 34 | } else if (root.val < p.val && root.val < q.val) { 35 | root = root.right; 36 | } else { 37 | return root; 38 | } 39 | } 40 | return null; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/SwordToOfferSolution/_66_ConstuctArray/Solution.java: -------------------------------------------------------------------------------- 1 | package SwordToOfferSolution._66_ConstuctArray; 2 | 3 | import java.util.Arrays; 4 | 5 | /* 6 | * 构建乘积数组 7 | * 8 | * 题目描述: 9 | * 给定一个数组 A[0, 1, …, n-1],请构建一个数组 B[0, 1, …, n-1], 10 | * 其中 B 中的元素 B[i] = A[0] × A[1] × … × A[i-1] × A[i+1] × … × A[n-1]。 11 | * 不能使用除法。 12 | * 13 | * 思路: 14 | * 1. 要想求 B[i],则以 i 位置将 A 划分为两部分; 15 | * 2. 左侧部分进行累乘,右侧部分也进行累乘,然后将这两部分的结果进行相乘即可; 16 | * 3. 例如计算 B[2],左侧计算 (A[0] * A[1]),右侧计算 (A[n-1] * ... * A[3])。 17 | */ 18 | public class Solution { 19 | 20 | public int[] multiply(int[] A) { 21 | int len = A.length; 22 | int[] B = new int[len]; 23 | 24 | B[0] = 1; 25 | // 计算下三角形 26 | for (int i = 1; i < len; i++) { 27 | B[i] = B[i - 1] * A[i - 1]; 28 | } 29 | int temp = 1; 30 | // 计算上三角形 31 | for (int j = len - 2; j >= 0; j--) { 32 | temp *= A[j + 1]; 33 | B[j] *= temp; 34 | } 35 | return B; 36 | } 37 | 38 | public static void main(String[] args) { 39 | Solution solution = new Solution(); 40 | int[] A = {1, 2, 3, 4, 5}; 41 | 42 | System.out.println(Arrays.toString(solution.multiply(A))); 43 | } 44 | } -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_02_DP/_343_IntegerBreak/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._02_DP._343_IntegerBreak; 2 | 3 | /* 4 | * 整数拆分 5 | * 6 | * 题目描述: 7 | * 给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 8 | * 返回你可以获得的最大乘积。 9 | * 10 | * 思路: 11 | * 1. 尽可能分成多个 3 的和,先求出 n 所包含 3 的个数,即 n/3=a,然后再求出 3 的余数,即 n%3=b; 12 | * 2. 根据 3 的余数做不同的判断; 13 | * 3. 如果余数为 0,则说明 n 包含整数个 3,则直接返回 3^a; 14 | * 4. 如果余数为 1,例如 10,则需要将 3+3+3+1,转化成 3+3+4,然后再求结果; 15 | * 5. 如果余数为 2,例如 8,则直接返回 3^a * 2 即可。 16 | */ 17 | public class Solution { 18 | public int integerBreak(int n) { 19 | if (n <= 3) { 20 | return n - 1; 21 | } 22 | 23 | int a = n / 3; 24 | int b = n % 3; 25 | if (b == 0) { 26 | return (int) Math.pow(3, a); 27 | } else if (b == 1) { 28 | return (int) Math.pow(3, a - 1) * 4; 29 | } else { 30 | return (int) Math.pow(3, a) * 2; 31 | } 32 | } 33 | 34 | public static void main(String[] args) { 35 | Solution solution = new Solution(); 36 | 37 | System.out.println(solution.integerBreak(2)); 38 | System.out.println(solution.integerBreak(3)); 39 | System.out.println(solution.integerBreak(10)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_04_BinarySearch/_278_FirstBadVersion/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._04_BinarySearch._278_FirstBadVersion; 2 | 3 | /* 4 | * 第一个错误版本 5 | * 6 | * 题目描述: 7 | * 假设你有 n 个版本 [1, 2, ..., n],你想找出导致之后所有版本出错的第一个错误的版本。 8 | * 你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。 9 | * 实现一个函数来查找第一个错误的版本。 10 | * 你应该尽量减少对调用 API 的次数。 11 | * 12 | * 思路: 13 | * 1. 使用二分法即可; 14 | * 2. 注意:因为使用了 right = middle,所以 while 的条件应该是 left < right; 15 | * 3. 如果当前的 middle 是坏版本,则说明第一个怀版本在当前 middle 的左边,则继续往左边找。 16 | */ 17 | public class Solution { 18 | public int firstBadVersion(int n) { 19 | if (n < 1) { 20 | return 0; 21 | } 22 | 23 | int left = 1, right = n; 24 | while (left < right) { 25 | int middle = left + ((right - left) >> 1); 26 | // 如果 middle 是错误版本的话,则说明 middle 左侧可能还存在错误版本 27 | // 因此从 [left, middle] 区间内继续寻找 28 | if (isBadVersion(middle)) { 29 | right = middle; 30 | } else { 31 | left = middle + 1; 32 | } 33 | } 34 | 35 | return left; 36 | } 37 | 38 | private boolean isBadVersion(int version) { 39 | return true; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_03_Tree/_111_MinimumDepthOfBinaryTree/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._03_Tree._111_MinimumDepthOfBinaryTree; 2 | 3 | /* 4 | * 二叉树的最小深度 5 | * 6 | * 题目描述: 7 | * 给定一个二叉树,找出其最小深度。 8 | * 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 9 | * 说明: 叶子节点是指没有子节点的节点。 10 | * 11 | * 思路: 12 | * 1. 注意:对于二叉树 [1, 2] 来说,它的最小深度为 2; 13 | * 2. 因为求的是所经过的节点数量; 14 | * 3. 还是采用之前的求最大深度的方法,只不过该题需要多一个判断条件; 15 | * 4. 叶子节点的定义是:如果当前节点没有左右孩子,也就是左右孩子都为 null,那么当前节点就是叶子节点; 16 | * 5. 如果当前节点的左右孩子都为空,则返回 1; 17 | * 6. 如果当前节点的左右孩子其中有一个为空,则返回不为空的孩子节点的深度; 18 | * 7. 如果当前节点的左右孩子都不为空,则返回左右孩子较小深度的节点值。 19 | */ 20 | public class Solution { 21 | class TreeNode { 22 | int val; 23 | TreeNode left; 24 | TreeNode right; 25 | 26 | TreeNode(int val) { 27 | this.val = val; 28 | } 29 | } 30 | 31 | public int minDepth(TreeNode root) { 32 | if (root == null) { 33 | return 0; 34 | } 35 | 36 | int leftDepth = minDepth(root.left); 37 | int rightDepth = minDepth(root.right); 38 | 39 | if (leftDepth == 0 || rightDepth == 0) { 40 | return leftDepth + rightDepth + 1; 41 | } 42 | return Math.min(leftDepth, rightDepth) + 1; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_04_BinarySearch/_153_FindMinimumInRotatedSortedArray/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._04_BinarySearch._153_FindMinimumInRotatedSortedArray; 2 | 3 | /* 4 | * 旋转数组的最小数字 5 | * 6 | * 题目描述: 7 | * 假设按照升序排序的数组在预先未知的某个点上进行了旋转。请找出其中最小的元素。 8 | * 你可以假设数组中不存在重复元素。 9 | * 10 | * 思路: 11 | * 1. 直接使用二分法进行查找; 12 | * 2. 拿 nums[middle] 和 nums[right] 进行比较: 13 | * 2.1) 如果 nums[middle] <= nums[right],说明 middle 和 right - 1 之内的所有的元素都比 right 小, 14 | * 则此最小的元素有可能在 nums[middle] 位置上,也有可能在 nums[middle] 的左边,所以需要将 right 定位到 middle 的位置; 15 | * 2.2) 如果 nums[middle] > nums[right],说明最小的元素在 middle 的右侧, 16 | * 因此需要将 left 置为 middle + 1。 17 | */ 18 | public class Solution { 19 | public static int findMin(int[] nums) { 20 | if (nums == null || nums.length == 0) { 21 | return 0; 22 | } 23 | 24 | int left = 0; 25 | int right = nums.length - 1; 26 | while (left < right) { 27 | int middle = left + ((right - left) >> 1); 28 | if (nums[middle] <= nums[right]) { 29 | right = middle; 30 | } else { 31 | left = middle + 1; 32 | } 33 | } 34 | return nums[left]; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_01_ArrayAndMatrix/_27_RemoveElement/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._01_ArrayAndMatrix._27_RemoveElement; 2 | 3 | /* 4 | * 移除元素 5 | * 6 | * 题目描述: 7 | * 给你一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,并返回移除后数组的新长度。 8 | * 不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。 9 | * 元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。 10 | * 11 | * 分析: 12 | * 1. 要求"原地移除所有等于 val 的元素",可以使用双指针的思想,i 指向待插入的位置,j 从 0 位置开始往后遍历数组; 13 | * 2. 当 nums[j] 遇到与 val 不等的时候,将 j 位置元素赋值给 i 位置元素,同时 i 往后移动即可。 14 | */ 15 | public class Solution { 16 | public int removeElement(int[] nums, int val) { 17 | if (nums == null || nums.length == 0) { 18 | return 0; 19 | } 20 | 21 | int i = 0; 22 | for (int j = 0; j < nums.length; j++) { 23 | if (nums[j] != val) { 24 | nums[i++] = nums[j]; 25 | } 26 | } 27 | 28 | return i; 29 | } 30 | 31 | public static void main(String[] args) { 32 | Solution solution = new Solution(); 33 | int[] nums1 = {3, 2, 2, 3}; 34 | int[] nums2 = {0, 1, 2, 2, 3, 0, 4, 2}; 35 | 36 | System.out.println(solution.removeElement(nums1, 3)); 37 | System.out.println(solution.removeElement(nums2, 2)); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_06_Search/_78_Subsets/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._06_Search._78_Subsets; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /* 7 | * 子集 8 | * 9 | * 题目描述: 10 | * 给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集),包含子集为空的情况。 11 | * 说明:解集不能包含重复的子集。 12 | * 13 | * 思路: 14 | * 回溯。 15 | */ 16 | public class Solution { 17 | public List> subsets(int[] nums) { 18 | List> res = new ArrayList<>(); 19 | if (nums == null || nums.length == 0) { 20 | return res; 21 | } 22 | 23 | List path = new ArrayList<>(); 24 | dfs(res, path, nums, 0); 25 | return res; 26 | } 27 | 28 | public void dfs(List> res, List path, int[] nums, int start) { 29 | res.add(new ArrayList<>(path)); 30 | for (int i = start; i < nums.length; i++) { 31 | path.add(nums[i]); 32 | dfs(res, path, nums, i + 1); 33 | path.remove(path.size() - 1); 34 | } 35 | } 36 | 37 | public static void main(String[] args) { 38 | Solution s = new Solution(); 39 | int[] nums = {1, 2, 3}; 40 | System.out.println(s.subsets(nums)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Other/BasicDataStructure/Knapsack.java: -------------------------------------------------------------------------------- 1 | package Other.BasicDataStructure; 2 | 3 | /** 4 | * N = 3, W = 4 5 | * wt = [2, 1, 3] 6 | * val = [4, 2, 3] 7 | */ 8 | public class Knapsack { 9 | 10 | public static int method(int N, int W, int[] wt, int[] val) { 11 | int[][] dp = new int[N + 1][W + 1]; 12 | 13 | for (int i = 0; i < N + 1; i++) { 14 | dp[i][0] = 0; 15 | } 16 | for (int j = 0; j < W + 1; j++) { 17 | dp[0][j] = 0; 18 | } 19 | 20 | for (int i = 1; i <= N; i++) { 21 | for (int w = 1; w <= W; w++) { 22 | // 背包总容量装不下重量为 wt[i - 1] 的物品 23 | if (W - wt[i - 1] < 0) { 24 | dp[i][w] = dp[i - 1][w]; 25 | } else { 26 | // 装或者不装,择优 27 | dp[i][w] = Math.max(dp[i - 1][w], 28 | dp[i - 1][W - wt[i - 1]] + val[i - 1]); 29 | } 30 | } 31 | } 32 | return dp[N - 1][W - 1]; 33 | } 34 | 35 | public static void main(String[] args) { 36 | int N = 3; 37 | int W = 4; 38 | int[] wt = {2, 1, 3}; 39 | int[] val = {4, 2, 3}; 40 | System.out.println(method(N, W, wt, val)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_03_Tree/_108_ConvertSortedArrayToBinarySearchTree/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._03_Tree._108_ConvertSortedArrayToBinarySearchTree; 2 | 3 | /* 4 | * 将有序数组转换为二叉搜索树 5 | * 6 | * 题目描述: 7 | * 将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。 8 | * 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。 9 | * 10 | * 思路: 11 | * 1. 直接采用分治的思想,处理完当前数组的根节点以后,再分别处理左右两部分的子数组。 12 | */ 13 | public class Solution { 14 | class TreeNode { 15 | int val; 16 | TreeNode left; 17 | TreeNode right; 18 | 19 | TreeNode(int val) { 20 | this.val = val; 21 | } 22 | } 23 | 24 | public TreeNode sortedArrayToBST(int[] nums) { 25 | if (nums == null || nums.length == 0) { 26 | return null; 27 | } 28 | 29 | return process(nums, 0, nums.length - 1); 30 | } 31 | 32 | private TreeNode process(int[] nums, int left, int right) { 33 | if (left > right) { 34 | return null; 35 | } 36 | 37 | int middle = left + ((right - left) >> 1); 38 | TreeNode root = new TreeNode(nums[middle]); 39 | root.left = process(nums, left, middle - 1); 40 | root.right = process(nums, middle + 1, right); 41 | return root; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_02_DP/_377_Combination_SumIV/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._02_DP._377_Combination_SumIV; 2 | 3 | /* 4 | * 组合总和 Ⅳ 5 | * 6 | * 题目描述: 7 | * 给定一个由正整数组成且不存在重复数字的数组,找出和为给定目标正整数的组合的个数。 8 | * 请注意,顺序不同的序列被视作不同的组合。 9 | * 10 | * 思路: 11 | * 1. 使用 DP,用 dp[i] 表示对于给定的数组,和为 i 的组合的个数,因此 dp[i] += dp[i-num]; 12 | * 2. 例如 nums=[1, 3, 4],target = 7: 13 | * dp[7] = dp[7 - 1] + dp[7 - 3] + dp[7 - 4] 14 | * = dp[6] + dp[4] + dp[3]。 15 | */ 16 | public class Solution { 17 | public int combinationSum4(int[] nums, int target) { 18 | if (nums == null || nums.length == 0) { 19 | return 0; 20 | } 21 | 22 | int[] dp = new int[target + 1]; 23 | dp[0] = 1; 24 | 25 | for (int i = 1; i <= target; i++) { 26 | for (int num : nums) { 27 | if (num <= i) { 28 | dp[i] += dp[i - num]; 29 | } 30 | } 31 | } 32 | return dp[target]; 33 | } 34 | 35 | public static void main(String[] args) { 36 | Solution solution = new Solution(); 37 | int[] nums = {1, 2, 3}; 38 | int target = 4; 39 | 40 | System.out.println(solution.combinationSum4(nums, target)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_02_String/_409_LongestPalindrome/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._02_String._409_LongestPalindrome; 2 | 3 | /* 4 | * 最长回文串 5 | * 6 | * 题目描述: 7 | * 给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串。 8 | * 在构造过程中,请注意区分大小写。比如 "Aa" 不能当做一个回文字符串。 9 | * 10 | * 思路: 11 | * 1. 使用 58 大小的数组存储 s 中每个字母出现的次数,如果每个字母出现的次数都是偶数个,则可以构成回文串; 12 | * 2. 如果有单独的字符,可以将该字符放在回文串的中间; 13 | * 3. A 与 a 的 ASCII 相差 32,因此 32 + 26 = 58。 14 | */ 15 | public class Solution { 16 | public int longestPalindrome(String s) { 17 | if (s == null || s.length() == 0) { 18 | return 0; 19 | } 20 | 21 | int[] nums = new int[58]; 22 | for (char c : s.toCharArray()) { 23 | nums[c - 'A']++; 24 | } 25 | 26 | int res = 0; 27 | int odd = 0; 28 | for (int num : nums) { 29 | res += num % 2 == 0 ? num : num - 1; 30 | if (num % 2 == 1) { 31 | odd = 1; 32 | } 33 | } 34 | res += odd; 35 | return res; 36 | } 37 | 38 | public static void main(String[] args) { 39 | Solution solution = new Solution(); 40 | String s = "abccccdd"; 41 | 42 | System.out.println(solution.longestPalindrome(s)); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_03_Tree/_437_PathSumIII/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._03_Tree._437_PathSumIII; 2 | 3 | /* 路径总和 Ⅲ 4 | * 5 | * 题目描述: 6 | * 给定一个二叉树,它的每个结点都存放着一个整数值。 7 | * 找出路径和等于给定数值的路径总数。 8 | * 路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。 9 | * 10 | * 思路: 11 | * 1. 注意:路径不一定以根节点开头,也不一定以叶节点结尾,但是要求连续; 12 | * 2. 这里使用到的是双重递归,先序遍历每个节点,然后再以每个节点为起始点递归寻找满足条件的路径。 13 | */ 14 | public class Solution { 15 | class TreeNode { 16 | int val; 17 | TreeNode left; 18 | TreeNode right; 19 | 20 | TreeNode(int val) { 21 | this.val = val; 22 | } 23 | } 24 | 25 | private int res = 0; 26 | 27 | public int pathSum(TreeNode root, int sum) { 28 | if (root == null) { 29 | return 0; 30 | } 31 | 32 | dfs(root, sum); 33 | pathSum(root.left, sum); 34 | pathSum(root.right, sum); 35 | 36 | return res; 37 | } 38 | 39 | private void dfs(TreeNode root, int target) { 40 | if (root == null) { 41 | return; 42 | } 43 | 44 | target -= root.val; 45 | if (target == 0) { 46 | res++; 47 | } 48 | dfs(root.left, target); 49 | dfs(root.right, target); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_06_Search/_112_PathSum/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._06_Search._112_PathSum; 2 | 3 | /* 4 | * 路径总和 5 | * 6 | * 题目描述: 7 | * 给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。 8 | * 9 | * 思路: 10 | * 1. 使用 DFS,每遍历到一个节点的时候,用 target 减去该节点的值,再根据以下三个条件判断是否返回 true; 11 | * 2. 如果剩下的 target 为 0,并且当前节点是叶节点,那么就说明我找到了一个从根节点开始到叶节点结束的路径了; 12 | * 3. 如果不满足上面的条件,那么就递归去找左右子树是否满足这个条件。 13 | */ 14 | public class Solution { 15 | 16 | class TreeNode { 17 | int val; 18 | TreeNode left; 19 | TreeNode right; 20 | 21 | TreeNode (int val) { 22 | this.val = val; 23 | } 24 | } 25 | 26 | public boolean hasPathSum(TreeNode root, int sum) { 27 | if (root == null) { 28 | return false; 29 | } 30 | 31 | return dfs(root, sum); 32 | } 33 | 34 | private boolean dfs(TreeNode root, int target) { 35 | if (root == null) { 36 | return false; 37 | } 38 | 39 | target -= root.val; 40 | if (target == 0 && root.left == null && root.right == null) { 41 | return true; 42 | } 43 | // 这里用 || 是因为:只要存在这么一条路径就可以,因此选左选右都行 44 | return dfs(root.left, target) || dfs(root.right, target); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_06_Search/_437_PathSumIII/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._06_Search._437_PathSumIII; 2 | 3 | /* 4 | * 路径总和 Ⅲ 5 | * 6 | * 题目描述: 7 | * 给定一个二叉树,它的每个结点都存放着一个整数值。 8 | * 找出路径和等于给定数值的路径总数。 9 | * 路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。 10 | * 11 | * 思路: 12 | * 1. 使用双重递归; 13 | * 2. 第一个递归 dfs 用于不断用 sum 减去当前节点的值,然后一直到 sum 为 0 为止; 14 | * 3. 第二个递归 pathSum 表示分别处理当前节点的左右孩子。 15 | */ 16 | public class Solution { 17 | class TreeNode { 18 | int val; 19 | TreeNode left; 20 | TreeNode right; 21 | 22 | TreeNode(int val) { 23 | this.val = val; 24 | } 25 | } 26 | 27 | int res = 0; 28 | 29 | public int pahtSum(TreeNode root, int sum) { 30 | if (root == null) { 31 | return 0; 32 | } 33 | dfs(root, sum); 34 | pahtSum(root.left, sum); 35 | pahtSum(root.right, sum); 36 | return res; 37 | } 38 | 39 | private void dfs(TreeNode root, int target) { 40 | if (root == null) { 41 | return; 42 | } 43 | 44 | target -= root.val; 45 | if (target == 0) { 46 | res++; 47 | } 48 | dfs(root.left, target); 49 | dfs(root.right, target); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Other/BasicAlgorithm/_19_ReverseList/Solution.java: -------------------------------------------------------------------------------- 1 | package Other.BasicAlgorithm._19_ReverseList; 2 | 3 | /* 4 | * 反转单向链表和双向链表 5 | */ 6 | public class Solution { 7 | // 单向链表反转 8 | class Node { 9 | int val; 10 | Node next; 11 | 12 | Node(int val) { 13 | this.val = val; 14 | } 15 | } 16 | 17 | public static Node reverseList(Node head) { 18 | Node pre = null; 19 | Node next = null; 20 | 21 | while (head != null) { 22 | next = head.next; 23 | head.next = pre; 24 | pre = head; 25 | head = next; 26 | } 27 | return pre; 28 | } 29 | 30 | // 双向链表反转 31 | class DoubleNode { 32 | int val; 33 | // 上一个 34 | DoubleNode last; 35 | DoubleNode next; 36 | 37 | DoubleNode(int val) { 38 | this.val = val; 39 | } 40 | } 41 | 42 | public static DoubleNode reserveDoubleList(DoubleNode head) { 43 | DoubleNode pre = null; 44 | DoubleNode next = null; 45 | 46 | while (head != null) { 47 | next = head.next; 48 | head.next = pre; 49 | head.last = next; 50 | pre = head; 51 | head = next; 52 | } 53 | return pre; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_03_Tree/_94_BinaryTreeInorderTraversal/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._03_Tree._94_BinaryTreeInorderTraversal; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Stack; 6 | 7 | /* 8 | * 二叉树的中序遍历 9 | * 10 | * 题目描述: 11 | * 给定一个二叉树,返回它的中序遍历。 12 | * 13 | * 思路: 14 | * 1. 中序遍历的顺序是:左->根->右; 15 | * 2. 它与前序遍历和后序遍历的方式是不同的; 16 | * 3. 既然先遍历左孩子,那么我就一直来到二叉树的最左孩子的位置,然后再开始收集结果。 17 | */ 18 | public class Solution { 19 | class TreeNode { 20 | int val; 21 | TreeNode left; 22 | TreeNode right; 23 | 24 | TreeNode(int val) { 25 | this.val = val; 26 | } 27 | } 28 | 29 | public List inorderTraversal(TreeNode root) { 30 | List res = new ArrayList<>(); 31 | if (root == null) { 32 | return res; 33 | } 34 | 35 | Stack stack = new Stack<>(); 36 | while (!stack.isEmpty() || root != null) { 37 | if (root != null) { 38 | stack.push(root); 39 | root = root.left; 40 | } else { 41 | root = stack.pop(); 42 | res.add(root.val); 43 | root = root.right; 44 | } 45 | } 46 | return res; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Other/AdvancedAlgorithm/_18_MaxHappy/Solution.java: -------------------------------------------------------------------------------- 1 | package Other.AdvancedAlgorithm._18_MaxHappy; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /* 7 | * 最大的活跃值 8 | */ 9 | public class Solution { 10 | 11 | public class Node { 12 | int actValue; 13 | List nexts; 14 | 15 | Node(int actValue) { 16 | this.actValue = actValue; 17 | this.nexts = new ArrayList<>(); 18 | } 19 | } 20 | 21 | public class ReturnType { 22 | int come; 23 | int noCome; 24 | 25 | ReturnType(int come, int noCome) { 26 | this.come = come; 27 | this.noCome = noCome; 28 | } 29 | } 30 | 31 | public int getMaxValue(Node head) { 32 | ReturnType data = process(head); 33 | return Math.max(data.come, data.noCome); 34 | } 35 | 36 | public ReturnType process(Node head) { 37 | int come = head.actValue; 38 | int noCome = 0; 39 | for (int i = 0; i < head.nexts.size(); i++) { 40 | Node next = head.nexts.get(i); 41 | ReturnType nextData = process(head); 42 | come += nextData.noCome; 43 | noCome += Math.max(nextData.come, nextData.noCome); 44 | } 45 | return new ReturnType(come, noCome); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_08_LinkedList/_142_LinkedListCycleII/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._08_LinkedList._142_LinkedListCycleII; 2 | 3 | /* 4 | * 环形链表 Ⅱ 5 | * 6 | * 题目描述: 7 | * 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。 8 | * 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 9 | * 如果 pos 是 -1,则在该链表中没有环。 10 | * 11 | * 思路: 12 | * 1. 使用快慢指针,直到相遇; 13 | * 2. 相遇后,让快指针指向链表的表头,然后再让快慢指针每次走一步; 14 | * 3. 等到再次相遇的时候,快满指针就会停留在入环的第一个节点上。 15 | */ 16 | public class Solution { 17 | class ListNode { 18 | int val; 19 | ListNode next; 20 | 21 | ListNode(int val) { 22 | this.val = val; 23 | } 24 | } 25 | 26 | public ListNode detectCycle(ListNode head) { 27 | if (head == null) { 28 | return null; 29 | } 30 | 31 | ListNode fast = head; 32 | ListNode slow = head; 33 | while (fast != null && fast.next != null) { 34 | fast = fast.next.next; 35 | slow = slow.next; 36 | if (fast == slow) { 37 | fast = head; 38 | while (fast != slow) { 39 | fast = fast.next; 40 | slow = slow.next; 41 | } 42 | return fast; 43 | } 44 | } 45 | return null; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_03_Tree/_543_DiameterOfBinaryTree/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._03_Tree._543_DiameterOfBinaryTree; 2 | 3 | /* 4 | * 二叉树的直径 5 | * 6 | * 题目描述: 7 | * 给定一棵二叉树,你需要计算它的直径长度。 8 | * 一棵二叉树的直径长度是任意两个结点路径长度中的最大值。 9 | * 这条路径可能穿过也可能不穿过根结点。 10 | * 11 | * 思路: 12 | * 1. 要知道 dfs 函数的返回值的含义,当左孩子为空的时候,说明左孩子给父节点返回来的是 0; 13 | * 2. 当左孩子不为空的时候,我就继续让左孩子的左孩子去跑 dfs,最后给我返回来的就是 dfs 返回的结果再加上 1; 14 | * 3. 这个 1 就是我当前节点的值,然后再更新 res; 15 | * 4. 最后 return max 是为了求左右孩子中最长的路径。 16 | */ 17 | public class Solution { 18 | class TreeNode { 19 | int val; 20 | TreeNode left; 21 | TreeNode right; 22 | 23 | TreeNode(int val) { 24 | this.val = val; 25 | } 26 | } 27 | 28 | private int res = 0; 29 | 30 | public int diameterOfBinaryTree(TreeNode root) { 31 | if (root == null) { 32 | return 0; 33 | } 34 | 35 | dfs(root); 36 | return res; 37 | } 38 | 39 | private int dfs(TreeNode root) { 40 | if (root == null) { 41 | return 0; 42 | } 43 | 44 | int left = dfs(root.left); 45 | int right = dfs(root.right); 46 | res = Math.max(res, left + right); 47 | 48 | // 注意,这里让求的是【直径长度】,所以和边有关,因此需要加 1 49 | return Math.max(left, right) + 1; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_04_HashTable/_594_LongestHarmoniousSubsequence/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._04_HashTable._594_LongestHarmoniousSubsequence; 2 | 3 | import java.util.HashMap; 4 | 5 | /* 6 | * 最长和谐子序列 7 | * 8 | * 题目描述: 9 | * 和谐数组是指一个数组里元素的最大值和最小值之间的差别正好是 1。 10 | * 现在,给定一个整数数组,你需要在所有可能的子序列中找到最长的和谐子序列的长度。 11 | * 12 | * 思路: 13 | * 1. 由于差值是 1,因此其实找的就是相邻元素的个数; 14 | * 2. 可以使用哈希表,key 表示当前元素,value 表示当前元素所出现的次数; 15 | * 3. 在遍历的时候,如果 当前元素+1 存在哈希表中,则开始计算两者出现的次数,并不断更新最大值。 16 | */ 17 | public class Solution { 18 | public int findLHS(int[] nums) { 19 | if (nums == null || nums.length == 0) { 20 | return 0; 21 | } 22 | 23 | HashMap map = new HashMap<>(); 24 | for (int num : nums) { 25 | map.put(num, map.getOrDefault(num, 0) + 1); 26 | } 27 | int res = 0; 28 | for (int key : map.keySet()) { 29 | if (map.containsKey(key + 1)) { 30 | res = Math.max(res, map.get(key) + map.get(key + 1)); 31 | } 32 | } 33 | return res; 34 | } 35 | 36 | public static void main(String[] args) { 37 | Solution solution = new Solution(); 38 | int[] nums = {1, 3, 2, 2, 5, 2, 3, 7}; 39 | 40 | System.out.println(solution.findLHS(nums)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_03_Greedy/_455_AssignCookies/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._03_Greedy._455_AssignCookies; 2 | 3 | import java.util.Arrays; 4 | 5 | /* 6 | * 分配饼干 7 | * 8 | * 题目描述: 9 | * 假设你是一位很棒的家长,想要给你的孩子们一些小饼干。 10 | * 但是,每个孩子最多只能给一块饼干。对每个孩子 i ,都有一个胃口值 gi ,这是能让孩子们满足胃口的饼干的最小尺寸; 11 | * 并且每块饼干 j ,都有一个尺寸 sj 。如果 sj >= gi ,我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。 12 | * 你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。 13 | * 14 | * 思路: 15 | * 1. 给孩子和饼干都进行排序; 16 | * 2. 维护两个指针,如果当前尺寸的饼干满足当前孩子的胃口,则两个指针一起往后移动; 17 | * 3. 如果如果当前尺寸的饼干不能满足当前孩子的胃口,则只需要饼干往后移动一下,再进行判断即可。 18 | */ 19 | public class Solution { 20 | 21 | public static int findContentChildren(int[] g, int[] s) { 22 | if (g == null || s == null || g.length == 0 || s.length == 0) { 23 | return 0; 24 | } 25 | 26 | Arrays.sort(g); 27 | Arrays.sort(s); 28 | 29 | int gi = 0; 30 | int sj = 0; 31 | // 这个地方和判断是不是子序列类似 32 | while (gi < g.length && sj < s.length) { 33 | if (g[gi] <= s[sj]) { 34 | gi++; 35 | } 36 | sj++; 37 | } 38 | return gi; 39 | } 40 | 41 | public static void main(String[] args) { 42 | int[] g = {1, 2, 3}; 43 | int[] s = {1, 1}; 44 | System.out.println(findContentChildren(g, s)); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/SwordToOfferSolution/_64_Accumulate/Solution.java: -------------------------------------------------------------------------------- 1 | package SwordToOfferSolution._64_Accumulate; 2 | 3 | /* 4 | * 求 1+2+…+n 5 | * 6 | * 题目描述: 7 | * 求 1+2+…+n,要求不能使用乘除法、for、while、if、else、switch、case 等关键字及条件判断语句(A?B:C)。 8 | * 9 | * 思路: 10 | * 1. 对于常见的运算符: 11 | * 单目运算符:++ 和 --; 12 | * 双目运算符:+ 和 -; 13 | * 移位运算符:<< 和 >>; 14 | * 关系运算符: < 和 >; 15 | * 逻辑运算符:&&、||、|、^、= 16 | * 2. 由于关键字的限制,这里使用短路运算符 &&; 17 | * 3. 如果前面的条件为假,则整个表达式的结果就是假,后面的条件就不进行判断了; 18 | * 4. n 直到 0 就返回结果。 19 | * 5. 关于短路运算: 20 | * 例如 if (A && B): 21 | * 如果 A 为 false,则不会执行并判断 B,即 && 短路; 22 | * 例如 if (A || B): 23 | * 如果 A 为 true,则不会执行并判断 B,即 || 短路。 24 | * 6. 因此,如果想要当 n 等于 1 时终止递归的话,则需要使用 n > 1 && sunNums(n - 1); 25 | * 7. 即当 n 等于 1 时,n > 1 不成立,此时短路,从而终止 sunNums(n - 1) 递归。 26 | */ 27 | public class Solution { 28 | int res = 0; 29 | 30 | public int sumNums(int n) { 31 | boolean flag = (n > 1) && sumNums(n - 1) > 0; 32 | res += n; 33 | return res; 34 | } 35 | 36 | public int sumNums2(int n) { 37 | boolean flag = (n > 1) && (n += sumNums2(n - 1)) > 0; 38 | return n; 39 | } 40 | 41 | public static void main(String[] args) { 42 | Solution solution = new Solution(); 43 | 44 | System.out.println(solution.sumNums(9)); 45 | System.out.println(solution.sumNums2(9)); 46 | } 47 | } -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_03_Tree/_669_TrimABinarySearchTree/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._03_Tree._669_TrimABinarySearchTree; 2 | 3 | /* 4 | * 修剪二叉搜索树 5 | * 6 | * 题目描述: 7 | * 给定一个二叉搜索树,同时给定最小边界 L 和最大边界 R。 8 | * 通过修剪二叉搜索树,使得所有节点的值在 [L, R] 中 (R>=L)。 9 | * 你可能需要改变树的根节点,所以结果应当返回修剪好的二叉搜索树的新的根节点。 10 | * 11 | * 思路: 12 | * 1. 首先要知道二叉搜索树的特点,对于每一个节点,它左子树的左右节点都是比它小的,而它右子树的左右节点都是比它大的; 13 | * 2. 现在开始从根节点开始修建; 14 | * 3. 如果根节点的值比 L 小,则说明根节点的左子树都是比 L 小的,则需要将左子树剪掉,因此需要直接返回右子树的修剪结果; 15 | * 4. 如果根节点的值比 R 大,则说明根节点的右子树都是比 R 大的,则需要将右子树剪掉,因此需要直接返回左子树的修剪结果; 16 | * 5. 如果根节点没问题,则递归地修改左右子节点; 17 | * 6. 如果根节点为空,则无需修改,直接返回空即可。 18 | */ 19 | public class Solution { 20 | class TreeNode { 21 | int val; 22 | TreeNode left; 23 | TreeNode right; 24 | 25 | TreeNode(int val) { 26 | this.val = val; 27 | } 28 | } 29 | 30 | public TreeNode trimBST(TreeNode root, int L, int R) { 31 | if (root == null) { 32 | return null; 33 | } 34 | 35 | if (root.val < L) { 36 | return trimBST(root.right, L, R); 37 | } 38 | if (root.val > R) { 39 | return trimBST(root.left, L, R); 40 | } 41 | 42 | root.left = trimBST(root.left, L, R); 43 | root.right = trimBST(root.right, L, R); 44 | 45 | return root; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Other/AdvancedAlgorithm/_14_02_LongestSumSubArrayLength/Solution.java: -------------------------------------------------------------------------------- 1 | package Other.AdvancedAlgorithm._14_02_LongestSumSubArrayLength; 2 | 3 | /* 4 | * 求累加和等于 aim 的最长子数组(全是正数) 5 | * 6 | * 思路: 7 | * 使用双指针。 8 | */ 9 | public class Solution { 10 | 11 | public static int getMaxLength(int[] arr, int aim) { 12 | if (arr == null || arr.length == 0 || aim <= 0) { 13 | return 0; 14 | } 15 | 16 | int left = 0; 17 | int right = 0; 18 | int sum = arr[0]; 19 | int len = 0; 20 | while (right < arr.length) { 21 | if (sum == aim) { 22 | len = Math.max(len, right - left + 1); 23 | // 在相等的情况下,left 可以往右,right 也可以往右 24 | // 但记得在往右的过程中,需要将边界的那个值在 sum 中减掉 25 | sum -= arr[left]; 26 | left++; 27 | } else if (sum < aim) { 28 | right++; 29 | if (right == arr.length) { 30 | break; 31 | } 32 | sum += arr[right]; 33 | } else { 34 | sum -= arr[left]; 35 | left++; 36 | } 37 | } 38 | return len; 39 | } 40 | 41 | public static void main(String[] args) { 42 | int[] arr = {3, 2, 5, 1, 1, 1, 1, 1, 1}; 43 | System.out.println(getMaxLength(arr, 6)); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_03_Tree/_530_MinimumAbsoluteDifferenceInBST/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._03_Tree._530_MinimumAbsoluteDifferenceInBST; 2 | 3 | /* 4 | * 二叉搜索树的最小绝对差 5 | * 6 | * 题目描述: 7 | * 给你一棵所有节点为非负值的二叉搜索树,请你计算树中任意两节点的差的绝对值的最小值。 8 | * 9 | * 思路: 10 | * 1. 根据二叉搜索树中序遍历的特性,输出的结果是升序的; 11 | * 2. 因此可以在中序遍历的时候用当前遍历到的根节点的值减去左节点的值,然后每次更新最小值即可; 12 | * 3. 但需要注意的是,由于每次需要用到前一个节点的值,因此需要使用一个节点记录前一个节点引用。 13 | */ 14 | public class Solution { 15 | class TreeNode { 16 | int val; 17 | TreeNode left; 18 | TreeNode right; 19 | 20 | TreeNode(int val) { 21 | this.val = val; 22 | } 23 | } 24 | 25 | private int res = 0; 26 | private TreeNode pre = null; 27 | 28 | public int getMinimumDifference(TreeNode root) { 29 | if (root == null) { 30 | return 0; 31 | } 32 | 33 | inorder(root); 34 | return res; 35 | } 36 | 37 | private void inorder(TreeNode root) { 38 | if (root == null) { 39 | return; 40 | } 41 | 42 | inorder(root.left); 43 | if (pre != null) { 44 | // 注意使用 root 减去 pre, 45 | // 因为 root 比 pre 大 46 | res = Math.min(res, root.val - pre.val); 47 | } 48 | // 需要移动 pre 49 | pre = root; 50 | inorder(root.right); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_04_BinarySearch/_69_Sqrt_x/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._04_BinarySearch._69_Sqrt_x; 2 | 3 | /* 4 | * x 的平方根,即求开方 5 | * 6 | * 题目描述: 7 | * 实现 int sqrt(int x) 函数。 8 | * 计算并返回 x 的平方根,其中 x 是非负整数。 9 | * 由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。 10 | * 11 | * 思路: 12 | * 0. 整体思路就是使用二分法来猜 x 开根号的结果,高了就往低了猜,低了就往高了猜,范围越来越小; 13 | * 1. 其次,一个数 x 的平方根最多不会超过 x 的一半,例如 8 的平方根是 2.8,而 8 的一半是 4; 14 | * 2. 由于一个数 x 的平方根 sqrt(被开方)一定存在于 0~x 之内; 15 | * 3. 这里使用 sqrt == x /middle 防止溢出,而不是使用 middle*middle >x; 16 | * 5. 注意 8 的平方根是 2.8,则需要将 0.8 舍去,最后返回 2。 17 | */ 18 | public class Solution { 19 | 20 | public int mySqrt(int x) { 21 | if (x <= 1) { 22 | return x; 23 | } 24 | 25 | int left = 1; 26 | int right = x; 27 | while (left <= right) { 28 | int middle = left + ((right - left) >> 1); 29 | int sqrt = x / middle; 30 | if (sqrt == middle) { 31 | return sqrt; 32 | } else if (sqrt < middle) { 33 | right = middle - 1; 34 | } else { 35 | left = middle + 1; 36 | } 37 | } 38 | return right; 39 | } 40 | 41 | public static void main(String[] args) { 42 | Solution solution = new Solution(); 43 | 44 | System.out.println(solution.mySqrt(8)); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_04_BinarySearch/_744_FindSmallestLetterGreaterThanTarget/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._04_BinarySearch._744_FindSmallestLetterGreaterThanTarget; 2 | 3 | /* 4 | * 寻找比目标字母大的最小字母 5 | * 6 | * 题目描述: 7 | * 给定一个只包含小写字母的有序数组 letters 和一个目标字母 target,寻找有序数组里面比目标字母大的最小字母。 8 | * 9 | * 思路: 10 | * 1. 使用二分法进行查找; 11 | * 2. 注意 middle 是如何取值的; 12 | * 3. 如果找不到就返回数组中的第一个字符。 13 | */ 14 | public class Solution { 15 | public static char nextGreatestLetter(char[] letters, char target) { 16 | if (letters == null || letters.length == 0) { 17 | return target; 18 | } 19 | 20 | int left = 0; 21 | int right = letters.length - 1; 22 | while (left <= right) { 23 | // 注意:+ 的优先级比 >> 高,所以需要带括号 24 | int middle = left + ((right - left) >> 1); 25 | // 这里需要注意:如果先写从右侧查找,则需要指定等于 middle 26 | // 否则,则不需要指定等于 middle 27 | if (target >= letters[middle]) { 28 | left = middle + 1; 29 | } else { 30 | right = middle - 1; 31 | } 32 | } 33 | return left < letters.length ? letters[left] : letters[0]; 34 | } 35 | 36 | 37 | public static void main(String[] args) { 38 | char[] chars = {'c', 'f', 'j'}; 39 | System.out.println(nextGreatestLetter(chars, 'c')); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_01_ArrayAndMatrix/_26_RemoveDuplicatesFromSortedArray/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._01_ArrayAndMatrix._26_RemoveDuplicatesFromSortedArray; 2 | 3 | /* 4 | * 删除有序数组中的重复项 5 | * 6 | * 题目描述: 7 | * 给你一个升序排列的数组 nums,请你原地删除重复出现的元素,使每个元素只出现一次,返回删除后数组的新长度。 8 | * 元素的相对顺序应该保持一致。 9 | * 将最终结果插入 nums 的前 k 个位置后返回 k。 10 | * 不要使用额外的空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。 11 | * 12 | * 分析: 13 | * 1. 可以使用双指针,由于是升序数组,那么第一个元素已经处于最终的位置了,所以使用 idx 指向第二个元素,也就是可能会被插入的位置; 14 | * 2. 指针 i 从第二个元素开始往后移动,每次比较当前元素和前一个元素是否相等,若不相等,则将当前元素赋给 idx 位置,然后 idx++; 15 | * 3. 赋值也是删除元素的一种方式。 16 | */ 17 | public class Solution { 18 | public int removeDuplicates(int[] nums) { 19 | if (nums == null || nums.length == 0) { 20 | return 0; 21 | } 22 | 23 | int idx = 1; 24 | for (int i = 1; i < nums.length; i++) { 25 | if (nums[i - 1] != nums[i]) { 26 | nums[idx++] = nums[i]; 27 | } 28 | } 29 | 30 | return idx; 31 | } 32 | 33 | public static void main(String[] args) { 34 | Solution solution = new Solution(); 35 | int[] nums1 = {1, 1, 2}; 36 | int[] nums2 = {0, 0, 1, 1, 1, 2, 2, 3, 3, 4}; 37 | 38 | System.out.println(solution.removeDuplicates(nums1)); // 2 39 | System.out.println(solution.removeDuplicates(nums2)); // 5 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_01_ArrayAndMatrix/_766_ToeplitzMatrix/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._01_ArrayAndMatrix._766_ToeplitzMatrix; 2 | 3 | /* 4 | * 托普利茨矩阵 5 | * 6 | * 题目描述: 7 | * 如果一个矩阵的每一方向由左上到右下的对角线上具有相同元素,那么这个矩阵是托普利茨矩阵。 8 | * 给定一个 M x N 的矩阵,当且仅当它是托普利茨矩阵时返回 True。 9 | * 10 | * 思路: 11 | * 1. 观察矩阵元素的规律,我们发现只需要比较当前行中除了最后一个元素的其它元素是否与下一行的除了第一个元素之外的其它元素是否对应相等即可; 12 | */ 13 | public class Solution { 14 | public boolean isToeplitzMatrix(int[][] matrix) { 15 | if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { 16 | return false; 17 | } 18 | 19 | for (int i = 0; i < matrix.length - 1; i++) { 20 | for (int j = 0; j < matrix[0].length - 1; j++) { 21 | if (matrix[i][j] != matrix[i + 1][j + 1]) { 22 | return false; 23 | } 24 | } 25 | } 26 | return true; 27 | } 28 | 29 | public static void main(String[] args) { 30 | Solution solution = new Solution(); 31 | int[][] matrix1 = { 32 | {1, 2, 3, 4}, 33 | {5, 1, 2, 3}, 34 | {9, 5, 1, 2} 35 | }; 36 | int[][] matrix2 = {{1, 2}, {2, 2}}; 37 | 38 | System.out.println(solution.isToeplitzMatrix(matrix1)); 39 | System.out.println(solution.isToeplitzMatrix(matrix2)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_01_ArrayAndMatrix/_769_MaxChunksToMakeSorted/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._01_ArrayAndMatrix._769_MaxChunksToMakeSorted; 2 | 3 | /* 4 | * 最多能完成排序的块 5 | * 6 | * 题目描述: 7 | * 数组 arr 是 [0, 1, ..., arr.length - 1] 的一种排列,我们将这个数组分割成几个“块”,并将这些块分别进行排序。 8 | * 之后再连接起来,使得连接的结果和按升序排序后的原数组相同。 9 | * 我们最多能将数组分成多少块? 10 | * 11 | * 思路: 12 | * 0. 从前先后遍历,区间最大值等于数组索引的时候,就得到了一个所求区间; 13 | * 也就是说,一个区间内最大的数字,不应该大于这个区间最右边的 index,因此我们可以从左向右遍历数组的元素, 14 | * 如果已经观测到的最大值等于这个区间的 index,则可划分区间。 15 | * 1. 如果在分割后的数组中(也就是子数组中),最大元素的下标与 nums[i] 不等,那么排序后就无法形成从小到大连续的数组了; 16 | * 2. 所以只有找到最大的数与下标相等时,用来计数的 count 才能加 1; 17 | * 3. 如果没有找到,那么子数组继续向后寻找,找到后重新查找新的子数组。 18 | */ 19 | public class Solution { 20 | public int maxChunksToSorted(int[] nums) { 21 | if (nums == null || nums.length == 0) { 22 | return 0; 23 | } 24 | 25 | int res = 0; 26 | int cur = nums[0]; 27 | for (int i = 0; i < nums.length; i++) { 28 | cur = Math.max(cur, nums[i]); 29 | if (cur == i) { 30 | res++; 31 | } 32 | } 33 | return res; 34 | } 35 | 36 | public static void main(String[] args) { 37 | Solution solution = new Solution(); 38 | int[] nums = {1, 0, 2, 3, 4}; 39 | 40 | System.out.println(solution.maxChunksToSorted(nums)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_01_ArrayAndMatrix/_718_MaximumLengthOfRepeatedSubarray/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._01_ArrayAndMatrix._718_MaximumLengthOfRepeatedSubarray; 2 | 3 | /* 4 | * 最长重复子数组 5 | * 6 | * 题目描述: 7 | * 给两个整数数组 A 和 B ,返回两个数组中公共的、长度最长的子数组的长度。 8 | * 9 | * 思路: 10 | * 1. 定义 dp[i][j] 表示以元素 A[i-1] 与 B[j-1] 结尾的公共最长子数组; 11 | * 2. 如果 A[i-1]==B[j-1],则 dp[i][j]=dp[i-1][j-1]+1; 12 | * 3. 如果 A[i-1]!=B[j-1],则 dp[i][j]=0; 13 | * 4. 最后,如果构成了公共最长子数组,则记得维护它的最大长度,最终返回即可。 14 | */ 15 | public class Solution { 16 | public int findLength(int[] A, int[] B) { 17 | if (A == null || B == null || A.length == 0 || B.length == 0) { 18 | return 0; 19 | } 20 | 21 | int res = 0; 22 | int[][] dp = new int[A.length + 1][B.length + 1]; 23 | 24 | for (int i = 1; i <= A.length; i++) { 25 | for (int j = 1; j <= B.length; j++) { 26 | if (A[i - 1] == B[j - 1]) { 27 | dp[i][j] = dp[i - 1][j - 1] + 1; 28 | res = Math.max(res, dp[i][j]); 29 | } 30 | } 31 | } 32 | return res; 33 | } 34 | 35 | public static void main(String[] args) { 36 | Solution solution = new Solution(); 37 | int[] A = {1, 2, 3, 2, 1}; 38 | int[] B = {3, 2, 1, 4, 7}; 39 | 40 | System.out.println(solution.findLength(A, B)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_03_Greedy/_392_IsSubsequence/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._03_Greedy._392_IsSubsequence; 2 | 3 | /* 4 | * 判断是否是子序列 5 | * 6 | * 题目说明: 7 | * 给定字符串 s 和 t ,判断 s 是否为 t 的子序列。 8 | */ 9 | public class Solution { 10 | 11 | // 贪婪 12 | public static boolean isSubsequence1(String s, String t) { 13 | if (s == null || t == null) { 14 | return false; 15 | } 16 | 17 | char[] chars = s.toCharArray(); 18 | int index = -1; 19 | for (char c : chars) { 20 | index = t.indexOf(c, index + 1); 21 | if (index == -1) { 22 | return false; 23 | } 24 | } 25 | return true; 26 | } 27 | 28 | // 双指针 29 | public static boolean isSubsequence2(String s, String t) { 30 | if (s == null || t == null) { 31 | return false; 32 | } 33 | 34 | int i = 0; 35 | int j = 0; 36 | while (i < t.length() && j < s.length()) { 37 | if (t.charAt(i) == s.charAt(j)) { 38 | j++; 39 | } 40 | i++; 41 | } 42 | return j == s.length(); 43 | } 44 | 45 | public static void main(String[] args) { 46 | String s = "abc"; 47 | String s1 = "axc"; 48 | String t = "ahbgdc"; 49 | System.out.println(isSubsequence1(s, t)); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_02_String/_696_CountBinarySubstrings/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._02_String._696_CountBinarySubstrings; 2 | 3 | /* 4 | * 计数二进制子串 5 | * 6 | * 题目描述: 7 | * 给定一个字符串 s,计算具有相同数量 0 和 1 的非空(连续)子字符串的数量,并且这些子字符串中的所有 0 和所有 1 都是组合在一起的。 8 | * 重复出现的子串要计算它们出现的次数。 9 | * 10 | * 思路: 11 | * 1. 比价当前字符与前一个字符是否相等,如果相等,则当前长度加一,如果不等,则说明遇到了一个新的字符,则记录之前的长度, 12 | * 然后将当前长度置为 1,; 13 | * 2. 如果之前的长度大于等于当前长度,则说明已经满足了题目的要求,则 res++。 14 | */ 15 | public class Solution { 16 | 17 | public int countBinarySubstrings(String s) { 18 | if (s == null || s.length() == 0) { 19 | return 0; 20 | } 21 | int pre = 0; 22 | int cur = 1; 23 | int res = 0; 24 | for (int i = 1; i < s.length(); i++) { 25 | if (s.charAt(i) == s.charAt(i - 1)) { 26 | cur++; 27 | } else { 28 | pre = cur; 29 | cur = 1; 30 | } 31 | if (pre >= cur) { 32 | res++; 33 | } 34 | } 35 | return res; 36 | } 37 | 38 | public static void main(String[] args) { 39 | Solution solution = new Solution(); 40 | String s1 = "00110011"; 41 | String s2 = "10101"; 42 | 43 | System.out.println(solution.countBinarySubstrings(s1)); 44 | System.out.println(solution.countBinarySubstrings(s2)); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_03_Tree/_144_BinaryTreePreorderTraversal/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._03_Tree._144_BinaryTreePreorderTraversal; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Stack; 6 | 7 | /* 8 | * 二叉树的前序遍历 9 | * 10 | * 题目描述: 11 | * 给定一个二叉树,返回它的前序遍历。 12 | * 13 | * 思路: 14 | * 1. 如果使用迭代的方式,可以使用栈; 15 | * 2. 前序遍历的顺序是:根->左->右; 16 | * 3. 先将右子树入栈,再将左子树入栈。 17 | */ 18 | public class Solution { 19 | class TreeNode { 20 | int val; 21 | TreeNode left; 22 | TreeNode right; 23 | 24 | TreeNode(int val) { 25 | this.val = val; 26 | } 27 | } 28 | 29 | public List preorderTreaversal(TreeNode root) { 30 | List res = new ArrayList<>(); 31 | if (root == null) { 32 | return res; 33 | } 34 | 35 | Stack stack = new Stack<>(); 36 | stack.push(root); 37 | while (!stack.isEmpty()) { 38 | root = stack.pop(); 39 | res.add(root.val); 40 | // 先让右孩子入栈的原因是:我在遍历完根节点的时候,接下来需要遍历左子树, 41 | // 根据栈的出入规则,最先出来的应该是左子树 42 | if (root.right != null) { 43 | stack.push(root.right); 44 | } 45 | if (root.left != null) { 46 | stack.push(root.left); 47 | } 48 | } 49 | return res; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_08_LinkedList/_92_ReverseLinkedListII/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._08_LinkedList._92_ReverseLinkedListII; 2 | 3 | /* 4 | * 反转链表 Ⅱ 5 | * 6 | * 题目描述: 7 | * 反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。 8 | * 9 | * 说明: 10 | * 1 ≤ m ≤ n ≤ 链表长度。 11 | * 12 | * 思路: 13 | * 1. 头插法,将 m 到 n 之内的节点不断地插入到 m 所指的节点之前; 14 | * 2. 还是需要理清楚各个节点之间的引用关系,具体看代码。 15 | */ 16 | public class Solution { 17 | class ListNode { 18 | int val; 19 | ListNode next; 20 | 21 | ListNode(int val) { 22 | this.val = val; 23 | } 24 | } 25 | 26 | public ListNode reverseBetween(ListNode head, int m, int n) { 27 | if (head == null) { 28 | return null; 29 | } 30 | 31 | ListNode dummy = new ListNode(-1); 32 | dummy.next = head; 33 | ListNode pre = dummy; 34 | 35 | int mm = m; 36 | ListNode cur = head; 37 | // 注意这里是 --m, 38 | // cur 会来到 m 的位置 39 | while (--m > 0) { 40 | cur = cur.next; 41 | pre = pre.next; 42 | } 43 | 44 | int times = n - mm; 45 | while (cur.next != null && times-- > 0) { 46 | ListNode temp = cur.next; 47 | // 下面这一行语句用于防止链表丢失 48 | cur.next = temp.next; 49 | temp.next = pre.next; 50 | pre.next = temp; 51 | } 52 | return dummy.next; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Other/Other/GetMaxLength.java: -------------------------------------------------------------------------------- 1 | package Other.Other; 2 | 3 | import java.util.HashMap; 4 | 5 | /** 6 | * 1.给定一个数组,值全是正数,请返回累加和为给定值 k 的最长子数组长度。 7 | * 2.给定一个数组,值可以为正、负和0,请返回累加和为给定值 k 的最长子数组长度。 8 | */ 9 | public class GetMaxLength { 10 | public static int getMaxLength1(int[] arr, int k) { 11 | if (arr == null || arr.length == 0 || k < 0) return 0; 12 | 13 | int left = 0; 14 | int right = 0; 15 | int sum = arr[0]; 16 | int len = 0; 17 | while (right < arr.length) { 18 | if (sum == k) { 19 | len = Math.max(len, right - left + 1); 20 | sum -= arr[left++]; 21 | } else if (sum < k) { 22 | right++; 23 | if (right == arr.length) { 24 | break; 25 | } 26 | sum += arr[right]; 27 | } else { 28 | sum -= arr[left++]; 29 | } 30 | } 31 | return len; 32 | } 33 | 34 | public static int getMaxLength2(int[] arr, int k) { 35 | if (arr == null || arr.length == 0) return 0; 36 | int len = 0; 37 | int sum = 0; 38 | 39 | HashMap map = new HashMap<>(); 40 | map.put(0, -1); 41 | for (int i = 0; i < arr.length; i++) { 42 | sum += arr[i]; 43 | if (map.containsKey(sum - k)) len = Math.max(len, i - map.get(sum - k)); 44 | if (!map.containsKey(sum)) map.put(sum, i); 45 | } 46 | return len; 47 | } 48 | 49 | public static void main(String[] args) { 50 | int[] arr = {7, 3, 2, 1, 1, -6, 7}; 51 | int k = 7; 52 | System.out.println(getMaxLength2(arr, k)); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_03_Tree/_113_PathSumII/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._03_Tree._113_PathSumII; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /* 7 | * 路径总和 Ⅱ 8 | * 9 | * 题目描述: 10 | * 给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。 11 | * 12 | * 思路: 13 | * 1. 可以使用 DFS,如果目标和已经为 0,并且已经到了叶节点了,则开始收集路径上的节点; 14 | * 2. 否则继续递归当前节点的左右子树是否满足要求。 15 | */ 16 | public class Solution { 17 | class TreeNode { 18 | int val; 19 | TreeNode left; 20 | TreeNode right; 21 | 22 | TreeNode(int val) { 23 | this.val = val; 24 | } 25 | } 26 | 27 | private List> res = new ArrayList<>(); 28 | private List path = new ArrayList<>(); 29 | 30 | public List> pathSum(TreeNode root, int sum) { 31 | if (root == null) { 32 | return res; 33 | } 34 | 35 | dfs(root, sum); 36 | return res; 37 | } 38 | 39 | private void dfs(TreeNode root, int target) { 40 | if (root == null) { 41 | return; 42 | } 43 | 44 | path.add(root.val); 45 | target -= root.val; 46 | 47 | if (root.left == null && root.right == null && target == 0) { 48 | res.add(new ArrayList<>(path)); 49 | } 50 | dfs(root.left, target); 51 | dfs(root.right, target); 52 | path.remove(path.size() - 1); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_03_Tree/_298_BinaryTreeLongestConsecutiveSequence/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._03_Tree._298_BinaryTreeLongestConsecutiveSequence; 2 | 3 | /* 4 | * 二叉树最长连续序列 5 | * 6 | * 题目描述: 7 | * 给你一棵指定的二叉树,请你计算它最长连续序列路径的长度。 8 | * 该路径,可以是从某个初始结点到树中任意结点,通过「父 - 子」关系连接而产生的任意路径。 9 | * 这个最长连续的路径,必须从父结点到子结点,反过来是不可以的。 10 | * 11 | * 思路: 12 | * 1. 在递归的时候比较左右节点与当前节点的值,如果左节点或右节点减一之后与当前节点相等,则当前最长连续路径的长度加 1; 13 | * 2. “如果左节点或右节点减一之后与当前节点相等” 说明左右节点比当前节点大一个数,此时就需要将长度加 1。 14 | */ 15 | public class Solution { 16 | class TreeNode { 17 | int val; 18 | TreeNode left; 19 | TreeNode right; 20 | 21 | TreeNode(int val) { 22 | this.val = val; 23 | } 24 | } 25 | 26 | private int res = 0; 27 | 28 | public int longestConsecutive(TreeNode root) { 29 | if (root == null) { 30 | return 0; 31 | } 32 | 33 | process(root, 1); 34 | return res; 35 | } 36 | 37 | private void process(TreeNode root, int sum) { 38 | if (root == null) { 39 | return; 40 | } 41 | 42 | res = Math.max(res, sum); 43 | 44 | if (root.left != null) { 45 | process(root.left, root.left.val - 1 == root.val ? sum + 1 : 1); 46 | } 47 | if (root.right != null) { 48 | process(root.right, root.right.val - 1 == root.val ? sum + 1 : 1); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_08_LinkedList/_24_SwapNodesInPairs/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._08_LinkedList._24_SwapNodesInPairs; 2 | 3 | /* 4 | * 两两交换链表中的节点 5 | * 6 | * 题目描述: 7 | * 给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。 8 | * 你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。 9 | * 10 | * 思路: 11 | * 1. 这题需要改变两个节点的 next 指针的引用; 12 | * 2. 首先需要创建一个 dummy 节点,让它的 next 指向 head; 13 | * 3. 然后创建一个引用 pre,让其指向 dummy; 14 | * 4. 在 pre 的后继节点以及后继的后继不为 null 的情况下,遍历链表; 15 | * 5. 创建每个节点的引用,然后交换两个节点的引用,最后再让 pre 的 next 指向两者之间的后者节点; 16 | * 6. 也就是说 pre 需要来到 cur1 的位置,这点很重要; 17 | * 7. 最后需要返回 dummy 节点的 next; 18 | * 8. 画图解决一切。 19 | */ 20 | public class Solution { 21 | class ListNode { 22 | int val; 23 | ListNode next; 24 | 25 | ListNode(int val) { 26 | this.val = val; 27 | } 28 | } 29 | 30 | public ListNode swapPairs(ListNode head) { 31 | if (head == null) return null; 32 | 33 | ListNode dummy = new ListNode(-1); 34 | dummy.next = head; 35 | 36 | ListNode pre = dummy; 37 | 38 | while (pre.next != null && pre.next.next != null) { 39 | ListNode cur1 = pre.next; 40 | ListNode cur2 = pre.next.next; 41 | ListNode nex = cur2.next; 42 | 43 | cur1.next = nex; 44 | cur2.next = cur1; 45 | pre.next = cur2; 46 | 47 | pre = cur1; 48 | } 49 | return dummy.next; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Other/AdvancedAlgorithm/_27_LongestSubstring/Solution.java: -------------------------------------------------------------------------------- 1 | package Other.AdvancedAlgorithm._27_LongestSubstring; 2 | 3 | /* 4 | * 最长公共子串 5 | * 6 | * 思路: 7 | * 1. https://www.nowcoder.com/questionTerminal/02e7cc263f8a49e8b1e1dc9c116f7602; 8 | * 2. https://www.cnblogs.com/dartagnan/archive/2011/10/06/2199764.html 9 | */ 10 | public class Solution { 11 | 12 | public int findLongest(String s1, String s2) { 13 | if (s1 == null || s1.length() == 0 || s2 == null || s2.length() == 0) { 14 | return 0; 15 | } 16 | 17 | int len1 = s1.length(); 18 | int len2 = s2.length(); 19 | int[][] dp = new int[len1][len2]; 20 | int ans = 0; 21 | for (int i = 0; i < len1; ++i) { 22 | for (int j = 0; j < len2; ++j) { 23 | if (s1.charAt(i) == s2.charAt(j)) { 24 | if (i == 0 || j == 0) { 25 | dp[i][j] = 1; 26 | } else { 27 | dp[i][j] = dp[i - 1][j - 1] + 1; 28 | } 29 | ans = Math.max(ans, dp[i][j]); 30 | } 31 | } 32 | } 33 | return ans; 34 | } 35 | 36 | public static void main(String[] args) { 37 | Solution solution = new Solution(); 38 | String s1 = "1AB2345CD"; 39 | String s2 = "12345EF"; 40 | 41 | System.out.println(solution.findLongest(s1, s2)); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_01_DoublePointer/_11_ContainerWithMostWater/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._01_DoublePointer._11_ContainerWithMostWater; 2 | 3 | /* 4 | * 盛最多水的容器 5 | * 6 | * 题目描述: 7 | * 给定一个长度为 n 的整数数组 height。 8 | * 有 n 条垂线,第 i 条线的两个端点分别是 (i, 0) 和 (i, height[i])。 9 | * 找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。 10 | * 11 | * 说明:你不能倾斜容器,且 n 的值至少为 2。 12 | * 13 | * 思路: 14 | * 1. 使用双指针,从数组两端不断地向中间靠拢; 15 | * 2. 每次比较两个指针所指的元素大小(也就是比较高度),水的多少取决于短板的长度再乘以两个指针之间的距离; 16 | * 3. 即维护一个最终的结果 res,每次计算水的容量时更新 res,遍历完整个数组后,最终的 res 就是答案。 17 | */ 18 | public class Solution { 19 | public int maxArea(int[] height) { 20 | if (height == null || height.length < 2) { 21 | return 0; 22 | } 23 | 24 | int res = 0; 25 | int left = 0, right = height.length - 1; 26 | 27 | while (left < right) { 28 | if (height[left] < height[right]) { 29 | res = Math.max(res, (right - left) * height[left]); 30 | left++; 31 | } else { 32 | res = Math.max(res, (right - left) * height[right]); 33 | right--; 34 | } 35 | } 36 | 37 | return res; 38 | } 39 | 40 | public static void main(String[] args) { 41 | Solution solution = new Solution(); 42 | int[] height = {1, 8, 6, 2, 5, 4, 8, 3, 7}; 43 | 44 | System.out.println(solution.maxArea(height)); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_03_Tree/_513_FindBottomLeftTreeValue/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._03_Tree._513_FindBottomLeftTreeValue; 2 | 3 | import java.util.LinkedList; 4 | import java.util.Queue; 5 | 6 | /* 7 | * 找树左下角的值 8 | * 9 | * 题目描述: 10 | * 给定一个二叉树,在树的最后一行找到最左边的值。 11 | * 12 | * 思路: 13 | * 1. 一般情况下,当我们使用 BFS 的时候,会先将 root.left 入队,然后再将 root.right 入队,这样的话最后来到的是最右侧的节点; 14 | * 2. 现在反过来即可,让 root.right 先入队,然后再让 root.left 再入队。 15 | */ 16 | public class Solution { 17 | class TreeNode { 18 | int val; 19 | TreeNode left; 20 | TreeNode right; 21 | 22 | TreeNode(int val) { 23 | this.val = val; 24 | } 25 | } 26 | 27 | public int findBottomLeftValue(TreeNode root) { 28 | if (root == null) { 29 | return -1; 30 | } 31 | Queue queue = new LinkedList<>(); 32 | queue.offer(root); 33 | while (!queue.isEmpty()) { 34 | int size = queue.size(); 35 | for (int i = 0; i < size; i++) { 36 | root = queue.poll(); 37 | // 注意顺序 38 | if (root.right != null) { 39 | queue.offer(root.right); 40 | } 41 | if (root.left != null) { 42 | queue.offer(root.left); 43 | } 44 | } 45 | } 46 | // 最后返回的就是最左下角的节点 47 | return root.val; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Other/AdvancedAlgorithm/_15_MostEOR/Solution.java: -------------------------------------------------------------------------------- 1 | package Other.AdvancedAlgorithm._15_MostEOR; 2 | 3 | import java.util.HashMap; 4 | 5 | /* 6 | * 求数组中的异或和为 0 的最多划分 7 | * 8 | * 题目描述 9 | * 给定一个整型数组arr,其中可能有正有负有零。 10 | * 你可以随意把整个数组切成若干个不相容的子数组,求异或和为0的子数组最多可能有多少个? 11 | * 整数异或和定义:把数组中所有的数异或起来得到的值。 12 | * 13 | * 思路: 14 | * 与《累加和等于 k 的最长子数组的长度》题目类似,只不过这里又在里面套了一个 dp. 15 | */ 16 | public class Solution { 17 | public static int mostEOR(int[] arr) { 18 | if (arr == null || arr.length == 0) { 19 | return 0; 20 | } 21 | 22 | int res = 0; 23 | int xor = 0; 24 | int[] dp = new int[arr.length]; 25 | HashMap map = new HashMap<>(); 26 | map.put(0, -1); 27 | for (int i = 0; i < arr.length; i++) { 28 | xor ^= arr[i]; 29 | if (map.containsKey(xor)) { 30 | // 之前子数组的异或和中最早的位置 31 | int pre = map.get(xor); 32 | dp[i] = pre == -1 ? 1 : (dp[pre] + 1); 33 | } 34 | if (i > 0) { 35 | dp[i] = Math.max(res, dp[i]); 36 | } 37 | // 因为每次需要存的是更新后的位置,即求的是最晚的位置 38 | map.put(xor, i); 39 | res = Math.max(res, dp[i]); 40 | } 41 | return res; 42 | } 43 | 44 | public static void main(String[] args) { 45 | int[] arr = {3, 2, 1, 9, 0, 7, 0, 2, 1, 3}; 46 | System.out.println(mostEOR(arr)); 47 | } 48 | } -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_06_Search/_77_Combinations/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._06_Search._77_Combinations; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /* 7 | * 组合 8 | * 9 | * 题目描述: 10 | * 给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。 11 | * 12 | * 思路: 13 | * 1. 回溯法; 14 | * 2. 注意剪枝操作。 15 | */ 16 | public class Solution { 17 | public static List> combine(int n, int k) { 18 | List> res = new ArrayList<>(); 19 | if (n < 1 || k < 1) { 20 | return res; 21 | } 22 | 23 | List path = new ArrayList<>(); 24 | dfs(res, path, n, k, 1); 25 | return res; 26 | } 27 | 28 | 29 | public static void dfs(List> res, List path, int n, int k, int start) { 30 | // 来到了树的底部,则开始生成结果 31 | // 由于限制是 k 个数的组合 32 | // 这里也可以 k == 0 33 | if (k == path.size()) { 34 | res.add(new ArrayList<>(path)); 35 | return; 36 | } 37 | 38 | // 不断遍历 1 到 n 中的每个数 39 | // 剪枝:n - (k - path.size()) + 1 40 | for (int i = start; i <= n; i++) { 41 | path.add(i); 42 | // 注意: i + 1 43 | dfs(res, path, n, k - 1, i + 1); 44 | path.remove(path.size() - 1); 45 | } 46 | } 47 | 48 | public static void main(String[] args) { 49 | System.out.println(combine(4, 2)); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Other/BasicAlgorithm/_13_ArrayStack/Solution.java: -------------------------------------------------------------------------------- 1 | package Other.BasicAlgorithm._13_ArrayStack; 2 | 3 | /* 4 | * 用数组实现固定的栈 5 | * 思路: 6 | * 1. 用 index 表示新进来的数应该放在哪个位置; 7 | * 2. 如果需要添加元素(进栈),则将新的元素放到 index 的位置,然后 index++; 8 | * 3. 如果需要删除元素(出栈),则出去的就是 --index 位置上的元素。 9 | */ 10 | public class Solution { 11 | public static class ArrayStack { 12 | private Integer[] arr; 13 | private Integer index; 14 | 15 | // 由于是固定数组,所以需要给定一个初始化数组大小 16 | public ArrayStack(int initSize) { 17 | if (initSize < 0) { 18 | throw new IllegalArgumentException("The init size is less than 0."); 19 | } 20 | arr = new Integer[initSize]; 21 | index = 0; 22 | } 23 | 24 | // 取栈顶元素 25 | public Integer peek() { 26 | if (index == 0) { 27 | return null; 28 | } 29 | return arr[index - 1]; 30 | } 31 | 32 | // 压栈操作 33 | public void push(int num) { 34 | if (index == arr.length) { 35 | throw new ArrayIndexOutOfBoundsException("The stack is full."); 36 | } 37 | arr[index++] = num; 38 | } 39 | 40 | // 出栈操作 41 | public Integer pop() { 42 | if (index == 0) { 43 | throw new ArrayIndexOutOfBoundsException("The stack is empty."); 44 | } 45 | return arr[--index]; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_07_Sort/_75_SortColors/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._07_Sort._75_SortColors; 2 | 3 | /* 4 | * 颜色分类 5 | * 6 | * 题目描述: 7 | * 给定一个包含红色、白色和蓝色,一共 n 个元素的数组, 8 | * 原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。 9 | * 使用整数 0、 1 和 2 分别表示红色、白色和蓝色。 10 | * 11 | * 思路: 12 | * 1. 荷兰国旗问题的修改版,只不过这里的数组只包含 0、1 和 2; 13 | * 2. zero 表示等于 0 的区域,two 表示等于 2 的区域,one 表示等于 1 的区域; 14 | * 3. 全程查看 arr[one] 的状态,根据不同的状态进行不同的交换。 15 | */ 16 | public class Solution { 17 | 18 | public static void sortColors(int[] arr) { 19 | if (arr == null || arr.length == 0) { 20 | return; 21 | } 22 | 23 | int zero = -1; 24 | int one = 0; 25 | int two = arr.length; 26 | while (one < two) { 27 | if (arr[one] == 0) { 28 | swap(arr, ++zero, one++); 29 | } else if (arr[one] == 2) { 30 | swap(arr, --two, one); 31 | } else { 32 | one++; 33 | } 34 | } 35 | } 36 | 37 | public static void swap(int[] arr, int i, int j) { 38 | int temp = arr[i]; 39 | arr[i] = arr[j]; 40 | arr[j] = temp; 41 | } 42 | 43 | public static void main(String[] args) { 44 | int[] arr = {2, 0, 2, 1, 1, 0}; 45 | int[] arr1 = {1, 0, 2}; 46 | 47 | sortColors(arr1); 48 | for (int i : arr1) { 49 | System.out.print(i + " "); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_01_ArrayAndMatrix/_252_MeetingRooms/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._01_ArrayAndMatrix._252_MeetingRooms; 2 | 3 | import java.util.Arrays; 4 | import java.util.Comparator; 5 | 6 | /* 7 | * 会议室 8 | * 9 | * 题目描述: 10 | * 给定一个会议时间安排的数组,每个会议时间都会包括开始和结束的时间 [[s1,e1],[s2,e2],...] (si < ei),请你判断一个人是否能够参加这里面的全部会议。 11 | * 12 | * 思路: 13 | * 1. 每个子数组按照左边界元素排序,判断当前数组的右边界元素是否大于下一个数组的左边界元素; 14 | * 2. 若成立,则会议时间存在重合,因此无法参加全部的会议。 15 | */ 16 | public class Solution { 17 | public boolean canAttendMeetings(int[][] intervals) { 18 | if (intervals == null || intervals.length == 0 || intervals[0].length == 0) { 19 | return false; 20 | } 21 | 22 | //Arrays.sort(intervals, Comparator.comparingInt(a -> a[0])); 23 | Arrays.sort(intervals, (a, b) -> a[0] - b[0]); 24 | for (int i = 0; i + 1 < intervals.length; i++) { 25 | if (intervals[i][1] > intervals[i + 1][0]) { 26 | return false; 27 | } 28 | } 29 | 30 | return true; 31 | } 32 | 33 | public static void main(String[] args) { 34 | int[][] intervals1 = {{0, 30}, {5, 10}, {15, 20}}; 35 | int[][] intervals2 = {{7, 10}, {2, 4}}; 36 | 37 | Solution solution = new Solution(); 38 | System.out.println(solution.canAttendMeetings(intervals1)); // false 39 | System.out.println(solution.canAttendMeetings(intervals2)); // true 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_05_StackAndQueue/_232_ImplementQueueUsingStacks/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._05_StackAndQueue._232_ImplementQueueUsingStacks; 2 | 3 | import java.util.Stack; 4 | 5 | /* 6 | * 用栈实现队列 7 | * 8 | * 题目描述: 9 | * 使用栈实现队列的下列操作: 10 | * push(x) -- 将一个元素放入队列的尾部。 11 | * pop() -- 从队列首部移除元素。 12 | * peek() -- 返回队列首部的元素。 13 | * empty() -- 返回队列是否为空 14 | * 15 | * 思路: 16 | * 1. 这里格外注意两个栈之间的逻辑顺序。 17 | */ 18 | public class Solution { 19 | class MyQueue { 20 | private Stack s1; 21 | private Stack s2; 22 | 23 | public MyQueue() { 24 | s1 = new Stack<>(); 25 | s2 = new Stack<>(); 26 | } 27 | 28 | public void push(int x) { 29 | s1.push(x); 30 | } 31 | 32 | public int pop() { 33 | // 只要 s2 为空,那么就需要将 s1 中的所有元素灌入到 s2 中 34 | if (s2.isEmpty()) { 35 | while (!s1.isEmpty()) { 36 | s2.push(s1.pop()); 37 | } 38 | } 39 | return s2.pop(); 40 | } 41 | 42 | public int peek() { 43 | if (s2.isEmpty()) { 44 | while (!s1.isEmpty()) { 45 | s2.push(s1.pop()); 46 | } 47 | } 48 | return s2.peek(); 49 | } 50 | 51 | public boolean empty() { 52 | return s1.isEmpty() && s2.isEmpty(); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_08_Mathematics/_400_NthDigit/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._08_Mathematics._400_NthDigit; 2 | 3 | /* 4 | * 第 N 个数字 5 | * 6 | * 题目描述: 7 | * 数字以 0123456789101112131415… 的格式序列化到一个字符序列中。 8 | * 在这个序列中,第 5 位(从下标 0 开始计数)是 5,第 13 位是 1,第 19 位是 4,等等。 9 | * 10 | * 思路: 11 | * 0. 关键就是找第 n 个数字在哪个整数上,例如第 11 个数字是整数 10 的 0 部分; 12 | * 1. 对于给定的 n: 13 | * 首先确定 n 一共有几位数,例如 11 是 2 位数,123 是 3 位数,2414 是 4 位数,记 digit; 14 | * 然后确定 n 所在的数字,记 num; 15 | * 最后确定 n 是 num 中的哪一位,并将结果返回。 16 | * 2. 例如,2901 = 9 + 180 + 2700 + 12,是 4 位数,第 12 位; 17 | * 3. 在哪个整数中?1000 + (12 - 1) / 4 = 1000 + 2 = 1002; 18 | * 4. 然后在 1002 中确定最终是哪一位?即 (n - 1) % 4 = 3,s.charAt(3) = 2。 19 | * 5. https://i.loli.net/2020/05/19/WSs1DrnChpPFJV6.png 20 | */ 21 | public class Solution { 22 | public int findNthDigit(int n) { 23 | // n 所在数字的位数 24 | int digit = 1; 25 | // 数字范围开始的第一个数 26 | long start = 1; 27 | // 占多少位 28 | long count = 9; 29 | while (n > count) { 30 | n -= count; 31 | digit++; 32 | start *= 10; 33 | count = digit * start * 9; 34 | } 35 | long num = start + (n - 1) / digit; 36 | return Long.toString(num).charAt((n - 1) % digit) - '0'; 37 | } 38 | 39 | public static void main(String[] args) { 40 | Solution solution = new Solution(); 41 | System.out.println(solution.findNthDigit(2901)); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_05_StackAndQueue/_225_ImplementStackUsingQueues/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._05_StackAndQueue._225_ImplementStackUsingQueues; 2 | 3 | import java.util.LinkedList; 4 | import java.util.Queue; 5 | 6 | /* 7 | * 用队列实现栈 8 | * 9 | * 题目描述: 10 | * 使用队列实现栈的下列操作: 11 | * push(x) -- 元素 x 入栈 12 | * pop() -- 移除栈顶元素 13 | * top() -- 获取栈顶元素 14 | * empty() -- 返回栈是否为空 15 | * 16 | * 思路: 17 | * 1. 还是使用两个队列进行实现,只不过一个是普通的队列,另一个需要使用双端队列; 18 | * 2. 普通队列正常入队,正常出队,而双端队列在入队的时候需要从尾部入队; 19 | * 3. 也是理清楚这两个队列之间的逻辑关系即可。 20 | */ 21 | public class Solution { 22 | class MyStack { 23 | 24 | private Queue q1; 25 | private LinkedList q2; 26 | 27 | public MyStack() { 28 | q1 = new LinkedList<>(); 29 | q2 = new LinkedList<>(); 30 | } 31 | 32 | public void push(int x) { 33 | q1.offer(x); 34 | q2.addFirst(x); 35 | } 36 | 37 | public int pop() { 38 | if (q1.isEmpty() && q2.isEmpty()) { 39 | return -1; 40 | } 41 | q1.poll(); 42 | return q2.pollFirst(); 43 | } 44 | 45 | public int top() { 46 | if (q2.isEmpty()) { 47 | return -1; 48 | } 49 | return q2.peekFirst(); 50 | } 51 | 52 | public boolean empty() { 53 | return q1.isEmpty() && q2.isEmpty(); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Other/BasicAlgorithm/_20_ZigZagPrintMatrix/Solution.java: -------------------------------------------------------------------------------- 1 | package Other.BasicAlgorithm._20_ZigZagPrintMatrix; 2 | 3 | /* 4 | * 之字形打印矩阵 5 | * https://i.loli.net/2019/12/09/gr27ViOonkjflEP.png 6 | * 用 A 和 B 控制点的位置,用一个布尔变量控制从左下到右上,还是右上到左下。 7 | */ 8 | public class Solution { 9 | public static void printMatrixZigZag(int[][] matrix) { 10 | int aR = 0; 11 | int aC = 0; 12 | int bR = 0; 13 | int bC = 0; 14 | int endR = matrix.length - 1; 15 | int endC = matrix[0].length - 1; 16 | boolean fromUp = false; 17 | 18 | // 点 A 走到右边,然后向下走,直至走到最后 19 | while (aR != endR + 1) { 20 | printLevel(matrix, aR, aC, bR, bC, fromUp); 21 | // A 的列数来到最后一列,这时 A 才往下走,即行数增加,否则不变 22 | aR = aC == endC ? aR + 1 : aR; 23 | aC = aC == endC ? aC : aC + 1; 24 | bC = bR == endR ? bC + 1 : bC; 25 | bR = bR == endR ? bR : bR + 1; 26 | fromUp = !fromUp; 27 | } 28 | System.out.println(); 29 | } 30 | 31 | // 具体的打印方式 32 | private static void printLevel(int[][] matrix, int aR, int aC, int bR, int bC, boolean fromUp) { 33 | if (fromUp) { 34 | while (aR != bR + 1) { 35 | System.out.println(matrix[aR++][aC--] + " "); 36 | } 37 | } else { 38 | while (bR != aR - 1) { 39 | System.out.println(matrix[bR--][bC++] + " "); 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_02_DP/_322_CoinChange/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._02_DP._322_CoinChange; 2 | 3 | import java.util.Arrays; 4 | 5 | /* 6 | * 零钱兑换 7 | * 8 | * 题目描述: 9 | * 给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。 10 | * 如果没有任何一种硬币组合能组成总金额,返回 -1。 11 | * 12 | * 思路: 13 | * 1. 整体流程是使用一维 dp 数组进行实现,不断地填充数组; 14 | * 2. 具体流程:https://www.youtube.com/watch?v=jgiZlGzXMBw; 15 | * 3. 画图理解。 16 | */ 17 | public class Solution { 18 | public int coinChange(int[] coins, int amount) { 19 | if (coins == null || coins.length == 0) { 20 | return -1; 21 | } 22 | 23 | // 0~11 24 | int[] dp = new int[amount + 1]; 25 | Arrays.fill(dp, amount + 1); 26 | dp[0] = 0; 27 | 28 | // i 表示 dp 数组的索引,我们需要做的就是不断地填充 dp 数组 29 | for (int i = 1; i <= amount; i++) { 30 | // 每次获取每个硬币 31 | for (int j = 0; j < coins.length; j++) { 32 | if (i - coins[j] >= 0) { 33 | dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1); 34 | } 35 | } 36 | } 37 | // 需要考虑不可能达到金额的情况 38 | return dp[amount] > amount ? -1 : dp[amount]; 39 | } 40 | 41 | public static void main(String[] args) { 42 | Solution solution = new Solution(); 43 | int[] coins = {1, 2, 5}; 44 | int amount = 11; 45 | 46 | System.out.println(solution.coinChange(coins, amount)); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/SwordToOfferSolution/_09_QueueWithTwoStacks/Solution.java: -------------------------------------------------------------------------------- 1 | package SwordToOfferSolution._09_QueueWithTwoStacks; 2 | 3 | import java.util.Stack; 4 | 5 | /* 6 | * 用两个栈实现队列 7 | * 8 | * 题目描述: 9 | * 请实现两个函数,分别完成在队列尾部插入结点 appendTail 和在队列头部删除结点 deleteHead 的功能。 10 | * 11 | * 思路: 12 | * 1. 如果 stackPush 决定要把元素往 stackPop 里送的话,那么要一次性的送全部送完; 13 | * 2. 如果 stackPop 里面有东西,则 stackPush 一定不要送。 14 | */ 15 | public class Solution { 16 | static class MyQueue { 17 | Stack stackPush; 18 | Stack stackPop; 19 | 20 | public MyQueue() { 21 | stackPush = new Stack<>(); 22 | stackPop = new Stack<>(); 23 | } 24 | 25 | public void appendTail(int value) { 26 | stackPush.push(value); 27 | } 28 | 29 | public int deleteHead() { 30 | if (stackPop.isEmpty()) { 31 | while (!stackPush.isEmpty()) { 32 | stackPop.push(stackPush.pop()); 33 | } 34 | } 35 | 36 | return stackPop.pop(); 37 | } 38 | 39 | public int peek() { 40 | if (stackPop.isEmpty()) { 41 | while (!stackPush.isEmpty()) { 42 | stackPop.push(stackPush.pop()); 43 | } 44 | } 45 | 46 | return stackPop.peek(); 47 | } 48 | 49 | public boolean empty() { 50 | return stackPop.isEmpty() && stackPush.empty(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/SwordToOfferSolution/_15_NumberOf1InBinary/Solution.java: -------------------------------------------------------------------------------- 1 | package SwordToOfferSolution._15_NumberOf1InBinary; 2 | 3 | /* 4 | * 二进制中 1 的个数 5 | * 6 | * 题目描述: 7 | * 请实现一个函数,输入一个整数,输出该数二进制表示中 1 的个数。 8 | * 例如把 9 表示成二进制是 1001,有两位是 1。因此如果输入 9,该函数输出 2。 9 | * 10 | * 思路: 11 | * 1. 一个数 n 与一个比它小 1 的数 n-1 进行与运算,得到的结果会消除 n 中最低位上的那个 1; 12 | * 2. 由于每次都会消除 1 个 1,所以等到 n 为 0 的时候,消除的次数就是 n 中 1 的个数。 13 | * 3. 还可以使用 lowbit 方法。 14 | */ 15 | public class Solution { 16 | public int numberOf1(int n) { 17 | int count = 0; 18 | // 注意这里的 n 是不等于 0 19 | while (n != 0) { 20 | n &= (n - 1); 21 | count++; 22 | } 23 | return count; 24 | } 25 | 26 | public int numberOf1_2(int n) { 27 | int ans = 0; 28 | while (n != 0) { 29 | n -= (n & -n); 30 | ans++; 31 | } 32 | return ans; 33 | } 34 | 35 | public static void main(String[] args) { 36 | Solution solution = new Solution(); 37 | 38 | System.out.println(solution.numberOf1(7)); 39 | System.out.println(solution.numberOf1(2097152)); 40 | System.out.println(solution.numberOf1(00000000000000000000000000001011)); 41 | 42 | System.out.println("--"); 43 | System.out.println(solution.numberOf1_2(7)); 44 | System.out.println(solution.numberOf1_2(00000000000000000000000010000000)); 45 | System.out.println(solution.numberOf1_2(00000000000000000000000000001011)); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_06_Search/_113_PathSumII/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._06_Search._113_PathSumII; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /* 7 | * 路径总和 Ⅱ 8 | * 9 | * 题目描述: 10 | * 给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。 11 | * 12 | * 思路: 13 | * 1. 先序遍历二叉树,把结点加入路径; 14 | * 2. 在先序遍历中,记录从根节点到当前节点的路径: 15 | * 如果当前路径是从根节点到叶节点形成的路径并且各节点值的和等于目标值 sum, 16 | * 则将此路径加入到结果集中。 17 | */ 18 | public class Solution { 19 | class TreeNode { 20 | int val; 21 | TreeNode left; 22 | TreeNode right; 23 | 24 | TreeNode(int val) { 25 | this.val = val; 26 | } 27 | } 28 | 29 | List> res = new ArrayList<>(); 30 | List path = new ArrayList<>(); 31 | 32 | public List> pathSum(TreeNode root, int sum) { 33 | if (root == null) { 34 | return res; 35 | } 36 | 37 | dfs(root, sum); 38 | return res; 39 | } 40 | 41 | private void dfs(TreeNode root, int target) { 42 | if (root == null) { 43 | return; 44 | } 45 | 46 | path.add(root.val); 47 | target -= root.val; 48 | 49 | if (target == 0 && root.left == null && root.right == null) { 50 | res.add(new ArrayList<>(path)); 51 | } 52 | dfs(root.left, target); 53 | dfs(root.right, target); 54 | path.remove(path.size() - 1); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_01_DoublePointer/_415_AddStrings/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._01_DoublePointer._415_AddStrings; 2 | 3 | /* 4 | * 字符串相加 5 | * 6 | * 题目描述: 7 | * 给定两个字符串形式的非负整数 num1 和 num2 ,计算它们的和。 8 | * 9 | * 思路: 10 | * 1. 使用双指针,分别指向两个字符串的末尾,逐渐向前计算,即模式计算加法的过程; 11 | * 2. 当指针走过了其中一个字符串的头部的时候,需要给其中短的字符串添加 0,以便于计算; 12 | * 3. 最后遍历完 num1 和 num2 后跳出循环,需要根据进位来判断是否在头部添加进位 1。 13 | */ 14 | public class Solution { 15 | public String addString(String num1, String num2) { 16 | StringBuilder res = new StringBuilder(); 17 | int i = num1.length() - 1; 18 | int j = num2.length() - 1; 19 | int carry = 0; 20 | // 注意:这里使用 或 运算,因为 num1 和 num2 的长度不一定相等, 21 | // 所以,只要其中有一个字符串还可以计算,那么就继续执行 while 循环语句 22 | while (i >= 0 || j >= 0) { 23 | // 取 i 或 j 所指位置上的数字 24 | int n1 = (i >= 0) ? num1.charAt(i--) - '0' : 0; 25 | int n2 = (j >= 0) ? num2.charAt(j--) - '0' : 0; 26 | int sum = n1 + n2 + carry; 27 | carry = sum / 10; 28 | res.append(sum % 10); 29 | } 30 | if (carry == 1) { 31 | res.append(1); 32 | } 33 | 34 | return res.reverse().toString(); 35 | } 36 | 37 | public static void main(String[] args) { 38 | Solution solution = new Solution(); 39 | 40 | String s1 = "123"; 41 | String s2 = "456"; 42 | System.out.println(solution.addString(s1, s2)); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/SwordToOfferSolution/_45_SortArrayForMinNumber/Solution.java: -------------------------------------------------------------------------------- 1 | package SwordToOfferSolution._45_SortArrayForMinNumber; 2 | 3 | import java.util.Arrays; 4 | 5 | /* 6 | * 把数组排成最小的数 7 | * 8 | * 题目描述: 9 | * 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。 10 | * 例如输入数组 {3, 32, 321},则打印出这 3 个数字能排成的最小数字 321323。 11 | * 12 | * 思路: 13 | * 1. 比较字符串 s1s2 与 s2s1 的大小,如果 s1s2 > s2s1,则把 s2 排在前面;否则把 s1 排在前面; 14 | * 2. 由于在拼接数字的时候有可能会涉及到整数范围的溢出(大数问题),所以使用字符串表示; 15 | * 3. 注意在拼接的时候,由于 "" 的位置不同,则会产生不同的效果。 16 | */ 17 | public class Solution { 18 | public String minNumber(int[] nums) { 19 | if (nums == null || nums.length == 0) { 20 | return ""; 21 | } 22 | 23 | String[] str = new String[nums.length]; 24 | for (int i = 0; i < nums.length; i++) { 25 | str[i] = String.valueOf(nums[i]); 26 | } 27 | 28 | Arrays.sort(str, (o1, o2) -> (o1 + o2).compareTo(o2 + o1)); 29 | StringBuilder sb = new StringBuilder(); 30 | for (String s : str) { 31 | sb.append(s); 32 | } 33 | return sb.toString(); 34 | } 35 | 36 | public static void main(String[] args) { 37 | Solution solution = new Solution(); 38 | int[] nums = {3, 30, 34, 5, 9}; 39 | 40 | System.out.println(solution.minNumber(nums)); 41 | 42 | String s1 = 123 + "" + 123; 43 | String s2 = 123 + 123 + ""; 44 | System.out.println(s1); // 123123 45 | System.out.println(s2); // 246 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_05_StackAndQueue/_155_MinStack/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._05_StackAndQueue._155_MinStack; 2 | 3 | import java.util.Stack; 4 | 5 | /* 6 | * 最小栈 7 | * 8 | * 题目描述: 9 | * 设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。 10 | * push(x) —— 将元素 x 推入栈中。 11 | * pop() —— 删除栈顶的元素。 12 | * top() —— 获取栈顶元素。 13 | * getMin() —— 检索栈中的最小元素。 14 | * 15 | * 思路: 16 | * 1. 维护两个栈即可。 17 | */ 18 | public class Solution { 19 | class MinStack { 20 | 21 | private Stack s1; 22 | private Stack s2; 23 | 24 | public MinStack() { 25 | s1 = new Stack<>(); 26 | s2 = new Stack<>(); 27 | } 28 | 29 | public void push(int x) { 30 | s1.push(x); 31 | if (s2.isEmpty() || x < s2.peek()) { 32 | s2.push(x); 33 | } else { 34 | s2.push(s2.peek()); 35 | } 36 | } 37 | 38 | public void pop() { 39 | if (!s1.isEmpty() && !s2.isEmpty()) { 40 | s1.pop(); 41 | s2.pop(); 42 | } 43 | } 44 | 45 | public int top() { 46 | if (!s1.isEmpty()) { 47 | return s1.peek(); 48 | } 49 | return -1; 50 | } 51 | 52 | public int getMin() { 53 | if (!s2.isEmpty()) { 54 | return s2.peek(); 55 | } 56 | return -1; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/SwordToOfferSolution/_44_DigitsInSequence/Solution.java: -------------------------------------------------------------------------------- 1 | package SwordToOfferSolution._44_DigitsInSequence; 2 | 3 | /* 4 | * 数字序列中的某一位数字 5 | * 6 | * 题目描述: 7 | * 数字以 0123456789101112131415… 的格式序列化到一个字符序列中。 8 | * 在这个序列中,第 5 位(从 0 开始计数)是 5,第 13 位是 1,第 19 位是 4 等等。 9 | * 请写一个函数求任意位对应的数字。 10 | * 11 | * 思路一: 12 | * https://dyfloveslife.github.io/2019/11/29/offer-DigitsInSequence/ 13 | * 14 | * 思路二: 15 | * 详见:https://github.com/dyfloveslife/LeetCodeAndSwordToOffer/blob/master/src/LeetCodeSolution/AlgorithmThought/_08_Mathematics/_400_NthDigit/Solution.java 16 | */ 17 | public class Solution { 18 | public int digitsInSequence(int[] arr, int n) { 19 | if (n < 0) { 20 | return -1; 21 | } 22 | if (n <= 9) { 23 | return n; 24 | } 25 | 26 | int index = 0; 27 | for (int i = 1; i <= 9; i++) { 28 | if (arr[i] >= n) { 29 | index = i; 30 | break; 31 | } 32 | } 33 | n = n - arr[index - 1]; 34 | int temp1 = n / index; 35 | int temp2 = n % index; 36 | int temp3 = (int) (temp1 + Math.pow(10, index - 1)); 37 | return (int) (temp3 / Math.pow(10, index - temp2 - 1)) % 10; 38 | } 39 | 40 | public static void main(String[] args) { 41 | Solution solution = new Solution(); 42 | int[] arr = {0, 10, 190, 2890, 38890, 488890, 5888890, 68888890, 788888890}; 43 | 44 | System.out.println(solution.digitsInSequence(arr, 11)); 45 | } 46 | } -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_03_Tree/_145_BinaryTreePostorderTraversal/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._03_Tree._145_BinaryTreePostorderTraversal; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Stack; 6 | 7 | /* 8 | * 二叉树的后序遍历 9 | * 10 | * 题目描述: 11 | * 给定一个二叉树,返回它的后序遍历。 12 | * 13 | * 思路: 14 | * 1. 后序遍历需要用到两个栈; 15 | * 2. 学会了前序遍历,那么后序遍历也就很清楚了; 16 | * 3. 只需要将前序遍历的入栈顺序修改一下,让其进入到另外一个栈中,则就称为了后序遍历; 17 | * 4. 当然也可以只使用一个栈,然后在最后的时候将这个 list 进行反转即可。 18 | */ 19 | public class Solution { 20 | class TreeNode { 21 | int val; 22 | TreeNode left; 23 | TreeNode right; 24 | 25 | TreeNode(int val) { 26 | this.val = val; 27 | } 28 | } 29 | 30 | public List postorderTraversal(TreeNode root) { 31 | List res = new ArrayList<>(); 32 | if (root == null) { 33 | return res; 34 | } 35 | 36 | Stack s1 = new Stack<>(); 37 | Stack s2 = new Stack<>(); 38 | s1.push(root); 39 | 40 | while (!s1.isEmpty()) { 41 | root = s1.pop(); 42 | s2.push(root); 43 | if (root.left != null) { 44 | s1.push(root.left); 45 | } 46 | if (root.right != null) { 47 | s1.push(root.right); 48 | } 49 | } 50 | while (!s2.isEmpty()) { 51 | res.add(s2.pop().val); 52 | } 53 | 54 | return res; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Other/BasicAlgorithm/_35_LowestLexicography/Solution.java: -------------------------------------------------------------------------------- 1 | package Other.BasicAlgorithm._35_LowestLexicography; 2 | 3 | import java.util.Arrays; 4 | import java.util.Comparator; 5 | 6 | /* 7 | * 字典序最小的字符串 8 | * 9 | * 题目描述: 10 | * 有一个包含许多字符串的数组,将每个字符串进行拼接,这肯定有许多不同种拼接的结果。 11 | * 找出其中字典序最小的拼接后的字符串。 12 | * 13 | * 思路: 14 | * 1. 使用贪心,但不能按照比较字符的大小进行,例如 b 和 ba,得到的结果是 bab < bba,其实答案是 bba < bab; 15 | * 2. 之前的方法是比较 str1 与 str2 的大小,谁小就将谁放在前面; 16 | * 3. 但是以上是不正确的,反例也给出了; 17 | * 4. 正确的比较方式是如果 str1.str2 ≤ str2.str1,则将 str1 放在前面; 18 | * 5. 这里涉及到比较策略的传递性。 19 | */ 20 | public class Solution { 21 | 22 | public static class MyComparator implements Comparator { 23 | 24 | @Override 25 | public int compare(String a, String b) { 26 | return (a + b).compareTo(b + a); 27 | } 28 | } 29 | 30 | public static String lowestString(String[] strs) { 31 | if (strs == null || strs.length == 0) { 32 | return ""; 33 | } 34 | 35 | Arrays.sort(strs, new MyComparator()); 36 | 37 | StringBuilder sb = new StringBuilder(); 38 | for (int i = 0; i < strs.length; i++) { 39 | sb.append(strs[i]); 40 | } 41 | return sb.toString(); 42 | } 43 | 44 | public static void main(String[] args) { 45 | String[] strs1 = {"jibw", "ji", "jp", "bw", "jibw"}; 46 | System.out.println(lowestString(strs1)); 47 | 48 | String[] strs2 = {"ba", "b"}; 49 | System.out.println(lowestString(strs2)); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_01_DoublePointer/_167_TwoSumII/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._01_DoublePointer._167_TwoSumII; 2 | 3 | /* 4 | * 两数之和Ⅱ- 输入有序数组 5 | * 6 | * 题目描述: 7 | * 给定一个已按照升序排列的有序数组,找到两个数使得它们相加之和等于目标数。 8 | * 函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2,返回的下标不是从 0 开始的。 9 | * 10 | * 思路: 11 | * 1. 使用双指针的方法,left 指向左侧小的数,right 指向右侧大的数; 12 | * 2. 如果两个指针所指向的数的和等于 target,则返回索引; 13 | * 3. 如果两个指针所指向的数的和小于 target,则 left 右移,让和变大即可; 14 | * 4. 如果两个指针所指向的数的和大于 target,则 right 左移,让和变小即可; 15 | * 5. 由于数组中的数最多遍历一次,所以时间复杂度为 O(N),空间复杂度为 O(1)。 16 | */ 17 | public class Solution { 18 | 19 | public static int[] twoSum(int[] numbers, int target) { 20 | if (numbers == null || numbers.length == 0) { 21 | return null; 22 | } 23 | 24 | int left = 0; 25 | int right = numbers.length - 1; 26 | while (left < right) { 27 | int sum = numbers[left] + numbers[right]; 28 | if (sum == target) { 29 | return new int[]{left + 1, right + 1}; 30 | } else if (sum < target) { 31 | left++; 32 | } else { 33 | right--; 34 | } 35 | } 36 | return null; 37 | } 38 | 39 | public static void main(String[] args) { 40 | int[] numbers = {2, 7, 11, 15}; 41 | int target = 9; 42 | int[] res = Solution.twoSum(numbers, target); 43 | for (int i : res) { 44 | System.out.print(i + " "); 45 | } 46 | } 47 | } 48 | 49 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_02_DP/_123_BestTimeToBuyAndSellStockIII/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._02_DP._123_BestTimeToBuyAndSellStockIII; 2 | 3 | /* 4 | * 买股票的最佳时机 Ⅲ 5 | * 6 | * 题目描述: 7 | * 给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。 8 | * 设计一个算法来计算你所能获取的最大利润。 9 | * 你最多可以完成两笔交易。 10 | * 注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。 11 | * 12 | * 示例: 13 | * 输入: [3, 3, 5, 0, 0, 3, 1, 4] 14 | * 输出: 6 15 | * 解释:买入 0 卖出 3,买入 1 卖出 4,得到利润为 6。 16 | * 17 | * 思路: 18 | * 1. 核心思想:买入股票就扣钱,卖出股票就加钱; 19 | * 2. 因此按照流程“买入->卖出->买入->卖出”,最后求得第二次卖出的最大值即可; 20 | */ 21 | public class Solution { 22 | public int maxProfit(int[] nums) { 23 | if (nums == null || nums.length == 0) { 24 | return 0; 25 | } 26 | 27 | int b1 = Integer.MIN_VALUE; 28 | int b2 = Integer.MIN_VALUE; 29 | int s1 = 0; 30 | int s2 = 0; 31 | 32 | for (int i = 0; i < nums.length; i++) { 33 | // 第一次买入的时候,是亏钱的 34 | b1 = Math.max(b1, -nums[i]); 35 | // 第一次卖出的时候,是赚钱的 36 | s1 = Math.max(s1, b1 + nums[i]); 37 | // 第二次买入的时候,是在第一次 s1 的基础上减钱的 38 | b2 = Math.max(b2, s1 - nums[i]); 39 | // 第二次卖出的时候,是赚钱的 40 | s2 = Math.max(s2, b2 + nums[i]); 41 | } 42 | return s2; 43 | } 44 | 45 | public static void main(String[] args) { 46 | Solution solution = new Solution(); 47 | int[] nums = {3, 3, 5, 0, 0, 3, 1, 4}; 48 | 49 | System.out.println(solution.maxProfit(nums)); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/SwordToOfferSolution/_08_NextNodeInBinaryTrees/Solution.java: -------------------------------------------------------------------------------- 1 | package SwordToOfferSolution._08_NextNodeInBinaryTrees; 2 | 3 | /* 4 | * 二叉树的下一个结点 5 | * 6 | * 题目描述: 7 | * 给定一棵二叉树和其中的一个结点,如何找出中序遍历顺序的下一个结点? 8 | * 树中的结点除了有两个分别指向左右子结点的指针以外,还有一个指向父结点的指针。 9 | * 或者说,如何在二叉树中找到一个节点的后继节点? 10 | * 后继节点指的是在中序遍历中,当前节点的下一个节点叫做当前节点的后继节点。 11 | * 12 | * 思路: 13 | * 1. 如果当前节点有右子树,则当前节点的后继就是其右子树中最左侧的节点; 14 | * 2. 如果当前节点没有右子树,可利用父指针,朝上继续找,直至找到某个节点是其父节点的左孩子,则当前节点的后继就是那个父节点; 15 | */ 16 | public class Solution { 17 | static class TreeNode { 18 | int val; 19 | TreeNode left; 20 | TreeNode right; 21 | TreeNode parent; 22 | 23 | TreeNode(int val) { 24 | this.val = val; 25 | } 26 | } 27 | 28 | private static TreeNode getNextNode(TreeNode node) { 29 | if (node == null) { 30 | return null; 31 | } 32 | 33 | // 如果当前节点有右孩子,则在右孩子中的最左侧的节点就是当前节点的后继 34 | if (node.right != null) { 35 | node = node.right; 36 | while (node.left != null) { 37 | node = node.left; 38 | } 39 | return node; 40 | // 如果某个节点没有右子树 41 | } else { 42 | TreeNode parent = node.parent; 43 | // 当前节点等于它父亲节点的左孩子的时候,就停止 while 44 | // 如果当前节点的父节点是 null,则说明当前节点没有后继 45 | while (parent != null && parent.left != node) { 46 | node = parent; 47 | parent = node.parent; 48 | } 49 | return parent; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_02_String/_205_IsomorphicStrings/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._02_String._205_IsomorphicStrings; 2 | 3 | /* 4 | * 同构字符串 5 | * 6 | * 题目描述: 7 | * 给定两个字符串 s 和 t,判断它们是否是同构的。 8 | * 如果 s 中的字符可以被替换得到 t ,那么这两个字符串是同构的。 9 | * 所有出现的字符都必须用另一个字符替换,同时保留字符的顺序。 10 | * 两个字符不能映射到同一个字符上,但字符可以映射自己本身。 11 | * 12 | * 思路: 13 | * 1. 记录当前字符上一次出现的位置,如果两个字符串中的字符上次出现的位置一样,那么就属于同构的。 14 | */ 15 | public class Solution { 16 | public boolean isIsomorphic(String s, String t) { 17 | if (s == null || t == null) { 18 | return false; 19 | } 20 | if (s.length() != t.length()) { 21 | return false; 22 | } 23 | 24 | int[] nums1 = new int[128]; 25 | int[] nums2 = new int[128]; 26 | for (int i = 0; i < s.length(); i++) { 27 | char ss = s.charAt(i); 28 | char tt = t.charAt(i); 29 | // 如果两个字符上次出现的位置不一样,则直接返回 false 30 | if (nums1[ss] != nums2[tt]) { 31 | return false; 32 | } 33 | 34 | nums1[ss] = i + 1; 35 | nums2[tt] = i + 1; 36 | } 37 | return true; 38 | } 39 | 40 | public static void main(String[] args) { 41 | Solution solution = new Solution(); 42 | String s1 = "foo"; 43 | String t1 = "bar"; 44 | String s2 = "paper"; 45 | String t2 = "title"; 46 | 47 | System.out.println(solution.isIsomorphic(s1, t1)); 48 | System.out.println(solution.isIsomorphic(s2, t2)); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_03_Tree/_572_SubtreeOfAnotherTree/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._03_Tree._572_SubtreeOfAnotherTree; 2 | 3 | /* 4 | * 另一个树的子树 5 | * 6 | * 题目描述: 7 | * 给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和节点值的子树。 8 | * s 的一个子树包括 s 的一个节点和这个节点的所有子孙。 9 | * s 也可以看做它自身的一棵子树。 10 | * 需要格外注意:该题与“树的子结构”的判断条件有些不同。 11 | * 12 | * 思路: 13 | * 1. 建议先做 LeetCode.100 题,判断两棵树是否相等; 14 | * 2. 判断一棵树是否是另外一棵树的子树,需要注意是“或”的关系: 15 | * 2.1) 当前两棵树相等; 16 | * 2.2) 或者 t 是 s 的左子树; 17 | * 2.3) 或者 t 是 s 的右子树。 18 | */ 19 | public class Solution { 20 | class TreeNode { 21 | int val; 22 | TreeNode left; 23 | TreeNode right; 24 | 25 | TreeNode(int val) { 26 | this.val = val; 27 | } 28 | } 29 | 30 | public boolean isSubtree(TreeNode s, TreeNode t) { 31 | // 如果 s 原本就不是一棵树,则 t 就不需要进行比较了 32 | if (s == null) { 33 | return false; 34 | } 35 | 36 | // 这里是“或”的关系 37 | // 注意这里是用 t 的根节点继续与 s 的左右孩子继续比较 38 | return isSubtree(s.left, t) || isSubtree(s.right, t) || isSameTree(s, t); 39 | } 40 | 41 | // 判断是否是相同的树 42 | private boolean isSameTree(TreeNode s, TreeNode t) { 43 | if (s == null && t == null) { 44 | return true; 45 | } 46 | if (s == null || t == null) { 47 | return false; 48 | } 49 | if (s.val != t.val) { 50 | return false; 51 | } 52 | 53 | return isSubtree(s.left, t.left) && isSubtree(s.right, t.right); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/SwordToOfferSolution/_10_02_ClimbingStairs/Solution.java: -------------------------------------------------------------------------------- 1 | package SwordToOfferSolution._10_02_ClimbingStairs; 2 | 3 | /* 4 | * 爬楼梯 5 | * 6 | * 题目描述: 7 | * 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。 8 | * 你有多少种不同的方法可以爬到楼顶呢? 9 | * 10 | * 思路: 11 | * 与斐波那契数列问题相似,只不过初始值不同。 12 | */ 13 | public class Solution { 14 | // 递归 15 | public int climbStairs1(int n) { 16 | if (n < 2) { 17 | return 1; 18 | } 19 | return climbStairs1(n - 1) + climbStairs1(n - 2); 20 | } 21 | 22 | // DP 数组 23 | public int climbStairs2(int n) { 24 | if (n < 2) { 25 | return 1; 26 | } 27 | 28 | int[] dp = new int[n + 1]; 29 | dp[0] = 1; 30 | dp[1] = 1; 31 | for (int i = 2; i <= n; i++) { 32 | dp[i] = dp[i - 1] + dp[i - 2]; 33 | } 34 | return dp[n]; 35 | } 36 | 37 | // DP 38 | public int climbStairs3(int n) { 39 | if (n < 2) { 40 | return 1; 41 | } 42 | 43 | int res = 0; 44 | int prepre = 1; 45 | int pre = 1; 46 | for (int i = 2; i <= n; i++) { 47 | res = prepre + pre; 48 | prepre = pre; 49 | pre = res; 50 | } 51 | return res; 52 | } 53 | 54 | public static void main(String[] args) { 55 | Solution solution = new Solution(); 56 | System.out.println(solution.climbStairs1(7)); 57 | System.out.println(solution.climbStairs2(7)); 58 | System.out.println(solution.climbStairs3(7)); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_01_ArrayAndMatrix/_1046_LastStoneWeight/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._01_ArrayAndMatrix._1046_LastStoneWeight; 2 | 3 | import java.util.PriorityQueue; 4 | 5 | /* 6 | * 最后一块石头的重量 7 | * 8 | * 题目描述: 9 | * 有一堆石头,每块石头的重量都是正整数。 10 | * 每一回合,从中选出两块 最重的 石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下: 11 | * 如果 x == y,那么两块石头都会被完全粉碎; 12 | * 如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。 13 | * 最后,最多只会剩下一块石头。 14 | * 注意:返回此石头的重量。 15 | * 如果没有石头剩下,就返回 0。 16 | * 17 | * 思路: 18 | * 1. 由于需要动态地维护数组中元素的大小关系,因此可以使用堆; 19 | * 2. 为什么使用堆呢? 20 | * 3. 因为每次都需要从数组中选择两个最大的元素,然后进行相减; 21 | * 4. 那么“从数组中选择两个最大的元素”的这个动作就可以使用大根堆来完成。 22 | */ 23 | public class Solution { 24 | public int lastStoneWeight(int[] stones) { 25 | if (stones == null || stones.length == 0) { 26 | return 0; 27 | } 28 | 29 | // 定义大根堆 30 | PriorityQueue maxHeap = new PriorityQueue<>(stones.length, ((o1, o2) -> (o2 - o1))); 31 | for (int stone : stones) { 32 | maxHeap.offer(stone); 33 | } 34 | 35 | while (maxHeap.size() >= 2) { 36 | Integer max1 = maxHeap.poll(); 37 | Integer max2 = maxHeap.poll(); 38 | maxHeap.offer(max1 - max2); 39 | } 40 | return maxHeap.poll(); 41 | } 42 | 43 | public static void main(String[] args) { 44 | Solution solution = new Solution(); 45 | int[] stones = {2, 7, 4, 1, 8, 1}; 46 | 47 | System.out.println(solution.lastStoneWeight(stones)); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_01_ArrayAndMatrix/_80_RemoveDuplicatesFromSortedArrayII/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._01_ArrayAndMatrix._80_RemoveDuplicatesFromSortedArrayII; 2 | 3 | /* 4 | * 删除有序数组中的重复项 II 5 | * 6 | * 题目描述: 7 | * 给你一个有序数组 nums,请你原地删除重复出现的元素,使得出现次数超过两次的元素只出现两次,返回删除后数组的新长度。 8 | * 不要使用额外的数组空间,你必须在原地修改输入数组,并在使用 O(1) 额外空间的条件下完成。 9 | * 10 | * 思路: 11 | * 1. 该题可以抽象为「保留 K 位相同的元素」; 12 | * 2. 对于前 K 个相同的数字,可以直接保留,而对于后面任意的数字,只能在"与前面写入的位置之前的第 K 个元素进行比较时,其值不同"的情况下才能保留。 13 | */ 14 | public class Solution { 15 | public int removeDuplicates(int[] nums) { 16 | if (nums == null || nums.length == 0) { 17 | return 0; 18 | } 19 | 20 | return process(nums, 2); 21 | } 22 | 23 | private int process(int[] nums, int k) { 24 | // idx 代表当前需要被替换的位置 25 | int idx = 0; 26 | for (int num : nums) { 27 | // 若 nums[idx - k] 等于 num,则说明已经出现了 K+1 个相同的元素 28 | // 此时 idx 不变,而是继续往后遍历 num 29 | // 此刻的 idx 指向的就是待"删除"的元素 30 | if (idx < k || nums[idx - k] != num) { 31 | nums[idx++] = num; 32 | } 33 | } 34 | 35 | return idx; 36 | } 37 | 38 | public static void main(String[] args) { 39 | Solution solution = new Solution(); 40 | int[] nums1 = {1, 1, 1, 2, 2, 3}; 41 | int[] nums2 = {0, 0, 1, 1, 1, 1, 2, 3, 3}; 42 | 43 | System.out.println(solution.removeDuplicates(nums1)); // 5 44 | System.out.println(solution.removeDuplicates(nums2)); // 7 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/SwordToOfferSolution/_68_01_CommonParentInTree/Solution.java: -------------------------------------------------------------------------------- 1 | package SwordToOfferSolution._68_01_CommonParentInTree; 2 | 3 | /* 4 | * 二叉搜索树的最近公共祖先 5 | * 6 | * 题目描述: 7 | * 在二叉搜索树中(左边小于根,右边大于根),输入两个结点,求它们的最低公共祖先。 8 | * 9 | * 思路: 10 | * 1. 对于二叉搜索树,如果当前节点的值比两个输入的节点大,则继续在当前节点的左侧寻找最低公共祖先; 11 | * 2. 如果当前节点的值比两个输入的节点小,则在当前节点的右侧寻找最低公共祖先。 12 | */ 13 | public class Solution { 14 | static class TreeNode { 15 | private int val; 16 | private TreeNode left; 17 | private TreeNode right; 18 | 19 | TreeNode(int val) { 20 | this.val = val; 21 | } 22 | } 23 | 24 | // 递归 25 | public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { 26 | if (root == null) { 27 | return null; 28 | } 29 | 30 | if (root.val > p.val && root.val > q.val) { 31 | return lowestCommonAncestor(root.left, p, q); 32 | } 33 | if (root.val < p.val && root.val < q.val) { 34 | return lowestCommonAncestor(root.right, p, q); 35 | } 36 | return root; 37 | } 38 | 39 | // 迭代 40 | public TreeNode lowestCommonAncestor2(TreeNode root, TreeNode p, TreeNode q) { 41 | while (root != null) { 42 | if (root.val > p.val && root.val > q.val) { 43 | root = root.left; 44 | } else if (root.val < p.val && root.val < q.val) { 45 | root = root.right; 46 | } else { 47 | return root; 48 | } 49 | } 50 | 51 | return null; 52 | } 53 | } -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_03_Tree/_337_HouseRobberIII/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._03_Tree._337_HouseRobberIII; 2 | 3 | /* 4 | * 打家劫舍 Ⅲ 5 | * 6 | * 题目描述: 7 | * 在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。 8 | * 这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。 9 | * 一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 10 | * 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。 11 | * 计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。 12 | * 13 | * 思路: 14 | * 1. 其实就是二叉树的间隔遍历; 15 | * 2. 但需要注意的是,如果选择偷了某个节点后,那么它的父节点以及它的孩子是不能再偷的; 16 | * 3. 如果选择偷根节点,那么根节点的两个孩子不能再偷; 17 | * 4. 如果选择不偷根节点,那么就可以偷两个孩子节点,然后再选择较大的那个即可,当然可以不偷两个孩子节点; 18 | * 5. 当然也可以使用递归(后序遍历)的方式,让子节点将信息返回给父节点。 19 | */ 20 | public class Solution { 21 | class TreeNode { 22 | int val; 23 | TreeNode left; 24 | TreeNode right; 25 | 26 | TreeNode(int val) { 27 | this.val = val; 28 | } 29 | } 30 | 31 | public int rob(TreeNode root) { 32 | if (root == null) { 33 | return 0; 34 | } 35 | 36 | // 选择偷根节点 37 | int v1 = root.val; 38 | // 左孩子不为空,我先不偷左孩子,而是选择偷左孩子的左右孩子 39 | if (root.left != null) { 40 | v1 += rob(root.left.left) + rob(root.left.right); 41 | } 42 | // 右孩子不为空,我先不偷右孩子,而是选择偷右孩子的左右孩子 43 | if (root.right != null) { 44 | v1 += rob(root.right.left) + rob(root.right.right); 45 | } 46 | 47 | // 选择不偷根节点, 48 | // 那么我即偷左孩子,又偷右孩子 49 | int v2 = rob(root.left) + rob(root.right); 50 | 51 | // 最后取两者最大 52 | return Math.max(v1, v2); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_03_Tree/_236_LowestCommonAncestorOfABinaryTree/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._03_Tree._236_LowestCommonAncestorOfABinaryTree; 2 | 3 | /* 4 | * 二叉树的最近公共祖先 5 | * 6 | * 题目描述: 7 | * 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 8 | * 9 | * 思路: 10 | * 1. https://github.com/dyfloveslife/LeetCodeAndSwordToOffer/blob/master/src/SwordToOfferSolution/_68_02_CommonParentInTree/Solution.java 11 | * 2. 剑指 offer 做过这道题,现在重新思考一下解题方法; 12 | * 3. 由于不是二叉搜索树,因此不能通过比较节点值来确定最近公共祖先; 13 | * 4. 先从根节点开始判断,如果根节点就是 p 节点或者就是 q 节点,则说明 p 和 q 的最近公共祖先就是根节点; 14 | * 5. 否则递归找到左右节点,如果左节点为空,则返回右节点即可,如果右节点为空,则返回左节点即可; 15 | * 6. 其实就是后序遍历的思想,每当遍历完左右孩子的时候,就把信息返回给父节点; 16 | * 7. 找到了 p 或 q 节点,就向上返回,如果没有找到 p 或 q 节点,则向上返回 null。 17 | */ 18 | public class Solution { 19 | class TreeNode { 20 | int val; 21 | TreeNode left; 22 | TreeNode right; 23 | 24 | TreeNode(int val) { 25 | this.val = val; 26 | } 27 | } 28 | 29 | public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { 30 | if (root == null) { 31 | return null; 32 | } 33 | if (root == p || root == q) { 34 | return root; 35 | } 36 | 37 | TreeNode leftNode = lowestCommonAncestor(root.left, p, q); 38 | TreeNode rightNode = lowestCommonAncestor(root.right, p, q); 39 | if (leftNode == null) { 40 | return rightNode; 41 | } 42 | if (rightNode == null) { 43 | return leftNode; 44 | } 45 | return root; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_01_ArrayAndMatrix/_154_FindMinimumInRotatedSortedArrayII/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._01_ArrayAndMatrix._154_FindMinimumInRotatedSortedArrayII; 2 | 3 | /* 4 | * 寻找旋转排序数组中的最小值 II 5 | * 6 | * 题目描述: 7 | * 已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,4,4,5,6,7] 在变化后可能得到: 8 | * 若旋转 4 次,则可以得到 [4,5,6,7,0,1,4] 9 | * 若旋转 7 次,则可以得到 [0,1,4,4,5,6,7] 10 | * 注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]] 。 11 | * 给你一个可能存在重复元素值的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的最小元素 。 12 | * 你必须尽可能减少整个过程的操作步骤。 13 | * 14 | * 思路: 15 | * 1. 使用二分即可,使用 nums[mid] 和 nums[right] 元素进行比较; 16 | * 2. 由于存在重复元素并且需要找到最小值,所以需要 right 左移。 17 | */ 18 | public class Solution { 19 | public int findMin(int[] nums) { 20 | int left = 0, right = nums.length - 1; 21 | while (left < right) { 22 | int mid = left + ((right - left) >> 1); 23 | if (nums[mid] == nums[right]) { 24 | right--; 25 | } else if (nums[mid] < nums[right]) { 26 | right = mid; 27 | } else { 28 | left = mid + 1; 29 | } 30 | } 31 | 32 | return nums[left]; 33 | } 34 | 35 | public static void main(String[] args) { 36 | Solution solution = new Solution(); 37 | int[] nums1 = {1, 3, 5}; 38 | int[] nums2 = {2, 2, 2, 0, 1}; 39 | 40 | System.out.println(solution.findMin(nums1)); // 1 41 | System.out.println(solution.findMin(nums2)); // 0 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_01_ArrayAndMatrix/_566_ReshapeTheMatrix/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._01_ArrayAndMatrix._566_ReshapeTheMatrix; 2 | 3 | import java.util.Arrays; 4 | 5 | /* 6 | * 重塑矩阵 7 | * 8 | * 题目描述: 9 | * 在 MATLAB 中,有一个非常有用的函数 reshape,它可以将一个矩阵重塑为另一个大小不同的新矩阵,但保留其原始数据。 10 | * 给出一个由二维数组表示的矩阵,以及两个正整数 r 和 c,分别表示想要的重构的矩阵的行数和列数。 11 | * 重构后的矩阵需要将原始矩阵的所有元素以相同的行遍历顺序填充。 12 | * 如果具有给定参数的 reshape 操作是可行且合理的,则输出新的重塑矩阵;否则,输出原始矩阵。 13 | * 14 | * 思路: 15 | * 1. 只需要注意从二维到一维的转换关系即可。 16 | */ 17 | public class Solution { 18 | 19 | public int[][] matrixReshape(int[][] nums, int r, int c) { 20 | if (nums == null || nums.length == 0) { 21 | return new int[0][]; 22 | } 23 | 24 | int rows = nums.length; 25 | int cols = nums[0].length; 26 | if (rows * cols < r * c) { 27 | return nums; 28 | } 29 | 30 | int index = 0; 31 | int[][] res = new int[r][c]; 32 | for (int i = 0; i < r; i++) { 33 | for (int j = 0; j < c; j++) { 34 | // 核心 35 | res[i][j] = nums[index / cols][index % cols]; 36 | index++; 37 | } 38 | } 39 | return res; 40 | } 41 | 42 | public static void main(String[] args) { 43 | Solution solution = new Solution(); 44 | int[][] nums = {{1, 2}, {3, 4}}; 45 | 46 | System.out.println(Arrays.deepToString(solution.matrixReshape(nums, 1, 4))); 47 | System.out.println(Arrays.deepToString(solution.matrixReshape(nums, 2, 4))); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/SwordToOfferSolution/_23_EntryNodeInListLoop/Solution2.java: -------------------------------------------------------------------------------- 1 | package SwordToOfferSolution._23_EntryNodeInListLoop; 2 | 3 | /* 4 | * 循环链表中的入口节点 5 | * 6 | * 题目描述: 7 | * 一个链表中包含环,如何找出环的入口结点? 8 | * 9 | * 思路: 10 | * 1. 两个指针一个 fast、一个 slow 同时从一个链表的头部出发; 11 | * 2. fast 一次走 2 步,slow 一次走 1 步,如果该链表有环,两个指针必然在环内相遇; 12 | * 3. 此时只需要把其中的一个指针重新指向链表头部,另一个不变(还在环内); 13 | * 4. 这次两个指针一次走一步,相遇的地方就是入口节点。 14 | */ 15 | public class Solution2 { 16 | class ListNode { 17 | int val; 18 | ListNode next = null; 19 | 20 | ListNode(int val) { 21 | this.val = val; 22 | } 23 | } 24 | 25 | public ListNode entryNodeInListLoop(ListNode pHead) { 26 | if (pHead == null || pHead.next == null) { 27 | return null; 28 | } 29 | 30 | ListNode slowNode = pHead; 31 | ListNode fastNode = pHead; 32 | while (fastNode != null && fastNode.next != null) { 33 | // 慢指针走一步,快指针走两步 34 | slowNode = slowNode.next; 35 | fastNode = fastNode.next.next; 36 | // 如果两个指针相遇了,就让快指针重新指向链表头,然后和慢指针一起同速走 37 | if (slowNode == fastNode) { 38 | fastNode = pHead; 39 | while (slowNode != fastNode) { 40 | slowNode = slowNode.next; 41 | fastNode = fastNode.next; 42 | } 43 | } 44 | // 再次相遇的时候就是环入口 45 | if (slowNode == fastNode) { 46 | return fastNode; 47 | } 48 | } 49 | // 要是没有相遇,此链表没有环返回空 50 | return null; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_05_StackAndQueue/_503_NextGreaterElementII/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._05_StackAndQueue._503_NextGreaterElementII; 2 | 3 | import java.util.Arrays; 4 | import java.util.Stack; 5 | 6 | /* 7 | * 下一个更大元素 Ⅱ 8 | * 9 | * 题目描述: 10 | * 给定一个循环数组(最后一个元素的下一个元素是数组的第一个元素),输出每个元素的下一个更大元素。 11 | * 数字 x 的下一个更大的元素是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。 12 | * 如果不存在,则输出 -1。 13 | * 14 | * 示例: 15 | * 输入: [1, 2, 1] 16 | * 输出: [2, -1, 2] 17 | * 18 | * 思路: 19 | * 0. 还是单调栈的应用; 20 | * 1. 由于是循环数组,因此对于最后一个元素来说,需要找到它的下一个元素,可以通过取余的方式进行; 21 | * 2. 将结果数组都初始化为 -1,那么对于停留在栈中的元素,说明它是最大元素,没有下一个比它还大的元素; 22 | * 3. 因此就默认返回的是 -1。 23 | */ 24 | public class Solution { 25 | public int[] nextGreaterElements(int[] nums) { 26 | if (nums == null || nums.length == 0) { 27 | return new int[0]; 28 | } 29 | 30 | int len = nums.length; 31 | int[] res = new int[len]; 32 | Arrays.fill(res, -1); 33 | Stack stack = new Stack<>(); 34 | 35 | for (int i = 0; i < len * 2; i++) { 36 | int num = nums[i % len]; 37 | while (!stack.isEmpty() && num > nums[stack.peek()]) { 38 | res[stack.pop()] = num; 39 | } 40 | stack.push(i % len); 41 | } 42 | 43 | return res; 44 | } 45 | 46 | public static void main(String[] args) { 47 | Solution solution = new Solution(); 48 | int[] nums = {1, 2, 1}; 49 | 50 | System.out.println(Arrays.toString(solution.nextGreaterElements(nums))); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_01_DoublePointer/_16_3SumClosest/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._01_DoublePointer._16_3SumClosest; 2 | 3 | import java.util.Arrays; 4 | 5 | /* 6 | * 最接近的三数之和 7 | * 8 | * 题目描述: 9 | * 给定一个包括 n 个整数的数组 nums 和 一个目标值 target。 10 | * 找出 nums 中的三个整数,使得它们的和与 target 最接近。 11 | * 返回这三个数的和。 12 | * 假定每组输入只存在唯一答案。 13 | * 14 | * 思路: 15 | * 1. 与 15 题类似,只不过这道题需要找到最接近 target 的和; 16 | * 2. 还是先排序,然后使用双指针的方式不停地找到最接近 target 的三个数的和。 17 | */ 18 | public class Solution { 19 | public int threeSumClosest(int[] nums, int target) { 20 | if (nums == null) { 21 | return 0; 22 | } 23 | Arrays.sort(nums); 24 | int res = nums[0] + nums[1] + nums[2]; 25 | for (int i = 0; i < nums.length; i++) { 26 | int left = i + 1; 27 | int right = nums.length - 1; 28 | while (left < right) { 29 | int sum = nums[i] + nums[left] + nums[right]; 30 | // 核心 31 | if (Math.abs(target - sum) < Math.abs(target - res)) { 32 | res = sum; 33 | } else if (sum > target) { 34 | right--; 35 | } else { 36 | left++; 37 | } 38 | } 39 | } 40 | return res; 41 | } 42 | 43 | public static void main(String[] args) { 44 | Solution solution = new Solution(); 45 | int[] nums = {-1, 2, 1, -4}; 46 | int target = 1; 47 | 48 | System.out.println(solution.threeSumClosest(nums, target)); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_03_Tree/_653_TwoSumIVInputIsABST/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._03_Tree._653_TwoSumIVInputIsABST; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /* 7 | * 两数之和 Ⅳ - 输入 BST 8 | * 9 | * 题目描述: 10 | * 给定一个二叉搜索树和一个目标结果,如果 BST 中存在两个元素且它们的和等于给定的目标结果,则返回 true。 11 | * 12 | * 思路: 13 | * 1. 最初的思路是采用递归的方式,分别找到两个元素值等于 target; 14 | * 2. 但发现这两个元素有可能分布在左右子树两侧; 15 | * 3. 因此,先中序遍历二叉搜索树,然后通过双指针的方式来查找目标值。 16 | */ 17 | public class Solution { 18 | class TreeNode { 19 | int val; 20 | TreeNode left; 21 | TreeNode right; 22 | 23 | TreeNode(int val) { 24 | this.val = val; 25 | } 26 | } 27 | 28 | public boolean findTarget(TreeNode root, int k) { 29 | if (root == null) { 30 | return false; 31 | } 32 | 33 | List list = new ArrayList<>(); 34 | inorder(root, list); 35 | int i = 0; 36 | int j = list.size() - 1; 37 | while (i < j) { 38 | int sum = list.get(i) + list.get(j); 39 | if (sum == k) { 40 | return true; 41 | } else if (sum > k) { 42 | j--; 43 | } else { 44 | i++; 45 | } 46 | } 47 | return false; 48 | } 49 | 50 | private void inorder(TreeNode root, List list) { 51 | if (root == null) { 52 | return; 53 | } 54 | inorder(root.left, list); 55 | list.add(root.val); 56 | inorder(root.right, list); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_03_Greedy/_452_MinimumNumberOfArrowsToBurstBalloons/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._03_Greedy._452_MinimumNumberOfArrowsToBurstBalloons; 2 | 3 | import java.util.Arrays; 4 | import java.util.Comparator; 5 | 6 | /* 7 | * 用最少数量的箭引爆气球 8 | * 9 | * 题目描述: 10 | * 在二维空间中有许多球形的气球。对于每个气球,提供的输入是水平方向上,气球直径的开始和结束坐标。 11 | * 由于它是水平的,所以 y 坐标并不重要,因此只要知道开始和结束的x坐标就足够了。开始坐标总是小于结束坐标。 12 | * 一支弓箭可以沿着 x 轴从不同点完全垂直地射出。 13 | * 在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 x_start 和 x_end,且满足 x_start ≤ x ≤ x_end,则该气球会被引爆。 14 | * 可以射出的弓箭的数量没有限制。 弓箭一旦被射出之后,可以无限地前进。 15 | * 我们想找到使得所有气球全部被引爆,所需的弓箭的最小数量。 16 | * 17 | * 思路: 18 | * 该题的重点在于,边界接触是算重叠的,所以不更新 x。 19 | */ 20 | public class Solution { 21 | 22 | public static int findMinArrowShots(int[][] points) { 23 | if (points == null || points.length == 0) { 24 | return 0; 25 | } 26 | 27 | Arrays.sort(points, new Comparator() { 28 | @Override 29 | public int compare(int[] o1, int[] o2) { 30 | return o1[1] - o2[1]; 31 | } 32 | }); 33 | 34 | int count = 1; 35 | int x_end = points[0][1]; 36 | for (int[] point : points) { 37 | int start = point[0]; 38 | if (x_end < start) { 39 | count++; 40 | x_end = point[1]; 41 | } 42 | } 43 | return count; 44 | } 45 | 46 | public static void main(String[] args) { 47 | int[][] arr = {{1, 2}, {3, 4}, {5, 6}, {7, 8}}; 48 | System.out.println(findMinArrowShots(arr)); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_03_Greedy/_665_NonDecreasingArray/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._03_Greedy._665_NonDecreasingArray; 2 | 3 | /* 4 | * 非递减数组 5 | * 6 | * 题目描述: 7 | * 给定一个长度为 n 的整数数组,你的任务是判断在最多改变 1 个元素的情况下,该数组能否变成一个非递减数列。 8 | * 我们是这样定义一个非递减数列的:对于数组中所有的 i (1 <= i < n),满足 array[i] <= array[i + 1]。 9 | * 10 | * 思路: 11 | * 1. 假设 nums[i-1] > nums[i],例如 [2,3,7, 6 ,9,12],需要考虑修改哪个数才能保证不影响后序的顺序; 12 | * 此时,应修改 nums[i-1] = nums[i];如果修改成 nums[i] = nums[i-1] 的话,nums[i-1]有可能比 nums[i+1]要大; 13 | * 2. 假设 nums[i-2] > nums[i],例如 [8,9,10, 6 ,12],则只能修改成 nums[i] = nums[i-1]。 14 | */ 15 | public class Solution { 16 | public static boolean checkPossibility(int[] nums) { 17 | if (nums == null || nums.length == 0) { 18 | return false; 19 | } 20 | 21 | int count = 0; 22 | // 因为这里只看 i 前面的元素情况,不看后面的元素,所以索引从 1 开始 23 | for (int i = 1; i < nums.length && count < 2; i++) { 24 | // 已经是升序了,则直接判断下一位 25 | if (nums[i] >= nums[i - 1]) { 26 | continue; 27 | } 28 | 29 | // 程序能够执行到这里,说明需要改变元素 30 | count++; 31 | // i - 2 >= 0 保证不越界,此时 i 最小等于 2,说明 i 前面还有两个元素 32 | if (i - 2 >= 0 && nums[i - 2] > nums[i]) { 33 | nums[i] = nums[i - 1]; 34 | } else { 35 | nums[i - 1] = nums[i]; 36 | } 37 | } 38 | return count < 2; 39 | } 40 | 41 | public static void main(String[] args) { 42 | int[] arr = {4, 2, 3}; 43 | System.out.println(checkPossibility(arr)); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_08_LinkedList/_82_RemoveDuplicatesFromSortedListII/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._08_LinkedList._82_RemoveDuplicatesFromSortedListII; 2 | 3 | /* 4 | * 删除排序链表中的重复元素 Ⅱ 5 | * 6 | * 题目描述: 7 | * 给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字。 8 | * 9 | * 思路: 10 | * 1. 这里在改变指针的时候比较绕; 11 | * 2. 首先创建一个 dummy,以便于最后返回头节点,即 dummy.next; 12 | * 3. 通过 temp 来不断的划过相同的节点,然后 temp 停在相同节点的最后一个节点; 13 | * 4. 最后改变 cur 的 next 域,让它指向 temp 的下一个节点。 14 | */ 15 | public class Solution { 16 | class ListNode { 17 | int val; 18 | ListNode next; 19 | 20 | ListNode(int val) { 21 | this.val = val; 22 | } 23 | } 24 | 25 | public ListNode deleteDuplicates(ListNode head) { 26 | if (head == null) { 27 | return null; 28 | } 29 | 30 | ListNode dummy = new ListNode(-1); 31 | dummy.next = head; 32 | ListNode cur = dummy; 33 | 34 | while (cur.next != null && cur.next.next != null) { 35 | if (cur.next.val == cur.next.next.val) { 36 | ListNode temp = cur.next; 37 | // 这里让 temp 一直来到相同节点的最后一个节点, 38 | // temp 来到最后一个节点后,temp 的下一个节点就是一个不同的节点, 39 | // 然后让 cur 的 next 指向 temp 的下一个节点 40 | while (temp != null && temp.next != null && temp.val == temp.next.val) { 41 | temp = temp.next; 42 | } 43 | cur.next = temp.next; 44 | } else { 45 | cur = cur.next; 46 | } 47 | } 48 | return dummy.next; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_04_BinarySearch/_540_SingleElementInASortedArray/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._04_BinarySearch._540_SingleElementInASortedArray; 2 | 3 | /* 4 | * 有序数组中的单一元素 5 | * 6 | * 题目描述: 7 | * 给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。 8 | * 9 | * 思路: 10 | * 1. 根据题意可知,数组元素的个数为奇数个,并且可以知道,只出现一次的元素一定位于索引值为偶数的位置上; 11 | * 2. 可以对所有的偶数索引进行搜索,直到遇到第一个其后元素不相同的索引; 12 | * 3. 首先确保 middle 是偶数,如果不是偶数,则将其减 1; 13 | * 4. 然后检查 middle 上的元素是否与其后面的元素相同; 14 | * 4.1) 如果相同,则说明 middle 上的元素不是单个元素,则单个元素在 middle 之后,此时修改 left 为 middle + 2; 15 | * 4.2) 如果不相同,则说明单个元素位于 middle 或位于 middle 之前,此时修改 right 为 middle; 16 | * 5. 只要 left == right,则当前搜索的空间为 1 个元素,则返回该元素; 17 | * 6. 由于使用了表达式 right = middle,所以应使用 left < right 的形式,否则会出现死循环。 18 | */ 19 | public class Solution { 20 | public static int singleNonDuplicate(int[] nums) { 21 | if (nums == null || nums.length == 0) { 22 | return 0; 23 | } 24 | 25 | int left = 0; 26 | int right = nums.length - 1; 27 | while (left < right) { 28 | int middle = left + ((right - left) >> 1); 29 | if (middle % 2 == 1) { 30 | middle--; 31 | } 32 | 33 | if (nums[middle] == nums[middle + 1]) { 34 | left = middle + 2; 35 | } else { 36 | right = middle; 37 | } 38 | } 39 | return nums[left]; 40 | } 41 | 42 | public static void main(String[] args) { 43 | int[] arr = {3, 3, 7, 7, 10, 11, 11}; 44 | System.out.println(singleNonDuplicate(arr)); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_01_ArrayAndMatrix/_240_SearchA2DMatrixII/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._01_ArrayAndMatrix._240_SearchA2DMatrixII; 2 | 3 | /* 4 | * 搜索二维矩阵 Ⅱ 5 | * 6 | * 题目描述: 7 | * 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target。 8 | * 该矩阵具有以下特性: 9 | * 每行的元素从左到右升序排列。 10 | * 每列的元素从上到下升序排列。 11 | * 12 | * 思路: 13 | * 1. 剑指 Offer 的原题,注意观察矩阵元素的特点; 14 | * 2. 可以从左下角或者右上角的元素开始进行判断,每次如果不符合条件,则去掉相应的行或列。 15 | */ 16 | public class Solution { 17 | public boolean searchMatrix(int[][] matrix, int target) { 18 | if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { 19 | return false; 20 | } 21 | 22 | int rows = 0; 23 | int cols = matrix[0].length - 1; 24 | 25 | while (rows < matrix.length && cols > -1) { 26 | int cur = matrix[rows][cols]; 27 | if (cur == target) { 28 | return true; 29 | } else if (cur > target) { 30 | cols--; 31 | } else { 32 | rows++; 33 | } 34 | } 35 | return false; 36 | } 37 | 38 | public static void main(String[] args) { 39 | Solution solution = new Solution(); 40 | int[][] matrix = { 41 | {1, 4, 7, 11, 15}, 42 | {2, 5, 8, 12, 19}, 43 | {3, 6, 9, 16, 22}, 44 | {10, 13, 14, 17, 24}, 45 | {18, 21, 23, 26, 30} 46 | }; 47 | 48 | System.out.println(solution.searchMatrix(matrix, 5)); 49 | System.out.println(solution.searchMatrix(matrix, 20)); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_01_ArrayAndMatrix/_1049_LastStoneWeightII/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._01_ArrayAndMatrix._1049_LastStoneWeightII; 2 | 3 | /* 4 | * 最后一块石头的重量 Ⅱ 5 | * 6 | * 题目描述: 7 | * 有一堆石头,每块石头的重量都是正整数。 8 | * 每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下: 9 | * 如果 x == y,那么两块石头都会被完全粉碎; 10 | * 如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。 11 | * 最后,最多只会剩下一块石头。 12 | * 注意:返回此石头最小的可能重量。 13 | * 如果没有石头剩下,就返回 0。 14 | * 15 | * 思路: 16 | * 1. 0-1 背包问题; 17 | * 2. 可以将石头分成两个堆,由于拿走两个石头以后,还有机会将石头放回去,因此可以看成两个堆; 18 | * 3. 总重量为 sum,则需要计算的就是如果使两个堆的总重量接近 sum/2; 19 | * 4. dp[i] 表示背包容量限制为 i 时能够装载的最大石头的重量; 20 | * 5. 那么就可以分为拿或者不拿。 21 | */ 22 | public class Solution { 23 | public int lastStoneWeightII(int[] stones) { 24 | if (stones == null || stones.length == 0) { 25 | return 0; 26 | } 27 | 28 | // 获取石头的总重量 29 | int sum = 0; 30 | for (int num : stones) { 31 | sum += num; 32 | } 33 | 34 | int max = sum / 2; 35 | int[] dp = new int[max + 1]; 36 | for (int i = 0; i < stones.length; i++) { 37 | int curStone = stones[i]; 38 | for (int j = max; j >= curStone; j--) { 39 | dp[j] = Math.max(dp[j], dp[j - curStone] + curStone); 40 | } 41 | } 42 | return sum - 2 * dp[max]; 43 | } 44 | 45 | public static void main(String[] args) { 46 | Solution solution = new Solution(); 47 | int[] stones = {2, 7, 4, 1, 8, 1}; 48 | 49 | System.out.println(solution.lastStoneWeightII(stones)); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/SwordToOfferSolution/_10_01_Fibonacci/Solution.java: -------------------------------------------------------------------------------- 1 | package SwordToOfferSolution._10_01_Fibonacci; 2 | 3 | /* 4 | * 斐波那契数列 5 | * 6 | * 题目描述: 7 | * 写一个函数,输入 n,求斐波那契(Fibonacci)数列的第 n 项。 8 | * 9 | * 思路: 10 | * 1. 递归; 11 | * 2. 带有备忘录的 DP 数组迭代; 12 | * 3. DP。 13 | */ 14 | public class Solution { 15 | // 递归 16 | public int fib1(int n) { 17 | return n < 2 ? n : fib1(n - 1) + fib1(n - 2); 18 | } 19 | 20 | // DP 数组迭代 21 | public int fib2(int n) { 22 | if (n < 2) { 23 | return n; 24 | } 25 | 26 | int[] dp = new int[n + 1]; 27 | dp[0] = 0; 28 | dp[1] = 1; 29 | // 注意 i 的取值范围,画图理解 30 | for (int i = 2; i <= n; i++) { 31 | dp[i] = (dp[i - 1] + dp[i - 2]) % 1000000007; 32 | } 33 | 34 | return dp[n]; 35 | } 36 | 37 | // DP 38 | // 由于之前备忘录递归使用了大小为 N 的数组,所以空间复杂度为 O(N) 39 | // 但是发现,第 i 项只和第 i-1 项以及第 i-2 项有关, 40 | // 所以这里只是用三个变量,将空间复杂度降到 O(1) 41 | public int fib3(int n) { 42 | if (n < 2) { 43 | return n; 44 | } 45 | 46 | int res = 0; 47 | int pre = 1; 48 | int prepre = 0; 49 | 50 | for (int i = 2; i <= n; i++) { 51 | res = (pre + prepre) % 1000000007; 52 | prepre = pre; 53 | pre = res; 54 | } 55 | return res; 56 | } 57 | 58 | public static void main(String[] args) { 59 | Solution solution = new Solution(); 60 | 61 | System.out.println(solution.fib1(45)); 62 | System.out.println(solution.fib2(45)); 63 | System.out.println(solution.fib3(45)); 64 | } 65 | } -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_08_LinkedList/_1171_RemoveZeroSumConsecutiveNodesFromLinkedList/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._08_LinkedList._1171_RemoveZeroSumConsecutiveNodesFromLinkedList; 2 | 3 | import java.util.HashMap; 4 | 5 | /* 6 | * 从链表中删去总和值为零的连续节点 7 | * 8 | * 题目描述: 9 | * 给你一个链表的头节点 head,请你编写代码,反复删去链表中由 总和 值为 0 的连续节点组成的序列,直到不存在这样的序列为止。 10 | * 删除完毕后,请你返回最终结果链表的头节点。 11 | * 你可以返回任何满足题目要求的答案。 12 | * 13 | * 思路: 14 | * 1. 遍历两次 map,第一次将当前节点所对应的连续和作为 key,将当前节点作为 value,存入 map 中,有新的 value 就将其覆盖; 15 | * 2. 第二次遍历 map,直接将当前节点的 next 指向当前和所对应的 value 的 next 节点; 16 | * 3. 由于在第一次遍历 map 的时候,含有相同连续和的节点是被更新过的,也就是位于链表的后面的位置; 17 | * 4. 所以,在第二次遍历 map 的时候,直接来到被更新过的节点的 next 节点即可,也就是将之前的那些节点给删除掉。 18 | */ 19 | public class Solution { 20 | class ListNode { 21 | int val; 22 | ListNode next; 23 | 24 | ListNode(int val) { 25 | this.val = val; 26 | } 27 | } 28 | 29 | public ListNode removeZeroSumSublists(ListNode head) { 30 | if (head == null) { 31 | return null; 32 | } 33 | 34 | ListNode dummy = new ListNode(0); 35 | dummy.next = head; 36 | ListNode cur = dummy; 37 | HashMap map = new HashMap<>(); 38 | int sum = 0; 39 | for (cur = dummy; cur != null; cur = cur.next) { 40 | sum += cur.val; 41 | map.put(sum, cur); 42 | } 43 | 44 | sum = 0; 45 | for (cur = dummy; cur != null; cur = cur.next) { 46 | sum += cur.val; 47 | cur.next = map.get(sum).next; 48 | } 49 | return dummy.next; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/SwordToOfferSolution/_53_03_IntegerIdenticalToIndex/Solution.java: -------------------------------------------------------------------------------- 1 | package SwordToOfferSolution._53_03_IntegerIdenticalToIndex; 2 | 3 | /* 4 | * 数组中数值和对应下标相同的元素 5 | * 6 | * 题目描述: 7 | * 假设一个单调递增的数组里的每个元素都是整数并且是唯一的。 8 | * 请编程实现一个函数找出数组中任意一个数值等于其下标的元素。 9 | * 例如,在数组 {-3, -1, 1, 3, 5} 中,数字 3 和它的下标相等。 10 | * 11 | * 思路: 12 | * 1. 由于数组是递增有序的,所以可以使用二分法; 13 | * 2. 如果一开始的第 i 个数对应的下标就是 i,则直接就找到了; 14 | * 3. 如果第 i 个数对应的下标不是 i,假设元素为 m: 15 | * 3.1) 如果 m > i,即元素值大于它的下标,则对于任意大于 0 的数 k,位于下标 i + k 的数字的值大于或等于元素 m + k; 16 | * 也就是 nums[i + k] 大于或等于 m + k,又由于 m > i,则 m + k > i + k; 17 | * 也就是说,如果第 i 的数字的值大于 i,则它右边的数字都大于对应的下标; 18 | * 此时,只需要从左部分的数组中找就可以了。 19 | * 3.2) 如果 m < i,则情况正好相反,只需要从右部分的数组中找就可以了。 20 | */ 21 | public class Solution { 22 | public int integerIdenticalToIndex(int[] nums) { 23 | if (nums == null || nums.length < 1) { 24 | return -1; 25 | } 26 | 27 | int left = 0; 28 | int right = nums.length - 1; 29 | 30 | while (left <= right) { 31 | int middle = left + ((right - left) >> 1); 32 | if (nums[middle] == middle) { 33 | return middle; 34 | } 35 | if (nums[middle] > middle) { 36 | right = middle - 1; 37 | } else { 38 | left = middle + 1; 39 | } 40 | } 41 | 42 | return -1; 43 | } 44 | 45 | public static void main(String[] args) { 46 | Solution solution = new Solution(); 47 | int[] nums = {-3, -1, 1, 3, 5}; 48 | 49 | System.out.println(solution.integerIdenticalToIndex(nums)); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_02_String/_415_AddStrings/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._02_String._415_AddStrings; 2 | 3 | /* 4 | * 字符串相加 5 | * 6 | * 题目描述: 7 | * 给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和。 8 | * 注意: 9 | * num1 和num2 的长度都小于 5100. 10 | * num1 和num2 都只包含数字 0-9. 11 | * num1 和num2 都不包含任何前导零。 12 | * 你不能使用任何內建 BigInteger 库, 也不能直接将输入的字符串转换为整数形式。 13 | * 14 | * 思路: 15 | * 1. 使用双指针,从两个字符串的最后一位开始模拟相加操作,将进位单独保存下来; 16 | * 2. 将每次的计算结果添加到 StringBuilder 中; 17 | * 3. 最后需要将 StringBuilder 进行翻转,然后再返回。 18 | */ 19 | public class Solution { 20 | public String addStrings(String num1, String num2) { 21 | if (num1 == null || num1.length() == 0 || num2 == null || num2.length() == 0) { 22 | return ""; 23 | } 24 | 25 | int i = num1.length() - 1; 26 | int j = num2.length() - 1; 27 | 28 | StringBuilder sb = new StringBuilder(); 29 | 30 | int carry = 0; 31 | while (i >= 0 || j >= 0) { 32 | int n1 = (i >= 0) ? num1.charAt(i--) - '0' : 0; 33 | int n2 = (j >= 0) ? num2.charAt(j--) - '0' : 0; 34 | int sum = n1 + n2 + carry; 35 | carry = sum / 10; 36 | sum = sum % 10; 37 | 38 | sb.append(sum); 39 | } 40 | if (carry == 1) { 41 | sb.append("1"); 42 | } 43 | return sb.reverse().toString(); 44 | } 45 | 46 | public static void main(String[] args) { 47 | Solution solution = new Solution(); 48 | String num1 = "123"; 49 | String num2 = "234"; 50 | 51 | System.out.println(solution.addStrings(num1, num2)); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_01_ArrayAndMatrix/_118_PascalsTriangle/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._01_ArrayAndMatrix._118_PascalsTriangle; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | 7 | /* 8 | * 杨辉三角 9 | * 10 | * 题目描述: 11 | * 给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。 12 | * 在「杨辉三角」中,每个数是它左上方和右上方的数的和。 13 | * 14 | * 思路: 15 | * 1. 第 n 行的第 i 个数等于 n - 1 行的第 i - 1 个数加第 n - 1 行的第 i 个数; 16 | * 2. 时间复杂度为 O(numsRows ^ 2),不考虑返回值所占空间的话,空间复杂度为 O(1)。 17 | */ 18 | public class Solution { 19 | public List> generate(int numRows) { 20 | List> ans = new ArrayList<>(); 21 | if (numRows < 1) { 22 | return ans; 23 | } 24 | 25 | for (int i = 0; i < numRows; i++) { 26 | List row = new ArrayList<>(); 27 | // 第 n 行有 n + 1 个数字(n 从 0 开始) 28 | // 所以第 i 行的下标 j 最多来到 i 29 | for (int j = 0; j <= i; j++) { 30 | if (j == 0 || j == i) { 31 | row.add(1); 32 | } else { 33 | row.add(ans.get(i - 1).get(j - 1) + ans.get(i - 1).get(j)); 34 | } 35 | } 36 | ans.add(row); 37 | } 38 | 39 | return ans; 40 | } 41 | 42 | public static void main(String[] args) { 43 | Solution solution = new Solution(); 44 | 45 | System.out.println(Arrays.toString(solution.generate(5).toArray())); // [[1], [1, 1], [1, 2, 1], [1, 3, 3, 1], [1, 4, 6, 4, 1]] 46 | System.out.println(Arrays.toString(solution.generate(1).toArray())); // [[1]] 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_03_Tree/_637_AverageOfLevelsInBinaryTree/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._03_Tree._637_AverageOfLevelsInBinaryTree; 2 | 3 | import java.util.ArrayList; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | import java.util.Queue; 7 | 8 | /* 9 | * 二叉树的层平均值 10 | * 11 | * 题目描述: 12 | * 给定一个非空二叉树, 返回一个由每层节点平均值组成的数组. 13 | * 14 | * 思路: 15 | * 1. 直接使用 BFS 即可; 16 | * 2. 需要注意的是:由于总和可能会超过 int 的最大值,因此可以使用 double 类型的 sum 存储总和。 17 | */ 18 | public class Solution { 19 | class TreeNode { 20 | int val; 21 | TreeNode left; 22 | TreeNode right; 23 | 24 | TreeNode(int val) { 25 | this.val = val; 26 | } 27 | } 28 | 29 | public List averageOfLevels(TreeNode root) { 30 | List res = new ArrayList<>(); 31 | if (root == null) { 32 | return res; 33 | } 34 | Queue queue = new LinkedList<>(); 35 | queue.offer(root); 36 | 37 | while (!queue.isEmpty()) { 38 | int size = queue.size(); 39 | // 用 double 类型存储每次的总和,如果用 int 存储的话,它们的和有可能溢出 40 | double sum = 0; 41 | for (int i = 0; i < size; i++) { 42 | TreeNode node = queue.poll(); 43 | sum += node.val; 44 | if (node.left != null) { 45 | queue.offer(node.left); 46 | } 47 | if (node.right != null) { 48 | queue.offer(node.right); 49 | } 50 | } 51 | res.add(sum / size); 52 | } 53 | return res; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_01_ArrayAndMatrix/_243_ShortestWordDistance/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._01_ArrayAndMatrix._243_ShortestWordDistance; 2 | 3 | /* 4 | * 最短单词距离 5 | * 6 | * 题目描述: 7 | * 给定一列单词列表 words 和两个单词 word1、word2,返回列表中这两个单词的最短距离。 8 | * 你可以假设两个给定的单词不同,并且均在单词列表中。 9 | * 10 | * 思路: 11 | * 1. 在遍历 words 的过程中,判断当前单词是否等于给定的 word1 或 word2; 12 | * 2. 若等于,则更新索引,并维护一个 ans 取最小的距离。 13 | */ 14 | public class Solution { 15 | public int shortestDistance(String[] words, String word1, String word2) { 16 | if (words == null || words.length == 0) { 17 | return 0; 18 | } 19 | 20 | int idx1 = -1, idx2 = -1; 21 | int ans = Integer.MAX_VALUE; 22 | for (int i = 0; i < words.length; i++) { 23 | String word = words[i]; 24 | if (word.equals(word1)) { 25 | idx1 = i; 26 | } 27 | if (word.equals(word2)) { 28 | idx2 = i; 29 | } 30 | if (idx1 != -1 && idx2 != -1) { 31 | ans = Math.min(ans, Math.abs(idx1 - idx2)); 32 | } 33 | } 34 | 35 | return ans; 36 | } 37 | 38 | public static void main(String[] args) { 39 | String[] words = {"practice", "makes", "perfect", "coding", "makes"}; 40 | String word1 = "coding", word2 = "practice"; 41 | String word3 = "makes", word4 = "coding"; 42 | 43 | Solution solution = new Solution(); 44 | System.out.println(solution.shortestDistance(words, word1, word2)); // 3 45 | System.out.println(solution.shortestDistance(words, word3, word4)); // 1 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_03_Tree/_687_LongestUnivaluePath/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._03_Tree._687_LongestUnivaluePath; 2 | 3 | /* 4 | * 最长同值路径 5 | * 6 | * 题目描述: 7 | * 给定一个二叉树,找到最长的路径,这个路径中的每个节点具有相同值。 8 | * 这条路径可以经过也可以不经过根节点。 9 | * 注意:两个节点之间的路径长度由它们之间的边数表示。 10 | * 11 | * 思路: 12 | * 1. 注意理解题意:找到路径中每个节点具有相同值的最长路径; 13 | * 2. 对于当前节点来说,它的左孩字在给它返回最大值之后,需要再判断一下左孩子的节点值是否和当前节点值相同; 14 | * 3. 如果不同,则就需要将左孩子所返回的最大值置为 0,因为我需要找的是每个节点具有相同值的路径; 15 | * 4. 同理,对于右孩子也是一样的。 16 | */ 17 | public class Solution { 18 | class TreeNode { 19 | int val; 20 | TreeNode left; 21 | TreeNode right; 22 | 23 | TreeNode(int val) { 24 | this.val = val; 25 | } 26 | } 27 | 28 | private int res = 0; 29 | 30 | public int longestUnivaluePath(TreeNode root) { 31 | if (root == null) { 32 | return 0; 33 | } 34 | 35 | dfs(root); 36 | 37 | return res; 38 | } 39 | 40 | private int dfs(TreeNode root) { 41 | // 如果当前节点的左右孩子是空的,则不需要给当前节点返回任何值 42 | if (root.left == null && root.right == null) { 43 | return 0; 44 | } 45 | 46 | int leftSize = (root.left == null) ? 0 : dfs(root.left) + 1; 47 | int rightSize = (root.right == null) ? 0 : dfs(root.right) + 1; 48 | 49 | if (leftSize > 0 && root.left.val != root.val) { 50 | leftSize = 0; 51 | } 52 | if (rightSize > 0 && root.right.val != root.val) { 53 | rightSize = 0; 54 | } 55 | res = Math.max(res, leftSize + rightSize); 56 | 57 | return Math.max(leftSize, rightSize); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_01_DoublePointer/_125_Valid_Palindrome/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._01_DoublePointer._125_Valid_Palindrome; 2 | 3 | /* 4 | * 验证回文串 5 | * 6 | * 题目描述: 7 | * 给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。 8 | * 9 | * 思路: 10 | * 1. 由于只考虑字母和数字字符,因此可以使用 Character.isLetterOrDigit() 函数,只要遇到不是字母和数字的字符,则将其越过; 11 | * 2. 使用前后指针分别进行比较即可。 12 | */ 13 | public class Solution { 14 | public boolean isPalindrome(String s) { 15 | if (s == null) { 16 | return false; 17 | } 18 | // 空字符串也属于回文串 19 | if (s.length() == 0) { 20 | return true; 21 | } 22 | // 统一转换成小写字母 23 | s = s.toLowerCase(); 24 | int i = 0; 25 | int j = s.length() - 1; 26 | while (i < j) { 27 | // 这里的两个 while 循环表示的意思是:在不越界的情况下,只要当前的字符不是字母或者数字,则越过该字符 28 | while (i < j && !Character.isLetterOrDigit(s.charAt(i))) { 29 | i++; 30 | } 31 | while (i < j && !Character.isLetterOrDigit(s.charAt(j))) { 32 | j--; 33 | } 34 | if (s.charAt(i) != s.charAt(j)) { 35 | return false; 36 | } 37 | // 不要忘记更新 i 和 j 的索引 38 | i++; 39 | j--; 40 | } 41 | return true; 42 | } 43 | 44 | public static void main(String[] args) { 45 | Solution solution = new Solution(); 46 | String s1 = "A man, a plan, a canal: Panama"; 47 | String s2 = "0p"; 48 | 49 | System.out.println(solution.isPalindrome(s1)); 50 | System.out.println(solution.isPalindrome(s2)); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/SwordToOfferSolution/_11_01_MinNumberInRotatedArray/Solution.java: -------------------------------------------------------------------------------- 1 | package SwordToOfferSolution._11_01_MinNumberInRotatedArray; 2 | 3 | /* 4 | * 寻找旋转排序数组中的最小值 5 | * 6 | * 题目描述: 7 | * 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 8 | * 输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。 9 | * 注意:数组中不存在重复元素。 10 | * 11 | * 思路: 12 | * 1. 直接使用二分法进行查找; 13 | * 2. 拿 nums[middle] 和 nums[right] 进行比较: 14 | * 2.1) 如果 nums[middle] < nums[right],说明 middle 和 right - 1 之内的所有的元素都比 right 小, 15 | * 则此最小的元素有可能在 nums[middle] 位置上,也有可能在 nums[middle] 的左边,所以需要将 right 定位到 middle 的位置; 16 | * 2.2) 如果 nums[middle] > nums[right],说明最小的元素在 middle 的右侧, 17 | * 因此需要将 left 置为 middle + 1。 18 | * 3. 由于没有重复的元素,因此不需要判断相等的情况。 19 | */ 20 | public class Solution { 21 | 22 | // 数组中元素不重复 23 | public int minNumberInRotateArray(int[] nums) { 24 | if (nums == null || nums.length == 0) { 25 | return -1; 26 | } 27 | 28 | int left = 0, right = nums.length - 1; 29 | while (left < right) { 30 | int middle = left + ((right - left) >> 1); 31 | if (nums[middle] < nums[right]) { 32 | right = middle; 33 | } else if (nums[middle] > nums[right]) { 34 | left = middle + 1; 35 | } 36 | } 37 | 38 | return nums[left]; 39 | } 40 | 41 | public static void main(String[] args) { 42 | int[] nums1 = {4, 5, 6, 7, 0, 1, 2}; 43 | int[] nums2 = {3, 4, 5, 1, 2}; 44 | 45 | Solution solution = new Solution(); 46 | System.out.println(solution.minNumberInRotateArray(nums1)); 47 | System.out.println(solution.minNumberInRotateArray(nums2)); 48 | } 49 | } -------------------------------------------------------------------------------- /src/SwordToOfferSolution/_32_02_PrintTreesInLines/Solution.java: -------------------------------------------------------------------------------- 1 | package SwordToOfferSolution._32_02_PrintTreesInLines; 2 | 3 | import java.util.ArrayList; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | import java.util.Queue; 7 | 8 | /* 9 | * 分行从上到下打印二叉树 10 | * 11 | * 题目描述: 12 | * 从上到下按层打印二叉树,同一层的结点按从左到右的顺序打印,每一层打印到一行。 13 | * 14 | * 思路: 15 | * 1. 该题和上一道题的整体思路是一样的,也是使用 BFS 实现; 16 | * 2. 只不过在遍历当前队列中的元素之前,需要在每一层创建一个 ArrayList; 17 | * 3. 最后再将每个 ArrayList 添加到最终的结果 list 中进行返回。 18 | */ 19 | public class Solution { 20 | static class TreeNode { 21 | private int val; 22 | private TreeNode left; 23 | private TreeNode right; 24 | 25 | private TreeNode(int val) { 26 | this.val = val; 27 | } 28 | } 29 | 30 | public List> levelOrder(TreeNode root) { 31 | List> ans = new ArrayList<>(); 32 | if (root == null) { 33 | return ans; 34 | } 35 | 36 | Queue queue = new LinkedList<>(); 37 | queue.offer(root); 38 | while (!queue.isEmpty()) { 39 | int size = queue.size(); 40 | List list = new ArrayList<>(); 41 | for (int i = 0; i < size; i++) { 42 | TreeNode node = queue.poll(); 43 | list.add(node.val); 44 | if (node.left != null) { 45 | queue.offer(node.left); 46 | } 47 | if (node.right != null) { 48 | queue.offer(node.right); 49 | } 50 | } 51 | ans.add(list); 52 | } 53 | return ans; 54 | } 55 | } -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_02_DP/_139_WordBreak/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._02_DP._139_WordBreak; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | /* 7 | * 单词拆分 8 | * 9 | * 题目描述: 10 | * 给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。 11 | * 说明: 12 | * 拆分时可以重复使用字典中的单词。 13 | * 你可以假设字典中没有重复的单词。 14 | * 15 | * 思路: 16 | * 1. 使用 DP 的思想,dp[i] 表示在 s 中的前 i 个字母(从 0 到 i-1)能够与字典中的单词进行匹配成功,也就是能够进行拆分; 17 | * 2. 或者可以说 dp[i] 表示以 i-1 结尾的字符串是否可以被字典拆分; 18 | * 3. 那么转移方程就是 dp[j] = dp[i] + check(s[i+1, j]); 19 | * 4. 例如wordDict=["apple", "pen", "code"],s = "applepencode"; 20 | * 那么 dp[8] = dp[5] + check("pen") 表示如果想要判断 'c' 位置的话,则需要判断前 5 位(不包括 5)是否能够被拆分,以及从第 5 位到第 8 位上的字母。 21 | */ 22 | public class Solution { 23 | public boolean wordBreak(String s, List workDict) { 24 | if (s == null || s.length() == 0) { 25 | return false; 26 | } 27 | 28 | int len = s.length(); 29 | boolean[] dp = new boolean[len + 1]; 30 | dp[0] = true; 31 | for (int i = 1; i <= len; i++) { 32 | for (int j = 0; j < i; j++) { 33 | if (dp[j] && workDict.contains(s.substring(j, i))) { 34 | dp[i] = true; 35 | break; 36 | } 37 | } 38 | } 39 | 40 | return dp[len]; 41 | } 42 | 43 | public static void main(String[] args) { 44 | Solution solution = new Solution(); 45 | String s = "applepenapple"; 46 | List wordDict = Arrays.asList("apple", "pen"); 47 | 48 | System.out.println(solution.wordBreak(s, wordDict)); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_04_BinarySearch/_35_SearchInserrPosition/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._04_BinarySearch._35_SearchInserrPosition; 2 | 3 | /* 4 | * 搜索插入位置 5 | * 6 | * 题目描述: 7 | * 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 8 | * 请必须使用时间复杂度为 O(log n) 的算法。 9 | * 10 | * 思路: 11 | * 1. 看到有序数组和 O(long n),首先想到使用二分; 12 | * 2. 与普通二分的区别在于,该题有一个要求是"如果目标值不存在于数组中,返回它将会被按顺序插入的位置"; 13 | * 3. 其实需要注意二分最后返回的 i,如果没有找到 target 的话,i 最终会指向 target 左侧元素的位置,此时需要返回 i + 1; 14 | * 4. 这个 i + 1 就可以满足"如果目标值不存在于数组中,返回它将会被按顺序插入的位置"; 15 | * 5. 但如果 while 中的条件为 i <= j 的话,那么其实就可以省略步骤 i + 1,因为当 i == j 的时候,指针还会移动一次。 16 | */ 17 | public class Solution { 18 | public int searchInsert(int[] nums, int target) { 19 | if (nums == null || nums.length == 0) { 20 | return -1; 21 | } 22 | 23 | int i = 0, j = nums.length - 1; 24 | while (i <= j) { 25 | int mid = i + ((j - i) >> 1); 26 | if (nums[mid] == target) { 27 | return mid; 28 | } else if (nums[mid] < target) { 29 | i = mid + 1; 30 | } else { 31 | j = mid - 1; 32 | } 33 | } 34 | 35 | return i; 36 | } 37 | 38 | public static void main(String[] args) { 39 | Solution solution = new Solution(); 40 | int[] nums = {1, 3, 5, 6}; 41 | 42 | System.out.println(solution.searchInsert(nums, 5)); // 2 43 | System.out.println(solution.searchInsert(nums, 2)); // 1 44 | System.out.println(solution.searchInsert(nums, 7)); // 4 45 | System.out.println(solution.searchInsert(nums, 0)); // 0 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_06_Search/_216_CombinationSumIII/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._06_Search._216_CombinationSumIII; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /* 7 | * 组合求和 Ⅲ 8 | * 9 | * 题目描述: 10 | * 找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1~9 的正整数,并且每种组合中不存在重复的数字。 11 | * 所有数字都是正整数。 12 | * 解集不能包含重复的组合。 13 | * 14 | * 思路: 15 | * 1. 回溯 + 递归; 16 | * 2. “相加之和”说明下一次需要寻找 target - i,i 为本层数值; 17 | * 3. “不存在重复的数字”说明 dfs 方法中存在参数 start; 18 | * 4. “解集不能包含重复的组合”说明输入参数要预先排序,由于题目本身就是从 1 到 9,已经排好序了。 19 | */ 20 | public class Solution { 21 | 22 | public List> combinationSum3(int k, int n) { 23 | List> res = new ArrayList<>(); 24 | if (k == 0 || n == 0) { 25 | return res; 26 | } 27 | 28 | List path = new ArrayList<>(); 29 | dfs(res, path, n, k, 1); 30 | return res; 31 | } 32 | 33 | // residue: 剩余多少 34 | // start: 下一轮搜索的起始元素是多少 35 | public void dfs(List> res, List path, int residue, int k, int start) { 36 | if (k == 0 && residue == 0) { 37 | res.add(new ArrayList<>(path)); 38 | return; 39 | } 40 | if (k == 0 || residue == 0) { 41 | return; 42 | } 43 | 44 | for (int i = start; i <= 9; i++) { 45 | path.add(i); 46 | dfs(res, path, residue - i, k - 1, i + 1); 47 | path.remove(path.size() - 1); 48 | } 49 | } 50 | 51 | public static void main(String[] args) { 52 | Solution s = new Solution(); 53 | System.out.println(s.combinationSum3(3, 9)); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_02_DP/_509_FibonacciNumber/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._02_DP._509_FibonacciNumber; 2 | 3 | /* 4 | * 斐波那契数 5 | * 6 | * 题目描述: 7 | * 斐波那契数,通常用 F(n) 表示,形成的序列称为斐波那契数列。 8 | * 该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。 9 | * 10 | * F(0) = 0, F(1) = 1 11 | * F(N) = F(N - 1) + F(N - 2), 其中 N > 1. 12 | * 给定 N,计算 F(N)。 13 | * 14 | * 思路: 15 | * 1. 递归; 16 | * 2. 带有备忘录的 DP; 17 | * 3. DP 18 | */ 19 | public class Solution { 20 | 21 | // 递归 22 | public int fib1(int n) { 23 | if (n < 2) { 24 | return n; 25 | } 26 | 27 | return fib1(n - 1) + fib1(n - 2); 28 | } 29 | 30 | // 带有备忘录的 DP 31 | public int fib2(int n) { 32 | if (n < 2) { 33 | return n; 34 | } 35 | 36 | int[] dp = new int[n + 1]; 37 | dp[0] = 0; 38 | dp[1] = 1; 39 | 40 | for (int i = 2; i <= n; i++) { 41 | dp[i] = dp[i - 1] + dp[i - 2]; 42 | } 43 | return dp[n]; 44 | } 45 | 46 | // DP 47 | public int fib3(int n) { 48 | if (n < 2) { 49 | return n; 50 | } 51 | 52 | int prepre = 0; 53 | int pre = 1; 54 | int res = 0; 55 | for (int i = 2; i <= n; i++) { 56 | res = prepre + pre; 57 | prepre = pre; 58 | pre = res; 59 | } 60 | return res; 61 | } 62 | 63 | public static void main(String[] args) { 64 | Solution solution = new Solution(); 65 | 66 | System.out.println(solution.fib1(4)); 67 | System.out.println(solution.fib2(4)); 68 | System.out.println(solution.fib3(4)); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/SwordToOfferSolution/_32_01_PrintTreeFromTopToBottom/Solution.java: -------------------------------------------------------------------------------- 1 | package SwordToOfferSolution._32_01_PrintTreeFromTopToBottom; 2 | 3 | import java.util.ArrayList; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | import java.util.Queue; 7 | 8 | /* 9 | * 从上到下打印二叉树(不分行) 10 | * 11 | * 题目描述: 12 | * 从上往下打印出二叉树的每个结点,同一层的结点按照从左到右的顺序打印。 13 | * 14 | * 思路: 15 | * 1. 从上到下遍历二叉树,就是 BFS; 16 | * 2. 用队列保存节点,用链表保存输出节点的值; 17 | * 3. 当队列不为空的时候,取队首元素; 18 | * 4. 如果队首元素为空则跳过,否则将队首元素的左右孩子加入到队列中。 19 | */ 20 | public class Solution { 21 | static class TreeNode { 22 | private int val; 23 | private TreeNode left; 24 | private TreeNode right; 25 | 26 | private TreeNode(int val) { 27 | this.val = val; 28 | } 29 | } 30 | 31 | public int[] levelOrder(TreeNode root) { 32 | if (root == null) { 33 | return new int[0]; 34 | } 35 | 36 | List list = new ArrayList<>(); 37 | Queue queue = new LinkedList<>(); 38 | queue.offer(root); 39 | while (!queue.isEmpty()) { 40 | TreeNode node = queue.poll(); 41 | if (node == null) { 42 | continue; 43 | } 44 | 45 | list.add(node.val); 46 | if (node.left != null) { 47 | queue.offer(node.left); 48 | } 49 | if (node.right != null) { 50 | queue.offer(node.right); 51 | } 52 | } 53 | 54 | int[] res = new int[list.size()]; 55 | for (int i = 0; i < res.length; i++) { 56 | res[i] = list.get(i); 57 | } 58 | 59 | return res; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_07_Sort/_347_TopKFrequentElements/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._07_Sort._347_TopKFrequentElements; 2 | 3 | import java.util.*; 4 | 5 | /* 6 | * 前 K 个高频元素 7 | * 8 | * 题目描述: 9 | * 给定一个非空的整数数组,返回其中出现频率前 k 高的元素。 10 | * 11 | * 思路: 12 | * 1. 首先使用 map 存储数组中的每个数以及对应的出现次数; 13 | * 2. 然后根据谁出现次数多的方式进行比较,将数组中的值放进 heap 中,但 heap 中的个数不能超过给定的 k; 14 | * 3. 使用 LinkedList 存储前 k 个最高的元素,由于是大顶堆,所以最后再将 LinkedList 中的元素进行反转即可。 15 | */ 16 | public class Solution { 17 | public static List topKFrequent(int[] arr, int k) { 18 | if (arr == null || arr.length == 0) { 19 | return null; 20 | } 21 | 22 | // map.getOrDefault(a, b) :先判断 map 中有没有 a 这个 key, 23 | // 如果有的话,则返回对应的值;如果没有的话,则返回默认值 b 24 | HashMap map = new HashMap<>(); 25 | for (int n : arr) { 26 | map.put(n, map.getOrDefault(n, 0) + 1); 27 | } 28 | 29 | PriorityQueue maxHeap = new PriorityQueue<>((n1, n2) -> map.get(n1) - map.get(n2)); 30 | for (int n : map.keySet()) { 31 | maxHeap.add(n); 32 | if (maxHeap.size() > k) { 33 | maxHeap.poll(); 34 | } 35 | } 36 | 37 | // 由于涉及到添加元素,所以底层使用链表 38 | List res = new LinkedList<>(); 39 | while (!maxHeap.isEmpty()) { 40 | res.add(maxHeap.poll()); 41 | } 42 | Collections.reverse(res); 43 | return res; 44 | } 45 | 46 | public static void main(String[] args) { 47 | int[] arr = {4, 1, -1, 2, -1, 2, 3}; 48 | int k = 2; 49 | System.out.println(topKFrequent(arr, k)); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_01_ArrayAndMatrix/_228_SummaryRanges/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._01_ArrayAndMatrix._228_SummaryRanges; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /* 7 | * 汇总区间 8 | * 9 | * 题目描述: 10 | * 给定一个无重复元素的有序整数数组 nums。 11 | * 返回恰好覆盖数组中所有数字的最小有序区间范围列表。也就是说,nums 的每个元素都恰好被某个区间范围所覆盖,并且不存在属于某个范围但不属于 nums 的数字 x。 12 | * 列表中的每个区间范围 [a,b] 应该按如下格式输出: 13 | * "a->b",如果 a != b 14 | * "a",如果 a == b 15 | * 16 | * 思路:双指针 17 | * 1. 遍历数组的过程中,如果后一个元素与前一个元素的差值为 1,则说明需要继续向右移动指针; 18 | * 2. 否则开始结算,需要注意结算时左右索引的位置。 19 | */ 20 | public class Solution { 21 | public List summaryRanges(int[] nums) { 22 | List ans = new ArrayList<>(); 23 | if (nums == null || nums.length == 0) { 24 | return ans; 25 | } 26 | 27 | int n = nums.length; 28 | int i = 0; 29 | while (i < n) { 30 | int low = i; 31 | i++; 32 | while (i < n && nums[i] == nums[i - 1] + 1) { 33 | i++; 34 | } 35 | // 开始结算 36 | ans.add(deal(nums[low], nums[i - 1])); 37 | } 38 | 39 | return ans; 40 | } 41 | 42 | private String deal(int a, int b) { 43 | return a == b ? a + "" : a + "->" + b; 44 | } 45 | 46 | public static void main(String[] args) { 47 | Solution solution = new Solution(); 48 | int[] nums1 = {0, 1, 2, 4, 5, 7}; 49 | int[] nums2 = {0, 2, 3, 4, 6, 8, 9}; 50 | 51 | System.out.println(solution.summaryRanges(nums1)); // ["0->2","4->5","7"] 52 | System.out.println(solution.summaryRanges(nums2)); // ["0","2->4","6","8->9"] 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_03_Tree/_124_BinaryTreeMaximumPathSum/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._03_Tree._124_BinaryTreeMaximummaxPathSum; 2 | 3 | /* 4 | * 二叉树中的最大路径和 5 | * 6 | * 题目描述: 7 | * 给定一个非空二叉树,返回其最大路径和。 8 | * 本题中,路径被定义为一条从树中任意节点出发,达到任意节点的序列。该路径至少包含一个节点,且不一定经过根节点。 9 | * 10 | * 思路: 11 | * 0. https://i.loli.net/2020/08/04/g358Ae7xusrd6WP.png 12 | * 1. 思路与之前的方法相同,也是利用递归的返回值,来计算结果; 13 | * 2. 不过该题需要注意的是:由于求的是最大路径和,并且节点还含有负数,因此在递归返回的时候,如果返回值是负数,则舍弃; 14 | * 3. 每次在递归返回的时候,需要加上当前节点的值; 15 | * 4. 从图中可以看出,对于节点 8 来说,首先拿到左孩子的 maxPathSum,然后拿到右节点的 maxPathSum,然后再加上当前节点 8, 16 | * 看一看,能不能打破之前的总的 maxPathSum,如果能够打破的话,那么就更新这个 maxPathSum; 17 | * 5. 8 给 7 返回的是 8 中左右孩子最大的那个值,然后再加上 8 这个值,将这个结果返回给 7。 18 | */ 19 | public class Solution { 20 | class TreeNode { 21 | int val; 22 | TreeNode left; 23 | TreeNode right; 24 | 25 | TreeNode(int val) { 26 | this.val = val; 27 | } 28 | } 29 | 30 | // 注意,这里不能设置为 0, 31 | // 因为有可能二叉树中的节点都是负数,那么在最后返回的时候,显然不能用 0 作为返回值 32 | private int maxPathSum = Integer.MIN_VALUE; 33 | 34 | public int maxPathSum(TreeNode root) { 35 | if (root == null) { 36 | return 0; 37 | } 38 | dfs(root); 39 | return maxPathSum; 40 | } 41 | 42 | private int dfs(TreeNode root) { 43 | if (root == null) { 44 | return 0; 45 | } 46 | 47 | // 舍弃负数,如果 dfs 的返回值是负数,则将会重新计算路径和 48 | int left = Math.max(0, dfs(root.left)); 49 | int right = Math.max(0, dfs(root.right)); 50 | maxPathSum = Math.max(maxPathSum, left + right + root.val); 51 | return Math.max(left, right) + root.val; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/SwordToOfferSolution/_30_MinInStack/Solution.java: -------------------------------------------------------------------------------- 1 | package SwordToOfferSolution._30_MinInStack; 2 | 3 | import java.util.Stack; 4 | 5 | /* 6 | * 包含 min 函数的栈 7 | * 8 | * 题目描述: 9 | * 定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数。 10 | * 在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)。 11 | * 12 | * 思路: 13 | * 可以设置一个辅助栈,将每次最小的元素(之前最小的元素与新加入的元素,这两者之间的最小值)都保存起来。 14 | * 1. 设置一个 data 栈和一个 min 栈,入栈的时候,min 随着 data 一起增长; 15 | * 2. 压入 data 栈的栈顶元素和 min 的栈顶进行比较,如果 data.peek() < min.peek(),则将当前数同时压入 min 栈, 16 | * 否则重复压入 min 栈的栈顶; 17 | * 3. min 栈中的栈顶就是此时 data 栈中的最小元素; 18 | * 4. 弹出的时候,两个栈的栈顶都同时弹出。 19 | * 5. 也就是说,stack1 存储所有元素,stack2 保存的是 stack1 中所有降序元素的子序列; 20 | * 6. 入栈的时候,元素 x 先进入 stack1,然后判断 x 与 stack2.peek() 的大小,如果 x < stack2.peek(),则将 x 放入 stack2 中。 21 | */ 22 | public class Solution { 23 | 24 | static class MainStack { 25 | private Stack stack1; 26 | private Stack stack2; 27 | 28 | public MainStack() { 29 | this.stack1 = new Stack<>(); 30 | this.stack2 = new Stack<>(); 31 | } 32 | 33 | public void push(int x) { 34 | stack1.push(x); 35 | // 如果辅助栈为空,或者新加入的元素比辅助栈的栈顶小,则将新加入的元素入辅助栈 36 | if (stack2.isEmpty() || x <= stack2.peek()) { 37 | stack2.push(x); 38 | } 39 | } 40 | 41 | public void pop() { 42 | // 保持 stack1 和 stack2 的元素一致性 43 | if (stack1.pop().equals(stack2.peek())) { 44 | stack2.pop(); 45 | } 46 | } 47 | 48 | public int top() { 49 | return stack1.peek(); 50 | } 51 | 52 | public int min() { 53 | return stack2.peek(); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /src/SwordToOfferSolution/_56_01_NumbersAppearOnce/Solution.java: -------------------------------------------------------------------------------- 1 | package SwordToOfferSolution._56_01_NumbersAppearOnce; 2 | 3 | import java.util.Arrays; 4 | 5 | /* 6 | * 数组中只出现一次的两个数字 7 | * 8 | * 题目描述: 9 | * 一个整型数组里除了两个数字之外,其他的数字都出现了两次。 10 | * 请写程序找出这两个只出现一次的数字。要求时间复杂度是 O(n),空间复杂度是 O(1)。 11 | * 12 | * 思路: 13 | * 1. 两个相同的数,它们的异或结果是 0;任何一个数与 0 异或,其结果也是 0; 14 | * 2. 对于数组中,只有一个数字只出现了一次,而其它的数字只出现了两次,在找的时候,只需要将它们进行异或即可; 15 | * 3. 现在扩展到只有两个数字只出现了一次,而其它的数字只出现了两次,那么在找的时候,还是将它们进行异或操作; 16 | * 4. 然后在异或的结果中从右到左找到第一个为 1 的位置; 17 | * 5. 用这个位置上的数是不是 1 做划分,将原数组划分成一组在该位置上是 1 的部分,而另一组划分成在该位置上不是 1(也就是 0)的部分; 18 | * 6. 分别在这两部分做步骤(2)操作,即利用(2)中的思想,就可以分别得出这两部分只出现一次的数。 19 | * 20 | * 此外,异或运算是不考虑进位的加法运算,如下: 21 | * 0 ^ 0 = 0 + 0 = 0 22 | * 0 ^ 1 = 0 + 1 = 1 23 | * 1 ^ 1 = 1 + 1 = 0(不进位) 24 | */ 25 | public class Solution { 26 | // 重要的是如何将这两个不同的数划分在各自的组中 27 | public int[] singleNumbers(int[] nums) { 28 | if (nums == null || nums.length == 0) { 29 | return new int[]{-1, -1}; 30 | } 31 | 32 | int res = 0; 33 | for (int num : nums) { 34 | res ^= num; 35 | } 36 | 37 | res = Integer.highestOneBit(res); 38 | 39 | int[] ans = {0, 0}; 40 | for (int num : nums) { 41 | if ((res & num) == 0) { 42 | ans[0] ^= num; 43 | } else { 44 | ans[1] ^= num; 45 | } 46 | } 47 | 48 | return ans; 49 | } 50 | 51 | public static void main(String[] args) { 52 | Solution solution = new Solution(); 53 | int[] nums = {1, 2, 10, 4, 1, 4, 3, 3}; 54 | 55 | System.out.println(Arrays.toString(solution.singleNumbers(nums))); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_08_LinkedList/_328_OddEvenLinkedList/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._08_LinkedList._328_OddEvenLinkedList; 2 | 3 | /* 4 | * 奇偶链表 5 | * 6 | * 题目描述: 7 | * 给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。 8 | * 请尝试使用原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(nodes),nodes 为节点总数。 9 | * 10 | * 思路: 11 | * 1. 遍历一趟链表,交叉地改变奇偶节点的引用关系; 12 | * 2. 最后再将最后一个奇节点的 next 域指向偶节点链表中的第一个节点即可。 13 | */ 14 | public class Solution { 15 | class ListNode { 16 | int val; 17 | ListNode next; 18 | 19 | ListNode(int val) { 20 | this.val = val; 21 | } 22 | } 23 | 24 | public ListNode oddEvenList(ListNode head) { 25 | if (head == null) return null; 26 | 27 | // 指针 oddHead 表示在节点编号为奇数的节点上进行移动 28 | // 指针 evenHead 表示在节点编号为偶数的节点上进行移动 29 | ListNode oddHead = head, evenHead = head.next; 30 | // 程序最终会将给定的链表分成两个链表,一个奇数链表,一个偶数链表, 31 | // 而 cur 表示偶数链表的头节点,我们最后需要将奇数链表的最后一个节点的 next 域指向 cur, 32 | // 最终完成拼接 33 | ListNode cur = evenHead; 34 | 35 | ListNode oddNext = null, evenNext = null; 36 | while (oddHead.next != null && oddHead.next.next != null && evenHead.next != null) { 37 | // 防止链表断裂 38 | oddNext = oddHead.next.next; 39 | evenNext = evenHead.next.next; 40 | // 修改节点的引用 41 | oddHead.next = oddNext; 42 | evenHead.next = evenNext; 43 | // 来到下一个节点,继续处理 44 | oddHead = oddNext; 45 | evenHead = evenNext; 46 | } 47 | // 最后,需要将奇数链表中最后一个节点的 next 域指向偶数节点的头节点 48 | oddHead.next = cur; 49 | return head; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_02_DP/_70_ClimbingStairs/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._02_DP._70_ClimbingStairs; 2 | 3 | /* 4 | * 爬楼梯 5 | * 6 | * 题目描述 7 | * 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 8 | * 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? 9 | * 注意:给定 n 是一个正整数。 10 | * 11 | * 思路: 12 | * 1. 递归 13 | * 2. 带有备忘录的 DP 14 | * 3. DP 15 | * 4. f(n) = f(n - 1) + f(n - 2) 16 | * 5. f(0) = 1, f(1) = 1 17 | */ 18 | public class Solution { 19 | // 递归 20 | public int climbStairs1(int n) { 21 | if (n < 2) { 22 | return 1; 23 | } 24 | return climbStairs1(n - 1) + climbStairs1(n - 2); 25 | } 26 | 27 | // 带有备忘录的 DP 28 | public int climbStairs2(int n) { 29 | if (n < 2) { 30 | return 1; 31 | } 32 | 33 | int[] dp = new int[n + 1]; 34 | dp[0] = 1; 35 | dp[1] = 1; 36 | for (int i = 2; i <= n; i++) { 37 | dp[i] = dp[i - 1] + dp[i - 2]; 38 | } 39 | return dp[n]; 40 | } 41 | 42 | // DP 43 | public int climbStairs3(int n) { 44 | if (n < 2) { 45 | return 1; 46 | } 47 | 48 | int prepre = 1; 49 | int pre = 1; 50 | int res = 0; 51 | 52 | for (int i = 2; i <= n; i++) { 53 | res = prepre + pre; 54 | prepre = pre; 55 | pre = res; 56 | } 57 | return res; 58 | } 59 | 60 | public static void main(String[] args) { 61 | Solution solution = new Solution(); 62 | 63 | System.out.println(solution.climbStairs1(3)); 64 | System.out.println(solution.climbStairs2(3)); 65 | System.out.println(solution.climbStairs3(3)); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_01_ArrayAndMatrix/_59_SpiralMatrixII/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._01_ArrayAndMatrix._59_SpiralMatrixII; 2 | 3 | import java.util.Arrays; 4 | 5 | /* 6 | * 螺旋矩阵 II 7 | * 8 | * 题目描述: 9 | * 给你一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix。 10 | * 11 | * 思路:模拟 12 | * 1. 定义左、右、上、下、边界分别为 l、r、t、b,初始填入的第一个数 num 为 1; 13 | * 2. 按照从左到右、从上到下、从右到左、从下到上的顺序填入 num,每次填入后 num++,同时更新边界,即向内缩 1 个单位的距离 14 | */ 15 | public class Solution { 16 | 17 | public int[][] generateMatrix(int n) { 18 | int l = 0, r = n - 1, t = 0, b = n - 1; 19 | int[][] matrix = new int[n][n]; 20 | int num = 1; 21 | while (num <= n * n) { 22 | // 左 -> 右 23 | for (int i = l; i <= r; i++) { 24 | matrix[t][i] = num++; 25 | } 26 | t++; 27 | // 上 -> 下 28 | for (int i = t; i <= b; i++) { 29 | matrix[i][r] = num++; 30 | } 31 | r--; 32 | // 右 -> 左 33 | for (int i = r; i >= l; i--) { 34 | matrix[b][i] = num++; 35 | } 36 | b--; 37 | // 下 -> 上 38 | for (int i = b; i >= t; i--) { 39 | matrix[i][l] = num++; 40 | } 41 | l++; 42 | } 43 | 44 | return matrix; 45 | } 46 | 47 | public static void main(String[] args) { 48 | Solution solution = new Solution(); 49 | 50 | System.out.println(Arrays.deepToString(solution.generateMatrix(3))); // [[1, 2, 3], [8, 9, 4], [7, 6, 5]] 51 | System.out.println(Arrays.deepToString(solution.generateMatrix(1))); // [[1]] 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_02_DP/_1143_LongestCommonSubsequence/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._02_DP._1143_LongestCommonSubsequence; 2 | 3 | /* 4 | * 最长公共子序列 5 | * 6 | * 题目描述: 7 | * 给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列的长度。 8 | * 9 | * 思路: 10 | * 1. DP; 11 | * 2. 定义 dp[i][j] 表示 s1 中 1~i 字符与 s2 中 1~j 字符的最长公共子序列的长度; 12 | * 3. 之所以从 1 开始,是因为需要将 0 行和 0 列置为 0; 13 | * 例如 dp[0][3] 表示对于字符串 "" 和 "bab",它们之间的最长公共子序列为 0; 14 | * 3. 如果 s1 中的 i 和 s2 中的 j 相等,则 dp[i][j] 就等于前一个字符的最长公共子序列再加上当前相等的这个 1,即 dp[i-1][j-1]+1; 15 | * 4. 如果 s1 中的 i 和 s2 中的 j 不相等,则就需要求出 i 之前和 j 之前的最大值,即 max(dp[i-1][j], dp[i][j-1]); 16 | */ 17 | public class Solution { 18 | 19 | public int longestCommonSubsequence(String text1, String text2) { 20 | if (text1 == null || text2 == null || text1.length() == 0 || text2.length() == 0) { 21 | return 0; 22 | } 23 | 24 | int len1 = text1.length(); 25 | int len2 = text2.length(); 26 | 27 | int[][] dp = new int[len1 + 1][len2 + 1]; 28 | 29 | for (int i = 1; i <= len1; i++) { 30 | for (int j = 1; j <= len2; j++) { 31 | if (text1.charAt(i - 1) == text2.charAt(j - 1)) { 32 | dp[i][j] = dp[i - 1][j - 1] + 1; 33 | } else { 34 | dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); 35 | } 36 | } 37 | } 38 | return dp[len1][len2]; 39 | } 40 | 41 | public static void main(String[] args) { 42 | Solution solution = new Solution(); 43 | String s1 = "abcde"; 44 | String s2 = "ace"; 45 | 46 | System.out.println(solution.longestCommonSubsequence(s1, s2)); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/AlgorithmThought/_03_Greedy/_45_JumpGameII/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.AlgorithmThought._03_Greedy._45_JumpGameII; 2 | 3 | /* 4 | * 跳跃游戏 Ⅱ 5 | * 6 | * 题目描述: 7 | * 给定一个非负整数数组,你最初位于数组的第一个位置。数组中的每个元素代表你在该位置可以跳跃的最大长度。 8 | * 你的目标是使用最少的跳跃次数到达数组的最后一个位置。 9 | * 10 | * 也就是说,给定一个长度为 n 的 0 索引整数数组 nums,初始位置为 nums[0]。 11 | * 每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处: 12 | * 0 <= j <= nums[i] 13 | * i + j < n 14 | * 返回到达 nums[n - 1] 的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]。 15 | * 16 | * 思路: 17 | * 0. 需要求最少的跳跃次数; 18 | * 1. 还是使用贪心的方法,每次在可跳范围内选择可以跳的更远的位置; 19 | * 2. 用 end 表示当前能够到达的边界,在遍历数组的时候,每当到达了边界之后再更新新的边界。 20 | */ 21 | public class Solution { 22 | public int jump(int[] nums) { 23 | if (nums == null || nums.length == 0) { 24 | return 0; 25 | } 26 | 27 | int end = 0; 28 | int maxPos = 0; 29 | int steps = 0; 30 | // 这里需要注意的是,开始的时候边界是第 0 个位置,已经将步数加 1 了, 31 | // 因此如果是 i < nums.length 的话,那么 i 会来到最后一个位置, 32 | // 会造成 steps 多加了一次 33 | for (int i = 0; i < nums.length - 1; i++) { 34 | // 找到能跳的最远的距离 35 | maxPos = Math.max(maxPos, i + nums[i]); 36 | // 更新边界(更新下次能跳到最远的距离) 37 | if (i == end) { 38 | end = maxPos; 39 | steps++; 40 | } 41 | } 42 | 43 | return steps; 44 | } 45 | 46 | public static void main(String[] args) { 47 | Solution solution = new Solution(); 48 | int[] nums1 = {2, 3, 1, 1, 4}; 49 | int[] nums2 = {2, 3, 0, 1, 4}; 50 | 51 | System.out.println(solution.jump(nums1)); // 2 52 | System.out.println(solution.jump(nums2)); // 2 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/LeetCodeSolution/DataStructure/_08_LinkedList/_86_PartitionList/Solution.java: -------------------------------------------------------------------------------- 1 | package LeetCodeSolution.DataStructure._08_LinkedList._86_PartitionList; 2 | 3 | /* 4 | * 分割链表 5 | * 6 | * 题目描述: 7 | * 给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。 8 | * 你应当保留两个分区中每个节点的初始相对位置。 9 | * 10 | * 思路一:使用两个链表 11 | * 1. 遍历一遍链表,同时收集小于 x 的节点以及大于等于 x 的节点; 12 | * 2. 因此,采用两个 dummy,分别收集这两部分的链表; 13 | * 3. 相当于组成两个新的链表,一个链表中的节点都是小于 x 的,另一个链表中的节点都是大于等于 x 的; 14 | * 4. 然后让小于 x 的链表的最后一个节点指向另一个链表的第一个节点; 15 | * 5. 最后再将大于等于 x 的那个链表的最后一个节点的 next 指为 null 即可。 16 | * 17 | * 思路二:交换节点 18 | * 1. cur 表示当前遍历到的节点,pre 表示第一个比 x 大的节点; 19 | * 2. 如果当前节点的值比 x 小,则让 cur 与 pre 进行交换,否则 cur 就往后移动; 20 | * 3. 但这样做有个缺点,就是不能保证两个节点原先的先后顺序; 21 | * 4. 综上,应根据题目灵活选择合适的方法进行解答。 22 | */ 23 | public class Solution { 24 | class ListNode { 25 | int val; 26 | ListNode next; 27 | 28 | ListNode(int val) { 29 | this.val = val; 30 | } 31 | } 32 | 33 | public ListNode partition(ListNode head, int x) { 34 | if (head == null) { 35 | return null; 36 | } 37 | 38 | ListNode dummy1 = new ListNode(-1); 39 | ListNode minPre = dummy1; 40 | ListNode dummy2 = new ListNode(-1); 41 | ListNode maxPre = dummy2; 42 | 43 | ListNode cur = head; 44 | 45 | while (cur != null) { 46 | if (cur.val < x) { 47 | minPre.next = cur; 48 | minPre = cur; 49 | } else { 50 | maxPre.next = cur; 51 | maxPre = cur; 52 | } 53 | cur = cur.next; 54 | } 55 | minPre.next = dummy2.next; 56 | maxPre.next = null; 57 | return dummy1.next; 58 | } 59 | } 60 | --------------------------------------------------------------------------------