Leetcode 24. 两两交换链表中的节点
思路分析:
两两交换链表元素和数组中元素两两交换是一样。可采用迭代法或递归法求解。迭代法更加直观,用cur_node两个一更新的循环,在循环里面交换元素,用临时变量存储cur_node->next和cur_node->next->next->next。另外为保证首元素也能被交换,定义虚节点dummy_node,令dummy_node->next为head. 最后返回dummy_node->next。
递归法的实现代码非常简洁,根据cur_node和cur_node->next定义好终止条件,然后单层逻辑里实现两两元素的交换。
代码实现:
递归法:
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* cur_node = head;
if (cur_node==nullptr || cur_node->next==nullptr) {
return cur_node;
}
ListNode* next_node = cur_node->next;
cur_node->next = swapPairs(next_node->next);
next_node->next = cur_node;
return next_node;
}
};
迭代法:
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummy_node = new ListNode(0);
dummy_node->next=head;
ListNode* cur_node = dummy_node;
while (cur_node->next && cur_node->next->next) {
ListNode* tmp1 = cur_node->next;
ListNode* tmp2 = cur_node->next->next->next;
cur_node->next=cur_node->next->next;
cur_node->next->next=tmp1;
cur_node->next->next->next=tmp2;
cur_node=cur_node->next->next;
}
return dummy_node->next;
}
};
Leetcode 19.删除链表的倒数第N个节点
思路分析
直观的解法可以先统计链表的长度,方便正向处理链表。如果n和链表长度相等,则返回原始链表的第一个结点。其他情况可采取倒数n+1结点的next指向第n-1个结点(即覆盖)。
还有效率更高更巧妙的解法:双指针(快慢指针法),让fast指针先走n+1步,然后fast和slow同步走,等fast指向nullptr时,slow指针即只想待删除的倒数第N个节点的上一结点,然后用slow->next指向slow->next->next,即可实现删除倒数第N个节点的结果。
本题使用栈也很巧妙,使用一个栈来存储链表的所有节点。首先,依次将链表的节点入栈。然后,从栈中弹出n个节点,即倒数第n个节点。如果弹出的节点是头节点,则直接返回头节点的下一个节点作为新的头节点。否则,取出弹出节点的前一个节点(prevNode),将其next指针指向弹出节点的下一个节点,完成删除操作。最终返回头节点作为新的链表头结点。
代码实现
方法一:统计链表长度,正向删除对应结点
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
int len = 0;
int count = 0;
ListNode* cur_node = head;
while (cur_node != nullptr) {
len++;
cur_node = cur_node->next;
}
if (n == len) {
return head->next;
}
ListNode* tmp_node = head;
while (tmp_node != nullptr && tmp_node->next != nullptr) {
count++;
if (count == len - n) {
tmp_node->next = tmp_node->next->next;
} else {
tmp_node = tmp_node->next;
}
}
return head;
}
};
方法二:快慢指针(双指针)法
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode* slow = dummyHead;
ListNode* fast = dummyHead;
while(n-- && fast != nullptr) {
fast = fast->next;
}
fast = fast->next; // fast再提前走一步,因为需要让slow指向删除节点的上一个节点
while (fast != nullptr) {
fast = fast->next;
slow = slow->next;
}
slow->next = slow->next->next;
return dummyHead->next;
}
};
方法三:用stack
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
stack<ListNode*> nodeStack;
ListNode* currentNode = head;
// 将链表节点入栈
while (currentNode) {
nodeStack.push(currentNode);
currentNode = currentNode->next;
}
// 弹出并删除倒数第n个节点
for (int i = 0; i < n; i++) {
nodeStack.pop();
}
if (nodeStack.empty()) {
return head->next; // 要删除的是头节点
} else {
ListNode* prevNode = nodeStack.top();
prevNode->next = prevNode->next->next;
return head;
}
}
};
Leetcode 142.环形链表II
思路分析
本题首先需要确定链表中是否存在环,其次是找到入环的节点。可以实现快慢指针(双指针)法,如果fast和slow节点能相遇,则说明该链表中存在环,反之亦真。
然后根据快慢指针行驶速度和相遇的特征,可以分析出:从相遇节点和从head节点到入环节点的步数是一样的,因此可再建立双指针tmp1和tmp2,tmp1和tmp2相遇的节点即为入口节点。
当然本题还有直观解法,使用哈希表记录每个节点,如果出现之前已经出现的元素,则说明找到了入口元素,直接返回。
代码实现
快慢指针法
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* fast_node = head;
ListNode* slow_node = head;
ListNode* meeting_node = nullptr;
ListNode* entrance_node = new ListNode(0);
while (fast_node!=nullptr && fast_node->next!=nullptr) {
fast_node = fast_node->next->next;
slow_node = slow_node->next;
if (fast_node == slow_node) {
meeting_node = fast_node;
ListNode* tmp1 = head;
ListNode* tmp2 = meeting_node;
while (tmp1 != tmp2) {
tmp1 = tmp1->next;
tmp2 = tmp2->next;
}
entrance_node = tmp1;
return entrance_node;
}
}
return nullptr;
}
};
哈希表法
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if (head == nullptr || head->next == nullptr) {
return nullptr;
}
std::unordered_set<ListNode*> visited;
while (head != nullptr) {
if (visited.count(head)) {
return head;
}
visited.insert(head);
head = head->next;
}
return nullptr;
}
};