文章目录

  • 前言
  • 一、有序数组的平方
  • 二、轮转数组
  • 三、移动零
  • 四、两数之和 II - 输入有序数组
  • 五、反转字符串
  • 六、反转字符串中的单词 III
  • 七、链表的中间结点
  • 八、删除链表的倒数第 N 个结点
  • 总结

前言

Leetcode算法系列:https://leetcode-cn.com/study-plan/algorithms/?progress=njjhkd2

简单总结一下双指针相关的算法题:

一、有序数组的平方

题目链接:https://leetcode-cn.com/problems/squares-of-a-sorted-array/

题目描述:给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

思路1:因为有可能存在负数,所以从数组的两端开始计算,因为数组元素平方的最大值不是在开头,就是在末尾,由此可得下面解法:

class Solution {public int[] sortedSquares(int[] nums) {int[] res=new int[nums.length];int start=0,end=nums.length-1;for(int ri=end;ri>=0;ri--){ //从后往前    从两边向中间收缩。int t1=nums[start]*nums[start];int t2=nums[end]*nums[end];if(t1<t2){res[ri]=t2;end--;}else{res[ri]=t1;start++;}}return res;}
}

相反的,如果想从最小值的两边开始扩散则不太容易。因为在这样一个非降序排列的数组中不太好直接找到绝对值最小的那个值。那么可以考虑先对原数组求平方,然后再找最小值。由此得到思路2。

思路2:从最小值开始往两边扩散有一种更好的方法:先对原数组求平方之后,再找到最小值然后扩散即可。可参考算法如下:

    public int[] sortedSquares(int[] nums) {int len=nums.length-1,i;for(i=0;i<=len;i++)nums[i]=nums[i]*nums[i];int minIndex=0;for(i=1;i<=len;i++)if(nums[minIndex]>nums[i])minIndex=i;int[] ans=new int[len+1];i=0;ans[i++]=nums[minIndex];int left=minIndex-1,right=minIndex+1;while(left>=0&&right<=len){if(nums[left]<nums[right]){ans[i++]=nums[left];left--;}else{ans[i++]=nums[right];right++;}    }while(left>=0){ans[i++]=nums[left];left--;}while(right<=len){ans[i++]=nums[right];right++;}return ans;}

这两种做法的时间复杂度和空间复杂度都为O(n)。

二、轮转数组

题目链接:https://leetcode-cn.com/problems/rotate-array/

题目描述:给你一个数组,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。

思路:假设数组长度为 len,k=len 或 =0 时,原数组不会产生变化,所以需要取模来避免不必要的运算。 令 k = k%len 。有一种比较巧妙的解法是进行两次逆序,参考如下:

class Solution {public void rotate(int[] nums, int k) {int len=nums.length,k1=k%len;int tmp;int i=0,j=len-1;while(i<j){    // 逆序(第一次)tmp=nums[i];nums[i]=nums[j];nums[j]=tmp;i++;j--;}for(i=0,j=k1-1;i<j;i++,j--){    //分别逆序(左半部分第二次)tmp=nums[i];nums[i]=nums[j];nums[j]=tmp;}for(i=k1,j=len-1;i<j;i++,j--){   //分别逆序(右半部分第二次)tmp=nums[i];nums[i]=nums[j];nums[j]=tmp;}}
}

时间复杂度为O(n),空间复杂度为O(1)。

思路2:更直观的,如果我们新建一个数组,这道题立马就变得简单,但是空间复杂度也变为O(n)了。

