8种排序算法的C#实现

排序是将一个数据元素(或记录)的任意序列,重新排列成一个按关键字有序的序列。排序根据涉及的存储器的不同分为内部排序和外部排序:内部排序是指待排序记录存放在内存进行的排序过程;外部排序是指待排序记录的数量很大,以致内存一次不能容纳全部记录,在排序过程中尚需对外存进行访问的排序过程。本文仅讨论内部排序。

1.直接插入排序

最简单的排序方法,基本操作是将一个记录插入到已排好序的有序表中,从而得到一个新的、记录数增1的有序表。

此处为了程序的通用,没有采取数组0位作为哨兵位的做法。

C#实现:

 1 public static int[] StraightInsertionSort(int[] arr)
 2 {
 3     int temp;
 4     for (int i = 1; i < arr.Length; i++)
 5     {
 6         if (arr[i] < arr[i - 1])
 7         {
 8             //将arr[i]元素存入temp
 9             temp = arr[i];
10             int j = i;
11
12             //将i左侧比i大的元素右移一位
13             do
14             {
15                 arr[j] = arr[j - 1];
16                 j--;
17             } while (j > 0 && temp < arr[j - 1]);
18
19             //将temp存入比i大的最小元素的左侧位置
20             arr[j] = temp;
21         }
22
23         /*上边实现方法的简化写法,但需要多判断一次,因为初始时j==i==1>0
24         int j = i;
25         while (j > 0 && arr[j] < arr[j - 1])
26         {
27             temp = arr[j];
28             arr[j] = arr[j - 1];
29             arr[j - 1] = temp;
30             j--;
31         }
32         */
33     }
34     return arr;
35 }

直接插入排序

2.希尔排序

又称“缩小增量排序”,也是一种属插入排序类的方法,但在时间效率上较直接插入排序有较大的改进。

基本思想是先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。

C#实现:

 1 public static int[] ShellSort(int[] arr)
 2 {
 3     int temp;
 4     //初始步长
 5     int gap = arr.Length / 2;
 6     while (gap != 0)
 7     {
 8         #region 改进版的直接插入排序
 9         for (int i = gap; i < arr.Length; i++)
10         {
11             if (arr[i] < arr[i - gap])
12             {
13                 temp = arr[i];
14                 int j = i;
15
16                 do
17                 {
18                     arr[j] = arr[j - gap];
19                     j -= gap;
20                 } while (j - gap >= 0 && temp < arr[j - gap]);
21
22                 arr[j] = temp;
23             }
24         }
25         #endregion
26
27         //缩小增量
28         gap /= 2;
29     }
30     return arr;
31 }

希尔排序

3.冒泡排序

它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。

C#实现:

 1 public static int[] BubbleSort(int[] arr)
 2 {
 3     int temp;
 4     //每趟排序将无序元素中的最小元素换到有序元素的右侧
 5     for (int i = 0; i < arr.Length - 1; i++)
 6     {
 7         for (int j = arr.Length - 1; j > i; j--)
 8         {
 9             if (arr[j - 1] > arr[j])
10             {
11                 temp = arr[j - 1];
12                 arr[j - 1] = arr[j];
13                 arr[j] = temp;
14             }
15         }
16     }
17     return arr;
18 }

冒泡排序

4.快速排序

对冒泡排序的一种改进,基本思想是,通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。

快速排序是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短。

C#实现:

 1 public static int[] QuickSort(int[] arr)
 2 {
 3     return QuickSort(arr, 0, arr.Length - 1);
 4 }
 5
 6 /// <summary>
 7 /// 递归快速排序
 8 /// </summary>
 9 /// <param name="arr">目标数组</param>
