数据结构 之 栈和队列

|----------|-------------------------------------------------------|
| 定义 | 栈是一种后进先出的线性表,要求所有的数据插入和删除都只在表的同一端进行。这一端被称为栈顶,另一端称为栈底。 |
| 特点 | 后进先出 |
| 主要操作 | 1、入栈 2、出栈 3、读取栈顶元素 |

顺序栈

cs 复制代码
public class MyStack<T> where T : struct
{
    private T[] array;

    private int _count;
    private int _capacity;

    public int Count => _count;
    public int Capacity => _capacity;

    //栈初始化
    public MyStack(int size)
    {
        if (size <= 0)
        {
            throw new Exception("容量必须大于0");
        }

        array = new T[size];
        _capacity = size;
        _count = 0;
    }

    //入栈
    public void Push(T value)
    {
        if (_count >= _capacity)
        {
            throw new Exception("容量不够");
            return;
        }

        array[_count] = value;
        _count++;
    }

    //出栈
    public T Pop()
    {
        if (_count == 0)
        {
            throw new Exception("容量不够");
            return default;
        }

        var value = array[_count - 1];
        array[_count - 1] = default;
        _count--;

        return value;
    }

    //获取栈顶元素
    public T Peek()
    {
        if (_count == 0)
        {
            throw new Exception("容量不够");
            return default;
        }

        return array[_count - 1];
    }

    //从栈顶开始打印栈
    public void Print()
    {
        var str = "";

        str += $"栈中元素个数:{_count}  容量为:{_capacity}\n";
        
        for (var i = _count - 1; i >= 0; i--)
        {
            str += $"{array[i]} ";
        }
        Debug.Log(str);
    }
}

链栈

cs 复制代码
//链栈
public class LinkStack<T> where T : struct
{
    public class LinkNode
    {
        public T value;
        public LinkNode nextNode;

        public LinkNode(T _value)
        {
            value = _value;
        }
    }

    private LinkNode headNode;//头结点
    private int _count;
    public int Count => _count;

    public LinkStack()
    {
        headNode = new LinkNode(default);
    }

    //入栈
    public void Push(T value)
    {
        var targetNode = GetNodeByIndex(0);
        var newNode = new LinkNode(value);
        if (targetNode == null)
        {
            headNode.nextNode = newNode;
        }
        else
        {
            newNode.nextNode = targetNode;
            headNode.nextNode = newNode;
        }
        _count++;
    }

    //出栈
    public bool Pop(out T result)
    {
        var targetNode = GetNodeByIndex(1);
        if (targetNode == null)
        {
            result = default;   
            return false;
        }
        result = headNode.nextNode.value;
        headNode.nextNode = targetNode;
        _count--;
        return true;
    }

    //读取栈顶元素
    public bool Peek(out T result)
    {
        var targetNode = GetNodeByIndex(0);
        if (targetNode == null)
        {
            result = default;
            return false;
        }
        result = targetNode.value;
        return true;
    }

    //打印链栈中数据
    public void Print()
    {
        string str = "";
        ReadLinkList(ref str, headNode.nextNode);
        Console.WriteLine($"数据个数:{_count}   链栈元素:{str}");
    }

    private void ReadLinkList(ref string str, LinkNode node)
    {
        if (node == null)
        {
            return;
        }
        str += $"{node.value} ";
        ReadLinkList(ref str, node.nextNode);
    }

    //获取链表中指定索引的节点
    private LinkNode GetNodeByIndex(int index)
    {
        if (index < 0 || index >= _count)
        {
            return null;
        }

        var linkNode = headNode.nextNode;
        for (var i = 0; i < _count; i++)
        {
            if (index == i)
            {
                return linkNode;
            }
            else
            {
                linkNode = linkNode.nextNode;
            }
        }
        return null;
    }
}

队列

|------|-------------------------------------------------------------------|
| 定义 | 队列是一种先进先出的线性表,要求所有的数据插入在表的一端进行,而删除在表的另一端进行。进行插入的一端叫队尾,进行删除的一端叫队头。 |
| 特点 | 先进先出 |
| 主要操作 | 1、入队 2、出队 3、读取队头元素 |

普通队列

使用链表实现

cs 复制代码
//链队
public class LinkQueue<T> where T : struct
{
    public class LinkNode
    {
        public T value;
        public LinkNode nextNode;

        public LinkNode(T _value)
        {
            value = _value;
        }
    }

    private LinkNode headNode;//头结点
    private int _count;
    public int Count => _count;

    public LinkQueue()
    {
        headNode = new LinkNode(default);
    }

    //入队
    public void Enqueue(T value)
    {
        var targetNode = GetNodeByIndex(_count - 1);
        var newNode = new LinkNode(value);
        if (targetNode == null)
        {
            headNode.nextNode = newNode;
        }
        else
        {
            targetNode.nextNode = newNode;  
        }
        _count++;
    }

