数组
二分查找
重要的是判断是否要取等于号。
搜索旋转排序数组
整数数组 nums 按升序排列,数组中的值 互不相同 。
在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], …, nums[n-1], nums[0], nums[1], …, nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。
给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。
示例 1:
输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4
示例 2:
输入:nums = [4,5,6,7,0,1,2], target = 3
输出:-1
class Solution(object):
def search(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
# 多次利用二分查找
left = 0
right = len(nums)-1
while(left <= right):
mid = (left+right)/2
if nums[mid] == target:
return mid
# 左边是有序的
if nums[mid] >= nums[left]:
# 目标值在左边
if nums[mid] > target and nums[left] <= target:
right = mid - 1
else:
left = mid + 1
# 右边是有序的
else:
# 目标值在右边
if nums[right] >= target and nums[mid] < target:
left = mid + 1
else:
right = mid - 1
return -1
这个利用二分查找找到中点,之后判断哪边是有序的,之后再进行分析,可以说是多用了一次二分查找。其中的判断条件需要额外的注意。
双指针
双指针的一个典型应用就是实现原地删除,第一个指针指向新的数组(还是原来的数组),当且仅当第二个指针指向的那个数字是满足要求的元素的时候,才向前移动。
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案
class Solution(object):
def removeElement(self, nums, val):
"""
:type nums: List[int]
:type val: int
:rtype: int
"""
# 采用快慢指针的思想,一个用来查找,一个用来填值
slow = 0
fast = 0
while fast < len(nums):
if nums[fast] != val:
nums[slow] = nums[fast]
slow = slow + 1
fast = fast + 1
return slow
轮转数组
给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。
示例 1:
输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右轮转 1 步: [7,1,2,3,4,5,6]
向右轮转 2 步: [6,7,1,2,3,4,5]
向右轮转 3 步: [5,6,7,1,2,3,4]
示例 2:
输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释:
向右轮转 1 步: [99,-1,-100,3]
向右轮转 2 步: [3,99,-1,-100]
class Solution(object):
def rotate(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: None Do not return anything, modify nums in-place instead.
"""
# 利用三次reverse 反转整个区间
def reverse(i,j):
while i<j:
nums[i],nums[j]=nums[j],nums[i]
i+=1
j-=1
# 注意两点,首先是往后移几位意味着湖面的几位数字就要到前面去了,而不是前面的数字
# 其次是放着k超出输入长度的情况,所以来个取模运算
k = k % len(nums)
reverse(0,len(nums)-1)
reverse(0,k-1)
reverse(k,len(nums)-1)
return nums
本来想找双指针的题,之歌分本就不是。这个题用了反转的技巧,很多关于链表的题目都是有对于 反转的使用。
左右指针
属于双指针的一种,定义左右指针,相向而行。
给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]
class Solution(object):
def sortedSquares(self, nums):
"""
:type nums: List[int]
:rtype: List[int]
"""
def pows(x):
return x*x
# 思路:从两边向中间走,比较大小
left = 0
right = len(nums) - 1
result = [1]*len(nums)
k = len(nums) - 1
while left <= right:
if pows(nums[left]) > pows(nums[right]):
result[k] = pows(nums[left])
left += 1
else:
result[k] = pows(nums[right])
right -= 1
k -= 1
return result
滑动窗口
你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits 表示,其中 fruits[i] 是第 i 棵树上的水果 种类 。
你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果:
你只有 两个 篮子,并且每个篮子只能装 单一类型 的水果。每个篮子能够装的水果总量没有限制。
你可以选择任意一棵树开始采摘,你必须从 每棵 树(包括开始采摘的树)上 恰好摘一个水果 。采摘的水果应当符合篮子中的水果类型。每采摘一次,你将会向右移动到下一棵树,并继续采摘。
一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。
给你一个整数数组 fruits ,返回你可以收集的水果的 最大 数目。
输入:fruits = [1,2,1]
输出:3
解释:可以采摘全部 3 棵树。
# 这个问题滑动窗口没有问题,怎样去表达问题是关键,这里比价合适的是采用字典。
left = right = 0
basket = {}
# 水果总数
nums = 0
result = 0
while right < len(fruits):
if basket.has_key(fruits[right]):
basket[fruits[right]] += 1
nums += 1
right += 1
elif len(basket) < 2:
basket[fruits[right]] = 1
nums += 1
right += 1
else:
# 将第一个篮子中的水果全部弹出,使一个篮子空出来
# 这个地方有个坑是我执行完这个分支不能右移了,因为我这分支是right水果还没入篮呢,再对其右移会漏掉一个水果
while len(basket) >= 2:
basket[fruits[left]] -= 1
nums -= 1
if basket[fruits[left]] == 0:
basket.pop(fruits[left])
left += 1
result = result if result > nums else nums
return result