代码随想录算法训练营第一天| 704. 二分查找、27. 移除元素。

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一个值,所以才会报错

image-20231010233026061

超出时间限制: 这里为什么会超时呢

bug在于比如target值不存在于数组中的时候,会出现反复左右横跳的现象。target值较大时,会先去到右边的区间,但是当由于右边区间中的数字比target大时,又会把他往左边赶,但是并没有一个终止条件在这里面。

这里的话,仔细想是逻辑错了。我这里做的两个if判断条件,target大的时候是到mid_index到length之间,但是此时如果是小了的话,mid_index就会编程当前索引和0的中间值,其实这里就出了问题,应该是mid_index和上一次的mid_index之间的平均值,所以要引入left和right来记录上一个mid_index的位置

image-20231010233645058

Idea调试Bug :调用方法报错,问题已解决。这里写成了类名。。。

image-20231011000243407

left和right逻辑尝试失败
  • 分析了一下,mid_index计算公式就有问题,当target一旦在数组中没有对应的值时,此时mid_index移动的位置就出问题了。具体可以举例推导以下就会发现。

  • image-20231011004043788

image-20231011004104410

查看题解

  • 首先上面我自己的问题里,更新mid_index的时候应该更新leftright以后再使用(left+right)/2来更新才对。

  • left <= right还是应该left < right?

    这个就看我们用的区间是左闭右闭还是左闭右开,让left=right,去看是否在合法区间就知道该怎么选了。

  • while循环里面为什么用leftright的关系来做条件呢?

相比于我之前直接用target的值来比,其实Carl这样比也是同样的效果。

  • 左闭右闭的题解看完了,尝试自己写一遍,之后再尝试自己写以下左闭右开的情况。

成功!!!

image-20231011193546831

左闭右开也一次成功了,不过和Carl的代码稍微有一点点不一样,就是middle取值的时候我还是会给right先-1,其实这个我感觉是什么影响的。

image-20231011194718017

  • 另外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:向上取整。

移除元素

尝试暴力解法

  • 尝试失败... 没办法包含下面的这种情况

    image-20231011203345355

看题解

  • 注意,像C++中的erase函数实际上内部是一个O(n)的时间复杂度,java的类似接口是什么 ? 对应的是JAVA中Listremove方法

暴力解法需要用到两个for循环,再次尝试不看代码试一下:

再次失败...还是上次那种情况的错误

image-20231011205213442

小改了一下,再次失败...

image-20231011205623977

总结一下,有两个点

  1. 循环的重点应该用size去表示;

  2. 另外,由于元素都前移了,所以索引i也要前移。

最后总算成功了。。。

(图片上传有些问题,后面有空修改一下)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值