請輸入一個表達式
計算 7*2*2-5+1-5+3-3
請問: 計算機底層是如何運算得到結果的?
對計算機而言,它接收到的就是一個字串,我們需要將之解析為運算元、運算子,還得考慮優先權。
Learn More →
top++; stack[top] = data;
stack[top];top--, return value
import java.util.Scanner;
public class ArrayStackDemo {
public static void main(String[] args) {
ArrayStack arrStack = new ArrayStack(4);
String key = "";
boolean loop = true;
Scanner sc = new Scanner(System.in);
while(loop) {
System.out.println("s(show): 顯示棧 | a(add): 添加數據到棧中 | d(del): 將數據從棧中移出 | e(exit): 退出程序");
key = sc.next();
switch (key) {
case "s":
arrStack.list();
break;
case "e":
sc.close();
loop = false;
break;
case "a":
System.out.println("請輸入要添加的數據: ");
int data = sc.nextInt();
arrStack.push(data);
break;
case "d":
try {
int res = arrStack.pop();
System.out.printf("出棧的數據為 %d\n",res);
break;
} catch (Exception e) {
System.out.println(e.getMessage());
}
default:
break;
}
}
System.out.println("程序退出!");
}
}
//定義一個類表示 棧
class ArrayStack {
private int maxSize; //棧的大小
private int[] stack; //陣列,模擬棧
private int top = -1; //棧頂,初始化為-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]);
}
}
}
output:
s(show): 顯示棧 | a(add): 添加數據到棧中 | d(del): 將數據從棧中移出 | e(exit): 退出程序
a
請輸入要添加的數據:
10
s(show): 顯示棧 | a(add): 添加數據到棧中 | d(del): 將數據從棧中移出 | e(exit): 退出程序
a
請輸入要添加的數據:
20
s(show): 顯示棧 | a(add): 添加數據到棧中 | d(del): 將數據從棧中移出 | e(exit): 退出程序
a
請輸入要添加的數據:
30
s(show): 顯示棧 | a(add): 添加數據到棧中 | d(del): 將數據從棧中移出 | e(exit): 退出程序
a
請輸入要添加的數據:
40
s(show): 顯示棧 | a(add): 添加數據到棧中 | d(del): 將數據從棧中移出 | e(exit): 退出程序
a
請輸入要添加的數據:
50
棧滿
s(show): 顯示棧 | a(add): 添加數據到棧中 | d(del): 將數據從棧中移出 | e(exit): 退出程序
s
stack[3] = 40
stack[2] = 30
stack[1] = 20
stack[0] = 10
s(show): 顯示棧 | a(add): 添加數據到棧中 | d(del): 將數據從棧中移出 | e(exit): 退出程序
d
出棧的數據為 40
s(show): 顯示棧 | a(add): 添加數據到棧中 | d(del): 將數據從棧中移出 | e(exit): 退出程序
d
出棧的數據為 30
s(show): 顯示棧 | a(add): 添加數據到棧中 | d(del): 將數據從棧中移出 | e(exit): 退出程序
d
出棧的數據為 20
s(show): 顯示棧 | a(add): 添加數據到棧中 | d(del): 將數據從棧中移出 | e(exit): 退出程序
d
出棧的數據為 10
s(show): 顯示棧 | a(add): 添加數據到棧中 | d(del): 將數據從棧中移出 | e(exit): 退出程序
d
棧空
s(show): 顯示棧 | a(add): 添加數據到棧中 | d(del): 將數據從棧中移出 | e(exit): 退出程序
s
棧空,沒有數據
s(show): 顯示棧 | a(add): 添加數據到棧中 | d(del): 將數據從棧中移出 | e(exit): 退出程序
e
程序退出!
7*2*2-5+1-5+3-1 = ?
實際入棧順序演示請看視頻 EP33。
以 3+2*6-2
來做示範,正常來說我們的運算順序是3+(2*6)-2,所以結果為 13
那麼在棧中該如何去計算?
計算時,需先pop出兩個數再pop出一個運算子,因此會先將2,12
pop出數棧,再將 -
pop出符號棧做運算,得出 12-2 = 10
,將 10
push進數棧中。
目前數棧中剩下 10,3
而符號棧中剩下 +
。
再將10,3
pop 出數棧,將 +
pop出符號棧做運算,將10+3 = 13
,把 13
push進數棧中,現在符號棧為空,數棧僅剩一個數,因此結果為 13
。
請將圖片放大觀看
棧的個位數中綴表示法表達式計算程式碼如下:
public class Calculator {
public static void main(String[] args) {
String expression = "3+2*6-2";
ArrayStack2 numStack = new ArrayStack2(10);
ArrayStack2 operStack = new ArrayStack2(10);
//定義需要的相關變數
int index = 0; //用於遍歷
int num1 = 0,num2 = 0;
int oper = 0;
int res = 0;
char ch = ' '; //將每次遍歷得到的char保存到ch
//開始while循環遍歷expression
while(true) {
//依次得到expression的每個字符
ch = expression.substring(index, index+1).charAt(0);
//若為運算子
if(operStack.isOper(ch)) {
//判斷當前符號棧是否為空
if(!operStack.isEmpty()){
//處理當前運算子優先級小於等於棧中運算子優先級
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 { //如果是數,直接入棧
numStack.push(ch - 48); //ch是字元 要轉為數字 請對照ASCII表
}
//讓 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); //入棧
}
//將數棧最後的數pop出來,就是結果
System.out.printf("表達式 %s = %d",expression, numStack.pop());
}
}
class ArrayStack2 {
private int maxSize; //棧的大小
private int[] stack; //陣列,模擬棧
private int top = -1; //棧頂,初始化為-1
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;
}
//入棧
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]);
}
}
//返回運算子的優先級,優先級是programmer來定的
//優先級使用數字表示,數字越大,則優先級越高
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; //用於存放計算結果
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;
}
}
output
表達式 3+2*6-2 = 13
但是,這個code問題還是滿多的,比如最明顯的一點,它只能做個位數的+ - * /
現在,我們來完善它,使之能計算多位數。
思路
String keepNum = ""; //用於拼接多位數
把本來用於遍歷expression的while循環改良一下,27行是原先的程式碼,改善成28~40行:
//開始while循環遍歷expression
while(true) {
//依次得到expression的每個字符
ch = expression.substring(index, index+1).charAt(0);
//若為運算子
if(operStack.isOper(ch)) {
//判斷當前符號棧是否為空
if(!operStack.isEmpty()){
//處理當前運算子優先級小於等於棧中運算子優先級
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 { //如果是數,直接入棧
// numStack.push(ch - 48); //ch是字元 要轉為數字 請對照ASCII表
// 處理多位數
keepNum += ch;
//如果ch已經是expression的最後一位,就直接入棧
if(index == expression.length() - 1) {
numStack.push(Integer.parseInt(keepNum));
} else {
//判斷下一個字符是否為數字
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;
}
}
output
表達式 5+6*2-3 = 14
表達式 50+6*2-3 = 59
表達式 500+6*2-3 = 509
我們平時常看的運算式就是使用中綴表式法,運算子在數中間:
(3 + 4) * 5 - 6
也稱作波蘭表示法(Polish notation),特點是將運算子放在數字前面:
- * + 3 4 5 6
前綴表示法的計算機求值
例如:(3+4)*5-6
對應的前綴表示法是 -*+3456
,針對前綴表示法求值步驟如下:
也稱作逆波蘭表示法(Reverse Polish notation),特點是將運算子放在數字後面:
3 4 + 5 * 6 -
後綴表示法的計算機求值
例如:(3+4)*5-6
對應的後綴表示法是 34+5*6-
,針對後綴表示法求值步驟如下:
對人來說,中綴表示法很直觀的表現出要計算的內容,但對於計算機來說,中綴表示法並不好操作,因為需要去判斷遇到的運算子優先級和後面的運算子優先級誰高誰低。
就像我們前面實作的例子,因此,在計算結果時往往會將中綴表示法轉為其他表示法操作(一般轉成後綴表示法)。
需求
首先我們先模擬出一個【個位數(整數)後綴表示法計算機】:
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class PolishNotation {
public static void main(String[] args) {
//(3+4)*5-6 => 34+5*6-
String suffixExpression = "3 4 + 5 * 6 -" ;
//思路
//1. 先將"3 4 + 5 * 6 -" => 放到ArrayList中
//2. 將ArrayList傳遞給一個方法,遍歷ArrayList配合棧完成計算
List<String> rpnList = getListString(suffixExpression);
System.out.println("rpnList= " + rpnList);
int res = calculate(rpnList);
System.out.println("計算的結果 = " + res);
}
//將一個後綴表示法,依次將數字和運算子放入到ArrayList中
public static List<String> getListString(String suffixExpression) {
String[] split = suffixExpression.split(" ");
List<String> list = new ArrayList<String>();
for (String ele : split) {
list.add(ele);
}
return list;
}
//完成對後綴表示法的運算
public static int calculate(List<String> ls) {
//創建棧
Stack<String> stack = new Stack<String>();
//遍歷 ls
for (String item : ls) {
//使用正則取出數
if(item.matches("\\d+")) { // +:匹配多位數
//入棧
stack.push(item);
} else {
int num2 = Integer.parseInt(stack.pop());
int num1 = Integer.parseInt(stack.pop());
int res = 0;
if(item.equals("+")) {
res = num1 + num2;
} else if(item.equals("-")) {
res = num1 - num2;
} else if(item.equals("*")) {
res = num1 * num2;
} else if(item.equals("/")) {
res = num1 / num2;
} else {
throw new RuntimeException("運算子有誤");
}
// 把res入棧
stack.push(res + "");
}
}
//最後留在stack中的數據為運算結果
return Integer.parseInt(stack.pop());
}
}
output:
rpnList= [3, 4, +, 5, *, 6, -]
計算的結果 = 29
這個code的缺點是什麼呢?
無法完成我們所要達成的目標,所以我們現在來進行改善。
後綴表示法適合計算機去運算,但是人卻不太容易能夠直接寫出來,尤其是在表達式很長的情況下。因此在開發中,我們需要將中綴表達式轉為後綴表達式。
!!!(建議先看下面轉換過程圖例示範後再來看具體步驟如何實現。)!!!
(
",則直接將此運算子push進 s1 中(
",則直接push進s1)
",則依次pop s1 棧頂的運算子,並push進 s2,直到遇到左括號為止,此時將這一對括號丟棄光是看到八個步驟和密密麻麻的字大部分人都被勸退了吧?
其實並沒有那麼複雜,接著我會一步一步帶大家理解。
舉例說明:將中綴表達式"1+((2+3)*4)-5
"轉換為後綴表達式
結果為:"1 2 3 + 4 * + 5 -
"
1+((2+3
都沒有什麼問題,直接按順序push進各自的棧中即可。)
怎麼辦?* 4
push進各自的棧中)
-
,現在s1棧中僅有 +
,-
並沒有比 +
優先級高,因此將s1棧頂pop出並push到s2,再把 - 5
push至各自的棧中。即 -5+*4+321
逆序為 123+4*+5-
。
現在,我們將按步驟解釋程式碼的意義,最後會附上完整程式碼。
首先,因為直接對string做操作並不方便,所以我們需要寫一個方法將表達式字串轉為list儲存:
//1. 1+((2+3)*4)-5 => 123+4*+5-
//2. 直接對str進行操作並不方便,因此先將字串轉成一個中綴表示法的list
//3. 即 1+((2+3)*4)-5 => ArrayList [1,+,(,(,2,+,3,),*,4,),-,5]
String expression = "1+((2+3)*4)-5";
List<String> infixExpressionList = toInfixExpressionList(expression);
System.out.println(infixExpressionList);
//方法:將中綴表達式轉為對應的list
public static List<String> toInfixExpressionList(String s) {
//定義一個list,存放中綴表達式
List<String> ls = new ArrayList<String>();
int i = 0; //用於遍歷expression
String str; //對多位數的拼接
char c; //每遍歷一個字符就放入到c
do{
//如果 c 非數字, 需要加入到ls
if((c = s.charAt(i)) < 48 || (c = s.charAt(i)) > 57) {
ls.add("" + c);
i++;
} else {
str = ""; //將 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;
}
output
[1, +, (, (, 2, +, 3, ), *, 4, ), -, 5]
現在,我們需要將得到的中綴表達式對應的List 轉為 後綴表達式對應的List
//4. 將得到的中綴表達式對應的List => 後綴表達式對應的List
//即 [1,+,(,(,2,+,3,),*,4,),-,5] => [1,2,3,+,4,*,+,5,-] , 小括號被消除掉了
List<String> suffixExpressionList = parseSuffixExpressionList(infixExpressionList);
System.out.println(suffixExpressionList); //[1,2,3,+,4,*,+,5,-]
新建一個判斷運算子對應優先級的類
//返回一個運算子對應的優先級
class Operation {
private static int ADD = 1;
private static int SUB = 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 = SUB;
break;
case "*":
result = MUL;
break;
case "/":
result = DIV;
break;
default:
break;
}
return result;
}
}
//方法:將得到的中綴表達式對應的list => 後綴表達式的list
public static List<String> parseSuffixExpressionList(List<String> ls) {
//定義數棧和運算子棧
Stack<String> s1 = new Stack<String>(); //運算子棧
//由於s2這個棧在整個過程中沒有push & pop 並且最後我們還需要逆序 因此不使用stack , 直接使用list
//Stack<String> s2 = new Stack<String>();
List<String> s2 = new ArrayList<String>(); //儲存中間結果
for (String item : ls) {
//數, 加入s2
if(item.matches("\\d+")) {
s2.add(item);
} else if(item.equals("(")) { //左括號
s1.push(item);
} else if(item.equals(")")) { //右括號
while(!s1.peek().equals("(")) { //遇到左括號前將s1 pop到s2
s2.add(s1.pop());
}
s1.pop(); //將左括號消除
} else {
//當item的優先級小於等於s1棧頂運算子
while(s1.size() != 0 && (Operation.getValue(s1.peek()) >= Operation.getValue(item))) {
s2.add(s1.pop());
}
//還需要將item push入棧
s1.push(item);
}
}
//將s1中剩餘的運算子依次pop出並加入s2
while(s1.size() != 0) {
s2.add(s1.pop());
}
return s2; //因為是存在list中, 所以按順序輸出就是對應的後綴表達式
}
output
[1, +, (, (, 2, +, 3, ), *, 4, ), -, 5]
[1, 2, 3, +, 4, *, +, 5, -]
再用前面所寫的後綴表達式計算方法計算出結果
//完成對後綴表示法的運算
public static int calculate(List<String> ls) {
//創建棧
Stack<String> stack = new Stack<String>();
//遍歷 ls
for (String item : ls) {
//使用正則取出數
if(item.matches("\\d+")) { // +:匹配多位數
//入棧
stack.push(item);
} else {
int num2 = Integer.parseInt(stack.pop());
int num1 = Integer.parseInt(stack.pop());
int res = 0;
if(item.equals("+")) {
res = num1 + num2;
} else if(item.equals("-")) {
res = num1 - num2;
} else if(item.equals("*")) {
res = num1 * num2;
} else if(item.equals("/")) {
res = num1 / num2;
} else {
throw new RuntimeException("運算子有誤");
}
// 把res入棧
stack.push(res + "");
}
}
//最後留在stack中的數據為運算結果
return Integer.parseInt(stack.pop());
}
System.out.printf("expression = %d",calculate(suffixExpressionList));
output
[1, 2, 3, +, 4, *, +, 5, -]
expression = 16
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class PolishNotation {
public static void main(String[] args) {
//1. 1+((2+3)*4)-5 => 123+4*+5-
//2. 直接對str進行操作並不方便,因此先將字串轉成一個中綴表示法的list
//3. 即 1+((2+3)*4)-5 => ArrayList [1,+,(,(,2,+,3,),*,4,),-,5]
String expression = "1+((2+3)*4)-5";
List<String> infixExpressionList = toInfixExpressionList(expression);
System.out.println(infixExpressionList); //[1,+,(,(,2,+,3,),*,4,),-,5]
//4. 將得到的中綴表達式對應的List => 後綴表達式對應的List
//即 [1,+,(,(,2,+,3,),*,4,),-,5] => [1,2,3,+,4,*,+,5,-] , 小括號被消除掉了
List<String> suffixExpressionList = parseSuffixExpressionList(infixExpressionList);
System.out.println(suffixExpressionList); //[1,2,3,+,4,*,+,5,-]
//計算後綴表達式結果
System.out.printf("expression = %d",calculate(suffixExpressionList));
}
//方法:將得到的中綴表達式對應的list => 後綴表達式的list
public static List<String> parseSuffixExpressionList(List<String> ls) {
//定義數棧和運算子棧
Stack<String> s1 = new Stack<String>(); //運算子棧
//由於s2這個棧在整個過程中沒有push & pop 並且最後我們還需要逆序 因此不使用stack , 直接使用list
//Stack<String> s2 = new Stack<String>();
List<String> s2 = new ArrayList<String>(); //儲存中間結果
for (String item : ls) {
//數, 加入s2
if(item.matches("\\d+")) {
s2.add(item);
} else if(item.equals("(")) { //左括號
s1.push(item);
} else if(item.equals(")")) { //右括號
while(!s1.peek().equals("(")) { //遇到左括號前將s1 pop到s2
s2.add(s1.pop());
}
s1.pop(); //將左括號消除
} else {
//當item的優先級小於等於s1棧頂運算子
while(s1.size() != 0 && (Operation.getValue(s1.peek()) >= Operation.getValue(item))) {
s2.add(s1.pop());
}
//還需要將item push入棧
s1.push(item);
}
}
//將s1中剩餘的運算子依次pop出並加入s2
while(s1.size() != 0) {
s2.add(s1.pop());
}
return s2; //因為是存在list中, 所以按順序輸出就是對應的後綴表達式
}
//方法:將中綴表達式轉為對應的list
public static List<String> toInfixExpressionList(String s) {
//定義一個list,存放中綴表達式
List<String> ls = new ArrayList<String>();
int i = 0; //用於遍歷expression
String str; //對多位數的拼接
char c; //每遍歷一個字符就放入到c
do{
//如果 c 非數字, 需要加入到ls
if((c = s.charAt(i)) < 48 || (c = s.charAt(i)) > 57) {
ls.add("" + c);
i++;
} else {
str = ""; //將 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;
}
//完成對後綴表示法的運算
public static int calculate(List<String> ls) {
//創建棧
Stack<String> stack = new Stack<String>();
//遍歷 ls
for (String item : ls) {
//使用正則取出數
if(item.matches("\\d+")) { // +:匹配多位數
//入棧
stack.push(item);
} else {
int num2 = Integer.parseInt(stack.pop());
int num1 = Integer.parseInt(stack.pop());
int res = 0;
if(item.equals("+")) {
res = num1 + num2;
} else if(item.equals("-")) {
res = num1 - num2;
} else if(item.equals("*")) {
res = num1 * num2;
} else if(item.equals("/")) {
res = num1 / num2;
} else {
throw new RuntimeException("運算子有誤");
}
// 把res入棧
stack.push(res + "");
}
}
//最後留在stack中的數據為運算結果
return Integer.parseInt(stack.pop());
}
}
//返回一個運算子對應的優先級
class Operation {
private static int ADD = 1;
private static int SUB = 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 = SUB;
break;
case "*":
result = MUL;
break;
case "/":
result = DIV;
break;
default:
System.out.println("運算子有誤");
break;
}
return result;
}
}
補充:
表達式一下有空格一下沒空格怎麼處理?
/**
* 去除所有空白符
* @param s
* @return
*/
public static String replaceAllBlank(String s ){
// \\s+ 匹配任何空白字符,包括空格、制表符、换页符等等, 等价于[ \f\n\r\t\v]
return s.replaceAll("\\s+","");
}
想使用浮點數、整數混在一起計算怎麼辦?
/**
* 判断是不是数字 int double long float
* @param s
* @return
*/
public static boolean isNumber(String s){
Pattern pattern = Pattern.compile("^[-\\+]?[.\\d]*$");
return pattern.matcher(s).matches();
}
這部分大家自己看著改吧。
使用場景: 排隊買票 特徵 先進先出(FIFO) 有序列表,可以用陣列或連結串列來實現。 Queue兩個指針:rear 佇列的尾部(含)、front 佇列的頭部(不含)。 新增數據時,front不動rear動;刪除數據時,rear不動front動 => 由尾端加入數據,由頭部取出。 我們可以以排隊買票為例,將排隊的隊列視作Queue,先排隊的人先買票,後到的人排在後面等待買票,並且先買票的人買完票之後就離開隊伍;我們將第一個買票的人稱之為front,隊伍最後一個等待買票的人為rear。
Aug 29, 2024xhr 事件監聽 progress 可以獲取 loaded 和 total 兩個返回值,loaded 代表當前請求的 byte 數,total 則是總共的 byte 數。
Nov 4, 2023Search signals 為 Google 搜尋排名的考量指標之一,其中包含 Core Web Vitals 和 Mobile Friendly, HTTPS, No Intrusive Interstitials。 網站核心指標(Core Web Vitals)可以協助評估網站的使用體驗,並找出改進空間。若網站使用體驗不佳將會影響網站排名和SEO成效。 目前(2020年)使用者體驗專注於三個面向——載入速度(loading performance)、互動反應能力(Interactivity)、視覺穩定性(Visual Stability),並根據這三個面向延伸出三個主要的指標: Largest Contentful Paint(LCP):測量從網頁載入到頁面中最大面積元素渲染到畫面上所花費的時間。 First Input Delay (FID):測量使用者與網頁互動,直到瀏覽器回應互動事件的時間差。 Cumulative Layout Shift (CLS):測量累計佈局偏移,即畫面發生未預期排版移動的程度。
Nov 3, 2023Just like we have grammar rules and linguistic structures to frame our words and feelings into comprehensible sentences, we have design patterns and principles to shape our code.
Nov 3, 2023or
By clicking below, you agree to our terms of service.
New to HackMD? Sign up