赞
踩
目录
(1)boolean add( Long e) :链表尾插元素 e
(2)boolean add(int index, Long e):根据下标插入元素 e
(1)public Long remove (int index) 删除指定下标位置结点,返回被删除的结点元素值:
(2)public boolean remove (Long e) 从前向后找,删除第一个遇到的 e 的结点:
3. public Long get ( int index) 获取指定下标元素值:
4. public Long set ( int index, Long e ) 将指定下标位置的元素值改为 e ,并返回原来的元素值:
5. public void clear () 清空链表中的所有结点:
6. public int indexOf ( Long e) 从前向后,查找链表中指定元素的下标:
7. public int lastindexOf ( Long e) 从后向前,查找链表中指定元素的下标:
8. public boolean contains ( Long e) 判断链表中是否包含元素 e :
9. public boolean isEmpty () 判断链表是否为空链表:
因为链表类也实现了线性表接口的方法,所以链表的基本操作方法和顺序表一样。这里我们定义链表中的元素类型为包装类(体现对象的形式而不是基本类型)
这里提前定义了 MyNode 类,其中的属性为:Long val;MyNode prev;MyNode next;三个属性,一个元素值 val ,一个指向前一个结点的引用 prev,一个指向后一个结点的引用 next。
- public class MyNode {
- public Long val;
- MyNode prev;
- MyNode next;
-
- public MyNode(Long val) {
- this.val = val;
- this.next = this.prev = null;
- }
-
- }
在 MyLinkedList 类(自定义链表类)中实现自定义基本方法,其中包括属性 MyNode head;MyNode last;int size;三个属性,一个是引用 head 用于指向链表第一个结点,一个是引用 last 用于指向链表最后一个结点。
(代码放在最后了)
先分析尾插操作的主要步骤:
① 将链表的最后一个结点的 next 指向要插入的结点对象 node 。
② 将要插入的结点的 prev 指向链表最后一个结点对象。
③ 将要插入的结点的 next 指向 null 。(也可以不写,因为初始化时候已经定义过了)
④ 更新链表的 last 引用,要让 last 始终指向链表中的最后一个结点。
⑤ 最后链表内结点数 size 加 1。
再考虑可能发生的情况:
① 链表 size = 0 时,链表中没有结点:尾插后链表中只有被插入的结点,直接将链表的头结点引用和尾结点引用指向被插入结点即可,此时 node 结点的 prev 和 next 都指向 null。
② 链表 size > 0 时,使用主要步骤进行操作即可。
代码:
- public boolean add(Long e) {
- MyNode node = new MyNode(e);
-
- if (size == 0){
- this.head = this.last = node;
- node.prev = null; //可以不写,初始化已经置空过
- }else{
- this.last.next = node;
- node.prev = this.last;
- this.last = node;
- }
- size++;
- return true;
- }
主要步骤:改变 4 个引用
① curNode 要移动到指定下标处。目标下标为 0 ,跳 0 下;目标下标为 2 ,跳 2 下;目标下标为 3 ,跳 3 下。总结规律可得,跳 index 下即可到目标下标处。(循环次数)
② 因为要将 e 插入指定下标处,所以需要指定下标处的上一个结点,所以提前记录 prevNode 指向指定下标的上一个结点。
③ 进行 prevNode 和 curNode 引用指向的改变。
④ 进行 node 引用指向的改变 。
考虑可能的情况:
头插:(和尾插思考方式一样)
代码:
- public void add(int index, Long e) {
- MyNode node = new MyNode(e);
- node.prev = null;
- if (index < 0 || index > size){
- throw new RuntimeException("下标不合法");
- }
- if (size == 0){
- this.head = this.last = node;
- node.next = null;
- return;
- }
- if (size == 1){
- if (index == 0){
- addaFirst(e);
- return;
- }else {
- add(e);
- return;
- }
- }
- //代码如果执行到这里,只剩下一种情况,就是 size > 1
- //所以可以不使用 if 语句进行判断 size 的情况
- if (index == 0){
- addaFirst(e);
- return;
- }else if(index == size){
- add(e);
- return;
- }
- //代码执行到这里,说明以上的情况都不是。则此时的情况应使用改变 4 个引用的操作。
- MyNode curNode = this.head;
- for (int i = 0; i < index; i++) {
- curNode = curNode.next;
- }
- MyNode prevNode = curNode.prev;
- prevNode.next = node;
- curNode.prev = node;
- node.prev = prevNode;
- node.next = curNode;
-
- size++;
- return;
- }
其中的 addFirst ()方法:头插
- //头插(为了代码的方便)
- public boolean addaFirst(Long e){
- MyNode node = new MyNode(e);
- node.prev = null;
- if (size == 0){
- this.head = this.last = node;
- node.next = null;
- }else {
- this.head.prev = node;
- node.next = this.head;
- this.head = node;
- }
- this.size++;
- return true;
- }
主要思路:
① 删除这个结点,需要让它的前一个结点越过它,指向它的后一个结点。所以需要定义三个引用
:curNode(指向指定位置结点)、prevNode(指向它的前一个结点)、nextNode(指向它的后一个结点)。
② 改变 2 个引用:让 prevNode 的 next 指向 nextNode ;让 nextNode 的 prev 指向 prevNode 。
③ size - 1。
④ 注意:因为要返回被删除的元素值,所以在改变引用前,需要提前把原来的元素值存起来。
头删和尾删:
※考虑可能出现的情况:
代码:
- //删除指定下标位置的结点,返回被删除的元素值
- @Override
- public Long remove(int index) {
- if (index < 0 ||index >= size){
- //包括 size = 0 的情况
- throw new RuntimeException("当前下标不合法");
- }
- if (size == 1){
- Long e = this.head.val;
- this.head = this.last = null;
- size = 0;
- return e;
- }
-
- if (index == 0){
- //头删
- Long e = this.head.val;
- this.head = this.head.next;
- this.head.prev = null;
- size--;
- return e;
- }
- if (index == size - 1){
- Long e = this.last.val;
- this.last = this.last.prev;
- this.last.next = null;
- size--;
- return e;
- }
- //代码执行到这里,说明以上情况都不是,此时:
- // size > 1 && index > 0 && index < size - 1
- MyNode curNode = this.head;
- for (int i = 0; i < index; i++) {
- curNode = curNode.next;
- }
- MyNode prevNode = curNode.prev;
- MyNode nextNode = curNode.next;
- Long e = curNode.val;
-
- prevNode.next = nextNode;
- nextNode.prev = prevNode;
-
- size--;
- return e;
- }
主要思路:跟上面的大同小异,主要是可能的情况不同。
① 注意:在元素结点个数 size = 1 时,进行主要思路的改变 2 个引用的操作时,要特别注意 尾结点 last 的改变。
可能出现的情况:
代码:
- //从前向后找,删除第一个遇到的 e
- @Override
- public boolean remove(Long e) {
- MyNode curNode = this.head;
- //将有元素 e 的情况都放在循环里,如果循环结束后,还没有返回,则说明该链表中没有元素 e ,删除失败,返回 false
- for (int i = 0; i < size; i++) {
- if (curNode.val.equals(e)) {
- if (i == 0){
- //进行头删
- this.head = this.head.next;
- //此时 size 可能 = 1,也可能 > 1
- //本来头删的下一步为: this.head.prev = null ;
- //但是如果 size = 1 的话,this.head = this.head.next 后,this.head = null
- //如果执行这句话,就会发生空指针异常的错误。所以我们还要分一次情况分别讨论
- if (this.head != null){
- this.head.prev = null;
- size--;
- return true;
- }else {
- // size = 1 链表中只有一个元素的情况,执行完 this.head = this.head.next 后为空链表
- this.last = null;
- size--;
- return true;
- }
- }
-
- if (i == size - 1){
- //进行尾删
- this.last = this.last.prev;
- //这里也要思考,有没有上面的情况出现
- //不会出现上面的情况,因为如果 size = 1 的话,e 只能出现在 i = 0 的时候。即 e 的下标为 0,进行头删操作
- //所以如果代码走到这里,size 至少为 2 并且 i = 1,即尾删操作
- this.last.next = null;
- size--;
- return true;
- }
-
- //代码走到这里,只剩下一种情况,要删的元素在中间结点。
- //中间结点的特点是,既有前驱元素,也有后继元素,所以不用担心会出现空指针异常的错误出现
- MyNode prevNode = curNode.prev;
- MyNode nextNode = curNode.next;
-
- prevNode.next = nextNode;
- nextNode.prev = prevNode;
- size--;
- return true;
- }
- curNode = curNode.next;
- }
- return false;
- }
主要思路:
① 涉及到下标传参,首先要考虑下标不合法的情况。
② 要将 curNode 结点移动到指定下标结点下,需要进行循环。循环后返回结点的 val 即可。
代码:
- //获取指定下标的元素值
- @Override
- public Long get(int index) {
- if (index < 0 || index >= size){
- throw new RuntimeException("下标不合法");
- }
- MyNode curNode = this.head;
- for (int i = 0; i < index; i++) {
- curNode = curNode.next;
- }
- return curNode.val;
- }
主要思路:
① 考虑下标不合法的情况。
② 将引用 curNode 移动到指定下标结点处。
③ 将原来的 val 值提前记录,再改变结点对象的 val 值。
代码:
- //将指定下标位置元素值换成 e ,并返回被修改的元素值
- @Override
- public Long set(int index, Long e) {
- if (index < 0 || index >= size){
- throw new RuntimeException("下标不合法");
- }
- MyNode curNode = this.head;
- for (int i = 0; i < index; i++) {
- curNode = curNode.next;
- }
- Long a = curNode.val;
- curNode.val = e;
- return a;
- }
主要思路:
将链表的头结点和尾结点都置空,同时结点个数 size 置零。
代码:
- @Override
- public void clear() {
- this.head = this.last = null;
- size = 0;
- }
主要思路:
① 因为链表和链表结点中,没有存储下标的属性,所以需要我们自己定义一个变量 i 来表示下标。
② 进行循环判断。
③ 每次在将 curNode 指向下一个结点的同时,i 要进行加 1 的操作。( i 从 0 开始)
④ 如果找不到的话,返回 -1 。
代码:
- @Override
- public int indexOf(Long e) {
- MyNode curNode = this.head;
- int i = 0; //表示 curNode 此时的下标
- while (curNode != null){
- if (curNode.val.equals(e)){
- return i;
- }
- i++;
- curNode = curNode.next;
- }
- return -1;
- }
主要思路:跟从前向后查找一样,唯一区别是:这里的 i 应该从 size - 1 开始,依次递减。
代码:
- @Override
- public int lastindexOf(Long e) {
- MyNode curNode = this.last;
- int i = size - 1; // 表示 curNode 此时的下标
- while (curNode != null){
- if (curNode.val.equals(e)){
- return i;
- }
- i--;
- curNode = curNode.prev;
- }
- return -1;
- }
主要思路:只要链表中有元素 e 就返回 true ,所以我们可以直接调用 indexOf () 方法进行判断。
代码:
- @Override
- public boolean contains(Long e) {
- return indexOf(e) != -1;
-
- }
主要思路: 空链表代表链表结点个数为 0 。
代码:
- @Override
- public boolean isEmpty() {
- return size == 0;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。