算法-数列查找
算法-数列查找
1. 从两个正序数组中找出中位数
说明:给定两个正序(从小到大)整数数组:nums1 = int[n]
、nums2 = int[m]
,找出这两个数组全部元素的中位数,假定两个数组均非空(n, m >= 0
),且不同时为空数组(n + m >= 1
)。
例 1:
nums1 = [1,3]
、nums2 = [2]
;则合并后数组为[1, 2, 3]
,中位数为 2。例 2:
nums1 = [1,2]
、nums2 = [3,4]
;则合并后数组为[1, 2, 3, 4]
,中位数为(2 + 3) / 2 = 2.5
。
1.1 数组归并法
首先「归并」两个有序数组,假设数组长度分别为 length1
和 length2
,归并后中位数下标为 midIndex
。
- 由于归并后元素个数
sum
可能为奇数或偶数,所以midIndex
需要取更大的,确保包括中位数,- 奇数个的中位数位于
(sum / 2) + 1
,下标为sum / 2
; - 偶数个的中位数位于
sum / 2
和(sum / 2) + 1
,下标为(sum / 2) - 1
和sum / 2
; - 取最大的,也就是
midIndex = sum / 2
。
- 奇数个的中位数位于
- 所以偶数个
isEven == true
时,可以记录value = merge[midIndex - 1]
,再累加value += merge[midIndex]
,然后遍历结束后,如果isEven == true
,则value /= 2
。 - 但实际上,由于题目只需要查找中位数,所以只需要保证中位数及之前的元素有序即可,因此只需要排序
midIndex + 1
个元素,可以通过循环for (i = 0; i <= midIndex; i++)
来控制。由于题目给出两个数组都是正序排列(从小到大),可以每次都从两个数组中取一位最小的,直到一共取了midIndex + 1
个元素为止,因此需要两个变量index1
和index2
分别记录两个数组下一次从哪个元素开始取。 - 由于
midIndex
是两个数组归并后的中位数下标,所以从逻辑上可以确定一个特征:- 如果循环到第
i
次时,一个数组已被取完,即index1 >= length1
或index2 >= length2
下标越界,但归并后数组元素个数仍然小于midIndex + 1
个,则另一个数组的下标i
一定还未越界。换句话说,从i = 0
循环到i = midIndex
的过程中,两个数组对下标i
的取值不可能同时越界。
- 如果循环到第
- 归并时从哪个数组取了元素,就将对应数组的
index
更新到该元素的下标,表示数组剩余元素最小值的下标。
例如 [1, 2]
和 [0, 3, 4, 5, 6]
,用 > 表示两个数组归并过程中的的 index
,一共有 7 个元素,则归并后的中位数下标 midIndex == 3
;也即一共只需要归并 4 个元素即可:
归并下标 i |
merge | nums1 | nums2 |
---|---|---|---|
初始化 | [] |
[>1, 2] index1 = 0 |
[>0, 3, 4, 5, 6] index2 = 0 |
0 | [0] |
[>1, 2] index1 = 0 |
[0, >3, 4, 5, 6] index2 = 1 |
1 | [0, 1] |
[1, >2] index1 = 1 |
[0, >3, 4, 5, 6] index2 = 1 |
2 | [0, 1, 2] |
[1, 2 >] index1 = 2 越界 |
[0, >3, 4, 5, 6] index2 = 1 |
3 | [0, 1, 2, 3] |
[1, 2 >] index1 = 2 越界 |
[0, 3, >4, 5, 6] index2 = 2 |
归并完成,中位数即为 merge[midIndex] == merge[3] == 3
,如果元素总数为偶数,思路也是一样的,只是最终计算中位数为 (merge[midIndex - 1] + merge[midIndex]) / 2
。
1 | class Solution_Merge { |
2. 从数组中找出和为给定值的两个元素
说明:给定一个整型数组 nums
,找出「和为给定整数 target
」的「两个」元素下标(忽略顺序)。数组中的每个元素不能被重复选择,假定每个输入有且仅有一个解。
例 1:
nums = [2, 7, 11, 15]
、target = 9
,则输出[0, 1]
或[1, 0]
。例 2:
nums = [3, 3]
、target = 6
,则输出[0, 1]
或[1, 0]
。
2.1 双重循环暴力法
用双重循环比较任意两个元素的和是否与给定的 target
相等,如果相等则直接返回两个元素对应的下标组成的新数组,否则返回一个默认值 [0, 0]
,由于题目假定每个输入都会有且仅有一个解,因此可以假定一定会返回有效解。
注意:由于双重循环,判断两个元素之和在内层循环中,找到有效解时应当直接跳出外层循环,所以应该使用「定义循环名」的方式,避免多用一个 Flag
变量。
1 | class Solution_DoubleLoop { |
2.2 Map哈希法
因为主要目标是找到和等于 target
的两个元素,所以在确定其中一个元素 nums[i]
时,另一个元素「应该的取值」就已经确定为 nums[j] = target - nums[i]
,假如存在则结果就为 [i, j]
。所以问题可以转换为:每遍历到一个元素 nums[i]
时,都查找是否存在 nums[j] = target - nums[i]
:
- 遍历时将每个遍历过的元素及其对应下标都存入一个 Map;
- 每遍历一个新的元素
nums[i]
时,计算nums[i]
「应该」匹配的数为diff = target - nums[i]
; - 判断是否存在
Map.contains(diff)
;- 如果存在,则获取这个数的下标
j = Map.get(diff)
,然后返回[i, j]
。 - 否则继续遍历,直到最终仍找不到匹配的两个元素,返回默认值(依照题意则不会遇到这种情况)。
- 如果存在,则获取这个数的下标
1 | class Solution_MapHash { |