The challenge
Write a function that takes a positive integer and returns the next smaller positive integer containing the same digits.
For example:
nextSmaller(21) == 12
nextSmaller(531) == 513
nextSmaller(2071) == 2017
Return -1 (for Haskell
: return Nothing
, for Rust
: return None
), when there is no smaller number that contains the same digits. Also return -1 when the next smaller number with the same digits would require the leading digit to be zero.
nextSmaller(9) == -1
nextSmaller(111) == -1
nextSmaller(135) == -1
nextSmaller(1027) == -1 // 0721 is out since we don't write numbers with leading zeros
- some tests will include very large numbers.
- test data only employs positive integers.
The solution in Java
Option 1:
import java.util.Arrays;
import java.util.Collections;
import java.util.stream.Stream;
public class Solution {
public static long nextSmaller(long n) {
Integer[] val = Long.toString(n).chars().map(c -> c - '0').boxed().toArray(Integer[]::new);
int len = val.length;
for (int i = len - 1; i > 0; i--) {
if (val[i - 1] > val[i]) {
int maxIdx = i;
for (int j = i + 1; j < len; j++) {
if (val[i - 1] > val[j] && val[j] > val[maxIdx]) maxIdx = j;
}
val[i - 1] = val[i - 1] + val[maxIdx];
val[i - 1] = val[i - 1] - (val[maxIdx] = val[i - 1] - val[maxIdx]);
Arrays.sort(val, i, len, Collections.reverseOrder());
return val[0] == 0 ? - 1L : Long.valueOf(String.join("", Stream.of(val).map(String::valueOf).toArray(String[]::new)));
}
}
return -1L;
}
}
Option 2:
public class Solution {
public static long nextSmaller(long n) {
final char[] cs = Long.toString(n).toCharArray();
for (int l = cs.length, p = l-1, i; 0 < (i = p); p--) {
while (i < l && cs[i] < cs[p-1])
i++;
if (p < i)
return (p-1 == 0 && cs[i-1] == '0') ? -1 :
Long.parseLong(new StringBuilder(l)
.append(cs, p, i-1 - p).append(cs[p-1])
.append(cs, i, l - i).append(cs[i-1])
.reverse().insert(0, cs, 0, p-1).toString());
}
return -1;
}
}
Option 3:
public class Solution {
public static long nextSmaller(long n) {
final char[] chars = Long.toString(n).toCharArray();
for (int i = chars.length; 0 < i; i--) {
char digit = chars[i-1];
for (int j = i; j < chars.length; j++) {
if (chars[j] < digit) {
chars[i-1] = chars[j];
chars[j] = digit;
return (chars[0] == '0') ? -1 : Long.parseLong(new String(chars));
}
}
System.arraycopy(chars, i, chars, i-1, chars.length - i);
chars[chars.length-1] = digit;
}
return -1;
}
}
Test cases to validate our solution
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import org.junit.runners.JUnit4;
public class SolutionTests {
@Test
public void basicTests() {
assertEquals(12, Solution.nextSmaller(21));
assertEquals(790, Solution.nextSmaller(907));
assertEquals(513, Solution.nextSmaller(531));
assertEquals(-1, Solution.nextSmaller(1027));
assertEquals(414, Solution.nextSmaller(441));
assertEquals(123456789, Solution.nextSmaller(123456798));
}
}