剑指offer(python实现)

多米诺骨牌

在这里插入图片描述

  • 如果 “A。。。B”, 当 A = B, 则输出应为 “AAAAAA”.
  • 如果 “R。。。。L”, 输出应为 “RRRLLL”, 如果中间有奇数个点"R。。。。。L"则应输出"RRR.LLL"
  • 如果为 “L。。。R” , 我们不需要做任何事,原样输出就行
def pushDominoes(dominoes):
	stat = [(i, char) for i, char in enumerate(dominoes) if char != '.']
	stat = [(-1, 'L')] + stat + [(len(dominoes), 'R')]

	print(stat)
	for (i,ci), (j, cj) in zip(stat[:-1], stat[1:]):
		print(i, j, ci, cj)
		if ci == cj:
		    if i < 0:
		        dominoes = ci * (j-i) + dominoes[j+1:]
		    elif j == len(dominoes):
		        dominoes = dominoes[:i] + ci * (j-i) 
		    else:   
		        dominoes = dominoes[:i] + ci * (j-i+1) + dominoes[j+1:]

		elif ci > cj: # R > L
		    half = (j-i+1) // 2
		    rest = (j-i+1) % 2
		    dominoes = dominoes[:i] + 'R'*half + '.'*rest + 'L'*half + dominoes[j+1:]
	return dominoes

1. 二维数组的查找

在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
在这里插入图片描述
python代码:

    # array 二维列表
    def Find(target, array):
        # write code here
        while len(array)>0 and len(array[0])>0:
            j = len(array[0])
            if array[0][j-1] > target:
                array = [a[: j-1] for a in array]
            elif array[0][j-1] < target:
                array = array[1:]
            elif array[0][j-1] == target:
                return True
        return False

2. 重建二叉树

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

解题思路:
前序遍历:第一个数总是树的根结点的值
中序遍历:根结点的值在序列的中间,左子树的结点的值位于根结点的值的左边,右子树的结点位于根结点的值的右边

前序遍历序列{1,2,4,7,3,5,6,8}中数字1就是根结点的值;扫描中序遍历,就能确定根结点的值的位置;根据中序遍历的特点,在根结点的值1前面的三个数字都是左子树结点的值,位于1后面的数字都是右子树结点的值。这样就找到了左,右子树对应的前序遍历序列和中序遍历序列,接下来可以用递归的方法去完成。

python代码:

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    # 返回构造的TreeNode根节点
    def reConstructBinaryTree(self, pre, tin):
        # write code here
        if not pre or not tin:
            return None
        root = TreeNode(pre.pop(0))
        index = tin.index(root.val)
        
        root.left = self.reConstructBinaryTree(pre, tin[:index])
        root.right = self.reConstructBinaryTree(pre, tin[index+1:])
        return root

旋转数组的最小数字

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

若要求在排序的数组(或者部分排序的数组)中查找一个数字或者统计某个数字出现的次数,我们都可以尝试用二分查找算法

python代码:

# -*- coding:utf-8 -*-
class Solution:
    def minNumberInRotateArray(self, rotateArray):
        # write code here
        if len(rotateArray) == 0:
            return 0
        i = 0
        j = len(rotateArray)-1
        
        while rotateArray[i] >= rotateArray[j]:
            if (j-i) == 1:
                mid_idx = j
                break
                
            mid_idx = (i+j)/2
            #如果下标为i,mid_idx,j指向的三个数字相等,则只能顺序查找
            if rotateArray[i] == rotateArray[j] and rotateArray[mid_idx] == rotateArray[i]:
                return self.minInorder(rotateArray, i, j)
            
            if rotateArray[mid_idx] >= rotateArray[i]:
                i = mid_idx
            else:
                j = mid_idx
                
        return rotateArray[mid_idx]
    
    def minInorder(self, array, start, end):
        mins = array[start]
        for i in range(end):
            if mins > array[end]:
                mins = array[end]
        return mins

跳台阶

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

解题思路: 1级台阶,只有一种跳法,2级台阶,两种跳法;接下来是一般情况,把n级台阶看成是n的函数,记为f(n), 当n>2时,第一次跳的时候就有两种不同的选择:(1)第一次只跳1级,此时跳法数目=后面剩下的n-1级台阶的跳法数目,即为f(n-1);(2)第一次跳2级,此时跳法数目等于后面剩下的n-2级台阶的跳法数目,即为f(n-2);显然此时转化为斐波那契数列了。

# -*- coding:utf-8 -*-
class Solution:
    def jumpFloor(self, number):
        # write code here
        if number == 1:
            return 1
        if number == 2:
            return 2
        f = [None for i in range(number)]
        f[0] = 1
        f[1] = 2
        i = 0
        number -= 1
        while number >= 2:
            f[i+2] = f[i+1] + f[i]
            number -= 1
            i += 1
        return f[-1]

