Data Structure - > Chain Storage Structure

Posted by p.utsav on Tue, 17 Sep 2019 12:06:30 +0200

The Difference between Chain Storage Structure and Sequential Storage Structure


1. The memory address of the linked list storage structure is not necessarily continuous, but the memory address of the sequential storage structure must be continuous.
2. Chain storage is suitable for inserting, deleting and updating elements more frequently, while sequential storage structure is suitable for frequent queries.


How to understand the chain storage structure:


Like detective stories, the author will not let you know the whole story, but through the information on the scene or some judgment, step by step to achieve the designated link, that is to say, every step is to confirm the information and the next step of guidance. (Exclude some complicated cases with clues.)


dynamic chain

1) node


In the sequential structure, each data element only needs to store data element information, but in the chain structure, it not only needs to store data element information, but also needs to store the storage address of its successor elements.
    
Unlike the sequential storage structure, which is similar to standing in a queue, it is equivalent to the random distribution of information in the memory space that everyone will take with them where they are going.

Therefore, in order to represent the logical relationship between each data element ai and its immediate successor element ai+1, besides storing its own information, data ai also needs to store a storage location indicating its immediate successor. We call the domain storing data element information as data domain, the domain storing direct successor location as pointer domain, and the information stored in pointer domain as needle or chain. The storage image of the data element ai composed of these two parts of information is called Node.

n nodes (storage image of ai) are linked into a linked list, which is a linear table (a1, a2, a3... The chain storage structure is called single linked list because there is only one pointer field in each node of the linked list.

head node

We call the first node in the list as the head node. In the chain storage structure, the head node can be virtual and real, which means that the real head node stores information and the location of the subsequent nodes. The virtual head node does not store information. Its pointer domain stores pointers to the first node. The virtual head node is not really big. Brother, Brother is the next point that the pointer of his pointer field stores (but the virtual header node is the real node!!!).

A red node is a virtual header node. It does not store information, but only the address of the next node.

2) Head and tail pointers

For linear tables, there must always be a head and tail, and linked lists are no exception. We call the storage location of the first node in the list header pointer, so the whole list must be stored from the header pointer. Every node after that is where the successor pointer of the previous node points. So imagine where the last node points?

Finally, it means that direct succession does not exist, so the pointer of the last node of the linear list is null.

Header pointer: Just a reference variable, just a pointer to store the address of the header node.

Tail pointer: Represents the location of the last node in the list and stores the address of the last node.

When traversing the list, the tail pointer can be used as the termination condition. When the tail pointer points to NULL, it means that the list traversal is completed. It also reduces the time complexity of the tail insertion method, which only requires the tail pointer to move to the next node.

3) Insertion and deletion of linked list

insert

There are three kinds of insertion: head insertion, tail insertion and general insertion.

1. Head insertion;

Head insertion has special circumstances. When the list is empty and a node is inserted, we can first give the next hop of the header node to the next hop of A, then give the address of A to the next hop of the header node, and then point back to A.

When the list is not empty, the next hop of the head node points to the next hop assigned to C, the next hop of the head node points to C, and the tail pointer does not need to move.

Tail insertion method

When the list is empty, we find that it is the same as head insertion.

When the list is not empty:

Give the next hop of the endpoint to the next hop of the new element B, and the next hop of the endpoint points to the new element B. Don't forget that the tail pointer needs to point to the new element.

(3) General insertion:

If we want to insert new element D between AB, we can regard A as a header node, so that we can use header interpolation to calculate the next hop of A to the next hop of D, and point the next hop of A to D.

delete

There are also three types of deletion: header deletion, tail deletion and general deletion.

First, delete:

 

Head deletion is also a special case. When there is only one element in the list, the next hop of the head node is pointed to empty, and both the head pointer and the tail pointer are pointed to the head node (virtual head node).

When the length of the list is longer than 1, delete B, just assign the next hop of B to the next hop of the head node, and then point the deleted element to empty.

Tail deletion

In tail deletion, we need to know the precursor of the endpoint (the list does not know where the end is, so we need to look from the beginning), point the next hop of the precursor B to null, and the tail pointer to the precursor.

