前言
稀疏數組是我們一開始學數據結構的時候第一次有點味道的小算法了,大部分的人第一次是交代在這裏。這是因爲,這個需求來自於我們比較喜歡的五子棋小遊戲。這個遊戲主要是當年有個東西叫做電子詞典,也不知道是啥規則,清一色都有這個遊戲。
稀疏數組在應用在五子棋上面的一個原因是,落子不多的話格子是很少,但是我們定義全局的時候其實是一個二維數組。
說正事
棋盤中的代碼其實是一個數組,但是裏面會附帶很多0元素,這樣子保存起來其實是很划不來的,我們要實現的就是按照壓縮策略去進行存儲。
首先我們的數組長這個樣子:
|0| |1| |2| |3| |4| |5| |6| |7| |8|
|0| 0 0 0 0 0 1 1 0 0
|1| 0 0 0 0 0 0 0 0 0
|2| 0 0 0 0 0 0 0 0 0
|3| 0 0 0 0 0 0 0 0 0
|4| 0 0 0 0 0 0 0 0 0
|5| 0 0 0 0 0 0 0 0 0
|6| 0 0 0 0 0 0 0 2 2
|7| 0 0 0 0 0 0 0 0 0
|8| 0 0 0 0 0 0 0 0 0
我們的有效數據其實只有4個,我們定義稀疏數組的規則如下:
1.首行保存數組多少行多少列
2.剩下的行按照行號+列號+原數值來保存
具體的保存效果如下:
[9][9][4] 首行表示原數組有多少行多少列
[0][5][1] 剩下的行數存儲信息爲行號|列號|原數值
[6][7][2]
[6][8][1]
一旦這樣子去做,我們的數組一下子就減少很多,至少肉眼看過去盤面也小很多。
稀疏數組生成
稀疏數組的轉換,核心部分我加了詳細註釋
public void sparseArr(){
int count=0;
//因爲需要知道稀疏數組的大小,除了首行之外我們需要有多少個有效的元素
for(int i = 0; i< chessArry.length; i++){
for (int j = 0; j < chessArry[i].length; j++) {
if(chessArry[i][j]!=0){
count++;
}
}
}
//0行用來保存原數組的大小信息
sparseArray=new int[count+1][3];
//首行賦值
sparseArray[0][0]=chessArrySize;
sparseArray[0][1]=chessArrySize;
sparseArray[0][2]=count;
//保存有效元素按照 行號|列號|值的格式去存儲
int offset=1;
for (int i = 0; i < chessArry.length; i++) {
for (int j = 0; j < chessArry[i].length; j++) {
if(chessArry[i][j]!=0){
sparseArray[offset][0]=i;
sparseArray[offset][1]=j;
sparseArray[offset][2]=chessArry[i][j];
offset++;
}
}
}
}
稀疏數組存盤與恢復
存盤操作就是一個寫文件操作,但是文件需要我們約定一下格式,以便讀取的時候按照格式恢復,我這邊直接用豎線分割就好了。
public void storeChess(){
if(sparseArray==null){
sparseArr();
}
//把稀疏數組保存到磁盤
File file=new File(storePath);
PrintWriter printWriter=null;
try {
printWriter=new PrintWriter(new FileOutputStream(file));
for (int i = 0; i < sparseArray.length; i++) {
for (int j = 0; j < sparseArray[i].length; j++) {
printWriter.print(sparseArray[i][j]+"|");
}
printWriter.println();
}
System.out.printf("sparse data save to %s successful!\n",storePath);
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally {
if(printWriter!=null){
printWriter.close();
}
}
}
數據恢復
數據恢復包含兩個方面,一個是從磁盤讀取,另外是我們需要恢復成正常的數組,這個是數據解壓的操作。
public void restoreChess(){
if(chessArry!=null){
clearChess();
}
System.out.println("準備恢復棋盤...");
//從文件中恢復數據
File file=new File(storePath);
try {
BufferedReader br=new BufferedReader(new FileReader(file));
String firstLine= br.readLine();//首行記錄了棋盤的信息
String[] headSplits= firstLine.split("\\|");
int lineSize=Integer.valueOf(headSplits[0]);
int cloumnSize=Integer.valueOf(headSplits[1]);
int elCount=Integer.valueOf(headSplits[2]);
System.out.printf("棋盤行數:%d,棋盤列數%d,有效元素個數%d\n",lineSize,cloumnSize,elCount);
chessArry=new int[lineSize][cloumnSize];
for(int i=1;i<elCount+1;i++){ //第一行開始纔有元素
String line= br.readLine();//數組信息
String[] lineSplit= line.split("\\|");
int xIndex=Integer.valueOf(lineSplit[0]);
int yIndex=Integer.valueOf(lineSplit[1]);
int value=Integer.valueOf(lineSplit[2]);
chessArry[xIndex][yIndex]=value;
}
System.out.println("恢復棋盤完成...");
} catch (IOException e) {
e.printStackTrace();
}
}
整個連起來跑一通
略去原數組
壓縮後的數組
9 9 4
0 5 1
0 6 1
6 7 2
6 8 2
準備恢復棋盤...
棋盤行數:9,棋盤列數9,有效元素個數4
恢復棋盤完成...
|0| |1| |2| |3| |4| |5| |6| |7| |8|
|0| 0 0 0 0 0 1 1 0 0
|1| 0 0 0 0 0 0 0 0 0
|2| 0 0 0 0 0 0 0 0 0
|3| 0 0 0 0 0 0 0 0 0
|4| 0 0 0 0 0 0 0 0 0
|5| 0 0 0 0 0 0 0 0 0
|6| 0 0 0 0 0 0 0 2 2
|7| 0 0 0 0 0 0 0 0 0
|8| 0 0 0 0 0 0 0 0 0
後記
博客上面展示代碼其實比較長,但是還沒想到好點的辦法,最後附上源碼:
稀疏數組源碼