算法與數(shù)據(jù)結(jié)構(gòu)

1.深度優(yōu)先搜索

下面是深度優(yōu)先搜索遍歷的一個(gè)例子理朋,我們用整數(shù)標(biāo)記節(jié)點(diǎn)皆撩,G記錄有向邊膳算,G[u][v]表示節(jié)點(diǎn)u指向節(jié)點(diǎn)v族跛。用數(shù)組c[M]記錄遍歷狀態(tài):
c[u]==-1----表示u被dfs調(diào)用,但還沒(méi)有返回
c[u]==0----表示從來(lái)沒(méi)有遍歷過(guò)
c[u]==1----表示已經(jīng)遍歷并返回

#include <iostream>

#define M 100
int c[M];
int G[M][M];
bool dfs(int u){
    c[u]=-1;
    for(int v=0; v<M ; ++v)
        if(G[u][v]){
            if(c[v]==-1) return false;//說(shuō)明有環(huán)
            if(c[v]==0 && !dfs(v) ) return false;//如果c[v]是0举娩,則遍歷v析校,此時(shí)如果子圖有環(huán),則停止遍歷铜涉,返回false
        }
    std::cout<<u<<std::endl;
    c[u]=1;
    return true;
}

從注釋也容易看出來(lái)了智玻,深度優(yōu)先搜索可以用來(lái)判斷一個(gè)圖是否有環(huán)。另外芙代,深度優(yōu)先搜索的順序吊奢,恰恰滿足拓?fù)渑判?/p>

2.歐拉回路

2.1.無(wú)向圖

我們把和一個(gè)點(diǎn)相連的邊數(shù)是這個(gè)點(diǎn)的度數(shù),因?yàn)橐粭l邊對(duì)應(yīng)于兩個(gè)點(diǎn)纹烹,因此页滚,所有點(diǎn)的度數(shù)之和肯定是偶數(shù)。
一筆畫的充要條件是:除了起點(diǎn)和終點(diǎn)外铺呵,其它點(diǎn)的度數(shù)一定是偶數(shù)裹驰。

2.2.有向圖

對(duì)于有向圖,除了起點(diǎn)終點(diǎn)外片挂,入度等于出度幻林,并且,起點(diǎn)的出度比入度大1音念,終點(diǎn)的入度比出度大1.

3.子集生成

我們把問(wèn)題簡(jiǎn)化:假設(shè)集合有n個(gè)元素沪饺,是從1到n的整數(shù)。

3.1.增量構(gòu)造法

A表示集合數(shù)組闷愤,n是集合數(shù)組內(nèi)元素的個(gè)數(shù)整葡,cur表示子集中元素的數(shù)目,當(dāng)然肝谭,cur==0表示空集掘宪,此外蛾扇,我們還假設(shè),最開始的時(shí)候魏滚,A已經(jīng)排好了序镀首。

void print_sub_set(int A[], int n, int cur){
    for(int i=0 ; i<cur; ++i)
        std::cout<<A[i]<<" "<<std::flush;
    std::cout<<std::endl;
    int s=cur?A[cur-1]:0;
    for(int i=s ; i<n ; i++){
        A[cur]=i+1;
        print_sub_set(A ,n, cur+1);
    }
}

3.2.位向量法

對(duì)于每一個(gè)元素,用一個(gè)位表示它是否在子集中

void bit_vector(int B[], int n , int cur){
    if(cur==n){
        for(int i=0 ; i<n ; ++i)
            if(B[i])
                std::cout<<i+1<<std::flush;
        std::cout<<std::endl;
        return;
    }
    B[cur]=1;
    bit_vector(B, n, cur+1);
    B[cur]=0;
    bit_vector(B, n, cur+1);
}

3.3.二進(jìn)制法

位向量法就是一種二進(jìn)制法鼠次,此外更哄,集合的并,交腥寇,差等運(yùn)算成翩,是可以轉(zhuǎn)化成二進(jìn)制運(yùn)算的,所以赦役。麻敌。。

4.回溯法

回溯法在遞歸構(gòu)造中掂摔,把生成和檢查結(jié)合起來(lái)术羔,有效減少不必要的枚舉。
舉例:n皇后問(wèn)題

void queen_(int *A , int n, int cur , int *tot){
    if(cur==n) ++(*tot);
    else for(int i=0; i<n ;++i){
        A[cur]=i;
        bool ok=true;
        for(int j=0; j<cur ; ++j)
            if(A[j]==A[cur] || A[j]+j==A[cur]+cur || A[j]-j==A[cur]-cur) ok=false;
        if(ok) queen_(A, n, cur+1, tot);
    }
}

