LeetCode刷题笔记4寻找两个正序数组的中位数(1)

从上面的数组中我们可以知道中位数是第6个和第7个数的平均数,假设我们要求第6个数,我们只需要排除前面5个数,剩下的最小的一个就是第6个数。

以上面的数据为例:

k = 6 ; K/2-1 = 2 比较 A[2] =6; B[2] = 9、A[2] 小,即A[0]、A[1]、A[2] 都比第K个元素小。一共要排除(k-1) = 5个元素已经排除了三个 k = k - 3 = 3。此时A数组的起始位置是3

k=3, K/2-1 = 0;A[0+3] = 8、 B[0] = 5,B[0] 小,同理,排除B[0] k的总数减去1 k = k - 1 = 2 此时B数组的起始位置是1

k = 2, K/2-1 = 0; A[0+3] = 8 、B[0+1] = 6 B[1] 小 ,排除B[1] k = k - 1 = 1 此时排除的总数 = 3+1+1 = k-1 B数组的起始位置是2

比较A[3] B[2] A[3] 小,即第6个数是A[3] = 8

leetCode官方代码:

public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int length1 = nums1.length, length2 = nums2.length;
int totalLength = length1 + length2;
if (totalLength % 2 == 1) {
int midIndex = totalLength / 2;
double median = getKthElement(nums1, nums2, midIndex + 1);
return median;
} else {
int midIndex1 = totalLength / 2 - 1, midIndex2 = totalLength / 2;
double median = (getKthElement(nums1, nums2, midIndex1 + 1) + getKthElement(nums1, nums2, midIndex2 + 1)) / 2.0;
return median;
}
}

public int getKthElement(int[] nums1, int[] nums2, int k) {
/* 主要思路:要找到第 k (k>1) 小的元素,那么就取 pivot1 = nums1[k/2-1] 和 pivot2 = nums2[k/2-1] 进行比较

  • 这里的 “/” 表示整除
  • nums1 中小于等于 pivot1 的元素有 nums1[0 … k/2-2] 共计 k/2-1 个
  • nums2 中小于等于 pivot2 的元素有 nums2[0 … k/2-2] 共计 k/2-1 个
  • 取 pivot = min(pivot1, pivot2),两个数组中小于等于 pivot 的元素共计不会超过 (k/2-1) + (k/2-1) <= k-2 个
  • 这样 pivot 本身最大也只能是第 k-1 小的元素
  • 如果 pivot = pivot1,那么 nums1[0 … k/2-1] 都不可能是第 k 小的元素。把这些元素全部 “删除”,剩下的作为新的 nums1 数组
  • 如果 pivot = pivot2,那么 nums2[0 … k/2-1] 都不可能是第 k 小的元素。把这些元素全部 “删除”,剩下的作为新的 nums2 数组
  • 由于我们 “删除” 了一些元素(这些元素都比第 k 小的元素要小),因此需要修改 k 的值,减去删除的数的个数
    */

int length1 = nums1.length, length2 = nums2.length;
int index1 = 0, index2 = 0;
int kthElement = 0;

while (true) {
// 边界情况
if (index1 == length1) {
return nums2[index2 + k - 1];
}
if (index2 == length2) {
return nums1[index1 + k - 1];
}
if (k == 1) {
return Math.min(nums1[index1], nums2[index2]);
}

// 正常情况
int half = k / 2;
int newIndex1 = Math.min(index1 + half, length1) - 1;
int newIndex2 = Math.min(index2 + half, length2) - 1;
int pivot1 = nums1[newIndex1], pivot2 = nums2[newIndex2];
if (pivot1 <= pivot2) {
k -= (newIndex1 - index1 + 1);
index1 = newIndex1 + 1;
} else {
k -= (newIndex2 - index2 + 1);
index2 = newIndex2 + 1;
}
}
}

官方题解:划分数组

