剑指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. (概念)多个CPU和多核CPU以及超线程(Hyper-Threading)

    引言 在这篇文章中我会主要介绍CPU相关的一些重要概念和技术.如果你想更好地了解操作系统,那就从本文开始吧. 中央处理器(Central processing unit) 在我们了解其它概念之前,我们 ...

  2. Mysql优化--Show Profile

    Mysql 系列文章主页 =============== 是Mysql提供可以用来分析当前会话中语句执行的资源消耗情况.可以用于Sql的调优的测量.默认情况下处于关闭状态,并保存最近 15 次的运行结 ...

  3. Eclipse 一直不停 building workspace完美解决总结

    一.产生这个问题的原因多种1.自动升级 2.未正确关闭  3.maven下载lib挂起 等.. 二.解决总结(1).解决方法        方法1.修改eclipse启动文件 eclipse.ini ...

  4. WPF TextBlock 判断 isTextTrimmed 文本是否超出

    WPF TextBlock 设置TextTrimming情况下 判断 isTextTrimmed(Text 文本是否超出 是否出现了省略号) private bool HasTextTrimmed(T ...

  5. ASP.NET Core 添加统一模型验证处理机制

    一.前言 模型验证自ASP.NET MVC便有提供,我们可以在Model(DTO)的属性上加上数据注解(Data Annotations)特性,在进入Action之前便会根据数据注解,来验证输入的数据 ...

  6. 网络七层OSI模型简介

    0.  网络七层OSI模型(Open System Interconnection)总览: 1.  应用层 2.  表示层 :表示层的作用是使通信的应用程序能够解释交换数据的含义.这些服务包括数据压缩 ...

  7. Spring中配置DataSource的六种方式

    第一种:beans.xml <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource ...

  8. ANTLR和StringTemplate实例:自动生成单元测试类

    ANTLR和StringTemplate实例:自动生成单元测试类 1. ANTLR语法 要想自动生成单元测试,首先第一步就是分析被测试类.这里以Java代码为例,用ANTLR对Java代码进行分析.要 ...

  9. linux:CPU私有变量(per-CPU变量)

    一.简介2.6内核上一个新的特性就是per-CPU变量.顾名思义,就是每个处理器上有此变量的一个副本.per-CPU的最大优点就是,对它的访问几乎不需要锁,因为每个CPU都在自己的副本上工作.task ...

  10. 干货!Android Studio快捷键VS Eclipse快捷键~亲测!

    eclipse as 英文描述 中文描述 ctrl+shift+r ctrl+shift+r Navigate->File 找工作空间的文件 ctrl+shift+t ctrl+shift+t ...