变形: 一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
解题思路:
因为n级台阶,第一步有n种跳法:跳1级、跳2级、到跳n级
跳1级,剩下n-1级,则剩下跳法是f(n-1)
跳2级,剩下n-2级,则剩下跳法是f(n-2)
所以f(n)=f(n-1)+f(n-2)+…+f(1)
因为f(n-1)=f(n-2)+f(n-3)+…+f(1)
所以f(n)=2*f(n-1)

# -*- coding:utf-8 -*-
class Solution:
    def jumpFloorII(self, number):
        # write code here
        a = 1
        return a<<(number-1)

顺时针打印矩阵

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.
在这里插入图片描述
我们可以用一个循环来打印矩阵,每一次打印矩阵中的一个圈。

这个代码在牛客上编译没通过,但在sublime上编译通过了,不知道为什么??????

# -*- coding:utf-8 -*-
class Solution:
    # matrix类型为二维列表,需要返回列表
    def printMatrix(self, matrix):
        # write code here
        if len(matrix) <= 0 or len(matrix[0]) <= 0:
            return
        
        start = 0
        while len(matrix) > 2*start and len(matrix[0])> 2*start:
            self.print_circle(matrix, start)
            start += 1
    
    def print_circle(self, matrix, start):
        endY = len(matrix) - 1 - start 
        endX = len(matrix[0]) - 1 - start
        for i in range(start, endX+1):
            print(matrix[start][i])
            
        if start < endY:
            for i in range(start+1, endY+1):
                print(matrix[i][endX])
        
        if start<endX and start<endY:
            for i in range(endX-1, start-1, -1):
                print(matrix[endY][i])
                
        if start<endX and start<endY-1:
            for i in range(endY-1, start, -1):
                print(matrix[i][start])

从上往下打印二叉树(层序遍历)

从上往下打印出二叉树的每个节点,同层节点从左至右打印。

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    # 返回从上到下每个节点值列表,例:[1,2,3]
    def PrintFromTopToBottom(self, root):
        # write code here
        if not root:
            return []
        
        # 借助队列来实现
        queue = [root]
        res = []
        while len(queue)>0:
            node = queue.pop(0)
            res.append(node.val)
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)
        return res

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

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。

# -*- coding:utf-8 -*-
class Solution:
    def VerifySquenceOfBST(self, sequence):
        # write code here
        # [5,7,6,9,11,10,8]
        if sequence == None or len(sequence) == 0:
            return False
        
        # 在二叉搜索树中左子树的结点小于根结点
        for i in range(len(sequence)):
            if sequence[i] > sequence[-1]:
                break
        
        # 在二叉搜索数中右子树的结点大于根结点
        for j in range(i, len(sequence)):
            if sequence[j] < sequence[-1]:
                return False
        
        # 判断左子树是不是二叉搜索树
        left = True
        if i > 0:
            left = self.VerifySquenceOfBST(sequence[ : i])
        
        # 判断右子树是不是二叉搜索树
        right = True
        if i < len(sequence)-1:
            right = self.VerifySquenceOfBST(sequence[i : -1])
                
        return left and right

二叉树中和为某一值的路径

输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    # 返回二维列表,内部每个列表表示找到的路径
    def FindPath(self, root, expectNumber):
        # write code here
        if not root:
            return []
        
        res = []
        
        def FindPathMain(root, path, currentSum):
            currentSum += root.val

            path.append(root)
            isLeaf = root.left == None and root.right == None

            if currentSum == expectNumber and isLeaf:
                onePath = []
                for node in path:
                    onePath.append(node.val)
                res.append(onePath)

            if currentSum < expectNumber:
                if root.left:
                    FindPathMain(root.left, path, currentSum)
                if root.right:
                    FindPathMain(root.right, path, currentSum)

            path.pop()
            
        FindPathMain(root,[],0)
        
        return res

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

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。

思路一:
在遍历数组时保存两个值:一是数组中一个数字,一是次数。遍历下一个数字时,若它与之前保存的数字相同,则次数加1,否则次数减1;若次数为0,则保存下一个数字,并将次数置为1。遍历结束后,所保存的数字即为所求。然后再判断它是否符合条件即可。

# -*- coding:utf-8 -*-
class Solution:
    def MoreThanHalfNum_Solution(self, numbers):
        # write code here
        if len(numbers) == 0:
            return None
        
        cnt = 1
        res = numbers[0]
        for i in range(len(numbers)):
            if cnt == 0:
                res = numbers[i]
                cnt = 1
            elif res == numbers[i]:
                cnt += 1
            else:
                cnt -= 1
        time = 0
        for i in range(len(numbers)):
            if res == numbers[i]:
                time += 1
        if time > len(numbers)/2:
            return res
        else:
            return 0