(3) General deletion

First, traverse the list, find the precursor A that needs to delete element B, assign the next hop of B to the next hop of A. The next hop of A points directly to C, then there is no element pointing to B, but B also points to C, so the next hop of B points to null, and B disappears!

 

How to Realize Linear Tables with Linked Lists

 

Using linked lists to implement linear lists or inheriting the List interface. Next is how to use the linked list to realize the function operation of the linear table (this time the linear table is realized by using the structure of virtual header nodes) (LinkedList)

Single linked list code implementation:

1) First, we define the List interface.

package com.openlab.list;
/**
 * List Is the ultimate parent interface of a linear table
 * @author ABC
 * @param <E>
 */
public interface List<E> {
    
    /**
     * Get the number of elements in a linear table (the length of a linear table)
     * @return The Number of Effective Elements in a Linear Table
     */
    public int getSize();
    
    /**
     * Judging whether a linear table is empty
     * @return Whether the Boolean type value is empty or not
     */
    public boolean isEmpty();
    
    /**
     * Add element e to the index corner label specified in the linearity table
     * @param index Specified corner label 0 <= index <= size
     * @param e Elements to insert
     */
    public void add(int index,E e);
    
    /**
     * Insert an element in the header position of a linear table
     * @param e The element to be inserted is specified at corner zero
     */
    public void addFirst(E e);
    
    /**
     * Insert an element at the end of the table of the linearity
     * @param e The element to be inserted is specified at the corner size
     */
    public void addLast(E e);
    
    /**
     * Get the element at the specified index corner in the linearity table
     * @param index Specified corner label 0<=index<size
     * @return The element corresponding to the corner label
     */
    public E get(int index);
    
    /**
     * Get the elements of the header in a linear table
     * @return Table header element index=0
     */
    public E getFirst();
    
    /**
     * Getting the elements at the end of a linear table
     * @return The element index at the end of the table = size-1
     */
    public E getLast();
    
    /**
     * Modify the element at index specified in the linear table to a new element e
     * @param index Designated corners
     * @param e element fresh
     */
    public void set(int index,E e);
    
    /**
     * Determine whether the linear table contains the specified element e by default
     * @param e To determine whether elements exist or not
     * @return Boolean Type Value of Element Existence
     */
    public boolean contains(E e);
    
    /**
     * Get the label of the specified element e in the linearity table by default from before to after
     * @param e Data to be queried
     * @return Corner of data in linearity
     */
    public int find(E e);
    
    /**
     * Delete the element at the specified corner label in the linearity table and return
     * @param index Specified corner label 0<=index<size
     * @return Remove old elements
     */
    public E remove(int index);
    
    /**
     * Delete header elements from linear tables
     * @return Header element
     */
    public E removeFirst();
    
    /**
     * Delete tail elements in linear tables
     * @return Table tail element
     */
    public E removeLast();
    
    /**
     * Delete specified elements from a linear table
     * @param e Specified element
     */
    public void removeElement(E e);
    
    /**
     * Clear Linear Table
     */
    public void clear();
}

2) LinkedList implements List interface

The linked list is composed of many nodes. We can define a Node class to store the information of data items.

Since the node class we defined is not allowed to be manipulated by the outside world, the Node class is privatized and only allowed to be called in this class.

package com.openlab.lianbiao
 
public class LinkedList<E> implements List<E>{
 
	private class Node{  
    
		E data;  
		Node next; //Pointer field, same data field
		public Node() { 
			this(null,null);
		}
		public Node(E data,Node next) { 
			this.data=data;
			this.next=null;
		}
 
		@Override
		public String toString() {  
                                       
			return data.toString();
		}
	}
	
	private Node head; //Head Pointer to Head Node 
	private Node rear;  //End pointer to endpoint
	private int size;  //Number of recording elements
	
	public LinkedList() { 
		head = new Node();
		rear =head;
		size=0;
	}
	
	public LinkedList(E[] arr) {//Constructive Functions with Parameters
		this(); 
		for(E e:arr) {  
			addLast(e);
		}
	}
	
