1.反转链表
方法一:迭代 注意对于反转链表涉及到三个节点,因此需要设置3个指针,prev初始化为nullptr,curr初始化为head,而currnext指针在while迭代过程中初始化即可,另外在每一次迭代中仅需将curr节点指针反转即可
ListNode* reverseList(ListNode* head) {
ListNode* prev=nullptr;
ListNode* curr=head;
ListNode* cnext;
while(curr!=nullptr){
cnext=cnext->next;
curr->next=prev;
prev=curr;
curr=cnext;
}
return prev;
}
方法二:递归 这个思路是类似于后序遍历,从后往前操作节点
ListNode* reverseList(ListNode* head) {
if (!head || !head->next) {
return head;
}
ListNode* newHead = reverseList(head->next);
head->next->next = head;
head->next = nullptr;
return newHead;
}
2.求链表倒数第K个节点
方法一:快慢指针 设置一个临时指针fast,让fast指针先走k步,之后fast与head指针一直往后走,等fast指针为nullptr时,head也走到了倒数第K个节点的位置。注意两个while循环条件都是fast指针不为nullptr
ListNode* getKthFromEnd(ListNode* head, int k) {
ListNode *fast=head;
while(fast!=nullptr&&k>0){
fast=fast->next;
k--;
}
while(fast!=nullptr){
fast=fast->next;
head=head->next;
}
return head;
}
方法二:求出链表长度 这个方法较为简单粗暴,先一个循环求出链表长度n,再一个循环走n-k步即可
ListNode* getKthFromEnd(ListNode* head, int k) {
int n = 0;
ListNode* node = nullptr;
for (node = head; node; node = node->next) {
n++;
}
for (node = head; n > k; n--) {
node = node->next;
}
return node;
}
3.两条链表的第一个公共节点
类似于上面的先分别求出两条链表的长度,接着让长链表的指针先走abs(lenA-lenB)步,再两个链表的两个指针同时走直到相遇
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* tmpA=headA;ListNode* tmpB=headB;
int lenA=0;int lenB=0;
while(tmpA!=nullptr){
tmpA=tmpA->next;
lenA++;
}
while(tmpB!=nullptr){
tmpB=tmpB->next;
lenB++;
}
tmpA=headA;tmpB=headB;
if(lenA>lenB){
while(lenA!=lenB){
tmpA=tmpA->next;
lenA--;
}
}else{
while(lenB!=lenA){
tmpB=tmpB->next;
lenB--;
}
}
while(tmpA!=tmpB){
tmpA=tmpA->next;
tmpB=tmpB->next;
}
return tmpA;
}
4.判断链表是否有环
思路就是用快慢指针,fast每次移动两格(其实三格也可以,但是两格理论上更快),slow每次移动一格,如果有环迟早会相遇
- 时间复杂度:O(N),其中 N 是链表中的节点数。当链表中不存在环时,快指针将先于慢指针到达链表尾部,链表中每个节点至多被访问两次。当链表中存在环时,每一轮移动后,快慢指针的距离将减小一。而初始距离为环的长度,因此至多移动 N 轮。
- 空间复杂度:O(1)。我们只使用了两个指针的额外空间。
bool hasCycle(ListNode *head) {
if (head == nullptr || head->next == nullptr) {
return false;
}
ListNode* fast=head->next;
ListNode* slow=head;
while(slow!=fast){
if (fast == nullptr || fast->next == nullptr) {
return false;
}
fast=fast->next->next;
slow=slow->next;
}
return true;
}
5.环的入口
遇到链表题画图是一个很好的解决办法。图来自力扣官方解答
假设快慢指针在紫色点相遇,此时慢指针走过的路程为a+b,快指针走过的路程为a+b+n(b+c)。 为什么慢指针走过a+b就必然与快指针相遇,而不是慢指针走过a+b+m(b+c)呢?
首先,由于快指针一定先进入环内,这点毋庸置疑。 而且,快指针是慢指针速度的二倍,即慢指针走完一圈,快指针可以走两圈 所以不论慢指针入环时,快指针在哪一点,快指针都可以在慢指针未走过一圈时追上慢指针。 而由于快指针走过的节点数是慢指针的二倍,所以得到公式: (a + b) * 2 = a + b + n (b + c) 两边抵消 a+b,得到 a + b = n (b + c) 由于我们最终要求的是a,所以 a = n (b + c) - b 然而,此时慢指针在环里面所走过的路程刚好为b,如果此时有一个指针point从头开始走向环,即x路程 那么,慢指针刚好要走过的就是 n (b + c) - b + b = n (b + c) 即 point 走a的距离到达环的入口的时刻,刚好为slow走过n圈到达入口,两个指针相遇 也即point与slow两个指针相遇的时候就是那个入口!!!!
注意slow和fast都初始化为head指针
ListNode *detectCycle(ListNode *head) {
if(head==nullptr||head->next==nullptr)return nullptr;
ListNode* fast=head;
ListNode* slow=head;
while(1){
if(fast==nullptr||fast->next==nullptr)return nullptr;
slow=slow->next;
fast=fast->next->next;
if(slow==fast)break;
}
ListNode* point=head;
while(slow!=point){
slow=slow->next;
point=point->next;
}
return slow;
}