当前位置: 首页 > news >正文

第一章——数组基础(概念篇python版)

目录

  • 一、数组基础
  • 二、数组基本操作的具体实现
    • 2.1 向数组中添加元素(增)
    • 2.2 根据索引获取元素值(查)
    • 2.3 根据元素值去查数组(查)
      • 2.3.1 根据值查看数组中是否存在该元素
      • 2.3.2 根据值返回元素的索引
    • 2.4 获取数组中最大值(最小值)的索引(查)
    • 2.5 根据索引修改元素值(改)
    • 2.6 交换两个元素(改)
    • 2.7 从数组中删除元素 (删)
    • 2.8 根据元素值删除该元素(查+删)
    • 2.9 删除数组中值最大(最小)的元素(查+删)
  • 三、动态数组
    • 3.1 基本概念
    • 3.2 动态数组时间复杂度分析
      • 3.2.1 添加操作O(n)
      • 3.2.2 删除操作O(n)
      • 3.2.3 修改操作O(1)
      • 3.2.4 查询操作
      • 3.2.5 总结
    • 3.3 动态数组均摊复杂度
    • 3.4 Python列表与字典操作时间复杂度
      • 3.4.1 列表操作时间复杂度
      • 3.4.2 字典操作时间复杂度

在这里插入图片描述

一、数组基础

官方定义:
将元素顺序地存放在一块连续的存储区里,元素间的顺序关系由它们的存储顺序自然表示。

翻译:
将数据码成一排进行存放

索引可以有语义,索引也可以没有语义

数组最大的优点:快速查询,时间复杂度O(1)
数组最好应用于“索引有语义”的情况
但是并不是所有有语义的索引都适用
比如身份证号: 13131413415153

数组也可以处理没有语义的情况
我们在这一章,主要处理索引没有语义的情况数组的使用

索引没有语义,如何表示没有元素?
如何添加元素?如何删除元素?

我们可以自己定义一个Array类,来实现一些简单的功能
定义自己的数组类 :

"""
数组特点:占用一段连续的内存空间,支持随机(索引)访问,且时间复杂度为O(1)添加元素时间复杂度:O(n)删除元素时间复杂度:O(n)
"""class Arr:def __init__(self, capacity=10):"""构造函数:param capacity:数组最大容量,不指定的话默认为10"""self._capacity = capacity# 数组的有效元素数目,初始化为0self._size = 0# 由于python的list是动态扩展的,而我们要实现底层具有固定容量,# 占用一段连续内存空间的数组,所以用None来作为无效元素的标识self._data = [None] * self._capacitydef __getitem__(self, item):"""让Arr类支持索引操作:param item: 索引:return: 该索引对应位置上的元素值"""return self._data[item]def getSize(self):"""返回数组有效元素的个数:return: 数组有效元素的个数"""return self._sizedef getCapacity(self):"""返回数组的容量:return: 数组的容量"""return self._capacitydef isEmpty(self):"""判断数组是否为空:return: True为空,False为非空"""return self._size == 0def printArr(self):"""对数组元素进行打印"""for i in range(self._size):print(self._data[i], end=' ')print('\nSize: %d------Capacity: %d' % (self.getSize(), self.getCapacity()))

二、数组基本操作的具体实现

2.1 向数组中添加元素(增)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

    def add(self, index, elem):"""向数组中添加一个元素,注意数组占用的是一段连续的内存空间,所以在添加元素后,数组还是要保证这个特点,因此需要将后面的元素都向后挪一个位置而且注意需要从尾部开始挪,防止元素之间的覆盖时间复杂度:O(n):param index:添加元素所在的索引位置:param elem:所要添加的元素值"""# 判断插入的位置是否无效if index < 0 or index > self._size:raise Exception('Add Failed. Require 0 <= index <= self._size.')# 判断数组是否已经满了if self._size == self._capacity:# 默认扩容当前容量的二倍。容量翻倍要比容量加上一个固定值要好# 这样做均摊复杂度为O(1),具体百度。self._resize(self._capacity * 2)# 从尾部开始挪动元素,在index处腾出一个空间# 一定要注意,在步长为负数的情况下,区间是左开右闭的,即(index-1, self._size-1],# 所以是index-1,与正常的左闭右开区间是相反的for i in range(self._size - 1, index - 1, -1):self._data[i + 1] = self._data[i]# 将index处赋值为elemself._data[index] = elem# 数组有效元素值加1self._size += 1

