第一題
: 關於鍊錶問題;
問題:怎樣才能檢測到鍊錶中是否存在迴圈?
對訪問過的每個元素做個標記,繼續遍歷這個鍊錶,如果遇到某個已經做過標記的元素,說明鍊錶中存在迴圈。
(如何做標記呢?思考之
…如果鍊錶中的元素全部都是正整數的話,我們可以這樣做,對訪問過的每個元素加上個負號,然後我們繼續遍歷鍊錶,如果訪問到負數了,就說明煉表裡存在迴圈。如果既有正數又有負數呢?那麼我們可以先遍歷鍊錶以此找出其中的最大值,然後重新遍歷鍊錶,並改變遍歷過的元素值,使之大於最大值。然後繼續遍歷並和最大值進行比較。如果我們遍歷到乙個值大於最大值了,那麼就證明鍊錶中存在迴圈。如果鍊錶中不是數字,是字母或是字串呢
? 對此我們可以同樣也可以做些標誌,如在我們訪問過的字串後面加上乙個數字或是字元等.)
進一步限制
: 這個鍊錶位於唯讀記憶體區域,無法在元素上做標記.
當訪問每個元素時,把它儲存在乙個陣列中。檢查每乙個後繼元素,看看它是否已經存在陣列中。
(汗,竟然這樣卡人,我認為應該首先遍歷一遍鍊錶看看鍊錶中是否存在相同的元素值。若不存在我們可以使用上面的方法,若存在我們可以首先儲存住相同元素的小標。然後再進行判斷。)
再進一步限制
: 噢,記憶體空間有限,無法建立乙個足夠長度的陣列,然而,可以假定如果鍊錶中存在迴圈,它出現在前
n個元素之中.
設定乙個指標指向鍊錶的頭部,然後依次比較該值和後
n-1個元素,若存在相同的則說明有迴圈,否則,指標後移,比較它後面的
n-i個元素。
(噢,這樣的方法確實節省記憶體空間,僅使用了乙個指標的記憶體空間。不過可能需要多次遍歷鍊錶。不過這也是沒辦法了)
繼續限制:噢!不!鍊錶的長度是任意的,而且迴圈可能出現在任意的位置。
首先排除一種特殊情況,就是
3個元素的鍊錶中第
2個元素的後面是第
1個元素。設定兩個指標p1和
p2,p1指向第乙個元素,
p2指向第三個元素,看看他們是否相等,如果相等就屬於上述這種情況,如果不等,把
p1後移乙個元素,
p2向後移兩個元素。檢查兩個指標的值,如果相等,說明鍊錶中存在迴圈,如果不等,繼續按照前述方法進行。如果出現兩個指標都為
null
,說明鍊錶中不存在迴圈。如果鍊錶中存在迴圈,用這種方法肯定能夠檢測出來,因為其中乙個指標一定能夠追上另乙個。
(這種方法的關鍵點就在,
p1後移乙個元素,
p2後移兩個元素,這樣我們讓p2追
p1,如果存在迴圈,
p2肯定能夠追上
p1.)
2 系統呼叫和庫函式呼叫
通過這個問題,可以判斷候選人是否具有豐富的程式設計經驗以及是否具有找出這類問題答案的敏銳感覺。
簡明的回答是:函式庫呼叫是語言或應用程式的一部分,而系統呼叫是作業系統的一部分。你要確保弄懂「
trap
(自陷)」這個關鍵字的含義。系統呼叫是在作業系統核心發現乙個「
trap
」或中斷後進行的。
※函式庫呼叫
vs 系統呼叫
函式庫呼叫
系統呼叫
在所有的
ansi c
編譯器版本中,
c庫函式是相同的
各個作業系統的系統呼叫是不同的
它呼叫函式庫中的一段程式(或函式)
它呼叫系統核心的服務
與使用者程式相聯絡
是作業系統的乙個入口點
在使用者位址空間執行
在核心位址空間執行
它的執行時間屬於「使用者時間」
它的執行時間屬於「系統」時間
屬於過程呼叫,呼叫開銷較小
需要在使用者空間和核心上下文環境間切換,開銷較大 在
c函式庫
libc
中有大約
300個函式
在unix
中大約有
90個系統呼叫
典型的c函式庫呼叫:
system fprintf malloc
典型的系統呼叫:
chdir fork write brk;
庫函式呼叫通常比行內展開的**慢,因為它需要付出函式呼叫的開銷。但系統呼叫比庫函式呼叫還要慢很多,因為它需要把上下文環境切換到核心模式。
其他解答:
庫函式一般完成常見的特定功能,一般由某個組織製作發布,並形成一定的標準,庫函
數一般可以應用於不同的平台而不需要做任何修改。例如,c 函式庫能夠被絕大多數c 編譯
器支援。
系統呼叫函式一般與作業系統相關,不同的作業系統所使用的系統呼叫可能不太一樣。
一般來說,如果兩個作業系統差異很大,系統呼叫函式的可移植性就不高。例如windows 採
用了系統呼叫的應用程式不能直接在linux 下編譯執行。系統呼叫函式很多情況下需要訪問
系統特殊資源,使用系統呼叫時,該程式的狀態將從使用者態切換到核心態。圖1-1 所示為
linux
函式庫呼叫和系統呼叫示意圖。
圖1-1 庫函式呼叫和系統呼叫示意圖
庫函式在實現中也有可能需要使用系統呼叫,但它封裝了系統呼叫部分的操作。使用者不
必關心它使用了哪些系統呼叫。另外,進行上層應用程式開發時也沒有必要深入研究系統調
用函式的具體實現過程。 3
編寫一些**,確定乙個變數是有符號數還是無符號數
顯然這個問題我們無法使用函式來進行實現,因為函式我們都是直接定義好了的變數,他們的型別都是已知了的。故我們選擇使用巨集來實現這個。
我們都明白,乙個無符號數在任何時候都不會是負數的,但是它的補碼是負數。所以我們可以使用這樣的巨集來進行判斷:
#define
isunsigned(a) (a >= 0 &&
~a >= 0)
如果是型別的話,我們可使用下面你的巨集:
#define
isunsigned(type) ((type)0 - 1 > 0)
這個巨集的意思是,將0轉換為相應的型別,如果是無符號數,-1之後就會得到最大的那個數值,負責將會得到-1.從而我們就可以判斷所給的型別到底是無符號的還是有符號的了。
一些程式設計上的策略
這裡將向大家分享的是一些我對程式設計的思考總結,這些經驗在我畢生程式設計生涯中曾幫助我在無數的事情上作出正確的決定。這些程式設計策略有些是很顯然的,但實際程式設計中往往被人們忽略。下面的例子是用python寫的,但這些概念適用於任何程式語言。找出程式的主執行路徑 你的程式大部分時間都執行這些模組。首...
一些程式設計上的小技巧
下面介紹的方法 程式設計珠璣 裡也有所提及 下面的例子是用python寫的,但這些概念適用於任何程式語言。找出程式的主執行路徑 你的程式大部分時間都執行這些模組。首先優化這部分 但也不要在程式實現的第一次迭代中進行優化。那些處理邊界情況或失敗 異常處理的地方,這部分 不需要優化,除非它們引起了值得注...
一些程式設計上的小技巧
下面介紹的方法 程式設計珠璣 裡也有所提及 下面的例子是用python寫的,但這些概念適用於任何程式語言。找出程式的主執行路徑 你的程式大部分時間都執行這些模組。首先優化這部分 但也不要在程式實現的第一次迭代中進行優化。那些處理邊界情況或失敗 異常處理的地方,這部分 不需要優化,除非它們引起了值得注...