【算法题】剑指offer汇总--题题多解法--python实现

目录

未参考剑指offer上面的思路

3. 数组中的重复数字

题目:判断列表中是否有重复的数

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

4.二维数组中的查找

在这里插入图片描述

# -*- coding:utf-8 -*-
class Solution:
    # array 二维列表
    def Find(self, target, array):
        # write code here
        rows = len(array)
        cols = len(array[0])
        #此时应该用or不能用and
        if rows == 0 or cols == 0:
            return False
        for i in range(rows):
            if target >= array[i][0] and target <= array[i][-1]:
                for j in range(cols):
                    if target == array[i][j]:
                        return True
        return False

笔记
获取二维列表的维度:

       #获取行
        rows = len(array)
        #获取列
        cols = len(array[0])

获取二维数组的维度

       #获取行
        rows = array.shape(0)
        #获取列
        cols = array.shape(1)

5.替换空格

在这里插入图片描述

# -*- coding:utf-8 -*-
class Solution:
    # s 源字符串
    def replaceSpace(self, s):
        # write code here
        ss = ""
        for i in s:
            if i == " ":
                ss = ss + "%20"
            else:
                ss = ss + i
        return ss

6 从尾到头打印链表

在这里插入图片描述

class Solution:
    # 返回从尾部到头部的列表值序列,例如[1,2,3]
    def printListFromTailToHead(self, listNode):
        # write code here
        res=[]
        while listNode:
            res.append(listNode.val)
            listNode=listNode.next
        return res[::-1]  #逆序打印

笔记
python中[-1]、[:-1]、[::-1]、[2::-1]的使用方法

a=[1,2,3.4,5]
print(a)
[ 1 2 3 4 5 ] 
print(a[-1]) ###取最后一个元素
[5] 
print(a[:-1])  ### 除了最后一个取全部
[ 1 2 3 4 ] 
print(a[::-1]) ### 取从后向前(相反)的元素
[ 5 4 3 2 1 ]
print(a[2::-1]) ### 取从下标为2的元素翻转读取
[ 3 2 1 ]

7 重建二叉树

在这里插入图片描述

class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None
class Solution:
    # 返回构造的TreeNode根节点
    def reConstructBinaryTree(self, pre, tin):
        if len(pre) == 0:
            return None
        if len(pre) == 1:
            return TreeNode(pre[0])
        root = TreeNode(pre[0])
        # i = tin.index(pre[0])
        #
        # tin_lchild = tin[:i]
        # len_lchild = len(tin_lchild)
        # pre_lchild = pre[1:1+len_lchild]
        #
        # tin_rchild = tin[i+1:]
        # len_rchild = len(tin_rchild)
        # pre_rchild = pre[1+len_lchild:]
        root.left = self.reConstructBinaryTree(pre[1:1+len(tin[:tin.index(pre[0])])], tin[:tin.index(pre[0])])
        root.right = self.reConstructBinaryTree(pre[1+len(tin[:tin.index(pre[0])]):], tin[tin.index(pre[0])+1:])
        return root

笔记
递归★★★★★

class soultion:
    def nove(self,n,a,b,c):
        print(n,a,b,c)
        if n == 1:
            print(a,'------------>',c)
        else:
            self.nove(n-1,a,c,b)
            self.nove(1,a,b,c)
            self.nove(n-1,b,a,c)
if __name__ == "__main__":
    a = soultion()
    b = a.nove(3, 'A', 'B', 'C')
    print(b)

上述程序的执行结构图如下所示:
在这里插入图片描述
四个字母分别代表传入的(n,a,b,c)
传入顺序:由左到右,由上到下。

8 二叉树的下一个节点

在这里插入图片描述
题目分析
在这里插入图片描述
在这里插入图片描述

# -*- coding:utf-8 -*-
# class TreeLinkNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
#         self.next = None
class Solution:
    def GetNext(self, pNode):
        # write code here
        #有右子树
        if pNode.right != None:
            r = pNode.right
            while r.left:
                r = r.left
            return r
        #无右子树
        else:
            #如果是根节点(无父节点)
            if pNode.next == None:
                return None
            #如果不是根节点(有父节点)
            else:
                #如果是父节点的左孩子
                if pNode == pNode.next.left:
                    return pNode.next
                #如果是父节点的右孩子
                else:
                    while pNode.next.next.left != pNode.next:
                        pNode = pNode.next
                        #没有符合条件的即是尾节点
                        if pNode.next.next == None:
                            return None
                    return pNode.next.next
                

9 用两个栈实现队列

在这里插入图片描述
题目分析
在这里插入图片描述
栈:先进后出
队列:先进先出
完成队列的push和pop操作,即实现头删和尾插。

  1. 首先建立两个栈,s1、s2
  2. 将队列中的元素“abcd”压入s1中,此时s2为空
  3. 将s1中的元素pop进s2中,此时pop一下s2中的元素,就可以达到和队列删除数据一样的顺序了
  4. 当s2只pop了一个元素a时(未pop完所有元素),s1中可能还会插入元素e,这时如果将s1中的元素e插入s2中,在a之后出栈的元素就是e了,显然,这样想是不对的,我们必须规定当s2中的元素pop完之后,也就是s2为空时,再插入s1中的元素。

题目分析笔记借鉴于下面的链接:
https://blog.csdn.net/cherrydreamsover/article/details/80466781

# -*- coding:utf-8 -*-
class Solution:
    def __init__(self):
        self.stack1 = []
        self.stack2 = []
    def push(self, node):
        # write code here
        #append操作为尾插,为了遵循栈的原则,在pop的时候必须是pop()
        self.stack1.append(node)
    def pop(self):
        # return xx
        if not self.stack2:
            while self.stack1:
                self.stack2.append(self.stack1.pop())
        #append操作为尾插,为了遵循栈的原则,在pop的时候必须是pop()
        return self.stack2.pop()

10 斐波那契数列

斐波那契数列定义:从第3项开始,每一项都等于前两项之和。
F(n)=F(n - 1)+F(n - 2)(n ≥ 3,n ∈ N*)
标准斐波那契数列(f(1)=1、f(2)=1))在这里插入图片描述

# -*- coding:utf-8 -*-
class Solution:
    def Fibonacci(self, n):
        # write code here
        count = 0
        a , b = 0 ,1
        while count < n:
            a , b = b , a+b
            count+=1
        return a
        """
        #下面的代码是用递归实现
        #经过测试,在n>=32时运行时间已经超过2s
        if n == 0:
            return 0 
        if n == 1:
            return 1
        return self.Fibonacci(n-1) + self.Fibonacci(n-2)
        """

11 旋转数组的最小数字

在这里插入图片描述

# -*- coding:utf-8 -*-
class Solution:
    def minNumberInRotateArray(self, rotateArray):
        # write code here
        if not rotateArray:
            return None
        if len(rotateArray) == 1:
            return rotateArray[0]
        i = 0 
        while rotateArray[i] and rotateArray[i+1]:
            if rotateArray[i] > rotateArray[i+1]:
                return rotateArray[i+1]
            i+=1
            

15 二进制中1的个数

在这里插入图片描述
在这里插入图片描述
下面代码中有两个比较神奇的操作:

 操作一      if n < 0:
            n = n & 0xffffffff

在Python中,数的大小是可以无限扩大的,不像c++中,数超过32位会溢出。
所以对于一个负数而言,进行了这个操作,实际上是提取了负数(在计算机中以补码形式存在)的后32位,前面的任意位则变成了0。
比如说 -1,
一开始是 111…(n个1)…11111111,
进行111…(n个1)…11111111 & 0x000…(n个0)…ffffffff,
得到,00…(n个0)…111111111(32个1),变成了含32个1的正数.

可能会有人对上面的例子有疑问,为什么经过上面的操作之后-1就变成了含32个1的正数?????
解释:
在操作之前,-1的补码是 111…(n个1)…1111111,但是黄色的1表示是负数,在经过操作之后,只保留了最后的32位(为什么保留32位,下面有解释,先继续往下看)此时00…(n个0)…111111111(32个1)中最左边的1不在是符号位,符号位而是最左边的0,因此变成了含32个1的正数.
为什么只保留最后32位,为什么是与0xffffffff相与????
解释:
为什么是与0xffffffff相与,只保留32位???而不是与0xffffffffffffffff(16个)相与保留64位呢,因为牛客网考虑了其他语言,在其他语言中,数超过32位会溢出。

