Circular list (one way + two way)

Posted by ghqwerty on Sun, 23 Feb 2020 07:46:35 +0100

1, One way circular list

1. One way circular list details

In fact, a unidirectional circular list is a pointer to the head node of the end node. Generally, the end node of the list points to null.

When adding a node, the tail situation is the same as the normal situation, and no special processing is needed (because the tail refers to the pointer to the head node). When adding nodes to the header, we need to discuss separately the first insertion of nodes, head.next = head.
Deletion nodes are divided into two types: head deletion and general case (tail does not need special treatment because tail has a pointer to head node). When deleting the last node of the header, special processing is also required.

	public class CircleLinkedList<E> extends AbstractList<E> {
	    private Node<E> head;
	    private Node<E> current;
	
	    public 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() {
	            return "Node{" +
	                    "element=" + element +
	                    ", next=" + next.element +
	                    '}';
	        }
	    }
	
	    @Override
	    public void clear() {
	        this.size = 0;
	        head = null;    //Clear memory space of all nodes
	    }
	
	    @Override
	    public void add(int index, E element) {
	        //Watch the head and tail of the list
	
	        if (index == 0) {   //Head insertion
	            Node<E> newNode = new Node<E>(element, head);
	            if (size == 0 || head == null) {   //When inserting the first node
	                head = newNode;
	                head.next = head;
	            } else {
	                Node<E> tail = getNode(size - 1);
	                head = newNode;
	                tail.next = head;
	            }
	
	        } else {    //In general, tail insertion is the same as in general, because the tail has a pointer to the head node
	            rangeCheckForAdd(index);
	            Node<E> preNode = getNode(index - 1);
	            Node<E> newNode = new Node<E>(element, preNode.next);
	            preNode.next = newNode;
	        }
	        size++;
	    }
	
	    @Override
	    public void remove(int index) {
	        rangeCheck(index);
	        Node<E> preNode = head;
	
	        if (index == 0) {   //Head deletion
	            if (size == 1) {
	                head = null;
	            } else {
	                head = head.next;
	                Node<E> tail = getNode(size - 1 - 1);   //If the head is changed, the parameters for finding the tail node also need to be changed, or check before changing the head
	                tail.next = head;
	
	                //Equivalent 1
	                // Node<E> tail = getNode(size - 1); head = head.next; tail.next = head;
	                //Equivalent writing 2
	                // preNode=preNode.next; Node<E> tail = getNode(size - 1); tail.next=preNode; head=preNode;
	            }
	        } else {    //In general, tail deletion is the same as in general, because the tail has a pointer to the head node
	            preNode = getNode(index - 1);
	            preNode.next = preNode.next.next;
	        }
	        size--;
	    }
	
	    public void reset() {
	        current = head;
	    }
	
	    public void next() {
	        if (current == null) return;
	        current = current.next;
	    }
	
	    /**
	     * Delete the current node and return the data of the deleted node. At the same time, move the current node backward
	     *
	     * @return
	     */
	    public E remove() {
	        if (current == null) return null;
	        E element = current.element;
	        int index = indexOf(element);
	        rangeCheck(index);
	        Node<E> preNode = head;
	
	        if (index == 0) {   //Head deletion
	            if (size == 1) {
	                head = null;
	            } else {
	                Node<E> tail = getNode(size - 1);
	                head = head.next;
	                tail.next = head;
	            }
	        } else {    //In general, tail deletion is the same as in general, because the tail has a pointer to the head node
	            preNode = getNode(index - 1);
	            preNode.next = preNode.next.next;
	        }
	        size--;
	
	        current = current.next;
	        return element;
	    }
	
	    @Override
	    public E set(int index, E element) {
	        rangeCheck(index);
	        Node<E> temp = getNode(index);
	        E old = temp.element;
	        temp.element = element;
	        return old;
	    }
	
	    @Override
	    public E get(int index) {
	        rangeCheck(index);
	        Node<E> temp = getNode(index);
	        return temp.element;
	    }
	
	    public Node<E> getNode(int index) {
	        Node<E> temp = head;
	        for (int i = 0; i < index; i++) {
	            temp = temp.next;
	        }
	        return temp;
	    }
	
	    @Override
	    public int indexOf(E element) {
	        Node<E> temp = head;
	        for (int i = 0; i < size; i++) {
	            if (temp.element.equals(element))
	                return i;
	            temp = temp.next;
	        }
	        return ELEMENT_NOT_FOUND;
	    }
	
	    @Override
	    public String toString() {
	        StringBuilder str = new StringBuilder();
	        str.append("size=").append(size);
	        if (size != 0) {
	            Node temp = head;
	            str.append(", [");
	            for (int i = 0; i < size; i++) {
	                str.append(temp);
	                if (i != size - 1) {
	                    str.append(",");
	                }
	                temp = temp.next;
	            }
	            str.append("]");
	        }
	        return str.toString();
	    }
	
	    public static void main(String[] args) {
	        CircleLinkedList<Object> list = new CircleLinkedList<>();    //Polymorphism, member method compilation on the left, operation on the right
	        list.add(new Person(23, "Gon Freecss"));
	        list.add(0, 66);
	        list.add(99.9);
	        list.add(list.size(), 100);
	        list.remove(0);
	        list.remove(list.size() - 1);
	        list.set(1, 666);
	        System.out.println(list.get(0));
	        System.out.println(list);
	    }
	}

