目录
1. 冒泡排序
1.1 排序原理
1. 从第一个元素开始, 比较相邻的元素. 如果前一个元素比后一个元素大, 就交换这两个元素的位置
2. 对每一个对相邻的元素做同样的工作, 最终最后位置的元素就是最大值.
3. 重复如此, 知道整个队列完成排序
1.2 AIP设计
类名 | Bubble |
构造方法 | Bubble() |
成员方法 | 1. public static void sort(Comparable[] a): 对数组内的元素进行排序 2. private static boolean greater(Comparable v, Comparable w): 判断v是否大于w 3. private static void exch(Comparable[] a, int i, int j) : 交换数组a中索引i和j处的值 |
1.3 代码实现
package sort;
public class Bubble {
/*
对数组a中的元素进行排序
*/
public static void sort(Comparable[] a){
for (int i = a.length - 1; i > 0; i--) {
for(int j = 0; j < i; j++){
// 比较j和j+1处元素的值, 如果j处的元素比j+1处的元素大, 则交换
if(greater(a[j], a[j+1])){
exch(a, j, j + 1);
}
}
}
}
/*
判断元素v是否大于元素w
*/
private static boolean greater(Comparable v, Comparable w){
return v.compareTo(w) > 0;
}
/*
交换数组a中, 索引i和j处的值
*/
private static void exch(Comparable[] a, int i, int j){
Comparable temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
1.4 时间复杂度分析
冒泡排序使用了双层的for循环, 其中内层循环体是真正完成排序的代码. 因此, 分析时间复杂度只需要分析内层循环的执行次数即可.
在最坏的情况下, 也即需要对一个降序数列进行排序, 那么, 元素比较的次数为:
(N-1) + (N-2) + (N-3) + ... + 2 + 1 = ((N-1) + 1) * (N-1)/2 = N^2/2 - N/2
元素交换的次数同样也为
(N-1) + (N-2) + (N-3) + ... + 2 + 1 = ((N-1) + 1) * (N-1)/2 = N^2/2 - N/2
因此, 总执行次数为
N^2 - N, 也即时间复杂度为O(N^2).
2. 选择排序
2.1 排序原理
1. 每次遍历的过程中, 都假定第一个索引处的元素是最小值, 和其他索引处的值一次进行比较. 如果当前索引处的值大于其他索引处的值, 则将最小值更新为另一个索引处的值, 直到遍历至队列末尾.
2. 交换第一个索引处和最小值所在的索引处的值.
2.2 API设计
类名 | Selection |
构造方法 | Selection() |
成员方法 | 1. public static void sort(Comparable[] a): 对数组内的元素进行排序 2. private static boolean greater(Comparable v, Comparable w): 判断v是否大于w 3. private static void exch(Comparable[] a, int i, int j) : 交换数组a中索引i和j处的值 |
2.3 代码实现
package sort;
public class Selection {
/*
对数组a中的元素进行排序
*/
public static void sort(Comparable[] a){
/*
i指针指向每一次排序时队列中第一个元素
for循环结束的条件是i < a.length - 1是因为只需要比较至倒数第二个元素,
因为最后一个元素只能是最大的
*/
for (int i = 0; i < a.length - 1; i++) {
// 首先假定本次遍历, 最小值的索引为i
int minIndex = i;
for(int j = i + 1; j < a.length; j++){
// 如果j处的元素比最小索引处的值还要小, 则更新minIndex
if (greater(a[minIndex], a[j])){
// 更新minIndex
minIndex = j;
}
}
// 一次扫描结束, 将minIndex处的值和第一个未排序的值(i)进行交换
exch(a, i, minIndex);
}
}
/*
判断元素v是否大于元素w
*/
private static boolean greater(Comparable v, Comparable w){
return v.compareTo(w) > 0;
}
/*
交换数组a中, 索引i和j处的值
*/
private static void exch(Comparable[] a, int i, int j){
Comparable temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
2.4 时间复杂度分析
选择排序同样使用了一个双层for循环进行排序, 外层循环完成了数据交换, 内层循环则完成了数据比较, 所以我们分别统计数据交换和比较次数:
数据比较次:
(N-1) + (N-2) + (N-3) + ... + 2 + 1 = ((N-1) + 1) * (N-1)/2 = N^2/2 - N/2
数据交换此时:
N-1
因此, 其时间复杂度和冒泡排序相同, 也为O(N^2).
3. 插入排序
3.1 排序原理
1. 把所有的元素分为两组, 已经排序的和未排序的.
2. 找到未排序的组中的第一个元素, 向已经排序的组中进行插入
3. 倒序遍历已经排序的元素, 依次和待插入的元素进行比较, 直到找到一个元素小于等于待插入元素, 那么就把待插入的元素放到这个位置, 其他元素向后移动一位(实际上, 是将待插入的元素从已排序的元素的末尾依次进行比较, 若待插入的元素比比较的元素小, 则将这两个元素进行交换, 以此实现对其他元素的向后移动).
*如下图所示, 其中红色表示需要向后移动一位的元素, 绿色表示待插入的元素
3.2 API设计
类名 | Insertion |
构造方法 | Insertion() |
成员方法 | 1. public static void sort(Comparable[] a): 对数组内的元素进行排序 2. private static boolean greater(Comparable v, Comparable w): 判断v是否大于w 3. private static void exch(Comparable[] a, int i, int j) : 交换数组a中索引i和j处的值 |
3.3 代码实现
package sort;
public class Insertion {
/*
对a数组中的元素进行排序
*/
public static void sort(Comparable[] a){
/*
i指针指向待插入的元素的初始位置
i的初始值为1, 因为初始状态时, 默认第一个元素已经排好序,
因此从第二个元素(i=1)开始进行比较
*/
for (int i = 1; i < a.length; i++) {
/*
j指针指向的则是待插入元素变化后的实时位置
*/
for (int j = i; j > 0; j--){
// 如果待插入元素比前一个元素要小, 则进行交换
if (greater(a[j-1], a[j])){
exch(a, j-1, j);
}else {
// 如果待插入元素大于或等于前一个元素, 则说明找到位置
break;
}
}
}
}
/*
比较元素v是否大于元素w
*/
private static boolean greater(Comparable v, Comparable w){
return v.compareTo(w) > 0;
}
/*
交换数组a中索引i, j处的值
*/
private static void exch(Comparable[] a, int i, int j){
Comparable temp;
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
3.4 时间复杂度分析
插入排序使用双层for循环, 其中内层循环是真正完成排序的代码.
最坏情况下, 需要对一个降序数组进行排序, 那么
需要进行比较的次数:
(N-1) + (N-2) + (N-3) + ... + 2 + 1 = ((N-1) + 1) * (N-1)/2 = N^2/2 - N/2
交换的次数:
(N-1) + (N-2) + (N-3) + ... + 2 + 1 = ((N-1) + 1) * (N-1)/2 = N^2/2 - N/2
总的执行次数为N^2-N, 因此时间复杂度为O(N^2).