在这篇文章中,简单介绍一下栈的原理及实现
一、简述栈
1、概念
栈是一种操作受限的线性表,它的限制在于只能在表尾进行插入或删除,其中表尾称为栈顶,表头称为栈底。
2、特点
栈的特点是“先进后出”
3、实现(C++)
在下述代码中简单实现了栈的“入栈、出栈、取栈顶元素、判栈空,求栈中有效元素的大小”等操作。
具体实现代码如下:
#include <iostream>
#include <assert.h>
using namespace std;

#include <string>
template <class T>
class Stack
{
public:
    Stack()//构造函数
        :_a(NULL)
        , _size(0)
        , _capacity(0)
    {}

    ~Stack()//析构函数
    {
        if (_a)
        {
            delete[] _a;
        }
        _size = 0;
        _capacity = 0;
    }

    void Push(const T& x)//入栈
    {
        _CheckCapacity();
        _a[_size++] = x;
    }
    void Pop()//出栈
    {
        assert(_size > 0);
        _size--;
    }
    T& Top()//取栈顶元素
    {
        assert(_size > 0);
        return _a[_size - 1];
    }
    bool empty()
    {
        return _size == 0;
    }
    size_t Size()
    {
        return _size;
    }
protected:
    void _CheckCapacity()
    {
        if (_size == _capacity)
        {
            _capacity = _capacity * 2 + 3;
            T* tmp = new T[_capacity];
            if (_a)
            {
                //memcpy(tmp, _a, sizeof(T)*_size);
                for (size_t i = 0; i < _size; ++i)
                {
                    tmp[i] = _a[i];
                }
                delete[] _a;
            }
            _a =tmp;
        }
    }


protected:
    T* _a;
    size_t _size;
    size_t _capacity;
};
4、问题简析
  在上述代码中,对于检查容量函数_CheckCapacity()有以下两点说明:
(1)、当空间不够用,需要开辟更大的空间时,为什么要用new,而不用realloc?
原因就是realloc和new有本质上的区别:realloc只负责分配空间;而new除了分配空间之外,还要调自定义类型的构造函数进行初始化。
如果对自定义类型不进行初始化,则会产生错误。因此使用new开辟空间。

(2)、向新开辟的空间拷贝数据时,为什么不能用memcpy?
先来看一段代码:

void _CheckCapacity()
    {
        if (_size == _capacity)
        {
            _capacity = _capacity * 2 + 3;
            T* tmp = new T[_capacity];
            if (_a)
            {
                memcpy(tmp, _a, sizeof(T)*_size);
            /*  for (size_t i = 0; i < _size; ++i)
                {
                    tmp[i] = _a[i];
                }*/
                delete[] _a;
            }
            _a =tmp;
        }
    }   
    与之前的函数相比,这个_CheckCapacity()函数中用的是memcpy进行拷贝,而非for循环,做了这样的变化之后,我们给出两个测试用例,看看会出现什么样的结果?  
     同样给出测试函数1:     
void TestStack1()
{
    Stack<int> s1;

    s1.Push(1);
    s1.Push(2);
    s1.Push(3);
    s1.Push(4);

    while (!s1.empty())
    {
        cout << s1.Top() << endl;
        s1.Pop();
    }
}
该函数的输出结果如下:

测试函数1输出结果
由上图可知,输出正常,而当我们将数据类型换成string,会出现什么样的情况呢?

void TestStack2()
{
    Stack<string> s2;

    s2.Push("aaaaaaaa");
    s2.Push("bbbbbbbbbbbbbbbbb");
    s2.Push("cccccccc");
    s2.Push("dddddddddd");

    while (!s2.empty())
    {
        cout << s2.Top() << endl;
        s2.Pop();
    }
}
上面这段代码的输出结果如下图:

测试函数2输出结果
对于以上的异常输出,有两点疑问:一是为什么程序会崩溃?二是为什么唯独第二个字符串输出的是随机值?
先来看第一个问题:

由上图可知,当旧空间中的string对象调用析构函数,将所指向的空间释放了之后,新开辟空间中的string对象的指针就会指向一段未知的空间,变成野指针,因此程序会崩溃。
再来看第二个问题:
对比一下我们可以发现,只有第二个字符串超过了16个字节,而其他的字符串均没有超过16个字节,那为什么超过16个字节就会出现随机值呢?因为string比较常用,因此系统对它进行了优化,即string对象中除了指针以外,还有16个字节的buff,所以一旦string对象所指向的内存空间中存储的数据超过16个字节,就会产生随机值。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]提供了一个朴素的解法,使用两个来存储字符串,一个用来存储普通字符,另一个用来存储特殊字符。遍历字符串,如果是普通字符则压入第一个,如果是特殊字符则弹出第一个顶元素。最后比较两个是否相同即可判断字符串是否有效。这个解法的时间复杂度是O(M + N),空间复杂度也是O(M + N)。\[1\] 引用\[2\]提供了另一个的应用场景,即判断括号是否有效。遍历字符串,如果是左括号则压入,如果是右括号则判断和顶元素是否匹配,不匹配则返回false,匹配则弹出顶元素。最后判断是否为空即可判断括号是否有效。\[2\] 引用\[3\]也提供了一个判断括号是否有效的解法,使用来操作。遍历字符串,如果是左括号则压入,如果是右括号则判断和顶元素是否匹配,不匹配则返回false,匹配则弹出顶元素。最后判断是否为空即可判断括号是否有效。这个解法使用了HashMap来存储括号的对应关系。\[3\] 综上所述,在解决字符串相关问题中有着广泛的应用,包括判断字符串是否有效、逆波兰表达式等。在解决这些问题时,可以帮助我们保存和处理字符的顺序,从而简化问题的处理过程。 #### 引用[.reference_title] - *1* *3* [Leetcode刷题03-](https://blog.csdn.net/weixin_47802917/article/details/123007699)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v4^insert_chatgpt"}} ] [.reference_item] - *2* [leetCode-类型详解](https://blog.csdn.net/zhiyikeji/article/details/125508011)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v4^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值