一、题目描述

找出数组中重复的数字

> 在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。

二、思路分析

  • 之前我们已经分析过了通过递归的方式解决此问题 。 递归将问题逐层细化已达到整体问题的解决

  • 而今天我们将从另外一个角度去分析次问题--迭代。所谓迭代就是通过一次循环遍历解决反转问题。而递归不同的是他将是从左至右的方式解决问题
  • 在范围内的链表节点先将他指向一个默认前置节点preNode 。然后将当前节点指针后移在重复next指针指向preNode 。就可以解决问题

  • 这样我们仅仅借助于一个preNode就可以完成节点2的反转。不过这里节点已的next指针还是指向节点2的。这一步我们会在最后处理首尾问题。

  • 最终将会是如下指向问题,对于节点3、节点4也是同样的操作。

  • 当指定范围内数据全部扫描完成之后内部指针结构如上。图中1、2、4、5被特殊标注出来因为这四个分别是外边界和内边界的节点。我们需要特殊将这些边界进行连接 。 1指向4 、 2指向5就完成了最终的反转

  • 相信通过上面的动画模拟,你应该可以轻松的理解迭代处理的方式。但是在我们实际处理中边界我们需要特殊存储处理。下面我们就通过代码层面来实现效果

三、AC 代码

bug

  • 按照上面的逻辑,我尝试实现了下
//外边界左侧节点
private static ListNode firstNode ;
//外边界右侧节点
private static ListNode lastNode ;
public ListNode reverseBetween(ListNode head, int left, int right) {
//preNode 作为接受反转节点
ListNode preNode=null;
//用于指向当前操作节点 , 也是内部右侧节点
ListNode currentNode = head;
//存储下一节点,方便赋值
ListNode nextNode=null;
//内部左侧节点
ListNode leftNode=head;
int index =1 ;
while (currentNode != null) {
nextNode = currentNode.next;
if (index == left-1) {
//捕获外部边界节点
firstNode = currentNode;
}
if (index >= left && index <= right) {
//指针修复
currentNode.next = preNode;
preNode = currentNode;
}
currentNode = nextNode;
if (index == right) {
//捕获外部边界节点
lastNode = nextNode;
break;
}
index++;
}
//因为是指定范围但是有可能是全部链表这时候外部边界都是null
if (firstNode != null) {
leftNode = firstNode.next;
firstNode.next = preNode;
}
if (lastNode != null) {
leftNode.next = lastNode;
return head;
} else {
return preNode;
}
}
  • 上面这段代码本地运行是成功的。而且在leetcode官网上执行[3,5] ,left=1 , right=1 单独执行输出结果也是[3,5] 时没有问题的。但是当提交执行全部测试用例的时候确保在【3,5】 1 ,1这个测试用例无法通过。我认为是leetcode官网执行测试代码的一个bug

添加头结点

  • 在我们上面代码中虽然leetcode没有通过但是那是leetcode的bug导致的,在里面我们不难发现有很多if else操作。这样的代码很难看至少在代码洁癖面前是不能容忍的。
  • 为什么会有那么的判断,主要是因为我们的外部边界和内部边界可能会出现重合。所以我们在原有的链表中在头部再添加一个默认节点。这样做是为了避免外边界空的情况。

  • 同样是left=2,right=4的情况,我们从红色头结点开始获取到left=2之前的节点和right=4的节点 。 即node1是我们之前说的firstNode。node5是lastNode。
  • leftNode=node2;rightNode=node4 。然后我们在将内部链表进行反转。反转的方法就是按照我们上面的逻辑借助另外一个空节点作为preNode。

  • 因为node1,和node5已经被我们记录下来了。下面我们只需要将内部外部指针进行关联就可以了
firstNode.next = rightNode;
leftNode.next = lastNode;
  • 最终将头结点后面部分返回就可以了
private static ListNode firstNode ;
private static ListNode lastNode ;
public ListNode reverseBetween2(ListNode head, int left, int right) {
ListNode visualNode = new ListNode(-1, head);
firstNode = visualNode;
for (int i = 1;i < left; i++) {
firstNode = firstNode.next;
}
ListNode rightNode = firstNode;
for (int i = 0; i < right - left + 1; i++) {
rightNode = rightNode.next;
}
ListNode leftNode = firstNode.next;
lastNode = rightNode.next;
rightNode.next = null;
ListNode wpre = null;
ListNode wcur = leftNode;
while (wcur != null) {
ListNode next = wcur.next;
wcur.next = wpre;
wpre = wcur;
wcur = next;
}
firstNode.next = rightNode;
leftNode.next = lastNode;
return visualNode.next;
}
  • 多执行几次看看最终的效果

  • 速度依旧是那么快,在内存使用上平均值是65%以上。和我们递归的方式进行对比不难发现。迭代的方式在时间和空间上都是最优的。

四、总结

  • 迭代和递归是解决链表常用的两种方式。迭代的优点就是不断的循环下去
  • 递归最大的问题就是容易导致死循环,在书写的时候需要特殊注意递归的结束条件

