【底层实现 | 数据结构与算法笔记02】 栈

系列文章目录

01-数组



简单概述

本篇文章的代码实现主要是学习算法与数据结构课程后的一个总结内容, 如果喜欢的话, 希望大家支持一下bobo老师的课程.

定义

栈是一个先进后出的数据结构, 当我们使用一个栈的时候, 其拥有一个栈顶指针, 指向最后一个进栈的元素, 每次进行出栈操作的时候, 此时先将栈内元素取出, 然后将栈顶指针进行移动.

如何实现

对于栈来说其底层的方式实现有很多种, 这里我们将会介绍: 数组栈, 链表栈的实现方式.

Stack结构图

栈接口

在实现之前, 我们会发现有很多方法是不同底层栈实现时所共有的方法, 因此这里可以将方法先抽出来放到一个接口中, 然后再对接口进行当中的方法进行重写.

Stack接口图

这里我们抽象出来的方法有: int getSize()其主要可以用来获取栈的大小, boolean isEmpty()则是用来判断栈是否为空, void push(E e)用来表示入栈的操作, E pop()则是用来表示出栈操作, E peek()用来表示查看栈顶元素; 这里可能有疑问的是, 在之前我们学习的数组那一部分的内容当中不是还有一个int getCapacity()方法用来获取容量的操作么, 为什么不写到接口中呢? 因为上面解释的很明确, 就是要将不同底层栈实现的共有方法提取出来, 对于链表栈, 我们根本不需要考虑其容量问题, 所以这里就没有将int getCapacity()作为共有方法.

public interface Stack<E> {
	// 获取栈的大小
   	int getSize();
    // 栈是否为空
    boolean isEmpty();
    // 入栈
    void push(E e);
    // 出栈
    E pop();
    // 查看栈顶元素
    E peek();
}

如何利用数组实现栈?

提示: 由于这里调用了我们之前学习到的数组的内容, 所以需要先浏览一下那篇文章.

ArrayStack结构图

成员变量

ArrayStack成员变量

由于我们之前设计好的数组实现了我们想要构建的各种栈操作, 因此这里我们直接私有化一个之前写好的数组对象即可.

// 数组
private Array<E> array;

构造方法

ArrayStack构造方法

在这里我们同之前学习的数组一样, 实现了两个构造方法, 一个无参public ArrayStack(), 一个有参public ArrayStack(int capcity), 其中public ArrayStack(int capacity)方法调用了数组对象的有参构造public Array(int capacity), 而public ArrayStack()则是调用了数组对象的无参构造public Array()进行了实现, 具体代码实现如下:

// 构造函数
public ArrayStack() {
    array = new Array<>();
}
public ArrayStack(int capacity) {
    // 构建一个数组对象
    array = new Array<>(capacity);
}

重写方法

ArrayStack重写方法

实现了构造之后, 我们就可以实现数组栈的大部分主体操作了, 这些主体操作很多都是对之前数组内容的调用, 因此到这里如果还没有看的小伙伴, 希望先去看一下实现一下之前写的数组.

push(E e), pop()以及peek()

public void push(E e), public E pop()public E peek() 的方法分别实现的是: 对元素入栈, 对元素出栈, 以及查看栈顶元素, 由于栈本身是一个先进后出的结构, 那么这里入栈其实就是对数组尾部插入元素, 这里的出栈同样也是对数组尾部移除元素, 而查看栈顶元素只不过是不进行移除元素操作, 这里我们思考一下发现, 不就是调用我们之前实现的数组当中的addLast(E e), removeLast()以及getLast()方法么, 都不需要自己在进行额外的思考了, 这也侧面说明当一个代码的健壮性和扩展性良好的情况下, 很多时候我们是能够直接复用, 而不需要花费额外精力写更多的代码的.

// 数组栈元素入栈
@Override
public void push(E e) {
    // 在数组末尾添加一个元素
    array.addLast(e);
}

// 数组栈元素出栈
@Override
public E pop() {
    // 取出数组最后一个元素
    return array.removeLast();
}

// 查看数组栈的栈顶元素
@Override
public E peek() {
    // 查看数组中的最后一个元素
    return array.getLast();
}

getSize(), isEmpty()

