算法通关村第二关—链表反转的拓展问题(白银)

news2024/11/26 16:41:09

       链表反转的拓展问题

一、指定区间反转

 LeetCode92:给你单链表的头指针head和两个整数left和right,其中left<=right。请你反转从位置left到位置right的链表节点,返回反转后的链表。
截屏2023-12-02 13.01.05.png

1.1 头插法

 反转的整体思想是,在需要反转的区间里,每遍历到一个节点,让这个新节点来到反转部分的起始位置。下面的图展示了整个流程。
image.png

class Solution {
    public ListNode reverseBetween(ListNode head, int left, int right) {
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode pre = dummy, last= dummy;
        int num = 0;
        for(num = 0; num < left - 1; num++){
            pre = pre.next;
        }
        ListNode cur = pre.next;
        for(int i = 1; i <= right - left; i++){
            ListNode next = cur.next;
            cur.next = next.next;
            next.next = pre.next;
            pre.next = next;
        }
        return dummy.next;
    }
}

1.2 穿针引线法

算法步骤:
第1步:先将待反转的区域反转;
第2步:把pre的next指针指向反转以后的链表头节点,把反转以后的链表的尾节点的next指针指向Succ。
image.png

public ListNode reverseBetween(ListNode head,int left,int right){
	//因为头节点有可能发生变化,使用虚拟头节点可以避免复杂的分类讨论
	ListNode dummyNode = new ListNode(-1);
	dummyNode.next = head;
	ListNode pre = dummyNode;

	//第1步:从虚拟头节点走1eft-1步,来到Left节点的前一个节点
	//建议写在fo「循环里,语义清晰
	for (int i = 0;i < left -1; i++)
	pre = pre.next;

	//第2步:从pre再走right-left+1步,来到right节点
	ListNode rightNode = pre;
	for (int i = 0;i < right - left +1; i++)
	rightNode = rightNode.next;

	//第3步:切出一个子链表
	ListNode leftNode = pre.next;
	ListNode succ = rightNode.next;

	//思考一下,如果这里不设置next为null会怎么样
	rightNode.next null;
	//第4步:同第206题,反转链表的子区间
	reverseLinkedList(leftNode);
	//第5步:接回到原来的链表中
	//想一下,这里为什么可以用rightNode
	pre.next = rightNode;
	leftNode.next = succ;
	return dummyNode.next;
}
private void reverseLinkedList(ListNode head){
//也可以使用递归反转一个链表
ListNode pre = null;
ListNode cur = head;
while(cur!=null){
	ListNode next = cur.next;
	cur.next = pre;
	pre = cur;
	cur = next;
}
}

二、两两交换链表中的节点

 LeetCode24.给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)
image.png
image.png
指针调整如下
image.png

class Solution {
    public ListNode swapPairs(ListNode head) {
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode pre = dummy, cur = head;
        while(cur != null && cur.next != null){
            ListNode next = cur.next;
            cur.next = next.next;
            next.next = pre.next;
            pre.next = next;
            //两两交换,要把pre移到本次交换后的第二个元素,cur移到下一次交换的第一个元素
            pre = cur;
            cur = cur.next;
        }
        return dummy.next;
    }
}

三、单链表加1

 LeetCode369.用一个非空单链表来表示一个非负整数,然后将这个整数加一。你可以假设这个整数除了0本身,没有任何前导的0。这个整数的各个数位按照高位在链表头部、低位在链表尾部的顺序排列。
image.png
我们先看一下加法的计算过程:
 计算是从低位开始的,而链表是从高位开始的,所以要处理就必须反转过来,此时可以使用栈,也可以使用链表反转来实现。
 基于栈实现的思路不算复杂,先把题目给出的链表遍历放到栈中,然后从栈中弹出栈顶数字digit,加的时候再考虑一下进位的情况就ok了,加完之后根据是否大于0决定视为下一次要进位。

