玩转双指针

一、算法简介

双指针主要用于遍历数组,两个指针指向不同的元素,从而协同完成任务。也可以延伸到多
个数组的多个指针。

若两个指针指向同一数组,遍历方向相同且不会相交,则也称为滑动窗口(两个指针包围的
区域即为当前的窗口),经常用于区间搜索

若两个指针指向同一数组,但是遍历方向相反,则可以用来进行搜索,待搜索的数组往往是排好序的。

二、指针小知识

对于C++ 语言,指针还可以玩出很多新的花样。一些常见的关于指针的操作如下

(1)指针与常量

简而言之,就是const修饰谁,谁就是常量,这里把“*”(星号),理解为独立的字符——指针,就比较好理解了

i. const int * p2, 为常量指针(先读const——常量,再读*——指针),即指向一个常量(const int)的指针。

ii. int * const p3,为指针常量(先读*——指针,再const——常量),指针本身是常量,指向的地址不可以变化,但是指向的地址所对应的内容(值)可以变化。

(2)指针函数与函数指针

#include <iostream>

// addition是指针函数,一个返回类型是指针的函数
int* addition(int a, int b) {
int* sum = new int(a + b);  // 注意,在函数内new出来的,出函数是不会自动销毁的,一定要在某处通过delete手动销毁。
return sum;
}

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

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

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


int main()
{
    int* m = addition(1, 2);  // 1 + 2 = 3
    int n = operation(3, *m, minus);  // 3 -3 = 0
    std::cout << *m << std::endl;
    std::cout << n << std::endl;
    return 0;
}

 输出结果为:

三、例题

Leetcode 1. 两数之和

因为数组已经排好序,我们可以采用方向相反的双指针来寻找这两个数字,一个初始指向最
小的元素,即数组最左边,向右遍历;一个初始指向最大的元素,即数组最右边,向左遍历。

Leetcode 88. 合并两个有序数组

Leetcode 142. 环形链表 II

对于链表找环路的问题,有一个通用的解法——快慢指针(Floyd 判圈法,其有数学证明)。

给定两个指针,分别命名为slow 和fast,起始位置在链表的开头。(步骤1) 每次fast 前进两步,slow 前进一步。
如果fast可以走到尽头,那么说明没有环路;如果fast 可以无限走下去,那么说明一定有环路,且一定存
在一个时刻slow 和fast 相遇。(步骤2)当slow 和fast 第一次相遇时,我们将fast 重新移动到链表开头,并让slow 和fast 每次都前进一步。(步骤3)当slow 和fast 第二次相遇时,相遇的节点即为环路的开始点。

该算法可以:

  • 判断链表是否有环
  • 计算环的长度
  • 寻找环的起点
/**
 * 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 *fast = head, *slow = head;
        // 步骤1
        do  
        {
            if (!fast || !fast->next) return nullptr;
            fast = fast->next->next;
            slow = slow->next;
        } while (fast != slow);
        // 步骤2
        fast = head;
        while (fast != slow)
        {
            fast = fast->next;
            slow = slow->next;
        }
        // 步骤3
        return fast;
    }
};

### Java 数据结构使用教程 #### 一、数据结构概述 数据结构是在计算机科学中用于组织和存储数据的一种特定形式的方法。通过合理的选择和设计数据结构,可以提高程序运行效率并优化资源利用[^1]。 #### 二、常见数据结构及其Java实现 以下是几种常见的数据结构以及它们在Java中的基本用法: ##### 1. 数组 (Array) 数组是一种线性数据结构,在内存中连续分配空间来保存相同类型的多个元素。 ```java int[] array = new int[5]; // 创建长度为5的整型数组 array[0] = 10; // 赋值给第一个位置 System.out.println(array[0]); // 输出第一个位置的值 ``` ##### 2. 链表 (Linked List) 链表由一系列节点组成,每个节点包含数据部分和指向下一个节点的指针。Java 中 `LinkedList` 是一种双向链表实现。 ```java import java.util.LinkedList; public class Main { public static void main(String[] args) { LinkedList<String> linkedList = new LinkedList<>(); linkedList.add("A"); // 添加元素到列表末尾 linkedList.addFirst("B"); // 插入头部 System.out.println(linkedList); } } ``` ##### 3. 栈 (Stack) 栈是一个后进先出(LIFO)的数据结构。可以通过继承 `Vector` 类或者使用 `Deque` 接口实现栈功能。 ```java import java.util.Stack; public class Main { public static void main(String[] args) { Stack<Integer> stack = new Stack<>(); stack.push(1); // 压入元素 stack.push(2); System.out.println(stack.pop()); // 弹出顶部元素 } } ``` ##### 4. 队列 (Queue) 队列遵循先进先出(FIFO)原则。Java 提供了多种队列接口和类支持,如 `PriorityQueue`, `LinkedList` 实现等。 ```java import java.util.Queue; import java.util.LinkedList; public class Main { public static void main(String[] args) { Queue<Integer> queue = new LinkedList<>(); queue.offer(1); // 尾部插入 queue.offer(2); System.out.println(queue.peek()); // 查看队首元素而不移除 System.out.println(queue.poll()); // 移除队首元素 } } ``` 上述代码展示了如何向队列添加元素、查看队首元素及移除队首元素的操作[^3]。 ##### 5. 列表 (List) 列表允许动态调整大小,并提供了丰富的操作方法。最常用的实现有 `ArrayList` 和 `LinkedList`。 ```java import java.util.ArrayList; public class Main { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); list.add("Apple"); list.remove(0); // 删除指定索引处的元素 System.out.println(list.size()); // 获取当前列表大小 } } ``` 这里介绍了创建空集合对象以及一些常用方法的应用场景[^2]。 ##### 6. 映射 (Map) 映射是以键值对的形式存储数据,其中键唯一而值可重复。主要实现包括 `HashMap`, `TreeMap` 等。 ```java import java.util.HashMap; public class Main { public static void main(String[] args) { HashMap<String, Integer> map = new HashMap<>(); map.put("Alice", 25); // 存储键值对 System.out.println(map.get("Alice")); // 根据键获取对应的值 } } ``` #### 总结 以上列举了几种典型的数据结构及其在Java环境下的具体应用实例。每种数据结构都有其适用范围与特点,开发者应根据实际需求选取合适的方案加以运用。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值