快速排序
快速排序体现了分治的思想。每次决定一个数的最终位置,将其放置到正确位置后,再通过递归的方式将两头的数组按照同样的方式快速排序。
需要注意的是,使用快速排序时,需要随机取那个决定数,这叫做随机选择比较子。因为当数组时已排好序的时候,指针只会从尾刷到头,快速排序的效率就会非常低,时间复杂度为O(n^2)。所以随机选择比较子后,将比较子和数组最左边的数交换,再做排序。这样的话就算数字已经排好序了,指针也不会从尾刷到头。
- 快速排序是一种不稳定的算法。算法不稳定是指:在排序之前,有两个数相等,但是在排序结束之后,它们两个有可能改变顺序。
- 时间复杂度:O(nlogn),这里n是数组的长度;
- 空间复杂度:O(logn),这里占用的空间主要来自递归函数的栈空间。
class Solution{
Random random = new Random();
public int[] sortArray(int[] nums) {
quickSort(nums, 0, nums.length-1);
return nums;
}
//快速排序
public void quickSort(int[] nums, int start, int last){
if(start<last){
int left = start, right = last;
//随机取数
int ranIndex = random.nextInt(last-start+1)+start;
int temp = nums[ranIndex];
nums[ranIndex] = nums[left];
nums[left] = temp;
int aim = nums[left];
while(left<right){
while(left<right && aim<=nums[right]) right--;
if(left<right) nums[left++] = nums[right];
while(left<right && nums[left]<=aim) left++;
if(left<right) nums[right--] = nums[left];
}
nums[left] = aim;
quickSort(nums, start, left-1);
quickSort(nums, left+1, last);
}
}
}
归并排序
归并排序也是体现了分治的思想。将排序任务分给两个子数组,再将两个子数组合并。这里合并的时候需要多申请n的空间资源存放排序后的数组。
- 时间复杂度:O(nlogn)
- 空间复杂度:O(n)
class Solution {
public int[] sortArray(int[] nums) {
int[] buffer = new int[nums.length];
mergeSort(nums, 0, nums.length-1, buffer);
return nums;
}
public void mergeSort(int[] nums, int start, int last, int[] buffer){
if(start<last){
int mid = (start+last)/2;
mergeSort(nums, start, mid, buffer);
mergeSort(nums, mid+1, last, buffer);
mergeTwoArray(nums, start, mid, last, buffer);
}
}
public void mergeTwoArray(int[] nums, int start, int mid, int last, int[] buffer){
int left = start, right = mid+1;
int i = left;
while(left<=mid && right<=last){
if(nums[left]<=nums[right]){
buffer[i++] = nums[left++];
} else{
buffer[i++] = nums[right++];
}
}
while(left<=mid) buffer[i++] = nums[left++];
while(right<=last) buffer[i++] = nums[right++];
for(int j = start; j<=last; j++){
nums[j] = buffer[j];
}
}
}
选择排序
选择排序适合于交换数据代价比较大的场合。每轮记住未排序数组中最小的下标,遍历完数组后把最小的下标数和排序后的下一个数(未排序数)交换。
- 时间复杂度:O(n^2)
- 空间复杂度:O(1)
public class Solution {
// 选择排序:每一轮选择最小元素交换到未排定部分的开头
public int[] sortArray(int[] nums) {
int len = nums.length;
// 循环不变量:[0, i) 有序,且该区间里所有元素就是最终排定的样子
for (int i = 0; i < len - 1; i++) {
// 选择区间 [i, len - 1] 里最小的元素的索引,交换到下标 i
int minIndex = i;
for (int j = i + 1; j < len; j++) {
if (nums[j] < nums[minIndex]) {
minIndex = j;
}
}
swap(nums, i, minIndex);
}
return nums;
}
private void swap(int[] nums, int index1, int index2) {
int temp = nums[index1];
nums[index1] = nums[index2];
nums[index2] = temp;
}
插入排序
插入排序在数组越有序的时候效率越高,越有序,需要移动指针的次数越少,最少只需要n次。
插入排序每次从左侧已排序数组的下一个数(未排序数)开始与已排序数组的数逐一比较,若未排序数更小,那就把大的已排序数往后挪一格,直到找到属于自己的位置。
class Solution{
public void InsertSort(int[] nums){
for(int i = 0; i<nums.length; i++){
int temp = nums[i];
int j = i;
while(j>0 && nums[j-1]>temp){
nums[j] = nums[j-1];
j--;
}
nums[j] = temp;
}
}
}