Data Structures and Algorithms (X) circular list

Circular list


For a single linked list, since each node only stores a pointer back to the rearward tail pointer chain stopped operation, a single linked list pointer in a terminal node is changed from null pointer to the first node, causes the entire single chain to form a ring, such a list to-tail circular list called unidirectional, referred circular list

Empty list

Non-empty list

Here the first node the first node are virtual, in fact, the main difference between a circular linked list and single linked list is that the determination condition for the loop, the original is judged p-> next is empty, it is now p-> next header is not equal to node, the loop is not finished

One-way circular linked list, initialization statement head and tail pointers in the Java language, there is a variable number of elements in list valid record size. And as a single list, circular list also used node, we create two domain data and attribute data pointer field next inner class Node, declare and initialize node

package DS02.动态链表;

import DS01.动态数组.List;
import java.util.Iterator;

//单向循环链表
public class LinkedSingleLoop<E> implements List<E> {
    private Node head;
    private Node rear;
    private int size;
    public LinkedSingleLoop(){
        head=null;
        rear=null;
        size=0;
    }
    
    //内部类
    class Node{   //链表内部的东西,内部类私有,外部不需要知道结点的存在
        E data;  //数据域      类型由外界决定
        LinkedSingleLoop.Node next; //指针域
        //构造函数
        Node(){
            this(null,null);
        }
        Node(E data, LinkedSingleLoop.Node next){
            this.data=data;
            this.next=next;
        }
        
        @Override
        public String toString() {
            return data.toString();     //由调用者决定,结点的toString
        }
    }
    @Override
    public int getSize() {
        return size;
    }

    @Override
    public boolean isEmpty() {
        return size==0;
    }

For the method to insert elements, the elements are stored in the nodes in the data field, first create a new node, here put it another thought, for a circular list we do with the real head node, insert elements as well as a special case , the list is empty, that is our first chart, it sentenced empty, it can be discussed separately.

Still inserted insert elements are three methods, the first plug, the end plug and an intermediate insert, it has been discussed earlier, in my said single chain, not to say, in the code related operations are also detailed comments.

    @Override
    public void add(int index, E e) {
        if(index<0||index>size){
            throw new IllegalArgumentException("角标越界");
        }
        Node n=new Node();  //创新结点
        if(isEmpty()){     //初始为空时,头尾指针都指向新结点
            n.data=e;      //同时新结点数据域存放元素e
            head=n;
            rear=n;
            rear.next=head;
        }else if(index==size){  //尾插
            n.next=rear.next;       //先把尾结点的指针域中的地址(也就是头结点地址)给新结点的指针域 (联系头结点和新结点)
            rear.next=n;            //再把新结点的地址给尾结点的指针域 (联系尾结点和新结点)
            rear=n;                 //移动尾指针 (新结点变成新的尾结点)
        }else if(index==0){     //头插
            rear.next=n;            //先把新结点的地址给尾结点的指针域(联系尾结点和新结点)
            n.next=head;            //把头结点的地址给新结点n的指针(联系头结点和新结点)
            head=n;                 //移动头指针(新结点变成新的头结点)
        }else{                  //中间插入
            Node p=head;                //借助指针p找到指定角标index的前一个结点
            for(int i=0;i<index-1;i++){
                p=p.next;               //移动指针p
            }                           //循环结束时,p在index-1
            n.next=p.next;              //让新结点指针域指向后一个元素地址(建立后一个元素与新结点的联系)
            p.next=n;                   //让p处结点指针域指向新结点的地址(建立前一个结点与新结点的联系)
        }
        size++;       //有效元素+1
    }
    
    @Override
    public void addFirst(E e) {
        //头插
        add(0,e);
    }

    @Override
    public void addLast(E e) {
        //尾插
        add(size,e);
    }

Obtaining the corresponding elements of the subscript, superscript acquired element corresponding angle, to modify the value of the element, by means of the pointer p are come, the time complexity of o (n), some special cases discussed separately, header, footer, the time can be effectively reduced the complexity

    //获取角标对应的元素
    @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;        //借助p指针遍历链表
            for(int i=0;i<index;i++){
                p=p.next;       //移动指针p
            }
            return p.data;   //返回p指针处结点的数据域
        }
    }

    @Override
    public E getFirst() {
        //获取表头
        return get(0);
    }

    @Override
    public E getLast() {
        //获取表尾
        return get(size-1);
    }
    
    //修改指定角标处元素的值,与get()方法类似
    @Override
    public void set(int index, E e) {
        if(index<0||index>size){
            throw new IllegalArgumentException("角标越界");
        }
        Node p=head;
        for(int i=0;i<index;i++){
            p=p.next;
        }
        p.data=e;
    }
    
    //判断是否包含元素e
    @Override
    public boolean contains(E e) {
        //调用find()方法,看是否能找到    
        return find(e)!=-1;
    }
    
    //获取元素e处的角标
    @Override
    public int find(E e) {
        Node p=head;    //借助指针p遍历
        for(int i=0;i<size;i++){
            if(p.data.equals(e)){   //对比指针p处数据域的元素是否参数e内容一样(这里不能用==比,==比的是地址,equals比的是内容)
                return i;   //返回角标
            }
            p=p.next;   //循环一次,指针p移动一次
        }
        return -1;
    }

Removing elements are three methods, delete head, tail and intermediate deleted deleted, are essentially disconnected to remove the front and rear contact junction node, it is recovered, is not repeated, the relevant operation code also detailed in comments.

  @Override
    public E remove(int index) {
        if(isEmpty()){
            throw new IllegalArgumentException("空表");
        }
        if(index<0||index>size){
            throw new IllegalArgumentException("角标越界");
        }
        E ret=null;		//变量ret用于接收返回删除的元素
        if(size==1){       //特殊情况:只有一个元素
            ret=head.data;		
            head=null;
            rear=null;
        }else if(index==size-1){    //尾删
            Node p=head;		
            while(p.next!=rear){    //p指针从头开始,p的下一个为尾结点时跳出
                p=p.next;  //p往后走
            }			   
            ret=rear.data;		//ret接收一下
            p.next=rear.next;	//让倒数第二个结点的指针域指向头结点地址(跳过尾结点)
            rear=p;		//更新尾指针
        }else if(index==1){     //头删
            ret=head.data;  //把元素值给ret用于返回
            rear.next=head.next;    //尾结点的指针域指向第二个元素
            head=head.next;     //头指针往后走,更新
        }else{
            Node p=head;        //借助指针p遍历
            for(int i=0;i<index-1;i++){
                p=p.next;
            }
            Node del=p.next;    //del存放要删除的结点
            ret=del.data;       //把结点的元素内容给ret返回
            p.next=del.next;    //p的指针域指向del的后一个元素,没有人知道del,被回收器回收
        }
        size--;     //有效元素个数递减
        return ret; //返回删除的元素
    }

    @Override
    public E removeFirst() {
    	//头删
        return remove(0);
    }

    @Override
    public E removeLast() {
    	//头删
        return remove(size-1);
    }

    //删除指定元素
    @Override
    public void removeElement(E e) {
        int index=find(e);		//先找再删
        if(index!=-1){			
            remove(index);
        }else{			//找不到对应角标时,抛异常
            throw new IllegalArgumentException("元素不存在");
        }
    }

Empty the list, the constructor code to take over

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

Iterator is traversing the linked list output element, is traversed over and over again order the calendar, the termination condition is not p-> next is not equal to the first node, only p-> next node is not equal to the end, but that would not traverse the last element , where the first node to create a virtual, then the next pointer p which can be a multiple mobile, can be traversed to the end node.

//迭代器
    @Override
    public Iterator<E> iterator() {
        return new LinkedSingleLoopIterator();  //创建内部类对象
    }
    //内部类
    public class LinkedSingleLoopIterator implements Iterator<E>{
        Node p;
        //构造函数
        public LinkedSingleLoopIterator(){
            p=new Node();   //创建虚拟结点
            p.next=head;    //从头结点开始遍历
        }
        @Override
        public boolean hasNext() {
            return p.next!=rear;    //继续条件:p的下一节点不为rear,即尾结点
        }

        @Override
        public E next() {
            p=p.next;       //p指针移动
            return p.data;
        }
    }

Such circular list code, of course, a function of each part must be finished test class test to ensure correctness of the code, all finished to avoid a problem, easy to find.

发布了70 篇原创文章 · 获赞 56 · 访问量 1980

Guess you like

Origin blog.csdn.net/qq_43624033/article/details/103617972