python基础②-数据结构

发布于:2025-07-17 ⋅ 阅读:(17) ⋅ 点赞:(0)

1.Dictionary字典

1.1 概念及常用函数

字典,也可以用dict表示,它是是 Python 中的内置数据结构,以键 - 值对(key - value pairs)的形式存储数据 ,具备高效的查找特性。但这里需要明白它的规则要求,

key 必须是可哈希的数据类型,像字符串、数字、元组等;

而 value 则灵活得多,可容纳任意数据类型,如列表、字典、函数等。

那接下来我们从以下几个函数进行介绍。

1.1.1 get函数

用于获取指定 key 对应的 value 。当 key 存在时,返回对应的 value;若 key 不存在,返回默认值(默认值未指定时返回 None )。

例如说:

student = {'name': '小明', 'age': 20}
# key 存在,返回对应 value
print(student.get('name'))  

# key 不存在,返回默认值 None
print(student.get('gender'))  

# 指定默认值,key 不存在时返回该默认值
print(student.get('gender', '未知'))  

相较于直接通过 dict[key] 取值,get 函数在 key 不存在时不会抛出 KeyError 异常。

在这里插入图片描述

1.1.2 setdefault函数

若 key 存在于字典中,返回对应的 value;若 key 不存在,会将该 key 插入字典,并将 value 设为指定的默认值。

例如说:

student = {'name': '小明', 'age': 20}
# key 存在,返回对应 value
print(student.setdefault('name', '小红'))  

# key 不存在,插入 key - value 对
student.setdefault('gender', '男')  

print(student)  

在这里插入图片描述

主要应用在处理字典动态添加键值对的场景中极为便捷,比如统计列表中元素出现次数时,可用于初始化计数 。

字典的查找操作:

dict1 = {"school":"GPNU", "age":"18"}
print(dict1["school"])

此时会输出:

GPNU

字典的增加操作:(同理这也是字典的修改操作,一样的)

dict1["name"] = "LKR"
print(dict1["name"])

此时会输出:

LKR

字典的删除操作:

dict1.pop("school")

1.2 字典循环(解构)

对于常规循环,就是直接遍历字典,默认拿到的是 key ,通过 key 可进一步获取对应的 value 。

student = {'name': '小明', 'age': 20}
for key in student:
    print(key, student[key])

在这里插入图片描述

这边介绍一种解构循环,更好用,它是同时遍历 key 和 value,一次就能读出来。

student = {'name': '小明', 'age': 20}
for key, value in student.items():
    print(key, value)

在这里插入图片描述

结果一样,解构循环让代码更简洁直观,在需要同时操作键和值时,无需额外通过 key 去字典中取值 。

还可以采用item函数,一次性全输出出来,如:

dict1 = {"school":"GPNU", "age":"18"}
dict1["name"] = "LKR"
for i,j in dict1.items():
    print(i, j)

结果输出如下:

school GPNU
age 18
name LKR

1.3 字典的嵌套

字典的value也可以是字典类型,从而形成嵌套结构,用于表示更复杂的数据关系 。

例如:

# 嵌套字典表示学生信息,包含基本信息和课程成绩
student = {
    'basic_info': {'name': '小明', 'age': 20},
    'courses': {'math': 90, 'english': 85}
}
# 访问嵌套字典中的值
print(student['basic_info']['name'])  
print(student['courses']['math'])  

这个跟列表的嵌套核心的分析方法是一样的。

1.4 字典的循环删除

在 Python 中,直接在循环中删除字典元素(如 for key in dict: del dict[key] )会引发错误,因为删除元素会改变字典的大小,导致迭代出错 。

这里的解决方法也还是一样,就是创建临时字典来存储数据,然后再进行删除。

例如:

student = {'name': '小明', 'age': 20, 'gender': '男'}
keys_to_delete = ['age', 'gender']
for key in keys_to_delete:
    del student[key]
print(student)

在这里插入图片描述

这里就成功删除了。

1.5 字符集与编码知识

在Python数据分析中,编码本质规定字符转二进制数据的规则,处理文本数据时需知晓编码规则,否则字典存储含中文等文本数据读写传输易出错;

多语言兼容需求下,Unicode等编码可统一字符表示、动态调整字节,影响字典key比较、序列化等操作;

编码知识贯穿数据采集、清洗、分析全流程,与字典嵌套结构关联紧密,构建完整编码体系才能保障高效数据分析实践,

