├── .gitignore ├── LICENSE ├── README.md ├── arrandmatrix ├── q1.py ├── q10.py ├── q11.py ├── q12.py ├── q13.py ├── q14.py ├── q15.py ├── q16.py ├── q17.py ├── q18.py ├── q19.py ├── q2.py ├── q20.py ├── q21.py ├── q22.py ├── q23.py ├── q24.py ├── q25.py ├── q26.py ├── q3.py ├── q4.py ├── q5.py ├── q6.py ├── q7.py ├── q8.py ├── q9.py ├── 下一个排列.py ├── 套路总结.md ├── 有趣的排序.py ├── 牛牛与妞妞.py ├── 牛牛数星星.py ├── 被围绕的区域.py └── 除自身以外数组的乘积.py ├── basic_algrithms ├── search_algrithms │ ├── benchmark.py │ ├── bfprt.py │ └── binary_search.py ├── sort_algrithms │ ├── benchmark.py │ ├── bubble_sort.py │ ├── bucket_sort.py │ ├── heap_sort.py │ ├── insert_sort.py │ ├── merge_sort.py │ ├── quick_sort.py │ ├── radix_sort.py │ ├── select_sort.py │ ├── shellsort.py │ └── top.py └── str_algrithms │ ├── kmp.py │ └── manacher.py ├── bigdata ├── q1.md ├── q2.md ├── q3.md ├── q4.md ├── q5.md └── q6.md ├── binarytree ├── q1.py ├── q10.py ├── q11.py ├── q12.py ├── q13.py ├── q14.py ├── q15.py ├── q16.py ├── q17.py ├── q18.py ├── q2.py ├── q20.py ├── q21.py ├── q22.py ├── q23.py ├── q24.py ├── q3.py ├── q4.py ├── q5.py ├── q6.py ├── q7.py ├── q8.py ├── q9.py ├── toolcls.py ├── 填充同一层的兄弟节点.py └── 构建平衡搜索二叉树.py ├── bitoper ├── q1.py ├── q2.py ├── q3.py ├── q4.py ├── q5.py ├── q6.py └── 套路总结.md ├── dp ├── q1.py ├── q10.py ├── q11.py ├── q12.py ├── q13.py ├── q14.py ├── q15.py ├── q16.py ├── q17.py ├── q18.py ├── q2.py ├── q3.py ├── q4.py ├── q5.py ├── q6.py ├── q7.py ├── q8.py ├── q9.py ├── 不等式数列.py └── 牛牛与世界杯门票.py ├── leetcode ├── 162.寻找峰值.py ├── 274.H指数.py └── 3.最长无重复子串.py ├── linkedlist ├── __init__.py ├── q1.py ├── q10.py ├── q11.py ├── q12.py ├── q13.py ├── q14.py ├── q15.py ├── q16.py ├── q17.py ├── q18.py ├── q19.py ├── q2.py ├── q20.py ├── q21.py ├── q3.py ├── q4.py ├── q5.py ├── q6.py ├── q7.py ├── q8.py ├── q9.py ├── toolcls.py ├── 排序链表.py └── 链表部分区间反转.py ├── other ├── kmp_shortstr.py ├── manacher_shortest.py ├── max_gap.py ├── n个骰子的点数.py ├── q1.py ├── q10.py ├── q11.py ├── q12.py ├── q13.py ├── q14.py ├── q15.py ├── q16.py ├── q17.py ├── q18.py ├── q19.py ├── q2.py ├── q20.py ├── q21.py ├── q22.py ├── q23.py ├── q24.py ├── q26.py ├── q27.py ├── q28.py ├── q29.py ├── q3.py ├── q30.py ├── q31.py ├── q32.py ├── q33.py ├── q34.py ├── q4.py ├── q5.py ├── q6.py ├── q7.py ├── q8.py ├── q9.py ├── 套路总结.md └── 扑克牌中的顺子.py ├── stackandqueue ├── q1.py ├── q10.py ├── q11.py ├── q2.py ├── q3.py ├── q4.py ├── q5.py ├── q6.py ├── q7.py ├── q8.py ├── q9.py ├── 丑数问题.py └── 括号匹配问题.py ├── string ├── q1.py ├── q10.py ├── q11.py ├── q12.py ├── q13.py ├── q14.py ├── q15.py ├── q16.py ├── q17.py ├── q18.py ├── q19.py ├── q2.py ├── q20.py ├── q21.py ├── q22.py ├── q23.py ├── q3.py ├── q4.py ├── q5.py ├── q6.py ├── q7.py ├── q8.py ├── q9.py ├── 两种排序方法.py ├── 单词拆分.py ├── 合法括号生成.py └── 小易喜欢的单词.py └── 分类代表题目 ├── TOPK及第K个相关问题 └── 查找数组中第k大的元素.py ├── 二分查找 ├── x的平方根.py ├── 不修改数组找出重复的数字.py ├── 搜索插入位置.py ├── 搜索范围.py ├── 无重复元素的旋转数组.py ├── 有重复元素的旋转数组.py └── 苹果堆.py ├── 位运算 ├── 重复数组中找出唯一一对不重复的数字.py └── 重复数组中找出唯一不重复的数字.py ├── 动态规划 ├── 分饼干.py ├── 最长上升子序列.py ├── 礼物的最大值.py ├── 股票类型问题 │ ├── 一次交易.py │ ├── 两次交易.py │ ├── 指定K次交易.py │ └── 无限次交易.py └── 跳石板问题.py ├── 双指针问题 ├── 和为s的三个数.py ├── 和为s的两个数字.py ├── 和大于等于s的最短子数组.py ├── 回文链表判断.py └── 装最多水的容器.py ├── 图问题 └── 拓扑排序 │ └── 课程表.py ├── 套路总结.md ├── 子数组累加和问题 ├── 数组中全是正整数且累加和为给定值.py ├── 数组中有正有负有零且累加和为给定值.py ├── 数组中有正有负有零且累加和小于等于给定值.py ├── 求累加和为最大值.py └── 累加和为0的最长子数组.py ├── 字符串 ├── 前缀树的应用.py ├── 排列组合问题.py ├── 数字翻译成字符串(动态规划).py ├── 最长不含重复字符的子字符串(动态规划).py ├── 最长公共子串.py └── 构造回文(最长公共子序列).py ├── 并查集 └── 畅通工程.py ├── 数学知识 ├── Nim游戏.py ├── N的阶乘的末尾0的个数.py ├── 不可以重复选择的组合的和.py ├── 可以有重复元素的组合的和.py ├── 完全平方数.py ├── 排列数.py ├── 摩尔投票算法.py ├── 数列还原.py ├── 混合颜料.py ├── 第k个排列.py ├── 计数质数.py ├── 超级素数幂.py ├── 阶乘后的零.py └── 页码统计.py ├── 树的遍历及各种变型 └── 完全二叉树的节点.py ├── 深搜、广搜和回溯 ├── 01翻转.py ├── 单词接龙.py ├── 单词搜索.py ├── 地牢逃脱.py ├── 堆砖块.py ├── 岛屿的个数.py ├── 推箱子.py ├── 牛牛游玩记.py └── 饥饿的小易.py ├── 滑动窗口问题 └── 同一个滑动窗口内最大值和最小值之差.py ├── 矩阵打印相关问题 ├── 90度旋转矩阵.py ├── 斜对角线打印矩阵.py └── 顺时针按圈打印矩阵.py ├── 背包及变型 └── 双核处理.py └── 贪心 ├── 最大数.py └── 跳跃游戏.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | .idea -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 resolvewang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## algorithm questions and answers 2 | 这个仓库会记录我使用Python解答[程序员代码面试指南:IT名企算法与数据结构题目最优解](https://book.douban.com/subject/26638586/) 3 | 这本书上的算法题,左老师提供了Java版本的解答,可以点击[这里](http://www.broadview.com.cn/book/651)下载Java版本的答案。 4 | 5 | 使用Python来实现的话,主要有两个原因:1)在实现之前思考是否可以直接使用Python标准库来实现,进一步熟悉标准库,当然为了理解算法,并不会真正 6 | 使用标准库的API来实现关键算法 2)左老师已经实现了Java版本,自己再使用Java来做的话,就会忍不住去参考左老师的代码。 7 | 8 | 本来这个项目完全不用单独开一个repo的,但是怕自己半途而废,所以希望使用这种方式来自勉,从而一直坚持下去。 9 | 10 | 11 | ## update 12 | - 刷了一些题目之后,发现了部分题目有固定的模式和套路,为了让自己能够将这种情况的解题思路牢记于心, 13 | 所以添加了一个部分`套路总结` 14 | - 目前大概刷完了 *程序员代码面试指南* 和 *剑指offer*,下一步计划是刷leetcode中低难度的题。 15 | 由于左神的书上的题目已经十分具有代表性了,所以 *剑指offer* 和 *leetcode* 中的题目只会挑选 16 | 部分具有代表性的题目进行记录和分类总结 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /arrandmatrix/q1.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定一个矩阵matrix,请按照转圈的方式打印它。 3 | 4 | 例如: 5 | 1 2 3 4 6 | 5 6 7 8 7 | 9 10 11 12 8 | 13 14 15 16 9 | 10 | 打印结果为: 11 | 1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10 12 | """ 13 | 14 | 15 | class MatrixPrinter: 16 | @classmethod 17 | def print_matrix(cls, matrix): 18 | start_r = 0 19 | start_c = 0 20 | end_r = len(matrix) - 1 21 | end_c = len(matrix[0]) - 1 22 | while start_r <= end_r and start_c <= end_c: 23 | cls.print_cycle_of_matrix(matrix, start_r, start_c, end_r, end_c) 24 | start_c += 1 25 | start_r += 1 26 | end_r -= 1 27 | end_c -= 1 28 | 29 | @classmethod 30 | def print_cycle_of_matrix(cls, matrix, start_r, start_c, end_r, end_c): 31 | if start_r == end_r: 32 | while start_c <= end_c: 33 | print(matrix[start_r][start_c], end=' ') 34 | start_c += 1 35 | elif start_c == end_c: 36 | while start_r <= end_r: 37 | print(matrix[start_r][start_c], end=' ') 38 | start_r += 1 39 | else: 40 | tmp_c = start_c 41 | tmp_r = start_r 42 | while tmp_c != end_c: 43 | print(matrix[tmp_r][tmp_c], end=' ') 44 | tmp_c += 1 45 | while tmp_r != end_r: 46 | print(matrix[tmp_r][tmp_c], end=' ') 47 | tmp_r += 1 48 | while tmp_c != start_c: 49 | print(matrix[tmp_r][tmp_c], end=' ') 50 | tmp_c -= 1 51 | while tmp_r != start_r: 52 | print(matrix[tmp_r][tmp_c], end=' ') 53 | tmp_r -= 1 54 | 55 | 56 | if __name__ == '__main__': 57 | arr = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]] 58 | MatrixPrinter.print_matrix(arr) -------------------------------------------------------------------------------- /arrandmatrix/q10.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个数组arr,该数组无序,但每个值均为正数,再给定一个正数k。求arr的 3 | 所有子数组中所有元素相加和为k的最长子数组长度。 4 | 例如,arr=[1, 2, 1, 1, 1], k=3 5 | 累加和为3的最长子数组长度为[1, 1, 1],所以结果返回3 6 | """ 7 | from array import array 8 | 9 | 10 | class MaxLength: 11 | @classmethod 12 | def get_max_length_for_unsigned_arr(cls, arr, num): 13 | length = len(arr) 14 | if length == 0: 15 | return 0 16 | left_index = 0 17 | right_index = 0 18 | value_total = 0 19 | values_length = 0 20 | count = 0 21 | while right_index < length: 22 | value_total += arr[right_index] 23 | count += 1 24 | if value_total == num: 25 | if count > values_length: 26 | values_length = count 27 | value_total -= arr[left_index] 28 | left_index += 1 29 | count -= 1 30 | elif value_total > num: 31 | value_total -= arr[left_index] 32 | left_index += 1 33 | count -= 1 34 | right_index += 1 35 | 36 | return values_length 37 | 38 | 39 | if __name__ == '__main__': 40 | cur_arr = array('i', [9, 3, 8, 4, 6, 3, 1, 8, 6, 5, 4, 5, 3, 5, 4, 10, 5, 8, 3, 5]) 41 | print(MaxLength.get_max_length_for_unsigned_arr(cur_arr, 15)) -------------------------------------------------------------------------------- /arrandmatrix/q11.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定一个无序数组arr,其中元素可正、可负、可0,给定一个整数k。求 3 | arr所有的子数组中累加和为k的最长子数组长度。 4 | 5 | 【补充题目】 6 | 给定一个无序数组arr,其中元素可正、可负、可0,求arr所有的子数组中正数和负数 7 | 个数相等的最长子数组长度。 8 | 9 | 该问题的解法可以从原问题的解法出发,把正数变成1,把负数变成-1,则问题转化为了 10 | 求给定数组中,和为0的最长子数组。 11 | 12 | 【补充题目】 13 | 给定一个无序数组arr,其中元素只是1或0.求arr所有的子数组中0和1个数相等的最长 14 | 子数组长度。 15 | 16 | 该问题的解法也可以从原问题的解法出发,把0变成-1,则该问题转化为了求给定数组中, 17 | 和为0的最长子数组。 18 | """ 19 | 20 | 21 | from array import array 22 | 23 | 24 | class LongestSumOfSubArray: 25 | @classmethod 26 | def get_longest_sum(cls, arr, k): 27 | if len(arr) == 0: 28 | return 0 29 | i = 0 30 | total = 0 31 | max_length = 0 32 | d = dict() 33 | d.setdefault(0, -1) 34 | while i < len(arr): 35 | total += arr[i] 36 | if total not in d: 37 | d[total] = i 38 | if (total - k) in d: 39 | j = d.get(total - k) 40 | if max_length < i - j: 41 | max_length = i - j 42 | i += 1 43 | return max_length 44 | 45 | 46 | if __name__ == '__main__': 47 | # 3 48 | arr = array('i', [1, 2, 3, 3]) 49 | print(LongestSumOfSubArray.get_longest_sum(arr, 6)) 50 | 51 | # 6 52 | arr = array('i', [2, 3, 3, 0, 0, 5, 3, -2, 5, 0, -2, 5, -1, 4, 4, 4, 4, -3, 5, 3]) 53 | print(LongestSumOfSubArray.get_longest_sum(arr, 10)) 54 | 55 | # 9 56 | arr = array('i', [4, 0, 1, 0, 0, 4, 0, -5, 5, 3, 3, 4, 2, -1, -3, 4, -4, 1, 5, 2]) 57 | print(LongestSumOfSubArray.get_longest_sum(arr, 10)) -------------------------------------------------------------------------------- /arrandmatrix/q12.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定一个无序数组arr,其中元素可正、可负、可0,给定一个整数k.求arr所有 3 | 的子数组中累加和小于或等于k的最长子数组长度. 4 | 例如: arr=[3,-2,-4,0,6],k=-2,相加和小于等于-2的最长子数组为[3,-2,-4,0], 5 | 所以结果返回4. 6 | """ 7 | 8 | 9 | class MaxLengthGetter: 10 | @classmethod 11 | def get_max_length(cls, arr, k): 12 | if not arr: 13 | return 0 14 | 15 | sum_arr = list() 16 | pre_sum = 0 17 | max_pre_sum = 0 18 | help_arr = list() 19 | for i in range(len(arr)): 20 | cur_sum = pre_sum + arr[i] 21 | sum_arr.append(cur_sum) 22 | pre_sum = cur_sum 23 | if i == 0: 24 | max_pre_sum = cur_sum 25 | else: 26 | max_pre_sum = max([max_pre_sum, cur_sum]) 27 | help_arr.append(max_pre_sum) 28 | max_length = 0 29 | for i in range(len(arr)): 30 | diff = sum_arr[i] - k 31 | res = cls.bisect_select(help_arr[:i+1], diff) 32 | if res != -1: 33 | max_length = max([max_length, i - res + 1]) 34 | 35 | return max_length 36 | 37 | @classmethod 38 | def bisect_select(cls, arr, k): 39 | left = 0 40 | right = len(arr) - 1 41 | res = -1 42 | 43 | while left <= right: 44 | mid = (left + right) >> 1 45 | if arr[mid] >= k: 46 | res = mid 47 | right = mid - 1 48 | else: 49 | left = mid + 1 50 | 51 | return res 52 | 53 | 54 | if __name__ == '__main__': 55 | my_arr = [3, -2, -4, 0, 6] 56 | print(MaxLengthGetter.get_max_length(my_arr, -2)) -------------------------------------------------------------------------------- /arrandmatrix/q14.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定一个长度为N的整型数组arr,其中有N个互不相等的自然数1~N,请实现 3 | arr的排序,但是不要把下标0~N-1位置上的数通过直接赋值的方式替换成1~N. 4 | 5 | 要求: 6 | 时间复杂度为O(N),额外空间复杂度是O(1) 7 | """ 8 | 9 | 10 | class ArrSorter: 11 | @classmethod 12 | def sort_arr(cls, arr): 13 | if not arr or len(arr) < 2: 14 | return arr 15 | 16 | for i in range(len(arr)): 17 | while arr[i] != i + 1: 18 | # 交换的时候需要使用临时变量,因为arr[i]会改变 19 | tmp = arr[i] - 1 20 | arr[i], arr[tmp] = arr[tmp], arr[i] 21 | return arr 22 | 23 | 24 | if __name__ == '__main__': 25 | my_arr = [3, 5, 1, 2, 8, 6, 4, 7] 26 | print(ArrSorter.sort_arr(my_arr)) -------------------------------------------------------------------------------- /arrandmatrix/q15.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定一个长度不小于2的数组arr,实现一个函数调整arr,要么让所有的偶数 3 | 下标都是偶数,要么让所有的奇数下标都是奇数. 4 | 5 | 要求: 6 | 如果arr的长度为N,函数要求时间复杂度为O(N),额外空间复杂度为O(1). 7 | """ 8 | 9 | 10 | class EvenOddPlacer: 11 | @classmethod 12 | def place_the_num(cls, arr): 13 | even_num = 0 14 | odd_num = 0 15 | for i in range(len(arr)): 16 | if arr[i] % 2 == 0: 17 | odd_num += 1 18 | else: 19 | even_num += 1 20 | 21 | # 数量小的必然有足够的位置放置, j只进不退,则时间复杂度为O(N) 22 | if odd_num <= even_num: 23 | i = 0 24 | j = 1 25 | while i < len(arr): 26 | while arr[i] % 2 != 0 and j < len(arr): 27 | arr[i], arr[j] = arr[j], arr[i] 28 | j += 2 29 | i += 2 30 | else: 31 | i = 1 32 | j = 0 33 | while i < len(arr): 34 | while arr[i] % 2 == 0 and j < len(arr): 35 | arr[i], arr[j] = arr[j], arr[i] 36 | j += 2 37 | i += 2 38 | 39 | return arr 40 | 41 | 42 | if __name__ == '__main__': 43 | my_arr = [1, 8, 3, 2, 4, 6] 44 | print(EvenOddPlacer.place_the_num(my_arr)) -------------------------------------------------------------------------------- /arrandmatrix/q16.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定一个数组arr,返回子数组的最大累加和. 3 | 4 | 例如: 5 | arr=[1, -2, 3, 5, -2, 6, -1],所有的子数组中,[3, 5, -2, 6]可以累加出 6 | 最大的和12,所以返回12. 7 | 8 | 要求: 9 | 如果arr长度为N,要求时间复杂度为O(N),额外空间复杂度为O(1). 10 | """ 11 | 12 | 13 | class MaxSum: 14 | @classmethod 15 | def get_max_sum(cls, arr): 16 | if not arr: 17 | return 0 18 | 19 | sum_arr = [] 20 | for i in range(len(arr)): 21 | if i == 0: 22 | sum_arr.append(arr[i]) 23 | else: 24 | if sum_arr[i-1] < 0: 25 | sum_arr.append(arr[i]) 26 | else: 27 | sum_arr.append(arr[i]+sum_arr[i-1]) 28 | 29 | return max(sum_arr) 30 | 31 | 32 | if __name__ == '__main__': 33 | my_arr = [-2, -3, -5, -1] 34 | print(MaxSum.get_max_sum(my_arr)) -------------------------------------------------------------------------------- /arrandmatrix/q17.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定一个矩阵matrix,其中的值有正、负和0,返回子矩阵的最大累加和. 3 | 4 | 例如,矩阵matrix为 5 | -90 48 78 6 | 64 -40 64 7 | -81 -7 66 8 | 其中,最大累加和的子矩阵为: 9 | 48 78 10 | -40 64 11 | -7 66 12 | 所以返回累加和209. 13 | 14 | 例如,matrix为: 15 | -1 -1 -1 16 | -1 2 2 17 | -1 -1 -1 18 | 19 | 其中,最大累加和的子矩阵为: 20 | 2 2 21 | 所以返回累加和为4. 22 | """ 23 | import sys 24 | from arrandmatrix.q16 import MaxSum 25 | 26 | 27 | class MaxMatrixSum: 28 | @classmethod 29 | def get_max_sum(cls, matrix): 30 | if not matrix: 31 | return 0 32 | 33 | max_value = -sys.maxsize 34 | 35 | for i in range(len(matrix)): 36 | j = i 37 | pre_arr = [0 for _ in range(len(matrix[0]))] 38 | while j < len(matrix): 39 | arr = cls.arr_add(matrix[j], pre_arr) 40 | max_value = max([MaxSum.get_max_sum(arr), max_value]) 41 | j += 1 42 | pre_arr = arr 43 | 44 | return max_value 45 | 46 | @classmethod 47 | def arr_add(cls, arr1, arr2): 48 | return [arr1[i]+arr2[i] for i in range(len(arr1))] 49 | 50 | 51 | if __name__ == '__main__': 52 | my_matrix = [ 53 | [-90, 48, 78], 54 | [64, -40, 64], 55 | [-81, -7, 66] 56 | ] 57 | 58 | print(MaxMatrixSum.get_max_sum(my_matrix)) -------------------------------------------------------------------------------- /arrandmatrix/q18.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:定义局部最小的概念。arr长度为1时,arr[0]是局部最小。arr长度为N(N>1)时, 3 | 如果arr[0]> 1) 34 | if arr[mid-1] < arr[mid]: 35 | return cls.get_local_min_value_detail(arr, start, mid-1) 36 | if arr[mid+1] < arr[mid]: 37 | return cls.get_local_min_value_detail(arr, mid+1, end) 38 | 39 | return mid 40 | 41 | 42 | if __name__ == '__main__': 43 | my_arr = [6, 5, 3, 4, 6, 7, 8] 44 | print(LocalMinValue.get_local_min_value(my_arr)) -------------------------------------------------------------------------------- /arrandmatrix/q19.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定一个double类型的数组arr,其中的元素可正、可负、可0,返回子数组累乘的最大 3 | 乘积。 4 | 5 | 例如: 6 | arr=[-2.5,4,0,3,0.5,8,-1],子数组[3,0.5,8]累乘可以获得最大的乘积12,所以返回12. 7 | """ 8 | 9 | 10 | class SubArrMaxSum: 11 | @classmethod 12 | def get_max_sum(cls, arr): 13 | if not arr: 14 | return 15 | 16 | if len(arr) == 1: 17 | return arr[0] 18 | 19 | pre_max = arr[0] 20 | pre_min = arr[0] 21 | res = arr[0] 22 | 23 | for i in range(1, len(arr)): 24 | end_max = pre_max * arr[i] 25 | end_min = pre_min * arr[i] 26 | 27 | pre_max = max([arr[i], end_max, end_min]) 28 | pre_min = min([arr[i], end_max, end_min]) 29 | 30 | res = max([res, pre_max]) 31 | 32 | return res 33 | 34 | 35 | if __name__ == '__main__': 36 | my_arr = [-2.5, 4, 0, 3, 0.5, 8, -1] 37 | print(SubArrMaxSum.get_max_sum(my_arr)) 38 | -------------------------------------------------------------------------------- /arrandmatrix/q2.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定一个矩阵,大小是N*N,把这个矩阵调整成顺时针转动90度后的形式。 3 | 4 | 例如: 5 | 1 2 3 4 6 | 5 6 7 8 7 | 9 10 11 12 8 | 13 14 15 16 9 | 10 | 顺时针转动90度,为: 11 | 13 9 5 1 12 | 14 10 6 2 13 | 15 11 7 3 14 | 16 12 8 4 15 | """ 16 | 17 | 18 | class MatrixRoater: 19 | @classmethod 20 | def roate_matrix_by_circle(cls, matrix, start_r, start_c, end_r, end_c): 21 | i = 0 22 | 23 | while i < end_c - start_c: 24 | tmp_value = matrix[start_r][start_c+i] 25 | matrix[start_r][start_c+i] = matrix[end_r-i][start_c] 26 | matrix[end_r-i][start_c] = matrix[end_r][end_c-i] 27 | matrix[end_r][end_c-i] = matrix[start_r+i][end_c] 28 | matrix[start_r+i][end_c] = tmp_value 29 | i += 1 30 | 31 | @classmethod 32 | def roate_maxtrix(cls, matrix): 33 | start_r = 0 34 | start_c = 0 35 | end_r = len(matrix) - 1 36 | end_c = len(matrix[0]) - 1 37 | 38 | while start_r <= end_r: 39 | cls.roate_matrix_by_circle(matrix, start_r, start_c, end_r, end_c) 40 | start_r += 1 41 | start_c += 1 42 | end_c -= 1 43 | end_r -= 1 44 | 45 | return matrix 46 | 47 | 48 | if __name__ == '__main__': 49 | arr = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]] 50 | print(MatrixRoater.roate_maxtrix(arr)) -------------------------------------------------------------------------------- /arrandmatrix/q22.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定一个整型数组arr,返回不包含本位置值的累乘数组. 3 | 4 | 例如: 5 | arr=[2, 3, 1, 4],它的累乘数组是[12, 8, 24, 6] 6 | 7 | 要求: 8 | 1.时间复杂度为O(N) 9 | 2.除需要返回的结果数组外,额外空间复杂度为O(1) 10 | 11 | 进阶: 12 | 对时间和空间复杂度都不变,要求不能用除法 13 | """ 14 | 15 | 16 | class ArrProduct: 17 | @classmethod 18 | def get_product_by_div(cls, arr): 19 | if not arr or len(arr) < 2: 20 | return 21 | 22 | all_value = 1 23 | res = [0 for _ in range(len(arr))] 24 | count_zero = 0 25 | zero_index = -1 26 | for i in range(len(arr)): 27 | if arr[i] == 0: 28 | count_zero += 1 29 | zero_index = i 30 | else: 31 | all_value = arr[i] * all_value 32 | 33 | if count_zero > 1: 34 | pass 35 | elif count_zero == 1: 36 | res[zero_index] = all_value 37 | else: 38 | for i in range(len(arr)): 39 | res[i] = int(all_value/arr[i]) 40 | 41 | return res 42 | 43 | @classmethod 44 | def get_product(cls, arr): 45 | if not arr or len(arr) < 2: 46 | return 47 | 48 | res = [0 for _ in range(len(arr))] 49 | res[0] = arr[0] 50 | 51 | for i in range(1, len(arr)): 52 | res[i] = res[i-1] * arr[i] 53 | 54 | tmp = 1 55 | for i in range(len(arr)-1, 0, -1): 56 | if i == len(arr) - 1: 57 | res[i] = res[i-1] 58 | else: 59 | tmp *= arr[i+1] 60 | res[i] = res[i-1] * tmp 61 | 62 | res[0] = tmp 63 | return res 64 | 65 | 66 | if __name__ == '__main__': 67 | my_arr = [1, 2, 3, 4] 68 | print(ArrProduct.get_product_by_div(my_arr)) 69 | print(ArrProduct.get_product(my_arr)) -------------------------------------------------------------------------------- /arrandmatrix/q25.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定一个无序整型数组arr,找到数组中未出现的最小正整数. 3 | 4 | 举例: 5 | arr=[-1, 2, 3, 4],返回1 6 | arr=[1, 2, 3, 4],返回5 7 | """ 8 | 9 | 10 | class SmallestNumFinder: 11 | @classmethod 12 | def get_the_smallest_num(cls, arr): 13 | if not arr: 14 | return 1 15 | 16 | l = 0 17 | r = len(arr) 18 | 19 | while l < r: 20 | if arr[l] == l + 1: 21 | l += 1 22 | print('l', l) 23 | elif arr[l] <= l or arr[l] > r or arr[arr[l]-1] == arr[l]: 24 | r -= 1 25 | arr[l] = arr[r] 26 | else: 27 | cls.swap(arr, l, arr[l]-1) 28 | 29 | return l + 1 30 | 31 | @classmethod 32 | def swap(cls, arr, index1, index2): 33 | tmp = arr[index1] 34 | arr[index1] = arr[index2] 35 | arr[index2] = tmp 36 | 37 | 38 | if __name__ == '__main__': 39 | my_arr = [-1, 1, 2, 3, 4] 40 | print(SmallestNumFinder.get_the_smallest_num(my_arr)) -------------------------------------------------------------------------------- /arrandmatrix/q3.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个矩阵maxtrix,按照“之”字形的方式打印这个矩阵,例如: 3 | 1 2 3 4 4 | 5 6 7 8 5 | 9 10 11 12 6 | 7 | “之”字形打印的结果为:1, 2, 5, 9, 6, 3, 4, 7, 10, 11, 8, 12 8 | 要求: 9 | 额外空间复杂度为O(1)。 10 | """ 11 | 12 | 13 | class MaxtrixPrinter: 14 | @classmethod 15 | def zig_maxtrix_print(cls, matrix): 16 | row_length = len(matrix) 17 | col_length = len(matrix[0]) 18 | if row_length == 1: 19 | for i in matrix[0]: 20 | print(i, end=' ') 21 | 22 | if col_length == 1: 23 | for i in matrix: 24 | print(matrix[i][0], end=' ') 25 | 26 | print(matrix[0][0], end=' ') 27 | 28 | up_to_down = True 29 | tr = 0 30 | tc = 0 31 | dr = 0 32 | dc = 0 33 | 34 | while tr < row_length - 1 and dc < col_length - 1: 35 | if tc < col_length - 1: 36 | tc += 1 37 | else: 38 | tr += 1 39 | 40 | if dr < row_length - 1: 41 | dr += 1 42 | else: 43 | dc += 1 44 | 45 | tmp_tc = tc 46 | tmp_tr = tr 47 | tmp_dr = dr 48 | tmp_dc = dc 49 | 50 | if up_to_down: 51 | while tmp_tr <= tmp_dr and tmp_dc <= tmp_tc: 52 | print(matrix[tmp_tr][tmp_tc], end=' ') 53 | tmp_tr += 1 54 | tmp_tc -= 1 55 | else: 56 | while tmp_tr <= tmp_dr and tmp_dc <= tmp_tc: 57 | print(matrix[tmp_dr][tmp_dc], end=' ') 58 | tmp_dr -= 1 59 | tmp_dc += 1 60 | 61 | up_to_down = not up_to_down 62 | 63 | 64 | if __name__ == '__main__': 65 | arr = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]] 66 | MaxtrixPrinter.zig_maxtrix_print(arr) 67 | 68 | 69 | -------------------------------------------------------------------------------- /arrandmatrix/q5.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个无序数组arr,求出需要排序的最短子数组长度。 3 | 例如: 4 | arr=[1, 5, 3, 4, 2, 6, 7],返回4,因为只有[5, 3, 2, 4]需要排序。 5 | """ 6 | class ShortestSubarr: 7 | @classmethod 8 | def get_shortest_subarr(cls, arr): 9 | if not arr or len(arr) == 1: 10 | return 0 11 | 12 | length = len(arr) 13 | max_index = -1 14 | index = length - 1 15 | 16 | min_value = arr[index] 17 | while index >= 0: 18 | if arr[index] <= min_value: 19 | min_value = arr[index] 20 | else: 21 | max_index = index 22 | index -= 1 23 | 24 | if max_index == -1: 25 | return 0 26 | 27 | min_index = -1 28 | index = 0 29 | max_value = arr[index] 30 | while index < length: 31 | if arr[index] >= max_value: 32 | max_value = arr[index] 33 | else: 34 | min_index = index 35 | index += 1 36 | 37 | return min_index - max_index + 1 38 | 39 | 40 | if __name__ == '__main__': 41 | print(ShortestSubarr.get_shortest_subarr([1, 5, 3, 4, 2, 6, 7])) -------------------------------------------------------------------------------- /arrandmatrix/q7.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定一个有N*M的整型矩阵matrix和一个整数K,matrix的每一行 3 | 和每一列都是排好序的。实现一个函数,判断k是否在matrix中. 4 | 5 | 例如: 6 | 0 1 2 5 7 | 2 3 4 7 8 | 4 4 4 8 9 | 5 7 7 9 10 | 11 | 如果K为7,返回True;如果K为6,返回False. 12 | 13 | 要求: 14 | 时间复杂度为O(N+M),额外空间复杂度为O(1). 15 | """ 16 | 17 | 18 | class NumberFinder: 19 | @classmethod 20 | def find_num_in_matrix(cls, matrix, num): 21 | rows = len(matrix) 22 | cols = len(matrix[0]) 23 | 24 | row = 0 25 | col = cols - 1 26 | while row < rows and col >= 0: 27 | if matrix[row][col] == num: 28 | return True 29 | elif matrix[row][col] > num: 30 | col -= 1 31 | else: 32 | row += 1 33 | else: 34 | return False 35 | 36 | 37 | if __name__ == '__main__': 38 | my_matrix = [ 39 | [0, 1, 2, 3, 4, 5, 6], 40 | [10, 12, 13, 15, 16, 17, 18], 41 | [23, 24, 25, 26, 27, 28, 29], 42 | [166, 176, 186, 187, 190, 195, 200], 43 | [233, 243, 321, 341, 356, 370, 380] 44 | ] 45 | print(NumberFinder.find_num_in_matrix(my_matrix, 233)) 46 | print(NumberFinder.find_num_in_matrix(my_matrix, 203)) 47 | -------------------------------------------------------------------------------- /arrandmatrix/下一个排列.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述: 3 | 4 | 实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。如果不存在 5 | 下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。必须原地修改,只允许使用额外常数 6 | 空间。 7 | 8 | 例子: 9 | 1,2,3 → 1,3,2 10 | 3,2,1 → 1,2,3 11 | 1,1,5 → 1,5,1 12 | """ 13 | 14 | 15 | 16 | 17 | class Solution: 18 | def nextPermutation(self, nums): 19 | """ 20 | :type nums: List[int] 21 | :rtype: void Do not return anything, modify nums in-place instead. 22 | """ 23 | length = len(nums) 24 | if not nums or length < 2: 25 | return 26 | 27 | index = length - 1 28 | need_sort = True 29 | while index >= 1: 30 | if nums[index] <= nums[index - 1]: 31 | index -= 1 32 | else: 33 | tmp = index - 1 34 | while index < length and nums[tmp] <= nums[index]: 35 | if (index + 1 < length and nums[tmp] >= nums[index + 1]) or index + 1 == length: 36 | nums[tmp], nums[index] = nums[index], nums[tmp] 37 | self.reverse(nums, tmp + 1, length - 1) 38 | break 39 | else: 40 | index += 1 41 | need_sort = False 42 | break 43 | if need_sort: 44 | nums.sort() 45 | 46 | def reverse(self, nums, start, end): 47 | while start < end: 48 | nums[start], nums[end] = nums[end], nums[start] 49 | end -= 1 50 | start += 1 51 | 52 | 53 | -------------------------------------------------------------------------------- /arrandmatrix/套路总结.md: -------------------------------------------------------------------------------- 1 | 1.矩阵打印相关问题 2 | 3 | 如果按照某个规律打印矩阵,可以按圈进行考虑。并且考虑几个顶点的横纵坐标变化即可。 4 | 5 | 2.子数组问题 6 | 7 | 如果涉及到有序子数组,那么可以考虑使用左右指针,无序子数组,可以考虑双指针(都从 8 | 左边开始移动) 9 | 10 | 如果是求一个子数组,那么可以考虑按数组每个位置结尾的可能性列举; 11 | 12 | 3.求元素不相同的数组有序后是否相邻元素相差为1 13 | 14 | 找到数组中元素的最大值和最小值,将差值和数组长度进行比较。这样可避免排序。 15 | 16 | 4.转换思维 17 | 18 | 子数组的一个片段arr[i:j]可以转换成 arr[0:j] - arr[0:i] 19 | 20 | 5.求和及其它迭代情况 21 | 22 | 有的时候,需要同时在多个递归或者循环中使用同一个值,我们可以把它设置为全局变量或者类属性。 23 | 24 | 6.几个重要的结构和计算方法 25 | 26 | (1)使用sum(arr[0...i])来构造辅助数组;(2)使用(1)得到的辅助数组构造单调和辅助数组;(3)利用 27 | arr[i:j]=arr[0...j] - arr[0:i]的性质做计算; 28 | 29 | 7.查找问题 30 | 31 | 1)如果是有序数组,第一想到二分查找; 32 | 33 | 2)如果在时间复杂度为O(log(N))可以很轻易做出来,那么一定要思考是否可以用二分查 34 | 找来搞定,时间复杂度为O(log(N)); 35 | 36 | 3)二分查找并不是数组有序时才能用,只要能确定二分两侧的某一侧一定存在想要找的内容,那么 37 | 就可以用二分查找; 38 | 39 | 8.数组的累积计算 40 | 41 | 累加/累乘:考虑从左往右以每个元素结尾的累加/累乘值(arr[0...i]),有的时候由于条件限制可能还需要考虑从右往左 42 | 的累加/累乘值(arr[i...n]),比如考虑除当前位置以外的其它位置的一种计算方式,就需要结合左边和右边两种情况。 43 | 44 | 9.提防错误 45 | 46 | 在使用就地运算交换两个数的时候,涉及到数组下标可能会被前一次运算影响的情况,一定得注意,比如 47 | > arr[l], arr[arr[l]-1] = arr[arr[l]-1], arr[l] 48 | 49 | 这里就需要注意,`arr[l]-1`在第一次运算的时候,已经发生了改变,这里不能用这种方式进行交换,需要将`arr[l]-1` 50 | 用一个临时变量来记录 -------------------------------------------------------------------------------- /arrandmatrix/有趣的排序.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述: 度度熊有一个N个数的数组,他想将数组从小到大 排好序,但是萌萌的度度熊只会 3 | 下面这个操作: 任取数组中的一个数然后将它放置在数组的最后一个位置。 问最少操作多少 4 | 次可以使得数组从小到大有序? 5 | 6 | 输入描述: 7 | 首先输入一个正整数N,接下来的一行输入N个整数。(N <= 50, 每个数的绝对值小于等于1000 8 | 9 | 输出描述: 10 | 输出一个整数表示最少的操作次数。 11 | 12 | 输入例子1: 13 | 4 14 | 19 7 8 25 15 | 16 | 输出例子1: 17 | 2 18 | """ 19 | 20 | 21 | import sys 22 | import copy 23 | 24 | 25 | class Solution: 26 | def get_least_opers(self, arr): 27 | if len(arr) <= 1: 28 | print(0) 29 | return 30 | 31 | cloned_arr = copy.copy(arr) 32 | cloned_arr.sort() 33 | pos = self.get_first_unorder_val(arr) 34 | if pos == -1: 35 | print(0) 36 | return 37 | index = 0 38 | while index < len(cloned_arr): 39 | if cloned_arr[index] == arr[pos]: 40 | print(len(cloned_arr)-index) 41 | return 42 | index += 1 43 | 44 | def get_first_unorder_val(self, arr): 45 | cloned_arr = copy.copy(arr) 46 | cloned_arr.sort() 47 | smaller_pos = -1 48 | index = 0 49 | 50 | while index < len(cloned_arr): 51 | val = cloned_arr[index] 52 | for pos, value in enumerate(arr): 53 | if value == val: 54 | if pos >= smaller_pos: 55 | smaller_pos = pos 56 | else: 57 | return pos 58 | index += 1 59 | 60 | return -1 61 | 62 | 63 | if __name__ == '__main__': 64 | n = int(sys.stdin.readline()) 65 | args = list(map(int, sys.stdin.readline().split())) 66 | s = Solution() 67 | s.get_least_opers(args) 68 | -------------------------------------------------------------------------------- /arrandmatrix/牛牛与妞妞.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述: 牛牛与妞妞闲来无聊,便拿出扑克牌来进行游戏。游戏的规则很简单,两个人随机 3 | 抽取四张牌,四张牌的数字和最大的取胜(该扑克牌总张数为52张,没有大小王,A=1,J=11, 4 | Q=12,K=13,每种数字有四张牌),现在两人已经分别亮出了自己的前三张牌,牛牛想要知 5 | 道自己要赢得游戏的概率有多大。 6 | 7 | 输入包含两行,第一行输入三个整数a1,b1,c1(1≤a1,b1,c1≤13),表示牛牛亮出的扑克牌。 8 | 第二行输入三个整数a2,b2,c2(1≤a2,b2,c2≤13),表示妞妞所亮出的扑克牌。 9 | 10 | 示例: 11 | 3 5 7 12 | 2 6 8 13 | 14 | 输出: 15 | 0.3995 16 | """ 17 | 18 | 19 | class Solution: 20 | def get_properties(self, a1, a2, a3, b1, b2, b3): 21 | a = [0 for _ in range(60)] 22 | vis = [0 for _ in range(15)] 23 | vis[a1] += 1 24 | vis[a2] += 1 25 | vis[a3] += 1 26 | vis[b1] += 1 27 | vis[b2] += 1 28 | vis[b3] += 1 29 | sum1 = a1 + a2 + a3 30 | sum2 = b1 + b2 + b3 31 | cnt = 0 32 | l, r = 0, 0 33 | for i in range(1, 14): 34 | for j in range(0, 4-vis[i]): 35 | a[cnt] = i 36 | cnt += 1 37 | 38 | for i in range(cnt): 39 | sum1 += a[i] 40 | for j in range(cnt): 41 | if i == j: 42 | continue 43 | sum2 += a[j] 44 | r += 1 45 | if sum1 > sum2: 46 | l += 1 47 | sum2 -= a[j] 48 | sum1 -= a[i] 49 | print('%.4f' % (l / r)) 50 | 51 | 52 | if __name__ == '__main__': 53 | s = Solution() 54 | a, b, c = list(map(int, input().split())) 55 | d, e, f = list(map(int, input().split())) 56 | s.get_properties(a, b, c, d, e, f) -------------------------------------------------------------------------------- /arrandmatrix/牛牛数星星.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述: 一闪一闪亮晶晶,满天都是小星星,牛牛晚上闲来无聊,便躺在床上数星星。牛牛把星星图看成一个 3 | 平面,左上角为原点(坐标为(1, 1))。现在有n颗星星,他给每颗星星都标上坐标(xi,yi),表示这颗星星 4 | 在第x行,第y列。现在,牛牛想问你m个问题,给你两个点的坐标(a1, b1)(a2,b2),表示一个矩形的左上 5 | 角的点坐标和右下角的点坐标,请问在这个矩形内有多少颗星星(边界上的点也算是矩形内)。 6 | 7 | 举例 8 | 输入: 9 | 4 10 | 1 1 11 | 2 2 12 | 3 3 13 | 1 3 14 | 4 15 | 1 1 2 2 16 | 1 1 3 3 17 | 2 2 3 3 18 | 1 2 2 3 19 | 20 | 输出: 21 | 2 22 | 4 23 | 2 24 | 2 25 | """ 26 | 27 | 28 | import sys 29 | 30 | 31 | class StarCounter: 32 | def get_num(self): 33 | n = int(sys.stdin.readline().strip()) 34 | data = [[0 for _ in range(1001)] for _ in range(1001)] 35 | for i in range(n): 36 | line = sys.stdin.readline().strip() 37 | p0, p1 = list(map(int, line.split())) 38 | data[p0][p1] = 1 39 | for i in range(1, 1001): 40 | for j in range(1, 1001): 41 | data[i][j] += data[i][j-1] + data[i-1][j] - data[i-1][j-1] 42 | 43 | m = int(sys.stdin.readline().strip()) 44 | for i in range(m): 45 | line = sys.stdin.readline().strip() 46 | a1, b1, a2, b2 = list(map(int, line.split())) 47 | rs = data[a2][b2]-data[a2][b1-1]-data[a1-1][b2]+data[a1-1][b1-1] 48 | print(rs) 49 | del a1, b1, a2, b2, line 50 | 51 | 52 | if __name__ == '__main__': 53 | star_counter = StarCounter() 54 | star_counter.get_num() -------------------------------------------------------------------------------- /arrandmatrix/除自身以外数组的乘积.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题: 给定长度为 n 的整数数组 nums,其中 n > 1,返回输出数组 output , 3 | 其中 output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积。 4 | 5 | 示例: 6 | [1,2,3,4] => [24,12,8,6] 7 | 8 | 要求: 9 | 请不要使用除法,且在 O(n) 时间复杂度内完成此题。 10 | """ 11 | 12 | 13 | class Solution: 14 | def productExceptSelf(self, nums): 15 | """ 16 | :type nums: List[int] 17 | :rtype: List[int] 18 | """ 19 | ret = [1 for _ in nums] 20 | tmp = 1 21 | 22 | p = 1 23 | for i in range(len(nums)): 24 | ret[i] = p 25 | p *= nums[i] 26 | 27 | p = 1 28 | for i in range(len(nums) - 1, -1, -1): 29 | ret[i] *= p 30 | p *= nums[i] 31 | 32 | return ret 33 | -------------------------------------------------------------------------------- /basic_algrithms/search_algrithms/benchmark.py: -------------------------------------------------------------------------------- 1 | """ 2 | 以系统提供的绝对正确的方法来对比我们自己实现的算法 3 | """ 4 | 5 | import random 6 | 7 | 8 | class Comparator: 9 | @classmethod 10 | def compare_with_bench(cls, arr, value): 11 | """只考虑了基础数据类型""" 12 | return True if value in arr else False 13 | 14 | @classmethod 15 | def gen_random_array(cls, max_size, max_value): 16 | arr = list() 17 | for _ in range(max_size): 18 | arr.append(random.randint(1, max_value)) 19 | 20 | return sorted(arr) 21 | 22 | @classmethod 23 | def gen_random_value(cls, max_value): 24 | return random.randint(1, max_value) -------------------------------------------------------------------------------- /basic_algrithms/search_algrithms/binary_search.py: -------------------------------------------------------------------------------- 1 | """ 2 | 二分搜索 3 | """ 4 | 5 | 6 | from basic_algrithms.search_algrithms.benchmark import Comparator 7 | 8 | 9 | def binary_search(arr, value): 10 | if not arr: 11 | return False 12 | length = len(arr) 13 | left = 0 14 | right = length-1 15 | 16 | while left <= right: 17 | mid = left + ((right-left) >> 1) 18 | if arr[mid] == value: 19 | return True 20 | elif arr[mid] > value: 21 | right = mid - 1 22 | else: 23 | left = mid + 1 24 | 25 | return False 26 | 27 | 28 | if __name__ == '__main__': 29 | max_times = 500 30 | max_size = 100 31 | max_value = 100 32 | 33 | res = True 34 | 35 | for i in range(max_times): 36 | arr = Comparator.gen_random_array(max_size, max_value) 37 | value = Comparator.gen_random_value(max_value) 38 | 39 | res1 = binary_search(arr, value) 40 | res2 = Comparator.compare_with_bench(arr, value) 41 | 42 | if res1 != res2: 43 | print(arr) 44 | print(value) 45 | print(res1) 46 | print(res2) 47 | res = False 48 | break 49 | 50 | if res: 51 | print('Success') 52 | else: 53 | print('Failed') 54 | 55 | -------------------------------------------------------------------------------- /basic_algrithms/sort_algrithms/benchmark.py: -------------------------------------------------------------------------------- 1 | """ 2 | 以系统提供的绝对正确的方法来对比我们自己实现的算法 3 | """ 4 | 5 | import copy 6 | import random 7 | 8 | 9 | class Comparator: 10 | @classmethod 11 | def compare_with_bench(cls, arr): 12 | # 注意这里返回的是一个新数组 13 | return sorted(arr) 14 | 15 | @classmethod 16 | def copy_arr(cls, arr): 17 | return copy.copy(arr) 18 | 19 | @classmethod 20 | def is_equal(cls, arr1, arr2): 21 | if (not arr1 and arr2) or (not arr2 and arr1): 22 | return False 23 | 24 | if not arr1 and not arr2: 25 | return True 26 | 27 | if len(arr1) != len(arr2): 28 | return False 29 | 30 | for index, value in enumerate(arr1): 31 | if arr2[index] != value: 32 | return False 33 | 34 | return True 35 | 36 | @classmethod 37 | def gen_random_array(cls, max_size, max_value): 38 | arr = list() 39 | for _ in range(max_size): 40 | arr.append(random.randint(1, max_value)) 41 | 42 | return arr 43 | 44 | -------------------------------------------------------------------------------- /basic_algrithms/sort_algrithms/bubble_sort.py: -------------------------------------------------------------------------------- 1 | """ 2 | 冒泡排序,注意这里会修改数组元素的位置顺序 3 | """ 4 | 5 | 6 | from basic_algrithms.sort_algrithms.benchmark import Comparator 7 | 8 | 9 | def bubble_sort(arr): 10 | if not arr or len(arr) < 2: 11 | return arr 12 | 13 | length = len(arr) 14 | for i in range(length): 15 | for j in range(length-i-1): 16 | # 只大于不等于可以保证稳定性 17 | if arr[j] > arr[j+1]: 18 | arr[j], arr[j+1] = arr[j+1], arr[j] 19 | 20 | return arr 21 | 22 | 23 | if __name__ == '__main__': 24 | max_times = 500 25 | max_size = 100 26 | max_value = 100 27 | 28 | res = True 29 | 30 | for _ in range(max_times): 31 | arr1 = Comparator.gen_random_array(max_size, max_value) 32 | arr2 = Comparator.copy_arr(arr1) 33 | 34 | bubble_sort(arr1) 35 | sorted_arr2 = sorted(arr2) 36 | 37 | if not Comparator.is_equal(arr1, sorted_arr2): 38 | res = False 39 | break 40 | 41 | if not res: 42 | print('Failed ') 43 | else: 44 | print('Success') -------------------------------------------------------------------------------- /basic_algrithms/sort_algrithms/bucket_sort.py: -------------------------------------------------------------------------------- 1 | """ 2 | 桶排序 3 | """ 4 | 5 | 6 | import sys 7 | 8 | from basic_algrithms.sort_algrithms.benchmark import Comparator 9 | 10 | 11 | class BucketSort: 12 | @classmethod 13 | def bucket_sort(cls, arr): 14 | if not arr or len(arr) < 2: 15 | return arr 16 | # 系统最小值 17 | max_value = -sys.maxsize 18 | # max(arr) 找到数组最大值 19 | for i in arr: 20 | max_value = i if i > max_value else max_value 21 | # 由待排序的最大值确定桶的大小 22 | bucket = [0 for _ in range(max_value+1)] 23 | # 对值进行计数 24 | for i in arr: 25 | bucket[i] += 1 26 | 27 | start = 0 28 | for index, value in enumerate(bucket): 29 | if value == 0: 30 | continue 31 | else: 32 | # 重新组织arr 33 | while value > 0: 34 | arr[start] = index 35 | value -= 1 36 | start += 1 37 | 38 | return arr 39 | 40 | 41 | if __name__ == '__main__': 42 | max_times = 500 43 | max_size = 100 44 | max_value = 100 45 | 46 | res = True 47 | 48 | for _ in range(max_times): 49 | arr1 = Comparator.gen_random_array(max_size, max_value) 50 | arr2 = Comparator.copy_arr(arr1) 51 | sorted_arr1 = BucketSort.bucket_sort(arr1) 52 | sorted_arr2 = sorted(arr2) 53 | 54 | if not Comparator.is_equal(sorted_arr1, sorted_arr2): 55 | res = False 56 | break 57 | 58 | if not res: 59 | print('Failed ') 60 | else: 61 | print('Success') 62 | -------------------------------------------------------------------------------- /basic_algrithms/sort_algrithms/insert_sort.py: -------------------------------------------------------------------------------- 1 | """ 2 | 插入排序 3 | """ 4 | 5 | 6 | from basic_algrithms.sort_algrithms.benchmark import Comparator 7 | 8 | 9 | def insert_sort(arr): 10 | if not arr or len(arr) < 2: 11 | return arr 12 | 13 | for i in range(1, len(arr)): 14 | j = i - 1 15 | while j >= 0 and arr[j] > arr[j+1]: 16 | arr[j], arr[j+1] = arr[j+1], arr[j] 17 | j -= 1 18 | 19 | return arr 20 | 21 | 22 | if __name__ == '__main__': 23 | max_times = 500 24 | max_size = 100 25 | max_value = 100 26 | 27 | res = True 28 | 29 | for _ in range(max_times): 30 | arr1 = Comparator.gen_random_array(max_size, max_value) 31 | arr2 = Comparator.copy_arr(arr1) 32 | 33 | sorted_arr1 = insert_sort(arr1) 34 | sorted_arr2 = sorted(arr2) 35 | 36 | if not Comparator.is_equal(sorted_arr1, sorted_arr2): 37 | res = False 38 | break 39 | if not res: 40 | print('Failed ') 41 | else: 42 | print('Success') 43 | -------------------------------------------------------------------------------- /basic_algrithms/sort_algrithms/quick_sort.py: -------------------------------------------------------------------------------- 1 | """ 2 | 随机快速排序 3 | """ 4 | 5 | import random 6 | 7 | from basic_algrithms.sort_algrithms.benchmark import Comparator 8 | 9 | 10 | def partition(arr, l, r): 11 | less = l - 1 12 | more = r 13 | while l < more: 14 | if arr[l] < arr[r]: 15 | less += 1 16 | arr[less], arr[l] = arr[l], arr[less] 17 | l += 1 18 | elif arr[l] > arr[r]: 19 | more -= 1 20 | arr[l], arr[more] = arr[more], arr[l] 21 | else: 22 | l += 1 23 | 24 | arr[more], arr[r] = arr[r], arr[more] 25 | return less, more 26 | 27 | 28 | def random_quick_sort_detail(arr, l, r): 29 | if l < r: 30 | random_index = l + int(random.random() * (r - l + 1)) 31 | arr[random_index], arr[r] = arr[r], arr[random_index] 32 | pos = partition(arr, l, r) 33 | random_quick_sort_detail(arr, l, pos[0]) 34 | random_quick_sort_detail(arr, pos[1]+1, r) 35 | 36 | 37 | def random_quick_sort(arr): 38 | if not arr or len(arr) < 2: 39 | return arr 40 | 41 | random_quick_sort_detail(arr, 0, len(arr)-1) 42 | return arr 43 | 44 | 45 | if __name__ == '__main__': 46 | max_times = 500 47 | max_size = 100 48 | max_value = 100 49 | 50 | res = True 51 | 52 | for _ in range(max_times): 53 | arr1 = Comparator.gen_random_array(max_size, max_value) 54 | arr2 = Comparator.copy_arr(arr1) 55 | 56 | sorted_arr1 = random_quick_sort(arr1) 57 | sorted_arr2 = sorted(arr2) 58 | 59 | if not Comparator.is_equal(sorted_arr1, sorted_arr2): 60 | res = False 61 | break 62 | 63 | if not res: 64 | print('Failed ') 65 | else: 66 | print('Success') 67 | -------------------------------------------------------------------------------- /basic_algrithms/sort_algrithms/radix_sort.py: -------------------------------------------------------------------------------- 1 | """ 2 | 基数排序 3 | """ 4 | 5 | 6 | class RadisSort: 7 | @classmethod 8 | def radis_sort(cls, arr): 9 | if not arr or len(arr) < 2: 10 | return arr 11 | return cls.sort_detail(arr) 12 | 13 | @classmethod 14 | def sort_detail(cls, arr): 15 | container = arr 16 | max_digit = 0 17 | for i in arr: 18 | max_digit = max([max_digit, cls.get_digit(i)]) 19 | for i in range(max_digit): 20 | bucket = [[] for _ in range(10)] 21 | for j in container: 22 | digit = cls.get_digit_num(j, i) 23 | bucket[digit].append(j) 24 | container = list() 25 | for j in bucket: 26 | container.extend(j) 27 | return container 28 | 29 | @classmethod 30 | def get_digit(cls, num): 31 | i = 0 32 | while num != 0: 33 | num = int(num/10) 34 | i += 1 35 | return i 36 | 37 | @classmethod 38 | def get_digit_num(cls, num, digit): 39 | return int((num/10**digit) % 10) 40 | 41 | 42 | if __name__ == '__main__': 43 | print(RadisSort.radis_sort([123, 1, 533, 22, 97, 6])) -------------------------------------------------------------------------------- /basic_algrithms/sort_algrithms/select_sort.py: -------------------------------------------------------------------------------- 1 | """ 2 | 选择排序 3 | """ 4 | 5 | from basic_algrithms.sort_algrithms.benchmark import Comparator 6 | 7 | 8 | def select_sort(arr): 9 | if not arr or len(arr) < 2: 10 | return arr 11 | 12 | for i in range(len(arr)): 13 | min_index = i 14 | for j in range(i, len(arr)): 15 | if arr[min_index] > arr[j]: 16 | min_index = j 17 | arr[min_index], arr[i] = arr[i], arr[min_index] 18 | 19 | return arr 20 | 21 | 22 | if __name__ == '__main__': 23 | max_times = 500 24 | max_size = 100 25 | max_value = 100 26 | 27 | res = True 28 | 29 | for _ in range(max_times): 30 | arr1 = Comparator.gen_random_array(max_size, max_value) 31 | arr2 = Comparator.copy_arr(arr1) 32 | 33 | sorted_arr1 = select_sort(arr1) 34 | sorted_arr2 = sorted(arr2) 35 | 36 | if not Comparator.is_equal(sorted_arr1, sorted_arr2): 37 | res = False 38 | break 39 | 40 | if not res: 41 | print('Failed ') 42 | else: 43 | print('Success') -------------------------------------------------------------------------------- /basic_algrithms/sort_algrithms/shellsort.py: -------------------------------------------------------------------------------- 1 | """ 2 | shell排序 3 | """ 4 | 5 | 6 | from basic_algrithms.sort_algrithms.benchmark import Comparator 7 | 8 | 9 | class ShellSort: 10 | @classmethod 11 | def shell_sort(cls, arr): 12 | if len(arr) < 1: 13 | return arr 14 | 15 | arr_len = len(arr) 16 | gap = arr_len // 2 17 | while gap > 0: 18 | for i in range(gap, arr_len): 19 | temp = arr[i] 20 | while i >= gap and arr[i-gap] > temp: 21 | arr[i] = arr[i-gap] 22 | i -= gap 23 | arr[i] = temp 24 | gap = gap//2 25 | return arr 26 | 27 | 28 | if __name__ == '__main__': 29 | max_times = 1000 30 | max_size = 100 31 | max_value = 100 32 | 33 | res = True 34 | 35 | for _ in range(max_times): 36 | arr1 = Comparator.gen_random_array(max_size, max_value) 37 | arr2 = Comparator.copy_arr(arr1) 38 | 39 | sorted_arr1 = ShellSort.shell_sort(arr1) 40 | sorted_arr2 = sorted(arr2) 41 | if not Comparator.is_equal(sorted_arr1, sorted_arr2): 42 | res = False 43 | break 44 | 45 | if not res: 46 | print('Failed ') 47 | else: 48 | print('Success') -------------------------------------------------------------------------------- /basic_algrithms/sort_algrithms/top.py: -------------------------------------------------------------------------------- 1 | # 拓扑排序 2 | def indegree0(v, e): 3 | if not v: 4 | return None 5 | tmp = v[:] 6 | # 找出入度为0的节点 7 | for i in e: 8 | if i[1] in tmp: 9 | tmp.remove(i[1]) 10 | 11 | if not tmp: 12 | return -1 13 | 14 | # 在关系图中标记出入度为0的节点 15 | for t in tmp: 16 | for i in range(len(e)): 17 | if t in e[i]: 18 | e[i] = 'useless' 19 | 20 | if v: 21 | for t in tmp: 22 | v.remove(t) 23 | return tmp 24 | 25 | 26 | def top_sort(v, e): 27 | result = [] 28 | while True: 29 | nodes = indegree0(v, e) 30 | if nodes == -1: 31 | print('there\'s a circle.') 32 | return 33 | elif not nodes: 34 | break 35 | else: 36 | result.extend(nodes) 37 | return result 38 | 39 | 40 | if __name__ == '__main__': 41 | v = ['a', 'b', 'c', 'd', 'e'] 42 | e = [('a', 'b'), ('a', 'd'), ('b', 'c'), ('d', 'c'), ('d', 'e'), ('e', 'c')] 43 | res = top_sort(v, e) 44 | print(res) 45 | 46 | -------------------------------------------------------------------------------- /basic_algrithms/str_algrithms/manacher.py: -------------------------------------------------------------------------------- 1 | """ 2 | Manacher算法 3 | """ 4 | 5 | 6 | import sys 7 | 8 | 9 | class Manacher: 10 | @classmethod 11 | def get_max_length(cls, str1): 12 | if not str1: 13 | return 0 14 | 15 | my_str = cls.get_manacher_str(str1) 16 | center = -1 17 | right = -1 18 | parr = [0 for _ in range(len(my_str))] 19 | max_value = -sys.maxsize 20 | for i in range(len(my_str)): 21 | if i > right: 22 | parr[i] = 1 23 | else: 24 | parr[i] = min([parr[2*center-i], right-i]) 25 | while i - parr[i] > -1 and i + parr[i] < len(my_str): 26 | if my_str[parr[i]+i] == my_str[i-parr[i]]: 27 | parr[i] += 1 28 | else: 29 | break 30 | 31 | if i + parr[i] > right: 32 | right = i + parr[i] 33 | center = i 34 | max_value = max([max_value, parr[i]]) 35 | 36 | return max_value - 1 37 | 38 | @classmethod 39 | def get_manacher_str(cls, str1): 40 | new_str = '#' 41 | for s in str1: 42 | new_str += '{}#'.format(s) 43 | 44 | return new_str 45 | 46 | 47 | if __name__ == '__main__': 48 | str1 = "abc1234321ab" 49 | print(Manacher.get_max_length(str1)) -------------------------------------------------------------------------------- /bigdata/q1.md: -------------------------------------------------------------------------------- 1 | ## 布隆过滤器 2 | 3 | ### 常见问题 4 | 网页黑名单系统、垃圾邮件过滤系统、网址去重问题,如果能容忍一定的失误率,且内存空间限制 5 | 比较严格,那么适用布隆过滤器。 6 | 7 | ### 失误率和精度 8 | 布隆过滤器的精度主要由hash函数个数和内存大小决定。 9 | 10 | ### 如何防止误报 11 | 建立白名单 12 | 13 | ### 主要过程及实现细节 14 | 过程:首先选择适当数量的hash函数,然后将待判断的str依次带入这k个hash函数中,分别求得它 15 | 们的hash value,再将hash vlaue对所限制的m大小的空间取余,这一步的目的是让hash value 16 | 都落到m的范围内,最后将取余的结果对应位图m的位置置为1. 17 | 如果一个str所有位置都是1,那么它就很可能早已存在了,否则就一定是新的数据。 18 | 19 | 实现代码:参考[该项目](https://github.com/kongtianyi/BloomFilterRedis/blob/master/BloomFilterRedis/BloomFilterRedis.py) 20 | 21 | -------------------------------------------------------------------------------- /bigdata/q2.md: -------------------------------------------------------------------------------- 1 | ## 大数据找最值问题 2 | 3 | ### 题目 4 | 有一个包含20亿个全是32位整数的大文件,在其中找到出现次数最多的数。内存限制2GB. 5 | 6 | ### 思路 7 | 一般统计词频的方法是使用hashmap,但是由于本题内存限制,可能无法将所有的数同时放 8 | 到内存中,由于hash函数具有确定性,所以我们可以遍历集合,将所有的数根据hash运算的 9 | 结果放到16个小文件中,再把每个小文件中的数据进行统计,把每个小文件的第一名拿来比较 10 | 即可得到最大的。 11 | 12 | ### 总结 13 | 将一个大的集合通过hash函数分配到多台机器上,或者多个文件中,是处理大数据题目最常用 14 | 的技巧之一。具体分配到多少台机器或者文件,是根据题目确定的。 15 | 16 | 17 | -------------------------------------------------------------------------------- /bigdata/q3.md: -------------------------------------------------------------------------------- 1 | ## 40亿非负整数中找到没有出现过的数 2 | 3 | ### 题目 4 | 1.32位无符号整数范围是0~4294967295,现在有一个包含40亿个无符号整数的文件,必定包含在整个范围 5 | 内不存在的数,可以使用的内存是1GB,怎么找到所有没有出现过的数? 6 | 7 | 2.内存限制为10MB,但是只用找到一个未出现的数即可。 8 | 9 | ### 思路 10 | 1.使用位图来辅助查找。遍历这40亿个数,如果某个数出现过,就把位图相应的index置为1,最终index为 11 | 0的位置的index则是没出现的。 12 | 13 | 2. 1)根据10MB的内存限制,确定统计区间的大小,将所有返回分成N(64)个区间,每个区间都是连续的,2)遍 14 | 历每个区间,只要某个区间的元素个数不足67108864(500MB/64)个,那么它必然缺少一些元素 3)对该区间上的 15 | 数按1的方法做bitmap映射,再遍历bitmap,即可找出没出现的数。 -------------------------------------------------------------------------------- /bigdata/q4.md: -------------------------------------------------------------------------------- 1 | ## 找到100亿个URL中重复的URL以及搜索词汇的 topK 问题 2 | 3 | ### 题目 4 | 1.有一个包含100亿个URL的大文件,假设每个URL占用64B,请找出其中所有重复的URL。 5 | 6 | 2.某搜索公司一天的用户搜索词汇是海量的(百亿数据量),请设计一种求出每天最热top100词汇的可行办法。 7 | 8 | ### 思路 9 | 1.很多**大数据问题都离不开分流,要么是哈希函数把大文件的内容分配给不同的机器,要么是哈希函数把大文件 10 | 拆成小文件,然后处理每一个小的集合**。这里可以先将URL通过hash运算分配到100台机器或者100个文件中,再对 11 | 每个小文件进行hash表遍历,找出重复的URL。 12 | 13 | 2.开始仍然按照上述方式分流,然后使用小根堆选出每个文件的TOP100,再将同一台机器的不同文件的TOP100继续使用 14 | 小根堆来选出合并后的TOP100,至于是两两合并还是将所有文件的TOP100拿来合并,那么就要根据给定的内存限制来决 15 | 定了。再把不同机器做合并即可。不同文件或者不同机器的TOPK也可以使用外排序来做。 -------------------------------------------------------------------------------- /bigdata/q5.md: -------------------------------------------------------------------------------- 1 | ## 40亿个非负整数中找到出现两次的数和所有数的中位数 2 | 3 | ### 题目 4 | 1.32位无符号整数的范围是0~4294967295,现在有40亿个无符号整数,可以使用最多1GB的内存,找出所有 5 | 出现了两次的数。 6 | 7 | 2.可以使用最多10MB的内存,怎么找到这40亿个整数中的中位数? 8 | 9 | ### 思路 10 | 1.可以准备1个大小为4294967295*2的bitmap,如果某个数出现过一次,那么就把bitmap[i*2+1]和bitmap[i*2] 11 | 设置为01,第二次遇到,设置为10,第三次遇到设置为11,如果为11之后遇到就不做更改。之后再遍历bitmap,如果 12 | bitmap[i*2+1]和bitmap[i*2]为10,那么就是出现了两次的数。 13 | 14 | 2.先分区间,区间分的规则是看最多能用多少内存,这里能用10MB,一个整数占4个字节,所以2M个整数占8M,所以一共 15 | 可以分成 4294967295/2M 个区间,然后遍历这40亿个数,看num落在哪个区间上(num/2M),将对应区间的num总数+1, 16 | 这样遍历一遍就可以得到40亿个数在各个区间的分布情况,通过累加每个区间的元素个数,就可以得到40亿个数的中位数。 17 | 累加后可以得到中位数位于哪个区间,当前k-1个累加和小于20亿,后k+1个累加和大于20亿,那么第k个区间即是存在中位 18 | 数的区间。然后再将对该区间做词频统计,即可算出中位数。 -------------------------------------------------------------------------------- /bigdata/q6.md: -------------------------------------------------------------------------------- 1 | ## 一致性Hash算法的基本原理 2 | 3 | ### 常规负载均衡/缓存策略及问题 4 | 5 | #### 策略 6 | 通过对请求的id进行hash运算,再对机器总数取余,可实现数据的缓存。 7 | 8 | #### 问题 9 | 在添加和摘除节点的时候,所有数据都需要根据id重新计算hash值,再对机器总数取余,然后进行大规模 10 | 的数据迁移。 11 | 12 | 13 | ### 一致性Hash算法 14 | 15 | #### 实现原理 16 | 使用Hash环,先将数据的id通过hash函数转换成哈希值,再把这些数字头尾相连,想象成一个闭合的环形, 17 | 那么一个数据id在计算出哈希值之后认为对应到环上的一个位置,通过对数据的id进行hash计算,再映射 18 | 到环中,然后顺时针寻找离这个位置最近的机器,那么该机器就是该数据的归属。 19 | 20 | 这样实现可以避免增删节点造成的大规模数据迁移。 21 | 22 | #### 虚拟节点 23 | 上述实现有一个问题,就是当机器很少的时候,可能节点在整个环上分布不均匀,从而导致各个节点负载不均 24 | 衡。解决方法是添加虚拟节点,每台机器根据不同的hash函数计算出多个hash值,对多个节点都放置一个服务 25 | 节点,即虚拟节点,具体可以在机器ip或者主机名后添加端口或者编号来实现,分别计算它们的hash值,节点变 26 | 多了,根据hash函数的性质,平衡性会变好,然后用一张映射表维护虚拟节点和实际节点的关系。 27 | 28 | 具体算法可以参考chord算法、KAD算法等。 29 | 30 | -------------------------------------------------------------------------------- /binarytree/q10.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:一棵二叉树原本是搜索二叉树,但是其中有两个节点调换了位置,使得这棵二叉树不再是搜索二叉树, 3 | 请找到这两个节点并返回。已知二叉树所有节点的值都不一样,给定二叉树的头结点head,返回一个长度为2的 4 | 二叉树节点类型的数组errs。errs[0]表示一个错误节点,errs[1]表示一个错误节点。 5 | 进阶:如果在原问题中得到了这两个错误节点,我们当然可以通过交换两个节点值的方式让整棵二叉树重新成为 6 | 搜索二叉树,但现在要求不能这么做,而是在结构上完全交换两个节点的位置,请实现调整的函数。 7 | """ 8 | 9 | from binarytree.toolcls import Node 10 | 11 | 12 | # todo 完成进阶题目 13 | class BSTChecker: 14 | def __init__(self): 15 | self.nodes = list() 16 | self.errors = list() 17 | 18 | def get_wrong_nodes(self): 19 | for index, node in enumerate(self.nodes[:-1]): 20 | if node.value > self.nodes[index+1].value: 21 | self.errors.append((node, self.nodes[index+1])) 22 | if len(self.errors) == 1: 23 | return list(self.errors[0]) 24 | 25 | if len(self.errors) == 2: 26 | return [self.errors[0][0], self.errors[1][1]] 27 | 28 | def get_all_nodes(self, head): 29 | if head is None: 30 | return None 31 | if head.left is not None: 32 | self.get_all_nodes(head.left) 33 | self.nodes.append(head) 34 | if head.right is not None: 35 | self.get_all_nodes(head.right) 36 | 37 | 38 | if __name__ == '__main__': 39 | head14 = Node(5) 40 | head14.left = Node(3) 41 | head14.right = Node(7) 42 | head14.left.left = Node(2) 43 | head14.left.right = Node(8) 44 | head14.right.left = Node(6) 45 | head14.right.right = Node(4) 46 | head14.left.left.left = Node(1) 47 | 48 | bst = BSTChecker() 49 | bst.get_all_nodes(head14) 50 | err_nodes = bst.get_wrong_nodes() 51 | for err_node in err_nodes: 52 | print(err_node.value) 53 | 54 | 55 | -------------------------------------------------------------------------------- /binarytree/q11.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定彼此独立的两棵树头结点分别为t1和t2,判断t1树是否包含t2树全部的拓扑结构。 3 | """ 4 | 5 | from binarytree.toolcls import Node 6 | 7 | 8 | class BSTTop: 9 | @classmethod 10 | def is_bst_top(cls, t1, t2): 11 | return cls.visit_in_pre(t1, t2) or cls.is_bst_top(t1.left, t2) or cls.is_bst_top(t1.right, t2) 12 | 13 | @classmethod 14 | def visit_in_pre(cls, t1, t2): 15 | if t2 is None: 16 | return True 17 | 18 | if t1 is None or t1.value != t2.value: 19 | return False 20 | 21 | return cls.visit_in_pre(t1.left, t2.left) and cls.visit_in_pre(t1.right, t2.right) 22 | 23 | 24 | if __name__ == '__main__': 25 | t1 = Node(1) 26 | t1.left = Node(2) 27 | t1.right = Node(3) 28 | t1.left.left = Node(4) 29 | t1.left.right = Node(5) 30 | t1.right.left = Node(6) 31 | t1.right.right = Node(7) 32 | t1.left.left.left = Node(8) 33 | t1.left.left.right = Node(9) 34 | t1.left.right.left = Node(10) 35 | 36 | t2 = Node(2) 37 | t2.left = Node(4) 38 | t2.left.left = Node(8) 39 | t2.right = Node(5) 40 | 41 | print(BSTTop.is_bst_top(t1, t2)) -------------------------------------------------------------------------------- /binarytree/q12.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定彼此独立的两棵树头结点分别为t1和t2,判断t1中是否有与t2树拓扑结构完全相同的子树。 3 | 4 | 思路: 5 | 1)使用二叉树序列化的方法将二叉树序列化成一个字符串 6 | 2)使用KMP算法判断t1字符串是否包含t2字符串 7 | 8 | 由于目前对KMP算法还不是很了解,所以直接使用了in进行判断 9 | """ 10 | 11 | 12 | from binarytree.toolcls import Node 13 | 14 | 15 | class TopTreeTool: 16 | @classmethod 17 | def is_all_top(cls, head1, head2): 18 | str_t1 = cls.pre_order_visit(head1) 19 | str_t2 = cls.pre_order_visit(head2) 20 | if str_t2 in str_t1: 21 | return True 22 | 23 | @classmethod 24 | def pre_order_visit(cls, head): 25 | if head is None: 26 | return '#!' 27 | 28 | s = '' 29 | 30 | s += str(head.value) 31 | s += '!' 32 | 33 | s += cls.pre_order_visit(head.left) 34 | s += cls.pre_order_visit(head.right) 35 | return s 36 | 37 | 38 | if __name__ == '__main__': 39 | t1 = Node(1) 40 | t1.left = Node(2) 41 | t1.right = Node(3) 42 | t1.left.left = Node(4) 43 | t1.left.right = Node(5) 44 | t1.right.left = Node(6) 45 | t1.right.right = Node(7) 46 | t1.left.left.right = Node(8) 47 | t1.left.right.left = Node(9) 48 | 49 | t2 = Node(2) 50 | t2.left = Node(4) 51 | t2.left.right = Node(8) 52 | t2.right = Node(5) 53 | t2.right.left = Node(9) 54 | 55 | print(TopTreeTool.is_all_top(t1, t2)) 56 | -------------------------------------------------------------------------------- /binarytree/q13.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:平衡二叉树的性质是:要么是一棵空树,要么任何一个节点的左右子树的高度差 3 | 的绝对值bu超过1。现在给定一棵二叉树的头结点head,请判断它是否是一棵平衡二叉树。 4 | """ 5 | 6 | from binarytree.toolcls import Node 7 | 8 | 9 | class BlanceTreeTool: 10 | res = True 11 | 12 | @classmethod 13 | def is_blanced_tree(cls, head, height): 14 | res = [1] 15 | cls.get_height(head, 0, res) 16 | return True if res[0] == 1 else False 17 | 18 | @classmethod 19 | def get_height(cls, head, height, res): 20 | if head is None: 21 | return height 22 | 23 | left = cls.get_height(head.left, height + 1, res) 24 | 25 | right = cls.get_height(head.right, height + 1, res) 26 | 27 | val = (left - right) if left > right else (right - left) 28 | 29 | if val > 1: 30 | res[0] = 0 31 | 32 | return max(left, right) 33 | 34 | 35 | if __name__ == '__main__': 36 | head = Node(1) 37 | head.left = Node(2) 38 | head.right = Node(3) 39 | head.left.left = Node(4) 40 | head.left.right = Node(5) 41 | head.right.left = Node(6) 42 | head.right.right = Node(7) 43 | 44 | print(BlanceTreeTool.is_blanced_tree(head, 0)) -------------------------------------------------------------------------------- /binarytree/q16.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定一个有序数组sortArr,已知其中没有重复值,用这个有序数组生成一棵平衡二叉搜索树,并且该搜索二叉树 3 | 中序遍历结果与sortArr一致。 4 | """ 5 | 6 | 7 | from binarytree.toolcls import Node 8 | from binarytree.q3 import PrintTree 9 | 10 | 11 | class ReconstructBalancedBST: 12 | @classmethod 13 | def reconstruct(cls, arr): 14 | if len(arr) == 0 or arr is None: 15 | return None 16 | 17 | return cls.reconstruct_detail(arr, 0, len(arr)-1) 18 | 19 | @classmethod 20 | def reconstruct_detail(cls, arr, start, end): 21 | if start > end: 22 | return None 23 | 24 | pos = (start + end)//2 25 | node = Node(arr[pos]) 26 | 27 | node.left = cls.reconstruct_detail(arr, start, pos-1) 28 | node.right = cls.reconstruct_detail(arr, pos+1, end) 29 | 30 | return node 31 | 32 | 33 | if __name__ == '__main__': 34 | arr = [1, 2, 3, 4, 5, 6, 7, 8, 9] 35 | PrintTree.print_tree(ReconstructBalancedBST.reconstruct(arr)) -------------------------------------------------------------------------------- /binarytree/q20.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:从二叉树的节点A出发,可以向上或者向下走,但是沿途的节点只能经过一次,当到达节点B时,路径上的节点 3 | 数叫做A到B的距离。现给定一棵二叉树的头结点head,求整个二叉树上节点间的最大距离。 4 | 5 | 要求:如果二叉树的节点数为N,时间复杂度要求为O(N) 6 | """ 7 | 8 | 9 | from binarytree.toolcls import Node 10 | 11 | 12 | class MaxDistance: 13 | @classmethod 14 | def find_max_distance(cls, head): 15 | record = [0] 16 | return cls.find_max_distance_detail(head, record) 17 | 18 | @classmethod 19 | def find_max_distance_detail(cls, head, record): 20 | if head is None: 21 | record[0] = 0 22 | return 0 23 | 24 | lmax = cls.find_max_distance_detail(head.left, record) 25 | max_from_left = record[0] 26 | 27 | rmax = cls.find_max_distance_detail(head.right, record) 28 | max_from_right = record[0] 29 | 30 | record[0] = max([max_from_left, max_from_right]) + 1 31 | 32 | cur_node_length = max_from_left + max_from_right + 1 33 | 34 | return max([lmax, rmax, cur_node_length]) 35 | 36 | 37 | if __name__ == '__main__': 38 | head1 = Node(1) 39 | head1.left = Node(2) 40 | head1.right = Node(3) 41 | head1.left.left = Node(4) 42 | head1.left.right = Node(5) 43 | head1.right.left = Node(6) 44 | head1.right.right = Node(7) 45 | head1.left.left.left = Node(8) 46 | head1.right.left.right = Node(9) 47 | print(MaxDistance.find_max_distance(head1)) 48 | 49 | head2 = Node(1) 50 | head2.left = Node(2) 51 | head2.right = Node(3) 52 | head2.right.left = Node(4) 53 | head2.right.right = Node(5) 54 | head2.right.left.left = Node(6) 55 | head2.right.right.right = Node(7) 56 | head2.right.left.left.left = Node(8) 57 | head2.right.right.right.right = Node(9) 58 | print(MaxDistance.find_max_distance(head2)) -------------------------------------------------------------------------------- /binarytree/q22.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:已知一棵二叉树所有的节点值不同,给定这棵二叉树正确的先序和中序数组,不要重建整棵树, 3 | 而是通过这两个数组直接生成正确的后序数组。 4 | """ 5 | 6 | from array import array 7 | 8 | 9 | class GenPosArr: 10 | @classmethod 11 | def gen_pos_arr(cls, pre, mid): 12 | if pre is None or mid is None: 13 | return 14 | 15 | d = dict() 16 | for index, value in enumerate(mid): 17 | d[value] = index 18 | 19 | l = [0 for _ in range(len(pre))] 20 | cls.gen_pos_arr_detail(pre, 0, len(pre)-1, mid, 0, len(mid)-1, l, len(l)-1, d) 21 | return l 22 | 23 | @classmethod 24 | def gen_pos_arr_detail(cls, pre, pre_start, pre_end, mid, mid_start, mid_end, l, l_pos, d): 25 | if pre_start > pre_end: 26 | return l_pos 27 | print(pre_start) 28 | l[l_pos] = pre[pre_start] 29 | l_pos -= 1 30 | index = d[pre[pre_start]] 31 | 32 | l_pos = cls.gen_pos_arr_detail(pre, pre_start+index-mid_start+1, pre_end, mid, index+1, mid_end, l, l_pos, d) 33 | return cls.gen_pos_arr_detail(pre, pre_start+1, pre_start+index-mid_start, mid, mid_start, index-1, l, l_pos, d) 34 | 35 | 36 | if __name__ == '__main__': 37 | pre_arr = [1, 2, 4, 5, 3, 6, 7] 38 | mid_arr = [4, 2, 5, 1, 6, 3, 7] 39 | pos_arr = GenPosArr.gen_pos_arr(pre_arr, mid_arr) 40 | print(pos_arr) 41 | -------------------------------------------------------------------------------- /binarytree/q24.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定一棵完全二叉树的头结点head,返回这棵树的节点个数。 3 | 4 | 要求:如果完全二叉树的节点数为N,请实现时间复杂度低于O(N)的解法。 5 | """ 6 | 7 | 8 | from binarytree.toolcls import Node 9 | 10 | 11 | class BCTNodeCounter: 12 | @classmethod 13 | def get_bct_node_num(cls, head): 14 | if head is None: 15 | return 0 16 | 17 | return cls.bs(head, 1, cls.get_most_left_level(head, 1)) 18 | 19 | @classmethod 20 | def bs(cls, head, l, h): 21 | if h == l: 22 | return 1 23 | if h == cls.get_most_left_level(head.right, l + 1): 24 | return cls.bs(head.right, l+1, h) + (1 << (h-l)) 25 | else: 26 | return cls.bs(head.left, l+1, h) + (1 << (h-l-1)) 27 | 28 | # 由于是完全二叉树,所以左边的层级可以代表整棵树的层级 29 | @classmethod 30 | def get_most_left_level(cls, head, start): 31 | while head is not None: 32 | start += 1 33 | head = head.left 34 | return start - 1 35 | 36 | 37 | if __name__ == '__main__': 38 | head = Node(1) 39 | head.left = Node(2) 40 | head.right = Node(3) 41 | head.left.left = Node(4) 42 | head.left.right = Node(5) 43 | head.right.left = Node(6) 44 | num = BCTNodeCounter.get_bct_node_num(head) 45 | print(num) -------------------------------------------------------------------------------- /binarytree/q3.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:二叉树可以使用常规的三种遍历结果来描述其结构,但是不够直观,尤其是当二叉树中有 3 | 重复值的时候,仅通过三种遍历的结果来构造二叉树的真实结构更是难上加难,有时则根本不可能。 4 | 给定一棵二叉树的头结点head,已知二叉树节点值的类型为32位整型,请实现一个打印二叉树的函数, 5 | 可以直观地展示树的形状,也便于画出真实的结构。 6 | 7 | 思路: 8 | 1)将二叉树逆时针旋转90度,根据其是左子树、右子树或者根节点,选择不同的标志进行组合 9 | 2)注意要考虑到大整数,对同一层的兄弟节点要对齐 10 | """ 11 | 12 | 13 | from binarytree.toolcls import Node 14 | 15 | 16 | class PrintTree: 17 | @classmethod 18 | def print_tree(cls, head): 19 | if head is None: 20 | return 21 | cls.print_in_order(head, 0, 'H', 17) 22 | 23 | @classmethod 24 | def print_in_order(cls, head, height, char, length): 25 | if head is None: 26 | return 27 | cls.print_in_order(head.right, height + 1, 'v', length) 28 | cls.print_cur(head, height, char, length) 29 | cls.print_in_order(head.left, height + 1, '^', length) 30 | 31 | @classmethod 32 | def print_cur(cls, head, height, char, length): 33 | print_str = char + str(head.value) + char 34 | left_str = int((length - len(print_str)) / 2) 35 | right_str = length - len(print_str) - left_str 36 | print(' ' * length * height + ' ' * left_str + char + str(head.value) + char + ' ' * right_str) 37 | 38 | 39 | if __name__ == '__main__': 40 | import sys 41 | 42 | head = Node(1) 43 | head.left = Node(-222222222) 44 | head.right = Node(3) 45 | head.left.left = Node(sys.maxsize) 46 | head.right.left = Node(55555555) 47 | head.right.right = Node(66) 48 | head.left.left.right = Node(777) 49 | PrintTree.print_tree(head) -------------------------------------------------------------------------------- /binarytree/q6.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定一棵二叉树的头结点和一个整数k,二叉树节点为整型,求累加和为sum的 3 | 最长路径长度,路径是指从某个节点往下,每次最多选择一个孩子节点或者不选所形成的节点链。 4 | 5 | 思路:借助"未排序数组中累加和为规定值的最长子数组长度问题"的思路来解决 6 | """ 7 | 8 | 9 | from binarytree.toolcls import Node 10 | 11 | 12 | class LongestTreePath: 13 | @classmethod 14 | def get_longest_tree_path(cls, head, k): 15 | if head is None: 16 | return 0 17 | sum_node_map = dict() 18 | sum_node_map.setdefault(0, 0) 19 | return cls.visit_by_first_order(head, 0, sum_node_map, k, 1, 0) 20 | 21 | @classmethod 22 | def visit_by_first_order(cls, head, sum, node_map, k, level, max_length): 23 | if head is None: 24 | return max_length 25 | sum += head.value 26 | if sum not in node_map: 27 | node_map[sum] = level 28 | if (sum - k) in node_map: 29 | sum_k_level = node_map[sum - k] 30 | if level - sum_k_level > max_length: 31 | max_length = level - sum_k_level 32 | max_length = cls.visit_by_first_order(head.left, sum, node_map, k, level + 1, max_length) 33 | max_length = cls.visit_by_first_order(head.right, sum, node_map, k, level + 1, max_length) 34 | 35 | return max_length 36 | 37 | 38 | if __name__ == '__main__': 39 | head = Node(-3) 40 | head.left = Node(3) 41 | head.right = Node(-9) 42 | head.left.left = Node(1) 43 | head.left.right = Node(0) 44 | head.left.right.left = Node(1) 45 | head.left.right.right = Node(6) 46 | head.right.left = Node(2) 47 | head.right.right = Node(1) 48 | 49 | print(LongestTreePath.get_longest_tree_path(head, 6)) 50 | print(LongestTreePath.get_longest_tree_path(head, -9)) 51 | -------------------------------------------------------------------------------- /binarytree/toolcls.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | def __init__(self, value): 3 | self.value = value 4 | self.left = None 5 | self.right = None -------------------------------------------------------------------------------- /binarytree/填充同一层的兄弟节点.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个二叉树 3 | struct TreeLinkNode { 4 | TreeLinkNode *left; 5 | TreeLinkNode *right; 6 | TreeLinkNode *next; 7 | } 8 | 填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。 9 | 初始状态下,所有 next 指针都被设置为 NULL。 10 | 11 | 要求: 12 | 1.额外空间复杂度为(1),递归的栈空间不算 13 | 2.给定的二叉树为满二叉树 14 | """ 15 | 16 | 17 | # Definition for binary tree with next pointer. 18 | # class TreeLinkNode: 19 | # def __init__(self, x): 20 | # self.val = x 21 | # self.left = None 22 | # self.right = None 23 | # self.next = None 24 | 25 | class Solution: 26 | def connect(self, root): 27 | if not root: 28 | return 29 | 30 | cur = root 31 | next_node = cur.left 32 | while next_node: 33 | cur.left.next = cur.right 34 | if cur.next: 35 | cur.right.next = cur.next.left 36 | cur = cur.next 37 | else: 38 | cur = next_node 39 | next_node = cur.left -------------------------------------------------------------------------------- /binarytree/构建平衡搜索二叉树.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题: 给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。本题中,一个高度平衡二叉树 3 | 是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。 4 | 5 | 思路: 6 | 在构建二叉树系列问题中,我们可以使用前序遍历,只要收集好构建二叉树节点关系的信息就可以了,一般左右节点都 7 | 直接调用递归获取即可 8 | """ 9 | 10 | 11 | class ListNode: 12 | def __init__(self, x): 13 | self.val = x 14 | self.next = None 15 | 16 | 17 | class TreeNode: 18 | def __init__(self, x): 19 | self.val = x 20 | self.left = None 21 | self.right = None 22 | 23 | 24 | class Solution: 25 | def sortedListToBST(self, head): 26 | """ 27 | :type head: ListNode 28 | :rtype: TreeNode 29 | """ 30 | if not head: 31 | return 32 | arr = list() 33 | while head: 34 | arr.append(head.val) 35 | head = head.next 36 | return self.process(arr, 0, len(arr) - 1) 37 | 38 | def process(self, nums, start, end): 39 | if start > end: 40 | return 41 | 42 | cur_index = (start + end) >> 1 43 | cur = TreeNode(nums[cur_index]) 44 | 45 | cur.left = self.process(nums, start, cur_index - 1) 46 | cur.right = self.process(nums, cur_index + 1, end) 47 | 48 | return cur -------------------------------------------------------------------------------- /bitoper/q1.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:如何不借助任何额外变量交换两个整数的值? 3 | """ 4 | 5 | 6 | class ValueSwap: 7 | @classmethod 8 | def swap(cls, a, b): 9 | a = a ^ b 10 | b = a ^ b 11 | a = a ^ b 12 | 13 | return a, b 14 | 15 | 16 | if __name__ == '__main__': 17 | a, b = 2, 3 18 | print(ValueSwap.swap(a, b)) -------------------------------------------------------------------------------- /bitoper/q2.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定两个32位整数a和b,返回a和b中较大的 3 | 4 | 要求: 5 | 不能用任何比较判断 6 | """ 7 | 8 | 9 | class BiggerGetter: 10 | @classmethod 11 | def flip(cls, n): 12 | return n ^ 1 13 | 14 | @classmethod 15 | def sign(cls, n): 16 | return cls.flip((n >> 31) & 1) 17 | 18 | @classmethod 19 | def get_bigger_num(cls, a, b): 20 | c = a - b 21 | d = c >> 31 & 1 22 | e = d ^ 1 23 | f = e ^ 1 24 | return e*a + f*b 25 | 26 | @classmethod 27 | def get_bigger_the_right_way(cls, a, b): 28 | c = a - b 29 | sa = cls.sign(a) 30 | sb = cls.sign(b) 31 | sc = cls.sign(c) 32 | diffsasb = sa ^ sb 33 | samesasb = cls.flip(diffsasb) 34 | return_a = diffsasb * sa + samesasb * sc 35 | return_b = cls.flip(return_a) 36 | return return_a * a + return_b * b 37 | 38 | 39 | if __name__ == '__main__': 40 | print(BiggerGetter.get_bigger_num(1, 2)) 41 | print(BiggerGetter.get_bigger_the_right_way(1, 2)) -------------------------------------------------------------------------------- /bitoper/q3.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述: 给定两个32位整数a和b,可正、可负、可0。不能使用算术运算符,实现a和b的加减乘除运算。 3 | 4 | 要求: 5 | 如果给定的a和b执行加减乘除的某些结果本来就会导致数据的溢出,那么你实现的结果不必对那些结果负责。 6 | """ 7 | 8 | 9 | class BitOperation: 10 | @classmethod 11 | def add(cls, x, y): 12 | value = x 13 | while y != 0 and pow(2, 31) >= y: 14 | # import time 15 | # time.sleep(0.5) 16 | # print(y) 17 | value = (x ^ y) 18 | y = ((x & y) << 1) 19 | x = value 20 | 21 | return value 22 | 23 | @classmethod 24 | def get_opposite(cls, args): 25 | return cls.add(~args, 1) 26 | 27 | @classmethod 28 | def minus(cls, x, y): 29 | new_y = cls.get_opposite(y) 30 | return cls.add(x, new_y) 31 | 32 | @classmethod 33 | def multi(cls, x, y): 34 | res = 0 35 | while y != 0: 36 | tmp = (y & 1) 37 | if tmp != 0: 38 | res = cls.add(x, res) 39 | y = (y >> 1) 40 | x = (x << 1) 41 | 42 | return res 43 | 44 | 45 | if __name__ == '__main__': 46 | a = 5 47 | b = 4 48 | print(BitOperation.add(a, b)) 49 | print(BitOperation.minus(a, -b)) 50 | print(BitOperation.multi(a, b)) -------------------------------------------------------------------------------- /bitoper/q4.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定一个32位整数n,可为0、正或者负,返回该整数二进制表达式中1的个数。 3 | """ 4 | 5 | 6 | class BitCounter: 7 | @classmethod 8 | def count(cls, n): 9 | if n < 0: 10 | return 32 11 | res = 0 12 | while n != 0: 13 | res += (n & 1) 14 | n = n >> 1 15 | return res 16 | 17 | @classmethod 18 | def count2(cls, n): 19 | res = 0 20 | while n != 0: 21 | n = (n & (n-1)) 22 | res += 1 23 | return res 24 | 25 | @classmethod 26 | def count3(cls, n): 27 | res = 0 28 | while n != 0: 29 | n -= (n & (~n+1)) 30 | res += 1 31 | return res 32 | 33 | 34 | if __name__ == '__main__': 35 | num = 65535 36 | print(BitCounter.count(num)) 37 | print(BitCounter.count2(num)) 38 | print(BitCounter.count3(num)) -------------------------------------------------------------------------------- /bitoper/q5.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定一个整型数组arr,其中只有一个数出现了奇数次,其它的数都出现了偶数次, 3 | 打印这个数。 4 | 5 | 进阶:有两个数出现了奇数次,其他的数都出现了偶数次,打印这两个数。 6 | 7 | 要求: 8 | 时间复杂度为O(N),额外空间复杂度为O(1). 9 | """ 10 | 11 | 12 | class OddFinder: 13 | @classmethod 14 | def find_one_odd(cls, arr): 15 | flag = 0 16 | for i in arr: 17 | flag ^= i 18 | 19 | return flag 20 | 21 | @classmethod 22 | def find_two_odd(cls, arr): 23 | pass 24 | 25 | 26 | if __name__ == '__main__': 27 | a = [1, 1, 2, 2, 5, 4, 4, 7, 7, 7, 7] 28 | b = [1, 1, 2, 2, 5, 4, 4, 7, 7, 7, 7, 3, 3, 3] 29 | print(OddFinder.find_one_odd(a)) -------------------------------------------------------------------------------- /bitoper/q6.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定一个整型数组arr和一个大于1的整数k。已知arr中只有1个数出现了1次, 3 | 其它的数都出现了k次,请返回只出现了1次的数。 4 | 5 | 要求: 6 | 时间复杂度为O(N),额外空间复杂度为O(1). 7 | 8 | 思路: 9 | k个k进制的数,无进位相加。比如2个相同的二进制的数,无进位相加,结果一定为0 10 | """ 11 | 12 | 13 | class KGetter: 14 | @classmethod 15 | def num2ksys(cls, num, k): 16 | res = [0 for _ in range(32)] 17 | i = 0 18 | while num != 0: 19 | res[i] = num % k 20 | num = int(num / k) 21 | i += 1 22 | 23 | return res 24 | 25 | @classmethod 26 | def ksys2num(cls, arr, k): 27 | length = len(arr) 28 | res = 0 29 | for i in range(length): 30 | res += arr[i] * pow(k, i) 31 | 32 | return res 33 | 34 | @classmethod 35 | def process(cls, arr, k): 36 | e0 = [0 for _ in range(32)] 37 | for i in arr: 38 | cls.execute(e0, i, k) 39 | res = cls.ksys2num(e0, k) 40 | return res 41 | 42 | @classmethod 43 | def execute(cls, e0, value, k): 44 | cur_ksys = cls.num2ksys(value, k) 45 | for i in range(len(e0)): 46 | e0[i] = (e0[i] + cur_ksys[i]) % k 47 | 48 | 49 | if __name__ == '__main__': 50 | arr = [1, 1, 1, 2, 6, 6, 2, 2, 10, 10, 10, 12, 12, 12, 6, 9] 51 | print(KGetter.process(arr, 3)) -------------------------------------------------------------------------------- /bitoper/套路总结.md: -------------------------------------------------------------------------------- 1 | 1.奇数&1=1, 偶数&1=0 2 | 3 | 2.异或运算是无进位相加 -------------------------------------------------------------------------------- /dp/q1.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定整数N,返回斐波那契数列的第N项 3 | 4 | 补充题目:给定整数N,代表台阶数,一次可以跨2个或者1个台阶,返回有多少种走法。 5 | 6 | 补充题目2:假设农场中成熟的母牛每年会生一头小母牛,而且永远不会死。第一年农场有1只成熟的母牛,从第二年开始, 7 | 母牛开始生小母牛。每只小母牛3年之后成熟又可以生小母牛。给定整数N,求出N年后牛的数量。 8 | 9 | 要求:对以上所有问题,实现时间复杂度为O(logN)的解法 10 | """ 11 | 12 | # 由于数学能力和理解能力有限,这里给出O(N)的解法 13 | 14 | class FBNZ: 15 | @classmethod 16 | def classic_question(cls, n): 17 | if n < 1: 18 | return 0 19 | 20 | if n == 1 or n == 2: 21 | return 1 22 | 23 | cur = 1 24 | pre = 1 25 | pos = 3 26 | 27 | while pos <= n: 28 | temp = cur 29 | cur = pre + cur 30 | pre = temp 31 | pos += 1 32 | 33 | return cur 34 | 35 | @classmethod 36 | def jump_tj(cls, n): 37 | if n < 1: 38 | return 0 39 | if n == 1 or n == 2: 40 | return n 41 | cur = 2 42 | pre = 1 43 | pos = 3 44 | 45 | while pos <= n: 46 | temp = cur 47 | cur = pre + cur 48 | pre = temp 49 | pos += 1 50 | 51 | return cur 52 | 53 | @classmethod 54 | def cow_count(cls, n): 55 | if n < 1: 56 | return 0 57 | 58 | if n == 1 or n == 2 or n == 3: 59 | return n 60 | 61 | cur = 3 62 | pre = 2 63 | prepare = 1 64 | pos = 4 65 | 66 | while pos <= n: 67 | temp1 = cur 68 | temp2 = pre 69 | cur = prepare + cur 70 | pre = temp1 71 | prepare = temp2 72 | pos += 1 73 | return cur 74 | 75 | 76 | if __name__ == '__main__': 77 | assert FBNZ.classic_question(20) == 6765 78 | assert FBNZ.jump_tj(20) == 10946 79 | assert FBNZ.cow_count(20) == 1873 -------------------------------------------------------------------------------- /dp/q10.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定三个字符串str1、str2和aim,如果aim包含且仅包含来自str1和str2的所有字符, 3 | 而且在aim中属于str1的字符之间保持原来在str1中的顺序,属于str2的字符之间保持原来在str2 4 | 中的顺序,那么称aim是str1和str2的交错组成。实现一个函数,判断aim是否是str1和str2的交 5 | 错组成。 6 | 7 | 举例AB 8 | str1="",str2="12",那么"AB12"、"A1B2"、"A12B"、"1A2B"和"1AB2"等都是str1和str2 9 | 的交错组成。 10 | """ 11 | 12 | 13 | class CrossStr: 14 | @classmethod 15 | def is_cross_str(cls, str1, str2, aim): 16 | if not str1 or not str2 or not aim: 17 | return False 18 | 19 | str_len1 = len(str1) 20 | str_len2 = len(str2) 21 | aim_len = len(aim) 22 | 23 | if str_len1 + str_len2 != aim_len: 24 | return False 25 | 26 | dp = [[False for _ in range(str_len2+1)] for _ in range(str_len1+1)] 27 | dp[0][0] = True 28 | 29 | for i in range(1, str_len1+1): 30 | if aim[i-1] == str1[i-1]: 31 | dp[i][0] = True 32 | 33 | for j in range(1, str_len2+1): 34 | if aim[j-1] == str2[j-1]: 35 | dp[0][j] = True 36 | 37 | for i in range(1, str_len1+1): 38 | for j in range(1, str_len2+1): 39 | if dp[i-1][j] and aim[i+j-1] == str1[i-1]: 40 | dp[i][j] = True 41 | elif dp[i][j-1] and aim[i+j-1] == str2[j-1]: 42 | dp[i][j] = True 43 | else: 44 | dp[i][j] = False 45 | 46 | return dp[str_len1][str_len2] 47 | 48 | 49 | if __name__ == '__main__': 50 | str1 = "1234" 51 | str2 = "abcd" 52 | aim = "1a23bcd4" 53 | print(CrossStr.is_cross_str(str1, str2, aim)) -------------------------------------------------------------------------------- /dp/q11.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个二维数组map,含义是一张地图,例如,如下矩阵: 3 | -2 -3 3 4 | -5 -10 1 5 | 0 30 -5 6 | 7 | 游戏规则如下: 8 | (1)骑士从左上角出发,每次只能向右或者向下走,最后到达右下角见到公主。 9 | (2)地图中每个位置的值代表骑士要遭遇的事情。如果是负数,说明此处有怪兽, 10 | 要让骑士掉血。如果是非负数,则代表此处有血瓶,能让骑士回血。 11 | (3)骑士从左上角到右下角的过程中,走到任何一个位置,血量都不能少于1. 12 | 13 | 为了保证骑士能见到公主,初始血量至少是多少?根据map,返回初始血量。 14 | """ 15 | 16 | 17 | class DungenonGame: 18 | @classmethod 19 | def get_min_hp(cls, m): 20 | if not m: 21 | return 1 22 | 23 | len1 = len(m) 24 | len2 = len(m[0]) 25 | dp = [[0 for _ in range(len2)] for _ in range(len1)] 26 | 27 | dp[len1-1][len2-1] = 1 if m[len1-1][len2-1] >= 0 else -m[len1-1][len2-1] + 1 28 | for rows in range(len1-2, -1, -1): 29 | dp[rows][len2-1] = max([dp[rows+1][len2-1]-m[rows][len2-1], 1]) 30 | rows += 1 31 | 32 | for cols in range(len2-2, -1, -1): 33 | dp[len1-1][cols] = max([dp[len1-1][cols+1]-m[len1-1][cols], 1]) 34 | cols += 1 35 | for rows in range(len1-2, -1, -1): 36 | for cols in range(len2 - 2, -1, -1): 37 | down = max(dp[rows+1][cols]-m[rows][cols], 1) 38 | right = max(dp[rows][cols+1]-m[rows][cols], 1) 39 | dp[rows][cols] = min(down, right) 40 | 41 | return dp[0][0] 42 | 43 | 44 | if __name__ == '__main__': 45 | my_map = [[-2, -3, 3], [-5, -10, 1], [10, 30, -5]] 46 | print(DungenonGame.get_min_hp(my_map)) -------------------------------------------------------------------------------- /dp/q12.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个字符串str,str全部由数字字符组成,如果str中某一个或某相邻两个字符组成 3 | 的子串值在1~26之间,则这个子串可以转换成一个字母,规定1转换为A,2转换为B,3转换 4 | 为C...26转换为Z,写一个函数,求str有多少种不同的转换结果,并返回种数。 5 | 6 | 举例: 7 | str="1111" 8 | 能转换出来的结果有"AAAA"、"LAA"、"ALA"、"AAL"和"LL",返回5. 9 | str="01" 10 | "0"没有对应的字母,而"01"根据规定不可转换,返回0. 11 | str="10" 12 | 能转换出的结果是"J",返回1: 13 | """ 14 | 15 | 16 | class TotalNumForChar: 17 | @classmethod 18 | def find_total_num_by_recurise(cls, str1): 19 | if not str1: 20 | return 0 21 | 22 | return cls.process(str1, 0) 23 | 24 | @classmethod 25 | def process(cls, str1, index): 26 | if index == len(str1): 27 | return 1 28 | if str1[index] == '0': 29 | return 0 30 | 31 | res = cls.process(str1, index+1) 32 | if index + 1 < len(str1) and int(str1[index])*10 + int(str1[index+1]) < 27: 33 | res += cls.process(str1, index+2) 34 | 35 | return res 36 | 37 | 38 | if __name__ == '__main__': 39 | my_str = "781231783161018231" 40 | print(TotalNumForChar.find_total_num_by_recurise(my_str)) -------------------------------------------------------------------------------- /dp/q15.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定一个数组arr,arr[i]=k代表可以从位置i向右跳1~k个距离。比如,arr[2]==3, 3 | 代表可以从位置2跳到位置3、位置4或者位置5.如果从位置0出发,返回最少跳几次能跳到arr最后的 4 | 位置上。 5 | 6 | 举例: 7 | arr=[3,2,3,1,1,4] 8 | arr[0]=3,选择跳到位置2;arr[2]=3,可以跳到最后的位置,所以返回2。 9 | 10 | 要求: 11 | 如果arr长度为N,要求实现时间复杂度为O(N)、额外空间复杂度为O(1)的方法。 12 | """ 13 | 14 | 15 | class JumpSteps: 16 | @classmethod 17 | def get_steps(cls, arr): 18 | if not arr: 19 | return 0 20 | 21 | jump = 0 22 | cur_pos = 0 23 | next_pos = 0 24 | 25 | for i in arr: 26 | if cur_pos < i: 27 | jump += 1 28 | cur_pos = next_pos 29 | 30 | next_pos = max([arr[i]+i, next_pos]) 31 | 32 | return jump 33 | 34 | 35 | if __name__ == '__main__': 36 | print(JumpSteps.get_steps([3, 2, 3, 1, 1, 4])) -------------------------------------------------------------------------------- /dp/q17.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:N皇后问题是指在N*N的棋盘上要摆N个皇后,要求任何两个皇后不同行、不同列,也不在 3 | 同一条斜线上,给定一个整数n,返回n皇后的摆法有多少种。 4 | 5 | 举例: 6 | n=1,返回1 7 | n=2或者3,2皇后和3皇后无论怎么摆都不行,返回0 8 | n=8,返回92 9 | """ 10 | 11 | 12 | class NQueenProblem: 13 | @classmethod 14 | def solve_by_recursive(cls, n): 15 | if n < 1: 16 | return 0 17 | record = [0 for _ in range(n)] 18 | return cls.recursive_detail(0, record, n) 19 | 20 | @classmethod 21 | def recursive_detail(cls, row, record, n): 22 | if row == n: 23 | return 1 24 | res = 0 25 | for col in range(n): 26 | if cls.is_valid(row, col, record): 27 | record[row] = col 28 | res += cls.recursive_detail(row+1, record, n) 29 | 30 | return res 31 | 32 | @classmethod 33 | def is_valid(cls, row, col, record): 34 | i = 0 35 | while i < row: 36 | if record[i] == col or abs(i-row) == abs(record[i]-col): 37 | return False 38 | i += 1 39 | 40 | return True 41 | 42 | 43 | if __name__ == '__main__': 44 | print(NQueenProblem.solve_by_recursive(8)) -------------------------------------------------------------------------------- /dp/q18.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给你一根长度为n的绳子,请把绳子剪成m段(m,n都是整数,m>1并且n>1),每段 3 | 绳子的长度记为k[0],k[1]...k[m]。请问k[0]*k[1]*...*k[m]可能的最大乘积是多 4 | 少?例如,当绳子长度为8时,我们把它剪成长度为2,3,3的三段,此时得到的最大乘积为18 5 | """ 6 | 7 | 8 | class Solution: 9 | def cut(self, n): 10 | if n <= 1: 11 | return 0 12 | if n == 2: 13 | return 1 14 | if n == 3: 15 | return 2 16 | 17 | arr = [0 for _ in range(n+1)] 18 | arr[1] = 1 19 | arr[2] = 2 20 | arr[3] = 3 21 | 22 | for i in range(4, n+1): 23 | max_val = 0 24 | for j in range(1, i): 25 | max_val = max([max_val, arr[i-j]*arr[j]]) 26 | arr[i] = max_val 27 | 28 | return arr[n] 29 | 30 | def recursive(self, n): 31 | if n <= 1: 32 | return 0 33 | if n == 2: 34 | return 1 35 | 36 | if n == 3: 37 | return 2 38 | 39 | return self.process(n) 40 | 41 | def process(self, n): 42 | if n < 4: 43 | return n 44 | max_val = 0 45 | for i in range(1, n): 46 | max_val = max([self.process(n-i) * self.process(i), max_val]) 47 | 48 | return max_val 49 | 50 | 51 | if __name__ == '__main__': 52 | print(Solution().cut(8)) 53 | print(Solution().recursive(8)) -------------------------------------------------------------------------------- /dp/不等式数列.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述: 度度熊最近对全排列特别感兴趣,对于1到n的一个排列,度度熊发现可以在中间根据大小关 3 | 系插入合适的大于和小于符号(即 '>' 和 '<' )使其成为一个合法的不等式数列。但是现在度度熊 4 | 手中只有k个小于符号即('<'')和n-k-1个大于符号(即'>'),度度熊想知道对于1至n任意的排列中 5 | 有多少个排列可以使用这些符号使其为合法的不等式数列。 6 | 7 | 输入描述: 8 | 输入包括一行,包含两个整数n和k(k < n ≤ 1000) 9 | 输出描述: 10 | 输出满足条件的排列数,答案对2017取模。 11 | 12 | 示例1 13 | 输入 14 | 5 2 15 | 输出 16 | 66 17 | """ 18 | import sys 19 | 20 | 21 | def solve(n, k): 22 | dp = [[0] * (k + 1) for i in range(n + 1)] 23 | for i in range(1, n - k + 1): 24 | dp[i][0] = 1 25 | for i in range(2, n + 1): 26 | for j in range(1, k + 1): 27 | dp[i][j] = (dp[i - 1][j - 1] * (i - j) + dp[i - 1][j] * (j + 1)) % 2017 28 | print(dp[n][k]) 29 | 30 | 31 | if __name__ == '__main__': 32 | n, k = list(map(int, sys.stdin.readline().strip().split())) 33 | -------------------------------------------------------------------------------- /dp/牛牛与世界杯门票.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:今年的世界杯要开始啦,牛牛作为一个球迷,当然不会放过去开幕式现场的机会。 3 | 但是牛牛一个人去又觉得太过寂寞,便想叫上了他的n个小伙伴陪他一起去莫斯科(一共n+1人)。 4 | 当牛牛开始订开幕式的门票时,发现门票有m种套餐,每种套餐需要花费x元,包含y张门票, 5 | 每张门票也可以单独购买,此时这张门票的价格为k元。请问牛牛要怎样选择购买门票, 6 | 使得他花费的钱最少。(每种套餐可以购买次数没有限制)。 7 | 8 | 示例 9 | 输入: 10 | 2 2 5 11 | 6 2 12 | 13 3 13 | 14 | 输出: 15 | 11 16 | """ 17 | import sys 18 | 19 | 20 | class WorldCup: 21 | def get_all_cost(self): 22 | n, m, k = list((map(int, sys.stdin.readline().strip().split()))) 23 | n += 1 24 | dp = [0 for _ in range(n+1)] 25 | for i in range(1, n+1): 26 | dp[i] = i*k 27 | 28 | while m > 0: 29 | x, y = list((map(int, sys.stdin.readline().strip().split()))) 30 | for i in range(1, n+1): 31 | if i - y >= 0: 32 | dp[i] = min([dp[i], dp[i-y]+x]) 33 | else: 34 | dp[i] = min([dp[i], x]) 35 | m -= 1 36 | print(dp[n]) 37 | 38 | 39 | if __name__ == '__main__': 40 | word_cup = WorldCup() 41 | word_cup.get_all_cost() -------------------------------------------------------------------------------- /leetcode/162.寻找峰值.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述: 峰值元素是指其值大于左右相邻值的元素。给定一个输入数组 nums, 3 | 其中 nums[i] ≠ nums[i+1],找到峰值元素并返回其索引。数组可能包含多 4 | 个峰值,在这种情况下,返回任何一个峰值所在位置即可。你可以假设 nums[-1] = nums[n] = -∞。 5 | 6 | 示例: 7 | nums = [1,2,3,1] => 2 8 | 3 是峰值元素,函数应该返回其索引 2。 9 | 10 | 思路: 11 | 由于最左和最右是最低点,因此中间一定存在局部最大值.二分查找的标准是每次都 12 | 往比mid更大的那边寻找,因为那边总会降低到无穷小 13 | """ 14 | 15 | 16 | class Solution(object): 17 | def findPeakElement(self, nums): 18 | """ 19 | :type nums: List[int] 20 | :rtype: int 21 | """ 22 | if len(nums) <= 1: 23 | return 0 24 | 25 | return self.process(nums, 0, len(nums) - 1) 26 | 27 | def process(self, nums, start, end): 28 | if start == end: 29 | return start 30 | 31 | mid = (start + end) >> 1 32 | 33 | if mid == 0: 34 | if nums[0] > nums[1]: 35 | return 0 36 | else: 37 | return 1 38 | 39 | if nums[mid] > nums[mid + 1] and nums[mid] > nums[mid - 1]: 40 | return mid 41 | elif nums[mid] < nums[mid + 1]: 42 | start = mid + 1 43 | elif nums[mid] < nums[mid - 1]: 44 | end = mid - 1 45 | 46 | return self.process(nums, start, end) 47 | -------------------------------------------------------------------------------- /leetcode/274.H指数.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定一位研究者论文被引用次数的数组(被引用次数是非负整数)。 3 | 编写一个方法,计算出研究者的 h 指数。 4 | h 指数的定义: “一位有 h 指数的学者,代表他(她)的 N 篇论文中至多有 h 篇论文, 5 | 分别被引用了至少 h 次,其余的 N - h 篇论文每篇被引用次数不多于 h 次。” 6 | 7 | 输入: citations = [3,0,6,1,5] 8 | 输出: 3 9 | """ 10 | 11 | 12 | class Solution: 13 | def hIndex(self, citations): 14 | """ 15 | :type citations: List[int] 16 | :rtype: int 17 | """ 18 | if not citations: 19 | return 0 20 | 21 | if len(citations) == 1: 22 | if citations[0] > 0: 23 | return 1 24 | else: 25 | return 0 26 | 27 | citations.sort(reverse=True) 28 | index = 1 29 | while index <= len(citations): 30 | if index < len(citations): 31 | if citations[index] <= index <= citations[index - 1]: 32 | return index 33 | elif index == len(citations): 34 | if citations[index - 1] >= index: 35 | return index 36 | 37 | index += 1 38 | 39 | return 0 -------------------------------------------------------------------------------- /leetcode/3.最长无重复子串.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def lengthOfLongestSubstring(self, s): 3 | """ 4 | :type s: str 5 | :rtype: int 6 | """ 7 | if len(s) < 2: 8 | return len(s) 9 | 10 | cur = 1 11 | left = -1 12 | max_length = 1 13 | while cur < len(s): 14 | rindex = cur - 1 15 | while rindex >= 0: 16 | if s[rindex] == s[cur]: 17 | break 18 | rindex -= 1 19 | 20 | left = max([left, rindex]) 21 | max_length = max([max_length, cur - left]) 22 | cur += 1 23 | 24 | return max_length -------------------------------------------------------------------------------- /linkedlist/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ResolveWang/algorithm_qa/a0cb649acaf8cf9d808272bc15f1951f2c05c828/linkedlist/__init__.py -------------------------------------------------------------------------------- /linkedlist/q1.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定两个有序链表的头指针head1和head2,打印两个 3 | 链表的公共部分(即链表节点的值相同的部分) 4 | 5 | 思路:由于链表有序,所以可以比较head1和head2的值进行判断 6 | """ 7 | 8 | 9 | class Node: 10 | def __init__(self, value): 11 | self.value = value 12 | self.next = None 13 | 14 | 15 | class PublicPart: 16 | @classmethod 17 | def get_public_part(cls, head1, head2): 18 | if head1 is None or head2 is None: 19 | return 20 | 21 | if head1.value == head2.value: 22 | print(head1.value, end=' ') 23 | cls.get_public_part(head1.next, head2.next) 24 | elif head1.value > head2.value: 25 | cls.get_public_part(head1, head2.next) 26 | elif head1.value < head2.value: 27 | cls.get_public_part(head1.next, head2) 28 | 29 | if __name__ == '__main__': 30 | cur_head1 = Node(2) 31 | cur_head1.next = Node(3) 32 | cur_head1.next.next = Node(5) 33 | cur_head1.next.next.next = Node(6) 34 | 35 | cur_head2 = Node(1) 36 | cur_head2.next = Node(2) 37 | cur_head2.next.next = Node(5) 38 | cur_head2.next.next.next = Node(7) 39 | cur_head2.next.next.next.next = Node(8) 40 | 41 | PublicPart.get_public_part(cur_head1, cur_head2) -------------------------------------------------------------------------------- /linkedlist/q14.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定一个链表的头结点head和一个整数num,请实现函数将值为num的节点 3 | 全部删除。例如,链表为 4 | 1->2->3->4->None,num=3 链表调整后为1->2->4->None 5 | """ 6 | 7 | from linkedlist.toolcls import Node, PrintMixin 8 | 9 | 10 | class RemoveAssignedNode(PrintMixin): 11 | @classmethod 12 | def remove_node(cls, head, num): 13 | if head is None: 14 | return head 15 | 16 | pre = None 17 | cur = head 18 | 19 | new_head = head 20 | 21 | while cur is not None: 22 | next = cur.next 23 | if cur.value == num: 24 | if pre is not None: 25 | pre.next = next 26 | else: 27 | new_head = cur.next 28 | cur.next = None 29 | del cur 30 | else: 31 | pre = cur 32 | cur = next 33 | 34 | return new_head 35 | 36 | 37 | if __name__ == '__main__': 38 | node = Node(1) 39 | node.next = Node(2) 40 | node.next.next = Node(3) 41 | node.next.next.next = Node(2) 42 | node.next.next.next.next = Node(4) 43 | 44 | RemoveAssignedNode.print_list(RemoveAssignedNode.remove_node(node, 2)) -------------------------------------------------------------------------------- /linkedlist/q16.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定一个无序单链表的头结点head,实现单链表的选择排序 3 | 要求:额外空间复杂度为O(1) 4 | 5 | 思路:选择排序是从未排序的部分找到最小值,放到排好序部分的末尾 6 | (区分选择和冒泡排序,冒泡是比较并交换,而选择是移动指针,每次指 7 | 针都是指向最小值,一趟过程后把当前指针指向的值放到排好序的尾部) 8 | """ 9 | 10 | from linkedlist.toolcls import Node, PrintMixin 11 | 12 | 13 | class SortList(PrintMixin): 14 | @classmethod 15 | def sort_list(cls, head): 16 | cur = head 17 | new_head = cls.get_head(cur) 18 | cur = new_head 19 | temp = new_head 20 | while cur is not None: 21 | cur = cls.get_head(cur.next) 22 | temp.next = cur 23 | temp = cur 24 | return new_head 25 | 26 | @classmethod 27 | def get_head(cls, head): 28 | if head is None or head.next is None: 29 | return head 30 | pre = None 31 | cur = head 32 | 33 | little_pre = None 34 | little = head 35 | 36 | while cur is not None: 37 | if cur.value < little.value: 38 | little_pre = pre 39 | little = cur 40 | pre = cur 41 | cur = cur.next 42 | 43 | if little != head: 44 | little_pre.next = little.next 45 | little.next = head 46 | return little 47 | 48 | 49 | if __name__ == '__main__': 50 | head = Node(3) 51 | head.next = Node(1) 52 | head.next.next = Node(2) 53 | head = SortList.sort_list(head) 54 | SortList.sort_list(head) 55 | SortList.print_list(head) 56 | 57 | head = Node(3) 58 | head.next = Node(1) 59 | head.next.next = Node(4) 60 | head.next.next.next = Node(2) 61 | head = SortList.sort_list(head) 62 | SortList.print_list(head) 63 | -------------------------------------------------------------------------------- /linkedlist/q17.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:链表节点值类型为int型,给定一个链表中的节点,但不给定整个链表 3 | 的表头,如何在链表中删除node?请实现这个函数,并分析会出现哪些问题? 4 | 要求:时间复杂度为O(1) 5 | 6 | 思路:将待删除节点的下一个节点的值赋值给待删除节点,直接删除下一个节点 7 | 8 | 分析:这样做的坏处是:1)无法删除最后一个节点;2)这种删除方式本质上根本不是删除 9 | node节点,而是把node节点的值改变,然后删除node的下一个节点,在实际的工程中可能 10 | 会带来很大的问题,比如工程上一个节点代表一个提供服务的服务器,外界对每个节点都有 11 | 很多依赖,那么在上述方法中,实际上影响的是节点3,它会无法对外提供服务,再如,节点 12 | 可能代表很复杂的结构,节点值的复制会相当复杂,可能改变节点值的这个操作都是被禁止的。 13 | """ 14 | 15 | 16 | from linkedlist.toolcls import Node, PrintMixin 17 | 18 | 19 | class RemoveNode(PrintMixin): 20 | @classmethod 21 | def remove_node(cls, node): 22 | if node is None: 23 | return 24 | if node.next is None: 25 | raise RuntimeError("it can't be removed") 26 | node.value = node.next.value 27 | new_next = node.next.next 28 | del node.next 29 | node.next = new_next 30 | 31 | if __name__ == '__main__': 32 | head = Node(1) 33 | head.next = Node(2) 34 | head.next.next = Node(3) 35 | node = head 36 | RemoveNode.print_list(head) 37 | RemoveNode.remove_node(node) 38 | RemoveNode.print_list(head) 39 | 40 | head = Node(1) 41 | head.next = Node(2) 42 | head.next.next = Node(3) 43 | node = head.next 44 | RemoveNode.print_list(head) 45 | RemoveNode.remove_node(node) 46 | RemoveNode.print_list(head) -------------------------------------------------------------------------------- /linkedlist/q19.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定两个有序单链表的头结点head1和head2,请合并两个有序链表,合并后的链表依然 3 | 有序,并返回合并后链表的头结点。 4 | 例如: 5 | 0->2->3->7->None 6 | 1->3->5->7->9->None 7 | 合并后的链表为:0->1->2->3->3->5->7->7->9->None 8 | """ 9 | 10 | from linkedlist.toolcls import Node, PrintMixin 11 | 12 | 13 | class MergeListTool(PrintMixin): 14 | @classmethod 15 | def merge(cls, head1, head2): 16 | if head1 is None: 17 | return head2 18 | if head2 is None: 19 | return head1 20 | 21 | cur1 = head1 22 | cur2 = head2 23 | 24 | while cur1 is not None and cur2 is not None: 25 | while cur1 is not None and cur1.value <= cur2.value: 26 | pre = cur1 27 | cur1 = cur1.next 28 | pre.next = cur2 29 | 30 | if cur1 is None: 31 | break 32 | 33 | while cur2 is not None and cur1.value > cur2.value: 34 | pre = cur2 35 | cur2 = cur2.next 36 | pre.next = cur1 37 | 38 | if cur2 is None: 39 | break 40 | 41 | if head1.value < head2.value: 42 | return head1 43 | else: 44 | return head2 45 | 46 | 47 | if __name__ == '__main__': 48 | head1 = Node(0) 49 | head1.next = Node(2) 50 | head1.next.next = Node(3) 51 | head1.next.next.next = Node(7) 52 | 53 | head2 = Node(1) 54 | head2.next = Node(3) 55 | head2.next.next = Node(5) 56 | head2.next.next.next = Node(7) 57 | head2.next.next.next.next = Node(9) 58 | 59 | new_head = MergeListTool.merge(head1, head2) 60 | MergeListTool.print_list(new_head) -------------------------------------------------------------------------------- /linkedlist/q4.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:分别实现反转单向链表和反转双向链表的函数 3 | 4 | 要求:如果链表为N,时间复杂度要求为O(N),额外空间复杂度要求为O(1) 5 | """ 6 | 7 | 8 | from linkedlist.toolcls import PrintMixin, Node, DoubleNode 9 | 10 | 11 | class RevertList(PrintMixin): 12 | @classmethod 13 | def revert_linked_list(cls, head): 14 | pre = None 15 | 16 | while head is not None: 17 | next = head.next 18 | head.next = pre 19 | pre = head 20 | head = next 21 | 22 | return pre 23 | 24 | @classmethod 25 | def revert_double_linked_list(cls, head): 26 | pre = None 27 | while head is not None: 28 | next = head.next 29 | head.next = pre 30 | head.pre = next 31 | pre = head 32 | head = next 33 | return pre 34 | 35 | 36 | if __name__ == '__main__': 37 | node = Node(1) 38 | node.next = Node(2) 39 | node.next.next = Node(3) 40 | revert_node = RevertList.revert_linked_list(node) 41 | RevertList.print_list(revert_node) 42 | 43 | node2 = DoubleNode(1) 44 | node2.next = DoubleNode(2) 45 | node2.next.pre = node2 46 | node2.next.next = DoubleNode(3) 47 | node2.next.next.pre = node2.next 48 | node2.next.next.next = DoubleNode(4) 49 | node2.next.next.next.pre = node2.next.next 50 | node2.next.next.next.next = DoubleNode(5) 51 | node2.next.next.next.next.pre = node2.next.next.next 52 | node2.next.next.next.next.next = DoubleNode(6) 53 | node2.next.next.next.next.next.pre = node2.next.next.next 54 | 55 | revert_double_node = RevertList.revert_double_linked_list(node2) 56 | RevertList.print_list(revert_double_node) -------------------------------------------------------------------------------- /linkedlist/toolcls.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | def __init__(self, value): 3 | self.value = value 4 | self.next = None 5 | 6 | 7 | class DoubleNode: 8 | def __init__(self, value): 9 | self.value = value 10 | self.next = None 11 | self.pre = None 12 | 13 | 14 | class PrintMixin: 15 | @staticmethod 16 | def print_list(head): 17 | while head is not None: 18 | print(head.value, end=' ') 19 | head = head.next 20 | print() 21 | 22 | @staticmethod 23 | def print_list_rand(head): 24 | while head is not None: 25 | if head.rand is None: 26 | print('-', end=' ') 27 | else: 28 | print(head.rand.value, end=' ') 29 | head = head.next 30 | print() 31 | -------------------------------------------------------------------------------- /linkedlist/排序链表.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述: 在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。 3 | 4 | 5 | 示例: 6 | 4->2->1->3 => 1->2->3->4 7 | """ 8 | 9 | 10 | # Definition for singly-linked list. 11 | # class ListNode(object): 12 | # def __init__(self, x): 13 | # self.val = x 14 | # self.next = None 15 | 16 | class Solution(object): 17 | def sortList(self, head): 18 | """ 19 | :type head: ListNode 20 | :rtype: ListNode 21 | """ 22 | if not head or not head.next: 23 | return head 24 | 25 | pre = head 26 | slow = head 27 | fast = head 28 | # 通过快慢指针获取前半段和后半段 29 | while fast and fast.next: 30 | pre = slow 31 | slow = slow.next 32 | fast = fast.next.next 33 | 34 | # 注意在链表操作的时候,一定要把尾指针置为None 35 | pre.next = None 36 | h1 = self.sortList(head) 37 | h2 = self.sortList(slow) 38 | return self.merge(h1, h2) 39 | 40 | def merge(self, h1, h2): 41 | if not h1: 42 | return h2 43 | 44 | if not h2: 45 | return h1 46 | 47 | if h1.val < h2.val: 48 | h1.next = self.merge(h1.next, h2) 49 | return h1 50 | else: 51 | h2.next = self.merge(h1, h2.next) 52 | return h2 -------------------------------------------------------------------------------- /linkedlist/链表部分区间反转.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题: 反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。说明: 1 ≤ m ≤ n ≤ 链表长度。 3 | 4 | 示例: 5 | 输入: 1->2->3->4->5->NULL, m = 2, n = 4 6 | 输出: 1->4->3->2->5->NULL 7 | 8 | 注意点: 9 | 与单链表整体反转不同,这里需要注意头结点是否改变 10 | """ 11 | 12 | 13 | class Solution: 14 | def reverseBetween(self, head, m, n): 15 | count = 1 16 | pre = None 17 | cur = head 18 | while count < m: 19 | pre = cur 20 | cur = cur.next 21 | count += 1 22 | 23 | pre2 = None 24 | rotated_node = None 25 | while count <= n and cur: 26 | next_node = cur.next 27 | if not rotated_node: 28 | rotated_node = cur 29 | cur.next = pre2 30 | pre2 = cur 31 | cur = next_node 32 | count += 1 33 | 34 | if pre: 35 | pre.next = pre2 36 | 37 | if rotated_node: 38 | rotated_node.next = cur 39 | 40 | return head if pre else pre2 -------------------------------------------------------------------------------- /other/kmp_shortstr.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定一个字符串str1,只能往str1的后面添加字符变成str2。 3 | 要求1:str2必须包含两个str1,两个str1可以有重合,但是不能以同一个位置开头。 4 | 要求2:str2尽量短。 5 | 最终返回str2. 6 | 7 | 举例: 8 | str1 = 123, 则str2 = 123123,这时候包含两个str1,且不以相同位置开头,且str2最短。 9 | """ 10 | 11 | 12 | class ShortestStr: 13 | @classmethod 14 | def get_shortest_str(cls, str1): 15 | if not str1: 16 | return str1 17 | 18 | if len(str1) == 1: 19 | return str1 + str1 20 | 21 | if len(str1) == 2: 22 | if str1[0] == str1[1]: 23 | return str1 + str1[0] 24 | else: 25 | return str1 + str1 26 | 27 | index = cls.find_loggest_prefix(str1) 28 | return str1 + str1[index:] 29 | 30 | @classmethod 31 | def find_loggest_prefix(cls, str1): 32 | length = len(str1) 33 | next_arr = [0 for _ in range(length+1)] 34 | next_arr[0] = -1 35 | next_arr[1] = 0 36 | 37 | pos = 2 38 | cn = 0 39 | 40 | while pos < len(next_arr): 41 | if str1[pos-1] == str1[cn]: 42 | cn += 1 43 | next_arr[pos] = cn 44 | pos += 1 45 | elif cn > 0: 46 | cn = next_arr[cn] 47 | else: 48 | next_arr[pos] = 0 49 | pos += 1 50 | return next_arr[-1] 51 | 52 | 53 | if __name__ == '__main__': 54 | test1 = "a" 55 | print(ShortestStr.get_shortest_str(test1)) 56 | 57 | test2 = "aa" 58 | print(ShortestStr.get_shortest_str(test2)) 59 | 60 | test3 = "ab" 61 | print(ShortestStr.get_shortest_str(test3)) 62 | 63 | test4 = "abcdabcd" 64 | print(ShortestStr.get_shortest_str(test4)) 65 | 66 | test5 = "abracadabra" 67 | print(ShortestStr.get_shortest_str(test5)) -------------------------------------------------------------------------------- /other/manacher_shortest.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定一个字符串str1,只能往str1的后面添加字符变成str2,要求str2 3 | 整体都是回文串且最短。 4 | 5 | 举例: 6 | str1 = ABC12321,则返回ABC12321CBA 7 | """ 8 | 9 | import sys 10 | 11 | 12 | class ShortestEnd: 13 | @classmethod 14 | def get_shortest_end_by_manacher(cls, str1): 15 | if not str1: 16 | return '' 17 | 18 | my_str = cls.get_manacher_str(str1) 19 | center = -1 20 | right = -1 21 | parr = [0 for _ in range(len(my_str))] 22 | max_value = -sys.maxsize 23 | for i in range(len(my_str)): 24 | if i > right: 25 | parr[i] = 1 26 | else: 27 | parr[i] = min([parr[2*center-i], right-i]) 28 | while i - parr[i] > -1 and i + parr[i] < len(my_str): 29 | if my_str[parr[i]+i] == my_str[i-parr[i]]: 30 | parr[i] += 1 31 | else: 32 | break 33 | 34 | if i + parr[i] > right: 35 | right = i + parr[i] 36 | center = i 37 | 38 | if right == len(my_str): 39 | max_value = parr[i] 40 | break 41 | return str1 + str1[:len(str1)+1-max_value][::-1] 42 | 43 | @classmethod 44 | def get_manacher_str(cls, str1): 45 | new_str = '#' 46 | for s in str1: 47 | new_str += '{}#'.format(s) 48 | 49 | return new_str 50 | 51 | 52 | if __name__ == '__main__': 53 | my_str = 'abcd12321' 54 | print(ShortestEnd.get_shortest_end_by_manacher(my_str)) -------------------------------------------------------------------------------- /other/max_gap.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定一个无序数组,求如果有序之后相邻两个元素最大的差值。 3 | 4 | 要求:时间复杂度为O(N) 5 | 6 | 比如 3,4,0,9,10,排序之后是0,3,4,9,10 7 | 相邻两数最大差值是5 8 | 9 | 思想:由于要求时间复杂度为O(N),所以无法使用基于比较的排序算法,可以 10 | 借用桶排序的思想。 11 | """ 12 | 13 | 14 | class MaxGapFounder: 15 | @classmethod 16 | def get_max_gap(cls, arr): 17 | if not arr or len(arr) < 2: 18 | return 0 19 | 20 | max_value = max(arr) 21 | min_value = min(arr) 22 | length = len(arr) 23 | 24 | if max_value == min_value: 25 | return 0 26 | 27 | has_num = [False for _ in range(length+1)] 28 | max_values = [0 for _ in range(length+1)] 29 | min_values = [0 for _ in range(length+1)] 30 | 31 | for index, value in enumerate(arr): 32 | pos = cls.bucket(value, length, max_value, min_value) 33 | max_values[pos] = max([max_values[pos], value]) if has_num[pos] else value 34 | min_values[pos] = min([min_values[pos], value]) if has_num[pos] else value 35 | has_num[pos] = True 36 | 37 | max_gap = 0 38 | i = 1 39 | last_max = max_values[0] 40 | while i <= length: 41 | if has_num[i]: 42 | max_gap = max(min_values[i] - last_max, max_gap) 43 | last_max = max_values[i] 44 | i += 1 45 | 46 | return max_gap 47 | 48 | @classmethod 49 | def bucket(cls, num, length, max_value, min_value): 50 | return int((num - min_value)/(max_value - min_value) * length) 51 | 52 | 53 | if __name__ == '__main__': 54 | my_arr = [3, 4, 0, 9, 10] 55 | print(MaxGapFounder.get_max_gap(my_arr)) -------------------------------------------------------------------------------- /other/n个骰子的点数.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题: 把n个骰子扔在地上,所有的骰子朝上一面的点数之和为s。输入n,打印出 3 | s的所有可能的值出现的概率 4 | """ 5 | 6 | 7 | class Probability: 8 | def __init__(self): 9 | self.max_value = 6 10 | 11 | def get_probability(self, n): 12 | if n < 1: 13 | return 14 | 15 | probalities = [0 for _ in range(self.max_value*n-n+1)] 16 | 17 | self.process(n, probalities) 18 | total = pow(self.max_value, n) 19 | for index, value in enumerate(probalities): 20 | print('{}:{}'.format(index+n, round(value/total, 4))) 21 | 22 | def process(self, n, probalities): 23 | for i in range(1, self.max_value+1): 24 | self.process_detail(n, i, n-1, probalities) 25 | 26 | def process_detail(self, aim, cur_sum, cur_n, probalities): 27 | if cur_n == 0: 28 | probalities[cur_sum-aim] += 1 29 | else: 30 | for i in range(1, self.max_value+1): 31 | self.process_detail(aim, cur_sum+i, cur_n-1, probalities) 32 | 33 | 34 | if __name__ == '__main__': 35 | solution = Probability() 36 | solution.get_probability(2) -------------------------------------------------------------------------------- /other/q11.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:设计一种结构,该结构具有如下三个功能: 3 | 1.insert(key):将某个key加入到该结构,做到不重复加入 4 | 2.delete(key):将原本在结构中的某个key删除 5 | 3.get_random():等概率随机返回结构中的任意一个key 6 | 7 | 要求: 8 | insert、delete和get_random方法的时间复杂度都是O(1) 9 | """ 10 | import random 11 | 12 | 13 | class RandomPool: 14 | def __init__(self): 15 | self.size = 0 16 | self.key_map = dict() 17 | self.data_map = dict() 18 | 19 | def insert(self, key): 20 | if key not in self.key_map: 21 | self.size += 1 22 | self.data_map[key] = self.size 23 | self.key_map[self.size] = key 24 | 25 | def delete(self, key): 26 | if key in self.key_map: 27 | to_delete_index = self.data_map.pop(key) 28 | self.key_map.pop(to_delete_index) 29 | last_key = self.key_map.pop(self.size) 30 | self.data_map.pop(last_key) 31 | self.size -= 1 32 | self.key_map[to_delete_index] = last_key 33 | self.data_map[last_key] = to_delete_index 34 | 35 | def get_random(self): 36 | if self.size == 0: 37 | return 38 | 39 | index = random.randint(1, self.size) 40 | return self.key_map[index] -------------------------------------------------------------------------------- /other/q12.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:假设函数Math.random()等概率随机返回一个在[0,1)上的数,那么我们 3 | 知道在[0,x)区间上的数出现的概率为(0 1: 33 | first_one_num = pow(10, pos-1) 34 | else: 35 | first_one_num = n - num * pow(10, pos-1) + 1 36 | 37 | other_one_num = num * (pos-1) * (pow(10, pos-1)/10) 38 | return int(first_one_num + other_one_num + cls.process(left, pos-1)) 39 | 40 | 41 | if __name__ == '__main__': 42 | print(OneCounter.get_nums_of_one(9991)) 43 | -------------------------------------------------------------------------------- /other/q17.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定一个长度为N且没有重复元素的数组arr和一个整数n,实现函数等概率随机打印 3 | arr中的m个数 4 | 5 | 要求: 6 | 1.相同的数不重复打印 7 | 2.时间复杂度为O(M),额外空间复杂度为O(1) 8 | 3.可以改变arr数组 9 | """ 10 | import random 11 | 12 | 13 | class RandomPrinter: 14 | @classmethod 15 | def print_random_value(cls, arr, m): 16 | if not arr or m < 0: 17 | return 18 | 19 | length = min([len(arr), m]) 20 | 21 | for i in range(length): 22 | random_index = random.randint(0, len(arr)-1-i) 23 | print(arr[random_index]) 24 | arr[random_index], arr[len(arr)-1-i] = arr[len(arr)-1-i], arr[random_index] 25 | 26 | 27 | if __name__ == '__main__': 28 | RandomPrinter.print_random_value([1, 2, 3, 4, 5], 5) -------------------------------------------------------------------------------- /other/q18.py: -------------------------------------------------------------------------------- 1 | """ 2 | 定义回文数的概念如下: 3 | 如果一个非负数左右完全对应,则该数是回文数,例如:121,22等 4 | 如果一个负数的绝对值左右完全对应,也是回文数,例如:-121,-22等 5 | 给定一个32位整数num,判断num是不是回文数 6 | """ 7 | import sys 8 | 9 | 10 | class PalindromeJudger: 11 | @classmethod 12 | def is_palindrome(cls, num): 13 | if num == -sys.maxsize: 14 | return False 15 | 16 | num = abs(num) 17 | 18 | high_pos = 1 19 | cur = 9 20 | while cur/num < 1: 21 | cur += 9 * pow(10, high_pos) 22 | high_pos += 1 23 | 24 | while num != 0: 25 | high_num = int(num / pow(10, high_pos-1)) 26 | low_num = num % 10 27 | if high_num != low_num: 28 | return False 29 | num = int((num - (high_num * pow(10, high_pos-1) - low_num))/10) 30 | high_pos -= 2 31 | 32 | return True 33 | 34 | 35 | if __name__ == '__main__': 36 | print(PalindromeJudger.is_palindrome(12321)) 37 | print(PalindromeJudger.is_palindrome(12320)) 38 | print(PalindromeJudger.is_palindrome(22)) 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /other/q19.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:有序数组arr可能经过一次旋转,也可能没有,且arr可能存在重复的数, 3 | 例如,有序数组[1,2,3,4,5,6,7],可以旋转处理成[4,5,6,7,1,2,3]等。给定 4 | 一个可能旋转过的有序数组,返回arr的最小值. 5 | 6 | 变型:找出旋转数组的中位数 7 | 思路还是找到最小值(断点),由于找到最小值后,我们可以很方便的找到离最小值m 8 | 个位置的数 9 | """ 10 | 11 | 12 | class SmallnestNumFinder: 13 | @classmethod 14 | def get_smallest_num(cls, arr): 15 | if not arr: 16 | return 17 | 18 | if len(arr) == 1: 19 | return arr[0] 20 | 21 | low = 0 22 | high = len(arr) - 1 23 | 24 | while True: 25 | if arr[low] < arr[high]: 26 | return arr[low] 27 | mid = int((high - low)/2) + low 28 | if mid == low: 29 | return arr[high] 30 | 31 | if arr[mid] > arr[high]: 32 | # deal mid to high 33 | low = mid 34 | elif arr[mid] < arr[low]: 35 | # deal low to mid 36 | high = mid 37 | else: 38 | cur = low 39 | while arr[cur] == arr[high]: 40 | cur += 1 41 | if arr[cur] < arr[high]: 42 | return arr[cur] 43 | else: 44 | low = cur 45 | 46 | 47 | if __name__ == '__main__': 48 | print(SmallnestNumFinder.get_smallest_num([4, 5, 5, 5, 1, 2, 3])) 49 | -------------------------------------------------------------------------------- /other/q2.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定两个不等于0的整数M和N,求M和N的最大公约数. 3 | """ 4 | 5 | 6 | def get_bigest_public_num(m, n): 7 | if n == 0: 8 | return m 9 | else: 10 | return get_bigest_public_num(n, m % n) 11 | 12 | 13 | if __name__ == '__main__': 14 | print(get_bigest_public_num(10, 23)) -------------------------------------------------------------------------------- /other/q20.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:有序数组arr可能经过一次旋转处理,也可能没有,且arr可能存在重复 3 | 的数.例如,有序数组[1,2,3,4,5,6,7],可以旋转成[4,5,6,7,1,2,3]等。给 4 | 定一个可能旋转过的有序数组arr,再给定一个数num,返回arr中是否含有num. 5 | """ 6 | 7 | 8 | class NumberFinder: 9 | @classmethod 10 | def is_exists(cls, arr, num): 11 | if not arr: 12 | return False 13 | 14 | low = 0 15 | high = len(arr) - 1 16 | 17 | while low <= high: 18 | mid = int(int(high - low) / 2 + low) 19 | if arr[mid] == num: 20 | return True 21 | if arr[low] == arr[mid] == arr[high]: 22 | while arr[low] == arr[mid] and low != mid: 23 | low += 1 24 | if low == mid: 25 | low += 1 26 | 27 | if low == mid: 28 | low += 1 29 | continue 30 | 31 | if arr[low] < arr[mid]: 32 | if arr[low] <= num < arr[mid]: 33 | high -= 1 34 | else: 35 | low = mid + 1 36 | else: 37 | if arr[mid] < num <= arr[high]: 38 | low = mid + 1 39 | else: 40 | high = mid - 1 41 | 42 | return False 43 | 44 | 45 | if __name__ == '__main__': 46 | print(NumberFinder.is_exists([4, 5, 6, 7, 1, 2, 3], 3)) 47 | print(NumberFinder.is_exists([4, 5, 6, 7, 1, 2, 3], 9)) -------------------------------------------------------------------------------- /other/q24.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:堆结构一般是使用固定长度的数组结构实现的。这样的实现虽然足够经典, 3 | 但存在扩容的负担,比如不断向堆中增加元素,使得固定数组快耗尽时,就不得不申请 4 | 一个更大的固定数组,然后把原来数组中的对象复制到新的数组里完成堆的扩容,所以, 5 | 如果扩容时堆的元素个数为N,那么扩容行为的时间复杂度为O(N)。请设计一种没有扩 6 | 容负担的堆结构,即在任何时刻有关堆的操作时间复杂度都不超过O(logN)。 7 | 8 | 要求: 9 | 1.没有扩容的负担 10 | 2.可以生成小根堆,也可以生成大根堆 11 | 3.包含getHead方法,返回当前堆顶的值 12 | 4.包含getSize方法,返回当前堆的大小 13 | 5.包含add(x)方法,即向堆中新加元素x,操作后依然是小根堆/大根堆 14 | 6.包含popHead方法,即删除并返回堆顶的值,操作后依然是小根堆/大根堆 15 | 7.如果堆中的节点个数为N,那么各个方法的时间复杂度为: 16 | getHead: O(1) 17 | getSize: O(1) 18 | add: O(logN) 19 | popHead: O(logN) 20 | """ -------------------------------------------------------------------------------- /other/q26.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定两个有序数组arr1和arr2,已知两个数组的长度都为N,求两个数组 3 | 中所有数的上中位数。 4 | 5 | 举例: 6 | arr1=[1, 2, 3, 4], arr2=[3, 4, 5, 6] 7 | 总共有8个数,那么上中位数是第4小的数,所以返回3 8 | arr1=[0, 1, 2], arr2=[3, 4, 5] 9 | 总共有6个数,那么上中位数是第3小的数,所以返回2 10 | 11 | 要求: 12 | 时间复杂度为O(logN),额外空间复杂度为O(1) 13 | """ 14 | 15 | 16 | class MiddleNumFinder: 17 | @classmethod 18 | def find_pre_middle_num(cls, arr1, arr2): 19 | if not arr1 or not arr2 or len(arr1) != len(arr2): 20 | return 21 | 22 | length = len(arr1) 23 | start1 = 0 24 | start2 = 0 25 | end1 = length - 1 26 | end2 = length - 1 27 | 28 | while start1 < end1: 29 | mid1 = (start1 + end1) >> 1 30 | mid2 = (start2 + end2) >> 1 31 | if arr1[mid1] == arr2[mid2]: 32 | return arr1[mid1] 33 | elif arr1[mid1] > arr2[mid2]: 34 | if (end1 - start1 + 1) % 2 == 1: 35 | end1 = mid1 36 | start2 = mid2 37 | else: 38 | end1 = mid1 39 | start2 = mid2 + 1 40 | else: 41 | if (end1 - start1 + 1) % 2 == 1: 42 | end2 = mid2 43 | start1 = mid1 44 | else: 45 | end2 = mid2 46 | start1 = mid1 + 1 47 | return min([arr1[start1], arr2[start2]]) 48 | 49 | 50 | if __name__ == '__main__': 51 | print(MiddleNumFinder.find_pre_middle_num([1, 2, 3, 4], [3, 4, 5, 6])) 52 | print(MiddleNumFinder.find_pre_middle_num([1, 2, 3, 4], [5, 6, 7, 8])) 53 | print(MiddleNumFinder.find_pre_middle_num([1, 2, 3, 4], [5, 6, 7, 8])) 54 | print(MiddleNumFinder.find_pre_middle_num([2, 7, 10, 10, 14, 14, 20], 55 | [4, 13, 13, 13, 21, 33, 47])) -------------------------------------------------------------------------------- /other/q27.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定两个有序数组arr1和arr2,再给定一个整数k,返回所有的数中 3 | 第k小的数。 4 | 举例: 5 | arr1=[1, 2, 3, 4, 5],arr2=[3, 4, 5], k=1, 返回1 6 | arr1=[1, 2, 3], arr2=[3, 4, 5, 6], k=4,返回3 7 | 8 | 要求: 9 | 如果arr1的长度为N,arr2的长度为M,时间复杂度请达到O(log(min{M,N})),额外 10 | 空间复杂度为O(1) 11 | """ 12 | from other.q26 import MiddleNumFinder 13 | 14 | 15 | class KthNumFinder: 16 | @classmethod 17 | def get_kth_num(cls, arr1, arr2, k): 18 | if k < 1 or k > len(arr1) + len(arr2): 19 | return 20 | 21 | short_arr = arr1 if len(arr1) <= len(arr2) else arr2 22 | long_arr = arr1 if len(arr1) > len(arr2) else arr2 23 | slen = len(short_arr) 24 | llen = len(long_arr) 25 | 26 | if k <= slen: 27 | return MiddleNumFinder.find_pre_middle_num(arr1[:k], arr2[:k]) 28 | elif k > llen: 29 | if short_arr[-1] <= long_arr[0]: 30 | return long_arr[k-slen-1] 31 | if long_arr[-1] <= short_arr[0]: 32 | return short_arr[k-llen-1] 33 | return MiddleNumFinder.find_pre_middle_num(short_arr[k-llen:], long_arr[k-slen:]) 34 | else: 35 | return MiddleNumFinder.find_pre_middle_num(short_arr, long_arr[k-slen:k]) 36 | 37 | 38 | if __name__ == '__main__': 39 | print(KthNumFinder.get_kth_num([1, 2, 3, 4, 5], [3, 4, 5], 1)) 40 | print(KthNumFinder.get_kth_num([1, 2, 6], [3, 4, 7, 21], 5)) 41 | print(KthNumFinder.get_kth_num([1, 2, 6, 9, 23, 14], [3, 4, 7, 21], 5)) -------------------------------------------------------------------------------- /other/q3.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定一个非负整数N,返回N!结果的末尾为0的数量. 3 | 例如: 4 | 3!=6,结尾的末尾没有0,返回0. 5!=120,结果的末尾有1个0,返回1. 5 | 1000000000!,结果的末尾有249999998个0,返回249999998. 6 | 7 | 进阶题目: 8 | 给定一个非负整数N,如果用二进制数表达N!的结果,返回最低位的1在 9 | 哪个位置上,认为最右的位置为位置0. 10 | 例如:1!=1,最低位的1在0位置上.2!=2,最低位的1在1位置上.1000000000!, 11 | 最低位的1在999999987位置上. 12 | """ 13 | 14 | 15 | class AssignedNumFinder: 16 | @classmethod 17 | def get_0_num(cls, n): 18 | if n < 0: 19 | return 0 20 | res = 0 21 | i = 5 22 | while i < n+1: 23 | cur = i 24 | while cur % 5 == 0: 25 | res += 1 26 | cur = cur / 5 27 | 28 | i += 5 29 | 30 | return res 31 | 32 | @classmethod 33 | def get_0_nums(cls, n): 34 | res = 0 35 | for i in range(1, n+1): 36 | res += cls.get_0_num(i) 37 | 38 | return res 39 | 40 | @classmethod 41 | def get_0_nums_best(cls, n): 42 | biggest_num = 0 43 | while pow(5, biggest_num) <= n: 44 | biggest_num += 1 45 | 46 | res = 0 47 | for i in range(1, biggest_num+1): 48 | res += int(n/pow(5, i)) 49 | 50 | return res 51 | 52 | @classmethod 53 | def get_1_nums_best(cls, n): 54 | biggest_num = 0 55 | while pow(2, biggest_num) <= n: 56 | biggest_num += 1 57 | 58 | res = 0 59 | for i in range(1, biggest_num + 1): 60 | res += int(n / pow(2, i)) 61 | 62 | return res 63 | 64 | 65 | if __name__ == '__main__': 66 | print(AssignedNumFinder.get_0_nums_best(1000000000)) 67 | print(AssignedNumFinder.get_1_nums_best(1000000000)) -------------------------------------------------------------------------------- /other/q34.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:一条直线上有居民点,邮局只能建在居民点上。给定一个有序整型数组arr, 3 | 每个值代表居民点的一维坐标,再给定一个正数num,表示邮局数量。选择num个居民 4 | 点建立num个邮局,使所有的居民点到邮局的总距离最短,返回最短的总距离。 5 | 6 | 举例: 7 | arr=[1,2,3,4,5,1000],num=2 8 | 第一个邮局建立在3位置,第二个建立在1000位置,那么1位置到邮局距离为2,2位置到 9 | 邮局距离为1,3位置到邮局距离为0,4位置到邮局距离为1,5位置到邮局距离为2,1000位置 10 | 到邮局距离为0.这种方案下,距离最短,为6,所以返回6 11 | """ 12 | import sys 13 | 14 | 15 | class PostAddress: 16 | @classmethod 17 | def solution_by_dp(cls, arr, num): 18 | if not arr or num < 1 or len(arr) <= num: 19 | return 0 20 | 21 | length = len(arr) 22 | w = [[0 for _ in range(length + 1)] for _ in range(length + 1)] 23 | for i, _ in enumerate(arr): 24 | j = i + 1 25 | while j < length: 26 | w[i][j] = w[i][j - 1] + arr[j] - arr[int((i + j) / 2)] 27 | j += 1 28 | 29 | dp = [[0 for _ in range(length)] for _ in range(num)] 30 | for i in range(length): 31 | dp[0][i] = w[0][i] 32 | 33 | for i in range(1, num): 34 | for j in range(i + 1, length): 35 | dp[i][j] = sys.maxsize 36 | for k in range(j + 1): 37 | dp[i][j] = min([dp[i][j], dp[i - 1][k] + w[k + 1][j]]) 38 | 39 | return dp[num - 1][length - 1] 40 | 41 | 42 | if __name__ == '__main__': 43 | print(PostAddress.solution_by_dp([1, 2, 3, 4, 5, 1000], 2)) 44 | -------------------------------------------------------------------------------- /other/q4.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:在一个二维坐标系中,所有的值都是double类型,那么一个矩形可以由4个点 3 | 来代表,(x1,y1)为最左的点、(x2,y2)为最上的点、(x3,y3)为最下的点、(x4,y4)为 4 | 最右的点.给定4个点代表的矩形,再给定一个点(x,y),判断(x,y)是否在矩形中. 5 | """ 6 | import math 7 | 8 | 9 | class PointInRectangle: 10 | @classmethod 11 | def is_inside_simple(cls, x1, y1, x4, y4, x, y): 12 | if x <= x1: 13 | return False 14 | if y1 <= y: 15 | return False 16 | if x >= x4: 17 | return False 18 | if y <= y4: 19 | return False 20 | 21 | return True 22 | 23 | @classmethod 24 | def is_inside(cls, x1, y1, x2, y2, x3, y3, x4, y4, x, y): 25 | if y1 == y2: 26 | return cls.is_inside_simple(x1, y1, x4, y4, x, y) 27 | l = abs(y4 - y3) 28 | k = abs(x4 - x3) 29 | s = math.sqrt(l*l+k*k) 30 | sin = l / s 31 | cos = k / s 32 | 33 | new_x1 = cos * x1 + sin * y1 34 | new_y1 = -sin * x1 + y1 * cos 35 | news_x4 = cos * x4 + sin * y4 36 | new_y4 = -sin * x4 + y4 * cos 37 | 38 | new_x = cos * x + sin * y 39 | new_y = -sin * x + y * cos 40 | 41 | return cls.is_inside_simple(new_x1, new_y1, news_x4, new_y4, new_x, new_y) 42 | 43 | 44 | if __name__ == '__main__': 45 | print(PointInRectangle.is_inside(0, 3, 3, 7, 4, 0, 7, 4, 4, 3)) -------------------------------------------------------------------------------- /other/q5.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:在一个二维坐标系中,所有的值都是double类型,那么一个三角形可以由3个点来代表, 3 | 给定3个点代表的三角形,再给定一个点(x,y),判断(x,y)是否在三角形中. 4 | """ 5 | 6 | 7 | class PointInTriangle: 8 | @classmethod 9 | def cross_product(cls, x1, y1, x2, y2): 10 | return x1 * y2 - x2 * y1 11 | 12 | @classmethod 13 | def is_inside(cls, x1, y1, x2, y2, x3, y3, x, y): 14 | if cls.cross_product(x3-x1, y3-y1, x2-x1, y2-y1) >= 0: 15 | x2, x3 = x3, x2 16 | y2, y3 = y3, y2 17 | 18 | if cls.cross_product(x2-x1, y2-y1, x-x1, y-y1) < 0: 19 | return False 20 | 21 | if cls.cross_product(x3-x2, y3-y2, x-x2, y-y2) < 0: 22 | return False 23 | 24 | if cls.cross_product(x1-x3, y1-y3, x-x3, y-y3) < 0: 25 | return False 26 | 27 | return True 28 | 29 | 30 | if __name__ == '__main__': 31 | x1 = -5 32 | y1 = 0 33 | x2 = 0 34 | y2 = 8 35 | x3 = 5 36 | y3 = 0 37 | x = 0 38 | y = 5 39 | print(PointInTriangle.is_inside(x1, y1, x2, y2, x3, y3, x, y)) -------------------------------------------------------------------------------- /other/q6.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:请把一段纸条竖着放在桌子上,然后从纸条的下边向上方对折1次,压出折痕后 3 | 展开,此时折痕是凹下去的,即折痕凸起的方向指向纸条的背面.如果从纸条的下边向上方 4 | 连续对折2次,压出折痕后展开,此时有三条折痕,从上到下依次是下折痕、下折痕和上折痕. 5 | 给定一个输入参数N,代表纸条都从下边向上方连续对折N次,请从上到下打印所有折痕的 6 | 方向。 7 | 8 | 例如: 9 | N=1时,打印: 10 | down 11 | N=2时,打印: 12 | down 13 | down 14 | up 15 | """ 16 | 17 | 18 | class FoldProblem: 19 | @classmethod 20 | def get_folds(cls, n): 21 | cls.deal_with_detail(1, n, True) 22 | 23 | @classmethod 24 | def deal_with_detail(cls, i, n, is_down): 25 | if i > n: 26 | return 27 | 28 | cls.deal_with_detail(i+1, n, True) 29 | if is_down: 30 | print('down') 31 | else: 32 | print('up') 33 | cls.deal_with_detail(i+1, n, False) 34 | 35 | 36 | if __name__ == '__main__': 37 | FoldProblem.get_folds(3) -------------------------------------------------------------------------------- /other/q7.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:有一个机器按自然数序列的方式吐出球(1号球、2号球、3号球...),你有一个 3 | 袋子,袋子最多能装K个球,除了袋子外,没有别的可用空间。设计一种方案,使得当机器 4 | 吐出第N个球的时候(N>K),你袋子中的球数是K个,同时可以保证从1号球到N号球中的每一 5 | 个,被选进袋子的概率都是K/N. 6 | """ 7 | import random 8 | 9 | 10 | class KNRandomGetter: 11 | @classmethod 12 | def get_res(cls, k, n): 13 | if k < 1 or n < 1: 14 | return 15 | 16 | if n <= k: 17 | return list(range(1, n+1)) 18 | 19 | res = list(0 for _ in range(k)) 20 | 21 | for i in range(k): 22 | res[i] = i + 1 23 | 24 | for i in range(k, n-k): 25 | value = random.randint(1, i+1) 26 | if value <= k: 27 | random_index = random.randint(0, k-1) 28 | res[random_index] = i 29 | 30 | return res 31 | 32 | 33 | if __name__ == '__main__': 34 | print(KNRandomGetter.get_res(10, 10000)) -------------------------------------------------------------------------------- /other/q8.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:哈希表常见的三个操作是put、get和containsKey,而且这三个操作的时间复杂度 3 | 为O(1).现在想加入一个setAll功能,就是把所有记录的value都设置成统一的值.请设计并实 4 | 现这种有setAll功能的哈希表,并且put、get、containsKey和setAll四个操作的时间复杂 5 | 度都为O(1). 6 | """ 7 | 8 | 9 | class MyValue: 10 | def __init__(self, value, last_update): 11 | self.value = value 12 | self.last_update = last_update 13 | 14 | 15 | class MyHashMap: 16 | def __init__(self): 17 | self.hash_map = dict() 18 | self.last_update = 0 19 | self.all_value = MyValue(None, -1) 20 | 21 | def put(self, key, value): 22 | self.last_update += 1 23 | my_value = MyValue(value, self.last_update) 24 | self.hash_map[key] = my_value 25 | 26 | def set_all(self, value): 27 | self.last_update += 1 28 | self.all_value = MyValue(value, self.last_update) 29 | 30 | def contains_key(self, key): 31 | return key in self.hash_map 32 | 33 | def get(self, key): 34 | if not self.contains_key(key): 35 | return 36 | if self.all_value.last_update > self.hash_map.get(key).last_update: 37 | return self.all_value.value 38 | else: 39 | return self.hash_map.get(key).value 40 | 41 | 42 | if __name__ == '__main__': 43 | my_hash_map = MyHashMap() 44 | my_hash_map.put('Tom', 1) 45 | my_hash_map.put('James', 2) 46 | print(my_hash_map.get('Tom')) 47 | print(my_hash_map.contains_key('James')) 48 | 49 | my_hash_map.set_all(3) 50 | my_hash_map.get('Tom') 51 | print(my_hash_map.get('Tom')) 52 | my_hash_map.put('Tom', 333) 53 | print(my_hash_map.get('Tom')) 54 | -------------------------------------------------------------------------------- /other/q9.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个长度为N(N>1)的整型数组arr,可以划分为左右两个部分,左部分为arr[0..K], 3 | 右部分为arr[K+1..N-1],K可以取值的范围是[0,N-2].求这么多划分方案中,左部分中 4 | 的最大值减去右部分的最大值的绝对值中,最大是多少? 5 | """ 6 | import sys 7 | 8 | 9 | class LeftRightMaxDiff: 10 | @classmethod 11 | def get_left_right_max_diff(cls, arr): 12 | if not arr or len(arr) < 2: 13 | return 14 | 15 | max_value = -sys.maxsize 16 | for index in range(len(arr)): 17 | max_value = max([max_value, arr[index]]) 18 | 19 | return max(max_value-arr[0], max_value-arr[-1]) 20 | 21 | 22 | if __name__ == '__main__': 23 | my_arr = [2, 7, 3, 1, 1] 24 | print(LeftRightMaxDiff.get_left_right_max_diff(my_arr)) -------------------------------------------------------------------------------- /other/套路总结.md: -------------------------------------------------------------------------------- 1 | 1.一类非常有用的数据结构 2 | 3 | 在原始数据结构上带上一个修改的时间戳,由该时间戳(或者版本号)来判断程序应该使用哪个值. 4 | 5 | 2.数组问题划分为左右两部分的相关问题的预处理技巧 6 | 7 | 分别从左向右遍历,生成辅助数组lArr,lArr[i]表示从0到i-1的相关结果,然后从右向左遍历,生 8 | 成辅助数组rArr[i],表示从i到length-1的相关结果,两者结合就可以求数组两部分相关的问题了. 9 | 10 | 3.LRU缓存实现 11 | 12 | 使用双端队列+HashMap 13 | 14 | 4.使用hashmap设计get/put/delete()操作为O(1)和一些其它操作为O(1)的数据结构 15 | 16 | 有时候,不仅需要根据key获取value进行操作,还需要根据value获取key进行操作,这种情况下可以 17 | 维护两个hashmap,一个是key-value,另外一个是value-key.此外,有时候还需要一个size/cap属性. 18 | 19 | 5.使用有限空间记录多个状态 20 | 21 | 如果某件事分做过和没做过,那么可以使用正负来记录状态. 22 | 23 | 6.数组方面等概率随机的面试题 24 | 25 | 和最后一个位置进行交换,然后把右指针往左移动 26 | 27 | 7.在查找类型题目中,如果题目要求时间复杂度为*logN*,那么优先考虑二分查找,目前出现过的二分情况有 28 | a) 一个有序数组; b)一个部分有序数组; c)两个有序数组,该类型需要对两个数组分别二分 29 | 30 | 8.在输入输出参数固定的情况下,然后一个操作的决策有限,比如做或者不做,可以考虑使用递归,然后将其 31 | 使用动态规划来进行优化,参考丢棋子问题 -------------------------------------------------------------------------------- /other/扑克牌中的顺子.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题:LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张^_^)... 3 | 他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!! 4 | “红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子.....LL不高兴了,他想了想,决定大\小 王可以看 5 | 成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4), 6 | “So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何。 7 | 为了方便起见,你可以认为大小王是0 8 | """ 9 | 10 | 11 | # -*- coding:utf-8 -*- 12 | class Solution: 13 | def IsContinuous(self, numbers): 14 | if not numbers or len(numbers) != 5: 15 | return False 16 | 17 | numbers.sort() 18 | 19 | index = 0 20 | total_zero = 0 21 | pre_num = -1 22 | while index < 5: 23 | if numbers[index] == 0: 24 | total_zero += 1 25 | index += 1 26 | else: 27 | if pre_num != -1: 28 | diff = numbers[index] - pre_num 29 | if diff == 1: 30 | pre_num = numbers[index] 31 | index += 1 32 | elif diff == 0: 33 | return False 34 | else: 35 | if total_zero < diff-1: 36 | return False 37 | else: 38 | pre_num = numbers[index] 39 | total_zero -= (diff -1) 40 | index += 1 41 | else: 42 | pre_num = numbers[index] 43 | index += 1 44 | 45 | return True 46 | 47 | 48 | if __name__ == '__main__': 49 | arr = [0, 3, 2, 6, 4] 50 | r = Solution().IsContinuous(arr) 51 | print(r) -------------------------------------------------------------------------------- /stackandqueue/q1.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:构造一个特殊的栈,要求 3 | 1)使之pop()、push()和getMin() (求最小值) 的操作的时间复杂度都为O(1) 4 | 2)可以使用现成的栈类型 5 | 6 | 思路:使用两个栈,一个栈保存所有数据,一个栈保存最小数据集 7 | """ 8 | 9 | 10 | class MyStack: 11 | def __init__(self): 12 | self.datastack = list() 13 | self.minstack = list() 14 | 15 | def push(self, value): 16 | length = len(self.minstack) 17 | if length == 0: 18 | self.minstack.append(value) 19 | else: 20 | if value <= self.minstack[-1]: 21 | self.minstack.append(value) 22 | 23 | self.datastack.append(value) 24 | 25 | def pop(self): 26 | if len(self.datastack) == 0: 27 | raise RuntimeError('the stack is empty') 28 | i = self.datastack.pop() 29 | if i == self.get_min(): 30 | self.minstack.pop() 31 | return i 32 | 33 | def get_min(self): 34 | if len(self.datastack) == 0: 35 | raise RuntimeError('the stack is empty') 36 | return self.minstack[-1] 37 | 38 | 39 | if __name__ == '__main__': 40 | stack = MyStack() 41 | stack.push(3) 42 | print(stack.get_min()) 43 | stack.push(4) 44 | print(stack.get_min()) 45 | stack.push(1) 46 | print(stack.get_min()) 47 | stack.pop() 48 | print(stack.get_min()) -------------------------------------------------------------------------------- /stackandqueue/q10.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定数组arr和整数num,共返回有多少个子数组满足如下情况: 3 | max(arr[i...j])-max(arr[i...j]) <= num 4 | max(arr[i...j])表示子数组arr[i..j]中的最大值,min(arr[i..j]) 5 | 表示子数组中的最小值。 6 | 7 | 要求:如果数组长度为N,实现时间复杂度为O(N)的解法。 8 | 9 | 思路:双向队列模拟动态窗口(单调队列),qmax单调递减,而qmin单调递增。 10 | 从i=0,j=0开始,j往后扩,直到不能扩为止,arr[i,j-1]肯定是满足需求的, 11 | 这时候再让i+1,再让j继续往后扩 12 | """ 13 | 14 | 15 | class MaxNumOfSubArr: 16 | @classmethod 17 | def get_num_of_sub_arr(cls, arr, num): 18 | if len(arr) == 0: 19 | return 0 20 | 21 | qmin = list() 22 | qmax = list() 23 | j = 0 24 | res = 0 25 | 26 | for index, value in enumerate(arr): 27 | while j < len(arr): 28 | while len(qmin) != 0 and arr[qmin[-1]] >= arr[j]: 29 | qmin.pop() 30 | qmin.append(j) 31 | 32 | while len(qmax) != 0 and arr[qmax[-1]] <= arr[j]: 33 | qmax.pop() 34 | qmax.append(j) 35 | 36 | if arr[qmax[0]] - arr[qmin[0]] > num: 37 | break 38 | j += 1 39 | 40 | if len(qmin) > 0 and qmin[0] == index: 41 | qmin.pop(0) 42 | if len(qmax) > 0 and qmax[0] == index: 43 | qmax.pop(0) 44 | res += (j - index) 45 | 46 | return res 47 | 48 | 49 | if __name__ == '__main__': 50 | cur_num = 5 51 | cur_arr = [2, 4, 1, 9, 5] 52 | print(MaxNumOfSubArr.get_num_of_sub_arr(cur_arr, cur_num)) -------------------------------------------------------------------------------- /stackandqueue/q2.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:编写一个类,用两个栈实现一个队列,要求: 3 | 支持队列的基本操作,add、poll和peek 4 | 5 | 思路:使用两个栈模拟队列操作 6 | """ 7 | 8 | 9 | class StackQueue: 10 | def __init__(self): 11 | self.stack_in = list() 12 | self.stack_out = list() 13 | 14 | def add(self, value): 15 | self.stack_in.append(value) 16 | 17 | def poll(self): 18 | if len(self.stack_out) == 0: 19 | if len(self.stack_in) == 0: 20 | raise RuntimeError('the queue is empty') 21 | while len(self.stack_in) > 0: 22 | self.stack_out.append(self.stack_in.pop()) 23 | return self.stack_out.pop() 24 | 25 | def peek(self): 26 | if len(self.stack_out) == 0: 27 | if len(self.stack_in) == 0: 28 | raise RuntimeError('the queue is empty') 29 | while len(self.stack_in) > 0: 30 | self.stack_out.append(self.stack_in.pop()) 31 | return self.stack_out[-1] 32 | 33 | if __name__ == '__main__': 34 | queue = StackQueue() 35 | queue.add(1) 36 | queue.add(2) 37 | queue.add(3) 38 | print(queue.peek()) 39 | print(queue.poll()) 40 | print(queue.peek()) 41 | print(queue.poll()) 42 | print(queue.peek()) 43 | print(queue.poll()) 44 | -------------------------------------------------------------------------------- /stackandqueue/q3.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:一个栈依次压入1、2、3、4、5,那么从栈顶到栈底分别为5、4、3、2、1。将这个栈 3 | 转置后,从栈顶到栈底为1、2、3、4、5,也就是实现栈中元素的逆序,要求: 4 | 只能用递归函数来实现和初始栈,不能使用其他数据结构, 5 | 6 | 思路:首先使用一个递归函数获取到栈底元素,再构造另外一个递归函数来进行reverse操作 7 | """ 8 | 9 | 10 | class ReverseTool: 11 | @classmethod 12 | def get_stack_bottom(cls, stack): 13 | i = stack.pop() 14 | if len(stack) == 0: 15 | return i 16 | else: 17 | last = cls.get_stack_bottom(stack) 18 | stack.append(i) 19 | return last 20 | 21 | @classmethod 22 | def reverse(cls, stack): 23 | if len(stack) == 0: 24 | return 25 | else: 26 | i = cls.get_stack_bottom(stack) 27 | cls.reverse(stack) 28 | stack.append(i) 29 | 30 | if __name__ == '__main__': 31 | my_stack = [1, 2, 3] 32 | ReverseTool.reverse(my_stack) 33 | print(my_stack) 34 | -------------------------------------------------------------------------------- /stackandqueue/q5.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:一个栈中元素的类型为整型,现在想将该栈从顶到底按从大到小的顺序排序,只许 3 | 额外申请一个栈。除此之外,可以申请新的变量,但不能申请额外的数据结构。如何完成排序? 4 | 5 | 思路:将要排序的栈记为stack,申请的辅助栈记为help。在stack上执行pop操作,弹出的 6 | 元素记为cur. 7 | 1)如果cur小于或等于help的栈顶元素,则将cur直接压入help 8 | 2)如果cur大于help的栈顶元素,则将help的元素逐一弹出,逐一压入stack,直到cur小于 9 | 或等于help的栈顶元素,再将cur压入help 10 | 3)将help的所有元素压入stack 11 | """ 12 | 13 | 14 | class SortedStack: 15 | def __init__(self): 16 | self.stack = list() 17 | self.help = list() 18 | 19 | def push(self, value): 20 | self.stack.append(value) 21 | 22 | def sort_stack(self): 23 | if len(self.stack) == 0: 24 | return self.stack 25 | while len(self.stack) > 0: 26 | cur = self.stack.pop() 27 | while len(self.help) > 0 and cur > self.help[-1]: 28 | help_top = self.help.pop() 29 | self.stack.append(help_top) 30 | self.help.append(cur) 31 | 32 | while len(self.help) > 0: 33 | self.stack.append(self.help.pop()) 34 | 35 | if __name__ == '__main__': 36 | sortStack = SortedStack() 37 | sortStack.push(3) 38 | sortStack.push(1) 39 | sortStack.push(6) 40 | sortStack.push(2) 41 | sortStack.push(5) 42 | sortStack.push(4) 43 | 44 | sortStack.sort_stack() 45 | print(sortStack.stack) -------------------------------------------------------------------------------- /stackandqueue/q7.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:有一个整型数组arr和一个大小为w的窗口从数组的最左边滑到最右边,窗口每次向 3 | 右边滑一个位置。例如,数组为[4,3,5,4,3,3,6,7],窗口大小为3时: 4 | [4 3 5] 4 3 3 6 7 窗口中最大值为5 5 | 4 [3 5 4] 3 3 6 7 窗口中最大值为5 6 | 4 3 [5 4 3] 3 6 7 窗口中最大值为5 7 | 4 3 5 [4 3 3] 6 7 窗口中最大值为4 8 | 4 3 5 4 [3 3 6] 7 窗口中最大值为6 9 | 4 3 5 4 3 [3 6 7] 窗口中最大值为7 10 | 11 | 如果数组长度为n,窗口大小为w,则一共产生 n-w+1 个窗口的最大值。 12 | 13 | 请实现一个函数,要求: 14 | 1)输入整型数组arr,窗口大小为w 15 | 2)输出一个长度为 n-w+1 的数组res,res[i]表示每一种窗口状态下的最大值。以本题为例, 16 | 结果应该返回[5, 5, 5, 4, 6, 7] 17 | 18 | 思路:借助双端队列来保存上一个滑动窗口的值状态 19 | """ 20 | 21 | 22 | class MaxValueOFWindow: 23 | @classmethod 24 | def get_max_values(cls, arr, window): 25 | if len(arr) == 0 or window < 1 or len(arr) < window: 26 | return None 27 | 28 | index = 0 29 | deque = list() 30 | max_values = list() # len(max_values) = len(arr) - window + 1 31 | 32 | while index < len(arr): 33 | while len(deque) > 0 and arr[deque[-1]] <= arr[index]: 34 | deque.pop() 35 | deque.append(index) 36 | 37 | if deque[-1] - deque[0] == window: 38 | deque.pop(0) 39 | 40 | if index >= window - 1: 41 | max_values.append(arr[deque[0]]) 42 | 43 | index += 1 44 | 45 | return max_values 46 | 47 | if __name__ == '__main__': 48 | res = MaxValueOFWindow.get_max_values([4, 3, 5, 4, 3, 3, 6, 7], 3) 49 | print(res) 50 | 51 | -------------------------------------------------------------------------------- /stackandqueue/丑数问题.py: -------------------------------------------------------------------------------- 1 | """ 2 | 编写一个程序,找出第 n 个丑数。 3 | 丑数就是只包含质因数 2, 3, 5 的正整数。 4 | 5 | 示例: 6 | n = 10 => 12 7 | 解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。 8 | 说明: 9 | 1 是丑数。 10 | n 不超过1690。 11 | """ 12 | 13 | 14 | class Solution: 15 | def nthUglyNumber(self, n): 16 | """ 17 | :type n: int 18 | :rtype: int 19 | """ 20 | if n < 1: 21 | return 0 22 | 23 | if n == 1: 24 | return 1 25 | 26 | l1 = [2] 27 | l2 = [3] 28 | l3 = [5] 29 | 30 | cur = 1 31 | count = 1 32 | 33 | s1 = {2} 34 | s2 = {3} 35 | 36 | while count < n: 37 | cur = min([l1[0], l2[0], l3[0]]) 38 | if cur == l1[0]: 39 | l1.pop(0) 40 | if cur * 2 not in s1: 41 | s1.add(cur * 2) 42 | l1.append(cur * 2) 43 | if cur * 3 not in s1: 44 | s1.add(cur * 3) 45 | l1.append(cur * 3) 46 | if cur * 5 not in s1: 47 | s1.add(cur * 5) 48 | l1.append(cur * 5) 49 | l1.sort() 50 | elif cur == l2[0]: 51 | l2.pop(0) 52 | if cur * 3 not in s2: 53 | s2.add(cur * 3) 54 | l2.append(cur * 3) 55 | if cur * 5 not in s2: 56 | s2.add(cur * 5) 57 | l2.append(cur * 5) 58 | 59 | l2.sort() 60 | elif cur == l3[0]: 61 | l3.pop(0) 62 | l3.append(cur * 5) 63 | count += 1 64 | 65 | return cur 66 | -------------------------------------------------------------------------------- /stackandqueue/括号匹配问题.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。 3 | 4 | 有效字符串需满足: 5 | 1.左括号必须用相同类型的右括号闭合。 6 | 2.左括号必须以正确的顺序闭合。 7 | 3.注意空字符串可被认为是有效字符串。 8 | 9 | 比如: "()" => true "()[]{}" => true "([)]" => false "{[]}" => true 10 | """ 11 | 12 | 13 | class Solution: 14 | def isValid(self, s): 15 | if not s: 16 | return True 17 | 18 | if len(s) & 1 == 1: 19 | return False 20 | 21 | sign_map = { 22 | '}': '{', 23 | ']': '[', 24 | ')': '(' 25 | } 26 | 27 | help_stack = list() 28 | index = 0 29 | while index < len(s): 30 | if s[index] in sign_map.values(): 31 | help_stack.append(s[index]) 32 | else: 33 | if not help_stack: 34 | return False 35 | tmp = help_stack.pop() 36 | if tmp != sign_map.get(s[index]): 37 | return False 38 | index += 1 39 | 40 | return True if not help_stack else False 41 | -------------------------------------------------------------------------------- /string/q1.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定两个字符串str1和str2,如果str1和str2中出现的字符种类一样且每种字符出现 3 | 的次数也一样,那么str1和str2互为变形词。请实现函数判断两个字符串是否互为变形词。 4 | 5 | 举例: 6 | str1='123', str2='231',返回true 7 | str1='123', str2='2331',返回false 8 | """ 9 | 10 | 11 | class Words: 12 | @classmethod 13 | def is_similar_words(cls, str1, str2): 14 | if len(str1) != len(str2): 15 | return False 16 | 17 | my_dict = dict() 18 | for i in str1: 19 | if i not in my_dict: 20 | my_dict[i] = 1 21 | else: 22 | my_dict[i] += 1 23 | 24 | for j in str2: 25 | if j not in my_dict: 26 | return False 27 | else: 28 | my_dict[j] -= 1 29 | if my_dict[j] < 0: 30 | return False 31 | 32 | return True 33 | 34 | 35 | if __name__ == '__main__': 36 | A = "abcabcabc" 37 | B = "bcacbaacb" 38 | print(Words.is_similar_words(A, B)) -------------------------------------------------------------------------------- /string/q16.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个整数N,求由"0"字符与"1"字符组成的长度为N的所有字符串中,满足"0"字符的左边 3 | 必有"1"字符的字符串数量。 4 | 5 | 举例: 6 | N=1。只由"0"和"1"组成,长度为1的所有字符串:"0"、"1"。只有字符串"1"满足要求,所以 7 | 返回1。 8 | N=2。只由"0"和"1"组成,长度为2的所有字符串为:"00"、"01"、"10"、"11"。只有字符串 9 | "10"和"11"满足要求,所以返回2。 10 | N=3。只由"0"和"1"组成,长度为3的所有字符串为:"000"、"001"、"010"、"011"、"100"、 11 | "101"、"110"、"111"。字符串"101"、"110"、"111"满足要求,所以返回3。 12 | """ 13 | 14 | 15 | class BinaryStrHanlder: 16 | @classmethod 17 | def get_num_by_recurise(cls, n): 18 | if not n: 19 | return 0 20 | return cls.process(1, n) 21 | 22 | @classmethod 23 | def process(cls, i, n): 24 | if i == n: 25 | return 1 26 | elif i == n - 1: 27 | return 2 28 | else: 29 | return cls.process(i+1, n) + cls.process(i+2, n) 30 | 31 | @classmethod 32 | def get_num(cls, n): 33 | if n < 3: 34 | return n 35 | i = 3 36 | pre_pre_total = 1 37 | pre_total = 2 38 | total = pre_pre_total + pre_total 39 | while i < n: 40 | tmp = total 41 | pre_pre_total = pre_total 42 | pre_total = tmp 43 | total = pre_total + pre_pre_total 44 | i += 1 45 | 46 | return total 47 | 48 | 49 | if __name__ == '__main__': 50 | for i in range(20): 51 | print(BinaryStrHanlder.get_num_by_recurise(i)) 52 | print(BinaryStrHanlder.get_num(i)) -------------------------------------------------------------------------------- /string/q18.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定一个字符串str,返回str的最长无重复字符子串的长度。 3 | 4 | 举例: 5 | str="abcd",返回4 6 | str="aabcb",最长无重复字符子串为"abc",返回3 7 | 8 | 要求: 9 | 如果str的长度为N,请实现时间复杂度为O(N)的方法。 10 | """ 11 | 12 | 13 | class LongestNotRepeatStr: 14 | @classmethod 15 | def get_longest_sut_str(cls, strs): 16 | if not strs: 17 | return '' 18 | 19 | pre = -1 20 | max_length = 0 21 | str_map = {i: -1 for i in strs} 22 | 23 | i = 0 24 | while i < len(strs): 25 | pre = max([pre, str_map[strs[i]]]) 26 | cur_length = i - pre 27 | max_length = max([max_length, cur_length]) 28 | str_map[strs[i]] = i 29 | i += 1 30 | 31 | return max_length 32 | 33 | 34 | if __name__ == '__main__': 35 | my_str = 'kqetrpslqrpbbdmjvjba' 36 | print(LongestNotRepeatStr.get_longest_sut_str(my_str)) -------------------------------------------------------------------------------- /string/q19.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:新型字符的定义如下: 3 | 1.新型字符是长度为1或者2的字符串 4 | 2.表现形式可以仅是小写字母,例如“e”;也可以是大写字母+小写字母,例如 5 | “Ab”;还可以是大写字母+大写字母,例如,“DC”。 6 | 现在给定一个字符串str,str一定是若干新型字符正确组合的结果,比如"eaCCBi", 7 | 由新型字符"e","a","CC"和"Bi"组成。再给定一个整数k,代表str中的位置。请 8 | 返回被k位置指中的新类型字符。 9 | 10 | 举例: 11 | str="aaABCDEcBCg"。 12 | 1. k=7时,返回"Ec" 13 | 2. k=4时,返回"CD" 14 | 3. k=10时,返回"g" 15 | """ 16 | 17 | 18 | class NewTypeStr: 19 | @classmethod 20 | def get_new_type_str(cls, strs, k): 21 | if not strs: 22 | return '' 23 | if k >= len(strs) or k < 0: 24 | return '' 25 | 26 | count = 0 27 | i = k - 1 28 | while i >= 0: 29 | if strs[i].isupper(): 30 | count += 1 31 | i -= 1 32 | else: 33 | break 34 | if count % 2 == 1: 35 | return strs[k-1:k+1] 36 | 37 | if strs[k].isupper(): 38 | return strs[k:k+2] 39 | else: 40 | return strs[k] 41 | 42 | 43 | if __name__ == '__main__': 44 | test_str = 'aaABCDEcBCg' 45 | print(NewTypeStr.get_new_type_str(test_str, 10)) 46 | print(NewTypeStr.get_new_type_str(test_str, 4)) 47 | print(NewTypeStr.get_new_type_str(test_str, 7)) -------------------------------------------------------------------------------- /string/q21.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定一个字符串str,返回把str全部切成回文子串的最小分割数。 3 | 举例: 4 | str="ABA" 5 | 不需要切割,str本身就是回文串。所以返回0. 6 | str="ACDCDCDAD" 7 | 最少需要切2次变成3个回文子串,比如"A"、"CDCDC"和"DAD",所以返回2. 8 | """ 9 | import sys 10 | 11 | 12 | class PalindromeMincut: 13 | @classmethod 14 | def get_min_cut(cls, strs): 15 | if not strs: 16 | return 0 17 | 18 | length = len(strs) 19 | dp = [0 for _ in range(length+1)] 20 | dp[length] = -1 21 | is_palindrome = [[False for _ in range(length)] for _ in range(length)] 22 | 23 | i = length - 1 24 | while i >= 0: 25 | dp[i] = sys.maxsize 26 | j = i 27 | while j < length: 28 | if strs[i] == strs[j] and (j - i < 2 or is_palindrome[i+1][j-1]): 29 | dp[i] = min([dp[i], dp[j+1]+1]) 30 | is_palindrome[i][j] = True 31 | j += 1 32 | i -= 1 33 | 34 | return dp[0] 35 | 36 | 37 | if __name__ == '__main__': 38 | strs = 'CCDBABDB' 39 | print(PalindromeMincut.get_min_cut(strs)) -------------------------------------------------------------------------------- /string/q3.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定一个字符串str和一个整数k,如果str中正好有连续的K个'0'字符出现时, 3 | 把连续的'0'字符去除,返回处理后的字符串。 4 | 5 | 举例: 6 | str='A00B',k=2, 返回'AB' 7 | str='0000A0000B00C000D0000',k=4,返回'AB00C000D' 8 | """ 9 | 10 | 11 | class SubStr: 12 | @classmethod 13 | def get_sub_str(cls, str1, k): 14 | i = 0 15 | length = len(str1) 16 | count = 0 17 | while i < length: 18 | temp = i 19 | while temp < length and str1[temp] == '0': 20 | count += 1 21 | temp += 1 22 | temp = i 23 | i += count 24 | if count == k: 25 | str1 = str1[0:temp]+'~'*k+str1[temp+k:] 26 | i += 1 27 | count = 0 28 | 29 | return str1 30 | 31 | 32 | if __name__ == '__main__': 33 | print(SubStr.get_sub_str('A00B', 2)) 34 | test4 = "0000A0000B00C000D0000" 35 | print(SubStr.get_sub_str(test4, 4)) -------------------------------------------------------------------------------- /string/q4.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个字符串str,把字符串str前面任意的部分挪到后面形成的字符串叫做str 3 | 的旋转词。比如 str='12345',str的旋转词有'12345','23451','34512'、 4 | '45123'和'51234'。给定两个字符串a和b,请判断a和b是否互为旋转词。 5 | 6 | 举例: 7 | a = 'abcd', b = 'cdab',返回True 8 | a = '1ab2', b = 'ab12', 返回False 9 | 10 | 要求: 11 | 如果a和b长度不一样,那么a和b必然不是旋转词,可以直接返回False。当a和b长度 12 | 一样,都为N时,要求解法的时间复杂度为O(N)。 13 | """ 14 | 15 | 16 | class RotationStr: 17 | @classmethod 18 | def is_rotation(cls, str1, str2): 19 | if len(str1) != len(str2): 20 | return False 21 | 22 | str1 = str1 * 2 23 | # 这里如果不用系统调用,那么就用KMP 24 | if str2 not in str1: 25 | return False 26 | return True 27 | 28 | 29 | if __name__ == '__main__': 30 | str1 = 'abcdefg123' 31 | str2 = '123abcdefg' 32 | print(RotationStr.is_rotation(str1, str2)) -------------------------------------------------------------------------------- /string/q5.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个字符串str,如果str符合日常书写的整数形式,并且属于32位整数的范围, 3 | 返回str所代表的整数值,否则返回0. 4 | 5 | 举例: 6 | str = '123',返回123 7 | str = '023',返回0 8 | str = 'A13',返回0 9 | str = '0',返回0 10 | str = '2147483647',返回2147483647 11 | str = '2147483648',返回0 12 | str = '-123',返回-123 13 | """ 14 | 15 | 16 | class IntegerGenerater: 17 | @classmethod 18 | def get_integer(cls, str1): 19 | if not str1 or str1[0] not in ['-', '1', '2', '3', '4', '5', '6', '7', '8', '9']: 20 | return 0 21 | 22 | if len(str1) == 1: 23 | if str1 == '-': 24 | return 0 25 | return str1 26 | 27 | for i in str1[1:]: 28 | if not i.isdigit(): 29 | return 0 30 | 31 | if int(str1) >= 2**31 or int(str1) < -2**31: 32 | return 0 33 | 34 | return int(str1) 35 | 36 | 37 | if __name__ == '__main__': 38 | print(IntegerGenerater.get_integer('2147483647')) 39 | print(IntegerGenerater.get_integer('-2147483648')) 40 | print(IntegerGenerater.get_integer('2147483648')) 41 | print(IntegerGenerater.get_integer('-2147483649')) 42 | print(IntegerGenerater.get_integer('-123')) -------------------------------------------------------------------------------- /string/q6.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定三个字符串str、frome和to,已知from字符串中无重复字符,把str中 3 | 所有from的子串全部替换成to字符串,对连续出现from的部分要求只替换成一个to字符串, 4 | 返回最终的结果字符串。 5 | 6 | 举例: 7 | str='123abc',from='abc',to='4567',返回'1234567' 8 | str='123',from='abc',to='456',返回'123' 9 | str='123abcabc', from='abc',to='X',返回'123X' 10 | """ 11 | 12 | 13 | class StrReplacer: 14 | @classmethod 15 | def replace_str(cls, str1, str_from, str_to): 16 | if not str_from or not str1 or str_from not in str1: 17 | return str1 18 | 19 | length = len(str1) 20 | length_from = len(str_from) 21 | i = 0 22 | pre_happend = 0 23 | res = list() 24 | while i < length: 25 | count = 0 26 | while count < length_from and i+count < length and str1[i+count] == str_from[count]: 27 | count += 1 28 | # 这里判断是否完全匹配,不完全匹配则将下标右移一位 29 | if count != length_from: 30 | pre_happend = 0 31 | res.append(str1[i]) 32 | i += 1 33 | else: 34 | i += count 35 | if pre_happend: 36 | pass 37 | else: 38 | res.append(str_to) 39 | pre_happend = 1 40 | 41 | return ''.join(res) 42 | 43 | 44 | if __name__ == '__main__': 45 | str1 = "abc1abcabc1234abcabcabc5678" 46 | str1_from = "abc" 47 | str1_to = "XXXXX" 48 | print(StrReplacer.replace_str(str1, str1_from, str1_to)) 49 | 50 | str2 = "abc" 51 | str2_from = "123" 52 | str2_to = "X" 53 | print(StrReplacer.replace_str(str2, str2_from, str2_to)) -------------------------------------------------------------------------------- /string/q7.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定一个字符串,返回它的统计字符串。比如,"aaabbadddffc"的统计字符串为 3 | "a_3_b_2_a_1_d_3_f_2_c_1". 4 | 5 | 补充题目:给定一个字符串的统计字符串cstr,再给定一个整数index,返回cstr所代表的原始 6 | 字符串上的第index个字符串。例如,"a_1_b_100"所代表的原始字符串上的第0个字符串是'a', 7 | 第50个字符串是'b'. 8 | """ 9 | 10 | 11 | class StrStatistics: 12 | @classmethod 13 | def statistics_str(cls, str1): 14 | if not str1: 15 | return str1 16 | 17 | length = len(str1) 18 | i = 0 19 | res = list() 20 | while i < length: 21 | count = 0 22 | while i + count < length and str1[i+count] == str1[i]: 23 | count += 1 24 | res.append('{}_{}'.format(str1[i], count)) 25 | i += count 26 | 27 | return '_'.join(res) 28 | 29 | @classmethod 30 | def get_char(cls, cstr, k): 31 | if not cstr: 32 | return 33 | 34 | res = cstr.split('_') 35 | index_sum = 0 36 | i = 1 37 | while index_sum < k: 38 | if i > len(res): 39 | return '' 40 | index_sum += int(res[i]) 41 | i += 2 42 | 43 | return res[i-1] 44 | 45 | 46 | if __name__ == '__main__': 47 | res = StrStatistics.statistics_str('aaabbadddffc') 48 | print(StrStatistics.get_char(res, 9)) -------------------------------------------------------------------------------- /string/q8.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定一个字符类型数组chas[],判断chas中是否所有字符都只出现过一次,请根据下面 3 | 两种不同的要求实现两个函数。 4 | 5 | 举例: 6 | chas=['a', 'b', 'c'],返回True;chas=['1', '2', '1'],返回False 7 | 8 | 要求: 9 | 1.实现时间复杂度为O(N)的方法。 10 | 2.在保证额外空间复杂度为O(1)的前提下,请实现时间复杂度尽量低的方法。 11 | """ 12 | 13 | 14 | from basic_algrithms.sort_algrithms import heap_sort 15 | 16 | 17 | class CharHanlder: 18 | @classmethod 19 | def handle_meth_1(cls, chas): 20 | if not chas: 21 | return True 22 | 23 | res = list() 24 | for i in chas: 25 | if i not in res: 26 | res.append(i) 27 | else: 28 | return False 29 | 30 | return True 31 | 32 | @classmethod 33 | def handle_meth_2(cls, chas): 34 | if not chas: 35 | return True 36 | 37 | heap_sort.HeapSort.heap_sort(chas) 38 | pre = '' 39 | for i in chas: 40 | if pre == i: 41 | return False 42 | pre = i 43 | 44 | return True 45 | 46 | 47 | if __name__ == '__main__': 48 | test_str = ['1', '2', '3', 'a', 'b', 'c', 'd', '4', '5', '6'] 49 | print(CharHanlder.handle_meth_1(test_str)) 50 | print(CharHanlder.handle_meth_2(test_str)) 51 | test_str2 = ['1', '2', '3', 'a', 'b', 'c', 'd', '4', 'a', '6'] 52 | print(CharHanlder.handle_meth_1(test_str2)) 53 | print(CharHanlder.handle_meth_2(test_str2)) -------------------------------------------------------------------------------- /string/两种排序方法.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述: 考拉有n个字符串字符串,任意两个字符串长度都是不同的。考拉最近学习到有两种字符串的排序方法: 3 | 1.根据字符串的字典序排序。例如: 4 | "car" < "carriage" < "cats" < "doggies < "koala" 5 | 2.根据字符串的长度排序。例如: 6 | "car" < "cats" < "koala" < "doggies" < "carriage" 7 | 考拉想知道自己的这些字符串排列顺序是否满足这两种排序方法,考拉要忙着吃树叶,所以需要你来帮忙验证。 8 | 输入描述: 9 | 输入第一行为字符串个数n(n ≤ 100) 10 | 接下来的n行,每行一个字符串,字符串长度均小于100,均由小写字母组成 11 | 12 | 输出描述: 13 | 如果这些字符串是根据字典序排列而不是根据长度排列输出"lexicographically", 14 | 如果根据长度排列而不是字典序排列输出"lengths", 15 | 如果两种方式都符合输出"both",否则输出"none" 16 | 17 | 输入例子1: 18 | 3 19 | a 20 | aa 21 | bbb 22 | 23 | 输出例子1: 24 | both 25 | """ 26 | 27 | 28 | import sys 29 | 30 | 31 | class Solution: 32 | def get_satisfied_methods(self, arr): 33 | if len(arr) <= 1: 34 | print('both') 35 | return 36 | 37 | by_length = True 38 | by_lex = True 39 | index = 1 40 | pre = arr[0] 41 | while index < len(arr): 42 | if len(arr[index]) < len(pre): 43 | by_length = False 44 | 45 | if arr[index] < pre: 46 | by_lex = False 47 | 48 | if not by_length and not by_lex: 49 | break 50 | pre = arr[index] 51 | index += 1 52 | 53 | if by_length and by_lex: 54 | print('both') 55 | return 56 | elif by_length: 57 | print('lengths') 58 | elif by_lex: 59 | print('lexicographically') 60 | else: 61 | print('none') 62 | 63 | 64 | if __name__ == '__main__': 65 | n = int(sys.stdin.readline().strip()) 66 | inputs = list() 67 | for _ in range(n): 68 | inputs.append(sys.stdin.readline().strip()) 69 | s = Solution() 70 | s.get_satisfied_methods(inputs) -------------------------------------------------------------------------------- /string/单词拆分.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题: 3 | 给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。 4 | 5 | 说明: 6 | 拆分时可以重复使用字典中的单词。 7 | 你可以假设字典中没有重复的单词。 8 | 9 | 示例: 10 | s = "leetcode", wordDict = ["leet", "code"] => true 11 | 解释: 返回 true 因为 "leetcode" 可以被拆分成 "leet code"。 12 | """ 13 | 14 | 15 | class Solution(object): 16 | def wordBreak(self, s, wordDict): 17 | """记忆化搜索,也可以改写成动态规划""" 18 | if not s: 19 | return False 20 | 21 | maps = dict() 22 | # 小优化 23 | wordDict = set(wordDict) 24 | return self.process(s, 0, wordDict, maps) 25 | 26 | def process(self, s, index, wordDict, maps): 27 | if maps.get(index) is not None: 28 | return maps.get(index) 29 | 30 | if index > len(s): 31 | return False 32 | 33 | if index == len(s): 34 | return True 35 | 36 | length = 1 37 | while index + length <= len(s): 38 | if maps.get(index + length) is not None: 39 | res = maps.get(index + length) 40 | if res: 41 | return True 42 | else: 43 | if s[index:index + length] in wordDict: 44 | res = self.process(s, index + length, wordDict, maps) 45 | if res == True: 46 | return res 47 | else: 48 | maps[index + length] = res 49 | length += 1 50 | return False -------------------------------------------------------------------------------- /string/合法括号生成.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题: 给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。 3 | 4 | 例如,给出 n = 3,生成结果为: 5 | ["((()))", "(()())", "(())()", "()(())", "()()()"] 6 | """ 7 | 8 | 9 | class Solution: 10 | def generateParenthesis(self, n): 11 | """ 12 | :type n: int 13 | :rtype: List[str] 14 | """ 15 | if n == 0: 16 | return list() 17 | 18 | res = list() 19 | self.process(n, n, '', res) 20 | return res 21 | 22 | def process(self, left, right, cur_str, res): 23 | if left == 0 and right == 0: 24 | res.append(cur_str) 25 | elif left == 0 and right != 0: 26 | self.process(left, right - 1, cur_str + ')', res) 27 | elif left != 0 and right == 0: 28 | return 29 | elif left < 0 or right < 0: 30 | return 31 | elif right == left: 32 | self.process(left - 1, right, cur_str + '(', res) 33 | elif right > left: 34 | self.process(left - 1, right, cur_str + '(', res) 35 | self.process(left, right - 1, cur_str + ')', res) 36 | 37 | 38 | -------------------------------------------------------------------------------- /分类代表题目/TOPK及第K个相关问题/查找数组中第k大的元素.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题: 在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素, 3 | 而不是第 k 个不同的元素。 4 | 5 | 示例 1: 6 | 输入: [3,2,1,5,6,4] 和 k = 2 7 | 输出: 5 8 | """ 9 | 10 | 11 | class Solution: 12 | def findKthLargest(self, nums, k): 13 | """ 14 | :type nums: List[int] 15 | :type k: int 16 | :rtype: int 17 | """ 18 | 19 | return self.partition(nums, 0, len(nums) - 1, len(nums) - k) 20 | 21 | def partition(self, nums, start, end, k): 22 | cur = start 23 | left = start - 1 24 | right = end 25 | while cur < right: 26 | if nums[cur] < nums[end]: 27 | left += 1 28 | nums[cur], nums[left] = nums[left], nums[cur] 29 | cur += 1 30 | elif nums[cur] == nums[end]: 31 | cur += 1 32 | else: 33 | right -= 1 34 | nums[cur], nums[right] = nums[right], nums[cur] 35 | 36 | nums[end], nums[right] = nums[right], nums[end] 37 | if k <= left: 38 | return self.partition(nums, start, left, k) 39 | elif left < k <= right: 40 | return nums[right] 41 | else: 42 | return self.partition(nums, right + 1, end, k) 43 | 44 | 45 | if __name__ == '__main__': 46 | arr = [3, 1, 2, 4] 47 | s = Solution() 48 | print(s.findKthLargest(arr, 2)) -------------------------------------------------------------------------------- /分类代表题目/二分查找/x的平方根.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题: 实现 int sqrt(int x) 函数。计算并返回 x 的平方根,其中 x 是非负整数。 3 | 由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。 4 | 5 | 示例: 6 | 8 => 2 7 | 8 的平方根是 2.82842...,由于返回类型是整数,小数部分将被舍去。 8 | """ 9 | 10 | 11 | class Solution: 12 | def mySqrt(self, x): 13 | """ 14 | :type x: int 15 | :rtype: int 16 | """ 17 | if x <= 1: 18 | return x 19 | 20 | num = 2 21 | start = 0 22 | end = 2 23 | while True: 24 | if x < num ** 2: 25 | num = (start + end) >> 1 26 | end = num 27 | elif num ** 2 <= x < (num + 1) ** 2: 28 | return num 29 | elif (num + 1) ** 2 == x: 30 | return num + 1 31 | elif x > (num + 1) ** 2: 32 | start = num + 1 33 | num *= 2 34 | end = num 35 | 36 | 37 | if __name__ == '__main__': 38 | solution = Solution() 39 | print(solution.mySqrt(8192)) -------------------------------------------------------------------------------- /分类代表题目/二分查找/不修改数组找出重复的数字.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述: 在一个长为n+1的数组里的所有数字都在1~n的范围内,所以数组中至少 3 | 有一个数字是重复的,找出任意一个重复的数字即可,但是不能改变输入的数组,要求 4 | 空间复杂度为O(1) 5 | """ 6 | 7 | 8 | class Solution: 9 | def get_duplicate_num(self, arr): 10 | start = 1 11 | end = len(arr) - 1 12 | while start <= end: 13 | mid = ((end - start) >> 1) + start 14 | num = self.count(arr, start, mid) 15 | if start == end: 16 | if num > 1: 17 | return start 18 | 19 | if num > (mid-start+1): 20 | end = mid 21 | else: 22 | start = mid + 1 23 | 24 | return None 25 | 26 | def count(self, arr, start, end): 27 | count = 0 28 | for i in arr: 29 | if start <= i <= end: 30 | count += 1 31 | 32 | return count 33 | 34 | 35 | if __name__ == '__main__': 36 | solution = Solution() 37 | array = [2, 3, 5, 4, 3, 2, 6, 7] 38 | r = solution.get_duplicate_num(array) 39 | print(r) 40 | 41 | array = [1, 2, 3, 4] 42 | r = solution.get_duplicate_num(array) 43 | print(r) -------------------------------------------------------------------------------- /分类代表题目/二分查找/搜索插入位置.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述: 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中, 3 | 返回它将会被按顺序插入的位置。 4 | 5 | 例子: [1,3,5,6], 5 => 2 [1,3,5,6], 2 => 1 [1,3,5,6], 7 => 4 6 | """ 7 | 8 | 9 | class Solution: 10 | def searchInsert(self, nums, target): 11 | if not nums: 12 | return 0 13 | 14 | start = 0 15 | end = len(nums) - 1 16 | while start <= end: 17 | mid = (start + end) >> 1 18 | if nums[mid] == target: 19 | return mid 20 | elif mid == 0: 21 | if nums[mid] < target: 22 | start = mid + 1 23 | else: 24 | return 0 25 | elif mid == len(nums) - 1: 26 | if nums[mid] > target: 27 | return mid 28 | else: 29 | return mid + 1 30 | elif mid - 1 >= 0 and nums[mid - 1] < target < nums[mid]: 31 | return mid 32 | elif mid - 1 >= 0 and nums[mid - 1] == target: 33 | return mid - 1 34 | elif mid + 1 < len(nums) and nums[mid] < target < nums[mid + 1]: 35 | return mid + 1 36 | elif mid + 1 == len(nums) - 1 and nums[mid+1] == target: 37 | return mid + 1 38 | elif nums[mid] > target: 39 | end = mid - 1 40 | elif nums[mid] < target: 41 | start = mid + 1 42 | 43 | if start > end: 44 | return start 45 | 46 | 47 | if __name__ == '__main__': 48 | solution = Solution() 49 | print(solution.searchInsert([1, 3, 5], 5)) -------------------------------------------------------------------------------- /分类代表题目/二分查找/苹果堆.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述: 有n堆苹果,每堆a[i]个,打印出在m次查询中,每次查询 3 | 对应的苹果位于哪个堆中。比如 4 | 5 | 输入: 6 | 3 7 | 2 5 10 8 | 2 9 | 12 5 10 | 11 | 输出: 12 | 3 13 | 2 14 | 15 | """ 16 | 17 | 18 | import sys 19 | 20 | 21 | class Solution: 22 | def get_apple_pos(self, apples, queries): 23 | heaps = self.pre_process(apples) 24 | for query in queries: 25 | self.find_pos_by_num(heaps, query) 26 | 27 | def find_pos_by_num(self, heaps, query): 28 | start = 0 29 | end = len(heaps) - 1 30 | while True: 31 | index = (start + end) >> 1 32 | if heaps[index][0] <= query <= heaps[index][1]: 33 | print(index + 1) 34 | return 35 | elif query > heaps[index][1]: 36 | start = index + 1 37 | else: 38 | end = index - 1 39 | 40 | def pre_process(self, apples): 41 | heaps = list() 42 | left = 0 43 | right = 0 44 | for i in apples: 45 | right += i 46 | heaps.append((left, right)) 47 | left = right + 1 48 | return heaps 49 | 50 | 51 | if __name__ == '__main__': 52 | n = sys.stdin.readline() 53 | ap_arr = list(map(int, sys.stdin.readline().split())) 54 | m = sys.stdin.readline() 55 | q_arr = list(map(int, sys.stdin.readline().split())) 56 | 57 | solution = Solution() 58 | solution.get_apple_pos(ap_arr, q_arr) 59 | -------------------------------------------------------------------------------- /分类代表题目/位运算/重复数组中找出唯一一对不重复的数字.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 3 | 找出只出现一次的那两个元素。 4 | 5 | 示例: 6 | [1,2,1,3,2,5] => [3,5] 7 | 8 | 要求: 9 | 时间复杂度为O(N),空间复杂度为O(1).结果输出的顺序不需要有序 10 | """ 11 | 12 | 13 | class Solution: 14 | def singleNumber(self, nums): 15 | xor = 0 16 | a = 0 17 | b = 0 18 | for i in nums: 19 | xor ^= i 20 | 21 | mask = 1 22 | while xor & mask == 0: 23 | mask = mask << 1 24 | 25 | for i in nums: 26 | if i & mask: 27 | a ^= i 28 | else: 29 | b ^= i 30 | return [a, b] 31 | 32 | 33 | if __name__ == '__main__': 34 | s = Solution() 35 | arr = [1, 2, 1, 3, 2, 5] 36 | print(s.singleNumber(arr)) 37 | -------------------------------------------------------------------------------- /分类代表题目/位运算/重复数组中找出唯一不重复的数字.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题: 3 | 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。 4 | 要求空间复杂度为O(1) 5 | 示例 1: 6 | 输入: [2,2,3,2] 7 | 输出: 3 8 | 9 | 延伸: 10 | 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了K次。找出那个只出现了1次的元素。 11 | 12 | 13 | 思路: 14 | 一个通用思路是使用位运算,除了要找的那个元素之外,每个元素出现了k次,那么将元素表示为32位整数,则这些整数 15 | 每个位置相加之后,分别对k取余,则一定能整除,所以算上剩下的那一个元素,剩下的一定是该元素。做法概括起来就是 16 | 统计每一位1的个数,然后对k取余 17 | 18 | 注意,负数做位运算会有坑。有的时候可以使用n&0x7FFFFFFF将其转换为正数 19 | """ 20 | 21 | 22 | class Solution: 23 | def singleNumber(self, arr): 24 | res = 0 25 | for i in range(32): 26 | bit_sum = 0 27 | for j in arr: 28 | bit_sum += ((j >> i) & 1) 29 | res |= ((bit_sum % 3) << i) 30 | return self.convert(res) 31 | 32 | def convert(self, num): 33 | if num >= 2 ** 31: 34 | num -= 2**32 35 | return num 36 | 37 | 38 | if __name__ == '__main__': 39 | s = Solution() 40 | array = [-2, 3, 3, 3] 41 | print(s.singleNumber(array)) -------------------------------------------------------------------------------- /分类代表题目/动态规划/分饼干.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述: 易老师购买了一盒饼干,盒子中一共有k块饼干,但是数字k有些数位变得模糊了,看不清楚数字 3 | 具体是多少了。易老师需要你帮忙把这k块饼干平分给n个小朋友,易老师保证这盒饼干能平分给n个小朋友。 4 | 现在你需要计算出k有多少种可能的数值 5 | 6 | 输入描述: 7 | 输入包括两行: 8 | 第一行为盒子上的数值k,模糊的数位用X表示,长度小于18(可能有多个模糊的数位) 9 | 第二行为小朋友的人数n 10 | 输出描述: 11 | 输出k可能的数值种数,保证至少为1 12 | 示例1 13 | 输入 14 | 9999999999999X 3 15 | 输出 16 | 4 17 | 18 | 状态转移方程: dp[i][(10*j+w)%n] += dp[i-1][j] 19 | """ 20 | 21 | 22 | import sys 23 | 24 | 25 | class Solution: 26 | def get_possible_rs(self, args, children): 27 | dp = [[0]*children for _ in range(len(args)+1)] 28 | dp[0][0] = 1 29 | for i in range(1, len(args)+1): 30 | for j in range(children): 31 | for w in range(10): 32 | if args[i-1] == 'X' or int(args[i-1]) == w: 33 | dp[i][(10*j+w) % children] += dp[i-1][j] 34 | print(dp[len(args)][0]) 35 | 36 | 37 | if __name__ == '__main__': 38 | k = sys.stdin.readline().strip() 39 | n = int(sys.stdin.readline().strip()) 40 | s = Solution() 41 | s.get_possible_rs(k, n) -------------------------------------------------------------------------------- /分类代表题目/动态规划/最长上升子序列.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述: 3 | 给定一个无序的整数数组,找到其中最长上升子序列的长度。 4 | 5 | 示例: 6 | 7 | 输入: [10,9,2,5,3,7,101,18] 8 | 输出: 4 9 | 解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。 10 | 说明: 11 | 12 | 可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。 13 | 你算法的时间复杂度应该为 O(n2) 。 14 | 进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗? 15 | """ 16 | 17 | 18 | class Solution: 19 | def lengthOfLIS(self, nums): 20 | if len(nums) <= 1: 21 | return len(nums) 22 | dp = [1] * len(nums) 23 | index = 1 24 | while index < len(nums): 25 | j = 0 26 | while j < index: 27 | if nums[j] < nums[index]: 28 | if dp[j] + 1 > dp[index]: 29 | dp[index] = dp[j] + 1 30 | j += 1 31 | index += 1 32 | 33 | return max(dp) -------------------------------------------------------------------------------- /分类代表题目/动态规划/礼物的最大值.py: -------------------------------------------------------------------------------- 1 | """ 2 | 在一个m*n的棋盘的每一1格都放有一个礼物,每个礼物都有一定的价值(价值都大于0)。 3 | 你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格,直到到达棋 4 | 盘的右下角。给定一个棋盘及其上面的礼物,请计算你最多能拿到多少价值的礼物? 5 | 6 | 比如: 7 | 1 10 3 8 8 | 12 2 9 6 9 | 5 7 4 11 10 | 3 7 16 5 11 | 12 | 最大的路线为(1,12,5,7,7,16,5),那么我们能拿到最大价值为53的礼物 13 | """ 14 | 15 | 16 | class GiftMaxValue: 17 | def get_max_value(self, matrix): 18 | if not matrix: 19 | return 0 20 | 21 | if len(matrix[0]) == 1: 22 | max_sum = 0 23 | for cols in matrix: 24 | max_sum += cols[0] 25 | return max_sum 26 | 27 | if len(matrix) == 1: 28 | return sum(matrix[0]) 29 | 30 | row_length = len(matrix) 31 | col_length = len(matrix[0]) 32 | dp = [[0 for _ in range(col_length)] for _ in range(row_length)] 33 | 34 | dp[0][0] = matrix[0][0] 35 | for i in range(1, col_length): 36 | dp[0][i] = dp[0][i - 1] + matrix[0][i] 37 | for i in range(1, row_length): 38 | dp[i][0] = dp[i - 1][0] + matrix[i][0] 39 | 40 | for row in range(1, row_length): 41 | for col in range(1, col_length): 42 | dp[row][col] = max([dp[row - 1][col], dp[row][col - 1]]) + matrix[row][col] 43 | 44 | return dp[row_length - 1][col_length - 1] 45 | 46 | 47 | if __name__ == '__main__': 48 | mat = [ 49 | [1, 10, 3, 8], 50 | [12, 2, 9, 6], 51 | [5, 7, 4, 11], 52 | [3, 7, 16, 5] 53 | ] 54 | 55 | print(GiftMaxValue().get_max_value(mat)) 56 | -------------------------------------------------------------------------------- /分类代表题目/动态规划/股票类型问题/一次交易.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题: 假设把股票价格按时间先后顺序存储在数组中,请问买卖 3 | 一次该股票可能获得的最大利润是多少? 4 | 5 | 例子: [9, 11, 8, 5, 7, 12, 16, 14] => 在价格5的时 6 | 候买入,在价格16的时候卖出,利润最大,为11 7 | """ 8 | 9 | 10 | class Stock: 11 | def maxProfit(self, prices, n): 12 | if n <= 1: 13 | return 0 14 | 15 | max_profit = 0 16 | min_buy = prices[0] 17 | 18 | index = 1 19 | while index < n: 20 | min_buy = min([prices[index], min_buy]) 21 | max_profit = max([max_profit, prices[index]-min_buy]) 22 | index += 1 23 | 24 | return max_profit 25 | 26 | 27 | if __name__ == '__main__': 28 | solution = Stock() 29 | print(solution.maxProfit([9, 11, 8, 5, 7, 12, 16, 14], 8)) 30 | 31 | -------------------------------------------------------------------------------- /分类代表题目/动态规划/股票类型问题/两次交易.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | 问题: 在股市的交易日中,假设最多可进行两次买卖(即买和卖的次数均小于等于2), 4 | 规则是必须一笔成交后进行另一笔(即买-卖-买-卖的顺序进行)。给出一天中的股票 5 | 变化序列,请写一个程序计算一天可以获得的最大收益。请采用实践复杂度低的方法实现。 6 | 7 | 思路: 以第i天为分界线,计算第i天之前进行一次交易的最大收益preProfit[i], 8 | 和第i天之后进行一次交易的最大收益postProfit[i].最后遍历一遍, 9 | max{preProfit[i] + postProfit[i]} (0≤i≤n-1)就是最大收益。 10 | """ 11 | 12 | 13 | import sys 14 | 15 | 16 | class Stock: 17 | def maxProfit(self, prices, n): 18 | if n <= 1: 19 | return 0 20 | 21 | pre_profit = [0 for _ in range(n)] 22 | post_profit = [0 for _ in range(n)] 23 | 24 | min_buy = prices[0] 25 | index = 1 26 | while index < n: 27 | min_buy = min([prices[index], min_buy]) 28 | pre_profit[index] = max([pre_profit[index - 1], prices[index] - min_buy]) 29 | index += 1 30 | 31 | max_sale = prices[index - 1] 32 | index = n - 2 33 | while index >= 0: 34 | max_sale = max([max_sale, prices[index]]) 35 | post_profit[index] = max([post_profit[index + 1], max_sale - prices[index]]) 36 | index -= 1 37 | 38 | max_profit = -sys.maxsize 39 | index = 0 40 | while index < n: 41 | max_profit = max([pre_profit[index] + post_profit[index], max_profit]) 42 | index += 1 43 | 44 | return max_profit 45 | 46 | 47 | if __name__ == '__main__': 48 | solution = Stock() 49 | print(solution.maxProfit([10, 5, 4, 3, 2, 1], 6)) 50 | -------------------------------------------------------------------------------- /分类代表题目/动态规划/股票类型问题/指定K次交易.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题: 假设把股票价格按时间先后顺序存储在数组中,可以最多买卖K次,请问 3 | 该股票可能获得的最大利润是多少? 4 | """ -------------------------------------------------------------------------------- /分类代表题目/动态规划/股票类型问题/无限次交易.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题: 假设把股票价格按时间先后顺序存储在数组中,可以买卖任意次,请问 3 | 该股票可能获得的最大利润是多少? 4 | 5 | 思路: 贪心算法,当价格了就卖,价格跌了就买 6 | """ 7 | 8 | 9 | class Stock: 10 | def maxProfit(self, prices, n): 11 | if n <= 1: 12 | return 0 13 | 14 | index = 0 15 | profit = 0 16 | while index < n - 1: 17 | if prices[index] < prices[index + 1]: 18 | profit += (prices[index + 1] - prices[index]) 19 | index += 1 20 | 21 | return profit 22 | 23 | 24 | if __name__ == '__main__': 25 | solution = Stock() 26 | print(solution.maxProfit([10, 22, 5, 75, 65, 80], 6)) 27 | -------------------------------------------------------------------------------- /分类代表题目/动态规划/跳石板问题.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述: 小易来到了一条石板路前,每块石板上从1挨着编号为:1、2、3. 3 | 这条石板路要根据特殊的规则才能前进:对于小易当前所在的编号为K的 石板, 4 | 小易单次只能往前跳K的一个约数(不含1和K)步,即跳到K+X(X为K的一个非1和 5 | 本身的约数)的位置。 小易当前处在编号为N的石板,他想跳到编号恰好为M的石 6 | 板去,小易想知道最少需要跳跃几次可以到达。 7 | 8 | 例如: 9 | N = 4,M = 24: 10 | 4->6->8->12->18->24 11 | 于是小易最少需要跳跃5次,就可以从4号石板跳到24号石板 12 | """ 13 | 14 | 15 | import sys 16 | import math 17 | 18 | 19 | class Solution: 20 | def get_smallest_steps(self, m, n): 21 | path = [100 for _ in range(n+1)] 22 | path[m] = 0 23 | for i in range(m, n-1): 24 | if path[i] == 100: 25 | continue 26 | 27 | factors = self.get_factors(i) 28 | for factor in factors: 29 | tmp = factor + i 30 | if tmp <= n and path[tmp] > path[i] + 1: 31 | path[tmp] = path[i] + 1 32 | print(path[n]) 33 | 34 | def get_factors(self, m): 35 | rs = list() 36 | for i in range(2, int(math.sqrt(m))+1): 37 | if m % i == 0: 38 | rs.append(i) 39 | if int(m / i) != i: 40 | rs.append(int(m/i)) 41 | return rs 42 | 43 | 44 | if __name__ == '__main__': 45 | a, b = list(map(int, sys.stdin.readline().strip().split())) 46 | s = Solution() 47 | s.get_smallest_steps(a, b) -------------------------------------------------------------------------------- /分类代表题目/双指针问题/和为s的三个数.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题: 给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ? 3 | 找出所有满足条件且不重复的三元组。 4 | 5 | 例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],满足要求的三元组集合为: 6 | [[-1, 0, 1],[-1, -1, 2]] 7 | """ 8 | 9 | 10 | class Solution: 11 | def threeSum(self, nums): 12 | res = [] 13 | nums.sort() 14 | for i in range(len(nums)-2): 15 | if i > 0 and nums[i] == nums[i-1]: 16 | continue 17 | l, r = i+1, len(nums)-1 18 | while l < r: 19 | s = nums[i] + nums[l] + nums[r] 20 | if s < 0: 21 | l +=1 22 | elif s > 0: 23 | r -= 1 24 | else: 25 | res.append((nums[i], nums[l], nums[r])) 26 | while l < r and nums[l] == nums[l+1]: 27 | l += 1 28 | while l < r and nums[r] == nums[r-1]: 29 | r -= 1 30 | l += 1; r -= 1 31 | return res -------------------------------------------------------------------------------- /分类代表题目/双指针问题/和为s的两个数字.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:输入一个递增排序的数组和一个数字S,在数组中查找两个数,是的他们的和正好是S, 3 | 如果有多对数字的和等于S,输出两个数的乘积最小的。 4 | 5 | 思路: 6 | 关于数组的题目,要是出现"一对"这个词,一般想着使用双指针或者归并排序的过程 7 | """ 8 | 9 | 10 | # -*- coding:utf-8 -*- 11 | class Solution: 12 | def find_numbers_with_sum(self, array, tsum): 13 | if not array or len(array) < 2: 14 | return list() 15 | if array[0] + array[1] > tsum: 16 | return list() 17 | if array[-1] + array[-2] < tsum: 18 | return list() 19 | 20 | left = 0 21 | right = len(array) - 1 22 | while array[left] + array[right] != tsum and left < right: 23 | if array[left] + array[right] > tsum: 24 | right -= 1 25 | else: 26 | left += 1 27 | 28 | if left == right: 29 | return list() 30 | 31 | return array[left], array[right] 32 | 33 | 34 | if __name__ == '__main__': 35 | print(Solution().find_numbers_with_sum([1, 2, 3, 4, 5], 7)) -------------------------------------------------------------------------------- /分类代表题目/双指针问题/和大于等于s的最短子数组.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题: 给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的子数组。 3 | 如果不存在符合条件的子数组,返回 0。 4 | 5 | 示例: 6 | 输入: [2,3,1,2,4,3], s = 7 7 | 输出: 2 8 | 解释: 子数组 [4,3] 是该条件下的长度最小的子数组。 9 | 10 | 方法: 11 | 子数组问题三板斧 12 | 1.以xxx位置开头的情况 13 | 2.以xxx位置结尾的情况 14 | 3.双指针 15 | 16 | 本题使用双指针即可解决,时间复杂度为O(n) 17 | """ 18 | 19 | 20 | class Solution: 21 | def minSubArrayLen(self, s, nums): 22 | """ 23 | :type s: int 24 | :type nums: List[int] 25 | :rtype: int 26 | """ 27 | if not nums: 28 | return 0 29 | 30 | min_length = float('+inf') 31 | left = 0 32 | right = 0 33 | cur = 0 34 | while right < len(nums): 35 | cur += nums[right] 36 | while cur >= s: 37 | min_length = min([min_length, right - left + 1]) 38 | cur -= nums[left] 39 | if left == right: 40 | right += 1 41 | left += 1 42 | else: 43 | left += 1 44 | else: 45 | right += 1 46 | 47 | return min_length if min_length != float('+inf') else 0 -------------------------------------------------------------------------------- /分类代表题目/双指针问题/回文链表判断.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题: 请判断一个链表是否为回文链表 3 | 4 | 要求: 5 | 时间复杂度为O(N),空间复杂度为O(1) 6 | """ 7 | 8 | 9 | import math 10 | 11 | 12 | # Definition for singly-linked list. 13 | class ListNode(object): 14 | def __init__(self, x): 15 | self.val = x 16 | self.next = None 17 | 18 | class Solution(object): 19 | def isPalindrome(self, head): 20 | """ 21 | :type head: ListNode 22 | :rtype: bool 23 | """ 24 | length = 0 25 | cur = head 26 | while cur: 27 | length += 1 28 | cur = cur.next 29 | 30 | if length <= 1: 31 | return True 32 | 33 | count = 1 34 | cur = head 35 | half = math.ceil(length / 2) 36 | while count < half: 37 | cur = cur.next 38 | count += 1 39 | half_head = cur 40 | cur = cur.next 41 | node = self.reverse(cur) 42 | half_head.next = node 43 | 44 | fast = head 45 | slow = head 46 | count = 1 47 | while count <= half: 48 | fast = fast.next 49 | count += 1 50 | while fast: 51 | if slow.val != fast.val: 52 | return False 53 | fast = fast.next 54 | slow = slow.next 55 | 56 | return True 57 | 58 | def reverse(self, head): 59 | pre = None 60 | cur = head 61 | while cur: 62 | tmp = cur.next 63 | cur.next = pre 64 | pre = cur 65 | cur = tmp 66 | return pre 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /分类代表题目/双指针问题/装最多水的容器.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题: 给定 n 个非负整数 a1, a2, ..., an, 每个数代表了坐标中的一个点 (i, ai)。画 n 条垂直线, 3 | 使得 i 垂直线的两个端点分别为(i, ai)和(i, 0)。找到两条线,使得其与 x 轴共同构成一个容器,以容纳 4 | 最多水。 5 | 6 | 示例: [1, 3, 2, 4] 选择[3, 2, 4],容量最大为 3*2 = 6 7 | 8 | 方法: 双指针两边逼近法 9 | """ 10 | 11 | 12 | class Solution: 13 | def maxArea(self, height): 14 | """ 15 | :type height: List[int] 16 | :rtype: int 17 | """ 18 | length = len(height) 19 | if length <= 1: 20 | return 0 21 | 22 | right_index = len(height) - 1 23 | left_index = 0 24 | max_area = 0 25 | while left_index < right_index: 26 | max_area = max([max_area, (right_index - left_index) * 27 | min([height[right_index], height[left_index]])]) 28 | if height[right_index] < height[left_index]: 29 | right_index -= 1 30 | else: 31 | left_index += 1 32 | 33 | return max_area -------------------------------------------------------------------------------- /分类代表题目/图问题/拓扑排序/课程表.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题: 现在你总共有 n 门课需要选,记为 0 到 n-1。 3 | 4 | 在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 , 5 | 你需要先完成课程 1 ,我们用一个匹配来表示他们: [0,1] 6 | 给定课程总量以及它们的先决条件,判断是否可能完成所有课程的学习? 7 | 8 | 示例: 9 | 2, [[1,0],[0,1]] => False 10 | 2, [[1, 0]] => True 11 | 12 | 思路: 13 | 拓扑排序,找到入度为0的点,依次消除,如果最后可完全消除,那么则无环 14 | """ 15 | 16 | 17 | import operator 18 | 19 | 20 | class Solution: 21 | def canFinish(self, numCourses, prerequisites): 22 | """ 23 | :type numCourses: int 24 | :type prerequisites: List[List[int]] 25 | :rtype: bool 26 | """ 27 | if not numCourses: 28 | return False 29 | 30 | if not prerequisites: 31 | return True 32 | 33 | while prerequisites: 34 | rs = self.find_root(prerequisites) 35 | if not rs: 36 | return False 37 | 38 | new_require = list() 39 | for item in prerequisites: 40 | if item[1] not in rs: 41 | new_require.append(item) 42 | prerequisites = new_require 43 | return True 44 | 45 | def find_root(self, prerequisites): 46 | child_getter = operator.itemgetter(0) 47 | parent_getter = operator.itemgetter(1) 48 | return set(map(parent_getter, prerequisites)) - set(map(child_getter, prerequisites)) 49 | -------------------------------------------------------------------------------- /分类代表题目/套路总结.md: -------------------------------------------------------------------------------- 1 | 1.旋转数组类型问题 2 | 3 | 思路是先找到旋转点,然后根据最后一个元素判断对哪个部分进行二分搜索 -------------------------------------------------------------------------------- /分类代表题目/子数组累加和问题/数组中全是正整数且累加和为给定值.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ResolveWang/algorithm_qa/a0cb649acaf8cf9d808272bc15f1951f2c05c828/分类代表题目/子数组累加和问题/数组中全是正整数且累加和为给定值.py -------------------------------------------------------------------------------- /分类代表题目/子数组累加和问题/数组中有正有负有零且累加和为给定值.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ResolveWang/algorithm_qa/a0cb649acaf8cf9d808272bc15f1951f2c05c828/分类代表题目/子数组累加和问题/数组中有正有负有零且累加和为给定值.py -------------------------------------------------------------------------------- /分类代表题目/子数组累加和问题/数组中有正有负有零且累加和小于等于给定值.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ResolveWang/algorithm_qa/a0cb649acaf8cf9d808272bc15f1951f2c05c828/分类代表题目/子数组累加和问题/数组中有正有负有零且累加和小于等于给定值.py -------------------------------------------------------------------------------- /分类代表题目/子数组累加和问题/求累加和为最大值.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ResolveWang/algorithm_qa/a0cb649acaf8cf9d808272bc15f1951f2c05c828/分类代表题目/子数组累加和问题/求累加和为最大值.py -------------------------------------------------------------------------------- /分类代表题目/子数组累加和问题/累加和为0的最长子数组.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述: 一个一维维数组中只有1和-1,实现程序,求和为0的最长子串长度 3 | 4 | 方法: 将sum(i)记录下来,如果i后面的index j 的和也为sum(i),那么则 5 | 它们中间的和必定为0,我们取最大值即可: max(j-i). 6 | 为了记录最大的distance,对于相同的sum,我们只记录第一次出现的index 7 | """ 8 | 9 | 10 | class Solution(object): 11 | def fun(self, l): 12 | dic = {} 13 | total = 0 14 | max_len = 0 15 | for x in range(len(l)): 16 | total += l[x] 17 | if total in dic: 18 | max_len = max(max_len, x - dic[total]) 19 | else: 20 | dic[total] = x 21 | return max_len 22 | 23 | 24 | if __name__ == '__main__': 25 | r = Solution().fun([-1, 1, -1, 1, 1, 1, -1, -1, -1]) 26 | print(r) 27 | -------------------------------------------------------------------------------- /分类代表题目/字符串/前缀树的应用.py: -------------------------------------------------------------------------------- 1 | """ 2 | 设计一个支持以下两种操作的数据结构: 3 | void addWord(word) 4 | bool search(word) 5 | search(word) 可以搜索文字或正则表达式字符串,字符串只包含字母 . 或 a-z 。 . 可以表示任何一个字母。 6 | 示例: 7 | addWord("bad") 8 | addWord("dad") 9 | addWord("mad") 10 | search("pad") -> false 11 | search("bad") -> true 12 | search(".ad") -> true 13 | search("b..") -> true 14 | """ 15 | 16 | 17 | class TrieNode: 18 | def __init__(self): 19 | self.word = False 20 | self.children = {} 21 | 22 | 23 | class WordDictionary: 24 | def __init__(self): 25 | self.root = TrieNode() 26 | 27 | def addWord(self, word): 28 | node = self.root 29 | for c in word: 30 | if c not in node.children: 31 | node.children[c] = TrieNode() 32 | node = node.children[c] 33 | node.word = True 34 | 35 | def search(self, word): 36 | return self.searchFrom(self.root, word) 37 | 38 | def searchFrom(self, node, word): 39 | for i in range(len(word)): 40 | c = word[i] 41 | if c == '.': 42 | for k in node.children: 43 | if self.searchFrom(node.children[k], word[i + 1:]): 44 | return True 45 | return False 46 | elif c not in node.children: 47 | return False 48 | node = node.children[c] 49 | return node.word -------------------------------------------------------------------------------- /分类代表题目/字符串/排列组合问题.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def conbition(self, input_str): 3 | if not input_str: 4 | return list() 5 | 6 | length = len(input_str) 7 | res = list() 8 | for i in range(1, length+1): 9 | self.pick_n_from_str(input_str, '', i, res) 10 | return res 11 | 12 | def pick_n_from_str(self, input_str, pre_str, n, res): 13 | # f(n, m) = f(n-1, m-1) + f(n-1, m) 14 | if len(pre_str) == n: 15 | res.append(pre_str) 16 | return 17 | 18 | if not input_str: 19 | return 20 | 21 | self.pick_n_from_str(input_str[1:], pre_str, n, res) 22 | self.pick_n_from_str(input_str[1:], pre_str+input_str[0], n, res) 23 | 24 | 25 | def Permutation(self, ss): 26 | if not ss: 27 | return list() 28 | res = list() 29 | self.process(ss, '', res) 30 | return sorted(list(set(res))) 31 | 32 | def process(self, ss, pre_ss, res): 33 | if len(ss) == 1: 34 | new_str = pre_ss + ss 35 | res.append(new_str) 36 | return 37 | 38 | index = 0 39 | while index < len(ss): 40 | cur_str = pre_ss + ss[index] 41 | new_ss = ss[:index] + ss[index + 1:] 42 | self.process(new_ss, cur_str, res) 43 | index += 1 44 | 45 | -------------------------------------------------------------------------------- /分类代表题目/字符串/数字翻译成字符串(动态规划).py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个数字,我们按照下面规则将其翻译成字符串: 3 | 0翻译成"a",1翻译成"b",...25翻译成"z",一个数字可能有多种翻译,比如 4 | 12258有5种不同的翻译,分别是"bccfi","bwfi","bczi","mcfi"和"mzfi", 5 | 求给定一个数字它的翻译方法有多少种? 6 | 7 | 思路: 8 | 套路就是求以每个位置结尾的情况有多少种翻译方式,可以通过动态规划求解 9 | dp[i] = dp[i-1] + tmp(tmp=dp[i-2]当num_str[index-1:index+1]可以 10 | 被翻译成合法的字符,否则tmp为0) 11 | """ 12 | 13 | 14 | class Num2Str: 15 | def get_total_res(self, num): 16 | if num < 0: 17 | return 0 18 | if len(str(num)) == 1: 19 | return 1 20 | 21 | str_num = str(num) 22 | dp = [0 for _ in range(len(str_num))] 23 | dp[0] = 1 24 | if int(str_num[0:2]) > 25: 25 | dp[1] = 1 26 | else: 27 | dp[1] = 2 28 | 29 | index = 2 30 | while index < len(str_num): 31 | tmp = 0 32 | if int(str_num[index-1: index+1]) <= 25: 33 | tmp = dp[index-2] 34 | dp[index] = dp[index-1] + tmp 35 | index += 1 36 | 37 | return dp[-1] 38 | 39 | 40 | if __name__ == '__main__': 41 | print(Num2Str().get_total_res(12258)) -------------------------------------------------------------------------------- /分类代表题目/字符串/最长不含重复字符的子字符串(动态规划).py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:请从字符串中找出一个最长的不包含重复字符的子字符串,计算 3 | 该最长子字符串的长度。假设字符串中只含有'a~z'的字符。例如,在字符 4 | 串'arabcacfr'中,最长不含重复字符的子字符串是'acfr',长度为4 5 | 6 | 思路: 7 | 分别求必须以i(0<=i<=len-1)结尾的最长不含重复字符的子串长度 8 | """ 9 | 10 | 11 | class LongestSubStr: 12 | def get_longest_substr(self, input_str): 13 | length = len(input_str) 14 | if length <= 1: 15 | return length 16 | 17 | dp = [0 for _ in range(length)] 18 | dp[0] = 1 19 | index = 1 20 | while index < length: 21 | if input_str[index] not in input_str[:index]: 22 | dp[index] = dp[index-1] + 1 23 | else: 24 | pre_index = input_str.rindex(input_str[index], 0, index-1) 25 | distance = index - pre_index 26 | if dp[index-1] < distance: 27 | dp[index] = dp[index-1] + 1 28 | else: 29 | dp[index] = distance 30 | index += 1 31 | 32 | return dp[length-1] 33 | 34 | 35 | if __name__ == '__main__': 36 | print(LongestSubStr().get_longest_substr('arabcacfr')) -------------------------------------------------------------------------------- /分类代表题目/字符串/最长公共子串.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题: 对于两个字符串,请设计一个时间复杂度为O(m*n)的算法(这里的m和n为两串的长度),求出两串的最长公共子串 3 | 的长度。这里的最长公共子串的定义为两个序列U1,U2,..Un和V1,V2,...Vn,其中Ui + 1 == Ui+1, 4 | Vi + 1 == Vi+1,同时Ui == Vi。给定两个字符串A和B,同时给定两串的长度n和m。 5 | """ 6 | 7 | 8 | class LongestSubstring: 9 | def findLongest(self, A, n, B, m): 10 | if m == 0 or n == 0: 11 | return 0 12 | 13 | dp = [[0 for _ in range(n)] for _ in range(m)] 14 | for i in range(n): 15 | if A[i] == B[0]: 16 | dp[0][i] = 1 17 | 18 | for i in range(m): 19 | if A[0] == B[i]: 20 | dp[i][0] = 1 21 | 22 | max_length = 0 23 | for row in range(1, m): 24 | for col in range(1, n): 25 | if B[row] == A[col]: 26 | dp[row][col] = dp[row - 1][col - 1] + 1 27 | max_length = max([max_length, dp[row][col]]) 28 | else: 29 | dp[row][col] = 0 30 | return max_length 31 | 32 | 33 | if __name__ == '__main__': 34 | solution = LongestSubstring() 35 | print(solution.findLongest("1AB2345CD", 9, "12345EF", 7)) -------------------------------------------------------------------------------- /分类代表题目/字符串/构造回文(最长公共子序列).py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:给定一个字符串s,你可以从中删除一些字符,使得剩下的串是一个回文串。 3 | 如何删除才能使得回文串最长呢? 4 | 5 | 输入 6 | 输入数据有多组,每组包含一个字符串s,且保证:1<=s.length<=1000. 7 | 8 | 输出 9 | 对于每组数据,输出一个整数,代表最少需要删除的字符个数。 10 | 11 | 示例 12 | abcda => 2 13 | google => 2 14 | """ 15 | 16 | 17 | import sys 18 | 19 | 20 | def get_max_len(strs): 21 | reversed_strs = strs[::-1] 22 | length = len(strs) 23 | dp = [[0] * (length+1) for _ in range(length+1)] 24 | 25 | for i in range(length): 26 | for j in range(length): 27 | if strs[i] == reversed_strs[j]: 28 | dp[i+1][j+1] = dp[i][j] + 1 29 | else: 30 | dp[i+1][j+1] = max(dp[i][j+1], dp[i+1][j]) 31 | print(length - dp[length][length]) 32 | 33 | 34 | if __name__ == '__main__': 35 | while True: 36 | s = sys.stdin.readline().strip() 37 | if not s: 38 | break 39 | 40 | get_max_len(s) -------------------------------------------------------------------------------- /分类代表题目/并查集/畅通工程.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述: 某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇。 3 | 省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只 4 | 要互相间接通过道路可达即可)。问最少还需要建设多少条道路? 5 | 6 | 输入描述: 7 | 测试输入包含若干测试用例。每个测试用例的第1行给出两个正整数,分别是城镇数目N ( < 1000 )和 8 | 道路数目M;随后的M行对应M条道路,每行给出一对正整数,分别是该条道路直接连通的两个城镇的编号。 9 | 为简单起见,城镇从1到N编号。 10 | 注意:两个城市之间可以有多条道路相通,也就是说 11 | 3 3 12 | 1 2 13 | 1 2 14 | 2 1 15 | 这种输入也是合法的 16 | 当N为0时,输入结束,该用例不被处理。 17 | 18 | 输出描述: 对每个测试用例,在1行里输出最少还需要建设的道路数目。 19 | 20 | 示例1 21 | 输入 22 | 4 2 23 | 1 3 24 | 4 3 25 | 26 | 3 3 27 | 1 2 28 | 1 3 29 | 30 | 2 3 31 | 5 2 32 | 1 2 33 | 34 | 3 5 35 | 999 0 36 | 0 37 | 38 | 输出 39 | 1 40 | 0 41 | 2 42 | 998 43 | """ 44 | import sys 45 | 46 | 47 | class Solution: 48 | """并查集的应用""" 49 | 50 | def find(self, pre, index): 51 | """pre每个位置的下标对应节点值,而pre[index]对应节点值的前一个数""" 52 | if pre[index] == index: 53 | return index 54 | else: 55 | tmp = self.find(pre, pre[index]) 56 | pre[index] = tmp 57 | return tmp 58 | 59 | def union(self, u, v, pre): 60 | node1 = self.find(pre, u) 61 | node2 = self.find(pre, v) 62 | if node1 != node2: 63 | pre[node2] = node1 64 | 65 | def get_roads(self): 66 | n, m = map(int, sys.stdin.readline().split()) 67 | count = 0 68 | pre = [i for i in range(n+1)] 69 | for i in range(m): 70 | x, y = [int(x) for x in sys.stdin.readline().split()] 71 | self.union(x, y, pre) 72 | 73 | for i in range(1, n+1): 74 | if pre[i] == i: 75 | count += 1 76 | 77 | print(count-1) 78 | 79 | 80 | if __name__ == '__main__': 81 | s = Solution() 82 | s.get_roads() 83 | -------------------------------------------------------------------------------- /分类代表题目/数学知识/Nim游戏.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述: 你和你的朋友,两个人一起玩 Nim游戏:桌子上有一堆石头,每次你们轮流拿掉 1 - 3 块石头。 3 | 拿掉最后一块石头的人就是获胜者。你作为先手。 4 | 5 | 你们是聪明人,每一步都是最优解。 编写一个函数,来判断你是否可以在给定石头数量的情况下赢得游戏。 6 | 7 | 8 | 示例: 9 | 输入: 4 10 | 输出: false 11 | 解释: 如果堆中有 4 块石头,那么你永远不会赢得比赛; 12 | 因为无论你拿走 1 块、2 块 还是 3 块石头,最后一块石头总是会被你的朋友拿走。 13 | 14 | 方法: 15 | f(4) = False 16 | f(5) = True 17 | f(6) = True 18 | f(7) = True 19 | f(8) = False 20 | 21 | 可以先多推几个,然后进行猜想和验证。这里可以将大数的情况往f(4),f(8),f(12)...转 22 | """ 23 | 24 | 25 | class Solution: 26 | def canWinNim(self, n): 27 | """ 28 | :type n: int 29 | :rtype: bool 30 | """ 31 | return not (n % 4 == 0) -------------------------------------------------------------------------------- /分类代表题目/数学知识/N的阶乘的末尾0的个数.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述: 给定一个正整数n,求它的阶乘结果中末尾的0的个数 3 | 例子: 11! = 39916800 => 2 4 | 5 | 思路: 6 | 这道题是一个数学问题,0的个数取决于10的因数个数,而对10进行因数 7 | 分解,最终由5这个质数进行决定 8 | num = n/5 + n/(5*5) + n/(5*5*5) + ... + n/(5*5*...*5) 9 | """ 10 | 11 | 12 | class Solution: 13 | def get_zeros(self, num): 14 | res = 0 15 | while num > 0: 16 | res += int(num/5) 17 | num = int(num/5) 18 | return res -------------------------------------------------------------------------------- /分类代表题目/数学知识/不可以重复选择的组合的和.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题: 给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字 3 | 和为 target 的组合。candidates 中的每个数字在每个组合中只能使用一次。 4 | 5 | 示例: 6 | 输入: candidates = [2,5,2,1,2], target = 5, 7 | 所求解集为: 8 | [ 9 | [1,2,2], 10 | [5] 11 | ] 12 | """ 13 | 14 | 15 | class Solution: 16 | def combinationSum2(self, candidates, target): 17 | if not candidates: 18 | return list() 19 | candidates.sort() 20 | res = list() 21 | seen = set() 22 | self.process(candidates, [], target, res, 0, seen) 23 | 24 | return res 25 | 26 | def process(self, nums, path, target, res, index, seen): 27 | if target == 0: 28 | if '#'.join(map(str, path)) not in seen: 29 | # 存放已经走过的路径,如果开始数组没排序,那么这里可以先排序 30 | # 也可以和本部分全排列的问题做法一样,直接除去nums中对应的值 31 | seen.add('#'.join(map(str, path))) 32 | res.append(path) 33 | return 34 | 35 | if target < 0: 36 | return 37 | 38 | if index < len(nums): 39 | self.process(nums, path, target, res, index + 1, seen) 40 | self.process(nums, path + [nums[index]], target - nums[index], res, index + 1, seen) 41 | 42 | -------------------------------------------------------------------------------- /分类代表题目/数学知识/可以有重复元素的组合的和.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题: 给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以 3 | 使数字和为 target 的组合。candidates 中的数字可以无限制重复被选取。 4 | 5 | 举例: 6 | 输入: candidates = [2,3,5], target = 8, 7 | 所求解集为: 8 | [ 9 | [2,2,2,2], 10 | [2,3,3], 11 | [3,5] 12 | ] 13 | """ 14 | 15 | 16 | class Solution: 17 | def combinationSum(self, candidates, target): 18 | if not candidates: 19 | return list() 20 | # 这里预先对所有元素进行排序,则可以避免收集重复的路径 21 | candidates.sort() 22 | res = list() 23 | self.process(target, [], candidates, 0, res) 24 | return res 25 | 26 | def process(self, target, path, candidates, index, res): 27 | """深度优先搜索""" 28 | if target < 0: 29 | return 30 | 31 | if target == 0: 32 | res.append(path) 33 | return 34 | 35 | for i in range(index, len(candidates)): 36 | self.process(target - candidates[i], path + [candidates[i]], candidates, i, res) -------------------------------------------------------------------------------- /分类代表题目/数学知识/完全平方数.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n。 3 | 你需要让组成和的完全平方数的个数最少。 4 | 5 | 示例: 6 | 12 => 3 7 | 解释: 12 = 4 + 4 + 4. 8 | """ 9 | 10 | 11 | class Solution: 12 | # 小技巧: 可以使用类变量记住状态 13 | dp = [0] 14 | 15 | def numSquares(self, n): 16 | cur_length = len(self.dp) 17 | if cur_length > n: 18 | return self.dp[n] 19 | 20 | for cur in range(cur_length, n+1): 21 | i = 1 22 | val = float('inf') 23 | while cur - i ** 2 >= 0: 24 | val = min([val, self.dp[cur - i ** 2] + 1]) 25 | i += 1 26 | self.dp.append(val) 27 | 28 | return self.dp[n] 29 | 30 | 31 | if __name__ == '__main__': 32 | s = Solution() 33 | print(s.numSquares(13)) -------------------------------------------------------------------------------- /分类代表题目/数学知识/排列数.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题: 给定一个没有重复数字的序列,返回其所有可能的全排列。 3 | 4 | 示例: 5 | 输入: [1,2,3] 6 | 输出: 7 | [ 8 | [1,2,3], 9 | [1,3,2], 10 | [2,1,3], 11 | [2,3,1], 12 | [3,1,2], 13 | [3,2,1] 14 | ] 15 | """ 16 | 17 | 18 | class Solution: 19 | def permute(self, nums): 20 | if not nums: 21 | return list() 22 | res = list() 23 | self.process(nums, [], res) 24 | return res 25 | 26 | def process(self, nums, path, res): 27 | if len(nums) == 0: 28 | res.append(path) 29 | return 30 | 31 | for i in range(len(nums)): 32 | # 注意这里是如何隐式"pop"掉某个元素的 33 | new_nums = nums[:i] + nums[i + 1:] 34 | # 这里可以直接对列表进行相加,返回一个新列表 35 | self.process(new_nums, path + [nums[i]], res) -------------------------------------------------------------------------------- /分类代表题目/数学知识/摩尔投票算法.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题: 3 | 给定一个大小为 n 的数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。 4 | 说明: 要求算法的时间复杂度为O(n),空间复杂度为O(1)。 5 | 6 | 示例: 7 | [1,1,1,3,3,2,2,2] => [1,2] 8 | """ 9 | 10 | 11 | class Solution: 12 | def majorityElement(self, nums): 13 | """ 14 | :type nums: List[int] 15 | :rtype: List[int] 16 | """ 17 | a, b, ca, cb = 0, 0, 0, 0 18 | 19 | for i in nums: 20 | if i == a: 21 | ca += 1 22 | elif i == b: 23 | cb += 1 24 | elif ca == 0: 25 | a = i 26 | ca += 1 27 | elif cb == 0: 28 | b = i 29 | cb = 1 30 | else: 31 | ca -= 1 32 | cb -= 1 33 | 34 | ca = 0 35 | cb = 0 36 | for i in nums: 37 | if i == a: 38 | ca += 1 39 | if i == b: 40 | cb += 1 41 | rs = [] 42 | if ca > len(nums) / 3: 43 | rs.append(a) 44 | if cb > len(nums) / 3 and a != b: 45 | rs.append(b) 46 | return rs 47 | 48 | 49 | # 延伸: 如果是超过1/k的话,则还是可以用这种方式,用Python自带的数据结构可以这样做 50 | import collections 51 | 52 | 53 | def majorityElement(nums, k): 54 | ctr = collections.Counter() 55 | for n in nums: 56 | ctr[n] += 1 57 | # 当有k个不同的投票者时,第k个因为是才加入counter的,在做了“-”运算之后必然 58 | # 就不存在了,所以能保持空间固定大小为O(k) 59 | if len(ctr) == k: 60 | ctr -= collections.Counter(set(ctr)) 61 | ctr = collections.Counter(n for n in nums if n in ctr) 62 | return [n for n in ctr if ctr[n] > len(nums)/k] -------------------------------------------------------------------------------- /分类代表题目/数学知识/混合颜料.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述: 你就是一个画家!你现在想绘制一幅画,但是你现在没有足够颜色的颜料. 3 | 为了让问题简单,我们用正整数表示不同颜色的颜料。你知道这幅画需要的n种颜色的颜料, 4 | 你现在可以去商店购买一些颜料,但是商店不能保证能供应所有颜色的颜料,所以你需要 5 | 自己混合一些颜料。混合两种不一样的颜色A和颜色B颜料可以产生(A XOR B)这种颜色的 6 | 颜料(新产生的颜料也可以用作继续混合产生新的颜色,XOR表示异或操作)。本着勤俭节约 7 | 的精神,你想购买更少的颜料就满足要求,所以兼职程序员的你需要编程来计算出最少需要 8 | 购买几种颜色的颜料? 9 | 10 | 输入描述: 11 | 第一行为绘制这幅画需要的颜色种数n (1 ≤ n ≤ 50) 12 | 第二行为n个数xi(1 ≤ xi ≤ 1,000,000,000),表示需要的各种颜料. 13 | 14 | 输出描述: 15 | 输出最少需要在商店购买的颜料颜色种数,注意可能购买的颜色不一定会使用在画中,只是为了产生新的颜色。 16 | 示例1 17 | 输入 18 | 3 19 | 1 7 3 20 | 输出 21 | 3 22 | """ 23 | 24 | 25 | def getHighPosition(number): 26 | count = 0 27 | while number > 0: 28 | number = number >> 1 29 | count += 1 30 | return count 31 | 32 | 33 | n = int(input()) 34 | colors = list(map(int, input().strip().split())) 35 | colors.sort() 36 | lastIndex = len(colors) - 1 37 | bLastIndex = lastIndex - 1 38 | num = 0 39 | while len(colors) > 2: 40 | if getHighPosition(colors[lastIndex]) == getHighPosition(colors[bLastIndex]): 41 | temp = colors[lastIndex] ^ colors[bLastIndex] 42 | if temp not in colors: 43 | colors.append(temp) 44 | colors.sort() 45 | lastIndex += 1 46 | bLastIndex += 1 47 | else: 48 | num += 1 49 | colors.pop() 50 | lastIndex -= 1 51 | bLastIndex -= 1 52 | 53 | print(num + len(colors)) -------------------------------------------------------------------------------- /分类代表题目/数学知识/第k个排列.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题: 给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。 3 | 4 | 按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下: 5 | 6 | "123" 7 | "132" 8 | "213" 9 | "231" 10 | "312" 11 | "321" 12 | 给定 n 和 k,返回第 k 个排列。 13 | 14 | 示例: 15 | n = 3, k = 3 => "213" 16 | """ 17 | 18 | 19 | import math 20 | 21 | 22 | class Solution: 23 | def getPermutation(self, n, k): 24 | """ 25 | :type n: int 26 | :type k: int 27 | :rtype: str 28 | """ 29 | if n < 1: 30 | return '' 31 | 32 | r = 1 33 | tmp = n 34 | while tmp > 0: 35 | r *= tmp 36 | tmp -= 1 37 | if r < k or k <= 0: 38 | return '' 39 | 40 | datas = list(range(1, n + 1)) 41 | step = n 42 | res = list() 43 | while len(datas) > 1: 44 | r = r / step 45 | cur_pos = math.ceil(k / r) - 1 46 | res.append(datas[cur_pos]) 47 | datas = datas[:cur_pos] + datas[cur_pos + 1:] 48 | k = k % r 49 | if k == 0: 50 | k = r 51 | step -= 1 52 | 53 | if len(datas) == 1: 54 | res.append(datas[0]) 55 | 56 | return ''.join(map(str, res)) -------------------------------------------------------------------------------- /分类代表题目/数学知识/计数质数.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题: 统计所有小于非负数整数 n 的质数的数量。 3 | 4 | 示例: 5 | 输入: 10 6 | 输出: 4 7 | 解释: 小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 。 8 | """ 9 | 10 | 11 | class Solution: 12 | def countPrimes(self, n): 13 | """ 14 | :type n: int 15 | :rtype: int 16 | """ 17 | bit_arr = [0 for i in range(n)] 18 | count = 0 19 | index = 2 20 | while index < n: 21 | if bit_arr[index] == 0: 22 | count += 1 23 | j = index 24 | while index*j < n: 25 | bit_arr[index*j] = 1 26 | j += 1 27 | index += 1 28 | return count -------------------------------------------------------------------------------- /分类代表题目/数学知识/超级素数幂.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述: 如果一个数字能表示为p^q(^表示幂运算)且p为一个素数,q为大于1的正整数就称这个数叫做超级素数幂。 3 | 现在给出一个正整数n,如果n是一个超级素数幂需要找出对应的p,q。 4 | 5 | 输入描述: 6 | 输入一个正整数n(2 ≤ n ≤ 10^18) 7 | 输出描述: 8 | 如果n是一个超级素数幂则输出p,q,以空格分隔,行末无空格。 如果n不是超级素数幂,则输出No 9 | 示例1 10 | 输入 11 | 27 12 | 13 | 输出 14 | 3 3 15 | """ 16 | 17 | import sys 18 | import math 19 | 20 | 21 | class Solution: 22 | def get_super_num(self, num): 23 | q_max = math.sqrt(num) 24 | q = 2 25 | while q <= q_max: 26 | p = num ** (1.0 / q) 27 | if p == int(p) and self.is_prime(int(p)): 28 | print(str(int(p)) + ' ' + str(q)) 29 | return 30 | q += 1 31 | print('No') 32 | 33 | def is_prime(self, num): 34 | if n < 2: 35 | return False 36 | i = 2 37 | while i * i <= num: 38 | if num % i == 0: 39 | return False 40 | i += 1 41 | return True 42 | 43 | 44 | if __name__ == '__main__': 45 | n = int(sys.stdin.readline().strip()) 46 | s = Solution() 47 | s.get_super_num(n) -------------------------------------------------------------------------------- /分类代表题目/数学知识/阶乘后的零.py: -------------------------------------------------------------------------------- 1 | """ 2 | 给定一个整数 n,返回 n! 结果尾数中零的数量。 3 | 示例: 4 | 5! => 1 5 | 6 | 思路: 7 | 0的个数取决于5的个数,问题可以转化为求5的因素,每个数可能有0,1,2...x个 8 | 5的因数 9 | """ 10 | 11 | 12 | class Solution: 13 | def trailingZeroes(self, n): 14 | """ 15 | :type n: int 16 | :rtype: int 17 | """ 18 | i = 0 19 | cur = 5 20 | while n / cur >= 1: 21 | i += 1 22 | cur = 5 * cur 23 | 24 | res = 0 25 | start = 1 26 | while start <= i: 27 | res += int(n / 5 ** start) 28 | start += 1 29 | return res -------------------------------------------------------------------------------- /分类代表题目/数学知识/页码统计.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述: 牛牛新买了一本算法书,算法书一共有n页,页码从1到n。牛牛于是想了一个算法题目: 3 | 在这本算法书页码中0~9每个数字分别出现了多少次? 4 | 5 | 6 | 输入描述: 7 | 输入包括一个整数n(1 ≤ n ≤ 1,000,000,000) 8 | 9 | 输出描述: 10 | 输出包括一行10个整数,即0~9这些数字在页码中出现的次数,以空格分隔。行末无空格。 11 | 示例1 12 | 输入 13 | 999 14 | 输出 15 | 189 300 300 300 300 300 300 300 300 300 16 | """ 17 | 18 | # encoding=utf-8 19 | 20 | 21 | def find(num): 22 | # 计算每个数字, 在每一位上出现的次数. 23 | res = [0] * 10 # 结果 24 | digit = 1 # 个位 25 | while True: 26 | low = num % digit 27 | cur = int((num % (10 * digit)) / digit) 28 | # 将数字分割, 例如 digit为100时, 表示百位. 12345 将有 high = 12, cur = 3, low = 45 29 | high = int(num / (10 * digit)) 30 | if cur == 0 and high == 0: 31 | break 32 | 33 | # 从0到9, 计算i在digit位出现的次数. 34 | for i in range(10): 35 | if i < cur: 36 | if i == 0: 37 | res[i] += high * digit 38 | else: 39 | res[i] += (high + 1) * digit 40 | elif i == cur: 41 | if i == 0: 42 | res[i] += (high - 1) * digit + low + 1 43 | else: 44 | # 比如 12345, i=3, cur=3,那么除了高位1200个,还有低位345+1个 45 | res[i] += high * digit + low + 1 46 | else: 47 | res[i] += high * digit 48 | digit *= 10 # 下一位 49 | return res 50 | 51 | 52 | num = int(input()) 53 | res = find(num) 54 | print(' '.join(map(str, res))) -------------------------------------------------------------------------------- /分类代表题目/树的遍历及各种变型/完全二叉树的节点.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题: 求一颗完全二叉树的所有节点数量 3 | 4 | 思路: 5 | 计算左子树和右子树高度是否相等 6 | 1.相等则说明左子树一定是满二叉树 7 | 2.不相等说明右子树一定是满二叉树 8 | """ 9 | 10 | 11 | class Solution: 12 | def countNodes(self, root): 13 | if not root: 14 | return 0 15 | leftDepth = self.getDepth(root.left) 16 | rightDepth = self.getDepth(root.right) 17 | if leftDepth == rightDepth: 18 | return pow(2, leftDepth) + self.countNodes(root.right) 19 | else: 20 | return pow(2, rightDepth) + self.countNodes(root.left) 21 | 22 | def getDepth(self, root): 23 | if not root: 24 | return 0 25 | return 1 + self.getDepth(root.left) -------------------------------------------------------------------------------- /分类代表题目/深搜、广搜和回溯/01翻转.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述:牛牛正在挑战一款名为01翻转的游戏。游戏初始有A个0,B个1,牛牛的目标就是把所有的值都变为1, 3 | 每次操作牛牛可以任意选择恰好K个数字,并将这K个数字的值进行翻转(0变为1,1变为0)。牛牛如果使用最少 4 | 的操作次数完成这个游戏就可以获得奖品,牛牛想知道最少的操作次数是多少? 5 | 6 | 例如:A = 4 B = 0 K = 3 7 | 0000 -> 1110 -> 1001 -> 0100 -> 1111 8 | 需要的最少操作次数为4 9 | 10 | 输入描述: 11 | 输入为一行: 一共三个整数A(0 ≤ A ≤ 100,000),B(0 ≤ B ≤ 100,000),K(1 ≤ K ≤100,000). 12 | 以空格分隔 13 | 输出描述: 14 | 输出一个整数,表示最少需要的操作次数。如果不能完成,则输出-1 15 | 示例1 16 | 输入 17 | 4 0 3 18 | 输出 19 | 4 20 | """ 21 | 22 | 23 | import sys 24 | 25 | 26 | class Solution: 27 | def get_reversed_time(self, a, b, k): 28 | if a == 0: 29 | print(0) 30 | return 31 | 32 | if k == a: 33 | print(1) 34 | return 35 | 36 | if k > a + b: 37 | print(-1) 38 | return 39 | 40 | visited = {a} 41 | queue = list() 42 | queue.append((a, b, 0)) 43 | while queue: 44 | cur = queue.pop(0) 45 | if cur[0] == 0: 46 | print(cur[2]) 47 | return 48 | 49 | for i in range(1, min([cur[0], k])+1): 50 | if cur[1] < k - i: 51 | continue 52 | 53 | new_a = cur[0] - i + (k - i) 54 | if new_a == 0: 55 | print(cur[2]+1) 56 | return 57 | if new_a not in visited: 58 | visited.add(new_a) 59 | queue.append((new_a, cur[0]+cur[1]-new_a, cur[2]+1)) 60 | print(-1) 61 | 62 | 63 | if __name__ == '__main__': 64 | s = Solution() 65 | a, b, k = map(int, sys.stdin.readline().split()) 66 | s.get_reversed_time(a, b, k) -------------------------------------------------------------------------------- /分类代表题目/深搜、广搜和回溯/饥饿的小易.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述: 小易总是感觉饥饿,所以作为章鱼的小易经常出去寻找贝壳吃。最开始小易在一个初始位置x_0。 3 | 对于小易所处的当前位置x,他只能通过神秘的力量移动到 4 * x + 3或者8 * x + 7。因为使用神秘力 4 | 量要耗费太多体力,所以它只能使用神秘力量最多100,000次。贝壳总生长在能被1,000,000,007整除的 5 | 位置(比如:位置0,位置1,000,000,007,位置2,000,000,014等)。小易需要你帮忙计算最少需要使 6 | 用多少次神秘力量就能吃到贝壳。 7 | 8 | 输入描述: 9 | 输入一个初始位置x_0,范围在1到1,000,000,006 10 | 输出描述: 11 | 输出小易最少需要使用神秘力量的次数,如果使用次数使用完还没找到贝壳,则输出-1 12 | 示例1 13 | 输入 14 | 125000000 15 | 输出 16 | 1 17 | """ 18 | 19 | import sys 20 | 21 | 22 | class Solution: 23 | def get_res(self, pos): 24 | queue = [pos] 25 | visited = {pos: 0} 26 | find_food = False 27 | while len(queue): 28 | cur_pos = queue.pop(0) 29 | 30 | if cur_pos == 0: 31 | find_food = True 32 | break 33 | if visited.get(cur_pos, 0) > 100000: 34 | break 35 | else: 36 | next_pos = (4 * cur_pos + 3) % 1000000007 37 | if next_pos not in visited: 38 | queue.append(next_pos) 39 | visited[next_pos] = visited[cur_pos] + 1 40 | 41 | next_pos = (8 * cur_pos + 7) % 1000000007 42 | if next_pos not in visited: 43 | queue.append(next_pos) 44 | visited[next_pos] = visited[cur_pos] + 1 45 | if find_food: 46 | print(visited[cur_pos]) 47 | else: 48 | print(-1) 49 | 50 | 51 | if __name__ == '__main__': 52 | args = int(sys.stdin.readline().strip()) 53 | solution = Solution() 54 | solution.get_res(args) 55 | -------------------------------------------------------------------------------- /分类代表题目/矩阵打印相关问题/90度旋转矩阵.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述: 给定一个 n × n 的二维矩阵表示一个图像。将图像顺时针旋转 90 度。 3 | 你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一 4 | 个矩阵来旋转图像。 5 | 6 | 技巧: 7 | 顺时针旋转: 先将矩阵进行上下行对称交换,然后再以左上到右下的对角线为对称轴进 8 | 行元素交换,比如 9 | 1 2 3 7 8 9 7 4 1 10 | 4 5 6 => 4 5 6 => 8 5 2 11 | 7 8 9 1 2 3 9 6 3 12 | 13 | 逆时针旋转: 将矩阵进行左右对称交换,再以左上到右下的对角线为对称轴进行元素交换, 14 | 比如 15 | 1 2 3 3 2 1 3 6 9 16 | 4 5 6 => 6 5 4 => 2 5 8 17 | 7 8 9 9 8 7 1 4 7 18 | """ 19 | 20 | 21 | class Rotation: 22 | def matrix_rotate(self, matrix): 23 | """顺时针交换""" 24 | matrix.reverse() 25 | n = len(matrix[0]) 26 | for i in range(n): 27 | for j in range(n): 28 | if j < i: 29 | matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j] 30 | 31 | def matrix_rotate2(self, matrix): 32 | """逆时针交换""" 33 | for row in matrix: 34 | row.reverse() 35 | n = len(matrix[0]) 36 | for i in range(n): 37 | for j in range(n): 38 | if j < i: 39 | matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j] 40 | 41 | 42 | if __name__ == '__main__': 43 | matrix = [ 44 | [1, 2, 3], 45 | [4, 5, 6], 46 | [7, 8, 9] 47 | ] 48 | solution = Rotation() 49 | solution.matrix_rotate2(matrix) 50 | print(matrix) -------------------------------------------------------------------------------- /分类代表题目/背包及变型/双核处理.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述: 一种双核CPU的两个核能够同时的处理任务,现在有n个已知数据量的任务需要交给CPU处理, 3 | 假设已知CPU的每个核1秒可以处理1kb,每个核同时只能处理一项任务。n个任务可以按照任意顺序放入 4 | CPU进行处理,现在需要设计一个方案让CPU处理完这批任务所需的时间最少,求这个最小的时间。 5 | 6 | 输入: 输入包括两行: 第一行为整数n(1 ≤ n ≤ 50) 第二行为n个整数length[i](1024 ≤ length[i] 7 | ≤ 4194304),表示每个任务的长度为length[i]kb,每个数均为1024的倍数。 8 | 9 | 输出描述: 输出一个整数,表示最少需要处理的时间 10 | 11 | 示例1 12 | 输入 13 | 5 3072 3072 7168 3072 1024 14 | 15 | 输出 16 | 9216 17 | """ 18 | import sys 19 | 20 | 21 | class Solution: 22 | def get_least_work_time(self, arr): 23 | if len(arr) <= 2: 24 | print(max(arr)) 25 | return 26 | 27 | total = sum(arr) // 2 + 1 28 | dp = [[0 for _ in range(total)] for _ in range(len(arr)+1)] 29 | for i in range(1, len(arr)+1): 30 | for j in range(1, total): 31 | if j >= arr[i-1]: 32 | dp[i][j] = max([dp[i-1][j], dp[i-1][j-arr[i-1]] + arr[i-1]]) 33 | else: 34 | dp[i][j] = dp[i-1][j] 35 | 36 | print(max([dp[len(arr)][total-1], sum(arr)-dp[len(arr)][total-1]])) 37 | 38 | 39 | if __name__ == '__main__': 40 | s = Solution() 41 | n = int(sys.stdin.readline()) 42 | args = list(map(int, sys.stdin.readline().split())) 43 | s.get_least_work_time(args) -------------------------------------------------------------------------------- /分类代表题目/贪心/最大数.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题描述: 给定一组非负整数,重新排列它们的顺序使之组成一个最大的整数。 3 | 4 | 示例: 5 | [10,2] => 210 6 | 7 | [3,30,34,5,9] => 9534330 8 | """ 9 | 10 | 11 | class Solution(object): 12 | def largestNumber(self, nums): 13 | """ 14 | :type nums: List[int] 15 | :rtype: str 16 | """ 17 | if not nums: 18 | return '' 19 | 20 | num_strs = list(map(str, nums)) 21 | 22 | res = self.process(num_strs, '') 23 | if res[0] == '0': 24 | return '0' 25 | return res 26 | 27 | def process(self, strs, cur): 28 | if not strs: 29 | return cur 30 | 31 | max_index = -1 32 | for index, value in enumerate(strs): 33 | if max_index == -1: 34 | max_index = index 35 | continue 36 | 37 | if int(value+strs[max_index]) > int(strs[max_index]+value): 38 | max_index = index 39 | 40 | return self.process(strs[:max_index] + strs[max_index + 1:], cur + strs[max_index]) -------------------------------------------------------------------------------- /分类代表题目/贪心/跳跃游戏.py: -------------------------------------------------------------------------------- 1 | """ 2 | 问题: 给定一个非负整数数组,你最初位于数组的第一个位置。数组中的每个元素代表你在该位置可以跳跃的最大长度。 3 | 判断你是否能够到达最后一个位置。 4 | 5 | 示例: 6 | 输入: [2,3,1,1,4] 7 | 输出: true 8 | 解释: 从位置 0 到 1 跳 1 步, 然后跳 3 步到达最后一个位置。 9 | 10 | 输入: [3,2,1,0,4] 11 | 输出: false 12 | 解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置。 13 | 14 | 思路: 15 | 我们从最后一个位置开始遍历数组,每当我们遍历到一个可以达到末尾位置的位置时,就更新末尾位置为当前位置,最后根 16 | 据最新的末尾位置是否为0即可 17 | """ 18 | 19 | 20 | class Solution: 21 | def canJump(self, nums): 22 | """ 23 | :type nums: List[int] 24 | :rtype: bool 25 | """ 26 | if not nums: 27 | return True 28 | 29 | index = len(nums) - 1 30 | last_good_pos = index - 1 31 | while index >= 0: 32 | if nums[index] + index >= last_good_pos: 33 | last_good_pos = index 34 | index -= 1 35 | 36 | return last_good_pos == 0 --------------------------------------------------------------------------------