剑指Offer——栈的java实现和栈的应用举例

栈是一种先进后出的数据结构, 栈的实现如下:

首先定义了栈需要实现的接口:

public interface MyStack<T> {
    /**
     * 判断栈是否为空
     */
    boolean isEmpty();
    /**
     * 清空栈
     */
    void clear();
    /**
     * 栈的长度
     */
    int length();
    /**
     * 数据入栈
     */
    boolean push(T data);
    /**
     * 数据出栈
     */
    T pop();
}

接下来定义了栈的数组实现:

package cn.edu.ujn.stack;
/**
 * 栈的数组实现, 底层使用数组
 * @author SHQ
 *
 * @param <T>
 */
public class MyArrayStack<T> implements MyStack<T> {
// 定义初始栈的大小
private Object[] objs = new Object[16];
// 栈的大小
    private int size = 0;  

    @Override
    public boolean isEmpty() {
        return size == 0;
    }  

    @Override
    public void clear() {
        // 将数组中的数据置为null, 方便GC进行回收
        for (int i = 0; i < size; i++) {
            objs[size] = null;
        }
        size = 0;
    }  

    @Override
    public int length() {
        return size;
    }  

    @Override
    public boolean push(T data) {
        // 判断是否需要进行数组扩容
        if (size >= objs.length) {
            resize();
        }
        objs[size++] = data;
        return true;
    }  

    /**
     * 数组扩容
     */
    private void resize() {
        Object[] temp = new Object[objs.length * 3 / 2 + 1];
        // 复制
        for (int i = 0; i < size; i++) {
            temp[i] = objs[i];
            objs[i] = null;
        }
        // 将objs重新设置为栈空间
        objs = temp;
    }  

    @SuppressWarnings("unchecked")
    @Override
    public T pop() {
        if (size == 0) {
            return null;
        }
        return (T) objs[--size];
    }  

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("MyArrayStack: [");
        for (int i = 0; i < size; i++) {
            sb.append(objs[i].toString());
            if (i != size - 1) {
                sb.append(", ");
            }
        }
        sb.append("]");
        return sb.toString();
    }
}

然后定义了栈的链表实现:

package cn.edu.ujn.stack;
/**
 * 栈的链表实现, 底层使用链表
 * @author SHQ
 *
 * @param <T>
 */
public class MyLinkedStack<T> implements MyStack<T> {
    /**
     * 栈顶指针
     */
    private Node top;
    /**
     * 栈的长度
     */
    private int size;

    public MyLinkedStack() {
        top = null;
        size = 0;
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }  

    @Override
    public void clear() {
        top = null;
        size = 0;
    }  

    @Override
    public int length() {
        return size;
    }  

    @Override
    public boolean push(T data) {
        Node node = new Node();
        node.data = data;
        node.pre = top;
        // 改变栈顶指针
        top = node;
        size++;
        return true;
    }  

    @Override
    public T pop() {
        if (top != null) {
            Node node = top;
            // 改变栈顶指针
            top = top.pre;
            size--;
            return node.data;
        }
        return null;
    }  

    /**
     * 将数据封装成结点
     */
    private final class Node {
        private Node pre;
        private T data;
    }
}

两种实现的比较, 主要比较数据入栈和出栈的速度:

package cn.edu.ujn.stack;

public class Test {
public static void main(String[] args) {
testSpeed();
}

private static void testSpeed() {
// 测试数组实现
//MyStack<Person> stack = new MyArrayStack<Person>();
// 测试链表实现
MyStack<Person> stack = new MyLinkedStack<Person>();
int num = 1000000;
long start = System.currentTimeMillis();
for (int i = 0; i < num; i++) {
stack.push(new Person("xing", 25));
}
long temp = System.currentTimeMillis();
System.out.println("push time: " + (temp - start));
while (stack.pop() != null)
;
System.out.println("pop time: " + (System.currentTimeMillis() - temp));
}
}

运行结果如下:

   

可见入栈、出栈速度MyArrayStack则有明显的优势.

