反转链表
- 要点:虚拟头结点
- 三种方式:头插法、迭代法和递归法
- 力扣题目链接
- 头插法:
ListNode* reverseList(ListNode* head) {
if(head==nullptr) return nullptr;
ListNode *vhead=new ListNode(); //虚拟头结点
ListNode *cur=head; //工作结点
ListNode *tmp1, *tmp2;
do{ //头插法
tmp1=cur->next; //待插入结点链表下一个,保证不断链
tmp2=vhead->next; //虚拟头结点链表下一个,保证不断链
cur->next=tmp2;
vhead->next=cur;
cur=tmp1;
}while(tmp1!=nullptr);
return vhead->next;
//或把上面那句改成,但释放不释放空间其实不要紧
//cur=vhead->next;
//delete vhead;
//return cur;
}
- 迭代法(理解递归法的基础):
ListNode* reverseList(ListNode* head) {
ListNode* temp; //保存cur的下一个节点
ListNode* cur=head;
ListNode* pre=NULL;
while(cur){
temp = cur->next; //保存一下cur的下一个节点
cur->next = pre; //翻转操作
//更新pre和cur指针
pre=cur;
cur=temp;
}
return pre;
}
- 递归法:
ListNode* reverseList(ListNode* head) {
if(!head || !head->next) return head;
//newHead就是head->next
ListNode* newHead=reverseList(head->next);
head->next->next=head;
head->next=nullptr;
return newHead;
}
- 算法复杂度:时间复杂度为O(n),空间复杂度为O(1)(递归为O(n))
两两交换链表中的节点
- 要点:理解反转链表这道题处理差不多,有递归和非递归写法
- 力扣题目链接
- 非递归解法:
ListNode* swapPairs(ListNode* head) {
ListNode *vhead=new ListNode();
vhead->next=head;
ListNode *cur=vhead;
while(cur->next!=nullptr&&cur->next->next!=nullptr){
ListNode *tmp1=cur->next->next->next; //不断链
ListNode *tmp=cur->next;
cur->next=tmp->next;
cur->next->next=tmp;
cur->next->next->next=tmp1;
cur=cur->next->next; //后移两格
}
return vhead->next;
}
- 递归解法:
ListNode* swapPairs(ListNode* head) {
if(head == nullptr || head->next == nullptr) return head;
ListNode* newHead = head->next;
head->next = swapPairs(newHead->next);
newHead->next = head;
return newHead;
}
- 算法复杂度:时间复杂度为O(n),空间复杂度为O(1)(递归为O(n))
删除倒数第N个节点
- 基本方法:虚拟头结点和双指针法
- 力扣题目链接
- 解法:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode *vhead=new ListNode();
vhead->next=head;
ListNode *fast=vhead, *slow=vhead;
for(int i=0; i<n; i++) fast=fast->next;
while(fast->next!=nullptr){
slow=slow->next;
fast=fast->next;
}
slow->next=slow->next->next; //删除结点
return vhead->next;
}
- 算法复杂度:时间复杂度为O(n),空间复杂度为O(1)
链表相交
- 两种方法:右对齐法和交替遍历法
- 交替遍历法正确性证明:
情况一:两个链表相交
链表 headA 和 headB 的长度分别是 m 和 n。假设链表 headA 的不相交部分有 a 个节点,链表 headB 的不相交部分有 b 个节点,两个链表相交的部分有 c 个节点,则有 。
- 如果,则两个指针会同时到达两个链表相交的节点,此时返回相交的节点;
- 如果,则指针 pA 会遍历完链表 headA,指针 pB 会遍历完链表 headB,两个指针不会同时到达链表的尾节点,然后指针 pA 移到链表 headB 的头节点,指针 pB 移到链表 headA 的头节点,然后两个指针继续移动,在指针 pA 移动了次、指针 pB 移动了次之后,两个指针会同时到达两个链表相交的节点,该节点也是两个指针第一次同时指向的节点,此时返回相交的节点。
情况二:两个链表不相交
链表 headA 和 headB 的长度分别是 m 和 n。考虑当和的情况:
- 如果,则两个指针会同时到达两个链表的尾节点,然后同时变成空值,返回null
- 如果,则由于两个链表没有公共节点,两个指针也不会同时到达两个链表的尾节点,因此两个指针都会遍历完两个链表,在指针 pA 移动了次、指针 pB 移动了次之后,两个指针会同时变成空值,返回null
- 力扣题目链接
- 右对齐法比较朴素,需要计算链表的长度:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
int size_a=getSize(headA);
int size_b=getSize(headB);
ListNode *a=headA, *b=headB;
if(size_a>size_b)
for(int i=0;i<size_a-size_b;i++) a=a->next;
else if(size_a<size_b)
for(int i=0;i<size_b-size_a;i++) b=b->next;
while(a!=b && a!=nullptr){
a=a->next;
b=b->next;
}
return a;
}
int getSize(ListNode *head){
int size=0;
while(head!=nullptr){
size++;
head=head->next;
}
return size;
}
- 交替遍历法:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if(headA==nullptr || headB==nullptr) return nullptr;
ListNode *pa=headA, *pb=headB;
while(pa!=pb){
pa = pa==nullptr ? headB : pa->next;
pb = pb==nullptr ? headA : pb->next;
}
return pa;
}
- 算法复杂度:时间复杂度为O(m+n),空间复杂度为O(1)
环形链表II
-
要点:判断是否有环,并且找出入口在哪儿
-
双指针法正确性证明要点:参考题解
- 判断无环:fast指针走过链表末端,说明链表无环,此时直接返回null
- 有环的情况下,快慢指针一定相遇
- 找入口(第二次相遇):head指针到入口处的步数=slow指针继续走到入口的步数
- 力扣题目链接
- 直接法(哈希表):
ListNode *detectCycle(ListNode *head) {
unordered_set<ListNode*> visited;
while(head!=nullptr){
if(visited.count(head)) return head;
visited.insert(head);
head=head->next;
}
return nullptr;
}
- 双指针法:
ListNode *detectCycle(ListNode *head) {
ListNode *fast=head, *slow=head;
while(true){
if(fast==nullptr || fast->next==nullptr) return nullptr;
fast=fast->next->next;
slow=slow->next;
if(fast==slow) break;
} //第一次相遇判断有环
fast=head; //构造第二次相遇找到入口
while(slow!=fast){
slow=slow->next;
fast=fast->next;
}
return fast;
}
- 算法复杂度:时间复杂度为O(n),快慢指针相遇前,指针走的次数小于链表长度,快慢指针相遇后,两个index指针走的次数也小于链表长度,总体为走的次数小于 2n;空间复杂度为O(1)(直接法为O(n))
参考资料
[1] 代码随想录
[2] Leetcode题解