文章目录
- 前言
- 一、什么是链表?
- 二、代码实现
- 总结
前言
链表是数据结构中十分简单的一种,一般有单向链表,双向链表,循环链表等,今天就使用节点来实现一个简单的单项链表类。
在下一期会实现双向链表以及循环链表的数据结构。
提示:以下是本篇文章正文内容,下面案例可供参考
一、什么是链表?
单向链表是一种线性表,实际上是由节点(Node)组成的,一个链表拥有不定数量的节点。其数据在内存中存储是不连续的,它存储的数据分散在内存中,每个结点只能也只有它能知道下一个结点的存储位置。由N各节点(Node)组成单向链表,每一个Node记录本Node的数据及下一个Node。向外暴露的只有一个头节点(Head),我们对链表的所有操作,都是直接或者间接地通过其头节点来进行的。链表在进行循环遍历时效率不高,但是插入和删除时优势明显。下图是原理图:
上图中最左边的节点即为头结点(Head),但是添加节点的顺序是从右向左的,添加的新节点会被作为新节点。最先添加的节点对下一节点的引用可以为空。引用是引用下一个节点而非下一个节点的对象。因为有着不断的引用,所以头节点就可以操作所有节点了。
下图描述了单向链表存储情况。存储是分散的,每一个节点只要记录下一节点,就把所有数据串了起来,形成了一个单向链表
节点(Node)是由一个需要储存的对象及对下一个节点的引用组成的。也就是说,节点拥有两个成员:储存的对象、对下一个节点的引用。下面图是具体的说明:
二、代码实现
首先新建一个节点类,设置存储数值的单元和存储下一个节点位置的单元:
public class Node {
Object data;//存储节点数据
Node next;//指向下一节点
}
设置一个接口,用来存放单项链表里的添加删除等抽象功能:
public interface Linkedfunction {
public Node get(int p);
public void Insert(int p, Object data);
public void delete(int p);
public void clean();
public int size();
}
接着使用一个类来继承接口,并重写接口内的抽象方法。
public class SingleLinked implements Linkedfunction {
private Node head;// 头节点
private int length;// 链表长度
/*
* 初始化链表
*/
public SingleLinked() {
head = null;
length = 0;
}
/*
* 清空链表
*/
public void clean() {
head = null;
length = 0;
}
/*
* 获取链表第p个节点
*/
public Node get(int p) {
Node current = head;
if (p > 0 && p <= length) {
for (int i = 0; i < p - 1; i++) {
current = current.next;
}
return current;
}
return null;
}
/*
* 插入节点头插法
*/
public void headInsert(Object data) {
Node n = new Node();
n.data = data;
n.next = head;
head = n;
length++;
}
/*
* 插入节点在p节点
*/
public void Insert(int p, Object data) {
Node current = head;
Node n = new Node();
n.data = data;
if (p > 0 && p <= length + 1) {
for (int i = 0; i < p - 2; i++) {
current = current.next;
}
n.next = current.next;
current.next = n;
length++;
}
}
/*
* 删除第p个节点
*/
public void delete(int p) {
Node current = head;
// 遍历链表直到p节点的前一节点
if (p > 0 && p <= length) {
if (p == 1) {// 若删除的是头节点
head = current.next;
length--;
return;
} else {
for (int i = 1; i <= p; i++) {
if (i == p - 1) {
current.next = current.next.next;
length--;
} else {
current = current.next;
}
}
}
}
}
/*
* 打印链表
*/
public void print() {
Node current = head;
for (int i = 0; i < length-1; i++) {
System.out.print(current.data + "->");
current = current.next;
}
System.out.print(current.data);
System.out.println();
}
@Override
public int size() {
return this.length;
}
}
最后新建一个test类,用来测试一下所写的单向链表的各个功能是否可以完美呈现。
public class test {
public static void main(String[] args) {
SingleLinked sl = new SingleLinked();
System.out.println("实例化一个列表");
sl.headInsert("a1");
sl.headInsert("a2");
sl.headInsert("a3");
sl.headInsert("a4");
sl.print();
System.out.println("删除第四项");
sl.delete(4);
sl.print();
System.out.println("输出第三项");
System.out.println(sl.get(3).data);
System.out.println("在第四项插入新元素");
sl.Insert(4, "a5");
sl.print();
System.out.println("在第二项插入新元素");
sl.Insert(2, "a6");
sl.print();
}
}
运行结果如下:
总结
原理:
此次实现的链表为单向链表,插入新数据的方法为头插法。链表存储是在程序运行过程中动态分配空间,相邻元素可以随意存放。所占内存空间分为两部分,一部分存储结点值,另一部分存放下一个节点的地址。
优点:
1)插入和删除速度快,只需要改变当前节点存储的下一个结点的位置就行。
2)没有空间限制,存储元素的个数无上限,基本只与内存空间大小有关。
缺点:
1)存取某个元素速度慢。
2)占用额外的空间来存储下一个结点的位置,不连续存放,浪费空间,空间碎片多。
3)查找速度慢。因为查找某个元素的时候,需要遍历链表,一个一个去查找。
时间复杂度:查找:O(n),插入/删除:O(1)