LeeCode三百题-6

[TOC]


代码工程获取

git clone --depth=1 https://gitee.com/SevDaisy/LeeCode300.git

#51 N 皇后

官方题解写的很好

  • 最暴力的解法:4个皇后在16个棋盘上总共有 16×15×14×13 种,暴力搜索
  • 剪枝优化:每行每列都只能有一个皇后。所以就是4行,每行有4种可能,总共是 4×3×2×1
  • 问题:如何保证每行只有一个皇后?
    • 对行做遍历循环,每次循环会且仅会放置一个皇后在当前这一行
  • 问题:如何保证每列只有一个皇后?
    • 将已放置皇后的列号放入一个集合
    • 每次查看要放置的新皇后所在的列号是否已经在集合中存在
    • 如果已存在,则说明新皇后不能放在这儿
  • 问题:如何保证每条斜线只有一个皇后?
    • 斜线分成两种,撇线和捺线
      • 撇线:i+j 为常数
      • 捺线:i-j 为常数
    • 核心思路:
      • 用一个数据结构表示各斜线上是否已经放置了皇后
      • 在准备放新皇后的时候,去这个数据结构里查询
    • 有两种方法:【集合储存】【位运算储存】
  • 关键点:用三个(n)辅助空间分别记录列撇捺的状态,并不能用空间成功地换到时间
    • 不用辅助空间,每次都主动去遍历棋盘,反而要快得多
    • 至于是为什么,要好好琢磨
package Order300;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class T51_n_queens {

public static void main(String[] args) {
BaseNode.util.errPrintList(new Solution().solveNQueens(1), "line", "\n");
}

/**
* 这个是力扣上两毫秒的范程
* 比我优化了辅助空间(用棋盘本身作为辅助空间)
* 用函数包装来重新定义了验证逻辑
*
* 真搞不懂,明明它这样每次都验证,复杂度更高啊!
* 不过辅助空间少了,这样就少了许多的写操作,—— 以大量的读操作代替了
*/
static class Solution_2ms {

class Solution {

public List<List<String>> solveNQueens(int n) {
List<List<String>> res = new ArrayList<List<String>>();
char[][] chessboard = new char[n][n];

for (char[] c : chessboard) {
Arrays.fill(c, '.');
}
trackback(0, n, chessboard, res);
return res;
}

public void trackback(
int row,
int n,
char[][] chessboard,
List<List<String>> res
) {
if (row >= n) {
res.add(Array2List(chessboard));
return;
}

for (int i = 0; i < n; i++) {
if (isValid(chessboard, row, i, n)) {
chessboard[row][i] = 'Q';
trackback(row + 1, n, chessboard, res);
chessboard[row][i] = '.';
}
}
}

public List<String> Array2List(char[][] chessboard) {
List<String> list = new ArrayList<String>();
for (char[] c : chessboard) {
list.add(String.copyValueOf(c));
}
return list;
}

public boolean isValid(char[][] chessboard, int row, int col, int n) {
for (int i = 0; i < row; i++) {
if (chessboard[i][col] == 'Q') {
return false;
}
}

for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
if (chessboard[i][j] == 'Q') {
return false;
}
}

for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
if (chessboard[i][j] == 'Q') {
return false;
}
}

return true;
}
}
}

/**
* 这个是力扣上一毫秒的范程
* 用了位运算
*/
static class Solution_1ms {

class Solution {

public List<List<String>> solveNQueens(int n) {
int[] queens = new int[n];
Arrays.fill(queens, -1);
List<List<String>> solutions = new ArrayList<List<String>>();
solve(solutions, queens, n, 0, 0, 0, 0);
return solutions;
}

public void solve(
List<List<String>> solutions,
int[] queens,
int n,
int row,
int columns,
int diagonals1,
int diagonals2
) {
if (row == n) {
List<String> board = generateBoard(queens, n);
solutions.add(board);
} else {
int availablePositions =
((1 << n) - 1) & (~(columns | diagonals1 | diagonals2));
while (availablePositions != 0) {
int position = availablePositions & (-availablePositions);
availablePositions = availablePositions & (availablePositions - 1);
int column = Integer.bitCount(position - 1);
queens[row] = column;
solve(
solutions,
queens,
n,
row + 1,
columns | position,
(diagonals1 | position) << 1,
(diagonals2 | position) >> 1
);
queens[row] = -1;
}
}
}

public List<String> generateBoard(int[] queens, int n) {
List<String> board = new ArrayList<String>();
for (int i = 0; i < n; i++) {
char[] row = new char[n];
Arrays.fill(row, '.');
row[queens[i]] = 'Q';
board.add(new String(row));
}
return board;
}
}
}

