博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[-算法篇-] 排序
阅读量:6252 次
发布时间:2019-06-22

本文共 9064 字,大约阅读时间需要 30 分钟。

本文测试数据:
private static int[] arr =  {8, 3, 5, 55, 7, 22, 32, 99};复制代码

一、冒泡排序

1.第一版:

循环28次----移动6次

private static void bubbleSort(int arr[]) {    int n = arr.length - 1;    int i, j, t;    for (i = 0; i < n; i++) {//遍历数组        for (j = i + 1; j <= n; j++) {//内层遍历,从i的下一个            if (arr[i] > arr[j]) {// 数组i元素大,则交换                t = arr[j];                arr[j] = arr[i];                arr[i] = t;            }        }    }}复制代码
  • i=0,第一趟
j 从 1 开始,arr[0]>arr[1],交换位置,保证大的数在后面,a[0]=3j +1 = 2 , 比较a[2]和a[0],arr[0]

  • i=1,第二趟
j 从 2 开始,arr[1]>arr[2],交换位置,保证大的数在后面,a[1]=5j +1 = 2 , 比较a[3]和a[1],arr[1]

然后同理,经过n趟,小的数移到了前面


2.相邻元素冒泡

循环28次----移动6次

private static void bubbleSort1(int[] arr) {    int n = arr.length - 1;    int i, j, t;    for (i = 0; i < n; i++) {        for (j = n; j > i; j--) {            if (arr[j - 1] > arr[j]) {// 相邻元素两两对比,如果前者大,交换位置                t = arr[j - 1];                arr[j - 1] = arr[j];                arr[j] = t;            }        }    }}复制代码
  • i=0,第一趟

相邻两个元素比较,小的往左走

  • i=1,第二趟

相邻两个元素比较,小的往左走


3.优化版冒泡

循环22次----移动6次

//		  平均T复杂度	稳定	  最好	最坏	 辅助空间//冒泡排序: O(n^2)	    YES      O(n)   O(n^2)  O(1)private static void bubbleSort2(int[] arr) {    int n = arr.length - 1;    int i, j, t;    boolean flag = true;    for (i = 0; i < n && flag; i++) {        //如果退出j循环时flag=false,说明本循环没有元素交换,说明已排序完成        flag = false;        for (j = n; j > i; j--) {            if (arr[j - 1] > arr[j]) {// 相邻元素两两对比,如果前者大,交换位置                t = arr[j - 1];                arr[j - 1] = arr[j];                arr[j] = t;                flag = true;            }        }    }}复制代码

二、选择排序

循环28次----移动6次

//		  平均T复杂度	稳定	  最好	 最坏	 辅助空间//选择排序: O(n^2)	    YES     O(n^2)   O(n^2)  O(1)public static void selectSort(int[] arr) {    int n = arr.length - 1;    int i, j, t, min;    for (i = 0; i < n; i++) {        min = i;        for (j = i + 1; j <= n; j++) {            if (arr[min] > arr[j]) {                min = j;            }        }        if (min != i) {//如果min和i相同,没必要交换            t = arr[i];//交换            arr[i] = arr[min];            arr[min] = t;        }    }}复制代码

在j循环中记录发现的最小值所在索引,如果min不是i,交换元素


三、插入排序

1.具体算法
//		  平均T复杂度	稳定	  最好	最坏	 辅助空间//插入排序: O(n^2)	    YES      O(n)   O(n^2)  O(1)public static int[] insertSort(int[] arr) {    int i, j, t;    int n = arr.length;    for (i = 1; i < n; i++) {//从i点开始遍历数组        t = arr[i];//使用temp变量记录i索引所在值        for (j = i; j > 0 && t < arr[j - 1]; j--) {//从i点向左遍历            arr[j] = arr[j - 1];//当遇到比temp大的数,就将前一个元素后推        }        arr[j] = t;//否则j位变成t    }    return arr;}复制代码

2.分析
  • 第一轮排序

i,j从索引1开始, t=3 , t与j前一个元素比较, 3<8 , j处值变成较大值 8 , j 向左指一位跳出j的for循环后,将j索引置为刚才保存的临时变量t=3,此时前两个元素排序完成。复制代码
  • 第二轮排序

i索引右移一格到2,t= 5 ,j 从索引2开始t与j前一个元素比较, 5 < 8 , j处值变成较大值 8 , j 向左指一位t与j前一个元素比较, 5 > 3 , 跳出j循环将j索引置为刚才保存的临时变量t=3,此时前3个元素排序完成。复制代码
  • 第三轮排序

i索引右移一格到3,t= 55 ,j 从索引3开始t与j前一个元素比较, 55 > 8 , 跳出j循环将j索引置为刚才保存的临时变量t=55,此时前4个元素排序完成。复制代码
  • 第四轮排序

i索引右移一格到4,t= 7 ,j 从索引4开始t与j前一个元素比较, 7 <55 , j处值变成较大值 55 , j 向左指一位 为3 t与j前一个元素比较, 7 < 8 , j处值变成较大值 8 ,  j 向左指一位 为2 t与j前一个元素比较, 7 > 5 , 跳出j循环 将j索引置为刚才保存的临时变量t=7,此时前5个元素排序完成。复制代码

插入排序第n轮排序后将前n+1个元素是顺序的


四、希尔排序

试了一下开篇中100W的随机整数,果然不愧为N*logN的算法, 0.365秒

//		  平均T复杂度	稳定	  最好		最坏	  辅助空间//希尔排序:O(nlogn)	  NO     O(n^1.3)	 O(n^2)   O(1)public static void shellSort(int[] arr) {    int i, j, t , gap;    int n = arr.length;    for (gap = n / 2; gap > 0; gap /= 2) {        for (i = gap; i < n; i++) {            t = arr[i];            for (j = i; j >= gap && t < arr[j - gap]; j -= gap) {                arr[j] = arr[j - gap];            }            arr[j] = t;        }    }}复制代码

希尔排序先将数据归整,最后gap=1时,相当于归整后的插入排序

开始gap取4,图中相同颜色进行比较,用的是插入排序交换的套路然后gap减半=2,图中相同颜色进行比较,用的是插入排序交换的套路然后gap减半=1,图中相同颜色进行比较,用的是插入排序交换的套路复制代码

五、堆排序

1.具体算法

试了一下开篇中100W的随机整数,果然不愧为N*logN的算法, 0.286秒

//		  平均T复杂度	稳定	  最好		最坏	  辅助空间//堆排序:O(nlogn)		NO	 O(nlogn)	O(nlogn)	O(1)private static void heapSort(int[] arr) {//左半的元素    for (int i = arr.length / 2 - 1; i >= 0; i--) {        perDownHeap(arr, i, arr.length);//形成堆    }    for (int i = arr.length - 1; i > 0; i--) {        swapRef(arr, 0, i);//交换首        perDownHeap(arr, 0, i);//维持堆形状    }}private static void swapRef(int[] arr, int i, int j) {    int t = arr[i];    arr[i] = arr[j];    arr[j] = t;}private static void perDownHeap(int[] arr, int i, int n) {    int child;    int t;    for (t = arr[i]; leftChild(i) < n; i = child) {        child = leftChild(i);        if (child != n - 1 && arr[child] < arr[child + 1]) {            child++;        }        if (t < arr[child]) {            arr[i] = arr[child];        } else {            break;        }    }    arr[i] = t;}复制代码

2.分析

第一次执行的是:perDownHeap(arr, 3, arr.length);private static void perDownHeap(int[] arr, int i, int n) {    int child;//0    int t;//55    for (t = arr[i]; leftChild(i) < n; i = child) {        child = leftChild(i);//7  arr[child]=99 即 55的左子是99        if (child != n - 1 && arr[child] < arr[child + 1]) {//此时条件不满足            child++;        }        if (t < arr[child]) {//走这里 55<99 条件满足            arr[i] = arr[child];//arr[3]=99        } else {            break;        }    }    arr[i] = t;//跳出循环, i = child ,arr[7]=t=55}第二次执行的是:perDownHeap(arr, 2, arr.length);private static void perDownHeap(int[] arr, int i, int n) {    int child;//0    int t;//5    for (t = arr[i]; leftChild(i) < n; i = child) {        child = leftChild(i);//5  arr[child]=22 即 5的左子是22 ,右子是arr[child + 1]=32        if (child != n - 1 && arr[child] < arr[child + 1]) {//此时条件满足            child++;//child = 6         }        if (t < arr[child]) {//走这里 5<32 条件满足            arr[i] = arr[child];//arr[2]=32        } else {            break;        }    }    arr[i] = t;//跳出循环, i = child ,arr[6]=t=5}>其他同上...绘图如下复制代码

接下来通过交换根节点和i,再对堆进行沉浮,形成小顶堆

for (int i = arr.length - 1; i > 0; i--) {    swapRef(arr, 0, i);    perDownHeap(arr, 0, i);}复制代码


六、归并排序

1.具体算法
private static void mergeSort(int[] arr) {    int[] temArr = new int[arr.length];//临时数组    mergeSort(arr, temArr, 0, arr.length - 1);}private static void mergeSort(int[] arr, int[] temArr, int left, int right) {    if (left < right) {        int center = (left + right) / 2;        mergeSort(arr, temArr, left, center);//左归并,使得左子序列有序        mergeSort(arr, temArr, center + 1, right);//右归并,使得右子序列有序        merge(arr, temArr, left, center + 1, right);//两个子序列合并    }}private static void merge(int[] arr, int[] temArr, int leftPos, int rightPos,    int leftEnd = rightPos - 1;//左起点比右终点大1    int tempPos = leftPos;//临时数组索引    int count = rightEnd - leftPos + 1;//此次合并的元素个数    while (leftPos <= leftEnd && rightPos <= rightEnd) {//说明到头了        boolean rightBig = arr[leftPos] <= arr[rightPos];//左边大?        //temArr[tempPos]取较小者,tempPos和较小者索引++        temArr[tempPos++] = rightBig ? arr[leftPos++] : arr[rightPos++];    }    while (leftPos <= leftEnd) {        temArr[tempPos++] = arr[leftPos++];    }    while (rightPos <= rightEnd) {        temArr[tempPos++] = arr[rightPos++];    }    for (int i = 0; i < count; i++, rightEnd--) {//将临时数组元素放到原数组        arr[rightEnd] = temArr[rightEnd];    }}复制代码

2.分析

|--- 第一次merge: merge(arr, temArr, 0, 1, 1)leftPos:0leftEnd:0arr[leftPos]:8rightPos:1rightEnd:1arr[rightPos]:3temArr[0] = arr[leftPos]和arr[rightPos]的较小者:arr[rightPos]=3temArr[1] = arr[leftPos++] = 8 将临时数组元素放到原数组|--- 第二次merge: merge(arr, temArr, 2, 3, 3)leftPos:2leftEnd: 2arr[leftPos]:5rightPos:3rightEnd:3arr[rightPos]:55temArr[0] = arr[leftPos]和arr[rightPos]的较小者 arr[leftPos]=5temArr[1] = arr[rightPos++] = 55将临时数组元素放到原数组|--- 第三次merge: merge(arr, temArr, 0, 2, 3)leftPos:0leftEnd: 1arr[leftPos]:3rightPos:2rightEnd:3arr[rightPos]:5见下图...复制代码
  • 第三次 merge


七、快速排序

1.具体算法
//		  平均T复杂度	稳定	 最好	最坏	  辅助空间//快速排序:O(nlogn)    NO     O(nlogn)	O(n^2)	O(n)~O(nlogn)public static void fastSort(int[] arr) {    fastSort(arr, 0, arr.length - 1);}public static void fastSort(int[] arr, int start, int end) {    int i, j, key, t;    if (start >= end) {        return;    }    i = start + 1;    j = end;    key = arr[start];//基准位    while (i < j) {//保证i比j大        //当j处值<=key,j向←--走 :说明从右找到到第一个大于key的数        while (key <= arr[j] && i < j) j--;        //当i处值>=key,i向--→走 :说明从右找到到第一个小于key的数        while (key >= arr[i] && i < j) i++;        if (i < j) {//交换            t = arr[j];            arr[j] = arr[i];            arr[i] = t;        }    }    arr[start] = arr[i];//交换基准位    arr[i] = key;    fastSort(arr, start, j - 1);//左半    fastSort(arr, j + 1, end);//右半}复制代码

分析:
  • Q1:第一趟sort

此时调用了: sort(arr, 0, 2);//对左半元素快速排序复制代码
  • Q1.1:Q1左半sort

  • Q1.1.1:Q1.1左半sort

  • Q1.1.2:Q1.1右半sort :一个元素不用排序,直接return

  • Q1.2:Q1右半sort:套路一样,就不分析了


小结:没有最好的排序,只有最适合的排序。
条目 平均T复杂度 稳定 最好 最坏 辅助空间
冒泡排序 O(n^2) YES O(n) O(n^2) O(1)
选择排序 O(n^2) NO O(n^2) O(n^2) O(1)
插入排序 O(n^2) YES O(n) O(n^2) O(1)
希尔排序 O(nlogn) NO O(n^1.3) O(n^2) O(1)
堆排序 O(nlogn) NO O(nlogn) O(nlogn) O(1)
归并排序 O(nlogn) YES O(nlogn) O(nlogn) O(n)
快速排序 O(nlogn) NO O(nlogn) O(n^2) O(n)~O(nlogn)
数据量较小时:冒泡排序,选择排序,插入排序就可以了数据量非常大时:最好用O(nlogn)复杂度的算法如果数据基本是有序的:插入排序数据随机性很大:快速排序需要要求稳定性且O(nlogn):归并排序是唯一的选择,代价是需要多消耗一倍空间如果不想出现最坏情况,也就是想一切在预期之内:堆排序是你不二的选择复制代码
  • 排序的稳定性:大小相同的元素在排序前后顺序有没有可能改变
设:Ki=Kj (1<=i<=n,1<=j<=n,i!=j)且在排序前ri先于rj(即i

下面用七张图记录一下:

ok,就先这样,以后有想到什么再补充

转载地址:http://ewbsa.baihongyu.com/

你可能感兴趣的文章
BFS --- 素数环
查看>>
for循环每次取出一个字符(不是字节)
查看>>
linux版本选择
查看>>
不写for也能选中checkbox!
查看>>
PCIE_DMA:xapp1052学习笔记
查看>>
css
查看>>
Java规则引擎及JSR-94[转]
查看>>
【c学习-13】
查看>>
给报表增加页眉
查看>>
Mysql配置参数说明
查看>>
python ----字符串基础练习题30道
查看>>
K 班1-7,alpha,beta 作业成绩汇总
查看>>
uva-10879-因数分解
查看>>
清空表且自增的id重新从0开始
查看>>
[杂记]如何在LaTeX里插入高亮代码
查看>>
「常微分方程」(阿諾爾德) Page 6 問題4 經過擴張相空間的每一點有且僅有一條積分曲線...
查看>>
同一个闭区间上有界变差函数的和与积都是有界变差函数
查看>>
java安全证书配置
查看>>
使用erlang 建立一个自动化的灌溉系统(1)准备工作
查看>>
python 调用aiohttp
查看>>