Python数组学习20250107

written by 五氧化二钒

数组

带着问题来学习:

问题1.数组和列表、集合之间有什么不同?

解决思路:画出表格,做总结。

答:

数据结构顺序长度元素类型重复元素Python
集合没有顺序 不可以通过下标访问长度可变 .add()增加元素 .remove()删除元素 .clear()清空集合没限制没有重复元素set1 = {1,2,3} 只声明{}是字典
列表 (线性列表) (链表)有顺序 可以通过list1[n]访问长度可变 .append()增加元素 .remove()删除第一个匹配到的元素 .pop(n)弹出索引为n的元素 .pop()弹出最后一个元素 .clear()清空列表没限制可以有重复元素list1 = []
元组有顺序 可以通过tup1[n]访问长度不可变 元素也不可变,不能增加删除修改没限制可以有重复元素tup1 = (1,"帅")
字典3.10版有顺序 可以通过键访问值长度可变 增加/修改元素dict[key] = value 删除元素del my_dict['age'] 清空元素my_dict.clear()字典的值没有限制 字典的键必须是不可变的不能重复,键值对一一对应my_dict = {'键1': 1, '键2': 2, '键3': 3}

问题2.如何理解数组的读取、查找、插入、删除等 基本操作

解决思路:描述数组的操作过程。

答:

数组的操作操作步骤时间复杂度
读取元素1.找到当前数组索引为0的位置。 2.将内存地址加上索引值,即为目标位置。O(1)
查找元素1.找到当前数组索引为0的位置。 2.逐个向后检测O(N)
插入元素(尾部)1.根据数组首地址和长度,计算出末尾地址,直接插入即可。O(1)
插入元素(中间)1.将要插入位置的元素和之后的元素挨个向后移一位。 2.插入到目标位置。O(N)
删除元素1.删除当前元素位置。 2.之后元素逐个上位一位。O(N)

例题1.寻找数组的中心索引

给你一个整数数组 nums ,请计算数组的 中心下标 。

数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。

如果中心下标位于数组最左端,那么左侧数之和视为 0 ,因为在下标的左侧不存在元素。这一点对于中心下标位于数组最右端同样适用。

如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回 -1 。

原题链接:724. 寻找数组的中心下标 - 力扣(LeetCode)

解题思路:遍历每一个元素,直到左边sum=右边sum为止。

答:

class Solution:
    
    def findMiddleIndex(self, nums):
        #先计算总和吧
        total = sum(nums)
​
        sumleft = 0
        sumright = 0
        #遍历吧
        for i in range(len(nums)):
            #看看左边(包括自身)
            
            sumleft = sumleft + nums[i]
​
            #看看右边(不包括自身)
            #右边可以用总的减左边的
            
            sumright = total - sumleft
​
            if sumleft-nums[i] == sumright:
                return i
        return -1
​
nums = []
solu = Solution()
n = solu.findMiddleIndex(nums)
print(n)

例题2.搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

原题链接:35. 搜索插入位置 - 力扣(LeetCode)

解题思路:遍历寻找即可

答1(实现了插入):

class Solution:
    def searchInsert(self, nums, target) :
        #先判断是否在数组中
        if(target in nums):
            #存在,完成
            n = nums.index(target)
            return n
        else:
            #不存在
            #找自己的位置
            #第一种寻找方法:顺序查找
            for i in range(len(nums)):
                if target < nums[i]:
                    nums.append(None)
                    #挨个移后方元素(包括i)
                    for j in range(len(nums)-i-1):
                        nums[-(j+1)] = nums[-(j+2)]
                    nums[i] = target
                    return nums
                else:
                    continue
​
            nums.append(target)
            return nums
​
solution = Solution()
print(solution.searchInsert([1,2,4,5,6],7))

答2(只找到插入位置即可):

class Solution:
    def searchInsert(self, nums, target) :
        #数字位于数组中时,输出下标
        if(target in nums):
            return nums.index(target)
        else:
            
            #遍历,如果target小于某一个数了,那么这个数下标就是要插入的位置
            for i in range(len(nums)):
                if target < nums[i]:
                    return i
                else:
                    continue
                
            #遍历完成后,若没有return
            return len(nums)
​
solu = Solution()
print(solu.searchInsert([1,2,3,4],5))

例题3.合并区间

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。

解决思路:覆盖的区域都染色成1,没覆盖的不染色默认0。最后全部遍历一遍,输出结果。

原题链接:56. 合并区间 - 力扣(LeetCode)

答1(区间为双闭区间的情况):

