Leetcode 1~5
最近开始刷题,在此记录一下。
1. Two number (Easy)
给定一个整数数组和一个目标值,该目标值是数组中两个数的和,输出这两个数的标号。注:每个数只能被用一次。
例如:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
最直观最暴力的解法就是两重循环,复杂度为 O ( n 2 ) O(n^2) O(n2)。
class Solution(object):
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
n = len(nums)
for i in range(n):
dif = target - nums[i]
for j in range(n):
if dif == nums[j] and i != j:
return [i, j]
这种方法虽然思路简单,但是实在是很慢,leetcode里显示的运行时间是2872 ms,占的空间是11.5 MB。
有没有办法改进呢?答案是肯定的。 改进的方法有很多种,在此我贴出我写的一种:
class Solution(object):
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
d = {}
for i, v in enumerate(nums):
dif = target - v
if dif in d:
return ([d[dif], i])
d[v] = i
可以看到该算法的复杂度为 O ( n ) O(n) O(n)。 leetcode中的运行时间为20 ms, 所占空间为11.8 MB。可以看到运行速度提高了非常非常多!
2. Add Two Numbers (Medium)
给定两个非负非空的链表, 反向遍历这两个链表并将其遍历结果作为两个数,以链表的形式输出这两个数的和。
例如:
Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 0 -> 8
Explanation: 342 + 465 = 807.
要做这一题,首先得知道什么是链表。
顾名思义,链表其实就是有一条链条连起来的表,哈哈。具体来说,就是表中的数都有两个域,一个是数据域,用于储存具体的数值,还有另外一个指针域,用于指向该数的下一个值。
用这种方式存储的话,好处是很明显的,因为每个数都有其后继的信息,因此一个表中的数在内存中并不需要顺序存储。对内存空间的利用率高,不会造成内存空间碎片。当然,也消耗了更多的内存空间啦。
明白了概念的话,链表的定义实现其实蛮简单的。这一题中也用注释来给大家提示了。
# Definition for singly-linked list.
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
运用该定义,一个链表就可以轻易的写出来啦。
l1 = ListNode(1)
l1.next = ListNode(2)
l1.next.next = ListNode(3)
遍历该链表结果如下:
while l1:
print(l1.val)
l1 = l1.next
# Result
1
2
3
回到该题,比较简单的思路就是将这两个链表转化成两个数先,算出这两个数的和,然后将结果转换成string, 然后反向,转换成链表就行。
python实现如下:
class Solution(object):
def addTwoNumbers(self, l1, l2):
"""
:type l1: ListNode
:type l2: ListNode
:rtype: ListNode
"""
count, num1, num2 = 0, 0, 0
while l1:
num1 = num1 + l1.val * pow(10, count)
count += 1
l1 = l1.next
count = 0
while l2:
num2 = num2 + l2.val * pow(10, count)
count += 1
l2 = l2.next
sum = num1 + num2
li = list(reversed(str(sum)))
output = ListNode(li[0])
temp = output
for i in range(1, len(li)):
temp.next = ListNode(li[i])
temp = temp.next
return output
该方法时间复杂度为 O ( n ) O(n) O(n)。 在leetcode中打败了86.11%的人,运行时间为68ms, 内存10.8MB。
当然,其实还有一种更简单的方法。通过观察可知,在不考虑满十进位的情况,其实最终的和就是每个数直接相加,不用倒置。加上进位的话,就只是把向前进位改成向后进位即可。
实现如下:
class Solution(object):
def addTwoNumbers(self, l1, l2):
"""
:type l1: ListNode
:type l2: ListNode
:rtype: ListNode
"""
sum = 0
temp = output = ListNode(0)
while l1 or l2 or sum:
if l1:
sum += l1.val
l1 = l1.next
if l2:
sum += l2.val
l2 = l2.next
temp.next = ListNode(sum%10)
temp = temp.next
sum = sum//10
return output.next
可以看到,该实现更加简洁一点。不过时间复杂度,运行时间和所占空间跟上面的方法一样。
3. Longest Substring Without Repeating Characters (Medium)
给定一个字符串,找出其中最长的不重复的子串的长度。
Input: "abcabcbb"
Output: 3
Input: "bbbbb"
Output: 1
Input: "pwwkew"
Output: 3
关于这一题,我的思路是,首先建一个空的字符串B 和 初始化为0的变量 length,遍历输入的字符串A,如果字符 i 不在B中,则加入B中;反之,则只保留B中从 i 开始往后的子串(包含i)。同时,每遍历一次,则计算一次B的长度,并与 length 对比,如果大于 length, 则将值赋给 length。
例如:
input: "pwwkew"
B = "pw", length = 2
-> w -> B = "w", length = 2
... B = "wke", length = 3
-> w -> B = "kew", length = 3
output: 3
实现如下:
class Solution(object):
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
ns = ''
length = 0
for i in s:
if i not in ns:
ns += i
else:
ns = ns.split(i)[-1]
ns += i
if len(ns) > length:
length = len(ns)
return length
该方法的时间复杂度为 O ( n ) O(n) O(n)。 在leetcode中打败了86.3%的提交方案,运行时间为48ms, 内存空间占用为11.1 MB。
该题我只提交了一种解决方案。当然,还有无数种方案等着大家挖掘。
4. Median of Two Sorted Arrays (Hard)
给定两个有序的数组,长度分别为 m 和 n, 找到这两个数组的中位数。
该算法的时间复杂度应该为
O
(
m
+
n
)
O(m + n)
O(m+n)。
这是我做的第一道困难模式的题,哈哈。我感觉其难点在于该题规定了时间复杂度。
如果不考虑时间复杂度的话,其实解决方案并不太难,直接将这两个数组合并,然后找出中位数就行了。最简单的合并方法,直接两重循环就成。但是这样的话时间复杂度并不符合要求。这时,考虑到两个数组是有序的,完全可以用归并排序的思想来做这一题。只取归并排序中的最后一步即可,时间复杂度也是符合要求的。
实现如下:
class Solution:
def findMedianSortedArrays(self, nums1: 'List[int]', nums2: 'List[int]') -> 'float':
list_o = []
i, j = 0, 0
if len(nums1) == 0:
list_o = nums2
elif len(nums2) == 0:
list_o = nums1
else:
while i < len(nums1) and j < len(nums2):
if nums1[i] < nums2[j]:
list_o.append(nums1[i])
i += 1
else:
list_o.append(nums2[j])
j += 1
list_o += nums1[i:]
list_o += nums2[j:]
r = len(list_o) % 2
m = len(list_o) // 2
if r == 1:
med = list_o[m]
else:
med = (list_o[m-1] + list_o[m])/2
return med
该方法打败了leetcode中69.09%的提交方案,运行时间为96ms,空间为 12.6 MB。
排名不高,所以显然还有更简单的方法。
其实有一种非常非常直观,但是容易被忽略的方法。就是直接合并这两个数组,然后调用 sort() 方法排序就行。我是在讨论区看到这个方法的,当时也是有点沮丧,这么简单的方法我当时居然么有想到!!
Anyway,贴代码吧。
class Solution:
def findMedianSortedArrays(self, nums1: 'List[int]', nums2: 'List[int]') -> 'float':
nums1.extend(nums2)
nums1.sort()
re = len(nums1) % 2
m = len(nums1)//2
if re == 1:
return nums1[m]
else:
return (nums1[m-1]+nums1[m])/2
短短几行,妥妥的。打败了92.8%, 运行时间88ms, 占空间12.8 MB。
5. Longest Palindromic Substring (Medium)
在一个字符串中找最长的回文结构的子串。
这一题我虽然是中等难度的,可是我做了很久… 比上一题hard的还要久很多。中间一度很想看别人的答案。
算了,不多说了。
首先说啥叫回文结构话,白话一点,其实就是对称结构。
例如:
Input: "babad"
Output: "bab"
Input: "cbbd"
Output: "bb"
Input: “xabax”
Output: “xabax”
那如何简单验证其是否为对称结构呢,就是将这个子串反向,它如果跟正向的一摸一样,就肯定是对称结构啦。
我首先实现了一个非常粗暴的方案:首先找出所有不重复的字符,然后找出每个不重复字符在字符串S中出现的位置 l, 比如 ‘abbba’, -> a: l = [0,4], b: l = [1,2,3] 。然后如果该字符正好出现了两次,则判断其两次之间的子串是否为对称结构。如果不是两次,则对 l 进行两重循环 i, j,判断 i,j之间的子串是否为对称结构。比如 ‘abbb’, -> a, b, bb, bbb.
实现如下:
class Solution:
def longestPalindrome(self, s: 'str') -> 'str':
if len(s) == 0:
return s
else:
set_s = set(s)
leng, pa_s = 0, ''
for i in set_s:
ind, l = 0, []
for j, v in enumerate(s):
if i == v:
l.append(j)
if len(l) == 2:
sub_s = s[l[ind]:l[ind+1]+1]
if sub_s == sub_s[::-1]:
if len(sub_s) > leng:
pa_s = sub_s
leng = len(sub_s)
else:
for i in l:
for j in l:
sub_s = s[i:j+1]
if sub_s == sub_s[::-1]:
if len(sub_s) > leng:
pa_s = sub_s
leng = len(sub_s)
return pa_s
当然,这个方法时间复杂度非常高,有 O ( n 3 ) O(n^3) O(n3)。 提交之后显示打败了打败了26.98%的方案,运行时间为 4084ms, 空间为 12.2 MB.
所以,有非常非常多简单的方法可以实现。
在此贴出一种,该方法非常巧妙,初始化 maxlen 为1,即对于一个字符串来说,至少有一个字符其本身为对称结构。然后从第一个字符开始,不断的验证从第一个字符开始,长度为maxlen的子串是否为对称结构。如果是则调整maxlen的长度。 同时大于一的最小对称结构其实只有两种,一种是 ‘aa’, 另外一种是 ‘aba’。 所以对于一个对称的子串来说,包含其的更大的对称子串只可能是 + 1 或者 + 2。
该代码实现如下:
class Solution:
def longestPalindrome(self, s: 'str') -> 'str':
if len(s) == 0:
return s
else:
maxlen, start = 1, 0
for i in range(len(s)):
if (i - maxlen) >= 1 and s[(i-maxlen-1):i+1] == s[(i-maxlen-1):i+1][::-1]:
start = i-maxlen-1
maxlen += 2
print(maxlen)
continue
if (i - maxlen) >= 0 and s[(i-maxlen):i+1] == s[(i-maxlen):i+1][::-1]:
start = i - maxlen
maxlen += 1
print(maxlen)
return s[start:start+maxlen]