问题一:求sum值
描述:给定一有序序列ary和sum值,求序列中是否存在两元素e1和e2,其和刚好为sum。
算法思想:
这是典型的两指针的用法。
i指针从头部开始,j指针从尾部开始,相向移动,
本质向讲,在移动过程中比较ary[i]+ary[j]与sum的大小,达到逐步排除元素的过程,缩短查找范围。
最开始,i和j处于序列首部和尾部
如果ary[i]+ary[j]=sum,直接返回结果。
如果ary[i]+ary[j]<sum,需要增大ary[i]或者ary[j],但是j已经不能在增大,只能i++,因此ary[i]永远被排除掉,缩短了查找范围,
然后在新的序列范围中,继续排除元素逐步缩短查找范围。
如果ary[i]+ary[j]>sum,需要减小ary[i]或者ary[j],但是i已经不能在减小,只能j--,因此ary[j]永远被排除掉,缩短了查找范围,
然后在新的序列范围中,继续排除元素逐步缩短查找范围。
从归纳法的角度看,不难发现,在缩短序列范围的过程中只能是i++或者j--,i只能想后一定吗,j只能向前一定,不能回溯。
java实现:
public static boolean search(int[] ary,int low,int high,int sum){ int i = low,j = high;//一前一后两指针 while(i<j){//两指针i,j不能碰头,这样能够避免sum=2*ary[i]=2*ary[j]的情况 if (ary[i]+ary[j] < sum) { i++; }else if(ary[i]+ary[j] > sum){ j--; }else { return true; } } return false; }
问题二:数组原地去重
描述:给定一重复元素ary,包含若干重复元素,求数组中不重复元素的个数的原地算法,可修改元素组。
Java实现:
/* *实现原地数组去重,可改变原数组,返回唯一元素的个数。 *实际上如果利用额外的辅助空间的话,数组去重的方式有很多, *此算法思想的优势就在于只需常数的额外辅助空间,也即是就地算法 * */ public static int removeDuplicates(int[] ary,int low,int high){ int i = low,j = i+1;//两指针,i从low开始,j从i+1开始 for (; j <= high; j++) { if (ary[i] != ary[j]) { ary[++i] = ary[j];//i始终记录唯一序列的末尾位置 } } return i-low+1; } /* * 如果要求重复元素的个数最多可为n(n>=1) * */ public static int removeDuplicates(int[] ary,int low,int high,int n){ int i = low+n-1,j = i+1;//i直接从第n个元素开始,j从i+1开始 for (; j <= high; j++) { if (ary[i-n+1] != ary[j]) {//在ary[i]前面且距离为n的元素为ary[i-n+1] ary[++i] = ary[j]; } } return Math.min(i-low+1, ary.length); }
问题三:三色排序
问题描述:输入一个整形的数组,每个元素在0到2之间,其中0,1,2分别代表红色、白色和蓝色。现要求对数组进行排序,相同颜色在一起,而且白色在红色之后,蓝色在白色之后,要求之间复杂度在O(n).
算法思想:
用两指针i和j记录0序列,1序列,2序列的分界位置
只要确定了i和j的值,就可以将三个序列区分开来。
让i指针记录1序列的头部
j指针记录2序列的头部
扫描指针k整个序列头开始扫描,过程中实时更新i和j的值,直到k遇到j位置,此时j后面的元素全为2
i前面的元素全为0,其余元素(i到j之间的元素)全为1。
java实现:
public static void sort(int[] ary){ int i = 0,j = ary.length,k = 0;//i记录1序列的头部,j记录2序列的头部,k始终处于1序列尾部的下一个位置 while(k<j){ if (ary[k] == 0) {//将ary[k]=0与ary[i]=1交换,此时可以明确知道ary[i]的值 swap(ary, i++, k++); }else if(ary[k] == 1){ k++; }else {//将ary[k]=?与ary[j]=2交换,此时无法知道ary[j--]的值,因此,k保持不变 swap(ary, k, --j); } } }