更新時(shí)間:2025-03-29 11:57:02作者:佚名
前言
在談?wù)撨@個(gè)問(wèn)題之前,我想談?wù)勔粋€(gè)問(wèn)題:“技術(shù)采訪(fǎng)中正在測(cè)試什么?”
到底要進(jìn)行技術(shù)采訪(fǎng)
今天,當(dāng)每個(gè)人都知道如何做問(wèn)題時(shí),面試官還知道每個(gè)人都可以做問(wèn)題并為面試做準(zhǔn)備,每個(gè)人都可以編寫(xiě)代碼。那么,為什么您仍在面試中研究這些問(wèn)題呢?那么,為什么有些人在編寫(xiě)代碼后仍然掛斷電話(huà)呢?
眾所周知,美國(guó)主要制造商的訪(fǎng)談中有80%正在采用算法,實(shí)際上,在過(guò)去的5 - 10年中,該算法僅在Google和Yahoo領(lǐng)導(dǎo)。盡管?chē)?guó)內(nèi)制造商對(duì)算法研究并不那么熱情,但他們?cè)絹?lái)越關(guān)注。
那么,算法采訪(fǎng)真的只是參加算法測(cè)試嗎?顯然不是。從本質(zhì)上講,什么測(cè)試是思考問(wèn)題的方式,分析和解決問(wèn)題的能力以及與同事溝通的能力,以查看您是否可以積極進(jìn)步來(lái)解決問(wèn)題。
回答想法
例程是:
盡管這是例行程序,但這不是一種有效的工作方式嗎?
當(dāng)您收到問(wèn)題時(shí),您應(yīng)該首先要澄清。因?yàn)楣ぷ骶褪沁@種情況。與提問(wèn)網(wǎng)站的網(wǎng)站不同,面試官通常不會(huì)一次給您所有條件,而是要求您考慮并詢(xún)問(wèn)他。然后,通過(guò)此鏈接,面試官將知道您如何看待問(wèn)題,您是否正在全面考慮,與他人進(jìn)行交流以及與您合作的未來(lái)是什么。
就像我們工作時(shí)一樣,我們需要不斷澄清產(chǎn)品經(jīng)理,尤其是未定義的零件。反復(fù)的討論是,銳化刀不會(huì)延遲木材的砍伐。然后,這個(gè)過(guò)程可能需要1-2周的時(shí)間,而您將不愿意開(kāi)始,否則,如果您努力工作,您將朝著錯(cuò)誤的方向發(fā)展,這將是一個(gè)完全的離開(kāi)。面試期間也是如此。撰寫(xiě)代碼后,面試官說(shuō)這不是我想問(wèn)的。當(dāng)時(shí)沒(méi)有時(shí)間,是我們?yōu)榇烁冻隽舜鷥r(jià)。
第二個(gè)分析思想是當(dāng)務(wù)之急,也是本文的核心。我將以L(fǎng)RU緩存為例,以展示我的思維方式和分析。
第三點(diǎn)是編寫(xiě)代碼沒(méi)有什么可說(shuō)的,最終需要實(shí)現(xiàn)。
第四點(diǎn)是運(yùn)行測(cè)試,許多學(xué)生可能會(huì)忘記它,因此,如果您可以主動(dòng)提出運(yùn)行測(cè)試用例,那么用一些示例進(jìn)行測(cè)試非常好。
有人說(shuō)我做了每個(gè)問(wèn)題,為什么我仍然失敗?然后比較這四個(gè)點(diǎn)以查看哪個(gè)鏈接是錯(cuò)誤的。
持續(xù)測(cè)試的原因
此外,為什么主要公司喜歡接受這個(gè)問(wèn)題?
首先,它可用于在多個(gè)方面和維度上檢查候選人:此問(wèn)題測(cè)試基本技能,測(cè)試數(shù)據(jù)結(jié)構(gòu)的理解和使用,并測(cè)試您是否可以編寫(xiě)可讀代碼。在45-60分鐘的采訪(fǎng)中找出真正的候選人水平并不容易。
其次,由于這個(gè)問(wèn)題很困難或容易,因此它可以像leetcode的問(wèn)題一樣簡(jiǎn)單,并且很難將系統(tǒng)設(shè)計(jì)的內(nèi)容包含在Redis中的近似LRU算法。
因此,跟進(jìn)可以無(wú)限地深入。如果面試官想問(wèn)您可以回答自己想要的一切,那么強(qiáng)大的雇用自然將無(wú)法逃脫。一些學(xué)生只會(huì)進(jìn)入第一或第二級(jí)。訪(fǎng)談是選擇最好的過(guò)程。其他學(xué)生將比您了解更多,并且他們具有良好的溝通能力。自然,其他人會(huì)得到報(bào)價(jià)。
讓我們以這個(gè)問(wèn)題為例。讓我們?cè)谶@里簡(jiǎn)要介紹我的思維過(guò)程,并給您一些建議。歡迎在消息區(qū)域分享您的想法。
什么是lru cachelru
LRU =最近使用的
這是一種緩存驅(qū)逐策略[1]
LRU算法假定將來(lái)將不會(huì)使用最少使用的信息,因此,當(dāng)容量受到限制時(shí),這些不經(jīng)常使用的信息可以被踢出并釋放。
例如,當(dāng)有熱門(mén)新聞時(shí),每個(gè)人都在搜索此信息。剛剛被一個(gè)人搜索的信息的可能性也高于兩天前搜索的過(guò)時(shí)新聞的可能性。因此,我們啟動(dòng)了很長(zhǎng)一段時(shí)間沒(méi)有使用的信息,也就是說(shuō)貝語(yǔ)網(wǎng)校,最近使用的信息被趕出了。
例如:我們的內(nèi)存能力為5,現(xiàn)在我們有五個(gè)1-5的數(shù)字。
我們現(xiàn)在想添加一個(gè)新號(hào)碼:6
但是容量已經(jīng)滿(mǎn),因此需要踢出一個(gè)。
然后,根據(jù)規(guī)則,您將擁有此緩存彈出策略。例如:
LRU的規(guī)則是踢出很長(zhǎng)一段時(shí)間沒(méi)有使用的內(nèi)容,其隱含的假設(shè)是,它認(rèn)為最近使用該信息的概率將來(lái)會(huì)更大。
在我們的示例中,我們踢出了最古老的1,然后變成:
連續(xù)迭代...
什么是緩存?
一個(gè)簡(jiǎn)單的理解是:保存一些可重復(fù)使用的信息,以便以后可以快速獲取信息。
至于它的存在,這是不確定的。最常見(jiàn)的是,它存在于內(nèi)存中,即內(nèi)存緩存,但也可以在內(nèi)存中存在。
還有更多用法場(chǎng)景,例如支持緩存的春季@CACH。上個(gè)月,我在工作中使用了此注釋。當(dāng)然,它是由我們公司打包的,這大大減少了對(duì)某個(gè)服務(wù)器的電話(huà)數(shù)量并解決了性能問(wèn)題。
例如,在進(jìn)行數(shù)據(jù)庫(kù)查詢(xún)時(shí),如果您不想每次要求時(shí)調(diào)用數(shù)據(jù)庫(kù),則我們將一些常用的數(shù)據(jù)存儲(chǔ)在內(nèi)存中以提高訪(fǎng)問(wèn)性能。
這個(gè)設(shè)計(jì)思想實(shí)際上遵循著名的“ 28法”。在閱讀和編寫(xiě)數(shù)據(jù)庫(kù)時(shí),每個(gè)I/O過(guò)程都會(huì)大量消耗很多,但實(shí)際上80%的請(qǐng)求使用了20%的數(shù)據(jù),因此將這20%的數(shù)據(jù)放入內(nèi)存可以大大提高整體效率。
簡(jiǎn)而言之,緩存的目的是存儲(chǔ)一些可重復(fù)使用的信息,以便可以快速獲得未來(lái)的請(qǐng)求。
LRU緩存
然后我們知道LRU和CACHE,我們將在一起是LRU CACH:
緩存滿(mǎn)足后,使用LRU算法清理老人。
詳細(xì)的思想解釋
說(shuō)了很多話(huà),讓我們來(lái)解決問(wèn)題的肉!
每個(gè)人都知道,這個(gè)經(jīng)典的問(wèn)題是使用hashmap + double鏈接列表,或使用Java中現(xiàn)成的linkedhashmap,但是為什么呢?您如何看待使用這兩個(gè)數(shù)據(jù)結(jié)構(gòu)?如果您在面試中沒(méi)有清楚地解釋這一點(diǎn),并且不要清楚地解釋思維過(guò)程,那么正確編寫(xiě)代碼是沒(méi)有用的。
與工作中的設(shè)計(jì)思想類(lèi)似,沒(méi)有人會(huì)告訴我們要使用哪些數(shù)據(jù)結(jié)構(gòu)。一般的想法是首先考慮哪些操作,然后基于這些操作,然后查看哪些數(shù)據(jù)結(jié)構(gòu)是合適的。
分析操作
讓我們分析此LRU緩存需要哪些操作:
首先,最基本的操作是能夠從中讀取信息,否則我該如何快速獲取信息?
然后,您必須添加新信息,并且新信息最近使用。
在添加新信息之前,您必須檢查是否存在任何空間。如果沒(méi)有空間,則必須先將舊的空間踢出,并且您需要能夠找到舊的舊空間并刪除它。
如果添加的新信息已經(jīng)在緩存中,則意味著密鑰已經(jīng)存在。為了更新值,您只需要調(diào)整此信息的優(yōu)先級(jí),該信息已從上次使用到最新信息。
查找數(shù)據(jù)結(jié)構(gòu)
第一個(gè)操作很明顯。我們需要一個(gè)可以快速搜索的數(shù)據(jù)結(jié)構(gòu)。它是哈希圖。如果您不了解hashmap的原理和設(shè)計(jì)規(guī)則,則可以在官方帳戶(hù)中發(fā)送消息“ hashmap”并給您熱門(mén)文章;
但是隨后的操作hashmap不再有用。 。 。
來(lái)吧,讓我們計(jì)算基本數(shù)據(jù)結(jié)構(gòu):
陣列,LinkedList,堆棧,隊(duì)列,樹(shù),BST,Heap,Hashmap
在執(zhí)行此類(lèi)數(shù)據(jù)結(jié)構(gòu)問(wèn)題時(shí),您將列出所有數(shù)據(jù)結(jié)構(gòu)并一一分析它們。有時(shí)這不是因?yàn)檫@種數(shù)據(jù)結(jié)構(gòu)不好,而是因?yàn)槠渌麛?shù)據(jù)結(jié)構(gòu)更好。
如何稱(chēng)呼更好?忘記了我們的測(cè)量標(biāo)準(zhǔn)!時(shí)間和空間復(fù)雜性,快速審查遞歸文章,并在官方帳戶(hù)中回復(fù)“遞歸”以獲取它。
然后我們的分析如下:
數(shù)組,堆棧和隊(duì)列基本上是由數(shù)組實(shí)現(xiàn)的(當(dāng)然,堆棧和隊(duì)列也可以使用linkedlist實(shí)現(xiàn)。)。插入新的,刪除舊的,然后調(diào)整訂單。數(shù)組可以完成或o(n),并且不能使用。
同樣,時(shí)間復(fù)雜度為O(logn)。
即使可能的話(huà)remove是什么意思?怎么讀,堆都是O(logn)。
linkedlist,有點(diǎn)好。按照從舊到新的順序,可以安排,刪除,插入和移動(dòng),并且它們都可以是O(1)!但是,刪除時(shí),我還需要一個(gè)以前的指針來(lái)刪除它,因此我需要一個(gè)double linkedlist。
然后,我們的數(shù)據(jù)結(jié)構(gòu)最終確定為:
hashmap + double linkedlist
清楚地定義數(shù)據(jù)結(jié)構(gòu)的內(nèi)容
選擇數(shù)據(jù)結(jié)構(gòu)后,您需要清楚地定義每個(gè)數(shù)據(jù)結(jié)構(gòu)的存儲(chǔ)內(nèi)容以及這兩個(gè)數(shù)據(jù)結(jié)構(gòu)如何相關(guān)。這是核心問(wèn)題。
讓我們首先考慮一個(gè)場(chǎng)景。在搜索引擎中,如果您輸入問(wèn)題問(wèn)題,Google將返回答案。
然后,讓我們假設(shè)存儲(chǔ)了這兩個(gè)數(shù)據(jù)結(jié)構(gòu),然后查看這些操作。如果它們倆都很平滑remove是什么意思?怎么讀,那就沒(méi)有問(wèn)題。如果有任何問(wèn)題,我們將對(duì)其進(jìn)行調(diào)整。
現(xiàn)在,我們的hashmap和linkedlist看起來(lái)像這樣:
然后讓我們回顧一下這四個(gè)操作:
操作1,沒(méi)問(wèn)題,只需閱讀hashmap的答案,o(1);
操作2。添加一組新的問(wèn)答,并且必須添加兩個(gè)數(shù)據(jù)結(jié)構(gòu)。首先,我們需要確定當(dāng)前緩存中是否存在此Q。然后,我們使用hashmap來(lái)判斷。
但是,您如何找到linkedlist的節(jié)點(diǎn)?這不是我們要尋找一個(gè)接一個(gè)的遍歷的東西,因?yàn)樗枰猳(n)時(shí)間,我們要使用o(1)時(shí)間進(jìn)行操作。
這意味著以這種方式無(wú)法錄制。您還需要在linkedlist中記錄每個(gè)listNode的位置。這是這個(gè)問(wèn)題的關(guān)鍵。
自然要記錄listNode的位置信息,即Hashmap,也就是說(shuō),保存每個(gè)listNode的參考。
考慮一下,這實(shí)際上是正確的。無(wú)需在hashmap中記錄答案。答案只需要在linkedlist中記錄。
之后,當(dāng)我們更新和移動(dòng)每個(gè)節(jié)點(diǎn)時(shí),無(wú)需更改其引用,因此不需要更改hashmap,而移動(dòng)的唯一一件事是先前的下一個(gè)指針。
然后再考慮一下,實(shí)際上無(wú)需在linkedlist中記錄問(wèn)題,無(wú)論如何,在哈希姆普中就有它。
這兩個(gè)數(shù)據(jù)結(jié)構(gòu)用于彼此協(xié)調(diào),無(wú)需記錄相同的信息。
更新的數(shù)據(jù)結(jié)構(gòu)如下:
通過(guò)這種方式,我們分析使用了哪些數(shù)據(jù)結(jié)構(gòu),每個(gè)數(shù)據(jù)結(jié)構(gòu)中存儲(chǔ)的內(nèi)容以及物理含義是什么。
實(shí)際上,Java中的LinkedHashmap實(shí)施得很好。但是,即使您可以在面試中使用它,它也會(huì)逐步派生,而不是一旦看到問(wèn)題就知道如何使用它,而是乍一看只是記住答案。
同學(xué)問(wèn)我,面試官是否問(wèn)我是否以前做過(guò)這個(gè)問(wèn)題,我應(yīng)該如何回答?
答:說(shuō)實(shí)話(huà)。
誠(chéng)意在訪(fǎng)談和工作中非常重要,所以說(shuō)實(shí)話(huà)。但是,如果面試官不問(wèn),則無(wú)需這么說(shuō)。 。 。
實(shí)際上,面試官不在乎。您之前已經(jīng)做過(guò)這個(gè)問(wèn)題,因?yàn)槊總€(gè)人都做過(guò),基本上已經(jīng)做到了。提出這個(gè)問(wèn)題是毫無(wú)意義的。只要您可以清楚地分析問(wèn)題并清楚地解釋邏輯,如果您做到了怎么辦?許多做過(guò)問(wèn)題的人無(wú)法清楚地解釋它。 。 。
總結(jié)
讓我們總結(jié)這四個(gè)操作:
第一個(gè)操作是get()API,無(wú)話(huà)可說(shuō)。
兩個(gè),三個(gè),四個(gè)是put()API,這有點(diǎn)麻煩:
繪畫(huà)時(shí),您在談話(huà)時(shí)說(shuō)話(huà)和寫(xiě)作,每個(gè)步驟均為從高級(jí)到詳細(xì)信息再到代碼,將代碼模塊化。
當(dāng)我畫(huà)這張照片時(shí),面試官?zèng)]有要求我編寫(xiě)代碼,然后直接進(jìn)入了下一個(gè)問(wèn)題...
然后,如果面試官要求您寫(xiě)作,那就寫(xiě)。 。 。
class?LRUCache?{
??//?HashMap:?
??//?LinkedList:?
??public?static?class?Node?{
??????int?key;
??????int?val;
??????Node?next;
??????Node?prev;
??????public?Node(int?key,?int?val)?{
??????????this.key?=?key;
??????????this.val?=?val;
??????}
??}
??Map?map?=?new?HashMap<>();
??private?Node?head;
??private?Node?tail;
??private?int?cap;
??public?LRUCache(int?capacity)?{
??????cap?=?capacity;
??}
??public?int?get(int?key)?{
??????Node?node?=?map.get(key);
??????if(node?==?null)?{
??????????return?-1;
??????}?else?{
??????????int?res?=?node.val;
??????????remove(node);
??????????appendHead(node);
??????????return?res;
??????}
??}
??public?void?put(int?key,?int?value)?{
??????//?先?check?有沒(méi)有這個(gè)?key
??????Node?node?=?map.get(key);
??????if(node?!=?null)?{
??????????node.val?=?value;
??????????//?把這個(gè)node放在最前面去
??????????remove(node);
??????????appendHead(node);
??????}?else?{
??????????node?=?new?Node(key,?value);
??????????if(map.size()???????????????appendHead(node);
??????????????map.put(key,?node);
??????????}?else?{
??????????????//?踢走老的
??????????????map.remove(tail.key);
??????????????remove(tail);
??????????????appendHead(node);
??????????????map.put(key,?node);
??????????}
??????}
??}
??private?void?appendHead(Node?node)?{
??????if(head?==?null)?{
??????????head?=?tail?=?node;
??????}?else?{
??????????node.next?=?head;
??????????head.prev?=?node;
??????????head?=?node;
??????}
??}
??private?void?remove(Node?node)?{
??????//?這里我寫(xiě)的可能不是最?elegant?的,但是是很?readable?的
??????if(head?==?tail)?{
??????????head?=?tail?=?null;
??????}?else?{
??????????if(head?==?node)?{
??????????????head?=?head.next;
??????????????node.next?=?null;
??????????}?else?if?(tail?==?node)?{
??????????????tail?=?tail.prev;
??????????????tail.next?=?null;
??????????????node.prev?=?null;
??????????}?else?{
??????????????node.prev.next?=?node.next;
??????????????node.next.prev?=?node.prev;
??????????????node.prev?=?null;
??????????????node.next?=?null;
??????????}
??????}
??}
}
/**
*?Your?LRUCache?object?will?be?instantiated?and?called?as?such:
*?LRUCache?obj?=?new?LRUCache(capacity);
*?int?param_1?=?obj.get(key);
*?obj.put(key,value);
*/
總結(jié)
然后,讓我們回去面試,為什么許多訪(fǎng)談都集中在算法考試上?這樣的采訪(fǎng)的原因是什么?算法問(wèn)題面試能否真正衡量一個(gè)人的工作能力? (當(dāng)然,對(duì)于某些工作經(jīng)驗(yàn),人們還將檢查系統(tǒng)設(shè)計(jì)的內(nèi)容。)
這是我一直在考慮的一個(gè)問(wèn)題,下班后,我越來(lái)越認(rèn)為這種采訪(fǎng)確實(shí)有效。
因?yàn)槲覀冃枰氖墙鉀Q未知問(wèn)題的能力,因此可能會(huì)忘記知識(shí),但是思考問(wèn)題的方式總是跟隨我們,我們需要不斷改進(jìn)。然后,在堅(jiān)實(shí)的基本技能的前提下,有正確的方法和想法可以指導(dǎo)您,沒(méi)有什么是不可能的。
參考
[1]
緩存替代政策:
重磅!程序員大白交流群-學(xué)術(shù)微信交流群已成立
額外贈(zèng)送福利資源!邱錫鵬深度學(xué)習(xí)與神經(jīng)網(wǎng)絡(luò),pytorch官方中文教程,利用Python進(jìn)行數(shù)據(jù)分析,機(jī)器學(xué)習(xí)學(xué)習(xí)筆記,pandas官方文檔中文版,effective java(中文版)等20項(xiàng)福利資源
獲取方式:掃描下方二維碼入群交流
點(diǎn)開(kāi)群公告即可領(lǐng)取下載鏈接
注意:請(qǐng)大家添加時(shí)修改備注為 [學(xué)校/公司 + 姓名 + 方向]
例如 —— 哈工大+張三+對(duì)話(huà)系統(tǒng)。
號(hào)主,微商請(qǐng)自覺(jué)繞道。謝謝!
推薦閱讀
關(guān)于程序員大白
程序員大白是一群哈工大,東北大學(xué),西湖大學(xué)和上海交通大學(xué)的碩士博士運(yùn)營(yíng)維護(hù)的號(hào),大家樂(lè)于分享高質(zhì)量文章,喜歡總結(jié)知識(shí),歡迎關(guān)注[程序員大白],大家一起學(xué)習(xí)進(jìn)步!