leetcode第一刷_Median of Two Sorted Arrays

这道题是我最初刷的那20多道之一,但一直没有过,被各种各样的情况折磨死了,直到把所有其他的题都写完,回来看大神对这道题是怎么处理的时候,才惊叹算法的奇妙。再次验证了我的想法,如果要处理各种各样的特殊情况,一定是算法本身有问题。

之前看过很多有关在两个排序数组中找中位数的解法,大多根据两个数组长度不同分了很多种情况,各种讨论。下面要介绍的方法并没有直接求中位数,而是把求中位数转换成了求两个数组合并之后的第(m+n)/2个数(当然根据总数的奇偶有所不同,不过思路上是没有问题的)。用k来表示进入递归的时候,应该求的两个数列合并之后的那个数,用二分的思想,我们先看A[k/2]与B[k/2]的大小关系,如果A[k/2-1]<B[k/2-1],那么A[0,k/2-1]中有没有可能存在合并之后的第K个数呢?A[k/2-1]大于A中在它之前的k/2-1个数,最好的情况,它也大于B中的在B[k/2-1]之前的k/2-1个数,也就是A[k/2-1]最大在合并后的数列中可以排到第(k/2-1+k/2-1+1)个位置,也即第k-1个位置。因此A[k/2-1]这一部分肯定不包含结果,应该直接扔掉。

对称的,当A[k/2-1]>B[k/2-1]时,有类似的结果。

当A[k/2-1] == B[k/2-1]时呢?说明这个数找到了,返回即可。

在实现的时候有几个技巧,两个数列个数不一样,为了下面编码部分的简便,可以在开头判断一下,如果前面数组个数多,就互换一下。第二点,数组中剩余的的元素可能比k/2要少,这时候就取一个较小的值即可。至于最开始两个数组合并后元素个数奇偶的问题,正是由于我们求的是第k个数而不是中位数,大大简化 了。设总数为t,如果是奇数,就求第t/2+1的那个数,如果是偶数,就分别求第t/2和第t/2+1的数,然后求平均。

28行代码完成了我100多行没有完成的事情,算法太美。

class Solution {
public:
    double findKth(int *A, int m, int *B, int n, int k){
        if(m>n){
            return findKth(B, n, A, m, k);
        }
        if(m == 0)
            return B[k-1];
        if(k == 1)
            return min(A[0], B[0]);
        int pa = min(m, k/2), pb = k-pa;
        if(A[pa-1]<B[pb-1]){
            return findKth(A+pa, m-pa, B, n, k-pa);
        }else if(A[pa-1]>B[pb-1]){
            return findKth(A, m, B+pb, n-pb, k-pb);
        }else{
            return A[pa-1];
        }
    }
    double findMedianSortedArrays(int A[], int m, int B[], int n) {
        int t = m+n;
        if(t&0x1){
            return findKth(A, m, B, n, t/2+1);
        }else{
            return (findKth(A, m, B, n, t/2)+findKth(A, m, B, n, t/2+1))/2;
        }
    }
};

leetcode第一刷_Median of Two Sorted Arrays

时间: 2024-08-30 11:49:38

leetcode第一刷_Median of Two Sorted Arrays的相关文章

leetcode第一刷_Remove Duplicates from Sorted Array II

