最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

最短時(shí)間學(xué)會(huì)基于C++實(shí)現(xiàn)DFS深度優(yōu)先搜索

 更新時(shí)間:2021年08月23日 10:35:06   作者:^jhao^  
常見(jiàn)使用深度優(yōu)先搜索(DFS)以及廣度優(yōu)先搜索(BFS)這兩種搜索,今天我們就來(lái)講講什么是深度優(yōu)先搜索,感興趣的可以了解一下

前言

同學(xué)們肯定或多或少的聽(tīng)到過(guò)別人提起過(guò)DFS,BFS,卻一直都不太了解是什么,其實(shí)兩個(gè)各為搜索算法,常見(jiàn)使用 深度優(yōu)先搜索(DFS) 以及 廣度優(yōu)先搜索(BFS) ,今天我們就來(lái)講講什么是深度優(yōu)先搜索,深度優(yōu)先就是撞到了南墻才知道回頭,才會(huì)往上一層返回。

1.迷宮找出口,區(qū)分dfs,bfs:

dfs: 深度優(yōu)先我們可以看出他一次只往一個(gè)方向走,直到撞到了才會(huì)返回上一層去嘗試其他結(jié)果,圖片為上左下右的去走

請(qǐng)?zhí)砑訄D片描述

BFS:廣度優(yōu)先就是嘗試該位置的旁邊的所有可能,再把所有可能的能走到的所有可能繼續(xù)走.撞到了就停止,如果全部可能都'碰壁'表示從該位置無(wú)法走到終點(diǎn)

在這里插入圖片描述

對(duì)比兩個(gè)遍歷方式,相信大家有了初步的認(rèn)識(shí),讓我們接下來(lái)看看這道題

一、DFS經(jīng)典放牌可能組合

題目:我們手上有編號(hào)為1~3的三個(gè)撲克牌,我們要放到編號(hào)為1 ~ 3的盒子當(dāng)中,并且每個(gè)盒子中只放一張牌,問(wèn)我們有多少種方法。

聰明的同學(xué)可能想一想就知道是6種了,但是當(dāng)我們的要用計(jì)算機(jī)表達(dá)時(shí)我們要如何處理呢?題目雖簡(jiǎn)單,所以我們借這道題來(lái)看看DFS如何解題.

方法一: 暴力循環(huán),三層循環(huán)嵌套,判斷每張牌只用一次,這種方法簡(jiǎn)單粗暴.

方法二: 深度搜索,我們選擇把按照從小到大的依次嘗試,在第一個(gè)盒子先開(kāi)始放1,這時(shí)我們剩下兩張牌,對(duì)第二個(gè)盒子來(lái)說(shuō),我們從小到大放,放2,第三個(gè)盒子只有3了,第一種放法就出來(lái)了 1 2 3。
此時(shí)我們?nèi)颗贫挤磐炅耍缓笪覀冞@時(shí)候就是撞到了南墻了,我們就開(kāi)始回收3,回收3的時(shí)候如果我們重復(fù)放3到第三個(gè)盒子就和之前重復(fù)了,所以我們?cè)倩厥?,這時(shí)我們就可以把3放到第二個(gè)盒子,把2放在第三個(gè)盒子,第二種放法就出來(lái)了,1 3 2。

這時(shí)我們放完了之后再回收,我們回收了2,3后,1的所有可能我們就已經(jīng)遍歷完了,這個(gè)時(shí)候我們就回收1,這時(shí)我們就可以開(kāi)始把2放到第一個(gè)盒子,這時(shí)我們還有1 ,3 ,我們按之前的從小到大的原則,把1放入第二個(gè)盒子,剩一個(gè)3放在第三個(gè)盒子,這時(shí)的第三種放法 2 1 3出來(lái)了

同理我們收兩張牌就有組合2 3 1 ,同理3 1 2 ,2 31 ,這樣我們的6種出來(lái)了,即:我們站在一個(gè)盒子,我們按照我們手中的牌,從小到大依次嘗試,即我們有一個(gè)循環(huán),從第一張牌開(kāi)始

