由题目要求的时间复杂度log(m+n)知不能遍历求解,必须采用二分法以压缩时间。每一次循环要尽量使范围缩小一半。而因为列表已经排好序,所以确定列表中一个元素的值,就能确定所有元素的范围。以此为基础,我构建了如下算法:
设两个列表的长度分别为L1、L2,其中L1>L2。求出分别的中位数M1、M2,若M1>M2,则L1中所有M1以后的元素都大于M2,而L2中所有M2之前的元素都小于M1。可得,所求中位数的值应在M1到M2之间。所以,设N<L2/2,则同时删去L1的最后N个数和L2的最前N个数,对所求的中位数无影响。该操作每一次都会使较短列表长度减半,而当其长度减小到一定时(如2以内),就可以相对容易地求出总中位数的值。循环此操作,即可满足二分的要求。
因为当数列长度为偶数时,中位数是其中间两个数的平均值。所以上述操作的临界条件是短列表长度小于等于2。没有必要真正删去列表中的数,只需用变量记录其首尾序号,并改动它即可。最后,当达到临界条件后,将长列表中间若干个元素拿出,和短列表中元素组合成一个新列表。此列表的中位数即为所求。
本题的方法其实并不复杂,但实现起来比较恶心,花费了我两个小时。主要原因一是平时都用C++做算法题,对Python的种种格式和操作还不太熟悉,查阅资料花费了一定时间。二是题中的越界检查和特殊情况(如列表为空)需要仔细考虑,额外花费不少时间。
我实现的步骤较为简单,但用时相对较长,高出平均,这主要是因为不想在最后分类讨论,就用了费时的sort函数和列表切片方法,以求方便。代码如下:
设两个列表的长度分别为L1、L2,其中L1>L2。求出分别的中位数M1、M2,若M1>M2,则L1中所有M1以后的元素都大于M2,而L2中所有M2之前的元素都小于M1。可得,所求中位数的值应在M1到M2之间。所以,设N<L2/2,则同时删去L1的最后N个数和L2的最前N个数,对所求的中位数无影响。该操作每一次都会使较短列表长度减半,而当其长度减小到一定时(如2以内),就可以相对容易地求出总中位数的值。循环此操作,即可满足二分的要求。
因为当数列长度为偶数时,中位数是其中间两个数的平均值。所以上述操作的临界条件是短列表长度小于等于2。没有必要真正删去列表中的数,只需用变量记录其首尾序号,并改动它即可。最后,当达到临界条件后,将长列表中间若干个元素拿出,和短列表中元素组合成一个新列表。此列表的中位数即为所求。
本题的方法其实并不复杂,但实现起来比较恶心,花费了我两个小时。主要原因一是平时都用C++做算法题,对Python的种种格式和操作还不太熟悉,查阅资料花费了一定时间。二是题中的越界检查和特殊情况(如列表为空)需要仔细考虑,额外花费不少时间。
我实现的步骤较为简单,但用时相对较长,高出平均,这主要是因为不想在最后分类讨论,就用了费时的sort函数和列表切片方法,以求方便。代码如下: