ZEROMAKE | keep codeing and thinking!
2018-06-28 | algorithm

leetcode 1-5 算法题

前言

  • 最近也还是没怎么写博文,所以打算直接找个不用怎么难写的算法来写写博客。
  • 为了加深印象,需要书写博文进行归纳,并且进行伪代码连续,以及手写算法。
  • 这边会缓慢更新。

一、Tow Sum

作为 leetcode 的第一题难度很低。

地址

1.1 题目

给定一个整数数组,返回这两个数字的索引,使它们合计成一个特定的目标。
您可能会认为每个输入都只有一个解决方案,并且您可能不会使用相同的元素两次。

英文 Given an array of integers, return indices of the two numbers such that they add up to a specific target.

You may assume that each input would have exactly one solution, and you may not use the same element twice.

Example:

1
Given nums = [2, 7, 11, 15], target = 9,
2
3
Because nums[0] + nums[1] = 2 + 7 = 9,
4
return [0, 1].

1.2 思路解题

1. 暴力破解

即直接两层嵌套循环,相加并下标不同,代码复杂度: O(n^2)。

1
def towSum(nums, target):
2
"""
3
:type nums: List[int]
4
:type target: int
5
:rtype: List[int]
6
"""
7
for index1, n in enumerate(nums):
8
for index2, j in enumerate(nums):
9
if index1 != index2 and (n + j) == target:
10
# 一定是外层的index小
11
return [index1, index2]

2. 动态规划

使用的字典/map key 存放值,val 存放下标,再遍历用目标减去遍历的数再到 map 中寻找,且下标不相同, 代码复杂度: O(n)。

1
def towSum(nums, target):
2
"""
3
:type nums: List[int]
4
:type target: int
5
:rtype: List[int]
6
"""
7
cache = {key: val for val, key in enumerate(nums)}
8
for index, n in enumerate(nums):
9
temp = target - n
10
if temp in cache:
11
index2 = cache[temp]
12
if index2 != index:
13
# 能到这里index一定小
14
return [index, index2]

3. 最优解

在 2 号方案的基础上优化,把 map 的生成去掉,并且去掉 enumerate,也是 O(n),但是在 leetcode 中仅需 36ms,python 中排第一。

1
def twoSum(nums, target):
2
"""
3
:type nums: List[int]
4
:type target: int
5
:rtype: List[int]
6
"""
7
cache = {}
8
for i in range(len(nums)):
9
n = nums[i]
10
index = target - n
11
if index in cache:
12
# 命中的话说明已经遍历到大的下标
13
return [cache[index], i]
14
# 由于必然是两个不同下标的数,在查找后才存入防止返回两个相同下标
15
# 所以在没有找到的情况下把下标较小的存入 map
16
cache[n] = i

二、Add Tow Numbers

难度: Medium

地址

2.1 题目

给你两个非空链表,表示两个非负整数。数字以相反的顺序存储,每个节点都包含一个数字。

这两个链表添加并将其作为链接列表返回。您可以假设这两个数字不包含任何前导零,除了数字 0 本身。

题目有点误导性,我一直以为是什么意思,实际上意思就是这个两个链表都是一个 10 进制数的每个位组成的倒序链表,想办法把两个数相加并且依旧返回这种格式。

英文 You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list. You may assume the two numbers do not contain any leading zero, except the number 0 itself.

Example

1
Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
2
Output: 7 -> 0 -> 8
3
Explanation: 342 + 465 = 807.

2.2 思路解题

1. 暴力算法

直接转换回数字相加再转换回去,复杂度: O(max(n,m)) + O(n) + O(m)。

1
class ListNode:
2
def __init__(self, x):
3
self.val = x
4
self.next = None
5
6
def numToList(num: int) -> ListNode:
7
res = ListNode(0)
8
temp = res
9
i = num
10
while True:
11
res.val = i % 10
12
i = i // 10
13
if i == 0:
14
break
15
res.next = ListNode(0)
16
res = res.next
17
return temp
18
19
def listToNum(l: ListNode) -> int:
20
res = 0
21
count = 0
22
while l:
23
res = l.val * 10 ** count
24
l = l.next
25
count += 1
26
return res
27
28
def addTwoNumbers(l1, l2):
29
return numToList(listToNum(l1) + ListToNum(l2))

2. 好的做法是

由于链表是倒序的数所以只需要每个位的数相加如果有进位加到下一个位上,复杂度: O(max(n, m))。