思路二:(快排)
对数组进行快速排序,然后再判断数组中间位置的数字出现的次数是否超过数组长度的一半。

# -*- coding:utf-8 -*-
class Solution:
    def MoreThanHalfNum_Solution(self, numbers):
        # write code here
        self.quicksort(numbers, 0, len(numbers)-1)
        
        mid = numbers[len(numbers)/2]
        cnt = 0
        for i in range(len(numbers)):
            if mid == numbers[i]:
                cnt += 1
        if cnt >len(numbers)/2:
            return mid
        else:
            return 0

    # 快速排序
    def quicksort(self, numbers, left, right):
        if left >right:
            return 
        
        i = left
        j = right
        key = numbers[i]
        
        while i < j:
            while i < j and numbers[j] >= key:
                j -= 1
            while i < j and numbers[i] <= key:
                i += 1
            if i < j:
                numbers[i], numbers[j] = numbers[j], numbers[i]
        
        numbers[left] = numbers[i]
        numbers[i] = key
        
        self.quicksort(numbers, left, i-1)
        self.quicksort(numbers, i+1, right)

最小的K个数

输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。

**解题思路:**基于堆排序算法,构建最大堆。时间复杂度为O(nlogk);如果用快速排序,时间复杂度为O(nlogn);如果用冒泡排序,时间复杂度为O(n*k)。

# -*- coding:utf-8 -*-
class Solution:
    def GetLeastNumbers_Solution(self, tinput, k):
        # write code here
        if k > len(tinput):
            return []
        
        length = len(tinput)/2 - 1 
        for i in range(length, -1, -1):
            self.min_heap(tinput, i, len(tinput)-1)
        
        for i in range(len(tinput)-1, -1, -1):
            tinput[0], tinput[i] = tinput[i], tinput[0]
            self.min_heap(tinput, 0, i-1)
        
        return tinput[:-(k+1):-1]
    
    # 建立最小堆 
    def min_heap(self, arr, start, end):
        dad = start
        son = start*2 + 1
        
        while son <= end:
            if son + 1 <= end and arr[son] > arr[son+1]:
                son = son + 1
            if arr[dad] < arr[son]:
                return 
            else:
                arr[dad], arr[son] = arr[son], arr[dad]
                dad = son
                son = son*2 + 1

连续子数组的最大和

