mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-12-07 01:28:34 +08:00
commit
1d9b0ebbd2
@ -213,68 +213,147 @@ public class Arrangement implements Serializable {
|
|||||||
private static class ArrangementIterator implements Iterator<String[]> {
|
private static class ArrangementIterator implements Iterator<String[]> {
|
||||||
|
|
||||||
private final String[] datas;
|
private final String[] datas;
|
||||||
|
private final int n;
|
||||||
private final int m;
|
private final int m;
|
||||||
private final boolean[] visited;
|
private final boolean[] visited;
|
||||||
private final String[] buffer;
|
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) {
|
ArrangementIterator(String[] datas, int m) {
|
||||||
this.datas = datas;
|
this.datas = datas;
|
||||||
|
this.n = datas.length;
|
||||||
this.m = m;
|
this.m = m;
|
||||||
this.visited = new boolean[datas.length];
|
this.visited = new boolean[n];
|
||||||
this.buffer = new String[m];
|
this.nextItem = null;
|
||||||
// 初始化 dfs 栈
|
this.nextPrepared = false;
|
||||||
stack.push(0);
|
|
||||||
|
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
|
@Override
|
||||||
public boolean hasNext() {
|
public boolean hasNext() {
|
||||||
return !end;
|
if (end) return false;
|
||||||
|
if (nextPrepared) return nextItem != null;
|
||||||
|
prepareNext();
|
||||||
|
return nextItem != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] next() {
|
public String[] next() {
|
||||||
while (!stack.isEmpty()) {
|
if (end && !nextPrepared) {
|
||||||
int depth = stack.size() - 1;
|
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) {
|
* 将状态推进到下一个可返回的排列并把它放入 nextItem。
|
||||||
// 这一层遍历结束
|
* 如果无更多排列,则将 end=true 并把 nextItem 置为 null。
|
||||||
if (!stack.isEmpty()) {
|
*/
|
||||||
int prev = stack.pop();
|
private void prepareNext() {
|
||||||
stack.push(prev + 1);
|
// 已经准备过或已结束
|
||||||
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果该元素未使用
|
// 若已达到输出深度,准备输出(但不抛出)
|
||||||
if (!visited[idx]) {
|
if (depth == m - 1) {
|
||||||
visited[idx] = true;
|
nextItem = Arrays.copyOf(buffer, m);
|
||||||
buffer[depth] = datas[idx];
|
// 取消当前visited,为下一次在同一层寻找下一个候选做准备
|
||||||
|
visited[indices[depth]] = false;
|
||||||
if (depth == m - 1) {
|
// 保持 depth 不变(下一次 prepare 会从 indices[depth]+1 开始寻找)
|
||||||
// 输出一个排列
|
nextPrepared = true;
|
||||||
visited[idx] = false;
|
return;
|
||||||
|
} else {
|
||||||
// 下一次从 idx+1 继续
|
// 向下一层深入:初始化下一层为-1并继续循环
|
||||||
stack.push(idx + 1);
|
depth++;
|
||||||
|
if (depth < m) {
|
||||||
return Arrays.copyOf(buffer, m);
|
indices[depth] = -1;
|
||||||
} else {
|
|
||||||
// 继续下一层
|
|
||||||
stack.push(idx + 1); // 当前层下一个起点
|
|
||||||
stack.push(0); // 下一层起点
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 已访问则跳过
|
|
||||||
stack.push(idx + 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 若循环结束,说明已经穷尽所有可能
|
||||||
end = true;
|
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.Disabled;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
@ -145,4 +146,51 @@ public class ArrangementTest {
|
|||||||
assertArrayEquals(new String[]{"1", "2", "3"}, all.get(9));
|
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