一、概述
链表是一种常见的数据结构,用于存储一系列具有相同类型的数据元素。它由多个节点组成,每个节点包含一个数据元素和一个指向下一个节点的指针。
链表与数组不同,它的节点在内存中不是连续存储的,而是通过每个节点中的指针链接在一起。这使得链表的插入和删除操作更加高效,但访问任意节点时需要遍历链表,因此访问操作的效率较低。
链表通常具有一个头节点,用于指向链表的第一个节点。如果链表为空,则头节点为空。在链表末尾,最后一个节点的指针通常为NULL,表示链表的结束。
链表有多种类型,包括单链表、双向链表和循环链表。单链表只有一个指向下一个节点的指针,双向链表有两个指针,一个指向前一个节点,一个指向后一个节点,而循环链表的最后一个节点的指针指向链表的头节点。
链表的优点是插入和删除操作的高效性,特别是在链表的中间位置。链表的缺点是访问操作的效率较低,因为需要遍历链表。另外,由于链表的节点需要存储额外的指针,所以链表比数组占用更多的内存空间。
总之,链表是一种灵活且常用的数据结构,在许多应用中都有广泛的应用。
在Java中,因为取消了指针,所以我们只能用内部类的方式来处理节点和节点之间的关联关系。
二、链表的特点
链表是真正的动态数据结构,不同于数组的扩容和缩容,链表本身通过next来连接下一个节点,不会存在内存浪费和溢出的情况,同时,链表也是最简单的数据结构,掌握链表可以帮助我们学习更复杂的数据结构,如图和树。学习链表也有助于更深入的理解引用和递归。
在Java中,本身也给我们提供了一种链表LinkedList,它是一个泛型类,实现了栈和队列等一系列接口方法,在学习算法和开发中会经常用到,十分的灵活好用。
三、链表的实现
为了更深刻的理解链表这种数据结构,我们将自己编写一个链表,代码如下,供读者参考:
代码实现
public class LinkedList<T> { //节点 class Node<T>{ //数据域 T data; //指针域 Node<T> next; public Node(T data) { this.data = data; } public Node(T data, Node<T> next) { this.data = data; this.next = next; } } //链表的头节点 private Node<T> Head; //链表的长度 private int length; //初始化 public LinkedList(){ //创建头节点,下一个为首元节点 this.Head = new Node<T>(null,null); this.length = 0; } //判断链表是否为空 public boolean isEmpty(){ return getLength()==0; } //获得链表长度 public int getLength(){ return this.length; } //添加节点 //头插法 public boolean addFirst(T data){ return add(data,1); } //在第几个位置插入元素 public boolean add(T data,int index){ if(index > getLength()+1 || index < 1) return false; index--; Node<T> tempNode = this.Head; int index_ = 0; while (tempNode!=null&&index!=index_){ tempNode = tempNode.next; index_++; } tempNode.next = new Node(data,tempNode.next); this.length++; return true; } //尾插法 public boolean addLast(T data){ return add(data,getLength()+1); } //遍历,这里借用Object.toString的方法,将其进行重写 @Override public String toString() { Node<T> tempNode = this.Head.next; StringBuilder builder = new StringBuilder(); while (tempNode!=null){ builder.append(tempNode.data + "->"); tempNode = tempNode.next; } builder.append("NULL"); return builder.toString(); } //查找元素是否存在 public boolean isHave(T data){ Node<T> tNode = this.Head.next; while (tNode!=null){ if(tNode.data.equals(data)) return true; tNode = tNode.next; } return false; } //获取指定位置元素 //获取首元节点元素 public T getFirst(){ return get(1); } //获取指定位置元素 public T get(int index){ if(index > getLength() || index < 1) return null; index--; Node<T> tempNode = this.Head; int index_ = 0; while (tempNode!=null&&index!=index_){ tempNode = tempNode.next; index_++; } return tempNode.next.data; } //获取尾节点元素 public T getLast(){ return get(getLength()); } //删除节点 //删除头节点 public T removeFirst(){ return remove(1); } //删除指定位置节点 public T remove(int index){ if(index > getLength() || index < 1) return null; index--; Node<T> tempNode = this.Head; int index_ = 0; while (tempNode!=null&&index!=index_){ tempNode = tempNode.next; index_++; } T res = tempNode.next.data; tempNode.next = tempNode.next.next; this.length--; return res; } //删除尾节点 public T removeLast(){ return remove(getLength()); } //删除第一个指定元素的节点 public boolean remove(T data){ Node<T> tempNode = this.Head; while (tempNode.next!=null){ if(tempNode.next.data.equals(data)){ tempNode.next = tempNode.next.next; this.length--; return true; } tempNode = tempNode.next; } return false; } //删除链表中所有是这个元素的节点 public int removeAll(T data){ int count = 0; while (remove(data)) count++; return count; } //修改指定位置的值 public boolean set(T data,int index){ remove(index); return add(data,index); } //使用递归的方式删除链表元素 public void remove_all_by_recursion(T value){ this.Head.next = recursion_remove(value,this.Head.next); } private Node recursion_remove(T value,Node node){ //递归终止条件 if(node==null) return null; //递归操作 if(node.data.equals(value)){ this.length--; return node.next; }else { node.next = recursion_remove(value,node.next); return node; } } }
测试链表
import java.util.Random; public class testMyLinked { public static void main(String[] args) { Random random = new Random(); LinkedList<Integer> linkedList = new LinkedList<>(); System.out.println(linkedList.getFirst()); System.out.println(linkedList.getLast()); for(int i = 0;i < 10;i++){ linkedList.addLast(random.nextInt(10)); System.out.println(linkedList.toString()); } linkedList.add(666,5); for(int i = 0;i < 10;i++){ linkedList.addFirst(random.nextInt(10)); System.out.println(linkedList.toString()); } System.out.println(linkedList.isHave(666)); System.out.println(linkedList.add(1145, 10)); System.out.println(linkedList.isHave(114)); System.out.println(linkedList.get(15)); System.out.println(linkedList.getFirst()); System.out.println(linkedList.getLast()); System.out.println(linkedList.remove(16)); System.out.println(linkedList.toString()); System.out.println(linkedList.removeFirst()); System.out.println(linkedList.removeLast()); System.out.println(linkedList.toString()); System.out.println(linkedList.remove(new Integer(1145))); System.out.println(linkedList.toString()); System.out.println(linkedList.removeAll(new Integer(6))); System.out.println(linkedList.toString()); System.out.println(linkedList.set(1919, 6)); System.out.println(linkedList.toString()); linkedList.remove_all_by_recursion(4); System.out.println(linkedList.toString()); } }
输出结果: