内容:

1、复杂排序

2、排序总结

3、比较器

4、实际问题

注:实现代码为Java

1、复杂排序

说明:关于简单排序(冒泡排序、选择排序、插入排序)  看这里:https://www.cnblogs.com/wyb666/p/10106010.html

复杂排序主要是以下三种:

  • 归并排序: 时间复杂度: O(N * logN) 额外空间复杂度: O(N)
  • 随机快速排序: 时间复杂度: O(N * logN) 额外空间复杂度: O(logN)
  • 堆排序: 时间复杂度: O(N * logN) 额外空间复杂度: O(1)

归并排序:

整体思路:利用归并的思想实现排序,采用经典的分治策略将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序

代码:

 1 public static void mergeSort(int[] arr) {
 2     if (arr == null || arr.length < 2) {
 3         return;
 4     }
 5     sortProcess(arr, 0, arr.length - 1);
 6 }
 7
 8 public static void sortProcess(int[] arr, int L, int R) {
 9     if (L == R) {
10         return;
11     }
12     int mid = (L + R) / 2; // L和R中点的位置
13     sortProcess(arr, L, mid);                // T(N/2)
14     sortProcess(arr, mid + 1, R);            // T(N/2)
15     merge(arr, L, mid, R);                    // O(N)
16     // T(N) = 2*T(N/2) + O(N) => O(N * logN)
17 }
18
19 public static void merge(int[] arr, int L, int mid, int R) {
20     // merge: sort the array(L to R)
21     int[] help = new int[R - L + 1];        // auxiliary array
22     int i = 0;
23     int p1 = L;
24     int p2 = mid + 1;
25     while (p1 <= mid && p2 <= R) {
26         // if the value of arr[p1] < arr[p2] give value arr[p1] to help[i]
27         // else give value arr[p2] to help[i]
28         help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
29     }
30     // p1\p2 must be and only one arrive the boundary
31     // give the remain elements of no-arrive boundary's one(p1 or p2) to help
32     while (p1 <= mid) {
33         help[i++] = arr[p1++];
34     }
35     while (p2 <= R) {
36         help[i++] = arr[p2++];
37     }
38     for (i = 0; i < help.length; i++) {
39         // give elements of help to arr
40         arr[L + i] = help[i];
41     }
42 }

随机快速排序:

快速排序:通过一轮的排序将序列分割成独立的两部分,其中一部分均比另一部分小(根据某个值划分这两个区域)。继续对长度较短的序列进行同样的分割,最后到达整体有序

众所周知,基础快速排序算法,其效率一个关键点就在与划分元素的选取,由于之前一直选取的是第一个元素,所以当遇到特殊输入,比如太大或者太小,就会造成区间划分极度不合理。

引入随机化,就是在每一次划分的时候随机选取一个元素作为关键字,用它来进行划分。由于每一次划分都是随机选取,所以每一次都选到不好的元素概率低,可以作为一个优化的方向

代码:

 1 public static void quickSort(int[] arr) {
 2     if (arr == null || arr.length < 2) {
 3         return;
 4     }
 5     quickSort(arr, 0, arr.length - 1);
 6 }
 7
 8 public static void quickSort(int[] arr, int L, int R) {
 9     if (L < R) {
10         // swap使最后一个数随机 减少极限情况的影响(eg: 1 2 3 4 5这样的数组)
11         swap(arr, L + (int) (Math.random() * (R - L + 1)), R);
12         // 将小于R的数移到左边 大于R的数移到右边 然后R移到中间 返回R的范围
13         int[] p = partition(arr, L, R);
14         // 以中间区域划分
15         quickSort(arr, L, p[0] - 1);
16         quickSort(arr, p[1] + 1, R);
17     }
18 }
19
20 public static int[] partition(int[] arr, int L, int R) {
21     // 默认以最后一个位置做划分
22     int less = L - 1;
23     int more = R;
24     while (L < more) {
25         // L表示当前位置
26         if (arr[L] < arr[R]) {
27             swap(arr, L++, ++less);
28         } else if (arr[L] > arr[R]) {
29             swap(arr, L, --more);
30         } else {
31             L++;
32         }
33     }
34
35     swap(arr, more, R);            // 将最后一个数替换到右边界
36     // 返回等于区域的左右边界
37     return new int[] { less + 1, more };
38 }

堆排序:

堆排序:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列

堆排序实质上是利用了二叉树 ,实质上在工程中二叉树利用数组结构存储,抽象存储原理如下:
二叉树可以转换成数组: 节点i的左右孩子分别是2*i+1和2*i+2 节点i的父节点是(i-1)/2

