diff --git a/LeetCode/LeetCode.xcodeproj/project.pbxproj b/LeetCode/LeetCode.xcodeproj/project.pbxproj index d106c3d..7413875 100644 --- a/LeetCode/LeetCode.xcodeproj/project.pbxproj +++ b/LeetCode/LeetCode.xcodeproj/project.pbxproj @@ -558,6 +558,8 @@ 430BE5562C82184D00265975 /* MinimumAverageOfSmallestAndLargestElementsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430BE5552C82184D00265975 /* MinimumAverageOfSmallestAndLargestElementsTests.swift */; }; 430BE5582C826F3700265975 /* NumberOfDistinctAverages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430BE5572C826F3700265975 /* NumberOfDistinctAverages.swift */; }; 430BE55A2C826F8A00265975 /* NumberOfDistinctAveragesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430BE5592C826F8A00265975 /* NumberOfDistinctAveragesTests.swift */; }; + 430E11AE2C8A3D3400D40AEF /* NextGreaterElementIII.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430E11AD2C8A3D3400D40AEF /* NextGreaterElementIII.swift */; }; + 430E11B02C8A3D5F00D40AEF /* NextGreaterElementIIITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430E11AF2C8A3D5F00D40AEF /* NextGreaterElementIIITests.swift */; }; 4311BC642A5838AC00137AED /* IsSubsequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4311BC632A5838AC00137AED /* IsSubsequence.swift */; }; 4311BC662A5838ED00137AED /* IsSubsequenceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4311BC652A5838ED00137AED /* IsSubsequenceTests.swift */; }; 4311BC682A5849FE00137AED /* PascalsTriangle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4311BC672A5849FE00137AED /* PascalsTriangle.swift */; }; @@ -1601,6 +1603,8 @@ 430BE5552C82184D00265975 /* MinimumAverageOfSmallestAndLargestElementsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MinimumAverageOfSmallestAndLargestElementsTests.swift; sourceTree = ""; }; 430BE5572C826F3700265975 /* NumberOfDistinctAverages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberOfDistinctAverages.swift; sourceTree = ""; }; 430BE5592C826F8A00265975 /* NumberOfDistinctAveragesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberOfDistinctAveragesTests.swift; sourceTree = ""; }; + 430E11AD2C8A3D3400D40AEF /* NextGreaterElementIII.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextGreaterElementIII.swift; sourceTree = ""; }; + 430E11AF2C8A3D5F00D40AEF /* NextGreaterElementIIITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextGreaterElementIIITests.swift; sourceTree = ""; }; 4311BC632A5838AC00137AED /* IsSubsequence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IsSubsequence.swift; sourceTree = ""; }; 4311BC652A5838ED00137AED /* IsSubsequenceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IsSubsequenceTests.swift; sourceTree = ""; }; 4311BC672A5849FE00137AED /* PascalsTriangle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PascalsTriangle.swift; sourceTree = ""; }; @@ -2475,6 +2479,7 @@ 3D5C910527A7F7630035C399 /* NestedListWeightSumII.swift */, 43349F752A52FB3D002E2379 /* NetworkDelayTime.swift */, 3D5C90C227A7F7620035C399 /* NextGreaterElementI.swift */, + 430E11AD2C8A3D3400D40AEF /* NextGreaterElementIII.swift */, 3D5C90F427A7F7620035C399 /* NextPermutation.swift */, 43169EDD2A61849700DF16FD /* NonDecreasingArray.swift */, 434679492A56BE2A00A1D225 /* NonOverlappingIntervals.swift */, @@ -2990,6 +2995,7 @@ 3D5C923327A7F76B0035C399 /* NestedListWeightSumIITests.swift */, 3D5C927227A7F76C0035C399 /* NestedListWeightSumTests.swift */, 43349F772A52FB4D002E2379 /* NetworkDelayTimeTests.swift */, + 430E11AF2C8A3D5F00D40AEF /* NextGreaterElementIIITests.swift */, 3D5C927027A7F76C0035C399 /* NextGreaterElementITests.swift */, 3D5C925B27A7F76C0035C399 /* NextPermutationTests.swift */, 43169EDF2A6184C400DF16FD /* NonDecreasingArrayTests.swift */, @@ -3773,6 +3779,7 @@ 43026BCB2AFED2B20066473B /* CountNodesEqualToAverageOfSubtree.swift in Sources */, 3D46242527E2921900A888C1 /* SortColors.swift in Sources */, 3D8424DF27F23D6100B91AAB /* RandomizedSet.swift in Sources */, + 430E11AE2C8A3D3400D40AEF /* NextGreaterElementIII.swift in Sources */, 3D146578280A10E500DF647A /* NumberOfDaysBetweenTwoDates.swift in Sources */, 438C2EDF2B1F8CF800C43AAA /* InsufficientNodesInRootToLeafPaths.swift in Sources */, 3D5C916E27A7F7630035C399 /* SearchSortedArrayOfUnknownSize.swift in Sources */, @@ -3936,6 +3943,7 @@ 3D5C92CC27A7F76C0035C399 /* AsteroidCollisionTests.swift in Sources */, 432F13872B267568003E9989 /* TwoSumBSTsTests.swift in Sources */, 436634AE2AFE81730087F13F /* TwoSumIVInputIsABSTTests.swift in Sources */, + 430E11B02C8A3D5F00D40AEF /* NextGreaterElementIIITests.swift in Sources */, 3D5C92D127A7F76C0035C399 /* LongestSubstringWithoutRepeatingCharactersTests.swift in Sources */, 3D5C931427A7F76C0035C399 /* IntersectionOfTwoArraysTests.swift in Sources */, 3D1D0C8128061A23006DA350 /* ValidNumberTests.swift in Sources */, diff --git a/LeetCode/LeetCode/Challenges/NextGreaterElementIII.swift b/LeetCode/LeetCode/Challenges/NextGreaterElementIII.swift new file mode 100644 index 0000000..9fa089e --- /dev/null +++ b/LeetCode/LeetCode/Challenges/NextGreaterElementIII.swift @@ -0,0 +1,89 @@ +// +// NextGreaterElementIII.swift +// LeetCode +// +// Created by William Boles on 05/09/2024. +// + +import Foundation + +//https://leetcode.com/problems/next-greater-element-iii/ +struct NextGreaterElementIII { + + //Time: O(n) where n is the number of elements in `nums` + //Space: O(n) + //array + //two pointers + //inline + //permutation + //pivot & swap + // + //Solution Description: + //The next greater element is the next permutation of `n` that is the smallest version that is greater than `n`. To find + //this "smallest greater than" permutation we need to find an element that when swapped with another minimally increases + //`n` in value. The element that minimally increases `n` in value is the left-most element that when reading from right + //to left is smaller than it's right neighbor i.e. the elements to it's right are in ascending order (right to left) and + //this element breaks that order e.g. + // + //In `834762` the `4` is that value + // + //We call this breaking pattern element the `pivot`. Once we have the `pivot` we need to find the smallest value that is + //greater than it to swap over - this is to minimise the increase in `n`. You might be thinking if everything to the + //right of `pivot` is ascending won't that smallest value be the last digit in `n` - be careful here, just because the + //`pivot` is smaller than it's neighor doesn't mean that it is smaller than all elements to the right and swapping the + //`pivot` with an element smaller than itself would not result in the smallest permutation of `n` greater than `n` but + //rather a smaller permutation of `n` e.g. + // + //In `834762` we don't want to swap the `4` with the `2` as `832764` < `834762` instead we want to swap it with the `6` + // + //So having swapped the `pivot` with the smallest right neighbor that is still greater than the `pivot` e.g. + // + //`836742` + // + //we can still make this number smaller by reversing those ascending numbers to the right of the old `pivot` index to + //descending order e.g. + // + //`836247` + // + //With our smallest permutation we just need to check that it isn't larger than Int32.max and return either it or -1. + // + //Similar to https://leetcode.com/problems/next-permutation/ + func nextGreaterElement(_ n: Int) -> Int { + guard n > 9 else { + return -1 + } + + var nums = Array(String(n)) + + //find the least significant value that is smaller than it's right neighbor + var pivot = nums.count - 2 + while pivot >= 0, nums[(pivot + 1)] <= nums[pivot] { + pivot -= 1 + } + + guard pivot >= 0 else { + //`n` is already as large as it can be + return -1 + } + + //find the least-significant-value to the right of the pivot that is larger than the pivot as we want + //`nums` to be > `n` + var lsv = nums.count - 1 + while lsv > 0, nums[lsv] <= nums[pivot] { + lsv -= 1 + } + nums.swapAt(pivot, lsv) + + //now the pivot is moved into place making `nums` > `n` we need to minimise `nums` so it's as small + //as can be while still > `n` + nums[(pivot + 1)...].reverse() + + guard let num32 = Int32(String(nums)) else { //casting to Int32 as problem expects Int32 + return -1 + } + + let num = Int(num32) //casting to Int to make comparison easier with `n` + + return num > n ? num : -1 + } +} diff --git a/LeetCode/LeetCode/Challenges/NextPermutation.swift b/LeetCode/LeetCode/Challenges/NextPermutation.swift index 8cca83f..d7ef9b8 100644 --- a/LeetCode/LeetCode/Challenges/NextPermutation.swift +++ b/LeetCode/LeetCode/Challenges/NextPermutation.swift @@ -11,45 +11,64 @@ import Foundation //https://leetcode.com/problems/next-permutation/ struct NextPermutation { - //Time: O(n) - Don't be tricked by the for inside a for, notice the breaks + //Time: O(n) where n is the number of elements in `nums` //Space: O(1) //array //two pointers //inline + //permutation + //pivot & swap // //Solution Description: - //In order to find the next larger permutation we need to the smallest unit index (indexing from the right) that - //has a larger value than the index directly to its right - this becomes our pivot index (if this doesn't exist - //then we have the highest value premutation and should return the lowest possible permutation i.e. all in - //ascending order). We then need to switch out this pivot with the smallest unit index that has a larger value - //than the pivots value - please note that we only switch out one index. Finally having switched out the pivot - //we then just need to ensure that everything after the pivot is the smallest it can be by sorting it in ascending - //order. + //To find the next permutation we need to find an element that when swapped with another minimally increases `nums` in + //value. The element that minimally increases `n` in value is the left-most element that when reading from right to + //left is smaller than it's right neighbor i.e. the elements to it's right are in ascending order (right to left) and + //this element breaks that order e.g. + // + //In `834762` the `4` is that value + // + //We call this breaking order element the `pivot`. Once we have the `pivot` we need to find the smallest value that is + //greater than it to swap over - this is to minimise the increase in `n`. You might be thinking if everything to the + //right of `pivot` is ascending won't that smallest value be the last digit in `n` - be careful here, just because the + //`pivot` is smaller than it's neighor doesn't mean that it is smaller than all elements to the right and swapping the + //`pivot` with an element smaller than itself would not result in the smallest permutation of `n` greater than `n` but + //rather a smaller permutation of `n` e.g. + // + //In `834762` we don't want to swap the `4` with the `2` as `832764` < `834762` instead we want to swap it with the `6` + // + //So having swapped the `pivot` with the smallest right neighbor that is still greater than the `pivot` e.g. + // + //`836742` + // + //we can still make this number smaller by reversing those ascending numbers to the right of the old `pivot` index to + //descending order e.g. + // + //`836247` // //See: https://www.youtube.com/watch?v=quAS1iydq7U&t=1s + //Similar to: https://leetcode.com/problems/next-greater-element-iii/ static func nextPermutation(_ nums: inout [Int]) { guard nums.count > 1 else { return } - var pivot = -1 //so if no pivot is found the whole array will be sorted in ascending order - //ls - less significant index - //ms = more significant index i.e. one to the right - for ls in (1.. nums[ms] { - pivot = ms - let insidePivot = (pivot + 1) - for index in (insidePivot.. nums[pivot] { - nums.swapAt(pivot, index) - break - } - } - break + //find the least significant value that is smaller than it's right neighbor + var pivot = nums.count - 2 + while pivot >= 0, nums[(pivot + 1)] <= nums[pivot] { + pivot -= 1 + } + + if pivot >= 0 { + //find the least-significant-value to the right of the pivot that is larger than the pivot + var lsv = nums.count - 1 + while lsv > 0, nums[lsv] <= nums[pivot] { + lsv -= 1 } + nums.swapAt(pivot, lsv) } + //now the pivot is moved into place making `nums` > `n` we need to minimise `nums` so it's as small + //as can be while still > `n`. nums[(pivot + 1)...].reverse() } diff --git a/LeetCode/LeetCodeTests/Tests/NextGreaterElementIIITests.swift b/LeetCode/LeetCodeTests/Tests/NextGreaterElementIIITests.swift new file mode 100644 index 0000000..e1f90cd --- /dev/null +++ b/LeetCode/LeetCodeTests/Tests/NextGreaterElementIIITests.swift @@ -0,0 +1,47 @@ +// +// NextGreaterElementIIITests.swift +// LeetCodeTests +// +// Created by William Boles on 05/09/2024. +// + +import XCTest + +@testable import LeetCode + +final class NextGreaterElementIIITests: XCTestCase { + + // MARK: - Tests + + func test_A() { + let n = 12 + + let result = NextGreaterElementIII().nextGreaterElement(n) + + XCTAssertEqual(result, 21) + } + + func test_B() { + let n = 21 + + let result = NextGreaterElementIII().nextGreaterElement(n) + + XCTAssertEqual(result, -1) + } + + func test_C() { + let n = 2147483486 + + let result = NextGreaterElementIII().nextGreaterElement(n) + + XCTAssertEqual(result, -1) + } + + func test_D() { + let n = 2147483476 + + let result = NextGreaterElementIII().nextGreaterElement(n) + + XCTAssertEqual(result, 2147483647) + } +} diff --git a/README.md b/README.md index ebe57f0..995e3c9 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ A collection of coding challenges and their solutions from: | "Path exists" | `BFS`, `DFS`, `Disjont sets` | | "Path may not exist"| `isolated vertices`, `cycles` | | "Generate all", "All permutations", "All combinations", "All possible", "Choices", "Branching" | `Backtracking (DFS)` | -| "Next Permutation" | `pivot & sorting` | +| "Next Permutation" | `pivot & swap` | | "Sorted", "Maximum", "Minimum" | `Binary Search`, `Two pointers` | | "Right-most", "Left-most"| `Binary Search` | | "Iterating array comparing elements" | `Stack` | @@ -79,6 +79,7 @@ A collection of coding challenges and their solutions from: | Reverse order of substrings while keeping the same order of each substring| Two passes - one to reverse all, one to reverse each substring | `ReverseWordsInAStringII` | | Wrap an arrays indexes round an offset | `Modulo` | `CircularArrayLoop` | | Need to build a relatioship between two arrays | Sort the arrays and nest one in the other | `Heaters` | +| Find the next permutation of a number | `pivot & swap` | `NextPermutation` `NextGreaterElementIII` | A lot of problems can be treated as graph problems.