C++数据结构:stack原理与实现

文章目录

    • 栈的理论讲解
    • 整体代码
    • stack的实现
      • (1) stack类的声明
      • (2) push()
      • (3) pop()
    • 总结

栈的理论讲解

stack是C++STL容器库中的一员,它的特点是:先入后出,后入先出。这里有一张原理图:
C++数据结构:stack原理与实现_第1张图片
这里说几个专有名词

  • 栈顶:也就是top()所访问的元素,即:栈中最后push(推入)的元素
  • 栈底:栈的底层数组。在C++STL的stack中,我们能够自己指定栈底为vector、list或者其他,默认是deque

我们常用的函数有:

方法 作用
top() 访问栈顶元素,也仅仅是访问操作
pop() 删除栈顶元素
empty() 检查栈中是否为空
size() 返回栈中所存储的元素个数
push() 向栈中推入元素

栈它有个较大的缺陷:它的效率不高,它是对已有的STL进行一个包装。
接下来我们会将这些函数的功能一一实现,并且使用模板,尽可能去模仿STL中的stack我们所实现的stack为顺序栈,这种栈的栈底是数组;还有一种栈为链式栈,它的栈底是链表

整体代码

这里先给出整体代码,然后再按照功能逐个分析:

#ifndef STACK_CPP
#define STACK_CPP

#define MINSIZE 3//最小规模

/*
此种Stack是基于数组实现的
真正的stack的底层可以指定各种容器
如vector、list、deque
*/

template<class T>
class Stack{
public:
    Stack();
    ~Stack();
    void pop();//出栈
    T top();//访问栈顶
    void push(const T& value);//添加元素
    int size();//返回元素的个数
    bool empty();//查看栈是否为空

    // void shrink_to_fit();//调整规模
protected:
    int sizeVal = 0;//元素个数
    int scale = MINSIZE;//规模
    T* arr;//存放数据
};

template<class T>
Stack<T>::Stack(){
    this->arr = new T[scale];
}

template<class T>
Stack<T>::~Stack(){
	delete[] arr;
}

template<class T>
void Stack<T>::pop(){
    //缩减规模
    sizeVal--;//这也反映了stack使用pop()之后,栈顶元素并不是真正被删除了,只是我们访问不到了
}

template<class T>
inline T Stack<T>::top(){
    return arr[sizeVal-1];
}

template<class T>
void Stack<T>::push(const T& value){
    //更改规模
    //检查规模是否需要扩容
    this->sizeVal++;
    //需要扩容
    if(this->sizeVal > this->scale){
        this->scale *= 1.5;
        T* newArr = new T[this->scale];
        for(int temp=0; temp<sizeVal-1; temp++){
            newArr[temp] = this->arr[temp];
        }
        newArr[sizeVal-1] = value;
        delete[] this->arr;
        arr = newArr;
    }

    //不需要扩容
    arr[sizeVal-1] = value;
}

template<class T>
inline int Stack<T>::size(){
    return  this->sizeVal;
}

template<class T>
inline bool Stack<T>::empty(){
    //三元表达式
    return this->sizeVal == 0 ? true : false;
}

#endif

stack的实现

大部分的函数实现还是很简单的,因此我这里只说我觉得有必要说的

(1) stack类的声明

先给代码再来分析:

template<class T>
class Stack{
public:
    Stack();
    ~Stack();
    void pop();//出栈
    T top();//访问栈顶
    void push(const T& value);//添加元素
    int size();//返回元素的个数
    bool empty();//查看栈是否为空

    // void shrink_to_fit();//调整规模
protected:
    int sizeVal = 0;//元素个数
    int scale = MINSIZE;//规模
    T* arr;//存放数据
};

我们将分析的重点放在protected权限中:

  1. 首先第一个问题:我们为什么使用protected而不是private?C++中STL的设计理念中有这么一条:可拓展,而要实现它的可拓展就需要将其私有部分设置为protected。
  2. scale是什么?在上面代码的注释中其实已经说的很清楚了:stack的规模,也就是它当前状态下所能够存储的最大元素个数。
    和先前的vector类似,stack也有可拓展的特性,因此我们需要根据所存放的元素个数的变化不断更改其规模,以达到目的。

(2) push()

push()负责将元素推入栈,是stack中最复杂、代码量最多的函数,但是实际上也不过如此:

template<class T>
void Stack<T>::push(const T& value){
    //更改规模
    //检查规模是否需要扩容
    this->sizeVal++;
    //需要扩容
    if(this->sizeVal > this->scale){
        this->scale *= 1.5;
        T* newArr = new T[this->scale];
        for(int temp=0; temp<sizeVal-1; temp++){
            newArr[temp] = this->arr[temp];
        }
        newArr[sizeVal-1] = value;
        delete[] this->arr;
        arr = newArr;
    }

    //不需要扩容
    arr[sizeVal-1] = value;
}

之前说到stack和先前的vector类似,也有自动扩容的特点,因此我们在添加元素的时候就需要考虑:**在添加元素之后,元素的个数是否超过了stack的规模?**若是超过了,就应当扩容,没超过直接添加元素就好。
这里我选择的扩容模式为扩容至原先的1.5倍。同时,为了避免浅拷贝,需要在堆中重新分配数组,也就是这里的newArr。
这里我偷了个懒,stack的效率没有达到最高,想要效率最高可以在for循环中对元素的拷贝操作更改为对元素的转发操作,也就是使用std::move(),反正在此后原数组的空间就被释放了,不需要再次访问,也就没有访问未定义的元素的可能。大家可以根据自己的需要去优化代码。

(3) pop()

pop()的作用是删除栈顶元素,但是想来想去:**好像在数组中没有删除元素的操作吧?**是的没错,在数组中确实没有删除元素的操作,因此我们不难得知:pop()所谓的删除元素并不是真正的删除,至少在顺序表中是这样,只是说在使用pop()之后,栈顶的位置变了,导致我们无法再访问到这个元素

template<class T>
void Stack<T>::pop(){
    //缩减元素个数
    //(伪删除)
    sizeVal--;//这也反映了stack使用pop()之后,栈顶元素并不是真正被删除了,只是我们访问不到了
}

我使用的方法是减少元素个数,因为我的top()访问就是通过元素个数

总结

stack的实现还是很简单的,希望大家在看完这篇文章之后能够有所收获。若是这篇文章中有什么不足或是错误,希望大佬们在评论区指出或者私信联系我,我将立即修改,感激不尽!!!

你可能感兴趣的:(玩转C++,c++,数据结构,开发语言)