科技改变生活 · 科技引领未来

  • 首页
  • 资讯
  • 技术
  • 百科
  • 问答
  • 学习
  • 看看
  • 站长
  • 生活
  • 快讯

首页 > 问答 > 媒体运营

add一e多少钱(美团技术拷问)

时间:2022-09-27 04:04 作者:马熙明

作者沉默王二来源公众号沉默王二一、linkedList的剖白大家好,我是linkedList,和ArrayList是同门师兄弟,但我俩练的内功却完全不同。师兄练的是动态数组,我练的是链表。问大家一个问题,知道我为什么要练链表这门内功吗?举个

作者沉默王二

来源公众号沉默王二

一、linkedList 的剖白

大家好,我是 linkedList,和 ArrayList 是同门师兄弟,但我俩练的内功却完全不同。师兄练的是动态数组,我练的是链表。

问大家一个问题,知道我为什么要练链表这门内功吗?

举个例子来讲吧,假如你们手头要管理一推票据,可能有一张,也可能有一亿张。

该怎么办呢?

申请一个 10G 的大数组等着?那万一票据只有 100 张呢?

申请一个默认大小的数组,随着数据量的增大扩容?要知道扩容是需要重新复制数组的,很耗时间。

关键是,数组还有一个弊端就是,假如现在有 500 万张票据,现在要从中间删除一个票据,就需要把 250 万张票据往前移动一格。

遇到这种情况的时候,我师兄几乎情绪崩溃,难受的要命。师父不忍心看到师兄这样痛苦,于是打我进入师门那一天,就强迫我练链表这门内功,一开始我很不理解,害怕师父偏心,不把师门最厉害的内功教我。

直到有一天,我亲眼目睹师兄差点因为移动数据而走火入魔,我才明白师父的良苦用心。从此以后,我苦练“链表”这门内功,取得了显著的进步,师父和师兄都夸我有天赋。

链表这门内功大致分为三个层次:

  • 第一层叫做“单向链表”,我只有一个后指针,指向下一个数据;
  • 第二层叫做“双向链表”,我有两个指针,后指针指向下一个数据,前指针指向上一个数据。
  • 第三层叫做“二叉树”,把后指针去掉,换成左右指针。

但我现在的功力还达不到第三层,不过师父说我有这个潜力,练成神功是早晚的事。

二、linkedList 的内功心法

好了,经过我这么样的一个剖白后,大家对我应该已经不陌生了。那么接下来,我给大家展示一下我的内功心法。

我的内功心法主要是一个私有的静态内部类,叫 Node,也就是节点。

private static class Node {     E item;     Node next;     Node prev;      Node(Node prev, E element, Node next) {         this.item = element;         this.next = next;         this.prev = prev;     } } 

它由三部分组成:

  • 节点上的元素
  • 下一个节点
  • 上一个节点

我画幅图给你们展示下吧。

add一e多少钱(美团技术拷问)

  • 对于第一个节点来说,prev 为 null;
  • 对于最后一个节点来说,next 为 null;
  • 其余的节点呢,prev 指向前一个,next 指向后一个。

我的内功心法就这么简单,其实我早已经牢记在心了。但师父叮嘱我,每天早上醒来的时候,每天晚上睡觉的时候,一定要默默地背诵一遍。虽然我有些厌烦,但我对师父的教诲从来都是言听计从。

03、linkedList 的招式

和师兄 ArrayList 一样,我的招式也无外乎“增删改查”这 4 种。在此之前,我们都必须得初始化。

linkedList list = new linkedList(); 

师兄在初始化的时候,默认大小为 10,也可以指定大小,依据要存储的元素数量来。我就不需要。

1)招式一:增

可以调用 add 方法添加元素:

list.add("沉默王二"); list.add("沉默王三"); list.add("沉默王四"); 

add 方法内部其实调用的是 linkLast 方法:

public boolean add(E e) {     linkLast(e);     return true; } 

linkLast,顾名思义,就是在链表的尾部链接:

void linkLast(E e) {     final Node l = last;     final Node newNode = new Node<>(l, e, null);     last = newNode;     if (l == null)         first = newNode;     else         l.next = newNode;     size++;     modCount++; } 
  • 添加第一个元素的时候,first 和 last 都为 null。
  • 然后新建一个节点 newNode,它的 prev 和 next 也为 null。
  • 然后把 last 和 first 都赋值为 newNode。

此时还不能称之为链表,因为前后节点都是断裂的。

  • 添加第二个元素的时候,first 和 last 都指向的是第一个节点。
  • 然后新建一个节点 newNode,它的 prev 指向的是第一个节点,next 为 null。
  • 然后把第一个节点的 next 赋值为 newNode。

