链表总结
个人看法
既然是总结,那我们就不满足于解出某道题,而是要把我们做过的所有链表题的相关方法和思想都总结一下,看看有没有什么万用的思想或者代码模板,至少要做到遇到新题的时候能有一些思路来做。
实际上我觉得链表的东西更多的就是理解链表指针的移动过程,实在不行也可以画图试试。
牛客的面试必考题还是比较经典的,建议全做,至少要做大半。
知识点
牛客-面试必考真题
- 双指针:不仅仅有像链表双循环的双指针,这里更多指的是快慢指针算法,常用于判断链表是否有环,找到链表的中间节点,确定链表的倒数第k个节点,找到链表相交的节点,合并两个链表等等。
- 排序:插入排序,或者分奇偶排序等等。
- 数学:利用链表做数学知识比如加法什么的
- 堆:一般出现第K大(小)的时候第一想法就是用堆,java就是用优先队列去做。
模板(建议背下来)
- 反转链表,可
- 链表是否有环
- 链表倒数第k个节点
- 链表中间节点(也可以引申为1/3节点)
- 遇到需要更改头节点或者要生成新的链表的题,一定要首先设定哑节点(火车头节点)dummy。
反转链表
双指针
public class Solution { public ListNode ReverseList(ListNode head) { ListNode pre = null; while (head != null) { ListNode curr = head.next; head.next = pre; pre = head; head = curr; } return pre; } }
附加题可以做做在指定区间反转。
public class Solution { /** * * @param head ListNode类 * @param m int整型 * @param n int整型 * @return ListNode类 */ public ListNode reverseBetween (ListNode head, int m, int n) { // write code here ListNode dummy = new ListNode(-1); dummy.next = head; ListNode pre = dummy; ListNode curr = head; for (int i = 1; i < m; i++){ pre = curr; curr = curr.next; } for (int i = 0; i < n - m; i++) { ListNode temp = curr.next; curr.next = temp.next; temp.next = pre.next; pre.next = temp; } return dummy.next; } }
链表是否有环
快慢指针
public class Solution { public boolean hasCycle(ListNode head) { if (head == null){ return false; } ListNode slow = head; ListNode fast = head; while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; if (slow == fast) { return true; } } return false; } }
这个题有一个附加题就是求链表环的入口节点, 可以看看题解什么的手推一下过程。
public class Solution { public ListNode detectCycle(ListNode head) { ListNode fast = head; ListNode slow = head; while (fast != null && fast.next != null) { fast = fast.next.next; slow = slow.next; if (fast == slow) { slow = head; while (slow != fast) { slow = slow.next; fast = fast.next; } break; } } if (fast == null || fast.next == null) { return null; } return fast; } }
链表倒数第k个节点
双指针,一个指针先走k步就可以了
public class Solution { /** * * @param head ListNode类 * @param n int整型 * @return ListNode类 */ public ListNode removeNthFromEnd (ListNode head, int n) { // write code here ListNode dummy = new ListNode(-1); dummy.next = head; ListNode fast = head; ListNode slow = dummy; for (int i = 0; i < n; i++) { fast = fast.next; } while (fast != null) { fast = fast.next; slow = slow.next; } slow.next = slow.next.next; return dummy.next; } }
链表中间节点
slow,fast快慢指针,slow走1步,fast走两步,fast走到链表结尾,slow走到中间节点。1/3节点自然就是fast走3步。
class Solution { public ListNode middleNode(ListNode head) { ListNode slow = head; ListNode fast = head; ListNode pre = null; while(fast != null && fast.next != null){ pre = slow; slow = slow.next; fast = fast.next.next; } return slow; } }
dummy节点声明
ListNode dummy = new ListNode(-1); dummy.next = head;
大概能想到的总结的就这么多,之后再想到什么就往文章补。
未完待续...