/* 第一次尝试 3个boolean[] 递归回溯 时间 4ms => 60.96% 空间 39.3MB => 13.05% */
static class Solution {

public List<List<String>> solveNQueens(int n) {
List<List<String>> ans = new ArrayList<>();
List<List<Character>> path = new ArrayList<List<Character>>() {
{
for (int i = 0; i < n; i++) {
add(
new ArrayList<Character>() {
{
for (int i = 0; i < n; i++) {
add('.');
}
}
}
);
}
}
};
backtrace(
ans,
path,
new boolean[n],
new boolean[2 * n - 1],
new boolean[2 * n - 1],
0,
n
);
return ans;
}

private boolean backtrace(
List<List<String>> answers,
List<List<Character>> path,
boolean[] col,
boolean[] pie撇,
boolean[] na捺,
int step,
int goal
) {
if (step == goal) {
answers.add(
new ArrayList<String>() {
{
for (List<Character> list : path) {
StringBuilder builder = new StringBuilder();
for (Character c : list) {
builder.append(c);
}
add(builder.toString());
}
}
}
);
return true;
}
for (int i = 0; i < goal; i++) {
if (!(col[i] || pie撇[step + i] || na捺[goal + step - i - 1])) {
path.get(step).set(i, 'Q');
col[i] = true;
pie撇[step + i] = true;
na捺[goal + step - i - 1] = true;
backtrace(answers, path, col, pie撇, na捺, step + 1, goal);
path.get(step).set(i, '.');
col[i] = false;
pie撇[step + i] = false;
na捺[goal + step - i - 1] = false;
}
}
return false;
}
}
}

#52 N皇后 II

  • N皇后问题没意思,这题不想写了,抄个答案完事儿
package Order300;

import java.util.HashSet;
import java.util.Set;

public class T52_n_queens_ii {

static class Solution {

public int totalNQueens(int n) {
Set<Integer> columns = new HashSet<Integer>();
Set<Integer> diagonals1 = new HashSet<Integer>();
Set<Integer> diagonals2 = new HashSet<Integer>();
return backtrack(n, 0, columns, diagonals1, diagonals2);
}

public int backtrack(
int n,
int row,
Set<Integer> columns,
Set<Integer> diagonals1,
Set<Integer> diagonals2
) {
if (row == n) {
return 1;
} else {
int count = 0;
for (int i = 0; i < n; i++) {
if (columns.contains(i)) {
continue;
}
int diagonal1 = row - i;
if (diagonals1.contains(diagonal1)) {
continue;
}
int diagonal2 = row + i;
if (diagonals2.contains(diagonal2)) {
continue;
}
columns.add(i);
diagonals1.add(diagonal1);
diagonals2.add(diagonal2);
count += backtrack(n, row + 1, columns, diagonals1, diagonals2);
columns.remove(i);
diagonals1.remove(diagonal1);
diagonals2.remove(diagonal2);
}
return count;
}
}
}
}

#53 最大子序和

package Order300;

public class T53_maximum_subarray {

public static void main(String[] args) {
System.out.println(); // new Solution().maxSubArray(new int[] { -2, 1, -3, 4, -1, 2, 1, -5, 4 })
}

static class Solution_dp {

public int maxSubArray(int[] nums) {
/* DP解决,不过因为 f(x) 仅和 f(x-1) 有关,所以找个变量保存 f(x-1) 就好了 */
int preSubSum = 0; // 包含 前一个元素 的所有子数组的maxSubArraySum
int maxSubSum = nums[0]; // 从 起点至今 的所有子数组的maxSubArraySum
for (int x : nums) {
preSubSum = Math.max(preSubSum + x, x);
maxSubSum = Math.max(maxSubSum, preSubSum);
// System.out.printf("%d\t%d\t%d\n", x, preSubSum, maxSubSum);
}
return maxSubSum;
}
}

/**
* left,right 都 从左至右 遍历
* right++,滑动窗口添加新节点,并求出当前滑动窗口的 sum
* 若 [left].val < 0 或 sum([left,right]) < 0 则 left++
* - (同时被原来的 left 指向的元素移出滑动窗口)
* 每当 right,left,sum 整理过一次后,更新保存的 maxSubSum
**/
static class Solution_指针 {

public int maxSubArray(int[] nums) {
int maxSum = nums[0]; // 从 起点至今 的所有 滑动窗口 的 maxSum
int sum = 0; // 当前 滑动窗口 的 sum
int iMax = nums.length;
for (int right = 0, left = 0; right < iMax; right++) {
sum += nums[right];
while (left < right && (nums[left] < 0 || sum < 0)) {
sum -= nums[left++];
}
maxSum = Math.max(sum, maxSum);
}
return maxSum;
}
}
}

