一、题目描述

找出数组中重复的数字

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

二、思路分析

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

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

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

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

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

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

三、AC 代码

bug

  • 按照上面的逻辑,我尝试实现了下
  1. //外边界左侧节点
  2. private static ListNode firstNode ;
  3. //外边界右侧节点
  4. private static ListNode lastNode ;
  5. public ListNode reverseBetween(ListNode head, int left, int right) {
  6. //preNode 作为接受反转节点
  7. ListNode preNode=null;
  8. //用于指向当前操作节点 , 也是内部右侧节点
  9. ListNode currentNode = head;
  10. //存储下一节点,方便赋值
  11. ListNode nextNode=null;
  12. //内部左侧节点
  13. ListNode leftNode=head;
  14. int index =1 ;
  15. while (currentNode != null) {
  16. nextNode = currentNode.next;
  17. if (index == left-1) {
  18. //捕获外部边界节点
  19. firstNode = currentNode;
  20. }
  21. if (index >= left && index <= right) {
  22. //指针修复
  23. currentNode.next = preNode;
  24. preNode = currentNode;
  25. }
  26. currentNode = nextNode;
  27. if (index == right) {
  28. //捕获外部边界节点
  29. lastNode = nextNode;
  30. break;
  31. }
  32. index++;
  33. }
  34. //因为是指定范围但是有可能是全部链表这时候外部边界都是null
  35. if (firstNode != null) {
  36. leftNode = firstNode.next;
  37. firstNode.next = preNode;
  38. }
  39. if (lastNode != null) {
  40. leftNode.next = lastNode;
  41. return head;
  42. } else {
  43. return preNode;
  44. }
  45. }
  • 上面这段代码本地运行是成功的。而且在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已经被我们记录下来了。下面我们只需要将内部外部指针进行关联就可以了
  1. firstNode.next = rightNode;
  2. leftNode.next = lastNode;
  • 最终将头结点后面部分返回就可以了
  1. private static ListNode firstNode ;
  2. private static ListNode lastNode ;
  3. public ListNode reverseBetween2(ListNode head, int left, int right) {
  4. ListNode visualNode = new ListNode(-1, head);
  5. firstNode = visualNode;
  6. for (int i = 1;i < left; i++) {
  7. firstNode = firstNode.next;
  8. }
  9. ListNode rightNode = firstNode;
  10. for (int i = 0; i < right - left + 1; i++) {
  11. rightNode = rightNode.next;
  12. }
  13. ListNode leftNode = firstNode.next;
  14. lastNode = rightNode.next;
  15. rightNode.next = null;
  16. ListNode wpre = null;
  17. ListNode wcur = leftNode;
  18. while (wcur != null) {
  19. ListNode next = wcur.next;
  20. wcur.next = wpre;
  21. wpre = wcur;
  22. wcur = next;
  23. }
  24. firstNode.next = rightNode;
  25. leftNode.next = lastNode;
  26. return visualNode.next;
  27. }
  • 多执行几次看看最终的效果

  • 速度依旧是那么快,在内存使用上平均值是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. python3 base64

    import base64s='hello world'bytes_by_s=s.encode() #将字符串编码-->字节码,b64_encode_bytes=base64.b64encode ...

  2. 1、MyBatis教程之环境准备和简介

    1.环境准备 jdk 8 + MySQL 5.7.19 maven-3.6.1 IDEA 学习前需要掌握: JDBC MySQL Java 基础 Maven Junit Idea快捷键 一键格式化代碼 ...

  3. 用jar命令打包war远程部署

    最近在看jboss的相关漏洞,用jmx-console进行war远程部署的时候碰到一个jsp转war的问题,研究了半天,记录一下免得搞忘了. 一开始网上是说的直接把jsp文件压缩成zip,再把后缀名改 ...

  4. 通俗地理解面向服务的架构(SOA)以及微服务之间的关系

    SOA是一种软件的应用架构方法,它基于面向对象,但又不是面向对象,整体上是面向服务的架构.SOA由精确的服务定义.松散的构件服务组成,以及业务流程调用等多个方面形成的一整套架构方法. 这话是不是听起来 ...

  5. Apache SkyWalking 告警配置指南

    Apache SkyWalking Apache SkyWalking是分布式系统的应用程序性能监视工具(Application Performance Management,APM),专为微服务.云 ...

  6. 【Python学习笔记】-虚拟环境virtualenv

    在开发python应用程序的时候,系统安装的python3只有一个版本:3.4.所有的第三方的包都回被pip安装到python3的site-packages目录下. 如果我们要要同时开发多个应用程序, ...

  7. 轻松理解 Java 静态代理/动态代理

    目录 什么是代理模式 定义 代理模式的主要角色 优点 缺点 静态代理 动态代理 JDK原生动态代理 例子 分析 小结 CGLIB动态代理 例子 分析 final类型 其他方案 尾声 理解Java动态代 ...

  8. 解决SQLPLUS无法使用上下箭头

    1 问题描述 SQLPLUS中使用上下箭头无法获取历史命令,如下图所示: 按上下箭头会显示^[[A/^[[B. 2 解决方案 需要安装rlwrap,可以的话可以用包管理器安装,笔者环境CentOS,这 ...

  9. Spring 学习笔记(三):Spring Bean

    1 Bean配置 Spring可以看做是一个管理Bean的工厂,开发者需要将Bean配置在XML或者Properties配置文件中.实际开发中常使用XML的格式,其中<bean>中的属性或 ...

  10. Kubernetes删除一直处于Terminating状态的namespace

    问题现象: 删除namespace,一直处于Terminating,并且用--force --grace-period=0 也删除不了 develop Terminating 4d9h Error f ...