关于回文算法的时间复杂度和空间复杂度优化

65 阅读5分钟

大家好,我是 V 哥,今天分享回文算法,搞点算法的东东,回文算法是一类用于判断一个字符串是否为回文的算法。回文是一种特殊的字符串,它正着读和倒着读都一样,例如 "racecar"、"level" 或 "madam" 等。以下是回文算法的一些特点和目的:

特点:

对称性:回文的核心特点是具有对称性,字符串的前半部分和后半部分(倒序)是完全相同的。

字符比较:算法通常会比较字符串中相对应位置的字符,例如第一个字符和最后一个字符,第二个字符和倒数第二个字符,以此类推。

以下是几种检查一个字符串是否为回文的方法:

方法一:使用双指针法

def is_palindrome(s):
    left = 0
    right = len(s) - 1
    while left < right:
        if s[left]!= s[right]:
            return False
        left += 1
        right -= 1
    return True

代码解释

  • 首先,我们定义了一个名为 is_palindrome 的函数,它接受一个字符串 s 作为输入。

  • 我们初始化两个指针,left 指向字符串的起始位置(索引为 0),right 指向字符串的末尾位置(索引为 len(s) - 1)。

  • 然后使用一个 while 循环,只要 left 小于 right,就比较 s[left]s[right] 的字符。

  • 如果它们不相等,说明该字符串不是回文,返回 False

  • 否则,将 left 加 1,right 减 1,继续比较下一对字符,直到 left 大于或等于 right,说明字符串是回文,返回 True

  • 时间复杂度:在双指针法中,我们最多需要遍历字符串的一半元素,因为 leftright 指针从字符串的两端向中间移动。每次比较操作的时间复杂度是 O(1)O(1),总的比较次数最多为 n/2n/2,所以时间复杂度是 O(n)O(n),其中 nn 是字符串的长度。

  • 空间复杂度:只使用了两个额外的指针变量 leftright,空间复杂度为 O(1)O(1),因为空间使用不随输入字符串的长度而增加。

方法二:使用切片反转字符串并比较

def is_palindrome(s):
    reversed_s = s[::-1]
    if s == reversed_s:
        return True
    else:
        return False

代码解释

  • 这里定义了 is_palindrome 函数,接受一个字符串 s

  • 我们使用切片 s[::-1] 来反转字符串,将结果存储在 reversed_s 中。

  • 然后将原始字符串 s 和反转后的字符串 reversed_s 进行比较。

  • 如果它们相等,说明该字符串是回文,返回 True;否则,返回 False

  • 时间复杂度:切片操作 s[::-1] 会遍历整个字符串,时间复杂度是 O(n)O(n),其中 nn 是字符串的长度。字符串比较操作的时间复杂度也是 O(n)O(n)。所以总的时间复杂度是 O(n)O(n)

  • 空间复杂度:切片操作会创建一个新的字符串 reversed_s,其长度等于原字符串的长度,因此空间复杂度是 O(n)O(n)

方法三:使用递归

def is_palindrome(s):
    if len(s) <= 1:
        return True
    if s[0] == s[-1]:
        return is_palindrome(s[1:-1])
    else:
        return False

代码解释

  • is_palindrome 函数接受一个字符串 s

  • 如果字符串的长度小于或等于 1,它是回文,返回 True

  • 如果字符串的第一个字符和最后一个字符相等,我们递归调用 is_palindrome 函数,传入去掉第一个和最后一个字符后的子串 s[1:-1]

  • 如果第一个字符和最后一个字符不相等,该字符串不是回文,返回 False

  • 时间复杂度:在最坏的情况下,需要对字符串中的每个字符进行比较,递归调用的深度最大为 n/2n/2,每次比较操作的时间复杂度是 O(1)O(1),所以总的时间复杂度是 O(n)O(n)

  • 空间复杂度:递归调用会将函数的上下文信息存储在栈上,递归调用的最大深度为 n/2n/2,因此空间复杂度是 O(n)O(n)

你可以使用以下方式调用这些函数:

print(is_palindrome("racecar"))
print(is_palindrome("hello"))

这将分别输出 TrueFalse

回文算法时间复杂度和空间复杂度优化

双指针法(已最优)

def is_palindrome(s):
    left = 0
    right = len(s) - 1
    while left < right:
        if s[left]!= s[right]:
            return False
        left += 1
        right -= 1
    return True
  • 时间复杂度O(n)O(n),因为在最坏的情况下,我们需要比较字符串的一半元素,每次比较操作的时间复杂度是 O(1)O(1),无法进一步优化,因为至少需要检查每个元素一次来确定是否为回文。
  • 空间复杂度O(1)O(1),只使用了两个指针,没有额外的存储开销,已经是最优的,因为没有使用额外的数据结构,只是使用了几个固定大小的变量。

切片反转并比较

def is_palindrome(s):
    reversed_s = s[::-1]
    if s == reversed_s:
        return True
    else:
        return False
  • 时间复杂度O(n)O(n),切片操作和比较操作都需要遍历整个字符串,难以进一步优化。
  • 空间复杂度O(n)O(n),切片操作会创建一个新的字符串,空间开销较大。可以考虑使用双指针法来避免创建新的字符串,从而优化空间复杂度。

递归法

def is_palindrome(s):
    if len(s) <= 1:
        return True
    if s[0] == s[-1]:
        return is_palindrome(s[1:-1])
    else:
        return False
  • 时间复杂度O(n)O(n),因为递归的深度最多为 n/2n/2,每次递归操作的时间复杂度是 O(1)O(1),难以进一步优化,因为需要检查字符串中的元素。
  • 空间复杂度O(n)O(n),由于递归调用需要存储函数的上下文信息在栈上,空间复杂度较高。可以将其转换为迭代形式(如双指针法)来降低空间复杂度。

最后

总而言之言而总之,对于回文检查算法,双指针法在时间复杂度为 O(n)O(n) 的同时,具有最优的 O(1)O(1) 空间复杂度,很难再对其进行优化。切片反转并比较以及递归法在时间复杂度上已经达到 O(n)O(n),但空间复杂度可以通过使用双指针法来优化,避免使用额外的空间存储反转后的字符串或存储递归调用的上下文信息。关注威哥爱编程,全栈之路就你行。