2021-03-06 一周刷题心得记录

一周练习题目记录

  • leetcode 206 反转链表
  • leetcode 92 反转链表Ⅱ
  • leetcode 138 复制带随机指针的链表
  • leetcode 2 两数相加
  • leetcode 146 LRU缓存机制

总结回顾

学会思想最重要

leetcode 206 反转链表

题目描述

反转一个单链表。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?

思路回顾详解

  • 首先明确链表的数据结构格式

    • 代码表现如下
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;
    }
}

迭代解法

public class Solution {
    
    
    
    public ListNode reverseListNode(ListNode head) {
    
    
        
        ListNode prev = null;
        ListNode current = head;
        
        while (current != null) {
    
    
            ListNode next = current.next;
            
            current.next = prev;
            
            prev = current;
            current = next;
        }
        
        return current;
    }
    
}

递归解法

public class Solution {
    
    
    public ListNode reverseListNode(ListNode head) {
    
    
        //  递归第一步,判断终止条件
        if(head == null || head.next == null) {
    
    
            return head;
        }
        //  递归第二步,递的过程
        ListNode newHead = reverseListNode(head.next);
        
        head.next.next = head;
        head.next = null;
        
        return newHead;
    }
}

leetcode 92 反转链表Ⅱ

题目描述

反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。

说明:
1 ≤ m ≤ n ≤ 链表长度。

示例:

输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL

迭代解法

  • 首先我们需要反转链表的中间部分,所以返回的还是链表的头节点 head
  • 需要找到反转链表的开始位置。所以要根据tou节点和开始位置去找。即 head = head.next
  • 头节点的位置变动,为了确定返回的时候能够正确地返回头节点,所以需要一个哨兵节点 dummy.next = head
  • 找到开始反转地节点位置之后,根据 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 reverseBetween(ListNode head, int left, int right) {
    
    
        // 首先判断临界情况
        if(head == null ||  left >= right){
    
    
            return head;
        }
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        head = dummy;
        
        for(int i = 1; i < left;i++) {
    
    
            head = head.next;
        }
        
        ListNode preM = head;
        ListNode mNode = head.next;
        ListNode nNode = mNode;
        ListNode behindN = nNode.next;
        
        for(int i = left;i < right;i++) {
    
    
            ListNode next = behindN.next;
            
            behindN.next = nNode;
            
            nNode = behindN;
            behindN = next;
        }
        
        preM.next = nNode;
        mNode.next = behindN;
        
        return dummy.next;
    }
}

递归解法

  • 由简到难探寻递归解法
  • 递归反转整个链表地解法如下
class Solution {
    
    
    public ListNode reverse(ListNode head) {
    
    
       if(head == null || head.next == null) {
    
    
           return head;
       }
       
       ListNode newHead = reverse(head.next);
        
       head.next.next = head;
       head.next = null;
        
       return newHead;
    }
}
  • 那么递归反转链表的前n个节点呢
  • 反转整个链表,头节点变成尾部节点,其next节点指向null。那么反转前n个链表就是第n个节点变成头节点
  • 即 head.next = nNode.next . 代码如下
class Solution {
    
    
    ListNode nNode = null;
    public ListNode reverseN(ListNode head, int n) {
    
    
        if(n == 1) {
    
    
            // 这里  nNode 作为  第n个节点的下一个节点
            nNode = head.next;
            //  返回第 n 个节点
            return head;
        }
        
        ListNode newNode = reverseN(head.next, n-1);
        
        head.next.next = head;
        head.next = nNode.next;
            
        return nNode;
    }
}
  • 那么反转链表的一部分 left-right 只要 left=1 即可满足递归条件
class Solution {
    
    
    public ListNode reverseBetween(ListNode head, int left, int right) {
    
    
        if(left == 1) {
    
    
            return reverseN(head, right);
        }
        
        head.next = reverseBetween(head.next, left-1, right-1);
        
        return head;
    }
}

leetcode 138 复制带随机指针的链表

题目描述

给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。

构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 。

例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 x 和 y ,同样有 x.random --> y 。

返回复制链表的头节点。

用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:

val:一个表示 Node.val 的整数。
random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为  null 。
你的代码 只 接受原链表的头节点 head 作为传入参数。

解法一:使用map集合

  • 我们可以定义一个map集合存储节点为镜像,再为其建立关系
