LeetCode之链表

141. 环形链表

/**
 * 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) {

        // 思路:使用哈希表,遍历链表并将节点存入哈希表
        // 如果遇到相同的节点,说明链表中有环

        // 初始化当前节点为链表的头节点
        ListNode cur = head;

        // 创建一个哈希集合用于存储已经访问过的节点
        Set<ListNode> set = new HashSet<>();

        // 逐个遍历链表
        while(cur != null) {
            // 将当前节点的下一个节点保存在临时变量中
            ListNode next = cur.next;
            // 如果下一个节点已经存在于哈希集合中,则说明有环
            if (set.contains(next)) {
                // 发现环,返回true
                return true;
            }
            // 将当前节点的下一个节点加入哈希集合
            set.add(next);
            // 移动当前节点到下一个节点
            cur = next;
        }
        // 遍历完链表没有发现环,返回false
        return false;
    }

}

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 {
    // addTwoNumbers 方法用于将两个链表表示的数字相加
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        // 创建一个哨兵节点,方便后续获取结果及遍历
        ListNode pre = new ListNode(0);
        // 定义指针 cur 指向当前节点,初始指向哨兵节点
        ListNode cur = pre;
        // 初始化进位值,范围在 0-1
        int carry = 0;

        // 遍历两个链表,直到它们都为空
        while (l1 != null || l2 != null) {
            // 如果链表 l1 非空,则取其值;否则用 0 代替
            int x = l1 == null ? 0 : l1.val;
            // 如果链表 l2 非空,则取其值;否则用 0 代替
            int y = l2 == null ? 0 : l2.val;

            // 计算当前位的和:两个节点的值加上进位
            int sum = x + y + carry;
            // 更新进位值
            carry = sum / 10; // 计算新的进位
            // 计算当前节点的实际值(个位)
            sum = sum % 10; // 取当前和的个位数

            // 创建新的节点,将其添加到结果链表中
            cur.next = new ListNode(sum);
            // 移动 cur 指针到下一个节点
            cur = cur.next;

            // 移动 l1 指针到下一个节点
            if (l1 != null) {
                l1 = l1.next;
            }
            // 移动 l2 指针到下一个节点
            if (l2 != null) {
                l2 = l2.next;
            }
        }

        // 如果最后还有进位,创建一个新节点,将其添加到结果链表的末尾
        if (carry > 0) {
            cur.next = new ListNode(carry);
        }

        // 返回哨兵节点的下一个节点,即为结果链表的头节点
        return pre.next;
    }
}

21. 合并两个有序链表

/**
 * 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 {

    /**
     * 合并两个有序链表

     * @param l1 第一个有序链表
     * @param l2 第二个有序链表
     * @return 合并后的有序链表
     */
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        // 创建一个虚拟头节点,值为 -1
        ListNode preHead = new ListNode(-1);

        // prev 用于遍历合并后的链表
        ListNode prev = preHead;

        // 当 l1 和 l2 都不为空时
        while (l1 != null && l2 != null) {

            // 如果 l1 的值小于等于 l2 的值
            if (l1.val < l2.val) {
                // 将 l1 节点连接到合并后的链表中
                prev.next = l1;
                // l1 指向下一个节点
                l1 = l1.next;
            } else {
                // 将 l2 节点连接到合并后的链表中
                prev.next = l2;
                // l2 指向下一个节点
                l2 = l2.next;
            }
            // prev 指向下一个节点
            prev = prev.next;
        }

        // 合并后 l1 和 l2 最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可
        prev.next = l1 == null ? l2 : l1;
        // 返回虚拟头节点的下一个节点,即合并后的链表头节点
        return preHead.next;
    }


}

138. 随机链表的复制

