堆排序时间复杂度_3.堆排序

点击上方蓝字,关注并星标,和我一起学技术.

f81b210dff5614b2b174a66d7d648039.png

堆结构

学过数据结构我们知道完全二叉树的结构如下a8cbd08235bd2bb917adb0fea68e2557.png

我们可以把一个数组通过某种对应关系把数组映射成一颗完全二叉树的.115b92454ad5f1fa3ebaef63f3c5e08a.png下标的映射关系如下:

  • 节点的左孩子:, 节点的右孩子为
  • 节点的父节点为:

所以本质上是不存在二叉树结构, 实际上就是把数组的下标通过这种映射为完成二叉树结构.


堆结构就是一颗完全二叉树, 大根堆是这样一个完全二叉树, 完全二叉树中任何一颗子树的最大值都是这颗子树的头部. 小根堆同理.

388967f86584242322220b3782d0684b.png

那么问题来了怎么把一个数组映射成一个堆结构?a2ea5f816bbfca242537b5b2b382bb79.png0到 i - 1 的位置已经是映射好的大根堆了, 现在新加进来i位置的数, 只要稍微调整一下 i 位置与父节点的关系即可. 只要i位置的值比父节点的值要大, 就一直向上调.

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

建立大根堆的过程第一是遍历一遍数组, 第二与完全二叉树的节点进行比较向上调整这只与高度有关, 所以总的时间复杂度为的总和.
为的时间复杂度.


第二个问题来了, 如果一个数组已经调整成为了一个大根堆, 但是数组中某个元素发生改变后怎么继续维持大根堆结构?

3d39e1afb112a011fbe3d51f9c1be0b8.png

如图6节点的值变为1, 那么变化为就不是一个大根堆了,这时候需要调整. 这个时候就是向下沉的过程, 比较1的左右孩子的最大值, 与最大值进行交换, 重复这个过程. 这时间复杂度显然只有

// 0 - heapSize - 1 的位置已经形成了大根堆. 当index位置的数变小后, 这个节点下沉的过程.
    public static void heapIfy(int[] arr, int index, int heapSize){
        int left = index * 2 + 1;
        int right = index * 2 + 2;
        while (left // 若left > heapSize, 则说明这个是叶节点无需处理.
            //  下面这句是找到左右子树的最大值
            int largest = right  arr[left]
                    ? right
                    : left;

            // 下面这句是比较,当前index的值与子树最大值比较
            largest = arr[largest] > arr[index] ? largest : index;

            // 如果index的值是最大的就无需调整
            if (largest == index)
                break;
            // index!=largest  就需要与子树最大值节点交换, 继续进行while循环
            swap(arr, largest, index);
            index = largest;
            left = index*2 + 1;
        }
    }

我们总结一下,

  • heapInsert过程是一个形成好的堆结构,新加进来一个数即最后的叶节点,这个叶节点向上调形成一个堆的过程.
  • heapIfy过程是一个形成好的堆结构,其中一个节点变化后,向下沉的形成一个堆的过程.

那么这个堆结构用什么用处呢?
我们不妨看一道题一个不断吐出不同数的机器, 我们需要不定时的求出吐出的所有数的中位数.

传统的做法就是先用一个容器去不断的接受这个数, 当需要中位数的时候就将数组排序一遍然后取中位值. 然而这个方法存在严重的问题, 每次新的数加进来后,如果又要取中位数那么就需要再次排序再取,这样时间的代价是非常高的.

我们考虑用堆排序这个结构解决这个问题.
用一个大根堆与一个小根堆, 在不断接受新的数的过程中始终保持大根堆与小根堆的数量一致各占一半.  较小的数放在大根堆,较大的数放在小根堆. 这样取中位数就能直接取得.

堆结构是极其重要的数据结构,很逆天,调整数的过程的时间复杂度就是, 如果有40多亿的数据,只需要比较调整32次. 所以这也是笔试面试的重点.

堆排序

堆排序就是利用堆结构进行排序.
将数组先弄成大根堆.

81002ae06e558109b1b52695ef9fcbb9.png然后将最大的数6与数组最后的元素交换. 这样最大的一个数就完成排序.

eec2eecf73c62d1fa8d793cffe0cf206.png将6从堆中踢除. 堆顶元素3进行heapIfy的过程,继续弄成一个大根堆.584002ccc02c734a617da4c686ca669d.png然后继续上面的过程, 直到所有数排序好.

public class Sort {

    public static void swap( int[] arr, int index1, int index2 ){
        int temp = arr[index1];
        arr[index1] = arr[index2];
        arr[index2] = temp;
    }
    
    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;
        }
    }
    
    // 0 - heapSize - 1 的位置已经形成了大根堆. 当index位置的数变小后, 这个节点下沉的过程.
    public static void heapify(int[] arr, int index, int heapSize){
        int left = index * 2 + 1;
        int right = index * 2 + 2;
        while (left // 若left > heapSize, 则说明这个是叶节点无需处理.
            //  下面这句是找到左右子树的最大值
            int largest = right  arr[left]
                    ? right
                    : left;

            // 下面这句是比较,当前index的值与子树最大值比较
            largest = arr[largest] > arr[index] ? largest : index;

            // 如果index的值是最大的就无需调整
            if (largest == index)
                break;
            // index!=largest  就需要与子树最大值节点交换, 继续进行while循环
            swap(arr, largest, index);
            index = largest;
            left = index*2 + 1;
            right = index*2 + 2;
        }
    }
    public static void heapSort(int[] arr){
        if (arr == null || arr.length 2){
            return;
        }

        // 建立大根堆的过程
        for (int i = 0; i             heapInsert(arr, i);
        }

        int heapSize = arr.length;
        // 堆中的最大数与最后一个数交换, 交换后堆的大小减1
        swap(arr, 0, --heapSize);

        while ( heapSize > 0){
            heapify(arr, 0, heapSize); // 继续调成一个大根堆
            swap(arr, 0, --heapSize);
        }
    }
}

时间复杂度为, 空间复杂度为
堆结构非常重要:

  • 堆结构的heapInsert与heapify过程
  • 堆结构的增大与减少
  • 如果只是建立堆的过程,时间复杂度为
  • 优先级队列就是堆结构
4fe71893d54708673434743852305faa.png
  • 1
    点赞
  • 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
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值