简单题和中等题


【1. 题目描述】

数轴上放置了一些筹码,每个筹码的位置存在数组 chips 当中。

你可以对 任何筹码 执行下面两种操作之一(不限操作次数,0 次也可以):


  • 将第 i 个筹码向左或者右移动 2 个单位,代价为 0。
  • 将第 i 个筹码向左或者右移动 1 个单位,代价为 1。

最开始的时候,同一位置上也可能放着两个或者更多的筹码。

返回将所有筹码移动到同一位置(任意位置)上所需要的最小代价。


​​​​

【示例 1】

输入:chips = [1,2,3]
输出:1
解释:第二个筹码移动到位置三的代价是 1,第一个筹码移动到位置三的代价是 0,总代价为 1。

【示例 2】

输入:chips = [2,2,2,3,3]
输出:2
解释:第四和第五个筹码移动到位置二的代价都是 1,所以最小总代价为 2。

【解题思路】

由题目条件可知,当我们将砝码移动偶数个单位的时候,代价为0,而要移动奇数个单位的时候,我们可以首先移动偶数个单位,然后再移动一个单位,这样,代价就是1。因此,我们只要统计所给出的数组中奇数和偶数的个数,然后返回个数更少的那一个。

【示例代码】

class Solution:
def minCostToMoveChips(self, chips: List[int]) -> int:
if len(chips) == 1:
return 0
odd_num = 0
even_num = 0
for item in chips:
if item % 2 == 0:
even_num += 1
else:
odd_num += 1
if even_num < odd_num:
return even_num
else:
return odd_num

【2. 题目描述】

给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

说明:解集不能包含重复的子集。

【示例】

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

【解题思路】

由题目条件可知是让我们构造集合 \(X\) 的真子集。容易想到的方法是:假设真子集集合为\(\Theta\)。我们先构造一个空的子集,可知道空子集一定属于\(\Theta\)。然后每次从 \(X\) 中获取一个元素为下一个待添加元素 \(\theta\) ,将 \(\theta\) 与已经存在于 \(\Theta\) 中的所有元素进行组合,如此进行下去即可获得完整的真子集 \(\Theta\)

【代码示例】

class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
sub = [[nums[0]]]
for i in range(1, len(nums)):
sub_len = len(sub)
for j in range(sub_len):
tmp = list(sub[j])
tmp.append(nums[i])
sub.append(tmp)
sub.append([nums[i]])
sub.append([])
return sub

【3. 题目描述】

给定一个非负整数 \(c\) ,你要判断是否存在两个整数 \(a\)\(b\),使得 \(a^2\) + \(b^2\) = \(c\)

【示例1】

输入: 5
输出: True
解释: 1 * 1 + 2 * 2 = 5

【示例2】

输入: 3
输出: False

【解题思路】

由题意可知,我们只需要遍历一下小于 \(sqrt(x)\) 的数字 \(m\) ,然后查看一下 \(x - m*m\) 是否为某一个整数的平方即可。

【示例代码】

class Solution:
def judgeSquareSum(self, c: int) -> bool:
if c == 0:
return True
for i in range(1, int(c**0.5)+1):
if i*i == c:
return True
if i*i < c:
another = int((c - i * i)**0.5)
if another * another == c - i * i:
return True
return False

【4. 题目描述】

给定一个仅包含大小写字母和空格 ' ' 的字符串 s,返回其最后一个单词的长度。如果字符串从左向右滚动显示,那么最后一个单词就是最后出现的单词。

如果不存在最后一个单词,请返回 0 。

说明:一个单词是指仅由字母组成、不包含任何空格字符的 最大子字符串。

【示例】

输入: "Hello World"
输出: 5

【解题思路】

按照空格来分开单词,保存最后一个单词即可。因为存在多个连续空格的情况,这就使得我们不能简单的使用​​split​​​方法。这里我们使用数组 ​​cs​​ 来保留见到的最后一个单词。因为可能存在单词后面还有一个空格的情况,因此检测到空格之后还有字母之后再进行数组清空,开始下一个单词的记录。

【示例代码】

class Solution:
def lengthOfLastWord(self, s: str) -> int:
cs = []
for i in range(len(s)):
if s[i] != ' ':
cs.append(s[i])
elif s[i] == ' ' and i < len(s)-1 and s[i+1] != ' ':
cs = []
return len(cs)

