数组速刷指南

106 阅读4分钟

引言

数组是线性数据结构之一,即将相同类型的元素存储于连续内存空间。本文主要总结了数组的特点,以及推荐了针对性的练习题,涉及到的算法包括二分法、双指针、滑动窗口和模拟。

数组的定义

数组是将相同类型的元素存储于连续内存空间的数据结构,其长度不可变。

数组可以方便的通过下标索引的方式获取到下标下对应的数据。

数组的数据结构,如图所示(int array[] = {2, 3, 1, 0, 2};):

image.png

数组特点

  • 数组下标都是从0开始的。
  • 数组内存空间的地址是连续的
  • 数组在删除或者增添元素的时候,要频繁移动其他元素的地址。
  • 二维数组每种语言的实现不同,但是一般每一行内的元素的地址是连续的。C++中二维数组的元素从左到右、从上至下在地址空间上是连续的。 Go和Java可能是二维数组可能是如下排列的方式:

二维数组

可变数组

可变数组 是经常使用的数据结构,其基于数组和扩容机制实现,相比普通数组更加灵活。常用操作有:访问元素、添加元素、删除元素。

C++:

// 初始化可变数组 
vector<int> arr; 
// 向尾部添加元素 
arr.push_back(2);

Go:

var arr []int
arr = append(arr,1)

Java:

List<Integer> arr = new ArrayList<>(); 
arr.add(2);

二分法

二分法,区间的定义一般为两种,左闭右闭即[left, right],或者左闭右开即[left, right)。

  1. 第一种写法,我们定义 target 是在一个在左闭右闭的区间里,也就是[left, right]
  • for left <= right 要使用 <= ,因为left == right是有意义的,所以使用 <=
  • if nums[mid] > target 的分支 right 要赋值为 middle - 1,因为当前这个nums[mid]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle - 1
for left <= right {
    mid := left + (right-left)>>1 
    if target == nums[mid] {
        return mid
    } else if target < nums[mid] {
        right = mid - 1
    } else {
        left = mid + 1
    }
}
  1. 第二种写法,定义 target 是在一个在左闭右开的区间里,也就是[left, right) ,那么:
  • for left < right ,这里使用 < , 因为left == right在区间[left, right)是没有意义的
  • if nums[mid] > target 的分支 right 更新为 mid,因为当前nums[mid]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为mid,下一个查询区间是[left, mid)
for left < right {
    mid := left + (right-left)>>1 
    if target == nums[mid] {
        return mid
    } else if target < nums[mid] {
        right = mid
    } else {
        left = mid + 1
    }
}

704. 二分查找

35. 搜索插入位置

34. 在排序数组中查找元素的第一个和最后一个位置

67. 有效的完全平方数

69. x 的平方根

双指针法

双指针法(快慢指针法)在数组和链表的操作中是非常常见的,很多考察数组和链表操作的面试题,都使用双指针法。

26. 删除有序数组中的重复项

27. 移除元素

283. 移动零

844. 比较含退格的字符串

977. 有序数组的平方

滑动窗口

滑动窗口,需要想清楚两个指针怎么更新,还要维护关于窗口的状态值

76. 最小覆盖子串

滑动窗口可用于解决一些列的字符匹配问题,典型的问题包括:在字符串 s 中找到一个最短的子串,使得其能覆盖到目标字符串 t。对于目标字符串 t,我们可以在字符串 s 上滑动窗口,当窗口包含 t 中的全部字符后,我们再根据题意考虑能否收缩窗口。

在窗口滑动的过程中,我们可以暴力地统计出窗口中所包含的字符是否满足题目要求,但这没有利用到滑动窗口的基本性质。事实上,窗口的滑动过程可分解为以下两步基础操作:

窗口右边界往右滑动一位:窗口右端新加入一个字符,但窗口中的其他字符没有发生变化; 窗口左边界往右滑动一位:窗口左端滑出一个字符,但窗口中的其他字符没有发生变化。

209. 长度最小的子数组

904. 水果成篮

模拟法

模拟类的题目在数组中很常见,不涉及到什么算法,就是单纯的模拟,十分考察大家边界的掌控能力。

相信大家有遇到过这种情况: 感觉题目的边界调节超多,一波接着一波的判断,找边界,拆了东墙补西墙,好不容易运行通过了,代码写的十分冗余,毫无章法,其实真正解决题目的代码都是简洁的,或者有原则性的,大家可以在这道题目中体会到这一点。

54. 螺旋矩阵

59. 螺旋矩阵 II

剑指 Offer 29. 顺时针打印矩阵

这三个题基本一样