4. Median of Two Sorted Arrays(归并排序思想)

原题

There are two sorted arrays nums1 and nums2 of size m and n respectively.

Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).

Example 1:

nums1 = [1, 3]
nums2 = [2]
The median is 2.0

Example 2:

nums1 = [1, 2]
nums2 = [3, 4]
The median is (2 + 3)/2 = 2.5

题目分析

这道题目在Leetcode上属于难度hard级别。

这里写图片描述

此题的基本思想可以借鉴归并排序。

第一步,先对两个有序列归并排序,不同的是当进行到一半时,便可终止归并,得到 i j 分别指向两个有序数组此时的位置。

第二步,如果两个数组的总个数为奇数,则返回 i j 所指向的元素的更大者即可;

若为偶数,需要利用 i j 所指向的元素大小进行讨论。

  • i 所指向的元素更大,则循环中最后的遍历落在了 nums1 中,那么,如果 i1>=0 (即 i 前存在前一个元素)且nums1[i1]>=nums2[j] ,则中位数为 (nums1[i1]+nums1[i])/2.0
  • 如果 i1==0 或者 nums1[i1]<nums2[j] ,则中位数为 (nums1[i]+nums2[j])/2.0 ;若落在了 nums2 中,则对称地进行讨论。

需要注意的是边界情况,例如 nums1=1,nums2=kong

代码实现C#

    public double FindMedianSortedArrays(int[] nums1, int[] nums2) {
            int n1 = nums1.Length, n2 = nums2.Length, i = 0, j = 0;

            // sorting nums1 and nums2, sum pointer
            int sumpointer = 0; 
            for (i = 0, j = 0; (i < n1 || j < n2) && sumpointer <= (n1 + n2) >> 1; sumpointer++){
            //indicates nums1.Length<nums2.Length          
                if (i >= n1) j++;     
                //nums1.Length>nums2.Length                        
                else if (j >= n2) i++;
                //combination nums1 and nums2 to sort
                else if (nums1[i] <= nums2[j]) i++; 
                else j++;
            }

            bool even = (n1 + n2) % 2 == 0;
            --i; --j;
            if (i < 0) return even == true ? (nums2[j - 1] + nums2[j]) / 2.0 : nums2[j];
            if (j < 0) return even == true ? (nums1[i - 1] + nums1[i]) / 2.0 : nums1[i];

            //odd analysis
            if (!even) return Math.Max(nums1[i], nums2[j]);

            //even analysis
            //shows stone point is in nums2
            if (nums1[i] < nums2[j]){ 
                if (j - 1 >= 0 && nums1[i] <= nums2[j - 1])
                    return (nums2[j - 1] + nums2[j]) / 2.0;
                return (nums1[i] + nums2[j]) / 2.0;
            }
            // shows stone point is in nums1
            if (i - 1 >= 0 && nums2[j] <= nums1[i - 1])
                return (nums1[i - 1] + nums1[i]) / 2.0;
            return (nums1[i] + nums2[j]) / 2.0;

    }

代码实现python

class Solution(object):
    def findMedianSortedArrays(self, nums1, nums2):
        """
        :type nums1: List[int]
        :type nums2: List[int]
        :rtype: float
        """
        n1 = len(nums1)
        n2 = len(nums2)
        i = 0
        j = 0
        # sorting nums1 and nums2, sum pointer
        sumpointer = 0
        while (i < n1 or j < n2) and sumpointer <= (n1 + n2) >> 1:
            if i >= n1:
                j+=1
            elif j >= n2:
                i+=1
            # combination nums1 and nums2 to sort
            elif nums1[i] <= nums2[j]:
                i+=1
            else:
                j+=1
            sumpointer+=1
        even = (n1 + n2) % 2 == 0
        i-=1
        j-=1
        if i < 0:
            return (nums2[j - 1] + nums2[j]) / 2.0 if even == True  else nums2[j]
        if j < 0:
            return (nums1[i - 1] + nums1[i]) / 2.0 if even == True else nums1[i]
        # odd analysis
        if even==False:
            return max(nums1[i], nums2[j])
        #even analysis, 1.shows stone point is in nums2
        if nums1[i] < nums2[j]:
            if j - 1 >= 0 and nums1[i] <= nums2[j - 1]:
                return (nums2[j - 1] + nums2[j]) / 2.0
            return (nums1[i] + nums2[j]) / 2.0
        # shows stone point is in nums1
        if i - 1 >= 0 and nums2[j] <= nums1[i - 1]:
                return (nums1[i - 1] + nums1[i]) / 2.0
        return (nums1[i] + nums2[j]) / 2.0

结果分析:
时间复杂度为 O(0.5(m+n)) ,空间复杂度为 O(1) ,计算结果排名:

这里写图片描述

