Leetcode 25
1 题目
1290. 二进制链表转整数
给你一个单链表的引用结点 head
。链表中每个结点的值不是 0 就是 1。已知此链表是一个整数数字的二进制表示形式。
请你返回该链表所表示数字的 十进制值 。
最高位 在链表的头部。
示例 1:
输入:head = [1,0,1] 输出:5 解释:二进制数 (101) 转化为十进制数 (5)
示例 2:
输入:head = [0] 输出:0
提示:
- 链表不为空。
- 链表的结点总数不超过
30
。 - 每个结点的值不是
0
就是1
。
2 我写了错误的代码
/*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/
int getDecimalValue(struct ListNode* head) {struct ListNode *cur = head;int result = 0 ;while (cur != NULL){int x = 0;result = result + cur->val * (2 ** (x));//本行开始报错x++;}return result;
}
你的思路方向是对的,但在 C 语言中不能直接使用2 **x
这种幂运算语法,而且变量x
的位置和递增方式也需要调整。下面是修改后的正确实现:
/*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/
int getDecimalValue(struct ListNode* head) {struct ListNode *cur = head;int length = 0;int result = 0;// 第一步:计算链表长度(确定最高位的幂)while (cur != NULL) {length++;cur = cur->next;}// 第二步:重新遍历链表计算十进制值cur = head;while (cur != NULL) {length--; // 从最高位开始递减// 使用pow函数需要包含math.h,这里用移位运算更高效result += cur->val * (1 << length);cur = cur->next;}return result;
}
代码说明:
- 首先需要遍历一次链表获取长度,这样才能知道最高位是 2 的多少次方
- C 语言中计算 2 的幂可以用移位运算
1 << n
(相当于 2ⁿ),比使用pow()
函数更高效 - 第二次遍历链表时,从最高位开始计算,每移动一个节点,幂次就减 1
- 累加每个节点值与对应权重的乘积,得到最终的十进制结果
例如对于链表1->0->1
:
- 第一次遍历得到长度为 3
- 第二次遍历:
- 第一个节点:1 × 2² = 4
- 第二个节点:0 × 2¹ = 0
- 第三个节点:1 × 2⁰ = 1
- 总和为 4+0+1=5
这种方法需要两次遍历链表,时间复杂度仍是 O (n),空间复杂度 O (1)。
【result += cur->val * (1 << length);详细解释】
这句话的作用是计算链表中当前节点值对应的十进制权重并累加到结果中,我们可以拆解来看:
-
cur->val
:当前链表节点的值(0 或 1,因为是二进制) -
1 << length
:这是 C 语言中计算 2 的幂的高效方式<<
是左移运算符,1 << n
表示将数字 1 的二进制向左移动 n 位- 等价于数学运算中的
2^length
(2 的 length 次方) - 例如:
1 << 3
等于 8(二进制 1000),即 2³=8
-
整体运算逻辑:
- 每个二进制位的十进制值 = 该位的值(0 或 1)× 2 的权重次方
- 权重从最高位开始计算,例如二进制
101
:- 第一位(最高位)1 的权重是 2²=4(
1 << 2
) - 第二位 0 的权重是 2¹=2(
1 << 1
) - 第三位 1 的权重是 2⁰=1(
1 << 0
)
- 第一位(最高位)1 的权重是 2²=4(
- 所以
result += cur->val * (1 << length)
就是把当前位的十进制值累加到结果中
举例说明,对于链表1->0->1
:
- 第一次循环(处理 1):
length=2
,计算1 * (1 << 2) = 4
,result=4 - 第二次循环(处理 0):
length=1
,计算0 * (1 << 1) = 0
,result=4+0=4 - 第三次循环(处理 1):
length=0
,计算1 * (1 << 0) = 1
,result=4+1=5
这样就得到了二进制101
对应的十进制值 5。
【>>有吗,反正<<就是表示幂次吧】
在 C 语言中,<<
(左移)和>>
(右移)是位运算符,它们的本质是对二进制位进行移动操作,而不是直接表示幂次,只是左移运算在特定情况下可以等价于计算 2 的幂。
具体说明:
-
左移运算符
<<
- 形式:
a << n
表示将a
的二进制位向左移动n
位,右边补 0 - 当
a=1
时,1 << n
恰好等于2ⁿ
(2 的 n 次方),这是因为:1 << 0 = 1(二进制1) → 2⁰ = 1 1 << 1 = 2(二进制10) → 2¹ = 2 1 << 2 = 4(二进制100) → 2² = 4 1 << 3 = 8(二进制1000) → 2³ = 8
- 但如果
a
不是 1,结果就不是单纯的幂次了,例如3 << 2 = 12
(二进制 11 左移 2 位变成 1100,即 12)
- 形式:
-
右移运算符
>>
- 形式:
a >> n
表示将a
的二进制位向右移动n
位 - 对于无符号数,左边补 0;对于有符号数,通常左边补符号位(正数补 0,负数补 1)
- 当
a
是正整数时,a >> n
相当于a ÷ 2ⁿ
(整除),例如:8 >> 1 = 4(8 ÷ 2¹ = 4) 8 >> 2 = 2(8 ÷ 2² = 2) 15 >> 2 = 3(15 ÷ 2² = 3.75,取整为3)
- 形式:
总结:
<<
和>>
是位移动运算符,不是专门的幂运算符号1 << n
等价于2ⁿ
(仅这一种特殊情况可以理解为幂次)a >> n
等价于a ÷ 2ⁿ
(正整数情况下,结果取整)
在之前的链表转整数代码中,用1 << length
来计算 2 的幂,只是利用了左移运算的这个特性,比调用pow()
函数更高效。
【我写的依旧错误的代码】
/*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/
int getDecimalValue(struct ListNode* head) {struct ListNode *cur = head;int result = 0 ;while (cur != NULL){int x = 0;result = result + cur->val * (1 << x);x++;cur = cur -> next ; }return result;
}
你的代码逻辑存在一个关键问题:变量x
在每次循环中都被重新初始化为 0,导致无法正确计算二进制位的权重。
具体来说,二进制链表的头部是最高位,而你的代码将每个节点都当作最低位(2⁰)来计算了。
例如对于链表1->0->1
:
- 你的代码计算为:1×2⁰ + 0×2⁰ + 1×2⁰ = 1+0+1=2(错误)
- 正确计算应该是:1×2² + 0×2¹ + 1×2⁰ = 4+0+1=5(正确)
修改后的代码如下:
/*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/
int getDecimalValue(struct ListNode* head) {struct ListNode *cur = head;int result = 0;int length = 0;// 先计算链表长度,确定最高位的权重while (cur != NULL) {length++;cur = cur->next;}// 重置指针到头部,准备计算结果cur = head;// 从最高位开始计算,权重是2^(length-1)int x = length - 1;while (cur != NULL) {result += cur->val * (1 << x); // 正确计算当前位的权重x--; // 每移动一个节点,权重降低一位cur = cur->next;}return result;
}
错误原因分析:
- 你的代码中
int x = 0;
放在了循环内部,导致每次循环x
都会被重置为 0 - 这使得每个节点的值都被乘以 2⁰(即 1),无论它在链表中的位置
- 二进制转换需要根据位置计算不同的权重(从高位到低位依次是 2ⁿ⁻¹, 2ⁿ⁻², ..., 2⁰)
如果你想使用更简洁的方式,也可以采用一次遍历的方法:
int getDecimalValue(struct ListNode* head) {struct ListNode *cur = head;int result = 0;while (cur != NULL) {result = result * 2 + cur->val; // 每次左移一位(乘以2)再加上当前值cur = cur->next;}return result;
}
这种方法更高效,通过逐步构建十进制数,避免了单独计算长度和权重的步骤。
原来就是一个数学问题呢.....
3 题解
如何遍历一个链表?代码框架如下:
// 遍历链表 head
while (head) { // 从链表头节点开始向后遍历,直到遇到空节点printf("%d\n", head->val); // 当前节点值head = head->next; // 准备遍历下一个节点
}
问题相当于给你一串 0 和 1,把它们拼成一个二进制数。
int getDecimalValue(struct ListNode* head) {int ans = 0;while (head) {ans = ans * 2 + head->val;head = head->next;}return ans;
}
复杂度分析
时间复杂度:O(n),其中 n 是链表的长度。
空间复杂度:O(1)。