说了你可能不信leetcode刷题局部链表反转D92存在bug,你看了就知道了的更多相关文章

  1. LeetCode刷题总结-链表

    LeetCode刷题总结-链表 一.链表     链表分为单向链表.单向循环链表和双向链表,一下以单向链表为例实现单向链表的节点实现和单链表的基本操作. 单向链表 单向链表也叫单链表,是链表中最简单的 ...

  2. leetcode刷题记录——链表

    使用java实现链表 单向链表 双向链表 单向循环链表 双向循环链表 题目记录 160.相交链表 例如以下示例中 A 和 B 两个链表相交于 c1: A: a1 → a2 c1 → c2 → c3 B ...

  3. Leetcode刷题之链表增加头结点的前缀节点

    链表之增加头结点的前缀节点 在许多链表题中往往需要在题目给的头结点之前增加一个前缀节点 通常在删除链表和头结点需要交换时需要用到这一操作 因为增加这个节点就避免了对删除头结点这种特殊情况的特殊处理 而 ...

  4. leetCode刷题(使用链表做加法)

    Input: (2 -> 4 -> 3) + (5 -> 6 -> 4) Output: 7 -> 0 -> 8 Explanation: 342 + 465 = ...

  5. leetcode刷题七<整数反转>

    给出一个 位的有符号整数,你需要将这个整数中每位上的数字进行反转. 示例 : 输入: 输出: 示例 : 输入: - 输出: - 示例 : 输入: 输出: 假设我们的环境只能存储得下 32 位的有符号整 ...

  6. LeetCode刷题笔记-递归-反转二叉树

    题目描述: 翻转一棵二叉树. 解题思路: 1.对于二叉树,立马递归 2.先处理 根节点,不需改动 3.处根的左子树和右子树需要交换位置 4.递归处理左子树和右子树.步骤见1-3步 Java代码实现: ...

  7. Leetcode刷题——007.整数反转

    上代码: #include <cmath> class Solution { public: int reverse(int x) { ; long long tx=llabs(x); ) ...

  8. Leetcode刷题之链表中箭头转移和内容转移

    链表中箭头转移和内容转移 链表中特别注意xxx.next=xxx 和xxx=xxx的区别 xxx.next=xxx表示将指针(箭头)转移 xxx=xxx表示将内容转移 Leetcode206翻转链表 ...

  9. LeetCode刷题专栏第一篇--思维导图&时间安排

    昨天是元宵节,过完元宵节相当于这个年正式过完了.不知道大家有没有投入继续投入紧张的学习工作中.年前我想开一个Leetcode刷题专栏,于是发了一个投票想了解大家的需求征集意见.投票于2019年2月1日 ...

随机推荐

  1. Mybatis日志源码探究

    一.项目搭建 1.pom.xml <dependencies> <dependency> <groupId>log4j</groupId> <ar ...

  2. C# 8.0 宝藏好物 Async streams

    之前写<.NET gRPC 核心功能初体验>,利用gRPC双向流做了一个打乒乓的Demo,存储消息的对象是IAsyncEnumerable<T>,这个异步可枚举泛型接口支撑了g ...

  3. iNeuOS工业互联平台,发布:消息管理、子用户权限管理、元件移动事件、联动控制和油表饼状图,v3.4版本

    目       录 1.      概述... 2 2.      平台演示... 2 3.      消息管理... 2 4.      子用户权限管理... 3 5.      元件移动事件... ...

  4. go中semaphore(信号量)源码解读

    运行时信号量机制 semaphore 前言 作用是什么 几个主要的方法 如何实现 sudog 缓存 acquireSudog releaseSudog semaphore poll_runtime_S ...

  5. 庐山真面目之十二微服务架构基于Docker搭建Consul集群、Ocelot网关集群和IdentityServer版本实现

    庐山真面目之十二微服务架构基于Docker搭建Consul集群.Ocelot网关集群和IdentityServer版本实现 一.简介      在第七篇文章<庐山真面目之七微服务架构Consul ...

  6. vue-router 监控全局路由,在路由中改变vuex中的状态值

  7. RabbitMQ 入门 (Go) - 7. 数据持久化(下)【完】

    数据库 我使用的是 PostgreSQL. 使用的驱动是 github.com/lib/pq 这个网址 https://pkg.go.dev/github.com/lib/pq 是官方文档. 创建数据 ...

  8. Java8中的默认方法

    作者:汤圆 个人博客:javalover.cc 前言 大家好啊,我是汤圆,今天给大家带来的是<Java8中的默认方法>,希望对大家有帮助,谢谢 文章纯属原创,个人总结难免有差错,如果有,麻 ...

  9. 2-fabric网络搭建流程

    目录 一.示例网络 下面开始一步步的搭建和叙述上述过程 二.创建网络 三.添加网络管理员 四.定义联盟 五.为联盟创建通道 六.节点和账本 七.应用程序和智能合约链码 八.完成网络 简化视觉词汇表 九 ...

  10. 解决JDK9以上的非法反射访问警告

    1 问题描述 JDK9以上很多库都有这种非法反射访问的警告,比如protostuff: 解决方法两个: JDK降级 添加JVM参数 2 原因 降到JDK8能解决以上问题. 但是这不是本文的重点. 先说 ...