专题【链表】【学习题】刷题日记

题目列表

学习题(22题)

2024.03.31

两数相加
19. 删除链表的倒数第 N 个结点
合并K个升序链表

2024.04.01

24. 两两交换链表中的节点
25. K 个一组翻转链表
61. 旋转链表

2024.04.02

83. 删除排序链表中的重复元素
82. 删除排序链表中的重复元素 II
86. 分隔链表
92. 反转链表 II
116. 填充每个节点的下一个右侧节点指针
141. 环形链表
142. 环形链表 II

2024.04.03

143. 重排链表
147. 对链表进行插入排序
148. 排序链表
203. 移除链表元素
206. 反转链表
234. 回文链表
328. 奇偶链表
445. 两数相加 II
2074. 反转偶数长度组的节点


2024.03.31

两数相加

题目

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例 1:
在这里插入图片描述

输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.

示例 2:

输入:l1 = [0], l2 = [0]
输出:[0]

示例 3:

输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]

提示:

  • 每个链表中的节点数在范围 [1, 100] 内
  • 0 <= Node.val <= 9
  • 题目数据保证列表表示的数字不含前导零

思路

两个数和一个进位相加,所以所有都不能为空或者0

答案

/**
 * 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; }
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode head = new ListNode();
        ListNode c = head;
        int carry = 0;
        while (l1 != null || l2 != null || carry != 0) {
            int lv1 = 0;
            int lv2 = 0;
            if (l1 != null) {
                lv1 = l1.val;
                l1 = l1.next;
            }

            if (l2 != null) {
                lv2 = l2.val;
                l2 = l2.next;
            }

            int sum = lv1 + lv2 + carry;
            c.next = new ListNode(sum % 10);
            carry = sum / 10;
            c = c.next;
        }
        return head.next;
    }
}

总结

  1. list中常见问题,找个节点dummy节点放在头结点前面。
  2. 分开当前节点c和头结点,方便节点遍历

19. 删除链表的倒数第 N 个结点

题目

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

示例 1:
在这里插入图片描述

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

示例 2:

输入:head = [1], n = 1
输出:[]

示例 3:

输入:head = [1,2], n = 1
输出:[1]

提示:

  • 链表中结点的数目为 sz
  • 1 <= sz <= 30
  • 0 <= Node.val <= 100
  • 1 <= n <= sz

进阶: 你能尝试使用一趟扫描实现吗?

思路

倒数第N个,距离倒数第1个节点,阶段距离为N-1。
所以两个指针,距离为N-1,当右边的到了最后一个节点的时候,左边这个就是。
为了删除,需要拿到左边节点的上一个节点,所以距离为N
为了删除头结点,需要dummy

答案

/**
 * 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; }
 * }
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummy = new ListNode(-1, head);
        ListNode fast = dummy, slow = dummy;
        // i < 1移动,1次。距离为1。i < n,移动n次,距离为n
        for (int i = 0; i < n; i++) {
            fast = fast.next;
        }
        while (fast.next != null) {
            slow = slow.next;
            fast = fast.next;
        }
        slow.next = slow.next.next;
        return dummy.next;
    }
}

总结

  1. 虚拟头节点,为了删除第一个节点
  2. 删除倒数第N个,需要拿到倒数第N+1个节点
  3. 倒数第N+1个节点,和最后一个节点距离为N
  4. fast节点先移动N,for里面小于N则,移动N

合并两个有序链表

题目

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例 1:
在这里插入图片描述

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

示例 2:

输入:l1 = [], l2 = []
输出:[]

示例 3:

输入:l1 = [], l2 = [0]
输出:[0]

提示:

  • 两个链表的节点数目范围是 [0, 50]
  • -100 <= Node.val <= 100
  • l1 和 l2 均按 非递减顺序 排列

思路

  1. 需不需要头结点?需要,否则第一次操作非常麻烦。
  2. 还需要一个当前节点用来遍历

答案

/**
 * 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; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        ListNode result = new ListNode(-1);
        ListNode current = result;
        while (list1 != null || list2 != null) {
            int lv1 = Integer.MAX_VALUE;
            int lv2 = Integer.MAX_VALUE;
            if (list1 != null) {
                lv1 = list1.val;
            }
            if (list2 != null) {
                lv2 = list2.val;
            }
            if (lv1 < lv2) {
                current.next = list1;
                list1 = list1.next;
            } else {
                current.next = list2;
                list2 = list2.next;
            }
            current = current.next;
        }
        return result.next;
    }
}

合并K个升序链表

题目

给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。

示例 1:

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
1->4->5,
1->3->4,
2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6

示例 2:

输入:lists = []
输出:[]

示例 3:

输入:lists = [[]]
输出:[]

提示:

  • k == lists.length
  • 0 <= k <= 10^4
  • 0 <= lists[i].length <= 500
  • -10^4 <= lists[i][j] <= 10^4
  • lists[i] 按 升序 排列
  • lists[i].length 的总和不超过 10^4

思路

为了方便比较大小,用优先队列。也可以自己维护堆
需要头结点,方便后续操作

答案

/**
 * 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; }
 * }
 */
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        PriorityQueue<ListNode> queue = new PriorityQueue<>(new Comparator<ListNode>() {
            @Override
            public int compare(ListNode l1, ListNode l2) {
                return l1.val - l2.val;
            }
        });
        for (ListNode node : lists) {
            if (node != null)
                queue.offer(node);
        }
        ListNode dummy = new ListNode();
        ListNode current = dummy;
        while (!queue.isEmpty()) {
            ListNode node = queue.poll();
            current.next = node;
            current = node;
            if (node.next != null)
                queue.offer(node.next);
        }

        return dummy.next;
    }
}