以下是我之前写的一篇归并排序博客,地址,在此一块汇总到这里。
http://blog.csdn.net/daigualu/article/details/68491601

归并排序

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列。

排序过程

归并排序的算法我们通常用递归实现,先把待排序区间[start,end]以中点二分,接着把左边子区间排序,再把右边子区间排序,这是分解的过程。最后把左区间和右区间用一次归并操作合并成有序的区间[start,end],这是治理的过程。如何归并呢? 简单来说两个指针分别指向待归并的数组,选出较小的,放到第三个指针控制的临时数组中。

实现代码

主题框架代码如下,见代码注释。

         /// <summary>
        /// 主题框架
        /// </summary>
        private void divideMerge(int[] unsort, int start, int end, int[] sort)
        {
            if (start < end)
            {
                int middle = (start + end) >> 1;
                //对前半部分归并排序
                divideMerge(unsort, start, middle, sort); 
                //对后半部分归并排序
                divideMerge(unsort, middle + 1, end, sort); 
                //此时,前、后半部分已经是有序序列,对它们实行二路归并
                merge(unsort, start, middle, end, sort);
            }

        }

调用的二路归并方法merge的实现代码如下,要特别注意临界值,此处pe为最大索引值,而不是元素个数,注意此处。

        /// <summary>
        /// 合并算法
        /// </summary>
        /// <param name="unsort">无序数组</param>
        /// <param name="ps">第1部分的起始位置</param>
        /// <param name="pm">第1部分的结束位置</param>
        /// <param name="pe">第2部分的结束位置</param>
        /// <param name="sort"></param>
        private void merge(int[] unsort, int ps, int pm, int pe, int[] sort)
        {
            int i = ps; //第一部分的取值范围为[ps,pm]
            int j = pm+1; //第二部分的取值范围为[pm+1,pe]

            int sortCount = 0;

            while (i <= pm && j <= pe)
            {
                if (unsort[i] < unsort[j])
                    sort[sortCount++] = unsort[i++];
                else
                    sort[sortCount++] = unsort[j++];
            }

            while (i <= pm)
                sort[sortCount++] = unsort[i++];

            while (j <= pe)
                sort[sortCount++] = unsort[j++];

            for (int sortIndex = 0; sortIndex < sortCount; sortIndex++)
                unsort[ps + sortIndex] = sort[sortIndex];

        }

封装了一个排序类,见下,提供的API有2个,

  1. 归并排序接口MergeSort()
  2. 构造函数,构造无序数组
    public class CMergeSort
    {
        private int[] _unsortArray;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="unsortArray"></param>
        public CMergeSort(int[] unsortArray)
        {
            _unsortArray = unsortArray;
        }

        /// <summary>
        /// 归并排序接口
        /// </summary>
        /// <returns></returns>
        public int[] MergeSort()
        {
            int maxIndex = _unsortArray.GetUpperBound(0);
            int[] sort = new int[maxIndex + 1];
            divideMerge(_unsortArray, 0, maxIndex, sort);
            return sort;
        }
  }

客户端调用

客户端调用刚才写好的对象,对无序数组a实行归并排序。

        static void Main(string[] args)
        {
            int[] a = new int[] { 9,7,10,6,3,5,2,7,9};
            var merge = new CMergeSort(a);
            int[] sortArray = merge.MergeSort();

            Console.Read();
        }

记录了归并排序的过程,对此进行了结果分析。

结果分析

对数组的{ 9,7,10,6,3,5,2,7,9}的归并排序过程如下,


这里写图片描述

归并排序过程的前半部分,过程示意图见下,从图中可见,步骤1234一直分割区间,等到步骤5时,左右区间长度都为1,此时发生一次归并,结果再与另一个区间长度为1的归并,即步骤6;步骤7分割,步骤8归并,步骤9归并后前半部分合并结束;
后半部分过程与前半部分归并一致,不再详述。

这里写图片描述

源码下载

http://download.csdn.net/detail/daigualu/9799598

我做Leetcode的专栏

http://blog.csdn.net/column/details/14761.html

汇总在GitHub上

