• 循环链表(单链表)


      在单链表中,尾节点的next指向null,如果尾节点的next指向头节点,链表不就循环起来了?在循环链表中,没有一个节点的next指向null。尽管每一个节点都指向下一个节点,但循环链表还是有头部和尾部之分。外部怎么访问循环链表?需要一个外部的引用指向链表,那指向链表的头节点还是尾节点?指向链表的尾节点。因为如果指向头节点的话,链表尾部的插入需要遍历整个链表,指向尾节点不存在这个问题。如果在头部插入,循环列表尾节点的next就是头节点,也没有问题,循环列表有如下几种情况

      当循环链表中只有一个节点时,节点自己指向自己。实现循环链表,需要一个外部变量指向链表的尾节点。

    class CircleLinkedList<T> {
        private class Node {
            T data;
            Node next;
    
            Node(T data) {
                this.data = data;
            }
        }
    
        private Node last;
    }

      向链表中插入元素有三种情况,插入到链表的头部,插入到链表的尾部,插入到链表两个节点之间。当然,无论是哪种情况,都要检查链表是否为空。如果链表为空,让last指向新创建的节点,并自己指向自己。

    private void addToEmpty(T data) {
        var newNode = new Node(data);
        last = newNode;
        newNode.next =last; // 链表自己指向自己
    }

      如果链表不为空,比如有如下链表

       1,向链表头部插入元素,比如向链表的头部插入6,那就先创建一个新节点newNode, 然后让newNode.next 指向last.next,因为last.next就是头节点,最后让last.next指向最新的头节点newNode.

    public void addFront(T data) {
        if (last == null) {
            addToEmpty(data);
        } else {
            var newNode = new Node(data);
            newNode.next = last.next;
            last.next = newNode;
        }
    }

      2,向链表尾部插入节点,让last的next指向新的节点,last再指向新节点,当然,不要忘记它是循环链表,新节点的next还要指向头节点

    public void addLast(T data) {
        if (last == null) {
            addToEmpty(data);
        } else {
            var newNode = new Node(data);
            newEntry.next = last.next; // 新节点的next指向头节点
            last.next = newEntry;
            last = newEntry;
        }
    }

       3,向两个节点之间添加元素,就是向某一个元素后面添加元素,首先遍历链表,找到这个元素。如果这个元素正好是尾节点,那么还要移动last的位置

    // 向某个item后面 添加值,
    public void addAfter(T item, T data) {
        if(last == null) {
            throw new RuntimeException("链表为空");
        } else {
            var found = false;
            var temp = last.next; // 头节点
    
            do {
                if(temp.data.equals(item)){
                    found = true;
                } else {
                    temp = temp.next;
                }
            } while(temp != last.next && !found);
    
            if(found) {
                // temp就是将要向它向面添加元素的节点
                var newNode = new Node(data);
                // 先获取到旧的temp.next
                var oldNext = temp.next;
                temp.next = newNode;
                newNode.next = oldNext;
    
                //如果正好添加尾节点后面,还要移动last的指向
                if (temp == last) {
                    last = newNode;
                }
            } else {
                throw new RuntimeException("没有找到元素");
            }
        }
    }

      删除节点:如果链表为空,肯定是要报错的。如果链表中只有一个节点,那么如果删除的节点正好是这个节点,那么就删除,last=null,如果不是,那也只能报错了。如果有多个节点,但正好删除的是最后一个节点,那就要循环链表,找到最后一个节点的前一个节点,然后修改last指向,

       如果是删除中间一个元素,遍历找到前一个节点,

    public void deleteNode(T item) {
        // 链表为空,肯定不能删除
        if(last == null) {
            throw new RuntimeException("链表为空");
        } else if(last.next == last) { // 链表只有一个节点
            if (last.data.equals(item)){
                last = null;
            } else {
                throw new RuntimeException("没有找到元素");
            }
            // 链表有多个节点
        } else if (last.data.equals(item)) { // 删除的节点,正好是最后一个节点
            // 找到最后一个节点的前一个节点
            var temp = last;
            while(temp.next != last) {
                temp = temp.next;
            }
    
            temp.next = last.next;
            last = temp;
        } else {
            var temp = last;
            // 找到前面一个节点
            while(!temp.next.data.equals(item) && temp.next != last) {
                temp = temp.next;
            }
    
            if(temp.next.data.equals(item)) {
                temp.next= temp.next.next;
            } else {
                throw new RuntimeException("没有找到元素");
            }
        }
    }

      遍历

    public void traverse() {
        if (last == null) {
            throw new RuntimeExceiption('空链表');
        }
    
        var p = last.next;
    
        do {
            System.out.print(p.data + " ");
            p = p.next;
        }
        while (p != last.next);
    }

      测试

    public static void main(String[] args) {
        var circleList = new CircleSingleLinkedList<Integer>();
    
        circleList.addFront(6);
        circleList.addEnd(8);
        circleList.addFront(2);
    
        circleList.addAfter(2, 10);
    
        circleList.traverse();
    
        System.out.println();
    
        circleList.deleteNode(8);
        circleList.traverse();
    }

       

  • 相关阅读:
    POJ 1811 大整数素数判断 Miller_Rabin
    hdu 4940 无源汇有上下界最大流
    hdu 4975 最大流解决行列和求矩阵问题,用到矩阵dp优化
    hdu 4971 多校10最大权闭合图
    hdu 4888 最大流给出行列和求矩阵
    poj 3621最优比例生成环(01分数规划问题)
    poj 2728 最优比例生成树(01分数规划)模板
    最优比例生成树最优比率生成树 01分数规划问题
    poj 2553强连通+缩点
    poj 2831 次小生成树模板
  • 原文地址:https://www.cnblogs.com/SamWeb/p/16093222.html
Copyright © 2020-2023  润新知