剑指offer 手刷python 汇总整理版本~

文章目录

0.递归&脑力

python实现单例模式

#使用python 实现单例模式
#单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。
 
#1.__new__ 是什么
# __new__方法接受的参数虽然也是和__init__一样,但__init__是在类实例创建之后调用,而 __new__方法正是创建这个类实例的方法。
# __new__ 方法必须返回值,__init__方法不需要返回值。
 
class Human(object):
    def __new__(cls, *agrs, **kwargs):
        a = object.__new__(cls, *agrs, **kwargs)
        print('__new__')
        print(a)
        return a  # __new__ 方法必须返回值,__init__方法不需要返回值。
 
    def __init__(self, name='lisi'):
        print('__init__')
        print(self)
        self.name = name
        
    def getName(self):
        print(self.name)
 
hu = Human() #不能传参数
hu.getName()
#可以发现: __new__方法的调用是发生在__init__之前的
# __new__
# <__main__.Human object at 0x00000000021FF5F8>
# __init__
# <__main__.Human object at 0x00000000021FF5F8>
# lisi
 
# 对于int这种 不可变的对象,我们只有重载它的__new__方法才能起到自定义的作用, 而 __init__ 方法是不起作用的。   
class PositiveInteger(int):
    def __new__(cls, value):
        return super(PositiveInteger, cls).__new__(cls, abs(value))
    
poi = PositiveInteger(-3)
print(poi) # 3
 
#使用__new__ 实现单例模式
class Singlton(object):
    _instance = None
    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super(Singlton, cls).__new__(cls, *args, **kwargs)
        return cls._instance
 
class myClass(Singlton):
    pass
    
a = myClass()
b = myClass()
print(a is b )  #True
print(a == b) #True 
print(id(a), id(b)) # 40345672 40345672
 
# 装饰器: 它可以让其他函数或类在不需要做任何代码修改的前提下增加额外功能,装饰器的返回值也是一个函数/类对象。装饰器的作用就是为已经存在的对象添加额外的功能
#初始的函数
def myFunc(): #需要增加功能的函数
    print('myFunc call')
    
def deco(func): # 将函数作为参数
    def wrapper():
        print('before func') #假设为增加的功能 1
        func()
        print('afther func')# 增加的功能2 
    return wrapper 
 
myFunc = deco(myFunc)
myFunc() #直接调用原来的函数
# before func
# myFunc call
# afther func
 
@deco  # 语法糖
def myFunc2():  #对函数进行功能扩展
    print('this is my Function2')
myFunc2() #@ 相当于省略了myFunc2 = deco(myFunc2)
 
#-----对带有参数的函数进行功能扩展-----
def deco2(func):
    def wrapper(a, b):
        print('before func ')
        res = func(a, b)
        print('afther func')
        return res
    return wrapper 
 
@deco2 
def func2(a, b):
    print('res is %d'%(a+b))
    return a+b 
 
res = func2(2, 5)
print(res)
 
#-----对参数不确定数的函数进行功能扩展-----
def deco3(func):
    def wrapper(*args, **kwargs):
        print('before func')
        res = func(*args, **kwargs)
        print('afther func ')
        return res 
    return wrapper 
 
@deco3 
def func3(a, b, c, d):
    res = a+b+c+d
    print('sum is %d'%(res))
    return res 
func3(1, 2, 3, 4)
 
@deco3
def func4(a, b, c):
    res = a + b + c 
    print('sum is %d'%(res))
    return res 
func4(1, 2, 3)
 
#-----使用装饰器实现单例模式-----
 
def singleton(cls, *args, **kwargs):
    instance = {}
    def getInstance():
        if cls not in instance:
            instance[cls] = cls(*args, **kwargs)
        return instance[cls]
    return getInstance
 
@singleton 
class myClass2(object):
    a = 1 
 
a = myClass2()
b = myClass2()
print(a is b)
print(a == b)
斐波那契数列
# -*- coding:utf-8 -*-
class Solution:
    def Fibonacci(self, n):
        if n == 0:
            return 0
        if n==1 or n==2:
            return 1
        memories = [1,1]
        for i in range(n-2):
            memories.append(memories[-1]+memories[-2])
        return memories[-1]
    def fib_loop_for(n):
        a, b = 0, 1
        for _ in range(n):
            a, b = b, a + b
        return a

    def fib_loop_while(n):
        a, b = 1, 1
        while n > 0:
            a, b = b, a + b
            n -= 1
        return a

跳台阶
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
dp[n]=dp[n−1]+dp[n−2]dp[n]=dp[n−1]+dp[n−2]

# -*- coding:utf-8 -*-
class Solution:
    def jumpFloor(self, number):
        # write code here
        '''
        n = 1 : 1 
        n = 2 : 1+1 = 2
        n = 3 : dp[n-2]+dp[n-1]
        '''
        if number == 1 or number == 2:
            return number
        dp = [1,2]
        for i in range(number-2):
            dp.append(dp[-1]+dp[-2])
        return dp[-1]123456789101112131415

变态跳台阶
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
思考:在dp[n] = dp[n-1] + dp[n-2] + … + dp[1] + 1(直接跳n)步骤
即dp[n]=∑n−1i=1dp[i]+1dp[n]=∑i=1n−1dp[i]+1

class Solution:
    def jumpFloorII(self, number):
        # write code here
        if number == 1 or number == 2:
            return number
        ret = sum_ = 3
        for i in range(number-2):
            ret = sum_+1
            sum_+=ret
        return ret 12345678910

矩形覆盖
我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2n的大矩形,总共有多少种方法?
思考: 2
1 1 种; 22 2种 23 3种 2*4 5种
dp[n]=dp[n−1]+dp[n−2]dp[n]=dp[n−1]+dp[n−2]

# -*- coding:utf-8 -*-
class Solution:
    def rectCover(self, number):
        # write code here

        if number<=2:
            return number
        dp = [1,2]
        for i in range(number-2):
            dp.append(dp[-1]+dp[-2])
        return dp[-1]
数值的n次方

思考:,用二分

