栈( Stack )

1 栈的介绍

  • 栈的英文为 Stack
  • 栈是一个先入后出(FILO-First In Last Out)的有序列表
  • 栈(stack)是限制线性元素表中元素的插入和删除只能在线性表的同一端进行的一种特殊线性表。允许插入和删除的一端,为变化的一段,称为栈顶(Top),另一端为固定的一段,称为栈底(Bottom)
  • 根据栈的定义可知,最先放入栈中的元素在栈底,最后放入的元素在栈顶,而删除元素刚好相反,最后放入的元素最先删除,最先放入的元素最后删除

2 栈的应用场景

  • 子程序的调用:在跳往子程序前,会先将下个指令的地址存到堆栈中,直到子程序执行完后再将地址取出,以回到原来的程序中
  • 处理递归调用:和子程序的调用类似,只是除了储存了下一个指令的地址外,也将参数、区域变量等数据存入堆栈中
  • 表达式的转换与求值(实际解决)
  • 二叉树的遍历
  • 图形的深度优先(depth-first)搜索法

3 数组模拟栈

思路分析:

  1. 使用数组来模拟栈
  2. 定义一个top 来表示栈顶,初始化为 -1
  3. 入栈的操作,当有数据加入到栈时,top++;stack[top] = data
  4. 出栈的操作,int value = stack[top];top--;return value
