直接插入排序
- 算法思想:把要排序的数组分为了两个部分, 一部分是数组的全部元素(除去待插入的元素), 另一部分是待插入的元素; 先将第一部分排序完成, 然后再插入这个元素. 其中第一部分的排序也是通过再次拆分为两部分来进行的.
- 算法顺序:
- ①. 从第一个元素开始,该元素可以认为已经被排序
- ②. 取出下一个元素,在已经排序的元素序列中从后向前扫描
- ③. 如果该元素(已排序)大于新元素,将该元素移到下一位置
- ④. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
- ⑤. 将新元素插入到该位置后
- ⑥. 重复步骤②~⑤
- JAVA代码实现:
public class DirectInsertion {
public static void main(String[] args) {
int[] arr = {2,4,6,8,3,9,10,1};
sort(arr);
for (int i : arr) {
System.out.print(i+" ");
}
}
public static void sort(int[] arr) {
for(int i=0;i<arr.length;i++) {
int num = arr[i];
for(int j=i;j>=0;j--) {
if(j>0 && arr[j-1]>num) {
arr[j]=arr[j-1];
}else {
arr[j]=num;
break;
}
}
}
}
}
- 性能说明:由于直接插入排序每次只移动一个元素的位, 并不会改变值相同的元素之间的排序, 因此它是一种稳定排序
希尔排序
- 算法思想:希尔排序是先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。
- 算法顺序:
- ①. 选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;(一般初次取数组半长,之后每次再减半,直到增量为1)
- ②. 按增量序列个数k,对序列进行k 趟排序;
- ③. 每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
- JAVA代码实现:
public static void sort(int[] arr) {
int gap = 1, i, j, len = arr.length;
int temp;
while (gap < len / 3)
gap = gap * 3 + 1;
for (; gap > 0; gap /= 3) {
for (i = gap; i < len; i++) {
temp = arr[i];
for (j = i - gap; j >= 0 && arr[j] > temp; j -= gap)
arr[j + gap] = arr[j];
arr[j + gap] = temp;
}
}
}
- 性能说明:
简单选择排序
- 算法思想:在未排序序列中找到最小(大)元素,存放到未排序序列的起始位置。在所有的完全依靠交换去移动元素的排序方法中,选择排序属于非常好的一种。
- 算法顺序:
- ①. 从待排序序列中,找到关键字最小的元素;
- ②. 如果最小元素不是待排序序列的第一个元素,将其和第一个元素互换;
- ③. 从余下的 N - 1 个元素中,找出关键字最小的元素,重复①、②步,直到排序结束
- JAVA代码实现:
public static void sort(int[] arr) {
for(int i=0;i<arr.length;i++) {
int min = i;
for(int j=i+1;j<arr.length;j++) {
if(arr[j]<arr[min])
min = j;
}
int tmp = arr[min];
arr[min] = arr[i];
arr[i] = tmp;
}
}
- 性能说明:
堆排序
冒泡排序
- 算法思想:是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
- 算法顺序:
- ①. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
- ②. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
- ③. 针对所有的元素重复以上的步骤,除了最后一个。
- ④. 持续每次对越来越少的元素重复上面的步骤①~③,直到没有任何一对数字需要比较。
- JAVA代码实现:
public static void sort(int[] arr) {
for(int i=0;i<arr.length-1;i++) {
for(int j=0 ;j<arr.length-1;j++) {
if(arr[j]>arr[j+1]) {
int t = arr[j];
arr[j] = arr[j+1];
arr[j+1] = t;
}
}
}
}
- 性能说明:
快速排序
- 算法思想:首先选一个轴值(pivot,也有叫基准的),通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
- 算法顺序:
- ①. 从数列中挑出一个元素,称为”基准”(pivot)。
- ②. 重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(相同的数可以到任一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
- ③. 递归地(recursively)把小于基准值元素的子数列和大于基准值元素的子数列排序。
- JAVA代码实现:
private static void sort(int[] arr) {
quicksort(arr,0,arr.length-1);
}
private static void quicksort(int[] arr, int left, int right) {
int i,j,t,temp;
if(left>right)
return;
temp = arr[left];
i = left;
j = right;
while(i!=j) {
while(arr[j]>=temp&&i<j) {
j--;
}
while(arr[i]<=temp&&i<j) {
i++;
}
if(i<j) {
t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
for (int q : arr) {
System.out.print(q+" ");
}
System.out.println();
arr[left] = arr[i];
arr[i] = temp;
quicksort(arr, left, i-1);
quicksort(arr, i+1, right);
}
- 性能说明:
- 快速排序是通常被认为在同数量级(O(nlog2n))的排序方法中平均性能最好的。但若初始序列按关键码有序或基本有序时,快排序反而蜕化为冒泡排序。为改进之,通常以“三者取中法”来选取基准记录,即将排序区间的两个端点与中点三个记录关键码居中的调整为支点记录。快速排序是一个不稳定的排序方法。
归并排序
- 算法思想:归并排序算法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。
- 算法顺序:
- ①. 将序列每相邻两个数字进行归并操作,形成 floor(n/2)个序列,排序后每个序列包含两个元素;
- ②. 将上述序列再次归并,形成 floor(n/4)个序列,每个序列包含四个元素;
- ③. 重复步骤②,直到所有元素排序完毕。
- JAVA实现:
public static int[] mergeSort(int[] data,int low,int high){
int mid = (low+high)/2;
if(low<high){
mergeSort(data,low,mid);
mergeSort(data,mid+1,high);
merge(data,low,mid,high);
}
return data;
}
public static void merge(int[] data, int low, int mid, int high) {
int[] temp = new int[high-low+1];
int i = low;
int j = mid+1;
int k = 0;
while(i<=mid && j<=high){
if(data[i]<data[j]){
temp[k++] = data[i++];
}else{
temp[k++] = data[j++];
}
}
while(i<=mid){
temp[k++] = data[i++];
}
while(j<=high){
temp[k++] = data[j++];
}
for(int x=0;x<temp.length;x++){
data[x+low] = temp[x];
}
}
- 性能说明:
- 从效率上看,归并排序可算是排序算法中的”佼佼者”. 假设数组长度为n,那么拆分数组共需logn,, 又每步都是一个普通的合并子数组的过程, 时间复杂度为O(n), 故其综合时间复杂度为O(nlogn)。另一方面, 归并排序多次递归过程中拆分的子数组需要保存在内存空间, 其空间复杂度为O(n)。
基数排序
总结
- 当原表有序或基本有序时,直接插入排序和冒泡排序将大大减少比较次数和移动记录的次数,时间复杂度可降至O(n);
- 而快速排序则相反,当原表基本有序时,将蜕化为冒泡排序,时间复杂度提高为O(n2);
- 原表是否有序,对简单选择排序、堆排序、归并排序和基数排序的时间复杂度影响不大。