【5. 题目描述】

统计所有小于非负整数 n 的质数的数量。

【示例】

输入: 10
输出: 4
解释: 小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 。

【解题思路】

如题,只需要遍历找出小于小于 \(n\) 的质数即可。需要注意的是:如果我们不进行任何优化会超出时间限制。简单的优化方法就是遍历数的时候,我们只遍历奇数。同样,进行质数判断的时候,我们也只需要判断两个奇数相乘是否与要遍历的数相等。

【示例代码】

class Solution:
def countPrimes(self, n: int) -> int:
num = 0
if n == 1:
return 0
for i in range(1, n, 2):
if not self.judgePrime(i):
num += 1
if n > 2:
num += 1
return num

def judgePrime(self, dig) -> bool:
if dig in [2, 3, 5, 7]:
return False
if dig <= 9:
return True
for item in range(3, int(dig**0.5)+1, 2):
if dig // item * item == dig:
return True
return False

【优化】

使用厄拉多塞筛法,初始将所有数字标记为False,之后将每个找到的质数的奇数倍置为True。

class Solution:
def countPrimes(self, n: int) -> int:
flags = [False for i in range(n)]
count = 0
for i in range(3, n, 2):
if not flags[i]:
max_ = n // i
if n // i * i < n:
max_ += 1
for j in range(3, max_, 2):
flags[j*i] = True
count += 1
if n > 2:
count += 1
return count

【6. 题目描述】

给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。

【示例 1】

输入: 123
输出: 321

【示例 2】

输入: -123
输出: -321

【示例 3】

输入: 120
输出: 21

【注意】

假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−231, 231 − 1]。请根据这个假设,如果反转后整数溢出那么就返回 0。

【解题思路】

每次获取数字的最后一位然后循环乘10即可。

【代码示例】

class Solution:
def reverse(self, x: int) -> int:
flag = True
if x < 0:
flag = False
x = -x
reverse_x = 0
while x != 0:
reverse_x = reverse_x * 10 + x % 10
x = x // 10
bi = 2**31
if flag:
if reverse_x > bi-1:
return 0
else:
if reverse_x > bi:
return 0
if not flag:
return -reverse_x
return reverse_x

【7. 题目描述】

假设你有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花卉不能种植在相邻的地块上,它们会争夺水源,两者都会死去。

给定一个花坛(表示为一个数组包含0和1,其中0表示没种植花,1表示种植了花),和一个数 n 。能否在不打破种植规则的情况下种入 n 朵花?能则返回True,不能则返回False。

【示例 1】

输入: flowerbed = [1,0,0,0,1], n = 1
输出: True

【示例 2】

输入: flowerbed = [1,0,0,0,1], n = 2
输出: False

【解题思路】

最靠左边的一个可以种的条件是该块地本身每种且其右边一块地也没有种,中间的地可以种植的条件是其左右两块地都没有被种,最右边一块地可以被种的条件是其本身没有被种且其左边一块地也没有被种。

【代码示例】

class Solution:
def canPlaceFlowers(self, flowerbed: List[int], n: int) -> bool:
if n == 0:
return True
if len(flowerbed) == 0:
return False
if len(flowerbed) == 1:
if flowerbed[0] != 0:
return False
elif n != 1:
return False
return True
if self.check(flowerbed[0:2]):
flowerbed[0] = 2
for i in range(1, len(flowerbed)-1):
if self.check(flowerbed[i-1:i+2]):
flowerbed[i] = 2
if self.check(flowerbed[len(flowerbed)-2:]):
flowerbed[len(flowerbed)-1] = 2
num = 0
for item in flowerbed:
if item == 2:
num += 1
if num >= n:
return True
else:
return False


def check(self, arr: List[int]) -> bool:
if len(arr) == 2:
# 靠边
if arr[0] == 0 and arr[1] == 0:
return True
if arr[0] == 0 and arr[1] == 0 and arr[2] == 0:
return True
return False

【8、题目描述】

冬季已经来临。 你的任务是设计一个有固定加热半径的供暖器向所有房屋供暖。现在,给出位于一条水平线上的房屋和供暖器的位置,找到可以覆盖所有房屋的最小加热半径。所以,你的输入将会是房屋和供暖器的位置。你将输出供暖器的最小加热半径。

