【算法】剑指Offer-牛客网知识点(待更新)

版权声明:本文为博主原创文章,转载时附上原文链接即可 https://blog.csdn.net/weixin_38705903/article/details/83118875

剑指Offer-牛客网网址:https://www.nowcoder.com/ta/coding-interviews

1.在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
数组寻值,for循环和enumerate()配合
数组用[]

2.请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
replace(函数)

3.输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。
链表:

4.输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
二叉树:
前序:根左右
中序:左根右
后序:左右根
知道中序+另外一种序列,可以重建二叉树,用递归,必要要有中序

5.用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
堆栈:https://www.cnblogs.com/yupeng/p/3413852.html
学会初始化类:

    def __init__(self):
        self.stack1 = []
        self.stack2 = []

这个更厉害,相当于每次pop之前的数据放在stack2,这一次pop之后与下一次pop之前的数据放在stack1,只有当stack2的数据输出完了,才一次性往stack2里放入新的数据

class Solution:
    def __init__(self):
        self.stack1 = []
        self.stack2 = []
    def push(self, node):
        self.stack1.append(node)
    def pop(self):
        if len(self.stack2) != 0:
            return self.stack2.pop()
        while len(self.stack1) !=0:
            self.stack2.append(self.stack1.pop())
 
        return self.stack2.pop()

6.把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
逻辑运算符号:http://www.runoob.com/python/python-operators.html#ysf4
与:and
或:or
非:not
等于:==
不等:!=
大于等于:>=
小于等于:<=
列表用[]
递归+二分法:

# -*- coding:utf-8 -*-
class Solution:
    def minNumberInRotateArray(self, rotateArray):
        # 可以使用min()函数, 但是在不适用min函数的情况下,思路应该是二分查找
        # 下面使用 递归实现2分查找,其中递归的时候必须使用return!!! 不然会返回none
        # write code here 
        start = 0
        end = len(rotateArray) - 1
        mid = int((start + end) / 2)
        if len(rotateArray) == 2:
            if rotateArray[0]>rotateArray[1]:
                return rotateArray[1]
            else:
                return rotateArray[0]
        elif rotateArray[mid] > rotateArray[end]:
            return self.minNumberInRotateArray(rotateArray[mid:end + 1])
        elif rotateArray[mid] < rotateArray[start]:
            return self.minNumberInRotateArray(rotateArray[start:mid + 1])

int(1.6)=1,会向下去取整
a=[1,2,3,4],若想显示2到4为,则a[1:4],python的[]末位是取不到的,所以要加1

7.大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。n<=39

python list 增加元素的三种方法h:https://blog.csdn.net/shuwenting/article/details/79716568

如果n不为0,1或2,写法应该

if n != 0 and n != 1 and n != 2:

如果写成

if n != 0 and 1 and 2:

逻辑符号and后面的1和2其实和n没有关系,所以是错的

python的for和其他语言不一样,是for i in range(n),in后面是一组数,而不是终点数
同时for里面的变量要替换成i

这个更简洁,利用了题目里第N个数在第N位的条件,[-1]表示倒数第一位

# -*- coding:utf-8 -*-
class Solution:
    def Fibonacci(self, n):
        # write code here
        res=[0,1,1,2]
        while len(res)<=n:
            res.append(res[-1]+res[-2])
        return res[n]

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

可用递归,但时间复杂度太大

class Solution:
    def jumpFloor(self, number):
        # write code here  
        if number == 1:
            return 1
        elif number == 2:
            return 2
        elif number > 2:
            jump = self.jumpFloor(number-1)+self.jumpFloor(number-2)
        return jump

用动态规划法

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

注意If和for后面都是有冒号的

9.一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
找规律即可,第N次等于前N-1次的总和加1
list求和方法:sum(list)
更厉害的思路是,找规律发现第N次的跳法是2^(N-1),这思路太厉害了。。

# -*- coding:utf-8 -*-
import math 
class Solution:
    def jumpFloorII(self, number):
        # write code here  
        return math.pow(2,number-1)

次方是math.pow()函数,需要import math
或者大神的更简洁(秀的我头皮发麻)

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

10.我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
解题关键:
当增加一块新的小矩形时,只有两种摆法,要么竖着放,要么横着放,当竖着放的时候,可递归N-1块时的情形,当横着放的时候,因为下面的一块也被确定了,所以可递归N-2块时的情形
所以是分类思想配合递归
走过的弯路:
不要一个个手写列出所有情况再找规律,这样容易考虑失误漏掉
在这里插入图片描述
或者用动态规划也可以,过程与跳台阶相同

11.输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
负数的补码等于反码+1。(负数的反码就是他的原码除符号位外,按位取反)
else也要加冒号
题目中默认最多32位

右移:a>>2,将a的二进制数右移2位,相当于除以2两次

但是这个方法没有充分利用python的函数,有个更简单的
bin()限制二进制数的格式,返回的是str格式
初始化字符串的例子:a = ‘Hello!’
**为次方
replace(‘0b’,’’):将0b替换成空(可以不加)
count(‘1’)计算1的个数

链接:https://www.nowcoder.com/questionTerminal/8ee967e43c2c4ec193b040ea7fbb10b8
来源:牛客网

# -*- coding:utf-8 -*-
class Solution:
    def NumberOf1(self, n):
        # write code here
        return bin(n).replace("0b","").count("1") if n>=0 else bin(2**32+n).replace("0b","").count("1")

12.给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
a的b次方:a**b

13.输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
列表的操作:https://www.cnblogs.com/dingkailinux/p/8099188.html
列表连接直接相加即可,例如:c=a+b

大神的精简代码:
在这里插入图片描述
经典结构:
“执行if的内容” if “if的条件” else “执行else的内容”

sorte()排序函数,默认由小到达,reverse=True,则由大到小
sort()与sorted()的不同在于,sort是在原位重新排列列表,而sorted()是产生一个新的列表。
sort()用法也与sorted()不太一样:Python List sort()方法
key=lambda用法:https://www.cnblogs.com/zle1992/p/6271105.html
https://blog.csdn.net/u014798502/article/details/78161728
相当于将数组变成了一个元组,多加了一个key值——c%2,也就是余数值(c是随便取的,也可以为其他字母),lambda定义了一个关于c的表达式,这个表达式返回的值是余数(c%2),然后按照余数值倒序排序
例如:

>>> a = [4,3,5,6,1]
>>> sorted(a,key=lambda b:b%2,reverse=True)
[3, 5, 1, 4, 6]

14.输入一个链表,输出该链表中倒数第k个结点。
链表的知识点
读取链表所有数据:

while head:
	res.append(head)
	head = head.next

要注意考虑代码的鲁棒性,注意边界情况

if k>len(res) or k<0:
	return None

15.输入一个链表,反转链表后,输出新链表的表头。
链表
pHead指的是表头的地址
反转链表即对链表的改变链表的指针域
考虑特殊情况,链表为空或者只有一个元素

16.输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
创建新的链表,先定义接连,然后将节点连接起来

for i in res:
	node = ListNode(i)
	pre.next = node
	pre = pre.next

思路:先将两个链表里面的值都取出来放在list,然后list排序,然后再根据排序创建一个个节点并形成链表
dummy = pre将其实地址保存,要注意的是,dummy和pre都是指针,所有pre在更新pre.next的时候,实际的链表指针域在变,所有dummy.next也在变(因为pre和dummy指向的是同一个地方!)
大神的代码:
在这里插入图片描述

17.输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
树是由n(n 0)个结点组成的有限集合,其中当n=0时,它是一颗空树,这是树的特例。
空树的高度或深度:0
空树的结点数:0
判断特定的值是否存在列表中,可使用关键字in
判断两棵树是否包含,生成树的前序遍历和中序遍历并比较
定义函数要加冒号,例如: def convert§:,同时要注意缩进
将数字转换为字符:str()

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def HasSubtree(self, pRoot1, pRoot2):
        # write code here
        #前序遍历
        def pre(tree):
            if tree:
                return str(tree.val)+pre(tree.left)+pre(tree.right)
            else:
                return ""
        #中序遍历
        def mid(tree):
            if tree:
                return mid(tree.left)+str(tree.val)+pre(tree.right)
            else:
                return ""
        if pRoot2:
            return pre(pRoot2) in pre(pRoot1) and mid(pRoot2) in mid(pRoot1)
        else:
            return False

if a: 或者 while a:,只要a不为0,都可以执行后续的指令

18.操作给定的二叉树,将其变换为源二叉树的镜像。
采用递归思路:只要root不是None,left和right就一直交换
在调用内部函数的时候要加上“ self. ”

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    # 返回镜像树的根节点
    def Mirror(self, root):
        # write code here
        if root:
            tmp = root.left
            root.left = self.Mirror(root.right)
            root.right = self.Mirror(tmp)
        return root

