├── leetcode ├── 005-Longest Palindromic Substring │ ├── code.py │ └── readme.md ├── 053-Maximum Subarray │ ├── code.py │ └── readme.md ├── 121-Best Time to Buy and Sell Stock │ ├── code.py │ └── readme.md ├── 122. Best Time to Buy and Sell Stock II │ ├── code.py │ └── readme.md ├── 123. Best Time to Buy and Sell Stock III │ ├── code.py │ └── readme.md ├── 188. Best Time to Buy and Sell Stock IV │ ├── code.py │ └── readme.md ├── 198-House Robber │ ├── code.py │ └── readme.md ├── 303-Range Sum Query - Immutable │ ├── code.py │ └── readme.md ├── 309. Best Time to Buy and Sell Stock with Cooldown │ ├── code.py │ └── readme.md ├── 338-Counting Bits │ ├── code.py │ └── readme.md ├── 343. Integer Break │ ├── code.py │ └── readme.md ├── 357. Count Numbers with Unique Digits │ ├── code.py │ └── readme.md ├── 392. Is Subsequence │ ├── code.py │ └── readme.md ├── 413-Arithmetic Slices │ ├── code.py │ └── readme.md ├── 486. Predict the Winner │ ├── code.py │ └── readme.md ├── 62. Unique Paths │ ├── code.py │ └── readme.md ├── 64. Minimum Path Sum │ ├── code.py │ └── readme.md ├── 646. Maximum Length of Pair Chain │ ├── code.py │ └── readme.md ├── 647-Palindromic Substrings │ ├── code.py │ └── readme.md ├── 650. 2 Keys Keyboard │ ├── code.py │ └── readme.md ├── 712-Minimum ASCII Delete Sum for Two Strings │ ├── code.py │ └── readme.md ├── 714-Best Time to Buy and Sell Stock with Transaction Fee │ ├── code.py │ └── readme.md ├── 746-Min Cost Climbing Stairs │ ├── code.py │ └── readme.md ├── 873. Length of Longest Fibonacci Subsequence │ ├── code.py │ └── readme.md ├── 877-Stone Game │ ├── code.py │ └── readme.md ├── 931-Minimum Falling Path Sum │ ├── code.py │ └── readme.md └── 983-Minimum Cost For Tickets │ ├── code.py │ └── readme.md ├── readme.md └── 剑指offer ├── 001-二维数组查找 ├── code.py └── readme.md ├── 002-替换空格 ├── code.py └── readme.md ├── 003-从尾到头打印链表 ├── code.py └── readme.md ├── 004-重建二叉树 ├── code.py └── readme.md ├── 005-用两个栈实现队列 ├── code.py └── readme.md ├── 006-旋转数组的最小数字 ├── code.py └── readme.md ├── 007-斐波拉契数列 ├── code.py └── readme.md ├── 008-跳台阶 ├── code.py └── readme.md ├── 009-变态跳台阶 ├── code.py └── readme.md ├── 010-矩形覆盖 ├── code.py └── readme.md ├── 011-二进制中1的个数 ├── code.py └── readme.md ├── 012-数值的整数次方 ├── code.py └── readme.md ├── 013-调整数组顺序使奇数位于偶数前面 ├── code.py └── readme.md ├── 014-链表中倒数第k个结点 ├── code.py └── readme.md ├── 015-反转链表 ├── code.py └── readme.md ├── 016-合并有序链表 ├── code.py └── readme.md ├── 017-树的子结构 ├── code.py └── readme.md ├── 018-二叉树的镜像 ├── code.py └── readme.md ├── 019-顺时针打印矩阵 ├── code.py └── readme.md ├── 020-包含min函数的栈 ├── code.py └── readme.md ├── 021-栈的压入、弹出序列 ├── code.py └── readme.md ├── 022-从上往下打印二叉树 ├── code.py └── readme.md ├── 023-二叉搜索树的后序遍历序列 ├── code.py └── readme.md ├── 024-二叉树中和为某一值的路径 ├── code.py └── readme.md ├── 025-复杂链表的复制 ├── code.py └── readme.md ├── 026-二叉搜索树与双向链表 ├── code.py └── readme.md ├── 027-字符串的排列 ├── code.py └── readme.md ├── 028-数组中出现次数超过一半的数字 ├── code.py └── readme.md ├── 029-最小的K个数 ├── code.py ├── partition.py └── readme.md ├── 030-连续子数组的最大和 ├── code.py └── readme.md ├── 031-整数中1出现的次数(从1到n整数中1出现的次数) ├── code.py └── readme.md ├── 032-把数组排成最小的数 ├── readme.md ├── solution1.py └── solution2.py ├── 033-丑数 ├── readme.md ├── solution1&2.py └── solution3.py ├── 034-第一个只出现一次的字符 ├── readme.md └── solution.py ├── 035-数组中的逆序对 ├── readme.md └── solution1.py ├── 036-两个链表的第一个公共结点 ├── readme.md └── solution.py ├── 037-数字在排序数组中出现的次数 ├── readme.md └── solution.py ├── 038-二叉树的深度 ├── readme.md └── solution.py ├── 039-平衡二叉树 ├── readme.md ├── solution1.py └── solution2.py ├── 040-数组中只出现一次的数字 ├── code.py └── readme.md ├── 041-和为S的连续正数序列 ├── code.py └── readme.md ├── 042-和为S的两个数字 ├── code.py └── readme.md ├── 043-左旋转字符串 ├── code.py └── readme.md ├── 044-翻转单词顺序列 ├── code.py └── readme.md ├── 045-扑克牌顺子 ├── code.py └── readme.md ├── 046-孩子们的游戏-圆圈中最后剩下的数 ├── code.py └── readme.md ├── 047-求1+2+3+...+n ├── code.py └── readme.md ├── 048-不用加减乘除做加法 ├── code.py └── readme.md ├── 049-把字符串转换成整数 ├── code.py └── readme.md ├── 050-数组中重复的数字 ├── code.py └── readme.md ├── 051-构建乘积数组 ├── code.py └── readme.md ├── 052-正则表达式匹配 ├── code.py └── readme.md ├── 053-表示数值的字符串 ├── code.py └── readme.md ├── 054-字符流中第一个不重复的字符 ├── code.py └── readme.md ├── 055-链表中环的入口结点 ├── code.py └── readme.md ├── 056-删除链表中重复的结点 ├── code.py └── readme.md ├── 057-二叉树的下一个结点 ├── code.py └── readme.md ├── 058-对称的二叉树 ├── code.py └── readme.md ├── 059-按之字形顺序打印二叉树 ├── code.py └── readme.md ├── 060-把二叉树打印成多行 ├── code.py └── readme.md ├── 061-序列化二叉树 ├── code.py └── readme.md ├── 062-二叉搜索树的第k个结点 ├── code.py └── readme.md ├── 063-数据流中的中位数 ├── code.py └── readme.md ├── 064-滑动窗口的最大值 ├── code.py └── readme.md ├── 065-矩阵中的路径 ├── code.py └── readme.md ├── 066-机器人的运动范围 ├── code.py └── readme.md └── readme.md /leetcode/005-Longest Palindromic Substring/code.py: -------------------------------------------------------------------------------- 1 | class Solution(object): 2 | def longestPalindrome(self, s): 3 | """ 4 | :type s: str 5 | :rtype: str 6 | """ 7 | res = '' 8 | n = len(s) 9 | for i in range(n): 10 | odd_s = self.palindromeAt(s, i, i) 11 | even_s = self.palindromeAt(s, i-1, i) 12 | res = max(res, odd_s, even_s, key=len) 13 | return res 14 | 15 | def palindromeAt(self, s, l, r): 16 | while l >= 0 and r < len(s) and s[l] == s[r]: 17 | l -= 1 18 | r += 1 19 | return s[l+1:r] 20 | 21 | print(Solution().longestPalindrome('caba')) 22 | 23 | -------------------------------------------------------------------------------- /leetcode/005-Longest Palindromic Substring/readme.md: -------------------------------------------------------------------------------- 1 | ## 005-Longest Palindromic Substring 2 | 3 | ### 一、题目描述 4 | 5 | Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000. 6 | 7 | ``` 8 | Example 1: 9 | 10 | Input: "babad" 11 | Output: "bab" 12 | Note: "aba" is also a valid answer. 13 | 14 | Example 2: 15 | 16 | Input: "cbbd" 17 | Output: "bb" 18 | 19 | Example 3: 20 | 21 | Input: "abb" 22 | Output: "bb" 23 | ``` 24 | 25 | ### 二、思路 26 | 27 | #### 2.1 暴力算法 28 | 29 | 遍历一个字符串的所有子串时间复杂度为o(n^2),验证一个子串是否回文时间复杂度为o(n),总共时间o(n^3)。 30 | 31 | 一个思路是:如果减少子串回文验证时间? 32 | 33 | #### 2.2 动态规划 34 | 35 | 每个位置的数,只可能构成长度为奇数和长度为偶数的回文。 36 | 37 | - 长度为奇数,以自己为轴向两边散开 38 | - 长度为偶数,以自己与后一个数(或前一个数)为轴向两边散开 39 | 40 | 在遍历的过程中,取最长的回文即为答案。 41 | 42 | 代码看起来似乎是子串遍历时间从o(n^2)降到了o(n),但是实际上每个字母遍历的次数时间复杂度是o(2n)=o(n)的,只是在遍历的时候验证了子串是否是回文,因此时间复杂度相比于暴力法减少了o(n)的时间。总的时间复杂度为o(n^2)。 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /leetcode/053-Maximum Subarray/code.py: -------------------------------------------------------------------------------- 1 | class Solution(object): 2 | def maxSubArray(self, nums): 3 | """ 4 | :type nums: List[int] 5 | :rtype: int 6 | """ 7 | res = nums[0] 8 | sum = res 9 | for i in range(1, len(nums)): 10 | if sum < 0: 11 | sum = nums[i] 12 | else: 13 | sum += nums[i] 14 | res = max(res, sum) 15 | return res 16 | 17 | print(Solution().maxSubArray([-2,1,-3,4,-1,2,1,-5,4])) 18 | print(Solution().maxSubArray([-2,1])) 19 | -------------------------------------------------------------------------------- /leetcode/053-Maximum Subarray/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | https://leetcode.com/problems/maximum-subarray/ 4 | 5 | Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum. 6 | 7 | ``` 8 | Example: 9 | 10 | Input: [-2,1,-3,4,-1,2,1,-5,4], 11 | Output: 6 12 | Explanation: [4,-1,2,1] has the largest sum = 6. 13 | ``` 14 | 15 | Follow up: 16 | 17 | If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle. 18 | 19 | ### 思路 20 | 21 | 遍历到第k个数的时候,如果前k-1个数之和为负数,则丢弃前k-1个数之和。遍历过程中保存最大和。 -------------------------------------------------------------------------------- /leetcode/121-Best Time to Buy and Sell Stock/code.py: -------------------------------------------------------------------------------- 1 | class Solution(object): 2 | def maxProfit1(self, prices): 3 | # 超时算法 4 | res = 0 5 | for i in range(len(prices)): 6 | for j in range(i, len(prices)): 7 | res = max(prices[j] - prices[i], res) 8 | return res 9 | 10 | def maxProfit(self, prices): 11 | # 零维动态规划 12 | if not prices: return 0 13 | dp = prices[0] 14 | res = 0 15 | for i in range(len(prices)): 16 | res = max(prices[i] - dp, res) 17 | dp = min(dp, prices[i]) 18 | return res 19 | 20 | print(Solution().maxProfit([7,1,5,3,6,4])) 21 | -------------------------------------------------------------------------------- /leetcode/121-Best Time to Buy and Sell Stock/readme.md: -------------------------------------------------------------------------------- 1 | https://leetcode.com/problems/best-time-to-buy-and-sell-stock/ 2 | 3 | ### 题目描述 4 | 5 | 题目链接:https://leetcode.com/problems/best-time-to-buy-and-sell-stock/submissions/ 6 | 7 | Say you have an array for which the ith element is the price of a given stock on day i. 8 | 9 | If you were only permitted to complete at most one transaction (i.e., buy one and sell one share of the stock), design an algorithm to find the maximum profit. 10 | 11 | Note that you cannot sell a stock before you buy one. 12 | 13 | ``` 14 | Example 1: 15 | 16 | Input: [7,1,5,3,6,4] 17 | Output: 5 18 | Explanation: Buy on day 2 (price = 1) and sell on day 5 (price = 6), profit = 6-1 = 5. 19 | Not 7-1 = 6, as selling price needs to be larger than buying price. 20 | Example 2: 21 | 22 | Input: [7,6,4,3,1] 23 | Output: 0 24 | Explanation: In this case, no transaction is done, i.e. max profit = 0. 25 | ``` 26 | 27 | ### 思路 28 | 29 | 很明显的暴力法时间复杂度o(n^2),会超时TLE 30 | 31 | 一种简单的方法是记录前i-1个数中最小的数,第i个数减去这个最小的数就为前i个数的最大收益。 -------------------------------------------------------------------------------- /leetcode/122. Best Time to Buy and Sell Stock II/code.py: -------------------------------------------------------------------------------- 1 | class Solution(object): 2 | def maxProfit(self, prices): 3 | """ 4 | :type prices: List[int] 5 | :rtype: int 6 | """ 7 | t_ik0 = 0 8 | t_ik1 = float('-inf') 9 | for p in prices: 10 | t_ik0_old = t_ik0 11 | t_ik0 = max(t_ik0, t_ik1 + p) #r or sell 12 | t_ik1 = max(t_ik1, t_ik0_old - p) # r or buy 13 | return t_ik0 14 | -------------------------------------------------------------------------------- /leetcode/122. Best Time to Buy and Sell Stock II/readme.md: -------------------------------------------------------------------------------- 1 | 2 | ### 题目描述 3 | 4 | https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/ 5 | 6 | Say you have an array for which the ith element is the price of a given stock on day i. 7 | 8 | Design an algorithm to find the maximum profit. You may complete as many transactions as you like (i.e., buy one and sell one share of the stock multiple times). 9 | 10 | Note: You may not engage in multiple transactions at the same time (i.e., you must sell the stock before you buy again). 11 | ``` 12 | Example 1: 13 | 14 | Input: [7,1,5,3,6,4] 15 | Output: 7 16 | Explanation: Buy on day 2 (price = 1) and sell on day 3 (price = 5), profit = 5-1 = 4. 17 | Then buy on day 4 (price = 3) and sell on day 5 (price = 6), profit = 6-3 = 3. 18 | Example 2: 19 | 20 | Input: [1,2,3,4,5] 21 | Output: 4 22 | Explanation: Buy on day 1 (price = 1) and sell on day 5 (price = 5), profit = 5-1 = 4. 23 | Note that you cannot buy on day 1, buy on day 2 and sell them later, as you are 24 | engaging multiple transactions at the same time. You must sell before buying again. 25 | Example 3: 26 | 27 | Input: [7,6,4,3,1] 28 | Output: 0 29 | Explanation: In this case, no transaction is done, i.e. max profit = 0. 30 | ``` 31 | 32 | ### 思路 33 | 34 | T[i][k][0] 表示在第i个位置完成k次交易后手上持0股。 35 | T[i][k][1] 表示在第i个位置完成k次交易后手上持1股。 36 | 本题中k = +Infinity 37 | 在第i个位置有三种可能操作,买、卖、不动。故公式如下: 38 | 39 | T[-1][k][0] = 0, T[-1][k][1] = -1 40 | T[i][k][0] = max(T[i-1][k][0], T[i-1][k][1] + prices[i]) # r or sell 41 | T[i][k][1] = max(T[i-1][k][1], T[i-1][k-1][0] - prices[i]) # r or buy 42 | 43 | -------------------------------------------------------------------------------- /leetcode/123. Best Time to Buy and Sell Stock III/code.py: -------------------------------------------------------------------------------- 1 | class Solution(object): 2 | def maxProfit(self, prices): 3 | """ 4 | :type prices: List[int] 5 | :rtype: int 6 | """ 7 | T_i10, T_i11, T_i20, T_i21 = [float('-inf')] * 4 8 | for p in prices: 9 | T_i10_old = T_i10 10 | T_i10 = max(T_i10, T_i11 + p) 11 | T_i11 = max(T_i11, -p) 12 | T_i20 = max(T_i20, T_i21 + p) 13 | T_i21 = max(T_i21, T_i10_old - p) 14 | return max(T_i20, T_i10, 0) 15 | 16 | print(Solution().maxProfit([3,3,5,0,0,3,1,4])) -------------------------------------------------------------------------------- /leetcode/123. Best Time to Buy and Sell Stock III/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目链接 2 | 3 | https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/ 4 | 5 | Say you have an array for which the ith element is the price of a given stock on day i. 6 | 7 | Design an algorithm to find the maximum profit. You may complete at most two transactions. 8 | 9 | Note: You may not engage in multiple transactions at the same time (i.e., you must sell the stock before you buy again). 10 | 11 | ``` 12 | Example 1: 13 | 14 | Input: [3,3,5,0,0,3,1,4] 15 | Output: 6 16 | Explanation: Buy on day 4 (price = 0) and sell on day 6 (price = 3), profit = 3-0 = 3. 17 | Then buy on day 7 (price = 1) and sell on day 8 (price = 4), profit = 4-1 = 3. 18 | 19 | Example 2: 20 | 21 | Input: [1,2,3,4,5] 22 | Output: 4 23 | Explanation: Buy on day 1 (price = 1) and sell on day 5 (price = 5), profit = 5-1 = 4. 24 | Note that you cannot buy on day 1, buy on day 2 and sell them later, as you are 25 | engaging multiple transactions at the same time. You must sell before buying again. 26 | 27 | Example 3: 28 | 29 | Input: [7,6,4,3,1] 30 | Output: 0 31 | Explanation: In this case, no transaction is done, i.e. max profit = 0. 32 | ``` 33 | 34 | ### 思路 35 | 36 | T[i][k][0] 表示在第i个位置完成k次交易后手上持0股。 37 | T[i][k][1] 表示在第i个位置完成k次交易后手上持1股。 38 | 本题中k = 2 39 | 在第i个位置有三种可能操作,买、卖、不动。故公式如下: 40 | 41 | ``` 42 | T[i][0][0] = 0 43 | 44 | T[i][1][0] = max(T[i-1][1][0], T[i-1][1][1] + p) 45 | T[i][1][1] = max(T[i-1][1][1], T[i-1][0][0] - p) 46 | 47 | T[i][2][0] = max(T[i-1][2][0], T[i-1][2][1] + p) 48 | T[i][2][1] = max(T[i-1][2][1], T[i-1][1][0] - p) 49 | 50 | ``` -------------------------------------------------------------------------------- /leetcode/188. Best Time to Buy and Sell Stock IV/code.py: -------------------------------------------------------------------------------- 1 | class Solution(object): 2 | def maxProfit(self, k, prices): 3 | n = len(prices) 4 | if k >= n>>1: 5 | k = n>>1 6 | T_ik0 = [0] * (k+1) 7 | T_ik1 = [float('-inf')] * (k+1) 8 | for p in prices: 9 | for d in range(1, k+1): 10 | T_ik0[d] = max(T_ik0[d], T_ik1[d] + p) 11 | T_ik1[d] = max(T_ik1[d], T_ik0[d-1] - p) 12 | return T_ik0[k] 13 | 14 | print(Solution().maxProfit(k=2, prices=[3,2,6,5,0,3])) -------------------------------------------------------------------------------- /leetcode/188. Best Time to Buy and Sell Stock IV/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目链接 2 | 3 | https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iv/ 4 | 5 | Say you have an array for which the ith element is the price of a given stock on day i. 6 | 7 | Design an algorithm to find the maximum profit. You may complete at most k transactions. 8 | 9 | Note: 10 | You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again). 11 | 12 | ``` 13 | Example 1: 14 | 15 | Input: [2,4,1], k = 2 16 | Output: 2 17 | Explanation: Buy on day 1 (price = 2) and sell on day 2 (price = 4), profit = 4-2 = 2. 18 | Example 2: 19 | 20 | Input: [3,2,6,5,0,3], k = 2 21 | Output: 7 22 | Explanation: Buy on day 2 (price = 2) and sell on day 3 (price = 6), profit = 6-2 = 4. 23 | Then buy on day 5 (price = 0) and sell on day 6 (price = 3), profit = 3-0 = 3. 24 | ``` 25 | 26 | ### 思路 27 | 28 | T[i][k][0] 表示在第i个位置完成k次交易后手上持0股。 29 | T[i][k][1] 表示在第i个位置完成k次交易后手上持1股。 30 | 本题中k = 任意值 31 | 在第i个位置有三种可能操作,买、卖、不动。故公式如下: 32 | 33 | ``` 34 | T[i][0][0] = 0 35 | T[i][k][0] = max(T[i-1][k][0], T[i][k][1] + p) 36 | T[i][k][1] = max(T[i-1][k][1], T[i][k-1][0] - p) 37 | ``` 38 | 39 | -------------------------------------------------------------------------------- /leetcode/198-House Robber/code.py: -------------------------------------------------------------------------------- 1 | class Solution(object): 2 | def rob(self, nums): 3 | """ 4 | :type nums: List[int] 5 | :rtype: int 6 | """ 7 | if len(nums) == 0: return 0 8 | if len(nums) == 1: return nums[0] 9 | dp = [0] * (len(nums)+1) 10 | dp[0] = nums[0] 11 | dp[1] = max(nums[0], nums[1]) 12 | for i in range(2, len(nums)): 13 | dp[i] = max(dp[i-1], dp[i-2]+nums[i]) 14 | return dp[len(nums)-1] 15 | 16 | print(Solution().rob([1,2,1,3])) 17 | print(Solution().rob([2,7,9,3,1])) 18 | print(Solution().rob([0,0])) -------------------------------------------------------------------------------- /leetcode/198-House Robber/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | https://leetcode.com/problems/house-robber/ 4 | 5 | You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night. 6 | 7 | Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police. 8 | 9 | ``` 10 | Example 1: 11 | 12 | Input: [1,2,3,1] 13 | Output: 4 14 | Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3). 15 | Total amount you can rob = 1 + 3 = 4. 16 | Example 2: 17 | 18 | Input: [2,7,9,3,1] 19 | Output: 12 20 | Explanation: Rob house 1 (money = 2), rob house 3 (money = 9) and rob house 5 (money = 1). 21 | Total amount you can rob = 2 + 9 + 1 = 12. 22 | ``` 23 | 24 | ### 思路 25 | 26 | dp(i)表示第i个位置能抢到的最大金额。 27 | 28 | dp(0) = f(0) 29 | dp(1) = max(f(0), f(1)) 30 | dp(i) = max(dp(i-2) + f(i), dp(i-1)) -------------------------------------------------------------------------------- /leetcode/303-Range Sum Query - Immutable/code.py: -------------------------------------------------------------------------------- 1 | class NumArray(object): 2 | 3 | def __init__(self, nums): 4 | """ 5 | :type nums: List[int] 6 | """ 7 | self.dp = [0]*(len(nums)+1) 8 | for i in range(len(nums)): 9 | if i == 0: 10 | self.dp[i] = nums[i] 11 | else: 12 | self.dp[i] = self.dp[i-1] + nums[i] 13 | 14 | def sumRange(self, i, j): 15 | """ 16 | :type i: int 17 | :type j: int 18 | :rtype: int 19 | """ 20 | return self.dp[j] - self.dp[i-1] 21 | 22 | 23 | 24 | # Your NumArray object will be instantiated and called as such: 25 | obj = NumArray([-2, 0, 3, -5, 2, -1]) 26 | print(obj.sumRange(0,2)) 27 | print(obj.sumRange(2,5)) 28 | print(obj.sumRange(0,5)) -------------------------------------------------------------------------------- /leetcode/303-Range Sum Query - Immutable/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | https://leetcode.com/problems/range-sum-query-immutable/ 3 | 4 | Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive. 5 | 6 | ``` 7 | Example: 8 | Given nums = [-2, 0, 3, -5, 2, -1] 9 | 10 | sumRange(0, 2) -> 1 11 | sumRange(2, 5) -> -1 12 | sumRange(0, 5) -> -3 13 | ``` 14 | 15 | Note: 16 | You may assume that the array does not change. 17 | There are many calls to sumRange function. 18 | 19 | 20 | ### 思路 21 | 22 | dp[i]表示从0到i的sum 23 | 24 | i到j的sum=dp[j]-dp[i] -------------------------------------------------------------------------------- /leetcode/309. Best Time to Buy and Sell Stock with Cooldown/code.py: -------------------------------------------------------------------------------- 1 | class Solution(object): 2 | def maxProfit(self, prices): 3 | """ 4 | :type prices: List[int] 5 | :rtype: int 6 | """ 7 | T_ik0, T_ik1 = 0, float('-inf') 8 | T_ik0_pre = 0 9 | 10 | for p in prices: 11 | T_ik0_old= T_ik0 12 | T_ik0 = max(T_ik0, T_ik1 + p) 13 | T_ik1 = max(T_ik1, T_ik0_pre - p) 14 | T_ik0_pre = T_ik0_old 15 | return T_ik0 16 | 17 | 18 | 19 | print(Solution().maxProfit([1,2,3,0,2])) 20 | 21 | 22 | -------------------------------------------------------------------------------- /leetcode/309. Best Time to Buy and Sell Stock with Cooldown/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/#/description 4 | 5 | Say you have an array for which the ith element is the price of a given stock on day i. 6 | 7 | Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times) with the following restrictions: 8 | 9 | - You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again). 10 | - After you sell your stock, you cannot buy stock on next day. (ie, cooldown 1 day) 11 | 12 | ``` 13 | Example: 14 | 15 | Input: [1,2,3,0,2] 16 | Output: 3 17 | Explanation: transactions = [buy, sell, cooldown, buy, sell] 18 | ``` 19 | 20 | ### 思路 21 | 22 | T[i][k][0] 第i天买入k次并且手里持0股。 23 | T[i][k][1] 第i天买入k次并且手里持1股。 24 | k = 任意值并且有冷却期一天 25 | 26 | 如果第i天要买进,那么第i-1天必须休息不操作,那么T[i-1][k][0] = T[i-2][k][0] 27 | 28 | 公式如下: 29 | 30 | T[i][k][0] = max(T[i-1][k][0], T[i-1][k][1] + p) # rest or sell 31 | T[i][k][1] = max(T[i-1][k][1], T[i-2][k][0] - p) # rest or buy 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /leetcode/338-Counting Bits/code.py: -------------------------------------------------------------------------------- 1 | class Solution1(object): 2 | '''暴力法O(n*size(n))''' 3 | def countBits(self, num): 4 | return [self.nBits(i) for i in range(num+1)] 5 | 6 | def nBits(self, num): 7 | cnt = 0 8 | while num: 9 | num &= (num-1) 10 | cnt += 1 11 | return cnt 12 | 13 | print(Solution1().countBits(3)) 14 | 15 | class Solution(object): 16 | def countBits(self, num): 17 | dp = [0] * (num+1) 18 | for i in range(num+1): 19 | dp[i] = dp[i>>1] + i%2 20 | return dp 21 | 22 | print(Solution().countBits(3)) -------------------------------------------------------------------------------- /leetcode/338-Counting Bits/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | https://leetcode.com/problems/counting-bits/ 4 | 5 | Given a non negative integer number num. For every numbers i in the range 0 ≤ i ≤ num calculate the number of 1's in their binary representation and return them as an array. 6 | 7 | ``` 8 | Example 1: 9 | 10 | Input: 2 11 | Output: [0,1,1] 12 | 13 | Example 2: 14 | 15 | Input: 5 16 | Output: [0,1,1,2,1,2] 17 | ``` 18 | 19 | Follow up: 20 | 21 | It is very easy to come up with a solution with run time O(n*sizeof(integer)). But can you do it in linear time O(n) /possibly in a single pass? 22 | Space complexity should be O(n). 23 | Can you do it like a boss? Do it without using any builtin function like __builtin_popcount in c++ or in any other language. 24 | 25 | ### 思路 26 | 27 | 暴力法O(n*sizeof(n))也能过,follow up中要求O(n)复杂度,需要用动态规划。 28 | 一个数的二进制数(假设为8位),它为前7位数的1个数和最右位1的个数之和,比如考虑5中1的个数 29 | 30 | 5的二进制为:00000111 31 | 5的二进制数由5>>1的二进制数00000011 与 5%2=1的二进制数的和 32 | 33 | 故dp[i] = dp[i>>1] + i%2 34 | 35 | 36 | -------------------------------------------------------------------------------- /leetcode/343. Integer Break/code.py: -------------------------------------------------------------------------------- 1 | class Solution(object): 2 | def integerBreak(self, n): 3 | """ 4 | :type n: int 5 | :rtype: int 6 | """ 7 | def max_n(n, i): 8 | if n % i == 0: 9 | return (n//i) ** i 10 | else: 11 | dn = (n // i + 1) * i - n 12 | return (n // i + 1) ** (i - dn) * (n // i) ** dn 13 | res = 1 14 | for i in range(2, n+1): 15 | res = max(res, max_n(n, i)) 16 | return res 17 | 18 | 19 | print(Solution().integerBreak(2)) -------------------------------------------------------------------------------- /leetcode/343. Integer Break/readme.md: -------------------------------------------------------------------------------- 1 | Given a positive integer n, break it into the sum of at least two positive integers and maximize the product of those integers. Return the maximum product you can get. 2 | 3 | ``` 4 | Example 1: 5 | 6 | Input: 2 7 | Output: 1 8 | Explanation: 2 = 1 + 1, 1 × 1 = 1. 9 | Example 2: 10 | 11 | Input: 10 12 | Output: 36 13 | Explanation: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36. 14 | ``` 15 | 16 | Note: You may assume that n is not less than 2 and not larger than 58. -------------------------------------------------------------------------------- /leetcode/357. Count Numbers with Unique Digits/code.py: -------------------------------------------------------------------------------- 1 | class Solution(object): 2 | def countNumbersWithUniqueDigits(self, n): 3 | """ 4 | :type n: int 5 | :rtype: int 6 | """ 7 | if n > 10: n = 10 8 | res = 0 9 | f1 = 10 10 | f2 = 9*9 11 | if n == 0: return 1 12 | for i in range(1, n+1): 13 | if i == 1: 14 | res += f1 15 | elif i == 2: 16 | res += f2 17 | else: 18 | f2 = f2 * (11-i) 19 | res += f2 20 | return res 21 | 22 | print(Solution().countNumbersWithUniqueDigits(2)) 23 | 24 | -------------------------------------------------------------------------------- /leetcode/357. Count Numbers with Unique Digits/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | https://leetcode.com/problems/count-numbers-with-unique-digits/ 4 | 5 | Given a non-negative integer n, count all numbers with unique digits, x, where 0 ≤ x < 10n. 6 | 7 | Example: 8 | 9 | Input: 2 10 | Output: 91 11 | Explanation: The answer should be the total numbers in the range of 0 ≤ x < 100, 12 | excluding 11,22,33,44,55,66,77,88,99 13 | 14 | ### 思路 15 | 16 | 设f(n)表示n位数有多少个不同数,则 17 | 18 | f(1) = 10 明显 19 | f(2) = 9 * 9 第一位i不为0,第二位j不同于i,分别为9。 20 | f(3) = f(2) * 8 已有两位不同数,第三位k不同于i和j,k取值为8种。 21 | f(4) = f(3) * 7 同理于f(3) 22 | ... ... 23 | f(10) = f(9) * 1 24 | f(11) = 0 25 | 26 | 27 | -------------------------------------------------------------------------------- /leetcode/392. Is Subsequence/code.py: -------------------------------------------------------------------------------- 1 | class Solution(object): 2 | def isSubsequence(self, s, t): 3 | """ 4 | :type s: str 5 | :type t: str 6 | :rtype: bool 7 | """ 8 | i, j = 0, 0 9 | while i < len(s) and j < len(t): 10 | if s[i] == t[j]: 11 | i += 1 12 | j += 1 13 | else: 14 | j += 1 15 | return i == len(s) 16 | 17 | print(Solution().isSubsequence(s = "abc", t = "ahbgdc")) 18 | print(Solution().isSubsequence(s = "axc", t = "ahbgdc")) 19 | -------------------------------------------------------------------------------- /leetcode/392. Is Subsequence/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | https://leetcode.com/problems/is-subsequence/ 4 | 5 | Given a string s and a string t, check if s is subsequence of t. 6 | 7 | You may assume that there is only lower case English letters in both s and t. t is potentially a very long (length ~= 500,000) string, and s is a short string (<=100). 8 | 9 | A subsequence of a string is a new string which is formed from the original string by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (ie, "ace" is a subsequence of "abcde" while "aec" is not). 10 | 11 | ``` 12 | Example 1: 13 | s = "abc", t = "ahbgdc" 14 | 15 | Return true. 16 | 17 | Example 2: 18 | s = "axc", t = "ahbgdc" 19 | 20 | Return false. 21 | ``` 22 | 23 | Follow up: 24 | If there are lots of incoming S, say S1, S2, ... , Sk where k >= 1B, and you want to check one by one to see if T has its subsequence. In this scenario, how would you change your code? 25 | 26 | Credits: 27 | Special thanks to @pbrother for adding this problem and creating all test cases. 28 | 29 | ### 思路 30 | 31 | 难点在follow up,解法: 32 | https://leetcode.com/problems/is-subsequence/discuss/87302/Binary-search-solution-for-follow-up-with-detailed-comments -------------------------------------------------------------------------------- /leetcode/413-Arithmetic Slices/code.py: -------------------------------------------------------------------------------- 1 | class Solution1(object): 2 | def numberOfArithmeticSlices(self, A): 3 | n = len(A) 4 | dp = [ [[0, 0] for _ in range(n)] for _ in range(n)] 5 | for i in range(n-2): 6 | if A[i+2] - A[i+1] == A[i+1] - A[i]: 7 | dp[i][i+2] = [1, A[i+1]-A[i]] 8 | for d in range(3, n): 9 | for i in range(n-d): 10 | if dp[i][i+d-1][0]: 11 | dp[i][i+d][0] = dp[i][i+d-1][1] == (A[i+d] - A[i+d-1]) 12 | if dp[i][i+d][0]: 13 | dp[i][i+d][1] = dp[i][i+d-1][1] 14 | cnt = 0 15 | for col in dp: 16 | for row in col: 17 | if row[0]: cnt += 1 18 | return cnt 19 | 20 | class Solution2(object): 21 | def numberOfArithmeticSlices(self, A): 22 | n = len(A) 23 | dp = [[None]*n for _ in range(n)] 24 | for i in range(n-2): 25 | if A[i+2] - A[i+1] == A[i+1] - A[i]: 26 | dp[i][i+2] = A[i+1]-A[i] 27 | 28 | for d in range(3, n): 29 | for i in range(n-d): 30 | if dp[i][i+d-1] is not None: 31 | if dp[i][i+d-1] == (A[i+d] - A[i+d-1]): 32 | dp[i][i+d] = dp[i][i+d-1] 33 | cnt = 0 34 | for col in dp: 35 | for row in col: 36 | if row is not None: cnt += 1 37 | return cnt 38 | 39 | class Solution3(object): 40 | # from discuss 41 | def numberOfArithmeticSlices(self, A): 42 | n = len(A) 43 | curr = 0 44 | sum = 0 45 | for i in range(2, n): 46 | if A[i] - A[i-1] == A[i-1] - A[i-2]: 47 | curr += 1 48 | sum += curr 49 | else: 50 | curr = 0 51 | return sum 52 | print(Solution3().numberOfArithmeticSlices([1, 1, 1, 1])) 53 | print(Solution3().numberOfArithmeticSlices([1, 2, 3, 4])) 54 | 55 | 56 | -------------------------------------------------------------------------------- /leetcode/413-Arithmetic Slices/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目链接 2 | 3 | https://leetcode.com/problems/arithmetic-slices/ 4 | 5 | A sequence of number is called arithmetic if it consists of at least three elements and if the difference between any two consecutive elements is the same. 6 | 7 | For example, these are arithmetic sequence: 8 | ``` 9 | 1, 3, 5, 7, 9 10 | 7, 7, 7, 7 11 | 3, -1, -5, -9 12 | ``` 13 | The following sequence is not arithmetic. 14 | ``` 15 | 1, 1, 2, 5, 7 16 | ``` 17 | A zero-indexed array A consisting of N numbers is given. A slice of that array is any pair of integers (P, Q) such that 0 <= P < Q < N. 18 | 19 | A slice (P, Q) of array A is called arithmetic if the sequence: 20 | A[P], A[p + 1], ..., A[Q - 1], A[Q] is arithmetic. In particular, this means that P + 1 < Q. 21 | 22 | The function should return the number of arithmetic slices in the array A. 23 | 24 | 25 | Example: 26 | ``` 27 | A = [1, 2, 3, 4] 28 | 29 | return: 3, for 3 arithmetic slices in A: [1, 2, 3], [2, 3, 4] and [1, 2, 3, 4] itself. 30 | ``` 31 | 32 | ### 思路 33 | 34 | 求一个数列的子数列是等差数列的数目。 35 | 36 | 这题和DP647比较像。遍历所有的子串需要的复杂度是o(n^2),判断一个子串是否是等差数列时间复杂度是o(n)。 37 | 38 | 所以本题的思路是通过动态规划打表降低等差数列的判断时间为o(1),动态规划公式如下: 39 | 40 | #### 思路1 直接思路,爆内存了 41 | dp[i][j][0] = True if f(j)-f(i)是等差数列 j-i=1 42 | 43 | dp[i][j][0] = dp[i][j-1][0] and f[j]-f[j-1] == dp[i][j-1][1] j-i>2 44 | if dp[i][j][0]: dp[i][j][1] = dp[i][j-1][1] 45 | 46 | #### 思路2 47 | 48 | 把bool标记删减掉就好了,明天看看discuss看看别人的思路。 49 | 50 | #### 思路3 51 | 52 | 牛逼的discuss思路,用curr表示第0~i个位置有多少个等差数列,求和即可。O(n)空间复杂度,O(1)时间复杂度。 53 | -------------------------------------------------------------------------------- /leetcode/486. Predict the Winner/code.py: -------------------------------------------------------------------------------- 1 | class Solution(object): 2 | def PredictTheWinner(self, nums): 3 | """ 4 | :type nums: List[int] 5 | :rtype: bool 6 | """ 7 | n = len(nums) 8 | dp = [[0] * n for _ in range(n)] 9 | for i in range(n): 10 | dp[i][i] = nums[i] 11 | for d in range(1, n): 12 | for i in range(n-d): 13 | dp[i][i+d] = max(nums[i] - dp[i+1][i+d], nums[i+d] - dp[i][i+d-1]) 14 | return dp[0][n-1] >= 0 15 | 16 | print(Solution().PredictTheWinner([1, 5, 233, 7])) -------------------------------------------------------------------------------- /leetcode/486. Predict the Winner/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | https://leetcode.com/problems/predict-the-winner/ 4 | 5 | Given an array of scores that are non-negative integers. Player 1 picks one of the numbers from either end of the array followed by the player 2 and then player 1 and so on. Each time a player picks a number, that number will not be available for the next player. This continues until all the scores have been chosen. The player with the maximum score wins. 6 | 7 | Given an array of scores, predict whether player 1 is the winner. You can assume each player plays to maximize his score. 8 | 9 | ``` 10 | Example 1: 11 | Input: [1, 5, 2] 12 | Output: False 13 | Explanation: Initially, player 1 can choose between 1 and 2. 14 | If he chooses 2 (or 1), then player 2 can choose from 1 (or 2) and 5. If player 2 chooses 5, then player 1 will be left with 1 (or 2). 15 | So, final score of player 1 is 1 + 2 = 3, and player 2 is 5. 16 | Hence, player 1 will never be the winner and you need to return False. 17 | 18 | Example 2: 19 | Input: [1, 5, 233, 7] 20 | Output: True 21 | Explanation: Player 1 first chooses 1. Then player 2 have to choose between 5 and 7. No matter which number player 2 choose, player 1 can choose 233. 22 | Finally, player 1 has more score (234) than player 2 (12), so you need to return True representing player1 can win. 23 | ``` 24 | 25 | Note: 26 | - 1 <= length of the array <= 20. 27 | - Any scores in the given array are non-negative integers and will not exceed 10,000,000. 28 | - If the scores of both players are equal, then player 1 is still the winner. 29 | 30 | ### 思路 31 | 32 | 设dp[i][j]表示从i...j,自身可以比对方多拿的数。 33 | 34 | dp[i][j] = max(f[i] - dp[i+1][j], f[j] - dp[i][j-1]) -------------------------------------------------------------------------------- /leetcode/62. Unique Paths/code.py: -------------------------------------------------------------------------------- 1 | class Solution(object): 2 | def uniquePaths(self, m, n): 3 | """ 4 | :type m: int 5 | :type n: int 6 | :rtype: int 7 | """ 8 | dp = [[0] * m for _ in range(n)] 9 | for i in range(n): 10 | for j in range(m): 11 | if i == 0 and j == 0: 12 | dp[i][j] = 1 13 | elif i == 0: 14 | dp[i][j] = dp[i][j-1] 15 | elif j == 0: 16 | dp[i][j] = dp[i-1][j] 17 | else: 18 | dp[i][j] = dp[i-1][j] + dp[i][j-1] 19 | return dp[n-1][m-1] 20 | 21 | print(Solution().uniquePaths(7, 3)) -------------------------------------------------------------------------------- /leetcode/62. Unique Paths/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | https://leetcode.com/problems/unique-paths/ 4 | 5 | A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below). 6 | 7 | The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked 'Finish' in the diagram below). 8 | 9 | How many possible unique paths are there? 10 | ![](https://aigroupz-1258285787.cos.ap-shanghai.myqcloud.com/blog/15508034087703.jpg) 11 | Above is a 7 x 3 grid. How many possible unique paths are there? 12 | 13 | **Note:** m and n will be at most 100. 14 | 15 | ``` 16 | Example 1: 17 | 18 | Input: m = 3, n = 2 19 | Output: 3 20 | Explanation: 21 | From the top-left corner, there are a total of 3 ways to reach the bottom-right corner: 22 | 1. Right -> Right -> Down 23 | 2. Right -> Down -> Right 24 | 3. Down -> Right -> Right 25 | Example 2: 26 | 27 | Input: m = 7, n = 3 28 | Output: 28 29 | ``` 30 | 31 | 32 | ### 思路 33 | 34 | dp[i][j]表示从(0,0)到(i,j)的可到达数。 35 | 36 | dp[i][j] = dp[i-1][j] + dp[i][j-1] -------------------------------------------------------------------------------- /leetcode/64. Minimum Path Sum/code.py: -------------------------------------------------------------------------------- 1 | class Solution(object): 2 | def minPathSum(self, grid): 3 | """ 4 | :type grid: List[List[int]] 5 | :rtype: int 6 | """ 7 | dp = [[0] * len(grid[0]) for _ in range(len(grid))] 8 | for i in range(len(grid)): 9 | for j in range(len(grid[0])): 10 | if i == 0 and j == 0: 11 | dp[i][j] = grid[i][j] 12 | elif i == 0: 13 | dp[i][j] = dp[i][j-1] + grid[i][j] 14 | elif j == 0: 15 | dp[i][j] = dp[i-1][j] + grid[i][j] 16 | else: 17 | dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j] 18 | return dp[i][j] 19 | 20 | print(Solution().minPathSum([ 21 | [1,3,1], 22 | [1,5,1], 23 | [4,2,1] 24 | ])) 25 | 26 | -------------------------------------------------------------------------------- /leetcode/64. Minimum Path Sum/readme.md: -------------------------------------------------------------------------------- 1 | Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path. 2 | 3 | Note: You can only move either down or right at any point in time. 4 | 5 | Example: 6 | 7 | Input: 8 | [ 9 | [1,3,1], 10 | [1,5,1], 11 | [4,2,1] 12 | ] 13 | Output: 7 14 | Explanation: Because the path 1→3→1→1→1 minimizes the sum. 15 | 16 | 太简单不写思路了。 -------------------------------------------------------------------------------- /leetcode/646. Maximum Length of Pair Chain/code.py: -------------------------------------------------------------------------------- 1 | class Solution(object): 2 | def findLongestChain(self, pairs): 3 | """ 4 | :type pairs: List[List[int]] 5 | :rtype: int 6 | """ 7 | pairs.sort(key=lambda d:d[0]) 8 | n = len(pairs) 9 | dp = [1] * n 10 | for i in range(n): 11 | for j in range(i): 12 | if pairs[j][1] < pairs[i][0]: 13 | dp[i] = max(dp[i], dp[j]+1) 14 | return dp[n-1] 15 | 16 | 17 | class Solution1(object): 18 | def findLongestChain(self, pairs): 19 | """ 20 | :type pairs: List[List[int]] 21 | :rtype: int 22 | """ 23 | pairs.sort(key=lambda d:d[1]) 24 | res = 0 25 | cur = float('-inf') 26 | for p in pairs: 27 | if cur < p[0]: 28 | res += 1 29 | cur = p[1] 30 | return res 31 | 32 | print(Solution().findLongestChain([[2, 3],[1,2], [4, 5]])) -------------------------------------------------------------------------------- /leetcode/646. Maximum Length of Pair Chain/readme.md: -------------------------------------------------------------------------------- 1 | ## 题目描述 2 | 3 | https://leetcode.com/problems/maximum-length-of-pair-chain/ 4 | 5 | You are given n pairs of numbers. In every pair, the first number is always smaller than the second number. 6 | 7 | Now, we define a pair (c, d) can follow another pair (a, b) if and only if b < c. Chain of pairs can be formed in this fashion. 8 | 9 | Given a set of pairs, find the length longest chain which can be formed. You needn't use up all the given pairs. You can select pairs in any order. 10 | 11 | ``` 12 | Example 1: 13 | Input: [[1,2], [2,3], [3,4]] 14 | Output: 2 15 | Explanation: The longest chain is [1,2] -> [3,4] 16 | Note: 17 | The number of given pairs will be in the range [1, 1000]. 18 | ``` 19 | 20 | ## 思路 21 | 22 | 1. 按照元素第0位排序 23 | 24 | dp[init] = 1 25 | 26 | for i in range 0...n: 27 | for j in 0...i: 28 | dp[i] = max(dp[i], dp[j]+1 if A[j][1]= 3 4 | X_i + X_{i+1} = X_{i+2} for all i + 2 <= n 5 | Given a strictly increasing array A of positive integers forming a sequence, find the length of the longest fibonacci-like subsequence of A. If one does not exist, return 0. 6 | 7 | (Recall that a subsequence is derived from another sequence A by deleting any number of elements (including none) from A, without changing the order of the remaining elements. For example, [3, 5, 8] is a subsequence of [3, 4, 5, 6, 7, 8].) 8 | 9 | 10 | ``` 11 | Example 1: 12 | 13 | Input: [1,2,3,4,5,6,7,8] 14 | Output: 5 15 | Explanation: 16 | The longest subsequence that is fibonacci-like: [1,2,3,5,8]. 17 | Example 2: 18 | 19 | Input: [1,3,7,11,12,14,18] 20 | Output: 3 21 | Explanation: 22 | The longest subsequence that is fibonacci-like: 23 | [1,11,12], [3,11,14] or [7,11,18]. 24 | ``` 25 | 26 | Note: 27 | 28 | 3 <= A.length <= 1000 29 | 1 <= A[0] < A[1] < ... < A[A.length - 1] <= 10^9 30 | (The time limit has been reduced by 50% for submissions in Java, C, and C++.) 31 | -------------------------------------------------------------------------------- /leetcode/877-Stone Game/code.py: -------------------------------------------------------------------------------- 1 | class Solution(object): 2 | def stoneGame(self, piles): 3 | n = len(piles) 4 | dp = [[0] * n for _ in range(n)] 5 | for i in range(n): 6 | dp[i][i] = piles[i] 7 | for d in range(1, n): # 步宽 8 | for i in range(n-d): 9 | dp[i][i+d] = max(piles[i]-dp[i+1][i+d], piles[i+d]-dp[i][i+d-1]) 10 | return dp[0][len(piles)-1] > 0 11 | 12 | print(Solution().stoneGame([5,3,4,5])) 13 | 14 | -------------------------------------------------------------------------------- /leetcode/877-Stone Game/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | https://leetcode.com/problems/stone-game/ 4 | 5 | Alex and Lee play a game with piles of stones. There are an even number of piles arranged in a row, and each pile has a positive integer number of stones piles[i]. 6 | 7 | The objective of the game is to end with the most stones. The total number of stones is odd, so there are no ties. 8 | 9 | Alex and Lee take turns, with Alex starting first. Each turn, a player takes the entire pile of stones from either the beginning or the end of the row. This continues until there are no more piles left, at which point the person with the most stones wins. 10 | 11 | Assuming Alex and Lee play optimally, return True if and only if Alex wins the game. 12 | 13 | ``` 14 | Example 1: 15 | 16 | Input: [5,3,4,5] 17 | Output: true 18 | 19 | Explanation: 20 | Alex starts first, and can only take the first 5 or the last 5. 21 | Say he takes the first 5, so that the row becomes [3, 4, 5]. 22 | If Lee takes 3, then the board is [4, 5], and Alex takes 5 to win with 10 points. 23 | If Lee takes the last 5, then the board is [3, 4], and Alex takes 4 to win with 9 points. 24 | This demonstrated that taking the first 5 was a winning move for Alex, so we return true. 25 | 26 | 27 | Note: 28 | 29 | 2 <= piles.length <= 500 30 | piles.length is even. 31 | 1 <= piles[i] <= 500 32 | sum(piles) is odd. 33 | ``` 34 | 35 | Follow-up: 36 | 37 | - What if piles.length can be odd? 38 | - What if we want to know exactly the diffenerce of score? 39 | 40 | ### 思路-2D DP 41 | 42 | 参考:https://leetcode.com/problems/stone-game/discuss/154610/C%2B%2BJavaPython-DP-or-Just-return-true 43 | 44 | 45 | dp[i][j]表示在i到j的石头堆里,比对手取得更多石头的最大数 46 | 47 | Alex先手,可以选f[i]和f[j]: 48 | 49 | - 如果选f[i],对手Lee的选择是dp[i+1][j],因此Alex的结果是f[i] - dp[i+1][j] 50 | - 如果选f[j],同理Alex的结果是f[j] - dp[i][j-1] 51 | 52 | 因此DP公式为: 53 | dp[i][i] = f[i] 54 | dp[i][j] = max(f[i]-dp[i+1][j], f[j]-dp[i][j-1]) 55 | 56 | 57 | -------------------------------------------------------------------------------- /leetcode/931-Minimum Falling Path Sum/code.py: -------------------------------------------------------------------------------- 1 | class Solution(object): 2 | def minFallingPathSum(self, A): 3 | """ 4 | :type A: List[List[int]] 5 | :rtype: int 6 | """ 7 | dp = [[0]*len(A[0]) for _ in range(len(A))] 8 | for i in range(len(A)): 9 | for j in range(len(A[0])): 10 | if i == 0: 11 | dp[i][j] = A[i][j] 12 | else: 13 | dp[i][j] = A[i][j] + min(dp[i-1][j], dp[i-1][max(0, j-1)], dp[i-1][min(j+1, len(A[0])-1)]) 14 | return min(dp[len(A)-1]) 15 | 16 | print(Solution().minFallingPathSum([[1,2,3],[4,5,6],[7,8,9]])) -------------------------------------------------------------------------------- /leetcode/931-Minimum Falling Path Sum/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目链接 2 | 3 | https://leetcode.com/problems/minimum-falling-path-sum/ 4 | 5 | Given a square array of integers A, we want the minimum sum of a falling path through A. 6 | 7 | A falling path starts at any element in the first row, and chooses one element from each row. The next row's choice must be in a column that is different from the previous row's column by at most one. 8 | 9 | 10 | ``` 11 | Example 1: 12 | 13 | Input: [[1,2,3],[4,5,6],[7,8,9]] 14 | Output: 12 15 | ``` 16 | 17 | Explanation: 18 | The possible falling paths are: 19 | 20 | - [1,4,7], [1,4,8], [1,5,7], [1,5,8], [1,5,9] 21 | - [2,4,7], [2,4,8], [2,5,7], [2,5,8], [2,5,9], [2,6,8], [2,6,9] 22 | - [3,5,7], [3,5,8], [3,5,9], [3,6,8], [3,6,9] 23 | 24 | The falling path with the smallest sum is [1,4,7], so the answer is 12. 25 | 26 | Note: 27 | 28 | 1 <= A.length == A[0].length <= 100 29 | -100 <= A[i][j] <= 100 30 | 31 | 32 | 33 | ### 思路 34 | 35 | 我的一个直观算法是o(n^2)的时间和空间复杂度,dp公式如下: 36 | 37 | dp[i][j] = f[i][j] + min(dp[i-1][j], dp[i-1][j-1], dp[i-1][j+1]) 38 | 39 | 讨论区的代码是在原数组上操作,节省o(n^2)的空间,但是会修改数组。 -------------------------------------------------------------------------------- /leetcode/983-Minimum Cost For Tickets/code.py: -------------------------------------------------------------------------------- 1 | class Solution1(object): 2 | def mincostTickets(self, days, costs): 3 | """ 4 | :type days: List[int] 5 | :type costs: List[int] 6 | :rtype: int 7 | """ 8 | dp = [0] * 366 9 | for i in range(1, 366): 10 | if i in days: 11 | dp[i] = min(dp[max(i-1, 0)]+costs[0], dp[max(i-7, 0)]+costs[1], dp[max(i-30, 0)] + costs[2]) 12 | else: 13 | dp[i] = dp[i-1] 14 | return dp[365] 15 | 16 | class Solution2(object): 17 | def mincostTickets(self, days, costs): 18 | """ 19 | :type days: List[int] 20 | :type costs: List[int] 21 | :rtype: int 22 | """ 23 | last7, last30 = [], [] 24 | cost = 0 25 | for d in days: 26 | while last7 and last7[0][0] + 7 <= d: 27 | last7.pop(0) 28 | while last30 and last30[0][0] + 30 <= d: 29 | last30.pop(0) 30 | last7.append((d, cost+costs[1])) 31 | last30.append((d, cost+costs[2])) 32 | cost = min(cost+costs[0], last7[0][1], last30[0][1]) 33 | return cost 34 | 35 | print(Solution2().mincostTickets(days = [1,4,6,7,8,20], costs = [2,7,15])) -------------------------------------------------------------------------------- /leetcode/983-Minimum Cost For Tickets/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | https://leetcode.com/problems/minimum-cost-for-tickets/ 4 | 5 | In a country popular for train travel, you have planned some train travelling one year in advance. The days of the year that you will travel is given as an array days. Each day is an integer from 1 to 365. 6 | 7 | Train tickets are sold in 3 different ways: 8 | 9 | - a 1-day pass is sold for costs[0] dollars; 10 | - a 7-day pass is sold for costs[1] dollars; 11 | - a 30-day pass is sold for costs[2] dollars. 12 | 13 | The passes allow that many days of consecutive travel. For example, if we get a 7-day pass on day 2, then we can travel for 7 days: day 2, 3, 4, 5, 6, 7, and 8. 14 | 15 | Return the minimum number of dollars you need to travel every day in the given list of days. 16 | 17 | ``` 18 | Example 1: 19 | 20 | Input: days = [1,4,6,7,8,20], costs = [2,7,15] 21 | Output: 11 22 | Explanation: 23 | For example, here is one way to buy passes that lets you travel your travel plan: 24 | On day 1, you bought a 1-day pass for costs[0] = $2, which covered day 1. 25 | On day 3, you bought a 7-day pass for costs[1] = $7, which covered days 3, 4, ..., 9. 26 | On day 20, you bought a 1-day pass for costs[0] = $2, which covered day 20. 27 | In total you spent $11 and covered all the days of your travel. 28 | Example 2: 29 | 30 | Input: days = [1,2,3,4,5,6,7,8,9,10,30,31], costs = [2,7,15] 31 | Output: 17 32 | Explanation: 33 | For example, here is one way to buy passes that lets you travel your travel plan: 34 | On day 1, you bought a 30-day pass for costs[2] = $15 which covered days 1, 2, ..., 30. 35 | On day 31, you bought a 1-day pass for costs[0] = $2 which covered day 31. 36 | In total you spent $17 and covered all the days of your travel. 37 | ``` 38 | 39 | Note: 40 | 41 | 1 <= days.length <= 365 42 | 1 <= days[i] <= 365 43 | days is in strictly increasing order. 44 | costs.length == 3 45 | 1 <= costs[i] <= 1000 46 | 47 | ### 思路 48 | 49 | #### 思路1 50 | 51 | 日历共有365天,dp[i]表示第i天的最小花费,如果日历表上没有,则为第i-1天的最小花费,DP公式为: 52 | 53 | dp[i] = min(dp[i-1]+cost[0], dp[i-7]+cost[1], dp[i-30]+cost[2]) 54 | 55 | #### 思路2 56 | 57 | 用队列分别存储7天、30天最便宜的花费,最后取最小值。 58 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # 聊聊算法刷题 2 | 3 | 算法题本身是公司筛选人才的一种手段,对公司来说易操作、成本低(找个题库谁都能做面试官)、人才损失率低(能通过算法题的,做业务问题不大)。 4 | 5 | 作为找工作一方要接受游戏规则,但是心里应该明白刷题本身是没什么价值的。 6 | 7 | 个人认为应该集中花时间刷两个月左右即可。 8 | 9 | 笔者面试准备之余写了篇小结:[面试必刷-《剑指offer》刷题小结](https://mp.weixin.qq.com/s?__biz=MzU4OTczNTg2OQ==&mid=2247484491&idx=1&sn=d9b03c21782678bac8b3e825535f4224&chksm=fdc9b699cabe3f8fca01456fc2f1d23d9b8c27efc7582fdf8d3d42d05e97e8e30657776c5baf&scene=21&token=1446701999&lang=zh_CN#wechat_redirect) 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /剑指offer/001-二维数组查找/code.py: -------------------------------------------------------------------------------- 1 | 2 | def find(target, array): 3 | i = 0 4 | j = len(array[0]) - 1 5 | while i < len(array) and j >= 0: 6 | base = array[i][j] 7 | if target == base: 8 | return True 9 | elif target > base: 10 | i += 1 11 | else: 12 | j -= 1 13 | return False 14 | 15 | print(find(4,[[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]])) -------------------------------------------------------------------------------- /剑指offer/001-二维数组查找/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。 3 | 4 | ### 分治解题思路 5 | 6 | 从右上角(或者左下角)开始查找: 7 | 8 | - 如果target更大,指针下移 9 | - 如果target更小,指针左移 10 | -------------------------------------------------------------------------------- /剑指offer/002-替换空格/code.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | def replaceSpace(s): 4 | # write code here 5 | s_len = len(s) 6 | space_count = 0 7 | for i in s: 8 | if i == ' ': 9 | space_count += 1 10 | s_len += 2 * space_count 11 | new_array = [' '] * s_len 12 | j = 0 13 | for i in range(len(s)): 14 | if s[i] == ' ': 15 | new_array[j] = '%' 16 | new_array[j+1] = '2' 17 | new_array[j+2] = '0' 18 | j += 3 19 | else: 20 | new_array[j] = s[i] 21 | j += 1 22 | return ''.join(new_array) 23 | print(replaceSpace('We Are Happy')) -------------------------------------------------------------------------------- /剑指offer/002-替换空格/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。 3 | 4 | ### 思路 5 | 6 | 先遍历找到多少个空格,然后开辟数组填充 -------------------------------------------------------------------------------- /剑指offer/003-从尾到头打印链表/code.py: -------------------------------------------------------------------------------- 1 | 2 | class ListNode(object): 3 | def __init__(self, x): 4 | self.val = x 5 | self.next = None 6 | 7 | 8 | # 解法1:用辅助栈存储 9 | def printListFromTailToHead(listNode): 10 | # write code here 11 | stack = [] 12 | result_array = [] 13 | node_p = listNode 14 | while node_p: 15 | stack.append(node_p.val) 16 | node_p = node_p.next 17 | while stack: 18 | result_array.append(stack.pop(-1)) 19 | return result_array 20 | 21 | 22 | # 解法2:本身栈调用 23 | result_array = [] 24 | def printListFromTailToHead(listNode): 25 | # write code here 26 | if listNode: 27 | printListFromTailToHead(listNode.next) 28 | result_array.append(listNode.val) 29 | return result_array 30 | -------------------------------------------------------------------------------- /剑指offer/003-从尾到头打印链表/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。 4 | 5 | ### 思路 6 | 用栈存放,再输出。 -------------------------------------------------------------------------------- /剑指offer/004-重建二叉树/code.py: -------------------------------------------------------------------------------- 1 | class TreeNode: 2 | def __init__(self, x): 3 | self.val = x 4 | self.left = None 5 | self.right = None 6 | 7 | 8 | # 返回构造的TreeNode根节点 9 | def reConstructBinaryTree(pre, tin): 10 | # write code here 11 | if not pre: 12 | return None 13 | root_val = pre[0] 14 | root = TreeNode(root_val) 15 | for i in range(len(tin)): 16 | if tin[i] == root_val: 17 | break 18 | root.left = reConstructBinaryTree(pre[1:1+i], tin[:i]) 19 | root.right = reConstructBinaryTree(pre[1+i:], tin[i+1:]) 20 | 21 | return root 22 | 23 | def preorder(root): 24 | if root: 25 | preorder(root.left) 26 | print(root.val) 27 | preorder(root.right) 28 | preorder(reConstructBinaryTree([1,2,3,4,5,6,7],[3,2,4,1,6,5,7])) -------------------------------------------------------------------------------- /剑指offer/004-重建二叉树/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。 4 | 5 | ### 思路 6 | 7 | - 用前序遍历找到根结点 8 | - 用根结点在中序遍历中切开左右子树,递归重建二叉树 -------------------------------------------------------------------------------- /剑指offer/005-用两个栈实现队列/code.py: -------------------------------------------------------------------------------- 1 | class CQueue(object): 2 | 3 | def __init__(self): 4 | self.stack1 = [] 5 | self.stack2 = [] 6 | 7 | def appendTail(self, value): 8 | self.stack1.append(value) 9 | 10 | def deleteHead(self): 11 | if not self.stack2: 12 | while self.stack1: 13 | self.stack2.append(self.stack1.pop(-1)) 14 | if len(self.stack2) == 0: 15 | return -1 16 | return self.stack2.pop(-1) 17 | -------------------------------------------------------------------------------- /剑指offer/005-用两个栈实现队列/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。 3 | 4 | ### 思路 5 | 一个栈用来存储 6 | pop时弹出stack2,stack2为空,pop出stack1存储在stack2中 -------------------------------------------------------------------------------- /剑指offer/006-旋转数组的最小数字/code.py: -------------------------------------------------------------------------------- 1 | class Solution(object): 2 | def minArray(self, numbers): 3 | low, high = 0, len(numbers) - 1 4 | while low < high: 5 | mid = (high+low) / 2 6 | if numbers[mid] > numbers[high]: 7 | low = mid + 1 8 | elif numbers[mid] < numbers[high]: 9 | high = mid 10 | else: 11 | if (numbers[high - 1] > numbers[high]): # 确保正确的下标 12 | low = high 13 | break 14 | high -= 1 # 如果numbers[hign-1]=numbers[high]的情况 15 | return numbers[low] 16 | -------------------------------------------------------------------------------- /剑指offer/006-旋转数组的最小数字/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。 3 | 4 | ### 思路 5 | 总体二分: 6 | - if mid大于high, low = mid - 1 7 | - if mid小于high, high = mid 8 | - 直到mid=high,取此位置的数 9 | -------------------------------------------------------------------------------- /剑指offer/007-斐波拉契数列/code.py: -------------------------------------------------------------------------------- 1 | def Fibonacci(n): 2 | # write code here 3 | f1 = 0 4 | f2 = 1 5 | if n == 0: return f1 6 | elif n == 1: return f2 7 | for _ in range(n-1): 8 | f2, f1 = f1+f2, f2 9 | return f2 10 | print(Fibonacci(3)) -------------------------------------------------------------------------------- /剑指offer/007-斐波拉契数列/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。 3 | n<=39 4 | 5 | ### 思路 6 | 7 | $$f(n)=f(n-1)+f(n-2)$$ -------------------------------------------------------------------------------- /剑指offer/008-跳台阶/code.py: -------------------------------------------------------------------------------- 1 | class Solution(object): 2 | def numWays(self, n): 3 | if n == 0: return 1 4 | f1 = 1 5 | f2 = 2 6 | if n == 1: return f1 7 | if n == 2: return f2 8 | for _ in range(n-2): 9 | f2, f1 = f1+f2, f2 10 | return f2 % 1000000007 11 | -------------------------------------------------------------------------------- /剑指offer/008-跳台阶/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。 3 | 4 | ### 思路 5 | 6 | 假设对于第n级台阶,总共有f(n)种跳法. 7 | 8 | 那么f(n) = f(n-1) + f(n-2),其中f(1)=1,f(2)=2 -------------------------------------------------------------------------------- /剑指offer/009-变态跳台阶/code.py: -------------------------------------------------------------------------------- 1 | def jumpFloorII(number): 2 | # write code here 3 | f1 = 1 4 | if number == 1: return f1 5 | for _ in range(number-1): 6 | f1 = 2*f1 7 | return f1 8 | -------------------------------------------------------------------------------- /剑指offer/009-变态跳台阶/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。 3 | 4 | ### 思路 5 | 6 | f(1)=1, f(2)=2, f(3)=4, f(4)=8 设n+1级f(n+1),有 7 | 8 | f(n+1) = f(1) + f(2) + ... + f(n) 9 | 10 | f(n+2) = f(1) + f(2) + ... + f(n+1) 11 | 12 | f(n+2)= 2f(1) + 2f(2) + ... + 2f(n) 13 | 14 | 故得f(n+2) = 2f(n+1) -------------------------------------------------------------------------------- /剑指offer/010-矩形覆盖/code.py: -------------------------------------------------------------------------------- 1 | def rectCover(number): 2 | # write code here 3 | f1 = 1 4 | f2 = 2 5 | if number == 1: return f1 6 | if number == 2: return f2 7 | for _ in range(number-2): 8 | f2, f1 = f1 + f2, f2 9 | return f2 10 | 11 | print(rectCover(3)) -------------------------------------------------------------------------------- /剑指offer/010-矩形覆盖/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 我们可以用 2 * 1 的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2 * 1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法? 4 | 5 | ### 思路 6 | 7 | f(1) = 1 8 | f(2) = 2 9 | 10 | f(n) = f(n-1) + f(n-2) -------------------------------------------------------------------------------- /剑指offer/011-二进制中1的个数/code.py: -------------------------------------------------------------------------------- 1 | from ctypes import * 2 | def NumberOf1(n): 3 | # write code here 4 | cnt = 0 5 | while c_int(n).value: 6 | n = n & (n-1) 7 | cnt += 1 8 | print(c_int(n), n) 9 | return cnt 10 | 11 | print(NumberOf1(-3)) 12 | 13 | 14 | 15 | 16 | # # -*- coding:utf-8 -*- 17 | # class Solution: 18 | # def NumberOf1(self, n): 19 | # # write code here 20 | # return bin(n).replace("0b","").count("1") if n>=0 else bin(2**32+n).replace("0b","").count("1") 21 | -------------------------------------------------------------------------------- /剑指offer/011-二进制中1的个数/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。 4 | 5 | ### 思路 6 | 7 | 如果n!=0,n的二进制中至少有一个1 8 | 9 | - 如果1在最低位,n-1 & n得到的数正好将这个1,变成0 10 | - 如果1不在最低位,n-1 & n得到的数正好将这个1,变成0 11 | 12 | 因此我们判断n-1 & n能够循环运行的次数就可以判断二进制中有多少个1了。 13 | 14 | 在python中需要使用c_int()函数不然负数不会变成0. 15 | -------------------------------------------------------------------------------- /剑指offer/012-数值的整数次方/code.py: -------------------------------------------------------------------------------- 1 | 2 | def Power(base, exponent): 3 | # write code here 4 | if exponent == 0: return 1 5 | flag = 1 6 | if exponent < 0: 7 | exponent *= -1 8 | flag = 0 9 | temp = base 10 | res = 1 11 | while(exponent): 12 | if exponent & 1: 13 | res *= temp 14 | temp *= temp 15 | exponent = exponent >> 1 16 | 17 | return res if flag else 1.0/res 18 | 19 | print(Power(2, 1)) -------------------------------------------------------------------------------- /剑指offer/012-数值的整数次方/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。 3 | 4 | 5 | 指数幂的所有边界包括: 6 | 7 | - 指数为0的情况,不管底数是多少都应该是1 8 | - 指数为负数的情况,求出的应该是其倒数幂的倒数 9 | - 指数为负数的情况下,底数不能为0 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /剑指offer/013-调整数组顺序使奇数位于偶数前面/code.py: -------------------------------------------------------------------------------- 1 | def reOrderArray(array): 2 | # write code here 3 | odd_cnt = 0 4 | res = [0] * len(array) 5 | # 统计个数 6 | for n in array: 7 | if n % 2 != 0: 8 | odd_cnt += 1 9 | # 填坑 10 | odd_i = 0 11 | even_i = odd_cnt 12 | for i in range(len(array)): 13 | if array[i] % 2 != 0: 14 | res[odd_i] = array[i] 15 | odd_i += 1 16 | else: 17 | res[even_i] = array[i] 18 | even_i += 1 19 | return res 20 | 21 | 22 | -------------------------------------------------------------------------------- /剑指offer/013-调整数组顺序使奇数位于偶数前面/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。 4 | 5 | ### 思路 6 | 7 | 遍历一次,统计奇数个数。 8 | 9 | 然后从前往后填坑。 -------------------------------------------------------------------------------- /剑指offer/014-链表中倒数第k个结点/code.py: -------------------------------------------------------------------------------- 1 | class ListNode: 2 | def __init__(self, x): 3 | self.val = x 4 | self.next = None 5 | 6 | 7 | def FindKthToTail(head, k): 8 | # write code here 9 | if not head: return None 10 | fast_p = head 11 | slow_p = head 12 | for _ in range(k): 13 | if fast_p: 14 | fast_p = fast_p.next 15 | else: 16 | return None 17 | while fast_p: 18 | fast_p = fast_p.next 19 | slow_p = slow_p.next 20 | return slow_p 21 | -------------------------------------------------------------------------------- /剑指offer/014-链表中倒数第k个结点/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 输入一个链表,输出该链表中倒数第k个结点。 4 | 5 | ### 思路 6 | 7 | 用快慢指针,快指针比慢指针快k步,到尾结点了慢指针就是倒数第k个结点。 -------------------------------------------------------------------------------- /剑指offer/015-反转链表/code.py: -------------------------------------------------------------------------------- 1 | class ListNode: 2 | def __init__(self, x): 3 | self.val = x 4 | self.next = None 5 | 6 | def ReverseList(pHead): 7 | # write code here 8 | 9 | if not pHead: return None 10 | head = ListNode(0) 11 | head.next = pHead 12 | p = pHead 13 | while(p.next): 14 | tp = p.next 15 | p.next = p.next.next 16 | tp.next = head.next 17 | head.next = tp 18 | return head.next 19 | 20 | 21 | # head->4->3->2->1->5 22 | # p tp 23 | -------------------------------------------------------------------------------- /剑指offer/015-反转链表/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 输入一个链表,反转链表后,输出新链表的表头。 4 | 5 | ### 思路 6 | 7 | 假设翻转1->2->3->4->5,步骤如下: 8 | 9 | ``` 10 | head->4->3->2->1->5 11 | p tp 12 | ``` 13 | - 1.我们将p的next指向tp的next 14 | - 2.将tp的next指向head的next 15 | - 3.将head的next指向tp 16 | -------------------------------------------------------------------------------- /剑指offer/016-合并有序链表/code.py: -------------------------------------------------------------------------------- 1 | class ListNode: 2 | def __init__(self, x): 3 | self.val = x 4 | self.next = None 5 | 6 | def Merge(pHead1, pHead2): 7 | # write code here 8 | res = ListNode(0) 9 | head = res 10 | while pHead1 and pHead2: 11 | if pHead1.val <= pHead2.val: 12 | head.next = pHead1 13 | head = head.next 14 | pHead1 = pHead1.next 15 | else: 16 | head.next = pHead2 17 | head = head.next 18 | pHead2 = pHead2.next 19 | 20 | if pHead1: 21 | head.next = pHead1 22 | if pHead2: 23 | head.next = pHead2 24 | return res.next 25 | -------------------------------------------------------------------------------- /剑指offer/016-合并有序链表/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。 4 | 5 | ### 思路 6 | 7 | 新开个链表,按大小存储即可 -------------------------------------------------------------------------------- /剑指offer/017-树的子结构/code.py: -------------------------------------------------------------------------------- 1 | class TreeNode: 2 | def __init__(self, x): 3 | self.val = x 4 | self.left = None 5 | self.right = None 6 | 7 | def helper(treeA, treeB): 8 | if not treeB: 9 | return True 10 | elif not treeA: 11 | return False 12 | elif treeA.val != treeB.val: 13 | return False 14 | else: 15 | return helper(treeA.left, treeB.left) and helper(treeA.right, treeB.right) 16 | 17 | 18 | def HasSubtree(pRoot1, pRoot2): 19 | # write code here 20 | if not pRoot1 or not pRoot2: return False 21 | # 2 是不是 1的子树 22 | res = False 23 | if pRoot1.val == pRoot2.val: 24 | res = helper(pRoot1, pRoot2) 25 | if res: 26 | return True 27 | else: 28 | return HasSubtree(pRoot1.left, pRoot2) or HasSubtree(pRoot1.right, pRoot2) 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /剑指offer/017-树的子结构/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构) 4 | 5 | ### 思路 6 | 7 | - 1. 遍历父结构 8 | - 2. 判断子结构是否相同 9 | -------------------------------------------------------------------------------- /剑指offer/018-二叉树的镜像/code.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | def Mirror(root): 4 | # write code here 5 | if not root: 6 | return None 7 | tmp = Mirror(root.right) 8 | root.right = Mirror(root.left) 9 | root.left = tmp 10 | return root -------------------------------------------------------------------------------- /剑指offer/018-二叉树的镜像/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 操作给定的二叉树,将其变换为源二叉树的镜像。 3 | 输入描述: 4 | 5 | ``` 6 | 二叉树的镜像定义:源二叉树 7 | 8 8 | / \ 9 | 6 10 10 | / \ / \ 11 | 5 7 9 11 12 | 镜像二叉树 13 | 8 14 | / \ 15 | 10 6 16 | / \ / \ 17 | 11 9 7 5 18 | ``` 19 | 20 | ### 思路 21 | 22 | 左右交换 -------------------------------------------------------------------------------- /剑指offer/019-顺时针打印矩阵/code.py: -------------------------------------------------------------------------------- 1 | def printMatrix(matrix): 2 | # write code here 3 | walked = [[False] * (len(matrix[0])+1) for _ in range(len(matrix)+1)] 4 | for j in range(len(walked[-1])): 5 | walked[-1][j] = True 6 | for i in range(len(walked)): 7 | walked[i][-1] = True 8 | len_row = len(matrix) - 1 9 | len_col = len(matrix[0]) - 1 10 | res = [] 11 | i = 0 12 | j = 0 13 | direction = 0 # 0向右,1向下,2向左,3向上 14 | 15 | while not walked[i][j]: 16 | res.append(matrix[i][j]) 17 | walked[i][j] = True 18 | if direction == 0: # right 19 | if j < len_col and not walked[i][j+1]: 20 | j += 1 21 | else: 22 | direction = 1 23 | i += 1 24 | elif direction == 1: # down 25 | if i < len_row and not walked[i+1][j]: 26 | i += 1 27 | else: 28 | direction = 2 29 | j -= 1 30 | elif direction == 2: # left 31 | if j > 0 and not walked[i][j-1]: 32 | j -= 1 33 | else: 34 | direction = 3 35 | i -= 1 36 | elif direction == 3: # up 37 | if i > 0 and not walked[i-1][j]: 38 | i -= 1 39 | else: 40 | direction = 0 41 | j += 1 42 | return res 43 | 44 | print(printMatrix([[1,2],[3,4]])) 45 | print(printMatrix([[1]])) 46 | -------------------------------------------------------------------------------- /剑指offer/019-顺时针打印矩阵/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10. 4 | 5 | ### 思路 6 | 7 | 1 2 3 4 8 | 5 6 7 8 9 | 9 10 11 12 10 | 13 14 15 16 11 | 12 | 用一个bool数组标记走过的路,到底了就按照 右→下→左→上→右 的方式修改运动方向。 -------------------------------------------------------------------------------- /剑指offer/020-包含min函数的栈/code.py: -------------------------------------------------------------------------------- 1 | class MinStack(object): 2 | 3 | def __init__(self): 4 | self.stack = [] 5 | self.min_stack = [] 6 | 7 | def push(self, node): 8 | # write code here 9 | self.stack.append(node) 10 | if not self.min_stack: 11 | self.min_stack.append(node) 12 | else: 13 | if self.min_stack[-1] < node: 14 | self.min_stack.append(self.min_stack[-1]) 15 | else: 16 | self.min_stack.append(node) 17 | def pop(self): 18 | # write code here 19 | self.stack.pop(-1) 20 | self.min_stack.pop(-1) 21 | 22 | def top(self): 23 | # write code here 24 | if self.stack: 25 | return self.stack[-1] 26 | else: 27 | return [] 28 | 29 | def min(self): 30 | # write code here 31 | return self.min_stack[-1] 32 | 33 | -------------------------------------------------------------------------------- /剑指offer/020-包含min函数的栈/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。 4 | 5 | ### 思路 6 | 7 | 用辅助栈存储当前data的最小值,辅助栈头即为min值。 -------------------------------------------------------------------------------- /剑指offer/021-栈的压入、弹出序列/code.py: -------------------------------------------------------------------------------- 1 | def IsPopOrder(pushV, popV): 2 | # write code here 3 | stack = [] 4 | i = 0 5 | for v in pushV: 6 | stack.append(v) 7 | while stack and stack[-1] == popV[i]: 8 | i += 1 9 | stack.pop(-1) 10 | if not stack: 11 | return True 12 | else: 13 | return False 14 | 15 | 16 | print(IsPopOrder([1, 2, 3, 4, 5], [4,3,5,1,2])) -------------------------------------------------------------------------------- /剑指offer/021-栈的压入、弹出序列/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的) 4 | 5 | ### 思路 6 | 7 | 遍历压入栈,存储于栈中,遍历过程中,如果栈顶是出栈结点,推出值。 8 | 9 | - 最终栈空则弹出序列有效 10 | - 栈不空则弹出序列无效 11 | -------------------------------------------------------------------------------- /剑指offer/022-从上往下打印二叉树/code.py: -------------------------------------------------------------------------------- 1 | def PrintFromTopToBottom(root): 2 | # write code here 3 | if not root: return [] 4 | queue = [root] 5 | res = [] 6 | while queue: 7 | n = len(queue) 8 | for _ in range(n): 9 | if not queue: break 10 | node = queue.pop(0) 11 | res.append(node.val) 12 | if node.left: 13 | queue.append(node.left) 14 | if node.right: 15 | queue.append(node.right) 16 | return res 17 | -------------------------------------------------------------------------------- /剑指offer/022-从上往下打印二叉树/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 从上往下打印出二叉树的每个节点,同层节点从左至右打印。 3 | 4 | ### 思路 5 | 6 | 二叉树层次遍历,用队列存储每层结点,再依次弹出。 -------------------------------------------------------------------------------- /剑指offer/023-二叉搜索树的后序遍历序列/code.py: -------------------------------------------------------------------------------- 1 | def helper(sequence): 2 | if len(sequence) <= 1: return True 3 | root = sequence[-1] 4 | for i in range(len(sequence)): 5 | if sequence[i] > root: 6 | break 7 | for j in range(i, len(sequence)-1): 8 | if sequence[j] < root: 9 | return False 10 | return helper(sequence[:i]) and helper(sequence[i:-1]) 11 | 12 | 13 | def VerifySquenceOfBST(sequence): 14 | # write code here 15 | if not sequence: return False 16 | return helper(sequence) 17 | print(VerifySquenceOfBST([7,4,6,5])) -------------------------------------------------------------------------------- /剑指offer/023-二叉搜索树的后序遍历序列/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。 4 | 5 | ### 思路 6 | 7 | 8 | 二叉搜索树有: 9 | 10 | - 结点值:左<根<右 11 | - 左右子树都是搜索树 12 | 13 | 后序遍历顺序为:左->右->根 14 | 15 | - 最后一个数为根结点,通过根节点值切割左右子树。 16 | - 判断左右子树是否二叉搜索树 17 | 18 | 对于[4,8,6,12,16,14,10] 19 | 20 | ``` 21 | 10 22 | 6 14 23 | 4 8 12 16 24 | ``` 25 | 26 | -------------------------------------------------------------------------------- /剑指offer/024-二叉树中和为某一值的路径/code.py: -------------------------------------------------------------------------------- 1 | ### 前序遍历,深度优先遍历dfs 2 | class Solution(object): 3 | def __init__(self): 4 | self.result_all = [] 5 | self.array = [] 6 | 7 | def pathSum(self, root, expectNumber): 8 | if not root: return [] 9 | self.array.append(root.val) 10 | expectNumber -= root.val 11 | if expectNumber == 0 and not root.left and not root.right: 12 | self.result_all.append(self.array[:]) 13 | self.pathSum(root.left, expectNumber) 14 | self.pathSum(root.right, expectNumber) 15 | self.array.pop() 16 | return self.result_all 17 | -------------------------------------------------------------------------------- /剑指offer/024-二叉树中和为某一值的路径/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的靠前) 4 | 5 | ### 思路 6 | 7 | 先序遍历: 8 | 9 | - 每次访问一个节点,那么就将当前权值求和 10 | - 如果当前权值和与期待的和一致,那么说明我们找到了一个路径,保存或者输出 11 | - 每次深度遍历到底部,回退一个点 12 |  -------------------------------------------------------------------------------- /剑指offer/025-复杂链表的复制/code.py: -------------------------------------------------------------------------------- 1 | class RandomListNode: 2 | def __init__(self, x): 3 | self.label = x 4 | self.next = None 5 | self.random = None 6 | 7 | 8 | # 返回 RandomListNode 9 | def Clone(pHead): 10 | # write code here 11 | if not pHead: return None 12 | p = pHead 13 | new_h = RandomListNode(p.label) 14 | pre_p = new_h 15 | random_map = {pHead: new_h} 16 | p = p.next 17 | while p: 18 | new_p = RandomListNode(p.label) 19 | random_map[p] = new_p 20 | pre_p.next = new_p 21 | pre_p = pre_p.next 22 | p = p.next 23 | p = pHead 24 | new_p = new_h 25 | while p: 26 | random_p = p.random 27 | if random_p: 28 | new_p.random = random_map[random_p] 29 | 30 | p = p.next 31 | new_p = new_p.next 32 | 33 | return new_h 34 | 35 | p = RandomListNode(1) 36 | p.next = RandomListNode(2) 37 | p.next.next = RandomListNode(3) 38 | p.next.next.next = RandomListNode(4) 39 | p.next.next.next.next = RandomListNode(5) 40 | print(Clone(p)) -------------------------------------------------------------------------------- /剑指offer/025-复杂链表的复制/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空) 4 | 5 | ### 思路 6 | 7 | 方案1: 8 | 9 | - 第一步:遍历老链表,构建正常的链表,用一个map记录p到new_p 10 | - 第二步:新老链表同步next移动,对比记录random指针。 11 | 12 | p 1->2->3->4 13 | map | | | | 14 | new_p 1->2->3->4 15 | 16 | 需要借助O(n)的空间,时间复杂度为o(n) 17 | 18 | 方案2: 19 | 20 | 不需要借助O(n)的空间,时间复杂度为o(n) 21 | 22 | 老新链表交叉存储,奇数位置为老链表,偶数位置新链表复制前一个位置。 23 | 24 | 新链表random即为旧链表random的后一个位置。 25 | 26 | p1->p1'->p2->p2'->...->pn->pn' -------------------------------------------------------------------------------- /剑指offer/026-二叉搜索树与双向链表/code.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | # class TreeNode: 3 | # def __init__(self, x): 4 | # self.val = x 5 | # self.left = None 6 | # self.right = None 7 | class Solution: 8 | def __init__(self): 9 | self.pre = None 10 | 11 | def Convert(self, root): 12 | # write code here 13 | if not root: return None 14 | self.helper(root) 15 | while root.left: 16 | root = root.left 17 | return root 18 | 19 | def helper(self, cur): 20 | if not cur: return None 21 | self.helper(cur.left) 22 | cur.left = self.pre 23 | if self.pre: 24 | self.pre.right = cur 25 | self.pre = cur 26 | self.helper(cur.right) 27 | 28 | -------------------------------------------------------------------------------- /剑指offer/026-二叉搜索树与双向链表/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。 4 | 5 | ### 思路 6 | 7 | 二叉搜索树性质有: 8 | 9 | - 没有相同结点 10 | - 值:左<根<右 11 | 12 | 因此我们需要,中序遍历中: 13 | 14 | - pre.right = curr 15 | - curr.left = pre 16 | -------------------------------------------------------------------------------- /剑指offer/027-字符串的排列/code.py: -------------------------------------------------------------------------------- 1 | 2 | def helper(s): 3 | if len(s) == 1: 4 | return s[0] 5 | res = [] 6 | for i in range(len(s)): 7 | l = helper(s[:i] + s[i+1:]) 8 | for j in l: 9 | res.append(s[i] + j) 10 | 11 | return res 12 | 13 | def Permutation(ss): 14 | # write code here 15 | if not ss: return [] 16 | words = list(ss) 17 | return list(sorted(set(helper(words)))) 18 | 19 | print(Permutation('aa')) 20 | -------------------------------------------------------------------------------- /剑指offer/027-字符串的排列/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。 4 | 5 | 输入描述: 6 | 7 | > 输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。 8 | 9 | 10 | ### 思路 11 | 12 | 取出第i个数,全排列其他非i位置的数拼在后面。 13 | -------------------------------------------------------------------------------- /剑指offer/028-数组中出现次数超过一半的数字/code.py: -------------------------------------------------------------------------------- 1 | def MoreThanHalfNum_Solution(numbers): 2 | # write code here 3 | res = None 4 | cnt = 0 5 | for i in numbers: # 留下数组中出现次数最高的数 6 | if not res: 7 | res = i 8 | cnt = 1 9 | else: 10 | if i == res: 11 | cnt += 1 12 | else: 13 | cnt -= 1 14 | if cnt == 0: 15 | res = None 16 | # 判断次数是否大于一半 17 | cnt = 0 18 | for i in numbers: 19 | if i == res: 20 | cnt += 1 21 | if cnt > len(numbers) / 2: 22 | return res 23 | else: 24 | return 0 25 | 26 | print(MoreThanHalfNum_Solution([1,2,3,2,2,2,5,4,2])) 27 | 28 | -------------------------------------------------------------------------------- /剑指offer/028-数组中出现次数超过一半的数字/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。 4 | 5 | 6 | ### 思路 7 | 8 | - 第一步:记录数组中次数出现最多的个数 9 | - 第二步:判断这个数是否出现次数超过一半 10 | 11 | O(n)复杂度。 12 | 13 | 14 | ### 扩展问题 15 | 16 | 17 | 如果有且只有一个的出现最多的那个数字出现的次数是数组长度的一半呢?又或者是一半减1? 18 | 我们还是继续从我们之前的那道超过一半的数来入手,我们的"阵地攻守"的解法是每遇到2个不同的数,就删除,剩下的就是那个出现次数超过一半的数字。 19 | 20 | 这个方法对于超过一半的情况可以成立,对于扩展问题就不再行得通了。 21 | 22 | 但是他们的本质是相同的, 23 | 24 | - 对于一个长度为2n的数组,如果有个数出现次数超过n,那么至少有1组,连续2个数是重复的相应的,于是"阵地攻守"中每次不同的元素就删除(同归于尽)就可以找到那个元素 25 | - 如果出现的次数为n, 那么至少有一组连续3个数是重复的。每3个不重复的数删除一次 26 | - 如果是n-1, 就是一组至少有连续4个数重复。每4个不重复的数删除一次 27 | 28 | -------------------------------------------------------------------------------- /剑指offer/029-最小的K个数/code.py: -------------------------------------------------------------------------------- 1 | 2 | def sink(array, k): 3 | n = len(array) 4 | left = 2 * k + 1 5 | right = 2 * k + 2 6 | if left >= n: return 7 | min_i = left 8 | if right < n and array[left] > array[right]: 9 | min_i = right 10 | if array[min_i] < array[k]: 11 | array[min_i], array[k] = array[k], array[min_i] 12 | sink(array, min_i) 13 | 14 | def build_heap(list): 15 | n = len(list) 16 | for i in range(n//2, -1, -1): 17 | sink(list, i) 18 | 19 | return list 20 | 21 | def GetLeastNumbers_Solution(tinput, k): 22 | if k > len(tinput): return [] 23 | heap = build_heap(tinput) # 建堆o(n)复杂度 24 | res = [] 25 | for _ in range(k): # 取topk o(klogn)复杂度 26 | heap[0], heap[-1] = heap[-1], heap[0] 27 | res.append(heap.pop()) 28 | sink(heap, 0) 29 | return res 30 | 31 | 32 | print(GetLeastNumbers_Solution([4,5,1,6,2,7,3,8],4)) -------------------------------------------------------------------------------- /剑指offer/029-最小的K个数/partition.py: -------------------------------------------------------------------------------- 1 | def partition(input, low, high): 2 | pivot = input[low] 3 | while low < high: 4 | while low < high and pivot <= input[high]: 5 | high -= 1 6 | input[low] = input[high] 7 | while low < high and pivot >= input[low]: 8 | low += 1 9 | input[high] = input[low] 10 | input[low] = pivot 11 | return low 12 | 13 | 14 | def GetLeastNumbers_Solution(tinput, k): 15 | if k > len(tinput) or k <= 0: return [] 16 | idx = partition(tinput, 0, len(tinput) - 1) 17 | low = 0 18 | high = len(tinput) - 1 19 | while idx != k - 1: 20 | if idx < k - 1: 21 | low = idx + 1 22 | idx = partition(tinput, low, high) 23 | if idx > k - 1: 24 | high = idx - 1 25 | idx = partition(tinput, low, high) 26 | return tinput[:k] 27 | 28 | 29 | print(GetLeastNumbers_Solution([4,5,1,6,2,7,3,8],4)) 30 | print(GetLeastNumbers_Solution([-4, 2, 1, 23, 7, 2, 18, 23, 42, 37, 8], 4)) -------------------------------------------------------------------------------- /剑指offer/029-最小的K个数/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。 3 | 4 | ### 思路 5 | 6 | 思路1: 7 | 8 | - 最小堆建立(需要o(n)时间复杂度) 9 | - 取出前k个数(每次取需要用logn时间重建堆)。时间复杂度为o(n)+o(k*logn) 10 | 11 | 思路2: 12 | 13 | - 用快排partition划分,一直划中第k个数 最差情况o(kn) 14 | 15 | -------------------------------------------------------------------------------- /剑指offer/030-连续子数组的最大和/code.py: -------------------------------------------------------------------------------- 1 | def FindGreatestSumOfSubArray(array): 2 | # write code here 3 | max = array[0] 4 | dp = [0] * (len(array) + 1) 5 | dp[0] = array[0] 6 | for i in range(1, len(array)): 7 | if dp[i-1] < 0: 8 | dp[i] = array[i] 9 | else: 10 | dp[i] = array[i] + dp[i-1] 11 | if dp[i] > max: 12 | max = dp[i] 13 | return max 14 | 15 | 16 | print(FindGreatestSumOfSubArray([6,-3,-2,7,-15,1,2,2])) 17 | -------------------------------------------------------------------------------- /剑指offer/030-连续子数组的最大和/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1) 4 | 5 | ### 思路 6 | 7 | 思路1, 动态规划: 8 | 9 | dp[i] = dp[i-1] + p[i] # if i != 0 and dp[i-1] > 0 10 | dp[i] = p[i] # if i == 0 or dp[i-1] < 0 11 | 12 | -------------------------------------------------------------------------------- /剑指offer/031-整数中1出现的次数(从1到n整数中1出现的次数)/code.py: -------------------------------------------------------------------------------- 1 | def NumberOf1Between1AndN_Solution(n): 2 | # write code here 3 | res = 0 4 | i = 1 # 个位开始 5 | while n // i != 0: 6 | high = n//(i*10) # 高位数 7 | current = (n//i) % 10 # 第i位数 8 | low = n - (n//i) * i # 低位数 9 | if current == 0: 10 | res += high * i 11 | elif current == 1: 12 | res += high * i + low + 1 13 | else: 14 | res += (high+1) * i 15 | i *= 10 16 | return res 17 | 18 | print(NumberOf1Between1AndN_Solution(20)) 19 | 20 | -------------------------------------------------------------------------------- /剑指offer/031-整数中1出现的次数(从1到n整数中1出现的次数)/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。 4 | 5 | ### 思路 6 | 7 | 以百位为例: 8 | 9 | - 百位数为0,情况由更高位决定,10022,百位数字出现1的情况:100-199,1100-1199,2100-2199,...9100-9199,共100*10=1000种。即高位(10) * 位置(100) 10 | - 百位数为1,情况由更高位和百位决定,10122,百位数字出现1的情况为:100-199, 1100-1199, 2100-2199,...9100-9199,10100-10122,共1000+23种。即高位(10) * 位置(100) + 低位(23) 11 | - 百位数大于1,10222,百位数出现1的情况为: 100-199, 1100-1199, ...9100-9199, 10100-10199 共1100种。即(高位(10)+1) * 位置(100) 12 | 13 | 最终统计所有位置可能的1的情况。时间复杂度O(logn) 14 | -------------------------------------------------------------------------------- /剑指offer/032-把数组排成最小的数/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。 4 | 5 | ### 思路 6 | 7 | 本题主要是找排序方法判断所有数的顺序方法。 8 | 9 | #### 思路1 10 | 11 | - 1.全部用个位数+长度补齐,比如{3, 32, 321}补齐成{3331, 3222, 3213},{1, 11, 111}补齐成{1111, 1112, 1113} o(nlogn) 12 | - 2.排序,从小到大取下标 o(nlogn) 13 | - 3.根据下标的数组装结果 14 | 15 | #### 思路2 16 | 17 | - 1.比较n1与n2,转成字符串s1与s2 18 | - 2.比较s1s2与s2s1大小,小的放前面 19 | - 3.依次输出所有字符串 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /剑指offer/032-把数组排成最小的数/solution1.py: -------------------------------------------------------------------------------- 1 | def number_len(n): 2 | res = 0 3 | while n: 4 | n //= 10 5 | res += 1 6 | return res 7 | 8 | 9 | def PrintMinNumber(numbers): 10 | # write code here 11 | if len(numbers) == 1: return numbers[0] 12 | max_len = number_len(max(numbers)) 13 | map = {} 14 | for i in range(len(numbers)): 15 | n = numbers[i] 16 | pad = n % 10 17 | n_len = number_len(n) 18 | for j in range(max_len-n_len): 19 | n = n*10+pad 20 | map[n*10+n_len] = i 21 | keys = sorted(map.keys()) 22 | res = numbers[map[keys[0]]] 23 | for i in range(1, len(keys)): 24 | n = numbers[map[keys[i]]] 25 | res = res * 10 ** number_len(n) + n 26 | return res 27 | 28 | 29 | 30 | # map = {numbers[i]:i for i in range(len(numbers))} 31 | 32 | print(PrintMinNumber([11,1,111])) -------------------------------------------------------------------------------- /剑指offer/032-把数组排成最小的数/solution2.py: -------------------------------------------------------------------------------- 1 | import functools 2 | def compare(s1, s2): 3 | if s1+s2 < s2+s1: 4 | return -1 5 | elif s1+s2 == s2+s1: 6 | return 0 7 | else: 8 | return 1 9 | 10 | def PrintMinNumber(numbers): 11 | # write code here 12 | if not numbers: return '' 13 | if len(numbers) == 1: return numbers[0] 14 | str_numbers = [str(n) for n in numbers] 15 | 16 | return ''.join(sorted(str_numbers, key=functools.cmp_to_key(compare))) 17 | 18 | print(PrintMinNumber([32, 3, 321])) 19 | 20 | 21 | -------------------------------------------------------------------------------- /剑指offer/033-丑数/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。 4 | 5 | ### 思路 6 | 7 | 思路1.暴力法 8 | 9 | 需要遍历所有数,o(n^2)复杂度,超时。 10 | 11 | 思路2.暴力+存储 12 | 13 | 也需要遍历所有数,只是反证丑数时节约时间,o(n^2)复杂度,依旧超时。 14 | 15 | 思路3.存储最小丑数,依次向上取,o(n)复杂度。 16 | 17 | 18 | -------------------------------------------------------------------------------- /剑指offer/033-丑数/solution1&2.py: -------------------------------------------------------------------------------- 1 | # 1 2 3 4 5 6 8 2 | 3 | # def is_ugly(n): 4 | # while n % 2 == 0: 5 | # n /= 2 6 | # while n % 3 == 0: 7 | # n /= 3 8 | # while n % 5 == 0: 9 | # n /= 5 10 | # return n == 1 11 | 12 | def GetUglyNumber_Solution(index): 13 | # write code here 14 | if index <= 0: return 0 15 | if index == 1: return 1 16 | uglys = {1} 17 | cnt = 0 18 | i = 0 19 | while cnt < index-1: 20 | i += 1 21 | if i/2.0 in uglys or i/3.0 in uglys or i/5.0 in uglys: 22 | cnt += 1 23 | uglys.add(i) 24 | 25 | return i 26 | 27 | print(GetUglyNumber_Solution(700)) -------------------------------------------------------------------------------- /剑指offer/033-丑数/solution3.py: -------------------------------------------------------------------------------- 1 | def GetUglyNumber_Solution(index): 2 | # write code here 3 | if index <= 1: return index 4 | res = [1] * index 5 | i2, i3, i5 = 0, 0, 0 6 | for i in range(1, index): 7 | res[i] = min(res[i2]*2, min(res[i3]*3, res[i5]*5)) 8 | if res[i] == res[i2]*2: i2 += 1 9 | if res[i] == res[i3]*3: i3 += 1 10 | if res[i] == res[i5]*5: i5 += 1 11 | return res[-1] 12 | 13 | 14 | print(GetUglyNumber_Solution(700)) -------------------------------------------------------------------------------- /剑指offer/034-第一个只出现一次的字符/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写). 4 | 5 | ### 思路 6 | 7 | 反向遍历,哈希表存储字母数量,最后一个数量为1的字母为最终的输出。 -------------------------------------------------------------------------------- /剑指offer/034-第一个只出现一次的字符/solution.py: -------------------------------------------------------------------------------- 1 | def FirstNotRepeatingChar(s): 2 | # write code here 3 | map = {} 4 | for i in range(len(s)): 5 | map[s[i]] = map.get(s[i], 0) + 1 6 | for i in range(len(s)): 7 | if map[s[i]] == 1: 8 | return i 9 | return -1 10 | 11 | print(FirstNotRepeatingChar('abac')) -------------------------------------------------------------------------------- /剑指offer/035-数组中的逆序对/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007 4 | 输入描述: 5 | 题目保证输入的数组中没有的相同的数字 6 | 7 | 数据范围: 8 | 9 | 对于%50的数据,size<=10^4 10 | 11 | 对于%75的数据,size<=10^5 12 | 13 | 对于%100的数据,size<=2*10^5 14 | 15 | 示例1 16 | 输入 17 | 1,2,3,4,5,6,7,0 18 | 输出 19 | 7 20 | 21 | ### 思路 22 | 23 | 暴力法时间复杂度是o(n^2),超时。 24 | 25 | 考虑一下,逆序是说a[i]>a[j],i k: 7 | high = mid - 1 8 | elif data[mid] < k: 9 | low = mid + 1 10 | return high 11 | 12 | def GetNumberOfK(data, k): 13 | # write code here 14 | if not data: return 0 15 | return biSearch(data, k+0.5) - biSearch(data, k-0.5) 16 | 17 | print(GetNumberOfK([3,3,3, 4],3)) 18 | -------------------------------------------------------------------------------- /剑指offer/038-二叉树的深度/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。 4 | 5 | ### 思路 6 | 7 | 层序遍历,easy题。 -------------------------------------------------------------------------------- /剑指offer/038-二叉树的深度/solution.py: -------------------------------------------------------------------------------- 1 | class TreeNode: 2 | def __init__(self, x): 3 | self.val = x 4 | self.left = None 5 | self.right = None 6 | 7 | 8 | class Solution: 9 | def TreeDepth(self, pRoot): 10 | # write code here 11 | if not pRoot: return 0 12 | queue = [pRoot] 13 | deep = 0 14 | while queue: 15 | n = len(queue) 16 | for _ in range(n): 17 | node = queue.pop(0) 18 | if node.left: queue.append(node.left) 19 | if node.right: queue.append(node.right) 20 | deep += 1 21 | return deep 22 | -------------------------------------------------------------------------------- /剑指offer/039-平衡二叉树/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 输入一棵二叉树,判断该二叉树是否是平衡二叉树。 3 | 4 | ### 思路 5 | 6 | 思路1:非剪枝 7 | 8 | 递归查看每棵子树是否满足平衡二叉树,o(nlogn)复杂度。 9 | 10 | 思路2:剪枝 11 | 12 | 看子树是否是平衡二叉树,如果不是返回-1,如果是返回长度。 13 | 14 | -------------------------------------------------------------------------------- /剑指offer/039-平衡二叉树/solution1.py: -------------------------------------------------------------------------------- 1 | class TreeNode: 2 | def __init__(self, x): 3 | self.val = x 4 | self.left = None 5 | self.right = None 6 | 7 | def getDeep(x): 8 | if x is None: 9 | return 0 10 | return 1 + max(getDeep(x.left), getDeep(x.right)) 11 | 12 | class Solution: 13 | def IsBalanced_Solution(self, pRoot): 14 | # write code here 15 | if not pRoot: 16 | return 1 17 | return abs(getDeep(pRoot.left)-getDeep(pRoot.right)) <= 1 \ 18 | and IsBalanced_Solution(pRoot.left) \ 19 | and IsBalanced_Solution(pRoot.right) 20 | 21 | -------------------------------------------------------------------------------- /剑指offer/039-平衡二叉树/solution2.py: -------------------------------------------------------------------------------- 1 | def getDeep(x): 2 | if x is None: 3 | return 0 4 | left = getDeep(x.left) 5 | if left == -1: 6 | return -1 7 | right = getDeep(x.right) 8 | if right == -1: 9 | return -1 10 | return -1 if abs(left-right) > 1 else 1 + max(left, right) 11 | 12 | 13 | class Solution: 14 | def IsBalanced_Solution(self, pRoot): 15 | # write code here 16 | return getDeep(pRoot) != -1 17 | -------------------------------------------------------------------------------- /剑指offer/040-数组中只出现一次的数字/code.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | # 返回[a,b] 其中ab是出现一次的两个数字 3 | def FindNumsAppearOnce(self, array): 4 | # write code here 5 | map = {} 6 | res = [] 7 | for n in array: 8 | map[n] = map.get(n, 0) + 1 9 | for n in array: 10 | if map[n] == 1: 11 | res.append(n) 12 | return res 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /剑指offer/040-数组中只出现一次的数字/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 一个整型数组里除了两个数字之外,其他的数字都出现了偶数次。请写程序找出这两个只出现一次的数字。 4 | 5 | ### 思路 6 | 7 | 8 | 位运算中异或的性质:两个相同数字异或=0,一个数和0异或还是它本身。感觉意义不大,不看了。 9 | 10 | -------------------------------------------------------------------------------- /剑指offer/041-和为S的连续正数序列/code.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def FindContinuousSequence(self, tsum): 3 | # write code here 4 | res = [] 5 | windows = [] 6 | sum = 0 7 | for t in range(1, tsum): 8 | windows.append(t) 9 | sum += t 10 | while sum > tsum: 11 | sum -= windows.pop(0) 12 | if sum == tsum: 13 | res.append(windows[:]) 14 | 15 | return res 16 | 17 | print(Solution().FindContinuousSequence(100)) 18 | 19 | -------------------------------------------------------------------------------- /剑指offer/041-和为S的连续正数序列/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck! 4 | 输出描述: 5 | 输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序 6 | 7 | 8 | ### 思路 9 | 10 | 用滑动窗口抓取 -------------------------------------------------------------------------------- /剑指offer/042-和为S的两个数字/code.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def FindNumbersWithSum(self, array, tsum): 3 | # write code here 4 | if not array: return [] 5 | lp = 0 6 | rp = len(array) - 1 7 | res = [array[-1]] * 2 8 | while lp < rp: 9 | tmp = array[lp] + array[rp] 10 | if tmp > tsum: 11 | rp -= 1 12 | elif tmp < tsum: 13 | lp += 1 14 | else: 15 | if array[lp] * array[rp] < res[0] * res[1]: 16 | res[0], res[1] = array[lp], array[rp] 17 | lp += 1 18 | rp -= 1 19 | 20 | return res if res[0] + res[1] == tsum else [] 21 | 22 | print(Solution().FindNumbersWithSum([], 0)) -------------------------------------------------------------------------------- /剑指offer/042-和为S的两个数字/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。 4 | 输出描述: 5 | 对应每个测试案例,输出两个数,小的先输出。 6 | 7 | ### 思路 8 | 9 | 左右指针,和大于target,右指针左移,和小于target,左指针右移。 10 | -------------------------------------------------------------------------------- /剑指offer/043-左旋转字符串/code.py: -------------------------------------------------------------------------------- 1 | def reverse(str, s, e): 2 | e -= 1 3 | while s < e: 4 | str[s], str[e] = str[e], str[s] 5 | s += 1 6 | e -= 1 7 | 8 | class Solution: 9 | 10 | def LeftRotateString(self, s, n): 11 | # write code here 12 | if len(s) == 0 or n == 0: return s 13 | s = list(s) 14 | reverse(s, 0, n) 15 | reverse(s, n, len(s)) 16 | reverse(s, 0, len(s)) 17 | return ''.join(s) 18 | 19 | print(Solution().LeftRotateString('abcdef', 3)) 20 | 21 | 22 | -------------------------------------------------------------------------------- /剑指offer/043-左旋转字符串/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它! 4 | 5 | ### 思路 6 | 7 | 第一反应 8 | 9 | ``` 10 | return s[n:] + s[:n]。 11 | ``` 12 | 13 | 这道题考的核心是应聘者是不是可以灵活利用字符串翻转。假设字符串abcdef,n=3,设X=abc,Y=def,所以字符串可以表示成XY,如题干,问如何求得YX。假设X的翻转为XT,XT=cba,同理YT=fed,那么YX=(XTYT)T,三次翻转后可得结果。 14 | 15 | 16 | -------------------------------------------------------------------------------- /剑指offer/044-翻转单词顺序列/code.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def ReverseSentence(self, s): 3 | # write code here 4 | stack = [n for n in s.split(' ')] 5 | stack.reverse() 6 | return ' '.join(stack) 7 | 8 | print(Solution().ReverseSentence("I am a student.")) -------------------------------------------------------------------------------- /剑指offer/044-翻转单词顺序列/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么? 4 | 5 | ### 思路 6 | 7 | 考察翻转,可以通过空格隔开,然后用栈存储,最后弹出。 -------------------------------------------------------------------------------- /剑指offer/045-扑克牌顺子/code.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def IsContinuous(self, numbers): 3 | # write code here 4 | if not numbers: return 0 5 | numbers.sort() 6 | cnt = 0 7 | jokers = 0 8 | pre = None 9 | for i in range(len(numbers)): 10 | if numbers[i] == 0: 11 | jokers += 1 12 | else: 13 | if pre is None: 14 | pre = numbers[i] 15 | else: 16 | if pre == numbers[i]: return 0 17 | cnt += numbers[i] - pre - 1 18 | pre = numbers[i] 19 | cnt -= jokers 20 | return cnt <= 0 21 | 22 | print(Solution().IsContinuous([0,3,1,6,4])) -------------------------------------------------------------------------------- /剑指offer/045-扑克牌顺子/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张^_^)...他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子.....LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何, 如果牌能组成顺子就输出true,否则就输出false。为了方便起见,你可以认为大小王是0。 4 | 5 | ### 思路 6 | 7 | 思路1: 8 | - 1. 排序 9 | - 2. 计算顺差 10 | 11 | 思路2: 12 | 13 | - 1.最大值-最小值 < 5 14 | - 2.不能有对 15 | - 3.必须有牌 16 | -------------------------------------------------------------------------------- /剑指offer/046-孩子们的游戏-圆圈中最后剩下的数/code.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def LastRemaining_Solution(self, n, m): 3 | # write code here 4 | n = list(range(n)) 5 | if not n: return -1 6 | i = 0 7 | while len(n) != 1: 8 | for _ in range(m-1): 9 | i += 1 10 | i %= len(n) 11 | n.pop(i) 12 | return n 13 | 14 | print(Solution().LastRemaining_Solution(5, 3)) -------------------------------------------------------------------------------- /剑指offer/046-孩子们的游戏-圆圈中最后剩下的数/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1) 4 | 5 | ### 思路 6 | 7 | 约瑟夫环(未解决) -------------------------------------------------------------------------------- /剑指offer/047-求1+2+3+...+n/code.py: -------------------------------------------------------------------------------- 1 | def sum(n): 2 | try: 3 | 1 % n 4 | return n + sum(n-1) 5 | except: 6 | return 0 7 | 8 | 9 | class Solution: 10 | def Sum_Solution(self, n): 11 | return sum(n) 12 | 13 | 14 | print(Solution().Sum_Solution(10)) -------------------------------------------------------------------------------- /剑指offer/047-求1+2+3+...+n/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。 4 | 5 | ### 思路 6 | 7 | 短路思想感觉太偏了,工程上可以用try catch -------------------------------------------------------------------------------- /剑指offer/048-不用加减乘除做加法/code.py: -------------------------------------------------------------------------------- 1 | from ctypes import * 2 | 3 | class Solution: 4 | def Add(self, num1, num2): 5 | # write code here 6 | while num2 != 0: 7 | temp = c_int(num1 ^ num2).value # 不带进位的相加结果 8 | num2 = c_int((num1 & num2) << 1).value # 带进位 9 | num1 = temp 10 | return num1 11 | 12 | print(Solution().Add(-1, 23)) 13 | 14 | -------------------------------------------------------------------------------- /剑指offer/048-不用加减乘除做加法/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。 4 | 5 | ### 思路 6 | 7 | 参考链接:https://www.nowcoder.com/questionTerminal/59ac416b4b944300b617d4f7f111b215 8 | 9 | 首先看十进制是如何做的: 5+7=12,三步走 10 | 第一步:相加各位的值,不算进位,得到2。 11 | 第二步:计算进位值,得到10. 如果这一步的进位值为0,那么第一步得到的值就是最终结果。 12 | 13 | 第三步:重复上述两步,只是相加的值变成上述两步的得到的结果2和10,得到12。 14 | 15 | 同样我们可以用三步走的方式计算二进制值相加: 5-101,7-111 第一步:相加各位的值,不算进位,得到010,二进制每位相加就相当于各位做异或操作,101^111。 16 | 17 | 第二步:计算进位值,得到1010,相当于各位做与操作得到101,再向左移一位得到1010,(101&111)<<1。 18 | 19 | 第三步重复上述两步, 各位相加 010^1010=1000,进位值为100=(010&1010)<<1。 20 | 继续重复上述两步:1000^100 = 1100,进位值为0,跳出循环,1100为最终结果。 -------------------------------------------------------------------------------- /剑指offer/049-把字符串转换成整数/code.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def StrToInt(self, s): 3 | # write code here 4 | res = 0 5 | flag = 1 6 | for i in range(len(s)): 7 | if i == 0 and s[i] == '+': 8 | continue 9 | elif i == 0 and s[i] == '-': 10 | flag = -1 11 | continue 12 | n = ord(s[i]) - ord('0') 13 | if n>=0 and n<=9: 14 | res = 10 * res + n 15 | else: 16 | return False 17 | return res * flag 18 | 19 | print(Solution().StrToInt('-1234')) 20 | -------------------------------------------------------------------------------- /剑指offer/049-把字符串转换成整数/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 将一个字符串转换成一个整数(实现Integer.valueOf(string)的功能,但是string不符合数字要求时返回0),要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0。 4 | 5 | 输入描述: 6 | 输入一个字符串,包括数字字母符号,可以为空 7 | 8 | 输出描述: 9 | 如果是合法的数值表达则返回该数字,否则返回0 10 | -------------------------------------------------------------------------------- /剑指offer/050-数组中重复的数字/code.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | # 这里要特别注意~找到任意重复的一个值并赋值到duplication[0] 3 | # 函数返回True/False 4 | def duplicate(self, numbers, duplication): 5 | # write code here 6 | map = [0] * 1000 7 | for n in numbers: 8 | if map[n] == 1: 9 | duplication[0] = n 10 | return True 11 | else: 12 | map[n] = 1 13 | return False -------------------------------------------------------------------------------- /剑指offer/050-数组中重复的数字/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。 4 | 5 | ### 思路 6 | 7 | 用了空间,可能面试会要求不能用空间吧 -------------------------------------------------------------------------------- /剑指offer/051-构建乘积数组/code.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def multiply(self, A): 3 | # write code here 4 | B = [1] * len(A) 5 | temp = 1 6 | for i in range(1, len(A)): 7 | temp *= A[i-1] 8 | B[i] *= temp 9 | temp = 1 10 | for i in range(len(A)-2, -1, -1): 11 | temp *= A[i+1] 12 | B[i] *= temp 13 | return B 14 | 15 | 16 | # [0, 1, 1] 17 | # [1, 0, 1] 18 | # [1, 1, 0] -------------------------------------------------------------------------------- /剑指offer/051-构建乘积数组/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 给定一个数组A[0,1,...,n-1],请构建一个数组B[0,1,...,n-1],其中B中的元素B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。不能使用除法。 4 | 5 | 6 | ### 思路 7 | 8 | 先算下三角,再算上三角。O(n)复杂度 9 | -------------------------------------------------------------------------------- /剑指offer/052-正则表达式匹配/code.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class Solution: 4 | # s, pattern都是字符串 5 | def match(self, s, pattern): 6 | # write code here 7 | s = '^' + s 8 | pattern = '^' + pattern 9 | i = len(s) - 1 10 | j = len(pattern) - 1 11 | star_match = False 12 | while i>=0 and j>=0: 13 | if star_match: 14 | if pattern[j] == '.' or pattern[j] == s[i]: 15 | if self.match(s[1:i], pattern[1:j]): # 先靠前匹配,从1开始是去掉index为0的^ 16 | return True 17 | else: 18 | i -= 1 19 | else: 20 | j -= 1 21 | star_match = False 22 | else: 23 | if s[i] == pattern[j] or pattern[j] == '.': 24 | i -= 1 25 | j -= 1 26 | elif pattern[j] == '*': 27 | star_match = True 28 | j -= 1 29 | else: 30 | return False 31 | return (i == -1 and j == -1) 32 | 33 | 34 | print(Solution().match('aa', 'a*')) -------------------------------------------------------------------------------- /剑指offer/052-正则表达式匹配/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 请实现一个函数用来匹配包括'.'和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配 4 | 5 | 6 | ### 思路 7 | 8 | 9 | 从后往前比较,递归判断,判断条件太多。无法AC 10 | -------------------------------------------------------------------------------- /剑指offer/053-表示数值的字符串/code.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | # s字符串 3 | def isNumeric(self, s): 4 | # write code here 5 | sign, point, hasE = False, False, False 6 | for i in range(len(s)): 7 | if s[i].lower() == 'e': 8 | if hasE: return False 9 | if i == len(s)-1: return False 10 | hasE = True 11 | elif s[i] == '+' or s[i] == '-': 12 | if sign and s[i-1].lower() != 'e': return False 13 | if not sign and i > 0 and s[i-1].lower() != 'e': return False 14 | sign = True 15 | elif s[i] == '.': 16 | if hasE or point: return False 17 | point = True 18 | elif ord(s[i]) < ord('0') or ord(s[i]) > ord('9'): 19 | return False 20 | return True -------------------------------------------------------------------------------- /剑指offer/053-表示数值的字符串/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。 -------------------------------------------------------------------------------- /剑指offer/054-字符流中第一个不重复的字符/code.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | # 返回对应char 3 | def __init__(self): 4 | self.data = [] 5 | def FirstAppearingOnce(self): 6 | # write code here 7 | return self.data[0] if self.data else '#' 8 | def Insert(self, char): 9 | # write code here 10 | n = len(self.data) 11 | for i in range(n-1, -1, -1): 12 | if self.data[i] == char: 13 | self.data.pop(i) 14 | return 15 | self.data.append(char) 16 | 17 | 18 | s = Solution() 19 | print(s.Insert('g')) 20 | print(s.Insert('o')) 21 | print(s.Insert('o')) 22 | print(s.Insert('g')) 23 | print(s.Insert('l')) 24 | print(s.Insert('e')) -------------------------------------------------------------------------------- /剑指offer/054-字符流中第一个不重复的字符/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。 4 | 5 | ``` 6 | 输出描述: 7 | 如果当前字符流没有存在出现一次的字符,返回#字符。 8 | ``` -------------------------------------------------------------------------------- /剑指offer/055-链表中环的入口结点/code.py: -------------------------------------------------------------------------------- 1 | class ListNode: 2 | def __init__(self, x): 3 | self.val = x 4 | self.next = None 5 | 6 | class Solution: 7 | def EntryNodeOfLoop(self, pHead): 8 | # write code here 9 | head = ListNode(0) 10 | head.next = pHead 11 | fast = head 12 | slow = head 13 | while True: 14 | if fast and fast.next: 15 | fast = fast.next.next 16 | slow = slow.next 17 | else: 18 | return None 19 | if fast == slow: 20 | fast = head 21 | break 22 | while fast != slow: 23 | fast = fast.next 24 | slow = slow.next 25 | return fast -------------------------------------------------------------------------------- /剑指offer/055-链表中环的入口结点/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。 4 | 5 | -------------------------------------------------------------------------------- /剑指offer/056-删除链表中重复的结点/code.py: -------------------------------------------------------------------------------- 1 | class ListNode: 2 | def __init__(self, x): 3 | self.val = x 4 | self.next = None 5 | 6 | 7 | class Solution: 8 | def deleteDuplication(self, pHead): 9 | # write code here 10 | head = ListNode(0) 11 | head.next = pHead 12 | pre = head 13 | p = head.next 14 | while p and p.next: 15 | if p.next.val == p.val: 16 | while p.next and p.next.val == p.val: 17 | p.next = p.next.next 18 | pre.next = p.next 19 | p = pre.next 20 | else: 21 | pre = p 22 | p = p.next 23 | return head.next -------------------------------------------------------------------------------- /剑指offer/056-删除链表中重复的结点/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5 4 | -------------------------------------------------------------------------------- /剑指offer/057-二叉树的下一个结点/code.py: -------------------------------------------------------------------------------- 1 | class TreeLinkNode: 2 | def __init__(self, x): 3 | self.val = x 4 | self.left = None 5 | self.right = None 6 | self.next = None 7 | 8 | class Solution: 9 | def GetNext(self, pNode): 10 | # write code here 11 | if not pNode: return None 12 | p = pNode 13 | if pNode.right: # 有右子树 14 | p = pNode.right 15 | while p.left: 16 | p = p.left 17 | return p 18 | # 没有右子树但是有父亲节点 19 | while pNode.next and pNode.next.right == pNode: 20 | pNode = pNode.next 21 | return pNode.next -------------------------------------------------------------------------------- /剑指offer/057-二叉树的下一个结点/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。 -------------------------------------------------------------------------------- /剑指offer/058-对称的二叉树/code.py: -------------------------------------------------------------------------------- 1 | class TreeNode: 2 | def __init__(self, x): 3 | self.val = x 4 | self.left = None 5 | self.right = None 6 | 7 | def isEqual(p1, p2): 8 | if not p1 and not p2: 9 | return True 10 | elif p1 and p2 and p1.val == p2.val: 11 | return True 12 | else: 13 | return False 14 | 15 | class Solution: 16 | def isSymmetrical(self, pRoot): 17 | # write code here 18 | if not pRoot: return True 19 | queue = [pRoot] 20 | while queue: 21 | n = len(queue) 22 | if queue[0] != pRoot and n % 2 != 0: 23 | return False 24 | for i in range(n//2): 25 | if not isEqual(queue[i], queue[-1-i]): 26 | return False 27 | for _ in range(n): 28 | node = queue.pop(0) 29 | if not node: continue 30 | queue.append(node.left) 31 | queue.append(node.right) 32 | return True -------------------------------------------------------------------------------- /剑指offer/058-对称的二叉树/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。 4 | 5 | ### 思路 6 | 7 | 思路1. 正常的层序遍历 8 | 9 | 思路2. 两种中序遍历 10 | 11 | - 1. 左->根->右 12 | - 2. 右->根->左 13 | -------------------------------------------------------------------------------- /剑指offer/059-按之字形顺序打印二叉树/code.py: -------------------------------------------------------------------------------- 1 | 2 | class Solution: 3 | def Print(self, pRoot): 4 | # write code here 5 | if not pRoot: return [] 6 | res = [] 7 | queue = [pRoot] 8 | j = -1 9 | while queue: 10 | j += 1 11 | n = len(queue) 12 | temp = [] 13 | for _ in range(n): 14 | node = queue.pop(0) 15 | temp.append(node.val) 16 | if node.left: queue.append(node.left) 17 | if node.right: queue.append(node.right) 18 | if j % 2: 19 | temp.reverse() 20 | res.append(temp) 21 | return res -------------------------------------------------------------------------------- /剑指offer/059-按之字形顺序打印二叉树/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。 4 | 5 | ### 思路 6 | 7 | 层序打印即可。 -------------------------------------------------------------------------------- /剑指offer/060-把二叉树打印成多行/code.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | # 返回二维列表[[1,2],[4,5]] 3 | def Print(self, pRoot): 4 | # write code here 5 | if not pRoot: return [] 6 | queue = [pRoot] 7 | res = [] 8 | while queue: 9 | n = len(queue) 10 | temp = [] 11 | for _ in range(n): 12 | node = queue.pop(0) 13 | temp.append(node.val) 14 | if node.left: queue.append(node.left) 15 | if node.right: queue.append(node.right) 16 | res.append(temp) 17 | return res 18 | -------------------------------------------------------------------------------- /剑指offer/060-把二叉树打印成多行/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。 4 | 5 | 6 | ### 思路 7 | 8 | 层序打印即可。 -------------------------------------------------------------------------------- /剑指offer/061-序列化二叉树/code.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | class TreeNode: 3 | def __init__(self, x): 4 | self.val = x 5 | self.left = None 6 | self.right = None 7 | 8 | class Solution: 9 | def __init__(self): 10 | self.index = -1 11 | 12 | def Serialize(self, root): 13 | # write code here 14 | if root: 15 | return str(root.val) + ' ' +\ 16 | self.Serialize(root.left) + \ 17 | self.Serialize(root.right) 18 | else: 19 | return '# ' 20 | 21 | def Deserialize(self, s): 22 | # write code here 23 | l = s.split() 24 | self.index += 1 25 | if self.index >= len(s): 26 | return None 27 | 28 | if l[self.index] != '#': 29 | root = TreeNode(int(l[self.index])) 30 | root.left = self.Deserialize(s) 31 | root.right = self.Deserialize(s) 32 | else: 33 | return None 34 | return root 35 | 36 | 37 | root = TreeNode(1) 38 | root.left = TreeNode(2) 39 | root.right = TreeNode(3) 40 | # root.left.left = TreeNode(4) 41 | root.left.right = TreeNode(5) 42 | 43 | 44 | print(Solution().Serialize(root)) 45 | -------------------------------------------------------------------------------- /剑指offer/061-序列化二叉树/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 请实现两个函数,分别用来序列化和反序列化二叉树 -------------------------------------------------------------------------------- /剑指offer/062-二叉搜索树的第k个结点/code.py: -------------------------------------------------------------------------------- 1 | 2 | class Solution: 3 | # 返回对应节点TreeNode 4 | 5 | def KthNode(self, pRoot, k): 6 | # write code here 7 | if not pRoot: return None 8 | stack = [] 9 | while pRoot or stack: 10 | while pRoot: 11 | stack.append(pRoot) 12 | pRoot = pRoot.left 13 | pRoot = stack.pop() 14 | k -= 1 15 | if k == 0: 16 | return pRoot 17 | pRoot = pRoot.right -------------------------------------------------------------------------------- /剑指offer/062-二叉搜索树的第k个结点/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。 4 | 5 | ### 思路 6 | 7 | 中序遍历第k个数 -------------------------------------------------------------------------------- /剑指offer/063-数据流中的中位数/code.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | def __init__(self): 3 | self.sortedList = [] 4 | def Insert(self, num): 5 | # write code here 6 | n = len(self.sortedList) 7 | self.sortedList.append(num) 8 | while n != 0: 9 | if self.sortedList[n] < self.sortedList[n-1]: 10 | self.sortedList[n], self.sortedList[n-1] = self.sortedList[n-1], self.sortedList[n] 11 | else: 12 | break 13 | n -= 1 14 | if n == 0: 15 | self.sortedList[n] = num 16 | 17 | def GetMedian(self, x): 18 | # write code here 19 | n = len(self.sortedList) 20 | if n % 2 == 0: 21 | return (self.sortedList[n//2]+self.sortedList[n//2-1]) / 2.0 22 | else: 23 | return self.sortedList[n//2] -------------------------------------------------------------------------------- /剑指offer/063-数据流中的中位数/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。 4 | -------------------------------------------------------------------------------- /剑指offer/064-滑动窗口的最大值/code.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | class Solution: 3 | def maxInWindows(self, num, size): 4 | # write code here 5 | if size == 0: return [] 6 | queue = [] 7 | res = [] 8 | for i in range(len(num)): 9 | # 过期 10 | while queue and queue[0] <= i-size: 11 | queue.pop(0) 12 | # 挤走小数 13 | while queue and num[queue[-1]] < num[i]: 14 | queue.pop(-1) 15 | queue.append(i) 16 | if i < size - 1: 17 | continue 18 | res.append(num[queue[0]]) 19 | return res 20 | -------------------------------------------------------------------------------- /剑指offer/064-滑动窗口的最大值/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: 4 | 5 | - {[2,3,4],2,6,2,5,1} 6 | - {2,[3,4,2],6,2,5,1} 7 | - {2,3,[4,2,6],2,5,1} 8 | - {2,3,4,[2,6,2],5,1} 9 | - {2,3,4,2,[6,2,5],1} 10 | - {2,3,4,2,6,[2,5,1]} 11 | 12 | 13 | ### 思路 14 | 15 | 滑动窗口用队列保存,当遇到以下情况做相应处理: 16 | 17 | - 1. 窗口第一个槽始终是最大值 18 | - 2. 窗口第一个槽过期要丢弃 19 | - 3. 窗口从尾比到头,丢弃较小的数 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /剑指offer/065-矩阵中的路径/code.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | def DFS(matrix, row, col, path, visited): 3 | if row < 0 or row >= len(matrix) or col < 0 or col >= len(matrix[0]) or (row, col) in visited: 4 | return False 5 | if path[0] == matrix[row][col]: 6 | if len(path) == 1: 7 | return True 8 | return DFS(matrix, row+1, col, path[1:], visited| {(row, col)}) or \ 9 | DFS(matrix, row-1, col, path[1:], visited| {(row, col)}) or \ 10 | DFS(matrix, row, col-1, path[1:], visited| {(row, col)}) or \ 11 | DFS(matrix, row, col+1, path[1:], visited| {(row, col)}) 12 | 13 | class Solution: 14 | def hasPath(self, matrix, rows, cols, path): 15 | # write code here 16 | array = list(matrix) 17 | array = [array[i*cols:(i+1)*cols] for i in range(rows)] 18 | for i in range(rows): 19 | for j in range(cols): 20 | visited = set() 21 | if DFS(array, i, j, list(path), visited): 22 | return True 23 | return False 24 | 25 | print(Solution().hasPath("ABCESFCSADEE",3,4,"BCCED")) 26 | print(Solution().hasPath("ABCEHJIGSFCSLOPQADEEMNOEADIDEJFMVCEIFGGS",5,8,"SLHECCEIDEJFGGFIE")) 27 | print(Solution().hasPath("AAAAAAAAAAAA",3,4,"AAAAAAAAAAAA")) 28 | 29 | -------------------------------------------------------------------------------- /剑指offer/065-矩阵中的路径/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。 例如 a b c e s f c s a d e e 这样的3 X 4 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。 4 | 5 | ### 思路 6 | 7 | DFS+回溯 8 | -------------------------------------------------------------------------------- /剑指offer/066-机器人的运动范围/code.py: -------------------------------------------------------------------------------- 1 | class Solution(object): 2 | def movingCount(self, m, n, k): 3 | """ 4 | :type m: int 5 | :type n: int 6 | :type k: int 7 | :rtype: int 8 | """ 9 | rows = m 10 | cols = n 11 | threshold = k 12 | array = [] 13 | for i in range(rows): 14 | res = [] 15 | for j in range(cols): 16 | res.append(getN(i, j)) 17 | array.append(res) 18 | visited = [[0] * len(array[0]) for _ in range(len(array))] 19 | return BFS(array, 0, 0, threshold, visited) 20 | 21 | def getN(i, j): 22 | res = 0 23 | while i: 24 | res += (i % 10) 25 | i //= 10 26 | while j: 27 | res += (j % 10) 28 | j //= 10 29 | return res 30 | 31 | def BFS(array, i, j, threshold, visited): 32 | if i<0 or j<0 or i>len(array)-1 or j>len(array[0])-1 or array[i][j]>threshold or visited[i][j]: 33 | return 0 34 | res = 1 35 | visited[i][j] = 1 36 | res += BFS(array, i+1, j, threshold, visited) 37 | res += BFS(array, i-1, j, threshold, visited) 38 | res += BFS(array, i, j+1, threshold, visited) 39 | res += BFS(array, i, j-1, threshold, visited) 40 | return res 41 | -------------------------------------------------------------------------------- /剑指offer/066-机器人的运动范围/readme.md: -------------------------------------------------------------------------------- 1 | ### 题目描述 2 | 3 | 地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子? 4 | 5 | 6 | ### 思路 7 | 8 | BFS 9 | -------------------------------------------------------------------------------- /剑指offer/readme.md: -------------------------------------------------------------------------------- 1 | # 面试必刷-《剑指offer》刷题小结 2 | 3 | ![](https://aigroupz-1258285787.cos.ap-shanghai.myqcloud.com/blog/15490245570653.jpg) 4 | 5 | ### 写在前面 6 | 7 | 面试季来了,不管是作为面试者还是以后作为面试官,了解算法这门程序员之间的沟通方式都是非常必要的。 8 | 9 | 找过工作的朋友应该都听说过《剑指offer》,笔者也是战5渣,本文主要对我这十多天刷过的《剑指offer》做个简单的分类小结,方便后面专项复(练)习~ 10 | 11 | 剑指offer推荐刷题地址: 12 | https://leetcode-cn.com/problemset/lcof/ (官方授权) 13 | 14 | 我的所有AC题解(Python语言),每道题都写了点自己的思路: 15 | https://github.com/nlpjoe/Coding4Interviews 16 | 17 | 18 | 19 | ### 正文 20 | 21 | 笔者是NLP方向,这两篇相关方向的面经写得很不错: 22 | 23 | > https://zhuanlan.zhihu.com/p/36387348 24 | > 25 | > https://zhuanlan.zhihu.com/p/45802662 26 | 27 | 如果您是高分段选手,看完觉得不用刷题,请点(x)然后私信我,务必要收下我这个好朋友= ̄ω ̄= 28 | 29 | 《剑指offer》剖析了80个典型的编程面试题,系统整理基础知识、代码质量、解题思路、优化效率和综合能力这5个面试要点。但是我刷题只有牛客网上的66题。 30 | 31 | 如果是单纯的面试需求,剑指offer的优先级肯定是在Leetcode之前,总的说它有三个优点: 32 | 33 | - 1.很可能在面试中出现原题 34 | - 2.约66题,题量少,但是涵盖的内容较全 35 | - 3.能培养一个良好的刷题习惯 36 | 37 | 它的缺点是: 38 | 39 | - 1.**只有66题,刷着容易过拟合** 40 | - 2.动态规划的题比较少,因此需要在Leetcode上专项训练。 41 | 42 | 算法题主要分成**数据结构**和**具体算法**部分,简单归类如下。基本每道题都很精彩,所以这里就不一一洗写了,题解可以看看我的代码仓库或者讨论区的内容。 43 | 44 | ### 数据结构类题目 45 | 46 | - LinkedList 47 | - 003-从尾到头打印链表 48 | - 014-链表中倒数第k个结点 49 | - 015-反转链表 50 | - 016-合并两个或k个有序链表 51 | - 025-复杂链表的复制 52 | - 036-两个链表的第一个公共结点 53 | - 055-链表中环的入口结点 54 | - 056-删除链表中重复的结点 55 | - Tree 56 | - 004-重建二叉树 57 | - 017-树的子结构 58 | - 018-二叉树的镜像 59 | - 022-从上往下打印二叉树 60 | - 023-二叉搜索树的后序遍历序列 61 | - 024-二叉树中和为某一值的路径 62 | - **026-二叉搜索树与双向链表** 63 | - 038-二叉树的深度 64 | - 039-平衡二叉树 65 | - 057-二叉树的下一个结点 66 | - 058-对称的二叉树 67 | - 059-按之字形顺序打印二叉树 68 | - 060-把二叉树打印成多行 69 | - 061-序列化二叉树 70 | - 062-二叉搜索树的第k个结点 71 | - Stack & Queue 72 | - 005-用两个栈实现队列 73 | - 020-包含min函数的栈 74 | - 021-栈的压入、弹出序列 75 | - 044-翻转单词顺序列(栈) 76 | - 064-滑动窗口的最大值(双端队列) 77 | - Heap 78 | - **029-最小的K个数** 79 | - Hash Table 80 | - 034-第一个只出现一次的字符 81 | - 图 82 | - 065-矩阵中的路径(BFS) 83 | - 066-机器人的运动范围(DFS) 84 | 85 | ### 具体算法类题目 86 | 87 | - 斐波那契数列 88 | - 007-斐波拉契数列 89 | - 008-跳台阶 90 | - 009-变态跳台阶 91 | - 010-矩形覆盖 92 | - 搜索算法 93 | - 001-二维数组查找 94 | - 006-旋转数组的最小数字(二分查找) 95 | - 037-数字在排序数组中出现的次数(二分查找) 96 | - 全排列 97 | - 027-字符串的排列 98 | - 动态规划 99 | - 030-连续子数组的最大和 100 | - 052-正则表达式匹配(我用的暴力) 101 | - 回溯 102 | - 065-矩阵中的路径(BFS) 103 | - 066-机器人的运动范围(DFS) 104 | - 排序 105 | - 035-数组中的逆序对(归并排序) 106 | - **029-最小的K个数**(堆排序) 107 | - **029-最小的K个数**(快速排序) 108 | - 位运算 109 | - 011-二进制中1的个数 110 | - 012-数值的整数次方 111 | - 040-数组中只出现一次的数字 112 | - 其他算法 113 | - 002-替换空格 114 | - 013-调整数组顺序使奇数位于偶数前面 115 | - 028-数组中出现次数超过一半的数字 116 | - 031-整数中1出现的次数(从1到n整数中1出现的次数) 117 | - 032-把数组排成最小的数 118 | - 033-丑数 119 | - 041-和为S的连续正数序列(滑动窗口思想) 120 | - 042-和为S的两个数字(双指针思想) 121 | - 043-左旋转字符串(矩阵翻转) 122 | - 046-孩子们的游戏-圆圈中最后剩下的数(约瑟夫环) 123 | - 051-构建乘积数组 124 | 125 | ### 节奏与方法 126 | 127 | 我个人觉得数据结构和DP在面试中手写代码的几率比较高,因此笔者目前的刷题节奏主要是: 128 | 129 | > 剑指offer->Leetcode动态规划->面试前再过一遍剑指offer 130 | 131 | 每个人基础不一样,不过我觉得刷题还是要全职专项的刷。 132 | 133 | **有个重要的点是:每道题做完一定要去讨论区!** 134 | 135 | 讨论区有非常精简的大神级代码,你好不容易AC了一道题准备去讨论区吹(装)水(逼),点开一看,“握草,还可以这样”。 136 | 137 | 思考为什么他可以写出这么好的代码,**把每道题的思路理解后用笔记本记录下来**,争取刷到融会贯通,即看见有个题能自动归类到某个方面,这样有一定好处。面试最重要的是让面试官日后能愿意与你以后一起工作,因此沟通交流非常重要。比如有时候面试需要交流,看着像是一道排序的题做不出来,就可以跟面试官交流:“我有几个不成熟的想法,一排序,二动态规划,三是直接搜索算法”,面试官可能就给个提示:“你先用排序试试吧“。 138 | 139 | 当然笔者还没到这境界,等面试结束后再来写后续部分吧。 140 | 141 | ### 组队学习 142 | 143 | 刷题我是和几个熟人一起刷的。如果您找不到地方,可以关注其他小伙伴们的公众号Datawhale(开源学习平台),里面有Leetcode组队打卡刷题。 144 | 145 | 如果有面试刷题(剑指offer+Leetcode)的小伙伴,可以一起共享招聘信息&刷题交流= ̄ω ̄= 146 | 147 | 添加AI圈机器狗微信(Echoooo741)拉您入群。 148 | 149 | ![](https://aigroupz-1258285787.cos.ap-shanghai.myqcloud.com/blog/15491047309717.jpg) 150 | 151 | ![](https://aigroupz-1258285787.cos.ap-shanghai.myqcloud.com/blog/15491052742040.jpg) 152 | 153 | --------------------------------------------------------------------------------