漫画:什么是冒泡排序?
快速排序–分治法
冒泡排序
冒泡排序的思想,相邻的元素两两比较,根据大小来交换元素的位置
原始的冒泡排序是稳定排序。由于该排序算法的每一轮要遍历所有元素,轮转的次数和元素数量相当,所以时间复杂度是O(N^2) 。
int[] array = new int[]{5,8,6,3,9,2,1,7};
//冒泡排序
public void BubbleSort1(int arr[]){
for (int i = 0; i <arr.length; i++){
System.err.println("i="+i);
//经过一次排序之后,最后一个元素最大/小 所以第二次只需要比较(arr.length - 1)个元素
// i ,右侧的i个元素有序 所以第i次只需要比较前(arr.length- i)个元素
for (int j = 0; j < arr.length - 1 - i; j++){
if (arr[j] > arr[j + 1]){
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
System.err.println(Arrays.toString(arr));
}
}
}
i=5时(第六轮)
整个数列已然是有序的了。可是我们的排序算法仍然继续执行第七轮、第八轮。
优化方案1
如果我们能判断出数列已经有序,并且做出标记,剩下的几轮排序就可以不必执行,提早结束工作。
public void BubbleSort2(int arr[]){
for (int i = 0; i <arr.length; i++){
System.err.println("i="+i);
//有序标记,每一轮的初始是true
boolean isSorted = true;
//经过一次排序之后,最后一个元素最大/小 所以第二次只需要比较(arr.length - 1)-1
// i 最后i个元素最大/小(有序) 所以第i次只需要比较(arr.length-1) - i
for (int j = 0; j < arr.length - 1 - i; j++){
if (arr[j] > arr[j + 1]){
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
//只要有交换就说明整体任然无序
isSorted = false;
}
}
if(isSorted){//没有任何交换,整体有序,跳出循环
break;
}
System.err.println(Arrays.toString(arr));
}
}
...........
i=5
[1, 2, 3, 5, 6, 7, 8, 9]
i=6(没有任何交换,整体有序,跳出循环)
优化方案2
{3,4,2,1,5,6,7,8};
这个数列的特点是前半部分(3,4,2,1)无序,后半部分(5,6,7,8)有序
定义一个变量n来保存一趟交换中最后一次发生交换的位置,并把它传递给下一趟交换
public void BubbleSort3(int arr[]){
//记录最后一次交换的位置
int lastExchangeIndex =0;
//无序数列的边界,每次比较只需要比到这里为止
int sortBorder=arr.length - 1;
for (int i = 0; i <arr.length; i++){
System.err.println("i="+i);
//有序标记,每一轮的初始是true
boolean isSorted = true;
for (int j = 0; j < sortBorder; j++){
if (arr[j] > arr[j + 1]){
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
//有元素交换,所以不是有序,标记变为false
isSorted = false;
//把无序数列的边界更新为最后一次交换元素的位置
lastExchangeIndex=j;
}
}
sortBorder=lastExchangeIndex;
if(isSorted){
break;
}
System.err.println(Arrays.toString(arr));
}
}
i=0
[3, 2, 1, 4, 5, 6, 7, 8]
i=1
[2, 1, 3, 4, 5, 6, 7, 8]
i=2
[1, 2, 3, 4, 5, 6, 7, 8]
i=3
快排
1.挖坑法
2.指针交换法
3.非递归实现:因为我们代码中一层一层的方法调用,本身就是一个函数栈。每次进入一个新方法,就相当于入栈;每次有方法返回,就相当于出栈。
所以,我们可以把原本的递归实现转化成一个栈的实现,在栈当中存储每一次方法调用的参数:
挖坑法代码优化
public void quickSort(int[] arr, int startIndex, int endIndex) {
// 递归结束条件:startIndex大等于endIndex的时候
if (startIndex >= endIndex) {
return;
}
// 得到基准元素位置
int pivotIndex = partition(arr, startIndex, endIndex);
// 用分治法递归数列的两部分
quickSort(arr, startIndex, pivotIndex - 1);
quickSort(arr, pivotIndex + 1, endIndex);
}
private int partition(int[] arr, int startIndex, int endIndex) {
// 取第一个位置的元素作为基准元素
int pivot = arr[startIndex];
int left = startIndex;
int right = endIndex;
// 坑的位置,初始等于pivot的位置
// int index = startIndex;
// 大循环在左右指针重合或者交错时结束
do {
while ((arr[right] > pivot) && (left < right))
right--;
if (left < right) {
arr[left] = arr[right];
left++;
}
while ((arr[left] < pivot) && (left < right))
left++;
if (left < right) {
arr[right] = arr[left];
right--;
}
} while (left < right);
arr[right] = pivot;
return right;
}