数组

数组

1 理论

存储方式:

  • 存储单元是一维的结构,数组是多维的结构
  • 二维数组有两种存储方式: 以列序为主序(column major order),以行序为主序(row major order)

以0开始的索引:

  • 每个数据元素都关联一个正数值,我们称之为索引,它表明数组中每个元素所在的位置。大部分语言将初始索引定义为零。

数组特点:

  • 占用一段连续的内存空间
  • 假设有个数组[0, 1, 2],它会以连续的空间存在在内存中;如果此时需要添加一个元素3,但是一开始连续的空间后面的位置存储了其他数据,此时就要重新开辟一段连续的空间来存储新的数组,之前的空间会被释放

数组的基本操作:

  • Insert——在指定索引位置插入一个元素
  • Get——返回指定索引位置的元素
  • Delete——删除指定索引位置的元素
  • Size——得到数组所有元素的数量

操作的时间复杂度:

  • 读取元素,支持随机(索引)访问,且时间复杂度为O(1)
  • 添加元素时间复杂度:O(n)
  • 删除元素时间复杂度:O(n) (尾部删除时间复杂度O(1))

在中间增加的时候,要先将最后一个元素往后移,然后倒数第二个元素往后移,以此类推,不然,先移动前面的会把后面的元素覆盖掉。在中间删除的时候,要先将删除元素的后一个元素往前移,后面的依次往前移

python之Numpy

  • NumPy 是一个 Python 包。 它代表 “Numeric Python”。 它是一个由多维数组对象和用于处理数组的例程集合组成的库。
  • NumPy 中定义的最重要的对象是称为 ndarray 的 N 维数组类型。 它描述相同类型的元素集合。 可以使用基于零的索引访问集合中的项目。
  • ndarray中的每个元素在内存中使用相同大小的块。 ndarray中的每个元素是数据类型对象的对象(称为 dtype)。
  • 从ndarray对象提取的任何元素(通过切片)由一个数组标量类型的 Python 对象表示。 下图显示了ndarray,数据类型对象(dtype)和数组标量类型之间的关系。
  • ndarray对象由计算机内存中的一维连续区域组成,带有将每个元素映射到内存块中某个位置的索引方案。 内存块以按行(C 风格)或按列(FORTRAN 或 MatLab 风格)的方式保存元素。

参考:https://blog.csdn.net/xiamiflying/article/details/82081036
参考:https://blog.csdn.net/Daycym/article/details/84309359
参考:https://blog.csdn.net/a373595475/article/details/79580734

2 相关编程

参考程序原博客:
https://blog.csdn.net/u013109501/article/details/88020739
https://blog.csdn.net/u013109501/article/details/88027009
1. 实现一个支持动态扩容的数组

