Skip to main content
Became Hot Network Question
Typo fix
Source Link
Toby Speight
  • 81.8k
  • 14
  • 101
  • 309

Critique request##request

Critique request##

Critique request

Source Link
coderodde
  • 29.8k
  • 14
  • 77
  • 194

Java - Converting a skip list to the ASCII art

Intro

This post won't present the actual mechanics of the skip list. Instead, all I got here is the facility for converting skip lists to funky ASCII art. For example, the unit test prints:

+-+                         +-+                         
| |------------------------>| |                         
+-+                         +-+                         
 |                           |                          
 V                           V                          
+-+            +-+          +-+               +-+       
| |----------->| |--------->| |-------------->| |       
+-+            +-+          +-+               +-+       
 |              |            |                 |        
 V              V            V                 V        
+-+  +-+  +-+  +-+  +----+  +-+  +---------+  +-+  +-+  
| |->|0|->|1|->|2|->|3000|->|4|->|5\n6\n\n7|->|6|->|7|  
+-+  +-+  +-+  +-+  +----+  +-+  +---------+  +-+  +-+ 

Code

com.github.coderodde.util.SkipList.java:

package com.github.coderodde.util;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Random;

public final class SkipList<K extends Comparable<? super K>> {

    static final class Node<K> {
        public K key;
        public Node<K> next;
        
        Node(K key, Node<K> next) {
            this.key = key;
            this.next = next;
        }
        
        @Override
        public int hashCode() {
            return Objects.hashCode(key);
        }
        
        @Override
        public boolean equals(Object o) {
            return Objects.equals(((Node<K>) o).key, this.key);
        }
    }
    
    static final class Index<K> {
        Node<K> node;
        Index<K> down;
        Index<K> right;
        
        Index(Node<K> node, Index<K> down, Index<K> right) {
            this.node = node;
            this.down = down;
            this.right = right;
        }
    }
    
    Index<K> head;
    int size;
    
    @Override
    public String toString() {
        return new ToStringConverter().convert();
    }
    
    private final class ToStringConverter {
        private int levels;
        private char[][] charMatrix;
        private final Map<Node<K>, Integer> columnIndexMap = new HashMap<>();
        
        ToStringConverter() {
            this.levels = getLevels();
        }
        
        String convert() {
            charMatrix = getCharacterMatrix();
            applyNodeChain();
            
            for (int l = 0; l < levels; l++) {
                applyIndexChainImpl(l);
            }
            
            return convertMatrixToString();
        }
        
        private Index<K> getStartIndex(int level) {
            Index<K> target = head;
            
            while (level-- > 0) {
                target = target.down;
            }
            
            return target;
        }
        
        private void setBox(int x, int y) {
            charMatrix[y - 1][x - 1] = '+';
            charMatrix[y - 1][x]     = '-';
            charMatrix[y - 1][x + 1] = '+';
            charMatrix[y    ][x - 1] = '|';
            charMatrix[y    ][x + 1] = '|';
            charMatrix[y + 1][x - 1] = '+';
            charMatrix[y + 1][x]     = '-';
            charMatrix[y + 1][x + 1] = '+';
        }
        
        private void applyIndexChainImpl(int level) {
            Index<K> index = getStartIndex(level);
            Index<K> next = index.right;
            int startRowIndex = level * 5;
            
            while (index != null) {
                // Print the box:
                int x = columnIndexMap.get(index.node);
                setBox(x, startRowIndex + 1);
                
                // Print the arrow to the node:
                charMatrix[startRowIndex + 3][x] = '|';
                charMatrix[startRowIndex + 4][x] = 'V';
                
                if (next != null) {
                    // Print the arrow to the right:
                    int arrowLength = columnIndexMap.get(next.node)
                                    - columnIndexMap.get(index.node)
                                    - 4;
                    
                    int xx = columnIndexMap.get(index.node) + 2;
                    
                    for (int i = 0; i < arrowLength; i++) {
                        charMatrix[startRowIndex + 1][xx++] = '-';
                    }
                    
                    charMatrix[startRowIndex + 1][xx] = '>';
                    next = next.right;
                }
                
                index = index.right;
            }
        }
        
