最全排序算法汇总(文末赠书)
专注于编程、互联网动态。最终将总结的技术、心得、经验(数据结构与算法、源码分析等)分享给大家,这里不只限于技术!还有职场心得、生活感悟、以及面经。点击上方 "程序员小乐" ,选择“置顶公众号”,第一时间送达!
每日英文
don't corrupted themselves, life is not only an opportunity, try to.
不要堕落了自己,人生并不只有一次机会,努力把握。乐乐有话说
成长这一路就是懂得,嘴努力;知道低调谦逊,学会强大自己;在每一个值得珍惜的日子,拼命去成为自己想成为的人 。
目录
1、简介
2、交换排序
(1)冒泡排序
(2)快速排序
3、插入排序
(1)直接插入排序
(2)希尔排序
4、选择排序
(1)简单选择排序
(2)堆排序
5、归并排序
6、基数排序
7、总结
1、简介
排序是计算机内经常进行的一种操作,其目的是将一组“无序”的记录序列调整为“有序”的记录序列。分内部排序和外部排序。若整个排序过程不需要访问外存便能完成,则称此类排序问题为内部排序。反之,若参加排序的记录数量很大,整个序列的排序过程不可能在内存中完成,则称此类排序问题为外部排序。内部排序的过程是一个逐步扩大记录的有序序列长度的过程。
将杂乱无章的数据元素,通过一定的方法按关键字顺序排列的过程叫做排序。假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,ri=rj,且ri在rj之前,而在排序后的序列中,ri仍在rj之前,则称这种排序算法是稳定的;否则称为不稳定的。
分类
稳定排序:假设在待排序的文件中,存在两个或两个以上的记录具有相同的关键字,在用某种排序法排序后,若这些相同关键字的元素的相对次序仍然不变,则这种排序方法是稳定的。其中冒泡,插入,基数,归并属于稳定排序,选择,快速,希尔,堆属于不稳定排序。
就地排序:若排序算法所需的辅助空间并不依赖于问题的规模n,即辅助空间为O(1),则称为就地排序。(百度百科)
2、交换排序
(1)冒泡排序
已知一组无序数据a[1]、a[2]、……a[n],需将其按升序排列。首先比较a[1]与a[2]的值,若a[1]大于a[2]则交换两者的值,否则不变。再比较a[2]与a[3]的值,若a[2]大于a[3]则交换两者的值,否则不变。再比较a[3]与a[4],以此类推,最后比较a[n-1]与a[n]的值。这样处理一轮后,a[n]的值一定是这组数据中最大的。再对a[1]~a[n-1]以相同方法处理一轮,则a[n-1]的值一定是a[1]~a[n-1]中最大的。再对a[1]~a[n-2]以相同方法处理一轮,以此类推。共处理n-1轮后a[1]、a[2]、……a[n]就以升序排列了。降序排列与升序排列相类似,若a[1]小于a[2]则交换两者的值,否则不变,后面以此类推。 总的来讲,每一轮排序后最大(或最小)的数将移动到数据序列的最后,理论上总共要进行n(n-1)/2次交换。
简单测试
1 class Program
2 {
3 static void Main(string[] args)
4 {
5 int[] arry = new int[] { 1, 2, 3, 4, 1, -1 };
6 arry.BubbleSort();
7 for (int i = 0; i < arry.Length; i++)
8 {
9 Console.Write("\t" + arry[i]);
10 }
11 Console.Read();
12 }
13 }
结果
这里采用的是为整型数组添加扩展方法实现的冒泡排序。
优点:稳定
缺点:慢,每次只移动相邻的两个元素。
时间复杂度:理想情况下(数组本来就是有序的),此时最好的时间复杂度为o(n),最坏的时间复杂度(数据反序的),此时的时间复杂度为o(nn) 。冒泡排序的平均时间负责度为o(nn).
(2)快速排序
设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序。值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。
一趟快速排序的算法是:
1)设置两个变量i、j,排序开始的时候:i=0,j=N-1;
2)以第一个数组元素作为关键数据,赋值给key,即key=A[0];
3)从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于key的值A[j],将A[j]和A[i]互换;
4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]互换;
5)重复第3、4步,直到i=j; (3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束)。
代码实现
1 /// <summary>
2 /// 快速排序
3 /// </summary>
4 /// <param name="arry">要排序的数组</param>
5 /// <param name="left">低位</param>
6 /// <param name="right">高位</param>
7 public static void QuickSort(this int[] arry, int left, int right)
8 {
9 //左边索引小于右边,则还未排序完成
10 if (left < right)
11 {
12 //取中间的元素作为比较基准,小于他的往左边移,大于他的往右边移
13 int middle = arry[(left + right) / 2];
14 int i = left - 1;
15 int j = right + 1;
16 while (true)
17 {
18 //移动下标,左边的往右移动,右边的向左移动
19 while (arry[++i] < middle && i < right);
20 while (arry[--j] > middle && j > 0);
21 if (i >= j)
22 break;
23 //交换位置
24 int number = arry[i];
25 arry[i] = arry[j];
26 arry[j] = number;
27
28 }
29 QuickSort(arry, left, i - 1);
30 QuickSort(arry, j + 1, right);
31 }
32 }
简单测试
1 static void Main(string[] args)
2 {
3 int[] arry = new int[] { 34,1,221,50,44,58,12,1,1};
4 //arry.BubbleSort();
5 arry.QuickSort(0, arry.Length-1 );
6 for (int i = 0; i < arry.Length; i++)
7 {
8 Console.Write("\t" + arry[i]);
9 }
10 Console.Read();
11 }
结果
3、插入排序
(1)直接插入排序
每次从无序表中取出第一个元素,把它插入到有序表的合适位置,使有序表仍然有序。
第一趟比较前两个数,然后把第二个数按大小插入到有序表中; 第二趟把第三个数据与前两个数从前向后扫描,把第三个数按大小插入到有序表中;依次进行下去,进行了(n-1)趟扫描以后就完成了整个排序过程。
直接插入排序属于稳定的排序,最坏时间复杂性为O(n^2),空间复杂度为O(1)。
直接插入排序是由两层嵌套循环组成的。外层循环标识并决定待比较的数值。内层循环为待比较数值确定其最终位置。直接插入排序是将待比较的数值与它的前一个数值进行比较,所以外层循环是从第二个数值开始的。当前一数值比待比较数值大的情况下继续循环比较,直到找到比待比较数值小的并将待比较数值置入其后一位置,结束该次循环。
值得注意的是,我们必需用一个存储空间来保存当前待比较的数值,因为当一趟比较完成时,我们要将待比较数值置入比它小的数值的后一位 插入排序类似玩牌时整理手中纸牌的过程。插入排序的基本方法是:每步将一个待排序的记录按其关键字的大小插到前面已经排序的序列中的适当位置,直到全部记录插入完毕为止。
代码实现
1 /// <summary>
2 /// 直接插入排序
3 /// </summary>
4 /// <param name="arry">要排序的数组</param>
5 public static void InsertSort(this int[] arry)
6 {
7 //直接插入排序是将待比较的数值与它的前一个数值进行比较,所以外层循环是从第二个数值开始的
8 for (int i = 1; i < arry.Length; i++)
9 {
10 //如果当前元素小于其前面的元素
11 if (arry[i] < arry[i - 1])
12 {
13 //用一个变量来保存当前待比较的数值,因为当一趟比较完成时,我们要将待比较数值置入比它小的数值的后一位
14 int temp = arry[i];
15 int j = 0;
16 for (j = i - 1; j >= 0 && temp < arry[j]; j--)
17 {
18 arry[j + 1] = arry[j];
19 }
20 arry[j + 1] = temp;
21 }
22 }
23 }
测试
1 static void Main(string[] args)
2 {
3 int[] arry = new int[] { 34,1,221,50,44,58,12};
4 //arry.BubbleSort();
5 //arry.QuickSort(0, arry.Length-1 );
6 arry.InsertSort();
7 for (int i = 0; i < arry.Length; i++)
8 {
9 Console.Write("\t" + arry[i]);
10 }
11 Console.Read();
12 }
结果
(2)希尔排序
希尔排序(Shell Sort)是插入排序的一种。也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因DL.Shell于1959年提出而得名。
希尔排序是基于插入排序的以下两点性质而提出改进方法的:
插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率。
但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位。
基本思想
先取一个小于n的整数d1作为第一个增量,把文件的全部记录分组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量 =1( < …<d2<d1),即所有记录放在同一组中进行直接插入排序为止。
该方法实质上是一种分组插入方法
比较相隔较远距离(称为增量)的数,使得数移动时能跨过多个元素,则进行一次比[2] 较就可能消除多个元素交换。D.L.shell于1959年在以他名字命名的排序算法中实现了这一思想。算法先将要排序的一组数按某个增量d分成若干组,每组中记录的下标相差d.对每组中全部元素进行排序,然后再用一个较小的增量对它进行,在每组中再进行排序。当增量减到1时,整个要排序的数被分成一组,排序完成。
一般的初次取序列的一半为增量,以后每次减半,直到增量为1。
代码实现
1 /// <summary>
2 /// 希尔排序
3 /// </summary>
4 /// <param name="arry">待排序的数组</param>
5 public static void ShellSort(this int[] arry)
6 {
7 int length = arry.Length;
8 for (int h = length / 2; h > 0; h = h / 2)
9 {
10 //here is insert sort
11 for (int i = h; i < length; i++)
12 {
13 int temp = arry[i];
14 if (temp < arry[i - h])
15 {
16 for (int j = 0; j < i; j += h)
17 {
18 if (temp<arry[j])
19 {
20 temp = arry[j];
21 arry[j] = arry[i];
22 arry[i] = temp;
23 }
24 }
25 }
26 }
27 }
28 }
测试
1 static void Main(string[] args)
2 {
3 int[] arry = new int[] { 34,1,221,50,44,58,12};
4 //arry.BubbleSort();
5 //arry.QuickSort(0, arry.Length-1 );
6 //arry.InsertSort();
7 arry.ShellSort();
8 for (int i = 0; i < arry.Length; i++)
9 {
10 Console.Write("\t" + arry[i]);
11 }
12 Console.Read();
13 }
结果
4、选择排序
(1)简单选择排序
设所排序序列的记录个数为n。i取1,2,…,n-1,从所有n-i+1个记录(Ri,Ri+1,…,Rn)中找出排序码最小的记录,与第i个记录交换。执行n-1趟 后就完成了记录序列的排序。
在简单选择排序过程中,所需移动记录的次数比较少。最好情况下,即待排序记录初始状态就已经是正序排列了,则不需要移动记录。
最坏情况下,即待排序记录初始状态是按逆序排列的,则需要移动记录的次数最多为3(n-1)。简单选择排序过程中需要进行的比较次数与初始状态下待排序的记录序列的排列情况无关。当i=1时,需进行n-1次比较;当i=2时,需进行n-2次比较;依次类推,共需要进行的比较次数是(n-1)+(n-2)+…+2+1=n(n-1)/2,即进行比较操作的时间复杂度为O(n^2),进行移动操作的时间复杂度为O(n)。
代码实现
1 /// <summary>
2 /// 简单选择排序
3 /// </summary>
4 /// <param name="arry">待排序的数组</param>
5 public static void SimpleSelectSort(this int[] arry)
6 {
7 int tmp = 0;
8 int t = 0;//最小数标记
9 for (int i = 0; i < arry.Length; i++)
10 {
11 t = i;
12 for (int j = i + 1; j < arry.Length; j++)
13 {
14 if (arry[t] > arry[j])
15 {
16 t = j;
17 }
18 }
19 tmp = arry[i];
20 arry[i] = arry[t];
21 arry[t] = tmp;
22 }
23 }
测试
1 static void Main(string[] args)
2 {
3 int[] arry = new int[] { 34,1,221,50,44,58,12};
4 //arry.BubbleSort();
5 //arry.QuickSort(0, arry.Length-1 );
6 //arry.InsertSort();
7 //arry.ShellSort();
8 arry.SimpleSelectSort();
9 for (int i = 0; i < arry.Length; i++)
10 {
11 Console.Write("\t" + arry[i]);
12 }
13 Console.Read();
14 }
结果
(2)堆排序
堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。堆分为大根堆和小根堆,是完全二叉树。大根堆的要求是每个节点的值都不大于其父节点的值,即A[PARENT[i]] >= A[i]。在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶。
代码实现
1 /// <summary>
2 /// 堆排序
3 /// </summary>
4 /// <param name="arry"></param>
5 public static void HeapSort(this int[] arry, int top)
6 {
7 List<int> topNode = new List<int>();
8
9 for (int i = arry.Length / 2 - 1; i >= 0; i--)
10 {
11 HeapAdjust(arry, i, arry.Length);
12 }
13
14 for (int i = arry.Length - 1; i >= arry.Length - top; i--)
15 {
16 int temp = arry[0];
17 arry[0] = arry[i];
18 arry[i] = temp;
19 HeapAdjust(arry, 0, i);
20 }
21 }
22 /// <summary>
23 /// 构建堆
24 /// </summary>
25 /// <param name="arry"></param>
26 /// <param name="parent"></param>
27 /// <param name="length"></param>
28 private static void HeapAdjust(int[] arry, int parent, int length)
29 {
30 int temp = arry[parent];
31
32 int child = 2 * parent + 1;
33
34 while (child < length)
35 {
36 if (child + 1 < length && arry[child] < arry[child + 1]) child++;
37
38 if (temp >= arry[child])
39 break;
40
41 arry[parent] = arry[child];
42
43 parent = child;
44
45 child = 2 * parent + 1;
46 }
47
48 arry[parent] = temp;
49 }
测试
1 static void Main(string[] args)
2 {
3 int[] arry = new int[] { 34,1,221,50,44,58,12};
4 //arry.BubbleSort();
5 //arry.QuickSort(0, arry.Length-1 );
6 //arry.InsertSort();
7 //arry.ShellSort();
8 //arry.SimpleSelectSort();
9 arry.HeapSort(arry.Length);
10 for (int i = 0; i < arry.Length; i++)
11 {
12 Console.Write("\t" + arry[i]);
13 }
14 Console.Read();
15 }
结果
5、归并排序
归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
归并过程为:比较a[i]和a[j]的大小,若a[i]≤a[j],则将第一个有序表中的元素a[i]复制到r[k]中,并令i和k分别加上1;否则将第二个有序表中的元素a[j]复制到r[k]中,并令j和k分别加上1,如此循环下去,直到其中一个有序表取完,然后再将另一个有序表中剩余的元素复制到r中从下标k到下标t的单元。归并排序的算法我们通常用递归实现,先把待排序区间[s,t]以中点二分,接着把左边子区间排序,再把右边子区间排序,最后把左区间和右区间用一次归并操作合并成有序的区间[s,t]。
归并操作(merge),也叫归并算法,指的是将两个顺序序列合并成一个顺序序列的方法。
如 设有数列{6,202,100,301,38,8,1}
初始状态:6,202,100,301,38,8,1
第一次归并后:{6,202},{100,301},{8,38},{1},比较次数:3;
第二次归并后:{6,100,202,301},{1,8,38},比较次数:4;
第三次归并后:{1,6,8,38,100,202,301},比较次数:4;
总的比较次数为:3+4+4=11,;
逆序数为14;
归并操作的工作原理如下:
第一步:申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
第二步:设定两个指针,最初位置分别为两个已经排序序列的起始位置
第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
重复步骤3直到某一指针超出序列尾
将另一序列剩下的所有元素直接复制到合并序列尾
代码实现
1 /// <summary>
2 /// 归并排序
3 /// </summary>
4 /// <param name="arry"></param>
5 /// <param name="first"></param>
6 /// <param name="last"></param>
7 public static void MergeSort(this int[] arry, int first, int last)
8 {
9 if (first + 1 < last)
10 {
11 int mid = (first + last) / 2;
12 MergeSort(arry, first, mid);
13 MergeSort(arry, mid, last);
14 Merger(arry, first, mid, last);
15 }
16 }
17 /// <summary>
18 /// 归并
19 /// </summary>
20 /// <param name="arry"></param>
21 /// <param name="first"></param>
22 /// <param name="mid"></param>
23 /// <param name="last"></param>
24 private static void Merger(int[] arry, int first, int mid, int last)
25 {
26 Queue<int> tempV = new Queue<int>();
27 int indexA, indexB;
28 //设置indexA,并扫描subArray1 [first,mid]
29 //设置indexB,并扫描subArray2 [mid,last]
30 indexA = first;
31 indexB = mid;
32 //在没有比较完两个子标的情况下,比较 v[indexA]和v[indexB]
33 //将其中小的放到临时变量tempV中
34 while (indexA < mid && indexB < last)
35 {
36 if (arry[indexA] < arry[indexB])
37 {
38 tempV.Enqueue(arry[indexA]);
39 indexA++;
40 }
41 else
42 {
43 tempV.Enqueue(arry[indexB]);
44 indexB++;
45 }
46 }
47 //复制没有比较完子表中的元素
48 while (indexA < mid)
49 {
50 tempV.Enqueue(arry[indexA]);
51 indexA++;
52 }
53 while (indexB < last)
54 {
55 tempV.Enqueue(arry[indexB]);
56 indexB++;
57 }
58 int index = 0;
59 while (tempV.Count > 0)
60 {
61 arry[first + index] = tempV.Dequeue();
62 index++;
63 }
64 }
测试
1 static void Main(string[] args)
2 {
3 int[] arry = new int[] { 34,1,221,50,44,58,12};
4 //arry.BubbleSort();
5 //arry.QuickSort(0, arry.Length-1 );
6 //arry.InsertSort();
7 //arry.ShellSort();
8 //arry.SimpleSelectSort();
9 //arry.HeapSort(arry.Length);
10 arry.MergeSort(0, arry.Length);
11 for (int i = 0; i < arry.Length; i++)
12 {
13 Console.Write("\t" + arry[i]);
14 }
15 Console.Read();
16 }
结果
6、基数排序
基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O (nlog(r)m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的稳定性排序法。
代码实现
1 /// <summary>
2 /// 基数排序
3 /// 约定:待排数字中没有0,如果某桶内数字为0则表示该桶未被使用,输出时跳过即可
4 /// </summary>
5 /// <param name="arry">待排数组</param>
6 /// <param name="array_x">桶数组第一维长度</param>
7 /// <param name="array_y">桶数组第二维长度</param>
8 public static void RadixSort(this int[] arry, int array_x = 10, int array_y = 100)
9 {
10 /* 最大数字不超过999999999...(array_x个9) */
11 for (int i = 0; i < array_x; i++)
12 {
13 int[,] bucket = new int[array_x, array_y];
14 foreach (var item in arry)
15 {
16 int temp = (item / (int)Math.Pow(10, i)) % 10;
17 for (int l = 0; l < array_y; l++)
18 {
19 if (bucket[temp, l] == 0)
20 {
21 bucket[temp, l] = item;
22 break;
23 }
24 }
25 }
26 for (int o = 0, x = 0; x < array_x; x++)
27 {
28 for (int y = 0; y < array_y; y++)
29 {
30 if (bucket[x, y] == 0) continue;
31 arry[o++] = bucket[x, y];
32 }
33 }
34 }
35
36 }
测试
1 static void Main(string[] args)
2 {
3 int[] arry = new int[] { 34,1,221,50,44,58,12};
4 //arry.BubbleSort();
5 //arry.QuickSort(0, arry.Length-1 );
6 //arry.InsertSort();
7 //arry.ShellSort();
8 //arry.SimpleSelectSort();
9 //arry.HeapSort(arry.Length);
10 //arry.MergeSort(0, arry.Length);
11 arry.RadixSort();
12 for (int i = 0; i < arry.Length; i++)
13 {
14 Console.Write("\t" + arry[i]);
15 }
16 Console.Read();
17 }
结果
7、总结
整数数组各种排序算法扩展类
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading.Tasks;
6
7 namespace Wolfy.Sort
8 {
9 /// <summary>
10 /// 排序辅助类
11 /// </summary>
12 public static class SortExtention
13 {
14 /// <summary>
15 /// 冒泡排序
16 /// </summary>
17 /// <param name="arry">要排序的整数数组</param>
18 public static void BubbleSort(this int[] arry)
19 {
20 for (int i = 0; i < arry.Length-1; i++)
21 {
22 for (int j = 0; j < arry.Length - 1 - i; j++)
23 {
24 //比较相邻的两个元素,如果前面的比后面的大,则交换位置
25 if (arry[j] > arry[j + 1])
26 {
27 int temp = arry[j + 1];
28 arry[j + 1] = arry[j];
29 arry[j] = temp;
30 }
31 }
32 }
33 }
34 /// <summary>
35 /// 快速排序
36 /// </summary>
37 /// <param name="arry">要排序的数组</param>
38 /// <param name="left">低位</param>
39 /// <param name="right">高位</param>
40 public static void QuickSort(this int[] arry, int left, int right)
41 {
42 //左边索引小于右边,则还未排序完成
43 if (left < right)
44 {
45 //取中间的元素作为比较基准,小于他的往左边移,大于他的往右边移
46 int middle = arry[(left + right) / 2];
47 int i = left - 1;
48 int j = right + 1;
49 while (true)
50 {
51 //移动下标,左边的往右移动,右边的向左移动
52 while (arry[++i] < middle && i < right) ;
53 while (arry[--j] > middle && j > 0) ;
54 if (i >= j)
55 break;
56 //交换位置
57 int number = arry[i];
58 arry[i] = arry[j];
59 arry[j] = number;
60
61 }
62 QuickSort(arry, left, i - 1);
63 QuickSort(arry, j + 1, right);
64 }
65 }
66 /// <summary>
67 /// 直接插入排序
68 /// </summary>
69 /// <param name="arry">要排序的数组</param>
70 public static void InsertSort(this int[] arry)
71 {
72 //直接插入排序是将待比较的数值与它的前一个数值进行比较,所以外层循环是从第二个数值开始的
73 for (int i = 1; i < arry.Length; i++)
74 {
75 //如果当前元素小于其前面的元素
76 if (arry[i] < arry[i - 1])
77 {
78 //用一个变量来保存当前待比较的数值,因为当一趟比较完成时,我们要将待比较数值置入比它小的数值的后一位
79 int temp = arry[i];
80 int j = 0;
81 for (j = i - 1; j >= 0 && temp < arry[j]; j--)
82 {
83 arry[j + 1] = arry[j];
84 }
85 arry[j + 1] = temp;
86 }
87 }
88 }
89 /// <summary>
90 /// 希尔排序
91 /// </summary>
92 /// <param name="arry">待排序的数组</param>
93 public static void ShellSort(this int[] arry)
94 {
95 int length = arry.Length;
96 for (int h = length / 2; h > 0; h = h / 2)
97 {
98 //here is insert sort
99 for (int i = h; i < length; i++)
100 {
101 int temp = arry[i];
102 if (temp < arry[i - h])
103 {
104 for (int j = 0; j < i; j += h)
105 {
106 if (temp < arry[j])
107 {
108 temp = arry[j];
109 arry[j] = arry[i];
110 arry[i] = temp;
111 }
112 }
113 }
114 }
115 }
116 }
117 /// <summary>
118 /// 简单选择排序
119 /// </summary>
120 /// <param name="arry">待排序的数组</param>
121 public static void SimpleSelectSort(this int[] arry)
122 {
123 int tmp = 0;
124 int t = 0;//最小数标记
125 for (int i = 0; i < arry.Length; i++)
126 {
127 t = i;
128 for (int j = i + 1; j < arry.Length; j++)
129 {
130 if (arry[t] > arry[j])
131 {
132 t = j;
133 }
134 }
135 tmp = arry[i];
136 arry[i] = arry[t];
137 arry[t] = tmp;
138 }
139 }
140 /// <summary>
141 /// 堆排序
142 /// </summary>
143 /// <param name="arry"></param>
144 public static void HeapSort(this int[] arry, int top)
145 {
146 List<int> topNode = new List<int>();
147
148 for (int i = arry.Length / 2 - 1; i >= 0; i--)
149 {
150 HeapAdjust(arry, i, arry.Length);
151 }
152
153 for (int i = arry.Length - 1; i >= arry.Length - top; i--)
154 {
155 int temp = arry[0];
156 arry[0] = arry[i];
157 arry[i] = temp;
158 HeapAdjust(arry, 0, i);
159 }
160 }
161 /// <summary>
162 /// 构建堆
163 /// </summary>
164 /// <param name="arry"></param>
165 /// <param name="parent"></param>
166 /// <param name="length"></param>
167 private static void HeapAdjust(int[] arry, int parent, int length)
168 {
169 int temp = arry[parent];
170
171 int child = 2 * parent + 1;
172
173 while (child < length)
174 {
175 if (child + 1 < length && arry[child] < arry[child + 1]) child++;
176
177 if (temp >= arry[child])
178 break;
179
180 arry[parent] = arry[child];
181
182 parent = child;
183
184 child = 2 * parent + 1;
185 }
186
187 arry[parent] = temp;
188 }
189 /// <summary>
190 /// 归并排序
191 /// </summary>
192 /// <param name="arry"></param>
193 /// <param name="first"></param>
194 /// <param name="last"></param>
195 public static void MergeSort(this int[] arry, int first, int last)
196 {
197 if (first + 1 < last)
198 {
199 int mid = (first + last) / 2;
200 MergeSort(arry, first, mid);
201 MergeSort(arry, mid, last);
202 Merger(arry, first, mid, last);
203 }
204 }
205 /// <summary>
206 /// 归并
207 /// </summary>
208 /// <param name="arry"></param>
209 /// <param name="first"></param>
210 /// <param name="mid"></param>
211 /// <param name="last"></param>
212 private static void Merger(int[] arry, int first, int mid, int last)
213 {
214 Queue<int> tempV = new Queue<int>();
215 int indexA, indexB;
216 //设置indexA,并扫描subArray1 [first,mid]
217 //设置indexB,并扫描subArray2 [mid,last]
218 indexA = first;
219 indexB = mid;
220 //在没有比较完两个子标的情况下,比较 v[indexA]和v[indexB]
221 //将其中小的放到临时变量tempV中
222 while (indexA < mid && indexB < last)
223 {
224 if (arry[indexA] < arry[indexB])
225 {
226 tempV.Enqueue(arry[indexA]);
227 indexA++;
228 }
229 else
230 {
231 tempV.Enqueue(arry[indexB]);
232 indexB++;
233 }
234 }
235 //复制没有比较完子表中的元素
236 while (indexA < mid)
237 {
238 tempV.Enqueue(arry[indexA]);
239 indexA++;
240 }
241 while (indexB < last)
242 {
243 tempV.Enqueue(arry[indexB]);
244 indexB++;
245 }
246 int index = 0;
247 while (tempV.Count > 0)
248 {
249 arry[first + index] = tempV.Dequeue();
250 index++;
251 }
252 }
253
254 /// <summary>
255 /// 基数排序
256 /// 约定:待排数字中没有0,如果某桶内数字为0则表示该桶未被使用,输出时跳过即可
257 /// </summary>
258 /// <param name="arry">待排数组</param>
259 /// <param name="array_x">桶数组第一维长度</param>
260 /// <param name="array_y">桶数组第二维长度</param>
261 public static void RadixSort(this int[] arry, int array_x = 10, int array_y = 100)
262 {
263 /* 最大数字不超过999999999...(array_x个9) */
264 for (int i = 0; i < array_x; i++)
265 {
266 int[,] bucket = new int[array_x, array_y];
267 foreach (var item in arry)
268 {
269 int temp = (item / (int)Math.Pow(10, i)) % 10;
270 for (int l = 0; l < array_y; l++)
271 {
272 if (bucket[temp, l] == 0)
273 {
274 bucket[temp, l] = item;
275 break;
276 }
277 }
278 }
279 for (int o = 0, x = 0; x < array_x; x++)
280 {
281 for (int y = 0; y < array_y; y++)
282 {
283 if (bucket[x, y] == 0) continue;
284 arry[o++] = bucket[x, y];
285 }
286 }
287 }
288
289 }
290
291 }
292
293 }
各排序算法时间复杂度与空间复杂度
如果您觉得不错,请别忘了转发、分享、点赞让更多的人去学习, 您的举手之劳,就是对小乐最好的支持,非常感谢!
如何您想进技术群交流,关注公众号在后台回复 “加群”,或者 “学习” 即可
来自:wolfy
链接:http://www.cnblogs.com/wolf-sun
著作权归作者所有,欢迎投稿。
本来 10 本书(面试心法+赠书预告,连续赠送5天),之前已经送出了 6 本(Java趣谈——如何构建一个高效且可伸缩的缓存、单链表的实现,看了都说好,中国程序员VS美国程序员,差距在哪里?),还有 4 本书籍今天送出,等活动结束这周六全部邮寄给大家。
在此感谢博文视点和杨中兴老师的赞助。
《青少年学编程系列丛书机器人Python青少年编程开发实例》介绍MicroPython的快速入门书籍,也是以TurnipBit为基础进行MicroPython实战应用的书籍。
《Android高性能编程》广泛覆盖了开发最优App所涉及的各种话题;深度探索本地编码;对于那些拒绝性能故障及粗放型资源利用的专业级Android开发人员的必备书目。
按 2+2 的规则来分配。活动规则如下:
送书 活动一参与方式:评论区留言,说说自己新的一年计划、理想、面试经历以及遇到的问题等。
获奖条件:留言点赞排名2名获奖 。
大家都知道,小编建立的知识星球『程序员小乐』也有一段时间了,为了感谢愿意相信我的球友们,特意单独从中拿出了两本书出来给星球的读者们。目前星球的人数还不多,人数只有390人左右 ,中奖概率还是蛮大的 。
获奖条件:星球内小程序自动抽奖 2 名获奖,仅限球友!
想加入星球,请扫描下方二维码加入!
送书活动截止日期:2018年4月20号晚上21点(共3天)
推荐阅读
看完本文有收获?请转发分享给更多人
关注「程序员小乐」,提升技能
看完本文有收获?请转发分享给更多人
关注「程序员小乐」,提升技能