从零编写一个单向循环链表(LoopSingle)--- Java实现

当你让单链表的尾指针指向头结点时,一个单向循环链表就诞生了!

循环链表:
在这里插入图片描述
当然,单向循环没这么简单。
首先,若你之前知道的单链表是有虚拟头结点的,那么在单向循环链表中,虚拟头结点是多余的,
其次,由于末尾结点指针域是不再指向null的,在操作时,是要考虑到的,
由于这两个问题,单向循环链表的重点就在于增加结点和删除结点的部分

所以这次我们着重于增加和删除两个方法,整体LoopSingle的实现放在文章末尾

add(int index,E e):

第0步:
判断插入下标是否合法,同时新建结点,给数据域赋值

if (index < 0 || index > size) {
	throw new IllegalArgumentException("插入位置不合法");
}
Node node = new Node();
node.data = e;

增加时,我们有四种情况:
1.第一个结点还未创建,这时head,rear指针都应该指向null。添加结点后,头尾指针都指向新加结点,同时末尾结点的next指向头结点即它自己的next域指向了它自己
在这里插入图片描述

		if (size == 0) {	//当结点元素个数为0,则链表无元素
			head = node;	//使头指针指向新建结点
			rear = node;	//使尾指针指向新建结点
			node.next = node;	//使尾结点的next域指向头结点
		} 

2.在0下标处添加结点,即头插。这时我们先要将新建结点next域指向头结点,之后让尾结点的next域指向新建结点,最后让头指针指向新建结点
在这里插入图片描述

		else if (index == 0) { 	// 头插
			node.next = head;	 // 新建结点的next域指向头结点
			rear.next = node;	//尾结点的next域指向新建结点
			head = node;	//将新建结点设置为头结点
		}

3.在末尾添加结点,即尾插。这时我们先要将新建结点next域指向头结点,然后将尾结点的next域指向新建结点,最后将rear指针指向新建结点。
在这里插入图片描述

		else if (index == size) { 	// 尾插
			node.next = head;	//新建结点的next域指向头结点
			rear.next = node;	//尾结点的next域指向新建结点
			rear = node;	//将新建结点设置为尾结点
		}

4.指定下标插入元素。这时的操作与单链表的随机插入操作一致,就是for循环找到插入位置前一个结点,然后让新建结点next域指向它的next域,最后让它的next域指向新建结点。
在这里插入图片描述

		else {		//一般情况下的操作
			Node temp = head;	//建立临时结点
			for (int i = 0; i < index - 1; i++) {	//找到插入位置前一个结点
				temp = temp.next;
			}
			node.next = temp.next;
			temp.next = node;
		}
		size++;		//最后别忘了给size加一

最后别忘了给size加一!!!最后别忘了给size加一!!!最后别忘了给size加一!!!

remove(int index)

依然时第0步:判断删除下标是否合法

if (index < 0 || index > size - 1) {
	throw new IllegalArgumentException("要删除的下标不合法");
}
E e = null;		//存放将要删除的元素

删除时任由四种情况:
1.删除时只剩一个元素。这时执行删除操作后,我们要让头尾指针指向null

		if (size == 1) {	//此时链表只有一个元素
			e = head.data;	//获取将要删除元素
			head.next = null;	//清空next域
			rear.next = null;	
			head = null;	//将head置空
			rear = null;	//将rear置空
		} 

2.删除头结点。将尾结点的next域指向头结点的next域,头结点next域置空,head指向rear的next域结点
在这里插入图片描述

		else if (index == 0) { 		// 头删
			e = head.data;			//获取删除元素
			rear.next = head.next;		//尾结点的next域指向头结点的next域
			head.next = null;		//头结点的next域置空
			head = rear.next;		//重新设置头结点
		}

3.删除尾结点。遍历链表找到尾结点的前序结点,将前序结点的next域指向头结点,将尾结点的next域置空,使rear指针指向前序结点
在这里插入图片描述

		else if (index == size - 1) { 	// 尾删
			Node temp = head;			//临时结点
			for(int i=0;i<size-2;i++){	//注意!!!想找到尾结点之前的结点,要循环size-2次
				temp = temp.next;
			}
			e = rear.data;		//获取删除数据
			temp.next = head;	//前序指向头结点
			rear.next = null;	//尾指针next域置空
			rear = temp;		//更新尾指针
		}

4.删除任意中间元素。找到删除结点前序结点,将前序结点next指向删除结点next域,将删除结点next域置空
在这里插入图片描述

		else {		//一般情况
			Node temp = head;	//临时结点
			for (int i = 0; i < index - 1; i++) {	//遍历找到删除结点前序结点
				temp = temp.next;
			}
			e = temp.next.data;		//获取删除结点数据
			Node n = temp.next;		//新建临时结点指向删除结点
			temp.next = temp.next.next;	//将前序结点的next域指向删除结点的next域
			n.next = null;	//将删除结点的next域置空
			n = null;		//将临时结点置空
		}
		size--;		//依然不要忘了size--
		return e;	//返回删除元素


其他部分比较简单,这里就不细讲了,直接上代码。

public class LoopSingle<E> implements List<E> {	//实现List接口(自定义,感兴趣可翻看之前文章)

	/**
	 * 结点定义
	 */
	private class Node {
		private E data;
		private Node next;

		public Node() {
			this(null, null);
		}

		public Node(E data, Node node) {
			this.data = data;
			this.next = node;
		}