19.输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下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.
思路:每次都打印第一行,然后把打印过的裁剪掉,再将剩下的矩阵逆时针旋转90度,重复上述步骤,直至矩阵元素为0——然而题目里面输入的是一个二维列表我擦
矩阵旋转:https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.rot90.html
大神思路:
在这里插入图片描述
while里面有四个步骤,也就是四个红色箭头,按顺序输入,这样为一个大循环,如果matrix仍不为0,则重复着四个步骤
在这里插入图片描述

  1. pop(0)是输出列表第一个元素
  2. pop()输出列表内最后一个元素,并且在原列表中不保留,通过配合for循环,可以巧妙地将最后一列提取出来
for row in a:
    print(row)
    res.append(row.pop())
  1. matrix.pop()[::-1]将列表最后一行的内容反向输出
  2. 和2同理,配合for,可以对列进行操作,row.pop(0)输出每一列第一个元素

因为在存储行的时候,是整个多个数字同时放入的,因此用+,若用append的话会将一维列表变成二维,在存储列的时候,是将列的每个数字单独一个个放入的,若用+的话,会认为字符串和int型相加,报错,因此要用append

因为是列表,所以会存在matrix = [[],[],[]]的情况,若 matrix.pop()为[]的话,res+存入[]后不会显示,对res的内容不影响,若用append的话,res中会显示有[],所以当res+时,只需要对matrix判断,当append()时,要对matrix[0]进一步判断
因为每一行的列元素是同时变得,因此只需要对matrix中其中元素,例如matrix[0]判断即可,若一个为空,则全为空,换个说法,对matrix[-1]判断也是可以,因为matrix[0]和matrix[-1]的列变化是相同的

20.定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
list反向排序:list.sort(reverse=True)
python寻找list中最大值、最小值并返回其所在位置
print c.index(min©) # 返回最小值
print c.index(max©) # 返回最大值
若在类里要初始化定义变量,要用下面的方法

def __init__(self):
        self.res=[]

在类里要用到定义的res时,都要加上self.,否则无法调用

21.输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
大神代码:
在这里插入图片描述
思路:模拟进栈出栈的顺序,每push一个,pushV的元素减一,没pop一个,popV的元素也减一
进栈出栈的两种情况:
1.进栈之后立刻出栈
2.进栈之后不出,继续放入下一个数

情况1时:pushV的第一个元素和popV的第一个元素必然相同(能想到这个关键的真是牛逼)
因为若不同的话,pushV的第一个元素就会进栈,所以前面不会有其他元素,而且popV的第一个元素在被排除后也会被删除

情况2时:栈内的最后一个元素等于popV的第一个元素,因为把pushV的元素放入栈后,顺序就会和popV的顺序相同(情况1的时候已经将特殊情况的时候给排除了),所以当栈内的最后一个元素等于popV的第一个元素时,就可以认为他能够顺利pop出

若不满足上述两种情况时,不断往栈内放入数据,直至pushV内元素为空,若此时还不能将popV内元素逐一排除,则认为不可能是该压栈序列的弹出序列

if,elif,else后面都要加冒号!
要注意检查是否为空,否则会报错。当列表为空的时候,pushV[0]和stack[-1]依然会被检索,此时会报错:IndexError: list index out of range
IDLE仿真看过程代码:

pushV = [1,2,3,4,5]
popV = [4,5,3,2,1]
stack = []
while popV:
    if pushV and pushV[0] == popV[0]:
        pushV.pop(0)
        popV.pop(0)
        print('1')
        print(pushV)
        print(popV)
        print(stack)
        print('\n')
    elif stack and stack[-1] == popV[0]:
        stack.pop()
        popV.pop(0)
        print('2')
        print(pushV)
        print(popV)
        print(stack)
        print('\n')
    elif pushV:
        stack.append(pushV.pop(0))
        print('3')
        print(pushV)
        print(popV)
        print(stack)
        print('\n')
    else:
        print('False')
        break

22.从上往下打印出二叉树的每个节点,同层节点从左至右打印。
想了很久还是不会写啊555,看看大神代码
在这里插入图片描述
每次下一层的节点放入一个新的列表里,res里每次只记录当前节点的值,而不是左右子节点(我太贪心了。。),用while和for去遍历,进行相同的操作:记录子节点和当前节点的值
知道currentStack内无节点了,即上一次的nextStack为空,然后跳出while,输出res结果,牛逼啊!

