基数排序、桶排序和计数排序的区别_计数排序和桶排序_Rnan-prince的博客-CSDN博客

目录

一、归并排序

1.归并排序的思想

2.代码实现

3.时间复杂度计算

4.归并排序的扩展 :小和问题

1.直接遍历的复杂度:

2.问题可以转换为,算一个数,右边有多少个数比他大:比1大有4个,比3大有2个…

注意点:当左右数组中有相同的数值,则应该向拷贝右组

注意点:左右两组中的数值必须都是有序的

5.归并排序的扩展 :逆序对问题

二、快速排序的引入---荷兰国旗问题【进行严格的排序】

1.问题一:

2.问题二:

三、快速排序

1.0版本:选择最后一个数值作为num,然后将前面的值进行划分【上面问题一和递归的结合】

2.0版本【上面问题二(荷兰国旗问题)和递归的结合】

3.0版本【随机选择一个数来划分,那个极端好和极端坏的情况都是等概率事件,复杂度与概率求期望,得到期望复杂度为 O ( N log ⁡ N ) O(N\log N)O(NlogN)。】

四、堆排序前序:二叉树

不完全二叉树示例:

二叉树结构:

大根堆:父节点的数比子节点的数要大

把新的数插入到堆中 :heapInsert

​编辑

时间复杂度:O(logn)

要删除最大的那个数值,然后找出剩余的最大值

过程

​编辑

代码

时间复杂度

五、堆排序

1.思路

1.1 给定一组数组,创建一个堆,初始化heapsize=0,然后先从0~0有序,然后0~1有序,然后逐次将数值放入堆中形成大根堆

1. 2.将排成大根堆的第一个数值跟最后一个数值进行交换

1.3 将heapsize--,将最后这个值跟数组断开联系

1.4 .根节点和最后一个节点交换完后,将最后一个节点(最大值)断开

2.代码实现

3.注意点

4.优化 【大根堆的调整】

3.1 实现思路:先分成多个小树,然后再小树中形成大根堆

3.2 时间复杂度

3.3 代码实现

六、堆排序扩展题目

1.实现思路

2.代码实现

七、桶排序

1.计数排序

​编辑

2.基数排序

2.1 将根据个位数将数字添加到桶中

2.2  把桶从左往右导出

2.3 再根据十位数将数值放入桶中

2.4 再将数值输入排序

2.5 再根据百位数将数值放入桶中

2.6 再将数值输入排序

2.7 代码实现

3.实现思路

使用的公式

八、排序算法的稳定性及其汇总

1.稳定性

2.排序是否可以做到稳定性

2.1 选择排序不能做到稳定性

2.2 冒泡排序可以做到稳定性

2.3 插入排序可以做到稳定

2.4 归并排序可以做到稳定

2.5 快速排序做不到稳定性

2.6 堆排序做不到稳定性

2.7 计数排序和基数排序都可以做到稳定性

3.总结

一、归并排序

1.归并排序的思想

归并排序的主要思想是分治法。主要过程是: 将n个元素从中间切开,分成两部分。(左边可能比右边多1个数) 将步骤1分成的两部分,再分别进行递归分解。直到所有部分的元素个数都为1。 从最底层开始逐步合并两个排好序的数列

2.代码实现

    public static void mergeSort(int[] arr) {if (arr == null || arr.length < 2) {return;}mergeSort(arr, 0, arr.length - 1);}public static void mergeSort(int[] arr, int l, int r) {if (l == r) {return;}int mid = l + ((r - l) >> 1);mergeSort(arr, l, mid);mergeSort(arr, mid + 1, r);merge(arr, l, mid, r);}//    merge是一个普通的过程,不是递归public static void merge(int[] arr, int l, int m, int r) {
