• 稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。
  • 不稳定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b 的后面。

1. 冒泡排序(已经会了,就不做笔记了)

时间复杂度分析:

  • 最坏的情况是每一个元素都要从前面交换到最后面,每排好一个元素就要遍历一遍数组,一共要排好n个元素,需要O(n^2)的时间复杂度
  • 最好的情况是遍历一遍数组就可排好,O(n)

空间复杂度:不需要额外的空间,O(1)

2. 快速排序

思想:找一个基准值,保证每递归一次基准值的左边都小于等于基准值,右边都大于等于基准值

算法描述:

  • 用数组的最左边的值作为基准值
  • 右指针从数组的最右边开始找第一个小于基准值的数(说明这个数的右边都大于等于基准值)
  • 左指针再从数组的最左边开始找第一个大于基准值的数(说明这个数的左边都小于等于基准值)
  • 交换这两个数
  • 直到左右两个指针重合,此时指向的数是小于基准值的数(因为是右指针先动的),交换基准值和当前左右指针指向的元素
  • 以当前左右指针的位置为分界点递归调用快排方法(递归退出条件是没有要排序的元素时,即左指针大于等于右指针)
 1 public class Solution {
 2     
 3     public static void main(String[] args){
 4         int[] nums = {23,46,0,8,11,18};
 5         Solution sol = new Solution();
 6         sol.quickSort(nums, 0, nums.length-1);
 7         for(int i = 0; i < nums.length; i++){
 8             System.out.println(nums[i]);
 9         }
10     }
11     
12     //快速排序算法
13     private void quickSort(int[] nums, int low, int high){
14         if(low >= high) return;//注意这里是>=而不是==
15         int pivot = nums[low];//用第一个元素作为基准值
16         int left = low;
17         int right = high;
18         while(left < right){
19             //必须先找右边的第一个小于基准值的数,如果先从左边开始找第一个大于基准值的数的话,
20             //最后合基准值交换位置的就会是比基准值大的数了。
21             while(left < right && nums[right] >= pivot)
22                 right--;
23             while(left < right && nums[left] <= pivot)
24                 left++;
25             if(left < right)
26                 swap(nums, left, right);
27         }
28         swap(nums, low, left);
29         quickSort(nums, low, left-1);
30         quickSort(nums, left+1, high);
31     }
32     
33     private void swap(int[] nums, int i, int j){
34         int tmp = nums[i];
35         nums[i] = nums[j];
36         nums[j] = tmp;
37     }
38 }

时间复杂度分析:

T(1) = 1;
T(n) = 2*T(n/2) + a*n;(a为常数,每次合并时,复杂度为O(n))
= 2*(2*T(n/4)+a*n/2) + a*n
= 4*T(n/4) + 2*a*n
= 4*(2*T(n/8)+a*n/4) + 2*a*n
= 8*T(n/8) + 3*a*n
=......
= 2^k*T(1) + k*a*n  (其中n==2^k,即k=log2(n))
= n + a*n*log2(n);

所以平均和最好的时间复杂度是每次都能对半分,是O(nlogn)

最坏的情况是每次其中一边的元素个数为0的时候,是O(n^2)

非递归快排https://blog.csdn.net/mine_song/article/details/64121553

快排升级版,快速选择算法https://zhuanlan.zhihu.com/p/64627590,http://www.noteanddata.com/classic-algorithm-quick-select.html

3. 归并排序

算法描述:

  • 把长度为n的输入序列分成两个长度为n/2的子序列;
  • 对这两个子序列分别采用归并排序;
  • 将两个排序好的子序列合并成一个最终的排序序列。
 1 public class Solution {
 2     
 3     public static void main(String[] args){
 4         int[] nums = {23,46,0,8,11,18};
 5         Solution sol = new Solution();
 6         sol.mergeSort(nums, 0, nums.length-1);
 7         for(int i = 0; i < nums.length; i++){
 8             System.out.println(nums[i]);
 9         }
10     }
11     
12     //归并排序算法
13     private void mergeSort(int[] nums, int start, int end){
14         if(start >= end)
15             return;
16         int mid = start + (end - start)/2;
17         mergeSort(nums, start, mid);
18         mergeSort(nums, mid+1, end);
19         merge(nums, start, mid, end);
20     }
21 
22     private void merge(int[] nums, int start, int mid, int end){
23         int[] tmp = new int[end-start+1];
24         int p1 = start;
25         int p2 = mid+1;
26         int k = 0;
27         //2路归并,将两个排好序的数组排成一个有序的数组
28         while(p1 <= mid && p2 <= end){
29             if(nums[p1] <= nums[p2])
30                 tmp[k++] = nums[p1++];
31             else
32                 tmp[k++] = nums[p2++];
33         }
34         while(p1 <= mid)
35             tmp[k++] = nums[p1++];
36         while(p2 <= end)
37             tmp[k++] = nums[p2++];
38         
39         //把排好序的子数组复制到原数组
40         for(int i = start; i <= end; i++)
41             nums[i] = tmp[i-start];
42     }
43 }

时间复杂度分析:归并排序的时间复杂度不受输入数据的影响,没有最好和最坏之分,都是O(nlogn)。

空间复杂度:需要一个额外的数组的内存空间,O(n)

4. 选择排序

选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。 

时间复杂度:无论什么数据都是O(n^2)的时间复杂度。

5. 插入排序

插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

算法描述:

一般来说,插入排序都采用in-place在数组上实现。具体算法描述如下:

  • 从第一个元素开始,该元素可以认为已经被排序;
  • 取出下一个元素,在已经排序的元素序列中从后向前扫描;
  • 如果该元素(已排序)大于新元素,将该元素移到下一位置;
  • 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
  • 将新元素插入到该位置后;
  • 重复步骤2~5。
 1 public class Solution {
 2     
 3     public static void main(String[] args){
 4         int[] nums = {23,46,0,8,11,18};
 5         Solution sol = new Solution();
 6         sol.insertionSort(nums);
 7         for(int i = 0; i < nums.length; i++){
 8             System.out.println(nums[i]);
 9         }
10     }
11     
12     //插入排序算法
13     private void insertionSort(int[] nums){
14         //可以认为第一个数已经排好序
15         for(int i = 1; i < nums.length; i++){
16             int cur = nums[i];
17             int preindex = i - 1;
18             
19             while(preindex >= 0 && nums[preindex] > cur){
20                 nums[preindex+1] = nums[preindex];
21                 preindex--;
22             }
23             nums[preindex+1] = cur;
24         }
25     }
26 }

时间复杂度分析:

最好的情况是内层循环没有循环次数,输入数据几乎是排好序的状态,O(n)

最坏的情况是内层循环要一直遍历到第一个元素,O(n^2)

6. 希尔排序

直接插入排序的升级版,以增量为分组,进行组内的插入排序,直至增量为1,即只有一个分组,再进行插入排序后排序即完成。

https://www.cnblogs.com/chengxiao/p/6104371.html

posted on 2020-07-14 18:11  小小糖果tt  阅读(116)  评论(0)    收藏  举报