最近正在学习Python,因为对语言的不熟练,所以打算刷题来使自己的语言掌握的更充分。在leetCode上看见了这么一道题
The string "PAYPALISHIRING" is written in a zigzag pattern on a given number of rows like this: (you may want to display this pattern in a fixed font for better legibility)
就是ZigZag 首先还是用图来说一下原理吧
首先就用简单的'0123456789012345678'来演示下
就是将数字按上图排放,以竖列形式拜访
读取的时候从左向右读所以最终读取出来的文字是"0482613579135726048"
读取方式也知道了,下面就该开始代码部分了。
首先我的思路就是按照图片方式,准备一个二维数组。
#这里是判断如果特殊情况,若给定行数过大或过下,直接做特殊处理
if numRows == 2:
array_1 = []
array_2 = []
for i in range(0,len(s)):
if i % 2 == 0:
array_1.append(s[i])
else:
array_2.append(s[i])
str = ''.join(array_1)
str += ''.join(array_2)
return str
elif numRows <= 1:
return s
elif numRows >= len(s):return s
#首先根据上图中方框部分算出,当numRows = 4时候重复部分的数量为6,当numRows = 3时重复部分的字母数量为4,可以在纸上画出当numRows = 5时重复部分的字符数量为8,所以计算可得重复部分的数量随numRows变化的数量就是numRows + numRows - 2
#count 为剩余部分有多少个字符
count = len(s) % (numRows + numRows - 2)
#index为总共有多少个块
index = len(s) // (numRows + numRows - 2)
#i为这个块中有多少列观察可得,numRows 数量单独占一列,剩下的一个字符占一列,所以最终每个块中有1 + numRows - 2列
i = index * (1 + numRows - 2)
#j为剩余部分字符减去列数后剩余数量+1列
j = (count - numRows + 1) if count > numRows else 1
#total为总列数
total = i + j
array = []
接下来就该开始循环给对应字符放入数组中了
for real_i in range(0,total):
#创建每列的数组
real_array = []
#这个判断为判断是否为该块的第一列(第一列为字母数量为numRows,其他列数字母数量为1)
if real_i % (numRows - 1) == 0:
#计算该块起始位置
g = 2 * real_i
#循环添加字符进入数组
for letter in range(g, g + numRows if g + numRows < len(s) else len(s)):
real_array.append(s[letter])
else:
#计算该列在该块中的位置
real_index_i = real_i % (numRows - 1)
#计算这个字符处于这个数组中的第几个位置
real_index_j = 2 * (real_i - real_index_i) + numRows
#取出字母
letter = s[real_index_i + real_index_j - 1]
#循环向数组中加入字符
for x in range(0,numRows):
if x == (numRows - 1 - real_index_i):
real_array.append(letter)
else:
real_array.append(' ')
#将这个数组加入总数组
array.append(real_array)
这步做完了后,所有操作基本已经算是完成,这个已经算是完成一多半
最后就剩下拼凑字符串了
str = ''
i = 0
#取数组和平时遍历数组方式不太一样,外层的循环实际上是用来遍历每个数组的角标i的位置的
while True:
for sub_array in array:
if len(sub_array) > i:
real = sub_array[i]
#这个地方的判断是为了防止数字崩溃,因为当时用数字做的测试,将数字转换成字符
if isinstance(real,int):
str += '%d '%(real)
else:
str += real
#下面部分是为了方便打印出对应的格式的(真实提交测试时需要删去)
str += ' '
str += '\n'
i += 1
if i == numRows:
break
return str
经过测试提交,leetCode 给出的error是timeLimit,说明代码是可以正确计算出结果了,但是耗时非常长,复杂度至少是O(n*numRows),太复杂了,耗时主要是在从数组中取出对应字符的步骤,所以需要优化,最终决定优化的方式是将数组转化成字典,用key对应所在位置,直接优化复杂度为O(n3)
#首先是循环部分修改
dic = {}
#real_i直接为所在位置的横坐标
for real_i in range(0,total):
if real_i % (numRows - 1) == 0:
g = 2 * real_i
for n in range(g, g + numRows if g + numRows < len(s) else len(s)):
#y为纵坐标
index_y = letter - g
index_x = real_i
dic[index_x + index_y * (total)] = s[n]
else:
real_index_i = real_i % (numRows - 1)
real_index_j = 2 * (real_i - real_index_i) + numRows
letter = s[real_index_i + real_index_j - 1]
index_y = numRows - 1 - real_index_i
index_x = real_i
dic[index_x + index_y * (total)] = letter
接下来就剩读取了,首先,现在key不是连续的,但是,咱们的位置排序却是从小到大排序的,所以,下一步的操作就是给key进行排序了,python有很强大的排序方法,就是sort
str = ''
i = 0
array = list(dic.keys())
array.sort()
for key in array:
letter = dic[key]
if isinstance(letter, int):
str += '%d' % (letter)
else:
str += letter
return str
将修改后的代码重新提交测试,成功通过。应该还有更好的方法,就是直接计算出对应的角标位置,但是碍于时间有限,就先用这个方法偷偷懒啦