【算法】链表
- 其他
- 2025-09-03 17:33:01

零:链表常用技巧 1:引入虚拟头结点
(1)便于处理边界情况
(2)方便我们对链表操作
2:两步尾插,头插(1)尾插
tail指向最后一个节点,tail.next 指向新尾节点,tail指针在指向新尾节点
(2)头插
新头结点.next = 虚拟头节点newhead.next
虚拟头节点newhead.next = 新头结点.
3:插入节点若有一个指针(next)指向被插入节点后的那个节点,那么这四句代码的顺序随意
反之,前两句必须写在前面(这两句顺序可以互换),后两句必须写在后面
一: 两数相加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) { //先new一个虚拟节点 ListNode newHead = new ListNode(0); ListNode cur1 = l1 , cur2 = l2, cur3 = newHead; int t = 0; while(cur1 != null || cur2 != null || t != 0){ //对第一个链表做加法 if(cur1 != null){ t += cur1.val; cur1 = cur1.next; } //对第二个链表做加法 if(cur2 != null){ t += cur2.val; cur2 = cur2.next; } //移动 cur3.next = new ListNode(t%10); cur3 = cur3.next; t = t/10; } return newHead.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 swapPairs(ListNode head) { if (head == null || head.next == null) { return head; } // 虚拟头结点 ListNode newHead = new ListNode(0); newHead.next = head; // 先搞四个指针 ListNode prev = newHead, cur = prev.next, next = cur.next, nnext = next.next; while (cur != null && next != null) { prev.next = next; next.next = cur; cur.next = nnext; prev = cur; cur = nnext; if (cur != null) { next = cur.next; } if (next != null) { nnext = next.next; } } return newHead.next; } } 三:143. 重排链表(写的酣畅淋漓的一道题 )这道题涵盖了双指针,前插,尾插,合并两个链表,代码调试也很爽,很重要的是我们在变量的选择和名称定义上一定要规范,否则写代码就是一种折磨
/** * 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) { if(head == null || head.next == null || head.next.next == null) return ; // 1:找中间节点 ListNode fast = head; ListNode slow = head; while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; } // 逆序准备工作:新的头结点 ListNode head1 = head; ListNode head2 = new ListNode(0); ListNode cur = slow.next; slow.next = null;// 将上半段尾插null,分开标志 // 2:逆序头插(很明显头插一直要用到的指针是头结点,这是关键),这里逆序链表头结点是虚拟节点注意注意!!!! while (cur != null) { ListNode tmp = cur.next; cur.next = head2.next; head2.next = cur; // cur = cur.next;错的cur更新为tmp,cur.next已经指向null了 cur = tmp; } // 3:合并两个链表(尾插)双指针 ListNode ret = new ListNode(0); ListNode prev = ret; ListNode cur1 = head1, cur2 = head2.next; while (cur1 != null) {// 左半链表长度>=右半 // 先插左 prev.next = cur1; cur1 = cur1.next; prev = prev.next; // 在插右 if (cur2 != null) { prev.next = cur2; cur2 = cur2.next; prev = prev.next; } } } } 四:合并K个升序链表
23. 合并 K 个升序链表
这道题的解法有三种,暴力解法(超时),优先级队列(文章写法),递归(难)
复习了优先级队列的lambda表达式写法,爽,战斗爽!AC爽
/** * 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<>((v1, v2) -> v1.val - v2.val);// 小根堆 // 放入第一批头结点 for (ListNode head : lists) { if (head != null) { queue.offer(head); } } // 合并链表 ListNode ret = new ListNode(0); ListNode cur = ret;// 定义指针 while (!queue.isEmpty()) { ListNode tmp = queue.poll(); cur.next = tmp; cur = cur.next; if (tmp.next != null) { tmp = tmp.next; queue.offer(tmp); } } return ret.next; } } 五:K个一组翻转链表25. K 个一组翻转链表
/** * 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) { // 第一步计算需要逆序多少组 int n = 0; ListNode cur = head; while(cur != null){ n++; cur = cur.next; } n = n/k;//逆序组数 ListNode tmp = head; ListNode ret = new ListNode(0); ListNode prev = ret; while(n != 0){ ListNode move = tmp; int m = k; while(m != 0){ //执行k次头插法 ListNode next = tmp.next; tmp.next = prev.next; prev.next = tmp; tmp = next; m--; } prev = move; n--; } prev.next = tmp; return ret.next; } } 六:相交链表力扣k神的图解,真的nb,太清晰了
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { * val = x; * next = null; * } * } */ public class Solution { public ListNode getIntersectionNode(ListNode headA, ListNode headB) { ListNode A = headA, B = headB; while (A != B) { A = A != null ? A.next : headB; B = B != null ? B.next : headA; } return A; } } 七:回文链表心得:
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) { ListNode mid = midNode(head); ListNode reverseHead = reverseList2(mid); //没有断 while(reverseHead != null){ if(head.val != reverseHead.val){ return false; } head = head.next; reverseHead = reverseHead.next; } return true; } public ListNode midNode(ListNode head){ //快慢双指针 ListNode slow = head; ListNode fast = head; while(fast != null && fast.next != null){ slow = slow.next; fast = fast.next.next; } return slow; } public ListNode reverseList(ListNode head){ //new 虚拟null节点 ListNode pre = null; ListNode cur = head; //头插 while(cur != null){ ListNode tmp = cur.next; cur.next = pre; pre = cur; cur = tmp; } return pre; } public ListNode reverseList2(ListNode head){ //递归的方法,head等不等于null,就是以防给的是空链表 if(head == null || head.next == null){ return head; } ListNode newHead = reverseList(head.next);//新的头结点 head.next.next = head; head.next = null; return newHead; } } 八:环形链表心得:快慢双指针,秒了
/** * 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) { if (head == null) return false; ListNode slow = head, fast = head; while (fast.next != null && fast.next.next != null) { slow = slow.next; fast = fast.next.next; if (slow == fast) { return true; } } return false; } }