一. 数据结构与算法
- 数据结构与算法是计算机科学的两个核心概念,它们在程序设计、系统开发和软件工程中发挥着重要作用。以下是对两个概念的简要介绍:
数据结构
-
数据结构是组织、存储和管理数据的方式。选择合适的数据结构可以提高程序的效率和可维护性。常见的数据结构包括:
-
- 数组:一组相同类型的元素的集合,可以通过索引快速访问。
-
- 链表:由节点组成的线性数据结构,每个节点包含数据和指向下一个节点的指针。
-
- 栈:一种后进先出(LIFO)的数据结构,允许在一端插入和删除元素。
-
- 队列:一种先进先出(FIFO)的数据结构,允许在一端插入元素,在另一端删除元素。
-
- 哈希表:通过哈希函数将键映射到值,提供快速的查找、插入和删除操作。
-
- 树:一种分层数据结构,最常用的是二叉树和二叉搜索树,也有平衡树(如AVL树和红黑树)。
-
- 图:由节点(顶点)和边组成,可以表示各种关系。
-
算法
算法是解决特定问题的一系列步骤或规则。算法的效率和性能通常通过时间复杂度和空间复杂度来衡量。常见的算法类型包括:
- 排序算法:对于数据进行排序的算法,如快速排序、归并排序和冒泡排序。
- 搜索算法:在数据结构中查找特定元素的算法,如二分搜索和线性搜索。
- 图算法:用于操作图的算法,如深度优先搜索(DFS)、广度优先搜索(BFS)、最短路径算法(如Dijkstra算法)和最小生成树算法(如Kruskal和Prim算法)。
- 动态规划:解决复杂问题的方法,通过将问题分解为更小的子问题并保存其结果来改善效率。
- 贪心算法:通过局部最优解来构建全局最优解的方法,通常用于优化问题。
二.列表
创建一个空的列表
list = [ ]
L = ['北京', '上海', '广州', '西安'] # 创建一个含有4个字符串的列表
- 创建列表的函数
list()
list(可迭代对象)
- 列表的常用方法
运算 | 结果 |
---|---|
s.index(x[, i[, j]]) | x 在 s 中首次出现项的索引号(索引号在 i 或其后且在 j 之前) |
s.count(x) | x 在 s 中出现的总次数 |
s.append(x) | 将 x 添加到序列的末尾 (等同于 s[len(s):len(s)] = [x] ) |
s.clear() | 从 s 中移除所有项 (等同于 del s[:] ) |
s.copy() | 创建 s 的浅拷贝 (等同于 s[:] ) |
s.extend(t) 或 s += t | 用 t 的内容扩展 s (基本上等同于 s[len(s):len(s)] = t ) |
s.insert(i, x) | 在由 i 给出的索引位置将 x 插入 s (等同于 s[i:i] = [x] ) |
s.pop([i]) | 提取在 i 位置上的项,并将其从 s 中移除 |
s.remove(x) | 删除 s 中第一个 s[i] 等于 x 的项目。 |
s.reverse() | 就地将列表中的元素逆序。 |
s.sort( key=None, *reverse=False*) | 对列表内的数据进行排序, reverse=False 升序排序,否则是降序排序 |
三.集合
- 创建集合
创建一个空集合必须用 set() 而不是 { },因为 { } 是用来创建一个空字典。
s1 = {10, 20, 30}
print(len(s1))
-
集合(set)没有索引查看的功能。集合是一种无序、不可重复的数据结构,用于存储唯一的元素。由于集合是无序的,所以不能通过索引来访问其中的元素。所以也没有对应的修改功能。
-
常用方法
方法 | 描述 |
---|---|
add() | 为集合添加元素 |
clear() | 移除集合中的所有元素 |
copy() | 拷贝一个集合 |
difference() | 返回多个集合的差集 |
difference_update() | 移除集合中的元素,该元素在指定的集合也存在。 |
discard() | 删除集合中指定的元素 |
intersection() | 返回集合的交集 |
intersection_update() | 返回集合的交集。 |
isdisjoint() | 判断两个集合是否包含相同的元素,如果没有返回 True,否则返回 False。 |
issubset() | 判断指定集合是否为该方法参数集合的子集。 |
issuperset() | 判断该方法的参数集合是否为指定集合的子集 |
pop() | 随机移除元素 |
remove() | 移除指定元素 |
symmetric_difference() | 返回两个集合中不重复的元素集合。 |
symmetric_difference_update() | 移除当前集合中在另外一个指定集合相同的元素,并将另外一个指定集合中不同的元素插入到当前集合中。 |
union() | 返回两个集合的并集 |
update() | 给集合添加元素 |
len() | 计算集合元素个数 |
四.字典
- 字典的创建
字典的表示方式以 {} 括起来, 以英文的冒号 ( : ) 分隔键值对,各键值对之间用逗号(,)分隔
d = {} # 创建空字典
d = {'name': "weimingze", "age": 35}
d = {'a': [1, 2, 3]}
d = {'b': {"bb": 222}}
-
注:
-
键必须是唯一的,但值则不必。
-
值可以取任何数据类型,但键必须是不可变的,如字符串,数字
-
-
字典的方法:
序号 | 函数及描述 |
---|---|
1 | dict.clear()删除字典内所有元素 |
2 | dict.copy()返回一个字典的浅复制 |
3 | dict.fromkeys(seq)创建一个新字典,以序列seq中元素做字典的键,val为字典所有键对应的初始值 |
4 | dict.get(key, default=None)返回指定键的值,如果键不在字典中返回 default 设置的默认值 |
5 | key in dict如果键在字典dict里返回true,否则返回false |
6 | dict.items()以列表返回一个视图对象 |
7 | dict.keys()返回一个视图对象 |
8 | dict.setdefault(key, default=None)和get()类似, 但如果键不存在于字典中,将会添加键并将值设为default |
9 | dict.update(dict2)把字典dict2的键/值对更新到dict里 |
10 | dict.values()返回一个视图对象 |
11 | pop(key,default)删除字典 key(键)所对应的值,返回被删除的值。 |
12 | popitem()返回并删除字典中的最后一对键和值。 |
五.栈–Stack
- 栈(Stack)是一种线性数据结构,它遵循后进先出(LIFO,Last In First Out)的原则。也就是说,最后放入栈中的元素最先被取出。栈的基本操作通常包括入栈(Push)、出栈(Pop)、查看栈顶元素(Peek或Top)和检查栈是否为空(IsEmpty)。
栈的基本操作
- 入栈(Push):将一个元素压入栈顶。
- 出栈(Pop):从栈顶移除一个元素并返回这个元素。
- 查看栈顶元素(Peek/Top):返回栈顶元素,但不移除它。
- 检查栈空(IsEmpty):判断栈是否为空。
栈的特点
-
顺序性:栈中的元素在特定的顺序中排列。
-
限制性:只能在栈顶进行插入和删除操作。
-
内存结构:可以使用数组或链表实现。
-
栈常见的操作
push(element)
: 添加一个新元素到栈顶位置.pop()
:移除栈顶的元素,同时返回被移除的元素。peek()
:返回栈顶的元素,不对栈做任何修改(这个方法不会移除栈顶的元素,仅仅返回它)。isEmpty()
:如果栈里没有任何元素就返回true
,否则返回false
。clear()
:移除栈里的所有元素。size()
:返回栈里的元素个数。这个方法和数组的length
属性很类似。
class Stack:
def __init__(self, size):
self.size = size
self.stack = []
self.top = -1
def is_empty(self): #isEmpty():如果栈里没有任何元素就返回`true`,否则返回`false`。
return self.top == -1
def is_full(self):
return self.top == self.size - 1
# push(element): 添加一个新元素到栈顶位置.
def push(self, item):
if not self.is_full():
self.stack.append(item)
self.top += 1
else:
print("栈已满")
#pop():移除栈顶的元素,同时返回被移除的元素。
def pop(self):
if not self.is_empty():
item = self.stack.pop()
self.top -= 1
return item
else:
print("栈为空")
# peek():返回栈顶的元素,不对栈做任何修改(这个方法不会移除栈顶的元素,仅仅返回它)。
def peek(self):
if not self.is_empty():
return self.stack[self.top]
else:
print("栈为空")
六.队列
队列(Queue)是一种数据结构,遵循先进先出(FIFO,First In First Out)的原则。这意味着最先放入队列的元素最先被取出。队列的基本操作包括入队(Enqueue)、出队(Dequeue)、查看队头元素(Front或Peek)和检查队列是否为空(IsEmpty)。
队列的基本操作
- 入队(Enqueue):将一个元素添加到队尾。
- 出队(Dequeue):从队头移除一个元素并返回这个元素。
- 查看队头元素(Front/Peek):返回队头元素,但不移除它。
- 检查队列空(IsEmpty):判断队列是否为空。
队列的特点
- 顺序性:队列中的元素按特定的顺序排列。
- 限制性:只能在队尾进行插入操作,而只能从队头进行删除操作。
- 内存结构:可以采用数组或链表实现。
队列常见的操作
enqueue(element)
:向队列尾部添加一个(或多个)新的项。dequeue()
:移除队列的第一(即排在队列最前面的)项,并返回被移除的元素。front()
:返回当前队列中第一个元素,也将是最先被移除的元素。队列不做任何变动(不移除元素,只返回元素信息——与Stack
类的peek
方法非常类似)。isEmpty()
:如果队列中不包含任何元素,返回true
,否则返回false
。size()
:返回队列包含的元素个数,与数组的length
属性类似.
队列的实现
-
基于数组的实现:
- 使用固定大小的数组来存储队列元素。
- 需要注意队满和队空情况,以避免溢出或下溢。
- 还需要记录队头和队尾的位置。
class Queue: def __init__(self, size): self.size = size self.queue = [None] * size self.front = self.rear = -1 def is_empty(self): return self.front == -1 def is_full(self): return (self.rear + 1) % self.size == self.front def enqueue(self, item): if not self.is_full(): if self.is_empty(): self.front = self.rear = 0 else: self.rear = (self.rear + 1) % self.size self.queue[self.rear] = item else: print("队列已满") def dequeue(self): if not self.is_empty(): item = self.queue[self.front] if self.front == self.rear: self.front = self.rear = -1 # Queue is empty after dequeuing else: self.front = (self.front + 1) % self.size return item else: print("队列为空") def peek(self): if not self.is_empty(): return self.queue[self.front] else: print("队列为空")
七.链表
- 链表(Linked List)是一种基本的数据结构,用于存储一系列的数据元素。与数组不同,链表中的元素不需要在内存中连续存储,而是通过指针(或引用)相互链接。链表的每个元素称为节点(Node),每个节点不仅存储数据,还存储一个或多个指向其他节点的指针。
链表的基本结构
一个链表通常由以下几个部分组成:
- 节点(Node):
- 每个节点包含两个主要部分:
- 数据域(Data):存储节点的数据。
- 指针域(Pointer):指向下一个节点的指针(在单向链表中),或指向前后节点的指针(在双向链表中)。
- 头指针(Head):
- 链表的起始节点的指针。如果链表为空,头指针通常为
None
。
链表的类型
-
单向链表(Singly Linked List):
- 每个节点只包含一个指向下一个节点的指针。
- 可以从头节点开始向后遍历,但不能反向遍历。
-
双向链表(Doubly Linked List):
- 每个节点包含两个指针:一个指向下一个节点,一个指向前一个节点。
- 支持从头到尾和从尾到头的双向遍历。
-
循环链表(Circular Linked List):
- 最后一个节点的指针指向头节点,而不是
None
,形成一个环。 - 可以是单向或双向的循环链表。
- 最后一个节点的指针指向头节点,而不是
链表的基本操作
-
插入操作:
- 在链表的头部、尾部或特定位置插入节点。
-
删除操作:
- 删除特定值的节点,或删除指定位置的节点。
-
查找操作:
- 根据值查找节点并返回其位置。
-
遍历操作:
- 从头部访问链表中的每个节点,通常用于打印链表中的元素。
链表的优缺点
优点:
- 动态大小:链表可以根据需要动态增长或缩小,适合需要频繁插入和删除的场景。
- 不需要连续内存:在内存中可以散布存储,避免了数组需要预先分配固定大小的问题。
缺点:
- 随机访问性能差:由于元素不在连续内存中,访问特定位置的元素时需要线性查找,时间复杂度为 O(n)。
- 额外的内存开销:每个节点都需要额外的内存来存储指针,尤其在存储大量数据时,开销比较明显。
链表常见的操作
-
append(element)
:向列表尾部添加一个新的项 -
insert(position, element)
:向列表的特定位置插入一个新的项。 -
remove(element)
:从列表中移除一项。 -
indexOf(element)
:返回元素在链表中的索引。如果列表中没有该元素则返回-1
。 -
removeAt(position)
:从列表的特定位置移除一项。 -
isEmpty()
:如果链表中不包含任何元素,返回true
,如果链表长度大于0则返回false
。 -
size()
:返回链表包含的元素个数。与数组的length
属性类似。
# 创建节点类,用于存放每个节点信息
class Node():
def __init__(self, el):
self.el = el
self.next = None
# 创建链表类
class LinkedList():
# 链表的类 初始化数据个数和头部节点
def __init__(self):
self.length = 0
self.head = None
# 根据元素获取链表中的位置
def indexof(self, el):
# 定义变量, 保存信息
current = self.head
index = 0
# 找到元素所在的位置
while (current != None):
if (current.el == el):
return index
index += 1
current = current.next
return -1 # 来到这个位置, 说明没有找到, 则返回-1
# 给链表尾部追加元素
def append(self, el):
# 根据传入的数据创建新节点
newNode = Node(el)
# 如果是空链表直接把头部节点指定为新节点
if (self.head == None):
self.head = newNode
# 链表不为空
else:
# 定义变量,保存当前头部节点
current = self.head
# 循环找到链表中最后一个节点
while (current.next != None):
current = current.next
# 把最后一个节点的next设置为新节点
current.next = newNode
self.length += 1 # 增加链表长度
# 根据下标插入节点
def insert(self, position, element):
# 检测越界问题: 越界插入失败
if (position < 0 or position > self.length):
return False
# 定义变量, 保存信息
newNode = Node(element)
current = self.head
previous = None
index = 0
if (position == 0):
newNode.next = current
self.head = newNode
else:
while (index < position):
previous = current
current = current.next
index += 1
newNode.next = current
previous.next = newNode
self.length += 1
return True
def removeAt(self, position):
# 检测越界问题: 越界移除失败, 返回None
if (position < 0 or position >= self.length):
return None
# 定义变量, 保存信息
current = self.head
previous = None
index = 0
# 判断是否是移除第一项
if (position == 0):
self.head = current.next
else:
while (index < position):
previous = current
current = current.next
index += 1
previous.next = current.next
self.length -= 1
return current.el
# 根据元素删除信息
def remove(self, el):
index = self.indexof(el)
return self.removeAt(index)
# 判断链表是否为空
def isEmpty(self):
return self.length == 0
# 获取链表的长度
def size(self):
return self.length
list1 = LinkedList()
for i in range(6):
list1.append(i)
print(list1.indexof("hello")) # -1
print(list1.indexof(4)) # 4
print(list1.remove(4)) # 4
print(list1.size()) # 5