Abstract data type

Keywords: Java queue stack arraylist LinkedList

1, Concept

  • Table List [interface, implemented]: a sequence (ordered set) composed of N elements of data elements A1, A2, A3... An. There is a precursor successor relationship between data elements. A special table of size 0 is called an empty table
  • Table implementation
    • Implementation of growable array: once the array is created, it is immutable, fixed capacity, continuous memory space, and supports index access
      • Vector: thread safe. You can specify the increment factor, which is twice the original by default
      • ArrayList: the thread is unsafe and the increment factor cannot be specified. The default value is 1.5 times of the original value
    • Implementation of double linked list: allocate space when necessary. The memory space is discontinuous and not easy to access by index
      • LinkedList
  • Compare ArrayList & LinkedList
    • Index access of ArrayList takes constant time, and insertion and deletion take linear time
    • LinkedList is not easy to index. Indexing takes linear time, while insertion and deletion take constant time
  • Stack ADT: stack is also a table. LIFO is last in first out. Insert and delete are restricted only at the end of the table
  • Queue ADT: a queue is also a table. FIFO is first in first out. Insertion is performed at the end of the table, and deletion is performed at the beginning of the table
  • Data type: a set of values and a set of operations on these values
  • Abstract data type ADT: a data type that can hide the internal data representation from the user
  • Object: an entity that can carry the value of a data type, as long as it contains three properties 👇
    • Status: values in data types
    • Identification: location in memory
    • Behavior: operations of data types
  • When the instance calls new()
    • Allocate memory space for new objects
    • Call the constructor to initialize the value in the object
    • Returns a reference to the object
  • The main function of data class oriented abstract data type is to simplify the organization and processing of data by encapsulating the representation of data
  • The function of the abstract data type of collection class is to simplify the operation of a group of data of the same type
  • API: separate use and implementation to achieve modular programming
  • Encapsulation: simplify implementation and isolate use case development, realize modular programming, limit the impact of modified code to local areas, improve the quality of software and promote code reuse
  • Inheritance: you can change the behavior of a class or add new functions to it without rewriting the whole class, which can effectively reuse code, but will destroy encapsulation. In fact, subclasses completely depend on the parent class, and the subclass code can access all instance variables of the parent class, which may distort the intention of the parent code

2, Implementation of abstract data type ADT

1. Linear table ArrayList based on array implementation

// Interface
public interface IList {
	
	void add(int e);
	
	void add(int index,int e);
	
	int get(int index);
	
	int remove();
	
	int remove(int index);
	
	boolean isEmpty();
	
	void clear();
	
	int size();
	
}
/**
 * ADT Abstract data type:
 * 1)data
 * 2)Data operation
 * 
 * Linear table based on array implementation
 */
public class NArrayList implements IList{
	
	/**
	 * Data: once the array is created, the length is immutable and the memory space is continuous
	 */
	private int[] data;
	
	/**
	 * Size: the number of current elements
	 */
	private int size;
	
	/**
	 * Create a list (linear table) with an initial capacity of 10
	 */
	public NArrayList() {
		this(10);
	}
	
	/**
	 * Create list
	 * 
	 * @param capacity Initial capacity
	 */
	public NArrayList(int capacity) {
		data = new int[capacity];
		size = 0;
	}


	// Time complexity O(1) constant level
	@Override
	public void add(int e) {
		if(size==data.length)
			// Capacity expansion
			grow();
		
		// add to
		data[size] = e;
		size++;
	}

	/**
	 * Capacity expansion
	 * Time complexity: O(n) linear level
	 */
	private void grow() {
		// Array size
		int oldLength = data.length;
		// Increment of array
		// Vector, double the original
		// ArrayList, 1.5 times the original
		int increment = oldLength >> 1;
		// New capacity
		int newLength = oldLength + increment;
		// Create a new array
		int[] tmp = new int[newLength];
		// Store the data in the original array into the new array
		for(int i=0;i<data.length;i++) {
			tmp[i] = data[i];
		}
		// assignment
		data = tmp;
		System.out.printf("Capacity expansion:%d --> %d\n",oldLength,newLength);
	}

