C#实现简单的栈和队列

C#提供了栈和队列,我们自己也可以尝试简单实现。
而且这可以作为一个很好的面试题,主要考察c#基础、类的设计以及数据结构。根据不同的职位需求可以考察选择不同的考察难度和角度。4年前我第一次参加面试并进现在的公司,职位基本是公司的最低岗位了。当时面的题目就是:实现一个栈。

简单的实现如下(考虑到顺序结构实现队列比较麻烦,采用链式结构):
PS:感谢@LoveZmJ的提醒,下面的代码有个bug,对Test有兴趣的人可以先不要看评论,玩玩“大家来找茬”~~

 

首先是结点类的实现:

 1  // 结点类
 2     // 注意应该使用泛型
 3     public class MyNode<T>
 4     {
 5         // 存储的数据
 6         public T Data
 7         {
 8             get { return _data; }
 9             set { _data = value; }
10         }
11 
12         // 指向下一个结点
13         public MyNode<T> next { get { return _next; } set { _next = value; } }
14 
15         //构造函数,不提供无参版本
16         public MyNode(T data)
17         {
18             _data = data;
19             _next = null;
20         }
21 
22         // 私有字段
23         private T _data;
24         private MyNode<T> _next;
25     }

然后抽象一个简单的父类:

 1 // 为栈和队列提取一些通用的成员,抽象出一个父类,此处用接口还是抽象函数?
 2     // 在C#中Stack和Queue继承自两个接口:IEnumerable<T>, ICollection
 3     // 但是作为简单的实现(特别是作为面试题答案),还是写成抽象类比较好,原因有二:
 4     // 1. 可以在抽象类中实现一些通用方法,子类只需要继承就可以直接用,可以简化代码
 5     // 2. 抽象出来的父类,和子类Stack、Queue可以看做“is-a”的关系。
 6     // 当然也可以是非抽象的普通类,但是处于“不能实例化”的考虑,应该是抽象的
 7     // 注意使用泛型
 8     public abstract class AbstactList<T>
 9     {
10         // 头结点,其后才是第一个结点
11         // 注意应该是protected,对外是不可见的
12         protected MyNode<T> Header { get; set; }
13         // 尾结点,即是最后一个结点
14         protected MyNode<T> Tail { get; set; }
15         // 当前结点个数,注意是只读属性

16         public int NoteCount { get { return _noteCount; } }
17 
18         // 构造函数,初始化头结点和结点个数
19         public AbstactList()
20         {
21             // 注意此处default(T)的使用
22             Header = new MyNode<T>(default(T));
23             Tail = Header;
24             _noteCount = 0;
25         }
26 
27         // “出的操作”,对于栈和队列都是一样的,所以可以写在父类里
28         // 注意应该从“头”端出,时间复杂度为O(1)
29         // 如果从“尾”端出,则会造成时间复杂度为O(n)
30         protected T Out()
31         {
32             // 注意判空,只要一个条件就可以了,将所有的条件都写在这里可以有利于在测试的时候检测出bug
33             if (Header.next == null && _noteCount == 0 && NoteCount == 0 && IsEmpty())
34             {
35                 throw new InvalidOperationException("Is empty!");
36             }
37 
38             MyNode<T> outNode = Header.next;
39             Header.next = Header.next.next;
40             _noteCount--;
41             return outNode.Data;
42         }
43 
44         // 判空
45         public bool IsEmpty()
46         {
47             return _noteCount == 0 ? true : false;
48         }
49 
50         // 对于“出”的操作,栈和队列是有区别的,所以申明成抽象方法
51         // 到子类中去具体实现
52         protected abstract void In(T NodeData);
53 
54         // 子类中还要用到,所以是Protected
55         protected int _noteCount;
56     }

栈的具体实现:

 1 // 栈的实现,继承自抽象类
 2     public class MyStack<T> : AbstactList<T>
 3     {
 4         // 实现“进”的方法,在“头”端
 5         // 由于实现抽象类方法的当前方法默认是虚的,所以无法设为private
 6         protected override void In(T NodeData)
 7         {
 8             MyNode<T> Node = new MyNode<T>(NodeData);
 9             Node.next = Header.next;
10             Header.next = Node;
11             _noteCount++;
12         }
13 
14         // 进栈,只是将操作改个名字
15         public void Push(T NodeData)
16         {
17             In(NodeData);
18         }
19 
20         // 出栈,只是将操作改个名字
21         public T Pop()
22         {
23             return Out();
24         }
25     }

队列的实现:

 1 // 队列的实现,继承自抽象类
 2     public class MyQueue<T> : AbstactList<T>
 3     {
 4         // 实现“进”的方法,在“尾”端
 5         // 由于实现抽象类方法的当前方法默认是虚的,所以无法设为private
 6         protected override void In(T NodeNode)
 7         {
 8             MyNode<T> Node = new MyNode<T>(NodeNode);
 9             Tail.next = Node;
10             Tail = Node;
11             _noteCount++;
12         }
13 
14         public void EnQue(T NodeData)
15         {
16             In(NodeData);
17         }
18 
19         public T DeQue()
20         {
21             return Out();
22         }
23     }

单元测试:

对栈和队列的单元测试
 1  [TestClass]
 2     public class UnitTest1
 3     {
 4         [TestMethod]
 5         public void StackTest()
 6         {
 7             MyStack<char> charStack = new MyStack<char>();
 8             Assert.IsTrue(charStack.IsEmpty());
 9             charStack.Push('a');
10             Assert.IsFalse(charStack.IsEmpty());
11             charStack.Push('b');
12             Assert.AreEqual(charStack.Pop(), 'b');
13             charStack.Push('c');
14             Assert.AreEqual(charStack.NoteCount,2);
15             Assert.AreEqual(charStack.Pop(), 'c');
16             Assert.AreEqual(charStack.Pop(), 'a');
17             Assert.IsTrue(charStack.IsEmpty());
18             
19             try
20             {
21                 charStack.Pop();
22             }
23             catch (Exception ex)
24             {
25                 Assert.IsInstanceOfType(ex,typeof(InvalidOperationException));
26             }
27         }
28 
29         [TestMethod]
30         public void QueueTest()
31         {
32             MyQueue<int> intQueue = new MyQueue<int>();
33             Assert.IsTrue(intQueue.IsEmpty());
34             intQueue.EnQue(1);
35             intQueue.EnQue(2);
36             Assert.AreEqual(intQueue.DeQue(), 1);
37             intQueue.EnQue(3);
38             Assert.AreEqual(intQueue.NoteCount,2);
39             Assert.AreEqual(intQueue.DeQue(), 2);
40             Assert.AreEqual(intQueue.DeQue(), 3);
41             Assert.IsTrue(intQueue.IsEmpty());
42 
43             try
44             {
45                 intQueue.DeQue();
46             }
47             catch (Exception ex)
48             {
49                 Assert.IsInstanceOfType(ex, typeof(InvalidOperationException));
50             }
51         }
52     }

后记:
作为更高难度,还可以加一个要求:实现Sort方法。由于使用的泛型,因此这应该是一个通用的排序方法,有两种方法可以实现:接口和委托,以后再写。

posted on 2013-03-24 16:33  I过T  阅读(5921)  评论(4编辑  收藏  举报

导航