1. 程式人生 > >傻瓜方法求集合的所有子集問題(java版)

傻瓜方法求集合的所有子集問題(java版)

    給定任意長度的一個集合,用一個數組表示,如{"a", "b","c"},求它的所有子集。結果是{ {a}, {b}, {c}, {a,b}, {a,c}, {b,c}, {a,b,c}}和一個空集。

    下面講的就是如何用一個原始的傻瓜方法(非演算法)求它的所有子集。

    首先我們知道是它的子集個數是2^length,如果長度是3,那子集就共有2的3次方=8個,包括空集。

    求子集,我的做法是對任何一項做判斷,有或者無,用1和0來對應表示。

    那麼像這種長度為3的,用二進位制來表示就是000、001、010……

    其實就是從0-2^3,用2進製表示出來就是所以的子集了。然後把0對應的子項給拿掉,譬如010對應的就是b,011對應的就是bc。只需要從0到2^3-1做一個迴圈,然後把0-7之間的數用二進位制表示出來,再與原集合進行對比。把0對應位置的字元去掉,這樣就得到了所有子集。

   原理很簡單,下面是程式碼

package huisu;

/**
 * Created by wolf on 2016/3/22.
 */
public class GetSet {
    private String[] origin = {"a", "b", "c"};

    private String[] targetArray;

    public static void main(String[] args) {
          new GetSet().doJob();
    }
    
    private void doJob() {
        //獲取將要分解的字串如果轉為2進位制最大是幾
        //如字串是3位,就是2^3。從[0 0 0]到[1 1 1]
        int maxLength = (int) Math.pow(2, origin.length);
        targetArray = new String[maxLength];

        for (int i = 0; i < targetArray.length; i++) {
            //十進位制轉2進位制
            targetArray[i] = Integer.toBinaryString(i);
        }

        buling();

        print();
    }

    /**
     * 給空位補0,湊齊位數
     */
    private void buling() {
        for (int i = 0; i < targetArray.length; i++) {
            //位數是完整的,不需要補0
            if (targetArray[i].length() == origin.length) {
                continue;
            }
            String temp = "";
            //0,1,10,11,111
            for (int j = 0; j < origin.length - targetArray[i].length(); j++) {
                temp += "0";
            }
            targetArray[i] = temp + targetArray[i];
        }
    }


    private void print(){
        for (int i = 0; i < targetArray.length; i++) {
            String s = targetArray[i];//如000,001,010
            for (int j = 0; j < s.length(); j++) {
                char item = s.charAt(j);
                if (item == '1') {
                    System.out.print(origin[j]);
                }
            }
            System.out.println();
        }
    }
}
    在第23行是將10進位制的0-7轉成二進位制,轉之後如下圖

    這裡就有個問題,那就是位數並不滿,像0、10之類的,將來和原始陣列做對應判斷的時候有點小麻煩,所以我做了個處理,把位數補齊。保持和原始陣列位數一樣。

    呼叫了buling(原諒我想不起來用什麼英語來表示補零)方法,把位數不足的前面全補上0.然後就變成了000,001,010……這樣就可以很方便的去判斷了,只打印1所在的位數就行了。參考print方法。

    總結:這種做法比較簡單易懂。也能適應任意長度的求子集問題。根據這種做法,還能解決另外一個問題——01揹包問題(有編號分別為a,b,c,d,e的五件物品,它們的重量分別是2,2,6,5,4,它們的價值分別是6,3,5,4,6,現在給你個承重為10的揹包,如何讓揹包裡裝入的物品具有最大的價值總和?)相信很容易能看出來,上面的方法求出來了所有子集,那麼對於01揹包問題,就是根據所有的子集,先砍掉所有超重的子集。然後去計算剩餘的子集的價值,找到最大的就OK了。