【一起学算法】 - 链表


在这里插入图片描述

文章前言

  • 【一起学算法】专栏持续更新中,会在这里记录算法学习的过程
  • 文末获取【一起学算法】Github仓库手写算法源码,一起跟着写一遍吧~
  • 【一起学算法】中所涉及的部分关于leetcode中的原题均可在leetcode官网的运行器通关~

单向链表

image-20211208204231073

节点结构

/**
 * @author Rhys.Ni
 * @version 1.0
 * @date 2021/12/5 9:59 上午
 * @Description 单向链表节点结构
 */
public class Node<T> {
    public T value;
    public Node<T> next;

    public Node(T value) {
        this.value = value;
    }
}

题记

单链表反转
  • 思路图解

image-20211213030503316

  • 关键代码

    //a -> b -> c -> null
    //null <- a <- b <- c
    public static Node reverse(Node head) {
      Node pre = null;
      Node next = null;
      while (head != null) {
        //提前记录next节点
        next = head.next;
        //将头结点的下一个节点指向Pre
        head.next = pre;
        //pre移动到当前头节点位置
        pre=head;
        //头结点移动到提前记录的next位置
        head=next;
      }
      //当head为null时说明已经到头了,此时应该返回pre所在位置的节点作为头结点
      return pre;
    }
    

##双向链表

在这里插入图片描述

节点结构

/**
 * @author Rhys.Ni
 * @version 1.0
 * @date 2021/12/5 10:35 上午
 * @Description
 */
public class DoubleNode<T> {
    public T value;
    public DoubleNode<T> pre;
    public DoubleNode<T> next;

    public DoubleNode(T value) {
        this.value = value;
    }
}

题记

双向链表反转
  • 思路图解

    image-20211213030331079

  • 关键代码

    public static DoubleNode<Integer> reverse(DoubleNode<Integer> head) {
      DoubleNode<Integer> pre = null;
      DoubleNode<Integer> next = null;
      while (head != null) {
    		//首先记录next节点
        next = head.next;
        //将当前头节点的下一个节点指向当前pre所在的地址
        head.next = pre;
        //将当前头结点的上一个节点指向原来next的地址
        head.last = next;
        //将pre标志移动到当前头的位置
        pre = head;
        //将next变为头结点
        head = next;
      }
      //当头结点到达Null的时候说明pre是最后一个有效头节点
      return pre;
    }
    
K个节点的组内逆序调整
  • 给定一个单链表的头节点head,和一个正数k,实现k个节点的小组内部逆序,如果最后一组不够k个就不调整

    • 例子: 调整前:1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8,k = 3

      ​ 调整后:3 -> 2 -> 1 -> 6 -> 5 -> 4 -> 7 -> 8

  • 思路图解

    image-20211218212040853

  • 关键代码

    /**
     * @author Rhys.Ni
     * @version 1.0
     * @date 2021/12/16 8:34 下午
     * @Description Definition for singly-linked list.
     * public class ListNode {
     * int val;
     * ListNode next;
     * ListNode() {}
     * ListNode(int val) { this.val = val; }
     * ListNode(int val, ListNode next) { this.val = val; this.next = next; }
     * }
     * 力扣原题:https://leetcode-cn.com/problems/reverse-nodes-in-k-group/?utm_source=LCUS&utm_medium=ip_redirect&utm_campaign=transfer2china
     */
    public class ReverseKGroup<T> {
        public ListNode reverseKGroup(ListNode head, int k) {
            ListNode start = head;
            ListNode groupEnd = findGroupEnd(start, k);
            //凑不够一组直接返回当前头节点
            if (groupEnd == null) {
                return head;
            }
            //第一组满足K个
            head = groupEnd;
            reverse(start, groupEnd);
            //上一组的最后一个节点(其实就是上一组的开始节点,经过反转后成为最后一个节点)
            ListNode lastEnd = start;
            //当上一组的最后一个节点不指向Null的时候(如果最后没凑够一组也是会返回Null的)
            while (lastEnd.next != null) {
                //将上一组最后一个节点的下一个节点设置为下一组的开始节点
                start = lastEnd.next;
                groupEnd = findGroupEnd(start, k);
                if (groupEnd == null) {
                    return head;
                }
                reverse(start, groupEnd);
                //上一组反转后的最后一个节点指向这一组的结束节点
                lastEnd.next = groupEnd;
                //将下一组的开始节点设置为反转后的最后一个节点
                lastEnd = start;
            }
            return head;
        }
    
        /**
         * 组内反转
         *
         * @param start
         * @param groupEnd
         * @return
         */
        public void reverse(ListNode start, ListNode groupEnd) {
            ListNode pre = null;
            ListNode curr = start;
            //代名词为:本组反转操作的最后标志位,实际为下一组的开始节点
            ListNode operationEnd = groupEnd.next;
            //如果Curr标志位跳到操作终止节点代表本组反转已经结束
            while (curr != operationEnd) {
                ListNode next = curr.next;
                curr.next = pre;
                pre = curr;
                curr = next;
            }
            //本组的开始节点(反转后应该是最后一个节点),指向下一组的开始节点
            start.next = operationEnd;
        }
    
        /**
         * 找每组最后一个节点
         *
         * @param start
         * @param k
         * @return
         */
        public ListNode findGroupEnd(ListNode start, int k) {
            while (--k != 0 && start != null) {
                start = start.next;
            }
            return start;
        }
    
    
        public class ListNode {
            int val;
            ListNode next;
    
            ListNode() {
            }
    
            ListNode(int val) {
                this.val = val;
            }
    
            ListNode(int val, ListNode next) {
                this.val = val;
                this.next = next;
            }
        }
    }
    
    
