python刷题算法汇总

一:Python程序员面试算法宝典

1. 二叉树

  1. # @Filename: trie树实现DNS缓存查找.py
  2. class TrieNode:
  3. def __init__(self):
  4. CHAR_COUNT = 11
  5. self.isLeaf = False
  6. self.url = None
  7. self.child = [None] * CHAR_COUNT
  8. for i in range(len(self.child)):
  9. self.child[i] = None
  10. i += 1
  11. def getIndexFromChar(c):
  12. # ord()返回ascii码
  13. return 10 if c == "." else (ord(c) - ord('0'))
  14. def getCharFromIndex(i):
  15. return '.' if i == 10 else ('0' + str(i))
  16. class DNSCache:
  17. def __init__(self):
  18. self.CHAR_COUNT = 11
  19. self.root = TrieNode()
  20. def insert(self, ip, url):
  21. lens = len(ip)
  22. pCrawl = self.root
  23. level = 0
  24. while level < lens:
  25. index = getIndexFromChar(ip[level])
  26. if pCrawl.child[index] == None:
  27. pCrawl.child[index] = TrieNode()
  28. pCrawl = pCrawl.child[index]
  29. level += 1
  30. pCrawl.isLeaf = True
  31. pCrawl.url = url
  32. def searchDNSCache(self, ip):
  33. pCrawl = self.root
  34. lens = len(ip)
  35. level = 0
  36. while level < lens:
  37. index = getIndexFromChar(ip[level])
  38. if pCrawl.child[index] == None:
  39. return None
  40. pCrawl = pCrawl.child[index]
  41. print(pCrawl.url)
  42. level += 1
  43. if pCrawl != None and pCrawl.isLeaf:
  44. return pCrawl.url
  45. return None
  46. if __name__ == "__main__":
  47. ipAdds = ["10.57.11.127", "121.57.61.129", "66.125.100.103", "10.57.11.122"]
  48. # ipAdds = ["10.57.11.127", "10.57.11.122"]
  49. # url = ["www.samsung.com", "www.huawei.com"]
  50. url = ["www.samsung.com", "www.samsung.net", "www.google.in", "www.huawei.com"]
  51. n = len(ipAdds)
  52. cache = DNSCache()
  53. for i in range(n):
  54. cache.insert(ipAdds[i], url[i])
  55. print(i)
  56. ip = "10.57.11.127"
  57. res_url = cache.searchDNSCache(ip)
  58. print(res_url)
  1. # @Filename: 二叉树最大子树和.py
  2. # 想到将所有子树进行求和,然后进行最大值对比,由于后续遍历会访问所有的子树,所以采用后续遍历,同时记录每颗子树的和
  3. # 只能找到最大值,怎么才算找到最大子树呢?
  4. class BiTree:
  5. def __init__(self):
  6. self.data = None
  7. self.lchild = None
  8. self.rchild = None
  9. class Test:
  10. def __init__(self):
  11. self.maxSum = -2 ** 31
  12. def constructTree(self):
  13. root = BiTree()
  14. node1 = BiTree()
  15. node2 = BiTree()
  16. node3 = BiTree()
  17. node4 = BiTree()
  18. root.data = 6
  19. node1.data = 3
  20. node2.data = -7
  21. node3.data = -1
  22. node4.data = 9
  23. root.lchild = node1
  24. root.rchild = node2
  25. node1.lchild = node3
  26. node1.rchild = node4
  27. return root
  28. def findMaxSubTree(self, root, maxRoot):
  29. if not root:
  30. return 0
  31. sum_l = self.findMaxSubTree(root.lchild, maxRoot)
  32. sum_r = self.findMaxSubTree(root.rchild, maxRoot)
  33. sums = sum_l + sum_r + root.data
  34. if sums > self.maxSum:
  35. self.maxSum = sums
  36. maxRoot.data = root.data
  37. return sums
  38. if __name__ == "__main__":
  39. test = Test()
  40. root = test.constructTree()
  41. # 创建一个节点用于记录最大子树
  42. maxRoot = BiTree()
  43. test.findMaxSubTree(root, maxRoot)
  44. # test.findMaxSubTree2(root, 0, maxRoot)
  45. print("最大子树和为:", test.maxSum)
  46. print("最大子树节点为:", maxRoot.data)
  1. # @Filename: 二叉树的层次遍历.py
  2. # 二叉树的层次遍历,将每次访问到的节点入队列,访问出栈时,将他的孩子节点入队列
  3. from collections import deque
  4. class BiTree:
  5. def __init__(self):
  6. self.data = None
  7. self.lchild = None
  8. self.rchild = None
  9. def arrtotree(arr):
  10. if not arr:
  11. return
  12. bt = BiTree()
  13. middle = len(arr) // 2
  14. bt.data = arr[middle]
  15. bt.lchild = arrtotree(arr[:middle])
  16. bt.rchild = arrtotree(arr[middle + 1:])
  17. return bt
  18. def printTreeLayer(root):
  19. if not root:
  20. return
  21. queue = deque()
  22. queue.append(root)
  23. while queue:
  24. root = queue.popleft()
  25. print(root.data, end=" ")
  26. if root.lchild:
  27. queue.append(root.lchild)
  28. if root.rchild:
  29. queue.append(root.rchild)
  30. if __name__ == "__main__":
  31. arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  32. root = arrtotree(arr)
  33. printTreeLayer(root)
  1. # @Filename: 判断一个数组是否是二元查找树后序遍历.py
  2. """
  3. 二元查找树后序遍历特性
  4. 1. 最后访问节点一定为根节点
  5. 2. 比根节点大的且最先访问的节点作为分割点,前面的点都比根节点<=,该点和后续节都比根节点>=
  6. """
  7. def IsAfterOrder(arr, start, end):
  8. if not arr:
  9. return
  10. # 找到比end的值大的最先的那个值的下标
  11. root = arr[end]
  12. i = start
  13. while i < end:
  14. if arr[i] > root:
  15. break
  16. i += 1
  17. j = i
  18. # 判定条件
  19. while j < end:
  20. if arr[j] < root:
  21. return False
  22. j += 1
  23. left_IsAfterOrder = True
  24. right_IsAfterOrder = True
  25. if i > start:
  26. left_IsAfterOrder = IsAfterOrder(arr, start, i - 1)
  27. if j < end:
  28. right_IsAfterOrder = IsAfterOrder(arr, i, end)
  29. return left_IsAfterOrder and right_IsAfterOrder
  30. if __name__ == "__main__":
  31. arr = [1, 3, 2, 5, 7, 6, 4]
  32. result = IsAfterOrder(arr, 0, len(arr) - 1)
  33. print(result)
  1. # @Filename: 判断两颗二叉树是否相等.py
  2. # @Software: PyCharm
  3. # @e_mail: sancica@163.com
  4. """
  5. 两颗二叉树相等"
  6. 1. 当前点相等
  7. 2. 左子树相等、右子树相等
  8. 递归下去
  9. """
  10. class BiTree:
  11. def __init__(self):
  12. self.data = None
  13. self.lchild = None
  14. self.rchild = None
  15. def isEqual(root1, root2):
  16. if not root1 and not root2:
  17. return True
  18. if root1 and not root2:
  19. return False
  20. if not root1 and root2:
  21. return False
  22. # 上面判断结构相等
  23. # 下面判断数据相等
  24. if root1.data == root2.data:
  25. return isEqual(root1.lchild, root2.lchild) and isEqual(root1.rchild, root2.rchild)
  26. else:
  27. return False
  28. def constructTree():
  29. root = BiTree()
  30. node1 = BiTree()
  31. node2 = BiTree()
  32. node3 = BiTree()
  33. node4 = BiTree()
  34. root.data = 3
  35. node1.data = 6
  36. node2.data = 6
  37. node3.data = 6
  38. node4.data = 6
  39. root.lchild = node1
  40. root.rchild = node2
  41. node1.rchild = node3
  42. node2.rchild = node4
  43. return root
  44. if __name__ == "__main__":
  45. root1 = constructTree()
  46. root1.data = 9999
  47. root2 = constructTree()
  48. print(isEqual(root1, root2))
  1. # @Filename: 在二叉排序数中找出第一个大于中间值的节点.py
  2. # 1. 找到最小值和最大值,分别为lchild.lchild.。。。和rchild.rchild.。。。直到为None
  3. # 2. 利用中序遍历,由于是有顺序的,直接比较即可
  4. class BiTree:
  5. def __init__(self):
  6. self.data = None
  7. self.lchild = None
  8. self.rchild = None
  9. def arrtotree(arr):
  10. if not arr:
  11. return
  12. bt = BiTree()
  13. middle = len(arr) // 2
  14. bt.data = arr[middle]
  15. bt.lchild = arrtotree(arr[:middle])
  16. bt.rchild = arrtotree(arr[middle + 1:])
  17. return bt
  18. def getMinNode(root):
  19. if not root:
  20. return
  21. while root.lchild:
  22. root = root.lchild
  23. return root
  24. def getMaxNode(root):
  25. if not root:
  26. return
  27. while root.rchild:
  28. root = root.rchild
  29. return root
  30. def getNode(root):
  31. min = getMinNode(root).data
  32. max = getMaxNode(root).data
  33. mid = (min + max) / 2
  34. result = None
  35. # 直接通过比较大小找到相应节点
  36. while root != None:
  37. if root.data <= mid:
  38. root = root.rchild
  39. else:
  40. result = root
  41. root = root.lchild
  42. return result
  43. if __name__ == "__main__":
  44. arr = [1, 2, 3, 4, 5, 6, 7]
  45. root = arrtotree(arr)
  46. print(getNode(root).data)
  1. # @Filename: 在二叉树中找出与输入整数相等的所有路径.py
  2. class BiTNode:
  3. def __init__(self):
  4. self.data = None
  5. self.lchild = None
  6. self.rchild = None
  7. def constructTree():
  8. root = BiTNode()
  9. node1 = BiTNode()
  10. node2 = BiTNode()
  11. node3 = BiTNode()
  12. node4 = BiTNode()
  13. root.data = 6
  14. node1.data = 3
  15. node2.data = 2
  16. node3.data = -1
  17. node4.data = 9
  18. root.lchild = node1
  19. root.rchild = node2
  20. node1.lchild = node3
  21. node1.rchild = node4
  22. return root
  23. def FindRoad(root, num, sums, v):
  24. sums += root.data
  25. v.append(root.data)
  26. # 如果必须是根节点到叶子节点的路径,则添加左右子树为None的判断,否则,不用添加
  27. # if not root.lchild and not root.rchild and num == sums:
  28. if num == sums:
  29. for i in v:
  30. print(i, end=" ")
  31. print()
  32. if root.lchild:
  33. FindRoad(root.lchild, num, sums, v)
  34. if root.rchild:
  35. FindRoad(root.rchild, num, sums, v)
  36. # 清除当前节点数据
  37. sums -= root.data
  38. v.pop()
  39. if __name__ == "__main__":
  40. root = constructTree()
  41. FindRoad(root, 8, 0, [])
  1. # @Filename: 在二叉树中找出路径最大的和.py
  2. # 这里的路径包含任意节点作为起点和终点
  3. """
  4. 分析:采用后续遍历,一次计算左子树最大节点和(连续的路径),右子树最大节点和(连续的路径),以及最大节点和(局部连续)
  5. """
  6. class BiTNode:
  7. def __init__(self, val):
  8. self.val = val
  9. self.lchild = None
  10. self.rchild = None
  11. class InfRef:
  12. def __init__(self):
  13. self.val = None
  14. def Max(a, b, c, d):
  15. maxs = a if a > b else b
  16. maxs = maxs if maxs > c else c
  17. maxs = maxs if maxs > d else d
  18. return maxs
  19. def findMaxPathRecursive(root, maxs):
  20. if not root:
  21. return 0
  22. sum_left = findMaxPathRecursive(root.lchild, maxs)
  23. sum_right = findMaxPathRecursive(root.rchild, maxs)
  24. # 这里需要加上本身节点,才能在上一层变成连续的路径
  25. allMax = root.val + sum_left + sum_right
  26. rightMax = root.val + sum_right
  27. leftMax = root.val + sum_left
  28. # 同时统计本身是否比左、右、左加右都还大
  29. tmpMax = Max(allMax, rightMax, leftMax, root.val)
  30. # 更新最大值
  31. if tmpMax > maxs.val:
  32. maxs.val = tmpMax
  33. subMax = sum_left if sum_left > sum_right else sum_right
  34. return root.val + subMax
  35. def findMaxPath(root):
  36. maxs = InfRef()
  37. maxs.val = -2 ** 31
  38. findMaxPathRecursive(root, maxs)
  39. return maxs.val
  40. if __name__ == "__main__":
  41. root = BiTNode(10)
  42. left = BiTNode(-1)
  43. right = BiTNode(-2)
  44. root.lchild = left
  45. root.rchild = right
  46. left1 = BiTNode(5)
  47. right1 = BiTNode(4)
  48. left.lchild = left1
  49. left.rchild = right1
  50. print(findMaxPath(root))
  1. # @Filename: 复制二叉树.py
  2. class BiTNode:
  3. def __init__(self):
  4. self.data = None
  5. self.lchild = None
  6. self.rchild = None
  7. def createDupTree(root):
  8. if not root:
  9. return None
  10. dupTree = BiTNode()
  11. dupTree.data = root.data
  12. dupTree.lchild = createDupTree(root.lchild)
  13. dupTree.rchild = createDupTree(root.rchild)
  14. return dupTree
  15. def printTreeMidOrder(root):
  16. if not root:
  17. return
  18. printTreeMidOrder(root.lchild)
  19. print(root.data, end=" ")
  20. printTreeMidOrder(root.rchild)
  21. def constructTree():
  22. root = BiTNode()
  23. node1 = BiTNode()
  24. node2 = BiTNode()
  25. node3 = BiTNode()
  26. node4 = BiTNode()
  27. root.data = 6
  28. node1.data = 3
  29. node2.data = -7
  30. node3.data = -1
  31. node4.data = 9
  32. root.lchild = node1
  33. root.rchild = node2
  34. node1.lchild = node3
  35. node1.rchild = node4
  36. return root
  37. if __name__ == "__main__":
  38. root1 = constructTree()
  39. root2 = createDupTree(root1)
  40. printTreeMidOrder(root1)
  41. print("\ndup it")
  42. printTreeMidOrder(root2)
  1. # @Filename: 对二叉树进行镜像翻转.py
  2. # @Software: PyCharm
  3. # @e_mail: sancica@163.com
  4. from collections import deque
  5. class BiTree:
  6. def __init__(self):
  7. self.data = None
  8. self.lchild = None
  9. self.rchild = None
  10. def reverseTree(root):
  11. if not root:
  12. return
  13. reverseTree(root.lchild)
  14. reverseTree(root.rchild)
  15. tmp = root.lchild
  16. root.lchild = root.rchild
  17. root.rchild = tmp
  18. def arrtotree(arr):
  19. if not arr:
  20. return
  21. bt = BiTree()
  22. middle = len(arr) // 2
  23. bt.data = arr[middle]
  24. bt.lchild = arrtotree(arr[:middle])
  25. bt.rchild = arrtotree(arr[middle + 1:])
  26. return bt
  27. def printTreeLayer(root):
  28. if not root:
  29. return
  30. queue = deque()
  31. queue.append(root)
  32. while queue:
  33. p = queue.popleft()
  34. print(p.data, end=" ")
  35. if p.lchild:
  36. queue.append(p.lchild)
  37. if p.rchild:
  38. queue.append(p.rchild)
  39. print()
  40. if __name__ == "__main__":
  41. arr = [1, 2, 3, 4, 5, 6, 7]
  42. root = arrtotree(arr)
  43. printTreeLayer(root)
  44. reverseTree(root)
  45. printTreeLayer(root)
  1. # @Filename: 将有序数组放到二叉树中.py
  2. # 可以采用中序遍历的顺序将数据放入进去
  3. # 实现思路,将中间节点作为根节点,然后将左子树的中间节点作为左子树的根节点,依次循环
  4. class BiTree:
  5. def __init__(self):
  6. self.data = None
  7. self.lchild = None
  8. self.rchild = None
  9. def arr2tree(arr):
  10. # 将采用递归调用
  11. # 1. 确定递归返回条件
  12. if not arr:
  13. return None
  14. # 2. 找到中点,并将该点放入二叉树
  15. middle = len(arr) // 2
  16. bt = BiTree()
  17. bt.data = arr[middle]
  18. # 递归建立左子树节点
  19. bt.lchild = arr2tree(arr[:middle])
  20. # 递归建立右子树
  21. bt.rchild = arr2tree(arr[middle + 1:])
  22. return bt
  23. def printTree_middle(root):
  24. if not root:
  25. return
  26. if root.lchild:
  27. printTree_middle(root.lchild)
  28. print(root.data, end=" ")
  29. if root.rchild:
  30. printTree_middle(root.rchild)
  31. if __name__ == "__main__":
  32. sorted_arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  33. print("数组")
  34. for num in sorted_arr:
  35. print(num, end=" ")
  36. print("\n")
  37. root = arr2tree(sorted_arr)
  38. # 采用中序遍历整个二叉树
  39. printTree_middle(root)
  1. # @Filename: 找出排序二叉树上任意两点的最近共同父节点1(路径对比法).py
  2. # 遍历所有路径,当前点与所需点相等,返回True,并push该节点
  3. """
  4. 分析:时间复杂度,最坏情况访问整个二叉树,为O(n),空间复杂度,由于导入栈s存储数据,最坏情况是存储整个二叉树,也为O(n)
  5. """
  6. class BiTree:
  7. def __init__(self):
  8. self.data = None
  9. self.lchild = None
  10. self.rchild = None
  11. class Stack:
  12. def __init__(self):
  13. self.items = []
  14. def is_empty(self):
  15. return True if not self.items else False
  16. def size(self):
  17. return len(self.items)
  18. def peek(self):
  19. if not self.is_empty():
  20. return self.items[-1]
  21. else:
  22. return None
  23. def pop(self):
  24. if not self.is_empty():
  25. return self.items.pop()
  26. else:
  27. print("栈已空")
  28. return None
  29. def push(self, item):
  30. self.items.append(item)
  31. def getPathFromRoot(root, node, s):
  32. # 路径遍历,传入栈s,将找到的路径递归加入栈中
  33. if not root:
  34. return False
  35. if root == node:
  36. s.push(root)
  37. return True
  38. if getPathFromRoot(root.lchild, node, s) or getPathFromRoot(root.rchild, node, s):
  39. s.push(root)
  40. return True
  41. return False
  42. def arraytotree(arr):
  43. if not arr:
  44. return
  45. bt = BiTree()
  46. middle = len(arr) // 2
  47. bt.data = arr[middle]
  48. bt.lchild = arraytotree(arr[:middle])
  49. bt.rchild = arraytotree(arr[middle + 1:])
  50. return bt
  51. def FindParentNode(root, node1, node2):
  52. stack1 = Stack()
  53. stack2 = Stack()
  54. getPathFromRoot(root, node1, stack1)
  55. getPathFromRoot(root, node2, stack2)
  56. common_parent = None
  57. while stack1.peek() == stack2.peek():
  58. common_parent = stack1.peek()
  59. stack2.pop()
  60. stack1.pop()
  61. return common_parent
  62. if __name__ == "__main__":
  63. arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  64. root = arraytotree(arr)
  65. node1 = root.lchild.lchild.lchild
  66. node2 = root.lchild.rchild
  67. res = FindParentNode(root, node1, node2)
  68. if res:
  69. print(str(node1.data)+"与"+str(node2.data)+"的最近公共父节点为:"+str(res.data))
  70. else:
  71. print("没有公共交接点")
  1. # @Filename: 找出排序二叉树上任意两点的最近共同父节点2(节点编号法).py
  2. """
  3. 将根节点设为编码1,左子树添加0,右子树添加1,对整个树进行编码,所以时间复杂度为O(n)
  4. 对两个子节点的编码分别max(n1, n2) = max(n1, n2)/2,直至两个节点相等,最终确定父节点编码
  5. """
  6. import math
  7. class BiTree:
  8. def __init__(self):
  9. self.data = None
  10. self.lchild = None
  11. self.rchild = None
  12. def arraytotree(arr):
  13. if not arr:
  14. return
  15. bt = BiTree()
  16. middle = len(arr) // 2
  17. bt.data = arr[middle]
  18. bt.lchild = arraytotree(arr[:middle])
  19. bt.rchild = arraytotree(arr[middle + 1:])
  20. return bt
  21. class IntRef:
  22. def __init__(self):
  23. self.num = None
  24. def getNo(root, node, number):
  25. # 找到node点的number编码,左子树*2,右子树*2+1
  26. if not root:
  27. return False
  28. if root == node:
  29. return True
  30. tmp = number.num
  31. number.num = 2 * tmp
  32. if getNo(root.lchild, node, number):
  33. return True
  34. else:
  35. number.num = 2 * tmp + 1
  36. return getNo(root.rchild, node, number)
  37. def getNodeFromNum(root, number):
  38. if not root or number < 0:
  39. return None
  40. if number == 1:
  41. return root
  42. # 计算number对应二进制的长度,根据完全二叉树的性质ceil(log2(n))+1
  43. lens = int(math.log(number) / math.log(2))
  44. number -= 1 << lens
  45. while lens > 0:
  46. # 这里终于搞明白了,获取当前这一位二进制的值
  47. print(1 << (lens - 1) & number >= 1)
  48. if (1 << (lens - 1) & number) >= 1:
  49. root = root.rchild
  50. else:
  51. root = root.lchild
  52. lens -= 1
  53. return root
  54. def FindParentNode(root, node1, node2):
  55. ref1 = IntRef()
  56. ref1.num = 1
  57. ref2 = IntRef()
  58. ref2.num = 1
  59. getNo(root, node1, ref1)
  60. getNo(root, node2, ref2)
  61. num1 = ref1.num
  62. num2 = ref2.num
  63. while num1 != num2:
  64. if num1 > num2:
  65. num1 //= 2
  66. else:
  67. num2 //= 2
  68. return getNodeFromNum(root, num1)
  69. if __name__ == "__main__":
  70. arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10,11,12,13,14,15,16,17,18,19,20]
  71. # arr = [1,2,3,4, 5,6,7,8,9,10]
  72. root = arraytotree(arr)
  73. node1 = root.lchild.rchild.lchild
  74. node2 = root.lchild.rchild.rchild
  75. res = FindParentNode(root, node1, node2)
  76. print(res.data)
  77. if res:
  78. print(str(node1.data) + "与" + str(node2.data) + "的最近公共父节点为:" + str(res.data))
  79. else:
  80. print("没有公共交接点")
  1. # @Filename: 把二叉树转换为双向链表.py
  2. """
  3. 搜索二叉树:左子树<根节点<右子树
  4. 通过中序遍历能够找出有序的顺序
  5. 本方法通过中序遍历,在遍历过程中修改指针的指向实现双向链表,即当前点的left = 上一节点,上一节点的right = 当前点
  6. """
  7. class BiTree:
  8. def __init__(self):
  9. self.data = None
  10. self.lc = None
  11. self.rc = None
  12. class Test:
  13. def __init__(self):
  14. self.pHead = None
  15. self.pEnd = None
  16. def arraytotree(self, arr):
  17. if not arr:
  18. return None
  19. bt = BiTree()
  20. middle = len(arr) // 2
  21. bt.data = arr[middle]
  22. bt.lc = self.arraytotree(arr[:middle])
  23. bt.rc = self.arraytotree(arr[middle + 1:])
  24. return bt
  25. def treetolist(self, root):
  26. if not root:
  27. return
  28. self.treetolist(root.lc)
  29. # 当前节点左子树指向前一节点
  30. root.lc = self.pEnd
  31. if not self.pEnd:
  32. # 保留头结点
  33. self.pHead = root
  34. else:
  35. # 前一节点的右节点指向当前节点
  36. self.pEnd.rc = root
  37. # 前移
  38. self.pEnd = root
  39. self.treetolist(root.rc)
  40. if __name__ == "__main__":
  41. arr = [1,2,3,4,5,6,7,8,9,10]
  42. test = Test()
  43. root = test.arraytotree(arr)
  44. test.treetolist(root)
  45. cur = test.pHead
  46. print("正向遍历")
  47. while cur:
  48. print(cur.data, end=" ")
  49. cur = cur.rc
  50. cur = test.pEnd
  51. print("\n")
  52. print("逆向遍历")
  53. while cur:
  54. print(cur.data, end=" ")
  55. cur = cur.lc