#54 螺旋矩阵

package Order300;

import java.util.ArrayList;
import java.util.List;

public class T54_spiral_matrix {

public static void main(String[] args) {
new Solution().spiralOrder(new int[5][5]);
}

/**
* 第一次尝试 保留了 syso,空间 37.1MB => 5.26%
* 第二次尝试 去除了 syso
* 第二次尝试 时间 0ms => 100% 空间 36.4MB => 73.79%
* 动态维护 iMin、iMax、jMin、jMax
* [iMin,iMax)
* [jMin,jMax)
**/
static class Solution {

public List<Integer> spiralOrder(int[][] matrix) {
List<Integer> out = new ArrayList<>();
/* 忽略健壮性检查 */
int iMin = 0;
int jMin = 0;
int iMax = matrix.length;
int jMax = matrix[0].length;
int step = 0;
int total = iMax * jMax;
int direction = 0;
for (int i = iMin, j = jMin; step < total;) {
step++;
out.add(matrix[i][j]);
// matrix[i][j] = step;
if (direction == 0) {
j++;
if (j == jMax) {
iMin++;
i = iMin;
j = jMax - 1;
direction = 1;
// System.out.printf("i: %2d\tj: %2d\t val: %02d\tdirection: %02d\n",i,j,step,direction);
}
} else if (direction == 1) {
i++;
if (i == iMax) {
jMax--;
i = iMax - 1;
j = jMax - 1;
direction = 2;
// System.out.printf("i: %2d\tj: %2d\t val: %02d\tdirection: %02d\n",i,j,step,direction);
}
} else if (direction == 2) {
j--;
if (j < jMin) {
iMax--;
i = iMax - 1;
j = jMin;
direction = 3;
// System.out.printf("i: %2d\tj: %2d\t val: %02d\tdirection: %02d\n",i,j,step,direction);
}
} else if (direction == 3) {
i--;
if (i < iMin) {
jMin++;
j = jMin;
i = iMin;
direction = 0;
// System.out.printf("i: %2d\tj: %2d\t val: %02d\tdirection: %02d\n",i,j,step,direction);
}
}
}
// for (int i = 0; i < 5; i++) {
// for (int j = 0; j < 5; j++) {
// System.out.printf("%2d ", matrix[i][j]);
// }
// System.out.println();
// }
return out;
}
}
}

#55 跳跃游戏

package Order300;

public class T55_jump_game {

public static void main(String[] args) {
System.out.println(new Solution().canJump(new int[] { 2, 3, 1, 1, 4 }));
System.out.println(new Solution().canJump(new int[] { 3, 2, 1, 0, 4 }));
}

/**
* 古早代码,现在提交
* 时间 2ms => 96.12% 空间 39.9MB => 43.81%
*/
static class Solution {

public boolean canJump(int[] nums) {
if (nums == null || nums.length < 1) return false;
int iMax = nums.length;
int rightMax = 0;

for (int i = 0; i < iMax && i <= rightMax; i++) {
/* 更新最远可达的位置 */
rightMax = Math.max(rightMax, i + nums[i]);
/* 如果最远已经达到或者超过终点了,返回成功 */
if (rightMax + 1 >= iMax) return true;
}

return false;
}
}
}

#56 合并区间

  • 官方的1ms范程,没看懂。
  • 如果有兴趣,可以请联系 qq 1052886775 提醒我。我谢谢你。🌝