/*
// 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) {
    
    
        if(head == null) {
    
    
            return null;
        }
        Map<Node, Node> copyMap = new HashMap<>();
        
        // 这里为了不破坏链表结构,使用一个复制链表
        Node puppetNode = head;
        
        while(puppetNode != null) {
    
    
            Node newNode = new Node(puppetNode.val);
            copyMap.put(puppetNode, newNode);
            puppetNode = puppetNode.next;
        }
        
        //  建立其映射关系
        puppetNode = head;
        while(puppetNode != null) {
    
    
            if(puppetNode.next != null) {
    
    
                copyMap.get(puppetNode).next = copyMap.get(puppetNode.next);
            }
            
            if(puppetNode.random != null) {
    
    
                copyMap.get(puppetNode).random = copyMap.get(puppetNode.random);
            }
            
            puppetNode = puppetNode.next;
        }
        
        return copyMap.get(head);
    }
}

解法二:回溯法

class Solution {
    
    
    Map<Node, Node> copyMap = new HashMap<>();
    public Node copyRandomList(Node head) {
    
    
        if(head == null) {
    
    
            return null;
        }
        
        //  注意终止条件是返回节点需要在集合中存在
        if(copyMap.containsKey(head)) {
    
    
           return copyMap.get(key);
        }
        //  
        Node newNode = new Node(head.val);
        copyMap.put(head, newNode);
        
        newNode.next = copyRandomList(head.next);
        newNode.random = copyRandomList(head.random);
        
        return copyMap.get(head);
    }
}

解法三:原地复制

  • 给链表的每一个节点后面增加一个其复制节点
  • 然后遍历取出复制节点
class Solution {
    
    
    public Node copyRandomList(Node head) {
    
    
        if(head == null) {
    
    
            return null;
        }
        Node puppetHead = head;
        while (puppetHead != null) {
    
    
            Node newNode = new Node(puppetHead.val);
            Node next = puppetHead.next;
            puppetHead.next = newNode;
            newNode.next = next;
            
            puppetHead = puppetHead.next.next;
        }
        
        puppetHead = head;
        
        //  建立随机指针关系
        while(puppetHead != null) {
    
    
            if(puppetHead.random != null) {
    
    
                puppetHead.next.random = puppetHead.random.next;
            }
            
            puppetHead = puppetHead.next.next;
        }
        
        Node result = head.next;
        Node removeNode = head.next;
        
        while(head != null && head.next != null) {
    
    
            head.next = head.next.next;
            head = head.next;
            if(removeNode != null && removeNode.next != null) {
    
    
                removeNode.next = removeNode.next.next;
                removeNode = removeNode.next;
            }
        }
        
        return result;
    }
}

leetcode 2 两数相加

题目描述

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

the answer

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    
    
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
    
    
        if(l1 == null) {
    
    
            return l2;
        }
        if(l2 == null) {
    
    
            return l1;
        }
        ListNode result = new ListNode(-1);
        ListNode cur = result;
        int carry = 0;
        while(l1 != null || l2 != null) {
    
    
            int v1 = l1==null?0:l1.val;
            int v2 = l2==null?0:l2.val;
            int sum = v1+v2+carry;
            carry = sum/10;
            cur.next = new ListNode(sum%10);
            cur = cur.next;
            
            l1 = l1==null?null:l1.next;
            l2 = l2==null?null:l2.next;
        }
        if(carry == 1) {
    
    
            cur.next= new ListNode(carry);
        }
        
        return result.next;
    }
}

leetcode 146 LRU缓存机制

题目描述

运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机制 。
实现 LRUCache 类:

LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。
 

进阶:你是否可以在 O(1) 时间复杂度内完成这两种操作?

The Answer

  • 双向链表维护数据结构
  • map来存储数据
class LRUCache {
    
    
    
    private class DouNode {
    
    
        DouNode next;
        DouNode prev;
        int key;
        int value;
        
        public DouNode(int key, int value) {
    
    
            this.key = key;
            this.value = value;
        }
    }
    
    DouNode head = null;
    DouNode tail = null;
    int capacity;
    Map<Integer, DouNode> cacheMap = new HashMap<>();

    public LRUCache(int capacity) {
    
    
        this.capacity = capacity;
        head = new DouNode(-1, -1);
        tail = new DouNode(-1, -1);
        head.next = tail;
        tail.prev = head;
    }
    
    public int get(int key) {
    
    
        if(!cacheMap.containsKey(key)) {
    
    
            return -1;
        }
        DouNode nowNode = cacheMap.get(key);
        //  取出当前节点
        nowNode.prev.next = nowNode.next;
        nowNode.next.prev = nowNode.prev;
        
        //  因为节点被使用,移动其到头部
        moveToHead(nowNode);
        return nowNode.value;
    }
    
    public void put(int key, int value) {
    
    
        if(get(key) != -1) {
    
    
            cacheMap.get(key).value = value;
        } else {
    
    
            DouNode insert = new DouNode(key, value);
            // 超出容量,移除尾部节点
            if(cacheMap.size() == capacity) {
    
    
                DouNode remvoeNode = tail.prev;
                cacheMap.remove(removeNode.key);

                removeNode.prev.next = removeNode.next;
                removeNode.next.prev = removeNode.prev;

                removeNode.next = null;
                removeNode.prev = null;
            }
            cacheMap.put(key, insert);
            moveToHead(insert);
        }
    }
    
    private void moveToHead(DouNode mNode) {
    
    
        mNode.next = head.next;
        head.next = mNode;
        
        mNode.prev = head;
        mNode.next.prev = mNode;
    }
}

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

本文题目来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/lru-cache

猜你喜欢

转载自blog.csdn.net/GoNewWay/article/details/114461789
今日推荐