-
数组
-
基础知识
定义:一种线性表数据结构。它使用一组连续的内存空间,来存储一组具有相同类型的数据。
数组的结构特性:线性结构、顺序存储结构
数组的最大特点:可以随机访问数据,即根据 "下标索引" ,直接访问任意的数组元素
数组第一个元素的地址被称为 "首地址" ,由于数组元素的地址是连续的,后续元素的地址都可以根据首地址,用一个公式计算出来,从而实现随机访问的功能。这个公式被称为 "寻址公式"。
寻址公式:下标i对应的元素地址 = 数据首地址 + i * 单个数据元素所占的内存大小
-
数组的分类
-
一维数组
可以形象的理解为一个数轴,数组的元素就是数轴上的各个点,任意两点之间仅有前后关系。
一维数组是单下标变量,类似于高中时期学过的集合,但与之不同的是,数组的元素下标以0开始。
-
二维数组
二维数组的本质是以数组作为数据元素的数组,即 数组的数组。
二维数组有多种理解方式,个人倾向于以下两种
-
将二维数组形象理解为平面直角坐标系
二维数组的元素可以视为一个平面直角坐标系中的点,每一个点都有一个它的坐标(x,y),任意两点之间有上下和左右 的关系。
-
将二维数组视为若干个一维数组的集合
这也是理解多维数组的一个通用方法,对于任意一个二维数组array[i][j],都可以视为由i个长度为j的一维数组的集 合。同一维数组类似的,二维数组中的下标i、j也从0开始。
值得注意的是,在C或C++语言中,所有一维数组的长度必须相同,而在JAVA中,则可以不同。
-
-
三维数组
与二维数组类似,三维数组也可以理解为一个空间直角坐标系或者若干个二维数组组成的集合。
二维,三维或更高维的数组,统称为多维数组
-
-
数组的基本操作
-
访问元素
我们可以通过下标访问数组中的第i个元素,只要下标i满足下面的公式即可.
-
查找元素
我们可以查找数组中是否含有值为value的元素,通过一个for循环,遍历数组中的元素,如果找到则返回元素下标,未找到则返回某个特值(如-1)。
示例代码如下:
//在数组array中查找值value第一次出现的位置 for(int i = 0; i < len(array); i++)//遍历数组,寻找值为value的元素第一次出现的位置 { if(value == array[i]) { return i; break; } } if(i == len(array)-1)//未找到,返回特值-1 { return -1; }
算法分析:
时间复杂度O(n),该算法运行时间依赖于数组元素的规模
空间复杂度O(1),该算法不需要额外空间
-
添加元素
-
向数组末尾添加元素value
如果我们在某一段程序中申请了长度为n的数组,而实际占用的长度小于n,那么可以直接将value放在数组尾部的空闲位置。如果长度已经占满,则添加失败。
一般申请的数组均为定长度数组,不可随意改变长度,需要申请动态数组时,可以使用STL里的victor类
-
向数组第i个位置(array[i])添加元素value
首先需要检查插入下标i是否合法,若合法且数组尾部还有空闲位置,则需要将i至len(array)-1位的元素依次后移一位,最后将value添加到第i位即可。
示例代码如下:
//向数组第i个位置添加元素value //已知下标i合法 for(int j = len(array) - 1; j >= i; j--) { array[j + 1] = array[j];//元素后移一位 } array[i] = value;//将新元素添加到第i位
算法分析:
时间复杂度O(n),用于将元素后移
空间复杂度O(1)
-
-
改变元素
改变元素的方法与访问元素类似,当我们试图改变数组第i个元素的时候,只需要访问下标,使用赋值语句用新值覆盖掉旧值即可。
-
删除元素
与添加元素类似,删除元素可以细分为以下两种:
-
删除数组末尾的元素
只需要将数组的计数值 (即len(array))减一,使末尾元素的下标非法即可。此算法时空复杂度为O(1)
-
删除数组第i个位置的元素
与添加元素类似,我们找到第i个位置,将i+1到len(array)-1位置的元素依次左移一位即可,新值将自动覆盖旧值
示例代码如下:
//删除数组第i个位置的元素 //已知下标i合法 for(int j = i; j <= len(array) - 1; j++) { array[j] = array[j + 1];//元素前移一位 }
算法分析:
时间复杂度O(n),用于将元素前移
空间复杂度O(1)
-
-
-
-
每日练习
Question 1 0066.加一
没什么可说的,满十进一,利用for循环判断即可,需要注意的是,如果digits[0]也需要进一的话,直接进位会导致下标越界。
代码如下:
class Solution { public: vector<int> plusOne(vector<int>& digits) { int n = digits.size();//计算digits的长度 digits[n-1]++;//给最后一位加一 for(int i = n -1; i >= 0; i--)//循环判断,满十进一 { if(digits[i] == 10) { //如果digits[0]为10,进一会使得下标越界,因此定义一个新的,长度为n+1的数组 if(i == 0) { vector<int> ans(n + 1); ans[0] = 1; return ans; } digits[i] = 0; digits [i - 1]++; } } return digits; } };
算法分析:
时间复杂度O(n),其中n是数组元素的数量
空间复杂度O(1)
Question 2 0724. 寻找数组的中心下标
我首先尝试了常规思路:遍历数组,对于每一个元素nums[i],分别计算它左边元素的和sumL和右边元素的和sumR,然后进行比较。如果相等,则返回i,如果遍历结束仍找不到相等的位置,返回-1
代码如下:
class Solution { public: int pivotIndex(vector<int>& nums) { int n = nums.size(); int i = 0; for(i = 0; i < n; i++) { int sumL = 0, sumR = 0; for(int j = 0; j < i; j++) { sumL += nums[j]; } for(int m = i+1; m < n; m++) { sumR += nums[m]; } if(sumL == sumR) { return i; } } return -1; } };
算法分析:
时间复杂度O(n^2),其中n为数组元素的数量
空间复杂度O(1),变量所占的内存空间不会随着n的增大而增大
这样的代码没有问题,但是会超时,所以我们需要进一步降低时间复杂度。观察发现,代码的时间复杂度较高主要是由两个嵌套的for循环导致的,因此我们需要将它变为一个循环或两个相互独立的循环
于是有了下面的代码:
class Solution { public: int pivotIndex(vector<int>& nums) { int n = nums.size(); int sumL = 0, sumR = 0; int i = 0; for(i = 0; i < n; i++)//计算nums的总和 { sumR += nums[i]; } for(i = 0; i < n; i++) { if((2 * sumL) == (sumR - nums[i]))//从0开始,计算左和,如果满足此表达式,说明左和等于右和 { return i; } sumL += nums[i]; } return -1;//没有任何一个i满足上述表达式,返回-1 } };
算法分析:
时间复杂度O(n),其中n为数组元素的数量
空间复杂度O(1)
这样我们就成功降低的算法的时间复杂度,成功AC
Question 3 0048. 旋转图像
虽然题目上说了需要原地旋转,但我们仍可以借用一个新矩阵NewMatrix来解决问题。当有了新矩阵之后,我们会发现,原矩阵第i行第j列的元素,会被换到倒数第j行第i列,即,两个矩阵之间的元素关系满足公式:
利用这个公式,我们可以写出代码如下:
class Solution { public: void rotate(vector<vector<int>>& matrix) { int n = matrix.size(); auto NewMatrix = matrix; for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { NewMatrix[j][n-i-1] = matrix[i][j]; } } matrix = NewMatrix; } };
算法分析:
时间复杂度O(n^2),主要由嵌套的for循环导致
空间复杂度O(n^2),主要由NewMatrix导致
-
总结
这两天的学习,我对数组的相关知识和操作有了更为详细的了解,对于二维数组 (矩阵) 的相关知识掌握的不如一维数组熟练,接下来需要在这方面再加以努力。
Day_2&Day_3 数组基础
最新推荐文章于 2024-09-15 11:40:06 发布