//處理第index個(gè)盒子 _ - - 并行遞歸,n有多大我們就調(diào)用多少次
DFS(vector<int>& box,int index,vector<int>& visited,int n)
{//box為盒子,index為當(dāng)前盒子,visited為標(biāo)記哪張牌用了的數(shù)組,n為處理幾張牌
	if(index == n+1)//每一個(gè)盒子都有牌了
	{
		return;
	}
	for(int i =1;i<n;++i)
	{
		if(visited[i]==0)
		{
			//表示沒(méi)有用過(guò)的牌就可以放在當(dāng)前的盒子當(dāng)中
			box[index] = i;//當(dāng)前的盒子放這張牌
			visited[i] =1 ;
			//這里的邏輯就是我們站在一個(gè)盒子,我們按照我們手中的牌,從小到大依次嘗試
			//到這里我們就處理下一個(gè)盒子
			DFS(box,index+1,visited,n);
			//一種方法完成,我們就回收牌
			visited[i]=0;//標(biāo)記成0就可以再次使用啦!
		}
	}
}

下面這個(gè)是測(cè)試n張牌,n個(gè)盒子的時(shí)候,我們所有的可能,可以自行粘貼復(fù)制到編輯器當(dāng)中測(cè)試,請(qǐng)自行輸入n

void DFS(int index, int n, vector<int>& box, vector<int>& visited)
{
	//處理當(dāng)前的盒子

	//我們這里打印看看每個(gè)盒子裝的都是什么
	if (index == n + 1)
	{
		for (int i=1;i<box.size();++i)
			cout << box[i] << " ";
		cout << endl;
		
		//表示每個(gè)盒子我們都已經(jīng)放滿了東西,我們就可以返回上一層
		return;
	}

	//每個(gè)盒子都要用n張牌來(lái)看看是否能夠使用,按照我們從小到大使用的規(guī)定原則
	for (int i = 1; i <= n; ++i)
	{
		//表示牌沒(méi)有被用過(guò),可以使用
		if (visited[i] == 0)
		{
			box[index] = i;//表示在看到index個(gè)盒子的時(shí)候,我們用第i張牌放入
			
			visited[i] = 1;//標(biāo)記當(dāng)前牌已經(jīng)被使用
			//走到這里我們當(dāng)前這個(gè)盒子該干的事情已經(jīng)干完了,我們就可以處理下一個(gè)盒子

			DFS(index + 1, n, box, visited);
			//表示所有的牌都已經(jīng)放入并且返回,這時(shí)我們回收牌
			visited[i] = 0;
		}
	}
}
int main()
{
	int n;
	vector<int> box;
	vector<int> vistied;
	cin >> n;
	box.resize(n + 1, 0);
	vistied.resize(n + 1, 0);
	//這里的1是我們當(dāng)前走到盒子,n是我們有多少牌,boxs是我們用來(lái)存放的盒子,	
	//visited是用來(lái)確定是否訪問(wèn)過(guò)的標(biāo)記矩陣
	DFS(1, n, box, vistied);
	return 0;
}

在這里插入圖片描述

剛開(kāi)始接觸DFS有時(shí)候會(huì)思考一些奇奇怪怪的問(wèn)題,這個(gè)時(shí)候大家應(yīng)該都去調(diào)試調(diào)試,一開(kāi)始的問(wèn)題一定是要解決的!!!

總結(jié)歸納:
深度優(yōu)先搜索的關(guān)鍵是解決當(dāng)下應(yīng)該怎么做,下一步的做法和當(dāng)下的做法是一樣的。當(dāng)下的做法如何做到嘗試每一種可能,我們用for循環(huán)遍歷,對(duì)于每一種可能的確定后,我們繼續(xù)下一步,當(dāng)前的可能等到從下一步會(huì)退之后再處理
DFS常見(jiàn)的模型:
DFS(當(dāng)前這一步的邏輯)
{
1.判斷邊界,是否已經(jīng)撞到墻了:向上回退
2.嘗試當(dāng)下的每一種可能
3.確定一種可能之后,繼續(xù)下一步,DFS(下一步)
}

二、leetcode 員工的重要性

員工的重要性

在這里插入圖片描述

/*
// Definition for Employee.
class Employee {
public:
    int id;
    int importance;
    vector<int> subordinates;
};
*/

class Solution {
public:
    int getImportance(vector<Employee*> employees, int id) {
        
    }
};

題目的分析,題目給我們的信息就是給到一個(gè)id,要求他的直系下屬和直系下屬的直系下屬的importance(重要度),并且vector<Employee*> employees,給我們一個(gè)保存員工的數(shù)據(jù)結(jié)構(gòu),我們可以先將id和他的重要度綁定起來(lái)(哈希表um),這樣我們用DFS遍歷的時(shí)候,就可以先處理當(dāng)前id,每一個(gè)id都要處理他的vector subordinates(這個(gè)是他的直系員工的id),再去遍歷當(dāng)前id的直系下屬.