2024.04.01


24. 两两交换链表中的节点

题目

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

示例 1:
在这里插入图片描述

输入:head = [1,2,3,4]
输出:[2,1,4,3]

示例 2:

输入:head = []
输出:[]

示例 3:

输入:head = [1]
输出:[1]

提示:

  • 链表中节点的数目在范围 [0, 100] 内
  • 0 <= Node.val <= 100

思路

  1. 因为需要翻转头结点,所以需要dummy
  2. 两个指针,分别指向两个要翻转的节点的上一个节点
  3. 调整指针的next
  4. 最后再调整两个指针,指向下一次要翻转的两个节点的上一个结点
  5. 判断条件看下奇偶个节点数的情况(1个节点和2个节点)

答案

/**
 * 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; }
 * }
 */
class Solution {
    public ListNode swapPairs(ListNode head) {
        
        ListNode dummy = new ListNode(-1, head);
        ListNode current = dummy;
        while (head != null && head.next != null) {
            current.next = head.next;
            head.next = head.next.next;
            current.next.next = head;
            current = head;
            head = head.next;
        }

        return dummy.next;
    }
}

25. K 个一组翻转链表

题目

给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。
k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。

示例 1:
在这里插入图片描述

输入:head = [1,2,3,4,5], k = 2
输出:[2,1,4,3,5]

示例 2:
在这里插入图片描述

输入:head = [1,2,3,4,5], k = 3
输出:[3,2,1,4,5]

提示:

  • 链表中的节点数目为 n
  • 1 <= k <= n <= 5000
  • 0 <= Node.val <= 1000

进阶: 你可以设计一个只用 O(1) 额外内存空间的算法解决此问题吗?

思路

  1. 先k个一组断开
  2. 然后翻转
  3. 递归调用

答案

/**
 * 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; }
 * }
 */
class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
    	// 翻转类,都需要一个头结点
        ListNode dummy = new ListNode(-1, head);
        ListNode c = dummy;
        // 拿到要翻转的最后一个节点,方便后面断开
        for (int i = 0; i < k && c != null; i++) {
            c = c.next;
        }
        if (c == null) {
            return dummy.next;
        }
		// 记录断开后的头节点
        ListNode next = c.next;
        c.next = null;
        ListNode newHead = reversNode(head);
        // 继续翻转后面的
        head.next = reverseKGroup(next, k);
        // 返回头节点
        return newHead;

    }

    public ListNode reversNode(ListNode node) {
        ListNode head = node, c = node;
        while (c != null && c.next != null) {
            ListNode next = c.next.next;
            c.next.next = head;
            head = c.next;
            c.next = next;
        }
        return head;
    }
}

61. 旋转链表

题目

给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。

示例 1:
在这里插入图片描述

输入:head = [1,2,3,4,5], k = 2
输出:[4,5,1,2,3]

示例 2:
在这里插入图片描述

输入:head = [0,1,2], k = 4
输出:[2,0,1]

提示:

  • 链表中节点的数目在范围 [0, 500] 内
  • -100 <= Node.val <= 100
  • 0 <= k <= 2 * 109

思路

  1. 右移k。先看右移1,则倒数第一个节点变为头结点。同理,右移K,则倒数第K个节点成为头结点。
  2. 变为找倒数第k个节点 + 把尾节点连接到原头结点
  3. 如果K大于链表长度,则为倒数第(K%链表长度)。示例2,k=4,链表长度为3,则为倒数第(4%3=1)

答案

/**
 * 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; }
 * }
 */
class Solution {
    public ListNode rotateRight(ListNode head, int k) {
        if (head == null) {
            return head;
        }
        ListNode fast = head, slow = head;
        for (int i = 0; i < k; i++) {
            if (fast.next == null) {
            	// 加速的,遍历一次之后就知道长度了
            	// i + 1是因为,遍历到k,调整了k-1次。长度为2遍历到最后一个元素,i=1
                k = i + (k % (i + 1)) + 1;
                fast = head;
            } else {
                fast = fast.next;
            }
        }

        while (fast.next != null) {
            fast = fast.next;
            slow = slow.next;
        }
        fast.next = head;
        ListNode newHead = slow.next;
        slow.next = null;
        return newHead;

    }
}