#区间为双闭区间的情况
​
class Solution:
    def merge(self, intervals):
​
        #先列出所有右界,找到最大的,开个新数组
        right = []
        for i in range(len(intervals)):
            right.append(intervals[i][1])
        #注意下标
        newlist = [0]*(max(right))
        #涂色
        #先把两端涂了再说
        #列出所有左界
        left = []
        for i in range(len(intervals)):
            left.append(intervals[i][0])
        #开始涂两端的色
        for i in range(len(newlist)):
            if(((i+1) in left) or ((i+1) in right)):
                newlist[i] = 1
            else:
                continue
        #开始涂中间的色
        #寻找两端差值大于1的
        for i in intervals:
            if (i[1] - i[0])>1:
                for j in range(i[1] - i[0]):
                    #找定位元素i[0]i[1]
                    newlist[i[0]+j] = 1
​
        #输出结果
        #若不存在0则输出大团圆
        if (0 not in newlist):
            return [[1,len(newlist)]]
        #若存在0,列出所有1的下标
        else:
            index = [index for index,value in enumerate(newlist) if value == 1]
            
            #遍历所有1的下标
            #设置记录点
            result_head = []
            result_tail = []
            for i in index:
                #如果当前下标-1不在index中,该位置(下标+1)就是一个区间的开端
                if (i-1) not in index:
                    result_head.append(i+1)
                #如果当前下标+1不在index中,该位置(下标+1)就是一个区间的结尾
                if (i+1) not in index:
                    result_tail.append(i+1)
​
            #输出结果
            resultlist = []
            
            for i,j in zip(result_head,result_tail):
                resultlist.append([i,j])
            return resultlist
                
            
solu = Solution()
print(solu.merge([[1,4],[5,6]]))
​

答2(区间为左闭右开的情况):

#区间为左闭右开的情况
​
class Solution:
    def merge(self, intervals):
​
        #先列出所有右界,找到最大的,开个新数组
        right = []
        for i in range(len(intervals)):
            right.append(intervals[i][1])
        #注意下标
        newlist = [0]*(max(right))
        #涂色
        #先把左端涂了再说
        #列出所有左界
        left = []
        for i in range(len(intervals)):
            left.append(intervals[i][0])
        #开始涂左端的色
        for i in range(len(newlist)):
            if((i+1) in left):
                newlist[i] = 1
            else:
                continue
        
        #开始涂中间的色
        #寻找两端差值大于等于2的,才需要涂(右端不涂)
        for i in intervals:
            if (i[1] - i[0])>=2:
                for j in range(i[1] - i[0] - 1):
                    #找定位元素i[0]i[1],这里应该是把左端又涂了一遍
                    newlist[i[0]+j] = 1
​
        print(newlist)
        #输出结果
        #若不存在0则输出大团圆
        if (0 not in newlist):
            return [[1,len(newlist)]]
        #若存在0,列出所有1的下标
        else:
            index = [index for index,value in enumerate(newlist) if value == 1]
            
            #遍历所有1的下标
            #设置记录点
            result_head = []
            result_tail = []
            for i in index:
                #如果当前下标-1不在index中,该位置(下标+1)就是一个区间的开端
                if (i-1) not in index:
                    result_head.append(i+1)
                #如果当前下标+1不在index中,该位置(下标+2)就是一个区间的结尾(开区间)
                if (i+1) not in index:
                    result_tail.append(i+2)
​
            #输出结果
            resultlist = []
            
            for i,j in zip(result_head,result_tail):
                resultlist.append([i,j])
            return resultlist
                
            
solu = Solution()
print(solu.merge([[1,4],[0,4]]))
​

答3(区间为左闭右开的情况+从0开始情况):

class Solution:
    def merge(self, intervals: List[List[int]]) -> List[List[int]]:
​
​
​
        #先列出所有右界,找到最大的,开个新数组
        right = []
        for i in range(len(intervals)):
            right.append(intervals[i][1])
        #注意下标
        newlist = ([0]*(max(right)+1))
        #涂色
        
        #列出所有左界
        left = []
        for i in range(len(intervals)):
            left.append(intervals[i][0])
​
​
​
        
        #开始涂左端+中间的色
        #寻找两端差值大于等于2的,才需要涂(右端不涂)
        for i in intervals:
            if (i[1] - i[0])>=2:
                for j in range(i[1] - i[0]):
                    #找定位元素i[0]i[1],这里应该是把左端又涂了一遍
                    newlist[i[0]+j] = 1
​
        
        #把右端写为0,这个是为了防止(n,n)的情况发生
        for i in intervals:
            newlist[i[1]] = 0
            
