棧(stack)、遞歸(八皇后問題)、排序算法分類,時間和空間復雜度簡介


一、棧的介紹:

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

5)出棧(pop)和入棧(push)的概念

 

 

 

 棧的使用場景:

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

 使用棧實現一個簡單的計算機功能:

import java.util.Stack;

public class Calculator {
    public static void main(String[] args) {
        //獲取一個和棧相關計算操作的對象
        AboutStack stack = new AboutStack();
        String calcu = "2000*1+3*2-4";
        //將字符串轉換為一個char數組
        char[] chars = calcu.toCharArray();

        //循環數組,判斷字符為數字還是操作符,進行添加和計算
        for (char ch:chars) {
            if ('0' <= ch && ch <= '9') {
                //添加數字到數字棧中
                stack.addToNumStack(ch);
            }else {
                //添加符號到符號棧中
                stack.addToSymbolStack(ch);
            }
        }
        //進行最后結果的計算和輸出
        System.out.println(stack.calculator(stack.numStack.pop(),stack.numStack.pop(),stack.symbolsStack.pop()));

    }

}
class AboutStack{
    public Stack<Integer> numStack;
    public Stack<Character> symbolsStack;
    public boolean next = false;

    public AboutStack() {
        //初始化數字棧和字符棧
        numStack = new Stack<>();
        symbolsStack = new Stack<>();
    }
    //添加數到數值棧中
    public void addToNumStack(char ch){
        if (numStack.isEmpty()) {//判斷當前棧是否為空,如果為空就直接將數字加入到數字棧中
            numStack.push(Integer.parseInt(String.valueOf(ch)));
            next = true;
            return;
        }
        if (next ){//判斷是否連續的兩個char字符都是數字,如果是,就表示是多位的數字,得到多位數字再放入到棧中
            int num = numStack.pop();
            String temp = num +""+String.valueOf(ch);//將上一個字符和現在的字符拼接成一個字符串,再將字符串轉換為數字存放在數值棧中
            numStack.push(Integer.parseInt(temp));
            next = true;
        }else {
            //表示上一個存放的是字符,所以這次的數字直接放入到數字棧中
            numStack.push(Integer.parseInt(String.valueOf(ch)));
            next = true;
        }
    }
    //添加符號到符號棧中
    public void  addToSymbolStack(char ch){
        //如果符號棧為空,直接將符號放入到棧中
        if (symbolsStack.isEmpty()) {
            symbolsStack.push(ch);
            next = false;
            return;
        }
        //如果該符號的優先級小於或者等於上一個符號,就先計算上一個符號優先級較大的,、
        // 再將計算結果放入到數值棧,將該符號放入到棧中
        if (priority(ch) <= priority(symbolsStack.peek())) {
            numStack.push(calculator(numStack.pop(), numStack.pop(), symbolsStack.pop()));
            addToSymbolStack(ch);
            //            symbolsStack.push(ch);
            next = false;
        }else{
            symbolsStack.push(ch);
            next = false;
        }

    }
    //判斷符號的優先級
    public int priority(char oper){
        if (oper == '*' || oper == '/') {
            return 2;
        }
        if (oper == '+' || oper == '-') {
            return 1;
        }else {
            return -1;
        }
    }

    //根據數值和符號計算出兩個數的值
    public int calculator(int num1,int num2,char ch){
        int res = 0; // res 用於存放計算的結果
        switch (ch) {
            case '+':
                res = num1 + num2;
                break;
            case '-':
                res = num2 - num1;// 注意順序
                break;
            case '*':
                res = num1 * num2;
                break;
            case '/':
                res = num2 / num1;
                break;
            default:
                break;
        }
        return res;
    }


}

二、前綴(波蘭表達式)、中綴、后綴表達式(逆波蘭表達式)

 

 

 前綴表達式的計算機求值:

 

 

 中綴表達式:

 

 

 后綴表達式(逆波蘭表達式):

 

 

 

 三、遞歸:就是方法自己調用自己,每次調用時傳入不同參數

1、遞歸調用機制的講解

(1)當程序執行到一個方法時,就會開辟一個獨立的空間(棧)

(2)每個空間的數據(局部變量),是獨立的,不會相互影響

(3)如果方法中的使用的引用類型變量(比如數組),就會共享該引用類型的數據。

(4)遞歸必須向退出遞歸的條件逼近,否則就是無線遞歸,出現Stack OverflowError,死龜(歸)了;

(5)當一個方法執行完畢,或者遇到return,就會返回,遵守誰調用,就將結果返回給誰,同時當方法執行完畢或者返回時,該方法也就執行完畢。

2、遞歸常用的場景

1)各種數學問題如:8皇后問題﹐漢諾塔,階乘問題,迷宮問題,球和籃子的問題(google編程大賽)

2)各種算法中也會使用到遞歸,比如快排,歸並排序,二分查找,分治算法等.
3)將用棧解決的問題-->第歸代碼比較簡潔

特別:八皇后問題(在8×8格的國際象棋上擺放8個皇后,使其不能互相攻擊,即任意兩個皇后都不能處於同一行、同一列或同一斜線上,問有多少種擺法..(92種).)

package com.zjl.recursion;

