不相交集类以及应用迷宫生成
簡單介紹:
考慮一個迷宮的生成,一個簡單算法就是從各處的墻壁開始(除入口和出口之外)。此時,不斷地隨機選擇一面墻,如果被該墻分割的單元彼此不聯通,那么就把這面墻拆掉。重復這個過程直到開始單元和終止單元聯通,那么就得到一個迷宮。實際上不斷的拆掉墻壁直到每個單元都可以從其他單元到達更好(這會使迷宮產生更多誤導的路徑)。
因此主要是兩個問題:
1. 拆掉墻如果兩個單元不連通。
2. 保留墻如果兩個單元已經聯通。
這里用于判斷不同單元是否在同一集合的數據結構即為不相交集類。
基本知識:
即我們要建立多個等價類,聯通的元素放在同一個等價類里面。
對應的,需要兩個操作:
1. 將兩個元素放到一個等價類里的話,采用union操作。
2. 查找某個元素在哪個等價類里,采用find操作。根據find的返回值可以判斷兩個元素是否在一個等價類里。
可以用數組來記錄,程序里我使用的是vector(仿照書里的)。
舉個例子看可能比較清楚:
假設元素ID剛開始為 0 1 2 3 4 5 6 7 8 9
建立相同大小的數組s,數組初始化值均為-1。
最直觀的處理,比如union(4,5)即使s[5] = 4。(約定union(x,y)使得s[y]=x,效果是相同的)。
find(4)會直接返回4本身,因為s[4]<0。
而find(5)則遞歸調用,實際上為:find(5) = find(s[5]) = 4,因此判斷出4,5屬于同一個等價類(我覺得可以理解為4這個結點即為等價類的代表)。
find最壞情形運行時間為O(N),因為對N個元素,有可能建立一個深度為N-1的樹。
因此對union和find操作都進行了改進。
為了控制樹的深度,可以重寫union的方式,比如unionBySize,unionByHeight,書上都介紹,這里就不說了。此時find運行時間為O(N)。
另外對于find巧妙的改進是路徑壓縮,即當find(x)調用時,修改遞歸找到的結點直接指向等價類的代表結點,使得路徑上的結點移近根結點,這樣下次調用時就很快了。
正如書上所說:
這種數據結構實現起來很簡單,每個例程只需要幾行代碼,而且可以使用一個簡單的數組。
可以看到迷宮差別很大,我只是使得入口出口兩個單元連通起來即結束循環。
效率什么的沒有考慮。
所有的代碼:數據集合類,迷宮生成類
/******************************************************************************** ** ** Filename: DisjSets.h ** ** Description: ** ** Version: 1.0 ** Created: 2011年12月06日 11時43分46秒 ** Revision: none ** Compiler: gcc ** ** Author: zhy (), izualzhy@163.com ** Company: ** *********************************************************************************/ #ifndef DISJSETS_H #define DISJSETS_H #include <vector> #include <iostream> using namespace std;class DisjSets {public:explicit DisjSets(int numElements);int find(int x);void unionSets(int root1, int root2);private:vector<int> s; }; #endif//DISJSETS_H
/******************************************************************************** ** ** Filename: DisjSets.cpp ** ** Description: ** ** Version: 1.0 ** Created: 2011年12月06日 11時45分09秒 ** Revision: none ** Compiler: gcc ** ** Author: zhy (), izualzhy@163.com ** Company: ** *********************************************************************************/ #include "DisjSets.h"DisjSets::DisjSets(int numElements) : s(numElements) {for ( int i=0; i<s.size(); ++i)s[i] = -1; }int DisjSets::find(int x) {//cout << "DisjSets::find x= " << x << "\ts[x] = " << s[x] << endl;if (s[x]<0)return x;else {s[x] = find(s[x]);return s[x];} }void DisjSets::unionSets(int root1, int root2) {if (s[root1]>s[root2]) {s[root1] = root2;} else {if (s[root1]==s[root2])s[root1]--;s[root2] = root1;} }
/******************************************************************************** ** ** Filename: MazeDataGenerator.h ** ** Description: generator the data of maze ** ** Version: 1.0 ** Created: 2011年12月06日 14時49分09秒 ** Revision: none ** Compiler: gcc ** ** Author: zhy (), izualzhy@163.com ** Company: ** *********************************************************************************/ #ifndef MAZEDATAGENERATOR_H #define MAZEDATAGENERATOR_H #include <vector> #include "DisjSets.h" class Room{public:Room(int row, int col) :i(row) ,j(col){for ( int i=0; i<4; ++i)wall[i] = true;}int i;int j;bool wall[4];bool operator==(const Room &room2){return ((this->i)==(room2.i) && (this->j==room2.j));} };class MazeDataGenerator {public:enum Direction{NORTH,EAST,SOUTH,WEST};MazeDataGenerator(int row, int column);~MazeDataGenerator(){delete mData;mData = NULL;for ( int i=0; i<mRows; ++i)for ( int j=0; j<mColumns; ++j)delete mRoom[i][j];for ( int i=0; i<mRows; ++i)delete [] mRoom[i];delete [] mRoom;}void print();private:bool isConnected(Room *room1, Room *room2) {return (mData->find(room1->i*mColumns+room1->j)==mData->find(room2->i*mColumns+room2->j));}void initRoad();bool connectTworoom(Room *room1, Room *room2, Direction d);private:DisjSets *mData;Room ***mRoom;int mColumns;int mRows; }; #endif//MAZEDATAGENERATOR_H
/******************************************************************************** ** ** Filename: MazeDataGenerator.cpp ** ** Description: ** ** Version: 1.0 ** Created: 2011年12月06日 14時51分42秒 ** Revision: none ** Compiler: gcc ** ** Author: zhy (), izualzhy@163.com ** Company: ** *********************************************************************************/ #include <stdlib.h> #include <time.h> #include <stdio.h> #include "MazeDataGenerator.h"MazeDataGenerator::MazeDataGenerator(int row, int column) :mRows(row) ,mColumns(column) {srand(time(NULL));mData = new DisjSets(row*column);mRoom = new Room**[row];for ( int i=0; i<row; ++i)mRoom[i] = new Room*[column];for ( int i=0; i<row; ++i)for ( int j=0; j<column; ++j)mRoom[i][j] = new Room(i,j);mRoom[0][0]->wall[NORTH] = mRoom[0][0]->wall[EAST] = false;mRoom[mRows-1][mColumns-1]->wall[SOUTH] = mRoom[0][0]->wall[WEST] = false;initRoad(); }void MazeDataGenerator::initRoad() {printf("%s\n",__FUNCTION__);Room *room = mRoom[0][0];while (!isConnected(mRoom[0][0],mRoom[mRows-1][mColumns-1])) {Direction side = (Direction)(rand()%4);int i = room->i;int j = room->j;switch(side) {case NORTH:i = room->i-1;break;case SOUTH:i = room->i+1;break;case EAST:j = room->j-1;break;case WEST:j = room->j+1;break;}if (i<0 || i>=mRows || j<0 || j>=mColumns)continue;if (!isConnected(mRoom[room->i][room->j],mRoom[i][j]))connectTworoom(mRoom[room->i][room->j],mRoom[i][j],side);room = mRoom[i][j];//print();} }bool MazeDataGenerator::connectTworoom(Room *room1, Room *room2, Direction d) {//printf("%s\n",__FUNCTION__);//printf("(%d,%d)-->(%d,%d)\n",room1.i,room1.j,room2.i,room2.j);if (room1==room2)return false;int index1 = room1->i*mColumns+room1->j;int index2 = room2->i*mColumns+room2->j;int root1 = mData->find(index1);int root2 = mData->find(index2);if (root1==root2) return false;mData->unionSets(root1,root2);room1->wall[d] = false;room2->wall[(d+2)%4] = false;return true; }void MazeDataGenerator::print() {printf("%s\n",__FUNCTION__);for ( int i=0; i<mRows; ++i) {for ( int j=0; j<mColumns; ++j)printf("%2d ",mData->find(mRows*i+j));printf("\n");}for (int j=0; j<mColumns; ++j)printf("__");printf("\n");for ( int i=0; i<mRows; ++i) {for ( int j=0; j<mColumns; ++j) {char s[3] = " ";if (((j-1<0) && i!=0) || mRoom[i][j]->wall[1]) s[0]='|';if ((i+1)>=mRows || mRoom[i][j]->wall[2]) s[1]='_';printf("%s",s);if ((j+1)==mColumns && i!=mRows-1) printf("|");}printf("\n");} }
參考資料:
數據結構與算法分析—C++描述
 
轉載自?http://www.cppblog.com/izualzhy/archive/2011/12/11/161913.html
總結
以上是生活随笔為你收集整理的不相交集类以及应用迷宫生成的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: weiss数据结构和算法书的使用说明
- 下一篇: 三十之惑–面霸的八月(第一部分)
