一、数组
(1)简单
1、原地删除排序数组中的重复项
给你一个 升序排列 的数组 nums ,请你原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。
- 定义两个指针(解决原地删除的问题),slow、fast;
-
- 长度为n的数组,快指针fast从[1]遍历到[n-1]
- 判断条件:if [fast] != [fast-1]
-
-
- 则让slow += 1
-
class Solution(object):
def removeDuplicates(self, nums):
# nums = [0,0,1,1,1,2,2,3,3,4]
n = len(nums)
fast = slow = 1
while fast<n:
if nums[fast] != nums[fast - 1]:
nums[slow] = nums[fast]
slow += 1
fast += 1
return slow
2、原地移除元素
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
- 使用双指针:
-
- 长度为n的数组,从0地址开始遍历
- 判断条件:if nums[fast] != val: 则
-
-
- nums[slow] = nums[fast]
- slow += 1
-
class Solution(object):
def removeElement(self, nums, val):
# nums =[0,1,2,2,3,0,4,2] val =2
n = len(nums)
slow = fast = 0
while fast < n:
if nums[fast] != val:
nums[slow] = nums[fast]
slow += 1
fast += 1
return slow
3、两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
- 暴力枚举:
-
- 先选定i,再从除i外的所有元素找到相加为target的j;
-
-
- 判断条件:if nums[i] + nums[j] == target:
-
class Solution(object):
def twoSum(self, nums, target):
n = len(nums)
for i in range(n):
for j in range(i+1, n):
if nums[i] + nums[j] == target:
return [i, j]
return []
4、二分法查找
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
- 使用二分法思想(时间复杂度是 O(logn))
-
- 先从数组的中位数开始查找;将中位数与target比较
- 出现三种可能性
-
-
- if nums[mid] == target:
- if nums[mid] > target:
- if nums[mid] < target:
-
class Solution(object):
def search(self, nums, target):
n = len(nums)
left, right = 0, n-1
while left <= right:
# 使用(right - left)//2 + left作用:
# 例如:n=6,left=0, right=5
# 5-2//2 = 1(会出现更新后的left小于现有的left)
# 故 (right - left)//2 + left = 3
mid = (right - left)//2 + left
if nums[mid] == target:
return mid
elif nums[mid] < target:
left = mid + 1
elif nums[mid] > target:
right = mid - 1
else:
mid = -1
return -1
5、数组排序(.sort()函数的使用)
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
- .sort()函数的使用
class Solution(object):
def merge(self, nums1, m, nums2, n):
nums1[m:] = nums2
nums1.sort()
return
6、数组中存在重复元素(set()函数的使用)
给你一个整数数组 nums 。如果任一值在数组中出现 至少两次 ,返回 true ;如果数组中每个元素互不相同,返回 false 。
- set()函数的使用
-
- python的set()方法可以实现将对象去重,比较去重前后的对象长度即可
class Solution(object):
def containsDuplicate(self, nums):
return True if len(set(nums)) != len(nums) else False
7、杨辉三角
给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。在「杨辉三角」中,每个数是它左上方和右上方的数的和。
- 杨辉三角
- 分层计算:
-
- 外层循环迭代每一行
- 内层循环迭代每一行中元素数值
- 最终将内层循环的结果添加到外层循环的每一行中
class Solution(object):
def generate(self, numRows):
ceng = 0
yanghui = []
while(ceng<numRows):
i = 0 # 每层第i个元素
hang = []
while(i<=ceng): # 每层元素数量不超过本层层数
if (i==0 or i==ceng): # 当元素i为每一层的头或尾时
hang.append(1) # 头尾数值为1
else:
hang.append(yanghui[ceng-1][i-1] + yanghui[ceng-1][i])
i += 1 # 计算本层下一个元素
yanghui.append(hang)
ceng += 1 # 计算下一层
return yanghui
8、一维数组动态和
给你一个数组 nums 。数组「动态和」的计算公式为:runningSum[i] = sum(nums[0]…nums[i]) 。请返回 nums 的动态和。
class Solution(object):
def runningSum(self, nums):
runningSum = []
a = nums[0]
for i in range(len(nums)):
if i!=0: # 从第2个元素开始加法
a += nums[i]
runningSum.append(a)
else:
runningSum.append(nums[0])
return runningSum
9、两数组交集(set()函数熟练运用)
给定两个数组 nums1 和 nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]
- 使用set()函数将现有数组进行简化
-
- 对简化后的数组进行比较,并将比较的结果存放进新的数组
class Solution(object):
def intersection(self, nums1, nums2):
a = list(set(nums1))
b = list(set(nums2))
ans = []
for i in range(len(a)):
for j in range(len(b)):
if a[i] == b[j]:
ans.append(b[j])
else:
continue
return ans
10、多数元素(.sort()函数)
给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。你可以假设数组是非空的,并且给定的数组总是存在多数元素。
- 使用.sort(),将原始的数组进行排序
-
- 特殊情况:当数组中的多数元素出现次数小于n/2时,这个方法就失效了
class Solution(object):
def majorityElement(self, nums):
nums.sort()
return nums[len(nums)//2]
11、搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。请必须使用时间复杂度为 O(log n) 的算法。
输入: nums = [1,3,5,6], target = 5 输出: 2
- 时间复杂度为 O(log n) 的算法:题目让我们使用二分法
-
- 第一步:确定左右区间:left, right = 0, len(nums)
- 第二部:进入循环并设置初始中间值mid
- 第三部:是否忽略中间值mid的右侧数值
class Solution(object):
def searchInsert(self, nums, target):
left, right = 0, len(nums) # 采用左闭右开区间[left,right)
while left < right: # 左闭右开所以不能有=,区间不存在
mid = (right-left)//2 + left # 防止溢出, //表示整除
if nums[mid] >= target:
right = mid # 若中点大于目标值,target在左侧,忽略右侧数值
else: # 中点小于目标值,target在右侧侧,忽略左侧数值
left = mid + 1
return left # 最终left=right
12、移动数组中的0
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。请注意 ,必须在不复制数组的情况下原地对数组进行操作。
输入: nums = [0,1,0,3,2]
输出: [1,3,2,0,0]
- 思路:
-
- 使用两个指针,指针1指向要处理的元素;指针2指向与其对比的下一个元素
-
-
- 检测到0调换容易出现调换前后都是0的情况,故使用检测到非0再调换
-
-
-
-
- j
- 0, 1, 0, 3, 2
- i
- 第一次循环nums[i]为0,则j+=1;变为
- j
- 0, 1, 0, 3, 2
- i
- 第二次循环,检测到非0,则调换nums[i], nums[j]
-
-
class Solution(object):
def moveZeroes(self, nums):
i = j = 0 # 使用两个指针
while j < len(nums):
if nums[j] != 0:
nums[i], nums[j] = nums[j], nums[i]
i += 1
j += 1
return
13、加一
给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。你可以假设除了整数 0 之外,这个整数不会以零开头。
示例 1:
输入:digits = [1,2,3]
输出:[1,2,4]
解释:输入数组表示数字 123。
- 思路:
-
- 从数组最后以为判断是否为9,这个特殊情况
-
-
- 是:则将最后一位改为0;前一位+1
- 否直接+1
-
-
- 如果数组权威9;则在if外面返回:return [1] + [0]*n
- 解题中出现的问题:
-
- for i in range(n):
-
-
- 这个循环会让i从0开始增加
- 导致:
-
-
-
-
- if digits[-i] == 9:
- else:
-
-
-
-
-
-
- digits[-i] = digits[-i] + 1出问题
-
-
-
-
-
- 原因:-i = -0;指向了数组的第一个值而不是最后一个
-
class Solution(object):
def plusOne(self, digits):
n = len(digits)
for i in range(1,n+1): # 特别注意,i从1开始
if digits[-i] == 9: # 如果-i位为9
digits[-i] = 0 # 设为0
else:
digits[-i] = digits[-i] + 1
return digits # 最终总有不为9的数值,所以在这里返回数值
return [1] + digits # 如果都是9,则在数组前加上1,并将数组内所有数改为0
14、最小花费爬楼梯(动态规划)
给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。请你计算并返回达到楼梯顶部的最低花费。
示例 1:
输入:cost = [10,15,20]
输出:15
解释:你将从下标为 1 的台阶开始。
- 支付 15 ,向上爬两个台阶,到达楼梯顶部。
总花费为 15 。
- 思路:要求是到达第n级台阶楼层顶部的最小花费,可以用动态规划来解,要求出状态转移方程。
- 理解题意需要注意两点:
-
- 第i级台阶是第i-1级台阶的阶梯顶部。
- 踏上第i级台阶花费cost[i],直接迈一大步跨过而不踏上去则不用花费。
- 到达第i级台阶的阶梯顶部的最小花费,有两个选择:
-
- 从第i-1级台阶开始(上台阶走1步)
-
-
- 先付出最小总花费zh[i-1]到达第i级台阶(即第i-1级台阶的阶梯顶部)
- 踏上第i级台阶需要再花费cost[i],再迈一步到达第i级台阶的阶梯顶部,
- 最小总花费为zh[i-1] + cost[i];
-
-
- 从第i-2级台阶开始(上台阶走2步)
-
-
- 先付出最小总花费zh[i-2]到达第i-1级台阶(即第i-2级台阶的阶梯顶部),
- 从i-1级台阶直接到第i级台阶的阶梯顶部,需要花费cost[i-1]
- 最小总花费为zh[i-2] + cost[i-1]);
-
- 则minCost[i]是上面这两个最小总花费中的最小值。
-
- zh[i] = min(zh[i-1] + cost[i], zh[i-2] + cost[i-1])
- 假设从地面开始爬楼梯,用cost[-1]=0代表从地面开始的花费
- 最小总花费的初始值为:
-
- 第0级台阶: zh[0] = min(zh[-1]+cost[0], zh[-2]+cost[-1]) = min(0+cost[0], 0+cost[-1]) = min(cost[0], 0) = 0,
- 第1级台阶: zh[1] = min(zh[0]+cost[1], zh[-1]+cost[0]) = min(cost[1], cost[0])。
- 因为可以选择初始阶梯
-
-
- 故:zh[0] = zh[1] = 0
-
class Solution(object):
def minCostClimbingStairs(self, cost):
n = len(cost)
zh = [0]*n # 共有n个阶梯,计算总花费要计算到n
for i in range(1,n): # 从第2个阶梯开始爬
zh[i] = min(zh[i-1] + cost[i], zh[i-2] + cost[i-1])
return zh[-1]
15、分饼干(贪心算法)
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
示例 1:
输入: g = [1,2,3], s = [1,1]
输出: 1
解释:
你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。
虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。
所以你应该输出1。
- 思路:以 g = [10,9,8,7],s = [5,6,7,8] 为例。
-
- 1、对 g 和 s 排序,目的就是为了从左到右遍历的时候,都是按照从小到大遍历。
-
- 2、以第一个孩子为基准,找饼干
-
- 3、找到饼干后将孩子(i)+1
class Solution(object):
def findContentChildren(self, g, s):
g.sort()
s.sort()
j = i = 0
sum= 0
for j in range(len(s)):
if i<len(g) and g[i]<=s[j]:
i+=1
sum+=1
return sum