此时的链表还不完整。

  • 添加第三个元素的时候,first 指向的是第一个节点,last 指向的是最后一个节点。
  • 然后新建一个节点 newNode,它的 prev 指向的是第二个节点,next 为 null。
  • 然后把第二个节点的 next 赋值为 newNode。

此时的链表已经完整了。

我这个增的招式,还可以演化成另外两个:

  • addFirst() 方法将元素添加到第一位;
  • addLast() 方法将元素添加到末尾。

addFirst 内部其实调用的是 linkFirst:

public void addFirst(E e) {     linkFirst(e); } 

linkFirst 负责把新的节点设为 first,并将新的 first 的 next 更新为之前的 first。

private void linkFirst(E e) {     final Node f = first;     final Node newNode = new Node<>(null, e, f);     first = newNode;     if (f == null)         last = newNode;     else         f.prev = newNode;     size++;     modCount++; } 

addLast 的内核其实和 addFirst 差不多,就交给大家自行理解了。

2)招式二:删

我这个删的招式还挺多的:

  • remove():删除第一个节点
  • remove(int):删除指定位置的节点
  • remove(Object):删除指定元素的节点
  • removeFirst():删除第一个节点
  • removeLast():删除最后一个节点

remove 内部调用的是 removeFirst,所以这两个招式的功效一样。

remove(int) 内部其实调用的是 unlink 方法。

public E remove(int index) {     checkElementIndex(index);     return unlink(node(index)); } 

unlink 方法其实很好理解,就是更新当前节点的 next 和 prev,然后把当前节点上的元素设为 null。

