day03 - 线性结构的实现与应用02

 

一、栈的基本概念

  • 堆栈(也简称作栈)是一种特殊的线性表,堆栈的数据元素以及数据元素间的逻辑关系和线性表完全相同,其差别是线性表允许在任意位置进行插入和删除操作,而堆栈只允许在固定一端进行插入和删除操作。

  • 堆栈中允许进行插入和删除操作的一端称为栈顶,另一端称为栈底。堆栈的插入和删除操作通常称为进栈或入栈,堆栈的删除操作通常称为出栈或退栈。

  • 数据集合:堆栈的数据集合可以表示为a0,a1,…,an-1,每个数据元素的数据类型可以是任意的类类型。

  • 操作集合:入栈(push)、出栈(pop)、取栈顶元素(getTop)、判断是否为空(isEmpty)

 

二、栈的实现

    1、接口代码

/**
*  栈、队列线性表接口
*/
public interface List<E> {
    /**
     *  入栈或入队
     * @param o
     * @return
     */
    boolean push(E o);

    /**
     *  出栈或出队
     * @return
     */
    E pop();

    /**
     *  取栈顶或取队头
     * @return
     */
    E getTop();

    /**
     *  栈或队列是否为空
     * @return
     */
    boolean isEmpty();
}

    2、顺序栈和链栈的实现代码

/**
*  栈的实现
*  FILO 先进后出
*/
public interface Stack<E> extends List<E> { }

/**
*  顺序栈
* @param <E>
*/
class ArrayStack<E> implements Stack<E>{

    // 顺序栈的存储空间
    private E[] stack;

    // 栈顶指针
    private int heap = -1;

    // 栈的大小
    public int size;

    // 栈的最大空间
    private int MAX_SIZE = 50;

    // 初始化栈
    public ArrayStack(){
        stack = (E[]) new Object[MAX_SIZE];
    }

    /**
     * 入栈
     * @param e 入栈元素
     * @return
     */
    @Override
    public boolean push(E e) {
        // 判断是否栈满
        if(size >= MAX_SIZE){
            return false;
        }
        //入栈
        size++;
        stack[++heap] = e;
        return true;
    }

    /**
     *  出栈
     * @return
     */
    @Override
    public E pop() {
        if(isEmpty()){
            throw new StackOverflowError("栈已为空!!");
        }
        // 出栈
        size--;
        return stack[heap--];
    }

    /**
     *  取栈顶元素但不出栈
     * @return
     */
    @Override
    public E getTop() {
        if(isEmpty()){
            throw new StackOverflowError("栈已为空!!");
        }
        return stack[heap];
    }

    /**
     *  判断栈是否为空
     * @return
     */
    @Override
    public boolean isEmpty() {
        return (heap == -1)?true:false;
    }
}

/**
*  链栈
* @param <E>
*/
class LinkedStack<E> implements Stack<E>{

    /**
     *  元素节点类
     * @param <E>
     */
    class Node<E>{
        E e;
        Node pre;
        Node next;
        public Node(E e){
            this.e = e;
        }
    }

    // 栈顶指针
    private Node<E> heap;

    // 栈的大小
    public int size;


    /**
     *  入栈
     * @param o
     * @return
     */
    @Override
    public boolean push(E e) {
        // 创建节点
        Node<E> node = new Node<>(e);
        size++;
        // 若栈顶为空,则给栈顶赋值
        if(heap == null){
            heap = node;
            return true;
        }
        // 连接原栈顶与新节点
        heap.next = node;
        node.pre = heap;
        heap = node; // 更新栈顶
        return true;
    }

    /**
     *  出栈
     * @return
     */
    @Override
    public E pop() {
        if(heap == null){
            throw new StackOverflowError("栈已为空!!");
        }
        size--;
        E e = heap.e; // 取栈顶元素值
        heap = heap.pre; // 更新栈顶
        return e;
    }

    /**
     *  取栈顶元素但不出栈
     * @return
     */
    @Override
    public E getTop() {
        if(heap == null){
            throw new StackOverflowError("栈已为空!!");
        }
        return heap.e;
    }

    @Override
    public boolean isEmpty() {
        return (size == 0)?true:false;
    }
}

 

 

三、Java中栈与堆的区别

  • 栈(stack):是一个先进后出的数据结构,通常用于保存方法(函数)中的参数,局部变量。在java中,所有基本类型和引用类型都在栈中存储。栈中数据的生存空间一般在当前scopes内(就是由{...}括起来的区域)。

  • 堆(heap):是一个可动态申请的内存空间(其记录空闲内存空间的链表由操作系统维护),C中的malloc语句所产生的内存空间就在堆中。在java中,所有使用new xxx()构造出来的对象都在堆中存储,当垃圾回收器检测到某对象未被引用,则自动销毁该对象。所以,理论上说java中对象的生存空间是没有限制的,只要有引用类型指向它,则它就可以在任意地方被使用。

 

