├── Go ├── Go 语言 map 如何顺序读取?.md ├── Go 语言 map 是并发安全的吗?.md ├── Go 语言 new 和 make 关键字的区别.md ├── Go 语言切片是如何扩容的?.md ├── Go 语言数组和切片的区别.md ├── go-gpm.md ├── go-scheduler-base.md ├── go-scheduler.md ├── q001.md ├── q002.md ├── q003.md ├── q004.md ├── q005.md ├── q006.md ├── q007.md ├── q008.md ├── q009.md ├── q010.md ├── q011.md ├── q012.md ├── q013.md ├── q014.md ├── q015.md ├── q016.md ├── q017.md ├── q018.md ├── q019.md ├── q020.md ├── q021.md ├── q022.md └── 为什么 Go for-range 的 value 值地址每次都一样?.md ├── LICENSE.md ├── MongoDB └── README.md ├── MySQL ├── README.md ├── mysql-index-b-plus.md ├── mysql-interview.md └── mysql-mvcc.md ├── Python └── README.md ├── README.md ├── Redis ├── README.md ├── redis-data-structure.md ├── redis-master-slave.md ├── redis-policy.md ├── redis-rdb.md └── redis.md ├── src ├── array_slice_to_min_num.py └── go_cook.py ├── 中间件 └── README.md ├── 数据结构与算法 ├── README.md ├── 两个合并排序的链表.py ├── 两个栈实现队列.py ├── 两个链表的第一个公共节点.py ├── 二叉搜索树和双向链表.py ├── 二叉搜索树的后序遍历序列.py ├── 二叉搜索树的第k个节点.py ├── 二叉树 │ ├── 二叉树的前中后序的递归,非递归及Morris遍历.py │ ├── 二叉树的序列化和反序列化.py │ ├── 二叉树的按层打印.py │ └── 判断t1树中是否有与t2树拓扑结构完全相同的子树.py ├── 二叉树中和为某一值的路径.py ├── 二叉树的下一个节点.py ├── 二叉树的深度.py ├── 二叉树的镜像.py ├── 从上往下打印二叉树.py ├── 从尾到头打印链表.py ├── 反转链表.py ├── 复杂链表的复制.py ├── 字节跳动最爱考的 64 道算法题.pdf ├── 对称的二叉树.py ├── 平衡二叉树.py ├── 序列化二叉树.py ├── 把二叉树打印成多行.py ├── 按之字形顺序打印二叉树.py ├── 数据结构相关面试题汇总.md ├── 栈和队列 │ ├── 两个栈组成一个队列.py │ ├── 如何仅用递归函数和栈操作逆序一个栈.py │ ├── 构造数组的MaxTree.py │ ├── 求最大子矩阵的大小.py │ ├── 生成窗口最大值数组.py │ ├── 用一个栈实现另一个栈的排序.py │ └── 设计一个有getMin功能的栈.py ├── 重建二叉树.py ├── 链表中倒数第k个节点.py ├── 链表中环的入口节点.py └── 链表问题 │ ├── 判断一个链表是否为回文结构.py │ ├── 判断一个链表是否有环,如果有的话返回第一个进环的节点.py │ ├── 判断两个无环链表是否相交,相交则返回第一个相交节点.py │ ├── 判断两个有环链表是否相交,相交则返回第一个相交节点.py │ ├── 向有环的环形链表中插入新节点.py │ ├── 在单链表中删除指定值的节点.py │ ├── 复制含有随机指针节点的链表.py │ ├── 将单向链表按某值划分成左边小,中间相等,右边大的形式.py │ ├── 将单链表的每K个节点之间逆序.py │ ├── 打印两个有序链表的公共部分.py │ └── 节点删除方式.py ├── 系统编程 └── README.md ├── 编程题 ├── Python 编程题-1.md ├── Python 编程题-2.md ├── Python 编程题-3.md ├── Python 编程题-4.md ├── Python 编程题-5.md ├── README.md ├── 丑数.py ├── 两种排序方法.py ├── 买苹果.py ├── 二维数组中的查找.py ├── 二进制中1的个数.py ├── 优雅的点.py ├── 分田地.py ├── 分苹果.py ├── 删除公共字符.py ├── 删除字符串中出现次数最少的字符.py ├── 删除链表中重复的节点.py ├── 判断两个IP是否属于同一子网.py ├── 包含min函数的栈.py ├── 取近似值.py ├── 变态青蛙跳.py ├── 合唱团.py ├── 合唱队.py ├── 合并表记录.py ├── 和为s的两个数字.py ├── 和为s的连续整数序列.py ├── 回文序列.py ├── 图片整理.py ├── 圆圈中最后剩下的数.py ├── 地下迷宫.py ├── 坐旋转字符串.py ├── 坐标移动.py ├── 字串的连接最长路径查找.py ├── 字符串中找出连续最长的数字串.py ├── 字符串分割.py ├── 字符串加密.py ├── 字符串加密2.py ├── 字符串合并处理.py ├── 字符串排序.py ├── 字符串最后一个单词的长度.py ├── 字符串的排列.py ├── 字符串运用-密码截取.py ├── 字符流中第一个不重复的字符.py ├── 密码验证合格程序.py ├── 小易喜欢的单词.py ├── 幸运的袋子.py ├── 扑克片顺子.py ├── 把字符串转化成整数.py ├── 招商银行.py ├── 数值的整数次方.py ├── 数列还原.py ├── 数字和为sum的方法数.py ├── 数字在排序数组中出现的次数.py ├── 数字游戏.py ├── 数字翻转.py ├── 数字颠倒.py ├── 数据分类处理.py ├── 数据流中的中位数.py ├── 数的子结构.py ├── 数组中出现次数超过一半的数字.py ├── 数组中只出现一次的数字.py ├── 数组中的逆序对.py ├── 数组中重复的数字.py ├── 整数与IP地址间的转换.py ├── 整数中1出现的次数.py ├── 斐波那契数列.py ├── 旋转数组的最小数字.py ├── 明明的随机数.py ├── 星际穿越.py ├── 替换空格.py ├── 最大奇约数.py ├── 最小的k个数.py ├── 末尾0的个数.py ├── 机器人的运动范围.py ├── 构建乘积数组.py ├── 查找兄弟单词.py ├── 栈的压入弹出序列.py ├── 正则表达式匹配.py ├── 求1+2+3+...+n.py ├── 求int型正整数在内存中存储时1的个数.py ├── 求和-好未来.py ├── 求和.py ├── 求小球落地5次后所经历的路程和第5次反弹的高度.py ├── 汽水瓶.py ├── 滑动窗口的最大值.py ├── 百度.py ├── 矩形覆盖.py ├── 矩阵中的路径.py ├── 称砝码.py ├── 第一个只出现一次的字符.py ├── 简单密码.py ├── 简单错误记录.py ├── 素数伴侣.py ├── 素数对.py ├── 统计回文.py ├── 统计每个月兔子的总数.py ├── 美团.py ├── 翻转单词顺序列.py ├── 藏宝图.py ├── 蛇形矩阵.py ├── 表示数值的字符串.py ├── 解救小易.py ├── 计算字符个数.py ├── 计算糖果.py ├── 识别有效的IP地址和掩码并进行分类统计.py ├── 调整数组顺序使奇数位于偶数前面.py ├── 质数因子.py ├── 购物单.py ├── 输入n个整数,输出出现次数大于等于数组长度一半的数。.py ├── 输入一行字符,分别统计出包含英文字母、空格、数字和其它字符的个数.py ├── 进制转换.py ├── 连续子数组的最大和.py ├── 青蛙跳台阶.py ├── 顺时针打印矩阵.py ├── 餐厅.py └── 饥饿的小易.py ├── 网络 └── README.md └── 项目与架构 └── README.md /Go/Go 语言 map 如何顺序读取?.md: -------------------------------------------------------------------------------- 1 | **原文链接:** [Go 语言 map 如何顺序读取?](https://mp.weixin.qq.com/s/iScSgfpSE2y14GH7JNRJSA) 2 | 3 | Go 语言中的 map 是一种非常强大的数据结构,它允许我们快速地存储和检索键值对。 4 | 5 | 然而,当我们遍历 map 时,会有一个有趣的现象,那就是输出的键值对顺序是不确定的。 6 | 7 | ## 现象 8 | 9 | 先看一段代码示例: 10 | 11 | ```go 12 | package main 13 | 14 | import "fmt" 15 | 16 | func main() { 17 | m := map[string]int{ 18 | "apple": 1, 19 | "banana": 2, 20 | "orange": 3, 21 | } 22 | 23 | for k, v := range m { 24 | fmt.Printf("key=%s, value=%d\n", k, v) 25 | } 26 | } 27 | ``` 28 | 29 | 当我们多执行几次这段代码时,就会发现,输出的顺序是不同的。 30 | 31 | ## 原因 32 | 33 | 首先,Go 语言 map 的底层实现是哈希表,在进行插入时,会对 key 进行 hash 运算。这也就导致了数据不是按顺序存储的,和遍历的顺序也就会不一致。 34 | 35 | 第二,map 在扩容后,会发生 key 的搬迁,原来落在同一个 bucket 中的 key,搬迁后,有些 key 可能就到其他 bucket 了。 36 | 37 | 而遍历的过程,就是按顺序遍历 bucket,同时按顺序遍历 bucket 中的 key。 38 | 39 | 搬迁后,key 的位置发生了重大的变化,有些 key 被搬走了,有些 key 则原地不动。这样,遍历 map 的结果就不可能按原来的顺序了。 40 | 41 | 最后,也是最有意思的一点。 42 | 43 | 那如果说我已经初始化好了一个 map,并且不对这个 map 做任何操作,也就是不会发生扩容,那遍历顺序是固定的吗? 44 | 45 | 答:也不是。 46 | 47 | Go 杜绝了这种做法,主要是担心程序员会在开发过程中依赖稳定的遍历顺序,因为这是不对的。 48 | 49 | 所以在遍历 map 时,并不是固定地从 0 号 bucket 开始遍历,每次都是从一个随机值序号的 bucket 开始遍历,并且是从这个 bucket 的一个随机序号的 cell 开始遍历。 50 | 51 | ## 如何顺序读取 52 | 53 | 如果希望按照特定顺序遍历 map,可以先将键或值存储到切片中,然后对切片进行排序,最后再遍历切片。 54 | 55 | 改造一下上面的代码,让它按顺序输出: 56 | 57 | ```go 58 | package main 59 | 60 | import ( 61 | "fmt" 62 | "sort" 63 | ) 64 | 65 | func main() { 66 | m := map[string]int{ 67 | "apple": 1, 68 | "banana": 2, 69 | "orange": 3, 70 | } 71 | 72 | // 将 map 中的键存储到切片中 73 | keys := make([]string, 0, len(m)) 74 | for k := range m { 75 | keys = append(keys, k) 76 | } 77 | 78 | // 对切片进行排序 79 | sort.Strings(keys) 80 | 81 | // 按照排序后的顺序遍历 map 82 | for _, k := range keys { 83 | fmt.Printf("key=%s, value=%d\n", k, m[k]) 84 | } 85 | } 86 | ``` 87 | 88 | 在上面的代码中,首先将 map 中的键存储到一个切片中,然后对切片进行排序。 89 | 90 | 最后,按照排序后的顺序遍历 map。这样就可以按照特定顺序输出键值对了。 91 | 92 | 以上就是本文的全部内容,如果觉得还不错的话欢迎**点赞**,**转发**和**关注**,感谢支持。 93 | 94 | *** 95 | 96 | **参考文章:** 97 | 98 | - https://go.dev/blog/maps 99 | - https://golang.design/go-questions/map/unordered/ 100 | 101 | **推荐阅读:** 102 | 103 | * [Go 语言 map 是并发安全的吗?](https://mp.weixin.qq.com/s/4mDzMdMbunR_p94Du65QOA) 104 | * [Go 语言切片是如何扩容的?](https://mp.weixin.qq.com/s/VVM8nqs4mMGdFyCNJx16_g) 105 | * [Go 语言数组和切片的区别](https://mp.weixin.qq.com/s/esaAmAdmV4w3_qjtAzTr4A) 106 | * [Go 语言 new 和 make 关键字的区别](https://mp.weixin.qq.com/s/NBDkI3roHgNgW1iW4e_6cA) 107 | * [为什么 Go 不支持 \[\]T 转换为 \[\]interface](https://mp.weixin.qq.com/s/cwDEgnicK4jkuNpzulU2bw) 108 | * [为什么 Go 语言 struct 要使用 tags](https://mp.weixin.qq.com/s/L7-TJ-CzYfuVrIBWP7Ebaw) 109 | -------------------------------------------------------------------------------- /Go/Go 语言 new 和 make 关键字的区别.md: -------------------------------------------------------------------------------- 1 | **原文链接:** [Go 语言 new 和 make 关键字的区别](https://mp.weixin.qq.com/s/NBDkI3roHgNgW1iW4e_6cA) 2 | 3 | 本篇文章来介绍一道非常常见的面试题,到底有多常见呢?可能很多面试的开场白就是由此开始的。那就是 new 和 make 这两个内置函数的区别。 4 | 5 | 其实这个问题本身并不复杂,简单来说就是,new 只分配内存,而 make 只能用于 slice、map 和 chan 的初始化,下面我们就来详细介绍一下。 6 | 7 | ## new 8 | 9 | new 是一个内置函数,它会分配一段内存,并返回指向该内存的指针。 10 | 11 | 其函数签名如下: 12 | 13 | ### 源码 14 | 15 | ```go 16 | // The new built-in function allocates memory. The first argument is a type, 17 | // not a value, and the value returned is a pointer to a newly 18 | // allocated zero value of that type. 19 | func new(Type) *Type 20 | ``` 21 | 22 | 从上面的代码可以看出,new 函数只接受一个参数,这个参数是一个类型,并且返回一个指向该类型内存地址的指针。 23 | 24 | 同时 new 函数会把分配的内存置为零,也就是类型的零值。 25 | 26 | ### 使用 27 | 28 | 使用 new 函数为变量分配内存空间: 29 | 30 | ```go 31 | p1 := new(int) 32 | fmt.Printf("p1 --> %#v \n ", p1) //(*int)(0xc42000e250) 33 | fmt.Printf("p1 point to --> %#v \n ", *p1) //0 34 | 35 | var p2 *int 36 | i := 0 37 | p2 = &i 38 | fmt.Printf("p2 --> %#v \n ", p2) //(*int)(0xc42000e278) 39 | fmt.Printf("p2 point to --> %#v \n ", *p2) //0 40 | ``` 41 | 42 | 上面的代码是等价的,`new(int)` 将分配的空间初始化为 int 的零值,也就是 0,并返回 int 的指针,这和直接声明指针并初始化的效果是相同的。 43 | 44 | 当然,new 函数不仅能够为系统默认的数据类型分配空间,自定义类型也可以使用 new 函数来分配空间,如下所示: 45 | 46 | ```go 47 | type Student struct { 48 | name string 49 | age int 50 | } 51 | var s *Student 52 | s = new(Student) //分配空间 53 | s.name = "zhangsan" 54 | fmt.Println(s) 55 | ``` 56 | 57 | 这就是 new 函数,它返回的永远是类型的指针,指针指向分配类型的内存地址。需要注意的是,new 函数只会分配内存空间,但并不会初始化该内存空间。 58 | 59 | ## make 60 | 61 | make 也是用于内存分配的,但是和 new 不同,它只用于 slice、map 和 chan 的内存创建,而且它返回的类型就是这三个类型本身,而不是他们的指针类型。因为这三种类型本身就是引用类型,所以就没有必要返回他们的指针了。 62 | 63 | 其函数签名如下: 64 | 65 | ### 源码 66 | 67 | ```go 68 | // The make built-in function allocates and initializes an object of type 69 | // slice, map, or chan (only). Like new, the first argument is a type, not a 70 | // value. Unlike new, make's return type is the same as the type of its 71 | // argument, not a pointer to it. The specification of the result depends on 72 | // the type: 73 | // Slice: The size specifies the length. The capacity of the slice is 74 | // equal to its length. A second integer argument may be provided to 75 | // specify a different capacity; it must be no smaller than the 76 | // length, so make([]int, 0, 10) allocates a slice of length 0 and 77 | // capacity 10. 78 | // Map: An empty map is allocated with enough space to hold the 79 | // specified number of elements. The size may be omitted, in which case 80 | // a small starting size is allocated. 81 | // Channel: The channel's buffer is initialized with the specified 82 | // buffer capacity. If zero, or the size is omitted, the channel is 83 | // unbuffered. 84 | func make(t Type, size ...IntegerType) Type 85 | ``` 86 | 87 | 通过上面的代码可以看出 make 函数的 `t` 参数必须是 slice、map 和 chan 中的一个,并且返回值也是类型本身。 88 | 89 | ### 使用 90 | 91 | 下面用 slice 来举一个例子: 92 | 93 | ```go 94 | var s1 []int 95 | if s1 == nil { 96 | fmt.Printf("s1 is nil --> %#v \n ", s1) // []int(nil) 97 | } 98 | 99 | s2 := make([]int, 3) 100 | if s2 == nil { 101 | fmt.Printf("s2 is nil --> %#v \n ", s2) 102 | } else { 103 | fmt.Printf("s2 is not nill --> %#v \n ", s2)// []int{0, 0, 0} 104 | } 105 | ``` 106 | 107 | slice 的零值是 `nil`,但使用 make 初始化之后,slice 内容被类型 int 的零值填充,如:`[]int{0, 0, 0}`。 108 | 109 | map 和 chan 也是类似的,就不多说了。 110 | 111 | ## 总结 112 | 113 | 通过以上分析,总结一下 new 和 make 主要区别如下: 114 | 115 | 1. make 只能用来分配及初始化类型为 slice、map 和 chan 的数据。new 可以分配任意类型的数据; 116 | 2. new 分配返回的是指针,即类型 `*Type`。make 返回类型本身,即 `Type`; 117 | 3. new 分配的空间被清零。make 分配空间后,会进行初始化; 118 | 119 | 120 | 以上就是本文的全部内容,如果觉得还不错的话欢迎**点赞**,**转发**和**关注**,感谢支持。 121 | 122 | *** 123 | 124 | **参考文章:** 125 | 126 | - https://go.dev/doc/effective_go#allocation_new 127 | - http://c.biancheng.net/view/5722.html 128 | - https://sanyuesha.com/2017/07/26/go-make-and-new/ 129 | 130 | **推荐阅读:** 131 | 132 | - [为什么 Go 不支持 []T 转换为 []interface](https://mp.weixin.qq.com/s/cwDEgnicK4jkuNpzulU2bw) 133 | - [为什么 Go 语言 struct 要使用 tags](https://mp.weixin.qq.com/s/L7-TJ-CzYfuVrIBWP7Ebaw) -------------------------------------------------------------------------------- /Go/Go 语言数组和切片的区别.md: -------------------------------------------------------------------------------- 1 | **原文链接:** [Go 语言数组和切片的区别](https://mp.weixin.qq.com/s/esaAmAdmV4w3_qjtAzTr4A) 2 | 3 | 在 Go 语言中,数组和切片看起来很像,但其实它们又有很多的不同之处,这篇文章就来说说它们到底有哪些不同。 4 | 5 | 另外,这个问题在面试中也经常会被问到,属于入门级题目,看过文章之后,相信你会有一个很好的答案。 6 | 7 | ## 数组 8 | 9 | 数组是同一种数据类型元素的集合,数组在定义时需要指定长度和元素类型。 10 | 11 | ![](https://cdn.jsdelivr.net/gh/yongxinz/picb@main/data/array.png) 12 | 13 | 例如:`[4]int` 表示一个包含四个整数的数组,数组的大小是固定的。并且长度是其类型的一部分(`[4]int` 和 `[5]int` 是不同的、不兼容的类型)。 14 | 15 | 数组元素可以通过索引来访问,比如表达式 `s[n]` 表示访问第 `n` 个元素,索引从零开始。 16 | 17 | ### 声明以及初始化 18 | 19 | ```go 20 | func main() { 21 | var nums [3]int // 声明并初始化为默认零值 22 | var nums1 = [4]int{1, 2, 3, 4} // 声明同时初始化 23 | var nums2 = [...]int{1, 2, 3, 4, 5} // ...可以表示后面初始化值的长度 24 | fmt.Println(nums) // [0 0 0] 25 | fmt.Println(nums1) // [1 2 3 4] 26 | fmt.Println(nums2) // [1 2 3 4 5] 27 | } 28 | ``` 29 | 30 | ### 函数参数 31 | 32 | 如果数组作为函数的参数,那么实际传递的是一份数组的拷贝,而不是数组的指针。这也就意味着,在函数中修改数组的元素是不会影响到原始数组的。 33 | 34 | ![](https://cdn.jsdelivr.net/gh/yongxinz/picb@main/data/arrayparams.png) 35 | 36 | ```go 37 | package main 38 | 39 | import ( 40 | "fmt" 41 | ) 42 | 43 | func Add(numbers [5]int) { 44 | for i := 0; i < len(numbers); i++ { 45 | numbers[i] = numbers[i] + 1 46 | } 47 | fmt.Println("numbers in Add:", numbers) // [2 3 4 5 6] 48 | } 49 | 50 | func main() { 51 | // declare and initialize the array 52 | var numbers [5]int 53 | for i := 0; i < len(numbers); i++ { 54 | numbers[i] = i + 1 55 | } 56 | 57 | Add(numbers) 58 | fmt.Println("numbers in main:", numbers) // [1 2 3 4 5] 59 | } 60 | ``` 61 | 62 | ## 切片 63 | 64 | 数组的使用场景相对有限,切片才更加常用。 65 | 66 | 切片(Slice)是一个拥有相同类型元素的可变长度的序列。它是基于数组类型做的一层封装。它非常灵活,支持自动扩容。 67 | 68 | ![](https://cdn.jsdelivr.net/gh/yongxinz/picb@main/data/slice.png) 69 | 70 | 切片是一种引用类型,它有三个属性:**指针**,**长度**和**容量**。 71 | 72 | 1. 指针:指向 slice 可以访问到的第一个元素。 73 | 2. 长度:slice 中元素个数。 74 | 3. 容量:slice 起始元素到底层数组最后一个元素间的元素个数。 75 | 76 | 底层源码定义如下: 77 | 78 | ```go 79 | type slice struct { 80 | array unsafe.Pointer 81 | len int 82 | cap int 83 | } 84 | ``` 85 | 86 | ### 声明以及初始化 87 | 88 | ```go 89 | func main() { 90 | var nums []int // 声明切片 91 | fmt.Println(len(nums), cap(nums)) // 0 0 92 | nums = append(nums, 1) // 初始化 93 | fmt.Println(len(nums), cap(nums)) // 1 1 94 | 95 | nums1 := []int{1,2,3,4} // 声明并初始化 96 | fmt.Println(len(nums1), cap(nums1)) // 4 4 97 | 98 | nums2 := make([]int,3,5) // 使用make()函数构造切片 99 | fmt.Println(len(nums2), cap(nums2)) // 3 5 100 | } 101 | ``` 102 | 103 | ### 函数参数 104 | 105 | 当切片作为函数参数时,和数组是不同的,如果一个函数接受一个切片参数,它对切片元素所做的更改将对调用者可见,类似于将指针传递给了底层数组。 106 | 107 | ```go 108 | package main 109 | 110 | import ( 111 | "fmt" 112 | ) 113 | 114 | func Add(numbers []int) { 115 | for i := 0; i < len(numbers); i++ { 116 | numbers[i] = numbers[i] + 1 117 | } 118 | fmt.Println("numbers in Add:", numbers) // [2 3 4 5 6] 119 | } 120 | 121 | func main() { 122 | var numbers []int 123 | for i := 0; i < 5; i++ { 124 | numbers = append(numbers, i+1) 125 | } 126 | 127 | Add(numbers) 128 | 129 | fmt.Println("numbers in main:", numbers) // [2 3 4 5 6] 130 | } 131 | ``` 132 | 133 | 再看一下上面的例子,把参数由数组变成切片,`Add` 函数中的修改会影响到 `main` 函数。 134 | 135 | ## 总结 136 | 137 | 最后来总结一下,面试时也可以这么来回答: 138 | 139 | 1. 数组是一个长度固定的数据类型,其长度在定义时就已经确定,不能动态改变;切片是一个长度可变的数据类型,其长度在定义时可以为空,也可以指定一个初始长度。 140 | 2. 数组的内存空间是在定义时分配的,其大小是固定的;切片的内存空间是在运行时动态分配的,其大小是可变的。 141 | 3. 当数组作为函数参数时,函数操作的是数组的一个副本,不会影响原始数组;当切片作为函数参数时,函数操作的是切片的引用,会影响原始切片。 142 | 4. 切片还有容量的概念,它指的是分配的内存空间。 143 | 144 | 以上就是本文的全部内容,如果觉得还不错的话欢迎**点赞**,**转发**和**关注**,感谢支持。 145 | 146 | *** 147 | 148 | **参考文章:** 149 | 150 | - https://go.dev/doc/effective_go#arrays 151 | - https://go.dev/blog/slices-intro 152 | - https://levelup.gitconnected.com/go-programming-array-vs-slice-5902b7fdd436 153 | 154 | **推荐阅读:** 155 | 156 | - [Go 语言 new 和 make 关键字的区别](https://mp.weixin.qq.com/s/NBDkI3roHgNgW1iW4e_6cA) 157 | - [为什么 Go 不支持 []T 转换为 []interface](https://mp.weixin.qq.com/s/cwDEgnicK4jkuNpzulU2bw) 158 | - [为什么 Go 语言 struct 要使用 tags](https://mp.weixin.qq.com/s/L7-TJ-CzYfuVrIBWP7Ebaw) -------------------------------------------------------------------------------- /Go/q001.md: -------------------------------------------------------------------------------- 1 | ## 交替打印数字和字母 2 | 3 | **问题描述** 4 | 5 | 使用两个 `goroutine` 交替打印序列,一个 `goroutine` 打印数字, 另外一个 `goroutine` 打印字母, 最终效果如下: 6 | 7 | ```bash 8 | 12AB34CD56EF78GH910IJ1112KL1314MN1516OP1718QR1920ST2122UV2324WX2526YZ2728 9 | ``` 10 | 11 | **解题思路** 12 | 13 | 问题很简单,使用 channel 来控制打印的进度。使用两个 channel ,来分别控制数字和字母的打印序列, 数字打印完成后通过 channel 通知字母打印, 字母打印完成后通知数字打印,然后周而复始的工作。 14 | 15 | **源码参考** 16 | 17 | ``` 18 | letter,number := make(chan bool),make(chan bool) 19 | wait := sync.WaitGroup{} 20 | 21 | go func() { 22 | i := 1 23 | for { 24 | select { 25 | case <-number: 26 | fmt.Print(i) 27 | i++ 28 | fmt.Print(i) 29 | i++ 30 | letter <- true 31 | } 32 | } 33 | }() 34 | wait.Add(1) 35 | go func(wait *sync.WaitGroup) { 36 | i := 'A' 37 | for{ 38 | select { 39 | case <-letter: 40 | if i >= 'Z' { 41 | wait.Done() 42 | return 43 | } 44 | 45 | fmt.Print(string(i)) 46 | i++ 47 | fmt.Print(string(i)) 48 | i++ 49 | number <- true 50 | } 51 | 52 | } 53 | }(&wait) 54 | number<-true 55 | wait.Wait() 56 | ``` 57 | 58 | **源码解析** 59 | 60 | 这里用到了两个`channel`负责通知,letter负责通知打印字母的goroutine来打印字母,number用来通知打印数字的goroutine打印数字。 61 | 62 | wait用来等待字母打印完成后退出循环。 63 | -------------------------------------------------------------------------------- /Go/q002.md: -------------------------------------------------------------------------------- 1 | ## 判断字符串中字符是否全都不同 2 | 3 | **问题描述** 4 | 5 | 请实现一个算法,确定一个字符串的所有字符【是否全都不同】。这里我们要求【不允许使用额外的存储结构】。 6 | 给定一个string,请返回一个bool值,true代表所有字符全都不同,false代表存在相同的字符。 7 | 保证字符串中的字符为【ASCII字符】。字符串的长度小于等于【3000】。 8 | 9 | 10 | **解题思路** 11 | 12 | 这里有几个重点,第一个是`ASCII字符`,`ASCII字符`字符一共有256个,其中128个是常用字符,可以在键盘上输入。128之后的是键盘上无法找到的。 13 | 14 | 然后是全部不同,也就是字符串中的字符没有重复的,再次,不准使用额外的储存结构,且字符串小于等于3000。 15 | 16 | 如果允许其他额外储存结构,这个题目很好做。如果不允许的话,可以使用golang内置的方式实现。 17 | 18 | **源码参考** 19 | 20 | 通过`strings.Count` 函数判断: 21 | 22 | ``` 23 | func isUniqueString(s string) bool { 24 | if strings.Count(s,"") > 3000{ 25 | return false 26 | } 27 | for _,v := range s { 28 | if v > 127 { 29 | return false 30 | } 31 | if strings.Count(s,string(v)) > 1 { 32 | return false 33 | } 34 | } 35 | return true 36 | } 37 | ``` 38 | 39 | 通过`strings.Index`和`strings.LastIndex`函数判断: 40 | 41 | ``` 42 | func isUniqueString2(s string) bool { 43 | if strings.Count(s,"") > 3000{ 44 | return false 45 | } 46 | for k,v := range s { 47 | if v > 127 { 48 | return false 49 | } 50 | if strings.Index(s,string(v)) != k { 51 | return false 52 | } 53 | } 54 | return true 55 | } 56 | ``` 57 | 58 | **源码解析** 59 | 60 | 以上两种方法都可以实现这个算法。 61 | 62 | 第一个方法使用的是golang内置方法`strings.Count`,可以用来判断在一个字符串中包含的另外一个字符串的数量。 63 | 64 | 第二个方法使用的是golang内置方法`strings.Index`和`strings.LastIndex`,用来判断指定字符串在另外一个字符串的索引未知,分别是第一次发现位置和最后发现位置。 65 | -------------------------------------------------------------------------------- /Go/q003.md: -------------------------------------------------------------------------------- 1 | ## 翻转字符串 2 | 3 | **问题描述** 4 | 5 | 请实现一个算法,在不使用【额外数据结构和储存空间】的情况下,翻转一个给定的字符串(可以使用单个过程变量)。 6 | 7 | 给定一个string,请返回一个string,为翻转后的字符串。保证字符串的长度小于等于5000。 8 | 9 | **解题思路** 10 | 11 | 翻转字符串其实是将一个字符串以中间字符为轴,前后翻转,即将str[len]赋值给str[0],将str[0] 赋值 str[len]。 12 | 13 | **源码参考** 14 | 15 | ``` 16 | func reverString(s string) (string, bool) { 17 | str := []rune(s) 18 | l := len(str) 19 | if l > 5000 { 20 | return s, false 21 | } 22 | for i := 0; i < l/2; i++ { 23 | str[i], str[l-1-i] = str[l-1-i], str[i] 24 | } 25 | return string(str), true 26 | } 27 | ``` 28 | 29 | **源码解析** 30 | 31 | 以字符串长度的1/2为轴,前后赋值 32 | -------------------------------------------------------------------------------- /Go/q004.md: -------------------------------------------------------------------------------- 1 | ## 判断两个给定的字符串排序后是否一致 2 | 3 | **问题描述** 4 | 5 | 给定两个字符串,请编写程序,确定其中一个字符串的字符重新排列后,能否变成另一个字符串。 6 | 这里规定【大小写为不同字符】,且考虑字符串重点空格。给定一个string s1和一个string s2,请返回一个bool,代表两串是否重新排列后可相同。 7 | 保证两串的长度都小于等于5000。 8 | 9 | **解题思路** 10 | 11 | 首先要保证字符串长度小于5000。之后只需要一次循环遍历s1中的字符在s2是否都存在即可。 12 | 13 | **源码参考** 14 | 15 | ``` 16 | func isRegroup(s1,s2 string) bool { 17 | sl1 := len([]rune(s1)) 18 | sl2 := len([]rune(s2)) 19 | 20 | if sl1 > 5000 || sl2 > 5000 || sl1 != sl2{ 21 | return false 22 | } 23 | 24 | for _,v := range s1 { 25 | if strings.Count(s1,string(v)) != strings.Count(s2,string(v)) { 26 | return false 27 | } 28 | } 29 | return true 30 | } 31 | ``` 32 | 33 | **源码解析** 34 | 35 | 这里还是使用golang内置方法 `strings.Count` 来判断字符是否一致。 36 | -------------------------------------------------------------------------------- /Go/q005.md: -------------------------------------------------------------------------------- 1 | ## 字符串替换问题 2 | 3 | **问题描述** 4 | 5 | 请编写一个方法,将字符串中的空格全部替换为“%20”。 6 | 假定该字符串有足够的空间存放新增的字符,并且知道字符串的真实长度(小于等于1000),同时保证字符串由【大小写的英文字母组成】。 7 | 给定一个string为原始的串,返回替换后的string。 8 | 9 | **解题思路** 10 | 11 | 两个问题,第一个是只能是英文字母,第二个是替换空格。 12 | 13 | **源码参考** 14 | 15 | ``` 16 | func replaceBlank(s string) (string, bool) { 17 | if len([]rune(s)) > 1000 { 18 | return s, false 19 | } 20 | for _, v := range s { 21 | if string(v) != " " && unicode.IsLetter(v) == false { 22 | return s, false 23 | } 24 | } 25 | return strings.Replace(s, " ", "%20", -1), true 26 | } 27 | ``` 28 | 29 | **源码解析** 30 | 31 | 这里使用了golang内置方法`unicode.IsLetter`判断字符是否是字母,之后使用`strings.Replace`来替换空格。 -------------------------------------------------------------------------------- /Go/q006.md: -------------------------------------------------------------------------------- 1 | ## 机器人坐标问题 2 | 3 | **问题描述** 4 | 5 | 有一个机器人,给一串指令,L左转 R右转,F前进一步,B后退一步,问最后机器人的坐标,最开始,机器人位于 0 0,方向为正Y。 6 | 可以输入重复指令n : 比如 R2(LF) 这个等于指令 RLFLF。 7 | 问最后机器人的坐标是多少? 8 | 9 | **解题思路** 10 | 11 | 这里的一个难点是解析重复指令。主要指令解析成功,计算坐标就简单了。 12 | 13 | **源码参考** 14 | 15 | ``` 16 | package main 17 | 18 | import ( 19 | "unicode" 20 | ) 21 | 22 | const ( 23 | Left = iota 24 | Top 25 | Right 26 | Bottom 27 | ) 28 | 29 | func main() { 30 | println(move("R2(LF)", 0, 0, Top)) 31 | } 32 | 33 | func move(cmd string, x0 int, y0 int, z0 int) (x, y, z int) { 34 | x, y, z = x0, y0, z0 35 | repeat := 0 36 | repeatCmd := "" 37 | for _, s := range cmd { 38 | switch { 39 | case unicode.IsNumber(s): 40 | repeat = repeat*10 + (int(s) - '0') 41 | case s == ')': 42 | for i := 0; i < repeat; i++ { 43 | x, y, z = move(repeatCmd, x, y, z) 44 | } 45 | repeat = 0 46 | repeatCmd = "" 47 | case repeat > 0 && s != '(' && s != ')': 48 | repeatCmd = repeatCmd + string(s) 49 | case s == 'L': 50 | z = (z + 1) % 4 51 | case s == 'R': 52 | z = (z - 1 + 4) % 4 53 | case s == 'F': 54 | switch { 55 | case z == Left || z == Right: 56 | x = x - z + 1 57 | case z == Top || z == Bottom: 58 | y = y - z + 2 59 | } 60 | case s == 'B': 61 | switch { 62 | case z == Left || z == Right: 63 | x = x + z - 1 64 | case z == Top || z == Bottom: 65 | y = y + z - 2 66 | } 67 | } 68 | } 69 | return 70 | } 71 | 72 | ``` 73 | 74 | **源码解析** 75 | 76 | 这里使用三个值表示机器人当前的状况,分别是:x表示x坐标,y表示y坐标,z表示当前方向。 77 | L、R 命令会改变值z,F、B命令会改变值x、y。 78 | 值x、y的改变还受当前的z值影响。 79 | 80 | 如果是重复指令,那么将重复次数和重复的指令存起来递归调用即可。 81 | -------------------------------------------------------------------------------- /Go/q009.md: -------------------------------------------------------------------------------- 1 | # 在 golang 协程和channel配合使用 2 | 3 | > 写代码实现两个 goroutine,其中一个产生随机数并写入到 go channel 中,另外一个从 channel 中读取数字并打印到标准输出。最终输出五个随机数。 4 | 5 | **解析** 6 | 7 | 这是一道很简单的golang基础题目,实现方法也有很多种,一般想答让面试官满意的答案还是有几点注意的地方。 8 | 9 | 1. `goroutine` 在golang中式非阻塞的 10 | 2. `channel` 无缓冲情况下,读写都是阻塞的,且可以用`for`循环来读取数据,当管道关闭后,`for` 退出。 11 | 3. golang 中有专用的`select case` 语法从管道读取数据。 12 | 13 | 示例代码如下: 14 | 15 | ```go 16 | func main() { 17 | out := make(chan int) 18 | wg := sync.WaitGroup{} 19 | wg.Add(2) 20 | go func() { 21 | defer wg.Done() 22 | for i := 0; i < 5; i++ { 23 | out <- rand.Intn(5) 24 | } 25 | close(out) 26 | }() 27 | go func() { 28 | defer wg.Done() 29 | for i := range out { 30 | fmt.Println(i) 31 | } 32 | }() 33 | wg.Wait() 34 | } 35 | ``` -------------------------------------------------------------------------------- /Go/q010.md: -------------------------------------------------------------------------------- 1 | # 实现阻塞读且并发安全的map 2 | 3 | GO里面MAP如何实现key不存在 get操作等待 直到key存在或者超时,保证并发安全,且需要实现以下接口: 4 | 5 | ```go 6 | type sp interface { 7 | Out(key string, val interface{}) //存入key /val,如果该key读取的goroutine挂起,则唤醒。此方法不会阻塞,时刻都可以立即执行并返回 8 | Rd(key string, timeout time.Duration) interface{} //读取一个key,如果key不存在阻塞,等待key存在或者超时 9 | } 10 | ``` 11 | 12 | **解析:** 13 | 14 | 看到阻塞协程第一个想到的就是`channel`,题目中要求并发安全,那么必须用锁,还要实现多个`goroutine`读的时候如果值不存在则阻塞,直到写入值,那么每个键值需要有一个阻塞`goroutine` 的 `channel`。 15 | 16 | [实现如下:](../src/q010.go) 17 | 18 | ```go 19 | type Map struct { 20 | c map[string]*entry 21 | rmx *sync.RWMutex 22 | } 23 | type entry struct { 24 | ch chan struct{} 25 | value interface{} 26 | isExist bool 27 | } 28 | 29 | func (m *Map) Out(key string, val interface{}) { 30 | m.rmx.Lock() 31 | defer m.rmx.Unlock() 32 | item, ok := m.c[key] 33 | if !ok { 34 | m.c[key] = &entry{ 35 | value: val, 36 | isExist: true, 37 | } 38 | return 39 | } 40 | item.value = val 41 | if !item.isExist { 42 | if item.ch != nil { 43 | close(item.ch) 44 | item.ch = nil 45 | } 46 | } 47 | return 48 | } 49 | ``` 50 | -------------------------------------------------------------------------------- /Go/q011.md: -------------------------------------------------------------------------------- 1 | # 高并发下的锁与map的读写 2 | 3 | 场景:在一个高并发的web服务器中,要限制IP的频繁访问。现模拟100个IP同时并发访问服务器,每个IP要重复访问1000次。 4 | 5 | 每个IP三分钟之内只能访问一次。修改以下代码完成该过程,要求能成功输出 success:100 6 | 7 | ```go 8 | package main 9 | 10 | import ( 11 | "fmt" 12 | "time" 13 | ) 14 | 15 | type Ban struct { 16 | visitIPs map[string]time.Time 17 | } 18 | 19 | func NewBan() *Ban { 20 | return &Ban{visitIPs: make(map[string]time.Time)} 21 | } 22 | func (o *Ban) visit(ip string) bool { 23 | if _, ok := o.visitIPs[ip]; ok { 24 | return true 25 | } 26 | o.visitIPs[ip] = time.Now() 27 | return false 28 | } 29 | func main() { 30 | success := 0 31 | ban := NewBan() 32 | for i := 0; i < 1000; i++ { 33 | for j := 0; j < 100; j++ { 34 | go func() { 35 | ip := fmt.Sprintf("192.168.1.%d", j) 36 | if !ban.visit(ip) { 37 | success++ 38 | } 39 | }() 40 | } 41 | 42 | } 43 | fmt.Println("success:", success) 44 | } 45 | ``` 46 | 47 | **解析** 48 | 49 | 该问题主要考察了并发情况下map的读写问题,而给出的初始代码,又存在`for`循环中启动`goroutine`时变量使用问题以及`goroutine`执行滞后问题。 50 | 51 | 因此,首先要保证启动的`goroutine`得到的参数是正确的,然后保证`map`的并发读写,最后保证三分钟只能访问一次。 52 | 53 | 多CPU核心下修改`int`的值极端情况下会存在不同步情况,因此需要原子性的修改int值。 54 | 55 | 下面给出的实例代码,是启动了一个协程每分钟检查一下`map`中的过期`ip`,`for`启动协程时传参。 56 | 57 | ```go 58 | package main 59 | 60 | import ( 61 | "context" 62 | "fmt" 63 | "sync" 64 | "sync/atomic" 65 | "time" 66 | ) 67 | 68 | type Ban struct { 69 | visitIPs map[string]time.Time 70 | lock sync.Mutex 71 | } 72 | 73 | func NewBan(ctx context.Context) *Ban { 74 | o := &Ban{visitIPs: make(map[string]time.Time)} 75 | go func() { 76 | timer := time.NewTimer(time.Minute * 1) 77 | for { 78 | select { 79 | case <-timer.C: 80 | o.lock.Lock() 81 | for k, v := range o.visitIPs { 82 | if time.Now().Sub(v) >= time.Minute*1 { 83 | delete(o.visitIPs, k) 84 | } 85 | } 86 | o.lock.Unlock() 87 | timer.Reset(time.Minute * 1) 88 | case <-ctx.Done(): 89 | return 90 | } 91 | } 92 | }() 93 | return o 94 | } 95 | func (o *Ban) visit(ip string) bool { 96 | o.lock.Lock() 97 | defer o.lock.Unlock() 98 | if _, ok := o.visitIPs[ip]; ok { 99 | return true 100 | } 101 | o.visitIPs[ip] = time.Now() 102 | return false 103 | } 104 | func main() { 105 | success := int64(0) 106 | ctx, cancel := context.WithCancel(context.Background()) 107 | defer cancel() 108 | 109 | ban := NewBan(ctx) 110 | 111 | wait := &sync.WaitGroup{} 112 | 113 | wait.Add(1000 * 100) 114 | for i := 0; i < 1000; i++ { 115 | for j := 0; j < 100; j++ { 116 | go func(j int) { 117 | defer wait.Done() 118 | ip := fmt.Sprintf("192.168.1.%d", j) 119 | if !ban.visit(ip) { 120 | atomic.AddInt64(&success, 1) 121 | } 122 | }(j) 123 | } 124 | 125 | } 126 | wait.Wait() 127 | 128 | fmt.Println("success:", success) 129 | } 130 | ``` -------------------------------------------------------------------------------- /Go/q012.md: -------------------------------------------------------------------------------- 1 | ## 1. 写出以下逻辑,要求每秒钟调用一次proc并保证程序不退出? 2 | 3 | ```go 4 | package main 5 | 6 | func main() { 7 | go func() { 8 | // 1 在这里需要你写算法 9 | // 2 要求每秒钟调用一次proc函数 10 | // 3 要求程序不能退出 11 | }() 12 | 13 | select {} 14 | } 15 | 16 | func proc() { 17 | panic("ok") 18 | } 19 | ``` 20 | 21 | **解析** 22 | 23 | 题目主要考察了两个知识点: 24 | 25 | 1. 定时执行执行任务 26 | 2. 捕获 panic 错误 27 | 28 | 题目中要求每秒钟执行一次,首先想到的就是 `time.Ticker`对象,该函数可每秒钟往`chan`中放一个`Time`,正好符合我们的要求。 29 | 30 | 在 `golang` 中捕获 `panic` 一般会用到 `recover()` 函数。 31 | 32 | ```go 33 | package main 34 | 35 | import ( 36 | "fmt" 37 | "time" 38 | ) 39 | 40 | func main() { 41 | go func() { 42 | // 1 在这里需要你写算法 43 | // 2 要求每秒钟调用一次proc函数 44 | // 3 要求程序不能退出 45 | 46 | t := time.NewTicker(time.Second * 1) 47 | for { 48 | select { 49 | case <-t.C: 50 | go func() { 51 | defer func() { 52 | if err := recover(); err != nil { 53 | fmt.Println(err) 54 | } 55 | }() 56 | proc() 57 | }() 58 | } 59 | } 60 | }() 61 | 62 | select {} 63 | } 64 | 65 | func proc() { 66 | panic("ok") 67 | } 68 | 69 | ``` -------------------------------------------------------------------------------- /Go/q013.md: -------------------------------------------------------------------------------- 1 | ## 为 sync.WaitGroup 中Wait函数支持 WaitTimeout 功能. 2 | 3 | ```go 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | "sync" 9 | "time" 10 | ) 11 | 12 | func main() { 13 | wg := sync.WaitGroup{} 14 | c := make(chan struct{}) 15 | for i := 0; i < 10; i++ { 16 | wg.Add(1) 17 | go func(num int, close <-chan struct{}) { 18 | defer wg.Done() 19 | <-close 20 | fmt.Println(num) 21 | }(i, c) 22 | } 23 | 24 | if WaitTimeout(&wg, time.Second*5) { 25 | close(c) 26 | fmt.Println("timeout exit") 27 | } 28 | time.Sleep(time.Second * 10) 29 | } 30 | 31 | func WaitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool { 32 | // 要求手写代码 33 | // 要求sync.WaitGroup支持timeout功能 34 | // 如果timeout到了超时时间返回true 35 | // 如果WaitGroup自然结束返回false 36 | } 37 | ``` 38 | 39 | 40 | **解析** 41 | 42 | 首先 `sync.WaitGroup` 对象的 `Wait` 函数本身是阻塞的,同时,超时用到的`time.Timer` 对象也需要阻塞的读。 43 | 44 | 同时阻塞的两个对象肯定要每个启动一个协程,每个协程去处理一个阻塞,难点在于怎么知道哪个阻塞先完成。 45 | 46 | 目前我用的方式是声明一个没有缓冲的`chan`,谁先完成谁优先向管道中写入数据。 47 | 48 | ```go 49 | package main 50 | 51 | import ( 52 | "fmt" 53 | "sync" 54 | "time" 55 | ) 56 | 57 | func main() { 58 | wg := sync.WaitGroup{} 59 | c := make(chan struct{}) 60 | for i := 0; i < 10; i++ { 61 | wg.Add(1) 62 | go func(num int, close <-chan struct{}) { 63 | defer wg.Done() 64 | <-close 65 | fmt.Println(num) 66 | }(i, c) 67 | } 68 | 69 | if WaitTimeout(&wg, time.Second*5) { 70 | close(c) 71 | fmt.Println("timeout exit") 72 | } 73 | time.Sleep(time.Second * 10) 74 | } 75 | 76 | func WaitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool { 77 | // 要求手写代码 78 | // 要求sync.WaitGroup支持timeout功能 79 | // 如果timeout到了超时时间返回true 80 | // 如果WaitGroup自然结束返回false 81 | ch := make(chan bool, 1) 82 | 83 | go time.AfterFunc(timeout, func() { 84 | ch <- true 85 | }) 86 | 87 | go func() { 88 | wg.Wait() 89 | ch <- false 90 | }() 91 | 92 | return <- ch 93 | } 94 | ``` 95 | -------------------------------------------------------------------------------- /Go/q014.md: -------------------------------------------------------------------------------- 1 | # 语法找错题 2 | 3 | ## 写出以下代码出现的问题 4 | 5 | ```go 6 | package main 7 | import ( 8 | "fmt" 9 | ) 10 | func main() { 11 | var x string = nil 12 | if x == nil { 13 | x = "default" 14 | } 15 | fmt.Println(x) 16 | } 17 | ``` 18 | 19 | golang 中字符串是不能赋值 `nil` 的,也不能跟 `nil` 比较。 20 | 21 | ## 写出以下打印内容 22 | 23 | ```go 24 | package main 25 | import "fmt" 26 | const ( 27 | a = iota 28 | b = iota 29 | ) 30 | const ( 31 | name = "menglu" 32 | c = iota 33 | d = iota 34 | ) 35 | func main() { 36 | fmt.Println(a) 37 | fmt.Println(b) 38 | fmt.Println(c) 39 | fmt.Println(d) 40 | } 41 | ``` 42 | 43 | ## 找出下面代码的问题 44 | 45 | ```go 46 | package main 47 | import "fmt" 48 | type query func(string) string 49 | 50 | func exec(name string, vs ...query) string { 51 | ch := make(chan string) 52 | fn := func(i int) { 53 | ch <- vs[i](name) 54 | } 55 | for i, _ := range vs { 56 | go fn(i) 57 | } 58 | return <-ch 59 | } 60 | 61 | func main() { 62 | ret := exec("111", func(n string) string { 63 | return n + "func1" 64 | }, func(n string) string { 65 | return n + "func2" 66 | }, func(n string) string { 67 | return n + "func3" 68 | }, func(n string) string { 69 | return n + "func4" 70 | }) 71 | fmt.Println(ret) 72 | } 73 | ``` 74 | 75 | 上面的代码有严重的内存泄漏问题,出错的位置是 `go fn(i)`,实际上代码执行后会启动 4 个协程,但是因为 `ch` 是非缓冲的,只可能有一个协程写入成功。而其他三个协程会一直在后台等待写入。 76 | 77 | ## 写出以下打印结果,并解释下为什么这么打印的。 78 | 79 | ```go 80 | package main 81 | import ( 82 | "fmt" 83 | ) 84 | func main() { 85 | str1 := []string{"a", "b", "c"} 86 | str2 := str1[1:] 87 | str2[1] = "new" 88 | fmt.Println(str1) 89 | str2 = append(str2, "z", "x", "y") 90 | fmt.Println(str1) 91 | } 92 | ``` 93 | 94 | golang 中的切片底层其实使用的是数组。当使用`str1[1:]` 使,`str2` 和 `str1` 底层共享一个数组,这回导致 `str2[1] = "new"` 语句影响 `str1`。 95 | 96 | 而 `append` 会导致底层数组扩容,生成新的数组,因此追加数据后的 `str2` 不会影响 `str1`。 97 | 98 | 但是为什么对 `str2` 复制后影响的确实 `str1` 的第三个元素呢?这是因为切片 `str2` 是从数组的第二个元素开始,`str2` 索引为 1 的元素对应的是 `str1` 索引为 2 的元素。 99 | 100 | ## 写出以下打印结果 101 | 102 | ```go 103 | package main 104 | 105 | import ( 106 | "fmt" 107 | ) 108 | 109 | type Student struct { 110 | Name string 111 | } 112 | 113 | func main() { 114 | fmt.Println(&Student{Name: "menglu"} == &Student{Name: "menglu"}) 115 | fmt.Println(Student{Name: "menglu"} == Student{Name: "menglu"}) 116 | } 117 | ``` 118 | 119 | 个人理解:指针类型比较的是指针地址,非指针类型比较的是每个属性的值。 120 | 121 | ## 写出以下代码的问题 122 | 123 | ```go 124 | package main 125 | 126 | import ( 127 | "fmt" 128 | ) 129 | 130 | func main() { 131 | fmt.Println([...]string{"1"} == [...]string{"1"}) 132 | fmt.Println([]string{"1"} == []string{"1"}) 133 | } 134 | ``` 135 | 136 | 数组只能与相同纬度长度以及类型的其他数组比较,切片之间不能直接比较。。 137 | 138 | ## 下面代码写法有什么问题? 139 | 140 | ```go 141 | package main 142 | import ( 143 | "fmt" 144 | ) 145 | type Student struct { 146 | Age int 147 | } 148 | func main() { 149 | kv := map[string]Student{"menglu": {Age: 21}} 150 | kv["menglu"].Age = 22 151 | s := []Student{{Age: 21}} 152 | s[0].Age = 22 153 | fmt.Println(kv, s) 154 | } 155 | ``` 156 | 157 | golang中的`map` 通过`key`获取到的实际上是两个值,第一个是获取到的值,第二个是是否存在该`key`。因此不能直接通过`key`来赋值对象。 158 | -------------------------------------------------------------------------------- /Go/q016.md: -------------------------------------------------------------------------------- 1 | # 记一道字节跳动的算法面试题 2 | 3 | ## 题目 4 | 5 | 这其实是一道变形的链表反转题,大致描述如下 6 | 给定一个单链表的头节点 head,实现一个调整单链表的函数,使得每K个节点之间为一组进行逆序,并且从链表的尾部开始组起,头部剩余节点数量不够一组的不需要逆序。(不能使用队列或者栈作为辅助) 7 | 8 | **例如:** 9 | 10 | 链表:`1->2->3->4->5->6->7->8->null, K = 3`。那么 `6->7->8`,`3->4->5`,`1->2`各位一组。调整后:`1->2->5->4->3->8->7->6->null`。其中 1,2不调整,因为不够一组。 11 | 12 | **解析** 13 | 14 | 原文: -------------------------------------------------------------------------------- /Go/q017.md: -------------------------------------------------------------------------------- 1 | # 多协程查询切片问题 2 | 3 | ## 题目 4 | 5 | 假设有一个超长的切片,切片的元素类型为int,切片中的元素为乱序排序。限时5秒,使用多个goroutine查找切片中是否存在给定的值,在查找到目标值或者超时后立刻结束所有goroutine的执行。 6 | 7 | 比如,切片 `[23,32,78,43,76,65,345,762,......915,86]`,查找目标值为 345 ,如果切片中存在,则目标值输出`"Found it!"`并立即取消仍在执行查询任务的`goroutine`。 8 | 9 | 如果在超时时间未查到目标值程序,则输出`"Timeout!Not Found"`,同时立即取消仍在执行的查找任务的`goroutine`。 10 | 11 | > 答案: 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Go/q018.md: -------------------------------------------------------------------------------- 1 | # 对已经关闭的的chan进行读写,会怎么样?为什么? 2 | 3 | ## 题目 4 | 5 | 对已经关闭的的 chan 进行读写,会怎么样?为什么? 6 | 7 | ## 回答 8 | 9 | - 读已经关闭的 chan 能一直读到东西,但是读到的内容根据通道内关闭前是否有元素而不同。 10 | - 如果 chan 关闭前,buffer 内有元素还未读 , 会正确读到 chan 内的值,且返回的第二个 bool 值(是否读成功)为 true。 11 | - 如果 chan 关闭前,buffer 内有元素已经被读完,chan 内无值,接下来所有接收的值都会非阻塞直接成功,返回 channel 元素的零值,但是第二个 bool 值一直为 false。 12 | - 写已经关闭的 chan 会 panic 13 | 14 | 15 | ## 示例 16 | 17 | ### 1. 写已经关闭的 chan 18 | 19 | ```go 20 | func main(){ 21 | c := make(chan int,3) 22 | close(c) 23 | c <- 1 24 | } 25 | //输出结果 26 | panic: send on closed channel 27 | 28 | goroutine 1 [running] 29 | main.main() 30 | ... 31 | ``` 32 | 33 | - 注意这个 send on closed channel,待会会提到。 34 | 35 | ### 2. 读已经关闭的 chan 36 | 37 | ```go 38 | package main 39 | import "fmt" 40 | 41 | func main() { 42 | fmt.Println("以下是数值的chan") 43 | ci:=make(chan int,3) 44 | ci<-1 45 | close(ci) 46 | num,ok := <- ci 47 | fmt.Printf("读chan的协程结束,num=%v, ok=%v\n",num,ok) 48 | num1,ok1 := <-ci 49 | fmt.Printf("再读chan的协程结束,num=%v, ok=%v\n",num1,ok1) 50 | num2,ok2 := <-ci 51 | fmt.Printf("再再读chan的协程结束,num=%v, ok=%v\n",num2,ok2) 52 | 53 | fmt.Println("以下是字符串chan") 54 | cs := make(chan string,3) 55 | cs <- "aaa" 56 | close(cs) 57 | str,ok := <- cs 58 | fmt.Printf("读chan的协程结束,str=%v, ok=%v\n",str,ok) 59 | str1,ok1 := <-cs 60 | fmt.Printf("再读chan的协程结束,str=%v, ok=%v\n",str1,ok1) 61 | str2,ok2 := <-cs 62 | fmt.Printf("再再读chan的协程结束,str=%v, ok=%v\n",str2,ok2) 63 | 64 | fmt.Println("以下是结构体chan") 65 | type MyStruct struct{ 66 | Name string 67 | } 68 | cstruct := make(chan MyStruct,3) 69 | cstruct <- MyStruct{Name: "haha"} 70 | close(cstruct) 71 | stru,ok := <- cstruct 72 | fmt.Printf("读chan的协程结束,stru=%v, ok=%v\n",stru,ok) 73 | stru1,ok1 := <-cs 74 | fmt.Printf("再读chan的协程结束,stru=%v, ok=%v\n",stru1,ok1) 75 | stru2,ok2 := <-cs 76 | fmt.Printf("再再读chan的协程结束,stru=%v, ok=%v\n",stru2,ok2) 77 | } 78 | 79 | ``` 80 | 81 | 输出结果 82 | 83 | ```bash 84 | 以下是数值的chan 85 | 读chan的协程结束,num=1, ok=true 86 | 再读chan的协程结束,num=0, ok=false 87 | 再再读chan的协程结束,num=0, ok=false 88 | 以下是字符串chan 89 | 读chan的协程结束,str=aaa, ok=true 90 | 再读chan的协程结束,str=, ok=false 91 | 再再读chan的协程结束,str=, ok=false 92 | 以下是结构体chan 93 | 读chan的协程结束,stru={haha}, ok=true 94 | 再读chan的协程结束,stru=, ok=false 95 | 再再读chan的协程结束,stru=, ok=false 96 | ``` 97 | 98 | 99 | ## 多问一句 100 | 101 | ### 1. 为什么写已经关闭的 `chan` 就会 `panic` 呢? 102 | 103 | ```go 104 | //在 src/runtime/chan.go 105 | func chansend(c *hchan,ep unsafe.Pointer,block bool,callerpc uintptr) bool { 106 | //省略其他 107 | if c.closed != 0 { 108 | unlock(&c.lock) 109 | panic(plainError("send on closed channel")) 110 | } 111 | //省略其他 112 | } 113 | ``` 114 | 115 | - 当 `c.closed != 0` 则为通道关闭,此时执行写,源码提示直接 `panic`,输出的内容就是上面提到的 `"send on closed channel"`。 116 | 117 | ### 2. 为什么读已关闭的 chan 会一直能读到值? 118 | 119 | ```go 120 | func chanrecv(c *hchan,ep unsafe.Pointer,block bool) (selected,received bool) { 121 | //省略部分逻辑 122 | lock(&c.lock) 123 | //当chan被关闭了,而且缓存为空时 124 | //ep 是指 val,ok := <-c 里的val地址 125 | if c.closed != 0 && c.qcount == 0 { 126 | if receenabled { 127 | raceacquire(c.raceaddr()) 128 | } 129 | unlock(&c.lock) 130 | //如果接受之的地址不空,那接收值将获得一个该值类型的零值 131 | //typedmemclr 会根据类型清理响应的内存 132 | //这就解释了上面代码为什么关闭的chan 会返回对应类型的零值 133 | if ep != null { 134 | typedmemclr(c.elemtype,ep) 135 | } 136 | //返回两个参数 selected,received 137 | // 第二个采纳数就是 val,ok := <- c 里的 ok 138 | //也就解释了为什么读关闭的chan会一直返回false 139 | return true,false 140 | } 141 | } 142 | ``` 143 | - `c.closed != 0 && c.qcount == 0` 指通道已经关闭,且缓存为空的情况下(已经读完了之前写到通道里的值) 144 | - 如果接收值的地址 `ep` 不为空 145 | - 那接收值将获得是一个该类型的零值 146 | - `typedmemclr` 会根据类型清理相应地址的内存 147 | - 这就解释了上面代码为什么关闭的 chan 会返回对应类型的零值 148 | 149 | 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /Go/q019.md: -------------------------------------------------------------------------------- 1 | # 简单聊聊内存逃逸? 2 | 3 | ## 问题 4 | 5 | 知道golang的内存逃逸吗?什么情况下会发生内存逃逸? 6 | 7 | ## 回答 8 | 9 | golang程序变量会携带有一组校验数据,用来证明它的整个生命周期是否在运行时完全可知。如果变量通过了这些校验,它就可以在栈上分配。否则就说它 逃逸 了,必须在堆上分配。 10 | 11 | 能引起变量逃逸到堆上的典型情况: 12 | 13 | - **在方法内把局部变量指针返回** 局部变量原本应该在栈中分配,在栈中回收。但是由于返回时被外部引用,因此其生命周期大于栈,则溢出。 14 | - **发送指针或带有指针的值到 channel 中。** 在编译时,是没有办法知道哪个 `goroutine` 会在 `channel` 上接收数据。所以编译器没法知道变量什么时候才会被释放。 15 | - **在一个切片上存储指针或带指针的值。** 一个典型的例子就是 `[]*string` 。这会导致切片的内容逃逸。尽管其后面的数组可能是在栈上分配的,但其引用的值一定是在堆上。 16 | - **slice 的背后数组被重新分配了,因为 append 时可能会超出其容量( cap )。** slice 初始化的地方在编译时是可以知道的,它最开始会在栈上分配。如果切片背后的存储要基于运行时的数据进行扩充,就会在堆上分配。 17 | - **在 interface 类型上调用方法。** 在 interface 类型上调用方法都是动态调度的 —— 方法的真正实现只能在运行时知道。想像一个 io.Reader 类型的变量 r , 调用 r.Read(b) 会使得 r 的值和切片b 的背后存储都逃逸掉,所以会在堆上分配。 18 | 19 | ## 举例 20 | 21 | **通过一个例子加深理解,接下来尝试下怎么通过 `go build -gcflags=-m` 查看逃逸的情况。** 22 | 23 | ```go 24 | package main 25 | import "fmt" 26 | type A struct { 27 | s string 28 | } 29 | // 这是上面提到的 "在方法内把局部变量指针返回" 的情况 30 | func foo(s string) *A { 31 | a := new(A) 32 | a.s = s 33 | return a //返回局部变量a,在C语言中妥妥野指针,但在go则ok,但a会逃逸到堆 34 | } 35 | func main() { 36 | a := foo("hello") 37 | b := a.s + " world" 38 | c := b + "!" 39 | fmt.Println(c) 40 | } 41 | ``` 42 | 43 | 执行go build -gcflags=-m main.go 44 | 45 | ```bash 46 | go build -gcflags=-m main.go 47 | # command-line-arguments 48 | ./main.go:7:6: can inline foo 49 | ./main.go:13:10: inlining call to foo 50 | ./main.go:16:13: inlining call to fmt.Println 51 | /var/folders/45/qx9lfw2s2zzgvhzg3mtzkwzc0000gn/T/go-build409982591/b001/_gomod_.go:6:6: can inline init.0 52 | ./main.go:7:10: leaking param: s 53 | ./main.go:8:10: new(A) escapes to heap 54 | ./main.go:16:13: io.Writer(os.Stdout) escapes to heap 55 | ./main.go:16:13: c escapes to heap 56 | ./main.go:15:9: b + "!" escapes to heap 57 | ./main.go:13:10: main new(A) does not escape 58 | ./main.go:14:11: main a.s + " world" does not escape 59 | ./main.go:16:13: main []interface {} literal does not escape 60 | :1: os.(*File).close .this does not escape 61 | ``` 62 | 63 | - `./main.go:8:10: new(A) escapes to heap` 说明 `new(A)` 逃逸了,符合上述提到的常见情况中的第一种。 64 | - `./main.go:14:11: main a.s + " world" does not escape` 说明 b 变量没有逃逸,因为它只在方法内存在,会在方法结束时被回收。 65 | - `./main.go:15:9: b + "!" escapes to heap` 说明 c 变量逃逸,通过`fmt.Println(a ...interface{})`打印的变量,都会发生逃逸,感兴趣的朋友可以去查查为什么。 66 | 67 | 以上操作其实就叫逃逸分析。下篇文章,跟大家聊聊怎么用一个比较trick的方法使变量不逃逸。方便大家在面试官面前秀一波。 68 | 69 | 70 | >原文 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /Go/q020.md: -------------------------------------------------------------------------------- 1 | # 字符串转成byte数组,会发生内存拷贝吗? 2 | 3 | ## 问题 4 | 5 | 字符串转成byte数组,会发生内存拷贝吗? 6 | 7 | ## 回答 8 | 9 | 字符串转成切片,会产生拷贝。严格来说,只要是发生类型强转都会发生内存拷贝。那么问题来了。 10 | 11 | 频繁的内存拷贝操作听起来对性能不大友好。有没有什么办法可以在字符串转成切片的时候不用发生拷贝呢? 12 | 13 | ## 解释 14 | 15 | ```go 16 | package main 17 | 18 | import ( 19 | "fmt" 20 | "reflect" 21 | "unsafe" 22 | ) 23 | 24 | func main() { 25 | a :="aaa" 26 | ssh := *(*reflect.StringHeader)(unsafe.Pointer(&a)) 27 | b := *(*[]byte)(unsafe.Pointer(&ssh)) 28 | fmt.Printf("%v",b) 29 | } 30 | ``` 31 | 32 | **`StringHeader` 是字符串在go的底层结构。** 33 | 34 | ```go 35 | type StringHeader struct { 36 | Data uintptr 37 | Len int 38 | } 39 | ``` 40 | 41 | **`SliceHeader` 是切片在go的底层结构。** 42 | 43 | ```go 44 | type SliceHeader struct { 45 | Data uintptr 46 | Len int 47 | Cap int 48 | } 49 | ``` 50 | 51 | 那么如果想要在底层转换二者,只需要把 StringHeader 的地址强转成 SliceHeader 就行。那么go有个很强的包叫 unsafe 。 52 | 53 | 1. `unsafe.Pointer(&a)`方法可以得到变量a的地址。 54 | 2. `(*reflect.StringHeader)(unsafe.Pointer(&a))` 可以把字符串a转成底层结构的形式。 55 | 3. `(*[]byte)(unsafe.Pointer(&ssh))` 可以把ssh底层结构体转成byte的切片的指针。 56 | 4. 再通过 `*`转为指针指向的实际内容。 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /Go/q022.md: -------------------------------------------------------------------------------- 1 | # sync.Map 的用法 2 | 3 | ## 问题 4 | 5 | ```go 6 | package main 7 | 8 | import ( 9 | "fmt" 10 | "sync" 11 | ) 12 | 13 | func main(){ 14 | var m sync.Map 15 | m.Store("address",map[string]string{"province":"江苏","city":"南京"}) 16 | v,_ := m.Load("address") 17 | fmt.Println(v["province"]) 18 | } 19 | ``` 20 | 21 | - A,江苏; 22 | - B`,v["province"]`取值错误; 23 | - C,`m.Store`存储错误; 24 | - D,不知道 25 | 26 | ## 解析 27 | 28 | `invalid operation: v["province"] (type interface {} does not support indexing)` 29 | 因为 `func (m *Map) Store(key interface{}, value interface{})` 30 | 所以 `v`类型是 `interface {}` ,这里需要一个类型断言 31 | 32 | ```go 33 | fmt.Println(v.(map[string]string)["province"]) //江苏 34 | ``` 35 | -------------------------------------------------------------------------------- /Go/为什么 Go for-range 的 value 值地址每次都一样?.md: -------------------------------------------------------------------------------- 1 | **原文链接:** [为什么 Go for-range 的 value 值地址每次都一样?](https://mp.weixin.qq.com/s/OoJ42UVYe72492mRUGtdvA) 2 | 3 | 循环语句是一种常用的控制结构,在 Go 语言中,除了 `for` 关键字以外,还有一个 `range` 关键字,可以使用 `for-range` 循环迭代数组、切片、字符串、map 和 channel 这些数据类型。 4 | 5 | 但是在使用 `for-range` 循环迭代数组和切片的时候,是很容易出错的,甚至很多老司机一不小心都会在这里翻车。 6 | 7 | 具体是怎么翻的呢?我们接着看。 8 | 9 | ## 现象 10 | 11 | 先来看两段很有意思的代码: 12 | 13 | ### 无限循环 14 | 15 | 如果我们在遍历数组的同时向数组中添加元素,能否得到一个永远都不会停止的循环呢? 16 | 17 | 比如下面这段代码: 18 | 19 | ```go 20 | func main() { 21 | arr := []int{1, 2, 3} 22 | for _, v := range arr { 23 | arr = append(arr, v) 24 | } 25 | fmt.Println(arr) 26 | } 27 | ``` 28 | 29 | 程序输出: 30 | 31 | ```go 32 | $ go run main.go 33 | 1 2 3 1 2 3 34 | ``` 35 | 36 | 上述代码的输出意味着循环只遍历了原始切片中的三个元素,我们在遍历切片时追加的元素并没有增加循环的执行次数,所以循环最终还是停了下来。 37 | 38 | ### 相同地址 39 | 40 | 第二个例子是使用 Go 语言经常会犯的一个错误。 41 | 42 | 当我们在遍历一个数组时,如果获取 `range` 返回变量的地址并保存到另一个数组或者哈希时,会遇到令人困惑的现象: 43 | 44 | ```go 45 | func main() { 46 | arr := []int{1, 2, 3} 47 | newArr := []*int{} 48 | for _, v := range arr { 49 | newArr = append(newArr, &v) 50 | } 51 | for _, v := range newArr { 52 | fmt.Println(*v) 53 | } 54 | } 55 | ``` 56 | 57 | 程序输出: 58 | 59 | ```go 60 | $ go run main.go 61 | 3 3 3 62 | ``` 63 | 64 | 上述代码并没有输出 `1 2 3`,而是输出 `3 3 3`。 65 | 66 | 正确的做法应该是使用 `&arr[i]` 替代 `&v`,像这种编程中的细节是很容易出错的。 67 | 68 | ## 原因 69 | 70 | 具体原因也并不复杂,一句话就能解释。 71 | 72 | 对于数组、切片或字符串,每次迭代,`for-range` 语句都会将原始值的副本传递给迭代变量,而非原始值本身。 73 | 74 | 口说无凭,具体是不是这样,还得靠源码说话。 75 | 76 | Go 编译器会将 `for-range` 语句转换成类似 C 语言的[**三段式循环**](https://github.com/golang/gofrontend/blob/e387439bfd24d5e142874b8e68e7039f74c744d7/go/statements.cc#L5384)结构,就像这样: 77 | 78 | ```go 79 | // Arrange to do a loop appropriate for the type. We will produce 80 | // for INIT ; COND ; POST { 81 | // ITER_INIT 82 | // INDEX = INDEX_TEMP 83 | // VALUE = VALUE_TEMP // If there is a value 84 | // original statements 85 | // } 86 | ``` 87 | 88 | 迭代[**数组**](https://github.com/golang/gofrontend/blob/e387439bfd24d5e142874b8e68e7039f74c744d7/go/statements.cc#L5501)时,是这样: 89 | 90 | ```go 91 | // The loop we generate: 92 | // len_temp := len(range) 93 | // range_temp := range 94 | // for index_temp = 0; index_temp < len_temp; index_temp++ { 95 | // value_temp = range_temp[index_temp] 96 | // index = index_temp 97 | // value = value_temp 98 | // original body 99 | // } 100 | ``` 101 | 102 | [**切片**](https://github.com/golang/gofrontend/blob/e387439bfd24d5e142874b8e68e7039f74c744d7/go/statements.cc#L5593): 103 | 104 | ```go 105 | // for_temp := range 106 | // len_temp := len(for_temp) 107 | // for index_temp = 0; index_temp < len_temp; index_temp++ { 108 | // value_temp = for_temp[index_temp] 109 | // index = index_temp 110 | // value = value_temp 111 | // original body 112 | // } 113 | ``` 114 | 115 | 从上面的代码片段,可以总结两点: 116 | 117 | 1. 在循环开始前,会将数组或切片赋值给一个新变量,在赋值过程中就发生了拷贝,迭代的实际上是副本,这也就解释了现象 1。 118 | 2. 在循环过程中,会将迭代元素赋值给一个临时变量,这又发生了拷贝。如果取地址的话,每次都是一样的,都是临时变量的地址。 119 | 120 | 以上就是本文的全部内容,如果觉得还不错的话欢迎**点赞**,**转发**和**关注**,感谢支持。 121 | 122 | *** 123 | 124 | **参考文章:** 125 | 126 | - 127 | - 128 | 129 | **推荐阅读:** 130 | 131 | - [为什么 Go 不支持 []T 转换为 []interface](https://mp.weixin.qq.com/s/cwDEgnicK4jkuNpzulU2bw) 132 | - [为什么 Go 语言 struct 要使用 tags](https://mp.weixin.qq.com/s/L7-TJ-CzYfuVrIBWP7Ebaw) -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 yongxinz 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 | -------------------------------------------------------------------------------- /MongoDB/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | - [MongoDB](#mongodb) 4 | - [1.MongoDB中对多条记录做更新操作命令是什么?](#1mongodb%e4%b8%ad%e5%af%b9%e5%a4%9a%e6%9d%a1%e8%ae%b0%e5%bd%95%e5%81%9a%e6%9b%b4%e6%96%b0%e6%93%8d%e4%bd%9c%e5%91%bd%e4%bb%a4%e6%98%af%e4%bb%80%e4%b9%88) 5 | - [2.MongoDB如何才会拓展到多个shard里?](#2mongodb%e5%a6%82%e4%bd%95%e6%89%8d%e4%bc%9a%e6%8b%93%e5%b1%95%e5%88%b0%e5%a4%9a%e4%b8%aashard%e9%87%8c) 6 | 7 | 8 | 9 | ## MongoDB 10 | ### 1.MongoDB中对多条记录做更新操作命令是什么? 11 | ### 2.MongoDB如何才会拓展到多个shard里? 12 | -------------------------------------------------------------------------------- /MySQL/README.md: -------------------------------------------------------------------------------- 1 | ## MySQL 2 | ### 1.事务 3 | 4 | 数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。 5 | 彻底理解数据库事务: http://www.hollischuang.com/archives/898 6 | 7 | ### 2.数据库索引 8 | 9 | [MySQL索引背后的数据结构及算法原理](http://blog.codinglabs.org/articles/theory-of-mysql-index.html) 10 | 11 | [面试中常被提到的最左前缀匹配原则](https://www.cnblogs.com/ljl150/p/12934071.html) 12 | 13 | 聚集索引,非聚集索引,B-Tree,B+Tree,最左前缀原理 14 | 15 | ### 3.乐观锁和悲观锁 16 | 17 | 悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作 18 | 19 | 乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。 20 | 21 | 乐观锁与悲观锁的具体区别: https://juejin.cn/post/6844903639207641096#heading-1 22 | 23 | ### 4.MVCC 24 | 25 | > ​ 全称是Multi-Version Concurrent Control,即多版本并发控制,在MVCC协议下,每个读操作会看到一个一致性的snapshot,并且可以实现非阻塞的读。MVCC允许数据具有多个版本,这个版本可以是时间戳或者是全局递增的事务ID,在同一个时间点,不同的事务看到的数据是不同的。 26 | 27 | ### [MySQL](http://lib.csdn.net/base/mysql)的innodb引擎是如何实现MVCC的 28 | 29 | innodb会为每一行添加两个字段,分别表示该行**创建的版本**和**删除的版本**,填入的是事务的版本号,这个版本号随着事务的创建不断递增。在repeated read的隔离级别([事务的隔离级别请看这篇文章](http://blog.csdn.net/chosen0ne/article/details/10036775))下,具体各种数据库操作的实现: 30 | 31 | - select:满足以下两个条件innodb会返回该行数据: 32 | - 该行的创建版本号小于等于当前版本号,用于保证在select操作之前所有的操作已经执行落地。 33 | - 该行的删除版本号大于当前版本或者为空。删除版本号大于当前版本意味着有一个并发事务将该行删除了。 34 | - insert:将新插入的行的创建版本号设置为当前系统的版本号。 35 | - delete:将要删除的行的删除版本号设置为当前系统的版本号。 36 | - update:不执行原地update,而是转换成insert + delete。将旧行的删除版本号设置为当前版本号,并将新行insert同时设置创建版本号为当前版本号。 37 | 38 | 其中,写操作(insert、delete和update)执行时,需要将系统版本号递增。 39 | 40 | ​由于旧数据并不真正的删除,所以必须对这些数据进行清理,innodb会开启一个后台线程执行清理工作,具体的规则是将删除版本号小于当前系统版本的行删除,这个过程叫做purge。 41 | 42 | 通过MVCC很好的实现了事务的隔离性,可以达到repeated read级别,要实现serializable还必须加锁。 43 | 44 | 参考:[MVCC浅析](http://blog.csdn.net/chosen0ne/article/details/18093187) 45 | 46 | ### 5.MyISAM和InnoDB 47 | 48 | MyISAM 适合于一些需要大量查询的应用,但其对于有大量写操作并不是很好。甚至你只是需要update一个字段,整个表都会被锁起来,而别的进程,就算是读进程都无法操作直到读操作完成。另外,MyISAM 对于 SELECT COUNT(*) 这类的计算是超快无比的。 49 | 50 | InnoDB 的趋势会是一个非常复杂的存储引擎,对于一些小的应用,它会比 MyISAM 还慢。他是它支持“行锁” ,于是在写操作比较多的时候,会更优秀。并且,他还支持更多的高级应用,比如:事务。 51 | 52 | mysql 数据库引擎: http://www.cnblogs.com/0201zcr/p/5296843.html 53 | MySQL存储引擎--MyISAM与InnoDB区别: https://segmentfault.com/a/1190000008227211 54 | 55 | ### 6.主键 超键 候选键 外键 56 | 57 | 主键:数据库表中对存储数据对象予以唯一和完整标识的数据列或属性的组合。一个数据列只能有一个主键,且主键的取值不能缺失,即不能为空值(Null). 58 | 59 | 超键:在关系中能唯一标识元组的属性集称为关系模式的超键。一个属性可以作为一个超键,多个属性组合在一起也可以作为一个超键。超键包含候选键和主键。 60 | 61 | 候选键:是最小超键,即没有冗余元素的超键。 62 | 63 | 外键:在一个表中存在的另一个表的主键称此表的外键。 64 | 65 | ### 7.视图的作用,视图可以更改么? 66 | 67 | 视图是虚拟的表,与包含数据的表不一样,视图只包含使用时动态检索数据的查询;不包含任何列或数据。使用视图可以简化复杂的sql操作,隐藏具体的细节,保护数据;视图创建后,可以使用与表相同的方式利用它们。 68 | 69 | 视图不能被索引,也不能有关联的触发器或默认值,如果视图本身内有order by则对视图再次order by将被覆盖。 70 | 71 | 创建视图: create view xxx as xxxxxx 72 | 73 | 对于某些视图比如未使用联结子查询分组聚集函数Distinct Union等,是可以对其更新的,对视图的更新将对基表进行更新;但是视图主要用于简化检索,保护数据,并不用于更新,而且大部分视图都不可以更新。 74 | 75 | ### 8.drop,delete与truncate的区别 76 | 77 | drop直接删掉表,truncate删除表中数据,再插入时自增长id又从1开始,delete删除表中数据,可以加where字句。 78 | 79 | 1.delete 语句执行删除的过程是每次从表中删除一行,并且同时将该行的删除操作作为事务记录在日志中保存以便进行回滚操作。truncate table则一次性地从表中删除所有的数据并不把单独的删除操作记录记入日志保存,删除行是不能恢复的。并且在删除的过程中不会激活与表有关的删除触发器,执行速度快。 80 | 81 | 2.表和索引所占空间。当表被truncate后,这个表和索引所占用的空间会恢复到初始大小,而delete操作不会减少表或索引所占用的空间。drop语句将表所占用的空间全释放掉。 82 | 83 | 3.一般而言,drop>truncate>delete 84 | 85 | 4.应用范围。truncate只能对table,delete可以是table和view 86 | 87 | 5.truncate和delete只删除数据,而drop则删除整个表(结构和数据) 88 | 89 | 6.truncate与不带where的delete:只删除数据,而不删除表的结构(定义)drop语句将删除表的结构被依赖的约束(constrain),触发器(trigger)索引(index);依赖于该表的存储过程/函数将被保留,但其状态会变为:invalid. 90 | 91 | ### 9.索引的工作原理及其种类 92 | 93 | 数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询,更新数据库表中数据。索引的实现通常使用B树以其变种B+树。 94 | 95 | 在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法。这种数据结构,就是索引。 96 | 97 | 为表设置索引要付出代价的:一是增加了数据库的存储空间,二是在插入和修改数据时要花费较多的时间(因为索引也要随之变动) 98 | 99 | 100 | ### 常用索引数据结构,以及MySQL B 树 B+ 树区别。 101 | 102 | https://mp.weixin.qq.com/s/d7Zfat2fP6IX5DMKKtEIjQ 103 | 104 | 105 | ### 如何对 MySQL 进行服务器扩容。 106 | 107 | https://mp.weixin.qq.com/s/r6usm2DkSC4Qx3FRSoMCNA 108 | 109 | 110 | ### 分库分表相关 111 | 112 | https://mp.weixin.qq.com/s/dGECnPailOkMX476KoTQfg 113 | 114 | ### 隔离级别、MVCC 115 | 116 | https://mp.weixin.qq.com/s/XOBhxc_AiuUxvwBsB_JprQ 117 | 118 | ### MySQL 的锁机制 119 | 120 | https://mp.weixin.qq.com/s/ezBZhZaUqQtASPeT5TKslw 121 | https://mp.weixin.qq.com/s/ZNdBk22Un-7wIYz2fM7-xA 122 | -------------------------------------------------------------------------------- /Redis/README.md: -------------------------------------------------------------------------------- 1 | 2 | ### 1、Redis 是什么? 3 | 4 | 1. 是一个完全开源免费的 key-value 内存数据库 5 | 2. 通常被认为是一个数据结构服务器,主要是因为其有着丰富的数据结构 strings、hash、list、sets、sorted sets 6 | 7 | 通常局限点来说,Redis也以消息队列的形式存在,作为内嵌的List存在,满足实时的高并发需求。在使用缓存的时候,redis比memcached具有更多的优势,并且支持更多的数据类型,把redis当作一个中间存储系统,用来处理高并发的数据库操作。 8 | 9 | - 速度快:使用标准C写,所有数据都在内存中完成,读写速度分别达到10万/20万 10 | - 持久化:对数据的更新采用Copy-on-write技术,可以异步地保存到磁盘上,主要有两种策略,一是根据时间,更新次数的快照(save 300 10 )二是基于语句追加方式(Append-only file,aof) 11 | - 自动操作:对不同数据类型的操作都是自动的,很安全 12 | - 快速的主--从复制,官方提供了一个数据,Slave在21秒即完成了对Amazon网站10G key set的复制。 13 | - Sharding技术:很容易将数据分布到多个Redis实例中,数据库的扩展是个永恒的话题,在关系型数据库中,主要是以添加硬件、以分区为主要技术形式的纵向扩展解决了很多的应用场景,但随着web2.0、移动互联网、云计算等应用的兴起,这种扩展模式已经不太适合了,所以近年来,像采用主从配置、数据库复制形式的,Sharding这种技术把负载分布到多个特定节点上去的横向扩展方式用处越来越多。 14 | 15 | ### 2、Redis 缺点 16 | 17 | - 是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。 18 | - Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。为避免这一问题,运维人员在系统上线时必须确保有足够的空间,这对资源造成了很大的浪费。 19 | 20 | ### 3、Redis宕机怎么解决? 21 | 22 | 宕机:服务器停止服务 23 | 24 | 如果只有一台redis,肯定会造成数据丢失,无法挽救 25 | 26 | 多台redis或者是redis集群,宕机则需要分为在主从模式下区分来看: 27 | 28 | slave从redis宕机,配置主从复制的时候才配置从的redis,从的会从主的redis中读取主的redis的操作日志,在redis中从库重新启动后会自动加入到主从架构中,自动完成同步数据; 29 | 30 | 如果从数据库实现了持久化,此时千万不要立马重启服务,否则可能会造成数据丢失,正确的操作如下:在slave数据上执行SLAVEOF ON ONE,来断开主从关系并把slave升级为主库,此时重新启动主数据库,执行SLAVEOF,把它设置为从库,连接到主的redis上面做主从复制,自动备份数据。 31 | 32 | 以上过程很容易配置错误,可以使用redis提供的哨兵机制来简化上面的操作。简单的方法:redis的哨兵(sentinel)的功能 33 | 34 | ### 4、redis和mecached的区别,以及使用场景 35 | 36 | 区别 37 | 38 | 1、redis和Memcache都是将数据存放在内存中,都是内存数据库。不过memcache还可以用于缓存其他东西,例如图片,视频等等 39 | 40 | 2、Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,hash等数据结构的存储 41 | 42 | 3、虚拟内存-redis当物理内存用完时,可以将一些很久没用的value交换到磁盘 43 | 44 | 4、过期策略-memcache在set时就指定,例如set key1 0 0 8,即永不过期。Redis可以通过例如expire设定,例如expire name 10 45 | 46 | 5、分布式-设定memcache集群,利用magent做一主多从,redis可以做一主多从。都可以一主一丛 47 | 48 | 6、存储数据安全-memcache挂掉后,数据没了,redis可以定期保存到磁盘(持久化) 49 | 50 | 7、灾难恢复-memcache挂掉后,数据不可恢复,redis数据丢失后可以通过aof恢复 51 | 52 | 8、Redis支持数据的备份,即master-slave模式的数据备份 53 | 54 | 9、应用场景不一样,redis除了作为NoSQL数据库使用外,还能用做消息队列,数据堆栈和数据缓存等;Memcache适合于缓存SQL语句,数据集,用户临时性数据,延迟查询数据和session等 55 | 56 | 使用场景 57 | 58 | 1、如果有持久方面的需求或对数据类型和处理有要求的应该选择redis 59 | 60 | 2、如果简单的key/value存储应该选择memcached. 61 | 62 | ### 5、Redis集群方案该怎么做?都有哪些方案? 63 | 64 | 1、redis 目前用的最多的集群方案,基本和twemproxy一致的效果,但它支持在节点数量改变情况下,旧节点数据客恢复到新hash节点 65 | 66 | 2、redis cluster3.0自带的集群,特点在于他的分布式算法不是一致性hash,而是hash槽的概念,以及自身支持节点设置从节点。具体看官方介绍 67 | 68 | 3、在业务代码层实现,起几个毫无关联的redis实例,在代码层,对key进行hash计算,然后去对应的redis实例操作数据。这种方式对hash层代码要求比较高,考虑部分包括,节点失效后的替代算法方案,数据震荡后的字典脚本恢复,实例的监控,等等 69 | 70 | ### 6、Redis回收进程是如何工作的 71 | 72 | 一个客户端运行了新的命令,添加了新的数据。 73 | 74 | redis检查内存使用情况,如果大于maxmemory的限制,则根据设定好的策略进行回收。 75 | 76 | 一个新的命令被执行等等,所以我们不断地穿越内存限制的边界,通过不断达到边界然后不断回收回到边界以下。 77 | 78 | 如果一个命令的结果导致大量内存被使用(例如很大的集合的交集保存到一个新的键),不用多久内存限制就会被这个内存使用量超越。 79 | 80 | ### 7、Redis 跟 MySQL 缓存一致性 81 | 82 | https://zhuanlan.zhihu.com/p/59167071 83 | 84 | ### 8、Redis 的几个基本数据类型,底层实现 85 | 86 | https://zhuanlan.zhihu.com/p/344918922 87 | 88 | ### 9、Redis 为什么那么快 89 | 90 | - https://zhuanlan.zhihu.com/p/160157573 91 | - https://xie.infoq.cn/article/b3816e9fe3ac77684b4f29348 92 | 93 | ### 10、Redis 中常见集群部署情况,出现性能问题如何排查。 94 | 95 | https://mp.weixin.qq.com/s/q79ji-cgfUMo7H0p254QRg 96 | 97 | ### 11、Redis 中的事务。 98 | 99 | https://mp.weixin.qq.com/s/Hevg_4YJT_PzVd1Z_yE1TQ 100 | 101 | ### 12、缓存雪崩、击穿、穿透 102 | 103 | https://mp.weixin.qq.com/s/_StOUX9Nu-Bo8UpX7ThZmg 104 | 105 | ### 13、Redis 的持久化 106 | 107 | https://mp.weixin.qq.com/s/yP2HH8840OMY4e7tKMymiA 108 | 109 | ### 14、Redis不是号称单线程也有很高的性能么?为啥还需要多线程? 110 | 111 | https://mp.weixin.qq.com/s/SYUYvKCxsyMbdBsRrJOZqA 112 | 113 | ### 15、Redis 过期策略和内存淘汰机制 114 | 115 | https://segmentfault.com/a/1190000023060522 116 | 117 | ### 16、Redis 分布式锁怎么用?有什么问题? 118 | 119 | https://mp.weixin.qq.com/s/IoDPieqgY995cyyWAQrQew 120 | 121 | ### 17、Redis为什么变慢了?一文讲透如何排查Redis性能问题 122 | 123 | https://mp.weixin.qq.com/s/Qc4t_-_pL4w8VlSoJhRDcg 124 | -------------------------------------------------------------------------------- /Redis/redis.md: -------------------------------------------------------------------------------- 1 | # Redis 基础 2 | 3 | ## Redis常见的数据结构? 4 | 5 | String、Hash、List、Set、SortedSet。 6 | 7 | ### 1.String 字符串类型 8 | 9 | 是redis中最基本的数据类型,一个key对应一个value。 10 | 11 | String类型是二进制安全的,意思是 redis 的 string 可以包含任何数据。如数字,字符串,jpg图片或者序列化的对象。 12 | 13 | **实战场景:** 14 | 15 | 1. 缓存: 经典使用场景,把常用信息,字符串,图片或者视频等信息放到redis中,redis作为缓存层,mysql做持久化层,降低mysql的读写压力。 16 | 2. 计数器:redis是单线程模型,一个命令执行完才会执行下一个,同时数据可以一步落地到其他的数据源。 17 | 3. session:常见方案spring session + redis实现session共享 18 | 19 | ### 2.Hash (哈希) 20 | 21 | 是一个Mapmap,指值本身又是一种键值对结构,如 value={{field1,value1},......fieldN,valueN}} 22 | 23 | ![](../images/1289934-20190621232209365-1000366002.png) 24 | 25 | **实战场景:** 26 | 27 | 1.缓存: 能直观,相比string更节省空间,的维护缓存信息,如用户信息,视频信息等。 28 | 29 | ### 3.链表 30 | 31 | List 说白了就是链表(redis 使用双端链表实现的 List),是有序的,value可以重复,可以通过下标取出对应的value值,左右两边都能进行插入和删除数据。 32 | 33 | ![](../images/1289934-20190621233618769-504231907.png) 34 | 35 | 使用列表的技巧 36 | 37 | - lpush+lpop=Stack(栈) 38 | - lpush+rpop=Queue(队列) 39 | - lpush+ltrim=Capped Collection(有限集合) 40 | - lpush+brpop=Message Queue(消息队列) 41 | 42 | **实战场景:** 43 | 44 | 1.timeline:例如微博的时间轴,有人发布微博,用lpush加入时间轴,展示新的列表信息。 45 | 46 | ### 4.Set 集合 47 | 48 | 集合类型也是用来保存多个字符串的元素,但和列表不同的是集合中 1. 不允许有重复的元素,2.集合中的元素是无序的,不能通过索引下标获取元素,3.支持集合间的操作,可以取多个集合取交集、并集、差集。 49 | 50 | 51 | ![](../images/1289934-20190622001013515-677922001.png) 52 | 53 | **实战场景;** 54 | 55 | 1. 标签(tag),给用户添加标签,或者用户给消息添加标签,这样有同一标签或者类似标签的可以给推荐关注的事或者关注的人。 56 | 2. 点赞,或点踩,收藏等,可以放到set中实现 57 | 58 | ### 5.zset 有序集合 59 | 60 | 有序集合和集合有着必然的联系,保留了集合不能有重复成员的特性,区别是,有序集合中的元素是可以排序的,它给每个元素设置一个分数,作为排序的依据。 61 | 62 | (有序集合中的元素不可以重复,但是score 分数 可以重复,就和一个班里的同学学号不能重复,但考试成绩可以相同)。 63 | 64 | 65 | ![](../images/1289934-20190622000959260-539243592.png) 66 | 67 | **实战场景:** 68 | 69 | 1. 排行榜:有序集合经典使用场景。例如小说视频等网站需要对用户上传的小说视频做排行榜,榜单可以按照用户关注数,更新时间,字数等打分,做排行。 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /src/array_slice_to_min_num.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | import operator 4 | 5 | if __name__ == "__main__": 6 | test = [3, 32, 321] 7 | 8 | numbers = list(map(str, test)) 9 | numbers.sort(cmp=lambda x, y: operator.eq(x+y, y+x)) 10 | print(''.join(numbers)) 11 | -------------------------------------------------------------------------------- /src/go_cook.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import sys 5 | 6 | 7 | def main(): 8 | x = [] 9 | 10 | # 此处没有用raw_input,是因为只能输入一行,换行,程序就结束; 11 | # sys.stdin想要结束输入,直接用ctrl+d; 12 | for line in sys.stdin: 13 | x.extend(line.split()) 14 | 15 | return len(set(x)) 16 | 17 | 18 | if __name__ == "__main__": 19 | print main() 20 | -------------------------------------------------------------------------------- /中间件/README.md: -------------------------------------------------------------------------------- 1 | ### Kafka 2 | 3 | 1、Kafka 为什么那么快? 4 | 5 | https://mp.weixin.qq.com/s/gmzIcvYvig2cDIcHJAnDRg 6 | 7 | 2、Kafka 重平衡是怎么做的? 8 | 9 | https://mp.weixin.qq.com/s/4DFup_NziFJ1xdc4bZnVcg 10 | 11 | 3、Kafka 架构体系 12 | 13 | https://mp.weixin.qq.com/s/QF9b9vpncLS24xMzFkanTQ 14 | 15 | 16 | ### RabbitMQ 17 | 18 | 1、RabbitMQ 高频考题 19 | 20 | https://mp.weixin.qq.com/s/XEMuc1QJX8TAe7_fqcmJBA 21 | 22 | 2、RabbitMQ 跟 Kafka 对比下,说下对 MQ 23 | 24 | https://mp.weixin.qq.com/s/XtMKQr7TaOjZkXgXZWh6lg -------------------------------------------------------------------------------- /数据结构与算法/两个合并排序的链表.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。 3 | ''' 4 | 5 | ''' 6 | 方法一:递归版本,注意比较很好理解 7 | 30ms 8 | 5670k 9 | ''' 10 | 11 | # -*- coding:utf-8 -*- 12 | # class ListNode: 13 | # def __init__(self, x): 14 | # self.val = x 15 | # self.next = None 16 | class Solution: 17 | # 返回合并后列表 18 | def Merge(self, pHead1, pHead2): 19 | # write code here 20 | if pHead1 == None: 21 | return pHead2 22 | elif pHead2 == None: 23 | return pHead1 24 | 25 | mergepHead = None 26 | if pHead1.val <= pHead2.val: 27 | mergepHead = pHead1 28 | mergepHead.next = self.Merge(pHead1.next, pHead2) 29 | elif pHead1.val > pHead2.val: 30 | mergepHead = pHead2 31 | mergepHead.next = self.Merge(pHead1, pHead2.next) 32 | 33 | return mergepHead 34 | 35 | ''' 36 | 版本二:非递归版本 37 | 23ms 38 | 5676k 39 | ''' 40 | 41 | # -*- coding:utf-8 -*- 42 | class ListNode: 43 | def __init__(self, x): 44 | self.val = x 45 | self.next = None 46 | 47 | class Solution: 48 | # 返回合并后列表 49 | def Merge(self, pHead1, pHead2): 50 | # write code here 51 | dummy = ListNode(0) 52 | pHead = dummy 53 | 54 | while pHead1 and pHead2: 55 | if pHead1.val >= pHead2.val: 56 | dummy.next = pHead2 57 | pHead2 = pHead2.next 58 | else: 59 | dummy.next = pHead1 60 | pHead1 = pHead1.next 61 | 62 | dummy = dummy.next 63 | if pHead1: 64 | dummy.next = pHead1 65 | elif pHead2: 66 | dummy.next = pHead2 67 | return pHead.next -------------------------------------------------------------------------------- /数据结构与算法/两个栈实现队列.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。 3 | ''' 4 | 5 | ''' 6 | 这一题还是挺难的,有两个栈stackA、stackB,A是入栈的,B是出栈的,入栈时,直接进入A即可,出栈时,先判断是否有元素, 7 | 如果B没有元素,pop肯定报错,应该先将A中所有的元素压倒B里面,再pop最上面一个元素,如果B中有就直接pop出,就可以, 8 | 这是最优的思路,两个栈实现了先进后出,在一起又实现了队列的先进先出。 9 | 23ms 10 | 5628k 11 | ''' 12 | 13 | # -*- coding:utf-8 -*- 14 | class Solution: 15 | def __init__(self): 16 | self.stackA = [] 17 | self.stackB = [] 18 | 19 | def push(self, node): 20 | # write code here 21 | self.stackA.append(node) 22 | 23 | def pop(self): 24 | # return xx 25 | if self.stackB: 26 | return self.stackB.pop() 27 | elif not self.stackA: 28 | return None 29 | else: 30 | while self.stackA: 31 | self.stackB.append(self.stackA.pop()) 32 | return self.stackB.pop() 33 | 34 | -------------------------------------------------------------------------------- /数据结构与算法/两个链表的第一个公共节点.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:输入两个链表,找出它们的第一个公共结点 3 | ''' 4 | 5 | ''' 6 | 思路:共同节点,意味着从共同节点开始之后所有的节点数都是相同的,这是链表,只要有一个共同节点,那么之后所有的指向 7 | 也是重复的。先依次遍历两个链表,记录两个链表的长度m和n,如果 m > n,那么我们就先让长度为m的链表走m-n个结点,然后 8 | 两个链表同时遍历,当遍历到相同的结点的时候停止即可。对于 m < n,同理。 9 | 10 | 32ms 11 | 5632k 12 | ''' 13 | 14 | # -*- coding:utf-8 -*- 15 | # class ListNode: 16 | # def __init__(self, x): 17 | # self.val = x 18 | # self.next = None 19 | class Solution: 20 | def FindFirstCommonNode(self, pHead1, pHead2): 21 | # write code here 22 | Length1 = self.GetLength(pHead1) 23 | Length2 = self.GetLength(pHead2) 24 | LengthDiff = abs(Length1 - Length2) 25 | 26 | if Length1 > Length2: 27 | pHeadLong = pHead1 28 | pHeadShort = pHead2 29 | else: 30 | pHeadLong = pHead2 31 | pHeadShort = pHead1 32 | 33 | for i in range(LengthDiff): 34 | pHeadLong = pHeadLong.next 35 | 36 | while pHeadLong != None and pHeadShort != None and pHeadLong != pHeadShort: 37 | pHeadLong = pHeadLong.next 38 | pHeadShort = pHeadShort.next 39 | 40 | return pHeadLong 41 | 42 | def GetLength(self, pHead): 43 | length = 0 44 | while pHead: 45 | pHead = pHead.next 46 | length += 1 47 | return length 48 | -------------------------------------------------------------------------------- /数据结构与算法/二叉搜索树和双向链表.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。 3 | 要求不能创建任何新的结点,只能调整树中结点指针的指向。 4 | ''' 5 | 6 | ''' 7 | 思路:由于输入的一个二叉搜索树,其左子树大于右子树的值,这位后面的排序做了准备,因为只需要中序遍历即可,将所有 8 | 的节点保存到一个列表,。对这个list[:-1]进行遍历,每个节点的right设为下一个节点,下一个节点的left设为上一个节点。 9 | 10 | 29ms 11 | 5632k 12 | ''' 13 | 14 | # -*- coding:utf-8 -*- 15 | # class TreeNode: 16 | # def __init__(self, x): 17 | # self.val = x 18 | # self.left = None 19 | # self.right = None 20 | class Solution: 21 | def Convert(self, pRootOfTree): 22 | # write code here 23 | if not pRootOfTree: 24 | return 25 | 26 | self.attr = [] 27 | self.midorder(pRootOfTree) 28 | for i, v in enumerate(self.attr[:-1]): 29 | v.right = self.attr[i + 1] 30 | self.attr[i + 1].left = v 31 | return self.attr[0] 32 | 33 | def midorder(self, root): 34 | if not root: 35 | return 36 | self.midorder(root.left) 37 | self.attr.append(root) 38 | self.midorder(root.right) 39 | 40 | -------------------------------------------------------------------------------- /数据结构与算法/二叉搜索树的后序遍历序列.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。 3 | 假设输入的数组的任意两个数字都互不相同。 4 | ''' 5 | 6 | ''' 7 | 二叉搜索树即是二叉排序树, 8 | 1. 后序遍历序列的最后一个元素为二叉树的根节点; 9 | 2. 二叉搜索树左子树上所有的结点均小于根结点、右子树所有的结点均大于根结点。 10 | 11 | 算法步骤如下: 12 | 1. 找到根结点; 13 | 2. 遍历序列,找到第一个大于等于根结点的元素i,则i左侧为左子树、i右侧为右子树; 14 | 3. 我们已经知道i左侧所有元素均小于根结点,那么再依次遍历右侧,看是否所有元素均大于根结点;若出现小于根结点的元素,则直接返回false;若右侧全都大于根结点,则: 15 | 4. 分别递归判断左/右子序列是否为后序序列; 16 | 17 | 25ms 18 | 5632k 19 | ''' 20 | 21 | # -*- coding:utf-8 -*- 22 | class Solution: 23 | def VerifySquenceOfBST(self, sequence): 24 | # write code here 25 | if not sequence: 26 | return False 27 | 28 | root = sequence[-1] 29 | 30 | i = 0 31 | for node in sequence[:-1]: 32 | if node > root: 33 | break 34 | i += 1 35 | 36 | for node in sequence[i:-1]: 37 | if node < root: 38 | return False 39 | 40 | left = True 41 | # i>0 意味i =0 或者1 的时候,两个元素在二叉树没有排序之分的,但是3个元素就有了左右子树之分 42 | if i > 0: 43 | left = self.VerifySquenceOfBST(sequence[:i]) 44 | 45 | right = True 46 | # len(sequence)>3才有左右之分的 47 | if i < len(sequence) - 2 and left: 48 | right = self.VerifySquenceOfBST(sequence[i + 1:]) 49 | 50 | return left and right 51 | 52 | 53 | -------------------------------------------------------------------------------- /数据结构与算法/二叉搜索树的第k个节点.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:给定一颗二叉搜索树,请找出其中的第k大的结点。例如, 5 / \ 3 7 /\ /\ 2 4 6 8 中,按结点数值大小顺序 3 | 第三个结点的值为4。 4 | ''' 5 | 6 | ''' 7 | 思路:二叉搜索树按照中序遍历的顺序打印出来正好就是排序好的顺序。 8 | 所以,按照中序遍历顺序找到第k个结点就是结果。 9 | 10 | 28ms 11 | 5632k 12 | ''' 13 | 14 | # -*- coding:utf-8 -*- 15 | # class TreeNode: 16 | # def __init__(self, x): 17 | # self.val = x 18 | # self.left = None 19 | # self.right = None 20 | 21 | class Solution: 22 | # 返回对应节点TreeNode 23 | def KthNode(self, pRoot, k): 24 | # write code here 25 | global result 26 | result = [] 27 | middle = self.midorder(pRoot) 28 | if k <= 0 or len(middle) < k: 29 | return None 30 | else: 31 | return middle[k - 1] 32 | 33 | def midorder(self, pRoot): 34 | if not pRoot: 35 | return [] 36 | self.midorder(pRoot.left) 37 | result.append(pRoot) 38 | self.midorder(pRoot.right) 39 | return result -------------------------------------------------------------------------------- /数据结构与算法/二叉树/二叉树的前中后序的递归,非递归及Morris遍历.py: -------------------------------------------------------------------------------- 1 | class TreeNode(object): 2 | def __init__(self, x): 3 | self.val = x 4 | self.left = None 5 | self.right = None 6 | 7 | # def create_tree(root): 8 | # element = input("Enter a key: ") 9 | # if element == '#': 10 | # root = None 11 | # else: 12 | # root = TreeNode(element) 13 | # print(root) 14 | # root.left = create_tree(root.left) 15 | # print(1) 16 | # root.right = create_tree(root.right) 17 | # print(2) 18 | # return root 19 | # 20 | # print(create_tree([None])) 21 | 22 | class Tree(): 23 | def __init__(self): 24 | self.root = None 25 | 26 | def add(self, item): 27 | node = TreeNode(item) 28 | if self.root == None: 29 | self.root = node 30 | return 31 | queue = [self.root] 32 | 33 | while queue: 34 | cur = queue.pop(0) 35 | if cur.left == None: 36 | cur.left = node 37 | return 38 | else: 39 | queue.append(cur.left) 40 | 41 | if cur.right == None: 42 | cur.right = node 43 | return 44 | else: 45 | queue.append(cur.right) 46 | 47 | # 广度遍历 48 | def breadth_travel(self): 49 | if self.root == None: 50 | return 51 | queue = [self.root] 52 | while queue: 53 | cur = queue.pop(0) 54 | print(cur.val, end='') 55 | if cur.left != None: 56 | queue.append(cur.left) 57 | if cur.right != None: 58 | queue.append(cur.right) 59 | 60 | # 深度遍历 61 | # 先序 62 | def preorder(self, node): 63 | if node == None: 64 | return 65 | print(node.val, end='') 66 | self.preorder(node.left) 67 | self.preorder(node.right) 68 | 69 | # 中序 70 | def middleorder(self, node): 71 | if node == None: 72 | return 73 | self.middleorder(node.left) 74 | print(node.val, end='') 75 | self.middleorder(node.right) 76 | 77 | def postorder(self, node): 78 | if node == None: 79 | return 80 | self.postorder(node.right) 81 | self.postorder(node.left) 82 | print(node.val, end='') 83 | 84 | if __name__ == "__main__": 85 | tree = Tree() 86 | tree.add(0) 87 | tree.add(1) 88 | tree.add(2) 89 | tree.add(3) 90 | tree.add(4) 91 | tree.add(5) 92 | tree.add(6) 93 | tree.add(7) 94 | tree.add(8) 95 | tree.add(9) 96 | tree.breadth_travel() 97 | tree.preorder(tree.root) 98 | tree.middleorder(tree.root) 99 | tree.postorder(tree.root) 100 | -------------------------------------------------------------------------------- /数据结构与算法/二叉树/二叉树的序列化和反序列化.py: -------------------------------------------------------------------------------- 1 | # 先序 2 | class Tree(): 3 | def __init__(self, val=None): 4 | self.val = val 5 | self.left = None 6 | self.right = None 7 | 8 | def preorder(root): 9 | if not root: 10 | return '#!' 11 | res = root.val + ['!'] 12 | res += preorder(root.left) 13 | res += preorder(root.right) 14 | return res 15 | 16 | tree = Tree([1]) 17 | print(preorder(tree)) 18 | 19 | # 反序列化(先序) 20 | def recoByPreString(prestr): 21 | def reconpreorder(values): 22 | key = values.pop(0) 23 | if key == "#": 24 | return None 25 | root = Tree(key) 26 | root.left = reconpreorder(values) 27 | root.right = reconpreorder(values) 28 | return root 29 | 30 | values = prestr.split('!') 31 | return reconpreorder(values) 32 | -------------------------------------------------------------------------------- /数据结构与算法/二叉树/二叉树的按层打印.py: -------------------------------------------------------------------------------- 1 | class Tree(): 2 | def __init__(self, val=None): 3 | self.val = val 4 | self.left = None 5 | self.right = None 6 | 7 | def printlayer(root): 8 | last = root 9 | queue = [] 10 | queue.append(root) 11 | while queue: 12 | root = queue.pop(0) 13 | print(root.val, end='') 14 | if root.left: 15 | nlast = root.left 16 | queue.append(root.left) 17 | if root.right: 18 | nlast = root.right 19 | queue.append(root.right) 20 | if root == last and queue: 21 | last = nlast 22 | -------------------------------------------------------------------------------- /数据结构与算法/二叉树/判断t1树中是否有与t2树拓扑结构完全相同的子树.py: -------------------------------------------------------------------------------- 1 | class Tree(): 2 | def __init__(self, val=None): 3 | self.val = val 4 | self.left = None 5 | self.right = None 6 | 7 | def Top(t1, t2): 8 | def issubTosub(t1, t2): 9 | if not t1 and not t2: 10 | return True 11 | if t1.val != t2.val: 12 | return False 13 | return issubTosub(t1.left, t2.left) and issubTosub(t1.right, t2.right) 14 | 15 | if not t1 or not t2: 16 | return False 17 | return Top(t1, t2) or Top(t1.left, t2) or Top(t1.right, t2) 18 | -------------------------------------------------------------------------------- /数据结构与算法/二叉树中和为某一值的路径.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。 3 | 路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。 4 | ''' 5 | 6 | ''' 7 | 思路: 8 | 递归先序遍历树, 把结点加入路径。使用列表结构存树结构 9 | 若该结点是叶子结点则比较当前路径和是否等于期待和,叶子节点说明该路径应该截止了 10 | 弹出结点,每一轮递归返回到父结点时,当前路径也应该回退一个结点。 11 | 12 | 25ms 13 | 5670k 14 | ''' 15 | 16 | # -*- coding:utf-8 -*- 17 | # class TreeNode: 18 | # def __init__(self, x): 19 | # self.val = x 20 | # self.left = None 21 | # self.right = None 22 | class Solution: 23 | # 返回二维列表,内部每个列表表示找到的路径 24 | def FindPath(self, root, expectNumber): 25 | # write code here 26 | if not root: 27 | return [] 28 | result = [] 29 | 30 | def FindPath2(root, path, currentNum): 31 | currentNum += root.val 32 | # root使用append转成了列表,因为最后要一个序列,root本身还是树结构 33 | path.append(root) 34 | # 判断是不是到叶子节点了,到叶子节点了就要判断值的和是不是相等 35 | flag = root.left == None and root.right == None 36 | # 返回值是一个等于整数的序列 37 | if currentNum == expectNumber and flag: 38 | onepath = [] 39 | for node in path: 40 | onepath.append(node.val) 41 | result.append(onepath) 42 | 43 | if currentNum < expectNumber: 44 | if root.left: 45 | FindPath2(root.left, path, currentNum) 46 | if root.right: 47 | FindPath2(root.right, path, currentNum) 48 | # 拿到一个正确的路径后要弹出,回到上一次父节点继续递归 49 | path.pop() 50 | 51 | FindPath2(root, [], 0) 52 | return result 53 | -------------------------------------------------------------------------------- /数据结构与算法/二叉树的下一个节点.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右 3 | 子结点,同时包含指向父结点的指针。 4 | ''' 5 | 6 | ''' 7 | 思路: 8 | (1) 若该节点存在右子树:则下一个节点为右子树最左子节点 9 | (2) 若该节点不存在右子树:这时分两种情况: 10 | 2.1 该节点为父节点的左子节点,则下一个节点为其父节点 11 | 2.2 该节点为父节点的右子节点,则沿着父节点向上遍历,知道找到一个节点的父节点的左子节点为该节点, 12 | 则该节点的父节点下一个节点(如图节点I,沿着父节点一直向上查找找到B(B为其父节点的左子节点), 13 | 则B的父节点A为下一个节点)。 14 | 15 | 34ms 16 | 5632k 17 | ''' 18 | 19 | # -*- coding:utf-8 -*- 20 | # class TreeLinkNode: 21 | # def __init__(self, x): 22 | # self.val = x 23 | # self.left = None 24 | # self.right = None 25 | # self.next = None 26 | class Solution: 27 | def GetNext(self, pNode): 28 | # write code here 29 | if not pNode: 30 | return 31 | # 该节点有右子节点,那么该节点的下一个节点就是有自己节点的最左节点 32 | if pNode.right != None: 33 | pNode = pNode.right 34 | while pNode.left != None: 35 | pNode = pNode.left 36 | return pNode 37 | # 该节点没有右子节点 38 | # 该节点为父节点的左子节点 39 | elif pNode.next != None and pNode.next.left == pNode: 40 | return pNode.next 41 | # 该节点为父节点的右子节点,它的下一个节点就是其父节点作为父节点的左子节点的下一个节点 42 | elif pNode.next != None and pNode.next.right == pNode: 43 | while pNode.next != None and pNode.next.left != pNode: 44 | pNode = pNode.next 45 | return pNode.next 46 | else: 47 | # 节点无父节点,即节点为根节点 48 | return pNode.next 49 | 50 | 51 | -------------------------------------------------------------------------------- /数据结构与算法/二叉树的深度.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径, 3 | 最长路径的长度为树的深度。 4 | ''' 5 | 6 | ''' 7 | 思路:利用递归实现。如果一棵树只有一个结点,那么它的深度为1。递归的时候无需判断左右子树是否存在,因为如果该节点 8 | 为叶节点,它的左右子树不存在,那么在下一级递归的时候,直接return 0。同时,记得每次递归返回值的时候,深度加一操 9 | 作,因为计算深度是从根节点下面一个节点开始计算的。 10 | 11 | 25ms 12 | 5632k 13 | ''' 14 | 15 | # -*- coding:utf-8 -*- 16 | # class TreeNode: 17 | # def __init__(self, x): 18 | # self.val = x 19 | # self.left = None 20 | # self.right = None 21 | class Solution: 22 | def TreeDepth(self, pRoot): 23 | # write code here 24 | if pRoot == None: 25 | return 0 26 | return max(self.TreeDepth(pRoot.left), self.TreeDepth(pRoot.right)) + 1 27 | -------------------------------------------------------------------------------- /数据结构与算法/二叉树的镜像.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:操作给定的二叉树,将其变换为源二叉树的镜像。 3 | ''' 4 | 5 | ''' 6 | 递归 7 | 23ms 8 | 5492k 9 | ''' 10 | 11 | # -*- coding:utf-8 -*- 12 | # class TreeNode: 13 | # def __init__(self, x): 14 | # self.val = x 15 | # self.left = None 16 | # self.right = None 17 | class Solution: 18 | # 返回镜像树的根节点 19 | def Mirror(self, root): 20 | # write code here 21 | if not root: 22 | return root 23 | root.left,root.right = root.right,root.left 24 | self.Mirror(root.left) 25 | self.Mirror(root.right) 26 | return root 27 | 28 | 29 | -------------------------------------------------------------------------------- /数据结构与算法/从上往下打印二叉树.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:从上往下打印出二叉树的每个节点,同层节点从左至右打印 3 | ''' 4 | 5 | ''' 6 | 广度优先层次遍历,利用一个队列来实现 7 | 层序遍历的基本过程是: 8 | 先根节点入队,然后: 9 | 1.从队列中取出一个元素 10 | 2.访问该元素所指的结点 11 | 3.若该元素所指结点的左、右孩子结点非空,则将其左、右孩子的指针顺序入队 12 | 13 | 利用队列,首先将根节点放入队列中,取队列的首节点,把值存进列表,然后考虑左右指针,把指针放进列表,在存值 14 | 15 | 33ms 16 | 5900k 17 | ''' 18 | 19 | # -*- coding:utf-8 -*- 20 | # class TreeNode: 21 | # def __init__(self, x): 22 | # self.val = x 23 | # self.left = None 24 | # self.right = None 25 | class Solution: 26 | # 返回从上到下每个节点值列表,例:[1,2,3] 27 | def PrintFromTopToBottom(self, root): 28 | # write code here 29 | if not root: 30 | return [] 31 | queue = [] 32 | result = [] 33 | 34 | queue.append(root) 35 | while len(queue) > 0: 36 | node = queue.pop(0) 37 | result.append(node.val) 38 | if node.left: 39 | queue.append(node.left) 40 | if node.right: 41 | queue.append(node.right) 42 | return result -------------------------------------------------------------------------------- /数据结构与算法/从尾到头打印链表.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:输入一个链表,从尾到头打印链表每个节点的值 3 | ''' 4 | 5 | ''' 6 | 方法一:使用extend,在尾部插入,其实最关键在于[::-1],只不过输入数据多样化,有可能还是集合,所以转成列表 7 | 这个方法效率应该还可以,先存入vector,再反转vector 8 | 26ms 9 | 5512k 10 | ''' 11 | 12 | # -*- coding:utf-8 -*- 13 | # class ListNode: 14 | # def __init__(self, x): 15 | # self.val = x 16 | # self.next = None 17 | 18 | class Solution: 19 | # 返回从尾部到头部的列表值序列,例如[1,2,3] 20 | def printListFromTailToHead(self, listNode): 21 | # write code here 22 | if not listNode: 23 | return [] 24 | 25 | result = [] 26 | while listNode.next is not None: 27 | result.extend([listNode.val]) 28 | listNode = listNode.next 29 | result.extend([listNode.val]) 30 | 31 | return result[::-1] 32 | 33 | ''' 34 | 方法二: 使用insert直接在头部插入 35 | 26ms 36 | 6336k 37 | ''' 38 | 39 | # -*- coding:utf-8 -*- 40 | # class ListNode: 41 | # def __init__(self, x): 42 | # self.val = x 43 | # self.next = None 44 | 45 | class Solution: 46 | # 返回从尾部到头部的列表值序列,例如[1,2,3] 47 | def printListFromTailToHead(self, listNode): 48 | # write code here 49 | if not listNode: 50 | return [] 51 | 52 | result = [] 53 | head = listNode 54 | 55 | while head: 56 | result.insert(0, head.val) 57 | head = head.next 58 | return result 59 | -------------------------------------------------------------------------------- /数据结构与算法/反转链表.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:输入一个链表,反转链表后,输出链表的所有元素。 3 | ''' 4 | 5 | ''' 6 | 这一题还是蛮难的。链表的翻转,例如 1->2->3->4->5 ==> 5->4->3->2->1 7 | tmp = pHead.next 把当前节点的下一个节点保存下来 8 | pHead.next = pre 把前一个节点移到当前节点的下一个节点,因为要翻转节点,pre始终指向要反转节点的首节点 9 | pre = pHead 当前节点向后移一位 10 | pHead = tmp 把之前保存的下一个节点指针再给当前节点接着翻转 11 | 33ms 12 | 6016k 13 | ''' 14 | 15 | # -*- coding:utf-8 -*- 16 | # class ListNode: 17 | # def __init__(self, x): 18 | # self.val = x 19 | # self.next = None 20 | class Solution: 21 | # 返回ListNode 22 | def ReverseList(self, pHead): 23 | # write code here 24 | if not pHead or not pHead.next: 25 | return pHead 26 | pre = None 27 | while pHead: 28 | tmp = pHead.next 29 | pHead.next = pre 30 | pre = pHead 31 | pHead = tmp 32 | return pre 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /数据结构与算法/复杂链表的复制.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点), 3 | 返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空) 4 | ''' 5 | 6 | ''' 7 | 思路:1. 根据旧链表创建新链表,不去管随机的那个指针 8 | 2. 根据旧链表中的随机指针,创建新链表中的随机指针 9 | 3. 从旧链表中拆分得到新链表 10 | 11 | 32ms 12 | 5632k 13 | ''' 14 | 15 | # -*- coding:utf-8 -*- 16 | class RandomListNode: 17 | def __init__(self, x): 18 | self.label = x 19 | self.next = None 20 | self.random = None 21 | class Solution: 22 | # 返回 RandomListNode 23 | def Clone(self, pHead): 24 | if pHead == None: 25 | return None 26 | self.CloneNodes(pHead) 27 | self.ConnectRandomNodes(pHead) 28 | return self.ReconnectNodes(pHead) 29 | # 复制原始链表的每个结点, 将复制的结点链接在其原始结点的后面 30 | def CloneNodes(self, pHead): 31 | pNode = pHead 32 | while pNode: 33 | pCloned = RandomListNode(0) 34 | pCloned.label = pNode.label 35 | pCloned.next = pNode.next 36 | # pCloned.random = None #不需要写这句话, 因为创建新的结点的时候,random自动指向None 37 | 38 | pNode.next = pCloned 39 | pNode = pCloned.next 40 | 41 | # 将复制后的链表中的复制结点的random指针链接到被复制结点random指针的后一个结点 42 | def ConnectRandomNodes(self, pHead): 43 | pNode = pHead 44 | while pNode: 45 | pCloned = pNode.next 46 | if pNode.random != None: 47 | pCloned.random = pNode.random.next 48 | pNode = pCloned.next 49 | 50 | # 拆分链表, 将原始链表的结点组成新的链表, 复制结点组成复制后的链表 51 | def ReconnectNodes(self, pHead): 52 | pNode = pHead 53 | pClonedHead = pClonedNode = pNode.next 54 | pNode.next = pClonedHead.next 55 | pNode = pNode.next 56 | 57 | while pNode: 58 | pClonedNode.next = pNode.next 59 | pClonedNode = pClonedNode.next 60 | pNode.next = pClonedNode.next 61 | pNode = pNode.next 62 | 63 | return pClonedHead 64 | -------------------------------------------------------------------------------- /数据结构与算法/字节跳动最爱考的 64 道算法题.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yongxinz/backend-interview/888e5d4fa8a81bbcbb1b36b596e306989df2ffc1/数据结构与算法/字节跳动最爱考的 64 道算法题.pdf -------------------------------------------------------------------------------- /数据结构与算法/对称的二叉树.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的, 3 | 定义其为对称的。 4 | ''' 5 | 6 | ''' 7 | 思路:二叉树镜像相同是对称的,利用递归做 8 | 9 | 34ms 10 | 6340k 11 | ''' 12 | 13 | # -*- coding:utf-8 -*- 14 | # class TreeNode: 15 | # def __init__(self, x): 16 | # self.val = x 17 | # self.left = None 18 | # self.right = None 19 | class Solution: 20 | def isSymmetrical(self, pRoot): 21 | # write code here 22 | return self.IsSymmetrical(pRoot, pRoot) 23 | 24 | def IsSymmetrical(self, pRoot1, pRoot2): 25 | if pRoot1 == None and pRoot2 == None: 26 | return True 27 | if pRoot1 == None or pRoot2 == None: 28 | return False 29 | if pRoot1.val != pRoot2.val: 30 | return False 31 | return self.IsSymmetrical(pRoot1.left, pRoot2.right) and self.IsSymmetrical(pRoot1.right, pRoot2.left) 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /数据结构与算法/平衡二叉树.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:输入一棵二叉树,判断该二叉树是否是平衡二叉树。 3 | ''' 4 | 5 | ''' 6 | 平衡二叉树:平衡二叉搜索树(Balanced Binary Tree)具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值 7 | 不超过1,并且左右两个子树都是一棵平衡二叉树。 8 | 9 | 思路:如果二叉树的每个节点的左子树和右子树的深度不大于1,它就是平衡二叉树。 10 | 先写一个求深度的函数,再对每一个节点判断,看该节点的左子树的深度和右子树的深度的差是否大于1 11 | 12 | 31ms 13 | 5520k 14 | ''' 15 | 16 | # -*- coding:utf-8 -*- 17 | # class TreeNode: 18 | # def __init__(self, x): 19 | # self.val = x 20 | # self.left = None 21 | # self.right = None 22 | class Solution: 23 | def IsBalanced_Solution(self, pRoot): 24 | # write code here 25 | if pRoot == None: 26 | # 返回True是因为这是最后的判断依据,在不断递归中,最后是叶子节点,即终止,如果叶子节点时,依然左右子树之差小于1,那么就是平衡二叉树,返回True 27 | return True 28 | depth1 = self.GetDepth(pRoot.left) 29 | depth2 = self.GetDepth(pRoot.right) 30 | if abs(depth1 - depth2) > 1: 31 | return False 32 | return self.IsBalanced_Solution(pRoot.left) and self.IsBalanced_Solution(pRoot.right) 33 | 34 | def GetDepth(self, root): 35 | if not root: 36 | return 0 37 | return max(self.GetDepth(root.left), self.GetDepth(root.right)) + 1 38 | -------------------------------------------------------------------------------- /数据结构与算法/序列化二叉树.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:请实现两个函数,分别用来序列化和反序列化二叉树 3 | ''' 4 | 5 | ''' 6 | 思路:最终要实现的是二叉树的序列化和反序列化。首先来看二叉树的序列化,二叉树的序列化就是采用前序遍历二叉树输出 7 | 节点,再碰到左子节点或者右子节点为None的时候输出一个特殊字符”#”。对于反序列化,就是针对输入的一个序列构建一棵 8 | 二叉树,我们可以设置一个指针先指向序列的最开始,然后把指针指向位置的数字转化为二叉树的结点,后移一个数字,继续 9 | 转化为左子树和右子树。当遇到当前指向的字符为特殊字符”#”或者指针超出了序列的长度,则返回None,指针后移,继续遍历。 10 | 11 | 26ms 12 | 5632k 13 | ''' 14 | 15 | # -*- coding:utf-8 -*- 16 | class TreeNode: 17 | def __init__(self, x): 18 | self.val = x 19 | self.left = None 20 | self.right = None 21 | 22 | class Solution: 23 | flag = -1 24 | 25 | def Serialize(self, root): 26 | # write code here 27 | if not root: 28 | return '#' 29 | return str(root.val) + ',' + self.Serialize(root.left) + ',' + self.Serialize(root.right) 30 | 31 | def Deserialize(self, s): 32 | # write code here 33 | self.flag += 1 34 | l = s.split(',') 35 | # flag每次加1,从0开始,不能超过字符串长度,否则返回None 36 | if self.flag >= len(s): 37 | return None 38 | 39 | root = None 40 | # 新建一个树对象来反序列化字符串 41 | if l[self.flag] != '#': 42 | # 往树中存值,递归输入s没问题,因为l[self.flag]是不断往后取值的 43 | root = TreeNode(int(l[self.flag])) 44 | root.left = self.Deserialize(s) 45 | root.right = self.Deserialize(s) 46 | return root 47 | 48 | -------------------------------------------------------------------------------- /数据结构与算法/把二叉树打印成多行.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。 3 | ''' 4 | 5 | ''' 6 | 思路:两个栈,分二级,第一级存一行的数curStack,第二级存着一行数所对应的left,right的值nextStack,每次结束 7 | 一轮循环,把curStack的值给result,然后再将nodes设成nextStack继续进行循环 8 | 9 | 28ms 10 | 5632k 11 | ''' 12 | 13 | # -*- coding:utf-8 -*- 14 | # class TreeNode: 15 | # def __init__(self, x): 16 | # self.val = x 17 | # self.left = None 18 | # self.right = None 19 | class Solution: 20 | # 返回二维列表[[1,2],[4,5]] 21 | def Print(self, pRoot): 22 | # write code here 23 | if not pRoot: 24 | return [] 25 | result,nodes,right = [],[pRoot],True 26 | while nodes: 27 | curStack,nextStack = [],[] 28 | for node in nodes: 29 | curStack.append(node.val) 30 | if node.left: 31 | nextStack.append(node.left) 32 | if node.right: 33 | nextStack.append(node.right) 34 | result.append(curStack) 35 | nodes = nextStack 36 | return result -------------------------------------------------------------------------------- /数据结构与算法/按之字形顺序打印二叉树.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印, 3 | 第三行按照从左到右的顺序打印,其他行以此类推。 4 | ''' 5 | 6 | ''' 7 | 思路一:按之字形顺序打印二叉树需要两个栈。我们在打印某一行节点时,拔下一层的子节点保存到相应的栈里。如果当前打印 8 | 的奇数层,则先保存左子节点再保存右子节点到第一个栈里;如果当前打印的是偶数层,则先保存右子节点再保存左子节点到 9 | 第二个栈里。 10 | 11 | 28ms 12 | 5512k 13 | ''' 14 | 15 | # -*- coding:utf-8 -*- 16 | # class TreeNode: 17 | # def __init__(self, x): 18 | # self.val = x 19 | # self.left = None 20 | # self.right = None 21 | class Solution: 22 | def Print(self, pRoot): 23 | # write code here 24 | if not pRoot: 25 | return [] 26 | result,nodes = [],[pRoot] 27 | right = True 28 | # 处理思路很好的,有一个二级机制,通过nextStack存下一节点的,这节点里面的值通过=nodes再转移到curStack里面 29 | # 但是在nextStack中可以调整顺序的 30 | while nodes: 31 | nextStack,curStack = [],[] 32 | if right: 33 | for node in nodes: 34 | curStack.append(node.val) 35 | if node.left: 36 | nextStack.append(node.left) 37 | if node.right: 38 | nextStack.append(node.right) 39 | else: 40 | for node in nodes: 41 | curStack.append(node.val) 42 | if node.right: 43 | nextStack.append(node.right) 44 | if node.left: 45 | nextStack.append(node.left) 46 | nextStack.reverse() 47 | right = not right 48 | result.append(curStack) 49 | nodes= nextStack 50 | return result 51 | 52 | ''' 53 | 思路二: 转换思路,存储的时候一直从左向右存储,打印的时候根据不同的层一次打印 54 | 55 | 25ms 56 | 5512k 57 | ''' 58 | 59 | # -*- coding:utf-8 -*- 60 | # class TreeNode: 61 | # def __init__(self, x): 62 | # self.val = x 63 | # self.left = None 64 | # self.right = None 65 | class Solution: 66 | def Print(self, pRoot): 67 | # write code here 68 | if not pRoot: 69 | return [] 70 | nodes,result,leftToright = [pRoot],[],True 71 | while nodes: 72 | curStack,nextStack = [],[] 73 | for node in nodes: 74 | curStack.append(node.val) 75 | if node.left: 76 | nextStack.append(node.left) 77 | if node.right: 78 | nextStack.append(node.right) 79 | # 调整的是一级的curStack的顺序了,不是二级的nextStack的顺序 80 | if not leftToright: 81 | curStack.reverse() 82 | if curStack: 83 | result.append(curStack) 84 | nodes = nextStack 85 | leftToright = not leftToright 86 | return result 87 | 88 | 89 | -------------------------------------------------------------------------------- /数据结构与算法/数据结构相关面试题汇总.md: -------------------------------------------------------------------------------- 1 | ## 1 红黑树 2 | 3 | 红黑树与AVL的比较: 4 | 5 | AVL是严格平衡树,因此在增加或者删除节点的时候,根据不同情况,旋转的次数比红黑树要多; 6 | 7 | 红黑是用非严格的平衡来换取增删节点时候旋转次数的降低; 8 | 9 | 所以简单说,如果你的应用中,搜索的次数远远大于插入和删除,那么选择AVL,如果搜索,插入删除次数几乎差不多,应该选择RB。 10 | 11 | 红黑树详解: https://xieguanglei.github.io/blog/post/red-black-tree.html 12 | 13 | 教你透彻了解红黑树: https://github.com/julycoding/The-Art-Of-Programming-By-July/blob/master/ebook/zh/03.01.md -------------------------------------------------------------------------------- /数据结构与算法/栈和队列/两个栈组成一个队列.py: -------------------------------------------------------------------------------- 1 | class TwostackQueue(): 2 | def __init__(self): 3 | self.stackpush = [] 4 | self.stackpop = [] 5 | 6 | def add(self,Newnum): 7 | self.stackpush.append(Newnum) 8 | 9 | def push(self): 10 | while self.stackpush: 11 | self.stackpop.append(self.stackpush.pop()) 12 | return self.stackpop.pop() 13 | 14 | queue = TwostackQueue() 15 | queue.add([1,2,3]) 16 | print(queue.push()) 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /数据结构与算法/栈和队列/如何仅用递归函数和栈操作逆序一个栈.py: -------------------------------------------------------------------------------- 1 | def recur(stack): 2 | def getandremove(stack): 3 | result = stack.pop() 4 | if len(stack) == 0: 5 | return result 6 | else: 7 | i = getandremove(stack) 8 | stack.append(result) 9 | return i 10 | 11 | if len(stack) == 0: 12 | return 13 | i = getandremove(stack) 14 | recur(stack) 15 | stack.append(i) 16 | return stack 17 | 18 | print(recur([1,2,3,4])) 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /数据结构与算法/栈和队列/构造数组的MaxTree.py: -------------------------------------------------------------------------------- 1 | # 每一棵树的父节点是他左边第一个比他大的数和他右边第一个比他大的数中较小的那个值 2 | # 如果这个数是整个树中的最大值,那么他就作为整个数的头结点出现 3 | 4 | class Node(): 5 | def __init__(self, value): 6 | self.value = value 7 | self.left = None 8 | self.right = None 9 | 10 | def getMaxtree(self, arr): 11 | narr = [Node(arr[i]) for i in range(len(arr))] 12 | lBigMap = {} 13 | rBigMap = {} 14 | stack = [] 15 | 16 | # 对输入的元素进行判断,输入元素大于栈顶元素,那么栈顶元素弹出直到小于输入元素 17 | for i in range(len(narr)): 18 | curNode = narr[i] 19 | while stack and stack[-1].value < curNode.value: 20 | cur = stack.pop() 21 | lBigMap[cur] = stack[-1] if stack else None 22 | rBigMap[cur] = curNode 23 | stack.append(curNode) 24 | 25 | # 上面的循环条件是两个,但是实际上最后还是会剩下来栈里面的几个元素 26 | while stack: 27 | cur = stack.pop() 28 | lBigMap[cur] = stack[-1] if stack else None 29 | rBigMap[cur] = None 30 | 31 | head = None 32 | for i in range(len(narr)): 33 | curNode = narr[i] 34 | left = lBigMap[curNode] 35 | right = rBigMap[curNode] 36 | if left == None and right == None: 37 | head = curNode 38 | elif left == None: 39 | if right.left == None: 40 | right.left = curNode 41 | else: 42 | right.right = curNode 43 | elif right == None: 44 | if left.left == None: 45 | left.left = curNode 46 | else: 47 | left.right = curNode 48 | else: 49 | parent = left if left.value < right.value else right 50 | if parent.left == None: 51 | parent.left = curNode 52 | else: 53 | parent.right = curNode 54 | return head 55 | 56 | node = Node(None) 57 | print(node.getMaxtree([3, 1, 2])) 58 | -------------------------------------------------------------------------------- /数据结构与算法/栈和队列/求最大子矩阵的大小.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yongxinz/backend-interview/888e5d4fa8a81bbcbb1b36b596e306989df2ffc1/数据结构与算法/栈和队列/求最大子矩阵的大小.py -------------------------------------------------------------------------------- /数据结构与算法/栈和队列/生成窗口最大值数组.py: -------------------------------------------------------------------------------- 1 | # 双端队列 2 | # 队列存的是数组的下标 3 | 4 | def getwindows(arr, w): 5 | deque = [] 6 | res = [] 7 | 8 | for i in range(len(arr)): 9 | while deque and arr[deque[-1]] <= arr[i]: 10 | deque.pop() 11 | deque.append(i) 12 | if deque[0] <= i - w: 13 | deque.pop(0) 14 | if i - w + 1 >= 0: 15 | res.append(arr[deque[0]]) 16 | return res 17 | 18 | print(getwindows([4, 3, 5, 4, 3, 3, 6, 7], 3)) 19 | -------------------------------------------------------------------------------- /数据结构与算法/栈和队列/用一个栈实现另一个栈的排序.py: -------------------------------------------------------------------------------- 1 | # class so(): 2 | # def __init__(self): 3 | # self.stack = [] 4 | # self.help = [] 5 | # 6 | # def add(self, Newnum): 7 | # self.stack.append(Newnum) 8 | # 9 | # def sor(self): 10 | # while self.stack: 11 | # num = self.stack.pop() 12 | # if self.help == [] or num > self.help[-1]: 13 | # self.help.append(num) 14 | # else: 15 | # while num < self.help[-1]: 16 | # self.stack.append(self.help.pop()) 17 | # self.help.append(num) 18 | # 19 | # return self.help 20 | 21 | def sor(stack): 22 | help = [] 23 | while stack: 24 | cur = stack.pop() 25 | while len(help) != 0 and help[-1] < cur: 26 | stack.append(help.pop()) 27 | help.append(cur) 28 | 29 | while help: 30 | stack.append(help.pop()) 31 | 32 | return stack 33 | 34 | print(sor([3,2,5,4,1])) 35 | -------------------------------------------------------------------------------- /数据结构与算法/栈和队列/设计一个有getMin功能的栈.py: -------------------------------------------------------------------------------- 1 | class Newstack(): 2 | def __init__(self): 3 | self.stackData = [] 4 | self.stackMin = [] 5 | 6 | def push(self, newNum): 7 | self.stackData.append(newNum) 8 | if self.stackMin == [] or newNum <= self.getMin(): 9 | self.stackMin.append(newNum) 10 | 11 | def getMin(self): 12 | if len(self.stackMin) == 0: 13 | return [] 14 | return self.stackMin[-1] 15 | 16 | def pop(self): 17 | value = self.stackData.pop() 18 | if self.getMin() == value: 19 | self.stackMin.pop() 20 | return value 21 | 22 | s = Newstack() 23 | print(s.push(1)) 24 | print(s.pop()) 25 | print(s.push(2)) 26 | print(s.push(3)) 27 | print(s.getMin()) 28 | print(s.push(1)) 29 | -------------------------------------------------------------------------------- /数据结构与算法/重建二叉树.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目: 3 | 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。 4 | 例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。 5 | ''' 6 | 7 | ''' 8 | 46ms 9 | 5760k 10 | ''' 11 | 12 | 13 | # -*- coding:utf-8 -*- 14 | class TreeNode: 15 | def __init__(self, x): 16 | self.val = x 17 | self.left = None 18 | self.right = None 19 | 20 | class Solution: 21 | # 返回构造的TreeNode根节点 22 | def reConstructBinaryTree(self, pre, tin): 23 | # write code here 24 | if not pre or not tin: 25 | return None 26 | root = TreeNode(pre[0]) 27 | val = tin.index(pre[0]) 28 | 29 | # 可以动手试一下啊,第一个参数 30 | root.left = self.reConstructBinaryTree(pre[1:val + 1], tin[:val]) 31 | root.right = self.reConstructBinaryTree(pre[val + 1:], tin[val + 1:]) 32 | return root 33 | -------------------------------------------------------------------------------- /数据结构与算法/链表中倒数第k个节点.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:输入一个链表,输出该链表中倒数第k个结点。 3 | ''' 4 | 5 | ''' 6 | 使用列表的切片,还是很快的 7 | 24ms 8 | 5632k 9 | ''' 10 | 11 | # -*- coding:utf-8 -*- 12 | # class ListNode: 13 | # def __init__(self, x): 14 | # self.val = x 15 | # self.next = None 16 | 17 | class Solution: 18 | def FindKthToTail(self, head, k): 19 | # write code here 20 | res = [] 21 | while head: 22 | res.append(head) 23 | head = head.next 24 | if k > len(res) or k < 1: 25 | return 26 | return res[-k] -------------------------------------------------------------------------------- /数据结构与算法/链表中环的入口节点.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:一个链表中包含环,请找出该链表的环的入口结点。 3 | ''' 4 | 5 | ''' 6 | 思路:链表指针区只能指向一个下一个节点,所以如果链表中由环一定是在尾部。 7 | 寻找链表中环的入口结点主要分成三个步骤:首先是设置两个快慢指针,如果快慢指针相遇,则快慢指针必然都在环中; 8 | 然后从相遇的地方设置一个指针向后遍历并记录走的步数,当这个指针重新指到开始的位置的时候,当前对应的步数就是 9 | 环中结点的数量k;然后设置两个指针从链表开始,第一个节点先走k步,然后第二个指针指到链表的开始,两个指针每次 10 | 都向后走一步,两个指针相遇的位置就是链表的入口。 11 | 12 | 程序可以运行,但在牛客网上报错 'NoneType' object has no attribute 'next' 不知道为什么?? 13 | ''' 14 | 15 | # -*- coding:utf-8 -*- 16 | class ListNode: 17 | def __init__(self, x): 18 | self.val = x 19 | self.next = None 20 | 21 | class Solution: 22 | def EntryNodeOfLoop(self, pHead): 23 | # write code here 24 | MeetingNode = self.MeetingNode(pHead) 25 | if not MeetingNode: 26 | return None 27 | NodeOfLoop = 1 28 | FlagNode = MeetingNode 29 | # 从相遇点定义一个指针,指针向后移动到相遇点即为环的节点数NodeOfLoop 30 | while FlagNode.next != MeetingNode: 31 | NodeOfLoop += 1 32 | FlagNode = FlagNode.next 33 | 34 | # 两个指针都位于首位,让其中一个节点向前走环的节点数步,然后第二个指针走,当两个指针相遇时,即为环节点入口 35 | pFast = pHead 36 | pSlow = pHead 37 | for i in range(NodeOfLoop): 38 | pFast = pFast.next 39 | while pSlow != pFast: 40 | pSlow = pSlow.next 41 | pFast = pFast.next 42 | return pFast 43 | 44 | # 定义快慢指针,如果有环,快慢指针一定会相遇 45 | def MeetingNode(self, pHead): 46 | if not pHead: 47 | return None 48 | pSlow = pHead.next 49 | pFast = pSlow.next 50 | while pSlow != pFast: 51 | pSlow = pSlow.next 52 | pFast = pFast.next.next 53 | return pSlow 54 | 55 | node1 = ListNode(1) 56 | node2 = ListNode(2) 57 | node3 = ListNode(3) 58 | node4 = ListNode(4) 59 | node5 = ListNode(5) 60 | node6 = ListNode(6) 61 | 62 | node1.next = node2 63 | node2.next = node3 64 | node3.next = node4 65 | node4.next = node5 66 | node5.next = node6 67 | node6.next = node3 68 | 69 | s = Solution() 70 | print(s.EntryNodeOfLoop(node1).val) 71 | -------------------------------------------------------------------------------- /数据结构与算法/链表问题/判断一个链表是否为回文结构.py: -------------------------------------------------------------------------------- 1 | def huiwen(arr): 2 | if arr == None or len(arr) < 2: 3 | return arr 4 | stack = [] 5 | cur = arr 6 | while cur != None: 7 | stack.append(cur.val) 8 | cur = cur.next 9 | while stack: 10 | if stack.pop().val != arr.val: 11 | return False 12 | arr = arr.next 13 | return True 14 | -------------------------------------------------------------------------------- /数据结构与算法/链表问题/判断一个链表是否有环,如果有的话返回第一个进环的节点.py: -------------------------------------------------------------------------------- 1 | class Node(): 2 | def __init__(self, val=None): 3 | self.val = val 4 | self.next = None 5 | 6 | def getloopmeet(head): 7 | fast = head.next 8 | slow = head.next.next 9 | while fast != slow: 10 | if fast != None or slow != None: 11 | return None 12 | fast = fast.next.next 13 | slow = slow.next 14 | fast = head 15 | while fast != slow: 16 | fast = fast.next 17 | slow = slow.fast 18 | return fast 19 | -------------------------------------------------------------------------------- /数据结构与算法/链表问题/判断两个无环链表是否相交,相交则返回第一个相交节点.py: -------------------------------------------------------------------------------- 1 | class Node(): 2 | def __init__(self, val=None): 3 | self.val = val 4 | self.next = None 5 | 6 | def Loop(head1, head2): 7 | n1 = 0 8 | n2 = 0 9 | cur1 = head1 10 | cur2 = head2 11 | while cur1 != None: 12 | n1 += 1 13 | cur1 = cur1.next 14 | print(n1) 15 | while cur2 != None: 16 | n2 += 1 17 | cur2 = cur2.next 18 | print(n2) 19 | 20 | n = abs(n1 - n2) 21 | print(n) 22 | cur1 = head1 23 | cur2 = head2 24 | if n1 > n2: 25 | while n > 0: 26 | cur1 = cur1.next 27 | else: 28 | while n > 0: 29 | cur2 = cur2.next 30 | 31 | while cur1 != cur2: 32 | cur1 = cur1.next 33 | cur2 = cur2.next 34 | return cur1 35 | 36 | # node1 = Node([1, 2, 3]) 37 | # node2 = Node([2, 3]) 38 | # print(Loop(node1, node2)) 39 | -------------------------------------------------------------------------------- /数据结构与算法/链表问题/判断两个有环链表是否相交,相交则返回第一个相交节点.py: -------------------------------------------------------------------------------- 1 | # 在环外面就相交,或者在环上相交(可能在同一个交点上,可能不在同一个交点上) 2 | 3 | def bothLoop(head1, node1, head2, node2): 4 | if head1 == None or head2 == None: 5 | return None 6 | if node1 == node2: 7 | cur1 = head1 8 | cur2 = head2 9 | n = 0 10 | while cur1 != node1: 11 | n += 1 12 | cur1 = cur1.next 13 | while cur2 != node2: 14 | n -= 1 15 | cur2 = cur2.next 16 | if cur1 != cur2: 17 | return None 18 | cur1 = head1 if n >= 0 else head2 19 | cur2 = head1 if cur1 == head2 else head2 20 | n = abs(n) 21 | while n != 0: 22 | n -= 1 23 | cur1 = cur1.next 24 | while cur1 != cur2: 25 | cur1 = cur1.next 26 | cur2 = cur2.next 27 | return cur1 28 | else: 29 | cur1 = node1.next 30 | while cur1 != node1: 31 | if cur1 == node2: 32 | return node1 33 | cur = cur1.next 34 | return None 35 | 36 | def noLoop(head1, head2): 37 | if head1 == None or head2 == None: 38 | return None 39 | cur1 = head1 40 | cur2 = head2 41 | n = 0 42 | while cur1.next != None: 43 | n += 1 44 | cur1 = cur1.next 45 | while cur2.next != None: 46 | n -= 1 47 | cur2 = cur2.next 48 | if cur1 != cur2: 49 | return None 50 | cur1 = head1 if n >= 0 else head2 51 | cur2 = head1 if cur1 == head2 else head2 52 | n = abs(n) 53 | while n != 0: 54 | cur1 = cur1.next 55 | n -= 1 56 | while cur1 != cur2: 57 | cur1 = cur1.next 58 | cur2 = cur2.next 59 | return cur1 60 | 61 | def getLoopNode(head): 62 | if head == None or head.next == None or head.next.next == None: 63 | return None 64 | slow = head.next 65 | fast = head.next.next 66 | while slow != fast: 67 | if fast.next == None or fast.next.next == None: 68 | return None 69 | slow = slow.next 70 | fast = fast.next.next 71 | fast = head 72 | while slow != fast: 73 | slow = slow.next 74 | fast = fast.next 75 | return slow 76 | 77 | class Node: 78 | def __init__(self, val=None): 79 | self.val = val 80 | self.next = None 81 | 82 | def getIntersectNode(head1, head2): 83 | if head1 == None or head2 == None: 84 | return None 85 | # 判断环的入口在哪里 86 | node1 = getLoopNode(head1) 87 | node2 = getLoopNode(head2) 88 | if node1 == None and node2 == None: 89 | return noLoop(head1, head2) 90 | # 在环之前就相等,node1 = node2,在环中相等, 91 | if node1 != None and node2 != None: 92 | return bothLoop(head1, node1, head2, node2) 93 | return None 94 | -------------------------------------------------------------------------------- /数据结构与算法/链表问题/向有环的环形链表中插入新节点.py: -------------------------------------------------------------------------------- 1 | # 指针给的是节点值 2 | class Node(): 3 | def __init__(self, value=None): 4 | self.value = value 5 | self.next = None 6 | 7 | def insertnum(head, num): 8 | node = Node(num) 9 | if head == None: 10 | node.next = node 11 | return node 12 | node = head 13 | pre = node 14 | cur = node.next 15 | while cur != head: 16 | if pre.value > num and cur.value <= num: 17 | break 18 | pre = pre.next 19 | cur = cur.next 20 | # num 小于节点值,pre只跑到最后一个节点,node跑道头结点 21 | pre.next = node 22 | node.next = cur 23 | # 是按顺序来的,返回的是 head 或者 node ,是有顺序决定的 24 | return head if head.value < num else node 25 | 26 | node = Node() 27 | node.insertnum([[1, 2, 3], 5]) 28 | -------------------------------------------------------------------------------- /数据结构与算法/链表问题/在单链表中删除指定值的节点.py: -------------------------------------------------------------------------------- 1 | def remove(head, num): 2 | if head == None: 3 | return None 4 | stack = [] 5 | while head != None: 6 | if head.val != num: 7 | stack.append(head) 8 | head = head.next 9 | while stack: 10 | # 这是一个链接操作 11 | stack[-1].next = head 12 | head = stack.pop() 13 | return head 14 | -------------------------------------------------------------------------------- /数据结构与算法/链表问题/复制含有随机指针节点的链表.py: -------------------------------------------------------------------------------- 1 | class RandNode: 2 | def __init__(self, data): 3 | self.val = data 4 | self.next = None 5 | self.rand = None 6 | 7 | def copyListWithRand(head): 8 | if head == None: 9 | return None 10 | map = {} 11 | cur = head 12 | while cur != None: 13 | map[cur] = RandNode(cur.val) 14 | cur = cur.next 15 | cur = head 16 | while cur != None: 17 | map[cur].next = None if cur.next == None else map[cur.next] 18 | map[cur].next = None if cur.ramd == None else map[cur.rand] 19 | cur = cur.next 20 | return map[head] 21 | -------------------------------------------------------------------------------- /数据结构与算法/链表问题/将单向链表按某值划分成左边小,中间相等,右边大的形式.py: -------------------------------------------------------------------------------- 1 | # 将所有的节点放置到一个数组中,对这个数组进行partition调整(快排调整过程),再将每个数组中每个节点串起来即可。 2 | # 快排-递归 3 | 4 | def list(head, pivot): 5 | def partition(nodeArr, pivot): 6 | left = - 1 7 | right = len(nodeArr) 8 | index = 0 9 | while index < right: 10 | if nodeArr[index].val < pivot: 11 | left += 1 12 | nodeArr[left], nodeArr[index] = nodeArr[index], nodeArr[left] 13 | index += 1 14 | elif nodeArr[index].val == pivot: 15 | index += 1 16 | else: 17 | right -= 1 18 | nodeArr[index], nodeArr[right] = nodeArr[right], nodeArr[index] 19 | 20 | if head == None or head.next == None: 21 | return head 22 | cur = head 23 | n = 0 24 | while cur != None: 25 | n += 1 26 | cur = cur.next 27 | nodeArr = [] 28 | cur = head 29 | while cur != None: 30 | nodeArr.append(cur) 31 | cur = cur.next 32 | partition(nodeArr, pivot) 33 | for i in range(n - 1): 34 | nodeArr[i].next = nodeArr[i + 1] 35 | nodeArr[-1].next = None 36 | return nodeArr[0] 37 | -------------------------------------------------------------------------------- /数据结构与算法/链表问题/将单链表的每K个节点之间逆序.py: -------------------------------------------------------------------------------- 1 | # 每隔 k 个就逆序,用栈结构 2 | 3 | def reverseNode(head, k): 4 | def reverse(stack, pre, next): 5 | while stack: 6 | cur = stack.pop() 7 | if pre != None: 8 | cur = cur.next 9 | pre = cur 10 | # pre.next = next 11 | return pre 12 | 13 | if head == None or head.next == None or k < 2: 14 | return head 15 | stack = [] 16 | cur = head 17 | newHaed = head 18 | pre = None 19 | while cur != None: 20 | next = cur.next 21 | stack.append(cur) 22 | if len(stack) == k: 23 | pre = reverse(stack, pre, next) 24 | newHead = cur if newHaed == head else newHaed 25 | cur = next 26 | return newHaed 27 | -------------------------------------------------------------------------------- /数据结构与算法/链表问题/打印两个有序链表的公共部分.py: -------------------------------------------------------------------------------- 1 | class Node(): 2 | def __init__(self, val=None): 3 | self.val = val 4 | self.next = None 5 | 6 | def compare(head1, head2): 7 | if head1 == None or head2 == None: 8 | return [] 9 | 10 | res = [] 11 | while head1 != None and head2 != None: 12 | if head1.val > head2.val: 13 | head2 = head2.next 14 | elif head1.val < head2.val: 15 | head1 = head1.next 16 | else: 17 | print(head1) 18 | res.append(head1) 19 | head1 = head1.next 20 | head2 = head2.next 21 | return res 22 | 23 | node = Node([1, 3, 4, 7]) 24 | node1 = Node([2, 3, 5, 7, 9]) 25 | print(compare(node, node1)) 26 | -------------------------------------------------------------------------------- /数据结构与算法/链表问题/节点删除方式.py: -------------------------------------------------------------------------------- 1 | # 链表里删元素,但是不知道元素的头结点和链表本身结构 2 | def dele(node): 3 | if node == None: 4 | return None 5 | next = node.next 6 | if next == None: 7 | return [] 8 | node.val = next.val 9 | node.next = next.next 10 | -------------------------------------------------------------------------------- /编程题/Python 编程题-2.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | - [代码的完整性](#代码的完整性) 4 | - [1、数值的整数次方](#1数值的整数次方) 5 | - [2、打印1到最大的n位数](#2打印1到最大的n位数) 6 | - [3、O(1)时间删除链表结点](#3O(1)时间删除链表结点) 7 | - [4、调整数组顺序使寄数位于偶数前面](#4调整数组顺序使寄数位于偶数前面) 8 | 9 | - [代码的鲁棒性](#代码的鲁棒性) 10 | - [5、链表中倒数第k个结点](#5链表中倒数第k个结点) 11 | - [6、反转链表](#6反转链表) 12 | - [7、合并两个排序的链表](#7合并两个排序的链表) 13 | - [8、树的子结构](#8树的子结构) 14 | 15 | 16 | 17 | 18 | ## 代码的完整性 19 | ### 1、数值的整数次方 20 | 21 | > 要求:求一个数的整数次方 22 | > 23 | > 思路:需要考虑次方是正数、负数和0,基数是0 24 | > 25 | > 浮点数相等不能直接用== 26 | 27 | ```python 28 | def power(base, exponent): 29 | if equal_zero(base) and exponent < 0: 30 | raise ZeroDivisionError 31 | ret = power_value(base, abs(exponent)) 32 | if exponent < 0: 33 | return 1.0 / ret 34 | else: 35 | return ret 36 | 37 | 38 | def equal_zero(num): 39 | if abs(num - 0.0) < 0.0000001: 40 | return True 41 | 42 | 43 | def power_value(base, exponent): 44 | if exponent == 0: 45 | return 1 46 | if exponent == 1: 47 | return base 48 | ret = power_value(base, exponent >> 1) 49 | ret *= ret 50 | if exponent & 1 == 1: 51 | ret *= base 52 | return ret 53 | ``` 54 | 55 | ### 2、打印1到最大的n位数 56 | > 要求:输入n,打印出从1到最大的n位数 57 | > 58 | > 思路:Python中已经对大整数可以进行自动转换了,所以不需要考虑大整数溢出问题 59 | 60 | ```python 61 | def print_max_n(n): 62 | for i in xrange(10 ** n): 63 | print i 64 | ``` 65 | 66 | ### 3、O(1)时间删除链表结点 67 | > 要求:O(1)时间删除链表结点 68 | > 69 | > 思路:如果有后续结点,后续结点的值前移,删除后续结点,如果没有,只能顺序查找了 70 | 71 | ```python 72 | def delete_node(link, node): 73 | if node == link: # 只有一个结点 74 | del node 75 | if node.next is None: # node是尾结点 76 | while link: 77 | if link.next == node: 78 | link.next = None 79 | link = link.next 80 | else: 81 | node.val = node.next.val 82 | n_node = node.next 83 | node.next = n_node.next 84 | del n_node 85 | ``` 86 | ### 4、调整数组顺序使奇数位于偶数前面 87 | > 思路:使用两个指针,前后各一个,为了更好的扩展性,可以把判断奇偶部分抽取出来 88 | 89 | ```python 90 | def reorder(nums, func): 91 | left, right = 0, len(nums) - 1 92 | while left < right: 93 | while not func(nums[left]): 94 | left += 1 95 | while func(nums[right]): 96 | right -= 1 97 | if left < right: 98 | nums[left], nums[right] = nums[right], nums[left] 99 | 100 | 101 | def is_even(num): 102 | return (num & 1) == 0 103 | ``` 104 | 105 | ## 代码的鲁棒性 106 | 107 | ### 5、链表中倒数第k个结点 108 | > 要求:求单链表中的倒数第k个结点 109 | > 110 | > 思路:使用快慢指针,快的先走k-1步,需要考虑空链表以及k为0 111 | 112 | ```python 113 | def last_kth(link, k): 114 | if not link or k <= 0: 115 | return None 116 | move = link 117 | while move and k-1 >= 0: 118 | move = move.next 119 | k -= 1 120 | while move: 121 | move = move.next 122 | link = link.next 123 | if k == 0: 124 | return link.val 125 | return None 126 | ``` 127 | 128 | ### 6、反转链表 129 | > 要求:反转链表 130 | > 131 | > 思路:需要考虑空链表,只有一个结点的链表 132 | 133 | ```python 134 | def reverse_link(head): 135 | if not head or not head.next: 136 | return head 137 | then = head.next 138 | head.next = None 139 | last = then.next 140 | while then: 141 | then.next = head 142 | head = then 143 | then = last 144 | if then: 145 | last = then.next 146 | return head 147 | ``` 148 | 149 | ### 7、合并两个排序的链表 150 | > 要求:合并两个排序的链表 151 | > 152 | > 思路:使用递归 153 | 154 | ```python 155 | def merge_link(head1, head2): 156 | if not head1: 157 | return head2 158 | if not head2: 159 | return head1 160 | if head1.val <= head2.val: 161 | ret = head1 162 | ret.next = merge_link(head1.next, head2) 163 | else: 164 | ret = head2 165 | ret.next = merge_link(head1, head2.next) 166 | return ret 167 | 168 | ``` 169 | 170 | ### 8、树的子结构 171 | > 要求:判断一棵二叉树是不是另一个的子结构 172 | > 173 | > 思路:使用递归 174 | 175 | ```python 176 | def sub_tree(tree1, tree2): 177 | if tree1 and tree2: 178 | if tree1.val == tree2.val: 179 | return sub_tree(tree1.left, tree2.left) and sub_tree(tree1.right, tree2.right) 180 | else: 181 | return sub_tree(tree1.left, tree2) or sub_tree(tree1.right, tree2) 182 | if not tree1 and tree2: 183 | return False 184 | return True 185 | ``` -------------------------------------------------------------------------------- /编程题/丑数.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:把只包含因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含因子7。 3 | 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。 4 | ''' 5 | 6 | ''' 7 | 思路:按顺序把每个丑数放在数组中,求下一个丑数 8 | 下一个丑数必定由有数组中的某一个丑数A * 2, B * 3, C * 5 的中的最小值得来。 9 | 分析:在数组中必定有一个丑数M2, 在它之前的数 * 2 都小于当前最大丑数, 在它之后的数 * 2都大于当前最大丑数, 10 | 同样有M3, M5 11 | 12 | notes:题目的意思应该是质数因子,因为8的因子有1,2,4,8,显然不符合要求的,但是质数只有2 13 | 36ms 14 | 5732k 15 | ''' 16 | 17 | # -*- coding:utf-8 -*- 18 | class Solution: 19 | def GetUglyNumber_Solution(self, index): 20 | # write code here 21 | if index < 1: 22 | return 0 23 | res = [1] 24 | t2 = t3 = t5 = 0 25 | 26 | nextdex = 1 27 | while nextdex < index: 28 | minNum = min(res[t2] * 2, res[t3] * 3, res[t5] * 5) 29 | res.append(minNum) 30 | 31 | # 前进的步伐还是很小的,没一个数都考虑到了 32 | while res[t2] * 2 <= minNum: 33 | t2 += 1 34 | while res[t3] * 3 <= minNum: 35 | t3 += 1 36 | while res[t5] * 5 <= minNum: 37 | t5 += 1 38 | 39 | nextdex += 1 40 | 41 | return res[nextdex - 1] 42 | -------------------------------------------------------------------------------- /编程题/两种排序方法.py: -------------------------------------------------------------------------------- 1 | x, arr = input(), [] 2 | 3 | for i in range(int(x)): 4 | arr.append(input()) 5 | 6 | length, lex = sorted(arr, key=len), sorted(arr) 7 | 8 | if length == arr and lex == arr: 9 | print('both') 10 | elif length == arr: 11 | print('lengths') 12 | elif lex == arr: 13 | print('lexicographically') 14 | else: 15 | print('none') 16 | -------------------------------------------------------------------------------- /编程题/买苹果.py: -------------------------------------------------------------------------------- 1 | # 这种题目一般都是动态规划或者贪心算法 2 | # 当然可以暴力,我自己也只会暴力 3 | 4 | x = int(input()) 5 | 6 | def getcount(x): 7 | for i in range(x // 6 + 1): 8 | if (x - i * 6) % 8 == 0: 9 | return i + (x - i * 6) // 8 10 | return -1 11 | 12 | print(int(getcount(x))) 13 | -------------------------------------------------------------------------------- /编程题/二维数组中的查找.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。 3 | 请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。 4 | ''' 5 | 6 | ''' 7 | 方法一: 循环迭代查找,不是最优 8 | 284ms 9 | 5760k 10 | ''' 11 | 12 | class Solution: 13 | # array 二维列表 14 | def Find(self, target, array): 15 | # write code here 16 | if not array: 17 | return 18 | row = len(array) 19 | col = len(array[0]) 20 | 21 | for i in range(row): 22 | for j in range(col): 23 | if target == array[i][j]: 24 | return True 25 | 26 | return False 27 | 28 | ''' 29 | 方法二:上述方法的时间复杂度是O(n^2),最优化的方式是从左下或者右上开始搜索 30 | 从右上开始搜索,如果数组中的数比该数要大,那么想左移动一位,如果数组中的数比该数小,向下移动一位, 31 | 因为数组本身是从左到右依次增大,从上到下一次增大 32 | 281ms 33 | 5644k 34 | ''' 35 | 36 | class Solution: 37 | # array 二维列表 38 | def Find(self, target, array): 39 | # write code here 40 | raw = len(array) 41 | col = len(array[0]) 42 | 43 | i = 0 44 | j = col - 1 45 | while i < raw and j >= 0: 46 | if array[i][j] > target: 47 | j -= 1 48 | elif array[i][j] < target: 49 | i += 1 50 | else: 51 | return True 52 | return False 53 | -------------------------------------------------------------------------------- /编程题/二进制中1的个数.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。 3 | ''' 4 | 5 | ''' 6 | 如果一个整数不为0,那么这个整数至少有一位是1。如果我们把这个整数减1,那么原来处在整数最右边的1就会变为0,原来在1 7 | 后面的所有的0都会变成1(如果最右边的1后面还有0的话)。其余所有位将不会受到影响。 8 | 举个例子:一个二进制数1100,从右边数起第三位是处于最右边的一个1。减去1后,第三位变成0,它后面的两位0变成了1, 9 | 而前面的1保持不变,因此得到的结果是1011.我们发现减1的结果是把最右边的一个1开始的所有位都取反了。这个时候如果我们 10 | 再把原来的整数和减去1之后的结果做与运算,从原来整数最右边一个1那一位开始所有位都会变成0。如1100&1011=1000.也就是 11 | 说,把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0.那么一个整数的二进制有多少个1,就可以进行多少 12 | 次这样的操作。 13 | 14 | 但是负数使用补码表示的,对于负数,最高位为1,而负数在计算机是以补码存在的,往右移,符号位不变,符号位1往右移, 15 | 最终可能会出现全1的情况,导致死循环。与0xffffffff相与,就可以消除负数的影响 16 | 17 | 24ms 18 | 5632k 19 | ''' 20 | 21 | # -*- coding:utf-8 -*- 22 | class Solution: 23 | def NumberOf1(self, n): 24 | # write code here 25 | count = 0 26 | if n<0: 27 | n = n & 0xffffffff 28 | while n: 29 | count += 1 30 | n = n & (n-1) 31 | return count 32 | -------------------------------------------------------------------------------- /编程题/优雅的点.py: -------------------------------------------------------------------------------- 1 | x = int(input()) 2 | y = int(x ** 0.5) 3 | 4 | count = 0 5 | for i in range(0, y + 1): 6 | for j in range(0, y + 1): 7 | if i ** 2 + j ** 2 == x: 8 | if i == 0 and j == 0: 9 | count += 1 10 | if i == 0 or j == 0: 11 | count += 2 12 | if i != 0 and j != 0: 13 | count += 4 14 | 15 | print(count) 16 | -------------------------------------------------------------------------------- /编程题/分田地.py: -------------------------------------------------------------------------------- 1 | # 就是把田地横竖各三刀,分成16块。取16块里面价值的最小值Di。然后求所有划分方法Di构成集合中的最大值 2 | -------------------------------------------------------------------------------- /编程题/分苹果.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 1.先判断数组总和是否可以被n整除,否则没法分均匀。 3 | 2.然后再判断每个牛的苹果与平均值的差,若差不是2的倍数,也无法移动成功 4 | 3.如果前两个条件满足就把比均值多的苹果数减去均值,将其总数除以2, 5 | 就是得到的移动次数了。 6 | ''' 7 | 8 | n = int(input()) 9 | ls = [int(i) for i in input().split()] 10 | 11 | def splitapple(n, ls): 12 | if sum(ls) % n != 0: 13 | return -1 14 | count = 0 15 | avg = sum(ls) // n 16 | 17 | for i in ls: 18 | if (i - avg) % 2 != 0: 19 | return -1 20 | if i > avg: 21 | count += (i - avg) 22 | return (int(count // 2)) 23 | 24 | print(splitapple(n, ls)) 25 | -------------------------------------------------------------------------------- /编程题/删除公共字符.py: -------------------------------------------------------------------------------- 1 | x = input().split() 2 | k = ''.join(list(map(str, input().split()))) 3 | # k = x[-1] 4 | # x.remove(x[-1]) 5 | 6 | res = [] 7 | for i in x: 8 | for j in i: 9 | if j in k: 10 | # i.replace(j, '') 11 | # print(i) 12 | n = i.index(j) 13 | i = i[:n] + i[n + 1:] 14 | res.append(i) 15 | 16 | print(' '.join(list(map(str, res)))) 17 | -------------------------------------------------------------------------------- /编程题/删除字符串中出现次数最少的字符.py: -------------------------------------------------------------------------------- 1 | # x = input() 2 | # 3 | # res = [] 4 | # for i in x: 5 | # if x.count(i) > 1: 6 | # res.append(i) 7 | # 8 | # print(''.join(res)) 9 | 10 | from collections import defaultdict 11 | while True: 12 | try: 13 | a = input() 14 | dd = defaultdict(int) 15 | for i in a: 16 | dd[i] += 1 17 | for i in dd: 18 | if dd[i] == min(dd.values()): 19 | a = a.replace(i,'') 20 | print(a) 21 | except: 22 | break 23 | 24 | -------------------------------------------------------------------------------- /编程题/删除链表中重复的节点.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 3 | 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5 4 | ''' 5 | 6 | ''' 7 | 思路一:将链表里面所有的数存在一个列表里面,然后把列表里面只出现一次的数提取出来,在新建一个链表放进去 8 | 9 | 28ms 10 | 5632k 11 | ''' 12 | 13 | # -*- coding:utf-8 -*- 14 | class ListNode: 15 | def __init__(self, x): 16 | self.val = x 17 | self.next = None 18 | 19 | class Solution: 20 | def deleteDuplication(self, pHead): 21 | # write code here 22 | res = [] 23 | while pHead: 24 | res.append(pHead.val) 25 | pHead = pHead.next 26 | 27 | # filter函数和map相似,但是filter是返回布尔值去去输入列表进行判断 28 | res = list(filter(lambda c: res.count(c) == 1, res)) 29 | 30 | newlist = ListNode(0) 31 | pre = newlist 32 | for i in res: 33 | node = ListNode(i) 34 | pre.next = node 35 | pre = pre.next 36 | return newlist.next 37 | 38 | ''' 39 | 思路二: 40 | 是有两个循环判断的控制,上一个是主要对应2 - 3 - 4这种情况的,可以很快的把头指针移过来,下面这个循环是对应 41 | 存在相同值的,不断循环找下一个值。 42 | 头指针 43 | PreNode 44 | Head - 1 - 1 - 1 - 2 - 2 - 3 - 4 - 4 - 4 - 5 - 5 - 6 - null 45 | pNode 46 | NextNode 47 | False 48 | pToBeDel 49 | PreNode 50 | 51 | 35ms 52 | 6008k 53 | ''' 54 | 55 | # -*- coding:utf-8 -*- 56 | # class ListNode: 57 | # def __init__(self, x): 58 | # self.val = x 59 | # self.next = None 60 | class Solution: 61 | def deleteDuplication(self, pHead): 62 | # write code here 63 | if pHead == None: 64 | return 65 | preHead = None 66 | pNode = pHead 67 | while pNode != None: 68 | needDelete = False 69 | nextNode = pNode.next 70 | if nextNode != None and nextNode.val == pNode.val: 71 | needDelete = True 72 | if needDelete == False: 73 | preHead = pNode 74 | pNode = pNode.next 75 | else: 76 | nodeVal = pNode.val 77 | pToBeDel = pNode 78 | while pToBeDel != None and pToBeDel.val == nodeVal: 79 | pToBeDel = pToBeDel.next 80 | if preHead == None: 81 | pHead = pToBeDel 82 | pNode = pToBeDel 83 | continue 84 | else: 85 | preHead.next = pToBeDel 86 | pNode = preHead 87 | return pHead 88 | -------------------------------------------------------------------------------- /编程题/判断两个IP是否属于同一子网.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yongxinz/backend-interview/888e5d4fa8a81bbcbb1b36b596e306989df2ffc1/编程题/判断两个IP是否属于同一子网.py -------------------------------------------------------------------------------- /编程题/包含min函数的栈.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数。 3 | ''' 4 | 5 | ''' 6 | 我们可以设计两个栈:Stack和StackMin,一个就是普通的栈,另外一个存储push进来的最小值。 7 | 首先是push操作: 8 | 每次压入的数据newNum都push进Stack中,然后判断StackMin是否为空,如果为空那也把newNum同步压入StackMin里; 9 | 如果不为空,就先比较newNum和StackMin中栈顶元素的大小,如果newNum较大,那就不压入StackMin里,只压入一个最小值 10 | 否则就同步压入StackMin里。弹出时,同步弹出,这是一个栈结构。 11 | 12 | 24ms 13 | 5760k 14 | ''' 15 | 16 | # -*- coding:utf-8 -*- 17 | class Solution: 18 | def __init__(self): 19 | self.stack = [] 20 | self.minstack = [] 21 | 22 | def push(self, node): 23 | # write code here 24 | self.stack.append(node) 25 | if self.minstack == [] or node < self.min(): 26 | self.minstack.append(node) 27 | else: 28 | self.minstack.append(self.min()) 29 | 30 | def pop(self): 31 | # write code here 32 | if self.minstack == [] or self.stack == []: 33 | return None 34 | self.minstack.pop() 35 | self.stack.pop() 36 | 37 | def top(self): 38 | # write code here 39 | return self.stack[-1] 40 | 41 | def min(self): 42 | # write code here 43 | return self.minstack[-1] -------------------------------------------------------------------------------- /编程题/取近似值.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:写出一个程序,接受一个正浮点数值,输出该数值的近似整数值。如果小数点后数值大于等于5,向上取整; 3 | 小于5,则向下取整。 4 | ''' 5 | 6 | ''' 7 | 思路: 8 | 9 | 32ms 10 | 3436k 11 | ''' 12 | 13 | try: 14 | while True: 15 | a = eval(input()) 16 | if (a - int(a)) >= 0.5: 17 | print(int(a) + 1) 18 | else: 19 | print(int(a)) 20 | except: 21 | pass 22 | -------------------------------------------------------------------------------- /编程题/变态青蛙跳.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法 3 | ''' 4 | 5 | ''' 6 | 因为n级台阶,第一步有n种跳法:跳1级、跳2级、到跳n级 7 | 跳1级,剩下n-1级,则剩下跳法是f(n-1) 8 | 跳2级,剩下n-2级,则剩下跳法是f(n-2) 9 | 所以f(n)=f(n-1)+f(n-2)+...+f(1) 10 | 因为f(n-1)=f(n-2)+f(n-3)+...+f(1) 11 | 所以f(n)=2*f(n-1) 12 | 然后求解这个无穷级数的和,正确答案应该是:每次至少跳一个,至多跳n个,一共有f(n)=2n-1种跳法 13 | 29ms 14 | 5632k 15 | ''' 16 | 17 | # -*- coding:utf-8 -*- 18 | class Solution: 19 | def jumpFloorII(self, number): 20 | # write code here 21 | return 2**(number-1) -------------------------------------------------------------------------------- /编程题/合唱团.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目要求是 n 个学生中选择 k 个,使这 k 个学生的能力值乘积最大,这是一个最优化问题,在优化过程中,提出了相邻 3 | 两个学生的位置编号差不超过 d 的约束。 4 | 5 | 递归或者动态变化 6 | 1.对该问题的分解是关键 7 | 从 n 个学生中,选择 k 个,可以看成是:先从 n 个学生里选择最后1个,然后在剩下的里选择 k-1 个,并且让这1个和 8 | 前一个满足约束条件 9 | 2.数学描述 10 | 记第 k 个人的位置为1,则可以用f[1][k]表示从 n 个人中选择 k 个的方案。 11 | 然后它的子问题,需要从1前面的left个人里面,选择 k-1 个,这里left表示 12 | k-1 个人中最后一个(即第k-1个)人的位置,因此,子问题可以表示成f[left][k-1] 13 | 14 | 在n和k定了之后,需要求解出n个学生选择k个能力乘积的最大值。因为能力值有正 15 | 有负,所以 16 | 当1对应的学生能力值为正时 17 | f[1][k] = max{f[left][k-1] arr[i]}(min{k-1,1-d}<=left<=1-1) 18 | 19 | ''' 20 | 21 | x1 = int(input()) 22 | x2 = [int(i) for i in input().split()] 23 | k, d = map(int, input().split()) 24 | 25 | fmax = [[0 for i in range(x1)] for i in range(k)] 26 | print(fmax) 27 | fmin = [[0 for i in range(x1)] for i in range(k)] 28 | 29 | for kk in range(k): 30 | for i in range(x1): 31 | if kk == 0 or i == 0: 32 | fmax[kk][i] = x2[i] 33 | fmin[kk][i] = x2[i] 34 | else: 35 | M = [] 36 | for t in range(max(i - d, 0), i): 37 | M.extend([fmax[kk - 1][t] * x2[i], fmin[kk - 1][t] * x2[i]]) 38 | fmax[kk][i], fmin[kk][i] = max(M), min(M) 39 | print(max(fmax[k - 1])) 40 | -------------------------------------------------------------------------------- /编程题/合唱队.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 两遍最长递增子序列,第一遍从左往右,第二遍从右往左,然后把两遍动态 3 | 规划的结果相加,区最大的那个,比如 8 186 186 150 200 160 130 197 200 4 | 第一遍dp的结果是1 1 1 2 2 1 3 4,第二遍dp的结果是3 3 2 3 2 1 1 1, 5 | 那么相加最大是5,所以需要出列的同学的个数是8-5+1 = 4. 6 | 7 | ''' -------------------------------------------------------------------------------- /编程题/合并表记录.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:数据表记录包含表索引和数值,请对表索引相同的记录进行合并,即将相同索引的数值进行求和运算, 3 | 输出按照key值升序进行输出。 4 | ''' 5 | 6 | ''' 7 | 思路: 8 | 9 | 10 | ''' 11 | 12 | n = input().split() 13 | res = {} 14 | for i in range(int(n[0])): 15 | k, d = map(int, input().split()) 16 | if k in res.keys(): 17 | res[k] = res[k] + d 18 | else: 19 | res[k] = d 20 | 21 | for k, d in res.items(): 22 | print(str(k) + ' ' + str(d)) 23 | -------------------------------------------------------------------------------- /编程题/和为s的两个数字.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:输入一个递增排序的数组和一个数字S,在数组中查找两个数,是的他们的和正好是S,如果有多对数字的和等于S, 3 | 输出两个数的乘积最小的 4 | ''' 5 | 6 | ''' 7 | 思路:设定两个指针,一个指向数组的起点,一个指向数组的终点,然后对两个数字求和,如果和大于目标值,则把后一个指针前移, 8 | 如果和小于目标值,则把前一个指针后移。两个指针交汇的时候如果还没找到,就终止操作。 9 | 10 | 37ms 11 | 5628k 12 | ''' 13 | 14 | # -*- coding:utf-8 -*- 15 | class Solution: 16 | def FindNumbersWithSum(self, array, tsum): 17 | # write code here 18 | if array == None or len(array) <= 0 or array[-1] + array[-2] < tsum: 19 | return [] 20 | start = 0 21 | end = len(array) - 1 22 | while start < end: 23 | if array[start] + array[end] < tsum: 24 | start += 1 25 | elif array[start] + array[end] > tsum: 26 | end -= 1 27 | else: 28 | return [array[start], array[end]] 29 | return [] 30 | 31 | 32 | -------------------------------------------------------------------------------- /编程题/和为s的连续整数序列.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此, 3 | 他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20, 4 | 21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck! 5 | ''' 6 | 7 | ''' 8 | 思路:设定两个指针,先分别指向数字1和数字2,并设这两个指针为small和big,对small和big求和,如果和大于目标值, 9 | 则从当前和中删除small值,并把small值加一,如果和小于目标值,则把big值加一,再把新的big值加入和中。如果和等于 10 | 目标值,就输出small到big的序列,同时把big加一并加入和中,继续之前的操作。 11 | 12 | 举例看起来更好理解一点,虽然是while循环里面没有做small到big之间的连接加减,但是由于都是从相邻出发,加和都是基于 13 | 1 2 3相邻数基础之上的加和,大了就对第一个数不断减,小了就对最后一个数不断加即可。 14 | 举例: 输入 tsum = 5 15 | 1 2 16 | middle = 3 cursum = 3 17 | 1 < 3: 18 | 3 < 5: 19 | 1 2 3 20 | cursum = 1 + 2 + 3 = 6 21 | 6 > 5: 22 | cursum - small = 6 - 1 = 5 23 | return 2 3 24 | 25 | 30ms 26 | 5620k 27 | ''' 28 | 29 | # -*- coding:utf-8 -*- 30 | class Solution: 31 | def FindContinuousSequence(self, tsum): 32 | # write code here 33 | if tsum < 3: 34 | return [] 35 | small = 1 36 | big = 2 37 | middle = (tsum + 1) // 2 38 | cursum = big + small 39 | output = [] 40 | 41 | while small < middle: 42 | if cursum == tsum: 43 | output.append(list(range(small, big + 1))) 44 | while cursum > tsum and small < middle: 45 | cursum -= small 46 | small += 1 47 | if cursum == tsum: 48 | output.append(list(range(small, big + 1))) 49 | big += 1 50 | cursum += big 51 | return output 52 | -------------------------------------------------------------------------------- /编程题/回文序列.py: -------------------------------------------------------------------------------- 1 | X = int(input()) 2 | n = [int(i) for i in input().split()] 3 | 4 | def huiwen(n, start, end): 5 | left = n[start] 6 | right = n[end] 7 | time = 0 8 | while start < end: 9 | if left < right: 10 | start += 1 11 | left += n[start] 12 | time += 1 13 | elif left > right: 14 | end -= 1 15 | right += n[right] 16 | time += 1 17 | elif left == right: 18 | start += 1 19 | end -= 1 20 | left = n[left] 21 | right = n[right] 22 | return time 23 | 24 | print(huiwen(n, 0, X - 1)) 25 | -------------------------------------------------------------------------------- /编程题/图片整理.py: -------------------------------------------------------------------------------- 1 | while True: 2 | try: 3 | x = input() 4 | res = [] 5 | res1 = [] 6 | for i in x: 7 | res.append(ord(i)) 8 | for j in sorted(res): 9 | res1.append(chr(j)) 10 | print(''.join(res1)) 11 | except: 12 | break 13 | -------------------------------------------------------------------------------- /编程题/圆圈中最后剩下的数.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了 3 | 一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。 4 | 每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始, 5 | 继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额 6 | 有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1) 7 | ''' 8 | 9 | ''' 10 | 思路:约瑟夫环问题 11 | 递推公式:f[i] = (f[i-1]+m)%i 12 | 详细解释:http://blog.csdn.net/u012505432/article/details/51747181 13 | 14 | 26ms 15 | 5632k 16 | ''' 17 | 18 | # -*- coding:utf-8 -*- 19 | class Solution: 20 | def LastRemaining_Solution(self, n, m): 21 | # write code here 22 | if n < 1 or m < 1: 23 | return -1 24 | temp = 0 25 | for i in range(1, n + 1): 26 | temp = (temp + m) % i 27 | return temp 28 | 29 | -------------------------------------------------------------------------------- /编程题/地下迷宫.py: -------------------------------------------------------------------------------- 1 | # dfs 2 | -------------------------------------------------------------------------------- /编程题/坐旋转字符串.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。 3 | 对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后 4 | 的结果,即“XYZdefabc”。是不是很简单?OK,搞定它 5 | ''' 6 | 7 | ''' 8 | 思路一: 9 | 10 | 29ms 11 | 5760k 12 | ''' 13 | 14 | # -*- coding:utf-8 -*- 15 | class Solution: 16 | def LeftRotateString(self, s, n): 17 | # write code here 18 | return s[n:] + s[:n] 19 | 20 | ''' 21 | 思路二:对原字符串进行扩充两倍,在这个基础上直接从要反转的地方取就可以,相当于前n个字符串翻转了,思想非常好 22 | 23 | 28ms 24 | 5760k 25 | ''' 26 | 27 | # -*- coding:utf-8 -*- 28 | class Solution: 29 | def LeftRotateString(self, s, n): 30 | # write code here 31 | if not s: 32 | return '' 33 | length = len(s) 34 | s += s 35 | return s[n:length + n] 36 | 37 | ''' 38 | 尽量避免使用内置函数 39 | 思路三:首先需要写一个reverse函数,把任何输入的字符串完全翻转。然后根据题目中给出的左旋转字符串的个数n, 40 | 用全字符串长度length减去旋转字符串个数n,求得对于新的字符串应该在哪一位进行旋转,然后分别旋转前[:length-n]子 41 | 串和[length-n:]子串,重新拼接两个子串即可。 42 | 举例: n=2 43 | 1 2 3 4 5 44 | 5 4 3 2 1 整体反转 45 | 3 4 5 1 2 对于5-2=3部分翻转,对于最后2 1再部分翻转 46 | 47 | 28ms 48 | 5760k 49 | ''' 50 | 51 | # -*- coding:utf-8 -*- 52 | class Solution: 53 | def LeftRotateString(self, s, n): 54 | # write code here 55 | if not s or len(s) < n or n < 0: 56 | return '' 57 | s = list(s) 58 | length = len(s) 59 | s = self.Reverse(s) 60 | s1 = self.Reverse(s[:length - n]) 61 | s2 = self.Reverse(s[length - n:]) 62 | result = ''.join(s1) + ''.join(s2) 63 | return result 64 | 65 | def Reverse(self, s): 66 | start = 0 67 | end = len(s) - 1 68 | while start < end: 69 | s[start], s[end] = s[end], s[start] 70 | start += 1 71 | end -= 1 72 | return s 73 | -------------------------------------------------------------------------------- /编程题/坐标移动.py: -------------------------------------------------------------------------------- 1 | while True: 2 | try: 3 | x = input().split(';') 4 | res = [0, 0] 5 | for i in range(len(x)): 6 | if x[i] == '': 7 | continue 8 | # print(x[i]) 9 | # print(x[i][0]) 10 | if x[i][0] == 'A': 11 | # print(''.join(x[i][1:])) 12 | if ''.join(x[i][1:]).isnumeric(): 13 | res[0] -= int(x[i][1:]) 14 | # print(res[0]) 15 | else: 16 | continue 17 | elif x[i][0] == 'D': 18 | if ''.join(x[i][1:]).isnumeric(): 19 | res[0] += int(x[i][1:]) 20 | else: 21 | continue 22 | elif x[i][0] == 'W': 23 | if ''.join(x[i][1:]).isnumeric(): 24 | res[1] += int(x[i][1:]) 25 | else: 26 | continue 27 | elif x[i][0] == 'S': 28 | if ''.join(x[i][1:]).isnumeric(): 29 | res[1] -= int(x[i][1:]) 30 | else: 31 | continue 32 | 33 | print(str(res[0]) + ',' + str(res[1])) 34 | except: 35 | break 36 | 37 | -------------------------------------------------------------------------------- /编程题/字串的连接最长路径查找.py: -------------------------------------------------------------------------------- 1 | n = input().split() 2 | 3 | res = [] 4 | print(int(n[0])) 5 | for i in range(int(n[0])): 6 | res.append(input().split()) 7 | 8 | res.sort() 9 | # print(res) 10 | 11 | for i in res: 12 | print(''.join(i)) 13 | -------------------------------------------------------------------------------- /编程题/字符串中找出连续最长的数字串.py: -------------------------------------------------------------------------------- 1 | # 在于判断是不是数的这个isnumeric()函数 2 | x = input() 3 | curlen, curstr, maxlen, maxstr = 0, '', 0, '' 4 | 5 | for i, v in enumerate(x): 6 | if v.isnumeric(): 7 | curlen += 1 8 | curstr += v 9 | if curlen > maxlen: 10 | maxlen = curlen 11 | maxstr = curstr 12 | else: 13 | curlen = 0 14 | curstr = '' 15 | print(maxstr) 16 | -------------------------------------------------------------------------------- /编程题/字符串分割.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:•连续输入字符串,请按长度为8拆分每个字符串后输出到新的字符串数组; 3 | •长度不是8整数倍的字符串请在后面补数字0,空字符串不处理。 4 | ''' 5 | 6 | ''' 7 | 思路: 8 | 9 | 37ms 10 | 3696k 11 | ''' 12 | 13 | str1 = input() 14 | str2 = input() 15 | 16 | def out(str): 17 | a = len(str) % 8 18 | if a != 0: 19 | str = str + ('0' * (8 - a)) 20 | for i in range(len(str) // 8): 21 | print(str[i * 8:8 * (i + 1)]) 22 | 23 | out(str1) 24 | out(str2) 25 | -------------------------------------------------------------------------------- /编程题/字符串加密.py: -------------------------------------------------------------------------------- 1 | # while True: 2 | # try: 3 | # x = input() 4 | # res = [] 5 | # for i in range(len(x)): 6 | # print(x[i]) 7 | # if x[i].islower(): 8 | # print(x[i]) 9 | # if x[i] == 'z': 10 | # x[i] = 'a' 11 | # else: 12 | # y1 = chr(ord(x[i]) + 1) 13 | # # print(y) 14 | # res.append(y1.upper()) 15 | # # print(res) 16 | # if x[i].isnumeric(): 17 | # if x[i] == '9': 18 | # x[i] = '0' 19 | # else: 20 | # y2 = str(int(x[i]) + 1) 21 | # res.append(y2) 22 | # if x[i].isupper(): 23 | # if x[i] == 'Z': 24 | # x[i] = 'z' 25 | # else: 26 | # y3 = chr(ord(x[i]) - 1) 27 | # res.append(y3.lower()) 28 | # print(''.join(res)) 29 | # 30 | # except: 31 | # break 32 | 33 | while True: 34 | try: 35 | a, b = input(), input() 36 | resA, resB = "", "" 37 | for i in a: 38 | if i.isupper(): 39 | if i != "Z": 40 | resA += chr(ord(i) + 1).lower() 41 | else: 42 | resA += "a" 43 | elif i.islower(): 44 | if i != "z": 45 | resA += chr(ord(i) + 1).upper() 46 | else: 47 | resA += "A" 48 | elif i.isdigit(): 49 | if i != "9": 50 | resA += chr(ord(i) + 1) 51 | else: 52 | resA += "0" 53 | for i in b: 54 | if i.isupper(): 55 | if i != "A": 56 | resB += chr(ord(i) - 1).lower() 57 | else: 58 | resB += "z" 59 | elif i.islower(): 60 | if i != "a": 61 | resB += chr(ord(i) - 1).upper() 62 | else: 63 | resB += "Z" 64 | elif i.isdigit(): 65 | if i != "0": 66 | resB += chr(ord(i) - 1) 67 | else: 68 | resB += "9" 69 | print(resA) 70 | print(resB) 71 | except: 72 | break 73 | -------------------------------------------------------------------------------- /编程题/字符串加密2.py: -------------------------------------------------------------------------------- 1 | while True: 2 | try: 3 | x = input() 4 | n = input().split() 5 | 6 | res = [] 7 | res1 = [] 8 | Cap = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 9 | 'V', 10 | 'W', 'X', 'Y', 'Z'] 11 | for i in x: 12 | if i not in res: 13 | res.append(i) 14 | else: 15 | continue 16 | for i in res: 17 | res1.append(i.upper()) 18 | 19 | res2 = [] 20 | for i in Cap: 21 | if i not in res1: 22 | res2.append(i) 23 | else: 24 | continue 25 | 26 | res3 = [] 27 | newres = res1 + res2 28 | # print(newres) 29 | 30 | for i in ''.join(n): 31 | if i.isupper(): 32 | if i in Cap: 33 | res3.append(newres[Cap.index(i)]) 34 | if i.islower(): 35 | if i.upper() in Cap: 36 | res3.append(newres[Cap.index(i.upper())].lower()) 37 | 38 | print(''.join(res3)) 39 | except: 40 | break 41 | -------------------------------------------------------------------------------- /编程题/字符串合并处理.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 将输入的两个字符串合并,要求是下标为奇数的字符和下标为偶数的字符分别从小到大 3 | 排序。 4 | ''' 5 | 6 | while True: 7 | try: 8 | dic = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'] 9 | 10 | # s是输入的合并后的字符串 11 | s = input().replace(' ', '') 12 | 13 | # ss为最终返回的字符串 14 | ss = '' 15 | 16 | # 字符串的奇数子串和偶数子串 17 | odd, even = '', '' 18 | # 经过下面的循环,提取奇数和偶数的子串 19 | for i, v in enumerate(s): 20 | if i % 2 == 0: 21 | even += v 22 | else: 23 | odd += v 24 | # 奇数与偶数部分排序 25 | odd = ''.join(sorted(odd)) 26 | even = ''.join(sorted(even)) 27 | 28 | # 如果字符串在0123456789abcdef范围内,对其做变换,否则不做任何处理 29 | for i in range(len(even)): 30 | if even[i] in '0123456789abcdefABCDEF': 31 | ss += dic[int(bin(dic.index(even[i].upper())).replace("0b", "").rjust(4, "0")[::-1], 2)] 32 | else: 33 | ss += even[i] 34 | # 注意偶数串可能比奇数串长一个字符,所以要判断一下 35 | if len(odd) != i: 36 | if len(odd) != i: 37 | ss += dic[int(bin(dic.index(odd[i].upper())).replace('0b', '').rjust(4, '0')[::-1], 2)] 38 | else: 39 | ss += odd[i] 40 | print(ss) 41 | except: 42 | break 43 | -------------------------------------------------------------------------------- /编程题/字符串排序.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 编写一个程序,将输入字符串中的字符按如下规则排序。 3 | 规则 1 :英文字母从 A 到 Z 排列,不区分大小写。 4 | 如,输入: Type 输出: epTy 5 | 规则 2 :同一个英文字母的大小写同时存在时,按照输入顺序排列。 6 | 如,输入: BabA 输出: aABb 7 | 规则 3 :非英文字母的其它字符保持原来的位置。 8 | 如,输入: By?e 输出: Be?y 9 | ''' 10 | 11 | while True: 12 | try: 13 | a = input() 14 | # res 是最终返回的字符串的列表形式,char 是提取的英文字母 15 | res, char = [False] * len(a), [] 16 | # 经过这个循环,把相应的非英文字母及其位置存储到 res 中,并且把英文字母提取出来 17 | for i, v in enumerate(a): 18 | if v.isalpha(): 19 | char.append(v) 20 | else: 21 | res[i] = v 22 | # 使用 lambda 表示式排序 23 | char.sort(key=lambda c: c.lower()) 24 | # 将 char 中对应的字符填到 res 中 25 | for i, v in enumerate(res): 26 | if not v: 27 | res[i] = char[0] 28 | char.pop(0) 29 | print(''.join(res)) 30 | except: 31 | break 32 | -------------------------------------------------------------------------------- /编程题/字符串最后一个单词的长度.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:计算字符串最后一个单词的长度,单词以空格隔开。 3 | ''' 4 | 5 | ''' 6 | 思路:split() 空格为界,字符串转列表 7 | 8 | 38ms 9 | 3824k 10 | ''' 11 | 12 | a = input().split() 13 | if len(a) > 1: 14 | print(len(a[-1])) 15 | else: 16 | print(len(a[0])) 17 | -------------------------------------------------------------------------------- /编程题/字符串的排列.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:输入一个字符串,按字典序打印出该字符串中字符的所有排列。 3 | 例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。 4 | ''' 5 | 6 | ''' 7 | 依次取一个元素,然后依次和之前递归形成的所有子串组合,形成新的字符串。 8 | 33ms 9 | 5632k 10 | ''' 11 | 12 | # -*- coding:utf-8 -*- 13 | class Solution: 14 | def Permutation(self, ss): 15 | # write code here 16 | if not len(ss): 17 | return [] 18 | if len(ss) == 1: 19 | return list(ss) 20 | 21 | charList = list(ss) 22 | charList.sort() 23 | pStr = [] 24 | for i in range(len(charList)): 25 | if i > 0 and charList[i] == charList[i-1]: 26 | continue 27 | temp = self.Permutation(''.join(charList[:i])+''.join(charList[i+1:])) 28 | for j in temp: 29 | pStr.append(charList[i]+j) 30 | return pStr -------------------------------------------------------------------------------- /编程题/字符串运用-密码截取.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Catcher是MCA国的情报员,他工作时发现敌国会用一些对称的密码进行通信,比如像这些ABBA,ABA,A,123321,但是他们有时会在开始或结束时 3 | 加入一些无关的字符以防止别国破解。比如进行下列变化 ABBA->12ABBA,ABA->ABAKK,123321->51233214 。因为截获的串太长了,而且存在多 4 | 种可能的情况(abaaab可看作是aba,或baaab的加密形式),Cathcer的工作量实在是太大了,他只能向电脑高手求助,你能帮Catcher找出最长的 5 | 有效密码串吗? 6 | ''' 7 | # x = input().split() 8 | # 9 | # def longest(x): 10 | # maximum = '' 11 | # temp = '' 12 | # for index in range(len(x)): 13 | # if len(maximum) >= len(x) - index: 14 | # break 15 | # for index2 in range(index + len(maximum), len(x) + 1): 16 | # fw = x[index:index2] 17 | # bw = fw[::-1] 18 | # if len(temp) > len(maximum): 19 | # maximum = temp 20 | # return maximum 21 | # 22 | # print(longest(x)) 23 | 24 | 25 | ''' 26 | 动态规划 27 | true j=i 28 | dp[j][i] = str[i] == str[j] i-j=1 29 | str[i] == str[j] && dp[j+1][i-1] i-j>1 30 | ''' 31 | 32 | def longestpalindrome(s): 33 | if s == s[::-1]: 34 | return len(s) 35 | maxlen = 0 36 | for i in range(len(s)): 37 | if i - maxlen >= 1 and s[i - maxlen - 1:i + 1] == s[i - maxlen - 1:i + 1][::-1]: 38 | maxlen += 2 39 | print(s[i - maxlen - 1:i + 1], maxlen) 40 | continue 41 | if i - maxlen >= 0 and s[i - maxlen:i + 1] == s[i - maxlen:i + 1][::-1]: 42 | maxlen += 1 43 | print(s[i - maxlen - 1:i + 1], maxlen) 44 | return maxlen 45 | 46 | while True: 47 | try: 48 | a = input() 49 | if a: 50 | print(longestpalindrome(a)) 51 | except: 52 | break 53 | -------------------------------------------------------------------------------- /编程题/字符流中第一个不重复的字符.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时, 3 | 第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。 4 | ''' 5 | 6 | ''' 7 | 思路:引入两个辅助存储空间。一个Dict存储当前出现的字符以及字符出现的次数,一个List存储当前出现字符。 8 | 然后每次比较List的第一个字符在Dict中对应的次数,如果为1则输出这个字符,如果不为1则弹出这个字符比较下一个字符。 9 | 10 | 26ms 11 | 5760k 12 | ''' 13 | 14 | # -*- coding:utf-8 -*- 15 | class Solution: 16 | # 返回对应char 17 | def __init__(self): 18 | # 存储当前字符 19 | self.alist = [] 20 | # 存储当前字符及其出现次数,出现大于1次,就设成2次 21 | self.adict = {} 22 | 23 | def FirstAppearingOnce(self): 24 | # write code here 25 | while len(self.alist) > 0 and self.adict[self.alist[0]] == 2: 26 | self.alist.pop(0) 27 | if len(self.alist) == 0: 28 | return '#' 29 | else: 30 | return self.alist[0] 31 | 32 | def Insert(self, char): 33 | # write code here 34 | if char not in self.adict.keys(): 35 | self.adict[char] = 1 36 | self.alist.append(char) 37 | else: 38 | self.adict[char] = 2 39 | -------------------------------------------------------------------------------- /编程题/密码验证合格程序.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 1.长度超过8位 3 | 2.包括大小写字母.数字.其它符号,以上四种至少三种 4 | 3.不能有相同长度超2的子串重复 5 | 说明:长度超过2的子串 6 | ''' 7 | 8 | # import re, sys 9 | # 10 | # for i in sys.stdin.readlines(): 11 | # print("OK" if len(i.strip()) > 8 and sum( 12 | # [1 if re.search(r"[A-Z]", i.strip()) else 0, 1 if re.search(r"[a-z]", i.strip()) else 0, 13 | # 1 if re.search(r"[0-9]", i.strip()) else 0, 1 if re.search(r"[^0-9a-zA-Z]", i.strip()) else 0]) > 2 and sum( 14 | # map(lambda c: i.strip().count(i.strip()[c:c + 3]) > 1, range(1, len(i.strip()) - 3))) == 0 else "NG") 15 | 16 | # 略微思考会发现,只需要判断长度为3的子串是否出现即可。因为假设子串长度为4的出现,则一定包括了长度为3的子串。同时需要注意, 17 | # 本题说的子串指包括了部分子串重叠的情况, 18 | # 例如Qw11111*ed这个是不能通过的。再就是需要注意,判断子串的时候只需要判断到len(str)-3就行了。 19 | 20 | import sys 21 | 22 | try: 23 | # 大小写,字母, 24 | def panchar(sss): 25 | standard = [0] * 4 26 | for i in sss: 27 | # print(i) 28 | # 0 29 | # 2 30 | # 1 31 | # A 32 | # b 33 | # print(len(sss)) 34 | # 数字 35 | if i.isdigit(): 36 | standard[0] = 1 37 | # print(i.isdigit()) 38 | # 小写 39 | if i.islower(): 40 | standard[1] = 1 41 | # 大写 42 | if i.isupper(): 43 | standard[2] = 1 44 | # 全都是字母,数字,空格 45 | if not i.isalpha() and not i.isdigit() and not i.isspace(): 46 | standard[3] = 1 47 | if sum(standard) >= 3: 48 | return False 49 | return True 50 | 51 | # 不能有相同长度超 2 的字串重复 52 | def zichuan(sss): 53 | for i in range(len(sss) - 3): 54 | zichuan_1 = sss[i: i + 3] 55 | zichuan_2 = sss[i + 1::] 56 | if zichuan_1 in zichuan_2: 57 | return True 58 | return False 59 | 60 | result = [] 61 | while True: 62 | line = sys.stdin.readline().strip() 63 | if line == '': 64 | break 65 | if len(line) <= 8: 66 | result.append('NG') 67 | # 大小写字母.数字.其它符号 68 | elif panchar(line): 69 | result.append('NG') 70 | elif zichuan(line): 71 | result.append('NG') 72 | else: 73 | result.append('OK') 74 | for i in result: 75 | print(i) 76 | except: 77 | pass 78 | 79 | 80 | # # 循环输入,try catch 81 | # while True: 82 | # try: 83 | # x = input().split() 84 | # 85 | # 86 | # except: 87 | # pass 88 | -------------------------------------------------------------------------------- /编程题/小易喜欢的单词.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yongxinz/backend-interview/888e5d4fa8a81bbcbb1b36b596e306989df2ffc1/编程题/小易喜欢的单词.py -------------------------------------------------------------------------------- /编程题/幸运的袋子.py: -------------------------------------------------------------------------------- 1 | # x = input() 2 | # y = map(int,input().split()) 3 | # n = 0 4 | # m = 1 5 | # count = 0 6 | # for i in y: 7 | # n += i 8 | # m *= i 9 | # print(n,m) 10 | # if m < n: 11 | # count +=1 12 | # 13 | # print(count) 14 | 15 | ''' 16 | 这个问题是求有重复数字的集合的所有子集的一个扩展 17 | 加了限制条件,这些限制条件恰好可以用来剪枝 18 | 19 | 深度优先搜索 + 剪枝 20 | ''' 21 | n = int(input()) 22 | x = sorted(list(map(int, input().split()))) # 从小到大排序 23 | 24 | # s 为和,p 为积,pos 为初始位置 25 | def dfs(x, pos, s, p): 26 | ans = 0 27 | i = pos 28 | while i < n: 29 | if s + x[i] > p * x[i]: 30 | ans += 1 + dfs(x, i + 1, s + x[i], p * x[i]) 31 | # 第一个测试案例中 1 1 1 就有问题,后面的等于前面的,会存在重复递归的问题 32 | elif x[i] == 1: 33 | ans += dfs(x, i + 1, s + 1, p) 34 | else: 35 | break 36 | while i < n - 1 and x[i] == x[i + 1]: 37 | i += 1 38 | i += 1 39 | return ans 40 | 41 | print(dfs(x, 0, 0, 1)) 42 | -------------------------------------------------------------------------------- /编程题/扑克片顺子.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张^_^)... 3 | 他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A, 4 | 黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子.....LL不高兴了,他想了想,决定大\小 王可以看成任何数字, 5 | 并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。 6 | LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何。为了方便起见, 7 | 你可以认为大小王是0。 8 | ''' 9 | 10 | ''' 11 | 思路:先统计王的数量,再把牌排序,如果后面一个数比前面一个数大于1以上,那么中间的差值就必须用王来补了。 12 | 看王的数量够不够,如果够就返回true,否则返回false。 13 | 14 | 31ms 15 | 5632k 16 | ''' 17 | 18 | # -*- coding:utf-8 -*- 19 | class Solution: 20 | def IsContinuous(self, numbers): 21 | # write code here 22 | if not numbers: 23 | return False 24 | numbers.sort() 25 | zero = numbers.count(0) 26 | 27 | # 里面有numbers[i+1]操作,所以取不到最后一个数 28 | for i, v in enumerate(numbers[:-1]): 29 | if v != 0: 30 | # numbers中不能有两个相同的数字 31 | if numbers[i + 1] == v: 32 | return False 33 | zero = zero - (numbers[i + 1] - v - 1) 34 | if zero < 0: 35 | return False 36 | return True 37 | -------------------------------------------------------------------------------- /编程题/把字符串转化成整数.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0 3 | ''' 4 | 5 | ''' 6 | 思路一: 使用int(),但是通过了 7 | 8 | 29ms 9 | 5516k 10 | ''' 11 | 12 | # -*- coding:utf-8 -*- 13 | class Solution: 14 | def StrToInt(self, s): 15 | # write code here 16 | try: 17 | return int(s) 18 | except: 19 | return 0 20 | 21 | ''' 22 | 思路二:就是一些特殊处理,比如 +123,就不合理,123前面不需要+,但是-123就合理,因为这是个负数 23 | 24 | 27ms 25 | 5624k 26 | ''' 27 | 28 | # -*- coding:utf-8 -*- 29 | class Solution: 30 | def StrToInt(self, s): 31 | # write code here 32 | if not s: 33 | return 0 34 | num = [] 35 | numbers = {'1':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':9} 36 | for i in s: 37 | if i in numbers.keys(): 38 | num.append(numbers[i]) 39 | elif i == '+': 40 | continue 41 | elif i == '-': 42 | continue 43 | else: 44 | return 0 45 | ans = 0 46 | for i in num: 47 | ans = ans * 10 + i 48 | if s[0] == '-': 49 | ans = 0 - ans 50 | return ans 51 | 52 | -------------------------------------------------------------------------------- /编程题/招商银行.py: -------------------------------------------------------------------------------- 1 | # x1 = raw_input() 2 | # x2 = raw_input() 3 | # 4 | # count = 0 5 | # for i in range(len(x1)): 6 | # for j in range(i, len(x2)): 7 | # if x1[i] == x2[j]: 8 | # continue 9 | # else: 10 | # count += 1 11 | # 12 | # if int(len(x1[0])) - count <= 1: 13 | # print 1 14 | # else: 15 | # print 0 16 | 17 | 18 | x1 = raw_input() 19 | x2 = raw_input().split() 20 | 21 | res = [] 22 | j = 0 23 | k = 0 24 | for i in range(3): 25 | if i == 0: 26 | res.append(x2[0]) 27 | continue 28 | if i % 2 != 0: 29 | x3 = list(x2) 30 | while x3: 31 | if j > len(x2): 32 | break 33 | res.append(x2[j]) 34 | j += 2 35 | x3.pop() 36 | if i % 2 == 0: 37 | x4 = list(x2) 38 | while x4: 39 | if j > len(x2): 40 | break 41 | res.append(x2[k]) 42 | k += 2 43 | x4.pop() 44 | 45 | print res 46 | print sum(int(i) for i in res) - 1 47 | 48 | -------------------------------------------------------------------------------- /编程题/数值的整数次方.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方 3 | ''' 4 | 5 | ''' 6 | 方法一: 直接这么写通过,0应该不算浮点数的,若只是负数,在python的计算中也没有问题 7 | 24ms 8 | 5632k 9 | ''' 10 | 11 | # -*- coding:utf-8 -*- 12 | class Solution: 13 | def Power(self, base, exponent): 14 | # write code here 15 | return base**exponent 16 | 17 | ''' 18 | 方法二: 19 | 需要注意的地方: 20 | 当指数为负数的时候 21 | 当底数为零切指数为负数的情况 22 | 在判断底数base是不是等于0的时候,不能直接写base==0, 因为计算机内表示小数时有误差,只能判断他们的差的绝对值是不是在一个很小的范围内 23 | 24 | 当n为偶数, a^n = a^(n/2) * a^(n/2) 25 | 当n为奇数, a^n = a^((n-1)/2) * a^((n-1)/2)) * a 26 | 利用右移一位运算代替除以2 27 | 利用位与运算代替了求余运算法%来判断一个数是奇数还是偶数 28 | 优化代码速度 29 | ''' 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /编程题/数列还原.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 思路:将 A 中缺失的部分补齐,然后求顺序对数,等于 k 则计数+1. 3 | 为此,先找到 A 缺失的是哪些数字,它们分别在什么位置。 4 | 其次,将这些缺失的数字全排列,分别按位置填充到 A 中, 5 | 计算当前组合的情况下 A 的顺序对数。 6 | 7 | ## 全排列:从 n 个不同元素中任取 m 个元素,按照一定的顺序排列起来, 8 | 叫做从 n 个不同元素中取出 m 个元素的一个排列。当 m=n 时所有的排 9 | 列情况叫做全排列。 10 | ''' 11 | 12 | from itertools import permutations 13 | 14 | # 求 A 中顺序对数 15 | def number(A): 16 | count = 0 17 | for i in range(len(A)): 18 | for j in range(i + 1, len(A)): 19 | if A[i] < A[j]: 20 | count += 1 21 | return count 22 | 23 | n, k = map(int, input().strip().split()) 24 | A = list(map(int, input().strip().split())) 25 | B = list(range(1, n + 1)) 26 | X = list(set(B).difference(set(A))) # B 和 A 的差集,即缺失的数字 27 | Y = list(permutations(X)) # 求 X 的全排列 28 | index = [] 29 | for i in range(n): 30 | if A[i] == 0: 31 | index.append(i) # A 中0所在的位置的索引 32 | m = len(index) 33 | count = 0 34 | 35 | # 往队列中嵌入全排列 36 | for x in Y: 37 | for i in range(m): 38 | A[index[i]] = x[i] 39 | num = number(A) 40 | if num == k: 41 | count += 1 42 | print(count) 43 | -------------------------------------------------------------------------------- /编程题/数字和为sum的方法数.py: -------------------------------------------------------------------------------- 1 | # k, d = map(int, input().split()) 2 | # arr = [int(i) for i in input().split()] 3 | # 4 | # class sum1(): 5 | # def __init__(self): 6 | # self.count = 0 7 | # 8 | # def subset(self, arr, i, d): 9 | # if d == 0: 10 | # self.count += 1 11 | # return self.count 12 | # elif arr[0] == d: 13 | # self.count += 1 14 | # return self.count 15 | # elif arr[i] > d: 16 | # return subset(self, arr, i - 1, d) 17 | # else: 18 | # A = subset(self, arr, i - 1, d) 19 | # B = subset(self, arr, i - 1, d - arr[i]) 20 | # return A or B 21 | # 22 | # no = sum1() 23 | # subset(no, arr, k, d) 24 | 25 | # 递归关系式 26 | # dp[i][j] = dp[i-1][j] + dp[i-1][j-A[i]] 27 | # i表示数组下标,j表示前i个元素的和,dp[i][j]表示前i个元素和为j的方案数 28 | n, m = map(int, input().split()) 29 | a = map(int, input().split()) 30 | 31 | dp = [0 for i in range(m + 1)] 32 | dp[0] = 1 33 | 34 | for i in range(n): 35 | j = m 36 | while j >= a[i]: 37 | dp[j] += dp[j - a[i]] 38 | j -= 1 39 | print(dp[m]) 40 | 41 | -------------------------------------------------------------------------------- /编程题/数字在排序数组中出现的次数.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:统计一个数字在排序数组中出现的次数。 3 | ''' 4 | 5 | ''' 6 | 思路一:count函数是顺序查找,最坏时间复杂度是O(n) 7 | 8 | 27ms 9 | 5760k 10 | ''' 11 | 12 | # -*- coding:utf-8 -*- 13 | class Solution: 14 | def GetNumberOfK(self, data, k): 15 | # write code here 16 | return data.count(k) 17 | 18 | ''' 19 | 思路二:看见有序就要想起使用二分法查找,最坏时间复杂度是O(logn) 20 | 21 | 对于一个有序数组,要考虑 1,2,2,2,3,4这种情况,计算k=2在数组中出现的次数,用二分法去找这个数,一个找最前面 22 | 出现的2的下标,一个找最后面2出现的小标,这样前后相减+1即可的到结果。 23 | 24 | 29ms 25 | 5632k 26 | ''' 27 | 28 | # -*- coding:utf-8 -*- 29 | class Solution: 30 | def GetNumberOfK(self, data, k): 31 | # write code here 32 | number = 0 33 | if data != None and len(data) > 0: 34 | length = len(data) 35 | First = self.GetFirst(data, length, k, 0, length - 1) 36 | Last = self.GetLast(data, length, k, 0, length - 1) 37 | if First > -1: 38 | number = Last - First + 1 39 | return number 40 | 41 | def GetFirst(self, data, length, k, start, end): 42 | if start > end: 43 | return -1 44 | middle = (start + end) // 2 45 | if data[middle] == k: 46 | if middle > 0 and data[middle - 1] == k: 47 | end = middle - 1 48 | else: 49 | return middle 50 | elif data[middle] > k: 51 | end = middle - 1 52 | else: 53 | start = middle + 1 54 | return self.GetFirst(data, length, k, start, end) 55 | 56 | def GetLast(self, data, length, k, start, end): 57 | if start > end: 58 | return -1 59 | middle = (start + end) // 2 60 | if data[middle] == k: 61 | if middle < end and data[middle + 1] == k: 62 | start = middle + 1 63 | else: 64 | return middle 65 | elif data[middle] > k: 66 | end = middle - 1 67 | else: 68 | start = middle + 1 69 | return self.GetLast(data, length, k, start, end) 70 | -------------------------------------------------------------------------------- /编程题/数字游戏.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 对于从小到大排序的数列arr,前 k 项之和为 sum,则1~sum都可以用前 k 3 | 项表示(取其中的某几个相加) 4 | 如果arr[k+1] <= sum+1,则1~sum + arr[k+1]可以用前 k+1 个数字表示 5 | ''' 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /编程题/数字翻转.py: -------------------------------------------------------------------------------- 1 | # x = input().split() 2 | # 3 | # res = [] 4 | # no = [] 5 | # for i in x: 6 | # no = i[::-1] 7 | # # 321 001 8 | # for j in range(len(no)): 9 | # if no[j] != '0': 10 | # no = no[j:] 11 | # break 12 | # # print(no) 13 | # res.append(no) 14 | # n = str(int(res[0]) + int(res[1])) 15 | # print(n) 16 | # n = n[::-1] 17 | # for j in range(len(n)): 18 | # if n[j] != '0': 19 | # n = n[j:] 20 | # break 21 | # print(n) 22 | 23 | # 731 288 24 | 25 | a = input().split() 26 | print(int(str(int(a[0][::-1]) + int(a[1][::-1]))[::-1])) -------------------------------------------------------------------------------- /编程题/数字颠倒.py: -------------------------------------------------------------------------------- 1 | print(input()[::-1]) 2 | -------------------------------------------------------------------------------- /编程题/数据分类处理.py: -------------------------------------------------------------------------------- 1 | while True: 2 | try: 3 | a = input().split()[1:] 4 | b = map(str, sorted(map(int, set(input().split()[1:])))) 5 | totalNum = 0 6 | res = '' 7 | for num in b: 8 | singleRes, count = '', 0 9 | for i, v in enumerate(a): 10 | if num in v: 11 | singleRes += str(i) + '' + '' 12 | totalNum += 2 13 | count += 1 14 | if count: 15 | singleRes = num + '' + str(count) + '' + singleRes 16 | totalNum += 2 17 | res += singleRes 18 | print((str(totalNum) + '' + res).rstrip()) 19 | except: 20 | break 21 | -------------------------------------------------------------------------------- /编程题/数据流中的中位数.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。 3 | 如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。 4 | ''' 5 | 6 | ''' 7 | 思路一:常规操作 8 | 9 | 29ms 10 | 5760k 11 | ''' 12 | 13 | # -*- coding:utf-8 -*- 14 | class Solution: 15 | def __init__(self): 16 | self.array = [] 17 | 18 | def Insert(self, num): 19 | # write code here 20 | self.array.append(num) 21 | self.array.sort() 22 | 23 | def GetMedian(self, M): 24 | # write code here 25 | length = len(self.array) 26 | if length % 2 == 1: 27 | return self.array[length // 2] 28 | return (self.array[length // 2] + self.array[length // 2 - 1]) / 2.0 29 | 30 | ''' 31 | 思路二: 32 | 为了保证插入新数据和取中位数的时间效率都高效,这里使用大顶堆+小顶堆的容器,并且满足: 33 | 1、两个堆中的数据数目差不能超过1,这样可以使中位数只会出现在两个堆的交接处; 34 | 2、大顶堆的所有数据都小于小顶堆,这样就满足了排序要求。 35 | 36 | 构建一个最大堆和一个最小堆,分别存储比中位数小的数和大的数。 37 | 当目前两堆总数为偶数的时候,把数字存入最大堆,然后重排最大堆,如果最大堆的堆顶数字大于最小堆堆顶数字, 38 | 则把两个堆顶数字交换,重排两堆,此时两堆数字总数为奇数,直接输出最大堆堆顶数字即为中位数; 39 | 如果当前两堆总数为奇数的时候,把数字存入最小堆,重排最小堆,如果最大堆的堆顶数字大于最小堆堆顶数字, 40 | 则把两个堆顶数字交换,重排两堆,此时两堆数字总数为偶数,取两堆堆顶数字做平均即为中位数 41 | 42 | 最大堆堆顶元素要小于最小堆堆顶的元素,最大堆,堆顶元素最大,从大到小,最小堆堆顶元素最小,从小到大,这样的话, 43 | 最大堆所有元素均小于最小堆了,中位数一定出现在两堆交替之间。 44 | 45 | 27ms 46 | 5632k 47 | ''' 48 | 49 | # -*- coding:utf-8 -*- 50 | class Solution: 51 | def __init__(self): 52 | self.left = [] 53 | self.right = [] 54 | self.count = 0 55 | 56 | def Insert(self, num): 57 | if self.count & 1 == 0: 58 | self.left.append(num) 59 | else: 60 | self.right.append(num) 61 | self.count += 1 62 | 63 | def GetMedian(self, x): 64 | if self.count == 1: 65 | return self.left[0] 66 | self.MaxHeap(self.left) 67 | self.MinHeap(self.right) 68 | if self.left[0] > self.right[0]: 69 | self.left[0], self.right[0] = self.right[0], self.left[0] 70 | self.MaxHeap(self.left) 71 | self.MinHeap(self.right) 72 | if self.count & 1 == 0: 73 | return (self.left[0] + self.right[0]) / 2.0 74 | else: 75 | return self.left[0] 76 | 77 | def MaxHeap(self, alist): 78 | length = len(alist) 79 | if alist == None or length <= 0: 80 | return 81 | if length == 1: 82 | return alist 83 | for i in range(length // 2 - 1, -1, -1): 84 | k = i; 85 | temp = alist[k]; 86 | heap = False 87 | while not heap and 2 * k < length - 1: 88 | index = 2 * k + 1 89 | if index < length - 1: 90 | if alist[index] < alist[index + 1]: index += 1 91 | if temp >= alist[index]: 92 | heap = True 93 | else: 94 | alist[k] = alist[index] 95 | k = index 96 | alist[k] = temp 97 | 98 | def MinHeap(self, alist): 99 | length = len(alist) 100 | if alist == None or length <= 0: 101 | return 102 | if length == 1: 103 | return alist 104 | for i in range(length // 2 - 1, -1, -1): 105 | k = i; 106 | temp = alist[k]; 107 | heap = False 108 | while not heap and 2 * k < length - 1: 109 | index = 2 * k + 1 110 | if index < length - 1: 111 | if alist[index] > alist[index + 1]: index += 1 112 | if temp <= alist[index]: 113 | heap = True 114 | else: 115 | alist[k] = alist[index] 116 | k = index 117 | alist[k] = temp 118 | 119 | -------------------------------------------------------------------------------- /编程题/数的子结构.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构) 3 | ''' 4 | 5 | ''' 6 | 分析,两个序列才能确定一个一棵树,所以先用先序遍历,再用字符串进行匹配是不对的,因为的树的结构你确定不了。 7 | 这一题,首先判断根节点是不是相同,不相同是一个递归,把pRoot1的左右子树一次和PRoot2进行判断 8 | 如果根节点相同,那么进入下一个函数,接着判断,左边节点的下一级和左边子树下一级是不是相同,又是一个递归。 9 | 两个递归操作 10 | 29ms 11 | 5632k 12 | ''' 13 | 14 | # -*- coding:utf-8 -*- 15 | # class TreeNode: 16 | # def __init__(self, x): 17 | # self.val = x 18 | # self.left = None 19 | # self.right = None 20 | class Solution: 21 | def HasSubtree(self, pRoot1, pRoot2): 22 | # write code here 23 | result = False 24 | # 判断根节点 25 | if pRoot1 != None and pRoot2 != None: 26 | if pRoot1.val == pRoot2.val: 27 | result = self.same(pRoot1, pRoot2) 28 | if not result: 29 | result = self.HasSubtree(pRoot1.left, pRoot2) 30 | if not result: 31 | result = self.HasSubtree(pRoot1.right, pRoot2) 32 | return result 33 | 34 | def same(self, pRoot1, pRoot2): 35 | if pRoot2 == None: 36 | return True 37 | if pRoot1 == None: 38 | return False 39 | # 这一步不断判断下一个节点,因为是递归操作。 40 | if pRoot1.val != pRoot2.val: 41 | return False 42 | return self.same(pRoot1.left, pRoot2.left) and self.same(pRoot1.right, pRoot2.right) 43 | -------------------------------------------------------------------------------- /编程题/数组中出现次数超过一半的数字.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。 3 | 由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。 4 | ''' 5 | 6 | ''' 7 | 思路1:快排,如果该数出现次数超过数组长度的一半,那么,排序之后,他应该位于数组的中间 8 | 9 | 思路2:根据数组的特点,出现次数超过一半的数,他出现的次数比其他数字出现的总和还要多,因此可以最开始保存两个数值: 10 | 数组中的一个数字以及它出现的次数,然后遍历,如果下一个数字等于这个数字,那么次数加一,如果不等,次数减一,当次数 11 | 等于0的时候,在下一个数字的时候重新复制新的数字以及出现的次数置为1,直到进行到最后,然后再验证最后留下的数字是否 12 | 出现次数超过一半,因为可能前面的次数依次抵消掉,最后一个数字就直接是保留下来的数字,但是出现次数不一定超过一半。 13 | 14 | 24ms 15 | 5632k 16 | ''' 17 | 18 | # -*- coding:utf-8 -*- 19 | class Solution: 20 | def MoreThanHalfNum_Solution(self, numbers): 21 | # write code here 22 | # 这个操作就是建立在出现次数超过一半的数,他出现的次数比其他数字出现的总和还要多,所以如果存在这个数,最后count 23 | # 肯定不为0的 24 | count = 1 25 | number = numbers[0] 26 | for i in numbers[1:]: 27 | if number == i: 28 | count += 1 29 | else: 30 | count -= 1 31 | if count == 0: 32 | number = i 33 | count += 1 34 | 35 | sum = 0 36 | for j in numbers: 37 | if j == number: 38 | sum += 1 39 | 40 | return number if sum > len(numbers) // 2 else 0 41 | 42 | -------------------------------------------------------------------------------- /编程题/数组中只出现一次的数字.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。 3 | ''' 4 | 5 | ''' 6 | 思路一:利用python自带的counter库 7 | 8 | 27ms 9 | 5632k 10 | ''' 11 | 12 | # -*- coding:utf-8 -*- 13 | class Solution: 14 | # 返回[a,b] 其中ab是出现一次的两个数字 15 | def FindNumsAppearOnce(self, array): 16 | # write code here 17 | from collections import Counter 18 | # 返回一个列表,map(f,input),对input进行f操作,第一个参数lambda函数,意思取返回值中的第一个数,因为counter函数返回的是字典, 19 | # counter().most_common返回的是有序的计数字段,去最后两个,顺序是从大到小的。 20 | return list(map(lambda c:c[0],Counter(array).most_common()[-2:])) 21 | 22 | ''' 23 | 思路二:异或运算 24 | ''' 25 | 26 | -------------------------------------------------------------------------------- /编程题/数组中的逆序对.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组 3 | 中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007 4 | ''' 5 | 6 | ''' 7 | notes:这一题没法用python做,用python始终是超时的 8 | 9 | 思路一:很巧妙 10 | 在牛客网上运算超时,但是思想很好 11 | 例如: 12 | 0 1 2 3 13 | 2 1 3 4 逆序数:1 14 | copy 1 2 3 4 15 | 1+0+0+0=1 16 | ''' 17 | 18 | # -*- coding:utf-8 -*- 19 | class Solution: 20 | def InversePairs(self, data): 21 | # write code here 22 | copy = [] 23 | count = 0 24 | for i in data: 25 | copy.append(i) 26 | copy.sort() 27 | 28 | for i in range(len(copy)): 29 | count += data.index(copy[i]) 30 | data.remove(copy[i]) 31 | 32 | return count % 10000000007 33 | 34 | ''' 35 | 思路二:这一题核心还是要用归并排序,归并排序能够有效的减少最坏时间复杂度,但是它有额外的开销,以空间换时间 36 | 归并排序,就是把原数据分成两个数组,每次取两个数组中的最小值放入一个新的数组中,直到其中一个数组全部取完 37 | 38 | 不过还是超时 39 | ''' 40 | 41 | # -*- coding:utf-8 -*- 42 | class Solution: 43 | def InversePairs(self, data): 44 | # write code here 45 | length = len(data) 46 | if data == None or length <= 0: 47 | return 0 48 | copy = [0] * length 49 | for i in range(length): 50 | copy[i] = data[i] 51 | 52 | count = self.InversePairsCore(data, copy, 0, length - 1) 53 | return count % 10000000007 54 | 55 | def InversePairsCore(self, data, copy, start, end): 56 | if start == end: 57 | copy[start] = data[start] 58 | return 0 59 | length = (end - start) // 2 60 | left = self.InversePairsCore(copy, data, start, start + length) 61 | right = self.InversePairsCore(copy, data, start + length + 1, end) 62 | 63 | # i初始化为前半段最后一个数字的下标 64 | i = start + length 65 | # j初始化为后半段最后一个数字的下标 66 | j = end 67 | 68 | indexCopy = end 69 | count = 0 70 | # 对两个数组进行对比取值的过程 71 | while i >= start and j >= start + length + 1: 72 | if data[i] > data[j]: 73 | copy[indexCopy] = data[i] 74 | indexCopy -= 1 75 | i -= 1 76 | count += j - start - length 77 | else: 78 | copy[indexCopy] = data[j] 79 | indexCopy -= 1 80 | j -= 1 81 | 82 | # 剩下的一个数组未取完的操作 83 | while i >= start: 84 | copy[indexCopy] = data[i] 85 | indexCopy -= 1 86 | i -= 1 87 | while j >= start + length + 1: 88 | copy[indexCopy] = data[j] 89 | indexCopy -= 1 90 | j -= 1 91 | return left + right + count 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /编程题/数组中重复的数字.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。 3 | 也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3}, 4 | 那么对应的输出是第一个重复的数字2。 5 | ''' 6 | 7 | ''' 8 | 应该是在线编辑器的bug 9 | 10 | 思路一: 11 | 12 | 未通过 13 | ''' 14 | 15 | # -*- coding:utf-8 -*- 16 | class Solution: 17 | # 这里要特别注意~找到任意重复的一个值并赋值到duplication[0] 18 | # 函数返回True/False 19 | def duplicate(self, numbers, duplication): 20 | # write code here 21 | if not numbers: 22 | return -1 23 | num = [] 24 | 25 | for i in numbers: 26 | if i in num: 27 | duplication[0] = i 28 | return True 29 | else: 30 | num.append(i) 31 | return False 32 | 33 | ''' 34 | 思路二: 35 | 最简单的方法:我最直接的想法就是构造一个容量为N的辅助数组B,原数组A中每个数对应B中下标,首次命中,B中对应元素+1。如果某次命中时,B中对应的不为0,说明,前边已经有一样数字了,那它就是重复的了。 36 | 举例:A{1,2,3,3,4,5},刚开始B是{0,0,0,0,0,0},开始扫描A。 37 | A[0] = 1  {0,1,0,0,0,0} 38 | A[1] = 2 {0,1,1,0,0,0} 39 | A[2] = 3 {0,1,1,1,0,0} 40 | A[3] = 3 {0,1,1,2,0,0},到这一步,就已经找到了重复数字。 41 | A[4] = 4 {0,1,1,2,1,0} 42 | A[5] = 5 {0,1,1,2,1,1} 43 | 时间复杂度O(n),空间复杂度O(n),算法优点是简单快速,比用set更轻量更快,不打乱原数组顺序。 44 | 45 | 未能通过 46 | ''' 47 | 48 | # -*- coding:utf-8 -*- 49 | class Solution: 50 | # 这里要特别注意~找到任意重复的一个值并赋值到duplication[0] 51 | # 函数返回True/False 52 | def duplicate(self, numbers, duplication): 53 | # write code here 54 | if not numbers: 55 | return False 56 | 57 | length = len(numbers) 58 | assist = [0] * length 59 | for i in numbers: 60 | if assist[numbers[i]] == 0: 61 | assist[numbers[i]] += 1 62 | else: 63 | duplication[0] = numbers[i] 64 | return True 65 | return False 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /编程题/整数与IP地址间的转换.py: -------------------------------------------------------------------------------- 1 | while True: 2 | try: 3 | print(sum([256 ** j * int(i) for j, i in enumerate(input().split('.')[::-1])])) 4 | b = int(input()) 5 | print('.'.join([str(b // (256 ** i) % 256) for i in range(3, -1, -1)])) 6 | except: 7 | break 8 | -------------------------------------------------------------------------------- /编程题/整数中1出现的次数.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、 3 | 10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出 4 | 任意非负整数区间中1出现的次数。 5 | ''' 6 | 7 | ''' 8 | 思路1:例:对于824883294,先求0-800000000之间(不包括800000000)的,再求0-24883294之间的。 9 | 如果等于1,如1244444,先求0-1000000之间,再求1000000-1244444,那么只需要加上244444+1,再求0-244444 10 | 之间的1如果大于1,例:0-800000000之间1的个数为8个100000000的1的个数加上100000000,因为从1000000000- 11 | 200000000共有1000000000个数且最高位都为1。对于最后一位数,如果大于1,直接加上1即可。 12 | 13 | 思路2:将1-n全部转换为字符串,只需要统计每个字符串中'1'出现的次数并相加即可 14 | 47ms 15 | 5504k 16 | ''' 17 | 18 | def countDigitOne(self, n): 19 | result = 0 20 | if n < 0: 21 | return 0 22 | length = len(str(n)) 23 | listN = list(str(n)) 24 | for i, v in enumerate(listN): 25 | a = length - i - 1 # a为10的幂 26 | if i == length - 1 and int(v) >= 1: 27 | result += 1 28 | break 29 | if int(v) > 1: 30 | result += int(10 ** a * a / 10) * int(v) + 10 ** a 31 | if int(v) == 1: 32 | result += (int(10 ** a * a / 10) + int("".join(listN[i + 1:])) + 1) 33 | return result 34 | 35 | # -*- coding:utf-8 -*- 36 | class Solution: 37 | def NumberOf1Between1AndN_Solution(self, n): 38 | # write code here 39 | count = 0 40 | for i in range(1,n+1): 41 | for i in str(i): 42 | if i == '1': 43 | count += 1 44 | return count 45 | -------------------------------------------------------------------------------- /编程题/斐波那契数列.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目: 3 | 大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项。 4 | n<=39 5 | ''' 6 | 7 | ''' 8 | 第三位是前两位之和,一直迭代的 9 | 25ms 10 | 5632k 11 | ''' 12 | 13 | # -*- coding:utf-8 -*- 14 | class Solution: 15 | def Fibonacci(self, n): 16 | # write code here 17 | res = [0,1] 18 | while len(res) <= n: 19 | res.append(res[-1]+res[-2]) 20 | return res[n] -------------------------------------------------------------------------------- /编程题/旋转数组的最小数字.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目: 3 | 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的 4 | 最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。NOTE:给出的所有元素都大于0,若数组大小为0, 5 | 请返回0。 6 | ''' 7 | 8 | ''' 9 | 这一题也不好解,在算法上,考虑数字没有重复的情况的话,使用二分法,有两个指针,第一个指针指向front,第二个指针指向rear, 10 | midIndex是中间数字,只要是旋转数组,那么首位一定大于中间位,所以如果首位大于中间位的话,那么就把指针从首位移到中间 11 | 位,前面数字向后移动,不断迭代,当首位和最后位只差1时,最后维就是最小值。此时最坏时间复杂度是O(logn),但是要考虑数字 12 | 重复的话,情况只可能是首位和末尾和中间重这种[1,0,1,1]只能取其中最小值,逐一排列,对于首位和中间位重的,比如[1,1,0], 13 | 把首位移动到后面去,可以处理,或者中间位和末尾重,比如[1,0,0],也是能处理,其他情况不存在,因为前提要求是旋转数组 14 | 830ms 15 | 5632k 16 | ''' 17 | 18 | # -*- coding:utf-8 -*- 19 | class Solution: 20 | def minNumberInRotateArray(self, rotateArray): 21 | # write code here 22 | if not rotateArray: 23 | return 0 24 | 25 | front, rear = 0, len(rotateArray) - 1 26 | midIndex = 0 27 | while rotateArray[front] >= rotateArray[rear]: 28 | if rear - front == 1: 29 | midIndex = rear 30 | break 31 | midIndex = (front + rear) // 2 32 | if rotateArray[front] == rotateArray[midIndex] and rotateArray[front] == rotateArray[rear]: 33 | return self.minOrder(rotateArray, front, rear) 34 | 35 | if rotateArray[front] <= rotateArray[midIndex]: 36 | front = midIndex 37 | elif rotateArray[rear] >= rotateArray[midIndex]: 38 | rear = midIndex 39 | return rotateArray[midIndex] 40 | 41 | def minOrder(self, array, front, end): 42 | result = array[0] 43 | for i in array[front:end + 1]: 44 | if i < result: 45 | result = i 46 | return result -------------------------------------------------------------------------------- /编程题/明明的随机数.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:明明想在学校中请一些同学一起做一项问卷调查,为了实验的客观性,他先用计算机生成了N个1到1000之间的 3 | 随机整数(N≤1000),对于其中重复的数字,只保留一个,把其余相同的数去掉,不同的数对应着不同的学生的学号。 4 | 然后再把这些数从小到大排序,按照排好的顺序去找同学做调查。请你协助明明完成“去重”与“排序”的工作。 5 | Input Param 6 | n 输入随机数的个数 7 | inputArray n个随机整数组成的数组 8 | Return Value 9 | OutputArray 输出处理后的随机整数 10 | 11 | 注:测试用例保证输入参数的正确性,答题者无需验证。测试用例不止一组。 12 | ''' 13 | 14 | ''' 15 | 思路:set可以去重,sort排序 16 | 17 | 42ms 18 | 3428k 19 | ''' 20 | 21 | while True: 22 | try: 23 | n = int(input()) 24 | l = [] 25 | for _ in range(n): 26 | l.append(int(input())) 27 | l = sorted(set(l)) 28 | for i in l: 29 | print(i) 30 | except: 31 | break 32 | -------------------------------------------------------------------------------- /编程题/星际穿越.py: -------------------------------------------------------------------------------- 1 | import math 2 | x = int(input()) 3 | 4 | n =( math.sqrt(1+4*x)-1)/2 5 | print(int(n)) -------------------------------------------------------------------------------- /编程题/替换空格.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:请实现一个函数,将一个字符串中的空格替换成“%20”。 3 | 例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。 4 | ''' 5 | 6 | ''' 7 | 方法1: 使用replace 8 | 24ms 9 | 5624k 10 | ''' 11 | class Solution: 12 | # s 源字符串 13 | def replaceSpace(self, s): 14 | # write code here 15 | return s.replace(' ','%20') 16 | 17 | 18 | ''' 19 | 方法二:这一题的关键是替换是从往后往前遍历还是从前往后遍历是不一样的,如果从前往后遍历的话,每一个空格都要移动这个空格后面 20 | 所有的字符串一次,但是如果从后往前遍历的话,每一个字符串只需要移动一次。 21 | ''' 22 | -------------------------------------------------------------------------------- /编程题/最大奇约数.py: -------------------------------------------------------------------------------- 1 | # x = int(input()) 2 | # 3 | # def sum(i): 4 | # if i % 2 != 0: 5 | # return i 6 | # elif i % 2 == 0: 7 | # return sum(i / 2) 8 | # 9 | # n = 0 10 | # for i in range(1, x + 1): 11 | # n += sum(i) 12 | # print(int(n)) 13 | 14 | x = int(input()) 15 | 16 | def getsum(n): 17 | if n == 1: 18 | return 1 19 | return n*n/4 + getsum(n/2) if n%2==0 else n + getsum(n-1) 20 | 21 | print(str(getsum(x))) 22 | -------------------------------------------------------------------------------- /编程题/最小的k个数.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。 3 | ''' 4 | 5 | ''' 6 | 思路1,这一题应用堆排序算法复杂度只有O(nlog k),堆是完全二叉树的一种,最大堆就是最上面的数是最大的 7 | 该方法基于二叉树或者堆来实现,首先把数组前k个数字构建一个最大堆,然后从第k+1个数字开始遍历数组,如果遍历到的 8 | 元素小于堆顶的数字,那么久将换两个数字,重新构造堆,继续遍历,最后剩下的堆就是最小的k个数,时间复杂度O(nlog k)。 9 | 10 | 思路2:排序 11 | ''' 12 | 13 | # -*- coding:utf-8 -*- 14 | class Solution: 15 | def GetLeastNumbers_Solution(self, tinput, k): 16 | # write code here、 17 | import heapq 18 | if tinput == None or len(tinput) < k or len(tinput) <= 0 or k <= 0: 19 | return [] 20 | 21 | # 建立最小堆,最上面那个数是最小的,返回一个列表,这个列表就是从最小值开始的k个数 22 | return heapq.nsmallest(k, tinput) 23 | 24 | 25 | # -*- coding:utf-8 -*- 26 | class Solution: 27 | def GetLeastNumbers_Solution(self, tinput, k): 28 | # write code here、 29 | import heapq 30 | if tinput == None or len(tinput) < k or len(tinput) <= 0 or k <= 0: 31 | return [] 32 | 33 | return sorted(tinput)[:k] 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /编程题/末尾0的个数.py: -------------------------------------------------------------------------------- 1 | x = int(input()) 2 | 3 | n = 1 4 | for i in range(1, x + 1): 5 | n *= i 6 | n = str(n)[::-1] 7 | count = 0 8 | for i in n: 9 | if i != '0': 10 | break 11 | count += 1 12 | print(count) 13 | -------------------------------------------------------------------------------- /编程题/机器人的运动范围.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格, 3 | 但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为 4 | 3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子? 5 | ''' 6 | 7 | ''' 8 | 思路:同上一题思路一样,判断条件改成了行坐标和列坐标的数位之和大于k 9 | 10 | 27ms 11 | 5728k 12 | ''' 13 | 14 | # -*- coding:utf-8 -*- 15 | class Solution: 16 | def movingCount(self, threshold, rows, cols): 17 | # write code here 18 | markmatrix = [False] * (rows * cols) 19 | count = self.GetNum(threshold, rows, cols, 0, 0, markmatrix) 20 | return count 21 | 22 | def GetNum(self, threshold, rows, cols, row, col, markmatrix): 23 | count = 0 24 | 25 | if self.GetSum(threshold, rows, cols, row, col, markmatrix): 26 | markmatrix[row * cols + col] = True 27 | count = 1 + self.GetNum(threshold, rows, cols, row - 1, col, markmatrix) + \ 28 | self.GetNum(threshold, rows, cols, row, col - 1, markmatrix) + \ 29 | self.GetNum(threshold, rows, cols, row + 1, col, markmatrix) + \ 30 | self.GetNum(threshold, rows, cols, row, col + 1, markmatrix) 31 | return count 32 | 33 | def GetSum(self, threshold, rows, cols, row, col, markmatrix): 34 | if row >= 0 and row < rows and col >= 0 and col < cols and self.getDigit(row) + self.getDigit( 35 | col) <= threshold and not markmatrix[row * cols + col]: 36 | return True 37 | return False 38 | 39 | def getDigit(self, number): 40 | sumNum = 0 41 | while number > 0: 42 | sumNum += number % 10 43 | number = number // 10 44 | return sumNum 45 | -------------------------------------------------------------------------------- /编程题/构建乘积数组.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:给定一个数组A[0,1,...,n-1],请构建一个数组B[0,1,...,n-1],其中B中的元素 3 | B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。不能使用除法 4 | ''' 5 | 6 | ''' 7 | 思路: 8 | B[0] = A[1] * A[2] * A[3] * A[4] *....*A[n-1] ;(没有A[0]) 9 | B[1 ]= A[0] * A[2] * A[3] * A[4] *....*A[n-1] ;(没有A[1]) 10 | B[2] = A[0] * A[1] * A[3] * A[4] *....*A[n-1] ;(没有A[2]) 11 | 举例: 输入: 1 2 3 4 5 12 | 输出: 120 60 40 30 24 13 | 相当于一个矩形,被省去的那个数字设为1,这样的话,先把上三角的数一行一行撑起来,接着在和下三角的数相乘,节省空间 14 | 15 | 27ms 16 | 5632k 17 | ''' 18 | 19 | # -*- coding:utf-8 -*- 20 | class Solution: 21 | def multiply(self, A): 22 | # write code here 23 | if not A or len(A) < 0: 24 | return 0 25 | length = len(A) 26 | B = [1] * length 27 | # 下三角,从1开始乘的 28 | for i in range(1, length): 29 | B[i] = B[i - 1] * A[i - 1] 30 | temp = 1 31 | # 上三角,接着下三角从大往小乘,节约空间,最后一位设为1,前面只有一位,在于之前计算好的相乘 32 | # 画一个矩形就明白了。 33 | for i in range(length - 2, -1, -1): 34 | temp = temp * A[i + 1] 35 | B[i] *= temp 36 | return B 37 | -------------------------------------------------------------------------------- /编程题/查找兄弟单词.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 字典顺序: 3 | 两个单词(字母按照自左向右顺序),先以第一个字母作为排序的基准,如果第一个字母相同, 4 | 就用第二个字母为基准,如果第二个字母相同就以第三个字母为基准。依次类推,如果 5 | 到某个字母不相同,字母顺序在前的那个单词顺序在前。如果短单词是长单词从首字母开始 6 | 连续的一部分,短单词顺序在前。 7 | ''' 8 | 9 | # while True: 10 | # try: 11 | # x = input().split() 12 | # from collections import defaultdict 13 | # 14 | # dd = defaultdict(list) 15 | # res = [] 16 | # 17 | # for i in range(1, int(x[0]) + 1): 18 | # # print(x[i]) 19 | # dd[''.join(sorted(x[i]))].append(x[i]) 20 | # # print(dd) 21 | # for key, value in dd.items(): 22 | # if key == ''.join(sorted(x[-2])): 23 | # res.append(value) 24 | # # print(len(value) - 1) 25 | # # print(value[int(x[-1])]) 26 | # 27 | # for i in res: 28 | # for j in i: 29 | # if ''.join(j) == x[-2]: 30 | # res[0].remove(j) 31 | # 32 | # # print(res) 33 | # if res == []: 34 | # print(0) 35 | # else: 36 | # print(len(res[0])) 37 | # print(res[0][int(x[-1]) - 1]) 38 | # 39 | # except: 40 | # break 41 | 42 | 43 | from collections import defaultdict 44 | 45 | while True: 46 | try: 47 | dd = defaultdict(list) 48 | a = input().split() 49 | words, lookup, num, brothers = a[1:1 + int(a[0])], a[-2], int(a[-1]), [] 50 | for i in words: 51 | dd[''.join(sorted(i))].append(i) 52 | for i in dd[''.join(sorted(lookup))]: 53 | if i != lookup: 54 | brothers.append(i) 55 | 56 | print(len(brothers)) 57 | if brothers and num <= len(brothers): 58 | print(sorted(brothers)[num - 1]) 59 | except: 60 | break 61 | -------------------------------------------------------------------------------- /编程题/栈的压入弹出序列.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字 3 | 均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就 4 | 不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的) 5 | ''' 6 | 7 | ''' 8 | 这题挺难的,比较抽象。可以手写模拟一下。 9 | 三个栈,一个是压栈,一个是出栈,一个是辅助栈,把数据从pushv向stack中压,如果压入数据和popv出栈的栈顶元素一致, 10 | 就从pushv和popv中同时弹出去,不要往stack中压了。等到pushv中元素全弹出来之后,判断stack中出栈元素和popv中出栈 11 | 元素是否一致,当popv中元素全部弹出,就结束,说明是一致的,手写演示一下就懂了。 12 | 13 | 入栈1,2,3,4,5 14 | 出栈4,5,3,2,1 15 | 首先1入辅助栈,此时栈顶1≠4,继续入栈2 16 | 此时栈顶2≠4,继续入栈3 17 | 此时栈顶3≠4,继续入栈4 18 | 此时栈顶4=4,出栈4,弹出序列向后一位,此时为5,,辅助栈里面是1,2,3 19 | 此时栈顶3≠5,继续入栈5 20 | 此时栈顶5=5,出栈5,弹出序列向后一位,此时为3,,辅助栈里面是1,2,3 21 | …. 22 | 依次执行,最后辅助栈为空。如果不为空说明弹出序列不是该栈的弹出顺序。 23 | 24 | 34ms 25 | 6312k 26 | ''' 27 | 28 | # -*- coding:utf-8 -*- 29 | class Solution: 30 | def IsPopOrder(self, pushV, popV): 31 | # write code here 32 | stack = [] 33 | while popV: 34 | 35 | # 如果第一个元素都相同,就直接弹出,压入栈为空还是要比的,一开始为空,是个问题,但是压空了就要比弹出了,第二个elif 36 | if pushV and pushV[0] == popV[0]: 37 | popV.pop(0) 38 | pushV.pop(0) 39 | # 如果stack中最后一个元素和popV中第一个元素相同,这就是压完了之后弹出的过程中进行的比较 40 | elif stack and stack[-1] == popV[0]: 41 | stack.pop() 42 | popV.pop(0) 43 | elif pushV: 44 | stack.append(pushV.pop(0)) 45 | else: 46 | return False 47 | return True 48 | -------------------------------------------------------------------------------- /编程题/正则表达式匹配.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:请实现一个函数用来匹配包括'.'和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的 3 | 字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a" 4 | 和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配 5 | ''' 6 | 7 | ''' 8 | 思路: . 表示可以是任意字符,* 表示前面的字符可以出现任意次,所以在patten中*不可能位于首位的 9 | aaa a.a ab*ac*a(b出现0次,c出现0次,这样就是aaa了) 10 | 11 | 26ms 12 | 5628k 13 | ''' 14 | 15 | # -*- coding:utf-8 -*- 16 | class Solution: 17 | # s, pattern都是字符串 18 | def match(self, s, pattern): 19 | # write code here 20 | # 如果s和pattern匹配, 直接True 21 | if s == pattern: 22 | return True 23 | # 如果pattern为'', 因为s和pattern不相等, 直接False 24 | elif pattern == '': 25 | return False 26 | # 当s为'', 如果pattern为'.', 则返回True 27 | # 当s为'', 如果pattern长度为1且不为'.', 或者pattern第二个字符不是*, 则pattern不可能为空, 返回False 28 | # 若pattern长度不为1, 且第二个字符为*, pattern还有空的可能, 从第三个字符开始迭代 29 | elif s == '': 30 | if pattern == ".": 31 | return False 32 | elif len(pattern) == 1 or pattern[1] != '*': 33 | return False 34 | else: 35 | return self.match(s, pattern[2:]) 36 | 37 | # 如果pattern长度不小于二, 而且pattern的第二个字符不是*的情况下 38 | # 当 pattern[0] 不等于s[0], 且不为 . 的时候, s和pattern必不相等 39 | # 否则, s 和 pattern 都右移一位, 继续比较 40 | if len(pattern) >= 2 and pattern[1] != '*': 41 | if s[0] != pattern[0] and pattern[0] != '.': 42 | return False 43 | else: 44 | return self.match(s[1:], pattern[1:]) 45 | # 如果pattern长度不小于2, 且pattern第二个字符为*的情况下 46 | # 如果s[0]不等于pattern[0], 且pattern[0]不为 . , 那么第一位比较不成功, pattern必须后移两位继续比较后面是否能和s第一位匹配 47 | # 如果s[0]等于pattern[0], 或者pattern[0]为 . , 第一位匹配, 那么会有 48 | # 1. aaa 和 a*a 这种情况, 星号代表了多个a, 因此s需要不断右移一位继续比较 49 | # 2. a 和 a*a 中这情况, 这时候星号代表0个a, 因此s不需要右移, pattern需要右移两位 50 | # 3. abc 和 a*bc 这种情况, 星号代表了1个a, s右移一位, pattern右移两位继续比较 51 | elif len(pattern) >= 2 and pattern[1] == '*': 52 | if s[0] != pattern[0] and pattern[0] != '.': 53 | return self.match(s, pattern[2:]) 54 | else: 55 | return self.match(s[1:], pattern) or self.match(s, pattern[2:]) or self.match(s[1:], pattern[2:]) 56 | # 除去上述pattern不小于2情况, 只剩下pattern等于1的情况, 因此如果pattern为".", 而且s长度为1, 返回True 57 | elif pattern == '.' and len(s) == 1: 58 | return True 59 | return False -------------------------------------------------------------------------------- /编程题/求1+2+3+...+n.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。 3 | ''' 4 | 5 | ''' 6 | 思路一:利用了python的特性吧,好想法。 7 | 8 | 29ms 9 | 5632k 10 | ''' 11 | 12 | # -*- coding:utf-8 -*- 13 | class Solution: 14 | def Sum_Solution(self, n): 15 | # write code here 16 | return sum(list(range(1, n + 1))) 17 | 18 | ''' 19 | 思路二:利用两个函数,一个函数充当递归函数的角色,另一个函数处理终止递归的情况。如果对n连续进行两次反运算, 20 | 那么非零的n转换为True,0转换为False。利用这一特性终止递归。注意考虑测试用例为0的情况。 21 | 22 | 34ms 23 | 6140k 24 | ''' 25 | 26 | # -*- coding:utf-8 -*- 27 | class Solution: 28 | def Sum_Solution(self, n): 29 | # write code here 30 | return self.sum(n) 31 | 32 | def sum0(self, n): 33 | return 0 34 | 35 | def sum(self, n): 36 | fun = {False: self.sum0, True: self.sum} 37 | return n + fun[not not n](n - 1) -------------------------------------------------------------------------------- /编程题/求int型正整数在内存中存储时1的个数.py: -------------------------------------------------------------------------------- 1 | x = input().split() 2 | 3 | x = bin(int(x[0])) 4 | print(x.count('1')) 5 | -------------------------------------------------------------------------------- /编程题/求和-好未来.py: -------------------------------------------------------------------------------- 1 | # n, m = map(int, input().split()) 2 | # 3 | # for i in range(n): 4 | # for j in range(i, n + 1): 5 | # if i + j == m: 6 | # print(i, j) 7 | 8 | # dfs 9 | n, m = list(map(int, input().split())) 10 | li = [] 11 | 12 | def f(n, m, l, k): 13 | if m == 0: 14 | print(' '.join(l)) 15 | for i in range(k, n + 1): 16 | if i > m: 17 | break 18 | l.append(str(i)) 19 | f(n, m - i, l, i + 1) 20 | l.pop() 21 | 22 | f(n, m, li, 1) 23 | -------------------------------------------------------------------------------- /编程题/求和.py: -------------------------------------------------------------------------------- 1 | # arr = {3,34,4,12,5,2} 2 | # S = 9 3 | 4 | import numpy as np 5 | 6 | # def dp_subset(arr, s): 7 | # subset = np.zeros((len(arr), s + 1), dtype=bool) 8 | # subset[:, 0] = True 9 | # subset[0, :] = False 10 | # subset[0, arr[0]] = True 11 | # for i in range(1, len(arr)): 12 | # for s in range(1, s + 1): 13 | # if arr[i] > s: 14 | # subset[i, s] = subset[i - 1, s] 15 | # else: 16 | # A = subset[i - 1, s - arr[i]] 17 | # B = subset[i - 1, s] 18 | # subset[i, s] = A or B 19 | # r, c = subset.shape 20 | # return subset[r - 1, c - 1] 21 | 22 | def rec_subset(arr, i, s): 23 | if s == 0: 24 | return True 25 | elif i == 0: 26 | return arr[0] == s 27 | elif arr[i] > s: 28 | return rec_subset(arr, i - 1, s) 29 | else: 30 | A = rec_subset(arr, i - 1, s - arr[i]) 31 | B = rec_subset(arr, i - 1, s) 32 | return A or B 33 | -------------------------------------------------------------------------------- /编程题/求小球落地5次后所经历的路程和第5次反弹的高度.py: -------------------------------------------------------------------------------- 1 | while True: 2 | try: 3 | x = input() 4 | number = int(x) 5 | print(2.875 * number) 6 | print(0.03125 * number) 7 | except: 8 | break 9 | -------------------------------------------------------------------------------- /编程题/汽水瓶.py: -------------------------------------------------------------------------------- 1 | # 每两个汽水瓶换一个,所以除以 2 就可以了 2 | 3 | while True: 4 | try: 5 | a = int(input()) 6 | if a != 0: 7 | print(a // 2) 8 | except: 9 | break 10 | -------------------------------------------------------------------------------- /编程题/滑动窗口的最大值.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及 3 | 滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的 4 | 滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, 5 | {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。 6 | ''' 7 | 8 | ''' 9 | 思路一: 10 | 2 3 4 4 第一轮判断 11 | 3 4 2 4 12 | 4 2 6 6 第一轮结束时进入第二个判断条件 13 | 2 6 2 6 14 | 6 2 5 6 15 | 2 5 1 5 16 | 17 | 23ms 18 | 5728k 19 | ''' 20 | 21 | # -*- coding:utf-8 -*- 22 | class Solution: 23 | def maxInWindows(self, num, size): 24 | # write code here 25 | if size <= 0 or len(num) < size: 26 | return [] 27 | length = len(num) 28 | curnum = max(num[:size]) 29 | ans = [curnum] 30 | # 循环次数是length-size,是因为滑窗大小是size,length只能有length-size个滑窗 31 | for i in range(0, length - size): 32 | # 对滑窗内每个数字都进行扫描,直到找到和最大值相等数字,相等表明我的一个滑窗扫描结束,进入下一个滑窗进行扫描,并取最大值 33 | if num[i] == curnum: 34 | curnum = max(num[i + 1:i + 1 + size]) 35 | # 跳过一个滑窗字符为首的一个滑窗,进入一个新的判断滑窗 36 | elif num[i + size] > curnum: 37 | curnum = num[i + size] 38 | ans.append(curnum) 39 | return ans 40 | 41 | ''' 42 | 思路二:用双向队列做,没看懂 43 | 44 | 45 | ''' 46 | 47 | -------------------------------------------------------------------------------- /编程题/百度.py: -------------------------------------------------------------------------------- 1 | x = input().split() 2 | length = len(x) 3 | 4 | res = [] 5 | y = [] 6 | for i in range(length): 7 | y += str(x[i]) 8 | length1 = len(y) 9 | 10 | y = y + y 11 | # print(y) 12 | 13 | for i in range(length1): 14 | # print(y[i:(i + length1)]) 15 | if y[i:i + length1] not in res: 16 | res.append(y[i:i + length1]) 17 | else: 18 | continue 19 | 20 | print(res) 21 | print(len(res)) 22 | -------------------------------------------------------------------------------- /编程题/矩形覆盖.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目: 3 | 我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法? 4 | ''' 5 | 6 | ''' 7 | 依旧是斐波那契数列 8 | 2*n的大矩形,和n个2*1的小矩形 9 | 其中 2*target 为大矩阵的大小 10 | 有以下几种情形: 11 | 1⃣️target <= 0 大矩形为<= 2*0,直接return 1; 12 | 2⃣️target = 1大矩形为2*1,只有一种摆放方法,return1; 13 | 3⃣️target = 2 大矩形为2*2,有两种摆放方法,return2; 14 | 4⃣️target = n 分为两步考虑: 15 | 第一次摆放一块 2*1 的小矩阵,则摆放方法总共为f(target - 1) 16 | √ 17 | √ 18 | 第一次摆放一块1*2的小矩阵,则摆放方法总共为f(target-2) 19 | 因为,摆放了一块1*2的小矩阵(用√√表示),对应下方的1*2(用××表示)摆放方法就确定了,所以为f(targte-2) 20 | √ √ 21 | × × 22 | target >= 3 f(n) = f(target - 1) + f(targte-2) 23 | 24 | 29ms 25 | 5632k 26 | ''' 27 | 28 | # -*- coding:utf-8 -*- 29 | class Solution: 30 | def rectCover(self, number): 31 | # write code here 32 | if number == 0: 33 | return 0 34 | elif number == 1: 35 | return 1 36 | elif number == 2: 37 | return 2 38 | else: 39 | res = [0, 1, 2] 40 | while len(res) <= number: 41 | res.append(res[-1] + res[-2]) 42 | return res[number] 43 | -------------------------------------------------------------------------------- /编程题/矩阵中的路径.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个 3 | 格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路 4 | 径不能再进入该格子。 例如 a b c e s f c s a d e e 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含 5 | "abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。 6 | ''' 7 | 8 | ''' 9 | 思路:优化版回溯法 10 | 1.将matrix字符串模拟映射为一个字符矩阵(但并不实际创建一个矩阵) 11 | 2.取一个boolean[matrix.length]标记某个字符是否已经被访问过,用一个布尔矩阵进行是否存在该数值的标记。 12 | 3.如果没找到结果,需要将对应的boolean标记值置回false,返回上一层进行其他分路的查找。 13 | 14 | 36ms 15 | 5697k 16 | ''' 17 | 18 | # -*- coding:utf-8 -*- 19 | class Solution: 20 | def hasPath(self, matrix, rows, cols, path): 21 | # write code here 22 | if not matrix and rows <= 0 and cols <= 0 and path == None: 23 | return False 24 | # 模拟的字符矩阵 25 | markmatrix = [0] * (rows * cols) 26 | pathlength = 0 27 | # 从第一个开始递归,当然第一二个字符可能并不位于字符串之上,所以有这样一个双层循环找起点用的,一旦找到第一个符合的字符串,就开始进入递归, 28 | # 返回的第一个return Ture就直接跳出循环了。 29 | for row in range(rows): 30 | for col in range(cols): 31 | if self.hasPathCore(matrix, rows, cols, row, col, path, pathlength, markmatrix): 32 | return True 33 | return False 34 | 35 | def hasPathCore(self, matrix, rows, cols, row, col, path, pathlength, markmatrix): 36 | # 说明已经找到该路径,可以返回True 37 | if len(path) == pathlength: 38 | return True 39 | 40 | hasPath = False 41 | if row >= 0 and row < rows and col >= 0 and col < cols and matrix[row * cols + col] == path[pathlength] and not \ 42 | markmatrix[row * cols + col]: 43 | pathlength += 1 44 | markmatrix[row * cols + col] = True 45 | # 进行该值上下左右的递归 46 | hasPath = self.hasPathCore(matrix, rows, cols, row - 1, col, path, pathlength, markmatrix) \ 47 | or self.hasPathCore(matrix, rows, cols, row, col - 1, path, pathlength, markmatrix) \ 48 | or self.hasPathCore(matrix, rows, cols, row + 1, col, path, pathlength, markmatrix) \ 49 | or self.hasPathCore(matrix, rows, cols, row, col + 1, path, pathlength, markmatrix) 50 | 51 | # 对标记矩阵进行布尔值标记 52 | if not hasPath: 53 | pathlength -= 1 54 | markmatrix[row * cols + col] = False 55 | return hasPath 56 | -------------------------------------------------------------------------------- /编程题/称砝码.py: -------------------------------------------------------------------------------- 1 | def fama(n, weight, nums): 2 | res = set() 3 | for i in range(nums[0] + 1): 4 | res.add(i * weight[0]) 5 | for i in range(1, n): 6 | tmp = list(res) 7 | for j in range(1, nums[i] + 1): 8 | for wt in tmp: 9 | res.add(wt + j * weight[i]) 10 | return len(res) 11 | 12 | while True: 13 | try: 14 | n = int(input()) 15 | weight = [int(i) for i in input().split()] 16 | nums = [int(i) for i in input().split()] 17 | print(fama(n, weight, nums)) 18 | except: 19 | break 20 | 21 | -------------------------------------------------------------------------------- /编程题/第一个只出现一次的字符.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:在一个字符串(1<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置 3 | ''' 4 | 5 | ''' 6 | 思路一:先遍历一遍字符串,用一个hash表存放每个出现的字符和字符出现的次数。再遍历一遍字符串,找到hash值等于1的输出即可。 7 | 40ms 8 | 5632k 9 | 10 | 思路二:利用python函数的特性 11 | 32ms 12 | 5632k 13 | ''' 14 | 15 | # -*- coding:utf-8 -*- 16 | class Solution: 17 | def FirstNotRepeatingChar(self, s): 18 | # write code here 19 | if s == None or len(s) <= 0: 20 | return -1 21 | alist = list(s) 22 | blist = {} 23 | for i in alist: 24 | if i not in blist.keys(): 25 | blist[i] = 0 26 | blist[i] += 1 27 | for i in blist: 28 | if blist[i] == 1: 29 | # %d format: a number is required, not str ,i此时返回的是只出现一次的那个字符串是不符合题意的 30 | # return i 31 | return s.index(i) 32 | return -1 33 | 34 | # -*- coding:utf-8 -*- 35 | class Solution: 36 | def FirstNotRepeatingChar(self, s): 37 | # write code here 38 | if s == None or len(s) <= 0: 39 | return -1 40 | for i in s: 41 | if s.count(i) == 1: 42 | return s.index(i) 43 | return -1 44 | -------------------------------------------------------------------------------- /编程题/简单密码.py: -------------------------------------------------------------------------------- 1 | x = input().split() 2 | 3 | def mima(s): 4 | if s in 'Z': 5 | s = 'a' 6 | elif s.isupper(): 7 | s = chr(ord(s.lower()) + 1) 8 | elif s in 'abc': 9 | s = '2' 10 | elif s in 'def': 11 | s = '3' 12 | elif s in 'ghi': 13 | s = '4' 14 | elif s in 'jkl': 15 | s = '5' 16 | elif s in 'mno': 17 | s = '6' 18 | elif s in 'pqrs': 19 | s = '7' 20 | elif s in 'tuv': 21 | s = '8' 22 | elif s in 'wxyz': 23 | s = '9' 24 | elif s in '0': 25 | s = '0' 26 | return s 27 | 28 | res = [] 29 | for i in range(len(x[0])): 30 | res.append(mima(x[0][i])) 31 | 32 | print(''.join(res)) 33 | -------------------------------------------------------------------------------- /编程题/简单错误记录.py: -------------------------------------------------------------------------------- 1 | # while True: 2 | # x = input().split() 3 | # y = x[0].index('\\') 4 | # print(y) 5 | # count = x[y[-1]:] 6 | # if count >= 16: 7 | # count = count[-16:] 8 | # print(count) 9 | 10 | error = dict() 11 | filelist = [] 12 | while True: 13 | try: 14 | record = ''.join(''.join(input().split('\\')[-1].split())) 15 | filename = record.split() 16 | if len(filename[0]) >= 16: 17 | filename[0] = filename[0][-16:] 18 | record = ''.join(filename) 19 | if record not in error.keys(): 20 | error[record] = 1 21 | filelist.append(record) 22 | else: 23 | error[record] += 1 24 | except: 25 | break 26 | key = filelist[-8:] 27 | for each in key: 28 | print(''.join(each.split()), error[each]) 29 | -------------------------------------------------------------------------------- /编程题/素数伴侣.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yongxinz/backend-interview/888e5d4fa8a81bbcbb1b36b596e306989df2ffc1/编程题/素数伴侣.py -------------------------------------------------------------------------------- /编程题/素数对.py: -------------------------------------------------------------------------------- 1 | n = [int(i) for i in input().split()] 2 | 3 | arr, res = [], 0 4 | for i in range(2, n[0] + 1): 5 | flag = True 6 | for j in range(2, int(i ** 0.5 + 1)): 7 | if i % j == 0: 8 | flag = False 9 | break 10 | if flag: 11 | arr.append(i) 12 | # print(arr) 13 | 14 | for i in range(len(arr)): 15 | if arr[i] > n[0] / 2: 16 | break 17 | else: 18 | if n[0] - arr[i] in arr: 19 | res += 1 20 | print(res) 21 | -------------------------------------------------------------------------------- /编程题/统计回文.py: -------------------------------------------------------------------------------- 1 | n1 = input() 2 | n2 = input() 3 | n = [] 4 | 5 | def huiwen(s): 6 | return s == s[::-1] 7 | 8 | count = 0 9 | for i in range(len(n1) + 1): 10 | n = n1[:i] + n2 + n1[i:] 11 | if huiwen(n): 12 | count += 1 13 | print(count) 14 | -------------------------------------------------------------------------------- /编程题/统计每个月兔子的总数.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 有一只兔子,从出生后第3个月起每个月都生一只兔子,小兔子长到第三个月后每个月又生一只兔子,假如兔子都不死,问每个月的兔子总数为多少? 3 | 4 | ''' 5 | 6 | while True: 7 | try: 8 | x = [int(i) for i in input().split()] 9 | 10 | res = [1, 2] 11 | 12 | for i in range(x[0] - 3): 13 | res.append(res[-1] + res[-2]) 14 | 15 | # print(res) 16 | print(res[-1]) 17 | except: 18 | break 19 | -------------------------------------------------------------------------------- /编程题/美团.py: -------------------------------------------------------------------------------- 1 | # x = int(input()) 2 | # res = [] 3 | # for i in range(x - 1): 4 | # res.append(input().split()) 5 | # # print(res) 6 | # 7 | # for i in range(1, len(res)): 8 | # if res[i - 1][1] == res[i][0]: 9 | # x -= 1 10 | # print(x + 1) 11 | 12 | 13 | k, d = map(int, input().split()) 14 | l = input().split() 15 | 16 | res = [] 17 | for i in range(len(l)): 18 | if l[i] == '0' and l[i + 1] == '1': 19 | res.append(l[i]) 20 | res.append(l[i + 1]) 21 | 22 | # print(res) 23 | if res[:-1] == '1': 24 | for i in range(d): 25 | res[:(-2 - 2 * i)] = '1' 26 | 27 | print(res.count(1)) 28 | -------------------------------------------------------------------------------- /编程题/翻转单词顺序列.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容 3 | 颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把 4 | 句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么? 5 | ''' 6 | 7 | ''' 8 | 思路一: 9 | 10 | 26ms 11 | 5632k 12 | ''' 13 | 14 | # -*- coding:utf-8 -*- 15 | class Solution: 16 | def ReverseSentence(self, s): 17 | # write code here 18 | if s == None or len(s) <= 0: 19 | return '' 20 | # 以空格为界,提取单一字符,然后用切片拍一下顺序 21 | return ' '.join(s.split(' ')[::-1]) 22 | 23 | ''' 24 | 思路二:首先需要写一个reverse函数,把任何输入的字符串完全翻转。然后从前往后依次遍历新字符串,如果遇到空格, 25 | 就把空格前的字符串用reverse翻转,添加空格,继续遍历。需要注意的是,如果新字符串结尾不是空格,当遍历到结尾的 26 | 时候,前一个空格到结尾的字符串没有翻转,因此记得跳出遍历后,需要再完成一次翻转操作。 27 | 28 | 举例: 29 | student. a am I 30 | I ma a .tneduts 31 | I pEnd = ' ',首先让空格前面的字符进行翻转,然后 pStart = pEnd 32 | am pStart = ' ',两个指针都向前进到a位,下一次pEnd继续向前进到空格为止,然后ma翻转变成am 33 | a 34 | student. 同理 35 | 36 | 37 | 37ms 38 | 5488k 39 | ''' 40 | 41 | # -*- coding:utf-8 -*- 42 | class Solution: 43 | def ReverseSentence(self, s): 44 | # write code here 45 | if s == None or len(s) <= 0: 46 | return '' 47 | s = list(s) 48 | s = self.Reverse(s) 49 | pStart = 0 50 | pEnd = 0 51 | listTemp = [] 52 | result = '' 53 | 54 | while pEnd < len(s): 55 | if pEnd == len(s) - 1: 56 | listTemp.append(self.Reverse(s[pStart:])) 57 | break 58 | if s[pStart] == ' ': 59 | pStart += 1 60 | pEnd += 1 61 | listTemp.append(' ') 62 | elif s[pEnd] == ' ': 63 | listTemp.append(self.Reverse(s[pStart:pEnd])) 64 | pStart = pEnd 65 | else: 66 | pEnd += 1 67 | for i in listTemp: 68 | result += ''.join(i) 69 | return result 70 | 71 | def Reverse(self, s): 72 | start = 0 73 | end = len(s) - 1 74 | while start < end: 75 | s[start], s[end] = s[end], s[start] 76 | start += 1 77 | end -= 1 78 | return s -------------------------------------------------------------------------------- /编程题/藏宝图.py: -------------------------------------------------------------------------------- 1 | # 体会一下,真的很好, 贪心 2 | x = input() 3 | y = input() 4 | 5 | for i in x: 6 | if y and i==y[0]: 7 | y =y[1:] 8 | print('No' if y else 'Yes') 9 | -------------------------------------------------------------------------------- /编程题/蛇形矩阵.py: -------------------------------------------------------------------------------- 1 | # while True: 2 | # try: 3 | # n = input() 4 | # start = 0 5 | # for i in range(n): 6 | # start += i 7 | # count = start 8 | # for j in range(i + 2, n + 2): 9 | # print(count + 1) 10 | # count += j 11 | # 12 | # except: 13 | # break 14 | 15 | def minusOne(num): 16 | return num - 1 17 | 18 | while True: 19 | try: 20 | lineNum = int(input()) 21 | temp = [] 22 | for i in range(2, lineNum + 2): 23 | temp.append(sum(range(i))) 24 | for j in range(lineNum): 25 | print(' '.join(map(str, temp))) 26 | temp = list(map(minusOne, temp[1:])) 27 | except Exception: 28 | break 29 | -------------------------------------------------------------------------------- /编程题/表示数值的字符串.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。 3 | 例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。 但是"12e","1a3.14","1.2.3","+-5" 4 | 和"12e+4.3"都不是。 5 | ''' 6 | 7 | ''' 8 | 思路一:利用float强转 9 | 10 | 7ms 11 | 5632k 12 | ''' 13 | 14 | # -*- coding:utf-8 -*- 15 | class Solution: 16 | # s字符串 17 | def isNumeric(self, s): 18 | # write code here 19 | try: 20 | return float(s) 21 | except: 22 | return 0 23 | 24 | ''' 25 | 思路二:正则表达式 26 | 27 | 28ms 28 | 5632k 29 | ''' 30 | 31 | # -*- coding:utf-8 -*- 32 | class Solution: 33 | # s字符串 34 | def isNumeric(self, s): 35 | # write code here 36 | if not s: 37 | return 0 38 | import re 39 | # 正则表达式,*匹配前一个字符出现无限次或0次,?匹配前一个字符出现一次或0次,+匹配前一个字符出现1次 40 | # 或无限次,.表示小数点已经转义了 41 | return re.match(r'^[\+\-]?[0-9]*(\.[0-9]*)?([eE][\+\-]?[0-9]+)?$', s) 42 | 43 | ''' 44 | 思路三:判断s中所有字符串,以e为界,e后面不能出现.或空,否则直接返回False,然后把e前后两部分全部放到 45 | 一个判断函数里面,考虑所有出现的字符串,+-不能出现在首位,字符串里面.出现次数不能超过1 46 | 47 | 28ms 48 | 5632k 49 | ''' 50 | 51 | # -*- coding:utf-8 -*- 52 | class Solution: 53 | # s字符串 54 | def isNumeric(self, s): 55 | # write code here 56 | if not s or len(s) <= 0: 57 | return 0 58 | alist = [i.lower() for i in s] 59 | if 'e' in alist: 60 | index = alist.index('e') 61 | front = alist[:index] 62 | behind = alist[index + 1:] 63 | if '.' in behind or len(behind) == 0: 64 | return False 65 | isfront = self.Digit(front) 66 | isbehind = self.Digit(behind) 67 | return isfront and isbehind 68 | else: 69 | isNum = self.Digit(alist) 70 | return isNum 71 | 72 | def Digit(self, alist): 73 | dotNum = 0 74 | allowNum = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '-', '.', 'e'] 75 | for i in range(len(alist)): 76 | if alist[i] not in allowNum: 77 | return False 78 | if alist[i] == '.': 79 | dotNum += 1 80 | if alist[i] in '+-' and i != 0: 81 | return False 82 | if dotNum > 1: 83 | return False 84 | return True 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /编程题/解救小易.py: -------------------------------------------------------------------------------- 1 | x = input().split() 2 | n1 = [int(i) for i in input().split()] 3 | n2 = [int(i) for i in input().split()] 4 | 5 | res = [] 6 | for i in range(len(n1)): 7 | res.append(n1[i] + n2[i] - 2) 8 | 9 | print(min(res)) 10 | -------------------------------------------------------------------------------- /编程题/计算字符个数.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:写出一个程序,接受一个有字母和数字以及空格组成的字符串,和一个字符, 3 | 然后输出输入字符串中含有该字符的个数。不区分大小写。 4 | ''' 5 | 6 | ''' 7 | 思路: 8 | 9 | 36ms 10 | 3552k 11 | ''' 12 | 13 | a = input().lower() 14 | b = input().lower() 15 | print(a.count(b)) 16 | -------------------------------------------------------------------------------- /编程题/计算糖果.py: -------------------------------------------------------------------------------- 1 | n = int(input()) 2 | x = [int(i) for i in input().split()] 3 | 4 | sum = 0 5 | max = x[0] 6 | for i in range(n): 7 | if sum >= 0: 8 | sum += x[i] 9 | else: 10 | sum = x[i] 11 | if sum > max: 12 | max = sum 13 | print(max) 14 | -------------------------------------------------------------------------------- /编程题/识别有效的IP地址和掩码并进行分类统计.py: -------------------------------------------------------------------------------- 1 | ''' 2 | A B C D E IP地址错误会错误掩码 私有IP 3 | 4 | 判断子网掩码是否有效 5 | 1.通过判断是否为 255.255.255.255 ,如果是,错误加1, 6 | 2.判断子网掩码是否正确。根据子网掩码二进制规律(开头为连续的1,然后为0),我们 7 | 将子网掩码按位取反,然后加1,得到的新二进制维,然而我们通过判断二进制中1的个数来 8 | 判断是否为合法的子网掩码。 9 | 10 | 判断ABCDE地址 11 | ''' 12 | 13 | -------------------------------------------------------------------------------- /编程题/调整数组顺序使奇数位于偶数前面.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分, 3 | 所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。 4 | ''' 5 | 6 | ''' 7 | 方法一: 两个数组解 8 | 25ms 9 | 5504k 10 | ''' 11 | 12 | # -*- coding:utf-8 -*- 13 | class Solution: 14 | def reOrderArray(self, array): 15 | # write code here 16 | 17 | res1 = [] 18 | res2 = [] 19 | for i in array: 20 | if i % 2 == 0: 21 | res1.append(i) 22 | else: 23 | res2.append(i) 24 | return res2 + res1 25 | 26 | ''' 27 | 方法二:类似冒泡的思想,相互交换 28 | ''' 29 | -------------------------------------------------------------------------------- /编程题/质数因子.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:功能:输入一个正整数,按照从小到大的顺序输出它的所有质数的因子(如180的质数因子为2 2 3 3 5 ) 3 | 最后一个数后面也要有空格 4 | 5 | 详细描述: 6 | 函数接口说明: 7 | public String getResult(long ulDataInput) 8 | 输入参数: 9 | long ulDataInput:输入的正整数 10 | 返回值: 11 | String 12 | 13 | ''' 14 | 15 | ''' 16 | 把输入的数进行因式分解,只不过分解的数必须是质数 17 | 思路:对于一个数来说,比如180,从2开始遍历,如果能被2整除,那么180/=2,并且输出2,之后再拿90重复上述操作 18 | 直到变成1为止 19 | 20 | 45ms 21 | 3560k 22 | ''' 23 | 24 | n, res = int(input()), [] 25 | for i in range(2, n // 2 + 1): 26 | while n % i == 0: 27 | res.append(i) 28 | n = n / i 29 | print(" ".join(map(str, res)) + " " if res else str(n) + " ") 30 | -------------------------------------------------------------------------------- /编程题/购物单.py: -------------------------------------------------------------------------------- 1 | N_m = input().split() 2 | N = int(N_m[0]) 3 | m = int(N_m[1]) 4 | 5 | v = [] 6 | p = [] 7 | q = [] 8 | 9 | for i in range(m): 10 | vpq = input().split() 11 | v.append(int(vpq[0])) 12 | p.append(int(vpq[1])) 13 | q.append(int(vpq[2])) 14 | 15 | def max_fun(N, m, v, p, q): 16 | res = [[0] * (N - 1) for _ in range(m + 1)] 17 | # 商品 18 | for i in range(1, m + 1): 19 | # 价格 20 | for j in range(10, N + 1, 10): 21 | # 为主件时 22 | if q[i - 1] == 0: 23 | if v[i - 1] <= j: 24 | res[i][j] = max(res[i - 1][j], res[i - 1][j - v[i - 1]] + v[i - 1] * p[i - 1]) 25 | # 为配件时 26 | elif v[i - 1] + v[q[i - 1]] <= j: 27 | res[i][j] = max(res[i - 1][j], res[i - 1][j - v[i - 1] - v[q[i - 1]]] + v[i - 1] * p[i - 1] + v[ 28 | q[i - 1] * p[q[i - 1]]]) 29 | print(res[m][int(N / 10) * 10]) 30 | 31 | max_fun(N, m, v, p, q) 32 | 33 | res[m] -------------------------------------------------------------------------------- /编程题/输入n个整数,输出出现次数大于等于数组长度一半的数。.py: -------------------------------------------------------------------------------- 1 | x = [int(i) for i in input().split()] 2 | for i in x: 3 | k = x.count(i) 4 | if k * 2 >= len(x): 5 | print(i) 6 | break 7 | -------------------------------------------------------------------------------- /编程题/输入一行字符,分别统计出包含英文字母、空格、数字和其它字符的个数.py: -------------------------------------------------------------------------------- 1 | while True: 2 | try: 3 | x = input() 4 | 5 | count1 = 0 6 | count2 = 0 7 | count3 = 0 8 | count4 = 0 9 | for i in x: 10 | if i.isalpha(): 11 | count1 += 1 12 | elif i.isspace(): 13 | count2 += 1 14 | elif i.isnumeric(): 15 | count3 += 1 16 | else: 17 | count4 += 1 18 | print(count1) 19 | print(count2) 20 | print(count3) 21 | print(count4) 22 | except: 23 | break 24 | -------------------------------------------------------------------------------- /编程题/进制转换.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:写出一个程序,接受一个十六进制的数值字符串,输出该数值的十进制字符串。(多组同时输入 ) 3 | ''' 4 | 5 | ''' 6 | 思路一: 用 int(x,[base]) 将一个数字或 base 类型的字符串转换成整数。 缺省 base ,按十进制算 7 | 8 | 36ms 9 | 3448k 10 | ''' 11 | 12 | while True: 13 | try: 14 | print(int(input(), 16)) 15 | except: 16 | break 17 | 18 | ''' 19 | 思路二: 用 eval(source[, globals[, locals]]) ,将字符串转掉,变成一个数字,以十进制打印出来 20 | 21 | 35ms 22 | 3564k 23 | ''' 24 | 25 | while True: 26 | try: 27 | n = input() 28 | n = eval(n) 29 | print(n) 30 | except: 31 | break 32 | -------------------------------------------------------------------------------- /编程题/连续子数组的最大和.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常 3 | 需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望 4 | 旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。你会不会被他 5 | 忽悠住?(子向量的长度至少是1) 6 | ''' 7 | 8 | ''' 9 | 对于连续子数组,可以用一个数值来存储当前和,如果当前和小于零,那么在进行到下一个元素的时候,直接把当前和赋值为下 10 | 一个元素,如果当前和大于零,则累加下一个元素,同时用一个maxNum存储最大值并随时更新。也可以利用动态规划解决。 11 | 12 | 29ms 13 | 5624k 14 | ''' 15 | 16 | # -*- coding:utf-8 -*- 17 | class Solution: 18 | def FindGreatestSumOfSubArray(self, array): 19 | # write code here 20 | if array == None or len(array) <= 0: 21 | return 0 22 | 23 | sum = 0 24 | result = array[0] 25 | for i in range(len(array)): 26 | if sum <= 0: 27 | sum = array[i] 28 | else: 29 | sum += array[i] 30 | 31 | if sum > result: 32 | result = sum 33 | 34 | return result 35 | 36 | 37 | -------------------------------------------------------------------------------- /编程题/青蛙跳台阶.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。 3 | ''' 4 | 5 | ''' 6 | 对于本题,前提只有 一次 1阶或者2阶的跳法。 7 | a.如果两种跳法,1阶或者2阶,那么假定第一次跳的是一阶,那么剩下的是n-1个台阶,跳法是f(n-1); 8 | b.假定第一次跳的是2阶,那么剩下的是n-2个台阶,跳法是f(n-2) 9 | c.由a\b假设可以得出总跳法为: f(n) = f(n-1) + f(n-2)  10 | d.然后通过实际的情况可以得出:只有一阶的时候 f(1) = 1 ,只有两阶的时候可以有 f(2) = 2 11 | e.可以发现最终得出的是一个斐波那契数列: 12 |          13 |        | 1, (n=1) 14 | f(n) = | 2, (n=2) 15 |        | f(n-1)+f(n-2) ,(n>2,n为整数) 16 | 17 | 28ms 18 | 5632k 19 | ''' 20 | 21 | # -*- coding:utf-8 -*- 22 | class Solution: 23 | def jumpFloor(self, number): 24 | # write code here 25 | res = [1,2] 26 | while len(res) <= number: 27 | res.append(res[-1]+res[-2]) 28 | if number == 1: 29 | return 1 30 | else: 31 | return res[number-1] -------------------------------------------------------------------------------- /编程题/顺时针打印矩阵.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 题目:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如, 3 | 如果输入如下矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 4 | 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10. 5 | ''' 6 | 7 | ''' 8 | 思路超神: 9 | 可以模拟魔方逆时针旋转的方法,一直做取出第一行的操作 10 | 例如 11 | 1 2 3 12 | 4 5 6 13 | 7 8 9 14 | 输出并删除第一行后,再进行一次逆时针旋转,就变成: 15 | 6 9 16 | 5 8 17 | 4 7 18 | 继续重复上述操作即可 19 | 20 | 24ms 21 | 5632k 22 | ''' 23 | 24 | # -*- coding:utf-8 -*- 25 | class Solution: 26 | # matrix类型为二维列表,需要返回列表 27 | def printMatrix(self, matrix): 28 | # write code here 29 | result = [] 30 | while (matrix): 31 | result += matrix.pop(0) 32 | if not matrix: 33 | break 34 | matrix = self.turn(matrix) 35 | return result 36 | 37 | def turn(self, matrix): 38 | newmat = [] 39 | row = len(matrix) 40 | col = len(matrix[0]) 41 | for i in range(col): 42 | newmat1 = [] 43 | for j in range(row): 44 | newmat1.append(matrix[j][i]) 45 | newmat.append(newmat1) 46 | newmat.reverse() 47 | return newmat 48 | 49 | -------------------------------------------------------------------------------- /编程题/餐厅.py: -------------------------------------------------------------------------------- 1 | # 在 m 批客人中选择 n 批上 n 个桌子 2 | n, m = [int(x) for x in input().split()] 3 | # 每个桌子可容纳的最大人数 4 | table = [int(x) for x in input().split()] 5 | # 分别表示第 i 批客人的人数和预计消费金额 6 | cus = [[int(x) for x in input().split()] for i in range(m)] 7 | 8 | # 将桌子按可容纳人数从小到大排列 9 | table.sort() 10 | # 按每批顾客的人数从小到大排序 11 | cus = sorted(cus, key=lambda x: x[0]) 12 | # 总金额 13 | money = 0 14 | 15 | for i in range(n): 16 | # 盛放第 i 个可接受的顾客批 j 17 | temp = [] 18 | # 注意 range 中要用 cus 的 length 时更新 19 | for j in range(len(cus)): 20 | if cus[j][0] > table[i]: 21 | break 22 | temp.append(cus[j]) 23 | # 按消费金额排序 24 | temp = sorted(temp, key=lambda x: x[1]) 25 | if temp: 26 | money += temp[-1][1] 27 | # 此批客人已就坐 28 | cus.remove() 29 | print(money) 30 | -------------------------------------------------------------------------------- /编程题/饥饿的小易.py: -------------------------------------------------------------------------------- 1 | ''' 2 | f(x) = 4x+3, g(x) = 8x+7 3 | 计算得到以下两个规律: 4 | g(f(x)) = f(g(x)) 即 f 和 g 的执行顺序没有影响 5 | f(f(f(x))) = g(g(x)) 即做3次 f 变换等价于做2次 g 变换 6 | 由于规律(1)对于一个可行方案,我们可以调整其变换的顺序。如ffggfggff,我们可以转化成fffffgggg 7 | 由于规律(2),并且为了使执行次数最少,每3个f我们可以转化成2个g,如fffffgggg,可以转化成ffgggggg 8 | 因此一个最优的策略,f的执行次数只能为0,1,2.对于输入的x,我们只需要求x,4*x+3, 9 | (4*x+3)+3的最小执行次数就可以了。 10 | ''' 11 | 12 | 13 | 14 | # x = int(input()) 15 | # y1 = 4 * x + 3 16 | # y2 = 8 * x + 7 17 | # 18 | # if y1 % 1000000007 == 0: 19 | # print(y1 // 1000000007) 20 | # if y2 % 1000000007 == 0: 21 | # print(y2 // 1000000007) 22 | -------------------------------------------------------------------------------- /项目与架构/README.md: -------------------------------------------------------------------------------- 1 | 1、项目细节讲解,流程图,瓶颈在哪儿 2 | 3 | 项目整体架构 4 | 共包含哪些服务 5 | 服务之间信息流是如何流转的 6 | 在项目中,有没有遇到什么难点 7 | 有没有排查过项目的线上问题 8 | 你觉得你现在的设计有什么问题么? 9 | 如果你负责的服务从100TPS变成1万TPS会有什么问题?怎么处理? 10 | 如果你负责的这个功能之后需要频繁变更,你怎么设计? 11 | 等等等 12 | 13 | 2、QPS,如何压测,性能测试,性能指标,性能优化。 14 | 15 | 3、秒杀场景设计 16 | 17 | 4、高并发 18 | 19 | 5、分布式锁实现方案。 20 | 21 | 6、1G 内存,1T 文件,想找到出现次数第二大的字符串。 22 | 23 | 7、项目架构图,自我感觉项目难点,现在再让你重新做这个项目你会有什么修改跟调整 24 | 25 | 8、常用的负载均衡算法,自己选择个负载均衡算法来实现并进行自测。 26 | 27 | 9、常用限流方法,自己咋实现。 28 | 29 | 10、分布式链路追踪的实现跟理解。 30 | --------------------------------------------------------------------------------