一、插入排序(Insertion Sort)



    1. 基本思想:



    每次将一个待排序的数据元素,插入到前面已经排好序的数列中的适当位置,使数列依然有序;直到待排序数据元素全部插入完为止。



    2. 排序过程:



    【示例】:



    [初始关键字] [49] 38 65 97 76 13 27 49



    J=2(38) [38 49] 65 97 76 13 27 49



    J=3(65) [38 49 65] 97 76 13 27 49



    J=4(97) [38 49 65 97] 76 13 27 49



    J=5(76) [38 49 65 76 97] 13 27 49



    J=6(13) [13 38 49 65 76 97] 27 49



    J=7(27) [13 27 38 49 65 76 97] 49



    J=8(49) [13 27 38 49 49 65 76 97]



    一、插入排序(Insertion Sort)



    1. 基本思想:



    每次将一个待排序的数据元素,插入到前面已经排好序的数列中的适当位置,使数列依然有序;直到待排序数据元素全部插入完为止。



    2. 排序过程:



    【示例】:



    [初始关键字] [49] 38 65 97 76 13 27 49



    J=2(38) [38 49] 65 97 76 13 27 49



    J=3(65) [38 49 65] 97 76 13 27 49



    J=4(97) [38 49 65 97] 76 13 27 49



    J=5(76) [38 49 65 76 97] 13 27 49



    J=6(13) [13 38 49 65 76 97] 27 49



    J=7(27) [13 27 38 49 65 76 97] 49



    J=8(49) [13 27 38 49 49 65 76 97]



   


    二、选择排序



    1. 基本思想:



    每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。



    2. 排序过程:



    【示例】:



    初始关键字 [49 38 65 97 76 13 27 49]



    第一趟排序后 13 [38 65 97 76 49 27 49]



    第二趟排序后 13 27 [65 97 76 49 38 49]



    第三趟排序后 13 27 38 [97 76 49 65 49]



    第四趟排序后 13 27 38 49 [49 97 65 76]



    第五趟排序后 13 27 38 49 49 [97 97 76]



    第六趟排序后 13 27 38 49 49 76 [76 97]



    第七趟排序后 13 27 38 49 49 76 76 [ 97]



    最后排序结果 13 27 38 49 49 76 76 97




三、冒泡排序(BubbleSort)

 


    1.基本思想:

 


    两两比较待排序数据元素的大小,发现两个数据元素的次序相反时即进行交换,直到没有反序的数据元素为止。

 


    2.排序过程:

 


    设想被排序的数组R[1..N]垂直竖立,将每个数据元素看作有重量的气泡,根据轻气泡不能在重气泡之下的原则,从下往上扫描数组R,凡扫描到违反本原则的轻气泡,就使其向上“漂浮”,如此反复进行,直至最后任何两个气泡都是轻者在上,重者在下为止。

 


    【示例】:

 


    491313 13 13 13 13 13

 


    384927 27 27 27 27 27

 


    653849 38 38 38 38 38

 


    976538 49 49 49 49 49

 


    769765 49 49 49 49 49

 


    137697 65 65 65 65 65

 


    272776 97 76 76 76 76

 


    494949 76 97 97 97 97



    四、快速排序(Quick Sort)



    1. 基本思想:



    在当前无序区R[1..H]中任取一个数据元素作为比较的“基准”(不妨记为X),用此基准将当前无序区划分为左右两个较小的无序区:R[1..I-1]和R[I+1..H],且左边的无序子区中数据元素均小于等于基准元素,右边的无序子区中数据元素均大于等于基准元素,而基准X则位于最终排序的位置上,即R[1..I-1]≤X.Key≤R[I+1..H](1≤I≤H),当R[1..I-1]和R[I+1..H]均非空时,分别对它们进行上述的划分过程,直至所有无序子区中的数据元素均已排序为止。



    2. 排序过程:



    【示例】:



    初始关键字 [49 38 65 97 76 13 27 49]



    第一次交换后 [27 38 65 97 76 13 49 49]



    第二次交换后 [27 38 49 97 76 13 65 49]



    J向左扫描,位置不变,第三次交换后 [27 38 13 97 76 49 65 49]



    I向右扫描,位置不变,第四次交换后 [27 38 13 49 76 97 65 49]



    J向左扫描 [27 38 13 49 76 97 65 49]



    (一次划分过程)



    初始关键字 [49 38 65 97 76 13 27 49]



    一趟排序之后 [27 38 13] 49 [76 97 65 49]



    二趟排序之后 [13] 27 [38] 49 [49 65]76 [97]



    三趟排序之后 13 27 38 49 49 [65]76 97



    最后的排序结果 13 27 38 49 49 65 76 97



    各趟排序之后的状态




