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

来源:互联网 发布:wp7软件下载 编辑:程序博客网 时间:2024/06/06 00:33

题目来源: 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_smallsubArr2 = 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]

subArr1 subArr2 num1[0]+…+num1[i-1] num1[i]+…+num1[m-1] num2[0]+…+num2[j-1] num2[j]+…+num2[n-1]

根据上面推出的subArr1和subArr2的长度关系,我们很容易得出

i+j=mi+nj=(m+n)0.5

i+j+1=mi+nj=(m+n)0.5+1

从而得出关系
j=(m+n+1)0.5i

以简化算法。


比较一下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/

阅读全文
0 0
原创粉丝点击