83. 删除排序链表中的重复元素

题目

给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。

示例 1:
在这里插入图片描述

输入:head = [1,1,2]
输出:[1,2]

示例 2:
在这里插入图片描述

输入:head = [1,1,2,3,3]
输出:[1,2,3]
提示:

  • 链表中节点数目在范围 [0, 300] 内
  • -100 <= Node.val <= 100
  • 题目数据保证链表已经按升序 排列

思路

  1. 如果当前指针值和下一个相等,则删除下一个。当前指针不变(因为下一个变了)
  2. 如果不相等,当前指针后移
  3. 因为用到下一个指针的值,所以条件为next不能为null

答案

/**
 * 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; }
 * }
 */
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        ListNode result = head;
        // 兼容head为null
        while (head != null && head.next != null) {
            if (head.val == head.next.val) {
                head.next = head.next.next;
            } else {
                head = head.next;
            }
        }
        return result;
    }
}

82. 删除排序链表中的重复元素 II

题目

给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。

示例 1:
在这里插入图片描述

输入:head = [1,2,3,3,4,4,5]
输出:[1,2,5]

示例 2:
在这里插入图片描述

输入:head = [1,1,1,2,3]
输出:[2,3]

提示:

  • 链表中节点数目在范围 [0, 300] 内
  • -100 <= Node.val <= 100
  • 题目数据保证链表已经按升序 排列

思路

  1. 和上一题的区别是,重复了都删除
  2. 因为头结点也可能删除,需要dummy
  3. 拿last记录当前值,赋值为next的值
  4. next.next.val和last相同就把下一个节点删除(已经有重复的(next和next.next)了,就删除)

答案

/**
 * 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; }
 * }
 */
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        ListNode dummy = new ListNode(-1, head);
        ListNode current = dummy;
        // 元素个数不够了,不可能重复了(0个或1个元素)
        while (current.next != null && current.next.next != null) {
        	// 确实存在重复
            if (current.next.val == current.next.next.val) {
            	// 记录重复值
                int last = current.next.val;
                // 开始删除
                while (current.next != null && last == current.next.val) {
                    current.next = current.next.next;
                }
            } else {
            	// 不重复,日常next
                current = current.next;
            }
        }
        return dummy.next;
    }
}

86. 分隔链表

题目

给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。
你应当 保留 两个分区中每个节点的初始相对位置。

示例 1:
在这里插入图片描述

输入:head = [1,4,3,2,5,2], x = 3
输出:[1,2,2,4,3,5]

示例 2:

输入:head = [2,1], x = 2
输出:[1,2]

提示:

  • 链表中节点的数目在范围 [0, 200] 内
  • -100 <= Node.val <= 100
  • -200 <= x <= 200

思路

  1. 搞两个空节点,一个是比目标值小的,一个是比目标值大的,最后合并
  2. 需要dummy节点,因为不知道第一个元素是比目标元素大还是小
  3. 整体就是操作链表的过程,链接到新节点、当前遍历节点后移、最后把节点断开再重连

答案


/**
* 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; }
* }
*/
class Solution {
   public ListNode partition(ListNode head, int x) {
       ListNode lessHeader = new ListNode();
       ListNode greatHeader = new ListNode();
       ListNode lessCurrent = lessHeader;
       ListNode greatCurrent = greatHeader;
       while (head != null) {
       	   // 链接新节点
           if (head.val >= x) {
               greatCurrent.next = head;
               greatCurrent = greatCurrent.next;
           } else {
               lessCurrent.next = head;
               lessCurrent = lessCurrent.next;
           }
           head = head.next;
       }
   	   // 头尾相接
       lessCurrent.next = greatHeader.next;
       // 断开链接
       
       greatCurrent.next = null;
       return lessHeader.next;
   }
}

92. 反转链表 II

题目

给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。

示例 1:
在这里插入图片描述

输入:head = [1,2,3,4,5], left = 2, right = 4
输出:[1,4,3,2,5]

示例 2:

输入:head = [5], left = 1, right = 1
输出:[5]

提示:

  • 链表中节点数目为 n
  • 1 <= n <= 500
  • -500 <= Node.val <= 500
  • 1 <= left <= right <= n

进阶: 你可以使用一趟扫描完成反转吗?

思路

反转链表可以归结为

  1. 找到要翻转的部分
  2. 用指针拿住其他部分
  3. 翻转
  4. 重新连接

答案

/**
 * 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; }
 * }
 */
class Solution {
    public ListNode reverseBetween(ListNode head, int left, int right) {
        ListNode dummy = new ListNode(-1, head);
        ListNode leftPreNode = dummy, rightNode = null, next = null;
        ListNode current = dummy;
        int i = 0;
        while (current != null) {
        	// 因为是前一个节点,所以-1.这里搞不明白,可以代入值试一下
            if (i == left - 1) {
                leftPreNode = current;
            }
            // 找到右边
            if (i == right) {
                rightNode = current;
                next = rightNode.next;
                break;
            }
            current = current.next;
            i++;
        }
		// 节点拆分,准备翻转
        rightNode.next = null;
      	// 拿住尾结点方便后续链接
        ListNode tail = leftPreNode.next;
        // 翻转
        leftPreNode.next = reverse(leftPreNode.next);
        // 重连
        tail.next = next;
        return dummy.next;
    }
	