	// Time complexity: complexity linear level O(n)
	@Override
	public void add(int index, int e) {

		if(data.length==size)
			grow();
		if(index>size)
			throw new ArrayIndexOutOfBoundsException(index);
		
		// move
		int last = size-1;
		for(int i=last;i>=index;i--) {
			data[i+1] = data [i];
		}
		data[index] = e;
		size++;
	}

	/**
	 * Get index location element
	 * Time complexity O(1) constant level
	 */
	@Override
	public int get(int index) {
		if(index<0 || index >=size)
			throw new ArrayIndexOutOfBoundsException(index);
		return data[index];
	}

	/**
	 * Delete last
	 * Time complexity O(1) constant level
	 */
	@Override
	public int remove() {
		if(isEmpty())
			throw new ArrayIndexOutOfBoundsException();
		// Delete the last and return
		return data[--size];
	}

	/**
	 * Delete index location
	 * Time complexity: O(n) linear level
	 */
	@Override
	public int remove(int index) {
		
		if(index<0 || index>=size) {
			throw new ArrayIndexOutOfBoundsException(index);
		}
		int tmp = data[index];
		// index + 1 ~ size - 1, move forward one bit
		for(int i=index+1;i<size;i++) {
			data[i-1] = data[i];
		}
		size--;
		return tmp;
		
	}

	/**
	 * Is the array empty
	 */
	@Override
	public boolean isEmpty() {
		return size == 0;
	}

	/**
	 * Empty array
	 */
	@Override
	public void clear() {
		size = 0;
	}

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

2. Implementation of bidirectional linked list LinkedList

/**
 * node
 */
public class Node {

	/**
	 * element
	 */
	int element;
	
	/*
	 * precursor
	 */
	Node prev;
	
	/*
	 * Successor
	 */
	Node next;
	
	/**
	 * Create a node
	 */
	public Node() {
	}

	/**
	 * Create a new node
	 * 
	 * @param element element
	 * @param prev 	  Precursor node
	 * @param next    Successor node
	 */
	public Node(int element, Node prev, Node next) {
		super();
		this.element = element;
		this.prev = prev;
		this.next = next;
	}
}

/**
 * Bidirectional linked list
 */
public class NLinkedList implements IList {

	/**
	 * size
	 */
	int size;

	/**
	 * Head pointer
	 */
	Node head;

	/**
	 * Tail pointer
	 */
	Node tail;

	/**
	 * Constructor
	 */
	public NLinkedList() {
		// Head node without precursor
		head = new Node();
		// Tail node has no successor
		tail = new Node();

		// End to end at the beginning
		head.next = tail;
		tail.prev = head;
	}

	/**
	 * Add a new node at the tail 
	 * Time complexity: constant level O(1)
	 */
	@Override
	public void add(int e) {
		// Insert the end, that is, the previous node of the tail pointer, followed by the tail node, and the precursor of the tail node is the precursor of the new node
		Node node = new Node(e, tail.prev, tail);

		// The predecessor node of the tail node is followed by the new node
		tail.prev.next = node;
		// The precursor of the tail node is the new node
		tail.prev = node;

		size++;
	}

	/**
	 * The time complexity of insertion and deletion of linked list is constant level O(1)
	 * Insert a new node at the index location
	 */
	@Override
	public void add(int index, int e) {
		// If the end is inserted
		if (index == size) {
			add(e);
		} 
		else {
			// Get index location node
			Node target = getNode(index);
			// Create a new node. The precursor is the precursor of the target node and the follower is the target node
			Node node = new Node(e, target.prev, target);
			// The predecessor of the target node is followed by a new node
			target.prev.next = node;
			// The precursor of the target node is a new node
			target.prev = node;

			size++;
		}
	}

	/**
	 * Get the time complexity O(n) of the inode
	 * 
	 * @param index Indexes
	 * @return Target node
	 */
	private Node getNode(int index) {
		if (index < 0 || index >= size)
			throw new ArrayIndexOutOfBoundsException();

		// 1. Look from front to back
		// Start from the head with the needle finger
		Node node = head;
		for (int i = 0; i <= index; i++) {
			node = node.next;
		}

		// 2. Look from the back
		// TODO

		return node;
	}

