实现一个时间复杂度小于O(n)的list

题目
  • 众所周知,普天同庆,本宝宝10-15裸辞了。本想做个勇士,脚踩七彩祥云,纵身投入花花世界溜达一圈。但怎奈何呀,我呸,怎奈何,理想很丰满,现实很骨感。苦于无人做伴,再者大盘受创。money在哭泣,内心很焦虑,so,投了投简历。
  • 试题
Design a special linked lists with Java implementation.(提示:时间复杂度都小于O(n))
Attention: Follow the instructions in the comments.

public interface LinkList {
 // time complexity less than O(n)
   // if value exists return true otherwise return false
   boolean isExists(Integer value);
  
   // time complexity less than O(n)
   void add(Node node);
}

//------------
// Main class
//------------
public class LinkListImpl implement LinkList {
  
}

public class Node {
 private Integer value;
  // You can add fields if you need
}

//----------
// unit test
//----------
// use: Junit4
// test function which you implement
public void Test{
   // function: isExists and add
}
答题思路
  • 第一想到的是hashmap的put方法和containsKey方法。因为感觉(好吧,或许雌性动物都爱凭第一直觉,但不幸啊,第六感往往是真的,第一直觉嘛,呵呵哒)hashmap这么常用的一个数据结构,不可能说整出来个O(n)的时间复杂度吧,PS:我只说是链表部分。后来一想,我勒个去,后来搞了树,我搞个泥巴。其实插入新节点,维护一个头指针,每次写到链表头部就OK。问题是isExists方法,怎么才能做到时间复杂度小于O(n).
  • 第二想到的是双向链表,但也没办法实现查找复杂度小于O(n).所以,顿觉,关键点就在于怎么能够用时间复杂度小于O(n)的情景去实现isExists方法,这个方法说白了就是查找。
  • 第三想到的是号称无所不能的二分查找,我记得二分查找的时间复杂度就是小于O(n)的。但问题是什么呢,第一:假如现在10个元素,我先比较头,再比较尾。如果还是找不着,就开启真正的查找。比如我第一次比较第5个元素,第5个,这个5只是一个index脚标,我要比较的是第5个脚标对应node的value值,我要拿到这个value值,就仍然需要从头遍历到第5个node。哦豁,小于O(n)时间复杂度一下子就被干翻了。第二:如果要用到二分查找,那么就必须保证add插入方法是保证整条链表有序的。把一个新的node节点有序的写入,这下子可不是直接写到头结点就完事儿了的。又得来一次遍历。哦豁,小于O(n)时间复杂度两下子就又被干翻了。PS:插一句哈,为啥说二分查找无所不能,我那个去美团写中间件的小伙伴说,她当时忘了去哪一家面试来着,人家叫她写一个算法,忘了是开方还是开立方根了哈。总之最后人家跟她说从二分查找思路入手。
  • 第四想明白了关键点是保证既定时间复杂度下的查找,只要能找到对应的节点,通过双链表的左右指针,就可以在对应时间复杂度的情况下add写入一个新节点。哈哈哈,至此,恍然大悟。查找嘛,我勒个去,写代码多年,面试也多次,最常见被问的就是:怎么优化SQL慢查询?这个问题,索引是逃不过的。既然索引大法已经祭出,答案也就自然出来了。 本来如果全表扫描,就是O(n)呀。加了索引就能优化查询,那么通常的结果也大概率是小于O(n)的。常见的就是hash索引,B树。但这两种吧,和主题linklist有些偏差哈。但在索引界,还有一种索引表现形式:跳跃链表。事已至此,哈哈哈,我特么管它跳不跳的,后边跟个链表就是无敌。这就意味着,我只要按照跳跃链表的实现方案去实现就OK了。
  • 两个问题:为什么MySQL没有用跳表去做索引?为什么Redis偏偏用了跳表去实现ZSet?
代码
  • 看了看跳跃链表的实现思路,献上纯手工撸的代码。PS:以前就知道有跳表这么个东西,没具体看过,之前人家一跟我说Redis的ZSet底层实现什么跳表,我就懵逼。
Node
/**
 * @author hehongxia
 * @since 2020年10月25日01:48:36
 * @version 1.0.0
 */
public class Node {

    private Integer value ;
    private Node left ;
    private Node right ;
    private Node up ;
    private Node down ;

    public Node(Integer value){
        this.value = value ;
    }

    @Override
    public String toString(){
        return "value is : "+ value;
    }
    
}

LinkList
/**
 * @author hehongxia
 * @since 2020年10月25日01:46:36
 * @version 1.0.0
 */
public interface LinkList {

    /**
     * time complexity less than O(n)
     * @param node the new Node to add for the list
     */
    void add(Node node);

    /**
     * get the list's size
     * @return list's size
     */
    Integer size();

    /**
     * time complexity less than O(n)
     * @param value the value to find in list
     * @return if value exists then return true otherwise return false
     */
    Boolean isExists(Integer value);

}

LinkListImpl
import java.util.Objects;
import java.util.Random;

/**
 * @author hehongxia
 * @version 1.0.0
 * @since 2020年10月25日02:05:55
 */
public class LinkListImpl implements LinkList {

    private Integer nodeNumber;
    private Integer nodeHeight;
    private Node listHead;
    private Node listTail;
    private Random heightRandom;

