算法设计与分析—面试题分享.pptVIP

  • 11
  • 0
  • 约2.16千字
  • 约 13页
  • 2016-11-02 发布于湖北
  • 举报
—— * 第一节:寻找满足条件的两个数 题目: 1.输入一个有序的数组和一个数字, 在数组中查找两个数,使得它们的和正好是输入的那个数字。 例如:输入数组1、2、4、7、11、15和数字15。由于4+11=15,因此输出4和11 要求时间复杂度是O(n)。如果有多对数字的和等于输入的数字,输出任意一对即可 * 思路分析: 1.直接穷举,从数组中任意选取两个数,判定它们的和是否为输入的那个数字。此举复杂度为O(N^2)。很显然,我们要寻找效率更高的解法。 2.题目相当于:对每个a[i],然后查找判断sum-a[i]是否也在原始序列中,每一次要查找的时间都要花费为O(N),这样下来,最终找到两个数还是需要O(N^2)的复杂度。那如何提高查找判断的速度列? 二分查找,将原来O(N)的查找时间提高到O(logN),这样对于N个a[i],都要花logN的时间去查找相对应的sum-a[i]是否在原始序列中,总的时间复杂度已降为O(N*logN),且空间复杂度为O(1)。 3.更好办法:用两个指针i,j,各自指向数组的首尾两端,令i=0,j=n-1,然后i++,j--,逐次判断a[i]+a[j]?=sum,如果某一刻a[i]+a[j]sum,则要想办法让sum的值减小,所以此刻i不动,j--,如果某一刻a[i]+a[j]sum,则要想办法让sum的值增大,所以此刻i++,j不动。 * 核心代码 Pair findSum(int *s,int n,int x) { //sort(s,s+n); 如果数组非有序的,那就事先排好序O(N*logN) int *begin=s; int *end=s+n-1; while(beginend) //俩头夹逼,或称两个指针两端扫描法,很经典的方法,O(N) { if(*begin+*endx) { --end; } else if(*begin+*endx) { ++begin; } else { return Pair(*begin,*end); } } return Pair(-1,-1); } * 思考:输入一个无序的数组和一个数字, 在数组中查找两个数,使得它们的和正好是输入的那个数字。 能满足:时间复杂度是O(n),空间复杂度不限。 * 第二节:寻找满足条件的的多个数 1.输入两个整数 n 和 m,从数列1,2,3.......n 中 ,随意取几个数, 使其和等于 m ,要求将其中所有的可能组合列出来。 * 分析 1. 这个问题属于子集和问题(也是背包问题)。本程序采用 回溯法+剪枝 2.X数组是解向量,t=∑(1,..,k-1)Wi*Xi, r=∑(k,..,n)Wi 3. 若t+Wk+W(k+1)=M,则Xk=true,递归左儿子(X1,X2,..,X(k-1),1); 否则剪枝; 4.若t+r-Wk=M t+W(k+1)=M,则置Xk=0,递归右儿子(X1,X2,..,X(k-1),0);否则剪枝; 本题中W数组就是(1,2,..,n),所以直接用k代替WK值。 * 图解分析: 比如n=4,m=5;则可行解为: (1,0,0,1)和(0,1,1,0) * 核心代码 * 类题:给定k个整数集合S和两个整数m,n,是否存在的一个子集H,使得m是取自H中至多n个整数的和(可以重复取)。 分析: ①.首先将输入的k个数从大到小排序,并存放在数组b中,b[0]最大。,记cn是当前剩余个数,cm是剩余数值。开始cm=m,cn=n. ②.其次,通过回溯迭代法,一次选择合乎要求的整数。在选择过程中将大的数先作为m的一个和项,并用一维数组a存放这些选定的和项。A中当前存放的数可能与前一个刚选过的数相同。用lev记录当前选到的数的个数,x[lev]记录在这k个数中被选到的数的下标。在确定时,当前考虑的候选数是b[x[lev]] ,那么只要当前的个数cn尚未为0,而且b[]可以作为一个和项,即cm=b[x[lev]],那么将b[x[lev]]考虑进来。假如已完成任务,那么结束。否则进行后面的选择。如果做到一定时候发现无法深入下去,那么将刚选择的决定推翻(即回退),或考虑其他选择或继续回退。 ③.比如:m=10,n=3,k=3,以及k个数集合S={4,2,1}时,10=4+4+2,故10是满足条件的数。 * 核心代码 * 课外思考题 1.求一个整数用二进制表示时1的个数。 2.求500万以内的所有亲和数。(如果两个数a和b,a的所有因数之和等于b,b的所有真因数之和等于a,则称a,b是一对亲和数。) 例如: 220的因子是:1、2、4、5、10、11、20、22、44

文档评论(0)

1亿VIP精品文档

相关文档