Linked list, single linked list, two-way (one-way) circular linked list

dynamic chain

Single linked list:

//Head node refers to the first node in the linked list, which can be divided into real head node and virtual head node;
(1) Real head node: the first node is used to store data (usually the real head node is used).
(2) Virtual head node: the first node cannot store data.
(3) Header pointer: it is just a reference variable that stores the address of the header node. There is no precursor node and there are successors.
(4) Tail pointer: the same head pointer is just the pointer to the last node in the linked list. The tail points to null. There is a precursor but no successor.

To add elements to a single linked list:

To insert an element at element 3, first set the pointer p so that p points to the previous element to be inserted. For example, to insert an element at element 3 in the figure, first let p find element 2 because the address of element 3 exists in element 2, and then give the address of element 2 to the new element so that the new element points to element 3 Then let element 2 point to the new element.
//For understanding with the help of table building, there is actually no element of corner mark.

Delete elements from single linked list:

//Delete element 4, replace the address of element 5 stored in element 4 with the address of element 4 originally stored in element 3, and leave the address of element 4 blank. Directly, let element 3 directly point to element 5 and release element 4.
//If both the head pointer and the tail pointer point to the same element, when deleting, just set the head pointer and the tail pointer to null respectively.

Code part:
//Define the linked list class, but there is another node class, so we need to use the internal class to define the node. Here, the linked list is in the outermost layer and the node is in the interior, so the linked list is an external class and the node is an internal class.
//If you use the toString method, because if you don't use toString, the result is the name of the data, not the data itself, so you use the toString method to get the data content. The print node is actually the data stored in the print node.
//Here is a dynamic linked list, which is automatically expanded, so you don't need to set the default value.

//Unidirectional linked list
public class LinkedSinglyList<E> implements List<E> {

    //Define node object
    private class Node {
        E data;     //Data domain
        Node next;  //Pointer field
        public Node(){
            this(null,null);
        }
        public Node(E data) {
            this(data,null);
        }
        public Node(E data, Node next) {
            this.data = data;
            this.next = next;
        }
        @Override
        public String toString() {
            return data.toString();
        }
    }
    private Node head;  //Head pointer
    private Node tail;  //Tail pointer
    private int size;   //Number of elements

    public LinkedSinglyList() {
        head = null;
        tail = null;
        size = 0;
    }

    public LinkedSinglyList(E[] arr) {
        if (arr == null || arr.length == 0) {
            throw new IllegalArgumentException("arr is null");
        }
        for (int i = 0; i < arr.length; i++) {
            add(arr[i]);
        }
    }

    @Override
    public void add(E element) {
        add(size, element);
    }

    @Override
    public void add(int index, E element) {
        if (index < 0 || index > size) {
            throw new IllegalArgumentException("add index out of range");
        }
        Node n = new Node(element);
        if (size == 0) {
            head = n;
            tail = n;
        } else if (index == 0) {
            n.next = head;
            head = n;
        } else if (index == size) {
            tail.next = n;
            tail = n;
        } else {
            Node p = head;
            for (int i = 0; i < index - 1; i++) {
                p = p.next;
            }
            n.next = p.next;
            p.next = n;
        }
        size++;
    }

