一.栈
1.栈(stack)又名堆栈,仅允许在表的一端(栈顶)进行插入和删除。向一个栈插入新元素称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。由于栈的这种特性,所以栈内元素是先进后出,即先进栈的元素,后出栈。
2.栈的操作。
1)Push(入栈)
a.向栈中插入一个元素,插入前判断栈是否已满,满了则抛异常。
b.如果栈未满,栈顶指针top先+1,然后再插入元素,否则将覆盖之前的栈顶。
2)Pop(出栈)
a.移除栈顶元素,移除前先判断栈是否为空,空了则抛异常。
b.移除栈顶指针top所指元素,再top-1,它与入栈操作刚好相反。
3)Peek(查看)
a.只能查看栈顶元素,查看前先判断栈是否为空,空了给相应提示信息。
3.栈的实现(数组)
public class MyStack {
//栈的最多容量
private int maxSize;
//栈顶指针
private int top;
//栈
private char[] stackArray;
public MyStack(int size) {
maxSize = size;
stackArray = new char[size];
top = -1;
}
public void push(char value) {
//栈在不满的情况下才能插入
if(!this.isFull()) {
//先++,再赋值
stackArray[++top] = value;
}
}
public char pop() {
//栈在不空的情况下才能移除,否则返回空
if(!this.isEmpty()) {
//先清除栈顶元素,再--
return stackArray[top--];
}
return 0;
}
public char peek() {
//栈在不空的情况下才能查询栈顶元素,否则返回空
if(!this.isEmpty()) {
return stackArray[top];
}
return 0;
}
public boolean isEmpty() {
return (top == -1);
}
public boolean isFull() {
return (top == maxSize -1);
}
@Override
public String toString() {
return "MyStack [maxSize=" + maxSize + ", top=" + top + ", stackArray=" + Arrays.toString(stackArray) + "]";
}
}
4.栈的实例1——单词逆序
由于栈先进后出的特性,单词逆序正是一个很应景的实例。
public class TestReverse {
@org.junit.Test
public void testMyStack() {
String target = "你好,我叫黄国攀";
MyStack stack = new MyStack(target.length());
for (int i = 0; i < target.length(); i++) {
stack.push(target.charAt(i));
}
System.out.println(target);
for (int i = 0; i < target.length(); i++) {
System.out.print(stack.pop());
}
}
}
结果:
5.栈的实例2——匹配括号
从字符串中不断读取字符,一旦匹配到左括号如{ [(,就将该字符压入栈中,当读到右括号)] },则弹出栈顶括号,判断括号是否匹配,不匹配则报错。一直没被匹配的括号也会报错。
public class TestMatch {
@Test
public void main() {
String target = "ab{[]}]";
System.out.println("Target string: " + target);
MyStack stack = new MyStack(target.length());
for (int i = 0; i < target.length(); i++) {
char ch = target.charAt(i);
switch (ch) {
case '{':
case '[':
case '(':
stack.push(ch);
break;
case '}':
case ']':
case ')':
char chx = stack.pop();
if ((chx == '{' && ch == '}') ||
(chx == '[' && ch == ']') ||
(chx == '(' && ch == ')')) {
System.out.println(chx + "and" + ch + " Match success!");
} else {
System.out.println("Error:" + ch + " don't match " + chx + " location: " + i);
}
break;
default:
break;
}
}
}
}
结果:
二.队列
1.队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,进行插入操作的端称为队尾,进行删除操作的端称为队头。顾名思义,队列中的元素先进先出,就像排队一样。
2.队列的操作。
1)Insert(插入)
a.向队尾中插入元素,插入前判断队列是否已满,满了报异常 。
b.向队尾插入新元素,队尾指针rear+1,再插入元素。
2)Remove(移除)
a.移除队头元素,移除前判断队列是否为空,空了报异常 。
b. 移除队头元素,同时队头指针front+1。
注意:由于生活中队列都是往前移,所以这里不够直观,因为如果保持头指针不动的话,就需求移动后面的元素,这样效率太差,所以这里头指针向上移,后面的元素就可以保持原来位置。
3)Peek(查看)
a.查看队头元素,查看前判断队列是否为空,空了返回提示信息 。
循环队列
按照队列的操作方式,很快队列就满了,rear到达最大位置,但是可能之前有移除元素,队列前已经空出来了,我们仍然无法插入元素,所以这时候需要循环队列,环绕式处理,这就需要把rear指针绕回队列的起始位置。
3.循环队列的实现
public class MyQueue {
private int maxSize;
//头指针
private int front;
//尾指针
private int rear;
//队列中的元素个数
private int nItems;
private long[] queueArray;
public MyQueue(int size) {
this.maxSize = size;
this.front = 0;
this.rear = -1;
this.nItems = 0;
queueArray = new long[size];
}
public void insert(long value) {
//队列不满才能添加元素
if(!this.isFull()) {
//为了实现循环队列
if(rear == maxSize-1) {
rear = -1;
}
queueArray[++rear] = value;
nItems++;
}
}
public long remove() {
//取出当前元素并且上移元素坐标
long temp = queueArray[front++];
if(!this.isEmpty()) {
//为实现循环队列
if(front == maxSize) {
front = 0;
}
nItems--;
return temp;
}
return 0;
}
public long peek() {
if(!this.isEmpty()) {
return queueArray[front];
}
return 0;
}
public boolean isEmpty() {
return (nItems == 0);
}
public boolean isFull() {
return (nItems == maxSize);
}
@Override
public String toString() {
return "MyQueue [maxSize=" + maxSize + ", front=" + front + ", rear=" + rear + ", nItems=" + nItems
+ ", queueArray=" + Arrays.toString(queueArray) + "]";
}
}
测试类:
public class TestQueue {
@Test
public void test() {
MyQueue queue = new MyQueue(5);
queue.insert(1);
queue.insert(2);
queue.insert(3);
queue.insert(4);
queue.insert(5);
System.out.println(queue.toString());
//移除2次队头元素
queue.remove();
queue.remove();
//查看队头元素
System.out.println(queue.peek());
System.out.println(queue.toString());
}
}
结果:
4.优先级队列。
基本属性和基本操作和普通一样,不过在优先级队列中,元素按 关键字的值有序, 这种关键字最小或最大的元素总在队头。
元素再插入的时候会按照顺序插入到合适的位置,以保证队列有序。
1)实现:
采用插入排序实现队列中元素的排序,详情可以查看我之前的博客请点击这里
public class MyPriorityQueue {
private int maxSize;
private long[] priorityQueue;
private int numItems;
public MyPriorityQueue(int size) {
this.maxSize = size;
priorityQueue = new long[size];
numItems = 0;
}
public void insert(long value) {
if(!this.isFull()) {
insertByDesc(value);
}
}
//按降序插入,队头总是最大元素
public void insertByDesc(long value) {
long temp = 0;
int index = 0;
//如果没有元素,直接插入
if(numItems == 0) {
priorityQueue[numItems++] = value;
} else {、
//这里采用插入排序,是因为之前的队列已基本有序
for (int i = numItems; i <= numItems; i++) {
index = i;
while (index > 0 && value <= priorityQueue[index-1]) {
priorityQueue[index] = priorityQueue[index-1];
index--;
}
priorityQueue[index] = value;
}
numItems++;
}
}
public long remove() {
if(!this.isEmpty()) {
return priorityQueue[--numItems];
}
return 0;
}
public long peek() {
if(!this.isEmpty()) {
return priorityQueue[numItems-1];
}
return maxSize;
}
public boolean isFull() {
return (numItems == maxSize);
}
public boolean isEmpty() {
return (numItems == 0);
}
@Override
public String toString() {
return "MyPriorityQueue [maxSize=" + maxSize + ", priorityQueue=" + Arrays.toString(priorityQueue)
+ ", numItems=" + numItems + "]";
}
}
测试类:
public class TestPriorityQueue {
@Test
public void test() {
MyPriorityQueue priorityQueue = new MyPriorityQueue(8);
priorityQueue.insert(2);
priorityQueue.insert(4);
priorityQueue.insert(7);
priorityQueue.insert(6);
priorityQueue.insert(1);
priorityQueue.insert(3);
priorityQueue.insert(5);
System.out.println(priorityQueue.peek());
System.out.println(priorityQueue.toString());
//移除2次
priorityQueue.remove();
priorityQueue.remove();
System.out.println(priorityQueue.peek());
System.out.println(priorityQueue.toString());
}
}
结果:
总结:栈和队列的学习暂告一段落,现在是基于数组的实现,以后会增加使用其他数据结构实现的实例。虽然现在可能用不到这些知识,是因为前人已经帮我们封装好了,我们不需要重复造轮子,但是以后的路还长着呢,说不定我们需要改善某个工具,或者在前人基础上DIY,所以这些基础知识也是必不可少,本着学习的态度,我们不说知其然而知其所以然,至少也应该对这些基础知识有一定的认识。
参考文献:Java数据结构和算法.(第二版)