for(int id: um[id]->subordinates)
        {
            //對(duì)于當(dāng)前我們需要sum加上加上當(dāng)前id的重要度
            sum += um[id]->importance;
            //遍歷當(dāng)前id的所有直系下屬
            DFS(id,um,sum);
        }

在這里插入圖片描述

// 題目答案:
class Solution {
public: 
    void DFS(int id,unordered_map<int,Employee*>& um,int& sum)
    {
        //對(duì)于每一個(gè)id,下屬的id都會(huì)有屬于他們的vector<int> subordinates;
        //所以我們都要去遍歷當(dāng)前id的subordinates
        for(int id: um[id]->subordinates)
        {
            //對(duì)于當(dāng)前我們需要sum加上加上當(dāng)前id的重要度
            sum += um[id]->importance;
            //遍歷當(dāng)前id的直系下屬(**一路走到黑**),統(tǒng)計(jì)完的結(jié)果放到sum當(dāng)中
            DFS(id,um,sum);
        }
    }
    
    int getImportance(vector<Employee*> employees, int id) {
        //單個(gè)員工 id ,返回這個(gè)員工和他所有下屬的重要度之和。
        //DFS:深度遍歷這個(gè)員工的旗下所有重要度之和,這題適合一條路走到黑
        //因?yàn)樵趘ector中的查找都是O(N)的時(shí)間復(fù)雜度,我們選擇用哈希表,并且將id與員工的指針建立映射,因?yàn)槲覀冊(cè)贒FS中要使用到用id找到員工
        unordered_map<int,Employee*> um;
        for(Employee* e : employees)
        {
            //將employees的數(shù)據(jù)導(dǎo)入us(哈希表中)
            um[e->id] = e;
        }
        int sum = um[id]->importance;//提前加上當(dāng)前id的重要度
        //dfs是遍歷當(dāng)前id的subordinates的重要度
        DFS(id, um,sum);
        return sum;
    }
};

這道題之后實(shí)際上后面的題目也都是類(lèi)似的,大家從這道題開(kāi)始可以嘗試自己寫(xiě)一寫(xiě),再看看我的代碼解析,這樣幫助理解!

三、leetcode 圖像渲染

733. 圖像渲染

這題實(shí)際上用bfs,dfs都可以,這里我們講的是dfs,所以我們用dfs的方式解決,一開(kāi)始我的想法是,遍歷每一個(gè)位置的上下左右,將合法的位置并且是舊顏色的改成新顏色.

注:方向矩陣的概念:

在這里插入圖片描述

int arr[4][2] ={{1,0},{0,1},{-1,0},{0,-1}};//方向矩陣
    void DFS(vector<vector<int>>& image, int curX, int curY, int newColor,int row ,int col,int oldcolor)
    {
        image[curX][curY] = newColor;//上色
        //遍歷 (sr,sc)的上下左右 -- 我們可以設(shè)置一個(gè)全局的方向矩陣
        for(int i=0;i<4;++i)
        {
            //(newX,newY)就是該點(diǎn)的上下左右了
            int newX = curX+arr[i][0];
            int newY = curY+arr[i][1];
            if(newX<0||newX>=row
            ||newY<0||newY>=col)
            continue;//表示新的坐標(biāo)越界了不符合要求,跳過(guò)
            
            //走到這里表示新的坐標(biāo)沒(méi)有問(wèn)題,如果是就顏色我們就上色
            if(image[newX][newY] == oldcolor)
            {     
                //當(dāng)前的點(diǎn)處理完,我們處理下一個(gè)點(diǎn)
                DFS(image,newX,newY,newColor,row,col,oldcolor);
            }
            else
            continue;        
        }
    }

但是這樣子會(huì)有一個(gè)問(wèn)題,當(dāng)測(cè)試用例為新顏色和舊顏色相同的時(shí)候就會(huì)變成了死遞歸,所以我們少不了用標(biāo)記矩陣,將我們走過(guò)的位置標(biāo)記了他就不會(huì)重復(fù)走了。

在這里插入圖片描述

在這里插入圖片描述

以下為本道題的全部代碼

