数据结构之队列和栈

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/Leaderxin/article/details/102703084

不知道大家有没有玩过一个叫《拖板车》的扑克游戏(貌似暴露了年龄。。),本篇我们尝试让计算机来玩这个游戏,并且我们在确定双方的牌和顺序后就能立马知道谁将是赢家。在玩这个游戏前我们需要先了解下队列和栈。

队列简介

队列是一种特殊的线性结构,它只允许在队列的首部进行删除操作,这称作“出队”,而在队列的尾部进行插入操作,这称为“入队"。这和我们实际生活中排队买东西的场景一样,先买东西的肯定是队首的第一个,新来的人肯定是排队在最后面。我们称之为先进先出(FIFO)原则。

队列实现

队列的数据结构实现起来比较简单,用一个数组控制先进先出即可,.net平台已经内置了这种数据结构,我们一起看一下万能的微软是如何实现队列的。代码较多,我们看一下关键部分入队和出队的代码。

private T[] _array;

private int _head;

private int _tail;

private int _size;

private int _version;	
//出队
public T Dequeue()
		{
			//判断队列中是否有元素,如果没有抛出空队列异常
			if (_size == 0)
			{
				System.ThrowHelper.ThrowInvalidOperationException(System.ExceptionResource.InvalidOperation_EmptyQueue);
			}
			//取得队首元素,作为返回结果
			T result = _array[_head];
			//将队首元素重置为默认值
			_array[_head] = default(T);
			//_head指向队首下一个元素
			_head = (_head + 1) % _array.Length;
			//队列长度减一
			_size--;
			//版本更新
			_version++;
			return result;
		}
//入队
public void Enqueue(T item)
		{
			//.net在初始化队列的时候会给数组一个初始长度,当数组数量达到一定条件会扩容,每次增加4的长度
			if (_size == _array.Length)
			{
				int num = (int)((long)_array.Length * 200L / 100);
				if (num < _array.Length + 4)
				{
					num = _array.Length + 4;
				}
				SetCapacity(num);
			}
			//将新元素加到数组末尾
			_array[_tail] = item;
			//_tail指向末尾后一个索引
			_tail = (_tail + 1) % _array.Length;
			//队列长度+1
			_size++;
			_version++;
		}		

栈简介

栈和队列类似,也是一种特殊的线性结构,同样在尾部进行插入操作,但是删除元素同样是在队尾,采用先进后出原则。假如我们往一个直径只有乒乓球大小的水杯分别放入2,3,1号乒乓球,那么我们想要拿出2号球就必须先分别拿出1号球和3号球,这里的水杯就是典型的栈结构。同样地我们看下微软关于栈的关键代码实现。

private T[] _array;

private int _size;

private int _version;
//出栈
public T Pop()
		{
			//判断栈中是否还有元素可以出栈,没有则抛出空栈异常
			if (_size == 0)
			{
				System.ThrowHelper.ThrowInvalidOperationException(System.ExceptionResource.InvalidOperation_EmptyStack);
			}
			//版本+1
			_version++;
			//取的数组尾部元素作为返回结果,并且长度记录-1
			T result = _array[--_size];
			//将原来的尾部元素重置为默认值
			_array[_size] = default(T);
			return result;
		}
//入栈
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;
			}
			//将新元素添加到数组尾部同时长度记录+1
			_array[_size++] = item;
			_version++;
		}		

拖板车游戏

游戏玩法

将一副扑克牌随机分为两份,小明和小红各一份。小明先将手中的第一张扑克牌放在桌上,然后小红也拿出手中的第一张牌放在小明刚打出的扑克牌上面,就像这样两人交替出牌。出牌时,如果某人打出的牌和桌上某张牌数字相同,即可将这两张牌及中间所夹的牌全部收走并放到自己手牌的末尾。当其中任意一人手中牌为空的时候,游戏结束,对手获胜。
在这里插入图片描述
我们分析下这个游戏过程,两个人的操作都是一样的,手里的牌相当于一个队列,出牌是出队,赢牌是入队。而桌上的牌相当于一个栈,出牌的时候是入栈,赢牌的时候是出栈。这样我们的实现思路就清晰了,下面我们用程序来模拟这个游戏。

