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

def PrintFromTopToBottom(self, root):
        if not root:
            return []
        res = []
        queue = [root]
        while queue:
            if queue[0].left:
                queue.append(queue[0].left)
            if queue[0].right:
                queue.append(queue[0].right)
            res.append(queue.pop(0).val)
        return res
        

思路:用List来实现队列。一个List来储存结点。一个list来储存要打印的值。如果有子节点,则取出来放在队列的末尾。

易错点:储存的是val,要有return值;空树时候要有判断;pop(0)是弹出第一项,相当于队列的功能。

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

  def VerifySquenceOfBST(self, sequence):
        if not sequence:
            return False
        lenth = len(sequence)
        root = sequence[-1]
        for i in range(lenth):
            if sequence[i] > root:
                break
        for j in range(i,lenth):
            if sequence[j] < root:
                return False
        left = True
        if i>0:
            left = self.VerifySquenceOfBST(sequence[:i])
        right = True
        if j < lenth-1:
            right = self.VerifySquenceOfBST(sequence[i:])
        return left and right
            

思路:后续遍历的理解:有公共父节点则跳。链接:https://www.nowcoder.com/questionTerminal/a861533d45854474ac791d90e447bafd
来源:牛客网
ST的后序序列的合法序列是,对于一个序列S,最后一个元素是x (也就是根),如果去掉最后一个元素的序列为T,那么T满足:T可以分成两段,前一段(左子树)小于x,后一段(右子树)大于x,且这两段(子树)都是合法的后序序列。完美的递归定义 : ) 。

二叉搜索树:二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树

易错点:先判断i,j再递归。

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

    def FindPath(self, root, target):
        res = []
        paths = self.dfs(root)
        return [p for p in paths if sum(p) == target]

    def dfs(self, root):
        if root is None: return []
        if root.left is None and root.right is None:
            return [[root.val]]

        paths = [[root.val] + path for path in self.dfs(root.left)] \
            + [[root.val] + path for path in self.dfs(root.right)]

        return paths

更简洁:链接:https://www.nowcoder.com/questionTerminal/b736e784e3e34731af99065031301bca
来源:牛客网

class Solution:
    def FindPath(self, root, expectNumber):
        def subFindPath(root):
            if root:
                b.append(root.val)
                if not root.right and not root.left and sum(b) == expectNumber:
                    a.append(b[:])  #深拷贝,只有这样a中添加的才是正确的结果,否则递归返回时改变了b内的内容会让a中已经保存的内容也变掉
                else:
                    subFindPath(root.left),subFindPath(root.right)
                b.pop()
        a, b = [], []
        subFindPath(root)
        return a

思路:1求出每一个叶结点的路径,然后与输入数字对比 2 

  •      递归先序遍历树, 把结点加入路径。
  •      若该结点是叶子结点则比较当前路径和是否等于期待和。
  •      弹出结点,每一轮递归返回到父结点时,当前路径也应该回退一个结点

DFS核心代码:https://blog.csdn.net/liangzhaoyang1/article/details/51415719

/** 
 * DFS核心伪代码 
 * 前置条件是visit数组全部设置成false 
 * @param n 当前开始搜索的节点 
 * @param d 当前到达的深度 
 * @return 是否有解 
 */  
bool DFS(Node n, int d){  
    if (isEnd(n, d)){//一旦搜索深度到达一个结束状态,就返回true  
        return true;  
    }  
  
    for (Node nextNode in n){//遍历n相邻的节点nextNode  
        if (!visit[nextNode]){//  
            visit[nextNode] = true;//在下一步搜索中,nextNode不能再次出现  
            if (DFS(nextNode, d+1)){//如果搜索出有解  
                //做些其他事情,例如记录结果深度等  
                return true;  
            }  
  
            //重新设置成false,因为它有可能出现在下一次搜索的别的路径中  
            visit[nextNode] = false;  
        }  
    }  
    return false;//本次搜索无解  
}  

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

def Clone(self, head):
        if not head:return None
       #递归思想:把大问题转化若干子问题
       #此题转化为一个头结点和除去头结点剩余部分,剩余部分操作和原问题一致

        new = RandomListNode(head.label)
        #开辟一个新节点
        new.random = head.random
        new.next = head.next
        #递归其他节点
        new.next = self.Clone(head.next)
        return new

思路一:递归解法

 def Clone(self, pHead):
        p1 = pHead #不改变pHead
        while p1:  #复制链表,留random为空
            new = RandomListNode(p1.label) #注意写法
            new.next = p1.next
            new.random = None
            
            p1.next = new
            p1 = new.next
        
        p1 = pHead  #添加random
        while p1:
            new = p1.next
            if p1.random:
                new.random = p1.random.next
            p1 = new.next
            
        p1 = pHead  #拆分
        newhead = None
        newnode = None
        #先把p1往后移动
        if p1:
            newhead = newnode = p1.next
            p1.next = newnode.next
            p1 = p1.next
            
        while p1:
            newnode.next = p1.next
            newnode = newnode.next
            p1.next = newnode.next
            p1 = p1.next
        
        return newhead

