344.反转字符串
这道题的要求是不开辟新的内存空间,原地修改输入数组
所以采用了双指针法,一个指针指向首位一个指针指向末位,交换首位末位并且两个指针向内各走一步
记住这种做法
def reverseString(self, s: List[str]) -> None:
"""
Do not return anything, modify s in-place instead.
"""
l,r=0,len(s)-1
while l<r:
s[l],s[r]=s[r],s[l]
l+=1
r-=1
541. 反转字符串II
操作字符串前还要明确一件事,字符串属于不可变类型,我们可以去模拟的改变字符串列表。
不然会出现,‘str’ object does not support item assignment
这道题的题意大概是说 以2k为一段,反转前k个字符串;如果最后还剩一小段在 k-2k之间长度,依然反转前k个字符串;如果最后还剩一下段在k长度以下,就直接反转这一段。
def reverseStr(self, s: str, k: int) -> str:
def reverse_substring(text):
left, right = 0, len(text) - 1
while left < right:
text[left], text[right] = text[right], text[left]
left += 1
right -= 1
return text
res = list(s)
for cur in range(0, len(s), 2 * k):
res[cur: cur + k] = reverse_substring(res[cur: cur + k])
return ''.join(res)
其中的反转函数直接用了344反转字符串中的那种从两端两两交换的方法,然后以2k为步长,去遍历整段字符串,去反转每一段前k字符串;由于python切片的语言特性,剩余长度不足k的话它也是可以自动反转的。这样的代码不用判别很多边界条件,看起来逻辑十分简洁。
剑指Offer 05.替换空格
首先很容易想到,str.replace('old', 'new') . 但是由于字符串是不可变类型,这个函数是不会修改原有的str的。在这里把卡哥关于字符串和数组的比较搬过来吧:
{ 这里也给大家拓展一下字符串和数组有什么差别,
字符串是若干字符组成的有限序列,也可以理解为是一个字符数组,但是很多语言对字符串做了特殊的规定,接下来我来说一说C/C++中的字符串。
在C语言中,把一个字符串存入一个数组时,也把结束符 '\0'存入数组,并以此作为该字符串是否结束的标志。
例如这段代码:
char a[5] = "asd";
for (int i = 0; a[i] != '\0'; i++) {
}
在C++中,提供一个string类,string类会提供 size接口,可以用来判断string类字符串是否结束,就不用'\0'来判断是否结束。
例如这段代码:
string a = "asd";
for (int i = 0; i < a.size(); i++) {
}
那么vector< char > 和 string 又有什么区别呢?
其实在基本操作上没有区别,但是 string提供更多的字符串处理的相关接口,例如string 重载了+,而vector却没有。
所以想处理字符串,我们还是会定义一个string类型。}
那么这道题的思路是什么:
首先需要去统计一下有多少空格,接着拿一个数组去扩充到把这些空格都填成字符后的大小,原始空格只要增加两个空格位置就行了. nums.extend()函数等于 += ,因此追加的元素也要是[ ].
接着定义两个指针, l, r 分别指向原始字符串s的尾端 和扩容后的数组nums的尾端,
然后用 l 去遍历原始s, while l >= 0:
l -= 1
如果说nums[l] != ' ', 把nums[l]的值赋给nums[r], r -= 1, 后退一位
如果说 nums[l] == ' ' , 这个时候要填%20, nums[r-2] nums[r-1] nums[r] 要填成 % 2 0,此时r 后退三位
def replaceSpace(self, s: str) -> str:
n_space = s.count(' ')
res = list(s)
res.extend([' '] * n_space * 2)
l, r = len(s) - 1, len(res) - 1
while l >= 0:
if res[l] != ' ':
res[r] = res[l]
r -= 1
else:
res[r-2:r+1] = '%20'
r -= 3
l -= 1
return ''.join(res)
为什么要从后向前填充,从前向后填充不行么?
从前向后填充就是O(n^2)的算法了,因为每次添加元素都要将添加元素之后的所有元素向后移动。
其实很多数组填充类的问题,都可以先预先给数组扩容带填充后的大小,然后在从后向前进行操作。
这么做有两个好处:
- 不用申请新数组。
- 从后向前填充元素,避免了从前先后填充元素要来的 每次添加元素都要将添加元素之后的所有元素向后移动。
151.翻转字符串里的单词(小米测开的面试遇到)
这个题以我七八月份刷牛客网上的华为机考题库的经验来说,我是可以用模拟的手段去做的。就是先把字符串以空格为界,变成数组;然后拿数组去把每个单词倒序输出;最后再转成字符return。这时空复杂度都是O(n)。
代码大概长这个样子:
def reverseWords(self, s: str) -> str:
res = []
s = list(s.split())
for i in range(len(s)-1, -1, -1):
res.append(s[i])
str_res = ''
for i in range(len(res)):
str_res += str(res[i])
str_res += ' '
str_res = str_res.rstrip()
return str_res
卡哥的思路是让我们锻炼字符串的基本操作的,是先把字符串整体反转,然后把每个单词中的字符进行反转。这个纯不用库函数的写法我不想在抄了。纯属是哈人,就算是面试,面试官也不会想让你这样写,但是思路可以抄一下
可以直接用两个切片的反转,先整体字符串反转一下,然后遍历的时候,每个单词再用切片反转一下。
剑指Offer58-II.左旋转字符串
首先,切片是可以过的
def reverseLeftWords(self, s: str, n: int) -> str:
length = len(s)
return s[n:length] + s[:n]
其次,正经思路是局部反转加全局反转
def reverseLeftWords(self, s: str, n: int) -> str:
s = list(s)
s[0:n] = list(reversed(s[0:n]))
s[n:] = list(reversed(s[n:]))
s.reverse()
return "".join(s)
当然我觉得也可以自己写一下这个reverse函数