如何灵活使用“双指针”

双指针一般运用于数组和链表当中。在数组当中,我们通常利用2个下标去进行操作(如:删除数组中的某个元素,二分查找等);在链表当中,我们会去用2个指针(快指针fast和慢指针slow)对链表结构进行操作(如:求链表的中间结点,求相交链表的交点,判断链表是否成环等)。

双指针在数组中的应用:

     1.原地删除数组中值为val的元素,要求时间复杂度为O(N),空间复杂度为O(1)

这道题由于时间复杂度的限制,没办法用双重循环去删除数组中的重复元素,因此这里采用了双下标去删除的做法。一个下标为slow,另一个为fast,它们都从0开始,当某位置的元素不是val,将fast下标的值赋值给slow后,fast和slow一起++;如果某位置的元素是val,则slow不进行任何操作,fast++。当fast = 数组大小 -1的时候结束循环。

int removeElement(int* nums, int numsSize, int val)
{
    //定义2个下标变量
    int slow = 0;
    int fast = 0;
    while(fast < numsSize)
    {
        //判断是否是要删除的元素
        if(nums[fast] == val)
        {
            fast++;
        }
        else
        {
            nums[slow] = nums[fast];
            fast++;
            slow++;
        }
    }
    //返回删除后的元素个数
    return slow;
}

2.逆置数组(反转数组)

定义2个下标left和right,left从0开始right从数组的大小-1开始,2个下标所对应的元素进行交换。最终当left >= right的时候停止交换。

void reverse(int* nums, int size) 
{
    int left = 0;
    int right = size - 1;
    while (left < right) 
    {
        //交换left和right对应的元素
        int tmp = nums[left];
        nums[left] = nums[right];
        nums[right] = tmp;
        left++;
        right--;
    }
}

双指针在链表中的应用:

1.求链表的中间结点(如果有2个中间结点,返回第2个)

定义快指针fast和慢指针slow,fast每次走2步slow每次走1步,这样当fast到尾结点或者NULL的时候,slow正好处于链表的中间位置。

下图是对应2种情况,fast->next为NULL和fast为NULL。

struct ListNode* middleNode(struct ListNode* head)
{
    //定义2个指针,快指针1次走2格,慢指针1次走1格
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    //注意判断条件,fast为NULL时fast在NULL处,fast->next=NULL时fast在尾结点处。
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
    }
    return slow;
}

2.求链表的倒数第k个结点

定义快指针fast和慢指针slow,要求倒数第k个结点,也就是这个结点是距离尾结点的距离为k-1距离NULL为k】。此时让fast指针先走k步,然后fast和slow一起走,当fast==NULL时候,slow所指向的结点就是倒数第k个结点。

struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) 
{
    struct ListNode* fast = pListHead;
    struct ListNode* slow = pListHead;
    //这里需要注意,如果k给的值大于链表的长度,则也要结束循环,最后判断返回NULL
    while(k  && fast)
    {
        fast = fast->next;
        k--;
    }
    if(fast == NULL && k)
        return NULL;
    while(fast)
    {
        fast = fast->next;
        slow = slow->next;
    }
    return slow;
}

3.求2个相交链表的交点

定义2个指针用来去求2条链表的长度,然后根据链表的长度定义一个指向长链表的指针LongList指向短链表的指针ShortList。让LongList先走2条链表的长度差的绝对值的步数,然后LongList和ShortList一起走,并且同时进行判断,如果LongList == ShortList那么第一个相等的点就是2条链表的交点。

struct ListNode* getIntersectionNode(struct ListNode *headA, struct ListNode *headB) 
{
    //思路:先让长的链表的指针走它比短的长多少的步数,然后两个指针同时移动进行比较
    struct ListNode* curA = headA;
    struct ListNode* curB = headB;
    int LA = 0;
    int LB = 0;
    while(curA)
    {
        LA++;
        curA = curA->next;
    }     
    while(curB)
    {
        LB++;
        curB = curB->next;
    }
    //定义一个长链表的指针和一点短链表的指针
    struct ListNode* LongList = LA>LB?headA:headB;  
    struct ListNode* ShortList = LA<=LB?headA:headB;  
    //求出间隔的步数
    int gap = abs(LA-LB);//abs求的是绝对值
    while(gap--)
    {
        LongList = LongList->next;
    }
    //找第一个相同结点,然后return该点
    while(LongList)
    {
        if(LongList == ShortList)
            return LongList;
        LongList = LongList->next;
        ShortList = ShortList->next;
    }
    //两条链表不相交,返回NULL
    return NULL;
}

4.判断链表是否成环

定义快指针fast和慢指针slow,快指针每次走2步,慢指针每次走1步,如果快指针再次与慢指针相遇(fast == slow),则该链表成环,否则不成环。【这里快指针的步数可以改变,但要注意有可能会出现快指针永远追不上慢指针的情况。】

扫描二维码关注公众号,回复: 12665649 查看本文章

快指针每次和慢指针间距缩小1,所以如果该链表成环的话,快指针会与慢指针再次重合

bool hasCycle(struct ListNode *head) 
{
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    //通过定义一个快指针和慢指针,如果快指针能追上慢指针,则该链表有环,否则该链表无环
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        //快指针追上了慢指针
        if(fast == slow)
            return true;
    }
    //如果快指针遇到NULL,则该链表肯定不成环
    return false;
}

5.求成环链表进环的第一个结点(入环点)

在上一个题的基础上,我们可以求出fast和slow相遇点。然后定义一个指针meet指向该结点,再定义一个指向头结点的指针headhead和meet一起走,它们的值相同的那个点就是入环点

struct ListNode* detectCycle(struct ListNode *head) 
{
    //通过head和fast与slow的交点(meet)这两个指针进行查找入环点
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(fast == slow)
            break;
    }
    if(fast == NULL || fast->next == NULL)
        return NULL;
    struct ListNode* meet = fast;
    while(head != meet)
    {
        head = head->next;
        meet = meet->next;
    }
    return head;
}

猜你喜欢

转载自blog.csdn.net/weixin_51696091/article/details/114371627