一、概念
(一)一些名词
链表:顾名思义,就是像链条一样,一节一节串起来的数据结构,每两小节的连接点被称之为链表的节点。
节点:链表中存储的每个元素称为节点,节点包括数据域和指针域。
数据域:数据域存储的是元素的值。
指针域:指针域存储的是当前元素与其前后元素之间的联系,简单来说就是前后元素的指针/内存地址。前后元素分别称为前驱节点和后继节点,其指针域分别称为前驱(prev)和后继(next)。
头结点:可以将链表的第一个节点作为头节点。
尾节点:可以将链表的最后一个节点作为尾节点。
(二)分类
链表可以简单分为单向链表、双向链表、循环链表、双向循环链表。
1、单向链表
特点:具有头节点,没有尾节点,最后一个节点通过头节点向后遍历访问。每个节点的指针域只有后继,没有前驱,只能单向遍历。最后一个节点的后继是null。
2、双向链表
特点:具有头节点和尾节点,每个节点的指针域包括前驱和后继,可以双向遍历(从头节点向尾节点遍历或者从尾节点向头节点遍历)。头节点的前驱和尾节点的后继都是null。
3、循环链表
特点:单向循环链表,在单向链表的基础上将最后一个节点的后继指向头节点。
4、双向循环链表
特点:在双向链表的基础上将头节点的前驱指向尾节点,将尾节点的后继指向头节点。
二、单向链表
(一)构造
public class Node<T>{
// 数据域
private T data;
// 指针域:后继
private Node next;
public Node(T data){
this.data = data;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
}
(二)插入
public Node insertNode(Node head, Node nodeInsert, int index) {
// 获取当前链表的长度,此处未实现
int size = getLength(head);
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException();
}
// 首部插入
if (index == 0) {
nodeInsert.next = head;
head = nodeInsert;
return head;
}
// 中部插入
Node tempNode = head;
int count = 0;
if (index > 0 && index < size) {
while (count < index - 1) {
tempNode = tempNode.next;
count++;
}
nodeInsert.next = tempNode.next;
tempNode.next = nodeInsert;
return head;
}
// 尾部插入
if (index == size) {
while (count < index - 1) {
tempNode = tempNode.next;
count++;
}
tempNode.next = nodeInsert;
nodeInsert.next = null;
return head;
}
}
(三)删除
public Node deleteNode(Node head, int index) {
// 获取当前链表的长度,此处未实现
int size = getLength(head);
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException();
}
// 头部删除
if (index == 0) {
head = head.next;
return head;
}
// 中部删除
if (index > 0 || index < size - 1) {
Node tempNode = head;
int count = 0;
while (count < index - 1) {
tempNode = tempNode.next;
count++;
}
tempNode.next = tempNode.next.next;
return head;
}
// 尾部删除
if (index == size - 1) {
Node tempNode = head;
int count = 0;
while (count < index - 1) {
tempNode = tempNode.next;
count++;
}
tempNode.next = null;
return head;
}
}
注意:单向链表插入和删除时,需要提前一个位置进行判断,即找到要插入或者删除节点的前驱节点进行操作。插入时,还需要先将新节点和原节点建立联系,然后再建立头节点或temp节点和新节点的联系,防止找不到原节点。
三、双向链表
(一)构造
public class Node<T> {
private T data;
private Node prev;
private NOde next;
public Node(T data, Node prev, Node next) {
this.data = data;
this.prev = prev;
this.next = next;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public Node getPrev() {
return prev;
}
public void setPrev(Node prev) {
this.prev = prev;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
}
(二)插入
public Node insertNode(Node head, Node tail, Node nodeInsert, int index) {
// 获取当前链表的长度,此处未实现
int size = getLength(head);
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException();
}
// 头部插入
if (index == 0) {
nodeInsert.prev = null;
nodeInsert.next = head;
head.prev = nodeInsert;
head = nodeInsert;
return head;
}
// 中部插入
if (index > 0 || index < size) {
Node tempNode = head;
int count = 0;
while (count < index - 1) {
tempNode = tempNode.next;
count++;
}
// 将新节点与原节点双向连接起来
nodeInsert.next = tempNode.next;
tempNode.next.prev = nodeInsert;
// 将原节点的前驱与新节点双向连接起来
nodeInsert.prev = tempNode;
tempNode.next = NodeInsert;
return head;
}
// 尾部插入
if (index == size) {
tail.next = nodeInsert;
nodeInsert.prev = tail;
nodeInsert.next = null;
return head;
}
}
(三)删除
public Node deleteNode(Node head, Node tail, int index) {
// 获取当前链表的长度,此处未实现
int size = getLength(head);
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException();
}
// 头部删除
if (index == 0) {
head.next.prev = null;
head = head.next;
return head;
}
// 中部删除
if (index > 0 && index < size) {
Node nodeDelete = head;
int count = 0;
while (count < index) {
nodeDelete = nodeDelete.next;
count++;
}
nodeDelete.next.prev = nodeDelete.prev;
nodeDelete.prev.next = nodeDelete.next;
return head;
}
// 尾部删除
if (index == size - 1) {
tail.prev.next = null;
tail = tail.prev;
return head;
}
}
以上内容皆为个人愚见,如有不足之处,还请不吝赐教。谢谢!