剑指offer 50~68-II题 python题解记录

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

法一:我以为我想的是个牛气方法,结果😅了
虽然代码行数很少,但每次count都会遍历一遍s

class Solution:
    def firstUniqChar(self, s: str) -> str:
        for i in s:
            if s.count(i)==1:
                return i
        return " "

法二:正解是用字典记录下每个字符出现的次数,再返回第一个只出现一次的字符

class Solution:
    def firstUniqChar(self, s: str) -> str:
        dic={}
        for i in s:
            if i not in dic:
                dic[i]=1
            else:
                dic[i]+=1
        for k in dic:
            if dic[k]==1:
                return k
        return " "

简洁版:
太厉害了,如果字符已经在字典中了,那么其值为0,否则为1

class Solution:
    def firstUniqChar(self, s: str) -> str:
        dic={}
        for i in s:
            dic[i]=not i in dic
        for k in dic:
            if dic[k]==1:
                return k
        return " "

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

法一:自己想的笨方法
将两个链表存储下来,然后从后往前比较,遇到不相等的节点时,返回该节点的前一个节点即可。需要注意的是,当两个链表从第一个节点开始相交时,最后需要多一个判断。
不要学这种方法,当链表长度无穷时,该方法必然占用极大空间。。还是学正解吧。。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        NodeA, NodeB = [],[] ## 用于存储AB两个链表的list
        ind = headA
        while ind!=None:
            NodeA.append(ind)
            ind = ind.next
        ind = headB
        while ind!=None:
            NodeB.append(ind)
            ind = ind.next
        if min(len(NodeA),len(NodeB)) == 0: # 当链表为空时,直接返回null
            return None
        for i in range(1,min(len(NodeA),len(NodeB))+1): # 开始从后往前判断
            if NodeA[-i]!= NodeB[-i]:
                return NodeA[-i].next
        # 对特殊情况做处理
        if len(NodeA)==i:
            return NodeA[0]
        elif len(NodeB)==i:
            return NodeB[0]
        else:
            return None
        
        

法二:正解——双指针法
当链表headA 和headB 都不为空时,创建两个指针pA 和pB,初始时分别指向两个链表的头节点 headA 和headB,然后将两个指针依次遍历两个链表的每个节点。具体做法如下:

  • 每步操作需要同时更新指针 pA 和pB。
  • 如果指针pA 不为空,则将指针pA 移到下一个节点;如果指针pB 不为空,则将指针 pB 移到下一个节点。
  • 如果指针pA 为空,则将指针pA 移到链表headB 的头节点;如果指针pB 为空,则将指针 pB 移到链表headA 的头节点。
  • 当指针pA 和pB 指向同一个节点或者都为空时,返回它们指向的节点或者null。

证明该做法的正确性:
情况一:两个链表相交
则,链表headA 和headB 的长度分别是 m 和 n。假设链表headA 的不相交部分有 a 个节点,链表 headB 的不相交部分有 b 个节点,两个链表相交的部分有 c 个节点,则有 a+c=m,b+c=n。

  • 如果a=b,则两个指针会同时到达两个链表的第一个公共节点,此时返回两个链表的第一个公共节点;
  • 如果 a != b ,则指针 pA 会遍历完链表headA,指针pB 会遍历完链表headB,两个指针不会同时到达链表的尾节点,然后指针pA 移到链表headB 的头节点,指针pB 移到链表headA 的头节点,然后两个指针继续移动,在指针pA 移动了a+c+b次、指针pB 移动了b+c+a次之后,两个指针会同时到达两个链表的第一个公共节点,该节点也是两个指针第一次同时指向的节点,此时返回两个链表的第一个公共节点。【喵啊!!!】

情况二:两个链表不相交
链表 headA 和headB 的长度分别是 m 和 nn。考虑当m=n 和 m!=n时,两个指针分别会如何移动:

  • 如果 m=n,则两个指针会同时到达两个链表的尾节点,然后同时变成空值null,此时返回null;
  • 如果 m!=n,则由于两个链表没有公共节点,两个指针也不会同时到达两个链表的尾节点,因此两个指针都会遍历完两个链表,在指针pA 移动了 m+n 次、指针 pB 移动了 n+mn+m 次之后,两个指针会同时变成空值null,此时返回 null。
    【题解描述参考👉LeetCode-Solution
# Definition for singly-linked list.
# 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:
            pA = headB if not pA else pA.next
            pB = headA if not pB else pB.next
        return pA  

题55-II: 平衡二叉树

法一:自己想的方法
遍历套遍历:遍历二叉树,针对每个节点,计算其左子树和右子树的深度,判断深度差【根据题意得到的最直接的想法】
代码如下:

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        flag = 1 # 指示节点是否满足平衡这一要求
        def TreeDepth(p: TreeNode) -> int: # 递归求解树的深度
            LD,RD = 0,0
            if not p:
                return 0
            else:
                LD = TreeDepth(p.left)
                RD = TreeDepth(p.right)
                return (LD if LD>RD else RD)+1

        def preorder(p: TreeNode) -> None: # 先序遍历
            nonlocal flag,TreeDepth # 指明变量作用域
            if (p is not None)&(flag==1):
            	# 判断该节点的左右子树深度差是否大于1
                flag = 0 if abs(TreeDepth(p.left)-TreeDepth(p.right))>1 else 1
                preorder(p.left)
                preorder(p.right)
        preorder(root)
        return True if flag==1 else False

法二:官方题解
官方题解和我的方法在思路上是一样的,但是在代码表述上简洁很多,直接上代码:

