知识的学习在于点滴记录,坚持不懈;知识的学习要有深度和广度,不能只流于表面,坐井观天;知识要善于总结,不仅能够理解,更知道如何表达!
文章目录
子集树
解空间就是问题所有解的可能取值构成的空间,一个问题的解往往包含了得到这个解的每一步,就是对应解空间树中一条从根节点到叶子节点的路径。
子集树就代表了一类问题的解空间,它并不是真实存在的数据结构,也就是说并不是真的有一颗这样的树,只是抽象出来的解的空间树。
当问题求解的结果是集合S的某一子集的时候,其对应的解空间就是一颗子集树,时间复杂度是 O ( 2 n ) O(2^n) O(2n),看下面的这段代码:
@Test
public void test01(){
int[] arr = {
1,2,3};
backstrace01(arr, 0, arr.length);
}
private void backstrace01(int[] arr, int i, int length) {
if(i == length){
System.out.println("hello world!");
} else {
backstrace01(arr, i+1, length);
backstrace01(arr, i+1, length);
}
}
上面的代码运行以后,会输出多少次hello world!呢?上面的backstrace01函数的递归调用就是一个二叉树的遍历过程,如果给节点的左树枝标识1,右树枝标识0,那么这颗树就称作子集树,看下面的代码和图示:
@Test
public void test01(){
int[] arr = {
1,2,3};
int[] brr = new int[arr.length];
backstrace02(arr, brr, 0, arr.length);
}
private void backstrace02(int[] arr, int[] brr, int i, int length) {
if(i == length){
for (int j=0; j<length; ++j){
if(brr[j] == 1){
System.out.print(arr[j] + " ");
}
}
System.out.println();
} else {
brr[i] = 1;
backstrace02(arr, brr, i+1, length);
brr[i] = 0;
backstrace02(arr, brr, i+1, length);
}
}
上面代码中用brr做了辅助数组,来记录遍历过程中,是遍历子集树节点的左孩子还是右孩子,如图:
运行上面的代码,可以看出来,打印出来了序列{1,2,3}的所有的子集情况,也就是从根节点到某一个叶子节点的路径就代表了问题的一个解,可以根据上面的图梳理出所有子集结果。
一组整数序列,选择其中的一部分整数,让选择的整数和序列中剩下的整数的和的差值最小
这个问题是笔试面试中经常出现的,是典型的求子集的问题,可以用子集树完美解决,代码如下:
// 原始的整形数组序列
static int[] arr = {
12,32,8,15,26,7,6,258};
// 定义一个获取子集的辅助数组
static int[] brr = new int[arr.length];
// 存储到目前位置,最合适的子集数组
static int[] bestx = new int[arr.length];
// 记录剩下的整数的和
static int r = 0;
// 记录已选择的整数的和
static int cv = 0;
static int min = Integer.MAX_VALUE;
public static void main(String[] args) {
for (int i = 0; i < arr.length; i++) {
r += arr[i];
}
backstrace(arr, brr, 0, arr.length);
System.out.println(