链表专题下
今天继续剩下的链表题
文章目录
138. 随机链表的复制
遍历两遍链表,第一次建立新的节点,第二次建立节点之间的联系
java
/*
// Definition for a Node.
class Node {
int val;
Node next;
Node random;
public Node(int val) {
this.val = val;
this.next = null;
this.random = null;
}
}
*/
class Solution {
public Node copyRandomList(Node head) {
if(head==null){
return null;
}
Map<Node, Node> map = new HashMap<>();
Node p = head;
//创建新节点建立与原始节点的映射关系,map用于解决random节点的链接问题
while (p != null) {
map.put(p, new Node(p.val));
p = p.next;
}
p = head;
//将各个节点连接起来
while (p != null) {
Node newNode = map.get(p);
newNode.next = map.get(p.next);
newNode.random = map.get(p.random);
p = p.next;
}
return map.get(head);//返回新的头节点
}
}
- 第一次遍历(建映射) :遍历旧链表,为每个旧节点创建对应的新节点,并建立
Map<旧节点, 新节点>的映射关系。 - 第二次遍历(连指针) :再次遍历旧链表,通过 Map 查找,依次将新节点的
next和random指针指向正确的新节点,最终返回头节点。
148. 排序链表
归并排序

- 先用快慢指针找到每部分的中点
- 再根据升序合并起来

java
/**
* 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 sortList(ListNode head) {
if(head == null||head.next == null){
return head;
}
ListNode slow = head;
ListNode fast = head.next;
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
}
ListNode second = slow.next;
slow.next = null;
ListNode first = head;
first = sortList(first);
second = sortList(second);
return mergeList(first,second);
}
public ListNode mergeList(ListNode left,ListNode right){
ListNode dummy = new ListNode(0);
ListNode tail = dummy;
while(left != null && right != null){
if(left.val < right.val){
tail.next = left;
left = left.next;
}else{
tail.next = right;
right = right.next;
}
tail = tail.next;
}
if(left != null){
tail.next = left;
}else{
tail.next = right;
}
return dummy.next;
}
}
23. 合并 K 个升序链表
暴力
遍历数组,依次合并
java
/**
* 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 mergeKLists(ListNode[] lists) {
if(lists == null || lists.length == 0){
return null;
}
ListNode result = null;
for(ListNode list:lists){
result = mergeTwoLists(result, list);
}
return result;
}
public ListNode mergeTwoLists(ListNode left,ListNode right){
ListNode dummy = new ListNode(0);
ListNode tail = dummy;
while(left != null && right != null){
if(left.val < right.val){
tail.next = left;
left = left.next;
}else{
tail.next = right;
right = right.next;
}
tail = tail.next;
}
if(left != null)tail.next = left;
else tail.next = right;
return dummy.next;
}
}
优化
两两合并
对数组里的相邻两个链表两两合并,把合并后的结果存进新的数组,合并完一轮后得到一个新的数组,再次合并这个新的数组,最终得到一条链表
java
/**
* 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 mergeKLists(ListNode[] lists) {
if(lists == null || lists.length == 0){
return null;
}
while(lists.length > 1){
List<ListNode> tempList = new ArrayList<>();
for(int i = 0;i < lists.length;i += 2){
ListNode l1 = lists[i];
ListNode l2 = null;
if(i + 1 < lists.length)l2 = lists[i + 1];
tempList.add(mergeTwoLists(l1,l2));
}
lists = tempList.toArray(new ListNode[0]);
}
return lists[0];
}
public ListNode mergeTwoLists(ListNode left,ListNode right){
ListNode dummy = new ListNode(0);
ListNode tail = dummy;
while(left != null && right != null){
if(left.val < right.val){
tail.next = left;
left = left.next;
}else{
tail.next = right;
right = right.next;
}
tail = tail.next;
}
if(left != null)tail.next = left;
else tail.next = right;
return dummy.next;
}
}
146.LRU缓存
用哈希表和双向链表实现
- 哈希表 负责 O(1) 的极速查找(通过 key 直接定位)。
- 双向链表 负责 O(1) 的顺序维护(随时把节点拔出来插到队首,或者把队尾的节点删掉)

java
class LRUCache {
private int cap;// 容量
private Node head,tail;
private Map<Integer,Node> map;
public LRUCache(int capacity) {
cap = capacity;
head = new Node(0,0);
tail = new Node(0,0);
head.next = tail;
tail.pre = head;
map = new HashMap<>();
}
public int get(int key) {
if(map.containsKey(key)){
Node tmp = map.get(key);
remove(tmp);//删去双向链表里的该节点
headinsert(tmp);//再把该节点插到头部
return tmp.value;
}
return -1;
}
public void put(int key, int value) {
if(map.containsKey(key)){
remove(map.get(key));
map.remove(key);
}
Node temp = new Node(key,value);
headinsert(temp);
map.put(key,temp);
if(map.size()>cap){
Node todel = tail.pre;
remove(todel);
map.remove(todel.key);
}
}
//删除双向链表中指定节点
private void remove(Node temp){
Node tmp_pre = temp.pre;
Node tmp_nxt = temp.next;
tmp_pre.next = tmp_nxt;
tmp_nxt.pre = tmp_pre;
}
//从双向链表头部插入节点
private void headinsert(Node temp){
Node nxt = head.next;
head.next = temp;
temp.next = nxt;
temp.pre = head;
nxt.pre = temp;
}
}
class Node{
int key;// 为了淘汰尾部节点时去哈希表里删除对应数据
int value;
Node pre;
Node next;
public Node(){}
public Node(int key,int value){
this.key = key;
this.value = 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);
*/