Deepening and improving JAVA foundation [Chinese]

data structure

Main contents:
Introduction to data structure
linear structure
tree structure

Introduction to data structure

1 what is a data structure

In short, a data structure is a container that stores data in a specific layout. This "layout" determines that a data structure is efficient for some operations and inefficient for others Therefore, we need to understand various data structures in order to select the most appropriate data structure when dealing with practical problems.

Data structure = logical structure + physical structure (sequence, chain, index, hash)
Logical structure: abstract relationship between data elements
Physical structure: (storage structure), the form of storage in computer memory

2 logical classification of data structure

Data structures are logically divided into three basic types:

2.1 linear structure

There is a one-to-one relationship between the elements in the data structure;
Common linear structures:
Linear table, stack, queue, string (one-dimensional array), etc.

2.2 tree structure

There is a one to many relationship between the elements in the data structure;
Common tree structure:
Binary tree, red black tree, B tree, Huffman tree, etc.

2.3 graphic structure

The elements in the data structure have many to many relationships;
Common graphic structures:
Directed graph, undirected graph, simple graph, etc.

2, Linear structure

1 stack structure

1.1 definition of stack

Stack is a storage structure that can only access data from one end and follows the "last in first out (LIFO)" principle.

1.2 implementation of stack container

1.2.1 create stack container

/**
* Custom stack container
*/
public class MyStack<E> {
	private Object[] arr;//Physical structure of storage elements
	private int stackLength = 4;//Default length of array
	private int size; //Record the number of elements in the stack container
	private int index = -1;//Pointer to the subscript position of the operation array
	/**
	* Determine whether the stack container is empty
	* @return
	*/
	public boolean empty(){
		return false;
	}
	/**
	* Get stack top element
	* @return
	*/
	public E pop(){
		return null;
	}
	/**
	* Add element to stack container
	* @param item
	* @return
	*/
	public E push(E item){
		return null;
	}
	public static void main(String[] args) {
	}
}

1.2.2 implementation of adding elements

 /**
     * Add element to stack container
     * @param item
     * @return
     */
    public E push(E item){
        //Initialize array
        this.capacity();
        //Adding elements to an array
        this.arr[++index]=item;
        //Number of record elements
        this.size++;
        return item;
    }

    /**
     * Initialize the array or expand the capacity of the array by 1.5 times the capacity
     */
    private void capacity(){
        //Data initialization
        if(this.arr == null){
            this.arr = new Object[this.stackLength];
        }
        //Expand the array by 1.5x
        if(this.size - (this.stackLength-1) >= 0){
            this.stackLength = this.stackLength + (this.stackLength >> 1);
            this.arr = Arrays.copyOf(this.arr,this.stackLength);
        }
    }

1.2.3 implementation acquisition element

/**
* Get stack top element
* @return
*/
public E pop(){
	//Throw an exception if there are no elements in the stack container
	if(this.index == -1){
	throw new EmptyStackException();
	}
	//Number of record elements
	this.size--;
	//Return stack top element
	return (E) this.arr[index--];
}

1.2.4 judge whether the stack container is empty

/**
* Determine whether the stack container is empty
* @return
*/
public boolean empty(){
	return this.size == 0;
}

2 linked list structure

2.1 definition of linked list structure

2.1.1 what is a linked list

The linked list structure is composed of many nodes, and each node contains two parts:

  • Data part: save the actual data of this node.
  • Address part: saves the address of the previous or next node.
2.1.2 linked list classification
  • Unidirectional linked list
  • Bidirectional linked list
  • Bidirectional circular linked list

2.1.3 characteristics of linked list

  • The location of nodes in memory is arbitrary, that is, logically adjacent data elements are not necessarily adjacent physically.
  • When accessing, you can only enter the linked list through the head or tail pointer, and scan the other nodes backward or forward through the pointer field of each node, so the time spent looking for the first node and the last node is different.

Advantages and disadvantages of linked list:

  • Advantages: the number of data elements can be expanded, inserted, deleted and other operations freely. There is no need to move the data, just modify the link pointer, and the modification efficiency is high.
  • Disadvantages: sequential access must be adopted, that is, when accessing data elements, they can only be accessed in the order of linked list, and the efficiency of access nodes is low.

2.2 one way linked list structure

2.2.1 definition of one-way linked list

One way linked list (single linked list) is a kind of linked list. Its characteristic is that the link direction of the linked list is one-way. The access to the linked list should be read sequentially from the head.

2.2.2 realize one-way linked list