五、堆排序(Heap Sort)


1. 基本思想:


堆排序是一树形选择排序,在排序过程中,将R[1..N]看成是一颗完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系来选择最小的元素。


2. 堆的定义: N个元素的序列K1,K2,K3,...,Kn.称为堆,当且仅当该序列满足特性:


Ki≤K2i Ki ≤K2i+1(1≤ I≤ [N/2])



Unity3D教程:手游开发常用排序算法



堆实质上是满足如下性质的完全二叉树:树中任一非叶子结点的关键字均大于等于其孩子结点的关键字。例如序列10,15,56,25,30,70就是一个堆,它对应的完全二叉树如上图所示。这种堆中根结点(称为堆顶)的关键字最小,我们把它称为小根堆。反之,若完全二叉树中任一非叶子结点的关键字均大于等于其孩子的关键字,则称之为大根堆。


3. 排序过程:


堆排序正是利用小根堆(或大根堆)来选取当前无序区中关键字小(或最大)的记录实现排序的。我们不妨利用大根堆来排序。每一趟排序的基本操作是:将当前无序区调整为一个大根堆,选取关键字最大的堆顶记录,将它和无序区中的最后一个记录交换。这样,正好和直接选择排序相反,有序区是在原记录区的尾部形成并逐步向前扩大到整个记录区。


【示例】:对关键字序列42,13,91,23,24,16,05,88建堆




Unity3D教程:手游开发常用排序算法





Unity3D教程:手游开发常用排序算法






    六、几种排序算法的比较和选择



    1. 选取排序方法需要考虑的因素:



    (1) 待排序的元素数目n;



    (2) 元素本身信息量的大小;



    (3) 关键字的结构及其分布情况;



    (4) 语言工具的条件,辅助空间的大小等。



    2. 小结:



    (1) 若n较小(n <= 50),则可以采用直接插入排序或直接选择排序。由于直接插入排序所需的记录移动操作较直接选择排序多,因而当记录本身信息量较大时,用直接选择排序较好。



    (2) 若文件的初始状态已按关键字基本有序,则选用直接插入或冒泡排序为宜。



    (3) 若n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序、堆排序或归并排序。 快速排序是目前基于比较的内部排序法中被认为是最好的方法。



    (4) 在基于比较排序方法中,每次比较两个关键字的大小之后,仅仅出现两种可能的转移,因此可以用一棵二叉树来描述比较判定过程,由此可以证明:当文件的n个关键字随机分布时,任何借助于“比较”的排序算法,至少需要O(nlog2n)的时间。



    (5) 当记录本身信息量较大时,为避免耗费大量时间移动记录,可以用链表作为存储结构。


=================================相关代码================================


一、归并排序

