每日一题——Python实现PAT甲级1029 Median(举一反三+思想解读+逐步优化)


一个认为一切根源都是“自己不够强”的INTJ

个人主页:用哲学编程-CSDN博客
专栏:每日一题——举一反三
Python编程学习
Python内置函数

Python-3.12.0文档解读

目录

我的方法

代码功能和结构点评

时间复杂度分析

空间复杂度分析

优化建议

我要更强!

代码详解:

时间和空间复杂度

示例解释

示例输入

合并后的数组

中位数位置

详细步骤

哲学和编程思想

编程思想

哲学思想

示例的哲学与编程思想

举一反三

技巧1:化繁为简

技巧2:递归的自相似性

技巧3:二分查找

技巧4:抽象

技巧5:分治法和递归结合(归并排序)

总结


题目链接


我的方法

nums1=list(map(int,input().split()))
N1=nums1[0]
nums1=nums1[1:]
nums2=list(map(int,input().split()))
N2=nums2[0]
nums2=nums2[1:]nums1+=nums2
nums1.sort()if (N1+N2)%2==0:print(nums1[(N1+N2)//2-1])
else:print(nums1[(N1+N2)//2])

这段代码的功能是读取两个列表(nums1 和 nums2),合并它们,排序,然后根据合并后的列表长度的奇偶性,输出中位数。

代码功能和结构点评

  1. 输入处理:
    • 两次调用 input().split() 读取输入,将其转换为整型列表。
    • nums1 和 nums2 的第一个元素被分别赋值给 N1 和 N2,表示两个列表的长度。
    • 余下的元素分别保存在 nums1 和 nums2 中。
  2. 合并与排序:
    • 将 nums2 合并到 nums1 中,然后对合并后的列表进行排序。
  3. 中位数计算:
  • 根据合并后列表长度的奇偶性,计算并输出中位数。

时间复杂度分析

  1. 读取输入:
    • map(int, input().split()) 的时间复杂度是 O(N),这里 N 是输入的元素总数。
  2. 合并列表:
    • nums1 += nums2 是 O(N2),其中 N2 是 nums2 的长度。
  3. 排序:
    • 使用 sort() 方法对列表排序,时间复杂度是 O((N1 + N2) log(N1 + N2)),因为采用的是 Timsort 算法。
  4. 中位数查找:
  • 访问列表元素的时间复杂度是 O(1)。

综上所述,总的时间复杂度是: [ O(N1 + N2 + (N1 + N2) \log(N1 + N2)) ]

空间复杂度分析

  • 需要额外的空间来存储输入的整数列表,空间复杂度是 O(N1 + N2)。
  • sort() 方法在最差情况下使用 O(N1 + N2) 的额外空间(Timsort 的空间复杂度)。

综上所述,总的空间复杂度是: [ O(N1 + N2) ]

优化建议

合并和排序的时间复杂度是这段代码的主要瓶颈。如果只是为了找中位数,可以采用更高效的算法(如归并排序中的选择算法),其时间复杂度为 O(N1 + N2),无需对整个列表排序。


我要更强!

要优化这段代码的时间复杂度和空间复杂度,可以使用一种称为“二分查找”的方法来找到两个有序数组的中位数,而无需将它们合并和排序。这个方法的时间复杂度是 O(log(min(N1, N2))),空间复杂度是 O(1)。

以下是实现这个方法的完整代码和注释:

def findMedianSortedArrays(nums1, nums2):def find_kth_element(arr1, arr2, k):# 如果 arr1 比 arr2 长,交换它们if len(arr1) > len(arr2):arr1, arr2 = arr2, arr1# 如果 arr1 为空,直接返回 arr2 中的第 k 个元素if len(arr1) == 0:return arr2[k - 1]# 如果 k == 1,返回两个数组第一个元素中较小的一个if k == 1:return min(arr1[0], arr2[0])# 取两个数组的第 k//2 个元素进行比较i = min(len(arr1), k // 2)j = min(len(arr2), k // 2)if arr1[i - 1] > arr2[j - 1]:return find_kth_element(arr1, arr2[j:], k - j)else:return find_kth_element(arr1[i:], arr2, k - i)total_len = len(nums1) + len(nums2)if total_len % 2 == 1:# 如果总长度是奇数,返回第 (total_len // 2 + 1) 个元素return find_kth_element(nums1, nums2, total_len // 2 + 1)else:# 如果总长度是偶数,返回第 (total_len // 2) 个元素return find_kth_element(nums1, nums2, total_len // 2)# 读取输入
import sys
input = sys.stdin.read
data = input().split()# 解析输入
n1 = int(data[0])
nums1 = list(map(int, data[1:n1+1]))n2 = int(data[n1+1])
nums2 = list(map(int, data[n1+2:]))# 调用函数并输出结果
print(findMedianSortedArrays(nums1, nums2))
  • 代码详解:

  1. 定义 find_kth_element 函数:
    • 该函数用于查找两个有序数组中的第 k 个元素。
    • 如果数组 arr1 比 arr2 长,则交换它们,以确保 arr1 是较短的数组。
    • 如果 arr1 为空,则直接返回 arr2 中的第 k 个元素。
    • 如果 k == 1,返回两个数组第一个元素中较小的一个。
    • 通过比较 arr1 和 arr2 的第 k//2 个元素来缩小查找范围。
  2. 计算总长度 total_len:
    • 如果总长度是奇数,则返回第 (total_len // 2 + 1) 个元素。
    • 如果总长度是偶数,则返回第 (total_len // 2) 个元素(即中间两个元素偏左的那个)。
  3. 读取和解析输入:
    • 使用 sys.stdin.read 读取输入,并根据输入格式解析成两个数组。
  4. 调用函数并输出结果:
  • 调用 findMedianSortedArrays 函数计算中位数,并输出结果。

这样,通过二分查找,我们能以 O(log(min(N1, N2))) 的时间复杂度找到两个有序数组的中位数。

时间和空间复杂度

  • 时间复杂度:O(log(min(N1, N2))),因为我们对较短的数组进行二分查找。
  • 空间复杂度:O(1),只使用了常数级别的额外空间。

这段代码高效地找到了两个有序数组的中位数,避免了合并和排序的高时间复杂度,并且只使用了常数级别的额外空间。

示例解释

示例输入

数组1:[11, 12, 13, 14]
数组2:[9, 10, 15, 16, 17]

合并后的数组

合并并排序后,我们得到一个新的排序数组:[9, 10, 11, 12, 13, 14, 15, 16, 17]

中位数位置

由于合并后的数组长度为 9(奇数),中位数是第 5 个元素(偏左的那个):13

详细步骤

因为 arr1[1] > arr2[1],所以排除 arr2 的前 j = 2 个元素,并递归查找剩下的第 k - j = 3 个元素。

因为 arr1[0] <= arr2[0],所以排除 arr1 的前 i = 1 个元素,并递归查找剩下的第 k - i = 2 个元素。

因为 arr1[0] <= arr2[0],所以排除 arr1 的前 i = 1 个元素,并递归查找剩下的第 k - i = 1 个元素。

  1. 调用 findMedianSortedArrays(nums1, nums2):
    • nums1 = [11, 12, 13, 14]
    • nums2 = [9, 10, 15, 16, 17]
    • total_len = 9(奇数)。
  2. 查找第 (9 // 2 + 1) = 5 个元素。
  3. 调用 find_kth_element(nums1, nums2, 5):
    • k = 5,初始时 arr1 = [11, 12, 13, 14] 和 arr2 = [9, 10, 15, 16, 17]。
  4. 比较两个数组的第 k // 2 = 2 个元素:
    • i = min(len(arr1), k // 2) = 2
    • j = min(len(arr2), k // 2) = 2
    • arr1[1] = 12
    • arr2[1] = 10
  5. 调用 find_kth_element(nums1, nums2[2:], 3),即:
    • arr1 = [11, 12, 13, 14]
    • arr2 = [15, 16, 17]
    • k = 3。
  6. 比较两个数组的第 k // 2 = 1 个元素:
    • i = min(len(arr1), k // 2) = 1
    • j = min(len(arr2), k // 2) = 1
    • arr1[0] = 11
    • arr2[0] = 15
  7. 调用 find_kth_element(nums1[1:], nums2, 2),即:
    • arr1 = [12, 13, 14]
    • arr2 = [15, 16, 17]
    • k = 2。
  8. 比较两个数组的第 k // 2 = 1 个元素:
    • i = min(len(arr1), k // 2) = 1
    • j = min(len(arr2), k // 2) = 1
    • arr1[0] = 12
    • arr2[0] = 15
  9. 调用 find_kth_element(nums1[1:], nums2, 1),即:
    • arr1 = [13, 14]
    • arr2 = [15, 16, 17]
    • k = 1。
  10. 由于 k == 1,直接返回两个数组的第一个元素中较小的一个,即:
  • min(arr1[0], arr2[0]) = min(13, 15) = 13

所以,合并后数组的中位数为 13。


哲学和编程思想

编程思想

  1. 分治法(Divide and Conquer):
    • 该方法通过将问题分成更小的子问题,然后递归地解决这些子问题。具体来说,二分查找的方法将两个数组的中位数问题分解为对较短数组的一部分和较长数组的一部分进行递归查找。
    • 分治法的核心在于将一个复杂问题分解成更小、更容易解决的部分,然后组合这些部分的解来解决整个问题。
  2. 递归(Recursion):
    • 递归是一种直接或间接调用自身的编程技术。这种方法通过不断地缩小问题规模,最终解决最小规模的问题来达到解决整个问题的目的。
    • 递归的核心思想在于找到基准情况(base case)和递归步骤(recursive step),基准情况是问题的最小实例,它可以直接解答,而递归步骤则是将问题缩小并继续递归求解。
  3. 二分查找(Binary Search):
  • 二分查找是一种在有序数组中查找元素的高效算法,其时间复杂度为 O(log n)。在这个方法中,二分查找用于确定数组中第 k 个元素,从而显著减少了查找的时间复杂度。
  • 二分查找的核心思想在于每次比较时将搜索范围缩小一半,从而快速定位目标元素。

哲学思想

  1. 化繁为简(Reductionism):
    • 这种思想主张将复杂的问题分解为更小、更简单的问题来解决。在这个算法中,通过将两个数组的中位数问题分解为查找第 k 个元素的问题,我们可以更容易地处理问题。
    • 化繁为简的哲学在于相信任何复杂的问题都可以通过适当的分解和简化来解决。
  2. 递归的自相似性(Self-Similarity in Recursion):
    • 递归过程中的每一层调用看起来都与其他层次类似,只是处理的规模不同。这种自相似性是许多自然界和数学现象的共同特征。
    • 递归的自相似性在编程中的应用体现了问题的结构和解决方案之间的一致性。
  3. 抽象(Abstraction):
  • 抽象是一种只关注问题的高层次视角,而忽略具体实现细节的方法。在这个算法中,通过定义 find_kth_element 函数,我们把查找第 k 个元素的具体实现细节封装起来,使得主函数的逻辑更加清晰。
  • 抽象的核心在于从复杂的现实中提取出关键的本质部分,从而简化问题的解决过程。

示例的哲学与编程思想

通过运用上述思想,能够高效地解决合并两个有序数组并找到中位数的问题:

  1. 分治法使我们能够递归地缩小问题规模,避免了直接合并两个数组的高昂时间复杂度。
  2. 递归允许我们自然地处理分治法分解出的子问题。
  3. 二分查找提供了一种高效的方式来确定数组的中位数位置。
  4. 化繁为简的哲学思想帮助我们将复杂问题分解,使其更易于解决。
  5. 递归的自相似性和抽象使得代码结构清晰,逻辑简洁。

通过结合这些编程和哲学思想,不仅能够高效地解决问题,还能够提高代码的可读性和可维护性。


举一反三

理解这些编程和哲学思想后,您可以应用这些思想解决其他复杂问题。以下是一些技巧和示例代码,帮助您举一反三:

技巧1:化繁为简

问题:给定一个整数数组,找到数组中的第 k 小的元素(不包括重复元素)。

技巧:可以利用分治法和递归来解决这个问题。

def findKthSmallest(arr, k):def quickselect(left, right, k_smallest):if left == right:return arr[left]pivot_index = partition(left, right)if k_smallest == pivot_index:return arr[k_smallest]elif k_smallest < pivot_index:return quickselect(left, pivot_index - 1, k_smallest)else:return quickselect(pivot_index + 1, right, k_smallest)def partition(left, right):pivot = arr[right]store_index = leftfor i in range(left, right):if arr[i] < pivot:arr[i], arr[store_index] = arr[store_index], arr[i]store_index += 1arr[store_index], arr[right] = arr[right], arr[store_index]return store_indexunique_arr = list(set(arr))return quickselect(0, len(unique_arr) - 1, k - 1)# 示例使用
arr = [3, 2, 1, 5, 6, 4, 3, 2]
k = 2
print(findKthSmallest(arr, k))  # 输出 3

技巧2:递归的自相似性

问题:计算斐波那契数列的第 n 个数。

技巧:递归的自相似性可以自然地解决这种问题。

def fibonacci(n):if n <= 1:return nelse:return fibonacci(n - 1) + fibonacci(n - 2)# 示例使用
n = 10
print(fibonacci(n))  # 输出 55

技巧3:二分查找

问题:在一个旋转排序数组中找到一个目标值。

技巧:二分查找可以高效地解决这个问题。

def search_rotated_array(nums, target):left, right = 0, len(nums) - 1while left <= right:mid = (left + right) // 2if nums[mid] == target:return midif nums[left] <= nums[mid]:if nums[left] <= target < nums[mid]:right = mid - 1else:left = mid + 1else:if nums[mid] < target <= nums[right]:left = mid + 1else:right = mid - 1return -1# 示例使用
nums = [4, 5, 6, 7, 0, 1, 2]
target = 0
print(search_rotated_array(nums, target))  # 输出 4

技巧4:抽象

问题:计算字符串的所有可能子集。

技巧:抽象出递归的核心部分,使得代码更清晰。

def subsets(s):def backtrack(start, path):result.append(path[:])for i in range(start, len(s)):path.append(s[i])backtrack(i + 1, path)path.pop()result = []backtrack(0, [])return result# 示例使用
s = "abc"
print(subsets(s))  # 输出 [[''], ['a'], ['a', 'b'], ['a', 'b', 'c'], ['a', 'c'], ['b'], ['b', 'c'], ['c']]

技巧5:分治法和递归结合(归并排序)

问题:对一个整数数组进行排序。

技巧:利用分治法和递归实现归并排序。

def merge_sort(arr):if len(arr) <= 1:return arrmid = len(arr) // 2left_half = merge_sort(arr[:mid])right_half = merge_sort(arr[mid:])return merge(left_half, right_half)def merge(left, right):sorted_array = []while left and right:if left[0] < right[0]:sorted_array.append(left.pop(0))else:sorted_array.append(right.pop(0))sorted_array.extend(left or right)return sorted_array# 示例使用
arr = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
print(merge_sort(arr))  # 输出 [1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9]

总结

通过理解这些技巧,可以在解决各种问题时应用相应的编程和哲学思想:

  • 化繁为简:将复杂问题分解为简单子问题。
  • 递归的自相似性:利用递归解决具有自相似性的复杂问题。
  • 二分查找:在有序或部分有序的数据结构中快速查找元素。
  • 抽象:将复杂的逻辑封装在函数内,使代码更简洁清晰。

分治法和递归结合:通过分治法和递归解决需要多步骤处理的问题。


感谢阅读,关注我每日一题提升自己。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/842522.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深度学习环境安装教程-anaconda-python-pytorch

首先是anaconda的安装&#xff0c;可以从下面地址下载安装包 Index of /anaconda/archive/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 尽量选择最新的日期的anaconda进行安装&#xff0c;我这里是windows电脑&#xff0c;因此选择了windos-x86_64.exe&#xf…

Git相关命令介绍

Git是一个功能强大的版本控制系统&#xff0c;它提供了一系列的命令来帮助开发者进行日常的版本控制操作。以下是一些常用的Git命令及其简要介绍&#xff1a; 初始化和配置 git init: 初始化一个新的Git仓库。git config: 配置Git的设置&#xff0c;如用户名、邮箱等。 文件…

如何在anaconda的环境下安装langchain

1、安装anaconda&#xff1b; 2、在终端上&#xff0c;输入&#xff1a; conda install langchain -c conda-forge Proceed ([y]/n)? y 输入&#xff1a;Y 3、安装完成后&#xff0c;输入&#xff1a; python -c "import langchain; print(langchain.__version__)&…

Redisson集成SpringBoot

前言&#xff1a;Redisson集成SpringBoot主要有两种方式&#xff0c;一个是使用redisson-spring-boot-starter依赖&#xff08;优先推荐&#xff09;&#xff0c;毕竟springboot主打的就是约定大于配置&#xff0c;这个依赖就是为springboot准备的。 再一种方式就是引入rediss…

JVM学习-javap解析Class文件

解析字节码的作用 通过反编译生成字节码文件&#xff0c;可以深入了解Java工作机制&#xff0c;但自己分析类文件结构太麻烦&#xff0c;除了第三方的jclasslib工具外&#xff0c;官方提供了javapjavap是jdk自带的反解析工具&#xff0c;它的作用是根据class字节码文件&#x…

JVM(9):虚拟机性能分析和故障解决工具之jmap工具

1 jmap(Memory Map for Java)作用 一个多功能的命令&#xff0c;它可以生成 java 程序的 dump 文件&#xff0c; 也可以查看堆内对象信息、查看 ClassLoader 的信息以及 finalizer 队列 2 命令格式 jmap [options] 参数解释&#xff1a; 第一个参数&#xff1a;options no…

一个投稿好方法让你的文章早日发表

作为一名单位信息宣传员,我初入此行时,满腔热情,怀揣着传播单位价值、展示团队风采的理想,一头扎进了稿件撰写的海洋。我的目标很简单,就是通过文字的力量,让外界听到我们的声音,感受到我们的活力。然而,理想很丰满,现实却给我上了生动的一课。 起初,我遵循传统路径,选择了一家…

QT安装和配置[安装注意点][QT找不到python27.dll][缩小空间]

安装注意点 本文摘录于&#xff1a;https://blog.csdn.net/Python_0011/article/details/131699443只是做学习备份之用&#xff0c;绝无抄袭之意&#xff0c;有疑惑请联系本人&#xff01; 双击"qt-online-installer-windows-x64-4.8.0.exe"文件后输入账号选择如下控…

云原生架构内涵_1.云原生架构定义

1.云原生架构 从技术的角度&#xff0c;云原生架构是基于云原生技术的一组架构原则和设计模式的集合&#xff0c;旨在将云应用中的非业务代码部分进行最大化的剥离&#xff0c;从而让云设施接管应用中原有的大量非功能特性&#xff08;如弹性、韧性、安全、可观测性、灰度等&a…

什么是Capto刀柄,一起来认识一下

大家好&#xff0c;今天咱们不聊齿轮&#xff0c;说一说一款刀柄的相关内容。目前&#xff0c;高速加工中心的主轴转速可以达到10,000——50,000r /min &#xff0c;极大地提高了生产率。高速加工工具系统的主要作用是保证刀具在机床主轴中的精确定位&#xff0c;将主轴的运动和…

车辆工程计算机编程:深度探索与未来挑战

车辆工程计算机编程&#xff1a;深度探索与未来挑战 随着科技的不断进步&#xff0c;车辆工程领域与计算机编程的交融日益紧密&#xff0c;为行业发展注入了新的活力。然而&#xff0c;对于许多人来说&#xff0c;车辆工程计算机编程究竟学什么&#xff0c;仍是一个充满困惑和…

【C++刷题】优选算法——递归第四辑

记忆化搜索篇 什么是记忆化搜索&#xff1f; 带 备忘录 的递归 如何实现记忆化搜索&#xff1f; a.添加一个备忘录 <可变参数&#xff0c;返回值>b.每次递归返回的时候&#xff0c;把结果放到备忘录里c.每次递归进入的时候&#xff0c;先查看一下备忘录 记忆化搜索 vs 常…

对于个人而言,大数据时代如何更好地管理自己的信息?

在大数据时代&#xff0c;管理个人信息变得尤为重要。以下是几个建议来更好地管理个人信息&#xff1a; 认识和了解自己的数字足迹&#xff1a;了解自己在互联网上的活动&#xff0c;包括浏览历史、社交媒体和在线购物数据等。通过查阅自己的帐户设置和隐私选项&#xff0c;可以…

golang信号通知 signal.Notify NotifyContext完整示例

在看示例之前有必要先看看Go程序中信号的默认行为&#xff0c; go中信号的默认行为如下&#xff1a; SIGHUP、SIGINT或SIGTERM信号会导致程序退出。SIGQUIT、SIGILL、SIGTRAP、SIGABRT、SIGSTKFLT、SIGEMT或SIGSYS信号会导致程序退出并进行堆栈转储。SIGTSTP、SIGTTIN或SIGT…

使用Nginx作为反向代理实现MQTT内外网通信

使用Nginx作为反向代理实现MQTT内外网通信 步骤1: 安装Nginx 确保你的服务器上已安装Nginx。如果未安装&#xff0c;可以通过以下命令在Ubuntu上安装Nginx&#xff1a; sudo apt update sudo apt install nginx步骤2: 配置Nginx 编辑Nginx的配置文件&#xff0c;通常是/etc…

全面掌握Prompt提示词技巧

本文综合介绍了Prompt提示词的各种技巧&#xff0c;包括高级提示工程技术、设计提示的通用技巧、优化prompt的十个技巧、AI提示词网站合集、提示工程指南以及ChatGPT提示词技巧等&#xff0c;旨在帮助读者深入理解和应用这些技巧&#xff0c;提高与AI模型的交互效率和质量。 文…

C++ 类模板 函数模板

类模板 #include <bits/stdc.h> using namespace std; //多少变量就写多少个 template<typename T1, typename T2> class Cat { public:Cat(){}Cat(T1 name, T2 age){this->age age;this->name name;}void print(){cout << this->name << …

Python脚本启动应用并输入账号或密码

一、简介 如果每天要启动某个软件还要输入账号密码登录的需求的话&#xff0c;可以参考本文章&#xff1b; 二、Python环境 环境&#xff1a;Python3.11 已经在Windows电脑中配置Python环境变量&#xff0c;且配置了pipd的环境变量&#xff1b; 三、安装模块 安装所需要的…

计算机毕业设计 | SpringBoot招投标系统 任务发布网站(附源码)

1&#xff0c;绪论 在市场范围内&#xff0c;任务发布网站很受欢迎&#xff0c;有很多开发者以及其他领域的牛人&#xff0c;更倾向于选择工作时间、工作场景更自由的零工市场寻求零散单子来补贴家用。 如今市场上&#xff0c;任务发布网站鱼龙混杂&#xff0c;用户需要找一个…

(原创)从右到左排列RecycleView的数据

问题的提出 当我们写一个Recycleview时&#xff0c;默认的效果大概是这样的&#xff1a; 当然&#xff0c;我们也可以用表格布局管理器GridLayoutManager做成这样&#xff1a; 可以看到&#xff0c;默认的绘制方向是&#xff1a; 从左到右&#xff0c;从上到下 那么问题来了…