//本文同步發表於簡書,若想食用更佳,點選檢視原文:https://www.jianshu.com/p/978859c65c1b
前言
高階資料結構難點很多,而且小編接近一年沒有碰過程式碼了,簡書一天能釋出的文章數目有限?,所以今天決定爆肝一個晚上來一個超長的部落格。
但小編能力有限,只會講解下列幾個部分:
- 樹、圖遍歷的基礎——搜尋
- 佇列
- 樹的基本知識
- 二叉樹
- 二叉排序樹
- 平衡樹Treap
- 紅黑樹(待更中……)
- 樹狀陣列
- 線段樹
- 圖論(待更中……)
實際上這都是我從網上找來的一大堆看似很高階,實則很高階的東西,但是小編會非常詳細的記錄經驗和講解知識,以及一些實際的例題。
不過圖論內容較多,圖論將會另更一篇(不過如果想立刻學的話可以看一下我昔日的部落格:傳送門),屆時在章末補上鍊接。
一、樹、圖遍歷的基礎——搜尋
在此部分,我們先講解深度優先搜尋(dfs),廣度優先搜尋就放到佇列裡面講。
搜尋是至關重要的,因為樹,圖在遍歷時,使用的就是搜尋演算法,遍歷是它們的基本操作之一。
1)前置基礎
務必學會遞迴
2)核心思想
如果你現在正身處一個迷宮中,你想走出迷宮,那麼方法主要有兩種
-
1>一條路走到黑,不撞南牆不回頭,只要認定一個方向,就一直走到邊界才掉頭
-
2>環顧四周,不停向外擴充套件,先看離自己近的是不是出口,再看遠的
第一種辦法靠碰運氣,說不準第一次方向就蒙對了,但也有可能運氣差到遍歷整個地圖才能找到。
第二種情況也靠碰運氣,說不準終點就在起點旁邊,但是也可能運氣差到終點在邊界處。
我們現在將要講解的就是第一種辦法。
3)談談怎麼程式碼實現吧
整個過程是這樣的:
void dfs(一些需要傳遞的資訊,來唯一識別當前的狀態,例如當前走的次數,當前的位置等)
{
if(判斷當前狀態是否是目標狀態) 如果是就存下這個答案,返回
for(逐個列舉可能的狀態)
{
擴充套件新狀態;
if(新狀態可以使用)
{
有時需要做標記;
dfs(新狀態);
回溯,即撤回標記,防止下次不能再到達這個狀態(有時不需要回溯)
}
}
}
具體一點,對於迷宮,要求輸出最小步數,可以修改成這樣:
void dfs(橫座標,縱座標,當前步數)
{
if(橫座標==終點橫座標&&縱座標==終點縱座標)
{
ans=min(ans,當前步數);
return;
}
for(列舉四個方向,即上下左右)
{
擴充套件出下一步的橫座標,縱座標;
if(橫座標、縱座標沒有超出地圖範圍且這個新地方沒有走過)
{
標記這個點走過了;
dfs(新橫座標,新縱座標,步數+1);
標記這個點沒有走過;
}
}
}
例題
例1
對於上面的迷宮,正好小編最近刷了一道規規矩矩的迷宮題。
題目連結:https://www.luogu.com.cn/problem/P1605
對於不想抬起寶貴的手點選連結(因為這樣手移動會做功消耗動能,與空氣摩擦還要生熱)的同志,直接看題吧:
P1605 迷宮
題目背景
給定一個N*M方格的迷宮,迷宮裡有T處障礙,障礙處不可通過。給定起點座標和終點座標,問: 每個方格最多經過1次,有多少種從起點座標到終點座標的方案。在迷宮中移動有上下左右四種方式,每次只能移動一個方格。資料保證起點上沒有障礙。
題目描述
無
輸入格式
第一行N、M和T,N為行,M為列,T為障礙總數。第二行起點座標SX,SY,終點座標FX,FY。接下來T行,每行為障礙點的座標。
輸出格式
給定起點座標和終點座標,問每個方格最多經過1次,從起點座標到終點座標的方案總數。
輸入輸出樣例
輸入 #1
2 2 1
1 1 2 2
1 2
輸出 #1
1
說明/提示
【資料規模】
1≤N,M≤5
AC程式碼
#include<bits/stdc++.h>
using namespace std;
int n,m,t,ans,sx,sy,fx,fy,a,b;
int edge[100][100],mapp[100][100];
int next[4][2]={{1,0},{-1,0},{0,1},{0,-1}};//下一步怎麼走
void dfs(int x,int y)
{
if(x==fx&&y==fy) //判斷目標狀態
{
ans++;//解的個數增加
return;
}
for(int i=0;i<4;i++)
{
int tx=x+next[i][0];
int ty=y+next[i][1];//擴充套件出下一步的位置
if(tx<1||ty<1||tx>n||ty>m||mapp[tx][ty]==1||edge[tx][ty]==1) continue; //判斷下一步走這個點是否成立
mapp[tx][ty]=1;//標記走過了,防止同一路徑重複走造成死迴圈
dfs(tx,ty);
mapp[tx][ty]=0;//標記回沒走過,因為可能另一條路徑需要經過這個點
}
}
int main()
{
cin>>n>>m>>t;
cin>>sx>>sy>>fx>>fy;
for(int i=1;i<=t;i++)
{
cin>>a>>b;
edge[a][b]=1;//標記障礙物
}
mapp[sx][sy]=1;
dfs(sx,sy);
cout<<ans;
return 0;
}
例2
題目連結:https://www.luogu.com.cn/problem/P1135
P1135 奇怪的電梯
預覽結束,請點此進入原文:https://www.jianshu.com/p/978859c65c1b