数据结构与算法(六)栈的链式存储结构

188 阅读4分钟

这是我参与11月更文挑战的第26天,活动详情查看:2021最后一次更文挑战

栈的链式存储结构,简称为链栈。

想想看,栈只是栈顶来做插入和删除操作,栈顶放在链表的头部还是尾部呢?

由于单链表有头指针,而栈顶指针也是必须的,那么干嘛不让他们合二为一呢,所以比较好的办法是把栈顶放到单链表的头部。

另外栈顶在头部了,那么单链表的头结点也就失去了意义,通常对于链栈来说,是不需要头结点的。

同样对于链栈来说,基本不存在栈满的情况,除非内存已经没有可用的空间了。

栈的链式存储结构与线性表的链式存储结构差不多,但是其相对比较简单,只在表尾进行操作。

刚好复习下之前学习的线性表中的链表。

但是链栈和线性表的链式存储结构是有区别的,链栈不需要头结点,有栈顶就可以了。

首先定义一个链栈的接口:

/// <summary>
    /// 链栈接口
    /// </summary>
    public interface IChainStack<T>
    {
        /// <summary>
        /// 清空栈
        /// </summary>
        /// <returns></returns>
        bool ClearStack();
        /// <summary>
        /// 判断栈是否为空
        /// </summary>
        /// <returns>true:空、false:不空</returns>
        bool IsEmptyStack();
        /// <summary>
        /// 获取栈顶元素
        /// </summary>
        /// <returns></returns>
        GetTopStack();
        /// <summary>
        /// 入栈
        /// </summary>
        /// <returns></returns>
        bool PushStack(T val);
        /// <summary>
        /// 出栈
        /// </summary>
        /// <returns></returns>
        bool PopStack();
        /// <summary>
        /// 获取栈长度
        /// </summary>
        /// <returns></returns>
        int LengthStack();
        /// <summary>
        /// 判断栈是否已满
        /// </summary>
        /// <returns></returns>
        bool IsFull();
        /// <summary>
        /// 打印栈
        /// </summary>
        void paint();
    }

 

然后定义一个链栈的节点

/// <summary>
    /// 链栈节点类
    /// </summary>
    public class ChainStackNode<T>
    {
        /// <summary>
        /// 链表中的数据域
        /// </summary>
        public T data;
        /// <summary>
        /// 链表中的下一个节点的指针域
        /// </summary>
        public ChainStackNode<T> Next;
        // 每个结点有两部分组成,指针+数据,第一个节点没有数据,只有指针,最后一个节点只有数据,没有指针。
        // 那么构造函数需要有三次重载。
        /// <summary>
        /// 中间节点构造函数
        /// </summary>
        /// <param name="val"></param>
        /// <param name="index"></param>
        public ChainStackNode(T val, ChainStackNode<T> index)
        {
            this.data = val;
            this.Next = index;
        }
        /// <summary>
        /// 最后一个节点构造函数
        /// </summary>
        /// <param name="val"></param>
        public ChainStackNode(T val)
        {
            this.data = val;
            this.Next = null;
        }
        /// <summary>
        /// 第一个节点构造函数
        /// </summary>
        /// <param name="val"></param>
        /// <param name="index"></param>
        public ChainStackNode(ChainStackNode<T> index)
        {
            // 默认值
            this.data = default(T);
            this.Next = index;
        }
 
        /// <summary>
        /// 定义一个空节点
        /// </summary>
        /// <param name="val"></param>
        /// <param name="index"></param>
        public ChainStackNode()
        {
            this.data = default(T);
            this.Next = null;
        }
    }

 

最后我们定义一个类来实现上边定义的接口;实现链栈的操作功能。

