CodeTop手撕教程Leet-code(反转链表、最长子串、LRU缓存机制)

目录

1. 反转链表

头插法(迭代)

尾递归

2. 无重复字符的最长子串

HashSet法(嵌套循环)

HashMap法(最优)⭐

队列法

窗口法(数组模拟HashMap)

3. LRU缓存机制⭐⭐⭐


1. 反转链表

206. 反转链表 - 力扣(LeetCode) (leetcode-cn.com)

头插法(迭代)

思路:newHead为新的头结点、cur当前结点、cur不为空时定义curNext记录cur的下一个结点位置

class Solution {
    public ListNode reverseList(ListNode head) {
        if (head == null || head.next == null) return head;
        ListNode newHead = null;
        ListNode cur = head;
        while (cur != null) {
            //记录cur的next位置
            ListNode curNext = cur.next;
            cur.next = newHead;
            newHead = cur;
            //cur往后走
            cur = curNext;
        }
        return newHead;
    }
}

尾递归

思路:核心思想与迭代法一致

class Solution {
    public ListNode reverseList(ListNode head) {
        return reverse(null,head);
    }
    //递归逆置
    private static ListNode reverse(ListNode pre, ListNode cur) {
        if (cur == null) return pre;
        ListNode curNext = cur.next;
        cur.next = pre;
        return reverse(cur,curNext);
    }
}

2. 无重复字符的最长子串

3. 无重复字符的最长子串 - 力扣(LeetCode) (leetcode-cn.com)

HashSet法(嵌套循环)

思路:用start和ret作记录。双层循环,外层遍历,内层循环将重复的左指针向右移动,直至不重复

class Solution {
    public int lengthOfLongestSubstring(String s) {
        //定义set记录是否出现过该字符
        Set set = new HashSet<>();
        int ret = 0;
        int left = 0;
        for (int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            while (set.contains(ch)) {
                set.remove(s.charAt(left));
                left++;
            }
            set.add(ch);
            ret = Math.max(ret, i-left+1);
        }
        return ret;
    }
}

HashMap法(最优)⭐

思路:用HashMap存储字符出现的位置(没有时先默认为 -1),计算完长度后Map存入当前的字符出现的位置Map.put(s.charAt( i ) , i ),最大长度为:当前位置i-该字符第一次出现的位置start+1

class Solution {
    public int lengthOfLongestSubstring(String s) {
        HashMap map = new HashMap<>();
        int ret = 0;
        int start = 0;
        for (int i = 0; i < s.length(); i++) {
            int index = map.getOrDefault(s.charAt(i),-1);   //当Map集合中有这个key时,就使用这个key值;如果没有就使用默认值defaultValue
            start = Math.max(start, index + 1); //要么不变,要么起始位置往后挪一位
            ret = Math.max(ret, i-start+1);
            map.put(s.charAt(i),i);
        }
        return ret;
    }
}

下面使用map.containsKey()进行判断,思路相同,更易读,但是效率变低了

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if (s == null) return -1;
        int index = 0;
        int max = 0;
        HashMap map = new HashMap<>();
        for (int i = 0; i < s.length(); i++) {
            if (map.containsKey(s.charAt(i))) { //如果Map中已有该字符
                index = Math.max(index, map.get(s.charAt(i))+1);    //不重复字符串的起始位置
            }
            max = Math.max(max, i-index+1);
            map.put(s.charAt(i), i);
        }
        return max;
    }
}

队列法

思路:队列queue存储遍历过程中的最长子串,ret记录最终结果。

注意最终返回ret和queue.size()的最大值

class Solution {
    public int lengthOfLongestSubstring(String s) {
        //ret记录最终结果
        int ret = 0;
        // 用queue记录当前最长子串
        Queue queue = new LinkedList<>();
        for (int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            if (queue.contains(ch)) {
                ret = Math.max(ret, queue.size());
                while(queue.poll() != ch);
            }
            queue.offer(ch);
        }
        return Math.max(ret,queue.size());
    }
}

窗口法(数组模拟HashMap)

思路:数组 last 用于记录字符上次出现的位置。(利用字符的ASCII码,模拟实现HashMap功能)

遍历字符串,index记录当前字符并当作数组 last 的索引使用

注意:last[index]中存的位置内容为 i + 1,因为遍历i是从0开始的,而非记录的长度

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int[] last = new int[128];
        //定义数组存储字符上一次出现的位置
        int ret = 0;
        int start = 0;  //窗口开始的位置
        for (int i = 0; i < s.length(); i++) {
            //将当前字符作为数组last的索引
            int index = s.charAt(i);
            start = Math.max(start, last[index]);
            ret = Math.max(ret, i - start + 1);
            //因为i是从0开始遍历的,并非记录的长度
            last[index] = i + 1;
        }
        return ret;
    }
}

3. LRU缓存机制⭐⭐⭐

146. LRU 缓存 - 力扣(LeetCode) (leetcode-cn.com)

思路:自定义双向链表+HashMap,注意删除链表结点时返回的是key!!

class LRUCache {
    public int cap;
    HashMap map;
    DoubleLinkedList cache;

    LRUCache(int capacity) {
        this.cap = capacity;
        map = new HashMap<>();
        cache = new DoubleLinkedList();
    }

    public int get(int key) {
        if (!map.containsKey(key)) return -1;
        int val = map.get(key).val;
        put(key,val);

        return val;
    }

    public void put(int key,int value) {
        Node node = new Node(key,value);
        if (map.containsKey(key)) {
            cache.delete(map.get(key));
        } else {
            if (map.size() == this.cap) {
                // 先删除旧的cacheNode
                map.remove(cache.deleteLast());
            }
        }
            // 添加新的结点
        cache.addFirst(node);
        map.put(key,node);
    }
}


//构建双向链表,head:recently used  tail:LRU
class DoubleLinkedList {
    Node head;
    Node tail;

    public DoubleLinkedList() {
        head = new Node(0,0);
        tail = new Node(0,0);
        head.next = tail;
        tail.prev = head;
    }

    //头插(最近使用的元素)
    public void addFirst(Node node) {
        node.next = head.next;
        node.prev = head;
        head.next.prev = node;
        head.next = node;
    }

    //删除方法
    public int delete(Node node) {
        int key = node.key;
        node.prev.next = node.next;
        node.next.prev = node.prev;
        return key;
    }

    //删除尾结点
    public int deleteLast() {
        if (head.next == tail) return -1;
        return delete(tail.prev);
    }
}
//构建双向链表Node
class Node {
    public int key;
    public int val;
    public Node prev;
    public Node next;

    public Node(int key, int val) {
        this.key = key;
        this.val = val;
    }
}

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

思路:使用LinkedHashMap,保持插入顺序

class LRUCache {
    private int cap;
    private Map map = new LinkedHashMap<>();  //保持插入顺序

    public LRUCache(int capacity) {
        this.cap = capacity;
    }
    
    public int get(int key) {
        if (map.containsKey(key)) {
            int value = map.get(key);
            map.remove(key);
            // 保证每次查询后,都在末尾
            map.put(key, value);
            return value;
        }
        return -1;
    }
    
    public void put(int key, int value) {
        if (map.containsKey(key)) {
            map.remove(key);
        } else if (map.size() == cap) {
            Iterator> iterator = map.entrySet().iterator();
            iterator.next();
            iterator.remove();
        }
        map.put(key, value);
    }
}

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

你可能感兴趣的:(CodeTop手撕代码教程,leetcode,java,数据结构)