LeetCode hot100 链表
24 两两交换链表中的节点
/**
* 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 swapPairs(ListNode head) {
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
ListNode temp = dummyHead;
while (temp.next != null && temp.next.next != null) {
ListNode node1 = temp.next;
ListNode node2 = temp.next.next;
temp.next = node2;
node1.next = node2.next;
node2.next = node1;
temp = node1;
}
return dummyHead.next;
}
}
解法:
- 递归调用
25 K个一组翻转链表
/**
* 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 reverseKGroup(ListNode head, int k) {
ListNode hair = new ListNode(0);
hair.next = head;
ListNode pre = hair;
while (head != null) {
ListNode tail = pre;
// 查看剩余部分长度是否大于等于 k
for (int i = 0; i < k; ++i) {
tail = tail.next;
if (tail == null) {
return hair.next;
}
}
ListNode nex = tail.next;
ListNode[] reverse = myReverse(head, tail);
head = reverse[0];
tail = reverse[1];
// 把子链表重新接回原链表
pre.next = head;
tail.next = nex;
pre = tail;
head = tail.next;
}
return hair.next;
}
public ListNode[] myReverse(ListNode head, ListNode tail) {
ListNode prev = tail.next;
ListNode p = head;
while (prev != tail) {
ListNode nex = p.next;
p.next = prev;
prev = p;
p = nex;
}
return new ListNode[]{tail, head};
}
}
解法
- 反转单个链表的方法:
- 遍历链表,对每个节点保存后节点的值
- 该节点的next指针指向前面的节点
146 LRU缓存
class LRUCache {
class DList{
int key;
int val;
DList pre;
DList next;
public DList(int k,int v,DList l1,DList l2){
key = k;val = v; pre = l1; next = l2;
}
public DList(){}
// public Dlist(int v){val = v;}
}
Map<Integer,DList> rec = new HashMap<>();
DList head;
DList tail;
int size;
int capacity;
public LRUCache(int capacity) {
this.size = 0;
this.capacity = capacity;
// 使用伪头部和伪结尾
head = new DList();
tail = new DList();
head.next = tail;
tail.pre = head;
}
public int get(int key) {
DList node = rec.getOrDefault(key,null);
if(node == null)
return -1;
// 把这个节点放到头节点,代表最先使用的
moveToHead(node);
return node.val;
}
public void put(int key, int value) {
DList node = rec.getOrDefault(key,null);
if(node != null){
node.val = value;
moveToHead(node);
return;
}else{
if(size == capacity) {
DList oldtail = removeTail();
rec.remove(oldtail.key);
size--;
}
DList tmp = new DList(key,value,null,null);
// 直接插入头部
tmp.next = head.next;
tmp.pre = head;
head.next = tmp;
tmp.next.pre = tmp;
rec.put(key,tmp);
size++;
}
}
private void moveToHead(DList node){
DList nodepre = node.pre;
DList nodenxt = node.next;
nodepre.next = node.next;
nodenxt.pre = node.pre;
node.next = head.next;
node.pre = head;
head.next = node;
node.next.pre = node;
}
/**
* 返回删除的元素
* @return
*/
private DList removeTail(){
DList tailpre = tail.pre;
DList tailprepre = tailpre.pre;
tailprepre.next = tail;
tail.pre = tailprepre;
return tailpre;
}
}
解法
- map + LinkedList
- 用双向链表,在容量满时能快速移除最后一个有效元素
- 对头、对尾双指针
- 在put或get时,将该节点放到链表队头,代表最先使用
关于 LinkedHashMap
底层实现: HashMap + 双向链表
- 插入顺序为 key1 , key2 ,key3 ,key4
- 即可获得上方图示的结果
注意:
新插入的节点会放到最后
head指向的节点为第一个放入的节点
LRU的应用
class LRUCache extends LinkedHashMap {
private int capacity;
public LRUCache(int capacity) {
//accessOrder为true
super(capacity, 0.75F, true);
this.capacity = capacity;
}
public int get(int key) {
return (int)super.getOrDefault(key, -1);
}
public void put(int key, int value) {
super.put(key, value);
}
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > capacity;
}
}
这里重写了removeEldestEntry方法,然后removeEldestEntry方法在afterNodeInsertion中被调用,如果这个方法返回真,那么就会删除head指向的节点。根据每次get的节点都会放到尾部的特性,所以head指向的节点就是最久没有使用到的节点,所以可以删除。由于我们每次put完(HashMap#putVal())都会调用这个afterNodeInsertion方法,所以可以上面的设计可以使put过后如果size超了,将删除最久没有使用的一个节点,从而腾出空间给新的节点。