2.2.2.1 create linked list interface
/**
* Method API definition of accessing elements based on linked list structure
* @param <E>
*/
public interface MyList<E> {
	void add(E element);
	E get(int index);
	int size();
	E remove(int index);
}
2.2.2.2 create a one-way linked list class
/**
* Container for element access based on one-way linked list
* @param <E>
*/
public class MySinglyLinkedList<E> implements MyList<E> {
	/**
	* Add elements to the linked list
	* @param element
	*/
	@Override
	public void add(E element) {
	}
	/**
	* Get elements based on their location
	* @param index
	* @return
	*/
	@Override
	public E get(int index) {
		return null;
	}
	/**
	* Get the number of elements
	* @return
	*/
	@Override
	public int size() {
		return 0;
	}
	/**
	* Delete elements based on their location
	* @param index
	* @return
	*/
	@Override
	public E remove(int index) {
		return null;
	}
	public static void main(String[] args) {
	}
}
2.2.2.3 create node class
/**
* Define node objects in a one-way linked list
*/
class Node<E>{
	private E item;//Storage element
	private Node next;//Stores the address of the next node object
	Node(E item,Node next){
		this.item = item;
		this.next = next;
	}
}
2.2.2.4 method of adding elements
private Node head;//Store the header node in the linked list.
private int size;//Record the number of elements.
	/**
	* Add elements to the linked list
	* @param element
	*/
	@Override
	public void add(E element) {
		//Create node
		Node<E> node = new Node<>(element,null);
		//Find tail node
		Node tail = getTail();
		//Hooking of nodes
		if(tail == null)
			this.head = node;
		else
			tail.next = node;
		//Number of record elements
		this.size++;
	}
	/**
	* Tail finding node
	*/
	private Node getTail(){
		//Whether the header node exists
		if(this.head == null){
			return null;
	}
	//Find tail node
	Node node = this.head;
	while(true){
		if(node.next == null)break;
		node = node.next;//Move the pointer to the next node
	}
	return node;
}
2.2.2.5 method of obtaining elements
/**
* Get elements based on their location
* @param index
* @return
*/
@Override
public E get(int index) {
		//Verify the validity of Index
		this.checkIndex(index);
		//Get the specified node according to the location
		Node<E> node = this.getNode(index);
		//Returns the elements in this node
		return node.item;
	}
	/**
	* Verify the Index
	*/
	private void checkIndex(int index){
		if(!(index >= 0 && index < this.size)){
			throw new IndexOutOfBoundsException("Index: "+index+" Size:"+this.size);
		}
	}
	/**
	* Get nodes based on location
	*/
	private Node getNode(int index){
		Node<E> node = this.head;
		for(int i=0;i<index;i++){
			node = node.next;
		}
	return node;
}
2.2.2.6 implement the method of deleting elements
/**
* Delete elements based on their location
* @param index
* @return
*/
@Override
public E remove(int index) {
	//Verify the validity of Index
	this.checkIndex(index);
	//Locate the node object by location
	Node<E> node = this.getNode(index);
	//Gets the element in the node object
	E item = node.item;
	//Remove the node object from the one-way linked list
	//Judge whether the currently deleted node is a header node
	if(this.head == node){
		this.head = node.next;
	}else{
		Node<E> temp = this.head;
		for(int i=0;i< index - 1;i++){
			temp = temp.next;
		}
		temp.next = node.next;
	}
	node.next = null;
	//Number of record elements
	this.size--;
	//Return the element to
	return item;
}
2.2.2.7 get the number of elements
/**
* Get the number of elements
* @return
*/
@Override
public int size() {
	return this.size;
}

2.3 two way linked list structure

2.3.1 definition of bidirectional linked list

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 precursor and direct successor respectively.

2.3.2 realize bidirectional linked list

