定义

栈又名堆栈,是一种操作受限的线性表,仅能在表尾进行插入和删除操作。

它的特点是先进后出,就好比我们往桶里面放盘子,放的时候都是从下往上一个一个放(入栈),取的时候只能从上往下一个一个取(出栈),这个比喻并非十分恰当,比如拿盘子的时候只是习惯从上面开始拿,也可以从中间拿,而栈的话是只能操作最上面的元素,这样比喻只是为了便于了解。

刚开始接触栈可能会有些疑问,我们已经有数组和链表了,为什么还要栈这个操作受限制的数据结构呢?数组和链表虽然灵活,但是操作起来也更容易出错,而栈因为操作受限,在特定场景中使用还是有优势的。

当某个数据集合只涉及在一端插入和删除数据,并且满足先进后出的特性时,我们就应该首选“栈”这种数据结构。

栈的实现

栈的实现方式有两种,一种是基于数组实现的顺序栈,另一种是基于链表实现的链式栈。它的主要操作也就两个,即入栈和出栈,难度并不大。

先了解一下入栈(Push)和出栈(Pop),如下图

    

顺序栈

基于数组实现,就面临着数组大小固定、扩容成本大的问题,下面是使用C#实现出栈和入栈简单功能代码。

    // 基于数组实现的顺序栈
   public class ArrayStack
  {
       private string[] items;  // 数组
       private int count;       // 栈中元素个数
       private int n;           //栈的大小

       // 初始化数组,申请一个大小为n的数组空间
       public ArrayStack(int n)
      {
           this.items = new string[n];
           this.n = n;
           this.count = 0;
      }

       // 入栈操作
       public bool Push(string item)
      {
           // 数组空间不够了,直接返回false,入栈失败。
           if (count == n) return false;
           // 将item放到下标为count的位置,并且count加一
           items[count] = item;
           ++count;
           return true;
      }

       // 出栈操作
       public string Pop()
      {
           // 栈为空,则直接返回null
           if (count == 0) return null;
           // 返回下标为count-1的数组元素,并且栈中元素个数count减一
           string tmp = items[count - 1];
           --count;
           return tmp;
      }
  }

