JAVA數據結構與算法(二)棧、遞歸

1) 棧的英文爲 (stack)
2) 棧是一 先入後出 (FILO-First In Last Out) 的有序列表
3) (stack) 是限制線性表中元素的插入和刪除 只能在線性表的同一端 進行的一種特殊線性表。容許插入和刪除的一端,爲 變化的一端,稱爲 棧頂 (Top) ,另外一端爲 固定的一端,稱爲 棧底 (Bottom)
4) 根據棧 的定義可知,最早放入棧中元素在棧底,最後放入的元素在棧頂,而刪除元素恰好相反,最後放入的元素最早刪除,最早放入的元素最後刪除

棧的應用場景

1) 程序的調用:在跳往子程序前,會先將下個指令的地址存到堆棧中,直到子程序執行完後再將地址取出,以回到原來的程序中  
2) 理遞歸調用:和子程序的調用相似,只是除了儲存下一個指令的地址外,也將參數、區域變量等數據存入堆棧中
3) 達式的轉 [ 中綴表達式轉後綴表達式 ] ( 實際解決 )
4) 叉樹的遍歷
5) 形的深度優先 (depth first) 搜索法。

棧的代碼實現

//定義一個 ArrayStack 表示棧
class ArrayStack {
    private int maxSize; // 棧的大小
    private int[] stack; // 數組,數組模擬棧,數據就放在該數組
    private int top = -1;// top表示棧頂,初始化爲-1

//構造器
    public ArrayStack(int maxSize) {
        this.maxSize = maxSize;
        stack = new int[this.maxSize];
    }

//棧滿
    public boolean isFull() {
        return top == maxSize - 1;
    }

//棧空
    public boolean isEmpty() {
        return top == -1;
    }

//入棧
    public void push(int value) {
        //先判斷棧是否滿
        if(isFull()) {
            System.out.println("棧滿");
            return;
        }
        top++;
        stack[top] = value;
    }

//出棧 
//將棧頂的數據返回
    public int pop() {
        //先判斷棧是否空
        if(isEmpty()) {
            //拋出異常
            throw new RuntimeException("棧空,沒有數據~");
        }
        int value = stack[top];
        top--;
        return value;
    }

//[遍歷棧], 遍歷時,須要從棧頂開始顯示數據
    public void list() {
        if(isEmpty()) {
            System.out.println("棧空,沒有數據~~");
            return;
        }
        //須要從棧頂開始顯示數據
        for(int i = top; i >= 0 ; i--) {
            System.out.printf("stack[%d]=%d\n", i, stack[i]);
        }
    }

測試:java

public static void main(String[] args) {
    //先建立一個arraystack對象表示棧
        ArrayStack stack =new ArrayStack(4);
        String key= "";
        boolean loop = true;
        Scanner scanner =new Scanner(System.in);
        while (loop){
            System.out.println("show: 表示顯示棧");
            System.out.println("exit: 退出程序");
            System.out.println("push: 表示添加數據到棧(入棧)");
            System.out.println("pop: 表示從棧取出數據(出棧)");
            System.out.println("請輸入你的選擇");
            key = scanner.next();
            switch (key) {
                case "show":
                    stack.list();
                    break;
                case "push":
                    System.out.println("請輸入一個數");
                    int value = scanner.nextInt();
                    stack.push(value);
                    break;
                case "pop":
                    try {
                        int res = stack.pop();
                        System.out.printf("出棧的數據是 %d\n", res);
                    } catch (Exception e) {
                        // TODO: handle exception
                        System.out.println(e.getMessage());
                    }
                    break;
                case "exit":
                    scanner.close();
                    loop = false;
                    break;
                default:
                    break;
            }
        }
        System.out.println("程序退出了!");
    }

 

遞歸(Recursion)

簡單的說: 遞歸就是方法自己調用本身,每次調用時傳入不一樣的變量.遞歸助於編程者解決複雜的問題,同時可讓代碼變得簡潔程序員

遞歸調用機制算法

1) 打印 問題
2) 階乘

遞歸用於解決什麼樣的問題