2.3.2.1 create a two-way linked list class
/**
* Container for element access based on bidirectional linked list
* @param <E>
*/
public class MyDoublyLinkedList<E> implements MyList<E> {
	/**
	* Method of adding elements to bidirectional linked list
	* @param element
	*/
	@Override
	public void add(E element) {
	}
	/**
	* Gets the element according to the specified location
	* @param index
	* @return
	*/
	@Override
	public E get(int index) {
		return null;
	}
	/**
	* Returns the number of elements
	* @return
	*/
	@Override
	public int size() {
		return 0;
	}
	/**
	* Deletes the element according to the specified location
	* @param index
	* @return
	*/
	@Override
	public E remove(int index) {
		return null;
	}
	public static void main(String[] args) {
	}
}
2.3.2.2 create node class
/**
* Defines the node object of the bidirectional linked list
*/
class Node<E>{
	E item;//Record element
	Node<E> prev;//Record previous node object
	Node<E> next;//Record the next node object
	Node(Node<E> prev,E item,Node<E> next){
		this.prev = prev;
		this.item = item;
		this.next = next;
	}
}
2.3.2.3 implement the method of adding elements
private Node head; //Record header node
private Node tail; //Record tail node
private int size; //Number of record elements
/**
* Method of adding elements to bidirectional linked list
* @param element
*/
@Override
public void add(E element) {
this.linkLast(element);
}
/**
* Add the node object to the tail of the bidirectional linked list
*/
private void linkLast(E element){
	//Get tail node
	Node t = this.tail;
	//Create node object
	Node<E> node = new Node<>(t,element,null);
	//Define the new node as the tail node
	this.tail = node;
	if(t == null){
		this.head = node;
	}else{
		t.next = node;
	}
		this.size++;
}
2.3.2.4 method of obtaining elements
/**
* Gets the element according to the specified location
* @param index
* @return
*/
@Override
public E get(int index) {
	//Check the validity of Index
	this.checkIndex(index);
	//Find node objects by location
	Node<E> node = this.getNode(index);
	return node.item;
}
/**
* Verify the validity of Index
*/
private void checkIndex(int index){
	if(!(index >= 0 && index < this.size)){
		throw new IndexOutOfBoundsException("Index: "+index+" Size:"+size);
		}
	}
	/**
	* Gets the specified node object according to the location
	*/
	private Node getNode(int index){
	//Judge which node the current position is closer to the head or tail
	if(index < (this.size >> 1)){
	Node node = this.head;
	for(int i=0;i<index;i++){
		node = node.next;
	}
		return node;
	}else{
		Node node = this.tail;
		for(int i=this.size-1;i>index;i--){
			node = node.prev;
	}
	return node;
	}
}
2.3.2.5 implement the method of deleting elements
/**
* Deletes the element according to the specified location
* @param index
* @return
*/
@Override
public E remove(int index) {
	//Verify the validity of the Index
	this.checkIndex(index);
	//Gets the node object according to the specified location
	Node<E> node = this.getNode(index);
	//Gets the element in the node object
	E item = node.item;
	//Judge whether the current node is a header node
	if(node.prev ==null){
	this.head = node.next;
	}else{
	//Complete the connection between the direct predecessor node of the current node and the direct successor node of the current node
	node.prev.next = node.next;
	}
	//Judge whether the current node is the tail node
	if(node.next == null){
		this.tail = node.prev;
	}else{
		//Complete the connection between the direct successor node of the current node and the direct predecessor node of the current node
		node.next.prev = node.prev;
	}
	//The current node disconnects from its direct predecessor node
	node.prev = null;
	//The current node disconnects from its immediate successor
	node.next = null;
	node.item = null;
	//Number of record elements
	this.size--;
	return item;
}
2.3.2.6 get the number of elements
/**
* Returns the number of elements
* @return
*/
@Override
public int size() {
	return this.size;
}
2.3.2.7 add elements to the header of the bidirectional linked list
/**
* Add an element to the head of a two-way linked list
*
*/
public void addFirst(E element){
this.linkFirst(element);
}
/**
* Add elements to the head of the linked list
*
*/
private void linkFirst(E element){
	//Get header node object
	Node head = this.head;
	Node node = new Node(null,element,head);
	//Define the new node as the head node
	this.head = node;
	//Judge whether there is a node in the current linked list. If not, the node is both the head node and the tail node
	if(head == null){
		this.tail = node;
	}else{
		head.prev = node;
	}
	//Number of record elements
	this.size++;
}
2.3.2.8 add elements to the tail of the bidirectional linked list
/**
* Add elements at the end of the linked list
* @param element
*/
public void addLast(E element){
	this.linkLast(element);
}

3, Tree structure

1 Introduction to tree structure

Tree structure is a nonlinear storage structure, which stores a set of data elements with "one to many" relationship.

2 related terms of tree

2.1 node

Each data element stored in a tree structure is called a "node".

2.2 degree of node

The number of subtrees owned by a node.

2.3 degree of tree

The maximum number of layers of nodes in the tree.

2.4 leaf node

A node with a degree of 0 is also called a terminal node.

2.5 branch node

Nodes whose degree is not 0 are also called non terminal nodes or internal nodes.

2.6 children

It can also be called subtree or child node, which represents the direct node under the current node.

2.7 parents