# -*- coding:utf-8 -*-
class Solution:
    def Power(self, base, exponent):
        # write code here
        if exponent ==0:
            return 1
        if exponent ==1:
            return base
        ans,pos=1,1
        if exponent <0:
            pos=-1
            exponent = - exponent
        if exponent %2 ==0: # 偶数
            ans = self.Power(base,exponent//2)*self.Power(base,exponent//2)
        else: # 奇数
            ans = base * self.Power(base,exponent//2)*self.Power(base,exponent//2)
        return ans if pos==1 else 1.0/ans
二分法和牛顿迭代法求平方根

img

# https://www.jiuzhang.com/problem/sqrtx-ii/#tag-lang-python
class Solution:
    """
    @param: x: a double
    @return: the square root of x
    """
    def sqrt(self, x):
        if x >= 1:
            start, end = 1, x
        else:
            start, end = x, 1
        
        while end - start > 1e-10:
            mid = (start + end) / 2
            if mid * mid < x:
                start = mid
            else:
                end = mid
                
        return start
        
##二分法,下面这个不太对,要x>=1 才行
import math
from math import sqrt
 
def sqrt_binary(num):
	x=sqrt(num)
	y=num/2.0
	low=0.0
	up=num*1.0
	count=1
	while abs(y-x)>0.00000001:
		print count,y
		count+=1		
		if (y*y>num):
			up=y
			y=low+(y-low)/2
		else:
			low=y
			y=up-(up-y)/2
	return y
 
print(sqrt_binary(5))
## 牛顿法 二次方
## https://zhuanlan.zhihu.com/p/111598542
def sqrt_newton(num):
	x=sqrt(num)
	y=num/2.0
	count=1
	while abs(y-x)>0.00000001:
		print count,y
		count+=1
		y=((y*1.0)+(1.0*num)/y)/2.0000
	return y
 
print(sqrt_newton(5))
print(sqrt(5))

### 牛顿法 三次方
def cube_newton(num):
	x=num/3.0
	y=0
	count=1
	while abs(x-y)>0.00000001:
		print count,x
		count+=1
		y=x
		x=(2.0/3.0)*x+(num*1.0)/(x*x*3.0)
	return x
 
print(cube_newton(27))
丑数

只包含因子2、3和5的数称作丑数(Ugly Number),求按从小到大的顺序的第N个丑数。因为丑数只包含质因子2,3,5,假设我们已经有n-1个丑数,按照顺序排列,且第n-1的丑数为M。那么第n个丑数一定是由这n-1个丑数分别乘以2,3,5,得到的所有大于M的结果中,最小的那个数。

事实上我们不需要每次都计算前面所有丑数乘以2,3,5的结果,然后再比较大小。因为在已存在的丑数中,一定存在某个数T2T2,在它之前的所有数乘以2都小于已有丑数,而T2×2T2×2的结果一定大于M,同理,也存在这样的数T3,T5T3,T5,我们只需要标记这三个数即可。

# -*- coding:utf-8 -*-
class Solution:
    def GetUglyNumber_Solution(self, index):
        # write code here
        if index == 0:
            return 0
        # 1作为特殊数直接保存
        baselist = [1]
        min2 = min3 = min5 = 0
        curnum = 1
        while curnum < index:
            minnum = min(baselist[min2] * 2, baselist[min3] * 3, baselist[min5] * 5)
            baselist.append(minnum)
            # 找到第一个乘以2的结果大于当前最大丑数M的数字,也就是T2
            while baselist[min2] * 2 <= minnum:
                min2 += 1
            # 找到第一个乘以3的结果大于当前最大丑数M的数字,也就是T3
            while baselist[min3] * 3 <= minnum:
                min3 += 1
            # 找到第一个乘以5的结果大于当前最大丑数M的数字,也就是T5
            while baselist[min5] * 5 <= minnum:
                min5 += 1
            curnum += 1
        return baselist[-1]
正则表达式匹配

请实现一个函数用来匹配包括’.‘和’*‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’*'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串”aaa”与模式”a.a”和”abaca”匹配,但是与”aa.a”和”ab*a”均不匹配
思考:
第一种情况,当p == ” 时 return s==”
当len§==1时 要满足len(s)==1 AND (p[0]s[0] OR p[0] == ‘.’)
当len§>1时,要讨论p[1] 是不是为’*’ ,因为如果p[1]
’ 时候 可能会是p[2:] 和 s 匹配情况
但当p[1]!=’
’ 时候 意味着 必须要关注是否 p[0]s[0] 或者 p[0]’.’
那么这两个可以合并为
IF len§ == 0 or p[1]!=’
返回 len(s) AND match(p[1:],s[1:]) AND (p[0]==s[0] OR p[0] == ‘.’)
然后最复杂的一种情况p[1] == ‘

p = ‘b*bbacd’ s = ‘bbbbbacd’
很明显的是如果p[0]!=s[0] 且 p[0]!=’.’ 那么 看p[2:] 和 s 的匹配情况
如果p[0] == s[0] 或者 p[0] == ‘.’ , 可以判断p[2:] 和 s[1:] … p[2:] 和 s[2:] … p[2:] 和 s[3:] … 搞个循环 就可以

# -*- coding:utf-8 -*-
class Solution:
    # s, pattern都是字符串
    def __init__(self):
        self.dic = {}
    def match(self, s, p):
        # write code here
        if (s,p) in self.dic:
            return self.dic[(s,p)]
        if p == '':
            return s==''
        if len(p)==1 or p[1]!='*':
            self.dic[(s[1:],p[1:])] = self.match(s[1:],p[1:])
            return len(s)>0 and (p[0]=='.' or p[0]==s[0]) and self.dic[(s[1:],p[1:])]
        while(len(s) and (p[0]=='.' or p[0]==s[0])):
            self.dic[(s,p[2:])] = self.match(s,p[2:])
            if self.match(s[:],p[2:]):
                return True
            s = s[1:]
        self.dic[(s,p[2:])] = self.match(s,p[2:])
        return self.dic[(s,p[2:])]
LeetCode#42. Trapping Rain Water 收集雨水

Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.

img
The above elevation map is represented by array [0,1,0,2,1,0,1,3,2,1,2,1]. In this case, 6 units of rain water (blue section) are being trapped. Thanks Marcos for contributing this image!

Example:

Input: [0,1,0,2,1,0,1,3,2,1,2,1]
Output: 6

这道收集雨水的题跟之前的那道 Largest Rectangle in Histogram 有些类似,但是又不太一样,先来看一种方法,这种方法是基于动态规划 Dynamic Programming 的,维护一个一维的 dp 数组,这个 DP 算法需要遍历两遍数组,第一遍在 dp[i] 中存入i位置左边的最大值,然后开始第二遍遍历数组,第二次遍历时找右边最大值,然后和左边最大值比较取其中的较小值,然后跟当前值 A[i] 相比,如果大于当前值,则将差值存入结果,参见代码如下:

Class Solution:
   def trap_Water(nums):
       if not nums:
           return 
       res,mx,n = 0,0,len(nums)
       dp=[]
       for i in range(n): # 当前节点左边最大值mx
            dp[i] = mx
            mx = max(mx,nums[i])
       mx=0
       for i in range(n-1,0,-1):# 当前节点右边最大值mx
            dp[i] = min(mx,dp[i])
            mx = max(mx,nums[i])
            if dp[i] - nums[i] >0:# 如果当前节点小于左边和右边的较小的那个,就可以存储了,
                res += dp[i] - nums[i]
       return res


public class Solution {
    public int trap(int[] height) {
        int res = 0, mx = 0, n = height.length;
        int[] dp = new int[n];
        for (int i = 0; i < n; ++i) {
            dp[i] = mx;
            mx = Math.max(mx, height[i]);
        }
        mx = 0;
        for (int i = n - 1; i >= 0; --i) {
            dp[i] = Math.min(dp[i], mx);
            mx = Math.max(mx, height[i]);
            if (dp[i] - height[i] > 0) res += dp[i] - height[i];
        }
        return res;
    }
}
LeetCode 84. Largest Rectangle in Histogram 直方图中最大的矩形

Given n non-negative integers representing the histogram’s bar height where the width of each bar is 1, find the area of largest rectangle in the histogram.

img

Above is a histogram where width of each bar is 1, given height = [2,1,5,6,2,3].

img

The largest rectangle is shown in the shaded area, which has area = 10 unit.

For example,
Given height = [2,1,5,6,2,3],
return 10.

这道题让求直方图中最大的矩形,刚开始看到求极值问题以为要用DP来做,可是想不出递推式,只得作罢。这道题如果用暴力搜索法估计肯定没法通过OJ,但是我也没想出好的优化方法,在网上搜到了网友水中的鱼的博客,发现他想出了一种很好的优化方法,就是遍历数组,每找到一个局部峰值(只要当前的数字大于后面的一个数字,那么当前数字就看作一个局部峰值,跟前面的数字大小无关),然后向前遍历所有的值,算出共同的矩形面积,每次对比保留最大值。这里再说下为啥要从局部峰值处理,看题目中的例子,局部峰值为 2,6,3,我们只需在这些局部峰值出进行处理,为啥不用在非局部峰值处统计呢,这是因为非局部峰值处的情况,后面的局部峰值都可以包括,比如1和5,由于局部峰值6是高于1和5的,所有1和5能组成的矩形,到6这里都能组成,并且还可以加上6本身的一部分组成更大的矩形,那么就不用费力气去再统计一个1和5处能组成的矩形了。代码如下:

//就是遍历数组,每找到一个局部峰值(只要当前的数字大于后面的一个数字,那么当前数字就看作一个局部峰值,跟前面的数字大小无关),然后向前遍历所有的值,算出共同的矩形面积,每次对比保留最大值。
// Pruning optimize
class Solution {
public:
    int largestRectangleArea(vector<int> &height) {
        int res = 0;
        for (int i = 0; i < height.size(); ++i) {
            if (i + 1 < height.size() && height[i] <= height[i + 1]) {
                continue;
            }//找到局部峰值
            int minH = height[i];
            // 局部峰值往前计算所有的值,得到最大的面积
            for (int j = i; j >= 0; --j) {
                minH = min(minH, height[j]);
                int area = minH * (i - j + 1);
                res = max(res, area);
            }
        }
        return res;
    }
};
leetcode:买卖股票的最佳时机(python)

https://www.cnblogs.com/guohai-stronger/p/11837218.html

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。

注意你不能在买入股票前卖出股票。

示例 1:

输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。

示例 2:

输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

#我们可以设置两个变量,一个变量表示当前的最小价格,一个变量表示当前的最大利润。遍历这个#数组,先更新目前为止的最小价格,然后再将当前的价格减去当前的最小价格(利润),以此为根#据更新最大利润。遍历完数组,返回最大利润即可。
class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        minPrice = float("inf")
        maxProfit = 0
        if len(prices) == 0:
            return 0
        for num in prices:
            if num < minPrice:
                minPrice = num
            if num - minPrice > maxProfit:
                maxProfit = num - minPrice
        return maxProfit

#DP如何做??
https://www.cnblogs.com/guohai-stronger/p/11837218.html

1.数组运算

调整数组顺序使奇数位于偶数前面
# -*- coding:utf-8 -*-
class Solution:
    def reOrderArray(self, array):
        def isOdd(a):
            return (a & 1) == 1
        answer = [i for i in array if isOdd(i)]
        answer.extend([i for i in array if not isOdd(i)])
        return answer
    
# -*- coding:utf-8 -*-
class Solution:
    def reOrderArray(self, array):
        return sorted(array, key = lambda x: x % 2 == 0)
二分查找(九章算法)

题目:给定一个排好序的证书数组nums,和一个整数target,寻找target在nums中任何一个/第一次出现/最后一次出现的位置,不存在return-1。

思路:基本上看到时间复杂度要求O(logN)基本就是要用二分法,二分法的本质是保留有解的一半。

3.通用的二分法模板(四个要素)

①start+1<end

②start+(end-start)/2

③A[mid]==,<,>

④A[start] A[end] ? target

class Solution:
    # @param nums: The integer array
    # @param target: Target number to find
    # @return the first position of target in nums, position start from 0 
    def binarySearch(self, nums, target):
        if not nums:
            return -1
            
        start, end = 0, len(nums) - 1
        # 用 start + 1 < end 而不是 start < end 的目的是为了避免死循环
        # 在 first position of target 的情况下不会出现死循环
        # 但是在 last position of target 的情况下会出现死循环
        # 样例:nums=[1,1] target = 1
        # 为了统一模板,我们就都采用 start + 1 < end,就保证不会出现死循环
        while start + 1 < end:
            # python 没有 overflow 的问题,直接 // 2 就可以了
            # java和C++ 最好写成 mid = start + (end - start) / 2
            # 防止在 start = 2^31 - 1, end = 2^31 - 1 的情况下出现加法 overflow
            mid = (start + end) // 2
            
            # > , =, < 的逻辑先分开写,然后在看看 = 的情况是否能合并到其他分支里
            if nums[mid] < target:
                # 写作 start = mid + 1 也是正确的
                # 只是可以偷懒不写,因为不写也没问题,不会影响时间复杂度
                # 不写的好处是,万一你不小心写成了 mid - 1 你就错了
                start = mid
            elif nums[mid] == target:
                end = mid
            else: 
                # 写作 end = mid - 1 也是正确的
                # 只是可以偷懒不写,因为不写也没问题,不会影响时间复杂度
                # 不写的好处是,万一你不小心写成了 mid + 1 你就错了
                end = mid
                
        # 因为上面的循环退出条件是 start + 1 < end
        # 因此这里循环结束的时候,start 和 end 的关系是相邻关系(1和2,3和4这种)
        # 因此需要再单独判断 start 和 end 这两个数谁是我们要的答案
        # 如果是找 first position of target 就先看 start,否则就先看 end
        if nums[start] == target:
            return start
        if nums[end] == target:
            return end
        
        return -1

查找range

class Solution:
    """
    @param nums: the array of integers
    @param target: 
    @return: the starting and ending position
    """
    def searchRange(self, nums, target):
        # Write your code here.
        if not nums:
            return [-1, -1]
            
        first_index =  self.binarySearch(nums, target, True)
        if first_index == -1:
            return [-1, -1]
        last_index =  self.binarySearch(nums, target, False)
        return [first_index, last_index]
    
    def binarySearch(self, nums, target, isFindFirst):
        start, end = 0, len(nums) - 1 
        while start + 1 < end:
            mid = (start + end) // 2 
            if target > nums[mid]:
                start = mid
            elif target < nums[mid]:
                end = mid
            else:
                if isFindFirst:
                    end = mid
                else:
                    start = mid
        if isFindFirst:
            if nums[start] == target:
                return start 
            if nums[end] == target:
                return end 
            return -1
        
        else:
            if nums[end] == target:
                return end
            if nums[start] == target:
                return start
            return -1
旋转数组的最小数字

思考:二分判断

# -*- coding:utf-8 -*-
class Solution:
    def minNumberInRotateArray(self, rotateArray):
        # write code here
        if rotateArray == []:
            return 0
        _len = len(rotateArray)
        left = 0
        right = _len - 1
        while left <= right:
            mid = int((left + right) >> 1)
            if rotateArray[mid]<rotateArray[mid-1]:
                return rotateArray[mid]
            if rotateArray[mid] >= rotateArray[right]:
                # 说明在【mid,right】之间
                left = mid + 1
            else:
                # 说明在【left,mid】之间
                right = mid - 1
        return rotateArray[mid]
    
# -*- coding:utf-8 -*-
class Solution:
    def minNumberInRotateArray(self, nums):
        # write code here
        if not nums:
            return -1
            
        start, end = 0, len(nums) - 1
        while start + 1 < end:
            mid = (start + end) // 2
            if nums[mid] > nums[end]:
                start = mid
            else:
                end = mid
                
        return min(nums[start], nums[end])
LeetCode-旋转数组查找

https://www.cnblogs.com/flix/p/12788975.html

假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
你可以假设数组中不存在重复的元素。
你的算法时间复杂度必须是 O(log n) 级别。
示例:

Copy输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4

输入: nums = [4,5,6,7,0,1,2], target = 3
输出: -1

题目链接: https://leetcode-cn.com/problems/search-in-rotated-sorted-array/
做这题之前要先把二分查找做了,并理解二分查找两种写法的不同的原因。

题目要求时间复杂度必须是O(logn)级别,所以不能直接遍历查找,而O(logn)的时间复杂度通常意味着二分查找。
旋转数组可以看做是两段升序数组的拼接,所以可以使用二分查找来做。令查找的边界left=0, right=nums.size()-1, 每次查找都在[left, right]范围内进行:

  • 计算mid=(left+right)/2或者mid=left+(right-left)/2,两种方法的结果是一样的,后一种可以防止溢出;
  • 如果nums[mid]==target,则说明找到了target,返回mid即可;
  • 如果nums[left]<=nums[mid],则说明左侧区间[left, mid]范围内的数组是单调递增的:
    • 如果nums[left]<=target<nums[mid],则说明target则[left, mid-1]之间,将right更新为mid-1;(这里用的是target<nums[mid]不是target<=nums[mid],因为nums[mid]肯定不等于target,等于的话就直接返回了)
    • 否则,说明target在[mid+1, right]之间,将left更新为mid+1;
  • 否则,则说明右侧区间[mid, right]是单调递增的:
    • 如果nums[mid]<target<=nums[right],则说明target在[mid+1, right]之间,则将left更新为mid+1;
    • 否则,说明target在[left, mid-1]之间,将right更新为mid-1.
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size()-1;
        while(left<=right){
            int mid = left+(right-left)/2;
            if(nums[mid]==target) return mid;
            else if(nums[mid]>=nums[left]){ // mid 在左边,注意是 >=,不是 >
                if(nums[left]<=target && target<nums[mid]) right = mid-1;
                else left = mid+1;
            }
            else if(nums[mid]<nums[left]){   // mid 在右边
                if(nums[mid]<target && target<=nums[right]) left = mid+1;
                else right = mid-1;
            }
        }
        return -1;
    }
};
和为S的连续正数序列

