数据结构与算法:堆栈

一、堆栈

堆栈(Stack)是一组相同数据类型的组合,所有的操作均在堆栈的顶端进行,具有“先进后出”(First In Last Out,FILO)的特性。堆栈结构在计算机中的应用相当广泛,时常被用来解决计算机的问题,例如递归调用,子程序的调用等。堆栈的数据结构原理,类似于下图:

1、堆栈简介

谈到先进后出(First In Last Out)的概念,其实就如同自助餐中餐盘由桌面网上一个一个叠放,且取用时由最上面先拿,这就是典型堆栈概念的应用。由于堆栈是一种抽象数据结构(Abstract Data Type),它有一下特性:

  1. 只能从堆栈的顶端存取数据。
  2. 数据的存取符合“先进后出”的原则。

可以参考下面的堆栈操作示意图:

堆栈的基本运算入下表所示:

create 创建一个空堆栈
push 把数据存压入堆栈顶端,并返回新堆栈
pop 从堆栈顶端弹出数据,并返回新堆栈
isEmpty 判断堆栈是否为空堆栈,是则返回true,不是则返回false
full 判断堆栈是否已满,是则返回true,不是则返回false

堆栈在程序设计领域中,包含数组结构与链表结构两种设计方式,下面分别介绍。

二、使用数组来实现堆栈

以数组结构来实现堆栈的好处是设计的算法都相当简单,但是如果堆栈本身的大小是变动的,而数组的大小只能事先定义好,那么数组规划太大了又浪费空间,规划太小了则又不够用。看下面的一个示例:使用数组结构来设计一个C#程序,并使用循环来控制准备压入或弹出的元素,模仿堆栈的各种操作,其中必须包括压入(push)与弹出(pop)方法,最后还要输出堆栈内所有的元素。代码如下:

定义堆栈类:

using System;

namespace StackByArray
{
    public class StackArray
    {
        // 使用数组来模拟堆栈的声明
        private int[] Stack;
        // 定义指向堆栈顶端的指针
        private int top;

        // 构造函数
        public StackArray(int stackSize)
        {
            // 建立数组
            Stack = new int[stackSize];
            // 数组的索引从0开始,当数组为空时,堆栈的顶端指针为-1
            top = -1;
        }

        /// <summary>
        /// 入栈 把指定的数据压入堆栈顶端
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public bool Push(int data)
        {
            // 判断堆栈顶端的索引是否大于数组大小
            if(top>=Stack.Length)
            {
                Console.WriteLine("堆栈已满,无法在加入");
                return false;
            }
            else
            {
                // 将数据压入堆栈,同时top上移一位
                Stack[++top] = data;
                return true;
            }
        }

        /// <summary>
        /// 判断堆栈是否为空堆栈,如果是则返回true,否则返回false
        /// </summary>
        /// <returns></returns>
        public bool IsEmpty()
        {
            return top == -1 ? true : false;
        }

        /// <summary>
        /// 出栈 从堆栈的顶端弹出数据
        /// </summary>
        /// <returns></returns>
        public int Pop()
        {
            if(IsEmpty())
            {
                return -1;
            }
            else
            {
                // 先将数据弹出,然后在将堆栈指针下移
                return Stack[top--];
            }
        }
    }
}

在Main方法中调用:

using System;

namespace StackByArray
{
    class Program
    {
        static void Main(string[] args)
        {
            int value;
            StackArray stack = new StackArray(10);
            Console.WriteLine("按顺序输入10个数:");
            for (int i = 0; i < 10; i++)
            {
                value = int.Parse(Console.ReadLine());
                // 入栈
                stack.Push(value);
            }
            Console.WriteLine("=================");
            while(!stack.IsEmpty())
            {
                Console.WriteLine($"堆栈弹出顺序:{stack.Pop()}");
            }

            Console.ReadKey();
        }
    }
}

程序输出结果:

三、使用链表来实现堆栈

虽然以数组结构来制作堆栈的好处是制作与设计的算法都相当简单,但是如果堆栈本身是变动的话,数组大小就无法事先规划声明。此时往往会考虑使用最大可能性的数组空间,但是这将造成内存空间的浪费,而用链表来制作堆栈的优点是随时可以动态改变链表的长度,不过缺点是设计时算法较为复杂。下面我们用链表来实现堆栈。

Node节点类:

namespace StackByLinked
{
    /// <summary>
    /// 节点
    /// </summary>
    public class Node
    {
        public int Data { get; set; }

        public Node Next { get; set; }

        public Node(int data)
        {
            Data = data;
            Next = null;
        }
    }
}

堆栈类:

using System;

namespace StackByLinked
{
    public class StackLinked
    {
        // 定义指向堆栈底端的指针
        public Node Front;
        // 定义指向堆栈顶端的指针
        public Node Rear;

        /// <summary>
        /// 判断堆栈是否为空堆栈
        /// </summary>
        /// <returns></returns>
        public bool IsEmpty()
        {
            return Front == null;
        }

        /// <summary>
        /// 打印输出堆栈的内容
        /// </summary>
        public void Print()
        {
            Node current = Front;
            while(current!=null)
            {
                Console.Write(current.Data + " ");
                current = current.Next;
            }
            Console.WriteLine();
        }

        /// <summary>
        /// 入栈
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public void Push(int data)
        {
            Node newNode = new Node(data);
            if(IsEmpty())
            {
                Front = newNode;
                Rear = newNode;
            }
            else
            {
                Rear.Next = newNode;
                Rear = newNode;
            }
        }

        /// <summary>
        /// 出栈 返回栈顶元素
        /// </summary>
        /// <returns></returns>
        public int Pop()
        {
            Node newNode;
            if(IsEmpty())
            {
                Console.WriteLine("当前为空队列");
                return -1;
            }
            newNode = Front;
            if(newNode == Rear)
            {
                Front = null;
                Rear = null;
                Console.WriteLine("当前为空队列");
                return -1;
            }
            else
            {
                while(newNode.Next!=Rear)
                {
                    newNode = newNode.Next;
                }
                int value = Rear.Data;
                // newNode被null赋值,相当于移除了Rear
                newNode.Next = Rear.Next;
                Rear = newNode;
                return value;
            }
        }
    }
}

Main方法调用:

using System;

namespace StackByLinked
{
    class Program
    {
        static void Main(string[] args)
        {
            StackLinked stack = new StackLinked();
            int choice = 0;
            while(true)
            {
                Console.Write("0结束 1把数据加入堆栈 2从堆栈弹出数据:");
                choice = int.Parse(Console.ReadLine());
                if(choice==2)
                {
                    int value= stack.Pop();
                    Console.WriteLine($"弹出的栈顶元素:{value}");
                    Console.WriteLine("数据弹出后堆栈中的内容");
                    stack.Print();
                }
                else if(choice == 1)
                {
                    Console.WriteLine("请输入要加入堆栈的数据:");
                    choice = int.Parse(Console.ReadLine());
                    stack.Push(choice);
                    Console.WriteLine("数据加入后堆栈中的内容:");
                    stack.Print();
                }
                else if(choice == 0)
                {
                    break;
                }
                else
                {
                    Console.WriteLine("输入错误");
                }
            }

            Console.ReadKey();
        }
    }
}

程序运行结果:

猜你喜欢

转载自www.cnblogs.com/dotnet261010/p/12324551.html