数据结构part1——数组

二分法查找

35.搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

示例 1:

输入: nums = [1,3,5,6], target = 5
输出: 2
示例 2:

输入: nums = [1,3,5,6], target = 2
输出: 1
示例 3:

输入: nums = [1,3,5,6], target = 7
输出: 4

class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        l,r=0,len(nums)-1
        while l<=r:
            mid=(l+r)>>1
            if nums[mid]>target:
                r=mid-1
            elif nums[mid]<target:
                l=mid+1
            else:
                return mid
        //分别处理如下四种情况
        // 目标值在数组所有元素之前 [0,0)
        // 目标值等于数组中某一个元素 return middle
        // 目标值插入数组中的位置 [left, right) ,return right 即可
        // 目标值在数组所有元素之后的情况 [left, right),return right 即可
        return r+1

思路:我们从数组的最左端和最右端开始遍历,每次取区间为[left,right]的中间值nums[mid],然后将mid位置的数字与目标数字比较,不断缩减区间[left,right]的大小,最后返回结果。
时间复杂度:O(logn)

704.二分查找

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

示例 1:

输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
示例 2:

输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        if len(nums)==1:
            return 0 if nums[0]==target else -1
        l,r=0,len(nums)-1
        while l<=r:
            mid=(l+r)>>1
            if nums[mid]>target:
                r=mid-1
            elif nums[mid]<target:
                l=mid+1
            else:
                return mid
        return -1

这道题相对来说就是搜索插入位置的简化表,其实就是搜索数组中是否有我们需要找到的目标数字target,没有就返回-1,有就返回对应的索引index

移除元素

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

说明:

为什么返回数值是整数,但输出的答案是数组呢?

请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

你可以想象内部操作如下:

// nums 是以“引用”方式传递的。也就是说,不对实参作任何拷贝
int len = removeElement(nums, val);

// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}

示例 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],也会被视作正确答案。
示例 2:

输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。

提示:

0 <= nums.length <= 100
0 <= nums[i] <= 50
0 <= val <= 100

class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        slow=0
        for fast in nums:
            if fast!=val:
                nums[slow]=fast
                slow+=1
        return slow

这道题我们可以用一个巧妙的双指针法来解决,我们使用fast’指针来遍历数组,slow指针用来指向数组中与val值相等的数字的位置,当fast指针遍历的数字不等于val值的时候,就将slow指针处的数字替换成fast指针处的值,相当于我们把数组中数字为val的数字覆盖了,遍历完数组时,slow指针指向的就是最后一个值不等于val的数字,我们直接返回即可。

比较含退格的字符串

给定 S 和 T 两个字符串,当它们分别被输入到空白的文本编辑器后,判断二者是否相等,并返回结果。 # 代表退格字符。

注意:如果对空文本输入退格字符,文本继续为空。

示例 1:

输入:S = “ab#c”, T = “ad#c”
输出:true
解释:S 和 T 都会变成 “ac”。
示例 2:

输入:S = “ab##”, T = “c#d#”
输出:true
解释:S 和 T 都会变成 “”。
示例 3:

输入:S = “a##c”, T = “#a#c”
输出:true
解释:S 和 T 都会变成 “c”。
示例 4:

输入:S = “a#c”, T = “b”
输出:false
解释:S 会变成 “c”,但 T 仍然是 “b”。

提示:

1 <= S.length <= 200
1 <= T.length <= 200
S 和 T 只含有小写字母以及字符 ‘#’。

class Solution:
    def backspaceCompare(self, s: str, t: str) -> bool:
        i,j=len(s)-1,len(t)-1
        count_s,count_t=0,0
        while i >= 0 or j >= 0:
            # s字符串的第一次遍历
            while i >= 0:
                if s[i] == "#":
                    count_s += 1
                    # 指针往前推一个
                    i -= 1
                elif count_s>0:
                    i -= 1
                    count_s -= 1
                else:
                    break

            # t同上
            while j >= 0:
                if t[j] == "#":
                    count_t += 1
                    # 指针往前推一个
                    j -= 1
                elif count_t>0:
                    # 代表有退格符#
                    j -= 1
                    count_t -= 1
                else:
                    break
            if i >= 0 and j >= 0:
                # 不相等返回False
                if s[i] != t[j]:
                    return False
            elif i>=0 or j>=0:
                return False
            # 相等就不管,看下一个字符
            j -= 1
            i -= 1
        return True