It can also be called parent node, which represents the direct upper node of the current node.

2.8 root node

Nodes without parent nodes. There is only one root node in a tree structure.

2.9 ancestry

From all nodes above the current node.

2.10 descendants

All nodes under the current node.

2.11 brother

Children of the same parents.

3 Introduction to binary tree

Binary Tree is an important type of tree structure. The data structure abstracted from many practical problems is often in the form of Binary Tree. Even a general tree can be simply transformed into Binary Tree, and the storage structure and algorithm of Binary Tree are relatively simple, so Binary Tree is particularly important. The characteristic of Binary Tree is that each node can only have two subtrees at most, which can be divided into left and right.

3.1 binary tree classification

3.1.1 full binary tree

Full binary tree means that all nodes on each layer except the last layer have two child nodes.

3.1.2 complete binary tree

For a complete binary tree, except that the last layer may be dissatisfied, all other layers reach the maximum number of nodes in this layer. If the last layer is dissatisfied, all nodes in this layer are in the left row.

3.2 binary tree traversal

Traversal method of binary tree:

  • Preorder traversal: root left right
  • Middle order traversal: left root right
  • Post order traversal: left right root
  • Sequence traversal: traversal layer by layer from top to bottom

3.2.1 preorder traversal

Preorder traversal order: root left right

3.2.2 middle order traversal

Middle order traversal order: left root right

3.2.3 post order traversal

Post order traversal order: left right root

3.2.4 sequence traversal

Sequence traversal sequence:
Starting from the root node, access the left and right child nodes in turn, and then start from the left and right children to their child nodes in turn until the node is accessed.

3.3 binary tree sorting

3.3.1 binary tree sorting analysis

Using the binary tree structure and traversal mode, the element sorting processing based on binary tree can be realized.

3.3.2 implementation of binary tree sorting

3.3.2.1 create a binary tree sorter class
/**
* A sorter for element sorting based on binary tree structure
*/
public class BinaryTreeSort<E extends Integer> {
	/**
	* Add element to sorter
	*/
	public void add(E element){
	}
	/**
	* Sort elements
	*/
	public void sort(){
	}
	public static void main(String[] args) {
	}
}
3.3.2.2 create node class
/**
* Define node class
*/
class Node<E extends Integer>{
	private E item;//Store elements
	private Node left;//Address of left subtree
	private Node right;//Address for storing right subtree
	Node(E item){
	this.item = item;
}
	/**
	* Add node
	*/
	public void addNode(Node node){
	//Complete the judgment between the elements in the new node and the elements in the current node
	//If the elements in the new node are smaller than those in the current node, the new node will be placed in the left subtree of the current node.
	if(node.item.intValue() < this.item.intValue()){
	if(this.left == null)
		this.left = node;
	else
		this.left.addNode(node);
	}else{
	//If the elements in the new node are larger than those in the current node, the new node will be placed in the current node
	In the right subtree of.
	if(this.right == null)
		this.right = node;
	else
		this.right.addNode(node);
	}
}
/**
* Traversing binary tree using middle order
*/
public void inorderTraversal(){
	//Find the node on the far left
		if(this.left != null)this.left.inorderTraversal();
		System.out.println(this.item);
		if(this.right != null)this.right.inorderTraversal();
	}
}
3.3.2.3 implement the method of adding elements to the sorter
/**
* Add element to sorter
*/
public void add(E element){
//Instantiate node object
Node<E> node = new Node<>(element);
//Judge whether there is a root node in the current binary tree. If there is no new node, then there is no root
if(this.root == null)
	this.root = node;
else
	this.root.addNode(node);
}
3.3.2.4 implement the sorting method in the sorter
/**
* Sort elements
*/
public void sort(){
	//Judge whether the root node is empty
	if(this.root == null)return ;
	this.root.inorderTraversal();
}

4 custom tree structure container

4.1 tree structure definition

The parent node of the current node can be found
The child nodes of the current node can be found
The sibling node of the current node can be found
The ancestor node of the current node can be found
Can find the descendants of the current node

4.2 user defined tree structure analysis

4.3 implement custom tree structure container

4.3.1 create a tree structure container class

/**
* Container for element storage based on tree structure
*/
public class MyTree<E> {
	/**
	* Add element to container
	*/
	public void add(E parent,E item){
	}
	/**
	* Gets the parent node of the current node
	*/
	public E getParent(E item){
		return null;
	}
	/**
	* Get the child nodes of the current node
	*/
	public List<E> getChild(E item){
		return null;
	}
	/**
	* Get the sibling node of the current node
	*/
	public List<E> getBrother(E item){
		return null;
	}
	/**
	* Get the ancestor node of the current node
	*/
	public List<E> getForefathers(E item){
		return null;
	}
	/**
	* Get the descendants of the current node
	*/
	public List<E> getGrandChildren(E item){
		return null;
	}
	public static void main(String[] args) {
	}
}

