關(guān)于n個(gè)數(shù)里選k個(gè)的不同方法及一些思考

給出兩個(gè)整數(shù)n和k,返回從1到n中取k個(gè)數(shù)字的所有可能的組合
例如:
如果n=4,k=2,結(jié)果為
[? [2,4],? [3,4],? [2,3],? [1,2],? [1,3],? [1,4],?]

這題本身并不是特別的難,但是不同方法的復(fù)雜度差很多,而且響應(yīng)了之前碰到的那句:

求所有符合的結(jié)果用深度遞歸(及時(shí)修剪),求最優(yōu)結(jié)果或結(jié)果數(shù)量用動(dòng)態(tài)規(guī)劃;

最先想到的方式:
方法一:
用走迷宮的思路暴力遞歸,n個(gè)數(shù)每一個(gè)都可以看做迷宮的一步,有選和不選兩種選擇:

class Solution {
    public ArrayList<List<Integer>> result = new ArrayList<List<Integer>>();
    public void go(int n,int k,int i,ArrayList<Integer> list){
        if(list.size()==k){
             result.add(list);
             return;
        }
        if(i>n) return;
        // 不把當(dāng)前位置的數(shù)選入
        go(n,k,i+1,list);
        ArrayList<Integer> newList = new ArrayList<Integer>(list);
        newList.add(i);
        // 把當(dāng)前位置的數(shù)選入
        go(n,k,i+1,newList);
    }
    public List<List<Integer>> combine(int n, int k) {
        go(n,k,1,new ArrayList<Integer>());
        return result;
    }
}

這種方式表面上像一顆二叉樹一樣展開n個(gè)數(shù)有2^n次方種遞歸,但是對(duì)二叉樹進(jìn)行適當(dāng)裁剪,對(duì)不要對(duì)枝葉不再擴(kuò)展,勉強(qiáng)通過了測試55 ms 42.3 MB;

方法二:
層序遞歸的思路,一層層的去求,求f(n,1),f(n,2),......,f(n,k)---->n個(gè)數(shù)選1個(gè)的集合,選2個(gè)的集合....k個(gè)的集合;這種方式會(huì)建立大量的集合;
一開始我這樣做,0-k的每一位上有1-n種選擇,表面上感覺是k*n的復(fù)雜度,但是由于是不重復(fù)的,每次為了避免前面選過的數(shù)再次被選我用的HashSet不斷判斷的方式來避免重復(fù)加入,最后直接超時(shí)了;

class Solution {
    public List<List<Integer>> combine(int n, int k) {
        // 
        HashSet<Set<Integer>> result = new HashSet<Set<Integer>>();
        result.add(new HashSet<Integer>());
        for(int i=1;i<=k;i++){
            HashSet<Set<Integer>> newResult = new HashSet<Set<Integer>>();
            for(int j=1;j<=n;j++){
                for(Set<Integer> set:result){
                    if(!set.contains(j)){
                        HashSet<Integer> newSet = new HashSet<Integer>(set);
                        newSet.add(j);
                        newResult.add(newSet);
                    }
                }
            }
            result=newResult;
        }
        ArrayList<List<Integer>> res = new ArrayList<List<Integer>>();
        for(Set<Integer> set:result){
            ArrayList<Integer> temp = new ArrayList<Integer>(set);
            res.add(temp);
        }
        return res;
    }
}

方法三:
動(dòng)態(tài)規(guī)劃的背包思路: dp[i][j]--->∑dp[i-1][j]+∑f(dp[i-1][j-1],i);利用前面的結(jié)果,二維的一格格的求,(方法二相當(dāng)于一維的f(n,1)->f(n,2)->f(n,3)->.....->f(n,k),n恒定,一層層的求), 這種方式比方法二快,但比走迷宮暴力并剪枝的方式慢4倍, 213 ms -270.3 MB勉強(qiáng)通過;
(之前華為那道題兩個(gè)數(shù)組互相交換,最后最小值,那道題則用動(dòng)態(tài)規(guī)劃轉(zhuǎn)為背包問題最好,映證了那句話:求所有符合的結(jié)果用深度遞歸(及時(shí)修剪),求最優(yōu)結(jié)果或結(jié)果數(shù)量用動(dòng)態(tài)規(guī)劃;)

class Solution {
    public List<List<Integer>> combine(int n, int k) {
        // dp[i][j]--->dp[i-1][j]+f(dp[i-1][j-1],i);
        ArrayList<List<Integer>>[][] dp= new ArrayList[n+1][k+1];
        for(int i=0;i<=n;i++){
            for(int j=0;j<=k;j++){
                if(i==0||j==0){
                    ArrayList<List<Integer>> temp = new ArrayList<List<Integer>>();
                    temp.add(new ArrayList<Integer>());
                    dp[i][j]=temp;
                }else{
                    ArrayList<List<Integer>> newResult = new ArrayList<List<Integer>>();
                    for(List<Integer> list:dp[i-1][j-1]){
                        ArrayList<Integer> newList = new ArrayList<Integer>(list);
                        newList.add(i);
                        newResult.add(newList);
                    }

                    for(List<Integer> list:dp[i-1][j]){
                        if(list.size()==j){
                            newResult.add(new ArrayList<Integer>(list));
                        }
                    }
                    dp[i][j]=newResult;
                }
            }
        }
        return dp[n][k];
    }
}