相比较与二分查找,划分数组的官方题解确实有一定的理解难度。我这里不打算按照官方题解来解释这个问题。希望能够更加容易理解这个方法。

假设我们将两个数组合并成一个有序数组,并且按照下面的规则划分,

当总是是奇数,左侧的长度比右侧多1 ,当总数是偶数 左右两侧长度相等 即奇数时,中位数是左侧的最大值,偶数时是左侧最大值与右侧最小值的平均数。

官方划分数组.png

在合并后的数组中,前(m+n)/2中的数如果有 i 个来自数组A那么剩余的 j = m+n)/2 - i个必然来自n。

举例说明:(m+n)/2 = 6

当 i = 0 时 j = 6 即前6个全部来自B数组,左侧最大值为B[5] = 30 > 右侧最小值A[0] = 1 不能构成有序数组, 不符合

当 i = 1 时 j = 5 左侧最大值是 A[0] B[4] 中的较大值;B4=[15] > 右侧最小值A[1] = 3 (B[5] = 30 > A[1]) 不能构成有序数组, 不符合

当 i = 2 时 j = 4 左侧最大值为B[3] = 12 右侧最小值是 A[2] = 6 不符合

当 i = 3 时 j = 3 左侧最大值是 B[2] = 9 右侧最小值是 A[3] = 8 不符合

当 i = 4 时 j = 2 左侧最大致是 A[3] = 8 右侧最小值是 B[2] = 9 符合 因为 两个数组总长度是偶数 中位数是 (左侧最大 + 右侧最小) / 2 .0= (8 + 9) /2.0 = 8.5

明显的此时的时间复杂度为O(m) 不符合 O(log (m+n)) 的算法。

采用二分查找法来进行处理,以数组长度较小的作为定准。为什么不能以较大的呢?假设m = 20 n = 2;因为二分查找, i 的取值范围是[0,20],k = (m+2)/2 = 11 如果i取大于11的数那么j就只能取负数,明显是不合理的。因此需要用较小长度的数组作为定准。

既然采用二分法来查找i的取值,那么什么时候 这个范围向上,什么时候范围向下呢?

我们继续补全上面案例中的数据

i = 5 时 j = 1 左侧最大数 A[4] = 12 右侧最小数为 B[1] = 6 不符合

i = 6 时 j = 0 左侧最大数 A[5] = 19 右侧最小数B[0] = 1 不符合

可以发现,当A[i] <= B[j-1]的时候 i 还可以增加。否则只能减小。

数组AB都有序,i 减小,j会增加,如果此时再去减小i 那么A[i] <= B[j-1]会永远为true,只有增加I减小j。

这样我们就能理解官方的数组划分的方法了。

官方参考代码:

public double findMedianSortedArrays(int[] nums1, int[] nums2) {
if (nums1.length > nums2.length) {
return findMedianSortedArrays(nums2, nums1);
}

int m = nums1.length;
int n = nums2.length;
int left = 0, right = m;
// median1:前一部分的最大值
// median2:后一部分的最小值
int median1 = 0, median2 = 0;

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

写在最后

本次我的分享也接近尾声了,感谢你们在百忙中花上一下午来这里聆听我的宣讲,希望在接下来的日子,我们共同成长,一起进步!!!

最后放上一个大概的Android学习方向及思路(详细的内容太多了~),提供给大家:

对于程序员来说,要学习的知识内容、技术有太多太多,这里就先放上一部分,其他的内容有机会在后面的文章向大家呈现出来,不过我自己所有的学习资料都整理成了一个文档,一直在不断学习,希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!

为什么某些人会一直比你优秀,是因为他本身就很优秀还一直在持续努力变得更优秀,而你是不是还在满足于现状内心在窃喜!希望读到这的您能点个小赞和关注下我,以后还会更新技术干货,谢谢您的支持!

Android架构师之路很漫长,一起共勉吧!

如果你觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言,一定会认真查询,修正不足,谢谢。

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

id移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算**

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值