当前位置:   article > 正文

10种排序算法的复杂度,比较,与实现_排序算法复杂度对比

排序算法复杂度对比

1.时间复杂度和空间复杂度,及稳定性

2.冒泡排序

 最好是n,最坏是 n的平方,在内存里排。是稳定的。

  1. void Bubble_Sort(int[] arr) //参数为数组和长度
  2. {
  3. int n=arr.length;
  4. for (int i = 0; i < n ; i++) {
  5. for (int j = 0; j < n - i - 1; j++) { //n-i-1是比较次数,每比较一趟就少一次
  6. if (arr[j] > arr[j + 1]) { //交换arr[j]与arr[j+1]的值
  7. int temp = arr[j];
  8. arr[j] = arr[j + 1];
  9. arr[j + 1] = temp;
  10. }
  11. }
  12. }
  13. }

3.插入排序

  1. public void insertSort(int [] nums)
  2. {
  3. if(nums.length==0 && nums.length==1)
  4. {
  5. return ;
  6. }
  7. //注意,遍历的最大位置是 长度减2
  8. for(int i=0;i<nums.length-1;i++)
  9. {
  10. //这个border,表示已排数组的右界。
  11. int border=i;
  12. int current =nums[i+1];
  13. //从右往左遍历已排数组,如果比current大,就让当前遍历的这个元素后移一位。
  14. while(border>=0 && current<nums[border])
  15. {
  16. //右移一位
  17. nums[border+1]=nums[border];
  18. border--;
  19. }
  20. //停的时候,说明已经找到 那个小于等于 current的元素的位置了。
  21. nums[border+1]=current;
  22. }
  23. }

