数据结构与算法 -- 数组

一、数组基本概念

         数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据。

线性表:线性表就是数据排成像一条线一样的结构。每个线性表上的数据最多只有前和后两个方向。其实除了数组,链表、队列、栈等也是线性表结构。

 连续的内存空间和相同类型的数:因为这两个限制,它才有了一个堪称“杀手锏”的特性:“随机访问”。但有利就有弊,这两个限制也让数组的很多操作变得非常低效,比如要想在数组中删除、插入一个数据,为了保证连续性,就需要做大量的数据搬移工作。

        数组是适合查找操作,但是查找的时间复杂度并不为 O(1)。即便是排好序的数组,你用二分查找,时间复杂度也是 O(logn)。数组支持随机访问,根据下标随机访问的时间复杂度为 O(1)。

二、数组的“插入” 和“删除”

         假设数组的长度为 n,现在,如果我们需要将一个数据插入到数组中的第 k 个位置。为了把第 k 个位置腾出来,给新来的数据,我们需要将第 k~n 这部分的元素都顺序地往后挪一位。数组插入操作的最好时间复杂度为O(1),最坏时间复杂度为O(n),平均时间复杂度为O(n)。

         如果数组中的数据是有序的,我们在某个位置插入一个新的元素时,就必须按照刚才的方法搬移 k 之后的数据。但是,如果数组中存储的数据并没有任何规律,数组只是被当作一个存储数据的集合。在这种情况下,如果要将某个数据插入到第 k 个位置,为了避免大规模的数据搬移,我们还有一个简单的办法就是,直接将第 k 位的数据搬移到数组元素的最后,把新的元素直接放入第 k 个位置。该场景下,在第k个位置插入一个元素的时间复杂度就变为O(1)。

 

         如果我们要删除第 k 个位置的数据,为了内存的连续性,也需要搬移数据,不然中间就会出现空洞,内存就不连续了。如果删除数组末尾的数据,则最好情况时间复杂度为 O(1);如果删除开头的数据,则最坏情况时间复杂度为 O(n);平均情况时间复杂度也为 O(n)。

        在某些特殊场景下,我们并不一定非得追求数组中数据的连续性。如果我们将多次删除操作集中在一起执行,这样能够有效提升删除的效率。删除数据时可以先记录下已经删除的数据。每次的删除操作并不是真正地搬移数据,只是记录数据已经被删除。当数组没有更多空间存储数据时,再触发执行一次真正的删除操作。

三、数组越界

         在使用数组的时候,一定要警惕数组越界,像C语言这种没有数组越界检测机制的语言,出现数组越界可能会带来意想不到的逻辑错误。在进行程序开发过程中,一定记得数组边界条件的检查,防止数组越界。

四、容器与数组的关系

         ArrayList 、vector最大的优势就是可以将很多数组操作的细节封装起来。比如前面提到的数组插入、删除数据时需要搬移其他数据等。另外,它还有一个优势,就是支持动态扩容。动态扩容操作涉及内存申请和数据搬移,是比较耗时的。所以,如果事先能确定需要存储的数据大小,最好在创建 ArrayList 的时候事先指定数据大小。指定大小能够减少不必要的内存申请和数据搬移,提升性能。

        对于业务开发,直接使用容器就足够了,省时省力。毕竟损耗一丢丢性能,完全不会影响到系统整体的性能。但如果你是做一些非常底层的开发,比如开发网络框架,性能的优化需要做到极致,这个时候数组就会优于容器,成为首选。

五、常见数组 题目

1、实现两个有序数组合并为一个有序数组


package main

import (
	"fmt"
)

func mergeTwoArray(arr1 []int, arr2 []int) []int {
	lengthOfArray1 := len(arr1)
	lengthOfArray2 := len(arr2)
	mergedArray := make([]int, 0)

	i := 0
	j := 0
	for i < lengthOfArray1 && j < lengthOfArray2 {
		if arr1[i] <= arr2[j] {
			mergedArray = append(mergedArray, arr1[i])
			i++
		} else if arr1[i] >= arr2[j] {
			mergedArray = append(mergedArray, arr2[j])
			j++
		}
	}

	for i <lengthOfArray1 {
		mergedArray = append(mergedArray, arr1[i])
		i++
	}
	for j <lengthOfArray2 {
		mergedArray = append(mergedArray, arr2[j])
		j++
	}
	return mergedArray
}

func main() {
	arr1 := []int{1, 3, 5, 7}
	arr2 := []int{2, 4, 6, 8, 9, 10}
	arr := mergeTwoArray(arr1, arr2)
	fmt.Println(arr)
}



[1 2 3 4 5 6 7 8 9 10]

声明:参考极客时间《数据结构与算法之美》专栏

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值