資料結構與演算法
資料結構和演算法概述
資料結構和演算法的關係
資料data結構(structure)是一門研究組織資料方式的學科,有了程式語言也就有了資料結構.學好資料結構可以編寫出更加漂亮,更加有效率的程式碼。
要學習好資料結構就要多多考慮如何將生活中遇到的問題,用程式去實現解決。
程式=資料結構+演算法
資料結構是演算法的基礎,換言之,想要學好演算法,需要把資料結構學到位。
線性結構
線性結構作為最常用的資料結構,其特點是資料元素之間存在一對一的線性關係
線性結構有兩種不同的儲存結構,即順序儲存結構(陣列和鏈式儲存結構(連結串列)。順序儲存的線性表稱為順序表,順序表中的儲存元素是連續的
鏈式儲存的線性表稱為連結串列,連結串列中的儲存元素不一定是連續的,元素節點中存放資料元素以及相鄰元素的地址資訊
線性結構常見的有:陣列、佇列、連結串列和棧
稀疏陣列
當一個陣列中大部分元素為0,或者為同一個值的陣列時,可以使用稀疏陣列來儲存該陣列。
記錄陣列一共有幾行幾列,有多少個不同的值
把具有不同值的元素的行列及值記錄在一個小規模的陣列中,從而縮小程式的規模
二維陣列轉換成稀疏陣列
思路:
遍歷原始的二維陣列,得到有效資料的個數sum
根據sum就可以建立稀疏陣列sparseArr int[sum+1][3]
將二維陣列的有效資料資料存入到稀疏陣列
稀疏陣列轉換成原始的二維陣列思路
先讀取稀疏陣列的第一行,根據第一行的資料,建立原始的二維陣列,比如上面的chessArr2=int[11[11】
在讀取稀疏陣列後幾行的資料,並賦給原始的二維陣列即可.
程式碼實現
建立二維陣列
//稀疏陣列
public class SparseArray {
public static void main(String[] args) {
//建立一個原始的二維陣列11*11
//0:沒有棋子,1:黑子,2:藍子
int chessArr1[][] = new int[11][11];
chessArr1[1][2] = 1;
chessArr1[2][3] = 2;
//輸出原始的二維陣列
for (int[] row : chessArr1){
for (int data : row){
System.out.printf("%d\t",data);
}
System.out.println();
}
}
}
增強for迴圈
for(資料型別 變數名 :遍歷的目標){ //資料型別 變數名:宣告一個變數用來接收遍歷目標遍歷後的元素
}
如果是實現了Iterable介面的物件或者是陣列物件都可以使用增強for迴圈。
普通for迴圈可以沒有遍歷的目標,而增強for迴圈一定要有遍歷的目標
printf()函式
Java中的printf()方法是類似於C語言的printf()風格的一種格式化輸出功能。printf()並不使用過載的 “+” 運算子(C沒有過載)來連線引號內的字串或者字串變數,而是使用特殊的佔位符來表示資料將來的位置。而且它將插入格式化字串的引數,按照順序以逗號隔開,在字串後面順次排列。
這些佔位符在Java中被稱為 格式修飾符 。
%d表示是一個整數
\t
是補全當前字串長度到8的整數倍,最少1個最多8個空格
public class SparseArray {
public static void main(String[] args) {
//建立一個原始的二維陣列11*11
//0:沒有棋子,1:黑子,2:藍子
int chessArr1[][] = new int[11][11];
chessArr1[1][2] = 1;
chessArr1[2][3] = 2;
//輸出原始的二維陣列
System.out.println("原始的二維陣列");
for (int[] row : chessArr1){
for (int data : row){
System.out.printf("%d\t",data);
}
System.out.println();
}
/**
* 二維陣列轉換成稀疏陣列
* 遍歷原始的二維陣列,得到有效資料的個數sum
* 根據sum就可以建立稀疏陣列sparseArr int[sum+1][3]
* 將二維陣列的有效資料資料存入到稀疏陣列
*/
int sum = 0;
for (int i = 0; i < 11; i++) {
for (int j = 0; j < 11; j++) {
if (chessArr1[i][j] != 0){
sum++;
}
}
}
int sparseArr[][] = new int[sum+1][3];
sparseArr[0][0] = 11;
sparseArr[0][1] = 11;
sparseArr[0][2] = sum;
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++;
sparseArr[count][0] = i;
sparseArr[count][1] = j;
sparseArr[count][2] = chessArr1[i][j];
}
}
}
//輸出稀疏陣列
System.out.println();
System.out.println("轉換為稀疏陣列如下:");
for (int i = 0;i < sparseArr.length; i++) {
System.out.printf("%d\t%d\t%d\t\n",sparseArr[i][0],sparseArr[i][1],sparseArr[i][2]);
}
System.out.println();
/**
* 稀疏陣列轉換成原始的二維陣列思路
* 先讀取稀疏陣列的第一行,根據第一行的資料,建立原始的二維陣列,比如上面的chessArr2=int[11[11】
* 在讀取稀疏陣列後幾行的資料,並賦給原始的二維陣列即可.
*/
int chessArr2[][] = new int[sparseArr[0][0]][sparseArr[0][1]];
for (int i = 1; i < sparseArr.length; i++){
chessArr2[sparseArr[i][0]][sparseArr[i][1]] = sparseArr[i][2];
}
System.out.println();
System.out.println("恢復後的二維陣列");
for (int[] row : chessArr2){
for (int data : row){
System.out.printf("%d\t",data);
}
System.out.println();
}
}
}
課後練習
要求:
1)在前面的基礎上,將稀疏陣列儲存到磁碟上,比如map.data
2)恢復原來的陣列時,讀取map.data進行恢復
程式碼實現
思路:
使用FileOutputStream將稀疏陣列寫入chess.text檔案
迴圈遍歷稀疏陣列
如果是當前行的第三個元素,直接寫入資料值。
如果不是第三個元素,以逗號結尾的形式寫入資料值,以便後續讀取時可以透過逗號進行分割。
String.valueOf(a) 將整數 a 轉換為字串。
使用 BufferedReader 從 chess.text 檔案中逐行讀取資料。
稀疏矩陣的第一行包含稀疏陣列的行數和列數,從中獲取行數 row 和列數 col,並初始化 chessArray2 陣列。
System.out.println("把稀疏陣列儲存到檔案中.....");
//使用FileOutputStream將稀疏陣列寫入chess.text檔案
FileOutputStream fileOutputStream = new FileOutputStream(new File("chess.text"));
/**
* 迴圈遍歷稀疏陣列
* 如果是當前行的第三個元素,直接寫入資料值。
* 如果不是第三個元素,以逗號結尾的形式寫入資料值,以便後續讀取時可以透過逗號進行分割。
* String.valueOf(a) 將整數 a 轉換為字串。
*/
for (int i = 0; i< sparseArr.length;i++){
for (int j = 0;j< sparseArr[0].length;j++){
int a = sparseArr[i][j];
if(j == 2){
fileOutputStream.write((String.valueOf(a)).getBytes());
}else {
fileOutputStream.write((String.valueOf(a)+",").getBytes());
}
}
fileOutputStream.write("\n".getBytes());
}
System.out.println("-----讀取檔案中的稀疏陣列並恢復為原來的陣列-------");
//使用 BufferedReader 從 chess.text 檔案中逐行讀取資料。
BufferedReader bufferedReader = new BufferedReader(new FileReader("chess.text"));
String line = null;
int c = 0;
String row = null;
String col = null;
//第一行(稀疏矩陣的第一行)包含稀疏陣列的行數和列數,從中獲取行數 row 和列數 col,並初始化 chessArray2 陣列。
int[][] chessArray2 = null;
while((line = bufferedReader.readLine())!= null){
c++;
if(c==1){//稀疏矩陣的第一行
String[] array = line.split(",");//以,為分割讀取檔案
row = array[0];
col = array[1];
chessArray2 = new int[Integer.parseInt(row)][Integer.parseInt(col)];
}else {
String[] array = line.split(",");
String row2 = array[0];
String col2 = array[1];
String val2 = array[2];
chessArray2[Integer.parseInt(val2)][Integer.parseInt(col2)] = Integer.parseInt(val2);
}
}
for (int[] r:chessArray2){
for (int item:r){
System.out.printf("%d\t",item);
}
System.out.println();
}
fileOutputStream.close();
佇列
佇列是一個有序列表,可以用陣列
(順序儲存)或是連結串列
(鏈式儲存)來實現
遵循先入先出的原則。即:先存入佇列的資料,要先取出。後存入的要後取出
使用陣列實現佇列
public class ArrayQueue {
public static void main(String[] args) {
//測試
//建立佇列
ArrayQueue1 arrayQueue1 = new ArrayQueue1(6);
char key = ' '; //接收使用者輸入
Scanner scanner = new Scanner(System.in);
boolean loop = true;
//輸出一個選單
while (loop) {
System.out.println("s(show): 顯示佇列");
System.out.println("e(exit): 退出程式");
System.out.println("a(add): 新增資料到佇列");
System.out.println("g(get): 從佇列中取出資料");
System.out.println("h(head): 檢視佇列頭資料");
key = scanner.next().charAt(0); //接收一個字元
switch (key) {
case 's':
arrayQueue1.showQueue();
break;
case 'a':
System.out.println("輸出一個數");
int value = scanner.nextInt();
arrayQueue1.addQueue(value);
break;
case 'g':
try {
int res = arrayQueue1.getQueue();
System.out.printf("取出的資料是%d\n",res);
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'h': //檢視佇列頭的資料
try {
int res = arrayQueue1.headQueue();
System.out.printf("佇列頭的資料是%d\n",res);
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'e':
scanner.close();
loop = false;
break;
default:
break;
}
}
System.out.println("程式退出");
}
}
//使用陣列模擬佇列,編寫一個ArrayQueue類
class ArrayQueue1 {
private int maxSize; //表示陣列的最大容量
private int front; //佇列頭
private int rear; //佇列尾
private int[] arr; //存放資料,模擬佇列
//建立佇列的構造器
public ArrayQueue1(int arrMaxSize) {
maxSize = arrMaxSize;
arr = new int[maxSize];
front = -1; //指向佇列頭部,指向佇列頭的前一個位置
rear = -1; //指向佇列尾的資料,佇列的最後一個資料
}
//判斷佇列是否滿
public boolean isFull() {
return rear == maxSize - 1;
}
//判斷佇列是否為空
public boolean isEmpty() {
return rear == front;
}
//新增資料到佇列
public void addQueue(int n) {
//判斷佇列是否滿
if (isFull()) {
System.out.println("佇列已滿,不能新增資料");
return;
}
rear++; //讓rear後移一位
arr[rear] = n;
}
//獲取佇列的資料,出佇列
public int getQueue() {
//判斷佇列是否為空
if (isEmpty()) {
//丟擲異常
throw new RuntimeException("佇列為空,不能取出資料");
}
front++; //front後移
return arr[front];
}
//顯示佇列的所有資料
public void showQueue() {
//遍歷
if (isEmpty()) {
System.out.println("佇列為空,沒有資料~~");
return;
}
for (int i = 0; i < arr.length; i++) {
System.out.printf("arr[%d]=%d\n",i,arr[i]);
}
}
//顯示佇列的頭資料
public int headQueue() {
if (isEmpty()) {
throw new RuntimeException("佇列為空沒有資料");
}
return arr[front+1];
}
}
非線性結構
二維陣列,多維陣列,廣義表,樹結構,圖結構
此筆記參考的尚矽谷的資料結構與演算法課程