链表(指针)

先定义结构体:

struct ListNode {
    int val;
    ListNode *next;
    ListNode() : val(0), next(nullptr) {}
    ListNode(int x) : val(x), next(nullptr) {}
    ListNode(int x, ListNode *next) : val(x), next(next) {}
};
160.相交链表

错误的写法:

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        if (*headA == nullptr || *headB == nullptr) return null;
        ListNode *A = *headA, *B = *headB;
        while (*A != *B) {
            *A = *A != nullptr ? *A->next : *headB;
            *B = *B != nullptr ? *B->next : *headA;
        }
        return *A;
    }
};

正确的写法:

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        if (headA == nullptr || headB == nullptr) return nullptr;
        ListNode *A = headA, *B = headB;
        while (A != B) {
            A = A != nullptr ? A->next : headB;
            B = B != nullptr ? B->next : headA;
        }
        return A;
    }
};

解释一下这两者之间的区别:

  1. ListNode *A = headA; 这行代码中,我们直接将headA的值(也就是一个ListNode类型的指针)赋给了A。这里没有进行任何解引用操作,A现在是一个指针变量,它存储了headA的值,即指向链表中某个节点的地址。

  2. ListNode *A = *headA; 这行代码中,我们对headA进行了解引用操作,这意味着我们试图获取headA指向的节点的值(而不是节点的地址)。然而,这里存在一个逻辑问题,因为headA本身就是一个指针,它的解引用(*headA)将得到一个ListNode类型的实际节点,而不是一个地址。所以,如果我们尝试将一个ListNode类型的节点赋值给一个指向指针的指针变量A,这在类型上是不匹配的,会导致编译错误。

正确的做法是使用第一种赋值方式,即直接将headA(一个指针)赋值给A(另一个指针变量)。这样,A就成为了指向headA所指向节点的一个新的指针。这样做的目的是创建一个新的指针,而不是试图获取或修改链表中的节点。

总结来说,ListNode *A = headA;是正确的赋值方式,因为它创建了一个指向相同节点的新指针。而ListNode *A = *headA;是不正确的,因为它试图将一个节点的值赋给一个指针变量,这在类型上不匹配,并且不是我们想要的操作。


如果想将指针A的值赋给另一个指针B,应该使用ListNode *B = A;这样的赋值语句。这会创建一个新的指针B,它指向与A相同的ListNode对象。

在C++中,指针之间的赋值是非常直接的,因为指针本质上就是存储地址的变量。将一个指针赋值给另一个指针时,实际上是将地址复制到了新的指针变量中。这样,两个指针变量都将指向相同的内存地址,也就是同一个ListNode对象。

例如:

ListNode *A = headA; // A 指向 headA 所指向的 ListNode 对象
ListNode *B = A;      // 现在 B 也指向与 A 相同的 ListNode 对象

在这个例子中,AB都是指针,它们都存储了指向headA所指向的ListNode对象的地址。因此,AB都指向链表中的同一个节点。如果通过AB对节点进行修改,这些修改将反映在另一个指针所指向的节点上,因为它们指向的是同一个对象。

这种指针之间的赋值在处理链表、树结构或其他复杂的数据结构时非常常见,因为它允许在不同的函数或代码块中传递对同一块内存区域的引用。


在语句 ListNode *A = headA; 中,等号左边的 * 不是解引用操作,而是声明操作符,用于声明 A 为一个指向 ListNode 类型的指针变量。

在这个上下文中,ListNode * 是一种类型声明,它告诉编译器 A 是一个指针,它将存储一个指向 ListNode 类型对象的内存地址。而 = headA 部分则是初始化部分,它将 A 指向 headA 所指向的地址,即 headA 本身就是一个指针,所以 A 现在持有了 headA 所指向的地址。

  • ListNode *:这是一个指针类型的声明,A 是一个指针变量。
  • A:这是一个变量名,现在是一个指向 ListNode 类型的指针。
  • = headA:这是一个初始化表达式,它将 A 指向和 headA 相同的地址。

所以,整个语句 ListNode *A = headA; 创建了一个名为 A 的指针变量,并将其初始化为指向 headA 所指向的 ListNode 对象。这里没有发生解引用操作,而是创建了一个指针,它存储了一个地址。(所以我更喜欢写成ListNode* A = headA)


表达式 *A = *headA 涉及到两个不同的操作:解引用和赋值。

在这个表达式中:

  • *A 是对指针 A 进行解引用操作。这意味着你正在访问 A 所指向的内存地址上的值。如果 A 是一个有效的指针,*A 将会得到该地址上存储的值。

  • *headA 同样是解引用操作,它访问 headA 指针所指向的内存地址上的值。

  • = 是赋值操作符,它将右侧的值(在这个例子中是 *headA)赋给左侧的变量(在这个例子中是 *A)。

所以,*A = *headA 这个表达式的含义是:将 headA 指针指向的 ListNode 对象的值(即 headA 指向的地址上存储的内容),赋值给 A 指针目前指向的地址上。这实际上是将两个指针指向的 ListNode 对象的值进行了交换或者复制。

然而,这个操作通常不是你想要的,因为它改变了 A 指针指向的 ListNode 对象的内容,而不是改变了 A 指针本身。如果你的目的是让 A 指向 headA 所指向的同一个 ListNode 对象,你应该直接使用 A = headA; 这样的赋值操作。

在处理指针时,重要的是要清楚你的意图是什么,以及你想要操作的是指针本身(通过赋值操作),还是指针指向的值(通过解引用操作)。错误的操作可能会导致程序行为不符合预期。

141.环形链表
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
        ListNode* slow = head;
        ListNode* fast = head;
        while(slow != nullptr && slow->next != nullptr) {
            slow = slow->next;
            fast = fast->next->next;
            if (slow == fast) return true;
        }
        return false;
    }
};
class Solution {
public:
    bool hasCycle(ListNode *head) {
        ListNode* slow = head;
        ListNode* fast = head;
        while(fast != nullptr && fast->next != nullptr) {
            slow = slow->next;
            fast = fast->next->next;
            if (slow == fast) return true;
        }
        return false;
    }
};

第一个代码是错误的,第二个代码是正确的。第一个代码中的错误是由于fast一定比slow快,slow能用不代表fast也能用,fast能用则slow一定能用,所以判断slow可能导致fast在跳跃的时候空指针解引用。


递归这玩意是谁发明的?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值