zoukankan      html  css  js  c++  java
  • 大根堆和堆排序的原理与实现

    shift up

    • 插入一个元素时,把元素追加到列表的最后,也就是堆的叶子节点上。然后执行shifup操作,对新元素进行从下往上的调整。
    • 判断当前元素是否比父节点更大,如果是,则交换。否则就终止。
    • 因为插入一个元素时,列表已经是一个大根堆,所以当出现父元素大于自己时,就没有必要继续,因为父元素的父元素值更大。

    shift down

    • 删除一个元素时,把该元素和列表的最后一个元素交换。然后列表的长度减一(如果用count计数的话)。剩余的元素进行shiftdown操作。

    • shiftdown,如果两个孩子中存在比自己更大的元素,就和那个孩子交换值。然后这个孩子作为根,继续这个操作。

    • 如下: 插入时执行shiftUp,删除时执行shiftDown

    // 堆排序
    class Heap{
            private List<Integer> data;
    
            public Heap(){
                this.data = new ArrayList<>();
            }
    
            public void insert(int value){
                this.data.add(value);
                this.shiftUp(this.data.size() - 1);
            }
            public int pop(){
                this.swap(0, this.data.size() - 1);
                int res = this.data.remove(this.data.size() - 1);
                this.shiftDown(0);
                return res;
            }
    
            /**
             * 上移,元素加入时执行
             * @param k
             */
            public void shiftUp(int k){
                // 0 元素没有父元素
                // 当出现k比父元素小的时候,就没有必要继续下去,因为,父元素的父元素一定是更大的数
                while(k >= 1 && data.get(k) > data.get((k - 1) / 2)){
                    swap(k, (k - 1) / 2);
                    k = (k - 1) / 2;
                }
            }
    
            /**
             * 下移,调整堆时执行
             * @param k
             */
            public void shiftDown(int k){
                // 省略了对左孩子的判断
                while(2 * k + 1 < data.size()){
                    int child =  2 * k + 1;
                    if(child + 1 < this.data.size() && this.data.get(child) < this.data.get(child + 1)){
                        child += 1;
                    }
                    if(this.data.get(child) > this.data.get(k)){
                        this.swap(k, child);
                    }
                    k = child;
                }
    
            }
    
            public void swap(int left, int right){
                int temp = this.data.get(left);
                this.data.set(left, this.data.get(right));
                this.data.set(right, temp);
            }
        }
        
        // main
            _215_数组中的第k个最大元素.Heap2 heap = handle.new Heap2();
            // 建立堆
            for(int item : list){
                heap.insert(item);
            }
            for(int item : list){
                System.out.print(heap.pop() + " ");
            }
    

    heapify

    • 不需要一个一个的插入元素,可以直接对原数组的所有非叶节点进行heapify,即可构成一个大根堆。
    • ((size - 1) - 1) / 2 表示最大下标减一。当然,用size/2也行,多出的几个元素都是叶子节点,都可以当成是只有一个元素的堆。
            public Heap2(List<Integer> list){
                this.data = new ArrayList<>(list);
                heapify();
            }
            public void heapify(){
                for(int i = (data.size() - 1 - 1) / 2; i >= 0; --i){
                    shiftDown(i);
                }
            }
    

    就地排序

    • 这里的nums数组的数据从0开始拍,这样的话,按照如下方式计算父子节点
      • 左孩子: index * 2 + 1
      • 右孩子: index * 2 + 2
      • 父节点: (index - 1) / 2 或者 (length -1 -1)/2。有的算法直接用 (length / 2)也不会有错,因为最右边的叶子节点都可以看成是单个的大根堆。
    class Heap {
            public void heapSort(int [] nums){
                // 从第一个非叶节点开始,执行shiftdown操作
                int size = nums.length;
    
                // 建立一个堆
                // 很多携程(size-1)/2 或者 size/2都行
                for( int i = (size -1 - 1)/2; i >= 0; --i){
                    shiftDown(nums, i, size);
                }
                // 从堆中取元素
                for (int i = size - 1; i > 0; i --){
                    swap(nums, 0, i);           // i 元素被放到0位置
                    shiftDown(nums, 0, i);      // 重新堆0位置的元素shiftdown
                }
            }
    
            /**
             * 对第i个元素执行下移操作。大根堆
             * @param nums
             * @param i
             */
            public void shiftDown(int [] nums, int k, int size){
                // 用while 少了一次循环,避免了递归
                while( 2 * k + 1 < size){
                    int child = 2 * k + 1;      // 左孩子
                    if ( child + 1 < size && nums[child+1] > nums[child])
                        child += 1;             // 右孩子
    
                    if (nums[k] < nums[child]){
                        swap(nums, child, k);
                    }
    
                    k = child;                  // 从被交换的孩子开始,继续往下迭代。直到到达叶子节点
                }
            }
    
            public void swap(int[] a, int i, int j) {
                int temp = a[i];
                a[i] = a[j];
                a[j] = temp;
            }
        }
    
  • 相关阅读:
    HTTP Error 502.5
    ubuntu 换源 aliyun
    vsdbg 下载方法 使用下载工具下载后手动安装
    Asp.NET Core Nginx Ocelot ForwardedHeaders X-Forwarded-For
    ocelot性能测试
    Hyper V 内部网络(NAT)设置 配置固定IP / DHCP
    Powershell ExecutionPolicy 执行策略
    centos ftp服务器搭建 vsftpd 匿名访问配置方法 ftp 550 Failed to open file 错误处理
    powershell与linux bash对比
    virtualbox 配置记录
  • 原文地址:https://www.cnblogs.com/twilight0402/p/14065871.html
Copyright © 2011-2022 走看看