代码随想录算法训练营第四天| 24. 两两交换链表中的节点,19.删除链表的倒数第N个节点 ,142.环形链表II

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;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值