Leetcode-2021.03记录-part1

本文记录了2021年3月LeetCode的刷题心得,涉及动态规划在解决比特位计数问题中的应用,如低位有效判断和设置最低有效位,以及单调数列、区域和检索等数据结构问题的解决方案。文章深入浅出地分析了题目,并提供了相应的代码实现。
摘要由CSDN通过智能技术生成

三月刷题记录-part1

此博文为个人学习笔记,记录2021年3月Leetcode每日一题刷题记录

338. 比特位计数

题目链接
题目描述

给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。

示例1

输入: 2
输出: [0,1,1]

示例2

输入: 5
输出: [0,1,1,2,1,2]

进阶提示

  • 给出时间复杂度为O(n*sizeof(integer))的解答非常容易。但你可以在线性时间O(n)内用一趟扫描做到吗?
  • 要求算法的空间复杂度为O(n)。
  • 你能进一步完善解法吗?要求在C++或任何其他语言中不使用任何内置函数(如 C++ 中的 __builtin_popcount)来执行此操作。

分析

要在一次线性扫描的时间内解决问题,也就是说直接一次性往数组内遍历赋值。

所以需要找寻赋值规律。

1. 动态规划-低位有效判断

我们先来看这样一个例子

  • 1111,1110他们的bits取决于

    111的bits + 末位是否为1

  • 1101,1100他们的bits取决于

    110的bits + 末位是否为1

很容易可以得到递推公式:

bits[i] = bits[i >> 1] + (i & 1)

公式解释:

  • bits[i] 表示第i个数的bit为1的个数
  • i >> 1,表示i右移1位 可以把1110,1111变为 111,简单理解就是舍掉最低1位
  • i & 1,与运算。将第i个数与1作与运算,如果末位为1,则i & 1的结果就是1,否则0。

代码

func countBits(num int) []int {
   
	bits := make([]int, num+1)
	for i := 1; i <= num; i++ {
   
		bits[i] = bits[i>>1] + (i & 1)
	}
	return bits
}
2. 动态规划-设置最低有效位

1 & 0 = 001 & 000 = 0 = 000
2 & 1 = 010 & 001 = 0 = 000
3 & 2 = 011 & 010 = 2 = 010
4 & 3 = 100 & 011 = 0 = 000
5 & 4 = 101 & 100 = 4 = 100
6 & 5 = 110 & 101 = 4 = 100
7 & 6 = 111 & 110 = 6 = 110
8 & 7 = 1000 & 0111 = 0 = 0000
不难看出一个规律
n & n-1 得到一个数,这个数就是将n 的最末位的1变成0的结果

所以我们可以得到这样一个递推公式:

bits[i] = bits[i & [i-1]] + 1

公式解释:

  • i & i-1 将i的最末位1 变成了0,得到数m
  • 这个m一定在i之前,因为最末位1变成了0,之前的位不变
  • bits[i] = bits[m] + 1,因为末位1变成0,第i个相比较第m个,i的个数少1

代码

func countBits(num int) []int {
   
	bits := make([]int, num+1)
	for i := 1; i <= num; i++ {
   
		bits[i] = bits[i&(i-1)] + 1
	}
	return bits
}

896. 单调数列

题目描述

如果数组是单调递增或单调递减的,那么它是单调的。

如果对于所有 i <= j,A[i] <= A[j],那么数组 A 是单调递增的。 如果对于所有 i <= j,A[i]> = A[j],那么数组 A 是单调递减的。

当给定的数组 A 是单调数组时返回 true,否则返回 false。

示例

示例 1:

输入:[1,2,2,3]
输出:true

示例 2

输入:[6,5,4,4]
输出:true

示例 3:

输入:[1,3,2]
输出:false

示例 4:

输入:[1,2,4,5]
输出:true

示例 5:

输入:[1,1,1]
输出:true

分析

简单题,循环暴力破解即可

  1. 循环遍历数组,用前一个和后一个做比较
  2. 在比较的时候,要注意第一次比较的结果
  3. 如果第一次比较是小于,那么后面不能出现大于
  4. 如果第一次比较是大于,那么后面不能出现小于
  5. 如果第一次比较是等于,那么继续比较下一个
  6. 时间复杂度O(n)

代码

func isMonotonic(A []int) bool {
   
	flag := 0
	// flag 初始等于0,表示还未存在大于小于关系
	// flag 为1 表示递增;flag 为2,表示递减
	var val1, val2 int
	for index := 0; index < len(A)-1; index++ {
   
		val1 = A[index]   // 前一个变量
		val2 = A[index+1] // 后一个变量
		// 相等则继续判断,无论递增递减都满足
		if val1 == val2 {
   
			continue
		}
		// 小于的判定
		if val1 < val2 {
   
			// 如果flag == 2, 说明之前是递减序列,返回false
			if flag == 2 {
   
				return false
				// 如果flag == 0,第一次出现小于,赋值1
			} else if flag == 0 {
   
				flag = 1
			}
		}
		// 大于的判定
		if val1 > val2 {
   
			// 如果flag == 1,说明之前是递增序列,返回false
			if flag == 1 {
   
				return false
				// 如果flag == 0,第一次出现大于,赋值2
			} else if flag == 0 {
   
				flag = 2
			}
		}
	}
	// 全部遍历结束,到这步说明都满足
	return true
}

303. 区域和检索 - 数组不可变

题目描述

给定一个整数数组 nums,求出数组从索引 i 到 j(i ≤ j)范围内元素的总和,包含 i、j 两点。

实现 NumArray 类:

NumArray(int[] nums) 使用数组 nums 初始化对象
int sumRange(int i, int j) 返回数组 nums 从索引 i 到 j(i ≤ j)范围内元素的总和,包含 i、j 两点(也就是 sum(nums[i], nums[i + 1], … , nums[j]))

示例

输入:
[“NumArray”, “sumRange”, “sumRange”, “sumRange”]
[[[-2, 0, 3, -5, 2, -1]], [0, 2], [2, 5], [0, 5]]
输出:
[null, 1, -1, -3]

解释:
NumArray numArray = new NumArray([-2, 0, 3, -5, 2, -1]);
numArray.sumRange(0, 2); // return 1 ((-2) + 0 + 3)
numArray.sumRange(2, 5);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值