劍指Offer面試題:7.旋轉陣列的最小數字
一、題目:旋轉陣列的最小數字
題目:把一個數組最開始的若干個元素搬到陣列的末尾,我們稱之為陣列的旋轉。輸入一個遞增排序的陣列的一個旋轉,輸出旋轉陣列的最小元素。例如陣列{3,4,5,1,2}為{1,2,3,4,5}的一個旋轉,該陣列的最小值為1。
這道題最直觀的解法並不難,從頭到尾遍歷陣列一次,我們就能找出最小的元素。這種思路的時間複雜度顯然是O(n)。但是這個思路沒有利用輸入的旋轉陣列的特性,肯定達不到面試官的要求。
我們注意到旋轉之後的陣列實際上可以劃分為兩個排序的子陣列,而且前面的子陣列的元素都大於或者等於後面子陣列的元素。我們還注意到最小的元素剛好是這兩個子陣列的分界線。在排序的陣列中我們可以用二分查詢法實現O(logn)
二、解題思路
Step1.和二分查詢法一樣,我們用兩個指標分別指向陣列的第一個元素和最後一個元素。
Step2.接著我們可以找到陣列中間的元素:
如果該中間元素位於前面的遞增子陣列,那麼它應該大於或者等於第一個指標指向的元素。此時陣列中最小的元素應該位於該中間元素的後面。我們可以把第一個指標指向該中間元素,這樣可以縮小尋找的範圍。移動之後的第一個指標仍然位於前面的遞增子陣列之中。如果中間元素位於後面的遞增子陣列,那麼它應該小於或者等於第二個指標指向的元素。此時該陣列中最小的元素應該位於該中間元素的前面。
Step3.接下來我們再用更新之後的兩個指標,重複做新一輪的查詢。
按照上述的思路,第一個指標總是指向前面遞增陣列的元素,而第二個指標總是指向後面遞增陣列的元素。最終第一個指標將指向前面子陣列的最後一個元素,而第二個指標會指向後面子陣列的第一個元素。也就是它們最終會指向兩個相鄰的元素,而第二個指標指向的剛好是最小的元素。這就是迴圈結束的條件。
以前面的陣列{3,4,5,1,2}為例,下圖展示了在該陣列中查詢最小值的過程:
三、解決問題
3.1 程式碼實現
public static int GetMin(int[] numbers) { if (numbers == null || numbers.Length <= 0) { return int.MinValue; } int index1 = 0; int index2 = numbers.Length - 1; // 把indexMid初始化為index1的原因: // 一旦發現數組中第一個數字小於最後一個數字,表明該陣列是排序的 // 就可以直接返回第一個數字了 int indexMid = index1; while (numbers[index1] >= numbers[index2]) { // 如果index1和index2指向相鄰的兩個數, // 則index1指向第一個遞增子陣列的最後一個數字, // index2指向第二個子陣列的第一個數字,也就是陣列中的最小數字 if (index2 - index1 == 1) { indexMid = index2; break; } indexMid = (index1 + index2) / 2; // 特殊情況:如果下標為index1、index2和indexMid指向的三個數字相等,則只能順序查詢 if (numbers[index1] == numbers[indexMid] && numbers[indexMid] == numbers[index2]) { return GetMinInOrder(numbers, index1, index2); } // 縮小查詢範圍 if (numbers[indexMid] >= numbers[index1]) { index1 = indexMid; } else if (numbers[indexMid] <= numbers[index2]) { index2 = indexMid; } } return numbers[indexMid]; } public static int GetMinInOrder(int[] numbers, int index1, int index2) { int result = numbers[index1]; for (int i = index1 + 1; i <= index2; ++i) { if (result > numbers[i]) { result = numbers[i]; } } return result; }
這裡需要注意的是:
(1)把indexMid初始化為index1的原因:一旦發現數組中第一個數字小於最後一個數字,表明該陣列是排序的,就可以直接返回第一個數字了。
(2)特殊情況的分析:如果下標為index1、index2和indexMid指向的三個數字相等,則只能順序查詢,因此這裡定義了一個GetMinInOrder()方法。
3.2 單元測試
(1)典型輸入,單調升序的陣列的一個旋轉
// 典型輸入,單調升序的陣列的一個旋轉 [TestMethod] public void GetMinNumTest1() { int[] array = {3, 4, 5, 1, 2}; Assert.AreEqual(Program.GetMin(array),1); }
(2)有重複數字,並且重複的數字剛好的最小的數字
// 有重複數字,並且重複的數字剛好的最小的數字 [TestMethod] public void GetMinNumTest2() { int[] array = { 3, 4, 5, 1, 1, 2 }; Assert.AreEqual(Program.GetMin(array), 1); }
(3)有重複數字,但重複的數字不是第一個數字和最後一個數字
// 有重複數字,但重複的數字不是第一個數字和最後一個數字 [TestMethod] public void GetMinNumTest3() { int[] array = { 3, 4, 5, 1, 2, 2 }; Assert.AreEqual(Program.GetMin(array), 1); }
(4)有重複的數字,並且重複的數字剛好是第一個數字和最後一個數字
// 有重複的數字,並且重複的數字剛好是第一個數字和最後一個數字 [TestMethod] public void GetMinNumTest4() { int[] array = { 1, 0, 1, 1, 1 }; Assert.AreEqual(Program.GetMin(array), 0); }
(5)單調升序陣列,旋轉0個元素,也就是單調升序陣列本身
// 單調升序陣列,旋轉0個元素,也就是單調升序陣列本身 [TestMethod] public void GetMinNumTest5() { int[] array = { 1, 2, 3, 4, 5 }; Assert.AreEqual(Program.GetMin(array), 1); }
(6)陣列中只有一個數字
// 陣列中只有一個數字 [TestMethod] public void GetMinNumTest6() { int[] array = { 2 }; Assert.AreEqual(Program.GetMin(array), 2); }
(7)魯棒性測試:輸入NULL
// 魯棒性測試:輸入NULL [TestMethod] public void GetMinNumTest7() { Assert.AreEqual(Program.GetMin(null), int.MinValue); }
單元測試的結果如下圖所示:
對於GetMin方法編寫的單元測試的程式碼覆蓋率已達到了100%:
作者:周旭龍
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連結。
相關推薦
劍指Offer面試題:7.旋轉陣列的最小數字
一、題目:旋轉陣列的最小數字 題目:把一個數組最開始的若干個元素搬到陣列的末尾,我們稱之為陣列的旋轉。輸入一個遞增排序的陣列的一個旋轉,輸出旋轉陣列的最小元素。例如陣列{3,4,5,1,2}為{1,2,3,4,5}的一個旋轉,該陣列的最小值為1。 這道題最直觀的解法並不難,從頭到尾遍歷陣列一次
劍指Offer面試題:13.調整陣列順序使奇數位於偶數前面
一、題目:調整陣列順序使奇數位於偶數前面 題目:輸入一個整數陣列,實現一個函式來調整該陣列中數字的順序,使得所有奇數位於陣列的前半部分,所有偶數位於陣列的後半部分。 例如有以下一個整數陣列:12345,經過調整後可以為:15342、13542、13524等等。 二、解題思路 2.1 基本解
劍指Offer面試題:35.將字串轉換為數字
一、題目:將字串轉換為數字 題目:寫一個函式StrToInt,實現把字串轉換成整數這個功能。當然,不能使用atoi或者其他類似的庫函式。 二、程式碼實現 (1)考慮輸入的字串是否是NULL、空字串 (2)考慮輸入的字串是否包含正負號或者是否是隻包含正負號 (3)考慮輸入的字串是否
劍指Offer面試題:34.翻轉單詞順序VS左旋轉字串
一、題目一:翻轉單詞順序 1.1 題目說明 題目一:輸入一個英文句子,翻轉句子中單詞的順序,但單詞內字元的順序不變。為簡單起見,標點符號和普通字母一樣處理。例如輸入字串"I am a student.",則輸出"student. a am I"。 1.2 解題思路 第一步翻轉句子中所有的字
劍指offer 面試題:重建二叉樹
題目:輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重複的數字。例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建二叉樹並返回。 思路:二叉樹先序是根左右,中序 是左根右。所以先找到
劍指offer 面試題:從尾到頭列印連結串列
題目:輸入一個連結串列,按連結串列值從尾到頭的順序返回一個ArrayList。 思路:有多種放法。(1)先反轉連結串列,再列印連結串列。(2)使用棧。 /** * struct ListNode { * int val; * struct ListNode *n
劍指Offer面試題:17.樹的子結構
一、題目:樹的子結構 題目:輸入兩棵二叉樹A和B,判斷B是不是A的子結構。例如下圖中的兩棵二叉樹,由於A中有一部分子樹的結構和B是一樣的,因此B是A的子結構。 該二叉樹的節點定義如下,這裡使用C#語言描述: public class BinaryTreeNode {
劍指Offer面試題:31.兩個連結串列的第一個公共節點
一、題目:兩個連結串列的第一個公共節點 題目:輸入兩個連結串列,找出它們的第一個公共結點。 連結串列結點定義如下,這裡使用C#語言描述: public class Node { public int key; public Node
劍指Offer面試題:2.二維陣列中的查詢
一、題目:二維陣列中的查詢 題目:在一個二維陣列中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函式,輸入這樣的一個二維陣列和一個整數,判斷陣列中是否含有該整數。 例如下面的二維陣列就是每行、每列都遞增排序。如果在這個陣列中查詢數字7,則返回true;
劍指Offer面試題:22.二叉搜尋樹的後序遍歷序列
一、題目:二叉搜尋樹的後序遍歷序列 題目:輸入一個整數陣列,判斷該陣列是不是某二叉搜尋樹的後序遍歷的結果。如果是則返回true,否則返回false。假設輸入的陣列的任意兩個數字都互不相同。 例如在下面的一顆二叉搜尋樹中,輸入陣列{5,7,6,9,11,10,8},則返回true,因為這個整數序列是
劍指Offer面試題:14.連結串列的倒數第k個節點
PS:這是一道出境率極高的題目,記得去年參加校園招聘時我看到了3次,但是每次寫的都不完善。 一、題目:連結串列的倒數第k個節點 題目:輸入一個連結串列,輸出該連結串列中倒數第k個結點。為了符合大多數人的習慣,本題從1開始計數,即連結串列的尾結點是倒數第1個結點。例如一個連結串列有6個結點,從頭結點開始
劍指Offer面試題:28.連續子陣列的最大和
一、題目:連續子陣列的最大和 題目:輸入一個整型陣列,數組裡有正數也有負數。陣列中一個或連續的多個整陣列成一個子陣列。求所有子陣列的和的最大值。要求時間複雜度為O(n)。例如輸入的陣列為{1,-2,3,10,-4,7,2,-5},和最大的子陣列為{3,10,-4,7,2},因此輸出為該子陣列的和18。
劍指Offer面試題:4.從尾到頭列印連結串列
一、題目:從尾到頭列印連結串列 題目:輸入一個連結串列的頭結點,從尾到頭反過來打印出每個結點的值。 到解決這個問題肯定要遍歷連結串列。遍歷的順序是從頭到尾的順序,可輸出的順序卻是從尾到頭。也就是說第一個遍歷到的結點最後一個輸出,而最後一個遍歷到的結點第一個輸出。這就是典型的“後進先出”,我
劍指Offer面試題:10.數值的整數次方
一、題目:數值的整數次方 題目:實現函式double Power(doublebase, int exponent),求base的exponent次方。不得使用庫函式,同時不需要考慮大數問題。 在.NET Framework提供的BCL中,Math類實現了一個Pow方法,例如要求2的三次方,可
劍指Offer面試題:6.用兩個棧實現佇列
一、題目:用兩個棧實現佇列 題目:用兩個棧實現一個佇列。佇列的宣告如下,請實現它的兩個函式appendTail和deleteHead,分別完成在佇列尾部插入結點和在佇列頭部刪除結點的功能。 原文是使用C++結合模板實現的定義,這裡我們採用C#結合泛型來實現這個佇列的定義,我們要實現的就是兩
劍指Offer面試題:20.棧的壓入、彈出序列
一、題目:棧的壓入、彈出序列 題目:輸入兩個整數序列,第一個序列表示棧的壓入順序,請判斷第二個序列是否為該棧的彈出順序。假設壓入棧的所有數字均不相等。例如序列1、2、3、4、5是某棧的壓棧序列,序列4、5、3、2、1是該壓棧序列對應的一個彈出序列,但4、3、5、1、2就不可能是該壓棧序列的彈出序列。
劍指Offer面試題:30.第一個只出現一次的字元
一、題目:第一個只出現一次的字元 題目:在字串中找出第一個只出現一次的字元。如輸入"abaccdeff",則輸出'b'。要求時間複雜度為O(n)。 最直觀的想法是從頭開始掃描這個字串中的每個字元。當訪問到某字元時拿這個字元和後面的每個字元相比較,如果在後面沒有發現重複的字元,則該字元就是隻出現
劍指Offer面試題:23.二叉樹中和為某一值的路徑
一、題目:二叉樹中和為某一值的路徑 題目:輸入一棵二叉樹和一個整數,打印出二叉樹中結點值的和為輸入整數的所有路徑。從樹的根結點開始往下一直到葉結點所經過的結點形成一條路徑。例如輸入下圖中二叉樹和整數22,則打印出兩條路徑,第一條路徑包含結點10、12,第二條路徑包含結點10、5和7。 二叉
劍指Offer面試題:11.列印1到最大的n位數
一、題目:列印1到最大的n位數 題目:輸入數字n,按順序打印出從1最大的n位十進位制數。比如輸入3,則打印出1、2、3一直到最大的3位數即999。 二、不同的解法 2.1 不假思索的解法 最容易想到的辦法是先求出最大的n位數,然後用一個迴圈從1開始逐個列印: static v
劍指Offer面試題:32.數字在排序陣列中出現的次數
一、題目:數字在排序陣列中出現的次數 題目:統計一個數字在排序陣列中出現的次數。例如輸入排序陣列{1,2,3,3,3,3,4,5}和數字3,由於3在這個陣列中出現了4次,因此輸出4。 二、解題思路 2.1 直接運用二分查詢 既然輸入的陣列是排序的,那麼我們很自然地就能想到用二分查詢演算法。