栈是一种后进先出(LIFO, Last In First Out)的数据结构。可以将其想象成一叠盘子,最后放上去的盘子最先被取走。
Push(入栈):将元素添加到栈顶。
Pop(出栈):移除栈顶的元素。
Peek(查看栈顶):查看栈顶的元素,但不移除。
isEmpty(判空):检查栈是否为空。
链表是一种线性数据结构,由一系列节点组成,每个节点包含数据和指向下一个节点的指针(或引用)。
单向链表:每个节点只有一个指向下一个节点的指针。
双向链表:每个节点有两个指针,分别指向前一个节点和后一个节点。
循环链表:最后一个节点指向第一个节点,形成一个环。
插入(Insert):在链表的指定位置插入一个新节点。
删除(Delete):从链表中删除指定节点。
查找(Search):在链表中查找指定值的节点。
遍历(Traverse):访问链表中的每个节点。
在单向链表中,删除节点需要找到待删除节点的前一个节点。如果只知道待删除节点,而无法直接访问前一个节点,删除操作会变得复杂。
问题描述
假设我们有一个单向链表,现在要删除一个已知节点,但无法直接访问该节点的前一个节点。
解决方案
1. 使用双向链表:双向链表的每个节点都有指向前一个节点的指针,可以方便地找到前一个节点。
2. 遍历链表:从头节点开始遍历链表,找到待删除节点的前一个节点,然后执行删除操作。
以下是一个单向链表的 Python 实现,包括插入、删除和显示操作。
class Node:
def __init__(self, data=None):
self.data = data
self.next = None
class LinkedList:
def __init__(self):
self.head = Node() # 初始化一个虚拟头节点
# 在链表尾部插入数据
def append(self, data):
node = Node(data)
# 如果链表为空,则直接插入
if self.head.next is None:
self.head.next = node
else:
# 做遍历,获取第一个数据节点
temp = self.head.next
# 判断数据节点后面是否还存在节点,如果存在节点指针向后移动。否则认为是最后一个节点,将新节点链接到最后一个节点后边
while temp.next is not None:
temp = temp.next
temp.next = node
def prepend(self, data):
node = Node(data)
# 将新节点插入到虚拟头节点之后
node.next = self.head.next
self.head.next = node
def display(self):
if self.head.next is None:
print("LinkedList is empty")
return
temp = self.head.next
while temp is not None:
print(temp.data)
temp = temp.next
def remove(self, data):
if self.head.next is None:
raise Exception('LinkedList is empty')
temp = self.head
while temp.next is not None and temp.next.data != data:
temp = temp.next
if temp.next is not None:
temp.next = temp.next.next
if __name__ == '__main__':
ls = LinkedList()
ls.append(4)
ls.append(5)
ls.prepend(3)
ls.display() # 输出: 3 4 5
ls.remove(4)
ls.display() # 输出: 3 5
二叉查找树(BST)是一种特殊的二叉树,具有以下性质:
1. 每个节点都有一个键值(key)。
2. 左子树中的所有节点的键值都小于该节点的键值。
3. 右子树中的所有节点的键值都大于该节点的键值。
4. 左子树和右子树也分别是二叉查找树。
def insert(self, key):
if self.root is None:
self.root = TreeNode(key)
else:
self._insert(self.root, key)
def _insert(self, node, key):
if key < node.key:
if node.left is None:
node.left = TreeNode(key)
else:
self._insert(node.left, key)
elif key > node.key:
if node.right is None:
node.right = TreeNode(key)
else:
self._insert(node.right, key)
def delete(self, key):
self.root = self._delete(self.root, key)
def _delete(self, node, key):
if node is None:
return node
if key < node.key:
node.left = self._delete(node.left, key)
elif key > node.key:
node.right = self._delete(node.right, key)
else:
# 找到要删除的节点
if node.left is None:
return node.right
elif node.right is None:
return node.left
temp = self._min_value_node(node.right)
node.key = temp.key
node.right = self._delete(node.right, temp.key)
return node
def search(self, key):
return self._search(self.root, key)
def _search(self, node, key):
if node is None or node.key == key:
return node
if key < node.key:
return self._search(node.left, key)
return self._search(node.right, key)
3.2.4 遍历
中序遍历:左子树 -> 根节点 -> 右子树。
前序遍历:根节点 -> 左子树 -> 右子树。
后序遍历:左子树 -> 右子树 -> 根节点。
队列是一种先进先出(FIFO, First In First Out)的数据结构。Python 标准库中的 `queue` 模块提供了多种队列实现,包括普通队列、双端队列和优先队列。所以只要记得基本的模块是queue就好
import queue
q = queue.Queue()
q.put(1)
q.put(2)
print(q.get()) # 输出: 1
from collections import deque
q = deque()
q.append(1)
q.appendleft(2)
print(q.pop()) # 输出: 1
print(q.popleft()) # 输出: 2
import queue
q = queue.PriorityQueue()
q.put((1, 'Task 1'))
q.put((3, 'Task 3'))
print(q.get()) # 输出: (1, 'Task 1')
在多线程环境中,操作数据结构时可能会遇到线程安全问题。例如,`heapq` 模块不是线程安全的,如果多个线程同时操作堆,可能会导致数据不一致。为了解决这个问题,可以使用锁(Lock)来确保线程安全。
import heapq
import threading
heap = []
lock = threading.Lock()
def safe_push(item):
with lock:
heapq.heappush(heap, item)
def safe_pop():
with lock:
return heapq.heappop(heap)
1. 数据筛选:loc 的妙用
`loc` 方法是 Pandas 中用于基于标签(label)进行数据选择的强大工具。它可以帮助我们根据条件筛选行和列,提取所需的数据子集。
df['Age'] > 30:这是一个布尔索引,用于选择 'Age' 列中大于 30 的行。
['ID', 'Name']:指定要选择的列。
df.loc[...]:使用 `loc` 方法将两者结合,筛选出满足条件的 'ID' 和 'Name'。
df = pd.DataFrame(data)
print("公司员工的基本数据:\n",df)
df2 = df.loc[df['Age'] > 30, ['ID', 'Name']]
print("年龄大于30岁的员工:\n", df2)
2. 数据删除与添加:drop`和 concat
在数据处理过程中,经常需要删除不需要的行或列,并添加新的数据。 Pandas 提供了 `drop` 方法用于删除数据,`concat` 方法用于连接数据。
df.drop(df[df['ID'] == 2].index):首先找到 'ID' 列中值为 2 的行,然后使用 `drop` 方法删除这些行。`.index` 用于获取要删除的行的索引。
pd.concat([df, pd.DataFrame([new_student])], ignore_index=True)`:创建一个包含新学生信息的 DataFrame,然后使用 `pd.concat` 方法将其添加到原始 DataFrame 中。`ignore_index=True` 参数用于重新生成索引。
df2= df.drop(df[df['ID'] == 2].index)
print("删除ID为2的学生记录\n",df2)
ad_student = {'ID': 6, 'Name': 'Frank', 'Score': 85}
#添加新行axis: 指定连接的轴,0 或 'index' 表示按行连接,1 或 'columns' 表示按列连接。
df3 = pd.concat([df2, pd.DataFrame([ad_student])], axis=0)
3. 数据列操作:添加与删除列
除了删除行,我们还可以添加或删除列。
df['Job Title'] = ['Manager', 'Analyst', 'Developer']`:创建一个新的列 'Job Title' 并为其赋值。
df.drop('Department', axis=1)`:删除 'Department' 列。`axis=1` 参数指定删除列,而不是行。
#添加新列,直接赋值添加
df['Job Title'] = ['Manager', 'Analyst', 'Developer']
print("添加职工头衔后的信息\n",df)
#axis指定删除的方向。axis=0 或 axis='index' 表示删除行,axis=1 或 axis='columns' 表示删除列。
df = df.drop('Department', axis=1)
print("\n删除'Department'后的信息:\n", df)
4. 数据分组与聚合:groupby
`groupby` 方法是 Pandas 中用于数据分组和聚合的关键工具。它可以将数据按照指定的列进行分组,然后对每个组应用聚合函数(如平均值、总和、计数等)。
df.groupby('Team'):按 'Team' 列对数据进行分组。
['Age']`:选择要计算平均值的 'Age' 列。
.mean()`:计算每个组的平均值。
df2 = df.groupby('Team')['Age'].mean()
5. 数据合并:merge
`merge` 函数用于将两个 DataFrame 按照指定的键进行合并。它可以执行多种类型的连接,如内连接、外连接、左连接和右连接。
pd.merge(left_df, right_df, on='Key', how='inner')`:使用 'Key' 列作为连接键,执行内连接。`how='inner'` 参数指定只保留两个 DataFrame 中都存在的键。
m_df = pd.merge(l_df, r_df, on='Key', how='inner')
6. 问题 1:** 我们使用 `groupby` 方法按 'Product' 列分组,然后分别计算每组 'Sales' 列的总和和平均值。
* **问题 2:** 我们使用 `groupby` 方法按 'Region' 和 'Product' 列分组,然后计算每组 'Sales' 列的平均值。
product_sales_sum = df.groupby('Product')['Sales'].sum()
product_sales_mean = df.groupby('Product')['Sales'].mean()
# 将两个series合并为一个dataframe
product_sales = pd.DataFrame({'sum': product_sales_sum, 'mean': product_sales_mean})
# product_sales = df.groupby('Product')['Sales'].agg(['sum', 'mean'])
print("\n按商品分组的总销售额和平均销售额:\n", product_sales)
# 2. 按照地区分组,求出该地区每个商品的平均销售额
region_product_sales = df.groupby(['Region', 'Product'])['Sales'].mean()