单向链表是一种线性表数据结构,其中每个节点包含两个部分:数据域和指向下一个节点的指针。
单向链表的节点类型包括:
1、数据域:存储节点的数据,通常包括节点的值和指向下一个节点的指针。
2、指针域:存储指向下一个节点的指针,通常包括当前节点的地址和指向下一个节点的指针。
单向链表的基本操作包括:
1、插入节点:在链表的头部插入一个新节点,该节点的数据域设置为新节点的数据,指针域指向新节点的下一个节点。
2、删除节点:从链表的头部删除一个节点,该节点的数据域设置为删除节点的数据,指针域指向删除节点的前一个节点。
3、获取节点:获取链表中指定位置的节点,该节点的数据域设置为指定位置的节点的数据,指针域指向该节点的下一个节点。
4、遍历链表:按照指定的顺序访问链表中的所有节点,从头部开始逐个访问,直到到达尾部。
5、查找节点:在链表中查找指定值的节点,返回该节点的数据域或指针域,如果不存在则返回NULL。
倒序遍历链表:按照倒序访问链表中的所有节点,从尾部开始逐个访问,直到到达头部。
6、反转链表:将链表中的节点顺序翻转,可以通过修改指针域来实现,也可以直接修改数据域来实现。
单向链表相比于数组链表具有更好的灵活性和可扩展性,因为它可以动态地分配和释放内存,并且可以实现快速的插入和删除操作。然而,单向链表也有一些缺点,例如需要额外的空间来存储指针域,导致插入和删除操作的时间复杂度较高。
上代码:
package com.demo._03单向链表;
import java.util.Iterator;
import java.util.function.Consumer;
/**
* @create 2023/5/4 16:20
* @Desc 单向链表(带哨兵)
* 遍历逻辑,head 改成 head.next
**/
public class SinglyLinkedListSentinel implements Iterable<Integer>{
// 头指针
private Node head = new Node(000, null);
@Override
public Iterator<Integer> iterator() {
return new Iterator<Integer>() {
Node p = head.next ;
// 是否存在下一个元素
@Override
public boolean hasNext() {
return p != null;
}
// 返回当前元素的值,并且指向下一个元素
@Override
public Integer next() {
int v = p.value;
p = p.next ;
return v;
}
};
}
/**
* 节点类
*/
private static class Node{
// 值
int value ;
// 下一个节点指针
Node next ;
public Node(int value, Node next) {
this.value = value;
this.next = next;
}
}
/**
* 向链表头部添加
* @param value
*/
public void addFirst(int value) {
insert(0, value);
}
/**
* 遍历链表1
* @param consumer
*/
public void loop(Consumer<Integer> consumer) {
Node p = head ;
while (p != null){
// 处理完当前节点,获取当前节点的下一个节点
consumer.accept(p.value);
p = p.next ;
}
}
/**
* 遍历链表2
* @param consumer
*/
public void loop2(Consumer<Integer> consumer) {
for (Node p = head ; p != null; p = p.next ){
// 处理完当前节点,获取当前节点的下一个节点
consumer.accept(p.value);
}
}
/**
* 查找最后一个节点
* @return
*/
private Node findLast() {
Node p ;
for (p = head ; p.next != null; p = p.next ){
}
return p ;
}
/**
* 添加尾元素
* @param value
*/
public void addLast(int value) {
Node last = findLast();
// 加入哨兵,不存在为空的情况
// if(last == null) {
// addFirst(value);
// return;
// }
last.next = new Node(value, null) ;
}
private Node findNode(int index) {
int i = -1;
for (Node p = head; p != null ; p = p.next, i++) {
if(i == index) {
return p ;
}
}
return null ;
}
/**
* 根据索引获取元素
* @param index
* @return
*/
public int get(int index) {
Node node = findNode(index);
if(node == null) {
throw illegalArgumentException(index);
}
return node.value ;
}
/**
* 向索引位置插入
* @param index 索引位置
* @param value 插入值
*/
public void insert(int index, int value) {
// 添加哨兵了,去掉
// if(index == 0) {
// addFirst(value);
// return;
// }
// 找到上一个节点
Node prev = findNode(index - 1);
if(prev == null) {
throw illegalArgumentException(index);
}
prev.next = new Node(value, prev.next);
}
/**
* 异常
* @param index
* @return
*/
private IllegalArgumentException illegalArgumentException(int index) {
return new IllegalArgumentException(String.format("index [%d] 不合法%n", index));
}
/**
* 删除第一个节点
*/
public void removeFirst() {
remove(0);
}
/**
* 删除指定索引元素
* @param index 索引
*/
public void remove(int index){
// 添加哨兵,去掉判断逻辑
/*if(index == 0) {
removeFirst();
return;
}*/
// 上一个节点
Node prev = findNode(index - 1) ;
if(prev == null) throw illegalArgumentException(index) ;
// 被删除的节点
Node removed = prev.next ;
if(removed == null) throw illegalArgumentException(index) ;
prev.next = removed.next ;
}
public static void main(String[] args) {
SinglyLinkedListSentinel list = new SinglyLinkedListSentinel();
list.addLast(1);
list.addLast(2);
list.addLast(3);
list.addLast(4);
list.insert(4, 5);
// list.loop(value -> System.out.println(value));
// System.out.println("----------------------");
// list.loop2(value -> System.out.println(value));
// System.out.println("----------------------");
list.addFirst(5);
list.addFirst(6);
list.addFirst(7);
list.addFirst(8);
// System.out.println(list.get(0));
// list.removeFirst();
// list.remove(3);
for (Integer integer : list) {
System.out.println(integer);
}
}
}