方法四:
用DFS深度優(yōu)先遍歷,類似于走迷宮但是每一步橫向鋪開,方法一的每一步都只考慮兩種情況,選和不選,這里直接擴(kuò)散到 i到n-(k-size)+1 (其中size是前面遞歸過來的已經(jīng)選了的情況數(shù);
)中的任意位置,類似于棋盤不只上下左右走了,棋子?可以跳到任意位置;
在每一次遞歸前l(fā)ist.add(i),執(zhí)行g(shù)o(next)遞歸之后,又及時(shí)的進(jìn)行刪除動(dòng)作,list.remove(last);只在最后符合要求時(shí)clone這個(gè)list,并放入結(jié)果集合;(不保存中間結(jié)果)
最后只用了2ms 42MB;

class Solution {
    public ArrayList<List<Integer>> result = new ArrayList<List<Integer>>();
    public void go(int n,int k,int i,ArrayList<Integer> list){
        if(list.size()==k){
            // 這里與方法一不同,需要克隆后放入結(jié)果集;因?yàn)檫f歸后還要撤銷給for的下一個(gè)用;
             result.add(new ArrayList<Integer>(list));
             return;
        }
        if(i>n) return;
        int size = list.size();
        for(int index = i;index<=n-(k-size)+1;index++){
            list.add(index);
            go(n,k,index+1,list);
            // 每一層及每一次調(diào)用都及時(shí)刪除,方便下一次for循環(huán)恢復(fù);
            list.remove(size);
        }
    }
    public List<List<Integer>> combine(int n, int k) {
        go(n,k,1,new ArrayList<Integer>());
        return result;
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市异逐,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌插掂,老刑警劉巖灰瞻,帶你破解...
    沈念sama閱讀 221,548評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異辅甥,居然都是意外死亡酝润,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門璃弄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來要销,“玉大人,你說我怎么就攤上這事夏块∈韪溃” “怎么了纤掸?”我有些...
    開封第一講書人閱讀 167,990評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長浑塞。 經(jīng)常有香客問我借跪,道長,這世上最難降的妖魔是什么酌壕? 我笑而不...
    開封第一講書人閱讀 59,618評(píng)論 1 296
  • 正文 為了忘掉前任掏愁,我火速辦了婚禮,結(jié)果婚禮上卵牍,老公的妹妹穿的比我還像新娘果港。我一直安慰自己,他們只是感情好糊昙,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評(píng)論 6 397
  • 文/花漫 我一把揭開白布辛掠。 她就那樣靜靜地躺著,像睡著了一般溅蛉。 火紅的嫁衣襯著肌膚如雪公浪。 梳的紋絲不亂的頭發(fā)上他宛,一...
    開封第一講書人閱讀 52,246評(píng)論 1 308
  • 那天船侧,我揣著相機(jī)與錄音,去河邊找鬼厅各。 笑死镜撩,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的队塘。 我是一名探鬼主播袁梗,決...
    沈念sama閱讀 40,819評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼憔古!你這毒婦竟也來了遮怜?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,725評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤鸿市,失蹤者是張志新(化名)和其女友劉穎锯梁,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體焰情,經(jīng)...
    沈念sama閱讀 46,268評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡陌凳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了内舟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片合敦。...
    茶點(diǎn)故事閱讀 40,488評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖验游,靈堂內(nèi)的尸體忽然破棺而出充岛,到底是詐尸還是另有隱情保檐,我是刑警寧澤,帶...
    沈念sama閱讀 36,181評(píng)論 5 350
  • 正文 年R本政府宣布崔梗,位于F島的核電站展东,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏炒俱。R本人自食惡果不足惜盐肃,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望权悟。 院中可真熱鬧砸王,春花似錦、人聲如沸峦阁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽榔昔。三九已至驹闰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間撒会,已是汗流浹背嘹朗。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留诵肛,地道東北人屹培。 一個(gè)月前我還...
    沈念sama閱讀 48,897評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像怔檩,于是被迫代替她去往敵國和親褪秀。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • 在C語言中,五種基本數(shù)據(jù)類型存儲(chǔ)空間長度的排列順序是: A)char B)char=int<=float C)ch...
    夏天再來閱讀 3,350評(píng)論 0 2
  • 回溯算法 回溯法:也稱為試探法薛训,它并不考慮問題規(guī)模的大小媒吗,而是從問題的最明顯的最小規(guī)模開始逐步求解出可能的答案,并...
    fredal閱讀 13,669評(píng)論 0 89
  • 數(shù)據(jù)結(jié)構(gòu)算法大全(用 PASCAL 描述) 1.數(shù)論算法 求兩數(shù)的最大公約數(shù) function gcd(a,b:i...
    心想事成_ae7e閱讀 517評(píng)論 0 0
  • 樹形動(dòng)態(tài)規(guī)劃乙埃,顧名思義就是樹+DP闸英,先分別回顧一下基本內(nèi)容吧:動(dòng)態(tài)規(guī)劃:問題可以分解成若干相互聯(lián)系的階段,在每一個(gè)...
    Mr_chong閱讀 1,488評(píng)論 0 2
  • 動(dòng)態(tài)規(guī)劃 動(dòng)態(tài)規(guī)劃是一種高性能的牛逼算法膊爪,由美國的R.Bellman提出自阱,它是動(dòng)態(tài)就是體現(xiàn)在此算法是基于一個(gè)遞推公...
    董澤平閱讀 1,171評(píng)論 0 12