2024/12/11 【字符串】LeetCode344.反转字符串 【√】解法众多:双指针,栈,range,reversed,切片,列表推导,reverse,交换函数实现的两种方法

344. 反转字符串 - 力扣(LeetCode)

代码随想录

解法1:双指针

因为while每次循环需要进行条件判断,而range函数不需要,直接生成数字,因此时间复杂度更低。推荐使用range
class Solution:
    def reverseString(self, s: List[str]) -> None:
        """
        Do not return anything, modify s in-place instead.
        """
        n = len(s)
        i, j = 0, n-1
        while i < j:
            #1.常见的交换数值
            # temp = s[i]
            # s[i] = s[j]
            # s[j] = temp
            #2.通过位运算的异或运算实现交换两个数的操作
            # s[i] = chr(ord(s[i]) ^ ord(s[j]))
            # s[j] = chr(ord(s[i]) ^ ord(s[j]))
            # s[i] = chr(ord(s[i]) ^ ord(s[j]))
            #3.
            s[i], s[j] = s[j], s[i]
            i += 1
            j -= 1

1.C++中库函数swap()(交换函数)在Python中的对应

在 Python 中,C++ 的 swap() 函数的功能(交换两个变量的值)可以直接通过 Python 的多重赋值实现,而不需要一个显式的库函数。

原理

Python 中的 a, b = b, a 是一个简洁的语法糖,实际上等价于:

  1. 创建一个元组 (b, a),然后将其解包赋值给 ab
  2. 这一过程不会引入额外的临时变量。

这比 C++ 的 swap() 函数更直观且更简洁。


如果需要类似 swap() 的函数

虽然 Python 本身没有内置的 swap() 函数,但我们可以自己定义一个:

def swap(a, b):
    return b, a

# 使用 swap 函数
x = 5
y = 10
x, y = swap(x, y)

print(x)  # 输出:10
print(y)  # 输出:5

这种方式和多重赋值本质上是一样的,只是通过函数形式进行了封装。


与 C++ 的 swap() 对比

特性C++ 的 swap()Python 的多重赋值
语法 std::swap(a, b); a, b = b, a
是否需要库支持 需要(<utility> 不需要,原生支持
操作对象 基本类型或对象 任意可赋值的对象(变量、列表元素等)
是否高效 高效(通常由编译器优化为原地交换) 高效(无需额外内存分配)

 

2.swap函数实现的两种方式

swap可以有两种实现。

一种就是常见的交换数值:如1

一种就是通过位运算:如2

位运算是一种直接对二进制位进行操作的运算,通常用于底层编程、硬件控制和性能优化等场景。

位运算操作的对象是整数类型的数据(比如 int),这些数据在计算机中以二进制形式存储。

位运算的主要特点是高效,因为它直接在二进制层面进行计算,通常比其他运算(如加减乘除)快。

按位异或(^)(常见位运算操作之一,还包括按位与(&),按位或(|),按位取反(~),左移(<<),右移(>>))

  • 功能:两个数的每个位都进行逻辑异或操作(相同为 0,不同为 1)。
  • 作用:可以用来翻转特定的二进制位。
  • 示例
    5 ^ 3  // 0101 ^ 0011 = 0110
    结果:6

位运算的应用场景

  1. 快速计算

    • 通过移位实现乘法和除法(如 x << 1 等价于 x * 2)。
  2. 标志位操作

    • 用于设置、清除、切换特定位。例如,用按位与清除某个位,用按位或设置某个位。
  3. 判断奇偶性

    • n & 1 == 0 表示偶数,n & 1 == 1 表示奇数。😍
    • 与操作AND)是一种基本的二进制位运算,在计算机科学中非常常用。它用于比较两个二进制数的每个位,只在两个位同时为1时结果为1,否则为0。
  4. 位掩码

    • 通过掩码操作(与运算)保留特定的二进制位。
  5. 交换两个数

    • 通过异或运算实现:
    • a = a ^ b
      b = a ^ b
      a = a ^ b

3. 怎么理解通过异或运算实现交换两个数?

