LeedCode 2. 玩转双指针

本文深入探讨了双指针在遍历数组、查找特定和、合并有序数组等场景的应用,并提供了详细的示例代码。同时,介绍了快慢指针在解决链表环问题中的作用。此外,还讲解了如何利用双指针对字符串进行最小覆盖子串查找。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.算法解释:

双指针主要用于遍历数组,两个指针指向不同的元素。如果两个指针指向同一个数组,但是遍历方向相反,则可以用来搜索。通常这个数组时排好序的。

2. 指针函数与函数指针:

指针函数是指返回类型为指针的函数,函数指针是指向函数的指针。可详细通过如下示例来理解。

//addition是指针函数,一个返回类型为指针的函数
int* additition(int a, int b) {
    int* sum = a + b;
    return sum;
} 

int subtraction(int a, int b) {
    return a - b;
}

//minus是函数指针,一个指向函数subtraction的指针
int (*minus) (int, int) = subtraction;

int operation(int x, int y, int (*func)(int, int)) {
    return (*func)(x,y);
}


int* m = addititon(1, 2); //返回一个指针,指向的结果是3
int n = operation(3, *m, minus); //注意这里*操作符为解引用操作符,它返回指针m所指的地址所保存的值,也就是3。最终返回结果为3-3=0。

3. LeetCode 167  力扣

题目:在一个增序的整数数组里找到两个数,使它们的和为给定值。已知有且只有一对解。

思路:使用两个指针,一个指向开头,一个指向结尾。如果双指针指向的和为Target,则结束。如果小于Target,则头指针后移。如果大于Target,则尾指针前移。

class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
        int i = 0;
        int j = numbers.size()-1;
        while (i<j) {
            if (numbers[i] + numbers[j] < target) i++;
            else if (numbers[i] + numbers[j] > target) j--;
            else return vector{i+1, j+1};
        }
        return vector{i+1, j+1};

    }
};

4. LeetCode 88 归并两个有序数组 力扣

 题目:给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。请你合并 nums2 到 nums1 中,使合并后的数组同样按非递减顺序排列。

思路:使用两个指针,分别指向nums1和nums2的原始数组的结尾。将指针指向的值中,较大的值copy到nums1[m+n-1]。注意遍历结束后,如果nums2的值没有被遍历到最开头,需要将剩下的值赋值到nums1。

class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        while (m > 0 && n > 0) {
            if (nums1[m-1] >= nums2[n-1]) {
                nums1[m+n-1] = nums1[m-1];
                m--;
            }
            else {
                nums1[m+n-1] = nums2[n-1];
                n--;
            }
        }
        for (int i = 0; i < n; i++) { //为什么是n,如果m=0,n可能还是>0,则需要把nums2剩余的序列拷贝到nums1。也就是从0到n-1位置的元素。
            nums1[i] = nums2[i];
        }
    }
};

5. LeetCode 142 快慢指针 

题目:给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

思路:使用快慢指针,快指针每次走两步,慢指针每次走一步。当快慢指针第一次相遇时,将快指针指向开头,然后让他慢走,第二次相遇即为环的入口。

证明:

    在fast和slow第一次相遇的时候,假定slow走了n步,环路的入口是在p步,那么:

  slow走的路径: p+c = n;(1) c为fast和slow相交点 距离环路入口的距离

  fast走的路径: p+c+k*L = 2*n;(2), L为环路的周长,k是整数

  fast从头开始走,步长为1. 当fast又走了p步时,slow也走了p步,slow在环上走了c+p步,而c+p=n, 而n是L的倍数(将(2)-(1)得k*L = n,说明n是L的倍数)。所以这时候就走到了入口处。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode *slow = head;
        ListNode *fast = head;
        //第一次相遇,如果不相遇就已经走到尽头,表示无环,直接return null。
        do {
            if (!fast || !fast->next) return NULL;
            slow = slow->next;
            fast = fast->next->next;
        }while (slow != fast); //因为最开始的时候fast和slow都被初始化为head,是相等的。所以需要先执行命令再判断条件,所以用do while而非while
        fast = head;
        while (fast != slow) {
            fast = fast->next;
            slow = slow->next;
        }
        return fast;

        
    }
};

6. LeetCode最小覆盖子串  力扣

题目:给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。

思路:首先是我们怎么找到涵盖t的子串? 使用双指针l和r, 它们最开始都指向起始位置,然后向右滑动r指针,直到l和r之间的子串,包含了所有的t的字符。但是这样的子串不一定是最小的。为了找到最小的,我们要尝试向右滑动l指针。如果[l+1, r]区间也包括所有t,那就可以直接继续滑动,如果滑动l导致果[l+1, r]区间不再包括所有t,则需要统计差哪个字符,然后向右滑动r。知道r到达s的最右边。

string minWindow(string s, string t) {
  vector<bool> flags(128, false);
  vector<int> counts(128,0);
  for (int i=0; i<t.size();i++) {
    counts[t[i]]++;
    flags[t[i]] = true;
  }
  
  int cnt = 0, l = 0, min_l = 0, min_size = s.size()+1;
  for (int r = 0; r < s.size(); r++) {
    if (flags[s[r]]) {
      if (counts[s[r]]-- > 0) cnt++;
      while (cnt == t.size()) {
        if (r-l+1 < min_size) {
          min_l = l;
          min_size = r-l+1;
        }
        if (flags[s[l]] && ++counts[s[l]] > 0) {
          cnt--;
        }
        l++;
      }
    }
    
  }
  return min_size > s.size()? "":s.substr(min_l, min_size);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值