交换排序: 将排序表中两个记录的关键码比较,若与排序要求相逆,则将两者交换。
一、.冒泡排序
1.基本思想:对待排序序列从前往后,依次比较相邻元素的排序码,若发现逆序则交换,使排序码较大的元素逐渐从前部移向后部。
2.代码结果:
排序之前:
[9, -16, 21, 23, -30, -49, 21, 30, 30]
开始排序
[-16, 9, 21, -30, -49, 21, 23, 30, 30]
[-16, 9, -30, -49, 21, 21, 23, 30, 30]
[-16, -30, -49, 9, 21, 21, 23, 30, 30]
[-30, -49, -16, 9, 21, 21, 23, 30, 30]
[-49, -30, -16, 9, 21, 21, 23, 30, 30]
[-49, -30, -16, 9, 21, 21, 23, 30, 30]
排序之后:
[-49, -30, -16, 9, 21, 21, 23, 30, 30]
3.图示:
4.效率分析:
从原始冒泡算法可以看出,
待排序元素是正序时,只进行 1 次循环,比较 n-1 次,移动元素次数为 0 次;
待排序元素是逆序时,需要进行 n-1 次循环,比较 (n^2-n)/2 次,移动元素次数为 3*(n^2-n)/2 次;
所以,时间复杂度为O(n^2);由于移动元素过多,是内排序中速度较慢的一种;因为冒泡排序只进行元素间的顺序移动,所以是一个稳定的算法。
5.测试代码:
原始代码:
public class BubbleSort {
public static void bubbleSort(int[] data) {
System.out.println("开始排序");
int arrayLength = data.length;
for (int i = 0; i < arrayLength - 1; i++) {
for (int j = 0; j < arrayLength - 1 - i; j++) {
if (data[j] > data[j + 1]) {
int temp = data[j + 1];
data[j + 1] = data[j];
data[j] = temp;
}
}
System.out.println(java.util.Arrays.toString(data));
}
}
public static void main(String[] args) {
int[] data = { 9, -16, 21, 23, -30, -49, 21, 30, 30 };
System.out.println("排序之前:\n" + java.util.Arrays.toString(data));
bubbleSort1(data);
System.out.println("排序之后:\n" + java.util.Arrays.toString(data));
}
}
优化一:增加标记,如果在j=0 到 i 之间没有发生交换,则标记为true,说明该区间序列已有序,减少不必要比较,提高效率。
//优化1
public static void bubbleSort1(int[] data) {
System.out.println("开始排序");
int arrayLength = data.length;
for (int i = 0; i < arrayLength - 1; i++) {
boolean flag = false;
for (int j = 0; j < arrayLength - 1 - i; j++) {
if (data[j] > data[j + 1]) {
int temp = data[j + 1];
data[j + 1] = data[j];
data[j] = temp;
flag = true;
}
}
System.out.println(java.util.Arrays.toString(data));
if (!flag)
break;
}
}
优化二:记录每一次循环交换的最后一个数的位置,记录该位置,下一次检索到该位置即可(因为该位置到最后位置的序列已有序)。
//优化2
public static void bubbleSort2(int[] data) {
System.out.println("开始排序");
int arrayLength = data.length;
int i=arrayLength-1;
int pos=0;
while(i>0) {
for (int j = 0; j <i; j++) {
if (data[j] > data[j + 1]) {
pos=j;
int temp = data[j + 1];
data[j + 1] = data[j];
data[j] = temp;
}
}
System.out.println(java.util.Arrays.toString(data));
i=pos;
}
}
还有其它优化的方式,这里只记录理解和写法比较容易的几种。
二、.快速排序(重点)(分区交换排序)
是至今为止,所有内排算法中速度最快的一种
1.基本思想:任取待排序序列中的某个元素作为标准(也称为支点,界点,一般取第一个元素),通过一次划分,将待排元素分为左右两个子序列,左子序列元素的排序码均小于基准元素的排序码,右子序列的排序码均大于或等于基准元素的排序码,然后分别对两个子序列继续进行划分,直至每个序列只有一个元素为止,最后得到的序列便是有序序列。
2.图示:
3.一次划分过程:
1.low指向待划分区域首元素,high指向待划分区域尾元素:
2.R[0]=R[low] (为了减少数据的移动将作为标准的元素暂存
到R[0]中,最后再放入最终位置)
3.high从后往前移动直到R[high].key<R[0]key;
4. R[low]=R[high], low++
5.low从前往后移动直到R[low].key>=R[0].key;
6. R[high]=R[low], high--
7. goto 3
8.直到 loW==hgh 时,R[low]=R[0](即将作为标准的元素放到
其最终位置)
概括地说,一次划分就是从表的两端交替地向中间进行扫描
将小的放到左边,大的放到右边,作为标准的元素放到中间。
4.效率分析:
5.测试代码:
public class QuickSort0 {
private static void swap(int[] data, int i, int j) {
int temp = data[i];
data[i] = data[j];
data[j] = temp;
}
private static void subSort(int[] data, int start, int end) {
if (start < end) {
int base = data[start];
int low = start;
int high = end + 1;
while (true) {
while (low < end && data[++low] - base <= 0)
;
while (high > start && data[--high] - base >= 0)
;
if (low < high) {
swap(data, low, high);
} else {
break;
}
}
swap(data, start, high);
subSort(data, start, high - 1);//递归调用
subSort(data, high + 1, end);
}
System.out.println(java.util.Arrays.toString(data));
}
public static void quickSort(int[] data){
subSort(data,0,data.length-1);
}
public static void main(String[] args) {
int[] data = { 9, -16, 30, 23, -30, -49, 25, 21, 30 };
System.out.println("排序之前:\n" + java.util.Arrays.toString(data));
System.out.println("开始排序");
quickSort(data);
System.out.println("排序之后:\n" + java.util.Arrays.toString(data));
}
}