1
class ListNode:
2
def __init__(self, x):
3
self.val = x
4
self.next = None
5
6
def addTwoNumbers(l1, l2):
7
"""
8
:type l1: ListNode
9
:type l2: ListNode
10
:rtype: ListNode
11
"""
12
list1 = l1
13
list2 = l2
14
data = ListNode(0)
15
res = data
16
last = 0
17
while list1 and list2:
18
numSum = list1.val + list2.val + last
19
num = numSum % 10
20
last = numSum // 10
21
data.val = num
22
data = data.next
23
list1 = list1.next
24
list2 = list2.next
25
if not list1 and not list2:
26
break
27
data.next = ListNode(0)
28
if not list1:
29
list1 = ListNode(0)
30
if not list2:
31
list2 = ListNode(0)
32
if last == 1:
33
data.next = ListNode(last)
34
return res

三、Longest Substring Without Repeating Characters

3.1 题目

给定一个字符串,找出不含有重复字符的最长子串的长度。

示例:

给定 abcabcbb ,没有重复字符的最长子串是 abc ,那么长度就是 3。
给定 bbbbb ,最长的子串就是 b ,长度是 1。
给定 pwwkew ,最长子串是 wke ,长度是 3。请注意答案必须是一个子串,pwke 是 子序列 而不是子串。

英文 Given a string, find the length of the longest substring without repeating characters.

Examples:

Given “abcabcbb”, the answer is “abc”, which the length is 3.
Given “bbbbb”, the answer is “b”, with the length of 1.
Given “pwwkew”, the answer is “wke”, with the length of 3. Note that the answer must be a substring, “pwke” is a subsequence and not a substring.

3.2 思路解题

1. 暴力破解法

双重循环,把每一种可能性遍历出来,判断是否有重复,复杂度 O(n^3).

1
def allUnique(s, start, end):
2
temp = set()
3
for i in range(start, end):
4
ch = s[i]
5
if temp.has(ch):
6
return False
7
temp.add(ch)
8
return True
9
10
def lengthOfLongestSubstring(s):
11
slen = len(s)
12
res = 0
13
for i in range(slen):
14
for j in range(1, slen):
15
if allUnique(s, i, j):
16
res = max(res, j-i)
17
return res

2.最优解,滑动窗口

使用 dict 存放已遍历的字符为 key, val 为 index + 1,只有 dict 存在且上一次的 index 大于才是这次的字符串窗口内有重复,这个时候把窗口的开始移动到这个重复字符的后一位(保证 abcaf 这种可以使用), 复杂度: O(n)。

1
def lengthOfLongestSubstring(s):
2
cache = {}
3
start = 0
4
res = 0
5
slen = len(s)
6
for end in range(slen):
7
char = s[end]
8
# cache[char] <= start 说明这个字符上次重复是交叉的不在当前的字符串窗口
9
if char in cache and cache[char] > start:
10
# 如果重复的字符比 start 大说明是当前的字符串重复
11
# 如果发生重复把滑动窗口的起始移动到上一个重复的后一位
12
start = cache[char]
13
if end - start > res:
14
res = end - start
15
# 计算不重复的字符长度
16
# 设置字符并把这个字符的下一个索引写入
17
cache[char] = end + 1
18
# 判断最后一次无重复是否为最长
19
if slen - start > res:
20
res = slen - start
21
return res

四、Median of Two Sorted Arrays

4.1 题目

给定两个大小为 m 和 n 的有序数组 nums1 和 nums2 。

请找出这两个有序数组的中位数。要求算法的时间复杂度为 O(log (m+n)) 。

你可以假设 nums1 和 nums2 均不为空。

英文 There are two sorted arrays nums1 and nums2 of size m and n respectively.

Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).

You may assume nums1 and nums2 cannot be both empty.

4.2 思路解题

1. 暴力破解法

把两个数组合并,并且排序取中位数。

由于 python 的特性这个方法在 python 的题解里是最优解。