1) 種數學問題如 : 8 後問題 , 漢諾塔 , 階乘問題 , 迷宮問題 , 球和籃子的問題 (google 編程大賽 )
2) 種算法中也會使用到遞歸,好比快排,歸併排序,二分查找,分治算法等 .
3) 用棧解決的問題 --> 第歸代碼比較簡潔

歸須要遵照的重規則

1) 行一 方法 ,就建立一個新的受保護的獨立空間 ( 空間 )
2) 方法 局部變量是獨立的,不會相互影 , 好比 n
3) 果方法中使用的是引用類型變量 ( 好比數組 ) ,就會共享該引用類型的數據 .
4) 歸必須向退出遞歸的條件逼近,不然就是無限遞 , 出現 StackOverflowError 死龜了 :)
5) 方法 行完畢,或者遇到 return ,就會返回,遵照誰調用,就將結果返回給誰,同時 方法 行完畢或者返回時, 就執行完畢。
l 遞歸應用場景
迷宮問題

說明: 1)球獲得的路徑,和程序員設置的找路策略有關即:找路的上下左右的順序相關編程

2)再獲得小球路徑時,能夠先使用(下右上左),再改爲(下左)看路徑是不是有變化數組

3) 試回溯 現象
4) : 如何求出最短路徑 ?
/**
 * @author Sun.Mr
 * @create 2019-09-17 22:21
 */
public class Migong {
    public static void main(String[] args) {
        //先建立一個二維數組,模擬迷宮
        //地圖
        int[][] map = new int[8][7];
        //使用1表示牆
        //上下左右皆置位1
        for (int i = 0; i < 7; i++) {
            map[0][i] = 1;
            map[7][i] = 1;
        }
        for (int i = 0; i < 8; i++) {
            map[i][0] = 1;
            map[i][6] = 1;
        }
        //設置擋板
        map[3][1] = 1;
        map[3][2] = 1;

        //輸出地圖
        System.out.println("地圖的狀況");
        for (int i = 0; i < 7; i++) {
            for (int j = 0; j < 7; j++) {
                System.out.print(map[i][j] + " ");
            }
            System.out.println();
        }

        //使用遞歸回溯給小球找路
        setWay(map, 1, 1);

        //輸出新的地圖, 小球走過,並標識過的遞歸
        System.out.println("小球走過,並標識過的 地圖的狀況");
        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 7; j++) {
                System.out.print(map[i][j] + " ");
            }
            System.out.println();
        }
    }
    
    //使用遞歸回溯給小球找路
    //(1,1)----->(6,5)
    //4. 約定: 當map[i][j] 爲 0 表示該點沒有走過 當爲 1 表示牆  ; 2 表示通路能夠走 ; 3 表示該點已經走過,可是走不通
    //5. 在走迷宮時,須要肯定一個策略(方法) 下->右->上->左 , 若是該點走不通,再回溯
    /**
     *
     * @param map  表示地圖
     * @param i 從哪一個位置開始找
     * @param j
     * @return 若是找到通路,就返回true,不然就返回false
     */
    public static boolean setWay(int[][] map, int i, int j) {
        if(map[6][5] == 2) { // 通路已經找到ok
            return true;
        } else {
            if(map[i][j] == 0) { //若是當前這個點尚未走過
                //按照策略 下->右->上->左  走
                map[i][j] = 2; // 假定該點是能夠走通.
                if(setWay(map, i+1, j)) {//向下走
                    return true;
                } else if (setWay(map, i, j+1)) { //向右走
                    return true;
                } else if (setWay(map, i-1, j)) { //向上
                    return true;
                } else if (setWay(map, i, j-1)){ // 向左走
                    return true;
                } else {
                    //說明該點是走不通,是死路
                    map[i][j] = 3;
                    return false;
                }
            } else { // 若是map[i][j] != 0 , 多是 1, 2, 3
                return false;
            }
        }
    }

}

遞歸-八皇后問題(回溯算法)

