双指针思维模式:理解双指针算法的思维模式和设计思路
Posted 吴NDIR
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了双指针思维模式:理解双指针算法的思维模式和设计思路相关的知识,希望对你有一定的参考价值。
博客昵称:吴NDIR
个人座右铭:得之淡然,失之坦然
作者简介:喜欢轻音乐、象棋,爱好算法、刷题
其他推荐内容
计算机导论速记思维导图
五种排序算法
二分查找入门讲解
今天让我们聊一下双指针吧!在一些算法中,使用双指针可以使时间复杂度得到很大的优化。
索引
概念
- 双指针是指在某些问题中,我们需要在数组、字符串或其他数据结构中使用两个指针(通常指向不同位置),以便我们可以同时操作它们,并解决特定的问题。
- 问题:假设我们要在已排序的数组中查找两个数,使它们的和为一个特定的目标值(target)。
小吴去市场买鱼,它应该怎么从已有的稽币中拿出两张付给老板? |
---|
- 从问题中我们可以知道给定的数组[2,4,5,8,20]以及target=13,可能比较直接的思路就是暴力破解
- 暴力破解的思路是遍历每对数,直到找到两个数等于目标和或者已经遍历完所有可能的数对。
- 具体地,可以使用两个嵌套循环,外层循环从数组的第一个元素开始遍历,内层循环从外层循环中的元素的下一个元素开始遍历,直到遍历完整个数组,这样就可以找出两个元素之和等于目标和的情况。
- 在代码实现中,当找到两个元素之和等于目标和的情况时,直接输出这两个元素并返回,否则说明在数组中找不到所需的两个数,则输出“找不到”。
但是,这种暴力解法的时间复杂度为 O ( n 2 ) O(n^2) O(n2),因为需要嵌套两层循环,因此一旦输入的数组很大,算法所需要的时间也会随之增大。因此在处理大规模问题时,这种暴力解法通常不是一个好的选择. |
- 更好的选择是使用优化算法,例如双指针。
- 首先将两个指针指向数组的两端,即一个指针指向数组的第一个元素,另一个指针指向数组的最后一个元素。
- 接着,根据两个指针所指向元素的和与目标和的大小关系来移动指针。如果两个元素的和小于目标和,则将指向较小元素的指针右移,寻求更大的元素。如果两个元素的和大于目标和,则将指向较大元素的指针左移,寻求更小的元素。如果两个元素的和等于目标和,则直接返回这两个元素。
此双指针算法的时间复杂度为 O(n),因为将两个指针移动到最中间的时候问题必定会被解决。和无序数组的暴力解法相比,使用双指针算法可以有效地提高查找效率,适用于较大的数组,并且避免了需要嵌套的循环,提高程序的运算效率。 |
对比:我们可以看到,当数组元素较多时,双指针的优势就显露出来了。 |
代码实现
void find(int arr[], int n, int targetSum)
int left = 0, right = n - 1;
while (left < right)
int sum = arr[left] + arr[right];
if (sum == targetSum)
printf("这两个数分别是 %d 和 %d", arr[left], arr[right]);
return;
else if (sum < targetSum)
left++;
else
right--;
printf("不存在两个数之和等于目标值");
- 示例
int main()
int arr[] = 2, 3, 5, 7, 11;//例子
int n = sizeof(arr) / sizeof(arr[0]);
int target = 10;//目标值
find(arr, n, target);
return 0;
- 结果
引例
- 题目描述:给定一个数组nums,编写一个函数将所有0移动到数组的末尾,同时保持非零元素的相对顺序。
示例1
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
示例2
输入: nums = [0]
输出: [0]
讲解
- 这道题目的思路是使用双指针来遍历整个数组,其中一个指针left表示当前非零元素应该被放置的位置,另一个指针right则用来遍历整个数组。当right指向一个非零元素时,就将它与left指向的位置的元素进行交换,然后left向后移动一位,以便下一个非零元素可以被放置在正确的位置。当right遍历完整个数组时,left的位置右边应全为0。以下是步骤:
- 用两个指针left和right初始化为0;
- 遍历数组,当nums[right]不为0时,就将nums[left]与nums[right]交换,并将left向后移动一位;
- 遍历完整个数组后,数组中所有的0都已经移动到了末尾,完成。
示例代码
//交换对应元素
void swap(int *a, int *b)
int t = *a;
*a = *b, *b = t;
void move(int *nums, int numsSize)
int left = 0, right = 0;
while (right < numsSize)
//当nums[right]不为0时
if (nums[right])
swap(nums + left, nums + right);
left++;
right++;
思维导图整理大厂面试高频数组25: 有序数组的平方的两种双指针思想, 力扣977
此专栏文章是对力扣上算法题目各种方法的总结和归纳, 整理出最重要的思路和知识重点并以思维导图形式呈现, 当然也会加上我对导图的详解.
目的是为了更方便快捷的记忆和回忆算法重点(不用每次都重复看题解), 毕竟算法不是做了一遍就能完全记住的. 所以本文适合已经知道解题思路和方法, 想进一步加强理解和记忆的朋友, 并不适合第一次接触此题的朋友(可以根据题号先去力扣看看官方题解, 然后再看本文内容).
关于本专栏所有题目的目录链接, 刷算法题目的顺序/注意点/技巧, 以及思维导图源文件问题请点击此链接.
想进大厂, 刷算法是必不可少的, 欢迎和博主一起打卡刷力扣算法! 博主同步更新了算法视频讲解, 更易于理解, 不想看文章的 欢迎来看!
关注博主获得题解更新的最新消息!!!
文章目录
0.导图整理
1.直接排序
这是最直观的思路: 将数组 nums 中的数平方后直接排序, 同样因为使用了排序算法, 时间和空间复杂度都非常高.
2.双指针思想
数组原本是有序的, 只是在平方之后就变得无序了, 根本原因就是负数平方之后可能成为最大数了, 那么数组平方的最大值就在数组的两端, 不是最左边就是最右边, 不可能是中间.
这样我们就能确定了平方后最大值的位置, 所以用双指针指向数组的两端, 必定能找到平方后的最大值, 将其放到新数组末尾, 之后不断向中间移动, 通过比较两个指针平方后的大小, 就能不断地将当前的最大值放入数组的尾部, 直到两个指针相遇为止.
这种一种比较简单的双指针实现方式, 另外一种稍微麻烦一点的方式就是 先找到负值和正值的分界点, 相当于找到了平方后的最小值, 然后向两边不断进行遍历, 不过这种方法还多了一层循环来找分界点, 这种思想也要了解一下, 对于之后的题目还是挺有用的.
源码
Python:
# 直接排序
class Solution:
def sortedSquares(self, nums: List[int]) -> List[int]:
return sorted(num * num for num in nums)
# 双指针
class Solution:
def sortedSquares(self, nums: List[int]) -> List[int]:
n = len(nums)
ans = [0] * n
# 从两端开始遍历, 找到平方最大元素放到数组末尾
i, j, pos = 0, n - 1, n - 1
while i <= j:
if nums[i] * nums[i] > nums[j] * nums[j]:
ans[pos] = nums[i] * nums[i]
i += 1
else:
ans[pos] = nums[j] * nums[j]
j -= 1
pos -= 1
return ans
java:
// 直接排序
class Solution
public int[] sortedSquares(int[] nums)
int[] ans = new int[nums.length];
for (int i = 0; i < nums.length; ++i)
ans[i] = nums[i] * nums[i];
Arrays.sort(ans);
return ans;
// 双指针
class Solution
public int[] sortedSquares(int[] nums)
int n = nums.length;
int[] ans = new int[n];
// 从两端开始遍历, 找到平方最大元素放到数组末尾
for (int i = 0, j = n - 1, pos = n - 1; i <= j;)
if (nums[i] * nums[i] > nums[j] * nums[j])
ans[pos] = nums[i] * nums[i];
++i;
else
ans[pos] = nums[j] * nums[j];
--j;
--pos;
return ans;
我的更多精彩文章链接, 欢迎查看
各种电脑/软件/生活/音乐/动漫/电影技巧汇总(你肯定能够找到你需要的使用技巧)
力扣算法刷题 根据思维导图整理笔记快速记忆算法重点内容(欢迎和博主一起打卡刷题哦)
计算机专业知识 思维导图整理
最值得收藏的 Python 全部知识点思维导图整理, 附带常用代码/方法/库/数据结构/常见错误/经典思想(持续更新中)
最值得收藏的 C++ 全部知识点思维导图整理(清华大学郑莉版), 东南大学软件工程初试906科目
最值得收藏的 计算机网络 全部知识点思维导图整理(王道考研), 附带经典5层结构中英对照和框架简介
最值得收藏的 算法分析与设计 全部知识点思维导图整理(北大慕课课程)
最值得收藏的 数据结构 全部知识点思维导图整理(王道考研), 附带经典题型整理
最值得收藏的 人工智能导论 全部知识点思维导图整理(王万良慕课课程)
最值得收藏的 数值分析 全部知识点思维导图整理(东北大学慕课课程)
最值得收藏的 数字图像处理 全部知识点思维导图整理(武汉大学慕课课程)
红黑树 一张导图解决红黑树全部插入和删除问题 包含详细操作原理 情况对比
各种常见排序算法的时间/空间复杂度 是否稳定 算法选取的情况 改进 思维导图整理
人工智能课件 算法分析课件 Python课件 数值分析课件 机器学习课件 图像处理课件
考研相关科目 知识点 思维导图整理
考研经验–东南大学软件学院软件工程(这些基础课和专业课的各种坑和复习技巧你应该知道)
东南大学 软件工程 906 数据结构 C++ 历年真题 思维导图整理
最值得收藏的 考研高等数学 全部知识点思维导图整理(张宇, 汤家凤), 附做题技巧/易错点/知识点整理
最值得收藏的 考研线性代数 全部知识点思维导图整理(张宇, 汤家凤), 附带惯用思维/做题技巧/易错点整理
考研思修 知识点 做题技巧 同类比较 重要会议 1800易错题 思维导图整理
考研近代史 知识点 做题技巧 同类比较 重要会议 1800易错题 思维导图整理
考研马原 知识点 做题技巧 同类比较 重要会议 1800易错题 思维导图整理
考研数学课程笔记 考研英语课程笔记 考研英语单词词根词缀记忆 考研政治课程笔记
Python相关技术 知识点 思维导图整理
Numpy常见用法全部OneNote笔记 全部笔记思维导图整理
Pandas常见用法全部OneNote笔记 全部笔记思维导图整理
Matplotlib常见用法全部OneNote笔记 全部笔记思维导图整理
PyTorch常见用法全部OneNote笔记 全部笔记思维导图整理
Scikit-Learn常见用法全部OneNote笔记 全部笔记思维导图整理
Java相关技术/ssm框架全部笔记
科技相关 小米手机
以上是关于双指针思维模式:理解双指针算法的思维模式和设计思路的主要内容,如果未能解决你的问题,请参考以下文章
思维导图整理大厂面试高频数组25: 有序数组的平方的两种双指针思想, 力扣977
思维导图整理大厂面试高频数组25: 有序数组的平方的两种双指针思想, 力扣977
思维导图整理大厂面试高频数组25: 有序数组的平方的两种双指针思想, 力扣977
思维导图整理大厂面试高频数组24: 合并两个有序数组的两种双指针思想, 力扣88