1
def findMedianSortedArrays(nums1, nums2):
2
"""
3
:type nums1: List[int]
4
:type nums2: List[int]
5
:rtype: float
6
"""
7
num = nums1 + nums2
8
num = sorted(num)
9
if len(num)%2 != 0:
10
return num[len(num)//2]
11
x = len(num)//2
12
m = num[x] + num[x-1]
13
return m/2

顺便放个其它语言的暴力破解的最优解。

1
double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size) {
2
int total = nums1Size+nums2Size;
3
int nums[total];
4
double result = 0.0;
5
int i1 = 0, i2 = 0;
6
int k = 0;
7
for (int i = 0 ; i < total ; i++) {
8
if (i1 >= nums1Size) {
9
nums[i] = nums2[i2++];
10
} else if( i2 >= nums2Size) {
11
nums[i] = nums1[i1++];
12
} else if(nums1[i1] > nums2[i2]) {
13
nums[i] = nums2[i2++];
14
} else if(nums1[i1] < nums2[i2]) {
15
nums[i] = nums1[i1++];
16
} else if(nums1[i1] == nums2[i2]) {
17
nums[i] = nums1[i1++];
18
nums[++i] = nums2[i2++];
19
}
20
}
21
k = total / 2;
22
if (total % 2 == 1) {
23
result = nums[k] + 0.0;
24
} else {
25
result = ( nums[k - 1] + nums[k] ) / 2.0;
26
}
27
return result;
28
}

2. 另一种解法是把问题改造为查找第 m 大的数

直接比较两个数组的第 m//2 个数小于的说明不在这个这个数组的前 m//2中。

因为在 python 中不算是最优解就不做 python

1
int min(int a, int b) {
2
return a > b ? b : a;
3
}
4
5
void swapInt(int* a, int* b) {
6
int swap;
7
swap = *a;
8
*a = *b;
9
*b = swap;
10
}
11
12
int findKth(int* nums1, int m, int* nums2, int n, int k) {
13
int* snums;
14
int temp, j, i;
15
16
// 数组1的偏移量
17
int start1 = 0;
18
// 数组2的偏移量
19
int start2 = 0;
20
21
while(1) {
22
if (m > n) {
23
// 把大的放到nums2上
24
swapInt(&m, &n);
25
swapInt(&start1, &start2);
26
snums = nums1;
27
nums1 = nums2;
28
nums2 = snums;
29
}
30
if (m == 0) {
31
// 说明已经有一个数组被排除直接查找还有的数组的第 k 大数。
32
return nums2[start2 + k - 1];
33
}
34
if (k == 1) {
35
// 如果是取第一个大的数直接取两个数组的第一个数并取其中的最小值
36
return min(nums1[start1], nums2[start2]);
37
}
38
temp = k / 2;
39
i = min(m, temp);
40
j = min(n, temp);
41
// 比较 k/2 的数排除不需要的元素。
42
if (nums1[start1 + i - 1] > nums2[start2 + j -1]) {
43
k -= j;
44
start2 += j;
45
n -= j;
46
} else {
47
k -= i;
48
start1 += i;
49
m -= i;
50
}
51
}
52
}
53
54
double findMedianSortedArrays(int* nums1, int m, int* nums2, int n) {
55
int slen = m + n;
56
int left = (slen + 1) / 2;
57
int right = (slen + 2) / 2;
58
if (left == right){
59
// 说明只有一个中位数
60
return (double)findKth(nums1, m, nums2, n, left);
61
}
62
int res1 = findKth(nums1, m, nums2, n, left);
63
int res2 = findKth(nums1, m, nums2, n, right);
64
// 取出两个中位数相加并 /2
65
return (double)(res1 + res2) / 2.0;
66
}

五、Longest Palindromic Substring

5.1 题目

给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

示例 1:

1
输入: "babad"
2
输出: "bab"
3
注意: "aba"也是一个有效答案。

示例 2:

1
输入: "cbbd"
2
输出: "bb"
英文 Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.

Example 1:

1
Input: "babad"
2
Output: "bab"
3
Note: "aba" is also a valid answer.

Example 2:

1
Input: "cbbd"
2
Output: "bb"

5.2 思路解题

1. 暴力破解

使用三层循环,强制枚举每个可能性。

1
def longestPalindrome(targetStr: str) -> str:
2
"""
3
:type targetStr: str
4
:rtype: str
5
"""
6
str_len = len(targetStr)
7
last_len = 0
8
index = [0,0]
9
for start_index in range(str_len - 1):
10
for end_index in range(start_index+1, str_len):
11
start = start_index
12
end = end_index
13
while start < end and targetStr[start] == targetStr[end]:
14
start += 1
15
end -= 1
16
if start >= end:
17
# 说明是一个回文
18
temp = end_index - start_index
19
if temp > last_len:
20
last_len = temp
21
index = [start_index, end_index + 1]
22
return targetStr[index[0]: index[1]]

2. 使用动态规划法