链表简介;
链表是数据结构中较为常见且简单的一种线性表链式结构; 由链表存储的对象在内存中通常是分散的;链表中的每一个元素称为节点, 每一个节点都有一个直接后继, 如果双链表的话还有一个直接前驱, 首节点没有直接前驱, 但是在循环链表中可以将尾节点理解为直接前驱; 尾节点没有直接后继;
链表和数组对比:
链表对首尾元素的增删改查效率极高(O(1)); 而数组对数组中每个元素的删除插入总是会伴随着整体后移或前移, 效率比较低,其删改效率为O(n);
数组相较于链表优势是按索引进行取值的速度极快(O(1)), 而链表需要一层一层的寻值;
数组结构中往往需要占用内存的的成片空间, 内存利用率不高, 而链表元素都是分散的,可以有效利用空闲位置;
链表的基本分类:
1. 单链表 2. 双链表 3. 循环链表
各种链表在Java中的实现:
单链表在Java中的实现:
在Java中LinkedList类的底层实现是使用了双向链表, 本单链表的实现主要是参照了LinkedList类的方法命名;并实现了链表的增删改查的操作;
程序如下:
基础节点类: 定义了节点存储值属性----value/ next 所代表的是该节点的直接后继
public class LinkNode<T> {
// 值
private T value;
// 指针
private LinkNode<T> next;
public LinkNode(T value) {
this.value = value;
}
public LinkNode() {}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
public LinkNode<T> getNext() {
return next;
}
public void setNext(LinkNode<T> next) {
this.next = next;
}
}
这是具体的方法实现,方法没有经过大量的测试, 但是基本功能应该是没有大问题的; 如果发现bug请告知;
package learn.linklist;
import lombok.Data;
import java.util.NoSuchElementException;
/**
* @Author:张
* @Version:1.0
* @Date:2024/6/11-10:35
* @Since:jdk1.8
* @Description:
*/
@Data
public class MyLinkList<T> {
// 头节点
private LinkNode<T> head;
// 尾节点
private LinkNode<T> end;
// 头节点标记
private boolean isHead = true;
// 节点个数
private int size;
// 插入
// 首插入
public void addFirst(T element) {
// 如果首节点为null
if(head == null) {
head = new LinkNode<>();
head.setValue(element);
size ++;
return;
}
// 1. 首先new一个节点
LinkNode<T> tLinkNode = new LinkNode<>(element);
// 2. 把这个节点的next属性设置为首节点
tLinkNode.setNext(head);
// 3. 将此节点变成首节点
head = tLinkNode;
// 节点个数增加
size++;
}
// 尾插入
public void addLast(T element) {
if(end == null) {
// 插入头节点
addFirst(element);
end = head;
return;
}
// 1. 先初始化一个新节点
LinkNode<T> tLinkNode = new LinkNode<>();
tLinkNode.setValue(element);
// 2. 将end的next指向这个新节点
end.setNext(tLinkNode);
// 3. 将end更新为新节点
end = tLinkNode;
size ++;
}
// 中间插入
public void add(int index, T element) {
// 异常判断
if(index > size || index < 0) {
throw new ArrayIndexOutOfBoundsException();
}
// 首节点之前
if(index == 0){
addFirst(element);
return;
}
//尾节点
if(index == size) {
addLast(element);
return;
}
// 其他位置
LinkNode<T> c = head;
LinkNode<T> b = null;
int i = 0;
while (i <= size) {
if(i > 0 && i == index) {
LinkNode<T> newNode = new LinkNode<>(element);
newNode.setValue(element);
newNode.setNext(c);
b.setNext(newNode);
size++;
return;
}
i++;
b = c;
c = c.getNext();
}
}
// 普通插入
public void add(T element) {
addLast(element);
}
// 删除
/**
* 无参删除head
*/
public void remove(){
// 没有参数的删除就是移除首节点
head = head.getNext();
size --;
}
/**
* 删除第一个匹配的节点
* @param element
*/
public void remove(T element) {
LinkNode<T> c = head;
T targetValue = c.getValue();
LinkNode<T> b = null;
int i = 0;
while (i <= size) {
if(targetValue != null && targetValue.equals(element)) {
if(i == size - 1) {
end = b;
b.setNext(null);
}else {
if(i == 0) {
head = head.getNext();
}else {
b.setNext(c.getNext());
}
}
size--;
return;
}
i++;
b = c;
c = c.getNext();
targetValue = c.getValue();
}
}
/**
* 删除index位置的节点
* @param index
*/
public void remove(int index) {
// 异常判断
if(index > size || index < 0) {
throw new ArrayIndexOutOfBoundsException();
}
if(index == 0) {
remove();
return;
}
// 其他位置
LinkNode<T> c = head;
LinkNode<T> b = null;
int i = 0;
while (i <= size) {
if(i > 0 && i == index) {
if(index == size - 1) {
end = b;
b.setNext(null);
}else {
b.setNext(c.getNext());
}
size--;
return;
}
i++;
b = c;
c = c.getNext();
}
}
// 查找
public T get(int index) {
// 异常判断
if(index > size || index < 0) {
throw new ArrayIndexOutOfBoundsException();
}
if(index == 0) {
return getFirst();
}
if(index == size - 1) {
return getLast();
}
int i = 1;
LinkNode<T> currentNode = head.getNext();
T value = currentNode.getValue();
while (i < size) {
if(index == i) {
return value;
}
currentNode = currentNode.getNext();
value = currentNode.getValue();
i++;
}
return null;
}
public T getFirst() {
if(head == null) {
throw new NoSuchElementException();
}
return head.getValue();
}
public T getLast() {
if(end == null) {
throw new NoSuchElementException();
}
return end.getValue();
}
// 更新
public void set(int index, T element){
// 异常判断
if(index > size || index < 0) {
throw new ArrayIndexOutOfBoundsException();
}
if(index == 0) {
head.setValue(element);
return;
}
if(index == size - 1) {
end.setValue(element);
return;
}
LinkNode<T> currentNode = head.getNext();
int i = 1;
while (i < size) {
if (i == index) {
currentNode.setValue(element);
return;
}
i++;
currentNode = currentNode.getNext();
}
}
// 遍历打印链表
public void show() {
// 先打印头结点信息
System.out.print("[ ");
System.out.print(head.getValue() + " ");
LinkNode<T> next = head.getNext();
while (true) {
if (next != null) {
System.out.print(next.getValue() + " ");
}else {
break;
}
next = next.getNext();
}
System.out.println("]");
}
}
双链表在Java中的实现:
因为双链表和单链表实现起来区别并不是很大,所以只做了简单的实现;
甚至双链表在前继后驱的指针改变上非常的方便;
public class LinkNode<T> {
private LinkNode<T> preNode;
private LinkNode<T> nextNode;
private T value;
public LinkNode(T value) {
this.value = value;
}
public LinkNode() {}
public LinkNode<T> getPreNode() {
return preNode;
}
public void setPreNode(LinkNode<T> preNode) {
this.preNode = preNode;
}
public LinkNode<T> getnextNode() {
return nextNode;
}
public void setnextNode(LinkNode<T> nextNode) {
this.nextNode = nextNode;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
public class MyLinkedList<T> {
@Setter
@Getter
private LinkNode<T> head;
@Setter
@Getter
private LinkNode<T> tail;
private int size;
/**
* 将元素添加的首部
*
* @param value
*/
public void addFirst(T value) {
LinkNode<T> newNode = new LinkNode<>(value);
if (size == 0) { // 容器中没有一个元素
head = newNode;
// 因为只有一个元素,所以尾节点也是它
tail = newNode;
} else {
// 首节点的前驱变成该节点
head.setPreNode(newNode);
// 该节点的后继变成首节点
newNode.setnextNode(head);
// 首节点易主
head = newNode;
}
// 元素个数增加
size++;
}
/**
* 将新元素添加到容器尾部
*/
public void addLast(T value) {
if (size == 0) {
addFirst(value);
} else {
LinkNode<T> newNode = new LinkNode<>(value);
// 尾节点的后继变成该节点
tail.setnextNode(newNode);
// 新节点的前驱变成尾节点
newNode.setPreNode(tail);
// 新节点变成尾节点
tail = newNode;
size++;
}
}
/**
* 在index处插入节点
*
* @param index
* @param value
*/
public void add(int index, T value) {
// 判断节点的合理性
if (index < 0 || index > size) {
throw new ArrayIndexOutOfBoundsException();
}
// 如果index为0
if (index == 0) {
addFirst(value);
return;
}
// 如果index为size
if (index == size) {
addLast(value);
return;
}
int i = 1;
LinkNode<T> currentNode = head.getnextNode();
LinkNode<T> newNode = new LinkNode<>(value);
while (true) {
if (i == index) {
LinkNode<T> preNode = currentNode.getPreNode();
preNode.setnextNode(newNode);
newNode.setPreNode(preNode);
newNode.setnextNode(currentNode);
currentNode.setPreNode(newNode);
size++;
return;
}
currentNode = currentNode.getnextNode();
i++;
}
}
/**
* 插入一个节点
* @param value
*/
public void add(T value) {
addLast(value);
}
/**
* 在index处批量插入一个list
*
* @param index
* @param newList
*/
public void addAll(int index, MyLinkedList<T> newList) {
// 判断节点的合理性
if (index < 0 || index > size) {
throw new ArrayIndexOutOfBoundsException();
}
// index为零 首部直接插入
if (index == 0) {
LinkNode<T> newListTail = newList.getTail();
newListTail.setnextNode(head);
head.setPreNode(newListTail);
head = newListTail;
size += newList.size();
return;
}
// 如果index为size
if (index == size) {
LinkNode<T> newListHead = newList.getHead();
newListHead.setPreNode(tail);
tail.setnextNode(newListHead);
tail = newListHead;
size += newList.size();
return;
}
int i = 1;
LinkNode<T> currentNode = head.getnextNode();
while (true) {
if(index == i) {
LinkNode<T> nextNode = currentNode.getnextNode();
currentNode.setnextNode(newList.getHead());
newList.getHead().setPreNode(currentNode);
newList.getTail().setnextNode(nextNode);
nextNode.setPreNode(newList.getTail());
size += newList.size();
}
currentNode = currentNode.getnextNode();
i++;
}
}
/**
* 返回容器的长度
*
* @return
*/
public int size() {
return size;
}
public void show() {
int i = 0;
LinkNode<T> currentNode = head;
System.out.print("[ " + currentNode.getValue());
while (i < size - 1) {
i++;
currentNode = currentNode.getnextNode();
System.out.print("," + currentNode.getValue());
}
System.out.println(" ]");
}
}
至于循环链表,我觉得大差不差,首尾相接即可;