數據結構與算法_1.稀疏數組和隊列_1

1. 稀疏數組和隊列

1.1 稀疏(sparsearray)數組


  • 基本概念
    • 當一個數組中大部分元素爲0,或者爲同一個值的數組時,可以使用稀疏數組來保存該數組。
  • 處理方法
    • 記錄數組一共有幾行幾列,有多少個不同的值。
    • 把具有不同值的元素的行列及值記錄在一個小規模的數組中,從而縮小程序的規模
  • 舉例
    • 普通數組在這裏插入圖片描述
    • 稀疏數組稀疏數組
  • 代碼實現
public class AparseArray {
    public static void main(String[] args) {
        // 1.創建一個原始的二維數組:11*11
        // 1.1 0表示沒有棋子,1表示黑子,2表示藍子
        int chessArr1[][] = new int[11][11];
        chessArr1[1][2] = 1;
        chessArr1[2][3] = 2;
        // 1.2 輸出原始二維數組
        System.out.println("原始二維數組爲:");
        for (int[] row : chessArr1) {
            for (int data : row) {
                System.out.printf("%d\t", data);
            }
            System.out.println();
        }

        // 2.將二維數組轉爲稀疏數組
        // 2.1 遍歷二維數組,得到非零數據的個數
        int sum = 0;
        for (int i = 0; i < chessArr1.length; i++) {
            for (int j = 0; j < chessArr1.length; j++) {
                if (chessArr1[i][j] != 0) {
                    sum++;
                }
            }
        }
        // 2.2 創建對應的稀疏數組
        int sparseArr[][] = new int[sum + 1][3];
        // 2.3 給稀疏數組賦值
        sparseArr[0][0] = 11;
        sparseArr[0][1] = 11;
        sparseArr[0][2] = sum;
        // 2.4 遍歷二維數組,將非0的值存放到sparseArr中
        int count = 0;
        for (int i = 0; i < chessArr1.length; i++) {
            for (int j = 0; j < chessArr1.length; j++) {
                if (chessArr1[i][j] != 0) {
                    count++;
                    sparseArr[count][0] = i;
                    sparseArr[count][1] = j;
                    sparseArr[count][2] = chessArr1[i][j];
                }
            }
        }
        // 2.5 輸出稀疏數組
        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]);
        }

        // 3.將稀疏數組恢復成原始二維數組
        // 3.1 先讀取稀疏數組的第一行,根據第一行的數據,創建原始二維數組
        int chessArr2[][] = new int[sparseArr[0][0]][sparseArr[0][1]];
        // 3.2 再讀取稀疏數組後幾行的數據,並賦給原始二維數組即可
        for (int i = 1; i < sparseArr.length; i++) {
            chessArr2[sparseArr[i][0]][sparseArr[i][1]] = sparseArr[i][2];
        }
        // 3.3 輸出恢復後的二維數組
        System.out.println("恢復後的二維數組爲:");
        for (int[] row : chessArr2) {
            for (int data : row) {
                System.out.printf("%d\t", data);
            }
            System.out.println();
        }
    }
}

運行結果爲:

原始二維數組爲:
0 0 0 0 0 0 0 0 0 0 0 
0 0 1 0 0 0 0 0 0 0 0 
0 0 0 2 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 
稀疏數組爲:
11 11 2 
1  2  1 
2  3  2 
恢復後的二維數組爲:
0 0 0 0 0 0 0 0 0 0 0 
0 0 1 0 0 0 0 0 0 0 0 
0 0 0 2 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0

1.2 隊列


  • 基本概念
    • 隊列是一個有序列表,可以用數組或是鏈表來實現。
    • 遵循先入先出的原則。即:先存入隊列的數據,要先取出。後存入的要後取出
  • 數組模擬隊列
    • 示意圖在這裏插入圖片描述
    • 其中 MaxSize 是該隊列的最大容量,兩個變量 front 及 rear 分別記錄隊列前後端的下標,front 會隨着數據輸出而改變,而 rear 則是隨着數據輸入而改變。
    • 代碼實現
public class Demo01ArrayQueue {
    public static void main(String[] args) {
        // 創建一個隊列
        ArrayQueue queue = new ArrayQueue(3);
        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':
                    queue.showQueue();
                    break;
                case 'a':
                    System.out.println("請輸入一個數");
                    int value = scanner.nextInt();
                    queue.addQueue(value);
                    break;
                case 'g':
                    try {
                        int res = queue.getQueue();
                        System.out.printf("取出的數據是%d\n", res);
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'h':
                    try {
                        int res = queue.headQueue();
                        System.out.printf("隊列頭數據是%d\n", res);
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'e':
                    scanner.close();
                    loop = false;   // 退出while循環
                    break;
                default:
                    break;
            }
        }
        System.out.println("程序退出");
    }
}