10 /// <param name="low">数组起始位置</param>
11 /// <param name="high">数组终止位置</param>
12 /// <returns></returns>
13 private static int[] QuickSort(int[] arr, int low, int high)
14 {
15     if (low < high)
16     {
17         //将目标数组一分为二,得到枢轴位置
18         int pivotLoc = Partition(arr, low, high);
19         //对低子表递归排序
20         QuickSort(arr, low, pivotLoc - 1);
21         //对高子表递归排序
22         QuickSort(arr, pivotLoc + 1, high);
23     }
24     return arr;
25 }
26
27 private static void Swap(ref int index, ref int indexReplace)
28 {
29     int temp = index;
30     index = indexReplace;
31     indexReplace = temp;
32 }
33
34 /// <summary>
35 /// 一趟快排
36 /// </summary>
37 /// <param name="arr">目标数组</param>
38 /// <param name="low">数组起始位置</param>
39 /// <param name="high">数组终止位置</param>
40 /// <returns></returns>
41 private static int Partition(int[] arr, int low, int high)
42 {
43     //用子表的第一个记录作枢轴记录
44     int pivotKey = arr[low];
45     //从表的两端交替地向中间扫描
46     while (low < high)
47     {
48         //将比枢轴记录小的记录交换到低端
49         while (low < high && arr[high] >= pivotKey)
50         {
51             --high;
52         }
53         arr[low] = arr[high];
54         //将比枢轴记录大的记录交换到高端
55         while (low < high && arr[low] <= pivotKey)
56         {
57             ++low;
58         }
59         arr[high] = arr[low];
60     }
61     //枢轴记录到位
62     arr[low] = pivotKey;
63     return low;
64 }

快速排序

5.简单选择排序

一趟简单排序的操作为:通过n-i次关键字间的比较,从n-i+1个记录中选出关键字最小的记录,并和第i(1<=i<=n)个记录交换。

C#实现:

 1 public static int[] SimpleSelectionSort(int[] arr)
 2 {
 3     int temp;
 4     //每趟排序将无序元素中的最小元素换到有序元素的右侧
 5     for (int i = 0; i < arr.Length; i++)
 6     {
 7         int min = i;
 8         for (int j = i + 1; j < arr.Length; j++)
 9         {
10             if (arr[j] < arr[min])
11             {
12                 min = j;
13             }
14         }
15         temp = arr[i];
16         arr[i] = arr[min];
17         arr[min] = temp;
18     }
19     return arr;
20 }

直接选择排序

6.堆排序

堆排序是一种树形选择排序,是对直接选择排序的有效改进。

初始时把要排序的n个数的序列看作是一棵顺序存储的二叉树(一维数组存储二叉树),调整它们的存储序,使之成为一个堆,将堆顶元素输出,得到n 个元素中最小(或最大)的元素,这时堆的根节点的数最小(或者最大)。然后对前面(n-1)个元素重新调整使之成为堆,输出堆顶元素,得到n 个元素中次小(或次大)的元素。依此类推,直到只有两个节点的堆,并对它们作交换,最后得到有n个节点的有序序列。

C#实现:

 1 private static int heapSize;
 2 public static int[] HeapSort(int[] arr)
 3 {
 4     heapSize = arr.Length;
 5     //构建最大堆
 6     for (int i = arr.Length / 2; i > 0; i--)
 7     {
 8         BuildMaxHeap(arr, i);
 9     }
10     //将堆顶元素放入堆底的有序序列,并重新调整最大堆
11     for (int i = arr.Length - 1; i > 0; i--)
12     {
13         //每次在构建好最大堆后,将第一个元素arr[0]和最后一个元素arr[i]交换
14         swapHeap(arr, 0, i);
15         //此时arr[i]为堆的最大值,所以对arr[0]到arr[i - 1]元素进行调整
16         heapSize--;
17         //每次新组成的堆除了根节点其他的节点都保持最大堆的特性,因此只要再以根节点为基准调整就可以得到新的最大堆
18         BuildMaxHeap(arr, 1);
19     }
20     return arr;
21 }
22
23 //构建最大堆:调整当前节点和子节点使当前节点为最大元素
24 private static void BuildMaxHeap(int[] arr, int index)
25 {
26     int largeIndex = index;
27     //当前节点的左子节点
28     int leftChildIndex = index << 1;
29     //当前节点的右子节点
30     int rightChildIndex = (index << 1) + 1;
31     //左子节点存在且左子节点大于当前节点
32     if (leftChildIndex <= heapSize && arr[leftChildIndex - 1] > arr[largeIndex - 1])
33     {
34         largeIndex = leftChildIndex;
35     }
36     //右子节点存在且右子节点大于当前节点
37     if (rightChildIndex <= heapSize && arr[rightChildIndex - 1] > arr[largeIndex - 1])
38     {
39         largeIndex = rightChildIndex;
40     }
41     if (index != largeIndex)
42     {
43         swapHeap(arr, index - 1, largeIndex - 1);
44         BuildMaxHeap(arr, largeIndex);
45     }
46 }
47
48 //互换堆中的两个值
49 private static void swapHeap(int[] arr, int index, int indexReplace)
50 {
51     int temp = arr[index];
52     arr[index] = arr[indexReplace];
53     arr[indexReplace] = temp;
54 }

