數據結構必要知識
線性結構
-
線性結構是最常用的數據結構,數據元素之間存在一對一的線性關系。
-
線性結構有兩種不同的存儲結構,即順序存儲結構和鏈式存儲結構。順序存儲的線性表稱為順序表,順序表中的存儲元素是連續的。
-
鏈式存儲的線性表稱為鏈表,鏈表中的存儲元素不一定是連續的,元素節點中存放數據元素以及相鄰的地址信息。
-
線性表結構常見的有:數組,隊列,鏈表,棧
非線性結構
非線性結構包括:二維數組,多維數組,廣義表,樹結構,圖結構(這就不是一對一了)
稀疏數組
基本介紹
-
定義:
當一個數組中大部分的元素為0,或為同一個值的數組時,可以使用稀疏數組來保存該數組。
eg:
可以用這種數組模擬各種棋盤,迷宮什么的.
在這里我們用問題驅動的模式:
我們現在要制作一個棋盤游戲.
先不考慮這個棋怎么玩.
我們現在必須要構建出一個二維數組.
如下面所示.
*
類似這個就是一個棋盤
- 用0表示沒有被下過的地方.
- 用1表示黑子.
- 用2表示藍子.
無疑這么大的一個棋盤所需要記錄的東西有點太多了,太耗費空間了,有沒有簡單一點的辦法?
辦法就是:
稀疏矩陣
回到剛才的圖片中.
我們發現其中大部分的數字都是重復的,基本都是0,實際上我們真正在乎的就只有有棋子的部分就是上面那個二維數組中有1,2的節點.
稀疏編號 row col val 0 11 11 2 1 1 2 1 2 2 3 2 這就是圖片中棋盤的稀疏矩陣(就是第二張圖片的簡化版本).
一個二階數組.
用這么一個簡單的方式就可以輕松的描繪出那個復雜的充滿了0和1的正方形棋盤.
那我們都要記錄什么內容呢
-
棋盤大小
-
上面棋子的落點
於是我們就知道了真正的問題就是如何存儲這些數據.
-
稀疏編號為0的那一行里面保存的是關於這個棋盤的數據.
-
11行
-
11列
-
里面有2個棋子.
-
-
稀疏編號從1開始的那些行保存的是關於棋子的信息
- col 第幾列
- row 第幾行
- val 是哪種棋子
-
稀疏數組處理的方法是:
-
-
記錄數組一共有幾行幾列,有多少個不同的值
-
把具有不同值的元素的行列值記錄在一個小規模的數組(稀疏數組)中,從而縮小程序的規模。
-
既然我們知道了這些,那么基本就可以清楚稀疏數組的運行方式:
用第一行的那個數組創建整個棋盤,用剩下的數組建立各種棋子.
那么就開始看代碼吧:
首先必須知道一點,稀疏數組出現之前必須現有一個對應的二維數組(就是那個滿是0與1的棋盤).
要不然這....這思維真有點厲害了.
所以我們可以先看一段制造二維數組(棋盤)的代碼
//創建一個原始的二維數11*11
//0表示沒有旗子,1表示黑子,2表示藍子
//這里在程序中手動輸入棋子...low一點.
int chessArr1[][] = new int[11][11];
chessArr1[1][2] = 1;
chessArr1[2][3] = 2;
//輸出原始的二維數組
for (int[] row : chessArr1) {
for (int data : row) {
System.out.println("原始的二維數組");
System.out.printf("%d\t", data);
}
System.out.println();//換行用
}
好了,棋盤制造完畢,這個棋盤就是圖中的棋盤.
然后我們想辦法將這個二位數組(棋盤)化為稀疏數組
- 我們先完成對於稀疏數組第一行的構建(別忘了,這一行可和其它行不一樣哦)
- 這一行中有三個值,一個是二位數組行數,一個是二維數組列數,一個是棋盤中棋子數所以我們要遍歷整個數組.
//1.先遍歷二維數組 得到非0數據的個數
int sum = 0;
for (int i = 0; i < 11; i++) {
for (int j = 0; j < 11; j++) {
if (chessArr1[i][j] != 0) {
sum++;
}
}
}
//將得到的值放入稀疏數組
int spareseArr[][] = new int[sum + 1][3];//sum是一共有多少個棋
//給稀數組賦值
//這是稀疏數組的第一行,也就是存放標准二維數組(棋盤)信息的地方
spareseArr[0][0] = 11;//有多少行
spareseArr[0][1] = 11;//有多少列
spareseArr[0][2] = sum;//必須拿到sum才能創建數組(sum在棋盤上有幾個棋子)
現在遍歷完了數組,我們知道了第一行,現在我們要繼續完成之后的行數了.
- 現在開始第二行的構建,第二行開始存儲的是每個棋子的行,列,值(顏色).想要得到這些消息
就必須再次遍歷整個數組,找到這些棋盤.
/*稀疏數組已經創建過了.spareseArr[][]
int chessArr1[][] = new int[11][11];//二維數組(棋盤)
*/
//遍歷二維數組,將非0的值放到sparseArr中
int count = 0;//count 用於記錄是第幾個非0數據
for (int i = 0; i < 11; i++) {
for (int j = 0; j < 11; j++) {
if (chessArr1[i][j] != 0) {
//count是從1開始的,並不存在第0個棋子,但是卻存在數組中的第0個元素,正好和第一行錯開.
count++;//代表的是第幾個棋子。 spareseArr[count]中存放的一維數組中存放的內容就是這個棋子的信息。
spareseArr[count][0] = i;//第一列,存放(非零數據)棋子在普通數組(棋盤)中的位置(行數)
spareseArr[count][1] = j;//第二列,存放(非零數據)棋子在普通數組(棋盤)中的位置(列數)
spareseArr[count][2] = chessArr1[i][j];//第三列,存放(非零數據)數據。(是黑棋藍棋)
}
}
}
就這樣我們構建了第0行和剩下的稀疏矩陣.
但是我們不僅如此,雖然將那么大的一個棋盤壓縮了.
但是我們還要把這個棋盤復原.
那就要將稀疏數組化為二維數組(棋盤)
//將稀疏數組--》恢復成二維數組
/**
* 1.先讀取稀疏數組的第一行,根據第一行的數據,創建原始的二維數組,比如上面的chressArray
* 2.在讀取稀疏數組后幾行的數據,並賦給原始的二維數組即可。
*/
//1.先讀取稀疏數組的第一行,根據第一行的數據,創建原始的二維數組
//稀疏數組第一列是普通二維數組的行,第二列是普通數組的列
int chessArr2[][] = new int[spareseArr[0][0]][spareseArr[0][1]];//普通數組
//2.在讀取稀疏數組后幾行的數據(從第二行開始),並付給原始的二維數組即可
//之所以從1開始,是因為稀疏矩陣第一行存的是普通數組的信息
for (int i=1;i<spareseArr.length;i++){
chessArr2[spareseArr[i][0]][spareseArr[i][1]]=spareseArr[i][2];
//chessArr2[spareseArr[i][0]非零數據所在行][spareseArr[i][1]非零數據所在列]=spareseArr[i][2]非零數據的值。
}
//輸出恢復后的二維數組
System.out.println("恢復后的二維數組");
for (int[] row : chessArr2) {
for (int data : row) {
//關於printf相關知識:https://blog.csdn.net/qq_39017218/article/details/80042287
System.out.printf("%d\t", data);
}
System.out.println();
}
}
}
這里解釋的很清楚了,就不多敘述了,這就是java算法中最簡單的一種,稀疏數組.