int arr[4][2] ={ { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 } };
class Solution {
public:
    void DFS(vector<vector<int>>& image, int curX, int curY, int newColor,vector<vector<int>>& visited,int row ,int col,int oldColor)
    {
        //給當(dāng)前位置染色
         image[curX][curY] = newColor;
         //標(biāo)記當(dāng)前位置已經(jīng)使用了
         visited[curX][curY]=1;

        
        //遍歷 (sr,sc)的上右下左 -- 我們可以設(shè)置一個(gè)全局的方向矩陣
        for(int i=0;i<4;++i)
        {
            //(newX,newY)就是該點(diǎn)的上下左右了
            int newX = curX+arr[i][0];
            int newY = curY+arr[i][1];
            //判斷新坐標(biāo)是否合法
            if(newX<0 || newX>=row
            || newY<0 || newY>=col)
            continue;

            
            //如果是沒(méi)有訪問(wèn)過(guò)的并且是舊顏色就遞歸這個(gè)點(diǎn)的周?chē)?
            if(visited[newX][newY] == 0 && 
               image[newX][newY] == oldColor)
                 DFS(image,newX,newY,newColor,visited,row,col,oldColor) ; 
               
        }
    }
    vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int newColor) {
    //我們這道題也可以用BFS,DFS做都沒(méi)問(wèn)題,我們這里講到DFS,所以就用DFS的思路解決
        int row = image.size();
        int col = image[0].size();
        int oldColor =image[sr][sc];
        //初始化標(biāo)記矩陣,沒(méi)訪問(wèn)過(guò)的置成0
        vector<vector<int>> visited(row,vector<int>(col,0));
        DFS(image,sr,sc,newColor,visited,row,col,oldColor);
        return image;
    }
};

四、leetcode 被圍繞的區(qū)域

130. 被圍繞的區(qū)域

在這里插入圖片描述

