数组
数组
数组
引文:数组看起来简单基础,但是很多人没有理解这个数据结构的精髓。带着为什么数组要从0开始编号,而不是从1开始的问题,进入主题。
数组如何实现随机访问
-
数组是一种线性数据结构,用连续的存储空间存储相同类型的数据
-
线性表:数组、链表、队列、栈
-
非线性表: 树、图
-
数组如何实现下标随机访问?
- 一维数组寻址公式:
a[i]_address = base_address + i*data_type_size
- 纠正数组和链表的错误认识。数组的查找操作时间复杂度并不是O(1)。即便是有序数组,用二分查找,时间复杂度也是O(logn) . 正确表述:数组支持随机访问,根据下标随即访问的时间复杂度为O(1)。
低效的插入和删除
-
插入:最好O(1)【尾插】 最坏O(n) 【头插】 平均O(n)
-
另一种插入: 数组若无序,插入新的元素时,可以将第k个位置的元素移动到数组末尾,把新的元素插入到第k个位置,此处复杂度为 O(1)
-
删除:最好O(1)【尾删】 最坏O(n) 【头删】 平均O(n)
-
多次删除集中在一起,提高效率
记录下已经被删除的数据,每次的删除操作并不是搬移数据,只是记录数据已经被删除,当数组没有更多的存储空间时,再触发一次真正的删除操作。即JVM标记清除垃圾回收算法。
数组的访问越界问题
例子:
int main(int argc, char* argv[]){
int i = 0;
int arr[3] = {0};
for(; i<=3; i++){
arr[i] = 0;
printf("hello world\n");
}
return 0;
}
- 此例在《C陷阱与缺陷》出现过:如果用来编译这段程序的编译器按照内存地址递减的方式给变量分配内存,那么内存中的 i 将会被置为0,则为死循环永远出不去。 【注】64位操作系统下,编译器默认会8字节对齐。 类型同为int
容器能否完全替代数组?
相比于数字,java中的ArrayList封装了数组的很多操作,并支持动态扩容。一旦超过存储容量,扩容时比较消耗内存,因为涉及到内存申请和数据搬移。
数组适合的场景:
- Java ArrayList 的使用涉及装箱和拆箱,有一定的性能损耗,如果特别关注性能,可以考虑数组。
- 若数据大小事先已知,并且涉及的数据操作非常简单,可以使用数组。
- 表示多维数组的时候,数组往往更加直观。
- 业务开发容器即可,底层开发,如网络框架,性能优化。选择数组
解答开篇问题
- 从偏移(offset)角度理解 a[0] 0 为偏移量,如果从1开始计数,会多出 k-1 。增加了一次减法运算,增加CPU负担。为什么循环要写成
for(int i = 0;i<3;i++)
而不是for(int i = 0 ;i<=2;i++)
。第一个直接就可以算出$3-0 = 3$ 有三个数据,而后者 $2-0+1$个数据,多出1个加法运算,很恼火。 - 也有一定的历史原因。
课后练习
三数之和
-
题目描述:给定一个包含 n 个整数的数组
nums
,判断nums
中是否存在三个元素 *a,b,c ,*使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。**注意:**答案中不可以包含重复的三元组。
例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4], 满足要求的三元组集合为: [ [-1, 0, 1], [-1, -1, 2] ]
-
Ans:
1 public class Sum { 2 public List<List<Integer>> threeSum(int[] nums){ 3 List<List<Integer>> res = new ArrayList<>(); 4 Arrays.sort(nums); 5 for (int i = 0; i < nums.length - 2; i++){ 6 if(i > 0 && nums[i] == nums[i-1]) continue; 7 int low = i + 1, high = nums.length - 1, sum = 0 - nums[i]; 8 while (low < high) { 9 if(nums[low] + nums[high] == sum){ 10 res.add(Arrays.asList(nums[i],nums[low],nums[high])); 11 while(low < high && nums[low] == nums[low+1]) low++; 12 while(low < high && nums[high] == nums[high-1]) high--; 13 low++; 14 high--; 15 }else if (nums[low] + nums[high] < sum) { 16 low++; 17 }else{ 18 high--; 19 } 20 } 21 } 22 return res; 23 } 24 }