单链表-删除-增添-反转

重试数据结构的单链表,一种说不出来的感觉,码代码就是一个坑,跳进去了,就爬不出来了,以前是用c++写的链表,现在开始是用Java来写一遍。按道理来说,原理都是一样的,但是由于博主自己以前太浪,以至于从前都不知道学了啥,所以这次算是重拾吧。
下面给大家分享一下本次写单链表本宝宝给自己挖的坑或者自己掉进去的坑。

  1. 什么是链表
  2. 链表的组成
  3. 链表的实现

我们可以尝试使用现实生活中的例子去理解一下链表,链:一看到这个字,第一反应是不是狗链,链接等。当然这跟狗链的区别还是比较大的,但是不乏有共同的点。
1.链表是一种数据结构,而且跟数组队列一样都是线性表。但是他跟数组的区别就是,数组存储的空间都是连续的,但是链表都是可以分散的,也就是分线性的(咱们要专业一点),因为引用域的存在(指针域的说法用于c++),他存储的是地址。所以只要有地址,那么每个节点的分布我都可以找到。就好比——你可以轻易找到中国的任意一个省份一样。
那么链表是用来干啥呢,说了是一种数据结构,那么就是存储数据啦,有一些基本操作,比如,增删改查等。
优点:消除了数组需要预先分配内存大小的弊端。
缺点:引用域“占地面积”太大了,这是特别消耗内存的。

博主还是想去偷张图来给大家去看看链表长啥样。
因为博主画画是在太丑了,真的不想去尝试,说多了都是泪。
嘻嘻
2.从上面那个图可以看到,组成由数据域加引用域
数据域就是存储你想要存储的数据啦,在这里我们可以注意一下:
看这里
当我们不知道自己要在链表里面存储什么样的数据类型的时候,那么我们就可以使用泛型来标志。
泛型它不是一种数据类型,它只是一个符号,可以泛指Java中的引用类型
3.开始写链表
首先节点类:
节点类里面包含数据域引用域,还要构造函数,如果我们的头结点不存储东西,那么我们就需要构造一下没有参数的函数。

public class Node<E>{
 public E e;
 public Node<E> next;
 public Node() {
  this.next=null;
  this.e=null;
 }
 public Node(E e)
 {
  this.e=e;
  this.next=null;
 }
}

然后,我们就可以写个链表类啦!在这里主要给大家演示实现,增添元素,删除某位置的元素(根据位置插入某元素的原理差不多),还有反转。
在这里不得不提的一个小坑,哇,可我气死啦,大概一个小时耗在在,没错,博主就是这么傻。
我刚开始呢,定义数据域和引用域的时候都是用的private,后来我发现在链表的构造函数里,怎么样都不能让数据域或者指针域为null;
这只是因为,我把属性定义为私有,哇,这么基础,然后,我傻傻的一直找啊,上网看啊看,真是惨痛的教训。

(1)增添:

 public void append(E e)
 {
  Node<E> node=new Node<E>(e);
  if(size==0)
  {
   root.next=node;
   tail=node;
  }
  else if(size>0)
  {
   tail.next=node;
   tail=node;
  }
  size++; 
 }

这个其实没事好说,但是就是要注意一个点,头结点插入的时候一定要单独写出来,然后再定义尾节点。
(2)根据位置删除
既然是根据位置,那么我们应该通过遍历来查找到此位置的元素咯,这其实就是跟数组比较得到的不好的地方了,数组的话,直接可以根据下标来找到我们需要的元素,但是链表的话,我们就需要根据头结点来逐个遍历来找到。利用引用域,当然这也就可以看出来,每个节点之间联系密切,就算分散开,我们也是可以找得到的。
那么首先我们需要获取到节点:

public Node<E> getNode(int index)
 {
  Node<E> node=new Node<E>();
  node=root.next;
  for(int i=0;i<=index-1;i++)//得到当前节点
  {
   node=node.next;
  }
  return node;
 }

好啦,第二坑我的地方来了。我们定义的头结点是不存储东西的,那么他到底算不算是我的第一个节点呢,答案是肯定的,他并不算,没有存储东西那就不算。所以我们需要主要,这个获得的节点是当前的节点还是前一个节点,因为这都有一定的便利。
我这里写的就是获得当前节点了,因为这里会让我有些绕,等你们亲自体会就知道了,主要结合删除方法来看,你得时刻知道当前的节点,还有各种next。

然后接着就写删除方法:

 public E remove(int index)
 {
  Node<E> removeNode=getNode(index);
  E data=removeNode.e;
  Node<E> node;
  if(index==size-1)
  {
   if(index==0)
   {
    root.next=null;
    tail=root;
    size--;
   }
   else
   {
    removeNode.next=null;
    tail=removeNode;
    size--;
   }
  }
  else if(index==0)
  {
   
   root.next=removeNode.next;
   removeNode.next=null;
   size--;
  }
  
  else if(index<0||index>=size)
  {
   System.out.println("越界删除");
   return null;
  }  
  return data;
 }

第三坑:我一开始的思路是,首先判断是不是越界,(这个肯定有的),然后判断是不是尾节点,接着判断是不是删除0号元素,如果是,那么就单独写,如果不是,那就是再写。如果是尾节点,那么判断是不是删除0号元素,如果是,那就只剩下头结点了,不是再继续写。
但是这种方法使得的获取节点的方式频繁报错,最后我就变成判断,删除的位置是不是0
最后就是反转:
在这里插入图片描述

突然用截图,每次都好生气的,编辑的时候自己就会突然发现bug,气。就是粘贴不上来。
反转的话,其实就是按照正常的思路,生成一个头结点放在尾巴那里,让尾巴tail等于原来第0个节点。然后所有指针反向,注意一下,需要断开反转掉的指针。

最后的最后给大家一下测试样例,验证一下本宝宝的代码:

  Linked<String> link=new Linked<>();
  link.append("abc");
  link.append("def");
  link.append("gh");
  link.append("123");
  link.append("12");
  System.out.println("链表的长度为:"+link.size());
  for(int i=0;i<link.size();i++)
  {
   System.out.println("第"+i+"个元素为:"+link.getvalue(i));
   System.out.println();
  }
  link.remove(0);
  System.out.println("链表的长度为:"+link.size());
  for(int i=0;i<link.size();i++)
  {
   System.out.println("第"+i+"个元素为:"+link.getvalue(i));
   System.out.println();
  }
  
  link.append("789");
  link.append("10");
  System.out.println("链表的长度为:"+link.size());
  for(int i=0;i<link.size();i++)
  {
   System.out.println("第"+i+"个元素为:"+link.getvalue(i));
   System.out.println();
  }
  
  link.remove(5);
  System.out.println("链表的长度为:"+link.size());
  for(int i=0;i<link.size();i++)
  {
   System.out.println("第"+i+"个元素为:"+link.getvalue(i));
   System.out.println();
  }
  link.reverse();
  for(int i=0;i<link.size();i++)
  {
   System.out.println("第"+i+"个元素为:"+link.getvalue(i));
   System.out.println();
  }

首先是添加5个元素,然后链表长度为5;
遍历输出五个自己添加的元素,看一下是否正确。
接着删除第0个元素,再遍历输出,看一下是否成功删除。
然后再添加两个元素,删除一个元素。输出,反转输出。
在这里插入图片描述
在这里插入图片描述

从上面可以发现反转成功。goodbye!

猜你喜欢

转载自blog.csdn.net/chan_fan/article/details/85045075