链表的基本实现
#include<assert.h>
typedef int Rank;
template<typename T> struct ListNode{
T data;
ListNode<T> *pre;
ListNode<T> *succ;
ListNode(T const& data, ListNode<T> *pre, ListNode<T> *succ)
: data(data), pre(pre), succ(succ) {}
ListNode(){}
ListNode<T>* insertAsPre(T const& e){
return pre = new ListNode<T>(e, this, pre);
}
ListNode<T>* insertAsSucc(T const& e){
return succ = new ListNode<T>(e, this, succ);
}
};
template <typename T> class LinkedList{
private:
ListNode<T> *header;
ListNode<T> *trailer;
int _size;
protected:
void init(){
header = new ListNode<T>();
trailer = new ListNode<T>();
header->succ = trailer;
header->pre = NULL;
trailer->pre = header;
trailer->succ = NULL;
_size = 0;
}
int clear(){
int oldSize = _size;
while(_size > 0)
remove(header->succ);
return oldSize;
}
void copyNodes(ListNode<T> *p, int n){
assert(n >= 0 && valid(p));
init();
while(n--){
insertAsLast(p->data);
p = p->succ;
}
}
void merge(LinkedList<T> left, int m, LinkedList<T> right, int n){
init();
int i = 0, j = 0;
while(i < m && j < n){
if(left.get(i) < right.get(j))
insertAsLast(left.get(i++));
else
insertAsLast(right.get(j++));
}
while(i < m)
insertAsLast(left.get(i++));
while(j < n)
insertAsLast(right.get(j++));
}
void mergeSort(LinkedList<T>& l, int n){
if(n < 2)
return;
LinkedList<T> left, right;
int m = n >> 1;
left.copyNodes(l.header->succ, m);
right.copyNodes(l.get(m), n - m);
mergeSort(left, m);
mergeSort(right, n - m);
merge(left, m, right, n - m);
}
void selectionSort(LinkedList<T>& l, int n){
for(int i = 0; i < n; i++){
int min = i;
for(int j = i + 1; j < n; j++)
if(l.get(j) < l.get(min))
min = j;
l.swap(i, min);
}
}
void insertionSort(LinkedList<T>& l, int n){
assert(valide(p) && n >= 0);
for (Rank r = 0; r < n; r++){
insertAsSucc(search(p->data, r,p), p->data);
p = p->succ;
remove(p->pre);
}
}
public:
LinkedList()
{
init();
}
LinkedList(LinkedList<T> const *right, int n)
{
copyNodes(left, n);
}
LinkedList(LinkedList<T> const *right, Rank r, int n){
copyNodes(right[r], n);
}
LinkedList(ListNode<T>* p, int n){
copyNodes(p, n);
}
~LinkedList()
{
clear();
delete header;
delete trailer;
}
// read only interface
Rank size() const{
return _size;
}
bool isEmpty() const{
return !_size;
}
T& operator[](Rank r) const{
return get(r);
}
T& get(Rank r) const{
assert(0 <= r && r < _size);
ListNode<T> *p = header;
while(r--)
p = p->succ;
return p->data;
}
ListNode<T> first() const{
return header->succ;
}
ListNode<T> last() const{
return trailer->pre;
}
bool valid(ListNode<T> const* p) const{
return p && (p != trailer) && (p != header);
}
bool isAssending() const{
ListNode<T> *p = header->succ;
while(p->succ != trailer){
if(p->data > p->succ->data)
return false;
p = p->succ;
}
return true;
}
ListNode<T>* find(T const& e, int n, ListNode<T>* p) const{
assert(0 <= n && n <= _size);
while(n--){
if(p->data == e)
return p;
p = p->succ;
}
return nullptr;
}
ListNode<T>* find(T const& e){
return find(e, _size, header->succ);
}
ListNode<T>* search(T const& e, int n, ListNode<T>* p) const{
assert(0 <= n && n <= _size);
while(n-- > 0)
if(((p=p->pre)->data) <= e)
break;
return p; //use validate to check if success
}
ListNode<T>* search(T const& e){
return search(e, _size, header->succ);
}
ListNode<T>* max(ListNode<T>* p, int n) const{
ListNode<T> *max = p;
while(n--){
if(max->data < p->data)
max = p;
p = p->succ;
}
return max;
}
ListNode<T>* max(){
return max(header->succ, _size);
}
// writable interface
ListNode<T>* insertAsFirst(T const& e){
return header->insertAsSucc(e);
}
ListNode<T>* insertAsLast(T const& e){
return trailer->insertAsPre(e);
}
ListNode<T>* insertAsPre(T const& e, ListNode<T>* p){
ListNode<T> *x = new ListNode(e, p->pre, p);
p->pre->succ = x;
p->pre = x;
_size++;
return x;
}
ListNode<T>* insertAsSucc(T const& e, ListNode<T>* p){
ListNode<T>* x = new ListNode(e, p, p->succ);
p->succ->pre = x;
p->succ = x;
_size++;
return x;
}
T remove(ListNode<T>* p){
T e = p->data;
p->pre->succ = p->succ;
p->succ->pre = p->pre;
delete p;
_size--;
return e;
}
void merge(LinkedList<T>& right){
merge(*this, _size, right, right._size);
}
void sort(ListNode<T>* p, int n){
}
void sort(){
sort(first(),_size)
}
int deduplicate(){
if(_size < 2)
return 0;
int oldSize = _size;
ListNode<T> *p = header->succ;
Rank r = 0;
while(p != trailer){
ListNode<T> *q = find(p->data, r, p);
q ? remove(q) : r++;
}
return oldSize - _size;
}
int uniquify(){
if(_size < 2)
return 0;
int oldSize = _size;
ListNode<T> *p, *q;
for(p=header, q=p->succ; q != trailer; p=q, q=p->succ){
if(p->data == q->data)
remove(q);
}
return oldSize - _size;
}
void reverse(){}
// traverse
void traverse(void (*visit)(T&)){
for(ListNode<T> *p = header->succ; p != trailer; p = p->succ)
visit(p->data);
}
template<typename VST> void traverse(VST& visit){
for(ListNode<T> *p = header->succ; p != trailer; p = p->succ)
visit(p->data);
}
};
双指针技巧
判断链表中是否有环
题目
思路
- 快慢指针遍历链表,若快指针能追上慢指针一圈则说明有环,否则无环
代码
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def hasCycle(self, head: Optional[ListNode]) -> bool:
if not head or not head.next: return False
slow,fast = head,head
while fast and fast.next:
slow,fast = slow.next, fast.next.next
if slow is fast: return True
return False
复杂度
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(1)
查找链表中环的起点
题目
思路
- 使用快慢指针
- 当慢指针进入环中,快指针已经进入环中,又因为快指针速度是慢指针两倍,所以快指针必定在一圈内追上慢指针
- 假设起点到环的起点距离为 a a a,环的起点到相遇点距离为 b b b,相遇点到终点距离为 c c c
- 慢指针走过的路程为 a + b a+b a+b,快指针走过的路程为 a + n ( b + c ) + b a+n(b+c)+b a+n(b+c)+b,又因为快指针速度为慢指针两倍,所以有 a + n ( b + c ) + b = 2 ( a + b ) a+n(b+c)+b = 2(a+b) a+n(b+c)+b=2(a+b),推出 a = n ( b + c ) − b a = n(b+c)-b a=n(b+c)−b
- 观察上式可知当慢指针再走 b + n ( b + c ) b+n(b+c) b+n(b+c)时,可以回到环的起点,而同样时长也可以从起点走到环起点
- 所以取链首,每次向前一格,慢指针每次向前一格,当相遇时则说明在环首
代码
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
if not head or not head.next: return None
slow,fast = head,head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
cur = head
while cur != slow:
cur = cur.next
slow = slow.next
return cur
return None
复杂度
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(1)
相交链表
题目
思路
- 分别遍历两条链表,获取两条链表的长度
- 长链表向前便宜长度差
- 两链表同时向前,交点即为所求
代码
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
a,b = headA,headB
lenA,lenB = 0,0
while a:
lenA+=1
a= a.next
while b:
lenB +=1
b = b.next
a,b = headA,headB
if lenA>lenB:
for i in range(lenA-lenB):
a=a.next
else:
for i in range(lenB-lenA):
b = b.next
while a!=b and a:
a=a.next
b=b.next
return a
复杂度
- 时间复杂度: O ( n + m ) O(n+m) O(n+m)
- 空间复杂度: O ( 1 ) O(1) O(1)
删除链表的倒数第N个节点
题目
思路
- 使用快慢指针,快指针先向前偏移N个结点;快慢指针同时前进,当快指针到达链尾时,慢指针恰好到达要删除的节点的前一个节点
- 若快指针直接到达链尾后,则直接删除头结点
代码
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
slow,fast = head,head
if not head.next: return None
for _ in range(n): fast = fast.next
while fast and fast.next:
slow = slow.next
fast = fast.next
if not fast: return head.next
slow.next = slow.next.next
return head
复杂度
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(1)
经典问题
反转链表
题目
题解1
思路
- 使用一个变量保存反转后的链表头,然后每一次将当前结点指向该头结点,然后两个节点顺势下移
代码
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
prev = None
cur = head
while cur:
t = cur.next
cur.next = prev
prev = cur
cur = t
return prev
复杂度
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(1)
题解2
思路
- 如图所示,反转子链表后,需将子链表的尾部(head.next.next)接到当前头部,然后再将当前头部和子链表尾部断开 否则成环
代码
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
if not head or not head.next: return head
reverse = self.reverseList(head.next)
head.next.next = head
head.next = None
return reverse
复杂度
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( n ) O(n) O(n)
奇偶链表
题目
思路
- 如上图所示,将odd,even分别指向奇节点尾部、偶节点尾部
- 然后将偶节点尾部的下一个结点插入到奇节点尾部
- 再将两个节点下移一格
代码
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def oddEvenList(self, head: ListNode) -> ListNode:
if not head or not head.next: return head
odd,even = head,head.next
while odd and even and odd.next and even.next:
t = even.next.next
even.next.next = odd.next
odd.next = even.next
even.next = t
odd,even = odd.next,even.next
return head
复杂度
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(1)
回文链表
题目
思路
- 将链表的第 ⌈ n 2 ⌉ \lceil \frac{n}{2} \rceil ⌈2n⌉个结点作为头翻转,然后比对两个子链是否相等
- 第 ⌈ n 2 ⌉ \lceil \frac{n}{2} \rceil ⌈2n⌉个结点可以使用快慢指针的方式找到,而翻转链表见 反转链表
代码
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
if not head.next: return True
slow,fast = head,head
while fast and fast.next: slow,fast = slow.next,fast.next.next
if fast: slow = slow.next
mid = slow
def reverse(head: ListNode):
prev = None
cur = head
while cur:
t = cur.next
cur.next = prev
prev = cur
cur = t
return prev
mid = reverse(mid)
left,right = head,mid
while right:
if left.val != right.val: return False
left,right = left.next,right.next
return True
复杂度
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(1)
应用
合并两个有序链表
题目
思路
- 选举头结点较小的作为初始链,设置两个指针分别指向两条链的当前位置
- 将较小的节点插入主链后方,若主链当前位置为较小则直接向前移动
代码
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
if not l1 or not l2:
return l1 if l1 else l2
if l1.val>l2.val: l1,l2 = l2,l1
head,i,j = l1,l1,l2
while i.next and j:
if i.next.val > j.val:
temp = j.next
j.next = i.next
i.next = j
j = temp
i=i.next
if j:
i.next = j
return head
复杂度
- 时间复杂度: O ( m + n ) O(m+n) O(m+n)
- 空间复杂度: O ( 1 ) O(1) O(1)
两数相加
题目
思路
- 从左往右遍历两条链表,将链表两个节点和进位的和添加到结果链表当中,并将记录进位
代码
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
overflow = 0
if(l1 or l2):
x = l1.val + l2.val + overflow
head = cur = ListNode(x if x < 10 else x % 10)
# print(cur.val)
overflow = x // 10
l1 = l1.next
l2 = l2.next
while(l1 or l2):
a = l1.val if l1 else 0
b = l2.val if l2 else 0
x = a + b + overflow
overflow = x // 10
cur.next = ListNode(x if x < 10 else x % 10)
# print(cur.val)
cur = cur.next
if l1: l1 = l1.next
if l2: l2 = l2.next
# while head:
# print(head.val)
# head = head.next
if overflow > 0:
cur.next = ListNode(overflow)
return head
复杂度
- 时间复杂度: O ( n + m ) O(n+m) O(n+m)
- 空间复杂度: O ( 1 ) O(1) O(1)
扁平化多级双向链表
题目
思路
- dfs链表,将回溯到的值保存在数组当中
- 将数组转化为链表
代码
"""
# Definition for a Node.
class Node:
def __init__(self, val, prev, next, child):
self.val = val
self.prev = prev
self.next = next
self.child = child
"""
from collections import deque
class Solution:
def flatten(self, head: 'Node') -> 'Node':
if not head: return head
res = []
def dfs(head: 'Node'):
if not head: return
res.append(head.val)
if head.child: dfs(head.child)
if head.next: dfs(head.next)
dfs(head)
head = Node(res[0])
cur = head
for n in res[1:]:
node = Node(n)
cur.next = node
node.prev = cur
cur = cur.next
return head
复杂度
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( n ) O(n) O(n)
复制带随机指针的链表
题目
题解1
思路
- 使用map保存旧节点和新节点之间的相互引用
- 当旧节点指向空的时候,从map中取值的时候需要判空
代码
"""
# Definition for a Node.
class Node:
def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
self.val = int(x)
self.next = next
self.random = random
"""
class Solution:
def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]':
if not head: return None
old,newHead = head.next,Node(head.val)
nodeMap = {head: newHead}
new = newHead
while old:
node = Node(old.val)
new.next = node
nodeMap[old] = node
old,new = old.next,new.next
old,new = head,newHead
while old:
new.random = nodeMap.get(old.random)
old,new = old.next,new.next
return newHead
复杂度
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( n ) O(n) O(n)
题解2
思路
- 复制过程,将新节点插到旧节点之后
- 复制完成之后将偶数节点全部取出
代码
"""
# Definition for a Node.
class Node:
def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
self.val = int(x)
self.next = next
self.random = random
"""
class Solution:
def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]':
if not head: return None
cur = head
while cur:
newNode = Node(cur.val)
newNode.next = cur.next
cur.next = newNode
cur = cur.next.next
cur = head
while cur:
cur.next.random = cur.random.next if cur.random else None
cur = cur.next.next
head = head.next
cur = head
while cur and cur.next:
cur.next = cur.next.next
cur = cur.next
return head
复杂度
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(1)
旋转链表
题目
思路
- 将链表首尾相连
- 旋转 k k k次相当于将链表的第 k m o d n k \mod n kmodn个节点断开
代码
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def rotateRight(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
if not head: return None
cur = head
length = 0
while cur.next:
cur = cur.next
length += 1
length += 1
cur.next = head
for _ in range(length - (k % length) - 1): head = head.next
newHead = head.next
head.next = None
return newHead
复杂度
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(1)
引用
题目源