字節跳動提前批,面試官和我聊了半小時的優先隊列……

2020年秋天還沒到,互聯網公司便按捺不住招賢納士的步伐了,開始啓動秋季招聘的提前批工作。最近有讀者昨天剛參加完字節跳動的提前批一面,與我交流時聊了他的面試經歷,因爲之前我在春招時也面試過字節跳動。

在整個的面試流程中,字節跳動至少會有 3 輪技術面,每一輪面試都會考算法。 像這位讀者的面試中,面試官讓他寫優先隊列的實現算法並且講解思路,他回答了4種實現:無序數組、有序數組、無序鏈表和有序鏈表的實現,當時面試官誇他講得很全,結果一面過了。 今天結合讀者的面試經歷,我講一講優先隊列的四種實現方法。

優先隊列

優先隊列,與堆棧和隊列一樣,都是元素的集合。在刪除操作上,棧是先進後出的數據結構,刪除最近添加的元素;普通的隊列是一種先進先出的數據結構,元素在隊列尾追加,而從隊列頭刪除;在優先隊列中,元素被賦予優先級。當訪問元素時,具有最高優先級的元素最先刪除。優先隊列具有最高級先出 (first in, largest out)的特性。

優先級隊列的主要操作是insert,delMax(刪除最大值) 和 isEmpty(判斷是否爲空)。

優先級隊列有很多應用,例如:

  • AI中的A *搜索算法

  • 使用Huffman編碼進行數據壓縮

  • 事件-驅動模擬(Event-driven simulation)

無序數組實現優先隊列

優先級隊列類

構造一個最大優先級隊列,其中具有最大值的元素具有最高優先級。Key必須擴展Comparable接口。這是因爲我們要在優先級隊列中的元素之間進行比較,而不是在優先級隊列對象之間進行比較。

優先級隊列類:Key[] pq是通用類型的數組 ,N表示該數組中的元素數。

public class UnorderedArrayMaxPQ<Key extends Comparable<Key>> {
    private Key[] pq;  
    private int N;
	public UnorderedArrayMaxPQ(int capacity) {
	        pq = (Key[]) new Comparable[capacity];
	        N = 0;
    }
}

操作函數

insert():將元素插入數組的末尾

delMax():每次查找最大元素數時,都需要掃描所有元素。刪除最大元素的技巧是,將最大元素與數組中的最後一個元素交換,然後將其刪除。

isEmpty():判斷隊列是否爲空

//將元素插入數組的末尾
public void insert(Key item)  { pq[N++] = item;   }
//刪除(並返回)最大項
public Key delMax() {
    int max = 0;
    for (int i = 1; i < N; i++)
        if (less(max, i)) max = i;
    swap(max, N-1);
    return pq[--N];
}
//隊列是否爲空?
public boolean isEmpty()   
{ return N == 0; }

有序數組實現優先隊列

優先級隊列類

public class OrderedArrayMaxPQ<Key extends Comparable<Key>> {
    private Key[] pq;
    private int N; 
    public OrderedArrayMaxPQ(int capacity) {
        pq = (Key[]) (new Comparable[capacity]);
        N = 0;
    }
}

操作函數

insert():將元素插入到已排序位置的數組中,維護排序的數組。

delMax():max元素將始終位於末尾,將其刪除。

isEmpty():判斷隊列是否爲空。

//將元素插入到已排序位置的數組中
 public void insert(Key item){ 
    int i = N-1; 
    //只要新元素小於pq [i],就向右移動
     while (i >= 0 && less(item, pq[i])) {
        pq[i+1] = pq[i];
        i--;
    }
    pq[i+1] = item;
    N++;
}
//刪除(並返回)最大項
 public key delMax(){return pq [-N]; }
//隊列是否爲空?
public boolean isEmpty(){return N == 0; }

無序鏈表實現優先隊列

鏈表節點的實現

private class Node
{
     Key item;
     Node next;
 }

操作函數

insert():採用頭插法將元素插入鏈表。

delMax():每次查找鏈表的最大元素數時,然後將其刪除。

isEmpty():判斷鏈表是否爲空。

public boolean isEmpty()
{ return N==0;}

public void Insert(Key v)
{
   Node Item=new Node();
   Item.item=v;
   Item.next=first;
   first=Item;
   N++;
  }

public Key delMax()
{
    Node Item=new Node();
    Item.next=first;
   
    Node maxItem=first;
    Node maxItemPrev=Item;

    while(Item.next.next!=null)
    {
      if(less(maxItem.item,Item.next.next.item))
      {
          maxItem=Item.next.next;
          maxItemPrev=Item.next;
      }
      Item=Item.next;
    }
   
    Key max=maxItem.item;
    maxItemPrev.next=maxItem.next;
    if(first==maxItem) first=maxItem.next;
    maxItem=null;
    N--;
    return max;
}

