双指针应用

本文介绍了双指针法的基本概念,通过数组和链表的实例展示如何使用双指针技术,包括判断回文数组和查找链表中间节点。然后提出一个关于点赞日志的稍有难度的问题,利用双指针算法统计曾是“热帖”的帖子编号。
摘要由CSDN通过智能技术生成

目录

双指针法

几个经典的案例

一,数组

二,链表

一个稍有难度的题目

题目描述

输入输出格式

输入格式

输出格式

数据范围

输入样例

输出样例

题解

代码


双指针法

        (对撞指针、快慢指针)双指针法,指的是在遍历对象的过程中,不是普通的使用单个指针进行访问,而是使用两个相同方向(快慢指针)或者相反方向(对撞指针)的指针进行扫描,从而达到相应的目的。换言之,双指针法充分使用了数组有序这一特征,从而在某些情况下能够简化一些运算。

        双指针法什么时候能用:当右指针单调递增时,左指针能单调不减或不增。

        双指针法没有很多的套路或者模型,往往需要不断地做题来加深对这个方法地认识。下面介绍一道例题来讲解双指针法的具体实现。

几个经典的案例

一,数组

        数组的遍历:当需要同时访问数组中的两个元素时,可以使用双指针法来遍历数组。一个指针可以指向数组的起始位置,另一个指针可以指向数组的末尾位置。通过这种方式,我们可以同时从两个方向遍历数组,从而提高算法的效率。

        以下是一个示例代码,展示了如何使用双指针法来判断一个数组是否是回文数组(即正序和逆序读取元素都相同):

#include <iostream>
using namespace std;

bool isPalindrome(int arr[], int n) {
    int left = 0;       // 左指针
    int right = n - 1;  // 右指针

    while (left < right) {
        if (arr[left] != arr[right]) {
            return false;
        }
        left++;
        right--;
    }
    return true;
}

int main() {
    int arr[] = {1, 2, 3, 2, 1};
    int n = sizeof(arr) / sizeof(arr[0]);

    if (isPalindrome(arr, n)) {
        cout << "The array is a palindrome." << endl;
    } else {
        cout << "The array is not a palindrome." << endl;
    }

    return 0;
}

         在上述示例中,我们使用了两个指针 left 和 right 来同时遍历数组的两端。如果左右指针所指向的元素不相等,说明数组不是回文数组,直接返回 false。如果左右指针所指向的元素相等,继续向中间移动,直到左指针超过右指针,此时数组是回文数组,返回 true

二,链表

        链表问题:双指针法也可以用于解决链表相关的问题。在链表中,我们可以使用两个指针分别指向两个不同的节点,从而实现一些特定的操作。

        以下是一个示例代码,展示了如何使用双指针法来找到链表的中间节点:

#include <iostream>
using namespace std;

struct ListNode {
    int val;
    ListNode* next;
    ListNode(int x) : val(x), next(NULL) {}
};

ListNode* findMiddle(ListNode* head) {
    ListNode* slow = head;  // 慢指针
    ListNode* fast = head;  // 快指针

    while (fast != NULL && fast->next != NULL) {
        slow = slow->next;
        fast = fast->next->next;
    }

    return slow;
}

int main() {
    ListNode* head = new ListNode(1);
    head->next = new ListNode(2);
    head->next->next = new ListNode(3);
    head->next->next->next = new ListNode(4);
    head->next->next->next->next = new ListNode(5);

    ListNode* middle = findMiddle(head);
    cout << "The middle node value is: " << middle->val << endl;

    return 0;
}

         在上述示例中,我们使用了两个指针 slow 和 fast 来寻找链表的中间节点。其中,慢指针每次移动一步,快指针每次移动两步。当快指针到达链表的末尾时,慢指针正好到达链表的中间位置。

一个稍有难度的题目

题目描述

        小明维护着一个程序员论坛。现在他收集了一份”点赞”日志,日志共有 N� 行。

        其中每一行的格式是:

ts id  

        表示在 ts时刻编号 id的帖子收到一个”赞”。

        现在小明想统计有哪些帖子曾经是”热帖”。 如果一个帖子曾在任意一个长度为 D 的时间段内收到不少于 K个赞,小明就认为这个帖子曾是”热帖”。具体来说,如果存在某个时刻 T 满足该帖在 [T,T+D)这段时间内(注意是左闭右开区间)收到不少于 K个赞,该帖就曾是”热帖”。

        给定日志,请你帮助小明统计出所有曾是”热帖”的帖子编号。

输入输出格式

输入格式

第一行包含三个整数 N,D,K。

以下 N行每行一条日志,包含两个整数 ts和 id。

输出格式

按从小到大的顺序输出热帖 id。

每个 id占一行。

数据范围

1≤K≤N≤10^5
0≤ts,id≤10^5
1≤D≤10000

输入样例
7 10 2
0 1
0 10
10 10
10 1
9 1
100 3
100 3
输出样例
1
3

题解

用双指针法。

  1. 定义结构体数组logs[N]。可以用pair库来实现。其中,logs[i].first表示ts,logs[i].second表示id。
  2. 对logs数组进行排序。可以用algorthm库中的sort函数进行快速排序。时间复杂度在O(n * log n )之内。
  3. 定义指针int i = 0, j = 0。一开始 i , j 都指向logs[0]。每次循环,j++(j是快指针)。统计[ i , j ]之间每个id的赞数,可以用cnt数组来存。其中cnt[i]表示id为i的帖子在[ i , j ]时间段内收到的点赞数之和。
  4. 一旦发现logs[j].first -logs[i].first >= d(时间跨度大于d了,要扔掉 i 的数值)。让i ++,同时cnt[logs[ i ].second] -- 。
  5. 如果某个id点赞数大于等于k,标记这个id。
  6. 最后遍历所有的id,输出被标记的那些。

代码

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

typedef pair<int, int> PII;

#define x first
#define y second

const int N = 100010;

int n, d, k;
PII logs[N];
bool st[N];
int cnt[N];

int main(){
    scanf("%d%d%d", &n, &d, &k);

    for (int i = 0; i < n; i ++ ) scanf("%d%d", &logs[i].x, &logs[i].y);
    
    sort(logs, logs + n);

   
    for (int i = 0, j = 0; i < n; i ++ ){
        int t = logs[i].y;
        cnt[t] ++;

        while (logs[i].x - logs[j].x >= d){
            cnt[logs[j].y] --;
            j ++;
            
        }
        
        if (cnt[t] >= k) st[t] = true;
    }

    for (int i = 0; i <= 100000; i ++ ) if (st[i]) cout << i << endl;

    return 0;
}

  • 22
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值