E unlink(Node x) {     // assert x != null;     final E element = x.item;     final Node next = x.next;     final Node prev = x.prev;      if (prev == null) {         first = next;     } else {         prev.next = next;         x.prev = null;     }      if (next == null) {         last = prev;     } else {         next.prev = prev;         x.next = null;     }      x.item = null;     size--;     modCount++;     return element; } 

remove(Object) 内部也调用了 unlink 方法,只不过在此之前要先找到元素所在的节点:

public boolean remove(Object o) {     if (o == null) {         for (Node x = first; x != null; x = x.next) {             if (x.item == null) {                 unlink(x);                 return true;             }         }     } else {         for (Node x = first; x != null; x = x.next) {             if (o.equals(x.item)) {                 unlink(x);                 return true;             }         }     }     return false; } 

这内部就分为两种,一种是元素为 null 的时候,必须使用 == 来判断;一种是元素为非 null 的时候,要使用 equals 来判断。equals 是不能用来判 null 的,会抛出 NPE 错误。

removeFirst 内部调用的是 unlinkFirst 方法:

public E removeFirst() {     final Node f = first;     if (f == null)         throw new NoSuchElementException();     return unlinkFirst(f); } 

unlinkFirst 负责的就是把第一个节点毁尸灭迹,并且捎带把后一个节点的 prev 设为 null。

private E unlinkFirst(Node f) {     // assert f == first && f != null;     final E element = f.item;     final Node next = f.next;     f.item = null;     f.next = null; // help GC     first = next;     if (next == null)         last = null;     else         next.prev = null;     size--;     modCount++;     return element; } 

3)招式三:改

可以调用 set() 方法来更新元素:

list.set(0, "沉默王五"); 

来看一下 set() 方法:

public E set(int index, E element) {     checkElementIndex(index);     Node x = node(index);     E oldVal = x.item;     x.item = element;     return oldVal; } 

首先对指定的下标进行检查,看是否越界;然后根据下标查找原有的节点:

Node node(int index) {     // assert isElementIndex(index);      if (index < (size >> 1)) {         Node x = first;         for (int i = 0; i < index; i++)             x = x.next;         return x;     } else {         Node x = last;         for (int i = size - 1; i > index; i--)             x = x.prev;         return x;     } } 

size >> 1:也就是右移一位,相当于除以 2。对于计算机来说,移位比除法运算效率更高,因为数据在计算机内部都是二进制存储的。

换句话说,node 方法会对下标进行一个初步判断,如果靠近前半截,就从下标 0 开始遍历;如果靠近后半截,就从末尾开始遍历。

找到指定下标的节点就简单了,直接把原有节点的元素替换成新的节点就 OK 了,prev 和 next 都不用改动。

4)招式四:查

我这个查的招式可以分为两种:

  • indexOf(Object):查找某个元素所在的位置
  • get(int):查找某个位置上的元素

indexOf 的内部分为两种,一种是元素为 null 的时候,必须使用 == 来判断;一种是元素为非 null 的时候,要使用 equals 来判断。因为 equals 是不能用来判 null 的,会抛出 NPE 错误。

public int indexOf(Object o) {     int index = 0;     if (o == null) {         for (Node x = first; x != null; x = x.next) {             if (x.item == null)                 return index;             index++;         }     } else {         for (Node x = first; x != null; x = x.next) {             if (o.equals(x.item))                 return index;             index++;         }     }     return -1; } 

get 方法的内核其实还是 node 方法,这个之前已经说明过了,这里略过。

public E get(int index) {     checkElementIndex(index);     return node(index).item; } 

其实,查这个招式还可以演化为其他的一些,比如说:

  • getFirst() 方法用于获取第一个元素;
  • getLast() 方法用于获取最后一个元素;
  • poll() 和 pollFirst() 方法用于删除并返回第一个元素(两个方法尽管名字不同,但方法体是完全相同的);
  • pollLast() 方法用于删除并返回最后一个元素;
  • peekFirst() 方法用于返回但不删除第一个元素。

四、linkedList 的挑战

说句实在话,我不是很喜欢和师兄 ArrayList 拿来比较,因为我们各自修炼的内功不同,没有孰高孰低。

虽然师兄经常喊我一声师弟,但我们之间其实挺和谐的。但我知道,在外人眼里,同门师兄弟,总要一较高下的。

比如说,我们俩在增删改查时候的时间复杂度。

也许这就是命运吧,从我进入师门的那天起,这种争论就一直没有停息过。

无论外人怎么看待我们,在我眼里,师兄永远都是一哥,我敬重他,他也愿意保护我。

相关话题

  • 烫头发大概多少钱(理发店200块和2000块的烫染发有啥差别)
  • 52度的五粮液多少钱一瓶(第八代经典五粮液涨价到)
  • 去日本需要多少钱(2022日本入境指南)
  • 买车贷款利率是多少钱(贷款15万)
  • 激光治近视多少钱(做一次激光近视手术)
  • 柿饼多少钱一斤(黑柿子价格超过普通柿子10倍)
  • 三七片多少钱一盒(经常喝三七粉)
  • 扫路车多少钱一辆(扫路车怎么选)
  • zx6r多少钱(2019款川崎忍者ZX)
  • 速效救心丸多少钱(突发心梗)
  • 海马机油多少钱(9块油价又如何)
  • 南京红盒烟多少钱(1942年)
  • 脂肪瘤治疗多少钱(脂肪瘤是怎么引起的)
  • 长命百岁锁多少钱(礼物不在贵)
  • 柿饼多少钱一斤(俗称)
  • mri检查需要多少钱(磁共振检查到底是什么)
  • 240挖机多少钱(2019)
  • 一般的翡翠手镯多少钱(大妈戴四色翡翠手镯)
  • 人血白蛋白多少钱(为什么做完手术)
  • 水电装修多少钱一平方(水电改造时)

热门推荐

  • 淘工厂上线“百城百味·百万爆款”计划,扩充生鲜食品供给力!
  • 京东发动“内容”之战,视频、直播或成突破口!
  • 饿了么发布即时零售行业首个商家AI经营工具!
  • 侯毅卸任引热议,业内人士:新零售不会退场!
  • 多平台媒体账号如何高效引流?
  • 新手短视频运营的技巧汇总!
  • 微信公众号自运营和代运营该选哪个?
  • 电商运营都该做哪些工作?
  • 淘宝成立直播电商公司,提供“保姆式”全托管运营服务!
  • 新零售渠道有哪些?
  • 小红书常用的推广引流方法有哪些?
  • 微信私域流量3招搞定!
  • 电商运营主要工作是什么?
  • 运营搜索怎么做?
  • 新零售的风刮起来了!它都有哪些模式?
  • 做新媒体人需必备的4个运营技能,助力打造爆款!
  • 公众号日常如何做好内容维护和运营推广?
  • 如何选择私域运营的工具呢?
  • 超市的新型经营模式有哪些?
  • 美团买菜更名“小象超市”背后,走上即时零售的新赛道!

马熙明

关注
免责声明:本文章由会员“马熙明”发布,如果文章侵权,请联系我们处理,本站仅提供信息存储空间服务 如因作品内容、版权和其他问题请于本站联系

关注排行榜

  1. 1淘工厂上线“百城百味·百万爆款”计划,扩充生鲜食品供给力!
  2. 2京东发动“内容”之战,视频、直播或成突破口!
  3. 3饿了么发布即时零售行业首个商家AI经营工具!
  4. 4侯毅卸任引热议,业内人士:新零售不会退场!
  5. 5多平台媒体账号如何高效引流?
  6. 6新手短视频运营的技巧汇总!
  7. 7微信公众号自运营和代运营该选哪个?
  8. 8电商运营都该做哪些工作?
  9. 9淘宝成立直播电商公司,提供“保姆式”全托管运营服务!
  10. 10新零售渠道有哪些?

编辑精选

Copyright ©2009-2022 KeJiTian.Com, All Rights Reserved

版权所有 未经许可不得转载

增值电信业务经营许可证备案号:辽ICP备14006349号

网站介绍 商务合作 免责声明 - html - txt - xml