【LeetCode热题100】双指针

2024-08-20 19:20
文章标签 leetcode 指针 100 热题

本文主要是介绍【LeetCode热题100】双指针,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

class Solution {
public:void moveZeroes(vector<int>& nums) {int dst = -1,cur = 0;while(cur<nums.size()){if(nums[cur] == 0){cur++;}else{swap(nums[dst+1],nums[cur]);cur++;dst++;}}}
};

题目分析:对于数组分块/数组划分的问题,我们可以使用双指针算法进行求解(这道题的“指针”是数组下标)。我们使用两个“指针”dst和cur,将数组划分为三个区间:[0,dst]已处理过且不为0的区间、[dst+1,cur-1]已处理过且为0的区间、[cur,n-1]待处理的区间。两个指针的作用:cur--从左往右扫描数组,遍历数组;dst--已处理的区间内,非零元素的最后一个位置。cur向后依次遍历,可能会有两种情况:1.cur指向的元素为0,cur++  2.cur指向的元素不为0,swap(nums[dst+1],nums[cur]),cur++,dst++。由于刚开始时没有已处理的区间,所以dst=-1,cur=0。

 

class Solution {
public:void duplicateZeros(vector<int>& arr) {//1.找到要复写的最后一个数int cur = 0,dst = -1;while(cur < arr.size()){if(arr[cur] == 0) dst+=2;else dst++;if(dst >= arr.size()-1){break;}cur++;}//处理一下边界情况:dst加了两下,加过了if(dst == arr.size()){arr[dst-1]=0 ;dst-=2;cur--;}//2.从后向前复写while(cur >= 0){if(arr[cur] == 0){arr[dst]=arr[dst-1]=0;dst -= 2;cur--;}else{arr[dst]=arr[cur];dst--;cur--;}}}
};

 题目分析:这道题是对数组元素进行操作,可以考虑双指针算法。整体思路就是先根据“异地”操作,然后优化成双指针下的“就地”操作。我们首先想到的是另外创建一个数组,进行异地操作,一个指针cur指向原数组,另一个指针dst指向新建数组,cur依次遍历每一个数,按照要求在新建数组进行复写。然而,这样的做法明显不符合“就地操作”的要求,因此,我们需要让cur和dst都指向原数组,如果它们都指向了起始位置,这样遇到复写0之后就会覆盖后面的元素,肯定会出问题。所以我们需要从后往前复写,dst需要指向数组最后一个元素,cur要指向最后一个“复写”的数,因此,我们总共有两步:1.先找到最后一个“复写”的数  2.“从后向前”完成复写操作。经过多次实践,可以通过如下方法找到最后一个复写的数:1)先判断cur位置的值 2)决定dest向后移动一步或者两步 3)判断一下dest是否已经结束 4)cur++  在开始之前,我们需要先让dst指向-1,cur指向0(因为最终dst指向最后一个要复写的位置,刚开始的时候并不知道最后一个位置在哪,因此设为-1),当这个循环退出时,cur指向了最后一个“复写”的数。此外,在找最后一个要复写的数的过程中,我们还可能因为cur最后一次指向0,dst需要加两次,跳到了arr的外面,即下标为n的位置,对于这种情况,我们需要单独处理一下,让arr[n-1]=0,然后dst回退两次,cur--即可。

class Solution {
public:int bitSum(int n){int sum = 0;while(n){int lastbit = n%10;sum += lastbit*lastbit;n /= 10;}return sum;}bool isHappy(int n) { int slow = n, fast = bitSum(n);while(slow != fast){slow = bitSum(slow);fast = bitSum(bitSum(fast));}return slow == 1;}
};

题目分析:给定一个正整数,按照题目给定的过程重复计算,会有两种结果:其一是这个数在某一次变成1,那么之后会一直循环1;第二种是这个数会在某个圈里无限循环,但是不是1。因此,一定会进入循环(鸽巢原理),我们可以通过判断当进入循环后,这个值是不是1来确定这个数是不是快乐数。

那么如何找到进入循环的某一个数呢?这个就需要用到快慢指针的思想:

(一)定义快慢指针

(二)慢指针每次向后移动一步,快指针每次向后移动两步

(三)判断相遇时候的值即可

