Python 数据结构 每个程序员都应该知道

360影视 动漫周边 2025-05-15 10:38 1

摘要:简单来说,数据结构是用于组织、处理、检索和存储数据的专用格式。你可以把它们想象成不同类型的容器,每种容器都有独特的属性,使其适合特定的任务。选择合适的数据结构可以提高程序的效率和可读性。另一方面,选择不当可能会导致缓慢、内存密集型且难以维护的应用程序。

在用 Python 编程时,选择合适的数据结构有助于编写更易于维护的代码。它常常会改变你解决问题的方法。


简单来说,数据结构是用于组织、处理、检索和存储数据的专用格式。你可以把它们想象成不同类型的容器,每种容器都有独特的属性,使其适合特定的任务。选择合适的数据结构可以提高程序的效率和可读性。另一方面,选择不当可能会导致缓慢、内存密集型且难以维护的应用程序。

Python 有几种内置数据结构,可以帮助你有效地存储、管理和操作数据。了解何时以及如何使用它们对于编写干净且性能良好的代码至关重要。

列表(有序、可变)元组(有序、不可变)字典(键值映射)集合(无序、唯一元素)

列表是 Python 中简单但非常有用的数据结构。它们可以包含任何类型的对象,并且在你需要修改序列(例如添加、删除或排序元素)时非常有用。

tasks = ["write report", "send email", "attend meeting"]tasks.append("review pull request") # Add a task at the endtasks.insert(1, "check calendar") # Insert task at position 1completed_task = tasks.pop(2) # Remove and return the item at index 2print("Tasks left:", tasks)print("Completed:", completed_task)

输出:

Tasks left: ['write report', 'check calendar', 'attend meeting', 'review pull request']Completed: send email

通过添加、插入和删除项目来管理动态任务列表。

使用场景:适用于需要频繁更新的有序数据——如队列、购物车或日志。

元组类似于列表,但它们是不可变的。一旦创建,其内容就不能更改。它们非常适合固定集合的项。

这个用于存储和访问固定位置的数据,不应该改变。

coordinates = (37.7749, -122.4194) print(f"Latitude: {coordinates[0]}, Longitude: {coordinates[1]}")

这里还有一个例子。

Latitude: 37.7749, Longitude: -122.4194

这返回一个包含最小值和最大值的元组。

def min_max(numbers): return (min(numbers), max(numbers))print(min_max([3, 7, 1, 9]))

Output:

(1, 9)


何时使用:当你想确保数据完整性或从函数返回多个值时,使用元组。

字典允许你将键与值关联起来并快速访问它们。键必须是唯一的且不可变的。

user = { "name": "Alice", "email": "alice@example.com", "is_active": True}user["is_active"] = False # Update a valueprint(f"User {user['name']} is active: {user['is_active']}")

这里我们为用户数据创建一个字典并更新一个字段。

User Alice is active: False

Output:

def word_count(text): counts = {} for word in text.lower.split: counts[word] = counts.get(word, 0) + 1 return countsprint(word_count("Python is powerful and Python is fast"))

这给出了句子中每个单词出现的次数。

{'python': 2, 'is': 2, 'powerful': 1, 'and': 1, 'fast': 1}

何时使用:适用于计数器、查找、缓存以及存储对象状数据。

集合是唯一项的集合。你可以执行快速成员检查和集合运算,如并集或交集。

python_devs = {"Alice", "Bob", "Charlie"}javascript_devs = {"Alice", "Eve", "Dave"}both = python_devs & javascript_devs # Intersectioneither = python_devs | javascript_devs # Uniononly_python = python_devs - javascript_devs # Differenceprint("Knows both:", both)print("Knows either:", either)print("Knows only Python:", only_python)

这个操作使用集合运算在两个开发者组之间查找共同和独特的用户。

Knows both: {'Alice'}Knows either: {'Bob', 'Charlie', 'Eve', 'Dave', 'Alice'}Knows only Python: {'Bob', 'Charlie'}

这里我们使用集合来消除重复的电子邮件。

emails = ["a@example.com", "b@example.com", "a@example.com"]unique_emails = set(emails)print(unique_emails)

Output:

{'b@example.com', 'a@example.com'}

何时使用:适用于去重、成员检查以及需要集合代数时(例如过滤或比较)。

接下来来看 Python 标准库中的数据结构,这些数据结构扩展了内置类型的函数。这些是针对常见编程需求专门设计的解决方案,可以加快代码速度,使代码更简洁,并且通常更节省内存。

现在让我来看看 collections 和 heapq 模块中的一些数据结构。