同时要考虑特殊情况,例如输入为空
在这里插入图片描述
看来我对广度优先搜索和队列的理解还不够深,牛逼啊
广度优先搜索:一层一层地寻找所有的点:
图片来源:https://blog.csdn.net/refusing/article/details/80600210
在这里插入图片描述

23.输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
二叉搜索树:左叶<根<右叶
https://www.cnblogs.com/coffeeSS/p/5452719.html
切片:
https://www.cnblogs.com/zhangjiansheng/p/6853640.html

后序——左右根
最后一个必为根——比根小的比在左叶
倒数第二个必为右子叶根
第一个必为最左叶——必为最小值

思路:
1、比根小的为左叶,比根大的在右叶
2、分而治之,针对左叶右叶进行同样的操作划分,直至数组为1个数,return,划分的操作相同,因此可以参考上一题,采用同一个函数划分,然后利用列队,更新每次划分的数组
3、每次划分数后,立即判断,不符合搜索二叉树的条件:左叶中出现比根大的值或右叶中出现比根小的值
4、考虑边界行,例如为空时,则搜索二叉树不成立,返回False
5、停止条件,若出现错误,或者一直划分到最底层

# -*- coding:utf-8 -*-
class Solution:
    def SpliltArray(self,Array,Judge,LastRoot):
        #Judeg = 1表示True,仍为搜索二叉树
        LeftArray = []
        RightArray = []
        for i in range(len(Array)):
            if len(Array)!=2 and Array[i] > Array[-1]:
                LeftArray = Array[:i]
                RightArray = Array[i:-1]
                LastRoot = Array[-1]
                break
        return LeftArray,RightArray,Judge,LastRoot
    
    def VerifySquenceOfBST(self, sequence):
        # write code here
        cur = [sequence]
        if sequence==[]:
            return False
        LastRoot = 0
        while cur:
            Judge = 1
            LeftArray,RightArray,Judge,LastRoot = self.SpliltArray(cur.pop(),Judge,LastRoot)
            for i in LeftArray:
                if i > LastRoot:
                    Judge = 0
            for i in RightArray:
                if i < LastRoot:
                    Judge = 0
            if Judge:
                if len(LeftArray)>1:
                    cur.append(LeftArray)
                if len(RightArray)>1:
                    cur.append(RightArray)
                if cur==[]:
                    return True
            else:
                return False

大神代码:
配合了递归的思想,不断递归直到为空,若所有都为空(即不出现返回False),则整个序列都是True,一旦有一个环节为False,则整个都为False

python_bisect模块的使用
x_insert_point = bisect.bisect_left(L,x)  #在L中查找x,x存在时返回x左侧的位置,x不存在返回应该插入的位置

在这里插入图片描述

24、输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)
一开始提都没看懂:https://blog.csdn.net/u014525494/article/details/80978647
叶子节点:没有子节点的节点(一定要到底)
不知道怎么根据数组找到树的结构,好像也不是前序遍历
大神代码:
在这里插入图片描述

class Solution:
    # 返回二维列表,内部每个列表表示找到的路径
    def FindPath(self, root, expectNumber):
        res=[]
        treepath=self.dfs(root)
        for i in treepath:
            if sum(map(int,i.split('->')))==expectNumber:
                res.append(list(map(int,i.split('->'))))
        return res
 
    def dfs(self, root):
        if not root: return []
        if not root.left and not root.right:
            return [str(root.val)]
        treePath = [str(root.val) + "->" + path for path in self.dfs(root.left)]
        treePath += [str(root.val) + "->" + path for path in self.dfs(root.right)]
        return treePath

思路:
一、先用递归,得到树的所有路径
1)若root的值为空,则返回空
2)若root的两个叶子都为空,则返回root.val
3)否则(即有一个叶子不为空),将树的路径放入treePath中,且为深度优先搜索,左叶优先于右叶

treePath = [str(root.val) + “->” + path for path in self.dfs(root.left)]
实际上是将left里面的值提取出来,然后分别和val连接

map(): map() 会根据提供的函数对指定序列做映射:
http://www.cnblogs.com/Lambda721/p/6128351.html

int:转化为int形式
list:放进list里

二、
1)

在这里插入图片描述