大根堆和小根堆:

  • 大根堆: 树的任何一个子树的最大值是这颗子树的顶部
  • 小根堆: 树的任何一个子树的最小值是这颗子树的顶部

堆排序过程:

  • 堆排序实际上就是利用堆结构的排序
  • 首先建立一个大根堆(复杂度为O(n)) -> 使用heapInsert插入数
  • 然后把大根堆第一个数和最后一个数替换 拿出这个元素把堆的size-1 然后使用heapify对堆进行调整

去掉堆顶的最大数或最小数: 把堆顶(第一个元素)和最后一个元素交换,拿出这个元素然后把堆的size-1,然后对堆顶进行hapify调整

其实优先级队列结构其实就是堆结构

代码:

 1 public static void heapSort(int[] arr){
 2     if(arr==null||arr.length>2){
 3         return;
 4     }
 5     for(int i=0; i<arr.length; i++){
 6         heapInsert(arr, i);                    // 初步建立大根堆 0到i
 7     }
 8     int heapSize = arr.length;
 9     swap(arr, 0, --heapSize);                // 第一个和最后一个交换 然后heapSize-1
10     while(heapSize>0){
11         heapify(arr, 0, --heapSize);
12         swap(arr, 0, --heapSize);
13     }
14 }
15
16 public static void heapInsert(int[] arr, int index){
17     // 向大根堆中插入元素   并调整元素位置
18     while(arr[index]>arr[(index-1)/2]){
19         swap(arr, index, (index-1)/2);
20         index = (index-1)/2;
21     }
22 }
23
24 public static void heapify(int[] arr, int index, int heapSize){
25     // 拿出堆顶元素之后的调整堆
26     int left = index*2+1;
27     int right = left+1;
28     while(left<heapSize){
29         int largest = right<heapSize && arr[right] > arr[left] ? right : left;
30         largest = arr[largest] > arr[index] ? largest : index;
31         if(largest == index){
32             break;
33         }
34         swap(arr, largest, index);
35         index = largest;
36         left = index * 2 + 1;
37         right = left + 1;
38     }
39 }

2、排序总结

比较少用的排序(了解即可):

  • 桶排序: 时间复杂度O(N) 额外空间复杂度O(N)
  • 计数排序: 时间复杂度O(N) 额外空间复杂度O(N)
  • 基数排序: 时间复杂度O(N) 额外空间复杂度O(N)
  • 上述三种都是非基于比较的排序,与被排序的样本数据实际情况很有关系,在实际中并不常用
  • 另外以上三种都是稳定的排序

关于桶排序计数排序基数排序:

1 桶: 相当于一个容器,可以是数组中的某个位置也可以是双向链表也可以是一个队列也可以是一个堆
2     把相应东西放入相应桶内 然后再从低位置的桶依次到高位置 依次把东西倒出来
3
4     eg: 数组arr长度为60 数据值为0到60 使用桶排序: 生成一个数组temp长度为61 依次遍历arr得到arr[i]
5         将temp对应的数组值++ temp[arr[i]] = temp[arr[i]] + 1 遍历完了之后遍历数组temp输出结果
6         每个下标对应几个值就把下标值输出几遍
7         实际上这个实例是计数排序 实质上是桶排序的一种实现
8
9     基数排序将计数排序进行了改进,用10个桶进行排序 分别针对个位、十位、百位    

算法稳定性:

  • 追求算法稳定性的意义: 第一次排序和第二次排序在某些值相等的情况下保持原来的排序顺序
  • 冒泡排序: 可以实现稳定(相等的情况下后一个继续往后走)
  • 插入排序: 可以实现稳定(相等就不往前插入)
  • 选择排序: 做不到稳定
  • 归并排序: 可以实现稳定(merge相等的时候就先移动左边的)
  • 快速排序: 做不到稳定(partition时做不到)
  • 堆排序: 做不到稳定

工程中的综合排序算法(例如Java内置的sort):

1 基本思想:
2     数组长度很短(大概小于60),用插入排序(插入排序常数极低)
3     数组长度很长,会先判断里面装的是什么,如果都是基本类型就用快排(不在乎稳定性),如果都是
4         自己定义的类型就用归并排序(在乎稳定性)

3、比较器

什么是比较器:

