有人問我“連連看算法怎麼實現?”
我說“保存進二維數組中,然後判斷兩個點之間是否能夠連通”
“怎麼判斷是否連通?”
我說“判斷它們之間的連線拐角不超過兩個”
“怎麼判斷拐角不超過兩個?”
額,具體的實現我也不知道,於是上網找了一把,感覺大多數算法都很容易理解,實現卻不簡單,於是想了下怎麼能把連連看的算法簡單的實現,嘿!還真想出來了,至於文章標題上面還有個分治實現,則因為自己的思路感覺和分治(我對分治理解:大化小,小化更小,直到小的很容易)是一致的,所以不用去糾結分治是什麼,下面開始進入正題:
連連看的核心算法就像上文提及的:判斷兩個點之間的連線拐角不超過兩個。那麼不超過兩個無外乎三種情況:1、沒有拐角。2、一個拐角。3、兩個拐角。沒有拐角最簡單,我們先從它下手。
沒有拐角:
既然沒有拐角則只有兩種可能:在同一個水平線上 或者 在同一個垂直線上,並且中間都是空白。那我們首先判斷兩點 X坐標 或者 Y坐標 是否相等來得出是否在一條線上,接著把兩個點之間都看一遍,看看是否全部都是空的,全部都是空的,那麼兩點就滿足相連,否則只要有一個非空,則不滿足。
//沒有拐角的判定 private boolean zore(int x1,int y1,int x2,int y2){ if(x1 != x2 && y1 != y2){//兩點如果不在同一水平線或者垂直線,則不可能為沒有拐角相通 return false; } if(x1 == x2){//判斷兩點水平之間是否全空白 int minY = Math.min(y1, y2); int maxY = Math.max(y1, y2); for(int i=minY+1; i<maxY; i++){ if(checkArr[x1][i] !=0){ return false; } } }else if(y1 == y2){//判斷兩點垂直之間是否全空白 int minX = Math.min(x1, x2); int maxX = Math.max(x1, x2); for(int i=minX+1; i<maxX; i++){ if(checkArr[i][y1] !=0){ return false; } } } //能執行到這裡說明兩點之間全是空白,所以返回true return true; } 沒有拐角的代碼實現一個拐角:
畫一個直角,而且還必須以固定的兩個點為起點和終點,也只有兩種情況,如下圖所示:
圖中我們A B是我們游戲中選中的兩個點,那我們這兩個點能滿足一個拐角相連只有A->D->B 和 A->C->B這兩種情況,先看A->D->B這種情況:
我們已經知道A(x1,y1) 、B(x2,y2)兩點的坐標,這時D點的坐標自然也就出來了:(x2, y1),知道了D點坐標,那麼我們A-D-B這種一個拐角相連的情況就可以看做是:A到D是否沒有拐角相連,並且D到B是否沒有拐角相連。一個拐角相連的判斷瞬間就變成兩段沒有拐角相連的判斷,好簡單有木有!!不過我們還需要注意一個小地方,如果D點不是空白,那麼我們一切努力就白費了,所以加上一個判斷:D點非空白,直接Pass。
private boolean one(int x1,int y1,int x2,int y2){ if(x1 == x2 || y1 == y2 ){//一個拐角需要兩個坐標均不相等 return false; } return ( data[x1][y2]==0 &&zore(x1,y1,x1,y2)&&zore(x1,y2,x2,y2) ) ||(data[x2][y1]==0 && zore(x1,y1,x2,y1)&&zore(x2,y1,x2,y2) ); } 一個拐角的代碼實現兩個拐角:
之前我們一個拐角的做法是:把問題變成沒有拐角來簡單化,那我們現在來考慮怎麼把兩個拐角的變成一個拐角的來簡單化:
對於從A到B兩個拐角的情況,我們可以說至少第一個拐角的位置我們知道的,“啥?第一個拐角我們知道?兩個點之間兩個拐角的情況那麼多,別逗了!”別急,我這裡說第一個拐角的位置知道是因為第一個拐角只有四種情況:A的水平左邊、A的水平右邊、A的垂直上邊、A的垂直下邊,那麼要找第一個拐角點,我們只需要從A分別循環的向左、向右、向上、向下去找就是了。“那這四個方向那麼多點我們怎麼知道哪一個是第一個拐角點?”這個問題我也不知道,但是我知道如果點C是第一個拐角點,那麼C和B之間必定可以一個拐角相連,到這裡是不是兩個拐角的問題又變成了一個拐角的問題,好簡單有木有!有木有!有木有!重要的事情說三遍!
至於實現自然首先就是從A循環向四個方向去找空白的點,找到了就判斷這個空白的點和B點是否一個拐角相連,如果找到一個非空白點或者到了邊界,那麼循環就可以END了!
public boolean two(int x1,int y1,int x2,int y2){ //向左 for(int i=x1-1; i>-1; i--){ if(checkArr[i][y1]!=0){ break; } if( one(i,y1,x2,y2)){ return true; } } //向右 for(int i=x1+1; i<checkArr.length; i++){ if(checkArr[i][y1]!=0){ break; } if(one(i,y1,x2,y2)){ return true; } } //向上 for(int i=y1-1; i>-1; i--){ if(checkArr[x1][i]!=0){ break; } if(one(x1,i,x2,y2)){ return true; } } //向下 for(int i=y1+1; i<checkArr.length; i++){ if(checkArr[x1][i]!=0){ break; } if(one(x1,i,x2,y2)){ return true; } } return false; } 兩個拐角的代碼實現寫在最後:不得不說這個算法不高效,但我認為它很好理解,至於最後把附上自己的完整的代碼。
源碼