一、快速排序
思想:基于分治的思想,是冒泡排序的改进型。首先在数组中选择一个基准点并把基准点放于序列的开头(该基准点的选取可能影响快速排序的效率,关于基准点的选择方法后面再讲解),然后分别从数组的两端扫描数组,设两个指示标志(lo指向起始位置,hi指向末尾),首先从后半部分开始,如果发现有元素比该基准点的值小,就交换lo和hi位置的值,然后从前半部分开始扫秒,发现有元素大于基准点的值,就交换lo和hi位置的值,如此往复循环,直到lo>=hi,然后把基准点的值放到hi这个位置,一次排序就完成了。之后再采用递归的方式分别对前半部分和后半部分排序,当前半部分和后半部分均有序时该数组自然也就有序了。
时间复杂度:平均时间复杂度O(nlogn),最坏时间复杂度O(n*n)(数组全部倒序)。
空间复杂度:平均O(logn) 最坏O(n)。
代码:
public static void quickSort(int[] arr,int low,int high){
if(low>=high){
return;
}
int i=low;
int j=high;
int temp=arr[i];
while (i<j){
while (i<j&&temp<=arr[j]){
j--;
}
while (i<j&&temp>=arr[i]){
i++;
}
if(i<j){
int t=arr[i];
arr[i]=arr[j];
arr[j]=t;
}
}
arr[low]=arr[i];
arr[i]=temp;
quickSort(arr, low, i-1);
quickSort(arr, j+1, high);
}
二、归并排序
思想:基于分治的思想,不断细分至两个子元素,排序后向上归并。
时间复杂度:O(nlogn)。
代码:
public static void merge(int[] arr,int low,int mid,int high,int[] tmp){
int i=0;
int j=low;
int k=mid+1;
//两个数组归并到一个数组
while (j<=mid&&k<=high){
if(arr[i]<arr[k]){
tmp[i++]=arr[j++];
}else {
tmp[i++]=arr[k++];
}
}
//哪个剩了直接添加
while (j<=mid){
tmp[i++]=arr[j++];
}
while (k<=high){
tmp[i++]=arr[k++];
}
for(int t=0;t<i;t++){
arr[t+low]=tmp[t];
}
}
public static void mergeSort(int[] arr,int low,int high,int[] tmp){
if(low<high){
int mid=(low+high)/2;
mergeSort(arr, low, mid, tmp);
mergeSort(arr, mid+1, high, tmp);
merge(arr,low,mid,high,tmp);
}
}
public int[] sortArray(int[] nums) {
mergeSort(nums,0,nums.length-1, new int[nums.length]);
return nums;
}
三、堆排序
思想:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列。
时间复杂度:O(nlogn)
空间复杂度:O(1)
public static int[] sortArray(int[] nums) {
int len = nums.length;
// 将数组整理成堆
heapify(nums);
// 循环不变量:区间 [0, i] 堆有序
for (int i = len - 1; i >= 1; ) {
// 把堆顶元素(当前最大)交换到数组末尾
swap(nums, 0, i);
// 逐步减少堆有序的部分
i--;
// 下标 0 位置下沉操作,使得区间 [0, i] 堆有序
siftDown(nums, 0, i);
}
return nums;
}
/**
* 将数组整理成堆(堆有序)
*
* @param nums
*/
private static void heapify(int[] nums) {
int len = nums.length;
// 只需要从 i = (len - 1) / 2 这个位置开始逐层下移
for (int i = (len - 1) / 2; i >= 0; i--) {
siftDown(nums, i, len - 1);
}
}
/**
* @param nums
* @param k 当前下沉元素的下标
* @param end [0, end] 是 nums 的有效部分
*/
private static void siftDown(int[] nums, int k, int end) {
while (2 * k + 1 <= end) {
int j = 2 * k + 1;
if (j + 1 <= end && nums[j + 1] > nums[j]) {
j++;
}
if (nums[j] > nums[k]) {
swap(nums, j, k);
} else {
break;
}
k = j;
}
}
private static void swap(int[] nums, int index1, int index2) {
int temp = nums[index1];
nums[index1] = nums[index2];
nums[index2] = temp;
}