     public void rotate(int[] nums, int k) {int len=nums.length,k1=k%len;               // 注意:k 个元素轮转k次为其本身。if(k1==0)return;int index=len-k1;  //index 为起始下标。int[] tmp=Arrays.copyOf(nums,len);int i,j=0;for(i=index;i<len;i++)nums[j++]=tmp[i];for(i=0;i<index;i++)nums[j++]=tmp[i];}

思路3:想象一下这个数组是一个循环数组(参考循环队列),k 表示所有元素向右移动 k 次,这个时候最终元素的位置就需要对数组长度取模了,经过一丢丢的运算,下标为 index 的数的最终位置为 (index+k)%len,len 为数组长度。参考算法如下(空间复杂度仍为O(n)):

 public void rotate(int[] nums, int k) {int len=nums.length,k1=k%len;int[] tmp=Arrays.copyOf(nums,len);for(int i=0;i<len;i++){nums[(i+k1)%len]=tmp[i];}

思路4:将 3 的空间复杂度优化降低为O(1),缺点是算法会稍微显得有些复杂,参考如下:

 public void rotate(int[] nums, int k) {//注意:不重复的几轮交替转换一定会搞定,一定不会存在重复交换的情况。int len=nums.length,k1=k%len;int count=0;int indexNew,valueOld,valueNew;for(int i=0;count!=len;i++){  // i一定不会增加到len。indexNew=(i+k1)%len;valueOld=nums[i];valueNew=nums[indexNew];while(i!=indexNew){nums[indexNew]=valueOld;valueOld=valueNew;indexNew=(indexNew+k1)%len;valueNew=nums[indexNew];count++;}nums[i]=valueOld;    //最后收尾。count++;}

三、移动零

题目链接:https://leetcode-cn.com/problems/move-zeroes/

题目描述:给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
      请注意 ,必须在不复制数组的情况下原地对数组进行操作。

思路1:从头开始遍历数组,当遇到一个 0 时,将0之后的所有元素均往前移动一位,最后一个数置为0;下一次再遇到 0,依然将此 0 之后的所有元素往前移动一位,也将最后一个数置为0。 算法的时间复杂度为O(n2)。参考算法:

    public void moveZeroes(int[] nums) {int len=nums.length;int i,j,countZero=0;   for(i=0;i<len-countZero;){if(nums[i]==0){countZero++;for(j=i;j<len-1;j++)nums[j]=nums[j+1];nums[j]=0;}elsei++;  }}

思路2:双指针,首先,一个指针 a 标记数组中出现的第一个零,另一个指针 b 标记在 a 之后的第一个非零数,因为可能有连续的 0 存在,所以 b 并不总等于 a+1。
      然后,重复执行 “交换 a 和 b 所指向的值,再令 a++,此时 a 所指向的元素必为0;增加 b 的值,直到它指向下一个非零数(或者指向数组尾,此时算法结束)”。时间复杂度为O(n)。参考算法:

    public void moveZeroes(int[] nums) {int len=nums.length,a=0,b=0;while(a<len&&nums[a]!=0)a++;b=a+1;while(b<len&&nums[b]==0)b++;while(b<len){nums[a]=nums[b];nums[b]=0;a++;while(b<len&&nums[b]==0)b++;} }

思路2 也可以用下面的算法来实现:

    public void moveZeroes(int[] nums) {int indexNow = 0,indexNum = 0,len = nums.length;while(indexNum<m){if(nums[indexNum] != 0) nums[indexNow++] = nums[indexNum];++indexNum;}for(int i = indexNow; i < m; i++)nums[i] = 0;}

四、两数之和 II - 输入有序数组

题目链接:https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted/

题目描述:给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列 ,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] ,则 1 <= index1 < index2 <= numbers.length 。

以长度为 2 的整数数组 [index1, index2] 的形式返回这两个整数的下标 index1 和 index2。

你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。

你所设计的解决方案必须只使用常量级的额外空间。

思路1:二分查找:从第一个数开始,在之后的数中查找另一个数,使得二者之和等于target,存在即返回,否则遍历数组直至结束。时间复杂度为O(nlogn)。参考算法:

    public int[] twoSum(int[] numbers, int target) {for (int i = 0; i < numbers.length; ++i) {int low = i + 1, high = numbers.length - 1;while (low <= high) {int mid = (high - low) / 2 + low;if (numbers[mid] == target - numbers[i]) return new int[]{i + 1, mid + 1};else if (numbers[mid] > target - numbers[i]) high = mid - 1;else low = mid + 1;}}return new int[]{-1, -1};}

思路2:双指针,由于此数组为非递减,令指针 index1 指向数组第一个元素,令指针 index2 指向数组第二个元素,当这两处元素之和大于 target 时,只能减小 index2;小于 target 时,只能增大 index1;等于 target 时,算法结束。时间复杂度为O(n)。参考算法:

    public int[] twoSum(int[] numbers, int target) {int index1=0,index2=numbers.length-1;int tmp;while(index1<index2){tmp=numbers[index1]+numbers[index2];if(tmp==target)return new int[]{index1+1,index2+1};else if(tmp<target)index1++;elseindex2--;}return null;}

五、反转字符串

题目链接:https://leetcode-cn.com/problems/reverse-string/

题目描述:编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。

不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

思路:双指针,首尾元素交换即可。时间复杂度为O(n)。参考算法:

    public void reverseString(char[] s) {int index1=0,index2=s.length-1;char tmp;while(index1<index2){tmp=s[index1];s[index1]=s[index2];s[index2]=tmp;index1++;index2--;}}

六、反转字符串中的单词 III

题目链接:https://leetcode-cn.com/problems/reverse-words-in-a-string-iii/

题目描述:给定一个字符串 s ,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。

思路:双指针,一个指针每次指向字符串中单词的开头,另一个指针指向单词的末尾,反转即可。时间复杂度为O(n),空间复杂度为O(1)。参考算法:

public String reverseWords(String s) {char[] ss=s.toCharArray();int index1=0,index2;int len=ss.length;char tmp;for(int i=0;i<=len;i++){if(i==len||ss[i]==' '){  //将上面的代码稍微优化一下,减少了原来的代码量。index2=i-1;while(index1<index2){tmp=ss[index2];ss[index2]=ss[index1];ss[index1]=tmp;index2--;index1++;}index1=i+1;    }}return new String(ss);
}

七、链表的中间结点

题目链接:https://leetcode-cn.com/problems/middle-of-the-linked-list/

题目描述:给定一个头结点为 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。

思路1:简单的做法,遍历两次,第一次得到链表的长度;第二次返回链表的中间结点。参考算法:

    public ListNode middleNode(ListNode head) {int len=0;ListNode tmp;for(tmp=head;tmp!=null;tmp=tmp.next)len++;int mid=len/2,i=0;for(tmp=head;i<mid;i++)tmp=tmp.next;return tmp;}

思路2:双指针,两个指针之间的关系为:一个指针 index2 一次跑两步,另一个指针 index1 一次跑一步。这里需要注意比如结点总数为 2 或者 3 时,返回的中间节点是相同的。参考算法:

    public ListNode middleNode(ListNode head) {ListNode index1,index2;index1=index2=head;while(index2!=null&&index2.next!=null){ //节点总数为3时,index1仍为总数为2的结果,没有发生改变。index2=index2.next.next;index1=index1.next;}return index1;}

八、删除链表的倒数第 N 个结点

题目链接:https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/

题目描述:给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

思路1:基于上题的经验,直接采用双指针来做,仅需要修改两个指针之间的关系为:保持两个指针之间的距离恒定为 n。这样,当指针 index2 指向链表尾时,index1 为待删除结点的前一个结点,然后进行删除操作。但是还需要考虑删除第一个结点的情况,所以在刚开始时,我创建了一个 dummy 结点,它的 next 指向头节点,这样就方便多了。参考算法:

    public ListNode removeNthFromEnd(ListNode head, int n) {ListNode dummy,index1,index2;dummy=new ListNode(-1,head);//dummy 结点index1=dummy;index2=head;int count=0;while(index2!=null){index2=index2.next;count++;if(count>n)index1=index1.next;}index1.next=index1.next.next;return dummy.next;}

注,不管是通过计算链表长度的方式,还是通过建立辅助栈的解法,都没有双指针来的简洁。


总结

无。

LeetCode算法题5:双指针相关推荐

  1. LeetCode算法题-Valid Palindrome II(Java实现)

    这是悦乐书的第287次更新,第304篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第155题(顺位题号是680).给定非空字符串s,最多可以删除一个字符. 判断它是否是回 ...

  2. LeetCode算法题-K-diff Pairs in an Array(Java实现)

    这是悦乐书的第254次更新,第267篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第121题(顺位题号是532).给定一个整数数组和一个整数k,您需要找到数组中唯一的k- ...

  3. LeetCode算法题整理(200题左右)

    目录 前言 一.树(17) 1.1.后序遍历 1.2.层次遍历 1.3.中序 1.4.前序 二.回溯(20) 2.1.普通回溯 2.2.线性回溯:组合.排列.子集.分割 2.3.矩阵回溯 三.二分查找 ...

  4. LeetCode算法题-Nth Digit(Java实现)

    这是悦乐书的第215次更新,第228篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第83题(顺位题号是400).找到无限整数序列的第n个数字1,2,3,4,5,6,7,8 ...

  5. LeetCode算法题-Reverse Linked List(Java实现)

    这是悦乐书的第192次更新,第195篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第51题(顺位题号是206).反转单链表.例如: 输入:1-> 2-> 3- ...

  6. LeetCode算法题-Convert a Number to Hexadecimal(Java实现)

    这是悦乐书的第219次更新,第231篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第86题(顺位题号是405).给定一个整数,写一个算法将其转换为十六进制.对于负整数,使 ...

  7. leetcode算法题--零钱兑换

    原题链接:https://leetcode-cn.com/problems/coin-change/ 相关题目:leetcode算法题–完全平方数★ 动态规划 dp[i] i从0到amount,dp[ ...

  8. leetcode算法题-- 买卖股票的最佳时机

    原题链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/ 这类股票题目请见leetcode算法题–最佳买卖股票时机含 ...

  9. leetcode算法题--买卖股票的最佳时机 II

    原题链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/ 这类股票题目请见leetcode算法题–最佳买卖股票 ...

  10. leetcode算法题--买卖股票的最佳时机含手续费

    原题链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/ 这类股票题目请见 ...

最新文章

  1. mysql整理类型_Mysql 时间类型整理
  2. 学python最好的方式-最好的Python入门教程是?
  3. 【技术贴】解决 myeclipse打不开报错an error has occurred, see .
  4. 短期目标[Till 2011-08-05]
  5. .Net5发布在即,当心技术断层!
  6. csrf spring_无状态Spring安全性第1部分:无状态CSRF保护
  7. 浮点数在计算机中存储方式float,double)---转
  8. 面试题 03.05. 栈排序
  9. HDU-1087 Super Jumping! Jumping! Jumping!
  10. Linux内核hlist数据结构分析
  11. 全网首发:JDK绘制文字系列博文汇总
  12. CG标准函数库——数学函数(GPU编程与CG语言之阳春白雪下里巴人)
  13. 昆石VOS3000_2.1.2.4安装脚本
  14. 自己做一个table插件 (一)Ajax获取数据后动态生成table
  15. 基于Python的经纬度与xy坐标系相互转换
  16. 工程伦理2021年春季学期线上课程习题全部解答
  17. android x86占比,不输主流安卓机 Android x86系统体验分享(附性能对比)
  18. 信息系统工程工程监理将迎来新的发展机遇
  19. 加州大学河滨分校计算机科学专业,加州大学河滨分校UCR计算机科学Computer Science专业排名第176-200位(2021年THE世界大学商科排名)...
  20. Python基本图形绘制(第二周)turtle风轮绘制

热门文章

  1. PHP中调用SVN命令更新网站方法(解决文件名包含中文更新失败的问题)
  2. wios设置证书登陆
  3. [51nod1678]lyk与gcd问题
  4. theano 安装杂记
  5. 05-数据类型、常量、变量
  6. python 爬虫 ,抓取所有豆瓣好友读的书,列出读过最多的书。(模拟loging豆瓣)...
  7. python ppt pdf linux,带你用Python玩转PPT
  8. 【Git】Git 分支管理 ( 解决分支合并冲突 | 本地处理文件冲突 )
  9. 【Binder 机制】AIDL 分析 ( 创建 Service 服务 | 绑定 Service 远程服务 )
  10. 【Flutter】Dart 面向对象 ( get 方法 | set 方法 | 静态方法 )