//        辅助空间
//        一共有:r - l + 1个数int[] help = new int[r - l + 1];
//        i是用于遍历help数组的int i = 0;int p1 = l;int p2 = m + 1;while (p1 <= m && p2 <= r) {//一直往help里面黏贴,同时p1或p2指针一直右移直至越界help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];}
//        p1 <= m和 p2 <= r 只会中一个while (p1 <= m) {//把没越界的指针后面剩余的数组黏贴到help里面help[i++] = arr[p1++];}while (p2 <= r) {//把没越界的指针后面剩余的数组黏贴到help里面help[i++] = arr[p2++];}for (i = 0; i < help.length; i++) {//把排好序的help数组黏贴到原始数组的位置arr[l + i] = help[i];}}

3.时间复杂度计算

4.归并排序的扩展 :小和问题

1.直接遍历的复杂度:

2.问题可以转换为,算一个数,右边有多少个数比他大:比1大有4个,比3大有2个…

注意点:当左右数组中有相同的数值,则应该向拷贝右组

因为要先知道右数组中有多少个跟左数组中的数值相同的个数,然后将右组中全部的个数-相同的个数,得出左组数值的小项

注意点:左右两组中的数值必须都是有序的

两个数组求小和,左边的第一个数比右边的第一个数小,可以知道右边数组的其他数都比左边的大,要是没排序就是乱的,右边数组后面的数就不一定比它大了