输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序
思考:

解法:1.穷举法,2.滑动窗口法

,3.数字规律法(往下看)

S%奇数0 或者S%偶数偶数/2 就说明有这个连续序列,但是注意是正数序列,可能会出现越界情况

####滑动窗口法 https://blog.csdn.net/gui951753/article/details/92416352
# -*- coding:utf-8 -*-
class Solution:
    def FindContinuousSequence(self, tsum):
        """
        使用滑动窗口的方法来解决,
        设定一个动态的窗口,p_low指向窗口头部,
        p_high指向窗口尾部,窗口之间的值,为目标值。
        如果目标值为tsum,那就是其中一个解。否则移动窗口。
        :param tsum:
        """
        #错误判断处理,如果小于3的话 无解
        if tsum < 3:
            return []
        #设定初始的滑动窗口大小
        p_low = 1
        p_high = 2

        ans = []
        while p_low < p_high:
            #计算滑动窗口现在圈中的大小
            cur_sum = sum(range(p_low,p_high+1))
            if cur_sum == tsum:
                #找到一组解,并记录到ans数组中。
                ans.append(range(p_low,p_high+1))
                #移动滑动窗口,并寻找下一组解。
                p_high = p_high + 1
            elif cur_sum < tsum:
                p_high = p_high + 1
            else :
                p_low = p_low + 1
        return ans

if __name__ == '__main__':
    s = Solution()
    print  s.FindContinuousSequence(100)
######3
class Solution:
    def FindContinuousSequence(self, tsum):
        # write code here
        k = 2
        ret = []
        for k in range(2,tsum):
            if k%2==1 and tsum%k==0:
                tmp = []
                mid = tsum/k
                if mid-k/2>0:
                    for i in range(mid-k/2,mid+k/2+1):
                        tmp.append(i)
                    ret.append(tmp[:])
            elif k%2==0 and (tsum%k)*2==k:
                mid = tsum/k
                tmp = []
                if mid-k/2+1>0:
                    for i in range(mid-k/2+1,mid+k/2+1):
                        tmp.append(i)
                    ret.append(tmp[:])
        ret.sort()
        return ret
数字在排序数组中出现的次数

数字在排序数组中出现的次数
思考:原来是可以用hash做的,但是因为是排序数组,所以可以用二分查找

# -*- coding:utf-8 -*-
class Solution:
    def GetNumberOfK(self, data, k):
        # write code here
        start = 0
        end = len(data)-1
        while(start<=end):
            mid = (start+end)/2
            if data[mid]==k:
                cnt = 0
                tmp = mid
                while(tmp>=0 and data[tmp]==k):
                    cnt+=1
                    tmp-=1
                tmp = mid+1
                while(tmp<len(data) and data[tmp]==k):
                    cnt+=1
                    tmp+=1
                return cnt
            elif data[mid]>k:
                end = mid-1
            else:
                start = mid+1
        return 0
数组中只出现一次的数字

一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字
思考:用hash;或者位运算
首先利用0 ^ a = a; a^a = 0的性质
两个不相等的元素在位级表示上必定会有一位存在不同,
将数组的所有元素异或得到的结果为不存在重复的两个元素异或的结果,
据异或的结果1所在的最低位,把数字分成两半,每一半里都还有一个出现一次的数据和其他成对出现的数据,
问题就转化为了两个独立的子问题“数组中只有一个数出现一次,其他数都出现了2次,找出这个数字”。

class Solution:
    # 返回[a,b] 其中ab是出现一次的两个数字
    def FindNumsAppearOnce(self, array):
        # write code here
        ans,a1,a2,flag= 0,0,0,1
        for num in array:
            ans = ans ^ num
        while(ans):
            if ans%2 == 0:
                ans = ans >>1 
                flag = flag <<1
            else:
                break
        for num in array:
            if num & flag:
                a1 = a1 ^ num
            else:
                a2 = a2 ^ num
        return a1,a2
和为S的两个数字

输入一个递增排序的数组和一个数字S,在数组中查找两个数,是的他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
hash

# -*- coding:utf-8 -*-
class Solution:
    def FindNumbersWithSum(self, array, tsum):
        # write code here
        memorys= {}
        ret = []
        for num in array:
            if tsum-num in memorys:
                if ret == []:
                    ret = [tsum-num,num]
                elif ret and ret[0]*ret[1]>(tsum-num)*num:
                    ret = [tsum-num,num]
            else:
                memorys[num] = 1
        return ret

第一个只出现一次的字符

思考:用dict 最多256个来空间换时间

# -*- coding:utf-8 -*-
class Solution:
    def FirstNotRepeatingChar(self, s):
        # write code here
        if s=='':
            return -1
        ret = [0,-1]
        chars = {}
        for i,c in enumerate(s):
            if c in chars:
                chars[c][0] = chars[c][0] +1
            else:
                chars[c] =[1,i]
        for k,v in chars.items():
            if v[0] ==1:
                if ret[1] ==-1:
                    ret = v
                elif v[1] <= ret[1]:
                    ret = v
        return ret[1]
数组中的逆序对

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
方法1:思考:这边的python会超时,但是思路是对的
时间复杂度O(nlogn),空间复杂度O(n)。
先将数组逆转,构建一个新的数组L,将num二分插入到L中,所插入的位置i,代表有i个数字比当前这个数字小

import bisect
class Solution:
    def InversePairs(self, data):
        data.reverse()
        L = []
        ret = 0
        for d in data:
            pos = bisect.bisect_left(L,d)
            L.insert(pos,d)
            ret+= pos
            ret = ret % 1000000007
        return ret % 1000000007
    
def InversePairs(self, data):
    sortData = sorted(data)
    count = 0
    for i in sortData:
        pos = data.index(i)
        count += pos
        data.pop(pos)
    return count

方法2:归并排序的思路

连续子数组的最大和

可以用动态规划的思想来分析这个问题。如果用函数f(i)表示以第i个数字结尾的子数组的最大和,那么我们需要求出max[f(i)],其中0 <= i < n。我们可用如下边归公式求f(i):

这里写图片描述

这个公式的意义:当以第i-1 个数字结尾的子数组中所有数字的和小于0时,如果把这个负数与第i个数累加,得到的结果比第i个数字本身还要小,所以这种情况下以第i个数字结尾的子数组就是第i个数字本身。如果以第i-1 个数字结尾的子数组中所有数字的和大于0 ,与第i 个数字累加就得到以第i个数字结尾的子数组中所有数字的和。

class Solution:
    def FindGreatestSumOfSubArray(self, array):
        # write code here
        if len(array)==1:
            return array[0]
        cur = pos = array[0]
        for i in range(1,len(array)):
            pos = max(pos+array[i],array[i])
            cur = max(cur,pos)
        return cur
#返回下标
class Solution:
      def get_max(self,nums):
          if not nums:
              return 
          if len(nums)==1:
              return nums[0],0,1
          cur=[nums[0],0,1]
          pos=[nums[0],0,1]
          for i in range(1,len(nums)):
              if pos[0]+nums[i] > nums[i]:
                   pos[0] = pos[0]+nums[i]
                   pos[2] = i
              else:
                   pos = [nums[i],i,i+1]
              if cur[0] < pos:
                  cur = pos
          return cur[0],cur[1],cur[2]
####返回下标
class Solution:
    """
    @param: A: An integer array
    @return: A list of integers includes the index of the first number and the index of the last number
    """
    def continuousSubarraySum(self, A):
        n = len(A)
        # max_sum维护全局最大连续子数组和
        # sum维护当前最大连续子数组和,即当前元素必选的最大和
        max_sum, sum = A[0], 0
        # first, last表示全局最大连续子数组的左右端点
        # bg表示当前连续子数组的左端点
        first = last = 0
        bg = 0;
        for i in range(n):
            if sum >= 0:
                sum += A[i]
            else:
                bg = i
                sum = A[i]
            if max_sum < sum:
                max_sum = sum
                first = bg
                last = i
        return [first,last]
最小的K个数
import heapq
class Solution:
    def GetLeastNumbers_Solution(self, tinput, k):
        # write code here
        heaps = []
        ret = []
        for num in tinput:
            heapq.heappush(heaps,num)
        if k>len(heaps):
            return []
        for i in range(k):
            ret.append(heapq.heappop(heaps))
        return ret
第K大的数
class Solution:
    # @param k & A a integer and an array
    # @return ans a integer
    def kthLargestElement(self, k, A):
        if not A or k < 1 or k > len(A):
            return None
        return self.partition(A, 0, len(A) - 1, len(A) - k)
        
    def partition(self, nums, start, end, k):
        """
        During the process, it's guaranteed start <= k <= end
        """
        if start == end:
            return nums[k]
            
        left, right = start, end
        pivot = nums[(start + end) // 2]
        while left <= right:
            while left <= right and nums[left] < pivot:
                left += 1
            while left <= right and nums[right] > pivot:
                right -= 1
            if left <= right:
                nums[left], nums[right] = nums[right], nums[left]
                left, right = left + 1, right - 1
                
        # left is not bigger than right
        if k <= right:
            return self.partition(nums, start, right, k)
        if k >= left:
            return self.partition(nums, left, end, k)
        
        return nums[k]
数组中出现次数超过一半的数字

思考:摩尔投票法

# -*- coding:utf-8 -*-
class Solution:
    def MoreThanHalfNum_Solution(self, numbers):
        # write code here
        if numbers == []:
            return 0
        val,cnt = None,0
        for num in numbers:
            if cnt==0:
                val,cnt = num,1
            elif val == num:
                cnt+=1
            else:
                cnt-=1
        return val if numbers.count(val)*2>len(numbers) else 0
把数组排成最小的数

输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
str 化

链接:https://www.nowcoder.com/questionTerminal/8fecd3f8ba334add803bf2a06af1b993
来源:牛客网
# -*- coding:utf-8 -*-
class Solution:
    def PrintMinNumber(self, numbers):
        # write code here
        if not numbers:return ""
        numbers = list(map(str,numbers))
        numbers.sort(cmp=lambda x,y:cmp(x+y,y+x))
        return '0' if numbers[0]=='0' else ''.join(numbers)
数组中重复的数字

在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中第一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。

返回描述:

如果数组中有重复的数字,函数返回true,否则返回false。

如果数组中有重复的数字,把重复的数字放到参数duplication[0]中。(ps:duplication已经初始化,可以直接赋值使用。)

# -*- coding:utf-8 -*-
class Solution:
    # 这里要特别注意~找到任意重复的一个值并赋值到duplication[0]
    # 函数返回True/False
    def duplicate(self, numbers, duplication):
        # write code here
        dup = dict()
        for num in numbers:
            if num not in dup:
                dup[num] = True
            else:
                duplication[0]=num
                return True
构造乘积数组

B[i]=A[0]A[1]A[i-1]*A[i+1]…*A[n-1]
思考:分为下三角和上三角DP计算B
下三角
上三角(从最后往前)

class Solution:
    def multiply(self, A):
        # write code here
        size = len(A)
        B = [1]*size
        for i in range(1,size):
            B[i] = B[i-1]*A[i-1]
        tmp = 1
        for i in range(size-2,-1,-1):
            tmp = tmp*A[i+1]
            B[i] = B[i]*tmp
        return B
二维数组中的查找

在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
思考:从0,n-1出开始,小了往下,大了往左

# -*- coding:utf-8 -*-
class Solution:
    # array 二维列表
    def Find(self, target, array):
        # write code here
        if len(array)==0 or len(array[0])==0:
            return False
        i = 0
        j = len(array[0])-1
        while(i<len(array) and j>=0):
            if array[i][j]==target:
                return True
            elif array[i][j]>target:
                j-=1
            else:
                i+=1
        return False
扑克牌顺子

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的运气如何。为了方便起见,你可以认为大小王是0。

class Solution:
    def IsContinuous(self, numbers):
        # write code here
        if not numbers:
            return 0
        numbers.sort()
        zeros = numbers.count(0)
        for i, v in enumerate(numbers[:-1]):
            if v!=0:
                if numbers[i+1]==v:
                    return False
                zeros -= (numbers[i+1]-numbers[i]-1)
                if zeros<0:
                    return False
        return True
孩子们的游戏

每年六一儿童节,我都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数…这样下去…直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!_)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)

