算法入门之排序算法

本文深入介绍了排序算法的基础与进阶,重点讲解了堆排序、快速排序和归并排序的原理及实现。堆排序通过建立最大堆,依次取出最大值来排序;快速排序利用分治思想,通过选取基准元素快速划分数组;归并排序则通过递归合并有序子数组完成排序。这些排序算法在不同场景下各有优势,理解它们不仅有助于提升编程能力,还能解决实际问题。
摘要由CSDN通过智能技术生成

1. 概述

排序算法分为基础和进阶版的,我这里区分的一点主要是根据(平均)时间复杂度

  • 基础:冒泡排序、选择排序
  • 进阶:堆排序、快速排序、归并排序

其实对于进阶排序来说,大家不光要掌握排序的应用,在排序之外的思想也需要掌握:

  • 堆是一种很有用的数据结构,在 Java 里也叫优先级队列,在做“最大”、“最小”相关的题目时我们应该首先想到堆这种数据结构,在存入数据的时候就按照一定的顺序排列,且时间复杂度为 O(logK) 这里的 K 是堆的容量,取堆顶(最大或者最小值)的时间复杂度为 O(1)
  • 快速排序的分治思想在解决 TopK 的问题时有奇效,因为我们求 TopK 的问题时不需要将整个序列排序且求出前的这 K 个元素之间也不需要有序。分治思想简单说就是经过处理之后,当前位置之前的数字都比当前位置的数小(比当前数字小的这些数之间可能是无序的),当前位置之后的数字都比当前位置的数字大(比当前数字大的这些数之间也可能是无序的)。比如我们要求数组 [5, 3, 2, 1, 4, 6] ,当前的数字为4,则分治后的数组可能是这样的: [3, 2, 1, 4, 6, 5] ,4 之前的数字 [3, 2, 1] 都比 4小,4 之后的数字 [6, 5] 都比4大。回到 TopK 的问题上来,如果我们要求 [5, 3, 2, 1, 4, 6] 中前 3 小的数字,我们只需要找到第 3 个位置的数字即可,因为第 3 个位置前面的两个数字肯定比第 3 个位置的数小,第 3 个位置后面的数字肯定比第 3 个位置的数字大(不可能是前3小的),所以最终的结果是第 3 个位置加上前两个比它小的数字!
  • 归并排序在求逆序对的时候比较有效:

2. 堆排序

相较于堆排序这种排序算法,堆这种数据结构更为重要

/**
 * @Author Jason
 * @Date 2021/9/11 11:09 上午
 * @Description 堆排序
 * 1、 建立(大根)堆,每一个元素跟其父元素进行比较,如果比父元素大就替换父元素
 * 2、 得到堆顶元素,为最大元素
 * 3、 去掉堆顶,将堆最后一个元素放置到堆顶
 * 4、 调整堆结构,堆顶为第二大元素
 * 5、 重复3、4步骤,直至堆变空
 */
public class HeapSort {
    public static void main(String[] args) {
        int[] arr = {-4, 0, 7, 4, 9, -5, -1, 0, -7, -1};
        heapSort(arr);
        for (int i : arr) {
            System.out.printf("%3d", i);
        }
    }

    public static void heapSort(int[] arr) {
        int length = arr.length;

        if (length < 1) return;

        // 建立堆
        for (int i = 0; i < length; i++) {
            heapInsert(arr, i);
        }

        // 堆的大小
        int size = length;

        // 3~5的步骤
        while (size > 0) {
            // 交换首尾元素,相当于去掉堆顶
            int last = arr[size - 1];
            arr[size - 1] = arr[0];
            arr[0] = last;
            // 堆结构-1
            size--;
            // 调整新堆结构
            adjustHeap(arr, 0, size);
        }

    }


    /**
     * 建立大根堆
     *
     * @param arr   数组
     * @param index 下标,会一直向上找父、父的父...
     */
    public static void heapInsert(int[] arr, int index) {
        // 父元素的下标
        int fatherIndex = (index - 1) / 2;
        while (fatherIndex >= 0 && arr[index] > arr[fatherIndex]) {
            // 交换index和其父元素
            int temp = arr[index];
            arr[index] = arr[fatherIndex];
            arr[fatherIndex] = temp;
            // 更新父元素下标
            index = fatherIndex;
            fatherIndex = (index - 1) / 2;
        }
    }

