《算法导论》2.3-7 检查集合中是否存在两数字和为指定的X--算法和证明

习题2.3-7:设计一个算法,对于一个给定的包含n个整数的集合S和另一个给定的整数X,该算法可以在时间内确定S中是否存在两个元素,使得它们的和恰为X。

解题思路:首先应该想到的是先用一个的排序算法对S中的元素进行排序。接下来有两种处理思路,第一种思路是遍历已经排好序了的S中的所有元素a,并采用

二分查找的方法在S中查找X-a,如果能够找到,那么说明S中确实存在两个元素的和为X,算法终止。这种思路很显然是满足的限制要求的;第二种思路是我自己

想出的一个算法,这个算法也很简单,但是其正确性不是很好证明。

思路1:

CheckSum( A[1…n] , X )
1    MergeSort( A ) //从小到大排序
2    for i = 1 to n
3        if  BinarySearch( A[1…n] , X-A[i] ) != –1 //假设BinarySearch在找不到指定元素的时候返回-1
4              return true
5    return false

思路2:

CheckSum( A[1…n] , X)
1    MergeSort( A ) //从小到大排序
2    i = 1
3    j = n
4    while( i < j)
5        if( A[i] + A[j] == X)
6             return true
7        else if( A[i] + A[j] > X)
8             j--
9        else if( A[i] + A[j] < X)
10           i++
11   return false

算法正确性证明:

思路1的正确性是显而易见的;思路2的正确性就不那么直观,其可能令人感到困惑的地方在于:思路2会不会漏掉某些情况?下面开始思路2的证明,

但是因为我也是初学,证明过程不是很严谨和规范。

首先,假设S中“不”包含两个和为X的元素,那么思路2的第5行的测试条件永远不会成真,那么最终算法一定会返回false。因此,证明思路2的正确性

便转化成证明:

若S中存在两个元素a和b,使得a+b==X,那么算法一定会返回true。(*)

为了证明(*)成立,下面首先证明思路2的算法在执行过程中满足如下的特性:

