数组算法刷题

数组

二分查找

重要的是判断是否要取等于号。
搜索旋转排序数组

整数数组 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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值