有两个已经升序排列的数组a[]和b[], 它们的长度分别为m和n,求这两个数组的中位数。注意,如果m+n为奇数,则中位数为正中间那个数; 如果m+n为偶数,则中位数为正中间两个数的平均值。这是一道很经典的面试题,本人在去VMware面试的时候被问到过,当时我只给出了O(m+n)的算法。 仔细想来,有如下三种方法求解。
- 方法一: 先合并后求解,时间复杂度为O(m+n), 空间复杂度也为O(m+n)
- 方法二: 不合并但使用计数器辅助求解,时间复杂度为O(m+n), 空间复杂度为O(1)
- 方法三: 采用分治法求解,时间复杂度为O(log(m+n))
方法一: 申请一个数组c[], 长度为m+n。将a[]和b[]归并排序到c[]中;然后求解其中位数。
1 int get_median(int a[], size_t na, int b[], size_t nb, int *median) 2 { 3 int nc = na + nb; 4 5 /* 1. allocate aux[na+nb] */ 6 int *c = (int *)malloc(nc * sizeof(int)); 7 if (c == NULL) /* error */ 8 return -1; 9 10 /* 2. merge a[] and b[] into c[] */ 11 int i = 0; /* walk list A : read */ 12 int j = 0; /* walk list B : read */ 13 int k = 0; /* walk list C : write */ 14 15 while (i < na && j < nb) { 16 if (a[i] < b[j]) 17 c[k++] = a[i++]; 18 else 19 c[k++] = b[j++]; 20 } 21 22 while (i < na) 23 c[k++] = a[i++]; 24 25 while (j < nb) 26 c[k++] = b[j++]; 27 28 /* 3. get the median */ 29 *median = (nc % 2 == 0) ? (c[nc/2 - 1] + c[nc/2]) / 2 : c[nc/2]; 30 31 /* 4. free the aux[] */ 32 free(c); 33 34 return 0; 35 }
方法二: 对方法一的空间复杂度进行改进,改进后的空间复杂度为O(1),但时间复杂度仍为O(m+n)。
1 int get_median(int a[], size_t na, int b[], size_t nb) 2 { 3 int median = 0; 4 int nc = na + nb; 5 6 int prev = 0; 7 int this = 0; 8 9 int i = 0; /* walk list A : read */ 10 int j = 0; /* walk list B : read */ 11 int k = 0; /* walk list A u B */ 12 while (i < na && j < nb) { 13 if (a[i] < b[j]) 14 this = a[i++]; 15 else 16 this = b[j++]; 17 18 if (k++ == nc / 2) 19 goto done; 20 21 prev = this; 22 } 23 24 while (i < na) { 25 this = a[i++]; 26 27 if (k++ == nc / 2) 28 goto done; 29 30 prev = this; 31 } 32 33 while (j < nb) { 34 this = b[j++]; 35 36 if (k++ == nc / 2) 37 goto done; 38 39 prev = this; 40 } 41 42 done: 43 median = (nc % 2 != 0) ? this : (this + prev) / 2; 44 return median; 45 }
方法三: 采用分治策略降低时间复杂度。
。。。未完待续。。。
参考资料: