可以使用数组线性表实现栈,使用链表实现队列。
栈可以看做是一种特殊类型的线性表,访问、插入和删除其中的元素只能在栈尾进行。用数组线性表来实现栈比用链表来实现实现效率更高
这里有两种办法可用来设计栈和队列的类。
- 使用继承:可以通过继承数组线性表类ArrayList 来定义栈类,通过继承链表类LinkedList 来定义队列类。
- 使用组合:可以将数组线性表定义为栈类中的数据域,将链表定义为队列类中的数据域,
这两种设计方法都是可行的,但是相比之下,组合可能更好一些,因为它可以定义一个全新的栈类和队列类,而不需要继承数组线性表类与链表类中不必要和不合适的方法。
1、栈
栈的典型应用
- 逆序输出Conversion
输出次序与处理过程颠倒;递归深读和输出长度不易预知 - 递归嵌套 stack permutation + parenthesis
具有自相似性的问题可递归描述,但分支位置和嵌套深度不固定 - 延迟缓冲 evaluation
线性扫描算法模式中,在预读足够长之后,方能确定可处理的前缀 - 栈式计算 RPN
基于栈结构的特定计算模式
1.1 myStack的实现
public class myStack<E> {
private java.util.ArrayList<E> list = new java.util.ArrayList<E>();
public void push(E e){
list.add(e);
}
public E pop(){
return list.remove(list.size()-1);
}
public E top(){
return list.get(list.size()-1);
}
public int getSize(){
return list.size();
}
public boolean empty(){ //if stack is empty, return true;
if(list.size() == 0) return true;
else return false;
}
@Override
public String toString() {
return "stack:" + list.toString();
}
}
1.2 栈应用:进制转换问题
逆序输出Conversion :输出次序与处理过程颠倒;递归深读和输出长度不易预知
进制转换
输入一个数,可以转换成任意的n进制。
public class convert {
public static void main(String []args){
ConvertBase(-17,2);
}
static void ConvertBase (int num , int base){
myStack s = new myStack();
char digit [] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
boolean flag = false;
if(num < 0){
flag = true;
num *= -1;
}
while (num > 0) {
s.push(digit[num % base]);
num /= base;
}
if(flag){
s.push('-');
}
for(int i = s.getSize() ; i > 0 ; i--)
System.out.print(s.pop());
}
}
1.3 栈应用:括号匹配问题
stack permutation + parenthesis
具有自相似性的问题可递归描述,但分支位置和嵌套深度不固定
括号匹配问题
:判断一个类似于“(())()()()))(((()” 是否左括号和右括号可以一一正确匹配上。
static boolean paren(String s){
myStack stack = new myStack();
for(int i=0; i < s.length(); i++){
if(s.charAt(i) == '(') stack.push(1);
else if(!stack.empty()) stack.pop();
else return false;
}
return stack.empty();
}
这个题在学有限自动机课程时也遇到过,同样的思路~~
1.4 栈应用:栈混洗问题
/*
描述
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。
假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,
但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
输入
第一行:输入N个整数,空格隔开,按先后顺序入栈
第二行:输入N个整数,空格隔开,表示一种出栈顺序
输出
True 或者 False ,意思是第二行数据是否是一个合法的弹出序列
样例输入
1 2 3 4 5
4 5 3 2 1
样例输出
True
*/
static boolean sp(){
Scanner in = new Scanner(System.in);
System.out.println("enter your number");
String s1 = in.nextLine();
String s2 = in.nextLine();
String a1[] = s1.split(" ");
String a2[] = s2.split(" ");
Stack<Integer> s = new Stack<>();
int a[] = new int[a1.length]; //a 是压入栈的顺序 从 0--a.length
int b[] = new int[a1.length]; // b 是弹出的顺序 从 0--b.length
for(int i = 0;i < a.length;i++){
a[i] = Integer.valueOf(a1[i]);
b[i] = Integer.valueOf(a2[i]);
}
int k = 0;
for(int i = 0; i < a.length; i++){
s.push(a[i]);
while( s.empty()==false && b[k] == s.peek() )
{
s.pop();
k++;
}
}
if( k == a.length && s.empty())
return true;
else
return false;
}
2、队列的实现
队列,元素只能从队列的末端插入,从开始端访问和删除。由于删除是在线性表的起始位置进行的,所以用链表实现队列比用数组线性表实现效率更高。
package ReWrite;
public class GenericQueue<E> {
// 队列:尾进前出
private java.util.LinkedList<E> list = new java.util.LinkedList<E>();
/**
* 添加一个元素到队列
* @param e
*/
public void enqueue(E e) {
list.addLast(e);
}
/**
* 从队列删除一个元素
*/
public E dequeue() {
return list.removeLast();
}
public int getSize() {
return list.size();
}
@Override
public String toString() {
return "Queue:" + list.toString();
}
}
2、栈的实现
package ReWrite;
public class GenericStack<E> {
private java.util.ArrayList<E> list = new java.util.ArrayList<E>();
/**
* 添加一个元素到队列
* @param e
*/
public void push(E e) {
list.add(e);
}
/**
* 从队列删除一个元素
*/
public E pop() {
return list.remove(list.size()-1);
}
public int getSize() {
return list.size();
}
@Override
public String toString() {
return "stack:" + list.toString();
}
}
3、测试
package ReWrite;
public class Test {
public static void main(String[] args) {
/*
GenericQueue<String> queue = new GenericQueue<String>();
queue.enqueue("tom");
queue.enqueue("Ali");
queue.enqueue("Sua");
System.out.println("(1)" + queue);
System.out.println("(1)" + queue.dequeue());
*/
GenericStack<String> stack = new GenericStack<String>();
stack.push("apple");
stack.push("orange");
stack.push("megon");
System.out.println("(1)" + stack);
System.out.println("(2)" + stack.pop());
System.out.println("(3)" + stack.pop());
System.out.println("(4)" + stack);
}
}
4、优先队列
普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除。在优先队列( priority queue) 中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。例如,医院的急救室为病人赋予优先级,具有最高优先级的病人最先得到治疗。
可以使用堆实现优先队列,其中根结点是队列中具有最高优先级的对象。
package ReWrite;
import Sort.Heap;
public class MyPriorityQueue<E extends Comparable<E>> {
private Heap<E> heap = new Heap<>();
public void enqueue(E newObject) {
heap.add(newObject);
}
public E dequeue() {
return heap.remove();
}
public int getSize() {
return heap.getSize();
}
}
测试:
package ReWrite;
public class Test {
public static void main(String[] args) {
Patient p1 = new Patient("John",2);
Patient p2 = new Patient("Jim",1);
Patient p3 = new Patient("Tim",5);
Patient p4 = new Patient("Cindy",7);
MyPriorityQueue<Patient> priorityQueue = new MyPriorityQueue<>();
priorityQueue.enqueue(p1);
priorityQueue.enqueue(p2);
priorityQueue.enqueue(p3);
priorityQueue.enqueue(p4);
while(priorityQueue.getSize()>0)
System.out.println(priorityQueue.dequeue());
}
static class Patient implements Comparable<Patient>{
private String name;
private int priority;
public Patient(String name, int priority) {
this.name = name;
this.priority = priority;
}
@Override
public String toString() {
return name + "(priority:" + priority + ")";
}
@Override
public int compareTo(Patient o) {
return this.priority - o.priority;
}
}
}