        private void applyNodeChain() {
            int startRowIndex = levels * 5;
            int startColIndex = 0;
            
            for (Node<K> n = head.node; n != null; n = n.next) {
                
                columnIndexMap.put(n, startColIndex + 1);
                
                String nodeContent = 
                        Objects.toString(
                                n.key == null ? " " : n.key)
                                .replaceAll("\n", "\\\\n");
                
                int nodeContentLength = nodeContent.length();
                
                charMatrix[startRowIndex][startColIndex]     = '+';
                charMatrix[startRowIndex + 1][startColIndex] = '|';
                charMatrix[startRowIndex + 2][startColIndex] = '+';
                
                startColIndex++;
                
                for (int i = 0; i < nodeContentLength; i++) {
                    charMatrix[startRowIndex][startColIndex] = '-';
                    charMatrix[startRowIndex + 1][startColIndex] = 
                        nodeContent.charAt(i);
                    
                    charMatrix[startRowIndex + 2][startColIndex] = '-';
                    
                    startColIndex++;
                }
                
                charMatrix[startRowIndex][startColIndex] = '+';
                charMatrix[startRowIndex + 1][startColIndex] = '|';
                charMatrix[startRowIndex + 2][startColIndex] = '+';
                
                startColIndex++;
                
                if (n.next != null) {
                    charMatrix[startRowIndex + 1][startColIndex++] = '-';
                    charMatrix[startRowIndex + 1][startColIndex++] = '>';
                }
            }
        }
        
        private String convertMatrixToString() {
            StringBuilder sb = 
                    new StringBuilder(
                            charMatrix.length * (charMatrix[0].length + 1));
            
            for (char[] row : charMatrix) {
                for (char ch : row) {
                    sb.append(ch);
                }
                
                sb.append("\n");
            }
            
            return sb.toString();
        }
        
        private int getLevels() {
            int levels = 0;
            Index<K> index = SkipList.this.head;
            
            while (index != null) {
                levels++;
                index = index.down;
            }
            
            return levels;
        }
        
        private char[][] getCharacterMatrix() {
            int height = getCharMatrixHeight();
            int width = getCharMatrixWidth();
            char[][] charMatrix = new char[height][width];
            
            for (char[] row : charMatrix) {
                Arrays.fill(row, ' ');
            }
            
            return charMatrix;
        }
        
        private int getCharMatrixHeight() {
            return 5 * levels + 3;
        }
        
        private int getCharMatrixWidth() {
            int totalTextLength = 0;
            Node<K> node = head.node;
            
            while (node != null) {
                totalTextLength += 4; // Count box borders and arrow.
                String nodeText = 
                        node.key == null ?
                        " " : 
                        node.key.toString();
                
                nodeText = nodeText.replaceAll("\n", "\\\\n");
                totalTextLength += nodeText.length();
                node = node.next;
            }
            
            return totalTextLength;
        }
    }
}

com.github.coderodde.util.SkipListTest.java:

package com.github.coderodde.util;

import com.github.coderodde.util.SkipList.Index;
import com.github.coderodde.util.SkipList.Node;
import org.junit.Test;

public class SkipListTest {
    
    @Test
    public void test1() {
        SkipList<String> sl = getSkipList1();
        
        System.out.println("Print test:");
        System.out.println(sl);
    }
    
    private static SkipList<String> getSkipList1() {
        SkipList<String> sl = new SkipList<>();
        sl.size = 8;
        Node<String> nx = new Node<>(null, null);
        Node<String> n0 = new Node<>(null, null);
        Node<String> n1 = new Node<>(null, null);
        Node<String> n2 = new Node<>(null, null);
        Node<String> n3 = new Node<>(null, null);
        Node<String> n4 = new Node<>(null, null);
        Node<String> n5 = new Node<>(null, null);
        Node<String> n6 = new Node<>(null, null);
        Node<String> n7 = new Node<>(null, null);
        
        n0.key = "0";
        n1.key = "1";
        n2.key = "2";
        n3.key = "3000";
        n4.key = "4";
        n5.key = "5\n6\n\n7";
        n6.key = "6";
        n7.key = "7";
        
        nx.next = n0;
        n0.next = n1;
        n1.next = n2;
        n2.next = n3;
        n3.next = n4;
        n4.next = n5;
        n5.next = n6;
        n6.next = n7;
        n7.next = null;
        
        Index<String> il10 = new Index<>(null, null, null);
        Index<String> il11 = new Index<>(null, null, null);
        
        Index<String> il20 = new Index<>(null, null, null);
        Index<String> il21 = new Index<>(null, null, null);
        Index<String> il22 = new Index<>(null, null, null);
        Index<String> il23 = new Index<>(null, null, null);
        
        
        // Top index layer:
        il10.down = il20;
        il10.node = nx;
        il10.right = il11;
        
        il11.down = il22;
        il11.node = n4;
        il11.right = null;
        
        // Bottom index layer:
        
        il20.node = nx;
        il20.down = null;
        il20.right = il21;
        
        il21.node = n2;
        il21.down = null;
        il21.right = il22;
        
        il22.node = n4;
        il22.down = null;
        il22.right = il23;
        
        il23.node = n6;
        il23.down = null;
        il23.right = null;
        
        sl.head = il10;
        return sl;
    }
}

Critique request##

As always, I would like to receive any commentary how to make my code better.