一、 容器的由来,有什么好处?

对象存储不同类型的数据(String name, int age, double sal);对象多了,就用数组存起来,但数组在创建时,长度就固定了,没有办法更改,若一不小心添加多个对象,数据很容易溢出。
就需要设计一个容器类,底层还是依赖数组或链表,但是要根据不同需求,添加不同的功能,让容器方便操作存储的多个对象。

例如:ArrayList是针对数据的添加和查询设计的容器,因为设计的目的是方便查询和添加,而不是为了方便插入数据(若程序涉及到很多插入操作,则使用LinkedArrayList,LinkedArrayList底层依赖双向链表,方便插入数据),ArrayList的底层便依赖数组,但ArrayList还添加了其他功能:

  1. 泛型 — 方便存储不同类型的对象,不用再去为每个类型去编写对应的容器类;
  2. 动态扩容–弥补了数组为固定长度的缺点;
  3. 实现Iterable接口,覆写iterator()方法 – 方便用同样的代码遍历循环该容器;

算法1.1的ResizingArrayStack就是ArrayList的一个简化版

二、 为什么要分背包,队列,栈,而不用一个容器类全部实现所有功能?

窄接口设计,方便理解代码,比如用背包存储数据,就代表数据顺序不重要;用队列存储数据,就代表数据是先进先出,栈则代表数据是先进后出。

三、 数组和链表的区别?

数组创建时,长度就固定了,但是里面每个元素都会有一个索引值,查找起来非常方便。
链表是由一个个单位节点串联起来,单位节点里面存储数据,链表的元素没有对应的索引值,一般只有一个或两个索引指向链表的开头节点或末尾节点,节点之间依靠节点之间的线串起来(单向链表的每个节点里会有一个Node next指向下一个或上一个节点,双向链表的节点会有Node next 和 Node previous分别指向下一个和上一个节点)。增加元素不用担心溢出问题。

算法1.1 下压堆栈(数组实现)

底层依赖数组,实现数据后进先出,并能动态调整栈的大小,不用担心栈的溢出或浪费。
1.1.3 算法四_1.3节_背包,队列,栈总结
1.1.3 算法四_1.3节_背包,队列,栈总结
打个比方,我现在有几个正方形的玻璃珠,长×宽×高 = 1×1×1。要去店里面买一个罐子装他们,但现在店子里只有一个不透明的黑色金属罐,长×宽×高 = 1×1×10,因为金属罐底层依赖数组,所尺寸是固定的,不能伸缩,又因为是栈,所以每次装的新珠子都在所有珠子的最上面,每次取倒珠子,也只能倒出最上面的那一颗。
我需要经常往里面添加玻璃珠或取出玻璃珠,但数量自己都不确定。怕买小了,不够装,又怕买大了,自己亏了。所以我决定自己用黑科技改装这个黑色金属罐,让这个黑色金属罐能够根据里面装的珠子动态调整自己的高度。
很简单!核心:只需要买一个计数器记录当前有多少珠子,因为珠子高度是1,所以计数器的数字N就是所有珠子的高度;每次往里面添加或取出珠子时,N会和黑色金属罐的高度a.length作比较,当N=a.lenght或N<(1/4)*a.lenght时,都会开辟一个新的堆内存空间,相当于买一个新的黑色金属罐,把原来黑色金属罐里的珠子腾出来,重新装进新的黑色金属罐。

算法1.2 下压堆栈(链表实现)

底层依赖链表,实现数据后进先出,因为底层依赖链表,所以随时能动态添加数据,没有栈溢出这回事。

可以将Node理解为单元化的小罐子,每个小罐子只能装一颗玻璃珠,每个罐子外面有一根绳子指向它的下个罐子,形成一条链。现在我每次添加一颗玻璃,就需要拿出一个小罐子把玻璃珠装进去,而且将罐子外面的绳子系在上一个罐子上。

思路:因为是栈,所以删除元素是删除栈的最上面的元素,添加元素也是往最上面的元素的上面添加,又因为容器是链表,里面都是一个个的节点,所以我只需要定义一个成员变量Node first指向最上面的那一个元素。增删操作后,first指向会变,这就需要替换操作了。

替换操作细节:
Node oldfirst = first;
因为索引first里面存储的地址值后来还要赋给新元素的Node next,即新元素的next要指向之前添加的元素,所以要先保存起来,后面给新元素的next赋值时还会用到 - 定义一个Node oldfirst来保存之前添加元素的堆内存地址值。

first = new Node();
新开辟一个堆内存空间,并将堆内存的地址值赋给first,这样first就重新指向新元素的堆内存了。

first.item = item;
first.next = oldfirst;
first的Item item成员变量里存储将要添加的数据
first的Node next成员变量存储上一个元素的地址值

算法1.3 先进先出队列 (链表实现)

思路:
索引建立:先进先出,删除元素肯定是删除队列的第一个,添加元素肯定是往队列最后一个元素的后面添加。所以我要定义两个成员变量,又因为是链表,元素都是一个个节点,所以定义一个Node first指向队列的第一个元素,定义一个Node last,指向队列的最后一个元素。
节点指向:栈弹出一个元素后,first索引要接着指向下一个元素,下一个元素是之前添加的,所以节点是由栈上面的后来添加的元素指向下面的,之前添加的元素。而队列走一个元素之后,first索引要接着指向下一个元素,下一个元素是后来添加的,所以节点是由队列的前一个,之前添加的元素指向队列的后一个,之后添加的元素。

总结:

其实重点要理解容器有什么特性(栈-先进先出;队列:先进先出),再结合容器底层依赖的数据结构有什么特性(数组长度不变,单向链表里的单元节点Node只有一个指向),再去思考,删除元素是删除容器的哪一个元素(如栈永远删最上面那个,队列永远删最前面那个,背包无所谓,没有顺序),添加元素同理。再去思考需要哪些成员变量和方法,实现增删元素。

相关文章:

  • 2022-12-23
  • 2022-01-05
  • 2022-12-23
  • 2022-12-23
  • 2021-10-20
  • 2022-12-23
  • 2021-10-21
猜你喜欢
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-10-22
  • 2021-10-01
  • 2022-12-23
相关资源
相似解决方案