<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Snacks</title>
    <subtitle>Snack-size learning for a fast-paced world.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://snacks.devtestonly.uk/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://snacks.devtestonly.uk"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-09-15T00:00:00+00:00</updated>
    <id>https://snacks.devtestonly.uk/atom.xml</id>
    <entry xml:lang="en">
        <title>Kernel Space and User Space</title>
        <published>2026-09-15T00:00:00+00:00</published>
        <updated>2026-09-15T00:00:00+00:00</updated>
        
        <author>
          <name>someone</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://snacks.devtestonly.uk/os/kernel-space-user-space/"/>
        <id>https://snacks.devtestonly.uk/os/kernel-space-user-space/</id>
        
        <content type="html" xml:base="https://snacks.devtestonly.uk/os/kernel-space-user-space/">&lt;h2 id=&quot;understanding-kernel-space-and-user-space&quot;&gt;Understanding Kernel Space and User Space&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Kernel space&lt;/strong&gt; and &lt;strong&gt;user space&lt;/strong&gt; represent two distinct memory areas and execution modes designed to isolate user applications from the core operating system and physical hardware.&lt;/p&gt;
&lt;h3 id=&quot;1-what-is-kernel-space-and-user-space&quot;&gt;1. What is Kernel Space and User Space?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Kernel Space (Kernel Mode)&lt;/strong&gt;: This is the elevated, protected execution environment and memory region where the core operating system (the kernel) resides and executes. When the CPU is executing in kernel mode, it has unrestricted access to all physical hardware resources, complete system memory (both kernel and user address spaces), and all hardware instructions. This includes &lt;strong&gt;privileged instructions&lt;/strong&gt; such as direct I/O manipulation, timer control, disabling interrupts, and system halt commands.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;User Space (User Mode)&lt;/strong&gt;: This is the restricted execution environment and virtual address space reserved for user processes and applications (e.g., text editors, compilers, web browsers, and command shells). Code executing in user mode can only access its own assigned address space and non-privileged CPU instructions. It is strictly prevented from directly reading or modifying kernel memory or directly issuing instructions that control hardware devices. If a user-space process attempts an illegal memory access or privileged operation, the hardware traps the error to the OS (resulting in an exception such as a segmentation fault).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;2-does-distinguishing-between-the-two-require-hardware-support&quot;&gt;2. Does Distinguishing Between the Two Require Hardware Support?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Yes, distinguishing between kernel space and user space strictly requires hardware support&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Proper operating system isolation and protection cannot be implemented purely in software because user applications could easily bypass software-only restrictions. The underlying processor hardware must natively support distinct execution levels or modes. Without processor-level execution modes and memory-management unit (MMU) enforcement, an errant or malicious user program could modify kernel data structures or crash the entire computer system.&lt;/p&gt;
&lt;h3 id=&quot;3-how-does-the-cpu-know-it-is-executing-kernel-or-user-space-code&quot;&gt;3. How Does the CPU Know It Is Executing Kernel or User Space Code?&lt;/h3&gt;
&lt;p&gt;The CPU tracks and enforces its current execution state using dedicated hardware mechanisms built into the processor:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The Hardware Mode Bit &amp;amp; Protection Rings&lt;/strong&gt;:
Processors include a hardware &lt;strong&gt;mode bit&lt;/strong&gt; (or privilege bits) in internal control registers. In a classic dual-mode architecture, the mode bit is set to &lt;code&gt;0&lt;/code&gt; for kernel mode and &lt;code&gt;1&lt;/code&gt; for user mode. On x86 architectures, this is implemented via &lt;strong&gt;protection rings&lt;/strong&gt;, where &lt;strong&gt;Ring 0&lt;/strong&gt; corresponds to kernel mode (full privilege) and &lt;strong&gt;Ring 3&lt;/strong&gt; corresponds to user mode (least privilege), tracked by current privilege level bits in processor registers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Memory Page Protection Checks&lt;/strong&gt;:
Areas of virtual memory are tagged in page tables as belonging to kernel space or user space. During every memory access, the MMU checks the current CPU mode bit against the page’s access permissions. If a processor running in user mode (&lt;code&gt;mode bit = 1&lt;/code&gt; or Ring 3) attempts to access a page marked for kernel space (&lt;code&gt;mode bit = 0&lt;/code&gt; or Ring 0), the hardware generates a memory protection trap.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Controlled Mode Transitions&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Boot Time&lt;/strong&gt;: The computer hardware starts up in kernel mode (&lt;code&gt;0&lt;/code&gt;), allowing the operating system to safely load, initialize hardware drivers, and establish trap vectors.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Switching to User Mode&lt;/strong&gt;: Before handing control to a user process, the kernel executes an instruction that sets the mode bit to &lt;code&gt;1&lt;/code&gt; (user mode).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Trapping into Kernel Mode&lt;/strong&gt;: When a user process requires OS services (via a system call) or when a hardware interrupt/exception occurs, the processor automatically saves the current user context, switches the mode bit to &lt;code&gt;0&lt;/code&gt; (kernel mode), and jumps to a predefined, guarded handler location in kernel memory. User code cannot jump to arbitrary kernel addresses; it must pass through these strict hardware-enforced gates.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Returning to User Mode&lt;/strong&gt;: When the kernel completes the service request or interrupt handler, it issues a return instruction (e.g., &lt;code&gt;iret&lt;/code&gt; or &lt;code&gt;sysexit&lt;/code&gt;), which restores the user context and sets the mode bit back to &lt;code&gt;1&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;how-system-calls-cross-the-kernel-user-boundary&quot;&gt;How System Calls Cross the Kernel-User Boundary&lt;/h2&gt;
&lt;p&gt;In Linux and modern CPU architectures, &lt;strong&gt;trapping into kernel mode&lt;/strong&gt; is the hardware-enforced mechanism that allows an unprivileged user-space application to securely request kernel services.&lt;/p&gt;
&lt;h3 id=&quot;1-how-trapping-into-kernel-mode-works-in-linux&quot;&gt;1. How “Trapping into Kernel Mode” Works in Linux&lt;/h3&gt;
&lt;p&gt;Transitioning from user mode to kernel mode involves a tightly coordinated sequence between the C library, processor hardware, and kernel trap handlers:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;User-Space Setup &amp;amp; C Library Wrapper&lt;/strong&gt;: An application calls an API function (such as &lt;code&gt;open()&lt;/code&gt; or &lt;code&gt;write()&lt;/code&gt;). The C library (glibc) wrapper copies the call arguments into specific CPU registers (e.g., &lt;code&gt;%ebx&lt;/code&gt;, &lt;code&gt;%ecx&lt;/code&gt; on x86) and places the unique &lt;strong&gt;system call number&lt;/strong&gt; into a designated register (e.g., &lt;code&gt;%eax&lt;/code&gt; / &lt;code&gt;%rax&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Executing the Trap Instruction&lt;/strong&gt;: The wrapper executes a dedicated hardware instruction—such as &lt;strong&gt;&lt;code&gt;int 0x80&lt;/code&gt;&lt;/strong&gt; (software interrupt trap instruction) or modern fast-entry instructions like &lt;strong&gt;&lt;code&gt;syscall&lt;/code&gt;&lt;/strong&gt; / &lt;strong&gt;&lt;code&gt;sysenter&lt;/code&gt;&lt;/strong&gt; (x86) or &lt;strong&gt;&lt;code&gt;SVC&lt;/code&gt;&lt;/strong&gt; (ARM)—which triggers a software exception.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hardware Mode Switch &amp;amp; Jump&lt;/strong&gt;: The CPU hardware intercepts this instruction, automatically switches its hardware privilege level from &lt;strong&gt;user mode (Ring 3)&lt;/strong&gt; to &lt;strong&gt;kernel mode (Ring 0)&lt;/strong&gt;, and forces execution to jump to a fixed, predefined location in kernel memory (the &lt;code&gt;system_call()&lt;/code&gt; trap handler).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Context Saving &amp;amp; Service Execution&lt;/strong&gt;: The kernel saves the application’s registers onto the thread’s private &lt;strong&gt;kernel stack&lt;/strong&gt;. It validates the system call number against &lt;code&gt;NR_syscalls&lt;/code&gt; and uses it as an index into the &lt;strong&gt;&lt;code&gt;sys_call_table&lt;/code&gt;&lt;/strong&gt; to execute the corresponding kernel service routine.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Returning to User Space&lt;/strong&gt;: Once completed, the kernel restores the saved registers and executes a return instruction (such as &lt;code&gt;iret&lt;/code&gt;, &lt;code&gt;sysret&lt;/code&gt;, or &lt;code&gt;sysexit&lt;/code&gt;), which simultaneously restores the CPU privilege level back to user mode and hands control back to the application.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;2-are-there-special-instructions-for-changing-privilege-levels&quot;&gt;2. Are There Special Instructions for Changing Privilege Levels?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;User-Accessible Gate Instructions&lt;/strong&gt;: Yes. Instructions such as &lt;strong&gt;&lt;code&gt;syscall&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;sysenter&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;int 0x80&lt;/code&gt;&lt;/strong&gt;, and &lt;strong&gt;&lt;code&gt;SVC&lt;/code&gt;&lt;/strong&gt; exist specifically as “doors” or gate portals allowing user-space code to request a controlled transition into kernel mode.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;General Mode Modification Instructions&lt;/strong&gt;: However, there is &lt;strong&gt;no instruction&lt;/strong&gt; that allows a user-space application to arbitrarily set the privilege level bit or flip execution rings at will. Instructions that directly modify CPU control registers or privilege flags (such as x86 &lt;code&gt;%cr3&lt;/code&gt; or privilege bits in &lt;code&gt;EFLAGS&lt;/code&gt;) are designated as &lt;strong&gt;privileged instructions&lt;/strong&gt;. The processor allows privileged instructions to execute &lt;strong&gt;only&lt;/strong&gt; when the CPU is already in kernel mode.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;3-how-the-system-guards-against-unauthorized-user-space-actions&quot;&gt;3. How the System Guards Against Unauthorized User-Space Actions&lt;/h3&gt;
&lt;p&gt;The operating system and CPU hardware enforce protection through several hardware-backed barriers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Fixed, Predefined Entry Targets&lt;/strong&gt;: When an application issues a trap instruction (&lt;code&gt;syscall&lt;/code&gt; or &lt;code&gt;int 0x80&lt;/code&gt;), user-space code &lt;strong&gt;cannot specify an arbitrary target address&lt;/strong&gt; in kernel memory. The hardware restricts the jump strictly to a predefined, well-guarded code path registered in hardware control structures by the kernel during system boot.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hardware Trapping of Privileged Instructions&lt;/strong&gt;: If a program in user mode attempts to execute a privileged instruction or manually modify control registers, the CPU detects the ring violation, prevents execution, and generates a &lt;strong&gt;General Protection Fault&lt;/strong&gt; / exception. The kernel catches this hardware fault and typically terminates the offending process.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MMU Page Table Protection&lt;/strong&gt;: Virtual memory pages belonging to kernel space are flagged as supervisor/kernel-only in page tables. The Memory Management Unit (MMU) checks these protection bits on every memory access; any attempt by user-mode code (&lt;code&gt;Ring 3&lt;/code&gt;) to read, write, or execute kernel memory (&lt;code&gt;Ring 0&lt;/code&gt;) triggers an immediate hardware page fault.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kernel Parameter Validation&lt;/strong&gt;: Even after passing through a valid system call gate, the kernel treats all arguments provided by user space as untrusted. Routines like &lt;code&gt;copy_from_user()&lt;/code&gt; verify that provided pointers reside strictly within the calling process’s allowed user address space before dereferencing them.&lt;/li&gt;
&lt;/ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Skip List</title>
        <published>2024-07-10T00:00:00+00:00</published>
        <updated>2024-07-10T00:00:00+00:00</updated>
        
        <author>
          <name>someone</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://snacks.devtestonly.uk/dsa/skip-list/"/>
        <id>https://snacks.devtestonly.uk/dsa/skip-list/</id>
        
        <content type="html" xml:base="https://snacks.devtestonly.uk/dsa/skip-list/">&lt;p&gt;Skip List is a data structure, invented to solve the problem of organizing data for quick searching purposes. Despite its simple design, it provides relatively good performance, on average &lt;strong&gt;$O\lparen log~n\rparen$&lt;/strong&gt;, for both searching, insertion and deletion operations. So how does it work?&lt;/p&gt;
&lt;h2 id=&quot;the-searching-problem&quot;&gt;The Searching Problem&lt;/h2&gt;
&lt;p&gt;Searching is one of the oldest and most ubiquitous problems in the world of computer science. Many solutions have been created to solve this, and each solution has its own strengths and weaknesses.&lt;/p&gt;
&lt;p&gt;The first solution is quite simple, which is to use an Array containing data, arrange them in ascending or descending order, then use a Binary Searching algorithm to quickly find the desired element in this array. The solution gives good search time complexity, on average &lt;strong&gt;$O\lparen log~n\rparen$&lt;/strong&gt;. However, the time complexity required to insert or delete elements in the array is not very good, on average &lt;strong&gt;$O\lparen n\rparen$&lt;/strong&gt;, because both operations require shifting elements in the array one after another.&lt;/p&gt;
&lt;p&gt;There is a data structure to hold sorted data and give &lt;strong&gt;$O\lparen 1\rparen$&lt;/strong&gt; insertion and deletion time complexity, which is Linked List. However, because Linked List does not support random access, to find a position in the middle of the list, we must search from the beginning of the list, so the time to find the position to insert is &lt;strong&gt;$O\lparen n\rparen$&lt;/strong&gt;. Using a Linked List eliminates the ability to quickly search, which is the core requirement of the solution.&lt;/p&gt;
&lt;p&gt;Self-balancing Binary Search Tree family data structures solve the search problem very well, on average &lt;strong&gt;$O\lparen log~n\rparen$&lt;/strong&gt;, and at the same time give good insertion and deletion time complexity of &lt;strong&gt;$O\lparen log~n\rparen$&lt;/strong&gt;. However, they are complex organized data structures as those trees are not simple to balance, hence not so easy to implement.&lt;/p&gt;
&lt;p&gt;And that’s where &lt;strong&gt;Skip List&lt;/strong&gt; shines.&lt;/p&gt;
&lt;h2 id=&quot;the-structure-of-skip-list&quot;&gt;The Structure of Skip List&lt;/h2&gt;
&lt;p&gt;Inside the core Skip List utilizes Linked List to store data. Its improvement lies in the addition of layers. Each layer is a Linked List, however, the higher the layers, the smaller the number of elements. The reduction in the number of elements by layer is expected to decrease exponentially:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#000000, #E6EDF3); background-color: light-dark(#FFFFFF, #0D1117);&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;L2: | - - - - - - - 4 - - - - - - - - - - - null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;L1: | - - - 2 - - - 4 - - - 6 - - - 8 - - - null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;L0: | - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - null&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the example above, we have a Skip List with 3 layers: $L_0$, $L_1$, $L_2$. $L_0$ is the bottom layer, a Linked List with all elements sorted. Layer $L_1$ is located above $L_0$, consisting of only 2, 4, 6, 8, the number of elements is reduced to half compared to $L_0$. Layer $L_2$ is above $L_1$, there is only 1 element left: 4.&lt;/p&gt;
&lt;p&gt;At each layer there are two additional special elements: a sentinel element located at the beginning of the list, represented by the character &lt;code&gt;|&lt;/code&gt;, and a null element located at the end of the list, represented by &lt;code&gt;null&lt;/code&gt;. They mark the beginning and the end of the list.&lt;/p&gt;
&lt;h2 id=&quot;searching-in-a-skip-list&quot;&gt;Searching in a Skip List&lt;/h2&gt;
&lt;p&gt;So how does Skip List help in searching an element? The answer is that we always start searching from the highest layer of the Skip List, where there are the fewest elements, then gradually go down to lower layers if we still can’t find it. With each step down to the next layer, we only start searching from the element corresponding to the element we just traversed in the previous layer (called &lt;strong&gt;checkpoint&lt;/strong&gt;) and ignore all elements before that checkpoint. The search ends as soon as the desired element is found in any layer.&lt;/p&gt;
&lt;p&gt;To illustrate this concept, let’s find element 6:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Start at $L_2$, browse through: 4, null. Because 6 &amp;gt; 4 and we reached the end of current layer, go down to $L_1$ with 4 as checkpoint.&lt;/li&gt;
&lt;li&gt;At $L_1$, skip 4 and all elements before, browse through: 6. Because 6 = 6, we found it.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#000000, #E6EDF3); background-color: light-dark(#FFFFFF, #0D1117);&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;L2: | ============= 4 - - - - - - - - - - - null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;L1: | - - - 2 - - - 4 =====[6]- - - 8 - - - null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;L0: | - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - null&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, let’s find element 3:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Start at $L_2$, browse through: 4. Because 3 &amp;lt; 4, we know that we can’t find 3 in this layer, go down to $L_1$ and search from the beginning.&lt;/li&gt;
&lt;li&gt;At $L_1$, browse through: 2, 4. Because 3 &amp;gt; 2 and 3 &amp;lt; 4, we know that we can’t find 3 in this layer, go down to $L_0$ with checkpoint 2.&lt;/li&gt;
&lt;li&gt;At $L_0$, skip 2 and all elements before, browse through: 3. Because 3 = 3, we found it.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#000000, #E6EDF3); background-color: light-dark(#FFFFFF, #0D1117);&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;L2: | - - - - - - - 4 - - - - - - - - - - - null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;L1: | ===== 2 - - - 4 - - - 6 - - - 8 - - - null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;L0: | - 1 - 2 =[3]- 4 - 5 - 6 - 7 - 8 - 9 - null&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;building-a-skip-list&quot;&gt;Building a Skip List&lt;/h2&gt;
&lt;p&gt;Skip List is built from the lowest layer $L_0$ which is a Linked List containing all sorted elements. Then, gradually build up the layers above $L_1$, $L_2$, $L_3$. To build these layers, the way is to go through all elements of the layer below, at each element, flipping a coin to decide whether or not that element will be picked up to the current layer.&lt;/p&gt;
&lt;p&gt;Let’s illustrate the concept by builing this Skip List:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#000000, #E6EDF3); background-color: light-dark(#FFFFFF, #0D1117);&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;L2: | - - - - - - - 4 - - - - - - - - - - - null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;L1: | - - - 2 - - - 4 - - - 6 - - - 8 - - - null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;L0: | - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - null&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;They recommend the number of layers a Skip List with $n$ elements should have is about $log_2~n$. This Skip List has 9 elements, $log_2~9~\approx~3$, so we should build 3 layers.&lt;/p&gt;
&lt;p&gt;Start by building $L_0$, we sort all elements and build a Linked List. This should be straightforward:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#000000, #E6EDF3); background-color: light-dark(#FFFFFF, #0D1117);&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;L0: | - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - null&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, to build $L_1$, we loop through all elements in $L_0$. At each element, we flip a coin to decide if that element should be promoted to $L_1$. Let say if it’s Head &lt;strong&gt;(H)&lt;/strong&gt;, we promote that element, if it’s Tail &lt;strong&gt;(T)&lt;/strong&gt;, we leave it.&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#000000, #E6EDF3); background-color: light-dark(#FFFFFF, #0D1117);&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;L1: | - - - 2 - - - 4 - - - 6 - - - 8 - - - null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        T   H   T   H   T   H   T   H   T&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;L0: | - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - null&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To build $L_2$, we loop through all elements in $L_1$ and also do the coin flips:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#000000, #E6EDF3); background-color: light-dark(#FFFFFF, #0D1117);&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;L2: | - - - - - - - 4 - - - - - - - - - - - null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            T       H       T       T&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;L1: | - - - 2 - - - 4 - - - 6 - - - 8 - - - null&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s easy to see that the content of the Skip List depends on the result of the sequence of coin flips. And because the sequence of coin flip results depends on random probability, people call Skip List a &lt;strong&gt;probabilistic data structure&lt;/strong&gt;. Skip List content after each build is not exactly the same but will be different depending on the probability.&lt;/p&gt;
&lt;h2 id=&quot;insertion&quot;&gt;Insertion&lt;/h2&gt;
&lt;p&gt;To insert new element into the Skip List, we find the appropriate position in the bottom layer, insert the element in that position, and promote it through upper layers using coin flips. Consider adding a new layer if the number of elements has grown beyond the limit, in this example, when $log_2~n~\approx~4$.&lt;/p&gt;
&lt;p&gt;Let’s insert 6 to this Skip List:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#000000, #E6EDF3); background-color: light-dark(#FFFFFF, #0D1117);&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;L2: | - - - - - - - 4 - - - - - - - - - null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;L1: | - - - 2 - - - 4 - - - 7 - - - - - null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;L0: | - 1 - 2 - 3 - 4 - 5 - 7 - 8 - 9 - null&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No need to add a new layer cause after the insertion, number of elements is 9, which hasn’t reached the limit.&lt;/p&gt;
&lt;p&gt;Find the position in $L_0$, using searching procedure described above. Time complexity should be &lt;strong&gt;$O\lparen log~n\rparen$&lt;/strong&gt; in average. The position should be between 5 and 7:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#000000, #E6EDF3); background-color: light-dark(#FFFFFF, #0D1117);&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;L2: | ============= 4 - - - - - - - - - - - null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;L1: | - - - 2 - - - 4 - - - - - 7 - - - - - null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;L0: | - 1 - 2 - 3 - 4 = 5 -[*]- 7 - 8 - 9 - null&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this step we should record the search path we’ve gone through until reaching that position. Search path is an array of every checkpoints at every layers we touched. In this example, the search path through $L_2$, $L_1$, $L_0$ should be &lt;code&gt;[4, 4, 5]&lt;/code&gt;. Search path is consulted later to know which element will be the predecessor of new element at each layer. This help to easily promote the new element.&lt;/p&gt;
&lt;p&gt;Next, insert 6 to that position. This is just a simple Linked List insertion, time complexity of the operation should be &lt;strong&gt;$O\lparen 1\rparen$&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#000000, #E6EDF3); background-color: light-dark(#FFFFFF, #0D1117);&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;L2: | - - - - - - - 4 - - - - - - - - - - - null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;L1: | - - - 2 - - - 4 - - - - - 7 - - - - - null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;L0: | - 1 - 2 - 3 - 4 - 5 -[6]- 7 - 8 - 9 - null&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By consulting the search path &lt;code&gt;[4, 4, 5]&lt;/code&gt;, we know that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The predecessor of 6 at $L_0$ is 5.&lt;/li&gt;
&lt;li&gt;The predecessor of 6 at $L_1$ is 4.&lt;/li&gt;
&lt;li&gt;The predecessor of 6 at $L_2$ is 4.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, promote that new element through upper layers using coin flips, see how far the element could go up:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#000000, #E6EDF3); background-color: light-dark(#FFFFFF, #0D1117);&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;L2: | - - - - - - - 4 - - - - - - - - - - - null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                            T&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;L1: | - - - 2 - - - 4 - - - 6 - 7 - - - - - null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                            H&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;L0: | - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - null&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The promotion stops at $L_1$, we have this Skip List as final result:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#000000, #E6EDF3); background-color: light-dark(#FFFFFF, #0D1117);&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;L2: | - - - - - - - 4 - - - - - - - - - - - null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;L1: | - - - 2 - - - 4 - - - 6 - 7 - - - - - null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;L0: | - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - null&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;deletion&quot;&gt;Deletion&lt;/h2&gt;
&lt;p&gt;To remove an element from the Skip List, we find that element and remove it from every layers it appears. Consider removing top layer if it becomes empty. Time complexity should be &lt;strong&gt;$O\lparen log~n\rparen$&lt;/strong&gt; in average.&lt;/p&gt;
&lt;h2 id=&quot;memory-consumption&quot;&gt;Memory consumption&lt;/h2&gt;
&lt;p&gt;This data structure consumes about &lt;strong&gt;twice&lt;/strong&gt; as much memory as a Linked List with the same number of elements. That’s the price to pay for its speed.&lt;/p&gt;
</content>
        
    </entry>
</feed>
