算法入门-排序3

第三部分:排序

21.合并两个有序链表(简单)

题目:将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例 1:

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

第一种思路:

之前上数据结构了解过一些算法,但是都不是很熟悉,不过这道题用递归还是知道的(链表应该蛮常使用递归吧),思路很易懂:

  • 首先检查两个链表的头节点 list1list2

    • 如果 list1 为空,则返回 list2。因为如果一个链表为空,合并结果就是另一个非空链表。

    • 如果 list2 为空,则返回 list1,逻辑与前面相同。

  • 如果两个链表都不为空,比较 list1list2 的头节点值:

    • 如果 list1.val 小于 list2.val,则将 list1 作为合并后的链表的头节点,接着递归合并 list1.nextlist2,将结果连接到 list1next

    • 否则,将 list2 作为合并后的链表的头节点,递归合并 list1list2.next,将结果连接到 list2next

每次返回的都是较小的节点,并且通过递归调用连接了剩下的部分,这样最终合并后的链表会保持升序。

/**  
 * Definition for singly-linked list.  
 * public class ListNode {  
 *     int val; // 节点的值  
 *     ListNode next; // 指向下一个节点的引用  
 *     ListNode() {} // 默认构造函数  
 *     ListNode(int val) { this.val = val; } // 带值的构造函数  
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; } // 带值和下一个节点的构造函数  
 * }  
 */  
// ListNode 是链表节点的定义,每个节点包含一个整数值 val 和一个指向下一个节点的引用 next。  

class Solution {  
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {  
        // 如果 list1 为空,直接返回 list2  
        if (list1 == null) {  
            return list2;  
        }  
        // 如果 list2 为空,直接返回 list1  
        else if (list2 == null) {  
            return list1;  
        }  
        // 如果 list1 的节点值小于 list2 的节点值  
        else if (list1.val < list2.val) {  
            // 递归地合并 list1 的下一个节点和 list2,并将结果链接到 list1  
            list1.next = mergeTwoLists(list1.next, list2);  
            // 返回 list1,因为它的节点值小于 list2  
            return list1;  
        } else {  
            // 如果 list2 的节点值小于或等于 list1 的节点值  
            // 递归地合并 list1 和 list2 的下一个节点,并将结果链接到 list2  
            list2.next = mergeTwoLists(list1, list2.next);  
            // 返回 list2,因为它的节点值小于或等于 list1  
            return list2;  
        }  
    }  
}

229.多数元素 II(中等)

题目:给定一个大小为 n 的整数数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。

示例 1:

输入:nums = [3,2,3]
输出:[3]

示例 2:

输入:nums = [1]
输出:[1]

示例 3:

输入:nums = [1,2]
输出:[1,2]

第一种思路:

首先想到的其实是哈希Set,但是觉得可能会占较大空间开销,就先放下了这种思路。

然后想到了在循环中比较这个数出现次数是否大于 n / 3 ,然后自然地想到了先将数组进行排序,

这样由于相同元素都在一起了,这样就方便比较了:

  • 设定一个变量 timesnums.length / 3, 这是我们需要的阈值(即元素出现次数必须超过这个值)。

  • temp_times 初始化为 1,用于记录当前元素的出现次数,从索引 1 开始遍历数组,比较当前元素与前一个元素:

    • 如果相等,则 temp_times++ 增加计数。

    • 如果不相等,将 temp_times 重置为 1,并继续。

  • 每当 temp_times 超过 times,并且在 mylist 中不存在当前元素的前一个值(nums[i-1]),则将该元素添加到结果列表 mylist 中。

后来发现案例有数组长度小于3的,然后想到可以直接先判断:

  • 长度小于3: 首先检查输入数组 nums 的长度。如果长度小于 3,所有元素都可能是多数素,因此直接遍历数组,将不重复的元素添加到 mylist 中。

  • 数组长度大于或等于 3:接下来使用 Arrays.sort(nums) 将数组排序。这一步是关键,因为排序后的数组使我们可以顺序检查元素的出现次数。