class Solution:
    def FindPath(self, root, expectNumber):
        return [map(int, i.split("->")) for i in self.dfs(root) if sum(map(int, i.split('->'))) == expectNumber]
    def dfs(self, root):
        if not root: return []
        if not root.left and not root.right: return [str(root.val)]
        treePath = [str(root.val) + "->" + path for path in self.dfs(root.left)]
        treePath += [str(root.val) + "->" + path for path in self.dfs(root.right)]
        return treePath

25、输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
链表复制:https://www.cnblogs.com/catch/p/3353541.html
大神思路:
在这里插入图片描述

首先新建节点,然后复制random指针,然后复制next指针,最后返回当天指向的位置
由于在next的时候用了递归,因此会先寻找下一个节点,直到最后一个节点
next的建立顺序为倒数第二个指向倒数第一个,倒数第三个指向倒数第四个,……,最后第一个指向第二个,然后return第一个节点(head)

大神的另一个方法:
在这里插入图片描述

class Solution:
    def Clone(self, head):
        nodeList = []     #存放各个节点
        randomList = []   #存放各个节点指向的random节点。没有则为None
        labelList = []    #存放各个节点的值
 
        while head:
            randomList.append(head.random)
            nodeList.append(head)
            labelList.append(head.label)
            head = head.next
        #random节点的索引,如果没有则为1   
        labelIndexList = map(lambda c: nodeList.index(c) if c else -1, randomList)
 
        dummy = RandomListNode(0)
        pre = dummy
        #节点列表,只要把这些节点的random设置好,顺序串起来就ok了。
        nodeList=map(lambda c:RandomListNode(c),labelList)
        #把每个节点的random绑定好,根据对应的index来绑定
        for i in range(len(nodeList)):
            if labelIndexList[i]!=-1:
                nodeList[i].random=nodeList[labelIndexList[i]]
        for i in nodeList:
            pre.next=i
            pre=pre.next
        return dummy.next

重温map()用法:将序列依次传入函数中处理,最后返回list
https://www.cnblogs.com/liuqi-beijing/p/6228984.html

A if B else C:若满足B则执行A,否则执行C
nodeList.index©:返回nodeList中数值为c的index

哈希表:https://blog.csdn.net/duan19920101/article/details/51579136/

首先得到每个节点、节点值、以及随机指针
然后找到每个随机指针对应的节点index
然后先建立每个节点的随机指针关系
然后再将节点从头到尾按顺序连接起来

26、输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
大神思路:
在这里插入图片描述

midTraversal()是利用递归进行中序遍历,厉害啊原来是可以这样写,中序遍历是“左根右”,只要左边还有值,就会一直递归下去,直到没有左叶,然后return空,然后append最左边的值,然后才append根的值,最后递归右边

enumerate() 函数:http://www.runoob.com/python/python-func-enumerate.html
i为index,v为value
前面要加in!!!!

self.arr[:-1]表示除了倒数第一个以外都要

因为是搜索二叉树,所以中序遍历之后是由小到大排序的

