基于比较的三种O(NlogN)的排序

本文详细介绍了三种基于比较的O(NlogN)时间复杂度排序算法:堆排序、归并排序和快速排序。堆排序通过建堆和交换实现排序;归并排序利用分治策略和有序数组合并;快速排序则采用基准划分区间,并通过优化避免最坏情况。每种算法都结合了分治的思想,具有不同的特点和优化策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

堆排序:

对于堆排序来说,整个步骤还是比较繁琐,首先我们需要先进行建堆操作,从小到大就建大堆,从大到小就建小堆,建堆操作涉及到向下调整的过程,然后我们需要堆顶元素和数组最后一个元素进行交换,然后再进行从索引为0的位置向下调整,从而达到排序的效果。

1)建堆:

这里我们建大堆

public void createHeap(int[] array) {

    for (int i = (array.length - 1 - 1) / 2; i >= 0; i--) {
        adjust(i, array.length - 1, array);
    }
}

private void adjust(int parent, int useSize, int[] array) {
    int child = parent * 2 + 1;
    while (child < useSize) {
        if (child + 1 < useSize && array[child + 1] > array[child]) {
            child = child + 1;
        }
        if (array[child] > array[parent]) {
            int tmp = array[child];
            array[child] = array[parent];
            array[parent] = tmp;
            parent = child;
            child = parent * 2 + 1;
        } else {
            break;
        }
    }
}

2)进行原地堆排序:

public void heapSort(int[] array) {
    createHeap(array);
    int end = array.length - 1;
    while (end > 0) {
        int tmp = array[0];
        array[0] = array[end];
        array[end] = tmp;
        adjust(0, end, array);
        end--;
    }
}

为什么堆排序的时间复杂度是nlogN呢?

因为堆的底层是一棵完全二叉树,logN就代表树的深度,n代表进行排序的有n个元素。

归并排序:

归并排序是一个时间复杂度NlogN且是一个稳定的排序算法。归并的核心是在于并这个过程,给你两个数组,让你合并成一个有序数组,这就是归并的核心。归并排序算法也是分治法的经典应用之一。

public int[] mergeArray(int[] array1, int[] array2) {
    int[] arr = new int[array1.length + array2.length];
    int bs = 0;
    int be = array1.length - 1;
    int as = 0;
    int ae = array2.length - 1;
    int i = 0;
    while (bs <= be && as <= ae) {
        if (array1[bs] < array2[as]) {
            arr[i] = array1[bs];
            bs++;
            i++;
        } else {
            arr[i] = array2[as];
            as++;
            i++;
        }
    }
    while (bs <= be) {
        arr[i] = array1[bs];
        bs++;
        i++;
    }
    while (as <= ae) {
        arr[i] = array2[as];
        as++;
        i++;
    }
    return arr;
}

当我们懂得了归并的核心并之后,我们需要对整个数组进行一次一次的拆分

 

 

 进行拆分之后,我们可以就可以进行两个数组合并成一个有序数组的操作。

我们这里使用递归对整个数组进行区域划分。

public void mergeSort(int[] array, int begin, int end) {
    if (begin >= end) {
        return;
    }
    int mid = begin + ((end - begin) >>> 1);
    mergeSort(array, begin, mid);
    mergeSort(array, mid + 1, end);
    merge(array, begin, end, mid);
}

private void merge(int[] array, int begin, int end, int mid) {
    int[] tmp = new int[end - begin + 1];
    int bs = begin;
    int be = mid;
    int as = mid + 1;
    int ae = end;
    int i = 0;
    while (bs <= be && as <= ae) {
        if (array[bs] < array[as]) {
            tmp[i++] = array[bs++];
        } else {
            tmp[i++] = array[as++];
        }
    }
    while (bs <= be) {
        tmp[i++] = array[bs++];
    }
    while (as <= ae) {
        tmp[i++] = array[as++];
    }
    for (int j = 0; j < i; j++) {
        array[j + begin] = tmp[j];
    }
}

 快速排序:

快速排序是一个效率非常高的排序算法,平均的时间复杂度为NlogN,但是在最差情况下的时间复杂度为O(n^2)。最差的情况我们等等再进行讨论。

快速排序也是利用了分治思想,把一个数组分成一个个区间进行处理,不同与归并,快排使用了基准这个概念,我们以第一个元素为基准,把小于等于这个基准的值放在左区间,把大于等于这个基准的值放在右区间。然后进行分治,最后达到排序的效果。

1)找基准:

private int partition(int[] array, int begin, int end) {
    int tmp = array[begin];
    while (begin < end) {
        while (begin < end && array[end] >= tmp) {
            end--;
        }
        array[begin] = array[end];
        while (begin < end && array[begin] <= tmp) {
            begin++;
        }
        array[end] = array[begin];
    }
    array[begin] = tmp;
    return begin;
}

2)进行快速排序:

public void quickSort(int[] array, int begin, int end) {
    if (begin >= end) {
        return;
    }
    int mid = findMid(array, begin, end);
    int pivot = partition(array, begin, end);
    quickSort(array, begin, pivot - 1);
    quickSort(array, pivot + 1, end);
}

刚刚我们提过的最差情况,那就是分治区间只有左区间或者只有右区间,这时候时间复杂度就高了,这时候我们有三数取中法和随机选择法进行优化。三数取中法,就是让第一个元素和最后一个元素和中间元素进行大小比较,然后让数值在中间的数和第一个元素进行交换,达到不会有单区间的情况发生

public int findMid(int[] array, int begin, int end) {
    int mid = begin + ((end - begin) / 2);
    if (array[begin] > array[end]) {
        if (array[mid] > array[begin]) {
            return begin;
        } else if (array[mid] < array[end]) {
            return end;
        } else {
            return mid;
        }
    } else {
        if (array[mid] < array[begin]) {
            return begin;
        } else if (array[mid] > array[end]) {
            return end;
        } else {
            return mid;
        }
    }
}

然后我们在快速排序的过程中可以进行优化处理:

public void quickSort(int[] array, int begin, int end) {
    if (begin >= end) {
        return;
    }
    int mid = findMid(array, begin, end);
    int tmp = array[begin];
    array[begin] = array[mid];
    array[mid] = tmp;
    int pivot = partition(array, begin, end);
    quickSort(array, begin, pivot - 1);
    quickSort(array, pivot + 1, end);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值