{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和。

**思路:**动态规划

# -*- coding:utf-8 -*-
class Solution:
    def FindGreatestSumOfSubArray(self, array):
        # write code here
        res = array[0]   # 最大子数组的和
        Max = array[0]   # 累加的子数组的和
        for i in range(1, len(array)):
            Max = max(Max + array[i], array[i])
            res = max(Max, res)
        return res

整数中1出现的次数(从1到n整数中1出现的次数)

求出1-13的整数中1出现的次数,并算出 100-300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次。

思路1(暴力解法):

# -*- coding:utf-8 -*-
class Solution:
    def NumberOf1Between1AndN_Solution(self, n):
        # write code here
        res = 0
        for i in range(1,n+1):
            cnt = 0
            while i != 0:
                if i % 10 == 1:
                    cnt += 1
                i = i / 10
            res += cnt
        return res 

思路二:(找数学规律)
一、1的数目
编程之美上给出的规律:

  1. 如果第i位(自右至左,从1开始标号)上的数字为0,则第i位可能出现1的次数由更高位决定(若没有高位,视高位为0),等于更高位数字X当前位数的权重10i-1。

  2. 如果第i位上的数字为1,则第i位上可能出现1的次数不仅受更高位影响,还受低位影响(若没有低位,视低位为0),等于更高位数字X当前位数的权重10i-1+(低位数字+1)。

  3. 如果第i位上的数字大于1,则第i位上可能出现1的次数仅由更高位决定(若没有高位,视高位为0),等于(更高位数字+1)X当前位数的权重10i-1。

二、X的数目

这里的 X∈[1,9] ,因为 X=0 不符合下列规律,需要单独计算。

首先要知道以下的规律:

  • 从 1 至 10,在它们的个位数中,任意的 X 都出现了 1 次。
  • 从 1 至 100,在它们的十位数中,任意的 X 都出现了 10 次。
  • 从 1 至 1000,在它们的百位数中,任意的 X 都出现了 100 次。
  • 依此类推,从 1 至 10 i ,在它们的左数第二位(右数第 i 位)中,任意的 X 都出现了 10 i−1 次。

这个规律很容易验证,这里不再多做说明。

接下来以 n=2593,X=5 为例来解释如何得到数学公式。从 1 至 2593 中,数字 5 总计出现了 813 次,其中有 259 次出现在个位,260 次出现在十位,294 次出现在百位,0 次出现在千位。

现在依次分析这些数据

  • 首先是个位。从 1 至 2590 中,包含了 259 个 10,因此任意的 X 都出现了 259 次。最后剩余的三个数 2591, 2592 和 2593,因为它们最大的个位数字 3 < X,因此不会包含任何 5。(也可以这么看,3<X,则个位上可能出现的X的次数仅由更高位决定,等于更高位数字(259)x 101-1=259)。

  • 然后是十位。从 1 至 2500 中,包含了 25 个 100,因此任意的 X 都出现了 25×10=250 次。剩下的数字是从 2501 至 2593,它们最大的十位数字 9 > X,因此会包含全部 10 个 5。最后总计 250 + 10 = 260。(也可以这么看,9>X,则十位上可能出现的X的次数仅由更高位决定,等于更高位数字(25+1)x 102-1=260)。

  • 接下来是百位。从 1 至 2000 中,包含了 2 个 1000,因此任意的 X 都出现了 2×100=200 次。剩下的数字是从 2001 至 2593,它们最大的百位数字 5 == X,这时情况就略微复杂,它们的百位肯定是包含 5 的,但不会包含全部 100 个。如果把百位是 5 的数字列出来,是从 2500 至 2593,数字的个数与百位和十位数字相关,是 93+1 = 94。最后总计 200 + 94 = 294。(也可以这么看,5==X,则百位上可能出现X的次数不仅受更高位影响,还受低位影响,等于更高位数字(2)x 103-1+(93+1)=294)。

  • 最后是千位。现在已经没有更高位,因此直接看最大的千位数字 2 < X,所以不会包含任何 5。(也可以这么看,2<X,则千位上可能出现的X的次数仅由更高位决定,等于更高位数字(0)x 104-1=0)。

到此为止,已经计算出全部数字 5 的出现次数。

总结一下以上的算法,可以看到,当计算右数第 i 位包含的 X 的个数时:

取第 i 位左边(高位)的数字,乘以 10 i−1 ,得到基础值 a 。
取第 i 位数字,计算修正值:
如果大于 X,则结果为 a+ 10 i−1
如果小于 X,则结果为 a 。
如果等 X,则取第 i 位右边(低位)数字,设为 b ,最后结果为 a+b+1 。
相应的代码非常简单,效率也非常高,时间复杂度只有 O( log 10 n) 。

# -*- coding:utf-8 -*-
class Solution:
    def NumberOf1Between1AndN_Solution(self, n):
        # write code here
        if n<0:
            return 0
        
        high = n
        i = 1
        res = 0
        while high != 0:
            high = n/(10**i)   # 获取第i位的高位
            tmp = n%(10**i)    
            
            curr = tmp/(10**(i-1))  # 获取第i位
            low = tmp%(10**(i-1))   # 获取第i位的低位
             
            if curr == 1:    
                res += high*(10**(i-1)) + low + 1
            elif curr < 1:
                res += high*(10**(i-1))
            else:
                res += (high+1)*(10**(i-1))
            
            i += 1
        return res

丑数

把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。

思路一: 暴力解法

# -*- coding:utf-8 -*-
class Solution:
    def GetUglyNumber_Solution(self, index):
        # write code here
        i = 0
        while index:
            i += 1
            if self.isUgly(i):
                index -= 1
        return i

    def isUgly(self, num):
        while num%2 == 0:
            num /= 2
        while num%3 == 0:
            num /= 3
        while num%5 == 0:
            num /= 5
        
        if num == 1:
            return True
        else:
            return False

思路二:

# -*- coding:utf-8 -*-
class Solution:
    def GetUglyNumber_Solution(self, index):
        # write code here
        if index == 0:
            return 0
        num = [1 for i in range(index)]
        n2, n3, n5 = 0, 0, 0

        i = 1
        while i < index:
            num[i] = min(num[n2]*2, min(num[n3]*3, num[n5]*5))
            if num[i] == num[n2]*2:
                n2 += 1
            if num[i] == num[n3]*3:
                n3 += 1
            if num[i] == num[n5]*5:
                n5 += 1
            i += 1
            
        return num[-1]

非递减数列

给定一个长度为 n 的整数数组,你的任务是判断在最多改变 1 个元素的情况下,该数组能否变成一个非递减数列。
我们是这样定义一个非递减数列的: 对于数组中所有的 i (1 <= i < n),满足 array[i] <= array[i + 1]。

class Solution(object):
    def checkPossibility(self, nums):
        """
        :type nums: List[int]
        :rtype: bool
        """
        cnt = 0
        
        for i in range( len(nums)-1):
            # 如果存在前面的数大于后面的数
            if nums[i+1] < nums[i]:
                cnt += 1
                if i - 1 < 0 or nums[i-1]<nums[i+1]:
                    nums[i] = nums[i+1]
                else:
                    nums[i+1] = nums[i]
            if cnt > 1:
                return False
        return True

数组中只出现一次的数字

一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

# -*- coding:utf-8 -*-
class Solution:
    # 返回[a,b] 其中ab是出现一次的两个数字
    def FindNumsAppearOnce(self, array):
        # write code here
        # 异或结果为只出现一次的两个数字异或
        flag = 0
        for i in array:
            flag = i^flag
        
        ####以某一位是否为1来分离出现一次的两个数字
        idx = 0
        while flag & 1 == 0:
            flag >>= 1
            idx += 1
        
        # 分为两组,只出现一次的两个数字分别在两个组,再分别异或
        a, b = 0, 0
        for i in array:
            if self.isbit(i, idx):
                a = a^i
            else:
                b = b^i
        return [a,b]

    # 判断num的从低到高的idx位是否为1 
    def isbit(self, num, idx):
        num >>= idx
        return num & 1

和为S的连续正数序列

输入一个正数s,打印出所有和为s的连续正数序列(至少含有两个数)。

# -*- coding:utf-8 -*-
class Solution:
    def FindContinuousSequence(self, tsum):
        # write code here
        i = 1
        j = 2
        res = []
        
        while j <= tsum//2+1:
            sum_all = 0
            for x in range(i,j+1):
                sum_all += x
            
            if sum_all == tsum:
                res.append(list(range(i, j+1)))
                j += 1
                continue
            elif sum_all < tsum:
                j += 1
            else:
                i += 1
        return res

圆圈中最后剩下的数字

0,1,…,n-1这n个数字排成一个圆圈,从数字0开始每次从这个圆圈里删除第m个数字。求出这个圆圈里剩下的最后一个数字。

方法1:最简单明了的思路:

try:
    while True:
        n = input()
        num = []
        for i in range(n):
            num.append(i)
            
        cnt = 0
		# 用队列模拟,队首取数,用一个计数器计数,隔2个删一个,其他的重新放到队尾
        while len(num)!= 1:
            if cnt != 2:
                temp = num[0]
                num = num[1:]
                num.append(temp)
                cnt += 1
            else:
                num = num[1:]
                cnt = 0
        print(num[0])
except:
    pass

方法二:数学关系
在这里插入图片描述在这里插入图片描述
递归解法:

class Solution:
    def LastRemaining_Solution(self, n, m):
        # write code here
        # 递归法
        if n == 0:
            return -1
        if n == 1:
            return 0
        return (self.LastRemaining_Solution(n-1, m) + m) % n

循环解法:

class Solution:
    def LastRemaining_Solution(self, n, m):
        # 循环法
        if n < 1 or m < 1:
            return -1
        last = 0
        for i in range(2, n+1):
            last = (last + m)%i
        return last

按之字形顺序打印二叉树

请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def Print(self, pRoot):
        # write code here
        if not pRoot:
            return []
        res = []
        row = 0
        node_list = [pRoot]
        while node_list != []:
            row_list = []
            child_list = []
            for node in node_list:
                row_list.append(node.val)
                if node.left:
                    child_list.append(node.left)
                if node.right:
                    child_list.append(node.right)
            node_list = child_list
            if row % 2 == 1:
                row_list.reverse()
            res.append(row_list)
            row += 1
        return res

二叉树的序列化与反序列化

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def Serialize(self, root):
        # write code here
        s = ''
        queue = [root]
        while queue:
            root = queue.pop(0)
            if root:
                s += str(root.val)
                queue.append(root.left)
                queue.append(root.right)
            else:
                s += "#"
            s += ','
        return s
    def Deserialize(self, s):
        # write code here
        tree = s.split(',')
        if tree[0] == '#':
            return None
        queue = []
        root = TreeNode(int(tree[0]))
        queue.append(root)
        i = 1
        while queue:
            cur = queue.pop(0)
            if cur == None:
                continue
            cur.left = TreeNode(int(tree[i])) if tree[i]!='#' else None
            cur.right = TreeNode(int(tree[i+1])) if tree[i+1]!='#' else None
            
            i += 2
            queue.append(cur.left)
            queue.append(cur.right)
        return root
        

二叉搜索树中第K小的元素

给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。

解题思路: 由于是二叉搜索树,可以直接中序遍历

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    # 返回对应节点TreeNode
    # 中序遍历
    def KthNode(self, pRoot, k):
        # write code here
        if pRoot == None:
            return
        stack = []
        node = pRoot
        cnt = 0
        while node or stack:
            while node:
                stack.append(node)
                node = node.left
            node = stack.pop()
            cnt += 1
            if cnt == k:
                return node
            node = node.right

数据流中的中位数

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。

思路一: 下面这种方法时间复杂度较高,如果要降低时间复杂度,还得用最大堆,最小堆的方法

# -*- coding:utf-8 -*-
class Solution:
    def __init__(self):
        self.arr = []
    def Insert(self, num):
        # write code here
        self.arr.append(num)
        self.arr.sort()
    def GetMedian(self, fuck):
        # write code here
        length = len(self.arr)
        if length % 2 == 1:
            return self.arr[length//2]
        else:
            res = self.arr[length//2] + self.arr[length//2-1]
            return res/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]}。

# -*- coding:utf-8 -*-
class Solution:
    def maxInWindows(self, num, size):
        # write code here
        if size == 0 or len(num) == 0:
            return []
        res = []
        flag = 0
        while flag + size <= len(num):
            res.append(max(num[flag:flag+size]))
            flag += 1
        return res

表示数值的字符串

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+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
        if len(s) < 0:
            return False
        
        sign = False
        hasE = False
        decimal = False

        for i in range(len(s)):
            if s[i] == 'e' or s[i] == 'E':
                if i == len(s) - 1:   # e后面一定要有数字,否则False
                    return False
                if hasE:              # 不能同时存在两个 e
                    return False
                hasE = True
            elif s[i] == '+' or s[i] == '-':
                # 第二次出现+-符号,则必须紧跟在e之后
                if sign:
                    if s[i-1] != 'E' and s[i-1] != 'e':
                        return False
                # 第一次出现+-符号, 且不是在字符串开头,则也必须紧接e之后
                else:
                    sign = True
                    if i > 0 and s[i-1] != 'e' and s[i-1] != 'E':
                        return False
            # e后面不能接小数点,小数点不能出现两次
            elif s[i] == '.':
                if hasE or decimal:
                    return False
                decimal = True
            # 不合法字符
            else:
                if s[i] < '0' or s[i] > '9':
                    return False
        return True

扑克牌顺子

LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张_)…他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子…LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何, 如果牌能组成顺子就输出true,否则就输出false。为了方便起见,你可以认为大小王是0。

