算法入门-排序2

第三部分:排序

324.摆动排序II(中等)

题目:给你一个整数数组 nums,将它重新排列成 nums[0] < nums[1] > nums[2] < nums[3]... 的顺序。给你一个整数数组 nums,将它重新排列成 nums[0] < nums[1] > nums[2] < nums[3]... 的顺序。

你可以假设所有输入数组都可以得到满足题目要求的结果。你可以假设所有输入数组都可以得到满足题目要求的结果。

示例 1:

输入:nums = [1,5,1,1,6,4]
输出:[1,6,1,5,1,4]
解释:[1,4,1,5,1,6] 同样是符合题目要求的结果,可以被判题程序接受。

第一种思路:

这道题目挺值得思考的~

摆动排序的目标是使数组遵循特定的模式:nums[0] < nums[1] > nums[2] < nums[3] > nums[4],依此类推。因此,对于给定的数组 nums,可以将其分成两个部分:较小的元素和较大的元素,然后交替插入这两个部分的元素。

  1. 排序

    • 首先对数组进行排序,以便我们可以轻松地访问较小的和较大的元素。排序后,较小的元素在数组的前面,较大的元素在后面。

  2. 划分数组

    • 找到中间位置 mid,它将用于分别指向较小元素和较大元素的范围。对于长度为 n 的数组,较大的元素是从 mid 开始的。

  3. 交替填充

    • 使用一个循环,依次将较小的元素和较大的元素填入原数组nums中。具体地:

      • 在偶数位置(nums[0], nums[2], ...)填入较小的元素(从排序后的数组的中间向前),j 从 mid - 1 开始,向前遍历(较小元素),

      • 在奇数位置(nums[1], nums[3], ...)填入较大的元素(从排序后的数组的最后向前),k 从 mid - 1 开始,向前遍历(较大元素)。

通过条件检查,确保在创建新的排序结构时不会越界。

class Solution {  
    public void wiggleSort(int[] nums) {  
        int[] arr = nums.clone();  
        Arrays.sort(arr);  
        int n = nums.length;  
        int mid = (n + 1) / 2; // 中间索引  
        int j = mid - 1; // 较小元素的最后索引  
        int k = n - 1;   // 较大元素的最后索引  
        
        // 将新数组中的元素交替填充到原数组  
        for (int i = 0; i < n; i++) {  
            if (i % 2 == 0) {  
                nums[i] = arr[j--]; // 偶数索引填充较小元素  
            } else {  
                nums[i] = arr[k--]; // 奇数索引填充较大元素  
            }  
        }  
    }  
}

官方的其他方法看得有点迷~,这里就不讲述了。

56.合并区间(中等)

题目:以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间

示例 1:

输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].

第一种思路:

看完题目立刻想到(406.根据身高重建队列)可以先将数组进行排序,这样排完序后的二维数组就是:

以前边界为第一关键字升序,以后边界为第二关键字升序排列,然后遍历循环这个二维数组,先比较第 i 个数组后边界和第 i + 1个数组的前边界:

  • 如果第 i 个数组后边界 >= 第 i + 1个数组的前边界代表存在重叠,再比较第 i 个数组后边界和第 i + 1个数组的后边界:

    • 如果第 i 个数组后边界 <= 第 i + 1个数组的后边界:合并区间,更新下一个区间的起始值为当前区间的起始值。

    • 如果第 i 个数组后边界 > 第 i + 1个数组的后边界:如果当前区间的结束值大于下一个区间的结束值 直接将下一个区间替换为当前区间。、

  • 如果没有重叠,添加当前区间到结果中

最后返回合并后的区间,转换为二维数组

class Solution {  
    public int[][] merge(int[][] intervals) {  
        // 如果仅有一个区间,直接返回  
        if (intervals.length == 1)  
            return intervals;  

        // 对区间进行排序,首先根据起始值排序,如果起始值相同则根据结束值排序  
        Arrays.sort(intervals, new Comparator<int[]>() {  
            public int compare(int[] left, int[] right) {  
                // 比较起始值  
                if (left[0] != right[0]) {  
                    return left[0] - right[0];  
                } else {  
                    // 如果起始值相同,比较结束值  
                    return left[1] - right[1];  
                }  
            }  
        });  

        // 用 ArrayList 存储合并后的区间  
        List<int[]> re = new ArrayList<>();  

        // 遍历所有区间,尝试进行合并  
        for (int i = 0; i < intervals.length - 1; i++) {  
            // 如果当前区间的结束值大于等于下一个区间的起始值,则存在重叠  
            if (intervals[i][1] >= intervals[i + 1][0]) {  
                // 如果当前区间的结束值小于等于下一个区间的结束值  
                if (intervals[i][1] <= intervals[i + 1][1]) {  
                    // 合并区间,更新下一个区间的起始值为当前区间的起始值  
                    intervals[i + 1][0] = intervals[i][0];  
                } else {  
                    // 如果当前区间的结束值大于下一个区间的结束值  
                    // 直接将下一个区间替换为当前区间  
                    intervals[i + 1] = intervals[i];  
                }  
                // 如果到达倒数第二个区间,添加合并后的区间  
                if (i == intervals.length - 2)  
                    re.add(intervals[i + 1]);  
                continue; // 继续下次迭代  
            } else {  
                // 如果没有重叠,添加当前区间到结果中  
                if (i == intervals.length - 2)  
                    re.add(intervals[i + 1]);  
                re.add(intervals[i]);  
            }  
        }  
        // 返回合并后的区间,转换为二维数组  
        return re.toArray(new int[re.size()][]);  
    }  
}

虽然代码有些可以改进简化的地方,但好歹是自己写出来的而且性能还勉强看得过去😁(82%)

代码的改进:

  1. 简化合并逻辑

    • 使用 LinkedList 来处理合并后的区间,避免了多重条件判断,通过直接访问 merged.getLast() 来获取最新的合并区间。

  2. 避免重复更新

    • 直接在重叠的情况下更新最后一个合并区间的结束值,避免了在不同条件下的多次更新。

  3. 使用增强的 for 循环

    • 采用传统的 for 循环,由于需要访问索引,所以这里没用增强的 for 循环,但其他部分的逻辑简化了。

这样修改后的代码更加简洁明了,可读性更好,并且易于维护。合并区间的逻辑也变得更加直接。

class Solution {  
    public int[][] merge(int[][] intervals) {  
        if (intervals.length == 0) return new int[0][0];  
        
        // 排序  
        Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0]));  
        
        // 使用 LinkedList 存储合并后的区间  
        List<int[]> merged = new LinkedList<>();  
        
        // 添加第一个区间  
        merged.add(intervals[0]);  
        
        for (int i = 1; i < intervals.length; i++) {  
            int[] current = intervals[i];  
            int[] lastMerged = merged.getLast();  
            
            // 如果当前区间与上一个合并区间重叠  
            if (current[0] <= lastMerged[1]) {  
                // 更新合并区间的结束值  
                lastMerged[1] = Math.max(lastMerged[1], current[1]);  
            } else {  
                // 不重叠,则直接添加当前区间  
                merged.add(current);  
            }  
        }  
        
        // 转换为二维数组  
        return merged.toArray(new int[merged.size()][]);  
    }  
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值