    public static void adjustHeap(int[] arr, int start, int end) {
        int left = 2 * start + 1; // start节点的左孩子的下标
        int right = left + 1; // start节点的右孩子的下标
        int largest = 0;
        while (left < end) {
            // 找到左右孩子中最大的
            if (right < end && arr[right] > arr[left]) {
                largest = left + 1;
            } else {
                largest = left;
            }
            // 如果start比左右孩子中最大的还要大,已经是大根堆
            if (arr[start] > arr[largest]) break;

            // 否则交换start和largest
            int temp = arr[start];
            arr[start] = arr[largest];
            arr[largest] = temp;

            // 更新start
            start = largest;
            left = (start << 1) + 1;
            right = left + 1;
        }
    }
}

3. 快速排序

import java.util.Arrays;
import java.util.Random;

/**
 * @Author Jason
 * @Date 2021/10/1 11:55 上午
 * @Description
 */
public class quickSort {
    static Random random = new Random(System.currentTimeMillis());

    public static void main(String[] args) {
        for (int i = 0; i < 30; i++) {
            int len = random.nextInt(20) + 1;
            int[] arr = new int[len];
            for (int j = 0; j < len; j++) {
                arr[j] = random.nextInt(30) + 1;
            }
            int[] stand = new int[len];
            System.arraycopy(arr, 0, stand, 0, len);
            QuickSort(arr, 0, len - 1);
            Arrays.sort(stand);
            for (int j = 0; j < len; j++) {
                if (arr[j] != stand[j]) {
                    System.out.println("wrong!");
                }
            }
        }
        System.out.println("Done");

    }

    public static void QuickSort(int[] arr, int left, int right) {
        if (left < right) {
            int mid = Partition(arr, left, right);
            QuickSort(arr, left, mid - 1);
            QuickSort(arr, mid + 1, right);
        }
    }


    public static int Partition(int[] arr, int left, int right) {
        int pivot = arr[left];
        while (left < right) {
            // 先从最后开始向左找小于pivot的点
            while (left < right && arr[right] >= pivot) right--;
            // 如果下标没越界,则将从右往左找到的大于小于pivot的点置于left位置
            if (left < right) arr[left++] = arr[right];

            // 从左往右找大于pivot的点
            while (left < right && arr[left] <= pivot) left++;
            // 如果下标没越界,则将从左右往右找到的大于pivot的点置于right的位置
            if (left < right) arr[right--] = arr[left];
        }
        // left和right相等时,将pivot的值置到该位置,则此时pivot左侧的点都比它小,其右侧的点都比它大
        arr[left] = pivot;
        return left;
    }
}

4. 归并排序

/**
 * @Author Jason
 * @Date 2022/1/19 1:13 下午
 * @Description
 */
public class mergeSort {
    public static void main(String[] args) {
        int[] nums = {8, 4, 5, 7, 1, 3, 6, 2};
        mergeSort(nums, 0, nums.length - 1, new int[nums.length]);
        System.out.println(nums);
    }

    public static void mergeSort(int[] arr, int left, int right, int[] temp) {
        if (left < right) { // left >= right 说明待排序的区间为空或者只有一个元素,不需要排序
            int mid = left + ((right - left) >> 1);
            mergeSort(arr, left, mid, temp);
            mergeSort(arr, mid + 1, right, temp);
            merge(arr, left, mid, right, temp);
        }
    }


    public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
        int l = left, r = mid + 1, idx = 0;
        System.out.println("left=" + left + ", right =" + right + ", mid =" + mid);
        while (l <= mid && r <= right) {
            if (arr[l] < arr[r]) {
                temp[idx++] = arr[l++];
            } else {
                temp[idx++] = arr[r++];
            }
        }

        while (l <= mid) {
            temp[idx++] = arr[l++];
        }

        while (r <= right) {
            temp[idx++] = arr[r++];
        }

        // 将 temp 数组的值拷贝到 arr 中
        for (int i = 0; i < idx; i++) {
            arr[left + i] = temp[i];
        }
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值