# -*- coding:utf-8 -*-
class Solution:
    def IsContinuous(self, numbers):
        # write code here
        if len(numbers) != 5:
            return False
        
        hashmap = {}
        max_num = 0
        min_num = 99
        for i in numbers:
            if i > max_num:
                max_num = i
            if i < min_num and i != 0:
                min_num = i
            hashmap[i] = hashmap.get(i, 0) + 1
        
        # 除0之外数组中的最大值与最小值的差小于或等于4
        if max_num - min_num > 4:
            return False
        
        # 除0外,不能出现重复元素
        for k, v in hashmap.items():
            if v >= 2 and k != 0:
                return False
        return True

把字符串转换为整数

将一个字符串转换成一个整数(实现Integer.valueOf(string)的功能,但是string不符合数字要求时返回0),要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0。

        # write code here
        numlist=['0','1','2','3','4','5','6','7','8','9','+','-']
        sum=0
        label=1#正负数标记
        if s=='':
            return 0
        for string in s:
            if string in numlist:#如果是合法字符
                if string=='+':
                    label=1
                    continue
                if string=='-':
                    label=-1
                    continue
                else:
                    sum=sum*10+numlist.index(string)
            if string not in numlist:#非合法字符
                sum=0
                break#跳出循环
        return sum*label