2. Test results




2, Two way circular list

1. Two way circular list details

In fact, the two-way circular list is a two-way list, but the special thing is that the tail node has a pointer to the head node, and the head node also has a pointer to the tail node.

When adding and deleting nodes, it is similar to the case of unidirectional circular list division, except that when adding and deleting the head, the head and tail will change.

A group of people...

	public class DoubleCircleLinkedList<E> extends AbstractList<E> {
	    private Node<E> head;
	    private Node<E> tail;
	    private Node<E> current;
	
	    public static class Node<E> {
	        Node<E> last;
	        E element;
	        Node<E> next;
	
	        public Node(Node<E> last, E element, Node<E> next) {
	            this.last = last;
	            this.element = element;
	            this.next = next;
	        }
	
	        @Override
	        public String toString() {
	            StringBuilder str = new StringBuilder();
	            str.append("Node{ last=");
	            if (last != null)
	                str.append(last.element);
	            else
	                str.append("null");
	            str.append(", element=");
	            str.append(element);
	            str.append(", ");
	            str.append("next=");
	            if (next != null)
	                str.append(next.element);
	            else
	                str.append("null");
	            str.append(" }");
	            return str.toString();
	        }
	
	        @Override
	        protected void finalize() throws Throwable {
	            System.out.println("Node Hang up");
	        }
	    }
	
	    @Override
	    public void clear() {
	        this.size = 0;
	        //As long as any space in the JVM is not pointed to by the GC Roots object, it will be destroyed
	        head = null;
	        tail = null;
	    }
	
	    @Override
	    public void add(int index, E element) {
	        rangeCheckForAdd(index);
	        Node<E> node = getNode(index);
	
	        if (index == 0) {   //Insert in the head
	            Node<E> newNode = new Node<E>(tail, element, node); //The head node of two-way circular chain should point to the tail node
	            if (node == null || size == 0) {   //When inserting the first node
	                head = newNode;
	                tail = newNode;
	                head.last = tail;
	                tail.next = head;   //After the head is inserted, modify the tail node to point to the new head node
	            } else {
	                head.last = newNode;
	                head = newNode;
	                tail.next = head;   //After the head is inserted, modify the tail node to point to the new head node
	            }
	
	        } else if (index == size) {  //Tail insertion
	            Node<E> newNode = new Node<E>(tail, element, head); //The tail node of two-way circular list should point to the head node
	            tail.next = newNode;
	            tail = newNode;
	            head.last = tail;   //After the tail is inserted, modify the head node to point to the new tail node
	        } else {    //Ordinary circumstances
	            Node<E> former = node.last;
	            Node<E> newNode = new Node<E>(former, element, node);
	            former.next = newNode;
	            node.last = newNode;
	        }
	        size++;
	    }
	
	    @Override
	    public void remove(int index) {
	        rangeCheck(index);
	        Node<E> node = getNode(index);
	        Node<E> former = node.last;
	        Node<E> latter = node.next;
	
	        if (index == 0) {   //Head deletion
	            latter.last = tail; //The head node of two-way circular chain should point to the tail node
	            head = latter;
	            tail.next = head;   //The tail node of two-way circular list should point to the head node
	        } else if (index == size - 1) {     //Tail deletion
	            former.next = head; //The tail node of two-way circular list should point to the head node
	            tail = former;
	            head.last = tail;   //The head node of two-way circular chain should point to the tail node
	        } else {
	            //Ordinary circumstances
	            former.next = latter;
	            latter.last = former;
	        }
	        size--;
	    }
	
	    public void reset() {
	        current = head;
	    }
	
	    public void next() {
	        if (current == null) return;
	        current = current.next;
	    }
	
	    /**
	     * Delete the current node and return the data of the deleted node. At the same time, move the current node backward
	     *
	     * @return
	     */
	    public E remove() {
	        if (current == null) return null;
	        E element = current.element;
	        int index = indexOf(element);
	        rangeCheck(index);
	        Node<E> node = getNode(index);
	        Node<E> former = node.last;
	        Node<E> latter = node.next;
	
	        if (index == 0) {   //Head deletion
	            latter.last = tail; //The head node of two-way circular chain should point to the tail node
	            head = latter;
	            tail.next = head;   //The tail node of two-way circular list should point to the head node
	        } else if (index == size - 1) {     //Tail deletion
	            former.next = head; //The tail node of two-way circular list should point to the head node
	            tail = former;
	            head.last = tail;   //The head node of two-way circular chain should point to the tail node
	        } else {
	            //Ordinary circumstances
	            former.next = latter;
	            latter.last = former;
	        }
	        size--;
	
	        current = current.next;
	        return element;
	    }
	
	    @Override
	    public E set(int index, E element) {
	        rangeCheck(index);
	        Node<E> temp = getNode(index);
	        E old = temp.element;
	        temp.element = element;
	        return old;
	    }
	
	    @Override
	    public E get(int index) {
	        rangeCheck(index);
	        return getNode(index).element;
	    }
	
	
	    public Node<E> getNode(int index) {
	        Node<E> temp = null;
	        if (index < this.size >> 1) {
	            temp = head;    //Ab initio traversal
	            for (int i = 0; i < index; i++) {
	                temp = temp.next;
	            }
	        } else {
	            temp = tail;     //Traversal from tail
	            for (int i = 0; i < size - 1 - index; i++) {
	                temp = temp.last;
	            }
	        }
	        return temp;
	    }
	
	    @Override
	    public int indexOf(E element) {
	        Node<E> temp = head;
	        for (int i = 0; i < size; i++) {
	            if (temp.element.equals(element))
	                return i;
	            temp = temp.next;
	        }
	        return ELEMENT_NOT_FOUND;
	    }
	
	    @Override
	    public String toString() {
	        StringBuilder str = new StringBuilder();
	        str.append("size=").append(size);
	        if (size != 0) {
	            Node temp = head;
	            str.append(", [");
	            for (int i = 0; i < size; i++) {
	                str.append(temp.toString());
	                if (i != size - 1) {
	                    str.append(",");
	                }
	                temp = temp.next;
	            }
	            str.append("]");
	        }
	        return str.toString();
	    }
	
	    public static void main(String[] args) {
	        DoubleCircleLinkedList<Object> list = new DoubleCircleLinkedList<>();    //Polymorphism, member method compilation on the left, operation on the right
	        list.add(new Person(23, "Gon Freecss"));
	        list.add(0, 66);
	        list.add(99.9);
	        list.add(list.size(), 100);
	        list.remove(0);
	        list.remove(list.size() - 1);
	        list.set(1, 666);
	        System.out.println(list.get(0));
	        System.out.println(list);
	    }
	
	}

2. Test results

Published 38 original articles, won praise 5, visited 3783
Private letter follow

Topics: jvm