堆排序

7.归并排序

C#实现:

 1 public static int[] MergeSort(int[] arr)
 2 {
 3     //在整个排序过程中始终使用同一个暂存数组,空间利用率高
 4     int[] tempArr = new int[arr.Length];
 5     return MergeSort(arr, tempArr, 0, arr.Length - 1);
 6 }
 7
 8 /// <summary>
 9 /// 将目标数组循环折半,递归执行归并排序核心,再组合成有序数组
10 /// </summary>
11 /// <param name="arr">目标数组</param>
12 /// <param name="tempArr">暂存数组</param>
13 /// <param name="first">子表的起始位置</param>
14 /// <param name="last">子表的终止位置</param>
15 /// <returns></returns>
16 private static int[] MergeSort(int[] arr, int[] tempArr, int first, int last)
17 {
18     if (first < last)
19     {
20         int mid = (first + last) / 2;
21         MergeSort(arr, tempArr, first, mid);
22         MergeSort(arr, tempArr, mid + 1, last);
23         MergeSortCore(arr, tempArr, first, mid, last);
24     }
25     return arr;
26 }
27
28 /// <summary>
29 /// 归并排序核心:将两个有序的左右子表(以mid区分),合并成一个有序表
30 /// </summary>
31 /// <param name="arr">目标数组</param>
32 /// <param name="tempArr">暂存数组</param>
33 /// <param name="first">子表的起始位置</param>
34 /// <param name="mid">子表的划分位置</param>
35 /// <param name="last">子表的终止位置</param>
36 /// <returns></returns>
37 private static int[] MergeSortCore(int[] arr, int[] tempArr, int first, int mid, int last)
38 {
39     //左侧子表的起始位置
40     int indexA = first;
41     //右侧子表的起始位置
42     int indexB = mid + 1;
43     int tempIndex = 0;
44     //遍历左右子表,直到其中一个表遍历完
45     while (indexA <= mid && indexB <= last)
46     {
47         //左子表的最小元素 <= 右子表的最小元素
48         if (arr[indexA] <= arr[indexB])
49         {
50             //左子表的最小元素暂存数组,遍历左子表下标+1
51             tempArr[tempIndex++] = arr[indexA++];
52         }
53         else
54         {
55             tempArr[tempIndex++] = arr[indexB++];
56         }
57     }
58     //有一侧子表遍历完后,将另一侧子表剩下的数一次放入暂存数组中,暂存数组保持有序
59     while (indexA <= mid)
60     {
61         tempArr[tempIndex++] = arr[indexA++];
62     }
63     while (indexB <= last)
64     {
65         tempArr[tempIndex++] = arr[indexB++];
66     }
67     //将暂存数组中有序的元素写回到目标数组的指定位置,使进行归并的数组段有序
68     tempIndex = 0;
69     for (int i = first; i <= last; i++)
70     {
71         arr[i] = tempArr[tempIndex++];
72     }
73     return arr;
74 }

归并排序

8.基数排序

基数排序是借助“分配”和“收集”两种操作对单逻辑关键字进行排序的一种内部排序方法。例如,若关键字是数值,且其值都在0<=K<=999范围内,则可把每一个十进制数字看成一个关键字,即可认为K由3个关键字(Kº,K¹,K²)组成,其中Kº是百位数,K¹是十位数,K²是个位数。

