堆排序和桶排序

桶排序

  • 堆结构就是用数组实现的完全二叉树结构

什么是完全二叉树结构?

从左到右依次遍满的就是完全二叉树结构。

满足此结构,那么i结点的左孩子为2×i+1,右孩子为2×i+2。父节点为 (i-1)/2.

堆是特殊的完全二叉树

  • 完全二叉树中如果每棵子树的最大值都在顶部就是大树根
  • 完全二叉树中如果每棵子树的最小值都在顶部就是小树根
  • 堆结构的heapInsert与heapify操作
  • 堆结构的增大和减小
  • 优先级队列结构,就是堆结构

堆排序

大根堆,父结点不能比子节点小。

将最大值的顶结点去除,并且使得剩下的结构还是大根堆。

可以将最大值跟最后一位根节点交换,然后断开连接。交换上去的值,通过比较调换位置,使得堆恢复成大根堆。

public class Code06_HeapSort {

    public static void heapSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        for (int i = 0; i < arr.length; i++) {
            heapInsert(arr, i);
        }
        int size = arr.length;
        swap(arr, 0, --size);
        while (size > 0) {
            heapify(arr, 0, size);
            swap(arr, 0, --size);
        }
    }

    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 ;
        }
    }

    public static void heapify(int[] arr, int index, int size) {
        int left = index * 2 + 1;
        while (left < size) {
            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);
            index = largest;
            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;
    }
}

满二叉树N个结点

叶结点N/2个,进行 1 的动作

上一层N/4个,要进行 2次 动作

总体的复杂度 T(N)= N/2 + N/4*2 + N/8 *3 + ……

利用错位相减法,得到T(N) = N + N/2 + N/4 + N/8 + ……

属于O(N)级别的复杂度

题目1

已知一个几乎有序的数组,几乎有序是指,如果把数组排好顺序的话,每个元素移动的距离可以不超过k,并且k相对于数组来说比较小。请选择一个合适的排序算法针对这个数据进行排序。

假设k = 6

准备一个小根堆结构

遍历数组,先遍历前七个数字。弹出这七个数的最小值,就是当前数组的最小值,因为这个数组是几乎有序的,最小值调整到第一位是移动不超过6的。因此这样就找到了一个最小值,然后将这7个数字向后移动,每移动一次,就可以从小根堆中弹出一个当前最小值,最后遍历完之后,就完成了这个数组的排序。

这个方法的复杂度是O(N*logk), 那么几乎可以认为是一个O(N)的算法

数组需要扩容,但是扩容的次数是有限的。扩容的水平是O(logN)不会影响最终的表现

黑盒

<- add

-> poll

黑盒的情况,不需要手写堆。

比较器的使用

  • 比较器的实质就是重载比较运算符
  • 比较器可以很好的应用在特殊标准的排序上
  • 比较器可以很好的应用在根据特殊标准排序的结构上 比如堆

桶排序

(不基于比较的排序,要根据数据状况定制的。)

桶排序思想下的排序

计数排序

要将[0,200]年龄的人进行排序,可以准备一个[0,200]的一个数组。

每遇到一个k岁的人,那么arr[k]++,就可以统计出年龄的次频表出来。

然后按照年龄这个数组的顺序,将人倒出就进行了排序。

但是如果这个数据不是年龄,而是其他很大的数,那么就需要很大的数组来装载,这很浪费空间,也很没必要。

因此可以采用基数排序。

基数排序

先根据各位数字,放进0-9十个桶中,然后按顺序倒出来。然后再按照十位,放进对应的桶里,然后倒出来。 最后一直放到最高位,就成功排序了。(十进制的数)

仍然是要根据数据状况排序的,如果没有进制的数,还是没法排序

桶应该怎么表示

用一个词频数组来表示,这个词频数组处理十进制数的时候就是10位的一个数组。

然后将词频数组count,变成词频的累加数组count。那么count每一位表示的就是这一位,小于等于k的数有多少个。

再建立一个辅助的help数组,长度跟需要排序的数组一样长。

然后从右往左,看原来的数组。最后一位的个位数是k,count[k]–,然后并将这个数放在help的count[k]位置,cont[k]为当前个位数小于等于k的数的数量。

然后十位,百位同样这样操作。

代码
import java.util.Arrays;

public class Code08_RadixSort {

    // only for no-negative value
    public 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;
        }
        return res;
    }

    public static void radixSort(int[] arr, int begin, int end, int digit) {
        final int radix = 10;           //digit 表示最大值有几个十进制位
        int i = 0, j = 0;

        int[] bucket = new int[end - begin + 1]; //和原数组一样长的桶
        for (int d = 1; d <= digit; d++) {   //有多少位,出桶入桶多少次
            //count[i] 当前位是(0~i)的数有几个。处理成累计数组之后
            int[] count = new int[radix];
            for (i = begin; i <= end; i++) {
                j = getDigit(arr[i], d);    //拿出第d位数
                count[j]++;                 //形成词频表
            }
            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-1,填入辅助数组
                count[j]--;
            }
            for (i = begin, j = 0; i <= end; i++, j++) {
                arr[i] = bucket[j];    //把这个结构放回arr
            }
        }
    }

    public static int getDigit(int x, int d) {
        return ((x / ((int) Math.pow(10, d - 1))) % 10);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
堆排序的时间复杂度为O(nlogn),桶排序的时间复杂度为O(n)。 堆排序是一种基于二叉排序算法,它的时间复杂度为O(nlogn)。堆排序的过程包括建和排序两个步骤。建的时间复杂度为O(n),排序的时间复杂度为O(nlogn)。因此,堆排序的总时间复杂度为O(nlogn)。 桶排序是一种线性时间复杂度的排序算法,它的时间复杂度为O(n)。桶排序的过程是将待排序的元素分配到不同的桶中,然后对每个桶中的元素进行排序,最后将所有桶中的元素按顺序合并起来。由于桶排序的过程中,每个元素只需要与其他桶中的元素进行比较,所以桶排序的时间复杂度为O(n)。 以下是堆排序桶排序的示例代码: 堆排序的示例代码: ```python def heapify(arr, n, i): largest = i l = 2 * i + 1 r = 2 * i + 2 if l < n and arr[i] < arr[l]: largest = l if r < n and arr[largest] < arr[r]: largest = r if largest != i: arr[i], arr[largest] = arr[largest], arr[i] heapify(arr, n, largest) def heapSort(arr): n = len(arr) for i in range(n // 2 - 1, -1, -1): heapify(arr, n, i) for i in range(n - 1, 0, -1): arr[i], arr[0] = arr[0], arr[i] heapify(arr, i, 0) arr = [12, 11, 13, 5, 6, 7] heapSort(arr) print("Sorted array is:", arr) ``` 桶排序的示例代码: ```python def bucketSort(arr): n = len(arr) max_val = max(arr) min_val = min(arr) bucket_range = (max_val - min_val) / n buckets = [[] for _ in range(n)] for num in arr: index = int((num - min_val) / bucket_range) buckets[index].append(num) for i in range(n): buckets[i].sort() sorted_arr = [] for bucket in buckets: sorted_arr.extend(bucket) return sorted_arr arr = [12, 11, 13, 5, 6, 7] sorted_arr = bucketSort(arr) print("Sorted array is:", sorted_arr) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

写代码的信哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值