    @Override
    public void remove(E element) {
        int index = indexOf(element);
        if (index != -1) {
            remove(index);
        }
    }
    @Override
    public E remove(int index) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("remove index out of range");
        }
        E ret = null;
        if (size == 1) {
            ret = head.data;
            head = null;
            tail = null;
        } else if (index == 0) {
            Node n = head;
            ret = n.data;
            head = n.next;
            n.next = null;
        } else if (index == size - 1) {
            Node p = head;
            while (p.next != tail) {
                p = p.next;
            }
            ret = tail.data;
            p.next = null;
            tail = p;
        } else {
            Node p = head;
            for (int i = 0; i < index - 1; i++) {
                p = p.next;
            }
            Node n = p.next;
            ret = n.data;
            p.next = n.next;
            n.next = null;
        }
        size--;
        return ret;
    }

    @Override
    public E get(int index) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("get index out of range");
        }
        if (index == 0) {
            return head.data;
        } else if (index == size - 1) {
            return tail.data;
        } else {
            Node p = head;
            for (int i = 0; i < index; i++) {
                p = p.next;
            }
            return p.data;
        }
    }

    @Override
    public E set(int index, E element) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("get index out of range");
        }
        E ret = null;
        if (index == 0) {
            ret = head.data;
            head.data = element;
        } else if (index == size - 1) {
            ret = tail.data;
            tail.data = element;
        } else {
            Node p = head;
            for (int i = 0; i < index; i++) {
                p = p.next;
            }
            ret = p.data;
            p.data = element;
        }
        return ret;
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public int indexOf(E element) {
        Node p = head;
        int index = 0;
        while (!p.data.equals(element)) {
            p = p.next;
            index++;
            if (p == null) {
                return -1;
            }
        }
        return index;
    }

    @Override
    public boolean contains(E element) {
        return indexOf(element) != -1;
    }

    @Override
    public boolean isEmpty() {
        return size == 0 && head == null && tail == null;
    }

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

    @Override
    public void sort(Comparator<E> c) {
        if (c == null) {
            throw new IllegalArgumentException("comparator can not be null");
        }
        //Insert sort o here (n ^ 3)
        /*
        for (int i = 1; i < size; i++) {
            E e = get(i);
            int j = 0;
            for (j = i; j > 0 && c.compare(get(j - 1), e) > 0; j--) {
                set(j, get(j - 1));
            }
            set(j, e);
        }
        */
        if (size == 0 || size == 1) {
            return;
        }
        Node nodeA = head;
        Node nodeB = nodeA.next;
        while (true) {
            while (true) {
                if (c.compare(nodeA.data, nodeB.data) > 0) {
                    swap(nodeA, nodeB);
                }
                if (nodeB == tail) {
                    break;
                }
                nodeB = nodeB.next;
            }
            if (nodeA.next == tail) {
                break;
            }
            nodeA = nodeA.next;
            nodeB = nodeA.next;
        }
    }

    private void swap(Node nodeA, Node nodeB) {
        E temp = nodeA.data;
        nodeA.data = nodeB.data;
        nodeB.data = temp;
    }

    @Override
    public List<E> subList(int fromIndex, int toIndex) {
        //0 <= fromIndex <= toIndex <= size - 1    [fromIndex,toIndex]
        if (fromIndex < 0 || toIndex >= size || fromIndex > toIndex) {
            throw new IllegalArgumentException("must 0 <= fromIndex <= toIndex <= size - 1");
        }

        LinkedSinglyList<E> list = new LinkedSinglyList<>();
  
        Node nodeA = head;
        for (int i = 0; i < fromIndex; i++) {
            nodeA = nodeA.next;
        }
        Node nodeB = head;
        for (int i = 0; i < toIndex; i++) {
            nodeB = nodeB.next;
        }
        Node p = nodeA;
        while (true) {
            list.add(p.data);
            if (p == nodeB) {
                break;
            }
            p = p.next;
        }
        return list;
    }
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append('[');
        if (isEmpty()) {
            sb.append(']');
        } else {
            Node p = head;
            while (true) {
                sb.append(p.data);
                if (p == tail) {
                    sb.append(']');
                    break;
                }
                sb.append(',');
                sb.append(' ');
                p = p.next;
            }
        }
        return sb.toString();
    }
    @Override
    public Iterator<E> iterator() {
        return new LinkedSinglyListIterator();
    }

    class LinkedSinglyListIterator implements Iterator<E> {
        private Node cur = head;

        @Override
        public boolean hasNext() {
            return cur != null;
        }
        @Override
        public E next() {
            E ret = cur.data;
            cur = cur.next;
            return ret;
        }
    }
}

