dfs及常見搜索問題

dfs以及搜索問題

DFS是搜索的手段之一忆某。它從某個狀態(tài)開始鹃唯,不斷地轉(zhuǎn)移狀態(tài)直到無法轉(zhuǎn)移,然后回退到前一步的狀態(tài)阵幸,繼續(xù)轉(zhuǎn)移到其他狀態(tài)花履。直到找到最終的解。

全排列

給定一個沒有重復(fù)數(shù)字的序列侨嘀,返回其所有可能的全排列臭挽。

示例:

輸入: [1,2,3]
輸出:
[
  [1,2,3],
  [1,3,2],
  [2,1,3],
  [2,3,1],
  [3,1,2],
  [3,2,1]
]

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/permutations
著作權(quán)歸領(lǐng)扣網(wǎng)絡(luò)所有。商業(yè)轉(zhuǎn)載請聯(lián)系官方授權(quán)咬腕,非商業(yè)轉(zhuǎn)載請注明出處欢峰。

題解:

C++可以用庫

class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        vector<vector<int>> res;        
        sort(nums.begin(),nums.end());//先排序
        do
        {
            res.push_back(nums);
        }while(next_permutation(nums.begin(),nums.end()));
        return res;

        
    }
};

另一種全排列的方法,回溯的思想

思路來源于網(wǎng)友們涨共。
但是后來發(fā)現(xiàn)這樣好像順序不是那種遞增的順序
所以如果希望順序輸出纽帖,可以把string放到vector里vector<string>,在最后加一個sort举反,后排序方式懊直??
但是我在呕鸨牵客網(wǎng)這樣做意外出錯室囊,不曉得怎么回事雕崩。但是我看別人有這么做
思路, 遞歸方法backsee(數(shù)組融撞,index坐標(biāo))
第一個位置 for循環(huán)盼铁,每個數(shù)字循環(huán)一次(這里保證每個數(shù)字坐一次用的是swap方法),第一個數(shù)字確定后尝偎,考慮index+1~n之后的數(shù)字

class Solution {
public:
    void backsee(int n,vector<vector<int>>& res,vector<int>& nums,int index){
        if(index==n)
            res.push_back(nums);
        for(int i=index;i<n;i++){//可以想象第一個數(shù)有n種選擇饶火,第二個數(shù)位置就是n-1種了
            swap(nums[i],nums[index]);
            backsee(n,res,nums,index+1);//問題縮小到第二個數(shù)
            swap(nums[i],nums[index]);
        }
        
    }
    
    vector<vector<int>> permute(vector<int>& nums) {
        //類似搜索樹
        vector<vector<int>> res;  
        backsee(nums.size(),res,nums,0);
        return res;

        
    }
};

第三種方式,dfs思想致扯,我終于知道dfs思想是怎么回事了肤寝。
需要兩個數(shù)組(或者類似)
一個表示這個位置是否讀過
一個表示組成的新的排列
//在dfs時,有for循環(huán)抖僵,相當(dāng)于對每個起始點遍歷
在爬鹂矗客網(wǎng)調(diào)程序好煩 LeetCode真美好

#include <stdio.h>
#include <algorithm>
#include <vector>
#include <iostream>
#include <cstring>

using namespace std;
void dfs(int n,string s,int res[],string tmp){
    if(n==s.length()){
        cout<<tmp<<endl;
    }
        
    for(int i=0;i<s.length();i++){
            if(res[i]==0){
                res[i]=1;
                tmp[n]=s[i];
                dfs(n+1,s,res,tmp);
                res[i]=0; 
            }
    }
}

int main(){
    string s;
    cin>>s;
    string tmp;
    int x=s.length();
    sort(s.begin(),s.end());
    tmp=s.substr(0,s.length());//string賦初值?耍群?不能用fill刨摩??
    int res[s.length()];
   
    fill(res,res+x,0);
    
        dfs(0,s,res,tmp);
    // }
    //sort(s.begin(),s.end());
    //vector<vector<string>> tmp,
    cout<<endl;
    return 0;
}

帶剪枝的全排列

給定一個可包含重復(fù)數(shù)字的序列世吨,返回所有不重復(fù)的全排列。

示例:

輸入: [1,1,2]
輸出:
[
  [1,1,2],
  [1,2,1],
  [2,1,1]
]

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/permutations-ii
著作權(quán)歸領(lǐng)扣網(wǎng)絡(luò)所有寝殴。商業(yè)轉(zhuǎn)載請聯(lián)系官方授權(quán)局齿,非商業(yè)轉(zhuǎn)載請注明出處扯罐。

題解:

1、使用了STL 的count函數(shù)來剪枝沐祷,然后時間復(fù)雜度驚人。

執(zhí)行用時 :2280 ms, 在所有 C++ 提交中擊敗了5.03%的用戶

內(nèi)存消耗 :10 MB, 在所有 C++ 提交中擊敗了65.48%的用戶

