# 【算法】【链表】链表相关问题总结

2020-05-23 09:05:42  阅读：12  来源： 互联网

# 剑指offer

## 6. 从尾到头打印链表

### 递归

``````/**
* struct ListNode {
*     int val;
*     ListNode *next;
*     ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
return {};
return v;
}
};
``````

### 栈

``````class Solution {
public:
vector<int> v;
while (cur) {
v.push_back(cur->val);
cur = cur->next;
}
reverse(v.begin(), v.end());
return v;
}
};

``````

### reverse数组

``````class Solution {
public:
vector<int> v;
while (cur) {
v.push_back(cur->val);
cur = cur->next;
}
reverse(v.begin(), v.end());
return v;
}
};

``````

## 18. 删除链表的节点

``````/**
* struct ListNode {
*     int val;
*     ListNode *next;
*     ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* deleteNode(ListNode* head, int val) {
auto dummy = new ListNode(-1);
auto tmp = dummy;
{
tmp = tmp -> next;
}
// cout << tmp -> val <<endl;
// cout << head -> val <<endl;
tmp -> next = head -> next;
return dummy -> next;
}
};
``````

## 22. 链表中倒数第k个节点

### 快慢指针

``````/**
* struct ListNode {
*     int val;
*     ListNode *next;
*     ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* getKthFromEnd(ListNode* head, int k) {
while(fast){
fast = fast -> next;
if(k > 0) k--;
else low = low -> next;
}
return low;
}
};
``````

## 24. 反转链表

### 思路

``````/**
* struct ListNode {
*     int val;
*     ListNode *next;
*     ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
{
}
}
};
``````

## 25. 合并两个排序的链表

``````/**
* struct ListNode {
*     int val;
*     ListNode *next;
*     ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode* new_head = new ListNode(-1); //保存合并后的头结点
while(l1 && l2)
{
//小的追加到tmp后面
if(l1 -> val <= l2 -> val)
{
tmp -> next = l1;
l1 = l1 -> next;
}
else
{
tmp -> next = l2;
l2 = l2 -> next;
}
//更新tmp
tmp = tmp -> next;
}
//没处理完的直接追加到后面
tmp -> next = l1 ? l1 : l2;
}
};
``````

## 35. 复杂链表的复制

### 哈希

``````/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;

Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:

unordered_map<Node*, Node*> hashmap;
hashmap[NULL] = NULL;
//初始化hashmap
//hashmap key是原先的节点p val是新节点
for(auto p = head; p; p = p -> next)
hashmap[p] = new Node(p -> val);

for(auto p = head; p; p = p -> next)
{
//复制next指针
hashmap[p] -> next = hashmap[p -> next];
//复制random指针
hashmap[p] -> random = hashmap[p -> random];
}
}
};
``````

### 迭代

``````*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;

Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
//在每个节点后面添加它的复制
//复制next节点 复制的节点保存在原先节点的next指针处
//新复制的节点的next指针的原先节点的next指针
//原先： 1->2->3->4->5->null
//now： 1->1->2->2->3->3->4->4->5->5->null
{
auto np = new Node(p -> val);
auto next = p -> next;
p -> next = np;
np -> next = next;
p = next;
}
//复制random指针
for(auto p = head; p; p = p -> next -> next)
if(p -> random)
p -> next -> random = p -> random -> next;
//原链表不能修改 还原
//遍历链表 分开两个链表
auto dummy = new Node(-1);
auto cur = dummy;
for(auto p = head; p; p = p -> next)
{
cur -> next = p -> next;
cur = cur -> next;
p -> next = p -> next -> next;
}
return dummy -> next;
}
};
``````

## 36. 二叉搜索树与双向链表

### 递归

Leetcode要求改成双向循环链表

``````
``````

left 改成 前驱

right 改成后继

``````/**
* Definition for a binary tree node.
* struct TreeNode {
*     int val;
*     TreeNode *left;
*     TreeNode *right;
*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* convert(TreeNode* root) {
if (!root) return NULL;
auto sides = dfs(root);
return sides.first;
}

pair<TreeNode*, TreeNode*> dfs(TreeNode* root)
{
//叶子节点
if (!root->left && !root->right) return {root, root};
//左右子树都有
if (root->left && root->right)
{
//递归处理左右子树
auto lside = dfs(root->left), rside = dfs(root->right);
//拼接左右子树
lside.second->right = root, root->left = lside.second;
root->right = rside.first, rside.first->left = root;
return {lside.first, rside.second};
}
//只有左子树
if (root->left)
{
auto lside = dfs(root->left);
lside.second->right = root, root->left = lside.second;
return {lside.first, root};
}
//只有右子树
if (root->right)
{
auto rside = dfs(root->right);
rside.first->left = root, root->right = rside.first;
return {root, rside.second};
}
}
};

``````

# Leetcode

## 92. 反转链表II

``````1. 逆置前头节点->逆置后尾节点

2. 逆置前尾节点->逆置后头节点
3. 逆置前头节点的前驱
4. 逆置前尾节点的后继
``````

1. 找到开始逆置的节点，记录该节点的前驱和该节点；
``````/**
* struct ListNode {
*     int val;
*     ListNode *next;
*     ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int m, int n) {
int len = n - m + 1; // 需要写在改变m之前
{
}

{
ListNode *next = head -> next;
len--;
}

aft_tail -> next = head; //逆置后尾节点的下一个节点是逆置前尾节点的后继
return res;
}
};
``````

## 146. LRU缓存

``````class LRUCache {
public:

struct Node
{
int val, key;
Node *left, *right;
Node() : key(0), val(0), left(NULL), right(NULL) {}
};
int n;
unordered_map<int, Node*> hash;

void delete_node(Node *p)
{
p->left->right = p->right, p->right->left = p->left;
}

void insert_node(Node *h, Node *p)
{
p->right = h->right, h->right = p;
p->left = h, p->right->left = p;
}
/*初始化*/
LRUCache(int capacity) {
n = capacity;
hu = new Node(), tu = new Node();
hr = new Node(), tr = new Node();
hu->right = tu, tu->left = hu;
hr->right = tr, tr->left = hr;

//第一个双链表插入 n 个节点，n 是缓存大小；
//第二个双链表和哈希表都为空；
for (int i = 0; i < n; i ++ )
{
Node *p = new Node();
insert_node(hr, p);
}
}