2. 基本数字运算

  1. # @Filename: 判断1024的阶乘末尾有多少个0.py
  2. """
  3. 分析:
  4. 1. 结果中0的来源
  5. a. 5与偶数的乘积,由于偶数的数量肯定比5的倍数的数量多,所以只考虑5的倍数的数量
  6. b. 乘子中本来就有0的数,如100等,但由于100本身就是5和25的倍数,在a中就进行统计了,所以就不用考虑b这一项了
  7. """
  8. def zeroCount(n):
  9. count = 5
  10. res = 0
  11. while True:
  12. i = 1
  13. while i <= n:
  14. if i % count == 0:
  15. res += 1
  16. i += 1
  17. count *= 5
  18. if count > n:
  19. break
  20. print(res)
  21. def zeroCount_nice(n):
  22. count = 0
  23. while n > 0:
  24. n = n // 5
  25. count += n
  26. print(count)
  27. zeroCount_nice(1)
  1. # @Filename: 判断一个数是否为2的n次方.py
  2. """
  3. 分析:
  4. 与2沾边的数,最好采用位移操作
  5. 1. 构造2的倍数,判断是否相等
  6. 2. 判断该数的二进制的第一位是否为1,其他为0
  7. """
  8. def construct_num(n):
  9. i = 1
  10. while i <= n:
  11. if i == n:
  12. return True
  13. i <<= 1
  14. return False
  15. def judge_ord(n):
  16. # 负数在内存中是补码的形式存在
  17. if n < 1:
  18. return False
  19. if not (n & n - 1):
  20. return True
  21. else:
  22. return False
  23. print(judge_ord(0))
  1. # @Filename: 判断一个自然数是否是某个数的平方.py
  2. """
  3. 前提:不使用开方运算
  4. 分析:
  5. 1. 直接计算,i递增,计算i**2与n的大小关系
  6. 2. 利用二分查找法找到对应的i,减少运算的次数
  7. 3. 减法运算法:通过对平方数的构造规律得:(n+1)^2 = n^2+2n+1 = (n-1)^2+(2(n-1)+1)+2n+1 = ... = 1+(2*1+1)+(2*2+1)+...+(2*n+1)
  8. """
  9. def isPower_bn(n):
  10. i = 0
  11. j = n
  12. mid = (i+j)//2
  13. while i <=j:
  14. if mid**2 == n:
  15. return True
  16. elif mid**2 > n:
  17. j = mid-1
  18. else:
  19. i = mid+1
  20. mid = (i+j)//2
  21. return False
  22. def isPower_sub(n):
  23. """
  24. 减法运算法
  25. """
  26. res = n
  27. flag = 1
  28. while res >= 0:
  29. if res == 0:
  30. return True
  31. res -= flag
  32. flag += 2
  33. return False
  34. print(isPower_sub(25))
  1. # @Filename: 如何不使用^实现异或运算.py
  2. """
  3. 使用或 |实现
  4. """
  5. def xor(x, y):
  6. # (x | y)为1时,(0,1)(1,0)(1,1):需要排除(1,1),则在后面&(~x|~y)
  7. return (x | y) & (~x | ~y)
  8. print(xor(2, 2))
  1. # @Filename: 如何不使用加减乘除运算实现减法.py
  2. """
  3. 分析:
  4. 减法就是加一个负数,同加法一样处理
  5. """
  6. def add(a, b):
  7. # 将b转为b的负数形式,然后采用加法的运算
  8. # b = ~b + 1
  9. while True:
  10. res = a ^ b
  11. carry = (res & b) << 1
  12. if carry == 0:
  13. return res
  14. a = res
  15. b = carry
  16. return res
  17. def sub(a, b):
  18. return add(a, b)
  19. print(sub(14, 4))
  1. # @Filename: 如何不使用加减乘除运算实现加法.py
  2. """
  3. # 利用二进制的异或和 与运算实现加法
  4. """
  5. # 注:python没有自动溢出的功能,int是无线长的,随着位数增长,自动改变类型为long,所以这里需要用到截断
  6. def sum(num1, num2):
  7. while True:
  8. res = (num1 ^ num2) & 0xffffffff
  9. carry = ((num1 & num2) << 1) & 0xffffffff
  10. if carry == 0:
  11. # 前面部分,表示里面如果没有负数,后面部分表示有负数
  12. return res if res <= 0x7fffffff else ~(res ^ 0xffffffff)
  13. num1 = res
  14. num2 = carry
  15. print(sum(-11, 2))
  1. # @Filename: 如何不使用除法操作符实现两个正整数的除法.py
  2. """
  3. 分析:
  4. 1. 减法,被除数循环减去除数,直到值小于除数,次数即为商,余数为当前值(不使用%实现%)
  5. 2. 位移法,通过位移加快逼近速度
  6. """
  7. def sub_divid(n, m):
  8. count = 0
  9. while n >= m:
  10. n -= m
  11. count += 1
  12. return count
  13. def shift_divid(n, m):
  14. res = 0
  15. index = 0
  16. while n >= m:
  17. flag = m * 2 ** index
  18. count = 2 ** index
  19. if flag > n:
  20. n -= flag // 2
  21. count //= 2
  22. res += count
  23. index = 0
  24. elif flag < n:
  25. index += 1
  26. else:
  27. return res + count
  28. return res
  29. print(shift_divid(200, 20))
  1. # @Filename: 如何找最小的不重复数.py
  2. """
  3. 分析:
  4. 不重复数:数的每一位相邻之间不同,如1201 1212
  5. 题目:给定n,找到最小的那个不重复数
  6. 1. 首先n += 1
  7. 2. 从右往左找到第一对重复的数i-1和i,对i对应的数字加一(需考虑进位)
  8. a. 如果此时i和i+1对应的数字相等,则i++
  9. b. 否则continue
  10. """
  11. def find_min_num(n):
  12. n = n+1
  13. num = list(str(n))
  14. i = len(num) - 1
  15. while i > 0:
  16. if num[i] == num[i - 1]:
  17. n += 10 ** (len(num) - i - 1)
  18. num = list(str(n))
  19. flag = 1
  20. j = i+1
  21. while j < len(num):
  22. flag = 1 ^ flag
  23. num[j] = str(flag)
  24. j += 1
  25. print(num)
  26. n = int(''.join(num))
  27. if i < len(num)-1 and num[i] == num[i + 1]:
  28. i += 1
  29. continue
  30. i -= 1
  31. return n
  32. print(find_min_num(8989))
  1. # @Filename: 如何按要求比较两个数的大小.py
  2. """
  3. 题目:比较两个数大小,不能使用大于、小于、if语句
  4. """
  5. def maxs(a, b):
  6. return ((a + b) + abs(a - b)) / 2
  7. print(maxs(5, 6))
  1. # @Filename: 如何根据已知随机数生成函数计算新的随机数.py
  2. """
  3. 题目:假设已知随机生成函数rand7()能生成整数1~7之间的均匀分布,如何构造rand10()函数,使其产生的随机数是整数1~10的均匀分布
  4. 分析:
  5. (rand7()-1)*7+rand()可以造出1-49的均匀分布,通过截取1-40的数据然后求余得到1-10的均匀分布
  6. 1. 这里的已知随机函数可以为任意自然数,都可以推导出来(n>=4)
  7. """
  8. import random
  9. def rand7():
  10. return int(random.uniform(1, 7))
  11. def rand10():
  12. while True:
  13. num = (rand7()-1)*7+rand7()
  14. if num <=40:
  15. break
  16. return num % 10+1
  17. from collections import Counter
  18. res = []
  19. for i in range(10000000):
  20. res.append(rand10())
  21. print([i/10000000 for i in dict(Counter(res)).values()])
  1. # @Filename: 如何求有序数列的1500个数的值.py
  2. """
  3. 题目:数列是有序数列,里面的数能够被2或者3或者5整除
  4. 分析:
  5. 1. 遍历法
  6. 2. 规律法
  7. """
  8. def search(n):
  9. a = [0, 2, 3, 4, 5, 6, 8, 9, 10, 12, 14, 15, 16, 18, 20, 21, 22, 24, 25, 26, 27, 28]
  10. print(len(a))
  11. ret = (n // 22) * 30 + a[n % 22]
  12. print(ret)
  13. search(1500)
  1. # @Filename: 如何计算一个数的n次方.py
  2. # @Software: PyCharm
  3. # @e_mail: sancica@163.com
  4. """
  5. 分析:
  6. 1. 连乘或连除
  7. 2. 递归或者动态规划法
  8. """
  9. def power(d, n):
  10. # 判断是奇数还是偶数次,将采用不同的公式
  11. if n == 0: return 1
  12. if n == 1: return d
  13. tmp = power(d, abs(n) // 2)
  14. if n > 0:
  15. if n % 2 == 1:
  16. return tmp * tmp * d
  17. else:
  18. return tmp * tmp
  19. else:
  20. if n % 2 == 1:
  21. return 1 / (tmp * tmp * d)
  22. else:
  23. return 1 / (tmp * tmp)
  24. def dynamic_power(d, n):
  25. if n == 0:
  26. return 1
  27. if n == 1:
  28. return d
  29. if n == -1:
  30. return 1/d
  31. arr = [0] * abs(n)
  32. arr[0] = 1
  33. arr[1] = d
  34. i = 2
  35. while i < abs(n):
  36. arr[i] = arr[i // 2] ** 2
  37. i = i*2
  38. if i == abs(n):
  39. if n > 0:
  40. return arr[n]
  41. else:
  42. return 1 / arr[-n]
  43. else:
  44. if n > 0:
  45. return arr[n//2]**2 * d
  46. else:
  47. return 1 / (arr[-n//2]**2 * d)
  48. print(dynamic_power(2, -1))
  1. # @Filename: 将十进制数分别以二进制和十六进制形式输出.py
  2. """
  3. 分析:
  4. 十进制转二进制可以考虑采用“除2倒取余法”
  5. """
  6. def intToBinary(n):
  7. hexNum = 8 * 8
  8. bit = []
  9. for i in range(hexNum):
  10. b = n >> i
  11. # 同时计算商和余数
  12. c, d = divmod(b, 2)
  13. bit.append(str(d))
  14. return ''.join(bit[::-1])
  15. def intToHex(s):
  16. hexs = ""
  17. while s != 0:
  18. remainder = s % 16
  19. if remainder < 10:
  20. hexs = str(remainder + ord('0')) + hexs
  21. else:
  22. hexs = str(remainder - 10 + ord('A')) + hexs
  23. s = s >> 4
  24. return chr(int(hexs))
  25. print(intToBinary(11))
  26. print(intToHex(10))
  1. # @Filename: 求二进制数中1的个数.py
  2. """
  3. 分析:
  4. 1. 依次向右位移,判断最后一位是否为1(采用&1判断)
  5. 2. n依次与n-1相与&,每次都会减少一个1,直到结果为0
  6. """
  7. def judge_count(n):
  8. count = 0
  9. while n > 0:
  10. if n & 1 == 1:
  11. count += 1
  12. n = n >> 1
  13. return count
  14. print(judge_count(8))
  15. def judge_count_and(n):
  16. count = 0
  17. while True:
  18. if n != 0:
  19. n = n & (n - 1)
  20. count += 1
  21. else:
  22. break
  23. return count
  24. print(judge_count_and(8))

3. 字符串

  1. # @Filename: 判断一个字符串是否为另一个字符串旋转得到.py
  2. """
  3. 分析:
  4. s2由s1旋转得到,那么s2一定是s1s1的子串
  5. """
  6. def rotateSame(s1, s2):
  7. if len(s1) != len(s2):
  8. return False
  9. s1 = s1 + s1
  10. # 这里判断是否为子串用in,但自己实现的话需要用到kmp算法
  11. if s2 in s1:
  12. return True
  13. else:
  14. return False
  15. if __name__ == "__main__":
  16. print(rotateSame("waterbollte", "erbolltewat"))
  1. # @Filename: 判断一个字符串是否包含重复字符.py
  2. """
  3. 题目:字符串中是否包含重复字符串
  4. 分析:
  5. 1. 蛮力法,双重循环判断是否有相等字符
  6. 2. hash表法
  7. 3. set长度比较法
  8. 本代码实现hash法
  9. """
  10. def isDup(strs):
  11. str_dic = {}
  12. for i in strs:
  13. if i not in str_dic:
  14. str_dic[i] = 1
  15. else:
  16. return False
  17. return True
  18. def isDup1(strs):
  19. return len(strs) == len(set(strs))
  20. print(isDup("fdsasd"))
  1. # @Filename: 判断两个字符串是否为换为字符串.py
  2. """
  3. 换位字符串:两个字符串由同样的字符构成,但位置不同
  4. 分析:
  5. 1. 字典法
  6. """
  7. from collections import Counter
  8. def compare(str1, str2):
  9. counter1 = Counter(str1)
  10. counter2 = Counter(str2)
  11. for key in counter1.keys():
  12. if key in counter2:
  13. if counter1[key] != counter2[key]:
  14. return False
  15. else:
  16. return False
  17. return True
  18. if __name__ == "__main__":
  19. str1 = "aaaabcc"
  20. str2 = "abcbaaa"
  21. print(compare(str1, str2))
  1. # @Filename: 判断两个字符串的包含关系.py
  2. # @Software: PyCharm
  3. # @e_mail: sancica@163.com
  4. """
  5. 题目:判断字符串的包含关系:即B中出现的字符在A中都有出现,则A包含B
  6. 分析:
  7. 1. 双层循环
  8. 2. 空间换时间,利用hash表
  9. """
  10. # 本代码实现第二种方法
  11. def is_contain(str1, str2):
  12. cha_dic = {}
  13. for char in str2:
  14. if char not in cha_dic:
  15. cha_dic[char] = 1
  16. for char in str1:
  17. if char in cha_dic:
  18. cha_dic[char] = 0
  19. return not sum(cha_dic.values())
  20. print(is_contain("abcd", "agd"))
  1. # @Filename: 判断字符串是否为整数.py
  2. """
  3. 分析:
  4. 字符串形式的整数有三种表达方法 "52", "-52", "+52"
  5. """
  6. def judge_int(str):
  7. if str[0] == "+" or str[0] == "-":
  8. index = 1
  9. else:
  10. index = 0
  11. result = 0
  12. while index < len(str):
  13. if str[index].isdigit():
  14. result = result*10 + int(str[index])
  15. else:
  16. print("不是数字")
  17. return
  18. index += 1
  19. return -result if str[0] == "-" else result
  20. print(judge_int("+4234"))
  1. # @Filename: 句子中的单词反转.py
  2. """
  3. 分析:
  4. 1. 先对整个句子进行反转,最后再对每个单词进行反转
  5. """
  6. def reverseStr(stl, begin, end):
  7. # 使用异或的方式进行反转
  8. while begin < end:
  9. stl[begin] = ord(stl[begin]) ^ ord(stl[end])
  10. stl[end] = stl[begin] ^ ord(stl[end])
  11. stl[begin] = stl[begin] ^ stl[end]
  12. stl[begin] = chr(stl[begin])
  13. stl[end] = chr(stl[end])
  14. begin += 1
  15. end -= 1
  16. def swapWord(str):
  17. stl = list(str)
  18. reverseStr(stl, 0, len(stl) - 1)
  19. begin = 0
  20. end = 0
  21. while end < len(stl):
  22. if stl[end] == " ":
  23. reverseStr(stl, begin, end - 1)
  24. end = end + 1
  25. begin = end
  26. else:
  27. end += 1
  28. return "".join(stl)
  29. if __name__ == "__main__":
  30. print(swapWord("I love you"))
  1. # @Filename: 如何在二维数组中寻找最短路径(动态规划).py
  2. """
  3. 分析:动态规划
  4. 1. 定义一个二维数组存储中间路径最小值
  5. a. 初始化f[i,0] = arr[1][0] + ... + arr[i][0] f[0][j] = arr[0][1] + ... + arr[0][j]
  6. b. 动态推导公式f[i][j] = arr[i][j]+min(f[i-1][j], f[i][j-1])
  7. """
  8. def getMinPath(arr):
  9. len1 = len(arr)
  10. len2 = len(arr[0])
  11. # 初始化二维数组
  12. f = [[0] * len2 for _ in range(len1)]
  13. i = 0
  14. temp_sum = 0
  15. while i < len1:
  16. temp_sum += arr[i][0]
  17. f[i][0] = temp_sum
  18. i += 1
  19. i = 0
  20. temp_sum = 0
  21. while i < len2:
  22. temp_sum += arr[0][i]
  23. f[0][i] = temp_sum
  24. i += 1
  25. for i in range(1, len1):
  26. for j in range(1, len2):
  27. f[i][j] = arr[i][j] + min(f[i - 1][j], f[i][j - 1])
  28. print("二维数组最短路径长度为:", f[i][j])
  29. return f[i][j]
  30. getMinPath([[1, 4, 3], [8, 7, 5], [2, 1, 5]])
  1. # @Filename: 如何在二维数组中寻找最短路径(递归).py
  2. """
  3. 分析:
  4. 采用递归的方法,从最后值
  5. """
  6. def getMinPath(arr, i, j):
  7. if i == 0 and j == 0:
  8. return arr[i][j]
  9. elif i > 0 and j > 0:
  10. return arr[i][j] + min(getMinPath(arr, i - 1, j), getMinPath(arr, i, j - 1))
  11. elif i > 0 and j == 0:
  12. return arr[i][j] + getMinPath(arr, i - 1, j)
  13. elif i == 0 and j > 0:
  14. return arr[i][j] + getMinPath(arr, i, j - 1)
  15. if __name__ == "__main__":
  16. arr = [[1, 4, 3], [8, 7, 5], [2, 1, 5]]
  17. print(getMinPath(arr, len(arr) - 1, len(arr[0]) - 1))
  1. # @Filename: 如何找到到达目标词的最短链长度.py
  2. # @Software: PyCharm
  3. # @e_mail: sancica@163.com
  4. """
  5. # 实现BFS算法,广度优先遍历
  6. """
  7. from collections import deque
  8. class QItem:
  9. def __init__(self, word, lens):
  10. self.word = word
  11. self.lens = lens
  12. def isAdjacent(a, b):
  13. """
  14. 判断两个字符串是否为邻接字符串
  15. """
  16. index = 0
  17. for i in range(len(a)):
  18. if a[i] != b[i]:
  19. index += 1
  20. if index > 1:
  21. return False
  22. return index == 1
  23. def shortestChainLen(start, target, D):
  24. Q = deque()
  25. item = QItem(start, 1)
  26. Q.append(item)
  27. while Q:
  28. curr = Q[0]
  29. Q.pop()
  30. for it in D:
  31. temp = it
  32. if isAdjacent(curr.word, temp):
  33. item.word = temp
  34. item.lens = curr.lens + 1
  35. Q.append(item)
  36. D.remove(temp)
  37. if temp == target:
  38. return item.lens
  39. return 0
  40. if __name__ == "__main__":
  41. D = []
  42. D.append("pooN")
  43. D.append("pbcc")
  44. D.append("zamc")
  45. D.append("poIc")
  46. D.append("pbca")
  47. D.append("pbIc")
  48. D.append("poIN")
  49. start = "TooN"
  50. target = "pbca"
  51. print(shortestChainLen(start, target, D))
  1. # @Filename: 如何找到由其他单词组成的最长单词.py
  2. """
  3. 分析:
  4. 1. 首先按照单词长度进行排序
  5. 2. 由最长单词开始进行递归判断,从左到右判断子串是否在单词表中,存在继续判断右边
  6. """
  7. class LongestWord:
  8. def find(self, strArray, strs):
  9. if strs in strArray:
  10. return True
  11. else:
  12. return False
  13. def isContain(self, strArray, word, length):
  14. lens = len(word)
  15. if lens == 0:
  16. return True
  17. i = 1
  18. while i <= lens:
  19. if i == length:
  20. return False
  21. strs = word[0:i]
  22. if self.find(strArray, strs):
  23. if self.isContain(strArray, word[i:], length):
  24. return True
  25. i += 1
  26. return False
  27. def getLongestStr(self, strArray):
  28. strArray = sorted(strArray, key=len, reverse=True)
  29. i = 0
  30. while i < len(strArray):
  31. if self.isContain(strArray, strArray[i], len(strArray[i])):
  32. return strArray[i]
  33. i += 1
  34. return None
  35. if __name__ == "__main__":
  36. strArray = ['a', 'b', 'ab']
  37. lw = LongestWord()
  38. print(lw.getLongestStr(strArray))
  1. # @Filename: 如何求字符串的编辑距离(动态规划).py
  2. """
  3. 分析:
  4. 编辑距离:将一个字符串s1通过增、删、改(替换)的方式转为字符串s2所需要的步骤
  5. 动态规划求解:
  6. 1. 建立一个2为数组D,表示将s1的i子串变为s2的j子串所需要的编辑距离
  7. a. i = 0时,D[i][j] = j
  8. b. j = 0 时, D[i][j] = i
  9. 2. 增:如果计算出了D(i, j-1)的值,那么D(i, j) 等于D(i,j-1)+1
  10. 3. 删:如果计算出了D(i-1, j)的值,那么D(i,j)等于D(i-1, j)+1
  11. 4. 改:如果计算出了D(i-1, j-1)的值,如果s1[i]==s2[j],则D(i,j) = D(i-1, j-1),否则,D(i,j) = D(i-1, j-1)+1:将s2[j]改为s1[i]
  12. """
  13. def minEditDistance(s1, s2):
  14. len1 = len(s1)
  15. len2 = len(s2)
  16. editDistance = [[None] * (len2+1) for _ in range(len1+1)]
  17. # 初始化边缘值
  18. for i in range(len1+1):
  19. editDistance[i][0] = i
  20. for j in range(len2+1):
  21. editDistance[0][j] = j
  22. for i in range(1, len1+1):
  23. for j in range(1, len2+1):
  24. if s1[i-1] == s2[j-1]:
  25. editDistance[i][j] = min(editDistance[i-1][j]+1, editDistance[i][j-1]+1, editDistance[i-1][j-1])
  26. else:
  27. editDistance[i][j] = min(editDistance[i-1][j]+1, editDistance[i][j-1]+1, editDistance[i-1][j-1]+1)
  28. return editDistance[-1][-1]
  29. print(minEditDistance("", "ros"))
  1. # @Filename: 如何消除字符串的内嵌括号.py
  2. # @Software: PyCharm
  3. # @e_mail: sancica@163.com
  4. """
  5. 题目:字符串中的括号问题
  6. 分析:
  7. 1. 用栈或者一个数来确保括号的配对
  8. """
  9. def removeNestedPare(strs):
  10. arr = list(strs)[1:-1]
  11. register = 0
  12. for i in range(len(arr)):
  13. if arr[i] == "(":
  14. register += 1
  15. arr[i] = ''
  16. elif arr[i] == ")":
  17. arr[i] = ''
  18. register -= 1
  19. if register < 0:
  20. print("非法表达式,括号不匹配")
  21. return None
  22. print(arr)
  23. return '(' + ''.join(arr) + ')'
  24. print(removeNestedPare("(1,(2,3),(4,(5,6),7))"))
  1. # @Filename: 如果按照给定的字符序列对字符数组排序.py
  2. # 根据给定的字符串顺序对字符串进行排序
  3. """
  4. 分析:
  5. 对字符串顺序建立hash表,value值为顺序值,比较大小的时候利用value值进行比较,越小优先级越高
  6. """
  7. def compare(str1, str2, char_to_int):
  8. i = 0
  9. while i < len(str1) and i < len(str2):
  10. if char_to_int[str1[i]] < char_to_int[str2[i]]:
  11. return True
  12. elif char_to_int[str1[i]] > char_to_int[str2[i]]:
  13. return False
  14. i += 1
  15. def insertSort(s, char_to_int):
  16. # 插入排序
  17. lens = len(s)
  18. i = 1
  19. while i < lens:
  20. temp = s[i]
  21. j = i - 1
  22. while j >= 0:
  23. if compare(temp, s[j], char_to_int):
  24. # 每一项后移
  25. s[j + 1] = s[j]
  26. else:
  27. break
  28. j -= 1
  29. # 将对应位置赋值
  30. s[j + 1] = temp
  31. i += 1
  32. if __name__ == "__main__":
  33. s = ["bed", "dog", "dear", "eye"]
  34. sequence = "dgecfboa"
  35. char_to_int = {}
  36. for i in range(len(sequence)):
  37. char_to_int[sequence[i]] = i
  38. insertSort(s, char_to_int)
  39. print(s)
  1. # @Filename: 字符串反转(异或).py
  2. def reverse(strs):
  3. i = 0
  4. j = len(strs) - 1
  5. # 这里必须转为list,因为字符串属于常量,无法修改
  6. strs = list(strs)
  7. while i < j:
  8. # 通过异或的方式进行交换
  9. print(type(ord(strs[i])))
  10. strs[i] = chr(ord(strs[i]) ^ ord(strs[j]))
  11. strs[j] = chr(ord(strs[i]) ^ ord(strs[j]))
  12. strs[i] = chr(ord(strs[i]) ^ ord(strs[j]))
  13. i += 1
  14. j -= 1
  15. return ''.join(strs)
  16. if __name__ == "__main__":
  17. str = "abcdefg"
  18. print(reverse(str))
  1. # @Filename: 实现字符串的匹配(KMP).py
  2. """
  3. 题目:字符串匹配算法 KMP:
  4. 分析:
  5. 1. 首先建立nexts数组,该数组的值用于表明当子串第j位与主串第i位不匹配时,此时应该将j移动到的位置k
  6. a. 当j==0时,next[j] = -1
  7. b. 当j == 1时,next[j] = 0
  8. c. 当p[j] == p[k]:next[j+1] = next[j] + 1
  9. d. 当p[j] != p[k]: k = next[k]
  10. https://www.cnblogs.com/yjiyjige/p/3263858.html
  11. """
  12. def getNext(p, nexts):
  13. k = 0
  14. j = -1
  15. nexts[0] = -1
  16. while k < len(p):
  17. if j == -1 or p[j] == p[k]:
  18. k += 1
  19. j += 1
  20. nexts[k] = j
  21. else:
  22. j = nexts[j]
  23. def match(s, p, nexts):
  24. if not s or not p:
  25. return -1
  26. slen = len(s)
  27. plen = len(p)
  28. if slen < plen:
  29. return -1
  30. i = 0
  31. j = 0
  32. while i < slen and j < plen:
  33. print("i="+str(i)+","+str(j))
  34. if j == -1 or s[i] == p[j]:
  35. i += 1
  36. j += 1
  37. else:
  38. j = nexts[j]
  39. if j >=plen:
  40. return i-plen
  41. return -1
  42. if __name__ == "__main__":
  43. s = "abababaabcbab"
  44. p = "abaabc"
  45. lens = len(p)
  46. nexts = [0]*(lens+1)
  47. s = list(s)
  48. p = list(p)
  49. getNext(p, nexts)
  50. print("匹配结果:"+str(match(s, p, nexts)))
  1. # @Filename: 对大小写字母组成的字符数组进行排序.py
  2. """
  3. 题目:这里的排序指,将其中的所有小写字母排在大写字母的前面,不需要考虑小写字母之间或者大写字母之间的排序关系
  4. 分析:
  5. 1. 首尾指针一次遍历法
  6. """
  7. def sort(str):
  8. arr = list(str)
  9. i = 0
  10. j = len(arr) - 1
  11. while i < j:
  12. if 'A' <= arr[i] <= 'Z' and 'a' <= arr[j] <= 'z':
  13. arr[i] = ord(arr[i]) ^ ord(arr[j])
  14. arr[j] = arr[i] ^ ord(arr[j])
  15. arr[i] = arr[i] ^ arr[j]
  16. arr[i] = chr(arr[i])
  17. arr[j] = chr(arr[j])
  18. i += 1
  19. j -= 1
  20. continue
  21. if 'z' >= arr[i] >= 'a':
  22. i += 1
  23. if 'A' <= arr[j] <= 'Z':
  24. j -= 1
  25. return "".join(arr)
  26. print(sort("sFSDFfsDdfsDFDdfDFDF"))
  1. # @Filename: 求一个串中出现的第一个最长重复子串(后缀数组法).py
  2. """
  3. 分析:
  4. 后缀数组法:将字符串中找重复子串问题转化为从后缀数组中通过对比相邻的两个子串的公共串的长度
  5. 后缀数组:字符串r从第i个字符开始的后缀表示为suuffix(i)
  6. """
  7. class CommonSubString():
  8. # 找出最长的公共子串长度
  9. def maxPrefix(self, s1, s2):
  10. i = 0
  11. while i < len(s1) and i < len(s2):
  12. if s1[i] == s2[i]:
  13. i += 1
  14. else:
  15. return i
  16. return i
  17. def getMaxCommonStr(self, s):
  18. suffixes = []
  19. for i in range(len(s)):
  20. suffixes.append(s[i:])
  21. suffixes.sort()
  22. maxCommonStrLen = 0
  23. maxCommonStr = None
  24. for i in range(len(suffixes)-1):
  25. curMaxCommonStrLen = self.maxPrefix(suffixes[i], suffixes[i+1])
  26. if maxCommonStrLen < curMaxCommonStrLen:
  27. maxCommonStrLen = curMaxCommonStrLen
  28. maxCommonStr = suffixes[i][:maxCommonStrLen]
  29. return maxCommonStr, maxCommonStrLen
  30. if __name__ == "__main__":
  31. c = CommonSubString()
  32. str, len = c.getMaxCommonStr("banana")
  33. print(str, len)
  1. # @Filename: 求一个字符串的所有排列(递归法).py
  2. """
  3. 题目:输入字符串,输出字符串的所有排列
  4. 分析:
  5. 1. 递归法
  6. a:固定第一个字符,对后面的数据进行全排列
  7. b:全排列结束后,第一个字符与后面字符进行替换
  8. c: 换回原来的字符串
  9. """
  10. def swap(str, i, j):
  11. tmp = str[i]
  12. str[i] = str[j]
  13. str[j] = tmp
  14. def Permulation(str, start):
  15. if str==None or start < 0:
  16. return
  17. if start == len(str)-1:
  18. print("".join(str))
  19. else:
  20. i = start
  21. while i <len(str):
  22. # 下面代码即可剔除重复的排列元素
  23. # if i != start and str[i] == str[start]:
  24. # i += 1
  25. # continue
  26. swap(str, start, i)
  27. Permulation(str, start+1)
  28. swap(str, start, i)
  29. i += 1
  30. def Permulation_transe(s):
  31. str = list(s)
  32. Permulation(str, 0)
  33. if __name__ == "__main__":
  34. Permulation_transe("abbc")
  1. # @Filename: 求一个字符串的所有排列(非递归法).py
  2. """
  3. 题目:输入字符串,输出字符串的所有排列
  4. 分析:
  5. 1. 非递归法
  6. 1. 对字符串中的字符进行排序(升序)
  7. 2. 从右往左找到第一个递增的子字符串(两个字符)
  8. 3. 将较小的那个字符(index=pmin)与后面比它大的最小的字符进行交换
  9. 4. 将pmin后的字符进行逆序
  10. 5. 循环,知道找不到递增的子字符串
  11. """
  12. def swap(str, i, j):
  13. tmp = str[i]
  14. str[i] = str[j]
  15. str[j] = tmp
  16. def Reverse(str, begin, end):
  17. i = begin
  18. j = end
  19. while i < j:
  20. swap(str, i, j)
  21. i += 1
  22. j -= 1
  23. def getNextPermutation(str):
  24. end = len(str)-1
  25. cur = end
  26. suc =0
  27. tmp = 0
  28. while cur != 0:
  29. suc = cur
  30. cur = cur - 1
  31. # 找到子字符串
  32. if str[cur] < str[suc]:
  33. tmp = end
  34. # 找到比cur大的最小字符串的字符
  35. while str[tmp] < str[cur]:
  36. tmp -= 1
  37. # 交换
  38. swap(str, cur, tmp)
  39. # 逆序
  40. Reverse(str, suc, end)
  41. return True
  42. return False
  43. def Permulation(s):
  44. if s == None or len(s)< 1:
  45. return
  46. str = list(s)
  47. str.sort()
  48. print(str)
  49. while getNextPermutation(str):
  50. print(str)
  51. if __name__ == "__main__":
  52. Permulation("abc")
  1. # @Filename: 求两个字符串的最大公共子串(动态规划法).py
  2. """
  3. 题目:求最长公共子串
  4. 分析:
  5. 1. 采用动态规划,记录s1[i]结尾的子串与s2[j]结尾的子串的最长公共子串
  6. """
  7. def getMaxSubStr(str1, str2):
  8. len1 = len(str1)
  9. len2 = len(str2)
  10. sb = ""
  11. maxI = 0
  12. maxs = 0
  13. M = [[None]*(len1+1) for _ in range(len2+1)]
  14. i = 0
  15. # !!!
  16. while i < len1+1:
  17. M[0][i] = 0
  18. i += 1
  19. j = 0
  20. while j < len2+1:
  21. M[j][0] = 0
  22. j += 1
  23. i = 1
  24. while i < len1+1:
  25. j = 1
  26. while j < len2+1:
  27. if str1[i-1] == str2[j-1]:
  28. M[j][i] = M[j-1][i-1] + 1
  29. if M[j][i] > maxs:
  30. maxI = j
  31. maxs = M[j][i]
  32. else:
  33. M[j][i] = 0
  34. j += 1
  35. i += 1
  36. j = maxI - maxs
  37. while j < maxI:
  38. sb += str2[j]
  39. j += 1
  40. return sb
  41. if __name__ == "__main__":
  42. str1 = "abccad"
  43. str2 = "dgcadde"
  44. print(getMaxSubStr(str1, str2))
  1. # @Filename: 求两个字符串的最大公共子串(滑动比较法).py
  2. """
  3. # 题目:求两个字符串的最大公共子串
  4. 分析:
  5. 1. 滑动比较法
  6. a. 保持s1不变,依次滑动s2,
  7. b. 记录此时的最大公共子串maxLen, 最大公共子串在s1的起始位置
  8. c. 直到j > len(s1)-1
  9. """
  10. def getMaxSubStr(s1, s2):
  11. len1 = len(s1)
  12. len2 = len(s2)
  13. maxLen = 0
  14. maxLenEnd1 = 0
  15. sb = ""
  16. i = 0
  17. while i < len1 + len2:
  18. s1begin = s2begin = 0
  19. tmpMaxLen = 0
  20. if i < len1:
  21. s1begin = len1 - i
  22. else:
  23. s2begin = i - len1
  24. j = 0
  25. while s1begin + j < len1 and s2begin + j < len2:
  26. if s1[s1begin + j] == s2[s2begin + j]:
  27. tmpMaxLen += 1
  28. else:
  29. if tmpMaxLen > maxLen:
  30. maxLen = tmpMaxLen
  31. maxLenEnd1 = s1begin + j
  32. else:
  33. tmpMaxLen = 0
  34. j += 1
  35. if tmpMaxLen > maxLen:
  36. maxLen = tmpMaxLen
  37. maxLenEnd1 = s1begin + j
  38. i += 1
  39. i = maxLenEnd1 - maxLen
  40. while i < maxLenEnd1:
  41. sb += s1[i]
  42. i += 1
  43. return sb
  44. if __name__ == "__main__":
  45. str1 = "abccade"
  46. str2 = "dgcadde"
  47. print(getMaxSubStr(str1, str2))
  1. # @Filename: 求字符串里的最长回文子串(manacher).py
  2. """
  3. Manacher算法
  4. 分析:时间复杂度o(N),空间复杂度O(N)
  5. 1. 使用*对字符串进行填充,使得只出现一种奇数回文串的情况
  6. 1. 设置P[i]数组记录以i为中心的最长回文串的半径,未填充字符串最大回文串长度为P[i]-1
  7. 2. 四种情况
  8. a. 如果i没有落到P[id]回文串中,则直接对i作为中心,求回文串
  9. b. i落在P[id]中,找到i对应回文串的另一边:2*id-i, 如果P[2*id-i]的左边回文数覆盖范围超出P[id]覆盖范围,则P[i] = id+P[id]-i
  10. c. i落在P[id]中,找到i对应回文串的另一边:2*id-i, 如果P[2*id-i]的左边回文数覆盖范围刚好为P[id]覆盖范围,则P[i]的长度从P[2*id-i]开始算起,再查看是否能继续增加
  11. d. i落在P[id]中,找到i对应回文串的另一边:2*id-i, 如果P[2*id-i]的左边回文数覆盖范围刚好为P[id]覆盖范围,则P[i]=P[2*id-i]
  12. 3. 综合以上情况 P[i] = min(P[2*id-i], P[id]-i)
  13. """
  14. class Test:
  15. def __int__(self):
  16. self.center = None
  17. self.palindromeLen = None
  18. def mins(self, a, b):
  19. return a if a < b else b
  20. def getCenter(self):
  21. return self.center
  22. def getLen(self):
  23. return self.palindromeLen
  24. def Manacher(self, strs):
  25. lens = len(strs)
  26. newLen = 2 * lens + 1
  27. s = [None] * newLen
  28. p = [None] * newLen
  29. id = 0
  30. i = 0
  31. for i in range(newLen):
  32. if i % 2 == 0:
  33. s[i] = '*'
  34. else:
  35. s[i] = strs[i // 2]
  36. p[i] = 0
  37. print(s)
  38. print(p)
  39. self.center = -1
  40. self.palindromeLen = -1
  41. i = 1
  42. while i < newLen:
  43. if id + p[id] > i:
  44. p[i] = self.mins(id + p[id] - i, p[2 * id - i])
  45. else:
  46. p[i] = 1
  47. while i + p[i] < newLen and i - p[i] > 0 and s[i - p[i]] == s[i + p[i]]:
  48. p[i] += 1
  49. if i + p[i] > id + p[id]:
  50. # 此时后面的数将进入i点的回文领域
  51. id = i
  52. if p[i] - 1 > self.palindromeLen:
  53. self.center = (i + 1) // 2 - 1
  54. self.palindromeLen = p[i] - 1
  55. i += 1
  56. print(1)
  57. if __name__ == "__main__":
  58. strs = "abcbax"
  59. t = Test()
  60. t.Manacher(strs)
  61. # 这里还要进行处理
  62. if t.palindromeLen % 2 == 0:
  63. print("fsdf",strs[t.getCenter() - (t.getLen() // 2 + 1):t.getCenter() + (t.getLen() // 2 - 1)])
  64. else:
  65. print("ddd",strs[t.getCenter() - (t.getLen() // 2):t.getCenter() + (t.getLen() // 2)])
  1. # @Filename: 求字符串里的最长回文子串(中心扩展法).py
  2. # @Software: PyCharm
  3. # @e_mail: sancica@163.com
  4. """
  5. 中心扩展法:
  6. 分析:时间复杂度(O(n^2)),空间复杂度(O(1))
  7. 1. 当中心为一个点
  8. 2. 当中心为两个点
  9. """
  10. class Test:
  11. def __init__(self):
  12. self.startIndex = None
  13. self.lens = 0
  14. def getStartIndex(self):
  15. return self.startIndex
  16. def getLens(self):
  17. return self.lens
  18. def expandBothSide(self, strs, c1, c2):
  19. n = len(strs)
  20. while c1 >= 0 and c2 < n and strs[c1] == strs[c2]:
  21. c1 -= 1
  22. c2 += 1
  23. tmpStartIndex = c1 + 1
  24. tmpLen = c2 - c1 - 1
  25. if tmpLen > self.lens:
  26. self.lens = tmpLen
  27. self.startIndex = tmpStartIndex
  28. def getLongestPalindrome(self, strs):
  29. if not strs:
  30. return
  31. n = len(strs)
  32. for i in range(n - 1):
  33. # 奇数个回文数,中间只有一个点
  34. self.expandBothSide(strs, i, i)
  35. # 偶数个回文数,中间有两个点
  36. self.expandBothSide(strs, i, i + 1)
  37. if __name__ == "__main__":
  38. strs = "cbbd"
  39. t = Test()
  40. t.getLongestPalindrome(strs)
  41. print(strs[t.getStartIndex():t.getStartIndex() + t.getLens()])
  1. # @Filename: 求字符串里的最长回文子串(动态规划).py
  2. """
  3. # 最长回文子串
  4. 分析:
  5. 动态规划:时间复杂度O(n^2),空间复杂度O(n^2)
  6. 1.
  7. P[i,i] = 1
  8. 如果Si != Si+1->P[i,i+1] = 0 else P[i,i+1] = 1
  9. 如果Si = Sj -> P(i, j) = P(i+1,j-1)
  10. """
  11. class Test:
  12. def __init__(self):
  13. self.startIndex = None
  14. self.lens = None
  15. def getStartIndex(self):
  16. return self.startIndex
  17. def getLen(self):
  18. return self.lens
  19. def getLongestPalindrome(self, strs):
  20. if not strs:
  21. return
  22. n = len(strs)
  23. if n < 1:
  24. return
  25. self.startIndex = 0
  26. self.lens = 1
  27. historyRecord = [[0] * n for _ in range(n)]
  28. # 更新长度为1的回文字符串信息
  29. for i in range(n):
  30. historyRecord[i][i] = 1
  31. # 更新长度为2的回文字符串信息
  32. for i in range(n - 1):
  33. if strs[i] == strs[i + 1]:
  34. historyRecord[i][i + 1] = 1
  35. self.startIndex = i
  36. self.lens = 2
  37. # 从长度为3的字符串开始找起
  38. pLen = 3
  39. while pLen <= n:
  40. i = 0
  41. while i < n - pLen + 1:
  42. j = i + pLen - 1
  43. if strs[i] == strs[j] and historyRecord[i + 1][j - 1] == 1:
  44. historyRecord[i][j] = 1
  45. self.startIndex = i
  46. self.lens = pLen
  47. i += 1
  48. pLen += 1
  49. if __name__ == "__main__":
  50. strs = "babad"
  51. t = Test()
  52. t.getLongestPalindrome(strs)
  53. if t.getStartIndex() != -1 and t.getLen() != -1:
  54. print("最长的回文子串为:", strs[t.getStartIndex():t.getStartIndex() + t.getLen()])
  55. else:
  56. print("查找失败")
  1. # @Filename: 求解字符串中字典序最大的子序列(逆序遍历法).py
  2. # @Software: PyCharm
  3. # @e_mail: sancica@163.com
  4. """
  5. 分析:
  6. 字典序:首先找到字符串a中最大的字符,然后在该字符后面的子字符串中找到最大的字符,最终找到最后一个字符
  7. """
  8. def getLargestSub(src):
  9. res = [src[-1]]
  10. i = len(src) - 2
  11. while i > -1:
  12. if src[i] >= res[0]:
  13. res.insert(0, src[i])
  14. i -= 1
  15. return "".join(res)
  16. if __name__ == "__main__":
  17. print(getLargestSub("acbdxmng"))
  1. # @Filename: 统计字符串中连续的重复字符个数.py
  2. """
  3. 分析:
  4. 1. 普通遍历法,增加两个变量maxLen和curMaxLen即可
  5. 2. 采用递归法
  6. """
  7. def getMaxDupChar(s, startIndex, curMaxLen, maxLen):
  8. '''
  9. if startIndex == len(s)-1:
  10. return max(maxLen, curMaxLen)
  11. if s[startIndex] != s[startIndex + 1]:
  12. if maxLen < curMaxLen:
  13. maxLen = curMaxLen
  14. startIndex = startIndex + 1
  15. curMaxLen = 1
  16. return getMaxDupChar(s, startIndex, curMaxLen, maxLen)
  17. elif s[startIndex] == s[startIndex + 1]:
  18. return getMaxDupChar(s, startIndex+1, curMaxLen+1, maxLen)
  19. '''
  20. if startIndex + curMaxLen >= len(s):
  21. return max(maxLen, curMaxLen)
  22. if s[startIndex] != s[startIndex+curMaxLen]:
  23. if curMaxLen > maxLen:
  24. maxLen = curMaxLen
  25. startIndex += curMaxLen
  26. curMaxLen = 1
  27. return getMaxDupChar(s, startIndex, curMaxLen, maxLen)
  28. else:
  29. curMaxLen += 1
  30. return getMaxDupChar(s, startIndex, curMaxLen, maxLen)
  31. if __name__ == "__main__":
  32. s = "aabbbbddddddddfdfffff"
  33. print(getMaxDupChar(s, 0, 1, 0))

4. 排列组合与概率

  1. # @Filename: 如何判断还有几盏灯泡亮着.py
  2. """
  3. 判断还有几盏灯泡亮着
  4. 1. 100个灯泡排成一排,第一次将拉动所有灯泡,第二次将拉动2的倍数的灯泡,第三次将拉动3的倍数的灯泡,一次递推,拉动造成 相反的结果
  5. 2. 拉满100次后,剩下亮着的灯泡的编号
  6. 引申:如果没有拉满100次,如果拉了50次呢
  7. 那么50次以内的灯泡,按照以上判断,50次之后的灯泡需要判断是否存在组合因子统计(还要统计这样的个数)(一个在50内,一个在50外,这样的话,这个也算拉了奇数次,同时还要)
  8. """
  9. def factorisOdd(a):
  10. i = 1
  11. count = 0
  12. while i <= a:
  13. if a % i == 0:
  14. count += 1
  15. i +=1
  16. if count % 2 == 0:
  17. return 0
  18. return 1
  19. def totalCount(n):
  20. count = 0
  21. for i in range(1, n+1):
  22. if factorisOdd(i):
  23. count += 1
  24. print(i)
  25. totalCount(100)
  1. # @Filename: 如何拿到最多金币.py
  2. """
  3. 题目:10个房间有金币,每个房间只能进入一次,只能在一个房间中拿金币
  4. 策略:前四个房间只看不拿,后面的房间只要看到比前4个房间都多的金币就拿,否则就拿最后一个房间的金币
  5. 分析:
  6. 1. 首先需要生成所有10个房间的金币出现的情况
  7. 2. 模拟策略拿金币,模拟n次,最后统计n次,共拿到最多金币的次数m,最后结果为m/n
  8. 3. 考虑n的值为1000
  9. """
  10. import random
  11. def getMaxNum(count):
  12. # 先随机生成10个门的金币
  13. door = [random.randint(1, count) for _ in range(count)]
  14. max_4 = max(door[:4])
  15. i = 4
  16. while i < count:
  17. if door[i] >= max_4:
  18. return True
  19. i += 1
  20. return False
  21. if __name__ == "__main__":
  22. n = 10000
  23. m = 0
  24. for i in range(n):
  25. m += int(getMaxNum(10))
  26. print(m/n)
  1. # @Filename: 如何求数字的组合.py
  2. """
  3. 题目:给定一串数字(1,2,2,3,4,5),求出所有数字的排列组合
  4. 条件:4不能出现在第三位,3和5不能相连
  5. """
  6. class Test:
  7. def __int__(self, arr):
  8. self.numbers = arr
  9. self.visited = [None] * len(self.numbers)
  10. self.graph = [[None] * len(self.numbers) for _ in range(len(self.numbers))]
  11. self.n = 6
  12. self.combination = ''
  13. self.s = set()
  14. def depthFistSearch(self, start):
  15. self.visited[start] = True
  16. self.combination += str(self.numbers[start])
  17. # 遍历结束
  18. if len(self.combination) == self.n:
  19. # 判断4是否出现在第三位
  20. if self.combination.index("4") != 2:
  21. self.s.add(self.combination)
  22. pass
  23. j = 0
  24. while j < self.n:
  25. if self.graph[start][j] == 1 and self.visited[j] == False:
  26. self.depthFistSearch(j)
  27. j += 1
  28. # 下面两行特别关键,用于恢复递归前一次的现状
  29. self.combination = self.combination[:-1]
  30. self.visited[start] = False
  31. def getAllCombinations(self):
  32. # 构造图
  33. i = 0
  34. while i < self.n:
  35. j = 0
  36. while j < self.n:
  37. if i == j:
  38. self.graph[i][j] = 0
  39. else:
  40. self.graph[i][j] = 1
  41. j += 1
  42. i += 1
  43. # 确保3和5之间不可达
  44. self.graph[3][5] = 0
  45. self.graph[5][3] = 0
  46. i = 0
  47. while i < self.n:
  48. self.depthFistSearch(i)
  49. i += 1
  50. def printAllCombinations(self):
  51. print(self.s)
  52. if __name__ == "__main__":
  53. arr = [1, 2, 2, 3, 4, 5]
  54. t = Test(arr)
  55. t.getAllCombinations()
  56. t.printAllCombinations()
  1. # @Filename: 如何求正整数n所有可能的整数组合.py
  2. # @Software: PyCharm
  3. # @e_mail: sancica@163.com
  4. """
  5. 题目:给定一个整数n,找出所有能够和为n的整数组合,组合按照递增的形式展示
  6. 分析:典型的递归问题
  7. 1. 递归参数:sums, result
  8. 2. 递归结束条件:当sums为0的时候,打印result的值,并返回
  9. 3. 递归方式:外层一个while,控制第一个整数的大小,将第一个整数加入result,同时减小sums的值,递归调用
  10. """
  11. def getAllcombination(sums, result):
  12. if sums == 0:
  13. print(''.join(result))
  14. return
  15. # 每次都从1开始,会出现3,1这种逆序的模式,所以需要改变
  16. # i = 1
  17. i = 1 if not result else int(result[-1])
  18. while i <= sums:
  19. result.append(str(i))
  20. getAllcombination(sums - i, result)
  21. result.pop()
  22. i += 1
  23. getAllcombination(4, [])
  1. # @Filename: 如何用一个随机函数得到另一个随机函数.py
  2. # @Software: PyCharm
  3. # @e_mail: sancica@163.com
  4. """
  5. 题目:有一个随机函数能1/2的概率生成0,1,怎样 通过该随机函数也生成0,1概率分别为1/4, 3/4
  6. 分析:用两个已知随机函数得到的结果进行或即可
  7. """
  8. import random
  9. def get_random():
  10. a1 = int(round(random.random()))
  11. a2 = int(round(random.random()))
  12. result = a1 | a2
  13. return result
  14. n = 0
  15. for i in range(100000):
  16. n += get_random()
  17. print(n / 100000)
  1. # @Filename: 如何等概率地从大小为n的数组中选取m个整数.py
  2. """
  3. 分析:
  4. 每一次选取之后,需要排除该数据,然后再在剩下的数据中选择才能保证概率相等
  5. 1. 第一次1/n
  6. 2. [(n-1)/n]*[1/(n-1)] = 1/n
  7. 具体方案:
  8. 将随机选到的值与最前面的值进行交换
  9. """
  10. import random
  11. def getRandomM(a, n, m):
  12. i = 0
  13. res = []
  14. while i < m:
  15. j = random.randint(i, n - 1)
  16. res.append(a[j])
  17. a[j], a[i] = a[i], a[j]
  18. i += 1
  19. return res
  20. print(getRandomM([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 10, 6))
  1. # @Filename: 如何组合1,2,5这三个数使其和为100.py
  2. # @Software: PyCharm
  3. # @e_mail: sancica@163.com
  4. """
  5. 分析:
  6. 1. 蛮力法
  7. a. 最终公式为x+2y+5z=100, x最大为100, y最大为50, z最大为20
  8. 2. 数字规律法
  9. a. x+5z = 100-2y,,所以x+5z为偶数
  10. b. 依次列举z的值(0-20),求得对应给的x与y
  11. """
  12. def combinationCount(n):
  13. count = 0
  14. m = 0
  15. while m <= n:
  16. count += (n - m + 2) // 2
  17. x = 0 if m % 2 == 0 else 1
  18. while x <= n - m:
  19. print("{}+2x{}+5x{}={}".format(x, (n - x - m) // 2, m // 5, n))
  20. x += 2
  21. m += 5
  22. return count
  23. print(combinationCount(100))

5. 排序

  1. # @Filename: 冒泡排序.py
  2. # @Software: PyCharm
  3. # @e_mail: sancica@163.com
  4. """
  5. 分析:
  6. 冒泡排序:每次都进行全部遍历一遍,比较相邻数据大小,若满足条件则交换相邻数据
  7. 1. 是一种稳定的排序方法
  8. 2. T:最好的情况下时间复杂度为O(n),平均和最坏的情况下时间复杂度为O(n^2),空间复杂度为O(1):交换空间
  9. """
  10. def bubble_sort(arr):
  11. for i in range(len(arr) - 1):
  12. for j in range(1, len(arr) - i):
  13. if arr[j - 1] > arr[j]:
  14. arr[j - 1], arr[j] = arr[j], arr[j - 1]
  15. arr = [3, 4, 2, 8, 9, 5, 1]
  16. bubble_sort(arr)
  17. print(arr)
  1. # @Filename: 基数排序.py
  2. # @Software: PyCharm
  3. # @e_mail: sancica@163.com
  4. """
  5. 分析:
  6. 1. 根据个位数把数字分配到0-9变好的桶中
  7. 2. 将这些树串联起来
  8. 3. 接着再对十位数、百位数分配
  9. """
  10. import math
  11. def radix_sort(arr, radix=10):
  12. # 计算最大数据的最多位数
  13. k = int(math.ceil(math.log(max(arr), radix)))
  14. bucket = [[] for _ in range(radix)]
  15. for i in range(1, k + 1):
  16. for j in arr:
  17. bucket[j // (radix ** (i - 1)) % (radix)].append(j)
  18. del arr[:]
  19. for z in bucket:
  20. arr += z
  21. del z[:]
  22. return arr
  1. # @Filename: 堆排序.py
  2. """
  3. 分析:
  4. 不稳定的排序方法
  5. T:最坏情况也是O(nlogn)
  6. """
  7. def adjust_heap(arr, i, size):
  8. while True:
  9. maxs = i
  10. lchild = 2 * i + 1
  11. rchild = 2 * i + 2
  12. if i < size // 2:
  13. if lchild < size and arr[lchild] > arr[maxs]:
  14. maxs = lchild
  15. if rchild < size and arr[rchild] > arr[maxs]:
  16. maxs = rchild
  17. if maxs != i:
  18. arr[maxs], arr[i] = arr[i], arr[maxs]
  19. i = maxs
  20. else:
  21. break
  22. else:
  23. break
  24. def heap_sort(arr):
  25. first = len(arr) // 2 - 1
  26. for i in range(first, -1, -1):
  27. print(arr[i])
  28. adjust_heap(arr, i, len(arr) - 1)
  29. # 交换堆顶和堆尾
  30. for head_end in range(len(arr)-1, 0, -1):
  31. arr[head_end], arr[0] = arr[0], arr[head_end]
  32. # 由于前面已经调整为大顶堆,所以这里只需对交换头尾之后的对再进行一次调整即可
  33. adjust_heap(arr, 0, head_end-1)
  34. if __name__ == "__main__":
  35. arr = [3, 4, 2, 8, 9, 5, 1]
  36. print(arr)
  37. heap_sort(arr)
  38. print(arr)
  1. # @Filename: 堆排序.py
  2. ""
  3. 分析:
  4. 不稳定的排序方法
  5. T:最坏情况也是Onlogn
  6. """
  7. def adjust_heap(arr, i, size):
  8. while True:
  9. maxs = i
  10. lchild = 2 * i + 1
  11. rchild = 2 * i + 2
  12. if i < size // 2:
  13. if lchild < size and arr[lchild] > arr[maxs]:
  14. maxs = lchild
  15. if rchild < size and arr[rchild] > arr[maxs]:
  16. maxs = rchild
  17. if maxs != i:
  18. arr[maxs], arr[i] = arr[i], arr[maxs]
  19. i = maxs
  20. else:
  21. break
  22. else:
  23. break
  24. def heap_sort(arr):
  25. first = len(arr) // 2 - 1
  26. for i in range(first, -1, -1):
  27. print(arr[i])
  28. adjust_heap(arr, i, len(arr) - 1)
  29. # 交换堆顶和堆尾
  30. for head_end in range(len(arr)-1, 0, -1):
  31. arr[head_end], arr[0] = arr[0], arr[head_end]
  32. # 由于前面已经调整为大顶堆,所以这里只需对交换头尾之后的对再进行一次调整即可
  33. adjust_heap(arr, 0, head_end-1)
  34. if __name__ == "__main__":
  35. arr = [3, 4, 2, 8, 9, 5, 1]
  36. print(arr)
  37. heap_sort(arr)
  38. print(arr)
  1. # @Filename: 归并排序.py
  2. """
  3. 分析:
  4. 归并排序:
  5. 1. 首先将两个相邻的长度为1的子序列进行归并,得到n/2个长度为2或1的有序子序列
  6. 2. 将其两两归并
  7. 3. 重复1和2直到得到一个有序序列为止
  8. 是一种稳定的算法, T:O(nlogn) S:O(n)
  9. """
  10. def merge(left, right):
  11. i, j = 0, 0
  12. result = []
  13. while i < len(left) and j < len(right):
  14. # print(left[i], right[j], right)
  15. if left[i] < right[j]:
  16. result.append(left[i])
  17. i += 1
  18. else:
  19. result.append(right[j])
  20. j += 1
  21. # 添加某边还剩余的部分
  22. result += left[i:]
  23. result += right[j:]
  24. print(result)
  25. return result
  26. def merge_sort(arr):
  27. if len(arr) <= 1:
  28. return arr
  29. num = len(arr) // 2
  30. left = merge_sort(arr[:num])
  31. right = merge_sort(arr[num:])
  32. return merge(left, right)
  33. arr = [3, 4, 2, 8, 9, 5, 1]
  34. print(merge_sort(arr))
  1. # @Filename: 快速排序.py
  2. """
  3. # 分析:
  4. 1. 设置两个变量left, right, 最开始初始化left=0, right= n-1
  5. 2. 设置关键值key = arr[left]
  6. 3. 从right开始往左遍历,如果arr[right] < key,那么将right和left的数据进行交换
  7. 4. 从left开始往右遍历,如果arr[left] > key, 那么将left和right的数据进行交换
  8. 5. 以上步骤知道left == right
  9. 6. 1-5后,会得到两组数据,对每组数据递归调用1-5
  10. *: 当初始的序列整体或者部分有序时,快排性能降低,退化为冒泡排序
  11. T:最坏情况下O(n^2), 最好情况O(nlogn),平均O(nlogn),S(logn)
  12. *:不稳定的排序算法
  13. """
  14. def quick_sort(arr, left, right):
  15. if left < right:
  16. i, j = left, right
  17. key = arr[i]
  18. while i < j:
  19. # 循环从右边遍历
  20. while i < j and arr[j] >= key:
  21. j -= 1
  22. arr[i] = arr[j]
  23. # 循环从左边遍历
  24. while i < j and arr[i] <= key:
  25. i += 1
  26. arr[j] = arr[i]
  27. # 将key赋予中间值
  28. arr[i] = key
  29. quick_sort(arr, left, i - 1)
  30. quick_sort(arr, i + 1, right)
  31. arr = [3, 4, 2, 8, 9, 5, 1]
  32. quick_sort(arr, 0, len(arr)-1)
  33. print(arr)
  1. # @Filename: 快速排序.py
  2. """
  3. # 分析:
  4. 1. 设置两个变量left, right, 最开始初始化left=0, right= n-1
  5. 2. 设置关键值key = arr[left]
  6. 3. 从right开始往左遍历,如果arr[right] < key,那么将right和left的数据进行交换
  7. 4. 从left开始往右遍历,如果arr[left] > key, 那么将left和right的数据进行交换
  8. 5. 以上步骤知道left == right
  9. 6. 1-5后,会得到两组数据,对每组数据递归调用1-5
  10. *: 当初始的序列整体或者部分有序时,快排性能降低,退化为冒泡排序
  11. T:最坏情况下O(n^2), 最好情况O(nlogn),平均O(nlogn),S(logn)
  12. *:不稳定的排序算法
  13. """
  14. def quick_sort(arr, left, right):
  15. if left < right:
  16. i, j = left, right
  17. key = arr[i]
  18. while i < j:
  19. # 循环从右边遍历
  20. while i < j and arr[j] >= key:
  21. j -= 1
  22. arr[i] = arr[j]
  23. # 循环从左边遍历
  24. while i < j and arr[i] <= key:
  25. i += 1
  26. arr[j] = arr[i]
  27. # 将key赋予中间值
  28. arr[i] = key
  29. quick_sort(arr, left, i - 1)
  30. quick_sort(arr, i + 1, right)
  31. arr = [3, 4, 2, 8, 9, 5, 1]
  32. quick_sort(arr, 0, len(arr)-1)
  33. print(arr)
  1. # @Filename: 选择排序.py
  2. """
  3. 分析:
  4. 选择排序,每次选择最大或最小的一个数与前面的数进行交换
  5. 1. 是一种不稳定的排序方法, 如:5,8,5,2,9, 第一次选择2,将与第一个5交换,这里就破坏了稳定性了
  6. 2. T:无论好坏都是O(n^2), S:O(1):min
  7. """
  8. def select_sort(arr):
  9. i = 0
  10. while i < len(arr) - 1:
  11. min = i
  12. j = i + 1
  13. while j < len(arr):
  14. if arr[j] < arr[min]:
  15. min = j
  16. j += 1
  17. # 这里是选择后面最小的那一个数据,与最前面的数据进行交换
  18. arr[i], arr[min] = arr[min], arr[i]
  19. i += 1
  20. print("第{}次排序:{}".format(i, arr))
  21. arr = [3, 4, 2, 8, 9, 5, 1]
  22. select_sort(arr)
  23. print(arr)

6. 数组

  1. # @Filename: 从三个有序数组中找出他们的公共元素.py
  2. """
  3. 数组为递增数组
  4. 分析:分别设定i,j,k进行三个数组的遍历
  5. 1. 如果a[i]< b[j],则i++, 直到a[i]>=a[j]
  6. 2. 如果a[j] < b[i],则j++,直到b[j]>=a[i]
  7. 3. 如果两者相等,和c[k]比较,如果相等,记录并i++,如果不相等,k++直到>=a[i],
  8. """
  9. def findCommon(ar1, ar2, ar3):
  10. i = j = k = 0
  11. common = []
  12. while i < len(ar1) and j < len(ar2) and k < len(ar3):
  13. if ar1[i] < ar2[j]:
  14. i += 1
  15. elif ar1[i] > ar2[j]:
  16. j += 1
  17. else:
  18. k += 1
  19. if ar1[i] == ar2[j] == ar3[k]:
  20. common.append(ar1[i])
  21. i += 1
  22. return common
  1. # @Filename: 判断请求能否在给定的存储条件下完成.py
  2. def swap(arr, i, j):
  3. tmp = arr[i]
  4. arr[i] = arr[j]
  5. arr[j] = tmp
  6. def bubbleSort(R, O):
  7. # 冒泡法,由大到小排序
  8. lens = len(R)
  9. i = 0
  10. while i < lens - 1:
  11. j = lens - 1
  12. while j > i:
  13. if R[j] - O[j] > R[j - 1] - O[j - 1]:
  14. swap(R, j, j - 1)
  15. swap(O, j, j - 1)
  16. j -= 1
  17. i += 1
  18. def schedule(R, O, M):
  19. # 先按照R-O的大小对R和O就行排序
  20. bubbleSort(R, O)
  21. # 判断按照当前顺序是否能填充满
  22. total = 0
  23. i = 0
  24. while i < len(R):
  25. total += R[i]
  26. if total > M:
  27. return False
  28. else:
  29. total -= R[i]
  30. total += O[i]
  31. i += 1
  32. return True
  33. if __name__ == "__main__":
  34. R = [10, 15, 23, 20, 6, 9, 7, 16]
  35. O = [2, 7, 8, 4, 5, 8, 6, 8]
  36. N = 8
  37. M = 50
  38. scheduleResult = schedule(R, O, M)
  39. print(scheduleResult)
  40. print(dict(zip(R, O)))
  1. # @Filename: 在不排序的情况下求数组中的中位数.py
  2. # 中位数:奇数个数据的数组,中位数k = arr((e-s)/2); 偶数个数据的数组,中位数k = arr((e-s)/2) + arr((e-s)/2 +1)
  3. """
  4. 分析:使用快排的思想:左边的数据比右边的数据小,我们只需关注一边,知道找到第k大的值即可
  5. """
  6. def partition(arr, low, high):
  7. key = arr[low]
  8. while low < high:
  9. while low < high and arr[high] > key:
  10. high -= 1
  11. arr[low] = arr[high]
  12. while low < high and arr[low] < key:
  13. low += 1
  14. arr[high] = arr[low]
  15. arr[low] = key
  16. return low
  17. def getMid(arr):
  18. le = len(arr)
  19. k = (le - 1) // 2
  20. low = 0
  21. high = le - 1
  22. while True:
  23. # 将数组分为两半
  24. pos = partition(arr, low, high)
  25. if pos + 1 == k:
  26. break
  27. elif pos + 1 < k:
  28. low = pos + 1
  29. else:
  30. high = pos - 1
  31. return arr[k] if le % 2 == 1 else (arr[k] + arr[k + 1]) / 2
  32. if __name__ == "__main__":
  33. arr = [7, 5, 3, 1, 11, 9]
  34. print(getMid(arr))
  1. # @Filename: 在有规律的二维数组中进行高效的数据查找.py
  2. """
  3. 描述:一个二维数组,每一行都是递增顺序,每一列也是递增顺序,给定一个这样的arr和值k,找到k是否存在且所在位置
  4. 分析:
  5. 1. 从右上角开始遍历, 如果当前值大于k,则这一列没必要继续访问,j--, 如果小于k,这一行没必要访问,i++, 否则相等,找到位置
  6. """
  7. def findWithBinary(arr, data):
  8. if not arr:
  9. return False
  10. row = 0
  11. col = len(arr[0])-1
  12. while col >= 0 and row < len(arr):
  13. if arr[row][col] == data:
  14. return True
  15. elif arr[row][col] > data:
  16. col -= 1
  17. elif arr[row][col] < data:
  18. row += 1
  19. return False
  20. if __name__ == "__main__":
  21. arr = [[0, 1, 2, 3, 4], [10, 11, 12, 13, 14]]
  22. print(findWithBinary(arr, 10))
  1. # @Filename: 如何对任务进行调度.py
  2. """
  3. 题目:有n个相同的任务,有m个不同的服务器,每个服务器完成一个任务的时间t[i],求一个任务分配,求出完成n个任务,最短的时间
  4. 分析:
  5. 1. 贪心法
  6. """
  7. def calculate_process_time(t, n):
  8. if t == None:
  9. return None
  10. m = len(t)
  11. proTime = [0] * m
  12. i = 0
  13. while i < n:
  14. minTime = proTime[0] + t[0]
  15. minIndex = 0
  16. j = 1
  17. while j < m:
  18. if minTime > proTime[j] + t[j]:
  19. minTime = proTime[j] + t[j]
  20. minIndex = j
  21. j += 1
  22. proTime[minIndex] += t[minIndex]
  23. i += 1
  24. return proTime
  25. if __name__ == "__main__":
  26. t = [7, 10]
  27. n = 6
  28. proTime = calculate_process_time(t, n)
  29. print(proTime)
  1. # @Filename: 如何对数组进行循环位移.py
  2. # 要求:时间复杂度O(N),空间复杂度为O(1):允许使用两个附加变量
  3. # 和之间旋转数组的实现一样的
  4. """
  5. 1. 循环移动法
  6. 2. 空间换时间法:先将k到-1之间的数据放入tmp数组中,然后将0到k之间的数据放入tmp中,这里注意k有可能大于n,此时和移动n-k次结果一样
  7. 3. 翻转法:分为A 和B ,先翻转A, 在翻转B, 最后翻转A+B
  8. """
  9. def reverse(arr, start, end):
  10. # python 切片方便字符或者数组的操作,如果不用切片的话,就要自己建立循环和辅助变量,依次交换前后顺序达到逆序的功能
  11. arr[start:end] = arr[start:end][::-1]
  12. def rightShift(arr, k):
  13. if not arr:
  14. return
  15. lens = len(arr)
  16. reverse(arr, 0, lens-k)
  17. reverse(arr, lens-k, lens)
  18. reverse(arr, 0, lens)
  19. if __name__ == "__main__":
  20. k = 4
  21. arr = [1, 2, 3, 4, 5, 6, 7, 8]
  22. rightShift(arr, k)
  23. print(arr)
  1. # @Filename: 如何对磁盘分区.py
  2. """
  3. 题目:M个磁盘,磁盘大小D[i], m个分区,分区大小P[j], *顺序分配*
  4. 分析:
  5. 依次比较即可
  6. """
  7. def is_allocate(d, p):
  8. i = 0
  9. j = 0
  10. while i < len(d):
  11. if d[i] >= p[j]:
  12. remain = d[i] - p[j]
  13. j += 1
  14. if j == len(p):
  15. return True
  16. else:
  17. i += 1
  18. continue
  19. while remain >= p[j]:
  20. j += 1
  21. if j == len(p):
  22. return True
  23. remain = remain - p[j]
  24. i += 1
  25. return False
  26. if __name__ == "__main__":
  27. d = [120, 120, 120]
  28. p = [60, 80, 80, 20, 80]
  29. print(is_allocate(d, p))
  1. # @Filename: 如何寻找最多的覆盖点.py
  2. # 描述:长为L的木棍,一个坐标轴数据的数组,求L能包含最多坐标轴多少个点
  3. """
  4. 分析:
  5. 1. 一次加入点,直到长度大于L,回退j--, 记录此时长度,同时i++ j++
  6. """
  7. def maxCover(arr, L):
  8. maxCount = 1
  9. lens = len(arr)
  10. i = 0
  11. j = 1
  12. while j < lens:
  13. dis_sum = arr[j] - arr[i]
  14. if dis_sum > L:
  15. j -= 1
  16. if j - i + 1 > maxCount:
  17. maxCount = j - i + 1
  18. i += 1
  19. j += 1
  20. j += 1
  21. return maxCount
  22. if __name__ == "__main__":
  23. a = [1, 3, 7, 8, 10, 11, 12, 13, 15, 16, 17, 19, 25]
  24. print(maxCover(a, 8))
  1. # @Filename: 如何求两个有序集合的交集.py
  2. """
  3. 题目:两个有序集合,集合中的元素为一段范围 {[4,8], [9,13]}
  4. 分析:
  5. 1. 不利用有序集合这一特性,进行两个循环遍历
  6. 2. 利用有序集合特性
  7. a. 对于a[i]与b[j]
  8. 1. a[i][0] < b[j][0] and a[i][1] < b[j][0]: 不挨着,只有i++有可能挨着
  9. 1. a[i][0] < b[j][0] and b[j][0] <= a[i][1] < b[j][1]:挨着,记录交集,后面i++有可能挨着
  10. 1. b[j][0] < a[i][0] < b[j][1] and b[j][0] < a[i][1] < b[j][0]:挨着,记录交集,交集为a[i], 后面i++有可能挨着
  11. 1. b[j][0] < a[i][0] <= b[j][1] and a[i][1] > b[j][1]:挨着,记录交集,后面j++有可能挨着
  12. 1. b[j][0] < a[i][0]:不挨着,后面j++有可能挨着
  13. 1. a[i][0] < b[j][0] and a[i][1] > b[j][0]:挨着,记录交集,后面j++有可能挨着
  14. """
  15. def getIntersection(a, b):
  16. result = []
  17. i = j = 0
  18. while j < len(b) and i < len(a):
  19. if a[i][0] < b[j][0] and a[i][1] < b[j][0]:
  20. i += 1
  21. elif a[i][0] < b[j][0] and b[j][0] <= a[i][1] < b[j][1]:
  22. result.append([b[j][0], a[i][1]])
  23. i += 1
  24. elif b[j][0] <= a[i][0] < b[j][1] and b[j][0] < a[i][1] < b[j][1]:
  25. result.append([a[i][0], a[i][1]])
  26. i += 1
  27. elif b[j][0] < a[i][0] <= b[j][1] and a[i][1] > b[j][1]:
  28. result.append([a[i][0], b[j][1]])
  29. j += 1
  30. elif b[j][0] < a[i][0]:
  31. j += 1
  32. else:
  33. result.append([b[j][0], b[j][1]])
  34. j += 1
  35. return result
  36. if __name__ == "__main__":
  37. l1 = [[4, 8], [9, 13]]
  38. l2 = [[6, 12]]
  39. result = getIntersection(l1, l2)
  40. i = 0
  41. print(result)
  1. # @Filename: 如何求集合的所有子集.py
  2. """
  3. 迭代法:
  4. 1. set1中首先放入一个元素
  5. 2. 在加入一个元素,更新set1(1. 在set1中在原来元素中增加当前元素的组合, 2. 增加自身元素)
  6. 3. 循环操作
  7. """
  8. def getAllSub(str):
  9. if not str:
  10. print("参数不合理")
  11. return None
  12. arr = []
  13. # 将第一个节点加入集合中
  14. arr.append(str[0])
  15. i = 1
  16. while i < len(str):
  17. lens = len(arr)
  18. j = 0
  19. # 迭代轮次,添加构造组合
  20. while j < lens:
  21. arr.append(arr[j] + str[i])
  22. j += 1
  23. arr.append(str[i])
  24. i += 1
  25. return arr
  26. if __name__ == "__main__":
  27. result = getAllSub("abc")
  28. print(result)
  1. # @Filename: 如何获得最好的矩阵链相乘方法(动态规划-重点).py
  2. """
  3. 题目:求解多矩阵相乘,如何运用结合律实现计算数量最小化
  4. 分析:
  5. 1. 使用递归法
  6. 2. 使用动态规划
  7. 分析:建立一个矩阵(n*n),记录每一个组合的乘积次数最小值
  8. """
  9. def MatrixChainOrder(p, n):
  10. cost = [[None] * n for _ in range(n)]
  11. i = 1
  12. while i < n:
  13. # 本身到本身的计算次数为0
  14. cost[i][i] = 0
  15. i += 1
  16. cLen = 2
  17. while cLen < n:
  18. i = 1
  19. while i < n - cLen + 1:
  20. j = i + cLen - 1
  21. cost[i][j] = 2 ** 31
  22. k = i
  23. while k < j:
  24. q = cost[i][k] + cost[k + 1][j] + p[i - 1] * p[k] * p[j]
  25. if q < cost[i][j]:
  26. cost[i][j] = q
  27. k += 1
  28. i += 1
  29. cLen += 1
  30. return cost[1][n - 1]
  31. if __name__ == "__main__":
  32. arr = [1, 5, 2, 4, 6]
  33. n = len(arr)
  34. print(str(MatrixChainOrder(arr, n)))
  1. # @Filename: 如何获得最好的矩阵链相乘方法(递归法).py
  2. """
  3. 题目:求解多矩阵相乘,如何运用结合律实现计算数量最小化
  4. 分析:
  5. 1. 使用递归法
  6. """
  7. def MatrixChainOrder(p, i, j):
  8. # p为数组, i和j为括号
  9. if i == j:
  10. return 0
  11. mins = 2 ** 32
  12. k = i
  13. while k < j:
  14. count = MatrixChainOrder(p, i, k) + MatrixChainOrder(p, k + 1, j) + p[i - 1] * p[k] * p[j]
  15. if count < mins:
  16. mins = count
  17. k += 1
  18. return mins
  19. if __name__ == "__main__":
  20. arr = [1, 5, 2, 4, 6]
  21. n = len(arr)
  22. print(str(MatrixChainOrder(arr, 1, n-1)))
  1. # @Filename: 对二维数组进行旋转.py
  2. """
  3. 合理设置起始标记大小,确定起始点后,内部循环col++ & row++进行打印
  4. """
  5. def rotateArr(arr):
  6. le = len(arr[0])
  7. for i in range(len(arr[0])):
  8. col = le - i
  9. row = 0
  10. while col < le:
  11. print(arr[row][col], end=" ")
  12. col += 1
  13. row += 1
  14. print()
  15. le1 = len(arr)
  16. for i in range(le1):
  17. row = 0 + i
  18. col = 0
  19. while row < le1:
  20. print(arr[row][col], end=" ")
  21. col += 1
  22. row += 1
  23. print()
  24. if __name__ == "__main__":
  25. arr = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
  26. rotateArr(arr)
  1. # @Filename: 对大量有重复的数字的数组排序(字典法).py
  2. """
  3. 题目:对有大量重复数字的数组排序
  4. 分析:
  5. 1. 利用AVL树
  6. 2. 利用hash法
  7. """
  8. def sort(arr):
  9. num_count = {}
  10. for i in arr:
  11. if i in num_count:
  12. num_count[i] += 1
  13. else:
  14. num_count[i] = 1
  15. keys = sorted(num_count.keys(), key=lambda i: num_count[i])
  16. i = 0
  17. k = 0
  18. while i < len(arr):
  19. j = 0
  20. while j < num_count[keys[k]]:
  21. arr[i + j] = keys[k]
  22. j += 1
  23. k += 1
  24. i += j
  25. print(arr)
  26. return arr
  27. if __name__ == "__main__":
  28. arr = [15, 12, 15, 2, 2, 12, 2, 3, 12, 100, 3, 3]
  29. sort(arr)
  1. # @Filename: 找出数组中出现一次的数(三个数是唯一的,只需输出一个数即可).py
  2. # 和找出出现奇数次的数(两个奇数)差不多
  3. """
  4. 1. 所有值进行异或得到d, 通过找到一个位分为两组,三个奇数数中分开,变为两组,然后求得一个组中的数,
  5. 2. 得到这个数后,后面还能继续求解剩下的数据
  6. """
  7. def isOne(n, i):
  8. return (n >> i & 1) == 1
  9. def findSingle(arr):
  10. d = 0
  11. for i in arr:
  12. d ^= i
  13. i = 0
  14. while d >> i:
  15. arr1 = 0
  16. arr2 = 0
  17. len_1 = 0
  18. len_2 = 0
  19. if isOne(d, i):
  20. # 对原数组分为两组
  21. for j in arr:
  22. if j >> i & 1 == 1:
  23. arr1 ^= j
  24. len_1 += 1
  25. else:
  26. arr2 ^= j
  27. len_2 += 1
  28. if len_2 % 2 == 0 and arr2 != 0:
  29. return arr1
  30. elif len_1 % 2 == 0 and arr1 != 0:
  31. return arr2
  32. else:
  33. i += 1
  34. else:
  35. i += 1
  36. if __name__ == "__main__":
  37. arr = [6, 3, 4, 5, 9, 4, 3]
  38. print(findSingle(arr))
  1. # @Filename: 找出数组中出现奇数次的数(只有两个奇数).py
  2. # 前提:只有两个元素出现奇数次
  3. # 1. 词典法:记录出现次数
  4. # 2. 异或法:所有数据全部异或,得到出现奇数次的数的异或结果c,找到c中不为0的某一位数n,将c与数组中n位为1的数异或,得到a或者b, 然后c^a = b
  5. def get2Num(arr):
  6. # 首先全部异或
  7. c = 0
  8. for i in arr:
  9. c ^= i
  10. # 找到c中不为0的位置
  11. position = 0
  12. tmpResult = c
  13. while tmpResult & 1 == 0:
  14. position += 1
  15. tmpResult = tmpResult >> 1
  16. a = c
  17. for i in arr:
  18. if i >> position & 1 == 1:
  19. a ^= i
  20. b = c ^ a
  21. return a, b
  22. if __name__ == "__main__":
  23. arr = [3, 5, 6, 6, 5, 7, 2, 2]
  24. print(get2Num(arr))
  1. # @Filename: 找出数组中唯一重复元素2(累加求和法).py
  2. # @Software: PyCharm
  3. # @e_mail: sancica@163.com
  4. # 这里的前提是,数组里面的数据是连续的
  5. # 时间复杂度为O(N), 空间复杂度为O(1)
  6. def findDup(arr):
  7. mi = min(arr)
  8. ma = max(arr)
  9. return sum(arr) - sum([i for i in range(mi, ma + 1)])
  10. if __name__ == "__main__":
  11. arr = [1, 3, 4, 5, 2, 5, 6]
  12. print(findDup(arr))
  1. # @Filename: 找出数组中唯一重复元素3(异或法).py
  2. # @Software: PyCharm
  3. # @e_mail: sancica@163.com
  4. # 同样建立在数据是单一连续的基础上
  5. # time:O(n) space:O(1)
  6. def findDup(arr):
  7. mi = min(arr)
  8. ma = max(arr)
  9. l = ma-mi+1
  10. res = 0
  11. for i in range(len(arr)):
  12. res ^= arr[i]
  13. for i in range(l):
  14. res ^= (mi+i)
  15. return res
  16. if __name__ == "__main__":
  17. arr = [1, 3, 4, 5, 2, 5, 6]
  18. print(findDup(arr))
  1. # @Filename: 找出数组中唯一重复元素4(数据映射法).py
  2. # @Software: PyCharm
  3. # @e_mail: sancica@163.com
  4. # 通过元素值作为下标访问后续节点,同时将当前节点的值设置为相反数
  5. # 采用剑指offer效果更好
  6. def findDup(arr):
  7. index = 0
  8. while 1:
  9. if arr[index] > 0:
  10. arr[index] *= -1
  11. index = -arr[index]
  12. else:
  13. return -arr[index]
  14. if __name__ == "__main__":
  15. arr = [1, 3, 4, 5, 2, 2, 6]
  16. print(findDup(arr))
  1. # @Filename: 找出数组中唯一重复的元素1(字典法).py
  2. # @Software: PyCharm
  3. # @e_mail: sancica@163.com
  4. # 采用dict存储数据,如果当前数据已经在dict中,则该数据为重复数据
  5. # 时间复杂度 :O(N), 空间复杂度O(N)
  6. def findDup(arr):
  7. dic = {}
  8. for i in arr:
  9. if not dic.get(i, 0):
  10. dic[i] = 1
  11. else:
  12. return i
  13. if __name__ == "__main__":
  14. arr = [1, 3, 4, 1, 2, 5, 6]
  15. print(findDup(arr))
  1. # @Filename: 找出数组中的最大值和最小值(分治一次).py
  2. # 采用分治法,将相邻数据分为一组,将每一组的数组小的放在左边,大的放在右边
  3. # 分析:仅仅采用一次分治,但实际上左右可以继续采用分治的思想,利用递归的方法
  4. class MaxMin:
  5. def __init__(self):
  6. self.max = None
  7. self.min = None
  8. def getMax(self):
  9. return self.max
  10. def getMin(self):
  11. return self.min
  12. def getMaxAndMin(self, arr):
  13. if not arr:
  14. print("参数不合法")
  15. return
  16. i = 0
  17. lens = len(arr)
  18. while i < lens-1:
  19. if arr[i] > arr[i + 1]:
  20. tmp = arr[i]
  21. arr[i] = arr[i + 1]
  22. arr[i + 1] = tmp
  23. i += 2
  24. i = 1
  25. self.max = arr[1]
  26. while i < lens:
  27. if arr[i] > self.max:
  28. self.max = arr[i]
  29. i += 2
  30. i = 0
  31. self.min = arr[0]
  32. while i < lens:
  33. if arr[i] < self.min:
  34. self.min = arr[i]
  35. i += 2
  36. if __name__ == "__main__":
  37. arr = [7, 3, 19, 40, 4, 7, 1]
  38. m = MaxMin()
  39. m.getMaxAndMin(arr)
  40. print(m.getMax())
  41. print(m.getMin())
  1. # @Filename: 找出数组中的最大值和最小值(分治递归).py
  2. class MaxMin:
  3. def __init__(self):
  4. self.max = None
  5. self.min = None
  6. def getMax(self):
  7. return self.max
  8. def getMin(self):
  9. return self.min
  10. def getMaxAndMin(self, arr, l, r):
  11. if not arr:
  12. print("参数不合法")
  13. return
  14. list = []
  15. m = (l + r) // 2
  16. if l == r: # l与r之间只存在一个点
  17. list.append(arr[l])
  18. list.append(arr[r])
  19. return list
  20. if l + 1 == r: # 之间存在两个点
  21. list.append(arr[l] if arr[l] <= arr[r] else arr[r])
  22. list.append(arr[r] if arr[l] < arr[r] else arr[l])
  23. return list
  24. lList = self.getMaxAndMin(arr, l, m)
  25. rList = self.getMaxAndMin(arr, m + 1, r)
  26. max = lList[1] if lList[1] > rList[1] else rList[1]
  27. min = lList[0] if lList[0] < rList[0] else rList[0]
  28. list.append(min)
  29. list.append(max)
  30. return list
  31. if __name__ == "__main__":
  32. arr = [7, 3, 19, 40, 4, 7, 1]
  33. m = MaxMin()
  34. result = m.getMaxAndMin(arr, 0, len(arr) - 1)
  35. print(result[0])
  36. print(result[1])
  1. # @Filename: 找出数组中第k小的数.py
  2. """
  3. 1. 排序法:排好序之后,取下标为k-1的数据即可 O(nlog2n)
  4. 2. 部分排序法:对选择排序改造:首先找到最小,然后找第二小,知道找到第k小O(k*n)
  5. 3. 类似快速排序方法
  6. """
  7. def findSmallK(arr, low, high, k):
  8. i = low
  9. j = high
  10. splitElem = arr[i]
  11. # 将数据分为三部分,左边小于splitElem, 中间arr[i]=splitElem, 右边大于splitElem
  12. while i < j:
  13. while i < j and arr[j] >= splitElem:
  14. j -= 1
  15. if i < j:
  16. arr[i] = arr[j]
  17. i += 1
  18. while i < j and arr[i] <= splitElem:
  19. i += 1
  20. if i < j:
  21. arr[j] = arr[i]
  22. j -= 1
  23. arr[i] = splitElem
  24. subArrayIndex = i - low
  25. if subArrayIndex == k - 1:
  26. return arr[i]
  27. elif subArrayIndex > k - 1:
  28. # 第k小的数依然在左边
  29. return findSmallK(arr, low, i - 1, k)
  30. else:
  31. # 前面已经有i-low+1个小于第k小的数据了,所以剩下k - (i-low+1)
  32. return findSmallK(arr, i + 1, high, k-(i-low)-1)
  33. if __name__ == "__main__":
  34. k = 3
  35. arr = [4, 0, 1, 0, 2, 3]
  36. print(findSmallK(arr, 0, len(arr) - 1, k))
  1. # @Filename: 找出数组中绝对值最小的数.py
  2. """
  3. 前提:升序数组
  4. 采用二分法查找:
  5. 1. arr[mid] = 0,
  6. 2. arr[mid]<0 arr[mid+1]>0:min(-arr[mid], arr[mid+1)
  7. 3. 如果arr[mid-1]<0 arr[mid]>0:min(-arr[mid-1], arr[mid)
  8. 4. arr[mid]>0, 最小值在左边
  9. 5. arr[mid]<0, 最小值在右边
  10. """
  11. def findMin(arr):
  12. if not arr:
  13. print("输入参数不合理")
  14. return 0
  15. if arr[0] > 0:
  16. return arr[0]
  17. if arr[-1] < 0:
  18. return arr[-1]
  19. begin = 0
  20. end = len(arr) - 1
  21. while True:
  22. mid = begin + (end - begin) // 2
  23. if arr[mid] == 0:
  24. return 0
  25. elif arr[mid] > 0:
  26. if arr[mid - 1] > 0:
  27. end = mid - 1
  28. elif arr[mid] == 0:
  29. return 0
  30. else:
  31. return min(-arr[mid - 1], arr[mid])
  32. elif arr[mid] < 0:
  33. if arr[mid + 1] < 0:
  34. begin = mid + 1
  35. elif arr[mid + 1] == 0:
  36. return 0
  37. else:
  38. return min(-arr[mid], arr[mid + 1])
  39. if __name__ == "__main__":
  40. arr = [-10, -5, -2, 7, 15, 50]
  41. print(findMin(arr))
  1. # @Filename: 找出数组连续最大和(动态规划-经典).py
  2. # @Software: PyCharm
  3. # @e_mail: sancica@163.com
  4. """
  5. 经典的动态规划题:
  6. 当访问到第i个节点时:
  7. 1. 最大连续和子数组包含该点 if End[i-1]+arr[i]> arr[i]; End[i] = End[i-1]+arr[i]; All[i] = max(End[i], All[i-1])
  8. 2. 最大连续和子数组不包含该点 if End[i-1]+ arr[i] <= arr[i]; End[i] = arr[i]; All[i] = max(End[i], All[i-1])
  9. """
  10. def maxSubarr(arr):
  11. if not arr:
  12. return 0
  13. # 负责记录连续的子数组的和的值
  14. nEnd = arr[0]
  15. # 负责记录最大的子数组的和的值
  16. nAll = arr[0]
  17. for i in range(1, len(arr)):
  18. if nEnd + arr[i] < arr[i]:
  19. nEnd = arr[i]
  20. else:
  21. nEnd += arr[i]
  22. nAll = max(nAll, nEnd)
  23. return nAll
  1. # @Filename: 找出旋转数组中的最小值.py
  2. # 把一个有序数组最开始的若干个元素搬到数组的末尾,成为数组的旋转
  3. # 两个指针 low high,mid = (high+low)//2
  4. # 由于旋转数组的特性,采用左右递归的方式遍历,直到arr[mid]与arr[mid-1] arr[mid+1]之间满足有一定的大小关系
  5. # 分析:二分查找T:O(log2n),但在满足特殊情况是,需要全部遍历一遍O(N)
  6. def getMin(arr, low, high):
  7. if high < low:
  8. # ???,这里貌似是没有必要判断的
  9. return arr[0]
  10. if high == low:
  11. return arr[low]
  12. # mid = (low+high)/2
  13. # 防止low+high溢出
  14. mid = low + ((high - low) >> 1)
  15. if arr[mid] > arr[mid + 1]:
  16. return arr[mid + 1]
  17. # 这里还是要对应一种特殊情况,即mid为0的时候,但恰好这里能返回正确值
  18. elif arr[mid - 1] > arr[mid]:
  19. return arr[mid]
  20. elif arr[high] > arr[mid]:
  21. # 最小值在左半边
  22. return getMin(arr[:mid], low, mid - 1)
  23. elif arr[low] < arr[mid]:
  24. return getMin(arr[mid + 1:], mid + 1, high)
  25. else:
  26. # 对于一些特殊情况,如[2,2,2,1,2,2]和[2,1,2,2,2,2,2],无法确定是在左半边还是右半边
  27. return min(getMin(arr[:mid], low, mid - 1), getMin(arr[mid + 1:], mid + 1, high))
  28. if __name__ == "__main__":
  29. # array1 = [5, 6, 1, 2, 3, 4]
  30. # mins = getMin(array1, 0, len(array1) - 1)
  31. # print(mins)
  32. # array2 = [1, 1, 0, 1]
  33. # print(getMin(array2, 0, len(array2) - 1))
  34. print(getMin([1, 2], 0, 1))
  1. # @Filename: 按照要求构造新的数组.py
  2. # 给定数组a[N], 获得b[N],b中的每一个元素都是a中除了当前下标元素的乘积
  3. """
  4. 分析:
  5. 1. 首先遍历一遍a,将连乘数据错开放入b中,如b[0] = 1, b[1] = a[0], b[2]=a[0]*a[1]。。。
  6. 2. 再次逆序遍历一遍b,将连乘数据错开与对应b数据相乘,如b[-1] = b[-1]*1 b[-2] = b[-2]*a[-1], b[-3] = b[-1]*b[-2]
  7. """
  8. def calculate(a, b):
  9. b[0] = 1
  10. multi = 1
  11. for i in range(1, len(a)):
  12. multi *= a[i - 1]
  13. b[i] = multi
  14. multi = 1
  15. for i in range(len(a) - 2, -1, -1):
  16. print(i)
  17. multi *= a[i+1]
  18. b[i] *= multi
  19. if __name__ == "__main__":
  20. a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  21. b = [None] * len(a)
  22. calculate(a, b)
  23. print(b)
  1. # @Filename: 旋转数组的实现方式.py
  2. # 分析:遍历了两次数组T:O(N), S:O(1)
  3. def swap(arr, low, high):
  4. while low < high:
  5. tmp = arr[low]
  6. arr[low] = arr[high]
  7. arr[high] = tmp
  8. low += 1
  9. high -= 1
  10. def rotate(arr, div):
  11. # 采用inspace的修改方法,不需要返回数组
  12. if not arr or div < 0 or div >= len(arr):
  13. return
  14. if div == 0 or div == len(arr):
  15. return
  16. swap(arr, 0, div)
  17. swap(arr, div + 1, len(arr) - 1)
  18. swap(arr, 0, len(arr) - 1)
  19. if __name__ == "__main__":
  20. arr = [1, 2, 3, 4, 5]
  21. rotate(arr, 2)
  22. print(arr)
  1. # @Filename: 求数组中两个元素的最小距离.py
  2. # 采用动态规划,利用两个变量记录最近访问到的num1和num2
  3. def minDistance(arr, num1, num2):
  4. lastPos1 = None
  5. lastPos2 = None
  6. min_dis = len(arr)
  7. for i in range(len(arr)):
  8. if arr[i] == num1:
  9. lastPos1 = i
  10. if lastPos2:
  11. # min_dis = min_dis if abs(lastPos2 - lastPos1) > min_dis else abs(lastPos2 - lastPos1)
  12. min_dis = min(min_dis, lastPos1 - lastPos2)
  13. if arr[i] == num2:
  14. lastPos2 = i
  15. if lastPos1:
  16. # min_dis = min_dis if abs(lastPos2 - lastPos1) > min_dis else abs(lastPos2 - lastPos1)
  17. min_dis = min(min_dis, lastPos2 - lastPos1)
  18. return min_dis
  19. if __name__ == "__main__":
  20. arr = [4, 5, 6, 4, 7, 4, 6, 4, 7, 8, 5, 6, 4, 3, 10, 8]
  21. num1 = 4
  22. num2 = 8
  23. print(minDistance(arr, num1, num2))
  1. # @Filename: 求解最小三元组距离.py
  2. """
  3. 1. 蛮力法:三个循环依次记录
  4. 2. 最小距离法
  5. """
  6. def mins(a, b, c):
  7. mins = min(a, b, c)
  8. return mins
  9. def maxes(a, b, c):
  10. return max(a, b, c)
  11. def minDistance(a, b, c):
  12. aLen = len(a)
  13. blen = len(b)
  14. clen = len(c)
  15. curDist = 0
  16. minsd = 0
  17. minDist = 2 ** 32
  18. i = 0
  19. j = 0
  20. k = 0
  21. while True:
  22. # 计算得到最短距离
  23. curDist = maxes(a[i], b[j], c[k]) - mins(a[i], b[j], c[k])
  24. if curDist < minDist:
  25. minDist = curDist
  26. # 获取当前最小值的那一个数组,并在该数组中进行移动操作,只有在该数组中进行操作才能降低距离
  27. minsd = mins(a[i], b[j], c[k])
  28. if minsd == a[i]:
  29. i += 1
  30. if i >= aLen:
  31. break
  32. elif minsd == b[j]:
  33. j += 1
  34. if j >= aLen:
  35. break
  36. else:
  37. k += 1
  38. if k >= clen:
  39. break
  40. return minDist
  41. if __name__ == "__main__":
  42. a = [3, 4, 5, 7, 15]
  43. b = [10, 12, 14, 16, 17]
  44. c = [20, 21, 23, 24, 37, 30]
  45. print(minDistance(a,b,c))
  1. # @Filename: 求解迷宫问题.py
  2. """
  3. 题目:从一个矩阵的左上角走到右下角,其中1表示路径,0表示不通
  4. 分析:采用递归法
  5. """
  6. class Maze:
  7. def __init__(self):
  8. self.N = 4
  9. def printSolution(self, sol):
  10. i = 0
  11. while i < self.N:
  12. j = 0
  13. while j < self.N:
  14. print(sol[i][j], end=" ")
  15. j += 1
  16. print()
  17. i += 1
  18. def isSafe(self, maze, x, y):
  19. return x >= 0 and x < self.N and y >= 0 and y < self.N and maze[x][y] == 1
  20. def getPath(self, maze, x, y, sol):
  21. if x == self.N - 1 and y == self.N - 1:
  22. # 到达中点
  23. sol[x][y] = 1
  24. return True
  25. if self.isSafe(maze, x, y):
  26. sol[x][y] = 1
  27. if self.getPath(maze, x, y + 1, sol):
  28. return True
  29. if self.getPath(maze, x + 1, y, sol):
  30. return True
  31. sol[x][y] = 0
  32. return False
  33. return False
  34. if __name__ == "__main__":
  35. rat = Maze()
  36. maze = [[1, 0, 0, 0], [1, 1, 0, 1], [0, 1, 0, 0], [1, 1, 1, 1]]
  37. sol = [[0, 0, 0, 0] for _ in range(4)]
  38. res = rat.getPath(maze, 0, 0, sol)
  39. if res:
  40. rat.printSolution(sol)
  1. # @Filename: 给定一个矩阵,顺时针向里打印数据.py
  2. # matrix类型为二维列表,需要返回列表
  3. def printMatrix(matrix):
  4. # write code here
  5. if not matrix:
  6. return matrix
  7. res = []
  8. startX = 0
  9. while len(matrix) > 2 * startX and len(matrix[0]) > 2 * startX:
  10. res.extend(print_matrix_incircle(matrix, startX))
  11. startX += 1
  12. return res
  13. def print_matrix_incircle(matrix, start):
  14. res = []
  15. endX = len(matrix[0]) - 1 - start
  16. endY = len(matrix) - 1 - start
  17. # 从左往右打印,没有约束条件
  18. for i in range(start, endX + 1):
  19. print(matrix[start][i])
  20. res.append(matrix[start][i])
  21. # 从上往下打印,至少有两行
  22. if endY > start:
  23. for i in range(start + 1, endY + 1):
  24. print(matrix[i][endX])
  25. res.append(matrix[i][endX])
  26. # 从右往左打印,至少有两行两列
  27. if start < endX and endY > start:
  28. for i in range(endX - 1, start - 1, -1):
  29. print(matrix[endY][i])
  30. res.append(matrix[endY][i])
  31. # 从下往上打印,至少有三行两列
  32. if start < endY - 1 and start < endX:
  33. for i in range(endY - 1, start, -1):
  34. print(matrix[i][start])
  35. res.append(matrix[i][start])
  36. return res
  37. print(printMatrix([[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]]))

7. 栈

  1. # @Filename: 从数组中找出满足a+b=c+d的数.py
  2. # @Software: PyCharm
  3. # @e_mail: sancica@163.com
  4. # 使用字典存储 key:两个数的值 value为数据对
  5. def findPairs(arr):
  6. dic = {}
  7. for i in range(len(arr)):
  8. for j in range(i, len(arr)):
  9. if arr[i] + arr[j] in dic:
  10. print(dic[arr[i] + arr[j]][0], " + ", dic[arr[i] + arr[j]][1], " = ", arr[i], " + ", arr[j])
  11. else:
  12. dic[arr[i] + arr[j]] = [arr[i], arr[j]]
  13. if __name__ == "__main__":
  14. arr = [3, 4, 7, 10, 20, 9, 8]
  1. # @Filename: 实现LRU缓存.py
  2. # LRU : least recently used,最近最少使用
  3. # 1. 使用双向链表实现队列的操作,将最近使用的数据放在链表头,当链表满了之后,删除最后节点
  4. # 2. 使用哈希表用于缓存已经存在的页面
  5. # 每当访问一个页面,首先在哈希表中查找存不存在,存在则移动队列中的数据到表头,不存在则添加数据到队列表头(需要考虑队列是否已满)
  6. # deque本身是为了高效实现插入和删除的双向列表,使用与栈和队列, 增加了appendleft()和popleft()
  7. from collections import deque
  8. class LRU:
  9. def __init__(self, cacheSize):
  10. self.cacheSize = cacheSize
  11. self.queue = deque()
  12. self.hashSet = set()
  13. def isQueueFull(self):
  14. return len(self.queue) == self.cacheSize
  15. # 如果队列满了,删除末尾数据
  16. def enqueue(self, pageNum):
  17. if self.isQueueFull():
  18. self.hashSet.remove(self.queue[-1])
  19. self.queue.pop()
  20. self.queue.appendleft(pageNum)
  21. self.hashSet.add(pageNum)
  22. def accessPage(self, pageNum):
  23. if pageNum not in self.hashSet:
  24. self.enqueue(pageNum)
  25. elif pageNum != self.queue[0]:
  26. self.queue.remove(pageNum)
  27. self.queue.appendleft(pageNum)
  28. def printQueue(self):
  29. while len(self.queue) > 0:
  30. print(self.queue.popleft(), sep=" ")
  31. if __name__ == "__main__":
  32. lru = LRU(3)
  33. lru.accessPage(1)
  34. lru.accessPage(2)
  35. lru.accessPage(4)
  36. lru.accessPage(5)
  37. lru.accessPage(6)
  38. lru.accessPage(7)
  39. lru.printQueue()
  1. # @Filename: 栈排序.py
  2. class Stack:
  3. # 模拟栈
  4. def __init__(self):
  5. self.items = []
  6. def empty(self):
  7. return True if not self.items else False
  8. def size(self):
  9. return len(self.items)
  10. # 返回栈顶元素
  11. def peek(self):
  12. if not self.empty():
  13. return self.items[-1]
  14. else:
  15. return None
  16. # 弹栈
  17. def pop(self):
  18. if not self.empty():
  19. return self.items.pop()
  20. else:
  21. print("栈已空")
  22. return None
  23. # 压栈
  24. def push(self, item):
  25. self.items.append(item)
  26. def moveBottomToTop(s):
  27. # 将栈底元素移动到栈顶,其他栈元素顺序不变
  28. if s.empty():
  29. return
  30. top1 = s.pop()
  31. if not s.empty():
  32. moveBottomToTop(s)
  33. top2 = s.peek()
  34. if top2 < top1:
  35. s.pop()
  36. s.push(top1)
  37. s.push(top2)
  38. return
  39. s.push(top1)
  40. # 翻转栈
  41. def sort_stack(s):
  42. if s.empty():
  43. return
  44. moveBottomToTop(s)
  45. # 记录逆序的顺序,后面递归要还原回去
  46. top = s.peek()
  47. print(top)
  48. s.pop()
  49. # 将子栈的栈底移动到子栈的栈顶
  50. sort_stack(s)
  51. s.push(top)
  52. if __name__ == "__main__":
  53. s = Stack()
  54. for i in range(1, 6):
  55. s.push(i)
  56. print("before reverse")
  57. print(s.items)
  58. sort_stack(s)
  59. print("after reverse")
  1. # @Filename: 根据入栈顺序判断可能的出栈顺序.py
  2. # 依次入栈,如果当前入栈元素与出栈元素第一个相等,则出栈,出栈对比元素删除,继续入栈,循环
  3. class Stack:
  4. def __init__(self):
  5. self.items = []
  6. # 判断栈是否为空
  7. def empty(self):
  8. return True if not self.items else False
  9. # 返回栈的大小
  10. def size(self):
  11. return len(self.items)
  12. # 返回栈顶元素
  13. def peek(self):
  14. return self.items[-1]
  15. # 出栈
  16. def pop(self):
  17. if self.empty():
  18. print("栈已空")
  19. return None
  20. else:
  21. return self.items.pop()
  22. # 入栈
  23. def push(self, item):
  24. self.items.append(item)
  25. def isPopSerial(push, pop):
  26. if not push or not pop:
  27. return False
  28. if len(push) != len(pop):
  29. return False
  30. pushIndex = 0
  31. popIndex = 0
  32. stack = Stack()
  33. while pushIndex < len(push):
  34. stack.push(push[pushIndex])
  35. pushIndex += 1
  36. while not stack.empty() and stack.peek() == pop[popIndex]:
  37. stack.pop()
  38. popIndex += 1
  39. # 这里只需判断入栈是否为空就行了
  40. return stack.empty()
  41. if __name__ == "__main__":
  42. push = "123324"
  43. pop = "5213254"
  44. if isPopSerial(push, pop):
  45. print(pop +"是出栈顺序")
  46. else:
  47. print(pop +"不是出栈顺序")
  1. # @Filename: 用两个栈模拟队列操作.py
  2. # @Software: PyCharm
  3. # @e_mail: sancica@163.com
  4. # 入队列时将栈A正常入栈,当要出队列时判断:B是否为空:1.为空,则依次弹出A元素,放入栈B,2:不为空,直接输出栈B的内容
  5. class Stack:
  6. def __init__(self):
  7. self.items = []
  8. def empty(self):
  9. return True if not self.items else False
  10. def size(self):
  11. return len(self.items)
  12. def peek(self):
  13. return self.items[-1]
  14. def pop(self):
  15. return self.items.pop()
  16. def push(self, item):
  17. self.items.append(item)
  18. class MyQueue:
  19. def __init__(self):
  20. self.A = Stack()
  21. self.B = Stack()
  22. def push(self, item):
  23. self.A.push(item)
  24. def pop(self):
  25. if not self.B.empty():
  26. return self.B.pop()
  27. else:
  28. for i in range(self.A.size()):
  29. self.B.push(self.A.pop())
  30. return self.B.pop()
  31. if __name__ == "__main__":
  32. queue = MyQueue()
  33. queue.push(1)
  34. queue.push(2)
  35. print("输出队列元素", queue.pop())
  36. print("输出队列元素", queue.pop())
  1. # @Filename: 翻转栈的所有元素.py
  2. # 由于栈有两种实现方式,数组和链表
  3. # 栈的翻转对于链表来说,相当于链表的逆序
  4. # 本程序考虑数组的实现模式
  5. """
  6. 首先将栈底数据放在栈顶,然后对剩下栈内容做相同操作进行递归
  7. """
  8. class Stack:
  9. # 模拟栈
  10. def __init__(self):
  11. self.items = []
  12. def empty(self):
  13. return True if not self.items else False
  14. def size(self):
  15. return len(self.items)
  16. # 返回栈顶元素
  17. def peek(self):
  18. if not self.empty():
  19. return self.items[-1]
  20. else:
  21. return None
  22. # 弹栈
  23. def pop(self):
  24. if not self.empty():
  25. return self.items.pop()
  26. else:
  27. print("栈已空")
  28. return None
  29. # 压栈
  30. def push(self, item):
  31. self.items.append(item)
  32. def moveBottomToTop(s):
  33. # 将栈底元素移动到栈顶,其他栈元素顺序不变
  34. if s.empty():
  35. return
  36. top1 = s.pop()
  37. if not s.empty():
  38. moveBottomToTop(s)
  39. top2 = s.peek()
  40. s.pop()
  41. s.push(top1)
  42. s.push(top2)
  43. else:
  44. s.push(top1)
  45. # 翻转栈
  46. def reverse_stack(s):
  47. if s.empty():
  48. return
  49. moveBottomToTop(s)
  50. # 记录逆序的顺序,后面递归要还原回去
  51. top = s.peek()
  52. print(top)
  53. s.pop()
  54. # 将子栈的栈底移动到子栈的栈顶
  55. reverse_stack(s)
  56. s.push(top)
  57. if __name__ == "__main__":
  58. s = Stack()
  59. for i in range(1, 6):
  60. s.push(i)
  61. print("before reverse")
  62. print(s.items)
  63. reverse_stack(s)
  64. print("after reverse")
  65. print(s.items)
  1. # @Filename: 通过给定的车票找出旅程.py
  2. # 不考虑环的情况
  3. # 构建一个车票旅途的字典,同时建立一个reverse字典,由于只有起点没有前继,所以只有起点在reverse字典中没有key
  4. def printResult(inputs):
  5. reverse_inputs = dict(zip(inputs.values(), inputs.keys()))
  6. reverse_values = reverse_inputs
  7. start = None
  8. for k, v in inputs.items():
  9. if k not in reverse_values:
  10. start = k
  11. break
  12. print(start)
  13. # 通过开始点,构建旅途
  14. while start in inputs:
  15. print(start+"->"+inputs[start])
  16. start = inputs[start]
  17. if __name__ == "__main__":
  18. inputs = dict()
  19. inputs['西安'] = '成都'
  20. inputs['北京'] = '上海'
  21. inputs['大连'] = '西安'
  22. inputs['上海'] = '大连'
  23. printResult(inputs)

8. 链表

  1. # @Filename: 两个链表数之和(链表相加法).py
  2. # 时间复杂度:由于只需要一次遍历,为O(N), 空间复杂度:O(N),需要一个长为N的链表存储
  3. class LNode:
  4. def __init__(self):
  5. self.data = None
  6. self.next = None
  7. def add(head1, head2):
  8. if not head1 or not head1.next:
  9. return head2
  10. if not head2 or not head2.next:
  11. return head1
  12. c = 0 # 表示进位
  13. result_head = LNode()
  14. cur1 = head1.next
  15. cur2 = head2.next
  16. cur3 = result_head
  17. # 使用另一个链表保存结果, 也可以采用其中一条链表保存结果
  18. while cur1 and cur2:
  19. tmp = LNode()
  20. result = cur1.data + cur2.data + c
  21. if result > 9:
  22. c = 1
  23. tmp.data = result - 10
  24. else:
  25. tmp.data = result
  26. c = 0
  27. cur3.next = tmp
  28. cur3 = tmp
  29. cur1 = cur1.next
  30. cur2 = cur2.next
  31. # 如果cur1数据较多,将cur1的数据继续添加在result_head中
  32. if cur1:
  33. while cur1:
  34. tmp = LNode()
  35. sum = cur1.data + c
  36. tmp.data = sum % 10
  37. c = sum//10
  38. cur3.next = tmp
  39. cur3 = tmp
  40. cur1 = cur1.next
  41. if cur2:
  42. while cur2:
  43. tmp = LNode()
  44. sum = cur2.data + c
  45. tmp.data = sum % 10
  46. c = sum // 10
  47. cur3.next = tmp
  48. cur3 = tmp
  49. cur2 = cur2.next
  50. # 如果任然有进位的话
  51. if c:
  52. tmp = LNode()
  53. tmp.data = 1
  54. cur3.next = tmp
  55. return result_head
  1. # @Filename: 判断链表是否有环.py
  2. # @Software: PyCharm
  3. # @e_mail: sancica@163.com
  4. # 采用快慢指针,如果有环,那么总有一次,两个指针相等
  5. # 如果有环,则怎样确定环的入口位置:
  6. import random
  7. """
  8. 1. 先找到相遇点
  9. 2. 分别从头结点相遇点一次遍历,相遇的位置即入口点
  10. """
  11. class LNode:
  12. def __init__(self):
  13. self.data = None
  14. self.next = None
  15. def has_circle(head):
  16. slow = head
  17. fast = head
  18. while fast and fast.next:
  19. slow = slow.next
  20. fast = fast.next.next
  21. if slow == fast:
  22. return slow
  23. return False
  24. if __name__ == "__main__":
  25. # 创建一个循环链表
  26. circle_entrance = random.randint(5, 10)
  27. print(circle_entrance)
  28. entrance_node = None
  29. i = 1
  30. head = LNode()
  31. cur = head
  32. while i < 20:
  33. tmp = LNode()
  34. tmp.data = i
  35. cur.next = tmp
  36. cur = tmp
  37. if i == circle_entrance:
  38. entrance_node = cur
  39. i += 1
  40. # 连接形成环
  41. cur.next = entrance_node
  42. # 判断是否有环
  43. if has_circle(head.next):
  44. meet_node = has_circle(head.next)
  45. cur = head.next
  46. while cur != meet_node:
  47. cur = cur.next
  48. meet_node = meet_node.next
  49. print(meet_node.data)
  50. else:
  51. print("没有环")
  52. # 如果有环,找出入口点
  1. # @Filename: 合并两个有序链表.py
  2. # 分别用两个指针遍历,分别比较当前两个指针的大小,根据要求进行插入操作
  3. class LNode:
  4. def __init__(self):
  5. self.data = None
  6. self.next = None
  7. def merge_two_list(h1, h2):
  8. if not h1 and not h1.next:
  9. return h2
  10. if not h2 and not h2.next:
  11. return h1
  12. cur1 = h1.next
  13. cur2 = h2.next
  14. next = h2.next.next
  15. pre = h1
  16. while cur1 and cur2:
  17. if cur1.data >= cur2.data:
  18. cur2.next = pre.next
  19. pre.next = cur2
  20. pre = cur2
  21. cur2 = next
  22. if cur2:
  23. next = next.next
  24. else:
  25. pre = cur1
  26. cur1 = cur1.next
  27. # 判断哪一个链表先完
  28. if not cur1:
  29. pre.next = cur2
  30. return
  31. else:
  32. return
  1. # @Filename: 向右旋转k个位置.py
  2. class LNode:
  3. def __init__(self):
  4. self.data = None
  5. self.next = None
  6. def ReverseK(head, k):
  7. cur = head
  8. next_k = head
  9. i = 0
  10. while i < k and next_k:
  11. next_k = next_k.next
  12. i += 1
  13. if i < k:
  14. # 链表还没有k长
  15. return None
  16. while next_k.next:
  17. cur = cur.next
  18. next_k = next_k.next
  19. second_head = cur.next
  20. cur.next = None
  21. next_k.next = head.next
  22. head.next = second_head
  23. return head
  1. # @Filename: 如何展开链接列表.py
  2. class Node:
  3. def __init__(self):
  4. self.data = None
  5. self.right = None
  6. self.down = None
  7. class MergeList:
  8. def __init__(self):
  9. self.head = None
  10. def insert(self, head_ref, data):
  11. tmp = Node()
  12. tmp.data = data
  13. tmp.down = head_ref
  14. head_ref = tmp
  15. return head_ref
  16. def merge(self, a, b):
  17. if not a:
  18. return b
  19. if not b:
  20. return a
  21. if a.data < b.data:
  22. result = a
  23. result.down = self.merge(a.down, b)
  24. else:
  25. result = b
  26. result.down = self.merge(a, b.down)
  27. return result
  28. def flatten(self, root):
  29. if not root or not root.right:
  30. return root
  31. # 递归处理root.right链表
  32. root.right = self.flatten(root.right)
  33. root = self.merge(root, root.right)
  1. # @Filename: 无序链表移除重复项(hashset)3.py
  2. # @Software: PyCharm
  3. # @e_mail: sancica@163.com
  4. # 空间换时间,遍历时将数据载入list,每次遍历查看是否出现
  5. class LNode:
  6. def __init__(self):
  7. self.data = None
  8. self.next = None
  9. def remove_duplication(head):
  10. if not head or not head.next or not head.next.next:
  11. return
  12. node = []
  13. cur = head.next
  14. pre = head
  15. while cur:
  16. if cur.data not in node:
  17. node.append(cur.data)
  18. pre = cur
  19. cur = cur.next
  20. else:
  21. pre.next = cur.next
  22. cur = pre.next
  1. # @Filename: 无序链表移除重复项(双重循环)3.py
  2. # @Software: PyCharm
  3. # @e_mail: sancica@163.com
  4. # 对于无顺序的链表,删除其中重复的项目
  5. """
  6. 分析:利用双重循环,时间复杂度为O(N*N), 空间负责度为O(1),常数个指针变量保存前继节点等
  7. """
  8. class LNode:
  9. def __init__(self):
  10. self.data = None
  11. self.next = None
  12. def remove_duplication(head):
  13. if not head or not head.next:
  14. return
  15. Outer_cur = head.next
  16. while Outer_cur:
  17. Inter_pre = Outer_cur
  18. Inter_cur = Outer_cur.next
  19. while Inter_cur:
  20. if Inter_cur.data == Outer_cur.data:
  21. # 删除当前节点
  22. Inter_pre.next = Inter_cur.next
  23. Inter_cur = Inter_cur.next
  24. else:
  25. Inter_pre = Inter_cur
  26. Inter_cur = Inter_cur.next
  27. print(Outer_cur.data, Outer_cur.next)
  28. Outer_cur = Outer_cur.next
  1. # @Filename: 无序链表移除重复项(递归)2.py
  2. # @Software: PyCharm
  3. # @e_mail: sancica@163.com
  4. """
  5. 分析:还是双重遍历,时间复杂度为O(N*N),增加了额外的函数调用,效率较低
  6. """
  7. class LNode:
  8. def __init__(self):
  9. self.data = None
  10. self.next = None
  11. def removeDupRecursive(head):
  12. if not head.next:
  13. return head
  14. cur = head
  15. head.next = removeDupRecursive(head.next)
  16. pointer = head.next
  17. while pointer:
  18. if head.data == pointer.data:
  19. cur.next = pointer.next
  20. pointer = cur.next
  21. else:
  22. pointer = pointer.next
  23. cur = cur.next
  24. return head
  25. def remove_duplication(head):
  26. if not head:
  27. return
  28. head.next = removeDupRecursive(head.next)
  1. # @Filename: 查找链表倒数第K个元素.py
  2. # @Software: PyCharm
  3. # @e_mail: sancica@163.com
  4. # 采用两个指针,指针之间间隔k个node,当前面一个指针指向none,返回第一个指针的值
  5. class LNode:
  6. def __init__(self):
  7. self.data = None
  8. self.next = None
  9. def FindLastK(head, k):
  10. cur = head
  11. next_k = head
  12. i = 0
  13. while i < k and next_k:
  14. next_k = next_k.next
  15. i += 1
  16. if i < k:
  17. # 链表还没有k长
  18. return None
  19. while next_k:
  20. cur = cur.next
  21. next_k = next_k.next
  22. return cur.data
  1. # @Filename: 链表以K个节点为一组翻转.py
  2. # 这道题不复杂,就是比较繁琐
  3. class LNode:
  4. def __init__(self):
  5. self.data = None
  6. self.next = None
  7. def reverse(begin, end):
  8. pre = None
  9. cur = begin
  10. next = cur.next
  11. while cur != end:
  12. cur.next = pre
  13. pre = cur
  14. cur = next
  15. next = next.next
  16. cur.next = pre
  17. def reverse_per_k(head, k):
  18. if not head and not head.next:
  19. return
  20. j = k
  21. pre = head
  22. begin = head.next
  23. end = begin
  24. while end:
  25. # 找到end指针
  26. while j > 1 and end:
  27. end = end.next
  28. j -= 1
  29. if end:
  30. lnext = end.next
  31. reverse(begin, end)
  32. pre.next = end
  33. pre = begin
  34. pre.next = lnext
  35. begin = lnext
  36. end = begin
  37. j = k
  38. else:
  39. return
  1. # @Filename: 链表相邻元素翻转.py
  2. # @Software: PyCharm
  3. # @e_mail: sancica@163.com
  4. # 1. 通过交换相邻链表的值,但对某些场景不适用
  5. # 2. 遍历就地逆序法,需要添加一些指针
  6. class LNode:
  7. def __init__(self):
  8. self.data = None
  9. self.next = None
  10. def Reverse_eachother(head):
  11. if not head and not head.next:
  12. return
  13. pre = head
  14. cur = pre.next
  15. while cur and cur.next:
  16. next = cur.next.next
  17. pre.next = cur.next
  18. cur.next.next = cur
  19. cur.next = next
  20. pre = cur
  21. cur = next
  1. # @Filename: 链表逆序输出(不改变原结构:递归).py
  2. # 采用递归的方法实现链表的逆序,原链表为1 2 3 4 5 6 7, 那么只要变为 1 (7 6 5 4 3 2)时,只需要将1 放在最后即可实现排序,
  3. # 同时要得到 7 6 5 4 3 2, 只需要变为 2 (3 4 5 6 7)时,将2放在最末尾即可,依次进行递归
  4. class LNode:
  5. def __init__(self):
  6. self.data = None
  7. self.next = None
  8. def RecursiveReverse(head):
  9. if not head or not head.next:
  10. print(head.data)
  11. else:
  12. # 这里的new_head是最后一个数字的指针,一直向上传递,为了让head与新的第一个节点相连
  13. RecursiveReverse(head.next)
  14. print(head.data)
  15. def Reverse_print(head):
  16. if not head:
  17. return
  18. firstNode = head.next
  19. newhead = RecursiveReverse(firstNode)
  1. # @Filename: 链表逆序(递归)2.py
  2. # 采用递归的方法实现链表的逆序,原链表为1 2 3 4 5 6 7, 那么只要变为 1 (7 6 5 4 3 2)时,只需要将1 放在最后即可实现排序,
  3. # 同时要得到 7 6 5 4 3 2, 只需要变为 2 (3 4 5 6 7)时,将2放在最末尾即可,依次进行递归
  4. class LNode:
  5. def __init__(self):
  6. self.data = None
  7. self.next = None
  8. def RecursiveReverse(head):
  9. if not head or not head.next:
  10. return head
  11. else:
  12. # 这里的new_head是最后一个数字的指针,一直向上传递,为了让head与新的第一个节点相连
  13. new_head = RecursiveReverse(head.next)
  14. head.next.next = head
  15. head.next = None
  16. return new_head
  17. def Reverse(head):
  18. if not head:
  19. return
  20. firstNode = head.next
  21. newhead = RecursiveReverse(firstNode)
  22. head.next = newhead
  23. return head
  1. # @Filename: 链表逆序(遍历+指针节点)1.py
  2. # @Software: PyCharm
  3. # @e_mail: sancica@163.com
  4. # 创建链表结点类,使用引用方式实现链表的指针功能
  5. """
  6. 总结:对链表进行一次遍历,时间复杂度为O(N),N为链表长度,需要常数个向量保存pre cur next,空间复杂度为O(1)
  7. """
  8. class LNode:
  9. def __init__(self):
  10. self.data = None
  11. self.next = None
  12. def Reverse(head):
  13. if not head or not head.next:
  14. return
  15. pre = None
  16. cur = head.next
  17. while cur.next:
  18. next = cur.next
  19. cur.next = pre
  20. pre = cur
  21. cur = next
  22. cur.next = pre
  23. head.next = cur
  24. return head
  1. # @Filename: 链表逆序(遍历+插入)3.py
  2. # @Software: PyCharm
  3. # @e_mail: sancica@163.com
  4. # 采用插入法将,遍历数据时,将数据插入到头结点之后
  5. class LNode:
  6. def __init__(self):
  7. self.data = None
  8. self.next = None
  9. def Reverse(head):
  10. if not head or not head.next:
  11. return
  12. cur = head.next.next
  13. # 斩断第一个数据指针
  14. head.next.next = None
  15. while cur:
  16. next = cur.next
  17. cur.next = head.next
  18. head.next = cur
  19. cur = next
  1. # @Filename: 链表重新排序.py
  2. # 将原链表 0 1 2 3 4 5 重新排序为0 5 1 4 2 3, 要求只能修改当前链表,只能修改next域,不能修改数据域
  3. # 分析:先找到中间节点,对后面数据进行逆序,然后在将前半部分与后半部分子链表进行合并, 时间复杂度为O(N),空间复杂度为O(1)
  4. class LNode:
  5. def __init__(self):
  6. self.data = None
  7. self.next = None
  8. def FindMiddleNode(head):
  9. # 使用两个指针,一个速度为1, 一个速度为2,当2为None,1就为中间节点
  10. fast = head
  11. slow = head
  12. while fast and fast.next:
  13. fast = fast.next.next
  14. slow = slow.next
  15. # 将链表进行拆分
  16. second_head = slow.next
  17. slow.next = None
  18. return second_head
  19. def Reverse(head):
  20. pre = None
  21. cur = head
  22. next = head.next
  23. while next:
  24. cur.next = pre
  25. pre = cur
  26. cur = next
  27. next = next.next
  28. cur.next = pre
  29. return cur
  30. def Reorder(head):
  31. if not head or not head.next or not head.next.next or not head.next.next.next:
  32. return
  33. mid = FindMiddleNode(head.next)
  34. # 对后半部分链表进行逆序
  35. cur2 = Reverse(mid)
  36. cur1 = head.next
  37. # 合并两个链表
  38. while cur2 and cur1:
  39. next2 = cur2.next
  40. cur2.next = cur1.next
  41. cur1.next = cur2
  42. cur1 = cur2.next
  43. cur2 = next2




posted on 2019-06-20 15:59  TDS  阅读(368)  评论(0编辑  收藏  举报

导航