前言
今年想开始做LeetCode,但在做了几道题后发现,由于没有系统学过数据结构,所以个人感觉做起来还是比较困难的。好在这学期开始上’数据构与算法’这门课,所以正好可以边学边练。这里会记录一些新接触的算法概念和做题心得理解以及我的疑问等等。由于是小白,所以不可避免会有错误和问题,真诚希望看过博客的各位斧正。
另外,这道题我也是借鉴了其他博主的文章和代码,自己加了点理解和注释而已,还要继续学习。原文链接在这里,写的很详细,有助于加深理解。
链接: 4. 寻找两个正序数组的中位数
链接: 力扣经典 4. 寻找两个正序数组的中位数(多种语言解)
一、LC第4题题目描述
二、思路分析
1.时间复杂度
因为要求的时间复杂度为log(m+n),因此应使用二分查找。
关于二分查找的基本概念,我在文末整理了一些(GPT复制粘贴的)
2.思路分析
输入:两个正序数组nums1、nums2;
输出:合并后新数组newnums的中位数
我简单梳理成以下6条:
①将数组nums1和nums2各自平均切分为左右两部分:(如果数组长度是偶数就是均分,如果长度是奇数就是其中一半比另一半多一个数据)
num1 = num1left + num1right;num2 = num2left + num2right
②合并后的新数组newnumsTotal由newnumsTotal_left+newnumsTotal_right组成,
其中 newnumsTotal_left = nums1left + nums2left,
newnumsTotal_right = nums1right + nums2right
③并且保证nums1left [i-1] < nums2right [j] ,且nums2left [j-1] < nums1right [i]
即保证切片后,两个数组各自的左半边部分的最大值小于另一个数组右半部分的最小值。(i,j分别为nums1、nums2的切片处索引值)
④由于合并后的新数组的左右半部分长度是固定的*(nums1个nums2合并后总长度的一半(偶数),或其中一半比另一半多1(总长度为奇数时))*,所以只要确定数组nums1的切片索引值i,即可确定nums2的切片索引值 j —j=len(newnumTotal_left)-i-2,并有 i+j等于两数组长度之和的一半(或一半多一,当总元素个数为奇数时)。
⑤最后,根据左右两部分的长度关系确定中位数:如果总长度是奇数,中位数是左边最大的数;如果总长度是偶数,中位数是左边最大的数和右边最小的数的平均值。
⑥为了优化代码,可以先保证num1总是较短的那个数组
三、二分查找
【二分查找】:
二分查找(Binary Search)是一种在有序数组中查找特定元素的搜索算法。它的工作原理是将数组分成两半,判断所查找的元素可能存在于哪一半中,然后继续在那一半中查找,直到找到元素或确定元素不存在。
【二分查找的步骤】:
① 确定查找范围:初始化两个指针(或索引),分别指向数组的首元素和尾元素,确定查找的初始范围。
②计算中间位置:通过取首指针和尾指针的平均值(为了避免整数除法导致的结果偏差,通常采用 (low + high) / 2 的变形 low + (high - low) / 2),计算出中间位置。(但本人不太了解哦,没有写过)
③比较并调整范围:
(1)将中间位置的元素与要查找的元素进行比较。如果中间位置的元素正好是要查找的元素,则查找成功,返回该元素的索引或值。
(2)如果要查找的元素小于中间位置的元素,则说明要查找的元素位于当前范围的左半部分,将尾指针更新为中间位置的前一个位置(即 high = mid - 1)。
(3)如果要查找的元素大于中间位置的元素,则说明要查找的元素位于当前范围的右半部分,将首指针更新为中间位置的下一个位置(即 low = mid + 1)。
重复查找:重复步骤2和步骤3,直到找到元素或查找范围为空(即 low > high),此时表示元素不存在于数组中。
【时间复杂度】
二分查找的时间复杂度为 O(log n),其中 n 是数组的长度。
四、Python代码
class Solution:
def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
A, B = nums1, nums2
total = len(nums1) + len(nums2)
half = total//2 #//表示向下取整
if len(A) > len (B) :
A, B =B, A #保证nums1总是较短的那个数组
l,r = 0, len(A)-1 #设置双指针—— l 和r 为二份查找(找i)的左右边界
while True:
i = (l+r)//2 #A的切点
j = half-i-2 #B的切点
Aleft = A[i] if i >= 0 else float ("-infinity") #将数组分为左右两部分
Aright = A[i+1] if (i+1) < len(A) else float("infinity")
Bleft = B[j] if j >= 0 else float ("-infinity")
Bright = B[j+1] if (j+1) < len(B) else float("infinity")
#若分割正确
if Aleft <= Bright and Bleft <= Aright:
if total % 2: #如果合并后的新数组长度为奇数(即 total % 2 =1 =True)
return min(Aright,Bright)
return (max(Aleft, Bleft) + min(Aright, Bright)) / 2 #否则如果合并后的数组长度不是奇数而是偶数,返回两个中间值的平均值
#如果分割不正确,且A左大于B右,说明切片太靠右了,此时让二分查找的右边界向左移
elif Aleft > Bright:
r = i - 1
#如果分割不正确,且B左大于A右,说明切片太靠左了,此时让二分查找的左边界右移
else:
l = i + 1
五、总结与问题反思
如果看过前言附上的两篇优质博客,应该能发现代码我没有做改动,只是按照自己的理解梳理了一下,然而提交答案时,力扣给我判题“超过时间限制”,没有让我通过,我也不知道是什么原因~ 有遇到同样问题的伙伴吗?另外如果文章里有错误敬请斧正!