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<Character> 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<Character,Integer> 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<Character, Integer> 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<Character> 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<Integer, Node> 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<Integer, Integer> 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<Map.Entry<Integer, Integer>> 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);
 */

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
探险家小扣的行动轨迹,都将保存在记录仪中。expeditions[i] 表示小扣第 i 次探险记录,用一个字符数组表示。其中的每个「营地」由大小写字母组成,通过子 -> 连接。例:"Leet->code->Campsite",表示到访了 "Leet"、"code"、"Campsite" 三个营地。expeditions[0] 包含了初始小扣已知的所有营地;对于之后的第 i 次探险(即 expeditions[i] 且 i > 0),如果记录中包含了之前均没出现的营地,则表示小扣 新发现 的营地。 请你找出小扣发现新营地最多且索引最小的那次探险,并返回对应的记录索引。如果所有探险记录都没有发现新的营地,返回 -1。注意: 大小写不同的营地视为不同的营地; 营地的名称长度均大于 0。用python实现。给你几个例子:示例 1: 输入:expeditions = ["leet->code","leet->code->Campsite->Leet","leet->code->leet->courier"] 输出:1 解释: 初始已知的所有营地为 "leet" 和 "code" 第 1 次,到访了 "leet"、"code"、"Campsite"、"Leet",新发现营地 2 处:"Campsite"、"Leet" 第 2 次,到访了 "leet"、"code"、"courier",新发现营地 1 处:"courier" 第 1 次探险发现的新营地数量最多,因此返回 1。示例 2: 输入:expeditions = ["Alice->Dex","","Dex"] 输出:-1 解释: 初始已知的所有营地为 "Alice" 和 "Dex" 第 1 次,未到访任何营地; 第 2 次,到访了 "Dex",未新发现营地; 因为两次探险均未发现新的营地,返回 -1
最新发布
04-23

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值