import java.util.Arrays;
/**
* 题目要求:
* 打印一个字符串(长度不确定)的全字符组合情况,原始字符串中没有重复字符
例如:原始字符串是"def", 打印得到下列所有组合情况:
"d" "e" "f" "de" "df" "ed" "ef" "fd" "fe" "def" "dfe" "edf" "efd" "fde" "fed"
* @author Hs
*
*/
public class T1 {
public static void main(String[] args) {
// 定义一个字符串
String s = "abcd";
// 把字符串中每一个字符拿出来,放进字符串数组
char[] dataChar = s.toCharArray();// 先变成char数组
String[] datas = new String[dataChar.length];
// char转化为String
for (int i = 0; i < datas.length; i++) {
datas[i] = String.valueOf(dataChar[i]);
}
// new 一个根节点,根节点并没有数据,目的是为了能遍历到树中的所有数据,需要单根结构树.
// 因为cell类的构造器本身是一个递归函数,所以new root完成时,已经把所有数据作为节点,创建了一个树出来.
// 所有的情形已经储存在树中
// 剩下的只是遍历所有的情况
Cell root = new Cell(datas);// datas中没有数据是递归结束的信号
for(int i =1;i<=s.length();i++) {
Cell.view(root, i);
}
}
}
/**
* 1 节点类Cell.
* 2 树结构中的节点 拥有指针指向父节点father,子节点son,和下一个兄弟节点brother 还有一个floor.
* 3 用来储存该节点处在树结构的第几层.
*
* @author Hs
*
*/
class Cell {
public int floor;// 在树中的层数
public String data;// 存放具体数据
public Cell father;// 指向父节点
public Cell brother;// 指向下一个(相邻的)兄弟节点
public Cell son;// 指向子节点
public String[] sonRest;// sonRest数组,用来存放这一分支中,到此节点为止,所剩数据(已经删掉this对象中的data)
public String[] broRest;// broRest数组,类似于上面,用来存放这一行所剩数据
/**
* 这个构造器只调用一次,用来new root节点
* 所以他只有son有指向,father和brother都是null floor为0 data为null
* 因为data为空,所以sonRest和broRest不需要删除数据,等于s
*
* @param s 用户给出的字符串拆分成的数组,最原始的数据
*/
public Cell(String[] s) {
floor = 0;
data = null;
father = null;
brother = null;
//因为data为空,所以sonRest和broRest不需要删除数据,等于s
sonRest = Arrays.copyOf(s, s.length);
broRest = Arrays.copyOf(s, s.length);
//找到sonRest数组中第一个不为空数据,作为son的data
for (int i = 0; i < sonRest.length; i++) {
if (sonRest[i] != null) {
//son的floor要比this+1,father是this本身,data略,sonRest和broRest传递给son,此时的数据数组中并没有删除son已经用掉的数据
son = new Cell(floor + 1, this, sonRest[i], broRest,true);
break;
}
}
}
/**
* 另一个构造器,递归直到数据数组中不再有数据,至结束 所有cell对象一起组成一个逻辑树结构
*
* 7个成员变量中有4个需要传递过来的参数赋值,其中floor,father,broRest只能由兄弟节点或者父节点穿来,否则this无从得知信息
* sonRest 可以通过father调出来
* data 也只能在父节点或者兄弟节点中确定 传给this,因为this本身并不知道谁(父/bro)new的自己,所以不知道从哪个数据组中提取数据,而且(父/bro)必须知道有没有数据用来new新的cell,来判断是否让son或father指向空
*
*
*/
public Cell(int floor, Cell father, String data, String[] broRest,boolean isSon) {
this.floor = floor;
this.father = father;
this.data = data;
// 删除sonRest中 被this用过的data
String[] newSonRest = Arrays.copyOf(father.sonRest, father.sonRest.length);
for (int i = 0; i < newSonRest.length; i++) {
if(newSonRest[i]!=null) {
if (newSonRest[i].equals(data)) {
newSonRest[i] = null;
break;
}
}
}
// 删除broRest中 被this用过的data
String[] newBroRest = Arrays.copyOf(broRest, broRest.length);
for (int i = 0; i < newBroRest.length; i++) {
if(newBroRest[i]!=null) {
if (newBroRest[i].equals(data)) {
newBroRest[i] = null;
break;
}
}
}
//使用删除数据后的数据组作为this的剩余数据组
this.sonRest = newSonRest;
this.broRest = newBroRest;
if(isSon) {
this.broRest = Arrays.copyOf(newSonRest, newSonRest.length);
}
// 判断sonRest中是否还有数据,如果有就让son指向一个新的cell,如果没有就指向空
boolean flag1 = true; // 如果for正常结束 依然为true 证明 sonRest数组中已经没有数据
for (int i = 0; i < newSonRest.length; i++) {
if (newSonRest[i] != null) {
this.son = new Cell(floor + 1, this, newSonRest[i], this.broRest,true);// 因为是son 所以floor+1,father是this
flag1 = false;
break;
}
}
if (flag1) {
this.son = null;// 如果数组中没数据,son指向null
}
// 判断broRest中是否还有数据,如果有就让brother指向一个新的cell,如果没有就指向空
boolean flag2 = true; // 如果for正常结束 依然为true 证明 broRest数组中已经没有数据
for (int i = 0; i < this.broRest.length; i++) {
if (this.broRest[i] != null) {
this.brother = new Cell(floor, this.father, this.broRest[i], this.broRest,false);// 因为是brother 所以floor不变,father是this.father
flag2 = false;
break;
}
}
if (flag2) {
this.brother = null;// 如果数组中没数据,指针指向null
}
}
@Override
public String toString() {
if(data!=null) {
return data;
}else {
return "";
}
}
/**
* 递归遍历函数
* @param root 根节点
* @param size 遍历的层数,这是递归结束的唯一信号
*/
static void view(Cell root,int size) {
if(root!=null) {
if(root.floor==size) {
System.out.println(getInfo(root));
}else {
view(root.son, size);
}
if(root.brother!=null) {
view(root.brother, size);
}
}
}
static String getInfo(Cell c) {
if(c.father!=null) {
return getInfo(c.father)+c;
}else {
return c.toString();
}
}
}