有了上面这个插入数据的一般函数,还可以写两个比较特殊的函数,在数组的头部和尾部插入元素,只要直接调用add函数就行:

    def addLast(self, elem):"""向数组尾部添加元素时间复杂度:O(1):param elem: 所要添加的元素"""# 直接调用add方法,注意不用再判断合法性了self.add(self._size, elem)def addFirst(self, elem):"""向数组头部添加元素时间复杂度:O(n):param elem: 所要添加的元素"""# 直接调用add方法self.add(0, elem)

2.2 根据索引获取元素值(查)

    def get(self, index):"""获得索引index处的元素时间复杂度:O(1):param index: 数组索引:return: 数组索引处的值"""# 判断index的合法性,注意获取值的时候索引不能等于self._sizeif index < 0 or index >= self._size:raise Exception('Get Failed. Index is illegal.')return self._data[index]

由此可写出两个特殊函数,为获取头部和尾部元素的函数,直接调用get函数即可:

    def getFirst(self):"""获得数组首位元素的值:return: 首位元素的值"""# 直接调用get函数,安全可靠return self.get(0)def getLast(self):"""获得数组末尾元素的值:return: 末尾元素的值"""# 直接调用get函数,安全可靠return self.get(self._size - 1)

2.3 根据元素值去查数组(查)

2.3.1 根据值查看数组中是否存在该元素

    def contains(self, elem):"""查看数组中是否存在元素elem,最好不要传入一个浮点数时间复杂度:O(n):param elem: 目标元素:return: bool值,存在为真"""# 遍历数组for i in range(self._size):if self._data[i] == elem:# 找到了就返回Truereturn True# 遍历完了还没找到,返回Falsereturn False

2.3.2 根据值返回元素的索引

    def find(self, elem):"""在数组中查找元素,并返回元素所在的索引。如果数组中存在多个elem,只返回最左边elem的索引时间复杂度:O(n):param elem: 目标元素:return: 元素所在的索引,没找到则返回-1(无效值)"""# 遍历数组for i in range(self._size):if self._data[i] == elem:# 找到就返回索引return i# 没找到就返回-1return -1def findAll(self, elem):"""找到值为elem全部元素的索引时间复杂度:O(n):param elem: 目标元素:return: 一个列表,值为全部elem的索引"""# 建立一个新的数组用于存储索引值ret_list = Arr()# 遍历数组for i in range(self._size):if self._data[i] == elem:# 找到就将索引添加进ret_listret_list.addLast(i)return ret_list

2.4 获取数组中最大值(最小值)的索引(查)

    def get_Max_index(self):"""获取数组中的最大元素的索引,返回最大元素的索引值如果有多个最大值,默认返回最左边的那个索引时间复杂度:O(n):return: 最大元素的索引"""if self.isEmpty():raise Exception('Error, array is Empty!')# 记录最大值的索引,初始化为0max_elem_index = 0# 从索引1开始遍历,一直到数组尾部for i in range(1, self._size):# 如果当前索引的值大于最大值索引处元素的值if self._data[i] > self._data[max_elem_index]:# 更新max_elem_index,这样它还是当前最大值的索引max_elem_index = i# 遍历完后,将数组的最大值索引返回return max_elem_indexdef get_Min_index(self):"""获取数组中的最小元素的索引,返回最小元素的索引值如果有多个最小值,默认返回最左边的那个索引时间复杂度:O(n):return: 最小元素的索引"""if self.isEmpty():raise Exception('Error, array is Empty!')# 记录最小值的索引,初始化为0min_elem_index = 0# 从索引1开始遍历,一直到数组尾部for i in range(1, self._size):# 如果当前索引的值小于最小值索引处元素的值if self._data[i] < self._data[min_elem_index]:# 更新min_elem_index,这样它还是当前最小值的索引min_elem_index = i# 遍历完后,将数组的最小值索引返回return min_elem_index

