整个项目都托管在了 Github 上:https://github.com/ikesnowy/Algorithms-4th-Edition-in-Csharp
这一节内容可能会用到的库文件有 Generics,同样在 Github 上可以找到。
善用 Ctrl + F 查找题目。
首先是 FixedCapacityStackOfStrings 类,官方 JAVA 版本参考:FixedCapacityStackOfStrings.java
IsFull() 的实现比较简单,判断 N 与数组长度是否相等即可。
FixedCapacityStackOfStrings 类
using System; using System.Collections; using System.Collections.Generic; namespace _1._3._1 { class FixedCapacityStackOfStrings : IEnumerable<string> { private string[] a; private int N; /// <summary> /// 默认构造函数。 /// </summary> /// <param name="capacity">栈的大小。</param> public FixedCapacityStackOfStrings(int capacity) { this.a = new string[capacity]; this.N = 0; } /// <summary> /// 检查栈是否为空。 /// </summary> /// <returns></returns> public bool IsEmpty() { return this.N == 0; } /// <summary> /// 检查栈是否已满。 /// </summary> /// <returns></returns> public bool IsFull() { return this.N == this.a.Length; } /// <summary> /// 将一个元素压入栈中。 /// </summary> /// <param name="item">要压入栈中的元素。</param> public void Push(string item) { this.a[N] = item; this.N++; } /// <summary> /// 从栈中弹出一个元素,返回被弹出的元素。 /// </summary> /// <returns></returns> public string Pop() { this.N--; return this.a[N]; } /// <summary> /// 返回栈顶元素(但不弹出它)。 /// </summary> /// <returns></returns> public string Peek() { return this.a[N - 1]; } public IEnumerator<string> GetEnumerator() { return new ReverseEnmerator(this.a); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } private class ReverseEnmerator : IEnumerator<string> { private int current; private string[] a; public ReverseEnmerator(string[] a) { this.current = a.Length; this.a = a; } string IEnumerator<string>.Current => a[current]; object IEnumerator.Current => a[current]; void IDisposable.Dispose() { this.current = -1; this.a = null; } bool IEnumerator.MoveNext() { if (this.current == 0) return false; this.current--; return true; } void IEnumerator.Reset() { this.current = a.Length; } } } }首先是 Stack<> 类的实现,官方 JAVA 版本参考:Stack.java
输出内容:was best times of the was the it
Stack<> 类
using System; using System.Collections; using System.Collections.Generic; using System.Text; namespace Generics { public class Stack<Item> : IEnumerable<Item> { private Node<Item> first; private int count; /// <summary> /// 默认构造函数。 /// </summary> public Stack() { this.first = null; this.count = 0; } /// <summary> /// 复制构造函数。 /// </summary> /// <param name="s"></param> public Stack(Stack<Item> s) { if (s.first != null) { this.first = new Node<Item>(s.first); for (Node<Item> x = this.first; x.next != null; x = x.next) { x.next = new Node<Item>(x.next); } } } /// <summary> /// 检查栈是否为空。 /// </summary> /// <returns></returns> public bool IsEmpty() { return this.first == null; } /// <summary> /// 返回栈内元素的数量。 /// </summary> /// <returns></returns> public int Size() { return this.count; } /// <summary> /// 将一个元素压入栈中。 /// </summary> /// <param name="item">要压入栈中的元素。</param> public void Push(Item item) { Node<Item> oldFirst = this.first; this.first = new Node<Item>(); this.first.item = item; this.first.next = oldFirst; this.count++; } /// <summary> /// 将一个元素从栈中弹出,返回弹出的元素。 /// </summary> /// <returns></returns> public Item Pop() { if (IsEmpty()) throw new InvalidOperationException("Stack Underflow"); Item item = this.first.item; this.first = this.first.next; this.count--; return item; } /// <summary> /// 返回栈顶元素(但不弹出它)。 /// </summary> /// <returns></returns> public Item Peek() { if (IsEmpty()) throw new InvalidOperationException("Stack Underflow"); return this.first.item; } /// <summary> /// 将两个栈连接。 /// </summary> /// <param name="s1">第一个栈。</param> /// <param name="s2">第二个栈(将被删除)。</param> /// <returns></returns> public static Stack<Item> Catenation(Stack<Item> s1, Stack<Item> s2) { if (s1.IsEmpty()) { s1.first = s2.first; s1.count = s2.count; } else { Node<Item> last = s1.first; while (last.next != null) { last = last.next; } last.next = s2.first; s1.count += s2.count; } s2 = null; return s1; } /// <summary> /// 创建栈的浅表副本。 /// </summary> /// <returns></returns> public Stack<Item> Copy() { Stack<Item> temp = new Stack<Item>(); temp.first = this.first; temp.count = this.count; return temp; } public override string ToString() { StringBuilder s = new StringBuilder(); foreach (Item n in this) { s.Append(n); s.Append(' '); } return s.ToString(); } public IEnumerator<Item> GetEnumerator() { return new StackEnumerator(this.first); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } private class StackEnumerator : IEnumerator<Item> { private Node<Item> current; private Node<Item> first; public StackEnumerator(Node<Item> first) { this.current = new Node<Item>(); this.current.next = first; this.first = this.current; } Item IEnumerator<Item>.Current => current.item; object IEnumerator.Current => current.item; void IDisposable.Dispose() { this.current = null; this.first = null; } bool IEnumerator.MoveNext() { if (this.current.next == null) return false; this.current = this.current.next; return true; } void IEnumerator.Reset() { this.current = this.first; } } } }这个问题的通用解法见习题 1.3.46 的解答。
第 2、6、7 个不可能产生,可以画个栈模拟一下。
第 2 个
输出数 栈内数 4 0~3 6 0~3 + 5 8 0~3 + 5 + 7 7 0~3 + 5 5 0~3 3 0~2 2 0~1 9 0~1 0 Error 第 6 个 输出数 栈内数 0 null 4 1~3 6 1~3 + 5 5 1~3 3 1~2 8 1~2 + 7 1 Error 第 7 个 输出数 栈内数 1 0 4 0 + 2~3 7 0 + 2~3 + 5~6 9 0 + 2~3 + 5~6 + 8 8 0 + 2~3 + 5~6 6 0 + 2~3 + 5 5 0 + 2~3 3 0 + 2 0 Error
官方 JAVA 版本参考:Parentheses.java。
遇到左括号就入栈,遇到右括号就检查是否和栈顶的左括号匹配,如果匹配则弹栈,否则返回 false。
结束时如果栈不为空则返回 false,否则返回 true。
实际上是用除二取余法求一个十进制数的二进制形式。
利用一个栈对队列元素进行反序操作。
先把队列中的元素全部入栈,再依次弹出并加入队列中。
链表实现的话就是返回第一个结点 first 的 item 字段。
数组实现的话就是返回 first 对应的数组元素。
这里给出链表实现,完整实现见习题 1.3.2 的代码。
首先是 DoublingStackOfStrings 类,据我猜测应该是用数组实现的栈,扩容时长度增加一倍,缩短时长度减小一半。
官方 JAVA 代码参考:FixedCapacityStackOfString.java。
DoublingStackOfStrings 类
using System; using System.Collections; using System.Collections.Generic; namespace _1._3._8 { class DoublingStackOfStrings : IEnumerable<string> { private string[] items; private int count; /// <summary> /// 新建一个字符串栈。 /// </summary> public DoublingStackOfStrings() { this.items = new string[2]; this.count = 0; } /// <summary> /// 检查栈是否为空。 /// </summary> /// <returns></returns> public bool IsEmpty() { return this.count == 0; } /// <summary> /// 返回栈中字符串的数量。 /// </summary> /// <returns></returns> public int Size() { return this.count; } /// <summary> /// 向栈中压入一个字符串。 /// </summary> /// <param name="s"></param> public void Push(string s) { if (this.count == this.items.Length) Resize(this.items.Length * 2); this.items[this.count] = s; this.count++; } /// <summary> /// 从栈中弹出一个字符串,返回被弹出的元素。 /// </summary> /// <returns></returns> public string Pop() { if (IsEmpty()) throw new InvalidOperationException("Stack underflow"); count--; //缩小长度 if (count > 0 && count <= items.Length / 4) Resize(items.Length / 2); return items[count]; } /// <summary> /// 返回栈顶元素(但不弹出它)。 /// </summary> /// <returns></returns> public string Peek() { if (IsEmpty()) throw new InvalidOperationException("Stack underflow"); return items[count - 1]; } /// <summary> /// 为栈重新分配空间,超出空间的元素将被舍弃。 /// </summary> /// <param name="capcity">重新分配的空间大小。</param> private void Resize(int capcity) { string[] temp = new string[capcity]; for (int i = 0; i < this.count; ++i) { temp[i] = items[i]; } items = temp; } public IEnumerator<string> GetEnumerator() { return new StackEnumerator(this.items); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } private class StackEnumerator : IEnumerator<string> { int current; string[] items; public StackEnumerator(string[] items) { this.items = items; current = -1; } string IEnumerator<string>.Current => this.items[this.current]; object IEnumerator.Current => this.items[this.current]; void IDisposable.Dispose() { this.items = null; this.current = -1; } bool IEnumerator.MoveNext() { if (this.current == items.Length - 1) return false; this.current++; return true; } void IEnumerator.Reset() { this.current = -1; } } } }Program.cs
using System; namespace _1._3._8 { /* * 1.3.8 * * 给定以下输入,给出 DoublingStackOfStrings 的数组的内容和大小。 * * it was - the best - of times - - - it was - the - - * */ class Program { static void Main(string[] args) { DoublingStackOfStrings stack = new DoublingStackOfStrings(); string[] input = "it was - the best - of times - - - it was - the - -".Split(' '); foreach (string n in input) { if (n == "-") stack.Pop(); else stack.Push(n); } foreach (string s in stack) { Console.Write(s + ' '); } Console.WriteLine($"\nStack Size: {stack.Size()}"); } } }在计算中序表达式算法的基础上做修改。 压入数字时将该数字所在的位置也一并压入。 弹出数字进行运算时在位置靠前的数字前加上左括号。 A + B ) * C + D ) ) 为例。 A 压入栈中并记录位置 。 + 压入栈中。 B 压入栈中并记录位置。 ) 计算,在 A 之前加入左括号,结果 E 压入栈中,位置为 A 的位置。 * 压入栈中。 C 压入栈中并记录位置。 + 压入栈中。 D 压入栈中并记录位置。 ) 计算,在 C 之前加入左括号,结果 F 压入栈中,位置为 C 的位置。 ) 计算,在 E 之前加入左括号(也就是 A 之前),结果 G 压入栈中,位置为 E 的位置。
官方 JAVA 代码:InfixToPostfix.java。
其实就是把右括号换成相应运算符 对于 (A + B),忽略左括号,数字直接输出,运算符入栈,遇到右括号时再弹出 结果 A B +,变成后序表达式
官方 JAVA 代码:EvaluatePostfix.java。
遇到数字就入栈,遇到运算符就弹出两个数字运算,再把结果入栈。
如果倒着读取的话也可以用递归做,当作前序表达式计算即可。
先用 foreach 语句遍历一遍栈,把所有元素都压入一个临时栈中。
此时临时栈变成了源栈的一个倒序副本。
再将临时栈中的元素依次压入目标栈中,就得到了源栈的一个副本。
除了第一个以外都不可能。
根据题意,0 一定是最先入列的。
那么根据队列的特性,0 一定是最先出列的,因此除第一个以外其他几个序列都不可能。
对于 ResizingArrayQueueOfStrings 类,给出官方 JAVA 代码参考:ResizingArrayQueue.java。
ResizingArrayQueueOfStrings 类
using System; using System.Collections; using System.Collections.Generic; namespace _1._3._14 { class ResizingArrayQueueOfStrings<Item> : IEnumerable<Item> { private Item[] q; private int count; private int first; private int last; public ResizingArrayQueueOfStrings() { this.q = new Item[2]; this.count = 0; this.first = 0; } public bool IsEmpty() { return this.count == 0; } public int Size() { return this.count; } private void Resize(int capacity) { if (capacity < 0) throw new ArgumentException("capacity should be above zero"); Item[] temp = new Item[capacity]; for (int i = 0; i < count; ++i) { temp[i] = this.q[(this.first + i) % this.q.Length]; } this.q = temp; this.first = 0; this.last = this.count; } public void Enqueue(Item item) { if (this.count == this.q.Length) { Resize(this.count * 2); } this.q[this.last] = item; this.last++; if (this.last == this.q.Length) this.last = 0; this.count++; } public Item Dequeue() { if (IsEmpty()) throw new InvalidOperationException("Queue underflow"); Item item = this.q[first]; this.q[first] = default(Item); this.count--; this.first++; if (this.first == this.q.Length) this.first = 0; if (this.count > 0 && this.count <= this.q.Length / 4) Resize(this.q.Length / 2); return item; } public Item Peek() { if (IsEmpty()) throw new InvalidOperationException("Queue underflow"); return this.q[first]; } public IEnumerator<Item> GetEnumerator() { return new QueueEnumerator(this.q, this.first, this.last); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } private class QueueEnumerator : IEnumerator<Item> { int current; int first; int last; Item[] q; public QueueEnumerator(Item[] q, int first, int last) { this.current = first - 1; this.first = first; this.last = last; this.q = q; } Item IEnumerator<Item>.Current => this.q[this.current]; object IEnumerator.Current => this.q[this.current]; void IDisposable.Dispose() { } bool IEnumerator.MoveNext() { if (this.current == this.last - 1) return false; this.current++; return true; } void IEnumerator.Reset() { this.current = this.first - 1; } } } }Program.cs
using System; namespace _1._3._14 { /* * 1.3.14 * * 编写一个类 ResizingArrayQueueOfStrings, * 使用定长数组实现队列的抽象,然后扩展实现, * 使用调整数组的方法突破大小的限制。 * */ class Program { public static void Main(string[] args) { ResizingArrayQueueOfStrings<string> queue = new ResizingArrayQueueOfStrings<string>(); string[] input = "to be or not to - be - - that - - - is".Split(' '); foreach (string s in input) { if (!s.Equals("-")) queue.Enqueue(s); else if (!queue.IsEmpty()) Console.Write(queue.Dequeue() + ' '); } Console.WriteLine("(" + queue.Size() + " left on queue)"); } } }方法有很多,只要把所有输入保存,之后算出倒数第 k 个是正数第几个就可以了。
这里先全部入队,之后算出是正数第几个,再把前面的元素全部出队,剩下的第一个就是要求的元素了。
在习题 1.2.19 里已经写好了接受字符串作为参数构造函数(可以到 这个链接 里查看),
这里只要把所有字符串读入并调用相应构造函数就可以了。
ReadDates()
/// <summary> /// 从标准输入按行读取所有日期,返回一个日期数组。 /// </summary> /// <returns></returns> public static Date[] ReadDates() { char[] split = new char[] { '\n' }; string[] input = Console.In.ReadToEnd().Split(split, StringSplitOptions.RemoveEmptyEntries); Date[] d = new Date[input.Length]; for (int i = 0; i < input.Length; ++i) { d[i] = new Date(input[i]); } return d; }和前一题类似,按行读取输入再调用相应构造函数就可以了。
ReadTransactions()
/// <summary> /// 从标准输入中按行读取所有交易信息,返回一个 Transaction 数组。 /// </summary> /// <returns></returns> public static Transaction[] ReadTransactions() { char[] split = new char[] { '\n' }; string[] input = Console.In.ReadToEnd().Split(split, StringSplitOptions.RemoveEmptyEntries); Transaction[] t = new Transaction[input.Length]; for (int i = 0; i < input.Length; ++i) { t[i] = new Transaction(input[i]); } return t; }Program.cs
using System; namespace _1._3._17 { /* * 1.3.17 * * 为 Transaction 类完成练习 1.3.16 * */ class Program { static void Main(string[] args) { //用 Ctrl + Z 标记结束输入 Transaction[] t = Transaction.ReadTransactions(); foreach (Transaction n in t) { Console.WriteLine(n.ToString()); } } } }删除该结点的下一个结点。
如下图,没有任何结点指向 y 结点,失去了所有引用的 y 结点会被 GC 清理掉。
建立一个结点引用 Cur,让它移动到尾结点的前一个结点,让那个结点的 next 变为 null。
和上一题类似,只不过这次让 Cur 移动 k – 1 次即可。
遍历整条链表,方法和前两题类似,用一个结点引用 Cur 去访问就可以了。
在 x 之后插入 t,如下图所示。
由于先后问题,y 在第一句代码执行完毕之后无法访问,t 的 next 会指向自己。
直接把该节点的 next 域设为 null,后续元素就会因无法访问而被清理掉。
见练习 1.3.22,加入一些对边界情况的处理即可。
之前已经写过了删除指定结点(习题 1.3.20)和查找指定结点(习题 1.3.21),结合使用即可。
遍历一遍即可。
其实链表本身就是一个递归结构,链表的定义可以用递归的方式表示:
链表 = 头结点A + 链表B = 头结点A + 头结点B + 链表C……
所以 Max() 可以这么写:
Max(Node<Item> Cur, int nowmax)
如果 Cur 为空,则直接返回 nowmax。
否则检查 Cur 结点的值是否大于目前找到的最大值 nowmax。
如果不大于,继续查找下一个结点,返回 Max(Cur.next, nowmax)
否则,把 nowmax 修改为当前结点的值,继续查找,返回 Max(Cur.next, Cur.item)
其实就是一个长这样的链表:
显然说 first 和最后一个节点的指针重复了,所以我们只需要保留 last 的指针就行了。
入队(注意顺序)
出队
Queue.cs
using System; using System.Collections; using System.Collections.Generic; using System.Text; namespace _1._3._29 { public class Queue<Item> : IEnumerable<Item> { private Node<Item> last; private int count; /// <summary> /// 默认构造函数。 /// </summary> public Queue() { this.last = null; this.count = 0; } /// <summary> /// 检查队列是否为空。 /// </summary> /// <returns></returns> public bool IsEmpty() { return this.last == null; } /// <summary> /// 返回队列中元素的数量。 /// </summary> /// <returns></returns> public int Size() { return this.count; } /// <summary> /// 返回队列中的第一个元素(但不让它出队)。 /// </summary> /// <returns></returns> public Item Peek() { if (IsEmpty()) throw new InvalidOperationException("Queue underflow"); return this.last.next.item; } /// <summary> /// 将一个新元素加入队列中。 /// </summary> /// <param name="item">要入队的元素。</param> public void Enqueue(Item item) { Node<Item> oldLast = this.last; this.last = new Node<Item>(); this.last.item = item; this.last.next = this.last; if (oldLast != null) { this.last.next = oldLast.next; oldLast.next = this.last; } count++; } /// <summary> /// 将队列中的第一个元素出队并返回它。 /// </summary> /// <returns></returns> public Item Dequeue() { if (IsEmpty()) throw new InvalidOperationException("Queue underflow"); Item item = this.last.next.item; this.last.next = this.last.next.next; this.count--; if (IsEmpty()) this.last = null; return item; } public override string ToString() { StringBuilder s = new StringBuilder(); foreach (Item item in this) { s.Append(item); s.Append(" "); } return s.ToString(); } public IEnumerator<Item> GetEnumerator() { return new QueueEnumerator(this.last); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } private class QueueEnumerator : IEnumerator<Item> { private Node<Item> current; private Node<Item> first; public QueueEnumerator(Node<Item> last) { this.current = new Node<Item>(); this.current.next = last.next; this.first = this.current; } Item IEnumerator<Item>.Current => this.current.item; object IEnumerator.Current => this.current.item; void IDisposable.Dispose() { this.first = null; this.current = null; } bool IEnumerator.MoveNext() { if (this.current.next == first.next) return false; this.current = this.current.next; return true; } void IEnumerator.Reset() { this.current = this.first; } } } public class Node<Item> { public Item item; public Node<Item> next; } }Program.cs
using System; namespace _1._3._29 { /* * 1.3.29 * * 用环形链表实现 Queue。 * 环形链表也是一条链表,只是没有任何结点的链接为空,且只要链表非空则 last.next 的值为 first。 * 只能使用一个 Node 类型的实例变量(last)。 * */ class Program { static void Main(string[] args) { string input = "to be or not to - be - - that - - - is"; string[] s = input.Split(' '); Queue<string> queue = new Queue<string>(); foreach (string n in s) { if (!n.Equals("-")) queue.Enqueue(n); else if (!queue.IsEmpty()) Console.WriteLine(queue.Dequeue()); } Console.WriteLine($"({queue.Size()}) left on queue"); Console.WriteLine(queue); } } }书中给出了代码,这里说一下递归的实现。
如果说一个链表除了第一个结点剩下的都已经反转了,那么我们就只要把该结点插入到最后就行了(也就是原先的第二个结点之后)。
像这样:
双向链表的插入有顺序,务必当心。
双向链表长这样(似乎有一种画法是把空指针画成“接地”的样子):
删除中间那个:
再插回去:
原则是不要让有用的结点变得无法访问。
DoubleNode<>
using System; using System.Collections; using System.Collections.Generic; using System.Text; namespace _1._3._31 { /* * 1.3.31 * * 实现一个嵌套类 DoubleNode 用来构造双向链表, * 其中每个结点都含有一个指向前驱元素的应用和一项指向后续元素的引用(如果不存在则为 null)。 * 为以下任务实现若干静态方法: * 在表头插入结点。 * 在表尾插入结点。 * 从表头删除结点。 * 从表尾删除结点。 * 在指定结点之前插入新结点。 * 在指定结点之后插入新结点。 * 删除指定结点。 * */ public class DoubleLinkList<Item> : IEnumerable<Item> { private class DoubleNode<T> { public T item; public DoubleNode<T> prev; public DoubleNode<T> next; } DoubleNode<Item> first; DoubleNode<Item> last; int count; /// <summary> /// 建立一条双向链表。 /// </summary> public DoubleLinkList() { this.first = null; this.last = null; this.count = 0; } /// <summary> /// 检查链表是否为空。 /// </summary> /// <returns></returns> public bool IsEmpty() { return count == 0; } /// <summary> /// 返回链表中元素的数量。 /// </summary> /// <returns></returns> public int Size() { return this.count; } /// <summary> /// 在表头插入一个元素。 /// </summary> /// <param name="item">要插入的元素。</param> public void InsertFront(Item item) { DoubleNode<Item> node = new DoubleNode<Item>() { item = item, next = this.first, prev = null }; if (this.first != null) { this.first.prev = node; } else { this.last = node; } this.first = node; this.count++; } /// <summary> /// 在表尾插入一个元素。 /// </summary> /// <param name="item">要插入表尾的元素。</param> public void InsertRear(Item item) { DoubleNode<Item> node = new DoubleNode<Item>() { item = item, next = null, prev = last }; if (this.last != null) { this.last.next = node; } else { this.first = node; } this.last = node; this.count++; } /// <summary> /// 检索指定下标的元素。 /// </summary> /// <param name="index">要检索的下标。</param> /// <returns></returns> public Item At(int index) { if (index >= count || index < 0) throw new IndexOutOfRangeException(); DoubleNode<Item> current = this.first; for (int i = 0; i < index; ++i) { current = current.next; } return current.item; } /// <summary> /// 返回指定下标的结点。 /// </summary> /// <param name="index">要查找的下标。</param> /// <returns></returns> private DoubleNode<Item> Find(int index) { if (index >= count || index < 0) throw new IndexOutOfRangeException(); DoubleNode<Item> current = this.first; for (int i = 0; i < index; ++i) { current = current.next; } return current; } /// <summary> /// 在指定位置之前插入一个元素。 /// </summary> /// <param name="item">要插入的元素。</param> /// <param name="index">插入位置的下标。</param> public void InsertBefore(Item item, int index) { if (index == 0) { InsertFront(item); return; } if (index >= count || index < 0) throw new IndexOutOfRangeException(); DoubleNode<Item> current = Find(index); DoubleNode<Item> node = new DoubleNode<Item>() { next = current, prev = current.prev, item = item }; current.prev.next = node; current.prev = node; this.count++; } /// <summary> /// 在指定位置之后插入一个元素。 /// </summary> /// <param name="item">要插入的元素。</param> /// <param name="index">查找元素的下标。</param> public void InsertAfter(Item item, int index) { if (index == count - 1) { InsertRear(item); return; } if (index >= count || index < 0) throw new IndexOutOfRangeException(); DoubleNode<Item> current = Find(index); DoubleNode<Item> node = new DoubleNode<Item>() { prev = current, next = current.next, item = item }; current.next.prev = node; current.next = node; this.count++; } /// <summary> /// 删除表头元素。 /// </summary> /// <returns></returns> public Item DeleteFront() { if (IsEmpty()) throw new InvalidOperationException("List underflow"); Item temp = this.first.item; this.first = this.first.next; this.count--; if (IsEmpty()) { this.last = null; } return temp; } /// <summary> /// 删除表尾的元素。 /// </summary> /// <returns></returns> public Item DeleteRear() { if (IsEmpty()) throw new InvalidOperationException("List underflow"); Item temp = this.last.item; this.last = this.last.prev; this.count--; if (IsEmpty()) { this.first = null; } else { this.last.next = null; } return temp; } /// <summary> /// 删除指定位置的元素。 /// </summary> /// <param name="index">要删除元素的下标。</param> /// <returns></returns> public Item Delete(int index) { if (index < 0 || index >= this.count) throw new IndexOutOfRangeException(); if (index == 0) { return DeleteFront(); } if (index == count - 1) { return DeleteRear(); } DoubleNode<Item> current = Find(index); Item temp = current.item; current.prev.next = current.next; current.next.prev = current.prev; count--; return temp; } public override string ToString() { StringBuilder s = new StringBuilder(); foreach (Item i in this) { s.Append(i.ToString()); s.Append(" "); } return s.ToString(); } public IEnumerator<Item> GetEnumerator() { return new DoubleLinkListEnumerator(this.first); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } private class DoubleLinkListEnumerator : IEnumerator<Item> { DoubleNode<Item> current; DoubleNode<Item> first; public DoubleLinkListEnumerator(DoubleNode<Item> first) { this.current = new DoubleNode<Item>(); this.current.next = first; this.first = current; } Item IEnumerator<Item>.Current => current.item; object IEnumerator.Current => current.item; void IDisposable.Dispose() { this.current = null; this.first = null; } bool IEnumerator.MoveNext() { if (this.current.next == null) return false; this.current = this.current.next; return true; } void IEnumerator.Reset() { this.current = this.first; } } } }Program.cs
using System; namespace _1._3._31 { class Program { static void Main(string[] args) { DoubleLinkList<string> linklist = new DoubleLinkList<string>(); linklist.InsertRear("fourth"); linklist.InsertFront("first"); linklist.InsertAfter("second", 0); linklist.InsertBefore("third", 2); Console.WriteLine(linklist); linklist.DeleteFront(); Console.WriteLine(linklist); linklist.DeleteRear(); Console.WriteLine(linklist); linklist.Delete(1); Console.WriteLine(linklist); Console.WriteLine(linklist.At(0)); } } }在队列的基础上增加一个在队首插入元素的方法即可。
Steque.cs
using System; using System.Collections; using System.Collections.Generic; using System.Text; namespace _1._3._32 { //API: //public class Steque<Item> : Ienumerable<Item> // public Steque(); 默认构造函数。 // public bool IsEmpty(); 检查 Steque 是否为空。 // public int Size(); 返回 Steque 中的元素数量。 // public void Push(Item item); 向 Steque 中压入一个元素。 // public Item Pop(); 从 Steque 中弹出一个元素。 // public void Peek(); 返回栈顶元素(但不弹出它)。 // public void Enqueue(Item item); 将一个元素添加入 Steque 中。 public class Steque<Item> : IEnumerable<Item> { private Node<Item> first; private Node<Item> last; private int count; private class Node<T> { public T item; public Node<T> next; } /// <summary> /// 默认构造函数。 /// </summary> public Steque() { this.first = null; this.count = 0; } /// <summary> /// 检查栈是否为空。 /// </summary> /// <returns></returns> public bool IsEmpty() { return count == 0; } /// <summary> /// 返回栈内元素的数量。 /// </summary> /// <returns></returns> public int Size() { return this.count; } /// <summary> /// 将一个元素压入栈中。 /// </summary> /// <param name="item">要压入栈中的元素。</param> public void Push(Item item) { Node<Item> oldFirst = first; this.first = new Node<Item>(); this.first.item = item; this.first.next = oldFirst; if (oldFirst == null) { this.last = this.first; } count++; } /// <summary> /// 将一个元素从栈中弹出,返回弹出的元素。 /// </summary> /// <returns></returns> public Item Pop() { if (IsEmpty()) throw new InvalidOperationException("Stack Underflow"); Item item = first.item; first = first.next; count--; if (count == 0) { this.last = null; } return item; } /// <summary> /// 将一个元素加入队列中。 /// </summary> /// <param name="item">要入队的元素。</param> public void Enqueue(Item item) { Node<Item> oldLast = this.last; this.last = new Node<Item>(); this.last.item = item; this.last.next = null; if (IsEmpty()) this.first = this.last; else oldLast.next = this.last; count++; } /// <summary> /// 返回栈顶元素(但不弹出它)。 /// </summary> /// <returns></returns> public Item Peek() { if (IsEmpty()) throw new InvalidOperationException("Stack Underflow"); return first.item; } public override string ToString() { StringBuilder s = new StringBuilder(); foreach (Item n in this) { s.Append(n); s.Append(' '); } return s.ToString(); } public IEnumerator<Item> GetEnumerator() { return new StackEnumerator(this.first); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } private class StackEnumerator : IEnumerator<Item> { private Node<Item> current; private Node<Item> first; public StackEnumerator(Node<Item> first) { this.current = new Node<Item>(); this.current.next = first; this.first = this.current; } Item IEnumerator<Item>.Current => current.item; object IEnumerator.Current => current.item; void IDisposable.Dispose() { this.current = null; this.first = null; } bool IEnumerator.MoveNext() { if (this.current.next == null) return false; this.current = this.current.next; return true; } void IEnumerator.Reset() { this.current = this.first; } } } }Program.cs
using System; namespace _1._3._32 { /* * 1.3.32 * * Steque * 一个以栈为目标的队列(或称 steque), * 是一种支持 push、pop 和 enqueue 操作的数据类型。 * 为这种抽象数据类定义一份 API 并给出一份基于链表的实现。 * */ class Program { //见 Steque.cs static void Main(string[] args) { Steque<string> steque = new Steque<string>(); steque.Push("first"); steque.Push("second"); steque.Push("third"); steque.Enqueue("fourth"); Console.WriteLine(steque.ToString()); steque.Pop(); steque.Pop(); steque.Pop(); steque.Pop(); Console.WriteLine(steque.ToString()); steque.Enqueue("first"); steque.Push("zero"); Console.WriteLine(steque.ToString()); } } }动态数组这里要注意 first 不要小于零。
Deque 类
using System; using System.Collections; using System.Collections.Generic; namespace _1._3._33 { public class Deque<Item> : IEnumerable<Item> { private class DoubleNode<T> { public T item; public DoubleNode<T> next; public DoubleNode<T> prev; } DoubleNode<Item> first; DoubleNode<Item> last; int count; /// <summary> /// 默认构造函数,建立一个双端队列。 /// </summary> public Deque() { this.first = null; this.last = null; this.count = 0; } /// <summary> /// 检查队列是否为空。 /// </summary> /// <returns></returns> public bool IsEmpty() { return this.count == 0; } /// <summary> /// 返回队列中元素的数量。 /// </summary> /// <returns></returns> public int Size() { return this.count; } /// <summary> /// 向左端添加一个元素。 /// </summary> /// <param name="item">要添加的元素。</param> public void PushLeft(Item item) { DoubleNode<Item> oldFirst = this.first; this.first = new DoubleNode<Item>() { item = item, prev = null, next = oldFirst }; if (oldFirst == null) { this.last = this.first; } else { oldFirst.prev = this.first; } this.count++; } /// <summary> /// 向右端添加一个元素。 /// </summary> /// <param name="item">要添加的元素。</param> public void PushRight(Item item) { DoubleNode<Item> oldLast = this.last; this.last = new DoubleNode<Item>() { item = item, prev = oldLast, next = null }; if (oldLast == null) { this.first = this.last; } else { oldLast.next = this.last; } this.count++; } /// <summary> /// 从右端删除并返回一个元素。 /// </summary> /// <returns></returns> public Item PopRight() { if (IsEmpty()) { throw new InvalidOperationException(); } Item temp = this.last.item; this.last = this.last.prev; this.count--; if (this.last == null) { this.first = null; } else { this.last.next.item = default(Item); this.last.next = null; } return temp; } /// <summary> /// 从左端删除并返回一个元素。 /// </summary> /// <returns></returns> public Item PopLeft() { if (IsEmpty()) { throw new InvalidOperationException(); } Item temp = this.first.item; this.first = this.first.next; this.count--; if (this.first == null) { this.last = null; } else { this.first.prev.item = default(Item); this.first.prev = null; } return temp; } public IEnumerator<Item> GetEnumerator() { return new DequeEnumerator(this.first); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } private class DequeEnumerator : IEnumerator<Item> { private DoubleNode<Item> current; private DoubleNode<Item> first; public DequeEnumerator(DoubleNode<Item> first) { this.current = new DoubleNode<Item>(); this.current.next = first; this.current.prev = null; this.first = this.current; } public Item Current => current.item; object IEnumerator.Current => current.item; public void Dispose() { this.current = null; this.first = null; } public bool MoveNext() { if (this.current.next == null) return false; this.current = this.current.next; return true; } public void Reset() { this.current = this.first; } } } }在初始化迭代器的时候随机生成一个访问序列,之后按照这个访问序列进行迭代即可。
RandomBag.cs
using System; using System.Collections; using System.Collections.Generic; namespace _1._3._34 { public class RandomBag<Item> : IEnumerable<Item> { private Item[] bag; private int count; /// <summary> /// 建立一个随机背包。 /// </summary> public RandomBag() { this.bag = new Item[2]; this.count = 0; } /// <summary> /// 检查背包是否为空。 /// </summary> /// <returns></returns> public bool IsEmpty() { return this.count == 0; } /// <summary> /// 返回背包中元素的数量。 /// </summary> /// <returns></returns> public int Size() { return this.count; } /// <summary> /// 向背包中添加一个元素。 /// </summary> /// <param name="item">要向背包中添加的元素。</param> public void Add(Item item) { if (this.count == this.bag.Length) { Resize(this.count * 2); } this.bag[count] = item; count++; } /// <summary> /// 重新为背包分配内存空间。 /// </summary> /// <param name="capacity"></param> private void Resize(int capacity) { if (capacity <= 0) throw new ArgumentException(); Item[] temp = new Item[capacity]; for (int i = 0; i < this.count; ++i) { temp[i] = this.bag[i]; } this.bag = temp; } public IEnumerator<Item> GetEnumerator() { return new RandomBagEnumerator(this.bag, this.count); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } private class RandomBagEnumerator : IEnumerator<Item> { private Item[] bag; private int[] sequence; private int current; private int count; public RandomBagEnumerator(Item[] bag, int count) { this.bag = bag; this.current = -1; this.count = count; this.sequence = new int[count]; for (int i = 0; i < this.count; ++i) { this.sequence[i] = i; } Shuffle(sequence, DateTime.Now.Millisecond); } /// <summary> /// 随机打乱数组。 /// </summary> /// <param name="a">需要打乱的数组。</param> /// <param name="seed">随机种子值。</param> private void Shuffle(int[] a, int seed) { int N = a.Length; Random random = new Random(seed); for (int i = 0; i < N; ++i) { int r = i + random.Next(N - i); int temp = a[i]; a[i] = a[r]; a[r] = temp; } } Item IEnumerator<Item>.Current => this.bag[this.sequence[this.current]]; object IEnumerator.Current => this.bag[this.sequence[this.current]]; void IDisposable.Dispose() { this.bag = null; this.sequence = null; this.current = -1; } bool IEnumerator.MoveNext() { if (this.current == this.count - 1) return false; this.current++; return true; } void IEnumerator.Reset() { this.current = -1; } } } }事实上只需要在普通队列的基础上稍作修改就可以了。
出队时先随机选择一个元素,之后让它和最开始的元素做交换,之后正常出队即可。
RandomQueue.cs
using System; namespace _1._3._35 { public class RandomQueue<Item> { private Item[] queue; private int count; /// <summary> /// 新建一个随机队列。 /// </summary> public RandomQueue() { this.queue = new Item[2]; this.count = 0; } /// <summary> /// 判断队列是否为空。 /// </summary> /// <returns></returns> public bool IsEmpty() { return this.count == 0; } /// <summary> /// 为队列重新分配内存空间。 /// </summary> /// <param name="capacity"></param> private void Resize(int capacity) { if (capacity <= 0) { throw new ArgumentException(); } Item[] temp = new Item[capacity]; for (int i = 0; i < this.count; ++i) { temp[i] = this.queue[i]; } this.queue = temp; } /// <summary> /// 向队列中添加一个元素。 /// </summary> /// <param name="item">要向队列中添加的元素。</param> public void Enqueue(Item item) { if (this.queue.Length == this.count) { Resize(this.count * 2); } this.queue[this.count] = item; this.count++; } /// <summary> /// 从队列中随机删除并返回一个元素。 /// </summary> /// <returns></returns> public Item Dequeue() { if (IsEmpty()) { throw new InvalidOperationException(); } Random random = new Random(DateTime.Now.Millisecond); int index = random.Next(this.count); Item temp = this.queue[index]; this.queue[index] = this.queue[this.count - 1]; this.queue[this.count - 1] = temp; this.count--; if (this.count < this.queue.Length / 4) { Resize(this.queue.Length / 2); } return temp; } /// <summary> /// 随机返回一个队列中的元素。 /// </summary> /// <returns></returns> public Item Sample() { if (IsEmpty()) { throw new InvalidOperationException(); } Random random = new Random(); int index = random.Next(this.count); return this.queue[index]; } } }实现方法和 1.3.34 类似,初始化迭代器的时候同时初始化一个随机访问序列。
RandomQueue.cs
using System; using System.Collections; using System.Collections.Generic; namespace _1._3._36 { public class RandomQueue<Item> : IEnumerable<Item> { private Item[] queue; private int count; /// <summary> /// 新建一个随机队列。 /// </summary> public RandomQueue() { this.queue = new Item[2]; this.count = 0; } /// <summary> /// 判断队列是否为空。 /// </summary> /// <returns></returns> public bool IsEmpty() { return this.count == 0; } /// <summary> /// 为队列重新分配内存空间。 /// </summary> /// <param name="capacity"></param> private void Resize(int capacity) { if (capacity <= 0) { throw new ArgumentException(); } Item[] temp = new Item[capacity]; for (int i = 0; i < this.count; ++i) { temp[i] = this.queue[i]; } this.queue = temp; } /// <summary> /// 向队列中添加一个元素。 /// </summary> /// <param name="item">要向队列中添加的元素。</param> public void Enqueue(Item item) { if (this.queue.Length == this.count) { Resize(this.count * 2); } this.queue[this.count] = item; this.count++; } /// <summary> /// 从队列中随机删除并返回一个元素。 /// </summary> /// <returns></returns> public Item Dequeue() { if (IsEmpty()) { throw new InvalidOperationException(); } Random random = new Random(DateTime.Now.Millisecond); int index = random.Next(this.count); Item temp = this.queue[index]; this.queue[index] = this.queue[this.count - 1]; this.queue[this.count - 1] = temp; this.count--; if (this.count < this.queue.Length / 4) { Resize(this.queue.Length / 2); } return temp; } /// <summary> /// 随机返回一个队列中的元素。 /// </summary> /// <returns></returns> public Item Sample() { if (IsEmpty()) { throw new InvalidOperationException(); } Random random = new Random(); int index = random.Next(this.count); return this.queue[index]; } public IEnumerator<Item> GetEnumerator() { return new RandomQueueEnumerator(this.queue, this.count); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } private class RandomQueueEnumerator : IEnumerator<Item> { private int current; private int count; private Item[] queue; private int[] sequence; public RandomQueueEnumerator(Item[] queue, int count) { this.count = count; this.queue = queue; this.current = -1; this.sequence = new int[this.count]; for (int i = 0; i < this.count; ++i) { this.sequence[i] = i; } Shuffle(this.sequence, DateTime.Now.Millisecond); } /// <summary> /// 随机打乱数组。 /// </summary> /// <param name="a">需要打乱的数组。</param> /// <param name="seed">随机种子值。</param> private void Shuffle(int[] a, int seed) { int N = a.Length; Random random = new Random(seed); for (int i = 0; i < N; ++i) { int r = i + random.Next(N - i); int temp = a[i]; a[i] = a[r]; a[r] = temp; } } Item IEnumerator<Item>.Current => this.queue[this.sequence[this.current]]; object IEnumerator.Current => this.queue[this.sequence[this.current]]; void IDisposable.Dispose() { this.current = -1; this.sequence = null; this.queue = null; } bool IEnumerator.MoveNext() { if (this.current == this.count - 1) return false; this.current++; return true; } void IEnumerator.Reset() { this.current = -1; } } } }也就是约瑟夫问题,官方给出的 JAVA 版答案:Josephus.java。
报数时将一个人出队然后入队来模拟一个环。
报到 M 个后将那个人出队但不入队(删除)
随后继续循环。
这里采用“假删除”的方式,对要删除的元素不直接删除而是打上标记,这样就可以维持插入的顺序。
数组实现:
using System; namespace _1._3._38 { class ArrayBasedGeneralizeQueue<Item> { private Item[] queue; private bool[] IsVisited; private int count; private int first; private int last; /// <summary> /// 建立一个队列。 /// </summary> public ArrayBasedGeneralizeQueue() { this.queue = new Item[2]; this.IsVisited = new bool[2]; this.first = 0; this.last = 0; this.count = 0; } /// <summary> /// 检查队列是否为空。 /// </summary> /// <returns></returns> public bool IsEmpty() { return this.count == 0; } /// <summary> /// 为队列重新分配空间。 /// </summary> /// <param name="capacity"></param> private void Resize(int capacity) { Item[] temp = new Item[capacity]; for (int i = 0; i < this.count; ++i) { temp[i] = this.queue[i]; } this.queue = temp; bool[] t = new bool[capacity]; for (int i = 0; i < this.count; ++i) { t[i] = this.IsVisited[i]; } this.IsVisited = t; } /// <summary> /// 向队列中插入一个元素。 /// </summary> /// <param name="item">要插入队列的元素。</param> public void Insert(Item item) { if (this.count == this.queue.Length) { Resize(this.queue.Length * 2); } this.queue[this.last] = item; this.IsVisited[this.last] = false; this.last++; this.count++; } /// <summary> /// 从队列中删除并返回第 k 个插入的元素。 /// </summary> /// <param name="k">要删除元素的顺序(从 1 开始)</param> /// <returns></returns> public Item Delete(int k) { if (IsEmpty()) { throw new InvalidOperationException(); } if (k > this.last || k < 0) { throw new ArgumentOutOfRangeException(); } if (IsVisited[k - 1] == true) { throw new ArgumentException("this node had been already deleted"); } Item temp = this.queue[k - 1]; this.IsVisited[k - 1] = true; this.count--; return temp; } } }链表实现:
using System; namespace _1._3._38 { class LinkedListBasedGeneralizeQueue<Item> { private class Node<T> { public T item; public Node<T> next; public bool IsVisited; } private Node<Item> first; private Node<Item> last; private int count; /// <summary> /// 建立一个队列。 /// </summary> public LinkedListBasedGeneralizeQueue() { this.first = null; this.last = null; this.count = 0; } /// <summary> /// 检查数组是否为空。 /// </summary> /// <returns></returns> public bool IsEmpty() { return this.first == null; } /// <summary> /// 在队尾插入元素。 /// </summary> /// <param name="item">需要插入的元素。</param> public void Insert(Item item) { Node<Item> oldLast = this.last; this.last = new Node<Item>() { item = item, IsVisited = false, next = null }; if (oldLast == null) { this.first = this.last; } else { oldLast.next = this.last; } count++; } /// <summary> /// 删除第 k 个插入的结点 /// </summary> /// <param name="k">结点序号(从 1 开始)</param> /// <returns></returns> public Item Delete(int k) { if (k > this.count || k <= 0) { throw new ArgumentOutOfRangeException(); } k--; //找到目标结点 Node<Item> current = this.first; for (int i = 0; i < k; ++i) { current = current.next; } if (current.IsVisited == true) { throw new ArgumentException("this node had been already deleted"); } current.IsVisited = true; return current.item; } } }可以直接套用队列的实现方式,在满或空时抛出相应异常。
每次插入时都先搜索一遍链表,再判定相应动作。
可以按照书上的提示出队再入队,也可以直接用迭代器访问一遍进行复制。
直接把链栈的整个链表复制一份即可。
C# 中可以用 Directory 类里面的几个方法来获得文件路径和文件名。
这里我们使用两个栈来模拟缓冲区。
向左/向右移动 = 从左/右栈弹出相应数量的元素并压入另外一个栈。
插入/删除 = 左栈压入/弹出一个元素。
字符数量 = 左栈数量 + 右栈数量。
书上已经给出了思路,简单说明一下。
第一问是给定输入判断是否会下溢出,只要记录栈中元素的数量即可,一旦为负数则返回 true。
第二问是给定输出判断是否可能。
对于输出序列中的每一个数,如果栈顶为空或者栈顶数字小于当前输出序列的数,那么就从输入序列中输入数字,直到栈顶数字和当前输出序列中的数字相等。
如果当前输出序列中的数字和栈顶元素相等,从栈中弹出相应元素。
最后如果栈为空则可能,否则不可能。
可以结合习题 1.3.3 的解答查看。
通用解法见下一题。
这道题的解答参考了这篇博文:http://ceeji.net/blog/forbidden-triple-for-stack-generability/。
显然书中的解答已经十分明确,这里简单说明一下: 首先有结论:对于栈顶元素 Sn,栈中所有小于 Sn 的值都以递减形式保存(已经输出的不算)。 表现在输出序列中,Sn 输出之后,如果有小于 Sn 的值输出,其顺序必定是递减的。 例如序列 4 3 2 1 0 9 8 7 6 5 4 输出之后,3 2 1 0 递减输出;9 输出之后,8 7 6 5 递减输出。 依次验证其中的每个值都能满足结论。 而对于序列 4 6 8 7 5 3 2 9 0 1 对于 4,之后的 3 2 1 0 并不是以递减顺序输出的,因此这个序列是不合法的。
这里用的都是链式结构,头尾相接即可。
Queue:
/// <summary> /// 在当前队列之后附加一个队列。 /// </summary> /// <param name="q1">需要被附加的队列。</param> /// <param name="q2">需要附加的队列(将被删除)。</param> public static Queue<Item> Catenation(Queue<Item> q1, Queue<Item> q2) { if (q1.IsEmpty()) { q1.first = q2.first; q1.last = q2.last; q1.count = q2.count; } else { q1.last.next = q2.first; q1.last = q2.last; q1.count += q2.count; } q2 = null; return q1; }
Stack:
/// <summary> /// 将两个栈连接。 /// </summary> /// <param name="s1">第一个栈。</param> /// <param name="s2">第二个栈(将被删除)。</param> /// <returns></returns> public static Stack<Item> Catenation(Stack<Item> s1, Stack<Item> s2) { if (s1.IsEmpty()) { s1.first = s2.first; s1.count = s2.count; } else { Node<Item> last = s1.first; while (last.next != null) { last = last.next; } last.next = s2.first; s1.count += s2.count; } s2 = null; return s1; }
Steque:
/// <summary> /// 将两个 Steque 连接。 /// </summary> /// <param name="s1">第一个 Steque </param> /// <param name="s2">第二个 Steque (将被删除)</param> /// <returns></returns> public static Steque<Item> Catenation(Steque<Item> s1, Steque<Item> s2) { if (s1.IsEmpty()) { s1.first = s2.first; s1.last = s2.last; s1.count = s2.count; } else { s1.last.next = s2.first; s1.count += s2.count; } s2 = null; return s1; }按照双向队列原本的操作就可以实现,需要维护两个栈的长度以防越界。(左侧栈弹出了右侧栈栈底的内容)
用六个栈即可实现,具体请查看我的这篇博文(有点复杂):用 6 个栈实现一个 O(1) 队列。
修改后的迭代器代码:
private class StackEnumerator : IEnumerator<Item> { private Stack<Item> s; private int popcount; private int pushcount; private Node<Item> current; public StackEnumerator(Stack<Item> s) { this.s = s; this.current = s.first; this.popcount = s.popcount; this.pushcount = s.pushcount; } Item IEnumerator<Item>.Current => current.item; object IEnumerator.Current => current.item; void IDisposable.Dispose() { this.current = null; this.s = null; } bool IEnumerator.MoveNext() { if (s.popcount != this.popcount || s.pushcount != this.pushcount) throw new InvalidOperationException("Stack has been modified"); if (this.current.next == null) return false; this.current = this.current.next; return true; } void IEnumerator.Reset() { this.current = this.s.first; } }: #0000ff;">return this.rightcount; } /// <summary> /// 向左端添加一个元素。 /// </summary> /// <param name="item">要添加的元素。</param> public void PushLeft(Item item) { DoubleNode<Item> oldFirst = this.first; this.first = new DoubleNode<Item>() { item = item, prev = null, next = oldFirst }; if (oldFirst == null) { this.last = this.first; } else { oldFirst.prev = this.first; } this.leftcount++; } /// <summary> /// 向右端添加一个元素。 /// </summary> /// <param name="item">要添加的元素。</param> public void PushRight(Item item) { DoubleNode<Item> oldLast = this.last; this.last = new DoubleNode<Item>() { item = item, prev = oldLast, next = null }; if (oldLast == null) { this.first = this.last; } else { oldLast.next = this.last; } this.rightcount++; } /// <summary> /// 从右端删除并返回一个元素。 /// </summary> /// <returns></returns> public Item PopRight() { if (IsRightEmpty()) { throw new InvalidOperationException(); } Item temp = this.last.item; this.last = this.last.prev; this.rightcount--; if (this.last == null) { this.first = null; } else { this.last.next.item = default(Item); this.last.next = null; } return temp; } /// <summary> /// 从左端删除并返回一个元素。 /// </summary> /// <returns></returns> public Item PopLeft() { if (IsLeftEmpty()) { throw new InvalidOperationException(); } Item temp = this.first.item; this.first = this.first.next; this.leftcount--; if (this.first == null) { this.last = null; } else { this.first.prev.item = default(Item); this.first.prev = null; } return temp; } public IEnumerator<Item> GetEnumerator() { return new DequeEnumerator(this.first); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } private class DequeEnumerator : IEnumerator<Item> { private DoubleNode<Item> current; private DoubleNode<Item> first; public DequeEnumerator(DoubleNode<Item> first) { this.current = new DoubleNode<Item>(); this.current.next = first; this.current.prev = null; this.first = this.current; } public Item Current => current.item; object IEnumerator.Current => current.item; public void Dispose() { this.current = null; this.first = null; } public bool MoveNext() { if (this.current.next == null) return false; this.current = this.current.next; return true; } public void Reset() { this.current = this.first; } } } }
栈与队列。用有限个栈实现一个队列,保证每个队列操作(在最坏情况下)都只需要常数次的栈操作。
用六个栈即可实现,具体请查看我的这篇博文(有点复杂):用 6 个栈实现一个 O(1) 队列。
快速出错的迭代器。修改 Stack 的迭代器代码,确保一旦用例在迭代器中(通过 push() 或 pop() 操作)修改集合数据就抛出一个 java.util.ConcurrentModificationException 异常。
修改后的迭代器代码:
private class StackEnumerator : IEnumerator<Item> { private Stack<Item> s; private int popcount; private int pushcount; private Node<Item> current; public StackEnumerator(Stack<Item> s) { this.s = s; this.current = s.first; this.popcount = s.popcount; this.pushcount = s.pushcount; } Item IEnumerator<Item>.Current => current.item; object IEnumerator.Current => current.item; void IDisposable.Dispose() { this.current = null; this.s = null; } bool IEnumerator.MoveNext() { if (s.popcount != this.popcount || s.pushcount != this.pushcount) throw new InvalidOperationException("Stack has been modified"); if (this.current.next == null) return false; this.current = this.current.next; return true; } void IEnumerator.Reset() { this.current = this.s.first; } }转载于:https://www.cnblogs.com/ikesnowy/p/7157826.html
相关资源:Algorithms-4th-Edition-in-CSharp:算法(第四版)习题题解C#版-源码