皇后問題,是一個古老而著名的問題,是回溯算法的典型案例。該問題是國際西洋棋棋手馬克斯·貝瑟爾於1848年提出:在8×8格的國際象棋上擺放八個皇后,使其不能互相攻擊,即:意兩個皇后都不能處於同一行、同一列或同一斜線上,問有多少種擺法oop

 

題算法路分析測試

1) 一個皇后先放第一行第一列
2) 二個皇后放在第二行第一列、而後判斷是否 OK 果不 OK 續放在第 二列、第三列、依次把全部列都放完,找到一個合
3) 繼續第三個皇后,仍是第一列、第二列 …… 直到第 8 個皇后也能放在一個不衝突的位置,算是找到了一個正確
4 ) 當獲得一個正確解時,在棧回退到上一個棧時,就會開始回溯,即將第一個皇后,放到第一列的全部正確解,所有獲得 .
5) 而後回頭繼續第一個皇后放第二列,後面繼續循 環執行 1,2,3,4 的步驟 
 

說明論上應該建立一個二維數組來表示棋盤,可是實際上能夠經過算法,用一個一維數組便可解決問題. arr[8] = {0 , 4, 7, 5, 2, 6, 1, 3} //對應arr 下標 表示第幾行,即第幾個皇后,arr[i] = val , val 表示第i+1個皇后,放在第i+1行的第val+1this

代碼實現:google

package com.xtkj;

/**
 * @author Sun.Mr
 * @create 2019-09-18 10:54
 */
public class Queue8 {

    //定義一個max表示共有多少個皇后
    int max = 8;
    //定義數組array, 保存皇后放置位置的結果,好比 arr = {0 , 4, 7, 5, 2, 6, 1, 3}
    int[] array = new int[max];
    static int count = 0;
    public static void main(String[] args) {
        //測試一把 , 8皇后是否正確
        Queue8 queue8 = new Queue8();
        queue8.check(0);
        System.out.printf("一共有%d解法", count);


    }


    //編寫一個方法,放置第n個皇后
    public void check(int n){
        if (n == max) {
            print();
            return;
        }

        //依次放入皇后,並判斷是否衝突
        for(int i = 0; i < max; i++) {
            //先把當前這個皇后 n , 放到該行的第1列
            array[n] = i;
            //判斷當放置第n個皇后到i列時,是否衝突
            if(judge(n)) { // 不衝突
                //接着放n+1個皇后,即開始遞歸
                check(n+1); //
            }
            //若是衝突,就繼續執行 array[n] = i; 即將第n個皇后,放置在本行得 後移的一個位置
        }
    }

    /**
     * 查看當咱們放置第n個皇后, 就去檢測該皇后是否和前面已經擺放的皇后衝突
     * @param n
     * @return
     */
    private boolean judge(int n) {
        // 說明
        //1. array[i] == array[n]  表示判斷 第n個皇后是否和前面的n-1個皇后在同一列
        //2. Math.abs(n-i) == Math.abs(array[n] - array[i]) 表示判斷第n個皇后是否和第i皇后是否在同一斜線
        // n = 1  放置第 2列 1 n = 1 array[1] = 1
        // Math.abs(1-0) == 1  Math.abs(array[n] - array[i]) = Math.abs(1-0) = 1
        //3. 判斷是否在同一行, 沒有必要,n 每次都在遞增

        for (int i = 0; i < n; i++) {
            // 說明
            //1. array[i] == array[n]  表示判斷 第n個皇后是否和前面的n-1個皇后在同一列
            //2. Math.abs(n-i) == Math.abs(array[n] - array[i]) 表示判斷第n個皇后是否和第i皇后是否在同一斜線
            // n = 1  放置第 2列 1 n = 1 array[1] = 1
            // Math.abs(1-0) == 1  Math.abs(array[n] - array[i]) = Math.abs(1-0) = 1
            //3. 判斷是否在同一行, 沒有必要,n 每次都在遞增
            if (array[i] == array[n] || Math.abs(n - i) == Math.abs(array[n] - array[i])) {
                return false;
            }

        }
        return true;
    }

    private void print() {
        count++;
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i] + " ");
        }
        System.out.println();
    }

}

相關文章
相關標籤/搜索