1、两个单链表求和
445. 两数相加 II
双栈法更好理解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| public static ListNode addTwoNumbers(ListNode l1, ListNode l2) { LinkedList<ListNode> stack1 = new LinkedList<>(); LinkedList<ListNode> stack2 = new LinkedList<>(); ListNode res = null; while (l1 != null) { stack1.push(l1); l1 = l1.next; } while (l2 != null) { stack2.push(l2); l2 = l2.next; } int carry = 0; while (!stack1.isEmpty() || !stack2.isEmpty() || carry != 0) { int sum = 0; if (!stack1.isEmpty()) { sum += stack1.pop().val; } if (!stack2.isEmpty()) { sum += stack2.pop().val; } sum += carry; carry = sum / 10; ListNode node = new ListNode(sum % 10); node.next = res; res = node; } return res; }
|
将两个链表构造两个栈,使用栈先进后出的特性,做两个链表倒叙做计算,注意进位的情况。
2、单链表非递归翻转,不借助其他数据结构
206. 反转链表
单链表的翻转就是将相邻两个节点的指针翻转。需要借助两个额外的指针用来存储当前节点的前后节点。
1 2 3 4 5 6 7 8 9 10
| public static ListNode reverseList(ListNode head) { ListNode pre = null; while(head!=null){ ListNode temp = head.next; head.next = pre; pre = head; head = temp; } return pre; }
|
3、回文链表(用 O(n) 时间复杂度和 O(1) 空间复杂度)
234. 回文链表
通过快慢指针获取链表中间节点位置,将后半段链表反转,之后迭代前后链表,判断值是否相同。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| public static boolean isPalindrome(ListNode head) { if (head == null || head.next == null) { return true; } ListNode fast = head; ListNode slow = head; while (fast != null && fast.next != null) { fast = fast.next.next; slow = slow.next; } fast = slow; slow = head; ListNode pre = null; while (fast != null) { ListNode temp = fast.next; fast.next = pre; pre = fast; fast = temp; } while (slow != null && pre != null) { if(slow.val!=pre.val){ return false; } slow = slow.next; pre = pre.next; } return true; }
|
4、环形链表
141. 环形链表
检测链表中是否有环,可以使用两种方法:使用HashSet或者快慢指针。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public static boolean hasCycle(ListNode head) { if (head == null || head.next == null) { return false; } ListNode fast = head.next; ListNode slow = head; while (fast != slow) { if (fast == null || fast.next == null) { return false; } fast = fast.next.next; slow = slow.next; } return true; }
|
5、删除链表种所有值为val的节点
203. 移除链表元素
链表移除操作要借助一个哨兵节点,简化删除。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public static ListNode removeElements(ListNode head, int val) { ListNode sentinel = new ListNode(0); sentinel.next = head; ListNode pre = sentinel; while (head != null) { if (head.val == val) { pre.next = head.next; } else { pre = head; } head = head.next; } return sentinel.next; }
|
6、合并两个有序链表
21. 合并两个有序链表
需要使用哨兵节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public static ListNode mergeTwoLists(ListNode l1, ListNode l2) { ListNode head = new ListNode(0); ListNode pre = head; while (l1 != null && l2 != null) { if (l1.val <= l2.val) { pre.next = l1; l1 = l1.next; } else { pre.next = l2; l2 = l2.next; } pre = pre.next; } if (l1 == null) { pre.next = l2; } else { pre.next = l1; } return head.next; }
|
总结
链表常用的算法经常会借助快慢指针和哨兵节点。只要用到哨兵节点head就会有pre节点相配合。