	// 链表反转,原地翻转,多练,多画
    public ListNode reverse(ListNode head) {
        ListNode c = head, next = head;
        while (c != null && c.next != null) {
        	// 拿住
            next = c.next.next;
            // 变更(反转就是最后一个指向头)
            c.next.next = head;
            // 拿住(重新记录头的位置)
            head = c.next;
            // 变更(重新连接,完成第一次反转)
            c.next = next;
        }
        return head;
    }
}

116. 填充每个节点的下一个右侧节点指针

题目

给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:

struct Node {
  int val;
  Node *left;
  Node *right;
  Node *next;
}

填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。

示例 1:
在这里插入图片描述

输入:root = [1,2,3,4,5,6,7]
输出:[1,#,2,3,#,4,5,6,7,#]
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。序列化的输出按层序遍历排列,同一层节点由 next 指针连接,‘#’ 标志着每一层的结束。

示例 2:

输入:root = []
输出:[]

提示:

  • 树中节点的数量在 [0, 212 - 1] 范围内
  • -1000 <= node.val <= 1000

进阶:

  • 你只能使用常量级额外空间。
  • 使用递归解题也符合要求,本题中递归程序占用的栈空间不算做额外的空间复杂度。

思路

看不太懂题,所以试了一下
在这里插入图片描述
可知:

  1. 不用输出#
  2. 其实是层序遍历
  3. 按照层序遍历实现

答案

/*
// Definition for a Node.
class Node {
    public int val;
    public Node left;
    public Node right;
    public Node next;

    public Node() {}
    
    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, Node _left, Node _right, Node _next) {
        val = _val;
        left = _left;
        right = _right;
        next = _next;
    }
};
*/

class Solution {
    public Node connect(Node root) {
        List<Node> queue = new LinkedList<>();
        if (root != null) {
            queue.add(root);
        }
        while (!queue.isEmpty()) {
            int size = queue.size();
            Node head = queue.removeFirst();
            if (head.left != null) {
                queue.add(head.left);
            }
            if (head.right != null) {
                queue.add(head.right);
            }
            for (int i = 1; i < size; i++) {
                head.next = queue.removeFirst();
                head = head.next;
                if (head.left != null) {
                    queue.add(head.left);
                }
                if (head.right != null) {
                    queue.add(head.right);
                }
            }

        }
        return root;
    }
}

141. 环形链表

题目

给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

示例 1:
在这里插入图片描述

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

在这里插入图片描述

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:
在这里插入图片描述

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

提示:

  • 链表中节点的数目范围是 [0, 104]
  • -105 <= Node.val <= 105
  • pos 为 -1 或者链表中的一个 有效索引 。

进阶: 你能用 O(1)(即,常量)内存解决此问题吗?

思路

快慢指针,会的就会。
可以理解为两个人操场跑圈,有快有慢就会套圈

答案

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        ListNode fast = head, slow = head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow) {
                return true;
            }
        }
        return false;
    }
}

142. 环形链表 II

题目

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

示例 1:
在这里插入图片描述

输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:
在这里插入图片描述

输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:
在这里插入图片描述

输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。

提示:

  • 链表中节点的数目范围在范围 [0, 104] 内
  • -105 <= Node.val <= 105
  • pos 的值为 -1 或者链表中的一个有效索引

进阶: 你是否可以使用 O(1) 空间解决此题?

思路

其实是个数学问题
设:入环之前的长度为x,环长度为y。第一次相遇节点距离头结点距离为k
第一次相遇:快的跑了,慢的二倍的距离。(x + ny + k)= 2 (x + my + k).
化简下,(n-2m) * y-k = x,(n-2m-1)* y + (y - k) = x
第一次相遇之后,把快的挪到开头,走x时候,慢的在圈内绕n - 2m - 1 圈,最后再交点相遇

答案

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast = head, slow = head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow) {
                break;
            }
        }
        if (fast == null || fast.next == null) {
            return null;
        }

        fast = head;
        while (fast != slow) {
            fast = fast.next;
            slow = slow.next;
        }
        return slow;

    }
}

2024.04.03

143. 重排链表

题目

给定一个单链表 L 的头节点 head ,单链表 L 表示为:

L0 → L1 → … → Ln - 1 → Ln

请将其重新排列后变为:

L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …

不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

示例 1:
在这里插入图片描述

输入:head = [1,2,3,4]
输出:[1,4,2,3]

示例 2:
在这里插入图片描述

输入:head = [1,2,3,4,5]
输出:[1,5,2,4,3]

