栈与队列
栈与实现
ADT接口
栈(stack)是存放数据对象的一种特殊容器,其中的数据元素按线性的逻辑次序排列,只能对一端的数据进行操作,并且遵守先进后出的原则。
栈支持的操作接口
操作实例
给出了一个存放整数的栈从被创建开始,按以上接口实施一系列操作的过程。
实现
c++实现
定义一个Stack继承Vector类,并添加一些相关操作方法
1 #include "../Vector/Vector.h" //以向量为基类,派生出栈模板类
2 template <typename T> class Stack: public Vector<T> { //将向量癿首/末端作为栈底/顶
3 public: //size()、empty()以及其它开放接口,均可直接沿用
4 void push(T const& e) { insert(size(), e); } //入栈:等效亍将新元素作为向量癿末元素揑入
5 T pop() { return remove(size() - 1); } //出栈:等效亍初除向量癿末元素
6 T& top() { return (*this)[size() - 1]; } //叏顶:直接迒回向量癿末元素
7 };
java实现
使用list
public class Stack<E> {
//栈中属性
public List<E> items = new ArrayList<E>();
public Stack() {
}
//栈相关的方法
//压栈操作:添加一个新元素到栈顶位置.
public void push(E element){
items.add(element);
}
//出栈操作:移除栈顶的元素,同时返回被移除的元素。
public E pop(){
E e = items.get(items.size() - 1);
items.remove(items.size() - 1);
return e;
}
//peek操作:返回栈顶的元素,不对栈做任何修改(这个方法不会移除栈顶的元素,仅仅返回它)。
public E peek(){
return items.get(items.size() - 1);
}
//判断栈中元素是否为空:如果栈里没有任何元素就返回true,否则返回false。
public Boolean isEmpty(){
return items.size() == 0;
}
//获取栈中元素的个数:移除栈里的所有元素。
public int size(){
return items.size();
}
}
使用Vector
public class Stack02<E> {
//栈中属性
Vector items = new Vector<E>();
public Stack02() {
}
//栈相关的方法
//压栈操作:添加一个新元素到栈顶位置.
public void push(E element){
items.addElement(element);
}
//出栈操作:移除栈顶的元素,同时返回被移除的元素。
public E pop(){
E e = (E) items.elementAt(items.size() - 1);
items.remove(items.size() - 1);
return e;
}
//peek操作:返回栈顶的元素,不对栈做任何修改(这个方法不会移除栈顶的元素,仅仅返回它)。
public E peek(){
return (E) items.elementAt(items.size() - 1);
}
//判断栈中元素是否为空:如果栈里没有任何元素就返回true,否则返回false。
public Boolean isEmpty(){
return items.size() == 0;
}
//获取栈中元素的个数:移除栈里的所有元素。
public int size(){
return items.size();
}
}
栈的典型应用
分为4个方面:逆序输出、递归嵌套、延迟缓冲、逆波兰表达式
逆序输出
对进制进行处理
@Test
public void test2(){
StringBuffer str = dec2bin(100);
System.out.println(str);
}
public StringBuffer dec2bin(int decNumer) {
// 定义变量
Stack02 stack = new Stack02();
int remainder;
// 循环除法
while (decNumer > 0) {
remainder = decNumer % 2;
decNumer = (int) Math.floor(decNumer / 2);
stack.push(remainder);
}
// 将数据取出
StringBuffer stringBuffer = new StringBuffer();
while (!stack.isEmpty()) {
stringBuffer.append(stack.pop());
}
return stringBuffer;
}
public StringBuffer convert(int n,int base) {
// 定义变量
Stack02 stack = new Stack02();
char[] digit = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
while(n > 0){
stack.push(digit[n % base]);
n /= base;
}
// 将数据取出
StringBuffer stringBuffer = new StringBuffer();
while (!stack.isEmpty()) {
stringBuffer.append(stack.pop());
}
return stringBuffer;
}
递归嵌套
进行括号的匹配判断
@Test
public void test4(){
char[] par ={'(','{','[','(',')','[',']',']','}',')'};
char[] par1 ={'(','(',')',')'};
/* System.out.println(paren1(par,0, par.length ));*/
System.out.println(paren2(par1,0, par1.length-1));
}
public Boolean paren1(char exp[],int lo,int hi){
Stack02<Character> stack = new Stack02<Character>();
for (int i = lo;i < hi; i++){
switch (exp[i]) {
//左括号直接进栈;右括号若与栈顶失配,则表达式必不匹配
case '(':
case '[':
case '{':
stack.push(exp[i]);
break;
case ')':
if ((stack.isEmpty()) || ('(' != stack.pop())) return false;
break;
case ']':
if ((stack.isEmpty()) || ('[' != stack.pop())) return false;
break;
case '}':
if ((stack.isEmpty()) || ('{' != stack.pop())) return false;
break;
default:
break;
//非括号字符一律忽略
}
}
return stack.isEmpty();
}
public void trim(char[] exp,int lo,int hi){
while((lo <= hi) && (exp[lo] != '(') && (exp[lo] != ')')) lo++;
while((lo <= hi) && (exp[hi] != '(') && (exp[hi] != ')')) lo--;
}
public int divide(char[] exp,int lo,int hi){
int mi = lo;
int crc = 1;
while ((0 < crc) && (++mi < hi)){
if (exp[mi] == ')') crc--;
if (exp[mi] == '(') crc++;
}
return mi;
}
public Boolean paren2(char[] exp,int lo,int hi){
//有一点问题
trim(exp,lo,hi);
if (lo > hi) return true;
if (exp[lo] != '(') return false;
if (exp[hi] != ')') return false;
int mi = divide(exp,lo,hi);
if (mi > hi) return false;
return paren2(exp,lo + 1,mi -1) && paren2(exp,mi + 1, hi);
}
栈混洗(与括号匹配类似)
存在一个特点
延迟缓冲
进行表达式的计算
package com.atguigu.shed;
import org.junit.Test;
import java.util.ArrayList;
/**
* @anthor shkstart
* @create 2020-07-30 8:43
*/
public class mulate {
@Test
public void test1() {
String str = "(11*15+26)-3";
char[] ch = str.toCharArray();
ArrayList sb = new ArrayList();
System.out.println(evaluate1(ch,sb));
}
//通过设置二维数组,对各种比较的优先级作出判定
public static int optrtwo(char op) {
switch (op) {
case '+':
return 0; //加
case '-':
return 1; //减
case '*':
return 2; //乘
case '/':
return 3; //除
case '^':
return 4; //乘方
case '!':
return 5; //阶乘
case '(':
return 6; //左括号
case ')':
return 7; //右括号
case '':
return 8; //起始符与终止符
default:
System.exit(1); //未知运算符
}
return -1;
}
static char[][] pri = {
{'>', '>', '<', '<', '<', '<', '<', '>', '>'},
{'>', '>', '<', '<', '<', '<', '<', '>', '>'},
{'>', '>', '>', '>', '<', '<', '<', '>', '>'},
{'>', '>', '>', '>', '<', '<', '<', '>', '>'},
{'>', '>', '>', '>', '>', '<', '<', '>', '>'},
{'>', '>', '>', '>', '>', '>', ' ', '>', '>'},
{'<', '<', '<', '<', '<', '<', '<', '=', ' '},
{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
{'<', '<', '<', '<', '<', '<', '<', ' ', '='}
};
public char orderBetween(char op1, char op2) {
return pri[optrtwo(op1)][optrtwo(op2)];
}
//判断是否是数字,并进行正确的读取
public static boolean isdigit(char[] S, int i) {
if (('0' <= S[i]) && (S[i] <= '9')) {
return true;
}
return false;
}
public int readNumber(char[] S, Stack<Float> stk, int i) {
stk.push((float) (S[i] - 48));//当前数位对应的数值进栈
while (isdigit(S, ++i)) {
stk.push(stk.pop() * 10 + (float) (S[i] - 48));
}
if ('.' != S[i]) return i;
float fraction = 1; //否则,意味着还有小数部分
while (isdigit(S, ++i)) //逐位加入
stk.push(stk.pop() + ((float) (S[i] - 48)) * (fraction /= 10)); //小数部分
return i;
}
//两种计算的情况:阶乘或其他简单运算
public float calcu1(float num) {
float sum = 1;
if (num == 1) {
return 1;//根据条件,跳出循环
} else {
sum = (num * calcu1(num - 1));//运用递归计算
return sum;
}
}
public float calcu2(float num1, char op, float num2) {
switch (op) {
case '+':
return num1 + num2;
case '-':
return num1 - num2;
case '*':
return num1 * num2;
case '/':
return num1 / num2;
case '^':
return (int) num1 ^ (int) num2;
default:
System.exit(1);
}
return num1;
}
//通过表达式实现对于算式的计算
public float evaluate1(char[] S, ArrayList list) {
int i = 0;
Stack<Float> opnd = new Stack();
Stack<Character> optr = new Stack();
optr.push('');
while (!optr.isEmpty()) {
if (isdigit(S, i)) {
i = readNumber(S, opnd, i);
list.add(opnd.peek());
} else {
switch (orderBetween(optr.peek(), S[i])) {
case '<': //栈顶运算符优先级更低时
optr.push(S[i]);
i++; //计算推迟,当前运算符进栈
break;
case '=': //优先级相等(当前运算符为右括号或者尾部哨兵'')时
optr.pop();
i++; //脱括号并接收下一个字符
break;
case '>': { //栈顶运算符优先级更高时,可实现相应计算,并将结果重新入栈
char op = optr.pop(); //栈顶运算符出栈
list.add(op);
if ('!' == op) { //若属于一元运算符
float pOpnd = opnd.pop();
opnd.push(calcu1(pOpnd)); //实斲一元计算,结枅入栈
} else { //对亍其它(二元)运算符
float pOpnd2 = opnd.pop();
float pOpnd1 = opnd.pop(); //
opnd.push(calcu2(pOpnd1, op, pOpnd2));
}
break;
}
default:
System.exit(1);
}
}
}
System.out.println(list);
return opnd.pop();
}
}
逆波兰表达式
手动整理
代码实现
package com.atguigu.shed;
/**
* @anthor shkstart
* @create 2020-07-30 16:18
*/
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
/*
* 求解逆波兰表达式
* */
public class PolandNotation {
public static void main(String[] args) {
/*
//计算后续表达式
String lastExpersion = "1 2 + 5 * 6 -";
//将lastExpersion分解成单个字符并存入arraylist数组中
List<String> list = transferArrayList(lastExpersion);
//遍历list集合进行对应的计算操作
int res = 0;
res = calculator(list);
System.out.println(lastExpersion + "=" + res);*/
//中序转后序
String expersion = "1+((2+3)*4)-5";
List<String> infixExpersion = new ArrayList();
infixExpersion = toInfixExpersionList(expersion);
System.out.println("中缀表达式:"+infixExpersion);
List<String> suffixExpersion = new ArrayList();
suffixExpersion = parseSuffixExpersion(infixExpersion);
System.out.println("后缀表达式:"+suffixExpersion);
System.out.println("expersion="+calculator(suffixExpersion));
}
/*
将中序表达式的list转为后序表达式的list
准备一个栈s1,ArrayList集合s2
* 1.遍历中序表达式
2.如果是操作数直接入s2
3.如果是括号:
左括号(:直接入s1
右括号):将s1栈顶元素依次出栈然后放入s2直至栈顶为(为止
4.如果是操作符
s1为空则存入s1
栈顶值为(则如s1
否则
优先级如果大于栈顶运算符直接入s1
优先级如果小于等于栈顶运算符则将s1的栈顶运算符加到s2中然后再次进行4操作
* */
private static List<String> parseSuffixExpersion(List<String> list) {
Stack<String> s1 = new Stack<>();
List<String> s2 = new ArrayList<>();
for (String oper : list) {
if (oper.matches("d+")) {
//如果是操作数;
s2.add(oper);
} else if (oper.equals("(")) {
s1.push(oper);
} else if (oper.equals(")")) {
while (!s1.peek().equals("(")) {
s2.add(s1.pop());
}
s1.pop();
//将"( "出栈
} else {
//是操作符,当oper的优先级大于栈顶时将oper加入s1,否者将s1栈顶出栈加入s2并循环判断
while (s1.size() != 0 && getPriority(s1.peek().charAt(0)) >= getPriority(oper.charAt(0))) {
s2.add(s1.pop());
}
s1.push(oper);
}
}
while (s1.size() != 0) {
s2.add(s1.pop());
}
return s2;
}
//进行逆波兰表达式的运算规则
//从左至右扫描逆波兰表达式
//1.如果是操作数就进栈
//2.如果是操作符,就将两个操作数出栈进行运算
private static int calculator(List<String> list) {
if (list == null) {
throw new RuntimeException("集合为空");
}
Stack<String> stack = new Stack<>();
for (String oper : list) {
//如果oper是操作数则入栈
if (oper.matches("d+")) {
stack.push(oper);
} else {
//oper是字符则将两个书pop出
int num2 = Integer.parseint(stack.pop());
int num1 = Integer.parseint(stack.pop());
int res = 0;
//实际计算操作
res = doCalculator(num1, num2, oper);
stack.push("" + res);
}
}
return Integer.parseint(stack.pop());
}
//进行实际的计算处理
private static int doCalculator(int num1, int num2, String oper) {
char c = oper.charAt(0);
int res = 0;
switch (c) {
case '+':
res = num1 + num2;
break;
case '-':
res = num1 - num2;
break;
case '*':
res = num1 * num2;
break;
case '/':
if (num1 == 0) {
throw new RuntimeException("被除数不能为0");
}
res = num1 / num2;
break;
default:
System.out.println("参数有误");
break;
}
return res;
}
//将逆波兰表达式逐个存入list集合中
private static List transferArrayList(String lastExpersion) {
if (lastExpersion == "") {
System.out.println("逆波兰表达式为空!!");
return null;
}
String[] operArr = lastExpersion.split(" ");
//如果最后一位不是操作符而是操作数则表达式错误
if (operArr[operArr.length - 1].matches("d+")) {
throw new RuntimeException("逆波兰表达式有误,最后一位应该为操作符");
}
List<String> list = new ArrayList<String>();
for (String str : operArr) {
list.add(str);
}
return list;
}
//将中序表达式装入ArrayList中
public static List<String> toInfixExpersionList(String s) {
if (s == "") {
throw new RuntimeException("中序表达式不能为空!!");
}
int index = 0;
//相当于一个指针用于遍历s
char oper = ' ';
//用于存储s中index索引处的字符
List<String> list = new ArrayList<String>();
String str = "";
//用于处理多位数
do {
if ((oper = s.charAt(index)) < 48 || (oper = s.charAt(index)) > 57) {
//当前字符是非数字
list.add("" + oper);
index++;
} else {
str = "";
//当前字符为操作数,要判断是不是多位数
while (index < s.length() && (oper = s.charAt(index)) >= 48 && (oper = s.charAt(index)) <= 57) {
str += oper;
//拼接;
index++;
}
list.add(str);
}
}
while (index < s.length());
return list;
}
//得到操作符的优先级
public static int getPriority(int ch) {
if (ch == '+' || ch == '-') {
return 0;
} else if (ch == '*' || ch == '/') {
return 1;
} else {
return -1;
}
}
}
队列与实现
与栈一样,队列(queue)也是存放数据对象的一种容器,其中的数据对象也按线性的逻辑
次序排列。队列结构同样支持对象的插入和删除,但两种操作的范围分别被限制于队列的两端
若约定新对象只能从某一端插入其中,则只能从另一端删除已有的元素。允许取出元素的一
端称作队头(front),而允许插入元素的另一端称作队尾(rear)。
队列的实现
通过对List的继承
c++实现
java实现
public class Quene<T> extends List_DLNode<T> {
private Quene<T> qu;
public Quene() {
}
public Quene(Quene<T> qu) {
this.qu = qu;
}
public void enqueue(T e){
insertLast(e);
}
public T dequeue() throws ExceptionPositionInvalid {
return removeFirst();
}
public T front() throws ExceptionListEmpty {
return (T) first();
}
}
队列的应用
循环分配器
RoundRobin {
//循环分配器
Queue Q(clients);
//参不资源分配癿所有客户组成队列Q
while (!ServiceClosed()) {
//在服务兲闭乀前,反复地
e = Q.dequeue();
//队首癿客户出队,幵
serve(e);
//接叐服务,然后
Q.enqueue(e);
//重新入队
}
}
银行服务模拟
struct Customer {
int window;
unsigned int time;
}
;
//顺客类:所属窗口(队列)、服务时长
void simulate(int nWin, int servTime) {
//按指定窗口数、服务总时间模拟银行业务
Queue<Customer>* windows = new Queue<Customer>[nWin];
//为殏一窗口创建一个队列
for (int now = 0; now < servTime; now++) {
//在下班乀前,殏隑一个单位时间
if (rand() % (1 + nWin)) {
//新顺客以nWin/(nWin + 1)癿概率刡达
Customer c ;
c.time = 1 + rand() % 98;
//新顺客刡达,服务时长随机确定
c.window = bestWindow(windows, nWin);
//找出最佳(最短)癿服务窗口
windows[c.window].enqueue(c);
//新顺客加入对应癿队列
}
for (int i = 0; i < nWin; i++) //分删检查
if (!windows[i].empty()) //各非空队列
if (-- windows[i].front().time <= 0) //队首顺客癿服务时长减少一个单位
windows[i].dequeue();
//服务完毕癿顺客出列,由后继顺客接替
}
//while
delete [] windows;
//释放所有队列(此前,~List()会自劢清空队列)
}