如果没有小朋友,请返回-1

为了讨论方便,先把问题稍微改变一下,并不影响原意:
我们知道第一个人(编号一定是(m-1)) 出列之后,剩下的n-1个人组成了一个新的约瑟夫环(以编号为k=m mod n的人开始):

k, k+1, k+2, … n-2,n-1,0,1,2,… k-2

并且从k开始报0。
我们把他们的编号做一下转换:

k --> 0
k+1 --> 1
k+2 --> 2
...
...
k-2 --> n-2

变换后就完完全全成为了(n-1)个人报数的子问题,假如我们知道这个子问题的解:例如x是最终的胜利者,那么根据上面这个表把这个x变回去不刚好就是n个人情况的解吗?!!变回去的公式很简单,相信大家都可以推出来:x’=(x+k) mod n

如何知道(n-1)个人报数的问题的解?对,只要知道(n-2)个人的解就行了。(n-2)个人的解呢?当然是先求(n-3)的情况 —- 这显然就是一个倒推问题!好了,思路出来了,下面写递推公式:

令f表示i个人玩游戏报m退出最后胜利者的编号,最后的结果自然是f[n]

递推公式

让f[i]为i个人玩游戏报m退出最后的胜利者的编号,最后的结果自然是f[n]

f[1] = 0;

f[i] = (f[i - 1] + m) mod i;

有了这个公式,我们要做的就是从1-n顺序算出f的数值,最后结果是f[n]。

# 正经思路
# -*- coding:utf-8 -*-
class Solution:
    def LastRemaining_Solution(self, n, m):
        # write code here
        if n<1: return -1
        final,start = -1,0
        cnt = [i for i in range(n)]
        while cnt:
            k = (start+m-1)%n
            final = cnt.pop(k)
            n-=1
            start = k
        return final 
    
# 数学思路
class Solution{
def LastRemaining_Solution(self,n,m):
    if n <1 : return -1
    ans=0;
    for i in range(1,n+1):
       ans = (ans + m) %i
    return ans
}
#include<cstdio>
int main(){
    int n,m;
    while(scanf("%d %d",&n,&m)==2&&n&&m){
        int ans = 0;
        for(int i = 1;i <= n;++i){
            ans = (ans + m) % i;
        }
        printf("总人数:%d 每次出列的人喊的号数:%d 最后一个出列的人的序号:%d\n",n,m,ans+1);
    }
    return 0;
}

剪绳子

题目: 给你一根长度为n绳子,请把绳子剪成m段(m、n都是整数,n>1并且m>1)。每段的绳子的长度记为k[0]、k[1]、……、k[m]。k[0] * k[1]*…*k[m]可能的最大乘积是多少?例如当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到最大的乘积18。
思路:动态规划思想:即:长度为10的绳子,我们就计算出:长度1~9这9种长度的绳子,每种长度的最大乘积是多少。
  要求长度9的绳子的最大乘积,我们要知道18各个长度的最大乘积,要知道长度8的最大乘积,就要知道17长度的各个最大乘积,以此类推。