数组中重复的数字

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

# -*- coding:utf-8 -*-
class Solution:
    # 这里要特别注意~找到任意重复的一个值并赋值到duplication[0]
    # 函数返回True/False
    def duplicate(self, numbers, duplication):
        # write code here
        hashmap = {}
        for i in range(len(numbers)):
            hashmap[numbers[i]] = hashmap.get(numbers[i], 0) + 1
            if hashmap[numbers[i]] >= 2:
                duplication[0] = numbers[i]
                return True
        return False

构建乘积数组

给定一个数组A[0,1,…,n-1],请构建一个数组B[0,1,…,n-1],其中B中的元素B[i]=A[0]A[1]…*A[i-1]A[i+1]…*A[n-1]。不能使用除法。

解题思路:

B[i]的值可以看作下图的矩阵中每行的乘积。
下三角用连乘可以很容求得,上三角,从下向上也是连乘。
因此我们的思路就很清晰了,先算下三角中的连乘,即我们先算出B[i]中的一部分,然后倒过来按上三角中的分布规律,把另一部分也乘进去。
在这里插入图片描述

# -*- coding:utf-8 -*-
class Solution:
    def multiply(self, A):
        # write code here
        res = [None for _ in range(len(A))]
        if len(A) == 0:
            return []
        res[0] = 1
        
        for i in range(1,len(A)):
            # res[i] = res[i-1] * A[i]
            res[i] = res[i-1] * A[i-1]
        temp = 1
        for j in range(len(A)-2, -1, -1):
            temp *= A[j+1]
            res[j] *= temp
        return res