int queen(int n){
    int *A=new int[n];
    int tot=0;
    queen_(A, n , 0 , &tot);
    delete[] A;
    return tot;
}

5.廣度優(yōu)先搜索

比如二叉樹層次遍歷乙漓,就是廣度優(yōu)先搜索的一個(gè)例子级历。我們用一個(gè)先進(jìn)先出的隊(duì)列,實(shí)現(xiàn)了樹的廣度優(yōu)先搜索
對(duì)于圖叭披,仍然是要用隊(duì)列來(lái)實(shí)現(xiàn)廣度優(yōu)先搜索寥殖,然而,圖的情況稍微復(fù)雜涩蜘,除了采用隊(duì)列外嚼贡,還應(yīng)該引入另外的數(shù)據(jù)結(jié)構(gòu),來(lái)防止節(jié)點(diǎn)被重復(fù)遍歷到皱坛。
舉例:八數(shù)碼問(wèn)題
其實(shí)八數(shù)碼問(wèn)題可以歸結(jié)為路徑尋找問(wèn)題编曼,把每個(gè)狀態(tài)看做一個(gè)節(jié)點(diǎn),移動(dòng)空格相鄰的滑塊剩辟,到達(dá)另一個(gè)節(jié)點(diǎn),因此每種移動(dòng)方式可以看做是一條邊往扔。
因?yàn)槭菆D贩猎,因此需要特定的數(shù)據(jù)結(jié)構(gòu)記錄那些節(jié)點(diǎn)被遍歷過(guò)了,如果考慮用一個(gè)數(shù)來(lái)表示萍膛,比如序列123456780就用123456780表示吭服,這將耗費(fèi)9^9這么多的空間,如果采用int型存儲(chǔ)蝗罗,這將是幾百M(fèi)的空間艇棕,顯然這是不合理的蝌戒。實(shí)際上,節(jié)點(diǎn)總數(shù)只有9沼琉!=362880北苟,只有幾M的空間,因此打瘪,我們需要一種編碼方式友鼻,使得每個(gè)狀態(tài),和0到362880之間的整數(shù)一一對(duì)應(yīng)闺骚,一種方式就是按字典序映射彩扔,下面的代碼實(shí)現(xiàn)了這個(gè)功能:

#include <iostream>

int encode(int *A, int n){
    if(A==NULL || n<1)
        return -1;
    if(n==1)
        return 0;
    int *table=new int[n];
    table[0]=1;
    for(int i=1 ; i< n ; ++i)
        table[i]=table[i-1]*i;
    int *exist=new int[n];
    for(int i=0 ; i<n ; ++i)
        exist[i]=0;
    int sum=0;
    for(int i=0; i<n-1 ; ++i ){
        int temp=A[i];
        exist[temp]=1;
        for(int j=0 ; j<A[i] ;++j)
            if(exist[j])
                --temp;
        sum+=temp*table[n-1-i];
    }

    delete[] table;
    delete[] exist;

    return sum;
}

int decode(int A[], int n, int code){
    if(A==NULL || n<1 || code<0)
        return 0;
    int *table=new int[n+1];
    table[0]=1;
    for(int i=1 ; i< n+1 ; ++i)
        table[i]=table[i-1]*i;
    if(code >=table[n])
        return -1;
    int *exist=new int[n];
    for(int i=0 ; i<n ; ++i)
        exist[i]=0;
    int rank;
    for(int i=0; i<n ; ++i){
        int val;
        rank=code/table[n-1-i];
        for(int j=0 ; j<n ; ++j){
            if(!exist[j])
                --rank;
            if(rank<0){
                val=j;
                break;
            }
        }
        A[i]=val;
        exist[val]=1;
        code=code%table[n-1-i];
    }
    return 1;
}


/********test***************/

void print(int *A, int n){
    for(int i=0; i<n ; ++i)
        std::cout<<A[i]<<" "<<std::flush;
    std::cout<<std::endl;
}

int main(){

    int n;
    while(std::cin>>n){
        if(n<=0)
            break;
        int *A=new int[n];
        int code=0;
        int term=1;
        while(term==1){
            term=decode(A, n, code);
            if(term != 1)
                break;
            int t=encode(A, n);
            if(code != t){
                std::cout<<"wrong !"<<std::endl;
                std::cout<<"A:"<<std::endl;
                print(A, n);
                std::cout<<"code: "<<code<<std::endl;
                std::cout<<"encode: "<<t<<std::endl;
                break;
            }
            ++code;
        }
        std::cout<<"code: "<<code<<std::endl;
        delete[] A;
    }

    return 0;
}

