双栈排序java_数据结构和算法-栈

2023-11-10

1.栈(Stack)的介绍

栈是一个先入后出(FILO:First In Last Out)的有序列表。

栈(Stack)是限制线性表中元素的插入和删除只能在同一端进行的一种特殊线性表。

允许插入和删除的一端,为变化的一端,称为栈顶(Top),另一端为固定的一端,称为栈底(Bottom)。

根据栈的定义可知,最先放入栈中元素在栈底,最后放入的元素在栈顶

而删除元素刚好相反,最后放入的元素最先删除,最先放入的元素最后删除

2.入栈图解

0675c6bc1ebeba6b6afa6234fc60411c.png

3.出栈图解

bf3e82791a94c4d75732e42c0b2ee6bf.png

4.应用场景

1)子程序的调用:在跳往子程序前,会先将下个指令的地址存到堆栈中,直到子程序执行完后再将地址取出,以回到原来的程序中。

2)处理递归调用:和子程序的调用类似,只是除了储存下一个指令的地址外,也将参数、区域变量等数据存入堆栈中。

3)表达式的转换[中缀表达式转后缀表达式]与求值(实际解决)。

4)二叉树的遍历。

5)图形的深度优先(depth-first)搜索法。

5.用数组模拟栈

95c1f581cd7e756393c96a8d3c3b3a56.png

思路:

1)定义一个top来表示栈顶,初始化为-1

2)入栈的操作,当有数据加入到栈时,top++; stack[top] = data;

3)出栈的操作,int value = stack[top]; top--; return value;

代码实现:

48304ba5e6f9fe08f3fa1abda7d326ab.png

//定义一个 ArrayStack 表示栈

class ArrayStack {

private int maxSize; // 栈的大小

private int[] stack; // 数组,数组模拟栈,数据就放在该数组

private int top = -1;// top表示栈顶,初始化为-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;

}

//入栈-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]);

}

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

6.栈实现中缀表达式计算器

中缀表达式就是常见的运算表达式,如(3+4)×5-6

5942bcbafd4476f6d4dfd3410648f428.png

public class Calculator {

public static void main(String[] args) {

//根据前面老师思路,完成表达式的运算

String expression = "7*2*2-5+1-5+3-4"; // 15//如何处理多位数的问题?

//创建两个栈,数栈,一个符号栈

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); // 1 + 3

}

} else { //如果是数,则直接入数栈

//numStack.push(ch - 48); //? "1+3" '1' => 1

//分析思路

//1. 当处理多位数时,不能发现是一个数就立即入栈,因为他可能是多位数

//2. 在处理数,需要向expression的表达式的index 后再看一位,如果是数就进行扫描,如果是符号才入栈

//3. 因此我们需要定义一个变量 字符串,用于拼接

//处理多位数

keepNum += ch;

//如果ch已经是expression的最后一位,就直接入栈

if (index == expression.length() - 1) {

numStack.push(Integer.parseInt(keepNum));

}else{

//判断下一个字符是不是数字,如果是数字,就继续扫描,如果是运算符,则入栈

//注意是看后一位,不是index++

if (operStack.isOper(expression.substring(index+1,index+2).charAt(0))) {

//如果后一位是运算符,则入栈 keepNum = "1" 或者 "123"

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);//入栈

}

//将数栈的最后数,pop出,就是结果

int res2 = numStack.pop();

System.out.printf("表达式 %s = %d", expression, res2);

}

}

//先创建一个栈,直接使用前面创建好

//定义一个 ArrayStack2 表示栈, 需要扩展功能

class ArrayStack2 {

private int maxSize; // 栈的大小

private int[] stack; // 数组,数组模拟栈,数据就放在该数组

private int top = -1;// top表示栈顶,初始化为-1

//构造器

public ArrayStack2(int maxSize) {

this.maxSize = maxSize;

stack = new int[this.maxSize];

}

//增加一个方法,可以返回当前栈顶的值, 但是不是真正的pop

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;

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

public class Calculator {

public static void main(String[] args) {

//根据前面老师思路,完成表达式的运算

String expression = "7*2*2-5+1-5+3-4"; // 15//如何处理多位数的问题?

//创建两个栈,数栈,一个符号栈

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); // 1 + 3

}

} else { //如果是数,则直接入数栈

//numStack.push(ch - 48); //? "1+3" '1' => 1

//分析思路

//1. 当处理多位数时,不能发现是一个数就立即入栈,因为他可能是多位数

//2. 在处理数,需要向expression的表达式的index 后再看一位,如果是数就进行扫描,如果是符号才入栈

//3. 因此我们需要定义一个变量 字符串,用于拼接

//处理多位数

keepNum += ch;

//如果ch已经是expression的最后一位,就直接入栈

if (index == expression.length() - 1) {

numStack.push(Integer.parseInt(keepNum));

}else{

//判断下一个字符是不是数字,如果是数字,就继续扫描,如果是运算符,则入栈

//注意是看后一位,不是index++

if (operStack.isOper(expression.substring(index+1,index+2).charAt(0))) {

//如果后一位是运算符,则入栈 keepNum = "1" 或者 "123"

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);//入栈

}

//将数栈的最后数,pop出,就是结果

int res2 = numStack.pop();

System.out.printf("表达式 %s = %d", expression, res2);

}

}