class Solution {
public:
   void backsee(int n,vector<vector<int>>& res,vector<int>& nums,int index){
       //參數(shù):總長度n,結(jié)果res,中間結(jié)果nums,index
        if(index==n&&count(res.begin(),res.end(),nums)==0)
            res.push_back(nums);
        for(int i=index;i<n;i++){//可以想象第一個數(shù)有n種選擇攒岛,第二個數(shù)位置就是n-1種了
            swap(nums[i],nums[index]);
            backsee(n,res,nums,index+1);//問題縮小到第二個數(shù)
            swap(nums[i],nums[index]);
        }
        
    }
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        //題目思路赖临,看題解,可用set<vector>剔除重復(fù)的
        
        vector<vector<int>> res;
        backsee(nums.size(),res,nums,0);
        return res;
        
    }
};

2灾锯、使用C++自帶全排列

可以使用next_permutation兢榨,前提是先從小到大排序,該函數(shù)輸出的就是無重復(fù)的全排列顺饮,運行時間24ms吵聪,擊敗98.87%。
vector<vector<int>> permuteUnique(vector<int>& nums)
{
    sort(nums.begin(), nums.end());
    vector<vector<int>>result;
    result.push_back(nums);
    while (next_permutation(nums.begin(), nums.end()))
    {
        result.push_back(nums);
    }
    return result;
}
去重兼雄,考慮重復(fù)的定義吟逝,其實就是同一位選進去了多個相同的數(shù),換句話說就是若要不重復(fù)赦肋,同一位對同樣的數(shù)只能使用一個块攒,因此我們可以在每次DFS之前励稳,也就是為本位置選數(shù)之前,判斷是否已經(jīng)使用過相同的數(shù)了囱井,若沒有則正常DFS驹尼,若有則跳過本次循環(huán),這樣不僅去掉了重復(fù)琅绅,而且減少了遞歸次數(shù)扶欣。
作者:luo-ben-zhu-xiao-man-tou
鏈接:https://leetcode-cn.com/problems/permutations-ii/solution/dfsqu-zhong-huo-zhe-shi-yong-czi-dai-quan-pai-lie-/
來源:力扣(LeetCode)
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán)千扶,非商業(yè)轉(zhuǎn)載請注明出處料祠。

3、參考了評論區(qū)大佬

用map進行改進

執(zhí)行用時 :8 ms, 在所有 C++ 提交中擊敗了95.62%的用戶

內(nèi)存消耗 :11.1 MB, 在所有 C++ 提交中擊敗了25.72%的用戶

class Solution {
public:
   void backsee(int n,vector<vector<int>>& res,vector<int>& nums,int index,int used[]){
       //參數(shù):總長度n,結(jié)果res,中間結(jié)果nums,index
        map<int,int> ex;
        if(index==n)
            res.push_back(nums);
        for(int i=index;i<n;i++){//可以想象第一個數(shù)有n種選擇澎羞,第二個數(shù)位置就是n-1種了
            // cout<<i<<endl;
            if(ex.count(nums[i])==1){//這個位置某數(shù)已經(jīng)待過了,所以不考慮了
                continue;
            }
            
                
            swap(nums[i],nums[index]);
            backsee(n,res,nums,index+1,used);//問題縮小到第二個數(shù)
            swap(nums[i],nums[index]);
            ex[nums[i]]=1;
        }
        
    }
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        vector<vector<int>> res;
        int used[nums.size()];
        memset(used,0,sizeof(used));
        sort(nums.begin(),nums.end());
        backsee(nums.size(),res,nums,0,used);
        return res;
        
    }
};

子集

題目

給定一組不含重復(fù)元素的整數(shù)數(shù)組 nums髓绽,返回該數(shù)組所有可能的子集(冪集)。

說明:解集不能包含重復(fù)的子集妆绞。

示例:

