本文章描述了7大排序算法内容与实现,使用语言为java,代码贴在最后,可根据需要快速定位。由于笔者水平有限,有错误之处请指出,不足之处请包涵。
1.七大算法基本内容
2.算法比较与选择
3.代码实现与结果
1.七大算法基本内容
七大排序算法:冒泡排序,选择排序,直接插入排序,希尔排序,快速排序,堆排序,归并排序。
由于升序和降序逻辑一样,这里只讨论升序。
1.1. 冒泡排序:
算法简述:将元素中最大的元素依次移动到最后。
算法详细:
-
从开始到结尾依次比较相邻的元素,如果第一个比第二个大,就交换他们两个。(一趟过后,最大的元素移动到了最后。)
-
将最后的元素排除在下面操作之外。(最后元素已经是最大,无需重复比较)
-
重复1,2,直到没有任何一对元素需要比较。
1.2. 选择排序:
算法简述:将待排序的元素中最大的元素选出放到最后。
算法详细:
-
将待排序元素中第一个元素视为最小值,依次与后面元素做比较,若被比较元素小,则更新最小值。(一趟过后,选出最小值)
-
比较最小值与待排序元素的第一个元素,若相同则跳过,若不同,则交换最小元素与待排序元素的第一元素位置。(前面的元素慢慢有序化)
-
重复1,2,直到没有待排序元素。
1.3. 直接插入排序:
算法简述:每次从无序表中取出第一个元素,把它插入到有序表的合适位置,使有序表仍然有序。
算法详细:
-
起初将第一个元素视为有续,将无序表中第一个数与有序表作比较,插入到合适位置。
-
重复1,直到无序表没有元素。
1.4. 希尔排序:
算法简述:希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
算法详细:
-
先取一个小于前一增量或小于n的整数作为增量,把文件的全部记录分组。所有距离为d1的倍数的记录放在同一个组中。
-
对每一组中元素进行直接插入排序。
-
重复1,2直到增量等于0为止。
1.5. 快速排序:
算法简述:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
算法详细:
-
设置两个变量 i、j,排序开始的时候:i=0,j=N-1;以第一个数组元素作为关键数据,赋值给key,即key=A[0];
-
从 j 开始向前搜索,即由后开始向前搜索( j--),找到第一个小于key的值A[ j ],将A[ j ]和A[ i ]互换;
-
从 i 开始向后搜索,即由前开始向后搜索( i++),找到第一个大于key的A[ i ],将A[ i ]和A[ j ]互换;
-
重复2,3直到 i = j 为止。
-
将数组以 i 分为两部分,递归执行1,2,3,4,5.直到数组不能再分为止。
1.6. 堆排序:
算法简述:堆排序(Heapsort)是指利用堆积树(每个节点的值不大于其根节点的完全二叉树)这种数据结构所设计的一种排序,它是选择排序的一种,当数据很多时极大的减少了比较次数;数据量少时不适用。
算法详细:
-
建立大根堆。
-
将顶端元素与无序区最后元素交换,然后调整无序区堆为大根堆(调整堆)。
-
重复2,直到无序区元素为空。
建立大根堆算法:
-
从数组的 length / 2 处节点调整堆,依次到第一个节点。
调整堆算法:
-
将节点与其左右孩子比较,选出最大值。
-
若最大值为自己则结束,最大值为其孩子,则节点与孩子交换,并对其孩子进行1,2.直到结束。
1.7. 归并排序:
算法简述:将序列分为若干序列,对序列排序,然后将已有序序列两两合并并调整,直到没有序列可合并,得到完全有序的序列。
算法详细:
-
将序列依次中分为两序列,直到不能再分为止。
-
申请空间,使其大小为两个已排序序列之和,该空间用来存放合并后的序列。
-
将两序列中最小者依次放入1所申请空间类。
-
重复2,3,直到所有序列合并完成。
-
(注意)分割与合并是相对的,即分割成的两序列会排序后会合并在一起。
2.算法比较与选择
2.1. 算法比较
2.2. 选择
笔者测试数据容量范围为:10-100000,结论如下:
-
数据量较小(笔者的程序n<80)时,直接插入排序最佳(稳定且快速)。
-
通常情况下(100<n<100000),快速排序最优。
-
数据量超大时(至少n>100000吧),选择堆排序,归并排序。(其它网友总结)
以下是其它网友关于排序的测试结果与选择总结。
http://www.360doc.com/content/14/0813/19/16948208_401618179.shtml
3. 代码实现与结果
由于笔者偷懒,把所有排序算法写到一个文件中了,后面测试时发现使用策略模式代码更清晰直观,有兴趣的网友可以试试。
3.1. 排序代码:
public class SortBase2 {
/**
* 冒泡排序.
* @param nums ( 需要排序的数组)
* @return
*/
public void bubbleSort(int[] nums){
int temp;
for (int i = 0; i < nums.length - 1; i++){
for (int j = 0; j < nums.length - 1 - i; j++){
if(nums[j] > nums[j+1]){
temp = nums[j];
nums[j] = nums[j+1];
nums[j+1] = temp;
}
}
}
}
/**
* 选择排序.
* ( 每一次从待排序的数据元素中选出最小(或最大)的一个元素,
* 存放在序列的起始位置,直到全部待排序的数据元素排完. )
* @param nums
*/
public void selectSort(int[] nums){
int temp;
int minIndex;
for (int i = 0; i < nums.length; i++){
minIndex = i;
for(int j = i + 1; j < nums.length; j++ ){
if(nums[minIndex] > nums[j]){
minIndex = j;
}
}
if(minIndex != i){
temp = nums[i];
nums[i] = nums[minIndex];
nums[minIndex] = temp;
}
}
}
/**
* 直接插入排序.
* 描述:每次从无序表中取出第一个元素,把它插入到有序表的合适位置,使有序表仍然有序。
* @param nums ( 需要排序的数组)
*/
public void insertSort(int[] nums){
int temp;
for (int i = 1; i < nums.length; i++){
for (int j = 0; j < i; j++){
if(nums[i] < nums[j]){
temp = nums[i];
for(int k = i; k > j; k--){
nums[k] = nums[k-1];
}
nums[j] = temp;
break;
}
}
}
}
/**
* 直接插入排序2.
* 改进:从后面开始比较,并同时后移。较少了比较次数。
* @param nums
*/
public void insertSort2(int[] nums){
int temp;
for (int i = 0; i < nums.length; i++){
for (int j = i - 1; j >= 0; j--){
if (nums[j+1] < nums[j]){
temp = nums[j];
nums[j] = nums[j+1];
nums[j+1] = temp;
} else {
break;
}
}
}
}
/**
* 直接插入排序3.
* 改进:从后面开始比较,并同时后移,和2相比减少交换次数。
* @param nums
*/
public void insertSort3(int[] nums){
int temp;
for (int i = 1; i < nums.length; i++){
if(nums[i] < nums[i-1]){
temp = nums[i];
int j = i - 1;
for (; j >= 0 && temp < nums[j]; j--){
nums[j+1] = nums[j];
}
nums[j+1] = temp;
}
}
}
/**
* 希尔排序.
* @param nums ( 需要排序的数组)
* @return
*/
public void shellSort(int[] nums){
int temp;
int increment = nums.length;
while (increment > 1){
increment /= 2;
for (int i = increment; i < nums.length; i++){
if (nums[i] < nums[i - increment]){
temp = nums[i];
int j = i - increment;
for(; j >= 0 && nums[j] > temp; j -= increment){
nums[j + increment] = nums[j];
}
nums[j + increment] = temp;
}
}
}
}
/**
* 快速排序.
* @param nums
* @param low
* @param high
*/
public void quicklySort(int[] nums) {
int low = 0;
int high = nums.length - 1;
quicklySort(nums, low, high);
}
public void quicklySort(int[] nums, int low, int high) {
int mid;
if (low < high) {
mid = partition(nums, low, high);
quicklySort(nums, low, mid - 1);
quicklySort(nums, mid + 1, high);
}
}
// 快排辅助程序
private int partition(int[] nums, int low, int high) {
int key = nums[low];
while(low < high){
while(low < high && nums[high] >= key){
high--;
}
if (low < high){
nums[low] = nums[high];
}
while(low < high && nums[low] <= key){
low++;
}
if (low < high){
nums[high] = nums[low];
}
}
nums[low] = key;
return low;
}
/**
* 快速排序2.
* @param nums
* @param low
* @param high
*/
public void quicklySort2(int[] nums) {
int low = 0;
int high = nums.length - 1;
quicklySort2(nums, low, high);
}
public void quicklySort2(int[] nums, int low, int high) {
int mid;
if(low < high){
mid = partition2(nums, low, high);
quicklySort2(nums, low, mid - 1);
quicklySort2(nums, mid + 1, high);
}
}
private int partition2(int[] nums, int low, int high){
int key = nums[low];
while(low < high){
while(low < high && key <= nums[high]){
high -- ;
}
nums[low] = nums[high]; // 差别之处,虽然可能会出现nums[10]=nums[10]这样没有意义的赋值,但减少if的比较与判断时间。
while(low < high && key >= nums[low]){
low ++ ;
}
nums[high] = nums[low]; // 差别之处
}
nums[low] = key;
return low;
}
/**
* 堆排序.
* @param nums
* @return
*/
public void heapSort(int[] nums){
int temp;
// 构建堆
for (int i = nums.length / 2; i >= 0 ; i--){
heapAdjust(nums, nums.length, i);
}
// 最大的放在后面并重构堆
for(int i = nums.length - 1; i > 0; i--){
temp = nums[0];
nums[0] = nums[i];
nums[i] = temp;
heapAdjust(nums, i, 0);
}
}
/**
* 调整堆.
* @param nums
* @param size
* @param index
*/
private void heapAdjust(int[] nums, int size, int index){
int left = (index << 1) + 1;
int right = (index << 1) + 2;
int largest = index;
if(left < size && nums[left] > nums[largest]){
largest = left;
}
if(right < size && nums[right] > nums[largest]){
largest = right;
}
if(largest != index){
int temp = nums[index];
nums[index] = nums[largest];
nums[largest] = temp;
heapAdjust(nums, size, largest);
}
}
/**
* 并归排序
* @param nums
* @return
*/
public void mergingSort(int[] nums){
int tail = nums.length - 1;
sortSplit(nums, 0, tail);
}
/**
* 分割数组.
* @param nums
* @param head
* @param tail
*/
public void sortSplit(int[] nums, int head, int tail){
if(head >= tail){
return;
}
int mid = (head + tail) / 2;
sortSplit(nums, head, mid);
sortSplit(nums, mid + 1, tail);
merging(nums, head, mid + 1, tail);
}
/**
* 合并数组
* @param nums
* @param start1 第一个有序表的起始下标
* @param start2 第二个有序表的起始下标
* @param tail 第二个有序表的结束小标
* @return
*/
public void merging(int[] nums, int s1, int s2, int tail){
if(s1 >= tail){
return;
}
int[] tempArray = new int[tail - s1 + 1];
int index1 = s1;
int index2 = s2;
int i = 0;
while(index1 < s2 && index2 <= tail){
if(nums[index1] < nums[index2]){
tempArray[i] = nums[index1++];
}else{
tempArray[i] = nums[index2++];
}
i++;
}
// 剩下的补齐
while(index1 < s2){
tempArray[i++] = nums[index1++];
}
while(index2 <= tail){
tempArray[i++] = nums[index2++];
}
// 放入原数组
for(int j = 0; j < tempArray.length; j++){
nums[s1++] = tempArray[j];
}
}
}
3.2. 测试辅助代码
public class ProduceArray {
/**
* 产生0-999的随机数组.
* @param amount (数组容量)
* @return
*/
public int[] produceArray(int amount){
int[] array = new int[amount] ;
for (int i = 0; i < amount; i++){
array[i] = (int) (Math.random()*1000);
}
return array;
}
/**
* 打印数组。
* @param array
*/
public void printArray(int[] array){
for (int i:array){
System.out.print(i + " ");
}
System.out.println();
}
}
3.3. 测试程序代码:
import java.util.Arrays;
public class Test2 {
int times = 10; // 测试次数
int count = 10000; // 数组容量
/**
* @param args
*/
public static void main(String[] args) {
System.out.println("start");
Test2 mytest = new Test2();
ProduceArray pa = new ProduceArray();
int[][] array = new int[mytest.times][];
for(int i = 0; i < array.length; i++){
array[i] = pa.produceArray(mytest.count); // 生成随机数组。
// pa.printArray(array[i]);
}
mytest.bubbleSortTest(array); // 冒泡排序
mytest.selectSortTest(array); // 选择排序
mytest.insertSortTest(array); // 插入排序
mytest.insertSortTest2(array);
mytest.insertSortTest3(array);
mytest.shellSortTest(array);
mytest.quicklySort(array);
mytest.quicklySort2(array);
mytest.heapSort(array);
mytest.mergingSort(array);
}
public void bubbleSortTest(int[][] a){
ProduceArray pa = new ProduceArray();
SortBase mybbSort = new SortBase();
int[][] array = Arrays.copyOf(a, a.length); // 复制a的二维结构
int[] arrayTemp; // 零时数组
long start; // 记录排序运行开始时间
long end; // 记录排序运行结束时间
long[] times = new long[a.length]; // 记录排序运行时间
long time = 0; // 第time次测试
for(int i = 0; i < a.length; i++){
arrayTemp = Arrays.copyOf(array[i], array[i].length);
start = System.nanoTime(); // 纳秒级时间,基本能够满足测试精度需要
mybbSort.bubbleSort(arrayTemp); // 调用排序
end = System.nanoTime();
times[i] = end - start;
array[i] = arrayTemp;
}
for(long t : times){
time += t;
}
time /= times.length; // 算出平均运行时间
System.out.println("冒泡排序结果:");
if(array[0].length < 20){ // 数组容量小于20时打印数组,大于20打印平均运行时间。
for(int[] temp : array){
pa.printArray(temp);
}
}else{
System.out.println("平均运行时间: " + (time));
}
}
// 与前面冒泡排序的测试相同,只是调用的排序方法不同,更清晰直观的写法是使用策略模式来写,这里笔者先写的排序算法文件,所以偷懒了,下同。
public void selectSortTest(int[][] a){
ProduceArray pa = new ProduceArray();
SortBase mybbSort = new SortBase();
int[][] array = Arrays.copyOf(a, a.length);
int[] arrayTemp;
long start;
long end;
long[] times = new long[a.length];
long time = 0;
for(int i = 0; i < a.length; i++){
arrayTemp = Arrays.copyOf(array[i], array[i].length);
start = System.nanoTime();
mybbSort.selectSort(arrayTemp); // 这里是不同
end = System.nanoTime();
times[i] = end - start;
array[i] = arrayTemp;
}
for(long t : times){
time += t;
}
time /= times.length;
System.out.println("选择排序结果:");
if(array[0].length < 20){
for(int[] temp : array){
pa.printArray(temp);
}
}else{
System.out.println("平均运行时间: " + (time));
}
}
public void insertSortTest(int[][] a){
ProduceArray pa = new ProduceArray();
SortBase mybbSort = new SortBase();
int[][] array = Arrays.copyOf(a, a.length);
int[] arrayTemp;
long start;
long end;
long[] times = new long[a.length];
long time = 0;
for(int i = 0; i < a.length; i++){
arrayTemp = Arrays.copyOf(array[i], array[i].length);
start = System.nanoTime();
mybbSort.insertSort(arrayTemp);
end = System.nanoTime();
times[i] = end - start;
array[i] = arrayTemp;
}
for(long t : times){
time += t;
}
time /= times.length;
System.out.println("直接插入排序结果:");
if(array[0].length < 20){
for(int[] temp : array){
pa.printArray(temp);
}
}else{
System.out.println("平均运行时间: " + (time));
}
}
public void insertSortTest2(int[][] a){
ProduceArray pa = new ProduceArray();
SortBase mybbSort = new SortBase();
int[][] array = Arrays.copyOf(a, a.length);
int[] arrayTemp;
long start;
long end;
long[] times = new long[a.length];
long time = 0;
for(int i = 0; i < a.length; i++){
arrayTemp = Arrays.copyOf(array[i], array[i].length);
start = System.nanoTime();
mybbSort.insertSort2(arrayTemp);
end = System.nanoTime();
times[i] = end - start;
array[i] = arrayTemp;
}
for(long t : times){
time += t;
}
time /= times.length;
System.out.println("直接插入排序2结果:");
if(array[0].length < 20){
for(int[] temp : array){
pa.printArray(temp);
}
}else{
System.out.println("平均运行时间: " + (time));
}
}
public void insertSortTest3(int[][] a){
ProduceArray pa = new ProduceArray();
SortBase mybbSort = new SortBase();
int[][] array = Arrays.copyOf(a, a.length);
int[] arrayTemp;
long start;
long end;
long[] times = new long[a.length];
long time = 0;
for(int i = 0; i < a.length; i++){
arrayTemp = Arrays.copyOf(array[i], array[i].length);
start = System.nanoTime();
mybbSort.insertSort3(arrayTemp);
end = System.nanoTime();
times[i] = end - start;
array[i] = arrayTemp;
}
for(long t : times){
time += t;
}
time /= times.length;
System.out.println("直接插入排序3结果:");
if(array[0].length < 20){
for(int[] temp : array){
pa.printArray(temp);
}
}else{
System.out.println("平均运行时间: " + (time));
}
}
public void shellSortTest(int[][] a){
ProduceArray pa = new ProduceArray();
SortBase mybbSort = new SortBase();
int[][] array = Arrays.copyOf(a, a.length);
int[] arrayTemp;
long start;
long end;
long[] times = new long[a.length];
long time = 0;
for(int i = 0; i < a.length; i++){
arrayTemp = Arrays.copyOf(array[i], array[i].length);
start = System.nanoTime();
mybbSort.shellSort(arrayTemp);
end = System.nanoTime();
times[i] = end - start;
array[i] = arrayTemp;
}
for(long t : times){
time += t;
}
time /= times.length;
System.out.println("希尔排序结果:");
if(array[0].length < 20){
for(int[] temp : array){
pa.printArray(temp);
}
}else{
System.out.println("平均运行时间: " + (time));
}
}
public void quicklySort(int[][] a){
ProduceArray pa = new ProduceArray();
SortBase mybbSort = new SortBase();
int[][] array = Arrays.copyOf(a, a.length);
int[] arrayTemp;
long start;
long end;
long[] times = new long[a.length];
long time = 0;
for(int i = 0; i < a.length; i++){
arrayTemp = Arrays.copyOf(array[i], array[i].length);
start = System.nanoTime();
mybbSort.quicklySort(arrayTemp);
end = System.nanoTime();
times[i] = end - start;
array[i] = arrayTemp;
}
for(long t : times){
time += t;
}
time /= times.length;
System.out.println("快速排序结果:");
if(array[0].length < 20){
for(int[] temp : array){
pa.printArray(temp);
}
}else{
System.out.println("平均运行时间: " + (time));
}
}
public void quicklySort2(int[][] a){
ProduceArray pa = new ProduceArray();
SortBase mybbSort = new SortBase();
int[][] array = Arrays.copyOf(a, a.length);
int[] arrayTemp;
long start;
long end;
long[] times = new long[a.length];
long time = 0;
for(int i = 0; i < a.length; i++){
arrayTemp = Arrays.copyOf(array[i], array[i].length);
start = System.nanoTime();
mybbSort.quicklySort2(arrayTemp);
end = System.nanoTime();
times[i] = end - start;
array[i] = arrayTemp;
}
for(long t : times){
time += t;
}
time /= times.length;
System.out.println("快速排序2结果:");
if(array[0].length < 20){
for(int[] temp : array){
pa.printArray(temp);
}
}else{
System.out.println("平均运行时间: " + (time));
}
}
public void heapSort(int[][] a){
ProduceArray pa = new ProduceArray();
SortBase mybbSort = new SortBase();
int[][] array = Arrays.copyOf(a, a.length);
int[] arrayTemp;
long start;
long end;
long[] times = new long[a.length];
long time = 0;
for(int i = 0; i < a.length; i++){
arrayTemp = Arrays.copyOf(array[i], array[i].length);
start = System.nanoTime();
mybbSort.heapSort(arrayTemp);
end = System.nanoTime();
times[i] = end - start;
array[i] = arrayTemp;
}
for(long t : times){
time += t;
}
time /= times.length;
System.out.println("堆排序结果:");
if(array[0].length < 20){
for(int[] temp : array){
pa.printArray(temp);
}
}else{
System.out.println("平均运行时间: " + (time));
}
}
public void mergingSort(int[][] a){
ProduceArray pa = new ProduceArray();
SortBase mybbSort = new SortBase();
int[][] array = Arrays.copyOf(a, a.length);
int[] arrayTemp;
long start;
long end;
long[] times = new long[a.length];
long time = 0;
for(int i = 0; i < a.length; i++){
arrayTemp = Arrays.copyOf(array[i], array[i].length);
start = System.nanoTime();
mybbSort.mergingSort(arrayTemp);
end = System.nanoTime();
times[i] = end - start;
array[i] = arrayTemp;
}
for(long t : times){
time += t;
}
time /= times.length;
System.out.println("并归排序结果:");
if(array[0].length < 20){
for(int[] temp : array){
pa.printArray(temp);
}
}else{
System.out.println("平均运行时间: " + (time));
}
}
}
3.4. 测试结果:
测试次数:10
数组容量:10000
时间单位:纳秒
start
冒泡排序结果:
平均运行时间: 236966562
选择排序结果:
平均运行时间: 82126896
直接插入排序结果:
平均运行时间: 68611189
直接插入排序2结果:
平均运行时间: 59882902
直接插入排序3结果:
平均运行时间: 41153782
希尔排序结果:
平均运行时间: 1392813
快速排序结果:
平均运行时间: 969936
快速排序2结果:
平均运行时间: 849098
堆排序结果:
平均运行时间: 2131108
并归排序结果:
平均运行时间: 1727959
测试次数:10000
数组容量:80
start
冒泡排序结果:
平均运行时间: 15689
选择排序结果:
平均运行时间: 8874
直接插入排序结果:
平均运行时间: 6209
直接插入排序2结果:
平均运行时间: 4893
直接插入排序3结果:
平均运行时间: 3590
希尔排序结果:
平均运行时间: 5127
快速排序结果:
平均运行时间: 3776
快速排序2结果:
平均运行时间: 3737
堆排序结果:
平均运行时间: 7203
并归排序结果:
平均运行时间: 8613