4.3.2 method of adding elements

private Map<E,E> map = new HashMap<>();//String--->String
private Map<E,List<E>> map2 = new HashMap<>();//String ---->List
	/**
	* Add element to container
	*/
	public void add(E parent,E item){
	//Complete the mapping between single nodes
	this.map.put(item,parent);
	//Complete the mapping between multiple nodes
	List<E> list = this.map2.get(parent);
	//Judge whether there are child nodes under the current node. If not, create a new List
	if(list == null){
		list = new ArrayList<>();
		this.map2.put(parent,list);
	}
	list.add(item);
}

4.3.3 get the parent and child nodes of the current node

4.3.3.1 get parent node

/**
* Gets the parent node of the current node
*/
public E getParent(E item){
	return this.map.get(item);
}
4.3.3.2 obtaining child nodes
/**
* Get the child nodes of the current node
*/
public List<E> getChild(E item){
	return this.map2.get(item);
}

4.3.4 get the sibling node of the current node

/**
* Get the sibling node of the current node
*/
public List<E> getBrother(E item){
	//Gets the parent node of the current node
	E parent = this.getParent(item);
	//Gets all the child nodes of the current parent node
	List<E> list = this.getChild(parent);
	List<E> brother = new ArrayList<>();
		if(list != null){
			brother.addAll(list);
			brother.remove(item);
		}
	return brother;
}

4.3.5 get the ancestor node of the current node

/**
* Get the ancestor node of the current node
*/
public List<E> getForefathers(E item){
	//Gets the parent node of the current node
	E parent = this.getParent(item);
	//End recursive boundary condition
	if(parent == null){
		return new ArrayList<>();
	}
	//Call recursively to get the parent node of the current node's parent node again
	List<E> list = this.getForefathers(parent);
	//Add all node elements recursively to the returned List
	list.add(parent);
	return list;
}

4.3.6 get the descendant nodes of the current node

/**
* Get the descendants of the current node
*/
public List<E> getGrandChildren(E item){
	//Store elements in all descendant nodes
	List<E> list = new ArrayList<>();
	//Get the child nodes of the current node
	List<E> child = this.getChild(item);
	//End recursive boundary condition
	if (child == null){
		return list;
	}
	//Traverse child nodes
	for(int i=0;i<child.size();i++){
		//Gets the element in the node
		E ele = child.get(i);
		List<E> temp = this.getGrandChildren(ele);
		list.add(ele);
		list.addAll(temp);
	}
	return list;
}

4.3.7 testing custom containers

public static void main(String[] args) {
	//Instantiate container
	MyTree<String> myTree = new MyTree<>();
	//Add element
	myTree.add("root","biology");
	myTree.add("biology","Botany");
	myTree.add("biology","animal");
	myTree.add("biology","Fungi");
	myTree.add("animal","vertebrate");
	myTree.add("animal","Chordate animal");
	myTree.add("animal","coelenterate");
	myTree.add("vertebrate","mammal");
	myTree.add("vertebrate","fish");
	myTree.add("mammal","cat");
	myTree.add("mammal","cattle");
	myTree.add("mammal","people");
	System.out.println("---------Get parent node---------");
	String parent = myTree.getParent("fish");
	System.out.println(parent);
	System.out.println("---------Get child nodes---------");
	
	List<String> child= myTree.getChild("animal");
	for(int i=0;i<child.size();i++){
		System.out.println(child.get(i));
	}
	System.out.println("---------Get sibling node---------");
	List<String> brother = myTree.getBrother("vertebrate");
	for(int i=0;i<brother.size();i++){
		System.out.println(brother.get(i));
	}
	System.out.println("---------Get ancestor node---------");
	List<String> foreFathers = myTree.getForefathers("people");
	for(int i=0;i<foreFathers.size();i++){
		System.out.println(foreFathers.get(i));
	}
	System.out.println("---------Get descendant node---------");
	List<String> grandChildren = myTree.getGrandChildren("root");
	for(int i =0;i<grandChildren.size();i++){
		System.out.println(grandChildren.get(i));
	}
}

Keywords: Java Algorithm data structure

Added by ineedhelp on Mon, 21 Feb 2022 06:29:31 +0200