两个链表相加

给定两个链表的头节点head1和head2,认为从左到右是某个数字从低位到高位,返回相加之后的链表

  • 例子 4 -> 3 -> 6 -> 9 3 -> 5 -> 4

    返回 7 -> 8 -> 0 -> 0 -> 1

    解释 9634 + 453 = 10087

  • 思路图解

    image-20211222014356479

  • 关键代码

    //长短链表题
    //共分为三阶段:
    //一阶段:长链表有,短链表有
    //二阶段:长链表有,短链表无
    //三阶段:长链表无,短链表无
    
    /**
     * @author Rhys.Ni
     * @version 1.0
     * @date 2021/12/20 10:57 下午
     * @Description 两链表相加
     * @leetcode:https://leetcode.com/problems/add-two-numbers/
     */
    public class AddTwoNumbers {
        public ListNode addTwoNumbers(ListNode head1, ListNode head2) {
            int len1 = calcLength(head1);
            int len2 = calcLength(head2);
            ListNode longNode = len1 >= len2 ? head1 : head2;
            ListNode shortNode = longNode == head1 ? head2 : head1;
            ListNode currL = longNode;
            ListNode currS = shortNode;
            ListNode last = null;
            int carry = 0;
            int nodeSum = 0;
            //一阶段:长短链表都有的情况 直接判断短链表是否为NULL得知是否一阶段结束
            while (currS != null) {
                nodeSum = currL.val + currS.val + carry;
                currL.val = nodeSum % 10;
                carry = nodeSum / 10;
                last = currL;
                currL = currL.next;
                currS = currS.next;
            }
    
            //二阶段,长链表有,短链表无
            while (currL != null) {
                nodeSum = currL.val + carry;
                currL.val = nodeSum % 10;
                carry = nodeSum / 10;
                last = currL;
                currL = currL.next;
            }
    
            //三阶段:长短链表都无,校验进位信息决定是否补新节点
            if (carry != 0) {
                last.next = new ListNode(1);
            }
            return null;
        }
    
        /**
         * 计算链表长度
         *
         * @param node
         * @return
         */
        public int calcLength(ListNode node) {
            int length = 0;
            while (node != null) {
                length++;
                node = node.next;
            }
            return length;
        }
    
        public class ListNode {
            int val;
            ListNode next;
    
            ListNode() {
            }
    
            ListNode(int val) {
                this.val = val;
            }
    
            ListNode(int val, ListNode next) {
                this.val = val;
                this.next = next;
            }
        }
    }
    
两个有序链表的合并
  • 思路图解

    image-20211225114944761

  • 关键代码

    /**
     * @author Rhys.Ni
     * @version 1.0
     * @date 2021/12/22 8:35 下午
     * @Description 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
     * @leetcode:https://leetcode-cn.com/problems/merge-two-sorted-lists/
     */
    public class MergeTwoLists {
        public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
            if (list1 == null || list2 == null) {
                return list1 == null ? list2 : list1;
            }
    
            //找到最小的作为头节点(升序链表)
            ListNode head = list1.val <= list2.val ? list1 : list2;
            ListNode pre = head;
            ListNode curr1 = head.next;
            ListNode curr2 = head == list1 ? list2 : list1;
            //两链表都有节点
            while (curr1 != null && curr2 != null) {
                if (curr1.val <= curr2.val) {
                    pre.next = curr1;
                    curr1 = curr1.next;
                } else {
                    pre.next = curr2;
                    curr2 = curr2.next;
                }
                pre = pre.next;
            }
            //仅剩一个链表有节点
            pre.next = curr1 == null ? curr2 : curr1;
            return head;
        }
    
    
        public class ListNode {
            int val;
            ListNode next;
    
            ListNode() {
            }
    
            ListNode(int val) {
                this.val = val;
            }
    
            ListNode(int val, ListNode next) {
                this.val = val;
                this.next = next;
            }
        }
    }
    

在这里插入图片描述

源码获取地址

加油小伙伴~ 点击获取算法篇代码~

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

倪倪N

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值