数据结构-栈与队列
- 相比于数组这种存储数据的数据,栈(Stock)和队列(Queue)主要作用是在程序中作为构思算法的辅助工具,是一种程序员开发过程中的便利工具。Stock和Queue具有访问受限以及更加抽象的特征。
一、栈
栈只允许访问最后一个插入的元素,即栈是先进后出(FILO)的一种数据结构。栈主要提供的算法包括push,pop和peek。其中push是插入一个元素,pop是弹出最近添加的一个元素,peek是返回最近添加的一个元素。
栈的底层实现可以是数组,也可以是链表,这里采用数组实现一个栈,代码如下:
1 public class Stock1<T> { 2 private static final int EMPTY_TOP = -1; 3 private int maxSize; 4 private Object[] stockArray; 5 private int top; 6 7 public Stock1() { 8 this(16); 9 } 10 11 public Stock1(int initialCapacity) { 12 if (initialCapacity < 0) { 13 throw new IllegalArgumentException("The initialCapacity value must > 0, this value is:" + initialCapacity); 14 } 15 16 this.maxSize = initialCapacity; 17 this.top = EMPTY_TOP; 18 this.stockArray = new Object[this.maxSize]; 19 } 20 21 public void push(T value) { 22 this.checkIndex(this.top + 1); 23 this.stockArray[++this.top] = value; 24 } 25 26 @SuppressWarnings("unchecked") 27 public T pop() { 28 this.checkIndex(this.top); 29 return ((T) this.stockArray[this.top--]); 30 } 31 32 @SuppressWarnings("unchecked") 33 public T peek() { 34 this.checkIndex(this.top); 35 return (T) this.stockArray[this.top]; 36 } 37 38 public boolean isEmpty() { 39 return this.top == EMPTY_TOP; 40 } 41 42 public boolean isFull() { 43 return this.top == this.maxSize - 1; 44 } 45 46 public int size() { 47 return this.top + 1; 48 } 49 50 private void checkIndex(int index) { 51 if (index < 0 || index >= maxSize) { 52 throw new IllegalArgumentException("The index is error, index:=" + index + ", the max index is:" + (maxSize - 1)); 53 } 54 } 55 56 @Override 57 public String toString() { 58 StringBuffer sb = new StringBuffer(this.maxSize); 59 for (int i = this.top; i >= 0; i--) { 60 sb.append(this.stockArray[i]).append(","); 61 } 62 return "[" + sb.substring(0, Math.max(sb.length() - 1, 0)) + "]"; 63 } 64 }
二、队列
队列和栈一样,也只允许访问一个元素,但是这个元素是最早插入的,即队列是一个先进先出(FIFO)的数据结构。队列主要提供的算法包括insert,remove和peek。其中insert是在队列的尾部插入一个元素;remove是在队列的头部删除一个元素,并返回;peek是返回队列头部的元素的值。
队列的底层实现和栈一样,可以是数组或者是链表,代码如下:
1 public class Queue1<T extends Comparable<T>> { 2 private static int EMPTY_INDEX = 0; 3 protected final int maxSize; 4 protected final Comparable<T>[] queueArray; 5 protected int head; 6 protected int tail; 7 protected int size; 8 9 @SuppressWarnings("unchecked") 10 public Queue1(int initialCapacity) { 11 if (initialCapacity < 0) { 12 throw new IllegalArgumentException("The initialCapacity value must > 0, this value is:" + initialCapacity); 13 } 14 15 this.maxSize = initialCapacity; 16 this.queueArray = (Comparable<T>[]) Array.newInstance(Comparable.class, this.maxSize); 17 this.head = EMPTY_INDEX; 18 this.tail = EMPTY_INDEX; 19 this.size = 0; 20 } 21 22 public void push(T value) { 23 if (isFull()) { 24 throw new IllegalArgumentException("The queue is full, can't push value to this queue."); 25 } else { 26 this.size++; 27 this.queueArray[(this.tail++) % this.maxSize] = value; 28 } 29 } 30 31 @SuppressWarnings("unchecked") 32 public T pop() { 33 if (isEmpty()) { 34 throw new IllegalArgumentException("The queue is empty, can't pop value."); 35 } else { 36 this.size--; 37 return (T) this.queueArray[(this.head++) % this.maxSize]; 38 } 39 } 40 41 @SuppressWarnings("unchecked") 42 public T peek() { 43 if (isEmpty()) { 44 throw new IllegalArgumentException("The queue is empty, can't peek value."); 45 } else { 46 return (T) this.queueArray[this.head % this.maxSize]; 47 } 48 } 49 50 public boolean isEmpty() { 51 return this.size == 0; 52 } 53 54 public boolean isFull() { 55 return this.size == this.maxSize; 56 } 57 58 public int size() { 59 return this.size; 60 } 61 }
这段代码中我将insert方法换成了push方法,remove方法换成了pop方法。
三、优先级队列
优先级队列和普通队列类似,区别在于在remove元素的时候,返回的元素值是按照某种顺序来定义的。实现优先级队列有两种方式:
第一种,在插入元素的时候,按顺排列。即每次插入的时候,都会将要插入的元素插入到对应位置,并移动其他元素。此时插入的时间复杂度是O(n2),但是这样删除的代码和普通队列一样。
第二中,在插入元素的时候,直接将元素填充到队列尾部,但是在删除的时候,search全部数据,选择出要删除的数据出来。此时插入时间复杂度O(1),删除复杂度为O(n2)。
我们采用第一种方式来实现一个队列,针对插入数据慢的问题,我们可以采用堆来解决这个问题,以后再说。代码如下:
第一种代码是继承普通队列,只修改push方法:
1 public class PriorityQueue1<T extends Comparable<T>> extends Queue1<T> { 2 public PriorityQueue1(int initialCapacity) { 3 super(initialCapacity); 4 } 5 6 public void push(T value) { 7 if (isFull()) { 8 throw new IllegalArgumentException("This queue is full"); 9 } else { 10 int index = super.head; 11 for (int i = 0; i < super.size; i++) { 12 int compareResult = super.queueArray[index++ % super.maxSize].compareTo(value); 13 if (compareResult >= 0) { 14 index--; 15 break; 16 } 17 } 18 19 for (int i = super.tail; i > index; i--) { 20 super.queueArray[i % super.maxSize] = super.queueArray[(i - 1) % super.maxSize]; 21 } 22 23 super.queueArray[index] = value; 24 super.tail++; 25 super.size++; 26 } 27 } 28 }
第二种是不继承普通队列,自己实现:
1 public class PriorityQueue2<T extends Comparable<T>> { 2 protected final int maxSize; 3 protected int nItems; 4 protected Comparable<T>[] basicArray; 5 6 @SuppressWarnings("unchecked") 7 public PriorityQueue2(int initialCapacity) { 8 if (initialCapacity < 0) { 9 throw new IllegalArgumentException("The initialCapacity value must > 0, this value is:" + initialCapacity); 10 } 11 12 this.maxSize = initialCapacity; 13 this.basicArray = (Comparable<T>[]) Array.newInstance(Comparable.class, this.maxSize); 14 this.nItems = 0; 15 } 16 17 public boolean isEmpty() { 18 return this.nItems == 0; 19 } 20 21 public boolean isFull() { 22 return this.nItems == this.maxSize; 23 } 24 25 public int size() { 26 return this.nItems; 27 } 28 29 public void push(T value) { 30 if (this.isFull()) { 31 throw new IllegalArgumentException("This queue is full, can't push value."); 32 } else { 33 int index = 0; 34 for (; index < this.nItems; index++) { 35 if (this.basicArray[index].compareTo(value) <= 0) { 36 break; 37 } 38 } 39 40 for (int j = this.nItems; j > index; j--) { 41 this.basicArray[j] = this.basicArray[j - 1]; 42 } 43 44 this.basicArray[index] = value; 45 this.nItems++; 46 } 47 } 48 49 @SuppressWarnings("unchecked") 50 public T pop() { 51 if (this.isEmpty()) { 52 throw new IllegalArgumentException("This queue is empty, can't pop value."); 53 } else { 54 return (T) this.basicArray[--nItems]; 55 } 56 } 57 58 @SuppressWarnings("unchecked") 59 public T peek() { 60 if (this.isEmpty()) { 61 throw new IllegalArgumentException("This queue is empty, can't peek value."); 62 } else { 63 return (T) this.basicArray[this.nItems - 1]; 64 } 65 } 66 }
四、其他代码
1.解析表达式,将中缀表达式转换成后缀表达式
1 public class InToPost { 2 private Stock1<Character> stock; 3 private Queue1<String> queue; 4 private String input; 5 private StringBuffer sb; 6 7 public InToPost(String input) { 8 if (input != null && input.length() != 0) { 9 this.input = input.trim(); 10 } else { 11 this.input = ""; 12 } 13 14 this.stock = new Stock1<>(this.input.length()); 15 this.sb = new StringBuffer(this.input.length()); 16 this.queue = new Queue1<>(this.input.length()); 17 } 18 19 public String getInput() { 20 return input; 21 } 22 23 public String getOutput() { 24 return this.sb.toString(); 25 } 26 27 public String doTranshi() { 28 StringBuffer tmp = new StringBuffer(); 29 30 for (int j = 0; j < this.input.length(); j++) { 31 char ch = this.input.charAt(j); 32 switch (ch) { 33 case '+': 34 case '-': 35 pushValueToQueue(tmp.toString()); 36 sb.append(tmp.toString()); 37 tmp = new StringBuffer(); 38 gotPot(ch, 1); 39 break; 40 41 case '*': 42 case '/': 43 pushValueToQueue(tmp.toString()); 44 sb.append(tmp.toString()); 45 tmp = new StringBuffer(); 46 gotPot(ch, 2); 47 break; 48 49 case '(': 50 this.stock.push(ch); 51 break; 52 53 case ')': 54 pushValueToQueue(tmp.toString()); 55 sb.append(tmp.toString()); 56 tmp = new StringBuffer(); 57 while (!this.stock.isEmpty()) { 58 char chx = this.stock.pop(); 59 if (chx == '(') { 60 break; 61 } else { 62 sb.append(chx); 63 pushValueToQueue(String.valueOf(chx)); 64 } 65 } 66 break; 67 68 default: 69 tmp.append(ch); 70 break; 71 } 72 } 73 74 pushValueToQueue(tmp.toString()); 75 sb.append(tmp.toString()); 76 77 while (!this.stock.isEmpty()) { 78 char ch = this.stock.pop(); 79 this.sb.append(ch); 80 pushValueToQueue(String.valueOf(ch)); 81 } 82 83 return this.getOutput(); 84 } 85 86 private void gotPot(char ch, int prec) { 87 while (!this.stock.isEmpty()) { 88 char chx = this.stock.pop(); 89 if (chx == '(') { 90 this.stock.push(chx); 91 break; 92 } else { 93 int prec2 = 2; 94 if (chx == '+' || chx == '-') { 95 prec2 = 1; 96 } 97 if (prec2 >= prec) { 98 sb.append(chx); 99 pushValueToQueue(String.valueOf(chx)); 100 } else { 101 this.stock.push(chx); 102 break; 103 } 104 } 105 } 106 this.stock.push(ch); 107 } 108 109 private void pushValueToQueue(String value) { 110 if (value == null || value.trim().length() == 0) { 111 return; 112 } 113 this.queue.push(value.trim()); 114 } 115 116 public Queue1<String> getQueue() { 117 return this.queue; 118 } 119 } 120 121 class InToPostUtil { 122 public static String doTranshi(String line) { 123 return new InToPost(line).doTranshi(); 124 } 125 } 126 127 class InToPostApp { 128 public static void main(String[] args) { 129 System.out.println(InToPostUtil.doTranshi(null)); 130 System.out.println(InToPostUtil.doTranshi("")); 131 System.out.println(InToPostUtil.doTranshi(" ")); 132 System.out.println(InToPostUtil.doTranshi("a*b+c")); 133 System.out.println(InToPostUtil.doTranshi("a*(b+c)")); 134 System.out.println(InToPostUtil.doTranshi("a*(b+c*d)")); 135 System.out.println(InToPostUtil.doTranshi("a*(b+c*d/(e-f*g))")); 136 } 137 }
2.计算后缀表达式的值
1 package com.augmentum.gby.ds; 2 3 public class ParsePost { 4 private String line; 5 private InToPost inToPost; 6 7 public ParsePost(String line) { 8 this.line = line; 9 this.inToPost = new InToPost(this.line); 10 } 11 12 public double doParse() { 13 String reline = this.inToPost.doTranshi(); 14 if (reline == null || reline.length() == 0) { 15 throw new IllegalArgumentException("The input line value is empty. can't calc those value."); 16 } else { 17 Stock1<Double> stock = new Stock1<>(reline.length()); 18 Queue1<String> queue = this.inToPost.getQueue(); 19 while (!queue.isEmpty()) { 20 String value = queue.pop(); 21 if ("+".equals(value) || "-".equals(value) || "*".equals(value) || "/".equals(value)) { 22 double num1 = stock.pop(); 23 double num2 = stock.pop(); 24 switch (value.charAt(0)) { 25 case '+': 26 stock.push(num1 + num2); 27 break; 28 case '-': 29 stock.push(num2 - num1); 30 break; 31 case '*': 32 stock.push(num2 * num1); 33 break; 34 case '/': 35 stock.push(num2 / num1); 36 break; 37 } 38 } else { 39 stock.push(Double.valueOf(value)); 40 } 41 } 42 return stock.pop(); 43 } 44 } 45 } 46 47 class ParsePostUtil { 48 public static double doParse(String line) { 49 return new ParsePost(line).doParse(); 50 } 51 } 52 53 class ParsePostApp { 54 public static void main(String[] args) { 55 System.out.println(InToPostUtil.doTranshi("2*((3+4)*5+5)")); 56 System.out.println(InToPostUtil.doTranshi("2*3+4") + "; The result is:" + ParsePostUtil.doParse("2*3+4")); 57 System.out.println(InToPostUtil.doTranshi("2*(3+4)") + "; The result is:" + ParsePostUtil.doParse("2*(3+4)")); 58 System.out.println(InToPostUtil.doTranshi("2*(3+4*5)") + "; The result is:" + ParsePostUtil.doParse("2*(3+4*5)")); 59 System.out.println(InToPostUtil.doTranshi("2*3+4*5") + "; The result is:" + ParsePostUtil.doParse("2*3+4*5")); 60 System.out.println(InToPostUtil.doTranshi("2*((3+4)*5+5)") + "; The result is:" + ParsePostUtil.doParse("2*((3+4)*5+5)")); 61 System.out.println(InToPostUtil.doTranshi("2*((3+4)*5+9)") + "; The result is:" + ParsePostUtil.doParse("2*((3+4)*5+9)")); 62 System.out.println(InToPostUtil.doTranshi("20*3+4") + "; The result is:" + ParsePostUtil.doParse("20*3+4")); 63 System.out.println(InToPostUtil.doTranshi("20.5*4+4") + "; The result is:" + ParsePostUtil.doParse("20.5*4+4")); 64 } 65 }