双向链表:https://blog.csdn.net/kangxidagege/article/details/80211225
https://www.cnblogs.com/skywang12345/p/3561803.html
一个节点中同时保存了上一个节点和下一个节点的位置,因为题目中不允许新增节点,因此将比当前节点大的节点放在右边,比当前节点小的节点放在左边(arr中为由小到大,因此”每个节点的right设为下一个节点,下一个节点的left设为上一个节点")
举个例子:
原始的树结构为左图
递归之后arr=[1,3,4,5,6]
改成双向链表后为右图,每个节点都可访问到自己的前驱结点和后继结点(结构其实并没有改变,只是指针变了而已)
在这里插入图片描述

访问方式:
单链表:如果访问任意结点每次只能从头开始顺序向后访问
单循环链表:可以从任何一个结点开始,顺序向后访问到达任意结点
双向链表:可以从任何结点开始任意向前向后双向访问
操作:
单链表和单循环链表:只能在当前结点后插入和删除
双链表:可以在当前结点前面或者后面插入,可以删除前趋和后继(包括结点自己)
存储:
单链表和单循环链表存储密度大于双链表

若改变的是self的值的话,不需要return也可以

if node == None: return

相当于

if not node:
	return None

记得要考虑特殊情况,例如输入为空

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

输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。

将字符串分割成单个字符: https://blog.csdn.net/qq_29750277/article/details/82023347
Python字符串操作之字符串分割与组合:https://www.cnblogs.com/justdo-it/articles/8297303.html
连接:’’.join()

python全排列函数:https://www.cnblogs.com/gccbuaa/p/7343905.html

考虑特殊情况:

  1. 输入为空
  2. 有重复的字符
    Python 去除列表中重复的元素:https://www.cnblogs.com/chjbbs/p/5729540.html

输出的结果要按字母顺序排列:res.sort()
python3 列表排序(字母顺序排序、字母相反顺序排序和倒序):http://blog.sina.com.cn/s/blog_6436b8ec0102x25q.html

# -*- coding:utf-8 -*-
import itertools
class Solution:
    def Permutation(self, ss):
        # write code here
        if not ss: return []
        res = []
        for i in itertools.permutations(ss,len(ss)):
            res.append(''.join(i))
        #删除重复字符
        res = list(set(res))
        #结果按字母顺序排列
        res.sort()
        return res

和大神的思路一样,只不过他在append之前就做了重复字符的判断,他没有添加字母排序,我感觉是要加的
在这里插入图片描述

28、数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
python 中如何判断list中是否包含某个元素:http://outofmemory.cn/code-snippet/9098/python-list-contains-with-in-not-in
python 字典添加元素:http://www.cnblogs.com/sea-stream/p/9981648.html
取整:round (也可以不用)

# -*- coding:utf-8 -*-
class Solution:
    def MoreThanHalfNum_Solution(self, numbers):
        # write code here
        length = len(numbers)
        store = {}
        for i in numbers:
            if i not in store:
                store.update({i:1})
            else:
                store[i] = store[i]+1
            if store[i] > length/2:
                return i
        return 0

另一个使用字典的方法(思路相同)
在这里插入图片描述
Python 字典(Dictionary) has_key()方法:http://www.runoob.com/python/att-dictionary-has_key.html——这个在python3中已经删除,只能使用if i not in store

直接dict[2] = 1,就可以在字典中插入{2:1}

29、输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
注意特殊形况:
K大于n
n=0

tinput[0:k]可写成tinput[:k]

手写排序方法:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

30、HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)
大神思路和改良:
在这里插入图片描述
动态规划问题:https://www.cnblogs.com/wuyuegb2312/p/3281264.html
思路:
① 若一个数A的左边之和,小于零,则说明加上A之后必会比A小,因此直接从A开始最佳,【只有这种情况才会进行舍弃】,此时cur要归零
② 若左边之和大于零,则与A相加
③ 对比相加后和相加前的和,因为只需要输出最大的不用给出具体序列,所以只要每次都记录最大的和即可

同时赋多个值:res,cur = array[0],array[0]
自加:cur+=i
输出两个数的最大值:max(a,b)
判断特殊情况:输入为空!!!!

# -*- coding:utf-8 -*-
class Solution:
    def FindGreatestSumOfSubArray(self, array):
        # write code here
        if not array:
            return 0
        
        res,cur = array[0],array[0]
        for i in array[1:]:
            if cur < 0:
                cur = 0
            cur += i
            res = max(res,cur)
        return res

31、 求出1~ 13的整数中1出现的次数,并算出100~ 1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。
Python中获取两数相除的商和余数:https://blog.csdn.net/dfq12345/article/details/78198797/
递增序列range生成数列用法:https://blog.csdn.net/liangzuojiayi/article/details/51075463

动态规划思路:
① 基本类型,例如1~99的数,只要求其商和余数,只要为1,则说明有1,例如11余数是1,商是1,说明有2个1
② 组合类型,例如121,余数是1,商是12,而12中1个的个数在前面已经用同样的方法求过了,12中1的个数是1,因此121中1的个数是2
③ 将每个数的结果都按顺序存在列表中,方便后面的数读取,这样就不需要不断递归
④ 最后将每个数的求和并输出

# -*- coding:utf-8 -*-
class Solution:
    def NumberOf1Between1AndN_Solution(self, n):
        # write code here
        array_one = [0]
        for i in range(1,n+1):
            sum_one = 0
            quotient = i // 10
            reminder = i % 10
            if reminder == 1:
                sum_one += 1
            if quotient == 1:
                sum_one += 1
            elif quotient >= 10:
                sum_one += array_one[quotient]
            array_one.append(sum_one)
        return sum(array_one)

卧槽神仙答案哈哈哈
在这里插入图片描述

大神速度更快的答案:
在这里插入图片描述