public ListNode plusone(ListNode head){
Stack<Integer>st = new Stack();
while(head != null){
	st.push(head.val);
	head head.next;
}
int carry = 0; //表示是否进位
ListNode dummy = new ListNode(0);
int adder = 1; //负责给个位数加1
while (!st.empty() || carry != 0){
	int digit = st.empty() ? 0 : st.pop();
	int sum = digit + adder + carry;
	carry = sum >101  :0;
	sum = sum > 10 ? sum -10 : sum;
	ListNode cur = new ListNode(sum);
	cur.next = dummy.next;
	dummy.next = cur;
	adder = 0;
}
return dummy.next;
}

四、链表加法

 相加相链表是基于链表构造的一种特殊题,反转只是其中的一部分。这个题还存在进位等的问题,因此看似简单,但是手写成功并不容易。
 LeetCode445题,给你两个非空链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。你可以假设除了数字0之外,这两个数字都不会
以零开头。示例:
image.png

(1)使用栈实现

 思路是先将两个链表的元素分别压栈,然后再一起出栈,将两个结果分别计算。之后对计算结果取模,模数保存到新的链表中,进位保存到下一轮。完成之后再进行一次反转就行了。

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode headsum = new ListNode(0);
		//创建两个栈分别存储l1和l2的每一位
        Deque<ListNode> stack1 = new LinkedList();
        Deque<ListNode> stack2 = new LinkedList();
        while(l1 != null){
            stack1.push(l1);
            l1 = l1.next;
        }
        while(l2 != null){
            stack2.push(l2);
            l2 = l2.next;
        }
        int judge = 0;//判断是否有进位
        while(!stack1.isEmpty() || !stack2.isEmpty() || judge == 1){
			//初始化结点为0,巧妙处理一个栈为空另外一个栈不为空的情形
            ListNode node1 = new ListNode(0);
            ListNode node2 = new ListNode(0);
            if(!stack1.isEmpty()) node1 = stack1.pop();
            if(!stack2.isEmpty()) node2 = stack2.pop();
            int number = (node1.val + node2.val) + judge;
            ListNode num = new ListNode(number % 10);
			//顺势解决链表反转问题
            num.next = headsum.next;
            headsum.next = num;
			
            judge = number > 9 ? 1 : 0;
        }
        return headsum.next;
    }
}

(2)使用链表实现

 如果使用链表反转,先将两个链表分别反转,最后计算完之后再将结果反转,一共有三次反转操作,所以必然将反转抽取出一个方法比较好,代码如下:

public class Solution{
	public ListNode addInList(ListNode head1,ListNode head2){
		head1 = reverse(head1);
		head2 = reverse(head2);
		ListNode head = new ListNode(-1);
		ListNode cur = head;
		int carry = 0;
		while(head1 != null || head2 != null){
			int val = carry;
			if (head1 != null){
				val += head1.val;
				head1 head1.next;
			}
			if (head2 != null){
				val += head2.val;
				head2 = head2.next;
			}
			cur.next = new ListNode(val % 10);
			carry = val / 10;
			cur = cur.next;
		}
		if (carry > 0){
			cur.next = new ListNode(carry);
		}
		return reverse(head.next);
	}
}

private ListNode reverse(ListNode head){
ListNode cur = head;
ListNode pre = null;
while(cur != null){
	ListNode temp = cur.next;
	cur.next = pre;
	pre = cur;
	cur = temp;
}
return pre;
}
}

五、再论链表的回文序列问题

 在上一关介绍链表回文串的时候,我们介绍的是基于栈的,相对来说比较好理解,但是除此之外还有可以使用链表反转来进行,而且还可以只反转一半链表,这种方式节省空间。
 我们姑且称之为“快慢指针+一半反转”法。
 这个实现略有难度,主要是在while循环中pre.next=prepre和orepre=pre两行实现了一边遍历一边将访问过的链表给反转了,所以理解起来有些难度,如果不理解可以在学完链表反转之后再看这个问题。

