数据结构——队列,栈
队列
有时候,我们会遇到一些数字密码游戏,比如,给出一串数字: 4,6,8,3,5,2,2,6,0,解密规则是这样的,首先将第1个数删除,紧接着将第2个数字放到这串数的末尾,再将第3个数删除并将第4个数放到这串数的末尾,再将第5个数删除……如此往复,直到最后一个数也删除。按照刚才删除的顺序,将这些删除的数连在一起就是我们需要得到的目标数字。OK,这时候,拿出纸和笔,或者用一些小便签或者小卡片,将这9个数字分别写下来,开始模拟解密过程~
其实解密的过程就像是将这些数进行排队,每次从最前面拿两个,第1个扔掉,第2个放到末尾,如果不出意外的话,你能得到的目标数字串是:4,8,5,2,0,3,6,2,6。
在算法的世界里,没错,它就是队列,那么我们怎么用代码形式来实现它呢?
首先需要一个容器来存放这些数字,这里我们使用ArrayList,因为它的大小可以随着我们的需要而动态的增加,定义如下:
1 ArrayList Quenum = new ArrayList(); 2 Quenum.Add(4); 3 Quenum.Add(6); 4 Quenum.Add(8); 5 Quenum.Add(3); 6 Quenum.Add(5); 7 Quenum.Add(2); 8 Quenum.Add(2); 9 Quenum.Add(6); 10 Quenum.Add(0);
接下来就是模拟解密的过程了。解密的第一步是将第一个数删除,删除一个数最简单的方法是将所有后面的数都往前挪动一位,将前面的数覆盖,比如排队买票的时候,前方买好票的人离开,后面所有人就向前走一步,当然,这么做很耗费时间。
既然是队列,就有队首和队尾。这里引入队首变量head,和队尾变量tail,由于队列的原则是先进先出,因此,如果要删除一个数的话,只需要head++就可以了。而新增一个数也很简单,只需要在队尾加上即可,也就是tail++,按照这个思路,我们可以写出如下代码,代码并不复杂:
1 public static void Queue(ArrayList num) 2 { 3 int head = 0; 4 int tail = num.Count - 1; 5 while (head < tail) 6 { 7 Console.Write("{0} ", num[head]); 8 head++; 9 num.Add(num[head]); 10 tail++; 11 head++; 12 } 13 Console.Write("{0}", num[tail]); 14 }
运行得到测试结果:
这串数字有什么含义呢?哈哈,这是QQ群:代码之家--NET的QQ号,有兴趣的朋友们可以考虑加入哦~由于群刚建立不久,目前还没什么人呢~期待大家的到来~
题外话说完了,继续回到话题,什么是队列呢?队列是一种特殊的线性结构,它只允许在队列首部(head)进行删除操作,称为“出队”,而在队列尾部(tail)进行插入操作,称为“入队”。当队列中没有元素时(head==tail),称为空队列。队列是学习广度优先搜索及队列优化的Bellman-Ford最短算法的核心数据结构。
栈
刚刚说到的队列是一种先进先出的数据结构,还有一种是后进先出的数据结构,叫做栈。栈限定为只能在一端进行插入和删除操作。生活中也有很多栈的例子,比如我们在吃桶装薯片的时候,要想吃掉最后一片,就必须先把前面的薯片全部吃完(假设不考虑透明“抽屉”的存在),还有平时我们能见到的弹枪,最后装入的子弹会被第一个打出去。栈的实现也很简单,上面我们用解密qq号的问题来阐述了队列,这里我们继续趣味问题之旅。
这里的例子我们用回文,回文字符串是正读反读都相同的字符序列,比如“上海自来水来自海上”,英文单词里也有不少回文单词,比如:dad, eye, madam...但是像ahah这就不算一个回文了,OK,接下来我们要如何通过栈来判断一串字符串是不是回文呢?
从回文的规则我们可以知道,如果一个字符串是回文的话,那么它必须是中间对称的,因此,我们需要求出一个中点mid,假设传入的是string类型的str,那么,中点代码即是:
1 int mid = str.Length / 2 - 1;
定义出原始的字符串,以及需要入栈操作的字符串:
1 char[] originalString = str.ToCharArray(); 2 char[] stackString = new char[str.Length];
将mid之前的字符依次入栈:
1 for (int i = 0; i <= mid; i++) 2 { 3 stackString[top] = originalString[i]; 4 top++; 5 }
紧接着就是对mid前后的字符进行对比了:
1 for (int i = next; i < str.Length; i++) 2 { 3 top--; 4 char x = originalString[i]; 5 char c = stackString[top]; 6 7 if (originalString[i] != stackString[top]) 8 { 9 break; 10 } 11 12 } 13 if (top == 0) 14 { 15 Console.WriteLine("{0} is Palin number.", str); 16 } 17 else 18 { 19 Console.WriteLine("{0} is not Palin number.", str); 20 }
因此,全部的代码如下:
1 public static void Stack(string str) 2 { 3 char[] originalString = str.ToCharArray(); 4 char[] stackString = new char[str.Length]; 5 int top = 0; 6 int next; 7 int mid = str.Length / 2 - 1; 8 for (int i = 0; i <= mid; i++) 9 { 10 stackString[top] = originalString[i]; 11 top++; 12 } 13 14 if (mid % 2 == 0) 15 { 16 next = mid + 1; 17 } 18 else 19 { 20 next = mid + 2; 21 } 22 23 for (int i = next; i < str.Length; i++) 24 { 25 top--; 26 char x = originalString[i]; 27 char c = stackString[top]; 28 29 if (originalString[i] != stackString[top]) 30 { 31 break; 32 } 33 34 } 35 if (top == 0) 36 { 37 Console.WriteLine("{0} is Palin number.", str); 38 } 39 else 40 { 41 Console.WriteLine("{0} is not Palin number.", str); 42 } 43 }
当然,简单点的话,也可以这么做:
1 private static bool PalinNumber<T>(T value) 2 { 3 string str; 4 str = value.ToString(); 5 char x, y; 6 bool flag = true; 7 for (int i = 0; i < str.Length / 2; i++) 8 { 9 x = str[i]; 10 y = str[str.Length - 1 - i]; 11 if (x != y) 12 { 13 flag = false; 14 } 15 } 16 return flag; 17 } 18 19 public static void Stack<T>(T value) 20 { 21 if (PalinNumber<T>(value)) 22 { 23 Console.WriteLine("{0} is Palin number.", value); 24 } 25 else 26 { 27 Console.WriteLine("{0} is not Palin number", value); 28 } 29 }
很多人会更乐意使用下面的方法,虽然这里第一种方法略显繁杂,但更好地体现了栈的使用方式。栈也可以用来进行验证括号的匹配,这里就不多赘述了。
简单的两个趣味题阐述简单的队列与栈的概念,也藉由此对队列和栈有一个大致的了解。