本題就是想將全部沒(méi)有與外圍一圈O聯(lián)通的O換成X,與外界連通的O不做改變
思路:拿到這樣一道題,我們要將與外界O相連的O全部遍歷到,所以會(huì)用到深度優(yōu)先搜索,那么我們就可以從矩陣的外圍一圈(第一行,最后一行,第一列,最后一列)出發(fā),將外圍的每一個(gè)O都深度搜索,將搜索到的O(即與外界相連的先換成#)
最后再將O換成#,將#換回O。當(dāng)然也可以用標(biāo)記矩陣來(lái)寫(xiě),都是可以的,這里兩種方法都寫(xiě)出來(lái)看看

1.不使用標(biāo)記矩陣

int arr[4][2] ={ { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 } };
class Solution {
public:
    void DFS(vector<vector<char>>& board,int row,int col,int curX,int curY)
    {
        //當(dāng)前點(diǎn)就是聯(lián)通的‘O'
        board[curX][curY] ='#';
        for(int i =0;i<4;++i)
        {
            int newX = curX+arr[i][0];
            int newY = curY+arr[i][1];
            if(newX<0||newX>=row
            ||newY<0||newY>=col)
            continue;
            if(board[newX][newY]=='O')
            DFS(board,row,col,newX,newY);
        }
    }
    void solve(vector<vector<char>>& board) {
        //我們可以從每一個(gè)邊界的O出發(fā),鏈接的O就是不用改的,其他的都是要改成‘X'的
        //但是我們可以通過(guò)修改與外面相連的O暫時(shí)修改#,就不需要標(biāo)記矩陣了
        int row =board.size();
        int col=board[0].size();
        for(int i=0;i<row;++i)
        {
            if(board[i][0] =='O')
            DFS(board,row,col,i,0);
            if(board[i][col-1] =='O')
            DFS(board,row,col,i,col-1);
        }
        for(int j =0;j<col;++j)
        {
            if(board[0][j]=='O')
            DFS(board,row,col,0,j);
            if(board[row-1][j]=='O')
            DFS(board,row,col,row-1,j);
        }
        for(int i =0;i<row;++i)
        {
            for(int j =0;j<col;++j)
            {
                if(board[i][j]=='O')
                board[i][j]='X';
                if(board[i][j]=='#')
                board[i][j]='O';
            }
        }
        return ;
    }
};

2.使用標(biāo)記矩陣

int arr[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
class Solution {
public:
    void DFS(vector<vector<int>>& visited,vector<vector<char>>& board,int row,int col,int curX ,int curY)
    {
        //與外界聯(lián)通的還是O,我們標(biāo)記為1
        visited[curX][curY] = 1;

        for(int i =0;i<4;++i)
        {
            int newx = curX+arr[i][0];
            int newy = curY+arr[i][1];
            if(newx<0||newx>=row||
            newy<0||newy>=col)
            continue;
            //沒(méi)有被訪問(wèn)過(guò)的位置(防止重復(fù)遞歸)&& 為O的位置我們才遞歸
            if(visited[newx][newy]==0&&
            board[newx][newy]=='O')
            {
                DFS(visited,board,row,col,newx,newy);
            }       
        }
    }
    void solve(vector<vector<char>>& board) {
        //思路:我們從周?chē)腛出發(fā),與邊界O DFS都能獲取到的位置我們都可以做個(gè)標(biāo)記
        //最后將標(biāo)記的不動(dòng),沒(méi)標(biāo)記的換成'X'(內(nèi)陸)。
        int row = board.size();
        int col=board[0].size();
        vector<vector<int>> visited(row,vector<int>(col,0));
        //遍歷周?chē)鸀镺的位置
        //兩列
        for(int i =0;i<row;++i)
        {
            if(board[i][0]=='O')
            DFS(visited,board,row,col,i,0);
            if(board[i][col-1]=='O')
            DFS(visited,board,row,col,i,col-1);       
        }
        //兩行
        for(int j =0;j<col;++j)
        {
            if(board[0][j]=='O')
            DFS(visited,board,row,col,0,j);
            if(board[row-1][j]=='O')
            DFS(visited,board,row,col,row-1,j);    
        }
        //最后將沒(méi)有標(biāo)記的O換成X,標(biāo)記了的不變
        for(int i =0;i<row;++i)
        {
            for(int j =0;j<col;++j)
            {
                if(visited[i][j]==0)
                board[i][j] = 'X';
                cout<<visited[i][j];
            }
        }
    }
};

五、島嶼數(shù)量

200. 島嶼數(shù)量

在這里插入圖片描述

有了前面題的基礎(chǔ),對(duì)于這道題可謂是信手拈來(lái),詳情請(qǐng)看注釋:

int arr[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
class Solution {
public:
    void DFS(vector<vector<int>>& visited,vector<vector<char>>& grid,int row,int col,int curx,int cury)
    {
        //將當(dāng)前位置標(biāo)記為走過(guò)
        visited[curx][cury]=1;
        for(int i =0;i<4;++i)
        {
            int newx = curx+arr[i][0];
            int newy = cury+arr[i][1];
            if(newx<0||newx>=row||newy<0||newy>=col)
                continue;
            if(grid[newx][newy]=='1'&&visited[newx][newy]==0)
            DFS(visited,grid,row,col,newx,newy);
        }
    }
    int numIslands(vector<vector<char>>& grid) {
        //這道題其實(shí)也是用BFS,DFS都可以解,用DFS的話我的思路是從第一個(gè)開(kāi)始遍歷,遇到一個(gè)visited沒(méi)有訪問(wèn)過(guò)的1就去遍歷他的上下左右,將他周?chē)?都遍歷,在將次數(shù)加一
        int row =grid.size();
        int col=grid[0].size();
        int count =0;
        vector<vector<int>> visited(row,vector<int>(col,0));
        for(int i =0;i<row;++i)
        {
            for(int j =0;j<col;++j)
            {
                if(visited[i][j]==0&&grid[i][j]=='1')
                {
                    DFS(visited,grid,row,col,i,j);
                    count++;
                }
            }
        }
        return count;
    }
};

六、 小練習(xí):島嶼的最大面積

695. 島嶼的最大面積

這道題留給大家作為一個(gè)小練習(xí),有了前面的基礎(chǔ),相信這道題想必不是問(wèn)題?。?/p>

總結(jié)

回溯思想DFS一些難度較大的題一起講?。FS的學(xué)習(xí)時(shí)要注意多調(diào)試,想清楚了再下手寫(xiě),這樣往往能夠事半功倍

到此這篇關(guān)于最短時(shí)間學(xué)會(huì)基于C++實(shí)現(xiàn)DFS深度優(yōu)先搜索的文章就介紹到這了,更多相關(guān)C++ DFS深度優(yōu)先搜索內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

迁安市| 衢州市| 灵武市| 蕉岭县| 万年县| 明溪县| 宿迁市| 准格尔旗| 万盛区| 天柱县| 重庆市| 监利县| 桐庐县| 承德县| 广德县| 和林格尔县| 东乡族自治县| 弥勒县| 大邑县| 凤冈县| 老河口市| 黄陵县| 招远市| 永和县| 潼南县| 措美县| 云霄县| 涟水县| 东乡县| 蒙阴县| 日喀则市| 东光县| 富川| 岱山县| 定安县| 莆田市| 卢湾区| 和顺县| 福海县| 建昌县| 凤冈县|