package com.crisp.Stack;import java.util.Scanner;public class ArrayStackDemo {public static void main(String[] args) {//测试栈//创建一个菜单ArrayStack stack = new ArrayStack(5);String key = "";boolean loop = true;//控制是否退出菜单Scanner scanner = new Scanner(System.in);while(loop){System.out.println("show:显示栈");System.out.println("push:添加数据到栈(入栈)");System.out.println("pop:数据出栈(出栈)");System.out.println("exit:退出程序");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 exceptionSystem.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表示栈顶,初始化为-1//构造器public ArrayStack(){}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;}//入栈-pushpublic void push(int value){//判断是否栈满if(isFull()){System.out.println("~栈满,无法添加数据!!!~");return;}top++;stack[top] = value;}//出栈-poppublic 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]);}}}

4 链表模拟栈

思路分析:

  1. 使用一个链表来模拟栈
  2. 创建一个top 节点,使其一直指向链表中的第一个元素
  3. 入栈时,新加入的节点连接在top 后面,newNode.next = top.next;top.next = newNode
  4. 出栈时,top 后面的节点先出,top.next = top.next.next
package com.crisp.Stack;import java.util.Scanner;public class LinkedListStackDemo {public static void main(String[] args) {//测试,创建一个菜单LinkdedListStack linkdedListStack = new LinkdedListStack(5);String key = "";boolean loop = true;//控制是否退出菜单Scanner scanner = new Scanner(System.in);while(loop){System.out.println("show:显示栈");System.out.println("push:添加数据到栈(入栈)");System.out.println("pop:数据出栈(出栈)");System.out.println("exit:退出程序");System.out.println("请输入您要进行的操作:");key = scanner.next();switch (key){case "show":linkdedListStack.show();break;case "push":System.out.println("请输入一个数:");StackNode value = new StackNode(scanner.nextInt());linkdedListStack.push(value);break;case "pop":try {int res = linkdedListStack.pop();System.out.printf("出栈的数据是:%d\n",res);} catch (Exception e){// TODO: handle exceptionSystem.out.println(e.getMessage());}break;case "exit":scanner.close();loop =false;break;default:break;}}System.out.println("~~~程序已经退出~~~");}
}//定义一个LinkedListStack来表示栈结构
//用链表模拟栈
class LinkdedListStack{//定义top节点,始终指向链表中第一个元素//即栈的最后进入的元素private StackNode top = new StackNode(-1);//将其初始化为-1private int maxSize;//定义栈的最大容量//构造器public LinkdedListStack(int maxSize) {this.maxSize = maxSize;}//判断链表是否空public boolean isEmpty(){if(top.getNext() == null){return true;}else{return false;}}//判断链表是否满public boolean isFull(){int i = 0;//创建一个辅助节点用来遍历链表StackNode temp = top;while(true){if(i == maxSize){return true;}if(i < maxSize && temp.getNext() == null){return false;}//后移i++;temp = temp.getNext();}}//入栈public void push(StackNode newNode){//判满if(isFull()){System.out.println("~栈满!无法入栈~");return;}//将新节点连接到top的下一个节点newNode.setNext(top.getNext());top.setNext(newNode);}//出栈public int pop(){//判空if(isEmpty()){//抛出异常处理throw new RuntimeException("~栈空!没有可以出栈的元素~");}//用一个辅助变量存储要出栈的节点的值int value = top.getNext().getNo();//将与top相连接的节点出栈top.setNext(top.getNext().getNext());return value;}//遍历public void show(){//判空if(isEmpty()){System.out.println("~栈空!没有可以遍历的元素~");return;}//既然是栈,就从top开始遍历//创建一个辅助节点用来遍历链表StackNode temp = top;int i =0;while(true) {if(temp.getNext() == null){return;}System.out.println(temp.getNext().getNo());temp = temp.getNext();}}}//定义节点存储栈的数据
class StackNode{private int no;private StackNode next;//下一个节点//构造器public StackNode(int no) {this.no = no;}//set方法public int getNo() {return no;}public StackNode getNext() {return next;}//get方法public void setNo(int no) {this.no = no;}public void setNext(StackNode next) {this.next = next;}
}

5 使用栈完成表达式

思路:

  1. 通过一个index 值(索引),来遍历我们的表达式
  2. 如果我们发现是一个数字,就直接入数栈
  3. 如果发现扫描到的是一个符号,就分如下情况
    • 如果发现当前符号栈为空,就直接入栈
    • 如果符号栈有操作符,就进行比较,如果当前操作符的优先级小于或者等于栈中的操作符,就需要从数栈中pop 出两个数,再从符号栈中pop 出一个符号,进行运算,将得到结果,入数栈
    • 如果当前操作符的优先级大于栈中的操作符,就直接入符号栈
  4. 当表达式扫描完毕,就顺序的从数栈和符号栈中pop 出相应的数和符号,并运行
  5. 最后再数栈只有一个数字时,就是表达式的结果
package com.crisp.Stack;public class CalculatorDemo {public static void main(String[] args) {String expression = "100*2/4+3";//用于拼接字符串String Keepnum = "";//创建两个栈,一个数栈,一个符号栈CalStack numStack = new CalStack(10);CalStack operStack = new CalStack(10);//定义需要的相关变量int index = 0;//用于扫描int num1 = 0;int num2 = 0;int oper = 0;int res = 0;char ch = ' ';//将每次扫描得到char保存到ch//开始扫描expressionwhile(true){//依次得到expression中的每一个字符ch = expression.substring(index,index + 1).charAt(0);//判断ch 做相应的处理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 {//如果是数,直接入栈//注意ch中的值是字符,根据ASCII转换成相应的数//1、当处理多位数时不能只将一位入栈//2、需要在expression再向后看一位//3、因此需要定义一个字符串变量,用于拼接//处理多位数Keepnum += ch;//如果ch已经是最后一位,直接入栈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;}}//当表达式扫描完后,就顺序地从数栈和夫好战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\n",expression,numStack.pop());}
}//创建一个表达式栈,继承数组栈的功能
class CalStack extends ArrayStack{//构造器public CalStack(int maxSize) {super(maxSize);}//返回运算符的优先级//优先级由程序员确定//我们可以定义高优先级的运算符代表的数字更大//java中int和char可以混用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;//注意运算顺序default:break;}return res;}//增加一个方法,可以返回当前栈顶的值,但是不是pop//即不弹出栈public int peek(){return getStack()[getTop()];}
}

6 前缀、中缀、后缀表达式(逆波兰表达式)

6.1 前缀表达式(波兰表达式)
  • 前缀表达式又称波兰表达式,前缀表达式的运算符位于操作数之前
  • 举例:(3+4)*5-6对应的前缀表达式就是- * + 3 4 5 6

前缀表达式的计算机求值

  • 从右至左扫描,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对他们做相应的计算(栈顶元素和次顶元素),并将结果入栈;重复上述过程直到表达式最左端,最后运算得出的值即为表达式的结果
  • 例如:(3+4)*5-6对应的前缀表达式就是- * + 3 4 5 6,步骤如下:
    1. 从右至左扫描,将6、5、4、3压入栈堆
    2. 遇到+运算符,因此弹出3和4(3为栈顶元素,4为次顶元素),计算出3+4的值,得7,再将7入栈
    3. 接下来是*运算符,因此弹出7和5,计算出7*5 = 35,将35入栈
    4. 最后是-运算符,计算出35-6的值,即29,由此得出最终结果
6.2 中缀表达式
  • 中缀表达式就是常见的运算表达式,如:(3+4)*5-6
  • 中缀表达式的求值是我们人最熟悉的,但是对计算机不好操作,因为要判断操作符的优先级问题。因此,在计算结果时,往往会将中缀表达式转成其他表达式来操作(一般转为后缀表达式)
6.3 后缀表达式(逆波兰表达式)
  • 后缀表达式又称逆波兰表达式,与前缀表达式相似,只是运算符位于操作数之后
  • 比如:(3+4)*5-6对应的后缀表达式就是3 4 + 5 * 6 -
正常表达式 逆波兰表达式
a+b a b +
a+(b-c) a b c - +
a+(b-c)*d a b c - d * +
a+d*(b-c) a d b c - * +
a = 1+3 a 1 3 + =

后缀表达式的计算机求值

  • 从左到右扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对他们做相应的计算(次顶元素和栈顶元素),并将结果入栈;重复上述过程直到表达式最右端,最后运算得出的值即为表达式的结果
  • 例如:(3+4)*5-6对应的后缀表达式就是3 4 + 5 * 6 -,步骤如下:
    1. 从左至右扫描,将3和4压入栈堆
    2. 遇到+操作符,因此弹出4和3(4为栈顶元素,3为次顶元素),计算3+4的值,得7,再将7入栈
    3. 将5入栈
    4. 接下来是*运算符,因此弹出5和7,计算出7*5 = 35,将35入栈
    5. 将6入栈
    6. 最后是-运算符,计算出35-6的值,即29,由此得出最终结果
6.4 逆波兰计算器

​ 我们完成一个逆波兰计算器,要求完成如下任务:

  1. 输入一个逆波兰表达式,使用栈(Stack),计算其结果
  2. 支持小括号和多位数整数
  3. 思路分析
  4. 代码完成
package com.crisp.Stack;import java.util.ArrayList;
import java.util.List;
import java.util.Stack;public class RePolishNotation {public static void main(String[] args) {//先定义逆波兰表达式//(3+4)*5-6 => 3 4 + 5 * 6 -//为了方便,将逆波兰表达式的每个操作位用空格隔开String suffixExpression = "3 4 + 5 * 6 -";//思路//1、将后缀表达式放入ArrayList中//2、将ArrayList传递给一个方法,遍历ArrayList配合栈完成计算List<String> rpnList = getListString(suffixExpression);System.out.println("rpnList = "+rpnList);int res = calculator(rpnList);System.out.println("计算的结果是=" + res);}//将一个逆波兰表达式,依次将数据和运算符放入ArrayList中public static List<String> getListString(String suffixExpression){//将suffixExpression分割//按照空格来分割String[] split = suffixExpression.split(" ");List<String> list = new ArrayList<String>();for(String ele: split){list.add(ele);}return list;}//完成对逆波兰表达式的运算public static int calculator(List<String> ls){//创建堆栈,只需要一个即可Stack<String> stack = new Stack<String>();//遍历lsfor(String item: ls){//使用正则表达式来取出数if(item.matches("\\d+")){//匹配的是多位数//入栈stack.push(item);}else{//pop出两个数并运算,再入栈//注意:字符串转为整型int num2 = Integer.parseInt(stack.pop());int num1 = Integer.parseInt(stack.pop());int res = 0;switch (item){case "+":res = num1 + num2;break;case "-":res = num1 - num2;break;case "*":res = num1 * num2;break;case "/":res = num1 / num2;break;default:throw new RuntimeException("运算符有误!!!");}//把res入栈//将整型转为字符串的简单操作stack.push(res + "");}}//最后留在stack中的数据就是运算结果//注意将符号转为整型return Integer.parseInt(stack.pop());}}
6.6.5 中缀表达式转换位后缀表达式

​ 在前面代码实现的过程中,我们发现后缀表达式适合计算机进行运算,但是人却不太容易写出来,尤其是表达式很长的情况下,因此在开发中,我们需要将中缀表达式转成后缀表达式

具体步骤如下:

  1. 初始化两个栈:运算符栈s1 和储存中间结果的栈s2
  2. 从左至右扫描中缀表达式
  3. 遇到操作数,将其压入s2
  4. 遇到运算符时,比较其与s1 栈顶运算符的优先级
    1. 如果s1 为空,或栈顶运算符位左括号(,则直接将此运算符入栈
    2. 若优先级比栈顶运算符的高,也将运算符压入s1
    3. 否则,将s1 栈顶的运算符弹出并压入到s2 中,再次转换(4-1)与s1中新的栈顶运算符相比较
  5. 遇到括号时:
    1. 如果是左括号(,则直接压入s1
    2. 如果是右括号),则依次弹出s1 栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃
  6. 重复执行2至5步,直到表达式的最右边
  7. 将s1 中剩余的运算符依次弹出并压入s2
  8. 一次弹出s2 中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式

代码演示:

package com.crisp.Stack;import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Stack;public class RePolishNotation {public static void main(String[] args) {//测试完整功能String expression = "1+((2+3)*4)-5";System.out.printf("%s 对应的后缀表达式为:",expression);System.out.println(Infix2Suffix(expression));System.out.printf("%s = %d\n",expression,calculator(Infix2Suffix(expression)));}//实现中缀转后缀功能public static List<String> Infix2Suffix(String infixexpression){//定义一个InList存放中缀表达式的内容List<String> InList = new ArrayList<String>();int index = 0;//用于扫描char ch = ' ';//将每次扫描得到char保存到chString str;//对多位数拼接//一、将infixexpression中的值存入InList中do{//如果ch是一个非数字if((ch=infixexpression.charAt(index)) < 48 || (ch=infixexpression.charAt(index)) > 57){InList.add("" + ch);index++;//index后移}else { //如果是一个数字,需要考虑多位数str = "";//将str置为空while(index < infixexpression.length() && (ch=infixexpression.charAt(index)) >= 48 && (ch=infixexpression.charAt(index)) <= 57){str += ch;//拼接多位数index++;}InList.add(str);}}while (index < infixexpression.length());//二、将InList中的值转为后缀表达式//初始化两个栈,运算符栈s1,存储中间结果的栈s2Stack<String> s1 = new Stack<String>();//说明:由于s2栈中元素不参与pop操作,且最后返回值为逆序,所以用List代替List<String> s2 = new ArrayList<String>();//遍历InListfor(String item: InList){//用正则表达式判断if(item.matches("\\d+")){//如果是一个数,直接加入s2s2.add(item);}else if(item.equals("(")){//如果是左括号,直接压入s1s1.push(item);}else if(item.equals(")")){//如果是右括号`)`,则依次弹出s1 栈顶的运算符,并压入s2//直到遇到左括号为止,此时将这一对括号丢弃while(!s1.peek().equals("(")){s2.add(s1.pop());}s1.pop();//将(弹出s1,消除小括号}else{//当item的优先级小于等于栈顶元素优先级//将s1栈顶的运算符弹出并加入到s2中//再次与s1中新的栈顶运算符比较while(s1.size() != 0 && priority(s1.peek()) >= priority(item)){s2.add(s1.pop());}//还需要将item入栈s1.push(item);}}//将s1中剩余的运算符依次弹出并加入s2while(s1.size() != 0){s2.add(s1.pop());}//由于存放在List,因此按顺序输出就是对应的后缀表达式return s2;}//创建一个方法,返回运算符的优先级public static int priority(String oper){if(Objects.equals(oper, "*") || Objects.equals(oper, "/")){return 2;}else if(Objects.equals(oper, "+") || Objects.equals(oper, "-")){return 1;}else{return -1;//假定目前表达式只含有+,-,*,/}}//完成对逆波兰表达式的运算public static int calculator(List<String> ls){//创建堆栈,只需要一个即可Stack<String> stack = new Stack<String>();//遍历lsfor(String item: ls){//使用正则表达式来取出数if(item.matches("\\d+")){//匹配的是多位数//入栈stack.push(item);}else{//pop出两个数并运算,再入栈//注意:字符串转为整型int num2 = Integer.parseInt(stack.pop());int num1 = Integer.parseInt(stack.pop());int res = 0;switch (item){case "+":res = num1 + num2;break;case "-":res = num1 - num2;break;case "*":res = num1 * num2;break;case "/":res = num1 / num2;break;default:throw new RuntimeException("运算符有误!!!");}//把res入栈//将整型转为字符串的简单操作stack.push(res + "");}}//最后留在stack中的数据就是运算结果//注意将符号转为整型return Integer.parseInt(stack.pop());}
}

该笔记整理自B站BV1E4411H73v,博主稍加整理改进以及排版

数据结构与算法——栈( Stack )相关推荐

  1. 数据结构与算法-栈与队列

    数据结构与算法-栈与队列 栈 基本概念 简单表述就是仅在表尾进行插入和删除操作的线性表. 常见操作 入栈和出栈, 均在线性表的尾部进行. 基本原则就是, 先入后出. 队列 基本概念 和栈不同的是,队列 ...

  2. 数据结构与算法 / 栈(stack)

    @time 2019-07-24 @author Ruo_Xiao @reference 极客时间 -> 数据结构与算法之美 ---------------------------------- ...

  3. 数据结构与算法 -- 栈 ADT

    这两天翻了下数据结构与算法分析.严蔚敏的数据结构.C和指针.C Primer Plus这些本书,受益很多.不过大多的示例不够完整,需要自己动手编写程序.又看了遍培训时的笔记,虽然很糙但是精华的部分还是 ...

  4. JavaScript数据结构与算法——栈详解

    1.栈基本知识 栈是一种特殊的列表,栈的元素只能通过列表的一端访问,这一端成为栈顶,栈具有先进后出的特点,要想访问栈底的元素,就必须将上边的元素先拿出来.对栈的操作主要是入栈和出栈,通过push()和 ...

  5. 数据结构与算法 栈的数组实现

    Java数据结构和算法 上一篇 主目录 下一篇 package stack;import java.util.Scanner;public class ArrayStackDemo {public s ...

  6. 1. 数据结构与算法——栈

    数据结构与算法 数据结构是计算机中存储.组织数据的方式 算法通俗理解就是解决问题的方法/步骤逻辑 定义:1.一个有限的指令集,每条指令的描述不依赖与语言2.接受一些输入(也可能不需要输入),产生输出3 ...

  7. 数据结构与算法--栈

    文章目录 前言 一.栈Stack是什么? 二.栈的具体实现 三.栈的时间复杂度分析 前言 栈作为一种数据结构,是一种只能在一端进行插入和删除操作的特殊线性表.它按照后进先出的原则存储数据,先进入的数据 ...

  8. char栈java,Java数据结构与算法-栈和队列(示例代码)

    (摘录加总结)------ 栈和队列不属于基础的数据结构,它们都属于线性表. 一.栈 对于栈存储操作元素只能在栈结构的一端进行元素的插入和删除,是一种性质上的线性表结构.按照"先进后出&qu ...

  9. 数据结构与算法—栈详解

    目录 什么是栈 设计与介绍 数组实现 结构设计 push插入 pop弹出并返回首位 其他操作 链表实现 结构设计 push插入 pop弹出 其他操作 实现代码 数组实现 链表实现 测试 总结 什么是栈 ...

  10. 数据结构与算法 | 栈

    栈 栈是一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作.进行数据插入和删除操作的一端为栈顶,另一端为栈底.栈中元素遵循先进后出的原则 假设我们依次将1, 2, 3, 4压入栈中 当我们再 ...

最新文章

  1. 新增16条设计规约!阿里巴巴Java开发手册(详尽版)开放下载!
  2. 电除尘原理计算机机箱,高炉煤气布袋除尘器计算机监控系统
  3. linux教程 sed命令的用法,Linux基础教程之文件三剑客sed命令用法详解
  4. 通过了面试,却不会和hr聊薪水?
  5. 如何用几何画板破解版制作正方体展开动画
  6. 相对客观的权重计算方法——熵权法
  7. Linux调用Windows指纹识别,生物识别--指纹识别用于web登录
  8. Win10下ImageAI-gpu(tensorflow-gpu)环境安装(cuda及cuDNN自动安装)及实时对象检测
  9. 看爬b站《工作细胞》一万条评论看伙伴们在讨论什么
  10. word2007 去背景底色
  11. AAC 音频格式详解
  12. 台式机通过网线连接笔记本的wifi网络
  13. 计算跑步时的热量消耗
  14. 《关键对话》教你如何摆脱沟通困境
  15. 如何解决电脑使用中任务栏“卡死”问题。
  16. 智能硬件——身份证识别skd
  17. CSS分割线虚线代码
  18. 好用的十六进制编辑工具010 Editor
  19. 你的梦想,是复制别人的么?
  20. 李嘉诚,原一平的故事

热门文章

  1. 山丽防水墙客户端的卸载
  2. 2022年上半年5月网络工程师试题及答案
  3. 什么是水仙花数python_什么是水仙花数python
  4. ubuntu14.04_cuda8.0_cuDnn5.0_python3.4_gtx750ti_tensorflow
  5. 2021高考自贡成绩查询,多图|自贡2021高考进行时
  6. 如何从0到开始写短视频剧本,轻轻松松获得百万点赞
  7. 三次bezier曲线 MATLAB,Matlab 画二次及三次Bezier曲线,8控制点的B样条曲线
  8. 谷歌(Google)浏览器显示内存不足,无法打开此网页
  9. 新手不要再被误导!这是一篇最新的Xposed模块编写教程
  10. 设置QQ空间背景音乐