同一组中不会产生小值 

    public static int smallSum(int[] arr) {if (arr == null || arr.length < 2) {return 0;}return mergeSort(arr, 0, arr.length - 1);}public static int mergeSort(int[] arr, int l, int r) {if (l == r) {return 0;}int mid = l + ((r - l) >> 1);return mergeSort(arr, l, mid) //左侧排序求小和+ mergeSort(arr, mid + 1, r) //右侧侧排序求小和+ merge(arr, l, mid, r);//2侧排序求小和}public static int merge(int[] arr, int l, int m, int r) {int[] help = new int[r - l + 1];int i = 0;int p1 = l;int p2 = m + 1;int res = 0;while (p1 <= m && p2 <= r) {res += arr[p1] < arr[p2] ? (r - p2 + 1) * arr[p1] : 0;//记录右边的数组有几个数比左边当前数要大help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];//一直往help里面黏贴,同时p1或p2指针一直右移直至越界,将2个数组合并重排}while (p1 <= m) {//把没越界的指针后面剩余的数组黏贴到help里面help[i++] = arr[p1++];}while (p2 <= r) {//把没越界的指针后面剩余的数组黏贴到help里面help[i++] = arr[p2++];}for (i = 0; i < help.length; i++) {//把排好序的help数组黏贴到原始数组的位置arr[l + i] = help[i];}return res;}

5.归并排序的扩展 :逆序对问题

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

示例:对于0来说,30,20,40,50都是逆序对。

二、快速排序的引入---荷兰国旗问题【进行严格的排序】

1.问题一:

小于区域不断往右扩展,遇到比num大的数,就与未比较的区域的数交换,把大于num的数扔到右边,直到小于区域与右边的大于区域相遇。 

2.问题二:

三、快速排序

1.0版本:选择最后一个数值作为num,然后将前面的值进行划分【上面问题一和递归的结合】

2.0版本【上面问题二(荷兰国旗问题)和递归的结合】

1.0,2.0版本,都有可能遇到划分的极端情况,左边区域很大,右边很小,复杂度就为 O ( N 2 ) O(N^2)O(N 2 )。

3.0版本【随机选择一个数来划分,那个极端好和极端坏的情况都是等概率事件,复杂度与概率求期望,得到期望复杂度为 O ( N log ⁡ N ) O(N\log N)O(NlogN)。】

随机取出一个数值,放在最后。以这个数为num

四、堆排序前序:二叉树

不完全二叉树示例:

二叉树结构:

要求从0出发

大根堆:父节点的数比子节点的数要大

如果新值比父大,则向上窜

把新的数插入到堆中 :heapInsert

 public static void heapInsert(int[] arr, int index) {while (arr[index] > arr[(index - 1) / 2]) {//当前节点数值大于父节点位置//(index - 1) /2:父亲的位置//0位置也符合swap(arr, index, (index - 1) /2);index = (index - 1)/2 ;}}

时间复杂度:O(logn)

要删除最大的那个数值,然后找出剩余的最大值

过程

先将树中的最后一个数值提到根节点位置,最大数字于heapsize最后一个元素交换,heapsize减一,然后第一个数做heapify的下移(与左右两个孩子进行比较)操作,如此反复,就能将全部数字排序

代码

 //某数a在index位置,将其往下移动public static void heapify(int[] arr, int index, int heapSize) {//size为数组长度//查找左子树的位置公式:i*2+1int left = index * 2 + 1;//左孩子位置//不仅判断left是否越界并且判断是否有右孩子while (left < heapSize) {//判断孩子是否存在//只有当右孩子存在且大于左孩子时,才取右孩子作为最大值;//其余情况选左孩子,包括//   1.右孩子不存在//  2.右孩子存在但没左孩子大//largest记录最大值的位置//arr[left + 1]:右孩子  arr[left ]:左孩子int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left;//比较父节点和大孩子之间谁大,记录下大的值的位置largest = arr[largest] > arr[index] ? largest : index;//如果父节点比较大,堆结构排好,退出,接着下一次查找if (largest == index) {break;}//孩子比较大,交换父和孩子的位置swap(arr, largest, index);//记录某数a的新位置//此时index应该指向原来largest位置上index = largest;//记录处于新位置的某数a的左孩子left = index * 2 + 1;}}

时间复杂度

新增一个数,或删除最大值,调整的复杂度都是 O ( log ⁡ N ) O(\log N)O(logN)。

五、堆排序

1.思路

  • 1.将数组重构成大根堆

  • 2.将数组的队头元素与队尾元素交换位置

  • 3.对去除了队尾元素的数组进行重构,再次重构成大根堆

1.1 给定一组数组,创建一个堆,初始化heapsize=0,然后先从0~0有序,然后0~1有序,然后逐次将数值放入堆中形成大根堆

1. 2.将排成大根堆的第一个数值跟最后一个数值进行交换

1.3 将heapsize--,将最后这个值跟数组断开联系

1.4 .根节点和最后一个节点交换完后,将最后一个节点(最大值)断开

2.代码实现

    public static void heapSort(int[] arr) {if (arr == null || arr.length < 2) {return;}//将所有数字搞成大根堆for (int i = 0; i < arr.length; i++) {// O(N)heapInsert(arr, i);// O(logN)}int size = arr.length;//0位置上的数与heapsize最后一个数交换swap(arr, 0, --size);while (size > 0) {// O(N)//0位置上的数重新调整位置heapify(arr, 0, size);// O(logN)//0位置上的数与heapsize最后一个数交换,heapsize减小swap(arr, 0, --size);// O(1)}}public static void heapInsert(int[] arr, int index) {while (arr[index] > arr[(index - 1) / 2]) {//当前节点数值大于父节点位置swap(arr, index, (index - 1) /2);index = (index - 1)/2 ;}}//某数a在index位置,将其往下移动public static void heapify(int[] arr, int index, int size) {//size为数组长度int left = index * 2 + 1;//左孩子位置while (left < size) {//判断孩子是否存在//只有当右孩子存在且大于左孩子时,才取右孩子作为最大值;//其余情况选左孩子,包括//    1.右孩子不存在//  2.右孩子存在但没左孩子大//largest记录最大值的位置int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left;//比较父节点和大孩子之间谁大,记录下大的值的位置largest = arr[largest] > arr[index] ? largest : index;//如果父节点比较大,堆结构排好,退出if (largest == index) {break;}//孩子比较大,交换父和孩子的位置swap(arr, largest, index);//记录某数a的新位置index = largest;//记录处于新位置的某数a的左孩子left = index * 2 + 1;}}public static void swap(int[] arr, int i, int j) {int tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;}

3.注意点

重点就是:如果我们发现根节点与孩子节点交换顺序之后,我们就需要重新检查交换之后的孩子节点以下的所有节点是否还满足大根堆的定义,因为可能我们交换后的孩子节点的值还是比他的孩子节点要小的.就比方上面那张图里我们所看到的.所以修改后的代码主要就是加上了重新校验的过程.

4.优化 【大根堆的调整】

如果现在有一个完全二叉树,而不是一个数组,则可以使用下面这个方法

3.1 实现思路:先分成多个小树,然后再小树中形成大根堆

全部数字变成大根堆,有优化做法,最小的树做heapify,然后次小…

3.2 时间复杂度

假设最底层代价是1,倒数第二层代价是二,如此类推:

3.3 代码实现

 public static void heapSort(int[] arr) {if (arr == null || arr.length < 2) {return;}//将所有数字搞成大根堆//做法1:
//      for (int i = 0; i < arr.length; i++) {// O(N)
//          heapInsert(arr, i);// O(logN)
//      }//做法2:for (int i = arr.length-1; i >= 0 ; i--) {heapify(arr, i, arr.length);}int size = arr.length;//0位置上的数与heapsize最后一个数交换swap(arr, 0, --size);while (size > 0) {// O(N)//0位置上的数重新调整位置heapify(arr, 0, size);// O(logN)//0位置上的数与heapsize最后一个数交换,heapsize减小swap(arr, 0, --size);// O(1)}}

六、堆排序扩展题目

1.实现思路

假设现在k=6,则先划分前下标为0~6的位置,因为0位置上的正确数一定在0-6这七个数中,所以将这7个数在小根堆中排好序,最小值就可以弹出放到0位置上,然后再加入下一个数,进行重复操作。

2.代码实现

    public void sortedArrDistanceLessK(int[] arr, int k) {
//        PriorityQueue:优先级队列的底层就是堆排序PriorityQueue<Integer> heap = new PriorityQueue<>();int index = 0;//k个数形成小根堆for (; index < Math.min(arr.length, k); index++) {heap.add(arr[index]);}int i = 0;for (; index < arr.length; i++, index++) {heap.add(arr[index]);//加一个数arr[i] = heap.poll();//弹出一个最小值}while (!heap.isEmpty()) {//依次弹出k个最小值arr[i++] = heap.poll();}}public static void main(String[] args) {
//        优先级队列默认就是小根堆PriorityQueue<Integer> heap = new PriorityQueue<>();heap.add(8);heap.add(3);heap.add(6);heap.add(2);heap.add(4);while (!heap.isEmpty()){System.out.println(heap.poll());}}

七、桶排序

不基于比较排序【如果很大的数组进行桶排序,则浪费空间】

1.计数排序

2.基数排序

根据数值中最多位数为主,不够的再数值前面补0

2.1 将根据个位数将数字添加到桶中

2.2  把桶从左往右导出

2.3 再根据十位数将数值放入桶中

2.4 再将数值输入排序

2.5 再根据百位数将数值放入桶中

2.6 再将数值输入排序

2.7 代码实现

 // only for no-negative valuepublic static void radixSort(int[] arr) {if (arr == null || arr.length < 2) {return;}radixSort(arr, 0, arr.length - 1, maxbits(arr));}//计算最大的十进制位是第几位public static int maxbits(int[] arr) {int max = Integer.MIN_VALUE;for (int i = 0; i < arr.length; i++) {max = Math.max(max, arr[i]);//寻找数组中最大的数}int res = 0;while (max != 0) {res++;max /= 10;//自动整除,因为max是int}return res;}public static void radixSort(int[] arr, int begin, int end, int digit) {//digit:表示几位数//10进制的基底是10final int radix = 10;int i = 0, j = 0;int[] bucket = new int[end - begin + 1];//digit多少个十进制位,也代表入桶出桶的次数【如果是100,则要进出3次】for (int d = 1; d <= digit; d++) {int[] count = new int[radix];//用于记录当前位上等于0,...,等于9的各有多少个数for (i = begin; i <= end; i++) {j = getDigit(arr[i], d);//确认当位上的数是多少count[j]++;//等于该位上的数,统计加1}//用于记录当前位上小于等于0,...,小于等于9的各有多少个数//同时也记录了当前位上等于0,...,等于9的数组最后一个数出桶后的位置for (i = 1; i < radix; i++) {count[i] = count[i] + count[i - 1];}for (i = end; i >= begin; i--) {j = getDigit(arr[i], d);bucket[count[j] - 1] = arr[i];//出桶后的位置上放该数count[j]--;//该桶上的数减一}for (i = begin, j = 0; i <= end; i++, j++) {//把bucket的数组导入arr中,相当于保留了这次桶排序arr[i] = bucket[j];}}}

3.实现思路

1.我们首先需要第一次遍历我们的序列,得到我们序列中的最大值MAX以及序列中的最小值MIN,找到我们序列中的最大值与最小值之后,那么我们就可以确定序列中的所有都是在MIN~MAX这个数据范围区间之中.

2.第二步我们就是需要根据序列的数据范围来确定我们到底需要几个桶来存放我们的元素,这一步其实是比较关键的,因为桶的数量太多或者太少都会降低桶排序的效率.【故假设桶的数量有【(max-min)/桶长+1】

3.确定完桶的数量之后,我们就可以给每个桶来划分数据范围了.一般是这样划分的,【(MAX-MIN)/桶的数量+1】,得到的结果就是桶长.之后每个桶的数据范围就通过桶的编号以及桶长就可以确定每个桶的数据范围.就如下面的公式:

左闭右开
桶的数据范围=[MIN+(桶的编号-1)*桶长,MIN+桶的编号 *桶长)=(当前数值大小-min)/桶长
有了每个桶的数据范围时候,我们第二次遍历序列将每个元素存到相应的桶里面了.这个过程我们要注意,在往桶里面添加元素的时候,就需要在每个桶里面将元素排好序.

4.当我们第二次遍历结束之后,我们就只需要按照桶的编号,在将该编号的桶里面的元素打印出来,桶排序就已经完成了.

使用的公式

nums的长度为L

最小值为min

最大值为max

gap为桶的范围


gap==(max-min)/L +1

桶的个数==(max-min)/gap+1

确定将元素放入哪一个桶中==(nums[i]-min)/gap

八、排序算法的稳定性及其汇总

1.稳定性

2.排序是否可以做到稳定性

2.1 选择排序不能做到稳定性

2.2 冒泡排序可以做到稳定性

2.3 插入排序可以做到稳定

2.4 归并排序可以做到稳定

2.5 快速排序做不到稳定性

2.6 堆排序做不到稳定性

2.7 计数排序和基数排序都可以做到稳定性

3.总结

简单排序【左程云Java】相关推荐

  1. 【左程云Java算法】Chapter1-5:用一个栈实现另一个栈的排序

    [左程云Java算法]Chapter1-5:用一个栈实现另一个栈的排序 [题目] 用一个栈实现另一个栈的排序 [要求] 一个栈中元素的类型为整型,现在想将该栈从顶到底按从大到小的顺序排序,只许申请一个 ...

  2. 数据结构与算法XS班-左程云第一节课笔记(位运算、算法是什么、简单排序)

    第1节 位运算.算法是什么.简单排序 ##这是数据结构与算法新手班-左程云第一节课的笔记## 1. 位运算 // 你们会不会表示一个数字的32位啊? // Java中int类型默认以32位二进制数在计 ...

  3. CSDN专访左程云,算法之道

    算法的庞大让很多人畏惧,程序员如何正确的学习并应用于面试.工作中呢?今天,CSDN邀请了IBM软件工程师.百度软件工程师.刷题5年的算法热爱者左程云,来担任CSDN社区问答栏目的第二十六期嘉宾,届时会 ...

  4. 一看“左程云:200道算法与数据结构”,二刷“阿里云:70+算法题、30种大厂笔试高频知识点”,3月过去终于挺进我梦中的字节!

    不管是学生还是已经工作的人,我想彼此都有一个相同的梦想:进大厂! 眼看着2020年还有个三十来天就要完美收尾了,那么如何才能在未来三个月弯道超车赶上"金三银四的春招",进入梦寐以求 ...

  5. 左程云算法笔记总结-基础篇

    基础01(复杂度.基本排序) 认识复杂度和简单排序算法 时间复杂度 big O 即 O(f(n)) 常数操作的数量写出来,不要低阶项,只要最高项,并且不要最高项的系数 一个操作如果和样本的数据量没有关 ...

  6. leetcode 452. Minimum Number of Arrows to Burst Balloons | 452. 用最少数量的箭引爆气球(左程云:最大线段重合问题)

    题目 https://leetcode.com/problems/minimum-number-of-arrows-to-burst-balloons/ 题解 重叠区间问题可以总结为在坐标轴上若干个位 ...

  7. 一周刷爆LeetCode,算法da神左神(左程云)耗时100天打造算法与数据结构基础到高级全家桶教程,直击BTAJ等一线大厂必问算法面试题真题详解 笔记

    一周刷爆LeetCode,算法大神左神(左程云)耗时100天打造算法与数据结构基础到高级全家桶教程,直击BTAJ等一线大厂必问算法面试题真题详解 笔记 教程与代码地址 P1 出圈了!讲课之外我们来聊聊 ...

  8. 数据结构与算法XS班-左程云第八节课笔记(归并排序和快速排序)

    第8节 归并排序和快速排序 ##这是数据结构与算法新手班-左程云第八节课的笔记## 归并排序 归并排序实际上是一个很经典的排序方法,时间复杂度o(N*logN). 递归版本(图解排序算法(四)之归并排 ...

  9. 左程云:程序员该如何学习算法?

    大家好,我是左程云.我本科就读于华中科技大学.硕士毕业于在芝加哥大学.先后在IBM.百度.GrowingIO和亚马逊工作,是一个刷题7年的算法爱好者. 我是<程序员代码面试指南--IT名企算法与 ...

最新文章

  1. 【爬坑】在 IDEA 中运行 Hadoop 程序 报 winutils.exe 不存在错误解决方案
  2. 线性表的顺序存储——顺序存储结构的抽象实现
  3. 产品需求文档的10步
  4. JDK1.5 与 JDK1.6的新特性
  5. #Pragma编译选项
  6. axios打包放到服务器上接口地址404_如何把网页文件放到云虚拟主机
  7. P1469 找筷子(python3实现)-- 10分
  8. linux SHH 免密码登录 配置
  9. linux下keytool生成证书_生成证书命令keytool
  10. ubuntu文件编码转换
  11. mysql根据id主键查询是找到了就不再遍历后面的数据了吗_MySQL索引相关
  12. 关于python语句与列表,元组,字典等集合的题
  13. 【微信小程序】常用组件及基本使用详解
  14. 远程桌面访问软件:TeamViewer
  15. select2.js插件支持拼音搜索(最新版-4.0.6)
  16. subs php,nginx subs_filter对输出内容进行替换
  17. c语言算兔子野鸡,“上山兔子下山鸡,离水甲鱼不要追”,有什么科学依据?...
  18. HDFS与HBASE的动态节点的扩容(增删)小白级(二)
  19. 长沙计算机学院欧阳登轶,我校学生在2020年湖南省第十六届“强智杯”大学生计算机程序设计竞赛中喜获一等奖...
  20. linux 开启ssdp服务,无法网络发现,Windows Server 2012如何启用SSDP Discovery服务

热门文章

  1. vue实现搜索功能的代码/实现搜索的程序
  2. c++ stack用法 入门必看 超详细
  3. 自适应模拟退火粒子群算法BSAPSO(学习笔记_03)
  4. JavaScript 基础学习(三)
  5. 如何做一个好的BI总监
  6. Java分布式跟踪系统Zipkin(五):Brave源码分析-Brave和SpringMVC整合
  7. python第二版课后习题答案_《python核心编程第二版》课后习题6-12答案
  8. matlab与化学,MATLAB与化学
  9. .Net Core 文件的上传下载
  10. 小学计算机走进魔力画室教案,山西经济出版社小学第一册三年级信息技术第三单元活动1-12教案教案2017年(37页)-原创力文档...