package Order300;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class T56_merge_intervals {

public static void main(String[] args) {
new Solution()
.merge(new int[][] { { 1, 3 }, { 2, 6 }, { 8, 10 }, { 15, 18 } });
new Solution().merge(new int[][] { { 1, 4 }, { 4, 5 } }); // -> [1,5]
new Solution().merge(new int[][] { { 1, 4 }, { 1, 5 } }); // -> [1,5]
new Solution().merge(new int[][] { { 1, 4 }, { 2, 5 } }); // -> [1,5]
new Solution().merge(new int[][] { { 1, 4 }, { 0, 0 } }); // -> [1,4] [0,0]
new Solution()
.merge(new int[][] { { 2, 3 }, { 5, 5 }, { 2, 2 }, { 3, 4 }, { 3, 4 } });
new Solution()
.merge(new int[][] { { 2, 2 }, { 3, 3 }, { 4, 4 }, { 4, 4 }, { 5, 5 } });
new Solution()
.merge(new int[][] { { 4, 6 }, { 2, 2 }, { 2, 1 }, { 1, 7 } });
}

/**
* 第一次尝试 排序+双指针 时间 5ms => 96.45% 空间 41MB => 54.59%
* 一开始就想着 排序+双指针,但是怎么都写不对
* 我把 start 和 end 分成了两个一维数组再排序,这浪费了一些信息
* 照官方指导,将 二维数组[](start,end)直接排序以后再双指针,很轻松就写出来了
*/
static class Solution {

public int[][] merge(int[][] intervals) {
/**
* 第二个参数可以用 lamba 表达式,不一定要写完整的 new Compator @Override ...
* 排序可以仅按照 start 来排。因为 end 的顺序并不重要。
*/
Arrays.sort(intervals, (a, b) -> a[0] - b[0]);

List<int[]> buffer = new ArrayList<>();
int start, end;
start = intervals[0][0];
end = intervals[0][1];
for (int[] pair : intervals) {
if (end < pair[0]) {
buffer.add(new int[] { start, end });
start = pair[0];
end = pair[1];
} else {
end = (end > pair[1] ? end : pair[1]);
}
}
buffer.add(new int[] { start, end });

/**
* 不需要自己写转换。可以直接用 ArrayList 对象自带的 toArray 方法。
* int[][] out = new int[buffer.size()][2];
* for (int i = 0; i < buffer.size(); i++) {
* out[i] = buffer.get(i);
* }
* return out;
*/
return buffer.toArray(new int[0][]);
}
}

/**
* LeeCode 上 1ms 的范程
* TODO 没看懂。。。 苗苗搞我,我心情极差。不想看了。之后有空再看。
*/
class Solution_1ms {

public int[][] merge(int[][] intervals) {
int max = -1; //生成标记数组的最大索引
for (int[] interval : intervals) {
if (max < interval[1]) {
max = interval[1];
}
}
int[] flag = new int[++max];
int start, end, count;
for (int[] interval : intervals) {
start = interval[0];
end = interval[1];
count = 1 + end - start; //区域长度
//flag[start] = 1 + end - start;

int p = start;
while (p > -1 && flag[p] == 0) {
p--;
}
int startSource = start;
//若 p==-1 , start之前全为0, 则从start开始, start不变
if (p != -1) {
int tempEnd = p + flag[p] - 1;
if (tempEnd >= start) { //有交叉区域
//初始化区间开头 flag[p + 1] = 1 + end - p;
start = p;
//区间未被包括 区间已被包括
tempEnd = tempEnd > end ? tempEnd : end;
count = tempEnd - p + 1;
}
}

//从当前位置向后查找有没有不为零的标记,如果有,清除标记,并判断是否要改变count数值
for (int i = startSource; i <= end; i++) {
if (flag[i] != 0) { //end落在区域内
if (flag[i] > end - i + 1) {
// end = i + flag[i] - 1;
//flag[start] = 1 + (i + flag[i] - 1) - start;
count = i + flag[i] - start;
flag[i] = 0;
i = end;
} else { //end在区域右侧
p = i;
i += flag[i] - 1;
flag[p] = 0;
}
}
}
flag[start] = count;
}
//遍历flag,获取区间
int index = 0, pos = 0;
while (index < max) {
if (flag[index] > 0) {
start = index;
index = start + flag[start] - 1;
intervals[pos][0] = start;
intervals[pos][1] = index;
pos++;
}
index++;
}
//生成返回结果
int[][] res = new int[pos][2];
System.arraycopy(intervals, 0, res, 0, pos);

return res;
}
}
}

#57 插入区间

