├── README.md └── src └── com └── leetcode_cn ├── easy ├── AddBinary.java ├── AddWithoutPlus.java ├── Base7.java ├── BestTimeToBuyAndSellStockII.java ├── BinaryTreeLevelOrderTraversalII.java ├── BinaryWatch.java ├── ClimbingStairs.java ├── ConvertSortedArrayToBinarySearchTree.java ├── CountAndSay.java ├── CountPrimes.java ├── DeleteNodeInALinkedList.java ├── DesignCircularDeque.java ├── DestinationCity.java ├── DetectCapital.java ├── DiameterOfBinaryTree.java ├── DuiChengErChaShu.java ├── FairCandySwap.java ├── Fib.java ├── FindAllAnagramsInAString.java ├── FindAllNumbersDisappearedInAnArray.java ├── FindPivotIndex.java ├── FindTheDifference.java ├── FindWordsByCharacters.java ├── FlippingAnImage.java ├── GuessNumbers.java ├── HammingDistance.java ├── HappyNumber.java ├── ImplementQueueUsingStacks.java ├── ImplementStr.java ├── IncreasingDecreasingString.java ├── InvertBinaryTree.java ├── IslandPerimeter.java ├── IsomorphicStrings.java ├── JudgeRouteCircle.java ├── KDiffPairsInAnArray.java ├── KeyboardRow.java ├── KthLargest.java ├── LargestTriangleArea.java ├── LeafSimilarTrees.java ├── LengthOfLastWord.java ├── LetterCasePermutation.java ├── LongestCommonPrefix.java ├── LongestUnivaluePath.java ├── LongestWordInDictionary.java ├── MaxAreaOfIsland.java ├── MaximumDepthOfBinaryTree.java ├── MaximumProductOfThreeNumbers.java ├── MaximumSubarray.java ├── MergeSortedArray.java ├── MergeTwoSortedLists.java ├── MiddleOfTheLinkedList.java ├── MinimumAbsoluteDifference.java ├── MinimumIndexSumOfTwoLists.java ├── MissingNumber.java ├── NetworkDelayTime.java ├── NonDecreasingArray.java ├── One2N.java ├── PalindromeNumber.java ├── PascalsTriangleII.java ├── PlusOne.java ├── PowerOfFour.java ├── PrimeNumberOfSetBitsInBinaryRepresentation.java ├── RemoveDuplicatesFromSortedArray.java ├── RemoveDuplicatesFromSortedList.java ├── RemoveElement.java ├── RepeatedStringMatch.java ├── ReverseInteger.java ├── ReverseLinkedList.java ├── ReverseVowelsOfAString.java ├── RomanToInteger.java ├── RotateArray.java ├── RotateString.java ├── SameNum.java ├── SameTree.java ├── SearchInserPosition.java ├── SortIntegersByTheNumberOfOneBits.java ├── SparseArraySearchLcci.java ├── Sqrtx.java ├── SymmetricTree.java ├── TransposeMatrix.java ├── TwoSum.java ├── TwoSumIIInputArrayIsSorted.java ├── UncommonWordsFromTwoSentences.java └── ValidParentheses.java ├── hard ├── BestTimeToBuyAndSellStockIII.java ├── BinaryTreePostorderTraversal.java ├── BusRoutes.java ├── CountDifferentPalindromicSubsequences.java ├── DungeonGame.java ├── FallingSquares.java ├── FindKthSmallestPairDistance.java ├── FindMinimumInRotatedSortedArrayII.java ├── FindTheClosestPalindrome.java ├── FirstMissingPositive.java ├── FrogJump.java ├── IPO.java ├── InsertInterval.java ├── IntegerToEnglishWords.java ├── JumpGameII.java ├── KInversePairsArray.java ├── LongestValidParentheses.java ├── MaxPointsOnALine.java ├── MaxSumOfRectangleNoLargerThanK.java ├── MedianOfTwoSortedArrays.java ├── MergeKSortedLists.java ├── MinimumNumberOfRefuelingStops.java ├── NQueens.java ├── NQueensII.java ├── NumberOfAtoms.java ├── PalindromePairs.java ├── PalindromePartitioningII.java ├── PostorderTraversal.java ├── PrefixAndSuffixSearch.java ├── PreimageSizeOfFactorialZeroesFunction.java ├── ProfitableSchemes.java ├── RandomPickWithBlacklist.java ├── RegularExpressionMatching.java ├── ReverseNodesInKGroup.java ├── SlidingWindowMaximum.java ├── SubstringWithConcatenationOfAllWords.java ├── SudokuSolver.java ├── SuperEggDrop.java ├── SwimInRisingWater.java ├── TextJustification.java ├── TrappingRainWater.java └── WildcardMatching.java └── medium ├── AddTwoNumbers.java ├── ArrayNesting.java ├── BeautifulArray.java ├── BestTimeToBuyAndSellStockWithTransactionFee.java ├── BinaryTreeInorderTraversal.java ├── BinaryTreeLevelOrderTraversal.java ├── BinaryTreePreorderTraversal.java ├── BinaryTreeZigzagLevelOrderTraversal.java ├── BulbSwitcher.java ├── BullsAndCows.java ├── CarFleet.java ├── CoinChange2.java ├── CombinationSum.java ├── CombinationSumII.java ├── CombinationSumIII.java ├── CombinationSumIV.java ├── Combinations.java ├── CompareVersionNumbers.java ├── ConstructBinaryTreeFromPreorderAndInorderTraversal.java ├── ConstructBinaryTreeFromPreorderAndPostorderTraversal.java ├── ContainerWithMostWater.java ├── ContinuousSubarraySum.java ├── ConvertSortedListToBinarySearchTree.java ├── DecodeWays.java ├── DivideTwoIntegers.java ├── Dota2Senate.java ├── EscapeTheGhosts.java ├── EvaluateDivision.java ├── FindFirstAndLastPositionOfElementInSortedArray.java ├── FindMinimumInRotatedSortedArray.java ├── FriendCircles.java ├── GenerateParentheses.java ├── GrayCode.java ├── GroupAnagrams.java ├── HIndex.java ├── HandOfStraights.java ├── ImplementMagicDictionary.java ├── ImplementTriePrefixTree.java ├── IntegerBreak.java ├── IntegerReplacement.java ├── IntegerToRoman.java ├── JumpGame.java ├── KThSymbolInGrammar.java ├── KokoEatingBananas.java ├── KthSmallestElementInASortedMatrix.java ├── LargestSumOfAverages.java ├── LetterCombinationsOfAPhoneNumber.java ├── LongestPalindromicSubstring.java ├── LongestSubstringWithAtLeastKRepeatingCharacters.java ├── LongestSubstringWithoutRepeatingCharacters.java ├── LongestWordInDictionaryThroughDeleting.java ├── MaskingPersonalInformation.java ├── MaximalSquare.java ├── MaximumBinaryTree.java ├── MaximumLengthOfPairChain.java ├── MergeIntervals.java ├── Minesweeper.java ├── MinimumPathSum.java ├── MinimumSizeSubarraySum.java ├── MinimumSwapsToMakeSequencesIncreasing.java ├── MultiplyStrings.java ├── NextPermutation.java ├── NonOverlappingIntervals.java ├── PartitionEqualSubsetSum.java ├── PartitionList.java ├── PartitionToKEqualSumSubsets.java ├── PeekingIterator.java ├── PermutationSequence.java ├── Permutations.java ├── PermutationsII.java ├── PondSizes.java ├── Powxn.java ├── PrimePalindrome.java ├── PrintZeroEvenOdd.java ├── RabbitsInForest.java ├── RectangleArea.java ├── RemoveKDigits.java ├── RemoveNthNodeFromEndOfList.java ├── ReorganizeString.java ├── RestoreIPAddresses.java ├── ReverseLinkedListII.java ├── ReverseWordsInAString.java ├── RotateImage.java ├── RotateList.java ├── ScoreAfterFlippingMatrix.java ├── SearchA2DMatrix.java ├── SearchInRotatedSortedArray.java ├── SetMatrixZeroes.java ├── ShiftingLetters.java ├── SimplifyPath.java ├── SmallestStringWithSwaps.java ├── SortColors.java ├── SoupServings.java ├── SpiralMatrix.java ├── SpiralMatrixII.java ├── SplitArrayIntoConsecutiveSubsequences.java ├── StringToIntegerAtoi.java ├── SubarrayProductLessThanK.java ├── SubarraySumEqualsK.java ├── Subsets.java ├── Sum3.java ├── Sum3Closest.java ├── Sum4.java ├── SummaryRanges.java ├── SurroundedRegions.java ├── SwapNodesInPairs.java ├── TopKFrequentElements.java ├── TotalHammingDistance.java ├── Triangle.java ├── UniqueBinarySearchTrees.java ├── UniquePaths.java ├── UniquePathsII.java ├── ValidParenthesisString.java ├── ValidSudoku.java ├── ValidTicTacToeState.java ├── ValidateBinarySearchTree.java ├── ValidateIPAddress.java ├── WordBreak.java ├── WordLadder.java ├── WordSearch.java └── ZigZagConversion.java /README.md: -------------------------------------------------------------------------------- 1 | # leetcode-cn 2 | - [leetcode](https://leetcode.com) 官网看题目刷题有点困难,所以我就对照 [leetcode-cn](https://leetcode-cn.com) 同步进行了,确实需要提升下英语水平 3 | 4 | - 都是用 Java 语言编写,代码和注释都在 src 文件夹中,分为 easy、medium、hard 三部分 5 | 6 | - 脑子不动要生锈了,刷多少题不是我们的目的,重要的还是解题时候的思考和优秀算法的思路 7 | 8 | - 做个记录,平日里偷得闲时可以做做题活动活动脑子,往后也可以方便回头再嚼 9 | 10 | - 重要的是想问题的思路,锻炼逻辑思维能力 11 | 12 | - 思想最重要 13 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/AddWithoutPlus.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | /***************不用加号的加法************/ 3 | 4 | /** 5 | * 设计一个函数把两个数字相加。不得使用 + 或者其他算术运算符。 6 | * 7 | * 示例: 8 | * 9 | * 输入: a = 1, b = 1 10 | * 输出: 2 11 | *   12 | * 13 | * 提示: 14 | * 15 | * a, b 均可能是负数或 0 16 | * 结果不会溢出 32 位整数 17 | * 18 | */ 19 | public class AddWithoutPlus { 20 | 21 | public int add(int a, int b) { 22 | int sum = 0; 23 | int carry = 0; 24 | while (b != 0) { 25 | sum = a ^ b; // 取其不需要进位的部分 | 只有不同才会为1 26 | carry = (a & b) << 1; // 取其需要进位的部分 | 只有都为1才会为1 | 左移一位表示进位 27 | a = sum; // 循环往复 当没有需要进位时说明全部该进位的都进位了 28 | b = carry; 29 | } 30 | return a; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/Base7.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | /***********************七进制数*********************/ 4 | /** 5 | * 6 | * 给定一个整数,将其转化为7进制,并以字符串形式输出。 7 | * 8 | * 示例 1: 9 | * 10 | * 输入: 100 输出: "202" 11 | * 12 | * 示例 2: 13 | * 14 | * 输入: -7 输出: "-10" 15 | * 16 | * 注意: 输入范围是 [-1e7, 1e7] 。 17 | * 18 | * @author ffj 19 | * 20 | */ 21 | public class Base7 { 22 | 23 | public static void main(String[] args) { 24 | int num = -7; 25 | String result = convertToBase7(num); 26 | System.out.println(result); 27 | } 28 | 29 | public static String convertToBase7(int num) { 30 | 31 | if (num == 0) 32 | return "0"; 33 | 34 | boolean negative = false; 35 | 36 | if (num < 0) 37 | negative = true; 38 | num = Math.abs(num); 39 | StringBuilder sb = new StringBuilder(); 40 | while (num > 0) { 41 | int n = num % 7; 42 | sb.append(String.valueOf(n)); 43 | num /= 7; 44 | } 45 | sb.reverse(); 46 | return negative ? "-" + sb.toString() : sb.toString(); 47 | } 48 | 49 | /** 50 | * 讨论中解法 51 | * 52 | * @param num 53 | * @return 54 | */ 55 | public String convertTo7(int num) { 56 | if (num < 0) 57 | return '-' + convertTo7(-num); 58 | if (num < 7) 59 | return num + ""; 60 | return convertTo7(num / 7) + num % 7; 61 | } 62 | 63 | // WHY Integer.toString(num, 7); 64 | } 65 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/BestTimeToBuyAndSellStockII.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | /***********************买卖股票的最佳时机 II******************/ 4 | /** 5 | * 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。 6 | * 7 | * 设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。 8 | * 9 | * 注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。 10 | * 11 | * 示例 1: 12 | * 13 | * 输入: [7,1,5,3,6,4] 输出: 7 14 | * 15 | * 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。 随后,在第 16 | * 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。 17 | * 18 | * 示例 2: 19 | * 20 | * 输入: [1,2,3,4,5] 输出: 4 21 | * 22 | * 解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。 23 | * 注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。 因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。 24 | * 25 | * 示例 3: 26 | * 27 | * 输入: [7,6,4,3,1] 输出: 0 28 | * 29 | * 解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。 30 | * 31 | * @author ffj 32 | * 33 | */ 34 | public class BestTimeToBuyAndSellStockII { 35 | 36 | public int maxProfit(int[] prices) { 37 | 38 | int profit = 0, length = prices.length; 39 | boolean buy = false; 40 | 41 | for (int i = 0; i < length - 1; i++) { 42 | 43 | if (buy) { 44 | if (prices[i] > prices[i - 1]) { 45 | // 卖 46 | profit += prices[i] - prices[i - 1]; 47 | buy = false; 48 | } 49 | } 50 | if (!buy) { 51 | if (prices[i] < prices[i + 1]) { 52 | // 买 53 | buy = true; 54 | } 55 | } 56 | } 57 | if (buy) // 最后一笔交易 58 | profit += prices[length - 1] - prices[length - 2]; 59 | return profit; 60 | } 61 | 62 | /** 63 | * 官网解法 64 | * 65 | * @param prices 66 | * @return 67 | */ 68 | public int maxProfit1(int[] prices) { 69 | int maxprofit = 0; 70 | for (int i = 1; i < prices.length; i++) { // 只要后一个数大于前一个数就算利润 71 | if (prices[i] > prices[i - 1]) 72 | maxprofit += prices[i] - prices[i - 1]; 73 | } 74 | return maxprofit; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/BinaryTreeLevelOrderTraversalII.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | import java.util.LinkedList; 4 | import java.util.List; 5 | import java.util.Queue; 6 | 7 | /****************** 二叉树的层次遍历 II *******************/ 8 | /** 9 | * 给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历) 10 | * 11 | * 例如: 给定二叉树 [3,9,20,null,null,15,7], 12 | * 13 | * 3 14 | * 15 | * / \ 16 | * 17 | * 9 20 18 | * 19 | * / \ 20 | * 21 | * 15 7 22 | * 23 | * 返回其自底向上的层次遍历为: 24 | * 25 | * [ [15,7], [9,20], [3] ] 26 | * 27 | * @author ffj 28 | * 29 | */ 30 | public class BinaryTreeLevelOrderTraversalII { 31 | 32 | public class TreeNode { 33 | int val; 34 | TreeNode left; 35 | TreeNode right; 36 | 37 | TreeNode(int x) { 38 | val = x; 39 | } 40 | } 41 | 42 | public List> levelOrderBottom(TreeNode root) { 43 | 44 | Queue queue = new LinkedList(); 45 | List> wrapList = new LinkedList>(); 46 | 47 | if (root == null) 48 | return wrapList; 49 | 50 | queue.offer(root); 51 | while (!queue.isEmpty()) { 52 | // 有值就塞入集合 同时将其左右子节点添加到队列中 53 | int levelNum = queue.size(); 54 | List subList = new LinkedList(); 55 | for (int i = 0; i < levelNum; i++) { 56 | if (queue.peek().left != null) 57 | queue.offer(queue.peek().left); 58 | if (queue.peek().right != null) 59 | queue.offer(queue.peek().right); 60 | subList.add(queue.poll().val); 61 | } 62 | // 指定下标 每次都塞到第一个 63 | wrapList.add(0, subList); 64 | } 65 | return wrapList; 66 | 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/BinaryWatch.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | import java.math.BigInteger; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | /*********************二进制手表********************/ 8 | /** 9 | * 10 | * 二进制手表顶部有 4 个 LED 代表小时(0-11),底部的 6 个 LED 代表分钟(0-59)。 11 | * 12 | * 每个 LED 代表一个 0 或 1,最低位在右侧。 13 | * 14 | * 15 | * 16 | * 例如,上面的二进制手表读取 “3:25”。 17 | * 18 | * 给定一个非负整数 n 代表当前 LED 亮着的数量,返回所有可能的时间。 19 | * 20 | * 案例: 21 | * 22 | * 输入: n = 1 返回: ["1:00", "2:00", "4:00", "8:00", "0:01", "0:02", "0:04", 23 | * "0:08", "0:16", "0:32"] 24 | * 25 | * 26 | * 注意事项: 27 | * 28 | * 输出的顺序没有要求。 小时不会以零开头,比如 “01:00” 是不允许的,应为 “1:00”。 分钟必须由两位数组成,可能会以零开头,比如 “10:2” 29 | * 是无效的,应为 “10:02”。 30 | * 31 | * @author ffj 32 | * 33 | */ 34 | public class BinaryWatch { 35 | 36 | public static void main(String[] args) { 37 | 38 | String i = new BigInteger(String.valueOf((7 << 6) + 44)).toString(2); 39 | // int i = Integer.parseInt(String.valueOf((7 << 6) + 44), 2); 40 | System.out.println(i); 41 | } 42 | 43 | /** 44 | * 返回二进制中1的个数 45 | * 46 | * @param n 47 | * @return 48 | */ 49 | public static int NumberOf1(int n) { 50 | int count = 0; 51 | while (n != 0) { 52 | count++; 53 | n = n & (n - 1); // 妙不可言 如:1100 & 1011 = 1000 54 | } 55 | return count; 56 | } 57 | 58 | public static List readBinaryWatch(int num) { 59 | 60 | List result = new ArrayList(); 61 | for (int i = 0; i < 12; i++) { 62 | for (int j = 0; j < 60; j++) { 63 | if (NumberOf1((i << 6) + j) == num) { // 神乎其技 64 | String value = String.valueOf(i) + ":" + ((j < 10) ? ("0" + String.valueOf(j)) : String.valueOf(j)); 65 | result.add(value); 66 | } 67 | } 68 | } 69 | return result; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/ClimbingStairs.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | /*************爬楼梯*********/ 4 | /** 5 | * 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 6 | * 7 | * 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? 8 | * 9 | * 注意:给定 n 是一个正整数。 10 | * 11 | * 示例 1: 12 | * 13 | * 输入: 2 输出: 2 14 | * 15 | * 解释: 有两种方法可以爬到楼顶。 16 | * 17 | * 1. 1 阶 + 1 阶 18 | * 19 | * 2. 2 阶 20 | * 21 | * 示例 2: 22 | * 23 | * 输入: 3 输出: 3 24 | * 25 | * 解释: 有三种方法可以爬到楼顶。 26 | * 27 | * 1. 1 阶 + 1 阶 + 1 阶 28 | * 29 | * 2. 1 阶 + 2 阶 30 | * 31 | * 3. 2 阶 + 1 阶 32 | * 33 | * @author ffj 34 | * 35 | */ 36 | public class ClimbingStairs { 37 | 38 | public static void main(String[] args) { 39 | int result = new ClimbingStairs().climbStairs(44); 40 | System.out.println("result:" + result); 41 | } 42 | 43 | /** 44 | * 斐波那契数列 相似 45 | * 46 | * @param n 47 | * @return 48 | */ 49 | public int climbStairs1(int n) { 50 | 51 | if (n == 1 || n == 2 || n == 3) 52 | return n; 53 | int res = 0; 54 | int i = 1, j = 2; 55 | int z = 3; 56 | while (z++ <= n) { 57 | res = i + j; 58 | i = j; 59 | j = res; 60 | } 61 | return res; 62 | } 63 | 64 | /** 65 | * 递归超时 66 | * 67 | * @param n 68 | * @return 69 | */ 70 | public int climbStairs(int n) { 71 | return helper(n, 0); 72 | } 73 | 74 | private int helper(int n, int num) { 75 | if (n == 0) 76 | return ++num; 77 | if (n < 0) 78 | return num; 79 | num = helper(n - 1, num); 80 | num = helper(n - 2, num); 81 | 82 | return num; 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/ConvertSortedArrayToBinarySearchTree.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | /*********************将有序数组转换为二叉搜索树************/ 4 | /** 5 | * 将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。 6 | * 7 | * 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。 8 | * 9 | * 示例: 10 | * 11 | * 给定有序数组: [-10,-3,0,5,9], 12 | * 13 | * 一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树: 14 | * 15 | * 0 / \ -3 9 / / -10 5 16 | * 17 | * @author ffj 18 | * 19 | */ 20 | public class ConvertSortedArrayToBinarySearchTree { 21 | 22 | public class TreeNode { 23 | int val; 24 | TreeNode left; 25 | TreeNode right; 26 | 27 | TreeNode(int x) { 28 | val = x; 29 | } 30 | } 31 | 32 | /** 33 | * 递归 分成左右两段进行 34 | * 35 | * @param nums 36 | * @return 37 | */ 38 | public TreeNode sortedArrayToBST(int[] nums) { 39 | if (nums.length == 0) 40 | return null; 41 | return helper(nums, 0, nums.length - 1); 42 | } 43 | 44 | public TreeNode helper(int[] nums, int low, int high) { 45 | if (low > high) 46 | return null; 47 | int mid = (low + high) / 2; 48 | TreeNode root = new TreeNode(nums[mid]); 49 | root.left = helper(nums, low, mid - 1); 50 | root.right = helper(nums, mid + 1, high); 51 | return root; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/CountAndSay.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | /*******************报数*****************/ 4 | /** 5 | * 报数序列是一个整数序列,按照其中的整数的顺序进行报数,得到下一个数。其前五项如下: 6 | * 7 | * 1. 1 8 | * 9 | * 2. 11 10 | * 11 | * 3. 21 12 | * 13 | * 4. 1211 14 | * 15 | * 5. 111221 16 | * 17 | * 1 被读作 "one 1" ("一个一") , 即 11。 18 | * 19 | * 11 被读作 "two 1s" ("两个一"), 即 21。 20 | * 21 | * 21 被读作 "one 2", "one 1" ("一个二" , "一个一") , 即 1211。 22 | * 23 | * 给定一个正整数 n(1 ≤ n ≤ 30),输出报数序列的第 n 项。 24 | * 25 | * 注意:整数顺序将表示为一个字符串。 26 | * 27 | * 示例 1: 28 | * 29 | * 输入: 1 输出: "1" 30 | * 31 | * 示例 2: 32 | * 33 | * 输入: 4 输出: "1211" 34 | * 35 | * @author ffj 36 | * 37 | */ 38 | public class CountAndSay { 39 | 40 | String s = "1"; // 初始值 41 | int sum = 1; // 相同的个数 42 | 43 | public String countAndSay(int n) { 44 | 45 | if (n == 1) 46 | return s; 47 | StringBuilder sb = new StringBuilder(); 48 | if (s.length() == 1) { 49 | sb.append("11"); 50 | } else { 51 | for (int i = 0; i < s.length() - 1; i++) { 52 | if (s.charAt(i) == s.charAt(i + 1)) { 53 | // 两者相等 54 | sum++; 55 | if (i == s.length() - 2) { 56 | // 倒数第二个数的情况 结束尾数相等 57 | sb.append(sum + "" + s.charAt(i)); 58 | sum = 1; 59 | } 60 | } else { 61 | // 两者不相等 62 | sb.append(sum + "" + s.charAt(i)); 63 | sum = 1; 64 | if (i == s.length() - 2) { 65 | // 倒数第二个数的情况 最后一个数数量就是 1 66 | sb.append(sum + "" + s.charAt(i + 1)); 67 | sum = 1; 68 | } 69 | } 70 | 71 | } 72 | } 73 | // 赋值 接着往下走 74 | s = sb.toString(); 75 | 76 | return countAndSay(n - 1); 77 | 78 | } 79 | 80 | public static void main(String[] args) { 81 | int n = 5; 82 | System.out.println(new CountAndSay().countAndSay(n)); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/CountPrimes.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | /*************计数质数*********/ 4 | /** 5 | * 统计所有小于非负整数 n 的质数的数量。 6 | * 7 | * 示例: 8 | * 9 | * 输入: 10 输出: 4 10 | * 11 | * 解释: 小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 。 12 | * 13 | * @author ffj 14 | * 15 | */ 16 | public class CountPrimes { 17 | 18 | public static void main(String[] args) { 19 | 20 | } 21 | 22 | public int countPrimes(int n) { 23 | if (n < 2) 24 | return 0; 25 | int[] ans = new int[n]; 26 | ans[0] = 1; 27 | ans[1] = 1; 28 | // 0 1 不是质数 29 | for (int i = 2; i <= Math.sqrt(n); ++i) { 30 | // 将非质数置 1 31 | if (ans[i] == 0) 32 | for (int j = i * i; j < n; j += i) 33 | ans[j] = 1; 34 | } 35 | int result = 0; 36 | // 统计质数个数 37 | for (int num : ans) { 38 | if (num == 0) 39 | result++; 40 | } 41 | return result; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/DeleteNodeInALinkedList.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | /**************删除链表中的节点**************/ 4 | /** 5 | * 请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点。 6 | * 7 | * 现有一个链表 -- head = [4,5,1,9],它可以表示为: 8 | * 9 | * 4 -> 5 -> 1 -> 9 10 | * 11 | * 示例 1: 12 | * 13 | * 输入: head = [4,5,1,9], node = 5 输出: [4,1,9] 14 | * 15 | * 解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9. 16 | * 17 | * 示例 2: 18 | * 19 | * 输入: head = [4,5,1,9], node = 1 输出: [4,5,9] 20 | * 21 | * 解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9. 22 | * 23 | * 说明: 24 | * 25 | * 链表至少包含两个节点。 26 | * 27 | * 链表中所有节点的值都是唯一的。 28 | * 29 | * 给定的节点为非末尾节点并且一定是链表中的一个有效节点。 30 | * 31 | * 不要从你的函数中返回任何结果。 32 | * 33 | * @author ffj 34 | * 35 | */ 36 | public class DeleteNodeInALinkedList { 37 | 38 | public class ListNode { 39 | int val; 40 | ListNode next; 41 | 42 | ListNode(int x) { 43 | val = x; 44 | } 45 | } 46 | 47 | /** 48 | * 直接将下个节点的值赋为该节点,然后该节点指向下下节点 49 | * 50 | * @param node 51 | */ 52 | public void deleteNode(ListNode node) { 53 | node.val = node.next.val; 54 | node.next = node.next.next; 55 | 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/DestinationCity.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | /**************旅行终点站************/ 3 | 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | /** 9 | * 给你一份旅游线路图,该线路图中的旅行线路用数组 paths 表示,其中 paths[i] = [cityAi, cityBi] 表示该线路将会从 cityAi 直接前往 cityBi 。请你找出这次旅行的终点站,即没有任何可以通往其他城市的线路的城市。 10 | * 11 | * 题目数据保证线路图会形成一条不存在循环的线路,因此只会有一个旅行终点站。 12 | * 13 | * 14 | * 示例 1: 15 | * 16 | * 输入:paths = [["London","New York"],["New York","Lima"],["Lima","Sao Paulo"]] 17 | * 输出:"Sao Paulo" 18 | * 解释:从 "London" 出发,最后抵达终点站 "Sao Paulo" 。本次旅行的路线是 "London" -> "New York" -> "Lima" -> "Sao Paulo" 。 19 | * 示例 2: 20 | * 21 | * 输入:paths = [["B","C"],["D","B"],["C","A"]] 22 | * 输出:"A" 23 | * 解释:所有可能的线路是: 24 | * "D" -> "B" -> "C" -> "A".  25 | * "B" -> "C" -> "A".  26 | * "C" -> "A".  27 | * "A".  28 | * 显然,旅行终点站是 "A" 。 29 | * 示例 3: 30 | * 31 | * 输入:paths = [["A","Z"]] 32 | * 输出:"Z" 33 | *   34 | * 35 | * 提示: 36 | * 37 | * 1 <= paths.length <= 100 38 | * paths[i].length == 2 39 | * 1 <= cityAi.length, cityBi.length <= 10 40 | * cityAi != cityBi 41 | * 所有字符串均由大小写英文字母和空格字符组成。 42 | * 43 | */ 44 | public class DestinationCity { 45 | 46 | public String destCity(List> paths) { 47 | 48 | Map temps = new HashMap<>(); 49 | for (List strs : paths) { 50 | temps.put(strs.get(0), strs.get(strs.size()-1)); 51 | } 52 | // 初始起始点 53 | String key = paths.get(0).get(0); 54 | while (temps.containsKey(key)) { 55 | key = temps.get(key); 56 | } 57 | return key; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/DetectCapital.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | /***********检测大写字母*********/ 3 | 4 | /** 5 | * 给定一个单词,你需要判断单词的大写使用是否正确。 6 | * 7 | * 我们定义,在以下情况时,单词的大写用法是正确的: 8 | * 9 | * 全部字母都是大写,比如"USA"。 10 | * 单词中所有字母都不是大写,比如"leetcode"。 11 | * 如果单词不只含有一个字母,只有首字母大写, 比如 "Google"。 12 | * 否则,我们定义这个单词没有正确使用大写字母。 13 | * 14 | * 示例 1: 15 | * 16 | * 输入: "USA" 17 | * 输出: True 18 | * 示例 2: 19 | * 20 | * 输入: "FlaG" 21 | * 输出: False 22 | * 注意: 输入是由大写和小写拉丁字母组成的非空单词。 23 | * 24 | */ 25 | public class DetectCapital { 26 | 27 | public static void main(String[] args) { 28 | 29 | System.out.println((int)'A'); // 65 30 | System.out.println((int)'Z'); // 90 31 | System.out.println((int)'a'); // 97 32 | System.out.println((int)'z'); // 122 33 | } 34 | 35 | public boolean detectCapitalUse(String word) { 36 | 37 | // 全大写 38 | if (word.toUpperCase().equals(word)) 39 | return true; 40 | // 只有首字母大写 | 全小写 41 | if (word.substring(1).toLowerCase().equals(word.substring(1))) 42 | return true; 43 | return false; 44 | 45 | } 46 | 47 | public boolean detectCapitalUse1(String word) { 48 | int l = word.length(); 49 | // 统计大写字母出现次数 50 | int count = 0; 51 | for(int i = 0; i < l; i++) { 52 | // 是大写字母且数量是否一致 53 | if(word.charAt(i) < 91 && count++ < i) { 54 | return false; 55 | } 56 | } 57 | // count 58 | // 0:全是小写字母 1:只有首字母大写 l:都是大写 59 | return count <= 1 || count == l; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/DiameterOfBinaryTree.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | /********************二叉树的直径****************/ 4 | /** 5 | * 给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过根结点。 6 | * 7 | * 示例 : 给定二叉树 8 | * 9 | * 1 10 | * 11 | * / \ 12 | * 13 | * 2 3 14 | * 15 | * / \ 16 | * 17 | * 4 5 18 | * 19 | * 返回 3, 它的长度是路径 [4,2,1,3] 或者 [5,2,1,3]。 20 | * 21 | * 注意:两结点之间的路径长度是以它们之间边的数目表示。 22 | * 23 | * @author ffj 24 | * 25 | */ 26 | public class DiameterOfBinaryTree { 27 | 28 | public class TreeNode { 29 | int val; 30 | TreeNode left; 31 | TreeNode right; 32 | 33 | TreeNode(int x) { 34 | val = x; 35 | } 36 | } 37 | 38 | int ans; 39 | 40 | public int diameterOfBinaryTree(TreeNode root) { 41 | 42 | ans = 1; 43 | helper(root); 44 | return ans - 1; 45 | } 46 | 47 | /** 48 | * 返回该节点左右较长的那条 49 | * 50 | * @param node 51 | * @return 52 | */ 53 | private int helper(TreeNode node) { 54 | if (node == null) 55 | return 0; 56 | int left = helper(node.left); 57 | int right = helper(node.right); 58 | ans = Math.max(ans, left + right + 1); 59 | return Math.max(left, right) + 1; 60 | 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/DuiChengErChaShu.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | /***************对称的二叉树************/ 3 | 4 | 5 | /** 6 | *请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。 7 | * 8 | * 例如,二叉树 [1,2,2,3,4,4,3] 是对称的。 9 | * 10 | *     1 11 | *    / \ 12 | *   2   2 13 | *  / \ / \ 14 | * 3  4 4  3 15 | * 16 | * 但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的: 17 | * 18 | *     1 19 | *    / \ 20 | *   2   2 21 | *    \   \ 22 | *    3    3 23 | * 24 | *   25 | * 26 | * 示例 1: 27 | * 输入:root = [1,2,2,3,4,4,3] 28 | * 输出:true 29 | * 30 | * 示例 2: 31 | * 输入:root = [1,2,2,null,3,null,3] 32 | * 输出:false 33 | *   34 | * 35 | * 限制: 36 | * 37 | * 0 <= 节点个数 <= 1000 38 | * 39 | */ 40 | public class DuiChengErChaShu { 41 | 42 | public class TreeNode { 43 | int val; 44 | TreeNode left; 45 | TreeNode right; 46 | TreeNode(int x) { val = x; } 47 | } 48 | 49 | public boolean isSymmetric(TreeNode root) { 50 | if (root == null) return true; 51 | return helper(root.left, root.right); 52 | } 53 | 54 | private boolean helper(TreeNode left, TreeNode right) { 55 | // 无节点 56 | if (left == null && right == null) return true; 57 | // 有一个有必不对称 58 | if (left == null || right == null) return false; 59 | // 接着往下递归比较 60 | return left.val == right.val && helper(left.left, right.right) && helper(left.right, right.left); 61 | } 62 | 63 | public static void main(String[] args) { 64 | } 65 | 66 | } 67 | 68 | 69 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/FairCandySwap.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | import java.util.Arrays; 4 | import java.util.HashSet; 5 | import java.util.Set; 6 | 7 | /**************公平的糖果交换***********/ 8 | /** 9 | * 爱丽丝和鲍勃有不同大小的糖果棒:A[i] 是爱丽丝拥有的第 i 块糖的大小,B[j] 是鲍勃拥有的第 j 块糖的大小。 10 | * 11 | * 因为他们是朋友,所以他们想交换一个糖果棒,这样交换后,他们都有相同的糖果总量。(一个人拥有的糖果总量是他们拥有的糖果棒大小的总和。) 12 | * 13 | * 返回一个整数数组 ans,其中 ans[0] 是爱丽丝必须交换的糖果棒的大小,ans[1] 是 Bob 必须交换的糖果棒的大小。 14 | * 15 | * 如果有多个答案,你可以返回其中任何一个。保证答案存在。 16 | * 17 | * 示例 1: 18 | * 19 | * 输入:A = [1,1], B = [2,2] 输出:[1,2] 20 | * 21 | * 示例 2: 22 | * 23 | * 输入:A = [1,2], B = [2,3] 输出:[1,2] 24 | * 25 | * 示例 3: 26 | * 27 | * 输入:A = [2], B = [1,3] 输出:[2,3] 28 | * 29 | * 示例 4: 30 | * 31 | * 输入:A = [1,2,5], B = [2,4] 输出:[5,4] 32 | * 33 | * 提示: 34 | * 35 | * 1 <= A.length <= 10000 36 | * 37 | * 1 <= B.length <= 10000 38 | * 39 | * 1 <= A[i] <= 100000 40 | * 41 | * 1 <= B[i] <= 100000 42 | * 43 | * 保证爱丽丝与鲍勃的糖果总量不同。 答案肯定存在。 44 | * 45 | * @author ffj 46 | * 47 | */ 48 | public class FairCandySwap { 49 | 50 | public static void main(String[] args) { 51 | int[] A = { 1, 2, 5 }, B = { 2, 4 }; 52 | int[] result = new FairCandySwap().fairCandySwap(A, B); 53 | System.out.println(Arrays.toString(result)); 54 | } 55 | 56 | public int[] fairCandySwap(int[] A, int[] B) { 57 | 58 | int sumA = Arrays.stream(A).sum(); 59 | int sumB = Arrays.stream(B).sum(); 60 | 61 | int diff = (sumA - sumB) / 2; // x - y 62 | 63 | // 题目要求是【交换一个糖果棒】 64 | Set set = new HashSet<>(); 65 | for (int a : A) 66 | set.add(a); 67 | for (int b : B) { 68 | if (set.contains(diff + b)) { 69 | return new int[] { diff + b, b }; 70 | } 71 | } 72 | return new int[0]; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/Fib.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | /** 4 | * 写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项。斐波那契数列的定义如下: 5 | * 6 | * F(0) = 0,   F(1) = 1 7 | * F(N) = F(N - 1) + F(N - 2), 其中 N > 1. 8 | * 斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。 9 | * 10 | * 答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。 11 | * 12 | *   13 | * 14 | * 示例 1: 15 | * 16 | * 输入:n = 2 17 | * 输出:1 18 | * 示例 2: 19 | * 20 | * 输入:n = 5 21 | * 输出:5 22 | *   23 | * 24 | * 提示: 25 | * 26 | * 0 <= n <= 100 27 | * 28 | */ 29 | public class Fib { 30 | public static void main(String[] args) { 31 | 32 | int value = fib(5); 33 | System.out.println("fib(n) : " + value); 34 | 35 | } 36 | public static int fib(int n) { 37 | int[] arr = new int[101]; 38 | if (n == 0) return 0; 39 | if (n <= 2) return 1; 40 | arr[1] = 1; 41 | arr[2] = 1; 42 | for (int i = 3; i <= n; i++) { 43 | arr[i] = (arr[i-1] + arr[i-2]) % 1000000007; 44 | } 45 | return arr[n]; 46 | } 47 | } -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/FindAllNumbersDisappearedInAnArray.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | import java.util.stream.Collectors; 7 | 8 | /***************找到所有数组中消失的数字***************/ 9 | /** 10 | * 给定一个范围在 1 ≤ a[i] ≤ n ( n = 数组大小 ) 的 整型数组,数组中的元素一些出现了两次,另一些只出现一次。 11 | * 12 | * 找到所有在 [1, n] 范围之间没有出现在数组中的数字。 13 | * 14 | * 您能在不使用额外空间且时间复杂度为O(n)的情况下完成这个任务吗? 你可以假定返回的数组不算在额外空间内。 15 | * 16 | * 示例: 17 | * 18 | * 输入: [4,3,2,7,8,2,3,1] 19 | * 20 | * 输出: [5,6] 21 | * 22 | * @author ffj 23 | * 24 | */ 25 | public class FindAllNumbersDisappearedInAnArray { 26 | 27 | public static void main(String[] args) { 28 | int[] nums = { 4, 3, 2, 7, 8, 2, 3, 1 }; 29 | System.out.println(new FindAllNumbersDisappearedInAnArray().findDisappearedNumbers1(nums)); 30 | } 31 | 32 | /** 33 | * 超时 34 | * 35 | * @param nums 36 | * @return 37 | */ 38 | public List findDisappearedNumbers(int[] nums) { 39 | List result = new ArrayList<>(); 40 | if (nums.length == 0) 41 | return result; 42 | int len = nums.length; 43 | // int[] 转 List 44 | List numlist = Arrays.stream(nums).boxed().collect(Collectors.toList()); 45 | for (int i = 1; i <= len; i++) { 46 | if (!numlist.contains(i)) 47 | result.add(i); 48 | } 49 | return result; 50 | } 51 | 52 | /** 53 | * 一一对应 54 | * 55 | * @param nums 56 | * @return 57 | */ 58 | public List findDisappearedNumbers1(int[] nums) { 59 | List ret = new ArrayList(); 60 | 61 | // 将对应的值的下标 标记 62 | for (int i = 0; i < nums.length; i++) { 63 | int val = Math.abs(nums[i]) - 1; 64 | if (nums[val] > 0) { 65 | nums[val] = -nums[val]; 66 | } 67 | } 68 | 69 | // 没有标记的就说明该值没有 70 | for (int i = 0; i < nums.length; i++) { 71 | if (nums[i] > 0) { 72 | ret.add(i + 1); 73 | } 74 | } 75 | return ret; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/FindPivotIndex.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | import java.util.Arrays; 4 | 5 | /************寻找数组的中心索引*********/ 6 | /** 7 | * 给定一个整数类型的数组 nums,请编写一个能够返回数组“中心索引”的方法。 8 | * 9 | * 我们是这样定义数组中心索引的:数组中心索引的左侧所有元素相加的和等于右侧所有元素相加的和。 10 | * 11 | * 如果数组不存在中心索引,那么我们应该返回 -1。如果数组有多个中心索引,那么我们应该返回最靠近左边的那一个。 12 | * 13 | * 示例 1: 14 | * 15 | * 输入: nums = [1, 7, 3, 6, 5, 6] 16 | * 17 | * 输出: 3 18 | * 19 | * 解释: 20 | * 21 | * 索引3 (nums[3] = 6) 的左侧数之和(1 + 7 + 3 = 11),与右侧数之和(5 + 6 = 11)相等。 同时, 3 22 | * 也是第一个符合要求的中心索引。 23 | * 24 | * 示例 2: 25 | * 26 | * 输入: nums = [1, 2, 3] 27 | * 28 | * 输出: -1 29 | * 30 | * 解释: 数组中不存在满足此条件的中心索引。 31 | * 32 | * 说明: 33 | * 34 | * nums 的长度范围为 [0, 10000]。 35 | * 36 | * 任何一个 nums[i] 将会是一个范围在 [-1000, 1000]的整数。 37 | * 38 | * @author ffj 39 | * 40 | */ 41 | public class FindPivotIndex { 42 | /** 43 | * 先求和 再相减比较 44 | * 45 | * @param nums 46 | * @return 47 | */ 48 | public int pivotIndex(int[] nums) { 49 | // 求和 50 | int sum = Arrays.stream(nums).sum(); 51 | int sInt = 0; 52 | for (int i = 0; i < nums.length; sInt += nums[i++]) { 53 | if ((sum - sInt - nums[i]) == sInt) 54 | return i; 55 | } 56 | return -1; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/FindTheDifference.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /******************找不同*************/ 7 | /** 8 | * 给定两个字符串 s 和 t,它们只包含小写字母。 9 | * 10 | * 字符串 t 由字符串 s 随机重排,然后在随机位置添加一个字母。 11 | * 12 | * 请找出在 t 中被添加的字母。 13 | * 14 | * 示例: 15 | * 16 | * 输入: 17 | * 18 | * s = "abcd" t = "abcde" 19 | * 20 | * 输出: e 21 | * 22 | * 解释: 'e' 是那个被添加的字母。 23 | * 24 | * @author ffj 25 | * 26 | */ 27 | public class FindTheDifference { 28 | 29 | public static void main(String[] args) { 30 | String s = "abcd", t = "abcde"; 31 | System.out.println(new FindTheDifference().findTheDifference1(s, t)); 32 | } 33 | 34 | public char findTheDifference(String s, String t) { 35 | // key: 字符 , value: 出现的次数 36 | Map map = new HashMap<>(); 37 | for (char c : s.toCharArray()) { 38 | map.put(c, map.getOrDefault(c, 0) + 1); 39 | } 40 | for (char c : t.toCharArray()) { 41 | if (map.get(c) != null) { 42 | if (map.get(c) > 0) 43 | map.put(c, map.get(c) - 1); 44 | else 45 | return c; 46 | } else 47 | return c; 48 | } 49 | return 0; 50 | } 51 | 52 | /** 53 | * 位操作 由于只有一个不同 char,所以其余对称操作后值不变最后只会剩下多余的那个 char 54 | * 55 | * @param s 56 | * @param t 57 | * @return 58 | */ 59 | public char findTheDifference1(String s, String t) { 60 | int n = t.length(); 61 | char c = t.charAt(n - 1); 62 | for (int i = 0; i < n - 1; ++i) { 63 | c ^= s.charAt(i); 64 | c ^= t.charAt(i); 65 | } 66 | return c; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/FlippingAnImage.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | /*************翻转图像***********/ 4 | /** 5 | * 给定一个二进制矩阵 A,我们想先水平翻转图像,然后反转图像并返回结果。 6 | * 7 | * 水平翻转图片就是将图片的每一行都进行翻转,即逆序。例如,水平翻转 [1, 1, 0] 的结果是 [0, 1, 1]。 8 | * 9 | * 反转图片的意思是图片中的 0 全部被 1 替换, 1 全部被 0 替换。例如,反转 [0, 1, 1] 的结果是 [1, 0, 0]。 10 | * 11 | * 示例 1: 12 | * 13 | * 输入: [[1,1,0],[1,0,1],[0,0,0]] 14 | * 15 | * 输出: [[1,0,0],[0,1,0],[1,1,1]] 16 | * 17 | * 解释: 首先翻转每一行: [[0,1,1],[1,0,1],[0,0,0]]; 18 | * 19 | * 然后反转图片: [[1,0,0],[0,1,0],[1,1,1]] 20 | * 21 | * 示例 2: 22 | * 23 | * 输入: [[1,1,0,0],[1,0,0,1],[0,1,1,1],[1,0,1,0]] 24 | * 25 | * 输出: [[1,1,0,0],[0,1,1,0],[0,0,0,1],[1,0,1,0]] 26 | * 27 | * 解释: 首先翻转每一行: [[0,0,1,1],[1,0,0,1],[1,1,1,0],[0,1,0,1]]; 28 | * 29 | * 然后反转图片: [[1,1,0,0],[0,1,1,0],[0,0,0,1],[1,0,1,0]] 30 | * 31 | * 说明: 32 | * 33 | * 1 <= A.length = A[0].length <= 20 34 | * 35 | * 0 <= A[i][j] <= 1 36 | * 37 | * @author ffj 38 | * 39 | */ 40 | public class FlippingAnImage { 41 | 42 | public static void main(String[] args) { 43 | int[][] A = { { 1, 1, 0 }, { 1, 0, 1 }, { 0, 0, 0 } }; 44 | new FlippingAnImage().flipAndInvertImage(A); 45 | } 46 | 47 | public int[][] flipAndInvertImage(int[][] A) { 48 | 49 | int rows = A.length, cols = A[0].length; 50 | // 新建新二维数组 51 | int[][] B = new int[rows][cols]; 52 | for (int row = 0; row < rows; row++) { 53 | for (int col = cols - 1; col >= 0; col--) { 54 | // 循环赋值 55 | B[row][cols - col - 1] = A[row][col] == 0 ? 1 : 0; 56 | } 57 | } 58 | return B; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/GuessNumbers.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | /***************猜数字*************/ 3 | 4 | /** 5 | * 小A 和 小B 在玩猜数字。小B 每次从 1, 2, 3 中随机选择一个,小A 每次也从 1, 2, 3 中选择一个猜。 6 | * 他们一共进行三次这个游戏,请返回 小A 猜对了几次? 7 | * 8 | *   9 | * 10 | * 输入的guess数组为 小A 每次的猜测,answer数组为 小B 每次的选择。guess和answer的长度都等于3。 11 | * 12 | *   13 | * 14 | * 示例 1: 15 | * 输入:guess = [1,2,3], answer = [1,2,3] 16 | * 输出:3 17 | * 解释:小A 每次都猜对了。 18 | *   19 | * 20 | * 示例 2: 21 | * 输入:guess = [2,2,3], answer = [3,2,1] 22 | * 输出:1 23 | * 解释:小A 只猜对了第二次。 24 | *   25 | * 26 | * 限制: 27 | * 28 | * guess的长度 = 3 29 | * answer的长度 = 3 30 | * guess的元素取值为 {1, 2, 3} 之一。 31 | * answer的元素取值为 {1, 2, 3} 之一。 32 | * 33 | */ 34 | public class GuessNumbers { 35 | 36 | public int game(int[] guess, int[] answer) { 37 | int result = 0; 38 | for (int i = 0; i < 3; i++) { 39 | if (guess[i] == answer[i]) 40 | result++; 41 | } 42 | return result; 43 | } 44 | 45 | public static void main(String[] args) { 46 | System.out.println(2 ^ 2); // 异或 语法 相同输出 0 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/HammingDistance.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | /*****************汉明距离***************/ 4 | /** 5 | * 6 | * 两个整数之间的汉明距离指的是这两个数字对应二进制位不同的位置的数目。 7 | * 8 | * 给出两个整数 x 和 y,计算它们之间的汉明距离。 9 | * 10 | * 注意: 0 ≤ x, y < 231. 11 | * 12 | * 示例: 13 | * 14 | * 输入: x = 1, y = 4 15 | * 16 | * 输出: 2 17 | * 18 | * 解释: 19 | * 20 | * 1 (0 0 0 1) 21 | * 22 | * 4 (0 1 0 0) 23 | * 24 | * -----↑---↑ 25 | * 26 | * 上面的箭头指出了对应二进制位不同的位置。 27 | * 28 | * @author ffj 29 | * 30 | */ 31 | public class HammingDistance { 32 | 33 | public int hammingDistance(int x, int y) { 34 | int total = 0; 35 | for (int i = 0; i < 32; i++) { // 整数最多32位 36 | // 记录每一位上的1的个数和 37 | int count = 0; 38 | count += (x >> i) & 1; 39 | count += (y >> i) & 1; 40 | // 每一位的总汉明距离 = 1的个数 * 0的个数 41 | total += count * (2 - count); 42 | } 43 | return total; 44 | } 45 | 46 | /** 47 | * 异或操作求1个数 48 | * 49 | * @param x 50 | * @param y 51 | * @return 52 | */ 53 | public int hammingDistance1(int x, int y) { 54 | int num = x ^ y, total = 0; 55 | while (num != 0) { 56 | num &= (num - 1); // 每次&操作便会消除一位1 57 | total++; 58 | } 59 | return total; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/ImplementQueueUsingStacks.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | import java.util.Stack; 4 | 5 | /******************用栈实现队列****************/ 6 | /** 7 | * 使用栈实现队列的下列操作: 8 | * 9 | * push(x) -- 将一个元素放入队列的尾部。 10 | * 11 | * pop() -- 从队列首部移除元素。 12 | * 13 | * peek() -- 返回队列首部的元素。 14 | * 15 | * empty() -- 返回队列是否为空。 16 | * 17 | * 示例: 18 | * 19 | * MyQueue queue = new MyQueue(); 20 | * 21 | * queue.push(1); 22 | * 23 | * queue.push(2); 24 | * 25 | * queue.peek(); // 返回 1 26 | * 27 | * queue.pop(); // 返回 1 28 | * 29 | * queue.empty(); // 返回 false 30 | * 31 | * 说明: 32 | * 33 | * 你只能使用标准的栈操作 -- 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。 34 | * 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。 假设所有操作都是有效的 35 | * (例如,一个空的队列不会调用 pop 或者 peek 操作)。 36 | * 37 | * @author ffj 38 | * 39 | */ 40 | public class ImplementQueueUsingStacks { 41 | 42 | Stack stack1; // 声明两个stack 43 | Stack stack2; 44 | 45 | /** Initialize your data structure here. */ 46 | public ImplementQueueUsingStacks() { 47 | stack1 = new Stack<>(); 48 | stack2 = new Stack<>(); 49 | } 50 | 51 | /** Push element x to the back of queue. */ 52 | public void push(int x) { 53 | stack1.push(x); // 放在 stack1的栈顶 随后就放在了stack2的栈尾 54 | } 55 | 56 | /** Removes the element from in front of queue and returns that element. */ 57 | public int pop() { 58 | if (stack2.isEmpty()) { // 两个stack进行转换 59 | while (!stack1.isEmpty()) 60 | stack2.push(stack1.pop()); 61 | } 62 | return stack2.pop(); 63 | } 64 | 65 | /** Get the front element. */ 66 | public int peek() { 67 | if (!stack2.isEmpty()) { 68 | return stack2.peek(); 69 | } else { 70 | while (!stack1.isEmpty()) // 两个stack进行转换 71 | stack2.push(stack1.pop()); 72 | } 73 | return stack2.peek(); 74 | } 75 | 76 | /** Returns whether the queue is empty. */ 77 | public boolean empty() { 78 | return stack1.isEmpty() && stack2.isEmpty(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/InvertBinaryTree.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | /***********翻转二叉树********/ 4 | /** 5 | * 翻转一棵二叉树。 6 | * 7 | * 示例: 8 | * 9 | * 输入: 10 | * 11 | * 4 12 | * 13 | * / \ 14 | * 15 | * 2 7 16 | * 17 | * / \ / \ 18 | * 19 | * 1 3 6 9 20 | * 21 | * 输出: 22 | * 23 | * 4 24 | * 25 | * / \ 26 | * 27 | * 7 2 28 | * 29 | * / \ / \ 30 | * 31 | * 9 6 3 1 32 | * 33 | * 备注: 这个问题是受到 Max Howell 的 原问题 启发的 : 34 | * 35 | * 谷歌:我们90%的工程师使用您编写的软件(Homebrew),但是您却无法在面试时在白板上写出翻转二叉树这道题,这太糟糕了。 36 | * 37 | * @author ffj 38 | * 39 | */ 40 | public class InvertBinaryTree { 41 | public class TreeNode { 42 | int val; 43 | TreeNode left; 44 | TreeNode right; 45 | 46 | TreeNode(int x) { 47 | val = x; 48 | } 49 | } 50 | 51 | public TreeNode invertTree(TreeNode root) { 52 | 53 | // 翻转二叉树 54 | helper(root); 55 | return root; 56 | 57 | } 58 | 59 | /** 60 | * 实现该节点下的左右节点交换 61 | * 62 | * @param node 63 | */ 64 | private void helper(TreeNode node) { 65 | if (node == null) 66 | return; 67 | // 交换节点 68 | TreeNode temp = node.left; 69 | node.left = node.right; 70 | node.right = temp; 71 | // 递归遍历所有节点 72 | helper(node.left); 73 | helper(node.right); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/IslandPerimeter.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | /*******************岛屿的周长**************/ 4 | /** 5 | * 给定一个包含 0 和 1 的二维网格地图,其中 1 表示陆地 0 6 | * 表示水域。网格中的格子水平和垂直方向相连(对角线方向不相连)。整个网格被水完全包围,但其中恰好有一个岛屿(或者说,一个或多个表示陆地的格子相连组成的岛屿)。岛屿中没有“湖”(“湖” 7 | * 指水域在岛屿内部且不和岛屿周围的水相连)。格子是边长为 1 的正方形。网格为长方形,且宽度和高度均不超过 100 。计算这个岛屿的周长。 8 | * 9 | * 示例 : 10 | * 11 | * [[0,1,0,0], [1,1,1,0], [0,1,0,0], [1,1,0,0]] 12 | * 13 | * 答案: 16 14 | * 15 | * 解释: 它的周长是下面图片中的 16 个黄色的边: 16 | * 17 | * @author ffj 18 | * 19 | */ 20 | public class IslandPerimeter { 21 | 22 | public int islandPerimeter(int[][] grid) { 23 | 24 | int rows = grid.length, cols = grid[0].length, sum = 0; 25 | 26 | // 表示周围四个点 27 | int[][] arr = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } }; 28 | 29 | for (int row = 0; row < rows; row++) { 30 | for (int col = 0; col < cols; col++) { 31 | if (grid[row][col] == 1) { 32 | // 陆地 33 | int num = 4; 34 | for (int[] ar : arr) { 35 | int R = row + ar[0], C = col + ar[1]; 36 | // 四周有一块相邻陆地就减一条边 37 | if (R < rows && R >= 0 && C < cols && C >= 0 && grid[R][C] == 1) 38 | num--; 39 | } 40 | sum += num; 41 | } 42 | } 43 | } 44 | return sum; 45 | 46 | } 47 | 48 | /** 49 | * 讨论中解法 50 | * 51 | * @param grid 52 | * @return 53 | */ 54 | public int islandPerimeter1(int[][] grid) { 55 | int islands = 0, neighbours = 0; 56 | 57 | for (int i = 0; i < grid.length; i++) { 58 | for (int j = 0; j < grid[i].length; j++) { 59 | if (grid[i][j] == 1) { 60 | islands++; // count islands 61 | if (i < grid.length - 1 && grid[i + 1][j] == 1) 62 | neighbours++; // count down neighbours 63 | if (j < grid[i].length - 1 && grid[i][j + 1] == 1) 64 | neighbours++; // count right neighbours 65 | } 66 | } 67 | } 68 | 69 | return islands * 4 - neighbours * 2; 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/IsomorphicStrings.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | import java.util.HashMap; 4 | import java.util.HashSet; 5 | import java.util.Map; 6 | import java.util.Set; 7 | 8 | /************同构字符串************/ 9 | /** 10 | * 给定两个字符串 s 和 t,判断它们是否是同构的。 11 | * 12 | * 如果 s 中的字符可以被替换得到 t ,那么这两个字符串是同构的。 13 | * 14 | * 所有出现的字符都必须用另一个字符替换,同时保留字符的顺序。两个字符不能映射到同一个字符上,但字符可以映射自己本身。 15 | * 16 | * 示例 1: 17 | * 18 | * 输入: s = "egg", t = "add" 输出: true 19 | * 20 | * 示例 2: 21 | * 22 | * 输入: s = "foo", t = "bar" 输出: false 23 | * 24 | * 示例 3: 25 | * 26 | * 输入: s = "paper", t = "title" 输出: true 27 | * 28 | * 说明: 你可以假设 s 和 t 具有相同的长度。 29 | * 30 | * @author ffj 31 | * 32 | */ 33 | public class IsomorphicStrings { 34 | 35 | /** 36 | * 37 | * @param s 38 | * @param t 39 | * @return 40 | */ 41 | public boolean isIsomorphic(String s, String t) { 42 | 43 | if (s.length() != t.length()) 44 | return false; 45 | char[] sChar = s.toCharArray(), tChar = t.toCharArray(); 46 | // 用来存放映射值 不能重复映射 47 | Set visit = new HashSet<>(); 48 | Map map = new HashMap<>(); 49 | for (int i = 0; i < s.length(); i++) { 50 | char sch = sChar[i], tch = tChar[i]; 51 | if (!map.containsKey(sch)) { 52 | // map 中还没放置该 key 53 | map.put(sch, tch); 54 | if (!visit.add(tch)) // 已经映射过了 55 | return false; 56 | } else { 57 | if (map.get(sch) != tch) // 映射的值不是同一个 58 | return false; 59 | } 60 | } 61 | return true; 62 | } 63 | 64 | /** 65 | * 讨论区解法 转化为对应 ascii 码来确定唯一地址 66 | * 67 | * @param s 68 | * @param t 69 | * @return 70 | */ 71 | public boolean isIsomorphic1(String s, String t) { 72 | 73 | int m1[] = new int[256], m2[] = new int[256], n = s.length(); 74 | char[] sChar = s.toCharArray(), tChar = t.toCharArray(); 75 | for (int i = 0; i < n; ++i) { 76 | if (m1[sChar[i]] != m2[tChar[i]]) // 说明映射值不同 77 | return false; 78 | m1[sChar[i]] = i + 1; // 对应地址数字加 1 79 | m2[tChar[i]] = i + 1; 80 | } 81 | return true; 82 | 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/JudgeRouteCircle.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | /****************判断路线成圈****************/ 4 | /** 5 | * 初始位置 (0, 0) 处有一个机器人。给出它的一系列动作,判断这个机器人的移动路线是否形成一个圆圈,换言之就是判断它是否会移回到原来的位置。 6 | * 7 | * 移动顺序由一个字符串表示。每一个动作都是由一个字符来表示的。机器人有效的动作有 R(右),L(左),U(上)和 D(下)。输出应为 true 或 8 | * false,表示机器人移动路线是否成圈。 9 | * 10 | * 示例 1: 11 | * 12 | * 输入: "UD" 输出: true 13 | * 14 | * 示例 2: 15 | * 16 | * 输入: "LL" 输出: false 17 | * 18 | * @author ffj 19 | * 20 | */ 21 | public class JudgeRouteCircle { 22 | 23 | public boolean judgeCircle(String moves) { 24 | 25 | if (moves == null || moves.length() == 0) 26 | return true; 27 | int x = 0, y = 0; 28 | for (int i = 0; i < moves.length(); i++) { 29 | char ch = moves.charAt(i); 30 | switch (ch) { 31 | case 'R': 32 | x += 1; 33 | break; 34 | case 'L': 35 | x -= 1; 36 | break; 37 | case 'U': 38 | y += 1; 39 | break; 40 | case 'D': 41 | y -= 1; 42 | break; 43 | default: 44 | break; 45 | } 46 | } 47 | return x == 0 && y == 0; 48 | } 49 | 50 | /** 51 | * 52 | * @param moves 53 | * @return 54 | */ 55 | public boolean judgeCircle1(String moves) { 56 | int x = 0, y = 0; 57 | for (char move : moves.toCharArray()) { 58 | if (move == 'U') 59 | y--; 60 | else if (move == 'D') 61 | y++; 62 | else if (move == 'L') 63 | x--; 64 | else if (move == 'R') 65 | x++; 66 | } 67 | return x == 0 && y == 0; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/KeyboardRow.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | 7 | /**********************键盘行**************/ 8 | /** 9 | * 给定一个单词列表,只返回可以使用在键盘同一行的字母打印出来的单词。 10 | * 11 | * 示例1: 12 | * 13 | * 输入: ["Hello", "Alaska", "Dad", "Peace"] 输出: ["Alaska", "Dad"] 14 | * 15 | * 注意: 你可以重复使用键盘上同一字符。 你可以假设输入的字符串将只包含字母。 16 | * 17 | * @author ffj 18 | * 19 | */ 20 | public class KeyboardRow { 21 | 22 | public String[] findWords(String[] words) { 23 | 24 | if (words == null || words.length == 0) 25 | return new String[0]; 26 | 27 | List list1 = Arrays.asList('Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'); 28 | List list2 = Arrays.asList('A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L'); 29 | // List list3 = Arrays.asList('Z', 'X', 'C', 'V', 'B', 'N', 'M'); 30 | 31 | List ls = new ArrayList<>(); 32 | 33 | for (int i = 0; i < words.length; i++) { 34 | String s = words[i]; 35 | int sum1 = 0, sum2 = 0, sum3 = 0; 36 | for (int j = 0; j < s.length(); j++) { 37 | char c = Character.toUpperCase(s.charAt(j)); // 转大写字母统一比较 38 | if (list1.contains(c)) 39 | sum1 = 1; 40 | else if (list2.contains(c)) 41 | sum2 = 1; 42 | else 43 | sum3 = 1; 44 | 45 | if (sum1 + sum2 + sum3 != 1) // 不在同一行直接退出该循环 46 | break; 47 | 48 | if (j == s.length() - 1) 49 | ls.add(s); 50 | } 51 | } 52 | return ls.toArray(new String[ls.size()]); 53 | } 54 | 55 | /** 56 | * Java8 一行代码 57 | */ 58 | // return Stream.of(words).filter(s -> 59 | // s.toLowerCase().matches("[qwertyuiop]*|[asdfghjkl]*|[zxcvbnm]*")).toArray(String[]::new); 60 | } 61 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/KthLargest.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | /*************数据流中的第K大元素************************/ 8 | /** 9 | * 设计一个找到数据流中第K大元素的类(class)。注意是排序后的第K大元素,不是第K个不同的元素。 10 | * 11 | * 你的 KthLargest 类需要一个同时接收整数 k 和整数数组nums 的构造器,它包含数据流中的初始元素。每次调用 12 | * KthLargest.add,返回当前数据流中第K大的元素。 13 | * 14 | * 示例: 15 | * 16 | * int k = 3; int[] arr = [4,5,8,2]; 17 | * 18 | * KthLargest kthLargest = new KthLargest(3, arr); 19 | * 20 | * kthLargest.add(3); // returns 4 21 | * 22 | * kthLargest.add(5); // returns 5 23 | * 24 | * kthLargest.add(10); // returns 5 25 | * 26 | * kthLargest.add(9); // returns 8 27 | * 28 | * kthLargest.add(4); // returns 8 29 | * 30 | * 说明: 你可以假设 nums 的长度≥ k-1 且k ≥ 1。 31 | * 32 | * @author ffj 33 | * 34 | */ 35 | public class KthLargest { 36 | 37 | public static void main(String[] args) { 38 | int k = 3; 39 | int[] arr = { 4, 5, 8, 2 }; 40 | KthLargest kthLargest = new KthLargest(k, arr); 41 | int result = 0; 42 | result = kthLargest.add(3); // returns 4 43 | result = kthLargest.add(5); // returns 5 44 | result = kthLargest.add(10); // returns 5 45 | result = kthLargest.add(9); // returns 8 46 | result = kthLargest.add(4); // returns 8 47 | System.out.println(result); 48 | } 49 | 50 | private List numList = new ArrayList<>(); 51 | 52 | private int num; 53 | 54 | public KthLargest(int k, int[] nums) { 55 | for (int n : nums) { 56 | numList.add(n); 57 | } 58 | num = k; 59 | } 60 | 61 | public int add(int val) { 62 | numList.add(val); 63 | Collections.sort(numList); 64 | return numList.get(numList.size() - num); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/LargestTriangleArea.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | /************最大三角形面积*************/ 4 | /** 5 | * 6 | * 给定包含多个点的集合,从其中取三个点组成三角形,返回能组成的最大三角形的面积。 7 | * 8 | * 示例: 9 | * 10 | * 输入: points = [[0,0],[0,1],[1,0],[0,2],[2,0]] 输出: 2 11 | * 12 | * 解释: 这五个点如下图所示。组成的橙色三角形是最大的,面积为2。 13 | * 14 | * 注意: 15 | * 16 | * 3 <= points.length <= 50. 17 | * 18 | * 不存在重复的点。 19 | * 20 | * -50 <= points[i][j] <= 50. 21 | * 22 | * 结果误差值在 10^-6 以内都认为是正确答案。 23 | * 24 | * @author ffj 25 | * 26 | */ 27 | public class LargestTriangleArea { 28 | 29 | public double largestTriangleArea(int[][] points) { 30 | int N = points.length; 31 | double ans = 0; 32 | for (int i = 0; i < N; ++i) 33 | for (int j = i + 1; j < N; ++j) 34 | for (int k = j + 1; k < N; ++k) 35 | // 循环取三个点 取最大值 36 | ans = Math.max(ans, area(points[i], points[j], points[k])); 37 | return ans; 38 | } 39 | 40 | /** 41 | * 根据三点计算面积 42 | * 43 | * @param P 44 | * @param Q 45 | * @param R 46 | * @return 47 | */ 48 | public double area(int[] P, int[] Q, int[] R) { 49 | return 0.5 * Math.abs(P[0] * Q[1] + Q[0] * R[1] + R[0] * P[1] - P[1] * Q[0] - Q[1] * R[0] - R[1] * P[0]); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/LeafSimilarTrees.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /***********************叶子相似的树******************/ 7 | /** 8 | * 9 | * 请考虑一颗二叉树上所有的叶子,这些叶子的值按从左到右的顺序排列形成一个 叶值序列 。 10 | * 11 | * 举个例子,如上图所示,给定一颗叶值序列为 (6, 7, 4, 9, 8) 的树。 12 | * 13 | * 如果有两颗二叉树的叶值序列是相同,那么我们就认为它们是 叶相似 的。 14 | * 15 | * 如果给定的两个头结点分别为 root1 和 root2 的树是叶相似的,则返回 true;否则返回 false 。 16 | * 17 | * 提示: 18 | * 19 | * 给定的两颗树可能会有 1 到 100 个结点。 20 | * 21 | * @author ffj 22 | * 23 | */ 24 | public class LeafSimilarTrees { 25 | 26 | public class TreeNode { 27 | int val; 28 | TreeNode left; 29 | TreeNode right; 30 | 31 | TreeNode(int x) { 32 | val = x; 33 | } 34 | } 35 | 36 | /** 37 | * DFS 深度优先搜索 38 | * 39 | * @param root1 40 | * @param root2 41 | * @return 42 | */ 43 | public boolean leafSimilar(TreeNode root1, TreeNode root2) { 44 | 45 | List list1 = new ArrayList<>(); 46 | List list2 = new ArrayList<>(); 47 | 48 | dfs(list1, root1); 49 | dfs(list2, root2); 50 | 51 | return list1.equals(list2); 52 | 53 | } 54 | 55 | private void dfs(List list, TreeNode node) { 56 | 57 | if (node == null) 58 | return; 59 | 60 | if (node.left == null && node.right == null) { 61 | list.add(node.val); 62 | } 63 | 64 | dfs(list, node.left); 65 | dfs(list, node.right); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/LengthOfLastWord.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | import java.util.Arrays; 4 | 5 | /************最后一个单词的长度**********/ 6 | /** 7 | * 给定一个仅包含大小写字母和空格 ' ' 的字符串,返回其最后一个单词的长度。 8 | * 9 | * 如果不存在最后一个单词,请返回 0 。 10 | * 11 | * 说明:一个单词是指由字母组成,但不包含任何空格的字符串。 12 | * 13 | * 示例: 14 | * 15 | * 输入: "Hello World" 输出: 5 16 | * 17 | * @author ffj 18 | * 19 | */ 20 | public class LengthOfLastWord { 21 | 22 | public static void main(String[] args) { 23 | String s = "Hello World"; 24 | new LengthOfLastWord().lengthOfLastWord(s); 25 | } 26 | 27 | public int lengthOfLastWord(String s) { 28 | 29 | if (s == null || s.length() == 0) 30 | return 0; 31 | 32 | s = s.trim(); // 去空格 33 | 34 | String[] strArr = s.split(" "); 35 | 36 | int len = strArr[strArr.length - 1].length(); 37 | 38 | System.out.println(Arrays.toString(strArr)); 39 | System.out.println(strArr[strArr.length - 1].length()); 40 | return len; 41 | 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/MaximumDepthOfBinaryTree.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /***************二叉树的最大深度**********/ 7 | /** 8 | * 给定一个二叉树,找出其最大深度。 9 | * 10 | * 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。 11 | * 12 | * 说明: 叶子节点是指没有子节点的节点。 13 | * 14 | * 示例: 15 | * 16 | * 给定二叉树 [3,9,20,null,null,15,7], 17 | * 18 | * 3 19 | * 20 | * / \ 21 | * 22 | * 9 20 23 | * 24 | * / \ 25 | * 26 | * 15 7 27 | * 28 | * 返回它的最大深度 3 。 29 | * 30 | * @author ffj 31 | * 32 | */ 33 | public class MaximumDepthOfBinaryTree { 34 | 35 | public class TreeNode { 36 | int val; 37 | TreeNode left; 38 | TreeNode right; 39 | 40 | TreeNode(int x) { 41 | val = x; 42 | } 43 | } 44 | 45 | /** 46 | * 层次遍历 47 | * 48 | * @param root 49 | * @return 50 | */ 51 | public int maxDepth(TreeNode root) { 52 | 53 | if (root == null) 54 | return 0; 55 | List nodes = new ArrayList<>(); 56 | nodes.add(root); 57 | int num = 0; 58 | List nodesTemp; // 临时存放节点 59 | while (!nodes.isEmpty()) { 60 | num++; 61 | nodesTemp = new ArrayList<>(); 62 | for (TreeNode node : nodes) { 63 | if (node.left != null) 64 | nodesTemp.add(node.left); 65 | if (node.right != null) 66 | nodesTemp.add(node.right); 67 | } 68 | nodes.clear(); // 先清除再复制值 69 | nodes.addAll(nodesTemp); 70 | } 71 | return num; 72 | } 73 | 74 | /** 75 | * DFS 比较大小 76 | * 77 | * @param root 78 | * @return 79 | */ 80 | public int maxDepth1(TreeNode root) { 81 | if (root == null) 82 | return 0; 83 | else { 84 | int leftLen = maxDepth1(root.left); 85 | int rightLen = maxDepth1(root.right); 86 | return Math.max(leftLen, rightLen) + 1; 87 | } 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/MaximumProductOfThreeNumbers.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | import java.util.Arrays; 4 | 5 | /********************三个数的最大乘积*************/ 6 | /** 7 | * 给定一个整型数组,在数组中找出由三个数组成的最大乘积,并输出这个乘积。 8 | * 9 | * 示例 1: 10 | * 11 | * 输入: [1,2,3] 输出: 6 示例 2: 12 | * 13 | * 输入: [1,2,3,4] 输出: 24 注意: 14 | * 15 | * 给定的整型数组长度范围是[3,104],数组中所有的元素范围是[-1000, 1000]。 输入的数组中任意三个数的乘积不会超出32位有符号整数的范围。 16 | * 17 | * @author ffj 18 | * 19 | */ 20 | public class MaximumProductOfThreeNumbers { 21 | 22 | public int maximumProduct(int[] nums) { 23 | 24 | Arrays.sort(nums); 25 | int len = nums.length; 26 | // 若第一第二为负数 则第一第二和最后相乘 否则倒数三数相乘最大 27 | return Math.max(nums[len - 1] * nums[len - 2] * nums[len - 3], nums[0] * nums[1] * nums[len - 1]); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/MaximumSubarray.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | /****************最大子序和******************/ 4 | /** 5 | * 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 6 | * 7 | * 示例: 8 | * 9 | * 输入: [-2,1,-3,4,-1,2,1,-5,4], 输出: 6 10 | * 11 | * 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。 12 | * 13 | * 进阶: 14 | * 15 | * 如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。 16 | * 17 | * @author ffj 18 | * 19 | */ 20 | public class MaximumSubarray { 21 | 22 | /** 23 | * 动态规划 DP : 24 | * 25 | * nums:[-2,1,-3,4,-1,2,1,-5,4] 26 | * 27 | * dp : [-2,1,-2,4,3,5,6,1,5] 28 | * 29 | * @param nums 30 | * @return 31 | */ 32 | public int maxSubArray(int[] nums) { 33 | if (nums.length == 0) 34 | return 0; 35 | 36 | int len = nums.length; 37 | int[] dp = new int[len]; 38 | dp[0] = nums[0]; 39 | int max = nums[0]; 40 | for (int i = 0; i < len; i++) { 41 | // 上一个数为负数说明之前的序列都作废 自身重新开始 42 | dp[i] = nums[i] + (dp[i - 1] > 0 ? dp[i - 1] : 0); 43 | max = Math.max(max, dp[i]); 44 | } 45 | return max; 46 | } 47 | 48 | /** 49 | * 思想跟上一个方法一样 50 | * 51 | * @param A 52 | * @return 53 | */ 54 | public static int maxSubArray1(int[] A) { 55 | int maxSoFar = A[0], maxEndingHere = A[0]; 56 | for (int i = 1; i < A.length; ++i) { 57 | // 之前的序列数之和与当前元素相比 取较大值 58 | maxEndingHere = Math.max(maxEndingHere + A[i], A[i]); 59 | maxSoFar = Math.max(maxSoFar, maxEndingHere); 60 | } 61 | return maxSoFar; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/MergeSortedArray.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | /***********合并两个有序数组********/ 4 | /** 5 | * 给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。 6 | * 7 | * 说明: 8 | * 9 | * 初始化 nums1 和 nums2 的元素数量分别为 m 和 n。 10 | * 11 | * 你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。 12 | * 13 | * 示例: 14 | * 15 | * 输入: nums1 = [1,2,3,0,0,0], m = 3 16 | * 17 | * nums2 = [2,5,6], n = 3 18 | * 19 | * 输出: [1,2,2,3,5,6] 20 | * 21 | * @author ffj 22 | * 23 | */ 24 | public class MergeSortedArray { 25 | 26 | public static void main(String[] args) { 27 | int[] nums1 = { 1, 2, 3, 0, 0, 0 }, nums2 = { 2, 5, 6 }; 28 | int m = 3, n = 3; 29 | new MergeSortedArray().merge(nums1, m, nums2, n); 30 | } 31 | 32 | /** 33 | * 从后往前比较 尾数谁大放谁 34 | * 35 | * @param nums1 36 | * @param m 37 | * @param nums2 38 | * @param n 39 | */ 40 | public void merge(int[] nums1, int m, int[] nums2, int n) { 41 | int nums1index = m - 1, nums2index = n - 1, index = m + n - 1; 42 | while (nums1index >= 0 && nums2index >= 0) { 43 | if (nums1[nums1index] > nums2[nums2index]) 44 | nums1[index--] = nums1[nums1index--]; 45 | else 46 | nums1[index--] = nums2[nums2index--]; 47 | } 48 | while (nums2index >= 0)// nums2 中还有元素剩余 49 | nums1[index--] = nums2[nums2index--]; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/MergeTwoSortedLists.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | /************************合并两个有序链表**************/ 4 | /** 5 | * 将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 6 | * 7 | * 示例: 8 | * 9 | * 输入:1->2->4, 1->3->4 10 | * 11 | * 输出:1->1->2->3->4->4 12 | * 13 | * @author ffj 14 | * 15 | */ 16 | public class MergeTwoSortedLists { 17 | 18 | public class ListNode { 19 | int val; 20 | ListNode next; 21 | 22 | ListNode(int x) { 23 | val = x; 24 | } 25 | } 26 | 27 | /** 28 | * 返回值较小的节点 递归 29 | * 30 | * @param l1 31 | * @param l2 32 | * @return 33 | */ 34 | public ListNode mergeTwoLists(ListNode l1, ListNode l2) { 35 | 36 | if (l1 == null) 37 | return l2; 38 | if (l2 == null) 39 | return l1; 40 | 41 | if (l1.val < l2.val) { 42 | l1.next = mergeTwoLists(l1.next, l2); 43 | return l1; 44 | } else { 45 | l2.next = mergeTwoLists(l1, l2.next); 46 | return l2; 47 | } 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/MiddleOfTheLinkedList.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | /********************链表的中间结点*******************/ 4 | /** 5 | * 给定一个带有头结点 head 的非空单链表,返回链表的中间结点。 6 | * 7 | * 如果有两个中间结点,则返回第二个中间结点。 8 | * 9 | * 示例 1: 10 | * 11 | * 输入:[1,2,3,4,5] 12 | * 13 | * 输出:此列表中的结点 3 (序列化形式:[3,4,5]) 返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。 14 | * 15 | * 注意,我们返回了一个 ListNode 类型的对象 ans,这样: ans.val = 3, ans.next.val = 4, 16 | * ans.next.next.val = 5, 以及 ans.next.next.next = NULL. 示例 2: 17 | * 18 | * 输入:[1,2,3,4,5,6] 19 | * 20 | * 输出:此列表中的结点 4 (序列化形式:[4,5,6]) 由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。 21 | * 22 | * 提示: 23 | * 24 | * 给定链表的结点数介于 1 和 100 之间。 25 | * 26 | * @author ffj 27 | * 28 | */ 29 | public class MiddleOfTheLinkedList { 30 | 31 | public class ListNode { 32 | int val; 33 | ListNode next; 34 | 35 | ListNode(int x) { 36 | val = x; 37 | } 38 | } 39 | 40 | public ListNode middleNode(ListNode head) { 41 | ListNode node = head, middleNode = head; 42 | while (node != null && node.next != null) { // 速度相差两倍 快的一方到链尾 慢的一方就是链中间 43 | middleNode = middleNode.next; 44 | node = node.next.next; 45 | } 46 | return middleNode; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/MinimumAbsoluteDifference.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | /******************最小绝对差**************/ 3 | 4 | import java.util.ArrayList; 5 | import java.util.Arrays; 6 | import java.util.List; 7 | 8 | /** 9 | * 给你个整数数组 arr,其中每个元素都 不相同。 10 | * 11 | * 请你找到所有具有最小绝对差的元素对,并且按升序的顺序返回。 12 | * 13 | *   14 | * 15 | * 示例 1: 16 | * 输入:arr = [4,2,1,3] 17 | * 输出:[[1,2],[2,3],[3,4]] 18 | * 19 | * 示例 2: 20 | * 输入:arr = [1,3,6,10,15] 21 | * 输出:[[1,3]] 22 | * 23 | * 示例 3: 24 | * 输入:arr = [3,8,-10,23,19,-4,-14,27] 25 | * 输出:[[-14,-10],[19,23],[23,27]] 26 | *   27 | * 28 | * 提示: 29 | * 30 | * 2 <= arr.length <= 10^5 31 | * -10^6 <= arr[i] <= 10^6 32 | * 33 | */ 34 | public class MinimumAbsoluteDifference { 35 | 36 | public List> minimumAbsDifference(int[] arr) { 37 | 38 | List> list = new ArrayList<>(); 39 | // 升序 40 | Arrays.sort(arr); 41 | // 差值 42 | int dv = Math.abs(arr[0] - arr[1]); 43 | List list1 = new ArrayList<>(); 44 | list1.add(arr[0]); 45 | list1.add(arr[1]); 46 | list.add(list1); 47 | for (int i = 1; i < arr.length - 1; i++) { 48 | int dvTemp = Math.abs(arr[i] - arr[i+1]); 49 | if (dvTemp <= dv) { 50 | List list2 = new ArrayList<>(); 51 | list2.add(arr[i]); 52 | list2.add(arr[i+1]); 53 | if (dvTemp < dv) { 54 | list.clear(); // 清空原先的 55 | dv = dvTemp; // 新的差值 56 | } 57 | list.add(list2); 58 | } 59 | } 60 | return list; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/NonDecreasingArray.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | /*********************非递减数列****************/ 4 | /** 5 | * 给定一个长度为 n 的整数数组,你的任务是判断在最多改变 1 个元素的情况下,该数组能否变成一个非递减数列。 6 | * 7 | * 我们是这样定义一个非递减数列的: 对于数组中所有的 i (1 <= i < n),满足 array[i] <= array[i + 1]。 8 | * 9 | * 示例 1: 10 | * 11 | * 输入: [4,2,3] 输出: True 解释: 你可以通过把第一个4变成1来使得它成为一个非递减数列。 12 | * 13 | * 示例 2: 14 | * 15 | * 输入: [4,2,1] 输出: False 解释: 你不能在只改变一个元素的情况下将其变为非递减数列。 说明: n 的范围为 [1, 10,000]。 16 | * 17 | * @author ffj 18 | * 19 | */ 20 | public class NonDecreasingArray { 21 | 22 | public boolean checkPossibility(int[] nums) { 23 | int length = nums.length; 24 | if (length == 1) 25 | return true; 26 | int num = 0; 27 | for (int i = 0; i < length - 1; i++) { 28 | if (nums[i] > nums[i + 1]) { 29 | num++; 30 | if (i != 0 && nums[i - 1] > nums[i + 1] && i + 3 <= length 31 | && nums[i + 2] < Math.max(nums[i - 1], nums[i])) // 前后数字都要比较 32 | return false; 33 | } 34 | if (num > 1) 35 | return false; 36 | } 37 | return true; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/PascalsTriangleII.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | /****************杨辉三角 II**************/ 7 | /** 8 | * 给定一个非负索引 k,其中 k ≤ 33,返回杨辉三角的第 k 行。 9 | * 10 | * 11 | * 12 | * 在杨辉三角中,每个数是它左上方和右上方的数的和。 13 | * 14 | * 示例: 15 | * 16 | * 输入: 3 17 | * 18 | * 输出: [1,3,3,1] 19 | * 20 | * @author ffj 21 | * 22 | */ 23 | public class PascalsTriangleII { 24 | 25 | public static void main(String[] args) { 26 | System.out.println(new PascalsTriangleII().getRow(3)); 27 | } 28 | 29 | // i=0: [1] 30 | // i=1: [1,1] 31 | // i=2: [1,1,1] -> [1,2,1] 32 | // i=3: [1,2,1,1] -> [1,2,3,1] -> [1,3,3,1] 33 | // i=4: [1,3,3,1,1] -> [1,3,3,4,1] -> [1,3,6,4,1] -> [1,4,6,4,1] 34 | public List getRow(int rowIndex) { 35 | Integer[] row = new Integer[rowIndex + 1]; 36 | Arrays.fill(row, 1); 37 | for (int i = 0; i <= rowIndex; i++) { 38 | for (int j = i - 1; j >= 1; j--) { 39 | row[j] += row[j - 1]; 40 | } 41 | } 42 | return Arrays.asList(row); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/PlusOne.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | import java.util.Arrays; 4 | 5 | /***************加一**********/ 6 | /** 7 | * 给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。 8 | * 9 | * 最高位数字存放在数组的首位, 数组中每个元素只存储一个数字。 10 | * 11 | * 你可以假设除了整数 0 之外,这个整数不会以零开头。 12 | * 13 | * 示例 1: 14 | * 15 | * 输入: [1,2,3] 输出: [1,2,4] 16 | * 17 | * 解释: 输入数组表示数字 123。 18 | * 19 | * 示例 2: 20 | * 21 | * 输入: [4,3,2,1] 输出: [4,3,2,2] 22 | * 23 | * 解释: 输入数组表示数字 4321。 24 | * 25 | * @author ffj 26 | * 27 | */ 28 | public class PlusOne { 29 | 30 | public static void main(String[] args) { 31 | int[] result = new PlusOne().plusOne(new int[] { 9, 9 }); 32 | System.out.println(Arrays.toString(result)); 33 | } 34 | 35 | public int[] plusOne(int[] digits) { 36 | int length = digits.length; 37 | digits[length - 1] = digits[length - 1] + 1; 38 | if (digits[length - 1] > 9) {// 9 -> 10 39 | digits[length - 1] = 0; 40 | digits = plushelper(length - 2, digits); 41 | } 42 | return digits; 43 | } 44 | 45 | /** 46 | * 进位 47 | * 48 | * @param index 49 | * @param digits 50 | * @return 51 | */ 52 | private int[] plushelper(int index, int[] digits) { 53 | if (index < 0) { 54 | int[] newArr = new int[digits.length + 1]; 55 | System.arraycopy(digits, 0, newArr, 1, digits.length); 56 | newArr[0] = 1; 57 | return newArr; 58 | } else { 59 | if (digits[index] == 9) { 60 | digits[index] = 0; 61 | digits = plushelper(index - 1, digits); 62 | } else 63 | digits[index] += 1; 64 | } 65 | return digits; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/PowerOfFour.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | /***************4的幂**********/ 4 | /** 5 | * 给定一个整数 (32 位有符号整数),请编写一个函数来判断它是否是 4 的幂次方。 6 | * 7 | * 示例 1: 8 | * 9 | * 输入: 16 输出: true 10 | * 11 | * 示例 2: 12 | * 13 | * 输入: 5 输出: false 14 | * 15 | * 进阶: 16 | * 17 | * 你能不使用循环或者递归来完成本题吗? 18 | * 19 | * @author ffj 20 | * 21 | */ 22 | public class PowerOfFour { 23 | 24 | public boolean isPowerOfFour(int num) { 25 | 26 | // 首先得是2的幂:((num-1) & num) == 0, 然后又因为4的幂 二进制表示都是(这里只举八位,int是32位,0101 27 | // 0101),如果是4的幂,应该坐落在bit为1的位上。 28 | return (((num - 1) & num) == 0) && ((num & (0x55555555)) != 0); 29 | 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/PrimeNumberOfSetBitsInBinaryRepresentation.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | /*********************二进制表示中质数个计算置位****************/ 4 | /** 5 | * 给定两个整数 L 和 R ,找到闭区间 [L, R] 范围内,计算置位位数为质数的整数个数。 6 | * 7 | * (注意,计算置位代表二进制表示中1的个数。例如 21 的二进制表示 10101 有 3 个计算置位。还有,1 不是质数。) 8 | * 9 | * 示例 1: 10 | * 11 | * 输入: L = 6, R = 10 输出: 4 12 | * 13 | * 解释: 14 | * 15 | * 6 -> 110 (2 个计算置位,2 是质数) 16 | * 17 | * 7 -> 111 (3 个计算置位,3 是质数) 18 | * 19 | * 9 -> 1001 (2 个计算置位,2 是质数) 20 | * 21 | * 10-> 1010 (2 个计算置位,2 是质数) 22 | * 23 | * 示例 2: 24 | * 25 | * 输入: L = 10, R = 15 输出: 5 26 | * 27 | * 解释: 28 | * 29 | * 10 -> 1010 (2 个计算置位, 2 是质数) 30 | * 31 | * 11 -> 1011 (3 个计算置位, 3 是质数) 32 | * 33 | * 12 -> 1100 (2 个计算置位, 2 是质数) 34 | * 35 | * 13 -> 1101 (3 个计算置位, 3 是质数) 36 | * 37 | * 14 -> 1110 (3 个计算置位, 3 是质数) 38 | * 39 | * 15 -> 1111 (4 个计算置位, 4 不是质数) 40 | * 41 | * 注意: 42 | * 43 | * L, R 是 L <= R 且在 [1, 10^6] 中的整数。 44 | * 45 | * R - L 的最大值为 10000。 46 | * 47 | * @author ffj 48 | * 49 | */ 50 | public class PrimeNumberOfSetBitsInBinaryRepresentation { 51 | 52 | public static void main(String[] args) { 53 | int L = 10, R = 15; 54 | System.out.println(new PrimeNumberOfSetBitsInBinaryRepresentation().countPrimeSetBits(L, R)); 55 | } 56 | 57 | public int countPrimeSetBits(int L, int R) { 58 | int sum = 0; 59 | for (int i = L; i <= R; i++) { 60 | // 计算置位 61 | int num = Integer.bitCount(i); 62 | if (isPrimeNumber(num)) 63 | sum++; 64 | } 65 | return sum; 66 | } 67 | 68 | /** 69 | * 判断一个数是否是质数(素数) 70 | * 71 | * @param num 72 | * @return 73 | */ 74 | private boolean isPrimeNumber(int num) { 75 | if (num == 2) 76 | return true;// 2特殊处理 77 | if (num < 2 || num % 2 == 0) 78 | return false;// 识别小于2的数和偶数 79 | for (int i = 3; i <= Math.sqrt(num); i += 2) { 80 | if (num % i == 0) {// 识别被奇数整除 81 | return false; 82 | } 83 | } 84 | return true; 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/RemoveDuplicatesFromSortedArray.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | /********************删除排序数组中的重复项***************/ 4 | /** 5 | * 给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。 6 | * 7 | * 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。 8 | * 9 | * 示例 1: 10 | * 11 | * 给定数组 nums = [1,1,2], 12 | * 13 | * 函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。 14 | * 15 | * 你不需要考虑数组中超出新长度后面的元素。 16 | * 17 | * 示例 2: 18 | * 19 | * 给定 nums = [0,0,1,1,1,2,2,3,3,4], 20 | * 21 | * 函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。 22 | * 23 | * 你不需要考虑数组中超出新长度后面的元素。 24 | * 25 | * 说明: 26 | * 27 | * 为什么返回数值是整数,但输出的答案是数组呢? 28 | * 29 | * 请注意,输入数组是以“引用”方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。 30 | * 31 | * 你可以想象内部操作如下: 32 | * 33 | * nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝 34 | * 35 | * int len = removeDuplicates(nums); 36 | * 37 | * 在函数里修改输入数组对于调用者是可见的。 38 | * 39 | * 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。 40 | * 41 | * for (int i = 0; i < len; i++) { print(nums[i]); } 42 | * 43 | * @author ffj 44 | * 45 | */ 46 | public class RemoveDuplicatesFromSortedArray { 47 | 48 | public static void main(String[] args) { 49 | int[] nums = { 0, 0, 1, 1, 1, 2, 2, 3, 3, 4 }; 50 | System.out.println("length :" + new RemoveDuplicatesFromSortedArray().removeDuplicates(nums)); 51 | } 52 | 53 | public int removeDuplicates(int[] nums) { 54 | if (nums.length == 0) 55 | return 0; 56 | int num = nums[0], index = 1; 57 | for (int i = 1; i < nums.length; i++) { 58 | if (nums[i] == num) // 值相等就跳过 59 | continue; 60 | nums[index++] = nums[i]; 61 | num = nums[i]; 62 | } 63 | 64 | for (int i = 0; i < index; i++) 65 | System.out.println(nums[i]); 66 | return index; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/RemoveDuplicatesFromSortedList.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | /***************** 删除排序链表中的重复元素 ****************************/ 4 | /** 5 | * 给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。 6 | * 7 | * 示例 1: 8 | * 9 | * 输入: 1->1->2 输出: 1->2 10 | * 11 | * 示例 2: 12 | * 13 | * 输入: 1->1->2->3->3 输出: 1->2->3 14 | * 15 | * @author ffj 16 | * 17 | */ 18 | public class RemoveDuplicatesFromSortedList { 19 | public ListNode deleteDuplicates(ListNode head) { 20 | if (head == null || head.next == null) { 21 | return head; 22 | } 23 | ListNode p = head; 24 | ListNode q = head.next; 25 | while (p != null && q != null) { 26 | while (q != null && q.val == p.val) { // 值相等一直后移 27 | q = q.next; 28 | } 29 | p.next = q; // 赋值 30 | p = q; // p后移 31 | if (q != null) { 32 | q = q.next; // 后移 33 | } 34 | } 35 | return head; 36 | } 37 | 38 | /** 39 | * 官网解法 40 | * 41 | * @param head 42 | * @return 43 | */ 44 | public ListNode deleteDuplicates1(ListNode head) { 45 | ListNode current = head; 46 | while (current != null && current.next != null) { 47 | if (current.next.val == current.val) { 48 | current.next = current.next.next; 49 | } else { 50 | current = current.next; 51 | } 52 | } 53 | return head; 54 | } 55 | 56 | public class ListNode { 57 | int val; 58 | ListNode next; 59 | 60 | ListNode(int x) { 61 | val = x; 62 | } 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/ReverseInteger.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | /*********************反转整数*******************/ 4 | /** 5 | * 给定一个 32 位有符号整数,将整数中的数字进行反转。 6 | * 7 | * 示例 1: 8 | * 9 | * 输入: 123 输出: 321 10 | * 11 | * 示例 2: 12 | * 13 | * 输入: -123 输出: -321 14 | * 15 | * 示例 3: 16 | * 17 | * 输入: 120 输出: 21 18 | * 19 | * 注意: 假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−231, 231 − 1]。根据这个假设,如果反转后的整数溢出,则返回 0。 20 | * 21 | * @author ffj 22 | * 23 | */ 24 | public class ReverseInteger { 25 | 26 | public static void main(String[] args) { 27 | 28 | System.out.println(reverse(-345)); 29 | } 30 | 31 | public static int reverse(int x) { 32 | 33 | int rev = 0; 34 | 35 | while (x != 0) { 36 | int pop = x % 10; // 取余 37 | x /= 10; // 取整 38 | // Integer.MAX_VALUE = 2147483647 39 | if (rev > Integer.MAX_VALUE / 10 || (rev == Integer.MAX_VALUE / 10 && pop > 7)) 40 | return 0; 41 | // Integer.MIN_VALUE = -2147483648 42 | if (rev < Integer.MIN_VALUE / 10 || (rev == Integer.MIN_VALUE / 10 && pop < -8)) 43 | return 0; 44 | 45 | rev = rev * 10 + pop; 46 | } 47 | 48 | return rev; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/ReverseLinkedList.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | /*****************反转链表***************/ 4 | /** 5 | * 反转一个单链表。 6 | * 7 | * 示例: 8 | * 9 | * 输入: 1->2->3->4->5->NULL 10 | * 11 | * 输出: 5->4->3->2->1->NULL 12 | * 13 | * 进阶: 你可以迭代或递归地反转链表。你能否用两种方法解决这道题? 14 | * 15 | * @author ffj 16 | * 17 | */ 18 | public class ReverseLinkedList { 19 | public class ListNode { 20 | int val; 21 | ListNode next; 22 | 23 | ListNode(int x) { 24 | val = x; 25 | } 26 | } 27 | 28 | /** 29 | * 递归 30 | * 31 | * @param head 32 | * @return 33 | */ 34 | public ListNode reverseList(ListNode head) { 35 | ListNode reverseList = null; 36 | return helper(head, reverseList); 37 | } 38 | 39 | private ListNode helper(ListNode head, ListNode reverseList) { 40 | if (head == null) // 反转结束 41 | return reverseList; 42 | // 节点指针变换 43 | ListNode tempNode = head.next; 44 | head.next = reverseList; 45 | return helper(tempNode, head); 46 | } 47 | 48 | /** 49 | * 迭代 50 | * 51 | * @param head 52 | * @return 53 | */ 54 | public ListNode reverseList1(ListNode head) { 55 | 56 | ListNode newHead = null; 57 | while (head != null) { // 遍历 58 | ListNode next = head.next; 59 | head.next = newHead; 60 | newHead = head; 61 | head = next; 62 | } 63 | return newHead; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/ReverseVowelsOfAString.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | /*********反转字符串中的元音字母********/ 4 | /** 5 | * 6 | * 编写一个函数,以字符串作为输入,反转该字符串中的元音字母。 7 | * 8 | * 示例 1: 9 | * 10 | * 输入: "hello" 输出: "holle" 11 | * 12 | * 示例 2: 13 | * 14 | * 输入: "leetcode" 输出: "leotcede" 15 | * 16 | * 说明: 元音字母不包含字母"y"。 17 | * 18 | * @author ffj 19 | * 20 | */ 21 | public class ReverseVowelsOfAString { 22 | 23 | public String reverseVowels(String s) { 24 | 25 | if (s == null || s.length() == 0) 26 | return s; 27 | String vowels = "aeiouAEIOU"; 28 | char[] chars = s.toCharArray(); 29 | int start = 0, end = s.length() - 1; 30 | while (start < end) { 31 | // 首尾指针找出元音字母然后互换 32 | while (start < end && !vowels.contains(chars[start] + "")) 33 | start++; 34 | while (start < end && !vowels.contains(chars[end] + "")) 35 | end--; 36 | // 互换 37 | char temp = chars[start]; 38 | chars[start] = chars[end]; 39 | chars[end] = temp; 40 | start++; 41 | end--; 42 | } 43 | return new String(chars); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/RotateArray.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | /**************旋转数组***********/ 4 | /** 5 | * 给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。 6 | * 7 | * 示例 1: 8 | * 9 | * 输入: [1,2,3,4,5,6,7] 和 k = 3 输出: [5,6,7,1,2,3,4] 10 | * 11 | * 解释: 向右旋转 1 步: [7,1,2,3,4,5,6] 12 | * 13 | * 向右旋转 2 步: [6,7,1,2,3,4,5] 14 | * 15 | * 向右旋转 3 步: [5,6,7,1,2,3,4] 16 | * 17 | * 示例 2: 18 | * 19 | * 输入: [-1,-100,3,99] 和 k = 2 20 | * 21 | * 输出: [3,99,-1,-100] 22 | * 23 | * 解释: 向右旋转 1 步: [99,-1,-100,3] 24 | * 25 | * 向右旋转 2 步: [3,99,-1,-100] 26 | * 27 | * 说明: 28 | * 29 | * 尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。 30 | * 31 | * 要求使用空间复杂度为 O(1) 的原地算法。 32 | * 33 | * @author ffj 34 | * 35 | */ 36 | public class RotateArray { 37 | 38 | /** 39 | * 循环赋值(参考 Collections源码 rotate1 方法) 40 | * 41 | * @param nums 42 | * @param k 43 | */ 44 | public void rotate(int[] nums, int k) { 45 | int len = nums.length; 46 | int offset = k % len; 47 | if (offset < 0) 48 | offset += len; 49 | if (offset == 0) 50 | return; 51 | for (int i = 0, nMoved = 0; nMoved != len; i++) { 52 | int value = nums[i]; 53 | int index = i; 54 | do { 55 | // 下标渐移 56 | index += offset; 57 | if (index >= len) 58 | index -= len; 59 | // 值交换 60 | int temp = nums[index]; 61 | nums[index] = value; 62 | value = temp; 63 | // 记录改变的值数量 64 | nMoved++; 65 | } while (index != i); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/RotateString.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | /***************旋转字符串**********/ 4 | /** 5 | * 给定两个字符串, A 和 B。 6 | * 7 | * A 的旋转操作就是将 A 最左边的字符移动到最右边。 例如, 若 A = 'abcde',在移动一次之后结果就是'bcdea' 8 | * 。如果在若干次旋转操作之后,A 能变成B,那么返回True。 9 | * 10 | * 示例 1: 11 | * 12 | * 输入: A = 'abcde', B = 'cdeab' 输出: true 13 | * 14 | * 示例 2: 15 | * 16 | * 输入: A = 'abcde', B = 'abced' 输出: false 17 | * 18 | * 注意: 19 | * 20 | * A 和 B 长度不超过 100。 21 | * 22 | * @author ffj 23 | * 24 | */ 25 | public class RotateString { 26 | 27 | public static void main(String[] args) { 28 | String A = "abcde", B = "abced"; 29 | System.out.println(new RotateString().rotateString(A, B)); 30 | } 31 | 32 | public boolean rotateString(String A, String B) { 33 | int len = A.length(); 34 | if (len != B.length()) 35 | return false; 36 | if (A.equals(B)) 37 | return true; 38 | String copyA = A; 39 | for (int i = 0; i < len; i++) { 40 | copyA = copyA.substring(1) + copyA.substring(0, 1); 41 | if (copyA.equals(B)) 42 | return true; 43 | } 44 | return false; 45 | } 46 | 47 | /** 48 | * 妙不可言 49 | * 50 | * @param A 51 | * @param B 52 | * @return 53 | */ 54 | public boolean rotateString1(String A, String B) { 55 | if (A.length() != B.length()) 56 | return false; 57 | return (A + A).contains(B); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/SameNum.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | /****************数组中重复的数字**********/ 3 | 4 | /** 5 | * 找出数组中重复的数字。 6 | * 7 | * 8 | * 在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。 9 | * 10 | * 示例 1: 11 | * 12 | * 输入: 13 | * [2, 3, 1, 0, 2, 5, 3] 14 | * 输出:2 或 3 15 | *   16 | * 17 | * 限制: 18 | * 19 | * 2 <= n <= 100000 20 | * 21 | */ 22 | public class SameNum { 23 | 24 | public int findRepeatNumber(int[] nums) { 25 | 26 | int[] temp = new int[nums.length]; 27 | for (int num : nums) { 28 | if (temp[num] == 1) 29 | return num; 30 | temp[num] = 1; 31 | } 32 | return -1; 33 | } 34 | 35 | /** 36 | * 原地互换 37 | * @param nums 38 | * @return 39 | */ 40 | public int findRepeatNumber1(int[] nums) { 41 | int temp; 42 | for(int i = 0; i < nums.length; i++) { 43 | while (nums[i] != i){ // 保证下标即是值 44 | if (nums[i] == nums[nums[i]]) { // 找到重复 返回重复值 45 | return nums[i]; 46 | } 47 | temp = nums[i]; // 互换值 48 | nums[i] = nums[temp]; 49 | nums[temp] = temp; 50 | } 51 | } 52 | return -1; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/SameTree.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | /*********************相同的树***************/ 4 | /** 5 | * 6 | * 给定两个二叉树,编写一个函数来检验它们是否相同。 7 | * 8 | * 如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。 9 | * 10 | * 示例 1: 11 | * 12 | * 输入: 13 | * 14 | * -1-- 1 15 | * 16 | * / \ / \ 17 | * 18 | * 2 3 2 3 19 | * 20 | * [1,2,3], [1,2,3] 21 | * 22 | * 输出: true 23 | * 24 | * 示例 2: 25 | * 26 | * 输入: 27 | * 28 | * 1 1 29 | * 30 | * / \ 31 | * 32 | * 2 2 33 | * 34 | * [1,2], [1,null,2] 35 | * 36 | * 输出: false 37 | * 38 | * 示例 3: 39 | * 40 | * 输入: 41 | * 42 | * -1-- 1 43 | * 44 | * / \ / \ 45 | * 46 | * 2 1 1 2 47 | * 48 | * [1,2,1], [1,1,2] 49 | * 50 | * 输出: false 51 | * 52 | * @author ffj 53 | * 54 | */ 55 | public class SameTree { 56 | 57 | public static void main(String[] args) { 58 | 59 | } 60 | 61 | public class TreeNode { 62 | int val; 63 | TreeNode left; 64 | TreeNode right; 65 | 66 | TreeNode(int x) { 67 | val = x; 68 | } 69 | } 70 | 71 | /** 72 | * DFS 73 | * 74 | * @param p 75 | * @param q 76 | * @return 77 | */ 78 | public boolean isSameTree(TreeNode p, TreeNode q) { 79 | 80 | if (p == null && q == null) 81 | return true; 82 | 83 | if ((p == null && q != null) || (p != null && q == null) || p.val != q.val) 84 | return false; 85 | boolean value1, value2; 86 | value1 = isSameTree(p.left, q.left); 87 | value2 = isSameTree(p.right, q.right); 88 | return value1 && value2; 89 | 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/SearchInserPosition.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | /*************搜索插入位置***************/ 4 | /** 5 | * 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 6 | * 7 | * 你可以假设数组中无重复元素。 8 | * 9 | * 示例 1: 10 | * 11 | * 输入: [1,3,5,6], 5 输出: 2 12 | * 13 | * 示例 2: 14 | * 15 | * 输入: [1,3,5,6], 2 输出: 1 16 | * 17 | * 示例 3: 18 | * 19 | * 输入: [1,3,5,6], 7 输出: 4 20 | * 21 | * 示例 4: 22 | * 23 | * 输入: [1,3,5,6], 0 输出: 0 24 | * 25 | * @author ffj 26 | * 27 | */ 28 | public class SearchInserPosition { 29 | 30 | /** 31 | * 二分搜索 32 | * 33 | * @param nums 34 | * @param target 35 | * @return 36 | */ 37 | public int searchInsert(int[] nums, int target) { 38 | if (nums.length == 0) 39 | return 0; 40 | int length = nums.length; 41 | int mid = 0, lo = 0, hi = length - 1; 42 | while (lo < hi) { 43 | mid = (lo + hi) / 2; 44 | if (nums[mid] > target) 45 | hi = mid - 1; 46 | else if (nums[mid] < target) 47 | lo = mid + 1; 48 | else 49 | return mid; 50 | } 51 | System.out.println("hi :" + hi + " lo :" + lo); 52 | return target <= nums[lo] ? lo : lo + 1; 53 | } 54 | 55 | public static void main(String[] args) { 56 | int[] nums = { 1 }; 57 | int target = 1; 58 | System.out.println(new SearchInserPosition().searchInsert(nums, target)); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/SparseArraySearchLcci.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | /***********稀疏数组搜索********/ 3 | 4 | /** 5 | * 稀疏数组搜索。有个排好序的字符串数组,其中散布着一些空字符串,编写一种方法,找出给定字符串的位置。 6 | * 7 | * 示例1: 8 | * 9 | * 输入: words = ["at", "", "", "", "ball", "", "", "car", "", "","dad", "", ""], s = "ta" 10 | * 输出:-1 11 | * 说明: 不存在返回-1。 12 | * 示例2: 13 | * 14 | * 输入:words = ["at", "", "", "", "ball", "", "", "car", "", "","dad", "", ""], s = "ball" 15 | * 输出:4 16 | * 提示: 17 | * 18 | * words的长度在[1, 1000000]之间 19 | * 20 | */ 21 | public class SparseArraySearchLcci { 22 | 23 | public static void main(String[] args) { 24 | System.out.println(new SparseArraySearchLcci().findString(new String[]{"at", "", "", "", "ball", "", "", "car", "", "","dad", "", ""}, "ta")); 25 | } 26 | 27 | /** 28 | * 二分查找 29 | * @param words 30 | * @param s 31 | * @return 32 | */ 33 | public int findString(String[] words, String s) { 34 | int start = 0; 35 | int end = words.length - 1; 36 | 37 | while(start <= end) { 38 | int mid = (start + end) / 2; 39 | int omid = mid; 40 | while(mid >= 0 && words[mid].length() == 0) { // 向左透过空串 41 | mid--; 42 | } 43 | if (mid < 0) return -1; 44 | if (words[mid].compareTo(s) < 0) { 45 | start = omid + 1; // 如果在右边,使用初始mid 46 | } else if (words[mid].compareTo(s) > 0) { 47 | end = mid - 1; 48 | } else { 49 | return mid; 50 | } 51 | } 52 | return -1; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/Sqrtx.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | /*************x 的平方根************/ 4 | /** 5 | * 实现 int sqrt(int x) 函数。 6 | * 7 | * 计算并返回 x 的平方根,其中 x 是非负整数。 8 | * 9 | * 由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。 10 | * 11 | * 示例 1: 12 | * 13 | * 输入: 4 输出: 2 14 | * 15 | * 示例 2: 16 | * 17 | * 输入: 8 输出: 2 18 | * 19 | * 说明: 8 的平方根是 2.82842..., 由于返回类型是整数,小数部分将被舍去。 20 | * 21 | * @author ffj 22 | * 23 | */ 24 | public class Sqrtx { 25 | 26 | public static void main(String[] args) { 27 | int result = new Sqrtx().mySqrt(9); 28 | System.out.println("result:" + result); 29 | } 30 | 31 | /** 32 | * 累加法 33 | * 34 | * @param x 35 | * @return 36 | */ 37 | public int mySqrt(int x) { 38 | if (x == 0) 39 | return 0; 40 | int result = 1; 41 | while (true) { 42 | if ((result + 1) <= x / (result + 1)) 43 | result++; 44 | else 45 | break; 46 | } 47 | return result; 48 | } 49 | 50 | /** 51 | * 二分法 52 | * 53 | * @param x 54 | * @return 55 | */ 56 | public int mySqrt1(int x) { 57 | if (x == 0) 58 | return 0; 59 | int l = 1, u = x; 60 | while (true) { 61 | int mid = (l + u) / 2; 62 | if (mid > x / mid) 63 | u = mid - 1; 64 | else if (mid + 1 > x / (mid + 1)) 65 | return mid; 66 | else 67 | l = mid + 1; 68 | } 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/TransposeMatrix.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | /*******************转置矩阵**************/ 4 | /** 5 | * 给定一个矩阵 A, 返回 A 的转置矩阵。 6 | * 7 | * 矩阵的转置是指将矩阵的主对角线翻转,交换矩阵的行索引与列索引。 8 | * 9 | * 10 | * 11 | * 示例 1: 12 | * 13 | * 输入:[[1,2,3],[4,5,6],[7,8,9]] 输出:[[1,4,7],[2,5,8],[3,6,9]] 14 | * 15 | * 示例 2: 16 | * 17 | * 输入:[[1,2,3],[4,5,6]] 输出:[[1,4],[2,5],[3,6]] 18 | * 19 | * 20 | * 提示: 21 | * 22 | * 1 <= A.length <= 1000 23 | * 24 | * 1 <= A[0].length <= 1000 25 | * 26 | * @author ffj 27 | * 28 | */ 29 | public class TransposeMatrix { 30 | 31 | /** 32 | * 横、纵坐标互换即可 33 | * 34 | * @param A 35 | * @return 36 | */ 37 | public int[][] transpose(int[][] A) { 38 | 39 | int rows = A.length, cols = A[0].length; 40 | 41 | int[][] B = new int[cols][rows]; 42 | 43 | for (int row = 0; row < rows; row++) { 44 | for (int col = 0; col < cols; col++) { 45 | B[col][row] = A[row][col]; 46 | } 47 | } 48 | return B; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/easy/TwoSumIIInputArrayIsSorted.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.easy; 2 | 3 | import java.util.Arrays; 4 | 5 | /*****************两数之和 II - 输入有序数组**************/ 6 | /** 7 | * 8 | * 给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。 9 | * 10 | * 函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。 11 | * 12 | * 说明: 13 | * 14 | * 返回的下标值(index1 和 index2)不是从零开始的。 你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。 15 | * 16 | * 示例: 17 | * 18 | * 输入: numbers = [2, 7, 11, 15], target = 9 输出: [1,2] 19 | * 20 | * 解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。 21 | * 22 | * @author ffj 23 | * 24 | */ 25 | public class TwoSumIIInputArrayIsSorted { 26 | 27 | public static void main(String[] args) { 28 | int[] numbers = new int[] { 12, 13, 23, 28, 43, 44, 59, 60, 61, 68, 70, 86, 88, 92, 124, 125, 136, 168, 173, 29 | 173, 180, 199, 212, 221, 227, 230, 277, 282, 306, 314, 316, 321, 325, 328, 336, 337, 363, 365, 368, 370, 30 | 370, 371, 375, 384, 387, 394, 400, 404, 414, 422, 422, 427, 430, 435, 457, 493, 506, 527, 531, 538, 541, 31 | 546, 568, 583, 585, 587, 650, 652, 677, 691, 730, 737, 740, 751, 755, 764, 778, 783, 785, 789, 794, 803, 32 | 809, 815, 847, 858, 863, 863, 874, 887, 896, 916, 920, 926, 927, 930, 933, 957, 981, 997 }; 33 | int target = 542; 34 | System.out.println(Arrays.toString(twoSum(numbers, target))); 35 | } 36 | 37 | public static int[] twoSum(int[] numbers, int target) { 38 | 39 | int[] arr = new int[2]; 40 | if (numbers.length == 0) 41 | return arr; 42 | 43 | int len = numbers.length; 44 | int lo = 0, hi = len - 1; 45 | while (lo < hi) { // 一次移动一个指针 46 | int sum = numbers[lo] + numbers[hi]; 47 | if (sum == target) { 48 | arr[0] = lo + 1; 49 | arr[1] = hi + 1; 50 | return arr; 51 | } else if (sum > target) { 52 | hi--; 53 | } else { 54 | lo++; 55 | } 56 | } 57 | return arr; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/hard/BinaryTreePostorderTraversal.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.hard; 2 | 3 | import java.util.LinkedList; 4 | import java.util.List; 5 | import java.util.Stack; 6 | 7 | /**************二叉树的后序遍历***************/ 8 | /** 9 | * 给定一个二叉树,返回它的 后序 遍历。 10 | * 11 | * 示例: 12 | * 13 | * 输入: [1,null,2,3] 14 | * 15 | * 1 16 | * 17 | * \ 18 | * 19 | * 2 20 | * 21 | * / 22 | * 23 | * 3 24 | * 25 | * 输出: [3,2,1] 26 | * 27 | * 进阶: 递归算法很简单,你可以通过迭代算法完成吗? 28 | * 29 | * @author ffj 30 | * 31 | */ 32 | public class BinaryTreePostorderTraversal { 33 | 34 | public class TreeNode { 35 | int val; 36 | TreeNode left; 37 | TreeNode right; 38 | 39 | TreeNode(int x) { 40 | val = x; 41 | } 42 | } 43 | 44 | /** 45 | * 根 -> 右 -> 左 存入,遍历完全后从前往后即是 左 -> 右 -> 根 46 | * 47 | * @param root 48 | * @return 49 | */ 50 | public List postorderTraversal(TreeNode root) { 51 | LinkedList ans = new LinkedList<>(); 52 | Stack stack = new Stack<>(); 53 | if (root == null) 54 | return ans; 55 | 56 | stack.push(root); 57 | while (!stack.isEmpty()) { 58 | TreeNode cur = stack.pop(); 59 | // 在最前面存值 60 | ans.addFirst(cur.val); 61 | // 先左结点入栈 62 | if (cur.left != null) { 63 | stack.push(cur.left); 64 | } 65 | // 再是右结点入栈 66 | if (cur.right != null) { 67 | stack.push(cur.right); 68 | } 69 | } 70 | return ans; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/hard/DungeonGame.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.hard; 2 | 3 | import java.util.Arrays; 4 | 5 | /*******************地下城游戏***************/ 6 | /** 7 | * 8 | * 一些恶魔抓住了公主(P)并将她关在了地下城的右下角。地下城是由 M x N 9 | * 个房间组成的二维网格。我们英勇的骑士(K)最初被安置在左上角的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主。 10 | * 11 | * 骑士的初始健康点数为一个正整数。如果他的健康点数在某一时刻降至 0 或以下,他会立即死亡。 12 | * 13 | * 有些房间由恶魔守卫,因此骑士在进入这些房间时会失去健康点数(若房间里的值为负整数,则表示骑士将损失健康点数);其他房间要么是空的(房间里的值为 14 | * 0),要么包含增加骑士健康点数的魔法球(若房间里的值为正整数,则表示骑士将增加健康点数)。 15 | * 16 | * 为了尽快到达公主,骑士决定每次只向右或向下移动一步。 17 | * 18 | * 19 | * 编写一个函数来计算确保骑士能够拯救到公主所需的最低初始健康点数。 20 | * 21 | * 例如,考虑到如下布局的地下城,如果骑士遵循最佳路径 右 -> 右 -> 下 -> 下,则骑士的初始健康点数至少为 7。 22 | * 23 | * -2 (K) -3 3 -5 -10 1 10 30 -5 (P) 24 | * 25 | * 26 | * 说明: 27 | * 28 | * 骑士的健康点数没有上限。 29 | * 30 | * 任何房间都可能对骑士的健康点数造成威胁,也可能增加骑士的健康点数,包括骑士进入的左上角房间以及公主被监禁的右下角房间。 31 | * 32 | * @author ffj 33 | * 34 | */ 35 | public class DungeonGame { 36 | 37 | public static void main(String[] args) { 38 | int[][] dungeon = new int[][] { { -2, -3, 3 }, { -5, -10, 1 }, { 10, 30, -5 } }; 39 | System.out.println(calculateMinimumHP(dungeon)); 40 | } 41 | 42 | /** 43 | * 从最底层开始逐层往上 覆盖原先数值 44 | * 45 | * @param dungeon 46 | * @return 47 | */ 48 | public static int calculateMinimumHP(int[][] dungeon) { 49 | int m = dungeon.length, n = dungeon[0].length; 50 | // 增加一栏用于边界值比较 51 | int[] dp = new int[n + 1]; 52 | Arrays.fill(dp, Integer.MAX_VALUE); 53 | dp[n - 1] = 1; 54 | for (int i = m - 1; i >= 0; --i) { 55 | for (int j = n - 1; j >= 0; --j) { 56 | // 两种情况中所需生命值最小的情况 57 | dp[j] = Math.max(1, Math.min(dp[j], dp[j + 1]) - dungeon[i][j]); 58 | } 59 | } 60 | return dp[0]; 61 | 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/hard/FindMinimumInRotatedSortedArrayII.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.hard; 2 | 3 | import java.util.Arrays; 4 | 5 | /*************************寻找旋转排序数组中的最小值 II************/ 6 | /** 7 | * 假设按照升序排序的数组在预先未知的某个点上进行了旋转。 8 | * 9 | * ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。 10 | * 11 | * 请找出其中最小的元素。 12 | * 13 | * 注意数组中可能存在重复的元素。 14 | * 15 | * 示例 1: 16 | * 17 | * 输入: [1,3,5] 输出: 1 18 | * 19 | * 示例 2: 20 | * 21 | * 输入: [2,2,2,0,1] 输出: 0 22 | * 23 | * 说明: 24 | * 25 | * 这道题是 寻找旋转排序数组中的最小值 的延伸题目。 允许重复会影响算法的时间复杂度吗?会如何影响,为什么? 26 | * 27 | * @author ffj 28 | * 29 | */ 30 | public class FindMinimumInRotatedSortedArrayII { 31 | 32 | public int findMin(int[] nums) { 33 | 34 | Arrays.sort(nums); 35 | return nums[0]; 36 | 37 | } 38 | 39 | /** 40 | * 讨论中解法 41 | * 42 | * @param nums 43 | * @return 44 | */ 45 | public int findMin1(int[] nums) { 46 | int l = 0, r = nums.length - 1; 47 | while (l < r) { 48 | int mid = (l + r) / 2; 49 | if (nums[mid] < nums[r]) { 50 | r = mid; 51 | } else if (nums[mid] > nums[r]) { 52 | l = mid + 1; 53 | } else { 54 | r--; // nums[mid]=nums[r] no idea, but we can eliminate nums[r]; 55 | } 56 | } 57 | return nums[l]; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/hard/JumpGameII.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.hard; 2 | 3 | /*******跳跃游戏 II**********/ 4 | /** 5 | * 给定一个非负整数数组,你最初位于数组的第一个位置。 6 | * 7 | * 数组中的每个元素代表你在该位置可以跳跃的最大长度。 8 | * 9 | * 你的目标是使用最少的跳跃次数到达数组的最后一个位置。 10 | * 11 | * 示例: 12 | * 13 | * 输入: [2,3,1,1,4] 输出: 2 14 | * 15 | * 解释: 跳到最后一个位置的最小跳跃数是 2。 从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。 16 | * 17 | * 说明: 18 | * 19 | * 假设你总是可以到达数组的最后一个位置。 20 | * 21 | * @author ffj 22 | * 23 | */ 24 | public class JumpGameII { 25 | 26 | public int jump(int[] nums) { 27 | 28 | int jumps = 0, curEnd = 0, curFarthest = 0; 29 | for (int i = 0; i < nums.length; i++) { 30 | // 选择较远的点 31 | curFarthest = Math.max(curFarthest, i + nums[i]); 32 | if (i == curEnd) { // 所能到达的最远点 33 | jumps++; // 步数加一 34 | curEnd = curFarthest; // 赋值为最远点 35 | } 36 | } 37 | return jumps; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/hard/KInversePairsArray.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.hard; 2 | 3 | /*************K个逆序对数组************/ 4 | /** 5 | * 给出两个整数 n 和 k,找出所有包含从 1 到 n 的数字,且恰好拥有 k 个逆序对的不同的数组的个数。 6 | * 7 | * 逆序对的定义如下:对于数组的第i个和第 j个元素,如果满i < j且 a[i] > a[j],则其为一个逆序对;否则不是。 8 | * 9 | * 由于答案可能很大,只需要返回 答案 mod 109 + 7 的值。 10 | * 11 | * 示例 1: 12 | * 13 | * 输入: n = 3, k = 0 输出: 1 14 | * 15 | * 解释: 只有数组 [1,2,3] 包含了从1到3的整数并且正好拥有 0 个逆序对。 16 | * 17 | * 示例 2: 18 | * 19 | * 输入: n = 3, k = 1 输出: 2 20 | * 21 | * 解释: 22 | * 23 | * 数组 [1,3,2] 和 [2,1,3] 都有 1 个逆序对。 24 | * 25 | * 说明: 26 | * 27 | * n 的范围是 [1, 1000] 并且 k 的范围是 [0, 1000]。 28 | * 29 | * @author ffj 30 | * 31 | */ 32 | public class KInversePairsArray { 33 | 34 | /** 35 | * DP : dp[n][k] = dp[n][k-1] + dp[n - 1][k] - dp[n - 1][k - n] 36 | * 37 | * 当k>=n的时候,最后一项的数组坐标才能为非负数,从而最后一项才有值 38 | * 39 | * @param n 40 | * @param k 41 | * @return 42 | */ 43 | public int kInversePairs(int n, int k) { 44 | int[][] dp = new int[n + 1][k + 1]; 45 | int M = 1000000007; 46 | for (int i = 1; i <= n; i++) { 47 | for (int j = 0; j <= k; j++) { 48 | if (j == 0) 49 | dp[i][j] = 1; 50 | else { 51 | int val = (dp[i - 1][j] + M - ((j - i) >= 0 ? dp[i - 1][j - i] : 0)) % M; 52 | dp[i][j] = (dp[i][j - 1] + val) % M; 53 | } 54 | } 55 | } 56 | return ((dp[n][k] + M - (k > 0 ? dp[n][k - 1] : 0)) % M); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/hard/MedianOfTwoSortedArrays.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.hard; 2 | 3 | /****************两个排序数组的中位数******************/ 4 | /** 5 | * 给定两个大小为 m 和 n 的有序数组 nums1 和 nums2 。 6 | * 7 | * 请找出这两个有序数组的中位数。要求算法的时间复杂度为 O(log (m+n)) 。 8 | * 9 | * 示例 1: 10 | * 11 | * nums1 = [1, 3] nums2 = [2] 12 | * 13 | * 中位数是 2.0 14 | * 15 | * 示例 2: 16 | * 17 | * nums1 = [1, 2] nums2 = [3, 4] 18 | * 19 | * 中位数是 (2 + 3)/2 = 2.5 20 | * 21 | * @author ffj 22 | * 23 | */ 24 | public class MedianOfTwoSortedArrays { 25 | 26 | public static void main(String[] args) { 27 | int[] A = { 1, 2 }; 28 | int[] B = { 3, 4 }; 29 | double result = findMedianSortedArrays(A, B); 30 | System.out.println(result); 31 | } 32 | 33 | public static double findMedianSortedArrays(int[] A, int[] B) { 34 | int m = A.length; 35 | int n = B.length; 36 | if (m > n) { // to ensure m<=n 使得A数组长度小于等于B数组长度 37 | int[] temp = A; 38 | A = B; 39 | B = temp; 40 | int tmp = m; 41 | m = n; 42 | n = tmp; 43 | } 44 | int iMin = 0, iMax = m, halfLen = (m + n + 1) / 2; // 取整 总数的一半 45 | while (iMin <= iMax) { 46 | int i = (iMin + iMax) / 2; 47 | int j = halfLen - i; 48 | // 确定 i j 的值,将A B都分割成两部分 49 | if (i < iMax && B[j - 1] > A[i]) { 50 | iMin = iMin + 1; // i is too small 51 | } else if (i > iMin && A[i - 1] > B[j]) { 52 | iMax = iMax - 1; // i is too big 53 | } else { // i is perfect 54 | int maxLeft = 0; 55 | if (i == 0) { 56 | maxLeft = B[j - 1]; 57 | } else if (j == 0) { 58 | maxLeft = A[i - 1]; 59 | } else { 60 | maxLeft = Math.max(A[i - 1], B[j - 1]); 61 | } 62 | if ((m + n) % 2 == 1) { // 个数是奇数的话 中位数就是中间这个数 63 | return maxLeft; 64 | } 65 | 66 | int minRight = 0; 67 | if (i == m) { 68 | minRight = B[j]; 69 | } else if (j == n) { 70 | minRight = A[i]; 71 | } else { 72 | minRight = Math.min(B[j], A[i]); 73 | } 74 | 75 | return (maxLeft + minRight) / 2.0; 76 | } 77 | } 78 | return 0.0; 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/hard/NQueensII.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.hard; 2 | 3 | /*******************N皇后 II************/ 4 | /** 5 | * n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。 6 | * 7 | * 给定一个整数 n,返回 n 皇后不同的解决方案的数量。 8 | * 9 | * 示例: 10 | * 11 | * 输入: 4 输出: 2 12 | * 13 | * 解释: 4 皇后问题存在如下两个不同的解法。 [  [".Q..", "...Q", "Q...", "..Q."], // 解法 1   14 | * 15 | *  ["..Q.", "Q...", "...Q", ".Q.."] ] // 解法 2   16 | * 17 | * @author ffj 18 | * 19 | */ 20 | public class NQueensII { 21 | 22 | int num; 23 | 24 | public int totalNQueens(int n) { 25 | char[][] board = new char[n][n]; 26 | for (int i = 0; i < n; i++) 27 | for (int j = 0; j < n; j++) 28 | board[i][j] = '.'; 29 | dfs(board, 0); 30 | return num; 31 | } 32 | 33 | private void dfs(char[][] board, int colIndex) { 34 | if (colIndex == board.length) { 35 | num++; 36 | return; 37 | } 38 | 39 | for (int i = 0; i < board.length; i++) { 40 | if (validate(board, i, colIndex)) { 41 | board[i][colIndex] = 'Q'; 42 | dfs(board, colIndex + 1); 43 | board[i][colIndex] = '.'; 44 | } 45 | } 46 | } 47 | 48 | /** 49 | * 验证是否满足条件 50 | * 51 | * @param board 52 | * @param x 53 | * @param y 54 | * @return 55 | */ 56 | private boolean validate(char[][] board, int x, int y) { 57 | for (int i = 0; i < board.length; i++) { 58 | for (int j = 0; j < y; j++) { 59 | if (board[i][j] == 'Q' && (x + j == y + i || x + y == i + j || x == i)) 60 | return false; 61 | } 62 | } 63 | return true; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/hard/PalindromePartitioningII.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.hard; 2 | 3 | /****************分割回文串 II***************/ 4 | /** 5 | * 6 | * 给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。 7 | * 8 | * 返回符合要求的最少分割次数。 9 | * 10 | * 示例: 11 | * 12 | * 输入: "aab" 输出: 1 13 | * 14 | * 解释: 进行一次分割就可将 s 分割成 ["aa","b"] 这样两个回文子串。 15 | * 16 | * @author ffj 17 | * 18 | */ 19 | public class PalindromePartitioningII { 20 | 21 | public static void main(String[] args) { 22 | System.out.println(new PalindromePartitioningII().minCut("ababa")); 23 | } 24 | 25 | /** 26 | * DP 动态规划 27 | * 28 | * @param s 29 | * @return 30 | */ 31 | public int minCut(String s) { 32 | 33 | char[] c = s.toCharArray(); 34 | int n = c.length; 35 | int[] cut = new int[n]; 36 | boolean[][] pal = new boolean[n][n]; 37 | 38 | for (int i = 0; i < n; i++) { 39 | int min = i; 40 | for (int j = 0; j <= i; j++) { 41 | if (c[j] == c[i] && (j + 1 > i - 1 || pal[j + 1][i - 1])) { 42 | pal[j][i] = true; 43 | min = j == 0 ? 0 : Math.min(min, cut[j - 1] + 1); 44 | } 45 | } 46 | cut[i] = min; 47 | } 48 | return cut[n - 1]; 49 | 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/hard/PostorderTraversal.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.hard; 2 | 3 | import java.util.LinkedList; 4 | import java.util.List; 5 | import java.util.Stack; 6 | 7 | /*********二叉树的后序遍历*********************/ 8 | /** 9 | * 给定一个二叉树,返回它的 后序 遍历。 10 | * 11 | * 示例: 12 | * 13 | * 输入: [1,null,2,3] 1 \ 2 / 3 14 | * 15 | * 输出: [3,2,1] 16 | * 17 | * @author ffj 18 | * 19 | */ 20 | public class PostorderTraversal { 21 | 22 | /** 23 | * 迭代 24 | * 25 | * @param root 26 | * @return 27 | */ 28 | public List postorderTraversal(TreeNode root) { 29 | LinkedList ans = new LinkedList<>(); 30 | Stack stack = new Stack<>(); 31 | if (root == null) 32 | return ans; 33 | 34 | stack.push(root); 35 | while (!stack.isEmpty()) { 36 | TreeNode cur = stack.pop(); 37 | ans.addFirst(cur.val); 38 | if (cur.left != null) { 39 | stack.push(cur.left); 40 | } 41 | if (cur.right != null) { 42 | stack.push(cur.right); 43 | } 44 | } 45 | return ans; 46 | } 47 | 48 | /** 49 | * 递归 50 | * 51 | * @param root 52 | * @return 53 | */ 54 | public List postorderTraversal1(TreeNode root) { 55 | List res = new LinkedList<>(); 56 | helper(res, root); 57 | return res; 58 | } 59 | 60 | public void helper(List list, TreeNode root) { 61 | if (root == null) 62 | return; 63 | helper(list, root.left); 64 | helper(list, root.right); 65 | list.add(root.val); 66 | } 67 | 68 | public class TreeNode { 69 | int val; 70 | TreeNode left; 71 | TreeNode right; 72 | 73 | TreeNode(int x) { 74 | val = x; 75 | } 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/hard/PreimageSizeOfFactorialZeroesFunction.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.hard; 2 | 3 | /*********************阶乘函数后K个零**************/ 4 | /** 5 | * f(x) 是 x! 末尾是0的数量。(回想一下 x! = 1 * 2 * 3 * ... * x,且0! = 1) 6 | * 7 | * 例如, f(3) = 0 ,因为3! = 6的末尾没有0;而 f(11) = 2 ,因为11!= 39916800末端有2个0。给定 8 | * K,找出多少个非负整数x ,有 f(x) = K 的性质。 9 | * 10 | * 示例 1: 11 | * 12 | * 输入:K = 0 输出:5 13 | * 14 | * 解释: 0!, 1!, 2!, 3!, and 4! 均符合 K = 0 的条件。 15 | * 16 | * 示例 2: 17 | * 18 | * 输入:K = 5 输出:0 19 | * 20 | * 解释:没有匹配到这样的 x!,符合K = 5 的条件。 21 | * 22 | * 注意: 23 | * 24 | * K是范围在 [0, 10^9] 的整数。 25 | * 26 | * @author ffj 27 | * 28 | */ 29 | public class PreimageSizeOfFactorialZeroesFunction { 30 | 31 | /** 32 | * https://www.cnblogs.com/hutonm/p/5624996.html 33 | * 34 | * 因子“5”的个数相关 35 | * 36 | * @param K 37 | * @return 38 | */ 39 | public int preimageSizeFZF(int K) { 40 | // 因为K*5可能会超出int的范围,所以强转为long类型 41 | return binarySearch((long) K / 5, (long) K * 5, (long) K) ? 5 : 0; 42 | } 43 | 44 | private boolean binarySearch(long l, long r, long K) { 45 | while (l <= r) { // 因为l==r的时候也需要搜索 46 | long mid = l + (r - l) / 2; // (l + r) / 2 的方式(l + r) 两个long类型相加可能会超过long类型的范围 47 | long numOfZeros = getNumberofZeros(mid); 48 | if (numOfZeros == K) 49 | return true; 50 | else if (numOfZeros > K) 51 | r = mid - 1; 52 | else 53 | l = mid + 1; 54 | } 55 | return false; 56 | } 57 | 58 | /** 59 | * 获取该数阶层末尾“0”的个数 60 | * 61 | * 令f(x)表示正整数x末尾所含有的“0”的个数,则有: 62 | * 63 | * 当0 < n < 5时,f(n!) = 0; 64 | * 65 | * 当n >= 5时,f(n!) = k + f(k!), 其中 k = n / 5(取整)。 66 | * 67 | * @param num 68 | * @return 69 | */ 70 | private long getNumberofZeros(long num) { 71 | if (num < 5) 72 | return 0; 73 | return (num / 5 + getNumberofZeros(num / 5)); 74 | } 75 | 76 | public static void main(String[] args) { 77 | int K = 5; 78 | System.out.println(new PreimageSizeOfFactorialZeroesFunction().preimageSizeFZF(K)); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/hard/RandomPickWithBlacklist.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.hard; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.Random; 6 | 7 | /*********************黑名单中的随机数******************/ 8 | /** 9 | * 给定一个包含 [0,n ) 中独特的整数的黑名单 B,写一个函数从 [ 0,n ) 中返回一个不在 B 中的随机整数。 10 | * 11 | * 对它进行优化使其尽量少调用系统方法 Math.random() 。 12 | * 13 | * 提示: 14 | * 15 | * 1 <= N <= 1000000000 0 <= B.length < min(100000, N) [0, N) 不包含 N,详细参见 16 | * interval notation 。 17 | * 18 | * 示例 1: 19 | * 20 | * 输入: ["Solution","pick","pick","pick"] [[1,[]],[],[],[]] 输出: [null,0,0,0] 21 | * 22 | * 示例 2: 23 | * 24 | * 输入: ["Solution","pick","pick","pick"] [[2,[]],[],[],[]] 输出: [null,1,1,1] 25 | * 26 | * 示例 3: 27 | * 28 | * 输入: ["Solution","pick","pick","pick"] [[3,[1]],[],[],[]] Output: [null,0,0,2] 29 | * 30 | * 示例 4: 31 | * 32 | * 输入: ["Solution","pick","pick","pick"] [[4,[2]],[],[],[]] 输出: [null,1,3,1] 33 | * 34 | * 输入语法说明: 35 | * 36 | * 输入是两个列表:调用成员函数名和调用的参数。Solution的构造函数有两个参数,N 和黑名单 B。pick 37 | * 没有参数,输入参数是一个列表,即使参数为空,也会输入一个 [] 空列表。 38 | * 39 | * @author ffj 40 | * 41 | */ 42 | public class RandomPickWithBlacklist { 43 | int M; 44 | Random r = new Random(); 45 | Map map = new HashMap<>(); // 定义一个map来存放黑名单中的值 46 | 47 | /** 48 | * 图解:https://s3-lc-upload.s3.amazonaws.com/users/cafebaby/image_1530657902.png 49 | * 50 | * @param N 51 | * @param blacklist 52 | */ 53 | public RandomPickWithBlacklist(int N, int[] blacklist) { 54 | 55 | for (int i : blacklist) { 56 | map.put(i, -1); 57 | } 58 | M = N - map.size(); 59 | 60 | for (int i : blacklist) { 61 | if (i < M) { // 界限值以下有黑名单中的值 62 | while (map.containsKey(N - 1)) // 将黑名单中的value值替换为白名单中的值 63 | N--; 64 | map.put(i, --N); 65 | 66 | } 67 | } 68 | } 69 | 70 | public int pick() { 71 | int p = r.nextInt(M); // 产生一个随机数 72 | if (map.containsKey(p)) // 若是这个随机数在黑名单中 返回其不在黑名单中的值 73 | return map.get(p); 74 | return p; 75 | 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/hard/SlidingWindowMaximum.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.hard; 2 | 3 | import java.util.ArrayDeque; 4 | import java.util.Arrays; 5 | import java.util.Deque; 6 | 7 | /*******************滑动窗口最大值*****************/ 8 | /** 9 | * 给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口 k 内的数字。滑动窗口每次只向右移动一位。 10 | * 11 | * 返回滑动窗口最大值。 12 | * 13 | * 示例: 14 | * 15 | * 输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3 输出: [3,3,5,5,6,7] 16 | * 17 | * 解释: 18 | * 19 | * ---滑动窗口的位置------最大值 20 | * 21 | * [1 3 -1] -3 5 3 6 7 3 22 | * 23 | * 1 [3 -1 -3] 5 3 6 7 3 24 | * 25 | * 1 3 [-1 -3 5] 3 6 7 5 26 | * 27 | * 1 3 -1 [-3 5 3] 6 7 5 28 | * 29 | * 1 3 -1 -3 [5 3 6] 7 6 30 | * 31 | * 1 3 -1 -3 5 [3 6 7] 7 32 | * 33 | * 注意: 34 | * 35 | * 你可以假设 k 总是有效的,1 ≤ k ≤ 输入数组的大小,且输入数组不为空。 36 | * 37 | * 进阶: 38 | * 39 | * 你能在线性时间复杂度内解决此题吗? 40 | * 41 | * @author ffj 42 | * 43 | */ 44 | public class SlidingWindowMaximum { 45 | 46 | public static void main(String[] args) { 47 | int[] nums = { 1, 3 }; 48 | int k = 2; 49 | System.out.println(Arrays.toString(maxSlidingWindow(nums, k))); 50 | } 51 | 52 | public static int[] maxSlidingWindow(int[] nums, int k) { 53 | 54 | if (nums == null || k <= 0) { 55 | return new int[0]; 56 | } 57 | int n = nums.length; 58 | int[] r = new int[n - k + 1]; 59 | int ri = 0; 60 | // store index 61 | Deque q = new ArrayDeque<>(); // 双端队列 62 | for (int i = 0; i < n; i++) { 63 | // remove numbers out of range k 64 | while (!q.isEmpty() && q.peek() < i - k + 1) { // 超出个数的从头开始删 65 | q.poll(); 66 | } 67 | // remove smaller numbers in k range as they are useless 68 | while (!q.isEmpty() && nums[q.peekLast()] < nums[i]) { // 比当前值小的都不要 69 | q.pollLast(); 70 | } 71 | // q contains index... r contains content 72 | q.offer(i); 73 | if (i >= k - 1) { // 当满足个数时才添加数据 74 | r[ri++] = nums[q.peek()]; 75 | } 76 | } 77 | return r; 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/hard/TrappingRainWater.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.hard; 2 | 3 | import java.util.Stack; 4 | 5 | /*************接雨水***********/ 6 | /** 7 | * 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 8 | * 9 | * 10 | * 11 | * 上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 感谢 12 | * Marcos 贡献此图。 13 | * 14 | * 示例: 15 | * 16 | * 输入: [0,1,0,2,1,0,1,3,2,1,2,1] 输出: 6 17 | * 18 | * @author ffj 19 | * 20 | */ 21 | public class TrappingRainWater { 22 | 23 | public static void main(String[] args) { 24 | int[] height = { 0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1 }; 25 | int result = new TrappingRainWater().trap(height); 26 | System.out.println("result :" + result); 27 | } 28 | 29 | public int trap(int[] height) { 30 | 31 | // 无法积水 32 | if (height == null || height.length < 2) 33 | return 0; 34 | 35 | Stack stack = new Stack<>(); 36 | int water = 0, i = 0; 37 | while (i < height.length) { 38 | // 栈为空或是下个值小于当前值 推入栈中 39 | if (stack.isEmpty() || height[i] <= height[stack.peek()]) { 40 | stack.push(i++); 41 | } else { 42 | int pre = stack.pop(); 43 | if (!stack.isEmpty()) { // 计算此区域中积水的面积 44 | // find the smaller height between the two sides 45 | int minHeight = Math.min(height[stack.peek()], height[i]); 46 | // calculate the area 作累加 47 | water += (minHeight - height[pre]) * (i - stack.peek() - 1); 48 | } 49 | } 50 | } 51 | return water; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/AddTwoNumbers.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /**********两数相加********https://leetcode-cn.com/articles/add-two-numbers/*****/ 4 | /** 5 | * 给定两个非空链表来表示两个非负整数。位数按照逆序方式存储,它们的每个节点只存储单个数字。将两数相加返回一个新的链表。 6 | * 7 | * 你可以假设除了数字 0 之外,这两个数字都不会以零开头。 8 | * 9 | * 示例: 10 | * 11 | * 输入:(2 -> 4 -> 3) + (5 -> 6 -> 4) 输出:7 -> 0 -> 8 原因:342 + 465 = 807 12 | * 13 | * @author ffj 14 | * 15 | */ 16 | public class AddTwoNumbers { 17 | 18 | public static void main(String[] args) { 19 | ListNode l1 = new ListNode(2); 20 | l1.next = new ListNode(4); 21 | l1.next.next = new ListNode(3); 22 | 23 | ListNode l2 = new ListNode(5); 24 | l2.next = new ListNode(6); 25 | l2.next.next = new ListNode(4); 26 | ListNode result = addTwoNumbers(l1, l2); 27 | System.out.println(result.val + "->" + result.next.val + "->" + result.next.next.val); 28 | } 29 | 30 | public static ListNode addTwoNumbers(ListNode l1, ListNode l2) { 31 | 32 | ListNode dummyHead = new ListNode(0); 33 | ListNode p = l1, q = l2, curr = dummyHead; 34 | int carry = 0; 35 | while (p != null || q != null) { 36 | int x = (p != null) ? p.val : 0; 37 | int y = (q != null) ? q.val : 0; 38 | int sum = carry + x + y; 39 | // 取整 40 | carry = sum / 10; 41 | // 取余作为该值 42 | curr.next = new ListNode(sum % 10); 43 | // 游标后移 44 | curr = curr.next; 45 | if (p != null) // p q 还有值就后移 46 | p = p.next; 47 | if (q != null) 48 | q = q.next; 49 | } 50 | if (carry > 0) { 51 | // 进位 52 | curr.next = new ListNode(carry); 53 | } 54 | return dummyHead.next; 55 | } 56 | 57 | public static class ListNode { 58 | int val; 59 | ListNode next; 60 | 61 | ListNode(int x) { 62 | val = x; 63 | } 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/BeautifulArray.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.Arrays; 4 | 5 | /***************漂亮数组*************/ 6 | /** 7 | * 对于某些固定的 N,如果数组 A 是整数 1, 2, ..., N 组成的排列,使得: 8 | * 9 | * 对于每个 i < j,都不存在 k 满足 i < k < j 使得 A[k] * 2 = A[i] + A[j]。 10 | * 11 | * 那么数组 A 是漂亮数组。 12 | * 13 | * 给定 N,返回任意漂亮数组 A(保证存在一个)。 14 | * 15 | * 示例 1: 16 | * 17 | * 输入:4 18 | * 19 | * 输出:[2,1,4,3] 20 | * 21 | * 示例 2: 22 | * 23 | * 输入:5 24 | * 25 | * 输出:[3,1,2,5,4]   26 | * 27 | * 提示: 28 | * 29 | * 1 <= N <= 1000 30 | * 31 | * @author ffj 32 | * 33 | */ 34 | public class BeautifulArray { 35 | 36 | public static void main(String[] args) { 37 | int N = 5; 38 | int[] result = new BeautifulArray().beautifulArray(N); 39 | System.out.println(Arrays.toString(result)); 40 | } 41 | 42 | /** 43 | * 漂亮数组有以下的性质: 44 | * 45 | * (1)A是一个漂亮数组,如果对A中所有元素添加一个常数,那么A还是一个漂亮数组。 46 | * 47 | * (2)A是一个漂亮数组,如果对A中所有元素乘以一个常数,那么A还是一个漂亮数组。 48 | * 49 | * (3)A是一个漂亮数组,如果删除一些A中所有元素,那么A还是一个漂亮数组。 50 | * 51 | * (4) A是一个奇数构成的漂亮数组,B是一个偶数构成的漂亮数组,那么A+B也是一个漂亮数组 52 | * 比如:{1,5,3,7}+{2,6,4,8}={1,5,3,7,2,6,4,8}也是一个漂亮数组。 53 | * 54 | * 所以我们假设一个{1-m}的数组是漂亮数组,可以通过下面的方式构造漂亮数组{1-2m}: 55 | * 56 | * 1.对{1-m}中所有的数乘以2-1,构成一个奇数漂亮数组A。如{1,3,2,4},可以得到{1,5,3,7} 57 | * 2.对{1-m}中所有的数乘以2,构成一个偶数漂亮数组B,如{1,3,2,4}, 可以得到{2,6,4,8} 58 | * 3.A+B构成了{1-2m}的漂亮数组。{1,5,3,7}+{2,6,4,8}={1,5,3,7,2,6,4,8} 59 | * 60 | * 4.从中删除不要的数字即可。 61 | * 62 | * 63 | * @param N 64 | * @return 65 | */ 66 | public int[] beautifulArray(int N) { 67 | int[] nums = new int[N]; 68 | nums[0] = 1; 69 | if (N == 1) 70 | return nums; 71 | int m = N - 1; 72 | int k = 1; 73 | while (m != 1) { 74 | m >>= 1; 75 | k <<= 1; 76 | } 77 | int i = 1, t = 1, j; 78 | while (i < N) { 79 | for (j = 0; j < t; j++) { 80 | if (nums[j] + k <= N) { 81 | nums[i] = nums[j] + k; 82 | i++; 83 | } 84 | } 85 | t = i; 86 | k >>= 1; 87 | } 88 | return nums; 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/BestTimeToBuyAndSellStockWithTransactionFee.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /*************买卖股票的最佳时机含手续费********https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/solution/*******************/ 4 | /** 5 | * 给定一个整数数组 prices,其中第 i 个元素代表了第 i 天的股票价格 ;非负整数 fee 代表了交易股票的手续费用。 6 | * 7 | * 你可以无限次地完成交易,但是你每次交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。 8 | * 9 | * 返回获得利润的最大值。 10 | * 11 | * 示例 1: 12 | * 13 | * 输入: prices = [1, 3, 2, 8, 4, 9], fee = 2 14 | * 15 | * 输出: 8 16 | * 17 | * 解释: 能够达到的最大利润: 在此处买入 prices[0] = 1 18 | * 19 | * 在此处卖出 prices[3] = 8 20 | * 21 | * 在此处买入 prices[4] = 4 22 | * 23 | * 在此处卖出 prices[5] = 9 24 | * 25 | * 总利润: ((8 - 1) - 2) + ((9 - 4) - 2) = 8. 26 | * 27 | * 注意: 28 | * 29 | * 0 < prices.length <= 50000 / 0 < prices[i] < 50000 / 0 <= fee < 50000. 30 | * 31 | * @author ffj 32 | * 33 | */ 34 | public class BestTimeToBuyAndSellStockWithTransactionFee { 35 | 36 | public static void main(String[] args) { 37 | int[] prices = { 1, 3, 2, 8, 4, 9 }; 38 | int fee = 2; 39 | int result = maxProfit(prices, fee); 40 | System.out.println(result); 41 | 42 | } 43 | 44 | public static int maxProfit(int[] prices, int fee) { 45 | 46 | if (prices == null || prices.length == 0) { 47 | return 0; 48 | } 49 | // cash: 手头的现金,即总的赚的金额,同时也是未持股时的现金额 50 | // hold: 手中有持股时的现金,即总金额减去手中股票的买入价 51 | int cash = 0, hold = -prices[0]; 52 | for (int i = 1; i < prices.length; i++) { 53 | cash = Math.max(cash, hold + prices[i] - fee); // 若卖出比原本买股票之前赚 就卖 54 | hold = Math.max(hold, cash - prices[i]); // 买入后剩余的钱比之前持股时钱还要多 就买 55 | } 56 | return cash; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/BinaryTreeInorderTraversal.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Stack; 6 | 7 | /*******************二叉树的中序遍历****************/ 8 | /** 9 | * 给定一个二叉树,返回它的中序遍历。 10 | * 11 | * 示例: 12 | * 13 | * 输入: [1,null,2,3] 14 | * 15 | * 1 16 | * 17 | * \ 18 | * 19 | * 2 20 | * 21 | * / 22 | * 23 | * 3 24 | * 25 | * 输出: [1,3,2] 26 | * 27 | * 进阶: 递归算法很简单,你可以通过迭代算法完成吗? 28 | * 29 | * @author ffj 30 | * 31 | */ 32 | public class BinaryTreeInorderTraversal { 33 | 34 | public class TreeNode { 35 | int val; 36 | TreeNode left; 37 | TreeNode right; 38 | 39 | TreeNode(int x) { 40 | val = x; 41 | } 42 | } 43 | 44 | /** 45 | * 递归 46 | * 47 | * @param root 48 | * @return 49 | */ 50 | public List inorderTraversal(TreeNode root) { 51 | List list = new ArrayList<>(); 52 | return helper(list, root); 53 | } 54 | 55 | private List helper(List list, TreeNode node) { 56 | if (node != null) { 57 | helper(list, node.left); 58 | list.add(node.val); 59 | helper(list, node.right); 60 | } 61 | return list; 62 | } 63 | 64 | /** 65 | * 栈 stack 来解决 66 | * 67 | * @param root 68 | * @return 69 | */ 70 | public List inorderTraversal1(TreeNode root) { 71 | List res = new ArrayList<>(); 72 | Stack stack = new Stack<>(); 73 | TreeNode curr = root; 74 | while (curr != null || !stack.isEmpty()) { 75 | while (curr != null) { 76 | // 将其左节点依次放入 因为先读左节点 77 | stack.push(curr); 78 | curr = curr.left; 79 | } 80 | // 将栈顶弹出 81 | curr = stack.pop(); 82 | // 塞值 83 | res.add(curr.val); 84 | // 将右节点赋值给它 完美呈现了中序遍历 : 左 -> 根 -> 右 85 | curr = curr.right; 86 | } 87 | return res; 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/BinaryTreePreorderTraversal.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.ArrayList; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | 7 | /***************二叉树的前序遍历*************/ 8 | /** 9 | * 给定一个二叉树,返回它的 前序 遍历。 10 | * 11 | * 示例: 12 | * 13 | * 输入: [1,null,2,3] 14 | * 15 | * 1 16 | * 17 | * \ 18 | * 19 | * 2 20 | * 21 | * / 22 | * 23 | * 3 24 | * 25 | * 输出: [1,2,3] 26 | * 27 | * 进阶: 递归算法很简单,你可以通过迭代算法完成吗? 28 | * 29 | * @author ffj 30 | * 31 | */ 32 | public class BinaryTreePreorderTraversal { 33 | 34 | public class TreeNode { 35 | int val; 36 | TreeNode left; 37 | TreeNode right; 38 | 39 | TreeNode(int x) { 40 | val = x; 41 | } 42 | } 43 | 44 | /** 45 | * 递归 46 | * 47 | * @param root 48 | * @return 49 | */ 50 | public List preorderTraversal(TreeNode root) { 51 | List list = new ArrayList<>(); 52 | helper(list, root); 53 | return list; 54 | } 55 | 56 | private void helper(List list, TreeNode node) { 57 | if (node != null) { 58 | list.add(node.val); 59 | if (node.left != null) 60 | helper(list, node.left); 61 | if (node.right != null) 62 | helper(list, node.right); 63 | } 64 | } 65 | 66 | /** 67 | * 双向队列 68 | * 69 | * @param root 70 | * @return 71 | */ 72 | public List preorderTraversal1(TreeNode root) { 73 | LinkedList stack = new LinkedList<>(); 74 | LinkedList output = new LinkedList<>(); 75 | if (root == null) { 76 | return output; 77 | } 78 | stack.add(root); 79 | while (!stack.isEmpty()) { 80 | // 弹出队列中最后一个 81 | TreeNode node = stack.pollLast(); 82 | output.add(node.val); 83 | if (node.right != null) { 84 | stack.add(node.right); 85 | } 86 | if (node.left != null) { 87 | // 该节点就是下一个要读的根节点 88 | stack.add(node.left); 89 | } 90 | } 91 | return output; 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/BulbSwitcher.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /*******************灯泡开关******************/ 4 | /** 5 | * 初始时有 n 个灯泡关闭。 第 1 轮,你打开所有的灯泡。 第 2 轮,每两个灯泡你关闭一次。 第 3 6 | * 轮,每三个灯泡切换一次开关(如果关闭则开启,如果开启则关闭)。第 i 轮,每 i 个灯泡切换一次开关。 对于第 n 轮,你只切换最后一个灯泡的开关。 找出 7 | * n 轮后有多少个亮着的灯泡。 8 | * 9 | * 示例: 10 | * 11 | * 输入: 3 输出: 1 12 | * 13 | * 解释: 14 | * 15 | * 初始时, 灯泡状态 [关闭, 关闭, 关闭]. 16 | * 17 | * 第一轮后, 灯泡状态 [开启, 开启, 开启]. 18 | * 19 | * 第二轮后, 灯泡状态 [开启, 关闭, 开启]. 20 | * 21 | * 第三轮后, 灯泡状态 [开启, 关闭, 关闭]. 22 | * 23 | * 你应该返回 1,因为只有一个灯泡还亮着。 24 | * 25 | * @author ffj 26 | * 27 | */ 28 | public class BulbSwitcher { 29 | 30 | public static void main(String[] args) { 31 | System.out.println(new BulbSwitcher().bulbSwitch(5)); 32 | } 33 | 34 | /** 35 | * 详细解析 36 | * :https://leetcode.com/problems/bulb-switcher/discuss/77112/Share-my-o(1)-solution-with-explanation 37 | * 38 | * @param n 39 | * @return 40 | */ 41 | public int bulbSwitch(int n) { 42 | return (int) Math.sqrt(n); // 算术平方根 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/CarFleet.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.Arrays; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | /**************车队************/ 8 | /** 9 | * N 辆车沿着一条车道驶向位于 target 英里之外的共同目的地。 10 | * 11 | * 每辆车 i 以恒定的速度 speed[i] (英里/小时),从初始位置 position[i] (英里) 沿车道驶向目的地。 12 | * 13 | * 一辆车永远不会超过前面的另一辆车,但它可以追上去,并与前车以相同的速度紧接着行驶。 14 | * 15 | * 此时,我们会忽略这两辆车之间的距离,也就是说,它们被假定处于相同的位置。 16 | * 17 | * 车队 是一些由行驶在相同位置、具有相同速度的车组成的非空集合。注意,一辆车也可以是一个车队。 18 | * 19 | * 即便一辆车在目的地才赶上了一个车队,它们仍然会被视作是同一个车队。 20 | * 21 | * 会有多少车队到达目的地? 22 | * 23 | * 示例: 24 | * 25 | * 输入:target = 12, position = [10,8,0,5,3], speed = [2,4,1,1,3] 输出:3 26 | * 27 | * 解释: 28 | * 29 | * 从 10 和 8 开始的车会组成一个车队,它们在 12 处相遇。 30 | * 31 | * 从 0 处开始的车无法追上其它车,所以它自己就是一个车队。 32 | * 33 | * 从 5 和 3 开始的车会组成一个车队,它们在 6 处相遇。 34 | * 35 | * 请注意,在到达目的地之前没有其它车会遇到这些车队,所以答案是 3。 36 | * 37 | * 提示: 38 | * 39 | * 0 <= N <= 10 ^ 4 40 | * 41 | * 0 < target <= 10 ^ 6 42 | * 43 | * 0 < speed[i] <= 10 ^ 6 44 | * 45 | * 0 <= position[i] < target 46 | * 47 | * 所有车的初始位置各不相同。 48 | * 49 | * @author ffj 50 | * 51 | */ 52 | public class CarFleet { 53 | 54 | public static void main(String[] args) { 55 | 56 | } 57 | 58 | public int carFleet(int target, int[] position, int[] speed) { 59 | int number = 0; 60 | int p = position.length - 2;// 定义两个指针p、q 61 | int q = position.length - 1; 62 | Map map = new HashMap<>(); 63 | if (position.length == 0)// 数组为空,返回0 64 | return 0; 65 | for (int i = 0; i < position.length; i++) { // 将position和speed数组放入到Map中 66 | map.put(position[i], speed[i]); 67 | } 68 | Arrays.sort(position); 69 | while (p >= 0) { 70 | double pt = (target - position[p]) * 1.0 / map.get(position[p]);// p走到终点所需时间 71 | double qt = (target - position[q]) * 1.0 / map.get(position[q]);// q走到终点所需时间 72 | if (pt <= qt) { // 同一车队 73 | p--; 74 | } else { // 不同车队 指针移动 75 | number++; 76 | q = p--; 77 | } 78 | } 79 | return number + 1; 80 | 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/CoinChange2.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /**************零钱兑换 II**********/ 4 | /** 5 | * 给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。 6 | * 7 | * 示例 1: 8 | * 9 | * 输入: amount = 5, coins = [1, 2, 5] 输出: 4 10 | * 11 | * 解释: 有四种方式可以凑成总金额: 12 | * 13 | * 5=5 14 | * 15 | * 5=2+2+1 16 | * 17 | * 5=2+1+1+1 18 | * 19 | * 5=1+1+1+1+1 20 | * 21 | * 示例 2: 22 | * 23 | * 输入: amount = 3, coins = [2] 输出: 0 24 | * 25 | * 解释: 只用面额2的硬币不能凑成总金额3。 26 | * 27 | * 示例 3: 28 | * 29 | * 输入: amount = 10, coins = [10] 输出: 1 30 | * 31 | * 注意: 32 | * 33 | * 你可以假设: 34 | * 35 | * 0 <= amount (总金额) <= 5000 1 <= coin (硬币面额) <= 5000 硬币种类不超过 500 种 结果符合 32 36 | * 位符号整数 37 | * 38 | * @author ffj 39 | * 40 | */ 41 | public class CoinChange2 { 42 | 43 | public static void main(String[] args) { 44 | int amount = 5; 45 | int[] coins = { 1, 2, 3 }; 46 | int result = new CoinChange2().change(amount, coins); 47 | System.out.println("result: " + result); 48 | } 49 | 50 | public int change(int amount, int[] coins) { 51 | int[] dp = new int[amount + 1]; 52 | dp[0] = 1; 53 | for (int coin : coins) { 54 | for (int i = coin; i <= amount; i++) 55 | dp[i] += dp[i - coin]; 56 | } 57 | return dp[amount]; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/CombinationSum.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | 7 | /********************组合总和****************/ 8 | /** 9 | * 给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。 10 | * 11 | * candidates 中的数字可以无限制重复被选取。 12 | * 13 | * 说明: 14 | * 15 | * 所有数字(包括 target)都是正整数。 16 | * 17 | * 解集不能包含重复的组合。 18 | * 19 | * 示例 1: 20 | * 21 | * 输入: candidates = [2,3,6,7], target = 7, 22 | * 23 | * 所求解集为: 24 | * 25 | * [ [7], [2,2,3] ] 26 | * 27 | * 示例 2: 28 | * 29 | * 输入: candidates = [2,3,5], target = 8, 30 | * 31 | * 所求解集为: 32 | * 33 | * [ [2,2,2,2], [2,3,3], [3,5] ] 34 | * 35 | * @author ffj 36 | * 37 | */ 38 | public class CombinationSum { 39 | 40 | public List> combinationSum(int[] candidates, int target) { 41 | List> list = new ArrayList<>(); 42 | Arrays.sort(candidates); 43 | recursive(list, new ArrayList<>(), candidates, target, 0); 44 | return list; 45 | } 46 | 47 | /** 48 | * 49 | * @param list 50 | * 总的输出 list 51 | * 52 | * @param tempList 53 | * 存放的 list 54 | * @param nums 55 | * 数组 56 | * 57 | * @param remain 58 | * 剩余值 59 | * @param index 60 | * 数组下标 61 | */ 62 | private void recursive(List> list, List tempList, int[] nums, int remain, int index) { 63 | 64 | if (remain < 0) // return 或者进行 add 操作后就开始执行弹出尾部元素 塞入下个元素 65 | return; 66 | else if (remain == 0) 67 | list.add(new ArrayList<>(tempList)); // 这里需要注意不能直接 list.add(tempList),最终 tempList 所指向的对象是空的, 68 | // 所以需要 new 一个新对象,将值复制进去 69 | else { 70 | for (int i = index; i < nums.length; i++) { 71 | tempList.add(nums[i]); // 挨个塞入 72 | recursive(list, tempList, nums, remain - nums[i], i); // 由于元素可重复 所以是 i 73 | tempList.remove(tempList.size() - 1); // 挨个弹出 74 | } 75 | } 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/CombinationSumII.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | 7 | /*********************组合总和 II*********************/ 8 | /** 9 | * 给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。 10 | * 11 | * candidates 中的每个数字在每个组合中只能使用一次。 12 | * 13 | * 说明: 14 | * 15 | * 所有数字(包括目标数)都是正整数。 16 | * 17 | * 解集不能包含重复的组合。 18 | * 19 | * 示例 1: 20 | * 21 | * 输入: candidates = [10,1,2,7,6,1,5], target = 8, 22 | * 23 | * 所求解集为: 24 | * 25 | * [ [1, 7], [1, 2, 5], [2, 6], [1, 1, 6] ] 26 | * 27 | * 示例 2: 28 | * 29 | * 输入: candidates = [2,5,2,1,2], target = 5, 30 | * 31 | * 所求解集为: 32 | * 33 | * [ [1,2,2], [5] ] 34 | * 35 | * @author ffj 36 | * 37 | */ 38 | public class CombinationSumII { 39 | 40 | public List> combinationSum2(int[] candidates, int target) { 41 | List> list = new ArrayList<>(); 42 | Arrays.sort(candidates); 43 | recursive(list, new ArrayList<>(), candidates, target, 0); 44 | return list; 45 | 46 | } 47 | 48 | /** 49 | * DFS 添加每个满足条件的集合 50 | * 51 | * @param list 52 | * 最终返回集合 53 | * @param tempList 54 | * 每个满足条件的子集合 55 | * @param candidates 56 | * 数组 57 | * @param remain 58 | * 剩余值 59 | * @param index 60 | * 数组下标 61 | */ 62 | private void recursive(List> list, List tempList, int[] candidates, int remain, int index) { 63 | if (remain < 0) 64 | return; 65 | else if (remain == 0) 66 | list.add(new ArrayList<>(tempList)); 67 | else { 68 | for (int i = index; i < candidates.length; i++) { 69 | if (i > index && candidates[i] == candidates[i - 1]) // 说明两个值相等且之前一个值已经返回 70 | continue; 71 | tempList.add(candidates[i]); 72 | recursive(list, tempList, candidates, remain - candidates[i], i + 1); // 规定数组中每个数字在每个组合中只能使用一次 73 | tempList.remove(tempList.size() - 1); 74 | } 75 | } 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/CombinationSumIII.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /*****************组合总和 III****************/ 7 | /** 8 | * 找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。 9 | * 10 | * 说明: 11 | * 12 | * 所有数字都是正整数。 13 | * 14 | * 解集不能包含重复的组合。 15 | * 16 | * 示例 1: 17 | * 18 | * 输入: k = 3, n = 7 输出: [[1,2,4]] 19 | * 20 | * 示例 2: 21 | * 22 | * 输入: k = 3, n = 9 输出: [[1,2,6], [1,3,5], [2,3,4]] 23 | * 24 | * @author ffj 25 | * 26 | */ 27 | public class CombinationSumIII { 28 | 29 | public List> combinationSum3(int k, int n) { 30 | int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 31 | List> list = new ArrayList<>(); 32 | dfs(list, new ArrayList<>(), nums, k, n, 0); 33 | return list; 34 | 35 | } 36 | 37 | /** 38 | * 39 | * @param list 40 | * 最终返回的 list 41 | * @param tempList 42 | * 作为寻求满足条件的暂存 list 43 | * @param nums 44 | * 数组 45 | * @param reNum 46 | * 剩余的元素个数 47 | * @param reSum 48 | * 剩余的总和 49 | * @param index 50 | * 数组下标 51 | */ 52 | private void dfs(List> list, List tempList, int[] nums, int reNum, int reSum, int index) { 53 | if (reNum < 0 || reSum < 0) // 两者有其一小于 0 就是不满足条件 54 | return; 55 | if (reNum == 0 && reSum == 0) // 两者都为 0 时满足条件,塞入 56 | list.add(new ArrayList<>(tempList)); 57 | else if (reNum == 0 || reSum == 0) // 两者中有且只有一者等于 0 就不满足条件 58 | return; 59 | else { 60 | for (int i = index; i < nums.length; i++) { 61 | tempList.add(nums[i]); 62 | dfs(list, tempList, nums, reNum - 1, reSum - nums[i], i + 1); // 不能重复元素 所以 i + 1 63 | tempList.remove(tempList.size() - 1); 64 | } 65 | } 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/Combinations.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /***********组合*********/ 7 | /** 8 | * 给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。 9 | * 10 | * 示例: 11 | * 12 | * 输入: n = 4, k = 2 13 | * 14 | * 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ] 15 | * 16 | * @author ffj 17 | * 18 | */ 19 | public class Combinations { 20 | 21 | /** 22 | * 回溯法 23 | * 24 | * @param n 25 | * @param k 26 | * @return 27 | */ 28 | public List> combine(int n, int k) { 29 | List> combs = new ArrayList<>(); 30 | combine(combs, new ArrayList<>(), 1, n, k); 31 | return combs; 32 | } 33 | 34 | private static void combine(List> combs, List comb, int start, int n, int k) { 35 | if (k == 0) { 36 | // 注意要一个新对象 37 | combs.add(new ArrayList<>(comb)); 38 | return; 39 | } 40 | for (int i = start; i <= n; i++) { 41 | comb.add(i); 42 | combine(combs, comb, i + 1, n, k - 1); 43 | // 回溯 44 | comb.remove(comb.size() - 1); 45 | } 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/ConstructBinaryTreeFromPreorderAndInorderTraversal.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /*****************************从前序与中序遍历序列构造二叉树********************/ 4 | /** 5 | * 根据一棵树的前序遍历与中序遍历构造二叉树。 6 | * 7 | * 注意: 你可以假设树中没有重复的元素。 8 | * 9 | * 例如,给出 10 | * 11 | * 前序遍历 preorder = [3,9,20,15,7] 中序遍历 inorder = [9,3,15,20,7] 12 | * 13 | * 返回如下的二叉树: 14 | * 15 | * 3 16 | * 17 | * / \ 18 | * 19 | * 9 20 20 | * 21 | * / \ 22 | * 23 | * 15 7 24 | * 25 | * @author ffj 26 | * 27 | */ 28 | public class ConstructBinaryTreeFromPreorderAndInorderTraversal { 29 | 30 | public class TreeNode { 31 | int val; 32 | TreeNode left; 33 | TreeNode right; 34 | 35 | TreeNode(int x) { 36 | val = x; 37 | } 38 | } 39 | 40 | public TreeNode buildTree(int[] preorder, int[] inorder) { 41 | return helper(0, 0, inorder.length - 1, preorder, inorder); 42 | } 43 | 44 | /** 45 | * 返回根节点 46 | * 47 | * @param preStart 48 | * 根节点在先序遍历数组中位置 49 | * @param inStart 50 | * 中序遍历搜索起始位置 51 | * @param inEnd 52 | * 中序遍历搜索结束位置 53 | * @param preorder 54 | * 先序遍历数组 55 | * @param inorder 56 | * 中序遍历数组 57 | * @return 58 | */ 59 | private TreeNode helper(int preStart, int inStart, int inEnd, int[] preorder, int[] inorder) { 60 | if (preStart > preorder.length - 1 || inStart > inEnd) 61 | return null; 62 | TreeNode root = new TreeNode(preorder[preStart]); 63 | int inIndex = 0; 64 | for (int i = inStart; i <= inEnd; i++) { // 找到根节点对应的值 65 | if (inorder[i] == root.val) 66 | inIndex = i; 67 | } 68 | // 递归 划分左右节点 69 | root.left = helper(preStart + 1, inStart, inIndex - 1, preorder, inorder); 70 | root.right = helper(preStart + inIndex - inStart + 1, inIndex + 1, inEnd, preorder, inorder); 71 | return root; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/ConstructBinaryTreeFromPreorderAndPostorderTraversal.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.ArrayDeque; 4 | import java.util.Deque; 5 | 6 | /*************根据前序和后序遍历构造二叉树**************/ 7 | /** 8 | * 返回与给定的前序和后序遍历匹配的任何二叉树。 9 | * 10 | * pre 和 post 遍历中的值是不同的正整数。 11 | * 12 | * 示例: 13 | * 14 | * 输入:pre = [1,2,4,5,3,6,7], post = [4,5,2,6,7,3,1] 15 | * 16 | * 输出:[1,2,3,4,5,6,7] 17 | * 18 | * 提示: 19 | * 20 | * 1 <= pre.length == post.length <= 30 21 | * 22 | * pre[] 和 post[] 都是 1, 2, ..., pre.length 的排列 23 | * 24 | * 每个输入保证至少有一个答案。如果有多个答案,可以返回其中一个。 25 | * 26 | * @author ffj 27 | * 28 | */ 29 | public class ConstructBinaryTreeFromPreorderAndPostorderTraversal { 30 | 31 | public class TreeNode { 32 | int val; 33 | TreeNode left; 34 | TreeNode right; 35 | 36 | TreeNode(int x) { 37 | val = x; 38 | } 39 | } 40 | 41 | public TreeNode constructFromPrePost(int[] pre, int[] post) { 42 | Deque s = new ArrayDeque<>(); 43 | s.offer(new TreeNode(pre[0])); // head 节点 44 | for (int i = 1, j = 0; i < pre.length; ++i) { 45 | TreeNode node = new TreeNode(pre[i]); 46 | while (s.getLast().val == post[j]) { 47 | s.pollLast(); 48 | j++; 49 | } 50 | if (s.getLast().left == null) 51 | s.getLast().left = node; 52 | else 53 | s.getLast().right = node; 54 | s.offer(node); 55 | } 56 | return s.getFirst(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/ContainerWithMostWater.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /************************盛最多水的容器*******************/ 4 | /** 5 | * 6 | * 给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, 7 | * ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。 8 | * 9 | * 说明:你不能倾斜容器,且 n 的值至少为 2。 10 | * 11 | * 12 | * 图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。 13 | * 14 | * 15 | * 示例: 16 | * 17 | * 输入: [1,8,6,2,5,4,8,3,7] 输出: 49 18 | * 19 | * @author ffj 20 | * 21 | */ 22 | public class ContainerWithMostWater { 23 | 24 | public int maxArea(int[] height) { 25 | int length = height.length; 26 | int maxArea = 0; 27 | for (int i = 0; i < length - 1; i++) { 28 | for (int j = i + 1; j < length; j++) { 29 | maxArea = Math.max(maxArea, Math.min(height[i], height[j]) * (j - i)); 30 | } 31 | } 32 | return maxArea; 33 | } 34 | 35 | /** 36 | * 官网解法 双指针法 37 | * 38 | * @param height 39 | * @return 40 | */ 41 | public int maxArea1(int[] height) { 42 | int maxarea = 0, l = 0, r = height.length - 1; 43 | while (l < r) { 44 | maxarea = Math.max(maxarea, Math.min(height[l], height[r]) * (r - l)); 45 | // 移动较为矮的一方 用高度弥补长度 46 | if (height[l] < height[r]) 47 | l++; 48 | else 49 | r--; 50 | } 51 | return maxarea; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/DecodeWays.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /**********解码方法*******/ 4 | /** 5 | * 一条包含字母 A-Z 的消息通过以下方式进行了编码: 6 | * 7 | * 'A' -> 1 8 | * 9 | * 'B' -> 2 ... 10 | * 11 | * 'Z' -> 26 12 | * 13 | * 给定一个只包含数字的非空字符串,请计算解码方法的总数。 14 | * 15 | * 示例 1: 16 | * 17 | * 输入: "12" 输出: 2 18 | * 19 | * 解释: 它可以解码为 "AB"(1 2)或者 "L"(12)。 20 | * 21 | * 示例 2: 22 | * 23 | * 输入: "226" 输出: 3 24 | * 25 | * 解释: 它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。 26 | * 27 | * @author ffj 28 | * 29 | */ 30 | public class DecodeWays { 31 | 32 | public int numDecodings(String s) { 33 | int n = s.length(); 34 | if (n == 0) 35 | return 0; 36 | 37 | int[] memo = new int[n + 1]; 38 | memo[n] = 1; 39 | memo[n - 1] = s.charAt(n - 1) != '0' ? 1 : 0; 40 | // 必须遍历完 41 | for (int i = n - 2; i >= 0; i--) 42 | if (s.charAt(i) != '0') 43 | memo[i] = (Integer.parseInt(s.substring(i, i + 2)) <= 26) ? memo[i + 1] + memo[i + 2] : memo[i + 1]; 44 | 45 | return memo[0]; 46 | 47 | } 48 | 49 | /** 50 | * 条件版的 dp[i] = dp[i - 1] + dp[i - 2] 51 | * 52 | * @param str 53 | * @return 54 | */ 55 | public int numDecodings1(String str) { 56 | int len = str.length(); 57 | // 0 返回 58 | if (len == 0 || (len == 1 && str.charAt(0) == '0')) 59 | return 0; 60 | int[] dp = new int[2]; 61 | dp[0] = 1; 62 | dp[1] = 1; 63 | for (int i = 0; i < len; i++) { 64 | int temp = 0; 65 | if (str.charAt(i) != '0') 66 | temp += dp[1]; 67 | // 满足可拆分条件的 68 | if (i > 0 && (str.charAt(i - 1) == '1' || (str.charAt(i - 1) == '2' && str.charAt(i) <= '6'))) 69 | temp += dp[0]; 70 | dp[0] = dp[1]; 71 | dp[1] = temp; 72 | } 73 | return dp[1]; 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/EscapeTheGhosts.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /******************逃脱阻碍者****************/ 4 | /** 5 | * 6 | * 你在进行一个简化版的吃豆人游戏。你从 (0, 0) 点开始出发,你的目的地是 (target[0], target[1]) 。地图上有一些阻碍者,第 i 7 | * 个阻碍者从 (ghosts[i][0], ghosts[i][1]) 出发。 8 | * 9 | * 每一回合,你和阻碍者们*可以*同时向东,西,南,北四个方向移动,每次可以移动到距离原位置1个单位的新位置。 10 | * 11 | * 如果你可以在任何阻碍者抓住你之前到达目的地(阻碍者可以采取任意行动方式),则被视为逃脱成功。如果你和阻碍者同时到达了一个位置(包括目的地)都不算是逃脱成功。 12 | * 13 | * 当且仅当你有可能成功逃脱时,输出 True。 14 | * 15 | * 示例 1: 16 | * 17 | * 输入: ghosts = [[1, 0], [0, 3]] target = [0, 1] 18 | * 19 | * 输出:true 20 | * 21 | * 解释: 你可以直接一步到达目的地(0,1),在(1, 0)或者(0, 3)位置的阻碍者都不可能抓住你。 22 | * 23 | * 示例 2: 24 | * 25 | * 输入: ghosts = [[1, 0]] target = [2, 0] 26 | * 27 | * 输出:false 28 | * 29 | * 解释: 你需要走到位于(2, 0)的目的地,但是在(1, 0)的阻碍者位于你和目的地之间。 30 | * 31 | * 示例 3: 32 | * 33 | * 输入: ghosts = [[2, 0]] target = [1, 0] 34 | * 35 | * 输出:false 36 | * 37 | * 解释: 阻碍者可以和你同时达到目的地。 38 | * 39 | * 说明: 40 | * 41 | * 所有的点的坐标值的绝对值 <= 10000。 阻碍者的数量不会超过 100。 42 | * 43 | * @author ffj 44 | * 45 | */ 46 | public class EscapeTheGhosts { 47 | 48 | public boolean escapeGhosts(int[][] ghosts, int[] target) { 49 | 50 | // 就是如果你与目标点距离大于阻碍者与目标点的距离 就不可能到达 51 | int max = Math.abs(target[0]) + Math.abs(target[1]); 52 | for (int[] ghost : ghosts) { 53 | int dis = Math.abs(ghost[0] - target[0]) + Math.abs(ghost[1] - target[1]); 54 | if (dis <= max) 55 | return false; 56 | } 57 | return true; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/FindMinimumInRotatedSortedArray.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.Arrays; 4 | 5 | /**********************寻找旋转排序数组中的最小值************/ 6 | /** 7 | * 假设按照升序排序的数组在预先未知的某个点上进行了旋转。 8 | * 9 | * ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。 10 | * 11 | * 请找出其中最小的元素。 12 | * 13 | * 你可以假设数组中不存在重复元素。 14 | * 15 | * 示例 1: 16 | * 17 | * 输入: [3,4,5,1,2] 输出: 1 18 | * 19 | * 示例 2: 20 | * 21 | * 输入: [4,5,6,7,0,1,2] 输出: 0 22 | * 23 | * @author ffj 24 | * 25 | */ 26 | public class FindMinimumInRotatedSortedArray { 27 | 28 | public int findMin(int[] nums) { 29 | if (nums == null || nums.length == 0) { 30 | return 0; 31 | } 32 | if (nums.length == 1) { 33 | return nums[0]; 34 | } 35 | Arrays.sort(nums); 36 | return nums[0]; 37 | } 38 | 39 | /** 40 | * 二分法检索 41 | * 42 | * @param num 43 | * @return 44 | */ 45 | public int findMin1(int[] num) { 46 | if (num == null || num.length == 0) { 47 | return 0; 48 | } 49 | if (num.length == 1) { 50 | return num[0]; 51 | } 52 | int start = 0, end = num.length - 1; 53 | while (start < end) { 54 | int mid = (start + end) / 2; 55 | if (mid > 0 && num[mid] < num[mid - 1]) { // 说明是旋转的节点 56 | return num[mid]; 57 | } 58 | if (num[start] <= num[mid] && num[mid] > num[end]) { // 说明最小值在后半段 59 | start = mid + 1; 60 | } else { // 最小值在前半段 61 | end = mid - 1; 62 | } 63 | } 64 | return num[start]; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/FriendCircles.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /****************朋友圈***************/ 4 | /** 5 | * 班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 6 | * 的朋友。所谓的朋友圈,是指所有朋友的集合。 7 | * 8 | * 给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 9 | * 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。 10 | * 11 | * 示例 1: 12 | * 13 | * 输入: 14 | * 15 | * [ [1,1,0], 16 | * 17 | * [1,1,0], 18 | * 19 | * [0,0,1] ] 20 | * 21 | * 输出: 2 22 | * 23 | * 说明:已知学生0和学生1互为朋友,他们在一个朋友圈。 第2个学生自己在一个朋友圈。所以返回2。 24 | * 25 | * 示例 2: 26 | * 27 | * 输入: 28 | * 29 | * [ [1,1,0], 30 | * 31 | * [1,1,1], 32 | * 33 | * [0,1,1] ] 34 | * 35 | * 输出: 1 36 | * 37 | * 说明:已知学生0和学生1互为朋友,学生1和学生2互为朋友,所以学生0和学生2也是朋友,所以他们三个在一个朋友圈,返回1。 38 | * 39 | * 注意: 40 | * 41 | * N 在[1,200]的范围内。 42 | * 43 | * 对于所有学生,有M[i][i] = 1。 44 | * 45 | * 如果有M[i][j] = 1,则有M[j][i] = 1。 46 | * 47 | * @author ffj 48 | * 49 | */ 50 | public class FriendCircles { 51 | 52 | public int findCircleNum(int[][] M) { 53 | 54 | int[] visited = new int[M.length]; 55 | int count = 0; 56 | for (int i = 0; i < M.length; i++) { 57 | if (visited[i] == 0) { 58 | // 没在一个朋友圈 59 | dfs(M, visited, i); 60 | count++; 61 | } 62 | } 63 | return count; 64 | } 65 | 66 | /** 67 | * DFS 将相同朋友圈的全置1 68 | * 69 | * @param M 70 | * @param visited 71 | * @param i 72 | */ 73 | public void dfs(int[][] M, int[] visited, int i) { 74 | for (int j = 0; j < M.length; j++) { 75 | // 在同一个朋友圈 置一 76 | if (M[i][j] == 1 && visited[j] == 0) { 77 | visited[j] = 1; 78 | // 再找出与该同学在同一朋友圈的同学 79 | dfs(M, visited, j); 80 | } 81 | } 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/GenerateParentheses.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /**************************括号生成*********************/ 7 | /** 8 | * 9 | * 给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。 10 | * 11 | * 例如,给出 n = 3,生成结果为: 12 | * 13 | * [ 14 | * 15 | * "((()))", 16 | * 17 | * "(()())", 18 | * 19 | * "(())()", 20 | * 21 | * "()(())", 22 | * 23 | * "()()()" 24 | * 25 | * ] 26 | * 27 | * @author ffj 28 | * 29 | */ 30 | public class GenerateParentheses { 31 | 32 | public static void main(String[] args) { 33 | System.out.println(new GenerateParentheses().generateParenthesis(3)); 34 | } 35 | 36 | public List generateParenthesis(int n) { 37 | List list = new ArrayList<>(); 38 | helper("(", 1, n * 2 - 1, list); 39 | return list; 40 | } 41 | 42 | /** 43 | * 44 | * @param str 45 | * 挨个添加 "(" 或者 ")" 46 | * @param sum 47 | * 添加"(" : sum + 1, 添加 ")" : sum - 1 48 | * @param num 49 | * 剩余括号个数 50 | * @param list 51 | * 存放符合条件的String字符串 52 | */ 53 | private void helper(String str, int sum, int num, List list) { 54 | // 不符合 55 | if (sum < 0) 56 | return; 57 | // 次数到 但不符合条件 58 | if (num == 0 && sum != 0) 59 | return; 60 | if (num == 0 && sum == 0) { 61 | // 次数到了 且符合条件 62 | list.add(str); 63 | return; 64 | } 65 | // 递归 DFS 66 | helper(str + "(", sum + 1, num - 1, list); 67 | helper(str + ")", sum - 1, num - 1, list); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/GrayCode.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /********* 格雷编码 *****/ 7 | /** 8 | * 格雷编码是一个二进制数字系统,在该系统中,两个连续的数值仅有一个位数的差异。 9 | * 10 | * 给定一个代表编码总位数的非负整数 n,打印其格雷编码序列。格雷编码序列必须以 0 开头。 11 | * 12 | * 示例 1: 13 | * 14 | * 输入: 2 输出: [0,1,3,2] 15 | * 16 | * 解释: 17 | * 18 | * 00 - 0 19 | * 20 | * 01 - 1 21 | * 22 | * 11 - 3 23 | * 24 | * 10 - 2 25 | * 26 | * 对于给定的 n,其格雷编码序列并不唯一。 27 | * 28 | * 例如,[0,2,3,1] 也是一个有效的格雷编码序列。 29 | * 30 | * 00 - 0 31 | * 32 | * 10 - 2 33 | * 34 | * 11 - 3 35 | * 36 | * 01 - 1 37 | * 38 | * 示例 2: 39 | * 40 | * 输入: 0 输出: [0] 41 | * 42 | * 解释: 我们定义格雷编码序列必须以 0 开头。 43 | * 44 | * 给定编码总位数为 n 的格雷编码序列,其长度为 2^n。当 n = 0 时,长度为 2^0 = 1。 45 | * 46 | * 因此,当 n = 0 时,其格雷编码序列为 [0]。 47 | * 48 | * @author ffj 49 | * 50 | */ 51 | public class GrayCode { 52 | 53 | /** 54 | * 关键是搞清楚格雷编码的生成过程 G(i) = i ^ (i/2) 如 n = 3: 55 | * 56 | * G(0) = 000, 57 | * 58 | * G(1) = 1 ^ 0 = 001 ^ 000 = 001 59 | * 60 | * G(2) = 2 ^ 1 = 010 ^ 001 = 011 61 | * 62 | * G(3) = 3 ^ 1 = 011 ^ 001 = 010 63 | * 64 | * G(4) = 4 ^ 2 = 100 ^ 010 = 110 65 | * 66 | * G(5) = 5 ^ 2 = 101 ^ 010 = 111 67 | * 68 | * G(6) = 6 ^ 3 = 110 ^ 011 = 101 69 | * 70 | * G(7) = 7 ^ 3 = 111 ^ 011 = 100 71 | **/ 72 | public List grayCode(int n) { 73 | List codes = new ArrayList<>(); 74 | // 1<> 1)); // i>>1 即 i/2 77 | return codes; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/GroupAnagrams.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | /**************字母异位词分组**********/ 10 | /** 11 | * 给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。 12 | * 13 | * 示例: 14 | * 15 | * 输入: ["eat", "tea", "tan", "ate", "nat", "bat"], 16 | * 17 | * 输出: [ ["ate","eat","tea"], ["nat","tan"], ["bat"] ] 18 | * 19 | * 说明: 20 | * 21 | * 所有输入均为小写字母。 22 | * 23 | * 不考虑答案输出的顺序。 24 | * 25 | * @author ffj 26 | * 27 | */ 28 | public class GroupAnagrams { 29 | 30 | public List> groupAnagrams(String[] strs) { 31 | // key:排序后的str value:str 32 | Map> map = new HashMap<>(); 33 | Arrays.asList(strs).forEach(str -> { 34 | char[] chArr = str.toCharArray(); 35 | // 按字母排序 36 | Arrays.sort(chArr); 37 | String sortStrKey = String.valueOf(chArr); 38 | map.computeIfAbsent(sortStrKey, v -> new ArrayList<>()).add(str); 39 | }); 40 | return new ArrayList<>(map.values()); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/HIndex.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /*********H指数********/ 4 | /** 5 | * 给定一位研究者论文被引用次数的数组(被引用次数是非负整数)。编写一个方法,计算出研究者的 h 指数。 6 | * 7 | * h 指数的定义: “h 代表“高引用次数”(high citations),一名科研人员的 h 指数是指他(她)的 (N 篇论文中)至多有 h 8 | * 篇论文分别被引用了至少 h 次。(其余的 N - h 篇论文每篇被引用次数不多于 h 次。)” 9 | * 10 | * 示例: 11 | * 12 | * 输入: citations = [3,0,6,1,5] 输出: 3 13 | * 14 | * 解释: 给定数组表示研究者总共有 5 篇论文,每篇论文相应的被引用了 3, 0, 6, 1, 5 次。 由于研究者有 3 篇论文每篇至少被引用了 3 15 | * 次,其余两篇论文每篇被引用不多于 3 次,所以她的 h 指数是 3。 16 | * 17 | * 说明: 如果 h 有多种可能的值,h 指数是其中最大的那个。 18 | * 19 | * @author ffj 20 | * 21 | */ 22 | public class HIndex { 23 | 24 | public static void main(String[] args) { 25 | int[] citations = { 3, 0, 6, 1, 5 }; 26 | int result = new HIndex().hIndex(citations); 27 | System.out.println("result :" + result); 28 | } 29 | 30 | public int hIndex(int[] citations) { 31 | // [3,0,6,1,5] 32 | int length = citations.length; 33 | if (length == 0) { // 数组长度为 0 自然为 0 34 | return 0; 35 | } 36 | // 与下标对应 引用次数和论文数量 37 | int[] array2 = new int[length + 1]; 38 | for (int i = 0; i < length; i++) { 39 | if (citations[i] > length) { 40 | // 大于长度的值肯定在其中 放置最后 41 | array2[length] += 1; 42 | } else { 43 | array2[citations[i]] += 1; 44 | } 45 | } 46 | int t = 0; 47 | for (int i = length; i >= 0; i--) { 48 | t = t + array2[i]; 49 | if (t >= i) { // 满足条件 返回长度 50 | return i; 51 | } 52 | } 53 | return 0; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/HandOfStraights.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.TreeMap; 4 | 5 | /********************一手顺子****************/ 6 | /** 7 | * 爱丽丝有一手(hand)由整数数组给定的牌。 8 | * 9 | * 现在她想把牌重新排列成组,使得每个组的大小都是 W,且由 W 张连续的牌组成。 10 | * 11 | * 如果她可以完成分组就返回 true,否则返回 false。 12 | * 13 | * 示例 1: 14 | * 15 | * 输入:hand = [1,2,3,6,2,3,4,7,8], W = 3 输出:true 16 | * 17 | * 解释:爱丽丝的手牌可以被重新排列为 [1,2,3],[2,3,4],[6,7,8]。 18 | * 19 | * 示例 2: 20 | * 21 | * 输入:hand = [1,2,3,4,5], W = 4 输出:false 22 | * 23 | * 解释:爱丽丝的手牌无法被重新排列成几个大小为 4 的组。 24 | * 25 | * 提示: 26 | * 27 | * 1 <= hand.length <= 10000 28 | * 29 | * 0 <= hand[i] <= 10^9 30 | * 31 | * 1 <= W <= hand.length 32 | * 33 | * @author ffj 34 | * 35 | */ 36 | public class HandOfStraights { 37 | 38 | public boolean isNStraightHand(int[] hand, int W) { 39 | 40 | if (hand.length % W != 0) 41 | return false; 42 | // 先将元素塞入 treemap 中有序排列 43 | TreeMap count = new TreeMap<>(); 44 | for (int i : hand) { 45 | if (!count.containsKey(i)) 46 | count.put(i, 1); 47 | else 48 | count.replace(i, count.get(i) + 1); 49 | } 50 | 51 | while (count.size() > 0) { 52 | int first = count.firstKey(); 53 | // 每次删除一组数 54 | for (int card = first; card < first + W; ++card) { 55 | if (!count.containsKey(card)) // 没有该数直接返回 false 56 | return false; 57 | int num = count.get(card); 58 | // 减少 treemap 中相应 key 的 value 值 59 | if (num == 1) 60 | count.remove(card); 61 | else 62 | count.replace(card, num - 1); 63 | } 64 | } 65 | // 直到 treemap 中全都删除 66 | return true; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/ImplementMagicDictionary.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | /*********************实现一个魔法字典**************/ 8 | /** 9 | * 实现一个带有buildDict, 以及 search方法的魔法字典。 10 | * 11 | * 对于buildDict方法,你将被给定一串不重复的单词来构建一个字典。 12 | * 13 | * 对于search方法,你将被给定一个单词,并且判定能否只将这个单词中一个字母换成另一个字母,使得所形成的新单词存在于你构建的字典中。 14 | * 15 | * 示例 1: 16 | * 17 | * Input: buildDict(["hello", "leetcode"]), Output: Null 18 | * 19 | * Input: search("hello"), Output: False 20 | * 21 | * Input: search("hhllo"), Output: True 22 | * 23 | * Input: search("hell"), Output: False 24 | * 25 | * Input: search("leetcoded"), Output: False 26 | * 27 | * 注意: 28 | * 29 | * 你可以假设所有输入都是小写字母 a-z。 为了便于竞赛,测试所用的数据量很小。你可以在竞赛结束后,考虑更高效的算法。 30 | * 请记住重置MagicDictionary类中声明的类变量,因为静态/类变量会在多个测试用例中保留。 请参阅这里了解更多详情。 31 | * 32 | * @author ffj 33 | * 34 | */ 35 | public class ImplementMagicDictionary { 36 | 37 | Map> map; 38 | 39 | /** Initialize your data structure here. */ 40 | public ImplementMagicDictionary() { 41 | map = new HashMap<>(); 42 | } 43 | 44 | /** Build a dictionary through a list of words */ 45 | public void buildDict(String[] dict) { 46 | for (String s : dict) { 47 | // computeIfAbsent 返回value值 key如果没有则塞入 value为null则替换 48 | map.computeIfAbsent(s.length(), x -> new ArrayList<>()).add(s); 49 | } 50 | } 51 | 52 | /** 53 | * Returns if there is any word in the trie that equals to the given word after 54 | * modifying exactly one character 55 | */ 56 | public boolean search(String word) { 57 | // 长度一样的没有 直接返回false 58 | if (!map.containsKey(word.length())) 59 | return false; 60 | for (String s : map.get(word.length())) { 61 | int num = 0; 62 | for (int i = 0; i < word.length(); i++) { 63 | if (word.charAt(i) != s.charAt(i)) { // 发现不同加一 64 | if (++num > 1) // 超过1直接返回 65 | break; 66 | } 67 | } 68 | if (num == 1) // 满足条件 69 | return true; 70 | } 71 | return false; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/IntegerBreak.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /*************整数拆分*************/ 4 | /** 5 | * 给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。 6 | * 7 | * 示例 1: 8 | * 9 | * 输入: 2 输出: 1 10 | * 11 | * 解释: 2 = 1 + 1, 1 × 1 = 1。 12 | * 13 | * 示例 2: 14 | * 15 | * 输入: 10 输出: 36 16 | * 17 | * 解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。 18 | * 19 | * 说明: 你可以假设 n 不小于 2 且不大于 58。 20 | * 21 | * @author ffj 22 | * 23 | */ 24 | public class IntegerBreak { 25 | /** 26 | * 全部分解为 2 和 3 27 | * 28 | * @param n 29 | * @return 30 | */ 31 | public int integerBreak(int n) { 32 | int[] dp = new int[n + 1]; 33 | dp[1] = 1; 34 | for (int i = 2; i <= n; i++) { 35 | for (int j = 1; j <= i / 2; j++) 36 | dp[i] = Math.max(dp[i], Math.max(j, dp[j]) * Math.max(i - j, dp[i - j])); 37 | } 38 | return dp[n]; 39 | } 40 | 41 | public int integerBreak1(int n) { 42 | int max = 1; 43 | if (n == 2) // 1 * 1 44 | return 1; 45 | if (n == 3) // 1 * 2 46 | return 2; 47 | while (n > 0) { 48 | if (n == 2 || n == 3) 49 | return max * n; 50 | if (n == 4) { 51 | n -= 2; 52 | max *= 2; 53 | } else { 54 | n -= 3; 55 | max *= 3; 56 | } 57 | } 58 | return max; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/IntegerReplacement.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /********************整数替换*********************/ 4 | /** 5 | * 6 | * 给定一个正整数 n,你可以做如下操作: 7 | * 8 | * 1. 如果 n 是偶数,则用 n / 2替换 n。 9 | * 10 | * 2. 如果 n 是奇数,则可以用 n + 1或n - 1替换 n。 11 | * 12 | * n 变为 1 所需的最小替换次数是多少? 13 | * 14 | * 示例 1: 15 | * 16 | * 输入: 8 17 | * 18 | * 输出: 3 19 | * 20 | * 解释: 8 -> 4 -> 2 -> 1 21 | * 22 | * 示例 2: 23 | * 24 | * 输入: 7 25 | * 26 | * 输出: 4 27 | * 28 | * 解释: 7 -> 8 -> 4 -> 2 -> 1 或 7 -> 6 -> 3 -> 2 -> 1 29 | * 30 | * @author ffj 31 | * 32 | */ 33 | public class IntegerReplacement { 34 | public static void main(String[] args) { 35 | int n = 65535; 36 | int step = integerReplacement(n); 37 | System.out.println(step); 38 | } 39 | 40 | public static int integerReplacement(int n) { 41 | if (n == 1) 42 | return 0; 43 | int step = 0; 44 | while (n != 1) { 45 | if ((n & 1) == 0) { 46 | // 偶数 47 | n >>>= 1; // 右移一位即取一半 48 | } else if (n == 3 || ((n >>> 1) & 1) == 0) { // 特殊的3 以及当n右移一位为偶数时 减一操作 49 | n--; 50 | } else { // 加一操作 51 | n++; 52 | } 53 | step++; 54 | } 55 | return step; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/JumpGame.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /*************跳跃游戏***********/ 4 | /** 5 | * 给定一个非负整数数组,你最初位于数组的第一个位置。 6 | * 7 | * 数组中的每个元素代表你在该位置可以跳跃的最大长度。 8 | * 9 | * 判断你是否能够到达最后一个位置。 10 | * 11 | * 示例 1: 12 | * 13 | * 输入: [2,3,1,1,4] 输出: true 14 | * 15 | * 解释: 从位置 0 到 1 跳 1 步, 然后跳 3 步到达最后一个位置。 16 | * 17 | * 示例 2: 18 | * 19 | * 输入: [3,2,1,0,4] 输出: false 20 | * 21 | * 解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置。 22 | * 23 | * @author ffj 24 | * 25 | */ 26 | public class JumpGame { 27 | 28 | public static void main(String[] args) { 29 | int[] nums = { 5, 9, 3, 2, 1, 0, 2, 3, 3, 1, 0, 0 }; 30 | System.out.println(new JumpGame().canJump(nums)); 31 | } 32 | 33 | public boolean canJump(int[] nums) { 34 | 35 | if (nums == null || nums.length == 0) 36 | return true; 37 | int curMax = nums[0]; // 能到达的最远下标点 38 | for (int i = 1; i < nums.length; i++) { 39 | if (curMax < i) 40 | return false; // mean we are not able to reach position i 41 | curMax = Math.max(curMax, i + nums[i]); 42 | } 43 | return true; 44 | } 45 | 46 | /** 47 | * 双指针法 48 | * 49 | * @param nums 50 | * @return 51 | */ 52 | public boolean canJump1(int[] nums) { 53 | if (nums == null || nums.length == 0) 54 | return true; 55 | 56 | return helper(0, nums[0], nums); 57 | 58 | } 59 | 60 | private boolean helper(int start, int end, int[] nums) { 61 | if (end >= nums.length - 1) 62 | return true; 63 | if (end == start) // 说明到达不了 64 | return false; 65 | int father = end; 66 | for (int i = start; i <= end; i++) { 67 | father = Math.max(father, nums[i] + i); 68 | } 69 | return helper(end, father, nums); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/KThSymbolInGrammar.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /**********************第K个语法符号**************/ 4 | /** 5 | * 在第一行我们写上一个 0。接下来的每一行,将前一行中的0替换为01,1替换为10。 6 | * 7 | * 给定行数 N 和序数 K,返回第 N 行中第 K个字符。(K从1开始) 8 | * 9 | * 10 | * 例子: 11 | * 12 | * 输入: N = 1, K = 1 输出: 0 13 | * 14 | * 输入: N = 2, K = 1 输出: 0 15 | * 16 | * 输入: N = 2, K = 2 输出: 1 17 | * 18 | * 输入: N = 4, K = 5 输出: 1 19 | * 20 | * 解释: 21 | * 22 | * 第一行: 0 23 | * 24 | * 第二行: 01 25 | * 26 | * 第三行: 0110 27 | * 28 | * 第四行: 01101001 29 | * 30 | * 注意: 31 | * 32 | * N 的范围 [1, 30]. K 的范围 [1, 2^(N-1)]. 33 | * 34 | * @author ffj 35 | * 36 | */ 37 | public class KThSymbolInGrammar { 38 | 39 | int time = 0; 40 | 41 | public int kthGrammar(int N, int K) { 42 | 43 | if (N == 1) 44 | return 0; 45 | int len = (int) Math.pow(2, N - 1); 46 | if (K > len / 2) { // 统计调换次数 47 | K -= len / 2; 48 | time++; 49 | } 50 | kthGrammar(N - 1, K); 51 | 52 | return time % 2; // 调换次数偶数就是0 奇数就是1 53 | } 54 | 55 | /** 56 | * 一行代码 57 | * 58 | * @param N 59 | * @param K 60 | * @return 61 | */ 62 | public int kthGrammar1(int N, int K) { 63 | return Integer.bitCount(K - 1) & 1; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/KthSmallestElementInASortedMatrix.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.Arrays; 4 | import java.util.Comparator; 5 | import java.util.PriorityQueue; 6 | 7 | /*************有序矩阵中第K小的元素**********/ 8 | /** 9 | * 给定一个 n x n 矩阵,其中每行和每列元素均按升序排序,找到矩阵中第k小的元素。 请注意,它是排序后的第k小元素,而不是第k个元素。 10 | * 11 | * 示例: 12 | * 13 | * matrix = [ [ 1, 5, 9], [10, 11, 13], [12, 13, 15] ], 14 | * 15 | * k = 8, 16 | * 17 | * 返回 13。 说明: 你可以假设 k 的值永远是有效的, 1 ≤ k ≤ n2 。 18 | * 19 | * 20 | * @author ffj 21 | * 22 | */ 23 | public class KthSmallestElementInASortedMatrix { 24 | public static void main(String[] args) { 25 | 26 | } 27 | 28 | /** 29 | * 数组排序 30 | * 31 | * @param matrix 32 | * @param k 33 | * @return 34 | */ 35 | public int kthSmallest(int[][] matrix, int k) { 36 | int len = matrix.length; 37 | int[] num = new int[len * len]; 38 | for (int i = 0; i < len; i++) 39 | for (int j = 0; j < len; j++) 40 | num[i * len + j] = matrix[i][j]; 41 | // 数组升序排序 42 | Arrays.sort(num); 43 | return num[k - 1]; 44 | } 45 | 46 | /** 47 | * 堆排序 48 | * 49 | * @param matrix 50 | * @param k 51 | * @return 52 | */ 53 | public int kthSmallest1(int[][] matrix, int k) { 54 | // 大顶堆 55 | PriorityQueue maxHeap = new PriorityQueue<>(k, new Comparator() { 56 | @Override 57 | public int compare(Integer o1, Integer o2) { 58 | return o2 - o1; 59 | } 60 | }); 61 | 62 | for (int i = 0; i < matrix.length; i++) { 63 | // 比最大的还要大 忽略 64 | if (maxHeap.size() == k && matrix[i][0] > maxHeap.peek()) { 65 | break; 66 | } 67 | for (int j = 0; j < matrix[0].length; j++) { 68 | if (maxHeap.size() == k && matrix[i][j] > maxHeap.peek()) { 69 | break; 70 | } 71 | maxHeap.offer(matrix[i][j]); 72 | if (maxHeap.size() > k) { // 只管前 k 个数 73 | maxHeap.poll(); 74 | } 75 | } 76 | } 77 | return maxHeap.peek(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/LargestSumOfAverages.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /****************最大平均值和的分组*********/ 4 | /** 5 | * 我们将给定的数组 A 分成 K 个相邻的非空子数组 ,我们的分数由每个子数组内的平均值的总和构成。计算我们所能得到的最大分数是多少。 6 | * 7 | * 注意我们必须使用 A 数组中的每一个数进行分组,并且分数不一定需要是整数。 8 | * 9 | * 示例: 10 | * 11 | * 输入: 12 | * 13 | * A = [9,1,2,3,9] K = 3 14 | * 15 | * 输出: 20 16 | * 17 | * 解释: A 的最优分组是[9], [1, 2, 3], [9]. 得到的分数是 9 + (1 + 2 + 3) / 3 + 9 = 20. 我们也可以把 18 | * A 分成[9, 1], [2], [3, 9]. 这样的分组得到的分数为 5 + 2 + 6 = 13, 但不是最大值. 19 | * 20 | * 说明: 21 | * 22 | * 1 <= A.length <= 100. 23 | * 24 | * 1 <= A[i] <= 10000. 25 | * 26 | * 1 <= K <= A.length. 27 | * 28 | * 答案误差在 10^-6 内被视为是正确的。 29 | * 30 | * @author ffj 31 | * 32 | */ 33 | public class LargestSumOfAverages { 34 | 35 | public double largestSumOfAverages(int[] A, int K) { 36 | int N = A.length; 37 | // memo[i][j] 前i个数分成j组结果的最大值 38 | double[][] memo = new double[N + 1][N + 1]; 39 | double cur = 0; 40 | for (int i = 0; i < N; ++i) { 41 | cur += A[i]; 42 | memo[i + 1][1] = cur / (i + 1); 43 | } 44 | return search(N, K, A, memo); 45 | } 46 | 47 | public double search(int n, int k, int[] A, double[][] memo) { 48 | if (memo[n][k] > 0) 49 | return memo[n][k]; 50 | if (n < k) 51 | return 0; 52 | double cur = 0; 53 | for (int i = n - 1; i > 0; --i) { 54 | cur += A[i]; 55 | memo[n][k] = Math.max(memo[n][k], search(i, k - 1, A, memo) + cur / (n - i)); 56 | } 57 | return memo[n][k]; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/LongestSubstringWithAtLeastKRepeatingCharacters.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.Arrays; 4 | 5 | /*******************至少有K个重复字符的最长子串************/ 6 | /** 7 | * 找到给定字符串(由小写字符组成)中的最长子串 T , 要求 T 中的每一字符出现次数都不少于 k 。输出 T 的长度。 8 | * 9 | * 示例 1: 10 | * 11 | * 输入: s = "aaabb", k = 3 输出: 3 12 | * 13 | * 最长子串为 "aaa" ,其中 'a' 重复了 3 次。 14 | * 15 | * 示例 2: 16 | * 17 | * 输入: s = "ababbc", k = 2 18 | * 19 | * 输出: 5 20 | * 21 | * 最长子串为 "ababb" ,其中 'a' 重复了 2 次, 'b' 重复了 3 次。 22 | * 23 | * @author ffj 24 | * 25 | */ 26 | public class LongestSubstringWithAtLeastKRepeatingCharacters { 27 | 28 | public static void main(String[] args) { 29 | String s = "aabcbcw"; 30 | int k = 2; 31 | System.out.println(new LongestSubstringWithAtLeastKRepeatingCharacters().longestSubstring(s, k)); 32 | } 33 | 34 | public int longestSubstring(String s, int k) { 35 | char[] str = s.toCharArray(); 36 | int[] counts = new int[26]; 37 | int h, i, j, idx, max = 0, unique, noLessThanK; 38 | 39 | for (h = 1; h <= 26; h++) { // 满足子串中字母的个数 40 | // 初始都为 0 41 | Arrays.fill(counts, 0); 42 | // 首尾两指针 43 | i = 0; 44 | j = 0; 45 | // 子串中字母个数 46 | unique = 0; 47 | // 子串中满足条件的字母个数 48 | noLessThanK = 0; 49 | while (j < str.length) { 50 | if (unique <= h) { 51 | // 26 字母位置 52 | idx = str[j] - 'a'; 53 | if (counts[idx] == 0) 54 | // 首次出现 加一 55 | unique++; 56 | counts[idx]++; 57 | if (counts[idx] == k) 58 | // 已经满足要求 59 | noLessThanK++; 60 | j++; 61 | } else { // 从头开始删减字母 保持子串中字母个数 62 | idx = str[i] - 'a'; 63 | if (counts[idx] == k) // 说明该字母原先满足了条件 64 | noLessThanK--; 65 | counts[idx]--; 66 | if (counts[idx] == 0) // 说明原先该字母只有一个 67 | unique--; // 将该字母剔除 68 | i++; 69 | } 70 | if (unique == h && unique == noLessThanK) // 子串中字母个数 = 满足条件的字母个数 71 | max = Math.max(j - i, max); 72 | } 73 | } 74 | 75 | return max; 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/MaximumLengthOfPairChain.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.Arrays; 4 | 5 | /********************最长数对链************/ 6 | /** 7 | * 给出 n 个数对。 在每一个数对中,第一个数字总是比第二个数字小。 8 | * 9 | * 现在,我们定义一种跟随关系,当且仅当 b < c 时,数对(c, d) 才可以跟在 (a, b) 后面。我们用这种形式来构造一个数对链。 10 | * 11 | * 给定一个对数集合,找出能够形成的最长数对链的长度。你不需要用到所有的数对,你可以以任何顺序选择其中的一些数对来构造。 12 | * 13 | * 示例 : 14 | * 15 | * 输入: [[1,2], [2,3], [3,4]] 输出: 2 16 | * 17 | * 解释: 最长的数对链是 [1,2] -> [3,4] 18 | * 19 | * 注意: 20 | * 21 | * 给出数对的个数在 [1, 1000] 范围内。 22 | * 23 | * @author ffj 24 | * 25 | */ 26 | public class MaximumLengthOfPairChain { 27 | 28 | /** 29 | * DP 30 | * 31 | * @param pairs 32 | * @return 33 | */ 34 | public int findLongestChain(int[][] pairs) { 35 | 36 | // 排序 第一个元素升序 37 | Arrays.sort(pairs, (a, b) -> a[0] - b[0]); 38 | int N = pairs.length; 39 | int[] dp = new int[N]; 40 | Arrays.fill(dp, 1); 41 | 42 | for (int j = 1; j < N; ++j) { 43 | for (int i = 0; i < j; ++i) { 44 | if (pairs[i][1] < pairs[j][0]) 45 | dp[j] = Math.max(dp[j], dp[i] + 1); 46 | } 47 | } 48 | int ans = 0; 49 | for (int x : dp) 50 | if (x > ans) 51 | ans = x; 52 | return ans; 53 | 54 | } 55 | 56 | /** 57 | * 先满足第二个元素的条件 再满足 b < c 58 | * 59 | * @param pairs 60 | * @return 61 | */ 62 | public int findLongestChain1(int[][] pairs) { 63 | // 第二个元素升序 64 | Arrays.sort(pairs, (a, b) -> a[1] - b[1]); 65 | int cur = Integer.MIN_VALUE, ans = 0; 66 | for (int[] pair : pairs) 67 | if (cur < pair[0]) { 68 | cur = pair[1]; 69 | ans++; 70 | } 71 | return ans; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/MinimumPathSum.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /***********最小路径和*************/ 4 | /** 5 | * 给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 6 | * 7 | * 说明:每次只能向下或者向右移动一步。 8 | * 9 | * 示例: 10 | * 11 | * 输入: [ [1,3,1], [1,5,1], [4,2,1] ] 12 | * 13 | * 输出: 7 14 | * 15 | * 解释: 因为路径 1→3→1→1→1 的总和最小。 16 | * 17 | * @author ffj 18 | * 19 | */ 20 | public class MinimumPathSum { 21 | int min_val = Integer.MAX_VALUE; 22 | 23 | public int minPathSum(int[][] grid) { 24 | 25 | bfs(0, 0, grid, 0); 26 | return min_val; 27 | } 28 | 29 | /** 30 | * 超时 31 | * 32 | * @param row 33 | * @param col 34 | * @param grid 35 | * @param val 36 | */ 37 | private void bfs(int row, int col, int[][] grid, int val) { 38 | if (row == grid.length || col == grid[0].length) 39 | return; 40 | if (row == grid.length - 1 && col == grid[0].length - 1) { 41 | min_val = Math.min(min_val, val + grid[row][col]); 42 | return; 43 | } 44 | bfs(row + 1, col, grid, val + grid[row][col]); 45 | bfs(row, col + 1, grid, val + grid[row][col]); 46 | } 47 | 48 | /** 49 | * DP 50 | * 51 | * @param grid 52 | * @return 53 | */ 54 | public int minPathSumDP(int[][] grid) { 55 | int rows = grid.length, cols = grid[0].length; 56 | // 遍历所有数字 57 | for (int row = 0; row < rows; row++) { 58 | for (int col = 0; col < cols; col++) { 59 | if (row == 0 && col == 0) // grid[0][0] = grid[0][0] 60 | grid[row][col] = grid[row][col]; 61 | else if (row == 0 && col != 0) // 第一行上的数字只能是左边的数字选过来 62 | grid[row][col] = grid[row][col - 1] + grid[row][col]; 63 | else if (row != 0 && col == 0) // 第一列上的数字只能是上边的数字选过来 64 | grid[row][col] = grid[row - 1][col] + grid[row][col]; 65 | else // 其余的数字可能是上边或者左边选过来 选较小值来与该数相加 66 | grid[row][col] = Math.min(grid[row - 1][col], grid[row][col - 1]) + grid[row][col]; 67 | 68 | } 69 | } 70 | return grid[rows - 1][cols - 1]; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/MinimumSizeSubarraySum.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /************* 长度最小的子数组 *************************/ 4 | /** 5 | * 给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组。如果不存在符合条件的连续子数组,返回 0。 6 | * 7 | * 示例: 8 | * 9 | * 输入: s = 7, nums = [2,3,1,2,4,3] 输出: 2 解释: 子数组 [4,3] 是该条件下的长度最小的连续子数组。 进阶: 10 | * 11 | * 如果你已经完成了O(n) 时间复杂度的解法, 请尝试 O(n log n) 时间复杂度的解法。 12 | * 13 | * @author ffj 14 | * 15 | */ 16 | public class MinimumSizeSubarraySum { 17 | 18 | public static void main(String[] args) { 19 | int[] nums = { 1, 2, 3, 4, 5 }; 20 | int s = 15; 21 | int result = minSubArrayLen(s, nums); 22 | System.out.println(result); 23 | } 24 | 25 | public static int minSubArrayLen(int s, int[] nums) { 26 | 27 | for (int j = 0; j < nums.length; j++) { // 存在单个元素满足条件 28 | int num = nums[j]; 29 | if (num >= s) 30 | return 1; 31 | } 32 | int val = nums.length; 33 | boolean flag = false; 34 | for (int i = 0; i < nums.length - 1; i++) { // 连续元素 35 | int sum = nums[i]; 36 | for (int k = i + 1; k < nums.length; k++) { 37 | sum += nums[k]; 38 | if (sum >= s) { 39 | int value = k - i + 1; 40 | if (value <= val) { 41 | flag = true; 42 | val = value; 43 | } 44 | System.out.println("k :" + k + " i :" + i); 45 | break; 46 | } 47 | 48 | } 49 | } 50 | if (flag) 51 | return val; 52 | return 0; 53 | } 54 | 55 | /** 56 | * 精简写法 57 | * 58 | * @param s 59 | * @param a 60 | * @return 61 | */ 62 | public int minSubArrayLen1(int s, int[] a) { 63 | if (a == null || a.length == 0) 64 | return 0; 65 | 66 | int i = 0, j = 0, sum = 0, min = Integer.MAX_VALUE; 67 | 68 | while (j < a.length) { 69 | sum += a[j++]; 70 | 71 | while (sum >= s) { 72 | min = Math.min(min, j - i); 73 | sum -= a[i++]; 74 | } 75 | } 76 | 77 | return min == Integer.MAX_VALUE ? 0 : min; 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/MinimumSwapsToMakeSequencesIncreasing.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /********使序列递增的最小交换次数*********/ 4 | /** 5 | * 我们有两个长度相等且不为空的整型数组 A 和 B 。 6 | * 7 | * 我们可以交换 A[i] 和 B[i] 的元素。注意这两个元素在各自的序列中应该处于相同的位置。 8 | * 9 | * 在交换过一些元素之后,数组 A 和 B 都应该是严格递增的(数组严格递增的条件仅为A[0] < A[1] < A[2] < ... < 10 | * A[A.length - 1])。 11 | * 12 | * 给定数组 A 和 B ,请返回使得两个数组均保持严格递增状态的最小交换次数。假设给定的输入总是有效的。 13 | * 14 | * 示例: 输入: A = [1,3,5,4], B = [1,2,3,7] 输出: 1 15 | * 16 | * 解释: 17 | * 18 | * 交换 A[3] 和 B[3] 后,两个数组如下: 19 | * 20 | * A = [1, 3, 5, 7] , B = [1, 2, 3, 4] 21 | * 22 | * 两个数组均为严格递增的。 23 | * 24 | * 注意: 25 | * 26 | * A, B 两个数组的长度总是相等的,且长度的范围为 [1, 1000]。 27 | * 28 | * A[i], B[i] 均为 [0, 2000]区间内的整数。 29 | * 30 | * @author ffj 31 | * 32 | */ 33 | public class MinimumSwapsToMakeSequencesIncreasing { 34 | 35 | public int minSwap(int[] A, int[] B) { 36 | int len = A.length; 37 | // 第i位 转 的累加次数 38 | int[] swap = new int[len]; 39 | // 第i位 不转 的累加次数 40 | int[] not_swap = new int[len]; 41 | swap[0] = 1; 42 | for (int i = 1; i < len; i++) { 43 | if (A[i - 1] >= B[i] || B[i - 1] >= A[i]) { 44 | swap[i] = swap[i - 1] + 1; // 这个换 必然冲突 使得之前换的次数加上这次 45 | not_swap[i] = not_swap[i - 1]; // 这个不换 不影响 46 | } else if (A[i - 1] >= A[i] || B[i - 1] >= B[i]) { 47 | swap[i] = not_swap[i - 1] + 1; // 这个换 之前不换 加上 这次换 48 | not_swap[i] = swap[i - 1]; // 这个不换 之前的换 49 | } else { 50 | // 取较小值 51 | int temp = Math.min(swap[i - 1], not_swap[i - 1]); 52 | swap[i] = temp + 1; // 换 再加个1 53 | not_swap[i] = temp; // 不换 取较小值 54 | } 55 | } // 返回较小值 56 | return Math.min(not_swap[len - 1], swap[len - 1]); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/MultiplyStrings.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /*****************字符串相乘****************/ 4 | /** 5 | * 给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。 6 | * 7 | * 示例 1: 8 | * 9 | * 输入: num1 = "2", num2 = "3" 输出: "6" 10 | * 11 | * 示例 2: 12 | * 13 | * 输入: num1 = "123", num2 = "456" 输出: "56088" 14 | * 15 | * 说明: 16 | * 17 | * num1 和 num2 的长度小于110。 18 | * 19 | * num1 和 num2 只包含数字 0-9。 20 | * 21 | * num1 和 num2 均不以零开头,除非是数字 0 本身。 22 | * 23 | * 不能使用任何标准库的大数类型(比如 BigInteger)或直接将输入转换为整数来处理。 24 | * 25 | * @author ffj 26 | * 27 | */ 28 | public class MultiplyStrings { 29 | 30 | public String multiply(String num1, String num2) { 31 | 32 | int m = num1.length(), n = num2.length(); 33 | // 相乘结果的长度 34 | int[] pos = new int[m + n]; 35 | 36 | // 从低位开始循环乘 37 | for (int i = m - 1; i >= 0; i--) { 38 | for (int j = n - 1; j >= 0; j--) { 39 | // 相乘得值 40 | int mul = (num1.charAt(i) - '0') * (num2.charAt(j) - '0'); 41 | // 两数相乘结果在 pos 中影响的数值下标 42 | int p1 = i + j, p2 = i + j + 1; 43 | // 加上之前的旧值 44 | int sum = mul + pos[p2]; 45 | // 有进位则加上 46 | pos[p1] += sum / 10; 47 | // 取余 48 | pos[p2] = sum % 10; 49 | } 50 | } 51 | 52 | StringBuilder sb = new StringBuilder(); 53 | for (int p : pos) { 54 | // 过滤 0 开头的数 55 | if (!(sb.length() == 0 && p == 0)) { 56 | sb.append(p); 57 | } 58 | } 59 | return sb.length() == 0 ? "0" : sb.toString(); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/NextPermutation.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /*******************下一个排列****************/ 4 | /** 5 | * 实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。 6 | * 7 | * 如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。 8 | * 9 | * 必须原地修改,只允许使用额外常数空间。 10 | * 11 | * 以下是一些例子,输入位于左侧列,其相应输出位于右侧列。 12 | * 13 | * 1,2,3 → 1,3,2 14 | * 15 | * 3,2,1 → 1,2,3 16 | * 17 | * 1,1,5 → 1,5,1 18 | * 19 | * @author ffj 20 | * 21 | */ 22 | public class NextPermutation { 23 | 24 | public void nextPermutation(int[] nums) { 25 | int i = nums.length - 2; 26 | while (i >= 0 && nums[i + 1] <= nums[i]) { 27 | i--; 28 | } 29 | if (i >= 0) { 30 | int j = nums.length - 1; 31 | while (j >= 0 && nums[j] <= nums[i]) { 32 | j--; 33 | } 34 | swap(nums, i, j); 35 | } 36 | reverse(nums, i + 1); 37 | } 38 | 39 | /** 40 | * 翻转元素 41 | * 42 | * @param nums 43 | * @param start 44 | */ 45 | private void reverse(int[] nums, int start) { 46 | int i = start, j = nums.length - 1; 47 | while (i < j) { 48 | swap(nums, i, j); 49 | i++; 50 | j--; 51 | } 52 | } 53 | 54 | /** 55 | * 交换元素 56 | * 57 | * @param nums 58 | * @param i 59 | * @param j 60 | */ 61 | private void swap(int[] nums, int i, int j) { 62 | int temp = nums[i]; 63 | nums[i] = nums[j]; 64 | nums[j] = temp; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/NonOverlappingIntervals.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | /*********************无重叠区间**************/ 3 | 4 | import java.util.*; 5 | 6 | /** 7 | * 给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。 8 | * 9 | * 注意: 10 | * 11 | * 可以认为区间的终点总是大于它的起点。 12 | * 区间 [1,2] 和 [2,3] 的边界相互“接触”,但没有相互重叠。 13 | * 14 | * 示例 1: 15 | * 输入: [ [1,2], [2,3], [3,4], [1,3] ] 16 | * 输出: 1 17 | * 解释: 移除 [1,3] 后,剩下的区间没有重叠。 18 | * 19 | * 示例 2: 20 | * 输入: [ [1,2], [1,2], [1,2] ] 21 | * 输出: 2 22 | * 解释: 你需要移除两个 [1,2] 来使剩下的区间没有重叠。 23 | * 24 | * 示例 3: 25 | * 输入: [ [1,2], [2,3] ] 26 | * 输出: 0 27 | * 解释: 你不需要移除任何区间,因为它们已经是无重叠的了。 28 | * 29 | */ 30 | public class NonOverlappingIntervals { 31 | 32 | public int eraseOverlapIntervals(int[][] intervals) { 33 | if (intervals.length == 0) 34 | return 0; 35 | // 排序 根据 end 升序 36 | Arrays.sort(intervals, Comparator.comparingInt(o -> o[1])); 37 | int count = 1; // 区间数 38 | int end = intervals[0][1]; 39 | for (int i = 1; i < intervals.length; i++) { 40 | if (intervals[i][0] >= end) { // 根据结束节点来选择下一个区间 41 | count++; 42 | end = intervals[i][1]; 43 | } 44 | } 45 | return intervals.length - count; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/PartitionEqualSubsetSum.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | /****************分割等和子集************/ 3 | 4 | import java.util.Arrays; 5 | 6 | /** 7 | * 给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。 8 | * 9 | * 注意: 10 | * 11 | * 每个数组中的元素不会超过 100 12 | * 数组的大小不会超过 200 13 | * 示例 1: 14 | * 15 | * 输入: [1, 5, 11, 5] 16 | * 17 | * 输出: true 18 | * 19 | * 解释: 数组可以分割成 [1, 5, 5] 和 [11]. 20 | *   21 | * 22 | * 示例 2: 23 | * 24 | * 输入: [1, 2, 3, 5] 25 | * 26 | * 输出: false 27 | * 28 | * 解释: 数组不能分割成两个元素和相等的子集. 29 | * 30 | */ 31 | public class PartitionEqualSubsetSum { 32 | 33 | public boolean canPartition(int[] nums) { 34 | 35 | int len = nums.length; 36 | if (len == 0) { 37 | return false; 38 | } 39 | 40 | // 累加是否是偶数 41 | int sum = Arrays.stream(nums).sum(); 42 | if ((sum & 1) == 1) return false; 43 | 44 | int target = sum / 2; 45 | 46 | boolean[][] dp = new boolean[len][target + 1]; 47 | // 初始化成为 true 虽然不符合状态定义,但是从状态转移来说是完全可以的 48 | dp[0][0] = true; 49 | 50 | if (nums[0] == target) { 51 | dp[0][nums[0]] = true; 52 | } 53 | 54 | for (int i = 1; i < len; i++) { 55 | for (int j = 0; j <= target; j++) { 56 | 57 | dp[i][j] = dp[i - 1][j]; 58 | 59 | if (nums[i] <= j) { 60 | dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i]]; // 背包问题 背包有多少空间能不能用指定数量内来填充满 61 | } 62 | } 63 | 64 | // 由于状态转移方程的特殊性,提前结束,可以认为是剪枝操作 65 | if (dp[i][target]) { 66 | return true; 67 | } 68 | } 69 | return dp[len - 1][target]; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/PartitionList.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /************分隔链表*********/ 4 | /** 5 | * 给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。 6 | * 7 | * 你应当保留两个分区中每个节点的初始相对位置。 8 | * 9 | * 示例: 10 | * 11 | * 输入: head = 1->4->3->2->5->2, x = 3 12 | * 13 | * 输出: 1->2->2->4->3->5 14 | * 15 | * @author ffj 16 | * 17 | */ 18 | public class PartitionList { 19 | 20 | /** 21 | * 重新两链表 拼接 22 | * 23 | * @param head 24 | * @param x 25 | * @return 26 | */ 27 | public ListNode partition1(ListNode head, int x) { 28 | ListNode one = new ListNode(0); 29 | ListNode two = new ListNode(0); 30 | ListNode first = one, second = two; 31 | while (head != null) { 32 | int value = head.val; 33 | if (value < x) { 34 | first.next = head; 35 | first = first.next; 36 | } else { 37 | second.next = head; 38 | second = second.next; 39 | } 40 | head = head.next; 41 | } 42 | first.next = two.next; 43 | second.next = null; // 上面节点不是 new 的,引用所以要防止后续节点,若是 new ListNode(value) 则可不用置 null 44 | return one.next; 45 | } 46 | 47 | /** 48 | * 插入 49 | * 50 | * @param head 51 | * @param x 52 | * @return 53 | */ 54 | public ListNode partition(ListNode head, int x) { 55 | ListNode dummy = new ListNode(0); 56 | dummy.next = head; 57 | ListNode p = dummy; 58 | ListNode tail = dummy; 59 | while (p != null && p.next != null) { 60 | if (p.next.val >= x) 61 | p = p.next; 62 | else { 63 | if (p == tail) { // don't forget the edge cases when p==tail 64 | tail = tail.next; 65 | p = p.next; 66 | } else {// 节点交换 67 | ListNode tmp = p.next; 68 | p.next = tmp.next; 69 | tmp.next = tail.next; 70 | tail.next = tmp; 71 | tail = tmp; // don't forget to move tail. 72 | } 73 | } 74 | } 75 | return dummy.next; 76 | } 77 | 78 | } 79 | 80 | class ListNode { 81 | int val; 82 | ListNode next; 83 | 84 | ListNode(int x) { 85 | val = x; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/PeekingIterator.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.Iterator; 4 | import java.util.NoSuchElementException; 5 | 6 | /****************顶端迭代器**************/ 7 | /** 8 | * 给定一个迭代器类的接口,接口包含两个方法: next() 和 hasNext()。设计并实现一个支持 peek() 操作的顶端迭代器 -- 9 | * 其本质就是把原本应由 next() 方法返回的元素 peek() 出来。 10 | * 11 | * 示例: 12 | * 13 | * 假设迭代器被初始化为列表 [1,2,3]。 14 | * 15 | * 调用 next() 返回 1,得到列表中的第一个元素。 现在调用 peek() 返回 2,下一个元素。在此之后调用 next() 仍然返回 2。 16 | * 最后一次调用 next() 返回 3,末尾元素。在此之后调用 hasNext() 应该返回 false。 17 | * 进阶:你将如何拓展你的设计?使之变得通用化,从而适应所有的类型,而不只是整数型? 18 | * 19 | * @author ffj 20 | * 21 | */ 22 | // Java Iterator interface reference: 23 | // https://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html 24 | public class PeekingIterator implements Iterator { 25 | 26 | private Integer next; 27 | private Iterator iter; 28 | private boolean noSuchElement; // 默认值 false 29 | 30 | public PeekingIterator(Iterator iterator) { 31 | // initialize any member here. 32 | this.iter = iterator; 33 | if (iter.hasNext()) { 34 | next = iter.next(); 35 | } else 36 | noSuchElement = true; 37 | 38 | } 39 | 40 | // Returns the next element in the iteration without advancing the iterator. 41 | public Integer peek() { 42 | return next; 43 | } 44 | 45 | // hasNext() and next() should behave the same as in the Iterator interface. 46 | // Override them if needed. 47 | @Override 48 | public Integer next() { 49 | if (noSuchElement) 50 | throw new NoSuchElementException(); 51 | Integer res = next; 52 | if (iter.hasNext()) { 53 | next = iter.next(); 54 | } else 55 | noSuchElement = true; 56 | return res; 57 | } 58 | 59 | @Override 60 | public boolean hasNext() { 61 | return !noSuchElement; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/PermutationSequence.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /************* 第 k 个排列 ************/ 7 | /** 8 | * 给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。 9 | * 10 | * 按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下: 11 | * 12 | * "123", "132", "213", "231", "312", "321" 13 | * 14 | * 给定 n 和 k,返回第 k 个排列。 15 | * 16 | * 说明: 17 | * 18 | * 给定 n 的范围是 [1, 9]。 19 | * 20 | * 给定 k 的范围是[1, n!]。 21 | * 22 | * 示例 1: 23 | * 24 | * 输入: n = 3, k = 3 输出: "213" 25 | * 26 | * 示例 2: 27 | * 28 | * 输入: n = 4, k = 9 输出: "2314" 29 | * 30 | * @author ffj 31 | * 32 | */ 33 | public class PermutationSequence { 34 | 35 | public String getPermutation(int n, int k) { 36 | 37 | List numbers = new ArrayList<>(); 38 | int[] factorial = new int[n + 1]; 39 | StringBuilder sb = new StringBuilder(); 40 | 41 | // 存储一个阶乘值数组 42 | int sum = 1; 43 | factorial[0] = 1; 44 | for (int i = 1; i <= n; i++) { 45 | sum *= i; 46 | factorial[i] = sum; 47 | } 48 | // factorial[] = {1, 1, 2, 6, 24, ... n!} 49 | 50 | // 存储一个有序数值集合 51 | for (int i = 1; i <= n; i++) 52 | numbers.add(i); 53 | // numbers = {1, 2, 3, ..., n} 54 | 55 | k--; 56 | int index; 57 | for (int i = 1; i <= n; i++) { 58 | index = k / factorial[n - i]; 59 | sb.append(String.valueOf(numbers.get(index))); 60 | numbers.remove(index); 61 | // 一位一位计算 62 | k -= index * factorial[n - i]; 63 | } 64 | 65 | return sb.toString(); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/PermutationsII.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | 7 | /**************全排列 II***********/ 8 | /** 9 | * 给定一个可包含重复数字的序列,返回所有不重复的全排列。 10 | * 11 | * 示例: 12 | * 13 | * 输入: [1,1,2] 14 | * 15 | * 输出: [ [1,1,2], [1,2,1], [2,1,1] ] 16 | * 17 | * @author ffj 18 | * 19 | */ 20 | public class PermutationsII { 21 | 22 | public List> permuteUnique(int[] nums) { 23 | List> permuteUniques = new ArrayList<>(); 24 | if (nums == null || nums.length == 0) 25 | return permuteUniques; 26 | // 要去重 先排序 27 | Arrays.sort(nums); 28 | boolean[] used = new boolean[nums.length]; 29 | dfs(nums, used, permuteUniques, new ArrayList<>()); 30 | return permuteUniques; 31 | } 32 | 33 | /** 34 | * 35 | * @param nums 36 | * 原数组 37 | * @param used 38 | * 标记数字使用状态 39 | * @param permuteUniques 40 | * 目标集合 41 | * @param permuteUnique 42 | * 单个集合 43 | */ 44 | private void dfs(int[] nums, boolean[] used, List> permuteUniques, List permuteUnique) { 45 | if (permuteUnique.size() == nums.length) { 46 | permuteUniques.add(new ArrayList<>(permuteUnique)); 47 | return; 48 | } 49 | for (int i = 0; i < nums.length; i++) { 50 | // 已经标记过的略过 51 | if (used[i]) 52 | continue; 53 | // 数字有重复的 说明刚释放的值与该值一样 略过 54 | if (i > 0 && nums[i - 1] == nums[i] && !used[i - 1]) 55 | continue; 56 | used[i] = true; // 标记使用 57 | permuteUnique.add(nums[i]); // 该数字加入集合 58 | // 重复操作 选择剩余数字 59 | dfs(nums, used, permuteUniques, permuteUnique); 60 | // 当出栈时将最后一个数从集合中删除 同时该数恢复未使用状态 继续操作 61 | used[i] = false; 62 | permuteUnique.remove(permuteUnique.size() - 1); 63 | } 64 | 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/Powxn.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /******************Pow(x,n)*********/ 4 | /** 5 | * 实现 pow(x, n) ,即计算 x 的 n 次幂函数。 6 | * 7 | * 示例 1: 8 | * 9 | * 输入: 2.00000, 10 输出: 1024.00000 10 | * 11 | * 示例 2: 12 | * 13 | * 输入: 2.10000, 3 输出: 9.26100 14 | * 15 | * 示例 3: 16 | * 17 | * 输入: 2.00000, -2 输出: 0.25000 18 | * 19 | * 解释: 2^-2 = 1/22 = 1/4 = 0.25 20 | * 21 | * 说明: 22 | * 23 | * -100.0 < x < 100.0 24 | * 25 | * n 是 32 位有符号整数,其数值范围是 [−2^31, 2^31 − 1] 。 26 | * 27 | * @author ffj 28 | * 29 | */ 30 | public class Powxn { 31 | 32 | public static void main(String[] args) { 33 | double x = 2.0; 34 | int n = 10; 35 | double result = new Powxn().myPow(x, n); 36 | System.out.println("result: " + result); 37 | } 38 | 39 | public double myPow(double x, int n) { 40 | if (n == 0) 41 | return 1; 42 | double t = myPow(x, n / 2); 43 | // 避免多余的乘计算 44 | if (n % 2 != 0) 45 | return n < 0 ? 1 / x * t * t : x * t * t; 46 | else 47 | return t * t; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/RabbitsInForest.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.Arrays; 4 | 5 | /***********森林中的兔子***********/ 6 | /** 7 | * 森林中,每个兔子都有颜色。其中一些兔子(可能是全部)告诉你还有多少其他的兔子和自己有相同的颜色。我们将这些回答放在 answers 数组里。 8 | * 9 | * 返回森林中兔子的最少数量。 10 | * 11 | * 示例: 输入: answers = [1, 1, 2] 输出: 5 12 | * 13 | * 解释: 两只回答了 "1" 的兔子可能有相同的颜色,设为红色。 之后回答了 "2" 的兔子不会是红色,否则他们的回答会相互矛盾。 设回答了 "2" 14 | * 的兔子为蓝色。 此外,森林中还应有另外 2 只蓝色兔子的回答没有包含在数组中。 因此森林中兔子的最少数量是 5: 3 只回答的和 2 只没有回答的。 15 | * 16 | * 输入: answers = [10, 10, 10] 输出: 11 17 | * 18 | * 输入: answers = [] 输出: 0 说明: 19 | * 20 | * answers 的长度最大为1000。 answers[i] 是在 [0, 999] 范围内的整数。 21 | * 22 | * @author ffj 23 | * 24 | */ 25 | public class RabbitsInForest { 26 | 27 | public static void main(String[] args) { 28 | int[] answers = { 1, 1, 2 }; 29 | System.out.println("result:" + new RabbitsInForest().numRabbits(answers)); 30 | } 31 | 32 | public int numRabbits(int[] answers) { 33 | Arrays.sort(answers); // 先排序 34 | int currentNumber = 0; 35 | int currentCount = 0; // 数量 36 | int result = 0; 37 | for (int i = 0; i < answers.length; i++) { 38 | if (answers[i] == 0)// 唯一颜色 39 | result++; 40 | else { 41 | if (answers[i] == currentNumber && (currentCount <= answers[i])) { // 同一颜色 42 | currentCount++; 43 | continue; 44 | } else { // 又是一种颜色 45 | result += answers[i] + 1; 46 | currentNumber = answers[i]; 47 | currentCount = 1; 48 | } 49 | } 50 | 51 | } 52 | return result; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/RectangleArea.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /************************矩形面积******************/ 4 | /** 5 | * 在二维平面上计算出两个由直线构成的矩形重叠后形成的总面积。 6 | * 7 | * 每个矩形由其左下顶点和右上顶点坐标表示,如图所示。 8 | * 9 | * Rectangle Area 10 | * 11 | * 示例: 12 | * 13 | * 输入: -3, 0, 3, 4, 0, -1, 9, 2 输出: 45 14 | * 15 | * 说明: 假设矩形面积不会超出 int 的范围。 16 | * 17 | * @author ffj 18 | * 19 | */ 20 | public class RectangleArea { 21 | 22 | public int computeArea(int A, int B, int C, int D, int E, int F, int G, int H) { 23 | 24 | int sum = (C - A) * (D - B) + (H - F) * (G - E); // 两个矩形面积之和 25 | 26 | if (E >= C || H <= B || G <= A || F >= D) 27 | return sum; // 两个矩形没有相交 28 | 29 | return sum - ((Math.min(G, C) - Math.max(A, E)) * (Math.min(D, H) - Math.max(B, F))); // 减去相交的面积 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/RemoveNthNodeFromEndOfList.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /**********************删除链表的倒数第N个节点***********************/ 4 | /** 5 | * 给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。 6 | * 7 | * 示例: 8 | * 9 | * 给定一个链表: 1->2->3->4->5, 和 n = 2. 10 | * 11 | * 当删除了倒数第二个节点后,链表变为 1->2->3->5. 说明: 12 | * 13 | * 给定的 n 保证是有效的。 14 | * 15 | * 进阶: 16 | * 17 | * 你能尝试使用一趟扫描实现吗? 18 | * 19 | * @author ffj 20 | * 21 | */ 22 | public class RemoveNthNodeFromEndOfList { 23 | 24 | public class ListNode { 25 | int val; 26 | ListNode next; 27 | 28 | ListNode(int x) { 29 | val = x; 30 | } 31 | } 32 | 33 | /** 34 | * 两次循环 35 | * 36 | * @param head 37 | * @param n 38 | * @return 39 | */ 40 | public ListNode removeNthFromEnd(ListNode head, int n) { 41 | 42 | ListNode temp = new ListNode(0); 43 | temp.next = head; 44 | ListNode first = head; 45 | int len = 0; 46 | while (first != null) { // 计算长度 47 | len++; 48 | first = first.next; 49 | } 50 | len -= n; 51 | first = temp; 52 | while (len > 0) { // 遍历找到移除节点前节点 53 | len--; 54 | first = first.next; 55 | } 56 | first.next = first.next.next; // 移除节点 57 | return temp.next; 58 | 59 | } 60 | 61 | /** 62 | * 一次循环 63 | * 64 | * @param head 65 | * @param n 66 | * @return 67 | */ 68 | public ListNode removeNthFromEnd1(ListNode head, int n) { 69 | ListNode temp = new ListNode(0); 70 | temp.next = head; 71 | ListNode first = temp, second = temp; 72 | for (int i = 1; i < n + 1; i++) { 73 | first = first.next; 74 | } 75 | while (first != null) { // 当first节点为null时 second节点刚好在移除节点前一节点 76 | first = first.next; 77 | second = second.next; 78 | } 79 | second.next = second.next.next; 80 | return temp.next; 81 | 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/ReorganizeString.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.PriorityQueue; 6 | 7 | /**********重构字符串**************/ 8 | /** 9 | * 给定一个字符串S,检查是否能重新排布其中的字母,使得两相邻的字符不同。 10 | * 11 | * 若可行,输出任意可行的结果。若不可行,返回空字符串。 12 | * 13 | * 示例 1: 14 | * 15 | * 输入: S = "aab" 输出: "aba" 16 | * 17 | * 示例 2: 18 | * 19 | * 输入: S = "aaab" 输出: "" 20 | * 21 | * 注意: 22 | * 23 | * S 只包含小写字母并且长度在[1, 500]区间内。 24 | * 25 | * @author ffj 26 | * 27 | */ 28 | public class ReorganizeString { 29 | 30 | public static void main(String[] args) { 31 | String S = "aab"; 32 | System.out.println(new ReorganizeString().reorganizeString(S)); 33 | } 34 | 35 | public String reorganizeString(String S) { 36 | // Create map of each char to its count 37 | // map 统计次数 38 | Map map = new HashMap<>(); 39 | for (char c : S.toCharArray()) { 40 | int count = map.getOrDefault(c, 0) + 1; 41 | // Impossible to form a solution 42 | if (count > (S.length() + 1) / 2) 43 | return ""; 44 | map.put(c, count); 45 | } 46 | // Greedy: fetch char of max count as next char in the result. 47 | // Use PriorityQueue to store pairs of (char, count) and sort by count DESC. 48 | // 优先级队列 根据次数倒序 49 | PriorityQueue pq = new PriorityQueue<>((a, b) -> b[1] - a[1]); 50 | for (char c : map.keySet()) { 51 | pq.add(new int[] { c, map.get(c) }); 52 | } 53 | // Build the result. 54 | // 次数最多的两个依次交替 55 | StringBuilder sb = new StringBuilder(); 56 | while (!pq.isEmpty()) { 57 | int[] first = pq.poll(); 58 | if (sb.length() == 0 || first[0] != sb.charAt(sb.length() - 1)) { 59 | sb.append((char) first[0]); // 拼接 60 | if (--first[1] > 0) { 61 | pq.add(first); // 不为 0 就接着塞回去 62 | } 63 | } else { 64 | int[] second = pq.poll(); 65 | sb.append((char) second[0]); 66 | if (--second[1] > 0) { 67 | pq.add(second); 68 | } 69 | pq.add(first); 70 | } 71 | } 72 | return sb.toString(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/ReverseLinkedListII.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /*************反转链表 II**********/ 4 | /** 5 | * 反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。 6 | * 7 | * 说明: 1 ≤ m ≤ n ≤ 链表长度。 8 | * 9 | * 示例: 10 | * 11 | * 输入: 1->2->3->4->5->NULL, m = 2, n = 4 12 | * 13 | * 输出: 1->4->3->2->5->NULL 14 | * 15 | * @author ffj 16 | * 17 | */ 18 | public class ReverseLinkedListII { 19 | 20 | public class ListNode { 21 | int val; 22 | ListNode next; 23 | 24 | ListNode(int x) { 25 | val = x; 26 | } 27 | } 28 | 29 | public ListNode reverseBetween(ListNode head, int m, int n) { 30 | if (head == null) 31 | return null; 32 | // 新建一个节点并指向 head 33 | ListNode dummy = new ListNode(0); 34 | dummy.next = head; 35 | ListNode pre = dummy; 36 | // pre 为需要反转的前节点 37 | for (int i = 0; i < m - 1; i++) 38 | pre = pre.next; 39 | 40 | // 需要反转的节点 双指针 41 | ListNode start = pre.next; 42 | ListNode then = start.next; 43 | 44 | // 反转节点 45 | for (int i = 0; i < n - m; i++) { 46 | start.next = then.next; 47 | then.next = pre.next; 48 | pre.next = then; 49 | then = start.next; 50 | } 51 | return dummy.next; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/RotateImage.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /***************旋转图像************/ 4 | /** 5 | * 给定一个 n × n 的二维矩阵表示一个图像。 6 | * 7 | * 将图像顺时针旋转 90 度。 8 | * 9 | * 说明: 10 | * 11 | * 你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。 12 | * 13 | * 示例 1: 14 | * 15 | * 给定 matrix = [ [1,2,3], [4,5,6], [7,8,9] ], 16 | * 17 | * 原地旋转输入矩阵,使其变为: 18 | * 19 | * [ [7,4,1], [8,5,2], [9,6,3] ] 20 | * 21 | * 示例 2: 22 | * 23 | * 给定 matrix = [ [ 5, 1, 9,11], [ 2, 4, 8,10], [13, 3, 6, 7], [15,14,12,16] ], 24 | * 25 | * 原地旋转输入矩阵,使其变为: 26 | * 27 | * [ [15,13, 2, 5], [14, 3, 4, 1], [12, 6, 8, 9], [16, 7,10,11] ] 28 | * 29 | * @author ffj 30 | * 31 | */ 32 | public class RotateImage { 33 | /** 34 | * 先进行 X 轴旋转,再对角线旋转 35 | * 36 | * @param matrix 37 | */ 38 | public void rotate(int[][] matrix) { 39 | 40 | int len = matrix.length; 41 | // X 轴旋转 42 | for (int i = 0; i < len / 2; i++) { 43 | int[] temp = matrix[i]; 44 | matrix[i] = matrix[len - i - 1]; 45 | matrix[len - i - 1] = temp; 46 | } 47 | // 对角线旋转 48 | for (int x = 0; x < len; x++) { 49 | for (int y = 0; y < x; y++) { 50 | int temp = matrix[x][y]; 51 | matrix[x][y] = matrix[y][x]; 52 | matrix[y][x] = temp; 53 | } 54 | } 55 | } 56 | 57 | /** 58 | * 依次旋转 从外到内 59 | * 60 | * @param matrix 61 | */ 62 | public void rotate1(int[][] matrix) { 63 | int len = matrix.length; 64 | for (int i = 0; i < len / 2; i++) { 65 | int start = i; 66 | int end = len - i - 1; 67 | for (int j = 0; j < end - start; j++) { 68 | int temp = matrix[start][start + j]; 69 | matrix[start][start + j] = matrix[end - j][start]; 70 | matrix[end - j][start] = matrix[end][end - j]; 71 | matrix[end][end - j] = matrix[start + j][end]; 72 | matrix[start + j][end] = temp; 73 | } 74 | } 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/SearchA2DMatrix.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /********搜索二维矩阵**********/ 4 | /** 5 | * 编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性: 6 | * 7 | * 每行中的整数从左到右按升序排列。 8 | * 9 | * 每行的第一个整数大于前一行的最后一个整数。 10 | * 11 | * 示例 1: 12 | * 13 | * 输入: 14 | * 15 | * matrix = [ [1, 3, 5, 7], [10, 11, 16, 20], [23, 30, 34, 50] ] 16 | * 17 | * target = 3 18 | * 19 | * 输出: true 20 | * 21 | * 示例 2: 22 | * 23 | * 输入: 24 | * 25 | * matrix = [ [1, 3, 5, 7], [10, 11, 16, 20], [23, 30, 34, 50] ] 26 | * 27 | * target = 13 28 | * 29 | * 输出: false 30 | * 31 | * @author ffj 32 | * 33 | */ 34 | public class SearchA2DMatrix { 35 | 36 | public boolean searchMatrix(int[][] matrix, int target) { 37 | 38 | if (matrix == null || matrix.length == 0) 39 | return false; 40 | int rows = matrix.length, cols = matrix[0].length; 41 | // 从右上角往左下角找 42 | int row = 0, col = cols - 1; 43 | while (row < rows && col >= 0) { 44 | if (target > matrix[row][col]) 45 | row++; 46 | else if (target < matrix[row][col]) 47 | col--; 48 | else // 找到该数 49 | return true; 50 | } 51 | return false; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/SimplifyPath.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.Stack; 4 | 5 | /*************简化路径*********/ 6 | /** 7 | * 以 Unix 风格给出一个文件的绝对路径,你需要简化它。或者换句话说,将其转换为规范路径。 8 | * 9 | * 在 Unix 风格的文件系统中,一个点(.)表示当前目录本身;此外,两个点 (..) 10 | * 表示将目录切换到上一级(指向父目录);两者都可以是复杂相对路径的组成部分。更多信息请参阅:Linux / Unix中的绝对路径 vs 相对路径 11 | * 12 | * 请注意,返回的规范路径必须始终以斜杠 / 开头,并且两个目录名之间必须只有一个斜杠 /。最后一个目录名(如果存在)不能以 / 13 | * 结尾。此外,规范路径必须是表示绝对路径的最短字符串。 14 | * 15 | * 16 | * 17 | * 示例 1: 18 | * 19 | * 输入:"/home/" 输出:"/home" 20 | * 21 | * 解释:注意,最后一个目录名后面没有斜杠。 22 | * 23 | * 示例 2: 24 | * 25 | * 输入:"/../" 输出:"/" 26 | * 27 | * 解释:从根目录向上一级是不可行的,因为根是你可以到达的最高级。 28 | * 29 | * 示例 3: 30 | * 31 | * 输入:"/home//foo/" 输出:"/home/foo" 32 | * 33 | * 解释:在规范路径中,多个连续斜杠需要用一个斜杠替换。 34 | * 35 | * 示例 4: 36 | * 37 | * 输入:"/a/./b/../../c/" 输出:"/c" 38 | * 39 | * 示例 5: 40 | * 41 | * 输入:"/a/../../b/../c//.//" 输出:"/c" 42 | * 43 | * 示例 6: 44 | * 45 | * 输入:"/a//b////c/d//././/.." 输出:"/a/b/c" 46 | * 47 | * @author ffj 48 | * 49 | */ 50 | public class SimplifyPath { 51 | 52 | public static void main(String[] args) { 53 | 54 | } 55 | 56 | public String simplifyPath(String path) { 57 | Stack stack = new Stack<>(); 58 | // 根据"/"分割成数组 59 | String[] paths = path.split("/"); 60 | 61 | for (String s : paths) { 62 | if ("..".equals(s)) { 63 | if (stack.size() > 0) // 返回上一级 64 | stack.pop(); 65 | } else if (!"".equals(s) && !".".equals(s)) { // 入栈 66 | stack.push(s); 67 | } 68 | } 69 | if (stack.isEmpty()) 70 | return "/"; 71 | StringBuilder sb = new StringBuilder(); 72 | sb.append("/"); 73 | for (String s : stack) { 74 | sb.append(s); 75 | sb.append("/"); 76 | } 77 | String result = sb.toString(); 78 | // 移除最后一个斜杠后返回 79 | return result.substring(0, result.lastIndexOf("/")); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/SortColors.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /*************颜色分类***********/ 4 | /** 5 | * 给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。 6 | * 7 | * 此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。 8 | * 9 | * 注意: 不能使用代码库中的排序函数来解决这道题。 10 | * 11 | * 示例: 12 | * 13 | * 输入: [2,0,2,1,1,0] 14 | * 15 | * 输出: [0,0,1,1,2,2] 16 | * 17 | * 进阶: 18 | * 19 | * 一个直观的解决方案是使用计数排序的两趟扫描算法。 20 | * 21 | * 首先,迭代计算出0、1 和 2 元素的个数,然后按照0、1、2的排序,重写当前数组。 22 | * 23 | * 你能想出一个仅使用常数空间的一趟扫描算法吗? 24 | * 25 | * @author ffj 26 | * 27 | */ 28 | public class SortColors { 29 | 30 | public static void main(String[] args) { 31 | int[] nums = { 2, 0, 2, 1, 1, 0 }; 32 | new SortColors().sortColors(nums); 33 | } 34 | 35 | /** 36 | * 两遍循环 37 | * 38 | * @param nums 39 | */ 40 | public void sortColors(int[] nums) { 41 | // 统计出现个数 42 | int[] numbers = new int[3]; 43 | for (int num : nums) 44 | numbers[num] += 1; 45 | int index = 0; 46 | for (int i = 0; i < numbers.length; i++) { 47 | while (numbers[i] != 0) { 48 | nums[index++] = i; 49 | numbers[i] -= 1; 50 | } 51 | } 52 | } 53 | 54 | /** 55 | * 三路快排 0放最前/2放最后/1不变 56 | * 57 | * @param nums 58 | */ 59 | public void sortColors1(int[] nums) { 60 | 61 | int len = nums.length; 62 | if (len < 1) 63 | return; 64 | // left:0放置的下标/right:1放置的下标/index:循环进行中的下标 65 | int left = 0, index = 0, right = len - 1; 66 | while (index <= right) { 67 | // 1 不变继续遍历 68 | if (index < left || nums[index] == 1) 69 | index++; 70 | else if (nums[index] == 0) 71 | swap(nums, left++, index); 72 | else if (nums[index] == 2) 73 | swap(nums, right--, index); 74 | } 75 | } 76 | 77 | /** 78 | * 两两值交换 79 | * 80 | * @param nums 81 | * @param i 82 | * @param j 83 | */ 84 | private void swap(int[] nums, int i, int j) { 85 | if (i == j) 86 | return; 87 | int temp = nums[i]; 88 | nums[i] = nums[j]; 89 | nums[j] = temp; 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/SpiralMatrix.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /**************螺旋矩阵************/ 7 | /** 8 | * 给定一个包含 m x n 个元素的矩阵(m 行, n 列),请按照顺时针螺旋顺序,返回矩阵中的所有元素。 9 | * 10 | * 示例 1: 11 | * 12 | * 输入: [ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ] ] 13 | * 14 | * 输出: [1,2,3,6,9,8,7,4,5] 15 | * 16 | * 示例 2: 17 | * 18 | * 输入: [ [1, 2, 3, 4], [5, 6, 7, 8], [9,10,11,12] ] 19 | * 20 | * 输出: [1,2,3,4,8,12,11,10,9,5,6,7] 21 | * 22 | * @author ffj 23 | * 24 | */ 25 | public class SpiralMatrix { 26 | 27 | public static void main(String[] args) { 28 | int[][] matrix = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } }; 29 | System.out.println(new SpiralMatrix().spiralOrder(matrix)); 30 | } 31 | 32 | public List spiralOrder(int[][] matrix) { 33 | List list = new ArrayList<>(); 34 | if (matrix == null || matrix.length == 0) 35 | return list; 36 | int rows = matrix.length, cols = matrix[0].length; 37 | int up = 0, down = rows - 1, left = 0, right = cols - 1; 38 | 39 | while (up <= down || right >= left) { 40 | if (left > right) // 判断是否越界 41 | break; 42 | // left -> right 43 | for (int i = left; i <= right; i++) 44 | list.add(matrix[up][i]); 45 | right--; 46 | if ((up + 1) > down) 47 | break; 48 | // up -> down 49 | for (int i = up + 1; i <= down; i++) 50 | list.add(matrix[i][right + 1]); 51 | down--; 52 | if (left > right) 53 | break; 54 | // right -> left 55 | for (int i = right; i >= left; i--) 56 | list.add(matrix[down + 1][i]); 57 | left++; 58 | if ((up + 1) > down) 59 | break; 60 | // down -> up 61 | for (int i = down; i >= up + 1; i--) 62 | list.add(matrix[i][left - 1]); 63 | up++; 64 | } 65 | 66 | return list; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/SpiralMatrixII.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /*************螺旋矩阵 II***********/ 4 | /** 5 | * 给定一个正整数 n,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。 6 | * 7 | * 示例: 8 | * 9 | * 输入: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7, 6, 5 ] ] 10 | * 11 | * @author ffj 12 | * 13 | */ 14 | public class SpiralMatrixII { 15 | 16 | public static void main(String[] args) { 17 | int n = 3; 18 | System.out.println(new SpiralMatrixII().generateMatrix(n)); 19 | } 20 | 21 | public int[][] generateMatrix(int n) { 22 | int[][] result = new int[n][n]; 23 | int up = 0, down = n - 1, left = 0, right = n - 1, num = 1; 24 | while (up <= down || right >= left) { 25 | if (left > right) // 判断是否越界 26 | break; 27 | // left -> right 28 | for (int i = left; i <= right; i++) 29 | result[up][i] = num++; 30 | right--; 31 | if ((up + 1) > down) 32 | break; 33 | // up -> down 34 | for (int i = up + 1; i <= down; i++) 35 | result[i][right + 1] = num++; 36 | down--; 37 | if (left > right) // 判断是否越界 38 | break; 39 | // right -> left 40 | for (int i = right; i >= left; i--) 41 | result[down + 1][i] = num++; 42 | left++; 43 | if ((up + 1) > down) 44 | break; 45 | // down -> up 46 | for (int i = down; i >= up + 1; i--) 47 | result[i][left - 1] = num++; 48 | up++; 49 | } 50 | return result; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/SubarrayProductLessThanK.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /************************乘积小于K的子数组*******************/ 4 | /** 5 | * 给定一个正整数数组 nums。 6 | * 7 | * 找出该数组内乘积小于 k 的连续的子数组的个数。 8 | * 9 | * 示例 1: 10 | * 11 | * 输入: nums = [10,5,2,6], k = 100 输出: 8 12 | * 13 | * 解释: 8个乘积小于100的子数组分别为: [10], [5], [2], [6], [10,5], [5,2], [2,6], [5,2,6]。 14 | * 15 | * 需要注意的是 [10,5,2] 并不是乘积小于100的子数组。 16 | * 17 | * 说明: 18 | * 19 | * 0 < nums.length <= 50000 20 | * 21 | * 0 < nums[i] < 1000 22 | * 23 | * 0 <= k < 10^6 24 | * 25 | * @author ffj 26 | * 27 | */ 28 | public class SubarrayProductLessThanK { 29 | 30 | public static void main(String[] args) { 31 | int[] nums = { 10, 5, 2, 6 }; 32 | int k = 100; 33 | int result = numSubarrayProductLessThanK(nums, k); 34 | System.out.println(result); 35 | } 36 | 37 | public static int numSubarrayProductLessThanK(int[] nums, int k) { 38 | if (k <= 1) // 不存在比1还小的正整数 39 | return 0; 40 | int length = nums.length; 41 | int left = 0, result = 0, value = 1; 42 | for (int right = 0; right < length; right++) { 43 | value *= nums[right]; 44 | while (value >= k) 45 | value /= nums[left++]; // 乘积大于K 左下标往右移即除以最左数 46 | result += right - left + 1; // 右下标每往右移一个即新增一个数 满足的新的组数就是区间个数 47 | } 48 | return result; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/SubarraySumEqualsK.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /************和为 K 的子数组**********/ 7 | /** 8 | * 给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。 9 | * 10 | * 示例 1 : 11 | * 12 | * 输入:nums = [1,1,1], k = 2 输出: 2 , [1,1] 与 [1,1] 为两种不同的情况。 13 | * 14 | * 说明 : 15 | * 16 | * 数组的长度为 [1, 20,000]。 17 | * 18 | * 数组中元素的范围是 [-1000, 1000] ,且整数 k 的范围是 [-1e7, 1e7]。 19 | * 20 | * @author ffj 21 | * 22 | */ 23 | public class SubarraySumEqualsK { 24 | 25 | public static void main(String[] args) { 26 | int[] nums = { 1, 1, 1 }; 27 | int k = 2; 28 | int result = new SubarraySumEqualsK().subarraySum(nums, k); 29 | System.out.println("result :" + result); 30 | } 31 | 32 | public int subarraySum(int[] nums, int k) { 33 | int sum = 0, result = 0; 34 | // 存放连续子数组的和 35 | Map preSum = new HashMap<>(); 36 | // 子数组个数为 0 37 | preSum.put(0, 1); 38 | for (int i = 0; i < nums.length; i++) { 39 | sum += nums[i]; 40 | // 满足条件统计 41 | if (preSum.containsKey(sum - k)) { 42 | result += preSum.get(sum - k); 43 | } 44 | // 子数组之和相同也算不同子数组 统计子数组和的个数 45 | preSum.put(sum, preSum.getOrDefault(sum, 0) + 1); 46 | } 47 | return result; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/Subsets.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /*********子集*********/ 7 | /** 8 | * 给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。 9 | * 10 | * 说明:解集不能包含重复的子集。 11 | * 12 | * 示例: 13 | * 14 | * 输入: nums = [1,2,3] 15 | * 16 | * 输出: [ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2], [] ] 17 | * 18 | * @author ffj 19 | * 20 | */ 21 | public class Subsets { 22 | 23 | public static void main(String[] args) { 24 | int[] nums = { 1, 2, 3 }; 25 | List> subsets = new Subsets().subsets(nums); 26 | System.out.println(subsets); 27 | } 28 | 29 | /** 30 | * 遍历一个元素添加到原先的组合中组成新组合 31 | * 32 | * @param nums 33 | * @return 34 | */ 35 | public List> subsets1(int[] nums) { 36 | List> ret = new ArrayList>();// 最后输出的结果 37 | int n = nums.length; 38 | if (n == 0) 39 | return ret; 40 | ret.add(new ArrayList());// 初始化 41 | for (int i : nums) { 42 | List tmp;// 放第二层的list 43 | List> ttmp = new ArrayList<>();// ,第一层list,放之前的list加上新的之后的list 44 | for (List j : ret) { 45 | tmp = new ArrayList<>(j); 46 | tmp.add(i); 47 | ttmp.add(tmp); 48 | } 49 | // 拼合 50 | ret.addAll(ttmp); 51 | } 52 | return ret; 53 | } 54 | 55 | /** 56 | * 回溯法 57 | * 58 | * @param nums 59 | * @return 60 | */ 61 | public List> subsets(int[] nums) { 62 | List> subs = new ArrayList<>(); 63 | int len = nums.length; 64 | for (int i = 0; i <= len; i++) 65 | subsets(subs, new ArrayList<>(), 0, nums, i); 66 | return subs; 67 | } 68 | 69 | private static void subsets(List> subs, List sub, int start, int[] nums, int n) { 70 | if (n == 0) { 71 | subs.add(new ArrayList<>(sub)); 72 | return; 73 | } 74 | for (int i = start; i < nums.length; i++) { 75 | sub.add(nums[i]); 76 | subsets(subs, sub, i + 1, nums, n - 1); 77 | sub.remove(sub.size() - 1); 78 | } 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/Sum3.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | 7 | /****************三数之和****************/ 8 | /** 9 | * 给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 10 | * ?找出所有满足条件且不重复的三元组。 11 | * 12 | * 注意:答案中不可以包含重复的三元组。 13 | * 14 | * 例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4], 15 | * 16 | * 满足要求的三元组集合为: [ [-1, 0, 1], [-1, -1, 2] ] 17 | * 18 | * @author ffj 19 | * 20 | */ 21 | public class Sum3 { 22 | 23 | public List> threeSum(int[] nums) { 24 | 25 | Arrays.sort(nums); // 排序 26 | List> result = new ArrayList>(); 27 | for (int i = 0; i < nums.length - 2; i++) { 28 | if ((i == 0) || (i > 0 && nums[i] != nums[i - 1])) { 29 | int lo = i + 1, hi = nums.length - 1, sum = 0 - nums[i]; 30 | while (lo < hi) { 31 | int n = nums[lo] + nums[hi]; 32 | if (n == sum) { 33 | result.add(Arrays.asList(nums[i], nums[lo], nums[hi])); 34 | while (lo < hi && nums[lo] == nums[lo + 1]) // 跳过相同值的数 35 | lo++; 36 | while (lo < hi && nums[hi] == nums[hi - 1]) 37 | hi--; 38 | lo++; 39 | hi--; 40 | } else if (n < sum) 41 | lo++; 42 | else 43 | hi--; 44 | } 45 | } 46 | } 47 | 48 | return result; 49 | 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/Sum3Closest.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.Arrays; 4 | 5 | /***********************最接近的三数之和*****************/ 6 | /** 7 | * 8 | * 给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 9 | * 最接近。返回这三个数的和。假定每组输入只存在唯一答案。 10 | * 11 | * 例如,给定数组 nums = [-1,2,1,-4], 和 target = 1. 12 | * 13 | * 与 target 最接近的三个数的和为 2. (-1 + 2 + 1 = 2). 14 | * 15 | * @author ffj 16 | * 17 | */ 18 | public class Sum3Closest { 19 | 20 | public static void main(String[] args) { 21 | int[] nums = { 1, 2, 4, 8, 16, 32, 64, 128 }; 22 | int target = 82; 23 | System.out.println(threeSumClosest(nums, target)); 24 | } 25 | 26 | public static int threeSumClosest(int[] nums, int target) { 27 | 28 | if (nums.length < 3) 29 | return 0; 30 | int length = nums.length; 31 | Arrays.sort(nums); 32 | int closeNum = nums[0] + nums[1] + nums[length - 1]; // 初始值 33 | for (int i = 0; i < length - 2; i++) { 34 | int lo = i + 1, hi = length - 1; 35 | while (lo < hi) { 36 | int num = nums[i] + nums[lo] + nums[hi]; 37 | if (num > target) 38 | hi--; 39 | else if (num < target) 40 | lo++; 41 | else 42 | return num; 43 | 44 | if (Math.abs(num - target) < Math.abs(closeNum - target)) 45 | closeNum = num; 46 | } 47 | } 48 | return closeNum; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/SummaryRanges.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /******************汇总区间************/ 7 | /** 8 | * 给定一个无重复元素的有序整数数组,返回数组区间范围的汇总。 9 | * 10 | * 示例 1: 11 | * 12 | * 输入: [0,1,2,4,5,7] 13 | * 14 | * 输出: ["0->2","4->5","7"] 15 | * 16 | * 解释: 0,1,2 可组成一个连续的区间; 4,5 可组成一个连续的区间。 17 | * 18 | * 示例 2: 19 | * 20 | * 输入: [0,2,3,4,6,8,9] 21 | * 22 | * 输出: ["0","2->4","6","8->9"] 23 | * 24 | * 解释: 2,3,4 可组成一个连续的区间; 8,9 可组成一个连续的区间。 25 | * 26 | * @author ffj 27 | * 28 | */ 29 | public class SummaryRanges { 30 | 31 | /** 32 | * 循环分区 开始点和结束点 33 | * 34 | * @param nums 35 | * @return 36 | */ 37 | public List summaryRanges(int[] nums) { 38 | 39 | List list = new ArrayList<>(); 40 | int index = 0; 41 | if (nums.length > 0) { 42 | int len = nums.length, start = 0, end = 0; 43 | while (index < len) { 44 | start = nums[index]; 45 | while (index < len - 1 && nums[index] + 1 == nums[index + 1]) 46 | index++; 47 | end = nums[index]; 48 | if (start != end) { 49 | list.add(start + "->" + end); 50 | } else { 51 | list.add(start + ""); 52 | } 53 | index++; 54 | } 55 | } 56 | return list; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/SurroundedRegions.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /*********被围绕的区域*********/ 4 | /** 5 | * 给定一个二维的矩阵,包含 'X' 和 'O'(字母 O)。 6 | * 7 | * 找到所有被 'X' 围绕的区域,并将这些区域里所有的 'O' 用 'X' 填充。 8 | * 9 | * 示例: 10 | * 11 | * X X X X 12 | * 13 | * X O O X 14 | * 15 | * X X O X 16 | * 17 | * X O X X 18 | * 19 | * 运行你的函数后,矩阵变为: 20 | * 21 | * X X X X 22 | * 23 | * X X X X 24 | * 25 | * X X X X 26 | * 27 | * X O X X 28 | * 29 | * 解释: 30 | * 31 | * 被围绕的区间不会存在于边界上,换句话说,任何边界上的 'O' 都不会被填充为 'X'。 任何不在边界上,或不与边界上的 'O' 相连的 'O' 32 | * 最终都会被填充为 'X'。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。 33 | * 34 | * @author ffj 35 | * 36 | */ 37 | public class SurroundedRegions { 38 | 39 | int rows, cols; 40 | 41 | /** 42 | * 反向思维 不去考虑围住的区域 反过来考虑 不被围住的区域 43 | * 44 | * @param board 45 | */ 46 | public void solve(char[][] board) { 47 | 48 | if (board == null || board.length == 0) 49 | return; 50 | rows = board.length; 51 | cols = board[0].length; 52 | // 遍历边界点 53 | for (int row = 0; row < rows; row++) { 54 | helper(board, row, 0); 55 | helper(board, row, cols - 1); 56 | } 57 | for (int col = 0; col < cols; col++) { 58 | helper(board, 0, col); 59 | helper(board, rows - 1, col); 60 | } 61 | 62 | // 将 'O' 置为 'X', '-' 置为 'O' 63 | for (int row = 0; row < rows; row++) { 64 | for (int col = 0; col < cols; col++) { 65 | char value = board[row][col]; 66 | if (value == 'O') 67 | board[row][col] = 'X'; 68 | else if (value == '-') 69 | board[row][col] = 'O'; 70 | } 71 | } 72 | } 73 | 74 | private void helper(char[][] board, int row, int col) { 75 | if (row < 0 || col < 0 || row >= rows || col >= cols || board[row][col] != 'O') 76 | return; 77 | // 将边界 O 相邻的都置为 - 那剩下的就是都是包围的了 78 | board[row][col] = '-'; 79 | // 周围继续深度遍历 80 | helper(board, row, col + 1); 81 | helper(board, row, col - 1); 82 | helper(board, row + 1, col); 83 | helper(board, row - 1, col); 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/Triangle.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.List; 4 | import java.util.TreeSet; 5 | 6 | /**********************三角形最小路径和*************/ 7 | /** 8 | * 给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。 9 | * 10 | * 例如,给定三角形: 11 | * 12 | * [ [2], [3,4], [6,5,7], [4,1,8,3] ] 13 | * 14 | * 自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。 15 | * 16 | * 说明: 17 | * 18 | * 如果你可以只使用 O(n) 的额外空间(n 为三角形的总行数)来解决这个问题,那么你的算法会很加分。 19 | * 20 | * @author ffj 21 | * 22 | */ 23 | public class Triangle { 24 | 25 | int size = 0; 26 | TreeSet ts = new TreeSet<>(); 27 | List> list; 28 | 29 | /** 30 | * 超时 31 | * 32 | * @param triangle 33 | * @return 34 | */ 35 | public int minimumTotal(List> triangle) { 36 | list = triangle; 37 | size = triangle.size(); 38 | if (size == 0) 39 | return 0; 40 | 41 | helper(0, 0, 0); 42 | return ts.first(); 43 | } 44 | 45 | /** 46 | * 47 | * @param row 48 | * 行数 49 | * @param index 50 | * 在该行中下标 51 | * @param sum_length 52 | * 路径之和 53 | */ 54 | private void helper(int row, int index, int sum_length) { 55 | if (row >= size) { 56 | ts.add(sum_length); 57 | return; 58 | } 59 | sum_length += list.get(row).get(index); 60 | helper(row + 1, index, sum_length); 61 | helper(row + 1, index + 1, sum_length); 62 | } 63 | 64 | /** 65 | * DP 66 | * 67 | * @param triangle 68 | * @return 69 | */ 70 | public int minimumTotal1(List> triangle) { 71 | 72 | // 从倒数第二行开始往上走 73 | for (int i = triangle.size() - 2; i >= 0; i--) { 74 | // 从每行的起始下标开始直到 i 75 | for (int j = 0; j <= i; j++) { 76 | // i 行 j 下标的值 77 | int self = triangle.get(i).get(j); 78 | // 将 i 行 j 下标的值赋为 : i 行 j 下标的值 与 i+1 行 j 下标和 j+1 下标值之和的较小值 79 | triangle.get(i).set(j, 80 | Math.min(triangle.get(i + 1).get(j) + self, triangle.get(i + 1).get(j + 1) + self)); 81 | } 82 | } 83 | // 层层往上 顶层值便是路径最小值 84 | return triangle.get(0).get(0); 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/UniqueBinarySearchTrees.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /*******************不同的二叉搜索树*************/ 4 | /** 5 | * 给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种? 6 | * 7 | * 示例: 8 | * 9 | * 输入: 3 输出: 5 10 | * 11 | * 解释: 给定 n = 3, 一共有 5 种不同结构的二叉搜索树: 12 | * 13 | * @author ffj 14 | * 15 | */ 16 | public class UniqueBinarySearchTrees { 17 | 18 | /** 19 | * DP 20 | * 21 | * @param n 22 | * @return 23 | */ 24 | public int numTrees(int n) { 25 | int[] G = new int[n + 1]; 26 | G[0] = 1; 27 | G[1] = 1; 28 | 29 | for (int i = 2; i <= n; ++i) { 30 | for (int j = 1; j <= i; ++j) { 31 | G[i] += G[j - 1] * G[i - j]; 32 | } 33 | } 34 | return G[n]; 35 | } 36 | 37 | /** 38 | * 公式推理 Catalan number 39 | * 40 | * @param n 41 | * @return 42 | */ 43 | public int numTrees1(int n) { 44 | // Note: we should use long here instead of int, otherwise overflow 45 | long C = 1; 46 | for (int i = 0; i < n; ++i) { 47 | C = C * 2 * (2 * i + 1) / (i + 2); 48 | } 49 | return (int) C; 50 | } 51 | 52 | public static void main(String[] args) { 53 | System.out.println(new UniqueBinarySearchTrees().numTrees(4)); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/UniquePaths.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /***************不同路径************/ 4 | /*** 5 | * 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。 6 | * 7 | * 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。 8 | * 9 | * 问总共有多少条不同的路径? 10 | * 11 | * 例如,上图是一个7 x 3 的网格。有多少可能的路径? 12 | * 13 | * 说明:m 和 n 的值均不超过 100。 14 | * 15 | * 示例 1: 16 | * 17 | * 输入: m = 3, n = 2 输出: 3 18 | * 19 | * 解释: 20 | * 21 | * 从左上角开始,总共有 3 条路径可以到达右下角。 22 | * 23 | * 1. 向右 -> 向右 -> 向下 24 | * 25 | * 2. 向右 -> 向下 -> 向右 26 | * 27 | * 3. 向下 -> 向右 -> 向右 28 | * 29 | * 示例 2: 30 | * 31 | * 输入: m = 7, n = 3 输出: 28 32 | * 33 | * @author ffj 34 | * 35 | */ 36 | public class UniquePaths { 37 | 38 | public static void main(String[] args) { 39 | int result = new UniquePaths().uniquePaths(7, 3); 40 | System.out.println("result: " + result); 41 | } 42 | 43 | /** 44 | * 递归超时 45 | * 46 | * @param m 47 | * @param n 48 | * @return 49 | */ 50 | public int uniquePaths(int m, int n) { 51 | 52 | int sum = 0; 53 | int result = helper(m - 1, n - 1, sum); 54 | return result; 55 | } 56 | 57 | private int helper(int i, int j, int sum) { 58 | if (i == 0 && j == 0) 59 | return ++sum; 60 | if (i > 0) 61 | sum = helper(i - 1, j, sum); 62 | if (j > 0) 63 | sum = helper(i, j - 1, sum); 64 | return sum; 65 | } 66 | 67 | /** 68 | * DP dp[m][n] = dp[m][n - 1]+dp[m - 1][n] 69 | * 70 | * @param m 71 | * @param n 72 | * @return 73 | */ 74 | public int uniquePaths1(int m, int n) { 75 | int[][] dp = new int[m][n]; 76 | for (int i = 0; i < m; i++) { 77 | for (int j = 0; j < n; j++) { 78 | if (i == 0 || j == 0) 79 | dp[i][j] = 1; 80 | else 81 | dp[i][j] = dp[i][j - 1] + dp[i - 1][j]; 82 | } 83 | } 84 | return dp[m - 1][n - 1]; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/UniquePathsII.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /****************不同路径 II***********/ 4 | /** 5 | * 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。 6 | * 7 | * 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。 8 | * 9 | * 现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径? 10 | * 11 | * 网格中的障碍物和空位置分别用 1 和 0 来表示。 12 | * 13 | * 说明:m 和 n 的值均不超过 100。 14 | * 15 | * 示例 1: 16 | * 17 | * 输入: [ [0,0,0], [0,1,0], [0,0,0] ] 18 | * 19 | * 输出: 2 20 | * 21 | * 解释: 22 | * 23 | * 3x3 网格的正中间有一个障碍物。 24 | * 25 | * 从左上角到右下角一共有 2 条不同的路径: 26 | * 27 | * 1. 向右 -> 向右 -> 向下 -> 向下 28 | * 29 | * 2. 向下 -> 向下 -> 向右 -> 向右 30 | * 31 | * @author ffj 32 | * 33 | */ 34 | public class UniquePathsII { 35 | 36 | public static void main(String[] args) { 37 | 38 | } 39 | 40 | public int uniquePathsWithObstacles(int[][] obstacleGrid) { 41 | int m = obstacleGrid.length, n = obstacleGrid[0].length; 42 | // start end 是障碍直接返回0 43 | if (obstacleGrid[0][0] == 1 || obstacleGrid[m - 1][n - 1] == 1) 44 | return 0; 45 | int[][] dp = new int[m][n]; 46 | // 遍历边缘是否有障碍 47 | for (int i = 0; i < m; i++) { 48 | if (obstacleGrid[i][0] == 0) 49 | dp[i][0] = 1; 50 | else 51 | break; 52 | } 53 | for (int j = 0; j < n; j++) { 54 | if (obstacleGrid[0][j] == 0) 55 | dp[0][j] = 1; 56 | else 57 | break; 58 | } 59 | // dp[i][j] = dp[i-1][j] + dp[i][j-1] 60 | for (int i = 1; i < m; i++) { 61 | for (int j = 1; j < n; j++) { 62 | if (obstacleGrid[i][j] == 0) 63 | dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; 64 | } 65 | } 66 | return dp[m - 1][n - 1]; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/ValidSudoku.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | /*********************有效的数独******************/ 7 | /** 8 | * 判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。 9 | * 10 | * 数字 1-9 在每一行只能出现一次。 11 | * 12 | * 数字 1-9 在每一列只能出现一次。 13 | * 14 | * 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。 15 | * 16 | * 数独部分空格内已填入了数字,空白格用 '.' 表示。 17 | * 18 | * 说明: 19 | * 20 | * 一个有效的数独(部分已被填充)不一定是可解的。 21 | * 22 | * 只需要根据以上规则,验证已经填入的数字是否有效即可。 23 | * 24 | * 给定数独序列只包含数字 1-9 和字符 '.' 。 25 | * 26 | * 给定数独永远是 9x9 形式的。 27 | * 28 | * @author ffj 29 | * 30 | */ 31 | public class ValidSudoku { 32 | 33 | // 满足 3*3 方格 34 | private int[][] indexArr = { { 0, 0 }, { 0, 1 }, { 0, 2 }, { 1, 0 }, { 1, 1 }, { 1, 2 }, { 2, 0 }, { 2, 1 }, 35 | { 2, 2 } }; 36 | 37 | public static void main(String[] args) { 38 | } 39 | 40 | public boolean isValidSudoku(char[][] board) { 41 | 42 | // 循环整行和整列 43 | for (int i = 0; i < 9; i++) { 44 | Set hasVisited = new HashSet<>(); 45 | Set hasVisited1 = new HashSet<>(); 46 | for (int j = 0; j < 9; j++) { 47 | char ch = board[i][j]; 48 | char ch1 = board[j][i]; 49 | 50 | if ((ch != '.' && !hasVisited.add(ch)) || (ch1 != '.' && !hasVisited1.add(ch1))) 51 | return false; 52 | } 53 | } 54 | 55 | // 循环 3 * 3 方格 56 | for (int i = 0; i < 9; i += 3) { 57 | for (int j = 0; j < 9; j += 3) { 58 | Set hasVisited = new HashSet<>(); 59 | for (int[] arr : indexArr) { 60 | char ch = board[i + arr[0]][j + arr[1]]; 61 | if (ch != '.' && !hasVisited.add(ch)) 62 | return false; 63 | } 64 | } 65 | } 66 | return true; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/ValidateBinarySearchTree.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.Deque; 4 | import java.util.LinkedList; 5 | 6 | /*************验证二叉搜索树*********/ 7 | /** 8 | * 给定一个二叉树,判断其是否是一个有效的二叉搜索树。 9 | * 10 | * 假设一个二叉搜索树具有如下特征: 11 | * 12 | * 节点的左子树只包含小于当前节点的数。 13 | * 14 | * 节点的右子树只包含大于当前节点的数。 15 | * 16 | * 所有左子树和右子树自身必须也是二叉搜索树。 17 | * 18 | * 示例 1: 19 | * 20 | * 输入: 21 | * 22 | * 2 23 | * 24 | * / \ 25 | * 26 | * 1 3 27 | * 28 | * 输出: true 29 | * 30 | * 示例 2: 31 | * 32 | * 输入: 33 | * 34 | * 5 35 | * 36 | * / \ 37 | * 38 | * 1 4 39 | * 40 | *   / \ 41 | * 42 | *   3 6 43 | * 44 | * 输出: false 45 | * 46 | * 解释: 47 | * 48 | * 输入为: [5,1,4,null,null,3,6]。 49 | * 50 | * 根节点的值为 5 ,但是其右子节点值为 4 。 51 | * 52 | * @author ffj 53 | * 54 | */ 55 | public class ValidateBinarySearchTree { 56 | 57 | public class TreeNode { 58 | int val; 59 | TreeNode left; 60 | TreeNode right; 61 | 62 | TreeNode(int x) { 63 | val = x; 64 | } 65 | } 66 | 67 | public boolean isValidBST(TreeNode root) { 68 | 69 | Deque stack = new LinkedList<>(); 70 | TreeNode p = root; 71 | TreeNode pre = null; 72 | // 中序遍历 73 | while (p != null || !stack.isEmpty()) { 74 | while (p != null) { 75 | stack.push(p); 76 | p = p.left; 77 | } 78 | p = stack.pop(); 79 | // 如不满足则 false 前一节点与该节点作比较 80 | if (pre != null && pre.val >= p.val) 81 | return false; 82 | pre = p; 83 | p = p.right; 84 | } 85 | return true; 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/WordBreak.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | /***********************单词拆分****************/ 7 | /** 8 | * 给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。 9 | * 10 | * 说明: 11 | * 12 | * 拆分时可以重复使用字典中的单词。 你可以假设字典中没有重复的单词。 示例 1: 13 | * 14 | * 输入: s = "leetcode", wordDict = ["leet", "code"] 输出: true 15 | * 16 | * 解释: 返回 true 因为 "leetcode" 可以被拆分成 "leet code"。 17 | * 18 | * 示例 2: 19 | * 20 | * 输入: s = "applepenapple", wordDict = ["apple", "pen"] 输出: true 21 | * 22 | * 解释: 返回 true 因为 "applepenapple" 可以被拆分成 "apple pen apple"。 注意你可以重复使用字典中的单词。 23 | * 24 | * 示例 3: 25 | * 26 | * 输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"] 输出: 27 | * false 28 | * 29 | * @author ffj 30 | * 31 | */ 32 | public class WordBreak { 33 | 34 | public static void main(String[] args) { 35 | String s = "abcd"; 36 | List wordDict = Arrays.asList("a", "abc", "b", "cd"); 37 | System.out.println(wordBreak(s, wordDict)); 38 | } 39 | 40 | static boolean bol = false; 41 | 42 | public static boolean wordBreak(String s, List wordDict) { 43 | 44 | if (s.isEmpty()) { 45 | bol = true; 46 | return bol; 47 | } 48 | for (String str : wordDict) { 49 | if (s.startsWith(str)) { 50 | wordBreak(s.substring(str.length()), wordDict); 51 | } 52 | } 53 | return bol; 54 | } 55 | 56 | /** 57 | * 讨论中解法 DP 动态规划 58 | * 59 | * @param s 60 | * @param wordDict 61 | * @return 62 | */ 63 | public static boolean wordBreak1(String s, List wordDict) { 64 | boolean[] dp = new boolean[s.length() + 1]; 65 | dp[0] = true; 66 | 67 | for (int i = 1; i <= s.length(); i++) { 68 | for (int j = 0; j < i; j++) { // 为true说明之前的是可以匹配到的 69 | if (dp[j] && wordDict.contains(s.substring(j, i))) { 70 | dp[i] = true; 71 | break; 72 | } 73 | } 74 | } 75 | return dp[s.length()]; 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/com/leetcode_cn/medium/WordSearch.java: -------------------------------------------------------------------------------- 1 | package com.leetcode_cn.medium; 2 | 3 | /****************************单词搜索******************/ 4 | /** 5 | * 给定一个二维网格和一个单词,找出该单词是否存在于网格中。 6 | * 7 | * 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。 8 | * 9 | * 示例: 10 | * 11 | * board = [ ['A','B','C','E'], ['S','F','C','S'], ['A','D','E','E'] ] 12 | * 13 | * 给定 word = "ABCCED", 返回 true. 14 | * 15 | * 给定 word = "SEE", 返回 true. 16 | * 17 | * 给定 word = "ABCB", 返回 false. 18 | * 19 | * @author ffj 20 | * 21 | */ 22 | public class WordSearch { 23 | 24 | /** 25 | * DFS 26 | * 27 | * @param board 28 | * @param word 29 | * @return 30 | */ 31 | public boolean exist(char[][] board, String word) { 32 | char[] w = word.toCharArray(); 33 | for (int y = 0; y < board.length; y++) { 34 | for (int x = 0; x < board[y].length; x++) { 35 | if (exist(board, y, x, w, 0)) 36 | return true; 37 | } 38 | } 39 | return false; 40 | } 41 | 42 | /** 43 | * 递归 44 | * 45 | * @param board 46 | * @param y 47 | * @param x 48 | * @param word 49 | * @param i 50 | * @return 51 | */ 52 | private boolean exist(char[][] board, int y, int x, char[] word, int i) { 53 | if (i == word.length) 54 | return true; 55 | if (y < 0 || x < 0 || y == board.length || x == board[y].length) 56 | return false; 57 | if (board[y][x] != word[i]) 58 | return false; 59 | board[y][x] ^= 256; 60 | boolean exist = exist(board, y, x + 1, word, i + 1) || exist(board, y, x - 1, word, i + 1) 61 | || exist(board, y + 1, x, word, i + 1) || exist(board, y - 1, x, word, i + 1); 62 | board[y][x] ^= 256; 63 | return exist; 64 | } 65 | } 66 | --------------------------------------------------------------------------------