public class Recursion {
    public static void main(String[] args) {

        Queen8 queen8 = new Queen8();
        queen8.Check(0);
        System.out.println("符合八皇后的位置個數一共有:"+queen8.count);
    }
}

class Queen8 {
    //定義所有坐標的長度,表示一共8個皇后
    private static final int max = 8;
    //確定一個數組,表示8個皇后在每行的第幾個位置
    private int[] arr = new int[max];
    //統計一共有多少個解
    public static int count = 0;

    public void Check(int n) {
        //判斷是否到達最后一個皇后,如果是就將該數組輸出
        if (n >= max) {
            Print();
            return;
        }
        //如果不是,就將該皇后從該列的的第一個位置開始放,直到找到適合它的位置
        for (int i = 0; i < max; i++) {
                arr[n] = i;//將該皇后放在該列上
            if (Judge(n)) {//判斷該皇后的位置是否正確,如果返回結果是正確的,那就繼續放下一個皇后
                Check(n + 1);
            }
        }
    }

    //判斷該皇后放置的位置是否滿足八皇后的要求
    public boolean Judge(int n) {
        /**
         * 1、arr[i] == arr[n]判斷該皇后的位置是否和她前面的皇后的位置是在同一列,在就返回false
         * 2、Math.abs(i - n) == Math.abs(arr[i] - arr[n])判斷該皇后的對角線上(斜線)有沒有皇后,有就返回false
         * 3、如果返回結果為true就表示該位置可以放該皇后,不用判斷不同皇后是否在同一行
         * 因為每一行都只安排了一個皇后放置
         */
        for (int i = 0; i < n; i++) {
            if (arr[i] == arr[n] || Math.abs(i - n) == Math.abs(arr[i] - arr[n])) {
                return false;
            }
        }
        return true;
    }

    //輸出一個完整的可以完成8皇后的數組
    public void Print() {
        count++;//進行輸出操作,說明就有一個數組滿足8皇后的要求,對統計的和加一
        for (int i = 0; i < max; i++) {
            System.out.printf("%d ", arr[i]);
        }
        System.out.println();//每一個數組輸出后都進行換行操作
    }

}

四、排序算法

1、排序也稱排序算法
(Sort Algorithm),排序是將一組數據,依指定的順序進行排列的過程。
排序的分類:

1)內部排序:
指將需要處理的所有數據都加載到內部存儲器中進行排序。
2)外部排序法:
數據量過大,無法全部加載到內存中,需要借助外部存儲進行排序。

3)常見的排序算法

 

 2、算法的時間復雜度(度量一個程序算法執行時間的兩種方法)

1)事后統計的方法
這種方法可行,但是有兩個問題:一是要想對設計的算法的運行性能進行評測,需要實際運行該程序;二是所得時間的統計量依賴於計算機的硬件、軟件等環境因素,這種方式,要在同一台計算機的相同狀態下運行,才能比較那個算法速度更快。
2)事前估算的方法
通過分析某個算法的時間復雜度來判斷哪個算法更優.

時間頻度:

基本介紹:時間頻度:一個算法花費的時間與算法中語句的執行次數成正比例,哪個算法中語句執行次數多,它花費時間就多。一個算法中的語句執行次數稱為語句頻度或時間頻度。記為T(n)。

 

 

 

 

 

 時間復雜度說明;

1)一般情況下,算法中的基本操作語句的重復執行次數是問題規模n的某個函
數,用T(n)表示,若有某個輔助函數f(n),使得當n趨近於無窮大時,T(n)/ f(n)的極限值為不等於零的常數,則稱f(n)是T(n)的同數量級函數。記作T(n)=o( f(n)),稱O( f(n))為算法的漸進時間復雜度,簡稱時間復雜度。
2) T(n)不同,但時間復雜度可能相同。如: T(n)=n2+7nt6與T(n)=3n2+2n+2它
們的T(n)不同,但時間復雜度相同,都為O(n2)。
3)計算時間復雜度的方法:
(1)用常數1代替運行時間中的所有加法常數

(2)修改后的運行次數函數中,只保留最高階項

(3)去除最高階項的系數

常見的時間復雜度:實際應用中應該盡可能避免使用指數階的算法

 

 (1)常數階O(1)

 

 (2)對數階O(log2n):其中2可以是任意的整數

 

 (3)線性階O(n)

 

 (4)線性對數階O(nlogN)

 

 (5)平方階O(n2)

 

 (6)立方階O(n3)、K次方階O(nk):相當於上面的n次循環

平均時間復雜度和最壞時間復雜度

 

 算法的空間復雜度:

基本介紹:

1))類似於時間復雜度的討論,一個算法的空間復雜度(Space Complexity)定義為該
算法所耗費的存儲空間,它也是問題規模n的函數。
2)空間復雜度(Space Complexity)是對一個算法在運行過程中臨時占用存儲空間大
小的量度。有的算法需要占用的臨時工作單元數與解決問題的規模n有關,它隨着n的增大而增大,當n較大時,將占用較多的存儲單元,例如快速排序和歸並排序算法就屬於這種情況
3)在做算法分析時,主要討論的是時間復雜度。從用戶使用體驗上看,更看重的程序執行的速度。一些緩存產品(redis, memcache)和算法(基數排序)本質就是用
空間換時間.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM