数据结构与算法-循环链表详解

单项循环链表

图解

在这里插入图片描述

实现

步骤分析

由于之前已经写过了单链表单向链表,而单向循环链多数操作和单链表操作一致,所以我们只需基于之前的代码进行修改添加结点和删除结点的操作即可。

代码范例

添加操作
有元素的情况下,向0索引位置插入元素

如下图所示:

  1. 新结点的next指向0索引位置的元素
  2. 尾元素的next指针指向新结点
  3. first指针指向新结点

在这里插入图片描述

链表没有元素的情况下向0索引位置插入元素

如下图所示,操作步骤为:

1. 新结点的next指向自己
2. first的next指针指向新结点

在这里插入图片描述

常规插入(包括插入链表尾部)

如下图所示,操作步骤为:

		1. 新结点next指向指定索引位置的原元素
		2. 原元素的前驱结点指向新结点

在这里插入图片描述

代码实现
  @Override
    public void add(int index, E element) {
        rangeCheckForAdd(index);
        if (index == 0) {
            Node<E> newNode = new Node<E>(element, first);
            Node<E> lastNode = (size == 0) ? newNode : findNode(index - 1);
            lastNode.next = newNode;
            first = newNode;

        } else {
            Node<E> preNode = findNode(index - 1);
            Node<E> newNode = new Node<E>(element, preNode.next);
            preNode.next = newNode;
        }
        size++;
    }
删除操作
过程图解
链表元素大于1的情况下删除0索引位置元素

如下图所示,操作步骤为:

 1. 尾结点指向首结点的下一节点
 2. first指向首结点的下一节点

在这里插入图片描述

链表元素只有1的情况下删除0索引元素

如下图所示,将first指针置空即可

在这里插入图片描述

常规删除

如下图所示,将待删结点的前驱结点指向待删结点的后继节点即可
在这里插入图片描述

代码实现
 @Override
    public E remove(int index) {
        rangeCheck(index);

        Node<E> delNode = first;
        if (index == 0) {
            if (size == 1) {
                first = null;
            } else {
                Node<E> lastNode = findNode(size - 1);
                lastNode.next = first.next;
                first = first.next;
            }
        } else {
            Node<E> prevNode = findNode(index - 1);
            delNode = prevNode.next;
            prevNode.next = delNode.next;
        }
        size--;
        return delNode.element;
    }

完整代码

package com.study.Circle;

import com.study.singlelinkDemo.AbstractList;

/**
 * Created by Zsy on 2020/8/4.
 */
public class SingleCirecleLinkList<E> extends AbstractList<E> {
    private Node<E> first;

    private static class Node<E> {
        E element;
        Node<E> next;

        public Node(E element, Node<E> next) {
            this.element = element;
            this.next = next;
        }

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append(element).append("_").append(next.element);
            return sb.toString();
        }
    }


    @Override
    public void clear() {
        this.first = null;
        size = 0;
    }

    @Override
    public E get(int index) {

        return findNode(index).element;
    }

    public Node<E> findNode(int index) {
        rangeCheck(index);
        Node<E> node = first;
        for (int i = 0; i < index; i++) {
            node = node.next;
        }
        return node;
    }

    @Override
    public E set(int index, E element) {
        rangeCheck(index);
        Node<E> node = findNode(index);
        E oldNode = node.element;
        node.element = element;


        return oldNode;
    }

    @Override
    public void add(int index, E element) {
        rangeCheckForAdd(index);
        if (index == 0) {
            Node<E> newNode = new Node<E>(element, first);
            Node<E> lastNode = (size == 0) ? newNode : findNode(index - 1);
            lastNode.next = newNode;
            first = newNode;

        } else {
            Node<E> preNode = findNode(index - 1);
            Node<E> newNode = new Node<E>(element, preNode.next);
            preNode.next = newNode;
        }
        size++;
    }

    @Override
    public E remove(int index) {
        rangeCheck(index);

        Node<E> delNode = first;
        if (index == 0) {
            if (size == 1) {
                first = null;
            } else {
                Node<E> lastNode = findNode(size - 1);
                lastNode.next = first.next;
                first = first.next;
            }
        } else {
            Node<E> prevNode = findNode(index - 1);
            delNode = prevNode.next;
            prevNode.next = delNode.next;
        }
        size--;
        return delNode.element;
    }

    @Override
    public int indexOf(E element) {

        Node<E> node = first;
        if (element == null) {
            for (int i = 0; i < size; i++) {
                if (node == null) return i;
                node = node.next;
            }
        } else {
            for (int i = 0; i < size; i++) {
                if (element.equals(node.element))//防止空指针异常
                    return i;
                node = node.next;
            }
        }

        return ELEMENT_NOT_FOUND;
    }


    @Override
    public String toString() {
        Node<E> node = this.first;
        String statrStr = String.format("size: %d [", size);
        StringBuilder result = new StringBuilder();
        result.append(statrStr);
        for (int i = 0; i < size; i++) {
            if (i != 0) result.append(",");
            result.append(node.element);
            node = node.next;

        }
        result.append("]");
        return result.toString();
    }

}

