日常系列LeetCode《1·数组常用技巧篇》

Posted 常某某的好奇心

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了日常系列LeetCode《1·数组常用技巧篇》相关的知识,希望对你有一定的参考价值。

数据规模->时间复杂度

<=10^4 😮(n^2)
<=10^7:o(nlogn)
<=10^8:o(n)
10^8<=:o(1)

数组元素作为索引下标

优:减少内存占用(map:数组+链表,优化哈希冲突cost)

注:元素范围不能太大

LC442:https://leetcode.cn/problems/find-all-duplicates-in-an-array/
#时间复杂度为 O(n) 且仅使用常量额外空间的算法

#方案1:访问使用负数标记
class Solution:
    def findDuplicates(self, nums: List[int]) -> List[int]:
    	#o(1);假定返回的数组不算在额外的空间内
        res=[]
        #o(n):负数标记
        for x in nums:
            index=abs(x)-1
            if nums[index]<0: res.append(abs(x)) #已访问
            else: nums[index]*=-1
        return res
#方案2:访问使用+n标记
class Solution:
    def findDuplicates(self, nums: List[int]) -> List[int]:
        #o(n);+n标记
        n=len(nums)
        for x in nums:
            index=(x-1)%n
            nums[index]+=n
        #o(1):假定返回的数组不算在额外的空间内
        res=[]
        for i in range(n):
            if nums[i]>2*n:res.append(i+1)
        return res

LC448【top100】:https://leetcode.cn/problems/find-all-numbers-disappeared-in-an-array/
不使用额外空间且时间复杂度为 O(n)

class Solution:
    def findDisappearedNumbers(self, nums: List[int]) -> List[int]:
        #o(1)
        res=[]
        #o(n):+n标记
        n=len(nums)
        for x in nums:
            index=(x-1)%n
            nums[index]+=n
        #
        for i in range(n):
            if nums[i]<=n:res.append(i+1)#未访问
        return res

字符串字符作为下标索引

LC1002:https://leetcode.cn/problems/find-common-characters/
1 <= words.length <= 100
1 <= words[i].length <= 100
words[i] 由小写英文字母组成

class Solution:
    def commonChars(self, words: List[str]) -> List[str]:
        #o(n^2):利用words[0]初始minfreq
        minfreq=[0]*26
        for sr in words[0]:
            for c in sr:
                minfreq[ord(c)-ord('a')]+=1
        #o(n^2):统计freq[i],并获得最后minfreq
        for i in range(1,len(words)):
            freq=[0]*26
            for c in words[i]:
                freq[ord(c)-ord('a')]+=1
            for j in range(26):
                minfreq[j]=min(minfreq[j],freq[j])
        #minfreq转成字符数组
        res=list()
        for i in range(26):
            res.extend(minfreq[i]*[chr(i+ord('a'))])
        return res

LC1370:https://leetcode.cn/problems/increasing-decreasing-string/
1 <= s.length <= 500
s 只包含小写英文字母

class Solution:
    def sortString(self, s: str) -> str:
        #o(n^2):统计counts
        counts=[0]*26
        for c in s:
            counts[ord(c)-ord('a')]+=1
        #o(n^2):上升-下降
        res=list()
        while len(res)<len(s):
            #上升
            for i in range(26):
                if counts[i]>0:
                    res.append(chr(i+ord('a')))
                    counts[i]-=1
            #下降            
            for i in range(25,-1,-1):
                if counts[i]>0:
                    res.append(chr(i+ord('a')))
                    counts[i]-=1
        return  "".join(res)

双指针

1)快慢指针:
slow指针:已经处理的区域
fast 指针:指向当前正在处理的元素

2)对撞指针

LC283【top100】:https://leetcode.cn/problems/move-zeroes/
原地对数组进行操作
尽量减少完成的操作次数

方案一:交换
class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        #O(1):快慢指针
        slow=fast=0
        #o(n):比较,交换
        while fast< len(nums):
            if nums[fast]!=0:
                if slow!=fast: #减少交换次数,例如7-6-4-3-8
                    nums[slow],nums[fast]=nums[fast],nums[slow]
                slow+=1
            fast+=1
        #
        return nums
        
方案二:优化-使用赋值,代替交换;赋值语句比比较语句快
class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        #o(1):快慢指针
        slow=fast=0
        #o(n);比较,赋值
        while fast< len(nums):
            if nums[fast]!=0:
                nums[slow]=nums[fast]
                slow+=1
            fast+=1
        #尾部设0
        for i in range(slow,len(nums)) :
            nums[i]=0
        #
        return nums

LC26:https://leetcode.cn/problems/remove-duplicates-from-sorted-array/
空间复杂度为O(1)
1 <= nums.length <= 3 * 10^4
-104 <= nums[i] <= 104
nums 已按 升序 排列
不需要考虑数组中超出新长度后面的元素

class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        #o(1):快慢指针
        slow=0
        fast=1
        #o(n):赋值,交换
        while fast<len(nums):
            if nums[fast]!=nums[slow]:
                slow+=1
                nums[slow]=nums[fast]
            fast+=1
        #返回k
        return slow+1

