剑指offer刷题0402

本文深入讲解了四个经典算法题:丑数、字符串排列、寻找两个链表的第一个公共节点及二叉树深度的求解方法。提供了清晰的思路解析与Python代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

刷题四道,但是还是不牢固,需要再写。

参考与感谢:https://codingcat.cn/article/30

丑数

字符串的排列

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

二叉树的深度

 

丑数 

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

思路:

除了1之外,丑数都是由某个丑数2或者3或者*5得到的。方法1,从1开始去验证每个数是否是丑数,结束条件为已记录的丑数个数为目标值

方法2: 用生成的方法,因为,丑数都是由某个丑数2或者3或者*5得到的生成的,所以可以保存之前已求出的丑数,用来生成后续的丑数。关键是如何去生成与排序后续的丑数
**解决方式:用一个数组,按大小存放当前已经找到的丑数。假设当前数组中最大的丑数是M,要找一个新的丑数,则是根据列表中当前的数x去乘以2,3,5,生成出来的,去保存生成的结果。假设数组中已经有若干个丑数排好序后存放在数组中,并且把己有最大的丑数记做M。我们首先考虑把已有的每个丑数乘以 2。在乘以 2 的时钝能得到若干个小于或等于 M 的结果。由于是按照顺序生成的,小于或者等于 M 肯定己经在数组中了,我们不需再次考虑:还会得到若干个大于 M 的结果,但我们只需要第一个大于 M 的结果,因为我们希望丑数是按从小到大的顺序生成的,其他更大的结果以后再说。我们把得到的第一个乘以 2 后大于 M 的结果记为 M2,同样,我们把已有的每一个丑数乘以 3 和 5,能得到第一个大于 M 的结果 M3 和 M,那么下一个丑数应该是 M2、M3 和 M5 这 3 个数的最小者。

前面分析的时候,提到把已有的每个丑数分别都乘以 2、3 和 5。事实上这不是必须的,因为已有的丑数是按顺序存放在数组中的。对乘以 2 而言, 肯定存在某一个丑数 T2,排在它之前的每一个丑数乘以 2 得到的结果都会小于已有最大的丑数,在它之后的每一个丑数乘以 2 得到的结果都会太大。我们只需记下这个丑数的位置, 同时每次生成新的丑数的时候,去更新这个 T2。对乘以 3 和 5 而言, 也存在着同样的 T3 和 T5。**

 

# -*- coding:utf-8 -*-
class Solution:
    def GetUglyNumber_Solution(self, index):
        # write code here
        if index <7:     #如果不加判断<7的情况,牛客测试不能完全通过,不知为啥。感觉只用判断是佛为0即可
            return index
        numbers= [1,]
        p2 = 0
        p3 = 0
        p5 = 0
        MUglyIndex=1
        while len(numbers) < index:
            min_M=min(numbers[p2]*2,numbers[p3]*3,numbers[p5]*5)
            numbers.append(min_M)
            #numbers[nextUglyIndex]=min_M
            while(numbers[p2]*2<=numbers[MUglyIndex]): #此处记录位置
                p2 +=1
            while(numbers[p3]*3<=numbers[MUglyIndex]):
                p3 +=1
            while(numbers[p5]*5<=numbers[MUglyIndex]):
                p5 +=1
            MUglyIndex+=1
        print(len(numbers))
        return numbers[-1]
    

 

字符串的排列

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

思路:
这相当于一个排列组合的问题。以abc为例,思考一下如果人工做这个题的思路。首先是固定首位为a,那么剩下bc,第二位若排b,第三位则排c,否则二c三b,所以可以排出abc,acb。然后首位为b,剩下ac,可以排出bac,bca。然后首位为c,剩下ab,可以排出cab,cba。

整体思路应该是,先固定首位,然后余下的部分做排列;余下部分固定首位,再余下做排列……是一种递归的思路。

思路还可以,实现的时候要想清楚细节。

在具体实现中,固定首位取余下,代码方面可以这样做:字符串的首位依次与后边交换,比如说 abc,首位a先与它自己a换,即abc,然后递归bc;首位a再与b换,即bac,然后递归ac(递归后,记得再换回去);首位a再与c换,得到cba,然后递归ba……

因为题目要求字典序打印,而且输入中会有重复字符,所以对最终得到的list要做去重,然后字典序排序。

# -*- coding:utf-8 -*-
class Solution:
    def Permutation(self, ss):
        if len(ss) <=0:
            return []
        res = list()
        self.perm(ss,res,'')
        uniq = list(set(res))
        return sorted(uniq)
    def perm(self,ss,res,path):
        if ss=='':
            res.append(path)
        else:
            for i in range(len(ss)):
                self.perm(ss[:i]+ss[i+1:],res,path+ss[i])

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

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

思路:利用链表共节点的特性:

#先两个各遍历一遍,得到两个链表的长度。计算长度差,让长的先走一段,

#走到两个链表余下长度相同时,两个链表同时继续走,并比较元素,就能找到第一个相同元素。

 

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
#链表如果共节点,则会从第一个公共结点开始,之后的结点都是公共结点。先两个各遍历一遍,得到两个链表的长度。计算长度差,让长的先走一段,
#走到两个链表余下长度相同时,两个链表同时继续走,并比较元素,就能找到第一个相同元素。
class Solution:
    def FindFirstCommonNode(self, pHead1, pHead2):
        if not pHead1 or not pHead2:
            return None
        len1 = 0
        len2 = 0
        ptr1 = pHead1
        ptr2 = pHead2
        while ptr1:
            len1 += 1
            ptr1 = ptr1.next
        while ptr2:
            len2 += 1
            ptr2 = ptr2.next
        ptr1 = pHead1
        ptr2 = pHead2
        while len1 > len2:
            ptr1 = ptr1.next
            len1 -= 1
        while len2 > len1:
            ptr2 = ptr2.next
            len2 -= 1
        while len1 > 0:
            if id(ptr1) == id(ptr2):
                return ptr1
            else:
                ptr1 = ptr1.next
                ptr2 = ptr2.next
                len1 -= 1
#这个代码简洁多了
# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
  
class Solution:
    def FindFirstCommonNode(self, head1, head2):
        # write code here
        list1 = []
        list2 = []
        node1 = head1
        node2 = head2
        while node1:
            list1.append(node1.val)
            node1 = node1.next
        while node2:
            if node2.val in list1:
                return node2
            else:
                node2 = node2.next

 

 

二叉树的深度 

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

思路: 递归记录。树的深度为当前深度加左右子树深度的较大值。具体分析:树只有1个节点,其深度为1;如果根节点只有左子树,则深度为左子树的深度+1;如果只有右子树,则深度为右子树深度+1;如果左右都有,则深度为左右的深度的较大值+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)
        
        return max(left,right)+1

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值