数据结构与算法(二):数组

数据结构与算法(二):数组

最近开始学习王争老师的《数据结构与算法之美》,通过总结再加上自己的思考的形式记录这门课程,文章主要作为学习历程的记录。

一、如何实现随机访问

数组是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据。(对这句话进行解释,引出了以下几个概念)

1.线性表:数组,队列,栈,链表

​ 非线性表:树和图

2.连续的内存空间和相同类型的数据

​ 由于这两个限制,若想在数组中删除或插入一个数据,为了保证连续性,就需要做大量的数据搬移工作。

​ 将链表与数组进行对比,可以得出结论:链表适合插入和删除,时间复杂度O(1);数组适合查找操作,但查找时间复杂度并不为O(1)。即便是排好序的数组,用二分查找,时间复杂度也是O(logn)。故正确的表述为数组支持随机访问,根据下标随机访问的时间复杂度为O(1)。

二、低效的插入和删除

1.数组的插入操作

​ 若在数组的末尾插入元素,此时不需要移动数据,则最好时间复杂度为O(1)

​ 若在数组的开头插入元素,那所有的数据都需要往后移一位,此时最坏时间复杂度为O(n)

​ 因此平均情况时间复杂度为 ( 1 + 2 + . . . + n ) / n = O ( n ) (1+2+...+n)/n=O(n) (1+2+...+n)/n=O(n)

2.数组的删除操作

​ 若在数组的末尾删除元素,此时不需要移动数据,则最好时间复杂度为O(1)

​ 若在数组的开头删除元素,那所有的数据都需要往前移一位,此时最坏时间复杂度为O(n)

​ 因此平均情况时间复杂度为 ( 1 + 2 + . . . + n ) / n = O ( n ) (1+2+...+n)/n=O(n) (1+2+...+n)/n=O(n)

​ 此外,若执行多次删除操作时,可以记录下已经被删除的数据,每次的删除操作并不是搬移数据,只是记录数据已经被删除,当数组没有更多的存储空间时,再触发一次真正的删除操作。
以LeetCode上第35题搜索插入位置为例,给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。你可以假设数组中无重复元素。
示例 1:

输入: [1,3,5,6], 5
输出: 2

示例 2:

输入: [1,3,5,6], 2
输出: 1

class Solution(object):
    def searchInsert(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        flag = 0
        for i in range(len(nums)):
            if nums[i]==target:
                flag = 1
                return i
        if flag == 0:
            if target<=nums[0]:
                return 0
            elif target>=nums[-1]:
                return len(nums)
            else:
                for i in range(0,len(nums)-1):
                    if nums[i]<=target and nums[i+1]>=target:
                        return i+1

三、为何数组从0开始编号

​ 从偏移角度理解a[0],0为偏移量。

​ a[k]_address = base_address + k * type_size

​ 如果从1计数,计算数组元素 a[k] 的内存地址就会变为下式,会多出k-1

​ a[k]_address = base_address + (k-1)*type_size

​ 因此,若从1开始计数,则每次访问数据的时候都会多一次进行减法运算,增加cpu的负担。

参考资料:王争《数据结构与算法之美》

你可能感兴趣的:(数据结构,数组,数据结构与算法)