会编程的羽流云
选择排序从入门到精通——Java实现
选择排序
算法实现原理
1、选取出 n 条记录中最小的记录与第一条记录进行交换 —— 循环的第一趟
2、选取出除第一条记录以外的 n-1 条记录中最小的记录与第二条记录进行交换 —— 循环的第二趟
3、以此类推直到整个数组全部遍历排序完成。
与冒泡排序的对比
选择排序可以看成冒泡排序的改进版本
冒泡排序实际上是将数据从右至左排序完成(从右至左、从大到小进行交换排序),而快速排序是将数据从左到右排序完成(从左至右、从小到大进行交换排序),虽然选择排序相对于冒泡排序将交换次数从$O(n^2)$减少到了$O(n)$,但是比较次数还是保持$O(n^2)$
算法复杂度
比较次数 $O(n^2)$——比较次数与关键字的初始状态无关,总的比较次数 $N=(n-1)+(n-2)+...+1=n(n-1)/2$。
交换次数 $O(n)$——最好情况是,已经有序,交换 0 次;最坏情况交换 n-1 次,逆序交换 $n/2$ 次。
算法稳定性
选择排序是不稳定,当数据为2,3,2,1,4,5
时就会出现第一次出现的2会被交换到第二次出现的2后面,这样就造成了排序并没有按照相同数据按照初始的顺序进行排序的要求。
然而冒泡排序却不会出现这样的问题,因为冒泡排序是相邻的两个元素进行比较交换,当数据相等时是不进行操作的所以没有影响相同数据的初始顺序,也就是说冒泡排序是稳定的。
算法Java实现
/**
* @Author zhuangyan
* @Description //TODO 选择排序
* @Date 10:43 2020/3/9
* @Param []
* @return void
**/
public static void SelectSort(){
int temp;//临时变量
//创建新的数组
int[] arr = new int[]{4,7,1,6,2,8,432,67,26,65,31765,765,5235,54756,643,52345,76};
//进行排序
for(int i = 0;i < arr.length-1;i++){
int min = i;//选出"最小"下标(数据的第一条)
for(int j = i+1;j < arr.length;j++){
//寻找相对最小值对应下标
if(arr[min]>arr[j]){
min = j;
}
}
//如果寻找到最小值,将最小值与当前位置交换
if(min != i){
temp = arr[min];
arr[min] = arr[i];
arr[i] = temp;
}
}
//输出
System.out.print("选择排序:");
for(int i : arr){
System.out.print(i);
System.out.print(" ");
}
System.out.println();
}
优化
对于选择排序的优化,其实是对选择排序的深入理解的一个过程,相信看到了这里大家都知道了选择排序在每一次从左到右的循环周期内都是确定了一个最小的元素放置在前面,相信这时候大家已经想到了,既然从左到右可以选出最小的,那一定可以选出最大的。是的,本次优化的出发点也是从这一思想开始,不多说上代码。
/**
* @Author zhuangyan
* @Description //TODO 选择排序优化
* @Date 10:43 2020/3/9
* @Param []
* @return void
**/
public static void OptimizeSelectSort(){
int temp;//临时变量
//创建新的数组
int[] arr = new int[]{4,7,1,6,2,8,432,67,26,65,31765,765,5235,54756,643,52345,76};
int left = 0;//数据项的左侧下标
int right = arr.length - 1;//数据项的右侧下标
//进行排序
while(left < right){
int min = left;//初始化最小标兵
int max = left;//初始化最大标兵
//无需与本身进行比较无意义所以 i 从 left+1 开始计算
for(int i = left + 1;i <= right;i++){
if(arr[min] > arr[i]){
min = i;
}
if(arr[max] < arr[i]){
max = i;
}
}
//选取出最小值与第一元素进行交换
if(min != left){
temp = arr[left];
arr[left] = arr[min];
arr[min] = temp;
}
//这一步很重要,最开始没有考虑到,导致测试用例不能100%
//这一步的意义其实很简单,假如最大值在最左测,此时程序走到这里时
//最大的值已经被上面的步骤替换到了原本最小值所对应的下标处
//如果继续使用left作为最大值下标就会导致最小值被交换到后面
if(max == left){
max = min;
}
if(max != right){
temp = arr[right];
arr[right] = arr[max];
arr[max] = temp;
}
//左侧指针右移(此处的指针是本人的类比,这样比较形象,其实是数组的下标)
left++;
//右侧指针左移(此处的指针是本人的类比,这样比较形象,其实是数组的下标)
right--;
}
//输出
System.out.print("选择排序优化:");
for(int i : arr){
System.out.print(i);
System.out.print(" ");
}
System.out.println();
}
具体代码详见github:https://github.com/zytwist/algorithm