Do you know red and black trees? Tell you a different red and black tree and say something interesting!

Posted by xylex on Fri, 17 Dec 2021 00:06:46 +0100

Let's first look at the following two questions:

Question 1. Can the key values of the red black tree be repeated?
Question 2. Must the red black tree have a key value?

There are many introductions about red and black trees on the Internet, and red and black trees are also widely used. Ask Du Niang. She will tell you all kinds of implementation methods, including C and C + + versions, as well as the version used by the linux kernel. The code is similar, that is, how to correct and balance when inserting or deleting. Many articles are illustrated, realistic and vivid. When you try to balance left and right in your mind, you basically reach the edge of mental collapse.

How to maintain the balance between three generations of fathers, grandfathers, uncles and brothers and how to improve family relations is A headache. If the red and black tree is compared to A genealogy, you may be A high ancestor at first, and become Taizong after inserting the next node. With the reproduction of the family, you may finally become an AI emperor. At first, A is father B. later, B becomes father A, even Grandpa, uncle and brother. Do you say chaos, burn brain, burn brain, and annoy people.
To paraphrase what Guo Degang said to Yu Qian in the crosstalk: at our age, it doesn't matter who is whose father. There are no sizes on the stage, and rules are set under the stage. They are given to all kinds of binary trees on the stage.

Here is a simple website: Visual data structure and Algorithm Teaching , very good. There is a dynamic display of classical data structures, which can save you from all kinds of rotation. As shown below:

Having said so much, I return to the two questions at the beginning of this article:

Question 1: can the key values of red black tree be repeated?

Most people may think that it is impossible to repeat, because repeated key values will conflict or have no practical significance. In fact, it can be repeated. In order to show respect for the binary tree, multiple 2's are inserted consecutively. Use above Amway website , a very 2 red black tree was established. As shown below:


The red and black trees above have the same key values, which is incredible, but it is indeed a red and black tree.
So what is the practical significance of this very 2 red black tree and where can it be applied? Of course, there are, and it is very extensive. This place is the timer. For most server programs, they basically need to implement their own timer to complete some special repetitive work, such as the timer in nodejs engine libuv library, the timer in nginx, and the key value validity judgment of redis....

When managing multiple timers, there will be nodes with equal key values, that is, nodes with equal expiration time. At this time, how to judge who will execute first?
The following are some key codes for the implementation of libuv timer:

// The libuv timer uses the callback function to compare the size of the key, where the key is the expiration time timeout
static int timer_less_than(const struct heap_node* ha,
                           const struct heap_node* hb) {
  const uv_timer_t* a;
  const uv_timer_t* b;

  a = container_of(ha, uv_timer_t, heap_node);
  b = container_of(hb, uv_timer_t, heap_node);

  if (a->timeout < b->timeout)
    return 1;
  if (b->timeout < a->timeout)
    return 0;

  /* Compare start_id when both have the same timeout. start_id is
   * allocated with loop->timer_counter in uv_timer_start().
   */
   // If the expiration time is the same, start is used_ ID to determine who executes first
   // This start_id is a self increasing variable, which is added to the start of the timer in the heap_ ID should be greater than the timer added to the heap earlier
  return a->start_id < b->start_id;
}

// A function that starts a timer and adds it to the heap
int uv_timer_start(uv_timer_t* handle,
                   uv_timer_cb cb,
                   uint64_t timeout,
                   uint64_t repeat) {
  uint64_t clamped_timeout;

  if (uv__is_closing(handle) || cb == NULL)
    return UV_EINVAL;

  if (uv__is_active(handle))
    uv_timer_stop(handle);

  clamped_timeout = handle->loop->time + timeout;
  if (clamped_timeout < timeout)
    clamped_timeout = (uint64_t) -1;

  handle->timer_cb = cb;
  handle->timeout = clamped_timeout;
  handle->repeat = repeat;
  /* start_id is the second index to be compared in timer_less_than() */
  handle->start_id = handle->loop->timer_counter++;

  // Insert the timer into the heap and use timer_ less_ The than function sorts the heap
  heap_insert(timer_heap(handle->loop),
              (struct heap_node*) &handle->heap_node,
              timer_less_than);
  uv__handle_start(handle);

  return 0;
}

libuv uses the smallest heap to store and manage multiple timers, In the sorting process, if a node with equal time is found (see the function timer_less_than above), start_id is used to compare the size. This start_id is a self increasing variable. The start_id of the timer added to the heap later is greater than that of the timer added to the heap earlier, so as to judge who executes the expiration event with equal key values first.

Go back to the red black tree above. If you carefully observe the creation process of the tree, you will find that there is a chronological order for nodes with the same Key values. If they are inserted late, the default value is large and placed later. That is to say, the red black tree automatically realizes the function of storing Key values according to the time axis. Even if the expiration events are equal (Key values are equal), we can take out the minimum expiration event for execution according to the chronological order in which it is inserted into the red black tree.

nginx uses a red black tree to store and manage multiple timers. I won't introduce it here.

Question 2. Must the red black tree have a key value?

This tree can also be created, but it looks more difficult to understand than the very 2 tree above.
The timer node size comparison function timer of libuv above_ less_ Than has told us that you can compare nodes without relying on the key value. When you insert a node, you can tell the node who is "big" and who is "small" through the callback function. This size is not in the mathematical sense, but may be the size of a logical business in the business. The business rather than mathematical sequence of a node is evaluated through a series of multiple indicators rather than a single key. For example, in the evaluation of personal credit, a so-called "size" value may be calculated according to a number of indicators (age, length of service, consumption records, etc.).

I write too much. I need to stop at work. Let's throw a brick and attract jade.

Thank you for reading!

Topics: Nginx Algorithm data structure Binary tree