上面代码有一些很明显的缺点,比如存储的数据类型固定为string(C#中使用泛型可以很好的解决),大小固定...这只是简单的功能演示,后面分析C#中Stack源码时这些问题都会被化解。

出栈和入栈的时间复杂度是多少呢?这个很好计算,因为出栈和入栈都只涉及栈顶的元素,所以是O(1)。

空间复杂度呢?还是O(1),因为这里只额外使用了count和n两个临时变量。

‍♂ 空间复杂度是指除了原本的数据存储空间外,算法运行还需要额外的存储空间。例子中大小为n的数组是无法省略的,也就是说这n个空间是必须的,对复杂度不了解的可以点击查看一文搞定算法复杂度分析

链式栈

话不多说,上代码

    // 链表实现栈
   public class LinkStack<T>
  {
       //栈顶指示器
       public Node<T> Top { get; set; }

       //栈中结点的个数
       public int NCount { get; set; }

       //初始化
       public LinkStack()
      {
           Top = null;
           NCount = 0;
      }

       //获取栈的长度
       public int GetLength()
      {
           return NCount;
      }

       //判断栈是否为空
       public bool IsEmpty()
      {
           if ((Top == null) && (0 == NCount))
          {
               return true;
          }
           return false;
      }

       //入栈
       public void Push(T item)
      {
           Node<T> p = new Node<T>(item);
           if (Top == null)
          {
               Top = p;
          }
           else
          {
               p.Next = Top;
               Top = p;
          }
           NCount++;
      }

       //出栈
       public T Pop()
      {
           if (IsEmpty())
          {
               return default(T);
          }
           Node<T> p = Top;
           Top = Top.Next;
           --NCount;
           return p.Data;
      }
  }

   //结点定义
   public class Node<T>
  {
       public T Data;

       public Node<T> Next;

       public Node(T item)
      {
           Data = item;
      }
  }

时间复杂度和空间复杂度均为O(1).

C#中Stack源码分析

前面我们已经知道了顺序栈和链式栈的优缺点,那么C#语言中自带的Stack是基于什么实现的呢?

答案是顺序栈。Stack是一个泛型类,里面定义了一个泛型数组用以存储数据

    private T[] _array;

既然是一个顺序栈,为什么在使用的过程中什么不需要初始化数组大小,也不用担心扩容问题呢?

当我们实例化Stack的时候,会调用它的构造函数,初始化数组大小为0.

    public Stack()
  {
       _array = _emptyArray;
       _size = 0;
       _version = 0;
  }

向数组中添加元素时,会检测数组是否还有空闲容量,如果超出数组大小,将进行扩容

    public void Push(T item)
  {
       if (_size == _array.Length)
      {
           T[] array = new T[(_array.Length == 0) ? 4 : (2 * _array.Length)];
           Array.Copy(_array, 0, array, 0, _size);
           _array = array;
      }

       _array[_size++] = item;
       _version++;
  }

正是因为C#帮我们封装好了,所以我们使用起来才感觉如此的方便。

Push()函数的时间复杂度是多少呢?当栈中有空闲空间时,可以直接添加,它的时间复杂度是O(1)。但当内存不够需要扩容时,需要重新申请内存,进行数据搬移,所以时间复杂度就变成了O(n),其平均时间复杂度也为O(1).

总结

手把手教你实现栈以及C#中Stack源码分析的更多相关文章

  1. 【原】Spark中Client源码分析(二)

    继续前一篇的内容.前一篇内容为: Spark中Client源码分析(一)http://www.cnblogs.com/yourarebest/p/5313006.html DriverClient中的 ...

  2. 【原】Spark中Master源码分析(二)

    继续上一篇的内容.上一篇的内容为: Spark中Master源码分析(一) http://www.cnblogs.com/yourarebest/p/5312965.html 4.receive方法, ...

  3. 【原】 Spark中Worker源码分析(二)

    继续前一篇的内容.前一篇内容为: Spark中Worker源码分析(一)http://www.cnblogs.com/yourarebest/p/5300202.html 4.receive方法, r ...

  4. php中foreach源码分析(编译原理)

    php中foreach源码分析(编译原理) 一.总结 编译原理(lex and yacc)的知识 二.php中foreach源码分析 foreach是PHP中很常用的一个用作数组循环的控制语句.因为它 ...

  5. Spark中决策树源码分析

    1.Example 使用Spark MLlib中决策树分类器API,训练出一个决策树模型,使用Python开发. """ Decision Tree Classifica ...

  6. Java中ArrayList源码分析

    一.简介 ArrayList是一个数组队列,相当于动态数组.每个ArrayList实例都有自己的容量,该容量至少和所存储数据的个数一样大小,在每次添加数据时,它会使用ensureCapacity()保 ...

  7. Java中HashMap源码分析

    一.HashMap概述 HashMap基于哈希表的Map接口的实现.此实现提供所有可选的映射操作,并允许使用null值和null键.(除了不同步和允许使用null之外,HashMap类与Hashtab ...

  8. 深入理解 Node.js 中 EventEmitter源码分析(3.0.0版本)

    events模块对外提供了一个 EventEmitter 对象,即:events.EventEmitter. EventEmitter 是NodeJS的核心模块events中的类,用于对NodeJS中 ...

  9. Netty中FastThreadLocal源码分析

    Netty中使用FastThreadLocal替代JDK中的ThreadLocal[JAVA]ThreadLocal源码分析,其用法和ThreadLocal 一样,只不过从名字FastThreadLo ...

随机推荐

  1. 连通图与Tarjan算法

    引言 Tarjan算法是一个基于深度优先搜索的处理树上连通性问题的算法,可以解决,割边,割点,双连通,强连通等问题. 首先要明白Tarjan算法,首先要知道它能解决的问题的定义. 连通图 无向图 由双 ...

  2. CTFre-getit-WP

    攻防世界getit-WP 日子忙起来人也就忙,CTF慢慢刷,慢就是快. 下载之后,也没管别的直接就IDA打开:下载之后,也没管别的直接就IDA打开: 随便点点看得到三个可以字符串.F5看看: 懂个大概 ...

  3. NOIP 模拟 $28\; \rm 客星璀璨之夜$

    题解 \(by\;zj\varphi\) 概率与期望,考虑 \(\rm dp\) 设 \(dp_{i,j}\) 为消除 \(i-j\) 这一段行星的期望,转移: 枚举 \(k\) 为当前状态下第一个撞 ...

  4. Specification排序orderby

    废话不多说直接贴代码 Specification<Course> sf = new Specification<Course>() { @Override public Pre ...

  5. Qt学习笔记(2)-利用StackWidget实现选项卡式页面

    学习笔记第二篇,利用Qt实现选项卡式的页面,效果如图1.1-图1.3所示.程序实现的功能是通过点击状态栏实现不同页面的切换,实际上Qt中自带有Tab选项卡式的控件,本文利用StackWidge实现类似 ...

  6. qt 中的对象树

    本节内容讲解了什么是对象树以及其所带来的 GUI 编程好处.最后说明了在对象树中析构顺序问题并举了个特殊的例子,来说明平时编程中需要注意的一个点. 什么是对象树? 我们常常听到 QObject 会用对 ...

  7. 将svn项目导出,再导入到其他工作空间

    方法一: 对于一致svn地址,本地没有的项目,直接eclipse中svn检出即可. 若本地有项目,但想导入到另一个工作空间(即拷贝一份,不想再从svn拉),则需要用export方法. 方法二(expo ...

  8. 基于 Mysql 实现一个简易版搜索引擎

    前言 前段时间,因为项目需求,需要根据关键词搜索聊天记录,这不就是一个搜索引擎的功能吗? 于是我第一时间想到的就是 ElasticSearch 分布式搜索引擎,但是由于一些原因,公司的服务器资源比较紧 ...

  9. Spring Mvc原理分析(一)

    Servlet生命周期了解 Servlet的生命(周期)是由容器(eg:Tomcat)管理的,换句话说,Servlet程序员不能用代码控制其生命. 加载和实例化:时机取决于web.xml的定义,如果有 ...

  10. 乌班图安装redis问题

    ot@DESKTOP-5382063:/usr/local/redis/redis-3.0.4# make\ > cd src && make all make[1]: Ente ...