	/**
	 * Returns the node data at the index location
	 */
	@Override
	public int get(int index) {
		return getNode(index).element;
	}

	/**
	 * Delete the last node, the previous node of the tail pointer
	 */
	@Override
	public int remove() {
		if (isEmpty())
			throw new ArrayIndexOutOfBoundsException();

		// Last node
		Node node = tail.prev;

		// The tail pointer becomes the successor of the predecessor of the deleted node
		node.prev.next = tail;
		// The precursor of the deleted node becomes the precursor of the tail pointer
		tail.prev = node.prev;

		size--;
		// Returns the value of the deleted node
		return node.element;

	}

	/**
	 * Delete node at index location
	 */
	@Override
	public int remove(int index) {
		if (index < 0 || index >= size)
			throw new ArrayIndexOutOfBoundsException(index);

		// Target node
		Node node = getNode(index);

		node.next.prev = node.prev;
		node.prev.next = node.next;
		size--;

		return node.element;
	}

	/**
	 * Judge whether it is empty
	 */
	@Override
	public boolean isEmpty() {
		return size == 0;
	}

	/**
	 * Empty linked list
	 */
	@Override
	public void clear() {
		// end to end
		head.next = tail;
		tail.prev = head;

		size = 0;
	}

	/**
	 * Linked list size
	 */
	@Override
	public int size() {
		return size;
	}

}

3. Stack based on array

// Interface
public interface IStack {

	/**
	 * Stack pressing
	 * 
	 * @param e
	 * @return
	 */
	int push(int e);
	
	/**
	 * Read stack top element
	 * 
	 * @return
	 */
	int peek();
	
	/**
	 * Out of stack
	 * 
	 * @return
	 */
	int pop();
	
	/**
	 * size
	 * 
	 * @return
	 */
	int size();
	
	/**
	 * Is the stack empty
	 * 
	 * @return
	 */
	boolean isEmpty();
	
	/**
	 * empty
	 */
	void clear();
}

/**
 * Stack based on array implementation
 *
 */
public class NStack implements IStack {

	/**
	 * data
	 */
	private int[] data;

	/**
	 * Stack top identification
	 */
	private int top = -1;

	public NStack() {
		data = new int[10];
	}

	/**
	 * Stack pressing
	 */
	@Override
	public int push(int e) {
		if (data.length == top + 1)
			grow();

		data[++top] = e;
		return e;
	}

	/**
	 * Capacity expansion
	 */
	private void grow() {
		// Array size
		int oldLength = data.length;
		// Increment of array
		// Vector, double the original
		// ArrayList, 1.5 times the original
		int increment = oldLength >> 1;
		// New capacity
		int newLength = oldLength + increment;
		// Create a new array
		int[] tmp = new int[newLength];
		// Store the data in the original array into the new array
		for (int i = 0; i < data.length; i++) {
			tmp[i] = data[i];
		}
		// assignment
		data = tmp;
	}

	/**
	 * Read stack top element
	 */
	@Override
	public int peek() {
		if (isEmpty())
			throw new EmptyStackException();

		return data[top];
	}

	/**
	 * Out of stack
	 */
	@Override
	public int pop() {
		if (isEmpty())
			throw new EmptyStackException();

		return data[top--];
	}

	/**
	 * Number of elements
	 */
	@Override
	public int size() {
		return top + 1;
	}

	/**
	 * Is the stack empty
	 */
	@Override
	public boolean isEmpty() {
		return top == -1;
	}

	/**
	 * empty
	 */
	@Override
	public void clear() {
		top = -1;
	}

	@Override
	public String toString() {
		
		StringBuilder sb = new StringBuilder("[");
		for (int i=0;i<=top;i++) {
			sb.append(data[i]+",");
		}
		sb.deleteCharAt(sb.length()-1);
		sb.append("]");
		return sb.toString();
	}
}

4. Stack based on linked list

/**
 * Stack based on linked list
 *
 */
public class NStack implements IStack {