操作二	n = n & (n-1)

如果一个整数不为0,那么这个整数至少有一位是1。如果我们把这个整数减1,会发生下面三点变化:
以最右边的1为分界线,例如二进制数1100

  1. 最右边的1变为0
  2. 原来在1后面的所有的0都会变成1
  3. 原来在1前面数不受影响
    因次,可以得到结果是1011

我们发现减1的结果是把最右边的一个1开始的所有位都取反了。这个时候如果我们再把原来的整数和减去1之后的结果做与运算,从原来整数最右边一个1那一位开始所有位都会变成0。如1100&1011=1000.也就是说,把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0.那么一个整数的二进制有多少个1,就可以进行多少次这样的操作。

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

16 数值的整数次方

在这里插入图片描述
快速幂法:
快速幂就是快速算底数的n次幂。其时间复杂度为 O(log₂N)
在这里插入图片描述

# -*- coding:utf-8 -*-
class Solution:
    def Power(self, base, exponent):
        # write code here
        """
        法一:快速幂法
        """
        #用来记录exponent的正负
        flag = False
        if base == 0:
            if exponent > 0:
                return 0
            elif exponent < 0:
                return None
        else:
            if exponent == 0:
                return 1
            else:
                if exponent < 0:
                    flag = True
                    exponent = -exponent
                result = base
                #右移一位
                exponent >>= 1
                while exponent:
                    base *= base
                    #判断最右边的一位是否为1
                    if (exponent & 1) == 1:
                        result *= base
                    exponent >>= 1
                if flag:
                    return 1/result
                else:
                    return result
        """
        法二
        return base**exponent
        """      

18.2 删除链表中重复的节点

在这里插入图片描述

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def deleteDuplication(self, pHead):
        # write code here
        #由于链表可能从第一个节点就开始重复,所以在pHead的头节点之前再增加一个节点
        first = ListNode(-1)
        first.next = pHead
        cur = pHead
        per = first
        while cur and cur.next:
            if cur.val != cur.next.val:
                per = cur
                cur = cur.next
            else:
                #加上下面这句话之后,会保留重复的节点
                #比如链表1->2->3->3->4->4->5 
                #现在的输出为:1->2->5
                #加上下面这句话的输出为:1->2->3->4->5 
                #per = cur
                val = cur.val
                while cur and cur.val == val:
                    cur = cur.next
                per.next = cur
        return first.next

上述代码的完整调试程序

# -*- coding:utf-8 -*-
class Node(object):
    """"节点"""
    def __init__(self,elem):
        self.elem = elem
        self.next = None
        
class Singlelinklist(object):
    """单链表"""

    # 不传入节点时为空列表,因此node的默认值为None
    def __init__(self, node=None):
        # _表示私有属性
        # _head指向列表中的第一个节点
        self._head = node

    def is_empty(self):
        return self._head == None

    def lenth(self):
        # cur表示游标,用来遍历节点
        cur = self._head
        # count用来计数
        count = 0
        while cur != None:
            count += 1
            cur = cur.next
        return count

    def travel(self):
        """遍历(打印)整个列表"""
        cur = self._head
        while cur != None:
            print(cur.elem, end=" ")
            cur = cur.next
        print("")

    # 复杂度O(1)
    def add(self, item):
        """列表头部添加元素"""
        node = Node(item)
        # 下面两句话顺序不能改表,改变顺序之后,原有的列表会丢失
        node.next = self._head
        self._head = node
        # 上面个两句话可以用下面这一句话代替
        # self._head,node.next = node,self._head

    # 复杂度O(n)
    def append(self, item):
        """列表尾部添加元素"""
        # 传入的数据,先需要把数据转化为节点
        node = Node(item)
        if self.is_empty():
            self._head = node
        else:
            cur = self._head
            while cur.next != None:
                cur = cur.next
            cur.next = node

    # 复杂度O(n)
    def insert(self, pos, item):
        # pos从0开始索引
        # per表示pos最终指向pos-1的那个位置
        node = Node(item)
        per = self._head
        count = 0
        if pos <= 0:
            self.add(item)
        elif pos > (self.lenth() - 1):
            self.append(item)
        else:
            while count < (pos - 1):
                count += 1
                per = per.next
            # 此时per指向pos-1
            node.next = per.next
            per.next = node

    def remove(self, item):
        # 只删除与之相匹配的第一个,第二个之后出现的不删除
        if self.is_empty():
            return
        cur = self._head
        per = None
        while cur != None:
            # 判断item与节点是否相等
            if cur.elem == item:
                # 如果删除的是第一个结点的话
                if per == None:
                    self._head = cur.next
                else:
                    per.next = cur.next
                break
            else:
                per = cur
                cur = cur.next

    # 复杂度O(n)
    def search(self, item):
        cur = self._head
        while cur != None:
            if cur.elem == item:
                return True
            else:
                cur = cur.next
        return False

# class ListNode:
#     def __init__(self, x):
#         self.elem = x
#         self.next = None
class Solution:
    def deleteDuplication(self, pHead):
        # write code here
        #由于链表可能从第一个节点就开始重复,所以在pHead的头节点之前再增加一个节点
        # first = Node(-1)
        # first.next = pHead
        cur = pHead._head.next
        per = pHead._head
        while cur and cur.next:
            if cur.elem != cur.next.elem:
                per = cur
                cur = cur.next
            else:
                per = cur
                elem = cur.elem
                while cur and cur.elem == elem:
                    cur = cur.next
                per.next = cur
        return pHead._head.next


if __name__ == '__main__':
    s = [-1,1,2,3,3,4,4,5]
    li = Singlelinklist()
    for i in range(len(s)):
        li.append(s[i])
    # print(li)
    answer = Solution()
    a = answer.deleteDuplication(li)
    print(a)

19.正则表达式匹配

在这里插入图片描述

# 可以分为四种情况
#1  s、pattern都为空 ,此时为真
#2  s不为空,pattern为空,此时为假
#3  s为空,pattern不为空,此时可能为真也看了能为假
    #此时只有一种情形为真,len(pattern)为偶数,且每一个字母或者.后面都是*,比如:a*b*.*
#4  s、pattern都不为空
    #此时可以分为两种情形
    #4.1    pattern的第二个字符为*
        #此时分为三种情况
        #4.1.1  *表示前面的字符出现0次,比如:aa,ab*ac*
        #此时self.match(s,pattern[2:])
        #4.1.1  *表示前面的字符出现n次(用出现1次进行递归),比如:aaaab,a*b
        #此时self.match(s[1:],pattern[2:])
        #4.1.3  .表示多个字符时,比如:abc,.*,此时.表示abc,*表示出现一次
        #此时self.match(s[1:],pattern)
    #4.2    pattern的第二个字符不为*
        #此时比较简单,直接比较s、pattern的第一位即可
# -*- coding:utf-8 -*-
class Solution:
    def match(self, s, pattern):
        # write code here
        if s == "" and pattern == "":
            return True
        elif s != "" and pattern == "":
            return False
        elif s == "" and pattern != "":
            if len(pattern) % 2 == 0:
                    for i in range(len(pattern)):
                            if i // 2 != 0 and pattern[i] == "*":
                                return False
                    return True
            else:
                return False
        else:
            # pattern的第二个字符为*的情况
            if len(pattern) >= 2 and pattern[1] == "*":
                if s[0] == pattern[0] or pattern[0] == "." :
                        return self.match(s,pattern[2:]) or self.match(s[1:],pattern[2:]) or self.match(s[1:],pattern)
                elif  (s[0] != pattern[0] and pattern[1] != ".") and pattern[1] == "*":
                    return self.match(s,pattern[2:])
            #pattern的第二个字符不为*的情况
            else:
                if s[0] == pattern[0] or pattern[0] == ".":
                    return self.match(s[1:],pattern[1:])
                else:
                     return False

20.表示数值的字符串

在这里插入图片描述
题目分析

  1. ±出现在第一位或者是e和E的下一位
  2. E、e后面必须有数(不能是小数的形式),形式如下:2 、-2
  3. 不能出现e之外的其他字母
  4. 只能出现一次小数点