    //出队
    public bool Dequeue(out T result)
    {
        var targetNode = GetNodeByIndex(1);
        if (targetNode == null)
        {
            result = default;
            return false;
        }
        result = headNode.nextNode.value;
        headNode.nextNode = targetNode;
        _count--;
        return true;
    }

    //读取队头元素
    public bool Peek(out T result)
    {
        var targetNode = GetNodeByIndex(0);
        if (targetNode == null)
        {
            result = default;
            return false;
        }
        result = targetNode.value;
        return true;
    }

    //打印队列中数据
    public void Print()
    {
        string str = "";
        ReadLinkList(ref str, headNode.nextNode);
        Console.WriteLine($"数据个数:{_count}   队列元素:{str}");
    }

    private void ReadLinkList(ref string str, LinkNode node)
    {
        if (node == null)
        {
            return;
        }
        str += $"{node.value} ";
        ReadLinkList(ref str, node.nextNode);
    }

    //获取链表中指定索引的节点
    private LinkNode GetNodeByIndex(int index)
    {
        if (index < 0 || index >= _count)
        {
            return null;
        }

        var linkNode = headNode.nextNode;
        for (var i = 0; i < _count; i++)
        {
            if (index == i)
            {
                return linkNode;
            }
            else
            {
                linkNode = linkNode.nextNode;
            }
        }
        return null;
    }
}

循环队列

循环队列是一种使用固定大小的数组 ,并将数组在逻辑上视为一个环状结构的队列。

它的核心逻辑是使用两个指针(或索引):

front(队首):指向队列中的第一个元素。

rear(队尾):指向队列中下一个可以插入元素的位置。

基本操作逻辑

1、创建一个固定大小的数组。

frontrear 都初始化为 0。

2、入队

将新元素放入 rear 所指向的位置。然后,rear 指针不是简单地加1,而是通过一个取模运算向后移动:rear = (rear + 1) % capacitycapacity 是数组的总容量)。

这个取模操作是关键,它使得当 rear 到达数组末尾时,下一个位置会"绕回"到数组的开头,形成一个循环。

3、出队

返回 front 所指向的元素。然后,front 指针同样通过取模运算向后移动:front = (front + 1) % capacity

4、判空

front == rear 时,队列为空。

5、判满

这是循环队列的一个关键点。因为 frontrear 在循环中移动,rear 有可能追上 front

判断队列为满的条件是:(rear + 1) % capacity == front

注意:为了区分"空"和"满"的情况,我们有意牺牲了一个存储单元。也就是说,一个容量为 n 的循环队列,最多只能存放 n-1 个元素。如果不这样做,当队列满时,frontrear 也会相等,就和队列为空的条件冲突了。

操作图示

1、初始化

2、入队元素1

3、依次入队2、3、4

4、出队第一个元素

5、再次入队两个元素即达满队

代码实现

cs 复制代码
public class CycleQueue<T> where T:struct
{
    private T[] array;

    private int _count;
    private int _capacity;
    private int front;//队首游标
    private int rear;//队尾游标
    
    public int Count => _count;
    public int Capacity => _capacity;
    
    //队列初始化
    public CycleQueue(int size)
    {
        if (size <= 0)
        {
            throw new Exception("容量必须大于0");
        }

        array = new T[size];
        _capacity = size;
        _count = 0;
        front = 0;
        rear = 0;
    }
    
    public bool Enqueue(T value)
    {
        if (IsFullQueue())
        {
            Debug.LogError("满了!");
            return false;
        }
        
        array[rear] = value;
        rear = (rear + 1) % _capacity;
        _count++;
        return true;
    }
    
    public bool Dequeue(out T value)
    {
        if (IsEmptyQueue())
        {
            Debug.LogError("队伍为空!");
            value = default;
            return false;
        }

        value = array[front];
        array[front] = default;
        front = (front + 1) % _capacity;
        _count--;
        return true;
    }
    
    //获取队头元素
    public T Peek()
    {
        if (_count == 0)
        {
            throw new Exception("队列为空!");
            return default;
        }

        return array[front];
    }
    
    //判断是否为空队
    public bool IsEmptyQueue()
    {
        return front == rear;
    }
    
    //判断是否为满队
    public bool IsFullQueue()
    {
        return (rear + 1) % _capacity == front;
    }

    //打印循环队列
    public void Print()
    {
        var str = "";
        str += $"队列中元素个数:{_count}  容量为:{_capacity}\n";
        var flag = front;
        while (flag != rear)
        {
            str += $"{array[flag]} ";
            flag = (flag + 1) % _capacity;
        }
        Debug.Log(str);
    }
}