C#实现:

 1 public static int[] RadixSort(int[] arr)
 2 {
 3     //数组中数字的最大位数
 4     int maxDigit = 0;
 5
 6     //查找最大位数
 7     foreach (int item in arr)
 8     {
 9         int tempDigit = item.ToString().Length;
10         if (tempDigit > maxDigit)
11         {
12             maxDigit = tempDigit;
13         }
14     }
15
16     for (int i = 0; i < maxDigit; i++)
17     {
18         int[] tempArr = new int[arr.Length];
19         int[] countArr = new int[10];
20
21         for (int j = 0; j < arr.Length; j++)
22         {
23             //数组中每个元素在i位上的数字 = 该元素在<=i位的数字 - 该元素在<=(i-1)位的元素
24             int splitNum = (int)(arr[j] / Math.Pow(10, i)) - (int)(arr[j] / Math.Pow(10, i + 1)) * 10;
25             //累加每个元素在i位上的数字出现的次数,如12,118,92三个数字组成的数组,则当i==1(每个数字的十位)时,countArr[1] == 2, countArr[9] == 1,其余为0
26             countArr[splitNum]++;
27         }
28
29         //累加在i位上出现j及比j小的数字的次数
30         for (int j = 1; j < 10; j++)
31         {
32             countArr[j] += countArr[j - 1];
33         }
34
35         //从后向前遍历数组,根据每个元素i位上的数字(splitNum),得到比该数字小或等于该数字的元素个数(countArr[splitNum]),即为该元素在排序后数组中的位置(splitNumIndex)
36         for (int j = arr.Length - 1; j >= 0; j--)
37         {
38             int splitNum = (int)(arr[j] / Math.Pow(10, i)) - (int)(arr[j] / Math.Pow(10, i + 1)) * 10;
39             int splitNumIndex = countArr[splitNum] - 1;
40             tempArr[splitNumIndex] = arr[j];
41
42             countArr[splitNum]--;
43         }
44         Array.Copy(tempArr, arr, tempArr.Length);
45     }
46     return arr;
47 }

基数排序

总结:各排序算法的稳定性,时间、空间复杂度比较

附:上述代码的调用示例

 1 static void Main(string[] args)
 2 {
 3     int[] arr = { 1, 51, 15, 15, 4, 18, 12, 54, 3, 53, 9, 58, 0, 26, 325, 7, 5, 56, 842, 785, 93, 65, 10, 87, 8, 66, 2, 12, 678, 35, 33, 32, 62, 624, 63, 6, 25, 8, 11, 89, 46 };
 4     int[] arrResult = Sort.RadixSort(arr);
 5     for (int i = 0; i < arrResult.Length; i++)
 6     {
 7         Console.Write(arrResult[i]);
 8         if (i < arrResult.Length - 1)
 9         {
10             Console.Write(",");
11         }
12     }
13     Console.Read();
14 }

参考资料:八大排序算法 数据结构(C语言版)

时间: 2024-12-21 04:18:27

8种排序算法的C#实现的相关文章

八种排序算法

