C#实现顺序栈和链栈的代码实例

C#实现顺序栈和链栈的代码实例可以分成以下几个步骤:

第一步:定义栈的数据结构

在C#中,我们可以使用class或者struct定义一个栈的数据结构。在这里,我们以class为例,定义一个名为Stack的类:

public class Stack<T>
{
    private T[] _items;
    private int _count;

    public Stack(int capacity)
    {
        _items = new T[capacity];
        _count = 0;
    }

    public void Push(T item)
    {
        if (_count >= _items.Length)
        {
            var newItems = new T[_items.Length * 2];
            Array.Copy(_items, newItems, _items.Length);
            _items = newItems;
        }

        _items[_count++] = item;
    }

    public T Pop()
    {
        if (_count <= 0)
            throw new InvalidOperationException("Stack is empty");

        var item = _items[--_count];
        _items[_count] = default(T);
        return item;
    }

    public T Peek()
    {
        if (_count <= 0)
            throw new InvalidOperationException("Stack is empty");

        return _items[_count - 1];
    }

    public bool IsEmpty => _count == 0;
}

在这个实现中,我们使用了一个泛型类型参数T来表示栈的元素类型,使用一个数组_items来保存栈的元素,使用一个整数_count来表示栈的元素个数。在构造函数中,我们使用参数capacity来指定栈的容量。

第二步:实现顺序栈

2.1 栈的创建

首先,我们需要定义一个顺序栈类ArrayStack,并在其中定义一个基于数组的栈实现,代码如下:

public class ArrayStack<T>
{
    private T[] _items;
    private int _count;

    public ArrayStack(int capacity)
    {
        _items = new T[capacity];
        _count = 0;
    }

    // ...
}

同样,我们使用了一个泛型类型参数T来表示栈的元素类型,使用一个数组_items来存储栈中的元素。然后,我们在构造函数中使用参数capacity作为数组的长度,并初始化count为0表示栈中没有任何元素。

2.2 栈的入栈

然后,我们可以实现两个基本的操作:入栈(push)和出栈(pop)。入栈操作将一个元素添加到栈中,代码如下:

public void Push(T item)
{
    if (_count >= _items.Length)
    {
        var newItems = new T[_items.Length * 2];
        Array.Copy(_items, newItems, _items.Length);
        _items = newItems;
    }

    _items[_count++] = item;
}

入栈操作将元素存储在数组的尾部,并将计数器加1。如果数组已经满了,我们将其扩展为原来的两倍。

2.3 栈的出栈

出栈操作是将栈顶元素弹出并返回它的值。如果栈是空的,那么出栈操作将抛出异常。下面是出栈操作的代码:

public T Pop()
{
    if (_count <= 0)
        throw new InvalidOperationException("Stack is empty");

    var item = _items[--_count];
    _items[_count] = default(T);
    return item;
}

出栈操作首先检查栈是否为空,然后将_count减1以获取栈顶元素的索引。接下来,我们从数组中删除该元素,并将其设置为默认值null或0。

至此,我们已经成功地实现了一个基于数组的顺序栈。

第三步:实现链栈

3.1 栈的创建

接下来,我们将实现一个基于链表的链栈。首先,我们需要定义节点类Node,代码如下:

public class Node<T>
{
    public T Value { get; }

    public Node<T> Next { get; set; }

    public Node(T value)
    {
        Value = value;
        Next = null;
    }
}

我们使用泛型类型参数T来表示节点的值类型,使用Value属性来访问节点的值,使用Next属性来访问指向下一个节点的引用。在节点类构造函数中,我们使用参数value来初始化节点的值,将Next属性设置为null。

然后,我们需要定义一个链式栈类LinkedStack,代码如下:

public class LinkedStack<T>
{
    private Node<T> _top;

    public LinkedStack()
    {
        _top = null;
    }

    // ...
}

我们使用泛型类型参数T来表示链栈元素的类型。在构造函数中,我们将栈顶节点_top初始化为null。

3.2 栈的入栈

然后我们可以定义一个入栈(push)方法,将一个元素压入栈中,代码如下:

public void Push(T item)
{
    var newNode = new Node<T>(item);
    newNode.Next = _top;
    _top = newNode;
}

入栈操作首先创建一个新节点newNode,其值为item。然后将新节点的Next属性设置为栈顶节点_top,然后将新节点指定为栈顶节点_top。

3.3 栈的出栈

接下来,我们实现出栈(pop)方法,将栈顶元素弹出并返回它的值,代码如下:

public T Pop()
{
    if (_top == null)
        throw new InvalidOperationException("Stack is empty");

    var item = _top.Value;
    _top = _top.Next;
    return item;
}

出栈操作首先检查栈是否为空。然后,我们获取栈顶元素的值,将_top指向下一个元素,并返回弹出的元素的值。

至此,我们已经成功地实现了一个基于链表的链栈。

示例

示例1:顺序栈使用示例

现在,我们可以使用顺序栈实现一个简单的括号匹配程序。我们需要对字符序列中的括号进行匹配,并报告任何非法的括号匹配。下面是程序的实现:

public bool IsValid(string input)
{
    var stack = new ArrayStack<char>(input.Length);

    foreach (var ch in input)
    {
        if (ch == '(' || ch == '[' || ch == '{')
        {
            stack.Push(ch);
        }
        else if (ch == ')' || ch == ']' || ch == '}')
        {
            if (stack.IsEmpty)
                return false;

            var top = stack.Pop();
            if ((top == '(' && ch != ')') || (top == '[' && ch != ']') || (top == '{' && ch != '}'))
                return false;
        }
    }

    return stack.IsEmpty;
}

