[原]关于对求两个排序数组交集的扫描算法正确性的证明

在看《信息检索导论》的时候看到了这个算法的实现,书里是用来演示如何将两个term的倒排列表求交集。伪代码如下:

INTERSECT( p1, p2 )

1 answer ← {}

2 while p1 != NIL and p2 != NIL do

3   if docID( p1) = docID( p2 ) then

4     ADD( answer, docI D( p1 ) )

5     p1 ← next( p1 )

6     p2 ← next( p2 )

7   else if docID( p1 ) < docID( p2 ) then

8     p1 ← next( p1 )

9   else p2 ← next( p2 )

10 return answer

乍一看这段代码和归并排序的归并过程比较像,但是这个是只取两个数组中共同的元素。这里数组假设按从小到大排序,且没有重复。实际上这里数组也可以是链表,本文以数组为例,链表也是一样的。

其正确性大致看感觉应该没问题,但是想严格地说明其正确性似乎就感觉有些挠头发,没有一个特别直观的能一句话说明其正确性的方法。想了一会儿,还是得做一些定义。

首先给出第一个定义,也是一个循环不变式:

在循环的每次迭代开始的时候,数组A的当前元素A[i]和数组B的当前元素B[j]满足一下条件:

A[i] > B[1] ... B[j-1]

B[j] > A[1] ... A[i-1]

为了让算法在初始的时候满足这个条件,可以为每个数组的前面加上正无穷这个元素。这个循环不变式的证明可以使用数学归纳法,具体过程省略。

接下来为本算法所要完成的目标做一个进一步的说明:

本算法所要做的,对于每一个数组中的某个元素k来说,实际上是要找到对应的另一个有序数组中大于等于k的最小的元素。由于数组都有序,因此实际上就是另一个数组中,从第一个元素遍历,大于等于k的第一个元素。

当找到了这个另一个数组中大于等于k的第一个元素,实际上也就知道了另一个数组是否有等于k的元素。如找到的这个元素大于k,则可以确知另外的数组中没有相等的元素(可以利用数组有序的性质很好证明),因此可以跳过k这个元素。而如果相等,则是找到,可以从两个数组中分别跳到下一个元素,继续查找。

基于上面的思想,就不难理解算法的主干了。只是这里元素k不定,由于两个数组之间完全是对称的,其关系是相互的,因此每次跳过的k是两个数组的当前元素中较小的那个。因为较小的那个可以认为是找到了另一个数组中大于等于自己的最小的元素(也就是另一个数组中的当前元素,可以自行证明)。而较大的则不能这么说,因为另一个数组中后面还有可能有元素比它小。

嗯,这样算法的正确性基本上能说明白了。

(以上的叙述为了方便说得比较简单,还请理解)

时间: 2024-07-30 09:16:12

[原]关于对求两个排序数组交集的扫描算法正确性的证明的相关文章

程序员面试题目总结--数组(三)【旋转数组的最小数字、旋转数组中查找指定数、两个排序数组所有元素中间值、数组中重复次数最多的数、数组中出现次数超过一半的数】

转!http://blog.csdn.net/dabusideqiang/article/details/38271661 11.求旋转数组的最小数字 题目:输入一个排好序的数组的一个旋转,输出旋转数组的最小元素. 分析:数组的旋转:把一个数组最开始的若干个元素搬到数组的末尾.例如数组{3, 4, 5, 1, 2}为{1, 2, 3, 4, 5}的一个旋转,该数组的最小值为1.这道题最直观的解法并不难.从头到尾遍历数组一次,就能找出最小的元素,时间复杂度显然是O(N).但这个思路没有利用输入数组

[LeetCode]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)). 解法一:不考虑时间复杂度限制在O(log(m+n)),则将两个数组遍历一遍即可以组合成一个排好序的数组,然后取数组的中位数即可,时间复杂度O(m+n): c

LeetCode-4. 两个排序数组的中位数(详解)

链接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays/description/ 有两个大小为 m 和 n 的排序数组 nums1 和 nums2 . 请找出两个排序数组的中位数并且总的运行时间复杂度为 O(log (m+n)) . 示例 1: nums1 = [1, 3] nums2 = [2] 中位数是 2.0 示例 2: nums1 = [1, 2] nums2 = [3, 4] 中位数是 (2 + 3)/2 = 2.

Java求两个List的交集

1 package demo; 2 3 import java.util.List; 4 5 public class Demo { 6 7 @SuppressWarnings("unchecked") 8 public static void main(String[] args) { 9 List array1=new ArrayList(); 10 array1.add("1");array1.add("2"); 11 List array

求两个集合的交集和并集C#

我是用hashset<T>来实现的 具体如代码所示 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace JiaoJi { class Program { static void Main(string[] args) { int [] arrA=new int[8]{1,2,3,4,5,6,7,8}; int [] arrB=new int[5]{4,5,

哈希(4) - 求两个链表的交集(intersection)以及并集(union)

给定两个链表,求它们的交集以及并集.用于输出的list中的元素顺序可不予考虑. 例子: 输入下面两个链表: list1: 10->15->4->20 list2: 8->4->2->10 输出链表: 交集list: 4->10 并集list: 2->8->20->4->15->10 方法1 (简单方法) 可以参考链表系列中的"链表操作 - 求两个链表的交集(intersection)以及并集(union)" 方法2

求两个有序数组的中位数

这是我做的第二个leetcode题目,一开始以为和第一个一样很简单,但是做的过程中才发现这个题目非常难,给人一种“刚上战场就踩上地雷挂掉了”的感觉.后来搜了一下leetcode的难度分布表(leetcode难度及面试频率)才发现,该问题是难度为5的问题,真是小看了它!网上搜了很多答案,但是鲜见简明正确的解答,唯有一种寻找第k小值的方法非常好,在此整理一下. 首先对leetcode的编译运行吐槽一下:貌似没有超时判断,而且small和large的数据集相差很小.此题一开始我采用最笨的方法去实现,利

【递归打卡2】求两个有序数组的第K小数

[题目] 给定两个有序数组arr1和arr2,已知两个数组的长度分别为 m1 和 m2,求两个数组中的第 K 小数.要求时间复杂度O(log(m1 + m2)). [举例] 例如 arr1 = [1, 2,3],arr2 = [3,4,5,6],K = 4. 则第 K 小数为 3. 例如 arr1 = [0,1,2],arr2 = [3,4,5,7,8], K = 3; 则第 K 小数为 2. [难度] 难 解答 这道题和我上次讲的那一道题是非常非常类似的:递归打卡1:在两个长度相等的排序数组中

Java 合并两个排序数组

题目:将两个排序好的数组组成一个新的排序好的数组,给出A=[1,2,3,4],B=[2,4,5,6],返回 [1,2,2,3,4,4,5,6] 挑战 你能否优化你的算法,如果其中一个数组很大而另一个数组很小? 思路: 两根指针分别指向两个数组的开头,每次取值两个指针位置较小的数,指针后移,直到其中一个数组结束,将另外一个数组指针后面的元素连接到新数组的后面即可 关于挑战的情况 如果A[0]>B[B.length-1]||A[A.length-1]<B[0] ,可根据情况 直接将A连接到B的后面