1 在使用系统的内置sort时 如果针对的是普通类型 那么会根据普通类型的值来进行排序
2 但是如果针对的不是普通类型而是自定义类型 这时候就会根据类型对应的对象在内存中的位置进行排序
3 此时排序就没有任何意义了 但是我们可以写一个自定义的比较器传入给sort使用 让sort使用我们定义的比较器来进行排序

Java中比较器的使用:

  1 // 比较器返回负数表示第一个参数排序时应该放前面
  2 // 比较器返回正数表示第二个参数排序时应该放前面
  3 // 比较器返回0表示两个参数一样大
  4
  5 // 比较器
  6 import java.util.*;
  7
  8 public class MyComparator {
  9
 10     public static class Student {
 11         public String name;
 12         public int id;
 13         public int age;
 14
 15         public Student(String name, int id, int age) {
 16             this.name = name;
 17             this.id = id;
 18             this.age = age;
 19         }
 20     }
 21
 22     public static class IdAscendingComparator implements Comparator<Student> {
 23         // id升序排列
 24         @Override
 25         public int compare(Student o1, Student o2) {
 26             return o1.id - o2.id;
 27         }
 28
 29     }
 30
 31     public static class IdDescendingComparator implements Comparator<Student> {
 32         // id降序排列
 33         @Override
 34         public int compare(Student o1, Student o2) {
 35             return o2.id - o1.id;
 36         }
 37
 38     }
 39
 40     public static class AgeAscendingComparator implements Comparator<Student> {
 41         // age升序排序
 42         @Override
 43         public int compare(Student o1, Student o2) {
 44             return o1.age - o2.age;
 45         }
 46
 47     }
 48
 49     public static class AgeDescendingComparator implements Comparator<Student> {
 50         // age降序排序
 51         @Override
 52         public int compare(Student o1, Student o2) {
 53             return o2.age - o1.age;
 54         }
 55
 56     }
 57
 58     public static void printStudents(Student[] students) {
 59         for (Student student : students) {
 60             System.out.println("Name : " + student.name + ", Id : " + student.id + ", Age : " + student.age);
 61         }
 62         System.out.println("===========================");
 63     }
 64
 65     public static void main(String[] args) {
 66         Student student1 = new Student("A", 1, 23);
 67         Student student2 = new Student("B", 2, 21);
 68         Student student3 = new Student("C", 3, 22);
 69
 70         Student[] students = new Student[] { student3, student2, student1 };
 71         printStudents(students);
 72
 73         // 下面的sort不加后面的参数会按照内存中的地址排序
 74         Arrays.sort(students, new IdAscendingComparator());
 75         printStudents(students);
 76
 77         Arrays.sort(students, new IdDescendingComparator());
 78         printStudents(students);
 79
 80         Arrays.sort(students, new AgeAscendingComparator());
 81         printStudents(students);
 82
 83         Arrays.sort(students, new AgeDescendingComparator());
 84         printStudents(students);
 85
 86         // 优先队列其实就是个堆
 87         Comparator comparator = new AgeAscendingComparator();
 88         Queue<Student> heap = new PriorityQueue<Student>(3, comparator);
 89         heap.add(student3);
 90         heap.add(student2);
 91         heap.add(student1);
 92
 93         System.out.println("优先队列排序: ");
 94         while(!heap.isEmpty()){
 95             Student student = heap.poll();        // 弹出堆顶
 96             System.out.println("Name : " + student.name + ", Id : " + student.id + ", Age : " + student.age);
 97         }
 98
 99     }
100
101 }

4、实际问题

小和问题:

 1 问题描述:
 2     在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。
 3     求一个数组的小和。
 4         例子:
 5         [1,3,4,2,5]
 6         1左边比1小的数,没有;
 7         3左边比3小的数,1;
 8         4左边比4小的数,1、3;
 9         2左边比2小的数,1;
