LeetCode24.两两交换链表中的节点
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
思路:交换过程其实并不复杂,设计到过程模拟,设置虚拟节点以便最后能够轻易地找到并返回头节点,利用临时节点来存储操作结点的前后,方便将next域进行交换,同时注意循环中边界条件的判断,由此便能解决该问题。
尤其注意交换的次序,建议画图理解,以免出错。
时间复杂度:O(n);
空间复杂度:O(1);
ListNode* swapPairs(ListNode* head) {
if(head == NULL) return NULL; //当链表为空时直接返回
ListNode* dummyNode = new ListNode(0, head); //设置虚拟节点,
//以便完成交换后能够返回头节点
ListNode* tmp1 = dummyNode; //设置前置暂存节点指针
ListNode* p = head;
ListNode* tmp2; //设置后置暂存节点指针
while( p != NULL){
tmp2 = p -> next;
if(tmp2 == NULL) break; //判断当链表数为奇数时直接终止循环
p -> next = tmp2 ->next;
tmp2 -> next = p;
tmp1 -> next = tmp2;
tmp1 = p; //开始位置交替
p = p ->next;
}
head = dummyNode -> next;
delete dummyNode;
return head;
}
LeetCode19.删除链表
给你一个链表,删除链表的倒数第
n
个结点,并且返回链表的头结点
思路:有两种思路可以解决,首先将链表反转,这样的话删除倒数的第n个结点即为删除反转后的链表的第n个结点,删除完后,再将链表反转,返回头节点。但是这种方法太麻烦了,不建议采用。
比较推荐的是利用双指针的方法来解决。简单来说就是fast和slow结点都从头节点开始,fast首先走n+1步,而后fast和slow一起向后移动,直到fast==NULL,此时slow的next结点即为需要删除的节点,相当于是做好了一把“尺子”,往后面去量,从而解决该问题。
当然,有人会问,为什么fast是移动n+1步呢?
原因在于对于要删除的节点,只有当处于其前一个结点是,才好操作将其删除,而这正好是要让slow处于要删除的节点的前面,方便删除。
时间复杂度:O(n);
空间复杂度:O(1);
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyNode = new ListNode(0, head);//创建虚拟节点,方便后续返回头节点
ListNode* fast = dummyNode;
ListNode* slow = dummyNode; //定义双指针
n += 1; //fast指针需要移动的步数
while(n --){ //fast指针先移动n+1步
fast = fast -> next;
}
while(fast != NULL){
fast = fast -> next;
slow = slow -> next;
}
ListNode* tmp = slow -> next;
slow -> next = slow -> next -> next;
delete tmp;
head = dummyNode -> next;
return head;
}
面试题 02.07. 链表相交
给你两个单链表的头节点
headA
和headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回null
。
思路:这道题的关键在于理解最后判断的是指针是否相等,而当数值相当时不一定就是要找的交点。
简单来说,就是首先分别求两个链表的长度,取最长的链表为标准,率先移动两个链表长度差值单位的步数,使得长链表与短链表后部对齐,然后再统一共同向后移动,直到当两个指针(而不是数值)相同时,说明有交点,返回该交点节点即可,反之若循环结束了都没有相同,则返回NULL结束。
时间复杂度:O(n+m);
空间复杂度:O(1);
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* curA = headA;
ListNode* curB = headB;
int lenA = 0, lenB = 0;
//统计headA的长度
while(curA != NULL){
lenA ++;
curA = curA -> next;
}
//统计headB的长度
while(curB != NULL){
lenB ++;
curB = curB -> next;
}
//重新指向头节点
curA = headA;
curB = headB;
//将curA指向长度最长的链表头节点
if(lenA < lenB){
swap(lenA, lenB);
swap(curA, curB);
}
int gap = lenA - lenB;//计算差值
//将两个链表后半部分对齐,长的链表先移动
while(gap --){
curA = curA -> next;
}
while(lenB --){
if(curA == curB){ //注意这里比较的是指针是否相同,不是说数值相同就返回该节点
return curA;
}
curA = curA -> next;
curB = curB -> next;
}
return NULL; //循环结束,仍然没有找到相交的节点
}
LeetCode142.环形链表II
给定一个链表的头节点
head
,返回链表开始入环的第一个节点。 如果链表无环,则返回null
。如果链表中有某个节点,可以通过连续跟踪
next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果pos
是-1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。不允许修改 链表。
思路:需要解决两个问题,首先是判断是否有环,若没有,则返回NULL结束;反之如果有环,如何找到循环的入口结点。
这里采用了双指针的方法,fast指针和slow指针。fast每次走两步,slow每次走一步,如果链表中存在环,那么在移动过程中,必然会存在fast和slow相遇的时候,因为在有环条件下,在环中fast是相对于slow一步的距离移动,必然在某个时候两个结点会相遇;当有环确定好了后,从相遇节点和开始节点设置索引指针,每次移动一步,当相遇时,即可得知该相遇点为入口节点。
这里我只是简单了说明了解题方法,可以尝试画图,引入数学变量的方式来验证。
时间复杂度:O(n);
空间复杂度:O(1);
ListNode *detectCycle(ListNode *head) {
ListNode* fast = head;
ListNode* slow = head;
while(fast != NULL && fast -> next != NULL){
slow = slow -> next;
fast = fast -> next -> next;
if(fast == slow){ //说明存在环
ListNode* index1 = fast;
ListNode* index2 = head;//设置索引
while(index1 != index2){
index1 = index1 -> next;
index2 = index2 -> next;
}
return index1; //当index1==index2时,说明找到了入环节点
}
}
return NULL; //不存在环,直接返回NULL
}