• 循环链表


    循环链表

    单向循环链表

    单向循环链表的结构

    尾结点的next,指向头结点

    当只有一个结点

    接口设计

    相较于单向链表,单向循环链表需要重写插入结点删除结点两个方法

    • 插入节点,需要特别关注插入头节点的情况。此时需要拿到尾节点,然后将其next指向新节点。

    public class SingleCircleLinkedList<E> extends AbstractList<E> {
        ......
        
        /**
         * 往index位置添加元素 --- 改造
         * @param index
         * @param element
         */
        public void add(int index, E element){
            //边界判断
            rangeCheckForAdd(index);
            if (index == 0){// 给空链表添加第一个元素的情况
                //创建新节点
                Node<E> newFirst = new Node<>(element,first);
                //拿到最后一个节点
                Node<E> last = (size==0) ? newFirst : node(size - 1 );
                //尾指向头
                last.next = newFirst;
                //头指向
                first = newFirst;
            }else {
                //index上一个节点
                Node<E> prev = node(index - 1);
                //创建新节点,prev节点指向这个新节点
                prev.next = new Node<>(element, prev.next);
            }
            size++;
        }
            
        ......
        
    }
    
    • 如果删除的是头节点,删除后需让last指向新的头节点
    • 当只有一个节点并删除时, first指向null
    public class SingleCircleLinkedList<E> extends AbstractList<E> {
        ......
        
        /**
         * 删除index位置对应的元素 --- 改造
         * @param index
         * @return
         */
        public E remove(int index){
            //边界判断
            rangeCheck(index);
            //记录节点
            Node<E> node = first;
            if (index == 0){ // 删除第一个元素是特殊情况
                // 只有一个节点
                if (size == 1){
                    first = null;
                }else {
                    //拿到最后一个节点
                    Node<E> last = node(size - 1);
                    first = first.next;
                    last.next = first;
                }
            }else {
                //index上一个节点
                Node<E> prev = node(index - 1);
                //记录删除的节点
                node = prev.next;
                //node节点下一个节点,prev节点指向这个节点
                prev.next = node.next;
            }
            size --;
            return node.element;
        }
            
        ......
        
    }
    
    完整代码
    /**
     * 单向循环链表的实现
     * @param <E> 泛型
     */
    public class SingleCircleLinkedList<E> extends AbstractList<E> {
        /**
         * 元素所在的结点
         */
        private Node<E> first;
    
        /**
         * 内部类 结点
         * @param <E>
         */
        private static class Node<E>{
            E element;//元素
            Node<E> next;//下个元素的地址
            public Node(E element, Node<E> next) {
                this.element = element;
                this.next = next;
            }
        }
    
        /**
         * 返回index位置对应的元素
         * @param index
         * @return
         */
        public E get(int index){
            return node(index).element;
        }
        /**
         * 设置index位置的元素
         * @param index
         * @param element
         * @return
         */
        public E set(int index, E element){
            Node<E> node = node(index);
            E old = node.element;
            node.element = element;
            return old;
        }
    
        /**
         * 往index位置添加元素 --- 改造
         * @param index
         * @param element
         */
        public void add(int index, E element){
            //边界判断
            rangeCheckForAdd(index);
            if (index == 0){// 给空链表添加第一个元素的情况
                //创建新节点
                Node<E> newFirst = new Node<>(element,first);
                //拿到最后一个节点
                Node<E> last = (size==0) ? newFirst : node(size - 1 );
                //尾指向头
                last.next = newFirst;
                //头指向
                first = newFirst;
            }else {
                //index上一个节点
                Node<E> prev = node(index - 1);
                //创建新节点,prev节点指向这个新节点
                prev.next = new Node<>(element, prev.next);
            }
            size++;
        }
        /**
         * 删除index位置对应的元素 --- 改造
         * @param index
         * @return
         */
        public E remove(int index){
            //边界判断
            rangeCheck(index);
            //记录节点
            Node<E> node = first;
            if (index == 0){ // 删除第一个元素是特殊情况
                // 只有一个节点
                if (size == 1){
                    first = null;
                }else {
                    //拿到最后一个节点
                    Node<E> last = node(size - 1);
                    first = first.next;
                    last.next = first;
                }
            }else {
                //index上一个节点
                Node<E> prev = node(index - 1);
                //记录删除的节点
                node = prev.next;
                //node节点下一个节点,prev节点指向这个节点
                prev.next = node.next;
            }
            size --;
            return node.element;
        }
    
        /**
         * 查看元素的位置
         * @param element
         * @return
         */
        public int indexOf(E element){
            // 取出头结点
            Node<E> node = first;
            if(element ==null){// 当element为null时的处理
                // 遍历节点, 找到存储为null的节点, 返回索引
                for (int i = 0; i < size; i++) {
                    if (node.element == 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;
        }
        /**
         * 清除所有元素
         */
        public void clear(){
            size = 0;
            first = null;
        }
    
        /**
         * 获取index位置对应的节点对象
         * @param index
         * @return
         */
        private Node<E> node(int index){
            rangeCheck(index);
            Node<E> node = first;
            for (int i = 0; i < index; i++) {
                node = node.next;
            }
            return node;
        }
    
        /**
         * 打印
         * @return
         */
        @Override
        public String toString() {
            //Size = ? , [1, 2, 3]
            StringBuilder string = new StringBuilder();
            string.append("size=").append(size).append(", [");
            Node<E> node = first;
            for (int i = 0; i < size; i++) {
                if (i != 0){
                    string.append(", ");
                }
                string.append(node.element);
                node = node.next;
            }
            string.append("]");
            return string.toString();
        }
    
    
    }
    
    

    双向循环链表

    双向循环链表的结构
    • 头节点的prev指向尾节点。
    • 尾节点的next指向头节点。

    当只有一个节点

    接口设计

    相较于双向链表,双向循环链表需要重写插入节点删除节点两个方法

    • 需特殊处理添加第一个元素添加到尾节点两种特殊情况

    public class DulCircleLinkedList<E> extends AbstractList<E> {
        ......
        
        /**
         * 往index位置添加元素 --- 改造
         * @param index
         * @param element
         */
        public void add(int index, E element){
            //边界判断
            rangeCheckForAdd(index);
            // size == 0
            // index == 0
            if (index == size){//往最后添加元素
                Node<E> oldLast = last;
                last = new Node<>(element,oldLast,first);
                if (oldLast == null){//这是链表的第一个元素
                    first = last;
                    //指向第一个节点
                    first.next = first;
                    first.prev = first;
                }else {
                    oldLast.next = last;
                    first.prev = last;
                }
            }else {// 正常添加元素
                //插入位置的原结点,即为新节点的next节点
                Node<E> next = node(index);
                //新添加结点的上一个节点,即为该位置原节点的上一个节点
                Node<E> prev = next.prev;
                //创建新结点
                Node<E> node = new Node<>(element,prev,next);
                //原结点上一个结点的next,即为新添加节点
                prev.next = node;
                //原结点的上一个结点,为新添加结点
                next.prev = node;
                if (next == first){//index == 0 即添加的是第一个
                    first = node;
                }
            }
            size++;
        }
            
        ......
        
    }
    
    • 删除节点,就是在环上去掉某一个节点,然后根据删除的节点是首节点或者尾节点来处理firstlast
    • 需要特殊处理只有一个节点的删除操作

    public class DulCircleLinkedList<E> extends AbstractList<E> {
        ......
        
        /**
         * 删除index位置对应的元素 --- 改造
         * @param index
         * @return
         */
        public E remove(int index){
            //边界判断
            rangeCheck(index);
            Node<E> node = first;
            if (size == 1){
                first = null;
                last = null;
            }else {
                //删除的结点
                node = node(index);
                //删除结点的上一个结点
                Node<E> prev = node.prev;
                //删除结点的下一个结点
                Node<E> next = node.next;
                prev.next = next;
                next.prev = prev;
                //删除结点, 只需去掉对这个结点的引用
                if (node == first){//index == 0 即删除的是第一个
                    first = next;
                }
                if (next == last){//index == size - 1 即删除的是最后一个
                    last = prev;
                }
            }
            size --;
            return node.element;
        }
            
        ......
        
    }
    
    完整代码
    /**
     * 双向循环链表的实现
     * @param <E> 泛型
     */
    public class DulCircleLinkedList<E> extends AbstractList<E> {
        /**
         * 元素所在的结点
         */
        private Node<E> first;
        //增加last节点
        private Node<E> last;
    
        /**
         * 内部类 结点
         * @param <E>
         */
        private static class Node<E>{
            E element;//元素
            Node<E> prev;//增加上一个元素地址
            Node<E> next;//下个元素的地址
            public Node(E element, Node<E> prev,Node<E> next) {
                this.element = element;
                this.prev = prev;
                this.next = next;
            }
    
            @Override
            public String toString() {
                StringBuilder sb = new StringBuilder();
                if (prev !=null){
                    sb.append(prev.element);
                }else {
                    sb.append("null");
                }
                sb.append("_").append(element).append("_");
                if (next !=null){
                    sb.append(next.element);
                }else {
                    sb.append("null");
                }
                return sb.toString();
            }
        }
    
        /**
         * 返回index位置对应的元素
         * @param index
         * @return
         */
        public E get(int index){
            return node(index).element;
        }
        /**
         * 设置index位置的元素
         * @param index
         * @param element
         * @return
         */
        public E set(int index, E element){
            Node<E> node = node(index);
            E old = node.element;
            node.element = element;
            return old;
        }
    
        /**
         * 往index位置添加元素 --- 改造
         * @param index
         * @param element
         */
        public void add(int index, E element){
            //边界判断
            rangeCheckForAdd(index);
            // size == 0
            // index == 0
            if (index == size){//往最后添加元素
                Node<E> oldLast = last;
                last = new Node<>(element,oldLast,first);
                if (oldLast == null){//这是链表的第一个元素
                    first = last;
                    //指向第一个节点
                    first.next = first;
                    first.prev = first;
                }else {
                    oldLast.next = last;
                    first.prev = last;
                }
            }else {// 正常添加元素
                //插入位置的原结点,即为新节点的next节点
                Node<E> next = node(index);
                //新添加结点的上一个节点,即为该位置原节点的上一个节点
                Node<E> prev = next.prev;
                //创建新结点
                Node<E> node = new Node<>(element,prev,next);
                //原结点上一个结点的next,即为新添加节点
                prev.next = node;
                //原结点的上一个结点,为新添加结点
                next.prev = node;
                if (next == first){//index == 0 即添加的是第一个
                    first = node;
                }
            }
            size++;
        }
        /**
         * 删除index位置对应的元素 --- 改造
         * @param index
         * @return
         */
        public E remove(int index){
            //边界判断
            rangeCheck(index);
            Node<E> node = first;
            if (size == 1){
                first = null;
                last = null;
            }else {
                //删除的结点
                node = node(index);
                //删除结点的上一个结点
                Node<E> prev = node.prev;
                //删除结点的下一个结点
                Node<E> next = node.next;
                prev.next = next;
                next.prev = prev;
                //删除结点, 只需去掉对这个结点的引用
                if (node == first){//index == 0 即删除的是第一个
                    first = next;
                }
                if (next == last){//index == size - 1 即删除的是最后一个
                    last = prev;
                }
            }
            size --;
            return node.element;
        }
    
        /**
         * 查看元素的位置
         * @param element
         * @return
         */
        public int indexOf(E element){
            // 取出头结点
            Node<E> node = first;
            if(element ==null){// 当element为null时的处理
                // 遍历节点, 找到存储为null的节点, 返回索引
                for (int i = 0; i < size; i++) {
                    if (node.element == 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;
        }
        /**
         * 清除所有元素
         */
        public void clear(){
            size = 0;
            first = null;
            last = null;
        }
    
        /**
         * 获取index位置对应的节点对象
         * @param index
         * @return
         */
        private Node<E> node(int index){
            rangeCheck(index);
            //往后
            if (index < (size >> 1)){
                Node<E> node = first;
                for (int i = 0; i < index; i++) {
                    node = node.next;
                }
                return node;
            }else {//往前
                Node<E> node = last;
                for (int i = size - 1; i > index; i--) {
                    node = node.prev;
                }
                return node;
            }
        }
    
        /**
         * 打印
         * @return
         */
        @Override
        public String toString() {
            //Size = ? , [1, 2, 3]
            StringBuilder string = new StringBuilder();
            string.append("size=").append(size).append(", [");
            Node<E> node = first;
            for (int i = 0; i < size; i++) {
                if (i != 0){
                    string.append(", ");
                }
                string.append(node);
                node = node.next;
            }
            string.append("]");
            return string.toString();
        }
    
    }
    
    

    如何发挥循环链表的最大威力?

    约瑟夫问题

    可以考虑增设1个成员变量、3个方法:

    • current :用于指向某个节点
    • void reset() :让 current 指向头结点 first
    • E next():让 current 往后走一步,也就是 current = current.next
    • E remove() :删除 current 指向的节点,删除成功后让 current 指向下一个节点

    在双向循环链表的基础上增强

    /**
     * 双向循环链表的实现 --- 增强
     * @param <E> 泛型
     */
    public class DulCircleLinkedListPlus<E> extends AbstractList<E> {
        /**
         * 元素所在的结点
         */
        private Node<E> first;
        //增加last节点
        private Node<E> last;
        //用来指向某节点
        private Node<E> current;
    
        /**
         * 内部类 结点
         * @param <E>
         */
        private static class Node<E>{
            E element;//元素
            Node<E> prev;//增加上一个元素地址
            Node<E> next;//下个元素的地址
            public Node(E element, Node<E> prev,Node<E> next) {
                this.element = element;
                this.prev = prev;
                this.next = next;
            }
    
            @Override
            public String toString() {
                StringBuilder sb = new StringBuilder();
                if (prev !=null){
                    sb.append(prev.element);
                }else {
                    sb.append("null");
                }
                sb.append("_").append(element).append("_");
                if (next !=null){
                    sb.append(next.element);
                }else {
                    sb.append("null");
                }
                return sb.toString();
            }
        }
    
        /**
         * 返回index位置对应的元素
         * @param index
         * @return
         */
        public E get(int index){
            return node(index).element;
        }
        /**
         * 设置index位置的元素
         * @param index
         * @param element
         * @return
         */
        public E set(int index, E element){
            Node<E> node = node(index);
            E old = node.element;
            node.element = element;
            return old;
        }
    
        /**
         * 往index位置添加元素 --- 改造
         * @param index
         * @param element
         */
        public void add(int index, E element){
            //边界判断
            rangeCheckForAdd(index);
            // size == 0
            // index == 0
            if (index == size){//往最后添加元素
                Node<E> oldLast = last;
                last = new Node<>(element,oldLast,first);
                if (oldLast == null){//这是链表的第一个元素
                    first = last;
                    //指向第一个节点
                    first.next = first;
                    first.prev = first;
                }else {
                    oldLast.next = last;
                    first.prev = last;
                }
            }else {// 正常添加元素
                //插入位置的原结点,即为新节点的next节点
                Node<E> next = node(index);
                //新添加结点的上一个节点,即为该位置原节点的上一个节点
                Node<E> prev = next.prev;
                //创建新结点
                Node<E> node = new Node<>(element,prev,next);
                //原结点上一个结点的next,即为新添加节点
                prev.next = node;
                //原结点的上一个结点,为新添加结点
                next.prev = node;
                if (next == first){//index == 0 即添加的是第一个
                    first = node;
                }
            }
            size++;
        }
        /**
         * 删除index位置对应的元素 --- 改造
         * @param index
         * @return
         */
        public E remove(int index){
            //边界判断
            rangeCheck(index);
            return remove(node(index));
        }
    
        /**
         * 根据节点删除 --- 新增
         * @param node
         * @return
         */
        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){//index == 0 即删除的是第一个
                    first = next;
                }
                if (next == last){//index == size - 1 即删除的是最后一个
                    last = prev;
                }
            }
            size --;
            return node.element;
        }
    
        /**
         * 查看元素的位置
         * @param element
         * @return
         */
        public int indexOf(E element){
            // 取出头结点
            Node<E> node = first;
            if(element ==null){// 当element为null时的处理
                // 遍历节点, 找到存储为null的节点, 返回索引
                for (int i = 0; i < size; i++) {
                    if (node.element == 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;
        }
        /**
         * 清除所有元素
         */
        public void clear(){
            size = 0;
            first = null;
            last = null;
        }
    
        /**
         * 获取index位置对应的节点对象
         * @param index
         * @return
         */
        private Node<E> node(int index){
            rangeCheck(index);
            //往后
            if (index < (size >> 1)){
                Node<E> node = first;
                for (int i = 0; i < index; i++) {
                    node = node.next;
                }
                return node;
            }else {//往前
                Node<E> node = last;
                for (int i = size - 1; i > index; i--) {
                    node = node.prev;
                }
                return node;
            }
        }
    
        /**
         * 重置 指向头节点 --- 新增
         */
        public void reset(){
            current = first;
        }
    
        /**
         * current的下一个节点 --- 新增
         * @return
         */
        public E next(){
            if (current == null) return null;
            current = current.next;
            return current.element;
        }
    
        /**
         * 删除current指向的节点,删除成功后让current指向下一个节点 --- 新增
         * @return
         */
        public E remove(){
            if (current == null) return null;
            Node<E> next= current.next;
            E element = remove(current);
            if (size == 0){
                current = null;
            }else {
                current = next;
            }
            return element;
        }
    
        /**
         * 打印
         * @return
         */
        @Override
        public String toString() {
            //Size = ? , [1, 2, 3]
            StringBuilder string = new StringBuilder();
            string.append("size=").append(size).append(", [");
            Node<E> node = first;
            for (int i = 0; i < size; i++) {
                if (i != 0){
                    string.append(", ");
                }
                string.append(node);
                node = node.next;
            }
            string.append("]");
            return string.toString();
        }
    }
    

    解决约瑟夫问题

    public class Test {
        public static void main(String[] args) {
            josephus();
        }
        public static void josephus(){
            DulCircleLinkedListPlus<Integer> list = new DulCircleLinkedListPlus<>();
            for(int i = 1; i <= 8; i++){
                list.add(i);
            }
            list.reset(); // current->1
            while(!list.isEmpty()){
                list.next();
                list.next();
                System.out.println(list.remove());
            }
        }
    }
    

    静态链表

    前面所学习的链表,是依赖于指针(引用)实现的,有些编程语言是没有指针的,比如早期的 BASIC、FORTRAN 语言,没有指针的情况下,如何实现链表?

    • 可以通过数组来模拟链表,称为静态链表
    • 数组的每个元素存放 2 个数据:值、下个元素的索引
    • 数组 0 位置存放的是头结点信息

    可以表示如下结构

    思考:如果数组的每个元素只能存放 1 个数据呢?

    • 那就使用 2 个数组,1 个数组存放索引关系,1 个数组存放值
  • 相关阅读:
    JAVA基础(十六)Super关键字
    JAVA基础(十五)this关键字
    JAVA基础(十四2.0)
    JAVA基础(十三)多态
    JAVA基础(十二)抽象类与接口
    git基本使用
    vuetify中treeview部分属性梳理
    vuetify初次使用心得
    react-常见面试题
    maven中profile的使用
  • 原文地址:https://www.cnblogs.com/netu/p/14999193.html
Copyright © 2020-2023  润新知