学习单向链表带哨兵demo

一、定义

在计算机科学中,链表是数据元素的线性集合,其每个元素都指向下一个元素,元素存储上并不连续。

1.可以分三类为

  • 单向链表,每个元素只知道其下一个元素是谁

  • 双向链表,每个元素知道其上一个元素和下一个元素

  • 循环链表,通常的链表尾节点tail指向的都是null,而循环链表的tail指向的是头节点head

链表内还有一种特殊的节点称为哨兵节点,也叫做哑元(Dummy)节点,它不存储数据,通常用作头尾,用来简化边界判断,如下图所示:

2.性能

随机访问

根据index查找,时间复杂度O(n)

插入或删除

起始位置:O(1)

结束位置:如果已知tail尾节点是O(1),不知道tail尾节点是O(n)

中间位置:根据index查找时间 + O(1)

二、单向链表的插入、删除、查找示例

1.以下为不带哨兵节点代码

package com.tfq.arithmetic.linkedlist;

import java.util.Iterator;
import java.util.function.Consumer;

/**
 * @author: fqtang
 * @date: 2024/05/18/19:23
 * @description: 单向链表类
 */
public class SingleLinkedList implements Iterable<Integer> {
	/**
	 * 头指针
	 */
	private Node head = null;

	public void addFirst(int value) {
		//1.链表为空
//		head = new Node(value,null);
		//2.链表非空
		head = new Node(value, head);
	}

	/**
	 * 返回链表第一种方式
	 *
	 * @return
	 */
	public void loop(Consumer<Integer> consumer) {
		Node p = head;
		while(p != null) {
			consumer.accept(p.value);
			p = p.next;
		}
	}

	/**
	 * 返回链表第二种方式
	 *
	 * @return
	 */
	public void loop2(Consumer<Integer> consumer) {
		for(Node p = head; p != null; p = p.next) {
			consumer.accept(p.value);
		}
	}


   /**
	 * 返回链表第三种方式
	 *
	 * @return
	 */
	public void loop3() {
		recursion(head);
	}

	/**
	 * 递归链表
	 *
	 * @return
	 */
	public void recursion(Node curr) {
		if(curr == null) {
			return;
		}
		System.out.println(curr.value);
		recursion(curr.next);
	}


	/**
	 * 返回链表第四种方式
	 *
	 * @return
	 */
	@Override
	public Iterator<Integer> iterator() {
		//匿名内部类 ->带名字内部类
		return new NodeIterator();
	}

	private Node findLast() {
		if(head == null) {//空链表
			return null;
		}

		Node p = head;
		while(p.next != null) {
			p = p.next;
		}
		return p;
	}

	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 = 0;
		for(Node p = head; p != null; p = p.next, i++) {
			if(index == i) {
				return p;
			}
		}
		return null;//没找到
	}

	public int getValue(int index) {
		Node node = findNode(index);
		if(node == null) {
			//抛异常
			throw new IllegalArgumentException(String.format("index [%d] 不合法%n", index));
		}
		return node.value;
	}

	/**
	 * 向索引位置插值
	 *
	 * @param index 索引
	 * @param value 待插入值
	 */
	public void insert(int index, int value) {
		if(index == 0) {//若index=0,则向链表的head添加元素节点
			addFirst(value);
			return;
		}
		//找索引的上一个节点
		Node prev = findNode(index - 1);
		if(prev == null) {//上一个节点没找到
			throw new IllegalArgumentException(String.format("index [%d] 不合法%n", index));
		}
		prev.next = new Node(value, prev.next);
	}

	/**
	 * 删除第一个节点
	 */
	public void removeFirst() {
		if(head == null) {
			throw new IllegalArgumentException(String.format("index [%d] 不合法%n", 0));
		}
		head = head.next;
	}

	/**
	 * 根据索引删除指定元素
	 * @param index 待删除索引
	 */
	public void remove(int index) {
		if(index ==0){
			removeFirst();
			return;
		}
		//找到上一个节点
		Node prev = findNode(index - 1);
		if(prev == null){
			throw new IllegalArgumentException(String.format("index-1 [%d] 不合法%n", index));
		}
		//被删除的节点
		Node removed = prev.next;
		if(removed == null){
			throw new IllegalArgumentException(String.format("index [%d] 不合法%n", index));
		}
		//上一个节点指向被删除节点的下一个节点
		prev.next = removed.next;
	}

	/**
	 * 节点类,单向链表与节点是外部类与内部类,更好隐藏内部类,
	 * 方便外部调用,不需要外部知道内部实现细节
	 * 内部类使用了static,则此内部类没有使用外部类的变量。当内部类没有使用外部类的变量时推荐使用static
	 */
	private static class Node {
		/**
		 * 值
		 */
		int value;
		/**
		 * 下一个结点指针
		 */
		Node next;

		public Node(int value, Node next) {
			this.value = value;
			this.next = next;
		}

	}

	/**
	 * 内部类使用了一个外部类的成员变量时在内部类的类名上不能使用static,内部类使用了一个Node
	 */
	private class NodeIterator implements Iterator<Integer> {
		Node p = head;

		@Override
		public boolean hasNext() {//是否有下一个元素
			return p != null;
		}

		@Override
		public Integer next() {//返回当前元素值,并指向下一个元素
			int v = p.value;
			p = p.next;
			return v;
		}
	}
}