思路大概为:首先我们要从后往前遍历,因为退格符删除的是前面的字符,反序遍历才能确保我们遍历过的字符都是相等的,那么在我们遍历字符的时候又分为三种情况:
1.当遍历的字符是#,代表我们可以跳过一个字符,给我们的跳跃量count加1
2.当遍历的字符不是#,我们首先看count是否>0,大于0就当作跳过这个字符,不比较
3.当遍历的字符不是#而且count=0,此时退出循环,对另一个字符串也进行一次遍历,当另一个数组也退出循环后,比较两个字符串当前位置的字符是否相等。

有序数组的平方

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

示例 1:

输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]
示例 2:

输入:nums = [-7,-3,2,3,11]
输出:[4,9,9,49,121]

提示:

1 <= nums.length <= 104
-104 <= nums[i] <= 104
nums 已按 非递减顺序 排序

class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        i,j=0,len(nums)-1
        n=len(nums)
        res=[0]*n
        while  i<=j:
            if nums[i]*nums[i]>=nums[j]*nums[j]:
                n-=1
                res[n]=nums[i]*nums[i]
                i+=1
            else:
                n-=1
                res[n]=nums[j]*nums[j]
                j-=1
        return res

这道题的思路其实很容易想到,就是对数组进行排序,不过排序的标准是平方值的大小,也就是说负数也可能比正数大,那么题目首先给出了一个升序数组,我们就初始化一个结果集数组存放结果,然后使用双指针法,从最左和最右端同时开始比较
1.当左边的平方值大于等于右边的平方时,应该将左边的平方加入res
2.否则将右端的平方加入res

长度最小的子数组

给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。

示例 1:

输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
示例 2:

输入:target = 4, nums = [1,4,4]
输出:1
示例 3:

输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0

提示:

1 <= target <= 109
1 <= nums.length <= 105
1 <= nums[i] <= 105

进阶:

如果你已经实现 O(n) 时间复杂度的解法, 请尝试设计一个 O(n log(n)) 时间复杂度的解法。

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        res=float('inf')
        sublength=0
        for i in range(len(nums)):
            sum1=0
            for j in range(i,len(nums)):
                sum1+=nums[j]
                if sum1>=target:
                    sublength=j-i+1
                    res=sublength if res>sublength else res
                    break
        return 0 if res==float('inf') else res

暴力法双重for循环——时间复杂度:O(n**2)

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        res=float('inf')
        sublength=0
        sum1=0
        i=0
        for j in range(len(nums)):
            sum1+=nums[j]
            #注意我们这里的while相当于一个判定条件的意思,可以近似看作if
            while sum1>=target:
                sublength=j-i+1
                res=sublength if sublength<res else res
                sum1-=nums[i]
                i+=1
        return 0 if res==float('inf') else res
        //这里要判断res是否有发生变化
        //因为会出现一种情况就是整个数组的和都比target小,此时就应该返回0
     	//代表找不到和为target的子数组

滑动窗口法——时间复杂度O(n)
暴力法其实并不难,就是双重循环,找到目标和为target的子数组,然后更新最小长度即可,但是双重循环的时间复杂度较高,我们可不可以用一种更好的方法去解决呢?
其实就是用滑动窗口法
在这里插入图片描述
滑动窗口和双指针法非常类似,只需要遍历一次数组,**滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)的暴力解法降为O(n)**那么具体的思路是如何呢?
我们还是用题目中的例子
我们设置i指针指向数组的开头,然后从头遍历数组,每次都累加遍历的数组值和target比较,那么这时候有两种情况:
1.累加和sum<target,继续遍历(相当于扩大窗口)
2.累加和sum>=target,这时候滑动窗口的精妙之处就体现出来了
1)我们首先要记录此时的窗口大小——j-i+1
2)然后将其与res比较,保存最小的值(题目找出最小长度数组在这一步完成)
3)然后我们要减去nums[i]的值,相当于把i向前滑动(滑动窗口的精妙之处
3.最后返回res即可(注意这里要判断res是否有发生过改变)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值