• 转:2个指针位置的妙用


    使用两个指针可以轻松的解决许多算法问题,归纳出如下几种

    1、  判断链表是否带环

    带环链表的判断是链表中经常考察的内容。一个循环链表可以无休止地遍历下去。我们可以定义两个指针,一个快指针一个慢指针,如果在遍历到尾端前二者相遇,那么链表就是有环链表

    bool haveCycle(LinkList * Head)
    {
        if (!Head)
        {
            return false;
        }
        LinkList * fast=Head;
        LinkList * slow=Head;
        while (!fast)
        {
            if (fast==slow) //如果两指针相遇,则返回真
            {
                return true;
            }
            fast=fast->next;
            if (!fast)
            {
                return false;
            }
            fast=fast->next;   //快指针走两步
            slow=slow->next;   //慢指针走一步
        }
        return false;
    }

    2、  求链表中倒数第K个节点

    如果用普通的思路得遍历两遍链表,第一遍先求出链表的总长度N,然后第二遍走到第N-k个节点,这个节点就是所求的节点。如果链表很长,那么遍历两次的话就很费时,我们用两个指针的方法遍历一次链表即可,先让一个指针走K步,然后两个指针再一起走,直到第一个指针遍历到链表末尾。

    LinkList * FindRevK(LinkList * Head,int k)
    {
        if (!Head)
        {
            return NULL;
        }
        LinkList * first=Head;
        LinkList * second=Head;
        for (int i=0;i<k;i++)  //先让first走K步
        {
            if (!first)
            {
                return NULL; //链表长度小于K则返回NULL
            }
            first=first->next;
        }
        while (first)
        {
            first=first->next;
            second=second->next;
        }
        return second;
    }

    3、  二分法查找某个数

    用经典的二分法在一个有序数组中可以以log(n)的时间复杂度查找出给定的数。同样也是设定两个位置下表,lowhigh。由于该方法大家都熟悉不过了,只把代码贴上来就行了

    int search(int array[],int n,int value)
    {
        if (!array||n<0)
        {
            return -1;
        }
        int low=0;
        int high=n-1;    
        while (low<high)
        {
            int med=low+(high-low)/2; //这样可以防止low+high发生溢出
            if (array[med]<value)
            {
                low=med+1;
            }
            else if (array[med]>value)
            {
                high=med-1;
            }
            else
                return med;
        }
        return -1;
    }

    4、  在一个有序数组中找出和为N的两个数

    定义两个位置lowhigh,一个在开始处,一个在结尾处,如果二者之和大于Nhigh递减,如果二者之和小于Nlow递增,直到和为N或者二者相遇为止

    void findNum(int * array,int arraysize,int N,int & low,int & high)
    {
        low=0;
        high=arraysize-1;
        while (low<high)
        {
            if (low+high==N)
            {
                return;  //和为N,返回
            }
            else if (low+high<N)
            {
                low++;
            }
            else
            {
                high--;
            }
        }
        //没有两个数的和为N,则置下表为-1
        low=-1;
        high=-1;
    }

    5、  输入一个正数n,输出所有和为n连续正数序列

    输入 15
    输出
    15=1+2+3+4+5
    15=4+5+6
    15=7+8

    我们可用两个数lowhigh分别表示序列的最小值和最大值。首先把low初始化为1high初始化为2。如果从lowhigh的序列的和大于n的话,我们向右移动low,相当于从序列中去掉较小的数字。如果从lowhigh的序列的和小于n的话,我们向右移动high,相当于向序列中添加high的下一个数字。一直到low等于(1+n)/2,因为序列至少要有两个数字

    void printResult(int low,int high,int num)  //该函数实现将low到high之间的数输出到屏幕上
    {
        cout<<num<<"=";
        for (int i=low;i<high;i++)
        {
            cout<<i<<"+";
        }
        cout<<high<<endl;
    }
    void consecutiveN(int num)
    {
        int low=1,high=2;
        int sum=low+high;
        while (low<(num+1)/2)
        {
            if (sum==num)
            {
                printResult(low,high,num); //输出结果
                sum-=low;
                low++;
            }
            while(sum<num)
            {
                high++;
                sum+=high;
            }
            while(sum>num)
            {
                sum-=low; 
                low++;
            }
        }
    }

    上面的程序不好理解,可以用

    15=1+2+3+4+5理解,首先,肯定是会先输出这个。

    low=1,high=5;然后sum-=low,low++.low=2;然后小于了, 

    4+5+6.

    这个算法的特点是先输出最长的比如1+2+3+4+5,然后不断的减小,比如a+b这种形式,

    当2*low<num+1就退出

    6、 输入一个整数数组,调整数组4中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分

    如果不考虑时间复杂度,最简单的思路应该是从头扫描这个数组,每碰到一个偶数时,拿出这个数字,并把位于这个数字后面的所有数字往前挪动一位。挪完之后在数组的末尾有一个空位,这时把该偶数放入这个空位。由于碰到一个偶数,需要移动O(n)个数字,因此总的时间复杂度是O(n2)

    要求的是把奇数放在数组的前半部分,偶数放在数组的后半部分,因此所有的奇数应该位于偶数的前面。也就是说我们在扫描这个数组的时候,如果发现有偶数出现在奇数的前面,我们可以交换他们的顺序,交换之后就符合要求了。因此我们可以维护两个指针,第一个指针初始化为数组的第一个数字,它只向后移动;第二个指针初始化为数组的最后一个数字,它只向前移动。在两个指针相遇之前,第一个指针总是位于第二个指针的前面。如果第一个指针指向的数字是偶数而第二个指针指向的数字是奇数,我们就交换这两个数字

    void Reorder(int *pData, unsigned int length, bool (*func)(int));
    bool isEven(int n)
    {
        return (n & 1) == 0; //判断一个数字是不是偶数并没有用%运算符而是用&。理由是通常情况下位运算符比%要快一些
    }
    void ReorderOddEven(int *pData, unsigned int length)
    {
        if(pData == NULL || length == 0)
            return;    
        Reorder(pData, length, isEven);
    }
    void Reorder(int *pData, unsigned int length, bool (*func)(int))
    {
        if(pData == NULL || length == 0)
            return;    
        int *pBegin = pData;
        int *pEnd = pData + length - 1;    
        while(pBegin < pEnd)
        {
            if(!func(*pBegin)) //从前半部分找出第一个偶数
            {
                pBegin ++;
                continue;
            }
            if(func(*pEnd))  //从后半部分找出第一个奇数
            {
                pEnd --;
                continue;
            }
            //二者交换
            int temp = *pBegin;
            *pBegin = *pEnd;
            *pEnd = temp;
        }
    }

    这道题有很多变种。这里要求是把奇数放在偶数的前面,如果把要求改成:把负数放在非负数的前面等,思路都是都一样的

    转自:http://blog.csdn.net/dlengong/article/details/7418420

  • 相关阅读:
    Ubuntu14.04下Sublime Text 3解决无法输入中文
    100% 解决wine中文乱码问题
    历史朝代顺序表
    HTTP状态码分类
    Kali Linux 2019
    多线程下载器(针对于百度云下载)
    百度云破解版&&第三方下载工具&&分享链接无需提取码直接下载
    MongoDB 4.0.10 GridFS操作
    MongoDB 4.0.10 游标
    MongoDB 4.0.10 用户管理
  • 原文地址:https://www.cnblogs.com/youxin/p/3352542.html
Copyright © 2020-2023  润新知