数据结构之链表(二)——单向循环链表

在我的上篇博客中,详细介绍了关于单链表的实现,链接:https://blog.csdn.net/CDCSPR/article/details/100856471

在这里我要介绍的是单向循环链表的实现及其可能的应用。

单向循环链表

定义:

单向循环链表和普通的单链表区别主要在于尾结点,单链表的尾结点指向null,而单向循环链表是将尾结点的指针指向了头结点,首尾相连,从而构成了循环链表。如下图所示:

实现思路: 

循环链表的操作和线性链表基本一致,即链表中的最后一个节点的指针域指向头结点,整个链表形成一个环。

注意的是:循环链表判空条件:有的说是判断p或p->next是否等于头指针,有的说判断tail是否等于head,有的说判断head是否为空,这其实要根据实际情况来判断。若是不带头结点(这里说带头结点是只一个空的节点做为头结点)循环链表,则可以用判断head是否为空来判断链表是否为空。若是带头结点,则可以根据判断head是否等rear(head==rear)于来判断循环链表是否为空。

 实现代码:

首先先实现链表的存储单元,也就是结点类Node,结点类包括存储数据data和后继指针next两个元素:

private class Node {
		E data;// 数据域
		Node next;// 指针域

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

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

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

链表初始化:

 

    private Node head;//头结点
    private Node rear;//尾结点
    private int size;//链表长度

    public LoopSingle() {
        head=null;
        rear=null;
        size=0;
    }
    public LoopSingle(E[] arr){
        
    }

 

判断链表是否为空:

基于链表的结构特点,头结点是链表的开始位置,所以要判断链表是否为空,只要判断链表的头结点是为空即可

public boolean isEmpty() {
		return size == 0 && head == null && rear == null;
	}

链表的长度大小:原理:链表是存储的单元是结点,所有结点的数量也就是链表的长度。结点中存储着后继结点的引用地址,单向循环链表的终点是尾结点的指针为头结点,完成一个闭环,因此只需循环遍历所有结点,依次沿着后继指针循环,直到指针指向头结点,就可以得到链表的长度。

不过在这里,我们在对链表进行初始化时,链表有size属性,所以:

public int getSize() {
		return size;
	}

添加元素:

单向循环链表添加元素主要分为四种情况

1.空链表插入结点;

2.头结点插入元素,新增结点为头结点;

3.中间情况插入节点;

4.尾结点插入元素,也就是在插入在单链表末尾。

public void add(int index, E e) {
		if (index < 0 || index > size) {
			throw new IllegalArgumentException("插入角标非法");
		}
		Node n = new Node(e, null);
		if (isEmpty()) {//链表为空
			head = n;
			rear = n;
			rear.next = head;
		} else if (index == 0) { // 头插
			n.next = head;
			head = n;
			rear.next = head;
		} else if (index == size) { // 尾插
			n.next = head;
			rear.next = n;
			rear = n;
		} else { // 一般情况
			Node p = head;
			for (int i = 0; i < index - 1; i++) {
				p = p.next;
			}
			n.next = p.next;
			p.next = n;
		}
		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) {
			throw new IllegalArgumentException("查找角标非法");
		}
		if (index == 0) {
			return head.data;
		} else if (index == size - 1) {
			return rear.data;
		} else {
			Node p = head;
			for (int i = 0; i < index; i++) {
				p = p.next;
			}
			return p.data;
		}
	}

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

	@Override
	public E getLast() {//获取尾结点的元素
		return get(size);
	}

 查询链表中某个元素是否存在:

@Override
	public boolean contains(E e) {
		return find(e) != -1;
	}

	@Override
	public int find(E e) {
		if (isEmpty()) {
			return -1;
		}
		Node p = head;
		int index = 0;
		while (p.data != e) {
			p = p.next;
			index++;
			if (p == head) {
				return -1;
			}
		}
		return index;
	}

 删除链表中某个结点:

@Override
	public E remove(int index) {//删除链表中的第index个结点
		if (index < 0 || index >= size) {
			throw new IllegalArgumentException("删除角标非法");
		}
		E res = null;
		if (size == 1) {
			res = head.data;
			head = null;
			rear = null;
		} else if (index == 0) {
			res = head.data;
			head = head.next;
			rear.next = head;
		} else if (index == size - 1) {
			Node p = head;
			while (p.next != rear) {
				p = p.next;
			}
			p.next = rear.next;
			rear = p;
		} else {
			Node p = head;
			for (int i = 0; i < index - 1; i++) {
				p = p.next;
			}
			Node del = p.next;
			res = del.data;
			p.next = del.next;
		}
		size--;
		return res;
	}

	@Override
	public E removeFirst() {//删除链表的头结点
		return remove(0);
	}

	@Override
	public E removeLast() {//删除链表的尾结点
		return remove(size - 1);
	}

	@Override
	public void removeElement(E e) {//删除链表中元素值为e的结点
		remove(find(e));
	}

  清空整个链表:

@Override
	public void clear() {
		head = null;
		rear = null;
		size = 0;
	}

遍历整个链表:

@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("LoopQueue:size=" + getSize() + "\n");
		if (isEmpty()) {//判空
			sb.append("[]");
		} else {
			sb.append("[");
			Node p = head;
			while (true) {
				sb.append(p.data);
				if (p.next == head) {
					sb.append(']');
					break;
				} else {
					sb.append(',');
				}
				p = p.next;
			}
		}
		return sb.toString();
	}

总结:我认为单向循环链表是单链表的基本类似,单向循环链表与普通链表的区别主要是在尾结点的判断。并且其结构特点链表中最后一个结点的指针域不再是结束标记,而是指向整个链表的第一个结点,从而使链表形成一个环。带头结点的单向循环链表实现插入和删除操作较为方便。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值