├── test64_求1+2+…+n.py ├── test65_不用加减乘除做加法.py ├── test59_队列的最大值.py ├── test21_调整数组顺序使奇数位于偶数之前.py ├── test43_1_n整数中1出现的次数.py ├── test66_构建乘积数组.py ├── test31_栈的压入_弹出序列.py ├── test42_连续子数组的最大和.py ├── test67_把字符串转换成整数.py ├── test10_菲波那切数列.py ├── test9_用两个栈实现队列.py ├── README.md ├── test63_股票的最大利润.py ├── test62_圆圈中最后剩下的数字.py ├── test61_扑克牌中的顺子.py ├── test15_二进制中1的个数.py ├── test33_二叉搜索树的后序遍历序列.py ├── test24_反转链表.py ├── test4_二维数组中的查找.py ├── test30_包含min函数的栈.py ├── test47_礼物的最大价值.py ├── test16_数值的整数次方.py ├── test11_旋转数组的最小数字.py ├── test27_二叉树的镜像.py ├── test50_第一个只出现一次的字符.py ├── test45_把数组拍成最小的数字.py ├── test34_二叉树中和为某一值的路径.py ├── test48_最长不含重复字符的子字符串.py ├── test29_顺时针打印矩阵.py ├── test44_数字序列中的某一位数字.py ├── test46_把数字翻译成字符串.py ├── test28_对称的二叉树.py ├── test7_重建二叉树.py ├── test37_序列化二叉树.py ├── test36_二叉搜索树与双向链表.py ├── test12_矩阵中的路径.py ├── test54_二叉搜索树的第k大节点.py ├── test25_合并两个排序的链表.py ├── test14_剪绳子.py ├── test57_和为s的数字.py ├── test40_最小的k个数.py ├── test6_从尾到头打印链表.py ├── test49_丑数.py ├── test5_替换空格.py ├── test26_树的子结构.py ├── test22_链表中倒数第k个节点.py ├── test23_链表中环的入口节点.py ├── test17_打印从1到最大的n位数.py ├── test13_机器人的运动范围.py ├── test8_二叉树的下一个节点.py ├── test58_翻转字符串.py ├── test55_二叉树的深度.py ├── test38_字符串的排列.py ├── test3_数组中重复的数字.py ├── test56_数组中数字出现的次数.py ├── test60_n个骰子的点数.py ├── test35_复杂链表的复制.py ├── test68_树中两个节点的最低公共祖先.py ├── test53_在排序数组中查找数字.py ├── test32_从上到下打印二叉树.py ├── test18_删除链表的节点.py ├── test51_数组中的逆序对.py ├── test52_两个链表的第一个公共节点.py └── test39_数组中出现次数超过一半的数字.py /test64_求1+2+…+n.py: -------------------------------------------------------------------------------- 1 | # 面试题64 求1+2+…+n 2 | ''' 3 | 这里运用了闭包的概念,涉及到python的装饰器 4 | 但我不太懂这个 5 | ''' 6 | 7 | class Solution: 8 | def __init__(self): 9 | self.sum = 0 10 | 11 | def Sum_Solution(self, n): 12 | # write code here 13 | def qiusum(n): 14 | self.sum += n 15 | n -= 1 16 | return n > 0 and self.Sum_Solution(n) 17 | 18 | qiusum(n) 19 | return self.sum 20 | 21 | a = Solution() 22 | print(a.Sum_Solution(10)) 23 | -------------------------------------------------------------------------------- /test65_不用加减乘除做加法.py: -------------------------------------------------------------------------------- 1 | # 面试题65 不用加减乘除做加法 2 | ''' 3 | 对数字做运算,除了四则运算之外,也就只剩下位运算了。 4 | 程序中存在一些十六进制,是为了防止越位 5 | ''' 6 | 7 | class Solution: 8 | def Add(self, num1, num2): 9 | # write code here 10 | while num2 != 0: 11 | carry = (num1 & num2) << 1 # 进位 12 | num1 = (num1 ^ num2) % 0x100000000 # 两者异或 13 | num2 = carry % 0x100000000 14 | return num1 if num1<=0x7FFFFFFF else num1 |(~0x100000000+1) # 这步是为了防止越界 15 | 16 | a = Solution() 17 | print(a.Add(1,-2)) 18 | -------------------------------------------------------------------------------- /test59_队列的最大值.py: -------------------------------------------------------------------------------- 1 | # 面试题59 队列的最大值 2 | 3 | 4 | # 题目一 滑动窗口的最大值 5 | class Solution: 6 | # 这是暴力解法,我猜测内置函数Max也是一个一个对比寻找 7 | def maxInWindows(self, num, size): 8 | # write code here 9 | maxInWindows = [] 10 | if len(num)>=size and size>=1: 11 | for i in range(len(num)-size+1): 12 | maxInWindows.append(max(num[i:i+size])) 13 | return maxInWindows 14 | 15 | num = [2,3,4,2,6,2,5,1] 16 | a = Solution() 17 | # print(a.maxInWindows(num,3)) 18 | print(a.maxInWindows2(num,3)) 19 | -------------------------------------------------------------------------------- /test21_调整数组顺序使奇数位于偶数之前.py: -------------------------------------------------------------------------------- 1 | # 面试题21 调整数组顺序使奇数位于偶数之前 2 | ''' 3 | 这道题的思路类似于快排的算法,设置两个指针,当符合情况的时候,交换两个指针的值 4 | ''' 5 | 6 | def ReorderOddEven(lis): 7 | if not isinstance(lis,list) or not lis: 8 | return 9 | low,high = 0,len(lis)-1 10 | while low < high: 11 | while low nGreatestSum: 21 | nGreatestSum = nCurSum 22 | return nGreatestSum 23 | 24 | a = Solution() 25 | array = [-10,2,3] 26 | result = a.FindGreatestSumOfSubArray(array) 27 | print(result) 28 | -------------------------------------------------------------------------------- /test67_把字符串转换成整数.py: -------------------------------------------------------------------------------- 1 | # 面试题67 把字符串转换成整数 2 | ''' 3 | Python中str无相减,所以要利用ord()函数,将其转换成ascii码 4 | 这题难度不大,主要注意条件条件 5 | 6 | ''' 7 | 8 | class Solution: 9 | def StrToInt(self, s): 10 | # write code here 11 | if not s: 12 | return 0 13 | if s[0] == '+': 14 | return self.StrToIntCore(s[1:]) 15 | elif s[0] == '-': 16 | return -1*self.StrToIntCore(s[1:]) 17 | return self.StrToIntCore(s) 18 | 19 | def StrToIntCore(self,s): 20 | num = 0 21 | for i in s: 22 | if ord(i) < ord('0') or ord(i) > ord('9'): 23 | return 0 24 | num = num * 10 + ord(i) - ord('0') 25 | return num 26 | 27 | a = Solution() 28 | print(a.StrToInt('1536')) 29 | -------------------------------------------------------------------------------- /test10_菲波那切数列.py: -------------------------------------------------------------------------------- 1 | # 面试题10 菲波那切数列 2 | ''' 3 | 如果采用递归的话,数字过大会导致栈溢出。 4 | 有一个办法就是讲计算的结果保存下来,不需要重复计算,这样能优化一下性能。 5 | 但还是推荐非递归版本。 6 | ''' 7 | # 递归版本 8 | def Fibonacci_Recursion(num): 9 | if num <= 0: 10 | return 0 11 | elif num == 1: 12 | return 1 13 | else: 14 | return Fibonacci_Recursion(num-1)+Fibonacci_Recursion(num-2) 15 | 16 | # 非递归版本 17 | def Fibonacci_Loop(num): 18 | if num <= 0: 19 | return 0 20 | elif num == 1: 21 | return 1 22 | else: 23 | One,Two,fibN = 0,1,0 24 | for i in range(2,num+1): 25 | fibN = One+Two 26 | One,Two = Two,fibN 27 | return fibN 28 | 29 | if __name__=='__main__': 30 | print(Fibonacci_Recursion(10)) 31 | print(Fibonacci_Loop(10)) 32 | -------------------------------------------------------------------------------- /test9_用两个栈实现队列.py: -------------------------------------------------------------------------------- 1 | # 面试题9 用两个栈实现队列 2 | class CQueue: 3 | def __init__(self): 4 | self.stack1 = [] 5 | self.stack2 = [] 6 | 7 | def appendTail(self,element=None): 8 | if not element: 9 | return 10 | self.stack1.append(element) 11 | 12 | def deleteHead(self): 13 | if not self.stack1 and not self.stack2: 14 | return 15 | if not self.stack2:#若stack2为空 16 | while self.stack1: 17 | self.stack2.append(self.stack1.pop())# 将stack1的元素反向压入stack2 18 | return self.stack2.pop() 19 | 20 | if __name__ == '__main__': 21 | aa = CQueue() 22 | aa.appendTail(3) 23 | aa.appendTail(2) 24 | aa.appendTail() 25 | print(aa.deleteHead()) 26 | print(aa.deleteHead()) 27 | print(aa.deleteHead()) 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Target-Offer 2 | 根据《剑指offer》中的题目,用python来进行编写 3 | 4 | add 2018.8.2 5 | 历时一个多月,终于完成了《剑指offer》,里面很多题目很有意思,其变式在面经中经常看到,值得刷一刷。 6 | 但草草的刷了一遍之后,发现还有很多地方没完全掌握,应该花一点时间来总结一下 7 | 8 | 9 | ## 解决复杂问题的思路 10 | 解决难题的三种方法: 11 | 1. 画图 12 | 2. 举例 13 | 3. 分解 14 | 15 | 图形能使抽象的问题形象化。当面试题设计链表,二叉树等数据结构时,如果在纸上画几张草图,则题目中隐藏的规律就有可能变得很直观。 16 | 一两个例子能使抽象的问题具体化。很多与算法相关的问题都很抽象,未必一眼就能看出它们的规律。这时候我们不妨举几个例子,一步一步模拟运行的过程,说不定就能发现其中的规律,从而找到解决问题的窍门。 17 | 把复杂问题分解成若干个小问题,是可以解决很复杂问题的有效方法。如果我们遇到的问题很大,则可以尝试先把大问题分解成小问题,然后再递归地解决这些小问题。分治法/动态规划等方法应用的都是分解复杂问题的思路。 18 | 19 | ## 算法的使用 20 | 回溯法很适合解决迷宫及其类似的问题。如果面试题是求一个问题的最优解,那么可以尝试使用动态规划。加入我们在用动态规划分析问题时发现每一步都存在一个能得到最优解的选择,那么可以尝试使用贪婪算法。 21 | 22 | 23 | ## 代码的完整性 24 | 编成的时候,需要全面考虑所有可能的输入,确保写出的代码在完成了基本功能之外,还考了了边界条件,并做好了错误处理。 25 | 26 | ## 改善时间复杂度 27 | 降低时间复杂度的方法有两种: 28 | 1. 改用更加高效的算法 29 | 2. 用空间换取时间 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /test63_股票的最大利润.py: -------------------------------------------------------------------------------- 1 | # 面试题63 股票的最大利润 2 | ''' 3 | 还是考察数学建模能力。 4 | 如果用暴力法的话,每个数字都与后面的数字相减,时间复杂度是O(n^2) 5 | 书中的方法很好,假设diff(i)为当卖出价为第i个数字时的最大利润。 6 | 当第i个数字固定的时候,且为卖出价,只要找到前面最小的数字,即利润最大化。 7 | 所以把前面最小的数字保存起来,这样时间复杂度为O(n) 8 | 明显快了很多 9 | 为什么我不能想到这种方法呢? 10 | ''' 11 | 12 | class Solution: 13 | def MaxDiff(self,numbers): 14 | if not numbers or len(numbers) < 2: 15 | return 0 16 | minVal = numbers[0] 17 | maxDiff = numbers[1] - minVal 18 | for i in range(2,len(numbers)): 19 | # 更新最小值 20 | if numbers[i-1] < minVal: 21 | minVal = numbers[i-1] 22 | 23 | # 计算差值 24 | curDiff = numbers[i] - minVal 25 | if curDiff > maxDiff: 26 | maxDiff = curDiff 27 | return maxDiff 28 | 29 | numbers = [9,11,8,5,7,12,16,14] 30 | a = Solution() 31 | print(a.MaxDiff(numbers)) 32 | -------------------------------------------------------------------------------- /test62_圆圈中最后剩下的数字.py: -------------------------------------------------------------------------------- 1 | # 面试题62 圆圈中最后剩下的数字 2 | ''' 3 | 这是经典的方法,利用环形链表来模拟圆圈。 4 | 书上有一种基于数学推导的方法,但面试的时候,大概率是想不到的 5 | ''' 6 | 7 | class Node: 8 | def __init__(self,data,next=None): 9 | self.data = data 10 | self.next = next 11 | 12 | class Solution: 13 | def LastRemaining_Solution(self, n, m): 14 | # write code here 15 | if n < 1 or m < 1: 16 | return -1 17 | # 创建一个环形链表 18 | head = Node(0) 19 | p = head 20 | for i in range(1,n): 21 | temp = Node(i) 22 | p.next = temp 23 | p = temp 24 | p.next = head 25 | 26 | p = head 27 | while n > 1: 28 | for i in range(m-1): 29 | p = p.next 30 | # 弹出该元素 31 | temp = p.next 32 | p.data,p.next = temp.data,temp.next 33 | n -=1 34 | return p.data 35 | 36 | a = Solution() 37 | print(a.LastRemaining_Solution(4000,997)) 38 | -------------------------------------------------------------------------------- /test61_扑克牌中的顺子.py: -------------------------------------------------------------------------------- 1 | # 面试题61 扑克牌中的顺子 2 | ''' 3 | 我一开始的想法是,统计0的个数,然后看除0意外的数字是不是顺序排列,当不是顺序排序的时候,插入多少个0才能变顺序排列。 4 | 如果按我之前的想法,可能会变得复杂了。 5 | 而书上的想法是,比较0的个数和除0意外数字的间隔,若0的个数大于间隔,则说明是顺子。 6 | 这种方法能抽象的概括了很多中情况,我现在欠缺这种思维。 7 | ''' 8 | class Solution: 9 | def IsContinuous(self, numbers): 10 | # write code here 11 | if not numbers or len(numbers) != 5: 12 | return False 13 | numbers.sort() 14 | 15 | numberOfZeros = 0 16 | for i in numbers: 17 | if i == 0: 18 | numberOfZeros += 1 19 | 20 | numberOfGap = 0 21 | small = numberOfZeros 22 | while small < len(numbers)-1: 23 | if numbers[small] == numbers[small+1]: 24 | return False 25 | numberOfGap += numbers[small+1] - numbers[small] - 1 26 | small += 1 27 | return False if numberOfGap > numberOfZeros else True 28 | 29 | numbers = [2,4,0,0,3] 30 | a = Solution() 31 | print(a.IsContinuous(numbers)) 32 | -------------------------------------------------------------------------------- /test15_二进制中1的个数.py: -------------------------------------------------------------------------------- 1 | # 面试题15 二进制中1的个数 2 | ''' 3 | 由于python的整数没有上限,没有溢出的说法。 4 | 所以对于负数的处理,可以先*-1,让其正数在计算。 5 | 书上给了2种方法,对1进行左移和将本身与本身减1做位与运算。 6 | 一个整数减去1,再和原整数做位于运算,会把该整数最右边的1变成0。 7 | 另外,左移比右移考虑的问题要少。如果负数右移,会在最高位补1,而不是0. 8 | 9 | 别人给出了一种方法,将数字变成二进制,然后用count来统计1出现的次数。 10 | https://github.com/Jack-Lee-Hiter/AlgorithmsByPython/blob/master/Target%20Offer/%E4%BA%8C%E8%BF%9B%E5%88%B6%E4%B8%AD1%E7%9A%84%E4%B8%AA%E6%95%B0.py 11 | ''' 12 | def NumberOf1_1(num): 13 | count , flag = 0 , 1 14 | if num < 0: 15 | num *= -1 16 | count = 1 17 | while flag and flag <= num: 18 | if num & flag : 19 | count += 1 20 | flag = flag << 1 21 | return count 22 | 23 | def NumberOf1_2(num): 24 | count = 0 25 | if num < 0: 26 | num = num&0xffffffff 27 | while num: 28 | count += 1 29 | num = (num-1) & num 30 | return count 31 | 32 | 33 | if __name__ == '__main__': 34 | print(NumberOf1_1(11)) 35 | print(NumberOf1_2(-11)) 36 | -------------------------------------------------------------------------------- /test33_二叉搜索树的后序遍历序列.py: -------------------------------------------------------------------------------- 1 | # 面试题33 二叉搜索树的后序遍历序列 2 | ''' 3 | 对于大部分二叉树的题目,都是用递归解决 4 | 这道题先分析出规律,通过规律来写代码,这样就简单很多 5 | 6 | ''' 7 | 8 | def VerifySquenceOfBST(sequence): 9 | if not sequence: 10 | return False 11 | root = sequence[-1] 12 | length = len(sequence) 13 | # 左子树 14 | i = 0 15 | while i < length - 1: 16 | if sequence[i] > root: 17 | break 18 | i += 1 19 | # 右子树 20 | j = i 21 | while j < length - 1: 22 | if sequence[j] < root: 23 | return False # 当右子树中某个值小于root时,说明不存在该二叉搜索树 24 | j += 1 25 | # 判断左子树是不是二叉搜索树 26 | left = True 27 | if i > 0: # 当进行到此步,说明sequence不为空,当数组的元素只有1个的时候,不进入判断,返回的left为True 28 | left = VerifySquenceOfBST(sequence[:i]) 29 | # 判断右子树是不是二叉搜索树 30 | right = True 31 | if i < length - 1: 32 | right = VerifySquenceOfBST(sequence[i:length-1]) 33 | return left and right 34 | 35 | lis = [5,7,6,9,11,10,8] 36 | lis2 = [7,4,6,5] 37 | print(VerifySquenceOfBST(lis)) 38 | -------------------------------------------------------------------------------- /test24_反转链表.py: -------------------------------------------------------------------------------- 1 | # 面试题24 反转链表 2 | ''' 3 | 设置三个指针 4 | ''' 5 | 6 | class ListNode: 7 | def __init__(self,data=None,next=None): 8 | self.data = data 9 | self.next = next 10 | 11 | def ReverseList(Node): 12 | pReversedHead = None # 用于返回的头结点 13 | pNode = Node 14 | pPrev = None # pNode之前的指针 15 | while pNode: 16 | pNext = pNode.next # pNode之后的指针 17 | if not pNext: 18 | pReversedHead = pNode 19 | pNode.next = pPrev 20 | pPrev = pNode 21 | pNode = pNext 22 | return pReversedHead 23 | 24 | if __name__ == '__main__': 25 | node1 = ListNode(1) 26 | node2 = ListNode(2) 27 | node3 = ListNode(3) 28 | node4 = ListNode(4) 29 | node5 = ListNode(5) 30 | node6 = ListNode(6) 31 | 32 | node1.next = node2 33 | node2.next = node3 34 | node3.next = node4 35 | node4.next = node5 36 | node5.next = node6 37 | pReversedHead = ReverseList(node1) 38 | 39 | while pReversedHead: 40 | print(pReversedHead.data,end=' ') 41 | pReversedHead = pReversedHead.next 42 | -------------------------------------------------------------------------------- /test4_二维数组中的查找.py: -------------------------------------------------------------------------------- 1 | # 面试题4 二维数组中的查找 2 | ''' 3 | 这道题的思路就是从右上角开始寻找, 4 | 逐步缩减行和列。 5 | 如果不用numpy的话,就要输出参数就要包括行数和列数。 6 | 用numpy的话,有个bug,如果lis是一维数组,m,n = np.shape(lis)就会报错, 7 | 因为如果lis是一维的话,shape只会返回一个参数。 8 | ''' 9 | 10 | # 面试题4 二维数组中的查找 11 | import numpy as np 12 | def Find(lis,num): 13 | # 首先判断其是不是空的list 14 | if not isinstance(lis,list) or len(lis) == 0: 15 | print('空的list') 16 | return False 17 | m,n = np.shape(lis) 18 | rows,columns = 0,n-1#行和列 19 | while rows=0: 20 | if num == lis[rows][columns]: 21 | # 当查找到该值,返回true 22 | return True 23 | elif num < lis[rows][columns]: 24 | # 当num小于右上角,说明值在左边 25 | columns -= 1 26 | else: 27 | rows += 1 28 | return False 29 | 30 | if __name__ == '__main__': 31 | lis = [[1,2,8,9], 32 | [2,4,9,12], 33 | [4,7,10,13], 34 | [6,8,11,15]] 35 | lis1 = [] 36 | # print(Find(lis, 9.5)) 37 | print(Find(lis1,9)) 38 | -------------------------------------------------------------------------------- /test30_包含min函数的栈.py: -------------------------------------------------------------------------------- 1 | # 面试题30 包含min函数的栈 2 | ''' 3 | 本题的思路是构建2个栈,一个数据栈,一个辅助栈 4 | 对于辅助栈,每次都是压入当前的最小值 5 | ''' 6 | 7 | class StackWithMin: 8 | def __init__(self): 9 | self.m_data=[]#数据栈 10 | self.m_min =[]#辅助栈 11 | 12 | def push(self,data): 13 | self.m_data.append(data) 14 | 15 | if len(self.m_min) == 0 or data 0: 24 | up = maxValues[i-1][j] 25 | if j > 0: 26 | left = maxValues[i][j-1] 27 | maxValues[i][j] = max(up,left) + values[i][j] 28 | print(maxValues[cols-1][rows-1]) 29 | 30 | 31 | values=[[1,10,3,8], 32 | [12,2,9,6], 33 | [5,7,4,11], 34 | [3,7,16,5]] 35 | a = Solution() 36 | a.getMaxValue_solution1(values) 37 | -------------------------------------------------------------------------------- /test16_数值的整数次方.py: -------------------------------------------------------------------------------- 1 | # 面试题16 数值的整数次方 2 | ''' 3 | 此题不难,重点是考察了边界条件。 4 | 要想要特殊值0和负值。 5 | ''' 6 | g_InvalidInput = False 7 | def Power(base,exponent): 8 | global g_InvalidInput 9 | g_InvalidInput = False 10 | if base == 0 and exponent < 0: 11 | g_InvalidInput = True 12 | return 0.0 13 | result = PowerWithUnsighedExponent_quick(base,abs(exponent)) 14 | if exponent < 0: 15 | result = 1.0 / result 16 | return result 17 | 18 | 19 | def PowerWithUnsighedExponent(base,exponent): 20 | result = 1.0 21 | for i in range(exponent): 22 | result *= base 23 | return result 24 | 25 | def PowerWithUnsighedExponent_quick(base,exponent): 26 | if exponent == 0: 27 | return 1 28 | if exponent == 1: 29 | return base 30 | 31 | result = PowerWithUnsighedExponent_quick(base,exponent>>1) 32 | result = result * result # 即平方 33 | if exponent & 0x1: 34 | result *= base 35 | return result 36 | 37 | 38 | if __name__ == '__main__': 39 | result = Power(2,-5) 40 | print(result) 41 | print(g_InvalidInput) 42 | -------------------------------------------------------------------------------- /test11_旋转数组的最小数字.py: -------------------------------------------------------------------------------- 1 | # 面试题11 旋转数组的最小数字 2 | ''' 3 | 此道面试题是二分查找的变式,比较有意思,时间复杂度为O(logn) 4 | 解题的突破点在于画图。 5 | ''' 6 | def Min(lis): 7 | if not isinstance(lis,list) or not lis: 8 | return 9 | start , end = 0,len(lis)-1 10 | mid = start 11 | while lis[start] >= lis[end]: 12 | if end - start == 1: 13 | mid = end 14 | break 15 | mid = start + int((end-start)/2) 16 | # 如果start,end和mid指向的值都相同,只能顺序查找 17 | if lis[start]==lis[mid]==lis[end]: 18 | mid = MinInOrder(lis,start,end) 19 | break 20 | if lis[mid] >= lis[start]:#说明mid在前一段序列中 21 | start = mid 22 | elif lis[mid] <= lis[end]:#说明mid在后一段序列中 23 | end = mid 24 | return lis[mid] 25 | 26 | def MinInOrder(lis,start,end): 27 | val,min = lis[start],start 28 | for i in range(start,end+1): 29 | if lis[i]= len(numbers): 14 | vec.append(int(''.join(map(str,numbers)))) 15 | for i in range(n,len(numbers)): 16 | numbers[i],numbers[n] = numbers[n],numbers[i] 17 | self.Permutation(numbers,n+1,vec) 18 | numbers[i], numbers[n] = numbers[n], numbers[i] 19 | 20 | def check(self,numbers,n,i): 21 | if i > n: 22 | for j in range(n,i): 23 | if numbers[j] == numbers[i]: 24 | return False 25 | return True 26 | 27 | class Solution2: 28 | def PrintMinNumber(self, numbers): 29 | from functools import cmp_to_key 30 | if not numbers: 31 | return '' 32 | lmb = cmp_to_key(lambda n1, n2:int(str(n1)+str(n2))-int(str(n2)+str(n1))) 33 | array = sorted(numbers, key=lmb)# 不太懂里面的运行机理 34 | return ''.join([str(i) for i in array]) 35 | 36 | lis =[3,5,1,4,2] 37 | # a = Solution() 38 | # a.PrintMinNumber(lis) 39 | b=Solution2() 40 | c = b.PrintMinNumber(lis) 41 | print(c) 42 | -------------------------------------------------------------------------------- /test34_二叉树中和为某一值的路径.py: -------------------------------------------------------------------------------- 1 | # 面试题34 二叉树中和为某一值的路径 2 | ''' 3 | 本道题是以根节点为扩展的,包含的路径必须右根节点,所以不能某一部分的值之和 4 | ''' 5 | 6 | class TreeNode: 7 | def __init__(self,data=None,left=None,right=None): 8 | self.data = data 9 | self.left = left 10 | self.right = right 11 | 12 | def FindPath(Node,expectedSum): 13 | if not isinstance(Node,TreeNode) or not isinstance(expectedSum,int): 14 | return 15 | path = [] 16 | currentSum = 0 17 | Find_Path(Node,expectedSum,path,currentSum) 18 | 19 | def Find_Path(Node,expectedSum,path,currentSum): 20 | currentSum += Node.data 21 | path.append(Node) 22 | 23 | # 如果是叶节点,且路径上的节点值之和等于输入的值,则打印 24 | isLeaf = Node.left == None and Node.right == None 25 | if currentSum == expectedSum and isLeaf: 26 | for i in path: 27 | print(i.data,end=' ') 28 | print() 29 | 30 | # 如果不是叶节点,则遍历它的子节点 31 | if Node.left: 32 | Find_Path(Node.left,expectedSum,path,currentSum) 33 | if Node.right: 34 | Find_Path(Node.right,expectedSum,path,currentSum) 35 | 36 | # 在返回父节点之前,在路径上删除当前节点 37 | path.pop() 38 | 39 | node1 = TreeNode(10) 40 | node2 = TreeNode(5) 41 | node3 = TreeNode(12) 42 | node4 = TreeNode(4) 43 | node5 = TreeNode(7) 44 | 45 | node1.left,node1.right = node2,node3 46 | node2.left,node2.right = node4,node5 47 | 48 | FindPath(node1,22) 49 | -------------------------------------------------------------------------------- /test48_最长不含重复字符的子字符串.py: -------------------------------------------------------------------------------- 1 | # 面试题48 最长不含重复字符的子字符串 2 | ''' 3 | 还是利用动态规划的方法来解决 4 | 定义f(i)表示以第i个字符为结尾的不包含重复字符的子字符串的最大长度。 5 | 现在有2种情况: 6 | 1. 当第i个字符没有出现过时,f(i)=f(i-1)+1 7 | 2. 当第i个字符出现过时,记两个重复字符的距离为d 8 | a) 若d<=f(i-1)时,说明重复的字符在f(i-i)的最长子字符串中,所以f(i)=d 9 | b) 若d>f(i-1)时,说明重复的字符不在f(i-i)的最长子字符串中,所以f(i)=f(i-1)+1 10 | 11 | 下面的程序中,curLength相当于f(i)。 12 | 如果不能理解的话,可以新建一个list来保存f(i)。 13 | ''' 14 | 15 | class Solution: 16 | def longestSubstringWithoutDuplication(self, s1): 17 | curLength, maxLength = 0, 0#curLength相当于书上的f(i) 18 | position = [-1] * 26 # 这里26表示26个字母,这个list用于保存上一个重复字符的位置 19 | for i in range(len(s1)): 20 | prevIndex = position[ord(s1[i]) - ord('a')] # python无字符串相减,所以用ord将字符转换为ascii码 21 | if prevIndex < 0 or i - prevIndex > curLength: 22 | # 后半个判断条件是:当第i位的字符和重复的字符的位置相差大于curLength时,不变 23 | curLength += 1 24 | else: 25 | if curLength > maxLength: 26 | maxLength = curLength 27 | # 当第i位的字符和重复的字符的位置相差小于curLength时,让curLength=d 28 | curLength = i - prevIndex # 2个相同字符的距离d 29 | position[ord(s1[i]) - ord('a')] = i # 保存当先字符的下标 30 | if curLength > maxLength: 31 | maxLength = curLength 32 | print(maxLength) 33 | 34 | a = Solution() 35 | a.longestSubstringWithoutDuplication('arabcacfr') 36 | -------------------------------------------------------------------------------- /test29_顺时针打印矩阵.py: -------------------------------------------------------------------------------- 1 | # 面试题29 顺时针打印矩阵 2 | ''' 3 | 本面试题中包含多个循环和多个边界循环判断 4 | 很考虑编程的功力 5 | 本道题分解成多个圈进行打印 6 | 7 | 网上有种思路,就是每次打印完一行后,删除这一行并将数组进行旋转。 8 | 但旋转的复杂度有点高。 9 | 10 | ''' 11 | 12 | def PrintMatrixClockwisely(matrix): 13 | if not isinstance(matrix, list): 14 | return 15 | m, n = len(matrix), len(matrix[0]) 16 | start = 0 17 | while m > start * 2 and n > start * 2: 18 | PrintMatrixInCircle(matrix, m, n, start) 19 | start += 1 20 | 21 | def PrintMatrixInCircle(matrix,rows,columns,start): 22 | endX, endY = columns - 1 - start, rows - 1 - start 23 | #从左到右打印一行 24 | for i in range(start, endX + 1): 25 | print(matrix[start][i], end=' ') 26 | #从上到下打印一列 27 | if endY > start: 28 | for i in range(start + 1, endY + 1): 29 | print(matrix[i][endX], end=' ') 30 | #从右到左打印一行 31 | if endX > start and endY > start: 32 | for i in range(endY - 1, start - 1, -1): 33 | print(matrix[endX][i], end=' ') 34 | #从下到上打印一列 35 | if endX > start and endY - 1 > start: 36 | for i in range(endX - 1, start, -1): 37 | print(matrix[i][start], end=' ') 38 | 39 | 40 | if __name__ == '__main__': 41 | import numpy as np 42 | matrix = np.arange(1,17).reshape((1,-1)) 43 | matrix = matrix.tolist() 44 | print(matrix) 45 | # matrix = [[1]] 46 | 47 | PrintMatrixClockwisely(matrix) 48 | -------------------------------------------------------------------------------- /test44_数字序列中的某一位数字.py: -------------------------------------------------------------------------------- 1 | # 面试题44 数字序列中的某一位数字 2 | ''' 3 | 第一种方法是暴力解法,不足之处是应该计算一下range的范围,而不应该用index,这样会浪费很多内存 4 | 5 | 第二中方法是找规律,这个规律有点难找,无论在面试和笔试中,这点时间不可能想到,只能靠积累了。 6 | 7 | ''' 8 | class Solution: 9 | def digitAtIndex(self,index): 10 | if index < 0 : 11 | return -1 12 | xx = [i for i in range(index+1)] 13 | ss = ''.join(map(str,xx)) 14 | print(ss[index]) 15 | 16 | class Solution_2: 17 | def digitAtIndex(self,index): 18 | if index < 0: 19 | return -1 20 | digit = 1 21 | while True: 22 | numbers = self.countOfIntegers(digit) 23 | if index < numbers * digit: 24 | return self.digitAtIndex_2(index,digit) 25 | index -= digit*numbers 26 | digit += 1 27 | 28 | def countOfIntegers(self,digits): 29 | # 统计digits位一共有多少个数字 30 | if digits == 1: 31 | return 10 32 | count = pow(10,digits-1) 33 | return 9*count 34 | 35 | def digitAtIndex_2(self,index,digit): 36 | Location = index // digit 37 | yushu = index % digit 38 | xx = str(self.beginNumber(digit) + Location) 39 | return xx[yushu] 40 | 41 | def beginNumber(self,digit): 42 | if digit == 1: 43 | return 0 44 | return pow(10,digit-1) 45 | 46 | # a = Solution() 47 | # a.digitAtIndex(12) 48 | b=Solution_2() 49 | print(b.digitAtIndex(12)) 50 | 51 | -------------------------------------------------------------------------------- /test46_把数字翻译成字符串.py: -------------------------------------------------------------------------------- 1 | # 面试题46 把数字翻译成字符串 2 | ''' 3 | 将大问题分解成小问题解决,所以使用递归 4 | f(i)表示第i位数字开始的不同翻译的数目 5 | f(i)=f(i+1)+g(i,i+i)f(i+2) 6 | g(i,i+i)表示当第i位和第i+1位组合成的数字在10到25的范围内时,为1,否则为0 7 | 上面的递归公式说明,第i为的总翻译数等于,当认为第i位为单值时,总数是后面的f(i+i),当第i位和第i+1位组合组合时, 8 | 总数为f(i+2) 9 | 递归从最大的问题开始自上向下解决问题,但没法消除重复的问题。 10 | 所以我们可以从最小的问题开始自下向上解决问题。 11 | ''' 12 | 13 | class Solution: 14 | def GetTranslationCount(self,number): 15 | if number < 0: 16 | return 0 17 | numberInString = str(number) 18 | return self.GetTranslation_Count(numberInString) 19 | 20 | def GetTranslation_Count(self,number): 21 | count = 0 22 | counts = [None] * len(number) 23 | for i in range(len(number)-1,-1,-1): 24 | count = 0 25 | if i < len(number) -1: 26 | count += counts[i+1] 27 | else: 28 | count =1 29 | if i < len(number) -1: 30 | digit1 = int(number[i]) 31 | digit2 = int(number[i+1]) 32 | converted = digit1*10 + digit2 33 | if converted >= 10 and converted <= 25: 34 | if i < len(number) - 2: 35 | count+=counts[i+2] 36 | else: 37 | count+=1 38 | counts[i] = count 39 | print(counts) 40 | return counts[0] 41 | 42 | a = Solution() 43 | c = a.GetTranslationCount(12258) 44 | print(c) 45 | -------------------------------------------------------------------------------- /test28_对称的二叉树.py: -------------------------------------------------------------------------------- 1 | # 面试题28 对称的二叉树 2 | ''' 3 | 对于二叉树的编程,感觉大多数都是采用了递归的方法 4 | 将一个大问题分解成很多个小问题解决 5 | 6 | ''' 7 | 8 | class BinartTreeNode: 9 | def __init__(self,data=None,left=None,right=None): 10 | self.data = data 11 | self.left = left 12 | self.right = right 13 | 14 | def isSymmetrical(Node): 15 | if not isinstance(Node, BinartTreeNode): 16 | return False 17 | return is_Symmetrical(Node,Node) 18 | 19 | def is_Symmetrical(pRoot1,pRoot2): 20 | if not pRoot1 and not pRoot2: 21 | return True 22 | if not pRoot1 or not pRoot2: 23 | return False 24 | if pRoot1.data != pRoot2.data: 25 | return False 26 | return is_Symmetrical(pRoot1.left,pRoot2.right) and \ 27 | is_Symmetrical(pRoot1.right,pRoot2.left) 28 | 29 | def preOrder(root): 30 | if root: 31 | print(root.data,end=' ') 32 | preOrder(root.left) 33 | preOrder(root.right) 34 | 35 | if __name__ == '__main__': 36 | node1 = BinartTreeNode(8) 37 | node2 = BinartTreeNode(6) 38 | node3 = BinartTreeNode(6) 39 | node4 = BinartTreeNode(5) 40 | node5 = BinartTreeNode(7) 41 | node6 = BinartTreeNode(7) 42 | node7 = BinartTreeNode(5) 43 | 44 | node1.left,node1.right = node2,node3 45 | node2.left,node2.right = node4,node5 46 | # node3.left,node3.right = node6,node7 47 | node3.left = node6 48 | 49 | preOrder(node1) 50 | print() 51 | 52 | print(isSymmetrical(node1)) 53 | -------------------------------------------------------------------------------- /test7_重建二叉树.py: -------------------------------------------------------------------------------- 1 | # 面试题7 重建二叉树 2 | ''' 3 | 这道面试题采用了递归的方法,用python写只需要几行即可。 4 | 书上的大部分代码都是在检查边界条件,显得有些复杂。 5 | 这里参考了https://github.com/Jack-Lee-Hiter/AlgorithmsByPython/blob/master/Target%20Offer/%E9%87%8D%E5%BB%BA%E4%BA%8C%E5%8F%89%E6%A0%91.py 6 | ''' 7 | class BinaryTreeNode: 8 | def __init__(self,data=None,left=None,right=None): 9 | self.data = data 10 | self.left = left 11 | self.right = right 12 | 13 | # 非递归前序遍历 14 | # 当p非空或stack非空时,输出p.data并将p压入stack,遍历左子树 15 | # 当无左子树时,弹出stack,遍历该节点的右子树 16 | def preOrder1(BinaryTreeNode): 17 | if BinaryTreeNode == None: 18 | return 19 | stack = [] 20 | p = BinaryTreeNode 21 | while p != None or stack: 22 | while p != None: 23 | print(p.data,end=' ') 24 | stack.append(p) 25 | p = p.left 26 | if stack: 27 | p = stack.pop().right 28 | print() 29 | 30 | def ConstructCore(preorder,inorder): 31 | if not preorder or not inorder: 32 | return None 33 | root = BinaryTreeNode(preorder[0])#根节点 34 | # 判断输入的两个序列是不是匹配 35 | if set(preorder) != set(inorder): 36 | return None 37 | i = inorder.index(root.data) 38 | root.left = ConstructCore(preorder[1:i+1],inorder[:i]) 39 | root.right = ConstructCore(preorder[i+1:],inorder[i+1:]) 40 | return root 41 | 42 | if __name__ == '__main__': 43 | preorder = [1,2,4,7,3,5,6,8] 44 | inorder = [4,7,2,1,5,3,8,6] 45 | re_root = ConstructCore(preorder,inorder) 46 | preOrder1(re_root) 47 | -------------------------------------------------------------------------------- /test37_序列化二叉树.py: -------------------------------------------------------------------------------- 1 | # 面试题37 序列化二叉树 2 | ''' 3 | 序列化的意思是将二叉树存储成一个文件,反序列即将文件还原成二叉树。 4 | 在python里面,可以利用pickle或者json来进行处理,这里只是将其转化成一个list,没有存储 5 | 6 | 首先序列化,很简单,就是一个前序遍历,但不同之处在于,遇到none会打印'$',而不是跳过。 7 | 然后反序列化,这里有点难度,还是利用递归来处理,这里需要引入一个变量,让其一直递增来进行构建节点。 8 | 具体可以看看程序,反序列化也可以理解成构建一个二叉树。 9 | ''' 10 | 11 | class TreeNode: 12 | def __init__(self,data=None,left=None,right=None): 13 | self.data = data 14 | self.left = left 15 | self.right = right 16 | 17 | def Serialize(pRoot,serializelis): 18 | if pRoot == None: 19 | serializelis.append('$') 20 | return 21 | serializelis.append(str(pRoot.data)) 22 | Serialize(pRoot.left,serializelis) 23 | Serialize(pRoot.right,serializelis) 24 | 25 | def Deserialize(serializelis): 26 | tree,sp = deserlialize(serializelis,0) 27 | return tree 28 | 29 | def deserlialize(serializelis,sp): 30 | if sp >= len(serializelis) or not serializelis[sp].isdigit(): 31 | return None,sp+1 32 | node = TreeNode(int(serializelis[sp])) 33 | sp += 1 34 | node.left,sp = deserlialize(serializelis,sp) 35 | node.right,sp = deserlialize(serializelis,sp) 36 | return node,sp 37 | 38 | 39 | node1 = TreeNode(1) 40 | node2 = TreeNode(2) 41 | node3 = TreeNode(3) 42 | node4 = TreeNode(4) 43 | node5 = TreeNode(5) 44 | node6 = TreeNode(6) 45 | 46 | node1.left,node1.right = node2,node3 47 | node2.left = node4 48 | node3.left,node3.right = node5,node6 49 | 50 | serializelis =[] 51 | Serialize(node1,serializelis) 52 | print(serializelis) 53 | tree = Deserialize(serializelis) 54 | print(tree.left.data) 55 | -------------------------------------------------------------------------------- /test36_二叉搜索树与双向链表.py: -------------------------------------------------------------------------------- 1 | # 面试题36 二叉搜索树与双向链表 2 | ''' 3 | 很常见的题目,里面涉及到大量的指针操作 4 | 原先指向左子节点的指针调整为链表中指向前一个节点的指针 5 | 原先指向右子节点的指针调整为链表中指向后一个节点的指针 6 | ''' 7 | 8 | class TreeNode: 9 | def __init__(self,data=None,left=None,right=None): 10 | self.data = data 11 | self.left = left 12 | self.right = right 13 | 14 | def Convert(pRoot): 15 | if pRoot == None: 16 | return None 17 | # 当是叶节点的时候,返回叶节点 18 | if not pRoot.left and not pRoot.right: 19 | return pRoot 20 | 21 | # 处理左子树 22 | Convert(pRoot.left)# 当第一次递归的时候,pRoot指向叶节点的父节点 23 | left = pRoot.left 24 | 25 | # 连接根与左子树最大结点 26 | if left: 27 | while left.right: 28 | left = left.right 29 | pRoot.left = left 30 | left.right = pRoot 31 | 32 | # 处理右子树 33 | Convert(pRoot.right) 34 | right = pRoot.right 35 | 36 | # 连接根与右子树最小结点 37 | if right: 38 | while right.left: 39 | right = right.left 40 | pRoot.right = right 41 | right.left = pRoot 42 | 43 | # 下面是为了返回给主程序使用的,由于这个程序中没有用Convert的值,所以会抛弃 44 | while pRoot.left: 45 | pRoot = pRoot.left 46 | 47 | return pRoot 48 | 49 | pNode1 = TreeNode(10) 50 | pNode2 = TreeNode(6) 51 | pNode3 = TreeNode(14) 52 | pNode4 = TreeNode(4) 53 | pNode5 = TreeNode(8) 54 | pNode6 = TreeNode(12) 55 | pNode7 = TreeNode(16) 56 | 57 | pNode1.left,pNode1.right = pNode2,pNode3 58 | pNode2.left,pNode2.right = pNode4,pNode5 59 | pNode3.left,pNode3.right = pNode6,pNode7 60 | 61 | a = Convert(pNode1) 62 | while a: 63 | print(a.data,end=' ') 64 | a = a.right 65 | -------------------------------------------------------------------------------- /test12_矩阵中的路径.py: -------------------------------------------------------------------------------- 1 | # 面试题12 矩阵中的路径 2 | ''' 3 | 此题运用了回溯法,是暴力解法的升级版 4 | 首先找到第一个字符的位置,然后进入hasPathCore函数,通过比较下一个字符串的位置来选择进入哪一个分支。 5 | 此题修改了原来的lis,如果要求不允许修改,则需要重新建一个副本,不能查找类似lis2和s2的情况。 6 | 解决方案是去掉lis[i][j] = '*',但是这样会出现重复路径的问题。 7 | 参考了 https://blog.csdn.net/iyuanshuo/article/details/79649596 8 | ''' 9 | 10 | def hasPath(lis,s1): 11 | if not lis or not s1: 12 | return 13 | rows,cols = len(lis),len(lis[0]) 14 | for i in range(rows): 15 | for j in range(cols): 16 | if lis[i][j] == s1[0]: 17 | if hasPathCore(lis, s1[1:], rows, cols, i, j): 18 | return True 19 | return False 20 | 21 | def hasPathCore(lis,s1,rows,cols,i,j): 22 | if not s1: 23 | return True 24 | # lis[i][j] = '*' 25 | if j + 1 < cols and s1[0] == lis[i][j+1]:#往右找 26 | return hasPathCore(lis, s1[1:], rows, cols, i, j + 1) 27 | elif j - 1 >= 0 and s1[0] == lis[i][j - 1]:#往左找 28 | return hasPathCore(lis, s1[1:], rows, cols, i, j - 1) 29 | elif i + 1 < rows and s1[0] == lis[i + 1][j]:#往上找 30 | return hasPathCore(lis, s1[1:], rows, cols, i + 1, j) 31 | elif i - 1 >= 0 and s1[0] == lis[i - 1][j]:#往下找 32 | return hasPathCore(lis, s1[1:], rows, cols, i - 1, j) 33 | else: 34 | return False 35 | 36 | 37 | if __name__ == '__main__': 38 | lis = [list('abtg'), 39 | list('bbbb'), 40 | list('cfcs'), 41 | list('jdeh')] 42 | lis2 = [list('abfe'), 43 | list('abcd'), 44 | list('adce')] 45 | s1 = 'bfce' 46 | s2 = 'bbfe' 47 | print(hasPath(lis,s1)) 48 | -------------------------------------------------------------------------------- /test54_二叉搜索树的第k大节点.py: -------------------------------------------------------------------------------- 1 | # 面试题54 二叉搜索树的第k大节点 2 | ''' 3 | 题目应该改成“二叉搜索树的第k个节点” 4 | 首先是二叉搜索树,是有序的,所以根据中序遍历,就能到第K个节点 5 | 6 | 不过值得注意的是一些python的语法 7 | 变量是值传递,不可变对象,所以想要改变,可以将其放入一个list里面 8 | 而list是地址传递,list是可变对象 9 | 10 | 因为Python对象分为可变对象(list,dict,set等)和不可变对象(number,string,tuple等), 11 | 当传递的参数是可变对象的引用时,因为可变对象的值可以修改,因此可以通过修改参数值而修改原对象,这类似于C语言中的引用传递; 12 | 当传递的参数是不可变对象的引用时,虽然传递的是引用,参数变量和原变量都指向同一内存地址,但是不可变对象无法修改, 13 | 所以参数的重新赋值不会影响原对象,这类似于C语言中的值传递。 14 | https://blog.csdn.net/hohaizx/article/details/78427406 15 | ''' 16 | 17 | class TreeNode: 18 | def __init__(self, x): 19 | self.val = x 20 | self.left = None 21 | self.right = None 22 | 23 | class Solution: 24 | # 返回对应节点TreeNode 25 | def __init__(self): 26 | self.result = None 27 | self.k = None 28 | 29 | def KthNode(self, pRoot, k): 30 | # write code here 31 | if not pRoot or k == 0: 32 | return 33 | self.k = k 34 | self.KthNodeCore(pRoot) 35 | return self.result 36 | 37 | def KthNodeCore(self, pRoot): 38 | if pRoot: 39 | self.KthNodeCore(pRoot.left) 40 | if self.k == 1: 41 | self.result = pRoot 42 | self.k -= 1 43 | self.KthNodeCore(pRoot.right) 44 | 45 | node1 = TreeNode(5) 46 | node2 = TreeNode(3) 47 | node3 = TreeNode(7) 48 | node4 = TreeNode(2) 49 | node5 = TreeNode(4) 50 | node6 = TreeNode(6) 51 | node7 = TreeNode(8) 52 | 53 | node1.left,node1.right = node2,node3 54 | node2.left,node2.right = node4,node5 55 | node3.left,node3.right = node6,node7 56 | 57 | a = Solution() 58 | print(a.KthNode(node1,8)) 59 | -------------------------------------------------------------------------------- /test25_合并两个排序的链表.py: -------------------------------------------------------------------------------- 1 | # 面试题25 合并两个排序的链表 2 | ''' 3 | 书上用的是递归,代码更加简洁 4 | 但也能直接用循环 5 | ''' 6 | 7 | class ListNode: 8 | def __init__(self,data=None,next=None): 9 | self.data = data 10 | self.next = next 11 | 12 | def Merge(Node1,Node2): 13 | if not isinstance(Node1,ListNode) or not isinstance(Node2,ListNode): 14 | return None 15 | if not Node1.data: 16 | return Node2 17 | elif not Node2.data: 18 | return Node1 19 | 20 | pAnode,pBnode = Node1,Node2 21 | # 创建一个用于返回头指针的pHead,让其指向小的值 22 | if pAnode.data < pBnode.data: 23 | pHead = pAnode 24 | pAnode = pAnode.next 25 | else: 26 | pHead = pBnode 27 | pBnode = pBnode.next 28 | 29 | p1 = pHead# 此p1用于移动 30 | while pAnode and pBnode: 31 | if pAnode.data < pBnode.data: 32 | p1.next = pAnode 33 | pAnode = pAnode.next 34 | else: 35 | p1.next = pBnode 36 | pBnode = pBnode.next 37 | p1 = p1.next 38 | #当其中一个指针移动到最后的时候,补上另外一个指针 39 | if not pAnode: 40 | p1.next = pBnode 41 | else: 42 | p1.next = pAnode 43 | 44 | return pHead 45 | 46 | if __name__ == '__main__': 47 | node1 = ListNode(1) 48 | node2 = ListNode(3) 49 | node3 = ListNode(5) 50 | node4 = ListNode(7) 51 | node1.next = node2 52 | node2.next = node3 53 | node3.next = node4 54 | 55 | node5 = ListNode(2) 56 | node6 = ListNode(4) 57 | node7 = ListNode(6) 58 | node8 = ListNode(8) 59 | node5.next = node6 60 | node6.next = node7 61 | node7.next = node8 62 | 63 | pHead = Merge(node1,node5) 64 | while pHead: 65 | print(pHead.data,end=' ') 66 | pHead = pHead.next 67 | 68 | 69 | -------------------------------------------------------------------------------- /test14_剪绳子.py: -------------------------------------------------------------------------------- 1 | # 面试题14 剪绳子 2 | ''' 3 | 此道题有2中方法,动态规划和贪婪算法 4 | 在运用动态规划之前要分析能否把大问题分解成小问题,分解后的每个小问题也存在最优解。 5 | 如果把小问题的最优解组合起来能够得到整个问题的最优解,则可以运用动态规划。 6 | 这个思路有点类似于递归,但动态规划是自下而上的计算 7 | 8 | 把小问题的最优解存储起来,然后组合成最优解。 9 | ''' 10 | 11 | # 时间复杂度为O(n^2),空间复杂度为O(n) 12 | def maxProductAferCutting_solution1(length): 13 | if length < 2: 14 | return 0 15 | if length == 2: 16 | return 1 17 | if length == 3: 18 | return 2 19 | 20 | products = [0] * (length+1) 21 | products[1] = 1 22 | products[2] = 2 23 | products[3] = 3#长度为3的线段,如果不切分的话,长度是最大的,所以这里3个数字是不切分时的最大长度。 24 | 25 | for n in range(4,length+1): 26 | max = 0 27 | for i in range(1,int(n/2) + 1):#因为是对称的,所以一半 28 | product = products[i] * products[n-i] 29 | if max < product: 30 | max = product 31 | products[n] = max 32 | return products[length] 33 | 34 | # 时间和空间复杂度都为O(1) 35 | def maxProductAferCutting_solution2(length): 36 | if length < 2: 37 | return 0 38 | if length == 2: 39 | return 1 40 | if length == 3: 41 | return 2 42 | 43 | timesOf3 = length // 3 44 | # 当绳子最后剩下的长度为4时,不要再减去长度为3的绳子 45 | # 而是把绳子剪成长度为2的两端。 46 | # 所以下面一行的判断就是判断剩下的长度是否为4 47 | if length - timesOf3 * 3 == 1: 48 | timesOf3 -= 1 49 | 50 | # 应该有3种情况: 51 | # 1.当剩下长度为0,即能被3整除,此时timesOf2为0,所以最后的return的后面项是'*1' 52 | # 2.剩下长度为1,即绳子最后的长度为4 53 | # 3.剩下长度为2,此时此时timesOf2为1,所以最后的return的后面项是'*2' 54 | timesOf2 = (length - timesOf3 * 3) / 2 55 | 56 | return int(pow(3,timesOf3) * pow(2,timesOf2)) 57 | 58 | 59 | if __name__ == '__main__': 60 | print(maxProductAferCutting_solution1(10)) 61 | print(maxProductAferCutting_solution2(10)) 62 | -------------------------------------------------------------------------------- /test57_和为s的数字.py: -------------------------------------------------------------------------------- 1 | # 面试题57 和为s的数字 2 | ''' 3 | 求和为s的数字,是设置两个指针,第一个指向头,第二个指向尾 4 | 当两者之和大于tsum时,缩小第二个指针,否则增大第二个指针。 5 | 6 | 在求和为s的序列的时候,同样是设置两个指针,当之间的和小于tsum时,增加第二个指针。 7 | 当值大于tsum时,继续减少第一个指针,直至值与tsum相等。 8 | ''' 9 | 10 | class Solution: 11 | def FindNumbersWithSum(self, array, tsum): 12 | # write code here 13 | if not array: 14 | return [] 15 | left, right = 0, len(array) - 1 16 | while left<=right: 17 | temp = array[left] + array[right] 18 | if temp == tsum: 19 | return [array[left],array[right]] 20 | elif temp < tsum: 21 | left += 1 22 | else: 23 | right -= 1 24 | return [] 25 | 26 | class Solution2: 27 | def FindContinuousSequence(self, tsum): 28 | # write code here 29 | if tsum <= 0: 30 | return [] 31 | if tsum < 3: 32 | return [] 33 | small, big = 1, 2 34 | mid = (1 + tsum) >> 1 # 这步是为了循环停止,让small都大于中间的数了,怎么相加都会大于tsum 35 | curSum = small + big 36 | result = [] 37 | while small < mid: 38 | if curSum == tsum: 39 | result.append(list(range(small, big + 1))) 40 | # 这一步很巧妙,当大于tsum时,减少small的值 41 | # 这里需要用循环,而不是if 42 | while curSum > tsum and small < mid: 43 | curSum -= small 44 | small += 1 45 | if curSum == tsum: 46 | result.append(list(range(small, big + 1))) 47 | big += 1 48 | curSum += big 49 | return result 50 | 51 | # array = [1,2,4,7,11,15] 52 | # a = Solution() 53 | # print(a.FindNumbersWithSum(array,100)) 54 | a = Solution2() 55 | print(a.FindContinuousSequence(3)) 56 | -------------------------------------------------------------------------------- /test40_最小的k个数.py: -------------------------------------------------------------------------------- 1 | # 面试题40 最小的k个数 2 | ''' 3 | 第一种方法是基于快排的思想,因为pivot的划分,基于此划分,又可以处理左边或右边,直至pivot==k-1 4 | 第二种方法是不改变原数组,利用容器,当容器中的最大元素大于数组的值时,进行替换。 5 | 这里的容器可以是二叉树,最大堆,或者红黑树等。 6 | ''' 7 | 8 | class Solution: 9 | def GetLeastNumbers_Solution(self, tinput, k): 10 | if not tinput or len(tinput)k: 15 | pivot = self.Partition(tinput,0,pivot-1) 16 | else: 17 | pivot = self.Partition(tinput,pivot+1,len(tinput)-1) 18 | return sorted(tinput[:k]) 19 | 20 | def Partition(self,numbers,low,high): 21 | key = numbers[low] 22 | while low < high: 23 | while low < high and numbers[high] >= key: 24 | high -= 1 25 | numbers[low],numbers[high] = numbers[high],numbers[low] 26 | while low < high and numbers[low] <= key: 27 | low += 1 28 | numbers[low],numbers[high] = numbers[high],numbers[low] 29 | return low 30 | 31 | class Solution_1: 32 | def GetLeastNumbers_Solution(self, tinput, k): 33 | if not tinput or len(tinput)= 0 : 29 | if lis[indexOfOriginal] == ' ': 30 | lis_rep[indexOfNew] = '0' 31 | lis_rep[indexOfNew-1] = '2' 32 | lis_rep[indexOfNew - 2] = '%' 33 | indexOfNew -= 3 34 | else: 35 | lis_rep[indexOfNew] = lis[indexOfOriginal] 36 | indexOfNew -= 1 37 | indexOfOriginal -= 1 38 | return ''.join(map(str,lis_rep)) 39 | 40 | #利用list的append,但产生了O(n)的空间复杂度,时间复杂度为O(n) 41 | #其中,如果append超过了list的长度,会扩容,产生一个移动损耗 42 | def ReplaceBlank1(s1): 43 | if not isinstance(s1,str): 44 | print('非字符串') 45 | return 46 | lis = list(s1) 47 | lis_rep = [] 48 | for i in lis: 49 | if i == ' ': 50 | lis_rep.extend(list('%20')) 51 | else: 52 | lis_rep.append(i) 53 | return ''.join(map(str,lis_rep)) 54 | 55 | 56 | if __name__ == '__main__': 57 | s1 = ' We are happy ' 58 | s2 = None 59 | s3 = ' ' 60 | print(ReplaceBlank(s3)) 61 | print(ReplaceBlank1(s3)) 62 | -------------------------------------------------------------------------------- /test26_树的子结构.py: -------------------------------------------------------------------------------- 1 | # 面试题26 树的子结构 2 | ''' 3 | 树中的指针操作确实比链表的复杂挺多 4 | 虽然这道题的解题思路不难,但很考验编程能力 5 | 6 | ''' 7 | 8 | class TreeNode: 9 | def __init__(self, data=None, left=None, right=None): 10 | self.data = data 11 | self.left = left 12 | self.right = right 13 | 14 | 15 | def HasSubtree(pRoot1, pRoot2): 16 | if not isinstance(pRoot1, TreeNode) or not isinstance(pRoot2, TreeNode): 17 | return False 18 | result = False 19 | if pRoot1 and pRoot2: 20 | if pRoot1.data == pRoot2.data: 21 | result = DoesTree1HaveTree2(pRoot1, pRoot2) 22 | if not result: 23 | result = HasSubtree(pRoot1.left, pRoot2) 24 | if not result: 25 | result = HasSubtree(pRoot1.right, pRoot2) 26 | return result 27 | 28 | 29 | def DoesTree1HaveTree2(pRoot1, pRoot2): 30 | # 首先要明确的,如果从HasSubtree进入此函数,两指针都不会为空 31 | # 所以这里的空指针判断是自身迭代的结束条件 32 | # 当pRoot2为空的时候,说明遍历到了原来pRoot2的末尾,也就是上面的节点值都相等 33 | if not pRoot2: 34 | return True 35 | # 当pRoot2不空,而pRoot1为空,说明不相等 36 | if not pRoot1: 37 | return False 38 | if pRoot1.data != pRoot2.data: 39 | return False 40 | # 进行到此步,说明两节点的data是相等的,那就得比较左右子树 41 | return DoesTree1HaveTree2(pRoot1.left, pRoot2.left) and DoesTree1HaveTree2(pRoot1.right, pRoot2.right) 42 | 43 | 44 | if __name__ == '__main__': 45 | # 树1 46 | node1 = TreeNode(8) 47 | node2 = TreeNode(8) 48 | node3 = TreeNode(7) 49 | node4 = TreeNode(9) 50 | node5 = TreeNode(2) 51 | node6 = TreeNode(4) 52 | node7 = TreeNode(7) 53 | 54 | node1.left, node1.right = node2, node3 55 | node2.left, node2.right = node4, node5 56 | node5.left, node5.right = node6, node7 57 | 58 | # 树2 59 | node8 = TreeNode(8) 60 | node9 = TreeNode(9) 61 | node10 = TreeNode(2) 62 | 63 | node8.left, node8.right = node9, node10 64 | 65 | print(HasSubtree(node1, node8)) 66 | -------------------------------------------------------------------------------- /test22_链表中倒数第k个节点.py: -------------------------------------------------------------------------------- 1 | # 面试题22 链表中倒数第k个节点 2 | ''' 3 | 寻找链表中倒数第k个节点,可以设置2个指针,让其中一个指针先走k-1步, 4 | 然后两个指针同时移动。当先走的指针到达了链尾的时候,慢走的指针就是指向倒数第k个节点。 5 | 6 | 当要寻找链表中的中间节点,同样是设置两个指针,同时从头节点出发,一个指针每次移动一步,另外一个指针每次移动两步。 7 | 这样移动慢的指针就会指向中间节点。 8 | 9 | 当用一个指针遍历链表不能解决问题的时候,可以尝试用两个指针来遍历链表。可以让其中一个指针遍历的速度快些。 10 | 11 | ''' 12 | 13 | class ListNode: 14 | def __init__(self,data=None,next=None): 15 | self.data = data 16 | self.next = next 17 | 18 | def FindKthToTail(Node,k): 19 | if not isinstance(Node,ListNode) or not Node: 20 | return 21 | if not isinstance(k,int) or k<= 0: 22 | return 23 | pAhead = Node 24 | pBhead = pAhead 25 | i = 0 26 | while i < k-1 and pBhead != None: 27 | pBhead = pBhead.next 28 | i += 1 29 | if pBhead == None:# 当k值过大时,pBhead会指向None 30 | return 31 | while pBhead.next != None: 32 | pAhead = pAhead.next 33 | pBhead = pBhead.next 34 | print(pAhead.data) 35 | 36 | def List_Print(Node): 37 | while Node != None: 38 | print(Node.data,end=' ') 39 | Node = Node.next 40 | print() 41 | 42 | def FindListMiddle(Node): 43 | if not isinstance(Node,ListNode) or not Node: 44 | return 45 | pAhead = Node 46 | pBhead = pAhead 47 | while pBhead and pBhead.next != None:# 当pBhead不指向空且下一个节点不为空的时候 48 | #pAhead每次走一步,pBhead每次走两步 49 | pAhead = pAhead.next 50 | pBhead = pBhead.next.next 51 | print(pAhead.data) 52 | 53 | 54 | if __name__ == '__main__': 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 | 68 | List_Print(node1) 69 | 70 | FindKthToTail(node1,2) 71 | 72 | FindListMiddle(node1) 73 | -------------------------------------------------------------------------------- /test23_链表中环的入口节点.py: -------------------------------------------------------------------------------- 1 | # 面试题23 链表中环的入口节点 2 | ''' 3 | 可以将该问题分解成3个小问题来解决 4 | 1.确定链表中是否包含环 5 | 2.确定环中结点数目 6 | 3.寻找环的入口 7 | 8 | https://www.cnblogs.com/fankongkong/p/7007869.html 9 | 这篇文章介绍的方法类似于书上,但比书上的节点,其仅需要2步: 10 | 1.两指针相遇确定存在环 11 | 2.环内指针和指向头结点的指针同时移动,当两者相遇时,即是环的入口(不需要再次计算环内节点的数目) 12 | 原因是当slow和fast第一次相遇的时候,在环内的位置是x-y,所以当fast再次走y步的时候,刚好到达环内的入口。 13 | ''' 14 | 15 | class ListNode: 16 | def __init__(self,data=None,next=None): 17 | self.data = data 18 | self.next = next 19 | 20 | def MeetingNode(Node): 21 | if not isinstance(Node,ListNode) or not Node: 22 | return None 23 | pSlow = Node 24 | pFast = pSlow.next 25 | while pFast and pFast.next != None and pFast != pSlow: 26 | pSlow = pSlow.next 27 | pFast = pFast.next.next 28 | if not pFast or not pFast.next: 29 | return None 30 | 31 | return pFast 32 | 33 | def EntryNodeOfLoop(Node): 34 | if not isinstance(Node,ListNode) or not Node: 35 | return None 36 | meetingNode = MeetingNode(Node) 37 | if not meetingNode: 38 | return None 39 | #统计环内节点数 40 | nodesInLoop = 1 41 | pNode1 = meetingNode 42 | while pNode1.next != meetingNode: 43 | pNode1 = pNode1.next 44 | nodesInLoop += 1 45 | # 开始寻找环的入口 46 | # 先让pNode2移动nodesInLoop次,然后pNode1和pNode2同时移动 47 | # 当两个指针相遇的时候,即是环的入口节点 48 | pNode2 = Node 49 | for i in range(nodesInLoop): 50 | pNode2 = pNode2.next 51 | pNode1 = Node 52 | while pNode1 != pNode2: 53 | pNode1 = pNode1.next 54 | pNode2 = pNode2.next 55 | return pNode1 56 | 57 | if __name__ == '__main__': 58 | node1 = ListNode(1) 59 | node2 = ListNode(2) 60 | node3 = ListNode(3) 61 | node4 = ListNode(4) 62 | node5 = ListNode(5) 63 | node6 = ListNode(6) 64 | 65 | node1.next = node2 66 | node2.next = node3 67 | node3.next = node4 68 | node4.next = node5 69 | node5.next = node6 70 | node6.next = node2 71 | 72 | a = EntryNodeOfLoop(node1) 73 | print(a.data) 74 | 75 | -------------------------------------------------------------------------------- /test17_打印从1到最大的n位数.py: -------------------------------------------------------------------------------- 1 | # 面试题17 打印从1到最大的n位数 2 | ''' 3 | 此题目用字符串来解决大数字的问题,模拟加法的过程。 4 | ''' 5 | 6 | def Print1ToMaxOfNDigits(n): 7 | if n <= 0: 8 | return 9 | number = ['0'] * n 10 | while not Increment(number): 11 | PrintNumber(number) 12 | 13 | def Increment(number): 14 | isOverflow = False # 表示最大n位加一后有无溢出 15 | nTakeOver = 0 # 进位标志符 16 | nLength = len(number) 17 | for i in range(nLength-1,-1,-1):# 从个位开始计算,至到n位,所以是倒序 18 | nSum = int(number[i]) + nTakeOver # 表示第n位的值,如果无进位,则nTakeOver=0,如果进位了,则nTakeOver=1 19 | if i == nLength - 1:#表示个位 20 | nSum += 1 21 | 22 | if nSum >= 10:#判断是否进位 23 | if i == 0: # 当i==0时,说明已经溢出 24 | isOverflow = True 25 | else:# 若不是0,说明仍然处于n位之中 26 | nSum -= 10 27 | nTakeOver = 1#进位标志符置1 28 | number[i] = str(nSum) 29 | else:# 未发生进位 30 | number[i] = str(nSum) 31 | break 32 | return isOverflow 33 | 34 | def PrintNumber(number): 35 | isBeginning0 = True # 表示非零位的开始 36 | nLength = len(number) 37 | 38 | for i in range(nLength): 39 | if isBeginning0 and number[i] != '0':# 当number之前为0的话,isBeginning0一直为0,知道遇到第一个非零位 40 | isBeginning0 = False 41 | if not isBeginning0: 42 | print('%c' % number[i],end='') 43 | print('') 44 | 45 | def Print1ToMaxOfNDigits_2(n): 46 | if n<=0: 47 | return 48 | number = ['0'] * n 49 | for i in range(10): 50 | number[0] = str(i) # 设置最高位 51 | Print1ToMaxOfNDigitsRecursively(number,n,0) 52 | 53 | # index表示传入时对应是哪一位 54 | def Print1ToMaxOfNDigitsRecursively(number,length,index): 55 | if index == length - 1:# length-1表示最后一个,即个位 56 | PrintNumber(number) 57 | return 58 | for i in range(10): 59 | number[index + 1] = str(i)# index+1 下一位 60 | Print1ToMaxOfNDigitsRecursively(number,length,index+1) 61 | 62 | if __name__ == '__main__': 63 | # Print1ToMaxOfNDigits(2) 64 | Print1ToMaxOfNDigits_2(3) 65 | -------------------------------------------------------------------------------- /test13_机器人的运动范围.py: -------------------------------------------------------------------------------- 1 | # 面试题13 机器人的运动范围 2 | ''' 3 | 判断位数之和有两种方法: 4 | 1.转化为字符串 5 | 2.通过循环,例如a=101,每次对a进行%10,循环判断条件是a!=0 6 | https://www.cnblogs.com/54Leo/p/6133270.html 7 | https://github.com/Jack-Lee-Hiter/AlgorithmsByPython/blob/master/Target%20Offer/%E6%9C%BA%E5%99%A8%E4%BA%BA%E7%9A%84%E8%BF%90%E5%8A%A8%E8%8C%83%E5%9B%B4.py 8 | 和面试题12一样使用回溯法,进行迭代,套路很相近 9 | 10 | 另外,用visited = [[False] * rows ]* cols这种方式创建二维数组,是个坑,虽然行,但都是array的引用。 11 | 应该用test = [[0 for i in range(m)] for j in range(n)]这种方式。 12 | https://www.cnblogs.com/PyLearn/archive/2017/11/06/7795552.html 13 | 14 | ''' 15 | 16 | class Solution: 17 | def movingCount(self,threshold,rows,cols): 18 | if threshold < 0 or rows<= 0 or cols <= 0: 19 | return 0 20 | visited = [[0 for i in range(rows)]for j in range(cols)] 21 | count = self.movingCountCore(threshold,rows,cols,0,0,visited) 22 | return count 23 | 24 | def movingCountCore(self,threshold,rows,cols,i,j,visited): 25 | count = 0 26 | if self.check(threshold,rows,cols,i,j,visited): 27 | a = visited[1][0] 28 | visited[i][j] = True 29 | count = 1 + self.movingCountCore(threshold,rows,cols,i-1,j,visited) \ 30 | + self.movingCountCore(threshold,rows,cols,i+1,j,visited) \ 31 | + self.movingCountCore(threshold,rows,cols,i,j-1,visited) \ 32 | + self.movingCountCore(threshold,rows,cols,i,j+1,visited) 33 | return count 34 | 35 | def check(self, threshold, rows, cols, i, j, visited): 36 | if 0<=i 1: 43 | return False 44 | return True 45 | # 书上是下面一行,但是如果有一个结点不平衡的话,根节点肯定不平衡 46 | # return self.IsBalanced(pRoot.left) and self.IsBalanced(pRoot.right) 47 | 48 | def IsBalanced_Solution2(self, pRoot): 49 | depth = [0] 50 | return self.IsBalanced_Solution2_core(pRoot,depth) 51 | 52 | def IsBalanced_Solution2_core(self, pRoot,depth): 53 | if not pRoot: 54 | depth[0] = 0 55 | return True 56 | left = [0] 57 | right = [0] 58 | if self.IsBalanced_Solution2_core(pRoot.left,left) and self.IsBalanced_Solution2_core(pRoot.right,right): 59 | dif = left[0] - right[0] 60 | if abs(dif)<=1: 61 | depth[0] = max(left[0],right[0])+1 62 | return True 63 | return False 64 | 65 | 66 | node1 = TreeNode(1) 67 | node2 = TreeNode(2) 68 | node3 = TreeNode(3) 69 | node4 = TreeNode(4) 70 | node5 = TreeNode(5) 71 | node6 = TreeNode(6) 72 | node7 = TreeNode(7) 73 | 74 | node1.left,node1.right = node2,node3 75 | node2.left,node2.right = node4,node5 76 | node3.right = node6 77 | node5.left = node7 78 | 79 | a = Solution() 80 | # print(a.TreeDepth(node1)) 81 | print(a.IsBalanced_Solution(node1)) 82 | -------------------------------------------------------------------------------- /test38_字符串的排列.py: -------------------------------------------------------------------------------- 1 | # 面试题38 字符串的排列 2 | ''' 3 | 如果面试题是按照一定要求摆放若干个数字,则可以先求出这些数字的全排列,然后一一判断每个排列是不是满足题目给定的要求。 4 | 5 | 全排列算法,很常见,利用递归 6 | 固定第i个数组,然后处理后面n-i个数组 7 | 这里需要交换2个位置,但最后会交换回来。 8 | https://blog.csdn.net/summerxiachen/article/details/60579623 9 | 10 | 扩展提是全组合算法 11 | 这里需要用到一个缓存list 12 | 首先将元素压入vec,然后处理lis[1:]后面的元素 13 | ''' 14 | 15 | def Permutation(lis): 16 | if not isinstance(lis,list): 17 | return 18 | Permutation_(lis,0) 19 | 20 | def Permutation_(lis,n): 21 | if n >= len(lis): 22 | print(' '.join(lis)) 23 | return 24 | for i in range(n,len(lis)): 25 | if check(lis,n,i): 26 | lis[i], lis[n] = lis[n], lis[i] # 交换两个位置 27 | Permutation_(lis,n+1)# 注意,这里是n+1 28 | lis[i], lis[n] = lis[n], lis[i] # 交换回来 29 | 30 | def check(lis,n,i): 31 | # n,i是指这两个元素需要交换 32 | # 当lis[n]==lis[i]说明两个元素相同,不需要交换,不然会有重复 33 | if i > n:# 不用等号是允许自身与自身交换 34 | # 当不是与自身交换的时候,判断是否会有重复 35 | for j in range(n,i): 36 | if lis[j] == lis[i]: 37 | return False 38 | return True 39 | 40 | def Combination(lis): 41 | if not isinstance(lis,list): 42 | return 43 | vec = [] 44 | for i in range(1,len(lis)+1): 45 | Combination_(lis,vec,i) 46 | 47 | def Combination_(lis,vec,m): 48 | if m == len(vec): 49 | print(' '.join(vec)) 50 | return 51 | if lis: 52 | vec.append(lis[0]) 53 | Combination_(lis[1:],vec,m) 54 | vec.pop() 55 | Combination_(lis[1:],vec,m) 56 | 57 | # n个皇后问题 58 | def queens(n): 59 | lis = [x for x in range(n)] 60 | Permutation_n(lis,0) 61 | 62 | def Permutation_n(lis,n): 63 | if n >= len(lis): 64 | if SatisfyQueenRequirements(lis): 65 | print(' '.join(map(str,lis))) 66 | return 67 | for i in range(n,len(lis)): 68 | lis[i],lis[n] = lis[n],lis[i] 69 | Permutation_n(lis,n+1) 70 | lis[i],lis[n] = lis[n],lis[i] 71 | 72 | def SatisfyQueenRequirements(lis): 73 | Flag = True 74 | for i in range(0,len(lis)-1): 75 | for j in range(i+1,len(lis)): 76 | if abs(i - j) == abs(lis[i] - lis[j]): 77 | Flag = False 78 | break 79 | if not Flag: 80 | break 81 | return Flag 82 | 83 | # lis = list('abc') 84 | # Permutation(lis) 85 | # Combination(lis) 86 | queens(8) 87 | -------------------------------------------------------------------------------- /test3_数组中重复的数字.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 面试题3-1和3-2 数组中重复的数字 3 | 题目3-1是不知道有无重复且重复多少次,可以利用的方法有: 4 | 1.排序后再扫描,此时排序的时间复杂度为O(nlogn),扫描的复杂度为O(n) 5 | 2.利用哈希表,创建一个长度为n的list,往里面按下标填数字,当发现此位置已经被填了,说明此数字重复 6 | 3.利用书上的方法:将其两两交换,时间复杂度为O(n) 7 | 题目3-2是已经知道至少有一个数字是重复的,且不允许修改原来的list,与上题的前提不同。 8 | 1.创建一个长度为n的list,类似上题的方法2 9 | 2.利用类似于二分查抄的方法(书上的方法) 10 | 这里是将1到n-1的数字对半分,而不是针对原来的list,然后统计对半的数字在原来的list中重复的次数。 11 | 此方法能找到重复的数字,但不能保证找出所有重复的数字 12 | 时间复杂度为O(nlogn) 13 | ''' 14 | 15 | 16 | 17 | # 面试题3-1 数组中重复的数字 18 | def duplicate(lis): 19 | #判断边界条件 20 | if lis == None or len(lis) == 0: 21 | print('数组为空') 22 | return False 23 | # 判断数字在0到n-1的范围内 24 | Len = len(lis) 25 | for i in lis: 26 | if i < 0 or i> Len-1: 27 | print('值%d不在范围内' % i) 28 | return False 29 | Flag = True#标志位,表示找到第一个重复的数字 30 | for i in range(Len): 31 | while i != lis[i]: 32 | m = lis[i] 33 | if m == lis[m]: 34 | Flag = False 35 | break 36 | lis[i],lis[m] = lis[m],lis[i] 37 | if Flag == False: 38 | break 39 | if Flag == True: 40 | print('未找到重复数字') 41 | return False 42 | else: 43 | return lis[i] 44 | 45 | # 面试题3-2 46 | def getDupliation(lis): 47 | #判断边界条件 48 | if lis == None or len(lis) == 0: 49 | print('数组为空') 50 | return False 51 | # 判断数字在1到n-1的范围内 52 | Len = len(lis) 53 | for i in lis: 54 | if i < 1 or i> Len-1: 55 | print('值%d不在范围内' % i) 56 | return False 57 | 58 | start,end = 1,Len-1 59 | while end >= start: 60 | middle = int(start + 0.5*(end-start)) 61 | count = countRange(lis,start,middle) 62 | #如果只有1个数字时,统计出来的count多于1个,则说明这个数字是重复的 63 | if end == start: 64 | if count > 1: 65 | return start 66 | else: 67 | print('无重复') 68 | break 69 | 70 | if count > (middle - start + 1):#若count值大于start到middle的数量,则在其中继续二分查找 71 | end = middle 72 | else: 73 | start = middle + 1 74 | return False 75 | 76 | def countRange(lis,start,end): 77 | count = 0 78 | for i in lis: 79 | if i>=start and i<=end: 80 | count += 1 81 | return count 82 | 83 | if __name__ == '__main__': 84 | lis = [] 85 | print(getDupliation(lis)) 86 | -------------------------------------------------------------------------------- /test56_数组中数字出现的次数.py: -------------------------------------------------------------------------------- 1 | # 面试题56 数组中数字出现的次数 2 | ''' 3 | 这道题很难想到思路,但方法很巧妙,利用了二进制 4 | 首先,相同的数字异或为0,可以考虑将一个数组分成两个子数组,这两个子数组里面,分别包含一个出现1次的数字和若干个成对出现的数字。 5 | 这样通过异或,就得获得只出现1次的数字。 6 | 7 | 这道题要求了时间复杂度为O(n),空间复杂度为O(1),如果不这么要求的话,可以利用排序或者哈希表。 8 | ''' 9 | 10 | class Solution: 11 | # 返回[a,b] 其中ab是出现一次的两个数字 12 | def FindNumsAppearOnce(self, array): 13 | # write code here 14 | if not array or len(array)<2: 15 | return 16 | 17 | resultExclusiveOR = 0 18 | # 每一位都进行异或,当相同的数字异或,会变成0,所以最后的结果是两个只出现1次的数字异或的结果 19 | for i in array: 20 | resultExclusiveOR ^= i 21 | 22 | # 异或的结果为1,说明这2个数字的二进制的这一位不相同,可以根据这一位的不同,划分成2个子数组 23 | indexOf1 = self.FindFirstBitIs1(resultExclusiveOR) 24 | 25 | # 将2个子数组分别各自异或,因为每个子数组现在的组成是1个出现1次的数字和若干对成对出现的数字 26 | # 成对出现的的数字异或的结果为0 27 | # 所以最后的结果就是那个只出现1次的数字 28 | num1, num2 = 0, 0 29 | for i in array: 30 | if self.IsBit1(i,indexOf1): 31 | num1 ^= i 32 | else: 33 | num2 ^= i 34 | return sorted([num1,num2]) 35 | 36 | def FindFirstBitIs1(self,num): 37 | indexBit = 0 38 | # 后面的这个判断条件是为了防止数组中没有出现的数字 39 | # 但python中int无上限,所以我就认为设置了最大是10位 40 | while num & 1 == 0 and indexBit < 10: 41 | num = num >> 1 42 | indexBit += 1 43 | return indexBit 44 | 45 | def IsBit1(self,num,indexBit): 46 | num = num >> indexBit 47 | return num & 1 48 | 49 | ''' 50 | 同样是利用位运算的思想。把每个数字的每个位都对应相加,如果一个数字出现3次,那么它的二进制表示的每一位也就出现3次, 51 | 那么每一位都能被3整除。 52 | 所以当再加上一个不重复的数字时,就能得到这个结果 53 | ''' 54 | class Solution2: 55 | def FindNumberAppearingOnec(self,array): 56 | if not array: 57 | return 58 | bitSum = [0]*32 59 | for i in array: 60 | bitMask = 1 61 | # 将每一位的二进制都分别按位相加 62 | for j in range(31,-1,-1): 63 | bit = i & bitMask 64 | if bit != 0: 65 | bitSum[j] += 1 66 | bitMask = bitMask << 1 67 | 68 | # 当出现一位不能被3整除的时候,说明只出现1次的数字在这个二进制中也占1位。 69 | result = 0 70 | for i in bitSum: 71 | result = result<<1 72 | result += i % 3 73 | print(result) 74 | 75 | # array = [2,4,3,6,3,2,5,5] 76 | # a = Solution() 77 | # print(a.FindNumsAppearOnce(array)) 78 | array = [1,1,1,2,2,2,3,4,4,4] 79 | a = Solution2() 80 | a.FindNumberAppearingOnec(array) 81 | -------------------------------------------------------------------------------- /test60_n个骰子的点数.py: -------------------------------------------------------------------------------- 1 | # 面试题60 n个骰子的点数 2 | ''' 3 | 考察了数学建模的的能力,这种题很常见,但我自己的建模能力有点差。 4 | ''' 5 | 6 | class Solution: 7 | ''' 8 | 思路是固定第一个骰子,计算后面n-1个骰子出现的次数。这就是一个递归的想法。 9 | 程序可能有点绕,但思路是这样的。 10 | ''' 11 | def __init__(self): 12 | self.pProbabilities = {}# n个骰子相加之和出现的次数 13 | self.g_maxVal = 6 14 | 15 | def PrintProbability(self,number): 16 | if number < 1: 17 | return 18 | self.Probability(number) 19 | total = pow(self.g_maxVal, number) 20 | for i in self.pProbabilities.items(): 21 | print('%d %.2f' % (i[0], i[1] / total)) 22 | 23 | def Probability(self,number): 24 | for i in range(1, self.g_maxVal + 1): 25 | # 第一个骰子的值,从1循环到6 26 | self.Probability2(number, i) 27 | 28 | def Probability2(self,current,tsum): 29 | if current == 1: 30 | # 当处理完前面n—1个骰子,剩下1个骰子的时候,次数加一 31 | self.pProbabilities[tsum] = self.pProbabilities.get(tsum, 0) + 1 32 | else: 33 | for i in range(1, self.g_maxVal + 1): 34 | # 处理后面n-1个骰子 35 | self.Probability2(current - 1, i + tsum) 36 | 37 | class Solution2: 38 | def __init__(self): 39 | self.g_maxValue = 6 40 | 41 | def PrintProbability(self,number): 42 | if number < 1: 43 | return 44 | pProbabilities = [[0] * (self.g_maxValue * number + 1), [0] * (self.g_maxValue * number + 1)] 45 | flag = 0 46 | for i in range(1, self.g_maxValue + 1): 47 | pProbabilities[flag][i] = 1 48 | 49 | # 这里k表示第几个骰子 50 | for k in range(2, number + 1): 51 | for i in range(k): 52 | pProbabilities[1 - flag][i] = 0 53 | # self.g_maxValue*k表示k个骰子最大点数之和。这里i表示点数之和 54 | for i in range(k, self.g_maxValue * k + 1): 55 | pProbabilities[1 - flag][i] = 0 56 | j = 1 57 | while j <= i and j <= self.g_maxValue: 58 | # 一个数组的第n项等于另一个数组的第n-1,n-2,n-3,n-4,n-5,n-6项之和 59 | pProbabilities[1 - flag][i] += pProbabilities[flag][i - j] 60 | j += 1 61 | flag = 1-flag 62 | 63 | total = pow(self.g_maxValue, number) 64 | for i in range(number,self.g_maxValue*number+1): 65 | ratio = pProbabilities[flag][i]/total 66 | print('%d %.2f' % (i, ratio)) 67 | 68 | # a = Solution() 69 | # a.PrintProbability(2) 70 | # print() 71 | b = Solution2() 72 | b.PrintProbability(2) 73 | -------------------------------------------------------------------------------- /test35_复杂链表的复制.py: -------------------------------------------------------------------------------- 1 | # 面试题35 复杂链表的复制 2 | ''' 3 | 这道题有三种方法: 4 | 方法1:暴力解法,每次都遍历一遍,时间复杂度是O(n^2) 5 | 方法2:先将链表复制一遍,并将对应的节点存入hash表中,最后在构造pSibling,此方法的时间复杂度是O(n),空间复杂度是O(n) 6 | 方法3:将需要复制的节点插入到原节点后面,然后构造pSibling,最后断开成2个链表,此时的时间复杂度是O(n) 7 | 8 | 用图形化的方式表示出来,会有助于我们理清思路。 9 | ''' 10 | 11 | class ListNode: 12 | def __init__(self,data=None,pNext=None,pSibling=None): 13 | self.data=data 14 | self.pNext = pNext 15 | self.pSibling = pSibling 16 | 17 | def CloneNodes(pHead): 18 | p = pHead 19 | while p: 20 | temp = ListNode(p.data,p.pNext) 21 | p.pNext = temp 22 | p = temp.pNext 23 | 24 | def ConnectSiblingNodes(pHead): 25 | p = pHead 26 | while p: 27 | if p.pSibling: 28 | p_Clone = p.pNext 29 | p_Clone.pSibling = p.pSibling.pNext 30 | p = p.pNext.pNext 31 | 32 | def ReconnectNodes(pHead): 33 | p1 = pHead 34 | p2 = pHead.pNext 35 | Clone_Node_Head =pHead.pNext 36 | while p1 and p2: 37 | p1.pNext = p2.pNext 38 | if not p2.pNext: 39 | break 40 | p2.pNext = p2.pNext.pNext 41 | p1 = p1.pNext 42 | p2 = p2.pNext 43 | return Clone_Node_Head 44 | 45 | def Clone(pHead): 46 | if not isinstance(pHead,ListNode): 47 | return 48 | CloneNodes(pHead) 49 | ConnectSiblingNodes(pHead) 50 | return ReconnectNodes(pHead) 51 | 52 | # 方法2 利用哈希表 53 | def Clone_Hash(pHead): 54 | if not isinstance(pHead,ListNode): 55 | return 56 | p1 = pHead 57 | p2 = ListNode(p1.data) 58 | Hash = {p1:p2} 59 | p1 = p1.pNext 60 | Clone_Head = p2 61 | # 首先复制一个一样的链表,除了pSibling,并放入Hash表中 62 | while p1: 63 | p2.pNext = ListNode(p1.data) 64 | Hash[p1] = p2.pNext 65 | p1 = p1.pNext 66 | p2 = p2.pNext 67 | # 构建p2的pSibling 68 | p1,p2 = pHead,Clone_Head 69 | while p1: 70 | if p1.pSibling: 71 | p2.pSibling = Hash[p1.pSibling] 72 | p1 = p1.pNext 73 | p2 = p2.pNext 74 | return Clone_Head 75 | 76 | def Print(pHead): 77 | if not isinstance(pHead,ListNode): 78 | return 79 | p = pHead 80 | while p: 81 | print(p.data,end=' ') 82 | p = p.pNext 83 | print() 84 | 85 | 86 | node5 = ListNode('E') 87 | node4 = ListNode('D',node5) 88 | node3 = ListNode('C',node4) 89 | node2 = ListNode('B',node3) 90 | node1 = ListNode('A',node2) 91 | 92 | node1.pSibling = node3 93 | node2.pSibling = node5 94 | node4.pSibling = node2 95 | 96 | 97 | # p1 = Clone(node1) 98 | # Print(p1) 99 | p2 = Clone_Hash(node1) 100 | print(p1.pSibling.data) 101 | -------------------------------------------------------------------------------- /test68_树中两个节点的最低公共祖先.py: -------------------------------------------------------------------------------- 1 | # 面试题68 树中两个节点的最低公共祖先 2 | ''' 3 | 寻找树中两个节点的最低公共祖先,是一组题目。 4 | 当树是二叉搜索树的时候,最低祖先在两个节点的值的中间。 5 | 当节点值都大于两个节点的值时,最低公共祖先在该节点的左子树上 6 | 当节点值都小于两个节点的值时,最低公共祖先在该节点的右子树上 7 | 当树是普通的树的时候,而且有父母指针,可以将其转化成“两个链表中的以一个公共节点” 8 | 9 | 当树是普通的树的时候,而且没有父母指针。可以借助辅助空间。 10 | 首先借助辅助空间,存储找到两个节点的路径,得到两个list 11 | 然后将这两个list转化成“两个链表中的以一个公共节点” 12 | 这样就能得到最低公共祖先了。 13 | ''' 14 | 15 | class TreeNode: 16 | def __init__(self,data,left=None,right=None,parent = None): 17 | self.data = data 18 | self.left = left 19 | self.right = right 20 | self.parent = parent 21 | 22 | class Solution: 23 | def __init__(self,phead): 24 | self.phead = phead 25 | 26 | # 当树为二叉搜索树的情况 27 | def GetLowestNodeParent(self,node1,node2): 28 | if not node1 or not node2: 29 | return 30 | p = self.phead 31 | # 保证p1指向值小的节点,而p2指向值大的节点 32 | p1, p2 = node1, node2 33 | if p1.data > p2.data: 34 | p1, p2 = node2, node1 35 | 36 | while p.data > p2.data or p.data < p1.data: 37 | if p.data > p2.data: 38 | p = p.left 39 | else: 40 | p = p.right 41 | return p 42 | 43 | # 当树是普通的树,且有父母指针的时候 44 | def GetLowestNodeParent2(self,node1,node2): 45 | if not node1 or not node2: 46 | return 47 | p1,p2 = node1,node2 48 | while p1 != p2: 49 | p1 = p1.parent if p1.parent != None else node2 50 | p2 = p2.parent if p2.parent != None else node1 51 | return p1 52 | 53 | node1 = TreeNode(10) 54 | node2 = TreeNode(5) 55 | node3 = TreeNode(15) 56 | node4 = TreeNode(3) 57 | node5 = TreeNode(7) 58 | node6 = TreeNode(13) 59 | node7 = TreeNode(17) 60 | node8 = TreeNode(1) 61 | node9 = TreeNode(4) 62 | node10 = TreeNode(6) 63 | node11 = TreeNode(9) 64 | node12 = TreeNode(11) 65 | node13 = TreeNode(14) 66 | node14 = TreeNode(16) 67 | node15 = TreeNode(19) 68 | 69 | node1.left,node1.right = node2,node3 70 | node2.left,node2.right = node4,node5 71 | node3.left,node3.right = node6,node7 72 | node4.left,node4.right = node8,node9 73 | node5.left,node5.right = node10,node11 74 | node6.left,node6.right = node12,node13 75 | node7.left,node7.right = node14,node15 76 | 77 | # 当树是二叉树 78 | a = Solution(node1) 79 | # print(a.GetLowestNodeParent(node1,node1).data) 80 | 81 | # 当树是普通的树,且有父母指针的时候 82 | node2.parent,node3.parent = node1,node1 83 | node4.parent,node5.parent = node2,node2 84 | node6.parent,node7.parent = node3,node3 85 | node8.parent,node9.parent = node4,node4 86 | node10.parent,node11.parent = node5,node5 87 | node12.parent,node13.parent = node6,node6 88 | node14.parent,node15.parent = node7,node7 89 | 90 | print(a.GetLowestNodeParent2(node8,node9).data) 91 | -------------------------------------------------------------------------------- /test53_在排序数组中查找数字.py: -------------------------------------------------------------------------------- 1 | # 面试题53 在排序数组中查找数字 2 | ''' 3 | 三道题目都是二分查找的变式,当涉及到元素的值和下标的关系,而且又是查找,可以考虑下二分查找 4 | ''' 5 | 6 | # 题目一:数字在排序数组中出现的次数 7 | ''' 8 | 方法1:利用二分查找,找到等于k的数字,然后向左向右顺序扫描,此时二分查找的复杂度是O(logn),顺序查找时O(n),所以总的复杂度是O(n) 9 | 方法2:同样是利用二分查找,但二分查找是找到重复数字的开始和末尾,不进行顺序扫描,所以复杂度是O(logn) 10 | ''' 11 | class Solution: 12 | def GetNumberOfK(self, data, k): 13 | if not data: 14 | return 0 15 | number = 0 16 | first = self.GetFirstK(data,k) 17 | last = self.GetLastK(data,k) 18 | if first >-1 and last > -1: 19 | number = last -first +1 20 | return number 21 | 22 | def GetFirstK(self,data,k): 23 | start, end = 0, len(data) - 1 24 | while start <= end: 25 | mid = int((start + end) / 2) 26 | if data[mid]== k: 27 | if (mid > 0 and data[mid - 1] != k) or mid == 0: 28 | return mid 29 | else: 30 | end = mid - 1 31 | elif data[mid] > k: 32 | end = mid - 1 33 | else: 34 | start = mid + 1 35 | return -1 36 | 37 | def GetLastK(self,data,k): 38 | start, end = 0, len(data) - 1 39 | while start <= end: 40 | mid = int((start + end) / 2) 41 | if data[mid]== k: 42 | if (mid < len(data) - 1 and data[mid + 1] != k) or mid == len(data) - 1: 43 | return mid 44 | else: 45 | start = mid + 1 46 | elif data[mid] > k: 47 | end = mid - 1 48 | else: 49 | start = mid + 1 50 | return -1 51 | 52 | # 题目二 0~n-1中缺失的数字 53 | class Solution2: 54 | def GetMissingNumber(self,numbers): 55 | if not numbers: 56 | return -1 57 | start, end = 0, len(numbers) - 1 58 | while start <= end: 59 | mid = int((start + end) / 2) 60 | if numbers[mid] == mid: 61 | # 此时下标相同,说明缺失的数字在后面 62 | start = mid + 1 63 | else: 64 | if (mid > 0 and numbers[mid - 1] == (mid - 1)) or mid == 0: 65 | return mid 66 | end = mid - 1 67 | if start == len(numbers):#这是只有一个元素的情况 68 | return len(numbers) - 1 69 | return -1 70 | 71 | # 题目三 数组中数值和下标相等的元素 72 | class Solution3: 73 | def GetNumberSameAsIndex(self,numbers): 74 | if not numbers: 75 | return -1 76 | start, end = 0, len(numbers) - 1 77 | while start<=end: 78 | mid = (start + end) >> 1 79 | if mid == numbers[mid]: 80 | return mid 81 | elif mid < numbers[mid]: 82 | end = mid - 1 83 | else: 84 | start = mid + 1 85 | return -1 86 | 87 | 88 | lis = [1,2,3,3,3,3,4,5] 89 | lis1 = [0,1,2,4,5,6] 90 | lis2 = [-10,-8,2] 91 | # lis= [] 92 | # a = Solution() 93 | # b = a.GetNumberOfK(lis,1) 94 | # print(b) 95 | # a = Solution2() 96 | # print(a.GetMissingNumber(lis1)) 97 | a = Solution3() 98 | print(a.GetNumberSameAsIndex(lis2)) 99 | -------------------------------------------------------------------------------- /test32_从上到下打印二叉树.py: -------------------------------------------------------------------------------- 1 | # 面试题32 从上到下打印二叉树 2 | ''' 3 | 本道面试题很有意思 4 | 首先是不分行打印,这时是图的广度优先搜索,利用队列来实现 5 | 其次是分行的打印,这时在利用队列的基础上,需要用2个变量来控制分行 6 | 最后是之字形打印,这里就是利用栈了,而不是队列,利用两个栈来表示奇数层和偶数层,分别管理 7 | 8 | ''' 9 | 10 | class TreeNode: 11 | def __init__(self, data=None, left=None, right=None): 12 | self.data = data 13 | self.left = left 14 | self.right = right 15 | 16 | class MyQueue: 17 | def __init__(self): 18 | self.Queue = [] 19 | 20 | def enqueue(self, data): 21 | self.Queue.append(data) 22 | 23 | def dequeue(self): 24 | return self.Queue.pop(0) 25 | 26 | def Print(self): 27 | print(self.Queue) 28 | 29 | def Size(self): 30 | return len(self.Queue) 31 | 32 | 33 | # 不分行从上到下打印二叉树 34 | def PrintFromTopToBottom(Node): 35 | if not isinstance(Node, TreeNode): 36 | return 37 | Queue = MyQueue() 38 | Queue.enqueue(Node) 39 | while Queue.Size(): 40 | temp = Queue.dequeue() 41 | print(temp.data, end=' ') 42 | if temp.left: 43 | Queue.enqueue(temp.left) 44 | if temp.right: 45 | Queue.enqueue(temp.right) 46 | 47 | # 分行从上到下打印二叉树 48 | def PrintFromTopToBottom_2(Node): 49 | if not isinstance(Node, TreeNode): 50 | return 51 | Queue = MyQueue() 52 | toBePrinted, nextLevel = 1, 0 # toBePrinted表示本层未打印节点的数量,nextLevel表示下层要打印节点的数量 53 | Queue.enqueue(Node) 54 | while Queue.Size(): 55 | temp = Queue.dequeue() 56 | print(temp.data,end=' ') 57 | toBePrinted -= 1 58 | if temp.left: 59 | nextLevel += 1 60 | Queue.enqueue(temp.left) 61 | if temp.right: 62 | nextLevel += 1 63 | Queue.enqueue(temp.right) 64 | if toBePrinted == 0: 65 | print() 66 | toBePrinted = nextLevel 67 | nextLevel = 0 68 | 69 | # 之字形打印二叉树 70 | # 需要利用两个栈来管理奇数层和偶数层 71 | def PrintFromTopToBottom_3(Node): 72 | if not isinstance(Node, TreeNode): 73 | return 74 | stack1,stack2 = [Node],[]# 奇数栈从左往右打印,偶数栈从右往左打印 75 | stack = [stack1,stack2] 76 | current,next = 0,1 77 | while stack1 or stack2: 78 | temp = stack[current].pop() 79 | print(temp.data,end=' ') 80 | if current == 0: 81 | #在奇数层,下一层应该从右到左压入 82 | if temp.left: 83 | stack[next].append(temp.left) 84 | if temp.right: 85 | stack[next].append(temp.right) 86 | else: 87 | if temp.right: 88 | stack[next].append(temp.right) 89 | if temp.left: 90 | stack[next].append(temp.left) 91 | if not stack[current]: 92 | # 说明该层打印完了 93 | print() 94 | current = 1 -current 95 | next = 1- next 96 | 97 | 98 | node1 = TreeNode(8) 99 | node2 = TreeNode(6) 100 | node3 = TreeNode(10) 101 | node4 = TreeNode(5) 102 | node5 = TreeNode(7) 103 | node6 = TreeNode(9) 104 | node7 = TreeNode(11) 105 | 106 | node1.left, node1.right = node2, node3 107 | node2.left, node2.right = node4, node5 108 | node3.left, node3.right = node6, node7 109 | 110 | # PrintFromTopToBottom(node1) 111 | # PrintFromTopToBottom_2(node1) 112 | PrintFromTopToBottom_3(node1) 113 | -------------------------------------------------------------------------------- /test18_删除链表的节点.py: -------------------------------------------------------------------------------- 1 | # 面试题18 删除链表的节点 2 | ''' 3 | 题目1的关键在于如何在O(1)的时间内完成删除一个结点,即将后面结点的值赋予给该节点,然后删除后面的那个节点。 4 | 题目2是删除重复节点,难点在于想到各种边界条件。 5 | 6 | ''' 7 | 8 | class Node: 9 | #单链表节点 10 | def __init__(self,data,p=None): 11 | self.data = data 12 | self.next = p 13 | def __del__(self): 14 | self.data = None 15 | self.next = None 16 | 17 | class Link_List: 18 | def __init__(self): 19 | self.head = Node(None) 20 | 21 | def create(self,data): 22 | if len(data) == 0: 23 | print('list is null') 24 | return 25 | self.head.next = Node(data[0]) 26 | p = self.head.next 27 | for i in data[1:]: 28 | p.next = Node(i) 29 | p = p.next 30 | 31 | def print(self): 32 | p = self.head.next 33 | while p != None: 34 | print(p.data,end=' ') 35 | p = p.next 36 | print('') 37 | 38 | 39 | #题目一 在O(1)时间内删除链表节点 40 | def DeleteNode(LinkList,pToBeDeleted): 41 | if not isinstance(LinkList,Link_List) or not isinstance(pToBeDeleted,Node): 42 | return 43 | if pToBeDeleted.next != None: 44 | # 若删除的不是尾节点 45 | temp = pToBeDeleted.next 46 | pToBeDeleted.data = temp.data 47 | pToBeDeleted.next = temp.next 48 | elif LinkList.head.next == pToBeDeleted: 49 | # 若链表只有一个结点,删除头结点(也就是尾节点) 50 | LinkList.head.next = None 51 | else: 52 | # 若链表很多节点,删除尾节点 53 | p = LinkList.head.next 54 | while p.next != pToBeDeleted: 55 | p = p.next 56 | p.next = None 57 | 58 | # 题目二 删除链表中重复的节点 59 | def DeleteDuplication(LinkList): 60 | if not isinstance(LinkList,Link_List): 61 | return 62 | pPreNode , pNode = LinkList.head.next,LinkList.head.next # pNode指向第一个节点 63 | while pNode.next != None: 64 | if pNode.data != pNode.next.data: 65 | pPreNode = pNode 66 | pNode = pNode.next 67 | else: 68 | # 当数据重复了 69 | temp = pNode.data 70 | while pNode and pNode.data == temp:# 循环到不重复的值或者pNode为None 71 | pNode = pNode.next 72 | if pNode == None:# 当pNode指向空 73 | # 若数据是[1,1]这种头部重复的话,则直接令头指针指向空 74 | if pPreNode == None: 75 | LinkList.head.next = None 76 | return 77 | # 若数据是[1,2,2]这种尾部重复的话,则让pPreNode.next指向空 78 | else: 79 | pPreNode.next = pNode 80 | return 81 | # 尾部重复 82 | pPreNode.next = pNode 83 | 84 | 85 | 86 | 87 | # 题目1 88 | # node1 = Node(10) 89 | # node2 = Node(11) 90 | # node3 = Node(13) 91 | # node4 = Node(15) 92 | # node1.next = node2 93 | # node2.next = node3 94 | # node3.next = node4 95 | # 96 | # node5 = Node(17) 97 | # 98 | # S = Link_List() 99 | # S.head.next = node1 100 | # S.print() 101 | # DeleteNode(S,node1) 102 | # S.print() 103 | 104 | # 题目2 105 | node1 = Node(1) 106 | node2 = Node(1) 107 | node3 = Node(3) 108 | node4 = Node(3) 109 | node5 = Node(4) 110 | node6 = Node(5) 111 | node7 = Node(5) 112 | 113 | node1.next = node2 114 | node2.next = node3 115 | node3.next = node4 116 | node4.next = node5 117 | node5.next = node6 118 | node6.next = node7 119 | 120 | S = Link_List() 121 | S.head.next = node1 122 | S.print() 123 | DeleteDuplication(S) 124 | S.print() 125 | 126 | -------------------------------------------------------------------------------- /test51_数组中的逆序对.py: -------------------------------------------------------------------------------- 1 | # 面试题51 数组中的逆序对 2 | ''' 3 | 第一种方式是简单粗暴,每次都和后面的相比,时间复杂度是O(n) 4 | 第二种方法利用了递归的思想,很有意思,但很难想到,时间复杂度是O(nlogn) 5 | ''' 6 | 7 | # 用于修改递归最大深度,否则在牛客网上跑会超时 8 | import sys 9 | sys.setrecursionlimit(1000000) 10 | class Solution: 11 | def InversePairs(self, data): 12 | if not data : 13 | return 0 14 | Copy = [i for i in data] 15 | count = self.InversePairsCore(data, Copy, 0, len(data) - 1) 16 | del Copy 17 | return count % 1000000007 18 | 19 | def InversePairsCore(self,data,Copy,start,end): 20 | if start == end: 21 | Copy[start] == data[start] 22 | return 0 23 | 24 | mid = int((start + end) / 2) 25 | 26 | left = self.InversePairsCore(Copy, data, start, mid) 27 | right = self.InversePairsCore(Copy, data, mid + 1, end) 28 | 29 | # 下面相当于merge 30 | # i初始化为前半段最后一个数字的下标,j初始化为后半段最后一个数字的下标 31 | i, j = mid, end 32 | indexCopy = end 33 | count = 0 34 | while i >= start and j >= mid + 1: 35 | if data[i]>data[j]: 36 | Copy[indexCopy] = data[i] 37 | indexCopy -= 1 38 | i -= 1 39 | count += j - mid 40 | else: 41 | Copy[indexCopy] = data[j] 42 | indexCopy -= 1 43 | j -= 1 44 | 45 | # 进行到这里,说明知道有一个数组已经到头了,或者是i或者是j 46 | while i >= start: 47 | Copy[indexCopy] = data[i] 48 | indexCopy -= 1 49 | i -= 1 50 | while j >= mid + 1: 51 | Copy[indexCopy] = data[j] 52 | indexCopy -= 1 53 | j -= 1 54 | 55 | return left + right + count 56 | 57 | # 非递归版本 58 | class Solution2: 59 | def InversePairs(self, data): 60 | if not data : 61 | return 0 62 | temp = [0]*len(data) 63 | k = 1 64 | count = 0 65 | while k < len(data): 66 | count += self.InversePairsCore(data,temp,k) 67 | k *=2 68 | return count % 1000000007 69 | 70 | def InversePairsCore(self,data,temp,k): 71 | length = len(data) 72 | i = 0 73 | count = 0 74 | while i <= length - 2 * k: 75 | count += self.Merge(data, temp, i, i + k - 1, i + 2 * k - 1) 76 | i += 2 * k 77 | if i + k - 1 < length - 1: 78 | count += self.Merge(data, temp, i, i + k - 1, length - 1) 79 | return count 80 | 81 | def Merge(self,data,temp,start,mid,end): 82 | # 分别是两个半段的末尾的指针 83 | i,j = mid,end 84 | tempIndex = end # 缓存数组的指针 85 | count = 0 86 | while i >= start and j >= mid+1: 87 | if data[i] > data[j]: 88 | temp[tempIndex] = data[i] 89 | count += j - mid 90 | tempIndex -= 1 91 | i -= 1 92 | else: 93 | temp[tempIndex] = data[j] 94 | tempIndex -= 1 95 | j -= 1 96 | 97 | while i >= start: 98 | temp[tempIndex] = data[i] 99 | tempIndex -= 1 100 | i -= 1 101 | while j >= mid + 1: 102 | temp[tempIndex] = data[j] 103 | tempIndex -= 1 104 | j -= 1 105 | 106 | data[start:end+1] = temp[start:end+1]# 将缓存数组里面的数据复制回去原来的数组 107 | return count 108 | 109 | lis = [7,5,6,4] 110 | a = Solution2() 111 | b = a.InversePairs(lis) 112 | print(b) 113 | -------------------------------------------------------------------------------- /test52_两个链表的第一个公共节点.py: -------------------------------------------------------------------------------- 1 | # 面试题52 两个链表的第一个公共节点 2 | ''' 3 | 方法一:暴力解法,遍历链表A,在遍历的同事,查看每个链表A中的节点是否在链表B中,时间复杂度是O(mn) 4 | 方法二:利用两个辅助栈,分别压入两个量表,然后弹出,比较弹出的元素,时间复杂度是O(m+n),空间复杂度是O(m+n) 5 | 方法三:首先获得两个链表的长度,让长的链表先走“两链表长度之差”步,让他们相遇的时候,就是公共节点 6 | 方法四,让链表A接到链表B的尾部,组成环,接下来就是面试题23的方法,A每次走1步,B每次走2步,相遇的时候,让A回到起点,两指针 7 | 再一起移动,再次相遇的时候,就是公共节点。 8 | ''' 9 | 10 | class ListNode: 11 | def __init__(self, x): 12 | self.val = x 13 | self.next = None 14 | 15 | # 方法2 16 | class Solution2: 17 | # 这种方式没有完全通过牛客网 18 | # 是边界条件没调好,不确定是哪个边界条件 19 | def FindFirstCommonNode(self, pHead1, pHead2): 20 | if not pHead1 or not pHead2: 21 | return None 22 | if pHead1 == pHead2: 23 | return pHead1 24 | stack1, stack2 = [], [] 25 | p1,p2 = pHead1,pHead2 26 | while p1: 27 | stack1.append(p1) 28 | p1 = p1.next 29 | while p2: 30 | stack2.append(p2) 31 | p2 = p2.next 32 | p1, p2 = stack1.pop(), stack2.pop() 33 | while stack1 and stack2 and p1 == p2: 34 | p1, p2 = stack1.pop(), stack2.pop() 35 | return p1.next 36 | 37 | class Solution3: 38 | def FindFirstCommonNode(self, pHead1, pHead2): 39 | if not pHead1 and not pHead2: 40 | return None 41 | nLength1 = self.GetListLength(pHead1) 42 | nLength2 = self.GetListLength(pHead2) 43 | nLengthDif = abs(nLength2 - nLength1) 44 | 45 | pListNodeLong,pListNodeShort = pHead1,pHead2 46 | if nLength1 < nLength2: 47 | pListNodeLong, pListNodeShort = pHead2, pHead1 48 | 49 | for i in range(nLengthDif): 50 | pListNodeLong = pListNodeLong.next 51 | 52 | while pListNodeLong and pListNodeShort and pListNodeLong != pListNodeShort: 53 | pListNodeLong = pListNodeLong.next 54 | pListNodeShort = pListNodeShort.next 55 | 56 | return pListNodeLong 57 | 58 | def GetListLength(self,pHead): 59 | i = 0 60 | p = pHead 61 | while p: 62 | i+=1 63 | p = p.next 64 | return i 65 | 66 | class Solution3_1: 67 | # 这种方法不需要计算链表的长度,是上一种方法的改进 68 | # 当两链表一样长的时候,它们的公共节点的数目是一样,所以此时大家一起前进,会有p1==p2的情况 69 | # 当两链表不等长的时候,当短的链表走完的时候,它会重新指向长的链表,然后当长的链表走完的时候,会指向短的链表。 70 | # 此时,两链表到公共节点的距离就相等了。 71 | # 因为当短链表走完时,两指针之间的差值就相当于上面的nLengthDif 72 | def FindFirstCommonNode(self, pHead1, pHead2): 73 | p1 = pHead1 74 | p2 = pHead2 75 | while p1!=p2: 76 | p1 = p1.next if p1!=None else pHead2 77 | p2 = p2.next if p2!=None else pHead1 78 | return p1 79 | 80 | # 方法四 81 | class Solution4: 82 | def FindFirstCommonNode(self, pHead1, pHead2): 83 | if not pHead1 or not pHead2: 84 | return None 85 | p1 = pHead1 86 | pend = p1 87 | # 把p2接到p1后面 88 | while p1.next: 89 | p1 = p1.next 90 | pend = p1 91 | p1.next = pHead2 92 | 93 | p1 = pHead1.next 94 | p2 = p1.next 95 | if not p2: 96 | return None 97 | while p1 and p2 and p1 != p2: 98 | p1 = p1.next 99 | p2 = p2.next 100 | if p2:# 这步是防止p2下一个为空指针 101 | p2 = p2.next 102 | 103 | if p1 != p2: 104 | return None 105 | p1 = pHead1 106 | while p1 != p2: 107 | p1 = p1.next 108 | p2 = p2.next 109 | pend.next = None 110 | return p1 111 | 112 | node1 = ListNode(1) 113 | node2 = ListNode(2) 114 | node3 = ListNode(3) 115 | node4 = ListNode(4) 116 | node5 = ListNode(5) 117 | node6 = ListNode(6) 118 | node7 = ListNode(7) 119 | 120 | node1.next=node2 121 | node2.next=node3 122 | node3.next=node6 123 | node6.next=node7 124 | node4.next=node5 125 | node5.next=node6 126 | 127 | a2 = Solution2() 128 | b = a2.FindFirstCommonNode(node1,node4) 129 | print(b) 130 | 131 | 132 | -------------------------------------------------------------------------------- /test39_数组中出现次数超过一半的数字.py: -------------------------------------------------------------------------------- 1 | # 面试题39 数组中出现次数超过一半的数字 2 | ''' 3 | 三种方法: 4 | 1.将numbers排序,返回中位数 5 | 2.利用快排的思想,当快排插入的位置不等于mid时,相应的处理numbers的左边或者右边 6 | 因为当出现的次数超过一半时,中间的数字肯定是结果 7 | 但我这里优化了一下快排,返回的是一个范围 8 | 因为书上的快排方法,会反复的排列重复的数字 9 | 3.利用一个list来存储数字和该数字出现的次数,最后保存的是出现次数过半的数字 10 | 11 | 这道题很多方法,考验的东西挺多的 12 | 还有一个方法是利用hash表 13 | 14 | ''' 15 | 16 | class Solution: 17 | def MoreThanHalfNum_Solution(self, numbers): 18 | if not isinstance(numbers, list) or not numbers: 19 | return 0 20 | mid = len(numbers) >> 1 21 | pivot = self.Partition(numbers,0,len(numbers)-1) 22 | while mid < pivot[0] or mid > pivot[1]: 23 | if mid < pivot[0]: 24 | # 处理左边 25 | pivot = self.Partition(numbers, 0, pivot[0] - 1) 26 | else: 27 | # 处理右边 28 | pivot = self.Partition(numbers, pivot[1] + 1, len(numbers) - 1) 29 | result = numbers[mid] 30 | if self.CheckMoreThanHalf(numbers,result): 31 | return result 32 | else: 33 | return 0 34 | 35 | def Partition(self,numbers,low,high): 36 | key = numbers[low] 37 | left , right = low ,high 38 | first, last = low, high 39 | if low < high: 40 | while low < high and numbers[high] >= key: 41 | if numbers[high] == key: 42 | numbers[high], numbers[right] = numbers[right], numbers[high] 43 | right -= 1 44 | high -= 1 45 | numbers[low] = numbers[high] 46 | while low < high and numbers[low] <= key: 47 | if numbers[low] == key: 48 | numbers[low], numbers[left] = numbers[left], numbers[low] 49 | left += 1 50 | low += 1 51 | numbers[high] = numbers[low] 52 | numbers[low] = key 53 | # 将与key相同的元素聚积起来 54 | i = low - 1 55 | while first < left and numbers[i] != key: 56 | numbers[first],numbers[i]=numbers[i],numbers[first] 57 | i -= 1 58 | first += 1 59 | j = low + 1 60 | while last > right and numbers[j] != key: 61 | numbers[last], numbers[j] = numbers[j], numbers[last] 62 | j += 1 63 | last -= 1 64 | # 返回重复元素的范围 65 | return [i+1,j-1] 66 | 67 | def CheckMoreThanHalf(self,numbers,number): 68 | times = 0 69 | for i in numbers: 70 | if number == i: 71 | times += 1 72 | if times > (len(numbers)>>1): 73 | return True 74 | else: 75 | return False 76 | 77 | class Solution_1: 78 | # 利用将数组进行排序,然后返回中间值 79 | def MoreThanHalfNum_Solution(self, numbers): 80 | if not isinstance(numbers, list) or not numbers: 81 | return 0 82 | mid = len(numbers) >> 1 83 | numbers.sort() 84 | result = numbers[mid] 85 | if self.CheckMoreThanHalf(numbers,result): 86 | return result 87 | else: 88 | return 0 89 | 90 | def CheckMoreThanHalf(self,numbers,number): 91 | times = 0 92 | for i in numbers: 93 | if number == i: 94 | times += 1 95 | if times > (len(numbers)>>1): 96 | return True 97 | else: 98 | return False 99 | 100 | class Solution_2: 101 | def MoreThanHalfNum_Solution(self,numbers): 102 | if not isinstance(numbers, list) or not numbers: 103 | return 0 104 | result = [numbers[0],1] 105 | for i in numbers[1:]: 106 | if i == result[0]: 107 | # 如果该数字与记录的数字相同,则次数加一 108 | result[1] += 1 109 | elif result[1] == 0: 110 | result[0] = i 111 | result[1] = 1 112 | else: 113 | result[1] -= 1 114 | if self.CheckMoreThanHalf(numbers,result[0]): 115 | return result[0] 116 | else: 117 | return 0 118 | 119 | def CheckMoreThanHalf(self,numbers,number): 120 | times = 0 121 | for i in numbers: 122 | if number == i: 123 | times += 1 124 | if times > (len(numbers)>>1): 125 | return True 126 | else: 127 | return False 128 | 129 | class Solution_3: 130 | def MoreThanHalfNum_Solution(self,numbers): 131 | if not isinstance(numbers, list) or not numbers: 132 | return 0 133 | Num_Hash = {} 134 | for i in numbers: 135 | Num_Hash[i] = Num_Hash.get(i,0) + 1 136 | Num_Hash_Sort = sorted(Num_Hash.items(), key=lambda x: x[1], reverse=True) 137 | if Num_Hash_Sort[0][1] > (len(numbers)>>1): 138 | return Num_Hash_Sort[0][0] 139 | else: 140 | return 0 141 | 142 | 143 | numbers = [1,2,3,2,2,2,5,4,2] 144 | # a = Solution() 145 | # b = a.MoreThanHalfNum_Solution(c) 146 | # print(b) 147 | # a = Solution_2() 148 | # b = a.MoreThanHalfNum_Solution(numbers) 149 | # print(b) 150 | a = Solution_3() 151 | a.MoreThanHalfNum_Solution(numbers) 152 | 153 | --------------------------------------------------------------------------------