python實現通過指定瀏覽器免費觀看vip視訊
一、棧的介紹
●棧的英文為(stack)。出棧(pop),入棧(push)。
●棧是一個先入後出(FILO-First In Last Out)的有序列表。
●棧(stack)是限制線性表中元素的插入和刪除只能在線性表的同一端進行的一種特殊線性表。允許插入和刪除的一端,為變化的一端,稱為棧項(Top),另一端為固定的一端,稱為棧底(Bottom)。
●根據棧的定義可知,最先放入棧中元素在棧底,最後放入的元素在棧頂,而刪除元素剛好相反,最後放入的元素最先刪除,最先放入的元素最後刪除
二、棧的應用場景
➢子程式的呼叫:在跳往子程式前,會先將下個指令的地址存到堆疊中,直到子程式執行完後再將地址取出,以回到原來的程式中。
➢處理遞迴呼叫:和子程式的呼叫類似,只是除了儲存下一個指令的地址外,也將引數、區域變數等資料存入堆疊中。
➢表示式的轉換【中綴表示式轉字尾表示式】與求值(經典應用)。
➢二叉樹的遍歷。
➢圖形的深度優先(depth- first)搜尋法 。
三、棧的快速入門
1.陣列模擬棧的出棧和入棧操作
package com.xudong.DataStructures; import java.util.Scanner; public class ArrayStackDemo { 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("----------------"); System.out.println("show: 顯示棧"); System.out.println("exit: 退出"); System.out.println("push: 入棧"); System.out.println("pop: 出棧"); System.out.println("----------------"); 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",res); } catch (Exception e) { System.out.println(e.getMessage()); } break; case "exit": scanner.close(); loop = false; break; default: break; } System.out.println("程式退出!"); } } } //定義一個 ArrayStack 表示棧 class ArrayStack{ private int maxSize;// 棧的大小 private int[] stack;// 陣列模擬棧 private int top = -1;// top表示棧頂 //構造器 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; } //入棧 push public void push(int value){ //判斷是否棧滿 if (isFull()){ System.out.println("棧滿!"); return; } top++; stack[top] = value; } //出棧 pop 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]); } } }
2.棧實現綜合計算器(中綴表示式)
package com.xudong.DataStructures; public class CalculatorDemo { public static void main(String[] args) { String expression = "35*6+64/8-4-5+6*8"; //建立數棧和符號棧 ArrayStack2 numStack = new ArrayStack2(10); ArrayStack2 operStack = new ArrayStack2(10); //定義相關變數 int index = 0;//用於掃描 int num1 = 0; int num2 = 0; int oper = 0; int res = 0; char ch = ' ';//將每次掃描得到的char儲存到ch String keepNum = "";//用於拼接多位數 //開始while迴圈的掃描 expression while (true){ //依次得到expression 的每一個字元 ch = expression.substring(index,index + 1).charAt(0); //判斷ch是什麼,再做相應處理 if (operStack.isOper(ch)){//如果是運算子 if (!operStack.isEmpty()){ //如果符號棧有操作符,就進行比較。如果當前的操作符的優先順序小於或者等於棧中的操作符,就需要從數棧中pop出兩個數。 //在從符號棧中pop出一個符號,進行運算,將得到結果,入數棧,然後將當前的操作符入符號棧 if (operStack.priority(ch) <= operStack.priority(operStack.peek())){ num1 = numStack.pop(); num2 = numStack.pop(); oper = operStack.pop(); res = numStack.cal(num1,num2,oper); //把運算結果入數棧 numStack.push(res); //將當前操作符入符號棧 operStack.push(ch); }else { //如果當前的操作符的優先順序大於棧中的操作符,就直接入符號棧。 operStack.push(ch); } }else { //符號棧如果為空,直接入符號棧 operStack.push(ch); } }else { //處理多位數 keepNum += ch; //如果ch已經是expression最後一位,就直接入棧 if (index == expression.length() - 1){ numStack.push(Integer.parseInt(keepNum)); }else { //向expression的表示式的index後再看一位,如果是數繼續掃描,如果是符號則入棧 if (operStack.isOper(expression.substring(index + 1,index + 2).charAt(0))){ //如果後一位是運算子,則入棧 numStack.push(Integer.parseInt(keepNum)); //清空keepNum keepNum = ""; } } } //讓index + 1,並判斷是否到expression最後 index++; if (index >= expression.length()){ break; } } //當表示式掃描完畢,就順序的從數棧和符號棧pop出相應的數和符號,並執行 while (true){ //如果符號棧為空,則計算到最後的結果,數棧中只有一個數字【結果】 if (operStack.isEmpty()){ break; } num1 = numStack.pop(); num2 = numStack.pop(); oper = operStack.pop(); res = numStack.cal(num1,num2,oper); //把運算結果入數棧 numStack.push(res); } System.out.printf("表示式:%s = %d",expression,numStack.pop()); } } //建立一個棧 class ArrayStack2{ private int maxSize;// 棧的大小 private int[] stack;// 陣列模擬棧 private int top = -1;// top表示棧頂 //構造器 public ArrayStack2(int maxSize) { this.maxSize = maxSize; stack = new int[this.maxSize]; } //檢視棧頂 public int peek(){ return stack[top]; } //棧滿 public boolean isFull(){ return top == maxSize - 1; } //棧空 public boolean isEmpty(){ return top == -1; } //入棧 push public void push(int value){ //判斷是否棧滿 if (isFull()){ System.out.println("棧滿!"); return; } top++; stack[top] = value; } //出棧 pop 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]); } } //返回運算子的優先順序,優先順序用數字表示,數字越大,優先順序越高 public int priority(int oper){ if (oper == '*' || oper == '/'){ return 1; }else if (oper == '+' || oper == '-'){ return 0; }else { return -1;//假定目前只有加減乘除 } } //判斷是不是一個運算子 public boolean isOper(char val){ return val == '+' || val == '-' || val == '*' || val =='/'; } //計算方法 public int cal(int num1,int num2,int oper){ int res = 0;// res 用於存放計算的結果 switch (oper){ case '+': res = num1 + num2; break; case '-': res = num2 + num1; break; case '*': res = num1 * num2; break; case '/': res = num2 / num1; break; default: break; } return res; } }
字首表示式
字首表示式的計算機求值
●從右至左掃描表示式,遇到數字時,將數字壓入堆疊,遇到運算子時,彈出棧頂的兩個數,用運算子對它們做相應的計算(棧頂元素和次頂元素),並將結果入棧;重複上述過程直到表示式最左端,最後運算得出的值即為表示式的結果
例如: 【(3+4)X5-6】對應的字首表示式就是【- X + 3 4 5 6】,針對字首表示式求值步驟如下:
➢從右至左掃描,將6、5、4、3壓入堆疊
➢遇到【+】運算子,因此彈出3和4 (3為棧項元素,4為次頂元素),計算出3+4的值,得7,再將7入棧
➢接下來是【X】運算子,因此彈出7和5, 計算出7X5=35, 將35入棧
➢最後是【-】運算子,計算出35-6的值,即29,由此得出最終結果
中綴表示式
➢中綴表示式就是常見的運算表示式,如【(3+4)X5-6】
➢中綴表示式的求值是我們人最熟悉的,但是對計算機來說卻不好操作(前面我們講的案例就能看的這個問題),因此,在計算結果時,往往會將中綴表示式轉成其它表示式來操作,一般轉成字尾表示式.
字尾表示式(逆波蘭表示式)
➢字尾表示式又稱逆波蘭表示式,與字首表示式相似,只是運算子位於運算元之後
➢中舉例說明: 【(3+4)X5-6】 對應的字尾表示式就是【3 4 + 5 X 6-】
字尾表示式的計算機求值
●從左至右掃描表示式,遇到數字時,將數字壓入堆疊,遇到運算子時,彈出棧項的兩個數,用運算子對它們做相應的計算(次頂元素和棧項元素),並將結果入棧;重複上述過程直到表示式最右端,最後運算得出的值即為表示式的結果
例如: 【(3+4)X5-6】對應的字尾表示式就是【3 4 + 5 X 6 -】,針對字尾表示式求值步驟如下:
➢從左至右掃描,將3和4壓入堆疊;
➢遇到+運算子,因此彈出4和3 (4為棧項元素,3為次頂元素),計算出3+4的值,得7,再將7入棧;
➢將5入棧;
➢接下來是X運算子,因此彈出5和7,計算出7X5=35,將35入棧;
➢將6入棧;
➢最後是-運算子,計算出35-6的值,即29, 由此得出最終結果
中綴表示式轉換為字尾表示式
●可以看到,字尾表示式適合計算式進行運算,但是卻不太容易寫出來,尤其是表示式很長的情況下,因此在開發中,我們需要將中綴表示式轉成字尾表示式。
具體步驟如下:
➢初始化兩個棧:運算子棧s1和儲存中間結果的棧s2;
➢從左至右掃描中綴表示式;
➢遇到運算元時,將其壓s2;
➢遇到運算子時,比較其與s1棧頂運算子的優先順序:(1)如果s1為空,或棧頂運算子為左括號“(”, 則直接將此運算子入棧;
(2)否則,若優先順序比棧項運算子的高,也將運算子壓入s1;
(3)否則,將s1棧頂的運算子彈出並壓入到s2中,再次轉到(1)與s1中新的棧頂運算子相比較;
➢遇到括號時:
(1)如果是左括號“(”,則直接壓入s1
(2)如果是右括號“)”,則依次彈出s1棧頂的運算子,並壓入s2,直到遇到左括號為止,此時將這一對括號丟棄
➢重複步驟2至5,直到表示式的最右邊
➢將s1中剩餘的運算子依次彈出並壓入s2
➢依次彈出s2中的元素並輸出,結果的逆序即為中綴表示式對應的字尾表示式
package com.xudong.DataStructures;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class PolandNotationDemo {
public static void main(String[] args) {
//先定義 逆波蘭表示式
//【(30+4)X5-6】 => 【30 4 + 5 X 6 -】
String suffixExpression = "30 4 + 5 * 6 -";
List<String> rpnList = getListString(suffixExpression);
System.out.println("rpnList = " + rpnList);
int res = calculate(rpnList);
System.out.println("計算結果:" + res);
System.out.println("---------------------");
String expression = "11+((2+3)*42)-5";
List<String> infixExpressionList = toInfixExpressionList(expression);
System.out.println("中綴表示式對應的List = " + infixExpressionList);
List<String> suffixExpressionList = parseSuffixExpressionList(infixExpressionList);
System.out.println("中綴表示式對應的List = " + suffixExpressionList);
//計算結果
System.out.printf("expression = %d",calculate(suffixExpressionList));
}
//將中綴表示式轉成對應的List
public static List<String> toInfixExpressionList(String s){
//定義List,存放中綴表示式
List<String> ls = new ArrayList<>();
int i = 0;//指標,遍歷中綴表示式字串
String str;
char c;
do {
//如果c是非數字,則加入ls
if ((c = s.charAt(i)) < 48 || (c = s.charAt(i)) > 57){
ls.add("" + c);
i++;//i需要後移
}else {//如果是一個數,考慮多位數
str = "";
while (i < s.length() && (c = s.charAt(i)) >= 48 && (c = s.charAt(i)) <= 57){
str += c;//拼接
i++;
}
ls.add(str);
}
}while (i < s.length());
return ls;//返回
}
//將得到的中綴表示式對應的List => 字尾表示式對應的List
public static List<String> parseSuffixExpressionList(List<String> ls){
//定義一個棧和一個動態陣列
Stack<String> s1 = new Stack<>();//符號棧
List<String> s2 = new ArrayList<>();//儲存中間結果的陣列列表 s2
//遍歷ls
for(String item : ls){
//如果是一個數,加入s2
if (item.matches("\\d+")){
s2.add(item);
}else if (item.equals("(")){
s1.push(item);
}else if (item.equals(")")){
//如果是右括號“)”,則依次彈出s1棧頂的運算子,並壓入s2,直到遇到左括號為止,此時將這一對括號丟棄
while (!s1.peek().equals("(")){
s2.add(s1.pop());
}
s1.pop();//將 ( 彈出s1棧,消除小括號
}else {
//當item的優先順序小於等於s1棧頂運算子,將s1棧頂的運算子彈出並壓入到s2中,
// 再次轉到(1)與s1中新的棧頂運算子相比較
while (s1.size() != 0 && Operation.getValue(s1.peek()) >= Operation.getValue(item)){
s2.add(s1.pop());
}
//再將item壓入棧
s1.push(item);
}
}
//將s1中剩餘的運算子依次彈出並加入s2
while (s1.size() != 0){
s2.add(s1.pop());
}
return s2;
}
//將表示式的資料和運算子放到 ArrayList
public static List<String> getListString(String suffixExpression){
String[] split = suffixExpression.split(" ");
List<String> list = new ArrayList<>();
for (String ele : split){
list.add(ele);
}
return list;
}
//逆波蘭表示式運算
public static int calculate(List<String> ls){
//建立一個棧
Stack<String> stack = new Stack<>();
//遍歷ls
for (String item : ls){
if (item.matches("\\d+")){//匹配多位數
//入棧
stack.push(item);
}else {
//pop出兩個數再入棧
int num1 = Integer.parseInt(stack.pop());
int num2 = Integer.parseInt(stack.pop());
int res = 0;
if (item.equals("+")){
res = num1 + num2;
}else if (item.equals("-")){
res = num2 - num1;
}else if (item.equals("*")){
res = num1 * num2;
}else if (item.equals("/")){
res = num2 / num1;
}else {
throw new RuntimeException("運算子有誤!");
}
//將res入棧
stack.push(res + "");
}
}
//最後留在stack中的資料就是運算結果
return Integer.parseInt(stack.pop());
}
}
//返回運算子對應優先順序
class Operation{
private static int ADD = 1;
private static int SUM = 1;
private static int MUL = 2;
private static int DIV = 2;
public static int getValue(String operation){
int result = 0;
switch (operation){
case "+":
result = ADD;
break;
case "-":
result = SUM;
break;
case "*":
result = MUL;
break;
case "/":
result = DIV;
break;
default:
System.out.print("");
break;
}
return result;
}
}