从链表中删除数据的时间复杂度真的是O(1)吗?
本文經授權轉載自微信公眾號:小爭哥(xiaozhengge0822),作者:小爭哥
數組和鏈表作為最基礎的數據結構,在面試的時候,經常會被問到。最常被問到的一個問題,那就是,對比一下數組和鏈表。如果你是Java工程師,會被問到可能就是對比一下ArrayList和LinkedList。
從我面試的經歷來看,最常聽到的答案就是:數組查找的時間復雜度是O(1),但是刪除、插入數據的時間復雜度是O(n);而鏈表查找的時間復雜度是O(n),但是刪除、插入數據的時間復雜度是O(1)。
當然,這樣的答案并不全錯,但是過于籠統,過于教科書。為什么我會這么說呢?
這里有幾點回答不準確的地方,我一一糾正一下。
1. "數組查找的時間復雜度是O(1)"--描述不準確
實際上,在數組中查找一個數據,并不快速。打個比方,如果我們數組中存儲的是10個整數,2、3、1、19、...、12,如果我們要在里面查找數據12,我們還是要一個一個的遍歷整個數組元素,所以時間復雜度仍然是O(n)。即便數組中存儲的數據是有序的,我們利用二分查找算法來查找,那時間復雜度也只能做到O(logn)。
也就是說,在數組中,按照元素的值來查找,時間復雜度并不是O(1)。所以,“數組查找的時間復雜度是O(1)”表述是不對的。那正確是表述應該是怎樣的呢?
正確的表述應該是:“數組中按照下標隨機訪問的時間復雜度是O(1)”。這也是數組這種數據結構最大的特點之一了。如果在面試中,你能如此回答,面試官肯定會對你刮目相看。
2. “鏈表刪除數據的時間復雜度是O(1)”--描述不準確
實際上,這句話也不是全錯,主要還是描述不夠準確。鏈表的刪除操作可以分為好幾種情況,我們現在列出三種,將其定義為三個刪除函數。
void remove(Node* list, int dataValueToBeDeleted); void remove(Node* list, Node *pNodeToBeDeleted); void remove(Node* list, Node* pPreviousNodeToBeDeleted);我們逐一來看下,每個不同的刪除方式,時間復雜度都是多少。
1)第一種刪除方式:void remove(Node* list, int dataValueToBeDeleted);
其中,list表示鏈表,dataValueToBeDeleted參數表示要刪除的元素的值。比如,鏈表中存儲了5個數據,分別是:4、7、9、1、3,當dataValueToBeDeleted=9的時候,表示刪除鏈表中值等于9的那個結點。
對于這種刪除方式來說,我們總是要從鏈表頭開始遍歷整個鏈表數據,才能找到存儲了數據9的那個結點,所以,遍歷鏈表操作的時間復雜度是O(n)。
當通過遍歷找到這個要刪除的結點的時候,我們刪除它就很快速了,因為在遍歷的過程中,我們可以事先記錄要刪除結點的前驅結點。有了前驅結點,我們只需要簡單的指針操作,就可以將要刪除的結點刪除。這一步操作的時間復雜度是O(1)。
所以,根據大O時間復雜度表示法中的加法法則,這種刪除操作的總的時間復雜度是O(n)。
2)第二種刪除方式:void remove(Node* list, Node *pNodeToBeDeleted);
這種刪除方式中,list仍然表示鏈表,而我們pNodeToBeDeleted表示要刪除的結點的指針,反過來說,也就是,我們要刪除pNodeToBeDeleted這個指針指向的結點。
對于這種刪除方式,我們也要分兩種情況來考慮。
如果list鏈表是單鏈表,那這種刪除方式的時間復雜度是O(n)。因為,我們要刪除pNodeToBeDeleted結點,就要先找到它的前驅結點。而在單鏈表中,查找一個結點的前驅結點的時間復雜度是O(n)。所以,對于單鏈表來說,這種刪除操作的時間復雜度是O(n)。
如果list鏈表是雙向鏈表,那這種刪除方式的時間復雜度是O(1)。因為,在雙向鏈表中,我們可以快速地得到pNodeToBeDeleted結點的前驅結點,時間復雜度是O(1)。
3)第三種刪除方式:void remove(Node* list, Node* pPreviousNodeToBeDeleted);
這種刪除方式表示,我們刪除pPreviousNodeToBeDeleted指針指向的下一個結點。這里注意,并不是要刪除pPreviousNodeToBeDeleted指針指向的結點。
因為我們已經拿到了要刪除結點的前驅結點,所以,不管是單鏈表還是雙向鏈表,對于這種刪除方式,時間復雜度都是O(1)。
3. “鏈表插入數據的時間復雜度是O(1)”--描述不準確
對于插入的情況,跟刪除情況類似,也是描述不夠準確。在鏈表中插入數據,有很多種情況。比如,我們定義的下面這幾個函數表示的情況。
void insertBefore(Node* p, int dataValue); void insertAfter(Node* p, int dataValue);第一個函數表示,在p節點前面插入一個結點,結點值為dataValue。對于單鏈表來說,這種插入操作的時間復雜度是O(n)。對于雙向鏈表來說,這種插入操作的時間復雜度是O(1)。這里我只給出結論,具體留給你自己來分析吧。
第二個函數表示,在p節點后面插入一個結點,結點值為dataValue。不管是單鏈表還是雙向鏈表,這種插入操作的時間復雜度都是O(1)的。
當然,還有跟多的插入方式,比如下面這兩種,時間復雜度又是不一樣的。
void insertAtHead(Node* listHead, int dataValue); void insertToTail(Node* listHead, int dataValue);總結一下,也就是說,在分析鏈表和數組上操作的區別的時候,一定要描述準確各個操作是如何定義的,這樣才好具體分析時間復雜度。同樣的操作,不同的定義方式,時間復雜度是不一樣的
有道無術,術可成;有術無道,止于術
歡迎大家關注Java之道公眾號
好文章,我在看??
總結
以上是生活随笔為你收集整理的从链表中删除数据的时间复杂度真的是O(1)吗?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: springboot基于全局异常处理的简
- 下一篇: 于校园赠祥博