# -*- coding:utf-8 -*-
# -*- coding:utf-8 -*-
class Solution:
    # s字符串
    #def __init__(self, s):
        #self.s = s
    def isNumeric(self,s):
        # write code here
        point_count = 0
        #E/e的坐标不可能为负值,初始化一个小于0的数即可,但是-1不行,
        #初始化为-1时,E_coordinate+1=0,与e/E出现在第一位时矛盾
        E_coordinate = -5
        has_other = False
        has_more_point = False
        E_behind_false = False
        add_coordinate_false = False
        for i in range(len(s)):
            #记录小数点的个数
            if s[i] == ".":
                point_count += 1
            #判断E/e出现的位置
            elif s[i] == "e" or s[i] == "E":
                E_coordinate = i
                if i == len(s)-1:
                    return False
                elif (s[i+1] >= "0" and s[i+1] <= "9")  or s[i+1] == "+" or s[i+1] == "-":
                    for j in range(i+2,len(s)-1):
                        if s[j] < "0" or s[j] >"9":
                            E_behind_false = True
                else:
                    E_behind_false = True
            #判断+/-号出现的位置是否正确
            elif s[i] == "+" or s[i] == "-":
                if i != 0 and i != E_coordinate+1:
                    add_coordinate_false = True
            #判断是否有其他异常字符
            elif s[i] < "0" or s[i] >"9":
                has_other = True
        if point_count > 1:
            has_more_point = True

        if has_other ==True or has_more_point == True or E_behind_false == True or add_coordinate_false == True:
            return False
        else:
            return True

21 调整数组顺序使奇数位于偶数前面

在这里插入图片描述

# -*- coding:utf-8 -*-
class Solution:
    def reOrderArray(self, array):
        # write code here
        single = []
        double = []
        for i in array:
            if i % 2 ==0:
                double.append(i)
            else:
                single.append(i)
        return single + double

22 链表中的倒数第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
        #方法一:快慢指针
        #先让快指针向前走k个节点(每一步都需要判断是否越界)
        #当快指针到达尾节点时,慢指针到达倒数第k个节点
        slow = head
        fast = head
        for i in range(k):
            if fast:
                fast = fast.next
            else:
                return None
        while fast:
            fast = fast.next
            slow = slow.next
        return slow
        """
        #方法二:
        #先遍历一遍计算链表长度,再输出该链表中倒数第k个结点。
        
        #用于记录链表长度
        count = 0
        cur = head
        while cur:
            cur = cur.next
            count+=1
        if k > count:
            return None
        #经过上面的遍历,per已经指向最后,因此需要在初始化一遍
        cur = head
        for i in range(count-k):
            cur = cur.next
        return cur
        """

23.链表中环的入口节点

在这里插入图片描述
解题思路中结论二的证明
设:
链表头到环入口长度为–a
环入口到相遇点长度为–b
相遇点到环入口长度为–c
在这里插入图片描述

则:相遇时
快指针路程=a+(b+c)k+b ,k>=1 其中b+c为环的长度,k为绕环的圈数(k>=1,即最少一圈,不能是0圈,不然和慢指针走的一样长,矛盾)。
慢指针路程=a+b
快指针走的路程是慢指针的两倍,所以:
*(a+b)2=a+(b+c)k+b
化简可得:
a=(k-1)(b+c)+c 这个式子的意思是: 链表头到环入口的距离=相遇点到环入口的距离+(k-1)圈环长度。其中k>=1,所以k-1>=0圈。所以两个指针分别从链表头和相遇点出发,最后一定相遇于环入口。

"""
解题思路--快慢指针法
设置快慢指针,都从链表头出发,快指针每次走两步,慢指针一次走一步,假如有环,一定相遇于环中某点(结论1)。
接着让两个指针分别从相遇点(meet_point)和链表头(head_point)出发,两者都改为每次走一步,最终相遇于环入口(结论2)。
"""
# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def EntryNodeOfLoop(self, pHead):
        # write code here
        slow = pHead
        fast = pHead
        meet_point = pHead
        head_point = pHead
        while fast != None and fast.next != None:
            slow = slow.next
            fast = fast.next.next
            if slow == fast:
                meet_point = slow
                while head_point != meet_point:
                    head_point = head_point.next
                    meet_point = meet_point.next
                return head_point

24 反转链表

在这里插入图片描述

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    # 返回ListNode
    def ReverseList(self, pHead):
        # write code here
        #方法一:
        #相遇于方法二可以节省空间
        tem = None
        rev_head = None
        #pHead相当于cur,表示当前节点
        while pHead:
            #tem临时记录pHead的下一个节点
            tem = pHead.next
            #反转
            pHead.next = rev_head
            #反转之后的链表的首节点的更新
            rev_head =pHead 
            #当前节点继续往下走
            pHead = tem
        return rev_head
        """
        #方法二:
        #新创建一个链表,从头部添加节点
        new_list = None
        while pHead:
            #将数的形式转化为节点的形式
            node = ListNode(pHead.val)
            #在头部添加节点
            node.next = new_list
            #每次都需要让new_list指向新节点的头部
            new_list = node
            #旧链表遍历
            pHead = pHead.next
        return new_list
        """     

25 合并两个排序的链表

在这里插入图片描述

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    # 返回合并后列表
    def Merge(self, pHead1, pHead2):
        # write code here
        new_head = ListNode(-1)
        root = new_head
        while pHead1 != None and pHead2 != None:
            #下面的判断必须是if pHead1.val > pHead2.val:
            #不能是if pHead1 > pHead2:
            #不能对节点进行比较大小
            if pHead1.val > pHead2.val:
                new_head.next = pHead2
                new_head = new_head.next
                pHead2 = pHead2.next
            else:
                new_head.next = pHead1
                new_head = new_head.next
                pHead1 = pHead1.next
        if pHead1:
                new_head.next = pHead1
        if pHead2:
                new_head.next = pHead2
        return root.next

26 树的子结构

在这里插入图片描述

# -*- 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
        result = False
        if pRoot1 != None and pRoot2 != None:
            if pRoot1.val == pRoot2.val:
            #此方法中下面的语句可以用下面的这一句话代替
            #    return self.Perorder(pRoot1,pRoot2) or self.HasSubtree(pRoot1.left,pRoot2) or self.HasSubtree(pRoot1.right,pRoot2)
                result = self.Perorder(pRoot1,pRoot2)
            if not result:
                result = self.HasSubtree(pRoot1.left,pRoot2)
            if not result:
                result = self.HasSubtree(pRoot1.right,pRoot2)
        return result
            
    def Perorder(self, pRoot1, pRoot2):
        if not pRoot2:
            return True
        if not pRoot1:
            return False
        #下面的四句话可以写成
        #if pRoot1.val != pRoot2.val:
        #    return False
        #return self.Perorder(pRoot1.left,pRoot2.left) and self.Perorder(pRoot1.right,pRoot2.right)
        if pRoot1.val == pRoot2.val:
            return self.Perorder(pRoot1.left,pRoot2.left) and self.Perorder(pRoot1.right,pRoot2.right)
        else:
            return False

27 二叉树的镜像

在这里插入图片描述
在这里插入图片描述

# -*- 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 not root:
            return
        if root.left or root.right:
            root.left,root.right = root.right,root.left
        self.Mirror(root.left)
        self.Mirror(root.right)
        return root

28 判断对称二叉树

在这里插入图片描述

# -*- 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 pRoot is None:
            return True
        return self.check(pRoot.left,pRoot.right)
    def check(self,l,r):
        if l == None and r == None:
            return True
        elif (l != None and r == None) or (l == None and r != None) or (l.val != r.val):
            return False
        return self.check(l.left,r.right) and self.check(l.right,r.left)

29 顺时针打印矩阵

在这里插入图片描述
对于下面代码的两点解释

1.pop对于矩阵而言能否pop出某一行?

res += matrix.pop(0)

经过资料查找,所有的解释都是同一种说法:pop只是对于list进行操作,每次只能pop出一个元素。但是经过代码测试,pop函数可以pop出矩阵/二维数组的某一行(pop出某一列需要借助for循环)

测试代码如下:

a = [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]]
a.pop(0)
print(a)

结果:

