├── LICENSE ├── README.md └── leetcodetest ├── .idea ├── .gitignore ├── misc.xml ├── modules.xml ├── uiDesigner.xml └── vcs.xml ├── leetcodetest.iml ├── out └── production │ └── leetcodetest │ └── part_1 │ ├── section1$UnionFind.class │ └── section1.class └── src └── part_1 ├── easy ├── array │ ├── CheckPossibility665.java │ ├── LongestCommonPrefix14.java │ ├── RemoveDuplicates26.java │ ├── RemoveElement27.java │ ├── corpFlightBookings1109.java │ ├── runningSum1480.java │ └── sumOddLengthSubarrays1588.java ├── arrayandmatrix │ ├── FindErrorNums645.java │ ├── FindMaxConsecutiveOnes485.java │ ├── FindShortestSubArray697.java │ ├── IsToeplitzMatrix766.java │ ├── MatrixReshape566.java │ ├── MoveZeroes283.java │ ├── findRepeatNumber_swordOffer03.java │ ├── firstUniqChar_swordOffer50.java │ ├── replaceSpace_swordOffer05.java │ └── spiralOrder_swordOffer29.java ├── arraylist │ └── Stones1046.java ├── backtracking │ └── BinaryTreePaths257.java ├── binarysearch │ ├── FirstBadVersion278.java │ ├── MySqrt69.java │ ├── NextGreatestLetter744.java │ ├── minArray_swordOffer11.java │ └── sortedArrayToBST_interview0402.java ├── bitwiseoperator │ ├── CountBits338.java │ ├── FindComplement476.java │ ├── HammingDistance461.java │ ├── HasAlternatingBits693.java │ ├── IsPowerOfFour342.java │ ├── IsPowerOfTwo231.java │ ├── MissingNumber268.java │ ├── ReverseBits190.java │ ├── SingleNumber136.java │ └── hammingWeight_swordOffer15.java ├── dp │ ├── ClimbStairs70.java │ ├── NumArray303.java │ ├── Rob198.java │ ├── Rob2_213.java │ ├── jumpFloorII_swordOffer9.java │ ├── maxSubArray_swordOffer42.java │ ├── numWays_swordOffer10_2.java │ └── rectCover_swordOffer10.java ├── greedyalgorithm │ ├── CanPlaceFlowers605.java │ ├── CheckPossibility665.java │ ├── FindContentChildren455.java │ ├── IsSubsequence392.java │ ├── MaxProfit121.java │ ├── MaxProfit122.java │ └── MaxSubArray53.java ├── hashmap │ ├── ContainsDuplicate217.java │ ├── FairCandySwap888.java │ ├── FindLHS594.java │ ├── FirstUniqChar387.java │ ├── LongestConsecutive128.java │ └── TwoSum1.java ├── linkedlist │ ├── DeleteDuplicates83.java │ ├── GetIntersectionNode160.java │ ├── IsPalindrome234.java │ ├── MergeTwoLists21.java │ ├── ReverseList206.java │ ├── deleteNode_swordOffer18.java │ ├── getIntersectionNode_swordOffer52.java │ ├── getKthFromEnd_swordOffer22.java │ ├── mergeTwoLists_swordOffer25.java │ ├── reverseList_swordOffer24.java │ └── reversePrint_swordOffer06.java ├── math │ ├── AddBinary67.java │ ├── AddStrings415.java │ ├── CanWinNim292.java │ ├── ConvertToBase7_504.java │ ├── ConvertToTitle168.java │ ├── CountPrimes204.java │ ├── GCDandLCM_multiple.java │ ├── IsPerfectSquare367.java │ ├── IsPowerOfThree326.java │ ├── MajorityElement169.java │ ├── MaximumProduct628.java │ ├── NumEquivDominoPairs1128.java │ ├── PrefixesDivBy5_1018.java │ ├── Reverse7.java │ ├── ToHex405.java │ ├── TrailingZeroes172.java │ ├── WeChatRedPacket.java │ ├── lastRemaining_swordOffer62.java │ └── majorityElement_swordOffer39.java ├── other │ └── printNumbers_swordOffer17.java ├── sort │ ├── MaximumProduct628.java │ └── exchange_swordOffer21.java ├── stackandqueue │ ├── CQueue_swordOffer09.java │ ├── IsValid20.java │ ├── MinStack155.java │ ├── MinStack_swordOffer30.java │ ├── MyQueue232.java │ ├── MyStack225.java │ ├── firstUniqChar_swordOffer50.java │ └── getLeastNumbers_swordOffer40.java ├── string │ ├── BalancedStringSplit1221.java │ ├── CountBinarySubstrings696.java │ ├── IsAnagram242.java │ ├── IsContainsByPointer148.java │ ├── IsIsomorphic205.java │ ├── IsPalindrome9.java │ ├── LengthOfLastWord58.java │ ├── LongestPalindrome409.java │ ├── ReverseWords150.java │ ├── RomanToInt13.java │ ├── StrStr28.java │ └── reverseRightWords149.java ├── tree │ ├── AverageOfLevels637.java │ ├── DiameterOfBinaryTree543.java │ ├── FindMode501.java │ ├── FindSecondMinimumValue671.java │ ├── FindTarget653.java │ ├── FindTilt563.java │ ├── GetMinimumDifference530.java │ ├── HasPathSum112.java │ ├── InvertTree226.java │ ├── IsBalanced110.java │ ├── IsSameTree100.java │ ├── IsSubtree572.java │ ├── IsSymmetric101.java │ ├── LowestCommonAncestor235.java │ ├── MaxDepth104.java │ ├── MergeTrees617.java │ ├── MinDepth111.java │ ├── PathSum3_437.java │ ├── SortedArrayToBST108.java │ ├── SumOfLeftLeaves404.java │ ├── TreeNode.java │ ├── isSymmetric_swordOffer28.java │ ├── kthLargest_swordOffer54.java │ ├── levelOrder_swordOffer32_2.java │ ├── maxDepth_swordOffer55.java │ └── mirrorTree_swordOffer27.java └── twopoint │ ├── HasCycle141.java │ ├── Merge88.java │ ├── ReverseVowels345.java │ ├── TwoSum167.java │ ├── ValidPalindrome680.java │ ├── findContinuousSequence_swordOffer57.java │ ├── reverseLeftWords_swordOffer58.java │ ├── reverseWords_swordOffer58.java │ └── twoSum_swordOffer57.java ├── hard ├── array │ └── FindMedianSortedArrays4.java ├── backtracking │ ├── SolveNQueens51.java │ └── SolveSudoku37.java ├── bfs │ └── LadderLength127.java ├── dp │ ├── MaxProfit123.java │ ├── MaxProfit188.java │ ├── MinDistance72.java │ └── fib_swordOffer10_1.java ├── math │ └── countDigitOne_swordOffer43.java ├── other │ └── isMatch_swordOffer19.java ├── queue │ └── MaxSlidingWindow239.java ├── sort │ └── reversePairs_swordOffer51.java ├── stackandqueue │ ├── MedianFinder_swordOffer41.java │ └── maxSlidingWindow_swordOffer59.java ├── tree │ └── serialize_swordOffer37.java └── ufs │ ├── HitBricks803.java │ ├── NumSimilarGroups839.java │ └── SwimInWater778.java ├── medium ├── array │ ├── FindPeakElement162.java │ ├── MaxArea11.java │ ├── Rotate189.java │ └── Search33.java ├── arrayandmatrix │ ├── ArrayNesting565.java │ ├── ConstructArray667.java │ ├── FindDuplicate287.java │ ├── KthSmallest378.java │ ├── MaxChunksToSorted769.java │ ├── SearchMatrix240.java │ └── findNumberIn2DArray_swordOffer04.java ├── backtracking │ ├── CombinationSum2_40.java │ ├── CombinationSum39.java │ ├── CombinationSum3_216.java │ ├── Combine77.java │ ├── Exist79.java │ ├── LetterCombinations17.java │ ├── Partition131.java │ ├── Permute46.java │ ├── PermuteUnique47.java │ ├── RestoreIpAddresses93.java │ ├── Subsets78.java │ ├── SubsetsWithDup90.java │ ├── exist_swordOffer12.java │ └── partition_swordOffer86_2.java ├── bfs │ ├── NumSquares279.java │ └── ShortestPathBinaryMatrix1091.java ├── binarysearch │ ├── FindMin153.java │ ├── GetNumberOfK_newcoderJZ37.java │ ├── SearchRange34.java │ └── SingleNonDuplicate540.java ├── bitwiseoperator │ ├── FindNumsAppearOnce_swordOffer40.java │ ├── GetSum371.java │ ├── MaxProduct318.java │ ├── SingleNumber260.java │ └── SwapNumbers16_01.java ├── dfs │ ├── FindCircleNum547.java │ ├── MaxAreaOfIsland695.java │ ├── NumIslands200.java │ ├── PacificAtlantic417.java │ ├── Solve130.java │ ├── movingCount_swordOffer13.java │ └── permutation_swordOffer38.java ├── divideandconquer │ ├── DiffWaysToCompute241.java │ ├── GenerateTrees95.java │ └── myPow_swordOffer16.java ├── dp │ ├── CanPartition416.java │ ├── Change518.java │ ├── CoinChange322.java │ ├── CombinationSum4_377.java │ ├── DicesProbability60.java │ ├── FindLongestChain646.java │ ├── FindMaxForm474.java │ ├── FindTargetSumWays494.java │ ├── IntegerBreak343.java │ ├── LengthOfLIS300.java │ ├── LongestCommonSubsequence1143.java │ ├── MaxProfit309.java │ ├── MaxProfit714.java │ ├── MinDistance583.java │ ├── MinPathSum64.java │ ├── MinSteps650.java │ ├── NumDecodings91.java │ ├── NumSquares279.java │ ├── NumberOfArithmeticSlices413.java │ ├── UniquePaths62.java │ ├── WiggleMaxLength376.java │ ├── WordBreak139.java │ ├── constructArr_swordOffer66.java │ ├── dicesSum_swordOffer60.java │ ├── lengthOfLongestSubstring_swordOffer48.java │ ├── maxValue_swordOffer47.java │ └── nthUglyNumber_swordOffer49.java ├── graph │ ├── CanFinish207.java │ ├── FindOrder210.java │ ├── FindRedundantConnection684.java │ └── IsBipartite785.java ├── greedyalgorithm │ ├── EraseOverlapIntervals435.java │ ├── FindMinArrowShots452.java │ ├── PartitionLabels763.java │ ├── ReconstructQueue406.java │ ├── cuttingRope_swordOffer14_1.java │ └── numRescueBoats881.java ├── linkedlist │ ├── AddTwoNumbers2.java │ ├── AddTwoNumbers445.java │ ├── OddEvenList328.java │ ├── RemoveNthFromEnd19.java │ ├── SplitListToParts725.java │ ├── SwapPairs24.java │ ├── copyRandomList_swordOffer35.java │ ├── deleteDuplicates_swordOffer82.java │ └── detectCycle142.java ├── math │ ├── Divide29.java │ ├── MinMoves2_462.java │ └── ProductExceptSelf238.java ├── other │ ├── StrToInt_swordOffer49.java │ ├── add_swordOffer65.java │ ├── findNthDigit_swordOffer44.java │ ├── isNumber_swordOffer20.java │ ├── isStraight_swordOffer61.java │ ├── sumNums_swordOffer64.java │ └── translateNum_swordOffer46.java ├── sort │ ├── FindKthLargest215.java │ ├── FindLongestWord524.java │ ├── FrequencySort451.java │ ├── SortColors75.java │ ├── TopKFrequent347.java │ ├── minNumber_swordOffer45.java │ └── smallestK_interview17_14.java ├── stackandqueue │ ├── DailyTemperatures739.java │ ├── NextGreaterElements503.java │ └── validateStackSequences_swordOffer31.java ├── string │ ├── Convert6.java │ ├── CountSubstrings647.java │ ├── GenerateParenthesis22.java │ ├── IntToRoman12.java │ ├── LengthOfLongestSubstring3.java │ ├── LongestPalindrome5.java │ ├── Multiply43.java │ └── MyAtoi8.java ├── tree │ ├── ConvertBST538.java │ ├── FindBottomLeftValue513.java │ ├── FindLongest687.java │ ├── GetNext_swordOffer57.java │ ├── InorderTraversal94.java │ ├── KthSmallest230.java │ ├── LowestCommonAncestor235.java │ ├── LowestCommonAncestor236.java │ ├── MapSum677.java │ ├── NumTrees96.java │ ├── PostorderTraversal145.java │ ├── PreorderTraversal144.java │ ├── Rob337.java │ ├── SortedListToBST109.java │ ├── TreeNode.java │ ├── Trie208.java │ ├── TrimBST669.java │ ├── buildTree_swordOffer07.java │ ├── isSubStructure_swordOffer26.java │ ├── levelOrder_swordOffer32_1.java │ ├── levelOrder_swordOffer32_3.java │ ├── pathSum_swordOffer34.java │ ├── treeToDoublyList_swordOffer36.java │ └── verifyPostorder_swordOffer33.java ├── twopoint │ ├── FourSum18.java │ ├── JudgeSquareSum633.java │ ├── ThreeSum15.java │ └── ThreeSumClosest16.java └── ufs │ ├── EquationsPossible990.java │ ├── FindRedundantConnection684.java │ ├── MinimumEffortPath1631.java │ ├── RemoveStones947.java │ └── SmallestStringWithSwaps1202.java └── section1.java /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Arthurye 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /leetcodetest/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Datasource local storage ignored files 5 | /dataSources/ 6 | /dataSources.local.xml 7 | # Editor-based HTTP Client requests 8 | /httpRequests/ 9 | -------------------------------------------------------------------------------- /leetcodetest/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /leetcodetest/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /leetcodetest/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /leetcodetest/leetcodetest.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /leetcodetest/out/production/leetcodetest/part_1/section1$UnionFind.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h03147/Algorithm-bro/e51f3f013fa179d75300c9a7a996c59ed1a2a2cc/leetcodetest/out/production/leetcodetest/part_1/section1$UnionFind.class -------------------------------------------------------------------------------- /leetcodetest/out/production/leetcodetest/part_1/section1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h03147/Algorithm-bro/e51f3f013fa179d75300c9a7a996c59ed1a2a2cc/leetcodetest/out/production/leetcodetest/part_1/section1.class -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/array/LongestCommonPrefix14.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.array; 2 | 3 | public class LongestCommonPrefix14 { 4 | 5 | public String longestCommonPrefix(String[] strs) { 6 | // 按位纵向比较第一个strs[0]和其他每一个strs[i]的那一位是否相等,只要碰到一个字符串中的当前位字符 7 | // 不匹配或者i已经到其中某个字符串结尾了,都说明匹配结束了,已经是最长的前缀匹配了。反之说明最长的 8 | // 前缀就是strs[0] 9 | if(strs == null || strs.length == 0) { 10 | return ""; 11 | } 12 | int n = strs[0].length(); 13 | int cnt = strs.length; 14 | for(int i = 0; i < n; ++i) { 15 | char c = strs[0].charAt(i); 16 | for(int j = 1; j < cnt; ++j) { 17 | if(i == strs[j].length() || c != strs[j].charAt(i)) { 18 | return strs[0].substring(0, i); 19 | } 20 | } 21 | } 22 | return strs[0]; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/array/RemoveDuplicates26.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.array; 2 | 3 | public class RemoveDuplicates26 { 4 | 5 | public int removeDuplicates(int[] nums) { 6 | if(nums.length == 0) return 0; 7 | int i = 0, j = 1; 8 | while(j < nums.length) { 9 | if(nums[j] == nums[i]) { 10 | j++; 11 | } else { 12 | nums[++i] = nums[j++]; 13 | } 14 | } 15 | return i + 1; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/array/RemoveElement27.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.array; 2 | 3 | public class RemoveElement27 { 4 | 5 | public int removeElement(int[] nums, int val) { 6 | int right = nums.length; 7 | int left = 0; 8 | while(left < right) { 9 | if(nums[left] == val) { 10 | nums[left] = nums[right - 1]; 11 | right--; 12 | } else { 13 | left++; 14 | } 15 | } 16 | return left; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/array/corpFlightBookings1109.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.array; 2 | 3 | public class corpFlightBookings1109 { 4 | 5 | public int[] corpFlightBookings(int[][] bookings, int n) { 6 | // 差分法 前缀和的思路 7 | int[] nums = new int[n]; 8 | for(int[] book : bookings) { 9 | nums[book[0] - 1] += book[2]; 10 | if(book[1] < n) { 11 | nums[book[1]] -= book[2]; 12 | } 13 | } 14 | 15 | for(int i = 1; i < n; ++i) { 16 | nums[i] += nums[i - 1]; 17 | } 18 | return nums; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/array/runningSum1480.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.array; 2 | 3 | public class runningSum1480 { 4 | 5 | public int[] runningSum(int[] nums) { 6 | // // 方法一 中间变量找前序和累加 7 | // int size = nums.length; 8 | // int[] res = new int[size]; 9 | // int sum = 0; 10 | // for(int i = 0; i < size; ++i) { 11 | // sum += nums[i]; 12 | // res[i] += sum; 13 | // } 14 | // return res; 15 | 16 | // 方法二 从nums中第二个数开始,原地加前序结果和 17 | int size = nums.length; 18 | for(int i = 1; i < size; ++i) { 19 | nums[i] += nums[i - 1]; 20 | } 21 | return nums; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/array/sumOddLengthSubarrays1588.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.array; 2 | 3 | public class sumOddLengthSubarrays1588 { 4 | 5 | public int sumOddLengthSubarrays(int[] arr) { 6 | // // 方法一 暴力求解 7 | // int n = arr.length; 8 | // int sum = 0; 9 | // for(int start = 0; start < n; ++start) { 10 | // for(int length = 1; start + length <= n; length += 2) { 11 | // int end = start + length - 1; 12 | // for(int i = start; i <= end; ++i) { 13 | // sum += arr[i]; 14 | // } 15 | // } 16 | // } 17 | // return sum; 18 | 19 | // 方法二 前缀和 20 | int n = arr.length; 21 | int[] prefixSum = new int[n + 1]; 22 | for(int i = 0; i < n; ++i) { 23 | prefixSum[i + 1] = prefixSum[i] + arr[i]; 24 | } 25 | int sum = 0; 26 | for(int start = 0; start < n; ++start) { 27 | for(int length = 1; start + length <= n; length += 2) { 28 | int end = start + length - 1; 29 | // 得到前缀和数组prefixSums之后,对于 0 ≤ start ≤ end < n,数组arr的下标范围 30 | // [start,end]的子数组的和为 prefixSums[end + 1] − prefixSums[start] 31 | sum += prefixSum[end + 1] - prefixSum[start]; 32 | } 33 | } 34 | return sum; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/arrayandmatrix/FindErrorNums645.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.arrayandmatrix; 2 | 3 | public class FindErrorNums645 { 4 | 5 | public int[] findErrorNums(int[] nums) { 6 | // 用数组桶排序 统计每个数出现的次数,次数为2的就是重复的,次数为0的就是丢失的 7 | int[] cnt = new int[nums.length + 1]; 8 | for(int num : nums) { 9 | cnt[num]++; 10 | } 11 | int[] result = new int[2]; 12 | for(int i = 1; i < cnt.length; ++i) { 13 | if(cnt[i] == 1) { 14 | continue; 15 | } else if(cnt[i] == 2) { 16 | result[0] = i; 17 | } else { 18 | result[1] = i; 19 | } 20 | } 21 | return result; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/arrayandmatrix/FindMaxConsecutiveOnes485.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.arrayandmatrix; 2 | 3 | public class FindMaxConsecutiveOnes485 { 4 | 5 | public int findMaxConsecutiveOnes(int[] nums) { 6 | int max = 0, cur = 0; 7 | for(int num : nums) { 8 | cur = num == 1 ? cur + 1 : 0; 9 | max = Math.max(max, cur); 10 | } 11 | return max; 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/arrayandmatrix/FindShortestSubArray697.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.arrayandmatrix; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class FindShortestSubArray697 { 7 | 8 | public int findShortestSubArray(int[] nums) { 9 | /* 10 | * 分析题意:题目大概意思就是要找出数组的众数,并且还要找出众数在数组中第一次出现和最后一次出现的位置, 11 | * 两个位置组成区间长度就是答案, 如果众数不止一个,那么要取区间长度最短那个。 12 | */ 13 | Map numCnt = new HashMap<>(); 14 | Map numFirstIndex = new HashMap<>(); 15 | Map numLastIndex = new HashMap<>(); 16 | 17 | for(int i = 0; i < nums.length; ++i) { 18 | int num = nums[i]; 19 | numCnt.put(num, numCnt.getOrDefault(num, 0) + 1); 20 | numLastIndex.put(num, i); 21 | if(!numFirstIndex.containsKey(num)) { 22 | numFirstIndex.put(num, i); 23 | } 24 | } 25 | int maxCnt = 0; 26 | for(int num : nums) { 27 | maxCnt = Math.max(maxCnt, numCnt.get(num)); 28 | } 29 | int result = nums.length; 30 | for(int num : nums) { 31 | int cnt = numCnt.get(num); 32 | if(cnt != maxCnt) continue; 33 | result = Math.min(result, numLastIndex.get(num) - numFirstIndex.get(num) + 1); 34 | } 35 | return result; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/arrayandmatrix/IsToeplitzMatrix766.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.arrayandmatrix; 2 | 3 | public class IsToeplitzMatrix766 { 4 | 5 | public boolean isToeplitzMatrix(int[][] matrix) { 6 | // 检验第一行 7 | for(int i = 0; i < matrix[0].length; ++i) { 8 | if(!check(matrix, matrix[0][i], 0, i)) { 9 | return false; 10 | } 11 | } 12 | // 检验第一列 13 | for(int j = 1; j < matrix.length; ++j) { 14 | if(!check(matrix, matrix[j][0], j, 0)) { 15 | return false; 16 | } 17 | } 18 | return true; 19 | } 20 | private boolean check(int[][] matrix, int expectValue, int row, int col) { 21 | if(row >= matrix.length || col >= matrix[0].length) { 22 | return true; 23 | } 24 | if(matrix[row][col] != expectValue) { 25 | return false; 26 | } 27 | return check(matrix, expectValue, row + 1, col + 1); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/arrayandmatrix/MatrixReshape566.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.arrayandmatrix; 2 | 3 | public class MatrixReshape566 { 4 | 5 | public int[][] matrixReshape(int[][] mat, int r, int c) { 6 | int m = mat.length, n = mat[0].length; 7 | if(m * n != r * c) return mat; 8 | int[][] reshapeNUms = new int[r][c]; 9 | // int index = 0; 10 | // for(int i = 0; i < r; ++i) { 11 | // for(int j = 0; j < c; ++j) { 12 | // reshapeNUms[i][j] = mat[index / n][index % n]; 13 | // index++; 14 | // } 15 | // } 16 | for(int i = 0; i < m * n; ++i) { 17 | reshapeNUms[i / c][i % c] = mat[i / n][i % n]; 18 | } 19 | return reshapeNUms; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/arrayandmatrix/MoveZeroes283.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.arrayandmatrix; 2 | 3 | public class MoveZeroes283 { 4 | 5 | public void moveZeroes(int[] nums) { 6 | /* 7 | * 方法一:直接用一个索引指针,先把非零元素过滤存储到数组前面,保持非零元素相对顺序不变,遍历完毕后 8 | * 索引指针刚好停留在非零元素末尾位置,后面一直到数组结尾填充0即可。 9 | * 方法二:向右冒泡排序改编,只比较向右冒牌排序0元素,最终0元素会排在最右边。 10 | */ 11 | 12 | // 方法一 遍历移位法 13 | // int index = 0; 14 | // for(int num : nums) { 15 | // if(num != 0) { 16 | // nums[index++] = num; 17 | // } 18 | // } 19 | // while(index < nums.length) { 20 | // nums[index++] = 0; 21 | // } 22 | 23 | // 方法二 向右冒泡排序魔改版(只把0向右冒泡) 24 | int n = nums.length; 25 | for(int i = n - 1; i > 0; --i) { 26 | for(int j = 0; j < i; ++j) { 27 | if(nums[j] == 0 && nums[j + 1] != 0) { 28 | nums[j] = nums[j + 1]; 29 | nums[j + 1] = 0; 30 | } 31 | } 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/arrayandmatrix/findRepeatNumber_swordOffer03.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.arrayandmatrix; 2 | 3 | public class findRepeatNumber_swordOffer03 { 4 | 5 | public int findRepeatNumber(int[] nums) { 6 | // // 方法一 排序后滑动找相邻的数组 7 | // Arrays.sort(nums); 8 | // // System.out.println(Arrays.toString(nums)); 9 | // int result = -1; 10 | // for(int i = 1; i < nums.length; ++i) { 11 | // if(nums[i - 1] == nums[i]) { 12 | // result = nums[i]; 13 | // break; 14 | // } 15 | // } 16 | // return result; 17 | 18 | // 方法二 原地判断 nums[i] = i, 是否成立,如果成立就继续遍历后面的数,不成立就进一步找 index = nums[i] 19 | // 位置的 nums[index] = nums[i] 是否成立,如果成立,则找到了重复的数就是nums[i],否则把nums[i] 放到下标 20 | // index为nums[i] 的位置去,让此位置的下标index和其对应的数组值相等,相当于排序了一趟把他放在有序的位置, 21 | // 下一次再次找到重复的数组值,又去下标index为数组值的位置上去找,发现两个数组值相等,说明重复了,返回结果。 22 | for(int i = 0; i < nums.length; ++i) { 23 | while(nums[i] != i) { 24 | if(nums[nums[i]] == nums[i]) { 25 | return nums[i]; 26 | } 27 | swap(nums, i, nums[i]); 28 | } 29 | } 30 | return -1; 31 | } 32 | 33 | private void swap(int[] nums, int i, int j) { 34 | int t = nums[i]; 35 | nums[i] = nums[j]; 36 | nums[j] = t; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/arrayandmatrix/replaceSpace_swordOffer05.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.arrayandmatrix; 2 | 3 | public class replaceSpace_swordOffer05 { 4 | 5 | public String replaceSpace(String s) { 6 | /** 7 | * 分析题意: 8 | * ① 在字符串尾部填充任意字符,使得字符串的长度等于替换之后的长度。因为一个空格要替换成 9 | * 三个字符(%20),所以当遍历到一个空格时,需要在尾部填充两个任意字符。 10 | * ② 令 P1 指向字符串原来的末尾位置,P2 指向字符串现在的末尾位置。P1 和 P2 从后向前遍历, 11 | * 当 P1 遍历到一个空格时,就需要令 P2 指向的位置依次填充 02%(注意是逆序的),否则就填 12 | * 充上 P1 指向字符的值。从后向前遍是为了在改变 P2 所指向的内容时,不会影响到 P1 遍历原 13 | * 来字符串的内容。 14 | * ③ 当 P2 遇到 P1 时(P2 <= P1),或者遍历结束(P1 < 0),退出。 15 | */ 16 | StringBuilder str = new StringBuilder(s); 17 | int P1 = str.length() - 1; 18 | for(int i = 0; i <= P1; ++i) { 19 | if(str.charAt(i) == ' ') { 20 | str.append(" "); // 填充两个空格 21 | } 22 | } 23 | int P2 = str.length() - 1; 24 | while(P1 >= 0 && P2 > P1) { 25 | char c = str.charAt(P1--); 26 | if(c == ' ') { 27 | str.setCharAt(P2--, '0'); 28 | str.setCharAt(P2--, '2'); 29 | str.setCharAt(P2--, '%'); 30 | } else { 31 | str.setCharAt(P2--, c); 32 | } 33 | } 34 | return str.toString(); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/arraylist/Stones1046.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.arraylist; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | 6 | /** 7 | * description: 1046 粉碎石头 8 | */ 9 | public class Stones1046 { 10 | 11 | public static void main(String[] args) { 12 | int[] array1 = {2, 7, 4, 1, 8, 1}; 13 | System.out.println(stones(array1)); 14 | } 15 | 16 | private static int stones(int[] stones) { 17 | // {2,7,4,1,8,1} 18 | ArrayList arr2list = new ArrayList(); 19 | int size = stones.length; 20 | for(int i = 0; i < size; ++i) 21 | { 22 | arr2list.add(stones[i]); 23 | } 24 | 25 | while (size > 1) { 26 | Integer max1 = Collections.max(arr2list); 27 | arr2list.remove(max1); 28 | Integer max2 = Collections.max(arr2list); 29 | arr2list.remove(max2); 30 | if (max1 > max2) { 31 | int newstone = max1 - max2; 32 | arr2list.add(newstone); 33 | size -= 1; 34 | } else { 35 | size -= 2; 36 | } 37 | 38 | } 39 | if(arr2list.size() == 1) 40 | { 41 | return arr2list.get(0); 42 | }else { 43 | return 0; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/binarysearch/FirstBadVersion278.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.binarysearch; 2 | 3 | public class FirstBadVersion278 { 4 | 5 | public static void main(String[] args) { 6 | System.out.println(firstBadVersion(5)); 7 | } 8 | 9 | public static int firstBadVersion(int n) { 10 | /* 11 | * 分析题意:我们可以把需要查找的数组,想象成一个只有true和false两个值 12 | * 出现的boolean型数组,而且只要我们在搜索中找到一个true出现,代表从这 13 | * 个位置开始先后全部都是错误版本,返回结果一定都是true,这题我们依旧使 14 | * 用二分查找法来求解。既然当前版本右边都是错误版本,故我们需要判断,当前 15 | * 错误版本的左边是否存在比它更旧的版本也出现错误,即折半左区间[low, mid] 16 | *,而当前mid的值是false的时候,就说明第一个错误的版本一定在mid有边且不包 17 | * 括mid,即[mid + 1, n]的区间。 18 | */ 19 | 20 | int low = 1, high = n; 21 | // 因为向左折半的时候high = m故while循环相等情况要去掉 22 | while(low < high) { 23 | int mid = low + (high - low) / 2; 24 | if(isBadVersion(mid)) { 25 | high = mid; 26 | } else { 27 | low = mid + 1; 28 | } 29 | } 30 | return low; 31 | } 32 | 33 | private static boolean isBadVersion(int x){ 34 | boolean[] nums = {false, false, false, true, true}; 35 | return nums[x - 1]; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/binarysearch/MySqrt69.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.binarysearch; 2 | 3 | public class MySqrt69 { 4 | 5 | public static void main(String[] args) { 6 | System.out.println(mySqrt(4)); 7 | } 8 | 9 | public static int mySqrt(int x) { 10 | // // 方法一 请神法 11 | // return (int)Math.sqrt(x); 12 | /* 13 | * 分析题意:首先我们求的是开平方根,取值范围肯定是(0, x)范围内的, 14 | * 如果我们从大到小一个一个遍历,绝对的浪费时间,因为随着数越来越大,这 15 | * 个数的开平方只会在x的一半的左边越来越左,所以我们可以考虑用二分法来比 16 | * 较求值可以减少不必要的比较。 17 | */ 18 | 19 | // 首先x小于等于1时是特例,单独判断 20 | if(x <= 1) { 21 | return x; 22 | } 23 | 24 | int low = 1, high = x; 25 | while(low <= high) { 26 | int mid = low + (high - low) / 2; 27 | int sqrt = x / mid; 28 | if(sqrt == mid) { 29 | return mid; 30 | } else if(mid > sqrt) { 31 | high = mid - 1; 32 | } else { 33 | low = mid + 1; 34 | } 35 | } 36 | // 循环结束条件是high位置比low位置小一位,而本题取整舍去小数部分,故取左值high 37 | return high; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/binarysearch/NextGreatestLetter744.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.binarysearch; 2 | 3 | public class NextGreatestLetter744 { 4 | 5 | public static void main(String[] args) { 6 | char[] letters = {'c', 'f', 'j'}; 7 | System.out.println(nextGreatestLetter(letters, 'f')); 8 | } 9 | 10 | public static char nextGreatestLetter(char[] letters, char target) { 11 | /* 12 | * 分析题意:题目要求在给定有序列表中找到比target目标字母大的 13 | * 最小的字母,已知有序,那么我们就可以考虑用二分查找法来求值。 14 | */ 15 | int size = letters.length; 16 | int low = 0, high = size - 1; 17 | while(low <= high) { 18 | int mid = low + (high - low) / 2; 19 | // 因为我们要找到第一个比目标字母大的字母并非找目标字母,所以只要当前字母 20 | // 比目标字母小于或等于都归为一种情况,反之则是另一种情况。 21 | if(letters[mid] <= target) { 22 | low = mid + 1; 23 | } else { 24 | high = mid - 1; 25 | } 26 | } 27 | // 结束条件是high在low左边一位,故第一个大约目标值的位置应该为low指针位置, 28 | // 但是如果low指针一直走到了列表结尾也没找到大于target的最小字母,那就返回 29 | // 第一个字符就行。 30 | return low < size ? letters[low] : letters[0]; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/binarysearch/minArray_swordOffer11.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.binarysearch; 2 | 3 | public class minArray_swordOffer11 { 4 | 5 | public int minArray(int[] numbers) { 6 | /** 7 | * 分析题意:此时问题的关键在于确定对半分得到的两个数组哪一个是旋转数组,哪一个是非递减数组。我们很容易 8 | * 知道非递减数组的第一个元素一定小于等于最后一个元素。 9 | * 10 | * 通过修改二分查找算法进行求解(l 代表 low,m 代表 mid,h 代表 high): 11 | * 12 | * 当 nums[m] <= nums[h] 时,表示 [m, h] 区间内的数组是非递减数组,[l, m] 区间内的数组是旋转数组, 13 | * 此时令 h = m;否则 [m + 1, h] 区间内的数组是旋转数组,令 l = m + 1。 14 | */ 15 | if(numbers.length == 0) return 0; 16 | int l = 0, h = numbers.length - 1; 17 | while(l < h) { 18 | int m = l + (h - l) / 2; 19 | // 如果数组元素允许重复,会出现一个特殊的情况:nums[l] == nums[m] == nums[h],此时无法确定 20 | // 解在哪个区间,需要切换到顺序查找。 21 | if(numbers[l] == numbers[m] && numbers[m] == numbers[h]) { 22 | return minNumber(numbers, l, h); 23 | } else if(numbers[m] > numbers[h]) { 24 | l = m + 1; 25 | } else { 26 | h = m; 27 | } 28 | } 29 | return numbers[l]; 30 | } 31 | 32 | private int minNumber(int[] numbers, int l, int h) { 33 | for(int i = l; i < h; ++i) { 34 | if(numbers[i]> numbers[i + 1]) { 35 | return numbers[i + 1]; 36 | } 37 | } 38 | return numbers[l]; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/binarysearch/sortedArrayToBST_interview0402.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.binarysearch; 2 | 3 | public class sortedArrayToBST_interview0402 { 4 | 5 | public TreeNode sortedArrayToBST(int[] nums) { 6 | // 二分法,尽量均分左右子树使得高度最小 7 | return sortedArrayToBST(nums, 0, nums.length - 1); 8 | } 9 | 10 | public TreeNode sortedArrayToBST(int[] nums, int low, int high) { 11 | if(low > high) return null; 12 | int mid = low + (high - low) / 2; 13 | TreeNode node = new TreeNode(nums[mid]); 14 | node.left = sortedArrayToBST(nums, low, mid - 1); 15 | node.right = sortedArrayToBST(nums, mid + 1, high); 16 | return node; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/bitwiseoperator/CountBits338.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.bitwiseoperator; 2 | 3 | public class CountBits338 { 4 | 5 | public int[] countBits(int n) { 6 | // // 方法一 调用工具类 遍历统计每个数二进制数对应一的个数即可 7 | // int[] ret = new int[n + 1]; 8 | // for(int i = 1; i <= n; ++i) { 9 | // ret[i] = Integer.bitCount(i); 10 | // } 11 | // return ret; 12 | 13 | // 方法二 动态规划 找到递推方程为 dp[i] = dp[i&(i-1)] + 1 14 | // 验证:6(110) = 4(100) + 2(10) 15 | // 根据n & (n - 1) 实际上是把 n 的二进制数中最低为的1给去掉了,这里6的二进制110相当于把第二位1给去掉了 16 | // 而4的二进制100刚好等于2的二进制10 + 1 得到的,故推出动态规划递推式 dp[i] = dp[i&(i-1)] + 1 17 | int[] dp = new int[n + 1]; 18 | for(int i = 1; i <= n; ++i) { 19 | dp[i] = dp[i & (i - 1)] + 1; 20 | } 21 | return dp; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/bitwiseoperator/FindComplement476.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.bitwiseoperator; 2 | 3 | public class FindComplement476 { 4 | 5 | public int findComplement(int num) { 6 | /** 7 | * 分析题意:题目要求我们找到一个正整数二进制数的补数,即所有位按位取反即可。取反我们很容易想到异或。 8 | * 我们用掩码(位掩码)来与原码做异或运算,会更快找到补数。举例子 9 | * 例如 5的二进制表示为 101 ,为了按异或取反,我们可以找到一个掩码 111 ,这样这个掩码和原码进行位运 10 | * 算(异或)可以达到我们需要求补数的目的,101 ^ 111 = 010 结果对应十进制数为 2。验证成功 11 | */ 12 | 13 | // 正整数 有符号最高位为0,还剩下31位,1左移30位使其到可能的最高位1 14 | int mask = 1 << 30; 15 | //循环找num中的最高位1在哪一位,找到了循环结束,mask的1停留在与num最高位1相同位置做标记 16 | while((num & mask) == 0) mask >>= 1; 17 | // 然后mask左进一位再减1就得到了我们需要的和num位数相同,全1的掩码 18 | mask = (mask << 1) - 1; 19 | // 然后异或就是补数 20 | return num ^ mask; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/bitwiseoperator/HammingDistance461.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.bitwiseoperator; 2 | 3 | public class HammingDistance461 { 4 | 5 | public int hammingDistance(int x, int y) { 6 | /* 7 | * 分析题意:让 x 和 y 做异或运算即可知道相异的是哪几位,然后统计异或后1的个数就是两个整数之间的汉明距离 8 | */ 9 | 10 | // // 方法一 调用统计二进制数中1个数的工具类 11 | // return Integer.bitCount(x ^ y); 12 | 13 | // // 方法二 异或 x,y 再与上1 + 右位移运算,统计1的位数 14 | // int z = x ^ y; 15 | // int cnt = 0; 16 | // while(z != 0) { 17 | // if((z & 1) == 1) cnt++; 18 | // z = z >> 1; 19 | // } 20 | // return cnt; 21 | 22 | // 方法三 n&(n-1) 去除 n 的位级表示中最低的那一位 1 23 | int z = x ^ y; 24 | int cnt = 0; 25 | while(z != 0) { 26 | z &= z - 1; 27 | cnt++; 28 | } 29 | return cnt; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/bitwiseoperator/HasAlternatingBits693.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.bitwiseoperator; 2 | 3 | public class HasAlternatingBits693 { 4 | 5 | public boolean hasAlternatingBits(int n) { 6 | /* 7 | * 分析题意:如果 n 的二进制是 010101... 或者 101010... 交替出现的,我们可以尝试想到,相邻的两位数刚好异或 8 | * 我们不妨尝试让 n 右移一位,这样 1010 就变成 101了,然后 1010 ^ 101 正好就错位了,这两个数异或的结果为全1 9 | * 我们把这个异或的结果设为 a 存储起来,然后进一步验证下 a 是否是全1呢,于是我们进一步利用补1进位的特点,一个 10 | * 数二进制形式如果是全1,那么补一个1之后,最高位向左再进一个1,低为全部为0了,于是我们拿 a & (a + 1) 如果结 11 | * 果为0就验证成功。 12 | */ 13 | int a = n ^ (n >> 1); 14 | return (a & (a + 1)) == 0; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/bitwiseoperator/IsPowerOfFour342.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.bitwiseoperator; 2 | 3 | public class IsPowerOfFour342 { 4 | 5 | public boolean isPowerOfFour(int n) { 6 | // 这种数在二进制表示中有且只有一个奇数位为 1,例如 16(10000)。 7 | return n > 0 && (n & (n - 1)) == 0 && (n & 0b01010101010101010101010101010101) != 0; 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/bitwiseoperator/IsPowerOfTwo231.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.bitwiseoperator; 2 | 3 | public class IsPowerOfTwo231 { 4 | 5 | public boolean isPowerOfTwo(int n) { 6 | /* 7 | * 分析题意:题目要求找到 n 是否为 2 的 幂次方,从位运算角度分析,一个数如果是2的幂次方,那么他的二进制表示 8 | * 形式中,只有可能刚好有1个1可能出现在任意位置上,这样我们转换成十进制数就是 1 * 2^n(n表示当前位向右数后面 9 | * 有多少位,二进制转十进制的算法),当然 如果是2的幂次就要排除负数,所有的负数不是2的幂次方。 10 | */ 11 | 12 | // // 方法一 n 大于零,且只有一个1调用工具类统计其二进制1的个数是否恰好为1 13 | // return n > 0 && Integer.bitCount(n) == 1; 14 | 15 | // 方法二 用 (n & (n - 1)) 因为n恰好二进制数形式只有一个1,无论在哪一位上,那么 n - 1 一定向下降位数,恰好 16 | // 和 n 的二进制数的1的位置相邻错开了,故与运算结果为0 17 | return n > 0 && (n & (n - 1)) == 0; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/bitwiseoperator/MissingNumber268.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.bitwiseoperator; 2 | 3 | public class MissingNumber268 { 4 | 5 | public int missingNumber(int[] nums) { 6 | int ret = 0; 7 | for(int i = 0; i < nums.length; ++i) { 8 | ret = ret ^ i ^ nums[i]; 9 | } 10 | return ret ^ nums.length; 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/bitwiseoperator/SingleNumber136.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.bitwiseoperator; 2 | 3 | public class SingleNumber136 { 4 | 5 | public int singleNumber(int[] nums) { 6 | /* 7 | * 分析题意:正常的每个数都出现两次,成对这个特点,在位运算里面可以用到异或,相同的两个数异或为0,而0异或任何数 8 | * 都等于任何数,这样我们遍历一边nums,把所有元素都异或连接起来,成对的都抵消为0了,最后就剩 n 个0和那单独的一 9 | * 个元素异或就等于那个元素。 10 | */ 11 | 12 | // 初始化answer为0,因为0异或任何数都等于任何数所以不影响后面连续的元素异或。 13 | int answer = 0; 14 | for(int num : nums) answer ^= num; 15 | return answer; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/bitwiseoperator/hammingWeight_swordOffer15.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.bitwiseoperator; 2 | 3 | public class hammingWeight_swordOffer15 { 4 | 5 | // you need to treat n as an unsigned value 6 | public int hammingWeight(int n) { 7 | int cnt = 0; 8 | while(n != 0) { 9 | cnt++; 10 | n &= (n - 1); 11 | } 12 | return cnt; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/dp/NumArray303.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.dp; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class NumArray303 { 7 | 8 | 9 | private int[] sums; 10 | 11 | 12 | public static void main(String[] args) { 13 | NumArray303 numArray303 = new NumArray303(); 14 | int[] nums = {-2, 0, 3, -5, 2, -1}; 15 | numArray303.NumArray(nums); 16 | List result = new ArrayList<>(); 17 | result.add(null); 18 | result.add(numArray303.sumRange(0, 2)); 19 | result.add(numArray303.sumRange(2, 5)); 20 | result.add(numArray303.sumRange(0, 5)); 21 | 22 | System.out.println(result); 23 | } 24 | 25 | public void NumArray(int[] nums) { 26 | /* 27 | * 分析题意:本题要计算从索引i到索引j范围内元素的总和,可以转换为两个累加和做差, 28 | * 即累加和sum[j + 1]和累加和sum[i]做差。 29 | */ 30 | int n = nums.length; 31 | // 数组下标从0开始,我们开辟n + 1个空间是为了方便sunRange的运算i为0不需要做特殊处理了 32 | sums = new int[n + 1]; 33 | for(int i = 0; i < n; ++i) { 34 | // sum[i + 1]计算的是0到i下标的累加和 35 | sums[i + 1] = sums[i] + nums[i]; 36 | } 37 | } 38 | 39 | public int sumRange(int left, int right) { 40 | // 这里 right + 1 减去 left 是计算0到right的累加和减去0到left - 1的累加和, 41 | // 剩下的刚好就是left到right的闭区间总和了 42 | return sums[right + 1] - sums[left]; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/dp/Rob198.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.dp; 2 | 3 | public class Rob198 { 4 | 5 | public static void main(String[] args) { 6 | System.out.println(new Rob198().rob(new int[] {1, 2, 3, 1})); 7 | } 8 | 9 | public int rob(int[] nums) { 10 | if(nums == null || nums.length == 0) { 11 | return 0; 12 | } 13 | int len = nums.length; 14 | if(len == 1) { 15 | return nums[0]; 16 | } 17 | int[] dp = new int[len]; 18 | dp[0] = nums[0]; // 初始情况下,只有一家可以偷 19 | dp[1] = Math.max(nums[0], nums[1]); // 有两家可以偷的时候,我们就选最大值 20 | for(int i = 2; i < len; ++i) { 21 | // 如果偷当前第i家,那就是偷到的i - 2家总金额再加上第i家的金额数,否则就是不偷第i家,那就是只算到 22 | // i - 1家的总金额 23 | dp[i] = Math.max(dp[i - 2] + nums[i], dp[i - 1]); 24 | } 25 | 26 | return dp[len - 1]; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/dp/Rob2_213.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.dp; 2 | 3 | import java.util.Arrays; 4 | 5 | public class Rob2_213 { 6 | 7 | public static void main(String[] args) { 8 | System.out.println(new Rob2_213().rob(new int[] {1, 2, 3, 1})); 9 | } 10 | 11 | public int rob(int[] nums) { 12 | // 分析题意213:这个问题是之前单排房子偷盗的升级版,但是我们可以把这个环形的房子街区 13 | // 拆分成两个之前单排街区来按照之前的动态规划方法进行偷盗,然后再并到一起找最大值 14 | int n = nums.length; 15 | if(n == 1) return nums[0]; 16 | return Math.max(myRob(Arrays.copyOfRange(nums, 0, n - 1)), myRob(Arrays.copyOfRange(nums, 1, n))); 17 | } 18 | 19 | private int myRob(int[] nums) { 20 | int pre = 0, cur = 0, temp; 21 | for(int num : nums) { 22 | temp = cur; 23 | cur = Math.max(pre + num, cur); 24 | pre = temp; 25 | } 26 | return cur; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/dp/jumpFloorII_swordOffer9.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.dp; 2 | 3 | public class jumpFloorII_swordOffer9 { 4 | 5 | public int jumpFloorII(int target) { 6 | return (int) Math.pow(2, target - 1); 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/dp/maxSubArray_swordOffer42.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.dp; 2 | 3 | public class maxSubArray_swordOffer42 { 4 | 5 | public int maxSubArray(int[] nums) { 6 | int greatestSum = Integer.MIN_VALUE; 7 | int sum = 0; 8 | for(int num : nums) { 9 | sum = sum <= 0 ? num : sum + num; 10 | greatestSum = Math.max(greatestSum, sum); 11 | } 12 | return greatestSum; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/dp/numWays_swordOffer10_2.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.dp; 2 | 3 | public class numWays_swordOffer10_2 { 4 | 5 | public int numWays(int n) { 6 | // if(n == 0) return 1; 7 | // if(n <= 2) return n; 8 | // int pre2 = 1, pre1 = 2, ret = 0; 9 | // for(int i = 2; i < n; ++i) { 10 | // ret = (pre2 + pre1) % 1000000007; 11 | // pre2 = pre1; 12 | // pre1 = ret; 13 | // } 14 | // return ret; 15 | 16 | int pre2 = 1, pre1 = 1, ret = 0; 17 | for(int i = 0; i < n; ++i) { 18 | ret = (pre2 + pre1) % 1000000007; 19 | pre2 = pre1; 20 | pre1 = ret; 21 | } 22 | return pre2; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/dp/rectCover_swordOffer10.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.dp; 2 | 3 | public class rectCover_swordOffer10 { 4 | 5 | public int rectCover(int target) { 6 | if(target <= 2) return target; 7 | int pre2 = 1, pre1 = 2; 8 | int ret = 0; 9 | for(int i = 3; i <= target; ++i) { 10 | ret = pre2 + pre1; 11 | pre2 = pre1; 12 | pre1 = ret; 13 | } 14 | return ret; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/greedyalgorithm/CanPlaceFlowers605.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.greedyalgorithm; 2 | 3 | public class CanPlaceFlowers605 { 4 | 5 | public static void main(String[] args) { 6 | int[] flowerbed = {1, 0, 0, 0, 1}; 7 | int n = 1; 8 | System.out.println(canPlaceFlowers(flowerbed, n)); 9 | } 10 | 11 | public static boolean canPlaceFlowers(int[] flowerbed, int n) { 12 | /* 13 | * 分析题意:判断每个位置是种花,要看他的前置节点和 14 | * 后置节点是否为空(有间隔),为空就种,否则不能种花 15 | * 有两种特殊情况的边界值需要额外考虑,如果当前位置是 16 | * 第一个花盆,他就没有前置节点,或者当前位置是最后一 17 | * 个花盆,那么就没有后置节点。但是我们为了保证判断条 18 | * 件的一致性,所以第一个花盆,让他的前置节点直接给0 19 | * 最后一个花盆的后置节点同样给0。 20 | */ 21 | 22 | int size = flowerbed.length; 23 | for(int i = 0; i < size && n > 0; ++i) { 24 | if(flowerbed[i] == 1){ 25 | continue; 26 | } 27 | // 如果当前位置为0,则判断其前置节点和后置节点是否为0(有间隔) 28 | int pre = i == 0 ? 0 : flowerbed[i - 1]; 29 | int next = i == (size - 1) ? 0 : flowerbed[i + 1]; 30 | if(pre == 0 && next == 0) { 31 | flowerbed[i] = 1; 32 | n--; 33 | } 34 | } 35 | return n <= 0; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/greedyalgorithm/FindContentChildren455.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.greedyalgorithm; 2 | 3 | import java.util.Arrays; 4 | 5 | public class FindContentChildren455 { 6 | 7 | public static void main(String[] args) { 8 | int[] g = {1, 2, 3}, s = {1, 1}; 9 | System.out.println(findContentChildren(g, s)); 10 | 11 | } 12 | 13 | public static int findContentChildren(int[] g, int[] s) { 14 | // 孩子没有胃口或者没有饼干时,结束返回0 15 | if(g == null || s == null) { 16 | return 0; 17 | } 18 | 19 | // 都不为空时,g和s都先排序,以保证更快的找 20 | // 到刚好匹配胃口值g[i]的饼干大小s[j] 21 | Arrays.sort(g); 22 | Arrays.sort(s); 23 | int g_count = 0, s_count = 0; 24 | while(g_count < g.length && s_count < s.length) { 25 | if(g[g_count] <= s[s_count]) { 26 | g_count++; 27 | } 28 | s_count++; 29 | } 30 | return g_count; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/greedyalgorithm/IsSubsequence392.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.greedyalgorithm; 2 | 3 | public class IsSubsequence392 { 4 | 5 | public static void main(String[] args) { 6 | String s = "abc", t = "ahbgdc"; 7 | System.out.println(isSubsequence(s, t)); 8 | } 9 | 10 | public static boolean isSubsequence(String s, String t) { 11 | // 方法一 双指针遍历 12 | // int len1 = s.length(), len2 = t.length(); 13 | // int i = 0, j = 0; 14 | // while(i < len1 && j < len2) { 15 | // if(s.charAt(i) == t.charAt(j)) { 16 | // i++; 17 | // } 18 | // j++; 19 | // } 20 | // return i == len1; 21 | 22 | // 方法二 indexOf 23 | int index = -1; 24 | for(char c : s.toCharArray()) { 25 | index = t.indexOf(c, index + 1); 26 | if(index == -1) { 27 | return false; 28 | } 29 | } 30 | return true; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/greedyalgorithm/MaxProfit121.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.greedyalgorithm; 2 | 3 | public class MaxProfit121 { 4 | 5 | public static void main(String[] args) { 6 | int[] prices = {7, 1, 5, 3, 6, 4}; 7 | System.out.println(maxProfit(prices)); 8 | } 9 | 10 | public static int maxProfit(int[] prices) { 11 | int size = prices.length; 12 | 13 | // 初始情况,取第一个交易日股票价格为最小买入价格 复习加一 14 | int min_buy = prices[0]; 15 | int max_profit = 0; 16 | 17 | for(int i = 1; i < size; ++i) { 18 | if(prices[i] < min_buy) { 19 | min_buy = prices[i]; 20 | } else { 21 | // 只要第二天卖出去能不亏本,我们就记一次当天卖出的收益 22 | // 来比较每一次的正收益,保留最大值即为结果。 23 | max_profit = Math.max(max_profit, prices[i] - min_buy); 24 | } 25 | } 26 | return max_profit; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/greedyalgorithm/MaxProfit122.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.greedyalgorithm; 2 | 3 | public class MaxProfit122 { 4 | 5 | public static void main(String[] args) { 6 | int[] prices = {7, 1, 5, 3, 6, 4}; 7 | System.out.println(maxProfit(prices)); 8 | } 9 | 10 | public static int maxProfit(int[] prices) { 11 | /* 12 | * 只要有正收益,我们就做t + 1日的交易卖出,每天偶读有可能买, 13 | * 每天都有可能卖,只要前一天买入今天卖有正收益我们就算一次 14 | * 收益,最后累计总和就是最大收益。 15 | */ 16 | 17 | int total_profit = 0; 18 | for(int i = 1; i < prices.length; ++i) { 19 | if(prices[i] - prices[i - 1] > 0) { 20 | total_profit += prices[i] - prices[i - 1]; 21 | } 22 | } 23 | return total_profit; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/greedyalgorithm/MaxSubArray53.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.greedyalgorithm; 2 | 3 | public class MaxSubArray53 { 4 | 5 | public static void main(String[] args) { 6 | int[] nums = {-2, 1, -3, 4, -1, 2, 1, -5, 4}; 7 | System.out.println(maxSubArray(nums)); 8 | } 9 | 10 | public static int maxSubArray(int[] nums) { 11 | /* 12 | * 分析题意:题目要求找到连续子数组的最大和是多少 13 | * 那么我们首先要尝试去找到最大和子数组的起点,当 14 | * 然这个过程我们用肉眼看不出来,需要我们完整的遍 15 | * 历数组,找到所有可能连加和大于0的子数组,并不断 16 | * 的比较,最大的那个就是我们要找的最大和 17 | */ 18 | 19 | // 初始化前驱子数组和位置 20 | int pre = nums[0]; 21 | // 让最大连续子数组和初始化为前驱子数组和 22 | int max_subnums = pre; 23 | for(int i = 1; i < nums.length; ++i) { 24 | // 如果当前前驱子数组和大于0,那么我们当前位置nums[i] 25 | // 加前驱子数组和才有可能更大,否则重新定位前驱子 26 | // 数组和位置计算行的连续子数组和,在和之前已找到 27 | // 的连续子数组和比较,取最大的那个和为返回结果 28 | pre = pre > 0 ? pre + nums[i] : nums[i]; 29 | max_subnums = Math.max(max_subnums, pre); 30 | } 31 | return max_subnums; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/hashmap/ContainsDuplicate217.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.hashmap; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | public class ContainsDuplicate217 { 7 | public boolean containsDuplicate(int[] nums) { 8 | Set hashSet = new HashSet<>(); 9 | for(int num : nums) { 10 | if(hashSet.contains(num)) { 11 | return true; 12 | } 13 | hashSet.add(num); 14 | } 15 | return false; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/hashmap/FindLHS594.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.hashmap; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class FindLHS594 { 7 | 8 | public int findLHS(int[] nums) { 9 | Map hashMap = new HashMap<>(); 10 | // 先统计每个元素出现的次数 11 | for(int num : nums) { 12 | hashMap.put(num, hashMap.getOrDefault(num, 0) + 1); 13 | } 14 | int longest = 0; 15 | // 类似于滑动窗口如果存在相邻的两个数(必定最大值和最小值差值为1),并且加起来的个数和最大就覆盖掉最大值 16 | for(int num : hashMap.keySet()) { 17 | if(hashMap.containsKey(num +1)) { 18 | longest = Math.max(longest, hashMap.get(num) + hashMap.get(num + 1)); 19 | } 20 | } 21 | return longest; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/hashmap/FirstUniqChar387.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.hashmap; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * description: 387 给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。 8 | */ 9 | public class FirstUniqChar387 { 10 | 11 | public static void main(String[] args) { 12 | String s = "leetcode"; 13 | System.out.println(finds2c(s)); 14 | } 15 | 16 | private static int finds2c(String s) { 17 | char[] s2c = s.toCharArray(); 18 | Map map = new HashMap<>(); 19 | for(char item : s2c) 20 | { 21 | map.put(item, map.getOrDefault(item, 0) + 1); 22 | } 23 | for(int i =0; i < s2c.length; ++i) 24 | { 25 | if(map.get(s2c[i]) == 1) 26 | { 27 | return i; 28 | } 29 | } 30 | return -1; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/hashmap/TwoSum1.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.hashmap; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class TwoSum1 { 7 | 8 | public int[] twoSum(int[] nums, int target) { 9 | /* 10 | * 分析题意: 两数之和这题常规思路是先把数组排序,然后用双指针或者二分法求解,时间复杂度为O(nlogn), 11 | * 空间复杂度为O(1),那这里我们正在学哈希表,不妨尝试下用空间换时间来做这个题目,我们把数组中所有的 12 | * 值依次作为hashmap的key,而数组下标index存储为key对应的value,这样我们一趟遍历,把所有数组值和 13 | * 索引的键值对插入到hashmap中,每插入一个num:index,我们就做一个判断是否存在一个key为 14 | * target - nums[i],如果存在则说明正好(i对应位置的数)+ (target - nums[i])这两个数的和就是 15 | * target了,返回索引i和target - nums[i]作为key对应的value即为答案。 16 | * 时间复杂度为O(n),空间复杂度为O(n),典型的用空间换时间。 17 | */ 18 | Map map = new HashMap<>(); 19 | for(int i = 0; i < nums.length; ++i) { 20 | if(map.containsKey(target - nums[i])) { 21 | return new int[] {i, map.get(target - nums[i])}; 22 | } else { 23 | map.put(nums[i], i); 24 | } 25 | } 26 | return null; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/linkedlist/DeleteDuplicates83.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.linkedlist; 2 | 3 | public class DeleteDuplicates83 { 4 | 5 | public ListNode deleteDuplicates(ListNode head) { 6 | 7 | /* 8 | * 分析题意:一条链,而且有序,那一趟遍历就行,需要一个指针,如果当前节点和当前节点的下一个节点相等 9 | * 我们就把当前节点与下一个节点的指向箭头给断开让其链接到当前节点的下一个节点的下一个节点,否则当 10 | * 前节点后移就行了,当前节点移动到结尾,下一个节点为空就到底了。 11 | * 递归就是先不断链,直接到底,再倒车只要倒过来第一个不重复节点,跳过除第一个外和第一个节点相等的 12 | * 节点。 13 | * 迭代的思想就是,只保留第一个正向过去的不重复节点,看到重复的当场就做个了断,到结尾就结束了。 14 | */ 15 | 16 | // // 方法一 递归 17 | // if(head == null || head.next == null) return head; 18 | // head.next = deleteDuplicates(head.next); 19 | // return head.val == head.next.val ? head.next : head; 20 | 21 | // 方法二 迭代 22 | if(head == null || head.next == null) return head; 23 | 24 | ListNode cur = head; 25 | while(cur.next != null) { 26 | if(cur.val == cur.next.val) { 27 | cur.next = cur.next.next; 28 | } else { 29 | cur = cur.next; 30 | } 31 | } 32 | return head; 33 | } 34 | 35 | public class ListNode { 36 | int val; 37 | ListNode next; 38 | 39 | ListNode(int x) { 40 | val = x; 41 | next = null; 42 | } 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/linkedlist/MergeTwoLists21.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.linkedlist; 2 | 3 | import java.util.List; 4 | 5 | public class MergeTwoLists21 { 6 | 7 | public ListNode mergeTwoLists(ListNode l1, ListNode l2) { 8 | // // 方法一 递归写法 9 | // // l1 or l2 任意一个为空,返回另一个不为空的即可 10 | // if(l1 == null) return l2; 11 | // if(l2 == null) return l1; 12 | 13 | // if(l1.val < l2.val) { 14 | // l1.next = mergeTwoLists(l1.next, l2); 15 | // return l1; 16 | // } else { 17 | // l2.next = mergeTwoLists(l1, l2.next); 18 | // return l2; 19 | // } 20 | 21 | // 方法二 迭代写法 22 | ListNode preHead = new ListNode(-1); 23 | ListNode prev = preHead; 24 | 25 | while(l1 !=null && l2 != null) { 26 | if(l1.val < l2.val) { 27 | prev.next = l1; 28 | l1 = l1.next; 29 | } else { 30 | prev.next = l2; 31 | l2 = l2.next; 32 | } 33 | prev = prev.next; 34 | } 35 | prev.next = (l1 != null) ? l1 : l2; 36 | return preHead.next; 37 | } 38 | 39 | public class ListNode { 40 | int val; 41 | ListNode next; 42 | 43 | ListNode(int x) { 44 | val = x; 45 | next = null; 46 | } 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/linkedlist/deleteNode_swordOffer18.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.linkedlist; 2 | 3 | import part_1.medium.linkedlist.deleteDuplicates_swordOffer82; 4 | 5 | public class deleteNode_swordOffer18 { 6 | 7 | public ListNode deleteNode(ListNode head, int val) { 8 | if(head.val == val) return head.next; 9 | ListNode pre = head, cur = head.next; 10 | while(cur != null && cur.val != val) { 11 | pre = cur; 12 | cur = cur.next; 13 | } 14 | if(cur != null) pre.next = cur.next; 15 | return head; 16 | } 17 | 18 | private class ListNode { 19 | int val; 20 | ListNode next; 21 | 22 | ListNode(int x) { 23 | val = x; 24 | next = null; 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/linkedlist/getIntersectionNode_swordOffer52.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.linkedlist; 2 | 3 | public class getIntersectionNode_swordOffer52 { 4 | 5 | public ListNode getIntersectionNode(ListNode headA, ListNode headB) { 6 | /** 7 | * 分析题意:设 A 的长度为 a + c,B 的长度为 b + c,其中 c 为尾部公共部分长度, 8 | * 可知 a + c + b = b + c + a。 9 | * 如果两个人有缘分的话,那么他们必定会相遇~ 10 | */ 11 | ListNode l1 = headA, l2 = headB; 12 | while(l1 != l2) { 13 | l1 = (l1 == null) ? headB : l1.next; 14 | l2 = (l2 == null) ? headA : l2.next; 15 | } 16 | return l1; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/linkedlist/getKthFromEnd_swordOffer22.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.linkedlist; 2 | 3 | public class getKthFromEnd_swordOffer22 { 4 | 5 | public ListNode getKthFromEnd(ListNode head, int k) { 6 | if(head == null) return null; 7 | ListNode node = head; 8 | ListNode knode = head; 9 | while(node != null) { 10 | if(k <= 0) { 11 | knode = knode.next; 12 | } 13 | k--; 14 | node = node.next; 15 | } 16 | return knode; 17 | } 18 | 19 | public class ListNode { 20 | int val; 21 | ListNode next; 22 | 23 | ListNode(int x) { 24 | val = x; 25 | next = null; 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/linkedlist/mergeTwoLists_swordOffer25.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.linkedlist; 2 | 3 | public class mergeTwoLists_swordOffer25 { 4 | 5 | public ListNode mergeTwoLists(ListNode l1, ListNode l2) { 6 | ListNode newHead = new ListNode(-1); 7 | ListNode cur = newHead; 8 | while(l1 != null && l2 != null) { 9 | if(l1.val <= l2.val) { 10 | cur.next = l1; 11 | l1 = l1.next; 12 | } else { 13 | cur.next = l2; 14 | l2 = l2.next; 15 | } 16 | cur = cur.next; 17 | } 18 | if(l1 != null) { 19 | cur.next = l1; 20 | } 21 | if(l2 != null) { 22 | cur.next = l2; 23 | } 24 | return newHead.next; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/linkedlist/reverseList_swordOffer24.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.linkedlist; 2 | 3 | public class reverseList_swordOffer24 { 4 | 5 | // // 方法一 双指针法 6 | // public ListNode reverseList(ListNode head) { 7 | // if(head == null || head.next == null) return head; 8 | // ListNode node, nextNode, newHeadNode; 9 | // newHeadNode = null; 10 | // node = head; 11 | // while(node != null) { 12 | // nextNode = node.next; 13 | // node.next = newHeadNode; 14 | // newHeadNode = node; 15 | // node = nextNode; 16 | // } 17 | // return newHeadNode; 18 | // } 19 | // 方法二 头插法 20 | public ListNode reverseList(ListNode head) { 21 | ListNode newList = new ListNode(-1); 22 | while (head != null) { 23 | ListNode next = head.next; 24 | head.next = newList.next; 25 | newList.next = head; 26 | head = next; 27 | } 28 | return newList.next; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/math/AddBinary67.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.math; 2 | 3 | public class AddBinary67 { 4 | 5 | public static void main(String[] args) { 6 | System.out.println(new AddBinary67().addBinary("101", "100")); 7 | } 8 | 9 | public String addBinary(String a, String b) { 10 | // 根据加法运算法则,我们需要从后先前遍历相加,注意起始下标位置 11 | int i = a.length() - 1, j = b.length() - 1, carry = 0; 12 | StringBuilder sb = new StringBuilder(); 13 | // 如果a,b各个位置上两数和加起来不为零或者i,j没有遍历到起始位置 14 | while(carry == 1 || i >= 0 || j >= 0) { 15 | // 只要为1就是有效相加位,记录下来 16 | if(i >= 0 && a.charAt(i--) == '1') { 17 | carry++; 18 | } 19 | if(j >= 0 && b.charAt(j--) == '1') { 20 | carry++; 21 | } 22 | // 辗转相除法取余数反向输出即为结果 23 | sb.append(carry % 2); 24 | carry /= 2; 25 | } 26 | return sb.reverse().toString(); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/math/AddStrings415.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.math; 2 | 3 | public class AddStrings415 { 4 | 5 | public static void main(String[] args) { 6 | System.out.println(new AddStrings415().addStrings("101", "50")); 7 | } 8 | 9 | public String addStrings(String num1, String num2) { 10 | int i = num1.length() - 1, j = num2.length() - 1, carry = 0; 11 | StringBuilder sb = new StringBuilder(); 12 | while(carry == 1 || i >= 0 || j >= 0) { 13 | // 只要没越界,就取每一位字符将其转换成整型再做加法运算 14 | int x = i < 0 ? 0 : num1.charAt(i--) - '0'; 15 | int y = j < 0 ? 0 : num2.charAt(j--) - '0'; 16 | // 10进制数以然可以用辗转相除法求结果 17 | sb.append((x + y + carry) % 10); 18 | carry = (x + y + carry) / 10; 19 | } 20 | return sb.reverse().toString(); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/math/CanWinNim292.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.math; 2 | 3 | public class CanWinNim292 { 4 | 5 | public boolean canWinNim(int n) { 6 | // 当前回合剩余的石头数量是四的倍数,这个人会输 7 | return n % 4 != 0; 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/math/ConvertToBase7_504.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.math; 2 | 3 | public class ConvertToBase7_504 { 4 | 5 | public static void main(String[] args) { 6 | System.out.println(new ConvertToBase7_504().convertToBase7(10)); 7 | } 8 | 9 | public String convertToBase7(int num) { 10 | if(num == 0) { 11 | return "0"; 12 | } 13 | // 不断地运算,进行字符串的拼接,这里用到StringBuilder 14 | StringBuilder sb = new StringBuilder(); 15 | // 如果是负数我们也先转换成正数方便计算,最后结果记得符号加回来就行 16 | boolean isNegative = num < 0; 17 | if(isNegative) { 18 | num = - num; 19 | } 20 | 21 | // 辗转相除法让十进制数转换成七进制 22 | while(num > 0) { 23 | sb.append(num % 7); 24 | num /= 7; 25 | } 26 | 27 | // 最后结果就是所有的余数反过来写 28 | String answer = sb.reverse().toString(); 29 | return isNegative ? "-" + answer : answer; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/math/ConvertToTitle168.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.math; 2 | 3 | public class ConvertToTitle168 { 4 | 5 | public static void main(String[] args) { 6 | System.out.println(new ConvertToTitle168().convertToTitle(10)); 7 | } 8 | 9 | public String convertToTitle(int columnNumber) { 10 | // // 递归的写法 11 | // if(columnNumber == 0) { 12 | // return ""; 13 | // } 14 | // // 这里做自减操作时因为我们拿的时现实生活中的 1-26 表示 A-Z 二十六个字母 15 | // // 而后面拼接的时候计算机不这么认为计算机认为你从 'A'开始拼接,当前下标时 16 | // // 从0开始的也就是 0-25 表示 A-Z,所以每一位数做26进制转换的时候,每次我们都 17 | // // 时错开了一位,那减回来就好了(由此可见计算机对0有执念啊,太爱0了) 18 | // columnNumber--; 19 | // return convertToTitle(columnNumber / 26) + (char) (columnNumber % 26 + 'A'); 20 | 21 | // 迭代写法 22 | if(columnNumber == 0) { 23 | return ""; 24 | } 25 | StringBuilder sb = new StringBuilder(); 26 | while(columnNumber > 0) { 27 | columnNumber--; 28 | sb.append((char) (columnNumber % 26 + 'A')); 29 | columnNumber /= 26; 30 | } 31 | return sb.reverse().toString(); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/math/CountPrimes204.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.math; 2 | 3 | public class CountPrimes204 { 4 | 5 | public static void main(String[] args) { 6 | System.out.println(new CountPrimes204().countPrimes(10)); 7 | } 8 | 9 | // // 方法一 枚举 加 优化时间复杂度为O(sqrt(n)); 10 | // public int countPrimes(int n) { 11 | // int answer = 0; 12 | // for(int i = 2; i < n; ++i) { 13 | // answer += isPrime(i) ? 1 : 0; 14 | // } 15 | // return answer; 16 | // } 17 | 18 | // private boolean isPrime(int x) { 19 | // for(int i = 2; i <= (int) Math.sqrt(x); ++i) { 20 | // if(x % i == 0) { 21 | // return false; 22 | // } 23 | // } 24 | // return true; 25 | // } 26 | 27 | // 方法二 埃拉托斯特尼筛法 28 | public int countPrimes(int n) { 29 | boolean[] notPrime = new boolean[n + 1]; 30 | int count = 0; 31 | for(int i = 2; i < n; ++i) { 32 | if(notPrime[i]) { 33 | continue; 34 | } 35 | count++; 36 | // 在每次找到一个素数时,将能被素数整除的数排除掉 37 | // 从 i * i 开始,因为如果 k < i,那么 k * i 在之前就已经被去除过了 38 | for(long j = (long) i * i; j < n; j += i) { 39 | notPrime[(int) j] = true; 40 | } 41 | } 42 | return count; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/math/IsPerfectSquare367.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.math; 2 | 3 | import javax.imageio.stream.ImageInputStream; 4 | 5 | public class IsPerfectSquare367 { 6 | 7 | public static void main(String[] args) { 8 | System.out.println(new IsPerfectSquare367().isPerfectSquare(16)); 9 | } 10 | 11 | public boolean isPerfectSquare(int num) { 12 | 13 | /* 14 | * 分析题意:本题有一个规律是我们之前做过的刷题挑战的第六十二题(leecode 279 题)中就做过了 15 | * 找到小于等于n的所有完全平方数,你会发现一个规律,从最小的1开始连续增加的完全平方数 16 | * 1,4,9,16,25...他们之间的间隔刚好是一个以1为初始值,差为2的等差数列,4 - 1 = 3, 17 | * 9 - 4 = 5,16 - 9 = 7...,1,3,5,7,9,11...这样一个等差数列,那么按照这个规律, 18 | * 我们从num开始做减等差数列的操作,如果是完全平方数最后一定能减到0,反之则不行. 19 | */ 20 | int sum = 1; 21 | while(num > 0) { 22 | num -= sum; 23 | sum += 2; 24 | } 25 | return num == 0; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/math/IsPowerOfThree326.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.math; 2 | 3 | public class IsPowerOfThree326 { 4 | 5 | public static void main(String[] args) { 6 | System.out.println(new IsPowerOfThree326().isPowerOfThree(27)); 7 | } 8 | 9 | public boolean isPowerOfThree(int n) { 10 | // // 方法一 3的幂,结合我们前面学习的进制转换本题是不是可以联想到三进制数的转换 11 | // if(n < 1) { 12 | // return false; 13 | // } 14 | 15 | // // 如果n可以拆分为x个3,那每一趟除以3的余数一定为0 16 | // while(n % 3 == 0) { 17 | // // 辗转相除法,逢三进一位,让中间结果不断减小 18 | // n /= 3; 19 | // } 20 | // // 如果是3的幂次除到最后肯定是 3 / 3 = 1,否则就返回false 21 | // return n == 1; 22 | 23 | // 方法二 整数限制,整型数最大范围有限制,我们通过找Integer.MAX_VALUE,得知最大的数能取到的 24 | // 3的幂次为 3^19 = 1162261467,而3又是质数,根据数学性质,3^19的除数包括了3^0 到 3^19,故 25 | // 只要n大于0并且 1162261467 % n == 0, 说明这个n一定在除数范围内,是3的幂数。 26 | return n > 0 && (1162261467 % n == 0); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/math/NumEquivDominoPairs1128.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.math; 2 | 3 | 4 | /** 5 | * description: 1128. 等价多米诺骨牌对的数量 二元数组,正反相等 6 | */ 7 | public class NumEquivDominoPairs1128 { 8 | 9 | public static void main(String[] args) { 10 | int[][] dominoes ={{1, 2}, {2, 1}, {3, 4}, {5, 6}}; 11 | System.out.println(numEquivDominoPairs(dominoes)); 12 | } 13 | 14 | public static int numEquivDominoPairs(int[][] dominoes) { 15 | // 既然是二元组,不妨把两个数拼成一个数做降维操作 16 | // 本题的核心思想需要比较和交换,让二元组中第一个数始终小于第二个数, 17 | // 然后让第一个数当十位,第二个数当个位,这样组成的一个两位数当作计数数组的下标即可 18 | // [a, b] => if (a < b) {a * 10 + b} else {b * 10 + a} 19 | int[] copy = new int[100]; 20 | int answer = 0; 21 | for(int[] arr : dominoes) { 22 | answer += arr[0] < arr[1] ? copy[arr[0] * 10 + arr[1]]++ : copy[arr[1] * 10 + arr[0]]++; 23 | } 24 | return answer; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/math/PrefixesDivBy5_1018.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.math; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * description: 1018 可被 5 整除的二进制前缀 位运算 + 数学分析 8 | */ 9 | public class PrefixesDivBy5_1018 { 10 | 11 | public static void main(String[] args) { 12 | int[] A = {1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1}; 13 | System.out.println(prefixesDivBy5(A)); 14 | } 15 | 16 | public static List prefixesDivBy5(int[] A) { 17 | /** 18 | * 思路,能被5整除的十进制数,任意一个数减去个位数的值后都是一个整数,整数都能被5整除 19 | * 所以本题十位数以上一定能被5整除不用考虑,只用考虑到每次得到的十进制数的个位能否被5 20 | * 整除即可 21 | **/ 22 | int size = A.length; 23 | int sum = 0; 24 | List result_list = new ArrayList(); 25 | // // 1.可读性高 方便理解版 26 | // for(int i = 0; i < size; ++i) { 27 | // sum <<= 1; // 每次上一个数做位运算左移一位变成下一个数的十位数 28 | // sum += A[i]; // 下一个数等于上一个十进制数加上当前最后一位二进制 29 | // // (最后一位二进制数无论是0或者1都和换算成十进制数值都一样所以可以直接加) 30 | // sum %= 10; // 取个位数 31 | // result_list.add(sum % 5 == 0); 32 | // } 33 | 34 | // 2.减少代码冗余版 根据数论中同余运算规则 sum %= 10 等价于 sum %= 5 35 | for(int item : A) { 36 | result_list.add( ( sum = ((sum << 1) + item) % 5 ) == 0); 37 | } 38 | 39 | return result_list; 40 | 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/math/Reverse7.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.math; 2 | 3 | public class Reverse7 { 4 | 5 | public int reverse(int x) { 6 | // // 方法一 转字符串后反向拼接 7 | // String x2s = String.valueOf(x); 8 | // char one = x2s.charAt(0); 9 | // boolean isNegative = false; 10 | // if(one == '-') { 11 | // isNegative = true; 12 | // x2s = x2s.substring(1); 13 | // } 14 | // StringBuilder sb = new StringBuilder(); 15 | // for(int i = x2s.length() - 1; i >= 0; --i) { 16 | // sb.append(x2s.charAt(i)); 17 | // } 18 | // try { 19 | // x = isNegative ? Integer.parseInt("-" + sb.toString()) : Integer.parseInt(sb.toString()); 20 | // return x; 21 | // } catch (Exception e) { 22 | // return 0; 23 | // } 24 | 25 | // 方法二 数学方法 辗转相除法倒置即可 26 | int reverseNum = 0; 27 | while(x != 0) { 28 | if(reverseNum > Integer.MAX_VALUE / 10 || reverseNum < Integer.MIN_VALUE / 10) { 29 | return 0; 30 | } 31 | reverseNum = reverseNum * 10 + x % 10; 32 | x /= 10; 33 | } 34 | return reverseNum; 35 | // return reverseNum > Integer.MAX_VALUE / 10 || reverseNum < Integer.MIN_VALUE / 10 ? 0 : reverseNum; 36 | 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/math/ToHex405.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.math; 2 | 3 | public class ToHex405 { 4 | 5 | public static void main(String[] args) { 6 | System.out.println(new ToHex405().toHex(10)); 7 | } 8 | 9 | public String toHex(int num) { 10 | char[] dictionary = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; 11 | if(num == 0) { 12 | return "0"; 13 | } 14 | StringBuilder sb =new StringBuilder(); 15 | while(num != 0) { 16 | // 这里与运算操作是在取二进制数每四位一保留对应十六进制表中的哪一个数 17 | // 二进制数每四位表示一位十六进制数2^4 = 2 * 2 * 2 * 2 = 16 18 | // 0b代表二进制数的意思,如果不好理解,这里 num & 0b1111 可以换成 19 | // num & 15 或者 num & 0xf 20 | // 都是一个意思代表十六进制字典表中的第十六个数f 21 | sb.append(dictionary[num & 0b1111]); 22 | // 这里做逻辑右移4位和上一题除以7同理,我们知道二进制数中每右移一位代 23 | // 表除2,那右移四位就是代表除16,循环的中间存储过程,辗转相除法的每一 24 | // 趟的结果 25 | num >>>= 4; 26 | } 27 | return sb.reverse().toString(); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/math/TrailingZeroes172.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.math; 2 | 3 | public class TrailingZeroes172 { 4 | 5 | public static void main(String[] args) { 6 | System.out.println(new TrailingZeroes172().trailingZeroes(50)); 7 | } 8 | 9 | public int trailingZeroes(int n) { 10 | /* 11 | * 分析题意:题目要求找到数n阶乘尾数开始有多少个零,我们分析下多个连续的数相乘,哪几个数能乘出零呢? 12 | * 我们不难发现 2 * 5 能产生尾数是零的数,或者2和5的倍数相乘也可以产生,所以把2*5或者2*5的倍数作为一 13 | * 个组合存在,我们就是找能组成多少对组合的个数就是零的个数,在小于等于n的阶乘中,2的倍数的个数肯定比 14 | * 5的倍数的个数多,所以能组成的个数等于数量少的那个数的个数,故这里我们找5和其倍数的个数就是题目要的 15 | * n的阶乘零的数量了。 16 | */ 17 | 18 | // // 递归的写法 19 | // return n == 0 ? 0 : n / 5 + trailingZeroes(n / 5); 20 | 21 | // 迭代的写法 22 | int count = 0; 23 | while(n / 5 != 0) { 24 | count += n / 5; 25 | n /= 5; 26 | } 27 | return count; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/math/WeChatRedPacket.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.math; 2 | 3 | import java.util.Random; 4 | import java.util.Scanner; 5 | 6 | public class WeChatRedPacket { 7 | 8 | public static void main(String[] args) { 9 | Scanner sc = new Scanner(System.in); 10 | System.out.print("请您输入总金额:"); 11 | double sum = sc.nextDouble(); 12 | System.out.print("请输入红包个数:"); 13 | int people = sc.nextInt(); 14 | System.out.println(); 15 | robRed(sum, people); 16 | } 17 | 18 | private static void robRed(double sum, int num) { 19 | double min = 0.01; // 最少抢到0.01 20 | Random rd = new Random(); // 定义随机数 21 | for (int i = 1; i < num; i++) { 22 | double max = Math.ceil(sum - min * (num - i)); //第 i 个人可以拿到的最大钱数 Math.ceil()向上取整 23 | double get = rd.nextInt((int) ((max - min) * 100)) / 100; 24 | // get 有可能是0,所以限制最小值为0.01 25 | double money = get + min; 26 | sum -= money; 27 | System.out.print("第 " + i + " 个人抢到 " + String.format("%.2f", money) + "元" + ", "); 28 | if (i % 8 == 0) { 29 | System.out.println(); 30 | } 31 | } 32 | System.out.print("第 " + num + " 个人抢到 " + String.format("%.2f", sum) + "元"); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/math/lastRemaining_swordOffer62.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.math; 2 | 3 | public class lastRemaining_swordOffer62 { 4 | 5 | public int lastRemaining(int n, int m) { 6 | /** 7 | * 分析题意:约瑟夫环,圆圈长度为 n 的解可以看成长度为 n-1 的解再加上报数的长度 m。 8 | * 因为是圆圈,所以最后需要对 n 取余。 9 | */ 10 | if(n == 1) return 0; 11 | return (lastRemaining(n - 1, m) + m) % n; 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/math/majorityElement_swordOffer39.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.math; 2 | 3 | public class majorityElement_swordOffer39 { 4 | 5 | public int majorityElement(int[] nums) { 6 | int majority = nums[0], cnt0 = 1; 7 | for(int i = 1; i < nums.length; ++i) { 8 | cnt0 = nums[i] == majority ? cnt0 + 1 : cnt0 - 1; 9 | if(cnt0 == 0) { 10 | majority = nums[i]; 11 | cnt0 = 1; 12 | } 13 | } 14 | int cnt1 = 0; 15 | for(int num : nums) { 16 | if(num == majority) { 17 | cnt1++; 18 | } 19 | } 20 | return cnt1 > nums.length / 2 ? majority : 0; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/other/printNumbers_swordOffer17.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.other; 2 | 3 | public class printNumbers_swordOffer17 { 4 | 5 | public int[] printNumbers(int n) { 6 | int end = (int) Math.pow(10, n) - 1; 7 | int[] res = new int[end]; 8 | for(int i = 0; i < end; ++i) { 9 | res[i] = i + 1; 10 | } 11 | return res; 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/stackandqueue/CQueue_swordOffer09.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.stackandqueue; 2 | 3 | import java.util.Stack; 4 | 5 | public class CQueue_swordOffer09 { 6 | 7 | Stack in; 8 | Stack out; 9 | public CQueue_swordOffer09() { 10 | in = new Stack<>(); 11 | out = new Stack<>(); 12 | } 13 | 14 | public void appendTail(int value) { 15 | in.push(value); 16 | } 17 | 18 | public int deleteHead() { 19 | if (out.isEmpty()) 20 | while (!in.isEmpty()) { 21 | out.push(in.pop()); 22 | } 23 | 24 | if (out.isEmpty()) { 25 | return -1; 26 | } 27 | return out.pop(); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/stackandqueue/IsValid20.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.stackandqueue; 2 | 3 | import java.util.Stack; 4 | 5 | public class IsValid20 { 6 | 7 | public boolean isValid(String s) { 8 | 9 | /* 10 | * 分析题意:如果题目给的测试用例都是严格成对左右闭合出现,或者是由内向外闭合的,那我们就返回true,怎样的结构 11 | * 能正好同时满足这俩种情况呢,欸,这个时候我们想到了栈,我们遍历s字符换种的每一个字符,每次只让栈能push进 12 | * 左括号,然后让下一个最先遍历出现的右括号字符和当前栈顶的弹出做一个闭合并且类型一致的匹配,如果最后能完全匹配 13 | * 成功,那栈肯定是清空了,但是中途如果出现任意一个左括号和右括号闭合且类型一致匹配失败,都得返回false结束方法 14 | * 反之如果先出现了右括号,那栈肯定一开始就是空的了,也返回false。 15 | */ 16 | 17 | Stack stack = new Stack<>(); 18 | for(char c : s.toCharArray()) { 19 | if(c == '(' || c == '[' || c == '{') { 20 | stack.push(c); 21 | } else { 22 | if(stack.isEmpty()) { 23 | return false; 24 | } 25 | char cStack = stack.pop(); 26 | boolean b1 = c == ')' && cStack != '('; 27 | boolean b2 = c == ']' && cStack != '['; 28 | boolean b3 = c == '}' && cStack != '{'; 29 | 30 | if(b1 || b2 || b3) { 31 | return false; 32 | } 33 | } 34 | } 35 | 36 | return stack.isEmpty(); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/stackandqueue/MinStack155.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.stackandqueue; 2 | 3 | import java.util.Stack; 4 | 5 | public class MinStack155 { 6 | 7 | private Stack dataStack; 8 | private Stack minStack; 9 | private int min; 10 | 11 | /* 12 | * 分析题意:本题既要保证出栈的时候,栈顶元素保持原来后进先出的相对位置不变,又要保证随时能拿当前栈中剩下元素 13 | * 中的最小值,故我们至少需要用两个栈来实现题意的要求,一个栈dataStack正常存取元素即可,另一个栈minStack的 14 | * 元素数量和dataStack的相同,但是相对位置上存储的是dataStack栈剩下所有元素中的最小值。即可满足题意。 15 | */ 16 | 17 | /** initialize your data structure here. */ 18 | public MinStack155() { 19 | dataStack = new Stack<>(); 20 | minStack = new Stack<>(); 21 | min = Integer.MAX_VALUE; 22 | } 23 | 24 | public void push(int val) { 25 | dataStack.push(val); 26 | min = Math.min(min, val); 27 | minStack.push(min); 28 | } 29 | 30 | public void pop() { 31 | dataStack.pop(); 32 | minStack.pop(); 33 | min = minStack.isEmpty() ? Integer.MAX_VALUE : minStack.peek(); 34 | } 35 | 36 | public int top() { 37 | return dataStack.peek(); 38 | } 39 | 40 | public int getMin() { 41 | return minStack.peek(); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/stackandqueue/MinStack_swordOffer30.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.stackandqueue; 2 | 3 | import java.util.Stack; 4 | 5 | public class MinStack_swordOffer30 { 6 | 7 | Stack outStack; 8 | Stack minStack; 9 | /** initialize your data structure here. */ 10 | public MinStack_swordOffer30() { 11 | outStack = new Stack<>(); 12 | minStack = new Stack<>(); 13 | } 14 | 15 | public void push(int x) { 16 | outStack.push(x); 17 | minStack.push(minStack.isEmpty() ? x : Math.min(minStack.peek(), x)); 18 | } 19 | 20 | public void pop() { 21 | outStack.pop(); 22 | minStack.pop(); 23 | } 24 | 25 | public int top() { 26 | return outStack.peek(); 27 | } 28 | 29 | public int min() { 30 | return minStack.peek(); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/stackandqueue/MyQueue232.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.stackandqueue; 2 | 3 | import java.util.Stack; 4 | 5 | /** 6 | * 用栈实现队列 7 | */ 8 | public class MyQueue232 { 9 | 10 | private Stack in; 11 | private Stack out; 12 | /* 13 | * 解题思路:栈的顺序为后进先出,而队列的顺序为先进先出。使用两个栈实现队列,一个元素需要经过两个栈才能出队 * 列,在经过第一个栈时元素顺序被反转,经过第二个栈时再次被反转,此时就是先进先出顺序。 14 | */ 15 | 16 | /** Initialize your data structure here. */ 17 | public MyQueue232() { 18 | in = new Stack<>(); 19 | out = new Stack<>(); 20 | } 21 | 22 | /** Push element x to the back of queue. */ 23 | public void push(int x) { 24 | in.push(x); 25 | } 26 | 27 | /** Removes the element from in front of queue and returns that element. */ 28 | public int pop() { 29 | in2out(); 30 | return out.pop(); 31 | } 32 | 33 | /** Get the front element. */ 34 | public int peek() { 35 | in2out(); 36 | return out.peek(); 37 | } 38 | 39 | /** Returns whether the queue is empty. */ 40 | public boolean empty() { 41 | return in.isEmpty() && out.isEmpty(); 42 | } 43 | 44 | private void in2out() { 45 | while(out.isEmpty()) { 46 | while(!in.isEmpty()) { 47 | out.push(in.pop()); 48 | } 49 | } 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/stackandqueue/MyStack225.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.stackandqueue; 2 | 3 | import java.util.LinkedList; 4 | import java.util.Queue; 5 | 6 | /** 7 | * 用队列实现栈 8 | */ 9 | public class MyStack225 { 10 | 11 | private Queue queue; 12 | 13 | /* 14 | * 分析题意:在将一个元素 x 插入队列时,为了维护原来的后进先出顺序,需要让 x 插入队列首部。而队列的默认 15 | * 插入顺序是队列尾部,因此在将 x 插入队列尾部之后,需要让除了 x 之外的所有元素出队列,再入队列。 16 | */ 17 | 18 | /** Initialize your data structure here. */ 19 | public MyStack225() { 20 | queue = new LinkedList<>(); 21 | } 22 | 23 | /** Push element x onto stack. */ 24 | public void push(int x) { 25 | queue.add(x); 26 | int cnt = queue.size(); 27 | while(cnt-- > 1) { 28 | queue.add(queue.poll()); 29 | } 30 | } 31 | 32 | /** Removes the element on top of the stack and returns that element. */ 33 | public int pop() { 34 | return queue.poll(); 35 | } 36 | 37 | /** Get the top element. */ 38 | public int top() { 39 | return queue.peek(); 40 | } 41 | 42 | /** Returns whether the stack is empty. */ 43 | public boolean empty() { 44 | return queue.isEmpty(); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/stackandqueue/getLeastNumbers_swordOffer40.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.stackandqueue; 2 | 3 | import java.util.PriorityQueue; 4 | 5 | public class getLeastNumbers_swordOffer40 { 6 | 7 | public int[] getLeastNumbers(int[] arr, int k) { 8 | if(k > arr.length || k <= 0) { 9 | return new int[0]; 10 | } 11 | // 用Lambda表达式把默认小顶堆换成大顶堆 12 | PriorityQueue maxHeap = new PriorityQueue<>((o1, o2) -> o2 - o1); 13 | for(int num : arr) { 14 | maxHeap.add(num); 15 | if(maxHeap.size() > k) { 16 | maxHeap.poll(); 17 | } 18 | } 19 | int[] result = new int[maxHeap.size()]; 20 | int index = 0; 21 | for(int num : maxHeap) { 22 | result[index++] = num; 23 | } 24 | return result; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/string/BalancedStringSplit1221.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.string; 2 | 3 | public class BalancedStringSplit1221 { 4 | 5 | public int balancedStringSplit(String s) { 6 | int res = 0, cnt = 0; 7 | for(int i = 0; i < s.length(); ++i) { 8 | cnt += s.charAt(i) == 'L' ? 1 : -1; 9 | if(cnt == 0) { 10 | res++; 11 | } 12 | } 13 | return res; 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/string/CountBinarySubstrings696.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.string; 2 | 3 | public class CountBinarySubstrings696 { 4 | 5 | public int countBinarySubstrings(String s) { 6 | 7 | /* 8 | * 分析题意:题目要求在一个二进制串s中找到相同数量的0和1的子串的个数,并且这些子串中所有0和1都是连续的 9 | * 后面出现和前面相同长度相同格式的重复子串也记一次次数。那么我们可以用双变量遍历s串完成统计,一个变量 10 | * preLen统计当前具有相同数量0或1的字符子串的前序相同规律的字符子串的个数,curLen统计当前具有相同数量 11 | * 0或1的字符子串个数,然后不停向后滑动,保证两个变量存储的是当前同数子串个数和前序同数子串个数。每统计 12 | * 完一个同数子串个数就去判断比较如果当前串长度还没有比前序串长度大,那就说明还能凑出一个子串就记一次次 13 | * 数,最后遍历完毕,返回count即可。 14 | */ 15 | 16 | int preLen = 0, curLen = 1, count = 0; 17 | for(int i = 1; i < s.length(); ++i) { 18 | if(s.charAt(i) == s.charAt(i - 1)) { 19 | curLen++; 20 | } else { 21 | preLen = curLen; 22 | curLen = 1; 23 | } 24 | 25 | if(preLen >= curLen) { 26 | count++; 27 | } 28 | } 29 | return count; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/string/IsIsomorphic205.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.string; 2 | 3 | public class IsIsomorphic205 { 4 | 5 | public boolean isIsomorphic(String s, String t) { 6 | /* 7 | * 分析题意:题目要求找到所有对应位置的映射关系都要一一对应,不能一对多或者多对一,故我们只需要同步遍历 8 | * 两个字符串对应位置字符,给他们打上唯一的关联标识,并且标识还不能重复,才能保证唯一性。故我们可以用类 9 | * 似于时间戳的增量下标i作为标识,保证了串行序列化,本题就可以求解了。需要注意的是之前出现过的对应关联的 10 | * 两个对应字符,如果在后面遍历时再一次出现时,在比较完他们上一次关联时对应标识相同的情况下,我们要再一次 11 | * 更新其标识为这次遍历时的当前位置,保证永远都是和上次出现的位置标识递进的更新。 12 | * 我们可以用两个数组同步遍历其相对同步位置上的字符ASCII码作为索引下标,判断对应的值上他们两上一次出现 13 | * 时的位置下标(此下标是从两字符串起始位置0开始同步向后遍历的下标,而非上面说的两个数组的下标)是否相同, 14 | * 这里我们用for循环的不重复增量次数i(时间戳)作为下标,如果匹配成功则更新其下标为本次最新关联位置的下标。 15 | * 即可满足题意。 16 | */ 17 | int[] preIndexOfS = new int[128]; 18 | int[] preIndexOfT = new int[128]; 19 | 20 | for(int i = 0; i < s.length(); ++i) { 21 | char sc = s.charAt(i), tc = t.charAt(i); 22 | if(preIndexOfS[sc] != preIndexOfT[tc]) { 23 | return false; 24 | } 25 | preIndexOfS[sc] = i + 1; 26 | preIndexOfT[tc] = i + 1; 27 | } 28 | return true; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/string/IsPalindrome9.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.string; 2 | 3 | public class IsPalindrome9 { 4 | 5 | public boolean isPalindrome(int x) { 6 | /* 7 | * 分析题意:题目要求我们不能转换成字符串比对,也就是说不能开辟额外的空间,那这个时候我们就只能把整型数字 8 | * 对半拆分,然后倒置右半部分的数字,最后判断右半部分和左半部分数是否值相等就行了。 9 | * 在拆分的过程中同样需要考虑回文数的两种情况 10 | * 当回文数为偶数个长度时,左半部分等于右半部分倒置,返回true 11 | * 当回文数为奇数个长度时,此时while循环结束右半部分长度比左半部分多了正中间的那一位,也就是说把右半部分再 12 | * 除以10去掉最后一个位数,然后比较值相等返回true即可 13 | */ 14 | if(x == 0) { 15 | return true; 16 | } 17 | if(x < 0 || x % 10 == 0) { 18 | return false; 19 | } 20 | int right = 0; 21 | while(x > right) { 22 | right = right * 10 + x % 10; 23 | x /= 10; 24 | } 25 | return x == right || x == right / 10; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/string/LengthOfLastWord58.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.string; 2 | 3 | public class LengthOfLastWord58 { 4 | 5 | public int lengthOfLastWord(String s) { 6 | // // 方法一 标记清楚空格法 7 | // boolean lastOne = false; 8 | // int cnt = 0; 9 | // for(int i = s.length() - 1; i >= 0; --i) { 10 | // if(s.charAt(i) == ' ' && !lastOne) { 11 | // continue; 12 | // } 13 | // if(lastOne && s.charAt(i) == ' ') { 14 | // break; 15 | // } 16 | // lastOne = true; 17 | // cnt++; 18 | // } 19 | // return cnt; 20 | 21 | // 方法二 使用trim()去掉首尾空格直接反向遍历到第一个中间的空格结束 22 | s = s.trim(); 23 | int cnt = 0; 24 | for(int i = s.length() - 1; i >= 0; --i) { 25 | if(s.charAt(i) == ' ') { 26 | break; 27 | } 28 | cnt++; 29 | } 30 | return cnt; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/string/RomanToInt13.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.string; 2 | 3 | public class RomanToInt13 { 4 | 5 | public int romanToInt(String s) { 6 | 7 | // 分析题意:hash表映射,遍历字符串,如果下一个字符比当前大,说明 8 | // 是减法,反之做加法。 9 | Map symbolValues = new HashMap<>(); 10 | symbolValues.put('I', 1); 11 | symbolValues.put('V', 5); 12 | symbolValues.put('X', 10); 13 | symbolValues.put('L', 50); 14 | symbolValues.put('C', 100); 15 | symbolValues.put('D', 500); 16 | symbolValues.put('M', 1000); 17 | 18 | int n = s.length(), res = 0; 19 | for(int i = 0; i < s.length(); ++i) { 20 | if(i < n - 1 && symbolValues.get(s.charAt(i)) < symbolValues.get(s.charAt(i + 1))) { 21 | res -= symbolValues.get(s.charAt(i)); 22 | } else { 23 | res += symbolValues.get(s.charAt(i)); 24 | } 25 | } 26 | return res; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/string/StrStr28.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.string; 2 | 3 | public class StrStr28 { 4 | 5 | public int strStr(String haystack, String needle) { 6 | // // 方法一 调包侠 真香 7 | // return haystack.indexOf(needle); 8 | // 双指针暴力匹配 9 | int m = haystack.length(), n = needle.length(); 10 | if(needle == null || n == 0) return 0; 11 | int a = 0; 12 | for(int i = 0; i <= m - n; ++i) { 13 | a = i; 14 | int b = 0; 15 | while(b < n && haystack.charAt(a) == needle.charAt(b)) { 16 | a++; 17 | b++; 18 | } 19 | if(b == n) { 20 | return i; 21 | } 22 | } 23 | return -1; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/string/reverseRightWords149.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.string; 2 | 3 | 4 | public class reverseRightWords149 { 5 | 6 | public static void main(String[] args) { 7 | /** 8 | * 编程之美2.17(204页)数组循环位移改编成字符串循环位移, 9 | * 给一个字符串s,位移位数k,让你把字符串向右循环位移k位后 10 | * 输出结果 11 | */ 12 | String s = "abcd123"; 13 | int k = 3; 14 | 15 | // 方法一 字符串切片 16 | // s = s.substring(s.length() - k, s.length()) + s.substring(0, s.length() - k); 17 | // System.out.println("方法一:" + s); 18 | 19 | // 方法二 列表遍历拼接 20 | // StringBuilder sb = new StringBuilder(); 21 | // for(int i = s.length() - k; i < s.length(); ++i) { 22 | // sb.append(s.charAt(i)); 23 | // } 24 | // for(int j = 0; j < s.length() - k; ++j) { 25 | // sb.append(s.charAt(j)); 26 | // } 27 | // s = sb.toString(); 28 | // System.out.println("方法二:" + s); 29 | 30 | // 方法二优化版 列表遍历拼接 31 | StringBuilder sb = new StringBuilder(); 32 | for(int i = s.length() - k; i < s.length() + s.length() - k; ++i) { 33 | sb.append(s.charAt(i % s.length())); 34 | } 35 | s = sb.toString(); 36 | System.out.println("方法二优化版:" + s); 37 | } 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/tree/AverageOfLevels637.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.tree; 2 | 3 | import java.util.*; 4 | 5 | public class AverageOfLevels637 { 6 | 7 | public List averageOfLevels(TreeNode root) { 8 | List avgResult = new ArrayList<>(); 9 | if(root == null) return avgResult; 10 | // 根据队列先进先出的特点达到层序遍历的特点,并且至上而下从左向右 11 | Queue queue = new LinkedList<>(); 12 | // 第一层是根节点 13 | queue.offer(root); 14 | while(!queue.isEmpty()) { 15 | int size = queue.size(); 16 | // 下面size要做减法,但是我们这里每一层个数要临时存储方便一会做求每一层平均值的除数 17 | int tempSize = size; 18 | // 累加每一层节点个数,当被除数 19 | double sum = 0; 20 | // 只要当前层节点还没取完就不停的从队头取节点 21 | while(size > 0) { 22 | TreeNode node = queue.poll(); 23 | sum += node.val; 24 | if(node.left != null) queue.offer(node.left); 25 | if(node.right != null) queue.offer(node.right); 26 | size--; 27 | } 28 | // System.out.println(sum); 29 | double tempAvgOfLevel = sum / tempSize; 30 | avgResult.add(tempAvgOfLevel); 31 | } 32 | return avgResult; 33 | 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/tree/DiameterOfBinaryTree543.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.tree; 2 | 3 | public class DiameterOfBinaryTree543 { 4 | 5 | private int max = 0; 6 | 7 | public int diameterOfBinaryTree(TreeNode root) { 8 | depth(root); 9 | return max; 10 | } 11 | 12 | // 根据递归思想,算出每个节点作为根节点其左右子树的最深距离,然后相加找最大值 13 | private int depth(TreeNode root) { 14 | if(root == null) { 15 | return 0; 16 | } 17 | int l = depth(root.left); 18 | int r = depth(root.right); 19 | // 已存在最大长度,和新的对比,新的更大则替换,否则不变 20 | max = Math.max(max, l + r); 21 | // 找到每一层递归的最大局部深度 22 | return 1 + Math.max(l, r); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/tree/FindMode501.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.tree; 2 | 3 | import java.util.*; 4 | 5 | public class FindMode501 { 6 | 7 | private int curCnt = 1; 8 | private int maxCnt = 1; 9 | private TreeNode preNode = null; 10 | 11 | public int[] findMode(TreeNode root) { 12 | List maxCntNums = new ArrayList<>(); 13 | inOrder(root, maxCntNums); 14 | int[] result = new int[maxCntNums.size()]; 15 | int idx = 0; 16 | for(int num : maxCntNums) { 17 | result[idx++] = num; 18 | } 19 | return result; 20 | } 21 | 22 | private void inOrder(TreeNode node, List nums) { 23 | if(node == null) return; 24 | inOrder(node.left, nums); 25 | if(preNode != null) { 26 | if(preNode.val == node.val) { 27 | curCnt++; 28 | } else { 29 | curCnt = 1; 30 | } 31 | } 32 | if(curCnt > maxCnt) { 33 | maxCnt = curCnt; 34 | nums.clear(); 35 | nums.add(node.val); 36 | } else if(curCnt == maxCnt) { 37 | nums.add(node.val); 38 | } 39 | preNode = node; 40 | inOrder(node.right, nums); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/tree/FindTarget653.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.tree; 2 | 3 | import java.util.*; 4 | 5 | public class FindTarget653 { 6 | 7 | public boolean findTarget(TreeNode root, int k) { 8 | List nums = new ArrayList<>(); 9 | inOrder(root, nums); 10 | int s = 0, h = nums.size() - 1; 11 | while(s < h) { 12 | int sum = nums.get(s) + nums.get(h); 13 | if(sum == k) { 14 | return true; 15 | } else if(sum < k) { 16 | s++; 17 | } else { 18 | h--; 19 | } 20 | } 21 | return false; 22 | } 23 | 24 | private void inOrder(TreeNode root, List nums) { 25 | if(root == null) return; 26 | inOrder(root.left, nums); 27 | nums.add(root.val); 28 | inOrder(root.right, nums); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/tree/FindTilt563.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.tree; 2 | 3 | public class FindTilt563 { 4 | 5 | int total = 0; 6 | public int findTilt(TreeNode root) { 7 | dfs(root); 8 | return total; 9 | } 10 | 11 | private int dfs(TreeNode node) { 12 | if(node == null) { 13 | return 0; 14 | } 15 | int left = dfs(node.left); 16 | int right = dfs(node.right); 17 | total += Math.abs(left - right); 18 | return right + left + node.val; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/tree/GetMinimumDifference530.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.tree; 2 | 3 | public class GetMinimumDifference530 { 4 | 5 | private int minDifference = Integer.MAX_VALUE; 6 | private TreeNode preNode = null; 7 | 8 | public int getMinimumDifference(TreeNode root) { 9 | inOrder(root); 10 | return minDifference; 11 | } 12 | 13 | private void inOrder(TreeNode node) { 14 | if(node == null) return; 15 | inOrder(node.left); 16 | if(preNode != null) minDifference = Math.min(minDifference, node.val - preNode.val); 17 | preNode = node; 18 | inOrder(node.right); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/tree/HasPathSum112.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.tree; 2 | 3 | public class HasPathSum112 { 4 | 5 | public boolean hasPathSum(TreeNode root, int targetSum) { 6 | if(root == null) return false; 7 | // 如果一条到叶子节点的路径最后能让val == targetSum 则表示刚好等于目标和整数 8 | if(root.left == null && root.right == null && root.val == targetSum) return true; 9 | // 否则没到叶子节点就左右继续递归就行了 10 | return hasPathSum(root.left, targetSum - root.val) || hasPathSum(root.right, targetSum - root.val); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/tree/InvertTree226.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.tree; 2 | 3 | public class InvertTree226 { 4 | 5 | public TreeNode invertTree(TreeNode root) { 6 | /* 7 | * 分析题意:一句话解释,让当前节点左儿子等于右儿子,右儿子等于左儿子,递归完事了。没错! 8 | * 这就是一个简单的swap,两数交换位子。 9 | */ 10 | if(root == null) { 11 | return null; 12 | } 13 | TreeNode temp = root.left; 14 | root.left = root.right; 15 | root.right = temp; 16 | invertTree(root.left); 17 | invertTree(root.right); 18 | return root; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/tree/IsBalanced110.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.tree; 2 | 3 | public class IsBalanced110 { 4 | 5 | private boolean result = true; 6 | 7 | public boolean isBalanced(TreeNode root) { 8 | /* 9 | * 分析题意:要判断一棵树是不是平衡二叉树,关键在于判断一个二叉树每个节点的左右两个子树的高度差是否大于1, 10 | * 大于1了就不是平衡二叉树,那说到高度差我们肯定想到了上一个题目求树的高度,我们把所有不为空的节点当作子 11 | * 问题的根节点算他们左右子树的高然后做差判断是否大于1,既最容易想到的方法就是dfs的递归求解,最后结果要求 12 | * 返回boolean类型,故我们还需要申明一个变量作为返回结果,这里我封装了递归的方法,所以result需要写共享的 13 | * 全局变量方便修改。 14 | */ 15 | 16 | // 以当前根节点开始,算出它的左子树右子树高度差,先递归到底,然后反过来自下而上求每颗子数左右子树高度差 17 | maxDepth(root); 18 | return result; 19 | } 20 | 21 | private int maxDepth(TreeNode root) { 22 | // 如果当前节点作为根节点为空了,就说明当前路径到底了该返回了或者是当前已经非平衡了,无需继续dfs 23 | if(root == null || !result) { 24 | return 0; 25 | } 26 | // 找到当前节点为根节点时其左子树的高度 27 | int l = maxDepth(root.left); 28 | // 同理找到其右子树的深度 29 | int r = maxDepth(root.right); 30 | // 按照平衡二叉树的定义,左右子数深度差如果大于1了就是非平衡,result要改成false 31 | if(Math.abs(l - r) > 1) { 32 | result = false; 33 | } 34 | // 因为我们要求当前节点作为根节点时的最大深度,即它左右子数哪个更深我们就取哪个, 35 | // 然后高度加1结果传递回上一层,上一层合并以此类推回去 36 | return 1 + Math.max(l, r); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/tree/IsSameTree100.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.tree; 2 | 3 | public class IsSameTree100 { 4 | 5 | public boolean isSameTree(TreeNode p, TreeNode q) { 6 | if(p == null && q == null) { 7 | return true; 8 | } else if(p == null || q == null) { 9 | return false; 10 | } else if(p.val != q.val) { 11 | return false; 12 | } else { 13 | return isSameTree(p.left, q.left) && isSameTree(p.right, q.right); 14 | } 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/tree/IsSubtree572.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.tree; 2 | 3 | public class IsSubtree572 { 4 | 5 | public boolean isSubtree(TreeNode s, TreeNode t) { 6 | if(s == null) return false; 7 | // s是自身的一颗子树等于t或者递归根节点的左子数去找子树等于t或者递归根节点的右子数去找子树等于t 8 | return isSubtreeWithRoot(s, t) || isSubtree(s.left, t) || isSubtree(s.right, t); 9 | } 10 | 11 | private boolean isSubtreeWithRoot(TreeNode s, TreeNode t) { 12 | // 子树匹配查找过程中s中当前节点位置应该和t中当前节点位置是同步的并且相对位置相同 13 | // 那么如果s和t同时为空了,说明正好至上向下到叶子节点之后完全匹配返回true 14 | if(s == null && t == null) return true; 15 | // 两者其中一个先到叶子节点之后不同步结束则匹配失败返回false 16 | if(s == null || t == null) return false; 17 | // 匹配过程中任意一个节点里的值不相等也匹配失败返回false 18 | if(s.val != t.val) return false; 19 | // 如果当前节点匹配成功,继续向下递归他的左右子数节点,同步进行,左右子数有一个节点位置匹配失败 20 | // 在and条件下最终返回的结果都是false 21 | return isSubtreeWithRoot(s.left, t.left) && isSubtreeWithRoot(s.right, t.right); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/tree/IsSymmetric101.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.tree; 2 | 3 | public class IsSymmetric101 { 4 | 5 | public boolean isSymmetric(TreeNode root) { 6 | if(root == null) return true; 7 | // 如果非空就从根节点按照镜像对称递归按层比较左右子树 8 | return isSymmetric1(root.left, root.right); 9 | } 10 | 11 | private boolean isSymmetric1(TreeNode t1, TreeNode t2) { 12 | if(t1 == null && t2 == null) return true; 13 | if(t1 == null || t2 == null) return false; 14 | if(t1.val != t2.val) return false; 15 | return isSymmetric1(t1.left, t2. right) && isSymmetric1(t1.right, t2.left); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/tree/LowestCommonAncestor235.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.tree; 2 | 3 | public class LowestCommonAncestor235 { 4 | 5 | public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { 6 | if(root.val > p.val && root.val > q.val) return lowestCommonAncestor(root.left, p, q); 7 | if(root.val < p.val && root.val < q.val) return lowestCommonAncestor(root.right, p, q); 8 | return root; 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/tree/MaxDepth104.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.tree; 2 | 3 | import java.util.LinkedList; 4 | import java.util.Queue; 5 | 6 | public class MaxDepth104 { 7 | 8 | public int maxDepth(TreeNode root) { 9 | // // 方法一 dfs的递归求数高度 10 | // return root == null ? 0 : Math.max(maxDepth(root.left), maxDepth(root.right)) + 1; 11 | 12 | // 方法二 bfs在队列中按层去弹出和加入每层的叶子节点 13 | if(root == null) { 14 | return 0; 15 | } 16 | Queue queue = new LinkedList<>(); 17 | queue.offer(root); 18 | int answer = 0; 19 | while(!queue.isEmpty()) { 20 | int size = queue.size(); 21 | while(size > 0) { 22 | TreeNode node = queue.poll(); 23 | if(node.left != null) { 24 | queue.offer(node.left); 25 | } 26 | if(node.right != null) { 27 | queue.offer(node.right); 28 | } 29 | size--; 30 | } 31 | answer++; 32 | } 33 | return answer; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/tree/MergeTrees617.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.tree; 2 | 3 | public class MergeTrees617 { 4 | 5 | public TreeNode mergeTrees(TreeNode root1, TreeNode root2) { 6 | if(root1 == null && root2 == null) return null; 7 | if(root1 == null) return root2; 8 | if(root2 == null) return root1; 9 | 10 | // 如果对应相同位置的节点都有值就相加 11 | TreeNode root = new TreeNode(root1.val + root2.val); 12 | root.left = mergeTrees(root1.left, root2.left); 13 | root.right = mergeTrees(root1.right, root2.right); 14 | return root; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/tree/MinDepth111.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.tree; 2 | 3 | public class MinDepth111 { 4 | 5 | public int minDepth(TreeNode root) { 6 | if(root == null) return 0; 7 | // 非空就左右子树都递归,到底就收敛回来 8 | int left = minDepth(root.left); 9 | int right = minDepth(root.right); 10 | // 任意一个节点先到叶子节点后就开始反向收敛做节点的记录 11 | if(left == 0 || right == 0) return left + right + 1; 12 | // 如果最后收敛完毕了那就比较根节点到左右子数叶子节点的路径长度,取最小的那一个 13 | return Math.min(left, right) + 1; 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/tree/PathSum3_437.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.tree; 2 | 3 | public class PathSum3_437 { 4 | 5 | public int pathSum(TreeNode root, int targetSum) { 6 | if(root == null) return 0; 7 | // 从根节点开始向下找或者从其他非根节点开始向下找 8 | int result = pathSumStartWithRoot(root, targetSum) + pathSum(root.left, targetSum) + pathSum(root.right, targetSum); 9 | return result; 10 | } 11 | 12 | private int pathSumStartWithRoot(TreeNode root, int targetSum) { 13 | if(root == null) return 0; 14 | // 以当前节点作为起始点让路径和先归0,重新开始向下找路径和是否与targetSum匹配 15 | int result = 0; 16 | if(root.val == targetSum) result++; 17 | // 探索当前节点向下所有左右子树,targetSum与对应值做差,递归到二者相等记录找到一条路径,探底后马上收敛 18 | result += pathSumStartWithRoot(root.left, targetSum - root.val) + pathSumStartWithRoot(root.right, targetSum - root.val); 19 | return result; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/tree/SortedArrayToBST108.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.tree; 2 | 3 | public class SortedArrayToBST108 { 4 | 5 | public TreeNode sortedArrayToBST(int[] nums) { 6 | /* 7 | * 分析题意:本题逆向分析下,我们要构建一个二叉搜索树,既然给你一个有序的升序序列,那我们回想一下 8 | * 二叉搜索树的中序遍历不就是一个升序序列吗,左-根-右,那这个中序遍历序列最中间的值满足左右等分该 9 | * 值左右两边的值,然后我们至顶向下,让当前中间节点作为其左右子树的根节点,左右两边继续二分等分递归 10 | * 下去,即为我们想要的结果(就是二叉搜索树中序遍历的一个逆过程分析,自底向上反过来自顶向下) 11 | */ 12 | return nums == null ? null : buildTree(nums, 0, nums.length - 1); 13 | } 14 | 15 | private TreeNode buildTree(int[] nums, int l, int h) { 16 | if(l > h) return null; 17 | int mid = l + (h - l) / 2; 18 | TreeNode root = new TreeNode(nums[mid]); 19 | root.left = buildTree(nums, l, mid - 1); 20 | root.right = buildTree(nums, mid + 1, h); 21 | return root; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/tree/SumOfLeftLeaves404.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.tree; 2 | 3 | public class SumOfLeftLeaves404 { 4 | 5 | public int sumOfLeftLeaves(TreeNode root) { 6 | if(root == null) return 0; 7 | // 当前节点左子数如果是叶子节点那就把该左叶子节点的值加上右子树继续递归找右子树可能的左叶子节点值 8 | if(isLeaf(root.left)) return root.left.val + sumOfLeftLeaves(root.right); 9 | // 当前节点左子树不是叶子节点就继续其左右子树找到可能的左叶子节点就把其节点值加起来 10 | return sumOfLeftLeaves(root.left) + sumOfLeftLeaves(root.right); 11 | } 12 | 13 | private boolean isLeaf(TreeNode node) { 14 | if(node == null) return false; 15 | return node.left == null && node.right == null; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/tree/TreeNode.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.tree; 2 | 3 | public class TreeNode { 4 | int val; 5 | TreeNode left; 6 | TreeNode right; 7 | TreeNode() {} 8 | TreeNode(int val) { this.val = val; } 9 | TreeNode(int val, TreeNode left, TreeNode right) { 10 | this.val = val; 11 | this.left = left; 12 | this.right = right; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/tree/isSymmetric_swordOffer28.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.tree; 2 | 3 | public class isSymmetric_swordOffer28 { 4 | 5 | public boolean isSymmetric(TreeNode root) { 6 | return root == null ? true : recur(root.left, root.right); 7 | } 8 | 9 | private boolean recur(TreeNode L, TreeNode R) { 10 | if(L == null && R == null) return true; 11 | if(L == null || R == null || L.val != R.val) return false; 12 | return recur(L.left, R.right) && recur(L.right, R.left); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/tree/kthLargest_swordOffer54.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.tree; 2 | 3 | public class kthLargest_swordOffer54 { 4 | 5 | private int cnt = 0; 6 | private int ret; 7 | public int kthLargest(TreeNode root, int k) { 8 | inOrder(root, k); 9 | return ret; 10 | } 11 | 12 | private void inOrder(TreeNode node, int k) { 13 | if(node == null || cnt >= k) return; 14 | inOrder(node.right, k); 15 | cnt++; 16 | if(cnt == k) { 17 | ret = node.val; 18 | } 19 | inOrder(node.left, k); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/tree/levelOrder_swordOffer32_2.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.tree; 2 | 3 | public class levelOrder_swordOffer32_2 { 4 | 5 | public List> levelOrder(TreeNode root) { 6 | Queue queue = new LinkedList<>(); 7 | List> ret_list = new ArrayList<>(); 8 | queue.add(root); 9 | while(!queue.isEmpty()) { 10 | List list = new ArrayList<>(); 11 | int cnt = queue.size(); 12 | while(cnt-- > 0) { 13 | TreeNode t = queue.poll(); 14 | if(t == null) continue; 15 | list.add(t.val); 16 | queue.add(t.left); 17 | queue.add(t.right); 18 | } 19 | if(!list.isEmpty()) { 20 | ret_list.add(list); 21 | } 22 | } 23 | return ret_list; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/tree/maxDepth_swordOffer55.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.tree; 2 | 3 | public class maxDepth_swordOffer55 { 4 | 5 | public int maxDepth(TreeNode root) { 6 | // // 方法一 dfs 7 | // return root == null ? 0 : 1 + Math.max(maxDepth(root.left), maxDepth(root.right)); 8 | 9 | // 方法二 bfs 10 | if(root == null) return 0; 11 | Queue queue = new LinkedList<>(); 12 | queue.add(root); 13 | int ret = 0; 14 | while(!queue.isEmpty()) { 15 | int n = queue.size(); 16 | while(n > 0) { 17 | TreeNode node = queue.poll(); 18 | // if(node == null) continue; 19 | if(node.left != null) { 20 | queue.add(node.left); 21 | } 22 | if(node.right != null) { 23 | queue.add(node.right); 24 | } 25 | n--; 26 | } 27 | ret++; 28 | } 29 | return ret; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/tree/mirrorTree_swordOffer27.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.tree; 2 | 3 | public class mirrorTree_swordOffer27 { 4 | 5 | public TreeNode mirrorTree(TreeNode root) { 6 | if(root == null) return root; 7 | swap(root); 8 | mirrorTree(root.left); 9 | mirrorTree(root.right); 10 | return root; 11 | } 12 | 13 | private void swap(TreeNode root) { 14 | TreeNode t = root.left; 15 | root.left = root.right; 16 | root.right = t; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/twopoint/TwoSum167.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.twopoint; 2 | 3 | public class TwoSum167 { 4 | 5 | public static void main(String[] args) { 6 | int[] numbers = {2, 7, 11, 15}; 7 | for(int num : twoSum(numbers, 9)) { 8 | System.out.print(num + ", "); 9 | } 10 | } 11 | 12 | public static int[] twoSum(int[] numbers, int target) { 13 | // // 方法一 14 | // int[] answer = new int[2]; 15 | // for(int i = 0; i < numbers.length; ++i) { 16 | // for(int j = i + 1; j < numbers.length; ++j) { 17 | // if(numbers[i] + numbers[j] == target) { 18 | // answer[0] = i + 1; 19 | // answer[1] = j + 1; 20 | // break; 21 | // } 22 | // } 23 | // } 24 | // return answer; 25 | // } 26 | // 方法二 双指针实现 27 | int low = 0, high = numbers.length - 1; 28 | while(low < high) { 29 | int sum = numbers[low] + numbers[high]; 30 | if(sum == target) { 31 | return new int[] {low + 1, high + 1}; 32 | }else if(sum < target) { 33 | low++; 34 | } else { 35 | high--; 36 | } 37 | } 38 | return new int[] {0, 0}; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/twopoint/reverseLeftWords_swordOffer58.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.twopoint; 2 | 3 | public class reverseLeftWords_swordOffer58 { 4 | 5 | public String reverseLeftWords(String s, int n) { 6 | /** 7 | * 分析题意:本题涉及到翻转字符串,可以考虑用双指针,先把s字符串转换成字符数组,然后以n为数组下标 8 | * 分界线,(0, n - 1)为前半截,(n, size - 1)为后半截。先把两段分别翻转,然后整体翻转一下, 9 | * 就是我们要的结果。 10 | * 例如: 11 | * Input: 12 | * s="abcXYZdef" 13 | * n=3 14 | * 15 | * 先将 "abc" 和 "XYZdef" 分别翻转,得到 "cbafedZYX",然后再把整个字符串翻转得到 "XYZdefabc"。 16 | */ 17 | int size = s.length(); 18 | char[] chars = s.toCharArray(); 19 | if(n >= size) { 20 | return s; 21 | } 22 | reverse(chars, 0, n - 1); 23 | reverse(chars, n, size - 1); 24 | reverse(chars, 0, size - 1); 25 | return new String(chars); 26 | } 27 | 28 | private void reverse(char[] chars, int i, int j) { 29 | while(i < j) { 30 | swap(chars, i++, j--); 31 | } 32 | } 33 | 34 | private void swap(char[] chars, int i, int j) { 35 | char t = chars[i]; 36 | chars[i] = chars[j]; 37 | chars[j] = t; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/twopoint/reverseWords_swordOffer58.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.twopoint; 2 | 3 | public class reverseWords_swordOffer58 { 4 | 5 | public String reverseWords(String s) { 6 | /** 7 | * 分析题意:我们可以先翻转每个单词,再翻转整个字符串。这样就把单词成功全部翻转过来了,但是对于中间和两边有空格的情况 8 | * 我们任然需要开辟额外的空间来拼接处理多余的空格。最后才能返回结果。 9 | */ 10 | int n = s.length(); 11 | char[] chars = s.toCharArray(); 12 | int i = 0, j = 0; 13 | while(j <= n) { 14 | if(j == n || chars[j] == ' ') { 15 | reverse(chars, i, j - 1); 16 | i = j + 1; 17 | } 18 | j++; 19 | } 20 | reverse(chars, 0, n - 1); 21 | 22 | // 处理多余的空格 23 | String s_ret = new String(chars).trim(); 24 | String[] strings = s_ret.split("[\\s]+"); // “\s”的意思是匹配任意空白符,“\s+” 匹配任意一个或多个任意空白符 25 | StringBuilder sb = new StringBuilder(); 26 | for(String sc : strings) { 27 | sb.append(sc).append(" "); 28 | } 29 | return sb.toString().trim(); 30 | } 31 | 32 | private void reverse(char[] chars, int start, int end) { 33 | while(start < end) { 34 | swap(chars, start++, end--); 35 | } 36 | } 37 | 38 | private void swap(char[] chars, int start, int end) { 39 | char temp = chars[start]; 40 | chars[start] = chars[end]; 41 | chars[end] = temp; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/easy/twopoint/twoSum_swordOffer57.java: -------------------------------------------------------------------------------- 1 | package part_1.easy.twopoint; 2 | 3 | public class twoSum_swordOffer57 { 4 | 5 | public int[] twoSum(int[] nums, int target) { 6 | int i = 0, j = nums.length - 1; 7 | while(i < j) { 8 | int sum = nums[i] + nums[j]; 9 | if(sum == target) { 10 | return new int[] {nums[i], nums[j]}; 11 | } else if(sum < target) { 12 | i++; 13 | } else { 14 | j--; 15 | } 16 | } 17 | return new int[]{0, 0}; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/hard/dp/MaxProfit123.java: -------------------------------------------------------------------------------- 1 | package part_1.hard.dp; 2 | 3 | public class MaxProfit123 { 4 | 5 | public static void main(String[] args) { 6 | int[] prices = {3,3,5,0,0,3,1,4}; 7 | // 打印输出 8 | System.out.println(new MaxProfit123().maxProfit(prices)); 9 | } 10 | 11 | public int maxProfit(int[] prices) { 12 | /* 13 | * 由于我们最多可以完成两笔交易,因此在任意一天结束之后,我们会处于以下五个状态中的一种: 14 | * 未进行过任何操作; 15 | * 只进行过一次买操作; 16 | * 进行了一次买操作和一次卖操作,即完成了一笔交易; 17 | * 在完成了一笔交易的前提下,进行了第二次买操作; 18 | * 完成了全部两笔交易。 19 | */ 20 | 21 | int firstBuy = Integer.MIN_VALUE, firstSell = 0; 22 | int secondBuy = Integer.MIN_VALUE, secondSell = 0; 23 | 24 | for(int curPrice : prices) { 25 | if(firstBuy < -curPrice) { 26 | firstBuy = -curPrice; 27 | } 28 | 29 | if(firstSell < firstBuy + curPrice) { 30 | firstSell = firstBuy + curPrice; 31 | } 32 | 33 | if(secondBuy < firstSell - curPrice) { 34 | secondBuy = firstSell - curPrice; 35 | } 36 | 37 | if(secondSell < secondBuy + curPrice) { 38 | secondSell = secondBuy + curPrice; 39 | } 40 | } 41 | return secondSell; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/hard/dp/fib_swordOffer10_1.java: -------------------------------------------------------------------------------- 1 | package part_1.hard.dp; 2 | 3 | public class fib_swordOffer10_1 { 4 | 5 | public int fib(int n) { 6 | if(n <= 1) return n; 7 | int pre2 = 0, pre1 = 1; 8 | int fib = 0; 9 | for(int i = 2; i <= n; ++i) { 10 | fib = (pre2 + pre1) % 1000000007; 11 | pre2 = pre1; 12 | pre1 = fib; 13 | } 14 | return fib; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/hard/math/countDigitOne_swordOffer43.java: -------------------------------------------------------------------------------- 1 | package part_1.hard.math; 2 | 3 | public class countDigitOne_swordOffer43 { 4 | 5 | public int countDigitOne(int n) { 6 | /** 7 | * 分析题意:// 根据当前位的大小进行判断,比如数字 135x21 8 | * 如果遍历到百位,百位的数字x,x 需要分三种情况讨论 9 | * 第一种:等于1,那么在百位可以计算1的个数为 135 * 100 + 21 10 | * 第二种:大于等于 2,在百位可以计算出1的个数为 (135+1)*100 11 | * 第三种:小于 1,在百位计数出1的个数为 135*100 12 | * 所以大致的思路就是:遍历到哪一位时,将数字分为两部分,然后叠加每一位出现1的次数 13 | */ 14 | int count = 0; 15 | for(long pos = 1; pos <= n; pos *= 10){ 16 | int big = n / (int)pos; 17 | int small = n % (int)pos; 18 | // if(big % 10 == 1){ 19 | // count += small + 1; 20 | // } 21 | // // 之所以这样写,是把第二种和第三种合在了一起 22 | // // 如果因为如果大于2,加8一定会进一位,如果小于等于,就算+8,也不会产生影响 23 | // count += (big + 8) / 10 * pos; 24 | 25 | count += (big + 8) / 10 * pos + (big % 10 == 1 ? small + 1 : 0); 26 | } 27 | return count; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/hard/sort/reversePairs_swordOffer51.java: -------------------------------------------------------------------------------- 1 | package part_1.hard.sort; 2 | 3 | public class reversePairs_swordOffer51 { 4 | 5 | private int cnt = 0; 6 | private int[] tmp; // 声明全局辅助数组 方便传递 7 | 8 | public int reversePairs(int[] nums) { 9 | int n = nums.length; 10 | tmp = new int[n]; 11 | mergeSort(nums, 0, n - 1); 12 | return cnt; 13 | } 14 | 15 | private void mergeSort(int[] nums, int l, int h) { 16 | if(h - l < 1) return; 17 | int m = l + (h - l) / 2; 18 | mergeSort(nums, l, m); 19 | mergeSort(nums, m + 1, h); 20 | merge(nums, l, m, h); 21 | 22 | } 23 | 24 | private void merge(int[] nums, int l, int m, int h) { 25 | int i = l, j = m + 1, k = l; 26 | while(i <= m || j <= h) { 27 | if(i > m) { 28 | tmp[k] = nums[j++]; 29 | } else if(j > h) { 30 | tmp[k] = nums[i++]; 31 | } else if(nums[i] <= nums[j]) { 32 | tmp[k] = nums[i++]; 33 | } else { 34 | tmp[k] = nums[j++]; 35 | cnt += m - i + 1; // nums[i] > nums[j],说明 nums[i...mid] 都大于 nums[j] 左半部分都可以计入结果 36 | } 37 | k++; 38 | } 39 | for(k = l; k <= h; ++k) { 40 | nums[k] = tmp[k]; 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/hard/stackandqueue/MedianFinder_swordOffer41.java: -------------------------------------------------------------------------------- 1 | package part_1.hard.stackandqueue; 2 | 3 | import java.util.PriorityQueue; 4 | 5 | public class MedianFinder_swordOffer41 { 6 | 7 | /* 大顶堆,存储左半边元素 */ 8 | private PriorityQueue left; 9 | /* 小顶堆,存储右半边元素,并且右半边元素都大于左半边 */ 10 | private PriorityQueue right; 11 | /* 当前数据流读入的元素个数 */ 12 | private int N = 0; 13 | /** initialize your data structure here. */ 14 | public MedianFinder_swordOffer41() { 15 | left = new PriorityQueue<>((o1, o2) -> o2 - o1); 16 | right = new PriorityQueue<>(); 17 | } 18 | 19 | public void addNum(int num) { 20 | /* 插入要保证两个堆存于平衡状态 */ 21 | if (N % 2 == 0) { 22 | /* N 为偶数的情况下插入到右半边。 23 | * 因为右半边元素都要大于左半边,但是新插入的元素不一定比左半边元素来的大, 24 | * 因此需要先将元素插入左半边,然后利用左半边为大顶堆的特点,取出堆顶元素即为最大元素,此时插入右半边 */ 25 | left.add(num); 26 | right.add(left.poll()); 27 | } else { 28 | right.add(num); 29 | left.add(right.poll()); 30 | } 31 | N++; 32 | } 33 | 34 | public double findMedian() { 35 | if (N % 2 == 0){ 36 | return (left.peek() + right.peek()) / 2.0; 37 | } 38 | else { 39 | return (double) right.peek(); 40 | } 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/hard/stackandqueue/maxSlidingWindow_swordOffer59.java: -------------------------------------------------------------------------------- 1 | package part_1.hard.stackandqueue; 2 | 3 | import java.util.PriorityQueue; 4 | 5 | public class maxSlidingWindow_swordOffer59 { 6 | 7 | public int[] maxSlidingWindow(int[] nums, int k) { 8 | /** 9 | * 分析题意:题目描述的相当于要你在一个动态更新的大小为k的数组中找到最大值,涉及到动态存取 10 | * 一组数中的最值问题,我们优先想到用堆来实现,本题要取最大值,故我们尝试用大顶堆来求解, 11 | * 我们可以维护一个大小为k的堆,然后滑动窗口在数组中向右移动的时候把堆中最左边即将离开 12 | * 滑动窗口的旧元素删除,即将新到达窗口的元素加入到堆中,这样就能保证堆顶永远是当前滑动 13 | * 窗口的最大值,及时的添加到结果数组中,遍历结束返回结果即可。 14 | */ 15 | int[] result = new int[nums.length - k + 1]; 16 | if(k > nums.length || k < 1) { 17 | return new int[]{ }; 18 | } 19 | PriorityQueue heap = new PriorityQueue<>((o1, o2) -> o2 - o1); 20 | for(int i = 0; i < k; ++i) { 21 | heap.add(nums[i]); 22 | } 23 | int index = 0; 24 | result[index++] = heap.peek(); 25 | for(int i = 0, j = i + k; j < nums.length; ++i, ++j) { 26 | heap.remove(nums[i]); 27 | heap.add(nums[j]); 28 | result[index++] = heap.peek(); 29 | } 30 | return result; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/hard/tree/serialize_swordOffer37.java: -------------------------------------------------------------------------------- 1 | package part_1.hard.tree; 2 | 3 | public class serialize_swordOffer37 { 4 | 5 | private String deserializeStr; 6 | 7 | // Encodes a tree to a single string. 8 | public String serialize(TreeNode root) { 9 | // 序列化 我这里采用空格分割,方便一会还原,如果递归到叶子节点之后或者为空我们用 “#” 做标识 10 | if(root == null) return "#"; 11 | return root.val + " " + serialize(root.left) + " " + serialize(root.right); 12 | } 13 | 14 | // Decodes your encoded data to tree. 15 | public TreeNode deserialize(String data) { 16 | // 把刚刚我们序列化的树字符串取出来 17 | deserializeStr = data; 18 | // 按照我们设置的分隔符递归还原树的结构 19 | return deserialize(); 20 | } 21 | 22 | private TreeNode deserialize() { 23 | if(deserializeStr.length() == 0) return null; 24 | // 找到当前第一个节点的后一个位置 25 | int index = deserializeStr.indexOf(" "); 26 | // substring()方法左闭右开 刚好取到第一个节点 27 | String node = index == -1 ? deserializeStr : deserializeStr.substring(0, index); 28 | // 第一个节点已经取到,从当前deserializeStr中删除掉 29 | deserializeStr = index == -1 ? "" : deserializeStr.substring(index + 1); 30 | if(node.equals("#")) return null; 31 | int val = Integer.valueOf(node); 32 | TreeNode t = new TreeNode(val); 33 | t.left = deserialize(); 34 | t.right = deserialize(); 35 | return t; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/array/FindPeakElement162.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.array; 2 | 3 | public class FindPeakElement162 { 4 | 5 | public int findPeakElement(int[] nums) { 6 | // // O(n) 时间复杂度不满足题意 7 | // int maxNumIdx = 0, maxNum = Integer.MIN_VALUE; 8 | // for(int i = 0; i < nums.length; ++i) { 9 | // if(nums[i] > maxNum) { 10 | // maxNum = nums[i]; 11 | // maxNumIdx = i; 12 | // } 13 | // } 14 | // return maxNumIdx; 15 | 16 | // O(log n) 时间复杂度(二分法) 17 | int l = 0, r = nums.length - 1; 18 | while(l < r) { 19 | int mid = l + (r - l) / 2; 20 | if(nums[mid] < nums[mid + 1]) { 21 | l = mid + 1; 22 | } else { 23 | r = mid; 24 | } 25 | } 26 | return l; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/array/MaxArea11.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.array; 2 | 3 | public class MaxArea11 { 4 | 5 | public int maxArea(int[] height) { 6 | /* 7 | * 分析题意:双指针从两端向中间夹起来求面积,高度小的指针向内 8 | * 移动,高度高的指针保持不动。 9 | * 思考一个问题:为什么只移动高度小的指针? 10 | * 因为当两个指针靠近时,他们之间的距离就在缩短,你再让当前高 11 | * 度更高的指针向内移动,那必定面积只会更小,相反你让高度小的 12 | * 指针向内移动还有可能碰到更高的指针,让其面积更大。 13 | */ 14 | int n = height.length; 15 | int i = 0, j = n - 1, maxNum = 0; 16 | while(i < j) { 17 | maxNum = Math.max(maxNum, (j - i) * Math.min(height[i], height[j])); 18 | if(height[i] < height[j]) { 19 | i++; 20 | } else { 21 | j--; 22 | } 23 | } 24 | return maxNum; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/array/Search33.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.array; 2 | 3 | public class Search33 { 4 | 5 | public int search(int[] nums, int target) { 6 | int n = nums.length; 7 | if(n == 0) return -1; 8 | if(n == 1) return nums[0] == target ? 0 : -1; 9 | int l = 0, r = n - 1; 10 | while(l <= r) { 11 | int mid = l + (r - l) / 2; 12 | if(nums[mid] == target) { 13 | return mid; 14 | } 15 | if(nums[0] <= nums[mid]) { 16 | if(nums[0] <= target && target < nums[mid]) { 17 | r = mid - 1; 18 | } else { 19 | l = mid + 1; 20 | } 21 | } else { 22 | if(nums[mid] < target && target <= nums[n - 1]) { 23 | l = mid + 1; 24 | } else { 25 | r = mid - 1; 26 | } 27 | } 28 | } 29 | return -1; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/arrayandmatrix/ArrayNesting565.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.arrayandmatrix; 2 | 3 | public class ArrayNesting565 { 4 | 5 | public int arrayNesting(int[] nums) { 6 | /* 7 | * 分析题意:本题思想上有贪心算法那味道,也就是每一个元素都去尝试嵌套搜索,每搜索一个就让统计长度加一 8 | * 同时访问过的元素都覆盖为-1做标记,代表访问过了,这样做的好处可以减少重复的访问。每一个位置上能搜索 9 | * 最深的嵌套深度和前序的maxNest做比较,大就替换,最后遍历完一遍数组,maxNest就是最大集合了 10 | */ 11 | int maxNest = 0; 12 | for(int i = 0; i < nums.length; ++i) { 13 | int cnt = 0; 14 | for(int j = i; nums[j] != -1;) { 15 | cnt++; 16 | int t = nums[j]; 17 | nums[j] = -1; // 标识为已访问了 18 | j = t; 19 | } 20 | maxNest = Math.max(maxNest, cnt); 21 | } 22 | return maxNest; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/arrayandmatrix/ConstructArray667.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.arrayandmatrix; 2 | 3 | public class ConstructArray667 { 4 | 5 | public int[] constructArray(int n, int k) { 6 | /* 7 | * 题目描述:数组元素为 1~n 的整数,要求构建数组,使得相邻元素的差值不相同的个数为 k。让前 k+1 个 8 | * 元素构建出 k 个不相同的差值,序列为:1 k+1 2 k 3 k-1 ... k/2 k/2+1. 9 | */ 10 | int[] result = new int[n]; 11 | result[0] = 1; 12 | for(int i = 1, interval = k; i <= k; ++i, --interval) { 13 | result[i] = i % 2 == 1 ? result[i - 1] + interval : result[i - 1] - interval; 14 | } 15 | for(int i = k + 1; i < n; ++i) { 16 | result[i] = i + 1; 17 | } 18 | return result; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/arrayandmatrix/FindDuplicate287.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.arrayandmatrix; 2 | 3 | public class FindDuplicate287 { 4 | 5 | public int findDuplicate(int[] nums) { 6 | // 方法一 桶排序 用空间换时间 7 | // int[] cnt = new int[nums.length + 1]; 8 | // for(int num : nums) { 9 | // cnt[num]++; 10 | // } 11 | // for(int i = 1; i < cnt.length; ++i) { 12 | // if(cnt[i] > 1) return i; 13 | // } 14 | // return 0; 15 | 16 | // 方法二 二分法 每一次找中间值,考虑到1-n之间的数一定都会出现,如果非重复情况下每个数只会 17 | // 出现一次,故我们判断小于等于中间值元素个数如果超过了中间值则说明重复的数在左半边,否则去 18 | // 右半边找 19 | int low = 1, high = nums.length - 1; 20 | while(low <= high) { 21 | int cnt = 0; 22 | int mid = low + (high - low) / 2; 23 | for(int i = 0; i < nums.length; ++i) { 24 | if(nums[i] <= mid) cnt++; 25 | } 26 | if(cnt > mid) high = mid - 1; 27 | else low = mid + 1; 28 | } 29 | return low; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/arrayandmatrix/KthSmallest378.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.arrayandmatrix; 2 | 3 | public class KthSmallest378 { 4 | 5 | public int kthSmallest(int[][] matrix, int k) { 6 | /* 7 | * 分析题意:题目要求在有序数组中找到第k小的元素,也就是按从小到大排序顺序,找到第k个元素输出即可, 8 | * 最简单的方法就是把矩阵遍历一遍降维放到一维数组中去,然后排序一维数组,输出第k - 1个位置上的数即 9 | * 可,这是暴力求排序求解法,我们不做展开,本题我们要用二分法来降低时间复杂度,于是我们可以这样,每 10 | * 次折中找到一个mid值,然后遍历一遍数组找到所有不大于mid值的元素个数我们这里设为cnt,相当于把矩阵 11 | * 切割成两半,然后比较不大于mid元素的个数cnt和k的大小,如果cnt < k 说明第k小元素在大于mid的右半部 12 | * 分这时让低位指针的 mid + 1,否则说明k已经覆盖到不大于mid的左半部分里去了,我们就让 high = mid - 1 13 | * 向左折半缩小范围即可,最后可以在O(nlogk), k = high - low 的时间复杂度内找到答案。 14 | */ 15 | int m = matrix.length, n = matrix[0].length; 16 | int low = matrix[0][0], high = matrix[m - 1][n - 1]; 17 | while(low <= high) { 18 | int mid = low + (high - low) / 2; 19 | int cnt = 0; 20 | for(int i = 0; i < m; ++i) { 21 | for(int j = 0; j < n && matrix[i][j] <= mid; ++j) { 22 | cnt++; 23 | } 24 | } 25 | if(cnt < k) low = mid + 1; 26 | else high = mid - 1; 27 | } 28 | return low; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/arrayandmatrix/MaxChunksToSorted769.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.arrayandmatrix; 2 | 3 | public class MaxChunksToSorted769 { 4 | 5 | public int maxChunksToSorted(int[] arr) { 6 | /* 7 | * 分析题意:当遍历到第 i 个位置时,如果可以切成快,那满足前 i + 1 个元素的最大值一定等于i。 8 | * 否则一定有比 i 还要小的数组值被强制划分到后面的块去了,这样排序出来的各个块连起来 9 | * 不满足全局升序。 10 | */ 11 | int ans = 0; 12 | int maxNum = arr[0]; 13 | for(int i = 0; i < arr.length; ++i) { 14 | maxNum = Math.max(maxNum, arr[i]); // 统计前 i + 1 个元素中的最大元素 15 | if(maxNum == i) ans++; 16 | } 17 | return ans; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/arrayandmatrix/SearchMatrix240.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.arrayandmatrix; 2 | 3 | public class SearchMatrix240 { 4 | 5 | public boolean searchMatrix(int[][] matrix, int target) { 6 | // 方法一 比较第一列再从第一个不小于target的行开始从左向右遍历 7 | // int r = matrix.length, c = matrix[0].length; 8 | // for(int i = 0; i < r; ++i) { 9 | // if(target < matrix[i][0]) { 10 | // return false; 11 | // } else if(target == matrix[i][0]) { 12 | // return true; 13 | // } else { 14 | // for(int j = 1; j < c; ++j) { 15 | // if(target == matrix[i][j]) { 16 | // return true; 17 | // } 18 | // } 19 | // } 20 | // } 21 | // return false; 22 | 23 | // 方法二 在方法一基础上优化,根据矩阵有序的特点,我们优先按列遍历,但是这一次我们优先遍历比较最后一列 24 | // 因为这样如果target比最后一列某一个数大直接让行指针跳到下一行最后一个数继续比较,不用再考虑当前行了。 25 | int m = matrix.length, n = matrix[0].length; 26 | int r = 0, c = n - 1; 27 | while(r < m && c >= 0) { 28 | if(target == matrix[r][c]) { 29 | return true; 30 | } else if(target < matrix[r][c]) { 31 | c--; 32 | } else { 33 | r++; 34 | } 35 | } 36 | return false; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/arrayandmatrix/findNumberIn2DArray_swordOffer04.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.arrayandmatrix; 2 | 3 | public class findNumberIn2DArray_swordOffer04 { 4 | 5 | public boolean findNumberIn2DArray(int[][] matrix, int target) { 6 | /** 7 | * 分析题意:根据矩阵有序的特点,我们优先按列遍历,但是这一次我们优先遍历比较最后一列 8 | * 因为这样如果target比最后一列某一个数大直接让行指针跳到下一行最后一个数继续比较, 9 | * 不用再考虑当前行了。(相似的题可参考240题(我的刷题挑战第一百六十题有视频详解)) 10 | */ 11 | if(matrix == null || matrix.length == 0 || matrix[0].length == 0) return false; 12 | int m = matrix.length, n = matrix[0].length; 13 | int r = 0, c = n - 1; 14 | while(r < m && c >= 0) { 15 | if(matrix[r][c] == target) { 16 | return true; 17 | } else if(matrix[r][c] < target) { 18 | r++; 19 | } else { 20 | c--; 21 | } 22 | } 23 | return false; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/backtracking/CombinationSum3_216.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.backtracking; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class CombinationSum3_216 { 7 | 8 | 9 | public static void main(String[] args) { 10 | CombinationSum3_216 combinationSum3_216 = new CombinationSum3_216(); 11 | int k = 3, n = 7; 12 | System.out.println(combinationSum3_216.combinationSum3(k, n)); 13 | } 14 | 15 | public List> combinationSum3(int k, int n) { 16 | List> combinations = new ArrayList<>(); 17 | List combineList = new ArrayList<>(); 18 | backTracking(1, k, n, combineList, combinations); 19 | return combinations; 20 | } 21 | 22 | private void backTracking(int start, int k, int n, List combineList, List> combinations) { 23 | if(k == 0 && n == 0) { 24 | combinations.add(new ArrayList<>(combineList)); 25 | return; 26 | } 27 | 28 | // 不满足的情况 k先减到0,但是n没有等于0 29 | if(k == 0 || n == 0) { 30 | return; 31 | } 32 | 33 | for(int i = start; i <= 9; ++i) { 34 | combineList.add(i); 35 | backTracking(i + 1, k - 1, n - i, combineList, combinations); 36 | combineList.remove(combineList.size() - 1); 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/backtracking/Combine77.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.backtracking; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class Combine77 { 7 | 8 | 9 | public static void main(String[] args) { 10 | int n = 4, k = 2; 11 | Combine77 combine77 = new Combine77(); 12 | System.out.println(combine77.combine(n, k)); 13 | } 14 | 15 | public List> combine(int n, int k) { 16 | List> combinations = new ArrayList<>(); 17 | List combineList = new ArrayList<>(); 18 | backTracking(1, k, n, combineList, combinations); 19 | return combinations; 20 | } 21 | 22 | private void backTracking(int start, int k, int n, List combineList, List> combinations) { 23 | if(k == 0) { 24 | combinations.add(new ArrayList<>(combineList)); 25 | return; 26 | } 27 | 28 | for(int i = start; i <= n - k + 1; ++i) { 29 | combineList.add(i); 30 | backTracking(i + 1, k - 1, n, combineList, combinations); 31 | combineList.remove(combineList.size() - 1); 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/backtracking/Subsets78.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.backtracking; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class Subsets78 { 7 | 8 | public static void main(String[] args) { 9 | Subsets78 subsets78 = new Subsets78(); 10 | int[] nums = {1, 2, 3}; 11 | System.out.println(subsets78.subsets(nums)); 12 | } 13 | 14 | public List> subsets(int[] nums) { 15 | List> allSubsets = new ArrayList<>(); 16 | List tempSubset = new ArrayList<>(); 17 | for(int size = 0; size <= nums.length; ++size) { 18 | // 所有子集的长度可能为0到nums.length,每一个可能长度size我们都要dfs并且回溯他所有可能的排列组合 19 | backTracking(0, size, nums, tempSubset, allSubsets); 20 | } 21 | return allSubsets; 22 | } 23 | 24 | private void backTracking(int start, int size, int[] nums, List tempSubset, List> allSubsets) { 25 | if(tempSubset.size() == size) { 26 | allSubsets.add(new ArrayList<>(tempSubset)); 27 | return; 28 | } 29 | 30 | for(int i = start; i < nums.length; ++i) { 31 | tempSubset.add(nums[i]); 32 | backTracking(i + 1, size, nums, tempSubset, allSubsets); 33 | tempSubset.remove(tempSubset.size() - 1); 34 | } 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/binarysearch/FindMin153.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.binarysearch; 2 | 3 | public class FindMin153 { 4 | 5 | public static void main(String[] args) { 6 | int[] nums = {3, 4, 5, 1, 2}; 7 | System.out.println(findMin(nums)); 8 | } 9 | 10 | public static int findMin(int[] nums) { 11 | // 方法一 12 | // Arrays.sort(nums); 13 | // return nums[0]; 14 | 15 | // 方法二 找到当前第一个比前置节点小的数就是最小值 16 | // int pre = nums[0], size = nums.length; 17 | // if(size == 1) { 18 | // return nums[0]; 19 | // } 20 | // for(int i = 1; i < size; ++i) { 21 | // if(nums[i] < pre) { 22 | // return nums[i]; 23 | // } else { 24 | // pre = nums[i]; 25 | // } 26 | // } 27 | // return nums[0]; 28 | 29 | // 方法三 二分查找法 30 | int low = 0, high = nums.length - 1; 31 | while(low < high) { 32 | int mid = low + (high - low) / 2; 33 | if(nums[mid] <= nums[high]) { 34 | high = mid; 35 | } else { 36 | low = mid + 1; 37 | } 38 | } 39 | return nums[low]; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/binarysearch/GetNumberOfK_newcoderJZ37.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.binarysearch; 2 | 3 | public class GetNumberOfK_newcoderJZ37 { 4 | 5 | public int GetNumberOfK(int [] array , int k) { 6 | int first = binarySearch(array, k); 7 | int last = binarySearch(array, k + 1); 8 | return (first == array.length || array[first] != k) ? 0 : last - first; 9 | } 10 | 11 | private int binarySearch(int[] nums, int k) { 12 | int l = 0, h = nums.length; 13 | while(l < h) { 14 | int m = l + (h - l) / 2; 15 | if(nums[m] >= k) { 16 | h = m; 17 | } else { 18 | l = m + 1; 19 | } 20 | } 21 | return l; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/bitwiseoperator/FindNumsAppearOnce_swordOffer40.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.bitwiseoperator; 2 | 3 | public class FindNumsAppearOnce_swordOffer40 { 4 | 5 | public int[] FindNumsAppearOnce (int[] array) { 6 | int diff = 0; 7 | for(int num : array) { 8 | diff ^= num; 9 | } 10 | // 数组中两个只出现一次数的异或结果中最右边的那个位置上的1拿出来了,并且它绝对位置不变 11 | diff &= -diff; 12 | int[] res = new int[2]; 13 | for(int num : array) { 14 | if((num & diff) == 0) { 15 | res[0] ^= num; 16 | } else { 17 | res[1] ^= num; 18 | } 19 | } 20 | if(res[0] > res[1]) { 21 | int temp = res[0]; 22 | res[0] = res[1]; 23 | res[1] = temp; 24 | } 25 | return res; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/bitwiseoperator/GetSum371.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.bitwiseoperator; 2 | 3 | public class GetSum371 { 4 | 5 | public int getSum(int a, int b) { 6 | /** 7 | * 分析题意:做两数加法运算还不能用加号,我们就可以尝试用位运算来代替加法运算。 8 | * 首先我们知道第一个数是a,第二个数是b。那么 a ^ b 表示的就是a和b做加法运算 9 | * 只是没有考虑进位,也就是说他们的二进制数,如果在相同位置碰到两个1,异或结果 10 | * 为0,和 1 + 1 进位后这一位为0一致,0 + 1 在异或中等于1也没有问题。最大的 11 | * 问题就是进位我们如何解决呢,我们这里的结果方案是 让 (a & b) << 1,验证一下 12 | * 如果a和b二进制的当前相同位置上都为1,那么 1 & 1 = 1,然后左移一位 13 | * 就变成了10,符合二进制数加法规律1 + 1 = 10, 14 | * 如果是 1 & 0 = 0,或者 0 & 0 = 0 向左进位为 00,即没有进位,也符合二进制 15 | * 加法规律,故验证成功 16 | * 我们可以用递归来写本题, 17 | * 递归让 a = a ^ b 表示不考虑进位的加法运算,b = (a & b) << 1 记录进位 18 | * 这样在下一趟递归时,整个加法运算就完整了,那么如何才能结束递归呢? 19 | * 我们分析下可能可以让递归终止的原因,(a & b) << 1 每执行一次,最右边会多一个 0, 20 | * 那么继续递归,进位最右边的 0 会慢慢增多,最后进位会变为 0,即b = 0时递归终止。 21 | */ 22 | return b == 0 ? a : getSum(a ^ b, (a & b) << 1); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/bitwiseoperator/MaxProduct318.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.bitwiseoperator; 2 | 3 | public class MaxProduct318 { 4 | 5 | public int maxProduct(String[] words) { 6 | /** 7 | * 分析题意:字符串数组的字符串只含有小写字符。求解字符串数组中两个字符串长度的最大乘积,要求这两个字符串 8 | * 不能含有相同字符。本题主要问题是判断两个字符串是否含相同字符,由于字符串只含有小写字符,总共 26 位, 9 | * 因此可以用一个 32 位的整数来存储每个字符是否出现过。 10 | */ 11 | 12 | 13 | int n = words.length; 14 | int[] val = new int[n]; 15 | for(int i = 0; i < n; ++i) { 16 | for(char c : words[i].toCharArray()) { 17 | val[i] |= 1 << (c - 'a'); 18 | } 19 | } 20 | int ret = 0; 21 | for (int i = 0; i < n; i++) { 22 | for (int j = i + 1; j < n; j++) { 23 | if ((val[i] & val[j]) == 0) { 24 | ret = Math.max(ret, words[i].length() * words[j].length()); 25 | } 26 | } 27 | } 28 | return ret; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/bitwiseoperator/SingleNumber260.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.bitwiseoperator; 2 | 3 | public class SingleNumber260 { 4 | 5 | public int[] singleNumber(int[] nums) { 6 | /* 7 | * 分析题意:之前我们做的题目是找出一组数组中只出现一次的那个数,直接连续异或遍历一遍重复出现两次的数异或为0 8 | * 最后剩下的那一个数就是结果,那本题中数组中只出现一次的数有两个,我们用同样的方法异或求得的是着两个只出现一 9 | * 次的数异或的结果,并非这两个数,我们需要的是通过这个结果把这两个数的找出来,那怎么办呢? 10 | * 大胆试想一下,我还是想要用异或的方法,有没有一种方法能把整个数组拆分成两个子数组,让两个只出现一次的数分别 11 | * 出现在一个子数组中,这样我们再去异或两个子数组不就有变成一个数组中只有一个数出现一次的问题了。 12 | * 我们想一想之前将位运算第一题时我们学过这个知识点: 13 | * n&(-n) 得到 n 的位级表示中最低的那一位 1。 14 | * 这样我们把数组遍历一遍异或的结果就是只出现一次的那两个数异或的结果,单从二进制数的位上面去看,本质上是保留了 15 | * 这两个数相异个位置上的数标记为1了。因为两个数不相同所以按异或结果的二进制数最右边不为0的那个数1就是这两个数 16 | * 中其中一个数那一位是这个数了,通过这个特性,我们继续分析: 17 | * 18 | */ 19 | int diff = 0; 20 | for(int num : nums) { 21 | diff = diff ^ num; 22 | } 23 | 24 | diff &= -diff; 25 | int[] result = new int[2]; 26 | for(int num : nums) { 27 | if((num & diff) == 0) result[0] ^= num; 28 | else result[1] ^= num; 29 | } 30 | return result; 31 | 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/bitwiseoperator/SwapNumbers16_01.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.bitwiseoperator; 2 | 3 | // 面试题 16.01. 交换数字 (不用额外变量交换两个整数 程序员代码面试指南 :P317) 4 | public class SwapNumbers16_01 { 5 | 6 | public int[] swapNumbers(int[] numbers) { 7 | /* 8 | * 分析题意:不开辟额外空间,原地交换两个数,我们可以用到位运算的两个知识点 9 | * x ^ 0 = x, x ^ x = 0; 10 | * 例如第一个变量是a,第二个变量是b,那么我们先让 a = a ^ b,然后 b = a ^ b, 此时把前面 a 的新赋值带进来 11 | * 就得 b = a ^ b ^ b = a ^ 0 = a,这样 b = a 已经替换成功了,然后再让 a = a ^ b,把前面两个式子带入第 12 | * 三个式子中得:a = a ^ b ^ a ^ b ^ b,偶数个异或为0,0异或任何数都为任何数,得到结果 a = b。替换完毕。 13 | */ 14 | numbers[0] = numbers[0] ^ numbers[1]; 15 | numbers[1] = numbers[0] ^ numbers[1]; 16 | numbers[0] = numbers[0] ^ numbers[1]; 17 | return numbers; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/dfs/NumIslands200.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.dfs; 2 | 3 | public class NumIslands200 { 4 | 5 | private static int[][] directions = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}}; 6 | private static int m, n; 7 | 8 | public static void main(String[] args) { 9 | char[][] grid = {{'1','1','1','1','0'}, 10 | {'1','1','0','1','0'}, 11 | {'1','1','0','0','0'}, 12 | {'0','0','0','0','0'}}; 13 | System.out.println(numIslands(grid)); 14 | } 15 | 16 | public static int numIslands(char[][] grid) { 17 | m = grid.length; 18 | n = grid[0].length; 19 | int areaCount = 0; 20 | for(int i = 0; i < m; ++i) { 21 | for(int j = 0; j < n; ++j) { 22 | if(grid[i][j] == '1') { 23 | dfs(grid, i, j); 24 | areaCount++; 25 | } 26 | } 27 | } 28 | return areaCount; 29 | } 30 | 31 | private static void dfs(char[][] grid, int i, int j) { 32 | if(i < 0 || i >=m || j < 0 || j >=n || grid[i][j] != '1') { 33 | return; 34 | } 35 | grid[i][j] = '0'; 36 | for(int[] d : directions) { 37 | dfs(grid, i + d[0], j + d[1]); 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/dfs/permutation_swordOffer38.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.dfs; 2 | 3 | public class permutation_swordOffer38 { 4 | 5 | private List ret_list = new ArrayList<>(); 6 | 7 | public String[] permutation(String s) { 8 | if(s.length() == 0) return new String[0]; 9 | char[] s2c = s.toCharArray(); 10 | Arrays.sort(s2c); 11 | backtracking(s2c, new boolean[s2c.length], new StringBuilder()); 12 | String[] ret = new String[ret_list.size()]; 13 | for(int i = 0; i < ret_list.size(); ++i) { 14 | ret[i] = ret_list.get(i); 15 | } 16 | return ret; 17 | } 18 | 19 | private void backtracking(char[] s2c, boolean[] visited, StringBuilder sb) { 20 | if(sb.length() == s2c.length) { 21 | ret_list.add(sb.toString()); 22 | return; 23 | } 24 | for(int i = 0; i < s2c.length; ++i) { 25 | if(visited[i]) { 26 | continue; 27 | } 28 | // 去掉连续重复的字符 29 | if(i != 0 && s2c[i] == s2c[i - 1] && !visited[i - 1]) { 30 | continue; 31 | } 32 | visited[i] = true; 33 | sb.append(s2c[i]); 34 | backtracking(s2c, visited, sb); 35 | sb.deleteCharAt(sb.length() - 1); 36 | visited[i] = false; 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/divideandconquer/myPow_swordOffer16.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.divideandconquer; 2 | 3 | public class myPow_swordOffer16 { 4 | 5 | public double myPow(double x, int n) { 6 | boolean isNegative = false; 7 | if(n < 0) { 8 | n = -n; 9 | isNegative = true; 10 | } 11 | double res = pow(x, n); 12 | return isNegative ? 1 / res : res; 13 | } 14 | 15 | private double pow(double x, int n) { 16 | if(n == 0) return 1; 17 | if(n == 1) return x; 18 | double res = pow(x, n / 2); 19 | res = res * res; 20 | if(n % 2 != 0) res *= x; 21 | return res; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/dp/CanPartition416.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.dp; 2 | 3 | public class CanPartition416 { 4 | 5 | public static void main(String[] args) { 6 | System.out.println(new CanPartition416().canPartition(new int[] {1, 5, 11, 5})); 7 | } 8 | 9 | public boolean canPartition(int[] nums) { 10 | int sum = generateArraySum(nums); 11 | if(sum % 2 != 0) { 12 | return false; 13 | } 14 | int target = sum / 2; 15 | boolean[] dp = new boolean[target + 1]; 16 | dp[0] = true; 17 | // 0-1 背包一个物品只能用一次 18 | for(int num : nums) { 19 | // 从后先前,先计算 dp[i] 在计算 dp[i - num] 20 | for(int j = target; j >= num; --j) { 21 | dp[j] = dp[j] || dp[j - num]; 22 | } 23 | } 24 | return dp[target]; 25 | } 26 | 27 | private int generateArraySum(int[] nums) { 28 | int sum = 0; 29 | for(int num : nums) { 30 | sum += num; 31 | } 32 | return sum; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/dp/Change518.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.dp; 2 | 3 | public class Change518 { 4 | 5 | public static void main(String[] args) { 6 | System.out.println(new Change518().change(5, new int[] {1, 2, 5})); 7 | } 8 | 9 | public int change(int amount, int[] coins) { 10 | int[] dp = new int[amount + 1]; 11 | dp[0] = 1; 12 | for(int coin : coins) { 13 | for(int j = coin; j <= amount; ++j) { 14 | // 之前做完全背包问题是比较并只存储一个要么数量最大或最小,这里是全部都要即累加,所以 15 | // 是dp[j] = dp[j] + dp[j - coin]; 16 | dp[j] = dp[j] + dp[j - coin]; 17 | } 18 | } 19 | return dp[amount]; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/dp/CoinChange322.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.dp; 2 | 3 | public class CoinChange322 { 4 | 5 | public static void main(String[] args) { 6 | System.out.println(new CoinChange322().coinChange(new int[] {1, 2, 5}, 11)); 7 | } 8 | 9 | public int coinChange(int[] coins, int amount) { 10 | /* 11 | * 分析题意:因为硬币可以重复使用,因此这是一个完全背包问题。完全背包只需要 12 | * 将 0-1 背包的逆序遍历 dp 数组改为正序遍历即可。 13 | */ 14 | 15 | if(amount == 0) return 0; 16 | int[] dp = new int[amount + 1]; 17 | for(int coin : coins) { 18 | // 将逆序遍历改成正序遍历 19 | for(int j = coin; j <= amount; ++j) { 20 | if(j == coin) { 21 | // 如果j恰好等于当前面值coin,那就当前j下要1个硬币coin 22 | dp[j] = 1; 23 | } else if(dp[j] == 0 && dp[j - coin] != 0) { 24 | dp[j] = dp[j - coin] + 1; 25 | } else if(dp[j - coin] != 0) { 26 | dp[j] = Math.min(dp[j], dp[j - coin] + 1); 27 | } 28 | } 29 | } 30 | // 最后如果amount金额下有满足其条件下的硬币个数,就返回其个数否则找不到合适的就返回-1 31 | return dp[amount] == 0 ? -1 : dp[amount]; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/dp/CombinationSum4_377.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.dp; 2 | 3 | import java.util.Arrays; 4 | 5 | public class CombinationSum4_377 { 6 | 7 | public static void main(String[] args) { 8 | int[] nums = {1, 2, 3}; 9 | int target = 4; 10 | System.out.println(new CombinationSum4_377().combinationSum4(nums, target)); 11 | } 12 | 13 | public int combinationSum4(int[] nums, int target) { 14 | int[] dp = new int[target + 1]; 15 | dp[0] = 1; 16 | Arrays.sort(nums); 17 | // 外层背包容量i 18 | for(int i = 1; i <= target; ++i) { 19 | // 内层物品遍历nums找到所有可能的不超过容量i的物品装进去,累加所有能得到的组合 20 | for(int j = 0; j < nums.length && nums[j] <= i; ++j) { 21 | dp[i] = dp[i] + dp[i - nums[j]]; 22 | } 23 | } 24 | return dp[target]; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/dp/DicesProbability60.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.dp; 2 | 3 | public class DicesProbability60 { 4 | 5 | public double[] dicesProbability(int n) { 6 | /** 7 | * 动态规划的解法 8 | * dp[i][j] 代表前 i 个骰子的点数和 j 的概率,并执行状态转移 9 | * 而由于 dp[i] 仅由 dp[i−1] 递推得出,为降低空 10 | * 间复杂度,只建立两个一维数组 dp , tmp 交替前进即可 11 | * 时间复杂度O(n^2),空间复杂度O(n) 12 | */ 13 | double[] dp = new double[6]; 14 | Arrays.fill(dp, 1.0 / 6.0); 15 | for(int i = 2; i <= n; ++i) { 16 | double[] tmp = new double[5 * i + 1]; 17 | for(int j = 0; j < dp.length; ++j) { 18 | for(int k = 0; k < 6; ++k) { 19 | tmp[j + k] += dp[j] / 6.0; 20 | } 21 | } 22 | dp = tmp; 23 | } 24 | return dp; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/dp/FindLongestChain646.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.dp; 2 | 3 | import java.util.Arrays; 4 | 5 | public class FindLongestChain646 { 6 | 7 | public static void main(String[] args) { 8 | int[][] pairs = {{1, 2}, {2, 3}, {3, 4}}; 9 | System.out.println(new FindLongestChain646().findLongestChain(pairs)); 10 | } 11 | 12 | public int findLongestChain(int[][] pairs) { 13 | /* 14 | * 分析题意:找不相交子序列最长链的问题,分析点就在找到满足题意条件的左右边界上,问题就能得以解决 15 | */ 16 | // 把当前数列对按升序排列 17 | Arrays.sort(pairs, (a, b) -> (a[0] - b[0])); 18 | int n = pairs.length; 19 | int[] dp = new int[n]; 20 | // 初始情况下,最少肯定有一对,我们就把dp全部填充为左边界的最小值1 21 | Arrays.fill(dp, 1); 22 | 23 | for(int i = 1; i < n; ++i) { 24 | for(int j = 0; j < i; ++j) { 25 | // 如果前序数对的右边界小于当前数对的左边界,那么可以扩容 26 | if(pairs[j][1] < pairs[i][0]) { 27 | dp[i] = Math.max(dp[i], dp[j] + 1); 28 | } 29 | } 30 | } 31 | 32 | // return Arrays.stream(dp).max().orElse(0); 33 | 34 | int result = 0; 35 | for(int i = 0; i < n; ++i) { 36 | result = Math.max(result, dp[i]); 37 | } 38 | return result; 39 | 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/dp/FindMaxForm474.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.dp; 2 | 3 | public class FindMaxForm474 { 4 | 5 | public static void main(String[] args) { 6 | String[] strs = {"10", "0001", "111001", "1", "0"}; 7 | int m = 5, n = 3; 8 | System.out.println(new FindMaxForm474().findMaxForm(strs, m, n)); 9 | } 10 | 11 | public int findMaxForm(String[] strs, int m, int n) { 12 | int[][] dp = new int[m + 1][n + 1]; 13 | for(String s : strs) { 14 | int zeros = 0, ones = 0; 15 | for(char c : s.toCharArray()) { 16 | if(c == '0') { 17 | zeros++; 18 | } else { 19 | ones++; 20 | } 21 | } 22 | 23 | for(int i = m; i >= zeros; --i) { 24 | for(int j = n; j >= ones; --j) { 25 | dp[i][j] = Math.max(dp[i][j], dp[i - zeros][j - ones] + 1); 26 | } 27 | } 28 | } 29 | 30 | return dp[m][n]; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/dp/IntegerBreak343.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.dp; 2 | 3 | public class IntegerBreak343 { 4 | 5 | 6 | public static void main(String[] args) { 7 | System.out.println(new IntegerBreak343().integerBreak(10)); 8 | } 9 | 10 | public int integerBreak(int n) { 11 | // 这里取值范围取 n + 1 是为了方便下标取值,下标从零开始,但是dp[0] = 0,我们不需要,我们需要取到dp[n] 12 | int[] dp = new int[n + 1]; 13 | dp[1] = 1; 14 | for(int i = 2; i <= n; ++i) { 15 | for(int j = 1; j < i; ++j) { 16 | // 要么就拆分为 j 和 i - j 两个数的乘积,要么继续拆分 i - j 为其他至少两个数的乘积,然后比较取乘积最大的 17 | dp[i] = Math.max(dp[i], Math.max(j * (i - j), j * dp[i - j])); 18 | } 19 | } 20 | 21 | return dp[n]; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/dp/LengthOfLIS300.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.dp; 2 | 3 | public class LengthOfLIS300 { 4 | 5 | public static void main(String[] args) { 6 | System.out.println(new LengthOfLIS300().lengthOfLIS(new int[] {10,9,2,5,3,7,101,18})); 7 | } 8 | 9 | public int lengthOfLIS(int[] nums) { 10 | int n = nums.length; 11 | int[] dp = new int[n]; 12 | for(int i = 0; i < n; ++i) { 13 | //初始情况下,递增子序列长度最小为1 14 | int max = 1; 15 | // 遍历找所有下标小于等于i的数,作比较找到尽可能多的严格递增子序列 16 | for(int j = 0; j < i; ++j) { 17 | if(nums[i] > nums[j]) { 18 | // 找到了一个,nums[i] 比前序转移方程dp[j]的最大值还要大,说明递增子序列可以再加1 19 | max = Math.max(max, dp[j] + 1); 20 | } 21 | } 22 | // 每一趟下标小于等于i的子序列遍历都能找到以i为结尾下标的最大递增子序列长度 23 | dp[i] = max; 24 | } 25 | 26 | // 最后遍历找到最大的dp[i]即为结果 27 | // return Arrays.stream(dp).max().orElse(0); 28 | 29 | int result = 0; 30 | for(int i = 0; i < n; ++i) { 31 | result = Math.max(result, dp[i]); 32 | } 33 | return result; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/dp/MinDistance583.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.dp; 2 | 3 | public class MinDistance583 { 4 | 5 | public static void main(String[] args) { 6 | String word1 = "sea", word2 = "eat"; 7 | System.out.println(new MinDistance583().minDistance(word1, word2)); 8 | } 9 | 10 | public int minDistance(String word1, String word2) { 11 | int m = word1.length(), n = word2.length(); 12 | if(m == 0 || n == 0) return m + n; 13 | 14 | int[][] dp = new int[m + 1][n + 1]; 15 | 16 | for(int i = 1; i <= m; ++i) { 17 | for(int j = 1; j <= n; ++j) { 18 | if(word1.charAt(i - 1) == word2.charAt(j - 1)) { 19 | // 前序已经找到的最长公共子序列加1 20 | dp[i][j] = dp[i - 1][j - 1] + 1; 21 | } else { 22 | // 当前i和j位置字符不相等,说明错位了那就删除掉可能错位的位置(i或者j错位了二选一删掉一个) 23 | // 回到i - 1,j或者i,j - 1前序最长公共子序列作为当前结果继续先后探寻 24 | dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]); 25 | } 26 | } 27 | } 28 | 29 | // word1串的长度加上word2串长度,减去他们相同的最长的公共子序列长度两倍剩下的就是要删除的字符所需的步长 30 | return m + n - 2 * dp[m][n]; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/dp/MinPathSum64.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.dp; 2 | 3 | public class MinPathSum64 { 4 | 5 | 6 | public static void main(String[] args) { 7 | System.out.println(new MinPathSum64().minPathSum(new int[][] {{1, 3, 1}, {1, 5, 1}, {4, 2, 1}})); 8 | } 9 | 10 | /** 11 | * V(xy): 坐标 x,y 点的值 12 | * f(x,y): 0,0 到坐标 x,y 的最小路径和 13 | * 因为要求 从左上到右下x,y的最小路径和 14 | * 求x,y的最小路径和就是求 x,y的上一个坐标或x,y左边一个坐标 + x,y坐标的值 15 | * a=f(x-1,y) 16 | * b=f(x,y-1) 17 | * f(x,y) = min[a,b] + V(xy) 18 | **/ 19 | 20 | public int minPathSum(int[][] grid) { 21 | int row = grid.length; 22 | int col = grid[0].length; 23 | 24 | // 初始化第一行(行不动为0,列动) 25 | for(int i = 1; i < col; ++i) { 26 | grid[0][i] += grid[0][i - 1]; 27 | } 28 | 29 | // 初始化第一列 (行动,列不动为0) 30 | for(int i = 1; i < row; ++i) { 31 | grid[i][0] += grid[i - 1][0]; 32 | } 33 | 34 | // 寻找最小路径和 35 | for(int i = 1; i < row; ++i) { 36 | for(int j =1; j < col; ++j) { 37 | int min = Math.min(grid[i - 1][j], grid[i][j - 1]); 38 | grid[i][j] += min; 39 | } 40 | } 41 | return grid[row - 1][col - 1]; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/dp/MinSteps650.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.dp; 2 | 3 | public class MinSteps650 { 4 | 5 | public static void main(String[] args) { 6 | System.out.println(new MinSteps650().minSteps(3)); 7 | } 8 | 9 | public int minSteps(int n) { 10 | int[] dp = new int[n + 1]; 11 | dp[1] = 0; // 初始化的时候就有一个A,不用做任何操作 12 | int h = (int) Math.sqrt(n); 13 | for(int i = 2; i <= n; ++i) { 14 | dp[i] = i; 15 | for(int j = 2; j <= h; ++j) { 16 | if(i % j == 0) { 17 | dp[i] = dp[j] + dp[i / j]; 18 | break; 19 | } 20 | } 21 | } 22 | 23 | return dp[n]; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/dp/NumDecodings91.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.dp; 2 | 3 | public class NumDecodings91 { 4 | 5 | public static void main(String[] args) { 6 | System.out.println(new NumDecodings91().numDecodings("12")); 7 | } 8 | 9 | public int numDecodings(String s) { 10 | int n = s.length(); 11 | int[] dp = new int[n]; // 存储当前数字时的解码种数 12 | dp[0] = 1; // 没有实际意义,只是一种情况 13 | if(s.charAt(0) == '0') { 14 | return 0; 15 | } 16 | for(int i = 1; i < n; ++i) { 17 | if(s.charAt(i) == '0') { 18 | // 出现0,但是0前面不是1,2,不能解码 19 | if(s.charAt(i - 1) != '1' && s.charAt(i - 1) != '2') return 0; 20 | // 0出现在位置1,只有一种解码方式,出现在其他位置等于dp[i - 2] 21 | dp[i] = i == 1 ? 1 : dp[i - 2]; 22 | } else if(s.charAt(i - 1) == '1' || (s.charAt(i - 1) == '2' && s.charAt(i) - '0' > 0 && s.charAt(i) - '0' < 7)) { 23 | // 出现两种解法方式的条件 24 | // 出现在位置1,没有i - 2 25 | // 出现在其他位置dp[i - 1]+ dp[i - 2] 26 | dp[i] = i == 1 ? dp[i - 1] + 1 : dp[i - 1] + dp[i - 2]; 27 | } else { 28 | // 只有一种解码 29 | dp[i] = dp[i - 1]; 30 | } 31 | } 32 | return dp[n - 1]; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/dp/NumberOfArithmeticSlices413.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.dp; 2 | 3 | public class NumberOfArithmeticSlices413 { 4 | 5 | public static void main(String[] args) { 6 | System.out.println(new NumberOfArithmeticSlices413().numberOfArithmeticSlices(new int[] {1, 2, 3, 4})); 7 | } 8 | 9 | public int numberOfArithmeticSlices(int[] nums) { 10 | if(nums == null || nums.length == 0) { 11 | return 0; 12 | } 13 | 14 | int n = nums.length; 15 | int[] dp = new int[n]; 16 | 17 | for(int i = 2; i < n; ++i) { 18 | if(nums[i] - nums[i - 1] == nums[i - 1] - nums[i - 2]) { 19 | dp[i] = dp[i - 1] + 1; 20 | } 21 | } 22 | 23 | // 因为递增子区间不一定以最后一个元素为结尾,可以是任意一个元素结尾,因此需要返回 dp 数组累加的结果 24 | int total = 0; 25 | for(int count : dp) { 26 | total += count; 27 | } 28 | 29 | return total; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/dp/UniquePaths62.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.dp; 2 | 3 | import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput; 4 | 5 | public class UniquePaths62 { 6 | 7 | public static void main(String[] args) { 8 | System.out.println(new UniquePaths62().uniquePaths(3, 7)); 9 | } 10 | 11 | public int uniquePaths(int m, int n) { 12 | int[][] dp = new int[m][n]; 13 | // 第一列算是一种方法 14 | for(int i = 0; i < m; ++i) { 15 | dp[i][0] = 1; 16 | } 17 | // 第一行也算是一种方法 18 | for(int j = 0; j < n; ++j) { 19 | dp[0][j] = 1; 20 | } 21 | // 然后剩下来的行和列每一个路径从上向下走是一种方法,从左向右走也是一种方法所以要累加起来 22 | for(int i = 1; i < m; ++i) { 23 | for(int j = 1; j < n; ++j) { 24 | dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; 25 | } 26 | } 27 | 28 | return dp[m - 1][n - 1]; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/dp/WiggleMaxLength376.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.dp; 2 | 3 | public class WiggleMaxLength376 { 4 | 5 | public static void main(String[] args) { 6 | System.out.println(new WiggleMaxLength376().wiggleMaxLength(new int[] {1, 7, 4, 9, 2, 5})); 7 | } 8 | 9 | public int wiggleMaxLength(int[] nums) { 10 | /* 11 | * 分析题意:摆动序列,光看名字就很有规律,并且可以找到转移模板,就好比股票一样 12 | * 有涨有跌震荡波动才是人生常态,就好比呼吸一样,你不能只呼不吸或者只吸不呼对吧 13 | * 如果连续涨和连续跌都会吓死人的。故这题,满足下一个数比上一个数大,那么下下个 14 | * 满足条件的数必须是比下一个数小才能算进来,否则摆动子序列长度永远不增加。 15 | */ 16 | int up = 1, down = 1; 17 | for(int i = 1; i < nums.length; ++i) { 18 | // 只要碰不到下一个下跌(nums[i] < nums[i - 1]), up 的数值大小将永远原地踏步, else if 同理 19 | if(nums[i] > nums[i - 1]) { 20 | up = down + 1; 21 | } else if(nums[i] < nums[i - 1]) { 22 | down = up + 1; 23 | } 24 | } 25 | return Math.max(up, down); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/dp/WordBreak139.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.dp; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class WordBreak139 { 7 | 8 | public static void main(String[] args) { 9 | String s = "leetcode"; 10 | List wordDict = new ArrayList<>(); 11 | wordDict.add("leet"); 12 | wordDict.add("code"); 13 | System.out.println(new WordBreak139().wordBreak(s, wordDict)); 14 | } 15 | 16 | public boolean wordBreak(String s, List wordDict) { 17 | int n = s.length(); 18 | boolean[] dp = new boolean[n + 1]; 19 | dp[0] = true; 20 | // 背包时s字符串,物品时wordDict中的每一个word,向背包里面装物品,并且强调顺序性 21 | for(int i = 1; i <= n; ++i) { 22 | for(String word : wordDict) { 23 | int len = word.length(); 24 | if(len <= i && word.equals(s.substring(i - len, i))) { 25 | // 要么转包,要么不装包 26 | dp[i] = dp[i] || dp[i - len]; 27 | } 28 | } 29 | } 30 | return dp[n]; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/dp/constructArr_swordOffer66.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.dp; 2 | 3 | public class constructArr_swordOffer66 { 4 | 5 | public int[] constructArr(int[] a) { 6 | int n = a.length; 7 | int[] B = new int[n]; 8 | // 先从左到右把每个B[i]左边的所有数乘积算出来,存到对应位置 9 | for(int i = 0, product = 1; i < n; product *= a[i], ++i) { 10 | B[i] = product; 11 | } 12 | // 再从右向左把每个B[i]右边的所有数乘积算出来,累乘到上一步已经算好每个位置左边乘积的数上 13 | for(int i = n - 1, product = 1; i >= 0; product *= a[i], --i) { 14 | B[i] *= product; 15 | } 16 | return B; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/dp/dicesSum_swordOffer60.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.dp; 2 | 3 | import java.util.*; 4 | 5 | public class dicesSum_swordOffer60 { 6 | 7 | public List> dicesSum(int n) { 8 | final int face = 6; 9 | final int pointNum = face * n; 10 | long[][] dp = new long[n + 1][pointNum + 1]; 11 | for(int i = 1; i <= face; ++i) { 12 | dp[1][i] = 1; 13 | } 14 | 15 | for(int i = 2; i <= n; ++i) { 16 | // i 个骰子最小点数和为i个1 17 | for(int j = i; j <= pointNum; ++j) { 18 | for(int k = 1; k <= face && k <= j ; ++k) { 19 | dp[i][j] += dp[i - 1][j - k]; 20 | } 21 | } 22 | } 23 | final double totalNum = Math.pow(6, n); 24 | List> ret = new ArrayList<>(); 25 | for(int i = n; i <= pointNum; ++i) { 26 | ret.add(new AbstractMap.SimpleEntry<>(i, dp[n][i] / totalNum)); 27 | } 28 | return ret; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/dp/lengthOfLongestSubstring_swordOffer48.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.dp; 2 | 3 | public class lengthOfLongestSubstring_swordOffer48 { 4 | 5 | public int lengthOfLongestSubstring(String s) { 6 | Map dic = new HashMap<>(); 7 | int res = 0, tmp = 0; 8 | for(int j = 0; j < s.length(); j++) { 9 | int i = dic.getOrDefault(s.charAt(j), -1); // 获取索引 i 10 | dic.put(s.charAt(j), j); // 更新哈希表 11 | tmp = tmp < j - i ? tmp + 1 : j - i; // dp[j - 1] -> dp[j] 12 | res = Math.max(res, tmp); // max(dp[j - 1], dp[j]) 13 | } 14 | return res; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/dp/maxValue_swordOffer47.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.dp; 2 | 3 | public class maxValue_swordOffer47 { 4 | 5 | public int maxValue(int[][] grid) { 6 | int row = grid.length, col = grid[0].length; 7 | // 初始化第一行 8 | for(int i = 1; i < col; ++i) { 9 | grid[0][i] += grid[0][i - 1]; 10 | } 11 | // 初始化第一列 12 | for(int j = 1; j < row; ++j) { 13 | grid[j][0] += grid[j - 1][0]; 14 | } 15 | for(int i = 1; i < row; ++i) { 16 | for(int j = 1; j < col; ++j) { 17 | grid[i][j] += Math.max(grid[i - 1][j], grid[i][j - 1]); 18 | } 19 | } 20 | return grid[row - 1][col - 1]; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/dp/nthUglyNumber_swordOffer49.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.dp; 2 | 3 | public class nthUglyNumber_swordOffer49 { 4 | 5 | public int nthUglyNumber(int n) { 6 | int[] dp = new int[n + 1]; 7 | dp[1] = 1; 8 | int p2 = 1, p3 = 1, p5 = 1; 9 | for(int i = 2; i <= n; ++i) { 10 | int num2 = dp[p2] * 2, num3 = dp[p3] * 3, num5 = dp[p5] * 5; 11 | dp[i] = Math.min(Math.min(num2, num3), num5); 12 | if(dp[i] == num2) { 13 | p2++; 14 | } 15 | if(dp[i] == num3) { 16 | p3++; 17 | } 18 | if(dp[i] == num5) { 19 | p5++; 20 | } 21 | } 22 | return dp[n]; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/greedyalgorithm/FindMinArrowShots452.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.greedyalgorithm; 2 | 3 | import java.util.Arrays; 4 | import java.util.Comparator; 5 | 6 | public class FindMinArrowShots452 { 7 | 8 | public static void main(String[] args) { 9 | int[][] points = {{10, 16}, {2, 8}, {1, 6}, {7, 12}}; 10 | System.out.println(findMinArrowShots(points)); 11 | } 12 | 13 | public static int findMinArrowShots(int[][] points) { 14 | if(points.length == 0) { 15 | return 0; 16 | } 17 | 18 | // Arrays.sort(points, Comparator.comparingInt(o -> o[1])); 19 | Arrays.sort(points, new Comparator() { 20 | @Override 21 | public int compare(int[] o1, int[] o2) { 22 | return (o1[1] < o2[1]) ? -1 : ((o1[1] == o2[1]) ? 0 : 1); 23 | } 24 | }); 25 | 26 | int count = 1; 27 | int end = points[0][1]; 28 | for(int i = 1; i < points.length; ++i) { 29 | if(points[i][0] <= end) { 30 | continue; 31 | } 32 | end = points[i][1]; 33 | count++; 34 | } 35 | return count; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/greedyalgorithm/cuttingRope_swordOffer14_1.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.greedyalgorithm; 2 | 3 | public class cuttingRope_swordOffer14_1 { 4 | 5 | /** 6 | * 尽可能得多剪长度为 3 的绳子,并且不允许有长度为 1 的绳子出现。如果出现了,就从已经切好长度为 3 的绳子 7 | * 中拿出一段与长度为 1 的绳子重新组合,把它们切成两段长度为 2 的绳子。以下为证明过程。 8 | * 9 | * 将绳子拆成 1 和 n-1,则 1(n-1)-n=-1<0,即拆开后的乘积一定更小,所以不能出现长度为 1 的绳子。 10 | * 11 | * 将绳子拆成 2 和 n-2,则 2(n-2)-n = n-4,在 n>=4 时这样拆开能得到的乘积会比不拆更大。 12 | * 13 | * 将绳子拆成 3 和 n-3,则 3(n-3)-n = 2n-9,在 n>=5 时效果更好。 14 | * 15 | * 将绳子拆成 4 和 n-4,因为 4=2*2,因此效果和拆成 2 一样。 16 | * 17 | * 将绳子拆成 5 和 n-5,因为 5=2+3,而 5<2*3,所以不能出现 5 的绳子,而是尽可能拆成 2 和 3。 18 | * 19 | * 将绳子拆成 6 和 n-6,因为 6=3+3,而 6<3*3,所以不能出现 6 的绳子,而是拆成 3 和 3。这里 6 同样 20 | * 可以拆成 6=2+2+2,但是 3(n - 3) - 2(n - 2) = n - 5 >= 0,在 n>=5 的情况下将绳子拆成 3 比拆成 2 效果更好。 21 | * 22 | * 继续拆成更大的绳子可以发现都比拆成 2 和 3 的效果更差,因此我们只考虑将绳子拆成 2 和 3,并且优先拆成 3,当拆到 23 | * 绳子长度 n 等于 4 时,也就是出现 3+1,此时只能拆成 2+2。 24 | */ 25 | public int cuttingRope(int n) { 26 | if(n < 2) return 0; 27 | if(n == 2) return 1; 28 | if(n == 3) return 2; 29 | int timesOf3 = n / 3; 30 | if((n - timesOf3 * 3) == 1) { 31 | timesOf3--; 32 | } 33 | int timesOf2 = (n - timesOf3 * 3) / 2; 34 | return (int) (Math.pow(3, timesOf3) * Math.pow(2, timesOf2)); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/greedyalgorithm/numRescueBoats881.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.greedyalgorithm; 2 | 3 | public class numRescueBoats881 { 4 | 5 | public int numRescueBoats(int[] people, int limit) { 6 | // 题型:贪心算法 + 双指针 7 | // 保证每个人都过河,那么最重的那个人最重只能刚好和船的limit一样重 8 | // 其他人体重只可能小于等于他 9 | int ans = 0; 10 | int light = 0, heavy = people.length - 1; 11 | Arrays.sort(people); 12 | while(light <= heavy) { 13 | // 在还没过河的人当中,最重的和最轻的一起如果没超过limit,就赶紧做一艘船一起过去 14 | if(people[light] +people[heavy] <= limit) { 15 | light++; // 最轻的人坐船过去了一个 16 | } 17 | // 否则说明两人一起超重了,这个最轻的人和最重的一起坐都超重了,那其他人和最重的这 18 | // 个人一起坐只会更重,没办法,只能让当前最重的这个人一人独自做船过去咯 19 | heavy--; // 和最轻的人一起过去或者自己太重了独自坐船过去 20 | ans++; // 无论哪种每趟都有人能过河消耗一艘船 21 | } 22 | return ans; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/linkedlist/AddTwoNumbers2.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.linkedlist; 2 | 3 | public class AddTwoNumbers2 { 4 | 5 | public ListNode addTwoNumbers(ListNode l1, ListNode l2) { 6 | ListNode pre = new ListNode(0); 7 | ListNode cur = pre; 8 | int carry = 0; 9 | while(l1 != null || l2 != null) { 10 | int x = l1 == null ? 0 : l1.val; 11 | int y = l2 == null ? 0 : l2.val; 12 | int sum = x + y + carry; 13 | carry = sum / 10; 14 | sum = sum % 10; 15 | cur.next = new ListNode(sum); 16 | cur = cur.next; 17 | if(l1 != null) { 18 | l1 = l1.next; 19 | } 20 | if(l2 != null) { 21 | l2 = l2.next; 22 | } 23 | } 24 | if(carry == 1) { 25 | cur.next = new ListNode(carry); 26 | } 27 | return pre.next; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/linkedlist/RemoveNthFromEnd19.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.linkedlist; 2 | 3 | public class RemoveNthFromEnd19 { 4 | 5 | public ListNode removeNthFromEnd(ListNode head, int n) { 6 | /* 7 | * 分析题意:本题需要介绍的一个方法叫快慢指针法,和传统意义的快慢指针不一样,这里的快慢两个指针 8 | * 每一次移动的步长是一样的,只是快指针先走n个步长替还没出发的慢指针先把n长度的距离量好,等量好 9 | * 快指针也不用回来,站在它量好的距离末端,此时慢指针开始从head出发,和快指针同步向后移动,当 10 | * 快指针移动到结尾为空结束循环。而此时刚刚一同出发的慢指针是不是刚好就走了 11 | * linkedlist.size() - n个步长,恰好落到倒数第n个节点前一个位置,此时我们就可以让慢指针的next 12 | * 指向慢指针的next.next这样就把倒数第n个节点给删除出去了。 13 | */ 14 | ListNode fast = head; 15 | while(n-- > 0) { 16 | fast = fast.next; 17 | } 18 | // 这里是边界条件判断,如果当前快指针指向的位置为空,可能是n = linkedlist.size(),fast指针直 19 | // 接到底了,slow指针指向head,直接让slow = head.next即可跳过head达到删除的目的 20 | if(fast == null) return head.next; 21 | ListNode slow = head; 22 | while(fast.next != null) { 23 | fast = fast.next; 24 | slow = slow.next; 25 | } 26 | // 做断链删除操作 27 | slow.next = slow.next.next; 28 | return head; 29 | } 30 | 31 | private class ListNode { 32 | int val; 33 | ListNode next; 34 | 35 | ListNode(int x) { 36 | val = x; 37 | next = null; 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/linkedlist/SwapPairs24.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.linkedlist; 2 | 3 | public class SwapPairs24 { 4 | 5 | public ListNode swapPairs(ListNode head) { 6 | /* 7 | * 分析题意:这里要做到两两交换,至少需要四个指针,当前节点,当前节点的前序节点,当前节点的后续节点, 8 | * 当前后续节点的后续节点,这样我们就可以断链交换了,注意我们这是在真实的操作,带着存储地址一起移动 9 | * 节点,货真价实,所以对应节点上的指针也是捆绑着一起走的。这点很重要! 10 | */ 11 | 12 | ListNode node = new ListNode(-1); 13 | node.next = head; 14 | ListNode pre = node; 15 | while(pre.next != null && pre.next.next != null) { 16 | // 指针的定位 17 | ListNode l1 = pre.next, l2 = pre.next.next; 18 | ListNode next = l2.next; 19 | 20 | // 开始断链交换 21 | l1.next = next; 22 | l2.next = l1; 23 | pre.next = l2; 24 | 25 | // pre移动到l1指针所在的节点上 26 | pre = l1; 27 | } 28 | return node.next; 29 | } 30 | 31 | private class ListNode { 32 | int val; 33 | ListNode next; 34 | 35 | ListNode(int x) { 36 | val = x; 37 | next = null; 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/linkedlist/copyRandomList_swordOffer35.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.linkedlist; 2 | 3 | public class copyRandomList_swordOffer35 { 4 | 5 | public Node copyRandomList(Node head) { 6 | if(head == null) return head; 7 | 8 | // step 1 并排复制原链表的所有节点,直接放在原相同节点后面 9 | Node cur = head; 10 | while(cur != null) { 11 | Node clone = new Node(cur.val); 12 | clone.next = cur.next; 13 | cur.next = clone; 14 | cur = clone.next; 15 | } 16 | 17 | // step 2 进一步深度复制原链表所有节点的random链接指向到我们复制的一一对应新节点上 18 | cur = head; 19 | while(cur != null) { 20 | Node cloneRandom = cur.next; 21 | if(cur.random != null) { 22 | cloneRandom.random = cur.random.next; 23 | } 24 | cur = cloneRandom.next; 25 | } 26 | 27 | // step 3 复制成功,把新复制的链表从原链表中剥离出来 28 | cur = head; 29 | Node cloneNewHead = head.next; 30 | while(cur.next != null) { 31 | Node nextNode = cur.next; 32 | cur.next = nextNode.next; 33 | cur = nextNode; 34 | } 35 | return cloneNewHead; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/linkedlist/deleteDuplicates_swordOffer82.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.linkedlist; 2 | 3 | public class deleteDuplicates_swordOffer82 { 4 | 5 | public ListNode deleteDuplicates(ListNode head) { 6 | // head为空或者head.next为空的边界判断 7 | if(head == null || head.next == null) return head; 8 | ListNode next = head.next; 9 | // 情况一,head开始就重复了,找到了与当前head相连的所有重复的元素 10 | if(head.val == next.val) { 11 | // 进一步判断还有没有重复的元素 12 | while(next != null && head.val == next.val) { 13 | next = next.next; 14 | } 15 | // 直到找到重复元素后面第一个元素,继续递归判断后面元素的重复性 16 | return deleteDuplicates(next); 17 | } else { 18 | // 当前head不重复,继续递归找下一个,判断后面的节点是否重复,最后不重复的节点,则都和head接上了 19 | // 如果后面有出现和非head重复的中间重复节点,都删除掉,让只出现一次的元素接到head后面即可 20 | head.next = deleteDuplicates(head.next); 21 | return head; 22 | } 23 | } 24 | 25 | private class ListNode { 26 | int val; 27 | ListNode next; 28 | 29 | ListNode(int x) { 30 | val = x; 31 | next = null; 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/other/StrToInt_swordOffer49.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.other; 2 | 3 | public class StrToInt_swordOffer49 { 4 | 5 | public int StrToInt(String str) { 6 | if (str == null || str.length() == 0) 7 | return 0; 8 | boolean isNegative = str.charAt(0) == '-'; 9 | int ret = 0; 10 | for (int i = 0; i < str.length(); i++) { 11 | char c = str.charAt(i); 12 | if (i == 0 && (c == '+' || c == '-')) /* 符号判定 */ 13 | continue; 14 | if (c < '0' || c > '9') /* 非法输入 */ 15 | return 0; 16 | ret = ret * 10 + (c - '0'); 17 | } 18 | return isNegative ? 19 | -ret : ret; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/other/add_swordOffer65.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.other; 2 | 3 | public class add_swordOffer65 { 4 | 5 | public int add(int a, int b) { 6 | /** 7 | * 分析题意:做两数加法运算还不能用加号,我们就可以尝试用位运算来代替加法运算。 8 | * 首先我们知道第一个数是a,第二个数是b。那么 a ^ b 表示的就是a和b做加法运算 9 | * 只是没有考虑进位,也就是说他们的二进制数,如果在相同位置碰到两个1,异或结果 10 | * 为0,和 1 + 1 进位后这一位为0一致,0 + 1 在异或中等于1也没有问题。最大的 11 | * 问题就是进位我们如何解决呢,我们这里的解决方案是 让 (a & b) << 1,验证一下 12 | * 如果a和b二进制的当前相同位置上都为1,那么 1 & 1 = 1,然后左移一位 13 | * 就变成了10,符合二进制数加法规律1 + 1 = 10, 14 | * 如果是 1 & 0 = 0,或者 0 & 0 = 0 向左进位为 00,即没有进位,也符合二进制 15 | * 加法规律,故验证成功 16 | * 我们可以用递归来写本题, 17 | * 递归让 a = a ^ b 表示不考虑进位的加法运算,b = (a & b) << 1 记录进位 18 | * 这样在下一趟递归时,整个加法运算就完整了,那么如何才能结束递归呢? 19 | * 我们分析下可能可以让递归终止的原因,(a & b) << 1 每执行一次,最右边会多一个 0, 20 | * 那么继续递归,进位最右边的 0 会慢慢增多,最后进位会变为 0,即b = 0时递归终止。 21 | */ 22 | return b == 0 ? a : add(a ^ b, (a & b) << 1); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/other/findNthDigit_swordOffer44.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.other; 2 | 3 | public class findNthDigit_swordOffer44 { 4 | 5 | public int findNthDigit(int n) { 6 | int digit = 1; // 当前数字所属的位数:eg 10的位数是2,100的位数是3,1000的位数是4... 7 | long start = 1; // 当前digit位连续数字中的第一个数字 1位的第一个数是1, 2位的第一个数是10, 3位的第一个数是100,..... 8 | long count = 9; // 当前 9 | while (n > count) { // 1.确定所求数位的所在数字的位数 10 | n -= count; 11 | digit += 1; 12 | start *= 10; 13 | count = digit * start * 9; 14 | } 15 | long num = start + (n - 1) / digit; // 2.确定所求数位所在的数字 16 | return Long.toString(num).charAt((n - 1) % digit) - '0'; // 3.确定所求数位在 num 的哪一数位 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/other/isNumber_swordOffer20.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.other; 2 | 3 | public class isNumber_swordOffer20 { 4 | 5 | public boolean isNumber(String s) { 6 | if(s == null || s.length() == 0) return false; // s为空对象或 s长度为0(空字符串)时, 不能表示数值 7 | boolean isNum = false, isDot = false, ise_or_E = false; // 标记是否遇到数位、小数点、‘e’或'E' 8 | char[] str = s.trim().toCharArray(); // 删除字符串头尾的空格,转为字符数组,方便遍历判断每个字符 9 | for(int i=0; i= '0' && str[i] <= '9') isNum = true; // 判断当前字符是否为 0~9 的数位 11 | else if(str[i] == '.') { // 遇到小数点 12 | if(isDot || ise_or_E) return false; // 小数点之前可以没有整数,但是不能重复出现小数点、或出现‘e’、'E' 13 | isDot = true; // 标记已经遇到小数点 14 | } 15 | else if(str[i] == 'e' || str[i] == 'E') { // 遇到‘e’或'E' 16 | if(!isNum || ise_or_E) return false; // ‘e’或'E'前面必须有整数,且前面不能重复出现‘e’或'E' 17 | ise_or_E = true; // 标记已经遇到‘e’或'E' 18 | isNum = false; // 重置isNum,因为‘e’或'E'之后也必须接上整数,防止出现 123e或者123e+的非法情况 19 | } 20 | else if(str[i] == '-' ||str[i] == '+') { 21 | if(i!=0 && str[i-1] != 'e' && str[i-1] != 'E') return false; // 正负号只可能出现在第一个位置,或者出现在‘e’或'E'的后面一个位置 22 | } 23 | else return false; // 其它情况均为不合法字符 24 | } 25 | return isNum; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/other/isStraight_swordOffer61.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.other; 2 | 3 | public class isStraight_swordOffer61 { 4 | 5 | public boolean isStraight(int[] nums) { 6 | if(nums.length < 5) return false; 7 | Arrays.sort(nums); 8 | 9 | // 统计癞子的个数 10 | int cnt = 0; 11 | for(int num : nums) { 12 | if(num == 0) { 13 | cnt++; 14 | } 15 | } 16 | 17 | for(int i = cnt; i < nums.length - 1; ++i) { 18 | if(nums[i + 1] == nums[i]) { 19 | return false; 20 | } 21 | // 尝试用癞子个数去抵消掉两个不连续数之间的差值 22 | cnt -= nums[i + 1] - nums[i] - 1; 23 | } 24 | // 癞子刚好补完或者有多的都满足题意 25 | return cnt >= 0; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/other/sumNums_swordOffer64.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.other; 2 | 3 | public class sumNums_swordOffer64 { 4 | 5 | public int sumNums(int n) { 6 | int sum = n; 7 | // 双与运算如果不满足第一个条件,第二个条件就不会执行,从而达到递归终止的目的 8 | boolean b = (n > 0) && ((sum += sumNums(n - 1)) > 0); 9 | return sum; 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/other/translateNum_swordOffer46.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.other; 2 | 3 | public class translateNum_swordOffer46 { 4 | 5 | public int translateNum(int num) { 6 | if(num <= 9) return 1; 7 | // 先按最大可截取的数字长度获取输入数字的余数,然后递归的计算翻译方法 8 | // 然后再分情况 9 | int num2abc = num % 100; 10 | // 如果小于等于9或者大于等于26的时候,余数不能按照2位数字组合,比如45, 11 | // 只能拆分为4和5;反例25,可以拆分为2和5,也可以作为25一个整体进行翻译 12 | if(num2abc <= 9 || num2abc >= 26) { 13 | return translateNum(num / 10); 14 | } else { 15 | // num2abc = [10, 25]时,既可以当做一个字母,也可以当做两个字母 16 | return translateNum(num / 10) + translateNum(num / 100); 17 | } 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/sort/minNumber_swordOffer45.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.sort; 2 | 3 | public class minNumber_swordOffer45 { 4 | 5 | public String minNumber(int[] nums) { 6 | int n = nums.length; 7 | String[] nums2str = new String[n]; 8 | for(int i = 0; i < n; ++i) { 9 | nums2str[i] = nums[i] + ""; 10 | } 11 | // 比较的是 S1+S2 和 S2+S1 的大小,如果 S1+S2 < S2+S1,那么应该把 S1 排在前面,否则应该把 S2 排在前面。 12 | Arrays.sort(nums2str, (s1, s2) -> (s1 + s2).compareTo(s2 + s1)); 13 | System.out.println(Arrays.toString(nums2str)); 14 | String ret = ""; 15 | for(String str : nums2str) { 16 | ret += str; 17 | } 18 | return ret; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/stackandqueue/DailyTemperatures739.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.stackandqueue; 2 | 3 | import java.util.Stack; 4 | 5 | public class DailyTemperatures739 { 6 | 7 | public int[] dailyTemperatures(int[] T) { 8 | /* 9 | * 分析题意:这里我们用到了栈的结构,并且栈中存储的是温度数值中的下标 10 | * 结果数组我们定义为dist[n], 大小n就是温度数组的长度 11 | * 遍历温度数组,当前遍历下标(记:curIndex)对应温度大于栈顶元素对应下标温度时,说明栈顶元素作为下标对应温 12 | * 度的下一个比它大的温度数就是当前元素。满足条件,就让栈顶元素(记:preIndex)弹出栈,其就是对应要存储位置 13 | * 的下标,然后对应下标的结果数组dist[preIndex] = curIndex - preIndex; 这样就巧妙的利用了下标差来当作 14 | * 下一次升温的天数。 15 | */ 16 | int n = T.length; 17 | int[] dist = new int[n]; 18 | Stack indexs = new Stack<>(); 19 | for(int curIndex = 0; curIndex < n; ++curIndex) { 20 | while(!indexs.isEmpty() && T[curIndex] > T[indexs.peek()]) { 21 | int preIndex = indexs.pop(); 22 | dist[preIndex] = curIndex - preIndex; 23 | } 24 | indexs.add(curIndex); 25 | } 26 | 27 | return dist; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/stackandqueue/NextGreaterElements503.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.stackandqueue; 2 | 3 | import java.util.Arrays; 4 | import java.util.Stack; 5 | 6 | public class NextGreaterElements503 { 7 | 8 | public int[] nextGreaterElements(int[] nums) { 9 | /* 10 | * 分析题意:这题和我们上一题(739.温度上升)那题差异在于需要记录的不是下一个更大数之间的间隔,而是获得下一个 11 | * 更大数的值,并且数组是循环数组,但是我们依然可以用栈来实现找到刚好比当前数大的下一个数是谁,模板不变,只是 12 | * 返回结果的内容由原来的间隔天数变成了这个数字之后的第一个比它更大的数。 13 | */ 14 | int n = nums.length; 15 | int[] dist = new int[n]; 16 | Stack preStack = new Stack<>(); 17 | Arrays.fill(dist, -1); 18 | for(int i = 0; i < n * 2; ++i) { 19 | int num = nums[i % n]; 20 | while(!preStack.isEmpty() && nums[preStack.peek()] < num) { 21 | dist[preStack.pop()] = num; 22 | } 23 | if(i < n) { 24 | preStack.push(i); 25 | } 26 | } 27 | return dist; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/stackandqueue/validateStackSequences_swordOffer31.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.stackandqueue; 2 | 3 | import java.util.Stack; 4 | 5 | public class validateStackSequences_swordOffer31 { 6 | 7 | public boolean validateStackSequences(int[] pushed, int[] popped) { 8 | int n = pushed.length; 9 | Stack stack = new Stack<>(); 10 | for(int pushIndex = 0, popIndex = 0; pushIndex < n; ++pushIndex) { 11 | stack.push(pushed[pushIndex]); 12 | while(popIndex < n && !stack.isEmpty() 13 | && stack.peek() == popped[popIndex]) { 14 | stack.pop(); 15 | popIndex++; 16 | } 17 | } 18 | return stack.isEmpty(); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/string/Convert6.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.string; 2 | 3 | public class Convert6 { 4 | 5 | public String convert(String s, int numRows) { 6 | 7 | /* 8 | * 分析题意:通过从左向右迭代字符串s,我们可以轻松地确定字符s位于 Z 字形图案中的哪一行 9 | * ,我们可以使用 min(numRows,len(s)) 个列表来表示 Z 字形图案中的非空行共有多少行。 10 | * 从左到右迭代 s,将每个字符添加到合适的行。可以使用当前行和当前方向这两个变量对合适 11 | * 的行进行跟踪。 12 | * 只有当我们向上移动到最上面的行或向下移动到最下面的行时,当前方向才会发生改变。 13 | * 可以理解为从右到左 之字形 玩俄罗斯方块,每次方块都从右边边缘出现,一直落到左边的边缘。 14 | */ 15 | 16 | if(numRows == 1) return s; 17 | 18 | List rows = new ArrayList<>(); 19 | for(int i = 0; i < Math.min(numRows, s.length()); ++i) { 20 | rows.add(new StringBuilder()); 21 | } 22 | 23 | int curRow = 0; 24 | boolean goingDown = false; 25 | 26 | for(char c : s.toCharArray()) { 27 | rows.get(curRow).append(c); 28 | if(curRow == 0 || curRow == numRows - 1) goingDown = !goingDown; 29 | curRow += goingDown ? 1 : -1; 30 | } 31 | 32 | StringBuilder res = new StringBuilder(); 33 | for(StringBuilder sb : rows) { 34 | res.append(sb); 35 | } 36 | return res.toString(); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/string/CountSubstrings647.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.string; 2 | 3 | public class CountSubstrings647 { 4 | 5 | private int cnt = 0; 6 | public int countSubstrings(String s) { 7 | /* 8 | * 分析题意:计算所有可能的子回文字符串,我们可以从头遍历字符串s,然后每一次都让双指针指向当前遍历的字符位置 9 | * 以它为中点位置恰好切割回文串两边,指针向两边移动,只要满足对称位置相等我们就记录一个子串为一个回文串。 10 | * 注意回文字符串有两种: 11 | * 情况一:回文串是奇数个长度,那中点就是一个,在正中间 12 | * 情况二:回文串是偶数个长度,那中点就是当前位置和当前位置的下一个位置相邻对半分回文串 13 | */ 14 | for(int i = 0; i < s.length(); ++i) { 15 | extendSubStrings(s, i, i); // 回文串为奇数时的情况 16 | extendSubStrings(s, i, i + 1);// 回文串为偶数的情况 17 | } 18 | return cnt; 19 | } 20 | 21 | private void extendSubStrings(String s, int start, int end) { 22 | while(start >= 0 && end < s.length() && s.charAt(start) == s.charAt(end)) { 23 | start--; 24 | end++; 25 | cnt++; 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/string/GenerateParenthesis22.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.string; 2 | 3 | public class GenerateParenthesis22 { 4 | 5 | public List generateParenthesis(int n) { 6 | // 分析题意:深度优先遍历去匹配左右括号 7 | List res = new ArrayList<>(); 8 | dfs("", n, n, res); 9 | return res; 10 | } 11 | 12 | 13 | /** 14 | * @param curStr 当前递归得到的结果 15 | * @param left 左括号还有几个可以使用 16 | * @param right 右括号还有几个可以使用 17 | * @param res 结果集 18 | */ 19 | private void dfs(String curStr, int left, int right, List res) { 20 | // 因为每一次尝试,都使用新的字符串变量,所以无需回溯 21 | // 在左边和右边剩余的括号数都等于 0 的时候结算 22 | if(left == 0 && right == 0) { 23 | res.add(curStr); 24 | return; 25 | } 26 | 27 | // 剪枝(如图,左括号可以使用的个数严格大于右括号可以使用的个数,才剪枝,注意这个细节) 28 | if(left > right) { 29 | return; 30 | } 31 | 32 | if(left > 0) { 33 | dfs(curStr + "(", left - 1, right, res); 34 | } 35 | 36 | if(right > 0) { 37 | dfs(curStr + ")", left, right - 1, res); 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/string/IntToRoman12.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.string; 2 | 3 | public class IntToRoman12 { 4 | 5 | int[] values = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}; 6 | String[] symbols = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"}; 7 | public String intToRoman(int num) { 8 | 9 | /** 10 | * 分析题意:根据罗马数字的唯一表示法,为了表示一个给定的整数 num,我们寻找不超过 num 的最大符号值, 11 | * 将num 减去该符号值,然后继续寻找不超过 num 的最大符号值,将该符号拼接在上一个找到的符号之后,循环 12 | * 直至 num 为 0。最后得到的字符串即为 num 的罗马数字表示。 13 | * 编程时,可以建立一个数值-符号对的列表 valueSymbols,按数值从大到小排列。遍历 valueSymbols 中的 14 | * 每个数值-符号对,若当前数值 value 不超过 num,则从 num 中不断减去 value,直至 num 小于 value, 15 | * 然后遍历下一个数值-符号对。若遍历中 num 为 0 则跳出循环。 16 | */ 17 | 18 | StringBuilder sb = new StringBuilder(); 19 | for(int i = 0; i < values.length; ++i) { 20 | while(num >= values[i]) { 21 | num -= values[i]; 22 | sb.append(symbols[i]); 23 | } 24 | if(num == 0) { 25 | break; 26 | } 27 | } 28 | return sb.toString(); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/string/LengthOfLongestSubstring3.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.string; 2 | 3 | public class LengthOfLongestSubstring3 { 4 | 5 | public int lengthOfLongestSubstring(String s) { 6 | // 这题解题思路用到滑动窗口的思路 7 | // 从第一个字符开始创建一个候选的最长字串的窗口,然后尝试向右不 8 | // 断滑动窗口找到更长的串 9 | Set set = new HashSet<>(); 10 | int n = s.length(); 11 | int rt = -1, ans = 0; 12 | for(int i = 0; i < n; ++i) { 13 | // 窗口左边界向右移动一步,移除一个字符,尝试找到新的更长的 14 | // 不重复子串 15 | if(i != 0) { 16 | set.remove(s.charAt(i - 1)); 17 | } 18 | 19 | // 不断延长窗口右边界,尝试找到更长的不重复子串区间 20 | while(rt + 1 < n && !set.contains(s.charAt(rt + 1))) { 21 | set.add(s.charAt(rt + 1)); 22 | rt++; 23 | } 24 | ans = Math.max(ans, rt - i + 1); 25 | } 26 | return ans; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/string/Multiply43.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.string; 2 | 3 | public class Multiply43 { 4 | 5 | public String multiply(String num1, String num2) { 6 | if(num1.equals("0") || num2.equals("0")) { 7 | return "0"; 8 | } 9 | int m = num1.length(), n = num2.length(); 10 | int[] res = new int[m + n]; 11 | for(int i = m - 1; i >= 0; --i) { 12 | int n1 = num1.charAt(i) - '0'; 13 | for(int j = n - 1; j >=0; --j) { 14 | int n2 = num2.charAt(j) - '0'; 15 | // 根据乘法竖式的规律,按位进行相乘 16 | int sum = (res[i + j + 1] + n1 * n2); 17 | // 两数相乘后的结果中这一位上需要更新的值 18 | res[i + j + 1] = sum % 10; 19 | // 把前一位的进位加上 20 | res[i + j] += sum / 10; 21 | } 22 | } 23 | 24 | StringBuilder sb = new StringBuilder(); 25 | for(int i = 0; i < res.length; ++i) { 26 | if(i == 0 && res[i] == 0) continue; 27 | sb.append(res[i]); 28 | } 29 | return sb.toString(); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/string/MyAtoi8.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.string; 2 | 3 | public class MyAtoi8 { 4 | 5 | int sign = 1; 6 | int ans = 0, pop = 0; 7 | boolean hasSign = false; // 代表是否开始转换数字,用来过滤掉非数字 8 | for(int i = 0; i < str.length(); ++i) { 9 | // 先判断正负号 10 | if(str.charAt(i) == '-' && !hasSign) { 11 | sign = -1; 12 | hasSign = true; 13 | continue; 14 | } 15 | if(str.charAt(i) == '+' && !hasSign) { 16 | sign = 1; 17 | hasSign = true; 18 | continue; 19 | } 20 | // 过滤空格 21 | if(str.charAt(i) == ' ' && !hasSign) { 22 | continue; 23 | } 24 | 25 | // 合法的数字做数字转换 26 | if(str.charAt(i) >= '0' && str.charAt(i) <= '9') { 27 | hasSign = true; 28 | pop = str.charAt(i) - '0'; 29 | // 判断有符号整型边界溢出问题 30 | if(ans * sign > Integer.MAX_VALUE / 10 || (ans * sign == Integer.MAX_VALUE / 10 && pop * sign > 7)) { 31 | return 2147483647; 32 | } 33 | if(ans * sign < Integer.MIN_VALUE / 10 || (ans * sign == Integer.MIN_VALUE / 10 && pop * sign < -8)) { 34 | return -2147483648; 35 | } 36 | ans = ans * 10 + pop; 37 | } else { 38 | return ans * sign; 39 | } 40 | } 41 | return ans * sign; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/tree/ConvertBST538.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.tree; 2 | 3 | public class ConvertBST538 { 4 | 5 | 6 | private int sum = 0; 7 | 8 | public TreeNode convertBST(TreeNode root) { 9 | traver(root); 10 | return root; 11 | } 12 | 13 | private void traver(TreeNode root) { 14 | if(root == null) return; 15 | traver(root.right); 16 | sum += root.val; 17 | root.val = sum; 18 | traver(root.left); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/tree/FindBottomLeftValue513.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.tree; 2 | 3 | import java.util.*; 4 | 5 | public class FindBottomLeftValue513 { 6 | 7 | public int findBottomLeftValue(TreeNode root) { 8 | /* 9 | * 分析题意:要求左下角的值,并且题目描述的左下角指的是最深的一层的第一个左节点。那既然要考虑到层次,所以 10 | * 我们优先想到层序遍历,可以使用队列来实现 11 | */ 12 | Queue queue = new LinkedList<>(); 13 | // 题目规定根节点不为空 14 | queue.offer(root); 15 | // 按层入队和出队 16 | while(!queue.isEmpty()) { 17 | // 保证root指针最后指向从队列出队的最后一个不为空的节点 18 | root = queue.poll(); 19 | // 根据队列后进后出的原则,我们要最后取到的是左节点,故先让右子树先进队列,左子树后进去 20 | if(root.right != null) queue.offer(root.right); 21 | if(root.left != null) queue.offer(root.left); 22 | } 23 | // 循环结束后root就指向了最深层的第一个左下角的值了 24 | return root.val; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/tree/FindLongest687.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.tree; 2 | 3 | public class FindLongest687 { 4 | 5 | private int path = 0; 6 | 7 | public int longestUnivaluePath(TreeNode root) { 8 | // 递归去找同值节点最大深度,初始化从根节点开始尝试 9 | findLongest(root); 10 | return path; 11 | } 12 | 13 | private int findLongest(TreeNode root) { 14 | if(root == null) return 0; 15 | // 递归找左子树和root同值路径 16 | int left = findLongest(root.left); 17 | // 递归找右子树与root同值路径 18 | int right = findLongest(root.right); 19 | // 下面两行合起来找左子数与root与右子树同值路径 20 | int leftPath = (root.left != null && root.left.val == root.val) ? left + 1 : 0; 21 | int rightPath = (root.right != null && root.right.val == root.val) ? right + 1 : 0; 22 | // 比较当前已经存在的最大同值路径和上面三种可能的同值路径情况比较取最大的返回 23 | // leftPath和rightPath都并不为零 || leftPath为零rightPath不为零 || letfPath不为零rightPath为零 24 | path = Math.max(path, leftPath + rightPath); 25 | // 这里是findLongest自己递归自己的调用返回,接受的是left或者right这两种情况,所以要比较左右子数选择 26 | // 最大的同值路径返回 27 | return Math.max(leftPath, rightPath); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/tree/GetNext_swordOffer57.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.tree; 2 | 3 | public class GetNext_swordOffer57 { 4 | 5 | 6 | public TreeLinkNode GetNext(TreeLinkNode pNode) { 7 | /** 8 | * * 题目:二叉树的下一个结点 9 | * 分析题意:中序遍历的过程:先遍历树的左子树,再遍历根节点,最后再遍历右子树。 10 | * 所以最左节点是中序遍历的第一个节点。 11 | * 本题要找的有两种情况 12 | * 1. 如果一个节点的右子树不为空,那么该节点的下一个节点是右子树的最左节点; 13 | * 2. 否则,向上找第一个左链接指向的树包含该节点的祖先节点。 14 | */ 15 | if(pNode.right != null) { 16 | TreeLinkNode node = pNode.right; 17 | while(node.left != null) { 18 | node = node.left; 19 | } 20 | return node; 21 | } else { 22 | while(pNode.next != null) { 23 | TreeLinkNode parent = pNode.next; 24 | if(parent.left == pNode) { 25 | return parent; 26 | } 27 | pNode = pNode.next; 28 | } 29 | } 30 | return null; 31 | } 32 | 33 | 34 | public class TreeLinkNode { 35 | 36 | int val; 37 | TreeLinkNode left = null; 38 | TreeLinkNode right = null; 39 | TreeLinkNode next = null; // 指向父结点的指针 40 | 41 | TreeLinkNode(int val) { 42 | this.val = val; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/tree/KthSmallest230.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.tree; 2 | 3 | public class KthSmallest230 { 4 | 5 | private int val; 6 | private int count = 0; 7 | 8 | public int kthSmallest(TreeNode root, int k) { 9 | /* 10 | * 分析题意:题目要求在二叉搜索树中找第k小的元素,首先我们想到的是要先dfs,但是用哪一种遍历最合适呢? 11 | * 这里我们要用到一个关键信息点就是二叉搜索树的性质,中序遍历的有序性!就知道应该用中序遍历最合适, 12 | * 左 -> 根 -> 右 二叉搜索树中左子树最小,其次是根,然后是右子树,就是一个升序数列了。本题要求找第 13 | * k小,那么我们就从中序最小节点开始计数,当 count == k 时就可以马上结束遍历收敛回去,直接去对应位置 14 | * 节点值val回去即可 15 | */ 16 | inOrder(root, k); 17 | return val; 18 | } 19 | 20 | private void inOrder(TreeNode node, int k) { 21 | if(node == null) return; 22 | inOrder(node.left, k); 23 | count++; 24 | if(count == k) { 25 | val = node.val; 26 | return; 27 | } 28 | inOrder(node.right, k); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/tree/LowestCommonAncestor235.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.tree; 2 | 3 | public class LowestCommonAncestor235 { 4 | 5 | public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { 6 | if(root.val > p.val && root.val > q.val) return lowestCommonAncestor(root.left, p, q); 7 | if(root.val < p.val && root.val < q.val) return lowestCommonAncestor(root.right, p, q); 8 | return root; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/tree/LowestCommonAncestor236.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.tree; 2 | 3 | public class LowestCommonAncestor236 { 4 | 5 | public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { 6 | /* 7 | * 分析题意:本题不同于二叉搜索树,仅仅是一颗无顺序规律的二叉树,那就需要考虑为空的情况, 8 | * 如果不为空,我们需要递归遍历所有节点,然后反向返回判断当前节点和p,q之间的关系,如果 9 | * 满足是公共,那么一定是最近的公共祖先,也就是深度最大的公共祖先,因为我们是dfs之后反 10 | * 向收敛回去的,从叶子节点自底向上,最先找到的公共祖先符合题意。 20210720 复习一次 11 | */ 12 | 13 | // 先做收敛条件判断,如果root为空题目说了最少右两个节点或者更多,那dfs的时候,root为空了 14 | // 只能说明到叶子节点了,需要我们反向返回了,根据题目说的,p,q,一定在二叉树上,不在外面, 15 | // 那root能为空,说明p或q或者q和q在叶子节点上,到最深下届了,我们需要返回收敛了。或者是当 16 | // 前节点等于p或q中的一个,此时这条路径虽然可能还没到叶子节点,但我们是找公共祖先,一定是 17 | // 在当前p和q的上面去找才可能找到公共祖先,这也是dfs提前收敛的很好的边界判断,防止资源浪费 18 | if(root == null || root == p || root == q) return root; 19 | 20 | // 还没找到,就继续dfs左右子树 21 | TreeNode left = lowestCommonAncestor(root.left, p, q); 22 | TreeNode right = lowestCommonAncestor(root.right, p, q); 23 | 24 | // 左右子树其中一个到底了就继续探寻另一颗,反之亦然,或者是左右节点都不为空,说明p,q都找到了 25 | // 分别在左右子树上,那当前节点一定是最近公共祖先,或者是公共祖先是p,q中的其中一个,另一个 26 | // 一定存在与公共祖先左右子树中的的一边,才行,否则公共祖先只能继续向更浅的深度继续反向收敛 27 | // 上去(也就是找到更大的祖先接单判断其左右子树是否包含了p、q) 28 | return left == null ? right : right == null ? left : root; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/tree/NumTrees96.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.tree; 2 | 3 | public class NumTrees96 { 4 | 5 | public int numTrees(int n) { 6 | /** 7 | * dp解法,参考官方题解的思路 8 | */ 9 | int[] G = new int[n + 1]; 10 | G[0] = 1; 11 | G[1] = 1; 12 | for(int i = 2; i <= n; ++i) { 13 | for(int j = 1; j <= i; ++j) { 14 | G[i] += G[j - 1] * G[i - j]; 15 | } 16 | } 17 | return G[n]; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/tree/PostorderTraversal145.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.tree; 2 | import java.util.*; 3 | 4 | public class PostorderTraversal145 { 5 | 6 | public List postorderTraversal(TreeNode root) { 7 | // // 方法一 递归实现 8 | // List res = new ArrayList<>(); 9 | // postOrder(root, res); 10 | // return res; 11 | 12 | // 非递归Stack方法实现 13 | // 我们分析下前序非递归Stack写法,顺序是 root->left->right, 现在我们需要 left->right->root 14 | // 是不是可以把前序的 left 和 right 对调后变成 root->right-left 15 | // 然后反转变成 left->right->root 16 | List res = new ArrayList<>(); 17 | Stack stack = new Stack<>(); 18 | stack.push(root); 19 | while(!stack.isEmpty()) { 20 | TreeNode node = stack.pop(); 21 | if(node == null) continue; 22 | res.add(node.val); 23 | stack.push(node.left); 24 | stack.push(node.right); 25 | } 26 | Collections.reverse(res); 27 | return res; 28 | } 29 | 30 | // // 方法一 递归实现 31 | // private void postOrder(TreeNode root, List res) { 32 | // if(root == null) return; 33 | // postOrder(root.left, res); 34 | // postOrder(root.right, res); 35 | // res.add(root.val); 36 | // } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/tree/PreorderTraversal144.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.tree; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Stack; 6 | 7 | public class PreorderTraversal144 { 8 | 9 | public List preorderTraversal(TreeNode root) { 10 | // // 方法一 递归写法 11 | // List res = new ArrayList<>(); 12 | // preOrder(root, res); 13 | // return res; 14 | 15 | // 方法二 非递归写法 我们用栈来实现 16 | List res = new ArrayList<>(); 17 | Stack stack = new Stack<>(); 18 | stack.push(root); 19 | while(!stack.isEmpty()) { 20 | TreeNode node = stack.pop(); 21 | if(node == null) continue; 22 | res.add(node.val); 23 | stack.push(node.right); // 栈先进后出,而我们要根左右,所以先right进再left进取的时候就是前序 24 | stack.push(node.left); 25 | } 26 | return res; 27 | } 28 | 29 | // // 方法一 递归写法 30 | // private void preOrder(TreeNode root, List res) { 31 | // if(root == null) return; 32 | // res.add(root.val); 33 | // preOrder(root.left, res); 34 | // preOrder(root.right, res); 35 | // } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/tree/TreeNode.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.tree; 2 | 3 | public class TreeNode { 4 | int val; 5 | TreeNode left; 6 | TreeNode right; 7 | TreeNode() {} 8 | TreeNode(int val) { this.val = val; } 9 | TreeNode(int val, TreeNode left, TreeNode right) { 10 | this.val = val; 11 | this.left = left; 12 | this.right = right; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/tree/TrimBST669.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.tree; 2 | 3 | public class TrimBST669 { 4 | 5 | public TreeNode trimBST(TreeNode root, int low, int high) { 6 | /* 7 | * 分析题意:本题需要对树进行裁剪操作,如果不满足题意范围的节点值对应的节点,我们就需要做中断和重连接的操作 8 | * 这里的断开重连操作可以类比我们前面的链表断开重连来理解。然后再来分析树的结构,是一颗搜索二叉树,那根据搜 9 | * 索二叉树的性质,比当前节点值大的节点一定在该节点右子树范围内取探寻,比当前节点值小的节点在左子树范围内去 10 | * 探寻(题目规定节点值唯一,也就是不存在重复值的节点),那我们尝试用dfs探寻的时候每次比较都可以筛选淘汰当前 11 | * 节点相连的一半的子树节点,只去探寻在(low, high)内的一边子树即可,到底后返回裁剪即可。 12 | * 也就是说我们只保留值在 low ~ high 之间的节点即可。 13 | */ 14 | 15 | // 每日一趟dfs先判断当前接单是否为空,为空说明到底了开始收敛返回,不用进行后续操作 16 | if(root == null) return null; 17 | // 如果当前节点不在 low ~ high 范围内就裁剪掉范围外的那一边(包括当前节点不满足在范围内也一起裁剪掉),收敛 18 | // 返回拼接其满足范围内的那一边子树节点给它的父亲节点,防止断裂丢失,组成一颗新的搜索树 19 | if(root.val > high) return trimBST(root.left, low, high); 20 | if(root.val < low) return trimBST(root.right, low, high); 21 | 22 | // 在范围内的节点就证明在 low ~ high 之间,继续dfs探寻他的左右孩子节点即可 23 | root.left = trimBST(root.left, low, high); 24 | root.right = trimBST(root.right, low, high); 25 | return root; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/tree/levelOrder_swordOffer32_1.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.tree; 2 | 3 | public class levelOrder_swordOffer32_1 { 4 | 5 | public int[] levelOrder(TreeNode root) { 6 | Queue queue = new LinkedList<>(); 7 | List ret_list = new ArrayList<>(); 8 | queue.add(root); 9 | while(!queue.isEmpty()) { 10 | int cnt = queue.size(); 11 | while(cnt-- > 0) { 12 | TreeNode t = queue.poll(); 13 | if(t == null) continue; 14 | ret_list.add(t.val); 15 | queue.add(t.left); 16 | queue.add(t.right); 17 | } 18 | } 19 | int[] ret = new int[ret_list.size()]; 20 | int idx = 0; 21 | for(int num : ret_list) { 22 | ret[idx++] = num; 23 | } 24 | return ret; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/tree/levelOrder_swordOffer32_3.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.tree; 2 | 3 | public class levelOrder_swordOffer32_3 { 4 | 5 | public List> levelOrder(TreeNode root) { 6 | List> list = new ArrayList<>(); 7 | Queue queue = new LinkedList<>(); 8 | queue.add(root); 9 | boolean reverse = false; 10 | while(!queue.isEmpty()) { 11 | int cnt = queue.size(); 12 | List t_list = new ArrayList<>(); 13 | while(cnt-- > 0) { 14 | TreeNode t = queue.poll(); 15 | if(t == null) continue; 16 | t_list.add(t.val); 17 | queue.add(t.left); 18 | queue.add(t.right); 19 | } 20 | if(t_list.size() != 0) { 21 | if(reverse) { 22 | Collections.reverse(t_list); 23 | } 24 | list.add(t_list); 25 | } 26 | reverse = !reverse; 27 | } 28 | return list; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/tree/pathSum_swordOffer34.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.tree; 2 | 3 | public class pathSum_swordOffer34 { 4 | 5 | private List> ret = new ArrayList<>(); 6 | 7 | public List> pathSum(TreeNode root, int target) { 8 | backTracking(root, target, new ArrayList<>()); 9 | return ret; 10 | } 11 | 12 | private void backTracking(TreeNode node, int target, List path) { 13 | if(node == null) return; 14 | path.add(node.val); 15 | target -= node.val; 16 | if(target == 0 && node.left == null && node.right == null) { 17 | ret.add(new ArrayList<>(path)); 18 | } else { 19 | backTracking(node.left, target, path); 20 | backTracking(node.right, target, path); 21 | } 22 | path.remove(path.size() - 1); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/tree/treeToDoublyList_swordOffer36.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.tree; 2 | 3 | public class treeToDoublyList_swordOffer36 { 4 | 5 | // 中序遍历,声明两个指针,pre指向当前节点的前序节点,head指向双向链表全局头结点位置 6 | Node pre, head; 7 | public Node treeToDoublyList(Node root) { 8 | // 边界值判断 9 | if(root == null) return null; 10 | // 中序遍历 递归二叉搜索树 11 | inOrder(root); 12 | 13 | // 最后首尾相连,形成环形链表 14 | head.left = pre; 15 | pre.right = head; 16 | 17 | // 返回头结点 18 | return head; 19 | } 20 | 21 | private void inOrder(Node cur) { 22 | // 到叶子了开始收敛 23 | if(cur == null) return; 24 | // 先递归左子树 25 | inOrder(cur.left); 26 | 27 | // 根据二叉搜索树中序遍历的顺序性 28 | // 取当前节点作为根节点,向前(left)的节点比他小,向后(right)的节点比他大 29 | 30 | if(pre == null) { 31 | // 刚开始第一个节点没有前序节点,故pre为空,我们先让head做标记,方便后面返回结果 32 | head = cur; 33 | } else if(pre != null) { 34 | // 如果pre不为空就是非首节点,为中间节点,此时pre指向了上一个节点, 35 | // 我们让上一个节点的右指针指向当前节点 36 | pre.right = cur; 37 | // 让当前节点的左指针指向上一个节点,也就形成了双向链表 38 | cur.left = pre; 39 | } 40 | // 让pre指针后移一位,保存当前节点,方便遍历下一个节点和当前节点继续连接 41 | pre = cur; 42 | // 最后递归右子树 43 | inOrder(cur.right); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/twopoint/JudgeSquareSum633.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.twopoint; 2 | 3 | public class JudgeSquareSum633 { 4 | 5 | public static void main(String[] args) { 6 | System.out.println(judgeSquareSum(3)); 7 | } 8 | 9 | public static boolean judgeSquareSum(int c) { 10 | if(c < 0) { 11 | return false; 12 | } 13 | int low = 0, high = (int) Math.sqrt(c); 14 | while(low <= high) { 15 | int sum = low * low + high * high; 16 | if(sum == c) { 17 | return true; 18 | }else if(sum < c) { 19 | low++; 20 | }else { 21 | high--; 22 | } 23 | } 24 | return false; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /leetcodetest/src/part_1/medium/twopoint/ThreeSumClosest16.java: -------------------------------------------------------------------------------- 1 | package part_1.medium.twopoint; 2 | 3 | public class ThreeSumClosest16 { 4 | 5 | public int threeSumClosest(int[] nums, int target) { 6 | int res = 0; 7 | int min = Integer.MAX_VALUE; 8 | Arrays.sort(nums); 9 | int n = nums.length; 10 | for(int i = 0; i < n - 2; ++i) { 11 | if(i > 0 && nums[i] == nums[i - 1]) continue; 12 | int l = i + 1, r = n - 1; 13 | while(l < r) { 14 | int sum = nums[i] + nums[l] + nums[r]; 15 | if(sum == target) { 16 | return sum; 17 | } 18 | if(Math.abs(sum - target) < min) { 19 | min = Math.abs(sum - target); 20 | res = sum; 21 | } 22 | if(sum < target) { 23 | l++; 24 | } else { 25 | r--; 26 | } 27 | } 28 | } 29 | return res; 30 | 31 | } 32 | --------------------------------------------------------------------------------