一、棧的介紹:
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)和算法(基數排序)本質就是用
空間換時間.