通过异或运算实现交换两个数的原理,利用了异或运算的性质

  • a⊕a=0 (一个数异或自身等于 0)
  • a⊕0=a(一个数异或 0 等于自身)
  • a⊕b⊕b=a(因为 b⊕b=0,所以 a⊕b⊕b=a

实现步骤

设两个数分别为 ab,通过以下三步实现交换:

    1. 第一步
      a=a⊕b
      此时,保存的是 a 和 b 的异或值。

    2. 第二步
      b=a⊕b
      因为 a 现在是 a⊕b,所以 b 变成了:
      b=(a⊕b)⊕b=a⊕(b⊕b)=a⊕0=a
      此时 b 保存了原来的 a 值

    3. 第三步
      a=a⊕b
      因为 b 现在是原来的 a,所以 变成了:
      a=(a⊕b)⊕b=a⊕(b⊕b)=a⊕0=b
      此时 a 保存了原来的 b 值

代码示例

以下是用 Python 实现的代码:

# 初始值
a = 5  # 二进制:0101
b = 3  # 二进制:0011

# 交换过程
a = a ^ b  # a = 0101 ^ 0011 = 0110 (a 变为 6)
b = a ^ b  # b = 0110 ^ 0011 = 0101 (b 变为 5)
a = a ^ b  # a = 0110 ^ 0101 = 0011 (a 变为 3)

print("a =", a)  # 输出:a = 3
print("b =", b)  # 输出:b = 5

用异或实现交互两个数的操作为什么可以不用临时变量?

普通交换两个数的代码需要一个临时变量,通过异或实现交换的方式,不需要额外的变量,因为所有的信息都保存在 b 的二进制中。它通过数学逻辑直接完成交换。

注意事项

    1. 如果 a 和 b 指向同一个内存位置(如 a 和 b 是同一个变量),则这种方法不适用。因为会导致数据被清零:
      a=a⊕a=0

    2. 在实际开发中,虽然异或交换节省了一个临时变量,但现代编译器优化已经非常强大,普通交换(用临时变量)也能生成高效的代码。因此异或交换主要用作教学示例或在极端优化场景下使用。

4. 如果是两个字符进行交换可以用这个方法吗?

异或运算可以用于交换两个字符,因为字符在底层是以整数形式(ASCII 或 Unicode 值)存储的,而异或操作本质上是对整数进行操作。只要将字符视为整数,就可以使用同样的方法实现交换。

Python 示例

以下是交换两个字符的代码:

# 初始值
a = 'A'  # 字符 'A' 的 ASCII 值是 65
b = 'B'  # 字符 'B' 的 ASCII 值是 66

# 交换过程
a = chr(ord(a) ^ ord(b))  # 将字符转换为整数进行异或,再转回字符
b = chr(ord(a) ^ ord(b))
a = chr(ord(a) ^ ord(b))

# 输出结果
print("a =", a)  # 输出:a = B
print("b =", b)  # 输出:b = A

解释

  1. ord():将字符转换为其对应的 ASCII 或 Unicode 值。例如,ord('A') 返回 65
  2. chr():将整数值转换回字符。例如,chr(65) 返回 'A'

通过 ord()chr(),可以在字符和整数之间自由转换,从而使用异或交换字符。

注意事项

  1. 适用范围:字符的交换适用于所有单个字符(如 'a''A''你'),因为它们都可以被映射为整数。
  2. 不要用于字符串:如果需要交换的是多字符字符串,这种方法就不适用了,因为字符串不直接对应单个整数。

5.C++有^=这个符号,Python里面有^=这个符号吗?

是的,Python 中有 ^= 运算符,它是一种按位异或赋值运算符

注意事项

  1. ^= 的作用范围:只适用于整数和可以转换为整数的数据(如字符)。
  2. 与其他运算符类似^= 的用法与 +=-=*= 等运算符一致,只是它对应的是异或操作。

解法2:使用栈

class Solution:
    def reverseString(self, s: List[str]) -> None:
        """
        Do not return anything, modify s in-place instead.
        """
        stack = []
        for char in s:
            stack.append(char)
        for i in range(len(s)):
            s[i] = stack.pop()

1. Python中栈的表达:

stack = []

2. Python中加入元素到栈,和从栈中弹出元素的方法:

#加入栈
stack.append(char)
#弹出栈
s[i] = stack.pop()

解法3:使用range

class Solution:
    def reverseString(self, s: List[str]) -> None:
        n = len(s)
        for i in range(n//2):
            s[i], s[n-i-1] = s[n-i-1], s[i]
            i += 1

1. python中“//”和“/”的区别是什么?

在 Python 中,/// 都是用于除法运算的运算符,但它们的行为有所不同:


1. / 运算符

  • 功能:执行浮点除法(返回浮点数)。
  • 结果:即使两个操作数是整数,结果也是一个浮点数

示例:

print(5 / 2)  # 输出:2.5
print(4 / 2)  # 输出:2.0
print(7 / 3)  # 输出:2.3333333333333335
  • 适用场景:当需要精确的除法结果(包括小数部分)时使用 /

2. // 运算符

  • 功能:执行整数除法(也称为地板除,floor division)。
  • 结果:返回商的整数部分,向下取整(舍去小数部分)。
    • 如果操作数中有浮点数,结果仍为浮点数,但小数部分为 .0
    • 注意:// 的向下取整会处理负数,例如 −3.5-3.5 向下取整为 −4-4。

示例:

print(5 // 2)  # 输出:2
print(4 // 2)  # 输出:2
print(7 // 3)  # 输出:2
print(-7 // 3) # 输出:-3
print(7.0 // 3) # 输出:2.0
  • 适用场景:当只需要商的整数部分时使用 //,通常用于循环计算或整数分组场景。

扩展:和 % 的关系

  • %:取模运算符,返回除法的余数。
  • //% 的关系:可以表示为: a=(a//b)×b+(a%b)

例如:

a = 7
b = 3
print(a // b)  # 输出:2
print(a % b)   # 输出:1
# 验证关系
print((a // b) * b + (a % b))  # 输出:7

解法4:使用reversed

class Solution:
    def reverseString(self, s: List[str]) -> None:
        s[:] = reversed(s)

1. 介绍reversed函数和reverse函数

在 Python 中,reversed().reverse() 是两种不同的功能,用于反转序列的顺序。它们的用法和作用范围有所不同。


1. reversed() 函数

  • 类型:内置函数。
  • 作用:返回一个反转后的迭代器,不改变原序列。
  • 适用对象:任何可迭代对象(如字符串、列表、元组、range 等)

特点:

  1. 返回值:返回一个迭代器对象。
  2. 不会修改原序列,而是提供一个新的迭代器。
  3. 可以通过 list()tuple()str().join() 将结果转为具体的数据结构。

示例:

# 示例 1:反转列表
lst = [1, 2, 3, 4]
rev_lst = reversed(lst)
print(list(rev_lst))  # 输出:[4, 3, 2, 1]
print(lst)            # 原列表未改变:[1, 2, 3, 4]

# 示例 2:反转字符串
s = "hello"
rev_s = reversed(s)
print("".join(rev_s))  # 输出:'olleh'

# 示例 3:反转元组
tup = (10, 20, 30)
print(tuple(reversed(tup)))  # 输出:(30, 20, 10)

2. .reverse() 方法

  • 类型:列表的方法。
  • 作用原地反转列表的顺序。
  • 适用对象仅适用于列表,不能用于其他序列(如字符串、元组)。

用法:

list.reverse()

特点:

  1. 返回值:无返回值(None),直接修改原列表。
  2. 只能用于列表,不能用于字符串或元组等不可变序列。

总结与选择

  • reversed():更通用,适用于需要临时反转任意可迭代对象,并且不想修改原对象的场景。
  • .reverse():更高效,适用于需要直接修改列表顺序的场景。

2.为什么表达成

s[:] = reversed(s)可以正确运行,但是
s = reversed(s)不行,以及
s[:]是什么意思?

在 Python 中,s[:] = reversed(s)s = reversed(s) 的行为不同,是因为它们涉及到切片赋值变量重新绑定的区别。让我们逐一分析。


1. s[:] = reversed(s) 为什么可以运行?

这里的 s[:] 是对列表的切片操作,表示“对列表 s 的所有元素进行赋值”。
这实际上是修改列表的内容而不是重新绑定变量 s,因此它可以正确运行。

原理:

  • s[:] 是一个切片表达式,指代列表 s 的全部内容。
  • reversed(s) 返回一个迭代器,表示反转后的元素顺序。
  • s[:] = reversed(s) 的意思是“将 s 的所有元素替换为 reversed(s) 中的值”。

为什么适用?

  • 切片赋值允许你直接修改列表内容,而不会更改原列表的引用(地址)。
  • 列表的内容被反转后的值替换,而原变量 s 仍然指向同一个列表对象。

2. s = reversed(s) 为什么不行?

s = reversed(s) 中:

  • s 被重新绑定到 reversed(s) 返回的迭代器对象
  • 原列表 s 不再存在,而是被变量 s 覆盖。

结果:

  • 变量 s 不再是一个列表,而是一个迭代器。
  • 如果你试图对迭代器进行像列表一样的操作(如索引、切片等),就会报错。

例如:

s = [1, 2, 3, 4]
s = reversed(s)  # 现在 s 是一个迭代器
print(list(s))  # 输出:[4, 3, 2, 1](可以通过 list() 再转回来)
# print(s[0])  # 报错:'reversed' object is not subscriptable

总结:

  • s[:] = reversed(s) 修改了列表的内容。
  • s = reversed(s) 将变量 s 绑定为一个新的迭代器,丢失了列表的性质。

3. s[:] 的含义

[:] 是一个切片表达式,它有以下特点:

  • 指代整个序列的所有元素。
  • 对于列表来说,s[:] 返回一个与 s 内容相同的新列表(浅拷贝)。
  • 当用于赋值时(如 s[:] = ...),它不会创建新的列表对象,而是直接修改 s 的内容。

示例:

# 浅拷贝
s = [1, 2, 3]
copy_s = s[:]  # 创建了一个与 s 内容相同的新列表
print(copy_s)  # 输出:[1, 2, 3]
print(copy_s is s)  # 输出:False(不是同一个对象)

# 切片赋值
s = [1, 2, 3]
s[:] = [4, 5, 6]  # 修改 s 的所有元素
print(s)  # 输出:[4, 5, 6]

总结

  • s[:] = reversed(s) 修改了列表 s 的内容,保持了变量 s 的列表类型。
  • s = reversed(s) 将变量 s 绑定到一个新的迭代器,丢失了原列表。
  • s[:] 的作用
    • 不创建新对象,仅修改现有列表的内容。
    • 用于切片赋值或创建浅拷贝。

解法5:使用切片

class Solution:
    def reverseString(self, s: List[str]) -> None:
        s[:] = s[::-1]

看不懂这个表达

s[:] = s[::-1]是一种常用的 Python 表达方式,用于原地反转列表。要理解它的行为,需要逐步拆解:


1. s[::-1] 的含义

  • s[::-1] 是一个切片操作,表示对序列 s 按步长 -1 进行切片。
  • 步长为 -1 的意思是:从序列的末尾向前提取元素,从而创建一个新的反转后的序列。

示例:

s = [1, 2, 3, 4]
print(s[::-1])  # 输出:[4, 3, 2, 1]

说明:

  • s[::-1] 创建了一个反转后的新列表,不会修改原列表 s
  • 这个新列表与原列表内容相同,但顺序相反。

2. s[:] 的含义

  • s[:] 是一个切片赋值操作,用于修改列表的所有元素,而不改变列表对象本身。

示例:

s = [1, 2, 3]
s[:] = [4, 5, 6]  # 修改 s 的内容
print(s)  # 输出:[4, 5, 6]

3. s[:] = s[::-1] 的整体含义

  1. s[::-1]:创建了一个新的反转列表,但没有改变原列表 s
  2. s[:]:表示“修改原列表的内容,而不改变其对象引用”。
  3. 赋值操作:将 s[::-1] 中反转后的内容,逐一赋值给原列表 s

运行过程:

  • 原列表 s 的元素被替换为 s[::-1] 中的内容。
  • 原列表 s 依然指向相同的内存地址,但其内容已经被反转。

示例:

s = [1, 2, 3, 4]
s[:] = s[::-1]
print(s)  # 输出:[4, 3, 2, 1]

4. 为什么不用 s = s[::-1]

  • 如果使用 s = s[::-1]s 将被重新绑定到一个新的反转列表。
  • 原来的列表 s 不会被修改,而是丢失了引用。

示例:

s = [1, 2, 3, 4]
s = s[::-1]  # s 现在指向新列表
print(s)  # 输出:[4, 3, 2, 1]

5. 总结

表达式含义是否修改原列表
s[::-1] 创建一个新的反转列表,原列表不变
s[:] = s[::-1] 原地反转:修改原列表内容,保持同一对象
s = s[::-1] 将变量 s 绑定到一个新的反转列表,原列表被覆盖

如果需要在不改变列表引用的情况下反转列表,推荐使用 s[:] = s[::-1].reverse() 方法:

s = [1, 2, 3, 4]
s.reverse()  # 原地反转,等价于 s[:] = s[::-1]
print(s)  # 输出:[4, 3, 2, 1]

解法6:使用列表推导

class Solution:
    def reverseString(self, s: List[str]) -> None:
        s[:] = [s[i] for i in range(len(s)-1, -1, -1)]

看不懂这个表达

表达式 s[:] = [s[i] for i in range(len(s)-1, -1, -1)] 的作用是原地反转列表 s 的内容。让我们逐步解析其含义。


1. 外层结构:s[:] = ...

  • s[:] 是切片赋值,表示修改列表 s 的内容,而不是创建新的列表
  • 任何赋值操作(例如 s[:] = ...)都会替换列表的所有元素,而不会改变列表对象本身的引用。

2. 内层列表生成式:[s[i] for i in range(len(s)-1, -1, -1)]

这是一个列表推导式,用于生成反转后的列表。以下是它的构成部分:

详细解析:

  1. range(len(s)-1, -1, -1)

    • range 函数生成从 len(s)-10(包括 0)的递减索引。
    • 对于一个长度为 n 的列表,索引从 n-1(最后一个元素)开始,到 0(第一个元素)结束,步长为 -1
  2. s[i]

    • 遍历上述索引范围,每次取出对应索引位置的元素。
  3. 完整列表推导式

    • 将上述步骤结合起来,生成一个反转的列表。

3. 完整过程:s[:] = [s[i] for i in range(len(s)-1, -1, -1)]

  • 右侧:通过列表推导式生成一个反转后的新列表。
  • 左侧s[:] 将反转后的列表内容赋值到原列表 s 的每个位置,原列表被修改,但对象引用不变

特点:

  • s[:] = s[::-1] 效果相同。
  • 保持了 s 的列表对象不变,只是更新了内容。

5. 对比其他方法

方法描述是否修改原列表是否创建新列表
s[:] = s[::-1] 使用切片反转,原地修改
s = s[::-1] 使用切片反转,创建新列表赋值给 s
.reverse() 原地反转,内置方法
[s[i] for i in range(len(s)-1, -1, -1)] 手动实现反转(新列表)
s[:] = [s[i] for i in range(len(s)-1, -1, -1)] 手动实现原地反转

6. 总结

表达式 s[:] = [s[i] for i in range(len(s)-1, -1, -1)] 是手动实现列表反转的一种方式,和 s[:] = s[::-1] 效果相同,但更加显式,便于理解反转逻辑的实现。如果需要更简洁的代码,直接使用切片或内置方法更推荐。

解法7:使用reverse()

class Solution:
    def reverseString(self, s: List[str]) -> None:
        """
        Do not return anything, modify s in-place instead.
        """
        # 原地反转,无返回值
        s.reverse()

 

 




posted on 2024-12-16 07:00  axuu  阅读(63)  评论(0)    收藏  举报