双端队列(deque)是一种可以在两端进行快速添加和删除的队列。与列表不同,在列表中插入或删除开头元素是昂贵的(O(n)),而双端队列可以保持快速(O(1))。

你应该在什么时候使用双端队列?

当你正在构建一个任务队列,其中任务按顺序处理(例如打印机队列中的任务)时在数据流上实现滑动窗口算法构建 BFS(广度优先搜索)算法维护一个滚动缓冲区(跟踪最近 N 笔交易)

你应该避免使用它的时候?

如果你需要随机访问元素(例如立即访问第100个元素)如果你需要优化最小的内存占用

我们从任务队列开始。紧急任务被添加到左侧(以便它们首先被处理)。

from collections import deque# Initialize a dequetasks = deque(["email client", "compile report", "team meeting"])# Add a new urgent task to the beginningtasks.appendleft("fix production issue")# Add a low-priority task to the endtasks.append("update documentation")# Process tasksnext_task = tasks.popleft # Handles "fix production issue"later_task = tasks.pop # Handles "update documentation"print(tasks)

低优先级任务被追加到右侧(稍后处理)。popleft和 pop允许你从任一端处理任务。

deque(['email client', 'compile report', 'team meeting'])

一个 defaultdict 就像一个普通的字典一样工作,但会为缺失的键自动提供默认值,从而无需手动检查。

你应该在什么时候使用 defaultdict?当你需要:

自动分组项目,例如按文件扩展名组织文件计数出现次数,例如跟踪每个用户进行了多少次 API 调用构建图表示(例如邻接表)将数据累积到列表、集合或数字中,而无需担心初始化

什么时候避免使用?当你希望缺失的键显式地引发错误(以便更快地检测错误)

from collections import defaultdict# Group employees by departmentemployees = [ ("HR", "Alice"), ("Engineering", "Bob"), ("HR", "Carol"), ("Engineering", "Dave"), ("Sales", "Eve")]departments = defaultdict(list)for dept, name in employees: departments[dept].append(name)print(departments)

在这个代码片段中,任何新部门都使用空列表进行初始化。

defaultdict(, {'HR': ['Alice', 'Carol'], 'Engineering': ['Bob', 'Dave'], 'Sales': ['Eve']})

员工分组时无需手动检查部门键是否存在。这简化了通常需要多行错误检查的操作。

您可以使用 Counter 类来计数可哈希对象。它自动计数并跟踪频率。

什么时候应该使用 Counter?

分析日志文件并统计特定事件发生的频率找出应用程序返回的最常见错误代码用于跟踪资源使用情况,例如最常访问的 URL用于基本的多集合操作(添加、减去元素计数)

什么时候应该跳过它?当你只计算非常少数的项目,而一个普通的字典就足够了。

from collections import Counter# Analyze page visitspage_visits = [ "/home", "/products", "/about", "/products", "/home", "/contact"]visit_counter = Counter(page_visits)# Most visited pagesprint(visit_counter.most_common(2))# Adding more page viewsvisit_counter.update(["/home", "/blog"])print(visit_counter)

这里计数器对象给出了每个页面被访问的次数。most_common(2)高效地告诉我们最常访问的两个页面。update让我们可以轻松添加更多的页面浏览。

[('/home', 2), ('/products', 2)]Counter({'/home': 3, '/products': 2, '/about': 1, '/contact': 1, '/blog': 1})

heapq 模块提供了用于处理堆的功能——堆是一种特殊的树,其中最小的(或最大的)元素始终位于顶部。它支持高效的检索和插入,同时保持堆属性。

什么时候应该使用 heapq?

构建优先队列(例如,基于紧急程度的任务调度器)。在大型数据集中查找最小或最大的 K 个元素。实现迪杰斯特拉最短路径算法。合并排序的数据流。import heapq# Manage tasks by priority (lower number = higher priority)tasks = [(3, "write report"), (1, "fix critical bug"), (4, "team meeting")]# Convert the list into a heapheapq.heapify(tasks)# Add a new taskheapq.heappush(tasks, (2, "code review"))# Process tasks by prioritywhile tasks: priority, task = heapq.heappop(tasks) print(f"Processing {task} with priority {priority}")

heapify 根据优先级将任务排列成一个最小堆。使用 heappush 添加新任务的同时也保持堆顺序。heappop 总是获取下一个最高优先级(最低数字)的任务,确保高效调度。

Processing fix critical bug with priority 1Processing code review with priority 2Processing write report with priority 3Processing team meeting with priority 4

来源:自由坦荡的湖泊AI一点号

相关推荐