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 有互聯網一線大廠面試指南。