4.希尔排序(里面用了直接插入排序

最佳情况:T(n) = O(n)

最坏情况:T(n) = O(n2)

平均情况:T(n) = O(n2)           

不稳定: 需要多次插入排序,值相同的元素可能在各自的插入排序中移动,最后其 稳定 性就会被打乱

 算法停止的标识是 gap=0。其实就是每次移动gap个单位的,直接插入排序。

  1. public void HillSort(int [] nums)
  2. {
  3. int gap =(nums.length)/2;
  4. //希尔排序的循环条件是gap>0
  5. while(gap>0)
  6. {
  7. //不同于直接插入排序,这里最大索引到nums.length-1
  8. for(int i=gap;i<nums.length;i++)
  9. {
  10. //i-gap,就是直接插入排序的右界。
  11. int border=i-gap;
  12. //
  13. int current=nums[i];
  14. //这个while,就是一个一次移动gap位置的直接插入排序。
  15. while(border>=0 && nums[border] >current )
  16. {
  17. //往后移动gap个位置
  18. nums[border+gap]=nums[border];
  19. border-=gap;
  20. }
  21. //出来了,说明碰到小于等于current的了。
  22. nums[border+gap]=current;
  23. }
  24. gap=gap/2;
  25. }
  26. }

5.归并排序

最佳情况:T(n) = O(nlog2 n)

最坏情况:T(n) = O(nlog2 n)

平均情况:T(n) =O(nlog2n)       稳定

递归的把一个数组拆分为两个数组,直接数组长度只剩1(也就是分成两个元素)。

就是不断地递归  有序地合并 拆分的两个子数组。

合并的方法就是,有序合并 两个数组,且让新数组有序。

那为什么要递归到最深,也就是两个数组只有一个元素的时候呢?

因为这样复杂度低。如果只整一层归并呢?那样复杂度就是n的平方  即(n/2)(n/2)。

  1. public int[] mergeSort(int[] arr)
  2. {
  3. //当数组大小为1的时候,直接return,让递归的上一层排序即可。
  4. if (arr.length == 1) return arr;
  5. //copyOfRange,左闭右开
  6. int mid= (arr.length)/2;
  7. int [] leftArr=Arrays.copyOfRange(arr,0,mid);
  8. int [] rightArr=Arrays.copyOfRange(arr,mid,arr.length);
  9. //mergeSort 必须要有返回值,因为上一层要用
  10. return merge(mergeSort(leftArr),mergeSort(rightArr));
  11. }
  12. // 就是把两个有序数组, 合并成一个有序数组。
  13. public int[] merge(int[] left, int[] right) {
  14. int[] result = new int[left.length + right.length];
  15. for (int index = 0, i = 0, j = 0; index < result.length; index++) {
  16. //i 是用来遍历left 数组的,i都大于等于left.length了,则说明 left数组的元素都加完了
  17. if (i >= left.length)
  18. result[index] = right[j++];
  19. else if (j >= right.length)
  20. result[index] = left[i++];
  21. else if (left[i] > right[j])
  22. result[index] = right[j++];
  23. else
  24. result[index] = left[i++];
  25. }
  26. return result;
  27. }

6.选择排序

最佳情况:T(n) = O(n) 最差情况:T(n) = O(n2) 平均情况:T(n) = O(n2)

不稳定

就是每一轮都把最小的值放在最前面。

 比如第一轮 确定 1为最小的,放在最前面。 下一轮就不用 考虑第一个位置。

第一次排序结果:{1,3,7,2,6,9,5,8}

第二轮就 ,确认2是 第二轮最小的。下一轮就不用 考虑前两个位置。

第二轮排序结果为:{1,2,7,3,6,9,5,8}。

  1. public static int[] selectionSort(int[] array) {
  2. if (array.length == 0)
  3. return array;
  4. for (int i = 0; i < array.length; i++) {
  5. //每个i就是一轮。
  6. int minIndex = i;//最小索引初始化为。
  7. for (int j = i; j < array.length; j++) {
  8. if (array[j] < array[minIndex]) //找到最小的数
  9. minIndex = j; //将最小数的索引保存
  10. }
  11. //然后 位置i 和最小的索引 minIndex 的值交换。
  12. //i 位置就成了这一轮最小的。
  13. int temp = array[minIndex];
  14. array[minIndex] = array[i];
  15. array[i] = temp;
  16. }
  17. return array;
  18. }

7.快速排序

 时间复杂度:

  • 最佳情况:T(n) = O(nlogn)
  • 最差情况:T(n) = O(n的平方)
  • 平均情况:T(n) = O(nlogn)

空间复杂度(因为每次都要记录基准值):O(logn)

但是排序不稳定。

给定一个序列   6 1 2 7 9 3 4 5 10 8

在初始状态下,数字6在序列的第1位。我们的目标是将6挪到序列中间的某个位置,假设这个位置是k。现在就需要寻找这个k,并且以第k位为分界点,左边的数都小于等于6,右边的数都大于等于6。想一想,你有办法可以做到这点吗?

变成如下这样   6 1 2 7 9 3 4 5 10 8。

做法:

  1. 两个指针 一个从 右往左,一个从左到右。i=left ,j=right。i从基准 开始。
  2. 基准在左,就先 左移右指针。直到碰到一个小于 基准的元素。然后  i ,j位置元素交换。  然后再右动左指针,让直到碰到一个大于 基准的元素,然后  i ,j位置元素交换。
  3. 当  i 和 j相等的时候,就是 这一轮 的结束。
  4. 这种i和 j交叉 行走的方式, i 和 j最后一定会停在同一个位置。
  5. 最终把temp,插入到位置 i即可。
  6. 然后递归处理 最终位置 两侧的子数组即可。

  1. void quickSort(int[] arr,int left,int right)
  2. {
  3. if (right >= left) {
  4. //保存基数
  5. int basic = arr[left];
  6. //定义左右指针
  7. int i = left;
  8. int j = right;
  9. while (i < j) { //左指针小于右指针
  10. while (i < j && arr[j] > basic) {//操作右指针找到小于基数的下标
  11. j--;
  12. }
  13. //出了这个while,立马就得判断
  14. if (i < j) {
  15. arr[i] = arr[j];
  16. i++;
  17. }
  18. while (i < j && arr[i] < basic) {//相反,找到大于基数的下标
  19. i++;
  20. }
  21. if (i < j) {
  22. arr[j] = arr[i]; //大于基数的值赋给右指针所指的位置
  23. j--; //右指针左移
  24. }
  25. }
  26. arr[i] = basic; //将基数放入到指针重合处
  27. quickSort(arr, left, i - 1); //递归调用,对左半部分数组进行排序
  28. quickSort(arr, i + 1, right); //对右半部分数组进行排序
  29. }
  30. }

8. 堆排序

堆 是一个完全二叉树。

大根堆:每个节点的值都大于或者等于他的左右孩子节点的值。

跟二叉排序树不同之处是,  孩子节点只需要 小于父亲节点即可。

它满足完全二叉树的一些规则,最后一个非叶子节点是 arrLengrh/2-1

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/668407
推荐阅读
相关标签
  

闽ICP备14008679号