輸入: nums = [1,2,3]
輸出:
[
  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/subsets
著作權(quán)歸領(lǐng)扣網(wǎng)絡(luò)所有顺呕。商業(yè)轉(zhuǎn)載請聯(lián)系官方授權(quán),非商業(yè)轉(zhuǎn)載請注明出處括饶。

題解

這個問題類似于下面的目標(biāo)和株茶、部分和問題。

執(zhí)行用時 :0 ms, 在所有 C++ 提交中擊敗了100.00%的用戶

內(nèi)存消耗 :13.1 MB, 在所有 C++ 提交中擊敗了7.49%的用戶

class Solution {
public:
    void sousuo(vector<vector<int>>& res,vector<int>& nums,int index){
        if(index==nums.size()){
            res.push_back(nums);
            return;
        }
            
        int tmp=nums[index];
        //該元素不在
        nums.erase(nums.begin()+index);        
        sousuo(res,nums,index);
        //該元素在
        nums.insert(nums.begin()+index,tmp);
        sousuo(res,nums,index+1);
        
        
        
    }
    
    vector<vector<int>> subsets(vector<int>& nums) {
        //想了一下方法 dfs  動態(tài)規(guī)劃
        vector<vector<int>> res;
        // res.push_back(nums);
        sousuo(res,nums,0);
        return res;
        
    }
};

部分和&&目標(biāo)和問題

部分和問題

這一段來自《挑戰(zhàn)程序設(shè)計語言(第二版)》

給定整數(shù)a1图焰、a2启盛、.......an,判斷是否可以從中選出若干數(shù)技羔,使它們的和恰好為K僵闯。


1580957138(1).png

樣例輸入:

n=4
a={1,2,4,7}
k=13

樣例輸出

Yes {13=2+4+7}

樣例輸入:

n=4
a={1,2,4,7}
k=15

樣例輸出

No

題解思路:從a_1開始按順序決定每個數(shù)加或者不加,在全部n個數(shù)后判斷它們的和是不是k即可藤滥。

狀態(tài)數(shù)是2^{n+1}所以復(fù)雜度是O(2^n)

int a[MAX_N];
int n,k;

//已經(jīng)從前i項得到了和sum鳖粟,然后對于i項之后進行分支。
bool dfs(int i,int sum){
    //如果前n項都計算過了拙绊,則返回sum是否與k相等
    if(i==n)  return sum==k;
    //不加上a[i]的情況
    if(dfs(i+1,sum)) return ture;
    
    //加上a[i]的情況
    if(dfs(i+1,sum+a[i])) return true;
    return false;
}

void solve(){
    if (dfs(0,0)) printf("Yes\n");
    else printf("No\n");
}

目標(biāo)和問題[動態(tài)規(guī)劃等解法待補充]

類似問題

給定一個非負整數(shù)數(shù)組向图,a1, a2, ..., an, 和一個目標(biāo)數(shù),S∈毖剑現(xiàn)在你有兩個符號 + 和 -张漂。對于數(shù)組中的任意一個整數(shù),你都可以從 + 或 -中選擇一個符號添加在前面谨娜。

返回可以使最終數(shù)組和為目標(biāo)數(shù) S 的所有添加符號的方法數(shù)航攒。

示例 1:

輸入: nums: [1, 1, 1, 1, 1], S: 3
輸出: 5
解釋: 

-1+1+1+1+1 = 3
+1-1+1+1+1 = 3
+1+1-1+1+1 = 3
+1+1+1-1+1 = 3
+1+1+1+1-1 = 3

一共有5種方法讓最終目標(biāo)和為3。

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/target-sum
著作權(quán)歸領(lǐng)扣網(wǎng)絡(luò)所有趴梢。商業(yè)轉(zhuǎn)載請聯(lián)系官方授權(quán)漠畜,非商業(yè)轉(zhuǎn)載請注明出處币他。

這是一種類似暴力的解法

執(zhí)行用時 :1776 ms, 在所有 C++ 提交中擊敗了8.70%的用戶

內(nèi)存消耗 :8.8 MB, 在所有 C++ 提交中擊敗了29.83%的用戶

class Solution {
public:
    int jieguo(vector<int>& nums, int S,int now,int index,int count){
        if(index==nums.size()){
            if(now==S)
                return count+=1;
            else
                return count;
        }
        int res=0;
        res=jieguo(nums,S,now+nums[index],index+1,count);
        res=jieguo(nums,S,now-nums[index],index+1,res);
        return res;
        
    }
    
    int findTargetSumWays(vector<int>& nums, int S) {
        //用dfs方法
        int res=jieguo(nums,S,0,0,0);
        return res;
    }
};
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市憔狞,隨后出現(xiàn)的幾起案子蝴悉,更是在濱河造成了極大的恐慌,老刑警劉巖瘾敢,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拍冠,死亡現(xiàn)場離奇詭異,居然都是意外死亡簇抵,警方通過查閱死者的電腦和手機庆杜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來碟摆,“玉大人晃财,你說我怎么就攤上這事〉渫桑” “怎么了断盛?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長愉舔。 經(jīng)常有香客問我钢猛,道長,這世上最難降的妖魔是什么轩缤? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任厢洞,我火速辦了婚禮,結(jié)果婚禮上典奉,老公的妹妹穿的比我還像新娘。我一直安慰自己丧叽,他們只是感情好卫玖,可當(dāng)我...
    茶點故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著踊淳,像睡著了一般假瞬。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上迂尝,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天脱茉,我揣著相機與錄音,去河邊找鬼垄开。 笑死琴许,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的溉躲。 我是一名探鬼主播榜田,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼益兄,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了箭券?” 一聲冷哼從身側(cè)響起净捅,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎辩块,沒想到半個月后蛔六,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡废亭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年国章,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片滔以。...
    茶點故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡捉腥,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出你画,到底是詐尸還是另有隱情抵碟,我是刑警寧澤,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布坏匪,位于F島的核電站拟逮,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏适滓。R本人自食惡果不足惜敦迄,卻給世界環(huán)境...
    茶點故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望凭迹。 院中可真熱鬧罚屋,春花似錦、人聲如沸嗅绸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鱼鸠。三九已至猛拴,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蚀狰,已是汗流浹背愉昆。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留麻蹋,地道東北人跛溉。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親倒谷。 傳聞我的和親對象是個殘疾皇子蛛蒙,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,955評論 2 355