import collections
import random
from collections import defaultdict
class Ten_classics_arithmetic(object):
'''十大经典算法'''
def bubbleSort(self, nums):
'''冒泡排序'''
for i in range(len(nums) - 1): # 遍历 len(nums)-1 次
for j in range(len(nums) - i - 1): # 已排好序的部分不用再次遍历
if nums[j] > nums[j + 1]:
nums[j], nums[j + 1] = nums[j + 1], nums[j] # Python 交换两个数不用中间变量
return nums
def selectionSort(self, nums):
'''
选择排序
每次选出最小的元素,然后交换元素放到前面
'''
for i in range(len(nums) - 1): # 遍历 len(nums)-1 次
minIndex = i
for j in range(i + 1, len(nums)):
if nums[j] < nums[minIndex]: # 更新最小值索引
minIndex = j
nums[i], nums[minIndex] = nums[minIndex], nums[i] # 把最小数交换到前面
return nums
def insertionSort(self, nums):
'''
插入排序
待插入的数与待插入之前的数作比较,将大的数往后移动
'''
for i in range(len(nums) - 1): # 遍历 len(nums)-1 次
curNum, preIndex = nums[i + 1], i # curNum 保存当前待插入的数
while preIndex >= 0 and curNum < nums[preIndex]: # 将比 curNum 大的元素向后移动
nums[preIndex + 1] = nums[preIndex]
preIndex -= 1
nums[preIndex + 1] = curNum # 待插入的数的正确位置
return nums
def shellSort(self, nums):
'''
希尔排序
希尔排序是插入排序的一种更高效率的实现,它与插入排序的不同之处在于,它会优先比较距离较远的元素。
'''
lens = len(nums)
gap = 1
while gap < lens // 3:
gap = gap * 3 + 1 # 动态定义间隔序列
while gap > 0:
for i in range(gap, lens):
curNum, preIndex = nums[i], i - gap # curNum 保存当前待插入的数
while preIndex >= 0 and curNum < nums[preIndex]:
nums[preIndex + gap] = nums[preIndex] # 将比 curNum 大的元素向后移动
preIndex -= gap
nums[preIndex + gap] = curNum # 待插入的数的正确位置
gap //= 3 # 下一个动态间隔
return nums
def mergeSort(self, nums):
'''
归并过程
分而治之思想
'''
def merge(left, right):
result = [] # 保存归并后的结果
i = j = 0
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 = result + left[i:] + right[j:] # 剩余的元素直接添加到末尾
return result
# 递归过程
if len(nums) <= 1:
return nums
mid = len(nums) // 2
left = self.mergeSort(nums[:mid])
right = self.mergeSort(nums[mid:])
return merge(left, right)
def quickSort(self, nums): # 这种写法的平均空间复杂度为 O(nlogn)
'''
快速排序
本质上来看,快速排序应该算是在冒泡排序基础上的递归分治法.
'''
if len(nums) <= 1:
return nums
pivot = nums[0] # 基准值
left = [nums[i] for i in range(1, len(nums)) if nums[i] < pivot]
right = [nums[i] for i in range(1, len(nums)) if nums[i] >= pivot]
return self.quickSort(left) + [pivot] + self.quickSort(right)
def countingSort(self, nums):
'''
计数排序
在列表中记录出现的个数,最后再生成列表
'''
bucket = [0] * (max(nums) + 1) # 桶的个数
for num in nums: # 将元素值作为键值存储在桶中,记录其出现的次数
bucket[num] += 1
i = 0 # nums 的索引
for j in range(len(bucket)):
while bucket[j] > 0:
nums[i] = j
bucket[j] -= 1
i += 1
return nums
def binary_search(self, nums, target):
'''二分查找'''
nums.sort()
i, j = 0, len(nums) - 1
while i <= j:
mid = (i + j) // 2
if nums[mid] < target:
i = mid + 1
elif nums[mid] > target:
j = mid - 1
else:
return mid
return None
class List_arithmetic(object):
'''数组相关'''
def spiralOrder(self, matrix):
'''
:detail : 给定一个包含 m x n 个元素的矩阵(m 行, n 列),请按照顺时针螺旋顺序,返回矩阵中的所有元素。
输入:
[ [ 4, 5, 6 ],
[ 7, 8, 9 ]]
输出: [1,2,3,6,9,8,7,4,5]
:param matrix: list
:return: list
'''
if not matrix:
return []
anw = []
while True:
if not matrix: return anw
anw = anw + matrix.pop(0)
right = [row.pop() for row in matrix if row]
anw = anw + right
anw = anw + matrix.pop()[::-1] if matrix else anw
left = [row.pop(0) for row in matrix if row]
anw = anw + left[::-1]
class Shuffle_arithmetic(object):
'''洗牌算法'''
def shuffle(self, nums, number=None):
'''
:param nums: 列表
:param number: 需要获取的个数
:return: list
'''
length = len(nums)
number = length if not number else number
for i in range(length):
random_number = random.randint(i, length - 1)
nums[i], nums[random_number] = nums[random_number], nums[i]
if i == number - 1:
return nums[:i + 1]
return nums
res = Shuffle__arithmetic().shuffle([1, 3, 5, 7, 9], )
print(res)
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
class Pool_arithmetic(object):
'''蓄水池算法'''
def __init__(self, head: ListNode):
self.head = head
def get_random(self):
"""
:return: int
"""
count = 0
res = 0
cur = self.head
while cur:
if random.randint(0, count) < 1:
res = cur.val
cur = cur.next
count += 1
return res
class Dynamic_arithmetic(object):
'''动态规划算法'''
def climbStairs(self, n):
'''
:detail : 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? 注意:给定 n 是一个正整数。
:param n: n个台阶
:return: int
'''
if n <= 2:
return n
a, b = 1, 2 # 这是边界,而不是题目中所提的1步,2步
# tip 如果可以1 2 3 步 则为a,b,c=1,2,4
for i in range(3, n + 1):
a, b = b, a + b # 最优子结构
return b
def maxSubArray(self, nums) -> int:
'''
:detail : 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
:param nums: 整数数组
:return: int
'''
for i in range(1, len(nums)):
nums[i] = nums[i] + max(nums[i - 1], 0)
return max(nums)
def rob(self, nums):
'''
:detail: 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。
示例 1:
输入: [1,2,3,1]
输出: 4
解释: 偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3), 偷窃到的最高金额 = 1 + 3 = 4 。
:param nums: int
:return:
'''
if len(nums) == 0:
return 0
n = len(nums)
dp = [0, 0]
dp[1] = nums[0]
for i in range(2, n + 2):
dp.append(max(dp[i - 2] + nums[i - 1], dp[i - 1]))
return max(dp)
def uniquePathsWithObstacles(self, obstacleGrid):
'''
:detail: 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
网格中的障碍物和空位置分别用 1 和 0 来表示。
:param obstacleGrid: 多维数组
:return: int
'''
m = len(obstacleGrid)
n = len(obstacleGrid[0])
for i in range(m):
for j in range(n):
temp = obstacleGrid[i][j]
if temp == 0:
if i == j == 0:
obstacleGrid[i][j] = 1
else:
left = obstacleGrid[i][j - 1] if j > 0 else 0
up = obstacleGrid[i - 1][j] if i > 0 else 0
obstacleGrid[i][j] = left + up
else:
obstacleGrid[i][j] = 0
print(obstacleGrid)
return obstacleGrid[-1][-1]
def respace(self, dictionary, sentence: str) -> int:
'''
:detail: 恢复空格
示例:
输入:
dictionary = ["looked","just","like","her","brother"]
sentence = "jesslookedjustliketimherbrother"
输出: 7 解释: 断句后为"jess looked just like tim her brother",共7个未识别字符。
:param dictionary: list
:param sentence: str
:return: int
'''
d = {}.fromkeys(dictionary)
n = len(sentence)
f = [0] * (n + 1)
for i in range(1, n + 1):
f[i] = f[i - 1] + 1
for j in range(i):
if sentence[j:i] in d:
f[i] = min(f[i], f[j])
return f[-1]
# Definition for a binary tree node.
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Tree_arithmetic(object):
'''二叉树算法'''
def __init__(self):
'''
二叉树前序遍历的顺序为:
先遍历根节点;
随后递归地遍历左子树;
最后递归地遍历右子树。
二叉树中序遍历的顺序为:
先递归地遍历左子树;
随后遍历根节点;
最后递归地遍历右子树。
二叉树后序遍历的顺序为:
先递归地遍历左子树;
随后递归地遍历右子树;
最后遍历根节点。
对于任意一颗树而言,前序遍历的形式总是:
[ 根节点, [左子树的前序遍历结果],[右子树的前序遍历结果] ]
中序遍历的形式总是:
[ [左子树的中序遍历结果],根节点, [右子树的中序遍历结果] ]
后序遍历的形式总是:
[ [左子树的后序遍历结果],[右子树的后序遍历结果],根节点]
'''
def preorderTraversalI(self, root: TreeNode):
if not root:
return []
res = []
stack = [root]
# # 前序迭代模板:最常用的二叉树DFS迭代遍历模板
while stack:
cur = stack.pop()
res.append(cur.val)
if cur.right:
stack.append(cur.right)
if cur.left:
stack.append(cur.left)
return res
# # 后序迭代,相同模板:将前序迭代进栈顺序稍作修改,最后得到的结果反转
# while stack:
# cur = stack.pop()
# if cur.left:
# stack.append(cur.left)
# if cur.right:
# stack.append(cur.right)
# res.append(cur.val)
# return res[::-1]
def preorderTraversalII(self, root: TreeNode):
if not root:
return []
# 前序递归
return [root.val] + self.preorderTraversalII(root.left) + self.preorderTraversalII(root.right)
# # 中序递归
# return self.inorderTraversal(root.left) + [root.val] + self.inorderTraversal(root.right)
# # 后序递归
# return self.postorderTraversal(root.left) + self.postorderTraversal(root.right) + [root.val]
def preorderTraversalIII(self, root: TreeNode):
def dfs(cur):
if not cur:
return
# 前序递归
res.append(cur.val)
dfs(cur.left)
dfs(cur.right)
# # 中序递归
# dfs(cur.left)
# res.append(cur.val)
# dfs(cur.right)
# # 后序递归
# dfs(cur.left)
# dfs(cur.right)
# res.append(cur.val)
res = []
dfs(root)
return res
def levelOrder(self, root: TreeNode):
'''
:param root: root树
:return: 每一层的值信息 eg:[[2],[1,3]]
'''
if not root:
return []
cur, res = [root], []
while cur:
lay, layval = [], []
for node in cur:
layval.append(node.val)
if node.left: lay.append(node.left)
if node.right: lay.append(node.right)
cur = lay
res.append(layval)
return res
def preorder(self, root):
'''
:detail : 多叉树的遍历
:param root:
:return:
'''
res = []
def helper(root):
if not root:
return
res.append(root.val)
for child in root.children:
helper(child)
helper(root)
return res
def convertBST(self, root: TreeNode) -> TreeNode:
'''
:detail : 给定一个二叉搜索树(Binary Search Tree),把它转换成为累加树(Greater Tree),使得每个节点的值是原来的节点值加上所有大于它的节点值之和。
链接:https://leetcode-cn.com/problems/convert-bst-to-greater-tree
:param root: root tree
:return: root tree
'''
cur = 0
def dfs(root):
nonlocal cur
if not root: return
dfs(root.right)
cur += root.val
root.val = cur
dfs(root.left)
dfs(root)
return root
def mergeTrees(self, t1: TreeNode, t2: TreeNode) -> TreeNode:
'''两颗数合并'''
if not t1:
return t2
if not t2:
return t1
merged = TreeNode(t1.val + t2.val)
merged.left = self.mergeTrees(t1.left, t2.left)
merged.right = self.mergeTrees(t1.right, t2.right)
return merged
class BFS_arithmetic(object):
'''广度优先搜索(BFS)'''
def orangesRotting(self, grid) -> int:
'''
:detail:
在给定的网格中,每个单元格可以有以下三个值之一:
值 0 代表空单元格;
值 1 代表新鲜橘子;
值 2 代表腐烂的橘子。
每分钟,任何与腐烂的橘子(在 4 个正方向上)相邻的新鲜橘子都会腐烂。
链接:https://leetcode-cn.com/problems/rotting-oranges
:param grid: 多维数组
:return: time
'''
row = len(grid)
col = len(grid[0])
rotten = {(i, j) for i in range(row) for j in range(col) if grid[i][j] == 2} # 腐烂集合
fresh = {(i, j) for i in range(row) for j in range(col) if grid[i][j] == 1} # 新鲜集合
time = 0
while fresh:
if not rotten: return -1
temp_rotten = []
for di, dj in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
for i, j in rotten:
if (i + di, j + dj) in fresh:
temp_rotten.append((i + di, j + dj))
rotten = set(temp_rotten) # 即将腐烂的如果在新鲜的集合中,就将它腐烂
fresh -= rotten # 剔除腐烂的
time += 1
return time
class Tracl_arithmetic(object):
'''回溯算法'''
def __init__(self):
self.directions = [(0, -1), (-1, 0), (0, 1), (1, 0)]
def exist(self, board, word):
m = len(board)
if not m:
return False
n = len(board[0])
check = [[False for _ in range(n)] for _ in range(m)]
for i in range(m):
for j in range(n):
if self.search_word(0, i, j, check, board, word, m, n):
return True
return False
def search_word(self, index, start_x, start_y, check, board, word, m, n):
# 终止条件
if len(word) - 1 == index:
return board[start_x][start_y] == word[index]
if board[start_x][start_y] == word[index]:
check[start_x][start_y] = True
for x, y in self.directions:
new_x = start_x + x
new_y = start_y + y
if 0 <= new_x < m and 0 <= new_y < n and not check[new_x][new_y] and \
self.search_word(index + 1, new_x, new_y, check, board, word, m, n):
return True
check[start_x][start_y] = False
return False
class List_arithmetic(object):
'''字符串相关算法'''
def checkPossibility(self, nums) -> bool:
'''
:detail :给你一个长度为 n 的整数数组,请你判断在 最多 改变 1 个元素的情况下,该数组能否变成一个非递减数列。
示例 1:
输入: nums = [4,2,3]
输出: true
解释: 你可以通过把第一个4变成1来使得它成为一个非递减数列。
url: https://leetcode-cn.com/problems/non-decreasing-array/
:param nums: [2,4,0,1]
:return: Boolean
'''
length = len(nums)
count = 0
for i in range(1, length):
if nums[i] < nums[i - 1]:
count += 1
if i + 1 < length and i - 2 >= 0:
if nums[i + 1] < nums[i - 1] and nums[i] < nums[i - 2]:
return False
if count > 1:
return False
return True
def removedigits(self, s):
'''
: detail:
316. 去除重复字母(困难)
题目描述
给你一个仅包含小写字母的字符串,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证返回结果的字典序最小(要求不能打乱其他字符的相对位置)。
输入: "cbacdcbc"
输出: "acdb"
链接:https://leetcode-cn.com/problems/remove-duplicate-letters/solution/yi-zhao-chi-bian-li-kou-si-dao-ti-ma-ma-zai-ye-b-4/
:param s:
:return:
'''
dict_ = collections.Counter(s)
stack = []
seen = set()
for n in s:
if n not in seen:
while stack and n < stack[-1] and dict_[stack[-1]] > 0:
seen.discard(stack.pop())
seen.add(n)
stack.append(n)
dict_[n] -= 1
return ''.join(stack)
class SlipWindow_arithmetic(object):
'''滑动窗口'''
def __init__(self):
'''
什么是滑动窗口?
其实就是一个队列,比如例题中的 abcabcbb,进入这个队列(窗口)为 abc 满足题目要求,当再进入 a,队列变成了
abca,这时候不满足要求。所以,我们要移动这个队列!
如何移动?
我们只要把队列的左边的元素移出就行了,直到满足题目要求!
'''
def lengthOfLongestSubstring(self, s: str) -> int:
'''
:detail : 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/
:param s: 字符串
:return: number
'''
if not s:
return 0
lst = [[0] for i in range(len(s))]
temp = ''
for i, x in enumerate(s):
while x in temp:
temp = temp[1:]
temp += x
lst[i] = len(temp)
return max(lst)
def minWindow(self, s: str, t: str) -> str:
'''
:detail :
给你一个字符串 S、一个字符串 T 。请你设计一种算法,可以在 O(n) 的时间复杂度内,从字符串 S 里面找出:包含 T 所有字符的最小子串。
示例:
输入:S = "ADOBECODEBANC", T = "ABC"
输出:"BA
:param s: 字符串
:param t: 字符串
:return: 字符串
'''
dict_ = defaultdict(int)
count = len(t)
res = ''
min_len = float('inf')
for x in t:
dict_[x] += 1
start, end, length = 0, 0, len(s)
while end < length:
if dict_[s[end]] > 0:
count -= 1
dict_[s[end]] -= 1
end += 1
while count == 0:
if min_len > end - start:
res = s[start: end]
min_len = end - start
if dict_[s[start]] == 0:
count += 1
dict_[s[start]] += 1
start += 1
return res
class Link_arithmetic(object):
'''链表'''
def create_link(self, res):
'''创建链表'''
head = ListNode(-1)
p = head
while res:
p.next = ListNode(res.pop())
p = p.next
def hasCycle(self, head: ListNode) -> bool:
'''
:detail : 给定一个链表,判断链表中是否有环。
思路: 遍历链表,每次遍历到一个新的结点时,判断该结点是否在集合set中,如果在集合set中说明链表有环,否则记录在set(集合)中,继续遍历。
url: https://leetcode-cn.com/problems/linked-list-cycle/solution/zhe-shi-yi-ge-you-qu-de-shi-pin-ti-jie-kuai-man-zh/
:param head:
:return:
'''
if head is None:
return False
node = head
nodeset = set()
while node:
if node in nodeset:
return True
else:
nodeset.add(node)
node = node.next
return False
def hasCycleII(self, head: ListNode) -> bool:
'''
:detail : 给定一个链表,判断链表中是否有环。
思路: 根据快慢指针是否会相遇进行判断
url: https://leetcode-cn.com/problems/linked-list-cycle/solution/zhe-shi-yi-ge-you-qu-de-shi-pin-ti-jie-kuai-man-zh/
:param head:
:return:
'''
if head is None:
return False
fastnode = head
slownode = head
while (fastnode):
if fastnode.next and fastnode.next.next:
fastnode = fastnode.next.next
slownode = slownode.next
else:
return False
if fastnode == slownode:
return True
return False
def reverseList(self,head):
'''链表反转
https://blog.csdn.net/gongliming_/article/details/88712221
'''
if head == None or head.next == None: # 若链表为空或者仅一个数就直接返回
return head
pre = None
while (head != None):
next = head.next # 1
head.next = pre # 2
pre = head # 3
head = next # 4
return pre
Python总结常见算法题
最新推荐文章于 2024-03-15 17:35:43 发布