美文网首页
实战高频leetcode题目

实战高频leetcode题目

作者: 奉先 | 来源:发表于2021-10-04 06:33 被阅读0次

1. 反转链表 :

反转链表是常见简单题目,定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

第一次自己实现使用了栈来完成 。详见我的成功程序 :

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

    public ListNode reverseList(ListNode head) {
        if(head == null) return null ; 
        Stack<ListNode> stackNode = new Stack<>() ;
        while(head != null){
            stackNode.push(head) ;
            head = head.next ;
        }
        //这里需要按照pop出的Node的结点值新建Node,否则会有Cycle ListNode 
        ListNode newReturn = new ListNode(stackNode.pop().val) ;
        ListNode tail = newReturn;
        while(!stackNode.isEmpty()){
            ListNode tp = new ListNode(stackNode.pop().val);
            tail.next = tp ;
            tail = tail.next ;
        }
        return newReturn ; 
    }

经过题解,如果使用双指针可以更快解决。讲解视频 :https://ke.qq.com/webcourse/index.html#cid=3065907&term_id=103186001&taid=10593313500350515&type=1024&vid=5285890809407997971

    //双指针法 :
    public ListNode reverseList(ListNode head) {
        ListNode pre = null ;
        ListNode curr = head ;
        while(curr != null){
            ListNode next = curr.next ;
            curr.next = pre ;
            pre = curr ;
            curr = next ;
        }
        return pre ;
    }

2. 设计和实现一个 LRU (最近最少使用) 缓存机制 ,一个数据结构:

该数据结构应该具有以下API操作 :

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

LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1);    // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2);    // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1);    // 返回 -1 (未找到)
lRUCache.get(3);    // 返回 3
lRUCache.get(4);    // 返回 4

这里通过key来返回value,很容易想到使用HashMap来实现这个数据结构,但是hashmap无法解决的是记录最近访问的功能, 所以就使用双链表来记录时间问题。 这里使用了dummy节点 head 和tail来方便操作(边界条件就能简单解决了)。 那么靠近head的结点的就是新的节点,而靠近tail的节点是历史结点。

这里双链表结构中,定义了2个值,是key、value ; 主要是在put溢出场景中,需要删除尾节点,同时在HashMap中也删除,所以这里双链表如果同时记录了key和value,那么就非常快的拿到了key,并按照这个key来remove掉HashMap中的元素。
下面是详细的代码实现 :

class LRUCache {

    class DoubleLinkNode{
        private DoubleLinkNode next ;
        private DoubleLinkNode prev ;
        private int key ;
        private int val ;
        DoubleLinkNode(){}
        DoubleLinkNode(int _key, int _value){
            this.key =_key ;
            this.val =_value ;
        }
    }

    Map<Integer,DoubleLinkNode> map = new HashMap<>() ;
    //数据结构的容量
    private int capacity ;
    //数据结构目前的元素数量,在判断是否溢出时使用
    private int size;
    //伪头、尾结点
    DoubleLinkNode head ,tail ;

    public LRUCache(int capacity) {
        head = new DoubleLinkNode() ;
        tail = new DoubleLinkNode() ;
        head.next = tail ;
        tail.prev = head ;
        this.size = 0 ;
        this.capacity = capacity;
    }

    /**
     * 实现的主要的put方法:
     * @param key
     * @param value
     */
    public void put(int key, int value) {
       DoubleLinkNode node =  map.get(key) ;
       if(node ==null){
           //新建一个DoubleLink结点,HashMap中put新<key,value>
           DoubleLinkNode newNode = new DoubleLinkNode(key,value) ;
           map.put(key,newNode) ;
           addToHead(newNode);
           ++size;

           if(size > capacity){
            DoubleLinkNode tail = removeTail();
            map.remove(tail.key);
            --size ;
           }
       }
       else{
           node.val = value ;
           moveToHead(node);
       }
    }

    private DoubleLinkNode removeTail() {
        DoubleLinkNode oldTail = tail.prev ;
        oldTail.prev.next = tail ;
        tail.prev = oldTail.prev ;
        return oldTail ;
    }

    /**
     * 实现的主要的get方法
     * @param key
     * @return
     */
    public int get(int key) {
        DoubleLinkNode getD = map.get(key) ;
        if(getD == null){
            return  -1;
        }
        moveToHead(getD) ;
        return getD.val;
    }

    private void moveToHead(DoubleLinkNode getD) {
        removeNode(getD) ;
        addToHead(getD);
    }

    /**
     * 把结点增加到头结点上
     * @param getD
     */
    private void addToHead(DoubleLinkNode getD) {
        DoubleLinkNode oldFirst = head.next ;
        head.next = getD ;
        getD.next = oldFirst ;
        getD.prev = head;
        oldFirst.prev = getD ;
    }

    /**
     * 删除双链表中的一个结点
     * @param getD
     */
    private void removeNode(DoubleLinkNode getD) {
        DoubleLinkNode before = getD.prev ;
        DoubleLinkNode after = getD.next ;
        before.next = after;
        after.prev = before;
    }
}

3. 树的最大路径和

路径被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。同一个节点在一条路径序列中 至多出现一次 。该路径至少包含一个 节点,且不一定经过根节点。
路径和是路径中各节点值的总和。
使用树中常见的递归方法,问自己的左右孩子要结果。 下面是我提交的java代码 :

class Solution {
    int maxSum = Integer.MIN_VALUE ;

    public int maxPathSum(TreeNode root){
        maxSumDv(root) ;
        return maxSum ; 
    }

    public int maxSumDv(TreeNode root) {
        if(root == null) return 0 ;

        int leftSum = Integer.max(maxSumDv(root.left),0) ;
        int rightSum = Integer.max(maxSumDv(root.right),0) ;

        //本结点贡献的最大值 本结点贡献只能是单root 或者 root + root.left 或者 root + root.right
        int thisMaxSum = root.val + Integer.max(leftSum,rightSum);

        //更新最大值,最大值是跳出递归的全局变量
        maxSum = Integer.max(root.val + leftSum + rightSum,maxSum) ;

        return thisMaxSum ;
    }
}

相关文章

网友评论

      本文标题:实战高频leetcode题目

      本文链接:https://www.haomeiwen.com/subject/mzuvnltx.html