【剑指offer】双指针7题 全刷(详解)

目录

目录

目录

全部题目链接地址

[简单]剑指 Offer 18. 删除链表的节点

题目

方法

[简单]剑指 Offer 22. 链表中倒数第k个节点

题目

方法:双指针距离法

 [简单]剑指 Offer 25. 合并两个排序的链表

题目

方法:双指针

[简单]剑指 Offer 52. 两个链表的第一个公共节点

题目

方法1:栈(不华丽) 

方法2:双指针浪漫相遇

[简单]剑指 Offer 21. 调整数组顺序使奇数位于偶数前面

题目

方法1:双指针

 方法2:双指针优化

 [简单]剑指 Offer 57. 和为s的两个数字

题目 

方法1:双指针-嵌套for循环

 方法2:双指针-先相加再比较

[简单]剑指 Offer 58 - I. 翻转单词顺序 

题目

 方法1:双指针栈

 方法2:集合翻转法


全部题目链接地址

剑指 Offer - 学习计划 - 力扣(LeetCode)全球极客挚爱的技术成长平台

[简单]剑指 Offer 18. 删除链表的节点

题目

定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。

返回删除后的链表的头节点。

注意:此题对比原题有改动

题目地址:剑指 Offer 18. 删除链表的节点 - 力扣(LeetCode) 

方法

思路

删除一个节点其实就是让这个节点的上一个节点 指向 这个节点的下一个节点即可

代码

/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode(int x) { val = x; }* }*/
class Solution {public ListNode deleteNode(ListNode head, int val) {ListNode p = head;if(p.val == val)return head.next;while(p.next != null){if(p.next.val == val){p.next = p.next.next;return head;}p = p.next;}return head;}
}

 效果

[简单]剑指 Offer 22. 链表中倒数第k个节点

题目

输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。

例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。

题目地址:剑指 Offer 22. 链表中倒数第k个节点 - 力扣(LeetCode)  

方法:双指针距离法

思路

定义双指针,一左一右,左右的距离为k,也就是从左指针数到右指针数k个节点,让这两个指针一起往后走,右指针走到底就返回左指针即可。

例如

 

 

 

 代码

class Solution {public ListNode getKthFromEnd(ListNode head, int k) {ListNode left = head;ListNode right = head;while (k > 1){right = right.next;k--;}while (right.next!=null){right = right.next;left = left.next;}return left;}
}

效果

 [简单]剑指 Offer 25. 合并两个排序的链表

题目

入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。

示例1:

输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

本题与主站 21 题相同:力扣

题目地址:剑指 Offer 25. 合并两个排序的链表 - 力扣(LeetCode) 

方法:双指针

思路

简单的思路,就是两个指针分别指向两个链表,哪个的val小就取哪个。

class Solution {public ListNode mergeTwoLists(ListNode l1, ListNode l2) {ListNode p1 = l1;ListNode p2 = l2;ListNode res = new ListNode(-1);ListNode p = res;while (p1!=null && p2!=null){if (p1.val <= p2.val){p.next = p1;p1 = p1.next;}else {p.next = p2;p2 = p2.next;}p = p.next;}if (p1 == null){p.next = p2;}else if (p2 == null){p.next = p1;}return res.next;}
}

效果

[简单]剑指 Offer 52. 两个链表的第一个公共节点

题目

输入两个链表,找出它们的第一个公共节点。

如下面的两个链表

在节点 c1 开始相交。

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Reference of the node with value = 2
输入解释:相交节点的值为 2 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
输入解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
解释:这两个链表不相交,因此返回 null。

题目地址:剑指 Offer 52. 两个链表的第一个公共节点 - 力扣(LeetCode)

与另一题相同:160. 相交链表 - 力扣(LeetCode) 

方法1:栈(不华丽) 

思路

思路很简单,将两个链表分别入栈,然后出栈去比较栈顶元素,有交点的两个链表他们入栈之后的栈顶肯定是相同的,那么交点就在第一个不相同的栈顶元素的next。

代码

class Solution {ListNode getIntersectionNode(ListNode headA, ListNode headB) {if (headA==null || headB==null)return null;Stack<ListNode> stackA = new Stack<>();Stack<ListNode> stackB = new Stack<>();ListNode p1 = headA;ListNode p2 = headB;while (p1!=null){stackA.add(p1);p1 = p1.next;}while (p2!=null){stackB.add(p2);p2 = p2.next;}//最后两个节点不相同,那就是没有相交的节点if (stackA.peek()!= stackB.peek() )return null;while (!stackA.isEmpty() && !stackB.isEmpty()){if (stackA.peek()==stackB.peek()){stackA.pop();stackB.pop();continue;}else{return stackA.peek().next;}}//既然代码走到了这里,说明不是没有交点// 而是这两个栈的其中一个已经空了,那么没空的那个栈的栈顶元素.next就是交点//或者两个栈都空了if (stackA.isEmpty() && !stackB.isEmpty()){return stackB.peek().next;}else if (!stackA.isEmpty() && stackB.isEmpty()){return stackA.peek().next;}else {return headA;}}}

