写在前面 整个项目都托管在了 Github 上:https://github.com/ikesnowy/Algorithms-4th-Edition-in-Csharp 这一节内容可能会用到的库文件有 Sort和 SortData,同样在 Github 上可以找到。 善用 Ctrl + F 查找题目。 习题&题解 2.1.1 题目 按照算法 2.1 所示轨迹的格式给出选择排序是如何将数组 E A S Y Q U E S T I O N 排序的。 解答
2.1.2 题目 在选择排序中,一个元素最多可能会被交换多少次?平均可能会被交换多少次? 解答 最多会被交换 n 次,只要将一个有序数列循环右移一位就可以构造这样的情况。 例如:
其中,1,2,3 分别代表三种不同的取值以及其先后顺序。 假设这是第 i 次插入前,如果第 i 次插入的是 1,我们需要交换 b+c 次,插入 2 则需要交换 c 次,插入 3 则不需要交换。 根据题意,这是一个随机数组,我们假设其为均匀分布,那么三种取值的出现几率相等。 第 i 次插入所需要的平均交换次数即为:
class Program { // 查看最后结果 // 可以发现相同的 h 在数组大小不同时所产生的比值十分接近。 static void Main(string[] args) { Random random = new Random(); ShellSort sort = new ShellSort();
int size = 100; for (int i = 0; i < 5; i++) { double[] a = new double[size]; for (int j = 0; j < size; j++) { a[j] = random.NextDouble() * 100; } Console.WriteLine("ArraySize:" + size); sort.Sort(a); size *= 10; } } }
foreach (string n in a) { bool isFind = false; for (int i = 0; i < a.Length; i++) { if (ReferenceEquals(n, backup[i])) { isFind = true; break; } } if (!isFind) { return false; } }
foreach (string n in a) { bool isFind = false; for (int i = 0; i < a.Length; i++) { if (ReferenceEquals(n, backup[i])) { isFind = true; break; } } if (!isFind) { return false; } }
return true; }
/// /// 选择排序,其中的交换部分使用新建对象并复制的方法。 /// /// 用于排序的数组。 public static void SelectionSort(string[] s) { for (int i = 0; i < s.Length; i++) { int min = i; for (int j = i + 1; j < s.Length; j++) { if (s[j].CompareTo(s[min]) < 0) { min = j; } } string temp = new string(s[i].ToCharArray()); s[i] = s[min]; s[min] = temp; } } } }
using System; using System.Drawing; using System.Windows.Forms; using System.Threading;
namespace _2._1._17 { public partial class Form2 : Form { double[] randomDoubles; public Form2(int N) { InitializeComponent(); this.randomDoubles = new double[N]; Random random = new Random(); for (int i = 0; i < N; i++) { this.randomDoubles[i] = random.NextDouble() * 0.8 + 0.2; } drawPanel();
barX[0] = new PointF(4, drawRect.Top); for (int i = 1; i < this.randomDoubles.Length; i++) { barX[i] = new PointF(2 + unitX + barX[i - 1].X, drawRect.Top); }
RectangleF[] bars = new RectangleF[this.randomDoubles.Length]; for (int i = 0; i < this.randomDoubles.Length; i++) { SizeF size = new SizeF(unitX, (float)this.randomDoubles[i] * drawRect.Height); bars[i] = new RectangleF(barX[i], size); }
using System; using System.Drawing; using System.Windows.Forms; using System.Threading;
namespace _2._1._17 { public partial class Form3 : Form { double[] randomDoubles; public Form3(int N) { InitializeComponent(); this.randomDoubles = new double[N]; Random random = new Random(); for (int i = 0; i < N; i++) { this.randomDoubles[i] = random.NextDouble() * 0.8 + 0.2; } drawPanel();
barX[0] = new PointF(4, drawRect.Top); for (int i = 1; i < this.randomDoubles.Length; i++) { barX[i] = new PointF(2 + unitX + barX[i - 1].X, drawRect.Top); }
RectangleF[] bars = new RectangleF[this.randomDoubles.Length]; for (int i = 0; i < this.randomDoubles.Length; i++) { SizeF size = new SizeF(unitX, (float)this.randomDoubles[i] * drawRect.Height); bars[i] = new RectangleF(barX[i], size); }
using System; using System.Drawing; using System.Windows.Forms; using System.Threading;
namespace _2._1._18 { public partial class Form2 : Form { double[] randomDoubles; int sortI; int sortJ; int sortMin; public Form2(int N) { InitializeComponent(); this.randomDoubles = new double[N]; Random random = new Random(); for (int i = 0; i < N; i++) { this.randomDoubles[i] = random.NextDouble() * 0.8 + 0.2; } }
barX[0] = new PointF(4, drawRect.Top); for (int i = 1; i < this.randomDoubles.Length; i++) { barX[i] = new PointF(2 + unitX + barX[i - 1].X, drawRect.Top); }
RectangleF[] bars = new RectangleF[this.randomDoubles.Length]; for (int i = 0; i < this.randomDoubles.Length; i++) { SizeF size = new SizeF(unitX, (float)this.randomDoubles[i] * drawRect.Height); bars[i] = new RectangleF(barX[i], size); }
for (int i = 0; i < bars.Length; i++) { if (i == this.sortMin) { graphics.FillRectangle(Brushes.Red, bars[i]); } else if (i < this.sortI) { graphics.FillRectangle(Brushes.Gray, bars[i]); } else { graphics.FillRectangle(Brushes.Black, bars[i]); } } graphics.Dispose(); }
using System; using System.Drawing; using System.Windows.Forms; using System.Threading;
namespace _2._1._18 { public partial class Form3 : Form { double[] randomDoubles; int sortI; int sortJ; int n = 0; public Form3(int N) { InitializeComponent(); this.randomDoubles = new double[N]; Random random = new Random(); for (int i = 0; i < N; i++) { this.randomDoubles[i] = random.NextDouble() * 0.8 + 0.2; } }
barX[0] = new PointF(4, drawRect.Top); for (int i = 1; i < this.randomDoubles.Length; i++) { barX[i] = new PointF(2 + unitX + barX[i - 1].X, drawRect.Top); }
RectangleF[] bars = new RectangleF[this.randomDoubles.Length]; for (int i = 0; i < this.randomDoubles.Length; i++) { SizeF size = new SizeF(unitX, (float)this.randomDoubles[i] * drawRect.Height); bars[i] = new RectangleF(barX[i], size); }
for (int i = 0; i < bars.Length; i++) { if (i == this.sortJ) { graphics.FillRectangle(Brushes.Red, bars[i]); } else if (i <= this.sorti="" i=""> this.sortJ) { graphics.FillRectangle(Brushes.Black, bars[i]); } else { graphics.FillRectangle(Brushes.Gray, bars[i]); } } graphics.Dispose(); }
namespace _2._1._19 { class ShellSortWorstCase { /// /// 获得最坏情况的数组。 /// /// 数组大小。 /// 希尔排序最坏情况的数组。 public static int[] GetWorst(int n) { int l = 0; int?[] a = new int?[n + 1];
for (int i = 0; i < a.Length; i++) { a[i] = null; } int P = 40; int PAddition = P; for (int i = 0; l < 100; i++) { for (int j = 1; j <= n; j++) { if (a[j] == null && IsVisible(j, P)) { l++; a[j] = l; } } P += PAddition; }
int[] b = new int[n]; for (int i = 0; i < n; i++) { b[i] = (int)a[i + 1]; }
return b; }
/// /// 确认 j - i 是不是在排序样板(Sorting Template)上。 /// /// /// /// public static bool IsVisible(int i, int j) { int k = 0; while (k <= 100) { if (j - i >= k * 40 && j - i <= k * 41) return true; k++; } return false; } } }
会显示比较次数的 ShellSort 类
using System; using System.Diagnostics; using Sort;
namespace _2._1._19 { public class ShellSort : BaseSort { /// /// 默认构造函数。 /// public ShellSort() { }
/// /// 利用希尔排序将数组按升序排序。 /// /// 需要排序的数组。 public override void Sort(T[] a) { int n = a.Length; int compareTime = 0;
int h = 1; while (h < n / 3) { h = 3 * h + 1; }
while (h >= 1) { for (int i = h; i < n; i++) { for (int j = i; j >= h && Less(a[j], a[j - h]); j -= h) { Exch(a, j, j - h); compareTime++; } compareTime++; } Debug.Assert(IsHSorted(a, h)); h /= 3; } Console.WriteLine("CompareTime:" + compareTime); Debug.Assert(IsSorted(a)); }
/// /// 检查一次希尔排序后的子数组是否有序。 /// /// 排序后的数组。 /// 子数组间隔。 /// 是否有序。 private bool IsHSorted(T[] a, int h) where T : IComparable { for (int i = h; i < a.Length; i++) { if (Less(a[i], a[i - h])) { return false; } } return true; } } }
2.1.20 题目 希尔排序的最好情况。最好情况是什么?证明你的结论。 解答 由于每次 h 排序都是插入排序,希尔排序最好情况就是插入排序的最好情况,也就是已排序的数组。
2.1.21 题目 可比较的交易。用我们的 Date 类(请见 2.1.1.4 节)作为模板扩展你的 Transaction 类(请见练习 1.2.13),实现 Comparable 接口,使交易能够按照金额排序。 解答 事实上官方给出来的 Date 类以及 Transaction 类都已经实现了这些接口。 Date 类:Date.java Transaction 类:Transaction.java 代码
using System; using Sort;
namespace _2._1._21 { /* * 2.1.21 * * 可比较的交易。 * 用我们的 Date 类(请见 2.1.1.4 节) * 作为模板扩展你的 Transaction 类(请见练习 1.2.13), * 实现 Comparable 接口,使交易能够按照金额排序。 * */ class Program { static void Main(string[] args) { Transaction[] a = new Transaction[4]; a[0] = new Transaction("Turing 6/17/1990 644.08"); a[1] = new Transaction("Tarjan 3/26/2002 4121.85"); a[2] = new Transaction("Knuth 6/14/1999 288.34"); a[3] = new Transaction("Dijkstra 8/22/2007 2678.40");
Console.WriteLine("Unsorted"); for (int i = 0; i < a.Length; i++) { Console.WriteLine(a[i]); } Console.WriteLine();
Console.WriteLine("Sort by amount"); InsertionSort insertionSort = new InsertionSort(); insertionSort.Sort(a, new Transaction.HowMuchOrder()); for (int i = 0; i < a.Length; i++) Console.WriteLine(a[i]); Console.WriteLine(); } } }
for (int i = 0; i < a.Length; i++) { string input = Console.ReadLine(); a[i] = new Transaction(input); }
InsertionSort insertionSort = new InsertionSort();
Console.WriteLine("Unsorted"); for (int i = 0; i < a.Length; i++) { Console.WriteLine(a[i]); } Console.WriteLine();
Console.WriteLine("Sort by date"); insertionSort.Sort(a, new Transaction.WhenOrder()); for (int i = 0; i < a.Length; i++) Console.WriteLine(a[i]); Console.WriteLine();
Console.WriteLine("Sort by customer"); insertionSort.Sort(a, new Transaction.WhoOrder()); for (int i = 0; i < a.Length; i++) Console.WriteLine(a[i]); Console.WriteLine();
Console.WriteLine("Sort by amount"); insertionSort.Sort(a, new Transaction.HowMuchOrder()); for (int i = 0; i < a.Length; i++) Console.WriteLine(a[i]); Console.WriteLine(); } } }