public boolean isPalindrome(ListNode head){
if(head =null || head.next =null){
	return true;
}
ListNode slow = head,fast = head;
ListNode pre = head,prepre = null;
while(fast != null && fast.next != null){
	pre = slow;
	slow = slow.next;
	fast = fast.next.next;
	//将前半部分链表反转
	pre.next prepre;
	prepre pre;
}
if(fast != null){
	slow = slow.next;
}
while(pre != null && slow != null){
	if(pre.val != slow.val){
		return false;
	}
	pre = pre.next;
	slow = slow.next;
}
return true;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux 调试器 --- g d b 使用

目录 一&#xff1a;gdb简介 二&#xff1a;示例代码 三&#xff1a;使用 1.启动gdb 2.各种指令 <1>: 查看源代码 <2>:设置断点 <3>:查看断点信息 <4>:删除断点 <5>: run <6>:逐过程调试 <7>:逐语句调试 <8>:查…

AntV和AntD之间的区别与联系

前言&#xff1a;最近在调研前端的一些框架&#xff0c;技术栈主要是用react&#xff0c;所以找到了2个十分相似解决方案&#xff0c;拿来对比一下&#xff08;antd和antv都是基于react&#xff09; antd对比antv antd antv 解决方案企业级 UI 设计语言数据可视化解决方案提供…

Innodb数据结构空间占用

了解数据存储空间占用&#xff0c;可以更方便我们再企业中对于数据库相关优化做评估。 一、查看当前数据表空间占用信息 首先这里准备一张数据库表约2.3w数据量&#xff1a; CREATE TABLE project (tenantsid bigint(20) NOT NULL DEFAULT 0 COMMENT 租户ID,project_id bigi…

双击热备方案实现(全)

双击热备是应用与服务器的一种解决方案&#xff0c;其构造思想是主机和从机通过TCP/IP网络连接&#xff0c;正常情况下主机处于工作状态&#xff0c;从机处于监视状态&#xff0c;一旦从机发现主机异常&#xff0c;从机将会在很短的时间内代替主机。完全实现主机的功能。 要想实…

odoo15关于tree视图添加按钮说明

1、odoo15的tree已经可以像form一样直接添加header标签 2、选取具体数据后&#xff0c;按钮出现&#xff0c;只需要在按钮中添加具体功能即可&#xff0c;下面是一个继承 3、效果&#xff1a;

JVM之四种引用类型(五)

JVM 系列吊打面试官&#xff1a;说一下 Java 的四种引用类型 四种引种类型 1.强引用 在 Java 中最常见的就是强引用&#xff0c;把一个对象赋给一个引用变量&#xff0c;这个引用变量就是一个强引用。当一个对象被强引用变量引用时&#xff0c;它处于可达状态&#xff0c;它是…

如何通过添加香港高防IP来防御攻击?

​  针对外贸建站&#xff0c;租用香港服务器&#xff0c;除了站长们较为关注的价格外&#xff0c;安全性也是至关重要的。香港服务器在使用中可能会遭受到常见的 DDoS 网络攻击&#xff0c;而在 DDoS 防护这一块&#xff0c;您可以使用香港 DDoS 高防 IP 和香港高防服务器来…

基于51单片机多功能时钟闹钟系统

**单片机设计介绍&#xff0c;基于51单片机多功能时钟闹钟系统 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于51单片机的多功能时钟闹钟系统是一种基于单片机的电子设备&#xff0c;能够显示时间、设置闹钟、进行计时以及提…

解决方案:Mac 安装 pip

python3 --version 通过以下命令来下载pip&#xff1a; curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py curl命令允许您指定一个直接下载链接。使用-o选项来设置下载文件的名称。 通过运行以下命令安装下载的包&#xff1a; python3 get-pip.py

POJ 3233 Matrix Power Series 动态规划(矩阵的幂)

一、题目大意 给出一个矩阵A&#xff0c; 输出矩阵B的每一项对M取余数的值。 二、解题思路 以二维矩阵为例&#xff0c;首先计算K2的情况&#xff0c;我们设结果矩阵为B 有如下表达式 那么不难看出&#xff0c;需要的矩阵其实就是以下的两个矩阵相乘后的左上角的N*N个 然后…

Linux中的UDEV机制与守护进程

Linux中的UDEV守护进程 udev简介守护进程守护进程概念守护进程程序设计守护进程的应用守护进程和后台进程的区别 UDEV的配置文件自动挂载U盘 udev简介 udev是一个设备管理工具&#xff0c;udev以守护进程的形式运行&#xff0c;通过侦听内核发出来的uevent来管理/dev目录下的设…

3.4 路由器的DHCP配置

实验3.4 路由器的DHCP配置 一、任务描述二、任务分析三、具体要求四、实验拓扑五、任务实施&#xff08;一&#xff09;配置基于接口地址池的DHCP1.交换机的基本配置2.路由器的基本配置3.开启路由器的DHCP服务器功能4.配置路由器接口的DHCP功能5.设置计算机使用DHCP方式获取IP地…

前端又出新轮子Nue.js,但还是低代码更香!

前言 别TM卷了&#xff01;&#xff01;&#xff01; 自从前后端分离以来&#xff0c;前端前端的车轮滚滚向前&#xff0c;轮子造的越来越圆。每个人都在适应这个轮子的节奏&#xff0c;稍微不注意就会被甩出车轮之外。 调侃归调侃&#xff0c;既然口子已经开了&#xff0c;…

Java操作Excel之 POI介绍和入门

POI是Apache 提供的一个开源的Java API&#xff0c;用于操作Microsoft文档格式&#xff0c;如Excel、Word和PowerPoint等。POI是Java中处理Microsoft文档最受欢迎的库。 截至2023/12&#xff0c; 最新版本时 POI 5.2.5。 JDK版本兼容 POI版本JDK版本4.0及之上版本> 1.83.…

游戏测试 vs 软件测试:你知道它们的真正区别吗?

游戏测试和软件测试有什么区别&#xff1f;10大测试点对比&#xff01; 针对手游而言&#xff0c;游戏测试的本质是APP&#xff0c;所以不少手游的测试方式与APP测试异曲同工&#xff0c;然而也有所不同。APP更多的是具有一种工具&#xff0c;一款APP好不好用不重要&#xff0…

【译】DispatcherServlet

1.1. DispatcherServlet 见 Reactive 技术栈中的等效内容 Spring MVC和其他许多Web框架一样&#xff0c;是围绕前端控制器模式设计的&#xff0c;其中一个中央 Servlet&#xff0c;即 DispatcherServlet&#xff0c;为请求处理提供了一个共享算法&#xff0c;而实际工作则由可…

Unity 网格布局控件-Grid Layout Group

Unity 网格布局控件-Grid Layout Group是Unity中的UGUI控件&#xff0c;用于在 UI 中创建网格布局&#xff0c; 它的作用是&#xff1a;自动将子对象排列成网格&#xff0c;即我们可以通过该组件对子对象按行和列的形式排列&#xff0c;根据指定的约束条件自动调整它们的大小和…

【网络安全】红蓝对抗之企业互联网安全防护

01 什么是“红蓝对抗”&#xff1f; “红蓝对抗”最早起源于古罗马军队&#xff0c;在沙盘中用红色和蓝色来代表敌人和自己&#xff0c;他们认为蓝色代表勇敢和忠诚&#xff0c;红色代表血腥和暴力&#xff0c;所以选择用蓝色代表自己。 在中国&#xff0c;由于传统习俗与文化…

卡码网语言基础课 | 19. 洗盘子

目录 一、 栈的基本概念 二、 栈的操作 2.1 引入头文件 2.2 创建栈 2.3 栈的基本认识 三、 解答 通过本次练习&#xff0c;将学习到以下 C知识点&#xff1a; 栈的基本概念&#xff08;空栈、栈顶、栈底&#xff09;和特点&#xff08;先入后出&#xff09;入栈、出栈、获取…

高性能网络编程 - 白话TCP 三次握手过程

文章目录 概述TCP协议头的格式TCP Finite State Machine (FSM) 状态机三次握手如何在 Linux 系统中查看 TCP 状态 概述 每一个抽象层建立在低一层提供的服务上&#xff0c;并且为高一层提供服务。 我们需要知道 TCP在网络OSI的七层模型中的第四层——Transport层 -----------…