【数组和字符串】(一) 数组简介

目录

一、数组简介

1.1 寻找数组的中心索引

1.2 搜索插入位置

1.3 合并区间


一、数组简介

1.1 寻找数组的中心索引

1.1.1 问题描述

1.1.2 求解过程

法一:直接使用 for loop +  sum() + 切片来无脑暴力求解,复杂度至少 O(n^2)。对序列求和需要遍历范围内的所有元素,在 for 循环内对切片反复遍历求和,进行了太多的重复冗余计算,以至于效率肯定很低!虽然 Python 内置函数经过了优化,但也不能随便乱用,这时体现了数据结构与算法-复杂度分析的重要性了。(对具有 k 个元素的 seq 使用 sum() 复杂度为 O(k))

2020/05/31 - 8.50 % - 相当低效!

class Solution:
    def pivotIndex(self, nums: List[int]) -> int:
        for i in range(len(nums)):
            if sum(nums[:i]) == sum(nums[i+1:]):
                return i
        return -1

法二:维护左、右两个求和列表(双指针),然后再进行遍历与对比,减少了大量不必要的求和操作,复杂度 O(n)。

2020/05/31 - 94.08% - 还有优化空间

class Solution:
    def pivotIndex(self, nums: List[int]) -> int:
        if nums == [0]:
            return 0
            
        left_sum = [0]  # 从左到右累计和表
        right_sum = [0]  # 从右到左累计和表
        # 求和
        for i in range(len(nums)-1):                     # 例:nums=[1,7,3,6,5,6]
            left_sum.append(nums[i] + left_sum[i])       # 左:left_sum=[0,1,8,11,17]
            right_sum.append(nums[-i-1] + right_sum[i])  # 右:right_sum=[0,6,11,17,20]
        # 查表比较
        for j in range(len(left_sum)):
            if left_sum[j] == right_sum[-j-1]:
                return j
        return -1

法三:结合 Python 内置函数 sum() 和 enumerate() 加速求和与迭代,以减少 for 循环的使用次数,复杂度 O(n)。

2020/05/31 - 97.71% - 效率最高

class Solution:
    def pivotIndex(self, nums: List[int]) -> int:  
        right_sum = sum(nums)
        left_sum = 0
        
        for i, num in enumerate(nums):
            right_sum -= nums[i]
            if left_sum == right_sum:
                return i
            else:
                left_sum += nums[i]
        return -1

1.2 搜索插入位置

1.2.1 问题描述

1.2.2 求解过程

 法一:顺序查找,复杂度 O(n)

2020/05/31 - 72.76% - 还有提升空间!(使用一个变量维护 index 没有 enumerate() 快!) (其实本法仅次于二分查找)

class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        for index, num in enumerate(nums):
            if target <= num:
                return index
        return index+1
# ---------------------------------------------------------------------------
class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        nums.append(target)
        for index, num in enumerate(nums):
            if target <= num:
                return index

 法二:手写二分查找,复杂度 O(logn)。但写法不恰当,可能踩雷了!

2020/05/31 - 31.37% - 不恰当的写法下,还不如顺序查找!

class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        # 两端特殊情况处理
        if target <= nums[0]:  # 特殊情况 nums=[1], target=1
            return 0
        if target > nums[-1]:
            return len(nums)
        # 中间正常二分查找
        start = 0
        end = len(nums)-1
        while True:
            current = (start+end) // 2  # 中间当前 index
            
            if nums[current-1] < target <= nums[current]:
                return current
            elif target > nums[current]:
                start = current + 1
            else:
                end = current - 1

 法二:内置二分查找,复杂度 O(logn)。

2020/05/31 - 50.93% - 竟然也没有快多少??

class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:        
        if target > nums[-1]:
            return len(nums)
        from bisect import bisect_left
        return bisect_left(nums, target)

 法三: 参考 - 二分查找,复杂度 O(logn)。

2020/05/31 - 50.93%

class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        left = 0
        right = len(nums) - 1
        
        if nums[right] < target:
            return len(nums)
        
        while left <= right :
            mid = (left + right) // 2   # 更慢?
            if nums[mid] > target:
                right = mid - 1
            elif nums[mid] < target:
                left = mid + 1
            else:
                return mid
        return left

 法三: 参考 - 标准二分查找,复杂度 O(logn)。目测是 while True 整除 // 导致速度大幅下降?!!