水题. 我之前说过包含至多几个至少几个的问题都比较难,这个题可是让我大脸了.至多可以重复一次,那就重复次数多于两次再计算重复,否则的话像普通的数据一样直接按照重复次数前移就可以了嘛.不过说归说,这种inspace的思想还是有些用处的,数组这种实现方式致命的缺点就是删除或者添加中间的元素代价太大,因为不好把握数据的最终位置.这个题是一种情况,合并两个排序好的数组也是一个例子. class Solution { public: int removeDuplicates(int A[], int n)

leetcode第一刷_Search in Rotated Sorted Array

旋转数组的查找问题.从头开始扫一遍,O(N)的复杂度,一般也能过,甚至先排序以下,再二分都能过.不过这道题的目的当然不在于此. 想一下旋转之后对我们的查找产生了什么影响.如果没旋转过,我们直接比较target与A[middle]的大小,然后总能非常确定的丢掉源数组的一半,即把搜索空间减半,但是旋转之后,只根据A[middle]是确定不了下一轮的走向的,因为即使A[middle]比target大,按理说我们应该往前找,但是如果源数组是循环左移的,较小的数可能在后半部分. 上面说的都是旋转之后与没旋

leetcode第一刷_Search in Rotated Sorted Array II

接着上一篇,同样是旋转数组中查找问题.如果这个数组有重复元素怎么办呢?会有什么影响? 我举一个极端的例子,假设数组中的元素是这样的,1,1,2,1,1,1,1,我们要在这个数组中查找2,一开始的A[middle]=1,发现比target小,那我们就看看A[0]和A[N],发现都跟A[middle]相等,那么这个2到底在哪一半中?只有上帝知道,如果他老人家真的存在的话.这种时候我们怎么办呢?没有其他的办法,只能从头开始乖乖的扫描,直到发现target或者确定他不存在. 为什么会出现这种情况,或者说

leetcode第一刷_Convert Sorted List to Binary Search Tree

好,二叉搜索树粉末登场,有关他的问题有这么几个,给你一个n,怎样求所有的n个节点的二叉搜索树个数?能不能把所有的这些二叉搜索树打印出来? 这道题倒不用考虑这么多,直接转就行了,我用的思想是分治,每次找到一半的位置,分离出中间节点,作为新子树的根节点,然后递归构造前半部分和后半部分. class Solution { public: TreeNode *sortedListToBST(ListNode *head) { if(head == NULL) return NULL; int len =

leetcode第一刷_Binary Tree Inorder Traversal

递归实现当然太简单,也用不着为了ac走这样的捷径吧..非递归实现还挺有意思的. 树的非递归遍历一定要借助栈,相当于把原来编译器做的事情显式的写出来.对于中序遍历,先要訪问最左下的节点,一定是进入循环后,不断的往左下走,走到不能走为止,这时候,能够从栈中弹出訪问的节点,相当于"左根右"过程的"根",然后应该怎么做呢?想一下中序遍历完根节点之后应该干嘛,对,是走到右子树中继续反复这个过程,可是有一点,假设这个节点不包括右子树怎么办?这样的情况下,下一个应该訪问的节点应该

leetcode第一刷_Sqrt(x)

这道题乍看下来非常简单,实际上要注意的问题非常多. 注意看给出来的函数的接口,返回的是int值,也就是计算结果是个近似值.怎样求呢?难道是从2开始往上算?直到某个值正好接近x?当然不行,肯定超时了.再仔细想一下,对了,有二分法,从最大的开始,每次计算一下平方,如果结果比x大,那么缩短上界,否则提高下界. 思想很正确,下面的问题是最大的那个值是多少?你会毫不犹豫的说出是x啊,x的平方根肯定比x小吧.好,那如果x是INT_MAX呢,你想用什么类型来存储这个平方的结果?而且这样每次减半,也得好一会儿才

leetcode第一刷_Subsets II

要求子集,有非常现成的方法.N个数,子集的个数是2^N,每个元素都有在集合中和不在集合中两种状态,这些状态用[0,pow(2,N)]中每个数来穷举,如果这个数中的第i位为1,说明当前集合中包含源数组中的第i个数. 至于有没有重复的元素,大部分有重复元素的问题,都可以借助一个vis集合,里面存放所有已经求得的集合或者其他形式的解,只有少数题目会超时,哪些问题具体的说. class Solution { public: vector<vector<int> > subsetsWithD

leetcode第一刷_Decode Ways

这道题还挺难的.递归的思路是好想,不过不出意料的超时了. dp嘛.想一下i-1的编码加上第i个编码会怎样,如果加上的这个编码不是0,那么这一位可以独立解码,那长为i的解码个数至少是长为i-1的解码个数.还有呢?如果i-1位是1,可以把i-1位和i位同时解码出来,还有呢?如果i-1位是2而i位是0-6中的数字,也可以同时解码这两位编码.满足这个条件的时候,当前长度的解码个数还要加上i-2时的解码个数. 完全可以用一个数组存放过去的结果,但是很明显,当前的结论只与前一个和前前一个结果有关系.只要用三

leetcode第一刷_Gray Code

说到格雷码,应该没人不知道,具体它有什么用,我还真不是很清楚,我室友应该是专家.生成的规律不是很明显,之前看到帖子讲的,这会儿找找不到了.. 思想是这样的,如果有n位,在第2^(n-1)个编码下面画一条水平线的话,你会发现除了第一位之外,其他位都是关于这条线对称的,如下,以三位格雷码举例: 000 001 011 010 --------------------- 110 111 101 100 很神奇吧,我以前是不知道这个规律的.从一开始的一位格雷码,0,1,开始,每次对称的在上一个前面添加上