数组排序算法
前几天浏览网页,看到过一个帖子,问:
有哪些算法惊艳到了你?
下面有100多的回答,浏览了一些,有的是根本没听过,涉及到了多个领域的优秀算法,其中有一个回答是快排,而且还有很生动的动图演示。
后来做算法题时,就遇到了数组排序的问题,再去那网页找那个快排时,就没再看到那个动图TOT,可能是太多回答我没找太细,这里留一下网址:https://www.zhihu.com/question/26934313。
没找到后悔不已啊,只能自己百度去找相关详细的介绍了。
下面记录一下数组排序的几种方法:
1、最简单方便使用的就是 Arrays 自带的 Arrays.sort(array);这里不过多描述。
import java.util.Arrays; public class SortTest { public static void main (String[] args) { int[] arr = new int[10]; for(int i = 0; i < arr.length; i ++) { arr[i] = (int)(Math.random()*100); } Arrays.sort(arr); System.out.println(Arrays.toString(arr)); } }
2、冒泡排序
这个想必大家学语言的都学习过,当初脑子笨,逻辑不好,还记了好久才读懂原理,还好现在回想起来还是很简单的,不知道什么时候脑袋就开窍了,哈哈。
它的原理嘛,就是遍历数组,然后比较临近两个数的值的大小,最后将最小或者最大的数置换到最左边或者最右边。
下面来记录方法:
public void bubbleSort () { for(int i = 0; i < numArr.length; i ++) { for(int j = 0; j < numArr.length - 1 - i; j ++) { if(numArr[j] > numArr[j + 1]) { numArr[j] = numArr[j] + numArr[j + 1]; numArr[j + 1] = numArr[j] - numArr[j + 1]; numArr[j] = numArr[j] - numArr[j + 1]; } } } }
外层循环控制比较次数,内层从 0 下标开始循环,然后进行前后两个数值的比较,这里注意,内层循环到数组长度 -i 处下标即可。拿方法中的第一次循环来说:0 下标会与 1 下标元素比较大小,较大的数会被放到 1 下标位置,然后下标 1 与下标 2 比较,较大数再存储到 下标 2 中。这样内层循环到最后,最后一个元素存储的值必定是大于等于其他元素值的存在。外层第二次循环时,内层就不需要全部比对一遍了,因为最后一个值已经是最大的了,只要比较剩余的元素值即可,所以内部循环此数做 -i 处理。越到后面比起内层全部循环节省的循环此数越多哦~
或许也可以变成这样,直接使用 i 进行控制内层循环的次数,数组有多少个就会两两间比较 长度-1 次,每次内层循环出来又会减少一次,嗯,简化成这样可以了:
public void bubbleSort () { for(int i = numArr.length - 1; i > 0; i --) { for(int j = 0; j < i; j ++) { if(numArr[j] > numArr[j + 1]) { numArr[j] = numArr[j] + numArr[j + 1]; numArr[j + 1] = numArr[j] - numArr[j + 1]; numArr[j] = numArr[j] - numArr[j + 1]; } } } }
3、选择排序
这种排序也是遍历寻找最小或者最大的值,然后单提出来存储到一个最小或最大值变量中,做排序题时,忘了诸多排序方法,只能自己想出一个最笨的方法,结果居然也有名字:选择排序。
public void selectSort (int[] numArr) { int minSub; boolean isChange; for(int i = 0; i < numArr.length; i ++) { isChange = false; minSub = i; for(int j = i + 1;j < numArr.length; j ++) { if(numArr[minSub] > numArr[j]) { isChange = true; minSub = j; } } if(isChange) { numArr[minSub] = numArr[i] + numArr[minSub]; numArr[i] = numArr[minSub] - numArr[i]; numArr[minSub] = numArr[minSub] - numArr[i]; } } }
这个排序的思路是最简单易懂的:感觉有点类似冒泡的感觉,只不过多一个变量来进行存储最大或最小的值。比如当前方法:将 0 下标元素值当成最小值进行下标存储,然后遍历其后元素,寻找小于当前存储的变量值的元素,如果有,则对其下标进行存储,直到内层循环完成后,将存储的最小值下标与当前 0 下标值互换。最终得到升序排序的数组。
4、插入排序
简单理解,就是将一个数据插入到已经排好序的有序数组中,从而得到一个新的,个数 +1 的数组。核心思想就是将数组分成两部分,一部分包含了数组所有元素,将最后一个元素除外;第二部分就只有一个元素。而后第一部分排序完成后,再将最后元素插入到已排好序的第一部分中。
public void insertSort () { for(int i = 1; i < numArr.length; i ++) { for(int j = i; j > 0; j --) { if(numArr[j - 1] > numArr[j]) { numArr[j - 1] = numArr[j - 1] + numArr[j]; numArr[j] = numArr[j - 1] - numArr[j]; numArr[j - 1] = numArr[j - 1] - numArr[j]; } } } }
首先将数组分成两个部分,一部分有序,另一部分无序。看方法:首次循环,第一部分其实是没有的,直到找到最小值压入 0 下标位置,第一部分才形成,之后每次循环都将当次循环的下标值当成待排数,而后通过遍历已经排好的数组,判断该值是否大于前数,如果大于就前移一位,如果不大于,则不动,放置有序数组的最后一位。
这个也还好理解吧?
5、快速排序
据说是效率最快的,不过不如其它排序方法稳定,有可能相同值会有多次相对位置变动,因为它的大致实现是:
选择一个标志值(通常为数组第一个元素),根据这个标志值同时从前后遍历查找大于或小于该值的数,将数组分成小于标志值和大于标志值两部分,经过一次快排后,左边为小于标志值元素,右边为大于标志值数,接着左右两边分别再进行快排,重复刚开始的步骤。最后直到变成有序数组。
public void quickSort (int firstSub, int lastSub) { int i = firstSub; int j = lastSub; int key = numArr[i]; while(i < j) { while(i < j && numArr[j] >= key) j--; if(i < j){ numArr[i] = numArr[i] + numArr[j]; numArr[j] = numArr[i] - numArr[j]; numArr[i] = numArr[i] - numArr[j]; i++; } while(i < j && numArr[i] <= key) i++; if(i < j){ numArr[j] = numArr[i] + numArr[j]; numArr[i] = numArr[j] - numArr[i]; numArr[j] = numArr[j] - numArr[i]; j--; } } if(i > firstSub)quickSort(firstSub , i - 1); if(j < lastSub)quickSort(i + 1, lastSub); }
这个有点复杂,得仔细理解一下~