	@Override
	public int getSize() {  //Get the length of the linked list
		return size; 
	}
 
	@Override
	public boolean isEmputy() {//Sentence blank
		return size==0&&head.next==null;

3) Insert and delete

Insertion

@Override
	public void add(int index, E e) {
		if(index<0||index>size){ //Judging whether the adding position is legitimate
			throw new IllegalArgumentException("Illegal insertion of corners!");
		}
		Node n=new Node(e,null);//Create new nodes
		if(index==0){	//Head insertion
			n.next=head.next;//Store the address of the real header node in the pointer field of the new node
			head.next=n;//Replace the contents of the pointer field of the virtual header node with the address of the new node added
			if(size==0){//Are there any elements in the list?
				rear=n;//Point the tail pointer to the address of the new element
			}
		}else if(index==size){	//Tail insertion
			rear.next=n;
			rear=rear.next;
		}else{//Intermediate interpolation
			Node p=head;//Ab initio traversal
			for(int i=0;i<index;i++){
				p=p.next;//Find the precursor to delete the node
			}
			n.next=p.next;
			p.next=n;
		}
		size++;
		
	}

	@Override
	public void addFirst(E e) {//Head insertion
		add(0,e);
	}

	@Override
	public void addLast(E e) {//Tail insertion
		add(size,e);
	}
@Override
	public E get(int index) {
		if(index<0||index>=size){
			throw new IllegalArgumentException("Illegal corner marking!");
		}
		if(index==0){
			return head.next.data;
		}else if(index==size-1){
			return rear.data;
		}else{
			Node p=head;
			for(int i=0;i<=index;i++){
				p=p.next;
			}
			return p.data;
		}
	}

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

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

	@Override
	public void set(int index, E e) {
		if(index<0||index>=size){
			throw new IllegalArgumentException("Illegal modification of corner markers!");
		}
		if(index==0){
			head.next.data=e;
		}else if(index==size-1){
			rear.data=e;
		}else{
			Node p=head;
			for(int i=0;i<=index;i++){
				p=p.next;
			}
			p.data=e;
		}
	}
@Override
	public boolean contains(E e) { //Determine whether an element is included
		return find(e)!=-1; 
	}
@Override
	public int find(E e) {
		int index=-1;
		if(isEmpty()){
			return index;
		}
		Node p=head;
		while(p.next!=null){
			p=p.next;
			index++;
			if(p.data==e){
				return index;
			}
		}
		return -1;
	}

Delete


@Override
	public E remove(int index) { //Input Corner Deletes Corresponding Nodes
		if(index<0||index>=size){ //Judgment of legality
			throw new IllegalArgumentException("Deleting corner labels is illegal!");
		}
		E res=null;
		if(index==0){	//Header deletion
			Node p=head.next;
			res=p.data;
			head.next=p.next;
			p.next=null;
			p=null;
			if(size==1){ //In special cases, there is only one node left.
				rear=head; //Tail pointer forward
			}
		}else if(index==size-1){//Tail deletion
			Node p=head;
			res=rear.data;
			while(p.next!=rear){
				p=p.next;
			}
			p.next=null;
			rear=p;
		}else{
			Node p=head;
			for(int i=0;i<index;i++){
				p=p.next;
			}
			Node del=p.next;
			res=del.data;
			p.next=del.next;
			del.next=null;
			del=null;
		}
		size--;
		return res;
	}

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

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

	@Override
	public void removeElement(E e) { //find the element, return the corner mark and delete it
		int index=find(e);
		if(index==-1){
			throw new IllegalArgumentException("Element does not exist");
		}
		remove(index);
	}

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

 

(3) Rewrite toString

@Override
	public String toString() {
		StringBuilder sb=new StringBuilder();
		sb.append("LinkedList:size="+getSize()+"\n");
		if(isEmpty()){
			sb.append("[]");
		}else{
			sb.append("[");
			Node p=head;
			while(p.next!=null){
				p=p.next;
				if(p==rear){
					sb.append(p.data+"]");
				}else{
					sb.append(p.data+",");
				}
			}
			
		}
		return sb.toString();
	}