数据结构与算法之栈

比如我们在放盘子的时候都是从下往上一个个放,拿的时候是从上往下一个个的那,不能从中间抽,这种其实就是一个典型的栈型数据结构。后进先出即Last In First Out (LIFO)。

栈如何实现

  • 它是一个限定仅在表尾进行插入和删除操作线性表
    这一端被称为栈顶,相对地,把另一端称为栈底。
  • 向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;
  • 从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
  • 因为是线性表、所以可以使用数组或者链表实现,所以栈是特殊的链表和数组;
  • 既然栈也是一个线性表,那么我们肯定会想到数组和链表,而且栈还有这么多限制,那为什么我们还要使用这个数据结构呢?不如直接使用数组和链表来的更直接么?数组和链表暴露太多的接口,实现上更灵活了,有些技术理解不到位的人员就可能出错。所以在某些特定场景下最好是选择栈这个数据结构。

栈的分类

  1. 基于数组的栈——以数组为底层数据结构时,通常以数组头为栈底,数组头到数组尾为栈顶的生长方向
    在这里插入图片描述

  2. 基于单链表的栈——以链表为底层的数据结构时,以链表头为栈顶,便于节点的插入与删除,压栈产生的新节点将一直出现在链表的头部
    在这里插入图片描述
    两者的区别在于数组和链表的本质区别,链表可以动态扩容,而数组则是固定容量,有栈溢出的可能;

栈的基本操作

基于数组实现的栈:

package com.DataConstruct.stack;

public class MyStack {
    private Object[] table;
    private int top;
    private int defaultCapacity=10;
    private int capacity;

    public MyStack() {
        table=new Object[defaultCapacity];
        capacity=defaultCapacity;
        top=0;
    }

    public MyStack(int capacity) {
        this.capacity = capacity;
        table=new Object[capacity];
        top=0;
    }

    public void push(Object val){
        if(val==null)return;

        if(top+1<capacity){
            table[top]=val;
            top++;
        }else {
            //扩容操作...数组扩容为原来的1.5呗。
        }
    }
    public Object pop(){
        if (top<0)return -1;

        return table[top--];
    }
    public Object peek(){
        if (top<0)return -1;

        return table[top];
    }
    public int size(){
        return top;
    }

    public void print(){
        for (int i=0;i<top;i++){
            System.out.println(table[i].toString());
        }
    }

    public static void main(String[] args) {
        MyStack stack=new MyStack();
        stack.push("1");
        stack.push("2");
        stack.push("3");
        stack.push("4");
        stack.print();

        stack.pop();
        stack.print();
        System.out.println("-----");
        stack.push("ss");
        stack.push("dd");
        stack.push("aa");
        stack.print();

    }
}

JDK自带的数组栈ArrayStack继承了ArrayList,底层就是数组;


package org.yaml.snakeyaml.util;

import java.util.ArrayList;

public class ArrayStack<T> {
    private ArrayList<T> stack;

    public ArrayStack(int initSize) {
        stack = new ArrayList<T>(initSize);
    }

    public void push(T obj) {
        stack.add(obj);
    }

    public T pop() {
        return stack.remove(stack.size() - 1);
    }

    public boolean isEmpty() {
        return stack.isEmpty();
    }

    public void clear() {
        stack.clear();
    }
}

基于链表实现栈:

package com.DataConstruct.stack;

import com.DataConstruct.list.MyStack;

public class MyLinkedStack implements MyStack {
    private Node head;

    private Node tailParent;
    private Node tail;
    private int top;

    public MyLinkedStack() {
        top=0;
    }

    @Override
    public void push(Object val) {
        Node newNode=new Node(val);
        if(head==null){
            head=newNode;
            tail=newNode;
            top++;
        }else {
            tailParent=tail;
            tail.next=newNode;
            newNode.pre=tail;
            tail=newNode;
            top++;
        }
    }

    @Override
    public Object pop() {
        Node result=tail;
        //将尾结点和尾结点前一个结点的指针断掉;避免内存泄露
        tailParent.next=null;
        tail.pre=null;

        //将尾结点和尾结点前一个结点往前面移动一个位置;;
        tail=tailParent;
        tailParent=tailParent.pre;
        //元素个数-1;
        top--;
        //如果元素个数为0,需要把head与下一个结点的指针断掉。
        if(top==0){
            head=null;
        }
        return result.data;
    }

    @Override
    public Object peek() {
        if(tail!=null){
            return tail.data;
        }
        return -1;
    }

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

    public void print(){
        Node cur=head;
        while (cur!=null){
            System.out.println(cur.data);
            cur=cur.next;
        }
    }
    public static void main(String[] args) {
        MyLinkedStack stack=new MyLinkedStack();
        stack.push("11");
        stack.push("12");
        stack.push("13");
        stack.push("14");
        stack.print();
        System.out.println(stack.pop());
        System.out.println(stack.pop());
        stack.print();
        System.out.println(stack.peek());
    }
}
class Node {
    int index;
    Object data;
    Node next;
    Node pre;
    public Node(Object data) {
        this.data = data;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public Node getNext() {
        return next;
    }

    public void setNext(Node next) {
        this.next = next;
    }
}

通过数组和链表的实现方式,我们也可以还知道栈入栈和出栈的时间复杂度是O(1),效率较好。

栈的应用场景

我们JVM虚拟机的内存结构中,有一部分为虚拟机栈;是每个线程私有的,因为出栈和入栈,所有的基本数据类型和栈上分配变量都会被回收,减少JVM垃圾回收的压力。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值