描述
选择排序是一种简单的排序方法,每次从序列元素中选出最小的一个,顺序放在已排序的序列末尾,直到全部排序结束为止。
原理
- 从序列(N个元素)中找出最小元素,和第一个元素交换;
- 从剩下的序列中找出最小元素,和第二个元素交换;
- 重复(1)、(2)步,直到第N个元素被处理,排序结束。
下面图中给出了选择排序的详细过程:
性质
- 时间复杂度:O(N^2)
- 空间复杂度:O(1)
- 比较次数:N^2/2
- 交换次数:N
选择排序和初始输入数据无关。无论是正序、倒叙还是无序,都会按照上面的规则将所有数据一一比较找出最小元素,然后进行交换。
接口定义
为了方便后面学习排序,定义接口Sorted表示排序接口,在后面的学习中也会一致使用这个接口。
/**
* 排序接口
*/
public interface Sorted {
/**
* 排序
* @param arrays 待排序数组
* @param <T> 限定类型为Comparable接口类型
*/
<T extends Comparable<T>> void sort(T[] arrays);
}
另外,在选择排序中需要涉及两个操作:比较和交换。我们分别定义在AbstractSorted的less()方法和exch()方法中。在后续的排序分析中可能需要其他的方法,我们也会放到AbstractSorted抽象类中作为公共的父类方法使用。代码如下:
/**
* 抽象排序类
*/
public abstract class AbstractSorted implements Sorted {
/**
* 判断v小于w
* @param v
* @param w
*/
protected static boolean less(Comparable v, Comparable w) {
return v.compareTo(w) < 0;
}
/**
* 交换数组a中第i个元素和第j个元素
* @param a
* @param i
* @param j
*/
protected static void exch(Object[] a, int i, int j) {
if (i == j) {
return;
}
if (i >= arrays.length || j >= arrays.length) {
throw new ArrayIndexOutOfBoundsException();
}
Object swap = arrays[i];
arrays[i] = arrays[j];
arrays[j] = swap;
}
}
选择排序
/**
* 选择排序
*/
public class SelectionSorted extends AbstractSorted {
public <T extends Comparable<T>> void sort(T[] arrays) {
// 每趟循环找一个最小元素
for (int i = 0; i < arrays.length; i ++) {
// 标记最小元素位置
int minIndex = i;
// 从剩余的元素中找最小元素
for (int j = i + 1; j < arrays.length; j ++) {
if(less(arrays[j], arrays[minIndex])) {
minIndex = j;
}
}
// 交换
exch(arrays, i, minIndex);
String msg = String.format("第%2d次 ->:%s", i + 1, Arrays.toString(arrays));
System.out.println(msg);
}
}
}
测试代码
/**
* 排序测试
*/
@RunWith(JUnit4.class)
public class SortedTest {
private Integer[] data;
@Before
public void setUp() {
Random random = new Random();
data = new Integer[10];
for (int i = 0; i < data.length; i ++) {
data[i] = random.nextInt(10000);
}
}
/**
* 测试选择排序
*/
@Test
public void testSelectionSorted() {
long start = System.currentTimeMillis();
Sorted sorted = new SelectionSorted();
System.out.println("选择排序前: " + Arrays.toString(data));
sorted.sort(data);
System.out.println("选择排序后: " + Arrays.toString(data));
System.out.println("执行时间: " + (System.currentTimeMillis() - start) + " ms");
}
}
测试结果
选择排序前: [6834, 3139, 9217, 5177, 7578, 116, 2807, 3348, 765, 1002]
第 1次 ->:[116, 3139, 9217, 5177, 7578, 6834, 2807, 3348, 765, 1002]
第 2次 ->:[116, 765, 9217, 5177, 7578, 6834, 2807, 3348, 3139, 1002]
第 3次 ->:[116, 765, 1002, 5177, 7578, 6834, 2807, 3348, 3139, 9217]
第 4次 ->:[116, 765, 1002, 2807, 7578, 6834, 5177, 3348, 3139, 9217]
第 5次 ->:[116, 765, 1002, 2807, 3139, 6834, 5177, 3348, 7578, 9217]
第 6次 ->:[116, 765, 1002, 2807, 3139, 3348, 5177, 6834, 7578, 9217]
第 7次 ->:[116, 765, 1002, 2807, 3139, 3348, 5177, 6834, 7578, 9217]
第 8次 ->:[116, 765, 1002, 2807, 3139, 3348, 5177, 6834, 7578, 9217]
第 9次 ->:[116, 765, 1002, 2807, 3139, 3348, 5177, 6834, 7578, 9217]
第10次 ->:[116, 765, 1002, 2807, 3139, 3348, 5177, 6834, 7578, 9217]
选择排序后: [116, 765, 1002, 2807, 3139, 3348, 5177, 6834, 7578, 9217]
执行时间: 3 ms
参考资料
《Algorithms, 4th Edition》 https://algs4.cs.princeton.edu/21elementary/