package Order300;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class T57_insert_interval {

/**
* 第一次尝试 排序+双指针 时间 4ms => 16.24% 空间 40.5MB => 79.84%
* 用的是 T56 的常规解法,居然只超过 16.24% 的人
*/
static class Solution_first {

public int[][] insert(int[][] intervals, int[] newInterval) {
List<int[]> pool = new ArrayList<int[]>() {
{
add(newInterval);
for (int[] pair : intervals) {
add(pair);
}
}
};
Collections.sort(pool, (a, b) -> a[0] - b[0]);

List<int[]> buffer = new ArrayList<>();

int start, end;
start = pool.get(0)[0];
end = pool.get(0)[1];
for (int[] pair : pool) {
if (end < pair[0]) {
buffer.add(new int[] { start, end });
start = pair[0];
end = pair[1];
} else {
end = (end > pair[1] ? end : pair[1]);
}
}
buffer.add(new int[] { start, end });

return buffer.toArray(new int[0][]);
}
}

/**
* 1ms 范程 (未验证)
*
* 之所以比我快这么多,原因很简单。
* 本题中给定的 intervals 是保证不会有重叠部分的,无需考虑它!
*/
static class Solution_1ms {

public int[][] insert(int[][] intervals, int[] newInterval) {
int left = newInterval[0];
int right = newInterval[1];
boolean placed = false;
List<int[]> ansList = new ArrayList<int[]>();
for (int[] interval : intervals) {
if (interval[0] > right) {
// 在插入区间的右侧且无交集
if (!placed) {
ansList.add(new int[] { left, right });
placed = true;
}
ansList.add(interval);
} else if (interval[1] < left) {
// 在插入区间的左侧且无交集
ansList.add(interval);
} else {
// 与插入区间有交集,计算它们的并集
left = Math.min(left, interval[0]);
right = Math.max(right, interval[1]);
}
}
if (!placed) {
ansList.add(new int[] { left, right });
}
int[][] ans = new int[ansList.size()][2];
for (int i = 0; i < ansList.size(); ++i) {
ans[i] = ansList.get(i);
}
return ans;
}
}

/**
* 第二次尝试 仅考虑 newInterval 附近的区间合并问题
* 时间 2ms => 57.87% 空间 40.6MB => 70.71%
*/
static class Solution {

public int[][] insert(int[][] intervals, int[] newInterval) {
int left = newInterval[0];
int right = newInterval[1];
List<int[]> buffer = new ArrayList<>();
for (int[] pair : intervals) {
if (pair[0] > right || pair[1] < left) {
buffer.add(pair);
} else {
left = (left < pair[0] ? left : pair[0]);
right = (right > pair[1] ? right : pair[1]);
}
}
buffer.add(new int[] { left, right });
/* 必须有这个排序,不然会因为结果集顺序不正确而判错 */
Collections.sort(buffer, (a, b) -> a[0] - b[0]);
return buffer.toArray(new int[0][]);
}
}
}

#58 最后一个单词的长度

package Order300;

public class T58_length_of_last_word {

public static void main(String[] args) {
System.out.println(new Solution().lengthOfLastWord("Hello World"));
}

/**
* 第一次尝试 时间 0ms => 100%
* 巨简单的题,没什么好说的。
*/
static class Solution {

public int lengthOfLastWord(String s) {
int right = -1, left = -1;
for (int i = s.length() - 1; i >= 0; i--) {
if (right < 0) {
if (s.charAt(i) != ' ') {
right = i;
continue;
}
} else {
if (s.charAt(i) == ' ') {
left = i;
break;
}
}
}
return right - left;
}
}
}

#59 螺旋矩阵 II

package Order300;

