您的位置:首页 > 其它

【算法分析与设计】【第二周】4.Median of Two Sorted Arrays

2017-09-17 17:48 507 查看
题目来源: https://leetcode.com/problems/median-of-two-sorted-arrays/description/

题目大意:找出两个有序数组(升序)nums1和nums2的中位数。其中nums1的大小为m,nums2的大小为n。

最近两周都在学习分治的思想,所以选了这么一个题作为应用试试手。

思路大致有这么几种:

1.将两个数组直接合并成一个数组,使用快速排序后,直接求解中位数。这种做法很直接暴力,但没什么意思,主要是考察对排序算法的熟悉程度,快排也是利用分治算法的一种排序算法,写一遍算作对快排算法的一次复习。

时间复杂度O(nlogn)。

//快速排序
void quicksort(int* arr, int left, int right) {
if (left < right) {
int i = left, j = right, x = arr[left];
while (i < j) {
while(i < j && arr[j] >= x) // 从右向左找第一个小于x的数
j--;
if(i < j)
arr[i++] = arr[j];
while(i < j && arr[i] < x) // 从左向右找第一个大于等于x的数
i++;
if(i < j)
arr[j--] = arr[i];
}
arr[i] = x;
quicksort(arr, left, i - 1); // 递归调用
quicksort(arr, i + 1, right);
}
}

double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size) {
int arr[nums1Size+nums2Size];
for (int i = 0; i < nums1Size; i++) {
arr[i] = *(nums1+i);
}
int j = 0;
for (int i = nums1Size; i < nums1Size+nums2Size; i++) {
arr[i] = *(nums2+(j++));
}
quicksort(arr, 0, nums1Size+nums2Size-1);
int mid = (nums1Size+nums2Size)*0.5;
if ((nums1Size+nums2Size)%2 == 1) return arr[mid];
else return (arr[mid]+arr[mid-1])*0.5;
}


2.巧妙利用两个数组有序**这一条件,先依次比较两个数组,小的传入新的数组。其中短的数组用完了,即全部放入到新数组中去了,那么长数组中剩下的那一段就可以直接拿来放入到新数组中去了。

时间复杂度O(min{n, m})。

double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size) {
//两个有序数组的合并函数
int arr[nums1Size+nums2Size];
int i = 0, j = 0, k = 0;
while (i < nums1Size && j < nums2Size) {
if (*(nums1+i) <= *(nums2+j)) {
arr[k] = *(nums1+i);
i++;
} else {
arr[k] = *(nums2+j);
j++;
}
k++;
}
// 将较长一串的后半截直接放入
while(i < nums1Size)
arr[k++] = nums1[i++];
while(j < nums2Size)
arr[k++] = nums2[j++];
int mid = (nums1Size+nums2Size)*0.5;
if ((nums1Size+nums2Size)%2 == 1) return arr[mid];
else return (arr[mid]+arr[mid-1])*0.5;
}


3.利用刚刚学过的分治算法(《算法概论》P54),也是先将两个数组直接合并成一个数组,随机挑选v。

时间复杂度O(m+n)。

4.巧妙利用中位数的定义。根据中位数的特点,中位数最终将原数组分为两个子数组subArr1和subArr2,且**subArr1的所有元素均小于subArr2

具体来说,对于一个大小为n的数组arr而言,如果n是偶数,则中位数将该数组分为等长且长度为n/2的两个子数组。如果n是奇数,在这里,我们用中位数将该数组分为两个子数组,小的子数组比大的子数组长度小1。

在本题中,我们要找的中位数,将nums1和nums2数组合并后分为两个子数组,那么两个子数组subArr1和subArr2的长度关系满足

len(subArr1)=len(subArr2)=(m+n)∗0.5

或(len(subArr1))+1=len(subArr2)=(m+n)∗0.5+1。

对于子数组subArr1,我们可以理解成由nums1的某一部分和nums2的某一部分合并组成。因此,我们把nums1分成两部分,分别为num1_small和num1_large。nums2同理,分成num2_small和num2_large。由于subArr1中的所有元素均小于subArr2,所以subArr1应该由num1_small和num2_small组成。(当然,也可能出现num1_small或num2_small为空的现象。)

子数组subArr2同理,由num1_large和num2_large组成。



subArr1 = num1_small + num2_small
subArr2 = num1_large + num2_large


用i、j分别把nums1和nums2按上述方式分开。

num1_small = num1[0]+...+num1[i-1]
num1_large = num1[i]+...+num1[m-1]

num2_small = num2[0]+...+num2[j-1]
num2_large = num2[j]+...+num2[n-1]




subArr1subArr2
num1[0]+…+num1[i-1]num1[i]+…+num1[m-1]
num2[0]+…+num2[j-1]num2[j]+…+num2[n-1]
根据上面推出的subArr1和subArr2的长度关系,我们很容易得出

i+j=m−i+n−j=(m+n)∗0.5

或i+j+1=m−i+n−j=(m+n)∗0.5+1。

从而得出关系

j=(m+n+1)∗0.5−i

以简化算法。

比较一下4种方法的时间复杂度:

方法时间复杂度难度
快排O(nlogn)应掌握
利用两个数组有序O(min{n, m})较容易
Selection求第k大的数O(m+n)目前还没写出来
巧妙利用中位数的定义O(logmin{n, m})
以上是我对题目4的一些解析。写完这篇博客后,和室友交流,室友说利用分治思想中的归并排序做此题,亦可达到O(log(n, m)) 。分治思想博大精深,我还是需要继续进行探索。

参考:http://www.cnblogs.com/A_ming/archive/2010/04/15/1712313.html

https://leetcode.com/problems/median-of-two-sorted-arrays/discuss/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: