LeetCode每日三题day02

文章介绍了如何通过优先队列解决将数组和减半的最少操作次数问题,并展示了使用滑动窗口和双指针算法求解无重复字符的最长子串的方法。此外,还讨论了寻找两个正序数组中位数的O(log(m+n))时间复杂度解决方案,涉及二分查找策略。
摘要由CSDN通过智能技术生成
day02 每日三题
1、将数组和减半的最少操作次数2208

给你一个正整数数组 nums 。每一次操作中,你可以从 nums 中选择 任意 一个数并将它减小到 恰好 一半。(注意,在后续操作中你可以对减半过的数继续执行操作)

请你返回将 nums 数组和 至少 减少一半的 最少 操作数。

示例 1:

输入:nums = [5,19,8,1]
输出:3
解释:初始 nums 的和为 5 + 19 + 8 + 1 = 33 。
以下是将数组和减少至少一半的一种方法:
选择数字 19 并减小为 9.5 。
选择数字 9.5 并减小为 4.75 。
选择数字 8 并减小为 4 。
最终数组为 [5, 4.75, 4, 1] ,和为 5 + 4.75 + 4 + 1 = 14.75 。
nums 的和减小了 33 - 14.75 = 18.25 ,减小的部分超过了初始数组和的一半,18.25 >= 33/2 = 16.5 。
我们需要 3 个操作实现题目要求,所以返回 3 。
可以证明,无法通过少于 3 个操作使数组和减少至少一半。

示例 2:

输入:nums = [3,8,20]
输出:3
解释:初始 nums 的和为 3 + 8 + 20 = 31 。
以下是将数组和减少至少一半的一种方法:
选择数字 20 并减小为 10 。
选择数字 10 并减小为 5 。
选择数字 3 并减小为 1.5 。
最终数组为 [1.5, 8, 5] ,和为 1.5 + 8 + 5 = 14.5 。
nums 的和减小了 31 - 14.5 = 16.5 ,减小的部分超过了初始数组和的一半, 16.5 >= 31/2 = 16.5 。
我们需要 3 个操作实现题目要求,所以返回 3 。

用到的知识点:

优先队列

优先队列是一种特殊的队列数据结构,它的每个元素都有一个相关的优先级。优先队列的特点是在添加元素时,会根据元素的优先级自动进行排序,使得优先级高的元素排在前面。当访问或删除元素时,会首先处理优先级最高的元素。

下面介绍一下优先队列的使用方法:

  1. 创建优先队列:可以使用现有的数据结构库提供的优先队列类,如 Java 中的 PriorityQueue 类。也可以手动实现一个优先队列数据结构。
  2. 添加元素:使用 offer()add() 方法向优先队列中添加元素。添加的元素会根据其优先级自动排序。
  3. 访问元素:使用 peek() 方法可以访问优先队列中优先级最高的元素,即队列头部的元素。注意,此操作并不会移除队列中的元素。
  4. 删除元素:使用 poll() 方法可以移除并返回优先队列中优先级最高的元素。
  5. 自定义优先级比较器:如果要根据元素的特定属性或规则确定优先级,可以通过自定义比较器来指定元素之间的优先级关系。比较器可以在创建优先队列时通过构造函数参数传入,或者在添加元素时指定。

需要注意的是,优先队列并不保证所有元素的排序顺序,只保证队列头部的元素具有最高的优先级。而对于其他元素的顺序,取决于具体实现和使用的比较器。

优先队列常见的应用场景包括任务调度、事件管理、最短路径算法等,它能够高效地处理按优先级排序的任务或数据。

在java中的使用

在 Java 中,可以使用 java.util.PriorityQueue 类来实现优先队列。以下是在 Java 中使用优先队列的一般方法:

  1. 导入类:

    javaCopy Codeimport java.util.PriorityQueue;
    
  2. 创建优先队列对象:

    javaCopy CodePriorityQueue<T> pq = new PriorityQueue<>();
    

    其中 <T> 是要存储的元素类型,可以是任何具有可比较性的类型,例如整数、字符串或自定义对象。

  3. 添加元素:

    javaCopy Codepq.offer(element);
    

    javaCopy Codepq.add(element);
    

    这将根据元素的默认排序顺序将元素添加到优先队列中,并根据其优先级进行排序。

  4. 访问元素:

    javaCopy CodeT firstElement = pq.peek();
    

    这将返回优先队列中优先级最高的元素,但不会将其从队列中移除。如果队列为空,返回 null

  5. 删除元素:

    javaCopy CodeT removedElement = pq.poll();
    

    这将删除并返回优先队列中优先级最高的元素。如果队列为空,返回 null

  6. 自定义优先级比较器: 如果要基于特定规则确定元素的优先级,可以通过自定义比较器来实现。比较器需要实现 Comparator 接口或使用 lambda 表达式。

    javaCopy CodePriorityQueue<T> pq = new PriorityQueue<>(new MyComparator());
    

    javaCopy CodePriorityQueue<T> pq = new PriorityQueue<>((a, b) -> /* 比较逻辑 */);
    

    其中 MyComparator 是自定义的比较器类。

