冒泡排序
思想:
冒泡排序会重复遍历序列,遍历次数每次都减少1次,一次比较两个元素,前数比后数大就交换两数,每轮次比较都会把当前轮次的最大数沉底。
public static int[] bubbleSort(int[] arr){
for(int i = 0; i < arr.length - 1; i++){ //这里-1是因为第一个元素不与自己比较
for(int j = 0; j < arr.length - 1 - i; j++){ //这里-1是为了防止数组越界,-i表示比较次数逐次递减
if(arr[j] > arr[j+1]) //如果前数比后数大,就交换
swap(arr,j,j+1);
}
}
return arr;
}
复杂度分析:
如果已经有序,则算法只扫描一遍序列,不交换,复杂度为O(n);若无序,则除了遍历一遍,还要所有元素两两交换一次,复杂度为O(n^2
);平均复杂度为O(n^2)。
稳定性:
该排序算法不改变相同元素的相对位置,所以是稳定的。
可以对冒泡排序进行优化,加一个标志位
public static int[] bubbleSort(int[] arr){
for(int i = 0; i < arr.length - 1; i++){ //这里-1是因为第一个元素不与自己比较
boolean flag = true;
for(int j = 0; j < arr.length - 1 - i; j++){ //这里-1是为了防止数组越界,-i表示比较次数逐次递减
if(arr[j] > arr[j+1]){ //如果前数比后数大,就交换
swap(arr,j,j+1);
flag = false;
}
}
//如果flag为真则跳出外层for,flag为真的情况是内层if未执行,说明当前某趟已将序列排成有序,那就不用再做没用的剩下的外层遍历了
if(flag)
break;
}
return arr;
}
鸡尾酒排序(也叫双端冒泡),第一个for循环从前往后将最大数沉到后面,第二个从后往前将最小数浮到前面
public static int[] bubbleSort(int[] arr){
for(int i = 0; i < arr.length / 2; i++) {
for(int j = 0; j < arr.length - 1 - i; j++) {
if(arr[j] > arr[j+1])
swap(arr,j,j+1);
}
for(int j = arr.length-1; j > i; j--) {
if(arr[j] < arr[j-1])
swap(arr,j,j-1);
}
}
return arr;
}
快速排序
思想:
以分治的思想对pivot两端的序列进行排序,每次排序都将比当前pivot小的元素放在pivot左边,大的放pivot右边
public static int[] quickSort(int[] arr, int low, int high){
if(low < high){
int pivot = partition(arr,low,high);
quickSort(arr,low,pivot-1);
quickSort(arr,pivot+1,high);
}
return arr;
}
public static int partition(int[] arr, int low, int high){
int pivot = arr[low]; //以第一个数作为第一轮的pivot,其实这么做不好,若序列刚好逆序,会导致0(n^2)的复杂度
while(low < high){
while(low < high && arr[high] >= pivot)
high--;
arr[low] = arr[high]; //比pivot小的元素就放左边
while(low < high && arr[low] <= pivot)
low++;
arr[high] = arr[low]; //比pivot大的元素就放右边
}
arr[low] = pivot; //pivot归位
return low;
//这两句写成 arr[high] = pivot; return high; 也行,因为跳出 while 循环时 low == high
}
复杂度分析:
如果序列已经有序(正序或逆序),复杂度为O(n^2) ; 平均和最好复杂度为O(nlogn)。
稳定性:
该排序算法改变相同元素的相对位置,所以是不稳定的。
优化:
对于一个基本有序的序列,若取最开始或最后面的数作为pivot,则复杂度升高到跟冒泡排序一样。我们可以有两种取pivot的策略进行优化:
- 取序列中的随机一个数作为pivot
- 三位取中:取最左边,最右边,中间这三个数的值位于中间的数作为pivot
还可以采用双轴快排,用两个pivot将序列划分为三个部分分别快排,Java里Arrays.sort方法采取的就是双轴快排