这个还是比较重要的,毕竟后面从采集读不同编码文件、清洗转编码格式到分析分词统计词频,编码错则分词乱、词频错,懂编码才能让字典统计、嵌套解析不会出现错误,让分析结果更加精确。

以下代码演示了 GBK 编码与 UTF-8 编码的转换过程,核心逻辑是解码 → 重新编码

结合字符集知识,理解其作用需要从编码转换的必要性和实际场景出发,

# GBK 编码的字节串(假设原始数据是 GBK 编码)
bs = b'\xb8\xd5\xbd\xa8\xbc\xbc\xb9\xa4\xd7\xf7'  

# 1. 解码:将 GBK 字节串转换为 Unicode 字符串
#    GBK 是解码时的“钥匙”,必须与原始编码一致,否则乱码
s = bs.decode("gbk")  

# 2. 重新编码:将 Unicode 字符串转换为 UTF-8 字节串
#    UTF-8 是常用的通用编码,适合跨平台传输/存储
bs2 = s.encode("utf-8")  

print(bs2)

这个程序的关键在于

解码(decode):把计算机存储的二进制字节(bs)还原为人类可读的 Unicode 字符串(s),需要指定原始编码(如 gbk)。

编码(encode):把 Unicode 字符串(s)重新转换为二进制字节(bs2),指定目标编码(如 utf-8),让数据能在不同系统/平台通用。

在这里插入图片描述

再比如需要分析一个 GBK 编码的文本文件:

import jieba

# 1. 读取 GBK 编码的文件(模拟老系统数据)
with open("old_gbk_file.txt", "rb") as f:
    gbk_bytes = f.read()  # 读取 GBK 字节串

# 2. 解码为 Unicode 字符串
unicode_str = gbk_bytes.decode("gbk")  

# 3. 用字典统计词频(依赖正确的字符串)
word_count = {}
for word in jieba.lcut(unicode_str):
    if len(word) >= 2:
        word_count[word] = word_count.get(word, 0) + 1

# 4. 编码为 UTF-8 写入新文件(跨平台通用)
with open("new_utf8_file.txt", "w", encoding="utf-8") as f:
    for word, count in word_count.items():
        f.write(f"{word}:{count}\n")

这里若是跳过编码转换,直接用 utf-8 读 GBK 文件,会触发 UnicodeDecodeError 或导致分词/统计完全错误。

总的来说,在 Python 数据分析中,编码转换对处理文本数据是很重要的

不仅可以修复 GBK 等历史编码的乱码问题,还原真实文本含义。

此外,统一编码,也能确保字典、文件、网络传输的兼容性。

2.String字符串

字符串(String)是 Python 中最基本的数据类型之一,用于表示文本数据,掌握字符串的各种操作和函数对于编写高效的 Python 程序至关重要。

要学习字符串,首先要理解什么是字符,比方说“H”、"A"这种就是一个字符,而字符串就是由若干个这样的字符串到一起的,那就叫字符串,如“happy”这样子就算是一个字符串。

同理,字符串也有它的查找、切片,最大小值、求长度(len)等操作,跟前面介绍到的都是一样的,这里不过多赘述。

下面着重讲下它的String里特有的函数,下面一一进行介绍。

count函数:计算字符串中特定字符的数目

s = "Hello World"
print(s.count("o"))   #结果是2,因为字符串里有两个“o”

isupper函数:计算字符串中是否都是大写字母,只返回TrueFalse

print(s.isupper())   #结果是False,因为字符串混着大小写字母   

islower函数:计算字符串中是否都是小写字母,只返回TrueFalse

print(s.islower())   #结果是False,因为字符串混着大小写字母 

lower函数:把字符串里的所有字符改成小写的形式

upper函数:把字符串里的所有字符改成大写的形式

strip函数:删除字符串首尾两端的指定字符(默认是空白字符,包括空格、换行 \n、制表符 \t 等 ),不影响中间的字符

lstrip函数:把字符串最左边的所有空格删除

rstrip函数:把字符串最右边的所有空格删除

swapcase函数:把字符串的大小写形式互换

s = "  Hello World  "
print(s.lower())
print(s.upper())
print(s.strip())
print(s.lstrip())
print(s.rstrip())
print(s.swapcase())

此时输出为:

  hello world  
  HELLO WORLD  
Hello World
Hello World  
  Hello World
  hELLO wORLD  

replace函数:把字符串的若干字符换成用户要更改成的字符