int get(int key) {
if (hash[key]) //用哈希表判断key是否存在
{
Node *p = hash[key];
delete_node(p);
insert_node(hu, p);
return p->val; //如果key存在，则返回对应的value，同时将key对应的节点放到第二个双链表的最左侧；
}
return -1; //如果key不存在，则返回-1
}

void put(int key, int value) {
if (hash[key]) //用哈希表判断key是否存在
{
//key存在，则修改对应的value，同时将key对应的节点放到第二个双链表的最左侧；
Node *p = hash[key];
delete_node(p);
insert_node(hu, p);
p->val = value;
return;
}
//key不存在：
//如果缓存已满，则删除第二个双链表最右侧的节点（上次使用时间最老的节点），同时更新三个数据结构；
if (!n)
{
n ++ ;
Node *p = tu->left;
hash[p->key] = 0;
delete_node(p);
insert_node(hr, p);
}
//否则，插入(key, value)：从第一个双链表中随便找一个节点，修改节点权值，然后将节点从第一个双链表删除，插入第二个双链表最左侧，同时更新哈希表；
n -- ;
Node *p = hr->right;
p->key = key, p->val = value, hash[key] = p;
delete_node(p);
insert_node(hu, p);
}
};

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

``````

``````class LRUCache {
public:
LRUCache(int capacity) {
cap = capacity;
used = 0;
tail = new Node(0, 0);
}

int get(int key) {
if (m.count(key)) {
Node *p = m[key];
int val = p->val;
delete_node(p);
m[key] = insert_node(key, val);
return val;
}
return -1;
}

void put(int key, int value) {
if (m.count(key)) {
delete_node(m[key]);
used--;
}
m[key] = insert_node(key, value);
used++;
if (used > cap) {
m.erase(tail->pre->key);
delete_node(tail->pre);
used--;
}

}
private:
int cap, used;
struct Node{
int val, key;
Node *pre, *next;
Node(int key, int val): key(key), val(val), pre(NULL), next(NULL){};
};
unordered_map<int, Node*> m;
void delete_node(Node *p) {
p->pre->next = p->next;
p->next->pre = p->pre;
delete p;

}
Node* insert_node(int key, int val) {
Node *node = new Node(key, val);
return node;
}
};

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

## 147. 链表插入排序