对称的二叉树

请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def isSymmetrical(self, pRoot):
        # write code here
        if not pRoot:
            return True
        
        return self.xbb(pRoot.left, pRoot.right)
    
    # 迭代法
    def xbb(self, root1, root2):
        if not root1 and not root2:   # 都为None时返回True
            return True
        if not root1 or not root2:    # 有一个不为None时返回False
            return False
        if root1.val == root2.val:
            return self.xbb(root1.left, root2.right) and self.xbb(root1.right, root2.left)
        
        return False

二叉树的下一个结点

给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。

解题思路:
1 如果该结点存在右子树,则返回右子树的最左子结点
2 如果该结点不存在右子树,分为两种情况:

  1. 该结点为父结点的左子结点,返回该结点的父结点
  2. 该结点为父结点的右子节点,沿着父结点向上遍历,直到找到一个结点的父节点的左子结点为该结点,则该结点的父结点为下一结点

在这里插入图片描述

class Solution:
    def GetNext(self, pNode):
        # write code here
        
        # 1 如果该结点存在右子树,则返回右子树的最左子结点
        # 2 如果该结点不存在右子树,分为两种情况:
        #    (1) 该结点为父结点的左子结点,返回该结点的父结点
        #    (2) 该结点为父结点的右子节点,沿着父结点向上遍历,
        #        直到找到一个结点的父节点的左子结点为该结点,则该结点的父结点为下一结点
        if pNode.right:    # 存在右子树
            pNode = pNode.right
            while pNode.left:
                pNode = pNode.left
            return pNode
        while pNode.next:   #  无右子树
            if pNode.next.left == pNode:
                return pNode.next
            pNode = pNode.next
        return

栈的压入、弹出序列

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列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
        # 入栈过程中,如果栈顶数字刚好是下一个弹出的数字则弹出,
        # 否则继续入栈直到全部数字入栈
        # 若stack.empty()为true则表示压栈序列与弹出序列是对应的
        
        if len(pushV) != len(popV):
            return False
        
        stack = []
        index_poped = 0
        
        for i in range(len(pushV)):
            stack.append(pushV[i])
            while stack and stack[-1] == popV[index_poped]:
                stack.pop()
                index_poped += 1
        if not stack:
            return True
        return False

数组中的逆序对

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007。

思路一: 暴力解法
思路二:
以一个例子来说明:

输入数组:data=[3,2,1,5,4,6,0,7]

排序好数组:dataSorted=[0,1,2,3,4,5,6,7]

顺序遍历dataSorted数组,第一个元素0是最小的元素,因此在data数组中,0前面有多少个数,就有多少个逆序对。在0检测完之后,将0从data数组中删除,data=[3,2,1,5,4,6,7],dataSorted数组遍历到1,而1其实就是[1,2,3,4,5,6,7]中的最小元素

因此,原问题就变为子问题:

输入数组:data=[3,2,1,5,4,6,7]

排序好数组:dataSorted=[1,2,3,4,5,6,7]

……

直到遍历到最后一个元素。
思路三: 归并排序

# -*- coding:utf-8 -*-
class Solution:
    def InversePairs(self, data):
        # write code here
        # 思路一:暴力解法
        # cnt = 0
        # while len(data) >= 2:
            # for i in range(1, len(data)):
                # if data[i] < data[0]:
                    # cnt += 1
            # data = data[1:]
        # return cnt % 1000000007
        
        # 思路二
        cnt = 0
        copy = []
        for i in data:
            copy.append(i)
        copy.sort()
        
        for i in range(len(copy)):
            cnt += data.index(copy[i])
            data.remove(copy[i])
        return cnt % 1000000007

        # 思路三:归并排序

输入一个字符串

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

# -*- coding:utf-8 -*-
class Solution:
    def Permutation(self, ss):
        if len(ss) == 0:
            return []
        if len(ss) == 1:
            return [ss]
        sl = []
        for i in range(len(ss)):
            for j in self.Permutation(ss[0:i] + ss[i + 1:]):
                sl.append(ss[i] + j)
                
        res = list(set(sl))
        res.sort()
        return res

链表中倒数第k个结点

输入一个链表,输出该链表中倒数第k个结点。

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def FindKthToTail(self, head, k):
        # write code here
        if head == None or k <= 0:
            return 
        
        pre = head
        last = head
        for i in range(k-1):
            if pre.next:
                pre = pre.next
            else:
                return None
        
        while pre.next != None:
            pre = pre.next
            last = last.next
        return last
        

