题目一:
1.给定一个数组,升序数组,将他构建成一个BST
思路:升序数组,这就类似于中序遍历二叉树得出的数组,那么根节点就是在数组中间位置,找到中间位置构建根节点,然后中间位置的左右两侧是根节点的左右子树,递归的对左右子树进行处理,得出一颗BST
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
# @param num, a list of integers
# @return a tree node
def sortedArrayToBST(self, num):
if not num:
return None
mid = len(num)//2 #“//”表示整数除法;“/”浮点数除法;
root = TreeNode(num[mid])
left = num[:mid]
right = num[mid+1:]
root.left = self.sortedArrayToBST(left)
root.right = self.sortedArrayToBST(right)
return root
2.将链表转换为二叉树
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution(object):
def sortedListToBST(self, head):
if not head:
return None
if not head.next:
return TreeNode(head.val)
Lroot = self.find_root(head)
Troot = TreeNode(Lroot.val)
Troot.left = self.sortedListToBST(head)
Troot.right = self.sortedListToBST(Lroot.next)
return Troot
def find_root(self,head): #找到链表的中点作为后半部分的起点,并将链表切断。
if not head or not head.next:
return head
slow = head
fast = head
pre = head
while fast and fast.next:
pre = slow
slow = slow.next
fast = fast.next.next
pre.next = None
return slow
题目二:2sum
给定一个整数数组,返回两个数字的索引,使它们加起来等于一个特定的目标。
最佳思路:使用hashtable,建立数组值和下标的键值对,在python中相当于使用dict来存储,dict的key是数组的值,数组的下标是dict的value。
class Solution(object):
def twoSum(self, nums, target):
dict = {} # 定义一个字典
for i in xrange(len(nums)): # 遍历数组
x = nums[i] # 取当前数组的值
if target-x in dict: # 如果target减去当前值的差在字典的key中可以找到,直接可以返回dtarget-x对应的值,也即下标
return (dict[target-x], i) # 返回当前下标和找到的下标
dict[x] = i # 将数组值和下标数存入dict
题目三:最大连续子序列和
思路1:三层循环暴力法
第一层循环的索引i表示子序列左端的位置,第二层循环表示子列右端位置,第三层循环计算该子列的大小
思路2:二层循环
第二种算法是在第一个算法的基础上去掉了重复计算子序列大小的循环
思路3:分治法
在此例中,先将序列等分成左右两份,最大子序列只可能出现在三个地方:
- 整个子序列出现在左半部分;
- 整个子序列出现在右半部分;
- 跨越左右边界出现在中间。
思路4:采用动态规划的思路。
首先要知道最大子序列的两端的元素(或者连续子序列,整个子序列可以看作是一个元素)是不可能为负的,因为如果为负,总是会有通过将这个负的元素(或连续子序列)去掉而使子序列和更大。
在线算法从0开始累加,当累加到i时,若
∑
l
s
t
[
i
]
<
0
∑lst[i]<0
∑lst[i]<0,则可以将[0, i]这一段看作是最左端的小于0的连续子序列,此时[0, i]这一段便可以舍弃,并从i开始重新累加,当累加到某个值j时又小于0,则舍弃[i, j]这一段继续从j开始重新累加。
首先仔细观察数组的规律,可以得到下列动态规划的式子:
(1)如果i==0 或者 F[i-1]<=0 那么 F[i] = A[i]
(2)如果i!=0 并且 F[i-1]>0 那么 F[i] = F[i-1] + A[i]
class Solution(object):
def maxSubArray(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
ans = -2147483647
n = len(nums)
sums = 0
for i in range(0, n):
sums += nums[i]
if sums > ans:
ans = sums
if sums < 0:
sums = 0
return ans
题目四:数组中第k大的元素
思路:利用快速排序的思想
从数组S中随机找出一个元素X,把数组分为两部分Sa和Sb。Sa中的元素大于等于X,Sb中元素小于X。这时有两种情况:
- Sa中元素的个数小于k,则Sb中的第k-|Sa|个元素即为第k大数;
- Sa中元素的个数大于等于k,则返回Sa中的第k大数。时间复杂度近似为O(n)
def partition(num, low, high):
pivot = num[low]
while (low < high):
while (low < high and num[high] > pivot):
high -= 1
while (low < high and num[low] < pivot):
low += 1
num[low],num[high] = num[high],num[low]
num[high] = pivot
return high,num
def findkth(num,low,high,k): #找到数组里第k个数
index=(partition(num,low,high))[0]
print((partition(num,low,high))[1])
if index==k:return num[index]
if index<k:
return findkth(num,index+1,high,k)
else:
return findkth(num,low,index-1,k)
print(findkth([6,1,3,9,2],0,len([6,1,3,9,2])-1,4))
题目五:二叉搜索树的第k个节点
中序遍历一共三步:
- 递归左子树
- 存储根节点的值
- 递归右子树
本题也需要三步:
- 递归左子树,并判断有无返回节点。如果有,停止递归,返回所要返回的节点。
- 当左子树没有返回节点时,判断当前的根节点是不是第k个遍历到的值(即第k大)。如果是,则返回该节点,停止递归。
- 当左子树和根节点都没有返回节点时,递归右子树,并判断有无返回节点。如果有,停止递归,返回所要返回的节点。
count = 0
def KthNode(self, pRoot, k):
if not pRoot:
return None
node = self.KthNode(pRoot.left, k)
if node:
return node
self.count += 1
if self.count == k:
return pRoot
node = self.KthNode(pRoot.right, k)
if node:
return node
题目六:最大1矩形面积
def maximalSquare(self, matrix):
"""
My Method
算法:动规
思路:
用dp[i][j]记录以matrix[i][j]为"1"矩形的右下角的矩形的最大边长
matrix[i][j] == 0 的显然dp[i][j] == 0
对matrix[i][j] == 1的来说,如果在第一行或者第一列,显然dp[i][j]=1,最大也就是这么大了
对于其他位置,
像下面这样,右下角的那个1称之为matrix[i][j],那么要检查它的左,上,左上三个位置是否都为1,
如果这三个位置有一个不为1的,那么显然以i,j为最大矩形的右下角的那个矩形,只能是matrix[i][j]本身
那么dp[i][j] =1
否则就要看这三个位置的dp情况,可以看到,如果右下角的i,j代表的那个矩形,如果想扩充边长的话,其
值应该是dp[i][j] = min(dp[左],dp[上],dp[左上])+1,如此便可以构建状态转移方程
1 1 1 1 1 0 1 1
1 1 --> 1 1 1 vs 1 1 1
1 1 1 1 1 1
"""
if matrix == [] or matrix[0] == []:
return 0
n = len(matrix)
m = len(matrix[0])
dp = [[0] * m for _ in range(n)]
ans = 0
for i in range(n):
for j in range(m):
if i == 0 or j == 0:
dp[i][j] = int(matrix[i][j])
elif matrix[i][j] == '1':
if matrix[i - 1][j - 1] == '1' and matrix[i][j - 1] == '1' and matrix[i - 1][j] == '1':
dp[i][j] = min(dp[i - 1][j - 1], dp[i][j - 1], dp[i - 1][j]) + 1
else:
dp[i][j] = 1
else:
dp[i][j] = 0
ans = max(ans, dp[i][j])
return ans ** 2
def maximalSquare1(self, matrix):
"""
Solution Method
事实上,在状态转移时,可以不用管上下左右是不是'1',如果不是1的话,那么最小值就是0,当前值就是0+1 = 1
0 1
1 1
其实就是把我冗余的判断精简了
"""
if matrix == [] or matrix[0] == []:
return 0
n = len(matrix)
m = len(matrix[0])
dp = [[0] * m for _ in range(n)]
ans = 0
for i in range(n):
for j in range(m):
if i == 0 or j == 0:
dp[i][j] = int(matrix[i][j])
elif matrix[i][j] == '1':
dp[i][j] = min(dp[i - 1][j - 1], dp[i][j - 1], dp[i - 1][j]) + 1
else:
dp[i][j] = 0
ans = max(ans, dp[i][j])
return ans ** 2
题目七:按之字形顺序打印二叉树
面对要求的偶数层倒序,亦有两种解题思路,第一种是:获取到所有节点的值后,将偶数层的节点值倒序。第二种则是在获取节点的值的时候就倒序存入。以下给出两种方式的Python代码:
方式1:
-
按序获取每一层节点的值;
-
将偶数层节点的值倒序。
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def Print(self, pRoot):
# write code here
if not pRoot:
return []
resultArray = []
curLayerNodes = [pRoot]
isEvenLayer = True#Even:偶数
while curLayerNodes:
curLayerValues = []
nextLayerNodes = []
isEvenLayer = not isEvenLayer
for node in curLayerNodes:
curLayerValues.append(node.val)
if node.left:
nextLayerNodes.append(node.left)
if node.right:
nextLayerNodes.append(node.right)
curLayerNodes = nextLayerNodes
resultArray.append(curLayerValues[::-1]) if isEvenLayer else resultArray.append(curLayerValues)
return resultArray
方式2:
获取每一层的节点的值时,如果是偶数层,则将每个节点的值插入到列表的头部,即实现了获取节点值时如果是偶数层则倒序排列的效果:
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def Print(self, pRoot):
# write code here
if not pRoot:
return []
resultArray = []
curLayerNodes = [pRoot]
isEvenLayer = True
while curLayerNodes:
curLayerValues = []
nextLayerNodes = []
isEvenLayer = not isEvenLayer
for node in curLayerNodes:
curLayerValues.insert(0,node.val) if isEvenLayer else curLayerValues.append(node.val)
if node.left:
nextLayerNodes.append(node.left)
if node.right:
nextLayerNodes.append(node.right)
curLayerNodes = nextLayerNodes
resultArray.append(curLayerValues)
return resultArray
题目八:给定一个递增递减数组求转折点
最简单的思路就是, 遍历一遍, 找到第一个满足条件a[i]>a[i+1]的元素, 表明开始"下坡", 则i就是所找的最大值下标. 这种解法的时间复杂度为O(n). 当然, 这其中存在两种情况, 需要单独拿出来分析一下: 1, 数组单调递增. 因为题目要求数组"先递增后递减", 如果是"单调递增"的话, 那么最后一个元素, 肯定大于倒数第二个元素. 2, 数组单调递减. 同第一种情况的分析, "单调递减"的该数组, 肯定满足条件a[0]>a[1].
所以, 时间复杂度为O(n)的解法如下:
def findPeak(nums):
if nums:
if len(nums) == 1:
return 0
if nums[0] > nums[1]: #数组单调递减
return 0
index = len(nums)-1
if nums[index] > nums[index-1] : #数组单调递增
return index
#循环n-1次
for i in range(index):
if (nums[i] > nums[i+1]) :
return i #nums[i]
return -1
考虑时间复杂度为O(logn)的情况
满足时间复杂度O(logn)的查找算法, 你想到了什么呢? 二分查找. 那么查找的"峰顶"元素会满足什么样的条件呢? "峰顶"嘛, 大于左侧元素, 也大于右侧元素, 即a[i] > a[i-1] && a[i] > a[i+1].
所以, 时间复杂度为O(logn)的解决方法为:
def findPeak(nums):
if nums:
if len(nums) == 1:
return 0
if nums[0] > nums[1]: #数组单调递减
return 0
index = len(nums)-1
if nums[index] > nums[index-1] : #数组单调递增
return index
i = 0
j = index
while i<=j:
mid = (i + j) // 2
if nums[mid] > nums[mid - 1] and nums[mid] > nums[mid + 1]:
return mid
elif nums[mid] > nums[mid + 1]: # 处于下坡段, 即递减段
j = mid - 1
elif nums[mid] > nums[mid - 1]: # 处于上坡段, 即递增段
i = mid + 1
return -1