(reproduced) Summary of common data structures and algorithms --- data structures

A data structure is a collection that organizes data together in some form that not only stores the data but also supports operations to access and manipulate the data. An algorithm is a clearly specified collection of simple instructions to be followed in order to solve a problem. The following is the commonly used data structure and algorithm related content organized by myself. If there is any error, please point out.

In order to facilitate the description, the code parts involved in this article are all written in Java language. In fact, Java itself provides better implementations for several common data structures, such as linear tables, stacks, queues, etc., which we often use. Java collection framework, you can read this article if you need it. Java - Collections Framework fully resolved

一、线性表
  1.数组实现
  2.链表
二、栈与队列
三、树与二叉树
  1.树
  2.二叉树基本概念
  3.二叉查找树 4.平衡二叉树 5.红黑树 四、图 五、总结

1. Linear table

A linear list is the most common and simplest type of data structure, which is a finite sequence of n data elements.

There are generally two ways to implement a linear table. One is to use an array to store the elements of the linear table, that is, to use a group of consecutive storage units to store the data elements of the linear table in turn. The other is to use a linked list to store the elements of the linear list, that is, use a set of arbitrary storage units to store the data elements of the linear list (the storage units can be continuous or discontinuous).

array implementation

An array is a fixed-size data structure, and all operations on linear tables can be implemented through arrays. Although the size of an array cannot be changed once it is created, when the array can no longer store new elements in the linear table, we can create a new larger array to replace the current array. This makes it possible to implement dynamic data structures using arrays.

  • Code 1 creates a larger array to replace the current array
int[] oldArray = new int[10];

int[] newArray = new int[20]; for (int i = 0; i < oldArray.length; i++) { newArray[i] = oldArray[i]; } // 也可以使用System.arraycopy方法来实现数组间的复制 // System.arraycopy(oldArray, 0, newArray, 0, oldArray.length); oldArray = newArray;
  • Code 2 adds element e at array position index