		@Override
		public String toString() {
			return data.toString();
		}
	}

	private Node head;	//头指针
	private Node rear;	//尾指针
	private int size;	//当前链表元素个数

	public LoopSingle() {
		head = null;
		rear = null;
		size = 0;
	}

	@Override
	public int getSize() {		//获取链表元素个数
		return size;
	}

	@Override
	public boolean isEmpty() {		//判断链表是否为空
		return size == 0;
	}

	@Override
	public void add(int index, E e) {	//按下标添加元素
		if (index < 0 || index > size) {	//下标合法性判断
			throw new IllegalArgumentException("下标越界");
		}
		Node node = new Node();
		node.data = e;
		if (size == 0) {	// 链表为空的情况处理
			head = node;
			rear = node;
			node.next = node;
		} else if (index == 0) {	// 头插
			node.next = head; 
			rear.next = node;
			head = node;
		} else if (index == size) {	 // 尾插
			node.next = rear.next;
			rear.next = node;
			rear = node;
		} else {
			Node temp = head;
			for (int i = 0; i < index - 1; i++) {
				temp = temp.next;
			}
			node.next = temp.next;
			temp.next = node;
		}
		size++;
	}

	@Override
	public void addFirst(E e) {		//在表头添加元素
		add(0, e);
	}

	@Override
	public void addLast(E e) {		//在表尾添加元素
		add(size, e);
	}

	@Override
	public E get(int index) {		//获取指定下标元素
		if (index < 0 || index > size - 1) {
			throw new IllegalArgumentException("下标越界");
		}
		Node node = head;
		if (index == 0) {
			// 什么也不做
		} else if (index == size - 1) {
			node = rear;
		} else {
			for (int i = 0; i < index; i++) {
				node = node.next;
			}
		}
		return node.data;
	}

	@Override
	public E getFirst() {		//获取表头元素
		return get(0);
	}

	@Override
	public E getLast() {		//获取表尾元素
		return get(size - 1);
	}

	@Override
	public void set(int index, E e) {		//修改指定下标元素
		if (index < 0 || index > size - 1) {
			throw new IllegalArgumentException("下标越界");
		}
		Node node = head;
		if (index == 0) {
			// 什么也不做
		} else if (index == size - 1) {
			node = rear;
		} else {
			for (int i = 0; i < index; i++) {
				node = node.next;
			}
		}
		node.data = e;
	}

	@Override
	public boolean contains(E e) {		//判断指定元素是否存在与链表中
		if (isEmpty()) {
			return false;
		}
		Node node = head;
		for (int i = 0; i < size; i++) {
			if (node.data == e) {
				return true;
			}
			node = node.next;
		}
		return false;
	}

	@Override
	public int find(E e) {		//查找指定元素在链表中的下标
		if (!contains(e)) {
			return -1;
		}
		Node node = head;
		for (int i = 0; i < size; i++) {
			if (node.data == e) {
				return i;
			}
			node = node.next;
		}
		return -1;
	}

	@Override
	public E remove(int index) {	//删除指定下标元素
		if (index < 0 || index > size - 1) {
			throw new IllegalArgumentException("下标越界");
		}
		E e = null;
		if (size == 1) {
			e = head.data;
			head.next = null;
			head = null;
			rear = null;
		} else if (index == 0) { // 头删
			e = head.data;
			rear.next = head.next;
			head.next = null;
			head = rear.next;
		} else if (index == size - 1) { // 尾删
			Node temp = head;
			for(int i=0;i<size-2;i++){		//注意!!!想找到尾结点之前的结点,要循环size-2次
				temp = temp.next;
			}
			e = rear.data;
			temp.next = head;
			rear.next = null;
			rear = temp;
		} else {
			Node temp = head;
			for (int i = 0; i < index - 1; i++) {
				temp = temp.next;
			}
			e = temp.next.data;
			Node n = temp.next;
			temp.next = temp.next.next;
			n.next = null;
		}
		size--;
		return e;
	}

	@Override
	public E removeFirst() {	//删除表头元素
		return remove(0);
	}

	@Override
	public E removeLast() {		//删除表尾元素
		return remove(size - 1);
	}

	@Override
	public void removeElement(E e) {	//删除指定元素
		int index = find(e);
		if (index != -1) {
			remove(index);
		} else {
			System.out.println("删除元素不存在");
		}
	}

	@Override
	public void clear() {		//清空循环链表
		head = null;
		rear = null;
		size = 0;
	}

	@Override
	public boolean equals(Object obj) {		//重写的比较方法
		if (obj == null) {
			return false;
		}
		if (obj == this) {
			return true;
		}
		if (obj instanceof LoopSingle) {
			LoopSingle ls = (LoopSingle) obj;
			if (ls.size == getSize()) {
				Node node = head;
				Node n = ls.head;
				for (int i = 0; i < size; i++) {
					if (node.data != n.data) {
						return false;
					}
					node = node.next;
					n = n.next;
				}
				return true;
			}
		}
		return false;
	}

	@Override
	public String toString() {		//重写的toString方法
		StringBuilder sb = new StringBuilder();
		sb.append("LoopSingle " + "size:" + size + "\n");
		if (isEmpty()) {
			sb.append("[]");
		} else {
			Node node = head;
			sb.append("[");
			while (node != rear) {
				sb.append(node.data + ",");
				node = node.next;
			}
			sb.append(node.data + "]");
		}
		return sb.toString();
	}
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值