核心介绍💁🏻
今天周末尝试这吧昨天的动态数组的一个Demo算是总结完了,也发现了自己之前面试过程中,背的八股文其实有很大一部分都是不明其所以然的,不知道为什么要这样子去写,其实还是收获了很多的,之前的一些不明朗的地方也变的渐渐的明确了很多。
功能模块
在写动态链表这个demo的例子的时候,一开始想的其实很简单的,就是复刻一个ArrayList,然后写几个方法,基本上就完事了,其中可能稍微有点逻辑性的就是add(int index, E element),remove(int index)。这两个方法,其他的方法基本上都是简单的几行操作,之前在写这些方法的时候,尤其在工作中用的时候,压根没有去仔细思考过,为什么要这样,为什么add的时候,可以无限的往后面添加,LIST为什么不会出现想数组一样的溢出的情况,今天也算是大致了解了一些东西;
在执行add(int index,E element)这个方法的时候,会执行对应的扩容的操作,其实之前在背面试题的八股文,或者在经历过面试的大部分人都知道,这个ArrayList有动态扩容的机制,因为在JAVA操作数据库,把数据从数据库中取出来的操作,大部分都是以List作为返回结果,基本上没有以数组作为返回结果的。在工作中用到的大部分都是ArrayList操作多一点,在将配合Stream流,可以做很多操作。但是对于之前的,这个ArrayList是怎么来的,其实也很简单,就是有数组演变而来的,数组怎么演变而来的,因为数组是固定的长度的容量,一旦执行了类似 int[] array=new int[5];之后这个容量就固定了,你在添加第6个元素,那就出现数组溢出了,ArrayList的添加是怎么做的,每次都往后面添加元素,每次添加的时候都判断这个添加的元素和当前的数组的容量做判断,如果小于当前的数组容量,那不做判断,否则就要进行扩容,这个里面扩容因子是1.5倍。
其实我记得就是1.5倍,背八股文这个记了很多次了,为什么是1.5倍,不是1.2,1.4,或者为啥不直接扩充2倍,这个主要还是节省空间,其中最主要的就是为了方便位运算的操作。
如何扩容?,这就更简单的了,申请一个新的空间,然后将新的空间的容量,从原来旧的空间的容量扩大为1.5倍,然后把每个元素挨个移动过去,然后在将对象从新指向新的空间,这样就完成了扩容,非常简单,但是之前八股文是怎么描述的:新数组容量为旧数组的1.5倍:newCapacity = 1.5 * oldCapacity ,并且将旧数组内容通过Array.copyOf全部复制到新数组。此时,size还未真正+1,新旧数组长度(size一致),不过容量不同。把这里的系数1.5,称作扩容因子k = newCapacity / oldCapacity。这个描述非常的专业化,我也背过这段话,但是我当时背的时候完全是不理解的,什么1.5为啥不是1.25,为啥一定是1.5,因为1.5 可以充分利用移位操作,减少浮点数或者运算时间和运算次数,就这么可能是多思考后的简单一句话吧,也是我思考上的懒惰没有去仔细的想想原因,很多时候学这个都是只顾与追赶进度,而不是说真的去学习去思考。扩容很简单,八股文里面抽象出来的说法也很准确,但是我当时是不理解的。
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacity(size + 1);
for (int i = size; i > index; i--) {
elements[i] = elements[i - 1];
}
elements[index] = element;
size++;
}
private void ensureCapacity(int capacity) {
int oldCapacity = elements.length;
if (oldCapacity >= capacity) return;
// 新容量为旧容量的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
E[] newElements = (E[]) new Object[newCapacity];
for (int i = 0; i
这个是remove的方法,其实对比上面的add的方法,也我发现了一个我之前一直困扰我的问题,什么时候为啥从index开始,什么时候有为啥从size开始,这为什么有时候是i++,什么时候有从开始变成i--,之前做数据结构,不知道为啥这样做,反正盲敲这些例子,自己敲个几遍,也能记住remove这个开头的for循环是int i=index+1, i index ;i--这种的循环操作,如果从index 开始诺,那很容易就把中间index ----》size-1这个过程中的所有元素都给覆盖掉,从最后到index, 这个循环的过程用for循环标识就是这样子标识的 int i =size -1 ;i>index ;i--;这里面只是最外层的循环的操作,就是执行每一个挪动的元素的位置还要变,这个时候就是抽象的过程,elements[i] = elements[i-1] 其实还是自己的思维不够严谨,你要把前面一个的位置挪到到后面一个位置,那用语言描述就是那样描述,为什么之前不理解,就是抽象能力不够具象化;那remove操作刚好是反过来的,怎么挪动,remove,就是从index ---》size 的操作,这种操作就要从小的开始,所以条件就变成了int i= index+ 1 ;i
线性表里面的从大到小的循环思想,和从小到大的循环思想,前一个位置的数据给后后一个位置的数据位置,就是add,但是反过来,后一个位置的元素给前一个位置的元素就是remove,这个思想的到今天才理解到里面作者设计的巧妙所在,如果说之前设计的这些只是我在用这个ArrayList的工具,每次用气来,都没有对象移动的这种巧妙设计感所在,更多的可能只是重复性的完成功能任务,而这些元素的位置移动就是数据结构和算法的基础的部分,之前一直没有去理解里面的思想,这样是我之前为什么一直学数据结构,一直不知道里面的思想到底在哪里体现,巧妙的设计,就是本应该如此,这样。
public E remove(int index) {
rangeCheck(index);
E old = elements[index];
for (int i = index + 1; i
总 结
这个操作链表的大部分操作,类似get,set 操作,或者clear操作,工作中用的其实不多,真的类似注入add操作用的是非常多的,但是想remove操作用的就更少了,这个就需要用的迭代器进行操作,当然我到现在也不知道设计迭代器的合理的应用秒到底秒在哪里,很多数据结构的设计都是经过了很多次的演绎和设计,总结出来的结果,但是我之前学习数据结构的方法,类似囫囵吞枣式的学习,不仅仅只了解到了皮毛,也是我当时只能学到皮毛。主要原因是前面的思想理解的不够透彻,仅仅是简简单单的敲了敲书上的样例的代码,不沟理解真实的情况和思想,导致在后面的动态规划,二叉树,或者是HashMap这种理所应当的抽象的过程中的时候,学起来是非常的吃力和费劲的,即使可以将样例代码完整的敲出来,但是也不能体会到里面设计的巧妙之处。我相信我可以做到每天的日更的,即使水一篇也要是和数据结构相关的文章。
积累,专注,持之以后的坚持下去
2023.07.02
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.fwqtg.net
相关推荐: 使用Actor-Critic的DDPG强化学习算法控制双关节机械臂
在本文中,我们将介绍在 Reacher 环境中训练智能代理控制双关节机械臂,这是一种使用 Unity ML-Agents 工具包开发的基于 Unity 的模拟程序。 我们的目标是高精度的到达目标位置,所以这里我们可以使用专为连续状态和动作空间设计的最先进的De…