剑指offer21-40题整理

21、输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)(难!!多做几次)
【思路】借用一个辅助的栈,遍历压栈顺序,先讲第一个放入栈中,这里是1,然后判断栈顶元素是不是出栈顺序的第一个元素,这里是4,很显然1≠4,所以我们继续压栈,直到相等以后开始出栈,出栈一个元素,则将出栈顺序向后移动一位,直到不相等,这样循环等压栈顺序遍历完成,如果辅助栈还不为空,说明弹出序列不是该栈的弹出顺序。

举例:

入栈1,2,3,4,5

出栈4,5,3,2,1

首先1入辅助栈,此时栈顶1≠4,继续入栈2

此时栈顶2≠4,继续入栈3

此时栈顶3≠4,继续入栈4

此时栈顶4=4,出栈4,弹出序列向后一位,此时为5,,辅助栈里面是1,2,3

此时栈顶3≠5,继续入栈5

此时栈顶5=5,出栈5,弹出序列向后一位,此时为3,,辅助栈里面是1,2,3

….

依次执行,最后辅助栈为空。如果不为空说明弹出序列不是该栈的弹出顺序。

class Solution:
    def IsPopOrder(self, pushV, popV):
        stack=[]
        for i in pushV:
            stack.append(i)
            ##用while不用if是因为if只做一次操作,但如果stack里面有1,2,3的话,
            ##只能用while,把它们全部都弹出
            while(len(stack)!=0 and stack[-1]==popV[0]):
                stack.pop()
                popV.pop(0)
            else:
                continue
        return len(stack)==0

22、从上往下打印出二叉树的每个节点,同层节点从左至右打印。(思路很重要哦!)
在这里插入图片描述

class Solution:
    # 返回从上到下每个节点值列表,例:[1,2,3]
    def PrintFromTopToBottom(self, root):
        queue = []
        result = []
        if root == None:
            return result
            ##读取根节点,保存到队列queue中,
        queue.append(root)
        ##如果队列里面有数,则继续
        while queue:
        ##将队列的头部pop出来,即先进先出
            node=queue.pop(0)
            ##添加到result结果中
            result.append(node.val)
            ##再判断左右节点是否存在,存在,则存于队列中,继续循环
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)
        return result

23、输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。(再练习!)
注意二叉搜索树的特性!!

class Solution:
    def VerifySquenceOfBST(self, sequence):
        l=len(sequence)
        if l==0:
            return False
        root=sequence[-1]
        for i in range(l):
            if sequence[i]>root:
                break    
        for j in range(i,l):
            if sequence[j]<root:
                return False
        ##非常重要,设置一个标志值,再进入递归    
        left=True
        ##i>0为了保证左子树存在,等于0 就只有右子树了
        if i>0:
            left=self.VerifySquenceOfBST(sequence[0:i])
        # 判断 右子树是否为二叉树
        right=True
        ##同样保证右子树的存在
        if i<l-1:
            right=self.VerifySquenceOfBST(sequence[i:-1])
        return left and right

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

小知识点:二叉树的深度优先遍历和广度优先遍历
https://blog.csdn.net/qq_33356414/article/details/88596861


class Solution:
    def pathSum(self, root: TreeNode, target: int) -> List[List[int]]:
        if not root: return []
        if not root.left and not root.right:
            if root.val == target: return [[root.val]]
        return [[root.val] + path for path in self.pathSum(root.left, target-root.val) + self.pathSum(root.right, target-root.val)]

25、输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)(巨难!!注意注意!)
在这里插入图片描述
注意!一定要注意链表之间的连接关系,要依次后移。画个图会很清楚。

# -*- 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):
        if not pHead:
            return None
        node=pHead
        while node:
            ##复制前面的节点和值
            p1=RandomListNode(node.label)
            p1.label=node.label
            p1.next=node.next
            p1.random=None
            node.next=p1
            node=p1.next
        ##重新指向头结点
        node=pHead
        while node:
            p1=node.next
            if node.random:
                p1.random=node.random.next //指向下一个 因为都复制了 random也不在之前位置
            node=p1.next
        node=pHead
        p1=p1Head=node.next
        node.next=p1.next
        node=node.next //这里注意要先运行依次node后移,为了下面循环终止在复制的节点,这样下一个node就是空可以正常退出循环
        while node:
             p1.next=node.next
             p1=p1.next
             node.next=p1.next
             node=node.next
        return p1Head

