【算法】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)。缺点:因为对原链表进行了修改,所以多线程时对链表的操作要加锁。

代码

法二,递归:

java 复制代码
/**
 * 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; // 靠两边的节点不满足回文,返回 false
        if (front.val != cur.val) return false; // 靠中间的不满足回文,返回 false
        front = front.next; // 更新左节点,比较下一对
        return true;
    }
}

法三:

java 复制代码
/**
 * 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)。

代码

java 复制代码
/*
// 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。
  • 使用空值的头结点、尾节点,方便删除、插入。

代码

java 复制代码
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);
        // 存在则移到链头,返回 val
        if (node != null) {
            int val = node.val;
            removeToHead(node);
            return val;
        }
        // 不存在,返回 -1
        return -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);
 */
相关推荐
2501_9411113316 小时前
C++代码重构实战
开发语言·c++·算法
一叶之秋141216 小时前
从零开始:打造属于你的链式二叉树
数据结构·算法
CoovallyAIHub16 小时前
CV研究告别数据荒?PAN世界模型实现「多步推理与规划」,可自造高质量训练数据
深度学习·算法·计算机视觉
2501_9411116816 小时前
模板编译期哈希计算
开发语言·c++·算法
CoovallyAIHub16 小时前
智能体与小模型:AI迈向平民化的新浪潮
深度学习·算法·计算机视觉
jllllyuz17 小时前
基于粒子群优化(PSO)的特征选择与支持向量机(SVM)分类
开发语言·算法·matlab
啊吧怪不啊吧17 小时前
贪心算法(局部最优实现全局最优)第一篇
算法·贪心算法
Yue丶越17 小时前
【C语言】深入理解指针(四)
java·c语言·算法
Abona18 小时前
自动驾驶、无人机、机器人核心技术双范式
算法·机器人·自动驾驶·无人机