不积跬步,无以至千里;不积小流,无以成江海。
Java语言基础
排序算法
排序的分类:
内排序:待排序列完全存放在内存中进行的排序(这里介绍的都是内排序);
外排序:排序过程需要访问外存储器(数据太大)。
稳定与非稳定:
如果一个排序算法能够保留数组中重复元素的相对位置则可以被称为是 稳定 的。反之,则是 非稳定 的。
-
插入排序
直接插入排序
基本思想:
每次将一个待排序的元素,插入到前面已经排序好的序列中,直到全部元素插入结束。
过程动图:
代码实现:
public static void sort(int a[]) { for(int i = 0; i < a.length - 1; i++){ for(int j = i + 1; j > 0; j--){ if(a[j] < a[j - 1]){ int temp = a[j]; a[j] = a[j - 1]; a[j - 1] = temp; } } } }
希尔排序
基本思想:
将待排序数组按照步长gap进行分组,然后将每组的元素利用直接插入排序的方法进行排序;每次再将gap折半减小,循环上述操作;当gap=1时,利用直接插入,完成排序。
(插入排序在对几乎已经排好序的数据操作时,效率高)。
过程动图:
代码实现:
public static void xier(int a[]) { int length = a.length; int j = 0; for(int ins = length / 2; ins >= 1; ins /= 2){ for (int i = 0; i < length - ins ; i += ins) { for(j = i + ins; j > 0; j = j - ins){ if(a[j] < a[j - ins]){ int temp = a[j]; a[j] = a[j - ins]; a[j - ins] = temp; } } } } }
-
选择排序
简单选择排序
基本思想:
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
过程动图:
代码实现:
public static void select(int a[]) { for (int i = 0; i < a.length; i++) { int min = i; for(int j = i + 1; j < a.length; j++){ if(a[j] < a[min]){ min = j; } } if(min != i){ int temp = a[i]; a[i] = a[min]; a[min] = temp; } } }
堆排序
基本思想:
堆排序的过程就是将待排序的序列构造成一个堆,选出堆中最大的移走(大顶堆),再把剩余的元素调整成堆,找出最大的再移走,重复直至有序。
(使用大顶堆进行升序排序, 使用小顶堆进行降序排序)
(初始时把要排序的数的序列看作是一棵顺序存储的二叉树,调整它们的存储顺序,使之成为一个堆,这时堆的根节点的数最大。然后将根节点与堆的最后一个节点交换。然后对前面(n-1)个数重新调整使之成为堆。依此类推)
过程动图:
代码实现:
public static void heap(int a[]) { for(int i = a.length - 1; i > 0; i--){ maxheap(a, i); int temp = a[0]; a[0] = a[i]; a[i] = temp; } } public static void maxheap(int a[], int n) { int child; for(int i = (n - 1) / 2; i >= 0; i--){ //左子节点 child = i * 2 + 1; if(child != n && a[child] < a[child + 1]){ child++; } if(a[i] < a[child]){ int temp = a[i]; a[i] = a[child]; a[child] = temp; } } }
-
交换排序
冒泡排序
基本思想:
依次比较相邻的元素。如果第一个比第二个大,就交换它们两个,对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素会是最大的数,直至排序完成没有再需要交换。
过程动图:
代码实现:
public static void maopao(int a[]) { for (int i = 0; i < a.length - 1; i++) { for(int j = 0; j < a.length - i - 1; j++){ if(a[j] > a[j + 1]){ int temp = a[j + 1]; a[j + 1] = a[j]; a[j] = temp; } } } }
快速排序
基本思想:
快速排序的基本思想:挖坑填数+分治法。
快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。
(从数列中挑出一个元素,称为 “基准”(pivot);重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。)
过程动图:
代码实现:
public static void speedsort(int a[], int low, int high) { if(low >= high){ return; } int left = low; int right = high; int temp = a[left]; while(left < right){ while(left < right && a[right] >= temp){ right--; } a[left] = a[right]; while(left < right && a[left] <= temp){ left++; } a[right] = a[left]; } a[left] = temp; speedsort(a, low, left - 1); speedsort(a, left + 1, high); }
-
归并排序
基本思想:
归并排序算法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。
(归并排序的性能不受输入数据的影响,但表现比选择排序好的多,因为始终都是O(n log n)的时间复杂度。代价是需要额外的内存空间。)
过程动图:
代码实现:
public static void guibing(int a[]) { guibing(a, 0, a.length - 1); } public static void guibing(int a[], int low, int high) { if(low >= high){ return; } int mid = (low + high) / 2; guibing(a, low, mid); guibing(a, mid + 1, high); merge(a, low, mid, high); } public static void merge(int a[], int low, int mid, int high) { int i = low; int j = mid + 1; int[] temp = new int[a.length]; for(int k = low; k <= high; k++){ temp[k] = a[k]; } for(int k = low; k <= high; k++){ if(i > mid){ a[k] = temp[j++]; }else if(j > high){ a[k] = temp[i++]; }else if(temp[j] < temp[i]){ a[k] = temp[j]; j++; }else{ a[k] = temp[i]; i++; } } }
-
基数排序
基本思想:
所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。
MSD(Most significant digital) 从最左侧高位开始进行排序。MSD方式适用于位数多的序列。
LSD (Least significant digital)从最右侧低位开始进行排序。LSD方式适用于位数少的序列。
过程动图:
时间、空间复杂度总结: