数组学习总结
一、数组的定义
一维数组:
定义:数组是指有序的元素序列。
数组在内存中的存储方式:数组是存放在连续内存空间上的相同类型数据的集合(数组内存空间的地址是连续的)。
数组的删除与插入:在数组的尾部插入或者删除元素不会对其他的元素的位置有影响,但是如果在数组的中间位置插入或者删除元素那么就要移动它后面的元素。
优点:数组是可以随机访问的,遍历是非常的方便的。
缺点:不适合频繁的插入与删除。
二维数组:
二维数组其实就是一个矩阵。
二维数组的存储结构:二维数组其实就是一个一维的线性数组存储着其他一维数组的首地址。所以有一个问题二维数组在内存中是连续的吗?答案是:不是连续的,假如定义一个3 * 4的二维数组,他在内存中不是以3 * 4的连续地址空间,而是四条连续的地址空间组成的。(三条用来存储数据,一条用来存储前面三个数组的地址)
二、常见算法
二分法
前面说过了,数组对于元素的访问是非常的友好的,所以数组对于二分查找是非常友好的。
适用条件:数组中的元素有序(判断二分法能不能用的标准),数组中可以有重复的元素,但是会导致查找的结果不唯一。
时间复杂度分析:假设数组有n(n >= 1)个元素,查找次数最多位logn,最少为1次,所以时间复杂度为O(logn)
空间复杂度:不需要额外的空间,所以空间复杂度为O(1).
练手题目:Leetcode 35
题目:
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素。
实例一:
输入: [1,3,5,6], 5 输出: 2
实例二:
输入: [1,3,5,6], 2 输出: 1
题解:
int searchInsert(vector<int>& nums, int target)
{
int len = nums.size();
//头指针
int pre = 0;
//尾指针,为防止数组为空时尾指针等于0,所以这里稍加判断
int tail = len - 1 > 0 ? len - 1 : 0;
int mid = 0;
//最终结果
int index = -1;
//二分查找
while(pre <= tail)
{
mid = (pre + tail) / 2;
if(nums[mid] == target)
{
index = mid;
break;
}
else if(nums[mid] > target)
{
tail = mid - 1;
}
else
{
pre = mid + 1;
}
}
//index没有被赋值就说明该数组没有这个元素,所以插入这个元素
//但是插入到什么位置呢?我们知道这个元素一定在最后的pre与tail
//的区间中,现在区间里只有nums[mid]一个元素,所以目标元素一定
//出现在mid的左边或者右遍
if(index == -1)
{
//出现在右遍
if(target > nums[mid])
{
index = mid + 1;
nums.insert(nums.begin() + mid + 1, target);
}
else
{
//出现在左边
index = mid;
nums.insert(nums.begin() + mid, target);
}
}
return index;
}
注意的问题:
- 数组中最大的问题就是数组边界条件的设定,如本题
pre <= tail
可以,pre < tail
可不可以呢,其实是可以的,关键的问题是在你的条件之下,能不能把所有的元素都覆盖到并且数组不越界,只要满足以上的条件,那么条件就是合适的。 - 关于mid的求法,
mid = (pre + tail) / 2
和mid = left + (tial - pre) / 2
这两中算法中哪个更好,其实是第二种,原因是:假如pre和tail的数据类型都是int,那么pre + tail
就存在int类型溢出的问题,但是第二种就不会出现这种可能。
未完待续。。。。。