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

北战南征 提交于 2020-12-12 10:53:08

文章目录

0.递归&脑力

斐波那契数列

# -*- 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]

跳台阶
一只青蛙一次可以跳上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

##二分法
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(p)== 1时 要满足len(s) == 1 AND (p[0] == s[0] OR p[0] == ‘.’)
当len(p)> 1时,要讨论p[1] 是不是为’*’ ,因为如果p[1]==’*’ 时候 可能会是p[2:] 和 s 匹配情况
但当p[1]!=’*’ 时候 意味着 必须要关注是否 p[0] == s[0] 或者 p[0] == ’.’
那么这两个可以合并为
IF len(p) == 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:])]

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])

和为S的连续正数序列

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

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

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

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

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

方法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

最小的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

数组中出现次数超过一半的数字

思考:摩尔投票法

# -*- 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;
}

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的情况可能是:100 ~ 199,1100 ~ 1199,2100 ~ 2199,,…,11100 ~ 11199,一共1200个。可以看出是由更高位数字(12)决定,并且等于更高位数字(12)乘以 当前位数(100)。
② 如果百位上数字为1,百位上可能出现1的次数不仅受更高位影响还受低位影响。比如:12113,则可以知道百位受高位影响出现的情况是:100 ~ 199,1100 ~ 1199,2100 ~ 2199,,…,11100 ~ 11199,一共1200个。和上面情况一样,并且等于更高位数字(12)乘以 当前位数(100)。但同时它还受低位影响,百位出现1的情况是:12100~12113,一共114个,等于低位数字(113)+1。
③ 如果百位上数字大于1(2~9),则百位上出现1的情况仅由更高位决定,比如12213,则百位出现1的情况是:100 ~ 199,1100 ~ 1199,2100 ~ 2199,…,11100 ~ 11199,12100 ~ 12199,一共有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)

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

链表中倒数第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
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: # 中序遍历 + cnt 计数
    # 返回对应节点TreeNode
    def KthNode(self, pRoot, k):
        # write code here
        stack = []
        node = pRoot
        while node:
            stack.append(node)
            node = node.left
        cnt = 1
        while(stack and cnt<=k):
            node = stack.pop()
            right = node.right
            while right:
                stack.append(right)
                right = right.left
            cnt+=1
        if node and k==cnt-1:
            return node
        return None
   

重建二叉树

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

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

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

# -*- 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)


    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加记忆化搜索

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)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!