思路二:书上的解法,先复制链表,再加上random。最后删去原链表

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'''

思路三:哈希表法,将各节点的值储存起来

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

def Convert(self, pRootOfTree):
        if not pRootOfTree:
            return None
        self.res  =[]
        self.mid(pRootOfTree)
        for i,v in enumerate(self.res[:-1]):#必须是-1,开区间!所以是到最后第二位
            v.right = self.res[i+1]
            self.res[i+1].left = v
        return self.res[0]
        
    def mid(self,root): #中序遍历
        if not root:
            return None
        self.mid(root.left)
        self.res.append(root) #添加的是root不是val
        self.mid(root.right)
       

思路:中序遍历

易错点:res[:-1]的意思是到倒数第二位;注意enumerate的用法

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

    def Permutation(self,ss):
        if not ss:
            return []
        res = []
        self.helper(ss,res,'') #path是字符串
        return sorted(list(set(res)))#集合去重,list变成列表,sort排成字典序
    
    def helper(self,ss,res,path):
        if not ss:
            res.append(path)
        else:
            for i in range(len(ss)):
                self.helper(ss[:i]+ss[i+1:],res,path + ss[i]) #字符串用加号
        

思路:分而治之,递归解决,https://blog.csdn.net/sty945/article/details/79839567

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

        if not numbers: #集合去重
            return 0
        setnum = set(numbers)
        for i in setnum:
            if numbers.count(i) > len(numbers)/2:
                return i
        return 0

思路一:暴力法,利用集合去重

        if not numbers:
            return 0
        res = 0
        times = 0
        for i,num in enumerate(numbers):
            if times == 0:
                res = num
                times = 1
            elif num == res:
                times +=1
            else:
                times -=1
        return res if numbers.count(res) > len(numbers) /2 else 0
                

思路二: https://blog.csdn.net/fuxuemingzhu/article/details/79637409 利用了数组的性质

易错点:return是在for循环的外边。

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

        if not tinput or k > len(tinput):
            return []  #注意对K的判断
        tinput.sort()
        return tinput[:k

思路1:简单粗暴排序

        if not tinput or k > len(tinput) or not k:
            return []
        res = tinput[:k]
        for i in tinput[k:]:
            if i < max(res):
                res.remove(max(res))
                res.append(i)
        return sorted(res)

思路二:与k个数数组的最大i比较替换

    import heapq
        maxnum = []
        if k > len(tinput) or not tinput or k <= 0:
            return []
        for ele in tinput:
            ele = - ele
            if len(maxnum) <  k:
                heapq.heappush(maxnum, ele)
            else:
                heapq.heappushpop(maxnum,ele)
        res = map(lambda x: -x , maxnum)   #不能直接return .sort()
        res.sort()
        return res

思路三:堆排序的思想,降低时间复杂度,适合大量数据

易错点:k大于数组长度的情况

29 HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)

        maxsum = array[0]
        presum = array[0]
        i = 1
        while i < len(array):
            if presum >= 0:
                presum = presum+array[i]
                i = i+1
                if presum > maxsum:
                    maxsum = presum
            else: #不要与elif混淆
                presum =array[i]
                if presum > maxsum:
                    maxsum = presum
                i = i+1
        return maxsum
                

思路: 链接:https://www.nowcoder.com/questionTerminal/459bd355da1549fa8a49e350bf3df484
来源:牛客网
# 当前元素大于连续子数组和加上元素本身并且最大值比元素还小时,

            # 抛弃前面的连续子数组,重新开始计算连续数组和

            if num > (sum + num) and max < num:

思路2: 还可以用动态规划

链接:https://www.nowcoder.com/questionTerminal/459bd355da1549fa8a49e350bf3df484
来源:牛客网

# -*- coding:utf-8 -*-
class Solution:
    def FindGreatestSumOfSubArray(self, array):
        if not array:
            return 0
         
        dp = [array[0]]
         
        i = 1
        for num in array[1:]:
            if dp[i - 1] <= 0:
                dp.append(num)
            else:
                dp.append(dp[i - 1] + num)
            i += 1
         
        return max(dp)

30 求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。

        if not n:
            return 0
        res = ''
        for i in range(n+1):
            res = str(i)+res
        num = res.count('1')  #是‘1’不是1 一定要注意
        return num

思路1:转换成str计数

易错点:count的是‘1’而不是1,万分注意

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

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

思路2:找数学规律

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

        if not numbers:
            return ''
        for i in range(len(numbers)):
            for j in range(len(numbers) -1):
                   a = str(numbers[j])+ str(numbers[j+1])
                   b = str(numbers[j+1])+ str(numbers[j])
                   if a < b:
                       pass
                   elif a>=b:
                       temp = numbers[j]
                       numbers[j] = numbers[j+1]
                       numbers[j+1] = temp
        res = ''
        for i in numbers:
            res = res +str(i) #不要反了
        res1 = int(res)
        return res1
       
         

思路:类似于冒泡排序,不过比较规则不一样了。评论区链接https://www.nowcoder.com/questionTerminal/8fecd3f8ba334add803bf2a06af1b993
来源:牛客网
比较关键的一句话 所以在这里自定义一个比较大小的函数,比较两个字符串s1, s2大小的时候,先将它们拼接起来,比较s1+s2,和s2+s1那个大,如果s1+s2大,那说明s2应该放前面,所以按这个规则,s2就应该排在s1前面。

易错点:最后累加的顺序不要错了

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

        if index ==0:
            return 0
        res = [1]
        t1 = t2 = t3 = 0
        while len(res) <= index+1:
            minnum = min(2*res[t1],3*res[t2],5*res[t3])
            if minnum not in res:
                res.append(minnum)
            if minnum == 2*res[t1]:
                t1 = t1+1
            elif minnum == 3*res[t2]:
                t2 = t2+1
            elif minnum == 5*res[t3]:
                t3 = t3+1
        return res[index-1]
                

思路:利用生成的方法,利用三指针

易错点:注意重复值;注意第1个实际上是数组中第0个。以此类推。注意初始值为1。

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

        if not s:
            return -1
        res = dict.fromkeys(s,0) #初始化
        for i in s:
            res[i] = 1+ res[i]
        for j in s:  #重要,是看s的顺序
            if res[j] == 1:
                return s.index(j) #返回index
        return -1

思路:创建哈希表

易错:https://www.runoob.com/python/python-dictionary.html dict的用法

           注意第二次遍历是遍历的s。注意index的用法

35 输入两个链表,找出它们的第一个公共结点

        p1,p2 = pHead1,pHead2
        count1 = 0
        count2 = 0
        while p1:
            p1 = p1.next
            count1 = count1 + 1
        while p2:
            p2 = p2.next
            count2 = count2 + 1
        p1,p2 = pHead1,pHead2  
        if count1>=count2:
            if count1 > count2:
                for i in range(count1-count2):
                    p1 = p1.next
            while p1:
                if p1 == p2:
                    return p1
                else:
                    p1 = p1.next
                    p2 = p2.next
            return None
        elif count1<count2:
            for i in range(count2-count1):
                    p2 = p2.next
            while p1:
                if p1 == p2:
                    return p1
                else:
                    p1 = p1.next
                    p2 = p2.next
            return None

思路:第一遍,找出差值k。第二遍,长的先走k步再找公共结点

36 统计一个数字在排序数组中出现的次数

链接:https://www.nowcoder.com/questionTerminal/70610bf967994b22bb1c26f9ae901fa2?commentTags=Python
来源:牛客网

class Solution:
    def GetNumberOfK(self, data, k):
        if len(data) == 0:
            return 0
        lastPos, firstPos = self.getLastKPos(data, k), self.getFirstKPos(data, k)
        return  0 if firstPos == -1 else lastPos - firstPos + 1
 
    def getLastKPos(self, data, k):
        left, right = 0, len(data) - 1
        while left <= right:
            middle = (left + right) // 2
            if data[middle] == k:
                if middle == right or data[middle + 1] != k:
                    return middle
                else:
                    left = middle + 1
            elif data[middle] > k:
                right = middle - 1
            else:
                left = middle + 1
        return -1
 
    def getFirstKPos(self, data, k):
        left, right = 0, len(data) - 1
        while left <= right:
            middle = (left + right) // 2
            if data[middle] == k:
                if middle == 0 or data[middle - 1] != k:
                    return middle
                else:
                    right = middle - 1
            elif data[middle] > k:
                right = middle - 1
            else:
                left = middle + 1
        return -1

思路:利用二分查找,分别找到k的头尾,随后做差。

思路二:由于是整数,可以查找k-0.5,k+0.5

易错点:看到排序,就应该想到二分查找

37 输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。

        if not root:
            return 0
        left = self.TreeDepth(root.left)
        right = self.TreeDepth(root.right)
        maxdepth = max(left,right) + 1 
        return maxdepth

思路:来自评论区——递归求解
                                       假如是空节点,则返回0;
                                       否则,原树的深度由左右子树中深度较的深度加1,为原树的深度。
易错点:递归主要有两点:找好基准条件;想好递归规则。

38  输入一棵二叉树,判断该二叉树是否是平衡二叉树。

 if not pRoot:
            return True  #此处返回的是True
        left = self.TreeDepth(pRoot.left)
        right = self.TreeDepth(pRoot.right)
        diff = abs(left - right)
        if diff > 1:
            return False  #false不要拼写错了
        return self.IsBalanced_Solution(pRoot.left) and self.IsBalanced_Solution(pRoot.right)#递归调用

思路一:递归调用,注意基准条件与递归条件,以及最后返回的值

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

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def IsBalanced_Solution(self, p):
        return self.dfs(p) != -1
    def dfs(self, p):
        if p is None:
            return 0
        left = self.dfs(p.left)
        if left == -1:
            return -1
        right = self.dfs(p.right)
        if right == -1:
            return -1
        if abs(left - right) > 1:
            return -1
        return max(left, right) + 1

思路二:自下而上,降低重复率。

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

        res = []
        for i in array:
            if i not in res:
                res.append(i)
            elif i in res:
                res.remove(i)
        return res

思路:从头开始遍历数组,出现重复则剔除。问题是复杂度偏高。

 

        res = []      #调用count法
        for i in array:
            num = array.count(i)
            if num == 1:
                res.append(i)
        return res'''
        if not array or len(array)<3:
            return None
        hashres = {}
        res = []
        for i in array:
            if i in hashres:
                hashres[i] += 1
            else:
                hashres[i] = 1
        for j in hashres.keys():
            if hashres[j] == 1:
                res.append(j)
        return res'''
    def FindNumsAppearOnce(self, array):
        if not array:
            return []
        # 对array中的数字进行异或运算
        tmp = 0
        for i in array:
            tmp ^= i
        # 获取tmp中最低位1的位置
        idx = 0
        while (tmp & 1) == 0:
            tmp >>= 1
            idx += 1
        a = b = 0
        for i in array:
            if self.isBit(i, idx):
                a ^= i
            else:
                b ^= i
        return [a, b]
    def isBit(self, num, idx):
        """
        判断num的二进制从低到高idx位是不是1
        :param num: 数字
        :param idx: 二进制从低到高位置
        :return: num的idx位是否为1
        """
        num = num >> idx
        return num & 1

思路二:链接:https://www.nowcoder.com/questionTerminal/e02fdb54d7524710a7d664d082bb7811
来源:牛客网
    我们还是从头到尾依次异或数组中的每一个数字,那么最终得到的结果就是两个只出现一次的数字的异或结果。因为其它数字都出现了两次,在异或中全部抵消掉了。由于这两个数字肯定不一样,那么这个异或结果肯定不为0 ,也就是说在这个结果数字的二进制表示中至少就有一位为1 。我们在结果数字中找到第一个为1 的位的位置,记为第N 位。现在我们以第N 位是不是1 为标准把原数组中的数字分成两个子数组,第一个子数组中每个数字的第N 位都为1 ,而第二个子数组的每个数字的第N 位都为0 。

 现在我们已经把原数组分成了两个子数组,每个子数组都包含一个只出现一次的数字,而其它数字都出现了两次。因此到此为止,所有的问题我们都已经解决。

40 小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!

        res = [] #总结果
        res1 = []  #每一次的结果
        i = 1
        if tsum<=2:   #不能包含自己
            return []  #注意返回的是[]
        while i <= tsum/2 + 1:
            sumtmp = 0
            tmp = i      #存储之前的i值
            while sumtmp <= tsum:
                sumtmp = i + sumtmp
                i = i+1
                if sumtmp == tsum:  #相等则添加
                    print(tmp,i)
                    for j in range(tmp,i):
                        res1.append(j)
                if res1:
                    res.append(res1)
                res1 =[]
            i = tmp
            i = i+1
        return res

思路:遍历法

易错点:注意不能包含自己

思路二:双指针法

        if tsum <1:
            return None
        small = 0
        big = 1
        num = list(range(1,tsum))
        res = []
        while big < tsum and small < big :
            if sum(num[small:big]) == tsum:
                res.append(num[small:big])
                big = big + 1  #缺少此句会进入死循环
            elif sum(num[small:big]) < tsum:
                big = big + 1
            else:
                small = small + 1
        return res

评论区:

链接:https://www.nowcoder.com/questionTerminal/c451a3fd84b64cb19485dad758a55ebe
来源:牛客网
在答案区找到一个答案,说的很好,叫做双指针技术,就是相当于有一个窗口,窗口的左右两边就是两个指针,我们根据窗口内值之和来确定窗口的位置和宽度。非常牛逼的思路,虽然双指针或者所谓的滑动窗口技巧还是蛮常见的,但是这一题还真想不到这个思路。