├── .gitignore ├── README.md ├── SwordToOffer ├── countDigitOne.java ├── countDigitOne.md ├── exist.java ├── exist.md ├── fib.java ├── fib.md ├── findNthDigit.java ├── findNthDigit.md ├── findNumberIn2DArray.java ├── findNumberIn2DArray.md ├── findRepeatNumber.java ├── findRepeatNumber.md ├── firstUniqChar.java ├── firstUniqChar.md ├── getIntersectionNode.java ├── getIntersectionNode.md ├── isStraight.java ├── isStraight.md ├── lowestCommonAncestor.java ├── lowestCommonAncestor.md ├── lowestCommonAncestor2.java ├── lowestCommonAncestor2.md ├── replaceSpace.java ├── replaceSpace.md ├── reverseLeftWords.java ├── reverseLeftWords.md ├── reversePrint.java └── reversePrint.md ├── WeekContest ├── CustomStack.java ├── CustomStack.md ├── UndergroundSystem.java ├── UndergroundSystem.md ├── balanceBST.java ├── balanceBST.md ├── buildArray.java ├── buildArray.md ├── countTriplets.java ├── countTriplets.md ├── createTargetArray.java ├── createTargetArray.md ├── destCity.java ├── destCity.md ├── displayTable.java ├── displayTable.md ├── entityParser.java ├── entityParser.md ├── findDiagonalOrder2.java ├── findDiagonalOrder2.md ├── findLucky.java ├── findLucky.md ├── frogPosition.java ├── frogPosition.md ├── generateTheString.java ├── generateTheString.md ├── getTriggerTime.java ├── getTriggerTime.md ├── kLengthApart.java ├── kLengthApart.md ├── longestSubarray.java ├── longestSubarray.md ├── luckyNumbers.java ├── luckyNumbers.md ├── maxScore.java ├── maxScore.md ├── maxScore2.java ├── maxScore2.md ├── minCount.java ├── minCount.md ├── minNumberOfFrogs.java ├── minNumberOfFrogs.md ├── minSubsequence.java ├── minSubsequence.md ├── minTime.java ├── minTime.md ├── numOfMinutes.java ├── numOfMinutes.md ├── numSteps.java ├── numSteps.md ├── numTeams.java ├── numTeams.md ├── numTimesAllBlue.java ├── numTimesAllBlue.md ├── numWays.java ├── numWays.md ├── processQueries.java ├── processQueries.md ├── reformat.java ├── reformat.md ├── stringMatching.java ├── stringMatching.md ├── sumFourDivisors.java └── sumFourDivisors.md ├── easy ├── ArrayAndMatrix │ ├── canThreePartsEqualSum.java │ ├── canThreePartsEqualSum.md │ ├── containsDuplicate.java │ ├── containsDuplicate.md │ ├── distributeCandies.java │ ├── distributeCandies.md │ ├── findErrorNums.java │ ├── findErrorNums645.md │ ├── findLHS.java │ ├── findLHS.jpg │ ├── findLHS.md │ ├── findMaxConsecutiveOnes.java │ ├── findMaxConsecutiveOnes485.md │ ├── findShortestSubArray.java │ ├── findShortestSubArray.md │ ├── hasGroupsSizeX.java │ ├── hasGroupsSizeX.md │ ├── isToeplitzMatrix.java │ ├── isToeplitzMatrix.md │ ├── matrixReshape.java │ ├── matrixReshape.md │ ├── merge88.java │ ├── merge88.md │ ├── moveZeroes.java │ ├── moveZeroes.md │ ├── removeDuplicates.java │ ├── removeDuplicates.md │ ├── removeDuplicates图示.png │ ├── removeElement.java │ ├── removeElement.md │ ├── rotate.java │ ├── rotate.md │ ├── surfaceArea.java │ ├── surfaceArea.md │ └── surfaceArea图示.png ├── BinarySearch │ ├── findTheDistanceValue.java │ ├── findTheDistanceValue.md │ ├── findTheDistanceValue图示.png │ ├── mySqrt.java │ ├── mySqrt.md │ ├── mySqrt图示.png │ ├── nextGreatestLetter.java │ ├── nextGreatestLetter.md │ ├── searchInsert.java │ ├── searchInsert.md │ └── searchInsert图示.png ├── BitOperation │ ├── findComplement.java │ ├── findComplement.md │ ├── getSum.java │ ├── getSum.md │ ├── hammingDistance.java │ ├── hammingDistance.md │ ├── hammingWeight.java │ ├── hammingWeight.md │ ├── hasAlternatingBits.java │ ├── hasAlternatingBits.md │ ├── isPowerOfFour.java │ ├── isPowerOfFour.md │ ├── isPowerOfTwo.java │ ├── isPowerOfTwo.md │ ├── missingNumber.java │ ├── missingNumber.md │ ├── reverseBits.java │ ├── reverseBits.md │ ├── singleNumber.java │ └── singleNumber.md ├── DP │ ├── climbStairs.java │ ├── climbStairs.md │ ├── massage.java │ ├── massage.md │ ├── maxSubArray.java │ ├── maxSubArray.md │ ├── rob198.java │ └── rob198.md ├── LinkedList │ ├── ListNode.java │ ├── deleteDuplicates.java │ ├── deleteDuplicates.jpg │ ├── deleteDuplicates.md │ ├── deleteNode.java │ ├── deleteNode.md │ ├── getIntersectionNode.java │ ├── getIntersectionNode.md │ ├── hasCycle.java │ ├── hasCycle.md │ ├── isPalindrome.java │ ├── isPalindrome.md │ ├── isPalindrome1.jpg │ ├── isPalindrome2.jpg │ ├── mergeTwoLists.java │ ├── mergeTwoLists.jpg │ ├── mergeTwoLists.md │ ├── middleNode.java │ ├── middleNode.jpg │ ├── middleNode.md │ ├── removeElements.java │ ├── removeElements.md │ ├── reverseList.java │ ├── reverseList.jpg │ └── reverseList.md ├── Sort │ ├── getLeastNumbers.java │ ├── getLeastNumbers.md │ ├── intersect350.java │ ├── intersect350.md │ ├── intersection349.java │ ├── intersection349.md │ ├── majorityElement.java │ └── majorityElement.md ├── StackAndQueue │ ├── MinStack155.java │ ├── MinStack155.md │ ├── MyQueue232.java │ ├── MyQueue232.jpg │ ├── MyQueue232.md │ ├── MyStack225.1.jpg │ ├── MyStack225.2.jpg │ ├── MyStack225.java │ ├── MyStack225.md │ ├── getLeastNumbers.java │ ├── getLeastNumbers.md │ ├── isValid20.java │ └── isValid20.md ├── String │ ├── canConstruct.java │ ├── canConstruct.md │ ├── compressString.java │ ├── compressString.md │ ├── countBinarySubstrings.java │ ├── countBinarySubstrings.md │ ├── countCharacters.java │ ├── countCharacters.md │ ├── gcdOfStrings.java │ ├── gcdOfStrings.md │ ├── isAnagram.java │ ├── isAnagram.md │ ├── isIsomorphic.java │ ├── isIsomorphic.md │ ├── isPalindrome.java │ ├── isPalindrome.md │ ├── lengthOfLastWord.java │ ├── lengthOfLastWord.md │ ├── longestPalindrome.java │ ├── longestPalindrome.md │ ├── sortString.java │ └── sortString.md ├── Tree │ ├── averageOfLevels.java │ ├── averageOfLevels.md │ ├── binaryTreePaths.java │ ├── binaryTreePaths.md │ ├── convertBST.java │ ├── convertBST.md │ ├── diameterOfBinaryTree543.java │ ├── diameterOfBinaryTree543.md │ ├── findMode.java │ ├── findMode.md │ ├── findSecondMinimumValue.java │ ├── findSecondMinimumValue.md │ ├── findTarget.java │ ├── findTarget.md │ ├── hasPathSum112.java │ ├── hasPathSum112.md │ ├── invertTree.java │ ├── invertTree.md │ ├── isBalanced110.java │ ├── isBalanced110.md │ ├── isSameTree100.java │ ├── isSameTree100.md │ ├── isSubtree.java │ ├── isSubtree.md │ ├── isSymmetric.java │ ├── isSymmetric.md │ ├── longestUnivaluePath.java │ ├── longestUnivaluePath.md │ ├── lowestCommonAncestor.java │ ├── lowestCommonAncestor.md │ ├── maxDepth106.java │ ├── maxDepth106.md │ ├── mergeTrees.java │ ├── mergeTrees.md │ ├── minDepth.java │ ├── minDepth.md │ ├── pathSum437.java │ ├── pathSum437.md │ ├── sortedArrayToBST.java │ ├── sortedArrayToBST.md │ ├── sumOfLeftLeaves404.java │ ├── sumOfLeftLeaves404.md │ ├── trimBST.java │ └── trimBST.md ├── TwoPoint │ ├── findContinuousSequence.java │ ├── findContinuousSequence.md │ ├── isHappy.java │ ├── isHappy.md │ ├── reverseVowels.java │ ├── reverseVowels.md │ ├── twoSum167.java │ ├── twoSum167.md │ ├── validPalindrome.java │ └── validPalindrome.md └── other │ ├── TwoSum.java │ ├── TwoSum.md │ ├── isRectangleOverlap.java │ ├── isRectangleOverlap.md │ ├── isRectangleOverlap图示.png │ ├── judgeSquareSum633.java │ ├── judgeSquareSum633.md │ ├── numRookCaptures.java │ ├── numRookCaptures.md │ ├── orangesRotting.java │ └── orangesRotting.md ├── hard └── Greedy │ ├── jump.java │ ├── jump.md │ └── jump图示.png └── medium ├── ArrayAndMatrix ├── arrayNesting.java ├── arrayNesting565.md ├── checkSubarraySum.java ├── checkSubarraySum.md ├── constructArray.java ├── constructArray.md ├── kthSmallest.java ├── kthSmallest.md ├── kthSmallest图示.png ├── longestWPI.java ├── longestWPI.md ├── maxChunksToSorted.java ├── maxChunksToSorted.md ├── maxChunksToSorted图示.png ├── nextPermutation.java ├── nextPermutation.md ├── searchMatrix.java ├── searchMatrix.md └── searchMatrix图示.png ├── BFSorDFS ├── findCircleNum.java ├── findCircleNum.md ├── ladderLength.java ├── ladderLength.md ├── ladderLength.png ├── maxDistance.java ├── maxDistance.md ├── pacificAtlantic.java ├── pacificAtlantic.md ├── shortestPathBinaryMatrix.java ├── shortestPathBinaryMatrix.md ├── updateMatrix.java └── updateMatrix.md ├── BackTracking ├── combinationSum.java ├── combinationSum.md ├── combinationSum2.java ├── combinationSum2.md ├── combinationSum3.java ├── combinationSum3.md ├── combine77.java ├── combine77.md ├── exist.java ├── exist.md ├── findSubsequences491.java ├── findSubsequences491.md ├── generateParenthesis.java ├── generateParenthesis.md ├── letterCombinations17.java ├── letterCombinations17.md ├── maxAreaOfIsland.java ├── maxAreaOfIsland.md ├── numIslands.java ├── numIslands.md ├── partition131.java ├── partition131.md ├── permute46.java ├── permute46.md ├── permuteUnique47.java ├── permuteUnique47.md ├── restoreIpAddresses.java ├── restoreIpAddresses.md ├── solve130.java ├── solve130.md ├── subsets78.java ├── subsets78.md ├── subsetsWithDup90.java └── subsetsWithDup90.md ├── BinarySearch ├── findDuplicate.java ├── findDuplicate.md ├── singleNonDuplicate.java └── singleNonDuplicate.md ├── BitOperation ├── singleNumber260.java └── singleNumber260.md ├── DP ├── maxProduct.java ├── maxProduct.md ├── minPathSum64.java ├── minPathSum64.md ├── rob213.java ├── rob213.md ├── rob337.java ├── rob337.md ├── uniquePaths62.java ├── uniquePaths62.md ├── uniquePathsWithObstacles.md └── uniquePathsWithObstacles63.java ├── LinkedList ├── addTwoNumbers.java ├── addTwoNumbers.md ├── deleteDuplicates82.java ├── deleteDuplicates82.jpg ├── deleteDuplicates82.md ├── oddEvenList.java ├── oddEvenList.jpg ├── oddEvenList.md ├── removeNthFromEnd.java ├── removeNthFromEnd.md ├── rotateRight.java ├── rotateRight.md ├── rotateRight图示.png ├── splitListToParts.java ├── splitListToParts.jpg ├── splitListToParts.md ├── swapPairs.java ├── swapPairs.md └── swapPairs图示.png ├── Sort ├── findKthLargest.java ├── findKthLargest.md ├── frequencySort.java ├── frequencySort.md ├── sortColors.java ├── sortColors.md ├── topKFrequent.java └── topKFrequent.md ├── StackAndQueue ├── dailyTemperatures739.java ├── dailyTemperatures739.md ├── nextGreaterElements.java └── nextGreaterElements503.md ├── String ├── countSubstrings647.java ├── countSubstrings647.md ├── longestPalindrome5.java ├── longestPalindrome5.md └── longestPalindrome5图示.png ├── Tree ├── generateTrees95.java ├── generateTrees95.md ├── inorderTraversal.java ├── inorderTraversal.md ├── kthSmallest.java ├── kthSmallest.md ├── lowestCommonAncestor.java ├── lowestCommonAncestor.md ├── numTrees96.java ├── numTrees96.md ├── pathSum113.java └── pathSum113.md └── TwoPoint ├── maxArea.md └── minSubArrayLen.java /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | -------------------------------------------------------------------------------- /SwordToOffer/countDigitOne.java: -------------------------------------------------------------------------------- 1 | package SwordToOffer; 2 | 3 | /** 4 | * @Time : 2020年5月5日20:11:25 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 已总结 9 | */ 10 | 11 | /** 12 | * 输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数。 13 | * 例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。 14 | *

15 | * 示例 1: 16 | * 输入:n = 12 17 | * 输出:5 18 | *

19 | * 示例 2: 20 | * 输入:n = 13 21 | * 输出:6 22 | *   23 | *

24 | * 限制: 25 | * 1 <= n < 2^31 26 | *

27 | * 来源:力扣(LeetCode) 28 | * 链接:https://leetcode-cn.com/problems/1nzheng-shu-zhong-1chu-xian-de-ci-shu-lcof 29 | */ 30 | public class countDigitOne { 31 | private static int[] count = {0, 1, 20, 300, 4000, 50000, 600000, 7000000, 80000000, 900000000}; 32 | 33 | /** 34 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 35 | * 内存消耗 :36.6 MB, 在所有 Java 提交中击败了100.00%的用户 36 | */ 37 | public int countDigitOne(int n) { 38 | int res = 0, index = 0, pow = 1, pre = 0; 39 | while (n != 0) { 40 | int num = n % 10; 41 | if (num == 1) { 42 | res += pre + 1 + count[index]; 43 | } else if (num > 1) { 44 | res += pow + num * count[index]; 45 | } 46 | pre = pre + num * pow; 47 | pow *= 10; 48 | index++; 49 | n /= 10; 50 | } 51 | return res; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /SwordToOffer/countDigitOne.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/SwordToOffer/countDigitOne.md -------------------------------------------------------------------------------- /SwordToOffer/exist.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/SwordToOffer/exist.md -------------------------------------------------------------------------------- /SwordToOffer/fib.java: -------------------------------------------------------------------------------- 1 | package SwordToOffer; 2 | 3 | /** 4 | * @Time : 2020年4月14日11:22:35 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | /** 12 | * 写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项。斐波那契数列的定义如下: 13 | * F(0) = 0,   F(1) = 1 14 | * F(N) = F(N - 1) + F(N - 2), 其中 N > 1. 15 | * 斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。 16 | * 答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。 17 | * 18 | * 示例 1: 19 | * 输入:n = 2 20 | * 输出:1 21 | * 22 | * 示例 2: 23 | * 输入:n = 5 24 | * 输出:5 25 | * 26 | * 来源:力扣(LeetCode) 27 | * 链接:https://leetcode-cn.com/problems/fei-bo-na-qi-shu-lie-lcof 28 | */ 29 | public class fib { 30 | /** 31 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 32 | * 内存消耗 :36.5 MB, 在所有 Java 提交中击败了100.00%的用户 33 | */ 34 | public int fib(int n) { 35 | if(n == 0) 36 | return 0; 37 | int[] result = new int[n + 1]; 38 | result[1] = 1; 39 | for(int i = 2; i <= n; i++) 40 | result[i] = (result[i - 2] + result[i - 1]) % 1000000007; 41 | return result[n]; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /SwordToOffer/fib.md: -------------------------------------------------------------------------------- 1 | # 面试题10- I. 斐波那契数列 2 | 3 | ### 原题 4 | 写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项。斐波那契数列的定义如下: 5 | F(0) = 0,   F(1) = 1 6 | F(N) = F(N - 1) + F(N - 2), 其中 N > 1. 7 | 斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。 8 | 9 | 答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。 10 | 11 | 示例 1: 12 | 输入:n = 2 13 | 输出:1 14 | 15 | 示例 2: 16 | 输入:n = 5 17 | 输出:5 18 | 19 | 提示: 20 | 0 <= n <= 100 21 | 注意:本题与主站 509 题相同:https://leetcode-cn.com/problems/fibonacci-number/ 22 | 23 | 来源:力扣(LeetCode) 24 | [链接](https://leetcode-cn.com/problems/fei-bo-na-qi-shu-lie-lcof):https://leetcode-cn.com/problems/fei-bo-na-qi-shu-lie-lcof 25 | 26 | ### 解法:DP 27 | 28 | ```java 29 | public int fib(int n) { 30 | if(n == 0) 31 | return 0; 32 | int[] result = new int[n + 1]; 33 | result[1] = 1; 34 | for(int i = 2; i <= n; i++) 35 | result[i] = (result[i - 2] + result[i - 1]) % 1000000007; 36 | return result[n]; 37 | } 38 | ``` 39 | 40 | 思路分析: 41 | 42 | * 初学时候肯定都把斐波那契数列用递归来做,但是递归中有很多重复计算。 43 | * 解决重复计算的问题一种是利用记忆化搜索,将已经计算过的结果保存起来,下次需要使用直接拿出来。 44 | * 另外一种方式就是利用动态规划,设定边界条件,通过转移方程进行递推。本题中状态转移方程都已经写出来了,边界条件也给出了,所以直接动态规划即可。 45 | * 这个题还要注意取模,斐波那契数列可以看做很多数累加,对最后累加的最后结果取余,与在相加过程中不断取余用余数相加是一样的。这里如果不在过程中取余,会导致超出32位而出错。 46 | * 时间复杂度为$O(n)$,空间复杂度为$O(n)$。 47 | 48 | 运行结果: 49 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 50 | -------------------------------------------------------------------------------- /SwordToOffer/findNthDigit.java: -------------------------------------------------------------------------------- 1 | package SwordToOffer; 2 | 3 | /** 4 | * @Time : 2020年5月6日09:55:42 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 已总结 9 | */ 10 | 11 | /** 12 | * 数字以0123456789101112131415…的格式序列化到一个字符序列中。 13 | * 在这个序列中,第5位(从下标0开始计数)是5,第13位是1,第19位是4,等等。 14 | * 请写一个函数,求任意第n位对应的数字。 15 | *

16 | * 示例 1: 17 | * 输入:n = 3 18 | * 输出:3 19 | *

20 | * 示例 2: 21 | * 输入:n = 11 22 | * 输出:0 23 | *

24 | * 限制: 25 | * 0 <= n < 2^31 26 | *

27 | * 来源:力扣(LeetCode) 28 | * 链接:https://leetcode-cn.com/problems/shu-zi-xu-lie-zhong-mou-yi-wei-de-shu-zi-lcof 29 | */ 30 | public class findNthDigit { 31 | // 0, 一位数,两位数,三位数……的累计数字的和 32 | private static int[] sum = {10, 190, 2890, 38890, 488890, 5888890, 68888890, 788888890}; 33 | 34 | /** 35 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 36 | * 内存消耗 :35.8 MB, 在所有 Java 提交中击败了100.00%的用户 37 | */ 38 | public int findNthDigit(int n) { 39 | if (n <= 9) return n; 40 | int i = 0; 41 | for (; i < sum.length && n >= sum[i]; i++) ; 42 | int left = n - sum[i - 1]; 43 | int index = left / (i + 1), offset = left % (i + 1); 44 | String num = String.valueOf((int) Math.pow(10, i) + index); 45 | return num.charAt(offset) - '0'; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /SwordToOffer/findNthDigit.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/SwordToOffer/findNthDigit.md -------------------------------------------------------------------------------- /SwordToOffer/findNumberIn2DArray.java: -------------------------------------------------------------------------------- 1 | package SwordToOffer; 2 | 3 | /** 4 | * @Time : 2020年4月14日00:23:40 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 已总结 9 | */ 10 | 11 | /** 12 | * 在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。 13 | * 请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。 14 | *

15 | * 示例: 16 | * 现有矩阵 matrix 如下: 17 | * [ 18 | * [1, 4, 7, 11, 15], 19 | * [2, 5, 8, 12, 19], 20 | * [3, 6, 9, 16, 22], 21 | * [10, 13, 14, 17, 24], 22 | * [18, 21, 23, 26, 30] 23 | * ] 24 | * 给定 target = 5,返回 true。 25 | * 给定 target = 20,返回 false。  26 | *

27 | * 限制: 28 | * 0 <= n <= 1000 29 | * 0 <= m <= 1000 30 | *

31 | * 注意:本题与主站 240 题相同:https://leetcode-cn.com/problems/search-a-2d-matrix-ii/ 32 | *

33 | * 来源:力扣(LeetCode) 34 | * 链接:https://leetcode-cn.com/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof 35 | */ 36 | public class findNumberIn2DArray { 37 | /** 38 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 39 | * 内存消耗 :45.4 MB, 在所有 Java 提交中击败了100.00%的用户 40 | */ 41 | public boolean findNumberIn2DArray(int[][] matrix, int target) { 42 | int row = matrix.length; 43 | if (row == 0) 44 | return false; 45 | int col = matrix[0].length; 46 | if (col == 0) 47 | return false; 48 | int i = row - 1, j = 0; 49 | while (i > -1 && j < col) { 50 | if (target > matrix[i][j]) // 右移 51 | j++; 52 | else if (target < matrix[i][j]) 53 | i--; 54 | else return true; 55 | } 56 | return false; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /SwordToOffer/findNumberIn2DArray.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/SwordToOffer/findNumberIn2DArray.md -------------------------------------------------------------------------------- /SwordToOffer/findRepeatNumber.java: -------------------------------------------------------------------------------- 1 | package SwordToOffer; 2 | 3 | /** 4 | * @Time : 2020年4月14日00:19:19 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | /** 12 | * 找出数组中重复的数字。 13 | * 在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。 14 | * 数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。 15 | * 16 | * 示例 1: 17 | * 输入: 18 | * [2, 3, 1, 0, 2, 5, 3] 19 | * 输出:2 或 3 20 | * 21 | * 限制: 22 | * 2 <= n <= 100000 23 | * 24 | * 来源:力扣(LeetCode) 25 | * 链接:https://leetcode-cn.com/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof 26 | */ 27 | public class findRepeatNumber { 28 | /** 29 | * 执行用时 :1 ms, 在所有 Java 提交中击败了92.46%的用户 30 | * 内存消耗 :47.4 MB, 在所有 Java 提交中击败了100.00%的用 31 | */ 32 | public int findRepeatNumber(int[] nums) { 33 | int[] count = new int[nums.length]; 34 | for(int i : nums){ 35 | if(count[i] == 1) 36 | return i; 37 | count[i] = 1; 38 | } 39 | return -1; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /SwordToOffer/findRepeatNumber.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/SwordToOffer/findRepeatNumber.md -------------------------------------------------------------------------------- /SwordToOffer/firstUniqChar.java: -------------------------------------------------------------------------------- 1 | package SwordToOffer; 2 | 3 | /** 4 | * @Time : 2020年5月6日16:39:56 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 已总结 9 | */ 10 | 11 | /** 12 | * 在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 13 | * 14 | * 示例: 15 | * s = "abaccdeff" 16 | * 返回 "b" 17 | * 18 | * s = "" 19 | * 返回 " " 20 | * 21 | * 限制: 22 | * 0 <= s 的长度 <= 50000 23 | * 24 | * 来源:力扣(LeetCode) 25 | * 链接:https://leetcode-cn.com/problems/di-yi-ge-zhi-chu-xian-yi-ci-de-zi-fu-lcof 26 | */ 27 | public class firstUniqChar { 28 | /** 29 | * 执行用时 :4 ms, 在所有 Java 提交中击败了99.92%的用户 30 | * 内存消耗 :39.7 MB, 在所有 Java 提交中击败了100.00%的用户 31 | */ 32 | public char firstUniqChar(String s) { 33 | int[] count = new int[256]; 34 | char[] chars = s.toCharArray(); 35 | for(char c : chars) 36 | count[c]++; 37 | for(char c : chars){ 38 | if(count[c] == 1) 39 | return c; 40 | } 41 | return ' '; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /SwordToOffer/firstUniqChar.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/SwordToOffer/firstUniqChar.md -------------------------------------------------------------------------------- /SwordToOffer/isStraight.java: -------------------------------------------------------------------------------- 1 | package SwordToOffer; 2 | 3 | /** 4 | * @Time : 2020年5月9日16:32:55 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | import java.util.Arrays; 12 | 13 | /** 14 | * 从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。 15 | * 2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任意数字。A 不能视为 14。 16 | *

17 | * 示例 1: 18 | * 输入: [1,2,3,4,5] 19 | * 输出: True 20 | *

21 | * 示例 2: 22 | * 输入: [0,0,1,2,5] 23 | * 输出: True 24 | *

25 | * 限制: 26 | * 数组长度为 5  27 | * 数组的数取值为 [0, 13] . 28 | *

29 | * 来源:力扣(LeetCode) 30 | * 链接:https://leetcode-cn.com/problems/bu-ke-pai-zhong-de-shun-zi-lcof 31 | */ 32 | public class isStraight { 33 | /** 34 | * 执行用时 :1 ms, 在所有 Java 提交中击败了84.84%的用户 35 | * 内存消耗 :37.5 MB, 在所有 Java 提交中击败了100.00%的用户 36 | */ 37 | public boolean isStraight(int[] nums) { 38 | Arrays.sort(nums); 39 | int countZero = 0, i = 0, gap = 0; 40 | for (; i < 5 && nums[i] == 0; i++) 41 | countZero++; 42 | for (i = i + 1; i < 5; i++) { 43 | if (nums[i] == nums[i - 1]) 44 | return false; 45 | if (nums[i] - nums[i - 1] > 1) 46 | gap += nums[i] - nums[i - 1] - 1; 47 | } 48 | return countZero >= gap; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /SwordToOffer/isStraight.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/SwordToOffer/isStraight.md -------------------------------------------------------------------------------- /SwordToOffer/lowestCommonAncestor.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/SwordToOffer/lowestCommonAncestor.md -------------------------------------------------------------------------------- /SwordToOffer/lowestCommonAncestor2.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/SwordToOffer/lowestCommonAncestor2.md -------------------------------------------------------------------------------- /SwordToOffer/replaceSpace.java: -------------------------------------------------------------------------------- 1 | package SwordToOffer; 2 | 3 | /** 4 | * @Time : 2020年4月14日00:33:00 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 已总结 9 | */ 10 | 11 | /** 12 | * 请实现一个函数,把字符串 s 中的每个空格替换成"%20"。 13 | * 14 | * 示例 1: 15 | * 输入:s = "We are happy." 16 | * 输出:"We%20are%20happy." 17 | * 18 | * 限制: 19 | * 0 <= s 的长度 <= 10000 20 | * 21 | * 来源:力扣(LeetCode) 22 | * 链接:https://leetcode-cn.com/problems/ti-huan-kong-ge-lcof 23 | */ 24 | public class replaceSpace { 25 | public String replaceSpace(String s) { 26 | return s.replace(" ", "%20"); 27 | } 28 | 29 | /** 30 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 31 | * 内存消耗 :37.2 MB, 在所有 Java 提交中击败了100.00%的用户 32 | */ 33 | public String replaceSpace2(String s) { 34 | StringBuilder res = new StringBuilder(); 35 | for(char c : s.toCharArray()){ 36 | if(c == ' ') 37 | res.append("%20"); 38 | else 39 | res.append(c); 40 | } 41 | return res.toString(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /SwordToOffer/replaceSpace.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/SwordToOffer/replaceSpace.md -------------------------------------------------------------------------------- /SwordToOffer/reverseLeftWords.java: -------------------------------------------------------------------------------- 1 | package SwordToOffer; 2 | 3 | /** 4 | * @Time : 2020年5月9日10:51:00 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | /** 12 | * 字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。 13 | * 请定义一个函数实现字符串左旋转操作的功能。 14 | * 比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。 15 | * 16 | * 示例 1: 17 | * 输入: s = "abcdefg", k = 2 18 | * 输出: "cdefgab" 19 | * 20 | * 示例 2: 21 | * 输入: s = "lrloseumgh", k = 6 22 | * 输出: "umghlrlose"  23 | * 24 | * 限制: 25 | * 1 <= k < s.length <= 10000 26 | * 27 | * 来源:力扣(LeetCode) 28 | * 链接:https://leetcode-cn.com/problems/zuo-xuan-zhuan-zi-fu-chuan-lcof 29 | */ 30 | public class reverseLeftWords { 31 | /** 32 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 33 | * 内存消耗 :39.7 MB, 在所有 Java 提交中击败了100.00%的用户 34 | */ 35 | public String reverseLeftWords(String s, int n) { 36 | return s.substring(n + 1) + s.substring(0, n + 1); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /SwordToOffer/reverseLeftWords.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/SwordToOffer/reverseLeftWords.md -------------------------------------------------------------------------------- /SwordToOffer/reversePrint.java: -------------------------------------------------------------------------------- 1 | package SwordToOffer; 2 | 3 | /** 4 | * @Time : 2020年4月14日00:37:36 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 已总结 9 | */ 10 | 11 | import BaseClass.ListNode; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | /** 17 | *输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。 18 | * 19 | * 示例 1: 20 | * 输入:head = [1,3,2] 21 | * 输出:[2,3,1] 22 | * 23 | * 限制: 24 | * 0 <= 链表长度 <= 10000 25 | * 26 | * 来源:力扣(LeetCode) 27 | * 链接:https://leetcode-cn.com/problems/cong-wei-dao-tou-da-yin-lian-biao-lcof 28 | */ 29 | public class reversePrint { 30 | /** 31 | * 执行用时 :2 ms, 在所有 Java 提交中击败了59.23%的用户 32 | * 内存消耗 :40.1 MB, 在所有 Java 提交中击败了100.00%的用户 33 | */ 34 | public int[] reversePrint(ListNode head) { 35 | if(head == null) 36 | return new int[0]; 37 | List temp = new ArrayList<>(); 38 | while(head != null){ 39 | temp.add(head.val); 40 | head = head.next; 41 | } 42 | int n = temp.size(); 43 | int[] res =new int[n]; 44 | for(int i = 0; i < n; i++){ 45 | res[n - i - 1] = temp.get(i); 46 | } 47 | return res; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /SwordToOffer/reversePrint.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/SwordToOffer/reversePrint.md -------------------------------------------------------------------------------- /WeekContest/balanceBST.java: -------------------------------------------------------------------------------- 1 | package WeekContest; 2 | 3 | import BaseClass.TreeNode; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | /** 9 | * @Time : 2020年3月15日10:49:37 10 | * @Author : yyw@ustc 11 | * @E-mail : yang0@mail.ustc.edu.cn 12 | * @Github : https://github.com/ustcyyw 13 | * @desc : 14 | */ 15 | 16 | /** 17 | * 给你一棵二叉搜索树,请你返回一棵 平衡后 的二叉搜索树,新生成的树应该与原来的树有着相同的节点值。 18 | * 如果一棵二叉搜索树中,每个节点的两棵子树高度差不超过 1 ,我们就称这棵二叉搜索树是 平衡的 。 19 | * 如果有多种构造方法,请你返回任意一种。 20 | * 21 | * 示例: 22 | * 输入:root = [1,null,2,null,3,null,4,null,null] 23 | * 输出:[2,1,3,null,null,null,4] 24 | * 解释:这不是唯一的正确答案,[3,1,4,null,2,null,null] 也是一个可行的构造方案。 25 | *   26 | * 27 | * 提示: 28 | * 树节点的数目在 1 到 10^4 之间。 29 | * 树节点的值互不相同,且在 1 到 10^5 之间。 30 | * 31 | * 来源:力扣(LeetCode) 32 | * 链接:https://leetcode-cn.com/problems/balance-a-binary-search-tree 33 | */ 34 | public class balanceBST { 35 | /** 36 | * 难度 medium 3ms 37 | */ 38 | public TreeNode balanceBST(TreeNode root) { 39 | if(root == null) 40 | return null; 41 | List vals = new ArrayList<>(); 42 | traversal(root, vals); 43 | return build(vals, 0, vals.size() - 1); 44 | } 45 | 46 | private void traversal(TreeNode x, List vals){ 47 | if(x == null) return; 48 | traversal(x.left, vals); 49 | vals.add(x.val); 50 | traversal(x.right, vals); 51 | } 52 | 53 | private TreeNode build(List vals, int lo, int hi){ 54 | if(lo > hi) return null; 55 | if(lo == hi) return new TreeNode(vals.get(hi)); 56 | int mid = (hi - lo) / 2 + lo; 57 | TreeNode x = new TreeNode(vals.get(mid)); 58 | x.left = build(vals, lo, mid - 1); 59 | x.right = build(vals, mid + 1, hi); 60 | return x; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /WeekContest/buildArray.java: -------------------------------------------------------------------------------- 1 | import java.util.ArrayList; 2 | import java.util.List; 3 | 4 | /** 5 | * @Time : 2020年5月10日10:31:51 6 | * @Author : yyw@ustc 7 | * @E-mail : yang0@mail.ustc.edu.cn 8 | * @Github : https://github.com/ustcyyw 9 | * @desc : 已总结 10 | */ 11 | 12 | /** 13 | * 给你一个目标数组 target 和一个整数 n。每次迭代,需要从  list = {1,2,3..., n} 中依序读取一个数字。 14 | * 请使用下述操作来构建目标数组 target : 15 | * Push:从 list 中读取一个新元素, 并将其推入数组中。 16 | * Pop:删除数组中的最后一个元素。 17 | * 如果目标数组构建完成,就停止读取更多元素。 18 | * 题目数据保证目标数组严格递增,并且只包含 1 到 n 之间的数字。 19 | * 请返回构建目标数组所用的操作序列。 20 | * 题目数据保证答案是唯一的。 21 | * 22 | * 示例 1: 23 | * 输入:target = [1,3], n = 3 24 | * 输出:["Push","Push","Pop","Push"] 25 | * 解释: 26 | * 读取 1 并自动推入数组 -> [1] 27 | * 读取 2 并自动推入数组,然后删除它 -> [1] 28 | * 读取 3 并自动推入数组 -> [1,3] 29 | * 30 | * 示例 2: 31 | * 输入:target = [1,2,3], n = 3 32 | * 输出:["Push","Push","Push"] 33 | * 34 | * 示例 3: 35 | * 输入:target = [1,2], n = 4 36 | * 输出:["Push","Push"] 37 | * 解释:只需要读取前 2 个数字就可以停止。 38 | * 39 | * 示例 4: 40 | * 输入:target = [2,3,4], n = 4 41 | * 输出:["Push","Pop","Push","Push","Push"]  42 | * 43 | * 提示: 44 | * 1 <= target.length <= 100 45 | * 1 <= target[i] <= 100 46 | * 1 <= n <= 100 47 | * target 是严格递增的 48 | * 49 | * 来源:力扣(LeetCode) 50 | * 链接:https://leetcode-cn.com/problems/build-an-array-with-stack-operations 51 | 52 | */ 53 | public class buildArray { 54 | public List buildArray(int[] target, int n) { 55 | List res = new ArrayList<>(); 56 | int pre = 0; 57 | for(int i = 0; i < target.length; i++){ 58 | for(int j = pre + 1; j < target[i]; j++){ 59 | res.add("Push"); 60 | res.add("Pop"); 61 | } 62 | res.add("Push"); 63 | pre = target[i]; 64 | } 65 | return res; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /WeekContest/buildArray.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/WeekContest/buildArray.md -------------------------------------------------------------------------------- /WeekContest/countTriplets.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/WeekContest/countTriplets.md -------------------------------------------------------------------------------- /WeekContest/destCity.java: -------------------------------------------------------------------------------- 1 | package Niuke; 2 | 3 | import java.util.HashMap; 4 | import java.util.List; 5 | import java.util.Map; 6 | 7 | /** 8 | * @Time : 2020年5月3日10:31:54 9 | * @Author : yyw@ustc 10 | * @E-mail : yang0@mail.ustc.edu.cn 11 | * @Github : https://github.com/ustcyyw 12 | * @desc : 已总结 13 | */ 14 | 15 | /** 16 | * 给你一份旅游线路图,该线路图中的旅行线路用数组 paths 表示, 17 | * 其中 paths[i] = [cityAi, cityBi] 表示该线路将会从 cityAi 直接前往 cityBi 。 18 | * 请你找出这次旅行的终点站,即没有任何可以通往其他城市的线路的城市。 19 | * 题目数据保证线路图会形成一条不存在循环的线路,因此只会有一个旅行终点站。 20 | * 21 | * 示例 1: 22 | * 输入:paths = [["London","New York"],["New York","Lima"],["Lima","Sao Paulo"]] 23 | * 输出:"Sao Paulo" 24 | * 解释:从 "London" 出发,最后抵达终点站 "Sao Paulo" 。本次旅行的路线是 "London" -> "New York" -> "Lima" -> "Sao Paulo" 。 25 | * 26 | * 示例 2: 27 | * 输入:paths = [["B","C"],["D","B"],["C","A"]] 28 | * 输出:"A" 29 | * 解释:所有可能的线路是: 30 | * "D" -> "B" -> "C" -> "A".  31 | * "B" -> "C" -> "A".  32 | * "C" -> "A".  33 | * "A".  34 | * 显然,旅行终点站是 "A" 。 35 | * 36 | * 示例 3: 37 | * 输入:paths = [["A","Z"]] 38 | * 输出:"Z" 39 | * 40 | * 提示: 41 | * 1 <= paths.length <= 100 42 | * paths[i].length == 2 43 | * 1 <= cityAi.length, cityBi.length <= 10 44 | * cityAi != cityBi 45 | * 所有字符串均由大小写英文字母和空格字符组成。 46 | * 47 | * 来源:力扣(LeetCode) 48 | * 链接:https://leetcode-cn.com/problems/destination-city 49 | */ 50 | public class destCity { 51 | public String destCity(List> paths) { 52 | Map map = prepare(paths); 53 | String from = paths.get(0).get(0); 54 | while(true){ 55 | if(!map.containsKey(from)) 56 | return from; 57 | from = map.get(from); 58 | } 59 | } 60 | 61 | private Map prepare(List> paths){ 62 | Map map = new HashMap<>(); 63 | for(List path : paths) 64 | map.put(path.get(0), path.get(1)); 65 | return map; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /WeekContest/destCity.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/WeekContest/destCity.md -------------------------------------------------------------------------------- /WeekContest/displayTable.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/WeekContest/displayTable.md -------------------------------------------------------------------------------- /WeekContest/findDiagonalOrder2.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/WeekContest/findDiagonalOrder2.md -------------------------------------------------------------------------------- /WeekContest/findLucky.java: -------------------------------------------------------------------------------- 1 | package WeekContest; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * @Time : 2020年3月29日10:35:48 8 | * @Author : yyw@ustc 9 | * @E-mail : yang0@mail.ustc.edu.cn 10 | * @Github : https://github.com/ustcyyw 11 | * @desc : 12 | */ 13 | 14 | /** 15 | * 在整数数组中,如果一个整数的出现频次和它的数值大小相等,我们就称这个整数为「幸运数」。 16 | * 给你一个整数数组 arr,请你从中找出并返回一个幸运数。 17 | * 如果数组中存在多个幸运数,只需返回 最大 的那个。 18 | * 如果数组中不含幸运数,则返回 -1 。  19 | * 20 | * 示例 1: 21 | * 输入:arr = [2,2,3,4] 22 | * 输出:2 23 | * 解释:数组中唯一的幸运数是 2 ,因为数值 2 的出现频次也是 2 。 24 | * 25 | * 示例 2: 26 | * 输入:arr = [1,2,2,3,3,3] 27 | * 输出:3 28 | * 解释:1、2 以及 3 都是幸运数,只需要返回其中最大的 3 。 29 | * 30 | * 示例 3: 31 | * 输入:arr = [2,2,2,3,3] 32 | * 输出:-1 33 | * 解释:数组中不存在幸运数。 34 | * 35 | * 示例 4: 36 | * 输入:arr = [5] 37 | * 输出:-1 38 | * 39 | * 示例 5: 40 | * 输入:arr = [7,7,7,7,7,7,7] 41 | * 输出:7  42 | * 43 | * 提示: 44 | * 1 <= arr.length <= 500 45 | * 1 <= arr[i] <= 500 46 | * 47 | * 来源:力扣(LeetCode) 48 | * 链接:https://leetcode-cn.com/problems/find-lucky-integer-in-an-array 49 | */ 50 | public class findLucky { 51 | /** 52 | * 难度 easy 53 | */ 54 | public int findLucky(int[] arr) { 55 | Map map = new HashMap<>(); 56 | for(int i : arr) 57 | map.put(i, map.getOrDefault(i, 0) + 1); 58 | int res = -1; 59 | for(int key : map.keySet()){ 60 | if(key == map.get(key)) 61 | res = Math.max(res, key); 62 | } 63 | return res; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /WeekContest/findLucky.md: -------------------------------------------------------------------------------- 1 | # 5368. 找出数组中的幸运数 2 | 3 | ### 第182周周赛第一题 easy 4 | 在整数数组中,如果一个整数的出现频次和它的数值大小相等,我们就称这个整数为「幸运数」。 5 | 给你一个整数数组 `arr`,请你从中找出并返回一个幸运数。 6 | 如果数组中存在多个幸运数,只需返回 最大 的那个。 7 | 如果数组中不含幸运数,则返回 -1 。 8 | 9 | 示例 1: 10 | 输入:`arr = [2,2,3,4]` 11 | 输出:2 12 | 解释:数组中唯一的幸运数是 2 ,因为数值 2 的出现频次也是 2 。 13 | 14 | 示例 2: 15 | 输入:`arr = [1,2,2,3,3,3]` 16 | 输出:3 17 | 解释:1、2 以及 3 都是幸运数,只需要返回其中最大的 3 。 18 | 19 | 示例 3: 20 | 输入:`arr = [2,2,2,3,3]` 21 | 输出:-1 22 | 解释:数组中不存在幸运数。 23 | 24 | 示例 4: 25 | 输入:`arr = [5]` 26 | 输出:-1 27 | 28 | 示例 5: 29 | 输入:`arr = [7,7,7,7,7,7,7]` 30 | 输出:7 31 | 32 | 提示: 33 | `1 <= arr.length <= 500` 34 | `1 <= arr[i] <= 500` 35 | 36 | 来源:力扣(LeetCode) 37 | [链接](https://leetcode-cn.com/problems/find-lucky-integer-in-an-array):https://leetcode-cn.com/problems/find-lucky-integer-in-an-array 38 | 39 | ### 解法 使用HashMap 40 | 41 | ```java 42 | public int findLucky(int[] arr) { 43 | Map map = new HashMap<>(); 44 | for(int i : arr) 45 | map.put(i, map.getOrDefault(i, 0) + 1); 46 | int res = -1; 47 | for(int key : map.keySet()){ 48 | if(key == map.get(key)) 49 | res = Math.max(res, key); 50 | } 51 | return res; 52 | } 53 | ``` 54 | 55 | 思路分析: 56 | 57 | * 由题目对幸运数字的定义,我们需要先统计每一个元素出现的次数,这很显然使用`HashMap`就可以,键为元素,值为该元素出现的次数。统计某个元素的出现次数,如果该元素的值与出现次数相同,他就是一个幸运数字。 58 | * 对`map`中的键进行遍历。题目要求返回最大的幸运数组,所以当遇到幸运数字时,需要进行更新`res = Math.max(res, key);` 59 | * 注意,没有幸运数的时候返回-1,所以初始状态的`res = -1`。在循环中没有找到幸运数,将其返回时才不会出现错误。 60 | * 时间复杂度为$O(n)$,空间复杂度为$O(n)$。 61 | 62 | -------------------------------------------------------------------------------- /WeekContest/generateTheString.java: -------------------------------------------------------------------------------- 1 | package WeekContest; /** 2 | * @Time : 2020年3月8日10:32:20 3 | * @Author : yyw@ustc 4 | * @E-mail : yang0@mail.ustc.edu.cn 5 | * @Github : https://github.com/ustcyyw 6 | * @desc : 7 | */ 8 | 9 | /** 10 | * 给你一个整数 n,请你返回一个含 n 个字符的字符串,其中每种字符在该字符串中都恰好出现 奇数次 。 11 | * 返回的字符串必须只含小写英文字母。如果存在多个满足题目要求的字符串,则返回其中任意一个即可。 12 | *

13 | * 示例 1: 14 | * 输入:n = 4 15 | * 输出:"pppz" 16 | * 解释:"pppz" 是一个满足题目要求的字符串,因为 'p' 出现 3 次,且 'z' 出现 1 次。当然,还有很多其他字符串也满足题目要求,比如:"ohhh" 和 "love"。 17 | *

18 | * 示例 2: 19 | * 输入:n = 2 20 | * 输出:"xy" 21 | * 解释:"xy" 是一个满足题目要求的字符串,因为 'x' 和 'y' 各出现 1 次。当然,还有很多其他字符串也满足题目要求,比如:"ag" 和 "ur"。 22 | * 示例 3: 23 | *

24 | * 输入:n = 7 25 | * 输出:"holasss" 26 | *

27 | * 提示: 28 | * 1 <= n <= 500 29 | *

30 | * 难度:easy 31 | *

32 | * 来源:力扣(LeetCode) 33 | * 链接:https://leetcode-cn.com/problems/generate-a-string-with-characters-that-have-odd-counts 34 | * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 35 | */ 36 | public class generateTheString { 37 | public String generateTheString(int n) { 38 | StringBuilder sb = new StringBuilder(); 39 | if (n % 2 == 0) { 40 | for (int i = 0; i < n - 1; i++) 41 | sb.append('a'); 42 | sb.append('b'); 43 | } else { 44 | for (int i = 0; i < n; i++) 45 | sb.append('a'); 46 | } 47 | return sb.toString(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /WeekContest/generateTheString.md: -------------------------------------------------------------------------------- 1 | # 5352. 生成每种字符都是奇数个的字符串 2 | 3 | ### 原题 4 | 给你一个整数 n,请你返回一个含 n 个字符的字符串,其中每种字符在该字符串中都恰好出现 奇数次 。 5 | 返回的字符串必须只含小写英文字母。如果存在多个满足题目要求的字符串,则返回其中任意一个即可。 6 | 7 | 示例 1: 8 | 输入:n = 4 9 | 输出:"pppz" 10 | 解释:"pppz" 是一个满足题目要求的字符串,因为 'p' 出现 3 次,且 'z' 出现 1 次。当然,还有很多其他字符串也满足题目要求,比如:"ohhh" 和 "love"。 11 | 12 | 示例 2: 13 | 输入:n = 2 14 | 输出:"xy" 15 | 解释:"xy" 是一个满足题目要求的字符串,因为 'x' 和 'y' 各出现 1 次。当然,还有很多其他字符串也满足题目要求,比如:"ag" 和 "ur"。 16 | 17 | 示例 3: 18 | 输入:n = 7 19 | 输出:"holasss" 20 | 21 | 提示: 22 | 1 <= n <= 500 23 | 24 | 来源:力扣(LeetCode) 25 | [链接](https://leetcode-cn.com/problems/generate-a-string-with-characters-that-have-odd-counts):https://leetcode-cn.com/problems/generate-a-string-with-characters-that-have-odd-counts 26 | 27 | ### 解法 28 | 29 | ```java 30 | public String generateTheString(int n) { 31 | StringBuilder sb = new StringBuilder(); 32 | if (n % 2 == 0) { 33 | for (int i = 0; i < n - 1; i++) 34 | sb.append('a'); 35 | sb.append('b'); 36 | } else { 37 | for (int i = 0; i < n; i++) 38 | sb.append('a'); 39 | } 40 | return sb.toString(); 41 | } 42 | ``` 43 | 44 | 思路分析: 45 | 46 | * 只需要找到满足条件的一个字符串。条件是:字符串中的每一个字符,出现次数都是奇数。即没有限定要有几个字符,也没有限定每个字符至少至多能出现几次。 47 | * 那么找最简单的越方便,如果给定的n是奇数,那么我只用一个小写字母构成该长度的字符串就满足题意。如果n是偶数,那么我就用一个小写字母构成n-1位,第n位选另外一个小写字母也满足了题意。 48 | * 为了避免频繁的字符串拼接,使用`StringBuilder`类。 49 | * 时间复杂度是$O(n)$的,空间复杂度也是$O(n)$。 50 | 51 | 运行结果:执行时间1ms。 -------------------------------------------------------------------------------- /WeekContest/getTriggerTime.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/WeekContest/getTriggerTime.md -------------------------------------------------------------------------------- /WeekContest/kLengthApart.java: -------------------------------------------------------------------------------- 1 | package Niuke; 2 | 3 | /** 4 | * @Time : 2020年5月3日10:38:14 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 已总结 9 | */ 10 | 11 | /** 12 | * 给你一个由若干 0 和 1 组成的数组 nums 以及整数 k。 13 | * 如果所有 1 都至少相隔 k 个元素,则返回 True ;否则,返回 False 。 14 | * 15 | * 示例 1: 16 | * 输入:nums = [1,0,0,0,1,0,0,1], k = 2 17 | * 输出:true 18 | * 解释:每个 1 都至少相隔 2 个元素。 19 | * 20 | * 示例 2: 21 | * 输入:nums = [1,0,0,1,0,1], k = 2 22 | * 输出:false 23 | * 解释:第二个 1 和第三个 1 之间只隔了 1 个元素。 24 | * 25 | * 示例 3: 26 | * 输入:nums = [1,1,1,1,1], k = 0 27 | * 输出:true 28 | * 29 | * 示例 4: 30 | * 输入:nums = [0,1,0,1], k = 1 31 | * 输出:true 32 | * 33 | * 提示: 34 | * 1 <= nums.length <= 10^5 35 | * 0 <= k <= nums.length 36 | * nums[i] 的值为 0 或 1 37 | * 38 | * 来源:力扣(LeetCode) 39 | * 链接:https://leetcode-cn.com/problems/check-if-all-1s-are-at-least-length-k-places-away 40 | */ 41 | public class kLengthApart { 42 | public boolean kLengthApart(int[] nums, int k) { 43 | for (int pre = -100000, next = 0; next < nums.length; next++) { 44 | if (nums[next] == 1) { 45 | if (next - pre <= k) 46 | return false; 47 | pre = next; 48 | } 49 | } 50 | return true; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /WeekContest/kLengthApart.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/WeekContest/kLengthApart.md -------------------------------------------------------------------------------- /WeekContest/longestSubarray.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/WeekContest/longestSubarray.md -------------------------------------------------------------------------------- /WeekContest/maxScore.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @Time : 2020年4月26日10:32:41 3 | * @Author : yyw@ustc 4 | * @E-mail : yang0@mail.ustc.edu.cn 5 | * @Github : https://github.com/ustcyyw 6 | * @desc : 已总结 7 | */ 8 | 9 | /** 10 | * 给你一个由若干 0 和 1 组成的字符串 s , 11 | * 请你计算并返回将该字符串分割成两个 非空 子字符串(即 左 子字符串和 右 子字符串)所能获得的最大得分。 12 | * 「分割字符串的得分」为 左 子字符串中 0 的数量加上 右 子字符串中 1 的数量。 13 | * 14 | * 示例 1: 15 | * 输入:s = "011101" 16 | * 输出:5 17 | * 解释: 18 | * 将字符串 s 划分为两个非空子字符串的可行方案有: 19 | * 左子字符串 = "0" 且 右子字符串 = "11101",得分 = 1 + 4 = 5 20 | * 左子字符串 = "01" 且 右子字符串 = "1101",得分 = 1 + 3 = 4 21 | * 左子字符串 = "011" 且 右子字符串 = "101",得分 = 1 + 2 = 3 22 | * 左子字符串 = "0111" 且 右子字符串 = "01",得分 = 1 + 1 = 2 23 | * 左子字符串 = "01110" 且 右子字符串 = "1",得分 = 2 + 1 = 3 24 | * 25 | * 示例 2: 26 | * 输入:s = "00111" 27 | * 输出:5 28 | * 解释:当 左子字符串 = "00" 且 右子字符串 = "111" 时,我们得到最大得分 = 2 + 3 = 5 29 | * 30 | * 示例 3: 31 | * 输入:s = "1111" 32 | * 输出:3 33 | * 34 | * 提示: 35 | * 2 <= s.length <= 500 36 | * 字符串 s 仅由字符 '0' 和 '1' 组成。 37 | * 38 | * 来源:力扣(LeetCode) 39 | * 链接:https://leetcode-cn.com/problems/maximum-score-after-splitting-a-string 40 | */ 41 | public class maxScore { 42 | public int maxScore(String s) { 43 | int res = 0; 44 | char[] chars = s.toCharArray(); 45 | int left = 0, right = 0; 46 | for (char c : chars) { 47 | if (c == '1') 48 | right++; 49 | } 50 | for (int i = 0; i < s.length() - 1; i++) { 51 | if (chars[i] == '0') 52 | left++; 53 | else right--; 54 | res = Math.max(res, left + right); 55 | } 56 | return res; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /WeekContest/maxScore.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/WeekContest/maxScore.md -------------------------------------------------------------------------------- /WeekContest/maxScore2.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @Time : 2020年4月26日10:42:15 3 | * @Author : yyw@ustc 4 | * @E-mail : yang0@mail.ustc.edu.cn 5 | * @Github : https://github.com/ustcyyw 6 | * @desc : 已总结 7 | */ 8 | 9 | /** 10 | * 几张卡牌 排成一行,每张卡牌都有一个对应的点数。点数由整数数组 cardPoints 给出。 11 | * 每次行动,你可以从行的开头或者末尾拿一张卡牌,最终你必须正好拿 k 张卡牌。 12 | * 你的点数就是你拿到手中的所有卡牌的点数之和。 13 | * 给你一个整数数组 cardPoints 和整数 k,请你返回可以获得的最大点数。 14 | * 15 | * 示例 1: 16 | * 输入:cardPoints = [1,2,3,4,5,6,1], k = 3 17 | * 输出:12 18 | * 解释:第一次行动,不管拿哪张牌,你的点数总是 1 。但是,先拿最右边的卡牌将会最大化你的可获得点数。最优策略是拿右边的三张牌,最终点数为 1 + 6 + 5 = 12 。 19 | * 20 | * 示例 2: 21 | * 输入:cardPoints = [2,2,2], k = 2 22 | * 输出:4 23 | * 解释:无论你拿起哪两张卡牌,可获得的点数总是 4 。 24 | * 25 | * 示例 3: 26 | * 输入:cardPoints = [9,7,7,9,7,7,9], k = 7 27 | * 输出:55 28 | * 解释:你必须拿起所有卡牌,可以获得的点数为所有卡牌的点数之和。 29 | * 30 | * 示例 4: 31 | * 输入:cardPoints = [1,1000,1], k = 1 32 | * 输出:1 33 | * 解释:你无法拿到中间那张卡牌,所以可以获得的最大点数为 1 。 34 | * 35 | * 示例 5: 36 | * 输入:cardPoints = [1,79,80,1,1,1,200,1], k = 3 37 | * 输出:202 38 | * 39 | * 提示: 40 | * 1 <= cardPoints.length <= 10^5 41 | * 1 <= cardPoints[i] <= 10^4 42 | * 1 <= k <= cardPoints.length 43 | * 44 | * 来源:力扣(LeetCode) 45 | * 链接:https://leetcode-cn.com/problems/maximum-points-you-can-obtain-from-cards 46 | */ 47 | public class maxScore2 { 48 | public int maxScore(int[] cardPoints, int k) { 49 | int n = cardPoints.length, scoreR = 0, scoreL = 0; 50 | for(int i = 0; i < k; i++) 51 | scoreL += cardPoints[i]; 52 | int res = scoreL; 53 | for(int l = 1; l <= k; l++){ 54 | scoreL -= cardPoints[k - l]; 55 | scoreR += cardPoints[n - l]; 56 | res = Math.max(res, scoreR + scoreL); 57 | } 58 | return res; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /WeekContest/maxScore2.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/WeekContest/maxScore2.md -------------------------------------------------------------------------------- /WeekContest/minCount.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @Time : 2020年4月18日15:01:37 3 | * @Author : yyw@ustc 4 | * @E-mail : yang0@mail.ustc.edu.cn 5 | * @Github : https://github.com/ustcyyw 6 | * @desc : 已总结 7 | */ 8 | 9 | /** 10 | * 桌上有 n 堆力扣币,每堆的数量保存在数组 coins 中。 11 | * 我们每次可以选择任意一堆,拿走其中的一枚或者两枚,求拿完所有力扣币的最少次数。 12 | * 13 | * 示例 1: 14 | * 输入:[4,2,1] 15 | * 输出:4 16 | * 解释:第一堆力扣币最少需要拿 2 次,第二堆最少需要拿 1 次,第三堆最少需要拿 1 次,总共 4 次即可拿完。 17 | * 18 | * 示例 2: 19 | * 输入:[2,3,10] 20 | * 输出:8 21 | * 22 | * 限制: 23 | * 1 <= n <= 4 24 | * 1 <= coins[i] <= 10 25 | * 26 | * 来源:力扣(LeetCode) 27 | * 链接:https://leetcode-cn.com/problems/na-ying-bi 28 | */ 29 | public class minCount { 30 | /** 31 | * 0 ms 32 | */ 33 | public int minCount(int[] coins) { 34 | int res = 0; 35 | for(int i : coins) 36 | res += count(i); 37 | return res; 38 | } 39 | 40 | private int count(int num){ 41 | int count = 0; 42 | while(num > 0){ 43 | num -= 2; 44 | count++; 45 | } 46 | return count; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /WeekContest/minCount.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/WeekContest/minCount.md -------------------------------------------------------------------------------- /WeekContest/minNumberOfFrogs.java: -------------------------------------------------------------------------------- 1 | 2 | import java.util.HashMap; 3 | import java.util.Map; 4 | 5 | /** 6 | * @Time : 2020年4月19日11:08:10 7 | * @Author : yyw@ustc 8 | * @E-mail : yang0@mail.ustc.edu.cn 9 | * @Github : https://github.com/ustcyyw 10 | * @desc : 已总结 11 | */ 12 | public class minNumberOfFrogs { 13 | private static Map map = new HashMap<>(); // 某字符在count[]数组中的索引 14 | static { 15 | map.put('r', 1); 16 | map.put('o', 2); 17 | map.put('a', 3); 18 | } 19 | 20 | public int minNumberOfFrogs(String croakOfFrogs) { 21 | int res = 0, cur = 0; 22 | int[] count = new int[4]; // 分别记数 c r o a 结尾的字符串的个数 23 | for (char c : croakOfFrogs.toCharArray()) { 24 | if (c == 'c'){ 25 | count[0] += 1; 26 | cur++; 27 | res = Math.max(cur, res); 28 | } 29 | else if (c == 'k'){ 30 | count[3] -= 1; 31 | cur--; 32 | } 33 | else { 34 | int index = map.get(c); 35 | count[index] += 1; 36 | count[index - 1] -= 1; 37 | } 38 | for(int i : count) 39 | if(i < 0) 40 | return -1; 41 | } 42 | return cur > 0 ? -1 : res; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /WeekContest/minNumberOfFrogs.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/WeekContest/minNumberOfFrogs.md -------------------------------------------------------------------------------- /WeekContest/minSubsequence.java: -------------------------------------------------------------------------------- 1 | package WeekContest; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | 7 | /** 8 | * @Time : 2020年4月5日10:32:29 9 | * @Author : yyw@ustc 10 | * @E-mail : yang0@mail.ustc.edu.cn 11 | * @Github : https://github.com/ustcyyw 12 | * @desc : 已总结 13 | */ 14 | 15 | /** 16 | * 给你一个数组 nums,请你从中抽取一个子序列,满足该子序列的元素之和 严格 大于未包含在该子序列中的各元素之和。 17 | * 如果存在多个解决方案,只需返回 长度最小 的子序列。如果仍然有多个解决方案,则返回 元素之和最大 的子序列。 18 | * 与子数组不同的地方在于,「数组的子序列」不强调元素在原数组中的连续性,也就是说,它可以通过从数组中分离一些(也可能不分离)元素得到。 19 | * 注意,题目数据保证满足所有约束条件的解决方案是 唯一 的。同时,返回的答案应当按 非递增顺序 排列。  20 | * 21 | * 示例 1: 22 | * 输入:nums = [4,3,10,9,8] 23 | * 输出:[10,9] 24 | * 解释:子序列 [10,9] 和 [10,8] 是最小的、满足元素之和大于其他各元素之和的子序列。但是 [10,9] 的元素之和最大。  25 | * 26 | * 示例 2: 27 | * 输入:nums = [4,4,7,6,7] 28 | * 输出:[7,7,6] 29 | * 解释:子序列 [7,7] 的和为 14 ,不严格大于剩下的其他元素之和(14 = 4 + 4 + 6)。因此,[7,6,7] 是满足题意的最小子序列。注意,元素按非递增顺序返回。 30 | * 31 | * 示例 3: 32 | * 输入:nums = [6] 33 | * 输出:[6] 34 | * 35 | * 提示: 36 | * 1 <= nums.length <= 500 37 | * 1 <= nums[i] <= 100 38 | * 39 | * 来源:力扣(LeetCode) 40 | * 链接:https://leetcode-cn.com/problems/minimum-subsequence-in-non-increasing-order 41 | */ 42 | public class minSubsequence { 43 | public List minSubsequence(int[] nums) { 44 | List res = new ArrayList<>(); 45 | Arrays.sort(nums); 46 | int left = 0, sum = 0; 47 | for(int i : nums) 48 | left += i; 49 | for(int i = nums.length - 1; i >= 0; i--){ 50 | sum += nums[i]; 51 | left -= nums[i]; 52 | res.add(nums[i]); 53 | if(sum > left) 54 | break; 55 | } 56 | return res; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /WeekContest/minTime.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/WeekContest/minTime.md -------------------------------------------------------------------------------- /WeekContest/numSteps.java: -------------------------------------------------------------------------------- 1 | package WeekContest; 2 | 3 | /** 4 | * @Time : 2020年4月5日10:37:23 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | public class numSteps { 11 | public int numSteps(String s) { 12 | char[] num = s.toCharArray(); 13 | int count = 0; 14 | for(int i = num.length - 1; i >= 0; i--){ 15 | if(i == 0 && num[i] == '1') 16 | return count; 17 | if(num[i] == '1'){ 18 | count++; 19 | for(int j = i - 1; j >= 0; j--){ 20 | if(num[j] == '0'){ 21 | num[j] = '1'; 22 | break; 23 | } else { 24 | num[j] = '0'; 25 | } 26 | } 27 | } 28 | count++; 29 | } 30 | return count; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /WeekContest/numWays.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/WeekContest/numWays.md -------------------------------------------------------------------------------- /WeekContest/reformat.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/WeekContest/reformat.md -------------------------------------------------------------------------------- /WeekContest/stringMatching.java: -------------------------------------------------------------------------------- 1 | package WeekContest; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | 7 | /** 8 | * @Time : 2020年4月12日10:31:33 9 | * @Author : yyw@ustc 10 | * @E-mail : yang0@mail.ustc.edu.cn 11 | * @Github : https://github.com/ustcyyw 12 | * @desc : 13 | */ 14 | 15 | /** 16 | * 给你一个字符串数组 words ,数组中的每个字符串都可以看作是一个单词。请你按 任意 顺序返回 words 中是其他单词的子字符串的所有单词。 17 | * 如果你可以删除 words[j] 最左侧和/或最右侧的若干字符得到 word[i] ,那么字符串 words[i] 就是 words[j] 的一个子字符串。  18 | * 19 | * 示例 1: 20 | * 输入:words = ["mass","as","hero","superhero"] 21 | * 输出:["as","hero"] 22 | * 解释:"as" 是 "mass" 的子字符串,"hero" 是 "superhero" 的子字符串。 23 | * ["hero","as"] 也是有效的答案。 24 | * 25 | * 示例 2: 26 | * 输入:words = ["leetcode","et","code"] 27 | * 输出:["et","code"] 28 | * 解释:"et" 和 "code" 都是 "leetcode" 的子字符串。 29 | * 30 | * 示例 3: 31 | * 输入:words = ["blue","green","bu"] 32 | * 输出:[] 33 | *   34 | * 35 | * 提示: 36 | * 1 <= words.length <= 100 37 | * 1 <= words[i].length <= 30 38 | * words[i] 仅包含小写英文字母。 39 | * 题目数据 保证 每个 words[i] 都是独一无二的。 40 | * 41 | * 来源:力扣(LeetCode) 42 | * 链接:https://leetcode-cn.com/problems/string-matching-in-an-array 43 | */ 44 | public class stringMatching { 45 | /** 46 | * 4ms 47 | */ 48 | public List stringMatching(String[] words) { 49 | List res = new ArrayList<>(); 50 | Arrays.sort(words, (String x, String y) -> x.length() - y.length()); 51 | for(int i = 0; i < words.length; i++){ 52 | for(int j = i + 1; j < words.length; j++){ 53 | if(words[j].contains(words[i])){ 54 | res.add(words[i]); 55 | break; 56 | } 57 | } 58 | } 59 | return res; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /WeekContest/stringMatching.md: -------------------------------------------------------------------------------- 1 | # 5380. 数组中的字符串匹配 2 | 3 | ### 184周周赛第一题 easy 4 | 给你一个字符串数组 words ,数组中的每个字符串都可以看作是一个单词。请你按 任意 顺序返回 words 中是其他单词的子字符串的所有单词。 5 | 如果你可以删除 words[j] 最左侧和/或最右侧的若干字符得到 word[i] ,那么字符串 words[i] 就是 words[j] 的一个子字符串。 6 | 7 | 示例 1: 8 | 输入:words = ["mass","as","hero","superhero"] 9 | 输出:["as","hero"] 10 | 解释:"as" 是 "mass" 的子字符串,"hero" 是 "superhero" 的子字符串。 11 | ["hero","as"] 也是有效的答案。 12 | 13 | 示例 2: 14 | 输入:words = ["leetcode","et","code"] 15 | 输出:["et","code"] 16 | 解释:"et" 和 "code" 都是 "leetcode" 的子字符串。 17 | 18 | 示例 3: 19 | 输入:words = ["blue","green","bu"] 20 | 输出:[] 21 | 22 | 提示: 23 | `1 <= words.length <= 100` 24 | `1 <= words[i].length <= 30` 25 | `words[i] `仅包含小写英文字母。 26 | 题目数据 保证 每个 `words[i] `都是独一无二的。 27 | 28 | 来源:力扣(LeetCode) 29 | 链接:https://leetcode-cn.com/problems/string-matching-in-an-array 30 | 31 | ### 解法:直接按题目模拟 32 | 33 | ```java 34 | public List stringMatching(String[] words) { 35 | List res = new ArrayList<>(); 36 | Arrays.sort(words, (String x, String y) -> x.length() - y.length()); 37 | for(int i = 0; i < words.length; i++){ 38 | for(int j = i + 1; j < words.length; j++){ 39 | if(words[j].contains(words[i])){ 40 | res.add(words[i]); 41 | break; 42 | } 43 | } 44 | } 45 | return res; 46 | } 47 | ``` 48 | 49 | 思路分析: 50 | 51 | * 数据量很小,可以直接按题目思路模拟。题目中的要求就是,某一个字符串要是字符串数组中别的字符串的子字符串。 52 | * 首先,长的字符串不可能是短的子字符串,所以可以将字符串数组按照长度进行升序排序`Arrays.sort(words, (String x, String y) -> x.length() - y.length())`(利用lambda表示),这样在找`word[i]`是否是别的字符串的子串时,从`j = i + 1`开始遍历即可。 53 | * 判断包含与否,直接调用`string.contains()`方法即可,如果已经确定`word[i]`为某个字符串的子串,就打断内循环。😡自己写了判断子串的方法,结果还出了一次错,太🐎了。 54 | * 时间复杂度为$O(n^2)$,空间复杂度与满足题意的字符串个数成正比 55 | 56 | 运行结果: 57 | 58 | * 4ms -------------------------------------------------------------------------------- /WeekContest/sumFourDivisors.java: -------------------------------------------------------------------------------- 1 | package WeekContest; 2 | 3 | /** 4 | * @Time : 2020年3月22日10:37:10 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | /** 12 | * 给你一个整数数组 nums,请你返回该数组中恰有四个因数的这些整数的各因数之和。 13 | * 如果数组中不存在满足题意的整数,则返回 0 。 14 | * 15 | * 示例: 16 | * 输入:nums = [21,4,7] 17 | * 输出:32 18 | * 解释: 19 | * 21 有 4 个因数:1, 3, 7, 21 20 | * 4 有 3 个因数:1, 2, 4 21 | * 7 有 2 个因数:1, 7 22 | * 答案仅为 21 的所有因数的和。 23 | *   24 | * 提示: 25 | * 26 | * 1 <= nums.length <= 10^4 27 | * 1 <= nums[i] <= 10^5 28 | * 29 | * 来源:力扣(LeetCode) 30 | * 链接:https://leetcode-cn.com/problems/four-divisors 31 | * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 32 | */ 33 | public class sumFourDivisors { 34 | public int sumFourDivisors(int[] nums) { 35 | int sum = 0; 36 | for (int i : nums) 37 | sum += getOne(i); 38 | return sum; 39 | } 40 | 41 | private int getOne(int num) { 42 | if((int)Math.sqrt(num) == Math.sqrt(num)) 43 | return 0; 44 | int sum = 0; 45 | int count = 0; 46 | for (int i = 2; i <= (int)Math.sqrt(num); i++) { 47 | if (num % i == 0) { 48 | sum += i; 49 | sum += num / i; 50 | count++; 51 | } 52 | if (count > 1) 53 | return 0; 54 | } 55 | return count == 1 ? sum + 1 + num : 0; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /easy/ArrayAndMatrix/containsDuplicate.java: -------------------------------------------------------------------------------- 1 | package easy.ArrayAndMatrix; 2 | 3 | /** 4 | * @Time : 2020年2月14日18:27:57 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | import java.util.Arrays; 12 | import java.util.HashSet; 13 | 14 | /** 15 | * 给定一个整数数组,判断是否存在重复元素。 16 | * 如果任何值在数组中出现至少两次,函数返回 true。如果数组中每个元素都不相同,则返回 false。 17 | *

18 | * 示例 1: 19 | * 输入: [1,2,3,1] 20 | * 输出: true 21 | *

22 | * 示例 2: 23 | * 输入: [1,2,3,4] 24 | * 输出: false 25 | *

26 | * 示例 3: 27 | * 输入: [1,1,1,3,3,4,3,2,4,2] 28 | * 输出: true 29 | *

30 | * 来源:力扣(LeetCode) 31 | * 链接:https://leetcode-cn.com/problems/contains-duplicate 32 | */ 33 | public class containsDuplicate { 34 | /** 35 | * 执行用时 :12 ms, 在所有 Java 提交中击败了63.45%的用户 36 | * 内存消耗 :53.6 MB, 在所有 Java 提交中击败了5.11%的用户 37 | */ 38 | public boolean containsDuplicate(int[] nums) { 39 | HashSet numsSet = new HashSet<>((int) (nums.length / 0.75F + 1.0F)); 40 | for(int num: nums){ 41 | if(!numsSet.contains(num)) 42 | numsSet.add(num); 43 | else return true; 44 | } 45 | return false; 46 | } 47 | 48 | /** 49 | * 执行用时 :6 ms, 在所有 Java 提交中击败了85.44%的用户 50 | * 内存消耗 :51.1 MB, 在所有 Java 提交中击败了5.11%的用户 51 | */ 52 | public boolean containsDuplicate2(int[] nums) { 53 | Arrays.sort(nums); 54 | for(int i = 0; i < nums.length - 1; i++){ 55 | if(nums[i] == nums[i + 1]) 56 | return true; 57 | } 58 | return false; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /easy/ArrayAndMatrix/containsDuplicate.md: -------------------------------------------------------------------------------- 1 | # 一维数组是否存在重复元素 2 | 3 | ### 原题 4 | 5 | 给定一个整数数组,判断是否存在重复元素。 6 | 如果任何值在数组中出现至少两次,函数返回 true。如果数组中每个元素都不相同,则返回 false。 7 | 8 | 示例 1: 9 | 输入: [1,2,3,1] 10 | 输出: true 11 | 12 | 示例 2: 13 | 输入: [1,2,3,4] 14 | 输出: false 15 | 16 | 示例 3: 17 | 输入: [1,1,1,3,3,4,3,2,4,2] 18 | 输出: true 19 | 20 | 来源:力扣(LeetCode) 21 | [链接](https://leetcode-cn.com/problems/contains-duplicate):https://leetcode-cn.com/problems/contains-duplicate 22 | 23 | ---- 24 | 25 | ### 两种解法 26 | 27 | ##### 1.使用 HashSet (我的第一解) 28 | 29 | ```java 30 | public boolean containsDuplicate(int[] nums) { 31 | HashSet numsSet = new HashSet<>((int) (nums.length / 0.75F + 1.0F)); 32 | for(int num: nums){ 33 | if(!numsSet.contains(num)) 34 | numsSet.add(num); 35 | else return true; 36 | } 37 | return false; 38 | } 39 | ``` 40 | 41 | 思路分析: 42 | 43 | * 要判断数组中是否有重复元素,可以遍历数组的同时记录数组中出现了哪些元素,如果某个元素已经被记录过,说明数组中有重复元素。 44 | * 当遍历结束后没有发现重复元素,则返回`true`。 45 | * 遍历了一遍数组,但遍历的同时使用了哈希表的添加与查找是否存在,这两个操作时间成本都是均摊$O(log(k))$,所以这个算法时间复杂度为$O(nlog(k))$。空间复杂度为$O(k)$。其中k为哈希表中元素的个数。 46 | * 当然,计算散列函数可能会耗费一些时间。注意,将哈希表的初始容量设置为`(int) (nums.length / 0.75F + 1.0F)` 47 | 48 | 运行结果: 49 | * 执行用时 :12 ms, 在所有 Java 提交中击败了63.45%的用户 50 | * 内存消耗 :53.6 MB, 在所有 Java 提交中击败了5.11%的用户 51 | 52 | 53 | ##### 2.排序后看相邻元素(我的第二解) 54 | 55 | ```java 56 | public boolean containsDuplicate2(int[] nums) { 57 | Arrays.sort(nums); 58 | for(int i = 0; i < nums.length - 1; i++){ 59 | if(nums[i] == nums[i + 1]) 60 | return true; 61 | } 62 | return false; 63 | } 64 | ``` 65 | 66 | 思路分析: 67 | 68 | * 如果先将数组排序,数组中有重复元素则会排到相邻位置,只需要遍历数组判断是否有相邻元素相等即可。 69 | * 排序的时间复杂度为$O(nlog(n))$,即为此算法时间复杂度。空间复杂度为$O(1)$或者$O(n)$,这取决于排序算法是否使用了辅助数组,具体选择何种排序算法是和数组大小有关的。 70 | 71 | 运行结果: 72 | * 执行用时 :6 ms, 在所有 Java 提交中击败了85.44%的用户 73 | * 内存消耗 :51.1 MB, 在所有 Java 提交中击败了5.11%的用户 74 | 75 | -------------------------------------------------------------------------------- /easy/ArrayAndMatrix/findLHS.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/easy/ArrayAndMatrix/findLHS.jpg -------------------------------------------------------------------------------- /easy/ArrayAndMatrix/findMaxConsecutiveOnes.java: -------------------------------------------------------------------------------- 1 | package easy.ArrayAndMatrix; 2 | 3 | /** 4 | * @Time : 2020年2月10日16:28:12 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | /** 12 | * 给定一个二进制数组, 计算其中最大连续1的个数。 13 | *

14 | * 示例 1: 15 | * 输入: [1,1,0,1,1,1] 16 | * 输出: 3 17 | * 解释: 开头的两位和最后的三位都是连续1,所以最大连续1的个数是 3. 18 | *

19 | * 注意: 20 | * 输入的数组只包含 0 和1。 21 | * 输入数组的长度是正整数,且不超过 10,000。 22 | *

23 | * 来源:力扣(LeetCode) 24 | * 链接:https://leetcode-cn.com/problems/max-consecutive-ones 25 | */ 26 | public class findMaxConsecutiveOnes { 27 | /** 28 | * 执行用时 :3 ms, 在所有 Java 提交中击败了69.75%的用户 29 | * 内存消耗 :38.7 MB, 在所有 Java 提交中击败了64.94%的用户 30 | */ 31 | public int findMaxConsecutiveOnes(int[] nums) { 32 | int result = 0; 33 | int temp = 0; 34 | for (int num : nums) { 35 | if (num == 1) 36 | temp++; 37 | else { 38 | result = Math.max(result, temp); 39 | temp = 0; 40 | } 41 | } 42 | return Math.max(result, temp); 43 | } 44 | 45 | /** 46 | * 执行用时 :4 ms, 在所有 Java 提交中击败了36.84%的用户 47 | * 内存消耗 :39.4 MB, 在所有 Java 提交中击败了36.06%的用户 48 | */ 49 | public int findMaxConsecutiveOnes2(int[] nums) { 50 | int pre = -1; // 用来表示上一个为0元素的索引 51 | int result = 0; 52 | for (int i = 0; i < nums.length; i++) { 53 | if (nums[i] == 0) { 54 | result = Math.max(result, i - pre - 1); 55 | pre = i; 56 | } 57 | } 58 | result = Math.max(result, nums.length - pre - 1); 59 | return result; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /easy/ArrayAndMatrix/isToeplitzMatrix.java: -------------------------------------------------------------------------------- 1 | package easy.ArrayAndMatrix; 2 | 3 | /** 4 | * @Time : 2020年2月14日14:13:48 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | /** 12 | * 如果一个矩阵的每一方向由左上到右下的对角线上具有相同元素,那么这个矩阵是托普利茨矩阵。 13 | * 给定一个 M x N 的矩阵,当且仅当它是托普利茨矩阵时返回 True。 14 | *

15 | * 示例 1: 16 | * 输入: 17 | * matrix = [ 18 | *   [1,2,3,4], 19 | *   [5,1,2,3], 20 | *   [9,5,1,2] 21 | * ] 22 | * 输出: True 23 | * 解释: 24 | * 在上述矩阵中, 其对角线为: 25 | * "[9]", "[5, 5]", "[1, 1, 1]", "[2, 2, 2]", "[3, 3]", "[4]"。 26 | * 各条对角线上的所有元素均相同, 因此答案是True。 27 | *

28 | * 示例 2: 29 | * 输入: 30 | * matrix = [ 31 | *   [1,2], 32 | *   [2,2] 33 | * ] 34 | * 输出: False 35 | * 解释: 36 | * 对角线"[1, 2]"上的元素不同。 37 | * 说明: 38 | *  matrix 是一个包含整数的二维数组。 39 | * matrix 的行数和列数均在 [1, 20]范围内。 40 | * matrix[i][j] 包含的整数在 [0, 99]范围内。 41 | *

42 | * 进阶: 43 | * 如果矩阵存储在磁盘上,并且磁盘内存是有限的,因此一次最多只能将一行矩阵加载到内存中,该怎么办? 44 | * 如果矩阵太大以至于只能一次将部分行加载到内存中,该怎么办? 45 | *

46 | * 来源:力扣(LeetCode) 47 | * 链接:https://leetcode-cn.com/problems/toeplitz-matrix 48 | */ 49 | public class isToeplitzMatrix { 50 | /** 51 | * 执行用时 :1 ms, 在所有 Java 提交中击败了100.00%的用户 52 | * 内存消耗 :48.4 MB, 在所有 Java 提交中击败了5.27%的用户 53 | * 这个做法 一次使用了矩阵的两个元素 54 | */ 55 | public boolean isToeplitzMatrix(int[][] matrix) { 56 | int column = matrix[0].length; 57 | for (int i = 1; i < matrix.length; i++) { 58 | for (int j = 1; j < column; j++) { 59 | if (matrix[i][j] != matrix[i - 1][j - 1]) 60 | return false; 61 | } 62 | } 63 | return true; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /easy/ArrayAndMatrix/isToeplitzMatrix.md: -------------------------------------------------------------------------------- 1 | # 托普利茨矩阵 2 | 3 | ### 原题 4 | 如果一个矩阵的每一方向由左上到右下的对角线上具有相同元素,那么这个矩阵是托普利茨矩阵。 5 | 给定一个 M x N 的矩阵,当且仅当它是托普利茨矩阵时返回 True。 6 | 7 | 示例 1: 8 | 输入: 9 | matrix = [ 10 | [1,2,3,4], 11 | [5,1,2,3], 12 | [9,5,1,2] 13 | ] 14 | 输出: True 15 | 解释: 16 | 在上述矩阵中, 其对角线为: 17 | "[9]", "[5, 5]", "[1, 1, 1]", "[2, 2, 2]", "[3, 3]", "[4]"。 18 | 各条对角线上的所有元素均相同, 因此答案是True。 19 | 20 | 示例 2: 21 | 输入: 22 | matrix = [ 23 | [1,2], 24 | [2,2] 25 | ] 26 | 输出: False 27 | 解释: 28 | 对角线"[1, 2]"上的元素不同。 29 | 30 | 说明: 31 | matrix 是一个包含整数的二维数组。 32 | matrix 的行数和列数均在 [1, 20]范围内。 33 | matrix[i][j] 包含的整数在 [0, 99]范围内。 34 | 进阶: 35 | 如果矩阵存储在磁盘上,并且磁盘内存是有限的,因此一次最多只能将一行矩阵加载到内存中,该怎么办? 36 | 如果矩阵太大以至于只能一次将部分行加载到内存中,该怎么办? 37 | 38 | 来源:力扣(LeetCode) 39 | [链接](https://leetcode-cn.com/problems/toeplitz-matrix):https://leetcode-cn.com/problems/toeplitz-matrix 40 | 41 | ---- 42 | 43 | ### 解法 44 | 45 | ##### 1. 每次仅使用两个元素(我的第一解) 46 | 47 | ```java 48 | public boolean isToeplitzMatrix(int[][] matrix) { 49 | int column = matrix[0].length; 50 | for (int i = 1; i < matrix.length; i++) { 51 | for (int j = 1; j < column; j++) { 52 | if (matrix[i][j] != matrix[i - 1][j - 1]) 53 | return false; 54 | } 55 | } 56 | return true; 57 | } 58 | ``` 59 | 60 | 思路分析: 61 | 62 | * 观察矩阵其实很容易发现,满足托普利茨矩阵定义的矩阵,从第二行开始,每个元素都与左上角的元素相等。 63 | * 所以只需要一个二层循环,外层循环从第二行开始,内层循环从每行的第二个元素开始(因为第一行,以及其余行的第一个元素都是没有左上角元素的)。如果循环过程中有一组元素不相等,即不满足条件,返回`false`。 64 | * 由于两层循环,遍历了除第一行及每行第一个元素外的所有元素,所以时间复杂度为$O((n-1)(m-1))$;每次仅使用了两个元素,空间复杂度为$O(1)$。 65 | 66 | 运行结果: 67 | * 执行用时 :1 ms, 在所有 Java 提交中击败了100.00%的用户 68 | * 内存消耗 :48.4 MB, 在所有 Java 提交中击败了5.27%的用户 69 | -------------------------------------------------------------------------------- /easy/ArrayAndMatrix/matrixReshape.java: -------------------------------------------------------------------------------- 1 | package easy.ArrayAndMatrix; 2 | 3 | /** 4 | * @Time : 2020年2月10日16:04:21 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | /** 12 | * 在MATLAB中,有一个非常有用的函数 reshape,它可以将一个矩阵重塑为另一个大小不同的新矩阵,但保留其原始数据。 13 | * 给出一个由二维数组表示的矩阵,以及两个正整数r和c,分别表示想要的重构的矩阵的行数和列数。 14 | * 重构后的矩阵需要将原始矩阵的所有元素以相同的行遍历顺序填充。 15 | * 如果具有给定参数的reshape操作是可行且合理的,则输出新的重塑矩阵;否则,输出原始矩阵。 16 | * 17 | * 示例 1: 18 | * 输入: 19 | * nums = 20 | * [[1,2], 21 | * [3,4]] 22 | * r = 1, c = 4 23 | * 输出: 24 | * [[1,2,3,4]] 25 | * 解释: 26 | * 行遍历nums的结果是 [1,2,3,4]。新的矩阵是 1 * 4 矩阵, 用之前的元素值一行一行填充新矩阵。 27 | * 28 | * 示例 2: 29 | * 输入: 30 | * nums = 31 | * [[1,2], 32 | * [3,4]] 33 | * r = 2, c = 4 34 | * 输出: 35 | * [[1,2], 36 | * [3,4]] 37 | * 解释: 38 | * 没有办法将 2 * 2 矩阵转化为 2 * 4 矩阵。 所以输出原矩阵。 39 | * 注意: 40 | * 给定矩阵的宽和高范围在 [1, 100]。 41 | * 给定的 r 和 c 都是正数。 42 | * 43 | * 来源:力扣(LeetCode) 44 | * 链接:https://leetcode-cn.com/problems/reshape-the-matrix 45 | */ 46 | public class matrixReshape { 47 | /** 48 | * 执行用时 :1 ms, 在所有 Java 提交中击败了100.00%的用户 49 | * 内存消耗 :39.1 MB, 在所有 Java 提交中击败了75.12%的用户 50 | */ 51 | public int[][] matrixReshape(int[][] nums, int r, int c) { 52 | if(r * c != nums.length * nums[0].length) // 无法转化的时候,输出原矩阵。 53 | return nums; 54 | int[][] result = new int[r][c]; 55 | int count = 0; 56 | for(int[] row: nums) 57 | for(int item: row){ 58 | result[count / c][count % c] = item; 59 | count++; 60 | } 61 | return result; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /easy/ArrayAndMatrix/matrixReshape.md: -------------------------------------------------------------------------------- 1 | # 重塑矩阵 2 | 3 | ### 原题 4 | 5 | 在MATLAB中,有一个非常有用的函数 reshape,它可以将一个矩阵重塑为另一个大小不同的新矩阵,但保留其原始数据。 6 | 给出一个由二维数组表示的矩阵,以及两个正整数r和c,分别表示想要的重构的矩阵的行数和列数。 7 | 重构后的矩阵需要将原始矩阵的所有元素以相同的行遍历顺序填充。 8 | 如果具有**给定参数的reshape操作是可行且合理的,则输出新的重塑矩阵;否则,输出原始矩阵。** 9 | 10 | 示例 1: 11 | 输入: 12 | nums = 13 | [[1,2], 14 | [3,4]] 15 | r = 1, c = 4 16 | 输出: 17 | [[1,2,3,4]] 18 | 解释: 19 | 行遍历nums的结果是 [1,2,3,4]。新的矩阵是 1 * 4 矩阵, 用之前的元素值一行一行填充新矩阵。 20 | 21 | 示例 2: 22 | 输入: 23 | nums = 24 | [[1,2], 25 | [3,4]] 26 | r = 2, c = 4 27 | 输出: 28 | [[1,2], 29 | [3,4]] 30 | 解释: 31 | 没有办法将 2 * 2 矩阵转化为 2 * 4 矩阵。 所以输出原矩阵。 32 | 33 | 注意: 34 | 给定矩阵的宽和高范围在 [1, 100]。 35 | 给定的 r 和 c 都是正数。 36 | 37 | 来源:力扣(LeetCode) 38 | [链接](https://leetcode-cn.com/problems/reshape-the-matrix):https://leetcode-cn.com/problems/reshape-the-matrix 39 | 40 | ---- 41 | 42 | ### 解法 43 | 44 | ##### 1.我的第一解 45 | 46 | ```java 47 | public int[][] matrixReshape(int[][] nums, int r, int c) { 48 | if(r * c != nums.length * nums[0].length) // 无法转化的时候,输出原矩阵。 49 | return nums; 50 | int[][] result = new int[r][c]; 51 | int count = 0; 52 | for(int[] row: nums) 53 | for(int item: row){ 54 | result[count / c][count % c] = item; 55 | count++; 56 | } 57 | return result; 58 | } 59 | ``` 60 | 61 | 思路分析: 62 | 63 | * 无法完成reshape的意思就是,原矩阵元素无法恰好填充指定行数列数的新矩阵。所以当`r * c != nums.length * nums[0].length`时,返回原矩阵。 64 | * 遍历原矩阵,第`0,1,2...,c-1`个元素放入第一行(索引为0),其中第`0`个元素放入第一列...第`c-1`个元素放入第c-1列。第`c,c+1...,2c-1`个元素放入第二行(索引为1)... 65 | * 以此类推,所以需要一个变量`count`来表示当前是第几个元素,因为索引是从0开始的,所以`count=0`表示第一个元素。第`count`个元素应该放入`count/c`行,`count%c`列。 66 | * 遍历使用嵌套的 `for-each`语句即可,每添加一个元素进入新矩阵中,使得计算`count++`。 67 | 68 | 运行结果: 69 | * 执行用时 :1 ms, 在所有 Java 提交中击败了100.00%的用户 70 | * 内存消耗 :39.1 MB, 在所有 Java 提交中击败了75.12%的用户 -------------------------------------------------------------------------------- /easy/ArrayAndMatrix/merge88.java: -------------------------------------------------------------------------------- 1 | package easy.ArrayAndMatrix; 2 | 3 | /** 4 | * @Time : 2020年2月23日14:08:00 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 已总结 9 | */ 10 | 11 | /** 12 | * 给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。 13 | * 说明: 14 | * 初始化 nums1 和 nums2 的元素数量分别为 m 和 n。 15 | * 你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。 16 | * 示例: 17 | *

18 | * 输入: 19 | * nums1 = [1,2,3,0,0,0], m = 3 20 | * nums2 = [2,5,6], n = 3 21 | *

22 | * 输出: [1,2,2,3,5,6] 23 | *

24 | * 来源:力扣(LeetCode) 25 | * 链接:https://leetcode-cn.com/problems/merge-sorted-array 26 | */ 27 | public class merge88 { 28 | /** 29 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 30 | * 内存消耗 :38.3 MB, 在所有 Java 提交中击败了5.25%的用户 31 | * 就是归并排序的 merge 的过程 32 | */ 33 | public void merge(int[] nums1, int m, int[] nums2, int n) { 34 | int[] temp = new int[m]; 35 | System.arraycopy(nums1, 0, temp, 0, m); 36 | 37 | int i = 0, j = 0; 38 | for(int k = 0; k < m + n; k++){ 39 | if(i >= m) nums1[k] = nums2[j++]; 40 | else if (j >= n) nums1[k] = temp[i++]; 41 | else if(temp[i] > nums2[j]) nums1[k] = nums2[j++]; 42 | else nums1[k] = temp[i++]; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /easy/ArrayAndMatrix/merge88.md: -------------------------------------------------------------------------------- 1 | # 88.合并两个有序数组 2 | 3 | ### 原题 4 | 给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。 5 | 6 | 说明: 7 | 初始化 nums1 和 nums2 的元素数量分别为 m 和 n。 8 | 你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。 9 | 示例: 10 | 11 | 输入: 12 | nums1 = [1,2,3,0,0,0], m = 3 13 | nums2 = [2,5,6], n = 3 14 | 输出: [1,2,2,3,5,6] 15 | 16 | 来源:力扣(LeetCode) 17 | [链接](https://leetcode-cn.com/problems/merge-sorted-array):https://leetcode-cn.com/problems/merge-sorted-array 18 | 19 | ---- 20 | 21 | ### 解法 22 | 23 | ```java 24 | public void merge(int[] nums1, int m, int[] nums2, int n) { 25 | int[] temp = new int[m]; 26 | System.arraycopy(nums1, 0, temp, 0, m); 27 | 28 | int i = 0, j = 0; 29 | for(int k = 0; k < m + n; k++){ 30 | if(i >= m) nums1[k] = nums2[j++]; 31 | else if (j >= n) nums1[k] = temp[i++]; 32 | else if(temp[i] > nums2[j]) nums1[k] = nums2[j++]; 33 | else nums1[k] = temp[i++]; 34 | } 35 | } 36 | ``` 37 | 38 | 思路分析: 39 | 40 | * 这就是归并排序中的归并步骤。实现这一步需要辅助数组,这个题中要归并到`nums1`中,那么我们就用辅助数组来存放其前m个元素。`System.arraycopy(nums1, 0, temp, 0, m);` 41 | * 用`i,j`分别指向`temp,nums2`两个数组的当前元素。单次循环,将`nums1`的`m+n`个位置放满该放的元素。注意,当`i>=m`时,意味着`temp`中的元素已归并完毕,此时就无脑将剩余的`nums2`中的元素放到`nums1`中即可;同理`j>=n`时类似处理。 42 | * 除了上述两种情况,就需要比较两个数组中当前元素,将小的一个放到`num1`中。 43 | * 要复制m个元素到辅助数组,归并又需要放置n+m个元素(单层循环),所以时间复杂度为$O(2m+n)$;空间复杂度为$O(m)$。 44 | 45 | 运行结果: 46 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 47 | * 内存消耗 :38.3 MB, 在所有 Java 提交中击败了5.25%的用户 -------------------------------------------------------------------------------- /easy/ArrayAndMatrix/removeDuplicates.java: -------------------------------------------------------------------------------- 1 | package easy.ArrayAndMatrix; 2 | 3 | /** 4 | * @Time : 2020年3月29日09:38:44 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 已总结 9 | */ 10 | 11 | /** 12 | * 给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。 13 | * 不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。  14 | * 15 | * 示例 1: 16 | * 给定数组 nums = [1,1,2], 17 | * 函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。 18 | * 你不需要考虑数组中超出新长度后面的元素。 19 | * 20 | * 示例 2: 21 | * 给定 nums = [0,0,1,1,1,2,2,3,3,4], 22 | * 函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。 23 | * 你不需要考虑数组中超出新长度后面的元素。 24 | *   25 | * 26 | * 说明: 27 | * 为什么返回数值是整数,但输出的答案是数组呢? 28 | * 请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。 29 | * 你可以想象内部操作如下: 30 | * // nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝 31 | * int len = removeDuplicates(nums); 32 | * 33 | * // 在函数里修改输入数组对于调用者是可见的。 34 | * // 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。 35 | * for (int i = 0; i < len; i++) { 36 | *     print(nums[i]); 37 | * } 38 | * 39 | * 来源:力扣(LeetCode) 40 | * 链接:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array 41 | */ 42 | public class removeDuplicates { 43 | /** 44 | * 执行用时 :1 ms, 在所有 Java 提交中击败了99.71%的用户 45 | * 内存消耗 :41.4 MB, 在所有 Java 提交中击败了9.63%的用户 46 | */ 47 | public int removeDuplicates(int[] nums) { 48 | if(nums == null || nums.length == 0) 49 | return 0; 50 | int i = 0; 51 | for(int j = i + 1; j < nums.length; j++){ 52 | if(nums[j] != nums[i]){ 53 | nums[++i] = nums[j]; 54 | } 55 | } 56 | return i + 1; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /easy/ArrayAndMatrix/removeDuplicates图示.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/easy/ArrayAndMatrix/removeDuplicates图示.png -------------------------------------------------------------------------------- /easy/ArrayAndMatrix/removeElement.java: -------------------------------------------------------------------------------- 1 | package easy.ArrayAndMatrix; 2 | 3 | /** 4 | * @Time : 2020年3月29日10:20:04 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | /** 12 | * 给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。 13 | * 不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。 14 | * 元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。 15 | * 16 | * 示例 1: 17 | * 给定 nums = [3,2,2,3], val = 3, 18 | * 函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。 19 | * 你不需要考虑数组中超出新长度后面的元素。 20 | * 21 | * 示例 2: 22 | * 给定 nums = [0,1,2,2,3,0,4,2], val = 2, 23 | * 函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。 24 | * 注意这五个元素可为任意顺序。 25 | * 你不需要考虑数组中超出新长度后面的元素。  26 | * 27 | * 说明: 28 | * 为什么返回数值是整数,但输出的答案是数组呢? 29 | * 请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。 30 | * 你可以想象内部操作如下: 31 | * 32 | * // nums 是以“引用”方式传递的。也就是说,不对实参作任何拷贝 33 | * int len = removeElement(nums, val); 34 | * 35 | * // 在函数里修改输入数组对于调用者是可见的。 36 | * // 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。 37 | * for (int i = 0; i < len; i++) { 38 | *     print(nums[i]); 39 | * } 40 | * 41 | * 来源:力扣(LeetCode) 42 | * 链接:https://leetcode-cn.com/problems/remove-element 43 | */ 44 | public class removeElement { 45 | /** 46 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 47 | * 内存消耗 :38.6 MB, 在所有 Java 提交中击败了5.11%的用户 48 | */ 49 | public int removeElement(int[] nums, int val) { 50 | if(nums == null || nums.length == 0) 51 | return 0; 52 | int hi = nums.length - 1, lo = 0; 53 | while(lo <= hi){ 54 | if(nums[lo] == val){ 55 | nums[lo] = nums[hi]; 56 | hi--; 57 | } else { 58 | lo++; 59 | } 60 | } 61 | return lo; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /easy/ArrayAndMatrix/surfaceArea.java: -------------------------------------------------------------------------------- 1 | package easy.ArrayAndMatrix; 2 | 3 | /** 4 | * @Time : 2020年3月25日19:40:14 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | /** 12 | * 在 N * N 的网格上,我们放置一些 1 * 1 * 1  的立方体。 13 | * 每个值 v = grid[i][j] 表示 v 个正方体叠放在对应单元格 (i, j) 上。 14 | * 请你返回最终形体的表面积。 15 | * 16 | * 示例 1: 17 | * 输入:[[2]] 18 | * 输出:10 19 | * 20 | * 示例 2: 21 | * 输入:[[1,2],[3,4]] 22 | * 输出:34 23 | * 24 | * 示例 3: 25 | * 输入:[[1,0],[0,2]] 26 | * 输出:16 27 | * 28 | * 示例 4: 29 | * 输入:[[1,1,1],[1,0,1],[1,1,1]] 30 | * 输出:32 31 | * 32 | * 示例 5: 33 | * 输入:[[2,2,2],[2,1,2],[2,2,2]] 34 | * 输出:46 35 | *   36 | * 37 | * 提示: 38 | * 1 <= N <= 50 39 | * 0 <= grid[i][j] <= 50 40 | * 41 | * 来源:力扣(LeetCode) 42 | * 链接:https://leetcode-cn.com/problems/surface-area-of-3d-shapes 43 | */ 44 | public class surfaceArea { 45 | /** 46 | * 执行用时 :3 ms, 在所有 Java 提交中击败了98.69%的用户 47 | * 内存消耗 :41 MB, 在所有 Java 提交中击败了90.44%的用户 48 | */ 49 | public int surfaceArea(int[][] grid) { 50 | int sum = 0; // 用来记录几个格子上有放立方体 51 | int n = grid.length; 52 | for(int i = 0; i < n; i++){ 53 | if(grid[i][0] != 0) sum += 2; 54 | for(int j = 1; j < n; j++){ 55 | if(grid[i][j] != 0) sum += 2; 56 | sum += Math.abs(grid[i][j] - grid[i][j - 1]) + Math.abs(grid[j][i] - grid[j - 1][i]); 57 | } 58 | sum += grid[i][n - 1] + grid[i][0] + grid[0][i] + grid[n - 1][i]; 59 | } 60 | return sum; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /easy/ArrayAndMatrix/surfaceArea图示.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/easy/ArrayAndMatrix/surfaceArea图示.png -------------------------------------------------------------------------------- /easy/BinarySearch/findTheDistanceValue图示.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/easy/BinarySearch/findTheDistanceValue图示.png -------------------------------------------------------------------------------- /easy/BinarySearch/mySqrt.java: -------------------------------------------------------------------------------- 1 | package easy.BinarySearch; 2 | 3 | /** 4 | * @Time : 2020年3月11日15:07:47 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | /** 12 | * 实现 int sqrt(int x) 函数。 13 | * 计算并返回 x 的平方根,其中 x 是非负整数。 14 | * 由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。 15 | * 16 | * 示例 1: 17 | * 输入: 4 18 | * 输出: 2 19 | * 20 | * 示例 2: 21 | * 输入: 8 22 | * 输出: 2 23 | * 说明: 8 的平方根是 2.82842..., 24 | *   由于返回类型是整数,小数部分将被舍去。 25 | * 26 | * 来源:力扣(LeetCode) 27 | * 链接:https://leetcode-cn.com/problems/sqrtx 28 | * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 29 | */ 30 | public class mySqrt { 31 | /** 32 | * 执行用时 :2 ms, 在所有 Java 提交中击败了79.59%的用户 33 | * 内存消耗 :37.3 MB, 在所有 Java 提交中击败了5.09%的用户 34 | */ 35 | public int mySqrt(int x) { 36 | long lo = 0, hi = x; 37 | while(lo <= hi){ 38 | long mid = (hi - lo) / 2 + lo; 39 | if(mid * mid > x) hi = mid - 1; 40 | else if(mid * mid < x) lo = mid + 1; 41 | else return (int)mid; 42 | } 43 | return (int)hi; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /easy/BinarySearch/mySqrt.md: -------------------------------------------------------------------------------- 1 | # 69. x 的平方根 2 | ### 原题 3 | 4 | 实现 `int sqrt(int x)` 函数。 5 | 计算并返回 *x* 的平方根,其中 *x* 是非负整数。 6 | 由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。 7 | **示例 1:** 8 | 9 | ``` 10 | 输入: 4 11 | 输出: 2 12 | ``` 13 | 14 | **示例 2:** 15 | 16 | ``` 17 | 输入: 8 18 | 输出: 2 19 | 说明: 8 的平方根是 2.82842..., 20 | 由于返回类型是整数,小数部分将被舍去。 21 | ``` 22 | 23 | ### 解法:二分法的运用 24 | 25 | ```java 26 | public int mySqrt(int x) { 27 | long lo = 0, hi = x; 28 | while(lo <= hi){ 29 | long mid = (hi - lo) / 2 + lo; 30 | if(mid * mid > x) hi = mid - 1; 31 | else if(mid * mid < x) lo = mid + 1; 32 | else return (int)mid; 33 | } 34 | return (int)hi; 35 | } 36 | ``` 37 | 38 | 思路分析: 39 | 40 | * 题目要求找到x的算术平方根,只需要返回整数部分。如果答案为y,则满足$y^2=x$或者$y^2x$。这样看来我们只要从1开始线性查找,肯定可以找到唯一一个满足上述两个不等式的y,就是答案。 41 | 42 | * 查找,从1,2,3....n进行查找(当然,肯定到不了n),待检查的元素已经排序完成,要找到唯一的目标值。就很容易想到这可能是一个二分查找可以完成的任务。 43 | 44 | * 从二分法的做法来考虑。中间元素`long mid = (hi - lo) / 2 + lo;`,一开始说过我们的目标是要计算平方的。 45 | 46 | * 如果`mid * mid > x`,说明`mid`比我们要找的答案大,那么查找就应该在区间`[1, mid - 1]`进行。 47 | * 如果`mid * mid < x`,有可能`mid`是我们要找的答案,也有可能答案在区间`[mid + 1, hi]`中。 48 | * 如果`mid * mid = x`,则刚好x的算术平方根为整数,`mid`就是答案,返回它。 49 | 50 | * 二分查找的结束条件如常规的那样`lo > hi`时,`while`循环结束。循环结束时,我们需要思考,`lo`与`hi`哪一个数才是答案。就类似于[排序数组中寻找插入位置](https://github.com/ustcyyw/yyw_algorithm/blob/master/easy/BinarySearch/searchInsert.md),可以通过举例子来判断最终应该返回哪一个值。比如求8的根,如图示: 51 | 52 | ![mySqrt图示.png](https://github.com/ustcyyw/yyw_algorithm/blob/master/easy/BinarySearch/mySqrt%E5%9B%BE%E7%A4%BA.png?raw=true) 53 | 54 | 答案为2,所以最终应该返回hi。 55 | 56 | * 注意:本题使用int,中间求平方可能会溢出,所以使用long。 57 | 58 | * 时间复杂度为$O(log(n))$;只使用了3个辅助`long`型变量,空间复杂度为$O(n)$。 59 | 60 | 运行结果: 61 | * 执行用时 :2 ms, 在所有 Java 提交中击败了79.59%的用户 62 | * 内存消耗 :37.3 MB, 在所有 Java 提交中击败了5.09%的用户 -------------------------------------------------------------------------------- /easy/BinarySearch/mySqrt图示.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/easy/BinarySearch/mySqrt图示.png -------------------------------------------------------------------------------- /easy/BinarySearch/searchInsert.java: -------------------------------------------------------------------------------- 1 | package easy.BinarySearch; 2 | 3 | /** 4 | * @Time : 2020年3月8日22:16:56 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 已总结 9 | */ 10 | 11 | /** 12 | * 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 13 | * 你可以假设数组中无重复元素。 14 | * 15 | * 示例 1: 16 | * 输入: [1,3,5,6], 5 17 | * 输出: 2 18 | * 19 | * 示例 2: 20 | * 输入: [1,3,5,6], 2 21 | * 输出: 1 22 | * 23 | * 示例 3: 24 | * 输入: [1,3,5,6], 7 25 | * 输出: 4 26 | * 27 | * 示例 4: 28 | * 输入: [1,3,5,6], 0 29 | * 输出: 0 30 | * 31 | * 来源:力扣(LeetCode) 32 | * 链接:https://leetcode-cn.com/problems/search-insert-position 33 | */ 34 | public class searchInsert { 35 | /** 36 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 37 | * 内存消耗 :39 MB, 在所有 Java 提交中击败了27.54%的用户 38 | */ 39 | public int searchInsert(int[] nums, int target) { 40 | int lo = 0, hi = nums.length - 1; 41 | while(lo <= hi){ 42 | int mid = (hi - lo) / 2 + lo; 43 | if(nums[mid] > target) 44 | hi = mid - 1; 45 | else if(nums[mid] < target) 46 | lo = mid + 1; 47 | else return mid; 48 | } 49 | return lo; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /easy/BinarySearch/searchInsert.md: -------------------------------------------------------------------------------- 1 | # 35. 搜索插入位置 2 | 3 | ### 原题 4 | 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 5 | 你可以假设数组中无重复元素。 6 | 7 | 示例 1: 8 | 输入: [1,3,5,6], 5 9 | 输出: 2 10 | 11 | 示例 2: 12 | 输入: [1,3,5,6], 2 13 | 输出: 1 14 | 15 | 示例 3: 16 | 输入: [1,3,5,6], 7 17 | 输出: 4 18 | 19 | 示例 4: 20 | 输入: [1,3,5,6], 0 21 | 输出: 0 22 | 23 | 来源:力扣(LeetCode) 24 | [链接](https://leetcode-cn.com/problems/search-insert-position):https://leetcode-cn.com/problems/search-insert-position 25 | 26 | ### 解法:二分查找最简单的运用 27 | 28 | ```java 29 | public int searchInsert(int[] nums, int target) { 30 | int lo = 0, hi = nums.length - 1; 31 | while(lo <= hi){ 32 | int mid = (hi - lo) / 2 + lo; 33 | if(nums[mid] > target) 34 | hi = mid - 1; 35 | else if(nums[mid] < target) 36 | lo = mid + 1; 37 | else return mid; 38 | } 39 | return lo; 40 | } 41 | ``` 42 | 43 | 思路分析: 44 | 45 | * 一个排序数组和一个目标值,在数组中找到目标值,这是最最标准的二分查找;如果不存在,返回它将会被按顺序插入的位置,这也是java库函数中二分查找在做的事情。 46 | 47 | * 举个例子画图确定一下,插入位置的索引在哪,如`[1,2,3,7,8,10]`中查找5。图示如下: 48 | 49 | ![searchInsert图示.png](https://github.com/ustcyyw/yyw_algorithm/blob/master/easy/BinarySearch/searchInsert%E5%9B%BE%E7%A4%BA.png?raw=true) 50 | 51 | * 可以看到,当二分查找失败的时候,返回`lo`即是按顺序要插入位置的索引。 52 | 53 | * 时间复杂度为$O(log(n))$,空间复杂度$O(1)$。 54 | 55 | 运行结果: 56 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 57 | * 内存消耗 :39 MB, 在所有 Java 提交中击败了27.54%的用户 -------------------------------------------------------------------------------- /easy/BinarySearch/searchInsert图示.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/easy/BinarySearch/searchInsert图示.png -------------------------------------------------------------------------------- /easy/BitOperation/findComplement.java: -------------------------------------------------------------------------------- 1 | package easy.BitOperation; 2 | 3 | /** 4 | * @Time : 2020年3月28日12:13:14 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | /** 12 | * 给定一个正整数,输出它的补数。补数是对该数的二进制表示取反。 13 | * 14 | * 示例 1: 15 | * 输入: 5 16 | * 输出: 2 17 | * 解释: 5 的二进制表示为 101(没有前导零位),其补数为 010。所以你需要输出 2 。 18 | * 19 | * 示例 2: 20 | * 输入: 1 21 | * 输出: 0 22 | * 解释: 1 的二进制表示为 1(没有前导零位),其补数为 0。所以你需要输出 0 。 23 | * 24 | * 注意: 25 | * 26 | * 给定的整数保证在 32 位带符号整数的范围内。 27 | * 你可以假定二进制数不包含前导零位。 28 | * 本题与 1009 https://leetcode-cn.com/problems/complement-of-base-10-integer/ 相同 29 | * 30 | * 来源:力扣(LeetCode) 31 | * 链接:https://leetcode-cn.com/problems/number-complement 32 | */ 33 | public class findComplement { 34 | /** 35 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 36 | * 内存消耗 :36.3 MB, 在所有 Java 提交中击败了5.33%的用户 37 | */ 38 | public int findComplement(int num) { 39 | int move = 0; 40 | int res = 0; 41 | while(num != 0){ 42 | int temp = num & 1; 43 | if(temp == 0) res += (1 << move); 44 | move++; 45 | num >>>= 1; 46 | } 47 | return res; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /easy/BitOperation/findComplement.md: -------------------------------------------------------------------------------- 1 | # 476. 数字的补数 2 | 3 | ### 原题 4 | 给定一个正整数,输出它的补数。补数是对该数的二进制表示取反。 5 | 6 | 示例 1: 7 | 输入: 5 8 | 输出: 2 9 | 解释: 5 的二进制表示为 101(没有前导零位),其补数为 010。所以你需要输出 2 。 10 | 11 | 示例 2: 12 | 输入: 1 13 | 输出: 0 14 | 解释: 1 的二进制表示为 1(没有前导零位),其补数为 0。所以你需要输出 0 。 15 | 16 | 注意: 17 | 18 | 给定的整数保证在 32 位带符号整数的范围内。 19 | 你可以假定二进制数不包含前导零位。 20 | 本题与 1009 https://leetcode-cn.com/problems/complement-of-base-10-integer/ 相同 21 | 22 | 来源:力扣(LeetCode) 23 | [链接](https://leetcode-cn.com/problems/number-complement):https://leetcode-cn.com/problems/number-complement 24 | 25 | ### 解法: 位运算 26 | 27 | ```java 28 | public int findComplement(int num) { 29 | int move = 0; 30 | int res = 0; 31 | while(num != 0){ 32 | int temp = num & 1; 33 | if(temp == 0) res += (1 << move); 34 | move++; 35 | num >>>= 1; 36 | } 37 | return res; 38 | } 39 | ``` 40 | 41 | 思路分析: 42 | 43 | * 要对该数的二进制表示取反,首先需要知道每一位二进制位的数字是多少。这个做法就是常规的,用`int temp = num & 1;`得到当前位的数字。不断移位的操作也是常规的while循环和无符号右移`num >>>= 1`。 44 | 45 | * 但是怎么将数字取反后放到合适的位置。可以看一个例子。以 110010为例子 46 | 47 | * ``` 48 | 第一位是0,该位取反为1 000001 49 | 第二位是1,该位取反为0 000000 50 | 第三位是0,该位取反为1 000100 51 | 第四位是0,该位取反为1 001000 52 | 第五位是1,该位取反为0 000000 53 | 第六位是1,该位取反为0 000000 54 | 相加 001101 即为110010的按位取反 55 | ``` 56 | 57 | * 所以我们发现,当第`i`位数字为0时,就应该将1左移`i-1`位,成为最后结果的加数之一。当然第`i`位数字为1时,取反为0,就没必要进行移位相加了(反正都是0不影响) 58 | 59 | * 所以我们用一个变量`move`来表示需要左移多少位。 60 | 61 | * 注意到题目说每个数都没有前导零位,所以循环结束的标志就是`num == 0`。 62 | 63 | * 时间复杂度为$O(n)$,空间复杂度为$O(1)$。 64 | 65 | 运行结果: 66 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 67 | * 内存消耗 :36.3 MB, 在所有 Java 提交中击败了5.33%的用户 -------------------------------------------------------------------------------- /easy/BitOperation/getSum.md: -------------------------------------------------------------------------------- 1 | # 371. 两整数之和 2 | 3 | ### 原题 4 | 不使用运算符 + 和 - ,计算两整数 a 、b 之和。 5 | 6 | 示例 1: 7 | 输入: a = 1, b = 2 8 | 输出: 3 9 | 10 | 示例 2: 11 | 输入: a = -2, b = 3 12 | 输出: 1 13 | 14 | 来源:力扣(LeetCode) 15 | [链接](https://leetcode-cn.com/problems/sum-of-two-integers):https://leetcode-cn.com/problems/sum-of-two-integers 16 | 17 | ### 解法 18 | 19 | ```java 20 | public int getSum2(int a, int b) { 21 | while (b != 0) { 22 | int temp = a ^ b; 23 | 24 | int carry = (a & b) << 1; 25 | 26 | a = temp; 27 | b = carry; 28 | } 29 | return a; 30 | } 31 | ``` 32 | 33 | 思路分析: 34 | 35 | * 不能直接使用+号,显然只能通过位运算来完成加法操作。 36 | 37 | * 在二进制的加法中 38 | 39 | * ``` 40 | 1 + 0 = 1 41 | 0 + 1 = 1 42 | 0 + 0 = 0 43 | 1 + 1 = 0 // 需要进位1 44 | ``` 45 | 46 | * 忽略进位,这就是异或操作。(也可以这样来记,异或就是忽略进位的加法) 47 | 48 | * 再考虑如何进位。假设现在使用 0101与0101相加 49 | 50 | * ``` 51 | 0101 52 | + 0101 53 | = 1010 54 | ``` 55 | 56 | * 如果两个数的某一位都是1,那么相加后要进位,向左进1。而两位中有一个0或者都是0,就不需要进位,所以进位的情况就是两个数相与后为1的位,像左进位。所以进位操作为`(a & b) << 1` 57 | 58 | * 于是可以将加法拆分为两种个步骤 59 | 60 | * 两个数异或,得到忽略进位的加法结果。 61 | * 两个数相与后左移一位,得到进位信息。与忽略进位的加法结果进行相加。 62 | * 这时候,可能还会产生进位,所以要重复上述两个步骤,直到表示进位的数为0。 63 | 64 | * 再举个例子。 65 | 66 | * 比如 5+3: 0101 ^ 0011 = 0110 十进制的6 67 | * 0101 & 0011 = 0001,但是这是进位数的右边一位,所以进行左移一位的操作得到 0010 十进制的2 68 | * 下一步继续计算 6 + 2 同样使用异或得到无进位数 0100(4),由与运算且左移1位 0100(4) 69 | * 再进行 4 + 4的计算:无进位数0000,进位数1000 70 | * 再进行 0 + 8的计算:无进位数1000,进位数0000 进位数为0,停止 71 | 72 | -------------------------------------------------------------------------------- /easy/BitOperation/hammingDistance.java: -------------------------------------------------------------------------------- 1 | package easy.BitOperation; 2 | 3 | /** 4 | * @Time : 2020年3月27日17:11:04 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | /** 12 | * 两个整数之间的汉明距离指的是这两个数字对应二进制位不同的位置的数目。 13 | * 给出两个整数 x 和 y,计算它们之间的汉明距离。 14 | *

15 | * 注意: 16 | * 0 ≤ x, y < 2^31. 17 | *

18 | * 示例: 19 | * 输入: x = 1, y = 4 20 | * 输出: 2 21 | * 解释: 22 | * 1 (0 0 0 1) 23 | * 4 (0 1 0 0) 24 | * ↑ ↑ 25 | *

26 | * 上面的箭头指出了对应二进制位不同的位置。 27 | *

28 | * 来源:力扣(LeetCode) 29 | * 链接:https://leetcode-cn.com/problems/hamming-distance 30 | */ 31 | public class hammingDistance { 32 | /** 33 | * 注意 x y都是正数 34 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 35 | * 内存消耗 :36.3 MB, 在所有 Java 提交中击败了5.05%的用户 36 | */ 37 | public int hammingDistance(int x, int y) { 38 | int count = 0; 39 | while (y != 0 || x != 0) { // 直到移位使得都为0,那么更高位均为0,是相等的就不用去统计了。 40 | if ((y & 1) != (x & 1)) // 除了当前位,一定都变0(和0进行与运算),当前位则保留(和1进行与运算) 41 | count++; 42 | y >>= 1; // 右移动一位 43 | x >>= 1; 44 | } 45 | return count; 46 | } 47 | 48 | private int hammingDistance2(int x, int y) { 49 | int n = x ^ y; 50 | int count = 0; 51 | while (n != 0) { 52 | count++; 53 | n = n & (n - 1); 54 | } 55 | return count; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /easy/BitOperation/hammingWeight.java: -------------------------------------------------------------------------------- 1 | package easy.BitOperation; 2 | 3 | /** 4 | * @Time : 2020年3月27日14:19:51 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | /** 12 | * 编写一个函数,输入是一个无符号整数,返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。  13 | * 14 | * 示例 1: 15 | * 输入:00000000000000000000000000001011 16 | * 输出:3 17 | * 解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。 18 | * 19 | * 示例 2: 20 | * 输入:00000000000000000000000010000000 21 | * 输出:1 22 | * 解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。 23 | * 24 | * 示例 3: 25 | * 输入:11111111111111111111111111111101 26 | * 输出:31 27 | * 解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。 28 | * 29 | * 提示: 30 | * 31 | * 请注意,在某些语言(如 Java)中,没有无符号整数类型。 32 | * 在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的, 33 | * 其内部的二进制表示形式都是相同的。 34 | * 在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数 -3。 35 | *   36 | * 进阶: 37 | * 如果多次调用这个函数,你将如何优化你的算法? 38 | * 39 | * 来源:力扣(LeetCode) 40 | * 链接:https://leetcode-cn.com/problems/number-of-1-bits 41 | */ 42 | public class hammingWeight { 43 | /** 44 | * 执行用时 :1 ms, 在所有 Java 提交中击败了99.75%的用户 45 | * 内存消耗 :36.3 MB, 在所有 Java 提交中击败了5.45%的用户 46 | */ 47 | public int hammingWeight(int n) { 48 | int count = 0; 49 | while(n != 0){ 50 | if((n & 1) == 1) 51 | count++; 52 | n >>>= 1; // 53 | } 54 | return count; 55 | } 56 | 57 | public int hammingWeight2(int n) { 58 | int count = 0; 59 | while(n != 0){ 60 | count++; 61 | n = n & (n - 1); 62 | } 63 | return count; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /easy/BitOperation/hasAlternatingBits.java: -------------------------------------------------------------------------------- 1 | package easy.BitOperation; 2 | 3 | /** 4 | * @Time : 2020年3月28日12:01:15 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 已总结 9 | */ 10 | 11 | /** 12 | * 给定一个正整数,检查他是否为交替位二进制数:换句话说,就是他的二进制数相邻的两个位数永不相等。 13 | * 14 | * 示例 1: 15 | * 输入: 5 16 | * 输出: True 17 | * 解释: 18 | * 5的二进制数是: 101 19 | * 20 | * 示例 2: 21 | * 输入: 7 22 | * 输出: False 23 | * 解释: 24 | * 7的二进制数是: 111 25 | * 26 | * 示例 3: 27 | * 输入: 11 28 | * 输出: False 29 | * 解释: 30 | * 11的二进制数是: 1011 31 | * 32 | *  示例 4: 33 | * 输入: 10 34 | * 输出: True 35 | * 解释: 36 | * 10的二进制数是: 1010 37 | * 38 | * 来源:力扣(LeetCode) 39 | * 链接:https://leetcode-cn.com/problems/binary-number-with-alternating-bits 40 | */ 41 | public class hasAlternatingBits { 42 | /** 43 | * 执行用时 :1 ms, 在所有 Java 提交中击败了69.73%的用户 44 | * 内存消耗 :36 MB, 在所有 Java 提交中击败了5.74%的用户 45 | */ 46 | public boolean hasAlternatingBits(int n) { 47 | int pre = n & 1; 48 | n >>>= 1; 49 | while(n != 0){ 50 | if((n & 1) == pre) 51 | return false; 52 | pre = n & 1; 53 | n >>>= 1; 54 | } 55 | return true; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /easy/BitOperation/hasAlternatingBits.md: -------------------------------------------------------------------------------- 1 | # 693. 交替位二进制数 2 | 3 | ### 原题 4 | 给定一个正整数,检查他是否为交替位二进制数:换句话说,就是他的二进制数相邻的两个位数永不相等。 5 | 6 | 示例 1: 7 | 输入: 5 8 | 输出: True 9 | 解释: 10 | 5的二进制数是: 101 11 | 12 | 示例 2: 13 | 输入: 7 14 | 输出: False 15 | 解释: 16 | 7的二进制数是: 111 17 | 18 | 示例 3: 19 | 输入: 11 20 | 输出: False 21 | 解释: 22 | 11的二进制数是: 1011 23 | 24 | 示例 4: 25 | 输入: 10 26 | 输出: True 27 | 解释: 28 | 10的二进制数是: 1010 29 | 30 | 来源:力扣(LeetCode) 31 | [链接](https://leetcode-cn.com/problems/binary-number-with-alternating-bits):https://leetcode-cn.com/problems/binary-number-with-alternating-bits 32 | 33 | ### 解法:位运算 34 | 35 | ```java 36 | public boolean hasAlternatingBits(int n) { 37 | int pre = n & 1; 38 | n >>>= 1; 39 | while(n != 0){ 40 | if((n & 1) == pre) 41 | return false; 42 | pre = n & 1; 43 | n >>>= 1; 44 | } 45 | return true; 46 | } 47 | ``` 48 | 49 | 思路分析: 50 | 51 | * 判断某个数的二进制数相邻的两个位数永不相等。所以还是要按顺序知道它二进制数的每一位的数字是多少。参考[191.只出现一次的数字](https://github.com/ustcyyw/yyw_algorithm/blob/master/easy/BitOperation/hammingWeight.md)的做法来获取每一位二进制数字。 52 | * 这里需要拿当前位二进制的数字与前一位二进制的数字进行比较,所以在进入循环之前,先通过`int pre = n & 1`获取第一位二进制的数字。在循环过程中如果发现当前位的数字与前一位相等`(n & 1) == pre`,就返回false。 53 | * 在循环中不断无符号右移n的同时,还要不断更新`pre = n & 1`。 54 | * 时间复杂度:$O(1)$ 。运行时间依赖于数字n的位数。由于这题中n是 32 位数。空间复杂度是$O(1)$的。 55 | 56 | 运行结果: 57 | * 执行用时 :1 ms, 在所有 Java 提交中击败了69.73%的用户 58 | * 内存消耗 :36 MB, 在所有 Java 提交中击败了5.74%的用户 -------------------------------------------------------------------------------- /easy/BitOperation/isPowerOfFour.java: -------------------------------------------------------------------------------- 1 | package easy.BitOperation; 2 | 3 | /** 4 | * @Time : 2020年3月28日11:14:32 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | /** 12 | * 给定一个整数 (32 位有符号整数),请编写一个函数来判断它是否是 4 的幂次方。 13 | * 14 | * 示例 1: 15 | * 输入: 16 16 | * 输出: true 17 | * 18 | * 示例 2: 19 | * 输入: 5 20 | * 输出: false 21 | * 22 | * 进阶: 23 | * 你能不使用循环或者递归来完成本题吗? 24 | * 25 | * 来源:力扣(LeetCode) 26 | * 链接:https://leetcode-cn.com/problems/power-of-four 27 | */ 28 | public class isPowerOfFour { 29 | /** 30 | * 执行用时 :1 ms, 在所有 Java 提交中击败了100.00%的用户 31 | * 内存消耗 :36.6 MB, 在所有 Java 提交中击败了5.21%的用户 32 | * 在类载入的时候计算一次 后面都是直接使用了 33 | */ 34 | private static int flag; // flag = 715827882 35 | static{ 36 | int temp = 2; 37 | for(int i = 0; i <= 14; i++){ 38 | flag += temp; 39 | temp <<= 2; 40 | } 41 | } 42 | 43 | /** 44 | * 相比于2的幂,4的幂也只有一位是1,但是这一位必须位于1,3,5这样的奇数位置。 45 | * 所以用一个奇数位为0的mask与要检查的数相与,如果为0,才能说明那一位1出在奇数位上 46 | * 其实 flag = 0xaaaaaaaa 47 | */ 48 | public boolean isPowerOfFour(int num) { 49 | return num > 0 && (num & (num - 1)) == 0 && (num & flag) == 0; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /easy/BitOperation/isPowerOfFour.md: -------------------------------------------------------------------------------- 1 | # 342. 4的幂 2 | 3 | ### 原题 4 | 给定一个整数 (32 位有符号整数),请编写一个函数来判断它是否是 4 的幂次方。 5 | 6 | 示例 1: 7 | 输入: 16 8 | 输出: true 9 | 10 | 示例 2: 11 | 输入: 5 12 | 输出: false 13 | 14 | 进阶: 15 | 你能不使用循环或者递归来完成本题吗? 16 | 17 | 来源:力扣(LeetCode) 18 | [链接](https://leetcode-cn.com/problems/power-of-four):https://leetcode-cn.com/problems/power-of-four 19 | 20 | ### 解法:位运算 21 | 22 | ```java 23 | public boolean isPowerOfFour(int num) { 24 | return num > 0 && (num & (num - 1)) == 0 && (num & 0xaaaaaaaa) == 0; 25 | } 26 | ``` 27 | 28 | 思路分析: 29 | 30 | * 4的幂也是2的幂,参考[231. 2的幂](https://github.com/ustcyyw/yyw_algorithm/blob/master/easy/BitOperation/isPowerOfTwo.md)。但是4的幂更特殊一些,除了它一定非负,二进制表示只有1位是1以外,唯一的1出现在二进制中的奇数位(比如0,4,16,对应的二进制1出现在0,3,5位) 31 | * 所以该数字在满足为2的幂的基础上`num > 0 && (num & (num - 1)) == 0`,还要去判断二进制的1是否出现在奇数位置。位的与运算,某一位其中之一为0,那么该位的结果就是0。所以可以使用1010这样的序列与要判断的数相与,如果结果为0,就可以保证1只出现在奇数位上。 32 | * 1010这样的32位数字,用16进制表示就是0xaaaaaaaa。 33 | * 所以判断依旧为`num > 0 && (num & (num - 1)) == 0 && (num & 0xaaaaaaaa) == 0`。 34 | * 时间复杂度:$O(1)$ 。空间复杂度是$O(1)$。 35 | 36 | 运行结果: 37 | * 执行用时 :1 ms, 在所有 Java 提交中击败了100.00%的用户 38 | * 内存消耗 :36.6 MB, 在所有 Java 提交中击败了5.21%的用户 39 | 40 | -------------------------------------------------------------------------------- /easy/BitOperation/isPowerOfTwo.java: -------------------------------------------------------------------------------- 1 | package easy.BitOperation; 2 | 3 | /** 4 | * @Time : 2020年3月28日10:56:58 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | /** 12 | * 给定一个整数,编写一个函数来判断它是否是 2 的幂次方。 13 | * 14 | * 示例 1: 15 | * 输入: 1 16 | * 输出: true 17 | * 解释: 20 = 1 18 | * 19 | * 示例 2: 20 | * 输入: 16 21 | * 输出: true 22 | * 解释: 24 = 16 23 | * 24 | * 示例 3: 25 | * 输入: 218 26 | * 输出: false 27 | * 28 | * 来源:力扣(LeetCode) 29 | * 链接:https://leetcode-cn.com/problems/power-of-two 30 | */ 31 | public class isPowerOfTwo { 32 | /** 33 | * 执行用时 :1 ms, 在所有 Java 提交中击败了100.00%的用户 34 | * 内存消耗 :36.8 MB, 在所有 Java 提交中击败了5.50%的用户 35 | */ 36 | public boolean isPowerOfTwo(int n) { 37 | if(n < 0) 38 | return false; 39 | int count = 0; 40 | while(n != 0){ // 2的幂次 二进制位级表示只有1个1存在。 41 | if((n & 1) == 1) 42 | count++; 43 | n >>>= 1; 44 | if(count > 1) return false; // 当两个1存在时,肯定不满足条件了 直接返回false 45 | } 46 | return count == 1; 47 | } 48 | 49 | /** 50 | * 评论区别人的做法 51 | * 首先 负数不可能是2的幂 52 | * 2的幂次 二进制位级表示只有1个1存在。 53 | * 有一个技巧 x & (x - 1) 将x位级中最右的一位 1 改为 0。对于2的幂,改了之后一定是一串0000000 54 | */ 55 | public boolean isPowerOfTwo2(int n) { 56 | if(n < 0) return false; 57 | return (n & (n - 1)) == 0; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /easy/BitOperation/missingNumber.java: -------------------------------------------------------------------------------- 1 | package easy.BitOperation; 2 | 3 | /** 4 | * @Time : 2020年3月27日17:23:05 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 已总结 9 | */ 10 | 11 | /** 12 | * 给定一个包含 0, 1, 2, ..., n 中 n 个数的序列,找出 0 .. n 中没有出现在序列中的那个数。 13 | * 14 | * 示例 1: 15 | * 输入: [3,0,1] 16 | * 输出: 2 17 | * 18 | * 示例 2: 19 | * 输入: [9,6,4,2,3,5,7,0,1] 20 | * 输出: 8 21 | * 说明: 22 | * 你的算法应具有线性时间复杂度。你能否仅使用额外常数空间来实现? 23 | * 24 | * 来源:力扣(LeetCode) 25 | * 链接:https://leetcode-cn.com/problems/missing-number 26 | */ 27 | 28 | 29 | public class missingNumber { 30 | /** 31 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 32 | * 内存消耗 :42.7 MB, 在所有 Java 提交中击败了5.07%的用户 33 | * 运用等差数列的做法 34 | */ 35 | public int missingNumber(int[] nums) { 36 | int sum = 0; 37 | for(int i : nums) 38 | sum += i; 39 | return nums.length * (nums.length + 1) / 2- sum; 40 | } 41 | 42 | /** 43 | * 使用位运算 44 | * 1.异或满足交换律 45 | * 2.一个数与自己异或等于0。 46 | * 3.一个数与0异或得到它本身 47 | * 先将0,1,2……n进行异或,再将nums中的元素与刚才的结果异或。除了缺失的那个数,其余都与自己通过交换律进行了异或变为0 48 | * 仅有的那个数与一堆0异或,还是它本身 49 | * 50 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 51 | * 内存消耗 :42.6 MB, 在所有 Java 提交中击败了5.07%的用户 52 | */ 53 | public int missingNumber2(int[] nums) { 54 | int res = 0; 55 | for(int i = 1; i <= nums.length; i++) 56 | res ^= i; 57 | for(int i : nums) 58 | res ^= i; 59 | return res; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /easy/BitOperation/missingNumber.md: -------------------------------------------------------------------------------- 1 | # 268. 缺失数字 2 | 3 | ### 原题 4 | 给定一个包含 0, 1, 2, ..., n 中 n 个数的序列,找出 0 .. n 中没有出现在序列中的那个数。 5 | 6 | 示例 1: 7 | 输入: [3,0,1] 8 | 输出: 2 9 | 10 | 示例 2: 11 | 输入: [9,6,4,2,3,5,7,0,1] 12 | 输出: 8 13 | 14 | 说明: 15 | 你的算法应具有线性时间复杂度。你能否仅使用额外常数空间来实现? 16 | 17 | 来源:力扣(LeetCode) 18 | [链接](https://leetcode-cn.com/problems/missing-number):https://leetcode-cn.com/problems/missing-number 19 | 20 | ### 两种解法 21 | 22 | ##### 1.等差数列和 23 | 24 | ```java 25 | public int missingNumber(int[] nums) { 26 | int sum = 0; 27 | for(int i : nums) 28 | sum += i; 29 | return nums.length * (nums.length + 1) / 2- sum; 30 | } 31 | ``` 32 | 33 | 思路分析: 34 | 35 | * 1+2+……+n-1+n的和可以直接使用等差数列的求和公式得到。 36 | * 遍历数组,得到数组中所有元素的和,是缺少了一个数的和。用公式得到的和减去数组元素的和就得到了缺失的元素。 37 | * 时间复杂度为$O(n)$,空间复杂度为$O(1)$。 38 | 39 | 运行结果: 40 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 41 | * 内存消耗 :42.7 MB, 在所有 Java 提交中击败了5.07%的用户 42 | 43 | ##### 2.异或的运用 44 | 45 | ```java 46 | public int missingNumber2(int[] nums) { 47 | int res = 0; 48 | for(int i = 1; i <= nums.length; i++) 49 | res ^= i; 50 | for(int i : nums) 51 | res ^= i; 52 | return res; 53 | } 54 | ``` 55 | 56 | 思路分析: 57 | 58 | * 在本题中,我们可以想象,如果将1,2,3……n-1,n都放入数组中,那么数组中除了目标元素只出现了一次,其余元素都出现了两次。问题就变成了在一个所有元素都成对出现,但有一个元素只出现了一次的数组中找到只出现了一次的数组。 59 | * 所以可以参考[136.只出现一次的数字](https://github.com/ustcyyw/yyw_algorithm/blob/master/easy/BitOperation/singleNumber.md)使用异或的做法。 60 | * 异或可以将相同的两个数变为0 即 x ^ x = 0 61 | * 异或具有交换律。 62 | * 0与一个数异或结果仍然为该数 0 ^ x = x 63 | * 两个相同的数进行异或操作会得到0,同时异或具有交换律,所以对于这个无序数组,成对的元素依次异或的结果为0。同时0 ^ x = x,所以最终只出现的一个元素会剩下。 64 | * 本题先进行 `1 ^ 2 ^ 3 ^ …… ^ n - 1 ^ n`,再将这个结果与数组中每个元素进行异或得到的结果就是要找的答案。 65 | * 时间复杂度为$O(n)$,空间复杂度为$O(1)$。 66 | 67 | 运行结果: 68 | 69 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 70 | * 内存消耗 :42.6 MB, 在所有 Java 提交中击败了5.07%的用户 -------------------------------------------------------------------------------- /easy/BitOperation/singleNumber.java: -------------------------------------------------------------------------------- 1 | package easy.BitOperation; 2 | 3 | /** 4 | * @Time : 2020年3月1日00:06:43 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | /** 15 | * 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。 16 | * 17 | * 说明: 18 | * 你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗? 19 | * 20 | * 示例 1: 21 | * 输入: [2,2,1] 22 | * 输出: 1 23 | * 24 | * 示例 2: 25 | * 输入: [4,1,2,1,2] 26 | * 输出: 4 27 | * 28 | * 来源:力扣(LeetCode) 29 | * 链接:https://leetcode-cn.com/problems/single-number 30 | */ 31 | public class singleNumber { 32 | public int singleNumber(int[] nums) { 33 | Map map = new HashMap<>(); 34 | for(int i : nums){ 35 | map.put(i, map.getOrDefault(i, 0) + 1); 36 | } 37 | for(int i : nums){ 38 | if(map.get(i) == 1) 39 | return i; 40 | } 41 | return -1; 42 | } 43 | /** 44 | * 用位运算来做更快,思路的3点来源 45 | * 1.异或可以将相同的两个数变为0 x^x = 0 46 | * 2.异或具有交换律 47 | * 3.0与一个数异或结果仍然为该数 0 ^ x = x 48 | * 49 | * 执行用时 :1 ms, 在所有 Java 提交中击败了99.75%的用户 50 | * 内存消耗 :42.2 MB, 在所有 Java 提交中击败了5.02%的用户 51 | */ 52 | public int singleNumber2(int[] nums) { 53 | int res = 0; 54 | for(int i : nums) 55 | res ^= i; 56 | return res; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /easy/BitOperation/singleNumber.md: -------------------------------------------------------------------------------- 1 | # 136. 只出现一次的数字 2 | 3 | ### 原题 4 | 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。 5 | 6 | 说明: 7 | 你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗? 8 | 9 | 示例 1: 10 | 输入: [2,2,1] 11 | 输出: 1 12 | 13 | 示例 2: 14 | 输入: [4,1,2,1,2] 15 | 输出: 4 16 | 17 | 来源:力扣(LeetCode) 18 | [链接](https://leetcode-cn.com/problems/single-number):https://leetcode-cn.com/problems/single-number 19 | 20 | ### 两种解法 21 | 22 | ##### 1.使用HashMap 23 | 24 | ```java 25 | public int singleNumber(int[] nums) { 26 | Map map = new HashMap<>(); 27 | for(int i : nums){ 28 | map.put(i, map.getOrDefault(i, 0) + 1); 29 | } 30 | for(int i : nums){ 31 | if(map.get(i) == 1) 32 | return i; 33 | } 34 | return -1; 35 | } 36 | ``` 37 | 38 | 思路分析: 39 | 40 | * 只有一个元素出现了一次,其他元素都出现了两次。那么只需要通过HashMap计数每个元素出现的次数即可。键位元素,值为该元素出现的次数。 41 | * 统计完成后,对所有键遍历,直到找到某个键对应的值为1,该键即为答案。 42 | * 时间复杂度为$O(n)$,空间复杂度为$O(n)$ 43 | * 使用HashSet也可以,某个元素不存在与Set中时,放入Set,如果已经存在,说明是重复元素,将其删除即可。最终Set中只有我们要找的那个只出现一次的元素。 44 | 45 | ##### 2.异或的运用 46 | 47 | ```java 48 | public int singleNumber2(int[] nums) { 49 | int res = 0; 50 | for(int i : nums) 51 | res ^= i; 52 | return res; 53 | } 54 | ``` 55 | 56 | 思路分析: 57 | 58 | * 题目要求使用常数的空间。所以使用HashMap/Set的方法不可以。 59 | * 第一次接触完全想不到使用位运算,但是可以注意这个题目中涉及到的异或的一些性质。 60 | * 异或可以将相同的两个数变为0 即 x ^ x = 0 61 | * 异或具有交换律。 62 | * 0与一个数异或结果仍然为该数 0 ^ x = x 63 | * 两个相同的数进行异或操作会得到0,同时异或具有交换律,所以对于这个无序数组,成对的元素依次异或的结果为0。同时0 ^ x = x,所以最终只出现的一个元素会剩下。 64 | 65 | 66 | 运行结果: 67 | * 执行用时 :1 ms, 在所有 Java 提交中击败了99.75%的用户 68 | * 内存消耗 :42.2 MB, 在所有 Java 提交中击败了5.02%的用户 -------------------------------------------------------------------------------- /easy/DP/climbStairs.java: -------------------------------------------------------------------------------- 1 | package easy.DP; 2 | 3 | /** 4 | * @Time : 2020年2月25日16:01:38 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | /** 12 | * 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 13 | * 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? 14 | * 注意:给定 n 是一个正整数。 15 | *

16 | * 示例 1: 17 | * 输入: 2 18 | * 输出: 2 19 | * 解释: 有两种方法可以爬到楼顶。 20 | * 1. 1 阶 + 1 阶 21 | * 2. 2 阶 22 | *

23 | * 示例 2: 24 | * 输入: 3 25 | * 输出: 3 26 | * 解释: 有三种方法可以爬到楼顶。 27 | * 1. 1 阶 + 1 阶 + 1 阶 28 | * 2. 1 阶 + 2 阶 29 | * 3. 2 阶 + 1 阶 30 | *

31 | * 来源:力扣(LeetCode) 32 | * 链接:https://leetcode-cn.com/problems/climbing-stairs 33 | */ 34 | 35 | /** 36 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 37 | * 内存消耗 :36.1 MB, 在所有 Java 提交中击败了5.17%的用户 38 | */ 39 | public class climbStairs { 40 | public int climbStairs(int n) { 41 | int[] count = new int[n + 1]; 42 | count[0] = 1; 43 | count[1] = 1; // 边界条件 44 | for(int i = 2; i <= n; i++) 45 | count[i] = count[i - 1] + count[i - 2]; // 转移方程 46 | return count[n]; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /easy/DP/climbStairs.md: -------------------------------------------------------------------------------- 1 | # 70.爬楼梯 2 | 3 | ### 原题 4 | 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 5 | 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? 6 | 注意:给定 n 是一个正整数。 7 | 8 | 示例 1: 9 | 输入: 2 10 | 输出: 2 11 | 解释: 有两种方法可以爬到楼顶。 12 | 1. 1 阶 + 1 阶 13 | 2. 2 阶 14 | 15 | 示例 2: 16 | 输入: 3 17 | 输出: 3 18 | 解释: 有三种方法可以爬到楼顶。 19 | 1. 1 阶 + 1 阶 + 1 阶 20 | 2. 1 阶 + 2 阶 21 | 3. 2 阶 + 1 阶 22 | 23 | 来源:力扣(LeetCode) 24 | [链接](https://leetcode-cn.com/problems/climbing-stairs):https://leetcode-cn.com/problems/climbing-stairs 25 | 26 | ### 解法:动态规划 27 | 28 | ```java 29 | public int climbStairs(int n) { 30 | int[] count = new int[n + 1]; 31 | count[0] = 1; 32 | count[1] = 1; // 边界条件 33 | for(int i = 2; i <= n; i++) 34 | count[i] = count[i - 1] + count[i - 2]; // 转移方程 35 | return count[n]; 36 | } 37 | ``` 38 | 39 | 思路分析: 40 | 41 | * 题目要求共有多少种方法可以到达楼顶。每一个阶梯都可以选择走两个阶梯或者1个阶梯,看起来是可以通过回溯来解决的计数问题,但其中存在很多重复计算比如1->2->3与1->3两种方式走到第三个阶梯,之后的走法在回溯中都会重复计算。这样的问题 是典型的动态规划的问题。 42 | * 动态规划第一步:确定状态,找子问题及完成问题的最后一步。最后一步是,达到最后一个阶梯有多少种方法。要到达最后一个阶梯,可以从倒数第二个台阶走两阶,也可以从倒数第一个台阶走一阶。所以到达最后一个台阶的方法数,就转化为求两个子问题,到达倒数第一,二个台阶有多少方法,然后再相加。所以状态就是到达第i个台阶有多少种方法。用`count[i]`表示到达第`i`个台阶的方法总数。 43 | * 动态规划第二步:写出状态转移方程。从上面的描述可以直接得出来 转移方程为`count[i] = count[i - 1] + count[i - 2]`。 44 | * 动态规划第三步:边界条件,也就是找到无法通过转移方程得到的结果。大部分情况都是结合状态的实际意义来确定的。在本题中`count[0]=0,count[1]`的意义就是第0阶台阶有一种方法达到,就是不跳;第一阶台阶有一种方法就是从0阶跳一阶。 45 | * 动态规划第四步:确定计算方向,这个看状态方程与边界条件。在本题中显然是从小台阶得到大台阶。 46 | * 时间负责度为$O(n)$,空间复杂度也为$O(n)$。 47 | 48 | 运行结果: 49 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 50 | * 内存消耗 :36.1 MB, 在所有 Java 提交中击败了5.17%的用户 -------------------------------------------------------------------------------- /easy/DP/massage.java: -------------------------------------------------------------------------------- 1 | package easy.DP; 2 | 3 | /** 4 | * @Time : 2020年3月24日00:27:10 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 已总结 9 | */ 10 | 11 | /** 12 | * 一个有名的按摩师会收到源源不断的预约请求,每个预约都可以选择接或不接。在每次预约服务之间要有休息时间,因此她不能接受相邻的预约。 13 | * 给定一个预约请求序列,替按摩师找到最优的预约集合(总预约时间最长),返回总的分钟数。 14 | * 注意:本题相对原题稍作改动 15 | * 16 | * 示例 1: 17 | * 输入: [1,2,3,1] 18 | * 输出: 4 19 | * 解释: 选择 1 号预约和 3 号预约,总时长 = 1 + 3 = 4。 20 | * 21 | * 示例 2: 22 | * 输入: [2,7,9,3,1] 23 | * 输出: 12 24 | * 解释: 选择 1 号预约、 3 号预约和 5 号预约,总时长 = 2 + 9 + 1 = 12。 25 | * 26 | * 示例 3: 27 | * 输入: [2,1,4,5,3,1,1,3] 28 | * 输出: 12 29 | * 解释: 选择 1 号预约、 3 号预约、 5 号预约和 8 号预约,总时长 = 2 + 4 + 3 + 3 = 12。 30 | * 31 | * 来源:力扣(LeetCode) 32 | * 链接:https://leetcode-cn.com/problems/the-masseuse-lcci 33 | */ 34 | public class massage { 35 | public int massage(int[] nums) { 36 | if(nums == null || nums.length == 0) 37 | return 0; 38 | int first = 0, second = nums[0]; 39 | for(int i = 1; i < nums.length; i++){ 40 | int temp = second; 41 | second = Math.max(nums[i] + first, second); 42 | first = temp; 43 | } 44 | return second; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /easy/DP/massage.md: -------------------------------------------------------------------------------- 1 | # 10.17面试题 17.16. 按摩师 2 | ### 原题 3 | 4 | 一个有名的按摩师会收到源源不断的预约请求,每个预约都可以选择接或不接。在每次预约服务之间要有休息时间,因此她不能接受相邻的预约。给定一个预约请求序列,替按摩师找到最优的预约集合(总预约时间最长),返回总的分钟数。 5 | 6 | 示例 1: 7 | 输入: [1,2,3,1] 8 | 输出: 4 9 | 解释: 选择 1 号预约和 3 号预约,总时长 = 1 + 3 = 4。 10 | 11 | 示例 2: 12 | 输入: [2,7,9,3,1] 13 | 输出: 12 14 | 解释: 选择 1 号预约、 3 号预约和 5 号预约,总时长 = 2 + 9 + 1 = 12。 15 | 16 | 示例 3: 17 | 输入: [2,1,4,5,3,1,1,3] 18 | 输出: 12 19 | 解释: 选择 1 号预约、 3 号预约、 5 号预约和 8 号预约,总时长 = 2 + 4 + 3 + 3 = 12。 20 | 21 | 来源:力扣(LeetCode) 22 | [链接](https://leetcode-cn.com/problems/the-masseuse-lcci):https://leetcode-cn.com/problems/the-masseuse-lcci 23 | 24 | ### 解法 25 | 26 | ```java 27 | public int massage(int[] nums) { 28 | if(nums == null || nums.length == 0) 29 | return 0; 30 | int first = 0, second = nums[0]; 31 | for(int i = 1; i < nums.length; i++){ 32 | int temp = second; 33 | second = Math.max(nums[i] + first, second); 34 | first = temp; 35 | } 36 | return second; 37 | } 38 | ``` 39 | 40 | 思路分析: 41 | 42 | * 这个题其实看一下示例之后很容易发现,就是 [198题.打家劫舍i](https://github.com/ustcyyw/yyw_algorithm/blob/master/easy/DP/rob198.md)。 43 | * 这里做了一个状态压缩,因为计算截至`i`号最大的预约时间只需要`i-1`,`i - 2`的最大预约时间及`nums[i]`。所以只需要用两个变量`first, second`分别表示前一天,前前一天的状态即可。 44 | * 扩展: 45 | * [213.打家劫舍ii](https://github.com/ustcyyw/yyw_algorithm/blob/master/medium/DP/rob213.md) 46 | * [337.打家劫舍iii](https://github.com/ustcyyw/yyw_algorithm/blob/master/medium/DP/rob337.md) 47 | 48 | -------------------------------------------------------------------------------- /easy/DP/maxSubArray.java: -------------------------------------------------------------------------------- 1 | package easy.DP; 2 | 3 | /** 4 | * @Time : 2020年3月7日12:48:07 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | /** 12 | * 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 13 | * 14 | * 示例: 15 | * 输入: [-2,1,-3,4,-1,2,1,-5,4], 16 | * 输出: 6 17 | * 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。 18 | * 19 | * 进阶: 20 | * 如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。 21 | * 22 | * 来源:力扣(LeetCode) 23 | * 链接:https://leetcode-cn.com/problems/maximum-subarray 24 | */ 25 | public class maxSubArray { 26 | /** 27 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 28 | * 内存消耗 :41.6 MB, 在所有 Java 提交中击败了8.46%的用户 29 | * 贪心算法 30 | */ 31 | public int maxSubArray(int[] nums) { 32 | int res = nums[0]; 33 | int sum = nums[0]; 34 | for(int i = 1; i < nums.length; i++){ 35 | if(sum < 0) // 如果sum已经是负资产了,说明前面选定的子序列对于后面不论子序列在哪里结束都是负面影响,从当前元素开始尝试 36 | sum = nums[i]; 37 | else // 如果sum是正的,当前元素为必然要加上,当前元素为负则加上看sum还是不是正资产,判断交由下一次循环来判断。 38 | sum += nums[i]; 39 | res = Math.max(res, sum); // 因为每次循环都在记录最大和,所以不会出现遗漏。 40 | } 41 | return res; 42 | } 43 | 44 | /** 45 | * DP 46 | * 执行用时 :1 ms, 在所有 Java 提交中击败了98.60%的用户 47 | * 内存消耗 :41.5 MB, 在所有 Java 提交中击败了8.61%的用户 48 | * 49 | * 可以和152对照着看 50 | */ 51 | public int maxSubArray2(int[] nums) { 52 | int[] count = new int[nums.length]; // count[i] 表示 0~i 这些元素 所有以nums[i]为结尾的连续子序列中 的最大和 53 | count[0] = nums[0]; 54 | for(int i = 1; i < nums.length; i++){ 55 | count[i] = Math.max(count[i - 1] + nums[i], nums[i]); 56 | } 57 | int res = Integer.MIN_VALUE; 58 | for(int i : count) 59 | res = Math.max(res, i); 60 | return res; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /easy/DP/rob198.java: -------------------------------------------------------------------------------- 1 | package easy.DP; 2 | 3 | /** 4 | * @Time : 2020年2月26日23:42:01 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 已总结 9 | */ 10 | 11 | 12 | /** 13 | * 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统, 14 | * 如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 15 | * 给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。 16 | * 17 | * 示例 1: 18 | * 输入: [1,2,3,1] 19 | * 输出: 4 20 | * 解释: 偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。 21 | *   偷窃到的最高金额 = 1 + 3 = 4 。 22 | * 23 | * 示例 2: 24 | * 输入: [2,7,9,3,1] 25 | * 输出: 12 26 | * 解释: 偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。 27 | *   偷窃到的最高金额 = 2 + 9 + 1 = 12 。 28 | * 29 | * 来源:力扣(LeetCode) 30 | * 链接:https://leetcode-cn.com/problems/house-robber 31 | */ 32 | public class rob198 { 33 | /** 34 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 35 | * 内存消耗 :37 MB, 在所有 Java 提交中击败了5.21%的用户 36 | */ 37 | public int rob(int[] nums) { 38 | int n = nums.length; 39 | if(n == 0) return 0; 40 | int[] amount = new int[n + 1]; // amount[i]的意义是截至第i家,能偷的最多的钱为amount[i] 41 | amount[0] = 0; 42 | amount[1] = nums[0]; 43 | for(int i = 2; i <= n; i++){ 44 | amount[i] = Math.max(amount[i - 1], amount[i - 2] + nums[i - 1]); 45 | } 46 | return amount[n]; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /easy/DP/rob198.md: -------------------------------------------------------------------------------- 1 | # 198.打家劫舍I 2 | 3 | ### 原题 4 | 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 5 | 给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。 6 | 7 | 示例 1: 8 | 输入: [1,2,3,1] 9 | 输出: 4 10 | 解释: 偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。 11 | 偷窃到的最高金额 = 1 + 3 = 4 。 12 | 13 | 示例 2: 14 | 输入: [2,7,9,3,1] 15 | 输出: 12 16 | 解释: 偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。 17 | 偷窃到的最高金额 = 2 + 9 + 1 = 12 。 18 | 19 | 来源:力扣(LeetCode) 20 | [链接](https://leetcode-cn.com/problems/house-robber):https://leetcode-cn.com/problems/house-robber 21 | 22 | ### 解法 23 | 24 | ```java 25 | public int rob(int[] nums) { 26 | int n = nums.length; 27 | if(n == 0) return 0; 28 | int[] amount = new int[n + 1]; // amount[i]的意义是截至第i家,能偷的最多的钱为amount[i] 29 | amount[0] = 0; 30 | amount[1] = nums[0]; 31 | for(int i = 2; i <= n; i++){ 32 | amount[i] = Math.max(amount[i - 1], amount[i - 2] + nums[i - 1]); 33 | } 34 | return amount[n]; 35 | } 36 | ``` 37 | 38 | 思路分析: 39 | 40 | * 限制条件,不能偷连续的两家。本题要求出窃取的最高金额,是一个最优问题。可以尝试能不能使用动态规划。 41 | * 最后能偷到的最大金额,最后一家偷还是不偷呢?这取决于截至倒数第二家能偷到的最多金额以及截至倒数第三家能偷到的最多的金额。如果最后一家的钱加上截至倒数第三家的偷取的金额比截至倒数第二家能偷到的最多金额大,那么小偷就选择倒数第二家不偷。否则不如最后一家放弃,拿着截至倒数第二家的金额走。 42 | * 所以存在一个最优子结构,要得到截至第`i`家最多能偷取多少钱,就得先知道截至第`i-1`与截至第`i-2`家最多能偷取多少钱。所以可以确定问题的状态`amount[i]`表示截至第`i`家,能偷的最多的钱。 43 | * 下一个问题就是确定状态转移方程,从前面提到的小偷的决策逻辑可以知道,转移方程`amount[i] = Math.max(amount[i - 1], amount[i - 2] + nums[i - 1]);` 44 | * 确定计算方向,很明显要先知道前面的情况,所以是从小到大进行计算。边界条件的确定需要看哪些元素无法通过状态转移方程得到。第0家不存在,所以`amount[0] = 0`;截至第一家只能偷这一家的钱,所以`amount[1] = nums[0]`。 45 | * 根据状态定义,最终我们要求的就是`count[n]`。 46 | * 只进行了单层遍历,所以时间复杂度为$O(n)$,空间复杂度也为$O(n)$。当然空间复杂度可以做到$O(1)$,只需要两个辅助变量即可。 47 | 48 | 运行结果: 49 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 50 | * 内存消耗 :37 MB, 在所有 Java 提交中击败了5.21%的用户 -------------------------------------------------------------------------------- /easy/LinkedList/ListNode.java: -------------------------------------------------------------------------------- 1 | package easy.linkedList; 2 | 3 | /** 4 | * @Time : 2020年2月3日15:12:23 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | public class ListNode { 11 | public int val; 12 | ListNode next; 13 | 14 | ListNode(int x) { 15 | val = x; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /easy/LinkedList/deleteDuplicates.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/easy/LinkedList/deleteDuplicates.jpg -------------------------------------------------------------------------------- /easy/LinkedList/deleteNode.java: -------------------------------------------------------------------------------- 1 | package easy.linkedList; 2 | 3 | import BaseClass.ListNode; 4 | 5 | /** 6 | * @Time : 2020年3月23日19:24:35 7 | * @Author : yyw@ustc 8 | * @E-mail : yang0@mail.ustc.edu.cn 9 | * @Github : https://github.com/ustcyyw 10 | * @desc : 已总结 11 | */ 12 | 13 | /** 14 | * 请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点。 15 | * 现有一个链表 -- head = [4,5,1,9],它可以表示为:  16 | * 17 | * 示例 1: 18 | * 输入: head = [4,5,1,9], node = 5 19 | * 输出: [4,1,9] 20 | * 解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9. 21 | * 22 | * 示例 2: 23 | * 输入: head = [4,5,1,9], node = 1 24 | * 输出: [4,5,9] 25 | * 解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9. 26 | *   27 | * 28 | * 说明: 29 | * 链表至少包含两个节点。 30 | * 链表中所有节点的值都是唯一的。 31 | * 给定的节点为非末尾节点并且一定是链表中的一个有效节点。 32 | * 不要从你的函数中返回任何结果。 33 | * 34 | * 来源:力扣(LeetCode) 35 | * 链接:https://leetcode-cn.com/problems/delete-node-in-a-linked-list 36 | */ 37 | public class deleteNode { 38 | /** 39 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 40 | * 内存消耗 :38.5 MB, 在所有 Java 提交中击败了5.07%的用户 41 | */ 42 | public void deleteNode(ListNode node) { 43 | while(node.next.next != null){ 44 | node.val = node.next.val; 45 | node = node.next; 46 | } 47 | node.val = node.next.val; 48 | node.next = null; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /easy/LinkedList/deleteNode.md: -------------------------------------------------------------------------------- 1 | # 237. 删除链表中的节点 2 | 3 | ### 原题 4 | 请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点。 5 | 现有一个链表 -- head = [4,5,1,9],它可以表示为: 6 | 7 | 示例 1: 8 | 输入: head = [4,5,1,9], node = 5 9 | 输出: [4,1,9] 10 | 解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9. 11 | 12 | 示例 2: 13 | 输入: head = [4,5,1,9], node = 1 14 | 输出: [4,5,9] 15 | 解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9. 16 | 17 | 说明: 18 | 19 | 链表至少包含两个节点。 20 | 链表中所有节点的值都是唯一的。 21 | 给定的节点为非末尾节点并且一定是链表中的一个有效节点。 22 | 不要从你的函数中返回任何结果。 23 | 24 | 来源:力扣(LeetCode) 25 | [链接](https://leetcode-cn.com/problems/delete-node-in-a-linked-list):https://leetcode-cn.com/problems/delete-node-in-a-linked-list 26 | 27 | ### 解法 28 | 29 | ```java 30 | public void deleteNode(ListNode node) { 31 | while(node.next.next != null){ 32 | node.val = node.next.val; 33 | node = node.next; 34 | } 35 | node.val = node.next.val; 36 | node.next = null; 37 | } 38 | ``` 39 | 40 | 思路分析: 41 | 42 | * 这个题的难点在于读题目,给定的输入不是`head`而是要删除的值的结点。没有头结点,就不能通过找到要删除的结点的上一个结点,通过改变连接来删除指定结点。 43 | * 再看一下奇怪的说明。给定结点不是末尾结点,链表包含至少两个结点。如果通过值的覆盖,来完成删除值。那么当前的结点需要用下一个结点的值来覆盖,所以题目说给定结点不是末尾结点。最终是要删除一个值,意味着会减少一个结点,因为覆盖需要一直进行,所以删除的结点只能是尾结点。 44 | * 覆盖结点值是一个简单的迭代`node.val = node.next.val; node = node.next; `。关键在于什么时候停止?删除尾结点,就需要在尾结点的前一个结点停止`while(node.next.next != null)`,然后用尾结点的值覆盖其前一个节点,最后前一个结点的`node.next = null` 45 | * 时间复杂度为$O(n)$,空间复杂度为$O(1)$。 46 | 47 | 运行结果: 48 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 49 | * 内存消耗 :38.5 MB, 在所有 Java 提交中击败了5.07%的用户 -------------------------------------------------------------------------------- /easy/LinkedList/hasCycle.java: -------------------------------------------------------------------------------- 1 | package easy.linkedList; 2 | 3 | /** 4 | * @Time : 2020年2月3日16:16:31 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | import java.util.HashSet; 12 | import java.util.Set; 13 | 14 | /** 15 | * 给定一个链表,判断链表中是否有环。 16 | * 17 | * 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。 18 | * 示例 1: 19 | * 输入:head = [3,2,0,-4], pos = 1 20 | * 输出:true 21 | * 解释:链表中有一个环,其尾部连接到第二个节点。 22 | * 23 | * 来源:力扣(LeetCode) 24 | * 链接:https://leetcode-cn.com/problems/linked-list-cycle 25 | */ 26 | public class hasCycle { 27 | /** 28 | * 执行用时 :11 ms, 在所有 Java 提交中击败了5.35%的用户 29 | * 内存消耗 :38.1 MB, 在所有 Java 提交中击败了13.83%的用户 30 | */ 31 | public boolean hasCycle(ListNode head){ 32 | Set vals = new HashSet<>(); 33 | while(head != null){ 34 | if(vals.contains(head)) 35 | return true; 36 | else{ 37 | vals.add(head); 38 | head = head.next; 39 | } 40 | } 41 | return false; 42 | } 43 | 44 | /** 45 | * 标答 一个追击问题 46 | */ 47 | public boolean hasCycle2(ListNode head){ 48 | if(head == null || head.next == null) 49 | return false; 50 | ListNode fast = head.next; 51 | ListNode slow = head; 52 | while (slow != fast){ 53 | if(fast == null || fast.next == null) // 当快的游标跑到链表尾 说明没有环 54 | return false; 55 | fast = fast.next.next; // 只能移动两个结点,否则会跳过慢的游标 56 | slow = slow.next; 57 | } 58 | return true; // 循环结束 意味着快的游标追上慢的游标 存在换 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /easy/LinkedList/isPalindrome1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/easy/LinkedList/isPalindrome1.jpg -------------------------------------------------------------------------------- /easy/LinkedList/isPalindrome2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/easy/LinkedList/isPalindrome2.jpg -------------------------------------------------------------------------------- /easy/LinkedList/mergeTwoLists.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/easy/LinkedList/mergeTwoLists.jpg -------------------------------------------------------------------------------- /easy/LinkedList/middleNode.java: -------------------------------------------------------------------------------- 1 | package easy.linkedList; 2 | 3 | import BaseClass.ListNode; 4 | 5 | /** 6 | * @Time : 2020年2月13日17:54:36 7 | * @Author : yyw@ustc 8 | * @E-mail : yang0@mail.ustc.edu.cn 9 | * @Github : https://github.com/ustcyyw 10 | * @desc : 11 | */ 12 | 13 | /** 14 | * 给定一个带有头结点 head 的非空单链表,返回链表的中间结点。 15 | * 如果有两个中间结点,则返回第二个中间结点。  16 | * 17 | * 示例 1: 18 | * 输入:[1,2,3,4,5] 19 | * 输出:此列表中的结点 3 (序列化形式:[3,4,5]) 20 | * 返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。 21 | * 注意,我们返回了一个 ListNode 类型的对象 ans,这样: 22 | * ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL. 23 | * 24 | * 示例 2: 25 | * 输入:[1,2,3,4,5,6] 26 | * 输出:此列表中的结点 4 (序列化形式:[4,5,6]) 27 | * 由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。 28 | *   29 | * 30 | * 提示: 31 | * 给定链表的结点数介于 1 和 100 之间。 32 | * 33 | * 来源:力扣(LeetCode) 34 | * 链接:https://leetcode-cn.com/problems/middle-of-the-linked-list 35 | */ 36 | public class middleNode { 37 | /** 38 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 39 | * 内存消耗 :39.9 MB, 在所有 Java 提交中击败了5.06%的用户 40 | */ 41 | public ListNode middleNode(ListNode head) { 42 | ListNode fast = head; 43 | ListNode slow = head; 44 | while(fast != null && fast.next != null){ 45 | slow = slow.next; 46 | fast = fast.next.next; 47 | } 48 | return slow; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /easy/LinkedList/middleNode.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/easy/LinkedList/middleNode.jpg -------------------------------------------------------------------------------- /easy/LinkedList/middleNode.md: -------------------------------------------------------------------------------- 1 | # 链表的中间结点 2 | 3 | ### 原题 4 | 5 | 给定一个带有头结点 head 的非空单链表,返回链表的中间结点。 6 | 7 | 如果有两个中间结点,则返回第二个中间结点。 8 | 9 | 示例 1: 10 | 11 | 输入:`[1,2,3,4,5]` 12 | 输出:此列表中的结点 3 (序列化形式:`[3,4,5]`) 13 | 返回的结点值为 3 。 (测评系统对该结点序列化表述是` [3,4,5]`)。 14 | 注意,我们返回了一个` ListNode` 类型的对象 `ans`,这样: 15 | `ans.val = 3, ans.next.val = 4, ans.next.next.val = 5`, 以及 `ans.next.next.next = NULL.` 16 | 17 | 示例 2: 18 | 19 | 输入:`[1,2,3,4,5,6]` 20 | 输出:此列表中的结点 4 (序列化形式:`[4,5,6]`) 21 | 由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。 22 | 23 | 24 | 提示: 25 | 26 | 给定链表的结点数介于 1 和 100 之间。 27 | 28 | 来源:力扣(LeetCode) 29 | [链接](https://leetcode-cn.com/problems/middle-of-the-linked-list):https://leetcode-cn.com/problems/middle-of-the-linked-list 30 | 31 | ### 解法 32 | 33 | ##### 1.快慢双指针 34 | 35 | ```java 36 | public ListNode middleNode(ListNode head) { 37 | ListNode fast = head; 38 | ListNode slow = head; 39 | while(fast != null && fast.next != null){ 40 | slow = slow.next; 41 | fast = fast.next.next; 42 | } 43 | return slow; 44 | } 45 | ``` 46 | 47 | 思路分析: 48 | 49 | * 要找到链表的中间结点,这是一个可以用快慢指针解决的典型问题。 50 | * 慢指针每次移动一个结点,快指针每次移动两个结点。那么,当快指针移动到链表尾时,快指针刚好移动到链表的中间结点。 51 | * 确定了这个大的思路,接下来就要处理细节,处理细节一般都可以举例子看。比如有4个结点与5个结点的情况。见图示,我们可以发现: 52 | * `fast != null && fast.next != null`时,移动停止,左边的条件对应有偶数个结点的情况,右边的条件对应有奇数个结点的情况。 53 | * 无论链表中有奇数个结点还是偶数个结点,慢指针`slow`所指的结点即为所求。 54 | 55 | 图示: 56 | 57 | ![middleNode.jpg](https://github.com/ustcyyw/yyw_algorithm/blob/master/easy/LinkedList/middleNode.jpg?raw=true) 58 | 59 | 运行结果: 60 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 61 | * 内存消耗 :39.9 MB, 在所有 Java 提交中击败了5.06%的用户 62 | -------------------------------------------------------------------------------- /easy/LinkedList/reverseList.java: -------------------------------------------------------------------------------- 1 | package easy.linkedList; 2 | 3 | /** 4 | * @Time : 2020年2月5日21:03:22 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | import java.util.Stack; 12 | 13 | /** 14 | * 反转一个单链表。 15 | * 16 | * 示例: 17 | * 18 | * 输入: 1->2->3->4->5->NULL 19 | * 输出: 5->4->3->2->1->NULL 20 | * 进阶: 21 | * 你可以迭代或递归地反转链表。你能否用两种方法解决这道题? 22 | * 23 | * 来源:力扣(LeetCode) 24 | * 链接:https://leetcode-cn.com/problems/reverse-linked-list 25 | */ 26 | public class reverseList { 27 | /** 28 | *执行用时 : 29 | * 0 ms, 在所有 Java 提交中击败了100%的用户 30 | * 内存消耗 :37.1 MB, 在所有 Java 提交中击败了30.96%的用户 31 | */ 32 | public ListNode reverseList(ListNode head) { 33 | if(head == null || head.next == null) 34 | return head; 35 | ListNode reverseHead = null; 36 | while(head != null){ 37 | ListNode temp = new ListNode(head.val); 38 | temp.next = reverseHead; 39 | reverseHead = temp; 40 | head = head.next; 41 | } 42 | return reverseHead; 43 | } 44 | 45 | /** 46 | *递归的做法 47 | */ 48 | public ListNode reverseList2(ListNode head) { 49 | if(head == null || head.next == null) 50 | return head; 51 | ListNode tail = reverseList2(head.next); 52 | head.next.next = head; 53 | head.next = null; 54 | return tail; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /easy/LinkedList/reverseList.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/easy/LinkedList/reverseList.jpg -------------------------------------------------------------------------------- /easy/Sort/intersection349.java: -------------------------------------------------------------------------------- 1 | package easy.Sort; 2 | 3 | /** 4 | * @Time : 2020年3月23日19:52:11 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | import java.util.ArrayList; 12 | import java.util.Arrays; 13 | import java.util.List; 14 | 15 | /** 16 | * 给定两个数组,编写一个函数来计算它们的交集。 17 | * 18 | * 示例 1: 19 | * 20 | * 输入: nums1 = [1,2,2,1], nums2 = [2,2] 21 | * 输出: [2] 22 | * 示例 2: 23 | * 24 | * 输入: nums1 = [4,9,5], nums2 = [9,4,9,8,4] 25 | * 输出: [9,4] 26 | * 说明: 27 | * 28 | * 输出结果中的每个元素一定是唯一的。 29 | * 我们可以不考虑输出结果的顺序。 30 | * 31 | * 来源:力扣(LeetCode) 32 | * 链接:https://leetcode-cn.com/problems/intersection-of-two-arrays 33 | */ 34 | public class intersection349 { 35 | /** 36 | * 执行用时 :2 ms, 在所有 Java 提交中击败了99.17%的用户 37 | * 内存消耗 :39 MB, 在所有 Java 提交中击败了5.11%的用户 38 | */ 39 | public int[] intersection(int[] nums1, int[] nums2) { 40 | List temp = new ArrayList<>(); 41 | Arrays.sort(nums1); 42 | Arrays.sort(nums2); 43 | int pre = Integer.MIN_VALUE; 44 | for(int i = 0, j = 0; i < nums1.length && j < nums2.length; ){ 45 | if(nums1[i] < nums2[j]) i++; 46 | else if(nums1[i] > nums2[j]) j++; 47 | else { 48 | if(pre != nums1[i]){ 49 | pre = nums1[i]; 50 | temp.add(nums1[i]); 51 | } 52 | i++; 53 | j++; 54 | } 55 | } 56 | int[] res = new int[temp.size()]; 57 | for(int i = 0; i < res.length; i++) 58 | res[i] = temp.get(i); 59 | return res; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /easy/StackAndQueue/MyQueue232.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/easy/StackAndQueue/MyQueue232.jpg -------------------------------------------------------------------------------- /easy/StackAndQueue/MyStack225.1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/easy/StackAndQueue/MyStack225.1.jpg -------------------------------------------------------------------------------- /easy/StackAndQueue/MyStack225.2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/easy/StackAndQueue/MyStack225.2.jpg -------------------------------------------------------------------------------- /easy/StackAndQueue/getLeastNumbers.java: -------------------------------------------------------------------------------- 1 | package easy.StackAndQueue; 2 | 3 | /** 4 | * @Time : 2020年2月12日16:11:42 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | import java.util.Comparator; 12 | import java.util.PriorityQueue; 13 | 14 | /** 15 | * 输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。 16 | * 17 | * 示例 1: 18 | * 输入:arr = [3,2,1], k = 2 19 | * 输出:[1,2] 或者 [2,1] 20 | * 21 | * 示例 2: 22 | * 输入:arr = [0,1,2,1], k = 1 23 | * 输出:[0] 24 | * 25 | * 限制: 26 | * 0 <= k <= arr.length <= 1000 27 | * 0 <= arr[i] <= 1000 28 | * 29 | * 来源:力扣(LeetCode) 30 | * 链接:https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof 31 | */ 32 | public class getLeastNumbers { 33 | /** 34 | * 执行用时 :25 ms, 在所有 Java 提交中击败了100.00%的用户 35 | * 内存消耗 :48.8 MB, 在所有 Java 提交中击败了100.00%的用户 36 | * 优先队列的插入与删除元素都是logk 时间复杂度 n * logk 37 | */ 38 | public int[] getLeastNumbers(int[] arr, int k) { 39 | int[] result = new int[k]; 40 | PriorityQueue MaxPQ = new PriorityQueue<>(new NumReverseComparator()); 41 | for(int i : arr){ 42 | MaxPQ.add(i); 43 | if(MaxPQ.size() > k) 44 | MaxPQ.remove(); 45 | } 46 | int i = 0; 47 | while(!MaxPQ.isEmpty()) 48 | result[i++] = MaxPQ.remove(); 49 | return result; 50 | } 51 | static class NumReverseComparator implements Comparator{ 52 | public int compare(Integer i, Integer j){ 53 | return -(i - j); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /easy/StackAndQueue/getLeastNumbers.md: -------------------------------------------------------------------------------- 1 | # 找到数组中最小的k个数 2 | 3 | ### 原题 4 | 5 | 输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。 6 | 7 | 示例 1: 8 | 9 | 输入:arr = [3,2,1], k = 2 10 | 输出:[1,2] 或者 [2,1] 11 | 示例 2: 12 | 13 | 输入:arr = [0,1,2,1], k = 1 14 | 输出:[0] 15 | 16 | 17 | 限制: 18 | 19 | 0 <= k <= arr.length <= 1000 20 | 0 <= arr[i] <= 1000 21 | 22 | 来源:力扣(LeetCode) 23 | [链接](https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof):https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof 24 | 25 | ---- 26 | 27 | ### 一种解法 28 | 29 | ##### 1.使用最大优先队列(我的第一解) 30 | 31 | ```java 32 | public int[] getLeastNumbers(int[] arr, int k) { 33 | int[] result = new int[k]; 34 | PriorityQueue MaxPQ = new PriorityQueue<>(new NumReverseComparator()); 35 | for(int i : arr){ 36 | MaxPQ.add(i); 37 | if(MaxPQ.size() > k) 38 | MaxPQ.remove(); 39 | } 40 | int i = 0; 41 | while(!MaxPQ.isEmpty()) 42 | result[i++] = MaxPQ.remove(); 43 | return result; 44 | } 45 | static class NumReverseComparator implements Comparator{ 46 | public int compare(Integer i, Integer j){ 47 | return -(i - j); 48 | } 49 | } 50 | ``` 51 | 52 | 思路分析: 53 | 54 | * 要保留最小的k个数,这个问题典型的可以使用优先队列,不断将数组中元素放入队列,不断将最大元素出队。保持队列中有k个元素,遍历结束后,即可得到最小的k个数。 55 | * 保持队列的元素个数为k,于是不论是入队还是出队,底层堆的恢复顺序的时间复杂度都是$O(log(k))$的,这个问题中每个元素都入队出队各一次,所以时间复杂度为$O(2nlog(k))$。空间复杂度为$O(k)$的。 56 | * 另外一种很直观的解法,快速排序后取前k个元素,时间复杂度为$O(nlog(n))$。在k比较小的时候,使用优先队列显然更快。 57 | * 在java中,内置了最小优先队列,没有最大优先队列。但是优先队列的构造方法可以传入`Comparator`。所以我们可以自己实现一个`Comparator`,使得数的比较方式颠倒,就可以得到一个最大优先队列了。 58 | 59 | 代码分析: 60 | 61 | * 14~17行 实现`Comparator`接口,需要override方法`public int compare(Integer i, Integer j)`。为了得到最大优先队列,需要改变数比较的规则,所以` return -(i - j);` 62 | 63 | 运行结果: 64 | * 执行用时 :25 ms, 在所有 Java 提交中击败了100.00%的用户 65 | * 内存消耗 :48.8 MB, 在所有 Java 提交中击败了100.00%的用户 66 | -------------------------------------------------------------------------------- /easy/String/compressString.java: -------------------------------------------------------------------------------- 1 | package easy.String; 2 | 3 | /** 4 | * @Time : 2020年3月16日10:28:25 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | /** 12 | * 字符串压缩。利用字符重复出现的次数,编写一种方法,实现基本的字符串压缩功能。比如,字符串aabcccccaaa会变为a2b1c5a3。 13 | * 若“压缩”后的字符串没有变短,则返回原先的字符串。你可以假设字符串中只包含大小写英文字母(a至z)。 14 | * 15 | * 示例1: 16 | * 输入:"aabcccccaaa" 17 | * 输出:"a2b1c5a3" 18 | * 19 | * 示例2: 20 | * 输入:"abbccd" 21 | * 输出:"abbccd" 22 | * 解释:"abbccd"压缩后为"a1b2c2d1",比原字符串长度更长。 23 | * 24 | * 提示: 25 | * 字符串长度在[0, 50000]范围内。 26 | * 27 | * 来源:力扣(LeetCode) 28 | * 链接:https://leetcode-cn.com/problems/compress-string-lcci 29 | */ 30 | public class compressString { 31 | /** 32 | * 执行用时 :3 ms, 在所有 Java 提交中击败了100.00%的用户 33 | * 内存消耗 :39.4 MB, 在所有 Java 提交中击败了100.00%的用户 34 | */ 35 | public String compressString(String S) { 36 | int n = S.length(); 37 | if(n <= 1) 38 | return S; 39 | char[] chars = S.toCharArray(); 40 | StringBuilder res = new StringBuilder(); 41 | int i = 0, j = 1; 42 | for(; j < n; j++){ 43 | if(chars[j] != chars[i]){ 44 | res.append(chars[i]); 45 | res.append(j - i); 46 | i = j; 47 | } 48 | } 49 | res.append(chars[i]); 50 | res.append(j - i); 51 | return res.length() < n ? res.toString() : S; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /easy/String/compressString.md: -------------------------------------------------------------------------------- 1 | # 面试题 01.06. 字符串压缩 2 | 3 | ### 原题 4 | 字符串压缩。利用字符重复出现的次数,编写一种方法,实现基本的字符串压缩功能。比如,字符串`aabcccccaaa`会变为`a2b1c5a3`。若“压缩”后的字符串没有变短,则返回原先的字符串。你可以假设字符串中只包含大小写英文字母(a至z)。 5 | 6 | 示例1: 7 | 输入:`"aabcccccaaa"` 8 | 输出:`"a2b1c5a3"` 9 | 10 | 示例2: 11 | 输入:`"abbccd"` 12 | 输出:`"abbccd"` 13 | 解释:`"abbccd"`压缩后为`"a1b2c2d1"`,比原字符串长度更长。 14 | 15 | 提示: 16 | 字符串长度在[0, 50000]范围内。 17 | 18 | 来源:力扣(LeetCode) 19 | [链接](https://leetcode-cn.com/problems/compress-string-lcci):https://leetcode-cn.com/problems/compress-string-lcci 20 | 21 | ### 解法:双指针 22 | 23 | ```java 24 | public String compressString(String S) { 25 | int n = S.length(); 26 | if(n <= 1) 27 | return S; 28 | char[] chars = S.toCharArray(); 29 | StringBuilder res = new StringBuilder(); 30 | int i = 0, j = 1; 31 | for(; j < n; j++){ 32 | if(chars[j] != chars[i]){ 33 | res.append(chars[i]); 34 | res.append(j - i); 35 | i = j; 36 | } 37 | } 38 | res.append(chars[i]); 39 | res.append(j - i); 40 | return res.length() < n ? res.toString() : S; 41 | } 42 | ``` 43 | 44 | 思路分析: 45 | 46 | * 题目中所给的输入字符串,相等的字母都是连着的。根据压缩规则,需要知道每一个字母重复出现了几次,只需要用一个指针`i`指向当前字母的第一次出现的索引,另外一个指针`j`指向下一个字母第一次出现的索引。那么`j - i`就是当前字母出现的次数。 47 | * 最终的结果为一个字符串,为了避免频繁的拼接,使用`StringBuilder res = new StringBuilder();`来进行字符串的压缩。 48 | * 初始状态`i`指向数组的第一个元素,`j`从第二个元素开始。 49 | * 判断`chars[j]`与`chars[i]`是否相等,如果相等`j`还没有指向下一个字母第一次出现的索引,于是`j++`。 50 | * 反之,`j`已经是下一个字母第一次出现的索引。此时先将当前字母`chars[i]`添加到结果中`res.append(chars[i])`,然后将这个字母出现的次数也添加到结果中`res.append(j - i);`。并且此时`j`所指的元素就是下一个要压缩的字母第一次出现的位置,所以令`i = j`。 51 | * 循环直到结束`j = S.length()` 52 | * 不能忘记,循环结束时`j = S.length()`,还有最后一个字母没有添加到结果中,同样将最后一个字母添加进去。 53 | * 最后要判断压缩字符串的长度是否比之前短,如果是则返回压缩字符串,否则返回原字符串。 54 | * 时间复杂度为$O(n)$,空间复杂度$O(m)$,m为压缩字符串的长度。 55 | 56 | 运行结果: 57 | * 执行用时 :3 ms, 在所有 Java 提交中击败了100.00%的用户 58 | * 内存消耗 :39.4 MB, 在所有 Java 提交中击败了100.00%的用户 -------------------------------------------------------------------------------- /easy/String/lengthOfLastWord.java: -------------------------------------------------------------------------------- 1 | package easy.String; 2 | 3 | /** 4 | * @Time : 2020年3月19日23:49:25 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 已总结 9 | */ 10 | 11 | /** 12 | * 给定一个仅包含大小写字母和空格 ' ' 的字符串 s,返回其最后一个单词的长度。如果字符串从左向右滚动显示,那么最后一个单词就是最后出现的单词。 13 | * 如果不存在最后一个单词,请返回 0 。 14 | * 说明:一个单词是指仅由字母组成、不包含任何空格字符的 最大子字符串。 15 | 16 | * 示例: 17 | * 输入: "Hello World" 18 | * 输出: 5 19 | * 20 | * https://leetcode-cn.com/problems/length-of-last-word/ 21 | */ 22 | public class lengthOfLastWord { 23 | /** 24 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 25 | * 内存消耗 :38.1 MB, 在所有 Java 提交中击败了5.43%的用户 26 | */ 27 | public int lengthOfLastWord(String s) { 28 | char[] chars = s.toCharArray(); 29 | if(chars.length == 0) 30 | return 0; 31 | int tail = chars.length - 1; 32 | for(;tail >= 0; tail--){ // 处理"a ",先找到第一个非空的字符 33 | if(chars[tail] != ' ') 34 | break; 35 | } 36 | if(tail == -1) // " "的情况 37 | return 0; 38 | int i = tail - 1; 39 | for(; i >= 0; i--){ 40 | if(chars[i] == ' ') 41 | break; 42 | } 43 | return tail - i; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /easy/String/lengthOfLastWord.md: -------------------------------------------------------------------------------- 1 | # 58. 最后一个单词的长度 2 | 3 | ### 原题 4 | 给定一个仅包含大小写字母和空格 ' ' 的字符串 s,返回其最后一个单词的长度。如果字符串从左向右滚动显示,那么最后一个单词就是最后出现的单词。 5 | 如果不存在最后一个单词,请返回 0 。 6 | 7 | 说明:一个单词是指仅由字母组成、不包含任何空格字符的 最大子字符串。 8 | 9 | 示例: 10 | 输入: "Hello World" 11 | 输出: 5 12 | 13 | 来源:力扣(LeetCode) 14 | [链接](https://leetcode-cn.com/problems/length-of-last-word):https://leetcode-cn.com/problems/length-of-last-word 15 | 16 | ### 解法 17 | 18 | ```java 19 | public int lengthOfLastWord(String s) { 20 | char[] chars = s.toCharArray(); 21 | if(chars.length == 0) 22 | return 0; 23 | int tail = chars.length - 1; 24 | for(;tail >= 0; tail--){ // 处理"a ",先找到第一个非空的字符 25 | if(chars[tail] != ' ') 26 | break; 27 | } 28 | if(tail == -1) // " "的情况 29 | return 0; 30 | int i = tail - 1; 31 | for(; i >= 0; i--){ 32 | if(chars[i] == ' ') 33 | break; 34 | } 35 | return tail - i; 36 | } 37 | ``` 38 | 39 | 思路分析: 40 | 41 | * 以空格为分割符,要找到最后一个单词的长度。首先,如果所给的字符串长度为0,压根不存在字符,直接返回了。 42 | * 最后一个单词,那么我们就从后往前去找第一个单词的结尾字符。注意,字符串可能是以一系列的空格结尾,比如`"a "`。所以使用一个循环从字符串末尾寻找第一个非空格的字符,其索引用`tail`表示,当遇到第一个不非空格字符`chars[tail] != ' '`,跳出循环。 43 | * 注意如果整个字符串均为空,那么循环结束时`tail == -1`。这种情况下也不存在单词,返回0。 44 | * 最后一个单词的末尾索引找到后,从`int i = tail - 1`,向前找这个单词的第一个字符的前一个索引,当循环遇到空字符时就停止循环,此时`i + 1`为最后一个单词的第一个字符的索引。那么最后一个单词的长度即为`tail - i`。 45 | * 时间复杂度为$O(n)$,空间复杂度为$O(1)$。 46 | 47 | 运行结果: 48 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 49 | * 内存消耗 :38.1 MB, 在所有 Java 提交中击败了5.43%的用户 -------------------------------------------------------------------------------- /easy/String/longestPalindrome.java: -------------------------------------------------------------------------------- 1 | package easy.String; 2 | 3 | /** 4 | * @Time : 2020年2月15日14:06:47 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | /** 12 | * 给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串。 13 | * 在构造过程中,请注意区分大小写。比如 "Aa" 不能当做一个回文字符串。 14 | * 15 | * 注意: 16 | * 假设字符串的长度不会超过 1010。 17 | * 18 | * 示例 1: 19 | * 输入: 20 | * "abccccdd" 21 | * 输出: 22 | * 7 23 | * 解释: 24 | * 我们可以构造的最长的回文串是"dccaccd", 它的长度是 7。 25 | * 26 | * 来源:力扣(LeetCode) 27 | * 链接:https://leetcode-cn.com/problems/longest-palindrome 28 | */ 29 | public class longestPalindrome { 30 | /** 31 | *执行用时 :1 ms, 在所有 Java 提交中击败了100.00%的用户 32 | * 内存消耗 :41.3 MB, 在所有 Java 提交中击败了5.22%的用户 33 | * 一个字符出现偶数次,都可以用上;一个字符出现了奇数次,则只能用出现次数-1的元素。 34 | * 不过有一个出现奇数次的字符可以全用,就是把他放在中间。 35 | * 所以当没有出现奇数次的字符时,直接返回 s.length(), 否则返回 s.length() - countOdd + 1 36 | */ 37 | public int longestPalindrome(String s) { 38 | int[] count = new int[58]; // 大小写字母中间还有6个其它字符 39 | for (char c : s.toCharArray()) 40 | count[c - 65] += 1; 41 | int countOdd = 0; 42 | for (int i : count) { 43 | if (i % 2 != 0) 44 | countOdd++; 45 | } 46 | if(countOdd == 0) 47 | return s.length(); 48 | return s.length() - countOdd + 1; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /easy/String/longestPalindrome.md: -------------------------------------------------------------------------------- 1 | # 409. 最长回文串 2 | 3 | ### 原题 4 | 给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串。 5 | 在构造过程中,请注意区分大小写。比如 "Aa" 不能当做一个回文字符串。 6 | 7 | 注意: 8 | 假设字符串的长度不会超过 1010。 9 | 10 | 示例 1: 11 | 输入: 12 | `"abccccdd"` 13 | 输出: 14 | 7 15 | 解释: 16 | 我们可以构造的最长的回文串是`"dccaccd"`, 它的长度是 7。 17 | 18 | 来源:力扣(LeetCode) 19 | [链接](https://leetcode-cn.com/problems/longest-palindrome):https://leetcode-cn.com/problems/longest-palindrome 20 | 21 | ### 解法 22 | 23 | ```java 24 | public int longestPalindrome(String s) { 25 | int[] count = new int[58]; // 大小写字母中间还有6个其它字符 26 | for (char c : s.toCharArray()) 27 | count[c - 65] += 1; 28 | int countOdd = 0; 29 | for (int i : count) { 30 | if (i % 2 != 0) 31 | countOdd++; 32 | } 33 | if(countOdd == 0) 34 | return s.length(); 35 | return s.length() - countOdd + 1; 36 | } 37 | ``` 38 | 39 | 思路分析: 40 | 41 | * 本题是可以将所给字符串的所有字母拆开来按我们设置的顺序来构成回文字符串。由回文字符串的定义可以知道,回文字符串具有关于中轴线轴对称的性质。所以回文字符串除了中间字符以外的所有字符都需要成对出现。 42 | * 也就是说,`s`中的某个字符,如果出现了奇数次`t`,那么该字符可以用`t - 1`个去构造回文串(`t - 1`为偶数,成对了);如果出现了偶数次,都可以用于构造回文串。 43 | * 所以,回文串的长度只需要用`s.length()`减去出现了奇数次的字符串的数目再+1。加一是因为回文中心不需要成对出现,可以运行一个字符出现奇数次。不过有一种情况:所有字符都出现了偶数次,那么所有字符都可以用于构造回文串,回文串长度即为`s.length()` 44 | * 由此可以发现,我们需要去判断每一个字母出现了奇数次还是偶数次,这就需要对每个出现字母进行计数。 45 | * 经典方法:对于有限字母,可以用`int[]`代替`HashMap`,(本题只会出现大小写字母)以字符减去`a`为键,出现次数为值。 46 | * `int[]`的长度设定为58是因为,大小写字母之间还有6个其余字符。 47 | * 统计完每个字母的出现次数,就判断有多少个字母出现了奇数次。按前文逻辑`countOdd == 0`,所有字符都出现了偶数次,回文串长度即为`s.length()`。否则回文串的长度为`s.length() - countOdd + 1` 48 | * 时间复杂度为$O(n)$,空间复杂度为$O(1)$。 49 | 50 | 运行结果: 51 | * 执行用时 :1 ms, 在所有 Java 提交中击败了100.00%的用户 52 | * 内存消耗 :41.3 MB, 在所有 Java 提交中击败了5.22%的用户 -------------------------------------------------------------------------------- /easy/Tree/diameterOfBinaryTree543.java: -------------------------------------------------------------------------------- 1 | package easy.Tree; 2 | 3 | /** 4 | * @Time : 2020年2月20日15:03:20 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | import BaseClass.TreeNode; 12 | 13 | /** 14 | * 给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过根结点。 15 | * 示例 : 16 | * 给定二叉树 17 | * 1 18 | * / \ 19 | * 2 3 20 | * / \ 21 | * 4 5 22 | * 返回 3, 它的长度是路径 [4,2,1,3] 或者 [5,2,1,3]。 23 | * 注意:两结点之间的路径长度是以它们之间边的数目表示。 24 | * 25 | * 来源:力扣(LeetCode) 26 | * 链接:https://leetcode-cn.com/problems/diameter-of-binary-tree 27 | */ 28 | public class diameterOfBinaryTree543 { 29 | /** 30 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 31 | * 内存消耗 :38.8 MB, 在所有 Java 提交中击败了 32 | * 题目要求的最长直径,要么是当前结点左右的两条最长路径(到叶子结点)连接在一起,要么就是左右子树中的最大直径 33 | * 所以要求当前结点的最大直径,得先知道其左右子树的信息。于是需要从底往上递归 34 | */ 35 | public static int result; 36 | public int diameterOfBinaryTree(TreeNode root) { 37 | result = 0; 38 | depth(root); 39 | return result; 40 | } 41 | 42 | /** 43 | *计算每个结点的深度(到叶子结点最长路径包含的结点数) 44 | * 在计算过程中 不断地将result变量设置为左右子树种最大直径,然后与当前节点左右最长路径长度和进行比较,更新为当前树的最长路径。 45 | */ 46 | private int depth(TreeNode x){ 47 | if(x == null) return 0; 48 | int leftDepth = depth(x.left); 49 | int rightDepth = depth(x.right); 50 | // leftDepth - 1:左子树高度-1,即可得到左最长的边的长度,同理rightDepth - 1得到右子树最长的边。分别通过当前根结点相连接,边长+2 51 | result = Math.max(result, leftDepth + rightDepth); 52 | return Math.max(leftDepth, rightDepth) + 1; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /easy/Tree/diameterOfBinaryTree543.md: -------------------------------------------------------------------------------- 1 | # 543. 二叉树的直径 2 | 3 | ### 原题 4 | 给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。 5 | 6 | 示例 : 7 | 给定二叉树 8 | 9 | 1 10 | / \ 11 | 2 3 12 | / \ 13 | 4 5 14 | 返回 3, 它的长度是路径 [4,2,1,3] 或者 [5,2,1,3]。 15 | 16 | 注意:两结点之间的路径长度是以它们之间边的数目表示。 17 | 18 | 来源:力扣(LeetCode) 19 | [链接](https://leetcode-cn.com/problems/diameter-of-binary-tree):https://leetcode-cn.com/problems/diameter-of-binary-tree 20 | 21 | ### 解法 22 | 23 | ```java 24 | public static int result; 25 | public int diameterOfBinaryTree(TreeNode root) { 26 | result = 0; 27 | depth(root); 28 | return result; 29 | } 30 | 31 | private int depth(TreeNode x){ 32 | if(x == null) return 0; 33 | int leftDepth = depth(x.left); 34 | int rightDepth = depth(x.right); 35 | // leftDepth - 1:左子树高度-1,即可得到左最长的边的长度,同理rightDepth - 1得到右子树最长的边。分别通过当前根结点相连接,边长+2 36 | result = Math.max(result, leftDepth + rightDepth); 37 | return Math.max(leftDepth, rightDepth) + 1; 38 | } 39 | ``` 40 | 41 | 思路分析: 42 | 43 | * 题目定义,一棵二叉树的直径长度是任意两个结点路径长度中的最大值,并且这条路径可能穿过也可能不穿过根结点。树的题一般都可以考虑一下,如何使用递归。 44 | * 一个直观的想法是,二叉树的直径应该从左子树最深的叶子结点到达根然后再到达右边最深的叶子节点。 45 | * 但是来一个极端的例子,左子树为一个高度100的平衡二叉树,右子树为空,那么显然直径是左子树种过左子树根结点的路径。 46 | * 所以应该修正一下,直径应该是每一个结点的到达其左右最深的叶子结点的路径中最长的那一条。 47 | * 那么如何求左子树最深的叶子结点到达根然后再到达右边最深的叶子节点的路径长度。 48 | * 左子树最深的叶子结点到达根的路径长,就是左子树的高度-1(PS:这里高度是路径的结点数)。根结点同理到达右边最深的叶子节点的路径长,就是右子树的高度-1。 49 | * 然后将根结点两边的路径连接到根结点,由于连接到根,左边使得直径长度+1,右边也一样。所以这条路径长度就等于左子树高度+右子树高度。 50 | * 接下来就是写遍历每一个结点,求其子树高度+右子树高度,然后不断更新最大值。求树高就是一个很常规的递归:树高等于`max{左子树高, 右子树高} + 1`。在遍历过程就可以求其子树高度+右子树高度,然后不断更新最大值`result = Math.max(result, leftDepth + rightDepth);` 51 | * 时间复杂度,由于遍历了每一个结点,所以为$O(1)$,空间复杂度为树高成正比,最坏情况树高为n,最好情况下为logn。 52 | 53 | 运行结果: 54 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 55 | * 内存消耗 :38.8 MB, 在所有 Java 提交中击败了 -------------------------------------------------------------------------------- /easy/Tree/findSecondMinimumValue.java: -------------------------------------------------------------------------------- 1 | package easy.Tree; 2 | 3 | /** 4 | * @Time : 2020年2月29日11:23:53 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | import BaseClass.TreeNode; 12 | 13 | /** 14 | * 给定一个非空特殊的二叉树,每个节点都是正数,并且每个节点的子节点数量只能为 2 或 0。 15 | * 如果一个节点有两个子节点的话,那么这个节点的值不大于它的子节点的值。  16 | * 给出这样的一个二叉树,你需要输出所有节点中的第二小的值。如果第二小的值不存在的话,输出 -1 。 17 | * 18 | * 示例 1: 19 | * 输入: 20 | * 2 21 | * / \ 22 | * 2 5 23 | * / \ 24 | * 5 7 25 | * 输出: 5 26 | * 说明: 最小的值是 2 ,第二小的值是 5 。 27 | * 28 | * 示例 2: 29 | * 输入: 30 | * 2 31 | * / \ 32 | * 2 2 33 | * 输出: -1 34 | * 说明: 最小的值是 2, 但是不存在第二小的值。 35 | * 36 | * 来源:力扣(LeetCode) 37 | * 链接:https://leetcode-cn.com/problems/second-minimum-node-in-a-binary-tree 38 | */ 39 | public class findSecondMinimumValue { 40 | /** 41 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 42 | * 内存消耗 :36.8 MB, 在所有 Java 提交中击败了10%的用户 43 | */ 44 | public int findSecondMinimumValue(TreeNode root) { 45 | if(root == null) 46 | return -1; 47 | return find(root, root.val); 48 | } 49 | 50 | /** 51 | * 按照题目描述,这个二叉树是个最小堆,最上面的元素就是最小的元素。用rootValue表示最小值 52 | * 找到和rootValue值不相同的最小值,与rootValue不相同的最小值其实就是第二小的值。 53 | */ 54 | private int find(TreeNode x, int rootValue){ 55 | if(x.val != rootValue) // 如果当前结点不等于根结点至,那么当x值为以x为根的最小的非rootValue的值 56 | return x.val; 57 | // 这之下都是 当前结点值为根结点值的情况 58 | if(x.left == null) // 递归到叶子结点 且其值为根结点值,说明没有找到第二小的值,返回失败标志-1。 59 | return -1; 60 | int leftMin = find(x.left, rootValue); 61 | int rightMin = find(x.right, rootValue); 62 | if(leftMin == -1) 63 | return rightMin; 64 | if(rightMin == -1) 65 | return leftMin; 66 | return Math.min(leftMin, rightMin); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /easy/Tree/findTarget.java: -------------------------------------------------------------------------------- 1 | package easy.Tree; 2 | 3 | /** 4 | * @Time : 2020年3月1日19:37:15 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 已总结 9 | */ 10 | 11 | import BaseClass.TreeNode; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | /** 17 | * 给定一个二叉搜索树和一个目标结果,如果 BST 中存在两个元素且它们的和等于给定的目标结果,则返回 true。 18 | * 19 | * 案例 1: 20 | * 输入: 21 | * 5 22 | * / \ 23 | * 3 6 24 | * / \ \ 25 | * 2 4 7 26 | * Target = 9 27 | * 输出: True 28 | *   29 | * 案例 2: 30 | * 输入: 31 | * 5 32 | * / \ 33 | * 3 6 34 | * / \ \ 35 | * 2 4 7 36 | * Target = 28 37 | * 输出: False 38 | * 39 | * 来源:力扣(LeetCode) 40 | * 链接:https://leetcode-cn.com/problems/two-sum-iv-input-is-a-bst 41 | */ 42 | public class findTarget { 43 | /** 44 | * 执行用时 :3 ms, 在所有 Java 提交中击败了93.15%的用户 45 | * 内存消耗 :41.5 MB, 在所有 Java 提交中击败了51.69%的用户 46 | */ 47 | private List nums; 48 | public boolean findTarget(TreeNode root, int k) { 49 | nums = new ArrayList<>(); 50 | midTraversal(root); 51 | int i = 0, j = nums.size() - 1; 52 | while(i < j){ 53 | int sum = nums.get(i) + nums.get(j); 54 | if(sum < k) i++; 55 | else if(sum > k) j--; 56 | else return true; 57 | } 58 | return false; 59 | } 60 | 61 | private void midTraversal(TreeNode x){ 62 | if(x == null) 63 | return; 64 | midTraversal(x.left); 65 | nums.add(x.val); 66 | midTraversal(x.right); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /easy/Tree/findTarget.md: -------------------------------------------------------------------------------- 1 | # 653. 两数之和 IV - 输入 BST 2 | 3 | 给定一个二叉搜索树和一个目标结果,如果 BST 中存在两个元素且它们的和等于给定的目标结果,则返回 true。 4 | 5 | 案例 1: 6 | 输入: 7 | 8 | ``` 9 | 5 10 | / \ 11 | 3 6 12 | / \ \ 13 | 2 4 7 14 | ``` 15 | Target = 9 16 | 输出: True 17 | 18 | 案例 2: 19 | 输入: 20 | ``` 21 | 5 22 | / \ 23 | 3 6 24 | / \ \ 25 | 2 4 7 26 | ``` 27 | Target = 28 28 | 输出: False 29 | 30 | 来源:力扣(LeetCode) 31 | [链接](https://leetcode-cn.com/problems/two-sum-iv-input-is-a-bst):https://leetcode-cn.com/problems/two-sum-iv-input-is-a-bst 32 | 33 | ### 解法:中序遍历+双指针 34 | 35 | ```java 36 | private List nums; 37 | public boolean findTarget(TreeNode root, int k) { 38 | nums = new ArrayList<>(); 39 | midTraversal(root); 40 | int i = 0, j = nums.size() - 1; 41 | while(i < j){ 42 | int sum = nums.get(i) + nums.get(j); 43 | if(sum < k) i++; 44 | else if(sum > k) j--; 45 | else return true; 46 | } 47 | return false; 48 | } 49 | 50 | private void midTraversal(TreeNode x){ 51 | if(x == null) 52 | return; 53 | midTraversal(x.left); 54 | nums.add(x.val); 55 | midTraversal(x.right); 56 | } 57 | ``` 58 | 59 | 思路分析: 60 | 61 | * 判断BST中的两个元素是否能加和为一个定值。BST有一个很重要的性质,就是中序遍历BST可以得到一个有序的数组。而有序数组中两个元素是否能加和为`k`,这样与序数组中两个元素和为`k`,几乎没有区别。唯一的区别就是前者可能找不到这样的两个元素。 62 | * [双指针解决有序数组的两数之和问题](https://github.com/ustcyyw/yyw_algorithm/blob/master/easy/TwoPoint/twoSum167.md) 63 | * [二叉树的中序遍历(递归与非递归)](https://github.com/ustcyyw/yyw_algorithm/blob/master/medium/Tree/inorderTraversal.md) 64 | * 有了上述两个问题的基础,我们通过中序遍历先得到有序数组,然后使用双指针。两个元素不能加和为`k`,就是说当两指针已经相遇了依旧没有找到两个满足的元素,就返回`false`。所以第6行,循环条件是`i < j`。循环内部第10行找到了这样的两个元素即返回。 65 | * 时间复杂度,中序遍历与双指针都是$O(n)$,所以这里时间复杂度为$O(2n)$。空间复杂度由于使用了一个List来存放元素,所以为$O(n)$。 66 | 67 | 运行结果: 68 | * 执行用时 :3 ms, 在所有 Java 提交中击败了93.15%的用户 69 | * 内存消耗 :41.5 MB, 在所有 Java 提交中击败了51.69%的用户 -------------------------------------------------------------------------------- /easy/Tree/isSubtree.java: -------------------------------------------------------------------------------- 1 | package easy.Tree; 2 | 3 | import BaseClass.TreeNode; 4 | 5 | /** 6 | * @Time : 2020年2月28日15:08:53 7 | * @Author : yyw@ustc 8 | * @E-mail : yang0@mail.ustc.edu.cn 9 | * @Github : https://github.com/ustcyyw 10 | * @desc : 已总结 11 | */ 12 | 13 | /** 14 | * 给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和节点值的子树。 15 | * s 的一个子树包括 s 的一个节点和这个节点的所有子孙。s 也可以看做它自身的一棵子树。 16 | * 17 | * 示例 1: 18 | * 给定的树 s: 19 | * 3 20 | * / \ 21 | * 4 5 22 | * / \ 23 | * 1 2 24 | * 给定的树 t: 25 | * 4 26 | * / \ 27 | * 1 2 28 | * 返回 true,因为 t 与 s 的一个子树拥有相同的结构和节点值。 29 | * 30 | * 示例 2: 31 | * 给定的树 s: 32 | * 3 33 | * / \ 34 | * 4 5 35 | * / \ 36 | * 1 2 37 | * / 38 | * 0 39 | * 给定的树 t: 40 | * 4 41 | * / \ 42 | * 1 2 43 | * 返回 false。 44 | * 45 | * 来源:力扣(LeetCode) 46 | * 链接:https://leetcode-cn.com/problems/subtree-of-another-tree 47 | */ 48 | public class isSubtree { 49 | /** 50 | * 执行用时 :7 ms, 在所有 Java 提交中击败了94.26%的用户 51 | * 内存消耗 :40.7 MB, 在所有 Java 提交中击败了7.38%的用户 52 | */ 53 | public boolean isSubtree(TreeNode s, TreeNode t) { 54 | if(s == null) return false; 55 | if(isSame(s, t)) 56 | return true; 57 | else 58 | return isSubtree(s.left, t) || isSubtree(s.right, t); 59 | } 60 | 61 | private boolean isSame(TreeNode p, TreeNode q){ 62 | if(q == null && p == null) return true; 63 | if(q == null || p == null) return false; 64 | if(q.val != p.val) return false; 65 | else return isSame(q.left, p.left) && isSame(q.right, p.right); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /easy/Tree/lowestCommonAncestor.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/easy/Tree/lowestCommonAncestor.md -------------------------------------------------------------------------------- /easy/Tree/maxDepth106.java: -------------------------------------------------------------------------------- 1 | package easy.Tree; 2 | 3 | /** 4 | * @Time : 2020年2月19日23:18:58 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | import BaseClass.TreeNode; 12 | import javafx.util.Pair; 13 | 14 | import java.util.Stack; 15 | 16 | /** 17 | * 给定一个二叉树,找出其最大深度。 18 | * 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。 19 | * 说明: 叶子节点是指没有子节点的节点。 20 | * 21 | * 示例: 22 | * 给定二叉树 [3,9,20,null,null,15,7], 23 | * 3 24 | * / \ 25 | * 9 20 26 | * / \ 27 | * 15 7 28 | * 返回它的最大深度 3 。 29 | * 30 | * 来源:力扣(LeetCode) 31 | * 链接:https://leetcode-cn.com/problems/maximum-depth-of-binary-tree 32 | */ 33 | public class maxDepth106 { 34 | /** 35 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 36 | * 内存消耗 :39 MB, 在所有 Java 提交中击败了12.47%的用户 37 | */ 38 | public int maxDepth(TreeNode root) { 39 | if(root == null) return 0; 40 | return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1; 41 | } 42 | 43 | /** 44 | * 迭代 DFS 45 | *参考官方答案 BFS 其实也行,因为都是遍历了每个结点的深度 46 | */ 47 | public int maxDepth2(TreeNode root) { 48 | if(root == null) return 0; 49 | int result = 0; 50 | Stack> stack = new Stack<>(); 51 | stack.push(new Pair<>(root, 1)); 52 | while(!stack.empty()){ 53 | Pair curPair = stack.pop(); 54 | TreeNode curNode = curPair.getKey(); 55 | int curDepth = curPair.getValue(); 56 | if(curNode != null){ 57 | result = Math.max(result, curDepth); 58 | stack.push(new Pair<>(curNode.left, curDepth + 1)); // 从添加顺序看,是先走右子树到底部 59 | stack.push(new Pair<>(curNode.right, curDepth + 1)); 60 | } 61 | } 62 | return result; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /easy/Tree/pathSum437.java: -------------------------------------------------------------------------------- 1 | package easy.Tree; 2 | 3 | /** 4 | * @Time : 2020年2月21日21:58:25 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 已总结 9 | */ 10 | 11 | import BaseClass.TreeNode; 12 | 13 | /** 14 | * 给定一个二叉树,它的每个结点都存放着一个整数值。 15 | * 找出路径和等于给定数值的路径总数。 16 | * 路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。 17 | * 二叉树不超过1000个节点,且节点数值范围是 [-1000000,1000000] 的整数。 18 | *

19 | * 示例: 20 | * root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8 21 | * 10 22 | * / \ 23 | * 5 -3 24 | * / \ \ 25 | * 3 2 11 26 | * / \ \ 27 | * 3 -2 1 28 | *

29 | * 返回 3。和等于 8 的路径有: 30 | * 1. 5 -> 3 31 | * 2. 5 -> 2 -> 1 32 | * 3. -3 -> 11 33 | *

34 | * 来源:力扣(LeetCode) 35 | * 链接:https://leetcode-cn.com/problems/path-sum-iii 36 | */ 37 | public class pathSum437 { 38 | /** 39 | * 评论区大佬的解法 40 | * 3~4秒就解决了 41 | */ 42 | private int count; 43 | public int pathSum(TreeNode root, int sum) { 44 | count = 0; 45 | backTrack(root, sum, new int[1000], 0); 46 | return count; 47 | } 48 | 49 | public void backTrack(TreeNode x, int sum, int[] pathItem, int curIndex){ 50 | if(x == null) return; 51 | 52 | if(x.val == sum) count++; 53 | for(int i = curIndex - 1, temp = x.val; i >= 0; i--){ 54 | temp += pathItem[i]; 55 | if(temp == sum) 56 | count++; 57 | } 58 | 59 | pathItem[curIndex] = x.val; 60 | backTrack(x.left, sum, pathItem, curIndex + 1); 61 | backTrack(x.right, sum, pathItem, curIndex + 1); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /easy/Tree/sortedArrayToBST.java: -------------------------------------------------------------------------------- 1 | package easy.Tree; 2 | 3 | /** 4 | * @Time : 2020年3月1日18:35:25 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | import BaseClass.TreeNode; 12 | 13 | /** 14 | * 将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。 15 | * 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。 16 | * 17 | * 示例: 18 | * 给定有序数组: [-10,-3,0,5,9], 19 | * 一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树: 20 | * 0 21 | * / \ 22 | * -3 9 23 | * / / 24 | * -10 5 25 | * 26 | * 来源:力扣(LeetCode) 27 | * 链接:https://leetcode-cn.com/problems/convert-sorted-array-to-binary-search-tree 28 | */ 29 | public class sortedArrayToBST { 30 | /** 31 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 32 | * 内存消耗 :41.3 MB, 在所有 Java 提交中击败了5.03%的用户 33 | */ 34 | public TreeNode sortedArrayToBST(int[] nums) { 35 | int lo = 0, hi = nums.length - 1; 36 | return create(nums, lo, hi); 37 | } 38 | 39 | private TreeNode create(int[] nums, int lo, int hi){ 40 | if(lo > hi) return null; 41 | if(lo == hi) return new TreeNode(nums[lo]); 42 | int mid = (hi - lo) / 2 + lo; 43 | TreeNode temp = new TreeNode(nums[mid]); 44 | temp.left = create(nums, lo, mid - 1); 45 | temp.right = create(nums, mid + 1, hi); 46 | return temp; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /easy/Tree/trimBST.java: -------------------------------------------------------------------------------- 1 | package easy.Tree; 2 | 3 | /** 4 | * @Time : 2020年2月29日14:18:01 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | import BaseClass.TreeNode; 12 | 13 | /** 14 | * 给定一个二叉搜索树,同时给定最小边界L 和最大边界 R。通过修剪二叉搜索树,使得所有节点的值在[L, R]中 (R>=L) 。 15 | * 你可能需要改变树的根节点,所以结果应当返回修剪好的二叉搜索树的新的根节点。 16 | * 17 | * 示例 1: 18 | * 输入: 19 | * 1 20 | * / \ 21 | * 0 2 22 | * L = 1 23 | * R = 2 24 | * 输出: 25 | * 1 26 | * \ 27 | * 2 28 | * 29 | * 示例 2: 30 | * 输入: 31 | * 3 32 | * / \ 33 | * 0 4 34 | * \ 35 | * 2 36 | * / 37 | * 1 38 | * L = 1 39 | * R = 3 40 | * 输出: 41 | * 3 42 | * / 43 | * 2 44 | * / 45 | * 1 46 | * 47 | * 来源:力扣(LeetCode) 48 | * 链接:https://leetcode-cn.com/problems/trim-a-binary-search-tree 49 | */ 50 | public class trimBST { 51 | /** 52 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 53 | * 内存消耗 :39.5 MB, 在所有 Java 提交中击败了9.09%的用户 54 | * 返回以当前结点为根的已修剪好的数的根结点(可能根结点变化了)。 55 | */ 56 | public TreeNode trimBST(TreeNode root, int L, int R) { 57 | if(root == null) return null; 58 | if(root.val < L) return trimBST(root.right, L, R); 59 | if(root.val > R) return trimBST(root.left, L, R); 60 | root.left = trimBST(root.left, L, R); // 涉及到改变树结构的,肯定要改变链接 61 | root.right = trimBST(root.right, L, R); // 通过这两个链接更新,当前结点的左右子树都是修剪过的了。 62 | return root; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /easy/Tree/trimBST.md: -------------------------------------------------------------------------------- 1 | # 669. 修剪二叉搜索树 2 | 3 | ### 原题 4 | 给定一个二叉搜索树,同时给定最小边界L 和最大边界 R。通过修剪二叉搜索树,使得所有节点的值在[L, R]中 (R>=L) 。你可能需要改变树的根节点,所以结果应当返回修剪好的二叉搜索树的新的根节点。 5 | 6 | 示例 1: 7 | 输入: 8 | ``` 9 | 1 10 | / \ 11 | 0 2 12 | ``` 13 | L = 1 14 | R = 2 15 | 输出: 16 | ``` 17 | 1 18 | \ 19 | 2 20 | ``` 21 | 22 | 示例 2: 23 | 输入: 24 | ``` 25 | 3 26 | / \ 27 | 0 4 28 | \ 29 | 2 30 | / 31 | 1 32 | ``` 33 | L = 1 34 | R = 3 35 | 36 | 输出: 37 | ``` 38 | 3 39 | / 40 | 2 41 | / 42 | 1 43 | ``` 44 | 45 | 来源:力扣(LeetCode) 46 | [链接](https://leetcode-cn.com/problems/trim-a-binary-search-tre):https://leetcode-cn.com/problems/trim-a-binary-search-tree 47 | 48 | ### 解法 49 | 50 | ```java 51 | public TreeNode trimBST(TreeNode root, int L, int R) { 52 | if(root == null) return null; 53 | if(root.val < L) return trimBST(root.right, L, R); 54 | if(root.val > R) return trimBST(root.left, L, R); 55 | root.left = trimBST(root.left, L, R); // 涉及到改变树结构的,肯定要改变链接 56 | root.right = trimBST(root.right, L, R); // 通过这两个链接更新,当前结点的左右子树都是修剪过的了。 57 | return root; 58 | } 59 | ``` 60 | 61 | 思路分析: 62 | 63 | * 修剪一棵树,如果根结点的值小于给定的左边界`L`,那么当前结点及其左子树就会被修剪掉,修剪后的树应该是其右子树,但是右子树不一定是符合范围的树,所以要对其右子树叶进行修剪,然后返回修剪后的右子树。 64 | * 同理,根结点的值大于给定的右边界`R`,修剪后的树应该是其左子树且要对左子树修剪。 65 | * 涉及到改变树的结构,就需要更新链接,如果当前结点值在范围内,那么修建其左右子树,并且更新左右链接。最后将当前修剪好的子树返回。 66 | * 时间复杂度为$O(n)$, 空间复杂度与树高成正比。 67 | 68 | 运行结果: 69 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 70 | * 内存消耗 :39.5 MB, 在所有 Java 提交中击败了9.09%的用户 71 | 72 | -------------------------------------------------------------------------------- /easy/TwoPoint/findContinuousSequence.java: -------------------------------------------------------------------------------- 1 | package easy.TwoPoint; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * @Time : 2020年3月6日08:58:05 8 | * @Author : yyw@ustc 9 | * @E-mail : yang0@mail.ustc.edu.cn 10 | * @Github : https://github.com/ustcyyw 11 | * @desc : 12 | */ 13 | 14 | /** 15 | * 输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。 16 | * 序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。 17 | * 18 | * 示例 1: 19 | * 输入:target = 9 20 | * 输出:[[2,3,4],[4,5]] 21 | * 22 | * 示例 2: 23 | * 输入:target = 15 24 | * 输出:[[1,2,3,4,5],[4,5,6],[7,8]] 25 | *   26 | * 27 | * 限制: 28 | * 1 <= target <= 10^5 29 | * 30 | * 来源:力扣(LeetCode) 31 | * 链接:https://leetcode-cn.com/problems/he-wei-sde-lian-xu-zheng-shu-xu-lie-lcof 32 | */ 33 | public class findContinuousSequence { 34 | /** 35 | * 执行用时 :4 ms, 在所有 Java 提交中击败了75.32%的用户 36 | * 内存消耗 :37.4 MB, 在所有 Java 提交中击败了100.00%的用户 37 | * 一个指针指向参与相加的最小值,另外一个指针指向参与相加的最大值 38 | */ 39 | public int[][] findContinuousSequence(int target) { 40 | if(target == 2) return null; 41 | List lists = new ArrayList<>(); 42 | int min = 1, max = 2; 43 | int sum = 3; 44 | while(min <= target / 2){ 45 | if(sum > target){ 46 | sum -= min; 47 | min++; 48 | } 49 | else{ 50 | if(sum == target) 51 | lists.add(getOneArray(min, max)); 52 | max++; 53 | sum += max; 54 | } 55 | } 56 | return lists.toArray(new int[0][]); 57 | } 58 | 59 | private int[] getOneArray(int lo, int hi){ 60 | int[] res = new int[hi - lo + 1]; 61 | for(int i = lo; i <= hi; i++){ 62 | res[i - lo] = i; 63 | } 64 | return res; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /easy/TwoPoint/isHappy.java: -------------------------------------------------------------------------------- 1 | package easy.TwoPoint; 2 | 3 | /** 4 | * @Time : 2020年4月30日00:28:29 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 已总结 9 | */ 10 | 11 | import java.util.HashSet; 12 | import java.util.Set; 13 | 14 | /** 15 | * 编写一个算法来判断一个数 n 是不是快乐数。 16 | * 「快乐数」定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和, 17 | * 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。如果 可以变为  1,那么这个数就是快乐数。 18 | * 如果 n 是快乐数就返回 True ;不是,则返回 False 。 19 | *

20 | * 示例: 21 | * 输入:19 22 | * 输出:true 23 | * 解释: 24 | * 12 + 92 = 82 25 | * 82 + 22 = 68 26 | * 62 + 82 = 100 27 | * 12 + 02 + 02 = 1 28 | *

29 | * 来源:力扣(LeetCode) 30 | * 链接:https://leetcode-cn.com/problems/happy-number 31 | */ 32 | public class isHappy { 33 | /** 34 | * 执行用时 :2 ms, 在所有 Java 提交中击败了51.98%的用户 35 | * 内存消耗 :36.6 MB, 在所有 Java 提交中击败了8.33%的用户 36 | */ 37 | public boolean isHappy(int n) { 38 | Set set = new HashSet<>(); 39 | set.add(n); 40 | while (true){ 41 | n = create(n); 42 | if(n == 1) 43 | return true; 44 | if(set.contains(n)) 45 | return false; 46 | set.add(n); 47 | } 48 | } 49 | 50 | private int create(int n) { 51 | int sum = 0; 52 | while (n != 0){ 53 | sum += n % 10 * (n % 10); 54 | n /= 10; 55 | } 56 | return sum; 57 | } 58 | 59 | /** 60 | * 判断循环使用快慢指针指针,如同链表成环的判断。 61 | * 执行用时 :1 ms, 在所有 Java 提交中击败了99.89%的用户 62 | * 内存消耗 :36.4 MB, 在所有 Java 提交中击败了8.33%的用户 63 | */ 64 | public boolean isHappy2(int n){ 65 | int slow = n, fast = create(n); 66 | while(fast != 1){ 67 | if(fast == slow) return false; // 快指针等于慢指针,这个说明在计算过程中循环了,数字之间构成了环。 68 | fast = create(create(fast)); 69 | slow = create(slow); 70 | } 71 | return true; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /easy/TwoPoint/isHappy.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/easy/TwoPoint/isHappy.md -------------------------------------------------------------------------------- /easy/TwoPoint/twoSum167.java: -------------------------------------------------------------------------------- 1 | package easy.TwoPoint; 2 | 3 | /** 4 | * @Time : 2020年3月2日11:49:03 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 已总结 9 | */ 10 | 11 | /** 12 | * 给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。 13 | * 函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。 14 | * 15 | * 说明: 16 | * 返回的下标值(index1 和 index2)不是从零开始的。 17 | * 你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。 18 | * 19 | * 示例: 20 | * 输入: numbers = [2, 7, 11, 15], target = 9 21 | * 输出: [1,2] 22 | * 解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。 23 | * 24 | * 来源:力扣(LeetCode) 25 | * 链接:https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted 26 | */ 27 | public class twoSum167 { 28 | /** 29 | * 执行用时 :1 ms, 在所有 Java 提交中击败了98.37%的用户 30 | * 内存消耗 :42.3 MB, 在所有 Java 提交中击败了5.06%的用户 31 | */ 32 | public int[] twoSum(int[] numbers, int target) { 33 | int[] res = new int[2]; 34 | for(int i = 0, j = numbers.length - 1; i < j; ){ 35 | int sum = numbers[i] + numbers[j]; 36 | if(sum > target) j--; 37 | else if(sum < target) i++; 38 | else { 39 | res[0] = i + 1; 40 | res[1] = j + 1; 41 | break; 42 | } 43 | } 44 | return res; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /easy/TwoPoint/twoSum167.md: -------------------------------------------------------------------------------- 1 | # 167.两数之和-输入有序数组 2 | 3 | ### 原题 4 | 给定一个已按照 **升序排列的有序数组**,找到两个数使得它们相加之和等于目标数。 5 | 函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。 6 | 7 | 说明: 8 | 返回的下标值(index1 和 index2)不是从零开始的。 9 | 你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。 10 | 11 | 示例: 12 | 输入: numbers = [2, 7, 11, 15], target = 9 13 | 输出: [1,2] 14 | 解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。 15 | 16 | 来源:力扣(LeetCode) 17 | [链接](https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted):https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted 18 | 19 | ### 解法 双指针 20 | 21 | ```java 22 | public int[] twoSum(int[] numbers, int target) { 23 | int[] res = new int[2]; 24 | for(int i = 0, j = numbers.length - 1; i < j; ){ 25 | int sum = numbers[i] + numbers[j]; 26 | if(sum > target) j--; 27 | else if(sum < target) i++; 28 | else { 29 | res[0] = i + 1; 30 | res[1] = j + 1; 31 | break; 32 | } 33 | } 34 | return res; 35 | } 36 | ``` 37 | 38 | 思路分析: 39 | 40 | * 题目中的重点在于,一个有序数组。这与无序数组中找两个元素加和为`target`肯定是不同的。要充分利用有序这个条件。 41 | * 如果两个大的数相加,大于`target`,不知道将哪个替换为更小才有可能是答案;如果两个小的数相加,小于`target`,不知道将哪个替换为更大的才有可能是答案。 42 | * 但是如果使用双指针,一个指向小元素,一个指向大元素。那么相加大于`target`时,肯定是因为大的元素太大了,要换一个稍微小一点的。相加小于`target`时,肯定是因为小的元素不够大,要换一个稍微大一点的。为什么不是大的元素换大一点的呢?因为之前已经判断过了,才会将大元素指针移到这。 43 | 44 | * 时间复杂度,只有一次遍历,所以是$O(n)$的。空间复杂度是$O(1)$。 45 | 46 | 运行结果: 47 | * 执行用时 :1 ms, 在所有 Java 提交中击败了98.37%的用户 48 | * 内存消耗 :42.3 MB, 在所有 Java 提交中击败了5.06%的用户 -------------------------------------------------------------------------------- /easy/other/isRectangleOverlap.java: -------------------------------------------------------------------------------- 1 | package easy.other; 2 | 3 | /** 4 | * @Time : 2020年3月18日11:20:24 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | /** 12 | * 矩形以列表 [x1, y1, x2, y2] 的形式表示,其中 (x1, y1) 为左下角的坐标,(x2, y2) 是右上角的坐标。 13 | * 如果相交的面积为正,则称两矩形重叠。需要明确的是,只在角或边接触的两个矩形不构成重叠。 14 | * 给出两个矩形,判断它们是否重叠并返回结果。 15 | * 16 | * 示例 1: 17 | * 输入:rec1 = [0,0,2,2], rec2 = [1,1,3,3] 18 | * 输出:true 19 | * 20 | * 示例 2: 21 | * 输入:rec1 = [0,0,1,1], rec2 = [1,0,2,1] 22 | * 输出:false 23 | * 24 | * 说明: 25 | * 两个矩形 rec1 和 rec2 都以含有四个整数的列表的形式给出。 26 | * 矩形中的所有坐标都处于 -10^9 和 10^9 之间。 27 | * 28 | * 来源:力扣(LeetCode) 29 | * 链接:https://leetcode-cn.com/problems/rectangle-overlap 30 | 31 | */ 32 | public class isRectangleOverlap { 33 | /** 34 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 35 | * 内存消耗 :37.3 MB, 在所有 Java 提交中击败了5.49%的用户 36 | */ 37 | public boolean isRectangleOverlap(int[] rec1, int[] rec2) { 38 | return rec2[0] < rec1[2] && rec2[2] > rec1[0] && rec2[1] < rec1[3] && rec2[3] > rec1[1]; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /easy/other/isRectangleOverlap.md: -------------------------------------------------------------------------------- 1 | # 836. 矩形重叠 2 | 矩形以列表 `[x1, y1, x2, y2]` 的形式表示,其中 `(x1, y1)` 为左下角的坐标,`(x2, y2)` 是右上角的坐标。 3 | 如果相交的面积为正,则称两矩形重叠。需要明确的是,只在角或边接触的两个矩形不构成重叠。 4 | 给出两个矩形,判断它们是否重叠并返回结果。 5 | **示例 1:** 6 | 7 | ``` 8 | 输入:rec1 = [0,0,2,2], rec2 = [1,1,3,3] 9 | 输出:true 10 | ``` 11 | **示例 2:** 12 | 13 | ``` 14 | 输入:rec1 = [0,0,1,1], rec2 = [1,0,2,1] 15 | 输出:false 16 | ``` 17 | **提示:** 18 | 1. 两个矩形 `rec1` 和 `rec2` 都以含有四个整数的列表的形式给出。 19 | 2. 矩形中的所有坐标都处于 `-10^9` 和 `10^9` 之间。 20 | 3. `x` 轴默认指向右,`y` 轴默认指向上。 21 | 4. 你可以仅考虑矩形是正放的情况。 22 | 23 | [原题链接](https://leetcode-cn.com/problems/rectangle-overlap/): https://leetcode-cn.com/problems/rectangle-overlap/ 24 | 25 | ### 解法:一行代码 26 | 27 | ```java 28 | public boolean isRectangleOverlap(int[] rec1, int[] rec2) { 29 | return rec2[0] < rec1[2] && rec2[2] > rec1[0] && rec2[1] < rec1[3] && rec2[3] > rec1[1]; 30 | } 31 | ``` 32 | 33 | 思路分析: 34 | 35 | * 要看两个矩阵是否重叠,如果人为地选定一个矩形固定,我们考虑另外一个矩形与其相对位置就可以。 36 | * 接下来看图,看不重叠的情况与重叠情况,坐标之间有什么关系。 37 | 38 | ![isRectangleOverlap图示.png](https://github.com/ustcyyw/yyw_algorithm/blob/master/easy/other/isRectangleOverlap%E5%9B%BE%E7%A4%BA.png?raw=true) 39 | 40 | * 前四个图,就是四种不交叠的情况,其他的都可以看做这四种的特殊情况。 41 | * 矩形2的下底还在矩形1上底的下方(或重合),对应到题目坐标就是`rec2[1] >= rec1[3]` 42 | * 矩形2的上底还在矩形1上底的下方(或重合),对应到题目的坐标是`rec2[3] <= rec1[1]` 43 | * 矩形2的右边还在矩形1左边的左面(或重合) 44 | * 矩形2的左边还在矩形1右边的右面(或重合) 45 | * 不交叠情况只要满足其一就可以,所以用逻辑 或 进行连接就行。 46 | * 后四个图列举了一部分交叠的情况,要同时不满足上述不交叠的情况,所以要用逻辑且链接。`rec2[0] < rec1[2] && rec2[2] > rec1[0] && rec2[1] < rec1[3] && rec2[3] > rec1[1]` 47 | 48 | 运行结果: 49 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 50 | * 内存消耗 :37.3 MB, 在所有 Java 提交中击败了5.49%的用户 -------------------------------------------------------------------------------- /easy/other/isRectangleOverlap图示.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/easy/other/isRectangleOverlap图示.png -------------------------------------------------------------------------------- /easy/other/judgeSquareSum633.java: -------------------------------------------------------------------------------- 1 | package easy.other; 2 | 3 | /** 4 | * @Time : 2020年2月26日00:00:17 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | import java.util.HashSet; 12 | 13 | /** 14 | * 给定一个非负整数 c ,你要判断是否存在两个整数 a 和 b,使得 a2 + b2 = c。 15 | *

16 | * 示例1: 17 | *

18 | * 输入: 5 19 | * 输出: True 20 | * 解释: 1 * 1 + 2 * 2 = 5 21 | *   22 | *

23 | * 示例2: 24 | *

25 | * 输入: 3 26 | * 输出: False 27 | *

28 | * 来源:力扣(LeetCode) 29 | * 链接:https://leetcode-cn.com/problems/sum-of-square-numbers 30 | */ 31 | public class judgeSquareSum633 { 32 | /** 33 | * 执行用时 :5 ms, 在所有 Java 提交中击败了36.36%的用户 34 | * 内存消耗 :36.3 MB, 在所有 Java 提交中击败了5.18%的用户 35 | */ 36 | public boolean judgeSquareSum(int c) { 37 | int end = (int) Math.sqrt(c); 38 | for (int i = 0; i <= end; i++) { 39 | double temp = Math.sqrt(c - i * i); 40 | if (temp == (int) temp) 41 | return true; 42 | } 43 | return false; 44 | } 45 | 46 | /** 47 | * 其它人解法 48 | */ 49 | public boolean judgeSquareSum2(int c){ 50 | int end = (int) Math.sqrt(c); 51 | int start = 0; 52 | while(start <= end){ 53 | int temp = start * start + end * end; 54 | if(temp == c) 55 | return true; 56 | else if(temp < c) 57 | start++; 58 | else end--; 59 | } 60 | return false; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /hard/Greedy/jump.java: -------------------------------------------------------------------------------- 1 | package hard.Greedy; 2 | 3 | /** 4 | * @Time : 2020年5月4日14:18:42 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 已总结 9 | */ 10 | 11 | /** 12 | * 给定一个非负整数数组,你最初位于数组的第一个位置。 13 | * 数组中的每个元素代表你在该位置可以跳跃的最大长度。 14 | * 你的目标是使用最少的跳跃次数到达数组的最后一个位置。 15 | * 16 | * 示例: 17 | * 输入: [2,3,1,1,4] 18 | * 输出: 2 19 | * 解释: 跳到最后一个位置的最小跳跃数是 2。 20 | *   从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。 21 | * 22 | * 说明: 23 | * 假设你总是可以到达数组的最后一个位置。 24 | * 25 | * 来源:力扣(LeetCode) 26 | * 链接:https://leetcode-cn.com/problems/jump-game-ii。 27 | */ 28 | public class jump { 29 | /** 30 | * 执行用时 :2 ms, 在所有 Java 提交中击败了94.93%的用户 31 | * 内存消耗 :41.3 MB, 在所有 Java 提交中击败了5.00%的用户 32 | */ 33 | public int jump(int[] nums) { 34 | int n = nums.length; 35 | int[] count = new int[n]; 36 | int start = 0; 37 | for(int i = 0; i < n && start < n; i++){ 38 | if(i + nums[i] < start) 39 | continue; 40 | for(int j = start; j < n && j <= i + nums[i]; j++) 41 | count[j] = count[i] + 1; 42 | start = i + nums[i] + 1; 43 | } 44 | return count[n - 1] - 1; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /hard/Greedy/jump.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/hard/Greedy/jump.md -------------------------------------------------------------------------------- /hard/Greedy/jump图示.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/hard/Greedy/jump图示.png -------------------------------------------------------------------------------- /medium/ArrayAndMatrix/checkSubarraySum.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/medium/ArrayAndMatrix/checkSubarraySum.md -------------------------------------------------------------------------------- /medium/ArrayAndMatrix/constructArray.java: -------------------------------------------------------------------------------- 1 | package medium.ArrayAndMatrix; 2 | 3 | /** 4 | * @Time : 2020年2月13日17:28:23 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 已总结 9 | */ 10 | 11 | /** 12 | * 给定两个整数 n 和 k,你需要实现一个数组,这个数组包含从 1 到 n 的 n 个不同整数,同时满足以下条件: 13 | * ① 如果这个数组是 [a1, a2, a3, ... , an] ,那么数组 [|a1 - a2|, |a2 - a3|, |a3 - a4|, ... , |an-1 - an|] 中应该有且仅有 k 个不同整数;. 14 | * ② 如果存在多种答案,你只需实现并返回其中任意一种. 15 | *

16 | * 示例 1: 17 | * 输入: n = 3, k = 1 18 | * 输出: [1, 2, 3] 19 | * 解释: [1, 2, 3] 包含 3 个范围在 1-3 的不同整数, 并且 [1, 1] 中有且仅有 1 个不同整数 : 1 20 | *   21 | * 示例 2: 22 | * 输入: n = 3, k = 2 23 | * 输出: [1, 3, 2] 24 | * 解释: [1, 3, 2] 包含 3 个范围在 1-3 的不同整数, 并且 [2, 1] 中有且仅有 2 个不同整数: 1 和 2 25 | *

26 | * 来源:力扣(LeetCode) 27 | * 链接:https://leetcode-cn.com/problems/beautiful-arrangement-ii 28 | */ 29 | public class constructArray { 30 | /** 31 | * 执行用时 :1 ms, 在所有 Java 提交中击败了100.00%的用户 32 | * 内存消耗 :44.8 MB, 在所有 Java 提交中击败了5.34%的用户 33 | */ 34 | public int[] constructArray(int n, int k) { 35 | int[] result = new int[n]; 36 | result[0] = n; 37 | for (int i = k + 1, temp = n - k - 1; i < n; i++, temp--) 38 | result[i] = temp; 39 | for (int i = 1, flag = -1, temp = k; i < k + 1; i++) { 40 | result[i] = result[i - 1] + temp * flag; 41 | flag = -1 * flag; 42 | temp--; 43 | } 44 | return result; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /medium/ArrayAndMatrix/constructArray.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/medium/ArrayAndMatrix/constructArray.md -------------------------------------------------------------------------------- /medium/ArrayAndMatrix/kthSmallest.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/medium/ArrayAndMatrix/kthSmallest.md -------------------------------------------------------------------------------- /medium/ArrayAndMatrix/kthSmallest图示.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/medium/ArrayAndMatrix/kthSmallest图示.png -------------------------------------------------------------------------------- /medium/ArrayAndMatrix/longestWPI.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/medium/ArrayAndMatrix/longestWPI.md -------------------------------------------------------------------------------- /medium/ArrayAndMatrix/maxChunksToSorted.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/medium/ArrayAndMatrix/maxChunksToSorted.md -------------------------------------------------------------------------------- /medium/ArrayAndMatrix/maxChunksToSorted图示.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/medium/ArrayAndMatrix/maxChunksToSorted图示.png -------------------------------------------------------------------------------- /medium/ArrayAndMatrix/nextPermutation.java: -------------------------------------------------------------------------------- 1 | package medium.ArrayAndMatrix; 2 | 3 | /** 4 | * @Time : 2020年3月6日12:03:55 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 已总结 9 | */ 10 | 11 | import java.util.Arrays; 12 | 13 | /** 14 | * 实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。 15 | * 如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。 16 | * 必须原地修改,只允许使用额外常数空间。 17 | *

18 | * 以下是一些例子,输入位于左侧列,其相应输出位于右侧列。 19 | * 1,2,3 → 1,3,2 20 | * 3,2,1 → 1,2,3 21 | * 1,1,5 → 1,5,1 22 | *

23 | * 来源:力扣(LeetCode) 24 | * 链接:https://leetcode-cn.com/problems/next-permutation 25 | */ 26 | public class nextPermutation { 27 | /** 28 | * 执行用时 :1 ms, 在所有 Java 提交中击败了99.86%的用户 29 | * 内存消耗 :39 MB, 在所有 Java 提交中击败了30.38%的用户 30 | * 要得到下一个更大的数字而且该数字要尽量小,就需要尽量在低位换一个稍大的数字过来。 31 | * 比如通过改变第i位来使数字更大,交换的数字得从i+1,i+2...找。往i-1,i-2找会使得数字更小。 32 | * 要找一个更大的数字,而且要让这个更大的数字尽可能的小,那么就在 i+1,i+2...中找比i大的最小值: 33 | * 如果找不到这样的值,说明i+1,i+2...后面的数字都比i小,不满足题意。 34 | * 如果找到了,进行交换。交换之后,只能保证从0到i位是尽可能小的,i+1,i+2...还不一定,要进行排序才可以。 35 | * 从低位开始,就是从数组右侧开始遍历。 36 | */ 37 | public void nextPermutation(int[] nums) { 38 | int n = nums.length - 1; 39 | for (int i = n - 1; i >= 0; i--) { 40 | if (nums[i] < nums[i + 1]) { 41 | int j = nums.length - 1; 42 | for (; j > i && nums[j] <= nums[i]; j--) ; 43 | exch(nums, i, j); 44 | reverse(nums, i + 1, n); 45 | return; 46 | } 47 | } 48 | reverse(nums, 0, n); 49 | } 50 | 51 | private void exch(int[] nums, int i, int j) { 52 | int temp = nums[i]; 53 | nums[i] = nums[j]; 54 | nums[j] = temp; 55 | } 56 | 57 | private void reverse(int[] nums, int lo, int hi){ 58 | while(lo < hi){ 59 | exch(nums, lo++, hi--); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /medium/ArrayAndMatrix/nextPermutation.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/medium/ArrayAndMatrix/nextPermutation.md -------------------------------------------------------------------------------- /medium/ArrayAndMatrix/searchMatrix.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/medium/ArrayAndMatrix/searchMatrix.md -------------------------------------------------------------------------------- /medium/ArrayAndMatrix/searchMatrix图示.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/medium/ArrayAndMatrix/searchMatrix图示.png -------------------------------------------------------------------------------- /medium/BFSorDFS/findCircleNum.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/medium/BFSorDFS/findCircleNum.md -------------------------------------------------------------------------------- /medium/BFSorDFS/ladderLength.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/medium/BFSorDFS/ladderLength.md -------------------------------------------------------------------------------- /medium/BFSorDFS/ladderLength.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/medium/BFSorDFS/ladderLength.png -------------------------------------------------------------------------------- /medium/BFSorDFS/pacificAtlantic.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/medium/BFSorDFS/pacificAtlantic.md -------------------------------------------------------------------------------- /medium/BFSorDFS/shortestPathBinaryMatrix.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/medium/BFSorDFS/shortestPathBinaryMatrix.md -------------------------------------------------------------------------------- /medium/BFSorDFS/updateMatrix.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/medium/BFSorDFS/updateMatrix.md -------------------------------------------------------------------------------- /medium/BackTracking/combinationSum3.java: -------------------------------------------------------------------------------- 1 | package medium.Backtracking; 2 | 3 | /** 4 | * @Time : 2020年2月24日15:55:21 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 已总结 9 | */ 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | /** 15 | * 找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。 16 | * 说明: 17 | * 所有数字都是正整数。 18 | * 解集不能包含重复的组合。  19 | * 示例 1: 20 | * 输入: k = 3, n = 7 21 | * 输出: [[1,2,4]] 22 | * 示例 2: 23 | * 输入: k = 3, n = 9 24 | * 输出: [[1,2,6], [1,3,5], [2,3,4]] 25 | *

26 | * 来源:力扣(LeetCode) 27 | * 链接:https://leetcode-cn.com/problems/combination-sum-iii 28 | */ 29 | public class combinationSum3 { 30 | private List> result = null; 31 | 32 | /** 33 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 34 | * 内存消耗 :36.8 MB, 在所有 Java 提交中击败了5.08%的用户 35 | */ 36 | public List> combinationSum3(int k, int n) { 37 | result = new ArrayList<>(); 38 | if (k >= n || n == 0) 39 | return result; 40 | backTrack3(k, n, 0, 0, 0, new int[k]); 41 | return result; 42 | } 43 | 44 | private void backTrack3(int k, int n, int pre, int curSum, int curIndex, int[] temp) { 45 | if (curSum == n && curIndex == k){ 46 | List tempRes = new ArrayList<>(); 47 | for(int i : temp) 48 | tempRes.add(i); 49 | result.add(tempRes); 50 | return; 51 | } 52 | if(curIndex == k) // 这里要注意 元素个数有k个 但和不满足的 没有继续的可能 53 | return; 54 | // 初始化保证了不重复,第1个条件是减枝,第2个条件是可以取的元素的最大值为9 55 | for(int i = pre + 1; i <= n - curSum && i <= 9; i++){ 56 | temp[curIndex] = i; 57 | backTrack3(k, n, i, curSum + i, curIndex + 1, temp); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /medium/BackTracking/combine77.java: -------------------------------------------------------------------------------- 1 | package medium.Backtracking; 2 | 3 | /** 4 | * @Time : 2020年2月23日15:32:56 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | /** 15 | * 给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。 16 | * 示例: 17 | * 输入: n = 4, k = 2 18 | * 输出: 19 | * [ 20 | * [2,4], 21 | * [3,4], 22 | * [2,3], 23 | * [1,2], 24 | * [1,3], 25 | * [1,4], 26 | * ] 27 | *

28 | * 来源:力扣(LeetCode) 29 | * 链接:https://leetcode-cn.com/problems/combinations 30 | */ 31 | public class combine77 { 32 | private List> result = null; 33 | 34 | /** 35 | * 执行用时 :9 ms, 在所有 Java 提交中击败了72.85%的用户 36 | * 内存消耗 :42.8 MB, 在所有 Java 提交中击败了11.56%的用户 37 | */ 38 | public List> combine(int n, int k) { 39 | result = new ArrayList<>(); 40 | if (k == 0 || k > n) 41 | return result; 42 | backTrack(n, k, 0, new int[k], 0); 43 | return result; 44 | } 45 | 46 | private void backTrack(int n, int k, int pre, int[] temp, int curIndex) { 47 | if (curIndex == k) { 48 | List tempRes = new ArrayList<>(); 49 | for (int i : temp) 50 | tempRes.add(i); 51 | result.add(tempRes); 52 | return; 53 | } 54 | 55 | for (int i = pre + 1; i <= n; i++) { 56 | temp[curIndex] = i; 57 | backTrack(n, k, i, temp, curIndex + 1); 58 | } 59 | } 60 | 61 | /** 62 | * 剪枝过程就是:把 i <= n 改成 i <= n - (k - curIndex) + 1 63 | * 题解大佬的做法 在backTrack中的for循环 i的截至为 i <= n - (k - curIndex) + 1 64 | * 可以这么想: 65 | * 当剩余要选的数为2时,当前可选的最大的数为n-1,因为如果当前选了n,则下一个数没法选 66 | * 当剩余要选的数为3时,当前可选的最大的数为n-2,当前选了n-1则下一个数选n,但最后一个数就没法选了 67 | * 所以剩余x位时,可选最大数位 n - x + 1 68 | * 在选择curIndex位时,剩余要选的数位 k - curIndex 69 | */ 70 | } 71 | -------------------------------------------------------------------------------- /medium/BackTracking/exist.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/medium/BackTracking/exist.md -------------------------------------------------------------------------------- /medium/BackTracking/findSubsequences491.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/medium/BackTracking/findSubsequences491.md -------------------------------------------------------------------------------- /medium/BackTracking/generateParenthesis.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/medium/BackTracking/generateParenthesis.md -------------------------------------------------------------------------------- /medium/BackTracking/letterCombinations17.java: -------------------------------------------------------------------------------- 1 | package medium.Backtracking; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * @Time : 2020年2月22日21:10:35 8 | * @Author : yyw@ustc 9 | * @E-mail : yang0@mail.ustc.edu.cn 10 | * @Github : https://github.com/ustcyyw 11 | * @desc : 12 | */ 13 | public class letterCombinations17 { 14 | private static String[] map = { 15 | "", 16 | "", 17 | "abc", 18 | "def", 19 | "ghi", 20 | "jkl", 21 | "mno", 22 | "pqrs", 23 | "tuv", 24 | "wxyz" 25 | }; 26 | 27 | private List combinations = null; 28 | 29 | /** 30 | * 执行用时 :9 ms, 在所有 Java 提交中击败了5.09%的用户 31 | * 内存消耗 :38.2 MB, 在所有 Java 提交中击败了5.22%的用户 32 | * 执行用时这么长的原因,主要是因为大量的字符串的拼接 每次都需要创建新的字符串对象。 33 | * 如果改用StringBuilder对象 或者直接使用char[]转化为字符串会快非常多。 34 | */ 35 | public List letterCombinations(String digits) { 36 | if(digits == null || digits.equals("")) 37 | return new ArrayList(); 38 | 39 | combinations = new ArrayList<>(); 40 | combinateAt("", digits, 0); 41 | return combinations; 42 | } 43 | 44 | private void combinateAt(String pre, String digits, int curIndex){ 45 | if(curIndex == digits.length()){ 46 | combinations.add(pre); 47 | return; 48 | } 49 | int curDigit = digits.charAt(curIndex) - '0'; 50 | for(char c : map[curDigit].toCharArray()){ 51 | combinateAt(pre + c, digits, curIndex + 1); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /medium/BackTracking/maxAreaOfIsland.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/medium/BackTracking/maxAreaOfIsland.md -------------------------------------------------------------------------------- /medium/BackTracking/numIslands.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/medium/BackTracking/numIslands.md -------------------------------------------------------------------------------- /medium/BackTracking/permute46.java: -------------------------------------------------------------------------------- 1 | package medium.Backtracking; 2 | 3 | import com.sun.jmx.snmp.SnmpNull; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | /** 9 | * @Time : 2020年2月22日22:05:20 10 | * @Author : yyw@ustc 11 | * @E-mail : yang0@mail.ustc.edu.cn 12 | * @Github : https://github.com/ustcyyw 13 | * @desc : 14 | */ 15 | public class permute46 { 16 | /** 17 | * 执行用时 :2 ms, 在所有 Java 提交中击败了58.96%的用户 18 | * 内存消耗 :41 MB, 在所有 Java 提交中击败了5.03% 19 | */ 20 | private List> result = null; 21 | public List> permute(int[] nums) { 22 | result = new ArrayList<>(); 23 | if(nums == null || nums.length == 0) 24 | return result; 25 | 26 | List remain = new ArrayList<>(); 27 | for(int i : nums) 28 | remain.add(i); 29 | array(remain, new int[nums.length], 0); 30 | return result; 31 | } 32 | 33 | private void array(List remain, int[] temp, int curIndex){ 34 | if(remain.size() == 0){ 35 | List localResult = new ArrayList<>(); 36 | for(int i : temp) 37 | localResult.add(i); 38 | result.add(localResult); 39 | return; 40 | } 41 | 42 | for(int i = remain.size() - 1; i >= 0; i--){ 43 | temp[curIndex] = remain.remove(0); 44 | array(remain, temp, curIndex + 1); 45 | remain.add(temp[curIndex]); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /medium/BackTracking/restoreIpAddresses.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/medium/BackTracking/restoreIpAddresses.md -------------------------------------------------------------------------------- /medium/BackTracking/solve130.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/medium/BackTracking/solve130.md -------------------------------------------------------------------------------- /medium/BackTracking/subsets78.java: -------------------------------------------------------------------------------- 1 | package medium.Backtracking; 2 | 3 | /** 4 | * @Time : 2020年2月24日16:17:44 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc :已总结 9 | */ 10 | 11 | import java.util.ArrayList; 12 | import java.util.Arrays; 13 | import java.util.List; 14 | 15 | /** 16 | * 给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。 17 | * 说明:解集不能包含重复的子集。 18 | * 示例: 19 | * 输入: nums = [1,2,3] 20 | * 输出: 21 | * [ 22 | * [3], 23 | *   [1], 24 | *   [2], 25 | *   [1,2,3], 26 | *   [1,3], 27 | *   [2,3], 28 | *   [1,2], 29 | *   [] 30 | * ] 31 | *

32 | * 来源:力扣(LeetCode) 33 | * 链接:https://leetcode-cn.com/problems/subsets 34 | */ 35 | public class subsets78 { 36 | private List> result = null; 37 | 38 | /** 39 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 40 | * 内存消耗 :39.3 MB, 在所有 Java 提交中击败了5.08%的用户 41 | */ 42 | public List> subsets(int[] nums) { 43 | result = new ArrayList<>(); 44 | result.add(new ArrayList<>()); // 空集 45 | if (nums.length == 0) 46 | return result; 47 | for (int i = 1; i <= nums.length; i++) { 48 | backTrack(nums, i, 0, new int[i], 0); 49 | } 50 | 51 | return result; 52 | } 53 | 54 | private void backTrack(int[] nums, int k, int start, int[] temp, int curIndex) { 55 | if(curIndex == k){ 56 | List tempRes = new ArrayList<>(); 57 | for(int i : temp) 58 | tempRes.add(i); 59 | result.add(tempRes); 60 | return; 61 | } 62 | // 这里的剪树枝条件,可以参考77题 子集其实也就是一中组合,所以在选数的时候,如果选得太大,会导致后续不够选。 63 | for(int i = start; i < nums.length - k + curIndex + 1; i++){ 64 | temp[curIndex] = nums[i]; 65 | backTrack(nums, k, i + 1, temp, curIndex + 1); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /medium/BackTracking/subsets78.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/medium/BackTracking/subsets78.md -------------------------------------------------------------------------------- /medium/BackTracking/subsetsWithDup90.java: -------------------------------------------------------------------------------- 1 | package medium.Backtracking; 2 | 3 | /** 4 | * @Time : 2020年3月21日18:57:47 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 已总结 9 | */ 10 | 11 | import java.util.*; 12 | 13 | /** 14 | * 给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。 15 | * 说明:解集不能包含重复的子集。 16 | *

17 | * 示例: 18 | * 输入: [1,2,2] 19 | * 输出: 20 | * [ 21 | * [2], 22 | * [1], 23 | * [1,2,2], 24 | * [2,2], 25 | * [1,2], 26 | * [] 27 | * ] 28 | *

29 | * 来源:力扣(LeetCode) 30 | * 链接:https://leetcode-cn.com/problems/subsets-ii 31 | */ 32 | public class subsetsWithDup90 { 33 | /** 34 | * 执行用时 :1 ms, 在所有 Java 提交中击败了100.00%的用户 35 | * 内存消耗 :41.8 MB, 在所有 Java 提交中击败了5.01%的用户 36 | * 联系78题 只是多了一点 有重复元素 37 | */ 38 | private List> res; 39 | 40 | public List> subsetsWithDup(int[] nums) { 41 | res = new ArrayList<>(); 42 | res.add(new ArrayList<>()); 43 | if (nums == null || nums.length == 0) 44 | return res; 45 | Arrays.sort(nums); 46 | for (int i = 1; i <= nums.length; i++) { 47 | backTrack(nums, i, 0, new int[i], 0); 48 | } 49 | return res; 50 | } 51 | 52 | private void backTrack(int[] nums, int k, int start, int[] temp, int curIndex) { 53 | if (curIndex == k) { 54 | List tempRes = new ArrayList<>(); 55 | for (int i : temp) 56 | tempRes.add(i); 57 | res.add(tempRes); 58 | return; 59 | } 60 | for (int i = start; i < nums.length - k + curIndex + 1; i++) { 61 | if (i != start && nums[i] == nums[i - 1]) // 重复元素的解决方式就是,在选择某一位元素时,已经选过的就跳过 62 | continue; 63 | temp[curIndex] = nums[i]; 64 | backTrack(nums, k, i + 1, temp, curIndex + 1); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /medium/BackTracking/subsetsWithDup90.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/medium/BackTracking/subsetsWithDup90.md -------------------------------------------------------------------------------- /medium/BinarySearch/findDuplicate.java: -------------------------------------------------------------------------------- 1 | package medium.BinarySearch; 2 | 3 | /** 4 | * @Time : 2020年2月12日16:42:21 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | /** 12 | * 给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。 13 | * 假设只有一个重复的整数,找出这个重复的数。 14 | * 15 | * 示例 1: 16 | * 输入: [1,3,4,2,2] 17 | * 输出: 2 18 | * 19 | * 示例 2: 20 | * 输入: [3,1,3,4,2] 21 | * 输出: 3 22 | * 说明: 23 | * 不能更改原数组(假设数组是只读的)。 24 | * 只能使用额外的 O(1) 的空间。 25 | * 时间复杂度小于 O(n2) 。 26 | * 数组中只有一个重复的数字,但它可能不止重复出现一次。 27 | * 28 | * 来源:力扣(LeetCode) 29 | * 链接:https://leetcode-cn.com/problems/find-the-duplicate-number 30 | */ 31 | 32 | /** 33 | * 不能改变数组,所以不能排序。 34 | * 额外空间O(1),不能复制数组,也不能使用HashSet 35 | * 时间复杂度O(n平方),不能用暴力方法 36 | */ 37 | public class findDuplicate { 38 | /** 39 | * 别人想出来的解法 40 | * 类似于在矩阵中找到第k小的元素的思路,使用二分查找。 41 | * 执行用时 :4 ms, 在所有 Java 提交中击败了59.72%的用户 42 | * 内存消耗 :38.4 MB, 在所有 Java 提交中击败了10.64%的用户 43 | * 二分法时间复杂度 log(n), 每次遍历n+1个元素 所以总的时间复杂度为(n+1)logn 44 | */ 45 | public int findDuplicate(int[] nums) { 46 | int right = nums.length - 1; 47 | int left = 1; 48 | while (left < right) { 49 | int mid = (left + right) / 2; 50 | int count = 0; 51 | for (int num : nums) { 52 | if (num <= mid) 53 | count++; 54 | } 55 | if (count > mid) { // mid是在没有重复元素时,小于等于mid的数的个数 56 | right = mid; 57 | } else { 58 | left = mid + 1; 59 | } 60 | } 61 | return left; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /medium/BitOperation/singleNumber260.java: -------------------------------------------------------------------------------- 1 | package medium.BitOperation; 2 | 3 | /** 4 | * @Time : 2020年3月27日19:07:38 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 已总结 9 | */ 10 | 11 | /** 12 | * 给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。 13 | * 14 | * 示例 : 15 | * 输入: [1,2,1,3,2,5] 16 | * 输出: [3,5] 17 | * 注意: 18 | * 19 | * 结果输出的顺序并不重要,对于上面的例子, [5, 3] 也是正确答案。 20 | * 你的算法应该具有线性时间复杂度。你能否仅使用常数空间复杂度来实现? 21 | * 22 | * 来源:力扣(LeetCode) 23 | * 链接:https://leetcode-cn.com/problems/single-number-iii 24 | */ 25 | public class singleNumber260 { 26 | /** 27 | * 异或的交换律,x ^ x = 0, x ^ 0 = x 28 | * 将数组中的所有元素进行异或,最后剩下的是只出现一次的两个数的异或 29 | * 这两个数无法进行区分,如果数组中只有一个元素出项一次,就可以得到它 30 | * 所以这里考虑是否可以将两个只出现一次的元素分在不同的组合中,这两个组合中别的元素都出现了两次。 31 | * 两个不相同的数,二进制表示时,至少有某一位不相等,在该位异或的结果为1 32 | * 有一个技巧 x & -x 得到的是x的位级表示中最低的那一位 1 也就是 该数有且仅有一位是1,即x最低位1所在的那位,其余都是0 33 | * 比如 x = 10110100,-x 得到 01001100,相与得到 00000100。 34 | * 所以对目标的两个数的异或结果 two 进行 two & -two 就可以知道他们第一个不相同的位。 35 | * 通过这一位,就可以将原数组分成两部分,两个要找的数分别在两个部分。 36 | * 比如 two & -two = 0100。那么分开的两部分中,一部分的元素第3位均为1,另外一部分元素第3为均为0.通过与 0100 取与之后是否为0来判断 37 | * 除了要找的两个元素,其余元素都是成对出现,会被成对分到两部分中。 38 | * 这就转化成了,一个数组中,只有一个元素只出现一次,其余全都成对出现的情况。 39 | */ 40 | 41 | /** 42 | * 执行用时 :1 ms, 在所有 Java 提交中击败了100.00%的用户 43 | * 内存消耗 :42.1 MB, 在所有 Java 提交中击败了5.41%的用户 44 | */ 45 | public int[] singleNumber(int[] nums) { 46 | int two = 0; 47 | for(int i : nums) 48 | two ^= i; 49 | int mask = two & (-two); 50 | int temp = 0; 51 | for(int i : nums){ 52 | if((i & mask) == 0) temp ^= i; 53 | } 54 | return new int[]{temp, two ^ temp}; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /medium/BitOperation/singleNumber260.md: -------------------------------------------------------------------------------- 1 | # 260. 只出现一次的数字 III 2 | 3 | ### 原题 4 | 给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。 5 | 6 | 示例 : 7 | 输入: [1,2,1,3,2,5] 8 | 输出: [3,5] 9 | 10 | 注意: 11 | 结果输出的顺序并不重要,对于上面的例子, [5, 3] 也是正确答案。 12 | 你的算法应该具有线性时间复杂度。你能否仅使用常数空间复杂度来实现? 13 | 14 | 来源:力扣(LeetCode) 15 | [链接](https://leetcode-cn.com/problems/single-number-iii):https://leetcode-cn.com/problems/single-number-iii 16 | 17 | ### 解法:位运算 18 | 19 | ```java 20 | public int[] singleNumber(int[] nums) { 21 | int two = 0; 22 | for(int i : nums) 23 | two ^= i; 24 | int mask = two & (-two); 25 | int temp = 0; 26 | for(int i : nums){ 27 | if((i & mask) == 0) temp ^= i; 28 | } 29 | return new int[]{temp, two ^ temp}; 30 | } 31 | ``` 32 | 33 | 思路分析: 34 | 35 | * 这个题和[136.只出现一次的数字](https://github.com/ustcyyw/yyw_algorithm/blob/master/easy/BitOperation/singleNumber.md) 相似 36 | * 首先回顾一下,异或的三点性质: 37 | * 异或的交换律 38 | * x ^ x = 0 39 | * x ^ 0 = x 40 | * 将数组中的所有元素进行异或,最后剩下的是只出现一次的两个数a,b的异或 a ^ b。这两个数无法进行区分,如果数组中只有一个元素出项一次(就像136题那样),就可以得到它。所以这里考虑是否可以将a,b分在不同的子数组中,这两个子数组中别的元素都出现了两次。 41 | * 两个不相同的数,二进制表示时,至少有某一位不相等,在该位异或的结果为1。 42 | * 有一个技巧 x & -x 得到的是x的位级表示中最低的那一位 1 也就是 该数有且仅有一位是1,即x最低位1所在的那位是1,其余都是0。 43 | * 比如 x = 10110100,-x 得到 01001100,相与得到 00000100。 44 | * 所以对a, b两个数的异或结果 `two` 进行` two & -two` 就可以知道他们第一个不相同的位`int mask = two & (-two)`。通过这一位,就可以将原数组分成两部分,两个要找的数分别在两个部分。 45 | * 比如 `two & -two` = 0100。那么分开的两部分中,一部分的元素第3位均为1,另外一部分元素第3为均为0.通过与 0100 取与之后是否为0来判断。a,b中第三位一个为1,另外一个肯定为0,所以他们两被分开了。 46 | * 除了要找的两个元素,其余元素都是成对出现。这就转化成了,一个数组中,只有一个元素只出现一次,其余全都成对出现的情况。 47 | * 接下来就是在一个子数组中,对所有元素求异或,得到要找的一个元素,再通过`temp ^ two`得到另外一个要找的元素。 48 | * 分组与分组里所有元素的异或一起进行。`if((i & mask) == 0) temp ^= i;`满足这个组条件的进行异或。 49 | 50 | 运行结果: 51 | * 执行用时 :1 ms, 在所有 Java 提交中击败了100.00%的用户 52 | * 内存消耗 :42.1 MB, 在所有 Java 提交中击败了5.41%的用户 -------------------------------------------------------------------------------- /medium/DP/maxProduct.java: -------------------------------------------------------------------------------- 1 | package medium.DP; 2 | 3 | /** 4 | * @Time : 2020年3月13日20:54:21 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | /** 12 | * 给定一个整数数组 nums ,找出一个序列中乘积最大的连续子序列(该序列至少包含一个数)。 13 | * 14 | * 示例 1: 15 | * 输入: [2,3,-2,4] 16 | * 输出: 6 17 | * 解释: 子数组 [2,3] 有最大乘积 6。 18 | * 19 | * 示例 2: 20 | * 输入: [-2,0,-1] 21 | * 输出: 0 22 | * 解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。 23 | * 24 | * 来源:力扣(LeetCode) 25 | * 链接:https://leetcode-cn.com/problems/maximum-product-subarray 26 | */ 27 | public class maxProduct { 28 | /** 29 | * 评论区解法 30 | * DP 不像求子序列的和 因为这里的负数会改变符号。所以不能只维持最大的。 31 | */ 32 | public int maxProduct(int[] nums) { 33 | int res = Integer.MIN_VALUE; 34 | int min = 1, max = 1; // 表示以i这个元素为结尾的子串的最大/最小积 35 | for(int i = 0; i < nums.length; i++){ 36 | if(nums[i] < 0){ // 当第i个元素为负数时,截止上一位最大的乘上nums[i]反而可能是最小的;截止上一位最小的乘上nums[i]反而可能是最大的。 37 | int temp = min; // 所以将min max的值进行交换,这样才能在后续使用统一的代码。 38 | min = max; 39 | max = temp; 40 | } 41 | max = Math.max(max * nums[i], nums[i]); 42 | min = Math.min(min * nums[i], nums[i]); 43 | res = Math.max(res, max); 44 | } 45 | return res; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /medium/DP/minPathSum64.java: -------------------------------------------------------------------------------- 1 | package medium.DP; 2 | 3 | /** 4 | * @Time : 2020年2月25日20:14:13 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | /** 12 | * 给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 13 | * 说明:每次只能向下或者向右移动一步。 14 | * 15 | * 示例: 16 | * 输入: 17 | * [ 18 | *   [1,3,1], 19 | * [1,5,1], 20 | * [4,2,1] 21 | * ] 22 | * 输出: 7 23 | * 解释: 因为路径 1→3→1→1→1 的总和最小。 24 | * 25 | * 来源:力扣(LeetCode) 26 | * 链接:https://leetcode-cn.com/problems/minimum-path-sum 27 | */ 28 | public class minPathSum64 { 29 | /** 30 | * 执行用时 :3 ms, 在所有 Java 提交中击败了84.49%的用户 31 | * 内存消耗 :42.2 MB, 在所有 Java 提交中击败了19.78%的用户 32 | */ 33 | public int minPathSum(int[][] grid) { 34 | if(grid.length == 0 || grid[0].length == 0) 35 | return 0; 36 | int row = grid.length; 37 | int col = grid[0].length; 38 | // 初始条件 39 | 40 | for(int i = 1; i < col; i++) 41 | grid[0][i] += grid[0][i - 1]; 42 | for(int i = 1; i < row; i++) 43 | grid[i][0] += grid[i - 1][0]; 44 | for(int i = 1; i < row; i++){ 45 | for(int j = 1; j < col; j++) 46 | grid[i][j] += Math.min(grid[i - 1][j], grid[i][j - 1]); 47 | } 48 | return grid[row- 1][col - 1]; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /medium/DP/rob213.java: -------------------------------------------------------------------------------- 1 | package medium.DP; 2 | 3 | /** 4 | * @Time : 2020年2月27日00:18:41 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | /** 12 | * 你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都围成一圈,这意味着第一个房屋和最后一个房屋是紧挨着的。 13 | * 同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 14 | * 给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。 15 | * 16 | * 示例 1: 17 | * 输入: [2,3,2] 18 | * 输出: 3 19 | * 解释: 你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。 20 | * 21 | * 示例 2: 22 | * 输入: [1,2,3,1] 23 | * 输出: 4 24 | * 解释: 你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。 25 | *   偷窃到的最高金额 = 1 + 3 = 4 。 26 | * 27 | * 来源:力扣(LeetCode) 28 | * 链接:https://leetcode-cn.com/problems/house-robber-ii 29 | */ 30 | public class rob213 { 31 | /** 32 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 33 | * 内存消耗 :36.7 MB, 在所有 Java 提交中击败了5.06%的用户 34 | */ 35 | public int rob(int[] nums) { 36 | int n = nums.length; 37 | if (n == 0) return 0; 38 | if (n == 1) return nums[0]; // 注意这个特殊情况,只有一家的时候不存在第一家与最后一家的概念,直接返回金额就可以 39 | return Math.max(rob(nums, n, true), rob(nums, n, false)); 40 | } 41 | 42 | private int rob(int[] nums, int n, boolean rob1) { // 第三个参数表示要不要偷第一家 43 | int[] amount = new int[n + 1]; // amount[i]的意义是截至第i家,能偷的最多的钱为amount[i] 44 | amount[0] = 0; 45 | amount[1] = rob1 ? nums[0] : 0; // 如果指定不偷第一家,那么第一家处取得的最大金额就是0 46 | for (int i = 2; i <= n; i++) { 47 | amount[i] = Math.max(amount[i - 1], amount[i - 2] + nums[i - 1]); 48 | } 49 | return rob1 ? amount[n - 1] : amount[n]; // 如果指定偷第一家,那么最后一家不能偷,如果第n家不偷,其值等于amount[n - 1] 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /medium/DP/uniquePaths62.java: -------------------------------------------------------------------------------- 1 | package medium.DP; 2 | 3 | /** 4 | * @Time : 2020年2月25日15:29:39 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | import java.util.Arrays; 12 | 13 | /** 14 | * 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。 15 | * 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。 16 | * 问总共有多少条不同的路径? 17 | * 说明:m 和 n 的值均不超过 100。 18 | * 19 | * 示例 1: 20 | * 输入: m = 3, n = 2 21 | * 输出: 3 22 | * 解释: 23 | * 从左上角开始,总共有 3 条路径可以到达右下角。 24 | * 1. 向右 -> 向右 -> 向下 25 | * 2. 向右 -> 向下 -> 向右 26 | * 3. 向下 -> 向右 -> 向右 27 | * 28 | * 示例 2: 29 | * 输入: m = 7, n = 3 30 | * 输出: 28 31 | * 32 | * 来源:力扣(LeetCode) 33 | * 链接:https://leetcode-cn.com/problems/unique-paths 34 | */ 35 | public class uniquePaths62 { 36 | /** 37 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 38 | * 内存消耗 :36.1 MB, 在所有 Java 提交中击败了5.10%的用户 39 | */ 40 | public int uniquePaths(int m, int n) { 41 | int[][] count = new int[n][m]; 42 | for(int[] row : count) 43 | row[0] = 1; 44 | Arrays.fill(count[0], 1); 45 | for(int row = 1; row < n; row++) 46 | for(int col = 1; col < m; col++) 47 | count[row][col] = count[row - 1][col] + count[row][col - 1]; 48 | return count[n - 1][m - 1]; 49 | } 50 | 51 | public int uniquePaths2(int m, int n) { 52 | int[] count = new int[m]; 53 | Arrays.fill(count, 1); 54 | for(int row = 1; row < n; row++) 55 | for(int col = 1; col < m; col++) 56 | count[col] = count[col] + count[col - 1]; // 分别对应上格子与左边格子 57 | return count[m - 1]; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /medium/LinkedList/deleteDuplicates82.java: -------------------------------------------------------------------------------- 1 | package medium.LinkedList; 2 | 3 | import BaseClass.ListNode; 4 | 5 | /** 6 | * @Time : 2020年2月14日14:27:52 7 | * @Author : yyw@ustc 8 | * @E-mail : yang0@mail.ustc.edu.cn 9 | * @Github : https://github.com/ustcyyw 10 | * @desc : 11 | */ 12 | 13 | /** 14 | * 给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字。 15 | * 16 | * 示例 1: 17 | * 输入: 1->2->3->3->4->4->5 18 | * 输出: 1->2->5 19 | * 20 | * 示例 2: 21 | * 输入: 1->1->1->2->3 22 | * 输出: 2->3 23 | * 24 | * 来源:力扣(LeetCode) 25 | * 链接:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list-ii 26 | */ 27 | public class deleteDuplicates82 { 28 | /** 29 | * 执行用时 :1 ms, 在所有 Java 提交中击败了98.79%的用户 30 | * 内存消耗 :44.5 MB, 在所有 Java 提交中击败了5.03%的用户 31 | */ 32 | public ListNode deleteDuplicates(ListNode head) { 33 | ListNode fakeHead = new ListNode(-1); 34 | fakeHead.next = head; 35 | ListNode pre = fakeHead; 36 | while(head != null){ 37 | ListNode cur = head; 38 | while(cur.next != null && cur.next.val == head.val) 39 | cur = cur.next; 40 | if(head != cur){ 41 | pre.next = cur.next; 42 | head = pre.next; 43 | } else { 44 | head = head.next; 45 | pre = pre.next; 46 | } 47 | } 48 | return fakeHead.next; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /medium/LinkedList/deleteDuplicates82.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/medium/LinkedList/deleteDuplicates82.jpg -------------------------------------------------------------------------------- /medium/LinkedList/deleteDuplicates82.md: -------------------------------------------------------------------------------- 1 | # 删除排序链表中重复元素(只保留只出现过一次的元素) 2 | 3 | ### 原题 4 | 5 | 给定一个排序链表,删除所有含有重复数字的节点,**只保留原始链表中 没有重复出现 的数字**。 6 | 7 | 示例 1: 8 | 9 | 输入: 1->2->3->3->4->4->5 10 | 输出: 1->2->5 11 | 示例 2: 12 | 13 | 输入: 1->1->1->2->3 14 | 输出: 2->3 15 | 16 | 来源:力扣(LeetCode) 17 | [链接](https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list-ii):https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list-ii 18 | 19 | ---- 20 | 21 | ### 解法 22 | 23 | ##### 1.pre head cur三指针的使用(我的解法) 24 | 25 | ```java 26 | public ListNode deleteDuplicates(ListNode head) { 27 | ListNode fakeHead = new ListNode(-1); 28 | fakeHead.next = head; 29 | ListNode pre = fakeHead; 30 | while(head != null){ 31 | ListNode cur = head; 32 | while(cur.next != null && cur.next.val == head.val) 33 | cur = cur.next; 34 | if(head != cur){ 35 | pre.next = cur.next; 36 | head = pre.next; 37 | } else { 38 | head = head.next; 39 | pre = pre.next; 40 | } 41 | } 42 | return fakeHead.next; 43 | } 44 | ``` 45 | 46 | 思路分析: 47 | 48 | * 注意本题要将重复元素都删掉,而不是重复元素保留一个,删掉其它。 49 | * 设想一种特殊的情况,就是链表中只有一种重复元素,那么只使用`head`指针就没法完成,所以创建一个伪头指针`ListNode fakeHead = new ListNode(-1);`,并且使用一个前置指针`pre`,这样在调整`pre.next`时,就可以删除所有重复元素。 50 | * 接下来,还有一个存在的问题,就是要找到重复元素所在的最后一个结点。这是个排序好的链表,所以使用一个`cur`指针从当前结点移动到与之重复的最后一个结点。如果`cur`没有移动,也就是`head == cur`,说明这个元素不重复,要保留。 51 | * 移动`cur`的条件之一`cur.next != null`是为了使用`cur.next.val`时不会出现异常。外循环的条件就是正常的遍历链表的条件。 52 | * 单次遍历链表,时间复杂度为$O(n)$,只使用了辅助的指针,空间复杂度为$O(1)$。 53 | 54 | 图示:以1->2->3->3->4->4->5为输入链表来演示。 55 | 56 | ![deleteDuplicates82.jpg](https://github.com/ustcyyw/yyw_algorithm/blob/master/medium/LinkedList/deleteDuplicates82.jpg?raw=true) 57 | 58 | 运行结果: 59 | * 执行用时 :1 ms, 在所有 Java 提交中击败了98.79%的用户 60 | * 内存消耗 :44.5 MB, 在所有 Java 提交中击败了5.03%的用户 -------------------------------------------------------------------------------- /medium/LinkedList/oddEvenList.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/medium/LinkedList/oddEvenList.jpg -------------------------------------------------------------------------------- /medium/LinkedList/rotateRight.java: -------------------------------------------------------------------------------- 1 | package medium.LinkedList; 2 | 3 | /** 4 | * @Time : 2020年4月9日12:33:10 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | import BaseClass.ListNode; 12 | 13 | /** 14 | * 给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。 15 | * 16 | * 示例 1: 17 | * 输入: 1->2->3->4->5->NULL, k = 2 18 | * 输出: 4->5->1->2->3->NULL 19 | * 解释: 20 | * 向右旋转 1 步: 5->1->2->3->4->NULL 21 | * 向右旋转 2 步: 4->5->1->2->3->NULL 22 | * 23 | * 示例 2: 24 | * 输入: 0->1->2->NULL, k = 4 25 | * 输出: 2->0->1->NULL 26 | * 解释: 27 | * 向右旋转 1 步: 2->0->1->NULL 28 | * 向右旋转 2 步: 1->2->0->NULL 29 | * 向右旋转 3 步: 0->1->2->NULL 30 | * 向右旋转 4 步: 2->0->1->NULL 31 | * 32 | * 来源:力扣(LeetCode) 33 | * 链接:https://leetcode-cn.com/problems/rotate-list 34 | */ 35 | public class rotateRight { 36 | /** 37 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 38 | * 内存消耗 :39.4 MB, 在所有 Java 提交中击败了5.13%的用户 39 | */ 40 | public ListNode rotateRight(ListNode head, int k) { 41 | if(k == 0 || head == null) 42 | return head; 43 | int length = 1; 44 | ListNode tail = head; 45 | for (; tail.next != null; tail = tail.next) 46 | length++; 47 | int temp = k % length; 48 | if(temp == 0) return head; 49 | ListNode pre = head; 50 | for(int i = 0; i < length - temp - 1; i++) 51 | pre = pre.next; 52 | ListNode res = pre.next; 53 | pre.next = null; 54 | tail.next = head; 55 | return res; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /medium/LinkedList/rotateRight图示.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/medium/LinkedList/rotateRight图示.png -------------------------------------------------------------------------------- /medium/LinkedList/splitListToParts.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/medium/LinkedList/splitListToParts.jpg -------------------------------------------------------------------------------- /medium/LinkedList/swapPairs.java: -------------------------------------------------------------------------------- 1 | package medium.LinkedList; 2 | 3 | /** 4 | * @Time : 2020年3月8日21:42:54 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | import BaseClass.ListNode; 12 | 13 | /** 14 | * 给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。 15 | * 你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。 16 | * 17 | * 示例: 18 | * 给定 1->2->3->4, 你应该返回 2->1->4->3. 19 | * 20 | * 来源:力扣(LeetCode) 21 | * 链接:https://leetcode-cn.com/problems/swap-nodes-in-pairs 22 | * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 23 | */ 24 | public class swapPairs { 25 | /** 26 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 27 | * 内存消耗 :34.5 MB, 在所有 Java 提交中击败了30.75%的用户 28 | */ 29 | public ListNode swapPairs(ListNode head) { 30 | if(head == null || head.next == null) 31 | return head; 32 | ListNode fake = new ListNode(-1); 33 | fake.next = head; 34 | ListNode pre = fake; 35 | ListNode cur = head; 36 | while(true){ 37 | pre.next = cur.next; 38 | cur.next = cur.next.next; 39 | pre.next.next = cur; 40 | if(cur.next == null || cur.next.next == null) 41 | return fake.next; 42 | cur = cur.next; 43 | pre = pre.next.next; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /medium/LinkedList/swapPairs.md: -------------------------------------------------------------------------------- 1 | # 24.两两交换链表中的节点 2 | 3 | ### 原题 4 | 给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。 5 | 你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。 6 | 7 | 示例: 8 | 9 | 给定 1->2->3->4, 你应该返回 2->1->4->3. 10 | 11 | 来源:力扣(LeetCode) 12 | [链接](https://leetcode-cn.com/problems/swap-nodes-in-pairs):https://leetcode-cn.com/problems/swap-nodes-in-pairs 13 | 14 | ### 解法 15 | 16 | ```java 17 | public ListNode swapPairs(ListNode head) { 18 | if(head == null || head.next == null) 19 | return head; 20 | ListNode fake = new ListNode(-1); 21 | fake.next = head; 22 | ListNode pre = fake; 23 | ListNode cur = head; 24 | while(true){ 25 | pre.next = cur.next; 26 | cur.next = cur.next.next; 27 | pre.next.next = cur; 28 | if(cur.next == null || cur.next.next == null) 29 | return fake.next; 30 | cur = cur.next; 31 | pre = pre.next.next; 32 | } 33 | } 34 | ``` 35 | 36 | 思路分析: 37 | 38 | * 首先,如果链表为空或者只有一个结点时,不需要成对交换,直接返回`head`。 39 | * 改变链表的指针可以画示意图看我们需要几个变量来指向哪些结点。且经常使用一个技巧:创建一个不存在的结点来指向原链表的头结点。 40 | * `while`循环的代码运行的过程如下图所示 41 | 42 | ![swapPairs图示.png](https://github.com/ustcyyw/yyw_algorithm/blob/master/medium/LinkedList/swapPairs%E5%9B%BE%E7%A4%BA.png?raw=true) 43 | 44 | * 循环终止的条件为`cur.next == null || cur.next.next == null`分别对应原链表有偶数个结点和奇数个,结点的情况。循环结束的时候返回`fake.head`就是交换后链表真正的头部。 45 | 46 | 运行结果: 47 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 48 | * 内存消耗 :34.5 MB, 在所有 Java 提交中击败了30.75%的用户 49 | 50 | -------------------------------------------------------------------------------- /medium/LinkedList/swapPairs图示.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/medium/LinkedList/swapPairs图示.png -------------------------------------------------------------------------------- /medium/Sort/sortColors.java: -------------------------------------------------------------------------------- 1 | package medium.Sort; 2 | 3 | /** 4 | * @Time : 2020年3月10日12:08:25 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | /** 12 | * 给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。 13 | * 此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。 14 | * 注意: 15 | * 不能使用代码库中的排序函数来解决这道题。 16 | *

17 | * 示例: 18 | * 输入: [2,0,2,1,1,0] 19 | * 输出: [0,0,1,1,2,2] 20 | *

21 | * 进阶: 22 | * 一个直观的解决方案是使用计数排序的两趟扫描算法。 23 | * 首先,迭代计算出0、1 和 2 元素的个数,然后按照0、1、2的排序,重写当前数组。 24 | * 你能想出一个仅使用常数空间的一趟扫描算法吗? 25 | *

26 | * 来源:力扣(LeetCode) 27 | * 链接:https://leetcode-cn.com/problems/sort-colors 28 | */ 29 | public class sortColors { 30 | /** 31 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 32 | * 内存消耗 :38.5 MB, 在所有 Java 提交中击败了5.02%的用户 33 | * 三向切分的快速排序中切分的步骤 34 | */ 35 | public void sortColors(int[] nums) { 36 | for (int i = 0; i < nums.length; i++) { 37 | if (nums[i] == 1){ 38 | exch(nums, i, 0); 39 | break; 40 | } 41 | } 42 | int gt = nums.length - 1, lt = 0, i = 1; 43 | while(gt >= i){ 44 | if(nums[i] > 1) exch(nums, gt--, i); 45 | else if (nums[i] < 1) exch(nums, lt++, i++); 46 | else i++; 47 | } 48 | } 49 | 50 | private void exch(int[] nums, int i, int j) { 51 | int temp = nums[i]; 52 | nums[i] = nums[j]; 53 | nums[j] = temp; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /medium/String/longestPalindrome5图示.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/medium/String/longestPalindrome5图示.png -------------------------------------------------------------------------------- /medium/Tree/generateTrees95.java: -------------------------------------------------------------------------------- 1 | package medium.Tree; 2 | 3 | import BaseClass.TreeNode; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | /** 9 | * @Time : 2020年3月4日12:17:28 10 | * @Author : yyw@ustc 11 | * @E-mail : yang0@mail.ustc.edu.cn 12 | * @Github : https://github.com/ustcyyw 13 | * @desc : 已总结 14 | */ 15 | public class generateTrees95 { 16 | /** 17 | * 官方标答:使用递归,选定i为根,则其左子树在[1,2,...i - 1]生成,右子树在[i+1,i+2,...n]生成 18 | * 以i为根一共能生产的BST 为其左子树可能的数量乘右子树可能的数量。 19 | * 执行用时 :1 ms, 在所有 Java 提交中击败了100.00%的用户 20 | * 内存消耗 :41.4 MB, 在所有 Java 提交中击败了5.07%的用户 21 | */ 22 | public List generateTrees(int n) { 23 | if(n == 0) 24 | return new ArrayList<>(); 25 | return sonTrees(1, n); 26 | } 27 | 28 | private List sonTrees(int lo, int hi) { // [lo,hi]范围内所有的BST 29 | List res = new ArrayList<>(); 30 | if(lo > hi) 31 | res.add(null); 32 | else if(lo == hi) 33 | res.add(new TreeNode(lo)); 34 | else { 35 | // 当前区间内,所有能生产的BST,每一个值都可能为根,然后要考虑其左边区间所有子树与右边区间所有子树的可能 36 | for(int i = lo; i <= hi; i++){ 37 | List leftSon = sonTrees(lo, i - 1); 38 | List rightSon = sonTrees(i + 1, hi); 39 | for(TreeNode left : leftSon){ 40 | for(TreeNode right : rightSon){ 41 | TreeNode curRoot = new TreeNode(i); 42 | curRoot.left = left; 43 | curRoot.right = right; 44 | res.add(curRoot); 45 | } 46 | } 47 | } 48 | } 49 | return res; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /medium/Tree/lowestCommonAncestor.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/medium/Tree/lowestCommonAncestor.md -------------------------------------------------------------------------------- /medium/Tree/numTrees96.java: -------------------------------------------------------------------------------- 1 | package medium.Tree; 2 | 3 | /** 4 | * @Time : 2020年3月13日14:40:37 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | /** 12 | * 给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种? 13 | * 14 | * 示例: 15 | * 输入: 3 16 | * 输出: 5 17 | * 解释: 18 | * 给定 n = 3, 一共有 5 种不同结构的二叉搜索树: 19 | * 20 | * 1 3 3 2 1 21 | * \ / / / \ \ 22 | * 3 2 1 1 3 2 23 | * / / \ \ 24 | * 2 1 2 3 25 | * 26 | * 来源:力扣(LeetCode) 27 | * 链接:https://leetcode-cn.com/problems/unique-binary-search-trees 28 | */ 29 | public class numTrees96 { 30 | /** 31 | * 递归 然后发现有大量重复的计算 可以用记忆话搜索也可以使用dp 32 | */ 33 | public int numTrees(int n) { 34 | return count(1, n); 35 | } 36 | 37 | public int count(int lo, int hi){ 38 | if(lo >= hi) return 1; 39 | int sum = 0; 40 | for(int i = lo; i <= hi; i++){ 41 | sum += count(lo, i - 1) * count(i + 1, hi); 42 | } 43 | return sum; 44 | } 45 | 46 | /** 47 | * 执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户 48 | * 内存消耗 :36.7 MB, 在所有 Java 提交中击败了5.08%的用户 49 | */ 50 | public int numTrees2(int n) { 51 | int[] count = new int[n + 1]; // count[i]表示 i个结点可以构成多少种BST 52 | count[0] = 1; 53 | count[1] = 1; 54 | for(int i = 2; i <= n; i++){ 55 | for(int j = 0; j < i; j++) 56 | count[i] += count[j] * count[i - j - 1]; 57 | } 58 | return count[n]; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /medium/Tree/pathSum113.java: -------------------------------------------------------------------------------- 1 | package medium.Tree; 2 | 3 | import BaseClass.TreeNode; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | /** 9 | * @Time : 2020年4月14日19:50:11 10 | * @Author : yyw@ustc 11 | * @E-mail : yang0@mail.ustc.edu.cn 12 | * @Github : https://github.com/ustcyyw 13 | * @desc : 已总结 14 | */ 15 | 16 | /** 17 | * 给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。 18 | * 说明: 叶子节点是指没有子节点的节点。 19 | * 20 | * 示例: 21 | * 给定如下二叉树,以及目标和 sum = 22, 22 | * 5 23 | * / \ 24 | * 4 8 25 | * / / \ 26 | * 11 13 4 27 | * / \ / \ 28 | * 7 2 5 1 29 | * 返回: 30 | * 31 | * [ 32 | * [5,4,11,2], 33 | * [5,8,4,5] 34 | * ] 35 | * 36 | * 来源:力扣(LeetCode) 37 | * 链接:https://leetcode-cn.com/problems/path-sum-ii 38 | */ 39 | public class pathSum113 { 40 | /** 41 | * 执行用时 :1 ms, 在所有 Java 提交中击败了99.98%的用户 42 | * 内存消耗 :40 MB, 在所有 Java 提交中击败了5.26%的用户 43 | */ 44 | List> res; 45 | public List> pathSum(TreeNode root, int sum) { 46 | res = new ArrayList<>(); 47 | if(root == null) 48 | return res; 49 | backTrack(root, sum, 0, new ArrayList<>()); 50 | return res; 51 | } 52 | 53 | private void backTrack(TreeNode x, int sum, int curSum, List vals){ 54 | vals.add(x.val); 55 | curSum += x.val; 56 | if(x.left == null && x.right == null){ 57 | if(curSum == sum){ 58 | res.add(new ArrayList<>(vals)); 59 | } 60 | vals.remove(vals.size() - 1); 61 | return; 62 | } 63 | if(x.left != null) 64 | backTrack(x.left, sum, curSum, vals); 65 | if(x.right != null) 66 | backTrack(x.right, sum, curSum, vals); 67 | vals.remove(vals.size() - 1); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /medium/TwoPoint/maxArea.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ustcyyw/yyw_algorithm/d6b3b7204c955d936180d39e2582c21a34eacc34/medium/TwoPoint/maxArea.md -------------------------------------------------------------------------------- /medium/TwoPoint/minSubArrayLen.java: -------------------------------------------------------------------------------- 1 | package medium.TwoPoint; 2 | 3 | /** 4 | * @Time : 2020年3月2日10:22:00 5 | * @Author : yyw@ustc 6 | * @E-mail : yang0@mail.ustc.edu.cn 7 | * @Github : https://github.com/ustcyyw 8 | * @desc : 9 | */ 10 | 11 | /** 12 | * 给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组。如果不存在符合条件的连续子数组,返回 0。 13 | * 14 | * 示例:  15 | * 输入: s = 7, nums = [2,3,1,2,4,3] 16 | * 输出: 2 17 | * 解释: 子数组 [4,3] 是该条件下的长度最小的连续子数组。 18 | * 19 | * 进阶: 20 | * 如果你已经完成了O(n) 时间复杂度的解法, 请尝试 O(n log n) 时间复杂度的解法。 21 | * 22 | * 来源:力扣(LeetCode) 23 | * 链接:https://leetcode-cn.com/problems/minimum-size-subarray-sum 24 | */ 25 | public class minSubArrayLen { 26 | /** 27 | * 执行用时 :2 ms, 在所有 Java 提交中击败了89.60%的用户 28 | * 内存消耗 :42.8 MB, 在所有 Java 提交中击败了5.07%的用户 29 | */ 30 | public int minSubArrayLen(int s, int[] nums) { 31 | if(nums.length == 0) 32 | return 0; 33 | int min = Integer.MAX_VALUE; 34 | int product = nums[0]; 35 | int left = 0, right = 1; 36 | while(right < nums.length || product >= s){ 37 | if(product >= s){ 38 | min = Math.min(min, right - left); 39 | product = product - nums[left++]; 40 | } 41 | else { 42 | product = product + nums[right++]; 43 | } 44 | } 45 | return min == Integer.MAX_VALUE ? 0 : min; 46 | } 47 | } 48 | --------------------------------------------------------------------------------