注意:在 Java 中,优先队列是基于二叉堆或二叉树实现的,因此插入和删除元素的时间复杂度为 O(logN),其中 N 是队列中的元素个数。同时,队列中的元素并不保证有序,只有队头元素具有最高的优先级。

示例代码:

private static int solution(int[] nums) {
		int res = 0;
		double sum1 = 0;
		double sum2 = 0;
		//创建一个优先队列,降序排列,lambda表达式
		PriorityQueue<Double> numQueue = new PriorityQueue<Double>((a,b) -> b.compareTo(a));
		
		for (int i = 0; i < nums.length; i++) {
			numQueue.add((double) nums[i]);
		}
		for (int i = 0; i < nums.length; i++) {
			sum1 += nums[i];
		}
		while (sum2<=sum1/2) {
			double index = numQueue.poll();
			sum2 += index/2;
			numQueue.add(index/2);
			res++;
		}
		return res;
	}
2、无重复字符的最长子串3

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

示例 1:

输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例 2:

输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例 3:

输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

提示:

  • 0 <= s.length <= 5 * 104

  • s 由英文字母、数字、符号和空格组成

用到的知识点:滑动窗口、双指针

滑动窗口是一种常用的算法思想,用于解决一些数组或字符串相关的问题。它通常用于在线性时间复杂度内解决子数组或子串的问题。

滑动窗口的基本思想是维护一个窗口,通过调整窗口的起始位置和结束位置来解决问题。窗口通常是一个连续的子数组或子串。

下面是滑动窗口算法的一般步骤:

  1. 初始化窗口的起始位置 start 和结束位置 end
  2. 当窗口满足某个条件时,尝试缩小窗口的范围,即增加 start 的值。同时记录当前满足条件的结果。
  3. 当窗口不再满足条件时,尝试扩展窗口的范围,即增加 end 的值。
  4. 重复步骤 2 和 3,直到遍历完所有的元素。

滑动窗口的优点在于它只需遍历一次输入序列,因此可以在线性时间内解决问题。它适用于解决满足某种特定条件的子数组或子串的问题,例如求和、平均值、最大/最小值等。

以下是一个示例问题:给定一个整数数组和一个目标值,找到数组中和大于等于目标值的最短子数组的长度。可以使用滑动窗口算法来解决这个问题。

javaCopy Codeint minSubArrayLen(int target, int[] nums) {
    int n = nums.length;
    int start = 0;
    int end = 0;
    int sum = 0;
    int minLength = Integer.MAX_VALUE;
    
    while (end < n) {
        sum += nums[end];
        
        while (sum >= target) {
            minLength = Math.min(minLength, end - start + 1);
            sum -= nums[start];
            start++;
        }
        
        end++;
    }
    
    return minLength != Integer.MAX_VALUE ? minLength : 0;
}

在上述代码中,使用了两个指针 startend 表示窗口的起始位置和结束位置。通过移动这两个指针,在满足条件的情况下缩小窗口范围,直到找到最短的子数组。时间复杂度为 O(n),其中 n 是数组的长度。

滑动窗口是一个非常有用的算法思想,在解决一些数组或字符串相关问题时具有较高的效率和灵活性。

示例代码:

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if (s.length()==0) return 0;
        HashMap<Character, Integer> map = new HashMap<Character, Integer>();
        int max = 0;
        int left = 0;
        for(int i = 0; i < s.length(); i ++){
            if(map.containsKey(s.charAt(i))){
                left = Math.max(left,map.get(s.charAt(i)) + 1);
            }
            map.put(s.charAt(i),i);
            max = Math.max(max,i-left+1);
        }
        return max;
        
    }
}
3、寻找两个正序数组的中位数4

给定两个大小分别为 mn 的正序(从小到大)数组 nums1nums2。请你找出并返回这两个正序数组的 中位数

算法的时间复杂度应该为 O(log (m+n))

示例 1:

输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2

示例 2:

输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5

提示:

  • nums1.length == m
  • nums2.length == n
  • 0 <= m <= 1000
  • 0 <= n <= 1000
  • 1 <= m + n <= 2000
  • -106 <= nums1[i], nums2[i] <= 106

用到的知识点:暴力、二分查找

作者采用暴力算法,首先将数组合并然后再用sort方法,再用数学基础知识将中位数查找出来即可。

示例代码:

//暴力解法
	public static double solution(int[] num1,int[] num2) {
		int len1 = num1.length;
		int len2 = num2.length;
		int[] nums = new int[len1+len2];
		System.arraycopy(num1, 0, nums, 0, len1);
		System.arraycopy(num2, 0, nums, len1, len2);
		Arrays.sort(nums);
		if (nums.length==0) return 0;
		if (nums.length%2 !=0) {
			return nums[nums.length/2];
		}else {
			return (double)(nums[nums.length/2]+nums[(nums.length/2)+1])/2;
		}
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Chen_XL1207

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值