​
        #输出结果
        #若不存在0则输出大团圆(不可能存在0)
        if (0 not in newlist):
            return "疑似异常"
        #若存在0,列出所有1的下标
        else:
            index = [index for index,value in enumerate(newlist) if value == 1]
            
            #遍历所有1的下标
            #设置记录点
            result_head = []
            result_tail = []
            for i in index:
                #如果当前下标-1不在index中,该位置(下标)就是一个区间的开端
                if (i-1) not in index:
                    result_head.append(i)
                #如果当前下标+1不在index中,该位置(下标+1)就是一个区间的结尾(开区间)
                if (i+1) not in index:
                    result_tail.append(i+1)
​
            #输出结果
            resultlist = []
            
            for i,j in zip(result_head,result_tail):
                resultlist.append([i,j])
            return resultlist
​

答4(区间为左闭右开的情况+从0开始情况+去除(n,n)bug):

ps:所谓(n,n)bug就是在算左闭右开区间时,(n,n)应该被视为单独区间,不与相邻区间合并

代码先不写了

从上面的五个答案可以看出,没有经过任何算法学习的我,即使能写出相对正确的答案,也会漏掉一些情况或是写出bug,而且代码真的很冗余,行数也多,总之就是全是缺点。接下来展示的是网络上别人的正确答案,可以以此来管中窥豹一下算法的作用。

答5(参考答案):

class Solution:
    def merge(self, intervals: List[List[int]]) -> List[List[int]]:
        """
        方法:排序
        """
        intervals.sort(key = lambda x : x[0])
        merges = list()
        for interval in intervals:
            if not merges or merges[-1][-1] < interval[0]:
                merges.append(interval)
            else:
                merges[-1][-1] = max(merges[-1][-1], interval[1])
        return merges
​

解题思路:欲速则不达,我为我昨天为了省事无脑把代码从网页上复制下来的行为做检讨,今天我来补救,接下来我会详细细致解释为什么这里用方法5,以及是如何过度的。要想写出答5,我先看了一遍答5,然后自己敲,敲出来了答6。

答6(有bug):

解题思路:

1.先对所有区间进行排序,依据是左端。

2.由于数组是可变长度的,所以开一个二维数据,初始化,留着当容器。

3.接着遍历所有输入的区间,只需判断区间是否重复,就能够做出下一步操作。

4.若区间重复,则合并,不重复,就新增。

class Solution:
    def merge(self, intervals):
        # 先对输入的区间进行排序,依据是左端
        intervals.sort(key = lambda x : x[0])
        # 弄个results来存储最终结果,results是二维数组
        results = [[0]*1]
        # 遍历排序后的区间
        for i in intervals:
            # 如果i跟results中区间没有重合部分,那么新增一个区间
            if results[-1][-1] < i[0]:
                results.append(i)
            # 如果有重叠部分,就来合并,由于是升序的,所以只用考虑右端扩展
            else:
                results[-1][-1] = max(results[-1][-1], i[1])

        return results

solu = Solution()
print(solu.merge([[1,2],[2,3],[3,4]]))

答6虽然考虑了一些情况,但是有重大bug,这个bug就是每次都会有[0]这个区间出现在结果中,原因是我在创建数组时使用的是results = [[0]*1]这个方法,导致数组中已经有[0]了,那么我们会很自然的想,如果初始化为[1]是不是就避免了这个问题呢?的确,能够避免,但是这又会带来新的问题,那就是答3解决的问题,从0开始问题。也就是如果输入了[0,n]区间会导致没有从0开始。

所以,在数组声明上,不要偷懒写成[0]或者[0*1]这种方式,而是用list1 = list()这种方式,但这又会带来一个问题,由于此时声明的数组的是完全空的,也就是None的,所以在第一步判断,即判断results[-1][-1]的时候会导致找不到元素而报错,如图所示。

这就简单了,我们只需要在执行添加操作的判断条件中,加上一个条件“或者results为空”就可以解决,接下来请大家看,这代码对吗?

# 如果i跟results中区间没有重合部分(或results为空),那么新增一个区间
            if results[-1][-1] < i[0] or not results:
                results.append(i)

乍一看没什么问题,对吧,但当我们运行时,发现错误还是出现了,似乎此处新增的or not results没有起到作用一样,没错!就是没有起到作用,由于python的特性,他非常懒,条件连接词只判断前方的条件,所以并没有运行到or not results,python就急急忙忙抛出了错误。要解决这个问题就更简单了,只需要将or not results放到判断条件的第一个,就可以解决,接下来附上自己的、完整的、正确的、最终的、代码。

