一个认为一切根源都是“自己不够强”的INTJ
个人主页:用哲学编程-CSDN博客
专栏:每日一题——举一反三
Python编程学习
Python内置函数
Python-3.12.0文档解读
目录
我的写法
代码逻辑:
时间复杂度:
空间复杂度:
代码优化:
我要更强
代码解释:
时间复杂度分析:
空间复杂度分析:
哲学和编程思想
1. 简洁性和直接性(Simplicity and Directness)
2. 空间利用效率(Space Efficiency)
3. 时间效率(Time Efficiency)
4. 算法的高效性(Algorithmic Efficiency)
5. Robustness
6. 减少外部依赖(Reduced Dependency on External Functions)
具体说明优化方法的哲学和编程思想:
总结
举一反三
1. 简洁性和直接性
2. 空间利用效率
3. 时间效率
4. 算法的高效性
5. 代码的鲁棒性
6. 减少外部依赖
题目链接
我的写法
class Solution:def lengthOfLastWord(self, s: str) -> int:s=s.split()return len(s[-1])
这段代码定义了一个名为 Solution 的类,其中包含一个方法 lengthOfLastWord,该方法接收一个字符串 s 作为输入,并返回该字符串中最后一个单词的长度。下面是对这段代码的专业点评:
-
代码逻辑:
- 首先,使用 split() 方法将输入的字符串 s 分割成单词列表。split() 方法默认以空格作为分隔符,将字符串分割成多个子字符串,并返回这些子字符串组成的列表。
- 然后,通过索引 [-1] 获取列表中的最后一个元素,即最后一个单词。
- 最后,使用 len() 函数计算并返回这个单词的长度。
-
时间复杂度:
- split() 方法的时间复杂度为 O(n),其中 n 是字符串 s 的长度。这是因为 split() 需要遍历整个字符串来分割单词。
- len() 函数的时间复杂度为 O(1),因为它只需要返回列表中元素的数量。
- 因此,整体的时间复杂度主要由 split() 决定,为 O(n)。
-
空间复杂度:
- split() 方法会创建一个新的列表,其大小取决于字符串中单词的数量。在最坏的情况下,如果字符串中的每个字符都是单词的一部分(即没有空格),则列表的大小为 n。
- 因此,空间复杂度为 O(n),其中 n 是字符串 s 的长度。
-
代码优化:
- 这段代码已经相当简洁和高效,但如果要进一步优化,可以考虑直接从字符串的末尾开始遍历,找到最后一个单词的起始位置,然后计算其长度,这样可以避免创建额外的列表,从而减少空间复杂度。
总的来说,这段代码是一个简洁有效的解决方案,适用于大多数情况。如果输入的字符串非常大,可能需要考虑空间效率的优化。
我要更强
当然,我们可以通过直接从字符串末尾开始遍历来优化时间和空间复杂度。这种方法避免了使用 split() 方法创建额外的列表,从而减少了空间复杂度。下面是优化后的代码:
class Solution:def lengthOfLastWord(self, s: str) -> int:# 从字符串末尾开始向前遍历index = len(s) - 1# 跳过末尾的空格while index >= 0 and s[index] == ' ':index -= 1# 计算最后一个单词的长度length = 0while index >= 0 and s[index] != ' ':length += 1index -= 1return length
代码解释:
- 首先,我们初始化一个变量 index 为字符串 s 的最后一个字符的索引。
- 然后,我们使用一个循环从末尾向前移动 index,直到找到第一个非空格字符。这跳过了字符串末尾可能存在的空格。
- 接着,我们使用另一个循环继续向前移动 index,同时计算遇到的非空格字符的数量,即最后一个单词的长度。
- 最后,返回计算得到的长度。
时间复杂度分析:
- 在最坏的情况下,我们需要遍历整个字符串,因此时间复杂度为 O(n),其中 n 是字符串的长度。
空间复杂度分析:
- 我们没有使用任何额外的数据结构(如列表),只使用了几个变量来存储索引和长度,因此空间复杂度为 O(1)。
这种优化方法在处理大型字符串时尤其有效,因为它避免了创建可能非常大的中间列表。
哲学和编程思想
这些方法背后的哲学和编程思想主要包括以下几个方面:
1. 简洁性和直接性(Simplicity and Directness)
优化后的代码直接从字符串末尾开始遍历,避免了不必要的中间步骤。这种方法体现了“简洁即美”的编程哲学。过于复杂的解决方案可能会增加出错的概率和维护成本,而简单直接的方法则更易于理解和维护。
2. 空间利用效率(Space Efficiency)
通过避免创建额外的列表,优化后的代码极大地减少了空间占用,体现了“尽量减少资源使用”的编程思想。这种思维方式在处理大数据时尤为重要,因为内存资源是有限的。
3. 时间效率(Time Efficiency)
优化后的代码通过单次遍历字符串来计算最后一个单词的长度,而不是进行多次遍历或创建中间数据结构,从而提高了时间效率。这符合“尽量减少计算复杂度”的编程哲学。
4. 算法的高效性(Algorithmic Efficiency)
直接从字符串末尾开始遍历的算法是一种“贪心算法”(Greedy Algorithm)的应用,它在每一步选择当前最优解。这种方法保证了在最短的时间内找到最后一个单词的长度。
5. Robustness
无论字符串的情况如何(是否包含多个空格,是否为空字符串等),优化后的方法都能正确处理。这体现了“鲁棒性”的编程思想,即代码能够在各种边界情况下正常运行。
6. 减少外部依赖(Reduced Dependency on External Functions)
优化后的方法减少了对内置函数(如 split())的依赖,使得代码更加自包含和独立。这有助于提高代码的可移植性和灵活性,符合“减少依赖,增加自包含性”的编程原则。
具体说明优化方法的哲学和编程思想:
- 简洁性和直接性:通过直接从字符串末尾开始遍历,避免了 split() 方法的使用,简化了代码逻辑。
- 空间利用效率:通过在原字符串上进行操作,而不是创建中间列表,减少了不必要的空间占用。
- 时间效率:通过单次遍历字符串,减少了不必要的重复遍历,优化了时间复杂度。
- 算法的高效性:遍历字符串并计算最后一个单词的长度,这种贪心算法在步骤选择上每次都选择最优解。
- 减少外部依赖:减少对内置函数的依赖,使代码更加独立和灵活。
总结
通过直接处理字符串而不是依赖于 split() 等内置函数,优化后的代码不仅简化了逻辑,提高了时间和空间效率,还遵循了许多重要的编程哲学和思想,如简洁性、效率和减少依赖。这些思想在日常编程中都是非常重要的,可以帮助开发者编写出更高效、可维护性更强的代码。
举一反三
理解和应用这些哲学和编程思想,可以在编程过程中提高代码的质量和效率。以下是一些具体的技巧和建议,帮助你在编写代码时能够举一反三:
1. 简洁性和直接性
技巧:
- 减少中间步骤:在解决问题时,思考是否可以直接从输入到输出,不经过多余的中间步骤。
- 避免冗余代码:删除不必要的变量和重复的逻辑,保持代码简洁。
- 使用简洁的语法:利用Python的简洁语法特性,如列表推导式、生成器等,来减少代码的长度和复杂度。
示例:
# 繁琐的写法
def square_numbers(nums):result = []for num in nums:result.append(num * num)return result# 简洁的写法
def square_numbers(nums):return [num * num for num in nums]
2. 空间利用效率
技巧:
- 原地操作:如果可以,尽量在原数据结构上进行操作,避免创建新的数据结构。
- 使用生成器:对于需要惰性求值的情况,使用生成器表达式代替列表推导式,以减少内存占用。
- 释放不再使用的资源:及时删除或释放不再需要的对象,尤其是在处理大数据时。
示例:
# 使用生成器表达式而不是列表推导式
def large_range():for i in range(10**6):yield i * i# 使用生成器表达式
gen = (i * i for i in range(10**6))
3. 时间效率
技巧:
- 减少重复计算:避免在循环或递归中进行重复计算,使用缓存或记忆化技术。
- 优化算法:使用更高效的算法和数据结构,以减少时间复杂度。例如,使用哈希表而不是列表查找。
- 提前终止条件:在循环或递归中添加提前终止条件,以减少不必要的计算。
示例:
# 使用哈希表进行查找
def find_duplicates(nums):seen = set()for num in nums:if num in seen:return Trueseen.add(num)return False
4. 算法的高效性
技巧:
- 选择合适的算法:根据具体问题选择合适的算法,如贪心算法、动态规划、二分查找等。
- 分治思想:将问题分解成更小的子问题,递归求解,然后合并结果。
- 尽量减少复杂度:在设计算法时,尽量减少时间和空间复杂度。
示例:
# 使用分治思想的归并排序
def merge_sort(arr):if len(arr) <= 1:return arrmid = len(arr) // 2left = merge_sort(arr[:mid])right = merge_sort(arr[mid:])return merge(left, right)def merge(left, right):result = []i = j = 0while i < len(left) and j < len(right):if left[i] < right[j]:result.append(left[i])i += 1else:result.append(right[j])j += 1result.extend(left[i:])result.extend(right[j:])return result
5. 代码的鲁棒性
技巧:
- 边界条件处理:在编写代码时,考虑所有可能的边界情况,如空输入、极大值和极小值等。
- 异常处理:使用适当的异常处理机制,确保代码在异常情况下能够优雅地失败。
- 单元测试:编写单元测试来验证代码的正确性,特别是针对边界条件和异常情况。
示例:
def safe_divide(a, b):try:return a / bexcept ZeroDivisionError:return float('inf')# 单元测试
assert safe_divide(10, 2) == 5
assert safe_divide(10, 0) == float('inf')
6. 减少外部依赖
技巧:
- 减少对库的依赖:在可能的情况下,使用标准库或自己实现简单功能,避免引入不必要的第三方库。
- 模块化设计:将代码划分为独立的模块,使每个模块尽量少依赖其他模块。
- 自包含性:在编写模块时,确保它们尽量自包含,减少对外部资源的依赖。
# 自己实现简单的功能而不是依赖第三方库
def factorial(n):if n == 0:return 1return n * factorial(n - 1)
通过这些技巧,可以更好地应用简洁性、空间利用效率、时间效率、算法高效性、代码鲁棒性和减少外部依赖等编程哲学和思想,编写出更优雅、高效和可维护的代码。