提示:

  • 链表的长度范围为 [1, 5 * 104]
  • 1 <= node.val <= 1000

思路

老套路,拆、翻、合

  1. 找到后一半的节点
  2. 后一半翻转(记得联系下两种翻转)
  3. 合并

重点说下奇数个怎么取,其实前面多一个或者后面多一个都行,只要想清楚

L1 → L2 → L3 → L4 → L5
L1 → L2 __ L3 → L4 → L5或者 L1 → L2 → L3__L4 → L5
L1 → L2 __ L5 → L4 → L3或者 L1 → L2 → L3__L5 → L4
L1 → L5 → L2 → L4 → L3或者 L1 → L5 → L2→ L4 → L3
我们这里用好好理解的L1 → L2 → L3__L5 → L4

答案



/**
 * 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; }
 * }
 */
class Solution {
    public void reorderList(ListNode head) {
        ListNode midPre = findMidPre(head);
        ListNode l2 = reverse(midPre.next);
        midPre.next = null;
        ListNode l1 = head;
        merge(l1, l2);
    }

    // find一半 L1 → L2 → L3__L5 → L4
	public ListNode findMidPre(ListNode head) {
        ListNode fast = head, slow = head;
        while (fast.next != null && fast.next.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        return slow;
    }

    public ListNode reverse(ListNode head) {
        ListNode c = head, next = head;
        while (c != null && c.next != null && next != null) {
            next = c.next.next;
            c.next.next = head;
            head = c.next;
            c.next = next;
        }
        return head;
    }

    public ListNode merge(ListNode l1, ListNode l2) {
        ListNode dummy = new ListNode();
        ListNode c = dummy;
        while (l1 != null || l2 != null) {
            if (l1 != null) {
                c.next = l1;
                l1 = l1.next;
                c = c.next;
            }
            if (l2 != null) {
                c.next = l2;
                l2 = l2.next;
                c = c.next;
            }
        }
        return dummy.next;
    }
}

147. 对链表进行插入排序

题目

给定单个链表的头 head ,使用 插入排序 对链表进行排序,并返回 排序后链表的头 。
插入排序 算法的步骤:

  1. 插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
  2. 每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
  3. 重复直到所有输入数据插入完为止。

下面是插入排序算法的一个图形示例。部分排序的列表(黑色)最初只包含列表中的第一个元素。每次迭代时,从输入数据中删除一个元素(红色),并就地插入已排序的列表中。

对链表进行插入排序。

示例 1:
在这里插入图片描述

输入: head = [4,2,1,3]
输出: [1,2,3,4]

示例 2:
在这里插入图片描述

输入: head = [-1,5,3,4,0]
输出: [-1,0,3,4,5]

提示:

  • 列表中的节点数在 [1, 5000]范围内
  • -5000 <= Node.val <= 5000

思路

就是插入排序的思路,区别就是,要处理节点
把处理的节点断开,然后连接到插入位置的前后(断、连、连)

答案

/**
 * 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; }
 * }
 */
class Solution {
    public ListNode insertionSortList(ListNode head) {
        ListNode dummy = new ListNode();
        // 遍历
        while (head != null) {
            ListNode c = dummy;
            // c.next != null,表示要插入到最后。同时也处理了第一个元素的插入
            while (c.next != null && c.next.val < head.val) {
            	// 找到要插入的位置
                c = c.next;
            }
            // 拿到断之后的节点,方便后续操作(断)
            ListNode next = head.next;
            // 连接后继节点(连)
            head.next = c.next;
            // 链接前驱节点(连)
            c.next = head;
            // 移动head
            head = next;
        }
        return dummy.next;
    }
}

148. 排序链表

题目

给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。

示例 1:
在这里插入图片描述

输入:head = [4,2,1,3]
输出:[1,2,3,4]

示例 2:
在这里插入图片描述

输入:head = [-1,5,3,4,0]
输出:[-1,0,3,4,5]

示例 3:

输入:head = []
输出:[]

提示:

  • 链表中节点的数目在范围 [0, 5 * 104] 内
  • -105 <= Node.val <= 105

进阶: 你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?

思路

上一题的插入排序是O(n平方)
O(n log n)的算法,快排、归并
归并更适合链表,此处选择归并

  1. 找到中间节点
  2. 最小粒度返回
  3. 归并

答案

/**
 * 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; }
 * }
 */
class Solution {
    public ListNode sortList(ListNode head) {
    	// 递归的base条件,一个元素或者一个元素都没有,就不用排了
        if (head == null || head.next == null) {
            return head;
        }
        // 拆分成两个
        ListNode l1 = head;
        ListNode l1Tail = getMiddlePre(head);

        ListNode l2 = l1Tail.next;
        l1Tail.next = null;
		// 分别排序
        ListNode sortList1 = sortList(l1);
        ListNode sortList2 = sortList(l2);
        // 合并
        return merge(sortList1, sortList2);
    }