上面的方法使用了一个顺序栈来保存左括号字符('(', '[', '{')。对于每个右括号字符(')', ']', '}'),我们从栈中弹出一个字符,并将其与当前字符进行比较。如果匹配,则继续进行下一个字符;否则,表示括号匹配非法,返回false。如果字符串处理完毕后栈为空,则表示括号匹配合法,返回true。

示例2:链栈使用示例

下面是一个示例程序,使用链栈实现基本的计算器:

public class Calculator
{
    private readonly LinkedStack<double> _stack;

    public Calculator()
    {
        _stack = new LinkedStack<double>();
    }

    public double Evaluate(string expression)
    {
        var tokens = expression.Split();
        foreach (var token in tokens)
        {
            switch (token)
            {
                case "+":
                    _stack.Push(_stack.Pop() + _stack.Pop());
                    break;
                case "-":
                    _stack.Push(-_stack.Pop() + _stack.Pop());
                    break;
                case "*":
                    _stack.Push(_stack.Pop() * _stack.Pop());
                    break;
                case "/":
                    var numerator = _stack.Pop();
                    var denominator = _stack.Pop();
                    _stack.Push(denominator / numerator);
                    break;
                default:
                    _stack.Push(double.Parse(token));
                    break;
            }
        }

        var result = _stack.Pop();
        if (!_stack.IsEmpty)
            throw new ArgumentException("Expression contains too many values");

        return result;
    }
}

上面的程序使用了一个链栈来保存运算符和操作数。对于输入表达式的每个单词(由空格分隔),我们根据语法规则将其转换为一个数字或一个运算符,并在栈上执行相应的操作。最后,我们从栈中弹出结果值,并检查栈是否为空(如果不为空,则表示表达式中包含过多的数字)。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C#实现顺序栈和链栈的代码实例 - Python技术站

(0)
上一篇 2023年6月7日
下一篇 2023年6月7日

相关文章

  • c#中的常用ToString()方法总结

    C#中的常用ToString()方法总结 在C#编程中,ToString()方法是十分常用的方法之一。它用于将一个对象转化为字符串表示形式。本篇攻略将详细讲解C#中常用的ToString()方法及其用法。 ToString()方法的基本用法 在C#中,ToString()方法是定义在Object类中的虚方法,它可以被任意类型重写。因为所有类型都继承自Obje…

    C# 2023年6月1日
    00
  • 使用.Net Core编写命令行工具(CLI)的方法

    使用.Net Core编写命令行工具(CLI)的方法 在.Net Core中,可以使用C#编写命令行工具(CLI),以便在终端中执行各种任务。本攻略将详细介绍使用.Net Core编写命令行工具(CLI)的方法。 步骤 按照以下步骤使用.Net Core编写命令行工具(CLI): 创建一个新的.Net Core控制台应用程序。 dotnet new cons…

    C# 2023年5月16日
    00
  • .Net Core3.1 API访问进行频次限制

    首先,安装AspNetCore.RateLimit NuGet 包。您可以通过NuGet包管理器控制台或Visual Studio的NuGet包管理器来执行此操作。安装后,您将在项目中看到一个名为AspNetCoreRateLimit的文件夹,其中包含中间件的配置类。 接下来,您需要在 Startup.cs 文件中注册中间件。您可以在ConfigureSer…

    C# 2023年4月18日
    00
  • C# File.AppendText(string path):在指定文件末尾添加文本内容,并返回StreamWriter对象

    File.AppendText(string path) 是C#中的一个方法,用于向指定文件的末尾追加文本内容,如果文件不存在则会创建。下面是该方法的完整攻略: 方法定义: public static StreamWriter AppendText(string path) 方法参数: path:字符串,表示要追加文本的文件名和路径。 方法返回值: Stre…

    C# 2023年4月19日
    00
  • C#中的问号(?号)用法小结

    下面是“C#中的问号(?号)用法小结”的详细讲解: 什么是问号(?号)? 问号(?号)是C# 2.0引入的一个新运算符,也称为“空值传播运算符(null conditional operator)”或者“Elvis运算符(因为它看起来像Elvis Presley的头发)”。它的作用是在一个对象的成员操作中及早地发现并处理空值(null)。 为什么使用问号(?…

    C# 2023年5月14日
    00
  • C# javaScript函数的相互调用

    C#和JavaScript都是常用的编程语言,在Web开发中,经常需要对这两种语言进行交互。通过C#代码调用JavaScript函数可以为Web程序添加更多的交互性和动态性。同时,JavaScript函数也可以调用C#代码来实现更为复杂的功能,增强Web程序的性能和灵活性。 下面是“C#和JavaScript函数相互调用”的完整攻略: C#调用JavaScr…

    C# 2023年6月8日
    00
  • 浅析.net core 抛异常对性能影响

    浅析 .NET Core 抛异常对性能影响 在 .NET Core 中,抛出异常是一种常见的错误处理方式。然而,抛出异常会对性能产生一定的影响。本攻略将浅析 .NET Core 抛异常对性能的影响,并提供多个示例说明。 抛异常对性能的影响 抛出异常会对性能产生一定的影响,主要表现在以下几个方面: CPU 时间:抛出异常会消耗一定的 CPU 时间,这会影响应用…

    C# 2023年5月17日
    00
  • ASP.NET Core依赖注入系列教程之服务的注册与提供

    ASP.NET Core依赖注入系列教程之服务的注册与提供攻略 在ASP.NET Core应用程序中,依赖注入是一种常用的设计模式,用于管理应用程序中的对象和服务。本攻略将介绍如何在ASP.NET Core应用程序中注册和提供服务。 步骤 以下是注册和提供服务的步骤: 创建服务类。 创建一个服务类,该类将提供应用程序所需的服务。例如: public inte…

    C# 2023年5月17日
    00
合作推广
合作推广
分享本页
返回顶部