地址,欢迎一起讨论!若有问题请联系我!
https://github.com/jackzhenguo/leetcode-csharp

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 一个 Go 程序,用来查找两个有序数组中位数,可能如下:func FindMedian(arr1, arr2 []int) float64 { n1 := len(arr1) n2 := len(arr2) if n1 == 0 { return float64(arr2[n2/2]) } if n2 == 0 { return float64(arr1[n1/2]) } if n1 == 1 && n2 == 1 { return float64(arr1[0] + arr2[0]) / 2.0 } if n1 == 1 { if n2 % 2 == 0 { return float64(arr2[n2/2 - 1] + arr2[n2/2]) / 2.0 } else { return float64(arr2[n2/2]) } } if n2 == 1 { if n1 % 2 == 0 { return float64(arr1[n1/2 - 1] + arr1[n1/2]) / 2.0 } else { return float64(arr1[n1/2]) } } i := n1 - 1 j := n2 - 1 var median1, median2 int for i >= 0 && j >= 0 { if arr1[i] > arr2[j] { median1 = arr1[i] i-- } else { median1 = arr2[j] j-- } } if i == -1 { median2 = arr2[j-1] } else { median2 = arr1[i-1] } return float64(median1 + median2) / 2.0 } ### 回答2: 生成一个Go程序来查找两个有序数组中位数。 首先,我们需要明确中位数的定义。对于一个有序数组中位数是指位于数组中间位置的元素。如果数组长度为奇数,中位数就是唯一那个中间元素;如果数组长度为偶数,中位数是中间的两个元素的平均值。 接下来,我们可以使用以下步骤生成一个Go程序来找到两个有序数组中位数: 1. 声明一个函数,例如findMedian,该函数接受两个有序数组作为输入参数。 2. 初始化变量n,用于存储两个有序数组的总长度。 3. 通过将两个有序数组合并为一个新的有序数组,找到中位数所在的位置。可以使用合并排序或使用两个指针的方法来实现这一步骤。 4. 根据总长度n的奇偶性,分别处理中位数的情况: - 如果n为奇数,返回新数组中间位置的元素作为中位数。 - 如果n为偶数,返回新数组中间两个元素的平均值作为中位数。 5. 在主函数中调用findMedian函数,传递两个有序数组作为参数,并打印出中位数结果。 下面是一个示例的Go程序,通过使用合并排序的方法找到两个有序数组中位数: ```go package main import "fmt" func merge(nums1 []int, nums2 []int) []int { merged := make([]int, len(nums1)+len(nums2)) i, j, k := 0, 0, 0 for i < len(nums1) && j < len(nums2) { if nums1[i] < nums2[j] { merged[k] = nums1[i] i++ } else { merged[k] = nums2[j] j++ } k++ } for i < len(nums1) { merged[k] = nums1[i] i++ k++ } for j < len(nums2) { merged[k] = nums2[j] j++ k++ } return merged } func findMedian(nums1 []int, nums2 []int) float64 { merged := merge(nums1, nums2) n := len(merged) if n%2 == 0 { return float64(merged[n/2-1]+merged[n/2]) / 2.0 } else { return float64(merged[n/2]) } } func main() { nums1 := []int{1, 3} nums2 := []int{2} median := findMedian(nums1, nums2) fmt.Printf("Median is: %.2f", median) } ``` 以上示例展示了如何使用合并排序的方法找到两个有序数组中位数。你可以根据自己的需求进行扩展和修改,以适应其他情况和更复杂的需求。 ### 回答3: 下面是一个生成 Go 程序以找到两个有序数组中位数的示例: ```go package main import ( "fmt" ) func findMedianSortedArrays(nums1 []int, nums2 []int) float64 { m, n := len(nums1), len(nums2) if m > n { nums1, nums2, m, n = nums2, nums1, n, m } imin, imax, half_len := 0, m, (m+n+1)/2 var max_of_left, min_of_right int for imin <= imax { i := (imin + imax) / 2 j := half_len - i if i < m && nums2[j-1] > nums1[i] { imin = i + 1 } else if i > 0 && nums1[i-1] > nums2[j] { imax = i - 1 } else { if i == 0 { max_of_left = nums2[j-1] } else if j == 0 { max_of_left = nums1[i-1] } else { max_of_left = max(nums1[i-1], nums2[j-1]) } break } } if (m+n)%2 == 1 { return float64(max_of_left) } if i == m { min_of_right = nums2[j] } else if j == n { min_of_right = nums1[i] } else { min_of_right = min(nums1[i], nums2[j]) } return float64(max_of_left+min_of_right) / 2.0 } func max(a, b int) int { if a > b { return a } return b } func min(a, b int) int { if a < b { return a } return b } func main() { nums1 := []int{1, 3} nums2 := []int{2} median := findMedianSortedArrays(nums1, nums2) fmt.Printf("The median is: %.2f\n", median) } ``` 这个程序实现了在合并后的有序数组中找到中位数的逻辑。它首先判断数组的大小,然后使用二分查找的方法从较小的数组开始分割,保证左边的元素数量等于右边(或比右边多1)。然后通过比较左侧的最大值和右侧的最小值来确定中位数。如果数组长度是奇数,则直接返回中位数的值;如果是偶数,则计算左侧最大值和右侧最小值的平均值作为中位数的值。 在 main 函数中,我们定义了两个示例的有序数组 `nums1` 和 `nums2`,然后调用 `findMedianSortedArrays` 函数计算它们的中位数,并打印结果。 请注意,这只是一个示例程序,您可以根据自己的需求进行修改和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值