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
- 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;
}
}
}