	// 同之前
    public ListNode getMiddlePre(ListNode head) {
        ListNode fast = head, slow = head;
        while (fast.next != null && fast.next.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        return slow;
    }
	// 小的在前面,最后如果某一个排完了,就直接把没排完的链接到后面
    public ListNode merge(ListNode l1, ListNode l2) {
        ListNode dummy = new ListNode();
        ListNode c = dummy;
        while (l1 != null && l2 != null) {
            if (l1.val < l2.val) {
                c.next = l1;
                l1 = l1.next;
            } else {
                c.next = l2;
                l2 = l2.next;
            }
            c = c.next;
        }
        if (l1 == null) {
            c.next = l2;
        } else {
            c.next = l1;
        }
        return dummy.next;
    }
}

203. 移除链表元素

题目

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。

示例 1:
在这里插入图片描述

输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]

示例 2:

输入:head = [], val = 1
输出:[]

示例 3:

输入:head = [7,7,7,7], val = 7
输出:[]

提示:

  • 列表中的节点数目在范围 [0, 104] 内
  • 1 <= Node.val <= 50
  • 0 <= val <= 50

思路

  1. 删除的节点可能是第一个,所以有dummy节点
  2. 下一个节点,值一样就删除
  3. 否则,遍历到下一个
  4. 删除节点,所以要拿pre判断后面的

###答案

/**
 * 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; }
 * }
 */
class Solution {
    public ListNode removeElements(ListNode head, int val) {
    	// 虚拟头结点
        ListNode dummy = new ListNode(-1, head);
        // 遍历的当前节点
        ListNode current = dummy;
        // 因为判断的是next的值,所以终止条件也为next(为什么用next判断,是因为要删除节点,需要拿头结点删除)
        while (current.next != null) {
            if (current.next.val == val) {
            	// next 删除,相当于移动了一位,不需要再移动了
                current.next = current.next.next;
            } else {
                current = current.next;
            }
        }
        return dummy.next;
    }
}

206. 反转链表

题目

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例 1:
在这里插入图片描述

输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

示例 2:
在这里插入图片描述

输入:head = [1,2]
输出:[2,1]

示例 3:

输入:head = []
输出:[]

提示:

  • 链表中节点的数目范围是 [0, 5000]
  • -5000 <= Node.val <= 5000

进阶: 链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?

思路

  1. 原地翻转
  2. 递归翻转

详细思路见代码注释
就算背也得背下来

答案

/**
 * 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; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        return iterationReverse(head);
    }
	// 递归
    public ListNode recursionReverse(ListNode head) {
    	// 递归第一个要考虑的就是跳出条件(base条件)
    	// head == null 为了兼容,上来就是空
        if (head == null || head.next == null) {
            return head;
        }
		// 可以看到每次返回的,都是上次递归的值,最后一次递归的值是尾节点。
		// 所以返回了,翻转之前的尾结点,就是现在的头结点
        ListNode newHead = recursionReverse(head.next);
        // 下面就是怎么翻转
        // 原始1->2->3->4
        // 1->2->3<->4(从后向前,把->变为<->)
        head.next.next = head;
        // 1->2->3<-4(从后向前,把<->变为<-)
        head.next = null;
        return newHead;

    }
	
	// 原地
    public ListNode iterationReverse(ListNode head) {
        ListNode current = head, next = head;
        // 也是为了兼容
        // 可以这么记:1->2->3->4
        // 翻转一次2->1->3->4
        // current = 1, head = 2
        // 现在要做的3指向2
        // 即current.next.next = head;
        
        while (current != null && current.next != null) {
      		// 为了不让后面的丢失,先用next记住后面的
            next = current.next.next;
            // 核心操作(注意是指向head,不是current)
            current.next.next = head;
            // 调整head为新的head
            head = current.next;
            // 调整next
            current.next = next;
            // 为了好记,next = current.next.next;current.next.next = head;head = current.next;current.next = next;
            // 可以发现,首位相连成环(我愿称之为代码的美感,记录、调整、调整、调整)
        }
        return head;
    }
}

234. 回文链表

题目

给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。

示例 1:
在这里插入图片描述

输入:head = [1,2,2,1]
输出:true

示例 2:
在这里插入图片描述

输入:head = [1,2]
输出:false

提示:

  • 链表中节点数目在范围[1, 105] 内
  • 0 <= Node.val <= 9

进阶: 你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?

思路

  1. 找到中间节点的前驱结点(奇数时候,前面多或者后面多都行,在遍历对比时兼容)
  2. 拆分
  3. 翻转
  4. 一起遍历对比

答案

/**
 * 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; }
 * }
 */
class Solution {
    public boolean isPalindrome(ListNode head) {
        if (head == null) {
            return true;
        }
        ListNode middlePre = getMiddlePre(head);
        ListNode tailList = middlePre.next;
        middlePre.next = null;
        ListNode l2Head = iterationReverse(tailList);
        return check(head, l2Head);
    }
	// 拆分,奇数个节点的时候,前面多一个
    private ListNode getMiddlePre(ListNode head) {
        ListNode fast = head, slow = head;
        while (fast.next != null && fast.next.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        return slow;
    }
	// 原地翻转,试试上面的口诀
    private ListNode iterationReverse(ListNode head) {
        ListNode current = head, next = head;
        while (current != null && current.next != null) {
            next = current.next.next;
            current.next.next = head;
            head = current.next;
            current.next = next;
        }
        return head;
    }
	// 比较,由于前面的多一个,最后可能是2个都遍历完了(偶数个),或者第一个多一个(奇数个节点)
    private boolean check(ListNode l1, ListNode l2) {
        while (l1 != null && l2 != null) {
            if (l1.val != l2.val) {
                return false;
            }
            l1 = l1.next;
            l2 = l2.next;
        }
        return l1 == null || l1.next == null;
    }
}

328. 奇偶链表

题目

给定单链表的头节点 head ,将所有索引为奇数的节点和索引为偶数的节点分别组合在一起,然后返回重新排序的列表。
第一个节点的索引被认为是 奇数 , 第二个节点的索引为 偶数 ,以此类推。
请注意,偶数组和奇数组内部的相对顺序应该与输入时保持一致。
你必须在 O(1) 的额外空间复杂度和 O(n) 的时间复杂度下解决这个问题。

示例 1:
在这里插入图片描述

输入: head = [1,2,3,4,5]
输出: [1,3,5,2,4]

示例 2:
在这里插入图片描述

输入: head = [2,1,3,5,6,4,7]
输出: [2,3,6,7,1,5,4]

提示:

