题目描述:
给链表头节点 head 和 整数 k, 交换链表正数第k个节点和倒数第k个节点,返回头节点。
示例:
输入:head = [7,9,6,6,7,8,3,0,9,5], k = 5
输出:[7,9,6,6,8,7,3,0,9,5]
算法1:(交换两个节点)
交换节点需要找到要交换的节点的前一个节点。
1、遍历链表,得到链表总长度 n。
2、找到正数第k个节点的前一个节点 记为 p,也即走k-1步。
3、找到倒数第k个节点的的前一个节点 记为 q,走 n-k 步。
4、交换两个节点,需要维护四个节点,节点 p, q, 要交换的左节点 ptl, 要交换的右节点的下一个节点 ptr;
注意: 有两种例外情况需要判掉,否则会造成链表指针自己指向自己的问题。
4.1 特判: 当正数第 k 个节点在倒数第 k 个节点的相邻左边的时候
4.2 特判: 当正数第 k 个节点在倒数第 k 个节点的相邻右边的时候
其实这里可以直接特判当 p 和 q 相邻的时候,直接 reverse 一下两个节点会少一点代码量。
时间复杂度分析:
O(N)
C++代码实现:
class Solution {
public:
ListNode* swapNodes(ListNode* head, int k) {
if(!head ||!head->next) return head;
ListNode* dummy = new ListNode(-1);
dummy->next = head;
// 计算链表总长度
auto ptr = head;
int n = 0;
for(;ptr;ptr=ptr->next) n++;
// 正数第k个节点前一个节点,倒数第k个节点后一个节点
auto p = dummy, q = dummy;
for(auto i = 0; i < k - 1; i++) p = p->next;
int cnt = n - k;
for(auto i = 0; i < cnt; i++) q = q->next;
// 特判 当正数第k个节点在倒数第k个节点左边相邻
if(p->next == q){
auto temp = q->next->next;
p->next = q->next;
q->next->next = q;
q->next = temp;
}
// 特判 当正数第k个节点在倒数第k个节点右边相邻
else if(q->next == p){
auto temp = p->next->next;
q->next = p->next;
p->next->next = p;
p->next = temp;
}
else{
ListNode* ptl = p->next;
ListNode* ptr = q->next->next;
p->next = q->next;
q->next->next = ptl->next;
q->next = ptl;
ptl->next = ptr;
}
return dummy->next;
}
};
算法2:交换两个节点值
这种方式很取巧,之前很多题都特意说不能仅交换节点值,面试时可以跟面试官交流一下这种方法是否可行。
代码实现:
class Solution {
public:
ListNode* swapNodes(ListNode* head, int k) {
if(!head ||!head->next) return head;
ListNode* dummy = new ListNode(-1);
dummy->next = head;
// 计算链表总长度
auto ptr = head;
int n = 0;
for(;ptr;ptr=ptr->next) n++;
// 正数第k个节点前一个节点,倒数第k个节点后一个节点
auto p = dummy, q = dummy;
for(auto i = 0; i < k - 1; i++) p = p->next;
int cnt = n - k;
for(auto i = 0; i < cnt; i++) q = q->next;
// 交换节点值
swap(p->next->val, q->next->val);
return dummy->next;
}
};