65. 矩阵中的路径

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。 例如 a b c e s f c s a d e e 这样的3 X 4 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
在这里插入图片描述

回溯法

# -*- coding:utf-8 -*-
class Solution:
    def hasPath(self, matrix, rows, cols, path):
        # write code here
                # 参数校验
        if len(matrix) == 0 or len(matrix) != rows * cols or len(path) == 0:
            return False
        visited = [False] * len(matrix)
        pathlength = [0]
        for i in range(rows):
            for j in range(cols):
                # 以矩阵中每一个位置作为起点进行搜索
                if self.haspath(matrix, rows, cols, path, j, i, visited, pathlength):
                    return True
        return False

    # 判断矩阵位置(x,y)的字符能否加入已找到的路径中
    def haspath(self, matrix, rows, cols, path, x, y, visited, pathlength):
        '''
        :param matrix:字符矩阵
        :param rows:矩阵的行数
        :param cols:矩阵的列数
        :param path:需要寻找的路径
        :param x:当前位置的横坐标(对应列数)
        :param y:当前位置的纵坐标(对应行数)
        :param visited:访问标志数组
        :param pathlength:已经找到的路径长度
        :return:是否存在路径
        '''
        if pathlength[0] == len(path):
            return True
        curhaspath = False
        # 参数校验:1、位置坐标不超过行列数 2、当前位置字符等于路径中对应位置的字符 3、当前位置未存在于当前已找到的路径中
        if 0 <= x < cols and 0 <= y < rows \
                and matrix[y * cols + x] == path[pathlength[0]] \
                and not visited[y * cols + x]:

            visited[y * cols + x] = True
            pathlength[0] += 1
            # 分别向左,向右,向上,向下移动一个格子,任一方向能够继续往下走均可
            curhaspath = self.haspath(matrix, rows, cols, path, x - 1, y, visited, pathlength) \
                         or self.haspath(matrix,
                                         rows,
                                         cols,
                                         path,
                                         x, y - 1,
                                         visited,
                                         pathlength) \
                         or self.haspath(
                matrix, rows, cols, path, x + 1, y, visited, pathlength) \
                         or self.haspath(matrix, rows, cols, path,
                                         x, y + 1, visited,
                                         pathlength)
            # 如果不能再走下一步,需要回退到上一状态
            if not curhaspath:
                pathlength[0] -= 1
                visited[y * cols + x] = False
        return curhaspath
数据治理是确保数据准确性、可靠性、安全性、可用性和完整性的体系和框架。它定义了组织内部如何使用、存储、保护和共享数据的规则和流程。数据治理的重要性随着数字化转型的加速而日益凸显,它能够提高决策效率、增强业务竞争力、降低风险,并促进业务创新。有效的数据治理体系可以确保数据在采集、存储、处理、共享和保护等环节的合规性和有效性。 数据质量管理是数据治理中的关键环节,它涉及数据质量评估、数据清洗、标准化和监控。高质量的数据能够提升业务决策的准确性,优化业务流程,并挖掘潜在的商业价值。随着大数据和人工智能技术的发展,数据质量管理在确保数据准确性和可靠性方面的作用愈发重要。企业需要建立完善的数据质量管理和校验机制,并通过数据清洗和标准化提高数据质量。 数据安全与隐私保护是数据治理中的另一个重要领域。随着数据量的快速增长和互联网技术的迅速发展,数据安全与隐私保护面临前所未有的挑战。企业需要加强数据安全与隐私保护的法律法规和技术手段,采用数据加密、脱敏和备份恢复等技术手段,以及加强培训和教育,提高安全意识和技能水平。 数据流程管理与监控是确保数据质量、提高数据利用率、保护数据安全的重要环节。有效的数据流程管理可以确保数据流程的合规性和高效性,而实时监控则有助于及时发现并解决潜在问题。企业需要设计合理的数据流程架构,制定详细的数据管理流程规范,并运用数据审计和可视化技术手段进行监控。 数据资产管理是将数据视为组织的重要资产,通过有效的管理和利用,为组织带来经济价值。数据资产管理涵盖数据的整个生命周期,包括数据的创建、存储、处理、共享、使用和保护。它面临的挑战包括数据量的快速增长、数据类型的多样化和数据更新的迅速性。组织需要建立完善的数据管理体系,提高数据处理和分析能力,以应对这些挑战。同时,数据资产的分类与评估、共享与使用规范也是数据资产管理的重要组成部分,需要制定合理的标准和规范,确保数据共享的安全性和隐私保护,以及建立合理的利益分配和权益保障机制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值