  • n == 链表中的节点数
  • 0 <= n <= 104
  • -106 <= Node.val <= 106

思路

  1. 搞两个头结点,一个奇数的,一个偶数的
  2. 分别连接
  3. 最后合并

答案

/**
 * 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; }
 * }
 */
class Solution {
    public ListNode oddEvenList(ListNode head) {
    	// 防止next为null,所以兼容特殊情况(空和单一节点)
        if (head == null || head.next == null) {
            return head;
        }
        // 奇数index的头和现在指针
        ListNode oddHead = head, oddCurrent = oddHead;
        // 偶数index的头和现在指针
        ListNode evenHead = head.next, evenCurrent = evenHead;
		
		// 第三个开始遍历
        ListNode current = evenHead.next;
        while (current != null) {
        	// 链接奇数
            oddCurrent.next = current;
            oddCurrent = oddCurrent.next;
			// 1->2->3->4,current是3,next是4,4的next是null,不会成环
			// 1->2->3->4->5,current是5,next是null,正好把上一个4的next变为null
	
			// 移动
            current = current.next;

			// 连接偶数,这里有个特别考虑
			// 1->2->3->4,current是4
			// 如果节点个数是偶数(最后一个节点next为null,不需处理)
			// 1->2->3->4->5,current是null
			// 如果节点个数是奇数(最后一个偶数节点的next是最后一个奇数节点,这个时候需要断开,此时current正好是null可以兼容)
            evenCurrent.next = current;
            evenCurrent = evenCurrent.next;
            if (current != null) {
                current = current.next;
            }
        }
        
        oddCurrent.next = evenHead;
        return oddHead;
    }
}

445. 两数相加 II

题目

给你两个 非空 链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。
你可以假设除了数字 0 之外,这两个数字都不会以零开头。

示例1:
在这里插入图片描述

输入:l1 = [7,2,4,3], l2 = [5,6,4]
输出:[7,8,0,7]

示例2:

输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[8,0,7]

示例3:

输入:l1 = [0], l2 = [0]
输出:[0]

提示:

  • 链表的长度范围为 [1, 100]
  • 0 <= node.val <= 9
  • 输入数据保证链表代表的数字无前导 0

进阶: 如果输入链表不能翻转该如何解决?

思路

  1. 翻转,然后思路同《两数相加》
  2. 不让翻,又得翻。用栈或者递归(系统栈)
/**
 * 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; }
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        return recursion(l1, l2);
    }
	// 系统栈翻
	// 高位补0,7->2->4->3和5->6->4。变为7->2->4->3和0->5->6->4
    private ListNode recursion(ListNode l1, ListNode l2) {
        int l1Size = 0, l2Size = 0;
        ListNode c1 = l1, c2 = l2;
        while (c1 != null || c2 != null) {
            if (c1 != null) {
                l1Size++;
                c1 = c1.next;
            }
            if (c2 != null) {
                l2Size++;
                c2 = c2.next;
            }
        }
        if (l1Size > l2Size) {
            for (int i = l2Size; i < l1Size; i++) {
                l2 = new ListNode(0, l2);
            }
        } else if (l1Size < l2Size) {
            for (int i = l1Size; i < l2Size; i++) {
                l1 = new ListNode(0, l1);
            }
        }
        // 以上补0
        ListNode result = recursionHelper(l1, l2);
        // 因为头结点的进位还没处理,这里补一个
        if (result.val > 9) {
            int positive = result.val / 10;
            result.val = result.val % 10;
            return new ListNode(positive, result);
        }
        return result;
    }
    // 位数相同递归,一个返回值,要表达进位和当前节点。
    private ListNode recursionHelper(ListNode l1, ListNode l2) {
        if (l1.next == null) {
            return new ListNode(l1.val + l2.val);
        }
        // 递归调到最后一位
        ListNode node = recursionHelper(l1.next, l2.next);
        // 因为无法表示进位,进位在这里处理
        int lastValue = node.val;
        node.val = lastValue % 10;
        // 进位和当前节点
        return new ListNode(l1.val + l2.val + lastValue / 10, node);
    }
	// 系统栈,思路清晰,简单
    private ListNode stack(ListNode l1, ListNode l2) {
        List<ListNode> stack1 = new LinkedList<>();
        List<ListNode> stack2 = new LinkedList<>();
        while (l1 != null || l2 != null) {
            if (l1 != null) {
            	// linkedlist插入头结点比较效率高,同时也从头结点移除
                stack1.addFirst(l1);
                l1 = l1.next;
            }
            if (l2 != null) {
                stack2.addFirst(l2);
                l2 = l2.next;
            }
        }

        int positive = 0;
        ListNode last = null, current = null;
        // 进位、两个栈任意一个不为空则继续处理
        while (positive != 0 || !stack1.isEmpty() || !stack2.isEmpty()) {
            int l1Value = 0;
            if (!stack1.isEmpty()) {
                l1Value = stack1.removeFirst().val;
            }
            int l2Value = 0;
            if (!stack2.isEmpty()) {
                l2Value = stack2.removeFirst().val;
            }
            int value = l1Value + l2Value + positive;
            current = new ListNode(value % 10, last);
            last = current;
            positive = value / 10;
        }
        return current;
    }
}

2074. 反转偶数长度组的节点

题目

给你一个链表的头节点 head 。

链表中的节点 按顺序 划分成若干 非空 组,这些非空组的长度构成一个自然数序列(1, 2, 3, 4, …)。一个组的 长度 就是组中分配到的节点数目。换句话说:

  • 节点 1 分配给第一组
  • 节点 2 和 3 分配给第二组
  • 节点 4、5 和 6 分配给第三组,以此类推

注意,最后一组的长度可能小于或者等于 1 + 倒数第二组的长度 。

反转 每个 偶数 长度组中的节点,并返回修改后链表的头节点 head 。

示例 1:
在这里插入图片描述

输入:head = [5,2,6,3,9,1,7,3,8,4]
输出:[5,6,2,3,9,1,4,8,3,7]
解释:

  • 第一组长度为 1 ,奇数,没有发生反转。
  • 第二组长度为 2 ,偶数,节点反转。
  • 第三组长度为 3 ,奇数,没有发生反转。
  • 最后一组长度为 4 ,偶数,节点反转。

示例 2:
在这里插入图片描述

输入:head = [1,1,0,6]
输出:[1,0,1,6]
解释:

  • 第一组长度为 1 ,没有发生反转。
  • 第二组长度为 2 ,节点反转。
  • 最后一组长度为 1 ,没有发生反转。

示例 3:
在这里插入图片描述

输入:head = [2,1]
输出:[2,1]
解释:

  • 第一组长度为 1 ,没有发生反转。
  • 最后一组长度为 1 ,没有发生反转。

提示:

  • 链表中节点数目范围是 [1, 105]
  • 0 <= Node.val <= 105

思路

  1. 先拆成长度1->2->3->剩余
  2. 如果数组长度为偶数
  3. 拿pre,next
  4. 翻转
  5. 链接pre和next
  6. 继续

答案

/**
 * 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; }
 * }
 */
class Solution {
    public ListNode reverseEvenLengthGroups(ListNode head) {
        int length = 1;
        // 因为需要拿pre,方便后续重新连接,所以需要dummy
        ListNode dummy = new ListNode(-1, head);
        ListNode current = dummy;
		
		// 遍历
        while (current != null) {
            ListNode pre = current;
            // 记录当前链表长度
            int l = 0;
            // current.next是因为每次遍历都是指到尾结点。统一长度处理
            for (int i = 0; i < length && current.next != null; i++) {
                current = current.next;
                l++;
            }
            length++;
            // 链表长度为偶数。两种情况,1. 偶数位置,2.奇数位置元素个数未满
            if ((l & 1) == 0) {
                ListNode next = current.next;
                ListNode tail = pre.next;
                current.next = null;
                ListNode newHead = iteration(tail);
                // 可能遍历到头了,兼容下
                if (tail != null)
                    tail.next = next;
                pre.next = newHead;
                current = tail;
            }
        }
        return dummy.next;
    }

    private ListNode iteration(ListNode head) {
        ListNode current = head, next = head;
        while (current != null && current.next != null) {
            next = current.next.next;
            current.next.next = head;
            head = current.next;
            current.next = next;
        }
        return head;
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值