完整代碼

public class UnOrderedListMaxPQ<Key extends Comparable<Key>>
{
    private class Node
    {
        Key item;
        Node next;
    }
    //無序鏈表實現優先隊列大堆
    private Node first;
    private int N=0;
    public boolean isEmpty()
    { return N==0;}
   
    public int size()
    { return N;}
   
    public void Insert(Key v)
    {
       Node Item=new Node();
       Item.item=v;
       Item.next=first;
       first=Item;
       N++;
      }
   
    public Key delMax()
    {
        Node Item=new Node();
        Item.next=first;
       
        Node maxItem=first;
        Node maxItemPrev=Item;

        while(Item.next.next!=null)
        {
          if(less(maxItem.item,Item.next.next.item))
          {
              maxItem=Item.next.next;
              maxItemPrev=Item.next;
          }
          Item=Item.next;
        }
       
        Key max=maxItem.item;
        maxItemPrev.next=maxItem.next;
        if(first==maxItem) first=maxItem.next;
        maxItem=null;
        N--;
        return max;
    }
   
    public static void main(String[] args)
    {
        E2d4d3v3 pq=new E2d4d3v3();
        pq.Insert(1);
        pq.Insert(3);
        StdOut.println(pq.delMax());
        pq.Insert(2);
        StdOut.println(pq.delMax());
        StdOut.println(pq.delMax());
    }
}
}

有序鏈表實現優先隊列

鏈表節點的實現

 private class Node
    {
        Key item;
        Node next;
    }

操作函數

insert():將元素插入鏈表,並保持鏈表的順序。

delMax():鏈表的最大元素始終位於鏈表頭部,每次刪除鏈表的最大元素時,將鏈表頭部的元素刪除。

isEmpty():判斷鏈表是否爲空。

public boolean isEmpty()
{ return N==0;}

public int size()
{ return N;}

public void Insert(Key v)
{
   Node newItem=new Node();
   newItem.item=v;
  
   Node Item=new Node();
   Item.next=first;
     
    while(Item.next!=null && less(newItem.item,Item.next.item))
         Item=Item.next;
  
    newItem.next=Item.next;
    Item.next=newItem;
    //0節點增加新節點 或 新節點爲最大時修改first
    if(first==null || first==newItem.next)   first=newItem;
    N++;
  }

public Key delMax()
{
   Node maxItem=first;
   first=first.next;
   Key max=maxItem.item;
   N--;
   return max;
}

完整代碼

public class OrderedListMaxPQ<Key extends Comparable<Key>>
{
    private class Node
    {
        Key item;
        Node next;
    }
    //有序鏈表實現優先隊列大堆,first存儲最大元素
    private Node first;
    private int N=0;
   
    
    public boolean isEmpty()
    { return N==0;}
   
    public int size()
    { return N;}
   
    public void Insert(Key v)
    {
       Node newItem=new Node();
       newItem.item=v;
      
       Node Item=new Node();
       Item.next=first;
         
        while(Item.next!=null && less(newItem.item,Item.next.item))
             Item=Item.next;
      
        newItem.next=Item.next;
        Item.next=newItem;
        //0節點增加新節點 或 新節點爲最大時修改first
        if(first==null || first==newItem.next)   first=newItem;
        N++;
      }
   
    public Key delMax()
    {
       Node maxItem=first;
       first=first.next;
       Key max=maxItem.item;
       N--;
        return max;
    }


    public static void main(String[] args)
    {
        OrderedListMaxPQ pq=new OrderedListMaxPQ();
        pq.Insert(1);
        pq.Insert(3);
        StdOut.println(pq.delMax());
        pq.Insert(2);
        StdOut.println(pq.delMax());
        StdOut.println(pq.delMax());
    }
}

總結

咱們玩歸玩,鬧歸鬧,別拿面試開玩笑!吐槽字節跳動的“凡面試必算法”的同時,也要明白爲啥很多公司要考這麼多算法?其實核心是看候選人是不是足夠聰明,很多公司招聘工程師的必要條件就是聰明。

優先隊列的實現本身不難,但是像讀者這樣,優先隊列的四種實現方式全部講出來的面試者很少。讀者順利通過了一面,接下的面試還在繼續,祝願這位讀者能順利拿到字節offer!

作者:AlexanderChen
鏈接:https://juejin.im/post/5eea2654f265da02c94e0f04
來源:掘金
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

文章持續更新,可以微信搜索「 雲璈公子 」閱讀,回覆【資料】【面試】【簡歷】有我準備的一線大廠面試資料和簡歷模板,同時我的GitHub https://github.com/1170300826/JavaInterview 有互聯網一線大廠面試指南。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章