1. 程式人生 > >劍指Offer(面試題29~30)

劍指Offer(面試題29~30)

面試題29:陣列中出現次數超過一半的數字

題目:陣列中有一個數字出現的次數超過陣列長度的一半,請找出這個數字。例如輸入一個長度為9的陣列{1,2,3,2,2,2,5,4,2}。由於數字2在陣列中出現了5次,超過陣列長度的一半,因此輸出2。

bool g_bInputInvalid = false;
bool CheckInvalidArray(int* numbers,int length)
{
    g_bInputInvalid = false;
    if(numbers == NULL || length <= 0)
        g_bInputInvalid = true
; return g_bInputInvalid; } bool CheckMoreThanHalf(int* numbers,int length,int number) { int times = 0; for(int i=0;i<length;++i) { if(numbers[i] == number) times++; } bool isMoreThanHalf = true; if(times*2 <= length) { g_bInputInvalid = true
; isMoreThanHalf = false; } return isMoreThanHalf; } int MoreThanHalfNum(int* numbers,int length) { if(CheckInvalidArray(numbers,length)) return 0; int result = numbers[0]; int times = 1; for(int i = 1;i<length;++i) { if(times == 0) { result = numbers[i]; times = 1
; } else if(numbers[i] == result) times++; else times--; } if(!CheckMoreThanHalf(numbers,length,result)) result = 0; return result; }

面試題30:最小的K個數
題目:輸入n個整數,找出其中最小的k個數。例如輸入4、5、1、6、2、7、3、8這8個數字,則最小的4個數字是1、2、3、4。
解法一:O(n)的演算法,只有當我們可以修改輸入的陣列時可用
分析:可以基於陣列的第k個數字來調整,使得比第k個數字小的所有數字都位於陣列的左邊,比第k個數字大的所有數字都位於陣列的右邊。這樣調整之後,位於陣列中左邊的k個數字就是最小的k個數字(這k個數字不一定是排序的)。下面是參考程式碼:

void GetLeastNumbers(int* input,int n,int* output,int k)
{
    if(input == NULL || output == NULL || k>n || n<=0 || k <= 0)
        return;

    int start = 0;
    int end = n-1;
    int index = partition(input,n,start,end);
    while(index != k -1)
    {
        if(index > k-1)
        {
            end = index -1;
            index = partition(input,n,start,end);
        }
        else
        {
            start = index + 1;
            index = partition(input,n,start,end);
        }
    }

    for(int i = 0;i < k;++i)
        output[i] = input[i];
}

採用這種思路是有限制的。我們需要修改輸入的陣列,因為函式Partition會調整陣列中數字的順序。如果面試官要求不能修改輸入的陣列,則解法如下:
解法二:O(nlogk)的演算法,特別適合處理海量資料
先建立一個大小為k的資料容器來儲存最小的k個數字,接下來我們每次從輸入的n個整數中讀入一個數。如果容器中已有的數字少於k個,則直接把這次讀入的整數放入容器之中;如果容器中已有k個數字了,也就是容器已滿,此時我們不能再插入新的數字而只能替換已有的數字。找出這已有的k個數中的最大值,然後那這次待插入的整數和最大值進行比較。如果待插入的值比當前已有的最大值小,則用這個數替換當前已有的最大值;如果待插入的值比當前已有的最大值還要大,那麼這個數不可能是最小的k個整數之一,於是我們可以拋棄這個整數。

typedef multiset<int, greater<int> > intSet;
typedef multiset<int, greater<int> >::iterator setIterator;

void GetLeastNumbers(const vector<int>& data,intSet& leastNumbers,int k)
{
    leastNumbers.clear();

    if(k < 1 || data.size() < k)
        return;
    vector<int>::const_iterator iter =data.begin();
    for(; iter != data.end();++ iter)
    {
        if((leastNumbers.size()) < k)
        {
            leastNumbers.insert(*iter);
        }
        else
        {
            setIterator iterGreatest = leastNumbers.begin();

            if(*iter < *(leastNumbers.begin()))
            {
                leastNumbers.erase(iterGreatest);
                leastNumbers.insert(*iter);
            }
        }
    }
}

第二種解法雖然要慢一點,但它有兩個明顯得優點。一是沒有修改輸入的資料。我們每次只是從data中讀入數字,所有的寫操作都是容器leastNumbers中進行的。二是該演算法適合海量資料的輸入。假設題目是要求從海量的資料中找出最小的k個數字,由於記憶體的大小是有限的,有可能不能把這些海量的資料一次性全部載入記憶體。這時候,我們可以從輔助儲存空間(比如硬碟)中每次讀入一個數字,根據GetLeastNumbers的方式判斷是不是需要放入容器leastNumbers即可。這種思路只要求記憶體有足夠容納leastNumbers即可,因此它最適合的情形就是n很大並且k較小的問題。

參考資料:《劍指Offer》

相關推薦

Offer試題29~30

面試題29:陣列中出現次數超過一半的數字 題目:陣列中有一個數字出現的次數超過陣列長度的一半,請找出這個數字。例如輸入一個長度為9的陣列{1,2,3,2,2,2,5,4,2}。由於數字2在陣列中出現了5次,超過陣列長度的一半,因此輸出2。 bool g_

offer試題29:順時針列印矩陣

/* * 順時針列印矩陣 * 注意矩陣的維度 */ #include<iostream> using namespace std; //只要當前的起始行號小於終止行號或者起始列號小於終止列號,就可以繼續順時針列印 // 但是隨著順勢怎列印,剩下

