前言
作为一名测试工程师,想要提高自己的编程能力,不像开发一样具备先天条件,他们可以通过写业务代码来逐渐提升,而一个小团队的测试工程师更多的工作还是在功能测试,没有那么多的机会去写代码,此时我们想要提高编程能力,就可以通过刷题来逐渐提升。刷算法题作为提升编程能力和解决实际问题的有效手段,受到越来越多程序员和学生的青睐。通过刷算法题,我们可以不断锻炼自己的逻辑思维能力、编程实现能力以及解决问题的技巧,为日后的实际工作和学习打下坚实的基础。
实践
题目一
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
解题思路:
- 暴力法:最容易想到的方法是使用两层循环遍历数组中的每一个元素,并查找是否存在另一个元素与之相加等于目标值。
- 哈希表:通过空间换时间,我们可以利用哈希表来降低时间复杂度。遍历数组过程中,可以将每个元素的值和索引存储在哈希表中。在存储过程中,我们可以同时检查当前元素的补数(target-nums[i])是否已经在哈希表中,如果是则直接返回结果。
实现过程:
暴力法比较简单,这里主要用哈希表来实现
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
dic = {}
for index, num in enumerate(nums):
temp = target - num
if temp in dic:
return [dic[temp], index]
dic[num] = index
return []
我们对这段代码进行讲解:
- 首先,创建一个空的字典
dic,用于存储元素值和对应的索引。 - 使用
enumerate函数遍历数组nums,同时获取元素的索引和值。 - 对于每个元素,计算其与目标值的差值
temp。 - 在字典
dic中查找是否存在temp,如果存在,则说明找到了符合条件的两个整数,直接返回它们的索引。 - 如果字典中不存在
temp,则将当前元素的值和索引存入字典。 - 如果遍历完数组后仍未找到符合条件的结果,则返回一个空列表。
需要注意的是,代码中使用了Python特有的语法和函数,如列表推导式、字典的键值对操作和enumerate函数。这些都是Python简洁和高效的特性,方便了代码的编写和阅读。
复杂度分析:
- 时间复杂度:O(n),其中 n 是数组中的元素数量。在遍历数组的过程中,对于每一个元素,我们可以在哈希表中以 O(1) 的时间复杂度查找其补数。
- 空间复杂度:O(n),其中 n 是数组中的元素数量。哈希表中存储了 n 个元素的值和索引。
题目二
给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。字母异位词指字母相同,但排列不同的字符串。
解题思路:
1、创建一个空的哈希表 groups,用于存储分组后的结果。键为排序后的字符串(即字母相同且排列相同的字符串),值为属于该分组的字符串列表。
2、遍历字符串数组 strs 中的每一个字符串:
- 将当前字符串
s转换成排序后的字符串key。 - 如果
key已经存在于哈希表groups中,则将当前字符串s加入到对应的分组中。 - 如果
key不存在于哈希表groups中,则创建一个新的分组,并将当前字符串s加入到该分组中。
3、返回哈希表 groups 中的所有值(分组)作为结果。
实现过程:
import collections
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
groups = collections.defaultdict(list)
for s in strs:
key = "".join(sorted(s))
groups[key].append(s)
return list(groups.values())
solution = Solution()
solution.groupAnagrams(["eat", "tea", "tan", "ate", "nat", "bat"]) # [["bat"],["nat","tan"],["ate","eat","tea"]]
我们对这段代码进行讲解:
-
导入了 collections 模块,用于使用 defaultdict 来创建默认值为列表的字典。
-
定义了一个名为 Solution 的类,其中包含一个方法 groupAnagrams,用于实现字母异位词的分组。
-
方法 groupAnagrams 的参数包括 self 和 strs,其中 self 代表类的实例本身,而 strs 是一个字符串列表。
-
创建了一个默认值为列表的字典 groups,用于存储分组后的结果。
-
使用 for 循环遍历输入的字符串列表 strs 中的每一个字符串:
- 将当前字符串 str 排序后连接成一个新的字符串 key。
- 将原始的字符串 str 加入到对应 key 的分组中。
-
最后,返回字典 groups 的所有值(分组)作为结果列表。
复杂度分析:
- 时间复杂度:O(n * k * log(k)),其中 n 是字符串数组
strs的长度,k 是字符串的平均长度。对于每个字符串,我们需要花费 O(k * log(k)) 的时间进行排序。 - 空间复杂度:O(n * k),需要额外的哈希表来存储分组后的结果。
题目三
给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
解题思路:
- 首先,我们可以将数组中的所有元素存入一个集合(set)中,以便快速地进行查找操作。
- 接下来,我们遍历数组中的每一个元素
num,并检查是否存在比num小 1 的数字。如果不存在,则说明num是一个连续序列的开始。 - 对于每个连续序列的开始数字,我们通过不断增加当前数字的值来查找连续序列的长度。具体做法是,我们从该数字开始递增,直到找不到比当前数字大 1 的数字为止。
- 在查找过程中,我们记录每个连续序列的长度,并更新最大长度。最终,返回最大长度作为结果。
实现过程:
class Solution:
def longestConsecutive(self, nums: List[int]) -> int:
num_set = set(nums)
max_len = 0
for num in num_set:
if num - 1 not in num_set:
current_num = num
current_len = 1
while current_num + 1 in num_set:
current_num += 1
current_len += 1
max_len = max(max_len, current_len)
return max_len
solution = Solution()
solution.longestConsecutive([100,4,200,1,3,2]) # 4
我们对这段代码进行讲解:
-
首先,将输入的整数数组
nums转换为一个集合num_set。这样做可以提高查找操作的效率。 -
初始化最大长度
max_len为 0。 -
对于集合
num_set中的每个元素num进行遍历:- 如果
num - 1不在num_set中,说明num是一个连续序列的开始。 - 初始化当前数字和当前长度为
num和 1。 - 在循环中,通过递增当前数字的值,并判断递增后的数字是否存在于
num_set中,来查找连续序列的长度。 - 当找不到递增后的数字时,更新最大长度
max_len。
- 如果
-
最后,返回最大长度作为结果。
复杂度分析:
- 时间复杂度:O(n),其中 n 是数组的长度。在遍历数组的过程中,我们对每个数字只进行了一次查找操作,并且查找操作的时间复杂度是 O(1)。
- 空间复杂度:O(n),需要额外的空间来存储数字集合。
最后
当我们面对算法问题时,每一次解决都是一次成长。通过不断地思考、学习和实践,我们不仅提高了解决问题的能力,还培养了逻辑思维和分析技能。算法不仅仅是解决问题的工具,更是一种锻炼思维的方式。正如迈克尔·乔丹所说:“技术可以教,激情可以训练,但韧性却无法取代。”在刷题的过程中,我们不仅在不断提高自己的技术水平,更在锤炼自己的意志力和毅力。希望笔者可以坚持✊。