static void Main(string[] args) { Console.WriteLine("N\t准确值\t估计值\t比值"); QuickSortAnalyze sort = new QuickSortAnalyze(); int N = 100; int trialTime = 500; for (int i = 0; i < 3; i++) { int sumOfCompare = 0; int[] a = new int[N]; for (int j = 0; j < trialTime; j++) { for (int k = 0; k < N; k++) { a[k] = k; } SortCompare.Shuffle(a); sort.Sort(a); sumOfCompare += sort.CompareCount; } int averageCompare = sumOfCompare / trialTime; double estimatedCompare = 2 * N * Math.Log(N); Console.WriteLine(N + "\t" + averageCompare + "\t" + (int)estimatedCompare + "\t" + averageCompare / estimatedCompare); N *= 10; } }
static void Main(string[] args) { // 每次只让枢轴变为已排序,这就是最坏情况。 // 这种时候枢轴是当前子数组的最大值/最小值。 // 由于在我们的实现中总是取子数组的第一个元素作为枢轴。 // 因此一个已排序的数组可以达到最坏情况,比较次数达到 O(n^2)。 // 如果换作取最后一个元素,最坏情况会变成逆序数组。 // 我们的实现中如果碰到与枢轴相等的元素也会停止循环, // 因此如果数组中有重复的元素会减少比较次数。 // // 答案: // 1 2 3 4 5 6 7 8 9 10 // 2 3 4 5 6 7 8 9 10 11 // 3 4 5 6 7 8 9 10 11 12 // 4 5 6 7 8 9 10 11 12 13 // 5 6 7 8 9 10 11 12 13 14 // 6 7 8 9 10 11 12 13 14 15 QuickSortAnalyze quick = new QuickSortAnalyze { NeedShuffle = false // 关闭开始的打乱操作。 }; int[] a = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; quick.Sort(a); Console.WriteLine(quick.CompareCount); }
static void Main(string[] args) { // 根据书中的命题 U(中文版 P221,英文版 P347), // 当 k = 0 时,比较次数 ~2N。 // 也可以参考快速排序的分析 // Cn 代表找出 n 个元素的最小值所需的比较次数 // Cn = 1/n * (n+1) + 1/n * (n+1+C1) +...+ 1/n * (n+1+C(n-1)) // Cn = n+1 + 1/n * (C1 + ... + C(n-1)) // nCn = n(n+1) + ... + C(n-1) // (n-1)C(n-1) = n(n-1) + C0 + ... + C(n-2) // nCn - (n-1)C(n-1) = 2n + C(n-1) // nCn = 2n + nC(n-1) // Cn = 2 + C(n-1) // Cn = 2n-2, n > 1. var multiBy10 = 5; var repeatTime = 100; var n = 10000; Console.WriteLine("n\tAverage Compare"); for (var i = 0; i < multiBy10; i++) { long totalCompare = 0; for (var j = 0; j < repeatTime; j++) { var quick = new QuickSortAnalyze(); quick.Select(GetRandomArray(n), 0); totalCompare += quick.CompareCount; } Console.WriteLine("10^" + (i + 4) + "\t" + totalCompare / repeatTime); n *= 10; } }
static void Main(string[] args) { // 证明 // 我们设 C0(n) 代表将 n 个不重复元素排序时大小为 0 的数组的数量。 // 同理有 C1(n) 和 C2(n) 代表大小为 1 的数组的数量和大小为 2 的数组的数量。 // 设 k 代表切分位置,显然切分位置随机且概率相等,在 1~n 之间均匀分布。 // 根据条件,三者都满足下式。 // C(n) = 1/n sum(C(k - 1) + C(n - k)), k=1,2,...,n // 显然 sum(C(k - 1)) = sum(C(n - k)), k=1,2,...,n // 于是可以化简为 // C(n) = 2/n sum(C(k - 1)), k=1,2,...,n // nC(n) = 2 * sum(C(k-1)), k=1,2,...,n // 同理有 // (n-1)C(n-1) = 2 * sum(C(k-1)), k = 1,2,...,n-1 // 相减得到递推式 // nC(n) - (n-1)C(n-1) = 2*C(n-1) // C(n) = (n+1)/n * C(n-1) // 利用累乘法可以求得通项公式 // C(n)=C(k)*(n+1)/(k+1), n>k // 对于 C0 有 C0(0)=1, C0(1)=0 // C0(2)=C(0)+C(1)=1 // C0(n)=(n+1)/3, n>2 // 对于 C1 有 C1(0)=0, C1(1)=1 // C1(2)=C1(0)+C1(1)=1 // C1(n)=(n+1)/3, n>2 // 对于 C2 有 C2(0)=C2(1)=0, C2(2)=1 // C2(3)=1/3*2*(C2(0)+C2(1)+C2(2))=2/3 // C2(n)=C2(3)*(n+1)/4=(n+1)/6, n>3 // 结论 // C0(n)=C1(n)=(n+1)/3, C2(n)=(n+1)/6 var n = 1000; var sort = new QuickSortAnalyze(); Console.WriteLine("n\t0\t1\t2"); for (var i = 0; i < 5; i++) { var a = new int[n]; for (var j = 0; j < n; j++) { a[j] = j; } SortCompare.Shuffle(a); sort.Sort(a); Console.WriteLine(n + "\t" + sort.Array0Num + "\t" + sort.Array1Num + "\t" + sort.Array2Num); n *= 2; } }
static void Main(string[] args) { // 约为 NlogN 次 var sort = new QuickSortAnalyze(); var N = 100; Console.WriteLine("N\tCompare\tNlogN"); for (var i = 0; i < 4; i++) { var a = new int[N]; for (var j = 0; j < a.Length; j++) { a[j] = 1; } sort.Sort(a); Console.WriteLine(N + "\t" + sort.CompareCount + "\t" + N * Math.Log(N) / Math.Log(2)); N *= 10; } }
static void Main(string[] args) { QuickSortAnalyze quick = new QuickSortAnalyze { NeedShuffle = false, // 关闭打乱 NeedPath = true // 显示排序轨迹 }; int[] a = QuickBest.Best(10); for (int i = 0; i < 10; i++) { Console.Write(a[i] + " "); } Console.WriteLine(); quick.Sort(a); for (int i = 0; i < 10; i++) { Console.Write(a[i] + " "); } Console.WriteLine(); }
static void Main(string[] args) { // 插入排序 Console.WriteLine("Insertion Sort"); Test(new InsertionSort(), 7, 1); // 选择排序 Console.WriteLine("Selection Sort"); Test(new SelectionSort(), 7, 1); // 希尔排序 Console.WriteLine("Shell Sort"); Test(new ShellSort(), 7, 1); // 归并排序 Console.WriteLine("Merge Sort"); Test(new MergeSort(), 7, 1); // 快速排序 Console.WriteLine("Quick Sort"); var quick = new QuickSortAnalyze { NeedShuffle = false, NeedPath = false }; Test(quick, 7, 1); // 堆排序 Console.WriteLine("Heap Sort"); var array = new Item <int> [7]; for (var i = 0; i < 7; i++) { array[i] = new Item <int>(i, 1); } Heap.Sort(array); for (var i = 0; i < 7; i++) { Console.Write(array[i].Index + " "); } Console.WriteLine(); }