一道簡單的面試題,據說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"的水平。好吧,被打擊了。但是博客還是要繼續寫下去的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM