- 链表
- 递归
- 21. 合并两个有序链表">归并 + 双指针21. 合并两个有序链表
- 206. 反转链表">递归 or 双指针206. 反转链表
- 148. 排序链表">迭代归并 + 链表排序148. 排序链表
- 92. 反转链表 II">虚拟节点 + 递归 + 节点保存92. 反转链表 II
- 虚拟节点
- 48. 复杂链表的复刻 - AcWing题库">无需额外空间 48. 复杂链表的复刻 - AcWing题库
- 29. 删除链表中重复的节点 - AcWing题库">虚拟节点+双指针29. 删除链表中重复的节点 - AcWing题库
- 143. 重排链表">保存节点 143. 重排链表
- 19. 删除链表的倒数第 N 个结点">虚拟节点 / 双指针遍历19. 删除链表的倒数第 N 个结点
- 24. 两两交换链表中的节点">双指针+虚拟节点 + 节点保存24. 两两交换链表中的节点
- 25. K 个一组翻转链表">双指针+虚拟节点+一组节点交换25. K 个一组翻转链表
- 147. 对链表进行插入排序">虚拟节点 + 排序 147. 对链表进行插入排序
- 树和链表转换
- 环形链表
- 技巧题
- 剑指 Offer 18. 删除链表的节点 ">伪删除(替换值而不删除节点)剑指 Offer 18. 删除链表的节点
- 66. 两个链表的第一个公共结点">66. 两个链表的第一个公共结点
- 23. 合并K个升序链表">堆+合并23. 合并K个升序链表
链表
链表要注意
- 设计到头节点的更改,可以添加虚拟节点
- 如果设计节点更改,尾指针要设置为null (反转链表)
递归
归并 + 双指针21. 合并两个有序链表
/*** 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 l1, ListNode l2) {//合并有序的两个链表 ,类似于归并排序的思路ListNode cur1 = l1, cur2 = l2;ListNode head = new ListNode();ListNode res = head;while(cur1 != null && cur2 != null){if (cur1.val <= cur2.val) {res.next = new ListNode(cur1.val);res = res.next;cur1 = cur1.next;}else{res.next = new ListNode(cur2.val);res = res.next;cur2 = cur2.next;}}while(cur1 != null){res.next = new ListNode(cur1.val);res = res.next;cur1 = cur1.next;}while(cur2 != null){res.next = new ListNode(cur2.val);res = res.next;cur2 = cur2.next;}return head.next;}}
递归 or 双指针206. 反转链表
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 reverseList(ListNode head) {//递归法:我们直接来看递归最后一次(只有两个节点)的情况:tail = head//head.next.next = head;//head.next = null;//这样,每一次递归都返回反转链表的头节点。因此最后返回的就是整个链表的头节点//需要特判只有一个节点if (head == null || head.next == null)return head;ListNode tail = reverseList(head.next);head.next.next = head;head.next = null;return tail;}}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) {//使用两个指针来判断if (head == null || head.next == null)return head;ListNode curNode = head;ListNode nextNode = head.next;while(nextNode != null){ListNode temp = nextNode.next;nextNode.next = curNode;curNode = nextNode;nextNode = temp;}//最后尾指针要设定head.next = null;return curNode;}}
迭代归并 + 链表排序148. 排序链表
### 解题思路
由于链表不适合使用递归,使用迭代法求归并排序
时间复杂度o(2nlogn >=nlogn) 空间复杂度o(1)在循环内开辟的节点,每次跳出第二层循环 之前的dummy都会被GC,因此只用了一个节点的空间

### 代码
/*** 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) {//存到数组里面,排完序再更新到链表里面 时间复杂度o(n + nlog), space o(N)//如果需要o(N)的空间,使用迭代的归并排序int n = 0;ListNode e = head;while (e != null) {e = e.next; n ++;}//每次归并的长度是ifor (int i = 1; i < n; i *= 2){ListNode dummy = new ListNode(-1);ListNode cur = dummy;ListNode p = head, q, o = head;//每两个区间进行归并处理,表示当前一层的归并for (int j = 1; j <= n; j += i * 2){q = p = o;//p是第一个区间的头节点, q是第二个区间的头节点for (int k = 1;k <= i && q != null; k ++) q = q.next;int l = 0, r = 0;//归并处理while (l < i && r < i && q != null && p != null){if (p.val <= q.val) {cur.next = p;cur = cur.next;p = p.next;l++;}else {cur = cur.next = q; q = q.next;r++;}}while (l < i && p != null){cur = cur.next = p; p = p.next; l++;}while (r < i && q != null){cur = cur.next = q; q = q.next; r++;}o = q;cur.next = null;}head = dummy.next;for (ListNode c = head; c != null; c = c.next)System.out.print(c.val);System.out.println();}return head;}}
虚拟节点 + 递归 + 节点保存92. 反转链表 II
反转链表,可能会需要用到首节点的前一个点,因此使用虚拟节点
递归进行反转
保存开始反转链表首节点st 的前一个节点,尾节点ed的后一个节点

/*** 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 {//记录st的前一个点和ed的后一个点ListNode tmp, tmp1;int left, right;public ListNode reverseBetween(ListNode head, int left, int right) {//递归或者双指针if (head.next == null) return head;this.left = left; this.right = right;ListNode dummy = new ListNode(-1);dummy.next = head;ListNode st = dummy, ed = dummy;for (int i = 0; i < right; i ++){ed = ed.next;if (i < left){tmp1 = st;st = st.next;}}tmp = ed.next;ListNode new_head = reverse(st, ed);tmp1.next = new_head;st.next = tmp;return dummy.next;}//内部反转指向public ListNode reverse(ListNode cur, ListNode ed){if (cur == ed) return cur;ListNode new_head = reverse(cur.next, ed);cur.next.next = cur;cur.next = null;return new_head;}}
虚拟节点
无需额外空间 48. 复杂链表的复刻 - AcWing题库
思路
直接在每个点后面添加这个点复刻,并复制random,最后再将复刻的点分离出来
class Solution {public ListNode copyRandomList(ListNode head) {ListNode cur = head;//复刻节点,random指针不赋值,由于复刻了之后变成了两倍,因此要next两次for (; cur != null; cur = cur.next.next){ListNode n = new ListNode(cur.val);n.next = cur.next;cur.next = n;}//赋值random指针for (cur = head; cur != null; cur = cur.next.next){//如果random指针不为空,ndom指针,让新节点的random指向对应节点的复刻if (cur.random != null)cur.next.random = cur.random.next;}//分离复刻节点,建立dummy虚拟节点为头节点,用于连接复刻链表ListNode dummy = new ListNode(-1);cur = dummy;for (ListNode p = head; p != null; p = p.next){cur.next = p.next;cur = cur.next;//恢复原链表p.next = p.next.next;}return dummy.next;}}
虚拟节点+双指针29. 删除链表中重复的节点 - AcWing题库
思路居然和y总代码思路差不多,感动
定义两个指针 st,ed,同时定义虚拟节点 dummy
st表示重复节点的上一个节点
ed 退出循环的时候表示重复节点的尾节点的下一个节点。在循环中表示重复节点的尾节点
因此,从dummy开始遍历
ed向后移动一格如果
ed和st.next的值相等,并且ed没有遍历的到结尾,ed = ed.next,定义isRepeated = true表示st ~ed有重复值st.next = ed删除重复值,重置标志位
- 如果
ed和st.next的值不相等,st向后移动一格
/*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) { val = x; }* }*/class Solution {public ListNode deleteDuplication(ListNode head) {if (head == null || head.next == null) return head;ListNode dummy = new ListNode(-1);dummy.next = head;ListNode st = dummy, ed = head;boolean isRepeated = false;while (ed != null){ed = ed.next;while (ed != null && ed.val == st.next.val){ed = ed.next;isRepeated = true;}if (isRepeated){st.next = ed;isRepeated = false;}elsest = st.next;}return dummy.next;}}
保存节点 143. 重排链表
思路:
保存节点,但是在实际写代码的过程中总是应为保存变量 tmp 和tmp1和最终循环中止条件设定不好而一直调试。
/**
* 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 st = head, ed = head, tmp = st.next, tmp1 = head;
if (head == null || head.next == null) return;
while (tmp1.next.next != null){
tmp1 = tmp1.next;
}
ed = tmp1.next;
while (st != ed && st.next != ed){
tmp1.next = null;
st.next = ed;
ed.next = tmp;
st = tmp;
ed = tmp1;
tmp = st.next;
tmp1 = st;
if (st == ed) break;
while (tmp1.next != ed){
tmp1 = tmp1.next;
}
System.out.println(st.val+ " " +tmp.val + " "+ tmp1.val+ " " + ed.val );
}
}
}
虚拟节点 / 双指针遍历19. 删除链表的倒数第 N 个结点
思路
删除节点,需要用到节点的前一个点,可以创建一个虚拟节点作为链表的首节点,这样能删除链表实际的第一个节点
遍历统计个数 + 删除
/**
* 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 {
//由于我们要找到删除节点的前一个节点,如果是删除节点是头节点,我们找到的就应该是头节点的前一个节点
//建立一个dummy虚拟节点
//找到倒数k + 1个节点
public ListNode removeNthFromEnd(ListNode head, int n) {
if (head.next == null) return null;
ListNode dummy = new ListNode(-1);
dummy.next = head;
int cnt = 1;
while(head.next != null) {
head = head.next;
cnt ++;
}
int num = cnt - n;
head = dummy;
while(num -- > 0){
head = head.next;
}
head.next = head.next.next;
return dummy.next;
}
}
通过双指针遍历,指针长度就是n + 1,这样当ed为尾节点,st就是头节点的前一个节点
/**
* 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) {
if (head.next == null) return null;
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode st, ed;
st = dummy; ed = dummy;
for (int i = 0; i < n; i ++)
ed = ed.next;
while (ed.next != null){
st = st.next;
ed = ed.next;
}
st.next = st.next.next;
return dummy.next;
}
}
双指针+虚拟节点 + 节点保存24. 两两交换链表中的节点
设计到了头节点的更改,因此添加虚拟节点dummy
- 保存ed.next
- 修改内部指向
- 修改外部指向
- 更新ed 和st

/**
* 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) {
//双指针
if (head == null || head.next == null) return head;
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode st = dummy, ed = dummy.next.next;
while (ed != null){
ListNode tmp = ed.next;
ed.next = st.next;
st.next.next = tmp;
st.next = ed;
st = st.next.next;
if (tmp == null) break;
ed = tmp.next;
}
return dummy.next;
}
}
双指针+虚拟节点+一组节点交换25. K 个一组翻转链表
顺序遍历翻转链表
思路和上一题是一样的,不过是st 和 ed指针中加了 ft 和 sd指针,用于内部的指向反转
- 保存ed.next
- 修改内部指向
- 修改外部指向(保存,新的街尾节点,更新ed指向反转后的结尾节点)
- 更新ed 和st

/**
* 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) {
if (head.next == null) return head;
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode st = dummy, ed = dummy;
//交换多个节点,先将内部的指向翻转
//将两端的外部指向更改
for (int i = 0; i < k; i ++)
ed = ed.next;
while (ed != null){
//内部的更改,还需要两个指针ft,sd
ListNode ft = st.next, sd = ft.next;
//保存ed的next,用于st的指向和移动
ListNode tmp1 = ed.next;
//更改内部的指向
for (int i = 0; i < k - 1; i ++){
ListNode tmp = sd.next;
sd.next = ft;
ft = sd; sd = tmp;
}
//外部更改,重新修改ed 的位置
st.next.next = tmp1;
ListNode tmp2 = st.next;
st.next = ed;
ed = tmp2;
System.out.println(st.next.val + " " + ed.val);
//st,ed移动k步
for (int i = 0; i < k; i ++) {
st = st.next;
if (ed == null) break;
ed = ed.next;
}
}
return dummy.next;
}
}
递归翻转链表
class Solution {
ListNode dummy = new ListNode(-1);
public ListNode reverseKGroup(ListNode head, int k) {
dummy.next = null;
// st和ed为起始值,dummy连接第一次翻转返回的节点
ListNode st = head, ed = head;
if (k == 1) return head;
for (int i = 0; i < k - 1 && ed.next != null; i ++) ed = ed.next;
ListNode nst = st, ned = ed;
while (ed != null) {
// 保存下一次翻转的节点
for (int i = 0; i < k && ned != null; i ++){
ned = ned.next; nst = nst.next;
}
// 将st之后的节点和ed之间的节点翻转, 返回翻转后的末尾节点
ListNode t = ed.next;
reverse(st, ed);
// 否则连接下一次的首节点
if (ned == null) st.next = t;
// 如果下一次也反转,反转后的末尾节点和下一次翻转后的首节点(未反转的末尾节点)
else st.next = ned;
st = nst; ed = ned;
}
return dummy.next;
}
// 每一层翻转st和ed之间的节点, 返回翻转后的尾节点
ListNode reverse(ListNode st, ListNode ed){
// 如果st和ed相邻,返回ed,更新关系
if (st.next == ed) {
ed.next = st;
if (dummy.next == null) dummy.next = ed;
return st;
}
ed = reverse(st.next, ed);
ed.next = st;
return st;
}
}
虚拟节点 + 排序 147. 对链表进行插入排序
思路
新建一个链表:
直接循环链表,对每个点cur处理
在新建链表中,从头开始找,寻找一个比cur大的点的前一个点p,将cur插入到p后面
如果没有找到,此时p是结尾的最后一个点,那么直接将cur插入到p后面
可能会插入到头节点前面,因此可以使用虚拟节点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 insertionSortList(ListNode head) {
/*
新建一个链表:
直接循环链表,对每个点cur处理
在新建链表中,从头开始找,寻找一个比cur大的点的前一个点p,将cur插入到p后面
如果没有找到,此时p是结尾的最后一个点,那么直接将cur插入到p后面
可能会插入到头节点前面,因此可以使用虚拟节点dummy作为新建链表的首节点
*/
ListNode cur = head, p;
ListNode dummy = new ListNode(-1);
for (; cur != null;){
ListNode tmp = cur.next;
for (p = dummy; p.next != null && p.next.val <= cur.val;){
p = p.next;
}
cur.next = p.next;
p.next = cur;
cur = tmp;
}
return dummy.next;
}
}
树和链表转换
链表转二叉树
114. 二叉树展开为链表
第一种方法
使用前序遍历把对应的节点添加到list中,再分别取出每个节点,设置其左右子节点。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public void flatten(TreeNode root) {
List<TreeNode> list = new ArrayList<TreeNode>();
preOrder(root, list);
for (int i = 1; i < list.size(); i++){
TreeNode prev = list.get(i - 1), cur = list.get(i);
prev.left = null;
prev.right = cur;
cur.right = null;
}
}
public void preOrder(TreeNode root, List<TreeNode> list){
if (root != null){
list.add(root);
preOrder(root.left, list);
preOrder(root.right, list);
}
}
}
方法2
时间复杂度 o(n)
空间复杂度o(n)
- 如果当前节点有子树,按照右左的顺序存放子树进栈,记录当前节点root 为 prev
- 把节点pop出栈,连接当前点和父节点prev
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public void flatten(TreeNode root) {
if (root == null) return ;
Stack<TreeNode> stack = new Stack<TreeNode>();
//如果把节点pop出栈,连接当前点和父节点prev
//如果当前节点有子树,按照右左的顺序存放子树进栈,记录当前节点root 为 prev
stack.push(root);
TreeNode prev = null;
while(!stack.isEmpty()){
TreeNode cur = stack.pop();
if (prev != null){
prev.right = cur;
prev.left = null;
}
if (cur.right != null){
stack.push(cur.right);
}
if (cur.left != null)
stack.push(cur.left);
prev = cur;
}
}
}
方法
空间复杂度o(1)
环形链表
LC141. 环形链表
解题思路
快慢指针:fast指针每次移动2步,slow每次移动一步。
这样每次迭代,fast都会比slow多移动一步。
代码
/**
* 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;
if (fast == null || fast.next == null) return false;
while(fast != null){
fast = fast.next; slow = slow.next;
if (fast == null) return false;
fast = fast.next;
if (fast == slow) return true;
}
return false;
}
}
142. 环形链表 II
解题思路
//我们让快慢指针的初始位置更改,让他们第一次相遇的位置就是入口点
//位置定在哪里?
//让慢指针从head开始,它进入入口的时候,快指针也进入入口
//快指针初始位置在那?我们先看到:假设快慢指针同时在起点,他们最终相遇的位置距离入口是y
//当慢指针走了x步进入入口,快指针已经在环走了x步。
//慢指针回退y步,快指针回退2y步。慢指针进入入口的时候,快指针在环-y(== x)的地方
//如果快指针从y点(相遇点)出发(它此时距离入口就是环-y == x,和慢指针同样的距离),
//慢指针从head出发,他们最终相遇就是在入口点
代码
/**
* 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) {
//我们让快慢指针的初始位置更改,让他们第一次相遇的位置就是入口点
//位置定在哪里?
//让慢指针从head开始,它进入入口的时候,快指针也进入入口
//快指针初始位置在那?我们先看到:假设快慢指针同时在起点,他们最终相遇的位置距离入口是y
//当慢指针走了x步进入入口,快指针已经在环走了x步。
//慢指针回退y步,快指针回退2y步。慢指针进入入口的时候,快指针在环-y(== x)的地方
//如果快指针从y点(相遇点)出发(它此时距离入口就是环-y == x,和慢指针同样的距离),
//慢指针从head出发,他们最终相遇就是在入口点
ListNode fast = head, slow = head;
while(fast != null){
fast = fast.next; slow = slow.next;
if (fast == null) return null;
fast = fast.next;
if (fast == slow){
//System.out.println(slow.val);
slow = head;
while(fast != slow){
fast = fast.next; slow = slow.next;
}
return slow;
}
}
return null;
}
}
技巧题
伪删除(替换值而不删除节点)剑指 Offer 18. 删除链表的节点
不用真正删除节点,把下一个节点的值更新成当前值,并删除下一个节点即可
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode deleteNode(ListNode _head, int val) {
ListNode head = _head;
while (head.val != val && head.next.next != null){
head = head.next;
}
if (head.val != val) {
head.next = head.next.next;
return _head;
}
head.val = head.next.val;
head.next = head.next.next;
return _head;
}
}
66. 两个链表的第一个公共结点

null节点是一定要经过的。
当两个点没有重合的时候,A的路径:a->c->null->b->c->null B的路径:b->c->null->a->c->null
最后会在null重合。
两条路径的总步数是一样的。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
class Solution {
public ListNode findFirstCommonNode(ListNode headA, ListNode headB) {
ListNode l1 = headA, l2 = headB;
while (l1 != l2){
if (l1 != null)
l1 = l1.next;
else
l1 = headB;
if (l2 != null)
l2 = l2.next;
else
l2 = headA;
}
return l1;
}
}
堆+合并23. 合并K个升序链表
注意
ListNode dummy = new ListNode(-1), pre = dummy;
dummy是哨兵头,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 mergeKLists(ListNode[] lists) {
//最小堆+合并链表 o(logn * mn)
//如果使用多路归并,o(mnn)
// 插入对logn ,从堆中拿最小值,堆删除这个值,对应的数组下标后移一位,再加入这个值
PriorityQueue<ListNode> heap = new PriorityQueue<>((a, b)->a.val - b.val);
int n = lists.length;
if (n == 0) return null;
ListNode dummy = new ListNode(-1), pre = dummy;
//加入前n个
for (int i = 0; i < n; i ++)
if (lists[i] != null) heap.add(lists[i]);
while (!heap.isEmpty()){
//取出最小元素
ListNode num = heap.poll();
if (num.next != null) heap.add(num.next);
pre.next = num;
pre = num;
}
return dummy.next;
}
}