class Solution {
public:int maxArea(vector<int>& height) { int max= 0;int i =0,j=height.size()-1;int v = 0;//体积while( i < j){v = (j - i) * (height[i]<height[j]?height[i]:height[j]);if(v > max) max = v;if(height[i] < height[j]) i++;else j--;}return max;}
};

题目分析:第一种方法就是暴力求解,遍历每一个可能得组合,计算出它们的体积的最大值,但是这种方法是运行超时的,因此我们还要另辟蹊径。

第二种方法比较巧妙,我们先来看一个简单例子,如果数组是[6,2,5,4],那我们先计算最外面两个数的组合:6和4,宽度是3,高度是max(6,4)=4,体积=3*4=12,我们再往里面深入,如果是4和2组合,首先它们的宽度变成2(往里深入宽度必定减少),高度变成2(高度必定是≤4,),很显然,宽度必定减少,高度最多保持不变,那么4和往里面的树组合体积必定减少,因此就不用考虑4和里面任何数的组合,所以直接忽略4(两侧--6和4中较小的那一个值),数组变成[6,2,5],然后继续依照上面逻辑,先计算出最外侧两个数的体积,和上一个体积比较,最后直到整个数组被遍历完!

class Solution {
public:int triangleNumber(vector<int>& nums) {//1.优化sort(nums.begin(),nums.end());int size = nums.size();int fixi = size-1;int left = 0;int right = fixi-1;int ret = 0;while(fixi > 1){while(left < right){if(nums[left] + nums[right] > nums[fixi]){ret += right - left;right--;}else{left++;}}fixi--;left = 0;right = fixi-1;}return ret;}};

题目分析:如果要判断的三个数是有序的(a  <  b  <  c),那么只需要判断一次即可,就是判断 a+b是否大于c,因为b+c肯定大于a,a+c也肯定大于b。

这道题有两种解法,一种是暴力解法,遍历这个数组得到三个数,依次判断是否符合,这种解法的时间复杂度很高。另外一种解法,也是利用双指针的思想,比如给出的数组是[2,3,5,5,7,8,9,12],先固定一个最大的c,也就是最后的12,然后让left指向剩下的最小的,即2,再让right指向剩下最大的,即9,判断left+right和固定最大值c=12的大小关系,那么就有两种情况:

        1.left+right>c,那么right和left与right中间的任意一个组合都符合条件,不用继续遍历中间的,符合的组数为right-left,再让right--;

        2.left+right<=c,那么,left和left与right中间任意一个组合都不符合条件,left++;

当left=right后,这一回合遍历结束,再让固定的最大值c向前调整一个,然后继续上述步骤即可。

class Solution {
public:vector<int> twoSum(vector<int>& price, int target){vector<int> ret;int size = price.size();int left = 0;int right = size - 1;while(left < right){if(price[left] + price[right] < target){left++;}else if(price[left] + price[right] > target){right--;}else{return {price[left],price[right]};//隐式类型转换}}//虽然我们知道这条语句不会被执行,但是为了照顾编译器,//需要在每条返回路径上都返回return {1,1};}
};

题目分析:和有效三角形的个数的思想非常类似,也是用到双指针的思想。刚开始的时候,左指针left指向数组最左,右指针right指向数组最右,整个数组是单调递增的,先判断left+right和target的关系,分为三种情况:

        1.left+right > target,那么right和left与right之间的任意数之和都大于target,就没有必要继续遍历了,让right--;

        2.left+right < target,那么left和left与right之间的任意数之和都小于target,也没有必要继续遍历了,让left++;

        3.left+right > target,那么直接返回即可,走隐式类型转换。

需要注意的是,需要在循环外加一个return语句,这样编译器才不会报错,认为每条路径都会有返回值。

class Solution {
public:vector<vector<int>> threeSum(vector<int>& nums) {sort(nums.begin(),nums.end());int size = nums.size();vector<vector<int>> vv;for(int i = 0; i < size - 2; ){if(nums[i] > 0) break;int left = i + 1;int right = size - 1;while(left < right){if(-1 * nums[i] < nums[left] + nums[right]){right--;}else if(-1 * nums[i] > nums[left] + nums[right]){left++;}else{vv.push_back({nums[i],nums[left],nums[right]});left++;right--; //去重操作 left rightwhile(left < right && nums[left] == nums[left-1]) left++;while(left < right && nums[right] == nums[right+1]) right--;}}i++;//去重iwhile(i < size && nums[i] == nums[i-1]){i++;}          }return vv;}
};

题目分析:这道题依然是有两种解法:暴力解法和优化解法,

暴力解法:先对数组进行排序,然后对数组中的每三个数进行遍历,当找到三个数的符合条件后,将这三个数的组合插入set中,这样方便去重,但是暴力解法的时间复杂度是O(N3)。

优化解法:先对数组进行排序,然后固定一个数a(从最左侧开始固定),在该数后面的区间内,利用上题“双指针”算法,快速找到这个区间内和为-a的两个数。

处理细节

1.去重:找到一种结果之后,left和right指针要跳过重复元素,直到找到下一个不重复的元素,当使用完一次双指针算法后,a也要跳过重复元素。

2.不漏:找到一种结果之后,不要“停”,缩小区间,继续寻找

3.小优化:只有a≤0时,才有可能在后面的区间找到两个数之和=-a,因此a只需要遍历非正值即可。

class Solution {
public:vector<vector<int>> fourSum(vector<int>& nums, int target) {//1.排序sort(nums.begin(),nums.end());//2.按“双指针”查找vector<vector<int>> ret;int n = nums.size();for(int i = 0;i<n;)//固定a{for(int j=i+1;j<n;)//固定b{long long val = (long long)target - nums[i] - nums[j];int left = j+1;int right = n-1;while(left < right){int sum = nums[left] + nums[right];if(val > sum){left++;}else if(val < sum){right--;}else{ret.push_back({nums[i],nums[j],nums[left],nums[right]});left++;right--;//去重1while(left < right && nums[left] == nums[left-1]) left++;while(left < right && nums[right] == nums[right+1]) right--;}          }//去重2j++;while(j < n && nums[j-1] == nums[j]) j++;}//去重3i++;while(i < n && nums[i-1] == nums[i]) i++;}return ret;}
};

题目分析:这道题会借用上面三数之和的思想,首先还是对数组进行排序,然后先固定最左边第一个数a,然后从后面区间找出和为target-a的三个数,然后就是借助三数之和的思想,先固定剩下区间的第一个数b,再从剩下区间找和为target-a-b的两个数,然后继续遍历,要和三数之和一样要做到不重不漏。

这篇关于【LeetCode热题100】双指针的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1090960

相关文章

哈希leetcode-1

目录 1前言 2.例题  2.1两数之和 2.2判断是否互为字符重排 2.3存在重复元素1 2.4存在重复元素2 2.5字母异位词分组 1前言 哈希表主要是适合于快速查找某个元素(O(1)) 当我们要频繁的查找某个元素,第一哈希表O(1),第二,二分O(log n) 一般可以分为语言自带的容器哈希和用数组模拟的简易哈希。 最简单的比如数组模拟字符存储,只要开26个c

leetcode-24Swap Nodes in Pairs

带头结点。 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) { val = x; }* }*/public class Solution {public ListNode swapPairs(L

leetcode-23Merge k Sorted Lists

带头结点。 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) { val = x; }* }*/public class Solution {public ListNode mergeKLists

C++ | Leetcode C++题解之第393题UTF-8编码验证

题目: 题解: class Solution {public:static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num &

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟)

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟) 题目描述 给定一个链表,链表中的每个节点代表一个整数。链表中的整数由 0 分隔开,表示不同的区间。链表的开始和结束节点的值都为 0。任务是将每两个相邻的 0 之间的所有节点合并成一个节点,新节点的值为原区间内所有节点值的和。合并后,需要移除所有的 0,并返回修改后的链表头节点。 思路分析 初始化:创建一个虚拟头节点

C语言 | Leetcode C语言题解之第393题UTF-8编码验证

题目: 题解: static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num & MASK1) == 0) {return

【C++学习笔记 20】C++中的智能指针

智能指针的功能 在上一篇笔记提到了在栈和堆上创建变量的区别,使用new关键字创建变量时,需要搭配delete关键字销毁变量。而智能指针的作用就是调用new分配内存时,不必自己去调用delete,甚至不用调用new。 智能指针实际上就是对原始指针的包装。 unique_ptr 最简单的智能指针,是一种作用域指针,意思是当指针超出该作用域时,会自动调用delete。它名为unique的原因是这个

C语言指针入门 《C语言非常道》

C语言指针入门 《C语言非常道》 作为一个程序员,我接触 C 语言有十年了。有的朋友让我推荐 C 语言的参考书,我不敢乱推荐,尤其是国内作者写的书,往往七拼八凑,漏洞百出。 但是,李忠老师的《C语言非常道》值得一读。对了,李老师有个官网,网址是: 李忠老师官网 最棒的是,有配套的教学视频,可以试看。 试看点这里 接下来言归正传,讲解指针。以下内容很多都参考了李忠老师的《C语言非

【JavaScript】LeetCode:16-20

文章目录 16 无重复字符的最长字串17 找到字符串中所有字母异位词18 和为K的子数组19 滑动窗口最大值20 最小覆盖字串 16 无重复字符的最长字串 滑动窗口 + 哈希表这里用哈希集合Set()实现。左指针i,右指针j,从头遍历数组,若j指针指向的元素不在set中,则加入该元素,否则更新结果res,删除集合中i指针指向的元素,进入下一轮循环。 /*** @param

LeetCode:64. 最大正方形 动态规划 时间复杂度O(nm)

64. 最大正方形 题目链接 题目描述 给定一个由 0 和 1 组成的二维矩阵,找出只包含 1 的最大正方形,并返回其面积。 示例1: 输入: 1 0 1 0 01 0 1 1 11 1 1 1 11 0 0 1 0输出: 4 示例2: 输入: 0 1 1 0 01 1 1 1 11 1 1 1 11 1 1 1 1输出: 9 解题思路 这道题的思路是使用动态规划