10大排序算法之一:选择排序【不稳定】,一般不用选择排序的
提示:整个算法界,一共有十大排序算法,每一个算法都要熟悉,才算是算法入门
算法界的十大排序算法分别是:
选择排序、冒泡排序、插入排序、堆排序、希尔排序、归并排序、快速排序、桶排序、计数排序,基数排序。
如果你想进互联网大厂,至少这上面的其中最重要的8种,是必须熟练掌握的:
去掉希尔排序,希尔排序是一种改进的插入排序算法——所以只需要掌握插入排序
桶排序中最重要的就是计数排序和基数排序,都是桶的思想
根据算法复杂度低一点的,又稳定的
咱们可以最常用的算法实际上就四种:
插入排序(o(n^2))【当数据量小时,这个方法简单】【稳定】、
堆排序o(nlog(n))【不稳定】、
归并排序o(nlog(n))【稳定】,
快速排序o(nlog(n))(虽然快排不稳定,但是很多不需要稳定情况下,快排非常快)
因此,o(n)的桶排序很少用,除非面试官特别申明,否则都用比较排序。
归并排序是最常用的,复杂度低,而且稳定,达到了一个非常好的折中。
题目
今天先讲:选择排序
arr有N个数字,请你将其排序(升序);
一、选择排序的思想
首先,从0–N-1范围内选择最小的数,交换到0位置;
然后,从1–N-1范围内选择最小的数,交换到1位置;
然后,从2–N-1范围内选择最小的数,交换到2位置;
……
然后,从N-1–N-1范围内选择最小的数,交换到N-1位置;
啥意思?比如:arr=1 3 2 6 5 4
i从0–N-1遍历arr:
每一次从j=i–N-1找min的位置k
如果min比i小,交换i和k位置的数,让min去最左边
直到最后,每一次都把最小的值放到最左边,自然,就排序好了
看代码:
//复习选择排序
public static void chooseSortReview(int[] arr){
if (arr == null || arr.length < 2) return;
int N = arr.length;
//从i遍历
//枚举j从i--N-1范围,找min,有就交换i位置和min
for (int i = 0; i < N; i++) {
//令目前i位置就是min
int minPos = i;
for (int j = i; j < N; j++) {
//每次找是否还有更小的位置:
if (arr[j] < arr[i]) minPos = j;//更新
}
if (i != minPos) swap(arr, i, minPos);//确实有比i更小的,交换它的位置
//否则不管
}
}
//常用的交换数组函数
public static void swap(int[] arr, int i, int j){
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
显然,时间复杂度是o(n^2),
为啥呢,i遍历一遍N个元素,每个元素下的j要遍历N下
那你说为啥最后一趟,j只遍历了1下,你凭啥算N下呢?
注意,记住,我们说时间复杂度,要算最坏的情况!!!,所以,第一趟就要遍历N*N,即o(n^2)
如果遇到2个一样的数,可能位置会被调开
下面处在最前面的5,被选择排序,调到最后去了,5 5 5 的相对顺序被改变,则就是不稳定!!!!
如果相同的数字在同一个数组中,排序到最后他们的相对位置没变,那就是稳定的
如果下面这样就算是稳定的:
也就是黑5,绿5,红5,相对的位置,排序之后,仍然是黑5,绿5,红5,没变的话,就是稳定的排序算法。
因此,十大排序算法之1:选择排序,时间复杂度o(n^2),不稳定,
这是最差劲的排序算法,系统中不会用的。做个了解即可。
你怎么判断你写得算法代码对,还是不对呢?
左神教我们用对数器验证
所谓对数器,就是写一个绝对正确的代码:要么是系统的自带函数,那是必须绝对正确的,要么暴力解,暴力解就能保证对
也就是说,方法一是绝对对的
你的方法是优化方法2
每次操作,输出一组结果或者一个结果,你随机生成一些数据,拿绝对正确的方法1和你的方法2的结果来对比,有错就说明你的方法不对
如果验证了很多随机输入,输出都没有差异,那必然你的方法是对的
这就是对数器!!!
对于本题:对数器非常好写,显然就系统函数排序,天然绝对正确啊!
Arrays.sort(arr);
怎么造随机数组:
Math函数中
Math.random()可以生成0–1的随机数
(int)(maxValue * Math.random());//0-N-1的随机数,×最大的maxValve
不妨设maxValue =10,那(int)10*Math.random()就是0–9的随机数
这个就很简单了
对数器如下:
//对数器之构建随机数组
public static int[] createArray(int arrSize, int maxValue){
int[] arr = new int[arrSize];
for (int i = 0; i < arrSize; i++) {
arr[i] = (int)(maxValue * Math.random());//0-N-1的随机数,×最大的maxValve
}
return arr;
}
public static void checker(){
//生成检验数组
int[] arr = createArray(10000,10000);
int[] arr2 = new int[arr.length];//赋值同样一个数组arr
for (int i = 0; i < arr.length; i++) {
arr2[i] = arr[i];//copy即可
}
int[] arr3 = new int[arr.length];//赋值同样一个数组arr
for (int i = 0; i < arr.length; i++) {
arr3[i] = arr[i];//copy即可
}
//绝对的正确方法——暴力方法,或系统函数,操作arr
//对数器,校验,不对报错
Arrays.sort(arr);
//优化方法,操作arr2
chooseSort(arr2);
//复习,我写的选择排序代码,操作arr3
chooseSort(arr3);
//然后两个数组对位校验
boolean isSame = true;
for (int i = 0; i < arr.length; i++) {
if(arr[i] != arr2[i]) {
isSame = false;
break;//只要有一个不等,不必继续判断了
}
}
System.out.println(isSame == false ? "oops,wrong!" : "right!");
//然后两个数组对位校验
boolean isSame2 = true;
for (int i = 0; i < arr.length; i++) {
if(arr[i] != arr2[i]) {
isSame2 = false;
break;//只要有一个不等,不必继续判断了
}
}
System.out.println(isSame2 == false ? "oops,wrong!" : "right!");
}
测试代码:
//psvm固然可以直接生成main函数,快捷,但是刷题的界面有没有很难说,
// 我看看LeetCode,它只已经给定了函数了,只需要写算法,不过真实的企业面试可能没有这么便捷
//所以建议今后还是一五一十的手敲代码
public static void main(String[] args){
//但凡遇到一个算法,一定先写对数器,再写优化算法,这样可保证算法的可靠性。
checker();
}
总结
提示:重要经验:
1)十大排序算法,其8必须掌握,他们的时间复杂度分为为o(n^2),o(nlog(n)),o(n+k)=o(n)
2)今天将的选择排序,就是选择最小值放在最左边。了解了核心思想,写代码不是问题。
3)你会再牛的算法也没关系,但是你要是疏忽了最基础的这些算法,恐怕你就麻烦了,根基不牢地动山摇!!!!