煎饼排序
给定数组 A,我们可以对其进行煎饼翻转:我们选择一些正整数 k <= A.length,然后反转 A 的前 k 个元素的顺序。我们要执行零次或多次煎饼翻转(按顺序一次接一次地进行)以完成对数组 A 的排序。
返回能使 A 排序的煎饼翻转操作所对应的 k 值序列。任何将数组排序且翻转次数在 10 * A.length 范围内的有效答案都将被判断为正确。
示例 1:
输入:[3,2,4,1]
输出:[4,2,4,3]
解释:
我们执行 4 次煎饼翻转,k 值分别为 4,2,4,和 3。
初始状态 A = [3, 2, 4, 1]
第一次翻转后 (k=4): A = [1, 4, 2, 3]
第二次翻转后 (k=2): A = [4, 1, 2, 3]
第三次翻转后 (k=4): A = [3, 2, 1, 4]
第四次翻转后 (k=3): A = [1, 2, 3, 4],此时已完成排序。
示例 2:
输入:[1,2,3]
输出:[]
解释:
输入已经排序,因此不需要翻转任何内容。
请注意,其他可能的答案,如[3,3],也将被接受。
提示:
1 <= A.length <= 100
A[i] 是 [1, 2, …, A.length] 的排列
class Solution(object):
def pancakeSort(self, A):
"""
:type A: List[int]
:rtype: List[int]
"""
def tranv(A,index):#反转前k个数
k=[]
for i in range(index+1):
k.append(A[i])
for i in range(index+1):
A[i]=k.pop()
return None
n=len(A)
ans=[]
for i in range(n):
if n>1:
index,_=max(enumerate(A[:n]),key=lambda x:x[1])#找到前n个数的最大的索引
if index >0:
tranv(A,index)#前index+1个数反转
ans.append(index+1)
tranv(A,n-1)
ans.append(n)
n-=1
else:break
return ans
复制带随机指针的链表
给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。
要求返回这个链表的 深拷贝。
我们用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:
val:一个表示 Node.val 的整数。
random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为 null 。
示例 1:
输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
示例 2:
输入:head = [[1,1],[2,1]]
输出:[[1,1],[2,1]]
示例 3:
输入:head = [[3,null],[3,0],[3,null]]
输出:[[3,null],[3,0],[3,null]]
示例 4:
输入:head = []
输出:[]
解释:给定的链表为空(空指针),因此返回 null。
提示:
-10000 <= Node.val <= 10000
Node.random 为空(null)或指向链表中的节点。
节点数目不超过 1000 。
"""
# Definition for a Node.
class Node:
def __init__(self, x, next=None, random=None):
self.val = int(x)
self.next = next
self.random = random
"""
class Solution(object):
def copyRandomList(self, head):
"""
:type head: Node
:rtype: Node
"""
p = head
result = None
pre = None
D = {}
while p != None:
node = Node(p.val, random=p.random)
D[p] = node
if pre != None:
pre.next = node
else:
result = node
pre = node
p = p.next
q = result
while q != None:
if q.random != None:
q.random = D[q.random]
q = q.next
#print(result.val)
return result
最长重复子数组
给两个整数数组 A 和 B ,返回两个数组中公共的、长度最长的子数组的长度。
示例 1:
输入:
A: [1,2,3,2,1]
B: [3,2,1,4,7]
输出: 3
解释:
长度最长的公共子数组是 [3, 2, 1]。
说明:
1 <= len(A), len(B) <= 1000
0 <= A[i], B[i] < 100
class Solution(object):
def findLength(self, A, B):
"""
:type A: List[int]
:type B: List[int]
:rtype: int
"""
m = len(A)
n = len(B)
if m>n:
m, n, A, B = n, m, B, A
tmp_list = []
result = 0
# 利用','.join([str(i) for i in B])将B转化为字符串且任意两个元素之间用逗号间隔
str_B = ',' + ','.join([str(i) for i in B]) + ','
#print(type(str_B))
for r in A:
tmp_list.append(str(r))
if ',' + ','.join(tmp_list) + ',' in str_B:
result = max(result, len(tmp_list))
else:
tmp_list = tmp_list[1:]
return result
数组中的第k个最大元素
在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
示例 1:
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
示例 2:
输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
说明:
你可以假设 k 总是有效的,且 1 ≤ k ≤ 数组的长度。
import heapq
class Solution(object):
def findKthLargest(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: int
"""
result = heapq.nlargest(k,nums)
return result[-1]
无重复字符的最长子串
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
class Solution(object):
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
'''
if s == '':
return 0
s_list = list(s)
Subs = []
for j in range(1, len(s_list)+1):
for i in range(j-1):
Subs.append(s_list[i:j])
#print(Subs)
Lengest = 1
for i in range(len(Subs)):
L = len(set(Subs[i]))
#print(L)
if L == len(Subs[i]) and L > Lengest:
#print(Subs[i])
Lengest = L
return Lengest
'''
# 哈希集合,记录每个字符是否出现过
occ = set()
n = len(s)
# 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
rk, ans = 0, 0
for i in range(n):
if i != 0:
# 左指针向右移动一格,移除一个字符
occ.remove(s[i - 1])
while rk < n and s[rk] not in occ:
# 不断地移动右指针
occ.add(s[rk])
rk += 1
# 第 i 到 rk 个字符是一个极长的无重复字符子串
ans = max(ans, rk - i)
return ans
两个字符串的删除操作
给定两个单词 word1 和 word2,找到使得 word1 和 word2 相同所需的最小步数,每步可以删除任意一个字符串中的一个字符。
示例:
输入: “sea”, “eat”
输出: 2
解释: 第一步将"sea"变为"ea",第二步将"eat"变为"ea"
提示:
给定单词的长度不超过500。
给定单词中的字符只含有小写字母。
class Solution(object):
def minDistance(self, word1, word2):
"""
:type word1: str
:type word2: str
:rtype: int
"""
n, m = len(word1),len(word2)
dp = [[0 for _ in range(m+1)] for _ in range(n+1)]
for i in range(1,n+1):
for j in range(1,m+1):
if word1[i-1] == word2[j-1]:
dp[i][j] = dp[i-1][j-1] + 1
else:
dp[i][j] = max(dp[i-1][j],dp[i][j-1])# 最终回溯到dp[k][k],故跳过k到i和k到j之间的不等部分。
return n+m-2*dp[n][m]
破坏回文字符串
给你一个回文字符串 palindrome ,请你将其中 一个 字符用任意小写英文字母替换,使得结果字符串的字典序最小,且 不是 回文串。
请你返回结果字符串。如果无法做到,则返回一个空串。
示例 1:
输入:palindrome = “abccba”
输出:“aaccba”
示例 2:
输入:palindrome = “a”
输出:""
提示:
1 <= palindrome.length <= 1000
palindrome 只包含小写英文字母。
class Solution(object):
def symmetry(self, L, Len):
if Len == 0:
return False
i = 0
j = Len-1
if Len % 2 == 0:
while i < j:
if L[i] != L[j]:
break
i += 1
j -= 1
else:
while i <= j:
if L[i] != L[j]:
break
i += 1
j -= 1
if i > j:
return True
else: return False
def breakPalindrome(self, palindrome):
"""
:type palindrome: str
:rtype: str
"""
n = len(palindrome)
if n == 1:
return ''
for i in range(n):
if palindrome[i] > 'a':
sub = palindrome[:i]+'a'+palindrome[i+1:]
if not self.symmetry(sub, n):
return sub
if self.symmetry(palindrome, n):
palindrome = palindrome[:-1]+'b'
return palindrome
有效的括号字符串
给定一个只包含三种字符的字符串:( ,) 和 *,写一个函数来检验这个字符串是否为有效字符串。有效字符串具有如下规则:
任何左括号 ( 必须有相应的右括号 )。
任何右括号 ) 必须有相应的左括号 ( 。
左括号 ( 必须在对应的右括号之前 )。
- 可以被视为单个右括号 ) ,或单个左括号 ( ,或一个空字符串。
一个空字符串也被视为有效字符串。
示例 1:
输入: “()”
输出: True
示例 2:
输入: “(*)”
输出: True
示例 3:
输入: “(*))”
输出: True
注意:
字符串大小将在 [1,100] 范围内。
class Solution(object):
def checkValidString(self, s):
"""
:type s: str
:rtype: bool
"""
stack = [] #第一个栈用来记录所有(的index
star_stack = [] #第二个栈用来记录所有*的index
for i in range(len(s)):
if s[i] == "(":
stack.append(i)
elif s[i] == "*":
star_stack.append(i)
else:
if not stack and not star_stack:
return False #如果直接读到),直接return False
if stack:
stack.pop()
elif star_stack:
star_stack.pop() #先用(来消),再用*来消)
while stack and star_stack:
if stack.pop() > star_stack.pop(): #在所有)都处理了之后,只需要考虑(和*的index,此时的*全部当作)来考虑,比较index即可
return False
return not stack #判断是否有多余的(
最大加号标志
(第一次写思路,有错误望指正)
动态规划:
因为判断是否形成加法标志需要上下左右四个臂的长度,所以不能单一的变量表示不了这个状态;
我这边是用四个变量表示状态,分别为上下左右的连续臂展长度;
上左状态变量在正序遍历数组的时候确定,下右状态变量用逆序遍历数组的时候确定;
状态转移方程为 dp[i+1][j+1][0]=dp[i][j+1][0]+1 (上状态);
最后加法的阶数为四个状态中最下的那个确定;
class Solution:
def orderOfLargestPlusSign(self, N: int, mines: List[List[int]]) -> int:
l=[[1 for _ in range(N)] for _ in range(N)]
for i in mines:
l[i[0]][i[1]]=0
# 四个位置分别记录上左下右四个方向上的1的个数,且dp[i+1][j+1]记录的是以[i,j]为中心点。
dp = [[[0,0,0,0] for _ in range(N+2)] for _ in range(N+2)]
for i in range(N): #先正向遍历
for j in range(N):
if l[i][j]==0:
continue
else:
dp[i+1][j+1][0]=dp[i][j+1][0]+1
dp[i+1][j+1][1]=dp[i+1][j][1]+1
for i in range(N-1,-1,-1):
for j in range(N-1,-1,-1): #逆向遍历
if l[i][j]==0:
continue
else:
dp[i + 1][j + 1][2] = dp[i+2][j + 1][2]+1
dp[i + 1][j + 1][3] = dp[i + 1][j+2][3]+1
m=max([max([min(i) for i in j]) for j in dp])
return m
三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
class Solution(object):
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
n=len(nums)
res=[]
if(not nums or n<3):
return []
nums.sort()
res=[]
for i in range(n):
if(nums[i]>0):
return res
if(i>0 and nums[i]==nums[i-1]):
continue
L=i+1
R=n-1
while(L<R):
if(nums[i]+nums[L]+nums[R]==0):
res.append([nums[i],nums[L],nums[R]])
while(L<R and nums[L]==nums[L+1]):
L=L+1
while(L<R and nums[R]==nums[R-1]):
R=R-1
L=L+1
R=R-1
elif(nums[i]+nums[L]+nums[R]>0):
R=R-1
else:
L=L+1
return res
分数排名
编写一个 SQL 查询来实现分数排名。
如果两个分数相同,则两个分数排名(Rank)相同。请注意,平分后的下一个名次应该是下一个连续的整数值。换句话说,名次之间不应该有“间隔”。
±—±------+
| Id | Score |
±—±------+
| 1 | 3.50 |
| 2 | 3.65 |
| 3 | 4.00 |
| 4 | 3.85 |
| 5 | 4.00 |
| 6 | 3.65 |
±—±------+
例如,根据上述给定的 Scores 表,你的查询应该返回(按分数从高到低排列):
±------±-----+
| Score | Rank |
±------±-----+
| 4.00 | 1 |
| 4.00 | 1 |
| 3.85 | 2 |
| 3.65 | 3 |
| 3.65 | 3 |
| 3.50 | 4 |
±------±-----+
select Score, dense_rank() over (order by Score desc) as `Rank`
from Scores;
字母位移
有一个由小写字母组成的字符串 S,和一个整数数组 shifts。
我们将字母表中的下一个字母称为原字母的 移位(由于字母表是环绕的, ‘z’ 将会变成 ‘a’)。
例如·,shift(‘a’) = ‘b’, shift(‘t’) = ‘u’,, 以及 shift(‘z’) = ‘a’。
对于每个 shifts[i] = x , 我们会将 S 中的前 i+1 个字母移位 x 次。
返回将所有这些移位都应用到 S 后最终得到的字符串。
示例:
输入:S = “abc”, shifts = [3,5,9]
输出:“rpl”
解释:
我们以 “abc” 开始。
将 S 中的第 1 个字母移位 3 次后,我们得到 “dbc”。
再将 S 中的前 2 个字母移位 5 次后,我们得到 “igc”。
最后将 S 中的这 3 个字母移位 9 次后,我们得到答案 “rpl”。
提示:
1 <= S.length = shifts.length <= 20000
0 <= shifts[i] <= 10 ^ 9
import numpy as np
class Solution(object):
def shiftingLetters(self, S, shifts):
"""
:type S: str
:type shifts: List[int]
:rtype: str
"""
'''动态规划法
m = len(S)
n = len(shifts)
if n > m:
return
S = list(S)
# dp[j][i] 表示第j次位移后S[i]的值
dp = [[S[i] for i in range(n)] for j in range(n)]
for j in range(1, n+1):
for i in range(j):
if j == 1:
dp[j-1][i] = chr((ord(dp[j-1][i]) - 97 + shifts[j-1]) % 26 + 97)
#print(dp[0])
else:
dp[j-1][i] = chr((ord(dp[j-2][i]) - 97 + shifts[j-1]) % 26 + 97)
if m == n:
return ''.join(dp[-1][:])
return ''.join(dp[-1][:]) + ''.join(S[n+1])
'''
res=[]
total=sum(shifts)
for i in range(len(S)):
x=(ord(S[i])+total-97)%26+97
res.append(chr(x))
total-=shifts[i]
return ''.join(res)
索引处的解码字符串
给定一个编码字符串 S。为了找出解码字符串并将其写入磁带,从编码字符串中每次读取一个字符,并采取以下步骤:
如果所读的字符是字母,则将该字母写在磁带上。
如果所读的字符是数字(例如 d),则整个当前磁带总共会被重复写 d-1 次。
现在,对于给定的编码字符串 S 和索引 K,查找并返回解码字符串中的第 K 个字母。
示例 1:
输入:S = “leet2code3”, K = 10
输出:“o”
解释:
解码后的字符串为 “leetleetcodeleetleetcodeleetleetcode”。
字符串中的第 10 个字母是 “o”。
示例 2:
输入:S = “ha22”, K = 5
输出:“h”
解释:
解码后的字符串为 “hahahaha”。第 5 个字母是 “h”。
示例 3:
输入:S = “a2345678999999999999999”, K = 1
输出:“a”
解释:
解码后的字符串为 “a” 重复 8301530446056247680 次。第 1 个字母是 “a”。
提示:
2 <= S.length <= 100
S 只包含小写字母与数字 2 到 9 。
S 以字母开头。
1 <= K <= 10^9
解码后的字符串保证少于 2^63 个字母。
思路:
如果S=‘vk3u5xhq2v’, 这种时候需要找到每一个操作的原始和解码字符索引, 记为(x, y), x表示decode(S)中的位置(从1开始计算), y表示S中的位置(从0开始计算)
最终的字符为’vkuxhqv’
索引记录表对应的就是[v(1,0), k(2,1), 3(6,1), u(7,2), 5(35,2), x(36,3), h(37,4), q(38,5), 2(76,5), v(77,6)]
可以根据K值提前结束索引记录过程, 比如假设K=50, 上面的S在2(76,5)的时候由于76>=50, 跳出循环
然后开始逆向找K对应的应该是原始S中的哪个字符, 类似于进制转换, 把K与每个decode(S)取余, 结果为0的时候说明答案就是对应操作中的原始位置.
class Solution(object):
def decodeAtIndex(self, S, K):
"""
:type S: str
:type K: int
:rtype: str
"""
i = cnt = 0
c = ''
x = []# x[i] = [解码后的位置(从1开始),解码前的位置(从0开始)]
while i < len(S) and cnt < K:
if S[i].isdigit():# S[i]是数字
cnt *= int(S[i])
if S[i-1].isdigit(): x[-1][0] = cnt# 若上一个也是数字,则修改上一次的解码后位置。
else: x.append([cnt, len(c) - 1])# 若上一个不是数字,则记录这次的位置。即上一个字母经过复制在当下的位置。
else:# S[i]是字母
c += S[i]
cnt += 1
x.append([cnt, len(c) - 1])
i += 1
ret = ''
while x:
t = x.pop()
K %= t[0]
if K == 0:
ret = c[t[1]]
break
return ret
模式匹配
你有两个字符串,即pattern和value。 pattern字符串由字母"a"和"b"组成,用于描述字符串中的模式。例如,字符串"catcatgocatgo"匹配模式"aabab"(其中"cat"是"a",“go"是"b”),该字符串也匹配像"a"、"ab"和"b"这样的模式。但需注意"a"和"b"不能同时表示相同的字符串。编写一个方法判断value字符串是否匹配pattern字符串。
示例 1:
输入: pattern = “abba”, value = “dogcatcatdog”
输出: true
示例 2:
输入: pattern = “abba”, value = “dogcatcatfish”
输出: false
示例 3:
输入: pattern = “aaaa”, value = “dogcatcatdog”
输出: false
示例 4:
输入: pattern = “abba”, value = “dogdogdogdog”
输出: true
解释: “a”=“dogdog”,b="",反之也符合规则
提示:
0 <= len(pattern) <= 1000
0 <= len(value) <= 1000
你可以假设pattern只包含字母"a"和"b",value仅包含小写字母。
思路:
存在以下几种情况:
1)pattern和value都为空,返回True
2)仅pattern为空: 返回False
3)仅value为空,这时候分两种情况:如果pattern中只存在a或者b一种,那么令a或者b为空,符合条件,返回True;否则,由于不同pattern不能表示同一字符串,返回False
4)都不为空的情况下:如果pattern中只有a或者b一种,那意味这value全由一种字符串构成。它需满足以下两个条件:首先,value的长度需要被pattern的长度整除;其次,value中的每一个字符串需要都是a,根据value长度和pattern长度很容易计算出a的长度,然后顺次取出看是否相同即可。
5)如果pattern中同时含有a和b,也有两种情况。第一种,其中一个pattern只有一个,假设a只有一个,那么只需要令a= value, b=“”,即可,所以直接返回True
6)另一种情况,同时含有a, b且个数都大于1。这时候,我们先统计pattern中a, b各有多少个,然后遍历a的所有可能长度,对于某个确定的a的长度,结合a的个数和b的个数以及value的长度,我们即可以确定b的长度。这时候需要满足以下条件:首先b的长度也必须是整数;其次,value只能由a, b结合而成。
class Solution(object):
def patternMatching(self, pattern, value):
"""
:type pattern: str
:type value: str
:rtype: bool
"""
if not value and not pattern: return True
if not pattern: return False
if not value:
if len(pattern)==1: return True
else: return False
if len(set(pattern))==1: # 如果只有一种pattern
if len(value)%len(pattern)!=0:
return False
length = len(value) // len(pattern)
return all([value[i:i+length]==value[0:length] for i in range(0, len(value),length)])
if pattern.count('a')==1 or pattern.count('b')==1:# 两种pattern但是其中一种只有一个,只需要另这个为value,另外一个为空即可
return True
cnt_a = pattern.count('a')
cnt_b = pattern.count('b')
for i in range(len(value)//cnt_a): # 遍历a的所有可能长度
remain = len(value) - i * cnt_a
if remain % cnt_b != 0:
continue
j = remain // cnt_b
set_a = set()
set_b = set()
p = 0
for s in pattern:
if s=='a':
set_a.add(value[p:p+i])
p += i
else:
set_b.add(value[p:p+j])
p += j
if len(set_a)==len(set_b)==1:
return True
return False
二倍数对数组
给定一个长度为偶数的整数数组 A,只有对 A 进行重组后可以满足 “对于每个 0 <= i < len(A) / 2,都有 A[2 * i + 1] = 2 * A[2 * i]” 时,返回 true;否则,返回 false。
示例 1:
输入:[3,1,3,6]
输出:false
示例 2:
输入:[2,1,2,6]
输出:false
示例 3:
输入:[4,-2,2,-4]
输出:true
解释:我们可以用 [-2,-4] 和 [2,4] 这两组组成 [-2,-4,2,4] 或是 [2,4,-2,-4]
示例 4:
输入:[1,2,4,16,8,4]
输出:false
提示:
0 <= A.length <= 30000
A.length 为偶数
-100000 <= A[i] <= 100000
from collections import Counter
class Solution(object):
def canReorderDoubled(self, A):
"""
:type A: List[int]
:rtype: bool
"""
count = Counter(A)#对于不存在的key,count[key]=0
# 注意是对count进行排序
# sorted(count, key=abs)等价于sorted(count.keys(),key = abs)
for i in sorted(count, key=abs):
if count[i] > count[i*2]:
return False
count[i*2] -= count[i]
return True
链表随机节点
给定一个单链表,随机选择链表的一个节点,并返回相应的节点值。保证每个节点被选的概率一样。
进阶:
如果链表十分大且长度未知,如何解决这个问题?你能否使用常数级空间复杂度实现?
示例:
// 初始化一个单链表 [1,2,3].
ListNode head = new ListNode(1);
head.next = new ListNode(2);
head.next.next = new ListNode(3);
Solution solution = new Solution(head);
// getRandom()方法应随机返回1,2,3中的一个,保证每个元素被返回的概率相等。
solution.getRandom();
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
import random
class Solution(object):
def __init__(self, head):
"""
@param head The linked list's head.
Note that the head is guaranteed to be not null, so it contains at least one node.
:type head: ListNode
"""
assert head is not None
self.head = head
self.length = self.Length()
def Length(self):
p = self.head
L = 0
while p != None:
L += 1
p = p.next
return L
def getRandom(self):
"""
Returns a random node's value.
:rtype: int
"""
k = random.randint(0, self.length-1)
#print(k)
p = self.head
L = 0
while p != None:
if L == k:
break
L += 1
p = p.next
if p == None:
print(k, self.length)
return p.val
# Your Solution object will be instantiated and called as such:
# obj = Solution(head)
# param_1 = obj.getRandom()
有序数组中的单一元素
给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。
示例 1:
输入: [1,1,2,3,3,4,4,8,8]
输出: 2
示例 2:
输入: [3,3,7,7,10,11,11]
输出: 10
注意: 您的方案应该在 O(log n)时间复杂度和 O(1)空间复杂度中运行。
import collections
class Solution(object):
def singleNonDuplicate(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
count = collections.Counter(nums)
result = 0
for key, value in count.items():
if value == 1:
result = key
return result
等差数列划分
如果一个数列至少有三个元素,并且任意两个相邻元素之差相同,则称该数列为等差数列。
例如,以下数列为等差数列:
1, 3, 5, 7, 9
7, 7, 7, 7
3, -1, -5, -9
以下数列不是等差数列。
1, 1, 2, 5, 7
数组 A 包含 N 个数,且索引从0开始。数组 A 的一个子数组划分为数组 (P, Q),P 与 Q 是整数且满足 0<=P<Q<N 。
如果满足以下条件,则称子数组(P, Q)为等差数组:
元素 A[P], A[p + 1], …, A[Q - 1], A[Q] 是等差的。并且 P + 1 < Q 。
函数要返回数组 A 中所有为等差数组的子数组个数。
示例:
A = [1, 2, 3, 4]
返回: 3, A 中有三个子等差数组: [1, 2, 3], [2, 3, 4] 以及自身 [1, 2, 3, 4]。
思路:
首先遍历原数组 nums,用数组 diffs 存储相邻两个元素之间的差值。
然后遍历 diffs,用数组 cons 存储其中连续相同的差值的数目,比如有 33 个 33 连在一起,意味着原数组中这个位置存在一个最大长度为 44 的等差数列。
然后遍历 cons,对于长度为 n 的等差数列,其所有的长度大于等于 33 的子数列都是等差数列,则一共有 (n-2)(n-1)/2 个等差数列。
全部相加得到结果。
比如:
nums = [1,2,3,4,5,6,12,14,16]
diffs = [1,1,1,1,1,6,2,2]
cons = [5,1,2]
将 1 舍去,nums 中有长度为 5+1 和 2+1 的等差数列
result = (6-2)(6-1)/2 + (3-2)(3-1)/2
class Solution(object):
def numberOfArithmeticSlices(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
# 第一次遍历
diffs = []
for i in range(len(nums) - 1):
diffs.append(nums[i + 1] - nums[i])
# 第二次遍历
cons = []
a = 1
for i in range(1, len(diffs)):
if diffs[i] == diffs[i - 1]:
a += 1
else:
cons.append(a)
a = 1
cons.append(a)
# 第三次遍历
res = 0
for num in cons:
res += int(self.calc(num))
return res
# 用于计算cons内每个数代表的等差数列个数
def calc(self, n):
if n == 1:
return 0
n += 1
return (n-2)*(n-1)/2
最长回文子串(连续子串)
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: “babad”
输出: “bab”
注意: “aba” 也是一个有效答案。
示例 2:
输入: “cbbd”
输出: “bb”
class Solution(object):
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
n = len(s)
dp = [[False for j in range(n)] for i in range(n)]
begin_idx = 0
MaxL = 0
for j in range(n):
for i in range(j+1):
L = j - i + 1
if s[i] == s[j]:
if L <= 3:
dp[i][j] = True
else:
dp[i][j] = dp[i+1][j-1]
if dp[i][j] and L > MaxL:
#print(i, j)
begin_idx = i
MaxL = L
#print(dp)
return s[begin_idx:begin_idx+MaxL]
最长回文子串(不连续)
给定一个字符串s,找到其中最长的回文子序列,并返回该序列的长度。可以假设s的最大长度为1000。
示例 1:
输入:
“bbbab”
输出:
4
一个可能的最长回文子序列为 “bbbb”。
示例 2:
输入:
“cbbd”
输出:
2
一个可能的最长回文子序列为 “bb”。
class Solution(object):
def longestPalindromeSubseq(self, s):
"""
:type s: str
:rtype: int
"""
# 使用dp思想
n = len(s)
dp = [[0]*n for i in range(n)]
# 注意basecase
for i in range(n):
dp[i][i] = 1
# 倒着遍历
for i in range(n-2,-1,-1):
for j in range(i+1,n):
if s[i] == s[j]:
dp[i][j] = dp[i+1][j-1] + 2
else:
dp[i][j] = max(dp[i+1][j],dp[i][j-1])
return dp[0][n-1]
搜索旋转排序数组
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
你可以假设数组中不存在重复的元素。
你的算法时间复杂度必须是 O(log n) 级别。
示例 1:
输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4
示例 2:
输入: nums = [4,5,6,7,0,1,2], target = 3
输出: -1
思路:分成两段,分别进行二分查找,平均时间复杂度O(logn)
各种查找和排序算法的时间和空间复杂度
class Solution(object):
def BinarySearch(self, nums, start, end, target, n):
if end < 0 or start > n-1 or (start == end and nums[start] != target) or end < start:
return -1
mid = (start + end) // 2
if nums[mid] == target:
return mid
elif nums[mid] > target:
return self.BinarySearch(nums, start, mid-1, target, n)
else:
return self.BinarySearch(nums, mid+1, end, target, n)
def search(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
boundary = 0
n = len(nums)
for i in range(n-1):
if nums[i] > nums[i+1]:
boundary = i+1
answer1 = self.BinarySearch(nums, 0, boundary, target, n)
answer2 = self.BinarySearch(nums, boundary, n, target, n)
if answer1 == answer2 == -1:
return -1
elif answer1 != -1:
return answer1
else:
return answer2
分隔数组以得到最大和
给出整数数组 A,将该数组分隔为长度最多为 K 的几个(连续)子数组。分隔完成后,每个子数组的中的值都会变为该子数组中的最大值。
返回给定数组完成分隔后的最大和。
示例:
输入:A = [1,15,7,9,2,5,10], K = 3
输出:84
解释:A 变为 [15,15,15,9,10,10,10]
提示:
1 <= K <= A.length <= 500
0 <= A[i] <= 10^6
思路:动态规划
class Solution(object):
def maxSumAfterPartitioning(self, A, K):
"""
:type A: List[int]
:type K: int
:rtype: int
"""
n=len(A)
res=[0]*(n+1)
for i in range(1,n+1) :
j=i-1
mx=float("-inf")
while i-j <= K and j >= 0 :
#由于不知道lastSubA的大小,因此对lastSubA的所有可能都进行计算,取最大的
mx=max(mx,A[j])
res[i]=max(res[i],res[j]+mx*(i-j))
j-=1
return res[n]
摆动排序
给你一个无序的数组 nums, 将该数字 原地 重排后使得 nums[0] <= nums[1] >= nums[2] <= nums[3]…。
示例:
输入: nums = [3,5,2,1,6,4]
输出: 一个可能的解答是 [3,5,1,6,2,4]
from heapq import *
class Solution(object):
def wiggleSort(self, nums):
"""
:type nums: List[int]
:rtype: None Do not return anything, modify nums in-place instead.
"""
n = len(nums)
if n <= 1:
return nums
a = n // 2
new_nums = zip(nums, [i for i in range(n)])
lar = nlargest(a, new_nums)
laridx = [t[1] for t in lar]
#print(lar)
smal = [nums[t] for t in range(n) if t not in laridx]
lar = [nums[m] for m in laridx]
nums[1:n:2] = lar
nums[0:n:2] = smal
return nums
排序链表
在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。
示例 1:
输入: 4->2->1->3
输出: 1->2->3->4
示例 2:
输入: -1->5->3->4->0
输出: -1->0->3->4->5
思路:
解答一:归并排序(递归法)
题目要求时间空间复杂度分别为O(nlogn)O(nlogn)和O(1)O(1),根据时间复杂度我们自然想到二分法,从而联想到归并排序;
对数组做归并排序的空间复杂度为 O(n)O(n),分别由新开辟数组O(n)O(n)和递归函数调用O(logn)O(logn)组成,而根据链表特性:
数组额外空间:链表可以通过修改引用来更改节点顺序,无需像数组一样开辟额外空间;
递归额外空间:递归调用函数将带来O(logn)O(logn)的空间复杂度,因此若希望达到O(1)O(1)空间复杂度,则不能使用递归。
通过递归实现链表归并排序,有以下两个环节:
分割 cut 环节: 找到当前链表中点,并从中点将链表断开(以便在下次递归 cut 时,链表片段拥有正确边界);
我们使用 fast,slow 快慢双指针法,奇数个节点找到中点,偶数个节点找到中心左边的节点。
找到中点 slow 后,执行 slow.next = None 将链表切断。
递归分割时,输入当前链表左端点 head 和中心节点 slow 的下一个节点 tmp(因为链表是从 slow 切断的)。
cut 递归终止条件: 当head.next == None时,说明只有一个节点了,直接返回此节点。
合并 merge 环节: 将两个排序链表合并,转化为一个排序链表。
双指针法合并,建立辅助ListNode h 作为头部。
设置两指针 left, right 分别指向两链表头部,比较两指针处节点值大小,由小到大加入合并链表头部,指针交替前进,直至添加完两个链表。
返回辅助ListNode h 作为头部的下个节点 h.next。
时间复杂度 O(l + r),l, r 分别代表两个链表长度。
当题目输入的 head == None 时,直接返回None。
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def sortList(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
if not head or not head.next: return head # termination.
# cut the LinkedList at the mid index.
slow, fast = head, head.next
while fast and fast.next:
fast, slow = fast.next.next, slow.next
mid, slow.next = slow.next, None # save and cut.
# recursive for cutting.
left, right = self.sortList(head), self.sortList(mid)
# merge `left` and `right` linked list and return it.
h = res = ListNode(0)
while left and right:
if left.val < right.val: h.next, left = left, left.next
else: h.next, right = right, right.next
h = h.next
h.next = left if left else right
return res.next
岛屿的最大面积
给定一个包含了一些 0 和 1 的非空二维数组 grid 。
一个 岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在水平或者竖直方向上相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。
找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为 0 。)
示例 1:
[[0,0,1,0,0,0,0,1,0,0,0,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,1,1,0,1,0,0,0,0,0,0,0,0],
[0,1,0,0,1,1,0,0,1,0,1,0,0],
[0,1,0,0,1,1,0,0,1,1,1,0,0],
[0,0,0,0,0,0,0,0,0,0,1,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,0,0,0,0,0,0,1,1,0,0,0,0]]
对于上面这个给定矩阵应返回 6。注意答案不应该是 11 ,因为岛屿只能包含水平或垂直的四个方向的 1 。
示例 2:
[[0,0,0,0,0,0,0,0]]
对于上面这个给定的矩阵, 返回 0。
注意: 给定的矩阵grid 的长度和宽度都不超过 50。
class Solution(object):
def maxAreaOfIsland(self, grid):
"""
:type grid: List[List[int]]
:rtype: int
"""
m = len(grid)
if m == 0: return 0
n = len(grid[0])
ans = 0
def dfs(i, j):
if i < 0 or i >= m or j < 0 or j >= n: return 0
if grid[i][j] == 0: return 0
grid[i][j] = 0
top = dfs(i + 1, j)
bottom = dfs(i - 1, j)
left = dfs(i, j - 1)
right = dfs(i, j + 1)
return 1 + sum([top, bottom, left, right])
for i in range(m):
for j in range(n):
ans = max(ans, dfs(i, j))
return ans
合并区间
给出一个区间的集合,请合并所有重叠的区间。
示例 1:
输入: [[1,3],[2,6],[8,10],[15,18]]
输出: [[1,6],[8,10],[15,18]]
解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
示例 2:
输入: [[1,4],[4,5]]
输出: [[1,5]]
解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间。
class Solution(object):
def merge(self, intervals):
"""
:type intervals: List[List[int]]
:rtype: List[List[int]]
"""
intervals.sort()
m = len(intervals)
i = 0
while i < m-1:
if intervals[i][1] >= intervals[i+1][0]:
# 合并区间
intervals[i][1] = max(intervals[i][1], intervals[i+1][1])
del intervals[i+1]
m -= 1
else:
i += 1
return intervals
整数替换
给定一个正整数 n,你可以做如下操作:
- 如果 n 是偶数,则用 n / 2替换 n。
- 如果 n 是奇数,则可以用 n + 1或n - 1替换 n。
n 变为 1 所需的最小替换次数是多少?
示例 1:
输入:
8
输出:
3
解释:
8 -> 4 -> 2 -> 1
示例 2:
输入:
7
输出:
4
解释:
7 -> 8 -> 4 -> 2 -> 1
或
7 -> 6 -> 3 -> 2 -> 1
思路:每遇偶数除以2,没什么好说。
遇到奇数,应变换使其成为4的倍数,目的是使该数下一步变成能连续两次除以2的偶数
因此mod41时减1,mod43时加1
有个例外是3, 3——2——1 比 3——4——2——1更快
class Solution(object):
def integerReplacement(self, n):
"""
:type n: int
:rtype: int
"""
count = 0
while n>1:
if n==3:
n-=1
elif n%4==1:
n-=1
elif n%4==3:
n+=1
else:
n/=2
count+=1
return count
分割数组
给定一个数组 A,将其划分为两个不相交(没有公共元素)的连续子数组 left 和 right, 使得:
left 中的每个元素都小于或等于 right 中的每个元素。
left 和 right 都是非空的。
left 要尽可能小。
在完成这样的分组后返回 left 的长度。可以保证存在这样的划分方法。
示例 1:
输入:[5,0,3,8,6]
输出:3
解释:left = [5,0,3],right = [8,6]
示例 2:
输入:[1,1,1,0,6,12]
输出:4
解释:left = [1,1,1,0],right = [6,12]
提示:
2 <= A.length <= 30000
0 <= A[i] <= 10^6
可以保证至少有一种方法能够按题目所描述的那样对 A 进行划分。
class Solution(object):
def partitionDisjoint(self, A):
"""
:type A: List[int]
:rtype: int
"""
tmp,pos,m = A[0],0,A[0]
for i in range(1,len(A)):
m = max(m,A[i])
if A[i]<tmp:
pos = i
tmp = m
return pos+1
从英文中重建数字
给定一个非空字符串,其中包含字母顺序打乱的英文单词表示的数字0-9。按升序输出原始的数字。
注意:
输入只包含小写英文字母。
输入保证合法并可以转换为原始的数字,这意味着像 “abc” 或 “zerone” 的输入是不允许的。
输入字符串的长度小于 50,000。
示例 1:
输入: “owoztneoer”
输出: “012” (zeroonetwo)
示例 2:
输入: “fviefuro”
输出: “45” (fourfive)
class Solution(object):
def originalDigits(self, s):
"""
:type s: str
:rtype: str
"""
res = {}
for i in s:
if res.get(i):
res[i] += 1
else:
res[i] = 1
if res.get('z'): #0 : z
res['r'] -= res['z']
res['o'] -= res['z']
if res.get('w'): #2 : w
res['t'] -= res['w']
res['o'] -= res['w']
if res.get('u'): #4 : u; 1 : o; 3 : r; 5 : f;
res['f'] -= res['u']
res['o'] -= res['u']
res['r'] -= res['u']
if res.get('o') and res['o'] > 0:
res['n'] -= res['o']
if res.get('f') and res['f'] > 0: #7 : v
res['v'] -= res['f']
if res.get('r') and res['r'] > 0: #8 : t
res['t'] -= res['r']
if res.get('v') and res['v'] > 0: #6 : s; 9 : n
res['s'] -= res['v']
res['n'] -= res['v']
num = ''
if res.get('z'):
num += '0'*res['z']
if res.get('o'):
num += '1'*res['o']
if res.get('w'):
num += '2'*res['w']
if res.get('r'):
num += '3'*res['r']
if res.get('u'):
num += '4'*res['u']
if res.get('f'):
num += '5'*res['f']
if res.get('s'):
num += '6'*res['s']
if res.get('v'):
num += '7'*res['v']
if res.get('t'):
num += '8'*res['t']
if res.get('n'):
num += '9'*(res['n']//2)
return num
克隆图
给你无向 连通 图中一个节点的引用,请你返回该图的 深拷贝(克隆)。
图中的每个节点都包含它的值 val(int) 和其邻居的列表(list[Node])。
class Node {
public int val;
public List neighbors;
}
测试用例格式:
简单起见,每个节点的值都和它的索引相同。例如,第一个节点值为 1(val = 1),第二个节点值为 2(val = 2),以此类推。该图在测试用例中使用邻接列表表示。
邻接列表 是用于表示有限图的无序列表的集合。每个列表都描述了图中节点的邻居集。
给定节点将始终是图中的第一个节点(值为 1)。你必须将 给定节点的拷贝 作为对克隆图的引用返回。
示例 1:
输入:adjList = [[2,4],[1,3],[2,4],[1,3]]
输出:[[2,4],[1,3],[2,4],[1,3]]
解释:
图中有 4 个节点。
节点 1 的值是 1,它有两个邻居:节点 2 和 4 。
节点 2 的值是 2,它有两个邻居:节点 1 和 3 。
节点 3 的值是 3,它有两个邻居:节点 2 和 4 。
节点 4 的值是 4,它有两个邻居:节点 1 和 3 。
示例 2:
输入:adjList = [[]]
输出:[[]]
解释:输入包含一个空列表。该图仅仅只有一个值为 1 的节点,它没有任何邻居。
示例 3:
输入:adjList = []
输出:[]
解释:这个图是空的,它不含任何节点。
示例 4:
输入:adjList = [[2],[1]]
输出:[[2],[1]]
提示:
节点数不超过 100 。
每个节点值 Node.val 都是唯一的,1 <= Node.val <= 100。
无向图是一个简单图,这意味着图中没有重复的边,也没有自环。
由于图是无向的,如果节点 p 是节点 q 的邻居,那么节点 q 也必须是节点 p 的邻居。
图是连通图,你可以从给定节点访问到所有节点。
"""
# Definition for a Node.
class Node(object):
def __init__(self, val = 0, neighbors = []):
self.val = val
self.neighbors = neighbors
"""
class Solution(object):
def cloneGraph(self, node):
"""
:type node: Node
:rtype: Node
"""
lookup = {}
def dfs(node):
#print(node.val)
if not node: return
if node in lookup:
return lookup[node]
clone = Node(node.val, [])
lookup[node] = clone
for n in node.neighbors:
clone.neighbors.append(dfs(n))
return clone
return dfs(node)
分割链表
给你一个链表和一个特定值 x ,请你对链表进行分隔,使得所有小于 x 的节点都出现在大于或等于 x 的节点之前。
你应当保留两个分区中每个节点的初始相对位置。
示例:
输入:head = 1->4->3->2->5->2, x = 3
输出:1->2->2->4->3->5
解一:将小于x的都交换到前面
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def partition(self, head: ListNode, x: int) -> ListNode:
first = True
head_flag = False
new_head = ListNode(x-1)
new_head.next = head
p = new_head
q = p.next
while q:
print(q.val)
change = False
if q.val >= x and first:
flag = p
first = False
if flag == new_head:
head_flag = True
elif q.val < x and not first:
change = True
now = p
p.next = q.next
q.next = flag.next
flag.next = q
flag = flag.next
if change:
q = now.next
p = now
else:
p = q
q = q.next
return new_head.next
解二:
用两个链表分别存储小于x和大于等于x的节点
class Solution {
public:
ListNode* partition(ListNode* head, int x) {
ListNode* small = new ListNode(0);
ListNode* smallHead = small;
ListNode* large = new ListNode(0);
ListNode* largeHead = large;
while (head != nullptr) {
if (head->val < x) {
small->next = head;
small = small->next;
} else {
large->next = head;
large = large->next;
}
head = head->next;
}
large->next = nullptr;
small->next = largeHead->next;
return smallHead->next;
}
};
两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
你可以按任意顺序返回答案。
示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
示例 2:
输入:nums = [3,2,4], target = 6
输出:[1,2]
示例 3:
输入:nums = [3,3], target = 6
输出:[0,1]
提示:
2 <= nums.length <= 103
-109 <= nums[i] <= 109
-109 <= target <= 109
只会存在一个有效答案
解法一:暴力穷举
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
n = len(nums)
#dp = [[0 for j in range(n)] for i in range(n)]
re = []
for i in range(n):
for j in range(n):
if i != j and nums[i] + nums[j] == target:
re = [i,j]
return re
``
解法二:Hash表
```python
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
hashtable = dict()
for i, num in enumerate(nums):
if target - num in hashtable:
return [hashtable[target - num], i]
hashtable[nums[i]] = i
return []