    public LinkListImpl() {
        this.listHead = new Node(Integer.MIN_VALUE);
        this.listTail = new Node(Integer.MAX_VALUE);
        listHead.setRight(listTail);
        listTail.setLeft(listHead);
        this.nodeNumber = 0;
        this.nodeHeight = 0;
        this.heightRandom = new Random();
    }


    /**
     * time complexity less than O(n)
     *
     * @param addNode the new Node to add for the list
     */
    @Override
    public void add(Node addNode) {

        if(Objects.isNull(addNode) || Objects.isNull(addNode.getValue())){
            throw new IllegalArgumentException("param is illegal");
        }

        Node preNode;
        int level = 0;

        preNode = findNode(addNode.getValue());
        // if the node exist,just ignore or add it as a new Node to the right for the old equals node
        // here, add it
        if (Objects.equals(preNode.getValue(), addNode.getValue())) {
            // return ;
            if (Objects.nonNull(preNode.getDown())) {
                while (Objects.isNull(preNode.getDown())) {
                    preNode = preNode.getDown();
                }
            }
        }
        // add the node
        addNode.setLeft(preNode);
        addNode.setRight(preNode.getRight());
        preNode.getRight().setLeft(addNode);
        preNode.setRight(addNode);

        // update the higher node
        double standard = 0.5;
        int nodeHighest = 3;
        boolean firstCreateLevel = true;
        while (this.heightRandom.nextDouble() < standard && level < nodeHighest) {

            if (level >= this.nodeHeight && firstCreateLevel) {
                //Create a new empty TOP layer
                addEmptyLevel();
                firstCreateLevel = false;
            }

            while (Objects.isNull(preNode.getUp()) && Objects.nonNull(preNode.getLeft())) {
                preNode = preNode.getLeft();
            }

            preNode = preNode.getUp();

            Node addUpNode = new Node(addNode.getValue());
            addUpNode.setLeft(preNode);
            addUpNode.setRight(preNode.getRight());
            addUpNode.setDown(addNode);

            preNode.getRight().setLeft(addUpNode);
            preNode.setRight(addUpNode);
            addNode.setUp(addUpNode);

            addNode = addUpNode;
            level = level + 1;
        }

        this.nodeNumber = nodeNumber + 1;
    }

    /**
     * get the list's size
     *
     * @return list's size
     */
    @Override
    public Integer size() {
        return this.nodeNumber;
    }

    /**
     * time complexity less than O(n)
     *
     * @param value the value to find in list
     * @return if value exists then return true otherwise return false
     */
    @Override
    public Boolean isExists(Integer value) {

        if(Objects.isNull(value)){
            throw new IllegalArgumentException("param is illegal");
        }

        Node node = findNode(value);
        // now, already find the node witch one the value not less than.
        // if the two values id equal,return the node
        return Objects.equals(node.getValue(), value);

    }

    /**
     * to find a Node
     *
     * @param value the Node's value
     * @return if the value is find out then return the Node, otherwise return null
     */
    private Node findNode(Integer value) {

        Node node = listHead;

        while (true) {

            // to find the right Node until find the first larger one or equal one
            // foe example value=626, Ps: why the number is 626? aha,it's my birthday.
            //  126->220->626->813->1223
            //            |
            //            node (stop find)
            while (node.getRight().getValue() != Integer.MAX_VALUE && node.getRight().getValue() <= value) {
                node = node.getRight();
            }

            if (Objects.equals(node.getValue(), value)) {
                // if find it out from higher level,return it
                break;
            }

            if (Objects.isNull(node.getDown())) {
                // the search already reached the lowest level, but the value doesn't equals 626,just return null
                break;
            } else {
                // continue to find the value in lower layer from current node's right node to the next larger
                node = node.getDown();
            }
        }

        return node;

    }

    /**
     * to create a new higher layer
     */
    private void addEmptyLevel() {

        Node newLeftNode, newRightNode;
        newLeftNode = new Node(Integer.MIN_VALUE);
        newRightNode = new Node(Integer.MAX_VALUE);

        newLeftNode.setRight(newRightNode);
        newLeftNode.setDown(this.listHead);

        newRightNode.setLeft(newLeftNode);
        newRightNode.setDown(this.listTail);

        this.listHead.setUp(newLeftNode);
        this.listTail.setUp(newRightNode);
        this.listHead = newLeftNode;
        this.listTail = newRightNode;
        this.nodeHeight = nodeHeight + 1;

    }
}

unit test
import org.junit.Assert;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;

/**
 * @author hehongxia
 */
@FixMethodOrder(MethodSorters.JVM)
public class LinkListImplTest {

    private LinkListImpl linkList = new LinkListImpl();

    @Test
    public void add() {

        Node node = new Node(525);
        linkList.add(node);
        Assert.assertEquals("node.value=525写入异常", 1, linkList.size().longValue());

    }

    @Test
    public void isExists() {

        Node nodeOne = new Node(525);
        linkList.add(nodeOne);
        Assert.assertFalse(linkList.isExists(626));
        Node nodeTwo = new Node(626);
        linkList.add(nodeTwo);
        Assert.assertTrue(linkList.isExists(626));

    }

}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值