 这是我自己想出来的方法,过肯定能过,但是不算一个好方法。

方法2:双指针浪漫相遇

思路来自题解

剑指 Offer 52. 两个链表的第一个公共节点 - 力扣(LeetCode)

思路

思路很简单,定义两个指针分别指向两个链表的头结点,然后两个指针同步往后走,每次都比较看是否相同,如果某个指针走完了自己的链表,那就开始走另一个链表,这样总能相遇,相遇的那个节点就是相交节点。

例如:没有交点的情况,最后都会走到null,null==null,所以就返回了null,正确

再来看有交点的情况

 如果有交点且两个链表长度相同,则两指正同步走直接就能比对出来

代码

class Solution {ListNode getIntersectionNode(ListNode headA, ListNode headB) {if (headA==null||headB==null)return null;ListNode p1 = headA;ListNode p2 = headB;while (true){if (p1 == p2)return p1;if (p1 == null){p1 = headB;continue;}if (p2 == null){p2 = headA;continue;}p1 = p1.next;p2 = p2.next;}}}

 效果就比较好

[简单]剑指 Offer 21. 调整数组顺序使奇数位于偶数前面

题目

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数在数组的前半部分,所有偶数在数组的后半部分。

示例:

输入:nums = [1,2,3,4]
输出:[1,3,2,4] 
注:[3,1,2,4] 也是正确的答案之一。

剑指 Offer 21. 调整数组顺序使奇数位于偶数前面 - 力扣(LeetCode)

方法1:双指针

思路

两个指针,一左一右,左边找到偶数,右边找到奇数,调换位置即可

代码

class Solution {public int[] exchange(int[] nums) {int len = nums.length;if (len == 0)return nums;int right = len-1;int left = 0;while (left < right){//左边找到偶数if (isOddNumber(nums[left])){left++;continue;}//右边找到奇数if (!isOddNumber(nums[right])){right--;continue;}swap(nums,left,right);left++;right--;}return nums;}private void swap(int[] nums, int i, int p) {int index = nums[i];nums[i] = nums[p];nums[p] = index;}/*** 是奇数*/boolean isOddNumber (int num){if (num % 2 == 0){return false;}return true;}}

效果

 方法2:双指针优化

可以看到方法1不是很优秀

我们优化一下代码,思路是一样的

优化:

1. 将两个if 换成 while

2. 将 %2 替换成  &1 ,效果一样,但是后者效率更高

class Solution {public int[] exchange(int[] nums) {int len = nums.length;if (len == 0)return nums;int right = len-1;int left = 0;while (left < right){//左边找到偶数while (left<right && (nums[left] & 1) == 1){left++;}//右边找到奇数while (left<right  && (nums[right] & 1) == 0){right--;}//    //左边找到偶数//    while (left<right && nums[left] % 2 == 1){//        left++;//    }//    //右边找到奇数//    while (left<right  && nums[right] % 2 == 0){//        right--;//    }swap(nums,left,right);left++;right--;}return nums;}private void swap(int[] nums, int i, int p) {int index = nums[i];nums[i] = nums[p];nums[p] = index;}}

效果

 [简单]剑指 Offer 57. 和为s的两个数字

题目 

输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[2,7] 或者 [7,2]

示例 2:

输入:nums = [10,26,30,31,47,60], target = 40
输出:[10,30] 或者 [30,10]

剑指 Offer 57. 和为s的两个数字 - 力扣(LeetCode)

方法1:双指针-嵌套for循环

思路

从后往前找,如果 target>数>=target/2 才能做两个数中大的那个 

找到大的数,开始往前循环找小的数

代码

class Solution {//从后往前找,如果 target>数>=target/2 才能做两个数中大的那个//找到大的数,开始往前循环找小的数public int[] twoSum(int[] nums, int target) {int arr[] = new int[2];for (int i = nums.length-1; i >-1 ; i--) {if (nums[i] < target && nums[i]>=target/2){for (int j = i; j > -1 ; j--) {if (nums[j] ==target - nums[i]){arr[0] = nums[j];arr[1] = nums[i];return arr;}}}}return arr;}}

效果

