05_排序

堆排序

  • 以最大堆为例,如果删除一个最大堆的堆顶(并不是完全删除,而是跟末尾的节点交换位置),经过自我调整,第二大的元素就会被交换上来,称为最大堆新堆的堆顶。由于二叉堆的这个特性,每一次删除旧的堆顶,调整后的新堆顶都是大小仅次于旧堆顶的节点,那么只要反复删除堆顶,反复调整二叉堆,所得到的集合就是一个有序集合。
    在这里插入图片描述

  • 堆排序算法步骤:

  1. 把无序数组构建成二叉堆,需要从小到大排序,则构建最大堆;如果需要从大到小排序,则构建最小堆。
  2. 循环删除堆顶元素,替换到二叉堆的末尾,调整堆产生新的堆顶。
  • 时间复杂度:
    • 第一步,把无序数组构建成二叉堆,这一步的时间复杂度是O(n)。
    • 第二步,需要进行n-1次循环,每次循环调用一次downAdjust方法,所以第二步是(n-1)*logn,所以总的时间复杂度是O(nlogn).
  • 空间复杂度:
    • 因为没有开辟额外的集合空间,所以空间复杂度是O(1).
  • 快速排序 VS 堆排序
    • 相同点:堆排序和快速排序的平均时间复杂度都是O(nlogn),并且都是不稳定排序。
    • 不同点:
      • 快速排序的最坏时间复杂度是O(n**2),而堆排序的最坏时间复杂度稳定在O(nlogn)
      • 快速排序的空间平均复杂度是O(logn),而堆排序的空间复杂度是O(1).
  • 代码
/*下沉调整*/
public static void downAdjust(int[] array, int parentIndex, int length)
{
    
    
    int temp = array[parentIndex];
    int childIndex = 2 * parentIndex + 1;
    while(childIndex < length)
    {
    
    
        if(childIndex + 1 < length && array[childIndex + 1] > array[childIndex])
        {
    
    
            childIndex++;
        }
        if(temp >= array[childIndex])
        {
    
    
            break;
        }
        array[parentIndex] = array[childIndex];
        parentIndex = childIndex;
        childIndex = parentIndex * 2 + 1;
    }
    array[parentIndex] = temp;
}

/*堆排序(升序)*/
public static void heapSort(int[] array)
{
    
    
    // 1. 把无序数组构成最大堆
    for(int i = (array.length - 2) / 2; i >= 0; i--)  
    // 这个地方部分奇偶,都是一样方法找到父节点。
    {
    
    
        downAdjust(array, i, array.length);  // 创建对就是遍历非叶子节点,并下沉调整即可
    }

    System.out.println(Arrays.toString(array));

    // 2. 循环删除堆顶元素,移到集合尾部,调整产生新的堆顶。
    for(int i = array.length - 1; i > 0; i--)
    {
    
    
        int temp = array[i];
        array[i] = array[0];
        array[0] = temp;
        downAdjust(array, 0, i); // 最后是i,不是array.length
    }

}

public static void main(String[] args)
{
    
    
    int[] arr = new int[]{
    
    1, 2, 2, 6, 5, 7, 8, 9, 10, 0};+
    heapSort(arr);
    System.out.println(Arrays.toString(arr));
}

计数排序

  • 一个例子:
    • 问题:给出一个学生成绩表,要求按照成绩从低到高排序,如果成绩相同,则遵循着原表固有顺序。在这里插入图片描述
    • 数据变形:其实就是从统计数组的第二个元素开始,每个元素都加上前面所有元素之和。在这里插入图片描述
    • 目的:就是让统计数组存储的元素值,等于相应整数的最终排序位置的序号,例如下标9的元素值为5,代表最终9排在第五位,也就是99排在第五位,因为有个差值:最大值-最小值.
    • 过程:第一步遍历成绩表最后一行小绿的成绩,小绿的成绩是95,下标是5的元素,值是4,代表小绿的成绩排名在第四位。同时,4-1=3,该值改为3,下次碰到95分的就是第三位。其他的一次类推。
    • 属于稳定排序。
  • 复杂度
    • 时间复杂度:如果原始数据规模是n,最大和最小整数的差值是m,则为3n+m,为O(n + m).
    • 空间复杂度:如果不考虑结束数组,只考虑计数数组大小的话,是O(m),主要看内部new了多少。
  • 局限性
    • 当数列最大和最小差距过大时,并不适用计数排序。
    • 当数列元素不是整数时,也不适合。
  • 代码