class Arr:
    def __init__(self, capacity=10):
        """
        构造函数
        :param capacity: 数组最大容量,不指定的话默认为10
        """
        self._capacity = capacity
        self._size = 0                                  # 数组有效元素的数目,初始化为0
        self._data = [None] * self._capacity    # 由于python的list是动态扩展的,而我们要实现底层具有固定容量、占用一段连续的内存空间的数组,所以用None来作为无效元素的标识
	def add(self, index, elem):
        """
        向数组中添加一个元素,注意数组占用的是一段连续的内存空间,所以在添加元素后,数组还是要保证这个特点的,因此需要将后面的元素都向后挪一个位置,而且要注意要先从
        尾部开始挪,防止元素之间的覆盖
        时间复杂度:O(n)
        :param index:   添加的元素所在的索引
        :param elem:    所要添加的元素
        """
        if index < 0 or index > self._size:     # 插入的位置无效
            raise Exception('Add Filed. Require 0 <= index <= self._size')
        if self._size == self._capacity:        # 满了
            self._resize(self._capacity * 2)    # 默认扩容当前容量的二倍。容量翻倍要比容量加上一个固定值要好,这样做均摊复杂度为O(1)。具体请百度
 
        for i in range(self._size - 1, index - 1, -1):  # 从尾部开始挪动元素,在index处腾出一个空间
                                                        # 一定要注意在步长为负数的情况下,区间是左开右闭区间,即(index, self._size - 1],所以是index-1,与正常的左闭右开区间是相反的!
            self._data[i + 1] = self._data[i]
        self._data[index] = elem        # 将该位置赋值为elem
        self._size += 1                 # 数组有效元素数加1
 
    def addLast(self, elem):
        """
        向数组尾部添加元素
        时间复杂度:O(1)
        :param elem: 所要添加的元素
        """
        self.add(self._size, elem) # 直接调用add方法,注意不用再次判定合法性了,因为add函数中已经判断过了
 
    def addFirst(self, elem):
        """
        想数组头部添加元素
        时间复杂度:O(n)
        :param elem: 所要添加的元素
        """
        self.add(0, elem)   # 同理直接调用add方法
    def remove(self, index):
        """
        删除索引为index的元素。index后面的元素都要向前移动一个位置
        时间复杂度:O(n)
        :param index: 目标索引
        :return:      位于该索引的元素的值
        """
        if index < 0 or index >= self._size:    # index合法性检查
            raise Exception('Remove failed.Require 0 <= index < self._size')
        ret = self._data[index]                 # 拷贝一下index处的元素,便于返回
        for i in range(index + 1, self._size):  # index后面的元素都向前挪一个位置
            self._data[i - 1] = self._data[i]
        self._size -= 1         # 维护self._size
        self._data[self._size] = None   # 最后一个元素的垃圾回收
 
        if self._size and self._capacity // self._size == 4:   # 如果当前有效元素为总容量的四分之一且还存在有效元素,则将容量缩减为原来的一半
            self._resize(self._capacity // 2)
        return ret
 
    def removeFirst(self):
        """
        删除数组首位置的元素
        时间复杂度:O(n)
        :return: 数组首位置的元素
        """
        return self.remove(0)   # 调用remove函数
 
    def removeLast(self):
        """
        删除数组末尾的元素
        时间复杂度:O(1)
        :return: 数组末尾的元素
        """
        return self.remove(self._size - 1)      # 调用remove函数
     def _resize(self, new_capacity):
        """
        数组容量放缩至new_capacity,私有成员函数
        :param new_capacity: 新的容量
        """
        new_arr = Arr(new_capacity)         # 建立一个新的数组new_arr,容量为new_capacity
        for i in range(self._size):
            new_arr.addLast(self._data[i])  # 将当前数组的元素按当前顺序全部移动到new_arr中
        self._capacity = new_capacity       # 数组容量变为new_capacity
        self._data = new_arr._data          # 将new_arr._data赋值给self._data,从而完成数组的容量放缩操作

2.实现一个大小固定的有序数组,支持动态增删改操作

class Arr:
    def __init__(self, capacity=10):
        """
        构造函数
        :param capacity: 数组最大容量,不指定的话默认为10
        """
        self._capacity = capacity
        self._size = 0                                  # 数组有效元素的数目,初始化为0
        self._data = [None] * self._capacity    # 由于python的list是动态扩展的,而我们要实现底层具有固定容量、占用一段连续的内存空间的数组,所以用None来作为无效元素的标识
	def add(self, index, elem):
        """
        向数组中添加一个元素,注意数组占用的是一段连续的内存空间,所以在添加元素后,数组还是要保证这个特点的,因此需要将后面的元素都向后挪一个位置,而且要注意要先从
        尾部开始挪,防止元素之间的覆盖
        时间复杂度:O(n)
        :param index:   添加的元素所在的索引
        :param elem:    所要添加的元素
        """
        if index < 0 or index > self._size:     # 插入的位置无效
            raise Exception('Add Filed. Require 0 <= index <= self._size')
        if self._size == self._capacity:        # 满了
            raise Exception('Add Filed. The array is full.')
        for i in range(self._size - 1, index - 1, -1):  # 从尾部开始挪动元素,在index处腾出一个空间
                                                        # 一定要注意在步长为负数的情况下,区间是左开右闭区间,即(index, self._size - 1],所以是index-1,与正常的左闭右开区间是相反的!
            self._data[i + 1] = self._data[i]
        self._data[index] = elem        # 将该位置赋值为elem
        self._size += 1                 # 数组有效元素数加1
 
    def addLast(self, elem):
        """
        向数组尾部添加元素
        时间复杂度:O(1)
        :param elem: 所要添加的元素
        """
        self.add(self._size, elem) # 直接调用add方法,注意不用再次判定合法性了,因为add函数中已经判断过了
 
    def addFirst(self, elem):
        """
        想数组头部添加元素
        时间复杂度:O(n)
        :param elem: 所要添加的元素
        """
        self.add(0, elem)   # 同理直接调用add方法
    def remove(self, index):
        """
        删除索引为index的元素。index后面的元素都要向前移动一个位置
        时间复杂度:O(n)
        :param index: 目标索引
        :return:      位于该索引的元素的值
        """
        if index < 0 or index >= self._size:    # index合法性检查
            raise Exception('Remove failed.Require 0 <= index < self._size')
        ret = self._data[index]                 # 拷贝一下index处的元素,便于返回
        for i in range(index + 1, self._size):  # index后面的元素都向前挪一个位置
            self._data[i - 1] = self._data[i]
        self._size -= 1         # 维护self._size
        self._data[self._size] = None   # 最后一个元素的垃圾回收
        return ret
 
    def removeFirst(self):
        """
        删除数组首位置的元素
        时间复杂度:O(n)
        :return: 数组首位置的元素
        """
        return self.remove(0)   # 调用remove函数
 
    def removeLast(self):
        """
        删除数组末尾的元素
        时间复杂度:O(1)
        :return: 数组末尾的元素
        """
        return self.remove(self._size - 1)      # 调用remove函数

3. 实现两个有序数组合并为一个有序数组

test1 = [1,3,5,7,9]
test2=[2,4,6,7,10,11,34,55]
 
def mergetest(test1,test2):
                result=[ ]
                len1=len(test1)
                len2=len(test2)
                i=0
                j=0
                while i<len1 and j<len2:
                                if test1[i]<=test2[j]:
                                                result.append(test1[i])
                                                i+=1
                                else:
                                                result.append(test2[j])
                                                j+=1
                if i<len1:
                                for z in range(i+1,len1):
                                                result.append(test1[z])
                elif j<len2:
                                for z in range(j+1,len2):
                                                result.append(test2[z])
                return result
print (mergetest(test1,test2))

练习

1. Three Sum(求三数之和)

英文版:https://leetcode.com/problems/3sum/

中文版:https://leetcode-cn.com/problems/3sum/

给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。

  • 例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],
    满足要求的三元组集合为:
    [
    [-1, 0, 1],
    [-1, -1, 2]
    ]