利用上面編碼規(guī)則,對(duì)八數(shù)碼問(wèn)題的解決辦法:

/*
這個(gè)代碼調(diào)試的也讓人累覺(jué)不愛了僻爽。虫碉。。
1.memcpy函數(shù)按字節(jié)數(shù)拷貝內(nèi)存胸梆,所以敦捧,是9*sizeof(int), 而不是9!H槿啤绞惦!
2.突然冒出個(gè)想法:不必要對(duì)d進(jìn)行pop_front操作,把迭代器向前移動(dòng)就可以了洋措,然而济蝉。。菠发。當(dāng)插入或刪除vector內(nèi)的元素的時(shí)候王滤,面臨這迭代器失效問(wèn)題。滓鸠。雁乡。囧。糜俗。踱稍。
3.為了避免死循環(huán),father對(duì)start也要有記錄
*/
#include <iostream>
#include <vector>
#include <cstring>

int encode(int *A, int n){
    if(A==NULL || n<1)
        return -1;
    if(n==1)
        return 0;
    int *table=new int[n];
    table[0]=1;
    for(int i=1 ; i< n ; ++i)
        table[i]=table[i-1]*i;
    int *exist=new int[n];
    for(int i=0 ; i<n ; ++i)
        exist[i]=0;
    int sum=0;
    for(int i=0; i<n-1 ; ++i ){
        int temp=A[i];
        exist[temp]=1;
        for(int j=0 ; j<A[i] ;++j)
            if(exist[j])
                --temp;
        sum+=temp*table[n-1-i];
    }

    delete[] table;
    delete[] exist;

    return sum;
}

int decode(int A[], int n, int code){
    if(A==NULL || n<1 || code<0)
        return 0;
    int *table=new int[n+1];
    table[0]=1;
    for(int i=1 ; i< n+1 ; ++i)
        table[i]=table[i-1]*i;
    if(code >=table[n])
        return -1;
    int *exist=new int[n];
    for(int i=0 ; i<n ; ++i)
        exist[i]=0;
    int rank;
    for(int i=0; i<n ; ++i){
        int val;
        rank=code/table[n-1-i];
        for(int j=0 ; j<n ; ++j){
            if(!exist[j])
                --rank;
            if(rank<0){
                val=j;
                break;
            }
        }
        A[i]=val;
        exist[val]=1;
        code=code%table[n-1-i];
    }
    return 1;
}


int main(){

    std::vector<int> father(362880);
    std::vector<int> dist(362880);
    int dx[]={-1, 1, 0, 0};
    int dy[]={0, 0, -1, 1};

    int goon;
    std::cout<<"go on ??"<<std::endl;
    while (std::cin>>goon) {
        if(goon==0)
            break;

        int *star=new int[9];
        int *dest=new int[9];
        std::cout<<"input start:"<<std::endl;
        for(int i=0 ; i<9 ; ++i)
            std::cin>>star[i];
        std::cout<<"input dest:"<<std::endl;
        for(int i=0; i<9 ; ++i)
            std::cin>>dest[i];

        for(auto &s:father)
            s=-1;
        std::vector<int> d;
        int estar=encode(star, 9);
        int edest=encode(dest, 9);
        d.push_back(estar);
        dist[d[0]]=0;
        father[d[0]]=d[0];
        int front=0;
        while(front != d.size()){
            int cur=d[front];
            int S[9];
            decode(S, 9, cur);
            int z;
            for(z=0; z < 9 ; ++z)
                if(S[z]==0)
                    break;
            int row=z/3;
            int col=z%3;

            bool ok=false;
            for(int di=0; di<4 ; ++di){
                int nrow=row+dx[di];
                int ncol=col+dy[di];
                if(nrow>=0 && nrow<3 && ncol>=0 && ncol<3){

                    int nz=nrow*3+ncol;
                    int nS[9];
                    std::memcpy(&nS[0], &S[0], 9*sizeof(int));
                    nS[z]=nS[nz];
                    nS[nz]=0;
                    int enS=encode(nS, 9);
                    if(father[enS] == -1){

                            father[enS]=cur;
                            dist[enS]=dist[cur]+1;
                            if(enS==edest){
                                ok=true;
                                break;
                            }
                            d.push_back(enS);

                    }
                }
            }
            if(ok)
                break;
            ++front;

        }

        std::cout<<"shortest: "<<dist[edest]<<std::endl;
        
        delete[] star;
        delete[] dest;
        std::cout<<"go on ??"<<std::endl;

    }
    return 0;
}