Offer試題43~45

面試題43:n個骰子的點數 題目:把n個骰子仍在地上,所有骰子朝上一面的點數之和為s。輸入n,打印出s的所有可能的值出現的概念。 解法一:基於遞迴求骰子點數,時間效率不夠高 要想求出n個骰子的點數和,可以先把n個骰子分成兩堆:第一堆只有一個,另一堆有n-1

Offer試題33~34

面試題33:把陣列排成最小的數 題目:輸入一個正整數陣列,把數組裡所有數字拼接起來排成一個數,列印能拼接出的所有數字中最小的一個。例如輸入陣列{3,32,321},則打印出這3個數字能排成的最小數字321323。 分析:一個非常直觀的解決大數問題的方法就是把

offer試題 29. 順時針打印矩陣

如果 clas for 輸入 offer ID amp 代碼實現 AR 面試題 29. 順時針打印矩陣 題目描述 題目:輸入一個矩陣,按照從外向裏以順時針的順序依次打印出每一個數字,例如,如果輸入如下矩陣: 1 2 3 4 5 6 7 8 9 10 11 12 1

offer試題--從尾到頭列印單鏈表

程式碼如下(遞迴實現): /** * public class ListNode { * int val; * ListNode next = null; * * ListNode(int val) { *

offer試題39:陣列中出現次數超過陣列長度一半的數字

方法1: 從數學角度看,所求的數一定的原陣列(有序化)的中位數,那麼,我們可以通過對陣列排序,這樣得到的時間複雜度是O(nlogn)O(nlogn)。而如果能夠優化到在O(n)O(n)的時間內找到,就更好了。基於快速排序的partition階段,如果我們可以

offer試題35:複雜連結串列的複製

/* * 複雜連結串列的複製 * 複雜連結串列是指,結點的指標可能不規則地指向另一個結點 */ #include<iostream> using namespace std; struct ComplexListNode { int v

offer試題38:字串的排列

分析: 求字串中所有字元的全排列,實際上按照全排列的計算公式來理解,第一步是求出所有可能出現在第一個位置的字元,即用首個字元分別後餘下的字元交換;第二步是固定首個字元,餘下字元組成的子串進行第一步的

offer試題21:根據給定條件劃分陣列

/* * 題目 * 輸入一個數組,實現一個函式來調整該陣列中數字的順序,使得所有奇數位於陣列的前半部分 * 偶數位於陣列的後半部分 * 同時考慮程式碼的可擴充套件行 */ #include <i

offer試題31:棧的壓入和彈出序列

/* * 給定兩個序列,一個是棧的壓入序列,一個是彈出序列,判斷彈出序列能否匹配壓入序列 */ #include<iostream> #include<stack> usi

offer試題39 二叉樹的深度java

設計模式 博客 rgs 歷史 存在 復制 pri 取值 今天 摘要: 今天翻到了《劍指offer》面試題39,題目二中的解法二是在函數的參數列表中通過指針的方式進行傳值,而java是沒有指針的,所以函數要進行改造。然而我翻了下別人的java版本(我就想看看有什麽高大上的改造

offer試題43 n個骰子的點數 java

r+ nal ret 次循環 分而治之 源碼 ava 面試 ble 引言:寫這篇文章的初衷只是想做個筆記,因為這道題代碼量有點大,有點抽象,而書上並沒有詳細的註釋。為了加深印象和便於下次復習,做個記錄。 原題:把n個骰子扔到地上,所有骰子朝上一面的點數之後為s. 輸入n,打

offer試題571:和為S的數字

題目 輸入一個遞增排序的陣列和一個數字S,在陣列中查詢兩個數,是的他們的和正好是S,如果有多對數字的和等於S,輸出兩個數的乘積最小的。 ps: 對應每個測試案例,輸出兩個數,小的先輸出。 思路

offer試題答案彙總Java版

面試題2:實現Singleton模式 (1)餓漢模式 public class Singleton{ private static Singleton instance = new Singleton(); private Singleton(){}

offer》- 試題3:陣列中重複的數字java實現

題目一:         在一個長度為n的數組裡的所有數字都在0到n-1的範圍內。 陣列中某些數字是重複的,但不知道有幾個數字是重複的。也不知道每個數字重複幾次。請找出陣列中任意一個重複的數字。 例如,如果輸入長度為7的陣列{2,3,1,0,2,5,3},那麼對應的輸出是重複

offer試題322:分行從上到下列印二叉樹

完整程式碼地址 題目 從上到下按層列印二叉樹,同一層結點從左至右輸出。每一層輸出一行。 思路 用佇列來儲存要列印的節點。 同時我們需要兩個變數:一個變量表示在當前層中還沒有列印的節點數

offer試題二:單例模式的實現使用C++語言

#include <iostream> #include <iomanip> #include <cstdio> #include <cstdlib>

offer試題50:字元流中第一個只出現一次的字元【C++版本】

題目:字串中第一個只出現一次的字元。 在字串中找出第一個只出現一次的字元。如輸入"abaccdeff" "abaccdeff",則輸出′ b ′  ′b′。 解題思路: 1.使用雜湊表來記錄每個字元出現的次數,因為字元char char為8位,

offer試題 2. 實現 Singleton模式

模式 試題 枚舉 生成 test hand true 方法 單例模式 面試題 2. 實現 Singleton模式 題目:設計一個類,我們只能生成該類的一個實例。 單例模式:確保一個類只有一個實例,並提供了一個全局訪問點。 Code 1.餓漢模式 //餓漢模式 publ