堆排序

橙三吉。 提交于 2020-02-02 03:53:13

堆排序

  • 对于一个数组,我们可以用完全二叉树来表示.其第一个元素是它的根节点.
  • 下标i的元素,其左孩子节点是2i+1,右孩子节点是2i+2,根节点是(i-1)/2
  • 大根堆:任何一棵子树的root都是这棵子树的最大值
  • 小根堆:任何一棵子树的root都是这棵子树的最小值

建堆(自下而上)

给定一个数组,如果想利用堆结构去进行逻辑运算,首先要建立一个堆(大根堆or小根堆)。建堆的时候应该自行脑补一下完全二叉树,尽管我们的遍历,交换都是在数组中完成的。

拿建立大根堆来说

遍历数组,遍历到的元素跟根节点比较,如果比根节点大,就交换。但是此时还需要跟已交换节点的根节点比较,总之就是一直保证每棵子树root都是此子树的最大值!

import java.util.Arrays;

/**
 * 对于一个数组,我们可以用完全二叉树来表示.其第一个元素是它的根节点.
 * 下标i的元素,其左孩子节点是2i+1,右孩子节点是2i+2,根节点是(i-1)/2
 * 大根堆:此二叉树的树根是整棵树中的最大值
 * 小根堆:~是最小值
 */
public class HeapSort {
    public static void main(String[] args) {
        int[] arr = new int[]{2, 5, 6, 4, 1, 8};
        System.out.println(Arrays.toString(arr));
        heapSort(arr);
        System.out.println(Arrays.toString(arr));
    }

    public static void heapSort(int arr[]) {
        int size = arr.length;
        //先通过插入元素的方式,建立一个大顶堆
        for (int i = 0; i < arr.length; i++) {
            heapInsert(arr, i);
        }
        //换下最大元素至数组末尾
        swap(arr, 0, --size);
        //当size等于1的时候,就是只剩一个元素啦,数组就有序了
        while (size > 0) {
            heapify(arr, 0, size);
            swap(arr, 0, --size);
        }

    }

    /**
     * 建立大根堆的方法
     *
     * @param arr   原始数组,把它想象成一颗完全二叉树
     * @param index 数组中的某个元素下标
     */
    public static void heapInsert(int[] arr, int index) {
        //如果大于它的父节点,就交换他们的值,然后让index指向它的父节点,这是为了循环向上
        //查找调整
        while (arr[index] > arr[(index - 1) / 2]) {
            swap(arr, index, (index - 1) / 2);
            index = (index - 1) / 2;
        }
    }

    /**
     * 调整大根堆的方法.具体做法是取下这颗完全二叉树的根节点,也就是数组的最大值.
     * 然后数组长度size要减一.再对剩余元素组成的完全二叉树做调整.
     *
     * @param arr   长度为size的数组
     * @param index 当前完全二叉树或者其子树的根节点(他可能已经不是最大值了,需要做调整)
     * @param size  当前数组的长度
     */
    public static void heapify(int[] arr, int index, int size) {
        //当前子树的左孩子节点
        int left = 2 * index + 1;
        //数组不能下标越界
        while (left < size) {
            //先比较index的左右孩子谁大
            // arr[left + 1] > arr[left] ? left + 1 : left;
            // 这条语句必须这么写,left和left+1不能反过来

            int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left;
            //那个大的再和index比较
            largest = arr[largest] > arr[index] ? largest : index;
            if (largest == index) {
                break;
            }
            swap(arr, largest, index);
            //交换了,但是还需要比较剩下的子树,所以要将index指向largest
            index = largest;
            left = index * 2 + 1;
        }

    }

    public static void swap(int[] arr, int i, int j) {
        int temp;
        temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

例题

数组中的第K个最大元素

在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

示例 1:

输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
示例 2:

输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
class Solution {
    public int findKthLargest(int[] nums, int k) {
        //默认建立的是小顶堆
        PriorityQueue<Integer> pq=new PriorityQueue();
        for(int i=0;i<nums.length;i++)
        {
            //向堆中添加元素,维持一个窗口            
            pq.add(nums[i]);
            if(pq.size()>k)
            {
                //保证窗口的大小是3,如果超过3,就弹出当前堆中最小的元素,剩下的是
                //较大的元素,随着窗口的推进,可以将n-3个最小的元素都依次弹出
                //那么剩下的就是最大的3个元素
                pq.poll();
            }
        }
        //堆顶是最大的三个元素里最小的那个,也就是第三大的元素
        return pq.peek();
    }
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!