zoukankan      html  css  js  c++  java
  • 一道简单的面试题,据说90%人不能在30分钟内做出来

    不知道是不是我引起的话题,老赵出了个O1的面试题
    
    
    // Please write an sequence list implements the interface with the required
    // time complexity described in the comments. The users can add the same
    // element as many times as they want, but it doesn't support the null item.
    // You can use any types in .NET BCL but cannot use any 3rd party libraries.
    // PS: You don't need to consider the multi-threaded environment.
    interface IMyList<T> : IEnumerable<T>
    {
        // O(1)
        // Add an item at the beginning of the list.
        void AddFirst(T item);
        
        // O(1)
        // Add an item at the end of the list.
        void AddLast(T itme);
        
        // O(1)
        // Remove the item from the list. If the list contains multiple
        // copies/references of the item, remove one of them.
        void Remove(T item);
        
        // O(1)
        // Reverse the list.
        void Reverse();
    }
     

    这道题有实用性吗?当然有,这就是考验了你对各种数据结构的熟悉。
     
    这道题我当时的第一反应就是:
     
    insert要O1,那么需要该集合是链表结构。
     
    reverse要O1,那么该集合要符合双向链表的结构。
     
    remove O1,那么需要hash结构保持对相应结点的引用,在删除的时候还要注意不要使得指针断裂。
     
     
    但是老赵要求能在30分钟内完成,一般15分钟就要完成。
     
    因此我面对的问题是,作为冒牌.NET程序员,我对.NET中的双向链表结构的实现不是很熟悉。
     
    这时有两种选择,自己创建双链表结构,或者查找msdn找到.NET中双链表的实现。
     
    我觉得在30分钟内实现一个双链表完成这道题目对我来说基本上完不成,因为要考虑的事情比较多。因此使用内置数据结构看似是更好的做法。
     
    查找MSDN,我找到了LinkedListNode和LinkedList这两个数据结构,但是我在第一步就发现了问题:
     
    LinkedListNode的Previous属性和Next属性是只读的。我顿时疑惑产生,这样子怎么生成双链表呢?
     
    这时就需要LinkedList了。
     
     
    这时最开始的实现:

    public class MyClass<T> : IMyList<T>

        {

            bool isReversed = false;

            LinkedList<T> list = new LinkedList<T>();

            Dictionary<T, List<T>> dic = new Dictionary<T, List<T>>();

     

            public void AddFirst(T item)

            {

                if (!isReversed)

                {

                    list.AddFirst(item);

                }

                else

                {

                    list.AddLast(item);

                }

     

                CacheItemInDic(item);

            }

     

            private void CacheItemInDic(T item)

            {

                List<T> sameValueItems;

                if (!dic.TryGetValue(item,out sameValueItems))

                {

                    sameValueItems = new List<T>();

                }

                sameValueItems.Add(item);

            }

     

            public void AddLast(T item)

            {

                if (!isReversed)

                {

                    list.AddLast(item);

                }

                else

                {

                    list.AddFirst(item);

                }

     

                CacheItemInDic(item);

            }

     

            public void Remove(T item)

            {           

                T cachedItem;

                List<T> sameValueItems;

                if (!dic.TryGetValue(item, out sameValueItems))

                {

                    return;

                }

                else

                {

                    cachedItem = sameValueItems[0];

                }
              //写到这里的时候,突然发现问题

                list.Remove(item);

            }

     

            public void Reverse()

            {

                isReversed = !isReversed; ;

            }

     

            public IEnumerator<T> GetEnumerator()

            {

                throw new NotImplementedException();

            }

     

            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()

            {

                return GetEnumerator();

            }

        }
     
     
    写到这里,我意识到怎么能保证Remove是O1的呢?在链表中要remove一个元素很简单,只要交换一下指针即可。但是.NET中Node的指针是只读的。并且,Remove一个元素要先检查这个元素是否在链表中,而检查这个动作如果只有value的情况下无疑使O(N)复杂度的, 在这里我陷入了思考中……
    除非我们缓存的是一个节点而不是value,并且这个节点要能在O(1)时间内判断自己是否属于这个链表。问题来了,怎么判断呢?一个个遍历肯定是不行的,只有加入这个节点的时候,同时赋给这个节点一个指向这个节点的所属链表的指针才行。.NET中的LinkedList是不是这个样子的呢?作为一个冒牌.NET程序员还是不清楚,查MSDN吧,万幸,果然每个Node在插入LinkedList的时候都附上了相应的指针,也就是Node的list属性。
     
     
    这样我之前写的代码就错误了,做出如下修改一下:
    public class MyClass<T> : IMyList<T>
        {
            bool isReversed = false;
            LinkedList<T> list = new LinkedList<T>();
            Dictionary<T, List<LinkedListNode<T>>> dic = new Dictionary<T, List<LinkedListNode<T>>>();
            
            public void AddFirst(T item)
            {
                LinkedListNode<T> node;
                if (!isReversed)
                {
                    node = list.AddFirst(item);
                }
                else
                {
                    node = list.AddLast(item);
                }
     
                CacheItemInDic(item,node);
            }
     
            private void CacheItemInDic(T item,LinkedListNode<T> node)
            {
                List<LinkedListNode<T>> sameValueNodes;
                if (!dic.TryGetValue(item,out sameValueNodes))
                {
                    sameValueNodes = new List<LinkedListNode<T>>();
                }
                sameValueNodes.Add(node);
            }
     
            public void AddLast(T item)
            {
                LinkedListNode<T> node;
                if (!isReversed)
                {
                    node = list.AddLast(item);
                }
                else
                {
                    node = list.AddFirst(item);
                }
     
                CacheItemInDic(item, node);
            }
     
            public void Remove(T item)
            {
                LinkedListNode<T> cachedNode;
                List<LinkedListNode<T> > sameValueNodes;
                if (!dic.TryGetValue(item, out sameValueNodes))
                {
                    return;
                }
                else
                {
                    cachedNode = sameValueNodes[0];
                }
                if (cachedNode == null) return;
                list.Remove(cachedNode);
     
            }
     
            public void Reverse()
            {
                isReversed = !isReversed; ;
            }
     
            public IEnumerator<T> GetEnumerator()
            {
                LinkedListNode<T> node;
                if (!isReversed)
                    node = list.First;
                else
                    node = list.Last;
     
                while (true)
                {
                    if (node == null)
                        yield break;
     
                    yield return node.Value;
     
                    if (!isReversed)
                        node = node.Next;
                    else
                        node = node.Previous;
                }
            }
     
            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }
        }
    
     

    
    
     
    最终,边查边改,我花了一个多小时才完成这道简单的题目。按老赵的说法,就是“薪水是0"的水平。好吧,被打击了。但是博客还是要继续写下去的。
  • 相关阅读:
    jQuery火箭图标返回顶部代码
    jQuery火箭图标返回顶部代码
    jQuery火箭图标返回顶部代码
    fzu2020软工作业5
    [fiddler] 使用AutoResponder功能修改http响应
    [jest] 史莱姆也能学会的前端测试
    fzu2020软工作业3
    fzu2020软工作业4
    fzu2020软工作业2
    [python] 简易代码量统计脚本
  • 原文地址:https://www.cnblogs.com/lwzz/p/2424990.html
Copyright © 2011-2022 走看看