算法——寻找两个有序数组的中值
2014-04-12 12:48
330 查看
1. 算法描述
有两个数组 A 和 B,均为有序排列,A的长度为m,B的长度为n,求 A 和 B 合在一起后的中值.2. 问题分析
这里要注意一下:要充分利用 A和B均为有序的特性该问题进一步可转化为求A和B的任意K值,如三分位、四分位.
思路一:将A和B合并成新的数组
/** * 合并有序数组,然后寻找K值 * * @param a * 有序数组a * @param b * 有序数组b * @param k * k值位置,0<=k<=a.length+b.length-1 * @return k值 */ public static int findKthByMerge(int[] a, int[] b, int k) { System.out.println("Find kth by merge array first"); int[] ab = new int[a.length + b.length]; int ai = 0, bi = 0, abi = 0; while (ai < a.length && bi < b.length) { ab[abi++] = (a[ai] < b[bi]) ? a[ai++] : b[bi++]; } while (ai < a.length) { ab[abi++] = a[ai++]; } while (bi < b.length) { ab[abi++] = b[bi++]; } System.out.println(Arrays.toString(ab)); return ab[k]; }
这种方法最容易想到,合并成有序数组后即可求任意k值,其时间复杂度为 O(m+n), 空间复杂图为O(m+n)
这里反思一下:真的需要合并数组吗?
思路二:采用扫描计数方法
/** * 无需合并数组,利用计数机寻找K值 * * @param a * 有序数组a * @param b * 有序数组b * @param k * k值位置,0<=k<=a.length+b.length-1,k同时充当计数器 * @return k值 */ public static int findKthByCounter(int[] a, int[] b, int k) { System.out.println("Find kth by counter"); int ai = 0, bi = 0; int kth = 0; // 保存K值 while (ai < a.length && bi < b.length && k >= 0) { kth = (a[ai] < b[bi]) ? a[ai++] : b[bi++]; k--; } while (ai < a.length && k >= 0) { kth = a[ai++]; k--; } while (bi < b.length && k >= 0) { kth = b[bi++]; k--; } return kth; }
本算法是对算法一的改进,用一个临时变量保存K值,而不需要讲新合并的数组单独存储,节省了存储空间。
其 时间复杂度为O(m+n), 空间复杂度为O(1).
到此都是线性时间复杂度,已经是非常高效了,但又没有更加高效的方法进一步降低时间复杂度呢?
这里注意到原数组有序特性,利用二分特性可以将复杂度降至对数级别。
思路三:递归二分
/** * 递归二分查找K值 * * @param a * 有序数组a * @param b * 有序数组b * @param k * K值位置,0<=k<=m+n-1 * @param aStart * 数组a初始查找位置 * @param aEnd * 数组a结束查找位置 * @param bStart * 数组b初始查找位置 * @param bEnd * 数组b结束查找位置 * @return k值 */ public static int findKth(int a[], int b[], int k, int aStart, int aEnd, int bStart, int bEnd) { int aLen = aEnd - aStart + 1; int bLen = bEnd - bStart + 1; // 递归结束条件 if (aLen == 0) { return b[bStart + k]; } if (bLen == 0) { return a[aStart + k]; } if (k == 0) { return a[aStart] < b[bStart] ? a[aStart] : b[bStart]; } // 将k按比例分配到a和b中,(k+1)=ka+kb, int ka = (k + 1) * aLen / (aLen + bLen); int kb = (k + 1) - ka; ka += aStart; kb += bStart; // 因为a和b有序,aStart-ka , bStart-kb yi // 最大值进行比较 if (a[ka] > b[kb]) { k = k - (kb - bStart); // bStart - kb 这段应当排除,调整k值 aEnd = ka; // 新k值可能存在于 aStart - ka bStart = kb; // 新k值可能存在于 kb - bEnd 之间 } else { k = k - (ka - aStart); bEnd = kb; aStart = ka; } return findKth(a, b, k, aStart, aEnd, bStart, bEnd); }本方法计算中值每次将范围缩小一半,故而 其 时间复杂度为 lg(m+n).
3. 测试算法
public static void main(String[] args) { int A[] = { 0, 10, 30, 40, 50, 80, 89, 99, 101 }; // int A[]={}; int B[] = { -1, 33, 36, 56, 80, 83, 97, 98, 200 }; // int B[] = {}; int k = 0; int kth = 0; k = (A.length + B.length - 1) / 2; System.out.println("A.length=" + A.length + "\t" + Arrays.toString(A)); System.out.println("B.length=" + B.length + "\t" + Arrays.toString(B)); System.out.println("k-index = " + k); kth = findKthByMerge(A, B, k); System.out.println(kth); kth = findKthByCounter(A, B, k); System.out.println(kth); System.out.println("递归查找"); kth = findKth(A, B, k, 0, A.length - 1, 0, B.length - 1); System.out.println(kth); }
输出结果如下:
A.length=9 [0, 10, 30, 40, 50, 80, 89, 99, 101] B.length=9 [-1, 33, 36, 56, 80, 83, 97, 98, 200] k-index = 8 Find kth by merge array first [-1, 0, 10, 30, 33, 36, 40, 50, 56, 80, 80, 83, 89, 97, 98, 99, 101, 200] 56 Find kth by counter 56 递归查找 56
相关文章推荐
- 求两个有序数组的中值
- 数据结构与算法[LeetCode]—两个有序数组合并及找中点问题
- [LeetCode题解]从两个有序数组的并集中寻找第k小元素
- 两个有序数组的中位数 【算法】
- 算法20:两个排序数组merge后的中值
- 两个有序数组(有序段sorted run)简单归并算法的比较次数的分析
- 两个有序数组中,寻找第K大的数
- 算法练习4.Median of Two Sorted Arrays两个有序数组的中位数(递归、分治)
- 两个有序数组相关的算法
- 【简单算法】31.合并两个有序数组
- 算法面试题——两个有序数组,将一个数组放入另一个空间很大的数组,要求合并之后依然有序,时间复杂度要求最小,不使用额外的数组。
- 算法 - 合并两个有序数组为一个有序数组
- 【算法剖析】寻找两个已序数组中的第k大元素
- 寻找两个等size有序数组的中位数
- 认真对待每一道算法题 之 两个排序好的数组寻找的第k个大的数
- 【合并两个有序的子数组】算法实现
- 在两个有序数组中找第N数(算法)
- ~~~~(>_<)~~~~Median of Two Sorted Arrays:两个有序数组寻找中位数
- Median of Two Sorted Arrays - 寻找两个有序数组的中位数(重)
- 字符串算法——两个有序数组的中位数