One way circular linked list

	If the pointer to the last node of the single linked list points to the head of the linked list instead of to NULL,Then it constitutes a one-way circular linked list.


//Different from the single linked list, the single linked list allows the tail pointer to point to null, while the one-way circular linked list allows the tail pointer to point to the head pointer to complete the cycle.

Add element:

//Adding is similar to adding a single linked list. Set the pointer p to point to the previous element to be inserted, because the previous element has the address of the current element to be inserted. Give the address of the previous element to the new element, let the new element point to the current element to be inserted, and then let the previous element point to the new element.

Delete element:

//To delete an element, you need to set a null pointer. If you delete a header element, first pass the content of the header element to the null pointer, then let the header pointer point to the next element of the null pointer, and then let the null pointer point to null. Delete the tail and let the null pointer traverse. Because it is one-way, you must traverse to find the previous element of the tail element, The random operation is the same as deleting the header pointer;
//If there is only one element, you can directly make the head pointer and tail pointer point to null;
//If you delete other elements, you also need to traverse to the element before the element to be deleted and delete it with a null pointer;
code:

//One way circular linked list
public class LinkedSinglyCircularList<E> implements List<E> {

    //Define node object
    private class Node {
        E data;     //Data domain
        Node next;  //Pointer field
        public Node(){
            this(null,null);
        }
        public Node(E data) {
            this(data,null);
        }
        public Node(E data, Node next) {
            this.data = data;
            this.next = next;
        }
        @Override
        public String toString() {
            return data.toString();
        }
    }
    private Node head;  //Head pointer
    private Node tail;  //Tail pointer
    private int size;   //Number of elements

    public LinkedSinglyCircularList() {
        head = null;
        tail = null;
        size = 0;
    }

    public LinkedSinglyCircularList(E[] arr) {
        if (arr == null || arr.length == 0) {
            throw new IllegalArgumentException("arr is null");
        }
        for (int i = 0; i < arr.length; i++) {
            add(arr[i]);
        }
    }

    @Override
    public void add(E element) {
        add(size, element);
    }

    @Override
    public void add(int index, E element) {
        if (index < 0 || index > size) {
            throw new IllegalArgumentException("add index out of range");
        }
        Node n = new Node(element);
        if (size == 0) {
            head = n;
            tail = n;
            tail.next = head;   //new code
        } else if (index == 0) {
            n.next = head;
            head = n;
            tail.next = head;   //new code
        } else if (index == size) {
            n.next = tail.next; //new code
            tail.next = n;
            tail = n;
        } else {
            Node p = head;
            for (int i = 0; i < index - 1; i++) {
                p = p.next;
            }
            n.next = p.next;
            p.next = n;
        }
        size++;
    }

