【leetcode】排序数组:快速排序、堆排序、归并排序

【leetcode】排序数组:快速排序、堆排序、归并排序

  • 1、题目
  • 2、题解
    • [2.1 快速排序](#2.1 快速排序)
    • [2.2 堆排序](#2.2 堆排序)
    • [2.3 归并排序](#2.3 归并排序)

1、题目

2、题解

2.1 快速排序

随机快排的写法有很多种,但是针对极端case,比如中间有很多重复的数字的case,有些写法会超时。

下面这种写法,两边下标的值如果相等的话,i和j可以同时向内收缩,可以很好地处理这种极端case,值得学习和理解

java 复制代码
伪代码步骤
1、选第一个下标的值为partition
2、i = 第一个下标+1
3、j = 最后一个下标
4、while true 循环
    4.1 i<=最后一个下标 && i下标的值<partition:i++
    4.2 j>=第一个下标+1 && j下标的值>partition:j--
    4.3 如果i>=j:跳出循环
    4.4 交换下标i和下标j位置的值,同时i++, j--
5、交换第一个下标和j位置的值
6、继续快排

class Solution {
    private static final Random random = new Random();

    public int[] sortArray(int[] nums) {
        quickSort(nums, 0, nums.length-1);
        return nums;
    }

    private void quickSort(int[] nums, int start, int end) {
        if (start >= end) {
            return;
        }

        int randomIdx = random.nextInt(start, end+1);
        swap(nums, start, randomIdx);

        // 选第一个元素为partition
        int partition = nums[start];
        int i = start+1;
        int j = end;
        while (true) {
            // 下标i的左侧一定小于partition
            while (i<=end && nums[i]<partition) {
                i++;
            } 
            // 下标j的右侧一定大于partition
            while (j>=start+1 && nums[j]>partition) {
                j--;
            }
            if (i >= j) {
                break;
            }
            // 此时下标j一定小于等于partiton,下标i一定大于等于partition。
            // 两边同时向内收缩,可以加快运行时间,避免部分极端case超时(比如中间有大量重复的数字的case)
            swap(nums, i, j);
            i++;
            j--;
        }
        swap(nums, start, j);

        quickSort(nums, start, j-1);
        quickSort(nums, j+1, end);
    }

    private void swap(int[] nums, int left, int right) {
        int tmp = nums[right];
        nums[right] = nums[left];
        nums[left] = tmp;
    }
}

提交结果:

2.2 堆排序

堆排序:

1、从后向前遍历元素,构建最大堆:当前位置(i)的左子节点(2i+1)或者右子节点(2 i+2)的值比当前位置的值大,则将当前位置与子节点位置的值交换,然后对子节点继续做heapify操作。即如果当前位置比左子节点和右子节点都大,则不需要调整。

2、从后先前遍历元素:先交换第一个位置(heapify操作后肯定是最大值)和最后一个位置的值,然后对第一个位置做heapify操作,操作完成后继续遍历。

java 复制代码
class Solution {
    public int[] sortArray(int[] nums) {
        int len = nums.length;

        // 1. 构建最大堆(从最后一个非叶子节点开始)
        for (int i = len/2-1; i >= 0; --i) {
            heapify(nums, len, i);
        }

        // 2. 逐个从堆顶取出元素,放到数组末尾
        for (int i = len - 1; i > 0; i--) {
            // 将当前堆顶元素(最大值)与数组末尾元素交换
            swap(nums, 0, i);
            // 调整剩余元素,使其保持最大堆性质
            heapify(nums, i, 0);
        }

        return nums;
    }

    /**
     * 调整堆,使其满足最大堆性质
     * @param arr 待调整数组
     * @param n 堆的大小(需要调整的元素个数)
     * @param i 当前要调整的节点索引
     */
    private static void heapify(int[] arr, int n, int i) {
        int largest = i;        // 初始化最大元素为当前节点
        int left = 2 * i + 1;   // 左子节点索引
        int right = 2 * i + 2;  // 右子节点索引
        
        // 如果左子节点存在且大于当前最大节点
        if (left < n && arr[left] > arr[largest]) {
            largest = left;
        }
        
        // 如果右子节点存在且大于当前最大节点
        if (right < n && arr[right] > arr[largest]) {
            largest = right;
        }
        
        // 如果最大元素不是当前节点,需要交换并递归调整
        if (largest != i) {
            swap(arr, i, largest);
            // 递归调整受影响的子树
            heapify(arr, n, largest);
        }
    }

    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

提交结果:

2.3 归并排序

归并排序

1、开始下标大于等于末尾下标时,直接范围

1、找到开始下标和末尾下标的中点,将数组一分为2,继续递归进行排序

2、左半部分和右半部分有序后,新建一个相同长度的新数组,利用2个指针,依次将较小值赋值给新数组。最后将整理有序后的新数组再赋值回原数组。

java 复制代码
class Solution {
    public int[] sortArray(int[] nums) {
        mergeSort(nums, 0, nums.length-1);
        return nums;
    }

    private void mergeSort(int[] nums, int start, int end) {
        if (start>=end) {
            return;
        }

        int mid = (start+end) / 2;
        mergeSort(nums, start, mid);
        mergeSort(nums, mid+1, end);

        int[] numsCopy = new int[end-start+1];
        int idx = 0;
        int left = start;
        int right = mid+1;
        while (left<=mid && right<=end) {
            if (nums[left]<=nums[right]) {
                numsCopy[idx++] = nums[left++];
            } else {
                numsCopy[idx++] = nums[right++];
            }
        }
        while (left<=mid) {
            numsCopy[idx++] = nums[left++];
        }
        while (right<=end) {
            numsCopy[idx++] = nums[right++];
        }

        for (int i=start; i<=end; i++) {
            nums[i] = numsCopy[i-start];
        }
    }
}

提交结果:

相关推荐
小O的算法实验室2 小时前
2025年AEI SCI1区TOP,基于自适应进化算法的城市空中交通多目标枢纽选址,深度解析+性能实测
算法·论文复现·智能算法·智能算法改进
咘噜biu2 小时前
Java SpringBoot后端Filter包装请求(新增/覆盖请求头)
java·spring boot·filter·requestwrapper
历程里程碑2 小时前
LeetCode 283:原地移动零的优雅解法
java·c语言·开发语言·数据结构·c++·算法·leetcode
程序猿零零漆2 小时前
Spring之旅 - 记录学习 Spring 框架的过程和经验(五)Spring的后处理器BeanFactoryPostProcessor
java·学习·spring
虾说羊2 小时前
java中的反射详解
java·开发语言
星火飞码iFlyCode2 小时前
iFlyCode实践规范驱动开发(SDD):招考平台报名相片质量抽检功能开发实战
java·前端·python·算法·ai编程·科大讯飞
廋到被风吹走2 小时前
【Spring】HandlerInterceptor解析
java·后端·spring
leaves falling2 小时前
c语言-根据输入的年份和月份,计算并输出该月份的天数
c语言·开发语言·算法
jghhh012 小时前
锥束CT(CBCT)三维重构算法:FDK算法详解与实现
线性代数·算法·重构