【问题描述】
1.给定任意长度的乱序整型数组,使用选择排序算法将其按照从小到大的顺序进行排列即可。
2.在1的基础上优化选择排序算法,要求每次遍历同时确定最小元素和最大元素,达到减少遍历次数的目的。
3.分析选择排序的时间复杂度和空间复杂度。
4.解释说明选择排序的不稳定性。
【问题解决】
1.原始选择排序代码如下:
public class SelectionSortTest {
public static void main(String[] args) {
int[] arr= {5,3,6,8,1,7,9,9,4,2,1};
printIntArray(selectionSort(arr));
}
static int[] selectionSort(int[] arr) {
for (int i = 0; i < arr.length-1; i++) {
//定义最小元素的索引
int minIndex=i;
//查找最小元素索引
for (int j = i+1; j < arr.length; j++) {
minIndex=arr[j]<arr[minIndex]?j:minIndex;
}
//交换两个位置上的元素
swapIntArray(arr, i, minIndex);
System.out.print("第"+(i+1)+"次循环:");
printIntArray(arr);
}
return arr;
}
//打印整型数组
public static void printIntArray(int[] arr){
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]+" ");
}
System.out.println();
}
//交换整型数组指定位置的元素
public static void swapIntArray(int[] arr,int i,int j) {
int temp = arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
}
执行结果:
第1次循环:1 3 6 8 5 7 9 9 4 2 1
第2次循环:1 1 6 8 5 7 9 9 4 2 3
第3次循环:1 1 2 8 5 7 9 9 4 6 3
第4次循环:1 1 2 3 5 7 9 9 4 6 8
第5次循环:1 1 2 3 4 7 9 9 5 6 8
第6次循环:1 1 2 3 4 5 9 9 7 6 8
第7次循环:1 1 2 3 4 5 6 9 7 9 8
第8次循环:1 1 2 3 4 5 6 7 9 9 8
第9次循环:1 1 2 3 4 5 6 7 8 9 9
第10次循环:1 1 2 3 4 5 6 7 8 9 9
1 1 2 3 4 5 6 7 8 9 9
2.优化后的代码:
public class SelectionSortTest {
public static void main(String[] args) {
int[] arr= {5,3,6,8,1,7,9,9,4,2,1};
printIntArray(selectionSort(arr));
}
static int[] selectionSort(int[] arr) {
for (int i = 0; i <= arr.length-(1+i); i++) {
int maxlength = arr.length-(1+i);
//定义最小元素的索引
int minIndex=i;
//定义最大元素索引
int maxIndex=i;
//定位到最小值索引和最大值索引
for (int j = i+1; j <= maxlength; j++) {
minIndex=arr[j]<arr[minIndex]?j:minIndex;
maxIndex=arr[j]>arr[maxIndex]?j:maxIndex;
}
//交换指定位置上的元素
SelectionSortTest.swapIntArray(arr, i, minIndex);
SelectionSortTest.swapIntArray(arr, maxlength, maxIndex);
if(arr[maxlength-1]>arr[maxlength]){
SelectionSortTest.swapIntArray(arr, maxlength-1, maxlength);
}
System.out.print("第"+(i+1)+"次循环:");
SelectionSortTest.printIntArray(arr);
}
return arr;
}
//打印整型数组
public static void printIntArray(int[] arr){
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]+" ");
}
System.out.println();
}
//交换整型数组指定位置的元素
public static void swapIntArray(int[] arr,int i,int j) {
int temp = arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
}
执行结果:
第1次循环:1 3 6 8 5 7 1 9 4 2 9
第2次循环:1 1 6 8 5 7 3 2 4 9 9
第3次循环:1 1 2 4 5 7 3 6 8 9 9
第4次循环:1 1 2 3 5 6 4 7 8 9 9
第5次循环:1 1 2 3 4 5 6 7 8 9 9
第6次循环:1 1 2 3 4 5 6 7 8 9 9
1 1 2 3 4 5 6 7 8 9 9
3.选择排序使用双层循环,最消耗时间的代码是内层循环里定位目标元素索引的代码,即:
//定位到最小值索引
for (int j = i+1; j <= arr.length-(1+i); j++) {
minIndex=arr[j]<arr[minIndex]?j:minIndex;//此行代码是最消耗时间的
}
若数组长度为n,则该行代码执行次数是:(n-1)+(n-2)+......2+1,所以时间复杂度为:,由于选择排序本身的特性(遍历元素并在内层循环确定指定元素的索引然后进行元素交换,必然会使用(n*n)数量级的时间),不管在最好还是最坏的情况下,时间复杂度都是
。
空间上除了目标数组本身以外没有使用额外的数据结构,因此空间复杂度为。
4.只要能够证明经过选择排序后,大小相同的两个元素,相对前后位置对换了,就可以说明选择排序算法是不稳定的。我们可以在上列的代码中加入相应的打印语句,查看选择排序的整个过程:
public class SelectionSortTest {
public static void main(String[] args) {
int[] arr= {5,3,3,8,1,6,7,9,4,2};
printIntArray(selectionSort(arr));
}
static int[] selectionSort(int[] arr) {
for (int i = 0; i < arr.length-1; i++) {
//定义最小元素的索引
int minIndex=i;
//查找最小元素索引
for (int j = i+1; j < arr.length; j++) {
minIndex=arr[j]<arr[minIndex]?j:minIndex;
}
System.out.print("第"+(i+1)+"次循环:");
printIntArray(arr);
//交换两个位置上的元素
swapIntArray(arr, i, minIndex);
System.out.print("索引["+i+"]与["+minIndex+"]即元素‘"+arr[minIndex]+"’与‘"+arr[i]+"’交换:");
printIntArray(arr);
}
return arr;
}
//打印整型数组
public static void printIntArray(int[] arr){
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]+" ");
}
System.out.println();
}
//交换整型数组指定位置的元素
public static void swapIntArray(int[] arr,int i,int j) {
int temp = arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
}
执行结果:
第1次循环:5 3 3 8 1 6 7 9 4 2
索引[0]与[4]即元素‘5’与‘1’交换:1 3 3 8 5 6 7 9 4 2
第2次循环:1 3 3 8 5 6 7 9 4 2
索引[1]与[9]即元素‘3’与‘2’交换:1 2 3 8 5 6 7 9 4 3
第3次循环:1 2 3 8 5 6 7 9 4 3
索引[2]与[2]即元素‘3’与‘3’交换:1 2 3 8 5 6 7 9 4 3
第4次循环:1 2 3 8 5 6 7 9 4 3
索引[3]与[9]即元素‘8’与‘3’交换:1 2 3 3 5 6 7 9 4 8
第5次循环:1 2 3 3 5 6 7 9 4 8
索引[4]与[8]即元素‘5’与‘4’交换:1 2 3 3 4 6 7 9 5 8
第6次循环:1 2 3 3 4 6 7 9 5 8
索引[5]与[8]即元素‘6’与‘5’交换:1 2 3 3 4 5 7 9 6 8
第7次循环:1 2 3 3 4 5 7 9 6 8
索引[6]与[8]即元素‘7’与‘6’交换:1 2 3 3 4 5 6 9 7 8
第8次循环:1 2 3 3 4 5 6 9 7 8
索引[7]与[8]即元素‘9’与‘7’交换:1 2 3 3 4 5 6 7 9 8
第9次循环:1 2 3 3 4 5 6 7 9 8
索引[8]与[9]即元素‘9’与‘8’交换:1 2 3 3 4 5 6 7 8 9
1 2 3 3 4 5 6 7 8 9
我们可以观察到,对于初始数组int[] arr= {5,3,3,8,1,6,7,9,4,2};进行选择排序过程中,原本位于索引[1]上的元素“3”在第二次循环里和索引[9]上的元素“2”交换位置,使得原本索引[1]位置上的元素“3”在前,索引[2]位置上的元素“3”在后的相对状态,变成了索引[2]位置上的元素“3”在前,索引[1]位置上的元素“3”在后的相对状态。而且在后续的排序交换过程中,两者的相对位置依旧没有被换回来,导致选择排序前后,相同元素的相对前后相对位置发生了交换。所以,选择排序是不稳定的排序算法。