	// Stack top identification
	Node top;
	
	// size
	int size = 0;

	public NStack() {
		top = new Node();
	}

	@Override
	public int push(int e) {
		Node node = new Node(e, top, top.next);
		top = node;
		size++;
		return e;
	}

	@Override
	public int poll() {
		if (isEmpty())
			throw new EmptyStackException();
		int e = top.element;
		top = top.prev;
		top.next = null;
		size--;
		return e;
	}

	@Override
	public int peek() {
		if (isEmpty())
			throw new EmptyStackException();

		return top.element;
	}

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

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

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

	// Node inner class
	private static class Node {

		int element;
		Node prev;
		Node next;
		
		public Node() {
		}
		
		public Node(int element, Node prev, Node next) {
			super();
			this.element = element;
			this.prev = prev;
			this.next = next;
		}
	}
	
}

5. Array based queue ArrayDeque

// Interface
public interface IQueue {

	/**
	 * Join the team
	 * @param e
	 */
	void offer(int e);
	
	/**
	 * read
	 * @return
	 */
	int peek();
	
	/**
	 * Poll epoll
	 * @return
	 */
	int poll();
	
	int size();
	
	void clear();
	
	boolean isEmpty();
}

/**
 * Queue based on array implementation
 */
public class NQueue implements IQueue {

	/**
	 * data elements
	 */
	int[] data;

	/**
	 * Head pointer
	 */
	int head = 0;

	/**
	 * Tail pointer
	 */
	int tail = 0;

	public NQueue() {
		data = new int[10];
	}

	// operation
	@Override
	public void offer(int e) {
		if (data.length == tail + 1)
			grow();
		data[tail++] = e;
	}

	/*
	 * Capacity expansion
	 */
	private void grow() {
		// Array size
		int oldLength = data.length;
		// Increment of array
		// Vector, double the original
		// ArrayList, 1.5 times the original
		int increment = oldLength >> 1;
		// New capacity
		int newLength = oldLength + increment;
		// Create a new array
		int[] tmp = new int[newLength];
		// Store the data in the original array into the new array
		for (int i = 0; i < data.length; i++) {
			tmp[i] = data[i];
		}
		// assignment
		data = tmp;
	}

	@Override
	public int peek() {
		if (isEmpty())
			throw new EmptyStackException();
		return data[head];
	}

	@Override
	public int poll() {
		if (isEmpty())
			throw new EmptyStackException();
		return data[head++];
	}

	@Override
	public int size() {
		return tail - head;
	}

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

	@Override
	public boolean isEmpty() {
		return head == tail;
	}

	@Override
	public String toString() {

		StringBuilder sb = new StringBuilder("[");
		for (int i = head; i < tail; i++) {
			sb.append(data[i] + ",");
		}
		sb.deleteCharAt(sb.length() - 1);
		sb.append("]");
		return sb.toString();
	}
}

6. Queue LinkedList based on linked list

/**
 * Queue based on linked list
 *
 */
public class NQueue implements IQueue{
	
	// Header identification
	Node head;
	
	// Tail identification
	Node tail;
	
	//size
	int size = 0;

	public NQueue() {
		head = new Node();
		tail = new Node();
		head.next = tail;
		tail.prev = head;
	}
	
	@Override
	public void offer(int e) {
		Node node = new Node(e,tail.prev,tail);
		tail.prev.next = node;
		tail.prev = node;
		size++;
	}

	@Override
	public int peek() {
		if(isEmpty())
			throw new EmptyStackException();

		return head.next.element;
	}

	@Override
	public int poll() {
		if(isEmpty())
			throw new EmptyStackException();
		int e = head.next.element;
		head.next.next.prev = head;
		head.next = head.next.next;
		size--;
		return e;
	}

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

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

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

	// Node inner class
	private static class Node{
		
		int element;
		Node prev;
		Node next;
		
		public Node() {
		}
		
		public Node(int element, Node prev, Node next) {
			super();
			this.element = element;
			this.prev = prev;
			this.next = next;
		}
		
	}
}


Posted by phpcat on Fri, 03 Sep 2021 12:22:04 -0700