交互排序思想:在待排序的序列中选择两个记录,将他们的关键码进行比较。如果反序则交互它们的位置。
冒泡排序
基本思想:将序列分为有序区,无序区。每次从无序区冒泡一个最小的记录。
冒泡过程:从无序区从后往前扫描,两个相邻记录比较,如果后面比前面的小,则交互。
算法分析
冒泡排序-java实现
/**
* 将序列分为两部分:有序区:无序区 <br>
* 每趟从无序区 冒泡一个最小的,有序区+1.
* 稳定的排序
*
* @param arr
*/
public static void bubble_normal(int[] arr) {
int size = arr.length;
for (int i = 0; i < size - 2; i++) { //n-1轮冒泡
for (int j = size - 1; j > i; j--) {
if (arr[j] < arr[j - 1]) // 从后往前,小的记录往前冒泡
MathUtil.swap(arr, j, j - 1);
}
System.out.println("第 " + (i + 1) + "轮 :" + Arrays.toString(arr));
}
}
冒泡排序的变体
/**
* 非冒泡排序,也非选择排序,姑且叫他假冒泡<br>
* 将序列分为两部分:有序区:无序区<br>
* 每轮排序从无序区找到一个最小记录,无序区长度-1<br>
*
* 找到一个最小记录的过程:<br>
* 用无序区的第一个元素存储最小记录。<br>
* 对比交换:最小记录>无序区记录就交换<br>
* 纪录在排序过程中的移动不是冒泡,而是跳跃的交换。不稳定的排序<br>
*
* 缺点:本来位于前面的较小数被交换到后面
*
* @param arr
*/
public static void bubble_fake(int[] arr) {
int size = arr.length;
for (int i = 0; i < size - 2; i++) {//n-1轮
for (int j = i + 1; j < size; j++) {
if (arr[j] < arr[i]) //确保arr[i]为无序区最小的
MathUtil.swap(arr, i, j);
}
System.out.println("第 " + (i+1) + "轮 :" + Arrays.toString(arr));
}
}
/**
* 冒泡算法的改进,增加isSwaped标志<br>
* 在一轮循环中记录没有交互,也就表明序列已经有序的。<br>
* 当记录交换(冒泡)则isSwaped=true;说明无序。<br>
* @param arr
*/
public static void bubble_optimize(int[] arr) {
int size = arr.length;
boolean isSwaped = true;
for (int i = 0; i < size - 2 && isSwaped; i++) {//n-1轮
isSwaped = false; // 重置状态
for (int j = size - 1; j > i; j--) {
if (arr[j] < arr[j - 1]) { // 从后往前,小的记录往前冒泡
MathUtil.swap(arr, j, j - 1);
isSwaped = true; // 改变则赋值true
}
System.out.println("\t" + Arrays.toString(arr));
}
System.out.println("isChanged:" + isSwaped);
System.out.println("第 " + (i + 1) + "轮 :" + Arrays.toString(arr));
}
}
快速排序
基本思想:选择一个轴值,将待排序列分为两个部分,左侧记录均小于轴值,右侧记录均大于轴值。
划分过程:头尾两根指针分别指向划分区间的头尾,分别向中间靠拢。
过程中,如果尾针所指的记录<轴值,交互,头指针所指记录>轴值,交互。这样就保证了轴值前的所有记录都小于轴值,轴值之后的记录都大于轴值。
直到两根指针相遇的位置,即轴值的最终位置。完成划分。
排序过程:将序列一次划分后,分别对两个子序列进行划分(递归处理),直到划分区间<1 。
一次划分图示:
算法分析
快速排序-java实现
/**
* 划分区间:arr[first]~arr[end]<br>
* 右侧扫描:直到 尾指针 指向的记录 小于 轴值,交互,头指针+1。<br>
* 左侧扫描:直到 头指针 指向的记录 大于 轴值,交互,尾指针-1。<br>
* 头尾两根指针相遇,完成划分。<br>
*
* @param arr
* @param first 划分区间的头指针
* @param end 划分区间的头指针
* @return
*/
public static int partition(int[] arr, int first, int end) {
while (first < end) {//头尾指针相遇,退出循环,即为最终的轴值记录的位置
while (first < end && arr[first] < arr[end])// 右侧扫描
end--;
if (first < end) {
MathUtil.swap(arr, first, end);// 较小记录交互到前面
first++;
}
//具有操作的对称性
while (first < end && arr[first] < arr[end])// 左侧扫描
first++;
if (first < end) {
MathUtil.swap(arr, first, end);// 较大记录交互到后面
end--;
}
}
return first;
}
/**
* 将序列一次划分后,分别对两个子序列进行划分(递归处理),直到划分区间<1 <br>
* @param arr
* @param first
* @param end
*/
public static void quickSort(int[] arr,int first, int end){
if(first<end){//区间长度<1,递归结束
int pivot=partition(arr, first, end);
quickSort(arr, first, pivot - 1);//递归对左侧子序列进行快排
quickSort(arr, pivot + 1, end); //递归对右侧子序列进行快排
}
}
public static void qsort(int[] arr){
quickSort(arr, 0, arr.length-1);
}