    @Override
    public void remove(E element) {
        int index = indexOf(element);
        if (index != -1) {
            remove(index);
        }
    }
    @Override
    public E remove(int index) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("remove index out of range");
        }
        E ret = null;
        if (size == 1) {
            ret = head.data;
            head = null;
            tail = null;
        } else if (index == 0) {
            Node n = head;
            ret = n.data;
            head = n.next;
            n.next = null;
            tail.next = head; //new code
        } else if (index == size - 1) {
            Node p = head;
            while (p.next != tail) {
                p = p.next;
            }
            ret = tail.data;
            p.next = tail.next; //change code
            tail = p;
        } else {
            Node p = head;
            for (int i = 0; i < index - 1; i++) {
                p = p.next;
            }
            Node n = p.next;
            ret = n.data;
            p.next = n.next;
            n.next = null;
        }
        size--;
        return ret;
    }

    @Override
    public E get(int index) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("get index out of range");
        }
        if (index == 0) {
            return head.data;
        } else if (index == size - 1) {
            return tail.data;
        } else {
            Node p = head;
            for (int i = 0; i < index; i++) {
                p = p.next;
            }
            return p.data;
        }
    }

    @Override
    public E set(int index, E element) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("get index out of range");
        }
        E ret = null;
        if (index == 0) {
            ret = head.data;
            head.data = element;
        } else if (index == size - 1) {
            ret = tail.data;
            tail.data = element;
        } else {
            Node p = head;
            for (int i = 0; i < index; i++) {
                p = p.next;
            }
            ret = p.data;
            p.data = element;
        }
        return ret;
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public int indexOf(E element) {
        Node p = head;
        int index = 0;
        while (!p.data.equals(element)) {
            p = p.next;
            index++;
            if (p == head) {    //change code
                return -1;
            }
        }
        return index;
    }

    @Override
    public boolean contains(E element) {
        return indexOf(element) != -1;
    }

    @Override
    public boolean isEmpty() {
        return size == 0 && head == null && tail == null;
    }

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

    @Override
    public void sort(Comparator<E> c) {
        if (c == null) {
            throw new IllegalArgumentException("comparator can not be null");
        }
        if (size == 0 || size == 1) {
            return;
        }
        Node nodeA = head;
        Node nodeB = nodeA.next;
        while (true) {
            while (true) {
                if (c.compare(nodeA.data, nodeB.data) > 0) {
                    swap(nodeA, nodeB);
                }
                if (nodeB == tail) {
                    break;
                }
                nodeB = nodeB.next;
            }
            if (nodeA.next == tail) {
                break;
            }
            nodeA = nodeA.next;
            nodeB = nodeA.next;
        }
    }

    private void swap(Node nodeA, Node nodeB) {
        E temp = nodeA.data;
        nodeA.data = nodeB.data;
        nodeB.data = temp;
    }

    @Override
    public List<E> subList(int fromIndex, int toIndex) {
        if (fromIndex < 0 || toIndex >= size || fromIndex > toIndex) {
            throw new IllegalArgumentException("must 0 <= fromIndex <= toIndex <= size - 1");
        }

        LinkedSinglyList<E> list = new LinkedSinglyList<>();
        Node nodeA = head;
        for (int i = 0; i < fromIndex; i++) {
            nodeA = nodeA.next;
        }
        Node nodeB = head;
        for (int i = 0; i < toIndex; i++) {
            nodeB = nodeB.next;
        }
        Node p = nodeA;
        while (true) {
            list.add(p.data);
            if (p == nodeB) {
                break;
            }
            p = p.next;
        }
        return list;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append('[');
        if (isEmpty()) {
            sb.append(']');
        } else {
            Node p = head;
            while (true) {
                sb.append(p.data);
                if (p == tail) {
                    sb.append(']');
                    break;
                }
                sb.append(',');
                sb.append(' ');
                p = p.next;
            }
        }
        return sb.toString();
    }

    @Override
    public Iterator<E> iterator() {
        return new LinkedSinglyCircularListIterator();
    }

    class LinkedSinglyCircularListIterator implements Iterator<E> {
        private Node cur = head;
        private boolean flag = true; //Is it on the first lap

        @Override
        public boolean hasNext() {
            if (isEmpty()) {
                return false;
            }
            return flag;
        }
        @Override
        public E next() {
            E ret = cur.data;
            cur = cur.next;
            if (cur == head) {
                flag = false;
            }
            return ret;
        }
    }

    //Joseph Ring problem
    public void josephusLoop() {
        if (size <= 2) {
            return;
        }
        Node p = head;
        while (size != 2) {
            p = p.next;
            Node del = p.next;
            if (del == head) {
                head = del.next;
            } else if (del == tail) {
                tail = p;
            }
            p.next = del.next;
            del.next = null;
            p = p.next;
            size--;
        }
    }

    //Linked list inversion problem
    public void reverse() {
        if (size == 0 || size == 1) {
            return;
        }
        Node dummpyHead = new Node();   //Virtual head node
        Node p = head;
        for (int i = 0; i < size; i++) {
            Node n = new Node(p.data);
            if (dummpyHead.next == null) {
                tail = n;
            }
            n.next = dummpyHead.next;
            dummpyHead.next = n;
            p = p.next;
        }
        head = dummpyHead.next;
    }
}

