文章目录
前言
第二章内容是数组,此系列仅根据我本人学习顺序书写,写这篇文章时,前一章链表还有一些经典LeetCode没有写入博文,但是不影响学习基础的数据结构知识。
1. 数组的概念
数组是线性表结构最基本的结构,特点是元素是一个紧密在一起的序列,相互之间不需要记录彼此的关系就能访问。
什么是线性表???(了解即可)
用通俗易懂的方式来理解,可以将线性表类比成一排排的盒子,每个盒子里放着一个数据元素。这些盒子按照一定的次序排列在一条直线上,每个盒子都有一个编号,可以通过这个编号来找到特定的盒子。而且,每个盒子都有一个左边的盒子和一个右边的盒子,除了最左边的盒子没有左边的盒子,最右边的盒子没有右边的盒子。
这样,线性表就是一个有序的、线性排列的数据集合,它非常适合用来存储一系列有序的数据,比如数组就是线性表的一种常见实现方式。当我们需要按照顺序存储和访问一组数据时,可以使用线性表这种数据结构来帮助我们管理和操作数据。
数组用索引的数字来标识每项数据在数组中的位置,且在大多数编程语言中,索引是从0算起的。我们可以根据数组中的索引快速访问数组中的元素。(老生常谈)
注意:
- 数组从0开始记录,也就是第一个存元素的位置是a[0],最后一个是a[length - 1]。
- 数组中的元素在内存中是连续存储的,且每个元素占用相同大小的内存(因为普通数组要存放相同类型的元素)。
做题注意事项:
注意数组空间不一定是满的,100的空间可能只用了10个位置,所以要注意数据个数的变量size
和数组长度length
可能不一样,解题时注意
1.1 数组存储元素的特征
- 创建了一个大小为10的数组,请问此时数组里面是什么?(博主主要学习语言为java,这里只以java为例)
答: 默认会初始化为0 - 是否可以只初始化一部分位置?初始化的本质是什么?
答: 可以只初始化一部分位置,可以讲前五个位置从小到大下入,后面空着,此时数组内容为{1,2,3,4,5,0,0,0,0,0}。
初始化的本质就是覆盖已有的值,用你需要的值去覆盖原来的0,因为数组本来是{0,0,0,0,0,0,0,0,0,0},这里只不过被你替换成了{1,2,3,4,5,0,0,0,0,0}。如果此时你想知道有效元素的个数,就必须再使用一个额外的变量,例如size来标记。 - 上面已经初始化的元素之间是否可以空着,例如初始化为{1,0,0,4,5,0,2,0,4,0}。其中0位置仍然是未初始化的?
答: 不可以!!!要初始化,必须从前往后的连续空间初始化,不可以出现空缺的情况,以上提到的情况违背数组的原则。你正在进行某种运算期间可以先给部分位置赋值,而一旦稳定了,就不可以再出现空位置的情况。 - 删除元素时,已经被删除的位置该是什么呢?例如原始数组为{1,2,3,4,5,6,7,8,0,0},我删除4之后,根据数组移动的原则,从5开始向前移动,变成{1,2,3,5,6,7,8,?,0,0}
这里?位置的值为8,这时表示元素数量的size
会减一变成7,原来8的位置仍然是8。如果通过size来标记元素数量,最后一个8则不会被访问到
2. 数组-基本操作(CRUD)
大部分题目都是int类型,所以我们用int类型来实现基本功能
2.1 数组创建和初始化
创建一维数组:
int[] arr = new int[10];
//第二种方式
int[] nums = {2,5,0,4,6,-10}; //这种方式背过
初始化数组最基本方式-循环赋值:
for (int i = 0; i < arr.length; i++) {
arr[i] = i;
}
2.2 增加一个元素
注意边界
推荐实现方式(思考注释中的问题):
/**
*
* @param arr
* @param size 数组已存储的元素数量,从1开始编号
* @param element 待插入元素
* @return
*/
public static int addByElementSequence(int[] arr, int size, int element) {
// 1.为什么不是size > arr.length?
if (size >= arr.length) {
return -1;
}
// 2.思考为什么不是index = 0 or index = size - 1?
int index = size;
//寻找插入位置
// 3.这里为什么不是 i < size - 1
for (int i = 0; i < size; i++) {
if (element < arr[i]) {
index = i;
break;
}
}
//元素后移 为什么这里不是 j = size - 1
for (int j = size; j > index; j--) {
arr[j] = arr[j - 1]; // index下标开始的元素后移一个位置
}
arr[index] = element; //插入数据
return index;
}
问题讲解: 时刻注意size是从1开始计数
问题一:size
从1开始编号,表示实际元素的个数。而arr.length
也是从1开始,当空间满的时候就是size = arr.length
,此时就不能再插入元素了。
问题二:如果已有序列{3,4,7,8},如果插入元素大于最大值8,例如9,假如index = 0
,最后结果是{9,3,4,7,8}。假如index = size = 1
,最后结果就是{3,4,7,9,8}。
问题三:时刻注意size是从1开始编号,请读者根据这个思路思考一下
问题四:size-1(也就是j - 1的位置)是该数组有效元素的最后一个索引,从这里开始让最后一个向后移动一位arr[j] = arr[j-1]
从index这个下标开始的元素向后移动一个位置,如果是j = size - 1
,那么就是从index - 1的位置开始向后移动了
2.3 删除一个元素
对于删除,元素会有不存在的情况
分两步,首先是从最左侧开始查是否存在元素,如果元素存在,则从该位置开始执行删除操作。
假如序列是 1 2 3 4 5 6 7 8 9,要删除5,则先遍历,找到5,然后从5开始执行删除操作,也就是从6开始逐步覆盖上一个元素,最终将序列变成 1 2 3 4 6 7 8 9 [9]。
代码:
public static int removeByelement(int arr[], int size, int key) {
int index = -1;
for (int i = 0; i < size; i++) {
if (arr[i] == key) {
index = i;
break;
}
}
if (index != -1) {
for (int i = index + 1; i < size; i++) {
arr[i - 1] = arr[i];
}
size--;
}
return size;
}
3. 算法热身题目—单调数组问题
LeetCode 896 单调数列
如果数组是单调递增或单调递减的,那么它是 单调 的。
如果对于所有 i <= j
,nums[i] <= nums[j]
,那么数组nums
是单调递增的。 如果对于所有 i <= j,nums[i]> = nums[j]
,那么数组 nums
是单调递减的。
当给定的数组 nums
是单调数组时返回 true,否则返回 false。
代码(具体的讲解放在注释里了,这题比较基础):
public boolean isMonotonic(int[] nums) {
boolean inc = true, dec = true;
for(int i = 0; i < nums.length - 1; i++) {
if (nums[i] > nums[i + 1]) {
inc = false;
}
if (nums[i + 1] > nums[i]) {
dec = false;
}
}
return inc || dec;
}
4. 算法热身题目—数组合并问题
LeetCode88-数组合并
给你两个按 非递减顺序 排列的整数数组 nums1
和 nums2
,另有两个整数 m
和 n
,分别表示 nums1
和 nums2
中的元素数目。
请你 合并 nums2
到 nums1
中,使合并后的数组同样按 非递减顺序 排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1
中。为了应对这种情况,nums1 的初始长度为 m + n
,其中前 m
个元素表示应合并的元素,后 n 个元素为 0
,应忽略。nums2 的长度为 n
。
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。
我们直接使用空间复杂度为O(1)的方式(这是面试常用的)
思路:从后往前插入,A、B元素数量固定,所以排序后最远位置一定是A和B元素都最大的那个,每次找到A或B中最大的那个填入即可
代码:
public void merge(int[] nums1, int m, int[] nums2, int n) {
int i = m + n - 1;
int len1 = m - 1, len2 = n - 1;
while (len1 >= 0 && len2 >= 0) {
if (nums1[len1] <= nums2[len2]) {
nums1[i--] = nums2[len2--];
} else if (nums1[len1] > nums2[len2]) {
nums1[i--] = nums1[len1--];
}
}
//若其中一个数组还有值
while (len1 != -1) {
nums1[i--] = nums1[len1--];
}
while (len2 != -1) {
nums2[i--] = nums2[len2--];
}
}