mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-12-06 17:18:54 +08:00
commit
1d9b0ebbd2
@ -213,68 +213,147 @@ public class Arrangement implements Serializable {
|
||||
private static class ArrangementIterator implements Iterator<String[]> {
|
||||
|
||||
private final String[] datas;
|
||||
private final int n;
|
||||
private final int m;
|
||||
private final boolean[] visited;
|
||||
private final String[] buffer;
|
||||
private final Deque<Integer> stack = new ArrayDeque<>();
|
||||
boolean end = false;
|
||||
|
||||
// 每一层记录当前尝试的下标,-1表示还未尝试
|
||||
private final int[] indices;
|
||||
private int depth;
|
||||
private boolean end;
|
||||
|
||||
// 预取下一个元素
|
||||
private String[] nextItem;
|
||||
private boolean nextPrepared;
|
||||
|
||||
ArrangementIterator(String[] datas, int m) {
|
||||
this.datas = datas;
|
||||
this.n = datas.length;
|
||||
this.m = m;
|
||||
this.visited = new boolean[datas.length];
|
||||
this.buffer = new String[m];
|
||||
// 初始化 dfs 栈
|
||||
stack.push(0);
|
||||
this.visited = new boolean[n];
|
||||
this.nextItem = null;
|
||||
this.nextPrepared = false;
|
||||
|
||||
if (m < 0 || m > n) {
|
||||
// 无效或无解,直接结束
|
||||
this.indices = new int[Math.max(1, m)];
|
||||
this.buffer = new String[Math.max(1, m)];
|
||||
this.depth = -1;
|
||||
this.end = true;
|
||||
} else if (m == 0) {
|
||||
// m == 0: 只返回一个空数组
|
||||
this.indices = new int[0];
|
||||
this.buffer = new String[0];
|
||||
this.depth = 0;
|
||||
this.end = false;
|
||||
} else {
|
||||
this.indices = new int[m];
|
||||
Arrays.fill(this.indices, -1);
|
||||
this.buffer = new String[m];
|
||||
this.depth = 0;
|
||||
this.end = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return !end;
|
||||
if (end) return false;
|
||||
if (nextPrepared) return nextItem != null;
|
||||
prepareNext();
|
||||
return nextItem != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] next() {
|
||||
while (!stack.isEmpty()) {
|
||||
int depth = stack.size() - 1;
|
||||
if (end && !nextPrepared) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
if (!nextPrepared) {
|
||||
prepareNext();
|
||||
}
|
||||
if (nextItem == null) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
String[] ret = nextItem;
|
||||
// 清除预取缓存,下一次需要重新准备
|
||||
nextItem = null;
|
||||
nextPrepared = false;
|
||||
// 如果m == 0,该项是唯一项,迭代结束
|
||||
if (m == 0) {
|
||||
end = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int idx = stack.pop();
|
||||
if (idx >= datas.length) {
|
||||
// 这一层遍历结束
|
||||
if (!stack.isEmpty()) {
|
||||
int prev = stack.pop();
|
||||
stack.push(prev + 1);
|
||||
/**
|
||||
* 将状态推进到下一个可返回的排列并把它放入 nextItem。
|
||||
* 如果无更多排列,则将 end=true 并把 nextItem 置为 null。
|
||||
*/
|
||||
private void prepareNext() {
|
||||
// 已经准备过或已结束
|
||||
if (nextPrepared || end) {
|
||||
nextPrepared = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// special-case m == 0
|
||||
if (m == 0) {
|
||||
nextItem = new String[0];
|
||||
nextPrepared = true;
|
||||
// do not set end here; end will be set after returning this element in next()
|
||||
return;
|
||||
}
|
||||
|
||||
// 非递归模拟DFS,直到找到一个可返回的排列或穷尽
|
||||
while (depth >= 0) {
|
||||
int start = indices[depth] + 1;
|
||||
boolean found = false;
|
||||
for (int i = start; i < n; i++) {
|
||||
if (!visited[i]) {
|
||||
// 如果当前层之前有选过一个元素,要先取消之前选中的 visited
|
||||
if (indices[depth] != -1) {
|
||||
visited[indices[depth]] = false;
|
||||
}
|
||||
indices[depth] = i;
|
||||
visited[i] = true;
|
||||
buffer[depth] = datas[i];
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
// 本层没有可用元素,回溯
|
||||
if (indices[depth] != -1) {
|
||||
visited[indices[depth]] = false;
|
||||
indices[depth] = -1;
|
||||
}
|
||||
depth--;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 如果该元素未使用
|
||||
if (!visited[idx]) {
|
||||
visited[idx] = true;
|
||||
buffer[depth] = datas[idx];
|
||||
|
||||
if (depth == m - 1) {
|
||||
// 输出一个排列
|
||||
visited[idx] = false;
|
||||
|
||||
// 下一次从 idx+1 继续
|
||||
stack.push(idx + 1);
|
||||
|
||||
return Arrays.copyOf(buffer, m);
|
||||
} else {
|
||||
// 继续下一层
|
||||
stack.push(idx + 1); // 当前层下一个起点
|
||||
stack.push(0); // 下一层起点
|
||||
continue;
|
||||
// 若已达到输出深度,准备输出(但不抛出)
|
||||
if (depth == m - 1) {
|
||||
nextItem = Arrays.copyOf(buffer, m);
|
||||
// 取消当前visited,为下一次在同一层寻找下一个候选做准备
|
||||
visited[indices[depth]] = false;
|
||||
// 保持 depth 不变(下一次 prepare 会从 indices[depth]+1 开始寻找)
|
||||
nextPrepared = true;
|
||||
return;
|
||||
} else {
|
||||
// 向下一层深入:初始化下一层为-1并继续循环
|
||||
depth++;
|
||||
if (depth < m) {
|
||||
indices[depth] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// 已访问则跳过
|
||||
stack.push(idx + 1);
|
||||
}
|
||||
|
||||
// 若循环结束,说明已经穷尽所有可能
|
||||
end = true;
|
||||
return null;
|
||||
nextItem = null;
|
||||
nextPrepared = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ import cn.hutool.core.lang.Console;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
@ -145,4 +146,51 @@ public class ArrangementTest {
|
||||
assertArrayEquals(new String[]{"1", "2", "3"}, all.get(9));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
// 迭代器测试
|
||||
// ----------------------------------------------------
|
||||
@Test
|
||||
public void iteratorTest() {
|
||||
Arrangement arrangement = new Arrangement(new String[]{"1", "2", "3"});
|
||||
|
||||
// 测试 m=2 的情况
|
||||
List<String[]> iterResult = new ArrayList<>();
|
||||
for (String[] perm : arrangement.iterate(2)) {
|
||||
iterResult.add(perm);
|
||||
}
|
||||
|
||||
assertEquals(6, iterResult.size());
|
||||
assertArrayEquals(new String[]{"1", "2"}, iterResult.get(0));
|
||||
assertArrayEquals(new String[]{"1", "3"}, iterResult.get(1));
|
||||
assertArrayEquals(new String[]{"2", "1"}, iterResult.get(2));
|
||||
assertArrayEquals(new String[]{"2", "3"}, iterResult.get(3));
|
||||
assertArrayEquals(new String[]{"3", "1"}, iterResult.get(4));
|
||||
assertArrayEquals(new String[]{"3", "2"}, iterResult.get(5));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void iteratorFullTest() {
|
||||
Arrangement arrangement = new Arrangement(new String[]{"1", "2", "3"});
|
||||
|
||||
// 测试全排列的情况
|
||||
List<String[]> iterResult = new ArrayList<>();
|
||||
for (String[] perm : arrangement.iterate(3)) {
|
||||
iterResult.add(perm);
|
||||
}
|
||||
|
||||
assertEquals(6, iterResult.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void iteratorBoundaryTest() {
|
||||
Arrangement arrangement = new Arrangement(new String[]{"1", "2", "3"});
|
||||
|
||||
// 测试 m > n 的情况
|
||||
List<String[]> iterResult = new ArrayList<>();
|
||||
for (String[] perm : arrangement.iterate(5)) {
|
||||
iterResult.add(perm);
|
||||
}
|
||||
assertTrue(iterResult.isEmpty());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user