本文深入探讨了数据结构高级入门的相关知识,涵盖了链表、树、图和哈希表等多种数据结构的详细讲解和应用示例。通过具体代码实例,帮助读者理解每种数据结构的工作原理和应用场景。文章还介绍了数据结构选择与优化的策略,以提高程序性能和可维护性。数据结构高级入门是每个程序员必须掌握的关键技能。
数据结构高级入门:简洁教程 数据结构基础回顾基本概念
数据结构是计算机科学和编程中的一个重要领域,它研究数据的组织、管理和操作方式。数据结构的设计和选择对算法的效率有着至关重要的影响。常见的数据结构包括数组、链表、栈、队列、树、图等。每种数据结构都有其特定的应用场景和优缺点。
常见数据类型
-
整型:整数类型用于存储整数值,例如
int
。a = 42 print(a) # 输出 42
-
浮点型:浮点数类型用于存储小数,例如
float
。b = 3.14 print(b) # 输出 3.14
-
字符串:字符串类型用于存储文本数据,例如
str
。c = "Hello, world!" print(c) # 输出 Hello, world!
-
布尔型:布尔类型用于表示真/假,例如
bool
。d = True print(d) # 输出 True
单向链表
单向链表是一种线性数据结构,每个节点包含一个数据元素和一个指向下一个节点的指针。每个节点都指向链表中的下一个节点,最后一个节点指向 None
。
class Node:
def __init__(self, data):
self.data = data
self.next = None
class LinkedList:
def __init__(self):
self.head = None
def append(self, data):
new_node = Node(data)
if not self.head:
self.head = new_node
return
last = self.head
while last.next:
last = last.next
last.next = new_node
def display(self):
values = []
current = self.head
while current:
values.append(current.data)
current = current.next
print(values)
linked_list = LinkedList()
linked_list.append(1)
linked_list.append(2)
linked_list.append(3)
linked_list.display() # 输出 [1, 2, 3]
双向链表
双向链表与单向链表类似,但每个节点包含一个指向下一个节点的指针和一个指向前一个节点的指针。双向链表允许在链表中前进和后退。
class Node:
def __init__(self, data):
self.data = data
self.next = None
self.prev = None
class DoublyLinkedList:
def __init__(self):
self.head = None
def append(self, data):
new_node = Node(data)
if not self.head:
self.head = new_node
return
last = self.head
while last.next:
last = last.next
last.next = new_node
new_node.prev = last
def display(self):
values = []
current = self.head
while current:
values.append(current.data)
current = current.next
print(values)
doubly_linked_list = DoublyLinkedList()
doubly_linked_list.append(1)
doubly_linked_list.append(2)
doubly_linked_list.append(3)
doubly_linked_list.display() # 输出 [1, 2, 3]
循环链表
循环链表是一种特殊的链表,最后一个节点的指针指向链表的第一个节点,形成一个环。循环链表特别适用于需要循环遍历的数据结构。
class Node:
def __init__(self, data):
self.data = data
self.next = None
class CircularLinkedList:
def __init__(self):
self.head = None
def append(self, data):
new_node = Node(data)
if not self.head:
self.head = new_node
new_node.next = self.head
return
last = self.head
while last.next != self.head:
last = last.next
last.next = new_node
new_node.next = self.head
def display(self):
values = []
current = self.head
if self.head:
values.append(current.data)
current = current.next
while current != self.head:
values.append(current.data)
current = current.next
print(values)
circular_linked_list = CircularLinkedList()
circular_linked_list.append(1)
circular_linked_list.append(2)
circular_linked_list.append(3)
circular_linked_list.display() # 输出 [1, 2, 3]
树结构的进阶学习
二叉树
二叉树是一种特殊的树结构,每个节点最多有两个子节点,分别称为左子节点和右子节点。二叉树是许多数据结构和算法的基础。
class TreeNode:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
class BinarySearchTree:
def __init__(self):
self.root = None
def insert(self, data):
if not self.root:
self.root = TreeNode(data)
return
self._insert(self.root, data)
def _insert(self, node, data):
if data < node.data:
if not node.left:
node.left = TreeNode(data)
else:
self._insert(node.left, data)
else:
if not node.right:
node.right = TreeNode(data)
else:
self._insert(node.right, data)
def inorder_traversal(self):
return self._inorder_traversal(self.root)
def _inorder_traversal(self, node):
if node:
return self._inorder_traversal(node.left) + [node.data] + self._inorder_traversal(node.right)
return []
bst = BinarySearchTree()
bst.insert(5)
bst.insert(3)
bst.insert(8)
bst.insert(1)
bst.insert(4)
print(bst.inorder_traversal()) # 输出 [1, 3, 4, 5, 8]
平衡树
平衡树是一种特殊的二叉树,其左右子树的高度差不超过1。平衡树的典型代表是AVL树和红黑树。
AVL树
AVL树是一种自平衡二叉搜索树,其中任何节点的两个子树的高度差最多为1。AVL树通过旋转操作来保持平衡。
class AVLNode:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
self.height = 1
class AVLTree:
def insert(self, root, key):
if not root:
return AVLNode(key)
elif key < root.data:
root.left = self.insert(root.left, key)
else:
root.right = self.insert(root.right, key)
root.height = 1 + max(self.get_height(root.left), self.get_height(root.right))
balance = self.get_balance(root)
if balance > 1 and key < root.left.data:
return self.right_rotate(root)
if balance < -1 and key > root.right.data:
return self.left_rotate(root)
if balance > 1 and key > root.left.data:
root.left = self.left_rotate(root.left)
return self.right_rotate(root)
if balance < -1 and key < root.right.data:
root.right = self.right_rotate(root.right)
return self.left_rotate(root)
return root
def left_rotate(self, z):
y = z.right
T2 = y.left
y.left = z
z.right = T2
z.height = 1 + max(self.get_height(z.left), self.get_height(z.right))
y.height = 1 + max(self.get_height(y.left), self.get_height(y.right))
return y
def right_rotate(self, z):
y = z.left
T3 = y.right
y.right = z
z.left = T3
z.height = 1 + max(self.get_height(z.left), self.get_height(z.right))
y.height = 1 + max(self.get_height(y.left), self.get_height(y.right))
return y
def get_height(self, root):
if not root:
return 0
return root.height
def get_balance(self, root):
if not root:
return 0
return self.get_height(root.left) - self.get_height(root.right)
avl_tree = AVLTree()
root = None
root = avl_tree.insert(root, 10)
root = avl_tree.insert(root, 20)
root = avl_tree.insert(root, 30)
root = avl_tree.insert(root, 40)
root = avl_tree.insert(root, 50)
root = avl_tree.insert(root, 25)
堆
堆是一种特殊的完全二叉树,其中每个节点的值都大于或等于(最大堆)或小于或等于(最小堆)其子节点的值。堆常用于实现优先队列。
最大堆
最大堆是一种特殊的完全二叉树,每个节点的值都大于或等于其子节点的值。
def heapify(arr, n, i):
largest = i
left = 2 * i + 1
right = 2 * i + 2
if left < n and arr[i] < arr[left]:
largest = left
if right < n and arr[largest] < arr[right]:
largest = right
if largest != i:
arr[i], arr[largest] = arr[largest], arr[i]
heapify(arr, n, largest)
def build_max_heap(arr):
n = len(arr)
for i in range(n // 2 - 1, -1, -1):
heapify(arr, n, i)
def heap_sort(arr):
n = len(arr)
build_max_heap(arr)
for i in range(n - 1, 0, -1):
arr[i], arr[0] = arr[0], arr[i]
heapify(arr, i, 0)
arr = [12, 11, 13, 5, 6, 7]
heap_sort(arr)
print(arr) # 输出 [5, 6, 7, 11, 12, 13]
图结构的入门
图的基本概念
图是一种非线性的数据结构,由节点(顶点)和边(连接顶点的线)组成。图可以是无向图(边没有方向)或有向图(边有方向)。
图的表示方法
图的表示方法主要有两种:邻接矩阵和邻接表。
邻接矩阵
邻接矩阵是一种使用二维数组表示图的方法。矩阵的行和列分别表示顶点,矩阵元素表示顶点之间的连接。
class Graph:
def __init__(self, vertices):
self.V = vertices
self.graph = [[0 for column in range(vertices)] for row in range(vertices)]
def add_edge(self, u, v):
self.graph[u][v] = 1
self.graph[v][u] = 1
def print_matrix(self):
for row in self.graph:
print(row)
g = Graph(4)
g.add_edge(0, 1)
g.add_edge(0, 2)
g.add_edge(1, 2)
g.add_edge(2, 3)
g.print_matrix()
邻接表
邻接表是一种使用链表表示图的方法。每个顶点都关联一个链表,链表包含与该顶点相连的其他顶点。
class AdjNode:
def __init__(self, v):
self.vertex = v
self.next = None
class Graph:
def __init__(self, vertices):
self.V = vertices
self.graph = [None] * vertices
def add_edge(self, src, dest):
node = AdjNode(dest)
node.next = self.graph[src]
self.graph[src] = node
# For undirected graph, add an edge from dest to src as well
node = AdjNode(src)
node.next = self.graph[dest]
self.graph[dest] = node
def print_graph(self):
for i in range(self.V):
print(f"Adjacency list of vertex {i}")
temp = self.graph[i]
while temp:
print(f" -> {temp.vertex}", end="")
temp = temp.next
print()
g = Graph(4)
g.add_edge(0, 1)
g.add_edge(0, 2)
g.add_edge(1, 2)
g.add_edge(2, 3)
g.print_graph()
常见的图算法
深度优先搜索(DFS)
深度优先搜索是一种遍历图的方法,从一个顶点开始,尽可能深地访问相邻的顶点。
def dfs_util(graph, v, visited):
visited[v] = True
print(v, end=' ')
for i in graph[v]:
if not visited[i]:
dfs_util(graph, i, visited)
def dfs(graph, v):
visited = [False] * len(graph)
dfs_util(graph, v, visited)
graph = [
[1, 2],
[0, 2],
[0, 1, 3],
[2]
]
dfs(graph, 0)
广度优先搜索(BFS)
广度优先搜索是一种遍历图的方法,从一个顶点开始,按层次访问相邻的顶点。
from collections import deque
def bfs_util(graph, v, visited):
visited[v] = True
queue = deque([v])
while queue:
v = queue.popleft()
print(v, end=' ')
for i in graph[v]:
if not visited[i]:
visited[i] = True
queue.append(i)
def bfs(graph, v):
visited = [False] * len(graph)
bfs_util(graph, v, visited)
graph = [
[1, 2],
[0, 2],
[0, 1, 3],
[2]
]
bfs(graph, 0)
最短路径算法(Dijkstra算法)
Dijkstra算法用于计算从源节点到其他所有节点的最短路径。
import sys
def dijkstra(graph, src):
n = len(graph)
dist = [sys.maxsize] * n
dist[src] = 0
visited = [False] * n
pq = [(0, src)]
while pq:
dist_u, u = min(pq)
pq.remove((dist_u, u))
visited[u] = True
for v in range(n):
if graph[u][v] and not visited[v]:
new_dist = dist_u + graph[u][v]
if new_dist < dist[v]:
dist[v] = new_dist
pq.append((new_dist, v))
return dist
graph = [
[0, 4, 0, 0, 0, 0, 0, 8, 0],
[4, 0, 8, 0, 0, 0, 0, 11, 0],
[0, 8, 0, 7, 0, 0, 2, 0, 0],
[0, 0, 7, 0, 9, 14, 0, 0, 0],
[0, 0, 0, 9, 0, 10, 0, 0, 0],
[0, 0, 0, 14, 10, 0, 2, 0, 0],
[0, 0, 2, 0, 0, 2, 0, 1, 6],
[8, 11, 0, 0, 0, 0, 1, 0, 7],
[0, 0, 0, 0, 0, 0, 6, 7, 0]
]
print(dijkstra(graph, 0)) # 输出 [0, 4, 12, 19, 21, 11, 9, 8, 14]
哈希表的详解
基本原理
哈希表是一种数据结构,用于存储键值对。哈希表通过哈希函数将键映射到一个地址,从而实现快速的插入、删除和查找操作。
冲突解决方法
哈希冲突是指不同的键通过哈希函数映射到同一个地址。常见的冲突解决方法包括链地址法和开放地址法。
链地址法
链地址法通过在每个地址上使用一个链表来解决冲突。当发生冲突时,新的节点被添加到链表中。
class ListNode:
def __init__(self, key, value):
self.key = key
self.value = value
self.next = None
class HashTable:
def __init__(self, capacity):
self.capacity = capacity
self.table = [None] * capacity
def _hash(self, key):
return hash(key) % self.capacity
def put(self, key, value):
index = self._hash(key)
if not self.table[index]:
self.table[index] = ListNode(key, value)
else:
current = self.table[index]
while current.next:
if current.key == key:
current.value = value
return
current = current.next
current.next = ListNode(key, value)
def get(self, key):
index = self._hash(key)
current = self.table[index]
while current:
if current.key == key:
return current.value
current = current.next
return None
hash_table = HashTable(10)
hash_table.put("apple", 5)
hash_table.put("banana", 10)
hash_table.put("cherry", 15)
print(hash_table.get("apple")) # 输出 5
print(hash_table.get("banana")) # 输出 10
print(hash_table.get("cherry")) # 输出 15
开放地址法
开放地址法通过探查其他地址来解决冲突。当发生冲突时,使用一个探查函数来找到下一个可用地址。
class HashTable:
def __init__(self, capacity):
self.capacity = capacity
self.table = [None] * capacity
def _hash(self, key):
return hash(key) % self.capacity
def _probe(self, key, i):
return (self._hash(key) + i) % self.capacity
def put(self, key, value):
for i in range(self.capacity):
index = self._probe(key, i)
if not self.table[index]:
self.table[index] = (key, value)
return
if self.table[index][0] == key:
self.table[index] = (key, value)
return
def get(self, key):
for i in range(self.capacity):
index = self._probe(key, i)
if not self.table[index]:
return None
if self.table[index][0] == key:
return self.table[index][1]
return None
hash_table = HashTable(10)
hash_table.put("apple", 5)
hash_table.put("banana", 10)
hash_table.put("cherry", 15)
print(hash_table.get("apple")) # 输出 5
print(hash_table.get("banana")) # 输出 10
print(hash_table.get("cherry")) # 输出 15
实际应用案例
哈希表在实际应用中被广泛使用,例如数据库中的索引、缓存系统中的数据存储和查找、编译器中的符号表等。
class Cache:
def __init__(self, capacity):
self.capacity = capacity
self.cache = {}
def put(self, key, value):
if len(self.cache) >= self.capacity:
self.cache.pop(next(iter(self.cache)))
self.cache[key] = value
def get(self, key):
return self.cache.get(key, None)
cache = Cache(3)
cache.put("apple", 5)
cache.put("banana", 10)
cache.put("cherry", 15)
cache.put("date", 20)
print(cache.get("apple")) # 输出 None
print(cache.get("banana")) # 输出 10
print(cache.get("cherry")) # 输出 15
print(cache.get("date")) # 输出 20
数据结构在实际问题中的应用
典型问题案例分析
- LRU缓存
LRU(Least Recently Used)缓存是一种常用的数据结构,用于缓存最近使用的数据。当缓存满时,最近最少使用的数据将被移除。
from collections import OrderedDict
class LRUCache:
def __init__(self, capacity):
self.cache = OrderedDict()
self.capacity = capacity
def get(self, key):
if key not in self.cache:
return -1
self.cache.move_to_end(key)
return self.cache[key]
def put(self, key, value):
if key in self.cache:
self.cache.move_to_end(key)
self.cache[key] = value
if len(self.cache) > self.capacity:
self.cache.popitem(last=False)
cache = LRUCache(3)
cache.put("apple", 5)
cache.put("banana", 10)
cache.put("cherry", 15)
print(cache.get("apple")) # 输出 5
print(cache.get("banana")) # 输出 10
cache.put("date", 20)
print(cache.get("cherry")) # 输出 15
print(cache.get("date")) # 输出 20
print(cache.get("apple")) # 输出 -1
- 拓扑排序
拓扑排序是一种算法,用于将图中的节点按顺序排列,使得有指向关系的节点前在序列中的节点排在后。
from collections import defaultdict, deque
def topological_sort(graph):
in_degree = defaultdict(int)
for node in graph:
for neighbor in graph[node]:
in_degree[neighbor] += 1
queue = deque([node for node in graph if in_degree[node] == 0])
result = []
while queue:
node = queue.popleft()
result.append(node)
for neighbor in graph[node]:
in_degree[neighbor] -= 1
if in_degree[neighbor] == 0:
queue.append(neighbor)
return result
graph = {
'A': ['B', 'C'],
'B': ['D'],
'C': ['D'],
'D': []
}
print(topological_sort(graph)) # 输出 ['A', 'B', 'C', 'D']
数据结构选择与优化策略
在选择和优化数据结构时,需要考虑以下几个因素:
- 时间复杂度:数据结构的操作时间复杂度决定了算法的效率,例如数组的查找操作是 O(1),而链表的查找操作是 O(n)。
- 空间复杂度:数据结构的空间复杂度决定了算法的空间消耗,例如哈希表的空间复杂度通常为 O(n)。
- 应用场景:不同的数据结构适用于不同的应用场景,例如栈适用于递归调用场景,队列适用于任务调度场景,树适用于层级结构场景。
- 并发访问:在多线程环境中,需要考虑数据结构的并发访问安全性,例如使用锁机制保护共享资源。
有效的数据结构选择和优化策略包括:
# 示例代码展示如何选择和优化数据结构
def example_strategy():
# 示例:优化缓存结构
from collections import OrderedDict
class OptimizedCache:
def __init__(self, capacity):
self.capacity = capacity
self.cache = OrderedDict()
def get(self, key):
if key not in self.cache:
return None
self.cache.move_to_end(key)
return self.cache[key]
def put(self, key, value):
if key in self.cache:
self.cache.move_to_end(key)
self.cache[key] = value
if len(self.cache) > self.capacity:
self.cache.popitem(last=False)
cache = OptimizedCache(3)
cache.put("apple", 5)
cache.put("banana", 10)
cache.put("cherry", 15)
print(cache.get("apple")) # 输出 5
print(cache.get("banana")) # 输出 10
cache.put("date", 20)
print(cache.get("cherry")) # 输出 15
print(cache.get("date")) # 输出 20
print(cache.get("apple")) # 输出 None
# 示例:使用动态规划优化递归算法
def fibonacci(n, memo={}):
if n in memo:
return memo[n]
if n <= 1:
return n
memo[n] = fibonacci(n-1, memo) + fibonacci(n-2, memo)
return memo[n]
print(fibonacci(10)) # 输出 55
通过合理选择和优化数据结构,可以显著提高程序的性能和可维护性。
共同学习,写下你的评论
评论加载中...
作者其他优质文章