public class ChainStackList<T> : IChainStack<T>
    {
        /// <summary>
        /// 存储栈顶指针,其是有指针,没有数据
        /// </summary>
        public ChainStackNode<T> top = null;
        /// <summary>
        /// 栈数量
        /// </summary>
        public int count = 0;
 
        public ChainStackList()
        {
            top = new ChainStackNode<T>();
            count = 0;
        }
 
        /// <summary>
        /// 清空栈
        /// </summary>
        /// <returns></returns>
        public bool ClearStack()
        {
            count = 0;
            top = null;
            return true;
        }
        /// <summary>
        /// 获取栈顶数据
        /// </summary>
        /// <returns></returns>
        public T GetTopStack()
        {
            return top.Next.data;
        }
        /// <summary>
        /// 栈是否为空
        /// </summary>
        /// <returns></returns>
        public bool IsEmptyStack()
        {
            if (top.Next == null || count == 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        /// <summary>
        /// 栈是已满,链接基本上不存在满栈的情况,因为满栈基本上意味着系统崩溃
        /// </summary>
        /// <returns></returns>
        public bool IsFull()
        {
            if (top == null)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        /// <summary>
        /// 获取栈长度
        /// </summary>
        /// <returns></returns>
        public int LengthStack()
        {
            return count;
        }
        /// <summary>
        /// 打印栈
        /// </summary>
        public void paint()
        {
            ChainStackNode<T> tempTop = top;
            ChainStackNode<T> temp = new ChainStackNode<T>();
            for (int i = 0; i < count+1; i++)
            {
                temp = tempTop.Next;
                Console.WriteLine("data:"+ tempTop.data+"index:"+top.Next);
                tempTop = temp;
            }
        }
        /// <summary>
        /// 出栈
        /// </summary>
        /// <returns></returns>
        public bool PopStack()
        {
            if (IsEmptyStack())
            {
                Console.WriteLine("链栈为空,无法弹出");
                return false;
            }
            // 定义一个节点存储栈顶的下一个节点
            ChainStackNode<T> temp = top.Next;
            // 现在栈顶的下一个节点等于原来栈顶下一个节点的先一个节点
            top.Next = temp.Next;
            count--;
            return true;
        }
        /// <summary>
        /// 入栈
        /// </summary>
        /// <param name="val">入栈数据</param>
        /// <returns></returns>
        public bool PushStack(T val)
        {
            ChainStackNode<T> temp = new ChainStackNode<T>(val);
            if (temp == null)
            {
                Console.WriteLine("栈已满");
                return false;
            }
            // 将栈顶的下一个节点赋值给新节点
            temp.Next = top.Next;
            // 将新节点赋值给栈顶的下一个值
            top.Next = temp;
            count++;
            return true;
        }
    }

 

调用示例:

static void Main(string[] args)
        {
            ChainStackList<string> chainStrackList = new ChainStackList<string>();
            chainStrackList.PushStack("11111");
            chainStrackList.PushStack("22222");
            chainStrackList.PushStack("33333");
            chainStrackList.PushStack("44444");
            chainStrackList.PushStack("55555");
 
            Console.WriteLine("添加数据");
            Console.WriteLine("=========================================");
            Console.WriteLine("打印数据");
            chainStrackList.paint();
            Console.WriteLine("=========================================");
 
            chainStrackList.PopStack();
            chainStrackList.PopStack();
            Console.WriteLine("弹出栈");
            Console.WriteLine("=========================================");
            Console.WriteLine("打印数据");
            chainStrackList.paint();
            Console.WriteLine("=========================================");
 
            Console.WriteLine("获取栈顶数据:"+ chainStrackList.GetTopStack());
            Console.WriteLine("=========================================");
 
            Console.WriteLine("获取栈的长度:" + chainStrackList.LengthStack());
            Console.WriteLine("=========================================");
 
            Console.ReadKey();
        }

u=787310632,4078543751&fm=26&gp=0.jpg 

如上图所示:

在写链栈的时候,只需要搞清楚一件事情就可以了,栈顶top存储的是事实上的栈顶的指针。

Top.Next = 真正的栈顶。

那么添加数据时:

newNode.Next = top.Next;// 将栈顶的下一个节点赋值给新节点
top.Next = newNode;// 将新节点赋值给栈顶的下一个节点

 

删除数据时:

Temp = top.Next;// 定义一个节点存储栈顶的下一个节点
Top.next = temp.next;// 现在栈顶的下一个节点等于原来栈顶下一个节点的先一个节点

 

以上基本上就是链栈的精髓所在,最后放上一张上边代码最终运行的效果图:代码实例在文末,可下载。

微信截图_20191216231718.png

 

有好的建议,请在下方输入你的评论。

欢迎访问个人博客 guanchao.site

欢迎访问小程序:

在这里插入图片描述