目录
内存的工作原理
大家逛超市的时候,应该都看见过门口有储存柜,进去逛超市时,就把东西存入储存柜,如果你东西很多,就可能需要开两个柜子来放你的东西,然后你就只需要拿着储存柜小票轻轻松松去逛超市了,等逛完超市,你凭借小票在把自己的东西拿出来。其实计算机内存的工作原理大致就是这样。计算机就是超市门口这一堆储物柜的集合,每个单独的储物柜都有对应的小票,都有它们的地址。
当我们需要将数据存储到内存时,我们请求到计算机,计算机再分配给我们一片空间用于存储。
数组和链表
数组
数组(Array)是有序的元素序列。它的所有元素在内存中是相连的(紧靠在一起的)。
链表
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的,即链表中的元素可储存在内存的任何地方。链表中存储了每个元素即下一个元素的地址,所以在插入元素的情况下,它比数组更具有优势,因为它只需要将元素存入内存,然后将其地址储存在上一个元素中,不需像数组那样将后面的元素依次退位。
比较
整体比较
数组 | 链表 | |
内存占用 | 需要连续的内存空间 | 不需要连续的内存空间 |
大小可变 | 固定,分配多少就是多少,不能动态扩展,可能会浪费内存 | 链表的大小可动态变化 |
插入(中间) | 较慢,需要将插入元素之后的所有元素往后移动 | 较快,只需要放入内存,并将其地址放入前一个元素 |
删除 | 较慢,需要将删除元素之后的所有元素往前移 | 较快,只需要修改前一个元素的指向的地址即可 |
查询 | 较快,可以通过下标直接访问 | 较慢,只能通过遍历查询 |
访问形式 | 即可随机访问也可顺序访问 | 只能顺序访问 |
扩展性 | 不可动态扩展 | 可以动态扩展 |
存储数据类型 | 必须一致 | 可以不一致 |
时间复杂度
查询 | 插入 | 删除 | |
数组 | O(1) | O(n) | O(n) |
链表 | O(n) | O(1) | O(1) |
需要说明的是,数组如果通过下标随机查询,它的时间复杂度为O(1),但如果通过顺序访问,那么时间复杂度和链表一样为O(n),但如果这是个有序数组,通过顺序访问,那么根据二分查找,它的时间复杂度为O(logn)。上面的插入,删除仅仅指这一个操作,并不包含查询的时间,之所以数组的插入和删除操作的时间复杂度为O(n),是因为数组插入和删除一个元素时,其他元素的位置也要相应移动,在头部和尾部插入(注意考虑内存是否足够,足够时时间复杂度为O(1))的时间复杂度都为O(n),而链表只需要插入元素,再在前一个元素中添加地址即可,所以时间复杂度为O(1),总的来说,不能过于记住结论,需要实际情况灵活分析。
在实际生活中,其实数组用的情况更多,因为它支持随机访问,你想要哪个元素,你可以通过下标直接跳到你想要的元素,所有它的读取速度更快,而在生活中,很多情况都要求能够随机访问。
python中实现链表
下面会简单演示单向链表的实现。双向链表,循环链表的实现大家可以去看看这篇文章
单向链表实现,具有增删检查等功能,主要运用类与对象等知识
# @Time:2022/1/2813:21
# @Author:中意灬
# @File:链表.py
# @ps:tutu qqnum:2117472285
# #定义结点
class Node(object):
#单向链表的节点
def __init__(self,item):
self.item=item#item存放数据元素
self.next=None#next是下一个节点的标识
class SingleLinkList(object):
#单向链表
def __init__(self):
self.head=None
def is_empty(self):
#判断链表是否为空
return self.head == None
def length(self):
#链表长度
#初始指针指向head头部
cur=self.head
count=0
#指针指向None表示达到了尾部
while cur != None:
count+=1
#指针下移
cur=cur.next
return count
def items(self):
#遍历链表
#获取head指针
cur=self.head
#循环遍历
while cur is not None:
#返回生成器
yield cur.item
cur=cur.next
def add(self,item):
#向链表头部添加元素
node=Node(item)
#新节点指针指向原头部节点
node.next=self.head
#头部节点指针修改为新节点
self.head=node
def append(self,item):
#尾部添加元素
node=Node(item)
#先判断是否为空链表
if self.is_empty():
#空链表,head指向新节点
self.head=node
else:
#不是空链表,找到尾部,将尾部next节点指向新节点
cur=self.head
while cur.next != None:
cur=cur.next
cur.next=node
def insert(self, index, item):
# 指定位置插入元素
# 指定位置在第一个元素之前,在头部插入
if index <= 0:
self.add(item)
# 指定位置超过尾部,在尾部插入
elif index > (self.length() - 1):
self.append(item)
else:
# 创建元素结点
node = Node(item)
cur = self.head
# 循环到需要插入的位置
for i in range(index - 1):
cur = cur.next
node.next = cur.next
cur.next = node
def remove(self, item):
#删除元素
cur = self.head
pre = None
while cur != None:
# 找到指定元素
if cur.item == item:
# 如果第一个就是删除的节点
if not pre:
# 将头指针指向头节点的后一个节点
self.head = cur.next
else:
# 将删除位置前一个节点的next指向删除位置的后一个节点
pre.next = cur.next
return True
else:
# 继续按链表后移节点
pre = cur
cur = cur.next
def find(self, item):
#查找元素是否存在
return item in self.items()
测试
if __name__ == '__main__':
link_list=SingleLinkList()
#添加元素
for i in range(5):
link_list.append(i)
print('节点:',end='')
for i in link_list.items():
print(i, end='\t')
#头部插入元素
link_list.add(6)
print('插入头部元素后:',end='')
for i in link_list.items():
print(i, end='\t')
#尾部添加元素
link_list.append(10)
print('尾部添加元素后:',end='')
for i in link_list.items():
print(i,end='\t')
#中间插入元素
print('')
link_list.insert(3,9)
print('中间插入元素后:',end='')
for i in link_list.items():
print(i, end='\t')
#删除元素
link_list.remove(3)
print('删除元素后:',end='')
for i in link_list.items():
print(i, end='\t')
#查找元素
a=link_list.find(4)
print('是否查找到4这个元素:',end='')
print(a)
运行结果:
选择排序
假如辅导员让你做成绩分析,让你对整个专业的绩点进行一个从高到低的排序,该如何做?
姓名 | 平均绩点 |
张三 | 2.1 |
李四 | 3.2 |
老五 | 4.2 |
王麻子 | 0.8 |
一种办法是遍历这个列表,找出绩点最高的人,然后添加到一个新的列表,然后重复这个操作,就能得到一个有序列表,而这便是选择排序,时间复杂度为O(n^2)
另外一种方法是用快速排序的方法来实现,时间复杂度为O(nlogn),比选择排序较快。这里只讲解选择排序,后面再谈快速排序。
这是降序排序,升序思想一样
# @Time:2022/1/301:02
# @Author:中意灬
# @File:选择排序.py
# @ps:tutu qqnum:2117472285
#找出最大元素,返回其下标
def find_Max_num(arr):
Max_num=arr[0]
Max_num_index=0
for i in range(1,len(arr)):
if arr[i]>Max_num:
Max_num=arr[i]
Max_num_index=i
return Max_num_index
#排序
def sort_num(arr):
newArr=[]
for i in range(len(arr)):
Max_num_index=find_Max_num(arr)
newArr.append(arr.pop(Max_num_index))#找到一个,弹出一次,再次循环
return newArr
if __name__ == '__main__':
arr=[3.1,3.5,0.2,4.2]
new=sort_num(arr)
print(new)
运行结果:
[4.2, 3.5, 3.1, 0.2]
小结
- 计算机内存如一大堆储物柜
- 储存多个元素是,可使用数组或链表
- 数组的元素地址连着一起
- 链表的元素地址分开的,其中每个元素都包含着下一个元素的地址
- 数组有两种访问方式,随机访问和顺序访问,随机访问速度很快
- 链表只有一种访问方式,顺序访问,访问速度较慢
- 链表的插入和删除速度很快,数组较慢
- 在同一个数组中,所有元素的类型都必须一样,链表可以不一致