两指针扫描算法举例

问题一:求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);
            }
        }
    }

 

posted @ 2017-09-24 00:27  Qcer  阅读(697)  评论(0编辑  收藏  举报