//oldArray 表示当前存储元素的数组
//size 表示当前元素个数
public void add(int index, int e) { if (index > size || index < 0) { System.out.println("位置不合法..."); } //如果数组已经满了 就扩容 if (size >= oldArray.length) { // 扩容函数可参考代码1 } for (int i = size - 1; i >= index; i--) { oldArray[i + 1] = oldArray[i]; } //将数组elementData从位置index的所有元素往后移一位 // System.arraycopy(oldArray, index, oldArray, index + 1,size - index); oldArray[index] = e; size++; }

The above briefly writes two typical functions for arrays to implement linear tables. For details, we can refer to the source code of the ArrayList collection class in Java. The advantage of the linear table implemented by the array is that the elements can be accessed or modified by subscripting, which is more efficient. The main disadvantage is that the cost of insertion and deletion is relatively large. For example, when an element is inserted before the first position, all the The element is moved back one position. In order to improve the efficiency of adding or removing elements at any position, a chain structure can be used to implement a linear table.

linked list

A linked list is a non-consecutive, non-sequential storage structure on a physical storage unit, and the logical order of data elements is achieved through the link order of pointers in the linked list. A linked list consists of a series of nodes that do not have to be connected in memory. Each node consists of the data part Data and the chain part Next, and Next points to the next node, so that when adding or deleting, only the next point of the relevant node needs to be changed, which is very efficient.


The structure of a singly linked list

The following mainly uses code to show some basic operations of linked lists. It should be noted that the single linked list is mainly used here as an example, and the double linked list and circular linked list are not considered for the time being.

  • Code 3 node of linked list
class Node<E> {

    E item;
    Node<E> next;

    //构造函数
    Node(E element) { this.item = element; this.next = null; } }
  • Code 4 After the node is defined, the head node and tail node are generally initialized before use
//头节点和尾节点都为空 链表为空
Node<E> head = null;
Node<E> tail = null;
  • Code 5 empty linked list to create a new node
//创建一个新的节点 并让head指向此节点
head = new Node("nodedata1");

//让尾节点也指向此节点
tail = head;
  • Code 6 Append a node to the linked list
//创建新节点 同时和最后一个节点连接起来
tail.next = new Node("node1data2");

//尾节点指向新的节点 tail = tail.next;
  • Code 7 traverse the linked list sequentially
Node<String> current = head;
while (current != null) {
    System.out.println(current.item); current = current.next; }
  • Code 8 traverse the linked list in reverse order
static void printListRev(Node<String> head) { //倒序遍历链表主要用了递归的思想 if (head != null) { printListRev(head.next); System.out.println(head.item); } }
  • code singly linked list reversal
//单链表反转 主要是逐一改变两个节点间的链接关系来完成
static Node<String> revList(Node<String> head) {

    if (head == null) {
        return null;
    }

    Node<String> nodeResult = null; Node<String> nodePre = null; Node<String> current = head; while (current != null) { Node<String> nodeNext = current.next; if (nodeNext == null) { nodeResult = current; } current.next = nodePre; nodePre = current; current = nodeNext; } return nodeResult; }

The above paragraphs of code mainly show several basic operations of linked lists, and there are many operations such as getting specified elements, removing elements, etc. that you can complete by yourself. When writing these codes, you must clarify the relationship between nodes, so as not to Error-prone.

There are other ways to implement linked list, the common ones are circular singly linked list, doubly linked list, circular doubly linked list. A circular singly linked list  is mainly that the last node of the linked list points to the first node, forming a chain as a whole. The doubly linked list  mainly consists of two pointer parts in the node, one points to the predecessor element and the other points to the successor element. The implementation of the LinkedList collection class in JDK is a doubly linked list.  A circular doubly linked list  is where the last node points to the first node.

2. Stacks and Queues

Stacks and queues are also relatively common data structures. They are special linear lists, because for stacks, elements can only be accessed, inserted, and deleted at the top of the stack. For queues, elements can only be inserted from the end of the queue. Access and delete from the head of the queue.

stack

The stack is a table that restricts insertion and deletion to only one position. This position is the end of the table, called the top of the stack. The basic operations on the stack include push (into the stack) and pop (out of the stack). The former is equivalent to inserting , which is equivalent to removing the last element. The stack is sometimes called the LIFO (Last In First Out) table, which means last in first out.


stack model

Let's take a look at a classic topic to deepen our understanding of the stack.


A classic topic about stacks

The answer in the above picture is C, and the principle can be thought about.

Because a stack is also a table, any method that implements a table can implement a stack. We open the source code of the class Stack in the JDK, and we can see that it inherits the class Vector. Of course, Stack is a container class before Java2, and now we can use LinkedList to perform all operations on the stack.

queue

A queue is a special kind of linear table. The special feature is that it only allows deletion operations at the front of the table and insertion operations at the rear of the table. Like stacks, queues are an operation subject to Linear table of constraints. The end that performs the insert operation is called the tail of the queue, and the end that performs the delete operation is called the head of the queue.


Queue diagram

We can use a linked list to implement a queue. The following code simply shows the use of LinkedList to implement the queue class.

  • Code 9 Simple implementation of the queue class
public class MyQueue<E> {

    private LinkedList<E> list = new LinkedList<>(); // 入队 public void enqueue(E e) { list.addLast(e); } // 出队 public E dequeue() { return list.removeFirst(); } }

A normal queue is a first-in, first-out data structure, while in a priority queue, elements are assigned priorities. When accessing elements, the element with the highest priority is removed first. There are still many applications of priority queues in life. For example, the emergency department of a hospital assigns priority to patients, and the patients with the highest priority are treated first. In the Java collection framework, the class PriorityQueue is the implementation class of the priority queue. For details, you can read the source code.

3. Trees and Binary Trees

Tree structure is a kind of very important nonlinear data structure, among which tree and binary tree are the most commonly used. Before introducing the binary tree, let's briefly understand the related content of the tree.

Tree

A tree  is a set of hierarchical relationships composed of n (n>=1) finite nodes. It has the following characteristics: each node has zero or more child nodes; a node without a parent node is called a root node   parent node  ; in addition to the root node, each child node can be divided into multiple disjoint subtrees.


tree structure

Basic concepts of binary tree

  • definition

A binary tree is a tree structure with at most two subtrees per node. Usually subtrees are called "left subtree" and "right subtree". Binary trees are often used to implement binary search trees and binary heaps.

  • Relevant properties

Each node of a binary tree has at most 2 subtrees (there is no node with a degree greater than 2). The subtrees of a binary tree are divided into left and right, and the order cannot be reversed.

The i-th level of a binary tree has at most 2^(i-1) nodes; a binary tree of depth k has at most 2^k-1 nodes.

A binary tree with depth k and 2^k-1 nodes is called a  full binary tree  ;

 A binary tree with depth k and n nodes is called a complete binary tree  if and only if each node corresponds to a node numbered from 1 to n in a full binary tree of depth k .


 
  • Three traversal methods

In some applications of binary trees, it is often required to search for nodes with certain characteristics in the tree, or to perform some kind of processing on all nodes in the tree, which involves traversing the binary tree. A binary tree is mainly composed of three basic units, the root node, the left subtree and the right subtree. If it is limited to the left and the right, then according to the order of the traversal of the three parts, it can be divided into three types: pre-order traversal, in-order traversal and subsequent traversal.

(1)  Preorder traversal  If the binary tree is empty, no operation is performed, otherwise, the root node is visited first, then the left subtree is traversed in preorder, and finally the right subtree is traversed in preorder. (2)  Inorder traversal  If the binary tree is empty, no operation is performed, otherwise, the left subtree is traversed in inorder first, then the root node is visited, and finally the right subtree is traversed in inorder. (3)  Post-order traversal  If the binary tree is empty, no operation is performed, otherwise, the left subtree is traversed sequentially to access the root node, and then the right subtree is traversed in post-order, and finally the root node is accessed.


Write three traversal results for a given binary tree
  • Difference Between Tree and Binary Tree

(1) Each node of a binary tree has at most 2 child nodes, and the tree is unlimited. (2) The subtree of a node in a binary tree is divided into a left subtree and a right subtree. Even if a node has only one subtree, it is necessary to indicate whether the subtree is a left subtree or a right subtree, that is, the binary tree is ordered. (3) A tree can never be empty, it has at least one node, and a binary tree can be empty.

Above we mainly introduced the related concepts of binary tree. Next, we will start with binary search tree, introduce several common types of binary tree, and implement the previous theoretical part in code.

binary search tree

  • definition

A binary search tree is a binary sorted tree, also known as a binary search tree. A binary search tree is either an empty tree, or a binary tree with the following properties: (1) If the left subtree is not empty, the value of all nodes in the left subtree is less than the value of its root node; ( 2) If the right subtree is not empty, the value of all nodes on the right subtree is greater than the value of its root node; (3) The left and right subtrees are also binary sorting trees; (4) There is no key Nodes with equal values.


The construction process of a typical binary search tree
  • performance analysis

For the binary search tree, when the given values ​​are the same but the order is different, the constructed binary search tree shape is different, let's see an example below.


Different forms of balanced binary trees have different ASLs

It can be seen that the average search length of a binary search tree with n nodes is related to the shape of the tree. In the worst case, when the keywords inserted successively are in order, the binary search tree formed is transformed into a single tree, the depth of the tree is n, and its average search length (n+1)/2 (same as sequential search) , the best case is that the shape of the binary search tree is the same as that of the decision tree for split-half search, and its average search length is proportional to log2(n). On average, the average search length of a binary search tree and logn are of the same order of magnitude, so in order to obtain better performance, it is usually necessary to "balance" the construction process of the binary search tree, and then we will introduce the balanced binary tree. and red-black trees, both of which make the search tree O(log(n)) high.

  • Code 10 Nodes of a binary tree
class TreeNode<E> {

    E element;
    TreeNode<E> left;
    TreeNode<E> right;

    public TreeNode(E e) { element = e; } }

All three traversals of a binary search tree can be implemented directly recursively:

  • Code 12 Preorder Traversal
protected void preorder(TreeNode<E> root) { if (root == null) return; System.out.println(root.element + " "); preorder(root.left); preorder(root.right); }
  • Code 13 In-order traversal
protected void inorder(TreeNode<E> root) { if (root == null) return; inorder(root.left); System.out.println(root.element + " "); inorder(root.right); }
  • Code 14 Post-order traversal
protected void postorder(TreeNode<E> root) { if (root == null) return; postorder(root.left); postorder(root.right); System.out.println(root.element + " "); }
  • Listing 15 Simple implementation of binary search tree
/**
 * @author JackalTsc
 */
public class MyBinSearchTree<E extends Comparable<E>> { // 根 private TreeNode<E> root; // 默认构造函数 public MyBinSearchTree() { } // 二叉查找树的搜索 public boolean search(E e) { TreeNode<E> current = root; while (current != null) { if (e.compareTo(current.element) < 0) { current = current.left; } else if (e.compareTo(current.element) > 0) { current = current.right; } else { return true; } } return false; } // 二叉查找树的插入 public boolean insert(E e) { // 如果之前是空二叉树 插入的元素就作为根节点 if (root == null) { root = createNewNode(e); } else { // 否则就从根节点开始遍历 直到找到合适的父节点 TreeNode<E> parent = null; TreeNode<E> current = root; while (current != null) { if (e.compareTo(current.element) < 0) { parent = current; current = current.left; } else if (e.compareTo(current.element) > 0) { parent = current; current = current.right; } else { return false; } } // 插入 if (e.compareTo(parent.element) < 0) { parent.left = createNewNode(e); } else { parent.right = createNewNode(e); } } return true; } // 创建新的节点 protected TreeNode<E> createNewNode(E e) { return new TreeNode(e); } } // 二叉树的节点 class TreeNode<E extends Comparable<E>> { E element; TreeNode<E> left; TreeNode<E> right; public TreeNode(E e) { element = e; } }

The above code 15 mainly shows a simple binary search tree implemented by yourself, which includes several common operations. Of course, more operations still need to be completed by yourself. Because the operation of deleting nodes in a binary search tree is more complicated, I will introduce it in detail below.

  • Analysis of Delete Nodes in Binary Search Tree

To delete an element in a binary search tree, you first need to locate the node containing the element, as well as its parent node. Assuming that current points to the node in the binary search tree that contains the element, and parent points to the parent node of the current node, the current node may be the left or right child of the parent node. There are two situations to consider here:

  1. The current node has no left child, so just connect the patent node to the right child of the current node.
  2. The current node has a left child, let's say rightMost points to the node containing the largest element in the left subtree of the current node, and parentOfRightMost points to the parent of the rightMost node. Then first replace the element value in the current node with the element value in the rightMost node, connect the parentOfRightMost node to the left child of the rightMost node, and then delete the rightMost node.
    // 二叉搜索树删除节点
    public boolean delete(E e) {

        TreeNode<E> parent = null; TreeNode<E> current = root; // 找到要删除的节点的位置 while (current != null) { if (e.compareTo(current.element) < 0) { parent = current; current = current.left; } else if (e.compareTo(current.element) > 0) { parent = current; current = current.right; } else { break; } } // 没找到要删除的节点 if (current == null) { return false; } // 考虑第一种情况 if (current.left == null) { if (parent == null) { root = current.right; } else { if (e.compareTo(parent.element) < 0) { parent.left = current.right; } else { parent.right = current.right; } } } else { // 考虑第二种情况 TreeNode<E> parentOfRightMost = current; TreeNode<E> rightMost = current.left; // 找到左子树中最大的元素节点 while (rightMost.right != null) { parentOfRightMost = rightMost; rightMost = rightMost.right; } // 替换 current.element = rightMost.element; // parentOfRightMost和rightMost左孩子相连 if (parentOfRightMost.right == rightMost) { parentOfRightMost.right = rightMost.left; } else { parentOfRightMost.left = rightMost.left; } } return true; }

Balanced Binary Tree

Balanced binary tree, also known as AVL tree, is either an empty tree or a binary tree with the following properties: its left subtree and right subtree are both balanced binary trees, and the difference between the depths of the left subtree and the right subtree is equal to The absolute value does not exceed 1.


Balanced Binary Tree

AVL tree is the first self-balancing binary search tree algorithm invented. In AVL, the maximum difference in height between the two child subtrees of any node is 1, so it is also called a height-balanced tree. The maximum depth of an AVL tree with n nodes is about 1.44log2n. Lookups, insertions and deletions are O(log n) on average and worst case. Additions and deletions may require one or more tree rotations to rebalance the tree.

red-black tree

A red-black tree is a type of balanced binary tree that guarantees an event complexity of O(log n) for basic dynamic set operations in the worst case. The difference between a red-black tree and a balanced binary tree is as follows: (1) The red-black tree abandons the pursuit of complete balance, and pursues approximate balance. Under the condition that the time complexity of the balanced binary tree is not much different, it is guaranteed that each insertion only requires at most three rotations. It is easier to achieve a balance. (2) The balanced binary tree pursues absolute balance, the conditions are harsh, and it is more troublesome to implement, and the number of rotations required after each insertion of a new node cannot be predicted. click to see more

4. Figure

  • Introduction

A graph is a more complex data structure than a linear table and a tree. In a linear table, there is only a linear relationship between data elements. In a tree structure, there is an obvious hierarchical relationship between data elements, while in a graph structure In the graph, the relationship between nodes can be arbitrary, and any two data elements in the graph may be related. The application of graphs is quite extensive, especially the rapid development in recent years, which has penetrated into other branches such as linguistics, logic, physics, chemistry, telecommunication engineering, computer science and mathematics.

  • Related Reading

Because there is still a lot of content in this part of the picture, I will not introduce it in detail here. If you need it, you can search for relevant information yourself.

(1)  "Baidu Encyclopedia's Introduction to Graphs"
(2)  "Graphs of Data Structures (Storage Structure, Traversal)"

Reprint link: https://www.cnblogs.com/wanghuaijun/p/7302303.html

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324439861&siteId=291194637