若S中存在两个元素a和b,使得a+b==X(不妨设a<=b),则在思路2的算法执行过程汇中,每一次迭代开始之前(即算法第4行执行之前),A[i…j]都包含a和b。(#)

下面采用归纳法证明(#)的成立:(令 ik,jk 分别表示第 k 轮迭代开始之前 i 和 j 的取值,A[ik…jk]表示第k轮迭代开始之前的A[i…j])

证明:
1、第 1 轮迭代开始之前,i1=1,j1=n。A[ik…jk]即为A[1…n],a和b很显然包含在A[1…n]中,结论成立。
2、若第 k 轮迭代开始之前,A[ik…jk]包含a和b。则按照算法的执行步骤:
     (1)如果A[ik]+A[jk]==X,算法返回true,算法终止。
     (2)如果A[ik]+A[jk] > X,算法使得jk值减1,即第 k+1 轮迭代开始之前,ik+1 = ik,jk+1 = jk - 1。下面证
             明A[jk]不可能是a或者b中的任何一个: 
             (2.1)因为a<b,且A[ik…jk]包含a和b,所以A[jk]不可能是a。
             (2.2)假设A[jk]等于b,因为A[]是从小到大排序的,所以,必然有:
                        A[jk]+A[jk-1] > A[jk]+A[jk-2] > A[jk]+A[jk-3] > … > A[jk]+A[ik+1] >A[jk]+A[ik] > X,即:
                        b+A[jk-1] > b+A[jk-2] > b+A[jk-3] > … > b+A[ik+1] >b+A[ik] > X
                        也就是说a不可能是A[ik…jk-1]中的任何一个元素,这和前提:A[ik…jk]包含a和b 矛盾,所以假
                        设错误,所以A[jk]不是b。
              因为A[ik…jk]中包含a和b,而又已经证明A[jk]不是a或者b,又ik+1 = ik,jk+1 = jk - 1 ,所以,在第k+
              1轮迭代开始之前,A[ik+1…jk+1]一定包含a和b。
     (3)如果A[ik]+A[jk] < X,同理可证第k+1轮迭代开始之前A[ik+1,jk+1]一定包含a和b。 
3、由1、和2、可知,每一次迭代开始之前,A[i…j]都包含a和b。

现在(#)已经得到证明,而由(#)证(*)是很直观的。因为A[i…j]中始终包含a和b,并且每一次迭代A[i…j]的规模小一,所以,最坏的情况是迭代一直执行到i+1=j的

时候,因为此时A[i,j]包含a和b,所以A[i]一定是a,A[j]一定是b,算法检测到A[i]+A[j] = a+b=X,算法返回true。

总结:以上便是全部内容,从理论上讲思路2应该要比思路1要快(虽然它们都是)。但是很明显地,思路1的正确性更加直观。

时间: 2024-10-17 19:46:55

《算法导论》2.3-7 检查集合中是否存在两数字和为指定的X--算法和证明的相关文章

[算法学习]给定一个整型数组,找出两个整数为指定整数的和(3)

问题描述: 设计一个类,包含如下两个成员函数: Save(int input) 插入一个整数到一个整数集合里. Test(int target) 检查是否存在两个数和为输入值.如果存在着两个数,则返回true,否则返回false 允许整数集合中存在相同值的元素 分析: 与[算法学习]给定一个整型数组,找出两个整数为指定整数的和(2)不同,这里需要算出的是存不存在这两个数,可以在上一篇的基础上修改一下数据结构,HashMap其中key是数值,value是数值个数,然后需要作两步判断,map中存在数

[算法导论]练习2-4.d求排列中逆序对的数量

题目:给出一个确定在n个不同元素的任何排列中逆序对数量的算法,最坏情况需要Θ(nlgn)时间.(提示:修改归并排序.) 思路:修改从大到小排序的归并排序. 归并排序分为三步:分解.解决.合并. 分解:将排列A分解为A1.A2两个子排列. 解决:递归的从大到小排列A1和A2,在此同样递归的求解A1.A2的逆序对数量. 合并:按照递归排序的合并策略从大到小比较A1中的元素[a1,a2,a3…]和A2中的元素[b1,b2,b3…]. 1.若a1大于b1,则a1大于A2中的所有元素,逆序对的数量加上le

leetcode-1 Two Sum 找到数组中两数字和为指定和

 问题描述:在一个数组(无序)中快速找出两个数字,使得两个数字之和等于一个给定的值.假设数组中肯定存在至少一组满足要求. <剑指Offer>P214(有序数组) <编程之美>P176 Que:Given an array of integers, find twonumbers such that they add up to a specific target number. The function twoSum should return indices ofthe tw

一个数组中只有两个数是不同的,其他数字是成对出现的,下面代码可将该数组中不同的两数字找出并输出

#include<stdio.h>int main(){ void function(int * str, int size, int *p1, int *p2); int i = 0; int num1 = 0, num2 = 0; int arr[10] = {0}; int len = sizeof(arr) / sizeof(arr[0]); for (i = 0; i < len; i++) {  scanf("%d", &arr[i]); }  f

【算法导论学习-015】数组中选择第i小元素(Selection in expected linear time)

1.算法思想 问题描述:从数组array中找出第i小的元素(要求array中没有重复元素的情况),这是个经典的"线性时间选择(Selection in expected linear time)"问题. 思路:算法导论215页9.2 Selection in expect linear time 2.java实现 思路:算法导论216页伪代码 /*期望为线性时间的选择算法,输入要求,array中没有重复的元素*/ public static int randomizedSelect(i

算法导论 学习资源

学习的过程会遇到些问题,发现了一些比较好的资源,每章都会看下别人写的总结,自己太懒了,先记录下别人写的吧,呵呵. 1  Tanky Woo的,每次差不多都看他的 <算法导论>学习总结 - 1.前言 <算法导论>学习总结 - 2.第一章 && 第二章 && 第三章 <算法导论>学习总结 - 3.第四章 && 第五章 <算法导论>学习总结 - 4.第六章(1) 堆排序 <算法导论>学习总结 - 5.第六

not(expr|ele|fn)从匹配元素的集合中删除与指定表达式匹配的元素

not(expr|ele|fn) 概述 从匹配元素的集合中删除与指定表达式匹配的元素 参数 exprStringV1.0 一个选择器字符串.深圳dd马达 elementDOMElementV1.0 一个DOM元素 function(index)FunctionV1.4 一个用来检查集合中每个元素的函数.this是当前的元素. 示例 描述: 从p元素中删除带有 select 的ID的元素 HTML 代码: <p>Hello</p><p id="selected&quo

集合中list、ArrayList、LinkedList、Vector的区别、Collection接口的共性方法以及数据结构的总结

List (链表|线性表) 特点: 接口,可存放重复元素,元素存取是有序的,允许在指定位置插入元素,并通过索引来访问元素 1.创建一个用指定可视行数初始化的新滚动列表.默认情况下,不允许进行多项选择. 注意,这是 List(rows, false) 的一种便捷方法.还要注意,列表中的可视行数一旦创建就不能更改. public List(int rows)------------------row-----要显示的项数 2.创建一个初始化为显示指定行数的新滚动列表. 注意,如果指定了零行,则会按默

《算法导论》中动态规划求解钢条切割问题

动态规划算法概述 动态规划(dynamic programming)1是一种与分治方法很像的方法,都是通过组合子问题的解来求解原问题.不同之处在于,动态规划用于子问题重叠的情况,比如我们学过的斐波那契数列.在斐波那契数列的求解问题中,我们经常要对一个公共子问题进行多次求解,而动态规划算法,则对每个子问题只求解一次,将其解保存在一个表格中,从而避免了大量的冗余计算量. 动态规划算法常用于寻找最优解问题(optimization problem).而其规划大概可分为四步: 1.刻画一个最优解的结构特