编码实现

static void Main(string[] args)
        {
            var all = new List<string>();
            //首先将52张牌放入集合
            for (int i = 0; i < 4; i++) //每种牌四张,循环四次
            {
                for (int j = 2; j < 15; j++) //13种牌【2,3,4,5,6,7,8,9,10,J,Q,K,A】
                {
                    if (j <= 10)
                    {
                        all.Add(j.ToString());
                    }
                    else
                    {
                        switch (j)
                        {
                            case 11:
                                all.Add("J");
                                break;
                            case 12:
                                all.Add("Q");
                                break;
                            case 13:
                                all.Add("K");
                                break;
                            case 14:
                                all.Add("A");
                                break;
                            default:
                                break;
                        }
                    }
                }
            }
            //然后将54张牌随机分到小明和小红手里
            //定义两个队列用来记录小明和小红的手牌
            Queue<string> xiaoming = new Queue<string>();
            Queue<string> xiaohong = new Queue<string>();
            var strming = "小明手上的牌依次为:";
            var strhong = "小红手上的牌依次为:";
            for (int i = 0; i < 52; i++)
            {
                var random = new Random().Next(0, 51 - i);
                if (i%2==0)
                {
                    xiaoming.Enqueue(all[random]);
                    strming += all[random]+" ";
                }
                else
                {
                    xiaohong.Enqueue(all[random]);
                    strhong += all[random] + " ";
                }               
                all.RemoveAt(random);
            }
            Console.WriteLine(strming);
            Console.WriteLine(strhong);
            //开始游戏
            //先定义一个栈来记录桌面上的牌
            var stack = new Stack<string>();
            while (xiaoming.Count>0&&xiaohong.Count>0)
            {
                var first = xiaoming.Dequeue(); //小明出牌
                Console.WriteLine("小明出牌:"+first);
                //判断桌面上是否有刚出的牌
                if (stack.Contains(first))
                {
                    //如果有那这两张牌及中间的牌都入队小明
                    xiaoming.Enqueue(first);         
                    while (stack.First()!=first)
                    {
                        xiaoming.Enqueue(stack.Pop());
                    }
                    xiaoming.Enqueue(stack.Pop());
                    Console.Write("小明赢得一轮,桌上剩余牌为:");
                    foreach (var item in stack)
                    {
                        Console.Write(item + " ");
                    }
                    Console.WriteLine("");
                }
                else
                {
                    //没有的话放到桌面上
                    stack.Push(first);
                }
                first = xiaohong.Dequeue(); //小红出牌
                Console.WriteLine("小红出牌:" + first);
                //判断桌面上是否有刚出的牌
                if (stack.Contains(first))
                {
                    //如果有那这两张牌及中间的牌都入队小红
                    xiaohong.Enqueue(first);
                    while (stack.First() != first)
                    {
                        xiaohong.Enqueue(stack.Pop());
                    }
                    xiaohong.Enqueue(stack.Pop());
                    Console.Write("小红赢得一轮,桌上剩余牌为:");
                    foreach (var item in stack)
                    {
                        Console.Write(item + " ");
                    }
                    Console.WriteLine("");
                }
                else
                {
                    //没有的话放到桌面上
                    stack.Push(first);
                }
            }
            if (xiaoming.Count==0)
            {
                Console.WriteLine("小红获胜!");
            }
            else
            {
                Console.WriteLine("小明获胜!");
            }
            
            Console.ReadKey();
        }

来看一下执行结果,输出较多,只截了第一张和最后一张:
在这里插入图片描述
在这里插入图片描述
有趣的拖板车就到此为止啦~
欢迎关注我的算法系列博客:
算法(https://blog.csdn.net/leaderxin/article/category/9434286)

猜你喜欢

转载自blog.csdn.net/Leaderxin/article/details/102703084