聲明:本文題目來源於互聯網,僅供即將從學校畢業的.Net碼農(當然,我本人也是菜逼一個)學習之用。當然,學習了這些題目不一定會拿到offer,但是針對就業求職做些針對性的准備也是不錯的。此外,除了技術上的准備外,要想得到提升,還得深入內部原理,閱讀一些經典書籍(例如Jeffrey Richter的《CLR via C#》)以及借助Reflector或ILSpy反編譯查看源碼實現,知其然也知其所以然,方能得到感性認識到理性認識的飛躍!另外,本來想將本文標題取為就業求職寶典,但一想這名字太LOW了,而且太過浮華了,本文也根本達不到那個目標,於是將其改為儲備,簡潔明了。
一、准備必須熟練的技術點
1.1 原生js手寫ajax請求:不使用第三方js庫例如JQuery,借助XMLHttpRequest
注意:這里僅寫出了最基本的js代碼,至於什么二次封裝和重構各位可以自行解決;
function ajax(method, url, callback) { var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"); xhr.open(method, url, true); xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { var result = xhr.responseText; callback(result); } } xhr.send(); }
這里主要理解記憶為四個步湊即可:
(1)創建XMLHttpRequest對象:如果你足夠細心,還可以考慮一下各主流瀏覽器的兼容性;
(2)建立與服務器端的連接:借助open方法,GET還是POST?服務頁面地址是?異步還是同步?
(3)設置響應完成后的回調函數:注意條件是readyState=4且status=200的情況下,下面給出了這些條件的具體含義
屬性 |
描述 |
onreadystatechange |
每次狀態改變所觸發事件的事件處理程序 |
readyState |
對象狀態值:
|
responseText |
從服務器進程返回的數據的字符串形式 |
responseXML |
從服務器進程返回的DOM兼容的文檔數據對象 |
status |
從服務器返回的數字代碼,比如404(未找到)或200(就緒) |
statusText |
伴隨狀態碼的字符串信息 |
(4)最后正式發送請求:最后一步才是正式的發送此次Ajax請求,調用send方法;
PS:可以看看上面這段js方法具體如何應用的
View Code<script type="text/javascript"> function getServerTime() { ajax("GET", "AjaxHandler.ashx?action=gettime", afterSuccess); } function afterSuccess(data) { if (data != null) { document.getElementById("spTime").innerHTML = data; } } function ajax(method, url, callback) { var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"); xhr.open(method, url, true); xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { var result = xhr.responseText; callback(result); } } xhr.send(); } </script> </head> <body> <div align="center"> <input id="btnAjax" type="button" value="Get Server Time" onclick="getServerTime()" /> <br /> <span id="spTime" style="font-weight:bold;"></span> </div> </body>
1.2 基本排序算法:冒泡排序與快速排序
1.2.1 冒泡排序
(1)基本概念
冒泡排序(Bubble Sort),是一種計算機科學領域的較簡單的排序算法。
它重復地走訪過要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來。走訪數列的工作是重復地進行直到沒有再需要交換,也就是說該數列已經排序完成。這個算法的名字由來是因為越大的元素會經由交換慢慢“浮”到數列的頂端,故名。
下圖是一個經典的冒泡排序過程圖,可以看出,在排序過程中,大的記錄像石頭一樣“沉底”,小的記錄逐漸向上“浮動”,冒泡排序的名字也由此而來。
圖1 冒泡排序過程模擬
(2)算法過程
①比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。
②對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最后一對。這步做完后,最后的元素會是最大的數。
③針對所有的元素重復以上的步驟,除了最后一個。
④持續每次對越來越少的元素重復上面的步驟,直到沒有任何一對數字需要比較。
(3)代碼實現

public static void BubbleSort(int[] array) { int temp; bool flag; for (int i = array.Length - 1; i >= 1; i--) { flag = false; for (int j = 1; j <= i; j++) { if (array[j - 1] > array[j]) { temp = array[j - 1]; array[j - 1] = array[j]; array[j] = temp; flag = true; } } if (!flag) { return; } } }
這里為什么要借助flag標志位來判斷?因為如果在一趟排序中沒有發生元素交換,那么數組中的數據都已經有序了,這時就無需再繼續比較了,這也是冒泡排序算法結束的條件。
(4)測試結果
①這里首先使用一個包含10000個(本來想再對10萬個,100萬個進行測試,但是太懶了,所以...)隨機數的int數組簡單進行了五次測試,平均耗時每次812ms。

public static void BubbleSortDemo() { int maxSize = 10000; int[] array = new int[maxSize]; Random random = new Random(); for (int i = 0; i < maxSize; i++) { array[i] = random.Next(1, 10001); } Console.WriteLine("Before Bubble Sort:"); SortHelper.PrintArray(array); Stopwatch watcher = new Stopwatch(); watcher.Start(); SortHelper.BubbleSort(array); watcher.Stop(); Console.WriteLine("------------------------------------------------------------"); Console.WriteLine("After Bubble Sort:"); SortHelper.PrintArray(array); Console.WriteLine("Total Elapsed Milliseconds:{0}ms", watcher.ElapsedMilliseconds); }
②這里再使用一個包含10000個有序數據的int數組進行幾次測試,發現平均耗時均為0ms。
(5)復雜度分析
①時間復雜度
若待排序文件的初始狀態是正序的,一趟掃描即可完成排序(這里也解釋了我們為什么剛剛在代碼中設置一個flag標志)。所需的關鍵字比較次數C和記錄移動次數M均達到最小值:,
。所以,冒泡排序最好的時間復雜度為
。
若待排序文件是反序的,需要進行 n - 1 趟排序。每趟排序要進行 n - i 次關鍵字的比較(1≤i≤n-1),且每次比較都必須移動記錄三次來達到交換記錄位置。在這種情況下,比較和移動次數均達到最大值:
所以,冒泡排序的最壞時間復雜度為。
綜上所述,冒泡排序總的平均時間復雜度為。
②空間復雜度
由算法代碼可以清晰地看出,額外的輔助空間只有一個temp,因此空間復雜度為O(1)。
1.2.2 快速排序
(1)基本概念
快速排序(Quick Sort)是對冒泡排序的一種改進,由C. A. R. Hoare在1962年提出。它采用了一種分治的策略,通常稱其為分治法(Divide-and-ConquerMethod)。
它的基本思想是:通過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一部分的所有數據都要小,然后再按此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數據變成有序序列。
(2)算法過程
快速排序使用分治法(Divide and conquer)策略來把一個串行(list)分為兩個子串行(sub-lists)。具體步驟為:
圖2 快速排序過程模擬
- 從數列中挑出一個元素(一般都選擇第一個),稱為 "基准"(pivot),
- 重新排序數列,所有元素比基准值小的擺放在基准前面,所有元素比基准值大的擺在基准的后面(相同的數可以到任一邊)。在這個分區退出之后,該基准就處於數列的中間位置。這個稱為分區(partition)操作。
- 遞歸地(recursive)把小於基准值元素的子數列和大於基准值元素的子數列排序。
(3)代碼實現

public static void QuickSort(int[] array, int low, int high) { if (low < high) { int index = Partition(array, low, high); QuickSort(array, low, index - 1); QuickSort(array, index + 1, high); } } public static int Partition(int[] array, int low, int high) { int i = low; int j = high; int temp = array[low]; while (i != j) { // 先判斷右半部分是否有小於temp的數,如果有則交換到array[i] while (i < j && temp < array[j]) { j--; } if (i < j) { array[i++] = array[j]; } // 在判斷左半部分是否有大於temp的數,如果有則交換到array[j] while (i < j && temp > array[i]) { i++; } if (i < j) { array[j--] = array[i]; } } array[i] = temp; return i; }
(4)測試結果
①這里仍然使用一個包含10000個隨機數的int數組簡單進行了五次測試,平均耗時每次3ms。相比冒泡排序的平均耗時800+ms,快速排序果然名不虛傳,快的不是一點半點啊!

public static void QuickSortDemo() { int maxSize = 10000; int[] array = new int[maxSize]; Random random = new Random(); for (int i = 0; i < maxSize; i++) { array[i] = random.Next(1, 10001); } Console.WriteLine("Before Quick Sort:"); NewSortHelper.PrintArray(array); Stopwatch watcher = new Stopwatch(); watcher.Start(); NewSortHelper.RecursiveQuickSort(array, 0, array.Length - 1); watcher.Stop(); Console.WriteLine("------------------------------------------------------------"); Console.WriteLine("After Quick Sort:"); NewSortHelper.PrintArray(array); Console.WriteLine("Total Elapsed Milliseconds:{0}ms", watcher.ElapsedMilliseconds); }
②同樣,這里再使用一個包含10000個有序數據的int數組進行五次測試,發現平均耗時為343ms。這里也可以跟冒泡排序在此種情形下的耗時進行對比,發現快排在接近有序的情景時弱爆了。
(5)復雜度分析
①時間復雜度:
快速排序的時間主要耗費在划分(Partition)操作上,對長度為k的區間進行划分,共需k-1次關鍵字的比較。
假設有1到8代表要排序的數,快速排序會遞歸log(8)=3次,每次對n個數進行一次處理,所以他的時間復雜度為n*log n即O(n log n)。所以排序問題的時間復雜度可以認為是對排序數據的總的操作次數。
但是,比如一個序列5,4,3,2,1,要排為1,2,3,4,5。按照快速排序方法,每次只會有一個數據進入正確順序,無法把數據分成大小相當的兩份,很明顯,排序的過程就成了一個歪脖子樹,樹的深度為n,那時間復雜度就成了O(n2)。這也就解釋了為什么在剛剛的第二次有序數據測試時,快排仍然需要耗費一些時間了。
盡管快速排序的最壞時間為O(n2),但就平均性能而言,它是基於關鍵字比較的內部排序算法中速度最快者,快速排序亦因此而得名。它的平均時間復雜度為O(n log n)。
②空間復雜度:
這里快速排序是以遞歸形式進行的,而遞歸又需要棧的輔助,因此它所需要的輔助空間比冒泡排序多,因此其空間復雜度為O(log n)。這里可以看出,快速排序是典型的以空間換時間的經典案例。
1.3 基本查找算法:順序查找與二分查找
1.3.1 順序查找
順序查找是一種最基本最簡單的查找方法,它的基本思路是:從表的一段開始,順序掃描線性表,依次將掃描到的關鍵字與給定值K進行比較,若比較相等,則查找成功;若掃描結束后,仍未發現關鍵字等於K的記錄,則查找失敗。
其代碼實現也很簡單:

public static int SimpleSearch(int[] array, int key) { int result = -1; for (int i = 0; i < array.Length; i++) { if (array[i] == key) { result = i + 1; break; } } return result; }
優點:最簡單,對元素排列次序無要求,插入新元素方便。
缺點:速度慢,平均查找長度為(n+…+2+1)/n=(n+1)/2,約為表長度一半。
1.3.2 二分查找
二分查找又稱折半查找,它首先要求線性表是有序的,即表中記錄按關鍵字有序(比如:遞增有序或遞減有序)。
其基本思路是:設有序表A[0]~A[n-1]
①首先取中點元素A[mid]的關鍵字同給定值K進行比較,若相等則查找成功;否則,若K< A[mid].key,則在左子表中繼續進行二分查找;若K> A[mid].key,則在右子表中繼續進行二分查找;
②這樣,經過一次比較,就縮小一半查找空間,如此進行下去,直到查找成功,或者當前查找區間為空時為止(或區間的下界大於等於上界時為止)。
通過基本思路寫出代碼實現:

public static int BinarySearch(int[] array, int key) { int low = 0; int high = array.Length - 1; int mid = -1; while (low <= high) { mid = (low + high) / 2; if (array[mid] > key) { high = mid - 1; } else if (array[mid] < key) { low = mid + 1; } else { return mid + 1; } } return -1; }
通過以上的分析,我們也可以方便地得出其優缺點如下:
優點:時間復雜度為O(logn),查找速度快。
缺點:需要建立有序表,並且插入和刪除會比較麻煩。另外,只適用於順序存儲的有序表,不適用於鏈接存儲的有序表。
1.4 常見的設計模式:單例模式、工廠模式、觀察者模式、裝飾模式與適配器模式
這里可以閱讀Terry Lee的設計模式系列來理解學習一下
1.4.1 單例模式
.NET設計模式(2):單件模式(Singleton Pattern)
http://terrylee.cnblogs.com/archive/2005/12/09/293509.html
1.4.2 抽象工廠模式
.NET設計模式(3):抽象工廠模式(Abstract Factory)
http://terrylee.cnblogs.com/archive/2005/12/13/295965.html
1.4.3 觀察者模式
.NET設計模式(19):觀察者模式(Observer Pattern)
http://www.cnblogs.com/Terrylee/archive/2006/10/23/Observer_Pattern.html
1.4.4 裝飾模式
.NET設計模式(10):裝飾模式(Decorator Pattern)
http://terrylee.cnblogs.com/archive/2006/03/01/340592.html
1.4.5 適配器模式
.NET設計模式(8):適配器模式(Adapter Pattern)
http://terrylee.cnblogs.com/archive/2006/02/18/333000.html
1.4.6 外觀模式
.NET設計模式(12):外觀模式(Facade Pattern)
http://terrylee.cnblogs.com/archive/2006/03/17/352349.html
1.5 通過ADO.Net訪問數據庫與SQLHelper的封裝
1.5.1 ADO.Net訪問數據庫
假設數據庫有一張Student表,其中有四個字段:S#-學號,Sname-姓名,Sage-年齡,Ssex-性別;我們先新增一個頁面,取名為AdoNetDemo,html中不添加任何內容;在.cs文件中,寫入以下代碼,通過ADO.Net訪問數據庫,並將性別為男生的學生信息輸出到頁面中;

public partial class AdoNetDemo : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { BindDataInfos(); } } private void BindDataInfos() { string connstr = ConfigurationManager.ConnectionStrings["connStr"].ConnectionString; using (SqlConnection con = new SqlConnection(connstr)) { con.Open(); using (SqlCommand cmd = con.CreateCommand()) { cmd.CommandText = "select * from Student where Ssex=@sex"; cmd.Parameters.Add(new SqlParameter("@sex", "男")); using(SqlDataAdapter adapter = new SqlDataAdapter(cmd)) { DataTable dt = new DataTable(); adapter.Fill(dt); if(dt != null) { foreach(DataRow row in dt.Rows) { Response.Write(string.Format("學號:{0},姓名:{1},年齡:{2}</br>", row["S#"].ToString(), row["Sname"].ToString(), row["Sage"].ToString())); } } } } } } }
這里主要是看BindDataInfos這個方法,借助ADO.Net中最基本的幾個對象(Connection、Command、Adapter等)實現對指定數據庫表的訪問,並將其取出放到DataTable中,再根據指定格式輸出到頁面中。這里使用了using語句,其實質是幫我們自動生成try-finally,在離開using語句塊后會自動調用Dispose方法釋放資源,因為像Connection、Command這種對象是非托管資源,GC無法對其進行自動回收。
1.5.2 封裝SQLHelper
這里封裝了一個最基本的SQLHelper,實現了ExecuteNonQuery、ExecuteScalar、ExecuteDataTable,以及對Object類寫了兩個擴展方法,用於在C#類型和數據庫類型之間的轉換。

public static class MsSqlHelper { public static readonly string connstr = ConfigurationManager.ConnectionStrings["connStr"].ConnectionString; #region 00.OpenConnection public static SqlConnection OpenConnection() { SqlConnection con = new SqlConnection(connstr); con.Open(); return con; } #endregion #region 01.ExecuteNonQuery public static int ExecuteNonQuery(string cmdText, params SqlParameter[] parameters) { using (SqlConnection con = new SqlConnection(connstr)) { con.Open(); return ExecuteNonQuery(con, cmdText, parameters); } } public static int ExecuteNonQuery(SqlConnection con, string cmdText, params SqlParameter[] parameters) { using (SqlCommand cmd = con.CreateCommand()) { cmd.CommandText = cmdText; cmd.Parameters.AddRange(parameters); int result = cmd.ExecuteNonQuery(); return result; } } #endregion #region 03.ExecuteScalar public static object ExecuteScalar(string cmdText, params SqlParameter[] parameters) { using (SqlConnection con = new SqlConnection(connstr)) { con.Open(); return ExecuteScalar(con, cmdText, parameters); } } public static object ExecuteScalar(SqlConnection con, string cmdText, params SqlParameter[] parameters) { using (SqlCommand cmd = con.CreateCommand()) { cmd.CommandText = cmdText; cmd.Parameters.AddRange(parameters); object result = cmd.ExecuteScalar(); return result; } } #endregion #region 04.ExecuteDataTable public static DataTable ExecuteDataTable(string cmdText, params SqlParameter[] parameters) { using (SqlConnection con = new SqlConnection(connstr)) { con.Open(); return ExecuteDataTable(con, cmdText, parameters); } } public static DataTable ExecuteDataTable(SqlConnection con, string cmdText, params SqlParameter[] parameters) { using (SqlCommand cmd = con.CreateCommand()) { cmd.CommandText = cmdText; cmd.Parameters.AddRange(parameters); using (SqlDataAdapter adapter = new SqlDataAdapter(cmd)) { DataTable dt = new DataTable(); adapter.Fill(dt); return dt; } } } #endregion #region 05.ExtendMethod public static object ToDBValue(this object value) { return value == null ? DBNull.Value : value; } public static object FromDBValue(this object dbValue) { return dbValue == DBNull.Value ? null : DBNull.Value; } #endregion }
二、准備面試中回答的問題
注意:這里的問題沒有標准答案,大家可以自己總結,有興趣的園友可以搜搜相關內容,並可以在留言中發起討論。
2.1 你在工作中遇到最大的困難是什么?是怎么解決的?
2.2 (1)開發這個項目多長時間?
(2)開發這個項目一共幾個人?
(3)你對什么項目最熟悉?簡單說說這個項目。
PS:簡歷上寫的一定要熟悉,提前准備好,涉及到的技術點一定要有所了解。
2.3 網站優化的技術方案?
PS:客戶端緩存、頁面緩存、數據源控件緩存、ViewState處理、自定義緩存、IIS啟用內核模式緩存和用戶模式緩存、IIS啟用壓縮、服務器端應用和數據庫分離等等;
2.4 委托與事件的聯系與區別?
2.5 Session的原理?Session與Cookie的區別?
2.6 SQL連接池、字符串駐留池、GC垃圾回收原理的理解?
三、其他面試討論文章收藏
3.1 談談.Net技術面試:bigmonster
URL傳送門:http://www.cnblogs.com/bigmonster/archive/2011/05/14/2046427.html
3.2 我在面試.NET/C#程序員時會提出的問題:老趙
URL傳送門:http://blog.zhaojie.me/2011/03/my-interview-questions-for-dotnet-programmers.html
3.3 我也談面試-附贈一份題目:Anders06
URL傳送門:http://www.cnblogs.com/anders06/archive/2011/03/04/1971078.html
3.4 .Net工程師面試題在線交流:大熊(先生)
URL傳送門:http://www.cnblogs.com/Creator/archive/2011/06/07/2074607.html
3.5 我設計的ASP.Net筆試題,你會多少呢:Leo
URL傳送門:http://www.cnblogs.com/leotsai/p/aspnet-tech-test.html
3.6 數據庫優化系列:
(1)SQL調優之降龍十八掌系列—鋼鐵心臟:http://www.cnblogs.com/engine1984/p/3454499.html
(2)數據庫優化實踐系列—elysee:http://www.cnblogs.com/hnlshzx/p/3506593.html
3.7 ASP.NET性能優化系列:小洋(燕洋天)
URL傳送門:http://www.cnblogs.com/yanyangtian/archive/2010/07/16/1778986.html
3.8 設計模式系列:Terry Lee
URL傳送門:http://www.cnblogs.com/Terrylee/archive/2006/07/17/334911.html
3.9 大型網站技術架構系列:這里我能推薦一下我自己的嘛?么么嗒!
URL傳送門:http://www.cnblogs.com/edisonchou/p/3773828.html
四、個人總結
轉眼之間,又是9月份了,校園招聘的浪潮又要襲來,各位即將從學校畢業的園友們,你們准備好了嗎?其實,我個人是不建議也不喜歡刷面試題的,上面這些內容我也只看了一點,不過將一少部分面試題作為復習驗收檢測以查漏補缺還是有一定益處的。就如我開篇所說:就算你都學習了這些題目,甚至把這些題目的回答都記住了,你也不一定能拿到offer,技術學習不是死記硬背,重在理解與思路。自從進入園子以后,就看到各路大神的技術文章,對大神們頂禮膜拜,覺得以前把什么XX倫、XX華、XX友、XX迅啊視為偶像簡直就是弱爆了(這里沒有其他意思,就是一個自嘲,請各路fans一笑而過),現在你的偶像可能是XX楠、趙X、X濤、XX軍、XX陽...。但是,你可想到這些大神的牛逼也並非一日而就,Nothing can replaces hardwork,只有一步一步的繼續努力,不滿足於現狀,堅持學習(比如要想當.Net大神還得深入.Net內部原理,閱讀大牛的經典書籍並加以實踐),善於總結(學習並不是盲目的,也不是拼數量的,高效地總結所學會事半功倍),樂於分享(把學習到的東東寫成一篇篇的博客發到園子里就是一種分享)才會當上CTO、贏取白富美、走上人生巔峰(這句話源自:萬萬沒想到)。
對於未來,我不想過多設想,因為我智商平平,學校很渣,技術也不算好,實習經歷也很渣,但我會踏實走好每一步:我會努力地工作,也會繼續活躍在博客園,爭取翻譯一些Code Project上比較好的技術文章(不得不承認,一些印度三哥的技術文章寫得真tmd好!),也要爭取寫一些有質量的原創文章發到首頁與各位園友分享。至於將來的目標,那就是向園子里的各位大神看齊,向他們學習,使用技術改變生活(寫得了代碼做得好產品服務於客戶),同時也要熱愛生活(下得了廚房踢得了足球無愧於內心)。最后,借用大神老趙的一句話來做結尾,也與各位即將從學校畢業的碼農朋友們共勉:“先做人,再做技術人員,最后做程序員”。