记录python学习,直到学会基本的爬虫,使用python搭建接口自动化测试就算学会了,在进阶webui自动化,app自动化
python基础5-线性表之顺序表和链表
线性数据结构
线性表
线性表在不同的编程语言和应用场景中有不同的实现和特性,但它们的核心概念是数据元素之间存在线性关系,即每个元素(除了第一个和最后一个)都有一个前驱和一个后继。
性表是一种数据结构,其中数据元素之间存在一对一的线性关系。这意味着除了第一个和最后一个元素外,每个元素都有一个前驱和一个后继。线性表可以是空的,也可以包含任意数量的元素。线性表的特点是:
元素之间存在线性关系,即一对一的关系。
可以是同质的(所有元素类型相同)或异质的(元素类型不同)。
顺序表
顺序表是一种使用连续存储单元依次存储数据元素的线性表。在顺序表中,数据元素在物理位置上是连续的,通常使用数组来实现。顺序表的特点包括:
元素在内存中连续存储。
可以通过计算得到任意位置元素的存储地址(例如,通过索引)。
插入和删除操作可能需要移动元素,这可能导致效率较低。
链表
链表是一种线性表,其中数据元素在内存中不必连续存储,而是通过指针(或引用)相互链接。每个元素包含数据部分和至少一个指针指向序列中的下一个元素。链表的特点包括:
元素在内存中可以是不连续的。
每个节点包含数据和指向下一个节点的指针。
插入和删除操作不需要移动元素,只需要改变指针,因此某些操作可能更高效。
链表可以是单向的(每个节点指向下一个节点)或双向的(每个节点指向前一个和后一个节点)。
链表还是很熟悉啊,单链表和双链表,头插法和尾插法,虽然很少用到,这个可能在后端中都需要熟悉业务以及设计模式才可能用到的。支持离队、插队、也可以使用索引。
常见的线性表数据结构有哪些呢?
虽然咱很少用到,但是也可能会用到,所谓线性:可加性、齐次性、有顺序的即可
数组(Array):数组是最基本的线性表形式,它由相同类型的元素组成,这些元素在内存中连续存储。
示例:一个整数数组 int[] numbers = {1, 2, 3, 4, 5}。
链表(Linked List):链表由一系列节点组成,每个节点包含数据部分和指向下一个节点的指针。
示例:一个单链表,每个节点包含一个整数和一个指向下一个节点的引用。
栈(Stack):栈是一种后进先出(LIFO)的数据结构,只能在一端(栈顶)进行添加和删除操作。
示例:一个用于存储临时变量的栈,每次函数调用时,变量被压入栈顶,函数返回时弹出。
队列(Queue):队列是一种先进先出(FIFO)的数据结构,数据从一端进入(队尾),从另一端离开(队首)。
示例:一个用于任务调度的队列,任务按顺序加入队尾,按顺序从队首取出。
双端队列(Deque):双端队列允许从两端进行插入和删除操作。
示例:一个用于缓存最近访问项目的双端队列,可以快速从两端添加或移除项目。
字符串(String):字符串可以看作是字符的线性序列,每个字符占据一个位置。
示例:一个字符串 “hello”,它由五个字符组成,每个字符都是线性排列的。
动态数组(Dynamic Array):动态数组是一种可以动态扩展和收缩的数组,常见的实现有ArrayList(Java)和vector(C++)。
示例:一个动态数组 ArrayList list = new ArrayList<>();,可以动态添加和删除整数。
向量(Vector):向量是动态数组的一种实现,它提供了动态内存分配和数组操作的功能。
示例:一个向量 std::vector vec;,用于存储整数,并可以动态地增加和删除元素。
线性表之顺序表、链表
顺序表示例
# 顺序表(使用Python列表)
# 顺序表增删改查示例
# 增加操作
def insert_sequence_list(seq_list, index, value):
seq_list.insert(index, value)
# 删除操作
def delete_sequence_list(seq_list, index):
seq_list.pop(index)
# 修改操作
def update_sequence_list(seq_list, index, value):
seq_list[index] = value
# 查询操作
def search_sequence_list(seq_list, value):
return [index for index, elem in enumerate(seq_list) if elem == value]
#这里运用了列表推导式且搭配enumerate格式同时返回序列和对应的索引,注意该参数返回的元组
# 解析:
'''
eq_list:这是一个序列(比如列表、元组等),我们要在这个序列中搜索特定的元素。
enumerate(seq_list):这个函数会遍历seq_list中的每个元素,并返回一个包含元素索引和元素值的元组。例如,如果seq_list是[1, 2, 3],那么enumerate(seq_list)会生成[(0, 1), (1, 2), (2, 3)]。
index, elem in enumerate(seq_list):这里index是元素的索引,elem是元素的值。对于seq_list中的每个元素,enumerate都会产生一对索引和值。
if elem == value:这是一个条件语句,只有当elem(即seq_list中的元素)等于value时,才会执行列表推导式中的表达式。
return [index for index, elem in enumerate(seq_list) if elem == value]:整个表达式的意思是,返回一个包含所有满足elem == value条件的元素的索引的列表。
'''
# 示例
seq_list = [1, 2, 3, 4, 5]
insert_sequence_list(seq_list, 2, 10) # 在索引2的位置插入10
print(seq_list) # 输出: [1, 2, 10, 3, 4, 5]
delete_sequence_list(seq_list, 2) # 删除索引2的元素
print(seq_list) # 输出: [1, 2, 3, 4, 5]
update_sequence_list(seq_list, 1, 20) # 将索引1的元素修改为20
print(seq_list) # 输出: [1, 20, 3, 4, 5]
search_results = search_sequence_list(seq_list, 4) # 查询值为4的元素索引
print(search_results) # 输出: [3]
顺序表回显:
[1, 2, 10, 3, 4, 5] 在索引2的位置插入10
[1, 2, 3, 4, 5] 删除索引2的元素
[1, 20, 3, 4, 5] 将索引1的元素修改为20
[3] 查询值为4的元素索引
链表示例
# 链表(自定义链表节点类)
# 链表节点类
class ListNode:
def __init__(self, value=0, next=None):
self.value = value
self.next = next
# 链表类
class LinkedList:
def __init__(self):
self.head = None
# 增加操作
def insert(self, value):
new_node = ListNode(value)
new_node.next = self.head
self.head = new_node
# 删除操作
def delete(self, value):
current = self.head
previous = None
while current and current.value != value:
previous = current
current = current.next
if current:
if previous:
previous.next = current.next
else:
self.head = current.next
# 修改操作
def update(self, old_value, new_value):
current = self.head
while current and current.value != old_value:
current = current.next
if current:
current.value = new_value
# 查询操作
def search(self, value):
current = self.head
index = 0
while current:
if current.value == value:
return index
current = current.next
index += 1
return -1
# 示例
linked_list = LinkedList()
for i in range(1, 6):
linked_list.insert(i)
linked_list.insert(10) # 在链表头部插入10
current = linked_list.head
while current:
print(current.value, end=" -> ")
current = current.next
print("None")
linked_list.delete(3) # 删除值为3的节点
current = linked_list.head
while current:
print(current.value, end=" -> ")
current = current.next
print("None")
linked_list.update(5, 20) # 将值为5的节点修改为20
current = linked_list.head
while current:
print(current.value, end=" -> ")
current = current.next
print("None")
search_result = linked_list.search(20) # 查询值为20的元素索引
print(f"Value 20 found at index: {search_result}")
链表回显如下:
10 -> 5 -> 4 -> 3 -> 2 -> 1 -> None 在链表头部插入10
10 -> 5 -> 4 -> 2 -> 1 -> None 删除值为3的节点
10 -> 20 -> 4 -> 2 -> 1 -> None 将值为5的节点修改为20
Value 20 found at index: 1 查询值为20的元素索引
这个代码实现其实理解不难,用的是单链表中头插法
这里我们就以链表插入为例分析下代码作用
class ListNode:
定义了一个名为ListNode的类,用于表示链表中的节点。
def __init__(self, value=0, next=None):
这是ListNode类的构造函数,用于创建一个新的节点实例。它接受两个参数:value(节点存储的值,默认为0)和next(指向下一个节点的引用,默认为None)。
self.value = value
将传入的value参数赋值给当前节点的value属性。
self.next = next
将传入的next参数赋值给当前节点的next属性。
class LinkedList:
定义了一个名为LinkedList的类,用于表示整个链表。
def __init__(self):
这是LinkedList类的构造函数,用于创建一个新的链表实例。
self.head = None
将链表的头节点head初始化为None,表示链表为空。
def insert(self, value):
定义了一个名为insert的方法,用于在链表的头部插入一个新的节点。
new_node = ListNode(value)
创建一个新的ListNode实例,其值为传入的value参数。
new_node.next = self.head
将新节点的next属性设置为当前链表的头节点self.head。
self.head = new_node
将链表的头节点self.head更新为新节点new_node,这样新节点就成为了链表的第一个节点。
接下来是函数调用代码:
linked_list = LinkedList()
创建一个LinkedList实例,并将其赋值给变量linked_list。
for i in range(1, 6):
使用for循环,从1到5(不包括6)迭代。
linked_list.insert(i)
在每次迭代中,调用insert方法将当前的i值插入到链表头部。
linked_list.insert(10) # 在链表头部插入10
在循环结束后,再次调用insert方法,将值10插入到链表头部。
current = linked_list.head
将链表的头节点赋值给变量current,用于遍历链表。
while current:
使用while循环,只要current不是None(即链表不为空),就继续循环。
print(current.value, end=" -> ")
打印当前节点的值,并使用end=" -> "参数使得打印的节点值之间用箭头连接,而不是换行。
current = current.next
将current更新为下一个节点,以便在下一次迭代中处理。
print("None")
当current变为None时,表示链表已经遍历完毕,打印"None"表示链表的结束。
回顾下什么是头插法?
链表头插法是一种在链表头部添加新节点的方法。在链表中,头插法特别适用于需要频繁在列表前端插入数据的场景,因为它可以快速地将新元素插入到链表的头部,而不需要遍历整个链表来找到插入点。
头插法的基本步骤:
创建新节点:首先,你需要创建一个新的节点,这个节点将包含你想要添加到链表中的值。
设置新节点的下一个指针:新节点的 next 指针应该指向当前链表的头节点。
更新头节点:将链表的头节点更新为新创建的节点。
头插法的优点:
速度快:头插法的插入操作非常快,因为它不需要遍历链表,只需要常数时间(O(1))。
简单:头插法的逻辑简单,易于实现。
头插法的缺点:
内存消耗:频繁的头插法操作会导致链表的头部分内存消耗增加,因为每次插入都会在链表的头部创建新的节点。
可能的空间浪费:如果链表经常进行头插法和删除操作,可能会造成大量的内存分配和释放,这在某些情况下可能会导致空间浪费。
头插法的Python实现示例:
python
class ListNode:
def __init__(self, value=0, next=None):
self.value = value
self.next = next
class LinkedList:
def __init__(self):
self.head = None
def insert_at_head(self, value):
new_node = ListNode(value)
new_node.next = self.head
self.head = new_node
使用示例
linked_list = LinkedList()
linked_list.insert_at_head(10)
linked_list.insert_at_head(20)
linked_list.insert_at_head(30)
打印链表
current = linked_list.head
while current:
print(current.value, end=" -> ")
current = current.next
print("None")
在这个示例中,我们定义了一个 ListNode 类和一个 LinkedList 类。LinkedList 类包含一个 insert_at_head 方法,该方法实现了头插法。每次调用 insert_at_head 方法时,都会在链表的头部插入一个新的节点。最后,我们通过一个循环打印出链表中的所有值,可以看到新插入的节点(30)现在是链表的头节点。

该说不说,这可能需要一点点计算机专业背景,还好小编以前学过,理解起来比较轻松,实际中若接触都后端算法可能就会用到具体的数据结构,还要根据实际场景需求设计数据结构和数据模型,这可不是一件简单的事情噢,加油,慢慢来,学它个几个月直接投入实践使用,也就那样,该说不说
4790

被折叠的 条评论
为什么被折叠?