2.以下为代哨兵节点

哨兵用来简化边界判断(简化查找上一个节点和非空判断)简化代码。

package com.tfq.arithmetic.linkedlist;

import java.util.Iterator;
import java.util.function.Consumer;

/**
 * @author: fqtang
 * @date: 2024/05/18/19:23
 * @description: 单向链表类(带哨兵监视)
 */
public class SingleLinkedListSentinel implements Iterable<Integer> {
	/**
	 * 头指针指向哨兵节点
	 */
	private Node head = new Node(666, null);

	public void addFirst(int value) {
		insert(0,value);
	}

	/**
	 * 返回链表第一种方式
	 *
	 * @return
	 */
	public void loop(Consumer<Integer> consumer) {
		Node p = head.next;
		while(p != null) {
			consumer.accept(p.value);
			p = p.next;
		}
	}

	/**
	 * 返回链表第二种方式
	 *
	 * @return
	 */
	public void loop2(Consumer<Integer> consumer) {
		for(Node p = head.next; p != null; p = p.next) {
			consumer.accept(p.value);
		}
	}

	/**
	 * 返回链表第三种方式
	 *
	 * @return
	 */
	@Override
	public Iterator<Integer> iterator() {
		//匿名内部类 ->带名字内部类
		return new NodeIterator();
	}

	private Node findLast() {
		Node p = head;
		while(p.next != null) {
			p = p.next;
		}
		return p;
	}

	public void addLast(int value) {
		Node last = findLast();
		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(index == i) {
				return p;
			}
		}
		return null;//没找到
	}

	public int getValue(int index) {
		Node node = findNode(index);
		if(node == null) {
			//抛异常
			throw new IllegalArgumentException(String.format("index [%d] 不合法%n", index));
		}
		return node.value;
	}

	public void test() {
		int i = 0;
		for(Node p = head; p != null; p = p.next, i++) {
			System.out.println(p.value + ",索引值:" + i);
		}
	}

	/**
	 * 向索引位置插值
	 *
	 * @param index 索引
	 * @param value 待插入值
	 */
	public void insert(int index, int value) {
		//找索引的上一个节点
		Node prev = findNode(index - 1);
		if(prev == null) {//上一个节点没找到
			throw new IllegalArgumentException(String.format("index [%d] 不合法%n", index));
		}
		prev.next = new Node(value, prev.next);
	}

	/**
	 * 删除第一个节点
	 */
	public void removeFirst() {
		remove(0);
	}

	/**
	 * 根据索引删除指定元素
	 *
	 * @param index 待删除索引
	 */
	public void remove(int index) {
		//找到上一个节点
		Node prev = findNode(index - 1);
		if(prev == null) {
			throw new IllegalArgumentException(String.format("index-1 [%d] 不合法%n", index));
		}
		//被删除的节点
		Node removed = prev.next;
		if(removed == null) {
			throw new IllegalArgumentException(String.format("index [%d] 不合法%n", index));
		}
		//上一个节点指向被删除节点的下一个节点
		prev.next = removed.next;
	}

	/**
	 * 节点类,单向链表与节点是外部类与内部类,更好隐藏内部类,
	 * 方便外部调用,不需要外部知道内部实现细节
	 * 内部类使用了static,则此内部类没有使用外部类的变量。当内部类没有使用外部类的变量时推荐使用static
	 */
	private static class Node {
		/**
		 * 值
		 */
		int value;
		/**
		 * 下一个结点指针
		 */
		Node next;

		public Node(int value, Node next) {
			this.value = value;
			this.next = next;
		}

	}

	/**
	 * 内部类使用了一个外部类的成员变量时在内部类的类名上不能使用static,内部类使用了一个Node
	 */
	private class NodeIterator implements 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;
		}
	}
}

若要学习双向链表,请查看我下一章节。

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值