資料結構與演算法(java版)

码小贺發表於2024-07-11

資料結構與演算法

資料結構和演算法概述

資料結構和演算法的關係

資料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];
    }
}

非線性結構

二維陣列,多維陣列,廣義表,樹結構,圖結構
此筆記參考的尚矽谷的資料結構與演算法課程

相關文章