class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        def height(root: TreeNode) -> int: # 该函数用于求解该节点的高度,也是递归,但比我的递归在表达上简单
            if not root:
                return 0
            return max(height(root.left), height(root.right)) + 1

        if not root:
            return True
        # 这个return直接就实现了遍历递归,不需要像我那样写个单独的函数
        return abs(height(root.left) - height(root.right)) <= 1 and self.isBalanced(root.left) and self.isBalanced(root.right)

献上膝盖

要点回顾:如何求二叉树的深度、如何深度遍历【应用形式上要学会举一反三

题56-I: 数组中数字出现的次数

第一次见这种题,自然想不到,于是看了某人的题解,现只对代码进行详细注释:

class Solution:
    def singleNumbers(self, nums: List[int]) -> List[int]:
        res = nums[0] # res用于存储整个nums异或的结果,该值其实等于a^b
        for n in nums[1:]:
            res ^= n
        # 现在,开始寻找res在二进制下为1的位
        i = 1
        while res&i==0: # 循环起始条件为 res与上1的结果,其实就是res从右往左数的第一位
            i <<= 1 # 当第一位为0时,将i左移一位,直到res与上i的结果为1,这时i就指示了目标位
        a,b = None,None # 初始化a和b
        for n in nums:
            if n&i: # 根据n在第i位上的值,将nums化为两部分
                a = a^n if a else n # 这里的if else是为了正确初始化a和b的值
            else:
                b = b^n if b else n # 如果a或b为空,则赋值为n
        return [a,b]


姿势:异或:a ^b^a=a

题63:股票的最大利润

哦吼,自己终于完整想出&写出了一道题。
思想:举例说明:[2,5,3,1,6,4]中,从2开始往后遍历,如果遇到比2大的值,则求差并维护最大的值,如果遇到比2小的值,则更新遍历起点,即从1开始接着遍历。
代码

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
    	# front为遍历起点,plen为list的长度,res为result
        front,plen,res = 0,len(prices),0
        # 开始遍历
        while front < plen-1:
            rear = front + 1 # 初始化rear
            while prices[rear]>prices[front]: # 当rear所指值比front大,则
                tmp = prices[rear] - prices[front] # 求解当前的差值
                res = max(res,tmp) # 维护正确答案
                if rear == plen-1: # 遍历结束
                    break
                rear += 1 # 更新rear,即往后遍历
            front = rear # 当rear小于front,则更新遍历起点front
        return res

题68-II:二叉树的最近公共祖先

想到了思路,卡在了代码上。。。。
思路:对于节点R而言,如果其左子树包含p,右子树包含q;或者其本身为p,左子树或右子树中包含q;则该节点就是p和q的最近公共祖先。

代码如下:

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
        res = None
        def findn(r:TreeNode)->int:
            nonlocal p,q,res
            if not r:
                return 0
            ind = 1 if r.val==p.val or r.val==q.val else 0 # 判断当前节点是否为p/q;
            now = ind+findn(r.left)+findn(r.right) # 求以该节点为根的子树中是否包含p/q
            if now==2: #由于递归的存在,一定会先遍历答案,再往上便利,所以 一旦now==2,则该节点比为正确答案,此时,为避免答案重复,需要return 0;
                res=r
                return 0
            else:
                return now
        findn(root)
        return res

这种递归形式需要学习,函数只完成了判断当前节点是否为答案的过程,其递归隐藏在加法中,所以,当now==2时,为了避免答案出现重复,需要return 0;
【献膝盖给HappySuperman——orz】

### 使用 ADB 命令安全卸载华为手机预装应用 #### 开启开发者模式 为了能够使用ADB命令,需先激活华为手机的开发者模式。这通常是在设置中的“关于手机”页面多次点击软件版本号来完成[^3]。 #### 连接设备与电脑 确保手机已连接至计算机,并且开启了USB调试功能。可以通过在通知栏下拉查看是否有相应的USB调试提示确认这一点。 #### 验证连接状态 打开命令行界面,在Windows上可以是CMD或PowerShell;对于Mac/Linux则是终端。输入`adb devices`指令验证设备是否被正确识别: ```bash adb devices ``` 如果一切正常,将会显示所连设备列表及其序列号。 #### 获取root权限(可选) 某些预装应用程序可能需要更高权限才能移除。此时可以在命令行中尝试获取超级用户权限(取决于ROM支持情况),但这一步并非总是必需: ```bash adb root ``` #### 查找包名 要删除特定的应用程序前,首先要确定其确切的包名称。利用下面这条命令列出所有已安装APP的信息,从中查找目标服务或应用对应的包名: ```bash adb shell pm list packages ``` 也可以通过过滤器缩小范围,比如只看含有“huawei”的那些包: ```bash adb shell pm list packages | grep huawi ``` #### 执行卸载操作 一旦获得了想要移除的应用程序的确切包名之后,就可以用以下命令来进行卸载动作了。请注意替换`<package_name>`为实际的目标包名: ```bash adb uninstall <package_name> ``` 对于系统级应用,则应采用更严格的语法形式以实现禁用而非彻底清除数据文件的效果: ```bash adb shell pm disable-user --user 0 <package_name> ``` 这种方法能有效阻止该应用查看启动而不破坏原始安装环境,便于日后恢复原状。 #### 注意事项 - **慎重选择**:务必仔细核对每一个待删项目的包名,以免误删重要组件。 - **保持警惕**:部分核心进程和服务不可随意终止,否则可能导致系统不稳定甚至无法开机等问发生。 - **做好备份**:建议事先创建完整的Nandroid镜像或其他类型的全盘快照作为保险措施。 - **遵循指导**:始终按照官方文档指示行事,遇到不确定之处及时查阅资料或咨询专业人士意见[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值