s = "  Hello World  "
print(s.replace('l','q'))  #输出为  Heqqo Worqd  

split函数:字符分割操作

s = "  Hello World  "
print(s.split("Hel"))   #输出为['  ', 'lo World  ']

以下是为每个函数补充的说明和示例,可对应填入图中每个函数下方的空白处:

title函数:将字符串中每个单词的首字母转为大写,常用来规范文本格式。

s = "hello world"
result = s.title()
print(result)  # 输出: Hello World

find函数:查找子字符串在原字符串中第一次出现的索引,找不到则返回 -1,用于定位文本位置。

s = "apple banana apple orange"
index = s.find("apple")
print(index)  # 输出: 0(第一个apple的起始索引)
not_found = s.find("grape")
print(not_found)  # 输出: -1(未找到)

index函数:与 find 类似,查找子字符串第一次出现的索引,但找不到时会报错ValueError),适合确认子串必须存在的场景。

s = "apple banana apple orange"
index = s.index("banana")
print(index)  # 输出: 6(banana的起始索引)
# 下方代码会报错,因为grape不存在
# s.index("grape")  

startswith函数:判断字符串是否以指定子串开头,返回布尔值(True/False),用于筛选文本前缀。

s = "https://www.example.com"
is_web = s.startswith("https://")
print(is_web)  # 输出: True

file_name = "data.txt"
is_txt = file_name.startswith("text")
print(is_txt)  # 输出: False

isdigit函数:判断字符串是否仅由数字组成(0 - 9),返回布尔值,常用于校验输入是否为纯数字。

num_str = "12345"
print(num_str.isdigit())  # 输出: True

mixed_str = "12a34"
print(mixed_str.isdigit())  # 输出: False

join函数:将可迭代对象(列表、元组等)中的元素,用指定字符串连接成一个新字符串,常用于拼接文本。

words = ["hello", "world", "python"]
joined = "-".join(words)
print(joined)  # 输出: hello-world-python

nums = ("1", "2", "3")
joined_nums = "|".join(nums)
print(joined_nums)  # 输出: 1|2|3

f变量函数:在字符串前加 f,用 {} 嵌入变量/表达式,快速拼接动态内容,比传统格式化更简洁。

name = "Alice"
age = 25
info = f"姓名: {name}, 年龄: {age}"
print(info)  # 输出: 姓名: Alice, 年龄: 25

# 也支持表达式
price = 10
count = 3
total = f"总价: {price * count}"
print(total)  # 输出: 总价: 30

在这里插入图片描述

3.Array数组

数组(Array) 是一种存储相同类型元素的有序集合。数组元素必须是相同类型(如整数、浮点数),比列表(list)更节省内存,适合处理大量同类型数据。

Array数组的常用使用:

import array

# 创建整数数组('i'表示整数类型、‘f’表示单精度浮点数、‘d’表示双精度浮点数,‘U’表示Unicode字符)
arr = array.array('i', [1, 2, 3, 4])

# 访问元素
print(arr[0])  # 输出: 1

# 修改元素
arr[1] = 10
print(arr)  # 输出: array('i', [1, 10, 3, 4])

# 添加元素
arr.append(5)
print(arr)  # 输出: array('i', [1, 10, 3, 4, 5])

4.Linkedlist链表

4.1 存储结构(数组与链表)

维度 数组(Array) 链表(Linked List)
内存布局 连续内存空间 非连续,通过指针连接各个节点
存储方式 元素按顺序存储在连续地址中 每个节点包含数据和指向下一节点的指针(单链表)
图示 [A, B, C, D](连续存储) A → B → C → D(节点通过指针连接)

4.2 优缺点对比(数组与链表)

数组的优势

随机访问高效:适合需要频繁通过索引访问元素的场景(如遍历、二分查找)。

内存利用率高:连续存储无需额外指针,空间开销小。

缓存友好:连续内存可充分利用 CPU 缓存,提高访问速度。

数组的劣势

插入或删除效率低:尤其在数组中间操作时,需移动大量元素。

固定容量:多数语言中数组长度需预先定义,动态扩容代价高(如 Python 列表扩容时需重新分配内存)。

链表的优势

动态扩容:无需预先分配空间,可按需添加节点。

高效插入或删除:适合频繁插入删除的场景(如队列、栈)。

链表的劣势

随机访问低效:不支持直接通过索引访问,需从头遍历。

内存开销大:每个节点需额外存储指针,空间利用率低。