四、队列的基本概念

  • 队列(简称作队,Queue)也是一种特殊的线性表,队列的数据元素以及数据元素间的逻辑关系和线性表完全相同,其差别是线性表允许在任意位置插入和删除,而队列只允许在其一端进行插入操作在其另一端进行删除操作。

  • 队列中允许进行插入操作的一端称为队尾,允许进行删除操作的一端称为队头。队列的插入操作通常称作入队列,队列的删除操作通常称作出队列。

  • 数据集合:队列的数据集合可以表示为a0,a1,…,an-1,每个数据元素的数据类型可以是任意的类型。

  • 操作集合:入队(push)、出队(pop)、取队头元素(getTop)、判断是否为空(isEmpty)

 

五、队列的实现

顺序队列和链式队列的实现代码

/**
*  队列的实现
*  FIFO 先进先出
* @param <E>
*/
public interface Queue<E> extends List<E> { }

/**
*  顺序队列(数组实现)
* @param <E>
*/
class ArrayQueue<E> implements Queue<E>{

    // 顺序队列的存储空间
    private E[] queue;

    // 队头指针
    public int head = 0;

    // 队尾指针
    public int tail = 0;

    // 队列的大小
    public int size = 0;

    // 队列的最大空间
    private int MAX_SIZE = 50;

    // 初始化队列
    public ArrayQueue(){
        queue = (E[]) new Object[MAX_SIZE];
    }

    /**
     *  入队
     * @param e
     * @return
     */
    @Override
    public boolean push(E e) {
        // 判断是否队满
        if(size == MAX_SIZE){
            throw new IndexOutOfBoundsException("队列已满!!");
        }
        // 在队尾的后一位插入元素并更新队尾
        queue[tail] = e;
        if(tail >= MAX_SIZE - 1){
            tail = 0;
        }else {
            tail ++;
        }
        size++;
        return true;
    }

    /**
     *  出队
     * @return
     */
    @Override
    public E pop() {
        if(isEmpty()){
            throw new IndexOutOfBoundsException("队列已空!!");
        }
        // 取队头并更新队头
        E e = queue[head];
        if(head >= MAX_SIZE - 1){
            head = 0;
        }else {
            head ++;
        }
        size --;
        return e;
    }

    /**
     *  取队头但不出队
     * @return
     */
    @Override
    public E getTop() {
        if(isEmpty()){
            throw new IndexOutOfBoundsException("队列已空!!");
        }
        // 取队头
        return queue[head];
    }

    @Override
    public boolean isEmpty() {
        return (size == 0)?true:false;
    }
}

/**
*  链式队列(链表实现)
* @param <E>
*/
class LinkedQueue<E> implements Queue<E>{

    /**
     *  元素节点类
     * @param <E>
     */
    class Node<E>{
        E e;
        Node next;
        public Node(E e){
            this.e = e;
        }
    }

    // 队头指针
    private Node<E> head;

    // 队尾指针
    private Node<E> tail;

    // 队列的大小
    public int size;

    /**
     *  入队
     * @param e
     * @return
     */
    @Override
    public boolean push(E e) {
        // 在队尾的后一位插入元素并更新队尾
        Node node = new Node(e);
        tail.next = node;
        tail = node;
        size++;
        return true;
    }

    /**
     *  出队
     * @return
     */
    @Override
    public E pop() {
        // 取队头并更新队头
        E e = head.e;
        head = head.next;
        size --;
        return e;
    }

    /**
     *  取队头但不出队
     * @return
     */
    @Override
    public E getTop() {
        if(isEmpty()){
            throw new IndexOutOfBoundsException("队列已空!!");
        }
        // 取队头
        return head.e;
    }

    @Override
    public boolean isEmpty() {
        return (size == 0)?true:false;
    }
}

六、中缀表达式转换后缀表达式算法

  

     *  中缀转后缀算法

     *      1、运算符入栈,数字入队

     *      2、匹配到双 “()” 时,出栈 “()” 中的运算符

     *      3、栈顶优先级低出栈

     *      4、*、/ 号先入栈,待后一位数字入队后再直接出栈

     *      5、若中缀表达式已入栈入队完毕,则陆续把栈中的运算符出栈然后入队

/**
*  利用栈实现中缀表达式转后缀表达式
*/
public class InfixToSuffix {

    public static void main(String[] args) {

        InfixToSuffix its = new InfixToSuffix();
        // 中缀表达式
        String infix = "9+(3-1)*3+10/2";

        // 中缀转后缀 "9 3 1 - 3 * 10 2 / + +"
        its.infixToSuffix(infix);

    }

