若干算法题目备忘

1. 给定一数组,求和为0的所有连续子序列

数组sum,sum[k]为从第0到第k个数的和,则若sum[i]与sum[j]相等,则从第i+1到第j个数的和即为0

给sum排序,找到所有相等的元素即可。

升级问题,求和为k的所有连续子序列

同样生成数组sum并排序,现在要找的是差为k的两元素。

联想另一问题,找和为k的两元素,则排序,从两边往中间遍历;和大则右边左移,和小则左边右移

现在要找差为k的两元素,排序后,两指针一前一后从左往右遍历;差大则小指针右移,差小则大指针右移

(方法正确性证明:若存在大小两数差为k,若小指针先就位,则必然差小于k,则大指针终将右移至就位;若大指针先就位,则必然差大于k,则小指针终将右移至就位)

若存在元素相等的情况,则一但确定一对位置i和j满足差为k,则双层遍历

for(i0 = i, j0 = j; sum[i0] == sum[i]; i++)

for( j = max(j0, i+1); sum[j0] == sum[j]; j++)

记录i, j对;

2. 给定一数组,按出现频率由大到小排序;频率相同者按数字由大到小排序

先排序,统一加d使最小数为1,记录最大数+1值为M;

遍历,将数加出现次数*M,再排序,统一对M取余,统一减d,复原

3. 1到n,少一个数X,重复一个数Y,求X, Y

方法1,根据1到n的和可得X-Y; 根据1到n的平方和可得X平方-Y平方

方法2,从位置0开始,根据存储值设对应位置为0,连续处理,要么回到位置0,则将位置0设为0;要么止于位置Y,则设置位置0为-1。取下一值非0位置为起始位置继续

方法3,新数组作标记

方法4,

引:一组正整数,两个数出现了奇数次,其它数出现了偶数次,求这两个数

累积求异或^,结果值为两数的异或;取这个值不为0的一位,按此位将原数组分成两组,各自累积求异或,各得一值,即为所求

回观此题,1到n累积求异或得v1,数组中数累积求异或得v2,X^Y=v1^v2;取这个值不为0的一位,将1到n中此位为0的数累积求异或,再将数组中此位不为0的数累积求异或,这两个异或的异或,即为X或者Y。又已知X^Y,则可得另一个数。遍历一遍原数组即可确定各是X还是Y。

方法5,第8题解法

4. O(nlgn)复杂度求递增子序列

传统方法用动态规划,用数组a[], a[i]表示以数组位置i为结尾的最长子序列的长度。确定这个长度时,就要遍历位置i之前的所有元素,取元素值小于位置i元素的,并且长度最大的a[j],a[i] = a[j] + 1,顺便保留前驱位置j。此复杂度为O(n2)

现在用数组b[],b[k]表示长度为k+1的子序列中,结尾数最小的元素的位置。这样,在遍历原数组时,用折半搜索得第一个大于位置i元素的b[j],更新b[j]为位置i,同时记录i前驱为j-1。最终,取b[]的最后一个元素,即长度最大的子序列的位置,根据前驱位置得出结果序列。此复杂度为O(nlgn)

5. 有n个电子元件,用好的元件测试好的元件,得true;测试坏的元件,得false。而用坏的元件测试其它元件,结果不定。现已知好的元件数>n/2,求出一个好元件。

若n为偶数,平分两组,对应位置元件互相检测,得到k对双true元件对。每一对中,要么二者全为好元件,要么全为坏元件。好元件对数必然大于坏元件对数。则取这些元件对中全为第一组的元件,其数目k,且好元件数>k/2,则问题转化到规模k,k<=n/2。

若n为奇数,取最后一个元件放一边,对剩余元件平分两组对应互测得k对双true元件对:

    若k为偶数,则元件对中全为第一组的元素里,好元件数>=坏元件数;则如果单挑出的元件为好元件,则把其加入第一组后,得好元件数>坏元件数;如果单挑出元件为坏元件,则第一组里,好元件数>=坏元件数+2,把其加入第一组后,依然可得好元件数>坏元件数。则问题转化为k + 1,k+1<=n/2上取整

    若k为奇数,则元件对中全为第一组的元素里,好元件数>坏元件数;则不需再考虑单挑出的元件,问题即转化为k,k<=n/2下取整

总复杂度即为n+n/2+n/4…=O(n)

6. 求二叉树上两个结点的最近公共parent结点

方法一:推根入栈,遍历左子树,遍历右子树,根出栈。当找到第一个结点后,其后推根入栈时带标记。找到第二个结点时,连续出栈,直到找到第一个没标记的,即为所求。

方法二:遍历过程中,回溯时建立指向父结点指针,找到第二个结点后,依次求父结点直到第一个没有父结点指针的

变形:求二点距离

在方法一中,找到第一个结点后,记录在之后没有作标记的出栈点数,即为它到最近公共父结点的距离;第二个结点的距离可以在一路出栈找最近公共父结点时得出

在方法二中,直接按父结点指针找

7. 一架飞机加满油只能绕地球半圈。飞机间可互相加油,只能在起飞处安全降落。若使至少一架飞机绕地球一圈,需同时起飞多少架飞机?

6

8. 一个大小为n的数组,里面的数都属于范围[0, n-1],有不确定的重复元素,找到所有重复元素,要求O(1)空间和O(n)时间。  

  1. //By MoreWindows (http://blog.csdn.net/MoreWindows)  
  2. voidPrintRepeatedNums(int *a, int n)  
  3. {  
  4.     for(int i = 0; i < n; i++)  
  5.     {  
  6.         int nRealIndex = a[i] >= n ? a[i] – n : a[i];  
  7.         if (a[nRealIndex] >= n) //这个位置上的值大于n说明已经是第二次访问这个位置了  
  8.             printRealIndex;  
  9.         else  
  10.             a[nRealIndex] += n;  
  11.     }  

由条件提示明显需要将元素值与下标联系,直接想法是移动元素到对应位置,但实际上只要在对应位置作标记即可,不需要移动。而要作标记又不能丢失位置上原有数值,所以用+n的方式,即保留原信息,又作了标记。

点赞