基于上面写push(E e), pop() 以及peek() 的思路, 这里我们同样也可以调用数组中的getSize()isEmpty()的方法完成, 同样的后续的获取容量getCapacity()方法也可以使用这种方式直接调用完成.

// 获取数组栈的长度
@Override
public int getSize() {
    return array.getSize();
}

// 获取数组栈的长度
@Override
public boolean isEmpty() {
    return array.isEmpty();
}

toString()

String toString()方法这里就没法进行偷懒了, 还是需要进行一下重写, 但是有了之前数组当中写toString()方法的经验, 整体实现也很容易.

@Override
public String toString() {
    StringBuilder res = new StringBuilder();
    res.append("Stack: [");
    for (int i = 0; i < array.getSize(); i++) {
        res.append(array.get(i));
        if (i != array.getSize() - 1) {
            res.append(", ");
        }
    }
    res.append("] top");
    return res.toString();
}

成员方法

Array Stack成员方法

### getCapacity()

有了之前的经验, 我们其实知道这里同样只需要调用数组当中写的getCapactiy()方法就可以了, 但是由于其不是接口实现的方法, 所以这里单独列到了成员方法当中.

// 获取数组栈的容量
public int getCapacity(){
    // 获取数组的容量
    return array.getCapacity();
}

整体代码

到此, 我们就实现了整体的代码构建, 可以发现当我们有了可扩展性以及复用性高的代码后, 实现一些结构的操作, 根本不需要我们花费别的精力对其进行太多的修改就可以直接完成了, 因此在以后写代码时我们也要尽可能写出这样复用性以及可扩展性高的代码, 来帮助自己提升效率.

public class ArrayStack<E> implements Stack<E> {
    // 成员变量
   	private Array<E> array;
    
    // 构造函数
    // 无参构造函数
    public ArrayStack(){
        array = new Array<>();
    }
    // 有参构造函数
    public ArrayStack(int capacity){
        // 构建一个数组对象
        array = new Array<>(capacity);
    }
    // 获取数组栈的长度
    @Override
    public int getSize(){
        return array.getSize();
    }
    // 获取数组栈的长度
    @Override
    public boolean isEmpty(){
        return array.isEmpty();
    }
    // 数组栈元素入栈
    @Override
    public void push(E e){
        // 在数组的末尾添加一个元素
        array.addLast(e);
    }
    // 数组栈元素出栈
    @Override
    public E pop() {
        // 取出数组最后一个元素
        return array.removeLast();
    }
    // 查看数组栈的栈顶元素
    @Override
    public E peek(){
        // 查看数组中的最后一个元素
        return array.getLast();
    }
    // 获取数组栈的容量
    public int getCapacity(){
        // 获取数组的容量
        return array.getCapacity();
    }
    // 显示数组
    @Override
    public String toString() {
        StringBuilder res = new StringBuilder();
        res.append("Stack: [");
        for (int i = 0; i < array.getSize(); i++){
            res.append(array.get(i));
            if (i != array.getSize() - 1) {
                res.append(", ");
            }
        }
        res.append("] top");
        return res.toString();
    }
}

利用链表实现栈的操作-未更新

这里调用了链表的操作, 在底层实现了栈的操作

package stack;

import linkedlist.LinkedList;

public class LinkedListStack<E> implements Stack<E>{
    // 成员变量
    private LinkedList<E> list;

    // 构造方法
    // 无参构造
    public LinkedListStack() {
        this.list = new LinkedList<>();
    }
    // 判断栈的长度
    @Override
    public int getSize() {
        return list.getSize();
    }
    // 判断栈是否为空
    @Override
    public boolean isEmpty() {
        return list.isEmpty();
    }

    // 栈元素入栈
    @Override
    public void push(E e) {
        list.addFirst(e);
    }

    // 栈元素出栈
    @Override
    public E pop() {
        return list.removeFirst();
    }

    // 查看栈首元素
    @Override
    public E peek() {
        return list.getFirst();
    }

    // 打印队列
    @Override
    public String toString() {
        StringBuilder res = new StringBuilder();
        res.append("Stack: top");
        res.append(list);
        return res.toString();
    }
}

wwwwwwwww

参考文献

  1. 算法与数据结构
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值