def countDigitOne(self, n):
        """
        :type n: int
        :rtype: int
 
        例:对于824883294,先求0-800000000之间(不包括800000000)的,再求0-24883294之间的。
        如果等于1,如1244444,先求0-1000000之间,再求1000000-1244444,那么只需要加上244444+1,再求0-244444之间的1
        如果大于1,例:0-800000000之间1的个数为8个100000000的1的个数加上100000000,因为从1000000000-200000000共有1000000000个数且最高位都为1。
        对于最后一位数,如果大于1,直接加上1即可。
        """
        result = 0
        if n < 0:
            return 0
        length = len(str(n))
        listN = list(str(n))
        for i, v in enumerate(listN):
            a = length - i - 1  # a为10的幂
            if i==length-1 and int(v)>=1:
                result+=1
                break
 
            if int(v) > 1:
                result += int(10 ** a * a / 10) * int(v) + 10**a
            if int(v) == 1:
                result += (int(10 ** a * a / 10) + int("".join(listN[i+1:])) + 1)
        return result

for i, v in enumerate(listN):i是index,v是value

具体思路在这里,每个位数单独考虑
在这里插入图片描述
参考博文:http://www.cnblogs.com/nailperry/p/4752987.html,主要就是从数字出发找规律。
一、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)X101-1=259)。

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

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

32、输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
python技巧——将list中的每个int元素转换成str:https://blog.csdn.net/google19890102/article/details/80932546
考虑输入为空的情况!!!
map(): map() 会根据提供的函数对指定序列做映射:
http://www.cnblogs.com/Lambda721/p/6128351.html
numbers = list(map(str, numbers)):将numbers内容变为str
Python的sort函数和sorted、lambda和cmp:
https://blog.csdn.net/u014798502/article/details/78161728
关键思路:
https://blog.csdn.net/fanzitao/article/details/7895344
自己定义排序,令“s1+s2”小于“s2+s1”,则s1放在s2前面
【这题cmp没搞懂,具体实现先留着】

33、把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
思路:
用2,3,5这三个数不断地相互相乘,必为丑数,则只要按顺序将这些丑数逐一算出即可

具体:
初始丑数队列里只有1,ugly_number = [1]
2*1 = 2
3*1 = 3
5*1 = 5
发现上面三个数里面,2最小,则将2放入丑数队列中,ugly_number = [1,2]

因为选取了2对应的丑数,所以2的index后移一位,3和5不变:
2*2 = 4
3*1 = 3
5*1 = 5
发现上面三个数里面,3最小,则将3放入丑数队列中,ugly_number = [1,2,3]

因为选取了3对应的丑数,所以3的index后移一位,2和3不变:
2*2 = 4
3*2 = 6
5*1 = 5
发现上面三个数里面,4最小,则将4放入丑数队列中,ugly_number = [1,2,3,4]

因为选取了2对应的丑数,所以2的index后移一位,3和5不变:
2*3 = 6
3*2 = 6
5*1 = 5
因为2对应的丑数和3对应的丑数重复了,所以3的index后移一位(因子大的后移),所以变成:
2*3 = 6
3*3 = 9
5*1 = 5

以此类推,直到丑数个数达到要求

注意:
留意边界条件:N=0

# -*- coding:utf-8 -*-
class Solution:
    def GetUglyNumber_Solution(self, index):
        # write code here
        if index == 0:
            return 0
        ugly_num = [1]
        
        #设置初始位置
        index_two = 0
        index_three = 0
        index_five = 0
            
        while len(ugly_num) < index:
            temp_two = 2*ugly_num[index_two]
            temp_three = 3*ugly_num[index_three]
            temp_five = 5*ugly_num[index_five]
            
            #若有重复的数,则后移一位
            while temp_two == temp_three:
                index_three += 1
                temp_three = 3*ugly_num[index_three]
            while temp_five == temp_two or temp_five == temp_three:
                index_five += 1
                temp_five = 5*ugly_num[index_five]
            
            #将最小的数新增至丑数内
            if temp_two < temp_three and temp_two < temp_five:
                ugly_num.append(temp_two)
                index_two = index_two + 1
            elif  temp_three < temp_two  and temp_three < temp_five:
                ugly_num.append(temp_three)
                index_three = index_three + 1
            elif temp_five < temp_two and temp_five < temp_three:
                ugly_num.append(temp_five)
                index_five = index_five + 1
            
        return ugly_num[index-1]

34、在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).

猜你喜欢

转载自blog.csdn.net/weixin_38705903/article/details/83118875
今日推荐