[TOC]
排序算法
简单选择排序(了解)
- 基本思路:不断的从未排序的区域中选择出最小(大)的元素放入到排序的数组区域中;
- 时间复杂度:最好=平均=最差:O(n^2);
- 空间复杂度:O(1)
- 不稳定
public int[] selectSort(int[] nums){
int len = nums.length;
for(int i = 0 ; i < len ; i++){
int min = i;
for(int j = i ; j < len ; j++){
if (nums[j] < nums[min]){
min = j;
}
}
swap(nums,min,i);
}
return nums;
}
public void swap(int[] nums,int i,int j){
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
插入排序(熟悉)
- 基本思路:从第一个元素开始,依次从后选择一个元素,然后跟前面排好序的区域进行比较,插入到合适的位置中;
- 时间复杂度:最好:O(n);平均=最差:O(n^2);
- 空间复杂度:O(1)
-
- 特点:可以提前终止内存循环,在数组几乎有序的前提下,插入排序的时间复杂度可以达到O(N);
- 稳定
public int[] insertSort(int[] nums){ int len = nums.length; for(int i = 1 ; i < len ; i++){ int temp = nums[i]; int j = i; while(j > 0 && nums[j-1] > temp){ nums[j] = nums[j-1]; j--; } nums[j] = temp; } return nums; }
归并排序
- 基本思路:先两两拆分递归至一个元素,然后依次往上让相邻的子数组进行排序。分而治之加上对两个已经排好序的数组合并为一个新的排序数组;
- 时间复杂度:平均=最差=最好:O(nlogn)
- 空间复杂度:O(n)
- 稳定 || 不稳定:可以选择稳定也可以不稳定,区别在于将前后两个数组进行排序放入临时数组时,nums[l]与nums[r]的比较是< 还是 <= ;<=就会保持原来的位置。
class Solution { public int[] sortArray(int[] nums) { mergeSrot(nums,0,nums.length-1,new int[nums.length]); return nums; } public void mergeSrot(int[] nums,int left,int right,int[] temp){ if (left >= right){ return; } int mid = left+(right-left)/2; mergeSrot(nums,left,mid,temp); mergeSrot(nums,mid+1,right,temp); //本身就有序了,不需要在进行操作。 if (nums[mid] < nums[mid+1]){ return; } int l = left; int r = mid+1; int index = l; while(l <= mid && r <= right){ if (nums[l] <= nums[r]){ temp[index++] = nums[l++]; }else{ temp[index++] = nums[r++]; } } while(index <= right) temp[index++] = l==(mid+1)?nums[r++]:nums[l++]; for(int i = left;i<=right;i++){ nums[i] = temp[i]; } } }
快速排序
- 基本思路:分治;每次选择数组中的一个元素(一般是第一个),然后从尾部开始比较,当尾部指针指向的元素小于选择的元素,与头指针进行交换,然后再从头指针开始寻找比选择元素大的元素,与尾部指针交换,再从尾部开始,直到首位相邻;因为是分治,所以数组会被不断的折半;
- 时间复杂度:最好=平均=O(nlogn);最差O(n^2)
- 空间复杂度:O(logn)
- 不稳定
class Solution {
private static final Random RANDOM = new Random();
public int[] sortArray(int[] nums){
quickSort(nums,0,nums.length-1);
return nums;
}
public void quickSort(int[] nums,int left,int right){
if (left >= right){
return;
}
int pivot = partition(nums,left,right);
quickSort(nums,left,pivot-1);
quickSort(nums,pivot+1,right);
}
public int partition(int[] nums,int left,int right){
int randomIndex = RANDOM.nextInt(right - left + 1) + left;
swap(nums, left, randomIndex);
int lt = left;
int pivot = nums[lt];
for(int i = left+1;i <= right ; i++){
if (nums[i] < pivot){
lt++;
swap(nums,lt,i);
}
}
swap(nums,left,lt);
return lt;
}
public void swap(int[] nums,int i,int j){
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
这段代码优化的点: 1.不是选择数组中的第一个元素,而是随机选取一个,这样才能使算法的复杂度平均,不然很容易就变为插入排序了。 2.在根据pivot数组左右分割时,将pivot放在第一个,然后(left,lt]表示小于pivot的元素,依次遍历当前数组,若遍历到元素小于lt,则lt后移一位,然后将小于pivot的元素交换过来,相当于将小于pivot的元素都移到前面。遍历完成之后,此时小于pivot的元素值就都在(left,lt],再讲left与lt交换,此时pivot就在它最终的位置上,小于pivot的值都在他前面了
堆排序
- 基本思路:以大根堆为例,将一维的数组看成一个完成二叉树,保证每个分支节点都符合堆;
堆:大根堆就是左右子节点都小于父节点,小根堆就是左右子节点都大于父节点
调整堆:将首位元素交换后,需要重新将当前最大的元素调整到根节点,从根节点开始依次找出子节点较大的那个节点,然后交换上去,直到没有比他大的或者到底的; 初建堆:就是将原来树调整为堆。 - 时间复杂度:平均=最差=最好:O(nlong)
- 空间复杂度:O(1);
- 不稳定
- 堆排序的过程,可以看这个视频: 数据结构排序算法之堆排序演示
public int[] heapSort(int[] nums) {
int len = nums.length;
// 将数组整理成堆
buildHeap(nums);
// 循环不变量:区间 [0, i] 堆有序
for (int i = len - 1; i >= 1; ) {
// 把堆顶元素(当前最大)交换到数组末尾
swap(nums, 0, i);
// 逐步减少堆有序的部分
i--;
// 下标 0 位置下沉操作,使得区间 [0, i] 堆有序
siftDown(nums, 0, i);
}
return nums;
}
/**
* 将数组整理成堆(堆有序);
* 因为数据本身是用数组表示一棵完全二叉树,所以最后一个有子节点的根节点就为(len-1)/2
* 所以我们只需要对0~(len-1)/2个节点进行调整
*
* @param nums
*/
private void buildHeap(int[] nums) {
int len = nums.length;
// 只需要从 i = (len - 1) / 2 这个位置开始逐层下移
for (int i = (len - 1) / 2; i >= 0; i--) {
siftDown(nums, i, len - 1);
}
}
/**
* 调整的思路:
* 当前节点k,他的左节点为 2*k + 1 右节点为 2*k + 2;
* 如果他的左右节点都存在(不超过end),则先获取他们中的最大值下标j
* 然后将该值与根节点(k)比较,如果根节点小,则将j与k交换(根节点下移)
* 交换完成之后,k=j,来到原根节点被交换下来的位置,继续判断他是否需要被继续交换下去。
* @param nums
* @param k 当前下沉元素的下标
* @param end [0, end] 是 nums 的有效部分
*/
private 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;
}
}
public void swap(int[] nums,int i,int j){
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
排序常用的api
Arrays.sort()
指针对数组类型的排序
int[] a = {9, 8, 7, 2, 3, 4, 1, 0, 5};
Arrays.sort(a);
for(int i = 0; i < a.length; i ++) {
System.out.print(a[i] + " ");
}
如果要自定义排序规则,不能用基本类型的数组,因为要用到泛型。
Integer[] a = {9, 8, 7, 2, 3, 4, 1, 0, 5};
Arrays.sort(a,(o1,o2)->{
return o2-o1;
});
for(int i = 0; i < a.length; i ++) {
System.out.print(a[i] + " ");
}
Collections.sort()
针对List等Collection容器的排序api
List<Character> charList = new ArrayList<>(map.keySet());
Collections.sort(charList,(c1,c2)->{
return map.get(c2)-map.get(c1);
});
优先队列PriorityQueue
PriorityQueue<Map.Entry<Character,Integer>> items = new PriorityQueue<>((o1,o2)->o2.getValue()-o1,getValue());