 方法2:双指针-先相加再比较

思路

作者:Krahets
链接:https://leetcode.cn/problems/he-wei-sde-liang-ge-shu-zi-lcof/solutions/164083/mian-shi-ti-57-he-wei-s-de-liang-ge-shu-zi-shuang-/

方法1是,先把一个数和target比较,再和另一个数加起来再比对

这样的话需要比较两次,而且是两层循环

方法2是,先加起来,再比较,而且是一层循环

算法流程

 代码

class Solution {public int[] twoSum(int[] nums, int target) {int i = 0, j = nums.length - 1;while(i < j) {int s = nums[i] + nums[j];if(s < target) i++;else if(s > target) j--;else return new int[] { nums[i], nums[j] };}return new int[0];}
}

效果

[简单]剑指 Offer 58 - I. 翻转单词顺序 

题目

输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串"I am a student. ",则输出"student. a am I"。

示例 1:

输入: "the sky is blue"
输出: "blue is sky the"

示例 2:

输入: "  hello world!  "
输出: "world! hello"
解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。

示例 3:

输入: "a good   example"
输出: "example good a"
解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。

剑指 Offer 58 - I. 翻转单词顺序 - 力扣(LeetCode)

 方法1:双指针栈

思路

因为要颠倒顺序输出,所以我想到用栈,因为栈是先入后出。

然后用双指针分别指向一个单词的前后,放到栈里面就行了,最后再从栈里取出来。

代码流程

1.从左往右遍历字符串,找到非空格字符,用指针 l 记录位置

2.从 l 开始遍历寻找一个单词的最后一个字符,找到后,用指针 r 记录位置

3.将 [ l , r ]的数据入栈。

4.出栈拼接字符串

代码

class Solution {/***  1.从左往右遍历字符串,找到非空格字符,用指针 l 记录位置*  2.从 l 开始遍历寻找一个单词的最后一个字符,找到后,用指针 r 记录位置*  3.将 [ l , r ]的数据入栈。**/public String reverseWords(String s) {int len = s.length();Stack<String> stack = new Stack<>();for (int i = 0; i < len; i++) {if (s.charAt(i) !=' '){for (int j = i; j < len; j++) {if (j+1 >= len || s.charAt(j + 1) == ' '){//入栈 [i , j]stack.add(s.substring(i,j+1));i = j;break;}}}}String res = "" ;while (!stack.isEmpty()){res = res.concat(stack.pop());if (stack.isEmpty())break;res = res.concat(" ");}return res;}
}

效果

 方法2:集合翻转法

思路

先把字符串前后空格去掉,再把字符串按照一个或多个空格切分开,再放入到 list 集合中,再翻转这个集合就行。

思路简单,但是这些方法如何使用需要我们学习。

下面这行代码详解

List<String> wordList = Arrays.asList(s.split("\\s+"));

代码使用了Java的split()方法将一个字符串 s 按照空白字符(例如空格、制表符、换行符等)进行分割,并将结果存储到一个List<String>集合中。

让我们来解释这段代码的每个部分:

  1. s 是一个包含字符串的变量,你需要在使用这段代码前先给它赋值。

  2. split("\\s+") 是对字符串 s 进行分割的操作。split() 方法接受一个正则表达式作为参数来指定分隔符。在这里,我们使用了正则表达式 \\s+ 来表示一个或多个空白字符。这样,split("\\s+") 会将字符串 s 按照空白字符进行分割,并返回一个字符串数组。