解题思路:

  • 1.将数组排序 (为了解决重复的问题)
    2.定义三个指针,i,j,k。遍历i,遍历j,遍历k。j从i之后开始,k从j之后开始遍历。
  • 此程序是评论中的,先排序(从小到大),定义三个指针,用指针i遍历,指针l,r分别从i之后 最后 开始,向中间靠
import numpy as np 
import time

class Solution(object):
    def threeSum(self,nums):
        nums.sort()#从小到大排序
        res=[]
        for i in range(len(nums)):
            if i==0 or nums[i]>nums[i-1]:#在遍历过程,若有重复的值则不考虑
                j=i+1
                while j<(len(nums)-1) : #j要从i之后开始遍历
                    if j==i+1 or nums[j]>nums[j-1]:
                        k=j+1
                        while k<len(nums):
                            if k==j+1 or nums[k]>nums[k-1]:
                                s=nums[i]+nums[j]+nums[k]
                                if s==0:  #sum=0的保存下来
                                    res.append([nums[i],nums[j],nums[k]])
                                    k=len(nums) #因为已经排过序,所以不用再算后面的
                                elif s>0:
                                    j=len(nums)-1#后面的值比前面的大
                                    break
                                else:
                                    k=k+1
                            else:
                                k=k+1
                    else:
                        j=j+1
                    j=j+1
        return res
#先排序(从小到大),定义三个指针,用指针i遍历,指针l,r分别从i之后 最后 开始,向中间靠
    def threeSum_ref(self,nums):
        nums.sort()
        res =[]
        i = 0
        for i in range(len(nums)):
            if i == 0 or nums[i]>nums[i-1]:
                l = i+1
                r = len(nums)-1
                while l < r:
                    s = nums[i] + nums[l] +nums[r]
                    if s ==0: #s=0,l,r都往中间靠(有增大有减小可能有新的0出现)
                        res.append([nums[i],nums[l],nums[r]])
                        l +=1
                        r -=1
                        while l < r and nums[l] == nums[l-1]:
                            l += 1  
                        while r > l and nums[r] == nums[r+1]:
                            r -= 1
                    elif s>0:
                        r -=1
                    else :
                        l +=1
        return res

2. Majority Element(求众数)

英文版:https://leetcode.com/problems/majority-element/

中文版:https://leetcode-cn.com/problems/majority-element/

给定一个大小为 n 的数组,找到其中的众数。众数是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在众数。

示例 1:
输入: [3,2,3]
输出: 3
示例 2:
输入: [2,2,1,1,1,2,2]
输出: 2

解题思路

  • 如果这个数组是有序的,那么数组中间的数一定就是众数,所以一种解法是先排序,然后返回排序后数组中间位置的值,算法时间复杂度是 O(nlogn)
  • 摩尔投票算法,这种算法的思想是:众数是出现次数最多的数,众数和其他数出现的次数一一抵消后,剩下的一定就是众数,所以只需遍历一遍数组,就可以找到众数,时间复杂度是 O(n)
class Solution(object):
    def majorityElement(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        nums=sorted(nums)
        
        # 找出排在中间的那个数就OK了,“//”是取整
        n = len(nums)
    
        return nums[n // 2]

    def majorityElement1(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        #从第一个数开始count=1,遇到相同的就加1,遇到不同的就减1,减到0就重新换个数开始计数,总能找到最多的那个
        #遍历数据,若前后相等则cnt+1,若不同cnt-1,因为众数的数量一定会超过一半,最终的众数会是最终使cnt=0时的数据
        cnt, ret = 0, 0
        for num in nums:
            if cnt == 0:
                ret = num
            if num != ret:
                cnt -= 1
            else:
                cnt += 1
        return ret
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值