ListNode链表结构超详细解析,LeetCode题解

LeetCode第二题两数之和中,用到了ListNode这么一个结构。记录下解题思路和步骤。

ListNode结构:

ListNode是一个链表结构,首先我们要知道链表是什么。

链表是由数据+指针构造而成的,指针指向下一个节点的地址。如下图:

所以我们在定义ListNode结构的时候,就需要定义两个属性,一个是当前节点的值,一个是指向下一个节点地址,用来实现指针的功能。

class ListNode{
        int val;   //数据
        ListNode next;   //指向下一个节点的地址
}

可以加上构造函数:

class ListNode{
        int val;
        ListNode next;
        ListNode(int x) { val = x; }
        ListNode(int x, ListNode y) { val = x; next = y; }
}

假如说要定义[2, 4, 6]这么一个链表,也可以这么赋值,这样我感觉会更清晰的理解到链表的结构。

ListNode l1 = new ListNode(2, new ListNode(4, new ListNode(6)));

题解:

清楚链表的结构后,回到题目上。原题如下:

题目的意思是,两个链表的数字都是按照逆序方式存储。

2->4->3,对应的数字是342;5->6->4,对应的数字是465。相加得出807,对应的链表是7->0->8。

所以,如果两个链表直接相加,得出来的数其实是不对的。这里存在一个满十进位的问题。正确的操作应该是,链表相同位置的值相加后再加上前一节点进位的数, % 10得出本节点的数,➗ 10得出进位数。如果加到最后一个节点,发现进位数>0,那么需要在链表的尾部追加进位数。

如果两个链表长度不一致,那么缺少的数可以视为0。

举个🌰:

以题中两个链表为例,第一位2 + 5 + 上一节点进位数 = 7,7 / 10 = 0 进位数为0;7 % 10 = 7,  头部节点为7。

第二位4 + 6 + 上一节点的进位数 = 10,10 / 10 = 1,进位数为1;10 % 10 = 0  ,本节点数为 0。

第三位3 + 4 + 上一节点的进位数1 = 7,7 / 10 = 0,进位数为0;7 % 10 = 7 ,本节点数为8。

结果值为7->0->8

再举个🌰:

[9,9,9]和[9,9]两个链表相加

第一位9 + 9 +上一节点的进位数= 18,18 / 10 = 1,进位数= 1;18 % 10 = 8  本节点数= 8;

第二位9 + 9 + 上一节点进位数1 = 19, 19 / 10 = 1,进位数=1;19 % 10 = 9 本节点数= 9;

第三位9 + 0 + 上一节点进位数1 = 10,10 / 10 = 1,进位数=1;10 % 10  = 0 本节点数 = 0;

注意这个地方最后一个节点计算完毕,进位数>0 ,所以需要再追加一位。结果值应该是8->9->0->1。

步骤:

下面就可以解题了,先捋一下解题的步骤:

  1. 首先要定义两个链表,一个是链表的结果值,也就是头部信息。一个是在循环相加链表时动态赋值的链表。如果不把链表的头部信息单独存放一份,在循环赋值中就会丢失前面的头部信息,只会保存最后一个值。
  2. 循环遍历需要相加的两个链表,获取对应的值
  3. 计算出本节点的数,和进位数,给我们定义的链表赋值。这里要注意,第一次循环时,需要判断链表头部是否为空,如果为空的话,就给头部赋值。
  4. 循环结束后,判断进位数是否为1,是否需要尾部追加。

附上代码:

