链表是非常常见的一类线性结构的数据结构,每个节点包含有指针域和数据域,常见的包括单项列表、双向列表、循环列表。这篇文章将详细介绍单向链表。
单向链表每个节点包含当前节点的数据域和一个指向下一个节点的指针域,如下:
本文将介绍链表的尾节点插入、头结点插入、指定位置插入、删除头结点、删除尾节点、删除指定节点、删除指定元素,链表反转、链表是否为空、链表长度、获取头结点、获取尾节点。
链表的节点表示:
public class Node {
//下一个节点
public Node next;
//当前节点的数据
public String data;
public Node(String data) {
this.data = data;
this.next = null;
}
}
在创建好链表节点之后下来看链表的构造方法和成员变量,成员方法包括一个完整链表的头结点、尾节点以及链表的长度:
public class SingleLinkedList {
//头结点
private Node head;
//尾节点
private Node tail;
//长度
private int length;
public SingleLinkedList() {
this.head = null;
this.tail = null;
}
}
1、插入元素:尾部插入
/**
* 插入数据
*/
public Node insert(String data) {
//新节点
Node node = new Node(data);
if (isEmpty()) {
//首次插入
head = node;
tail = node;
} else {
//非首次插入
tail.next = node;
tail = node;
}
length++;
return node;
}
2、插入元素:头部插入
/**
* 头部插入元素
*/
public Node insertHead(String data) {
Node node = new Node(data);
Node lastNode;
lastNode = head;
head = node;
head.next = lastNode;
length++;
return node;
}
3、插入元素:指定位置
/**
* 指定位置插入
*/
public Node insert(String data, int position) {
if (position < 0) {
throw new IndexOutOfBoundsException();
}
//新节点
Node node = new Node(data);
if (position == 0) {
//插入头部
insertHead(data);
} else if (isEmpty() || position >= length()) {
//插入尾部
insert(data);
} else {
//插入中间
//node上一个元素
Node current = head;
//node的下一个元素
Node nextNode = current.next;
for (int i = 1; i < length(); i++) {
if (i == position) {
//上一个元素指向node
current.next = node;
//node下一个元素指向原始current的下一个
node.next = nextNode;
break;
} else {
//更新下一个节点
current = current.next;
nextNode = current.next;
}
}
length++;
}
return node;
}
4、删除元素:从头结点删除
/**
* 删除元素:从尾节点删除
*/
public Node deleteTail() {
Node deleteNode = tail;
if (isEmpty()) {
//没有元素。删除失败,抛出异常
throw new RuntimeException("please insert element first");
}
if (length == 1) {
//只有一个元素,头尾元素相同,直接删除
head = null;
tail = null;
} else {
//含义多个元素
Node lastNode = head;
//lastNode.next不会空指针就执行完毕,以为tail存在
while (lastNode.next != tail) {
lastNode = lastNode.next;
}
tail = lastNode;
tail.next = null;
}
length--;
return deleteNode;
}
5、删除元素:指定位置删除
/**
* 从指定位置删除
*/
public Node delete(int position) {
if (isEmpty()) {
//没有元素。删除失败,抛出异常
throw new RuntimeException("please insert element first");
}
if (position < 0 || position > length() - 1) {
//下标越界
throw new IndexOutOfBoundsException();
}
if (position == 0) {
//删除头部
return deleteHead();
} else if (position == length() - 1) {
//删除尾部
return deleteTail();
} else {
//删除中间,至少有3个元素
//上一个元素
Node lastNode = head;
//当前元素
Node currentNode = lastNode.next;
//下一个元素
Node nextNode = currentNode.next;
for (int i = 1; i < length(); i++) {
if (i == position) {
//上一个元素指向node
lastNode.next = nextNode;
break;
} else {
//更新下一个节点
lastNode = currentNode;
currentNode = nextNode;
nextNode = nextNode.next;
}
}
length--;
return currentNode;
}
}
6、删除元素:指定元素
/**
* 删除指定元素
*/
public String delete(String data) {
if (data == null) {
throw new NullPointerException();
}
if (isEmpty()) {
return null;
}
if (head.data.equals(data)) {
//头结点
deleteHead();
return data;
} else if (tail.data.equals(data)) {
deleteTail();
return data;
} else {
//删除中间的
//上一个元素
Node lastNode = null;
//当前元素
Node currentNode = head;
//下一个元素
Node nextNode = currentNode.next;
while (currentNode != null) {
if (currentNode.data.equals(data)) {
lastNode.next = nextNode;
length--;
return data;
}
if (nextNode == null) {
return null;
} else {
lastNode = currentNode;
currentNode = nextNode;
nextNode = nextNode.next;
}
}
}
return null;
}
7、链表反转:迭代法
其他的链表操作算法都不算太难,只要想通了写起来调试调试运行起来应该没问题,但是链表的反转需要一定的技巧了,由于头结点指向下一个引用,所以头结点是个关键,在链表的反转中通常有两种方法一种是迭代法,另一种是递归法,下面详细介绍迭代法的执行步骤,以链表数据1234为例,反转后的值为4321,初始结构如下:
创建三个变量第一个指向当一个节点,第二个指向当前节点,第三个指向下一个节点,使用while循环,每循环一次,向下移动一个位置,直到当前的节点为null跳出循环。
第一步:将第二个元素的指针域指向上一个,即当前节点为2,上一个节点为1,下一个节点为3,2指向1
第二步:进行下一次循环,向下走一步,当前当前节点为3,上一个节点为2,下一个节点为4,3指向2
第三步:再进行一次循环,以此类推,4节点指向3节点……
最后:将头结点和第二个节点之间的链断开
形成最终反转的链表如下:
通用算法表示如下:
/**
* 链表的反转:迭代法
*/
public Node revirse(Node head) {
tail = head;
if (isEmpty()) {
System.out.println("没有可以反转的链表");
}
if (head.next == null) {
return head;
}
//反转后的节点
Node reNode = revirse(head.next);
head.next.next = head;
head.next = null;
return reNode;
}
8、链表反转:递归法
/**
* 链表的反转:迭代法
*/
public Node revirse(Node head) {
tail = head;
if (isEmpty()) {
System.out.println("没有可以反转的链表");
}
if (head.next == null) {
return head;
}
//反转后的节点
Node reNode = revirse(head.next);
head.next.next = head;
head.next = null;
return reNode;
}
9、查看头结点
/**
* 查看头结点
*/
public Node getHead() {
return head;
}
10、查看尾节点
/**
* 查看尾节点
*/
public Node getTail() {
return tail;
}
11、查看所有节点
/**
* 查看所有节点
*/
public void display() {
if (isEmpty()) {
System.out.println("链表为空,请先插入元素");
return;
}
Node current = head;
while (current != null) {
System.out.print(current.data + " ");
current = current.next;
}
System.out.println();
}
12、节点是否为空
/**
* 查看节点是否为空
*/
public boolean isEmpty() {
return length == 0;
}
13、查看链表长度
/**
* 查看链表长度
*/
public int length() {
return length;
}
介绍完毕,关于双端链表看下一篇介绍!