数组理论基础及训练
一.数组理论
1.存储方式:数组是存放在连续内存空间上的相同类型数据的集合
因为数组在内存中是连续的,所以我们可以通过下标索引方便地获取到下标对应的数据. 如图,下标都是从0开始的:
2.删除与增添数组元素
也正是因为其在内存中连续的存储方式,导致我们在删除和增添(插入)数组元素的时候,需要同时移动其他元素的地址.如图:
注意,我们删除数组元素时一般直接覆盖.
3.二维数组
先行后列,在大部分编程语言中,每一行代表一个一维数组,所以左边第一位数字代表从0开始的哪个一维数组,第二位代表该一维数组从0开始的哪个元素.
4.二维数组的存储地址连续吗
- C++中二维数组是在内存空间中排成一条线存储的,可以通过如下实验看出:
void test_arr() {
int array[2][3] = {
{0, 1, 2},
{3, 4, 5}
};
cout << &array[0][0] << " " << &array[0][1] << " " << &array[0][2] << endl;
cout << &array[1][0] << " " << &array[1][1] << " " << &array[1][2] << endl;
}
int main() {
test_arr();
}
0x7ffee4065820 0x7ffee4065824 0x7ffee4065828
0x7ffee406582c 0x7ffee4065830 0x7ffee4065834
输出的地址为十六进制,可以看出输出顺序从上到下,每行从左到右,每个元素地址差四个字节Byte,所以看出每个元素占四个字节:
- Java中二维数组的存储并不是物理上完全连续的,而是数组的数组。这与一维数组的存储有所不同,如图:
首先我们要明确一维数组在Java中是一个对象,这个对象存储了数组的元信息(如长度)和指向存储实际元素的内存,我们创建的一个数组对象实际上是用引用存储了堆内存中数组对象的指针.
图片中蓝色块代表rating引用存储的数组对象的指针,通过这个对象内存中的内容找到一个一维数组在内存中是连续的,这个里面每个元素存的是一个数组的地址指针,通过这个指针指向的就是相应行的存储地址.
二.数组的二分查找
这个真的学到了!之前感觉一直云里雾里的,现在终于参透了这个算法的本质!那就是我们搜索区间不管是左闭右闭还是左闭右开,搜索区间里的下标一定都是可能对应target的下标! 没有可能的就不要放进来,当搜索区间里已经不可能有对应target的下标时跳出while循环,返回-1代表没找到.
class Solution {
public int search(int[] nums, int target) {
int lift = 0;
int right = nums.length - 1;
while(lift <= right){
int middle = lift +((right - lift) >> 1);
if(nums[middle] > target){
right = middle - 1;
} else if (nums[middle] < target) {
lift = middle + 1;
}else{
return middle;
}
}
return -1;
}
}
三.移除元素
- 暴力解法(锻炼代码能力)
- 双指针解法(细品)
public int removeElement(int[] nums, int val) {
int slow = 0;
for (int fast = 0; fast < nums.length; fast ++){
if (nums[fast] != val){
nums[slow] = nums[fast];
slow ++;
}
}
return slow;
}
四.有序数组的平方
- 双指针
class Solution {
public int[] sortedSquares(int[] nums) {
int i = 0;
int j = nums.length-1;
int[] ints = new int[nums.length];
int n = nums.length-1;
while(i <= j){
if ((nums[i]*nums[i]) < (nums[j]*nums[j])){
ints[n] = nums[j]*nums[j];
n--;
j--;
}else{
ints[n] = nums[i]*nums[i];
n--;
i ++;
}
}
return ints;
}
}