``````/**
* struct ListNode {
*     int val;
*     ListNode *next;
*     ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
//借助dummy指针
ListNode* dummy = new ListNode(-1);
{
ListNode* next = head -> next;
ListNode* p = dummy;
//找到待插入元素的合适位置
while(p -> next && p -> next -> val <= head -> val) p = p -> next;
//将待插入元素出入到链表中
head -> next = p -> next;
}
return dummy -> next;
}
};

``````

## 148. 排序链表

### 快速排序

``````/**
* struct ListNode {
*     int val;
*     ListNode *next;
*     ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
//得到尾节点
}
auto left = new ListNode(-1), mid = new ListNode(-1), right = new ListNode(-1);
auto ltail = left, mtail = mid, rtail = right;

int val = head -> val;
for(auto p = head; p; p = p -> next)
{
if(p -> val < val) ltail = ltail -> next = p;
else if(p -> val == val) mtail  = mtail -> next = p;
else rtail = rtail -> next = p;
}
ltail -> next = mtail -> next = rtail -> next = NULL;
left -> next = sortList(left -> next);
right -> next = sortList(right -> next);

//拼接三个链表
get_tail(left) -> next = mid -> next;
get_tail(left) -> next = right -> next;

auto p = left -> next;
// delete left;
// delete mid;
// delete right;
return p;
}
};
``````

### 归并排序

``````class Solution {
public:
while(fast && fast->next) {
pre = slow;
slow = slow->next;
fast = fast->next->next;
}
pre->next = nullptr;
}
ListNode* mergeTwoList(ListNode* h1, ListNode* h2) {
if(!h1) return h2;
if(!h2) return h1;
if(h1->val < h2->val) {
h1->next = mergeTwoList(h1->next, h2);
return h1;
}
else {
h2->next = mergeTwoList(h1, h2->next);
return h2;
}
}
};
``````

## 143. 重排链表

1. 将后半段的指针都反向；
2. 用两个指针分别从1和n开始往中间扫描，将后半段交替插入到前半段。

``````/**
* struct ListNode {
*     int val;
*     ListNode *next;
*     ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
//获取链表长度
int n = 0;
for (ListNode *p = head; p; p = p->next) n ++ ;
//长度小于2，不用重排
if (n <= 2) return;

//获取节点
for (int i = 0; i + 1 < (n + 1) / 2; i ++ )
later = later->next;
//逆置后半串节点
ListNode *a = later, *b = later->next;
while (b)
{
ListNode *c = b->next;
b->next = a;
a = b;
b = c;
}
//依次将后部分的节点插入到前面正确的位置
later->next = 0;
{
b = a->next;
a = b;
}
}
};
``````

## 141. 环形链表

### 快慢指针

``````/**
* struct ListNode {
*     int val;
*     ListNode *next;
*     ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:

while(fast && low)
{
fast = fast -> next;
low = low -> next;
if(fast) fast = fast -> next;
//如果快指针走到nullptr，说明链表没有环
else return false;

if(fast == low)
{
while(fast != low)
{
fast = fast -> next;
low = low -> next;
}
return true;
}
}
return false;
}
};
``````

## 160. 相交链表

### 双指针

``````/**
* struct ListNode {
*     int val;
*     ListNode *next;
*     ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
while(A != B)
{
A = A -> next;
B = B -> next;
if(!A && !B) return nullptr;
}
return A;
}
};
``````

## 86. 分隔链表

``````/**
* struct ListNode {
*     int val;
*     ListNode *next;
*     ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
//得到left的尾结点
}
ListNode* partition(ListNode* head, int x) {
auto left = new ListNode(-1), right = new ListNode(-1);
auto ltail = left, rtail = right;
for(auto p = head; p; p = p -> next)
{
if(p -> val < x) ltail = ltail -> next = p;
else rtail = rtail -> next = p;
}
ltail -> next  = rtail -> next = nullptr;
get_tail(left) -> next = right -> next;
return left -> next;
}
};
``````

## 23. 合并K个有序的链表

### 顺序合并

``````class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode* new_head = new ListNode(-1); //保存合并后的头结点
while(l1 && l2)
{
//小的追加到tmp后面
if(l1 -> val <= l2 -> val)
{
tmp -> next = l1;
l1 = l1 -> next;
}
else
{
tmp -> next = l2;
l2 = l2 -> next;
}
//更新tmp
tmp = tmp -> next;
}
//没处理完的直接追加到后面
tmp -> next = l1 ? l1 : l2;
}

ListNode* mergeKLists(vector<ListNode*>& lists) {
ListNode *ans = nullptr;
for (size_t i = 0; i < lists.size(); ++i) {
ans = mergeTwoLists(ans, lists[i]);
}
return ans;
}
};
``````

### 分治合并

``````class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode* new_head = new ListNode(-1); //保存合并后的头结点
while(l1 && l2)
{
//小的追加到tmp后面
if(l1 -> val <= l2 -> val)
{
tmp -> next = l1;
l1 = l1 -> next;
}
else
{
tmp -> next = l2;
l2 = l2 -> next;
}
//更新tmp
tmp = tmp -> next;
}
//没处理完的直接追加到后面
tmp -> next = l1 ? l1 : l2;
}
ListNode* merge(vector<ListNode*>& lists, int l, int r)
{
if(l == r) return lists[l];
if(l > r) return nullptr;
int mid = (l + r) >> 1;
return mergeTwoLists(merge(lists, l, mid), merge(lists, mid + 1, r));
}

ListNode* mergeKLists(vector<ListNode*>& lists) {
return merge(lists, 0, lists.size() - 1);
}
};
``````

## 234. 回文链表

### 逆置链表

``````/**
* struct ListNode {
*     int val;
*     ListNode *next;
*     ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
while(fast && fast -> next)
{
slow = slow -> next;
fast = fast -> next -> next;
}

fast = nullptr; //逆置后的头节点 逆置前的尾节点
while(slow)
{
auto next = slow -> next;
slow -> next = fast;
fast = slow;
slow = next;
}
{
if(head -> val != fast -> val) return false;
fast = fast -> next;
}
delete slow;
delete fast;
return true;
}
};
``````

fast一次走2步，slow一次走1步，链表全长为len，len为偶数时，slow到达前半段的最后一个节点，len为奇数时，slow到达正中间的节点，两种情况中，slow->next均为后半段的起始节点。

``````ListNode* getMid(ListNode* head){
while(fast && fast -> next){
slow = slow->next;
fast = fast->next->next;
}
delete fast;
return slow;
}
``````

## 19. 删除链表的倒数第N个节点

``````/**
* struct ListNode {
*     int val;
*     ListNode *next;
*     ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummy = new ListNode(-1);
auto fast = dummy, low = dummy;
while(fast)
{
fast = fast -> next;
if(n >= 0) n--;
else low = low -> next;
}
return dummy -> next;
}
};
``````

## 21. 合并两个有序链表

``````/**
* struct ListNode {
*     int val;
*     ListNode *next;
*     ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode* dummy = new ListNode(-1);
ListNode* tmp = dummy;
while(l1 && l2)
{
if(l1 -> val < l2 -> val)
{
tmp -> next = l1;
l1 = l1 -> next;
}
else
{
tmp -> next = l2;
l2 = l2 -> next;
}
tmp = tmp -> next;
}
tmp -> next = l1 ? l1 : l2;
return dummy -> next;
}
};
``````

## 876. 链表的中间节点

``````/**
* struct ListNode {
*     int val;
*     ListNode *next;
*     ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
while(fast  && fast -> next)
{
low = low -> next;
fast = fast -> next -> next;
}
return low;
}
};
``````
``````/**
* struct ListNode {
*     int val;
*     ListNode *next;
*     ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* dummy = new ListNode(-1);
auto fast = dummy, low = dummy;
while(fast -> next  && fast -> next -> next)
{
low = low -> next;
fast = fast -> next -> next;
}
return low -> next;

}
};
``````

## 114. 二叉树展开为链表

### 递归

``````/**
* Definition for a binary tree node.
* struct TreeNode {
*     int val;
*     TreeNode *left;
*     TreeNode *right;
*     TreeNode() : val(0), left(nullptr), right(nullptr) {}
*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* last = nullptr;
void flatten(TreeNode* root) {
if (root == nullptr) return;
flatten(root->right);
flatten(root->left);
root->right = last;
root->left = nullptr;
last = root;
}

};
``````

### 迭代(先序遍历)

``````/**
* Definition for a binary tree node.
* struct TreeNode {
*     int val;
*     TreeNode *left;
*     TreeNode *right;
*     TreeNode() : val(0), left(nullptr), right(nullptr) {}
*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
void flatten(TreeNode* root) {
//先序遍历
stack<TreeNode*> node_stack;
if(nullptr != root){
node_stack.push(root);
}

TreeNode* current_node = root;

while(!node_stack.empty()){
TreeNode *node = node_stack.top();
node_stack.pop();

if(nullptr != node->right){
node_stack.push(node->right);
}

if(nullptr != node->left){
node_stack.push(node->left);
}

if(node != root){
current_node->left = nullptr;
current_node->right = node;
}
current_node = node;
}
}
};
``````

# 总结

## 常用操作

### 获取链表中间位置的元素

``````ListNode* getMid(ListNode* head){
while(fast && fast -> next){
slow = slow->next;
fast = fast->next->next;
}
delete fast;
return slow;
}
``````

### 删除倒数第K个节点

``````ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummy = new ListNode(-1);
auto fast = dummy, low = dummy;
while(fast)
{
fast = fast -> next;
if(n >= 0) n--;
else low = low -> next;
}
return dummy -> next;
}
``````

### 逆置链表

``````ListNode* reverseList(ListNode* head) {
{
}
}
``````

## 技巧和注意事项

### 指针丢失和内存泄漏

#### 插入节点

``````/*
a -> b -> c -> d -> e
c后面插入f
*/
//写法1
cur = c -> next;
c -> next = f;
f -> next = cur;

//写法2
f -> next = c -> next;
c -> next = f;
``````

#### 删除节点

``````p -> next  = p -> next -> next;
``````