目录
1. 反转链表
头插法(迭代)
尾递归
2. 无重复字符的最长子串
HashSet法(嵌套循环)
HashMap法(最优)⭐
队列法
窗口法(数组模拟HashMap)
3. LRU缓存机制⭐⭐⭐
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);
}
}
3. 无重复字符的最长子串 - 力扣(LeetCode) (leetcode-cn.com)
思路:用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存储字符出现的位置(没有时先默认为 -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());
}
}
思路:数组 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;
}
}
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);
*/