// 使用數組模擬隊列
// 1. 編寫一個ArrayQueue類
class ArrayQueue {
    private int maxSize;    // 表示數組的最大容量
    private int front;      // 隊列頭
    private int rear;       // 隊列尾
    private int[] arr;      // 該數組用於存放數據,模擬隊列

    // 創建隊列的構造器
    public ArrayQueue(int arrMaxSize) {
        maxSize = arrMaxSize;
        arr = new int[maxSize];
        front = -1;     // 指向隊列頭部,front是指向隊列頭的第一個數據的前一個位置
        rear = -1;      // 指向隊列尾部,rear是指向隊列尾的最後一個數據

    }

    // 判斷隊列是否滿
    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];
    }
}
  • 數組模擬環形隊列
    • 問題分析
      • 上述數組隊列,沒能達到複用效果,只能使用一次。
      • 使用算法改進爲環形隊列(通過取模的方式來實現)。
    • 處理方法(算法)
      • 尾索引的下一個爲頭索引時表示隊列滿,即將隊列容量空出一個作爲約定。
      • 判斷隊列滿的依據:(rear + 1) % maxSize == front
      • 判斷隊列空的依據:rear == front
      • 隊列中有效數據的個數:(rear + maxSize - front) % maxSize
    • 代碼實現
public class Demo02CircleArrayQueue {
    public static void main(String[] args) {
        System.out.println("數組模擬環形隊列");

        // 創建一個隊列
        CircleArrayQueue queue = new CircleArrayQueue(4);
        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':
                    queue.showQueue();
                    break;
                case 'a':
                    System.out.println("請輸入一個數");
                    int value = scanner.nextInt();
                    queue.addQueue(value);
                    break;
                case 'g':
                    try {
                        int res = queue.getQueue();
                        System.out.printf("取出的數據是%d\n", res);
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'h':
                    try {
                        int res = queue.headQueue();
                        System.out.printf("隊列頭數據是%d\n", res);
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'e':
                    scanner.close();
                    loop = false;   // 退出while循環
                    break;
                default:
                    break;
            }
        }
        System.out.println("程序退出");
    }
}

class CircleArrayQueue {
    private int maxSize;    // 表示數組的最大容量
    private int front;      // front指向隊列的第一個元素,arr[front]是隊列的第一個元素,初始值=0
    private int rear;       // rear指向隊列的最後一個元素的後一個位置. 因爲希望空出一個空間做爲約定,初始值=0
    private int[] arr;      // 該數組用於存放數據,模擬隊列

    // 創建構造器
    public CircleArrayQueue(int arrMaxSize) {
        maxSize = arrMaxSize;
        arr = new int[maxSize];
    }

    // 判斷隊列是否滿
    public boolean isFull() {
        return (rear + 1) % maxSize == front;
    }

    // 判斷隊列是否爲空
    public boolean isEmpty() {
        return rear == front;
    }

    // 添加數據到隊列
    public void addQueue(int n) {
        // 判斷隊列是否滿
        if (isFull()) {
            System.out.println("隊列滿,不能加入!");
            return;
        }
        // 直接將數據加入
        arr[rear] = n;
        // 將rear後移,這裏必須考慮取模
        rear = (rear + 1) % maxSize;
    }

    // 獲取隊列的數據,出隊列
    public int getQueue() {
        // 判斷隊列是否空
        if (isEmpty()) {
            throw new RuntimeException("隊列爲空,不能取數據!");
        }
        // 這裏需要分析出front是指向隊列的第一個元素
        // 1.先把front對應的值保留到一個臨時變量
        // 2.將front後移,考慮取模
        // 3.將臨時保存的變量返回
        int value = arr[front];
        front = (front + 1) % maxSize;
        return value;
    }

    // 顯示隊列的所有數據
    public void showQueue() {
        // 遍歷
        if (isEmpty()) {
            System.out.println("空隊列");
            return;
        }
        // 從front開始遍歷
        for (int i = front; i < front + size(); i++) {
            System.out.printf("arr[%d]=%d\n", i % maxSize, arr[i % maxSize]);
        }
    }

    // 求出當前隊列有效數據的個數
    public int size() {
        return (rear + maxSize - front) % maxSize;
    }

    // 顯示隊列的頭數據,注意不是取數據
    public int headQueue() {
        // 判斷
        if (isEmpty()) {
            throw new RuntimeException("空隊列");
        }
        return arr[front];
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章