IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 9. 双指针技巧 -> 正文阅读

[数据结构与算法]9. 双指针技巧

一,什么是双指针

????????双指针大致可分为两类,一类是 "快慢指针",一类是 "左右指针"。
????????"快慢指针" --> 解决链表中的问题 (比如,寻找链表中点,判断链表是否有环等)
????????"左右指针" --> 解决数组中的问题 (比如,二分搜索,有序数组去重,滑动窗口等)

二,快慢指针

????????快慢指针初始化时都指向链表的表头,快指针(fast)每次走 2 步,慢指针(slow)每次走 1 步。
????????因为每次 fast 都比 slow 多走 1 步,当达到停止条件时, fast 所走过的节点数是 slow 的两倍。
????????对于停止条件:
????????????????若是无环链表,fast 走到 nullptr 时即停止,此时 fast 走完了整个链表,故 slow 停在了链表的中点。
????????????????若是有环链表,fastslow 最终会相遇,而相遇时 fastslow 正好多走了环的整数圈。

??????? a, 判断链表是否有环
????????b, 若有环,找到环的起点
????????c, 若无环,找到链表的中点
????????d, c 的升级版,找到无环链表的倒数第 k 个节点

a, 判断链表中是否有环
    判断的依据在于,若是无环,则 fast 一定会走到链表尾部; 若是有环,则 fast 和 slow 一定会相遇。
		
bool hasCycle(Node* head) {
    Node* fast = head;
    Node* slow = head;
			
    while (fast && fast->next) {
        slow = slow->next;
        fast = fast->next->next;
				
        if (slow == fast) {    // 若相遇,则说明该链表有环
            return true;
        }
    }
    return false;
}
b, 寻找有环链表中环的起点
    有环链表,环的起点是 3 。
	1 -> 2 -> 3 -> 4 -> 5
	      ^ 	   |
	      |	       v
	      8 -> 7 -> 6
		
    先看代码
		
Node* findStartNode(Node* head) {
    Node* fast = head;
    Node* slow = head;
			
    while (fast && fast->next) {
	    slow = slow->next;
	    fast = fast->next->next;
				
	    if (slow == fast) {    // 先找到二者相遇的节点
	        break;
	    }
    }
			
    // 此时 slow 与 fast 两个节点相遇了
    slow = head;		    // 让 slow 再指向 head
    while (slow != fast) {	// 让两个指针以相同的速度前进,再次相遇时便是环的起点
        slow = slow->next;
	    fast = fast->next;
    }
    return slow;
}
		
    第一部分代码无需解释,就是两者第一次相遇。
		
    需要理解的是,为何第一次相遇后,让其中一个指针从头开始与另外一个指针以相同速度前进,二者再次相遇时便是环的起点?
    假设第一次相遇时, slow 走了 k 个节点,那么 fast 一定走了 2k 个节点。也就是说 fast 比 slow 多走了 k 步(环长度的整数倍)。
    我们再假设,从 head 走到环的起点需要 m 步。那么第一相遇时, slow 已经在环里走了 k-m 步。(同理,fast 也相当于从环的起点走了 k-m 步)
    第一次相遇后,我们让 slow 指向 head,然后让 slow 和 fast 以相同速度继续走,会发现,当走 m 步后, slow 会走到环的起点,而 fast 也正好走到环的起点。(fast : k-m+m = k)
    所以它们第二次相遇的地点便是环的起点。
c, 寻找无环单链表的中点
    这个就比较简单了,直接让 fast 走到 nullptr,此时 slow 所在的节点便是链表的中点
    需要注意的是,链表节点为奇数或偶数时的不同情况。
    奇数:	1 -> 2 -> 3 -> 4 -> 5 -> nullptr	slow : 3 , fast : 5
    偶数:	1 -> 2 -> 3 -> 4 -> nullptr		slow : 3 , fast : nullptr
    寻找中点的作用 : 在链表的归并排序中需要用到

Node* getMiddleNode(Node* head) {
    Node* fast = head;
    Node* slow = head;
		
    while (fast && fast->next) {
	    fast = fast->next->next;
	    slow = slow->next;
    }
    return slow;
}
d, 寻找单链表的倒数第 k 个元素
    这个与寻找链表的中点是一样的,先让 fast 指针先跑 k 步,然后让 slow 和 fast 以同样速度跑。
    等 fast 跑到尾部时, slow 就处于倒数第 k 个元素了。(我们假设链表的长度大于 k)
		
    Node* getNode(Node* head, int k) {
	Node* fast = head;
	Node* slow = head;
			
	while (k-- && fast) {
	    fast = fast->next;
	}
			
	while (fast) {
	    slow = slow->next;
	    fast = fast->next;
	}
			
	return slow;
}

三,左右指针

????????左右指针一般运用在数组问题中,实际是两个索引值,一般初始化为 left = 0, right = nums.size() - 1;

????????a, 二分查找 (见 : 10. 二分搜索算法)
????????b, 有序数组去重 (leetcode 链接 : 26. Remove Duplicates from Sorted Array)
??????? c, 反转数组
??????? d, 滑动窗口 (见 : 11. 滑动窗口)

a, 二分查找
    在升序的数组中找到目标值,并返回其下标,若无,则返回 -1。
		
    int binarySearch(vector<int>& nums, int target) {
	    int left = 0, right = nums.size() - 1;
	    while (left <= right) {		// 注意,此处是 <=
	        int mid = left + (right - left) >> 1; // 注意, mid = (left + right) / 2; 可能导致越界
			
	        if (nums[mid] == target) {
		        return mid;
	        }
	        else if (nums[mid] < target) {
		        left = mid + 1;
	        }
	        else {
		        right = mid - 1;
	        }
	    }
	    return -1;
    }
b, 有序数组去重
    给定一个有序的数组,其中可能存在连续相同的数字,要求原地删除重复的元素并返回删除后数组的长度。(注,空间复杂度要求 O(1))
    比如 , input : [1, 2, 2, 3, 4, 4, 6, 8]
	      output : [1, 2, 3, 4, 6, 8]
	      return : 6
		
    int removeDuplicates(vector<int>& nums) {
        int left = 0, right = 0;
        int size = nums.size();
	
        if (size) {
	        while (right < size) {
	            if (nums[right] != nums[left]) {
		            nums[++left] = nums[right];
	            }
	            right++;
	        }
	        left++;		// 最后返回的是长度,故需要再 +1
        }
        return left;	// 若 size == 0, 则直接返回 0
    }
c, 反转数组
    将数组内的元素反转
	
    void reverse(vector<int>& nums) {
	    int left = 0, right = nums.size() - 1;
	    while (left < right) {
	        int temp = nums[left];
	        nums[left] = nums[right];
	        nums[right] = temp;
			
	        left++;	right--;
	    }
    }
d, 滑动窗口
    可以解决一大类字符串匹配的算法。

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-08-25 12:27:44  更:2021-08-25 12:29:51 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 23:19:34-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码