一、简单排序
1、冒泡排序
(1)基本思想:在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数依次进行比较和调整,让较大的数往下沉,较小的往上冒。即:每当两相邻的数比较后发现它们的排序与排序要求相反时,就将它们互换。
(2)实例:
(3)Java实现
private static void bubbleSrot(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
for (int j = i + 1; j < arr.length; j++) {
if(arr[i] > arr[j]){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
}
2、选择排序
(1)基本思想:在要排序的一组数中,选出最小的一个数与第一个位置的数交换;
然后在剩下的数当中再找最小的与第二个位置的数交换,如此循环到倒数第二个数和最后一个数比较为止。
(2)实例:
(3)用java实现
private static void chooseSort(int[] arr) {
for (int i = 0; i < arr.length; i++) {
int least = i;
for (int j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[least]) {
least = j;
}
}
// 将当前第一个元素与它后面序列中的最小的一个 元素交换,也就是将最小的元素放在最前端
int temp = arr[i];
arr[i] = arr[least];
arr[least] = temp;
}
}
3、冒泡排序与选择排序的比较
(1)选择排序改进了冒泡排序,将必要的交换次数从O(N²)减少到O(N),不幸的是比较次数依然是O(N²)级。
然而,选择排序依然为大记录量的排序提出了一个非常重要的改进,因为这些大量的记录需要在内存中移动,这就使交换的时间和比较的时间相比起来,交换的时间更为重要。(一般来说,Java语言中不是这种情况,Java中只是改变了引用位置,而实际对象的位置并没有发生改变)
(2)选择排序的效率:选择排序和冒泡排序执行了相同次数的比较:N*(N-1)/2。对于10个数据项,需要45次比较,然而,10个数据项只需要少于10次的交换。对于100个数据项,需要4950次比较,但只进行不到100次交换。N值很大时,比较的次数是主要的,所以结论是选择排序和冒泡哦排序一样运行了O(N²)时间。但是,选择排序无疑更快,因为它进行的交换少得多。
4、插入排序
(1)基本思想:在要排序的一组数中,假设前面(n-1) [n>=2] 个数已经是排
好顺序的,现在要把第n个数插到前面的有序数中,使得这n个数
也是排好顺序的。如此反复循环,直到全部排好顺序。
(2)实例
(3)用java实现
private static void insertionSort(int[] arr){
int in,out;
for(out = 1 ; out < arr.length ; out ++){
int temp = arr[out];
in = out;
while(in > 0 && arr[in-1] >= temp){
arr[in] = arr[in - 1];
--in;
}
arr[in] = temp;
}
}
算法解释:在外层的for循环中,out变量从1开始,向右移动。它标记了未排序部分的最左端数据。而在内层的while循环中,in变量从out变量开始,向左移动,直到temp变量小于in所指的数组数据项,或者它已经不能再向左移动为止。while循环的每一趟都向左移动了一个已排序的数据项。
插入排序的效率:这个算法中,第一趟排序,最多比较一次,第二趟排序,最多比较两次,以此类推,最后一趟最多比较N-1次,因此有1+2+3+…+N-1 = N*(N-1)/2。然而,因为在每一趟排序发现插入点之前,平均只有全体数据项的一半真的进行了比较,所以除以2最后是N*(N-1)/4。
对于随机顺序的数据,插入排序也需要O(N²)的时间级。当数据基本有序,插入排序几乎只需要O(N)的时间,这对把一个基本有序的文件进行排序是一个简单而有效的方法。
对于逆序排列的数据,每次比较和移动都会执行,所以插入排序不比冒泡排序快。
二、归并排序
(1)基本排序:归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。
(2)实例:
(3)用java实现
//将两个已排序的数组合并到第三个数组上。
private static void merge(int[] arrA, int[] arrB, int[] arrC) {
int aDex = 0, bDex = 0, cDex = 0;
int sizeA = arrA.length;
int sizeB = arrB.length;
// A数组和B数组都不为空
while (aDex < sizeA && bDex < sizeB) {
if (arrA[aDex] < arrB[bDex]) {
arrC[cDex++] = arrA[aDex++];
} else {
arrC[cDex++] = arrB[bDex++];
}
}
//A数组不为空,B数组为空
while (aDex < sizeA) {
arrC[cDex++] = arrA[aDex++];
}
//A数组为空,B数组不为空
while (bDex < sizeB) {
arrC[cDex++] = arrB[bDex++];
}
}
于是,详细的完整实现如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
运行测试:
三、 高级排序
有2个高级的排序算法,希尔排序和快速排序。这两种排序算法都比简单排序算法快得多:希尔排序大约需要O(N*(logN)²)时间,快速排序需要O(N*logN)时间。这两种排序算法都和归并排序不同,不需要大量的辅助存储空间。希尔排序几乎和归并排序一样容易实现,而快速排序是所有通用排序算法中最快的一种排序算法。
还有一种基数排序,是一种不常用但很有趣的排序算法。
1、希尔排序(最小增量排序)-- 基于 插入排序
(1)基本思想:算法先将要排序的一组数按某个增量d(n/2,n为要排序数的个数)分成若干组,每组中记录的下标相差d.对每组中全部元素进行直接插入排序,然后再用一个较小的增量(d/2)对它进行分组,在每组中再进行直接插入排序。当增量减到1时,进行直接插入排序后,排序完成。
(2)实例:
(3)用java实现
private static void shellSort(int[] arr) {
int inner, outer;
int temp;
int h = 1;
int nElem = arr.length;
while (h <= nElem / 3) {
h = h * 3 + 1;
}
while (h > 0) {
for (outer = h; outer < nElem; outer++) {
temp = arr[outer];
inner = outer;
while (inner > h - 1 && arr[inner - h] >= temp) {
arr[inner] = arr[inner - h];
inner -= h;
}
arr[inner] = temp;
}
h = (h - 1) / 3;
}
}
(1)基本思想:选择一个基准元素,通常选择第一个元素或者最后一个元素,通过一趟扫描,将待排序列分成两部分,一部分比基准元素小,一部分大于等于基准元素,此时基准元素在其排好序后的正确位置,然后再用同样的方法递归地排序划分的两部分。
(2)实例:
(3)用java实现
//快速排序
private static void recQuickSort(int arr [] ,int left,int right){
if(right - left <= 0){
return;
}else{
int pivot = arr[right];//一般使用数组最右边的元素作为枢纽
int partition = partitionIt(arr, left, right, pivot);
recQuickSort(arr, left, partition-1);
recQuickSort(arr, partition+1, right);
}
}
//划分
private static int partitionIt(int[] arr ,int left,int right,int pivot){
int leftPtr = left - 1;
//int rightPtr = right + 1;
int rightPtr = right ; //使用最右边的元素作为枢纽,划分时就要将最右端的数据项排除在外
while(true){
while(arr[++leftPtr] < pivot);
while(rightPtr > 0 && arr[--rightPtr] > pivot);
if(leftPtr >= rightPtr){
break;
}else{
//交换leftPtr和rightPtr位置的元素
int temp = arr[leftPtr];
arr[leftPtr] = arr[rightPtr];
arr[rightPtr] = temp;
}
}
//交换leftPtr和right位置的元素
int temp = arr[leftPtr];
arr[leftPtr] = arr[right];
arr[right] = temp;
return leftPtr;//返回枢纽位置
}
最后测试,10万条随机数据,排序完成耗时18~25ms。希尔排序耗时差不多,而简单排序中的插入排序和选择排序耗时3500ms以上,冒泡排序最慢,超过17000ms以上才完成;归并排序比希尔排序和快速排序稍微慢点,在30ms左右。