循环链表

循环链表

单向循环链表

单向循环链表的结构

尾结点的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 个数组存放值
原文地址:https://www.cnblogs.com/netu/p/14999193.html