class Solution {  
    public List<Integer> majorityElement(int[] nums) {  
        // 创建一个列表用于存储结果  
        List<Integer> mylist = new ArrayList<>();  
        
        // 如果数组长度小于 3,所有元素都有可能是多数元素  
        if (nums.length < 3) {  
            for (int num : nums) {  
                // 只将不重复的元素添加到结果列表中  
                if (!mylist.contains(num))  
                    mylist.add(num);  
            }  
            return mylist; // 返回结果列表  
        }  
        
        // 对数组进行排序  
        Arrays.sort(nums);  
        
        // 计算出现次数的阈值,即 n / 3  
        int times = nums.length / 3;  
        int temp_times = 1; // 当前元素的出现次数  
        
        // 从索引 1 开始遍历数组  
        for (int i = 1; i < nums.length; i++) {  
            // 如果当前元素与前一个元素相等,增加计数  
            if (nums[i] == nums[i - 1]) {  
                temp_times++;  
            } else {  
                // 否则,重置计数为 1,继续遍历  
                temp_times = 1;  
                continue;  
            }  
            
            // 如果当前元素的出现次数超过阈值  
            // 且结果列表中不包含当前元素的前一个值,添加到结果列表  
            if (temp_times > times && !mylist.contains(nums[i - 1])) {  
                mylist.add(nums[i - 1]);  
            }  
        }  
        
        // 返回包含所有出现次数超过 n / 3 的元素的结果列表  
        return mylist;  
    }  
}

后来经过代码分析后:

代码逻辑中的潜在问题与改进:
  1. 重复元素检测:当前方法可能会导致遗漏一些应被视为多数元素的值,尤其是在最后一个元素的出现次数只有在数组结尾时检查。可以考虑在遍历结束后再检查最后一个值。

  2. 效率:虽然排序的时间复杂度为 O(n log n),整体空间复杂度为 O(n),你可以考虑使用哈希表或摩尔投票算法来提高效率,使时间复杂度降低到 O(n)

  3. 最坏情况下的性能:如果数组中所有元素都相同,且更高的元素数量传递了少量相同的元素,可能会导致错误的判断。可以通过使用 HashMap 来精确地记录每个元素的计数。

原来用哈希表时间复杂度更低啊!!!

class Solution {  
    public List<Integer> majorityElement(int[] nums) {  
        // 创建一个列表用于存储结果  
        List<Integer> mylist = new ArrayList<>();  
        // 创建一个哈希表用于存储每个元素的出现次数  
        Map<Integer, Integer> countMap = new HashMap<>();  

        // 计算阈值,表示出现次数超过 n / 3 的元素  
        int threshold = nums.length / 3;  

        // 遍历输入数组,计数每个元素的出现次数  
        for (int num : nums) {  
            // 将元素 num 的出现次数更新到 countMap 中  
            countMap.put(num, countMap.getOrDefault(num, 0) + 1);  
        }  

        // 遍历 countMap,检查每个元素的出现次数是否超过阈值  
        for (Map.Entry<Integer, Integer> entry : countMap.entrySet()) {  
            // 如果当前元素的出现次数大于阈值,则将其添加到结果列表中  
            if (entry.getValue() > threshold) {  
                mylist.add(entry.getKey());  
            }  
        }  

        // 返回包含所有出现次数超过 n / 3 的元素的结果列表  
        return mylist;  
        
        /*
        上面这一段添加到结果集中的代码也可以用下面的代码替换
        List<Integer> ans = new ArrayList<>();
        for (int x : cnt.keySet()) {
            if (cnt.get(x) > nums.length / 3) {
                ans.add(x);
            }
        }
        return ans;
        */
        
    }  
}

虽然时间复杂度确实降低为O(n)了,但是执行用时没我的方法少好吧 ~╮(╰ - ╯)╭~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值