10.11(数组)
数组知识
-
Java是没有指针的,同时也不对程序员暴露其元素的地址,寻址操作完全交给虚拟机。
-
- 内存中一个地址可以存放多少个字节,相邻内存地址的差值是1bit还是1byte? 内存中的一个地址通常可以存放一个字节(8位)的数据。这是计算机内存的基本存储单元,也是最小的可寻址内存单元。
- JAVA中二维数组在内存中的排列方式是怎么样的? 每个一维数组
[i]
内部是连续存储的,但不同的一维数组之间并没有保证是连续的。这意味着在内存中,twoDArray[0]
和twoDArray[1]
的元素是分开存储的,它们的内存地址不一定相邻。 这种排列方式对于多维数组的高效表示是很重要的,因为它允许每个一维数组具有不同的长度,而不需要填充每个一维数组以使其具有相同的长度,从而节省了内存空间 - 每个一维数组的数据类型都相同,为什么他们占用的内存空间会不同呢? 这是因为Java 中的数组是引用数据类型,而每个引用指向不同的内存块。 让我通过一个示例来进一步说明这一点:
javaCopy codeint[][] twoDArray = new int[3][]; // 一个长度为3的一维数组数组 twoDArray[0] = new int[2]; twoDArray[1] = new int[3]; twoDArray[2] = new int[4]; twoDArray[0][0] = 1; twoDArray[0][1] = 2; twoDArray[1][0] = 3; twoDArray[1][1] = 4; twoDArray[1][2] = 5; twoDArray[2][0] = 6; twoDArray[2][1] = 7; twoDArray[2][2] = 8; twoDArray[2][3] = 9;
在这个示例中,twoDArray
是一个长度为3的一维数组数组,其中每个元素都是一个int[]
类型的一维数组。这些一维数组的长度可以不同,因此它们在内存中是独立存储的。每个一维数组内部是连续存储的,但它们之间的地址在内存中不一定是连续的,因为它们的长度不同。 当然如果说指定了二维数组的长度,在空间长度上肯定是大小一致的,这里主要是通过这种变长二维数组来说明这个问题。主要是清楚JAVA的数组是引用对象类型,实质上一个一维数组就是一个对象,每个对象都要new一个对象空间出来。
二分查找
个人想法
如果是暴力求解的话,时间复杂度是O(n),空间复杂度也是O(n)
优化的话也就是时间复杂度可以下降到O(logn)或者O(1),但是O(1)的话基本是上不可能,O(logn)的话,说明是要想办法取一半的元素来查找。
二分法的话,没记错应该是要找到中间值,这个的话根据索引来找,n个元素的话,分单数和双数:
错误分析:
单数的话,我就选[(n/2)+1]作为中间值索引mid_index,然后让target去和mid比较,小的话,继续mid/2+1,然后再去比。
这里的左闭右开应该是说中间值比target小的时候就是左闭右开,大就是左开右闭
Bug整理
不能在while中直接写return?还是java不能写?
不是JAVA不能在while里写return,而是说如果按照下面的方式写,那如果while循环结束了也没有执行到while循环里面的return,那最后总要return一个值,所以才会报错
超出时间限制: 这里为什么会超时呢
bug在于比如target值不存在于数组中的时候,会出现反复左右横跳的现象。target值较大时,会先去到右边的区间,但是当由于右边区间中的数字比target大时,又会把他往左边赶,但是并没有一个终止条件在这里面。
这里的话,仔细想是逻辑错了。我这里做的两个if判断条件,target大的时候是到mid_index到length之间,但是此时如果是小了的话,mid_index就会编程当前索引和0的中间值,其实这里就出了问题,应该是mid_index和上一次的mid_index之间的平均值,所以要引入left和right来记录上一个mid_index的位置
Idea调试Bug :调用方法报错,问题已解决。这里写成了类名。。。
left和right逻辑尝试失败
-
分析了一下,
mid_index
计算公式就有问题,当target
一旦在数组中没有对应的值时,此时mid_index移动的位置就出问题了。具体可以举例推导以下就会发现。
查看题解
-
首先上面我自己的问题里,更新
mid_index
的时候应该更新left
和right
以后再使用(left+right)/2
来更新才对。
-
left <= right
还是应该left < right
?这个就看我们用的区间是左闭右闭还是左闭右开,让
left
=right
,去看是否在合法区间就知道该怎么选了。 -
while循环里面为什么用
left
和right
的关系来做条件呢?
相比于我之前直接用target的值来比,其实Carl这样比也是同样的效果。
- 左闭右闭的题解看完了,尝试自己写一遍,之后再尝试自己写以下左闭右开的情况。
成功!!!
左闭右开也一次成功了,不过和Carl的代码稍微有一点点不一样,就是middle取值的时候我还是会给right先-1,其实这个我感觉是什么影响的。
-
另外Carl会用
>> 1
的方式来代替/2
,这里就刚好补一下这两者的区别。
">>1"与"/2"的区别 (右移1位运算与除以2运算的区别)
原始数据:N
N:非负数时, >>1和/2的运算结果是一样的。 10>>1=5 ; 10/2=5 0>>1=0 ;0/2=0
N:负数且是偶数,>>1和/2的运算结果是一样的。-10>>1=-5; -10/2=-5
N:负数且是奇数,>>1和/2的运算结果是不同的。 -5>>1=-3; -5/2=(int)(-2.5)=-2
即:N位负奇数时,>>1:向下取整 /2:向上取整。
移除元素
尝试暴力解法
-
尝试失败... 没办法包含下面的这种情况
看题解
- 注意,像C++中的
erase
函数实际上内部是一个O(n)的时间复杂度,java的类似接口是什么 ? 对应的是JAVA中List
的remove
方法
暴力解法需要用到两个for循环,再次尝试不看代码试一下:
再次失败...还是上次那种情况的错误
小改了一下,再次失败...
总结一下,有两个点:
-
循环的重点应该用size去表示;
-
另外,由于元素都前移了,所以索引i也要前移。
最后总算成功了。。。
(图片上传有些问题,后面有空修改一下)