  3. Arrays.asList(...) 将得到的字符串数组转换为List<String>集合。Arrays.asList(...) 是一个静态方法,它接受一个数组作为参数,并返回一个List集合。

综合起来,这段代码的作用是将一个字符串 s 按照空白字符进行分割,并将分割后的结果存储到List<String>类型的wordList集合中。

例如,假设 s 的值为:"Hello World Java",那么wordList将会是一个包含三个元素的List集合:["Hello", "World", "Java"]。

代码

class Solution {public String reverseWords(String s) {// 除去开头和末尾的空白字符s = s.trim();// 正则匹配连续的空白字符作为分隔符分割List<String> wordList = Arrays.asList(s.split("\\s+"));Collections.reverse(wordList);return String.join(" ", wordList);}
}

效果 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/53094.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Boot、Spring Cloud、Spring Alibaba 版本对照关系及稳定兼容版本

Spring Boot、Spring Cloud、Spring Alibaba 版本对照关系及稳定兼容版本 引言 在 Java 生态系统中&#xff0c;Spring Boot、Spring Cloud 和 Spring Alibaba 是非常流行的框架&#xff0c;它们提供了丰富的功能和优雅的解决方案。然而&#xff0c;随着不断的发展和更新&…

.Net6 Web Core API 配置 Autofac 封装 --- 依赖注入

目录 一、NuGet 包导入 二、Autofac 封装类 三、Autofac 使用 四、案例测试 下列封装 采取程序集注入方法, 单个依赖注入, 也适用, 可<依赖注入>的地方配置 一、NuGet 包导入 Autofac Autofac.Extensions.DependencyInjection Autofac.Extras.DynamicProxy 二、Auto…

应急响应-主机后门webshell的排查思路(webshell,启动项,隐藏账户,映像劫持,rootkit后门)

0x00 windows主机后门排查思路 针对主机后门windows&#xff0c;linux&#xff0c;在对方植入webshell后&#xff0c;需要立即响应&#xff0c;排查出后门位置&#xff0c;以及排查对外连接&#xff0c;端口使用情况等等 排查对外连接状态&#xff1a; 借助工具&#xff1a;p…

【Ansible】Ansible自动化运维工具之playbook剧本搭建LNMP架构

LNMP 一、playbooks 分布式部署 LNMP1. 环境配置2. 安装 ansble3. 安装 nginx3.1 准备 nginx 相关文件3.2 编写 lnmp.yaml 的 nginx 部分3.3 测试 nginx4. 安装 mysql4.1 准备 mysql 相关文件4.2 编写 lnmp.yaml 的 mysql 部分4.3 测试 mysql5. 安装 php5.1 编写 lnmp.yaml 的 …

整理mongodb文档:collation

文章连接 整理mongodb文档:collation 看前提示 对于mongodb的collation。个人主要用的范围是在createcollection&#xff0c;以及find的时候用&#xff0c;所以本片介绍的时候也是这两个地方入手&#xff0c;对新手个人觉得理解概念就好。不要求强制性掌握&#xff0c;但是要…

服务器流量

1.服务器流量分为入流量和出流量 入流量&#xff08;Inbound Traffic&#xff09;是指流向服务器的数据流量&#xff0c;也就是客户端发送到服务器的数据。这些数据可能包括请求信息、文件上传等。 出流量&#xff08;Outbound Traffic&#xff09;是指从服务器流向客户端的数…

【C++】内存管理

内存管理 一、C/C内存分布二、C 内存管理方式1. new/delete操作内置类型2. new和delete操作自定义类型 三、new 和 delete 的实现原理1. 内置类型2. 自定义类型 四、定位new表达式(placement-new) &#xff08;了解&#xff09;五、 malloc/free 和 new/delete 的区别 一、C/C内…

神码ai火车头标题伪原创【php源码】

这篇文章主要介绍了如何把python 代码打包成可执行软件&#xff0c;具有一定借鉴价值&#xff0c;需要的朋友可以参考下。希望大家阅读完这篇文章后大有收获&#xff0c;下面让小编带着大家一起了解一下。 火车头采集ai伪原创插件截图&#xff1a; Python 程序封装-打包成exe程…

stm32与上位机电脑间最快的通信方式是什么?

对于小型多关节机械臂的控制电路设计&#xff0c;选择合适的通信方式可以提高MCU与上位机之间的实时性。以下是一些在STM32上常用的通信方式&#xff0c;你可以根据你的具体需求选择适合的&#xff1a; 串口通信&#xff08;UART&#xff09;&#xff1a;串口通信是一种常见的…

C++类和对象入门(下)

C类和对象入门 1. Static成员1.1 Static成员的概念2.2 Static成员的特性 2.友元2.1 友元函数2.2 友元函数的特性2.3 友元类 3. 内部类3.1 内部类的概念和特性 4. 匿名对象5. 再次理解类和对象 1. Static成员 1.1 Static成员的概念 声明为static的类成员称为类的静态成员&…

Protobuf 调试踩坑记录

这是一次记录在使用protobuf中踩坑的记录&#xff0c;做一次记录整理&#xff0c;也希望能给其他遇到坑的同学一些帮助。 目录 背景&#xff1a; 踩坑记录 problem 1&#xff1a; solution&#xff1a; problem 2&#xff1a; solution&#xff1a; problem 3: solutio…

怎么加密文件夹才更安全?安全文件夹加密软件推荐

文件夹加密可以让其中数据更加安全&#xff0c;但并非所有加密方式都能够提高极高的安全强度。那么&#xff0c;怎么加密文件夹才更安全呢&#xff1f;下面我们就来了解一下那些安全的文件夹加密软件。 文件夹加密超级大师 如果要评选最安全的文件夹加密软件&#xff0c;那么文…