題目描述:
假設有這樣一個擁有3個操作的隊列:
1 Enqueue(v): 將v加入隊列
2 DeQueue:使隊列中的隊首元素刪除並返回此元素
3 MaxElement:返回隊列中的最大元素
請設計一種數據結構和算法,讓MaxElement操作的時間復雜度盡可能地低。
隊列是遵守“先入先出”原則的一種復雜數據結構。其底層的數據結構不一定要用數組來實現,還可以使用其他特殊的數據結構來實現,以達到降低MaxElement操作復雜度的目的。
分析與解法:
解法一
這個問題的關鍵在於取最大值的操作,並且考慮但隊列里面的元素動態增加和減少的時候,如何能夠非常快速地把最大值取出。
雖然,最直接的思路就是按傳統方式來實現隊列。利用一個數組或鏈表來存儲隊列的元素,利用兩個指針分別指向隊列的隊首和隊尾。如果采用這種方法,那么MaxElement操作需要遍歷隊列的所有元素。在隊列的長度為N的條件下,時間復雜度為O(N)。
解法二
根據取最大值的要求,可以考慮用最大堆來維護隊列中的元素。堆中每個元素都有指針指向它的后續元素。這樣,堆就可以很快實現返回最大元素的操作。同時,我們也能保證隊列的正常插入和刪除。MaxElement操作其實就是維護一個最大堆,其時間復雜度為O(1)。而入隊和出隊操作的時間復雜度為O(lgN)
開始不太能理解,后來想想好像是這樣的,比如,隊列是先進先出的,所以用一種數據結構來記錄元素的進出順序,而使用最大堆來維持找到最大值的效率。假如使用queue來存放所有的數據,當入隊的時候直接插入隊尾,而最大堆也需要將加入的值放在堆的最后,然后進行調整,直到滿足最大堆。如果是進行訪問最大值,可以直接訪問堆頂的元素。如果是進行出隊操作,從隊列的首部刪除該元素,並在最大堆中找到該元素,刪除之后,進行調整。這樣既能夠達到滿足隊列的“先進先出”,也能滿足在O(1)的條件下訪問到最大值。
解法三
曾經做過一種類似的題目是最小棧,也就是在O(1)的情況下訪問到棧中的最小元素。如是想到也可以使用這種方式來實現優先隊列,
如果使用兩個數組分別存放,第一個數組按進入的順序存放所有的元素,另一個數組中存放最大值的下標,另外還用一個變量記錄當前的最大值下標。
實現代碼:
class stack { public: stack() { stackTop=-1; maxStackItemIndex=-1; } void push(Type x) { stackTop++; if(stackTop>=Max) ; //棧滿 else { stackItem[stackTop]=x; if(x>Max()) { link2NextMaxItem[stackTop]=maxStackItemIndex; maxStackItemIndex=stackTop; } else link2NextMaxItem[stackTop]=-1; } } Type Pop() { Type ret; if(stackTop<0) ThrowException(); //已經沒有元素來,所以不能pop else { ret=stackItem[stackTop]; if(stackTop==maxStackItemIndex) { maxStackItemIndex=link2NextMaxItem[stackTop]; } stackTop--; } return ret; } Type Max() { if(maxStackItemIndex>=0) return stackItem[maxStackItemIndex]; else return -INF; } private: Type stackItem[MAXN]; int stackTop; int link2NextMaxItem[MAXN]; int maxStackItemIndex; };
這里,維護一個最大值的序列來保證Max操作的時間復雜度為O(1),相當於用空間復雜度換取了時間復雜度。
如果能夠用棧有效第實現隊列,而棧的Max操作又很容易,那么隊列的Max操作也就能有效地完成了。
一 內存泄漏
1 C++內存泄漏
在C程序中使用malloc()分配內存,使用free()來釋放內存,當未釋放不再使用的內存時就會出現“內存泄漏”
在C++程序中使用new()分配內存,使用delete()釋放內存,當未釋放不再使用的內存時就會出現“內存泄漏”
2 避免內存泄漏
每次調用malloc分配內存時,注意在以后要調用想要的free來釋放它。
3 檢測內存泄漏
內存泄漏的症狀就是罪魁進程的速度會減慢。原因是體積大的進程更有可能被系統換出,讓別的進程運行,而且大的進程在換入換出時花費的時間也更多。
觀察內存泄漏是一個兩步驟的過程。首先,使用swap命令觀察還有多少可用的交換空間,過一兩分鍾鍵入該命令兩到三次,看看可用的交換區是否在減少。還可以使用其他一些工具入netstat、vmstat等。第二個步驟是確定可疑的進程,看看它是不是該為內存泄漏負責,你可能已經知道哪個進程是罪魁禍首,不然可以使用ps -lu 用戶名命令來顯示所有進程的大小。
二、什么是大端和小端
Big-Endian和Little-Endian的定義如下:
1) Little-Endian就是低位字節排放在內存的低地址端,高位字節排放在內存的高地址端。
2) Big-Endian就是高位字節排放在內存的低地址端,低位字節排放在內存的高地址端。
舉一個例子,比如數字0x12 34 56 78在內存中的表示形式為:
1)大端模式:
低地址 -----------------> 高地址
0x12 | 0x34 | 0x56 | 0x78
2)小端模式:
低地址 ------------------> 高地址
0x78 | 0x56 | 0x34 | 0x12
可見,大端模式和字符串的存儲模式類似。
3)下面是兩個具體例子:
內存地址 | 小端模式存放內容 | 大端模式存放內容 |
0x4000 | 0x34 | 0x12 |
0x4001 | 0x12 | 0x34 |
32bit寬的數0x12345678在Little-endian模式以及Big-endian模式)CPU內存中的存放方式(假設從地址0x4000開始存放)為:
內存地址 | 小端模式存放內容 | 大端模式存放內容 |
0x4000 | 0x78 | 0x12 |
0x4001 | 0x56 | 0x34 |
0x4002 | 0x34 | 0x56 |
0x4003 | 0x12 | 0x78 |
4)大端小端沒有誰優誰劣,各自優勢便是對方劣勢:
小端模式 :強制轉換數據不需要調整字節內容,1、2、4字節的存儲方式一樣。
大端模式 :符號位的判定固定為第一個字節,容易判斷正負。