栈
基础概念
定义
栈是一种基于先进后出(FILO) 的数据结构,是一种只能在一端进行插入和删除操作的特殊线性表。它按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。
我们称数据进入到栈的动作为压栈,数据从栈中出去的动作为弹栈。
图示
代码实现
栈的API设计
代码实现
package com.study.stack;
import java.util.Iterator;
// 使用链表实现栈
public class stackAPI<T> implements Iterable<T>{
private Node head; // 头结点
private int N;// 栈的大小
class Node {
T item;// 结点存储的数据
Node next;// 下一个结点
public Node(T item, Node next) {
this.item = item;
this.next = next;
}
}
// 通过构造函数初始化头结点和栈结点
public stackAPI() {
this.head = new Node(null, null);
this.N = 0;
}
// 栈的大小
public int size() {
return N;
}
// 栈是否为空
public boolean isEmpty() {
return N == 0;
}
// 压栈
public void push(T t) {
// 找到头结点指向的第一个结点
Node oldFirst = head.next;
// 创建新节点
Node newNode = new Node(t, null);
//头结点指向新节点
head.next = newNode;
// 新节点指向与原来的第一个结点
newNode.next = oldFirst;
// 栈的大小+1
N++;
}
// 弹栈
public T pop() {
// 找到头结点指向的第一个结点
Node oldFirst = head.next;
// 栈为空的情况
if (oldFirst==null){
return null;
}
// 栈不为空
// 头结点指向原来第一个结点的下一个结点
head.next = oldFirst.next;
// 栈的大小-1
N--;
return oldFirst.item;
}
// 遍历栈 实现Iterable接口
@Override
public Iterator<T> iterator() {
return new SIterator();
}
private class SIterator implements Iterator{
private Node n;
public SIterator(){
this.n = head;
}
@Override
public boolean hasNext() {
return n.next!=null;
}
@Override
public Object next() {
n = n.next;
return n.item;
}
}
}
测试
package com.study.stack;
public class stackTest {
public static void main(String[] args) {
// 创建一个栈
stackAPI<String> stack = new stackAPI<>();
// 测试压栈
stack.push("a");
stack.push("b");
stack.push("c");
stack.push("d");
System.out.println("栈中的数据为:");
for (String s : stack) {
System.out.println(s);
}
System.out.println("栈的大小为:"+stack.size());
// 测试弹栈
String pop = stack.pop();
System.out.println("弹出的元素是:"+pop);
System.out.println("弹出元素后,栈的大小为:"+stack.size());
}
}
结果
一些关于栈的经典问题
匹配括号问题
问题描述:给定一个字符串,里边可能包含"()"小括号和其他字符,请编写程序检查该字符串的中的小括号是否成对出现。
思路:
- 扫描字符串,左括号进栈,遇到右括号的话,判断当前栈内是否有左括号可以出栈,有就出栈,没有的话,直接返回括号不匹配。
- 扫描完字符串,查看栈的大小,如果栈的大小为0,就说明匹配,反之就是不匹配
代码实现:
package com.study.stack;
// 给定一个字符串,里边可能包含"()"小括号和其他字符,请编写程序检查该字符串的中的小括号是否成对出现。
public class stackProblem {
public static void main(String[] args) {
String s = "()(hello)(hi))";
boolean match = match(s);
System.out.println("字符串"+s+"中的括号是否匹配:"+match);
}
// 括号匹配
public static boolean match(String s){
// 创建栈,存放括号
stackAPI<String> stack = new stackAPI<>();
// 遍历字符串,如果是左括号就入栈,右括号就出栈
for (int i = 0; i < s.length(); i++) {
String str = s.charAt(i)+"";
if (str.equals("(")){
stack.push(str);
}
if (str.equals(")")){
if (stack.size()==0){
return false;
}
stack.pop();
}
}
// 通过栈的大小是否为0来判断是否匹配
return stack.size()==0;
}
}
逆波兰表达式
问题描述:给定一个只包含加减乘除四种运算的逆波兰表达式的数组表示方式,求出该逆波兰表达式的结果。
思路:
- 遍历数组,数字入栈,符号出栈计算,一次性出栈两个,计算结果再入栈
- (这是建立在给定的逆波兰表达式是正确的情况下)
代码实现
package com.study.stack;
// 给定一个只包含加减乘除四种运算的逆波兰表达式的数组表示方式,求出该逆波兰表达式的结果。
public class stackProblem2 {
public static void main(String[] args) {
// 中缀表达式:3*(17-15)+18/6
String[] s = {"3","17","15","-","*","18","6","/","+"};
int result = getResult(s);
System.out.println("计算的结果是:"+result);
}
// 1. 创建栈对象
// 2. 遍历数组,数字入栈,
// 3. 符号出栈计算,一次性出栈两个,计算结果再入栈
public static int getResult(String[] s){
stackAPI<String> stack = new stackAPI<>();
for (int i = 0; i < s.length; i++) {
if (!s[i].equals("+")&&!s[i].equals("-")&&!s[i].equals("*")&&!s[i].equals("/")){
stack.push(s[i]);
}else if (s[i].equals("+")){
int m = Integer.parseInt(stack.pop());
int n = Integer.parseInt(stack.pop());
String s1 = String.valueOf(n + m);
stack.push(s1);
}else if (s[i].equals("-")){
int m = Integer.parseInt(stack.pop());
int n = Integer.parseInt(stack.pop());
String s1 = String.valueOf(n-m);
stack.push(s1);
}else if (s[i].equals("*")){
int m = Integer.parseInt(stack.pop());
int n = Integer.parseInt(stack.pop());
String s1 = String.valueOf(n*m);
stack.push(s1);
}else if (s[i].equals("/")){
int m = Integer.parseInt(stack.pop());
int n = Integer.parseInt(stack.pop());
String s1 = String.valueOf(n/m);
stack.push(s1);
}
}
return Integer.parseInt(stack.pop());
}
}
队列
基础概念
定义
队列是一种基于先进先出(FIFO) 的数据结构,是一种只能在一端进行插入,在另一端进行删除操作的特殊线性表,它按照先进先出的原则存储数据,先进入的数据,在读取数据时先读被读出来。
图示
代码实现
队列的API设计
代码实现
package com.study.queue;
import java.util.Iterator;
public class queueAPI<T> implements Iterable<T>{
private Node head; //头结点
private Node last; //尾结点
private int N;//元素个数
private class Node{
public T item;
public Node next;
public Node(T item, Node next) {
this.item = item;
this.next = next;
}
}
public queueAPI() {
this.head = new Node(null,null);
this.last = null;
this.N = 0;
}
// 判断队列是否为空
public boolean isEmpty(){
return N==0;
}
// 求队列的大小
public int size(){
return N;
}
//向队列中插入值
public void enqueue(T t){
if (last==null){//last为空,说明此时队列为空
last = new Node(t, null);
head.next=last;
}else { // 队列不为空,要更换last为新创建的结点
Node oldLast = last;
last = new Node(t, null);
oldLast.next=last;
}
// 元素加一
N++;
}
// 向队列中取值
public T dequeue(){
if (isEmpty()){
return null;
}
Node first = head.next;
head.next = first.next;
N--;
if (isEmpty()){
last=null;
}
return first.item;
}
// 遍历队列
@Override
public Iterator<T> iterator() {
return new QIterator();
}
private class QIterator implements Iterator{
private Node node;
public QIterator(){
this.node = head;
}
@Override
public boolean hasNext() {
return node.next!=null;
}
@Override
public Object next() {
node = node.next;
return node.item;
}
}
}
测试
package com.study.queue;
public class queueTest {
public static void main(String[] args) {
queueAPI<String> q = new queueAPI<>();
q.enqueue("1");
q.enqueue("2");
q.enqueue("3");
q.enqueue("4");
q.enqueue("5");
System.out.println("队列的长度为:"+q.size());
System.out.println("队列存储的数据为:");
for (String s : q) {
System.out.println(s);
}
String dequeue = q.dequeue();
System.out.println("出队的元素为:"+dequeue);
System.out.println("出队后的元素总数为:"+q.size());
}
}
结果