缓存不友好:非连续内存导致缓存命中率低。

4.3 程序解读

#定义一个名为“Node”的类,代表链表中的一个节点
class Node:
    def __init__(self, data):
        self.data = data  #存储节点的数据
        self.next = None  #指向下一个节点的指针,初始为None

如创建单个节点:

node = Node(10)  # 创建一个节点,数据为 10
print(node.data)  # 输出: 10
print(node.next)  # 输出: None

如果要连接多个节点成为链表:

#定义一个名为“Node”的类,代表链表中的一个节点
class Node:
    def __init__(self, data):
        self.data = data  #存储节点的数据
        self.next = None  #指向下一个节点的指针,初始为none

# 创建三个节点
node1 = Node(1)
node2 = Node(2)
node3 = Node(3)
node5 = Node(5)

# 连接节点形成链表:1 → 2 → 3 → 5
node1.next = node2  # node1 的下一个节点是 node2
node2.next = node3  # node2 的下一个节点是 node3
node3.next = node5  # node3 的下一个节点是 node5

# 遍历链表(从 node1 开始)
current = node1
while current is not None:
    print(current.data)  # 依次输出 1, 2, 3, 5
    current = current.next

通过以上程序,对于链表有个初步的理解认识,常用的场景主要是用于实现队列,栈等数据结构,或者是操作系统中的内存管理,理解链表的关键是掌握“节点通过指针连接”这一思想。

5.Queue队列

5.1 特点

FIFO(先进先出):先进入队列的元素先出队,类似排队。

应用场景:任务调度、消息传递、广度优先搜索(BFS)等。

5.2 Python 实现

标准库 queue.Queue(线程安全,适合多线程):

from queue import Queue

q = Queue()
q.put(1)  # 入队
q.put(2)
print(q.get())  # 出队,输出: 1

collections.deque(双端队列,高效且常用):

from collections import deque

q = deque()
q.append(1)  # 入队
q.append(2)
print(q.popleft())  # 出队,输出: 1

6.Stack栈

6.1 特点

LIFO(后进先出):最后进入栈的元素先出栈,类似叠盘子。

应用场景:函数调用栈、表达式求值、深度优先搜索(DFS)等。

6.2 Python 实现

使用列表(List)

stack = []
stack.append(1)  # 入栈
stack.append(2)
print(stack.pop())  # 出栈,输出: 2

collections.deque(推荐):

from collections import deque

stack = deque()
stack.append(1)  # 入栈
stack.append(2)
print(stack.pop())  # 出栈,输出: 2

7.Heap堆

7.1 特点

完全二叉树:每个节点的值都大于等于(最大堆)或小于等于(最小堆)其子节点的值。

高效操作:插入和删除元素的时间复杂度为 O(log n),获取最大值/最小值为 O(1)

应用场景:优先队列、堆排序、求Top-K问题等。

7.2 Python 实现(最小堆)

使用 heapq 模块(基于列表实现):

import heapq

heap = []
heapq.heappush(heap, 3)  # 插入元素
heapq.heappush(heap, 1)
heapq.heappush(heap, 2)
print(heapq.heappop(heap))  # 弹出最小元素,输出: 1

最大堆技巧:插入元素时取负,取出时再取负:

import heapq

max_heap = []
heapq.heappush(max_heap, -3)  # 插入 3 的负数
heapq.heappush(max_heap, -1)
print(-heapq.heappop(max_heap))  # 弹出最大元素,输出: 3

Heap还有大堆,大堆就是你按顺序取出的是从大到小,跟小堆刚好反过来。

但是由于python好像还没有一个函数是可以直接体现大堆的,所以我们在使用的时候还是要以小堆的那个heapify函数来,把各个元素添个负号就可以了。

7.3 对比总结

数据结构 特点 Python 实现 典型场景
队列 FIFO(先进先出) queue.Queuedeque 任务调度、BFS
LIFO(后进先出) listdeque 函数调用、DFS
完全二叉树,高效取极值 heapq(最小堆) 优先队列、堆排序

8.小结

本文系统地介绍了Python中多种重要数据结构,是python数据结构第一篇文章的补充。从字典的各类操作与嵌套,到字符串的丰富函数,再到数组、链表的特点与应用,以及队列、栈、堆的数据特性,希望对Python初学者理解和掌握数据结构有一定帮助。


网站公告

今日签到

点亮在社区的每一天
去签到