为什么测试结果是这样的? 可能有些朋友的想法是:数组实现的栈应该具有更快的遍历速度, 但增删速度应该比不上链表实现的栈才对。但是栈中数据的增删具有特殊性: 只在栈顶入栈和出栈。也就是说数组实现的栈在增加和删除元素时并不需要移动大量的元素, 只是在数组扩容时需要进行复制。而链表实现的栈入栈和出栈时都需要将数据包装成Node或者从Node中取出数据, 还需要维护栈顶指针和前驱指针。

栈的应用举例

1.将10进制正整数num转换为n进制

package cn.edu.ujn.stack;

public class StackApp {

/**
 * @param args
 */
public static void main(String[] args) {
//System.out.println(conversion4D2X(22, 2));
//System.out.println(isMatch("[()]"));
System.out.println(lineEdit("Hello  #world"));
}
/**
 *栈的应用举例-将10进制正整数num转换为n进制
 * @param num 待转化十进制数
 * @param n 转化进制
 * @return
 */
private static String conversion4D2X(int num, int n) {
    MyStack<Integer> myStack = new MyArrayStack<Integer>();
    Integer result = num;
    while (true) {
        // 将余数入栈
        myStack.push(result % n);
        result = result / n;
        if (result == 0) {
            break;
        }
    }
    StringBuilder sb = new StringBuilder();
    // 按出栈的顺序倒序排列即可
    while ((result = myStack.pop()) != null) {
        sb.append(result);
    }
    return sb.toString();
}
}

2.检验符号是否匹配.

'['和']', '('和')'成对出现时字符串合法. 例如"[][]()", "[[([]([])()[])]]"是合法的; "([(])", "[())"是不合法的.

遍历字符串的每一个char, 将char与栈顶元素比较. 如果char和栈顶元素配对, 则char不入栈, 否则将char入栈. 当遍历完成时栈为空说明字符串是合法的.

/**
 * 栈的应用举例-检验符号是否匹配:
 * '['和']', '('和')'成对出现时字符串合法. 例如"[][]()", "[[([]([])()[])]]"是合法的; "([(])", "[())"是不合法的.
 * @param str
 * @return boolean
 */
private static boolean isMatch(String str) {
    MyStack<Character> myStack = new MyArrayStack<Character>();
    char[] arr = str.toCharArray();
    for (char c : arr) {
        Character temp = myStack.pop();
        // 栈为空时只将c入栈
        if (temp == null) {
            myStack.push(c);
        }
        // 配对时c不入栈
        else if (temp == '[' && c == ']') {
        }
        // 配对时c不入栈
        else if (temp == '(' && c == ')') {
        }
        // 不配对时c入栈
        else {
            myStack.push(temp);
            myStack.push(c);
        }
    }
    return myStack.isEmpty();
}

3.行编辑

输入行中字符'#'表示退格, '@'表示之前的输入全都无效.

使用栈保存输入的字符, 如果遇到'#'就将栈顶出栈, 如果遇到@就清空栈. 输入完成时将栈中所有字符出栈后反转就是输入的结果:

/**
 * 栈的应用举例-行编辑:
 * 输入行中字符'#'表示退格, '@'表示之前的输入全都无效.
 * @param input
 * @return String
 */
private static String lineEdit(String input) {
    MyStack<Character> myStack = new MyArrayStack<Character>();
    char[] arr = input.toCharArray();
    for (char c : arr) {
        if (c == '#') {
            myStack.pop();
        } else if (c == '@') {
            myStack.clear();
        } else {
            myStack.push(c);
        }
    }
    // StringBuffer线程安全,StringBuilder线程不安全效率高
    StringBuilder sb = new StringBuilder();
    Character temp = null;
    while ((temp = myStack.pop()) != null) {
        sb.append(temp);
    }
    // 反转字符串
    sb.reverse();
    return sb.toString();
}

美文美图

 