双端队列

双端队列是一种允许从两端进行插入和删除操作的线性数据结构。它结合了栈和队列的特性。

|---------|-------------|
| 在前端添加元素 | AddFirst |
| 在后端添加元素 | AddLast |
| 从前端删除元素 | RemoveFirst |
| 从后端删除元素 | RemoveLast |
| 查看前端元素 | PeekFirst |
| 查看后端元素 | PeekLast |

cs 复制代码
//双端队列
public class DoubleQueue<T> where T : struct
{
    public class LinkNode
    {
        public T value;
        public LinkNode lastNode;
        public LinkNode nextNode;

        public LinkNode(T _value)
        {
            value = _value;
        }
    }

    private LinkNode headNode;//队头结点
    private LinkNode tailNode;//队尾结点

    private uint _count;
    public uint Count => _count;

    public DoubleQueue()
    {
        headNode = new LinkNode(default);
        tailNode = new LinkNode(default);

        headNode.nextNode = tailNode;
        tailNode.lastNode = headNode;
    }

    //从前端为队头,读取首元素
    public bool PeekFirst(out T value)
    {
        var isEmpty = _count == 0;
        value = isEmpty ? default : headNode.nextNode.value;
        return !isEmpty;
    }

    //以后端为队头,读取首元素
    public bool PeekLast(out T value)
    {
        var isEmpty = _count == 0;
        value = isEmpty ? default : tailNode.lastNode.value;
        return !isEmpty;
    }

    //从队头添加数据
    public void AddFirst(T value)
    {
        var newNode = new LinkNode(value);
        if (_count == 0)
        {
            headNode.nextNode = newNode;
            newNode.lastNode = headNode;
            newNode.nextNode = tailNode;
            tailNode.lastNode = newNode;
        }
        else
        {
            var next = headNode.nextNode;
            headNode.nextNode = newNode;
            newNode.lastNode = headNode;
            newNode.nextNode = next;
            next.lastNode = newNode;
        }
        _count++;
    }

    //从队尾添加数据
    public void AddLast(T value)
    {
        var newNode = new LinkNode(value);
        if (_count == 0)
        {
            headNode.nextNode = newNode;
            newNode.lastNode = headNode;
            newNode.nextNode = tailNode;
            tailNode.lastNode = newNode;
        }
        else
        {
            var last = tailNode.lastNode;
            last.nextNode = newNode;
            newNode.lastNode = last;
            newNode.nextNode = tailNode;
            tailNode.lastNode = newNode;
        }
        _count++;
    }

    //从队头出队
    public bool RemoveFirst(out T result)
    {
        if(_count == 0)
        {
            result = default;
            return false;
        }
        else
        {
            result = headNode.nextNode.value;
            var next = headNode.nextNode.nextNode;
            headNode.nextNode = next;
            next.lastNode = headNode;
            _count--;
            return true;
        }
    }

    //从队尾出队
    public bool RemoveLast(out T result)
    {
        if (_count == 0)
        {
            result = default;
            return false;
        }
        else
        {
            result = tailNode.lastNode.value;
            var last = tailNode.lastNode.lastNode;
            tailNode.lastNode = last;
            last.nextNode = tailNode;
            _count--;
            return true;
        }
    }

    //打印双端队列中数据
    public void Print()
    {
        string str = "";
        ReadLinkList(ref str, headNode.nextNode);
        Console.WriteLine($"数据个数:{_count}   列表:{str}");
    }

    private void ReadLinkList(ref string str, LinkNode node)
    {
        if (node == tailNode) return;
        str += $"{node.value} ";
        ReadLinkList(ref str, node.nextNode);
    }
}
相关推荐
zs宝来了1 小时前
HOT100系列-堆类型题
数据结构·算法·排序算法
报错小能手1 小时前
数据结构 带头节点的链表
数据结构·链表
sin_hielo2 小时前
leetcode 1590
数据结构·算法·leetcode
吃着火锅x唱着歌2 小时前
LeetCode 2748.美丽下标对的数目
数据结构·算法·leetcode
自然语2 小时前
人工智能之数字生命-学习的过程
数据结构·人工智能·深度学习·学习·算法
你好~每一天2 小时前
从传统行业到AI入门:我的CAIE Level I学习体验与思考
大数据·数据结构·人工智能·学习·jupyter·idea
wyiyiyi2 小时前
【数据结构+算法】非递归遍历二叉树的理解
大数据·数据结构·笔记·算法·leetcode·数据分析
fashion 道格3 小时前
从地图导航到数据结构:解锁带权有向图的邻接链表奥秘
c语言·数据结构·链表
专注API从业者3 小时前
构建分布式京东商品数据采集器:基于微服务的架构设计与实现
数据结构·数据库·分布式·微服务·架构