Part0 Basis
part1 sorting
1.插入排序
def insert_sort(lists):
count = len(lists)
for I in range(1,count):
key=lists[I]
j=i-1
while j>=0:
if lists[j]>key:
lists[j+1] = lists[j]
lists[j]=key
j -=1
else:
break
return lists
2.希尔排序
def shell_sort(lists):
count = len(lists)
step = 2
group = count/step
while group>0:
for I in range(0,group):
j = I + group
while j < count:
key= lists[j]
k = j - group
while k>=0:
if lists[k]>key:
lists[k+group] = lists[k]
lists[k] = key
k -= group
else:
break
j += group
group /=step
return lists
3 冒泡排序
描述
它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。
def bubble_sort(lists):
count = len(lists)
for I in range(0,count):
for j in range(I+1,count):
if lists[I]>lists[j]:
lists[I],lists[j]=lists[j],lists[I]
return lists
4 快速排序
描述
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
def quick_sort(array, left, right):
if left >= right:
return
low = left
high = right
key = array[low]
while left < right:
while left < right and array[right] > key:
right -= 1
array[left] = array[right]
while left < right and array[left] <= key:
left += 1
array[right] = array[left]
array[right] = key
quick_sort(array, low, left - 1)
quick_sort(array, left + 1, high)
Version 2
def Partition(data,start,end):
if data == None or start>end or start<0 or end>=len(data):
return None
key = data[start]
while(start<end):
while start<end and data[end] >= key:
end -= 1
if start<end:
data[start] = data[end]
while start<end and data[start]<= key:
start +=1
if start<end:
data[end] = data[start]
data[start]=key
return start # return the index of mid-number
def QuickSort(data,start,end):
if start==end:
return None
index = Partition(data,start,len(data)-1)
if index>start:
QuickSort(data,start,index-1)
if(index<end):
QuickSort(data,index+1,end)
if __name__=="__main__":
data = [2,3,5,5,1,1,4,6,8,0,9]
QuickSort(data,0,len(data)-1)
print data
5 选择排序
def select_sort(lists):
count = len(lists)
for i in range(0,count):
min_index = i
for j in range(i+1,count):
if lists[min_index]>lists[j]:
min_index=j
lists[min_index],lists[i]=lists[i],lists[min_index]
return lists
6 堆排序
描述
堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。堆分为大根堆和小根堆,是完全二叉树。大根堆的要求是每个节点的值都不大于其父节点的值,即A[PARENT[i]] >= A[i]。在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶。
def MAX_Heapify(heap,HeapSize,root): # 下滤
left = 2*root+1
right = left +1
larger = root
if left<HeapSize and heap[larger]<heap[left]:
larger=left
if right<HeapSize and heap[larger]<heap[right]:
larger=right
if larger!=root: #如果做了堆调整则larger的值等于左节点或者右节点的,这个时候做对调值操作
heap[larger],heap[root]=heap[root],heap[larger]
MAX_Heapify(heap,HeapSize,larger)
def Build_MAX_Heap(heap):#构造一个堆,将堆中所有数据重新排序
HeapSize = len(heap)
for i in range((HeapSize-2)/2,-1,-1): #从后往前出数
MAX_Heapify(heap,HeapSize,i)
def HeapSort(heap):
Build_MAX_Heap(heap)
for i in range(len(heap)-1,-1,-1): #将根节点取出与最后一位做对调,对前面len-1个节点继续进行对调整过程。
heap[0],heap[i] = heap[i],heap[0]
MAX_Heapify(heap,i,0)
return heap
7 归并排序
描述
归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
def merge(left,right):
i=j=0
result = []
while i <len(left) and j<len(right):
if left[i]<=right[j]:
result.append(left[i])
i+=1
else:
result.append(right[j])
j +=1
result += left[i:]
result += right[j:]
return result
def merge_sort(lists):
if len(lists)<=1:
return lists
num = len(lists)/2
left = merge_sort(lists[:num])
right = merge_sort(lists[num:])
return merge(left,right)
基数排序
#!/usr/bin/env python
#encoding=utf-8
import math
def sort(a, radix=10):
"""a为整数列表, radix为基数"""
K = int(math.ceil(math.log(max(a)+1, radix))) # 用K位数可表示任意整数
for i in range(1, K+1): # K次循环
bucket = [[] for i in range(radix)] # 不能用 [[]]*radix,否则相当于开了radix个完全相同的list对象
for val in a:
bucket[val%(radix**i)//(radix**(i-1))].append(val) # 獲得整數第K位數字 (從低到高)
del a[:]
for each in bucket:
a.extend(each) # 桶合并
数组中的逆序对(归并排序)
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007 输入描述: 题目保证输入的数组中没有的相同的数字
class Solution:
count=0
def InversePairs(self, data):
MergeSort(data)
return self.count%1000000007
def MergeSort(self,lists):
if len(lists) <= 1:
return lists
num = int( len(lists)/2 )
left = MergeSort(lists[:num])
right = MergeSort(lists[num:])
r, l=0, 0
result=[]
while l<len(left) and r<len(right):
if left[l] < right[r]:
result.append(left[l])
l += 1
else:
result.append(right[r])
r += 1
self.count += len(left)-l
result += right[r:]
result += left[l:]
return result
part2 DP题
数字翻转
给你一个01构成的数组。请你找出最小翻转步数,使得数组满足以下规则: 1的后面可以是1或者0,但是0的后面必须是0。
样例:
给出 array = [1,0,0,1,1,1] , 返回2。 解释: 把两个0翻转成1。
给出 array = [1,0,1,0,1,0] , 返回2。 解释: 把第二个1和第三个1都翻转成0。
class Solution:
"""
@param nums: the array
@return: the minimum times to flip digit
"""
def flipDigit(self, nums):
# Write your code here
dp = [[0, 0] for i in range(len(nums))]
dp[-1][1 - nums[-1]] = 1
for i in range(len(nums)-2,-1,-1):
if nums[i]==1: #dp[i][1] 如果该位变成1的话共需要翻转几次
#dp[i][0] 如果该位变成0的话共需要翻转几次
dp[i][1]=min(dp[i+1][0],dp[i+1][1])
dp[i][0]=dp[i+1][0]+1
else:
dp[i][1] = min(dp[i+1][0],dp[i+1][1])+1
dp[i][0] = dp[i+1][0]
return min(dp[0][0],dp[0][1])
丑数
把只包含因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
#动态规划,递推关系:k[i] = min(k[t2]*2, k[t3]*3, k[t5]*5)
class Solution:
def GetUglyNumber_Solution(self, index):
if index==0:
return 0
if index==1:
return 1
t2=t3=t5=0
numbers=[1]*index
for i in range(1,index):
numbers[i]=min(numbers[t2]*2,numbers[t3]*3,numbers[t5]*5)
if numbers[i] ==numbers[t2]*2:
t2 +=1
if numbers[i]==numbers[t3]*3:
t3 +=1
if numbers[i]==numbers[t5]*5:
t5 +=1
return numbers[index-1]
买卖股票的最佳时机 II (与最大子列和相似)
假设有一个数组,它的第i个元素是一个给定的股票在第i天的价格。设计一个算法来找到最大的利润。你可以完成尽可能多的交易(多次买卖股票)。然而,你不能同时参与多个交易(你必须在再次购买前出售股票)。
class Solution:
"""
@param prices: Given an integer array
@return: Maximum profit
"""
def maxProfit(self, prices):
# write your code here
total = 0
low, high = sys.maxint, sys.maxint
for x in prices:
if x > high: #当前价格如果还大于high 则持股
high = x
else: # 若小于等于high 可以卖了再买入
total += high - low
high, low = x, x
return total + high - low
最大连续乘积子串(动态规划)
def MaxProductSubstring(a):
if a==None or len(a)==0:
return 0
maxEnd = minEnd = maxResult = a[0]
for i in range(1,len(a)):
end1 = maxEnd * a[i]
end2 = maxEnd * a[i]
maxEnd = max(max(end1,end2),a[i])
minEnd = min(min(end1,end2),a[i]) # 因为是负数 但绝对值最大
maxResult=max(maxResult,maxEnd)
return maxResult
字符串编辑距离
题目描述
给定一个源串和目标串,能够对源串进行如下操作:
- 在给定位置上插入一个字符
- 替换任意字符
- 删除任意字符
写一个程序,返回最小操作数,使得对源串进行这些操作后等于目标串,源串和目标串的长度都小于2000。
def EditDistance(source,target):
lenS = len(source)
lenT = len(target)
DP = [[0]*(lenT+1)]*(lenT+1)
for i in range(lenS+1):
DP[i][0]=i
for j in range(lenT+1):
DP[0][j]=j
for i in range(1,lenS+1):
for j in range(1,lenT+1):
if source[i-1]==target[j-1]:
DP[i][j] = DP[i-1][j-1]
else:
DP[i][j]=1+min(DP[i-1][j],DP[i][j-1])
return DP[lenS][lenT]
交叉字符串
给出三个字符串:s1、s2、s3,判断s3是否由s1和s2交叉构成。
class Solution:
"""
@param s1: A string
@param s2: A string
@param s3: A string
@return: Determine whether s3 is formed by interleaving of s1 and s2
"""
def isInterleave(self, s1, s2, s3):
# write your code here
if not s1 or not s2 or not s3:
return False
if len(s1)+len(s2)!=len(s3):
return False
f =[[False]*(len(s2)+1) for i in range(len(s1)+1)]
f[0][0] = True
for i in range(1,len(s1)+1):
f[i][0]= s1[:i]==s3[:i]
for i in range(1,len(s2)+1):
f[0][i]= s2[:i]==s3[:i]
for i in range(1,len(s1)+1):
for j in range(1,len(s2)+1):
if s1[i-1]==s3[i+j-1]:
f[i][j] = f[i-1][j]
if s2[j-1]==s3[i+j-1]:
f[i][j] = f[i][j-1]
return f[-1][-1]
if __name__=="__main__":
#N,M = map(int, raw_input().split())
A= Solution()
s1 = "aabcc"
s2 = "dbbca"
s3= "aadbbcbcac"
print A.isInterleave(s1,s2,s3)
最长无重复字符的子串
给定一个字符串,请找出其中无重复字符的最长子字符串。
class Solution:
"""
@param s: a string
@return: an integer
"""
def lengthOfLongestSubstring(self, s):
# write your code here
if len(s)==0:
return 0
ans = 0
left = 0 # 记录合法的左边界index
last = {}
for i in range(len(s)):
# 子串中出现重复字符,变更left至上一次s[i]出现位置之后,使得子串合法
if s[i] in last and last[s[i]]>=left:
left = last[s[i]]+1
last[s[i]]=i
ans = max(ans,i-left+1)
return ans
01背包问题
# -*- coding:utf-8 -*-
def easy_package(weight,value,package):
N = len(weight)
M=package
weight.insert(0,0)
value.insert(0,0)
f = [[0]*(M+1) for I in range(N+1)] #NxM matrix
for i in range(1,N+1):
for j in range(1,M+1):
if weight[i]<=j:
f[i][j]=max(f[i-1][j],f[i-1][j-weight[i]]+value[i])
else:
f[i][j] = f[i-1][j]
return f[N][M]
# w[i]:第i个物体的重量
# - p[i]:第i个物体的价值
# - f[i][j]:前i个物体放入容量为j 包的最大价值
# - f[i-1][j]:前i个物体放入容量为j 包的最大价值
# - f[i-1][j-w[i]]:前i-1个物体放入容量为j-w[i] 包的最大价值
# f[i][j]=max{f[i-1][j-w[i]]+v[i](j>=w[i]) , f[i-1][j]}
if __name__=="__main__":
weight = [2,2,6,5,4]
value = [6,3,5,4,6]
package = 10
print easy_package(weight,value,package)
优化空间后的 01背包问题
# -*- coding:utf-8 -*-
def easy_package(weight,value,package):
N = len(weight)
M=package
weight.insert(0,0)
value.insert(0,0)
f = [0]*(M+1) #(M+1) matrix
for i in range(1,N+1):
for m in range(0,M+1)[::-1]:
if m>= weight[i]:
f[m] = max(f[m],f[m-weight[i]]+value[i])
return f[M]
# 先考虑一下上面的状态转移方程如何实现
# 肯定有一个主循环i = 1...N,每次算出来二维数组dp[i][0..V]的所有值。
# 那么如果只用一个数组f[0...V],能不能保证第i次循环结束后f[v]就是我们定义的状态f[i][v]呢?
# f[i][v]是由f[i-1][v]和f[i-1][v-c[i]]两个子问题递推而来,
# 能否保证在推f[i][v]时(也即在第i次主循环中推f[v]时)能够得到f[i-1][v]和f[i-1][v-c[i]]的值呢?
# 事实上,这要求在每次主循环中我们以v=V...0的顺序推f[v],
# 这样才能保证推f[v]时f[v-c[i]]保存的是状态f[i-1][v-c[i]]的值。
if __name__=="__main__":
weight = [2,2,6,5,4]
value = [6,3,5,4,6]
package = 10
print easy_package(weight,value,package)
完全背包问题
题目
有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价格是w[i].求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大
转化为01背包求解
最简单的想法是:考虑到第i种物品最多选V/c[i]件,于是可以把第i种物品转换为V/c[i]件费用及价值均不变的物品,然后求解这个01背包问题。但是这样完全没有改进时间复杂度,但这毕竟给了我们将完全背包转换为01背包问题的思路:将一种物品拆成多件物品
O(VN)的算法
for I = 1 ... N
for v = 0 ... V
f[v] = max{f[v], f[v-cost] + weight}
你会发现,这个伪代码与01背包的伪代码只有v的循环次序不同而已。为什么这样一改就行呢?首先,想想为什么01背包问题中要按照v=V…0的逆序来循环。这是因为要保证第i次循环中的状态f[i][v]是由状态f[i-1][v-c[i]]递推而来。换句话说,这正是为了保证每件物品只选一次,保证在考虑“选入第i件物品”这件策略时,依据的是一个绝无已经选入第i件物品的子结果f[i-1][v-c[i]]。而现在完全背包的特点恰好是每种物品可选无限件,所以在考虑“加选一件dii种物品”这种策略时,却正需要一个可能已选入第i种物品的子结果f[i][c-v[i]],所以就可以并且必须采用v=0…V的顺序循环
def solve3(vlist,wlist,totalWeight,totalLength):
"""完全背包问题"""
resArr = np.zeros((totalWeight)+1,dtype=np.int32)
for i in range(1,totalLength+1):
for j in range(1,totalWeight+1):
if wlist[i] <= j:
resArr[j] = max(resArr[j],resArr[j-wlist[i]]+vlist[i])
return resArr[-1]
if __name__ == '__main__':
v = [0,60,100,120]
w = [0,10,20,30]
weight = 50
n = 3
result = solve3(v,w,weight,n)
print(result)
(硬币问题) 动态规划 (其实是完全背包问题)
如果我们有面值为 1 元、3 元和 5 元的硬币若干枚,如何用最少的硬币凑够 11 元? (表面上这道题可以用贪心算法,但贪心算法无法保证可以求出解,比如 1 元换成 2 元的时候)
d(i)=min{ d(i-vj)+1 },其中 i-vj >=0,vj表示第 j 个硬币的面值;
Min=[x for x in range(12)];
VN=[1,3,5];
for i in range(1,12,1):
for j in range(3):
if VN[j]<=i and Min[i-VN[j]]+1<Min[i]:
Min[i]=Min[i-VN[j]]+1;
print(Min[1::1]);
换硬币问题(完全背包)
用足够多的 1 分,2 分和 5 分硬币凑出 1 元钱,一共有多少种方法?(541)
def coinsCount(coins,amount):
result = [0]*(amount+1)
result[0] = 1
for coin in coins:
for i in range(1,amount+1):
if i>=coin:
result[i] += result[i-coin]
return result[-1]
n个骰子的点数
题目: 把n个骰子仍在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s的所有可能的值出现的概率。
动态规划思想
假设f(m,n)表示投第m个骰子时,点数之和n出现的次数,投第m个骰子时的点数之和只与投第m-1个骰子时有关。
递归方程:f(m,n)=f(m-1,n-1)+f(m-1,n-2)+f(m-1,n-3)+f(m-1,n-4)+f(m-1,n-5)+f(m-1,n-6),表示本轮点数和为n出现次数等于上一轮点数和为n-1,n-2,n-3,n-4,n-5,n-6出现的次数之和。
初始条件:第一轮的f(1),f(2),f(3),f(4),f(5),f(6)均等于1.
def probability(n,dmax=6,dmin=1): # the minimum number in saizi is 1
dp = [0]*(n*dmax+1)
for i in range(dmin,dmax+1):
dp[i]=i
for i in range(2,n+1): # 第I个骰子扔过后的结果
for j in range(dmin*i,dmax*i+1)[::-1]:
for k in range(dmin,dmax+1):
if j>k:
dp[j] +=dp[j-k]
print dp
LIS 问题
一个序列有 N 个数:A[1],A[2],…,A[N],求出最长非降子序列的长度。 (讲 DP 基本都会讲到的一个问题 LIS:longest increasing subsequence)
# -*- coding:utf-8 -*-
定义 d(i),表示前 i 个数中以 A[i]结尾的最长非降子序列的长度
d(i) = max{1, d(j)+1},其中 j<i,A[j]<=A[i]
def LIS(nums):
d = [0]*len(nums)
answer = 1
for i in range(len(nums)):
d[i]=1
for j in range(i):
if nums[j]<=nums[i] and (d[j]+1)>d[i]:
d[i] = d[j]+1
answer = max(d)
return answer
if __name__=="__main__":
data = [5,3,4,8,6,7]
print LIS(data)
最长公共子序列 LCS(longest common sequence) 问题
c[i,j]表示Xi 和 Yj 的LCS的长度
c[i,j]= 0 if(I or j ==0)
= c[I-1,j-1]+1. If x[I]=y[j]
= max(c[I-1][j],c[I][j-1]). If x[I]!=y[j]
def LCS(s1,s2):
length1 = len(s1)
length2 = len(s2)
s1.insert(0,0)
s2.insert(0,0)
c = [[0]*(length2+1)]*(length1+1)
for i in range(1,len(c)):
for j in range(1,len(c[0])):
if s1[i]==s2[j]:
c[i][j] = c[i-1][j-1]+1
else:
c[i][j] = max(c[i-1][j],c[i][j-1])
return c[-1][-1]
if __name__=="__main__":
s1=[1,3,4,5,6,7,7,8]
s2=[3,5,7,4,8,6,7,8,2]
print LCS(s1,s2)
划分单词问题
给出一个字符串s和一个词典,判断字符串s是否可以被空格切分成一个或多个出现在字典中的单词。
样例
给出
s = "lintcode"
dict = ["lint","code"]
返回 true 因为”lintcode”可以被空格切分成”lint code”
class Solution:
"""
@param: s: A string
@param: dict: A dictionary of words dict
@return: A boolean
"""
def wordBreak(self, s, dict):
# write your code here
# f[i] means that until i ,it can be break into dict words
# f[i] is true if f[I-j]is true and f[I-j:i] is in dict (1<=j<=min(maxlength,i))
if len(dict)==0:
return len(s) == 0
n = len(s)
f = [False] *(n+1)
f[0] = True
maxLength = max([len(w) for w in dict])
for i in range(1,n+1):
for j in range(1,min(maxLength,i)+1):
if not f[i-j]:
continue
if s[i-j:i] in dict:
f[i] = True
break
return f[n]
part3. 树题
之字型打印二叉树
class Solution:
def Print(self, pRoot):
# write code here
if not pRoot:
return []
nodeStack=[pRoot]
result =[]
direction = 1
while nodeStack:
res = []
nextStack=[]
for i in nodeStack:
res.append(i.val)
if i.left:
nextStack.append(i.left)
if i.right:
nextStack.append(i.right)
nodeStack = nextStack[:]
if direction==-1:
res.reverse()
result.append(res) #result.extend(res)
direction *=-1
return result
不同的二叉查找树(动态规划)
给出 n,问由 1…n 为节点组成的不同的二叉查找树有多少种?
class Solution:
"""
@param n: An integer
@return: An integer
"""
def numTrees(self, n):
#The case for 3 elements example
# Count[3] = Count[0]*Count[2] (1 as root)
# + Count[1]*Count[1] (2 as root) 左边有几个数字 右边有几个数字
# + Count[2]*Count[0] (3 as root)
# Therefore, we can get the equation:
# Count[i] = ∑ Count[0...k] * count[ k+1....i] 0<=k<i-1
maxType = [1,1,2]
if n<=2:
return maxType[n]
maxType = maxType+[0]*(n-2)
for i in range(3,n+1):
for j in range(0,i):
maxType[i] += maxType[j]*maxType[i-j-1]
return maxType[-1]
最近公共祖先LCA问题
问题描述
求有根树的任意两个节点的最近公共祖先。
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
def getLCA(root,node1,node2):
if root==None:
return None
if root==node1 or root==node2:
return root
left = getLCA(root.left,node1,node2)
right = getLCA(root.right,node1,node2)
if(left!=None and right!=None):
return root
elif left!=None:
return left
elif right!=None:
return right
else:
return None
若为二叉搜索树的话(BST)
二叉搜索树是经过排序的,位于左子树的节点都比父节点小,位于右子树的节点都比父节点大。既然要找最低的公共祖先节点,我们可以从根节点开始进行比较。
若当前节点的值比两个节点的值都大,那么最低的祖先节点一定在当前节点的左子树中,则遍历当前节点的左子节点;
反之,若当前节点的值比两个节点的值都小,那么最低的祖先节点一定在当前节点的右子树中,则遍历当前节点的右子节点;
这样,直到找到一个节点,位于两个节点值的中间,则找到了最低的公共祖先节点。
def getLCA(root,node1,node2):
if root==None or root==node1 or root==node2:
return root
if root.val > max(node1.val,node2,val):
return getLCA(root.left,node1,node2)
elif root.val < min(node1.val,node2.val):
return getLCA(root.right,node1,node2)
else:
return root
二叉树的深度
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
class Solution:
def TreeDepth(self, pRoot):
# write code here
if pRoot is None:
return 0
else:
left_deepth=self.TreeDepth(pRoot.left)
right_deepth=self.TreeDepth(pRoot.right)
return max(left_deepth,right_deepth)+1
树的子结构
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
# -*- coding:utf-8 -*-
class TreeNode:
def **init**(self,
x): self.val = x self.left = None self.right = None
class Solution:
def HasSubtree(self, pRoot1, pRoot2):
if pRoot2==None or pRoot1==None: return False
if self.judgeSame(pRoot1,pRoot2):
return True
else:
result1=self.HasSubtree(pRoot1.left,pRoot2)
result2=self.HasSubtree(pRoot1.right,pRoot2)
result=result1 or result2
return result
def judgeSame(self,pRoot1, pRoot2):
if pRoot2==None:
return True
if(pRoot1!=None and pRoot2==None):
return False
if (pRoot2 != None and pRoot1 == None):
return False
if(pRoot1.val!=pRoot2.val):
return False
return self.judgeSame(pRoot1.left,pRoot2.left) and self.judgeSame(pRoot1.right,pRoot2.right)
重建二叉树
题目描述
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
# -*- coding:utf-8 -*-
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
class Solution:
# 返回构造的TreeNode根节点
def reConstructBinaryTree(self, pre, tin):
# write code here
if(len(pre)==0) or(len(tin)==0):
return None
rootValue = pre[0]
root = TreeNode(rootValue)
if len(pre)==1:
return root
rootTinIndex = tin.index(rootValue)
preStart = 1
preEnd = rootTinIndex + 1
tinStart = 0
tinEnd = rootTinIndex
if rootTinIndex>0:
root.left = self.reConstructBinaryTree(pre[preStart:preEnd],tin[tinStart:tinEnd])
if rootTinIndex<(len(pre)-1):
root.right =self.reConstructBinaryTree(pre[preEnd+1:],tin[tinEnd+1:])
return root
if __name__=="__main__":
A = Solution()
pre = [1,2,4,7,3,5,6,8]
tin = [4,7,2,1,5,3,8,6]
print A.reConstructBinaryTree(pre,tin)
part4 遍历与查找
字符串的任意全排列问题
def all_permulation(result,data,length):# 字符串的全排列 采用DFS思想
if len(result)==length:
global possible_answer
possible_answer.append(result[:])
return
for i in range(len(data)):
result.append(data[i])
all_permulation(result,data[:i]+data[i+1:],length)
result.pop(-1)
return
if __name__=="__main__":
data= ['a','b','c','d']
possible_answer=[]
all_permulation([],data,len(data))
print possible_answer
print len(possible_answer)
二分查找
注意height和low的初始值和while的判断条件
def BinarySearch(array,t):
low = 0
height = len(array)-1
while low < height:
mid = (low+height)/2
if array[mid] < t:
low = mid + 1
elif array[mid] > t:
height = mid - 1
else:
return array[mid]
return -1
二分查找(旋转数组的最小数字)
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
# -*- coding:utf-8 -*-
class Solution:
def minNumberInRotateArray(self, rotateArray):
# write code here
if len(rotateArray)==0:
return 0
index1=0
index2=len(rotateArray)-1
indexMid = index1
while rotateArray[index1]>= rotateArray[index2]:
if index2-index1==1:
indexMid=index2
break
indexMid=(index1+index2)/2
if rotateArray[indexMid]>=rotateArray[index1]:
index1 = indexMid
elif rotateArray[indexMid]<=rotateArray[index2]:
index2=indexMid
return rotateArray[indexMid]
两个排序数组的中位数
两个排序的数组A和B分别含有m和n个数,找到两个排序数组的中位数,要求时间复杂度应为O(log (m+n))。
class Solution:
"""
@param: A: An integer array
@param: B: An integer array
@return: a double whose format is *.5 or *.0
"""
def findMedianSortedArrays(self, A, B):
# write your code here
n = len(A)+len(B)
if n %2 ==1:
return self.findKth(A,B,n/2+1)
else:
small = self.findKth(A,B,n/2)
big = self.findKth(A,B,n/2+1)
return (small+big)/2.0
def findKth(self,A,B,k):
if len(A)==0:
return B[k-1]
if len(B)==0:
return A[k-1]
if k==1:
return min(A[0],B[0])
a = A[k/2-1] if len(A)>=k/2 else None
b = B[k/2-1] if len(B)>=k/2 else None
if b ==None or (a!=None and a<b): #delete k/2 small numbers
return self.findKth(A[k/2:],B,k-k/2)
else:
return self.findKth(A,B[k/2:],k-k/2)
if __name__=="__main__":
A=[1,2,3,4]
B=[4,5,6]
sol = Solution()
print sol.findMedianSortedArrays(A,B)
partition查找(29.最小的k个数)
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4
时间复杂度为O(nlogn)
# -*- coding:utf-8 -*-
class Solution:
def GetLeastNumbers_Solution(self, tinput, k):
# write code here
if(tinput==None or k==None or k>len(tinput) or k<0):
return None
start = 0
end = len(tinput)-1
index = self.Partition(tinput,start,end)
while(index!=(k-1)):
if index>k-1:
end = index-1
index = self.Partition(tinput,start,end)
else:
start = index +1
index = self.Partition(tinput,start,end)
return tinput[:k]
def Partition(self,data,start,end):
#sort this partition into two parts : one parts less than key, one parts more than key
# return the index of key
if(data==None or start<0 or end>=len(data) or start>end):
return #error
key = data[start]
while(start<end):
while start<end and data[end]>=key:
end -=1
if start<end:
data[start] = data[end]
while start<end and data[start]<=key:
start +=1
if start<end:
data[end] = data[start]
data[start]=key
return start
两数之和
给一个整数数组,找到两个数使得他们的和等于一个给定的数 target。
你需要实现的函数twoSum需要返回这两个数的下标, 并且第一个下标小于第二个下标。注意这里下标的范围是 0 到 n-1。
class Solution(object):
def twoSum(self, nums, target):
#hash用于建立数值到下标的映射
hash = {}
#循环nums数值,并添加映射
for i in range(len(nums)):
if target - nums[i] in hash:
return [hash[target - nums[i]], i]
hash[nums[i]] = i
#无解的情况
return [-1, -1]
三数之和
给出一个有n个整数的数组S,在S中找到三个整数a, b, c,找到所有使得a + b + c = 0的三元组。
注意事项 在三元组(a, b, c),要求a <= b <= c。
结果不能包含重复的三元组。
class Solution(object):
'''
题意:求数列中三个数之和为0的三元组有多少个,需去重
暴力枚举三个数复杂度为O(N^3)
先考虑2Sum的做法,假设升序数列a,对于一组解ai,aj, 另一组解ak,al
必然满足 i<k j>l 或 i>k j<l, 因此我们可以用两个指针,初始时指向数列两端
指向数之和大于目标值时,右指针向左移使得总和减小,反之左指针向右移
由此可以用O(N)的复杂度解决2Sum问题,3Sum则枚举第一个数O(N^2)
使用有序数列的好处是,在枚举和移动指针时值相等的数可以跳过,省去去重部分
'''
def threeSum(self, nums):
nums.sort()
res = []
length = len(nums)
for i in range(0, length - 2):
if i and nums[i] == nums[i - 1]:
continue
target = nums[i] * -1
left, right = i + 1, length - 1
while left < right:
if nums[left] + nums[right] == target:
res.append([nums[i], nums[left], nums[right]])
right -= 1
left += 1
while left < right and nums[left] == nums[left - 1]:
left += 1
while left < right and nums[right] == nums[right + 1]:
right -= 1
elif nums[left] + nums[right] > target:
right -= 1
else:
left += 1
return res
part5 线性数据结构
给出一个字符串(假设长度最长为1000),求出它的最长回文子串,你可以假定只有一个满足条件的最长回文串。
最长回文子串
class Solution(object):
# Python O(N^2)
def longestPalindrome(self, s):
ansl, ansr, maxx = 0, 1, 0
length = len(s)
for i in range(1, length * 2): #分为长度为奇数和偶数的情况
if i & 1 :
left = i //2
right = left
else :
left = i //2 - 1
right = left + 1
while (left >= 0) and (right < length) and (s[left] == s[right]):
left -= 1
right += 1
left += 1
right -= 1
if right - left > maxx:
maxx = right - left
ansl = left
ansr = right
return s[ansl: ansr + 1]
买卖股票的最佳时机
假设有一个数组,它的第i个元素是一支给定的股票在第i天的价格。如果你最多只允许完成一次交易(例如,一次买卖股票),设计一个算法来找出最大利润。
class Solution:
"""
@param prices: Given an integer array
@return: Maximum profit
"""
def maxProfit(self, prices):
# write your code here
total = 0
low, high = sys.maxint, 0
for x in prices:
if x - low > total:
total = x - low
if x < low:
low = x
return total
装最多水的容器
给定 n 个非负整数 a1, a2, …, an, 每个数代表了坐标中的一个点 (i, ai)。画 n 条垂直线,使得 i 垂直线的两个端点分别为(i, ai)和(i, 0)。找到两条线,使得其与 x 轴共同构成一个容器,以容纳最多水。
class Solution(object):
'''
题意:任取两个a[i]、a[j] 使得min(a[i], a[j]) * abs(i - j)最大化
用两个指针从两侧向中间扫描,每次移动数值较小的指针,用反证法可以证明
总是可以得到最优答案
'''
def maxArea(self, height):
left, right = 0, len(height) - 1
ans = 0
while left < right:
if height[left] < height[right]:
area = height[left] * (right - left)
left += 1
else:
area = height[right] * (right - left)
right -= 1
ans = max(ans, area)
return ans
链表中环的入口结点
一个链表中包含环,请找出该链表的环的入口结点。
第一步,找环中相汇点。分别用p1,p2指向链表头部,p1每次走一步,p2每次走二步,直到p1==p2找到在环中的相汇点。
第二步,找环的入口。接上步,当p1==p2时,p2所经过节点数为2x,p1所经过节点数为x,设环中有n个节点,p2比p1多走一圈有2x=n+x; n=x;可以看出p1实际走了一个环的步数,再让p2指向链表头部,p1位置不变,p1,p2每次走一步直到p1==p2; 此时p1指向环的入口。
class Solution:
def EntryNodeOfLoop(self, pHead):
# write code here
if(pHead==None or pHead.next==None):
return None
p1 = pHead
p2 = pHead
while(p2!=None and p2.next!=None):
p1=p1.next
p2 = p2.next.next
if(p1==p2):
p2 = pHead
while(p1!=p2):
p1 = p1.next
p2 = p2.next
if p1==p2:
return p1
return None
反转链表
# -*- coding:utf-8 -*-
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
class Solution:
# 返回ListNode
def ReverseList(self, pHead):
# write code here
pNew=None
pCur=pHead
while(pCur!=None):
pHead = pCur.next
pCur.next=pNew
pNew=pCur
pCur=pHead
return pNew
合并k个排序链表 并且返回合并后的排序链表
class ListNode(object):
def __init__(self, val, next=None):
self.val = val
self.next = next
class Solution:
"""
@param lists: a list of ListNode
@return: The head of one sorted list.
"""
from heapq import heappop, heappush
def mergeKLists(self, lists):
if not lists:
return None
trav = dummy = ListNode(-1)
heap = []
for link_list in lists:
if link_list:
self.heappushNode(heap, link_list)
while heap:
node = heappop(heap)[1]
trav.next = node
trav = trav.next
if trav.next:
self.heappushNode(heap, trav.next)
return dummy.next
def heappushNode(self, heap, node):
heappush(heap, (node.val, node))# input the key and the node
包含min的最小栈
创建一个辅助栈 来保存每个当前情况下的min值
class Solution:
stack=[]
aux_stack=[]
def push(self, node):
self.stack.append(node)
self.aux_stack.append(max(aux_stack[-1],node));
def pop(self):
# write code here
self.aux_stack.pop()
return self.stack.pop()
def top(self):
return self.stack[-1]
def min(self):
return aux_stack[-1]
顺时针打印矩阵
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10
其实zip还是反过来会这个二维数组操作,但要注意写成zip(*),表示这是一个zip的逆操作。
class Solution:
# matrix类型为二维列表,需要返回列表
def printMatrix(self, matrix):
if matrix==None or len(matrix)==0:
return None
a=[]
while matrix:
a.extend(list(matrix.pop(0)))
matrix=zip(*matrix)
matrix.reverse()
return a
Version2
def printMatrix(matrix):
result = []
while True:
result.extend(matrix.pop(0))
if matrix!=[] :
matrix = turn(matrix)
else:
break
return result
def turn(matrix):
originRows = len(matrix)
originCols = len(matrix[0])
answer = []
for i in range(originCols):
tmp = []
for j in range(originRows):
tmp.append(matrix[j][i])
answer.append(tmp)
answer.reverse()
return answer
翻转字符串
“How Are You” ->"”You Are How”
单词的构成:无空格字母构成一个单词
输入字符串是否包括前导或者尾随空格?可以包括,但是反转后的字符不能包括
如何处理两个单词间的多个空格?在反转字符串中间空格减少到只含一个
class Solution:
"""
@param: s: A string
@return: A string
"""
def reverseWords(self, s):
# write your code here
sList = list(s)
while len(sList)!=0 and sList[0]== " ":
del sList[0]
while len(sList)!=0 and sList[-1] == " ":
del sList[-1]
if len(sList)==1 or len(sList)==0:
return "".join(sList)
index_tmp = 0
while index_tmp<=len(sList)-2:
if sList[index_tmp]==" " and sList[index_tmp+1]==" ":
del sList[index_tmp]
else:
index_tmp +=1
for i in range(0,len(sList)/2):
sList[i],sList[-1-i] = sList[-1-i],sList[i]
begin = 0
end = 0
for i in range(len(sList)):
if sList[i] == " " or i==len(sList)-1:
end = i-1
if i == len(sList)-1:
end = i
if sList[end]>="Z"
for j in range(0, (end-begin)/2):
sList[begin+j],sList[end-j] = sList[end-j],sList[begin+j]
begin = i+1
return "".join(sList)
if __name__=='__main__':
A = Solution()
print A.reverseWords("How are You") #You are How
数学运算
数值的整数次方
实现函数 double Power(double base, int exponent)
考虑 base是否为0 考虑exponent为正数负数为0
数组中只出现一次的两个数字
题目描述 一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
此题考察的是异或运算的特点:即两个相同的数异或结果为0。 此题用了两次异或运算特点: (1)第一次使用异或运算,得到了两个只出现一次的数相异或的结果。 (2)因为两个只出现一次的数肯定不同,即他们的异或结果一定不为0,一定有一个位上有1。另外一个此位上没有1,我们可以根据此位上是否有1,将整个数组重新划分成两部分,一部分此位上一定有1,另一部分此位上一定没有1,然后分别对每部分求异或,因为划分后的两部分有这样的特点:其他数都出现两次,只有一个数只出现一次。因此,我们又可以运用异或运算,分别得到两部分只出现一次的数。
class Solution:
# 返回[a,b] 其中ab是出现一次的两个数字
def FindNumsAppearOnce(self, array):
# write code here
if len(array)<2:
return None
firstXOR = 0
for i in range(len(array)):
firstXOR = firstXOR ^ array[i]
flag = 1
while((flag^firstXOR)==0):
flag = flag <<1
a = b = 0
for i in range(len(array)):
if (array[i]&flag) ==0:
a = a^array[i]
else:
b = b^array[i]
return [a,b]
大数乘法
def multi(strA,strB):
negative = False
listA = list(strA)
listB = list(strB)
if listA[0]=='-':
negative = not negative
del listA[0]
if listB[0] =='-':
negative = not negative
del listB[0]
lenA = len(listA)
lenB = len(listB)
result = [0 for i in range(lenA+lenB)]
for i in range(1,lenA+1):
for j in range(1,lenB+1):
result[-(i+j)+1] +=int(listA[-i])*int(listB[-j])
for i in range(len(result)-1,0,-1):
if result[i]>=10:
result[i-1] +=result[i]//10
result[i] = result[i]%10
while(result[0]==0):
del result[0]
result = ''.join(map(str,result))
if negative:
result = '-'+result
return result
if __name__=='__main__':
a = "-123"
b = '456'
res=multi(a,b)
print('multi',res)
print('ok',int(a)*int(b))
大数加法
def addOperation(strA,strB):
listA = map(int,list(strA))
listB = map(int,list(strB))
while len(listA)<len(listB):
listA.insert(0,0)
while len(listB)<len(listA):
listB.insert(0,0)
result = [0 for i in range(len(listA)+1)]
for i in range(1,len(listA)+1):
result[-1*i] = listA[-1*i]+listB[-1*i]
for i in range(len(result)-1,0,-1):
if result[i]>=10:
result[i-1] +=result[i]//10
result[i] = result[i]%10
while(result[0]==0):
del result[0]
result = ''.join(map(str,result))
return result
if __name__=='__main__':
a = "123771321312"
b = '4561231231'
res=addOperation(a,b)
print('addOperation',res)
print('ok',int(a)+int(b))
最多有多少个点在同一条直线上
class Point:
def __init__(self, a=0, b=0):
self.x = a
self.y = b
class Solution:
"""
@param points: an array of point
@return: An integer
"""
def maxPoints(self, points):
# write your code here
len_points = len(points)
if len_points<=1:
return len_points
max_count = 0
for index1 in range(len_points):
p1 = points[index1]
gradients = {}
infinite_count = 0
duplicate_count = 0
for index2 in range(index1,len_points):
p2 = points[index2]
dx = p2.x-p1.x
dy = p2.y-p1.y
if dx==0 and dy==0:
duplicate_count +=1
if dx==0:
infinite_count +=1
else:
gra = float(dy)/dx
gradients[gra]=(gradients[gra] + 1 if gradients.has_key(gra) else 1)
max_count = max(max_count,infinite_count)
for k,v in gradients.items():
v +=duplicate_count
max_count = max(v,max_count)
return max_count