class Solution():
    def maxProfuctAfterCutting(self,length):
        if length < 2:
            return 0
        if length == 2:
            return 1
        if length == 3:
            return 2
        products = [0,1,2,3]
        for i in range(4,length+1):
            max = 0
            for j in range(1,i//2+1):
        # 思路:每次求解值时将其他小于需要求解的长度是都列出来放在一个数组里
        # 如:求长度为5,最优解数组里必须得有长度为1,2,3,4的最优解值
        # 注:此处使用列表保存最优解数组是为了性能优化,虽然递归求解也能解出,但会造成大量重复执行
                temp = products[j] * products[i-j]
                if temp>max:
                    max = temp
                products.append(max)
        return products[length]

1.1排序算法类

快速排序
class Solution:
    # @param {int[]} A an integer array
    # @return nothing
    def sortIntegers2(self, A):
        # Write your code here
        self.quickSort(A, 0, len(A) - 1)
    
    def quickSort(self, A, start, end):
        if start >= end:
            return
        
        left, right = start, end
        # key point 1: pivot is the value, not the index
        pivot = A[(start + end) // 2]

        # key point 2: every time you compare left & right, it should be 
        # left <= right not left < right
        while left <= right:
            while left <= right and A[left] < pivot:
                left += 1
            while left <= right and A[right] > pivot:
                right -= 1
            if left <= right:
                A[left], A[right] = A[right], A[left]
                left += 1
                right -= 1
        
        self.quickSort(A, start, right)
        self.quickSort(A, left, end)

####方法2
https://www.cnblogs.com/pungchur/p/12103465.html

import random
def quick_sort(li, left, right):
    if left < right:  # 待排序的区域至少有两个元素
        mid = partition(li, left, right)
        quick_sort(li, left, mid - 1)
        quick_sort(li, mid + 1, right)

# partition分区
def partition(li, left, right):
    tmp = li[left]
    while left < right:
        while left < right and li[right] >= tmp:
            right -= 1
        li[left] = li[right]
        while left < right and li[left] <= tmp:
            left += 1
        li[right] = li[left]
    li[left] = tmp
    return left


li = list(range(100))
random.shuffle(li)

quick_sort(li, 0, len(li) - 1)
print(li)
堆排序
https://www.cnblogs.com/pungchur/p/12103465.html

import random
def sift(li, low, high):
    # li表示树, low表示树根, high表示树最后一个节点的位置
    tmp = li[low]
    i = low
    j = 2 * i + 1  # 初识j指向空位的左孩子
    # i指向空位,j指向两个孩子
    while j <= high:  # 循环退出的第二种情况: j>high,说明空位i是叶子节点
        if j + 1 <= high and li[j] < li[j + 1]:  # 如果右孩子存在并且比左孩子大,指向右孩子
            j += 1
        if li[j] > tmp:
            li[i] = li[j]
            i = j
            j = 2 * i + 1
        else:  # 循环退出的第一种情况:j位置的值比tmp小,说明两个孩子都比tmp小
            break
    li[i] = tmp


def heap_sort(li):
    n = len(li)
    # 1. 构造堆
    for low in range(n // 2 - 1, -1, -1):
        sift(li, low, n - 1)
    # 2. 挨个出数
    for high in range(n - 1, -1, -1):
        li[0], li[high] = li[high], li[0]  # 退出 棋子
        sift(li, 0, high - 1)



li = list(range(100))
random.shuffle(li)
heap_sort(li)
print(li)
归并排序

https://www.jiuzhang.com/solution/merge-sort/#tag-lang-python

class Solution:
    # @param {int[]} A an integer array
    # @return nothing
    def sortIntegers2(self, A):
        # Write your code here
        temp = [0 for _ in range(len(A))]
        self.merge_sort(0, len(A) - 1, A, temp)
        
    def merge_sort(self, start, end, A, temp):
        if start >= end:
            return
        
        mid = (start + end) // 2
        self.merge_sort(start, mid , A, temp)
        self.merge_sort(mid + 1, end, A, temp)
        self.merge(start, mid, end, A, temp)
        
    def merge(self, start, mid, end, A, temp):
        left, right = start, mid + 1
        index = start
        while left <= mid and right <= end:
            if A[left] < A[right]:
                temp[index] = A[left]
                left += 1
            else:
                temp[index] = A[right];
                right += 1
                
            index += 1
            
        while left <= mid:
            temp[index] = A[left]
            left += 1
            index += 1
            
        while right <= end:
            temp[index] = A[right]
            right += 1
            index += 1
            
        for index in range(start, end + 1):
            A[index] = temp[index]
桶排序

计数排序

2.位运算

二进制中1的个数

思考:python没有无符号移动,需要处理下;python的int是不会溢出的,达到界限后会自己转为long,所以很麻烦。

# -*- coding:utf-8 -*-
class Solution:
    def NumberOf1(self, n):
        # write code here
        ans = 0
        if n<0:
            n = n & 0xffffffff
        while n:
            ans += n & 1
            n >>= 1
        return ans 
求1+2+3+…+n

思考:递归 or 公式法

# -*- coding:utf-8 -*-
class Solution:
#     def Sum_Solution(self, n):
#         # write code here
#         if n ==1:
#             return n
#         else:
#             return n+self.Sum_Solution(n-1)
    def Sum_Solution(self, n):
        # write code here
        if n == 1:
            return n
        return n*(n+1)/2
不用加减乘除做加法

思考:不计进位的和为 a^b,进位就是 a&b;python是用的补码来存的,不过python补码有一些问题;

python会讲补码存成正数 需要 -1 & num

https://blog.csdn.net/qq_16949707/article/details/106399430

a+b = a^b + (a&b)<<1;

# -*- coding:utf-8 -*-
class Solution:
    def Add(self, num1, num2):
        xorNum = num1 ^ num2
        andNum = num1 & num2 << 1
        while andNum:
            tmp1 = xorNum ^ andNum
            tmp2 = (xorNum & andNum) << 1
            tmp1 = tmp1 & 0xFFFFFFFF
            xorNum = tmp1
            andNum = tmp2
        return xorNum if xorNum <= 0x7FFFFFFF else ~(-1 & xorNum ^ 0xffffffff)
从1到n整数中1出现的次数

思考:可以从n的每个位上入手,pos来记录位,ans来记录当前1的个数,last来记录前面的数(这样讲好复杂,举个例子好了)
xxxxYzzzz (假设9位)
在Y上1的个数由xxxx,zzzz和Y来决定
首先至少有xxxx0000个
其次看Y
如果Y大于1那么会多了10000个
如果Y等于1那么会多了(zzzz+1)个

举例:

设N = abcde ,其中abcde分别为十进制中各位上的数字。
如果要计算百位上1出现的次数,它要受到3方面的影响:百位上的数字,百位以下(低位)的数字,百位以上(高位)的数字。
① 如果百位上数字为0,百位上可能出现1的次数由更高位决定。比如:12013,则可以知道百位出现1的情况可能是:100199,11001199,21002199,,…,1110011199,一共1200个。可以看出是由更高位数字(12)决定,并且等于更高位数字(12)乘以 当前位数(100)。
② 如果百位上数字为1,百位上可能出现1的次数不仅受更高位影响还受低位影响。比如:12113,则可以知道百位受高位影响出现的情况是:100199,11001199,21002199,,…,1110011199,一共1200个。和上面情况一样,并且等于更高位数字(12)乘以 当前位数(100)。但同时它还受低位影响,百位出现1的情况是:12100~12113,一共114个,等于低位数字(113)+1。
③ 如果百位上数字大于1(29),则百位上出现1的情况仅由更高位决定,比如12213,则百位出现1的情况是:100199,11001199,21002199,…,1110011199,1210012199,一共有1300个,并且等于更高位数字+1(12+1)乘以当前位数(100)。
——参考牛客网@藍裙子的百合魂

# -*- coding:utf-8 -*-
class Solution:
    def NumberOf1Between1AndN_Solution(self, n):
        # write code here
        if n<1:  return 0
        if n==1: return 1
        last,ans,pos = 0,0,1 # pos来记录位,ans来记录当前1的个数,last来记录前面的数(右边的数)
        while(n):
            num = n%10
            n = n/10
            ans += pos*n
            if num>1:
                ans+=pos
            elif num==1:
                ans+=(last+1)
            last = last+num*pos
            pos*=10
        return ans

3.字符串

翻转单词顺序列
# -*- coding:utf-8 -*-
class Solution:
    def ReverseSentence(self, s):
        # write code here
        ret = s.split(" ")
        ret.reverse()
        return ' '.join(ret)
左旋转字符串

对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。
思考:需要先K= K%len(S)

# -*- coding:utf-8 -*-
class Solution:
    def LeftRotateString(self, s, n):
        # write code here
        if s == '':
            return s
        n = n%len(s)
        return s[n:]+s[0:n]
把字符串转换成整数

将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0
思考:如果有正负号,需要在数字之前,出现其他字符或者字符串为空都非法返回0

class Solution:
    def StrToInt(self, s):
        # write code here
        flag = True  # 是否出现过 + - 符号
        pos = 1 # 正负号
        ret = None # 返回值
        if s=='':
            return 0
        for i in s:
            if i=='+' or i=='-':
                if flag:
                    pos = -1 if i=='-' else 1
                    flag = False
                else:
                    return 0
            elif i>='0' and i<='9':
                flag = False
                if ret == None:
                    ret = int(i)
                else:
                    ret = ret*10+int(i)
            else:
                return 0
        return pos*ret if ret else 0
判断一个字符串是否表示数值

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100",“5e2”,"-123",“3.1416"和”-1E-16"都表示数值。 但是"12e",“1a3.14”,“1.2.3”,"±5"和"12e+4.3"都不是。

# -*- coding:utf-8 -*-
class Solution:
    # s字符串
    def isNumeric(self, s):
        # write code here
        INVALID=0; SPACE=1; SIGN=2; DIGIT=3; DOT=4; EXPONENT=5;
        #0invalid,1space,2sign,3digit,4dot,5exponent,6num_inputs
        transitionTable=[[-1,  0,  3,  1,  2, -1],    #0 no input or just spaces 
                         [-1,  8, -1,  1,  4,  5],    #1 input is digits 
                         [-1, -1, -1,  4, -1, -1],    #2 no digits in front just Dot 
                         [-1, -1, -1,  1,  2, -1],    #3 sign 
                         [-1,  8, -1,  4, -1,  5],    #4 digits and dot in front 
                         [-1, -1,  6,  7, -1, -1],    #5 input 'e' or 'E' 
                         [-1, -1, -1,  7, -1, -1],    #6 after 'e' input sign 
                         [-1,  8, -1,  7, -1, -1],    #7 after 'e' input digits 
                         [-1,  8, -1, -1, -1, -1]]    #8 after valid input input space
        state=0; i=0
        while i<len(s):
            inputtype = INVALID
            if s[i]==' ': inputtype=SPACE
            elif s[i]=='-' or s[i]=='+': inputtype=SIGN
            elif s[i] in '0123456789': inputtype=DIGIT
            elif s[i]=='.': inputtype=DOT
            elif s[i]=='e' or s[i]=='E': inputtype=EXPONENT

            state=transitionTable[state][inputtype]
            if state==-1: return False
            else: i+=1
        return state == 1 or state == 4 or state == 7 or state == 8
字符串的排列

思考:dfs

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则按字典序打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

#https://www.jiuzhang.com/problem/permutations/#tag-lang-python
class Solution:
    """
    @param: nums: A list of integers.
    @return: A list of permutations.
    """
    def permute(self, nums):
        if not nums:
            return [[]]
            
        permutations = []
        self.dfs(nums, [], set(), permutations)
        return permutations
        
    def dfs(self, nums, permutation, visited, permutations):
        if len(nums) == len(permutation):
            permutations.append(list(permutation))
            return
        
        for num in nums:
            if num in visited:
                continue
            permutation.append(num)
            visited.add(num)
            self.dfs(nums, permutation, visited, permutations)
            visited.remove(num)
            permutation.pop()
            
# subsets
# https://www.jiuzhang.com/problem/subsets/#tag-lang-python
class Solution:
    """
    @param nums: A set of numbers
    @return: A list of lists
    """
    def subsets(self, nums):
        res = []
        # 排序
        nums.sort()
        # dfs搜索
        self.dfs(nums, 0, [], res)
        return res
        
    def dfs(self, nums, k, subset, res):
        # 当前组合存入res
        res.append(subset[:])
        # 为subset新增一位元素
        for i in range(k, len(nums)):
            subset.append(nums[i])
            # 下一层搜索
            self.dfs(nums, i + 1, subset, res)
            # 回溯
            del subset[-1]
替换空格
# -*- coding:utf-8 -*-
import re
class Solution:
    # s 源字符串
    def replaceSpace(self, s):
        # write code here
        return re.sub(" ","%20",s)
字符流中第一个不重复的字符

思考:用个队列和hash

# -*- coding:utf-8 -*-
class Solution:
    # 返回对应char
    def __init__(self):
        self.memory = dict() ## 次数
        self.queue = [] ## 第几个

    def FirstAppearingOnce(self):
        # write code here
        while len(self.queue) and self.memory[self.queue[0]]>1:
            self.queue.pop(0)

        return self.queue[0] if len(self.queue) else '#'
    def Insert(self, char):
        # write code here
        if char not in self.memory:
            self.memory[char]=0
        self.memory[char]+=1
        if self.memory[char]==1:
            self.queue.append(char)
最长回文子串

对于一个字符串,请设计一个高效算法,计算其中最长回文子串的长度。

给定字符串A以及它的长度n,请返回最长回文子串的长度。

# https://www.jiuzhang.com/problem/longest-palindromic-substring/#tag-lang-python
# 方法一
class Solution:
    """
    @param s: input string
    @return: the longest palindromic substring
    """
    def longestPalindrome(self, s):
        length = len(s)
        max_len, result = 0, ""
        
        for start in range(length):
            for end in range(start, length):
                new_len = end - start + 1
                # 小优化
                if new_len <= max_len:
                    continue
                if self.is_palindrome(s[start : end + 1]):
                    max_len = new_len
                    result = s[start : end + 1]
        
        return result
    
    
    # 判断是否回文
    def is_palindrome(self, s):
        length = len(s)
        for i in range(length // 2):
            if s[i] != s[length - i - 1]:
                return False
        return True

### 方法二
class Solution:
    """
    @param s: input string
    @return: the longest palindromic substring
    """
    def longestPalindrome(self, s):
        if not s:
            return ""
            
        n = len(s)
        is_palindrome = [[False] * n for _ in range(n)]
        
        for i in range(n):
            is_palindrome[i][i] = True
        for i in range(1, n):
            is_palindrome[i][i - 1] = True
            
        longest, start, end = 1, 0, 0
        for length in range(1, n):
            for i in range(n - length):
                j = i + length
                is_palindrome[i][j] = s[i] == s[j] and is_palindrome[i + 1][j - 1]
                if is_palindrome[i][j] and length + 1 > longest:
                    longest = length + 1
                    start, end = i, j
                    
        return s[start:end + 1]
    
# 比较好的DP
https://www.jianshu.com/p/a7741619dd58
     public String longestPalindrome(String s) {
        if (s.isEmpty()) {
            return s;
        }
        int n = s.length();
        boolean[][] dp = new boolean[n][n];
        int left = 0;
        int right = 0;
        for (int i = n - 2; i >= 0; i--) {
            dp[i][i] = true;
            for (int j = i + 1; j < n; j++) {
                dp[i][j] = s.charAt(i) == s.charAt(j) &&( j-i<3||dp[i+1][j-1]);//小于3是因为aba一定是回文
                if(dp[i][j]&&right-left<j-i){
                    left=i;
                    right=j;
                }
            }
        }
        return s.substring(left,right+1);
    }
最长公共子串

问题:有两个字符串str和str2,求出两个字符串中最长公共子串长度。

比如:str=acbcbcef,str2=abcbced,则str和str2的最长公共子串为bcbce,最长公共子串长度为5。

算法思路:

1、把两个字符串分别以行和列组成一个二维矩阵。

2、比较二维矩阵中每个点对应行列字符中否相等,相等的话值设置为1,否则设置为0。

3、通过查找出值为1的最长对角线就能找到最长公共子串。

针对于上面的两个字符串我们可以得到的二维矩阵如下:

img

从上图可以看到,str1和str2共有5个公共子串,但最长的公共子串长度为5。

为了进一步优化算法的效率,我们可以再计算某个二维矩阵的值的时候顺便计算出来当前最长的公共子串的长度,即某个二维矩阵元素的值由record[i][j]=1演变为record[i][j]=1 +record[i-1][j-1],这样就避免了后续查找对角线长度的操作了。修改后的二维矩阵如下:

img

# https://www.jiuzhang.com/problem/longest-common-substring/
# 滚动数组
# https://blog.csdn.net/qq_25800311/article/details/81607168
# 连续的公共字符串
class Solution:
    """
    @param A: A string
    @param B: A string
    @return: the length of the longest common substring.
    """
    def longestCommonSubstring(self, A, B):
        # write your code here
        m, n = len(A), len(B)
        dp = [ [ 0 for _ in range(n+1) ] for _ in range(2) ]
        maxLen = 0
        for i in range(1, m+1):
            for j in range(1, n+1):
                if A[i-1] == B[j-1]:
                    dp[i%2][j] = dp[(i-1)%2][j-1] + 1
                    maxLen = max(maxLen, dp[i%2][j])
                else:
                    dp[i%2][j] = 0
        return maxLen
    
    
### 最长公共子序列
### 不连续的公共字符串
# https://blog.csdn.net/littlethunder/article/details/25637173
def lcs(a,b):
	lena=len(a)
	lenb=len(b)
	c=[[0 for i in range(lenb+1)] for j in range(lena+1)]
	flag=[[0 for i in range(lenb+1)] for j in range(lena+1)]
	for i in range(lena):
		for j in range(lenb):
			if a[i]==b[j]:
				c[i+1][j+1]=c[i][j]+1
				flag[i+1][j+1]='ok'
			elif c[i+1][j]>c[i][j+1]:
				c[i+1][j+1]=c[i+1][j]
				flag[i+1][j+1]='left'
			else:
				c[i+1][j+1]=c[i][j+1]
				flag[i+1][j+1]='up'
	return c,flag
 
def printLcs(flag,a,i,j):
	if i==0 or j==0:
		return
	if flag[i][j]=='ok':
		printLcs(flag,a,i-1,j-1)
		print(a[i-1],end='')
	elif flag[i][j]=='left':
		printLcs(flag,a,i,j-1)
	else:
		printLcs(flag,a,i-1,j)
		
a='ABCBDAB'
b='BDCABA'
c,flag=lcs(a,b)
for i in c:
	print(i)
print('')
for j in flag:
	print(j)
print('')
printLcs(flag,a,len(a),len(b))

实现 Trie(前缀树) · Implement Trie
  • 题目要求实现一个Trie,包含插入、查找和查找前缀三个方法。
  • Trie树也称字典树,因为其效率很高,所以在在字符串查找、前缀匹配等中应用很广泛,其高效率是以空间为代价的。
  • 原理:利用串构建一个字典树,这个字典树保存了串的公共前缀信息,因此可以降低查询操作的复杂度。
  • 定义结点Node里包含一个isWord(表示这个结点是否是一个单词的结尾)和一个大小为26的next。

1.定义一个根结点root作为整棵树的查找起点。

2.比如插入一个单词apple:

我们从root开始,依次往下查找a,p,p,l,e:如果在结点n的下一个字母里没有找到我们想要的字母ch,我们就增加新结点到结点n的next[],式子next[ch-‘a’]=new Node()。

3.查找一个单词app:

我们从root开始,依次往下查找a,p,p:如果在结点n的下一个字母里没有找到我们想要的字母ch,那么说明不存在,返回False;如果有,那么继续往下找,直到查找的单词app找到最后一位,这时候我们判断一下这个位置的isWord是否为True,如果为True说明这是一个完整单词,否则只是部分,返回False。

4.查找一个前缀

查找一个前缀和查找一个单词的区别就是,当我们想要查找的前缀找到最后一位,不需要判断是否是完整的单词,直接返回True即可。如果不能全部找到则返回False。

  • 空间复杂度:字典树每个节点都需要用一个大小为26的数组来存储子节点的指针,所以空间复杂度较高。
  • 时间复杂度:假设所有插入字符串长度之和为n,构建字典树的时间复杂度为O(n);假设要查找的所有字符串长度之和为k,查找的时间复杂度为O(k)。因此时间复杂度为O(n+k)
# https://www.jiuzhang.com/problem/implement-trie/#tag-lang-python
class TrieNode:
    
    def __init__(self):
        self.children = {}
        self.is_word = False
    
    
class Trie:
    
    def __init__(self):
        self.root = TrieNode()

    """
    @param: word: a word
    @return: nothing
    """
    def insert(self, word):
        node = self.root
        for c in word:
            if c not in node.children:
                node.children[c] = TrieNode()
            node = node.children[c]
        
        node.is_word = True

    """
    return the node in the trie if exists 
    """
    def find(self, word):
        node = self.root
        for c in word:
            node = node.children.get(c)
            if node is None:
                return None
        return node
        
    """
    @param: word: A string
    @return: if the word is in the trie.
    """
    def search(self, word):
        node = self.find(word)
        return node is not None and node.is_word

    """
    @param: prefix: A string
    @return: if there is any word in the trie that starts with the given prefix.
    """
    def startsWith(self, prefix):
        return self.find(prefix) is not None
KMP算法

https://blog.csdn.net/bjweimengshu/article/details/104528964/

def same_start_end(s):
    """最长前后缀相同的字符位数"""
    n = len(s) #整个字符串长度
    j = 0 # 前缀匹配指向
    i = 1 # 后缀匹配指向
    result_list=[0]*n
    while i < n:
        if j == 0 and s[j] != s[i]:  # 比较不相等并且此时比较的已经是第一个字符了
            result_list[i] = 0    # 值为0
            i += 1  # 向后移动
        elif s[j] != s[i] and j != 0: #比较不相等,将j值设置为j前一位的result_list中的值,为了在之前匹配到的子串中找到最长相同前后缀
            j = result_list[j-1]
        elif s[j] == s[i]:   #相等则继续比较
            result_list[i] = j+1
            j = j+1
            i = i+1
    return result_list

def kmp(s,p):
    """kmp算法,s是字符串,p是模式字符串,返回值为匹配到的第一个字符串的第一个字符的索引,没匹配到返回-1"""
    s_length = len(s)
    p_length = len(p)
    i = 0  # 指向s
    j = 0  # 指向p
    next = same_start_end(p)
    while i < s_length:
        if s[i] == p[j]:  # 对应字符相同
            i += 1
            j += 1
            if j >= p_length:  # 完全匹配
                return i-p_length
        elif s[i] != p[j]:  # 不相同
            if j == 0:    # 与模式比较的是模式的第一个字符
                i += 1
            else:        # 取模式当前字符之前最长相同前后缀的前缀的后一个字符继续比较
                j = next[j]
    if i == s_length:    # 没有找到完全匹配的子串
        return -1
大数乘法

以字符串的形式读入两个数字,编写一个函数计算它们的乘积,以字符串形式返回。

(字符串长度不大于10000,保证字符串仅由’0’~'9’这10种字符组成)

示例1

"11","99"
"1089"
11*99=1089  
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# @param s string字符串 第一个整数
# @param t string字符串 第二个整数
# @return string字符串
#
class Solution:
    def solve(self, s, t):
        s = list(map(int, list(s)))
        s.reverse()# 把个位数放到index =0 开始的地方
        t = list(map(int, list(t)))
        t.reverse()# 把个位数放到index =0 开始的地方
        ans = [0]*(len(s)+len(t))
        for i in range(0, len(s)):
            for j in range(0, len(t)):
                ans[i+j] += s[i]*t[j]
        carry = 0
        for i in range(0, len(ans)):
            ans[i] += carry
            carry = int(ans[i]/10)
            ans[i] = str(ans[i] % 10)
        while ans[-1] == '0' and len(ans) > 1:
            ans.pop()
        ans.reverse()
        return ''.join(ans)



大数加法

https://www.nowcoder.com/practice/11ae12e8c6fe48f883cad618c2e81475?tpId=117&tPage=3&rp=3&ru=%2Fta%2Fcoding-interviews&qru=%2Fta%2Fcoding-interviews%2Fquestion-rankin

以字符串的形式读入两个数字,编写一个函数计算它们的和,以字符串形式返回。

(字符串长度不大于100000,保证字符串仅由'0'~'9'10种字符组成)

4.链表

链表中环的入口结点

Leetcode 142. Linked List Cycle II
寻找环的入口结点
这题是Leetcode 141. Linked List Cycle的扩展。
判断是否存在环用fast和slow两个指针,从head开始,一个走一步,一个走两步,如果最终到达同一个结点,则说明存在环。

class Solution(object):
    def hasCycle(self, head):
        """
        :type head: ListNode
        :rtype: bool
        """
        if head == None or head.next == None:
            return False
        slow = fast = head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
            if slow == fast:
                return True
        return False

而寻找环的入口,假设入口结点距离头结点a个单位,fast和slow相遇在距离入口结点b个单位的位置,环剩下的长度为c,则有a+b+c+b = 2*(a+b) -> a = c
因此,在重合时候,将fast置为head,再一步一步地走,当与slow重合时的结点即为入口结点

class Solution:
    def EntryNodeOfLoop(self, pHead):
        # write code here
        if pHead== None or pHead.next == None:
            return None
        fast = slow = pHead
        while(fast and fast.next):
            slow = slow.next
            fast = fast.next.next
            if slow == fast:
                fast = pHead
                while(fast!=slow):
                    fast = fast.next
                    slow = slow.next
                return fast
        return None
翻转链表

https://blog.csdn.net/songyunli1111/article/details/79416684

# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    # 返回ListNode
    def ReverseList(self, pHead):
        if not pHead or not pHead.next:
            return pHead
        last = None   #指向上一个节点
        while pHead:
            #先用tmp保存pHead的下一个节点的信息,
            #保证单链表不会因为失去pHead节点的next而就此断裂
            tmp = pHead.next   
            #保存完next,就可以让pHead的next指向last了
            pHead.next = last
            #让last,pHead依次向后移动一个节点,继续下一次的指针反转
            last = pHead
            pHead = tmp
        return last

###### 反转m和n之间的链表 lintcode92
# 1-2-3-4-5,m=3,n=4;
# 1-2-4-3-5;
class Solution:
    def reverseBetween(self , head , m , n ):
        # write code here
       if head is None or head.next is None or m>=n:
           return head

       # steps:1.find m node, 2. reverse m and n 3. return 
       # step_1
       dummy = ListNode(-1)
       dummy.next = head
       last = dummy
       i=1
       while(i<m):
          last = head
          head = head.next
          i += 1
       # temp save
       part1 = last # 前半部分的最后一个节点
       part2 = head # 后半部分的最后一个节点
       # step_2
       while(i<=n):
          tmp = head.next
          head.next = last
          last = head
          head = tmp
          i+=1

       part1.next = last # 指向最新的头结点
       part2.next = head # 保证后续有的连起来
       return dummy.next
链表中倒数第k个结点

思考:两个指针p,q p先走k步

class Solution:
    def FindKthToTail(self, head, k):
        # write code here
        p1 = p2 = head
        for i in range(k):
            if p1==None:
                return None
            p1 = p1.next
        while(p1):
            p2 = p2.next
            p1 = p1.next
        return p2
合并两个排序的链表
class Solution:
    # 返回合并后列表
    def Merge(self, pHead1, pHead2):
        # write code here
        ret = ListNode(0)
        tmp = ret 
        p1 = pHead1
        p2 = pHead2
        while(p1 and p2):
            if p1.val<p2.val:
                tmp.next = p1
                p1 = p1.next
            else:
                tmp.next = p2
                p2 = p2.next
            tmp = tmp.next
        if p1:
            tmp.next = p1
        if p2:
            tmp.next = p2
        return ret.next
复杂链表的复制

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

  • 将原链表所有的结点拷贝一份,并且每个拷贝的结点和原来的结点连续出现,如下图:
    在这里插入图片描述
  • 此时,原来结点指向下一个结点的指针都可以先链接上,原来的random指针也还在;但是每个复制新建的node的random区域还是空的
  • 找出规律:A.next.random = a.random.next
    理解:如上图:A.next.raomd,是指第二个“1结点”的random指针,它应该指向 第一个“1结点”的random(第一个“3结点”)后一个结点(即第二个“3结点”),所以有上述变化通式
  • 让每两个相同的结点断开,具体做法为:比如第一个“1结点” 让它指向 第一个“2结点”,random也跟着原来的指向变化,后面的结点重复操作,就能达到下图示意的,复制了复杂链表的操作

在这里插入图片描述

class RandomListNode:
    def __init__(self, x):
        self.label = x
        self.next = None
        self.random = None

class Solution:
    # 返回 RandomListNode
    def Clone(self, pHead):
        if pHead is None:
            return None
            
		# 为每一个结点复制一个node,并插入到原链表中
        pTemp = pHead 
        while pTemp:
            node = RandomListNode(pTemp.label)
            node.next = pTemp.next # 如下图2-1
            pTemp.next = node # 如下图 2-2
            pTemp = node.next # 如下图2-3:完成第一个新node插入,移动pTemp,重复上述,完成第二个
            
		# 实现新建node的random指向
        pTemp = pHead 
        while pTemp:
            if pTemp.random: # 如果random指针存在
                pTemp.next.random = pTemp.random.next # 图2-4:通过前一个random找到node的random
            pTemp = pTemp.next.next # 完成一个,移动pTemp帮助找到下一个的random
		
		# 断开 原来node 和新 node 间 的链接
        pTemp = pHead 
        newHead = pHead.next # 这个是新链表的头,到最后返回,一般要保留
        pNewTemp = pHead.next # 再新建一个中间变量来存储新的node
        while pTemp:
            pTemp.next = pTemp.next.next # 改变前一个node的指向,让它不要指向新建的node
            if pNewTemp.next: # 如果下个一的新node存在 (备注 1)
                pNewTemp.next = pNewTemp.next.next  # (备注 2)
                pNewTemp = pNewTemp.next # (备注 3)
            pTemp = pTemp.next # (备注 4) 备注(1~4)见下备注图解
        return newHead

# 测试一下
if __name__ == '__main__':
    n1 = RandomListNode(1)
    n2 = RandomListNode(2)
    n3 = RandomListNode(3)
    n4 = RandomListNode(4)
    n5 = RandomListNode(5)

    n1.next = n2
    n2.next = n3
    n3.next = n4
    n4.next = n5

    s = Solution()
    new_head = s.Clone(n1)
    res = new_head
    while res:
        print(res.label)
        res = res.next  # 输出结果 1 2 3 4 5 说明复制成功

两个链表的第一个公共结点

思考:设链表pHead1的长度为a,到公共结点的长度为l1;链表pHead2的长度为b,到公共结点的长度为l2,有a+l2 = b+l1

### 思路:第一次循环 while(pa!=pb)会将长度自动对齐;
### 第二次循环,就正常去判断了;一个trick
### https://blog.csdn.net/qq_36243414/article/details/90452723
class Solution:
    def FindFirstCommonNode(self, pHead1, pHead2):
        # write code here
        if pHead1== None or pHead2 == None:
            return None
        pa = pHead1
        pb = pHead2 
        while(pa!=pb):
            pa = pHead2 if pa is None else pa.next
            pb = pHead1 if pb is None else pb.next
        return pa

5.二叉树

二叉树的镜像(Symmetric Tree)

牛客网链接
Leetcode链接

class Solution:
    # 返回镜像树的根节点
    def Mirror(self, root):
        if root == None:
            return 
        self.Mirror(root.left)
        self.Mirror(root.right)
        root.left,root.right = root.right,root.left
二叉树的先序、中序、后续遍历 递归和非递归
# 先序打印二叉树(递归)
def preOrderTraverse(node):
    if node is None:
        return None
    print(node.val)
    preOrderTraverse(node.left)
    preOrderTraverse(node.right)
# 先序打印二叉树(非递归)
def preOrderTravese(node):
    stack = [node]
    while len(stack) > 0:
        print(node.val)
        if node.right is not None:
            stack.append(node.right)
        if node.left is not None:
            stack.append(node.left)
        node = stack.pop()
        
# 中序打印二叉树(递归)
def inOrderTraverse(node):
    if node is None:
        return None
    inOrderTraverse(node.left)
    print(node.val)
    inOrderTraverse(node.right)
# 中序打印二叉树(非递归)
def inOrderTraverse(node):
    stack = []
    pos = node
    while pos is not None or len(stack) > 0:
        if pos is not None:
            stack.append(pos)
            pos = pos.left
        else:
            pos = stack.pop()
            print(pos.val)
            pos = pos.right
            
# 后序打印二叉树(递归)
def postOrderTraverse(node):
    if node is None:
        return None
    postOrderTraverse(node.left)
    postOrderTraverse(node.right)
    print(node.val)
# 后序打印二叉树(非递归)
# 使用两个栈结构
# 第一个栈进栈顺序:左节点->右节点->跟节点
# 第一个栈弹出顺序: 跟节点->右节点->左节点(先序遍历栈弹出顺序:跟->左->右)
# 第二个栈存储为第一个栈的每个弹出依次进栈
# 最后第二个栈依次出栈
def postOrderTraverse(node):
    stack = [node]
    stack2 = []
    while len(stack) > 0:
        node = stack.pop()
        stack2.append(node)
        if node.left is not None:
            stack.append(node.left)
        if node.right is not None:
            stack.append(node.right)
    while len(stack2) > 0:
        print(stack2.pop().val)
        
# 先进先出选用队列结构
import queue
def layerTraverse(head):
    if not head:
        return None
    que = queue.Queue()      # 创建先进先出队列
    que.put(head)
    while not que.empty():
        head = que.get()    # 弹出第一个元素并打印
        print(head.val)
        if head.left:       # 若该节点存在左子节点,则加入队列(先push左节点)
            que.put(head.left)
        if head.right:      # 若该节点存在右子节点,则加入队列(再push右节点)
            que.put(head.right)
# 求二叉树节点个数
def treeNodenums(node):
    if node is None:
        return 0
    nums = treeNodenums(node.left)
    nums += treeNodenums(node.right)
    return nums + 1
平衡二叉树的判断

思考:BST的定义为,原问题拆分为计算树高度和判断高度差

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def IsBalanced_Solution(self, pRoot):
        if not pRoot: return True
        left = self.TreeDepth(pRoot.left)
        right = self.TreeDepth(pRoot.right)
        if abs(left - right) > 1:
            return False
        return self.IsBalanced_Solution(pRoot.left) and   self.IsBalanced_Solution(pRoot.right)
    
    def TreeDepth(self, pRoot):
        if not pRoot: return 0
        left = self.TreeDepth(pRoot.left)
        right = self.TreeDepth(pRoot.right)
        return max(left, right) + 1
二叉树的深度
# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def TreeDepth(self, pRoot):
        # write code here
        if pRoot == None:
            return 0
        if pRoot.left == None and pRoot.right==None:
            return 1
        return max(self.TreeDepth(pRoot.left),self.TreeDepth(pRoot.right))+1
二叉树的下一个结点

给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
思路:中序遍历的顺序为LVR
则有以下三种情况:
a. 如果该结点存在右子结点,那么该结点的下一个结点是右子结点树上最左子结点
b. 如果该结点不存在右子结点,且它是它父结点的左子结点,那么该结点的下一个结点是它的父结点
c. 如果该结点既不存在右子结点,且也不是它父结点的左子结点,则需要一路向祖先结点搜索,直到找到一个结点,该结点是其父亲结点的左子结点。如果这样的结点存在,那么该结点的父亲结点就是我们要找的下一个结点。

class Solution:
    def GetNext(self, pNode):
        # write code here
        # left root right
        if pNode == None: # 空节点
            return None
        if pNode.right: # 有右节点
            tmp = pNode.right
            while(tmp.left):
                tmp = tmp.left
            return tmp
        p = pNode.next # 没有右节点
        while(p and p.right==pNode):
            pNode = p
            p = p.next
        return p
对称的二叉树

Leetcode 101. Symmetric Tree
判断一棵树是不是左右对称的树

class Solution:
    def Symmetrical(self,Lnode,Rnode):
        if Lnode == None and Rnode == None:
            return True
        if Lnode and Rnode:
            return Lnode.val == Rnode.val and self.Symmetrical(Lnode.right,Rnode.left) and self.Symmetrical(Lnode.left,Rnode.right)
        else:
            return False
    def isSymmetrical(self, pRoot):
        # write code here
        if pRoot == None:
            return True
        return self.Symmetrical(pRoot.left,pRoot.right)
将二叉树按照层级转化为链表
    1
   / \
  2   3
 /
4
[
  1->null,
  2->3->null,
  4->null
]
"""
Definition of TreeNode:
class TreeNode:
    def __init__(self, val):
        this.val = val
        this.left, this.right = None, None
Definition for singly-linked list.
class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None
"""
class Solution:
    # @param {TreeNode} root the root of binary tree
    # @return {ListNode[]} a lists of linked list
    def binaryTreeToLists(self, root):
        # Write your code here
        #二叉树的层次遍历
        res_list = []
        if not root:
            return []
        
        q = [root]
        while q:
            new_q = []
            dummy = ListNode(0)
            pre = dummy
            
            for node in q:
                if node.left:
                    new_q.append(node.left)
                if node.right:
                    new_q.append(node.right)
                
                pre.next = ListNode(node.val)
                pre = pre.next
            
            q = new_q
            res_list.append(dummy.next)
        
        return res_list
把二叉树打印成多行

从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。

class Solution:
    # 返回二维列表[[1,2],[4,5]]
    def Print(self, pRoot):
        # write code here
        if pRoot == None:
            return []
        stack = [pRoot]
        ret = []

        while(stack):
            tmpstack = []
            tmp = []
            for node in stack:
                tmp.append(node.val)
                if node.left:
                    tmpstack.append(node.left)
                if node.right:
                    tmpstack.append(node.right)
            ret.append(tmp[:])
            stack = tmpstack[:]
        return ret
之字形打印二叉树
class Solution:
    def Print(self, pRoot):
        # write code here
        if pRoot == None:
            return []
        stack = [pRoot]
        step = 1
        ret = []
        while(stack):
            tmpstack = []
            tmp = []
            for node in stack:
                tmp+=[node.val]
                if node.left:
                    tmpstack.append(node.left)
                if node.right:
                    tmpstack.append(node.right)
            if step%2==0:
                tmp.reverse()
            ret.append(tmp)
            step += 1
            stack = tmpstack[:]
        return ret 
序列化和反序列化二叉树

Serialize and Deserialize Binary Tree

class Solution:
    def Serialize(self, root):
        # write code here
        def doit(node):
            if node: # 先序遍历的方式
                vals.append(str(node.val))
                doit(node.left)
                doit(node.right)
            else:
                vals.append('#')
        vals = []
        doit(root)
        return ' '.join(vals)

    def Deserialize(self, s):
        # write code here
        def doit():
            val = next(vals)
            if val == '#':
                return None
            node = TreeNode(int(val))
            node.left = doit()
            node.right = doit()
            return node
        vals = iter(s.split())
        return doit()
二叉平衡树中的第k小数

二叉搜索树中的第k大结点
Leetcode 230. Kth Smallest Element in a BST
思路:BST的中序遍历就是一个有序数组,需要注意到Leetcode中限制了k在[1,树结点个数]而牛客网没有,所以需要考虑k的值有没有超出

class Solution:
    # 返回对应节点TreeNode
    def KthNode(self, pRoot, k):
        # write code here
        if not pRoot:
            return 
        stack=[]
        cnt=0
        while pRoot or len(stack)>0:
            if pRoot is not None:
                stack.append(pRoot)
                pRoot = pRoot.left
            else:
                pRoot = stack.pop()
                cnt+=1
                if cnt==k:
                    return pRoot
                pRoot = pRoot.right
   
重建二叉树

Leetcode 105. Construct Binary Tree from Preorder and Inorder Traversal
根据先序、中序来构建二叉树

class Solution(object):
    def buildTree(self, pre, tin):
        """
        :type preorder: List[int]
        :type inorder: List[int]
        :rtype: TreeNode
        """
        if pre==[]:
            return None
        val = pre[0]
        idx = tin.index(val)
        ltin = tin[0:idx]
        rtin = tin[idx+1:]
        lpre = pre[1:1+len(ltin)]
        rpre = pre[1+len(ltin):]
        root = TreeNode(val)
        root.left = self.buildTree(lpre,ltin)
        root.right = self.buildTree(rpre,rtin)
        return root

Leetcode 106. Construct Binary Tree from Inorder and Postorder Traversal
根据中序和后序构建二叉树

class Solution(object):
    def buildTree(self, inorder, postorder):
        """
        :type inorder: List[int]
        :type postorder: List[int]
        :rtype: TreeNode
        """
        if postorder == []:
            return None
        val = postorder[-1]
        idx = inorder.index(val)
        lin = inorder[0:idx]
        rin = inorder[idx+1:]
        lpos = postorder[0:len(lin)]
        rpos = postorder[len(lin):-1]
        root = TreeNode(val)
        root.left = self.buildTree(lin,lpos)
        root.right = self.buildTree(rin,rpos)
        return root
二叉搜索树与双向链表

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

思考:左子树上最右结点 -> root -> 右子树上的最左结点

分情况讨论:1. 本节点结束;

​ 2.本节点有左右节点;

​ 2.1 有左子树,那本节点是左子树的最右侧的下一个

​ 2.1 有右子树,那本节点是右子树的最左侧的下一个(递归返回的保证是最左侧节点)

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def Convert(self, pRootOfTree):
        # write code here
        if pRootOfTree == None:
            return pRootOfTree
        if pRootOfTree.left == None and pRootOfTree.right == None:
            return pRootOfTree
        left = self.Convert(pRootOfTree.left)
        p = left
        if left:
            while(p.right):
                p = p.right
            p.right = pRootOfTree
            pRootOfTree.left = p
        right = self.Convert(pRootOfTree.right)
        if right:
            pRootOfTree.right = right
            right.left = pRootOfTree
        return left if left else pRootOfTree
判断树B是否是树A的子结构

输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

两个逻辑,一个是找到一个之后判断是否完全一样,另外一个是递归找到第一个一样的;

class Solution:
    # 给定两个二叉树(的根节点)A、B,判断B 是不是A 的二叉树
    def HasSubtree(self, pRoot1, pRoot2):
        if pRoot1 == None or pRoot2 == None:
            return False

        result = False
        if pRoot1.val == pRoot2.val:
            result = self.isSubtree(pRoot1, pRoot2)
        if result == False:
            result = self.HasSubtree(pRoot1.left, pRoot2) | self.HasSubtree(pRoot1.right, pRoot2)
        return result

    def isSubtree(self, root1, root2):
        if root2 == None:
            return True
        if root1 == None:
            return False
        if root1.val == root2.val:
            return self.isSubtree(root1.left, root2.left) & self.isSubtree(root1.right, root2.right)
        return False
        
二叉树中和为某一值的路径

输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。
思考:dfs

###### https://www.jiuzhang.com/problem/binary-tree-path-sum/#tag-lang-python
"""
Definition of TreeNode:
class TreeNode:
    def __init__(self, val):
        self.val = val
        self.left, self.right = None, None
"""
class Solution:
    # @param {TreeNode} root the root of binary tree
    # @param {int} target an integer
    # @return {int[][]} all valid paths
    def binaryTreePathSum(self, root, target):
        # Write your code here
        result = []
        path = []
        self.dfs(root, path, result, 0,  target)

        return result

    def dfs(self, root, path, result, len, target):
        if root is None:
            return
        path.append(root.val)
        len += root.val

        if root.left is None and root.right is None and len == target:
            result.append(path[:])

        self.dfs(root.left, path, result, len, target)
        self.dfs(root.right, path, result, len, target)

        path.pop()
######
### https://www.jiuzhang.com/problem/binary-tree-path-sum-ii/#tag-lang-python
"""
Definition of TreeNode:
class TreeNode:
    def __init__(self, val):
        self.val = val
        self.left, self.right = None, None
"""
class Solution:
    # @param {TreeNode} root the root of binary tree
    # @param {int} target an integer
    # @return {int[][]} all valid paths
    def binaryTreePathSum2(self, root, target):
        # Write your code here
        result = []
        path = []
        if root is None:
            return result
        self.dfs(root, path, result, 0,  target)
        return result

    def dfs(self, root, path, result, l, target):
        if root is None:
            return
        path.append(root.val)
        tmp = target
        for i in xrange(l , -1, -1):
            tmp -= path[i]
            if tmp == 0:
                result.append(path[i:])

        self.dfs(root.left, path, result, l + 1, target)
        self.dfs(root.right, path, result, l + 1, target)

        path.pop()
        
class Solution:
    # 返回二维列表,内部每个列表表示找到的路径
    def FindPath(self, root, expectNumber):
        # write code here
        ret = []
        def dfs(root,sum_,tmp):
            if root:
                if root.left==None and root.right == None:
                    if root.val == sum_:
                        tmp.append(root.val)
                        ret.append(tmp[:])
                else:
                    tmp.append(root.val)
                    dfs(root.left,sum_-root.val,tmp[:])
                    dfs(root.right,sum_-root.val,tmp[:])
        dfs(root,expectNumber,[])
        return ret

####
二叉搜索树的后序遍历序列

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
思考:后序遍历意味着num[-1]为root,那么根据这个值和二叉搜索树的性质,可以把数组划分成两个部分,left 和 right ,再递归判断

# coding:utf-8
class Solution:
    def VerifySquenceOfBST(self, sequence):
        # write code here
        if sequence==None or len(sequence)==0:
            return False
        length=len(sequence)
        root=sequence[length-1]
        # 在二叉搜索 树中 左子树节点小于根节点
        for i in range(length):#循环的值设置得很巧妙,和下方的left、right初始值对应
            if sequence[i]>root:
                break
        # 二叉搜索树中右子树的节点都大于根节点
        for j  in range(i,length):
            if sequence[j]<root:
                return False
        # 判断左子树是否为二叉树
        left=True
        if  i>0:#说明存在左子树,求左子树是否为后续遍历,然后更新left的值
            left=self.VerifySquenceOfBST(sequence[0:i])
        # 判断右子树是否为二叉树
        right=True
        if j<length-1:#说明存在右子树,判断右子树是否为后续遍历。如果不存在右子树,默认为真。
            right=self.VerifySquenceOfBST(sequence[i:-1])
        return left and right
最近公共祖先

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

给定一棵二叉树以及这棵树上的两个节点 o1 和 o2,请找到 o1 和 o2 的最近公共祖先节点。

示例1

[3,5,1,6,2,0,8,#,#,7,4],5,1
3
# https://www.jiuzhang.com/problem/lowest-common-ancestor/#tag-lang-python
class Solution:
    """
    @param: root: The root of the binary search tree.
    @param: A: A TreeNode in a Binary.
    @param: B: A TreeNode in a Binary.
    @return: Return the least common ancestor(LCA) of the two nodes.
    """
    def lowestCommonAncestor(self, root, A, B):
        if root is None:
            return None
        if root == A or root == B:
            return root
        left = self.lowestCommonAncestor(root.left, A, B)
        right = self.lowestCommonAncestor(root.right, A, B)
        if left and right:
            return root
        if left:
            return left
        if right:
            return right
        return None
    

6.复杂数据结构

顺时针打印矩阵
# -*- coding:utf-8 -*-
class Solution:
    # matrix类型为二维列表,需要返回列表
    def printMatrix(self, matrix):
        # write code here
        ans=[]
        m=len(matrix)
        if m==0:
            return ans
        n=len(matrix[0])
		# 边界范围
        upper_i =0;lower_i=m-1;left_j=0;right_j=n-1
        # 整体计数,确定何时结束
        num=1
        # 轮询
        i=0;j=0
        # 轮询方向
        right_pointer=1
        down_pointer=0
        while(num<=m*n):
            ans.append(matrix[i][j])
            if right_pointer==1: # 判断朝向
                if j<right_j: # 没有到边界
                    j=j+1
                else: # 到边界
                    right_pointer=0
                    down_pointer=1
                    upper_i = upper_i+1
                    i = i+1
            elif down_pointer == 1:
                if i<lower_i:
                    i = i+1
                else:
                    right_pointer=-1
                    down_pointer=0
                    right_j = right_j -1
                    j = j-1
            elif right_pointer ==-1:
                if j > left_j:
                    j=j-1
                else:
                    right_pointer=0
                    down_pointer=-1
                    lower_i =lower_i-1
                    i = i-1
            elif down_pointer == -1:
                if i > upper_i:
                    i=i-1
                else:
                    right_pointer=1
                    down_pointer=0
                    left_j = left_j +1
                    j = j+1
            num=num+1
        return ans
数据流中的中位数

Find Median from Data Stream

from heapq import *
#heapq默认就是最小堆,large 是最小堆,但是都是正数所以比较大的最小堆,small是最大堆,但是都是负数,所以是比较小的最大堆;
#1.首先将large 跟当前num做判断,然后弹出最小的那个,放入最大堆;这里large的个数并没有增加,只是增加了小的,
#2.如果large个数比较小,就在向large里面放,
#3.中位数如果是奇数个,那么它一定在large里面;如果是偶数个,那就求个平均数;
class MedianFinder:
    def __init__(self):
        self.heaps = [], []

    def addNum(self, num):
        small, large = self.heaps
        heappush(small, -heappushpop(large, num))
        if len(large) < len(small):
            heappush(large, -heappop(small))

    def findMedian(self):
        small, large = self.heaps
        if len(large) > len(small):
            return float(large[0])
        return (large[0] - small[0]) / 2.0
滑动窗口的最大值

给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{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个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
思考:假设当前窗口起始位置为start,结束位置为end,我们要构造一个stack, 使得stack[0]为区间[start,end]的最大值。

窗口的滑动过程中数字的进出类似一个队列中元素的出队入队,这里我们采用一个队列queue存储可能成为最大值的元素下标(之所以队列中存元素下标而不是元素值本身,是因为队列并不存储所有元素,而我们需要知道什么时候队首元素已经离开滑动窗口)。当遇到一个新数时,将它与队尾元素比较,如果大于队尾元素,则丢掉队尾元素,继续重复比较,直到新数小于队尾元素,或者队列为空为止,将新数下标放入队列。同时需要根据滑动窗口的移动判断队首元素是否已经离开

### 暴力解法
class Solution:
    def maxInWindows(self, num, size):
        # write code here

        ans=[]
        if not num or size==0:
            return ans 
        for i in range(len(num)-size+1):
            mx=0
            for j in range(size):
                mx=max(mx,num[i+j])
            ans.append(mx)
        return ans
### 单调队列解法  
## https://blog.csdn.net/qq_20141867/article/details/81088705
# -*- coding:utf-8 -*-
class Solution:
    def maxInWindows(self, num, size):
        # write code here
        # 存放可能是最大值的下标,单调递增的队列
        maxqueue = []
        # 存放窗口中最大值
        maxlist = []
        n = len(num)
        # 参数检验
        if n == 0 or size == 0 or size > n:
            return maxlist
        for i in range(n):
            # 判断队首下标对应的元素是否已经滑出窗口
            if len(maxqueue) > 0 and i - size >= maxqueue[0]:
                maxqueue.pop(0)
            # 判断新入的是否比第0 index的要大,如果是就要全部替换掉;
            while len(maxqueue) > 0 and num[i] > num[maxqueue[-1]]:
                maxqueue.pop()
            maxqueue.append(i)
            if i >= size - 1:
                maxlist.append(num[maxqueue[0]])
        return maxlist
用两个栈实现队列

思考:栈FILO,队列FIFO

# -*- coding:utf-8 -*-
class Solution:
    def __init__(self):
        self.stack1 = []
        self.stack2 = []
    def push(self, node):
        # write code here
        self.stack1.append(node)

    def pop(self):
        # return xx
        if len(self.stack2):
            return self.stack2.pop()
        while(self.stack1):
            self.stack2.append(self.stack1.pop())
        return self.stack2.pop()
包含min函数的栈

定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数。

# -*- coding:utf-8 -*-
class Solution:

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.stack = []
        self.minstack =[]

    def push(self, x):
        """
        :type x: int
        :rtype: void
        """
        self.stack.append(x)
        if len(self.minstack)==0 or self.minstack[-1][0]>x:
            self.minstack.append((x,1))
        elif self.minstack[-1][0] == x:
            self.minstack[-1] = (x,self.minstack[-1][1]+1)

    def pop(self):
        """
        :rtype: void
        """
        ans = self.stack[-1]
        self.stack = self.stack[0:len(self.stack)-1]
        if ans == self.minstack[-1][0]:
            if self.minstack[-1][1] == 1:
                self.minstack.remove(self.minstack[-1])
            else:
                self.minstack[-1] = (ans,self.minstack[-1][1]-1)
        return ans


    def top(self):
        """
        :rtype: int
        """
        return self.stack[-1]


    def min(self):
        """
        :rtype: int
        """
        return self.minstack[-1][0]
栈的压入、弹出序列

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

# -*- coding:utf-8 -*-
class Solution:
    def IsPopOrder(self, pushV, popV):
        # write code here
        stack = []
        while(popV):
            if pushV and pushV[0]==popV[0]:
                pushV.pop(0)
                popV.pop(0)
            elif stack and stack[-1]==popV[0]:
                stack.pop()
                popV.pop(0)
            elif pushV:
                stack.append(pushV.pop(0))
            else:
                return False
        return True
矩阵中的路径

思考:dfs加记忆化搜索

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VAqHxN4O-1608185509773)(C:\Users\lijingjie\AppData\Roaming\Typora\typora-user-images\image-20201202162808900.png)]

矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。

class Solution:
    def dfs(self, matrix, flag, rows, cols, r, c, s):
        if s == '':
            return True
        dx = [-1, 1, 0, 0]
        dy = [0, 0, -1, 1]  # 利用两个数组,来实现对每个格子周围格子的访问
        for k in range(4):
            x = dx[k] + r
            y = dy[k] + c
            if x >= 0 and x < rows and y >= 0 and y < cols and flag[x][y] and matrix[x * cols + y] == s[0]:
                flag[x][y] = False  # 修改当前格子的标识
                if self.dfs(matrix, flag[:], rows, cols, x, y, s[1:]):  # 递归
                    return True
                flag[x][y] = True
                # 如果上一个判断条件返回的是False,那么就说明这个格子目前还不是路径上的格子,再把当前格子的标识修改回来。
        return False

    def hasPath(self, matrix, rows, cols, path):
        if path == '':
            return True
        flag = [[True for c in range(cols)] for r in range(rows)]  # 定义一个表示矩阵
        for r in range(rows):
            # 对这个矩阵中的元素进行遍历,不断找路径进入矩阵的起点,直到以某个格子为起点找到整个路径为止。
            for c in range(cols):
                if matrix[r * cols + c] == path[0]:
                    flag[r][c] = False
                    if self.dfs(matrix, flag[:], rows, cols, r, c, path[1:]):
                        return True
                    flag[r][c] = True
        return False
机器人的运动范围

地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
思考:dfs+记忆化搜索,注意虽然说是可以向左右上下走动,但是因为是从坐标0,0开始,所以只需要向左或者向下走动就行;

# -*- coding:utf-8 -*-
class Solution:
    def movingCount(self, threshold, rows, cols):
        # write code here
        memories = set()
        def dfs(i,j):
            def judge(i,j):
                return sum(map(int, list(str(i)))) + sum(map(int, list(str(j)))) <= threshold
            if not judge(i,j) or (i,j) in memories:
                return
            memories.add((i,j))
            if i != rows - 1:
                dfs(i + 1, j)
            if j != cols - 1:
                dfs(i, j + 1)
        dfs(0,0)
        return len(memories)
  • 4
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值