public static int[] countSort(int[] array)
{
    
    
    // 1. 得到数列的最大和最小值,并算出差值d
    int max = array[0];
    int min = array[0];
    for(int i=0; i<array.length; i++)
    {
    
    
        if(array[i] > max)
        {
    
    
            max = array[i];
        }
        if(array[i] < min>)
        {
    
    
            min = array[i]
        }
    }
    int d = max - min;
    // 2. 创建统计数组并统计对应元素的个数
    int[] countArray = new int[d + 1];
    for(int i=0; i<array.length; i++)
    {
    
    
        countArray[array[i] - min]++;
    }
    // 3. 统计数组做变形,后面的元素等于前面元素之和。
    for(int i=1; i<countArray.length; i++)
    {
    
    
        countArray[i] += count[i - 1];
    }
    // 4. 倒序遍历原始数列,从统计数组找到正确位置,输出到结果数组中
    int[] sortedArray = new int[array.length];
    for(int i=array.length-1; i>=0; i--)  // 这个地方可以从前往后遍历,也可以从后往前遍历,之所从后往前遍历是因为这样是稳定的。
    {
    
    
        
        sortedArray[countArray[array[i] - min] - 1] = array[i];
        countArray[array[i] - min]--;
    }
    return sortedArray;

}

public static void main(String[] args)
{
    
    
    int[] array = new int[] {
    
    95, 94, 91, 98, 99, 90, 99, 93, 91, 92};
    int[] sortedArray = countSort(array);
    System.out.println(Arrays.toString(sortedArray));
}

桶排序

  • 步骤在这里插入图片描述
    • 创建桶。这里创建的桶数量等于原始数列的元素数量,除最后一个桶只包含数列最大值外,前面各个桶的区间按照比例来确定。 区间跨度 = (最大值 - 最小值) / (桶的数量 - 1)
    • 第二步遍历原始数列,把元素对号入座放入各个桶中。在这里插入图片描述
    • 第三步对桶中内部元素分别进行排序。
    • 遍历所有桶,输出所有元素
  • 时间复杂度是O(n), 空间复杂度是O(n)
  • 缺点:在元素分布极不均衡,第一个同种有n - 1个元素,最后一个桶中有一个元素。此时时间复杂度是O(nlogn),而且还白白创建了很多空桶。
  • 代码
public static double[] bucketSort(double[] array)
{
    
    
    // 1. 得到数列的最大值和最小值
    double max = array[0];
    double min = array[0];
    for(int i=0; i<array.length; i++)
    {
    
    
        if(array[i] > max)
        {
    
    
            max = array[i];
        }
        if(array[i] < min>)
        {
    
    
            min = array[i]
        }
    }
    double d = max - min;

    // 2. 初始化桶
    int bucketNum = array.length;
    ArrayList<LinkedList<Double>> bucketList = new ArrayList<LinkedList<Double>>(bucketNum);
    for(int i=1; i<bucketNum; i++)
    {
    
    
        bucketList.add(new LinkedList<Double>());
    }
    // 3. 遍历原始数组,将每个元素放入桶中。
    for(int i=1; i<array.length; i++)
    {
    
    
        int num = (int)((array[i] - min) * (bucketNum - 1) / d);
        bucketList.get(num).add(array[i]);
    }
    // 4. 对每个桶内部进行排序
     for(int i=1; i<bucketList.size(); i++)
    {
    
    
        // jdk 底层采用归并排序或者优化后的归并排序
        Collections.sort(bucketList.get(i));
    }
    // 5. 输出全部元素
    double[] sortedArray = new double[array.length];
    int index = 0;
    for(LinkedList<Double> list : bucketList){
    
    
        for(double element : list){
    
    
            sortedArray[index] = element;
            index++;
        }
    }
    return sortedArray;
}

public static void main(String[] args){
    
    
    double[] array = new double[]{
    
    4.12,6.421,0.0023,3.0,2.123,8.122,4.12, 10.09};
    double[] sortedArray = bucketSort(array);
    System.out.println(Arrays.toString(sortedArray));

}

排序总结在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_42310008/article/details/112846764
今日推荐