【说明】


  1. 给出的房屋和供暖器的数目是非负数且不会超过 25000。
  2. 给出的房屋和供暖器的位置均是非负数且不会超过10^9。
  3. 只要房屋位于供暖器的半径内(包括在边缘上),它就可以得到供暖。
  4. 所有供暖器都遵循你的半径标准,加热的半径也一样。

【示例 1】

输入: [1,2,3],[2]
输出: 1
解释: 仅在位置2上有一个供暖器。如果我们将加热半径设为1,那么所有房屋就都能得到供暖。

【示例 2】

输入: [1,2,3,4],[1,4]
输出: 1
解释: 在位置1, 4上有两个供暖器。我们需要将加热半径设为1,这样所有房屋就都能得到供暖。

【解题思路 1.0】

首先找出对于每个房间所有供暖器的最短距离,然后在这些最短距离中,取最大值,即为最小加热半径。

【示例代码 1.0】

class Solution:

def findRadius(self, houses: List[int], heaters: List[int]) -> int:
mins_ = [0 for i in range(len(houses))]
for i in range(len(houses)):
mins_[i] = abs(heaters[0] - houses[i])
for j in range(len(heaters)):
if abs(heaters[j] - houses[i]) < mins_[i]:
mins_[i] = abs(heaters[j] - houses[i])
return max(mins_)

【注】

上诉代码实现了我们的思想,但是时间复杂度较高因此通不过,可以考虑首先将heaters进行排序,然后使用二分查找的方法来进行优化。

【解题思路 2.0】

使用滑窗的方式进行求解。首先将houses和heaters进行排序,然后比较找出house与最近的heater的距离。即:用一个下标指向house,一个下标指向heater,两个下标均从0开始。具体如代码所示:

【示例代码 2.0】

class Solution:

def findRadius(self, houses: List[int], heaters: List[int]) -> int:
min_ = 0

heaters = sorted(heaters)
houses = sorted(houses)
houses_index = 0
heater_index = 0
while True:
if houses_index >= len(houses):
return min_
if houses[houses_index] < heaters[heater_index]:
dis = abs(houses[houses_index] - heaters[heater_index])
if min_ < dis:
min_ = dis
houses_index += 1
elif houses[houses_index] > heaters[heater_index]:
if heater_index >= len(heaters)-1: # last
dis = abs(houses[len(houses)-1] - heaters[heater_index])
if min_ < dis:
return dis
else:
return min_
elif heaters[heater_index+1] <= houses[houses_index]:
heater_index += 1
elif heaters[heater_index+1] > houses[houses_index]:
i_dis = abs(heaters[heater_index] - houses[houses_index])
i_add_dis = abs(heaters[heater_index+1] - houses[houses_index])
if i_dis >= i_add_dis:
heater_index += 1
if min_ < i_add_dis:
min_ = i_add_dis
elif min_ < i_dis:
min_ = i_dis
houses_index += 1
else:
houses_index += 1

【9. 题目描述】

给你一个长度为 n 的整数数组,请你判断在 最多 改变 1 个元素的情况下,该数组能否变成一个非递减数列。

我们是这样定义一个非递减数列的: 对于数组中所有的 i (0 <= i <= n-2),总满足 nums[i] <= nums[i + 1]。

【示例 1】

输入: nums = [4,2,3]
输出: true
解释: 你可以通过把第一个4变成1来使得它成为一个非递减数列。

【示例 2】

输入: nums = [4,2,1]
输出: false
解释: 你不能在只改变一个元素的情况下将其变为非递减数列。


找到第一处非递减的地方,然后将该处数字更改为能使到此处为止的数组非递减成立的最小数字,即前一位的数字或前二位的数字。当再次遇到非递减的情况时,就返回False。

【示例代码】

class Solution:
def checkPossibility(self, nums: List[int]) -> bool:
if len(nums) < 2:
return True
flag = False
for i in range(1, len(nums)):
if nums[i] < nums[i-1]:
if flag:
return False
flag = True
if i >= 2 and nums[i] < nums[i-2]:
nums[i] = nums[i-1]
else:
if nums[i] > nums[i-1]:
nums[i] = nums[i-1]
return True