答7(最终boss版):

class Solution:
    def merge(self, intervals):
        # 先对输入的区间进行排序,依据是左端
        intervals.sort(key = lambda x : x[0])
        # 弄个results来存储最终结果,results是二维数组
        results = list()
        # 遍历排序后的区间
        for i in intervals:
            # 如果i跟results中区间没有重合部分(或results为空),那么新增一个区间
            if not results or results[-1][-1] < i[0]:
                results.append(i)
            # 如果有重叠部分,就来合并,由于是升序的,所以只用考虑右端扩展
            else:
                results[-1][-1] = max(results[-1][-1], i[1])

        return results

solu = Solution()
print(solu.merge([[1,2],[2,3],[3,4]]))

问题3.数组在内存中是如何存放的?

网络上的各种答案,都在讲数组在内存中是连续存放的,但这就产生了我的一个疑问:

静态的数组还行,比如C语言在数组一开始声明时就分配了数组的大小,这可以保证数组在内存中地址连续,并且无法添加长度也就是数组溢出的问题。但是在python中,数组的长度是可变的,那在使用python创建数组后,我一直对这个数组进行.append()操作,如果数组地址是连续的,那么此时新增的元素也都是在内存中连续的,那python怎么知道数组要开多大的内存空间呢?不会覆盖到别的地址吗?

答:python的数组相对很智能了,一般在声明时,他会开1.125~2倍左右长的数组,来适当留出冗余为后面的操作留余地,但是当后期数组超过一开始留的冗余之后,怎么办?此时Python会在内存中另外寻找一段连续的地址,这段地址的大小是1.25~2倍的1.25~2倍,Python会把之前数组的元素ctrl+x(移动到新的+释放旧的)过来,从而实现了数组的长度变化。现在,我们就弄懂了Python的数组在内存中的长度增加,而如果数组长度减小,Python是否会智能管理内存减小呢?还真的会!但是这些内存管理的行为是由Python的内存管理器自动处理的,程序员通常不需要关心这些细节。人生苦短,我用Python!

问题4.在你常用的语言中,如何对数组执行初始化、数据访问、修改、迭代、排序、添加、删除等操作?

解决思路:画表格,当然我们还是用可爱的Python。

数据结构初始化数据访问修改迭代排序添加删除
集合1.初始化空集合 set1 = set() 2.初始化已知元素集合 set1 = {1,2} 3.从其他可迭代对象初始化集合 set1 = set(list1) 4.从字符串初始化集合 set1 = set("hello")无序,无法用下标访问。想要访问可以转化为列表。no使用map函数进行迭代 sq = map(lambda x: x**2, set2)newlist = sorted(set1) 注意相同数据类型.add().remove() .clear()
列表 (线性列表) (链表)1.初始化空列表 list1 = list() 2.初始化已知元素列表 list1 = [1,2] 3.从其他可迭代对象(如元组、集合、字符串等)创建列表 list1 = list(tuple1)使用下标访问list1[2] = 9使用map函数进行迭代 sq = map(lambda x: x**2 , list1)list1.sort()直接修改list1 或者nl = sorted(list1).append().remove()删除第一个匹配到的元素 .pop(n)弹出索引为n的元素 .pop()弹出最后一个元素 .clear()清空列表
元组1.初始化空元组 tuple1 = () 2.初始化单元素元组 tuple1 = (5,) 3.初始化多元素元组 tuple1 = (5,6,7)或tuple1 = 2,3,4 4.从其他可迭代对象创建 tp = tuple(list1)使用下标访问不可修改使用map函数进行迭代 sq = map(lambda x: x**2, tp)list1 = sorted(tp)元素不可变元素不可变
字典1.初始化空字典 dict1 = dict() 2.使用大括号 dict1 = {'name': 'Alice', 'age': 25} 3.使用zip将两个列表打包成一个字典 dict1 = dict(zip(keys, values))使用键访问 my_dict['name']通过键下标修改 my_dict['age'] = 31利用for循环 for key, value in my_dict.items() for key in my_dict.keys() for value in my_dict.values()使用sorted newlist = sorted(dict1.keys())用键下标添加 dict1['shuaige'] = 1.pop('键名')抛出值 del my_dict['键名'] .clear()清空字典

二维数组

问题1.二维数组和一维数组有什么异同?

答:二维数组不过就是将一维数组中每个元素变成了一维数组。所以二维数组的本质就是一维数组。

例题1.旋转矩阵

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值