//先创建一个栈,直接使用前面创建好

//定义一个 ArrayStack2 表示栈, 需要扩展功能

class ArrayStack2 {

private int maxSize; // 栈的大小

private int[] stack; // 数组,数组模拟栈,数据就放在该数组

private int top = -1;// top表示栈顶,初始化为-1

//构造器

public ArrayStack2(int maxSize) {

this.maxSize = maxSize;

stack = new int[this.maxSize];

}

//增加一个方法,可以返回当前栈顶的值, 但是不是真正的pop

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;

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

7.栈实现后缀表达式(逆波兰)计算器

中缀表达式的求值是我们人最熟悉的,但是对计算机来说却不好操作,因此,在计算结果时,往往会将中缀表达式转成其它表达式来操作(一般转成后缀表达式)

后缀表达式又称逆波兰表达式,与前缀表达式相似,只是运算符位于操作数之后,举例说明: (3+4)×5-6 对应的后缀表达式就是 3 4 + 5 × 6 –

再比如:

4722e995dba59149f33cad7a375077b1.png

1)后缀表达式的计算机求值

从左至右扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(次顶元素 和 栈顶元素),并将结果入栈;重复上述过程直到表达式最右端,最后运算得出的值即为表达式的结果

例如: (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,由此得出最终结果

代码实现

48304ba5e6f9fe08f3fa1abda7d326ab.png

//完成对逆波兰表达式的运算

/*

* 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,由此得出最终结果

*/

public static int calculate(List ls) {

// 创建给栈, 只需要一个栈即可

Stack stack = new Stack();

// 遍历 ls

for (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;

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());

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

2)中缀表达式转后缀表达式

具体步骤如下:

(1) 初始化两个栈:运算符栈s1和储存中间结果的栈s2;

(2) 从左至右扫描中缀表达式;

(3) 遇到操作数时,将其压s2;

(4) 遇到运算符时,比较其与s1栈顶运算符的优先级:

(4-1) 如果s1为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;

(4-2) 否则,若优先级比栈顶运算符的高,也将运算符压入s1;

(4-3) 否则,将s1栈顶的运算符弹出并压入到s2中,再次转到(4-1)与s1中新的栈顶运算符相比较;

(5) 遇到括号时:

(5-1) 如果是左括号“(”,则直接压入s1

(5-2) 如果是右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃

(6)重复步骤2至5,直到表达式的最右边

(7)将s1中剩余的运算符依次弹出并压入s2

(8)依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式

举例说明:将中缀表达式“1+((2+3)×4)-5”转换为后缀表达式的过程如下:

1feef95845fd21ba217f311cf48a2aa7.png

代码实现:

import java.util.ArrayList;

import java.util.List;

import java.util.Stack;

public class PolandNotation {

public static void main(String[] args) {

//完成将一个中缀表达式转成后缀表达式的功能

//说明

//1. 1+((2+3)×4)-5 => 转成 1 2 3 + 4 × + 5 –

//2. 因为直接对str 进行操作,不方便,因此 先将 "1+((2+3)×4)-5" =》 中缀的表达式对应的List

// 即 "1+((2+3)×4)-5" => ArrayList [1,+,(,(,2,+,3,),*,4,),-,5]

//3. 将得到的中缀表达式对应的List => 后缀表达式对应的List

// 即 ArrayList [1,+,(,(,2,+,3,),*,4,),-,5] =》 ArrayList [1,2,3,+,4,*,+,5,–]

String expression = "1+((2+3)*4)-5";//注意表达式

List infixExpressionList = toInfixExpressionList(expression);

System.out.println("中缀表达式对应的List=" + infixExpressionList); // ArrayList [1,+,(,(,2,+,3,),*,4,),-,5]

List suffixExpreesionList = parseSuffixExpreesionList(infixExpressionList);

System.out.println("后缀表达式对应的List" + suffixExpreesionList); //ArrayList [1,2,3,+,4,*,+,5,–]

System.out.printf("expression=%d", calculate(suffixExpreesionList)); // ?

/*

//先定义给逆波兰表达式

//(30+4)×5-6 => 30 4 + 5 × 6 - => 164

// 4 * 5 - 8 + 60 + 8 / 2 => 4 5 * 8 - 60 + 8 2 / +

//测试

//说明为了方便,逆波兰表达式 的数字和符号使用空格隔开

//String suffixExpression = "30 4 + 5 * 6 -";

String suffixExpression = "4 5 * 8 - 60 + 8 2 / +"; // 76

//思路

//1. 先将 "3 4 + 5 × 6 - " => 放到ArrayList中

//2. 将 ArrayList 传递给一个方法,遍历 ArrayList 配合栈 完成计算

List list = getListString(suffixExpression);

System.out.println("rpnList=" + list);

int res = calculate(list);

System.out.println("计算的结果是=" + res);

*/

}

//即 ArrayList [1,+,(,(,2,+,3,),*,4,),-,5] =》 ArrayList [1,2,3,+,4,*,+,5,–]

//方法:将得到的中缀表达式对应的List => 后缀表达式对应的List

public static List parseSuffixExpreesionList(List ls) {

//定义两个栈

Stack s1 = new Stack(); // 符号栈

//说明:因为s2 这个栈,在整个转换过程中,没有pop操作,而且后面我们还需要逆序输出

//因此比较麻烦,这里我们就不用 Stack 直接使用 List s2

//Stack s2 = new Stack(); // 储存中间结果的栈s2

List s2 = new ArrayList(); // 储存中间结果的Lists2

//遍历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中,再次转到(4.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; //注意因为是存放到List, 因此按顺序输出就是对应的后缀表达式对应的List

}

//方法:将 中缀表达式转成对应的List

// s="1+((2+3)×4)-5";

public static List toInfixExpressionList(String s) {

//定义一个List,存放中缀表达式 对应的内容

List ls = new ArrayList();

int i = 0; //这时是一个指针,用于遍历 中缀表达式字符串

String str; // 对多位数的拼接

char c; // 每遍历到一个字符,就放入到c

do {

//如果c是一个非数字,我需要加入到ls

if((c=s.charAt(i)) < 48 || (c=s.charAt(i)) > 57) {

ls.add("" + c);

i++; //i需要后移

} else { //如果是一个数,需要考虑多位数

str = ""; //先将str 置成"" '0'[48]->'9'[57]

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;//返回

}

//将一个逆波兰表达式, 依次将数据和运算符 放入到 ArrayList中

public static List getListString(String suffixExpression) {

//将 suffixExpression 分割

String[] split = suffixExpression.split(" ");

List list = new ArrayList();

for(String ele: split) {

list.add(ele);

}

return list;

}

//完成对逆波兰表达式的运算

/*

* 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,由此得出最终结果

*/

public static int calculate(List ls) {

// 创建给栈, 只需要一个栈即可

Stack stack = new Stack();

// 遍历 ls

for (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;

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());

}

}

//编写一个类 Operation 可以返回一个运算符 对应的优先级

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("不存在该运算符" + operation);

break;

}

return result;

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

import java.util.ArrayList;

import java.util.List;

import java.util.Stack;

public class PolandNotation {

public static void main(String[] args) {

//完成将一个中缀表达式转成后缀表达式的功能

//说明

//1. 1+((2+3)×4)-5 => 转成 1 2 3 + 4 × + 5 –

//2. 因为直接对str 进行操作,不方便,因此 先将 "1+((2+3)×4)-5" =》 中缀的表达式对应的List

// 即 "1+((2+3)×4)-5" => ArrayList [1,+,(,(,2,+,3,),*,4,),-,5]

//3. 将得到的中缀表达式对应的List => 后缀表达式对应的List

// 即 ArrayList [1,+,(,(,2,+,3,),*,4,),-,5] =》 ArrayList [1,2,3,+,4,*,+,5,–]

String expression = "1+((2+3)*4)-5";//注意表达式

List infixExpressionList = toInfixExpressionList(expression);

System.out.println("中缀表达式对应的List=" + infixExpressionList); // ArrayList [1,+,(,(,2,+,3,),*,4,),-,5]

List suffixExpreesionList = parseSuffixExpreesionList(infixExpressionList);

System.out.println("后缀表达式对应的List" + suffixExpreesionList); //ArrayList [1,2,3,+,4,*,+,5,–]

System.out.printf("expression=%d", calculate(suffixExpreesionList)); // ?

/*

//先定义给逆波兰表达式

//(30+4)×5-6 => 30 4 + 5 × 6 - => 164

// 4 * 5 - 8 + 60 + 8 / 2 => 4 5 * 8 - 60 + 8 2 / +

//测试

//说明为了方便,逆波兰表达式 的数字和符号使用空格隔开

//String suffixExpression = "30 4 + 5 * 6 -";

String suffixExpression = "4 5 * 8 - 60 + 8 2 / +"; // 76

//思路

//1. 先将 "3 4 + 5 × 6 - " => 放到ArrayList中

//2. 将 ArrayList 传递给一个方法,遍历 ArrayList 配合栈 完成计算

List list = getListString(suffixExpression);

System.out.println("rpnList=" + list);

int res = calculate(list);

System.out.println("计算的结果是=" + res);

*/

}

//即 ArrayList [1,+,(,(,2,+,3,),*,4,),-,5] =》 ArrayList [1,2,3,+,4,*,+,5,–]

//方法:将得到的中缀表达式对应的List => 后缀表达式对应的List

public static List parseSuffixExpreesionList(List ls) {

//定义两个栈

Stack s1 = new Stack(); // 符号栈

//说明:因为s2 这个栈,在整个转换过程中,没有pop操作,而且后面我们还需要逆序输出

//因此比较麻烦,这里我们就不用 Stack 直接使用 List s2

//Stack s2 = new Stack(); // 储存中间结果的栈s2

List s2 = new ArrayList(); // 储存中间结果的Lists2

//遍历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中,再次转到(4.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; //注意因为是存放到List, 因此按顺序输出就是对应的后缀表达式对应的List

}

//方法:将 中缀表达式转成对应的List

// s="1+((2+3)×4)-5";

public static List toInfixExpressionList(String s) {

//定义一个List,存放中缀表达式 对应的内容

List ls = new ArrayList();

int i = 0; //这时是一个指针,用于遍历 中缀表达式字符串

String str; // 对多位数的拼接

char c; // 每遍历到一个字符,就放入到c

do {

//如果c是一个非数字,我需要加入到ls

if((c=s.charAt(i)) < 48 || (c=s.charAt(i)) > 57) {

ls.add("" + c);

i++; //i需要后移

} else { //如果是一个数,需要考虑多位数

str = ""; //先将str 置成"" '0'[48]->'9'[57]

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;//返回

}

//将一个逆波兰表达式, 依次将数据和运算符 放入到 ArrayList中

public static List getListString(String suffixExpression) {

//将 suffixExpression 分割

String[] split = suffixExpression.split(" ");

List list = new ArrayList();

for(String ele: split) {

list.add(ele);

}

return list;

}

//完成对逆波兰表达式的运算

/*

* 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,由此得出最终结果

*/

public static int calculate(List ls) {

// 创建给栈, 只需要一个栈即可

Stack stack = new Stack();

// 遍历 ls

for (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;

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());

}

}

//编写一个类 Operation 可以返回一个运算符 对应的优先级

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("不存在该运算符" + operation);

break;

}

return result;

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

一、简介

栈(英语:stack)又称为堆栈或堆叠,栈作为一种数据结构,是一种只能在一端进行插入和删除操作的特殊线性表。它按照先进后出的原则存储数据,先进入的数据被压入栈底(Bottom),最后的数据在栈顶(Top)。我们把插入元素的操作称为入栈(Push),删除元素的操作称为出栈(Pop)。

由于堆叠数据结构只允许在一端进行操作,因而按照后进先出(LIFO, Last In First Out)的原理运作。栈也称为后进先出表。

这里以羽毛球筒为例,羽毛球筒就是一个栈,刚开始羽毛球筒是空的,也就是空栈,然后我们一个一个放入羽毛球,也就是一个一个push进栈,当我们需要使用羽毛球的时候,从筒里面拿,也就是pop出栈,但是第一个拿到的羽毛球是我们最后放进去的。

栈的结构如下图:

e14ef41f524f03410ff504132a157867.png

二、Java模拟简单的顺序栈实现

56389f4eaed28deeccd43bdacceefc40.png

48304ba5e6f9fe08f3fa1abda7d326ab.png

package com.ys.datastructure;

public class MyStack {

private int[] array;

private int maxSize;

private int top;

public MyStack(int size){

this.maxSize = size;

array = new int[size];

top = -1;

}

//压入数据

public void push(int value){

if(top < maxSize-1){

array[++top] = value;

}

}

//弹出栈顶数据

public int pop(){

return array[top--];

}

//访问栈顶数据

public int peek(){

return array[top];

}

//判断栈是否为空

public boolean isEmpty(){

return (top == -1);

}

//判断栈是否满了

public boolean isFull(){

return (top == maxSize-1);

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

测试:

48304ba5e6f9fe08f3fa1abda7d326ab.png

package com.ys.test;

import com.ys.datastructure.MyStack;

public class MyStackTest {

public static void main(String[] args) {

MyStack stack = new MyStack(3);

stack.push(1);

stack.push(2);

stack.push(3);

System.out.println(stack.peek());

while(!stack.isEmpty()){

System.out.println(stack.pop());

}

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

结果:

144d978b6f6305beaaca9747ec926717.png

这个栈是用数组实现的,内部定义了一个数组,一个表示最大容量的值以及一个指向栈顶元素的top变量。构造方法根据参数规定的容量创建一个新栈,push()方法是向栈中压入元素,指向栈顶的变量top加一,使它指向原顶端数据项上面的一个位置,并在这个位置上存储一个数据。pop()方法返回top变量指向的元素,然后将top变量减一,便移除了数据项。要知道 top 变量指向的始终是栈顶的元素。

产生的问题:

①、上面栈的实现初始化容量之后,后面是不能进行扩容的(虽然栈不是用来存储大量数据的),如果说后期数据量超过初始容量之后怎么办?(自动扩容)

②、我们是用数组实现栈,在定义数组类型的时候,也就规定了存储在栈中的数据类型,那么同一个栈能不能存储不同类型的数据呢?(声明为Object)

③、栈需要初始化容量,而且数组实现的栈元素都是连续存储的,那么能不能不初始化容量呢?(改为由链表实现)

三、增强功能版栈

对于上面出现的问题,第一个能自动扩容,第二个能存储各种不同类型的数据,解决办法如下:(第三个在讲链表的时候在介绍)

这个模拟的栈在JDK源码中,大家可以参考 Stack 类的实现。

54a109cb14d180bdf889412428a9cb76.png

48304ba5e6f9fe08f3fa1abda7d326ab.png

package com.ys.datastructure;

import java.util.Arrays;

import java.util.EmptyStackException;

public class ArrayStack {

//存储元素的数组,声明为Object类型能存储任意类型的数据

private Object[] elementData;

//指向栈顶的指针

private int top;

//栈的总容量

private int size;

//默认构造一个容量为10的栈

public ArrayStack(){

this.elementData = new Object[10];

this.top = -1;

this.size = 10;

}

public ArrayStack(int initialCapacity){

if(initialCapacity < 0){

throw new IllegalArgumentException("栈初始容量不能小于0: "+initialCapacity);

}

this.elementData = new Object[initialCapacity];

this.top = -1;

this.size = initialCapacity;

}

//压入元素

public Object push(Object item){

//是否需要扩容

isGrow(top+1);

elementData[++top] = item;

return item;

}

//弹出栈顶元素

public Object pop(){

Object obj = peek();

remove(top);

return obj;

}

//获取栈顶元素

public Object peek(){

if(top == -1){

throw new EmptyStackException();

}

return elementData[top];

}

//判断栈是否为空

public boolean isEmpty(){

return (top == -1);

}

//删除栈顶元素

public void remove(int top){

//栈顶元素置为null

elementData[top] = null;

this.top--;

}

/**

* 是否需要扩容,如果需要,则扩大一倍并返回true,不需要则返回false

* @param minCapacity

* @return

*/

public boolean isGrow(int minCapacity){

int oldCapacity = size;

//如果当前元素压入栈之后总容量大于前面定义的容量,则需要扩容

if(minCapacity >= oldCapacity){

//定义扩大之后栈的总容量

int newCapacity = 0;

//栈容量扩大两倍(左移一位)看是否超过int类型所表示的最大范围

if((oldCapacity<<1) - Integer.MAX_VALUE >0){

newCapacity = Integer.MAX_VALUE;

}else{

newCapacity = (oldCapacity<<1);//左移一位,相当于*2

}

this.size = newCapacity;

int[] newArray = new int[size];

elementData = Arrays.copyOf(elementData, size);

return true;

}else{

return false;

}

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

测试:

48304ba5e6f9fe08f3fa1abda7d326ab.png

//测试自定义栈类 ArrayStack

//创建容量为3的栈,然后添加4个元素,3个int,1个String.

@Test

public void testArrayStack(){

ArrayStack stack = new ArrayStack(3);

stack.push(1);

//System.out.println(stack.peek());

stack.push(2);

stack.push(3);

stack.push("abc");

System.out.println(stack.peek());

stack.pop();

stack.pop();

stack.pop();

System.out.println(stack.peek());

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

结果:

341a8fadc673f739c1700831f27339bb.png

四、利用栈实现字符串逆序

我们知道栈是后进先出,我们可以将一个字符串分隔为单个的字符,然后将字符一个一个push()进栈,在一个一个pop()出栈就是逆序显示了。如下:

将 字符串“how are you” 反转!!!

这里我们是用上面自定的栈来实现的,大家可以将ArrayStack替换为JDK自带的栈类Stack试试

48304ba5e6f9fe08f3fa1abda7d326ab.png

//进行字符串反转

@Test

public void testStringReversal(){

ArrayStack stack = new ArrayStack();

String str = "how are you";

char[] cha = str.toCharArray();

for(char c : cha){

stack.push(c);

}

while(!stack.isEmpty()){

System.out.print(stack.pop());

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

结果:

ac5b6320d8b4341d029cc276e490f816.png

五、利用栈判断分隔符是否匹配

写过xml标签或者html标签的,我们都知道进行匹配,[ 也必须和最近的 ] 进行匹配。

比如:这是符号相匹配的,如果是 abc] 那就是不匹配的。

对于 12,我们分析在栈中的数据:遇到匹配正确的就消除

82d65211ebecf581bed45c5fec3a537e.png

最后栈中的内容为空则匹配成功,否则匹配失败!!!

48304ba5e6f9fe08f3fa1abda7d326ab.png

//分隔符匹配

//遇到左边分隔符了就push进栈,遇到右边分隔符了就pop出栈,看出栈的分隔符是否和这个有分隔符匹配

@Test

public void testMatch(){

ArrayStack stack = new ArrayStack(3);

String str = "12";

char[] cha = str.toCharArray();

for(char c : cha){

switch (c) {

case '{':

case '[':

case '

stack.push(c);

break;

case '}':

case ']':

case '>':

if(!stack.isEmpty()){

char ch = stack.pop().toString().toCharArray()[0];

if(c=='}' && ch != '{'

|| c==']' && ch != '['

|| c==')' && ch != '('){

System.out.println("Error:"+ch+"-"+c);

}

}

break;

default:

break;

}

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

六、总结

根据栈后进先出的特性,我们实现了单词逆序以及分隔符匹配。所以其实栈是一个概念上的工具,具体能实现什么功能可以由我们去想象。栈通过提供限制性的访问方法push()和pop(),使得程序不容易出错。

对于栈的实现,我们稍微分析就知道,数据入栈和出栈的时间复杂度都为O(1),也就是说栈操作所耗的时间不依赖栈中数据项的个数,因此操作时间很短。而且需要注意的是栈不需要比较和移动操作,我们不要画蛇添足。

七、扩展

1、可查询最值的栈练习题

定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数。

实现一个特殊的栈,再实现栈的基本功能的基础上,再实现返回栈中最小元素的操作getmin。

要求:

①pop、push、getmin的时间复杂度为O(1)。

②设计的栈类型可以使用现有的栈结构。

方法1

eb2597ae34d00bace68d131ee0536573.png

方法2

8a96e238ecf23e6a66a2599272213ae2.png

区别

1.方法1和方法2都是利用StackMin来保存每一步的最小值。

2.方法1和方法2的实现时间复杂度都是O(1)。

3.区别在于方法1稍省空间,略费时间,方法2则相反。

48304ba5e6f9fe08f3fa1abda7d326ab.png

import java.util.Stack;

public class Solution {

private Stack stackData = new Stack<>();

private Stack stackMin = new Stack<>();

public void push(int node) {

//将当前元素压入栈

stackData.push(node);

/**

* 如果最小栈为空,那么直接压入

* 否则如果当前元素小于stackMin的顶部元素,直接压入,大于就继续压入stackMin的顶部元素

*/

if(stackMin.isEmpty()){

stackMin.push(node);

} else {

if (node < stackMin.peek().intValue()) {

stackMin.push(node);

}

else{

stackMin.push(stackMin.peek());

}

}

}

public void pop() {

stackData.pop();

stackMin.pop();

}

public int top() {

return stackData.peek();

}

public int min() {

return stackMin.peek();

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

2、栈的反转练习题

实现一个栈的逆序,但是只能用递归函数和这个栈本身的pop操作来实现,而不能自己申请另外的数据结构。

给定一个整数数组A即为给定的栈,同时给定它的大小n,请返回逆序后的栈。

测试样例:

[4,3,2,1],4

返回:

[1,2,3,4]

48304ba5e6f9fe08f3fa1abda7d326ab.png

// 思路:每次下标和上标的数据对调,然后各自指针向中间移动一位,递归调用,直到上指标小于stack大小的一半结束

public static Stack reverseStack(Stack stack,int n){

if (stack != null && !stack.isEmpty()) {

int size = stack.size();

int bottomindex = size-n;

int topindex = n-1;

int bottomdata = stack.get(bottomindex);

int topdata = stack.get(topindex);

int temp = bottomdata;

bottomdata = topdata;

topdata = temp;

stack.set(bottomindex, bottomdata);

stack.set(topindex, topdata);

n--;

if (n>(size/2)) {

reverseStack(stack, n);

}

}

return stack;

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

3、双栈排序练习题

请编写一个程序,按升序对栈进行排序(即最大元素位于栈顶),要求最多只能使用一个额外的栈存放临时数据,但不得将元素复制到别的数据结构中。

给定一个int[] numbers(C++中为vector&ltint>),其中第一个元素为栈顶,请返回排序后的栈。请注意这是一个栈,意味着排序过程中你只能访问到第一个元素。

测试样例:

[1,2,3,4,5]

返回:[5,4,3,2,1]

48304ba5e6f9fe08f3fa1abda7d326ab.png

public class TwoStacks {

public ArrayList twoStacksSort(int[] numbers) {

// write code here

int len = numbers.length;

int[] help = new int[len];

int n = len - 1;

int m = -1;

while(n >= 0){

int key = numbers[n--];

if(m == -1){

help[++m] = key;

}else{

if(help[m] > key){

help[++m] = key;

}else{

int k = m;

while(k>=0 && help[k]<=key){

help[k+1] = help[k];

k --;

}

help[k+1] = key;

m++;

}

}

}

ArrayList list = new ArrayList();

for(int i = 0; i < help.length; i++){

list.add(help[i]);

}

return list;

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

双栈排序java_数据结构和算法-栈 的相关文章

  • selenium+chormdriver+python 实现淘宝的信息爬取

    因为我是个爬虫新手 所以对爬虫还不熟练 这几天想着自己做一个淘宝信息的自动爬取 一开始感觉比较简单 但做到了登录界面 发现一直被网站检测出来 不能滑动滑块 接下来从网上翻遍了资料 整理了以下自己的代码 完成了这个艰难的工程 嘻嘻 对我来说
  • Rsync远程同步

    rsync rsync Remote Sync 远程同步 是一个开源的快速备份工具 可以在不同主机之间镜像同步整个目录树 支持增量备份 并保持链接和权限 且采用优化的同步算法 传输前执行压缩 因此非常适用于异地备份 镜像服务器等应用 rsy
  • MFC中设置焦点

    初次接触MFC 实现填完一系列表单后继续添加另外一张 并且将焦点设置为第一张初次填写时的焦点 可能就是指第一个获取焦点的控件 用 SetFocus m hWnd 实现重置表单的功能 UpdateData FALSE 更新数据时是 Updat
  • 【Docker】Docker的使用案例以及未来发展、Docker Hub 服务、环境安全的详细讲解

    Docker的工具实践及root概念和Docker容器安全性设置 1 使用案例 2 Docker解决的问题 3 Docker未来发展 4 Docker Hub 服务 5 技术局限 6 Docker环境安全 7 容器部署安全 1 使用案例 D
  • 希腊字母发音对照表及其latex命令

    拉丁字母是26个 希腊 Greek 字母是24个 发音即是它们各自的latex形式 大写字母的是其小写latex首字母大写后的形式 如 Delta Delta notation 西方的数学家们在推导数学定理时 仍然沿用并不好写也不好记的希腊
  • ArcGIS 文本数字写入csv文件后小数点位数减少

    在将经纬度数据写入csv文件的过程中 经度和纬度都是以小数点后保留4为小数的字符串形式存储的 但是在转成csv文件后 打开发现小数点位数缺失了 如图 在网上找了好久也没有找到解决办法 大部分都是解决文本数字过长导致以有效数字形式显示的问题
  • 如何有效的防护DDoS攻击

    DDoS攻击的类型和方法 分布式拒绝服务攻击 简称DDoS 是一种协同攻击 旨在使受害者的资源无法使用 它可以由一个黑客组织协同行动 也可以借助连接到互联网的多个受破坏设备来执行 这些在攻击者控制下的设备通常称为僵尸网络 有多种执行DDoS
  • stm32通用外部spi下载算法实现

    参考硬汉嵌入式 实战技能 任何支持SWD接口的单片机都可以方便移植的SPI Flash烧写算法制作 哔哩哔哩 bilibili 该up主提供的stm32H7的模板工程 目前需求是实现基于正点原子探索者stm32f407zet6 W25Q12
  • SpringMVC上传文件的 4 种方式,你都会么?

    1 本文内容 文件上传开发步骤 单文件上传 多文件上传 通过 MultipartHttpServletRequest 处理文件上传 通过自定义对象接收上传的文件 扩展知识 案例代码 2 预备知识 springmvc 系列中的测试案例 基本上
  • 智能汽车竞赛室外光电 组 1 安装ROS软件平台和运行第一个程序

    机器人操作系统 ROS 对机器人进行编程以使其完全符合在工业环境中的要求 它的工具 库和共享的开放资源 允许开发人员协同工作 利用现有工作的优势 简化和加快创建机器人行为的过程 ROS得到了一个庞大的全球社区的支持 其邮件列表 Wiki和R
  • [从零开始学DeepFaceLab-21]: 使用-命令行八大操作步骤-第6步:模型的选择与训练 - 进阶 - AMP模型训练参数详解与优化

    目录 前言 第1章 AMP模型训练参数详解 1 1 AMP参数汇总 1 2 参数详解
  • AOP实现企业级API访问接口监控(通过Google Guava缓存数据)

    开发了企业的功能模块 分享给大家参考 若大家看到我的实现有不足之处或有自己的见解欢迎评论区分享 学习去咯 文章目录 前言 一 AOP的基本知识 1 什么是AOP 2 有哪些AOP的概念 3 AOP包含的几个概念 4 AOP 有哪些应用场景
  • QT打开文件及文件路径

    获取文件夹路径 static QString getExistingDirectory QWidget parent Q NULLPTR const QString caption QString const QString dir QSt
  • Android NDK添加NEON以及cpufeatures支持

    本人使用Android studio3 0进行NDK开发 由于Android develop官网文档是针对2 2版本以下 这里为2 2以上版本的cmakelist配置做以下纪录 一 添加NEON支持 在build gradle app 中添
  • vue实现element自定义新增、删除table表格的行,和可输入input(可以自行修改成双击表格可编辑)

    效果如图 新增表格行 可点编辑再修改表格行内容 也可以自行修改成双击表格可编辑 思路 1 新增表格行 handleAddBtn 给表格数组 我这里是用tableData数组 push空的对象 2 删除行 handleDeleteBtn 首先
  • Python轻量级Web框架Flask(2)——Flask模板渲染/Flask项目拆分

    1 run启动参数详解 debug 是否开启调试模式 开启后 debug true 修改过python代码会自动重启 不用停止运行之后再去启动 port 启动指定服务器的端口号 默认是5000 host 主机 默认是127 0 0 1 只有
  • MySQL技术内幕InnoDB存储引擎 学习笔记 第四章 表

    InnoDB引擎表中 每张表都有一个主键 如果创建表时没有显式定义主键 则 1 首先看表中是否有非空的唯一索引 如果有 则该列为主键 2 否则自动创建一个6字节大小的指针作为主键 InnoDB所有数据都逻辑地放在一个表空间中 表空间又由段
  • 基于SSM的校园旧书交易交换平台

    末尾获取源码 开发语言 Java Java开发工具 JDK1 8 后端框架 SSM 前端 采用JSP技术开发 数据库 MySQL5 7和Navicat管理工具结合 服务器 Tomcat8 5 开发软件 IDEA Eclipse 是否Mave
  • KMeans算法

    目录 一 基本概念 二 Centroid Initialization Methods 三 Mini Batch K Means 四 找寻最优的聚类数量 4 1 拐点 4 2 silhouette score 轮廓分数 五 Kmeans的优

随机推荐

  • Java基础怎么样才算牢固,能熟练使用还是深入源码?

    java就是一门工具 本质上跟你开的车没什么区别 java的熟练程度 就犹如你的驾驶技术 本质上就是工具操作的熟练度 对于工具的操作而言 绝大多数人 会用就行了 源码基本上没有深入的必要 因为对于绝大多数程序员以及非程序员而言 这个轮不到你
  • B端产品的交互设计流程探索:乐高设计法和用户体验的二次提升

    面对突如其来的B端体验设计的挑战 可以解决吗 或者说设计师的思路该如何针对B端设计的特点进行调整 甚至设计管理的思路如何调整 一起来文章中看看解决办法 如何应对B端到来的挑战 随着互联网常规C端业务逐渐由蓝海转为红海 B端设计迎来了各种挑战
  • VS-SQL 连接DBHelper类

    using System using System Collections Generic using System Data using System Data SqlClient using System Text using Syst
  • 阿里云实习生部门笔试2020.04

    月初申请阿里c c 实习生 公司很快安排了上机笔试 是两道算法题大题 很难 没刷过题导致题目也看不怎么懂 笔试完第二天阿里云打电话安排另一场笔试 题目如下 评测题目 无 第一题 char str http www ibegroup com
  • python 题(附答案)

    1 一行代码实现1 100之和 gt gt 5050 2 描述 join 和split 区别 join用来连接字符串 split恰好相反拆分字符串 gt gt my name is huihui gt gt my name is huihu
  • vba可以放服务器上处理文档,当Word Doc位于服务器上时,如何从Excel VBA更改Word Doc中的文本?...

    我有一个宏 可以根据单词模板创建实验报告 然后将其保存到远程服务器 在我的Excel工作表中选择一个单元格 运行宏 使用空白模板打开Word实例 并根据所选单元格和其他数据保存文件 我想要做的是编辑空白模板中的标题 并将其更新为实验名称 该
  • 70进货卖100利润是多少_服装批发利润大揭秘!让你拿货砍价心里有个底

    你以为服装批发档口卖货的策略是薄利多销 你以为一包货只要小几百元很便宜 错 不要用零售的价格参考服装批发的价格 服装批发还可以更便宜 只要你了解服装批发商的利润空间 你就知道他们的价格底线在哪 拿货砍价更有目标 首先 我们从服装在市场上的来
  • python高性能服务器编写,Tornado的高性能服务器开发常用方法

    最近一直开发AI人脸识别相关的项目 需要提供给客户一些服务 所以我需要开发一些服务端程序 由于AI算法都是用python3写的 所以我就索性用起了python开发服务端 毕竟速度也快 以前用过Flask Django 这次决定有Tornad
  • 编译与链接

    把C 当脚本语言写 https www cnblogs com index html archive 2012 07 28 cppscript html vc 调用exe时 如何获取exe的输出信息 输出显示在IDE的输出中 http bl
  • [Python人工智能] 五.theano实现神经网络正规化Regularization处理

    从本系列文章开始 作者正式开始研究Python深度学习 神经网络及人工智能相关知识 前四篇文章讲解了神经网络基础概念 Theano库的安装过程及基础用法 theano实现回归神经网络 theano实现分类神经网络 这篇文章讲解Overfit
  • Hibenate技术详解

    一 Hibernate的基本用法 1 Hibernate的数据库操作 在所有的ORM框架中有一个非常重要的媒介 PO Persistence Object 持久化对象 持久化对象的作用是完成持久化操作 简单的说 通过该对象可以对数据进行增删
  • 逻辑回归介绍及statsmodels、sklearn实操

    目录 1 逻辑回归简要介绍 2 statsmodels中实现逻辑回归 3 sklearn实现逻辑回归 3 1 基础案例代码 3 2 样本不平衡问题处理 3 3 LogisticRegression模型参数说明 3 4 模型调优方法 1 逻辑
  • java 获取两个日期之间的所有月份 (年月)、以及月数、年数

    获取两个日期之间的所有月份 年月 param startTime param endTime return YYYY MM public static List
  • 347. 前 K 个高频元素

    1 哈希记录元素出现次数 2 放入优先队列 最大堆 3 依次出队获取结果 public class Solution public int TopKFrequent int nums int k 收集元素次数 Dictionary
  • linux内核设置选择硬件,Linux内核中USB充电器的解决方案

    当前最新的内核 v3 5 对USB充电器的整体方案支持的不是太好 这里讨论的USB充电器的方案仅指软件 方案 即充电器的检测需要由软件干预 比如读取USB PHY的寄存器 同时电池的充电值根据 充电器的不同类型 需要由软件来设置 硬件检测充
  • 导学:从提笔就怕,到写什么都赚钱

    做最懂技术的传播者 最懂传播的工程师 课程内容分析 课程的标题非常 标题党 实际课件的标题收敛很多 如何为系统学习新媒体写作做准备 正式开始课程前的建议 本节课程为正式课程开始前的一节导论课程 主要目的是 使学员明确 新媒体写作内容提升的学
  • k8s 系列之 CoreDNS 解读

    k8s 系列之 CoreDNS CoreDNS工作原理 kuberntes 中的 pod 基于 service 域名解析后 再负载均衡分发到 service 后端的各个 pod 服务中 如果没有 DNS 解析 则无法查到各个服务对应的 se
  • CMake 学习笔记(设置C++ 标准的版本)

    CMake 学习笔记 设置C 标准的版本 C 标准发展至今已经有很多个版本 包括最开始 C 98 后面的 C 11 C 14 C 17 等 如果我们的代码用到了比较新的C 特性 那么就需要编译器有对应的支持 这篇博客主要就是讲讲如何告诉 C
  • swift手动导入OC的第三方库

    声明 作为ios开发的新语言 相对比较oc 资源还是比较欠缺 有时候开发中 我们需要引入第三方库就不得不引入oc版的第三方库 然后苹果公司也给集成了这样的快捷方式 导入第三方的方法有 1 CocoaPods 2 手动将第三方的文件复制到工程
  • 双栈排序java_数据结构和算法-栈

    1 栈 Stack 的介绍 栈是一个先入后出 FILO First In Last Out 的有序列表 栈 Stack 是限制线性表中元素的插入和删除只能在同一端进行的一种特殊线性表 允许插入和删除的一端 为变化的一端 称为栈顶 Top 另