Set, Map in Java (Binary Search Tree)

foreword

This blog will summarize the data structure Set and Map based on the existing knowledge. The following blog is only a summary of the personal learning process. It is a great honor to be of help to all bloggers.
This blog will briefly introduce the application of the concepts and principles of Set and Map, as well as their respective characteristics and differences. This article will only be my own summary, and I will make supplementary modifications later with in-depth study.

concept and background

search

Searching is searching is a practice often used in data structures. Commonly used search methods include direct traversal and binary search

The time complexity of direct traversal is O(n), but the execution efficiency will be slower when there are too many elements, while the time complexity of binary search is O(log(n)), the efficiency is significantly improved compared with direct traversal, but the premise must Is the search interval ordered

The above search is more suitable for static search , that is, elements will not be read and deleted during the search process. On the other hand, we sometimes need to query one data to get another data associated with it, and sometimes need to search for data Perform some modification operations at the same time, that is, dynamic search
. The above two methods obviously cannot meet the requirements. Java provides us with two data structures, Set and Map, mainly to deal with the required dynamic search.

key-value model

Generally, we refer to the data to be searched as keywords (key), the data corresponding to keywords as (value), and the whole as a key-value key-value pair, so two models are derived: Pure key model and key-value model

Pure key model: that is, we need to search through the keyword key, such as finding whether there is a data with key=0 in a bunch of data key
-value model: using the key keyword to find out whether the data associated with it exists, such as in a user In the class, the age of an object can be found through the name of an object defined by it.
Set is a simple value storage key, which is used to search for a pure key model.
Map stores key-value key-value pairs, which are used for key-value model lookup

Set

illustrate

  • Set is an interface inherited from Collection
  • Its internal value stores a data type, namely key, and cannot store repeated elements inside
  • The most commonly used function of Set is to deduplicate the elements in the set
  • The value of the key in the Set cannot be modified. If you want to change it, you need to delete the original key, modify it and add it again
  • Sey cannot store key = null key
  • There are two commonly used implementation classes of the Set interface, TreeSet and HashSet, and LinkedHashSet maintains a doubly linked list on the basis of HashSet to record the insertion order of elements
  • The bottom layer of Set is implemented by Map, which uses a default object of key and Object as a key-value pair to store in Map
    insert image description here

The difference between TreeSet and HashSet

insert image description here

common method

insert image description here

Map

Map is an interface that does not inherit other interfaces. It stores key-value key-value pairs internally, and the key is always unique.

Description of Map

  • Map is an interface, you can't instantiate objects directly, you can only instantiate its implementation classes TreeMap and HashMap
  • The key stored in the Map must be unique, but the value can be repeated
  • Insert key into Map, key cannot be null, but value can
  • All the keys in the Map can be extracted and put into a Set, that is, call its keySet() method
  • Map.Entry<K,V> is an internal class of Map, which is used to store the key-value mapping relationship, and provides key and v, value acquisition methods and value setting methods for the class

The difference between TreeMap and HashMap

insert image description here

common method

insert image description here

Binary Search Tree

concept:

A binary search tree, also known as a binary sort tree, is an empty tree or a binary tree that satisfies the following characteristics

  • If its left subtree is not empty, the values ​​of all nodes in the left subtree are less than the root node
  • If his right subtree is not empty, the values ​​of all nodes in the right subtree are greater than the root node
  • Its left and right subtrees also satisfy the above rules, and it is also a binary search tree
    insert image description here

How to perform a search operation on a binary search tree

find val

If the root node is not empty

  • If val == root.val, find the node and return root
  • If val > root.val, then root = root.left, find in the left subtree
  • If val < root.val, then root = root.right, find in the right subtree

Returns null if the node is empty, not found
insert image description here

How to insert into a binary search tree

insert val

If the number is empty, insert val directly at the root node

Otherwise, determine the insertion position of val according to the search logic, and define a parent node parent = null

  • If val == root.val, there is already a node with this value in the tree, and it is not allowed to insert again
  • 如果 val > root.val,parent = root,root = root.right
  • if val < root.val, parent = root, root = root.left
    until root is equal to null, at this time, if (val > parent.val), parent.right = null else parent.left = null
    insert image description here

How to delete a binary search tree

If you directly delete a node of a binary search tree, it may cause the structure of the tree to collapse. Therefore, the deletion of a binary search tree needs to be discussed in the following cases: find the desired deletion node cur through the val value to be deleted, and
its The parent node is parent

  • cur.left = null
    1.cur is the root node root, then root = root.right
    2.cur is not the root node but parent.left, then parent.left = cur.right
    3.cur is not the root node but parent.right , then parent.right = cur.right

  • cur.right = null
    1.cur is the root node root, then root = root.left
    2.cur is not the root node but parent.left, then parent.left = cur.left
    3.cur is not the root node but parent.right , then parent.right = cur.left

  • cur.left != null && cur.right != null
    Here you need to use the substitution method, find a node in its right subtree (that is, the node with the smallest value), and replace it with the node to be deleted s position
    insert image description here