LC80:https://leetcode.cn/problems/remove-duplicates-from-sorted-array-ii/
空间复杂度为O(1)
1 <= nums.length <= 3 * 10^4
-104 <= nums[i] <= 104
nums 已按升序排列
不需要考虑数组中超出新长度后面的元素

class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        #o(1)
        slow=1#指向已处理的区域的最后一个位置
        fast=2
        #o(n):比较,赋值
        while fast<len(nums):
            if nums[fast]!=nums[slow-1]:
                slow+=1
                nums[slow]=nums[fast]
            fast+=1
        #
        return slow+1

LC27:https://leetcode.cn/problems/remove-element/
空间复杂度为O(1)
0 <= nums.length <= 100
0 <= nums[i] <= 50
0 <= val <= 100
不需要考虑数组中超出新长度后面的元素
进阶:如果要删除的元素特别少,怎样可以提高性能

#方案一:快慢指针
class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        #o(1)
        slow=fast=0
        #o(n)
        while fast<len(nums):
            if nums[fast]!=val:
                nums[slow]=nums[fast]
                slow+=1
            fast+=1
        #
        return slow

#方案二:进阶-对撞指针
#例如1-2-2-3-2-1-2
class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        #o(1)
        left,right=0,len(nums)-1
        #o(n)
        while left<=right:
            if nums[left]==val:#直至替换到非val值,left+=1
                nums[left]=nums[right]
                right-=1
            else:left+=1
        #
        return left

LC344:https://leetcode.cn/problems/reverse-string/
空间复杂度o(1)
1 <= s.length <= 105
s[i] 都是 ASCII 码表中的可打印字符

class Solution:
    def reverseString(self, s: List[str]) -> None:
        """
        Do not return anything, modify s in-place instead.
        """
        #o(1)
        left,right=0,len(s)-1
        #o(n)
        while left<=right:
            s[left],s[right]=s[right],s[left]
            left,right=left+1,right-1
        #
        return s

LC125【剑18】:https://leetcode.cn/problems/valid-palindrome/
1 <= s.length <= 2 * 10^5
字符串 s 由 ASCII 字符组成

class Solution:
    def isPalindrome(self, s: str) -> bool:
        #o(1)
        left,right=0,len(s)-1
        #o(n^2)
        while left<right:
            #忽略处理
            while left<right and not s[left].isalnum():
                left+=1
            while left<right and not s[right].isalnum():
                right-=1
            #转换,比较
            if left<right:
                if s[left].lower()!=s[right].lower():
                    return False
                left,right=left+1,right-1
        return True

LC11【top100】:https://leetcode.cn/problems/container-with-most-water/

n == height.length
2 <= n <= 10^5
0 <= height[i] <= 10^4

class Solution:
    def maxArea(self, height: List[int]) -> int:
        #o(1)
        left,right=0,len(height)-1
        #o(n)
        res=0
        while left<right:
            area=min(height[left],height[right])*(right-left)
            res=max(res,area)
            #更新:在宽度一定的情况下面积取决于最短的那个边
            if height[left]<=height[right]:
                left+=1
            else:right-=1
        #
        return res

LC1480:https://leetcode.cn/problems/running-sum-of-1d-array/

1 <= nums.length <= 1000
-10^6 <= nums[i] <= 10^6

class Solution:
    def runningSum(self, nums: List[int]) -> List[int]:
        #o(n)
        prefixsum=[0]*len(nums)
        #o(n):动态规划-通过中间值推导消除重复计算
        prefixsum[0]=nums[0]
        for i in range(1,len(nums)):
            prefixsum[i]=prefixsum[i-1]+nums[i]
        #
        return prefixsum

LC238【top100】:https://leetcode.cn/problems/product-of-array-except-self/
提示:
2 <= nums.length <= 10^5
-30 <= nums[i] <= 30
保证数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内
进阶:
你可以在 O(1) 的额外空间复杂度内完成这个题目吗?( 出于对空间复杂度分析的目的,输出数组不被视为额外空间。)

class Solution:
    def productExceptSelf(self, nums: List[int]) -> List[int]:
        #0(1):返回数组不作空间
        res=[0]*len(nums)
        #o(n)
        #①存前缀积
        res[0]=1
        for i in range(1,len(nums)):
            res[i]=res[i-1]*nums[i-1]
        #②o(1):优化空间复杂度-后缀积作变量更新
        sufixproduct=1
        for i in range(len(nums)-1,-1,-1):
            res[i]=res[i]*sufixproduct
            #更新
            sufixproduct=sufixproduct*nums[i]
        #
        return res

以上是关于日常系列LeetCode《1·数组常用技巧篇》的主要内容,如果未能解决你的问题,请参考以下文章

日常系列LeetCode《3·二维数组篇》

日常系列LeetCode《6·位运算篇》

日常系列LeetCode《9·哈希查找篇》

日常系列LeetCode《8·二分查找篇》

日常系列LeetCode《13·综合应用1篇》

日常系列LeetCode《5·数学篇》