/*
// 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 {

    // 方法 copyRandomList 接收链表头节点并返回深拷贝后的链表头节点
    public Node copyRandomList(Node head) {
        // 如果头节点为空,直接返回 null
        if (head == null) {
            return null;
        }
        
        Node cur = head; // 定义当前指针,初始化为链表头
        // 定义哈希表,key 为旧的节点,value 为新的节点
        Map<Node, Node> map = new HashMap<>();

        // 第一次遍历,将旧链表的每个节点和对应的新节点存入哈希表
        while (cur != null) {
            // 创建新节点,并将其与旧节点的映射存入哈希表
            map.put(cur, new Node(cur.val));
            cur = cur.next; // 移动到下一个节点
        }

        cur = head; // 重置当前指针,指向链表头

        // 第二次遍历,构建新链表的 next 和 random 指向
        while (cur != null) {
            // 设置新节点的 next 指向对应的下一个节点
            // 使用哈希表获取新节点的引用
            map.get(cur).next = map.get(cur.next);
            // 设置新节点的 random 指向对应的随机节点
            map.get(cur).random = map.get(cur.random);
            cur = cur.next; // 移动到下一个节点
        }

        // 返回新链表的头节点,通过哈希表获取
        return map.get(head);
    }

}

92. 反转链表 II

/**
 * 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 {
    // 方法 reverseBetween 用于在链表中反转从位置 left 到 right 的节点
    public ListNode reverseBetween(ListNode head, int left, int right) {
        // 创建一个虚拟头节点(dummyNode),为处理边界情况提供便利
        ListNode dummyNode = new ListNode(-1);
        dummyNode.next = head; // 将虚拟节点指向原链表的头
        ListNode pre = dummyNode; // pre 指针初始化为虚拟头节点

        // 找到位置 left 的前一个节点
        for (int i = 0; i < left - 1; i++) {
            pre = pre.next; // 移动 pre 指针
        }

        // cur 指向要反转的第一个节点
        ListNode cur = pre.next;
        ListNode next; // 用于保存下一个节点

        // 开始反转从 left 到 right 的节点
        for (int i = 0; i < right - left; i++) {
            next = cur.next; // 暂存 cur 的下一个节点
            cur.next = next.next; // 弹出 next 节点
            next.next = pre.next; // 将 next 插入到 pre 指针的后面
            pre.next = next; // 更新 pre 的 next 指向插入后的节点
        }

        // 返回新链表的头节点
        return dummyNode.next;
    }
}

19. 删除链表的倒数第 N 个结点

/**
 * 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 removeNthFromEnd(ListNode head, int n) {
        // 1. 定义一个哨兵节点,哨兵节点的 next 指向链表头
        ListNode dummy = new ListNode(0, head);
        
        // 2. 定义一个双端队列,用于存储链表的节点
        Deque<ListNode> stack = new LinkedList<>();
        
        // 3. 创建一个指针 cur 从哨兵节点开始遍历链表
        ListNode cur = dummy;
        
        // 4. 将所有的节点遍历并入栈
        while (cur != null) {
            stack.push(cur); // 将当前节点压入栈中
            cur = cur.next; // 移动到下一个节点
        }
        
        // 5. 出栈 n 次,找到倒数第 n 个节点的前一个节点
        for (int i = 0; i < n; i++) {
            stack.pop(); // 弹出栈顶节点
        }
        
        // 6. peek 获取当前栈顶元素,即倒数第 n 个节点的前一个节点
        ListNode pre = stack.peek(); 
        
        // 7. 将 pre 的 next 指向下下一个节点,跳过要删除的节点
        pre.next = pre.next.next;

        // 8. 返回哨兵节点的后续节点,这即是新的链表头
        return dummy.next; 
    }
}

82. 删除排序链表中的重复元素 II

/**
 * 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 deleteDuplicates(ListNode head) {
        // 1. 如果链表为空,直接返回
        if (head == null) {
            return head;
        }

        // 2. 创建一个哨兵节点,指向链表头
        ListNode dummy = new ListNode(0, head);
        ListNode cur = dummy; // 当前指针,初始化为哨兵节点

        // 3. 遍历链表
        while (cur.next != null && cur.next.next != null) {
            // 如果当前节点的值与下一个节点的值相同
            if (cur.next.val == cur.next.next.val) {
                int x = cur.next.val; // 记录重复的值
                // 4. 跳过所有与该值相同的节点
                while (cur.next != null && cur.next.val == x) {
                    cur.next = cur.next.next; // 删除当前节点
                }
            } else {
                // 5. 移动到下一个节点
                cur = cur.next;
            }
        }

        // 6. 返回去掉重复节点后链表的头节点
        return dummy.next; // 返回哨兵节点的下一个节点作为新头
    }
}

61. 旋转链表

/**
 * 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 {
    // 将链表向右旋转 k 个位置
    public ListNode rotateRight(ListNode head, int k) {
        // 1. 如果 k 为 0,链表为空或者只有一个节点,则直接返回头节点
        if (k == 0 || head == null || head.next == null) {
            return head;
        }
        
        // 2. 统计链表的长度 n
        int n = 1; // 初始化长度为 1,包含头节点
        ListNode iter = head; // 用于遍历链表
        while (iter.next != null) {
            iter = iter.next; // 移动到下一个节点
            n++; // 长度加 1
        }
        
        // 3. 计算需要连接的位置
        int add = n - k % n; // 计算新的头节点的位置
        if (add == n) {
            return head; // 如果 add 等于 n,表示不需要旋转,直接返回头节点
        }
        
        // 4. 创建环形链表
        iter.next = head; // 连接链表尾到头节点,形成环
        
        // 5. 找到新的尾节点
        while (add-- > 0) {
            iter = iter.next; // 移动到新的尾节点
        }
        
        // 6. 断开环并返回新头节点
        ListNode ret = iter.next; // 新头节点
        iter.next = null; // 断开连接
        return ret; // 返回新头节点
    }
}

86. 分隔链表

/**
 * 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 partition(ListNode head, int x) {
        // 创建一个新节点 small,作为小于 x 的链表的头节点
        ListNode small = new ListNode(0);
        // 记录小于 x 的链表的头节点,方便最后返回
        ListNode smallHead = small;
        // 创建一个新节点 large,作为大于等于 x 的链表的头节点
        ListNode large = new ListNode(0);
        // 记录大于等于 x 的链表的头节点,方便最后连接
        ListNode largeHead = large;
        // 遍历原始链表
        while (head!= null) {
            if (head.val < x) {
                // 如果当前节点值小于 x,将其加入到 small 链表中
                small.next = head;
                small = small.next;
            } else {
                // 如果当前节点值大于等于 x,将其加入到 large 链表中
                large.next = head;
                large = large.next;
            }
            // 移动原始链表的指针
            head = head.next;
        }
        // 将 large 链表的末尾置为 null,防止出现循环引用
        large.next = null;
        // 将 small 链表和 large 链表连接起来
        small.next = largeHead.next;
        // 返回划分后的链表的头节点
        return smallHead.next;
    }
}

146. LRU 缓存

class LRUCache {

    // 定义双向链表
    class DLinkedNode {
        // 存储map的key
        int key;
        // 存储value
        int value;
        // 前驱指针
        DLinkedNode prev;
        // 后继指针
        DLinkedNode next;
        public DLinkedNode() {}

        public DLinkedNode(int key, int value) {
            this.key = key;
            this.value = value;
        }
    }

    // 链表长度
    private int size;
    // 链表容量
    private int capacity;
    // 定义头节点
    private DLinkedNode head;
    // 定义尾节点
    private DLinkedNode tail;

    // 定义哈希表key为key,value为双向链表
    Map<Integer, DLinkedNode> cache = new HashMap<>();


    public LRUCache(int capacity) {
        this.size = 0;
        this.capacity = capacity;
        // 使用伪头部和尾部节点
        head = new DLinkedNode();
        tail = new DLinkedNode();
        head.next = tail;
        tail.prev = head;

    }
    
    // 获取值并将节点移到头部标识刚使用过
    public int get(int key) {
        DLinkedNode node = cache.get(key);
        if (node == null) {
            return -1;
        }
        moveToHead(node);
        return node.value;
    }

    // 移动刚使用的节点到头部
    public void moveToHead(DLinkedNode node) {
        // 将node移除下来
        removeNode(node);
        // 将Node节点加到Head位置
        addToHead(node);
    }

    // 将Node节点加到Head位置
    private void addToHead(DLinkedNode node) {
        node.prev = head;
        node.next = head.next;
        head.next.prev = node;
        head.next = node;
    }

    // 将node移除下来
    private void removeNode(DLinkedNode node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }

   // 移除尾部节点
    public DLinkedNode removeTail() {
        DLinkedNode res = tail.prev;
        removeNode(res);
        return res;
    }
    
    public void put(int key, int value) {
        DLinkedNode node = cache.get(key);
        if (node == null) {
            // 如果key 不存在,创建一个新的节点
            DLinkedNode newNode = new DLinkedNode(key, value);
            // 添加进哈希表
            cache.put(key, newNode);
            // 添加至双向链表的头部
            addToHead(newNode);
            ++size;
            if (size > capacity) {
                // 如果超出容量,删除双向链表的尾部节点
                DLinkedNode tail = removeTail();
                // 删除哈希表中对应的相
                cache.remove(tail.key);
                --size;
            } 
        } else {
                // 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
                node.value = value;
                moveToHead(node);
        }
    }
}

/**
 * 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);
 */

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值