Skip to content

Commit

Permalink
Implemented 'Next Greater Element III' challenge
Browse files Browse the repository at this point in the history
  • Loading branch information
wibosco committed Sep 5, 2024
1 parent 687b9cd commit 074deaf
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 24 deletions.
8 changes: 8 additions & 0 deletions LeetCode/LeetCode.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 */; };
Expand Down Expand Up @@ -1601,6 +1603,8 @@
430BE5552C82184D00265975 /* MinimumAverageOfSmallestAndLargestElementsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MinimumAverageOfSmallestAndLargestElementsTests.swift; sourceTree = "<group>"; };
430BE5572C826F3700265975 /* NumberOfDistinctAverages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberOfDistinctAverages.swift; sourceTree = "<group>"; };
430BE5592C826F8A00265975 /* NumberOfDistinctAveragesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberOfDistinctAveragesTests.swift; sourceTree = "<group>"; };
430E11AD2C8A3D3400D40AEF /* NextGreaterElementIII.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextGreaterElementIII.swift; sourceTree = "<group>"; };
430E11AF2C8A3D5F00D40AEF /* NextGreaterElementIIITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextGreaterElementIIITests.swift; sourceTree = "<group>"; };
4311BC632A5838AC00137AED /* IsSubsequence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IsSubsequence.swift; sourceTree = "<group>"; };
4311BC652A5838ED00137AED /* IsSubsequenceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IsSubsequenceTests.swift; sourceTree = "<group>"; };
4311BC672A5849FE00137AED /* PascalsTriangle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PascalsTriangle.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -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 */,
Expand Down Expand Up @@ -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 */,
Expand Down Expand Up @@ -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 */,
Expand Down Expand Up @@ -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 */,
Expand Down
89 changes: 89 additions & 0 deletions LeetCode/LeetCode/Challenges/NextGreaterElementIII.swift
Original file line number Diff line number Diff line change
@@ -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 right-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 and it is the break
//element that goes in descending 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
}
}
66 changes: 43 additions & 23 deletions LeetCode/LeetCode/Challenges/NextPermutation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,45 +11,65 @@ 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.
//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 right-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 and it is the break
//element that goes in descending 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`
//
//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.count).reversed() {
let ms = ls - 1
if nums[ls] > nums[ms] {
pivot = ms
let insidePivot = (pivot + 1)
for index in (insidePivot..<nums.count).reversed() {
if nums[index] > 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()
}

Expand Down
47 changes: 47 additions & 0 deletions LeetCode/LeetCodeTests/Tests/NextGreaterElementIIITests.swift
Original file line number Diff line number Diff line change
@@ -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)
}
}
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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` |
Expand Down Expand Up @@ -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.

Expand Down

0 comments on commit 074deaf

Please sign in to comment.