剑指Offer——栈的java实现和栈的应用举例的更多相关文章

  1. 剑指offer题解(Java版)

    剑指offer题解(Java版) 从尾到头打印链表 题目描述 输入一个链表,按从尾到头的顺序返回一个ArrayList. 方法1:用一个栈保存从头到尾访问链表的每个结点的值,然后按出栈顺序将各个值存入 ...

  2. 剑指 Offer 30. 包含min函数的栈 + 双栈实现求解栈中的最小值

    剑指 Offer 30. 包含min函数的栈 Offer_30 题目描述: 题解分析: 题目其实考察的是栈的知识,本题的目的是使用两个栈来求解最小值. 第二个栈主要用来维护第一个栈中的最小值,所以它里 ...

  3. 剑指Offer——知识点储备-Java基础

    剑指Offer--知识点储备-Java基础 网址来源: http://www.nowcoder.com/discuss/5949?type=0&order=0&pos=4&pa ...

  4. 《剑指offer》 包含min函数的栈

    本题来自<剑指offer> 包含min函数的栈 题目: 定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1)). 思路: 举例子让抽象问题具体 ...

  5. 剑指Offer(二十一):栈的压入、弹出序列

    剑指Offer(二十一):栈的压入.弹出序列 搜索微信公众号:'AI-ming3526'或者'计算机视觉这件小事' 获取更多算法.机器学习干货 csdn:https://blog.csdn.net/b ...

  6. 剑指 Offer 30. 包含min函数的栈

    剑指 Offer 30. 包含min函数的栈 定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min.push 及 pop 的时间复杂度都是 O(1). 示例 ...

  7. 力扣 - 剑指 Offer 30. 包含min函数的栈

    题目 剑指 Offer 30. 包含min函数的栈 思路1 使用一个辅助栈min_stack,用来维护栈的最小的元素 每次添加元素入栈时候,data_stack和min_stack都要同时维护 dat ...

  8. 【剑指Offer】包含min函数的栈 解题报告

    [剑指Offer]包含min函数的栈 解题报告 标签(空格分隔): 牛客网 题目地址:https://www.nowcoder.com/questionTerminal/beb5aa231adc45b ...

  9. 【Java】 剑指offer(30) 包含min函数的栈

    本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的min ...

  10. 剑指offer编程题Java实现——面试题7相关题用两个队列实现一个栈

    剑指offer面试题7相关题目:用两个队列实现一个栈 解题思路:根据栈的先入后出和队列的先入先出的特点1.在push的时候,把元素向非空的队列内添加2.在pop的时候,把不为空的队列中的size()- ...

随机推荐

  1. NOIP2014-11-3模拟赛

    字符串 题目描述 现在给一个字符串,你要做的就是当这个字符串中存在两个挨着的字符是相同的时就将这两个字符消除.需要注意的是,当把这两个字符消除后,可能又产生一对新的挨着的字符是相同的.比如,初始的字符 ...

  2. bzoj2839: 集合计数 容斥+组合

    2839: 集合计数 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 523  Solved: 287[Submit][Status][Discuss] ...

  3. Linux 下查看tomcat版本

    1.echo $JAVA_HOME 2.进入jdk路径 3.java -version

  4. 选取id不为sth的div元素

    选取id不为sth的div元素$("div:not(#sth)")

  5. vmware虚拟机CentOS7安装oracle数据库

    想用linux虚拟机装一个oracle,中间遇到的坑太多了,最后总算是安装好了,一定要写个全面的教程出来. 话不多说 通用编辑命令: vi test.txt   #进入编辑模式 编辑完成后按ESC退出 ...

  6. Mysql锁机制--并发事务带来的更新丢失问题

    Mysql 系列文章主页 =============== 刚开始学习 Mysql 锁的时候,觉得 Mysql 使用的是行锁,再加上其默认的可重复读的隔离级别,那就应该能够自动解决并发事务更新的问题.可 ...

  7. Hibernate 自动生产表

    hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库. 今天就来演示一下Hibernate最初级的 ...

  8. MultiTigger 绑定异常处理

    异常产生环境: 在初始化一个窗口后,没有show出来.在此窗口中,有个控件,重写了控件模板,并加了MultiTrigger. 注意:俩个Condition,一个是从外面绑定过来的Tag,一个是Cont ...

  9. brew的MAC安装

    1.介绍 brew是一个软件包管理工具,类似于centos下的yum或者ubuntu下的apt-get,非常方便,免去了自己手动编译安装的不便 brew 安装目录 /usr/local/Cellar ...

  10. Flume 读取RabbitMq消息队列消息,并将消息写入kafka

    首先是关于flume的基础介绍 组件名称 功能介绍 Agent代理 使用JVM 运行Flume.每台机器运行一个agent,但是可以在一个agent中包含多个sources和sinks. Client ...