【算法】day14 链表
1、回文链表 hot
题目:234. 回文链表 - 力扣(LeetCode)

分析:
法一:先放到数组,再双指针对比。时间复杂度 O(n+1/2n)=O(n),空间复杂度 O(n)。
法二,递归:先递归判断两边是不是回文,递归出来再判断中间是不是回文。先记录 head,再从 head 开始递归到 tail,tail 与 head 层层从边缘到中间比较。时间复杂度:递归遍历 O(n),再双向遍历 O(2n),共 O(n)。空间复杂度:栈深度 O(n)。
法三:先快慢指针获取中间节点,再翻转后半部分节点,对比完了再翻转回去。时间复杂度 O(获取中点1/2n+翻转1/2n+对比1/2n+翻转1/2n) = O(n),空间复杂度 O(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 {private ListNode front; // 记录左节点public boolean isPalindrome(ListNode head) {front = head;return isCycle(head);}public boolean isCycle(ListNode cur) {if (cur == null) return true; // 递归结束条件if (!isCycle(cur.next)) return false; // 靠两边的节点不满足回文,返回 falseif (front.val != cur.val) return false; // 靠中间的不满足回文,返回 falsefront = front.next; // 更新左节点,比较下一对return true;}
}
法三:
/*** 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) {// 1. 快慢指针获取中间节点ListNode middle = getMiddle(head);// 2. 把中间结点后的链表翻转reserve(middle);// 3. 对比ListNode t1 = head, t2 = middle.next;boolean flag = true;while (t2 != null) {if (t1.val != t2.val) {flag = false;break;}t1 = t1.next;t2 = t2.next;}// 4. 把链表翻转回去,恢复原样reserve(middle);return flag;}public ListNode getMiddle (ListNode head) {if (head == null || head.next == null) return head;ListNode slow = head, fast = head.next;while (fast != null && fast.next != null) {slow = slow.next;fast = fast.next.next;}return slow;}public void reserve (ListNode head) {ListNode t = head.next;head.next = null;while (t != null) {ListNode next = t.next;t.next = head.next;head.next = t;t = next;}}
}
2、随机链表的复制 hot
题目:138. 随机链表的复制 - 力扣(LeetCode)


分析:对于每个节点,我们需要访问并创建其下一个节点和随机节点,并插入新链表中。但是因为是随机节点,所以我们并不知道下一个节点/随机节点是否已经在之前被创建,如果不进行判断就会创建并连接多余的节点。当然也可以不判断,先遍历一遍创建完整链表,把旧-新节点对放到 map;再遍历一遍每个节点的随机节点,从 map 中取出对应的新节点进行连接。
时间复杂度 O(2n) = O(n),空间复杂度 哈希表 O(n)。
代码:
/*
// Definition for a Node.
class Node {int val;Node next;Node random;public Node(int val) {this.val = val;this.next = null;this.random = null;}
}
*/class Solution {public Node copyRandomList(Node head) {Map<Node, Node> map = new HashMap<>();// 1. 创建正常链表Node t = head;Node newHead = new Node(0), tail = newHead;while (t != null) {Node node = new Node(t.val);map.put(t, node);tail.next = node;tail = tail.next;t = t.next;}// 2. 连接随机节点t = head;tail = newHead.next;while (t != null) {Node node = map.get(t.random);tail.random = node;tail = tail.next;t = t.next;}return newHead.next;}
}
3、LRU 缓存 hot
题目:146. LRU 缓存 - 力扣(LeetCode)

分析:哈希表(频繁查找 key 对应的节点 O(1))+双向链表(频繁删除尾节点,单链表无法获取尾节点前驱)删插都是 O(1)。
- LRUCache(int capacity):初始化容量、双向链表。
- int get(int key):哈希表里查 key 对应的 node,没有则返回 -1;有则将其移动到链头(最近使用),并返回 value。
- void put(int key, int value):哈希表查 key 对应的 node,有则将其移动到链头,并更新 value;没有则插入到链头,如果size超过了容量,就删除链尾最久没有使用的 node。
- 使用空值的头结点、尾节点,方便删除、插入。
代码:
class LRUCache {class DoubleListNode {int key;int val;DoubleListNode pre;DoubleListNode next;public DoubleListNode () {}public DoubleListNode (int key, int val) {this.key = key;this.val = val;}}private DoubleListNode head, tail;private int size, capacity;private Map<Integer, DoubleListNode> map = new HashMap<>();public LRUCache(int capacity) {size = 0;this.capacity = capacity;head = new DoubleListNode();tail = new DoubleListNode();head.next = tail;tail.pre = head;}public int get(int key) {DoubleListNode node = map.get(key);// 存在则移到链头,返回 valif (node != null) {int val = node.val;removeToHead(node);return val;}// 不存在,返回 -1return -1;}public void put(int key, int val) {DoubleListNode node = map.get(key);// 如果存在,则更新值,并移到链头if (node != null) {node.val = val;removeToHead(node);return;}// 不存在,则创建新节点插到链头DoubleListNode newNode = new DoubleListNode(key, val);map.put(key, newNode);insertToHead(newNode);size++;// 如果链表大小超过容量,删除尾部最久没使用的if (size > capacity) {deleteTail();size--;}}public void removeToHead(DoubleListNode node) {delete(node);insertToHead(node);}public void delete(DoubleListNode node) {DoubleListNode pre = node.pre;DoubleListNode next = node.next;pre.next = next;next.pre = pre;}public void insertToHead(DoubleListNode node) {node.next = head.next;node.next.pre = node;head.next = node;node.pre = head;}public void deleteTail() {DoubleListNode deleteNode = tail.pre;DoubleListNode pre = deleteNode.pre;map.remove(deleteNode.key);pre.next = tail;tail.pre = pre;}
}/*** Your LRUCache object will be instantiated and called as such:* LRUCache obj = new LRUCache(capacity);* int param_1 = obj.get(key);* obj.put(key,value);*/