每日刷题——LeetCode42 剑指offer40

昨天组会,所以停更一天。

Leetcode42 接雨水

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
接雨水
今天的每日一题,是一道hard题。分享一个官方题解下的评论“打开一看,困难题(眉头一皱),定睛一看,接雨水,那没事了”。
这一题首先难点在于,如果确定一个位置能装的水的最大高度。一个位置能装水的最大高度,取决于左右两侧的最大高度的最小值。比如该位置左侧最大高度3,右侧最大高度5,那么能装的水的最大高度就是3。
根据找个条件,就很容易想到,用两个数组分别保存左侧的最大高度以及右侧的最大高度。
在这里插入图片描述
因此,可以得到

class Solution {
    public int trap(int[] height) {
    	int ans = 0;
    	int len = height.length;
    	if(len==0 || len==1){
    		return 0;
		}
    	int[] max = new int[len];
    	max[len-1] = 0;
    	for(int i = len-2; i>=0; i--){
    		max[i] = height[i+1] > max[i+1] ? height[i+1] : max[i+1];
		}
    	max[0] = 0;
    	for(int i=1; i<len; i++){
    		int leftmax = height[i-1] > max[i-1] ? height[i-1] : max[i-1];
    		max[i] = max[i] < leftmax ? max[i] : leftmax;
    		if(max[i]>height[i]) {
				ans += max[i] - height[i];
			}
		}
    	return ans;
    }
}

这里我用一个辅助数组代替了两个辅助数组,本以为应该够简洁。但是在题解中,看到了双指针的方法,将空间复杂度降为O(1)。这个方法有很多种解释,我觉得最好的就是下面这个,大家可以参考。

https://leetcode-cn.com/problems/volume-of-histogram-lcci/solution/shuang-zhi-zhen-an-xing-qiu-jie-xiang-xi-d162/

剑指offer40 最小的k个数

输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。

示例 1:
输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]

这是一道十分经典的TopK题目,也是受益颇多。

方法一:排序

这也是最简单的一种方法了,直接进行排序,然后取出前k个数即可。Java中数组排序功能Arrays.sort(),可以直接完成。
但是一道亮评 “如果面试时用sort,有被面试官写成段子发到知乎接收精英阶层群嘲的风险哦” ,应该懂了吧,这题远没有这么简单。

方法二 堆方法

虽然学过数据结构,但是我对堆这个数据结构已经基本忘光了,这算是补补课。

堆就是用数组实现的二叉树,所以它没有使用父指针或者子指针。堆根据“堆属性”来排序,“堆属性”决定了树中节点的位置。
堆分为两种:最大堆和最小堆,两者的差别在于节点的排序方式。
在最大堆中,父节点的值比每一个子节点的值都要大。在最小堆中,父节点的值比每一个子节点的值都要小。这就是所谓的“堆属性”,并且这个属性对堆中的每一个节点都成立。

在Java中,提供了PriorityQueue类实现小顶堆的功能。可以直接使用PriorityQueue()构造该类,主要的方法包括add() ,poll(),peek(),size()。实现add()和poll()的时间复杂度为O(n)。
了解了堆以后,这一题就十分简单,因为要找最小的k个数,只需要用一个大顶堆,维护当前最小的k个数,依次添加元素,如果堆的大小大于k,则删除最大的元素。

class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
    	int[] vec = new int[k];
    	if(k==0){
    		return vec;
		}
    	PriorityQueue<Integer> queue = new PriorityQueue<Integer> ((o1, o2) -> o2 - o1); // 定义比较函数,实现大顶堆
    	for(int num: arr){
    		queue.add(num);
    		if(queue.size() > k){
    			queue.poll();
			}
		}
    	for(int i = 0; i< k; i++){
    		vec[i] = queue.poll();
		}
    	return vec;
    }
}

方法三 快排

这里利用快速排序的思想。快排的划分函数每次执行完后都能将数组分成两个部分,小于等于分界值 pivot 的元素的都会被放到数组的左边,大于的都会被放到数组的右边,然后返回分界值的下标。与快速排序不同的是,快速排序会根据分界值的下标递归处理划分的两侧,而这里我们只处理划分的一边。

class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
    	if(k==0){
    		return new int[0];
		}else if(arr.length <= k){
    		return arr;
		}
    	partitionArray(arr, 0, arr.length-1, k);

    	int[] res = new int[k];
    	for(int i =0; i < k; i++){
    		res[i] = arr[i];
		}
    	return res;
    }
    public void partitionArray(int[] arr, int l, int h, int k){
    	int m = partition(arr, l, h);
    	if(k==m){
    		return;
		}else if(k<m){
    		partitionArray(arr, l, m-1, k);
		}else{
    		partitionArray(arr, m+1, h, k);
		}
	}
	public int partition(int[] arr, int l, int r){
    	int i = l;
    	int j = r;
    	int v = arr[l];
    	while(i < j){
    		while(j > i && arr[j] > v){
    			j--;
			}
    		if(i < j){
    			arr[i++] = arr[j];
			}
    		while(i < j && arr[i] < v){
    			i++;
			}
    		if(i < j){
    			arr[j--] = arr[i];
			}
		}
    	arr[i] = v;
    	return i;
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值