2.5 根据索引修改元素值(改)

    def set(self, index, elem):"""将索引为index的元素的值设置为elem时间复杂度:O(1):param index: 索引:param elem: 新的值"""# 判断index的合法性if index < 0 or index >= self._size:raise Exception('Set Failed. Index is illegal.')self._data[index] = elem

2.6 交换两个元素(改)

  def swap(self, index1, index2):"""交换分别位于索引index1和索引index2处的元素时间复杂度:O(1):param index1: 索引1:param index2: 索引2""" # 合法性检查if index1 < 0 or index2 < 0 or index1 >= self._size or index2 >= self._size:        raise Exception('Index is illegal')# 交换元素self._data[index1], self._data[index2] = self._data[index2], self._data[index1] 

2.7 从数组中删除元素 (删)

删除指定位置的元素,删除索引为1的元素 :
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
![![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/b19dc2ea68c84d4bbb4775e0c3386d89.png#pic_center)

    def remove(self, index):"""删除索引为index的元素。index后面的元素都要向前移动一个位置时间复杂度:O(n):param index: 目标索引:return: 位于该索引的元素的值"""# index合法性检查if index < 0 or index >= self._size:raise Exception('Remove Failed. Require 0 <= index < self._size')# 拷贝一下index处的元素,便于返回ret = self._data[index]# index后面的元素都向前挪一个位置for i in range(index, self._size - 1):self._data[i] = self._data[i + 1]# 维护self._sizeself._size -= 1# 最后一个元素的垃圾回收self._data[self._size] = None# 如果当前有效元素为总容量的四分之一且还存在有效元素,# 则将容量缩减为原来的一半if self._size and self._capacity // self._size == 4:self._resize(self._capacity // 2)return ret

有了上述的一般删除函数,也可以写出两个特殊的删除函数,分别是头部和尾部的删除函数,直接调用remove就行:

    def removeFirst(self):"""删除数组首位元素时间复杂度:O(n):return: 数组首位元素"""# 直接调用remove函数return self.remove(0)def removeLast(self):"""删除数组末尾的元素时间复杂度:O(1):return: 数组末尾元素"""# 直接调用remove函数return self.remove(self._size - 1)

2.8 根据元素值删除该元素(查+删)

其实就是调用find函数与remove函数

    def removeElement(self, elem):"""删除数组中为elem的元素,如果数组中不存在elem,那么什么都不做。如果存在多个相同的elem,只删除最左边的那个时间复杂度:O(n):param elem:要删除的目标元素"""# 尝试找到目标元素(最左边的)索引index = self.find(elem)# elem在数组中就删除,否则什么都不做if index != -1:# 调用remove函数self.remove(index)def removeAllElement(self, elem):"""删除数组内所有值为elem的元素,可以用递归来写,这里用的迭代方法。elem不存在就什么都不做:param elem: 要删除的目标元素"""while True:# 循环来找elem,如果还存在就继续删除index = self.find(elem)# 若存在if index != -1:self.remove(index)else:break

2.9 删除数组中值最大(最小)的元素(查+删)

先找出最大(最小)元素的索引,再根据该索引删除该元素

    def removeMax(self):"""删除数组中的最大元素,返回最大元素的值,如果有多个最大值,默认值删除最左边的那个时间复杂度:O(n):return:最大元素"""# 直接调用remove函数删除最大值return self.remove(self.get_Max_index())def removeMin(self):"""删除数组中的最小元素,返回最小元素的值,如果有多个最小值,默认值删除最左边的那个时间复杂度:O(2n),可以看成是O(n)的:return:最小元素"""# 直接调用remove函数删除最大值return self.remove(self.get_Max_index())

三、动态数组

3.1 基本概念

即数组长度可变的数组,可以根据用户需要,有效利用存储空间。
在这里插入图片描述
在这里插入图片描述

具体体现在添加元素和删除元素中,若添加元素时发现数组已经满了,则将数组长度自动扩大为原来的两倍,若删除元素后发现当前有效元素为总容量的四分之一且还存在有效元素,则将数组容量缩减至原来的一半。

add函数和remove函数中已经有所体现:

# 判断数组是否已经满了
if self._size == self._capacity:# 默认扩容当前容量的二倍。容量翻倍要比容量加上一个固定值要好# 这样做均摊复杂度为O(1),具体百度。self._resize(self._capacity * 2)# 如果当前有效元素为总容量的四分之一且还存在有效元素,
# 则将容量缩减为原来的一半
if self._size and self._capacity // self._size == 4:self._resize(self._capacity // 2)

再来看一下resize函数的实现:

    def resize(self, new_capacity):'''数组容量放缩至new_capacity,:param new_capacity:新的容量'''# 建立一个新数组,容量为new_capacitynew_arr = Arr(new_capacity)# 将当前数组的元素按当前顺序全部移动到new_arr中for i in range(self._size):new_arr.addLast(self._data[i])# 数组容量变为new_capacityself._capacity = new_capacity# 将new_arr._data赋值给self._data,从而完成数组的容量放缩操作self._data = new_arr._data

下面来看一下上述定义的数组类的一些基本增删改查操作:

import numpy as np
np.random.seed(7)
test = Arr()
print(test.getSize())
print(test.getCapacity())
print(test.isEmpty())
for i in range(8):test.add(0, np.random.randint(5))
test.printArr()
test.addLast(2)
test.printArr()
print(test.get(3))
test.set(3, 10)
test.printArr()
print(test.contains(10))
print(test.find(4))
test.findAll(1).printArr()
test.remove(3)
test.printArr()
test.removeFirst()
test.removeLast()
test.printArr()
test.removeElement(4)
test.printArr()
test.removeAllElement(3)
test.printArr()
for i in range(30):test.addLast(np.random.randint(10))
test.printArr()
print(test[3])
test.swap(0, 1)
test.printArr()

结果如下:

0
10
True
1  0  1  4  3  3  1  4  
Size: 8-----Capacity: 10
1  0  1  4  3  3  1  4  2  
Size: 9-----Capacity: 10
4
1  0  1  10  3  3  1  4  2  
Size: 9-----Capacity: 10
True
7
0  2  6  
Size: 3-----Capacity: 10
1  0  1  3  3  1  4  2  
Size: 8-----Capacity: 10
0  1  3  3  1  4  
Size: 6-----Capacity: 10
0  1  3  3  1  
Size: 5-----Capacity: 10
0  1  1  
Size: 3-----Capacity: 10
0  1  1  8  7  6  4  0  7  0  7  6  3  5  8  8  7  5  0  0  2  8  9  6  4  9  7  3  3  8  3  0  1  
Size: 33-----Capacity: 40
8
1  0  1  8  7  6  4  0  7  0  7  6  3  5  8  8  7  5  0  0  2  8  9  6  4  9  7  3  3  8  3  0  1  
Size: 33-----Capacity: 40

3.2 动态数组时间复杂度分析

3.2.1 添加操作O(n)

Addlast(e) :O(1)
AddFirst(e) :O(n)
add(index,e) :O(n/2)=O(n)
最坏情况:O(n)
Resize :O(n)

3.2.2 删除操作O(n)

removelast(e): O(1)
removeFirst(e) :O(n)
remove(index,e): O(n/2)=O(n)
最坏情况:O(n)
Resize :O(n)

3.2.3 修改操作O(1)

Set(index,e) :O(1)

3.2.4 查询操作

Get(index) :O(1)
Contains(e) :O(n)
Find(e) :O(n)

3.2.5 总结

增,删 O(n)
改:已知索引O(1) 未知索引O(n)
查:已知索引O(1) 未知索引O(n)

3.3 动态数组均摊复杂度

resize复杂度分析:
在这里插入图片描述

9次addlast操作,触发resize,总共进行了17次基本操作。平均,每次addlast,进行2次基本操作。
假设capacity=m, m+1次addlast,触发resize,总共进行2m+1次基本操作。平均,每次addlast,进行2次基本操作 。

均摊复杂度O(1)

3.4 Python列表与字典操作时间复杂度

3.4.1 列表操作时间复杂度

操作操作说明时间复杂度
index(value)查找list某个元素的索引O(1)
a = index(value)索引赋值O(1)
append(value)队尾添加O(1)
pop()队尾删除O(1)
pop(index)根据索引删除某个元素O(n)
insert(index, value)根据索引插入某个元素O(n)
iterration列表迭代O(n)
contains(in)列表是否包含某个元素O(n)
slice[x : y]切片,获取x, y为O(1),获取[x : y]内的值为O(k)O(k)
del slice[x : y]删除切片,删除后数据需要重新移动/合并O(n)
reverse列表翻转O(n)
sort排序O(nlogn)快排

3.4.2 字典操作时间复杂度

操作操作说明时间复杂度
copy复制O(n)
get(value)获取O(1)
set(value)修改O(1)
delete(value)删除O(1)
contains(in)包含O(1)
iterration字典迭代O(n)

因为字典的底层实现用了哈希表。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Android+Jacoco+code-diff全量、增量覆盖率生成实战
  • 共享经济背景下校园、办公闲置物品交易平台-计算机毕设Java|springboot实战项目
  • 系统架构设计师 - 软件工程(2)
  • Mysql面试一
  • 【数据结构算法经典题目刨析(c语言)】使用栈实现队列(图文详解)
  • javaEE中自定义注解以及注解的解析
  • CSP部分模拟题题解
  • 探索sqlmap的奥秘:Python中的强大SQL注入检测工具
  • python实现K-means图像聚类
  • Kubernetes--命令行工具 kubectl
  • 参与团体标准的意义以及作用
  • 旋转图像(LeetCode)
  • docker 启动 mongo,redis,nacos.
  • 网络安全实训第三天(文件上传、SQL注入漏洞)
  • 用7EPhone云手机进行TikTok的矩阵运营
  • Apache的80端口被占用以及访问时报错403
  • canvas实际项目操作,包含:线条,圆形,扇形,图片绘制,图片圆角遮罩,矩形,弧形文字...
  • eclipse的离线汉化
  • Iterator 和 for...of 循环
  • JAVA SE 6 GC调优笔记
  • PHP 小技巧
  • STAR法则
  • Stream流与Lambda表达式(三) 静态工厂类Collectors
  • Vue UI框架库开发介绍
  • vue 配置sass、scss全局变量
  • 技术胖1-4季视频复习— (看视频笔记)
  • 理清楚Vue的结构
  • 网页视频流m3u8/ts视频下载
  • 微服务入门【系列视频课程】
  • 微信如何实现自动跳转到用其他浏览器打开指定页面下载APP
  • 第二十章:异步和文件I/O.(二十三)
  • ​ 无限可能性的探索:Amazon Lightsail轻量应用服务器引领数字化时代创新发展
  • ​人工智能之父图灵诞辰纪念日,一起来看最受读者欢迎的AI技术好书
  • !$boo在php中什么意思,php前戏
  • # .NET Framework中使用命名管道进行进程间通信
  • #传输# #传输数据判断#
  • $$$$GB2312-80区位编码表$$$$
  • (3)llvm ir转换过程
  • (6)【Python/机器学习/深度学习】Machine-Learning模型与算法应用—使用Adaboost建模及工作环境下的数据分析整理
  • (MATLAB)第五章-矩阵运算
  • (附源码)springboot高校宿舍交电费系统 毕业设计031552
  • (附源码)ssm旅游企业财务管理系统 毕业设计 102100
  • (附源码)计算机毕业设计SSM保险客户管理系统
  • (论文阅读31/100)Stacked hourglass networks for human pose estimation
  • (一)VirtualBox安装增强功能
  • *++p:p先自+,然后*p,最终为3 ++*p:先*p,即arr[0]=1,然后再++,最终为2 *p++:值为arr[0],即1,该语句执行完毕后,p指向arr[1]
  • .mysql secret在哪_MySQL如何使用索引
  • .Net Attribute详解(上)-Attribute本质以及一个简单示例
  • .net(C#)中String.Format如何使用
  • .Net8 Blazor 尝鲜
  • .NetCore部署微服务(二)
  • .net和jar包windows服务部署
  • .NET牛人应该知道些什么(2):中级.NET开发人员
  • .NET中使用Redis (二)
  • /dev/VolGroup00/LogVol00:unexpected inconsistency;run fsck manually