堆排序(大顶堆、小顶堆)

什么是堆

堆分为大顶堆和小顶堆,堆需要满足以下条件:

  • 完全二叉树
  • 父节点>子节点(大顶堆)
  • 父子点<子节点(小顶堆)

例如下图:
在这里插入图片描述
父节点比它的子节点都要大的完全二叉树就是大顶堆。(左右孩子的大小就没有顺序)
在这里插入图片描述
相反,父节点比字节点都小的完全二叉树是小顶堆(左右孩子的大小就没有顺序)

堆排序思路

了解完什么是堆之后,我们了解下如何采取堆这样的结构进行排序。

首先堆结构我们可以采取数组的形式存储。我们从根节点开始从左往右标号的话,对应数组下标有这样的规律:

对于节点i
父节点:(i-1)/2
左孩子:2i+1
右孩子2
i+2

在这里插入图片描述

这样我们就可以将一个堆存储在数组里面了,然后利用堆的特性进行排序。

以大顶堆为例,根节点是整个堆里面值最大的,我们将最大值从堆里面移除,同时将最后一个节点放在根节点上面。此时堆变成这样:
在这里插入图片描述
这时的“堆”就不符合父节点>子节点了,所以需要对其进行重新构建:
在这里插入图片描述

此时的堆就符合条件了,根节点又是最大值了,我们重复上面的步骤,将根节点移除,最后一个节点放在根节点上:
在这里插入图片描述
然后一直重复以上操作,最后就可以得到一个排序好的数组了。

代码实现

我们捋一下堆排序的步骤:

  1. 构建堆
  2. 移除根节点,将最后一个节点放在根节点上
  3. 重构堆
    其中步骤2,3一直重复,直至堆的元素都移除完

重构堆

因为堆的构建也是基于堆重构的,所以我们先了解堆怎么重构
假设有怎么一个堆需要重建,

![在这里插入图片描述](https://img-blog.csdnimg.cn/0357db7032b3481fa1874df80004b364.png
我们发现被圈出来的部分不满足父节点最大的条件,所以需要将父节点和左右节点比较,选出最大的值和根节点交换。
在这里插入图片描述
交换完成之后,依旧存在不符合条件的,继续交换
在这里插入图片描述
这样就完成了一次重构,这就是一个完整的堆结构了。

代码实现

	/**
     * 重构堆
     * @param tree
     * @param n 堆的大小
     * @param i 对节点i进行重构
     */
    private void heapify(int[] tree, int n, int i) {
        if (i >= n) {
            return;
        }

        int c1 = 2 * i + 1;//左孩子
        int c2 = 2 * i + 2;//右孩子

        //找到最大值,放在父节点
        int max = i;
        if (c1 < n && tree[c1] > tree[max])
            max = c1;
        if (c2 < n && tree[c2] > tree[max])
            max = c2;

        //交换
        if (max != i) {
            swap(tree, i, max);
            //注意这里需要继续往下递归,因为将i和max交换了,此时的i可能会比它的子节点要小,所以需要继续往下递归检查
            heapify(tree, n, max);
        }

    }
	//交换元素
	private void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

建立堆

从上面我们可以得知如何重构一个堆了,那么如何将初始的数组构建成一个堆呢

我们是从k-1层往上开始构建堆的。例如初始数组是{2,1,5,3,4,10},按照与堆对应的位置,此时结构应该为:
在这里插入图片描述
我们找到最后一个节点的父节点对其进行重建
在这里插入图片描述
完成之后,再对下标为1进行重构,以此类推
在这里插入图片描述
在这里插入图片描述
这样就将初始数组构建成一个堆结构了。

代码实现

/**
     * 建立堆
     * @param tree
     * @param n tree大小
     */
    private void buildHeap(int[] tree, int n) {
        int lastNode = n - 1;	//最后一个节点
        int parentNode = (lastNode - 1) / 2;	//其父节点
        for (int i = parentNode; i >= 0; i--) {
            heapify(tree, n, i);//对i节点进行重构
        }
    }

排序

前面已经了解了堆重构和堆构建,下面就是我们需要的堆排序了。

排序的理论和步骤上面讲过了,这里就直接给代码了

代码实现

public void sort(int[] arr) {
		//1.构建堆
        buildHeap(arr, arr.length);
        for (int i = arr.length - 1; i >= 0; i--) {
        	//将根节点与最后一个节点交换,实现将最后一个节点放在根节点上
            swap(arr, i, 0);
            //重构堆,注意此时我们已经将最大值放在数组的后面了,我们重构堆的时候就不带上它了(相当于移除根节点),所以第二个参数用的是i而不是arr的长度
            heapify(arr, i, 0);
        }
    }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要将大顶堆转换为小顶堆,可以按照以下步骤进行操作: 1. 首先,将大顶堆的根节点(最大值)与最后一个叶子节点进行交换。 2. 然后,将交换后的最后一个叶子节点移出堆。 3. 接下来,对根节点进行下沉操作(与其子节点中较小的那个进行交换),以维持小顶堆的性质。 4. 重复步骤2和3,直到所有节点都被移出堆。 具体的实现过程如下: 1. 假设要将大顶堆存储在数组中,根节点的索引为0。 2. 交换根节点与最后一个叶子节点,即将根节点的值与数组末尾元素交换。 3. 对根节点进行下沉操作,找到它与子节点中较小值的索引,然后将其交换。 4. 重复步骤2和3,直到所有节点都被移出堆。 以下是一个示例实现的伪代码: ``` # 将大顶堆转换为小顶堆 def convert_to_min_heap(heap): n = len(heap) # 从最后一个非叶子节点开始,依次向前处理 for i in range(n // 2 - 1, -1, -1): # 将当前节点下沉到合适位置 heapify(heap, n, i) return heap # 下沉操作 def heapify(heap, n, i): smallest = i left = 2 * i + 1 right = 2 * i + 2 # 找到左子节点和右子节点中的最小值 if left < n and heap[left] < heap[smallest]: smallest = left if right < n and heap[right] < heap[smallest]: smallest = right # 如果最小值不是当前节点,则将其与当前节点交换,并继续下沉 if smallest != i: heap[i], heap[smallest] = heap[smallest], heap[i] heapify(heap, n, smallest) ``` 此伪代码通过调用 `convert_to_min_heap` 函数,将输入的大顶堆转换为小顶堆。注意,此实现假设堆中已经存在数据,并且堆的大小为 n。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值