Application of single linked list - Joseph problem: (Joseph Ring)

//Joseph Ring problem
    public void josephusLoop() {
        if (size <= 2) {
            return;
        }
        Node p = head;
        while (size != 2) {
            p = p.next;
            Node del = p.next;
            if (del == head) {
                head = del.next;
            } else if (del == tail) {
                tail = p;
            }
            p.next = del.next;
            del.next = null;
            p = p.next;
            size--;
        }
    }
	Let's start from the node and delete the element every time to the third node. As shown in the figure, starting from node 1, delete node 3, node 6, node 4, node 2, and finally the remaining node 5 and node 1.

code:
Linked list inversion problem:

//Linked list inversion problem
    public void reverse() {
        if (size == 0 || size == 1) {
            return;
        }
        Node dummpyHead = new Node();   //Virtual head node
        Node p = head;
        for (int i = 0; i < size; i++) {
            Node n = new Node(p.data);
            if (dummpyHead.next == null) {
                tail = n;
            }
            n.next = dummpyHead.next;
            dummpyHead.next = n;
            p = p.next;
        }
        head = dummpyHead.next;
    }
}

Every seven games:

code:

//Every seven games
/*
Enter the number of players
 Enter which player to start with
 Enter the number from which the player starts
 Enter a total of several numbers to play
 Print out all the numbers that each player will report
*/
public class SevenGame {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);

        System.out.print(">>>Please enter the number of players:");
        int playerCount = input.nextInt();

        System.out.print(">>>Please enter which player to start with:");
        int beginPlayer = input.nextInt();

        System.out.print(">>>Please enter which number to start with:");
        int beginNumber = input.nextInt();

        System.out.print(">>>Please enter the maximum number:");
        int maxNumber = input.nextInt();

        //Create a collection of players
        LinkedSinglyCircularList<ArrayList<String>> list = new LinkedSinglyCircularList<>();
        //Create player objects separately
        for (int i = 0; i < playerCount; i++) {
            list.add(new ArrayList<>());
        }

        //Starting player's corner marker
        int index = beginPlayer - 1;

        //Give the numbers to each player in turn
        for (int num = beginNumber; num <= maxNumber; num++) {
            list.get(index++ % playerCount).add(getAnswer(num));
        }

        for (int i = 0; i < list.size(); i++) {
            System.out.println("The first" + (i + 1) + "Players:" + list.get(i));
        }
    }

    private static String getAnswer(int num) {
        if (num % 7 == 0 || (num + "").contains("7")) {
            return "too";
        }
        return num + "";
    }
}

Bidirectional circular linked list

	Each node has two pointers to the predecessor node and the successor node respectively;
	Bidirectional linked list, also known as double linked list, is a kind of linked list. There are two pointers in each data node, pointing to the direct successor and direct precursor respectively. Therefore, starting from any node in the two-way linked list, you can easily access its predecessor node and successor node. Generally, we construct a two-way circular linked list.


Add element:
(1) : add elements from beginning to end:

(2) : add elements in the middle:

Some codes are shown in the figure:

@Override
    public void add(E element) {
        add(size, element);
    }

    @Override
    public void add(int index, E element) {
        if (index < 0 || index > size) {
            throw new IllegalArgumentException("add index out of range");
        }
        Node n = new Node(element);
        if (size == 0) {
            head = n;
            tail = n;
            tail.next = head;
            head.pre = tail;
        } else if (index == 0) {
            n.pre = head.pre;
            n.next = head;
            head.pre = n;
            head = n;
            tail.next = head;
        } else if (index == size) {
            n.next = tail.next;
            tail.next = n;
            n.pre = tail;
            tail = n;
            head.pre = tail;
        } else {
            Node p, q;
            if (index <= size / 2) {
                p = head;
                for (int i = 0; i < index - 1; i++) {
                    p = p.next;
                }
                q = p.next;
                p.next = n;
                n.pre = p;
                q.pre = n;
                n.next = q;
            } else {
                p = tail;
                for (int i = size - 1; i > index; i--) {
                    p = p.pre;
                }
                q = p.pre;
                q.next = n;
                n.pre = q;
                n.next = p;
                p.pre = n;
            }
        }
        size++;
    }