10         5左边比5小的数,1、3、4、2;
11         所以小和为1+1+3+1+1+3+4+2=16
12
13 解决思路:
14     使用归并排序中的merge将数组分割,然后合并的时候对比大小(如果小就计算)并使合并的整体有序

 1 public class SmallSum {
 2     public static int smallSum(int[] arr) {
 3         if (arr == null || arr.length < 2) {
 4             return 0;
 5         }
 6         return mergeSort(arr, 0, arr.length - 1);
 7     }
 8
 9     public static int mergeSort(int[] arr, int L, int R) {
10         if (L == R) {
11             return 0;
12         }
13         int mid = L + (R - L) / 2;            // 等同于int mid = (L + R) / 2;
14         return mergeSort(arr, L, mid) + mergeSort(arr, mid + 1, R)
15                 + merge(arr, L, mid, R);
16     }
17
18     public static int merge(int[] arr, int L, int mid, int R) {
19         int[] help = new int[R - L + 1]; // auxiliary array
20         int i = 0;
21         int p1 = L;
22         int p2 = mid + 1;
23         int res = 0;
24         while (p1 <= mid && p2 <= R) {
25             res += arr[p1] < arr[p2] ? (R - p2 + 1) * arr[p1] : 0;
26             help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
27         }
28         while (p1 <= mid) {
29             help[i++] = arr[p1++];
30         }
31         while (p2 <= R) {
32             help[i++] = arr[p2++];
33         }
34         for (i = 0; i < help.length; i++) {
35             arr[L + i] = help[i];
36         }
37         return res;
38     }
39
40     public static void main(String[] args) {
41         int[] arr = { 1, 3, 4, 2, 5 };
42         int[] arr2 = { 1 };
43         int[] arr3 = { 3, 1 };
44         System.out.println(smallSum(arr));
45         System.out.println(smallSum(arr2));
46         System.out.println(smallSum(arr3));
47     }
48 }

荷兰国旗问题:

1 荷兰国旗问题
2     给定一个数组 arr,和一个数 num,请把小于 num 的数放在数组的左边,等于 num 的数放在数组的中间,大于 num 的数放在数组的右边。
3    要求额外空间复杂度 O(1),时间复杂度 O(N)

 1 public static int[] partition(int[] arr, int L, int R, int num) {
 2     int less = L - 1;
 3     int more = R + 1;
 4     int cur = L;
 5     while (cur < more) {
 6         if (arr[cur] < num) {
 7             swap(arr, cur++, ++less);
 8         } else if (arr[cur] > num) {
 9             swap(arr, cur, --more);
10         } else {
11             cur++;
12         }
13     }
14
15     // 返回的是等于区域的左边界和右边界
16     if(less+1>more-1){
17         // 表示不存在等于区域
18         return new int[] { -1, -1 };
19     }
20     else{
21         // 等于区域存在
22         return new int[] { less + 1, more - 1 };
23     }
24
25 }

逆序对问题:

在一个数组中 如果左边的数比右边的数大 则这两个数构成构成一个逆序对 请打印所有逆序对

思路同上面的小和问题

 1 public class ReverseSequence {
 2     public static void reverseSequence(int[] arr) {
 3         if (arr == null || arr.length < 2) {
 4             return;
 5         }
 6         mergeSort(arr, 0, arr.length - 1);
 7     }
 8
 9     public static void mergeSort(int[] arr, int L, int R) {
10         if (L == R) {
11             return;
12         }
13         int mid = L + (R - L) / 2;
14         mergeSort(arr, L, mid);
15         mergeSort(arr, mid + 1, R);
16         merge(arr, L, mid, R);
17     }
18
19     public static void merge(int[] arr, int L, int mid, int R) {
20         int[] help = new int[R - L + 1];
21         int i = 0;
22         int p1 = L;
23         int p2 = mid + 1;
24         while (p1 <= mid && p2 <= R) {
25             if (arr[p1] > arr[p2]) {
26                 System.out.println(arr[p1] + " " + arr[p2]);
27             }
28             help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
29         }
30         while (p1 <= mid) {
31             help[i++] = arr[p1++];
32         }
33         while (p2 <= R) {
34             help[i++] = arr[p2++];
35         }
36         for (i = 0; i < help.length; i++) {
37             arr[L + i] = help[i];
38         }
39
40     }
41
42     public static void main(String[] args) {
43         int[] arr = { 3, 1, 2, 5, 3 };
44         ReverseSequence.reverseSequence(arr);
45     }
46 }

转载于:https://www.cnblogs.com/wyb666/p/10121104.html

