/*

面试题 02.07. 链表相交

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

图示两个链表在节点 c1 开始相交:

其实就是观察两个链表,如果相交,那么两个链表会同时遍历到相同的节点。所以

就是相同部分前去一个,是不同的,相同部分两个链表都相同

*/

#include <iostream>
#include <vector>
using namespace std;
struct ListNode {
    int val;
    ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};
class Solution{
public:
     ListNode *getIntersectionNode(ListNode *headA, ListNode *headB){
        if (headA == NULL || headB == NULL){
            return NULL;
        } 
        ListNode *pA = headA, *pB = headB;
        //cout << headB->next->val;
        int flg = 1;
        int flg2 = 1;
        while (pA != pB){
            cout << pA->val;
            cout << pB->val;
            pA = pA->next;
            pB = pB->next;
            if(pA == NULL && flg ){
                pA = headB;
                flg = 0;
            }
            if(pB == NULL && flg2 ){
                pB = headA;
                flg2 = 0;
            }
            //pA = pA == NULL ? headB  : pA->next;
            //cout << "a" << pA->val;
            //pB = pB == NULL ? headA : pB->next;
           // cout << "b" << pB->val;
        }
        return pA;
    }
};
void createListNode(vector<int> &a, ListNode *&head){
    ListNode *p = new ListNode(a[0]);
    head = p;
    for (int i = 1;i < a.size();++i){
        p->next = new ListNode(a[i]);
        p = p->next;
    }
}
int main(){
    vector<int> a = {1,4,2};
    vector<int> b = {9,5,3,1};
    ListNode *headA = new ListNode(a[0]);
    ListNode *headB = new ListNode(b[0]);
    createListNode(a, headA);
    createListNode(b, headB);
    ListNode *pA = headA;
    Solution s;
    ListNode *res = s.getIntersectionNode(headA, headB);
    if (res != NULL) { // 先检查res是否为NULL
        std::cout << res->val << endl; // 输出相交节点的值,并加上换行符以美化输出
    } else {
        std::cout << "No intersection found." << endl; // 如果没有相交节点,则输出提示信息
    }
    return 0;
    /*
        ListNode *pB = headB;
    for (int i = 1;i < a.size();++i){
        pA->next = new ListNode(a[i]);
        pA = pA->next;
    }
    for (int i = 1;i < b.size();++i){
        pB->next = new ListNode(b[i]);
        pB = pB->next;
    }
    */
}

/*

leetcode 142 环形链表 II

题意: 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

为了表示给定链表中的环,使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

说明:不允许修改给定的链表。

head = {3, 2, 0, -4}, pos = 1

输出:tail connects to node index 1

解释:链表中有一个环,其尾部连接到第二个节点。

*/

#include <iostream>
#include <vector>
using namespace std;

struct ListNode {
    int val;
    ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};
class Solution{
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode* slow = head, *fast = head;
        while (fast != NULL && fast->next != NULL){
            slow = slow->next;
            fast = fast->next->next;
            // 快慢指针相遇,此时从head 和 相遇点,同时查找直至相遇
            if (slow == fast) {
                ListNode* index1 = fast;
                ListNode* index2 = head;
                while (index1 != index2) {
                    index1 = index1->next;
                    index2 = index2->next;
                }
                return index2; // 返回环的入口
            }
        }
        return NULL;
    }
};
int main(){
    vector<int> nums = {3, 2, 0, -4, 5 };
    ListNode* head = new ListNode(nums[0]);
    ListNode* node = head;ListNode* cur = head;
    ListNode* posNode = nullptr; // 用于保存尾部连接的节点
    for (int i = 1; i < nums.size(); i++) {
        node->next = new ListNode(nums[i]);
        node = node->next;
        if (i == 2) {
            posNode = node; // 记录尾部连接的节点
        }
    }
    // 将尾部连接的节点连接到索引为1的节点,形成环
    node->next = posNode;

    Solution sol;
    ListNode* res = sol.detectCycle(head);
    if (res == NULL) {
        cout << "no cycle" << endl;
    }
    else {
        cout << "cycle, tail connects to node with value: " << res->val << endl;
    }
    return 0;
}

关于链表,该了解这些,介绍了如下几点:

  • 链表的种类主要为:单链表,双链表,循环链表
  • 链表的存储方式:链表的节点在内存中是分散存储的,通过指针连在一起。
  • 链表是如何进行增删改查的。
  • 数组和链表在不同场景下的性能分析

链表经典题目

#虚拟头结点

在链表:听说用虚拟头节点会方便很多? (opens new window)中,我们讲解了链表操作中一个非常重要的技巧:虚拟头节点。

链表的一大问题就是操作当前节点必须要找前一个节点才能操作。这就造成了,头结点的尴尬,因为头结点没有前一个节点了。

每次对应头结点的情况都要单独处理,所以使用虚拟头结点的技巧,就可以解决这个问题。

在链表:听说用虚拟头节点会方便很多? (opens new window)中,我给出了用虚拟头结点和没用虚拟头结点的代码,大家对比一下就会发现,使用虚拟头结点的好处。


#链表的基本操作

在链表:一道题目考察了常见的五个操作! (opens new window)中,我们通过设计链表把链表常见的五个操作练习了一遍。

这是练习链表基础操作的非常好的一道题目,考察了:

获取链表第index个节点的数值

在链表的最前面插入一个节点

在链表的最后面插入一个节点

在链表第index个节点前面插入一个节点

删除链表的第index个节点的数值

可以说把这道题目做了,链表基本操作就OK了,再也不用担心链表增删改查整不明白了。

这里我依然使用了虚拟头结点的技巧,大家复习的时候,可以去看一下代码。

#反转链表

在链表:听说过两天反转链表又写不出来了? (opens new window)中,讲解了如何反转链表。

因为反转链表的代码相对简单,有的同学可能直接背下来了,但一写还是容易出问题。

反转链表是面试中高频题目,很考察面试者对链表操作的熟练程度。

两种反转的方式,迭代法和递归法。

建议先学透迭代法,然后再看递归法,因为递归法比较绕,如果迭代还写不明白,递归基本也写不明白了。

可以先通过迭代法,彻底弄清楚链表反转的过程!

#删除倒数第N个节点

在链表:删除链表倒数第N个节点,怎么删? (opens new window)中我们结合虚拟头结点 和 双指针法来移除链表倒数第N个节点。

#链表相交

链表:链表相交 使用双指针来找到两个链表的交点(引用完全相同,即:内存地址完全相同的交点)

#环形链表

在链表:环找到了,那入口呢?在链表如何找环,以及如何找环的入口位置。

这道题目可以说是链表的比较难的题目了。 但代码却十分简洁,主要在于一些数学证明。