4.2堆

4.2堆

堆就我理解就是用数组表示一个完全二叉树,用上hashcode应该就是能更快找到数据

4.2.1堆上的运算

堆上的运算在此进行的shiftdown,不演示shiftup,为了实现最终的heapsort,shiftdown本身就是对数组进行堆排序

下面展示的是书中的伪代码:

过程:SHIFT-DOWN
输入:数组H[1···n]和位于1和n之间的索引i
输出:下移H[i],以使它不小于子结点
	1.done <- false
	2.if 2i > n then exist	{结点i是叶子结点}	//2i>n都是叶子结点(可以自行检验),如果是叶子结点则无法进行堆排序,则退出
	3.repeat
	4.	i <- 2i	//为当前结点的左子结点
	5.	if i+1 <= n and key(H[i+1])>key(H[i]) then i <- i+1	//i+1为右结点,右结点大于左节点i+1,防止总是左节点跟根结点进行互换,而i+1则为右结点,右结点也能得到充分排序 
	6.	if key(H[i/2]) < key(H[i]) then 互换H[i]和H[i/2]	//互换左子节点和父结点,因为满二叉树左结点总是存在,右结点不一定
	7.	else done <- true	//已经排序号,不需要互换,直接退出排序
	8.	end if
	9.until 2i > n or done

此处没有数组长度也就是i,所以无法进行代码演示,代码演示将在下面的MakeHeap中做出

4.2.2堆的创建

直接贴出伪代码:

过程:MAKEHEAP
输入:n个元素的数组A[1···n]
输出:A[1···n]转换成堆
	1.for i <- n/2 downto 1	// 进入shiftdown时,i会乘以2,所以n/2为自底向上进行shiftdown(从叶子结点出发),为1的时候,1代表最上面的根,已经排序好,所以退出循环
	2.shiftdown(A,1)

下面展示的是Java的实现:

package com.sheye;

/**
 * @author Sheye
 * @date 2019-10-17 17:33
 */
public class MakeHeap {
    private static int[] A = {3,4,5,5,7,9,10,11,17,20};

    static void shiftDown(int[] A,int i) {
        int n = A.length;
        boolean done = false;
        if (2 * i > n + 1) {
            return;
        }
        do {
            i = 2 * i;
            if (i + 1 <= n && A[i + 1 - 1] > A[i - 1]) {    
                i = i + 1;
            }
            if (A[i / 2 - 1] < A[i - 1]) {  
                int tmp = A[i - 1];
                A[i - 1] = A[i / 2 - 1];
                A[i / 2 - 1] = tmp;
            } else {
                done = true;
            }
        }
        while (!(2 * i > n  || done)) ;
        }

    static void makeHeap(int[] A,int n){
        for (int i=n/2;i>=1;i--){	//此处对i不进行处理,直接在交换的时候在数组下标里-1则完成
            shiftDown(A,i);
        }
        for (int j = 0; j < A.length; j++) {
            System.out.print(A[j]+",");
        }
    }
    public static void main(String[] args) {
        int n = A.length;
        makeHeap(A,n);
    }
}

贴上运行结果

排序的结果如下:
20,17,10,11,7,9,5,3,5,4,

4.2.3堆排序

堆排序就是对一个堆进行一个类似于选择排序的排序

HEAPSORT
输入:n个元素的数组A[1···n]
输出:以非降序排列的数组A
	1.MAKEHEAP
	2.for j <- downto 2
	3.	互换A[1]和A[j]	
	4.	shiftdown(A[1···j-1],1)	//代码中用MakeHeap (A[1..j-1])代替此行
	5.end for

上传代码之前先述说自己遇到的问题:

  1. 一开始,我们都在执着于下标是0还是1的问题,直到今天,我再重新看了看MAKEHEAP,算法中的n应该指的是实际二叉树里面的结点数(或者数组的长度),如果进行结点的大小的比较,或者交换值,直接再在原有的位置下标下-1就足够了(A.get(j-1))
		makeHeap(A, n);  //10个数建立堆
        for (int j = n; j >= 2; j--) {	//最后一个已经自动归位,到1则跳出循环
            int tmp1 = A.get(j - 1);
            int tmp2 = A.get(0);
            A.set(0, tmp1);
            A.set(j - 1, tmp2);    //因为堆的最左边总是最大值,将最大值移到数组的最后一位
            makeHeap(A, j - 1);    //排序完之后(移动最大的数到最右边),再将剩余的数(第一次是9个数)进行堆的建立
            for (int a : A) {
                System.out.print(a + ",");
            }
            System.out.println("");
        }

为了更简便使用heapsort,java的实现会与伪代码有出入,比如shiftDown(A, n, i)的三形参,我想有备注大家可以理解。

下面展示的是Java的实现:

package com.sheye;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Sheye
 * @date 2019-10-17 19:55
 */
public class HeapSort{
    private static List<Integer> A = new ArrayList();

    static void shiftDown(List<Integer> A, int n, int i) {
        boolean done = false;
        if (2 * i > n) {
            return;
        }
        do {
            i = 2 * i;
            if (i + 1 <= n && A.get(i + 1 - 1) > A.get(i - 1)) {
                i = i + 1;
            }
            if (A.get(i / 2 - 1) < A.get(i - 1)) {
                int tmp1 = A.get(i / 2 - 1);
                int tmp2 = A.get(i - 1);
                A.set(i - 1, tmp1);
                A.set(i / 2 - 1, tmp2);
            } else {
                done = true;
            }
        }
        while (!(2 * i > n || done));
    }

    static void makeHeap(List<Integer> A, int n) {
        for (int i = n / 2; i >= 1; i--) {
            shiftDown(A, n, i);   //A为数组A,n为将要建立堆的数组大小,i为数组1···n的索引,也就是当前结点
        }
    }

    static void heapSort(List<Integer> A, int n) {
        makeHeap(A, n);  //10个数建立堆
        for (int j = n; j >= 2; j--) {	//最后一个已经自动归位,到1则跳出循环
            int tmp1 = A.get(j - 1);
            int tmp2 = A.get(0);
            A.set(0, tmp1);
            A.set(j - 1, tmp2);    //因为堆的最左边总是最大值,将最大值移到数组的最后一位
            makeHeap(A, j - 1);    //排序完之后(移动最大的数到最右边),再将剩余的数(第一次是9个数)进行堆排序
            for (int a : A) {
                System.out.print(a + ",");
            }
            System.out.println("");
        }
    }

    public static void main(String[] args) {
        for (int i = 1; i <= 10; i++) {	//测试数据为1到10
            A.add(i);
        }
        int n = A.size();
        heapSort(A,n);
    }
}

为了方便大家查看每次排序的结果,特意输出每次排序结果

贴上运行结果:

每一次循环排序的结果如下:
9,8,7,4,5,6,3,1,2,10,
8,5,7,4,2,6,3,1,9,10,
7,5,6,4,2,1,3,8,9,10,
6,5,3,4,2,1,7,8,9,10,
5,4,3,1,2,6,7,8,9,10,
4,2,3,1,5,6,7,8,9,10,
3,2,1,4,5,6,7,8,9,10,
2,1,3,4,5,6,7,8,9,10,
1,2,3,4,5,6,7,8,9,10,

前段时间一直使用数组进行算法的实现,但是数组始终不能进行删除和替换,虽然此处用数组进行也没问题,但是之后的算法我都将用list进行,list底层就是数组,还能进行删除与替换,可以实现数组无法实现的功能,对了,还有本人能力浅薄,对算法的理解难免有错误,望大家指出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值