1、***两数之和为指定值
-
给定一个整形数组,是否能找出其中的两个数使得其和为某个指定的值?
-
示例: 输入数组为{1, 5, 7, 3}, 指定值为10, 则我们可以从中找出两个数3和7, 和等于10。
-
# 函数调用格式如下 # -*- coding: UTF-8 -*- def hasSum(array, target): sorted_array = sorted(array) length = len(sorted_array) i, j = (0, length - 1) while i < j: if sorted_array[i] + sorted_array[j] > target: j -= 1 elif sorted_array[i] + sorted_array[j] < target: i += 1 else: return sorted_array[i], sorted_array[j] if __name__ == '__main__': array = [1, 5, 3, 7] target = 4 res = hassum(array, target) print(res)
-
上面的方法只能找到一对和为目标值的数值,针对可能出现多对的情况
def hassum(array, target);
sorted_array = sorted(array) # 默认为升序
i, j = 0, len(array) - 1
result = []
while i < j:
if sorted_array[i] + sorted_array[j] > target:
j -= 1
elif sorted_array[i] + sorted_array[j] < target:
i += 1
else:
result.append((sorted_array[i], sorted_array[j]))
i += 1
return list(set(result)) # 去重
if __name__ == "__main__":
array = [0, 0, 4, 1, 2, 3, 5, 7]
target = 7
res = hassum(array, target)
print(res)
2、***股票买卖
给定一个数组,第i个元素代表第i天的股价。假设最多允许进行1次买卖,求可能的最大利润是多少?
-
示例: 输入price = [12, 15, 14, 8, 11, 10, 12], 则输出最大利润是4。
-
def get_max_profit(price): if price is None or len(price) == 0: return 0 max_profit = 0 min_price = price[0] length = len(price) # 遍历选出最小价格和计算最大的差价 for i in range(1, length): min_price = min(min_price, price[i]) max_profit = max(max_profit, price[i] - min_price) return max_profit # 函数调用格式如下 if __name__ == '__main__': price = [12, 15, 14, 8, 11, 10, 12] result = get_max_profit(price) print(result)
3、***快速排序
-
def quick_sort(array, start, end): # 递归条件 if start >= end: return left, right = start, end pivot = array[start] while left < right: while array[right] >= pivot and left < right: right -= 1 array[left] = array[right] while array[left] <= pivot and left < right: left += 1 array[right] = array[left] # 确认基准所在的位置,数组左侧都小于基准,右侧都大于基准 array[left] = pivot quick_sort(array, start, left - 1) quick_sort(array, right + 1, end) if __name__ == '__main__': array = [4, 5, 5, 3, 3, 1, 6, 8, 7, 2] quick_sort(array, 0, len(array) -1) print(array)
4、蛙跳
蛙跳,给出一维非负元素的数组,每个元素代表该元素位置能够跳的最远距离。假设初始位置在第一个元素,请根据输入数组判断是否能跳到数组的末尾。
-
示例:
- 输入数组 A = [2, 1, 3, 1, 1], 输出True
- 输入数组 A = [3, 2, 1, 0, 1], 输出False
-
A = [6, 2, 1, 1, 2, 1, 1, 0, 1]
-
# 思路:双指针 # 同向移动的俩指针:第一个指针扫描当前值,第二个指针记录当前最远的目的地。 # 扫描的同时,实时更新当前最远目的地。如果当前最远目的地能到达数组尾端,程序返 # 回true。如果当前最远目的地不能到达尾端,而且又不能向前移动的时候,我们认为不 # 可能到达终点,程序返回false。 def frog_jump(arr): # 数组长度 length = len(arr) # 如果数组长度小于等于1,则返回False if length <= 1: return False # 设置双指针,一个记录当前位置值,一个记录当前能跳最远距离 curMax = 0 for i in range(length - 1): # 如果当前位置值为零,当前最远距离无法后移 # 则认为蛙无法继续向前跳 if arr[i] == 0 and curMax < i + 1: return False # 如果当前位置元素非零,且当前最远距离大于之前最远距离 # 则认为蛙可以 继续向后跳 if arr[i] > 0 and arr[i] + i > curMax: curMax = arr[i] + i # 当最远位置大于等于数组长度时,认为蛙可以跳到末尾 if curMax >= length - 1: return True # 如果上述条件都不满足,则返回False return False if __name__ == '__main__': a = [2, 1, 3, 1, 1] b = [3, 2, 1, 0, 1] res = frog_jump(b) print(res) res = frog_jump(a) print(res)
5、数字分水岭
在一维数组中, 找出一个点, 使得其所有左边的数字均小于等于它,所有右边的数字都大于等于它, 返回这个点所在的下标。
思路:建立False矩阵,用于记录比左侧数值都大的位置,然后左遍历寻找比左侧都大的数值,右遍历比右侧数值都小的数,然后通过false矩阵确定最终位置。
-
示例: 输入一维数组A = [1, 0, 1, 0, 1, 2, 3], 返回下标4或5。
def get_point_number(arr): arrLen = len(arr) if arr == None or arrLen == 0: return False #用于记录左遍历时比左侧数都大的值的位置 isCurMax = [False for i in range(arrLen)] # 第一个指针:左遍历最大值 leftMax = arr[0] for i in range(arrLen - 1): if arr[i] >= leftMax: leftMax = arr[i] # 如果比之前最大值还大,则认为大于左侧所有数 isCurrMax[i] = True res = [] # 第二个指针记录从右侧遍历比右侧都小的值 rightMin = arr[arrLen - 1] for i in range(arrLen - 1, -1, -1): if arr[i] <= rightMin: rightMin = arr[i] # 数值满足比其右侧都小,且比其左侧都大 if isCurrMax[i]: res.append(i) return sorted(res) if len(res) > 0 else False if __name__ == '__main__': a = [1, 0, 1, 0, 1, 2, 3] res = get_point_number(a) print(res)
6、找出仅出现一次的数值
给定一个一维整形数组,里面只有一个数字出现了一次,其他数字都出现了2次,请将这个唯一出现了一次的数字找出来。
-
示例: 给定数组 A = [2, 35, 8, 16, 8, 2, 7, 16, 35], 返回唯一只出现了一次的7
-
异或运算性质:
-
任何数和 0做异或运算,结果仍然是原来的数,即 a ⊕ \oplus ⊕ 0=a。
-
任何数和其自身做异或运算,结果是 0,即 a ⊕ \oplus ⊕ a=0。
-
异或运算满足交换律和结合律,即 a ⊕ \oplus ⊕b ⊕ \oplus ⊕ a=b ⊕ \oplus ⊕ a ⊕ \oplus ⊕ a=b ⊕ \oplus ⊕ (a ⊕ \oplus ⊕ a)=b⊕0=b。
-
-
def onlyOneTimeNumber(arr): arrLen = len(arr) if arrLen < 2: return -1 res = 0 for i in range(arrLen): # 异或表达式 res ^= arr[i] return res # 函数调用格式如下 if __name__ == '__main__': array = [2, 35, 8, 16, 8, 2, 7, 16, 35] num = onlyOneTimeNumber(array) print("num=%d," % num)
# from functools import reduce
# def singlenum(array):
# return reduce(lambda x, y: x ^ y, array)
7、求一个二叉树的最大深度。
-
# 类的定义如下 class TreeNode(object): def __init__(self, x): self.val = x self.left = None self.right = None def maxDepth(root): if root is None: return 0 l = maxDepth(root.left) r = maxDepth(root.right) return max(l, r) + 1 # 函数调用格式如下 if __name__ == '__main__': root = TreeNode(5) root.left = TreeNode(3) root.right = TreeNode(4) root.right.right = TreeNode(0) res = maxDepth(root) print(res)
-
求二叉树的最小深度
class Solution:
def minDepth(self, root: TreeNode) -> int:
if not root:
return 0
if not root.left and not root.right:
return 1
min_depth = 10**9
if root.left:
min_depth = min(self.minDepth(root.left), min_depth)
if root.right:
min_depth = min(self.minDepth(root.right), min_depth)
return min_depth + 1
8、***最长回文子串
最长回文子串, 给定一个字符串s, 找到s中最长的回文子串, 假设s的最大长度为1000.
-
示例: s = “helloollyyd”, 则最长回文子串是"llooll", 长度为6
-
思路
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eSgsvJad-1628333885058)(.\image\最长回文子串思路.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-27WF6C57-1628333885062)(.\image\最长回文子串动态规划矩阵.png)]
-
-
def longestPalindrome(s): n = len(s) if n < 2: return s max_len = 1 begin = 0 # dp[i][j] 表示 s[i..j] 是否是回文串 dp = [[False] * n for _ in range(n)] for i in range(n): dp[i][i] = True # 递推开始 # 先枚举子串长度 for L in range(2, n + 1): # 枚举左边界,左边界的上限设置可以宽松一些 for i in range(n): # 由 L 和 i 可以确定右边界,即 j - i + 1 = L 得 j = L + i - 1 # 如果右边界越界,就可以退出当前循环 if j >= n: break if s[i] != s[j]: dp[i][j] = False else: if j - i < 3: dp[i][j] = True else: dp[i][j] = dp[i + 1][j - 1] # 只要 dp[i][L] == true 成立,就表示子串 s[i..L] 是回文,此时记录回文长度和起始位置 if dp[i][j] and j - i + 1 > max_len: max_len = j - i + 1 begin = i return s[begin:begin + max_len] # 函数调用格式如下 if __name__ == '__main__': s = "helloollyyd" res = longestPalindrome(s) print("res=," res)
-
9、保持顺序,移动0至末尾
-
给定一个数组nums, 编写一个函数将所有0移动到数组末尾, 同时保持非零元素的相对顺序不变。
- 示例: 输入数组nums = [0, 1, 0, 3, 12], 输出结果: [1, 3, 12, 0, 0]
- 思路及解法
使用双指针,左指针指向当前已经处理好的序列的尾部,右指针指向待处理序列的头部。
右指针不断向右移动,每次右指针指向非零数,则将左右指针对应的数交换,同时左指针右移。
注意到以下性质:
左指针左边均为非零数;
右指针左边直到左指针处均为零。
复杂度:O(n)
class Solution: def moveZeroes(self, nums: List[int]) -> None: n = len(nums) left = right = 0 while right < n: if nums[right] != 0: nums[left], nums[right] = nums[right], nums[left] left += 1 right += 1
-
def move_array(nums): for num in nums: if num == 0: nums.remove(num) nums.append(num) return nums if __name__ == '__main__': nums = [0, 1, 0, 3, 12] res = move_array(nums) print(res)
10、蛙跳2.0
在之前作业的基础上实现蛙跳2.0,给出一维非负元素的数组,每个元素代表该元素位置能够跳的最远距离。假设初始位置在第一个元素,并假设输入数组能满足到达数组末尾,求最少的跳数是多少?
-
示例: 输入数组 A = [2, 1, 3, 1, 1], 输出2
-
def main(): a = [2, 1, 3, 1, 1] res = frog_jump(a) print(res) def frog_jump(arr): result = 0 # 使用俩指针,分别记录上一轮和本轮的最远目的地 last = 0 curr = 0 for i in range(len(arr)): if i > last: # 如果当前指针越过了上轮最远目的地 # 那么更新上轮指针,并且实施一跳 last = curr result += 1 # 保存本轮的最远目的地值 curr = max(curr, i + arr[i]) return result if __name__ == '__main__': main() # 函数调用格式如下 def main(): a = [2, 1, 3, 1, 1] res = frog_jump(a) print(res) if __name__ == '__main__': main()
11、机器人路径
-
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为"Start")
-
机器人每次只能向下或者向右移动一步, 机器人试图达到网格的右下角(在下图中标记为"Finish")
-
问总共有多少条不同的路径?
-
示例1: 输入: m = 3, n = 2 输出: 3 解释: 从左上角开始,总共有 3 条路径可以到达右下角。 1. 向右 -> 向右 -> 向下 2. 向右 -> 向下 -> 向右 3. 向下 -> 向右 -> 向右 示例2: 输入: m = 7, n = 3 输出: 28 注意: 1 <= m, n <= 100
-
# 函数调用格式如下 def main(): number = allPaths(m=3, n=2) print("number=%d," % number) if __name__ == '__main__': main()
# 函数调用格式如下
def allPaths(m, n):
# 动态规化
matrix = [[0 for i in range(n)] for i in range(m)]
"""
matrix[0][0] = 1
matrix[1][n] = 1
matrix[n][1] = 1
"""
matrix[0][0] = 1
for i in range(1, m):
for j in range(1, n):
if i == 1:
matrix[0][j] = 1
if j == 1:
matrix[i][0] = 1
matrix[i][j] = matrix[i - 1][j] + matrix[i][j - 1]
return matrix[m - 1][n - 1]
def main():
number = allPaths(m=7, n=3)
print("number=%d," % number)
if __name__ == '__main__':
main()
12、最大下标距离
给定一个整形数组, 找出最大下标距离 j - i , 当且仅当 A[i] < A[j] 和 i < j 。
-
示例: 输入数组为[5, 3, 4, 0, 1, 4, 1], 则最大下标距离产生于A[i]=3, i=1和A[j]=4, j=5的时候, 此时j - i = 4, 这个4就是满足条件的最大下标距离。
-
# 函数调用格式如下 def main(): array = [5, 3, 4, 0 ,1, 4, 1] dis = maxDistance(array) print("dis=%d," % dis) if __name__ == '__main__': main()
13、***爬楼梯
假设你正在爬楼梯, 需要 n 阶你才能到达楼顶. 每次你可以爬 1 或 2 个台阶. 你有多少种不同的方法可以爬到楼顶呢?
-
**注意:**给定 n 是一个正整数.
-
示例1: 输入: 2 输出: 2 解释: 有两种方法可以爬到楼顶。 1. 1 阶 + 1 阶 2. 2 阶 示例2: 输入: 3 输出: 3 解释: 有三种方法可以爬到楼顶。 1. 1 阶 + 1 阶 + 1 阶 2. 1 阶 + 2 阶 3. 2 阶 + 1 阶
'''
标签:动态规划
本问题其实常规解法可以分成多个子问题,爬第n阶楼梯的方法数量,等于 2 部分之和
倒数第二步爬上 n-1 阶楼梯的方法数量。因为再爬1阶就能到第n阶
倒数第二步爬上 n-2 阶楼梯的方法数量,因为再爬2阶就能到第n阶
所以我们得到公式 arr[n] = arr[n-1] + arr[n-2]
同时需要初始化 arr[1]=1 和 arr[2]=2
时间复杂度:O(n)
'''
def methods(n):
if n == 1:
return '输入 %d 不符合题意' % n
arr = [0 for i in range(n + 1)]
i = 0
while i <= n:
if i == 0 or i == 1:
arr[i] = 1
i += 1
continue
arr[i] = arr[i - 1] + arr[i - 2]
i += 1
# print(arr)
return arr[n]
def main():
variable = methods(n=3)
print("dis=%d," % variable)
if __name__ == '__main__':
main()
14、***最长公共子序列
给定两个字符串S1、S2,求两个字符串的最长公共子序列
-
递归方法
- 因为求最长,所以从字符串末尾进行比较
- 如果末尾相同,则保留
- 再从倒数第二位开始比较
- 如果不同,分两种情况
- 删除S1的末尾元素,用倒数第二位同S2的末尾元素,进行比较
- 删除S2的末尾元素,用倒数第二位同S1的末尾元素,进行比较
- 重复上面步骤
def LCS(s1, s2): # 判断是否位空 if s1 == '' or s2 == '': return '' # 比较末位元素 elif s1[-1] == s2[-1]: return LCS(s1[:-1],s2[:-1]) + s1[-1] else: # 如果不相等,分两种情况 res_a = LCS(s1[:-1], s2) res_b = LCS(s1, s2[:-1]) if len(res_a) > len(res_b): return res_a return res_b if __name__ == "__main__": a = 'abcd' b = 'aebd' print(LCS(a, b))
-
动态规划
- L
15、二叉树锯齿形层次遍历(Z字型遍历算法)
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
class Solution(object):
def zigzaglevelorder(self, root):
res = []
que = [root]
if root == None:
return res
while que:
tempList = []
for i in range(len(que)):
node = que.pop(0)
tempList.append(node.val)
if node.left:
que.append(node.left)
if node.right:
que.append(node.right)
res.append(tempList)
temp = []
for i in range(len(que)):
if i % 2 == 0:
temp.append(res[i])
else:
temp.append(res[i][::-1])
return (temp)
16、判断压栈弹出顺序
输入两个整数序列,第一个序列表示站的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列(注意:这两个序列的长度是相等的)
- 栈特点:先进后出–栈顶元素先出
- 思路:
- 借用一个辅助栈,遍历压栈顺序,先将第一个放入栈中,这里是1,然后判断栈顶元素是不是出栈顺序的第一个元素,这里是4,很显然1≠4,所以我们继续压栈,直到相等以后开始出栈。出栈一个元素,则将出栈顺序向后移动一位,直到不相等,这样循环等压栈顺序遍历完成,如果辅助栈还不为空,说明弹出序列不是该栈的弹出顺序。
def validateStackSequences(self, pushed, popped):
stack, i = [], 0
for num in pushed:
stack.append(num) # num 入栈
while stack and stack[-1] == popped[i]: # 循环判断与出栈
stack.pop()
i += 1
# stack 为空则返回True,非空则为false
return not stack
if __name__ == '__main__':
pushed = [1,2,3,4,5]
poped = [4,3,5,1,2]
res = validateStackSequences(pushed, poped)
print(res)
17、字符串转换成树结构
输入是一个字符串形式表达的树,需要把它转换成一个真正的树结构。
例如: a
# b c
# d e f <---"(a(b(de)c(f)))"
18、在一个乱序的数组中找第K大的数字
19、反转链表
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点
class ListNode:
"""链表数据结构"""
def __init__(self, x, next=None):
self.val = x
self.next = next
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
# 定义尾部节点,默认为None
last = None
# 当传入的头部节点不为None时,即没有反转完成时
while head:
# 连续赋值,等效于:将等号右边的变量全部暂存为中间变量, 再赋值给右侧,顺序从左至右
# eg: l = last, h = head, hn = head.next, head.next = l, last = h, head = hn
# 按照反转链表的定义,反转后新链表的最后一个节点是当前的head节点,所以对于新链表head.next=None(last的默认值)
# 而last是一直变动的,处理完新链表的尾部后,从head开始,所以有last=head
# last的遍历过程在改变着链表,我们是使用head来限制其应该何时结束,因此head也应该逐次变化,直到head.next指向None
# 因此head变化为:head = head.next
head.next, last, head = last, head, head.next
return last