Implementation

public class BinarySearchTree {
    
    
    public static class TreeNode {
    
    
        public int key;
        public TreeNode left;
        public TreeNode right;

        public TreeNode(int key) {
    
    
            this.key = key;
        }
    }

    private TreeNode root = null;

    /**
     * 在搜索树中查找 key,如果找到,返回 key 所在的结点,否则返回 null
     *
     * @param key
     * @return null 表示没有查询到,非null表示查询到该节点
     */
    public TreeNode search(int key) {
    
    
        TreeNode node = root;
        while (node != null) {
    
    
            if (key == node.key) {
    
    
                return node;
            } else if (key > node.key) {
    
    
                node = node.right;
            } else {
    
    
                node = node.left;
            }
        }
        return null;
    }

    /**
     * 插入
     *
     * @param key
     * @return true 表示插入成功, false 表示插入失败
     */
    public boolean insert(int key) {
    
    
        if(root == null){
    
    
            root = new TreeNode(key);
            return true;
        }
        TreeNode parent = null;
        TreeNode node = root;
        while(node != null){
    
    
            if(key == node.key){
    
    
                return false;
            }else if(key < node.key){
    
    
                parent = node;
                node = node.left;
            }else {
    
    
                parent = node;
                node = node.right;
            }
        }
        if(key < parent.key){
    
    
            parent.left = new TreeNode(key);
        }else{
    
    
            parent.right = new TreeNode(key);
        }
        return true;
    }

    /**
     * 删除
     *
     * @param key
     * @return true成功返回 ,false失败返回
     */
    public boolean remove(int key) {
    
    
        TreeNode parent = null;
        TreeNode cur = root;
        while (cur != null) {
    
    
            if (key == cur.key) {
    
    
                break;
            } else if (key > cur.key) {
    
    
                parent = cur;
                cur = cur.right;
            } else {
    
    
                parent = cur;
                cur = cur.left;
            }
        }

        if(cur == null){
    
    
            return false;
        }

        if(parent == null){
    
    
            TreeNode n = cur.right;
            while(n.left != null){
    
    
                n = n.left;
            }
            n.left = cur.left;
            return true;
        }

        if(cur.left == null && cur.right == null){
    
    
            if(key < parent.key){
    
    
                parent.left = null;
            }else{
    
    
                parent.right = null;
            }
            return true;
        }

        if(cur.left != null && cur.right != null){
    
    
            TreeNode node = cur.right;
            while(node.left != null){
    
    
                node = node.left;
            }
            node.left = cur.left;
            node.right = cur.right;
            if(key < parent.key){
    
    
                parent.left = node;
            }else{
    
    
                parent.right = node;
            }
            return true;
        }

        if(cur.left == null){
    
    
            if(cur == root){
    
    
                root = root.right;
            }else if(cur == parent.left){
    
    
                parent.left = cur.right;
            }else if(cur == parent.right){
    
    
                parent.right = cur.right;
            }
            return true;
        }

        if(cur == root){
    
    
            root = root.left;
        }else if(cur == parent.left){
    
    
            parent.left = cur.left;
        }else if(cur == parent.right){
    
    
            parent.right = cur.left;
        }
        return true;
    }
}

performance

A large part of the execution speed of insertion and deletion
of a binary search tree depends on the execution efficiency of the search. Due to the different order of inserting elements, the final tree shape is different.
insert image description here
Best case: complete binary tree, time complexity O(log(n))
Worst case: single branch tree, time complexity O(n)

Relationship with Java collection classes

The TreeSet and TreeMap implemented by search tree in Java, in order to prevent the search tree from degenerating into a single-branch tree during the operation, the actual bottom layer uses red-black tree to realize the red-black tree is an
approximately peaceful binary search tree, which is in the binary Based on the search tree + the color has been constrained by the nature of the red-black tree itself , it is a balanced binary search tree that the red-black tree can always approximate when operating

TreeSet and TreeMap

  • TreeSet and TreeMap are Map and Set implemented by binary search tree in Java
  • The bottom layer of TreeSet and TreeMap is implemented with search tree (red-black tree), so its storage is ordered for key
  • The time complexity of adding/deleting/finding operations of TreeSet and TreeMap is O(log(n))
  • The methods of TreeSet and TreeMap are not synchronized, that is, thread is not safe

The above is a summary of the knowledge points of the seven sets and maps. With the deepening of follow-up learning, the content will be supplemented and modified synchronously. It will be a great honor to help all bloggers. Please correct me

Guess you like

Origin blog.csdn.net/m0_46233999/article/details/117904770