public static void MergeSort(int[] arr, int start, int end) //递归
 {
 if (start < end)
 {
 int middle = (start + end) / 2;
 MergeSort(arr,start,middle);
 MergeSort(arr,middle+1,end);
 MergeSort(arr,start,middle,end);
 }
 }

 public static void MergeSort(int[] arr, int start, int middle, int end)
 {
 int[] tmp = new int[end - start + 1];
 int i = start;
 int j = middle + 1;
 int k = 0;

 while (i <= middle && j <= end)
 {
 if (arr[i] < arr[j])
 {
 tmp[k] = arr[i];
 k++;
 i++;
 }
 else
 {
 tmp[k] = arr[j];
 k++;
 j++;
 }
 }

 while (i <= middle)
 {
 tmp[k] = arr[i];
 k++;
 i++;
 }

 while (j <= end)
 {
 tmp[k] = arr[j];
 k++;
 j++;
 }

 for (k = 0, i = start; i <= end; k++, i++)
 arr[i] = tmp[k];

 }

 二、希尔排序

 public static void ShellSort(int[] arr)
 {
 int gap = arr.Length / 2;
 int tmp;
 while (gap > 0)
 {
 for (int i = 0; i < arr.Length; i++)
 {
 tmp = arr[i];
 int j;
 for(j = i-gap;j >= 0 ;j-=gap)
 {
 if (arr[j] > tmp)
 arr[j + gap] = arr[j];
 else 
 break;
 }
 arr[j + gap] = tmp;
 }
 gap /= 2;
 }
 }

 三、基数排序

 public static void RadixSort(int[] arr)
 {
 int MaxLength = 1; //最大数的位数
 int tmp,num,i,j;
 List<int>[] list = new List<int>[10];
 for (i = 0; i < 10;i++ )
 list[i] = new List<int>();
 for (i = 0; i < arr.Length; i++)
 {
 num = arr[i];
 tmp = 0;
 while (num != 0)
 {
 tmp++;
 num /= 10;
 }
 if (tmp > MaxLength)
 MaxLength = tmp;
 }

 for (i = 0; i < MaxLength; i++)
 {
 for (j = 0; j < arr.Length; j++)
 {
 num = arr[j];
 tmp = i;
 while (tmp > 0)
 {
 num /= 10;
 tmp--;
 }
 tmp = num % 10; //第i+1位上的数
 list[tmp].Add(arr[j]);

 }
 tmp = 0;
 for (j = 0; j < 10; j++)
 {
 foreach (int k in list[j])
 {
 arr[tmp] = k;
 tmp++;
 }
 list[j].Clear();
 }
 }
 }

 四、快速排序
 static int Partition(int[] arr, int low, int high)
 {
 //进行一趟快速排序,返回中心轴记录位置
 // arr[0] = arr[low];
 int pivot = arr[low];//把中心轴置于arr[0]
 while (low < high)
 {
 while (low < high && arr[high] >= pivot)
 {
 --high;
 }
 Swap(ref arr[high], ref arr[low]);//将比中心轴记录小的移到低端
 while (low < high && arr[low] <= pivot)
 {
 ++low;
 }
 Swap(ref arr[high], ref arr[low]);//将比中心轴记录大的移到高端

 }
 arr[low] = pivot; //中心轴移到正确位置
 return low; //返回中心轴位置
 }
 static void Swap(ref int i, ref int j)
 {
 int t;
 t = i;
 i = j;
 j = t;
 }
 static void QuickSort(int[] arr, int low, int high)
 {
 if (low < high )//当 arr[low,high]为空或只一个记录无需排序
 {
 int pivot = Partition(arr, low, high);
 QuickSort(arr, low, pivot - 1);
 QuickSort(arr, pivot + 1, high);

 }
 }

 五、堆排序

 public static void HeapSort(int[] arr)
 {

 BuildMaxHeap(arr);

 for (int i = (arr.Length - 1); i > 0; i--)
 {
 Swap(ref arr[0], ref arr[i]); //将堆顶元素和无序区的最后一个元素交换 
 MaxHeaping(arr, 0, i); //将新的无序区调整为堆 
 }
 }

 /// <summary> 
 /// 初始化大根堆,由底向上建堆 
 /// 完全二叉树的基本性质,最底层节点是n/2,所以从arr.Length/2开始 
 /// </summary> 
 private static void BuildMaxHeap(int[] arr)
 {
 for (int i = (arr.Length / 2) - 1; i >= 0; i--)
 {
 MaxHeaping(arr, i, arr.Length);
 }
 }


 /// <summary> 
 /// 将指定的节点调整为堆 
 /// </summary> 
 /// <param name="i">需要调整的节点</param> 
 /// <param name="heapSize">堆的大小,也指数组中无序区的长度</param> 
 private static void MaxHeaping(int[] arr, int i, int heapSize)
 {
 int left = 2 * i + 1; // 左子节点 
 int right = 2 * i + 2; // 右子节点 
 int large = i; // 临时变量,存放大的节点值 

 // 比较左子节点 
 if (left < heapSize && arr[left] > arr[large])
 {
 large = left;
 }

 // 比较右子节点 
 if (right < heapSize && arr[right] > arr[large])
 {
 large = right;
 }

 // 如有子节点大于自身就交换,使大的元素上移 
 if (i != large)
 {
 Swap(ref arr[i], ref arr[large]);
 MaxHeaping(arr, large, heapSize);
 }
 }

 private static void Swap(ref int a, ref int b)
 {
 int tmp;
 tmp = a;
 a = b;
 b = tmp;
 }