最近一段时间自己在研究各种排序算法,于是自己写了一个八种排序算法的集合: /************************************************************************* > Copyright (c)2014 stay hungry,stay foolish !!! > File Name: sort.cpp > Author: kanty > Mail: [email protected] > Created Time:

几种排序算法比较

图解数据结构(10)——排序 十四.排序(Sort) http://www.cppblog.com/guogangj/archive/2009/11/13/100876.html <Thinking in Algorithm>12.详解十一种排序算法 http://blog.csdn.net/speedme/article/details/23021467

数据结构中的7种排序算法

数据结构中的7种排序算法 排序是将一个记录的任意序列重新排列成一个按键值有序的序列. 时间复杂度主要考虑元素的移动次数. 结构如下: 1.直接插入排序 1,定义:依次将待排序序列中的每一个记录插入到一个已经排好序的序列中,直到全部记录都排好序. 2,时间复杂度:在最好情况下,待排序序列为正序,时间复杂度为O(n):最坏情况下,待排序序列为逆序,时间复杂度为O(n^2);平均情况下,时间复杂度为O(n^2). 3,空间复杂度:O(1). public static void insertSort(

总结N种排序算法及实现

排序算法是一个简单的问题,但在此问题上却有大量的研究!当前的排序算法通常按照如下四个方面进行分类(或是评价): 1.时间复杂度:一个排序算法的理想性能是O(n).一般而言,好的性能O(nlogn),坏的性能O(n2). 2.空间复杂度(内存使用量) 3.稳定性:稳定的排序算法会让原本有相等键值的记录维持原本的相对次序. 4.排序方式:插入.交换.选择.合并等 一.冒泡排序:这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端. 步骤:1.比较相邻的两个元素,如果第一个比第二个大,就

八种排序算法(内部排序)

八种排序算法很长时间没有使用了,今天做一个总结,方便以后自己用的时候参考. 这八种排序算法都是内部算法,这八种排序算法分别是: 1. 插入排序 1)直接插入排序 2)希尔排序 2.选择排序 1)简单选择排序 2)堆排序 3.交换排序 1)冒泡排序 2)快速排序 4.归并排序 5.基数排序 一.直接插入排序 将一个记录插入到已经排好序的有序表中,从而得到一个新的.记录数增1的有序表.在实际操作中,先将序列的第一个记录看成是一个有序的子序列,然后从第二个.第三个.……记录逐个进行插入,直至整个序列有

12种排序算法:原理、图解、动画视频演示、代码以及笔试面试题目中的应用

出处:http://blog.csdn.net/han_xiaoyang/article/details/12163251. 声明:版权所有,转载请注明出处,谢谢. 0.前言 从这一部分开始直接切入我们计算机互联网笔试面试中的重头戏算法了,初始的想法是找一条主线,比如数据结构或者解题思路方法,将博主见过做过整理过的算法题逐个分析一遍(博主当年自己学算法就是用这种比较笨的刷题学的,囧),不过又想了想,算法这东西,博主自己学的过程中一直深感,基础还是非常重要的,很多难题是基础类数据结构和题目的思想综

四种排序算法PHP实现类

四种排序算法的PHP实现:1) 插入排序(Insertion Sort)的基本思想是: 每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子文件中的适当位置,直到全部记录插入完成为止. 2) 选择排序(Selection Sort)的基本思想是: 每一趟从待排序的记录中选出关键字最小的记录,顺序放在已排好序的子文件的最后,直到全部记录排序完毕. 3) 冒泡排序的基本思想是: 两两比较待排序记录的关键字,发现两个记录的次序相反时即进行交换,直到没有反序的记录为止. 4) 快速排序实质上和

经典的两种排序算法

一.冒泡排序 int temp = 0; for (int j = 1; j < a.Length; j++) { for (int i = 0; i < a.Length - j; i++)//内循环,每走一趟会把最小值放到最后 { if (a[i] < a[i + 1]) { temp = a[i]; a[i] = a[i + 1]; a[i + 1] = temp; } } } 二.选择法排序 int min; for (int j = 0; j< a.Length; j++

几种排序算法的C++实现——快速排序、堆排序、基数排序

排序算法是非常常见的面试笔试问题,考查的一个人的基本功,本文将一些排序做了C++的实现,就当是做个学习总结吧. 1.快速排序 快速排序的中心是填坑法,取一个数(这里选取第一个数)作为基准数temp,从队尾开始寻找第一个比基准数小的数a[j],交换a[j]和temp,然后队首开始查找第一个比temp大的数a[i],交换之,遍历的结果是当i>=j时,temp左边的数都小于temp,后边的数都大于temp,这个有点像归并排序.最后利用递归调用完成排序,代码如下: 1 void QuickSort(in

算法—比较两种排序算法:选择排序和插入排序

现在我们已经实现了两种排序算法,我们很自然地想知道选择排序和插入排序哪种更快.这里我们第一次用实践说明我们解决这个问题的办法. 性质:对于随机排序的无重复主键的数组,插入排序和选择排序的运行时间是平方级别的,两者之比应该是一个较小的常数. 例证:这个结论在过去的半个世纪中已经在许多不同类型的计算机上经过了验证.在1980年本书第一版完成之时插入排序就比选择排序快一倍,现在仍然是这样,尽管那时这些算法将10万条数据排序需要几个小时而现在只需要几秒钟.在你的计算机上插入排序也比选择排序快一些吗?可以