2020/05/31 - 96.65% - 基本是最快的了

class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        left = 0
        right = len(nums) - 1
        
        if nums[right] < target:
            return len(nums)
        
        while left <= right :
            mid = int((left + right) / 2)  # 修正!?!  ## 为什么这个比 // 更快呢?
            if nums[mid] > target:
                right = mid - 1
            elif nums[mid] < target:
                left = mid + 1
            else:
                return mid
        return left

 小结:在最快的方法中,没有使用 while True 的!!但不少还是使用了 // 的。然而,网上并未找到有关精确除 / 和 整除(地板除) // 之间的效率差异问题,也没有找到 Python3 中 while True 效率低的原因。

1.3 合并区间

1.3.1 问题描述

1.3.2 求解过程

法一:先用 if 语句排除特殊的少元素情况,复杂度 O(1)。然后,由于初始列表元素可能是乱序的,难以用固定规则把控,所以根据左元素从小到大排序,复杂度至少 O(nlogn)。然后用 for 训练遍历列表元素并比较,复杂度 O(n)。最后收尾 append(),复杂度 O(1)。综上,复杂度取决于排序过程,为 O(nlogn)。

2020/05/31 - 82.70% - 还有优化空间!

class Solution:
    def merge(self, intervals: List[List[int]]) -> List[List[int]]:
        if len(intervals) <= 1:  # 特殊情况 [], [[1,2]]
            return intervals
        # 左元素从小到大排序
        intervals.sort(key=lambda x: x[0])  # 特殊情况-乱序 [[2,3],[4,5],[6,7],[8,9],[1,10]]
        result = []
        temp = intervals[0]  # 首个 list 元素
        for index in range(1, len(intervals)):  
            if temp[1] < intervals[index][0]:  # 特殊情况-无交集 [[1,4],[5,6]]
                result.append(temp)  # 保存
                temp = intervals[index]  # 更新
            else:  # 正常情况-有交集 [[0,4],[1,4]]
                temp[1] = max(temp[1], intervals[index][1])
        result.append(temp)  # 收尾
        return result

小贴士Timsort 是一种混合、稳定高效的排序算法,源自合并排序和插入排序,旨在很好地处理多种真实数据。它由 Tim Peters 于2002年用在 Python 中。该算法查找已排序的数据的子序列,并使用该知识更有效地对其余部分进行排序。这是通过将已识别的子序列 (称为运行) 与现有运行合并直到满足某些条件来完成的。从 2.3 版本开始,Timsort 一直是 Python 的标准排序算法。如今,Timsort 已是 Python、Java、Android 平台和 GNU Octave 的默认排序算法。

本质上,Timsort 是一个经过大量优化的归并排序,而归并排序已经到达了最坏情况下,比较排序算法时间复杂度的下界。故最坏情况下,Timsort 时间复杂度为 O(nlogn)。最佳情况下,即输入已排好序,它则以线性时间运行 O(n)。可见 Timsort 是目前最好的排序方式。

参考链接:https://blog.csdn.net/sinat_35678407/article/details/82974174

法二:使用 enumerate() 来优化 for-range 循环,并简化表示

2020/05/31 - 92.64% - 效率最高

class Solution:
    def merge(self, intervals: List[List[int]]) -> List[List[int]]:
        if len(intervals) <= 1:  # 特殊情况 [], [[1,2]]
            return intervals
        # 左元素从小到大排序, 复杂度 O(nlogn)
        intervals.sort(key=lambda x: x[0])  # 特殊情况-乱序 [[2,3],[4,5],[6,7],[8,9],[1,10]]
        result = []
        temp = intervals[0]  # 首个 list 元素
        for index, elem in enumerate(intervals):  # 使用 enumerate() 以避免 range()
            if temp[1] < elem[0]:
                result.append(temp)
                temp = elem
            else:
                temp[1] = max(temp[1], elem[1])
        result.append(temp)  # 收尾
        return result

网址:https://leetcode-cn.com/explore/learn/card/array-and-string/198/introduction-to-array/1427/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章