public class T59_spiral_matrix_ii {

public static void main(String[] args) {
new Solution().generateMatrix(1);
new Solution().generateMatrix(2);
new Solution().generateMatrix(3);
new Solution().generateMatrix(4);
new Solution().generateMatrix(5);
}

/**
* 第一次尝试 重构改用了 T54 的代码(因此在变量设置上会显得有些许累赘)
* 时间 0ms => 100% 空间 36.4MB => 62.94%
*/
static class Solution {

public int[][] generateMatrix(int n) {
int[][] matrix = new int[n][n];
/* 忽略健壮性检查 */
int iMin = 0;
int jMin = 0;
int iMax = n;
int jMax = n;
int step = 0;
int total = iMax * jMax;
int direction = 0;
for (int i = iMin, j = jMin; step < total;) {
step++;
matrix[i][j] = step;
if (direction == 0) {
j++;
if (j == jMax) {
iMin++;
i = iMin;
j = jMax - 1;
direction = 1;
// System.out.printf("i: %2d\tj: %2d\t val: %02d\tdirection: %02d\n",i,j,step,direction);
}
} else if (direction == 1) {
i++;
if (i == iMax) {
jMax--;
i = iMax - 1;
j = jMax - 1;
direction = 2;
// System.out.printf("i: %2d\tj: %2d\t val: %02d\tdirection: %02d\n",i,j,step,direction);
}
} else if (direction == 2) {
j--;
if (j < jMin) {
iMax--;
i = iMax - 1;
j = jMin;
direction = 3;
// System.out.printf("i: %2d\tj: %2d\t val: %02d\tdirection: %02d\n",i,j,step,direction);
}
} else if (direction == 3) {
i--;
if (i < iMin) {
jMin++;
j = jMin;
i = iMin;
direction = 0;
// System.out.printf("i: %2d\tj: %2d\t val: %02d\tdirection: %02d\n",i,j,step,direction);
}
}
}
// System.out.println("\n--------------------");
// for (int i = 0; i < n; i++) {
// for (int j = 0; j < n; j++) {
// System.out.printf("%2d ", matrix[i][j]);
// }
// System.out.println();
// }
return matrix;
}
}
}

#60 排列序列

package Order300;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

public class T60_permutation_sequence {

public static void main(String[] args) {
Solution_官方题解_1ms s = new Solution_官方题解_1ms();
s.getPermutation(3, 1); //-> 123
s.getPermutation(4, 2); //-> 1243
s.getPermutation(4, 17); //-> 3412
s.getPermutation(4, 18); //-> 3421
}

/**
* 第一次尝试 k / % 法 自研算法
* 时间 3ms => 37.89% 空间 36MB => 25.63%
*/
static class Solution_first {

public String getPermutation(int n, int k) {
List<String> pool = new LinkedList<String>() {
{
for (int i = 0; i < n; i++) {
add(String.valueOf(i + 1));
}
}
};
StringBuilder builder = new StringBuilder();
int last = k - 1;/* 这处 -1 是一个难点 */
int level = n;
/* 这处 > 1 和 循环之后的 append(get(0)) 也是一个难点 */
while (level > 1) {
level--;
builder.append(pool.remove(last / fact(level)));
last = last % fact(level);
}
builder.append(pool.get(0));
System.out.println(builder.toString());
return builder.toString();
}

private int fact(int n) {
if (n == 0 || n == 1) {
return 1;
} else {
return n * fact(n - 1);
}
}
}

static class Solution_官方题解_1ms {

public String getPermutation(int n, int k) {
/**
* 对于有限个数的阶乘 用【数组】代替【rec函数】
* 优化效果:时间基本不变,空间占用变少
*/
int[] factorial = new int[n];
factorial[0] = 1;
for (int i = 1; i < n; ++i) {
factorial[i] = factorial[i - 1] * i;
}
--k;
StringBuffer ans = new StringBuffer();
/**
* 这个 valid 真的绝,比我的链表好了太多!
*
* valid 是 i in [0,n+1) a[i]=1 的数组 (填充全1用Arrays.fill实现)
* order 每次用 k/fact(n-i)+1 算得这次要用的数字应该是第几个
* 用 j in [1,n] 遍历 valid 数组
* 如果 j 没有被使用过,那么 valid[j] 就会是 1,order -= valid[j] 就数过去了一个数
* 如果 j 被使用过了,那么 valid[j] 就会是 0,order -= valid[j] 就相当于白数
* 等 order 减到 0 了,说明遇到了要找的第 order 个数。这个数就是当前的 j。
* StringBuffer 中添加这个 j,并且 valid[j] 标记为 0
* k 更新为 k % factorial[n - i]
*/
int[] valid = new int[n + 1];
Arrays.fill(valid, 1);
for (int i = 1; i <= n; ++i) {
int order = k / factorial[n - i] + 1;
for (int j = 1; j <= n; ++j) {
order -= valid[j];
if (order == 0) {
ans.append(j);
valid[j] = 0;
break;
}
}
k %= factorial[n - i];
}
return ans.toString();
}
}
}