双向循环链表

图解

在这里插入图片描述

实现

添加操作

常规添加
0索引位置添加

如下图所示,操作步骤为:

1.  新结点指向首结点所指向的元素
2. 尾结点的next指针指向新结点
3. first指针指向新结点

在这里插入图片描述

常规添加

如下图所示,操作步骤为:

1. 新结点指向指定索引的原结点及其前驱结点
2. 原结点的prev指针指向新结点
3. 原结点的前驱结点的next指针指向新结点

在这里插入图片描述

尾部添加

与首部添加同理,只是将first指向新结点改为last指向新结点
因为是否是尾部添加的判定条件为:index == size尾部添加还需要考虑一个情况是当前链表为空的情况。具体情况如下图所示,操作步骤为:

1. first和last都指向新结点
2. 新结点两个指针都指向自己

在这里插入图片描述

实现代码
  @Override
    public void add(int index, E element) {
        rangeCheckForAdd(index);


        if (index == size) {
            Node<E> oldLastNode = last;
            last = new Node<E>(oldLastNode, element, first);
            if (oldLastNode == null) {
                first = last;
                first.next = first;
                first.prev = first;
            } else {
                oldLastNode.next = last;
                first.prev = last;
            }
        } else {
            Node<E> node = findNode(index);
            Node<E> prev = node.prev;
            Node<E> newNode = new Node<E>(prev, element, node);
            prev.next = newNode;
            node.prev = newNode;

            if (index == 0) {
                first = newNode;
            }
        }
        size++;
    }

删除操作

删除操作的情况
1. 尾部删除:常规删除操作后,修改last指针指向即可
2. 首部删除:常规删除操作后,修改first指针指向即可
3. 常规删除
4. 只剩下一个元素的删除:first、last指针都置空
实现代码
 @Override
    public E remove(int index) {
        rangeCheck(index);
        Node<E> delNode = findNode(index);
        E removeNode = remove(delNode);
        return removeNode;
    }

    private E remove(Node<E> node) {
        if (size == 1) {
            first = null;
            last = null;
        } else {
            Node<E> prev = node.prev;
            Node<E> next = node.next;
            prev.next = next;
            next.prev = prev;

            if (node == first) {
                first = next;
            }

            if (node == last) {
                last = node;
            }
        }

        size--;
        return node.element;
    }

练习案例

约瑟夫问题

如下图所示,用双向链表实现,每次指针挪动三步,并移除该元素的操作
在这里插入图片描述

实现步骤

1. 加入current指针
2. 添加删除方法
3. 添加初始化current方法
4. 添加重置current指针方法

实现核心代码块

  private Node<E> current;


    public void reset() {
        current = first;
    }

    public E CurrentMoveToNext() {
        if (CurrentIsNullPoint()) return null;

        current = current.next;
        System.out.println(" move to"+current.element);
        return current.element;
    }

    public boolean CurrentIsNullPoint() {
        return current == null;
    }

    public E removeCurrentNode() {
        if (CurrentIsNullPoint()) return null;

        Node<E> currentNext = current.next;
        E removeValue = remove(current);

        if (size == 0)
            current=null;
        else
            current =currentNext;

        return removeValue;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shark-chili

您的鼓励将是我创作的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值