近來在考研的過程中,再次拿起數據結構的課本,重溫數據結構的魅力,讓自己有了比之前不一樣的體會,各種典型的數據結構如:線性表、棧、隊列、樹與圖等,確實是我們做軟件這一行人員必備的基礎知識,當然主要還是爲了考研鞏固知識目的,決定在接下來的一段時間裏與大家一起來重溫經典的數據結構知識!好了,不多說,讓我們進入今天的主題吧!
今天我們先來編寫一個衆所周知的遊戲——迷宮,以便從中來重溫和加深數據結構中有關棧的相關知識與操作。
大家都知道,至於迷宮的求解問題,可以用窮舉法進行求解。那麼什麼是窮舉法了,就是將每一種可能的情況都窮舉完。而具體到迷宮的求解問題上,由於在求解過程中可能會遇到某一路徑不可行的情況,此時我們就必須按原路返回,這時自然也就會想到棧的應用了,因爲棧的一個很重要的特性就是”先進後出”,可以用來記錄每次所探索的路徑,方便在原路返回的過程中,得到上一步所走路徑,再按此方法,退回到可以走得通的位置上,繼續探索下去,直到到達終點或者最終無法到達,正常退出程序爲止。
下面我們現在首先定義一下有關迷宮求解過程中運用到的相關定義與技巧吧!
1.我們規定每次探索的方向是把當前位置的相鄰的東邊位置作爲第一個探索位置,若不通,則再逆時間方向探索之。具體地說就是右——上——左—— 下的方向進行探索。當然這個方向大家可以隨意確定,只是確定後探索方向後,我們在下面編寫確定下一位置的函數過程中得根據這個方向進行編碼!當然迷宮的位置我們可以定義一個結構體表示,如:
1: typedef struct Postion 2: { 3: int x; 4: int y; 5: }Postion;
那麼確定下一探索位置的函數我們可以這麼編寫:
01: struct Postion nextPos(struct Postion pos,int dir) 02: { 03: //contrarotate(¨逆?時±針?旋y轉a)? 04: switch(dir) 05: { 06: case 1:pos.y+=1;break; 07: case 2:pos.x-=1;break; 08: case 3:pos.y-=1;break; 09: case 4:pos.x+=1;break; 10: default:break; 11: } 12: return pos; 13: }
2.我們定義一個順序棧來存儲探索過的路徑,以便返回時得到上一次走過的位置。而棧元素主要記錄了當前位置、步數以及下一探索方向,即:
1: typedef struct mazenode 2: { 3: struct Postion pos;//current location 4: int curstep;//current step number 5: int dir;//current direction 6: }mazenode;
接下來就是定義棧的各種典型操作呢,有初始化、進棧、出棧、是否爲空判斷等操作。(此步驟較簡單,在此先不貼出代碼了~)
3.最後讓我們來理清一下迷宮求解過程的關鍵思想吧!
do
{
if(當前的路徑可以通過)
{
留下足跡;
將當前位置保存併入棧
if(判斷當前路徑是否爲最終路徑)
{
退棧,並修改迷宮的數據,記錄所走的路線
並返回 1;
}
當前步數增一;
獲取下一位置;
}
else//當前位置走不通
{
if(當前棧不爲空)
{
退棧;
while(當前位置的所走方向爲4並且當
當前棧不爲空)
{
記錄當前位置不爲走不通;
退棧處理;
當前步數減一;
}
if(當前的位置所走方向小於4)
{
將當前位置的方向增一;
將當前位置重新進棧;
獲取下一將要通過的位置;
}
}
}
}
while(當前的棧不爲空)
(注:當前的邏輯寫得比較簡單,各位看客們呆會看具體的源碼哈!)
接下來我先貼一下有關棧的相關操作函數吧,主要包括典型的進棧
、出棧、判斷棧是否爲空等操作。
01: void initStack(struct Sqstack *s) 02: { 03: s->base=(struct mazenode **)malloc(STACK_INIT_SIZE*sizeof(struct mazenode *)); 04: if(!s->base) 05: { 06: printf("allocation fails!"); 07: exit(-1); 08: } 09: s->top=s->base; 10: s->stacksize=STACK_INIT_SIZE; 11: } 12: 13: void Push(struct Sqstack *s,struct mazenode * e) 14: { 15: if((s->top-s->base)>=STACK_INIT_SIZE) 16: { 17: s->base=(struct mazenode **)realloc(s->base,(STACK_INIT_SIZE+STACKINCREASE)*sizeof(struct mazenode *)); 18: if(!s->base) 19: { 20: printf("allocation fails!"); 21: exit(-1); 22: } 23: s->top=s->base+STACK_INIT_SIZE; 24: s->stacksize=STACK_INIT_SIZE+STACKINCREASE; 25: } 26: *s->top++=e; 27: } 28: 29: void Pop(struct Sqstack *s,struct mazenode **e) 30: { 31: if(s->base==s->top) 32: { 33: printf("the stack is empty!"); 34: exit(0); 35: } 36: *e=*(--s->top); 37: } 38: 39: int getTop(struct Sqstack *s,struct mazenode ** e) 40: { 41: if(s->base==s->top) 42: { 43: printf("the stack is empty!"); 44: return 0; 45: } 46: else 47: { 48: *e=*(s->top-1); 49: return 1; 50: } 51: } 52: 53: int emptyStack(struct Sqstack *s) 54: { 55: if(s->base==s->top) 56: { 57: return 1;//stack is empty! 58: } 59: else 60: { 61: return 0;//stack is not empty! 62: } 63: }
接下來是有關迷宮求解的“業務”規則代碼吧!
01: int Pass(struct Postion pos,int array[MAZESIZE][MAZESIZE]) 02: { 03: if(array[pos.x][pos.y]!=0&&array[pos.x][pos.y]!=-1) 04: { 05: return 1;//indicate the way can pass 06: } 07: else 08: { 09: return 0;//indicate the way can not pass 10: } 11: } 12: 13: struct Postion nextPos(struct Postion pos,int dir) 14: { 15: //contrarotate(¨逆?時±針?旋y轉a)? 16: switch(dir) 17: { 18: case 1:pos.y+=1;break; 19: case 2:pos.x-=1;break; 20: case 3:pos.y-=1;break; 21: case 4:pos.x+=1;break; 22: default:break; 23: } 24: return pos; 25: } 26: 27: 28: void markFoot(int arr[MAZESIZE][MAZESIZE],struct Postion pos) 29: { 30: arr[pos.x][pos.y]=0;//have pass by the way 31: } 32: 33: void markblock(int arr[MAZESIZE][MAZESIZE],struct Postion pos) 34: { 35: arr[pos.x][pos.y]=-1;//do not pass by the postion 36: }
然後是迷宮求解可走線路的核心算法:
01: int processMaze(int arr[MAZESIZE][MAZESIZE],struct Postion start,struct Postion end) 02: { 03: struct Sqstack s; 04: struct mazenode *p=(struct mazenode*)malloc(sizeof(struct mazenode)); 05: struct mazenode *nodelist=(struct mazenode *)malloc(100*sizeof(struct mazenode));//put down the way of sucess! 06: int curstep=1,flag=0; 07: struct Postion curpos=start,temp; 08: initStack(&s); 09: do 10: { 11: if(Pass(curpos,arr)) 12: { 13: markFoot(arr,curpos); 14: //struct mazenode node; 15: nodelist[flag].pos=curpos; 16: nodelist[flag].curstep=curstep; 17: nodelist[flag].dir=1;//towards east 18: Push(&s,nodelist+flag); 19: flag++; 20: if(curpos.x==end.x&&curpos.y==end.y) 21: { 22: while(!emptyStack(&s)) 23: { 24: Pop(&s,&p); 25: arr[p->pos.x][p->pos.y]=p->curstep; 26: } 27: return 1; 28: } 29: curstep++; 30: curpos=nextPos(curpos,1);//towards east 31: } 32: else 33: { 34: if(!emptyStack(&s)) 35: { 36: Pop(&s,&p); 37: while(p->dir==4&&!emptyStack(&s)) 38: { 39: markblock(arr,p->pos);//mark that the way is not passed 40: Pop(&s,&p); 41: curstep--; 42: } 43: if(p->dir<4) 44: { 45: p->dir++; 46: Push(&s,p); 47: temp=p->pos; 48: curpos=nextPos(temp,p->dir); 49: } 50: } 51: } 52: } 53: while(!emptyStack(&s)); 54: return 0;//failure 55: }
最後我們來實現下代碼的可行性測試函數吧(說白了就是main函數啦)
01: int _tmain(int argc, _TCHAR* argv[]) 02: { 03: int maze[8][8]={{0,0,0,0,0,0,0,0},{0,0,1,0,0,1,0,0},{0,1,1,0,0,0,1,0}, 04: {0,1,0,0,0,0,0,0},{0,1,1,1,0,0,1,0},{0,0,0,1,0,0,1,0}, 05: {0,0,0,1,1,1,1,0},{0,0,0,0,0,0,0,0}},i,j,flag; 06: struct Postion start,end; 07: start.x=1;start.y=2; 08: end.x=4;end.y=6; 09: printf("primative maze:\n"); 10: for(i=0;i<MAZESIZE;i++) 11: { 12: for(j=0;j<MAZESIZE;j++) 13: { 14: printf("%2d",maze[i][j]); 15: } 16: printf("\n"); 17: } 18: flag=processMaze(maze,start,end); 19: if(flag==1) 20: { 21: printf("maze processing success!\n"); 22: printf("processed maze:\n"); 23: for(i=0;i<MAZESIZE;i++) 24: { 25: for(j=0;j<MAZESIZE;j++) 26: { 27: printf("%2d",maze[i][j]); 28: } 29: printf("\n"); 30: } 31: } 32: else 33: { 34: printf("maze processing fail!\n"); 35: } 36: return 0; 37: }
好了,最後讓我們看看最終方案的運行情況吧!