每一步都有注释解析。

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode curr = null;  //循环中动态赋值的链表
        ListNode head = null;  //头部信息,也可以理解为最终的结果值

        int s = 0; //初始的进位数

        //循环遍历两个链表
        while(l1 != null || l2 != null){
            
            //取值
            int num1 = l1 != null ? l1.val : 0;
            int num2 = l2 != null ? l2.val : 0;

            //两个值相加,加上上一次计算的进位数
            int sum = num1 + num2 + s;

            //本次计算的进位数
            s = sum / 10;
            //本次计算的个位数
            int result = sum % 10;

            //给链表赋值
            //判断链表头部是否为空
            if(curr == null){
                //先给curr链表赋值
                curr = new ListNode(result);
                //指定head = curr,后续curr指向next节点重置节点信息时,head不会被重置
                head = curr;
            }else{
                //头部不为空,指定next节点
                curr.next = new ListNode(result);
                //curr链表向后移,为了下一次循环继续给next节点赋值
                curr = curr.next;
            }

            //同理,l1和l2链表向后移,获取下一节点
            if(l1 != null){
                l1 = l1.next;
            }
            if(l2 != null){
                l2 = l2.next;
            }
        }
        //循环结束后判断进位数
        if(s > 0) curr.next = new ListNode(s);
        
        //这里需要注意,是返回head链表,而不是curr。
        //curr因为上面每次循环时,做了指针后移操作curr = curr.next;
        //如果返回curr,是最后一次循环的值,而不是全部链表。
        return head;
    }
}

结束。

<think>好的,用户问的是“listnode是什么意思”。首先,我需要确定用户的问题背景。ListNode通常出现在编程中,特别是数据结构相关的上下文里。可能用户正在学习数据结构,或者遇到了相关的代码问题。 接下来,我应该回忆ListNode的基本定义。ListNode链表中的一个节点,每个节点包含数据和指向下一个节点的指针。需要区分单链表、双向链表等不同类型,这可能影响ListNode结构。 然后,考虑用户可能的实际需求。用户可能想知道ListNode结构、用途,或者如何在代码中实现它。可能需要给出一个具体的例子,比如用类或结构体来定义ListNode,并说明各部分的作用。 还要注意用户的背景。如果用户是初学者,可能需要用更简单的语言解释,避免过多的技术术语。但问题本身属于编程领域,适当的术语是必要的,所以需要平衡。 另外,检查是否有常见的误区。比如,链表节点与数组的区别,或者内存管理方面的注意事项,比如指针的正确使用,避免内存泄漏。这些可能对用户有帮助,但需要根据问题范围决定是否包含。 然后,组织回答的结构。先定义ListNode,再说明其组成部分,接着举例说明,最后提到常见的链表类型和应用场景。这样结构清晰,逐步深入,帮助用户建立全面的理解。 验证信息的准确性。确保单链表节点有数据和next指针,双向链表有prev指针。代码示例部分要检查语法是否正确,比如在Java中使用类,在C++中使用结构体,Python中用类的属性。 可能还需要说明链表的特点,比如动态大小、插入删除的高效性,以及相比数组的优缺点。这些信息能帮助用户理解为什么使用ListNode链表结构。 最后,总结应用场景,如实现栈、队列、图等数据结构,让用户了解实际用途,增强学习的动力。</think>ListNode链表节点)是数据结构链表(Linked List)的基本组成单元。以下是逐步解释: 1. **基础定义** - 每个ListNode存储两部分内容: - **数据域**:存放当前节点的数据(如整数、字符串、对象等) - **指针域**:指向下一个节点(或同时指向前一个节点)的引用 2. **核心结构示例** - 单链表节点: ```java class ListNode { int val; // 数据域 ListNode next; // 指向下一个节点的指针 } ``` - 双向链表节点: ```python class DListNode: def __init__(self, val): self.val = val # 数据域 self.next = None # 后驱指针 self.prev = None # 前驱指针 ``` 3. **内存特性** - 非连续存储:节点通过指针逻辑连接,物理上可以分散在内存不同位置 - 动态扩展:不需要预先分配固定空间(与数组对比) 4. **关键操作复杂度** - 插入/删除:$O(1)$ (已知位置时) - 随机访问:$O(n)$ (需从头遍历) 5. **典型应用场景** - 实现高级数据结构:栈、队列、哈希桶 - 内存管理系统:内存池块管理 - 文件系统:文件块链式存储 - 图结构:邻接表表示法 6. **特殊变种** - 循环链表:尾节点指向头节点 - 带哨兵节点:简化边界处理 - 异或链表:使用异或运算压缩指针存储空间 注意:不同编程语言的具体实现可能有差异,但核心逻辑一致。链表操作需要特别注意指针的正确维护,避免出现断链或循环引用。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值