Delete element:
(1) Delete elements from beginning to end:

(2) Middle delete element:

Some codes are shown in the figure:

@Override
    public void remove(E element) {
        int index = indexOf(element);
        if (index != -1) {
            remove(index);
        }
    }

    @Override
    public E remove(int index) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("remove index out of range");
        }
        E ret = null;
        Node node;
        if (size == 1) {
            ret = head.data;
            head = null;
            tail = null;
        } else if (index == 0) {
            ret = head.data;
            node = head.next;
            head.next = null;
            node.pre = head.pre;
            head.pre = null;
            head = node;
            tail.next = head;
        } else if (index == size - 1) {
            ret = tail.data;
            node = tail.pre;
            tail.pre = null;
            node.next = tail.next;
            tail.next = null;
            tail = node;
            head.pre = tail;
        } else {
            Node p, q, r;
            if (index <= size / 2) {
                p = head;
                for (int i = 0; i < index - 1; i++) {
                    p = p.next;
                }
                q = p.next;
                r = q.next;
                ret = q.data;
                p.next = r;
                r.pre = p;
                q.next = null;
                q.pre = null;
            } else {
                p = tail;
                for (int i = size - 1; i > index + 1; i--) {
                    p = p.pre;
                }
                q = p.pre;
                r = q.pre;
                ret = q.data;
                r.next = p;
                p.pre = r;
                q.next = null;
                q.pre = null;
            }
        }
        size--;
        return ret;
    }

//Pay attention to the corresponding relationship. Don't make a mistake!!!

Let's talk about the sorting method:
We can use insert sort or other methods:

@Override
    public void sort(Comparator<E> c) {
    	//An exception is thrown for empty judgment
        if (c == null) {
            throw new IllegalArgumentException("comparator can not be null");
        }
        //The valid length is empty or one
        if (size == 0 || size == 1) {
            return;
        }
        for (Node nodeA = head.next; nodeA != head; nodeA = nodeA.next) {
            E e = nodeA.data;
            Node nodeB;
            Node nodeC;
            for (nodeB = nodeA, nodeC = nodeB.pre; nodeC != tail && c.compare(nodeC.data, e) > 0; nodeB = nodeB.pre, nodeC = nodeC.pre) {
                nodeB.data = nodeC.data;
            }
            nodeB.data = e;
        }
    }

We also need classes that implement double ended queues:
The code is as follows:

//Method of double ended queue
    @Override
    public void addFirst(E element) {
        add(0, element);
    }

    @Override
    public void addLast(E element) {
        add(size, element);
    }

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

    @Override
    public E reomveLast() {
        return remove(size - 1);
    }

    @Override
    public E getFirst() {
        return get(0);
    }

    @Override
    public E getLast() {
        return get(size - 1);
    }

    //Stack method
    @Override
    public void push(E element) {
        addLast(element);
    }

    @Override
    public E pop() {
        return reomveLast();
    }

    @Override
    public E peek() {
        return getLast();
    }

    //Queue method
    @Override
    public void offer(E element) {
        addLast(element);
    }

    @Override
    public E poll() {
        return removeFirst();
    }

    @Override
    public E element() {
        return getFirst();
    }
}

On the definition of linked list node

public class ListNode {
    int val;
    ListNode next;
    ListNode() {}
    ListNode(int val) {
        this.val = val;
    }
    ListNode(int val, ListNode next) {
        this.val = val; this.next =next;
    }
}

Keywords: Algorithm data structure linked list

Added by ataylor20 on Mon, 17 Jan 2022 07:54:50 +0200