【金鱼佬学数据结构与算法】001-头插法反转单链

单链数据结构

在讨论头插法,反转一条单链之前,先回顾一下单链的数据结构。单链是一种线性表结构,存储的数据内容统一,可重复的。而单链跟数组最大的区别是,单链底层存储内存可不连续的,而这也导致单链上每个节点都有一个后继指针Next。
在这里插入图片描述

看一个场景小模型

回顾完单链这种数据结构后,我们再看一个场景小模型。回想我们校园时代,是否经历过排队,特别像军训时一样,被教官训练整蛊,从矮到高,从左往右排。这时学生就真的从矮到高排完了,如下图A~E一样。

在这里插入图片描述

怎么样,学生排完了,在学生的角度,矮的确实在左边,高的在右边。但是作为教官的我们的角度,却是相反的,所以我们就需要让学生反转了。这时候学生队伍的反转,就跟我们的头插法反转单链场景很像了。

还是根据这个场景进行说明,先让我们角度看去的高个子E,跑到A的位置,相当于开了一条新队伍,此时这条队伍的第一个人是E,最后一个人也是E,这个E至关重要,看清楚了。

在这里插入图片描述

排完后,这时,E后面是没人了的,而后面D进来,E就永远是最后一个人了,同理C、B、A这样进去,最后就反转了这条队伍。

在这里插入图片描述

上代码

  • 新建Node节点
public class Node {
  public int val;
  public Node next;

  public Node(int val) {
    this.val = val;
  }
}
  • 创建单链反转工具类
public class SingleLinkedList {
  public Node head;

  public SingleLinkedList() {
  }

  public SingleLinkedList(Node head) {
    this.head = head;
  }

  public Node reverseList() {
    if (this.head == null) {
      return null;  // 没有节点
    }

    if (this.head.next == null) {
      return this.head; // 只有一个节点
    }

    // 至少有两个节点
    // 先把当前链表的头取出来,当作一条新的链尾(this.head.next = null;),再把剩下的旧链部分当作当前链(Node cur = this.head.next;)
    Node cur = this.head.next;
    this.head.next = null;
    // 因为至少有两个结点,所以第一次取,剩下的旧链肯定至少还有一个节点
    while (cur != null) {
      // 再把剩下的旧链拆分,把第一个,跟后面的分开,curNext就是后面的链
      Node curNext = cur.next;
      // 因为上一步拆开,所以可以把新链放到当前链头后面
      cur.next = head;
      // 把当前链看作新链,覆盖到新链上面去
      this.head = cur;
      // 把curNext后面链当作当前链,接着判断当前链,直至当前链为空,即整条链已经进行反转处理
      cur = curNext;
    }
    return head;
  }
}
  • 测试类
public class App {
  public static void main(String[] args) {
    Node head = new Node(3);
    Node node1 = new Node(2);
    Node node2 = new Node(5);
    head.next = node1;
    node1.next = node2;

    Node h = head;
    while (null != h) {
      System.out.print(h.val + " ");
      h = h.next;
    }

    System.out.println("\n============");

    SingleLinkedList singleLinkedList = new SingleLinkedList(head);
    head = singleLinkedList.reverseList();
    while (null != head) {
      System.out.print(head.val + " ");
      head = head.next;
    }
  }
}

代码分析

上面三个类,单链反转重点关注工具类中的reverseList()方法。

  • 首先该方法,先判断特殊情况:

    1. 空链:直接返回null
    2. 该链只有一个节点:直接返回第一个节点
  • 除了1的两种情况外,该链至少两个或以上的节点,则可以进行反转返回

    1. 在进行这个代码分析之前,先记住场景小模型的排队案例,会让你更好理解代码的分析。

    2. 首先节点对象含有两个属性,一个是val,我们现实案例中,可设为Object或泛型,这里为了便于分析设为了int。另外还有一个属性是next,从前面的单链数据结构中,不难看出,单链是有个后继指针的,这个后继指针的作用是为了指向我们链上的下一个节点的。

      在这里插入图片描述

    3. 而此时我们有一条单链[4,3,8,9]要反转,最后的结果应该是[9,8,3,4]。核心代码中,首先要获取单链中除了头节点以外,剩下的一条链Node cur = this.head.next;

      在这里插入图片描述

      在这里插入图片描述

      • 初学者,可能会觉得this.head.next是获取到3而已。其实不是的,参考第2步的节点对象,可以得知,3这个节点对象,还有next属性(该属性又是一个Node节点类型)。所以this.head.next是获取到[3,8,9]

        在这里插入图片描述

      • 而代码注释中,提到先把当前链表的头取出来,当作一条新的链尾(this.head.next = null;),可能有人会问,为什么这段代码会在上一步代码的后面。细心的人应该也知道,如果先把第一个节点(head)的next置空的话,那再取链后面的内容就都为null了。

        在这里插入图片描述

      • 总结:这一步可以确定两段代码的含义,cur是可以看作当前队伍this.head看作是一条新队伍,对比上面场景模型的E

        在这里插入图片描述

    4. OK,现在进入循环,大家可以看到,上来又有一个叫curNext的变量,这个变量我们见名思其意,我们可以称为未来当前链。然后Node curNext = cur.next;跟第3步中的操作类比,curNext应该是cur这条链,除了第一个节点外,后面的节点,在这里即[8,9]

      在这里插入图片描述

    5. 在上一步操作看明白后,来到这一步cur.next = head;,可能大家有点懵,我一开始也懵在这里。

      • 首先再回想一下场景中E排好在新队伍后,D是怎么进去的,是不是跟C、B、A断开关系,然后去排在E前面。

      • 好会回过来看一下,这时候上一步的curNext是不是就是相当于C、B、A的那一段,那cur的val属性是不是就是我们D,那我们把cur(D)的next(C、B、A即[8,9])替换成(E即[4]),是不是就有cur.next = head。这时候的head在第3步第二点的操作中,head只剩下val属性值为4,next为null了。而cur.next指向了head,那cur的val是不是就是3,next是不是就是head,而head没有next,所以cur就是[3、4]了

        在这里插入图片描述

      • cur是[3、4],而第4步中curNext是不是剩下[8、9]。

        在这里插入图片描述

    6. 因为第3步总结说到,cur是当前队伍,this.head是新队伍,所以在进入下次循环之前,要给他们定义好,所以就有this.head=curcur=curNext。当然,他们的顺序不能颠倒,要是把cur先设为curNext,那再设this.head就两条链内容一样,都为[8、9]了。

完整流程图

根据代码分析后,是不是觉得循环还没有结束,有点可惜,可以再看完代码分析,及文字解析后,我们看图走完这个[4、3、8、9]的头插法单链反转流程吧

进入循环前

在这里插入图片描述

进入循环后

第一遍循环

在这里插入图片描述

第二遍循环

在这里插入图片描述

第三遍循环

在这里插入图片描述

第四遍循环

没有了,cur都null了,进不去了,这时候把最后一次循环的head返回即是倒转后的结果

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值