[[5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]

2.对列进行操作的时候,判断语句为什么是if matrix and matrix[0]而不是if matrix?
举个例子:
      a = [[1],[2],[3],[4],[5]]
      经过res += matrix.pop(0)
      a = [[2],[3],[4],[5]]
      再经过

            if matrix and matrix[0]:
                for row in matrix:
                    res.append(row.pop())

      a = [[],[],[],[]]
      对于此时的a,matrix为真,matrix[0]为假。
      因为[[],[],[],[]]相当于[False,False,False,False]
因此,在对列进行操作时,可能在对首列或者尾列操作之后形成[[],[],[],[]]的情形,一次需要使用if matrix and matrix[0]进行判断。

# -*- coding:utf-8 -*-
class Solution:
    # matrix类型为二维列表,需要返回列表
    def printMatrix(self, matrix):
        # write code here
        res = []
        while matrix :
            # 下面不能用append,append只接受一个元素
            #pop可以移除矩阵中的某一行
            #a = [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]]
            #a.pop(0)--->a = [[5,6,7,8],[9,10,11,12],[13,14,15,16]]
            res += matrix.pop(0)
            if matrix and matrix[0]:
                for row in matrix:
                    res.append(row.pop())
            if matrix:
                #先pop出最后一行,在进行取逆操作
                res += matrix.pop()[::-1]
            if matrix and matrix[0]:
                #逆序取矩阵中的每一行
                for row in matrix[::-1]:
                    res.append(row.pop(0))
        return res

30 包含min函数的栈

在这里插入图片描述

"""
思路:
得到最小元素min函数的复杂度为O(1)
建立一个辅助栈stack2,每当压入stack1的元素小于辅助栈stack2时,同时将元素压入stack2
在出栈时,当在stack1的栈顶元素于辅助栈stack2的栈顶元素相等时,辅助栈也需要进行pop操作
"""
# -*- coding:utf-8 -*-
class Solution:
    def __init__(self):
        self.stack1 = []
        self.stack2 = []
    def push(self, node):
        # write code here
        self.stack1.append(node)
        if not self.stack2:
            self.stack2.append(node)
        elif node <= self.stack2[-1]:
            self.stack2.append(node)
    def pop(self):
        # write code here
        if self.stack2[-1] == self.stack1[-1]:
            self.stack2.pop()
        #pop方法,需要返回元素
        return self.stack1.pop()
    def top(self):
        # write code here
        return self.stack1[-1]
    def min(self):
        # write code here
        return self.stack2[-1]

31 栈的压入、弹出序列

在这里插入图片描述
方法一–借助辅助空间
思路
借助一个辅助栈,把压入序列按顺序存储到辅助栈中,当辅助栈不为空且栈顶元素等于弹出序列第一个元素时,弹出辅助栈的栈顶元素,在判断输出序列的下一个元素

#方法一
# -*- coding:utf-8 -*-
class Solution:
    def IsPopOrder(self, pushV, popV):
        # write code here
        assist_stack = []
        j = 0
        for i in pushV:
            assist_stack.append(i)
            while assist_stack and assist_stack[-1] == popV[j]:
                assist_stack.pop()
                j+=1
        return len(assist_stack) == 0

方法一参考的以下博客:
https://blog.csdn.net/weixin_40941966/article/details/80798880

方法二–不借助辅助空间
思路:
如果popV是压入栈序列pushV的弹出序列应满足下列的规律:
该规律用白话叙述不容易理解,通过下面的例子进行解释
pushV = [1,2,3,4,5]
popV1 = [4,5,3,2,1]
popV2 = [4,3,5,1,2]
popV1是pushV的弹出序列,popV2不可能是pushV的弹出序列。

首先,我们看弹出序列的的首元素,popV1和popV2都为4,然后再压入栈序列中进行搜索,pushV[3] = 4
然后,对上面的序列进行pop操作,pop出元素4,可以得到
pushV = [1,2,3,5]
popV1 = [5,3,2,1]
popV2 = [3,5,1,2]
最关键的地方就在下面,弹出序列的下一个元素(popV1中的5,popV2中的3)应该处于pushV的什么位置????

经过分析,弹出序列的下一个元素必须处于上一步pop出的元素(即4)左边的一个元素或者右边所有元素中

此题的关键在于这一句话,不明白的细细品

代码有两个版本,思路一样,版本二相比于版本一节省时间,
建议先看版本一再看版本二,版本一更容易理解

# 版本二
#相比于版本一节省时间

# -*- coding:utf-8 -*-
class Solution:
    def IsPopOrder(self, pushV, popV):
        # write code here
        temp = 1
        error_flag = True
        while popV:
            #不再从头开始遍历,而是直接看正确位置范围内有没有该元素
            for i in range(temp-1,len(pushV)):
                if popV[0] == pushV[i]:
                    temp = i
                    popV.pop(0)
                    pushV.pop(i)
                    #如果在正确范围内有该元素,则error_flag = False
                    error_flag = False
                    break
            #上面的for循环执行完之后,如果没有在正确范围内找到(即error_flag没修改),
            # 则说明该序列不是pushV的弹出序列
            if error_flag:
                return False
            #重置错误标志
            error_flag = True
        return True

"""
    
下面的代码实现上面的描述,但是下面的for循环每次都遍历整个序列,时间比较长,在牛客网上无法通过。
最上面的代码是对下面代码的修改,节省了时间,可以在牛客网通过。

# 版本一

# -*- coding:utf-8 -*-
class Solution:
    def IsPopOrder(self, pushV, popV):
        # write code here
        #为什么初始化为1,以为下面有temp-1
        temp = 1
        while popV:
            for i in range(len(pushV)):
                #寻找本次弹出序列元素在popV的位置
                if popV[0] == pushV[i]:
                    #判断位置是否正确
                    if i < temp-1:
                        return False
                    #temp用于记录i的位置,方便判断弹出序列的下一元素是否处于正确位置
                    temp = i
                    popV.pop(0)
                    pushV.pop(i)
                    #终止for循环
                    break
        #弹出序列所有元素位置都正确,则  return True
        return True
"""

32 从上往下打印二叉树–广度遍历

在这里插入图片描述

# -*- 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
        answer = []
        if not root:
            return answer
        li = [root]
        while li:
            cur = li.pop(0)
            answer.append(cur.val)
            if cur.left:
                li.append(cur.left)
            if cur.right:
                li.append(cur.right)
        return answer

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

在这里插入图片描述
二叉搜索树
若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉搜索树。

在这里插入图片描述
后续遍历结果:1 5 3 9 12 10 7
二叉搜索树的后续遍历具有以下特点:

  • 最后一个元素为数的根节点
  • 存个一个分界线,分界线左边的所有元素都小于根节点,分界线右边的所有元素都大于根节点(上图的分界线就在3 9 之间)
  • 经过第二步的分割,形成两个切片[1 5 3]、[9,12,10],切片同样符合上述规律

根据上面的特点,在考虑两种特殊情况,1.无左孩子,2.无右孩子

# -*- coding:utf-8 -*-
class Solution:
    def VerifySquenceOfBST(self, sequence):
        # write code here
        if not sequence:
            return False
        if len(sequence) == 1:
            return True
        count1 = 0
        count2 = 0
        for i in range(len(sequence)):
            #左右孩子节点都不为空
            if sequence[i] < sequence[-1] and sequence[i+1] > sequence[-1]:
                #左子树的所有节点必须小于根节点
                for j in range(i+1):
                    if sequence[j] > sequence[-1]:
                        return False
                #右子树所有节点必须大于根节点
                for k in range(i+1,len(sequence)-1):
                    if sequence[k] < sequence[-1]:
                        return False
                return self.VerifySquenceOfBST(sequence[:i+1]) and self.VerifySquenceOfBST(sequence[i+1:-1])
            #仅有左孩子
            if sequence[i] < sequence[-1]:
                count1 += 1
            #仅有右孩子
            if sequence[i] > sequence[-1]:
                count2 += 1 
            if count1 == len(sequence)-1 or count2 == len(sequence)-1:
                return self.VerifySquenceOfBST(sequence[:-1])
        return False

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

在这里插入图片描述
叶节点:没有子节点的节点
思路:
用一临时列表记入当前先序遍历的整条路径,若此路径节点值的和等于expectNumber,则将路径加入最终返回的列表之中。
注意:当一父节点的两个子节点所在的路径都遍历完时,需要将父节点从临时列表中删除。

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    # 返回二维列表,内部每个列表表示找到的路径
    def __init__(self):
        self.li = []
        #用于保存遍历的路径
        self.tem = []
    def FindPath(self, root, expectNumber):
        # write code here
        #当root为空时,直接返回空列表
        if not root:
            return self.li
        #当此节点为叶节点,开始判断sum(self.tem)是否等于expectNumber
        if not root.left and not root.right:
            self.tem.append(root.val)
            if sum(self.tem) == expectNumber:
                self.li.append(self.tem)
                #数组长度大于1时,才能进行下面的切片操作
                #当self.tem = self.tem[:-1]用self.tem.pop()代替时,会出现问题
                if len(self.tem) > 1:
                    self.tem = self.tem[:-1]
            else:
                self.tem.pop()
            return self.li
        #下面三句话相当前序遍历
        self.tem.append(root.val)
        if root.left:
            self.FindPath(root.left, expectNumber)
        if root.right:
            self.FindPath(root.right, expectNumber)
        #遍历完一次,返回上一层时需要将父节弹出
        self.tem.pop()
        return self.li

35 复杂链表的复制

在这里插入图片描述
思路
在这里插入图片描述

# -*- coding:utf-8 -*-
# class RandomListNode:
#     def __init__(self, x):
#         self.label = x
#         self.next = None
#         self.random = None
class Solution:
    # 返回 RandomListNode
    def Clone(self, pHead):
        # write code here
        if not pHead:
            return None
        #1.在原有链表上进行链表的复制,不处理self.random
        cur = pHead
        while cur:
            clone_node = RandomListNode(cur.label)
            clone_node.next = cur.next
            cur.next = clone_node
            cur = cur.next.next
            
        #2.处理新增复制节点的self.random
        cur = pHead
        while cur:
            cur.next.random = cur.random
            cur = cur.next.next
        
        #进行链表的拆分
        cur = pHead
        clone_list = pHead.next
        while cur:
            clone_node = cur.next           
            cur.next =  clone_node.next 
            if clone_node.next:               
                clone_node.next = clone_node.next.next           
            else:               
                clone_node.next = None           
            cur = cur.next        
            
        return clone_list

36 二叉搜索树与双向链表

在这里插入图片描述
思路
二叉搜索树的中序遍历为排序列表
在中序遍历的基础上进行稍微改动即可。

#二叉搜索树的中序遍历为排序列表
# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def __init__(self):
        self.last_node = None
    def Convert(self, pRootOfTree):
        # write code here
        self.Tinorder(pRootOfTree)
        while pRootOfTree and pRootOfTree.left:
            pRootOfTree = pRootOfTree.left
        return pRootOfTree
    
    def Tinorder(self,cur):
        if not cur:
            return
        self.Tinorder(cur.left)
        #当前节点的左节点指向上一节点
        cur.left = self.last_node;
        #只有当上一个节点不为空时,上一节点的右节点才能指向当前节点
        if self.last_node:
             self.last_node.right = cur;
        self.last_node = cur;
        self.Tinorder(cur.right)

37 序列化二叉树

在这里插入图片描述

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def __init__(self):
        self.count = -1
    def Serialize(self, root):
        # write code here
        # 下面的两种判断方式相同
        # if root is None:
        if not root:
            #必须添加分隔符,否则两位数转化为字符串时会出现错误
            return "#,"
        #如果当前节点是父节点,则会继续递归
        #如果当前节点是叶节点,则会返回“.val , # , #"
        return str(root.val)+ ',' + self.Serialize(root.left) + self.Serialize(root.right)
    def Deserialize(self, s):
        # write code here
        self.count += 1
        #只是第一次进入函数时对字符串进行分割
        #split操作返回的是一个列表,如果每次都进行split操作会报错
        #因为第一次往后s变成了列表,列表没有split操作
        if self.count == 0:
            s = s.split(',')
        if self.count >= len(s):
            return None
        root = None
        if s[self.count] != "#":
            root = TreeNode(int(s[self.count]))
            root.left = self.Deserialize(s)
            root.right = self.Deserialize(s)
        return root

split示例
在这里插入图片描述

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

在这里插入图片描述

# -*- coding:utf-8 -*-
class Solution:
    def MoreThanHalfNum_Solution(self, numbers):
        # write code here
        dic = {}
        for i in numbers:
            if i not in dic:
                dic[i] = 1
            else:
                dic[i] += 1
        #values() 方法返回一个迭代器,可以使用 list() 来转换为列表,列表为字典中的所有值
        if max(dic.values()) > len(numbers)//2:
            #遍历字典(dic),查找所有“值”的最大值(key=dic.get(everykey)),返回该“值”的“键”(dic)
            return max(dic,key=dic.get)
        else:
            return 0

40 最小的K个数

在这里插入图片描述

# -*- coding:utf-8 -*-
class Solution:
    def GetLeastNumbers_Solution(self, tinput, k):
        # write code here
        if k > len(tinput):
            return []
        self.quick_sort(tinput,0,len(tinput)-1)
        return tinput[:k]
    
    def quick_sort(self,li,first,last):
        if first >= last:
            return
        mid_value = li[first]
        low = first
        high = last
        while low < high:
            while low < high and li[high] >= mid_value:
                high -= 1
            li[low] = li[high]
            while low < high and li[low] < mid_value:
                low += 1
            li[high] = li[low]
        li[low] = mid_value
        self.quick_sort(li,first,low-1)
        self.quick_sort(li,low+1,last)

41 数据流中的中位数

在这里插入图片描述
方法一–锥排序

思路:

  • 建立一个大顶锥,一个小顶锥,小顶锥用来存大的半数数据,大顶锥用来存小半数数据(小顶堆中的元素都大于等于大顶堆中的元素)
  • 当数目为偶数的时候,将这个值插入大顶堆中,再将大顶堆中根节点(即最大值)插入到小顶堆中
  • 当数目为奇数的时候,将这个值插入小顶堆中,再将小顶堆中根节点(即最小值)插入到大顶堆中
  • 取中位数的时候,如果当前个数为偶数,显然是取小顶堆和大顶堆根结点的平均值;如果当前个数为奇数,显然是取小顶堆的根节点
# -*- coding:utf-8 -*-
#导入优先队列算法
from heapq import *
#在heapq中只有小顶锥,大顶锥则用取反实现
class Solution:
    def __init__(self):
        #创建一个堆,可以使用list来初始化为 []
        self.heaps = [], []
        self.count = 0
    def Insert(self, num):
        # write code here
        small, large = self.heaps
        if self.count % 2 == 0:
            #heapq.heappush(heap, item)
            #将 item 的值加入 heap 中,保持堆的不变性。
            #heapq.heappushpop(heap, item)
            #将 item 放入堆中,然后弹出并返回 heap 的最小元素。
            """
            如果是偶数:
            先将取反放入大顶锥
            再将大顶锥的追锥顶pop出,并取反放入小顶锥
           (由于大顶锥用来存小半数数据,且大顶锥是小顶锥取反实现,
            因此大顶锥里面的数全为原始数据的相反数)
            """
            #-num的负号表示大顶锥里面的数全为原始数据的相反数
            #-heappushpop的负号表示小顶锥里面的数全为原始数据(即相反数的相反数)
            heappush(small, -heappushpop(large, -num))
        else:
            """
            如果是奇数:
            先将放入小顶锥
            再将大顶锥的追锥顶pop出,并取反放入大顶锥
            """
            heappush(large, -heappushpop(small, num))
        self.count+=1
    def GetMedian(self,ss):
        # write code here
        small,large = self.heaps
        if self.count % 2 != 0:
            return float(small[0])
        return (small[0] - large[0]) / 2.0

方法二

# -*- coding:utf-8 -*-
class Solution:
    def __init__(self):
        #用来记录数据流的个数
        self.count = 0
        self.li = []
    def Insert(self, num):
        # write code here
        self.li.append(num)
        self.li.sort()
        self.count+=1
    #题目有问题,缺少一个参数。需要自己加上
    def GetMedian(self,AA):
        # write code here
        if self.count % 2 == 0:
            #牛客网python版本为2+,被除数需要写成小数形式
            #python2    5/2 = 2
            #python3    5/2 = 2.5
            return (self.li[self.count//2-1]+self.li[self.count//2])/2.0
        else:
            return self.li[self.count//2]

66.构建乘积数组

在这里插入图片描述

class Solution:
    def multiply(self, A):
        # write code here
        if A==None:
            B = A
        else:
            B = [1] * len(A)
            for i in range(len(A)):
                for j in range(len(A)):
                    if i != j:
                        B[i] = B[i] * A[j]
        return  B

已参考多种思路

42 连续子数组的最大和

在这里插入图片描述

# -*- coding:utf-8 -*-

"""
思路1:
’最大连续子序列的和‘的第一个元素可能是原始序列中的任何一个元素
因此,第一次计算首元素为array[0]的最大连续和
     第二次计算首元素为array[1]的最大连续和
     ...
     最终得到’最大连续子序列的和‘
"""
class Solution:
    def FindGreatestSumOfSubArray(self, array):
        # write code here
        #最大连续子序列和不能初始化为0,
        #因为array的元素可能全小于0
        maxsum  = array[0]
        for i in range(len(array)):
            #下列两个参数初始化为array[i]即可,相应的i改为i+1
                #当前序列最大和
                tin_li_maxsum = array[i]
                #当前序列和
                tin_li_sum = array[i]
                for j in range(i+1,len(array)):
                    tin_li_sum = tin_li_sum + array[j]
                    if tin_li_sum > tin_li_maxsum:
                        tin_li_maxsum = tin_li_sum
                if tin_li_maxsum > maxsum:
                    maxsum = tin_li_maxsum
        return maxsum
"""
思路2:
	动态规划
"""
class Solution(object):
    def maxSubArray(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        dp = [0 for _ in range(len(nums))]
        #初始化
        dp[0] = nums[0]
        #定义循环
        for i in range(1,len(nums)):
            #状态转移
            if dp[i-1]>0:
                dp[i] = dp[i-1] + nums[i]
            else:
                dp[i] = nums[i]
        return max(dp)

45:把数组排成最小的数

在这里插入图片描述

"""
思路:
本题的最终目的就是将数组中的元素按照拼接顺序排好序,
因此,可以看成一道排序题,只不过需要自己定义排序规则
两个步骤:
1.排序(我选择的是比较简单的冒泡排序)
2.排序规则定义
    先将元素转化为字符串,然后拼接,比较拼接之后的大小。
    最后再决定num1,num2是否调换位置。
        str1 = str(num1) + str(num2)
        str2 = str(num2) + str(num1)
        if str1 >= str2:
            return str2
        else:
            return str1
"""
# -*- coding:utf-8 -*-
class Solution:
    def PrintMinNumber(self, numbers):
        # write code here
        """冒泡排序"""
        if not numbers:
            return ""
        n = len(numbers)
        count = 0
        while n > 0:
            for i in range(n - 1):
                if self.compare(numbers[i], numbers[i + 1]):
                    numbers[i], numbers[i + 1] = numbers[i + 1], numbers[i]
                    count += 1
            n -= 1
            #如果count==0,则说明此序列是已经排好序的序列
            if count == 0:
                break
        num_str = ""
        for i in numbers:
            num_str = num_str + str(i)
        return int(num_str)

    def compare(self, num1, num2):
        str1 = str(num1) + str(num2)
        str2 = str(num2) + str(num1)
        if str1 >= str2:
            return True
        else:
            return False

49:丑数

在这里插入图片描述

"""
思路:
创建一个数组uglyNumber,里面是拍好序的丑数
数组里面最大的丑数为uglyNumber[-1],该丑数肯定是前面某一个丑数乘以2、3或者5的结果,
所以我们首先考虑把已有的每个丑数分别乘以2、3、5,
乘以2、3、5之后,用count2、count3、count5记录首大于uglyNumber[-1]的数的下标
然后取uglyNumber[count2]*2,uglyNumber[count3]*3,uglyNumber[count5]*5这三者之中的最小值
最后一进行uglyNumber.append
"""
# -*- coding:utf-8 -*-
class Solution:
    def GetUglyNumber_Solution(self, index):
        # write code here
        if index <= 0:
            return 0
        uglyNumber = [1]
        count2 = 0
        count3 = 0
        count5 = 0
        while len(uglyNumber) < index:
            while uglyNumber[count2]*2 <= uglyNumber[-1]:
                count2 += 1
            while uglyNumber[count3]*3 <= uglyNumber[-1]:
                count3 += 1
            while uglyNumber[count5]*5<= uglyNumber[-1]:
                count5 += 1
            uglyNumber.append(min(uglyNumber[count2]*2,uglyNumber[count3]*3,uglyNumber[count5]*5))
        return uglyNumber[index-1]

50:第一个只出现一次的字符

在这里插入图片描述

"""
思路1:
    从头遍历,每个元素都与后面的所有元素进行对比,
    如果在后面没有发现重复的字符,则该字符就是只出现一次的字符。
    这种思路的时间复杂度是O(n**2)
    
思路2:
    创建一个哈希表(字典),键为元素,值为元素出现的次数,
    第一遍遍历之后得到每个元素出现的次数
    第二遍从头开始遍历,看元素在字典中对应的值是否等于1
    这种思路的时间复杂度是O(n),但是需要辅助空间
"""
# -*- coding:utf-8 -*-
class Solution:
    def FirstNotRepeatingChar(self, s):
        # write code here
        if not s:
            return -1
        dic = {}
        for i in s:
            if i not in dic:
                dic[i] = 1
            else:
                dic[i] += 1
        for i in range(len(s)):
            if dic[s[i]] == 1:
                return i

51:数组中的逆序对

注:本题在牛客网上无法通过,但是思路没有问题
在这里插入图片描述
解释一下思路2:
已知数组[7,5,6,4]
归并排序的实现步骤如下图所示:
在这里插入图片描述

  1. 在第一对长度为1的子数组{7}、{5}中,7大于5,因此(7,5)组成一个逆序对。同样,在第二对长度为1的子数组{6}、4}中,也有逆序对(6,4)
  2. 接下来我们统计两个长度为2的子数组之间的逆序对。
    首先比较left[0]和right[0]的大小,并将小的弹出。
    如果right[0]<left[0],则说明存在len(left)个逆序对,
    如果right[0]>left[0],则说明不存在逆序对
    重复上面的过程,直至left、right都为空
    在这里插入图片描述
"""
思路1:
    借用冒泡排序的思想----复杂度O(n**2)
    逐个比较该数字和它后面的数字的大小。如果后面的数字比它小,则这两个数字就组成一个逆序对。
    若构成一个逆序对,则计数器count+1
    由于每个数字都要和O(n)个数字进行比较,因此这种算法的时间复杂度是O(n2)
    代码附在最后面
思路2:
    借用归并排序的思想----复杂度O(nlog2n)
"""
# -*- coding:utf-8 -*-
class Solution:
	#思路二
    def __init__(self):
        self.count = 0

    def InversePairs(self, data):
        # write code here
        data = self.merge_sort(data)
        return self.count%1000000007

    def merge_sort(self, alist):
        """归并排序"""
        n = len(alist)
        if n <= 1:
            return alist
        mid = n // 2
        # left 归并排序后形成的新的有序列表
        left = self.merge_sort(alist[:mid])
        # right 归并排序后形成的新的有序列表
        right = self.merge_sort(alist[mid:])
        # 将两个有序的列表合并
        result = []
        while left and right:
            if left[0] < right[0]:
                result.append(left.pop(0))
            else:
                self.count += len(left)
                result.append(right.pop(0))
        result += left
        result += right
        return result

    """
    	#思路一
        def InversePairs(self, data):
            # write code here
            #冒泡排序
            n = len(data)
            count = 0
            while n > 0:
                for i in range(0,n-1):
                    if data[i] > data[i+1]:
                        data[i],data[i+1] =data[i+1],data[i]
                        count += 1
                n -= 1
                if count == 0:
                    break
            return count
    """

52:两个链表的第一个公共节点

在这里插入图片描述

"""
思路1:
    时间复杂度O(m+n),空间复杂度O(m+nm、n分别为两个链表的长度
    1.建立两个辅助栈,
    2.同时从两个栈顶弹出元素,如果存在公共节点,则总存在i使得弹出的第i个节点相同,
      第i+1个节点不同,
    3.那么,第i个节点即为第一个公共节点
    
思路2:
    时间复杂度O(m+n),m、n分别为两个链表的长度
    1.先遍历两个链表,计算两个链表的长度。
    2.让长的链表先走“两个链表之差”步
    3.然后,再让两个链表同时走,第一个相同的节点即为第一个公共节点
"""
# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    #思路二:
    def FindFirstCommonNode(self, pHead1, pHead2):
        # write code here
        len1,len2 = 0,0
        #p1,p2 = pHead1,pHead2
        p1 = pHead1
        p2 = pHead2
        while p1:
            p1 = p1.next
            len1 += 1
        while p2:
            p2 = p2.next
            len2 += 1
        if len1 > len2:
            for i in range(len1-len2):
                pHead1 = pHead1.next
        else:
            for i in range(len2-len1):
                pHead2 = pHead2.next
        while pHead1 != pHead2:
            pHead1 = pHead1.next
            pHead2 = pHead2.next
        return pHead1

53:数字在排序数组中出现的次数

在这里插入图片描述

"""
思路1-二分法    复杂度O(n)
    1.先用二分法找到某一个k出现的位置
    2.然后再向两边检测两边的元素时候等于k
    思路2-改进的二分法    复杂度O(logn)
    1.找出第一个k出现的位置
        1.1如果中间数比k小,则第一个k出现在右半段
        1.2如果中间数比k大,则第一个k出现在左半段
        1.3如果中间数等于k,则向左左搜索,找到第一个k的位置
    2.找出最后一个k出现的位置
        同上
    3.作差
思路3    复杂度O(logn)
    1.因为数组中的数为整数,因此引入两个数k-0.5,k+0.5
    2.查找k-0.5,k+0.5应该插入的位置,然后作差
"""
# -*- coding:utf-8 -*-
class Solution:
    def GetNumberOfK(self, data, k):
        # write code here
        #思路三
        return self.Serch(data,k+0.5)-self.Serch(data,k-0.5)
    def Serch(self,data,k):
        first = 0
        last = len(data)-1
        while first <= last:
            mid = (first+last) // 2
            if data[mid] < k:
                first = mid +1
            else:
                last = mid - 1
        return first

55.1:二叉树的深度

在这里插入图片描述

"""
思路:
    1.如果根节点只有左子树,那么树的深度应该是左子树的深度+1
    2.如果根节点只有右子树,那么树的深度应该是右子树的深度+1
    3.如果根节点既有右子树又有左子树,那么树的深度应该是深者的深度+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 not pRoot:
            return 0
        left = self.TreeDepth(pRoot.left)
        right = self.TreeDepth(pRoot.right)
        if left > right:
            return left + 1
        else:
            return right + 1

55.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 IsBalanced_Solution(self, pRoot):
        # write code here
        if self.TreeDepth(pRoot) != -1:
            return True
        return False
    def TreeDepth(self,root):
        if not root:
            return 0
        left = self.TreeDepth(root.left)
        right = self.TreeDepth(root.right)
        if abs(left-right)<=1 and left!=-1 and right!=-1:
            return max(left,right)+1
        else:
            return -1

56:数组中只出现一次的两个数字

在这里插入图片描述

"""
思路一    时间复杂度O(n)空间复杂度O(n)
    这也是最容易想到的思路,新建一个字典,用于存储数组中出现的元素,以及对应的出现次数。
    找出值为1的元素对应的键
    
思路二    异或法    时间复杂度O(n)空间复杂度O(1)
    两个相同数字异或=0,两个不同数字异或=1。一个数和0异或还是它本身。
    假设:一个数组中只有一个数出现一个,其他的数都出现两次。
        那么对改组的数进行异或,最终结果是出现次数为一次的数
    依照这个思路,我们来看仅有两个数(我们假设是A、B)出现一次的数组。
    我们首先还是先异或,最终肯定是A、B异或的结果,
    这个结果的二进制中的1,表现的是A和B的不同的位。
    我们就取第一个1所在的位数,假设是第n位,接着把原数组分成两类,分类标准是第n位是否为1。
    如此,相同的数肯定在一个类,因为相同数字所有位都相同,
    而不同的数,肯定不在一类。
    然后把这两个类按照最开始的思路,依次异或,剩余的两个结果就是这两个只出现一次的数字。
    
    在下面的程序中并没有真正的把原数组分为两组,
    而是用a去异或数组中第n位等于1的数
    用b去异或数组中第n位不等于1的数
    a、b即为所求
"""
# -*- coding:utf-8 -*-
class Solution:
    # 返回[a,b] 其中ab是出现一次的两个数字
    def FindNumsAppearOnce(self, array):
        # write code here
        if not array:
            return []
        s1 = self.Xor(array)
        index = 0
        while not s1&1:
            s1 >>= 1
            index += 1
        a,b = 0,0
        for i in array:
            if self.isBit(i, index):
                a ^= i
            else:
                b ^= i
        return [a, b]
    def Xor(self,array):
        res = 0
        for i in array:
            res ^= i
        return res
    #判断数i的从低到高的第index位是不是1
    def isBit(self,i,index):
        i = i >> index
        return i & 1

56扩展-数组中仅有一个数出现一次,其他数都出现三次

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

57.1 和为s的两个数字

在这里插入图片描述

"""
思路:
    假设:b>a且 a + b = s 、(a - m ) + (b + m) = s
        则:(a - m )(b + m)=ab - (b-a)m - m*m < ab;
        说明外层的乘积更小
    两个指针
    1.small指向数组开头,big指向数组结尾
    2.如果和小于sum,说明太小了,small右移寻找更大的数
    3.如果和大于sum,说明太大了,big左移寻找更小的数
    4.和相等,返回small、big
"""
# -*- coding:utf-8 -*-
class Solution:
    def FindNumbersWithSum(self, array, tsum):
        # write code here
        if len(array) < 2:
            return []
        small = 0
        big = len(array)-1
        while small < big:
            if array[small]+array[big] == tsum:
                return array[small],array[big]
            elif array[small]+array[big] < tsum:
                small += 1
            else:
                big -= 1
        return []

57.2 和为s的连续整数序列

在这里插入图片描述

"""
思路
    用两个数small和big分别表示序列的最小值和最大值。
    首先把small初始化为1,big初始化为2。
    如果从small到big的序列的和大于s,则可以从序列中去掉较小的值,也就是增大small的值。
    如果从small到big的序列的和小于s,则可以增大big,让这个序列包含更多的数字。
    因为这个序列至少要有两个数字,我们一直增加small到(1+s)/2为止。
"""
# -*- coding:utf-8 -*-
class Solution:
    def FindContinuousSequence(self, tsum):
        # write code here
        if tsum < 3:
            return []
        st = []
        small = 1
        big = 2
        while small < (tsum+1)//2:
            if self.Sum(small,big) == tsum:
                st.append([i for i in range(small,big+1)])
                small += 1
            elif self.Sum(small,big) < tsum:
                big += 1
            else:
                small += 1
        return st
    def Sum(self,small,big):
        return (small+big)*(big-small+1)//2

58.1 反转单词顺序列

在这里插入图片描述

"""
思路:
    以“ ”将s分隔开,并且对分开的每一部分进行逆序操作
    然后再以“ ”连接
"""
# -*- coding:utf-8 -*-
class Solution:
    def ReverseSentence(self, s):
        return " ".join(s.split(" ")[::-1])

58.2 左旋转字符串

在这里插入图片描述

"""
思路一:三次Reverse
    设计一个Reverse(s,m,n),该函数可以对字符串s中的第m-n个数进行反转。
    三次Reverse即可s=”abcXYZdef” k=3
    例:
        第一次Reverse(s,0,k-1)-->s=”cbaXYZdef”
        第二次Reverse(s,k,len(s)-1)-->s=”cbafedZYX”
        第三次Reverse(s,0,len(s)-1)-->s=”XYZdefabc”
思路二:先补后切
      s=”abcXYZdef” k=3
      l=len(s)
      1.补
      s+=s   -->   s=”abcXYZdefabcXYZdef”
      2.切
      s = s[k:l+k]
思路三:切片组合
      s=”abcXYZdef” k=3
      return s[k:]+s[:k]
"""
# -*- coding:utf-8 -*-
class Solution:
    def LeftRotateString(self, s, k):
        # write code here
        return s[k:]+s[:k]

61 打扑克中的顺子

在这里插入图片描述

"""
思路:
    1.先对numbers进行排序,并reverse
    2.在对numbers中非零的数计算两个数之间的差值(差值-1=允许插入的整数数量=”间隔“)
    3.比较零的数量,与第二部计算出来的的所有间隔之和
比如;
numbers=[5,7,0,4,8]
排序之后:numbers=[8,7,5,4,0]
间隔分别为(去掉numbers中的0)8-7-1=0,7-5-1=1,5-4-1=0
sum(间隔)=零的数量
return True
"""
# -*- coding:utf-8 -*-
class Solution:
    def IsContinuous(self, numbers):
        # write code here
        if not numbers:
            return False
        numbers.sort(reverse = True)
        #记录间隔之和
        index = 0
        #记录0的数量
        zero_index = 0
        for i in range(0,4):
            #之后两个数都不为0,且差值不为1的时候才计算”间隔“
            if numbers[i] and numbers[i+1] and numbers[i]-numbers[i+1]!=1:
                #出现相同的数字直接返回False
                if numbers[i]-numbers[i+1] == 0:
                    return False
                #间隔=numbers[i]-numbers[i+1])-1
                index+=((numbers[i]-numbers[i+1])-1)
            #一共四个王(0),numbers五个数,因此numbers[0]!=0,
            #因此,从numbers[1]开始判断0的数量即可
            elif numbers[i+1]==0:
                zero_index += 1
        if zero_index >= index:
            return True
        return False

62 圈中剩下的数字

在这里插入图片描述

"""
思路一
    将n个编号转换为一个循环链表
思路二
    每次删除数的下标是cur = (cur + m -1) % len(li)
"""
# -*- coding:utf-8 -*-
class Solution:
    def LastRemaining_Solution(self, n, m):
        # write code here
        if not m or not n:
            return -1
        cur = 0
        li=[i for i in range(n)]
        while len(li)>1:
            cur = (cur + m -1) % len(li)
            li.pop(cur)
        return li[0]

未归类–来自牛客剑指offer

把二叉树打印成多行

在这里插入图片描述

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    # 返回二维列表[[1,2],[4,5]]
    def Print(self, pRoot):
        # write code here
        #用两个列表分别保存当前行节点和下一行节点
        current = [pRoot]
        next_layer = []
        result = []
        if pRoot is None:
            return result
        while current:
            for i in current:
                if i.left:
                    next_layer.append(i.left)
                if i.right:
                    next_layer.append(i.right)
            #每一层输出一行,重点在于下面的append([]),[]就表示了每层一行
            result.append([i.val for i in current])
            current,next_layer = next_layer,[]
        return result

按之字形顺序打印二叉树

在这里插入图片描述

# -*- 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
        current = [pRoot]
        next_layer = []
        result = []
        deep = 0
        if pRoot is None:
            return result
        while current:
            deep += 1
            for i in current:
                if i.left:
                    next_layer.append(i.left)
                if i.right:
                    next_layer.append(i.right)
            if deep % 2 != 0:
                result.append([i.val for i in current])
            else:
                #下面这两句话的含义是一样的,都是逆序遍历current[]
                #result.append([current[len(current)-i-1].val for i in range(len(current))])
                result.append([i.val for i in current[::-1]])
            current,next_layer = next_layer,[]
        return result

二叉搜索树的第k个结点

在这里插入图片描述
二叉搜索树
二叉搜索树也叫二叉排序树,它可以是一棵空树。它具有一下特点

  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 它的左右子树也分别为二叉搜索树
  • 没有键值(key)相等的节点
  • 二叉搜索树的中序遍历结果是排好序的

二叉搜索树如下所示:
在这里插入图片描述

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    # 返回对应节点TreeNode
    def __init__(self):
        self.li = []
        self.serch_flag = False
        self.node = None

    def KthNode(self, pRoot, k):
        # write code here
        if k <= 0 or  not pRoot:
            return None
        self.li = self.Tinorder(pRoot)
        if len(self.li) < k:
            return None
        self.node = self.Serch(pRoot, self.li[k - 1])
        return self.node
    #二叉搜索树的中序遍历结果是排序好的
    def Tinorder(self, pRoot):
        """中序遍历"""
        if not pRoot:
            return
        self.Tinorder(pRoot.left)
        self.li.append(pRoot.val)
        self.Tinorder(pRoot.right)
        return self.li

    def Serch(self, pRoot, num):
        """数值搜索"""
        if not pRoot:
            return
        if pRoot.val == num:
            self.node = pRoot
        self.Serch(pRoot.left,num)
        self.Serch(pRoot.right,num)
        return self.node

本题扩展–任意二叉树的第K个节点

思路:

  • 首先利用前序遍历将二叉树存入列表
  • 再对列表进行快速排序
  • 然后在寻找节点
# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    # 返回对应节点TreeNode
    def __init__(self):
        self.li = []
        #self.serch_flag = False
        self.node = None

    def KthNode(self, pRoot, k):
        # write code here
        if k <= 0 or  not pRoot:
            return None
        self.li = self.Perorder(pRoot)
        if len(self.li) < k:
            return None
        self.li = self.Quick_sort(self.li, 0, len(self.li) - 1)
        self.node = self.Serch(pRoot, self.li[k - 1])
        return self.node

    def Perorder(self, pRoot):
        """先序遍历"""
        if not pRoot:
            return
        self.li.append(pRoot.val)
        self.Perorder(pRoot.left)
        self.Perorder(pRoot.right)
        return self.li

    def Quick_sort(self, alist, first, last):
        """快速排序"""
        # 终止条件
        if first >= last:
            return alist
        mid_value = alist[first]
        low = first
        high = last
        while low < high:
            while low < high and alist[high] >= mid_value:
                high -= 1
            alist[low] = alist[high]
            while low < high and alist[low] < mid_value:
                low += 1
            alist[high] = alist[low]
        alist[low] = mid_value

        self.Quick_sort(alist, first, low - 1)

        self.Quick_sort(alist, low + 1, last)
        return alist
    def Serch(self, pRoot, num):
        """数值搜索"""
        if not pRoot:
            return
        if pRoot.val == num:
            #self.serch_flag = True
            self.node = pRoot

        self.Serch(pRoot.left,num)
        self.Serch(pRoot.right,num)
        return self.node

滑动窗口的最大值

在这里插入图片描述

# -*- coding:utf-8 -*-
class Solution:
    def maxInWindows(self, num, size):
        # write code here
        maxli = []
        #有三种情况可以直接返回:
        #num = none
        #size = 0
        #size > len(num)
        if not num or not size or size > len(num):
            return maxli
        for i in range(len(num)-size+1):
            maxli.append(max(num[i:size+i]))
        return maxli

跳台阶

在这里插入图片描述
题目分析
题目实际为一个斐波那契数列(只不过f(1)=1、f(2)=2))

  1. 假定第一次跳的是一阶,那么剩下的是n-1个台阶,跳法是f(n-1)
  2. 假定第一次跳的是2阶,那么剩下的是n-2个台阶,跳法是f(n-2)
  3. 由1).2)假设可以得出总跳法为: f(n) = f(n-1) + f(n-2)
# -*- coding:utf-8 -*-
class Solution:
    def jumpFloor(self, number):
        # write code here
        if number == 0:
            return
        a , b = 1 ,1
        while number > 0:
            a , b = b , a+b
            number-=1
        return a
        """
        if number == 0:
            return 0
        if number == 1:
            return 1
        if number == 2:
            return 2
        return self.jumpFloor(number-1) + self.jumpFloor(number-2)
        """

变态跳台阶

在这里插入图片描述
题目分析
由上一题“跳台阶”可以联想到:

  • 假定第一次跳的是一阶,那么剩下的是n-1个台阶,跳法是f(n-1)
  • 假定第一次跳的是2阶,那么剩下的是n-2个台阶,跳法是f(n-2)
  • 。。。。。。
  • 假定第一次跳的是n-1阶,那么剩下的是1个台阶,跳法是f(1) = 1
  • 假定第一次跳的是n阶,那么剩下的是0个台阶,跳法是f(0) = 0

因此可以得到:
f(n) = f(0) + f(1) + f(2) + f(3) + … + f(n-2)+ f(n-1)
又因为:
f(n-1) = f(0) + f(1) + f(2) + f(3) + … + f(n-2)
联立上面的两个式子可以得到:
f(n) = 2* f(n-1)、 f(1) = 1 、f(0) = 0

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

矩形覆盖

在这里插入图片描述
在这里插入图片描述
题目分析
题目实际为一个斐波那契数列(只不过f(1)=1、f(2)=2))

# -*- coding:utf-8 -*-
class Solution:
    def rectCover(self, number):
        # write code here
        if number == 0:
            return 0
        a,b = 1,1
        while number > 0:
            a,b = b,a+b
            number -= 1
        return a
        """
        if number == 0:
            return 0
        if number == 1:
            return 1
        if number == 2:
            return 2
        return self.rectCover(number-1) + self.rectCover(number-2)
        """
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值