归并排序和堆排序 java实现

归并排序

官方介绍

归并排序(Merge Sort)是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并

用自己的话形容:给一个无序集合数组,使用分治思想,二分法进行拆分,将大问题拆分成小问题,从上往下把问题集合缩小到一,再从下往上,陆续把两个集合归并。从而得到一个有序集合。它的时间复杂性是O(nlogn).为什么呢?

数组长度是N,归并排序是用二分法将不停将问题集合缩小,直至集合大小为1.那么得到\frac{N}{2^{k}} = 1,k是树的深度,得到

k = log2(n). 每一层处理的节点数是N,那么时间复杂度就是O(nlogn)

这个算法的思想和快速排序有些类似。都是分治处理,区别是排序的顺序相反。快排是在分治的过程中排序,当分治到最后一层时,已经有序,后面没有操作了;归并排序是分治过程中只单纯获取到小集合,归并过程中将小集合合并排序。由于快排没有创建临时空间,所以它分治过程中是使用了比较和交换,导致快排算法是不稳定的;归并排序是要创建临时空间,将两个小集合合并进去,过程中无需交换,所以可以保持稳定性。所以归并是稳定的。

以下是我的java实现

import java.util.Arrays;

public class MergeSort {

    public static void main(String[] args) {
        int[] arr = {2,10,4,5,7,3,2,1,5,9,3,2,10,6,4,8,9,7,1};
        int[] dd = mergeS(arr, 0, arr.length - 1);
        System.out.println(Arrays.toString(dd));

    }

    static int[] mergeS(int[] arrs, int low, int high) {
        if (low == high) {
            int[] tmp = {arrs[low]};
            return tmp;
        }
        int mid = (high - low)/2 + low;
        //先获取左集合
        int[] left = mergeS(arrs, low, mid);
        //获取右集合
        int[] right = mergeS(arrs, mid + 1, high);
        //创建临时空间,将左右集合合并
        int[] tmp = new int[high - low + 1];
        int n = 0;
        int l1 = 0;
        int r1 = 0;
        while (!(l1 == left.length && r1 == right.length)) {
            //当左集合到底了,只处理右集合
            if (l1 == left.length) {
                tmp[n++] = right[r1++];
                continue;
            }
            //当右集合当底了,处理左集合
            if (r1 == right.length) {
                tmp[n++] = left[l1++];
                continue;
            }
            //比较元素大小,从小到大设置值到tmp上
            tmp[n++] = left[l1] < right[r1]? left[l1++]:right[r1++];
        }
        return tmp;
    }
}

 

堆排序

堆排序(英语:Heapsort)是指利用这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆的性质:即子结点的键值或索引总是小于(或者大于)它的父节点

自己理解:我们一般很容易理解数组和链表。那么堆是什么数据结构呢?堆是一个完全二叉树。数的结点有值。堆有两种

大根堆:父结点的值总是大于等于左右节点的值;

小跟堆:父结点的值总是小于等于左右节点的值;

大根堆是用于升序排序;小跟堆是用于降序排序。

大根堆,最大值总是在根结点。

堆排序,利用这个特性,可以获取到一个大根堆的最大值,然后将最大值移除,调整堆,就可以获取到新的最大值。如此递归,就可以获取到有序集合。

给一个无需集合,通过以下3步,就得到有序集合。

步骤一 构造初始堆。将给定无序序列构造成一个大顶堆(一般升序采用大顶堆,降序采用小顶堆)

步骤二 将堆顶元素与末尾元素进行交换,使末尾元素最大。

步骤三 然后继续调整堆,再将堆顶元素与末尾元素交换,得到第二大元素。如此反复进行交换、重建、交换。

 

以下是我的java实现

import java.util.Arrays;
import java.util.Collections;

public class HeapSort {

    public static void main(String[] args) {
        int[] arr = { 1, 3, 4, 5, 2, 6, 9, 7, 8, 0 };
//        heapAdjust(arr,0, arr.length);
//        System.out.println(Arrays.toString(arr));

        headSort(arr);
        System.out.println(Arrays.toString(arr));

    }
    
    //堆调整,给出父结点,对它的左右子树进行调整,构成一个该父结点起始的大根堆
    public static void heapAdjust(int[] arr, int index, int length) {
        int boy = 2 * index + 1;
        while (boy < length) {
            //对比左右子树,如果左子树大于右子树,则使用右子树
            if (boy + 1 < length && arr[boy] < arr[boy + 1]) {
                boy = boy + 1;
            }
            //父结点大于等于左右节点,无需调整。大根堆性质
            if (arr[index] >= arr[boy]) {
                return;
            }
            //交换父结点和最大子节点
            int tmp = arr[index];
            arr[index] = arr[boy];
            arr[boy] = tmp;
            //继续调整子节点
            index = boy;
            boy = 2 * index + 1;
        }
    }

    public static void headSort(int[] arr) {
        System.out.format("初始值: \t", arr.length -1);
        printPart(arr, 0, arr.length - 1);
        //这个last。这是一个公式,根据完全二叉树的性质,得到的最后一个非叶子结点的索引
        int last = arr.length / 2 -1;
        //构建大根堆  根据完全二叉树性质,从最后一个非叶子节点往前的节点,都是非叶子节点,都处理一遍堆调整,那么得到的就是一个大根堆
        for (int i = last; i >= 0;i--) {
            heapAdjust(arr, i, arr.length);
        }

        System.out.format("大根堆: \t", arr.length -1);
        printPart(arr, 0, arr.length - 1);
        int k = arr.length - 1;
        for(;k > 0;k--) {
           //将最大值根节点和最后一个节点交换,并且将最后一个节点移除出大根堆
            //为什么是移除最后一个节点?因为是要将最大值放在数组末尾,才能移除处理。
            int tmp = arr[k];
            arr[k] = arr[0];
            arr[0] = tmp;
            //注意k--,每次调整大根堆,都会把数组长度缩小。实现当前最大值移除大根堆的目的
            heapAdjust(arr, 0, k);

            System.out.format("第 %d 趟: \t", arr.length - k);
            printPart(arr, 0, arr.length - 1);
        }

    }

    public static void printPart(int[] list, int begin, int end) {
        for (int i = 0; i < begin; i++) {
            System.out.print("\t");
        }
        for (int i = begin; i <= end; i++) {
            System.out.print(list[i] + "\t");
        }
        System.out.println();
    }
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值