跟左神学算法2 排序相关相关推荐

  1. 数据结构与算法之排序算法

    数据结构与算法之排序算法 排序算法的介绍 ​ 排序也称排序算法(Sort Algorithm),排序是将一组数据,依指定的顺序进行排序的过程. 排序的分类 1)内部排序:指将需要处理的数据都加载到内部 ...

  2. 【java算法】排序算法大合集

    文章目录 排序分类/排序算法的分类 冒泡排序 选择排序 插入排序 希尔排序 交换法 移位法(效率高 快速排序 归并排序 基数排序 排序算法时间复杂度比较 相关术语补充 各个排序的区别总结 刷题 老子的 ...

  3. 大话数据结构读书笔记艾提拉总结 查找算法 和排序算法比较好 第1章数据结构绪论 1 第2章算法 17 第3章线性表 41 第4章栈与队列 87 第5章串 123 第6章树 149 第7章图 21

    大话数据结构读书笔记艾提拉总结 查找算法 和排序算法比较好 第1章数据结构绪论 1 第2章算法 17 第3章线性表 41 第4章栈与队列 87 第5章串 123 第6章树 149 第7章图 211 第 ...

  4. 常见数据结构和算法实现(排序/查找/数组/链表/栈/队列/树/递归/海量数据处理/图/位图/Java版数据结构)

    常见数据结构和算法实现(排序/查找/数组/链表/栈/队列/树/递归/海量数据处理/图/位图/Java版数据结构) 数据结构和算法作为程序员的基本功,一定得稳扎稳打的学习,我们常见的框架底层就是各类数据 ...

  5. GIS算法基础——左转算法拓扑生成

    GIS基础算法目录 GIS算法基础--左转算法拓扑生成 GIS算法基础--矢量数据压缩道格拉斯普克压缩算法(非递归实现) GIS算法基础--左转算法拓扑生成 GIS基础算法目录 基于JavaScrip ...

  6. 在Object-C中学习数据结构与算法之排序算法

    笔者在学习数据结构与算法时,尝试着将排序算法以动画的形式呈现出来更加方便理解记忆,本文配合Demo 在Object-C中学习数据结构与算法之排序算法阅读更佳. 目录 选择排序 冒泡排序 插入排序 快速 ...

  7. python算法题排序_python-数据结构与算法- 面试常考排序算法题-快排-冒泡-堆排-二分-选择等...

    算法可视化网站推荐---->visualgo 0.面试题中的排序算法 一些排序算法可能在工作中用的会比较少,但是面试却是不得不面对的问题.算法有助于提高我们对数据结构的理解以及提高自己的逻辑能力 ...

  8. c++ 二维数组 排序_【算法】排序算法之计数排序

    前几回,我们已经对[算法]排序算法之冒泡排序.[算法]排序算法之插入排序.[算法]排序算法之希尔排序.[算法]排序算法之选择排序.[算法]排序算法之快速排序.[算法]排序算法之归并排序.[算法]排序算 ...

  9. [转载] 令牌桶算法和漏桶算法python_排序算法(七):Bucket Sort 桶排序

    参考链接: 用Python进行存储桶Bucket Sort排序 Couting Sort 计数排序虽然快,但其只能对整数进行排序有一点的局限性.而 Bucket Sort 桶排序则没有这个限制.这里我 ...

最新文章

  1. 文件到Java中的byte []
  2. SAP 预算控制业务集成
  3. python360百科_python抓取360百科踩过的坑!
  4. asp编程工具_使用ASP.NET Core构建RESTful API的技术指南
  5. 认识进程 java 1615387415
  6. 电脑上老出现万象预览的广告_如何极速搜索“我的电脑”全部文件?搜索神器推荐!...
  7. 微课|中学生可以这样学Python(8.3节):递推算法例题讲解
  8. CUDA TOOlkit Programming Guide 2. Programming Model
  9. Python构建跳转表
  10. Ubuntu18.04下使用docker制作ubuntu20.04镜像
  11. spring boot小说网站系统 毕业设计源码论文+答辩PPT
  12. webservice(草稿)
  13. mybatis-基本架构
  14. java tire树_谢特——后缀数组+tire 树(示例代码)
  15. Java泛型进阶篇: 无界通配符、上界通配符以及下界通配符
  16. CF 229C Triangles
  17. mysql数据库应用经典案例_MySQL数据库“十宗罪”(十大经典错误案例)
  18. Glide 入门到精通之四——图片缩放和调整大小
  19. android:gravity=quot;centerquot;,android:layout_gravity=bottom不起作用问题
  20. 台式计算机屏幕扩展,电脑扩展显示器调整的方法

热门文章

  1. 《奠基计算机网络》2011年8月15日出版 视频下载 http://www.91xueit.com
  2. 网站设计中程序员和美工的配合问题
  3. maven error: element dependency can not have character children
  4. Broker模块划分
  5. HighChat动态绑定数据 数据后台绑定(三)
  6. 初识MySQL——人生若如初相逢
  7. 在服务器托管中asp***的防范注意些什么?
  8. 容器资源可视化隔离的实现方法
  9. C文件 写入 读取 函数
  10. go string 转 uint64_如何优雅的使用Go接口?