/*
2 6 4 1 3 7 0 5 8
8 1 5 7 3 6 4 0 2
*/

也可以用map結(jié)構(gòu)來(lái)記錄哪些被搜索過(guò)了悠抹。我們知道珠月,map采用樹的結(jié)構(gòu)存儲(chǔ)的。查找楔敌,插入等時(shí)間復(fù)雜度是O(logn)啤挎,因此比前面的方法慢些。為了便于討論卵凑,我們省去了father庆聘,只用dist記錄:

#include <iostream>
#include <vector>
#include <map>
#include <cstring>

int encode(int A[], int n){
    int sum=0;
    for(int i=0; i< n ; ++i)
        sum=sum*10+A[i];
    return sum;
}

void decode(int A[], int n , int code){
    for(int i=n-1; i>=0 ; --i){
        A[i]=code%10;
        code/=10;
    }
}

int main(){

    int dx[]={-1, 1, 0, 0};
    int dy[]={0, 0, -1, 1};

    int goon;
    std::cout<<"go on ??"<<std::endl;
    while (std::cin>>goon) {
        if(goon==0)
            break;

        int *star=new int[9];
        int *dest=new int[9];
        std::cout<<"input start:"<<std::endl;
        for(int i=0 ; i<9 ; ++i)
            std::cin>>star[i];
        std::cout<<"input dest:"<<std::endl;
        for(int i=0; i<9 ; ++i)
            std::cin>>dest[i];

        std::map<int, int> dist;

        std::vector<int> d;
        int en_star=encode(star, 9);
        int en_dest=encode(dest, 9);
        d.push_back(en_star);
        dist[en_star]=0;
        int front=0;
        while(front != d.size()){
            int cur=d[front];
            int S[9];
            decode(S, 9, cur);
            int z;
            for(z=0; z<9 ; ++z)
                if(S[z]==0)
                    break;
            int row = z/3;
            int col = z%3;
            bool ok=false;
            for(int i=0; i<4 ; ++i){
                int nrow=row+dx[i];
                int ncol=col+dy[i];
                if(nrow>=0 && nrow<3 && ncol>=0 && ncol<3){
                    int nz=nrow*3+ncol;
                    int nS[9];
                    memcpy(nS, S, 9*sizeof(int));
                    nS[z]=nS[nz];
                    nS[nz]=0;
                    int enS=encode(nS, 9);
                    if(dist.find(enS) == dist.end()){
                        dist[enS]=dist[cur]+1;
                        if(enS==en_dest){
                            ok=true;
                            break;
                        }
                        d.push_back(enS);
                    }
                }
            }

            if(ok)
                break;

            ++front;
        }

        if(dist.find(en_dest) != dist.end())
            std::cout<<"shortest steps: "<<dist[en_dest]<<std::endl;
        else
            std::cout<<"do not exist ! ! ! "<<std::endl;

        delete[] star;
        delete[] dest;
        std::cout<<"go on ??"<<std::endl;

    }
    return 0;
}

/*
2 6 4 1 3 7 0 5 8
8 1 5 7 3 6 4 0 2
*/
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末胜臊,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子伙判,更是在濱河造成了極大的恐慌象对,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件澳腹,死亡現(xiàn)場(chǎng)離奇詭異织盼,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)酱塔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門沥邻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人羊娃,你說(shuō)我怎么就攤上這事唐全。” “怎么了蕊玷?”我有些...
    開封第一講書人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵邮利,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我垃帅,道長(zhǎng)延届,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任贸诚,我火速辦了婚禮方庭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘酱固。我一直安慰自己械念,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開白布运悲。 她就那樣靜靜地躺著龄减,像睡著了一般。 火紅的嫁衣襯著肌膚如雪班眯。 梳的紋絲不亂的頭發(fā)上希停,一...
    開封第一講書人閱讀 51,708評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音署隘,去河邊找鬼脖苏。 笑死,一個(gè)胖子當(dāng)著我的面吹牛定踱,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播恃鞋,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼崖媚,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼亦歉!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起畅哑,我...
    開封第一講書人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤肴楷,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后荠呐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赛蔫,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年泥张,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了呵恢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡媚创,死狀恐怖渗钉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情钞钙,我是刑警寧澤鳄橘,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站芒炼,受9級(jí)特大地震影響瘫怜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜本刽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一鲸湃、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧盅安,春花似錦唤锉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至蝙寨,卻和暖如春晒衩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背墙歪。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工听系, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人虹菲。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓靠胜,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子浪漠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

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