一、题目分析。
题目:给定两个升序子数组A、B,查找升序和数组的中位数。
问题类型:查找问题。
关键条件:升序,中位数。
二、解法模型选择 初步分析。
查找目标:中位数。
数学描述:m个升序元素中,m为偶数时,第 m/2(整数除法)个与第 m/2+1 个元素的算术平均数;m为奇数时,第 m/2+1 个元素。
计算机描述:主要是一个查找问题。查找第 m/2 个与第 m/2+1 个元素,然后根据m的奇偶情况去返回相应答案。
特殊条件:两个数组是升序的,待查找目标也与顺序有关。
考虑使用方法:二分法。
二分法的原理:每次缩小一半的查找范围。(即不是根据所在位置去评价元素,而是根据目标元素去评价某个范围。)
二分法的原则:每次缩小范围,不能舍弃目标元素;每次都要改变搜索范围,且不能陷入“局部循环”,避免程序循环无法终止。
三、程序设计中的循环主干。
核心问题:如何查找两个升序子数组,所组成的和数组的第k个元素。
常规的查找方法:这是一个顺序结构。设置两个指向子数组的指针,更加由小到大的原则,依次比较,找到第k个元素。需要遍历k次,查找范围长度为k。(一个个元素遍历,遍历长度为k的范围)
二分法的查找方法:我们希望不要一个个元素遍历,一个个元素去判断、舍弃,而是一次性舍弃掉一半的元素。
我们上哪去找这些被舍弃的元素?在给定长度为k的范围,这个元素都不大于第k个元素。我们可以找子数组A、B的前k/2个元素来尝试。(尝试是程序设计非常重要的方法,有时候即便我们不能一下子找到最优秀的解法,也能够在所尝试的解法中进行一步步的修改,接近最优解法。)
此解法合理性分析:为了更加保守,我们选择A[k/2-1]与B[k/2-1]中的最小值来尝试,(这两个元素是作为分割子数组的标志),这里假设A[k/2-1]<=B[k/2-1]。
我们来分析A[k/2-1]在和数组中的最大位置。这是个升序数组、顺序结构,一个元素的位置,通过与其它元素比较来确定。元素的最大下标,是小于或等于该元素的个数(因为有可能存在相同的元素)。
对与A[k/2-1],在A中有 k/2-1个元素小于或等于它,在B中最多有k/2-1个元素小于或等于它,所以在A+B中最多有k-2个元素小于或等于它,它在A+B中的最大下标是k-2,比第k个元素(下标为k-1)小。
我们舍弃A中的A[0]到A[k/2-1]元素不会舍弃掉目标值,符合二分法的要求。
四、程序设计中的循环端口。
如何退出循环:我们可以大致预料到,随着舍弃元素过程的推进,最终会出现数组A或B中的元素被完全舍弃的情况。这时候就简化为了在一个数组中查找第k个元素的问题。
如何处理bug:我们每次做的事情是 :
(1)查找A和B中的k/2-1号元素。
(2)比较这两个元素。
(3)删去较小元素,及在其所在子数组中,比它小的元素。
可能出现的bug:
(1)越界:k==1 , k/2-1 == -1 这时只要返回min{A[0],B[0]}
(2)越界:k/2-1>m-1||k/2-1>n-1(m与n分别为每次操作中AB的大小)
这时只要将处理k/2-1号元素,改成处理m-1号元素即可。
(假设没有越界,我们这样做相当于减少删去的元素,不用担 心误
删目标元素)
五、程序的异常处理。
(1)数组不合法:m==0&&n==0;
(2)k不合法:k<1|| k>m+n;
六、程序的基本框架。
1、判断m+n的奇偶。
2、找第k个元素(分m+n奇偶情况讨论)
(1)异常处理。
(2)正常的循环:
[1]判断是否到达循环端口:到达则输出。
[2]未到达则更新参数,推进循环(更新子数组与k:这里本人使用两个指针与
vector<int>来表示一个数组的)
七、总结。
核心问题:如何设计一个循环程序。(计算机的优势是算力与内存,循环是发挥算力优势 的
一般方法)
问题分析:(1)将问题先用通俗语言或者数学语言描述出来。
(2)将问题用计算机学科术语描述出来,查找、增删、数据处理等。
建模: (1)规划解决问题的基本思路,先将情况理想化,不考虑细节。(循环中间)
(2)处理循环的端口问题。(循环开始与循环结束)
(3)处理特殊情况。(数组访问越界)
(4)异常处理。
(5)调试与优化。