26、输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
二叉搜索树的特性是,左<根<右,所以他的中序遍历是有序的,先中序遍历(mid函数),再把中序遍历的结果的右结点指向下一个,下一个的左结点指向当前,实现双向链表的pre 和next。

class Solution:
    def treeToDoublyList(self, root: 'Node') -> 'Node':
        if not root:
            return None
        nodes = []
        # dfs mid traverse
        def dfs(root):
            if not root:
                return
            dfs(root.left)
            nodes.append(root)
            dfs(root.right)
        dfs(root)
        for i in range(len(nodes) - 1):
            nodes[i].right = nodes[i + 1]
            nodes[i + 1].left = nodes[i]
        nodes[0].left = nodes[-1]
        nodes[-1].right = nodes[0]
        return nodes[0]

27、输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
集合(set)是一个无序的不重复元素序列。
参考这个视频,讲解的非常清晰 其他题也都有
https://www.bilibili.com/video/BV1cK4y1s7tN?spm_id_from=333.337.search-card.all.click
可以使用大括号 { } 或者 set() 函数创建集合,注意:创建一个空集合必须用 set() 而不是 { },因为 { } 是用来创建一个空字典。

class Solution:
    def permutation(self, s: str) -> List[str]:
        n=len(s)
        queue=list(s)
        res=[]
        def fix(x):
            if x==n-1:
                res.append(''.join(queue))
                return 
            seen=set()
            for i in range(x,n):
                if queue[i] in seen: continue
                seen.add(queue[i])
                queue[x],queue[i]=queue[i],queue[x]
                fix(x+1)
                queue[x],queue[i]=queue[i],queue[x]
        fix(0)
        return res

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

class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        count=0
        nums.sort()
        mid=nums[int(len(nums)/2)] // int()是向下取整
        for i in range(len(nums)):
            if nums[i]==mid:
                count=count+1
        if count>int(len(nums)/2):
            return mid
        else:return 0