    /**
     *  中缀转后缀算法
     *      1、运算符入栈,数字入队
     *      2、匹配到双 “()” 时,出栈 “()” 中的运算符
     *      3、栈顶优先级低出栈
     *      4、*、/ 号先入栈,待后一位数字入队后再直接出栈
     *      5、若中缀表达式已入栈入队完毕,则陆续把栈中的运算符出栈然后入队
     * @param infix
     * @return
     */
    public String infixToSuffix(String infix){
        // 创建一个栈
        Stack<Character> stack = new ArrayStack<>();
        // 创建一个队列
        Queue<String> queue = new ArrayQueue<>();
        // 中缀表达式转后缀表达式
        int start = 0;
        int end = 0;
        String x = "";
        // substring(int start, int end);  end >   >= start
        for(int i = 0; i < infix.length(); i ++){
            char c = infix.charAt(i);
            // '*' '/' 待后一位数字入队后入队
            if(!stack.isEmpty() && (stack.getTop().equals('*') || stack.getTop().equals('/'))){
                x = stack.pop() + "";
            }
            int s1 = ((ArrayStack<Character>) stack).size; // '*' '/' 入队标志 1
            switch (c){
                // '+' '-' 直接入栈 , 前面的数字入队
                case '+' : stack.push(c);  queue.push(infix.substring(start,end)); start = ++end; break;
                case '-' : stack.push(c);  queue.push(infix.substring(start,end)); start = ++end; break;
                // '*' '/' 符号入栈,数字入队后 符号出栈跟着入队
                case '*' : stack.push(c);  queue.push(infix.substring(start,end)); start = ++end; break;
                case '/' : stack.push(c);  queue.push(infix.substring(start,end)); start = ++end; break;
                // '(' 入栈 ')' 入栈并匹配 '(' ,出栈 '()' 间的运算符并入队
                case '(' : stack.push(c);  queue.push(infix.substring(start,end)); start = ++end; break;
                case ')' : stack.push(c);  queue.push(infix.substring(start,end));
                            while (true){
                                Character pop = stack.pop();
                                if(pop.equals('(')){
                                    start = ++end; break;
                                }else if(!pop.equals(')')){
                                    queue.push(pop + "");
                                }
                            } break;
                default: end++; break;
            }
            int s2 = ((ArrayStack<Character>) stack).size; // '*' '/' 入队标志 2
            if(s1 != s2){ // 当标志位不等时表示  '*' '/' 的后一位数字已入队
                queue.push(x);
                x = "";
            }
        }
        queue.push(infix.substring(start,end)); // 将最后一位运算符之后的数字入队
        queue.push(x); // 若'*' '/' 之后无其他运算符,则把最后一位 入队
        // 栈中的还未出栈符号全部出栈
        int size =  ((ArrayStack<Character>) stack).size;
        for(int s = 0; s < size; s ++){
            queue.push(stack.pop() + "");
        }
        // 打印后缀表达式
        size = ((ArrayQueue<String>) queue).size;
        StringBuilder suffix = new StringBuilder();
        Queue<String> newQueue = new ArrayQueue();
        for(int i = 0; i < size; i ++){
            String c = queue.pop();
            if(!c.equals("")){
                System.out.print(c + " ");
                suffix.append(c);
                newQueue.push(c);
            }
        }
        // 计算后缀表达式的值
        int calc = calc(newQueue, ((ArrayQueue<String>) newQueue).size);
        System.out.println("计算后缀表达式的值 = " + calc);
        return suffix + "";
    }

    /**
     *  计算后缀表达式的值
     * @param queue 中缀表达式队列
     * @param size 中缀表达式队列的长度
     * @return
     */
    public int calc(Queue<String> queue,int size){
        if(size == 1){
            return Integer.parseInt(queue.pop());
        }
        Queue<String> newQueue = new ArrayQueue();
        int f1 = Integer.parseInt(queue.pop());
        int f2 = Integer.parseInt(queue.pop());
        String f;
        boolean flag = true;
        for(int i = 2; i < size; i++){
            f =  queue.pop();
            if(flag){
                switch (f){
                    // 匹配到运算符直接运算,否则给相应数字赋值
                    case "+" :  f = (f1 + f2) + ""; newQueue.push(f); flag = false; break;
                    case "-" :  f = (f1 - f2) + ""; newQueue.push(f); flag = false; break;
                    case "*" :  f = (f1 * f2) + ""; newQueue.push(f); flag = false; break;
                    case "/" :  f = (f1 / f2) + ""; newQueue.push(f); flag = false; break;
                    default: newQueue.push(f1 + ""); // 入队不用计算的数字
                        // 赋值出队的数字
                        f1 = f2;
                        f2 = Integer.parseInt(f);
                }
            }else {
                newQueue.push(f);
            }
        }
        return calc(newQueue,((ArrayQueue<String>) newQueue).size);
    }
}

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值