方法二:(复杂度低,O(n):

class Solution:
    """第二种,假设有这个数字,那么它的数量一定比其它所有数字之和还要多,按照这个思路得出num,然后验证
    """
    def MoreThanHalfNum_Solution(self, numbers):
        if not numbers:
            return 0
        num = numbers[0]
        count = 1
        for i in range(1, len(numbers)):
            if numbers[i] == num:
                count += 1
            else:
                count -= 1
            if count == 0:
                num = numbers[i]
                count = 1
        ##上面得到了次数最多的数字,(由于出现次数大于一半,所以再怎样最后也是它)再计算他出现的总次数。        
        count = 0
        for i in numbers:
            if i == num:
               count += 1
        return num if count > len(numbers) / 2.0 else 0



29、输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
几种排序算法Python实现
https://blog.csdn.net/weixin_41571493/article/details/81875088
法1:冒泡排序,再取前k个(时间太久不通过)不行
只有快排可以满足时间复杂度

~~class Solution:
    def GetLeastNumbers_Solution(self, tinput, k):
        if not tinput or len(tinput)<k:
           return []
        l=len(tinput)
        for  i in range(l):
            for j in range(0,l-i-1):
                if tinput[j]>tinput[j+1]:
                    tinput[j],tinput[j+1]=tinput[j+1],tinput[j]
        return tinput[:k]~~ 

法2:快排的思想,(用partition分割开,左边是小于基准的,右边是大于的,当基准的index==k,前面的输出即可。)
快排的理解看这里
https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof/solution/jian-zhi-offer-40-zui-xiao-de-k-ge-shu-j-9yze/

class Solution:
    def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
        if k>=len(arr):return arr
        def quick(l,r):
            i,j=l,r
            while i<j:
                while i<j and arr[j]>=arr[l]:
                    j-=1
                while i<j and arr[i]<=arr[l]:
                    i+=1
                arr[i],arr[j]=arr[j],arr[i]
            arr[l],arr[i]=arr[i],arr[l]
            if i>k:
                quick(l,i-1)
            if i<k:
                quick(i+1,r)
            return arr[:k]
        return quick(0,len(arr)-1)  

法3:堆排序(高级排序,多看看哦)看完堆排序算法,再来写

在这里插入代码片

30、连续子数组最大和
有负数的情况,若小于0,则把当前置为计算初始值,每次和最大值比较并替换。
并没有忽略掉全部是负数的情况,若全部是负数,则第二次循环,piv的值就会变成当前数组的值,从而和array[0]进行比较,得到最大的元素值。(很厉害!)

class Solution:
    def FindGreatestSumOfSubArray(self, array):
        max=array[0]
        piv=0
        for i in range(len(array)):
            if piv<0:
                piv=array[i]
            else:
                piv=piv+array[i]
            if piv>max:
                max=piv
        return max

31、整数中1出现的次数(从1到n整数中1出现的次数)
求出1到13的整数中 包含1的数字有1、10、11、12、13因此共出现6次
参考这里的规律推算:
知道规律之后写代码:https://leetcode-cn.com/problems/1nzheng-shu-zhong-1chu-xian-de-ci-shu-lcof/solution/dong-hua-mo-ni-wo-tai-xi-huan-zhe-ge-ti-vxzwc/
python中 %是去余数 // 是去掉末尾
1024%10=4
1024//10=102

class Solution:
    def countDigitOne(self, n: int) -> int:
        high,low=n,0
        count=0
        cur=0
        nums=1
        // 一下子两个判断保证高位不是空(遍历完成)high=0的时候必须cur部位0 因为不存在0100这样的数字
        while high!=0 or cur!=0:
            cur=high%10
            high=high//10
            if cur==1:
                count+=nums*high+low+1
            elif cur==0:
                count+=nums*high
            else:
                count+=(high + 1) * nums
            low=nums*cur+low
            nums*=10
        return count

32、
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。(很难!!)
在这里插入图片描述

class Solution:
    def PrintMinNumber(self, numbers):
        if not numbers:
            return ""
        l=len(numbers)
        for i in range(l):
            for j in range(0,l-i-1):
                if int(str(numbers[j])+str(numbers[j+1]))>int(str(numbers[j+1])+str(numbers[j])):
                    numbers[j],numbers[j+1]=numbers[j+1],numbers[j]
        return int(''.join(map(str,numbers)))

33、把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。****
参考这个https://www.bilibili.com/video/BV1o541177He/?spm_id_from=333.788

class Solution:
    def nthUglyNumber(self, n: int) -> int:
        if n <= 6:
            return n 
        res = [1]
        t2 = t3 = t5 = 0
        for i in range(1, n):
            # 每次新生成的丑数是由三个数字的最小值构成的
            temp = min(res[t2] * 2, res[t3] * 3, res[t5] * 5)
            res.append(temp) 
            # 如果其中一个序列这次被选中,下一次从这个队列生成出用来比较的丑数就在这一次队列向后移动一位
            if temp == res[t2] * 2:
                t2 += 1
            if temp == res[t3] * 3:
                t3 += 1
            if temp == res[t5] * 5:
                t5 += 1 
        return res[-1]

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

class Solution:
    def firstUniqChar(self, s: str) -> str:
        a=list(s)
        map={}
        for i in a:
            if i in map:
                map[i]+=1
            else:
                map[i]=1
        for i in map:
            if map[i]==1:
               return i
        return " "
==================
一种优化的解法
class Solution:
    def firstUniqChar(self, s: str) -> str:
        a=list(s)
        map={}
        for i in a:
        ## 如果不在map 则true 如果在map 则false
            map[i] =not i in map
        for k,v in map.items():
            if v:return k
        return " "

35、在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007 (归并排序思想)
b站讲解

class Solution:
    def reversePairs(self, nums: List[int]) -> int:
        def mergeSort(L,R):
            if L>=R: return 0
            m=L+(R-L)//2
            cnt= mergeSort(L,m)+mergeSort(m+1,R)
            i,j=L,m+1
            pos=L
            while i<=m and j<=R:
                if nums[i]<=nums[j]:
                    tmp[pos]=nums[i]
                    i+=1
                elif nums[i]>nums[j]:
                    tmp[pos]=nums[j]
                    j+=1
                    cnt+=(m-i+1)
                pos+=1
            for k in range(i,m+1):
                tmp[pos]=nums[k]
                pos+=1
            for l in range(j,R+1):
                tmp[pos]=nums[l]
                pos+=1
            nums[L:R+1]=tmp[L:R+1]
            return cnt
        n=len(nums)
        tmp=[0]*n
        return mergeSort(0,n-1)
        

36、输入两个链表,找出它们的第一个公共结点。
b站视频
参考这个图
因为交换之后 数量之和就一样了

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

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        pa,pb=headA,headB
        while pa != pb:
            if pa:
                pa=pa.next 
            else:
                pa=headB
            if pb:
                pb=pb.next 
            else:
                pb=headA
        return pa

37、统计一个数字在排序数组中出现的次数。
由于数组有序,所以使用二分查找方法定位k的第一次出现位置和最后一次出现位置
第一次出现的位置,判断mid前面是否有相同数字,还有则把右指针指向mid-1,没有则first=mid
最后出现的位置,判断mid后面时候有相同的数字,还有则把左边的指向mid+1,没有则是最后的位置。
b站题解二分法查找 不要忽略排序数组这个重要条件

class Solution:
    def search(self, nums: List[int], target: int) -> int:
    ## 这个函数用来找目标x的右边界
        def find(x):
            L,R=0,len(nums)-1
            while L<=R:
                m=L+(R-L)//2
                if nums[m]<=x:
                   L=m+1
                else:
                   R=m-1
            return L
        return find(target)-find(target-1)

        

38、输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
求树的深度,可以从层次遍历出发考虑
层次遍历可以使用队列完成,也可以使用递归完成,所以有两种方法

<法一:递归>

  • 如果该树只有一个结点,它的深度为1.如果根节点只有左子树没有右子树,
  • 那么树的深度为左子树的深度加1;同样,如果只有右子树没有左子树,
  • 那么树的深度为右子树的深度加1。如果既有左子树也有右子树,
  • 那该树的深度就是左子树和右子树的最大值加1.
class Solution:
    def TreeDepth(self, pRoot):
        if not pRoot:
            return 0     
        left=self.TreeDepth(pRoot.left)
        right=self.TreeDepth(pRoot.right)
        return max(left,right)+1

<法二>:队列实现层序遍历:

class Solution:
    def levelOrder(self, root):
        # 存储最后层次遍历的结果
        res = []
        count = 0
        if root is None:
            return count
        # 模拟一个队列储存节点
        q = []
        # 首先将根节点入队
        q.append(root)
        # 列表为空时,循环终止
        while len(q) != 0:
            # 使用列表存储同层节点
            tmp = []
            # 记录同层节点的个数
            length = len(q)
            for i in range(length):
                # 将同层节点依次出队
                r = q.pop(0)
                if r.left is not None:
                    # 非空左孩子入队
                    q.append(r.left)
                if r.right is not None:
                    # 非空右孩子入队
                    q.append(r.right)
                tmp.append(r.val)
            if tmp:
                count += 1  # 统计层数
            res.append(tmp)
        return count
    def TreeDepth(self, pRoot):
        # 当树为空直接返回0
        if pRoot is None:
            return 0
        count = self.levelOrder(pRoot)
        return count

39、输入一棵二叉树,判断该二叉树是否是平衡二叉树。
平衡二叉树即平衡二叉查找树(左<根<右),左右节点高度差不能超过1。
利用上一节计算二叉树深度的方法,自顶向下,对于每个节点,都计算一下左子树以及右子树的差的绝对值,即每个节点都判断一下。
算法复杂度为O(N*2)

class Solution:
    def IsBalanced_Solution(self, pRoot):
        if pRoot is None:
            return True
        return abs(self.depth(pRoot.left)-self.depth(pRoot.right))<=1 and self.IsBalanced_Solution(pRoot.left) and self.IsBalanced_Solution(pRoot.right)               
    def depth(self,pRoot):
        if pRoot is None:
            return 0
        left=self.depth(pRoot.left)
        right=self.depth(pRoot.right)
        return max(left,right)+1
        

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

不能用暴力解法 用二进制的异或法

class Solution:
    def singleNumbers(self, nums: List[int]) -> List[int]:
        x=0
        #以下得到最后两个只出现一次数字的异或后的结果
        for num in nums:
            x=x^num
        # 以下可以判断出异或后结果 最低位的1在哪里 从而知道两个数字的二进制的不同在哪里(异或结果是1,那一定是一个0一个1 ) 从0001 到0010 到0100 依次与 如果结果是1 则说明那一位是不同的
        y=1
        while y&x==0:
            y<<=1
        # 得知不同位数后,可以分组 再次异或 得到最终不同的两个数字
        m,n=0,0
        for num in nums:
            if y&num==0:
                m^=num
            else:
                n^=num
        return [m,n]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值