刷題記錄---每日更新-8月7日

day1

1、輸入兩個單調遞增的鏈表,輸出兩個鏈表合成後的鏈表,當然我們需要合成後的鏈表滿足單調不減規則。

循環

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
            val(x), next(NULL) {
    }
};*/
class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
        // 空處理  
        if(pHead1 == NULL )
            return pHead2;
        if (pHead2 == NULL)
            return pHead1;

        ListNode*  pNewHead = NULL;

        // 比較兩個鏈表頭,找出頭結點
        if( pHead1->val < pHead2->val){
            pNewHead = pHead1;
            pHead1 = pHead1->next;
        }
        else{
            pNewHead = pHeada2;
            pHead2 = pHead2->next;
        }

        // 循環在兩個鏈表中找出最小值,連接到pNewHead
        ListNode* pTail = pNewHead;
        while(pHead1 && pHead2){
            if(pHead1->val < pHead2->val){
                pTail->next = pHead1;
                pHead1 = pHead1->next;
            }
            else{
                pTail->next = pHead2;
                pHead2 = pHead2->next;
            }
            pTail = pTail->next;
        }

        // 處理一個鏈表元素和並完畢,另一個鏈表還有元素的情況。
        if(pHead1 == NULL){
            pTail->next = pHead2;
        }
        else if(pHead2 == NULL)
            pTail->next = pHead1;

        return pNewHead;
    }
};

遞歸

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
            val(x), next(NULL) {
    }
};*/
class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
        // 遞歸出口,其中一個鏈表爲空或兩個都爲空
        if(pHead1 == NULL)
            return pHead2;
        if (pHead2 == NULL)
            return pHead1;

        ListNode* pNewHead = NULL;
        // 比較兩個鏈表的第一個元素,將pNewHead 指向最小的,然後繼續向後推進
        if( pHead1->val < pHead2->val){
            pNewHead = pHead1;
            pNewHead->next = Merge(pHead1->next, pHead2);
        }
        else{
            pNewHead = pHead2;
            pNewHead->next = Merge(pHead1, pHead2->next);
        }
        return pNewHead;
    }
};

2、求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等關鍵字及條件判斷語句(A?B:C)。

遞歸

class Solution {
public:
    int Sum_Solution(int n) {
        int ret = n;
        ret && (ret = ret + Sum_Solution(n-1));
        return ret;
    }
};

構造函數

#include <iostream>
using namespace std;

class Sum
{
public:
    Sum()
    {
        i++;
        sum += i;
    }
    static int i;
    static int sum;
};

int Sum::sum = 0;
int Sum::i = 0;
int main()
{
    Sum s[100];
    cout << Sum::sum << endl;
    return 0;
}

奇淫技巧,VS 編譯通不過,GCC 可以

class Solution {
public:
    int Sum_Solution(int n) {
        bool a[n][n+1];
        return sizeof(a)>>1;
    }
};

3、輸入一個鏈表,反轉鏈表後,輸出鏈表的所有元素。
答:一個指針保存當前結點,初始值爲 pHead,一個指針保存上一個結點初始值爲 NULL,一個指針保存新鏈表的頭初始值爲NULL。在循環內用一個臨時指針保存鏈表下一個結點。如果當前結點不爲空則繼續循環,在循環內首先保存鏈表的下一個結點,然後將當前結點的next 指向pRev,更新pRev,然後判斷是否走的最後一個結點(next爲空),如果是,則循環結束,更新newHead 的指向,否則向後推進當前指針。

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
            val(x), next(NULL) {
    }
};*/
class Solution {
public:
    ListNode* ReverseList(ListNode* pHead) {

        ListNode* pRev = NULL;
        ListNode* pCur = pHead;
        ListNode* pNewHead = NULL;
        while(pCur){
            ListNode* pNext = pCur->next;
            pCur->next = pRev;
            pRev = pCur;
            if (pNext == NULL){
                pNewHead = pRev;
                break;
            }
            pCur = pNext;
        }
        return pNewHead;
    }
};

4、輸入一個鏈表,輸出該鏈表中倒數第k個結點。
答:考慮 k 和 鏈表爲空和只有一個結點的情況,1.先讓快指針走 K 步,2. 快慢指針一起走,直到快指針爲空,返回慢指針

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
            val(x), next(NULL) {
    }
};*/
class Solution {
public:
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
        if (pListHead == NULL || pListHead->next == NULL)
            return pListHead;
        ListNode* fast = pListHead;
        while(k--){
            if(fast == NULL)
                return NULL;           
            fast = fast->next;

        }   
        while(fast){
            fast = fast->next;
            pListHead = pListHead->next;
        }
        return pListHead;      
    }
};

5、寫一個函數,求兩個整數之和,要求在函數體內不得使用+、-、*、/四則運算符號。
答:1. 無進位加(異或)2.取得進位(與運算,左移) 3.累加(直到進位爲0)

class Solution {
public:
    int Add(int num1, int num2)
    {
        int sum,carry;
        do{
            sum = num1^num2;
            carry = (num1&num2)<<1;
            num1 = sum;
            num2 = carry;
        }while(carry!=0);
        return sum;
    }
};

6、判斷鏈表否帶環,不帶環返回空,帶環返回環上節點。
答:一個指針走兩步一個指針走一步,如果快指針與慢指針相遇,則返回,否則向後走。循環繼續條件是fast 和 fast->next 不爲空。

ListNode* HasCircle (ListNode* pHead)
{
    ListNode* pFast = pHead;
    ListNode* pSlow = pHead;
    while(pFast && pFast->next)
    {
        pFast = pFast->next->next;
        pSlow = pSlow->next;
        if(pSlow == pFast)
            return pSlow;
    }
    return NULL;
}

7、 求帶環鏈表環的長度

int CircleLength(ListNode* meetNode)
{
    ListNode* pCur = meetNode->next;
    int length = 1;

    while (meetNode != pCur)
    {
        length++;
        pCur = pCur->next;
    }

    return length;
}

8、帶環鏈表的入口點

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};
*/
class Solution {
public:
    ListNode* GetMeetNode(ListNode* pHead){
        ListNode* pFast = pHead;
        ListNode* pSlow = pHead;

        while( pFast && pFast->next){
            pFast = pFast->next->next;
            pSlow = pSlow->next;
            if(pSlow == pFast)
                return pSlow;
        }
        return NULL;
    }


    ListNode* EntryNodeOfLoop(ListNode* pHead)
    {
        ListNode* pMeetNode = GetMeetNode(pHead);
        if(pMeetNode == NULL)
            return NULL;

        ListNode* pBegin = pHead;
        while(pMeetNode != pBegin){
            pMeetNode = pMeetNode->next;
            pBegin = pBegin->next;
        }
        return pMeetNode;
    }
};

9、不能被繼承的類

// 虛擬繼承,虛基類會由派生類構造
// 友元不能被繼承
// 模板參數設置爲友元
template<class T>
class A
{
    friend T;
private:
    A(){}
    ~A(){}
};

class B : virtual A<B>
{
public:
    B(){}
    ~B(){}
};

10、只能在堆上創建

// OnlyHeap
// 將構造函數設置爲私有,顯示提供一個接口 delete this
class OnlyHeap
{
public:
    OnlyHeap()
    {}

    void Destory()
    {
        delete this;
    }
private:
    ~OnlyHeap()
    {}
};

int main()
{
    OnlyHeap* p = new OnlyHeap();
    return 0;
}

11、 只能在棧創建
重載 new 和 delete 並設置爲私有

class OnlyStack
{
public:
    OnlyStack()
    {}
    ~OnlyStack()
    {}
private:
    void* operator new(size_t);
    void operator delete(void*);
}

12、判斷兩個單鏈錶鏈表是否相交,若相交,求交點。【基礎版:不考慮帶環】

答:1. 先判斷連個鏈表是否相交:都走到最後一個結點,然後比較是否相等,如果相交,則最後一個結點肯定相同。PS:不要想到 X 形交叉, 因爲這是單鏈表!只有一個next指針。
2.如果在 1. 中判斷相交的情況,然後求出兩個鏈表的長度,讓較長的鏈表向前走 兩個鏈表長度差 的步數,然後同時出發,相遇時就是相交點。

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
            val(x), next(NULL) {
    }
};*/
class Solution {
public:
    // 判斷鏈表是否相交
    bool isCross(ListNode* pHead1, ListNode* pHead2){
        if(pHead1 == NULL || pHead2 == NULL)
            return false;
        while(pHead1->next)
            pHead1 = pHead1->next;
        while(pHead2->next)
            pHead2 = pHead2->next;

        if(pHead1 == pHead2 )
            return true;
        return false;
    }

    // 獲取鏈表長度
    int getSize(ListNode* pHead){
        int i = 0;
        while(pHead){
            i++;
            pHead = pHead->next;
        }
        return i;
    }

    // 找出公共結點
    ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
        if(pHead1 == NULL || pHead2 == NULL)
            return NULL;
        // 特殊處理一下,兩個指針指向同一個鏈表,且鏈表只有一個結點的情況
        if(pHead1  == pHead2 )
            return pHead1;


        if(!isCross(pHead1, pHead2)){
             return NULL;
        }
        int step = 0;
         int size1 = getSize(pHead1);
         int size2 = getSize(pHead2);
         step = size1-size2;

        if(step > 0){
            while(step--)
                pHead1 = pHead1->next;
        }
        else if (step < 0){
            while(step++)
                pHead2 = pHead2->next;
        }

        while(pHead1 != pHead2){
            pHead1 = pHead1->next;
            pHead2 = pHead2->next;
        }
        return pHead1;
    }
};

13、判斷兩個單鏈錶鏈表是否相交,若相交,求交點。【升級版:考慮帶環】

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
            val(x), next(NULL) {
    }
};*/
class Solution {
public:
    // 找出鏈表的第一個公共結點
    ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {

        // 第一個公共結點
        ListNode* commonNode = NULL;

        if(pHead1  ==  NULL || pHead2 == NULL )
            return NULL;

        // (1) 判斷是否都有環
        ListNode* circleNode1; // 鏈表1 環上的結點(快慢指針相遇點),如果無環則爲空
        ListNode* circleNode2; // 鏈表2 環上的結點(快慢指針相遇點),如果無環則爲空

        circleNode1 = GetMeetNode(pHead1);
        circleNode2 = GetMeetNode(pHead2);

        // (2) 如果都有環  or 如果都無環  or 只有一個有環

        //  1. 都有環
        if (circleNode1 && circleNode2){
            // 鏈表1 環的入口點
            ListNode* circleEnterNode1 = GetCircleEnterNode(pHead1, circleNode1);
            // 鏈表2 環的入口點
            ListNode* circleEnterNode2 = GetCircleEnterNode(pHead2, circleNode2);
            // 如果入口點相同,臨時修改鏈表爲 Y 形狀,處理完畢後恢復
            if(circleEnterNode1 == circleEnterNode2){
                // 保存
                ListNode* enterNodeNext = circleEnterNode1->next;
                // 設置爲 Y 形,即無環
                circleEnterNode1->next = NULL;

                // 調用處理無環情況的函數
                commonNode =  GetFirstCommonNodeNoCircle(pHead1, pHead2);

                // 恢復
                circleEnterNode1->next = enterNodeNext;

            }// 如果入口點不同,將一個環遍歷一週看是否能遇到另外一個環的入口點
            else{
                // 獲取其中一個環的長度
                int circleLength = GetCircleLength(circleNode2);
                // 遍歷一週看是否能遇到另外一個環的入口點,如果遇到則找到,否則未找到返回環
                ListNode* next = circleEnterNode2->next;
                // 遍歷一圈
                while(circleLength--){
                    // 找到公共結點
                    if(next == circleEnterNode1){
                        commonNode = circleEnterNode1;     
                        break;
                    }
                    next = next->next;
                }
                // 未找到公共結點
                if(circleLength <= 0)
                    return NULL;

            }
        } // 2. 都無環
        else if (circleNode1 == NULL && circleNode2 == NULL){
            commonNode = GetFirstCommonNodeNoCircle(pHead1, pHead2);
        }


        // (3).其中一個無環, 肯定無公共結點
        return commonNode;
    }

private:


    // 獲取帶環鏈表環上的一個結點(快慢指針相遇點),如果不帶環則返回空
    ListNode* GetMeetNode(ListNode* pHead){
        if(pHead == NULL)
            return NULL;
        ListNode* pFast = pHead;
        ListNode* pSlow = pHead;
        // 快慢指針法
        while(pFast && pFast->next){
            pFast = pFast->next->next;
            pSlow = pSlow->next;
            if(pFast == pSlow){
                return pFast;
            }
        }
        return NULL;
    }

    // 根據快慢指針相遇點,過去環的入口點
    ListNode* GetCircleEnterNode(ListNode* pHead, ListNode* meetNode){
        if(pHead == NULL || meetNode == NULL)
            return NULL;

        while(pHead != meetNode){
            pHead = pHead->next;
            meetNode = meetNode->next;
        }
        return pHead;
    }

    // 判斷兩個鏈表是否相交
    bool isCross(ListNode* pHead1, ListNode* pHead2){

        if(pHead1 == NULL  || pHead2 == NULL){
            return false;
        }

        while(pHead1->next)
            pHead1 = pHead1->next;
        while(pHead2->next)
            pHead2 = pHead2->next;
        if(pHead1 == pHead2)
            return true;
        return false;
    }

    // 獲取鏈表長度
    int getListLength(ListNode* pHead){
        int length = 0;
        while(pHead){
            length++;
            pHead = pHead->next;
        }
        return length;
    }

    // 處理 Y 形狀
    ListNode* GetFirstCommonNodeNoCircle(ListNode* pHead1, ListNode* pHead2){

        if(pHead1 == NULL  || pHead2 == NULL)
            return NULL;


        // 如果不相交,直接返回NULL
        if (!isCross(pHead1, pHead2))
            return NULL;


        int step = 0;
        int lengthList1 = getListLength(pHead1);
        int lengthList2 = getListLength(pHead2);
        step = lengthList1 - lengthList2;

        if(step > 0){
            while(step--)
                pHead1 = pHead1->next;
        }
        else if(step<0){
            while(step++)
                pHead2 = pHead2->next;
        }

        while(pHead1 != pHead2){
            pHead1 = pHead1->next;
            pHead2 = pHead2->next;
        }
        return pHead1;    
    }

    // 獲取帶環鏈表環的長度
    int GetCircleLength(ListNode* meetNode){
        int length = 0;
        if(meetNode == NULL)
            return length;
        ListNode* next = meetNode->next;
        while(next != meetNode){
            length++;
            next = next->next;
        }
        return length;
    }

};

14、從尾到頭打印單鏈表

 void print_list_reverse(ListNode* pNode)
    {
        if (pNode == NULL)
            return;
        print_list_reverse(pNode->next);
        cout << pNode->data << " ";
    }

15、 刪除一個無頭單鏈表的非尾結點

   void delete_not_tail(ListNode* pNode)
    {
        assert(pNode);
        assert(pNode->next);

        pNode->data = pNode->next->data;
        ListNode* temp = pNode->next;
        pNode->next = temp->next;
        delete temp;
    }

16、輸入一個複雜鏈表(每個節點中有節點值,以及兩個指針,一個指向下一個節點,另一個特殊指針指向任意一個節點),返回結果爲複製後複雜鏈表的head。(注意,輸出結果中請不要返回參數中的節點引用,否則判題程序會直接返回空)

/*
struct RandomListNode {
    int label;
    struct RandomListNode *next, *random;
    RandomListNode(int x) :
            label(x), next(NULL), random(NULL) {
    }
};
*/
class Solution {
public:
    RandomListNode* Clone(RandomListNode* pHead)
    {
        if( NULL == pHead){
            return NULL;
        }

        // 保存新鏈表的頭
        RandomListNode* new_head = new RandomListNode(pHead->label);
        RandomListNode* cur_node_new_list = new_head;// 指向新鏈表的當前結點,用來遍歷
        RandomListNode* cur_node_old_list = pHead;   // 指向舊鏈表的當前結點,用來遍歷

        // 構建一個 map ,保存兩個鏈表相同位置上的結點指針
        map<RandomListNode*, RandomListNode*> map_random;
        map_random.insert(make_pair( cur_node_old_list, cur_node_new_list));// 插入第一個結點

        while(cur_node_old_list->next != NULL){
            // 創建一個新的結點
            RandomListNode* temp = new RandomListNode(cur_node_old_list->next->label);
            // 連接到新鏈表,並向後移動 cur 指針
            cur_node_new_list->next = temp;
            cur_node_new_list = temp;

            // 當前結點數據複製後,向後推進原鏈表的 cur
            cur_node_old_list = cur_node_old_list->next;
            // 將新節點與對應原結點指針插入 map
            map_random.insert(make_pair(cur_node_old_list, cur_node_new_list));
        }
        // 遍歷原鏈表,複製隨機指針指向
        cur_node_old_list = pHead;
        cur_node_new_list = new_head;

        while(cur_node_old_list){
            // 在map中取原結點的隨機指針指向
            cur_node_new_list->random = map_random[cur_node_old_list->random];
            cur_node_new_list = cur_node_new_list->next;
            cur_node_old_list = cur_node_old_list->next;
        }

        return new_head;
    }
};

17、兩個棧實現一個隊列

class Solution
{
public:
    void push(int node) {
        stack1.push(node);
    }

    int pop() {
       if(stack2.empty()){
            while(!stack1.empty()){
                stack2.push(stack1.top());
                stack1.pop();
            }
        }
        int ret = stack2.top();
        stack2.pop();
        return ret;
    }


private:
    stack<int> stack1;
    stack<int> stack2;
};

18、兩個隊列實現一個棧

#include<queue>
class Stack
{
public:
    void push(int data)
    {
        if (queue1.empty())
            queue2.push(data);
        else if (queue2.empty())
            queue1.push(data);
    }
    int pop()
    {
        int ret;
        if (queue1.empty())
        {
            while (queue2.size() > 1)
            {
                queue1.push(queue2.front());
                queue2.pop();
            }
            ret = queue2.front();
            queue2.pop();
        }
        else if (queue2.empty())
        {
            while (queue1.size() > 1)
            {
                queue2.push(queue1.front());
                queue1.pop();
            }
            ret = queue1.front();
            queue1.pop();
        }
        return ret;
    }
private:
    queue<int> queue1;
    queue<int> queue2;
};

void test_queue()
{
    Stack q;
    q.push(1);
    q.push(2);
    q.pop();
    q.push(3);
    q.pop();
    q.push(4);
    q.push(5);
    q.pop();
    q.pop();
    q.pop();
}

19、替換字符串中的空格爲$$$。要求時間複雜度爲O(N)
例如:將”talk is cheap show me the code”替換
爲”talk$$$is$$$cheap$$$show$$$me$$$the$$$code”。

void space_to_symbol()
{
    char str[100] = "talk is cheap show me the code";
    cout << str << endl;
    char* start = str;

    int space_count = 0;
    int old_length = 0;
    while (*start)
    {
        if (*start == ' ')
            space_count++;
        old_length++;
        start++;
    }
    int new_length = old_length + space_count * 2;
    while (old_length >= 0)
    {
        if (str[old_length] == ' ')
        {
            str[new_length--] = '$';
            str[new_length--] = '$';
            str[new_length--] = '$';
        }
        else
        {
            str[new_length--] = str[old_length];
        }
        old_length--;
    }
    cout << str << endl;
}

20、實現一個棧,要求push pop,min的時間複雜度爲O(1)
答:入棧時data 棧每次都需要壓入,min 棧,如果爲空則壓入,如果不爲空,用min棧頂元素和壓入的元素比較,如果壓入的元素小於min棧頂的值,則壓入min棧,並且壓入data棧。
出棧,如果兩個棧數據相同,則同時pop,否則只有data出棧

class Solution {
public:
    void push(int value) {
        if(s_min.empty()){
            s_min.push(value);
        }
        else{
            if(s_min.top() > value)
                s_min.push(value);
        }
        data.push(value);
    }
    void pop() {
        if(data.top() == s_min.top()){
            data.pop();
            s_min.pop();
        }
        else{
            data.pop();
        }
    }
    int top() {
        return data.top();
    }
    int min() {
        return s_min.top();
    }
private:
    stack<int> s_min;
    stack<int> data;
};

21、查找一個字符串中第一個只出現兩次的字符。
比如 “abcdefabcdefabc” 中第一個只出現兩次爲‘d’,
要求空間複雜度O(1),時間複雜度O(1)

char find_char_two(const char* str)
{
    assert(str);
    const char* start = str;
    char count[256]; // 因爲字符只有256 不管你字符串多長,都是O(1)的空間複雜度
    memset(count, 0, sizeof(count));
    // 統計次數
    while (*start)
    {
        count[*start]++;
        start++;
    }
    start = str;
    while (*start)
    {
        if (count[*start] == 2)
        {
            return *start;
        }
        start++;
    }
    return -1;
}

int main()
{
    //space_to_symbol();
    char c = find_char_two("abcdefabcdefabc");
    cout << c << endl;
    return 0;
}

22、輸入兩個整數序列,第一個序列表示棧的壓入順序,請判斷第二個序列是否爲該棧的彈出順序。假設壓入棧的所有數字均不相等。例如序列1,2,3,4,5是某棧的壓入順序,序列4,5,3,2,1是該壓棧序列對應的一個彈出序列,但4,3,5,1,2就不可能是該壓棧序列的彈出序列。(注意:這兩個序列的長度是相等的)

class Solution {
public:
    bool IsPopOrder(vector<int> v_push,vector<int> v_pop) {
        if(v_push.size() == 0 || v_pop.size() == 0 || v_push.size() != v_pop.size())
        return false;

        int idx_push = 0;
        int idx_pop = 0;
        int size = v_push.size();
        stack<int> s;
        while(idx_push < size)
        {
            s.push(v_push[idx_push++]);
            while(!s.empty() && s.top() == v_pop[idx_pop] && idx_pop < size)
            {
                s.pop();
                idx_pop++;
            }
        }
        return s.empty();
    }
};

23、輸入一個整數,輸出該數二進制表示中1的個數。其中負數用補碼錶示。

class Solution {
public:
     int  NumberOf1(int n) {
         int count = 0;
         while(n){
             count++;
             n = n&(n-1);
         }
         return count;
     }
};

24、 二叉樹的層序遍歷

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};*/
class Solution {
public:
    vector<int> PrintFromTopToBottom(TreeNode* root) {

        vector<int> v;
        if ( NULL == root)
            return v;
        queue<TreeNode*> q;
        q.push(root);
        while(!q.empty()){
            TreeNode* temp = q.front();
            v.push_back(temp->val);
            q.pop();
            if(temp->left)
                q.push(temp->left);
            if(temp->right)
                q.push(temp->right);
        }
        return v;

    }
};

25、給定一個整數N, 那麼N的階乘N!,末尾有多少個0呢?例如:N=10,N! = 3628800,末尾有兩個0。

int fac_zero_num(int num)
{
    int count = 0;
    int i = 1;
    for (; i <= num; i++)
    {
        int j = i;
        while (j % 5 == 0){
            count++;
            j /= 5;
        }
    }
    return count;
}

int fac_zero_num_2(int num)
{
    int ret = 0;
    while (num)
    {
        ret += num / 5;
        num /= 5;
    }
    return ret;
}

26、求二叉樹葉子結點的個數

int leaf_size(node* pnode)
    {
        if (NULL == pnode)
            return 0;
        if (NULL == pnode->left && NULL == pnode->right)
            return 1;
        return leaf_size(pnode->left) + leaf_size(pnode->right);
    }

27、求二叉樹第K層結點個數,設定根節點爲第一層

int num_K_node(node*pnode, int k)
    {
        if (pnode == NULL || k <= 0)
            return 0;
        if (pnode && k == 1)
            return 1;
        return num_K_node(pnode->left, k-1) + num_K_node(pnode->right, k-1);
    }

28、一個數組中有一個數字的次數超過了數組的一般,求出這個數字。如:int a[] = {2,3,2,2,2,2,2,4,1,2,3};

class Solution {
public:

    bool check(const vector<int>& v, int num){
        int count = 0;
        for(int i = 0; i < v.size(); ++i){
            if(num == v[i])
                count++;
        }
        if( count*2<=v.size())
            return false;
        return true;
    }
    int MoreThanHalfNum_Solution(vector<int> numbers) {

        if(numbers.size() == 0){
            cout << 0;
            return 0;
        }
        int count = 1;

        int length = numbers.size();

        int ret = numbers[0];
        int i = 1;
        for(; i < length; ++i){
            if(count == 0){
                ret = numbers[i];
                count = 1;
            }
            else if (ret == numbers[i])
                count++;
            else
                count--;
        }

        if(check(numbers, ret)){
            cout << ret;
            return ret;
        }
        cout << 0;
        return 0;
    }
};

day11

29、求二叉樹的高度

int TreeHeight(Node* pnode)
{
    if( NULL == pnode)
        return 0;
    int left= 1 + height(pnode->left);
    int right = 1 + height(pnode->right);
    return left > right ? left : right;
}

30、銷燬一顆二叉樹

void  Destroy(Node*& root)
{
    if(root)
    {
        Destroy(root->left);
        Destroy(root->right);
        delete root;
        root = NULL;
    }
}

31、鏈表翻轉。給出一個鏈表和一個數K,比如鏈表1->2->3->4->5->6->NULL,K=2,翻轉後 2->1->4->3->6->5->NULL,若K=3,翻轉後3->2->1->6->5->4->NULL,若K=4,翻轉後4->3->2->1->5->6->NULL,用程序實現ListNode* RotateList(ListNode* list, size_t k)

遞歸實現

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *reverseKGroup(ListNode *head, int k) {
        // 爲空或一個結點或 K 不符合 規範直接返回原head
        if( NULL == head || NULL == head->next || k < 2)
            return head;

        // 以k爲步數,找到逆置前下一組的頭指針
        ListNode* next_group_head = head;
        for(int i = 0; i < k; ++i){
            if(next_group_head)   
                next_group_head = next_group_head->next;
            else // 剩餘結點數小於K 不夠分一組時,下一組的頭指針就是head
                return head;
        }

        // 遞歸去處理剩餘組,直到不夠 K 個結點,即求出下一組逆置後的頭指針
        ListNode* new_next_group_head = reverseKGroup(next_group_head, k);
        ListNode* prev = NULL ,* cur = head;
        // 開始逆置處理當前組,從 head --- next_group_head下一組逆置前的第一個結點
        while(cur != next_group_head){
            ListNode* next = cur->next; // 保存下一個結點
            if(prev == NULL)      // 爲空則將當前組的一個結點指向下一組逆置後的第一個結點
                cur->next = new_next_group_head;
            else                  // 不爲空則表示逆置當前組的結點,指向prev即可
                cur->next = prev;
            prev = cur;   // 向後推進,直到走完當前組
            cur = next;           
        }
        // 返回逆置後的頭指針
        return prev;
    }
};

迭代解法

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
     ListNode *reverseKGroup(ListNode *head, int k)
    {
        // 特殊情況處理
        if (NULL == head || NULL == head->next || k < 2)
            return head;

        ListNode* prev = NULL, *pcur = head, *newhead = NULL;

        // 遍歷鏈表,處理每一組
        while (pcur){

            // 找到下一組的頭
            ListNode* next_group_head = pcur;
            int i = 0;
            for (; next_group_head && i < k; i++){
                next_group_head = next_group_head->next;
            }
            // 處理不足K 個元素的組
            if (i < k ){
                // 防止K 大於鏈表結點
                if (newhead == NULL)
                    newhead = head;
                break;
            }
            // 將當前分組[pcur, next_group_head) 逆置,並返回逆置後的頭指針
            ListNode* tmp = reverse(pcur, next_group_head);

            if (prev == NULL) // 第一個分組需要更新新的頭指針
                newhead = tmp;
            else  // 後續只需要將上一個分組的最後一個結點next 指向當前分組逆序後的的頭指針
                prev->next = tmp;
            // 當前分組的最後一個結點next 指向下一個分組開始
            pcur->next = next_group_head;
            // prev 保存當前分組的最後一個結點
            prev = pcur;
            // pcur 向後推進到下一個分組的開始
            pcur = next_group_head;
        }
        return newhead;
    }
    //    [start, end)
    ListNode* reverse(ListNode* start, ListNode* end)
    {
        ListNode* prev = NULL;
        ListNode* pcur = start;
        while (pcur != end){
            ListNode* next = pcur->next;
            pcur->next = prev;
            prev = pcur;
            pcur = next;
        }
        return prev;
    }
};

day12

32、輸入一棵二叉樹,判斷該二叉樹是否是平衡二叉樹。
(1)、方法1
前序遍歷的思想,每到一個結點求出其左右子樹的深度,判斷差是否大於-1或小於1。
然後遞歸判斷左右子樹。效率較低,因爲求高度時有很多結點被重複遍歷。

class Solution {
public:
    bool IsBalanced_Solution(TreeNode* pRoot) {
        if(pRoot == NULL)
            return true;

        int left_depth = Depth(pRoot->left);
        int right_depth = Depth(pRoot->right);
        int diff =  left_depth - right_depth;
        if( diff > 1 || diff < -1)
            return false;
        return IsBalanced_Solution(pRoot->left)&&IsBalanced_Solution(pRoot->right);

    }

    int Depth(TreeNode* node){
        if( NULL == node)
            return 0;
        int left = Depth(node->left);
        int right = Depth(node->right);

        return (left > right ? left + 1 : right + 1);
    }
};

(2)、方法2
採用後序遍歷的方式,通過一個引用參數從葉子結點往上求樹的高度,較少遍歷結點次數。如果差符合條件時,更新參數中傳來的height,並返回true。

class Solution {
public:
    bool IsBalanced_Solution(TreeNode* pRoot) {
        int index = 0;
        return IsBalanced(pRoot, index);
    }

    bool IsBalanced(TreeNode* node, int& height){
        if( node == NULL){
            height = 0;
            return true;
        }

        int left = 0;
        int right = 0;
        if( IsBalanced(node->left, left) && IsBalanced(node->right, right)){
            int diff = left - right;
            if(diff >= -1 && diff <= 1)
            {
                height = 1 + (left > right ? left : right);
                return true;
            }
        }
        return false;
    }
};

33、求一顆二叉樹的鏡像

class Solution {
public:
    void Mirror(TreeNode *pRoot) {
        if(pRoot == NULL)
            return;
        std::swap(pRoot->left, pRoot->right);
        Mirror(pRoot->left);
        Mirror(pRoot->right);
    }
};

34、在一個二維數組中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函數,輸入這樣的一個二維數組和一個整數,判斷數組中是否含有該整數。

從右上角開始

class Solution {
public:
    bool Find(int target, vector<vector<int> > array) {
        if( array.size() == 0)
            return false;
        int rows = array.size();
        int cols = array[0].size();

        int idx_row = 0;
        int idx_col = cols-1;
        while( idx_row <rows && idx_col >= 0){
            if(array[idx_row][idx_col] == target){
                return true;
            }
            else if(array[idx_row][idx_col] < target )
                    idx_row++;
            else
                    idx_col--;
        }
        return false;
    }
};

從左下角開始

class Solution {
public:
    bool Find(int target, vector<vector<int> > array) {

        if(array.size() == 0)
            return false;
        int row_size = array.size();
        int col_size = array[0].size();


        int idx_row = row_size - 1;
        int idx_col = 0;

        while(idx_col < col_size && idx_row >= 0)
        {
            if(target == array[idx_row][idx_col])
                return true;
            else if( target > array[idx_row][idx_col])
                ++idx_col;
            else
                --idx_row;
        }
      return false;
    }
};

day13

35、二叉樹的前序、中序和後序非遞歸遍歷

前序
藉助棧來實現。

 void PreOrder( node* pnode)
    {
        if (pnode == NULL)
            return;
        stack<node*> s;
        s.push(pnode);
        while (!s.empty())
        {
            const node* tmp = s.top();
            cout << tmp->data << " ";
            s.pop();
            if (tmp->right)
                s.push(tmp->right);
            if (tmp->left)
                s.push(tmp->left);
        }
    }

中序

void inOrder(node* root)
{
    if (root == NULL)
        return;
    stack<node*> s;
    node*  pcur = root;
    while (!s.empty() || pcur)
    {
        while (pcur)
        {
            s.push(pcur);
            pcur = pcur->left;
        }
        node* tmp = s.top();
        cout << tmp->data << " ";
        s.pop();
        if (tmp->right)
        {
            pcur = tmp->right;
        }
    }
}

後序

void postOrder(node* root)
{
    if (root == NULL)
        return;
    stack<node*> s;
    node* pcur = root;
    node* prev = NULL;

    while (pcur || !s.empty())
    {
        while (pcur)
        {
            s.push(pcur);
            pcur = pcur->left;
        }
        node* tmp = s.top();
        if (NULL == tmp->right || prev == tmp->r
        {
            cout << tmp->data << " ";
            s.pop();
            prev = tmp;
        }
        else
        {
            pcur = tmp->right;
        }
    }

}

36、已知集合A和B的元素分別用不含頭結點的單鏈表存儲,函數difference(
)用於求解集合A與B的差集,並將結果保存在集合A的單鏈表中。例如,若集合A={5,10,20,15,25,30},集合B={5,15,35,25},完成計算後A={10,20,30}。

void difference(node* & l1, node* l2)
{
    node* pa = l1;
    node* pb = l2;
    node* prev = NULL;
    while(pa)
    {
        node* pfind = pb;
        while(pfind && pa->data != pfind->data)
            pfind = pfind->next;
        if(pfind)
        {
            if(prev == NULL)
            {
                l1 = pa->next;
            }
            else
            {
                prev->next = pa->next;
            }
            node* tmp = pa;
            pa = pa->next;
            delete tmp;
        }
        else
        {
            prev = pa;
            pa = pa->next;
        }
    }
}

day14

37、判斷一個節點是否在一棵二叉樹中/在二叉樹中查找某個值

// 查找某個值
 node* Find(node* root, const T& key)
 {
     if (root == NULL)
         return NULL;
     if (root->data == key)
         return root;
     TreeNode<T>* ret = Find(root->left, key);
     if (ret)
         return ret;
     return Find(root->right, key);
 }

 // 判斷某個結點是否存在
bool InTree(TreeNode<T>* root, TreeNode<T>* pnode)
{
    if (root == NULL || pnode == NULL)
        return false;
    if (root == pnode)
        return true;
    bool ret = InTree(root->left,pnode);
    if (ret)
        return ret;
    return InTree(root->right, pnode);
}

38、輸入兩棵二叉樹A,B,判斷B是不是A的子結構。(ps:我們約定空樹不是任意一個樹的子結構)

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};*/
class Solution {

    bool is_subtree(TreeNode* pRoot1, TreeNode* pRoot2){
        if( NULL == pRoot2 )
            return true;
        if (NULL == pRoot1  || pRoot1->val != pRoot2->val)
            return false;
        return is_subtree(pRoot1->left, pRoot2->left) && is_subtree(pRoot1->right, pRoot2->right);
    }
public:
    bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
    {
        if( NULL == pRoot1 || NULL == pRoot2)
            return false;
        bool ret = false;
        if(pRoot1->val == pRoot2->val )
            ret = is_subtree(pRoot1, pRoot2);
        if(!ret)
            ret = HasSubtree(pRoot1->left, pRoot2);
        if(!ret)
            ret = HasSubtree(pRoot1->right, pRoot2);
        return ret;
    }
};

day15

39、判斷一棵樹是否是完全二叉樹

bool judge_full_binary_tree(TreeNode<char>* proot)
{
    if (NULL == proot)
        return true;
    bool must_has_child = false;
    queue<TreeNode<char>*> q;
    q.push(proot);
    while (!q.empty())
    {
        TreeNode<char>* tmp = q.front();
        q.pop();
        if (must_has_child)
        {
            if (tmp->left || tmp->right)
                return false;
        }
        else
        {
            if (tmp->left && tmp->right)
            {
                q.push(tmp->left);
                q.push(tmp->right);
            }
            else if (tmp->left && tmp->right == NULL)
            {
                must_has_child = true;
                q.push(tmp->left);
            }
            else if (tmp->left == NULL && tmp->right)
                return false;
        }
    }
    return true;
}

40、求二叉樹中兩個節點的最近公共祖先。

要求考慮以下三種種情況,給出解決方案,並解決:

1:二叉樹每個節點有parent(三叉鏈)

2:二叉樹是搜索二叉樹。

3:就是普通二叉樹。(儘可能實現時間複雜度爲O(N))

(1)、二叉樹每個節點有parent(三叉鏈)

// 獲取鏈表長度
int GetLength(TreeNode* pnode)
{
    int length = 0;

    while (pnode)
    {
        length++;
        pnode = pnode->parent;
    }

    return length;
}

// 兩個節點的最近公共祖先。 
TreeNode* common_ancestor(TreeNode* node1, TreeNode* node2)
{
    if (NULL == node1 || NULL == node2)
        return NULL;

    int list1_length = GetLength(node1);
    int list2_length = GetLength(node2);
    int diff = list1_length - list2_length;
    if (diff > 0)
    {
        while (diff--)
            node1 = node1->parent;
    }
    else if (diff < 0)
    {
        while (diff++)
            node2 = node2->parent;
    }

    while (node1 != node2)
    {
        node1 = node1->parent;
        node2 = node2->parent;
    }

    return node1;
}

(2)、二叉樹是搜索二叉樹

循環

BSTreeNode* common_ancestor(BSTreeNode* root,BSTreeNode* node1, BSTreeNode* node2)
{
    if (node1 == NULL || node2 == NULL || root == NULL)
        return NULL;


    BSTreeNode* pcur = root;

    while (pcur)
    {

        if (pcur->data < node1->data &&
            pcur->data < node2->data)
            pcur = pcur->right;
        else if (pcur->data > node1->data &&
            pcur->data > node2->data)
            pcur = pcur->left;
        else
            return pcur;
    }
    return NULL;
}

3:就是普通二叉樹。(儘可能實現時間複雜度爲O(N))

普通方法時間複雜度高一些

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {

    bool GetPath(vector<TreeNode*>& v, TreeNode* root, TreeNode* p)
    {
        if(root == p)
        {
            v.push_back(root);
            return true;
        }

        if(NULL == root)
            return false;

        v.push_back(root);
        if(GetPath(v,root->left,p))
            return true;
        if(GetPath(v,root->right,p))
            return true;
        v.pop_back();
        return false;
    }

    TreeNode* GetCommNode(vector<TreeNode*>& v1, vector<TreeNode*>& v2)
    {
        TreeNode* ret = NULL;

        int i = 0;
        int j = 0;
        while(i < v1.size() && j < v2.size())
        {
            if(v1[i] == v2[j])
                ret = v1[i];
            i++;
            j++;
        }
        return ret;
    }

public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {

        if( NULL == root || p == NULL || q == NULL)
            return NULL;
        vector<TreeNode*> path1;
        GetPath(path1,root, p);

        vector<TreeNode*> path2;
        GetPath(path2,root, q);

        return GetCommNode(path1,path2);


    }
};

遞歸方法O(N)

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if( NULL == root)
            return NULL;
        if( root == p || root == q )
            return root;

        TreeNode* left = lowestCommonAncestor(root->left, p,q);
        TreeNode* right = lowestCommonAncestor(root->right, p, q);

        if(left && right)
            return root;
        else 
            return left ? left : right;
    }
};

day16


41、由前序遍歷和中序遍歷重建二叉樹(前序:1 2 3 4 5 6 中序 3 2 4 1 6 5)

class Solution 
{
private:
    TreeNode* ConTree(const vector<int>& pre, int startPre, int endPre, 
                      const vector<int>& vin, int startIn,  int endIn )
    {
        if(startPre > endPre || startIn > endIn)
            return NULL;
        TreeNode* root = new TreeNode(pre[startPre]);

        for(int i = startIn; i <= endIn; ++i)
        {
            if (vin[i] == root->val){
                root->left = ConTree(pre, startPre + 1, startPre+i-startIn,
                                     vin, startIn,  i - 1);
                root->right = ConTree(pre, startPre+i-startIn+1, endPre, 
                                      vin, i+1, endIn);
            }
        }
        return root;
    }
public:
    TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) 
    {

        if( pre.size() != vin.size() || pre.empty() || vin.empty())
            return NULL;
        TreeNode* root = ConTree(pre, 0, pre.size() - 1, vin, 0, vin.size()-1);
        return root;
    }


};

42、C語言模擬實現C++繼承和多態

提示:C 實現一個 struct A 和 stuct B 包含一個 int 成員 a 和 b,要求達到B 繼承 A 的效果,也就是 B裏面包含一個 A,並且能達到多態的效果,也就是一個 A* p 指向一個指向A 調的是 A 的函數,指向 B 調的是 B 的函數。

// 父類虛表
typedef struct vtblA
{
    void(*pfun1)();
}vtblA;

// 子類虛表
typedef struct vtblB
{
    vtblA vtbl;
    // 指向子類特有的成員函數
    void(*fun2_B)(int);
}vtblB;

// 父類虛函數
void fun1()
{
    printf("父類fun1\n");
}
// 子類重寫父類虛函數fun1
void fun1_B()
{
    printf("子類重寫fun1\n");
}

// 子類特有函數
void fun2_B(int)
{   
    printf("子類特有fun2\n");
}

// 父類
typedef struct A
{
    vtblA* pvtl;// 父類虛表指針
    int a;      // 父類數據成員
}A;

// 子類
typedef struct B
{
    A base_a; // 從父類繼承而來的基類A
    int b;    // 子類數據成員
}B;

// 父類虛表結構
vtblA g_vtbl_A = {&fun1};
// 子類虛表結構
vtblB g_btbl_B = { {&fun1_B}, &fun2_B };

// 父類構造函數
void init_A(A* pa)
{
    pa->a = 10;
    pa->pvtl = &g_vtbl_A;
}
// 子類構造函數
void init_B(B* pb)
{
    init_A((A*)pb);
    pb->base_a.pvtl = (vtblA*)&g_btbl_B;
    pb->b = 20;
}
// 測試多態函數
void dosomething(A* p)
{
    // 如果p指向子類對象那麼輸出結果就是重寫後的函數pfun1
    vtblA* pvtbl = (vtblA*)p->pvtl;
    (*pvtbl).pfun1();
}
int main()
{

    // 定義子類對象並構造
    B b;
    init_B(&b);
    // 調用父類自己的函數
    vtblB* pvtvl = (vtblB*)b.base_a.pvtl;
    (*pvtvl).fun2_B(5);
    // 定義父類指針
    A* pa = (A*)&b;
    // 測試多態
    dosomething(pa);
    return 0;
}

day17

43、輸入一棵二叉搜索樹,將該二叉搜索樹轉換成一個排序的雙向鏈表。要求不能創建任何新的結點,只能調整樹中結點指針的指向。

class Solution {
public:
    TreeNode* Convert(TreeNode* pRootOfTree)
    {
        TreeNode* list_head = NULL;
        Con(pRootOfTree,list_head);
        return list_head;
    }
    void Con(TreeNode* root, TreeNode*& prev){
        if(root == NULL)
            return ;
        Con(root->right, prev);
        root->right = prev;
        if(prev)
            prev->left = root;
        prev = root;
        Con(root->left,prev);
    }
};

實現單例模式,線程安全且高效

懶漢 模式
特點:第一個獲取時創建實例,類似寫時拷貝,節省資源,缺點是需要加鎖,性能上可能會有影響。

// 單例模式之懶漢

std::mutex mtx;

class Singleton
{
public:
    static Singleton* GetInstance()
    {
        if (NULL == instance)
        {
            // 守衛鎖 (RAII) 防止異常造成死鎖
            // 以及保證線程安全
            lock_guard<mutex> clk(mtx);
            if (NULL == instance)
            {

                Singleton* tmp = new Singleton();
                MemoryBarrier();// 內存柵欄,防止指令優化導致順序改變
                instance = tmp;
            }
        }
        return instance;
    }

    static void DelInstance()
    {
        if (instance)
        {
            delete instance;
            instance = NULL;
        }
    }
    void Print()
    {
        cout << a << endl;
    }
    // 刪除對象可以用GC類然後定義全局對象 或者 etexit
    class GC
    {
    public:
        ~GC()
        {
            cout << "DelInstance" << endl;
            DelInstance();
        }
    };
private:
    // 防止拷貝
    Singleton()
        :a(1)
    {}
    Singleton(Singleton&);
    Singleton& operator=(const Singleton&);
private:
    static Singleton* instance;
    int a;
};

Singleton* Singleton::instance = NULL;

餓漢模式
特點:簡潔高效,無需加鎖,但是在靜態加載時會有缺陷。

// 餓漢模式
// 局部靜態或者全局靜態
class Singleton
{
public:
    static Singleton* GetInstance()
    {
        static Singleton instance;
        return &instance;
    }
    void Print()
    {
        cout << a << endl;
    }
    // 刪除對象可以用GC類 或者 etexit
private:
    // 防止拷貝
    Singleton()
        :a(1)
    {}
    Singleton(Singleton&);
    Singleton& operator=(const Singleton&);
private:
    int a;
};

day18

44、實現插入排序和希爾排序

插入排序

void InsertSort(int* a, int size)
{
    if (NULL == a || size <= 0)
        return;
    for (int i = 1; i < size; ++i)
    {
        int temp = a[i];
        int end = i - 1;
        while (end >= 0 && a[end] > temp)
        {
            a[end + 1] = a[end];
            end--;
        }
        a[end + 1] = temp;
    }
}

希爾排序

void ShellSort(int* a, int size)
{
    for (int gap = size / 2; gap > 0; gap /= 2)
    {
        for (int i = gap; i < size; i += gap)
        {
            int tmp = a[i];
            int end = i - gap;
            while (end >= 0 && a[end] > tmp)
            {
                a[end + gap] = a[end];
                end -= gap;
            }
            a[end + gap] = tmp;
        }
    }
}

45、 使用shell 腳本實現 希爾排序

#!/bin/bash


# 希爾排序
function shell_sort()
{
    # 獲取數組長度
    size=${#a[@]}

    for ((gap=size/2; gap > 0; gap/=2))
    do
        for ((idx = gap; idx < size; idx+=gap))
        do
            let tmp=a[idx]
            let end=idx-gap
            let save=a[end]
            while [ $end -ge 0  -a $save -ge $tmp ]
            do
                let a[end+gap]=a[end]
                let end=end-gap
                if [ $end -ge 0 ];then
                    let save=a[end]
                fi
            done
            let a[end+gap]=tmp
        done
    done
}

# 獲取數組個數
echo 'please input num'
read num
i=0
while [ $num -gt 0 ]
do
    read a[i]
    let i++
    let num-=1
done


shell_sort a

echo "排序 完成 開始打印"
echo ${a[@]}

day19

46、實現選擇排序和堆排序

選擇排序

static void selectSort(int* a, int size)
{
    if (a == NULL || size <= 0)
        return;
        // i < size - 1 最後一個元素已經是有序
    for (int i = 0; i < size - 1; ++i)
    {
        int min_flag = i;
        for (int j = i+1; j < size; ++j)
        {
            if (a[min_flag] > a[j])
                min_flag = j;
        }
        if (min_flag != i)
            std::swap(a[i], a[min_flag]);
    }
}

堆排序

void heapAdjustDown(int *a, int parent, int size)
{
    for (int child = parent * 2 + 1; child < size; child = parent * 2 + 1)
    {
        if (child + 1 < size && a[child + 1] > a[child])
            child++;
        if (a[parent] < a[child])
        {
            std::swap(a[parent], a[child]);
            parent = child;
        }
        else
            break;
    }
}

void heapSort(int* a, int size)
{
    if (a == NULL || size <= 0)
        return;
    // 1. 建堆
    for (int lastNotLeaf = (size - 2) / 2; lastNotLeaf >= 0; --lastNotLeaf)
        heapAdjustDown(a, lastNotLeaf, size);

    // 2.排序
    int count = size - 1;
    while (count > 0)
    {
        std::swap(a[0], a[count]);
        heapAdjustDown(a, 0, count);
        count--;
    }
}

47、本公司現在要給公司員工發波福利,在員工工作時間會提供大量的水果供員工補充營養。由於水果種類比較多,但是卻又不知道哪種水果比較受歡迎,然後公司就讓每個員工報告了自己最愛吃的k種水果,並且告知已經將所有員工喜歡吃的水果存儲於一個數組中。然後讓我們統計出所有水果出現的次數,並且求出大家最喜歡吃的前k種水果。

void GetFavoriteFruit(const vector& fruits,size_t k);

ps:要求打印出最喜歡的水果,並且效率儘可能的高。

提示:儘量STL的容器和算法,這樣能更快速高效的實現。


void GetFavoriteFruit(const vector<string>& fruits, size_t k)
{
    if (fruits.size() == 0 || k == 0)
        return;

    // 統計次數
    map<string, int> fruits_count;

    for (int i = 0; i < fruits.size(); ++i)
    {
        fruits_count[fruits[i]]++;
    }

    // 比較函數
    struct Compare
    {
        bool operator()(const  map<string, int>::iterator& left, const  map<string, int>::iterator& right)
        {
            return  left->second > right->second;
        }
    };

    set< map<string, int>::iterator, Compare> myset;
    map<string, int>::iterator it=fruits_count.begin();

    while (it != fruits_count.end())
    {
        myset.insert(it);
        it++;
    }

    set< map<string, int>::iterator>::iterator itset = myset.begin();
    while (k-- > 0)
    {
        if (itset != myset.end())
        {
            cout << (*itset)->first << " 次數 : " << (*itset)->second << endl;
            itset++;
        }
        else
        {
            cout << "K 有誤" << endl;
            return;
        }
    }
}

day20

48、冒泡排序和排序排序的三種實現方式

冒泡排序普通

void bubleSort(int*a, int size)
{
    if (a == NULL || size <= 0)
        return;
    for (int i = 0; i < size - 1; ++i)
        for (int j = 0; j < size - 1 - i; ++j)
        if (a[j] > a[j + 1])
            std::swap(a[j], a[j + 1]);
}

冒泡排序優化

void bubleSort_2(int*a, int size)
{
    if (a == NULL || size <= 0)
        return;
    bool flag = true;
    for (int i = 0; i < size - 1; ++i)
    {
        for (int j = 0; j < size - 1 - i; ++j)
        {
            if (a[j] > a[j + 1])
            {
                std::swap(a[j], a[j + 1]);
                flag = false;
            }
        }
        if (flag)
            break;
    }
}

快速排序交換版本

/*
*  交換法 左邊從右邊開始,左邊從右邊開始
*/
int partition(int* a, int low, int hight)
{
    int begin = low;
    int end = hight - 1;
    int baseval = a[end];
    while (begin < end)
    {
        while (begin < end && a[begin] <= baseval)
            begin++;
        while (begin < end && a[end] >= baseval)
            end--;
        if (begin < end)
            std::swap(a[begin], a[end]);
    }
    std::swap(a[begin], a[hight - 1]);
    return begin;
}

快速排序填坑法

/*
* 填坑法
*/

int partition2(int*a, int left, int right)
{
    int begin = left;
    int end = right - 1;
    int baseval = a[begin];
    while (begin < end)
    {
        while (begin < end && a[end] >= baseval)
            end--;
        a[begin] = a[end];
        while (begin < end && a[begin] <= baseval)
            begin++;
        a[end--] = a[begin];
    }
    a[begin] = baseval;
    return begin;
}

快速排序雙指針同向法


/* 
* 如果基準值在左邊,prev 右邊的都比基準值大,所需直接和low 交換
*如果是在右邊, prev 右邊的基準值都比基準值大,所以需要++然後在交換
*/
int partition3(int*a, int left, int right)
{   
    int prev = left - 1;
    int pcur = left;
    int baseval = a[left];
    while (pcur < right)
    {
        if (a[pcur] <= baseval && ++prev != pcur)
            std::swap(a[prev], a[pcur]);
        ++pcur;
    }
    std::swap(a[prev], a[left]);
    return prev;
}
void _quickSort(int *a, int left,  int right)
{
    if (left < right)
    {
        int key = partition3(a, left, right);
        _quickSort(a, left, key);
        _quickSort(a, key + 1, right);
    }
}

49、–實現快速排序的非遞歸。

void quickSort(int*a, int size)
{
    if (a == NULL || size <= 0)
        return;
    stack<int> s;
    int left = 0;
    int right = size;
    s.push(left);
    s.push(right);
    while (!s.empty())
    {
        right = s.top();
        s.pop();
        left = s.top();
        s.pop();
        if (left < right)
        {
            int key = partition2(a, left, right);
            s.push(left);
            s.push(key);
            s.push(key + 1);
            s.push(right);
        }
    }
}

並分析以下問題:

1:什麼時候快速排序最壞?怎麼避免最壞情況的出現?
基準值選的最差的時候最壞:
(1)、已經有有序
(2)、基準值每次都在開頭選導致分區不平衡
優化?

  • 隨機樞紐
  • 三數區中
  • 元素較少插入排序
  • 用棧

2:快速排序的時間複雜度是多少?

快速排序最佳運行時間O(nlogn),最壞運行時間O(N^2),隨機化以後期望運行時間O(nlogn),

day21

50、實現歸併排序

遞歸版本

void Merge(int* a, int * tmp, int left, int mid, int right)
{
    int begin1 = left;
    int end1 = mid;
    int begin2 = mid + 1;
    int end2 = right;
    int index = left;
    while (begin1 <= end1 && begin2 <= end2)
    {
        if (a[begin1] <= a[begin2])
            tmp[index++] = a[begin1++];
        else
            tmp[index++] = a[begin2++];
    }
    while (begin1 <= end1)
        tmp[index++] = a[begin1++];
    while (begin2 <= end2)
        tmp[index++] = a[begin2++];
}

void _MergeSort(int *a, int * tmp, int left, int right)
{
    if (left < right)
    {
        int mid = (left + right) / 2;
        _MergeSort(a, tmp, left, mid);
        _MergeSort(a, tmp, mid + 1, right);
        Merge(a, tmp, left, mid, right);
        memcpy(a + left, tmp + left, sizeof(int)*(right - left + 1));
    }
}

void MergeSort(int* a, int size)
{
    int * temp = new int[size];

    _MergeSort(a, temp, 0, size - 1);

    delete temp;
}

非遞歸版本

void MergeSort_Nor(int* a, int size)
{
    // 1. 分區
    // 2. 歸併
    // 3. 拷貝回原數組 
    // 4. 重複 1.2.3.直到區間大於size
    int * temp = new int[size];
    int step = 1;
    while (step < size)
    {
        for (int i = 0; i < size; i += 2*step)
        {
            int left = i;
            int mid = left + step - 1;
            int right = mid + step;
            if (mid >= size)
                mid = size - 1;
            if (right >= size)
                right = size - 1;

            Merge(a, temp, left, mid, right);
        }
        memcpy(a, temp, sizeof(int)*size);
        step *= 2;
    }

    delete[] temp;
}

51、有一個大文件內容如下:
data.txt
小明,1班,88分
小李,1班,90分
小王,1班,99分
小楊,1班,59分

要求:這個文件內容很大,不能全部放到內存,請按第三列的分數對這個文件的內容進行排序。
ps:可以考慮兩方面入手:歸併排序或linux指令。另外如果用代碼實現,可以儘可能使考慮使用STL輔助,也不用造太大的文件,太麻煩,就給一個小文件,給10行數據,假設內存中最多能放3行數據,這樣模擬實現就可以。

1、分
2、排序
3、合

split -- cat  -- sort 

day22

52、int a[] = { 12, 13, 12, 13, 19, 18, 15, 12, 15, 16, 17 },要求對數組a進行排序,要求時間複雜度爲O(N)

void GetMinMax(int *a, int size, int& min_data, int & max_data)
{
    min_data = max_data = a[0];
    for (int i = 1; i < size; ++i)
    {
        if (a[i] > max_data)
            max_data = a[i];
        if (a[i] < min_data)
            min_data = a[i];
    }
}

void CountSort(int *a, int size)
{
    int min_data;
    int max_data;
    GetMinMax(a, size,min_data, max_data);
    int range = max_data - min_data + 1;

    int *pcount = new int[range];
    memset(pcount, 0, sizeof(int)* range);
    for (int i = 0; i < size; ++i)
    {
        pcount[a[i]-min_data]++;
    }
    int index = 0;
    for (int i = 0; i < range; i++)
    {
        for (int j = 0; j < pcount[i]; ++j)
            a[index++] = i + min_data;
    }
}

53、求一個無序數組的中位數。
如:{2,5,4,9,3,6,8,7,1}的中位數爲5,{2,5,4,9,3,6,8,7,1,0}的中位數爲4和5。
要求:不能使用排序,時間複雜度儘可能低。
提示:考慮堆或者快排思想解決。


static int partition(int*a, int left, int right)
{
    int begin = left;
    int end = right - 1;
    int baseval = a[begin];
    while (begin < end)
    {
        while (begin < end && a[end] >= baseval)
            end--;
        a[begin] = a[end];
        while (begin < end && a[begin] <= baseval)
            begin++;
        a[end] = a[begin];
    }
    a[begin] = baseval;
    return begin;
}
static int MidElem(int*a, int size)
{
    if (a == NULL || size <= 0)
        return 0x7FFFFFFFF;

    int left = 0;
    int right = size;
    int mid = left + (right - left) / 2;
    int key = partition(a, left, right);
    if (size % 2 != 0)
    {
        while (key != mid)
        {
            if (key > mid)
                key = partition(a, left, key);
            else if (key < mid)
                key = partition(a, key + 1, right);
        }
        cout << a[mid] << endl;;
    }
    else
    {
        while (key != mid)
        {
            if (key > mid)
                key = partition(a, left, key);
            else if (key < mid)
                key = partition(a, key + 1, right);
        }
        cout << a[mid] << endl;
        mid -= 1;
        while (key != mid)
        {
            if (key > mid)
                key = partition(a, left, key);
            else if (key < mid)
                key = partition(a, key + 1, right);
        }
        cout << a[mid ] << endl;
    }



}

day23

54、將N個字符的數組,循環右移K位。時間複雜度O(N)。

普通解法

class Solution {
public:

    void reverse(string& str, int begin, int end){
        while (begin < end){
            std::swap(str[begin++], str[end--]);
        }
    }
    string LeftRotateString(string str, int n) {
        if (str.empty() || n < 0)
            return "";
        int begin1 = 0;
        int end1 = n - 1;
        int begin2 = n;
        int end2 = str.size() - 1;
        reverse(str, begin1, end1);
        reverse(str, begin2, end2);
        reverse(str, begin1, end2);
        return str;
    }
};

神奇解法

static string rever(string str, int n)
{
    if (n == 0 || str.empty())
        return "";
    int size = str.size();
    str += str;
    return str.substr(n , size);
}

55、刪除小寫字母字符串中重複字符。如果可以,優先刪除重複字符中排在比他小字符前面的字符。 比如,輸入:bbcacdww;輸出:bacdw

void DeleteReptChar(char* str)
{
    char count[26] = { 0 };
    char* pstart = str;
    while (*pstart)
    {
        int index = (*pstart++) - 'a';
        count[index]++;
    }
    while (*str)
    {
        char index = *str - 'a';
        if (count[index] == 1)
            cout << *str;
        else if (count[index] > 1)
            count[index]--;
        str++;
    }
}

day24

56 、實現一個位圖

class BitMap
{
public:
    BitMap(size_t size = 32)
        :_size(size)
    {
        _bitmap.resize(size / 32 + 1);
    }
    void Set(unsigned data)
    {
        // 獲取下標
        size_t index = data / 32;
        // 獲取比特位未知
        size_t seq = data % 32;
        _bitmap[index] = _bitmap[index] | (1 << seq);
    }
    void Reset(unsigned data)
    {
        // 獲取下標
        size_t index = data / 32;
        // 獲取比特位未知
        size_t seq = data % 32;
        int flag = 1 << seq;
        _bitmap[index] = _bitmap[index] & (~flag);
    }

    bool Check(unsigned data)
    {

        // 獲取下標
        size_t index = data / 32;
        // 獲取比特位未知
        size_t seq = data % 32;
        // 防止越界,不允許訪問超過size 的值
        assert(_size >= data);
        if (_bitmap[index] & (1 << seq))
            return true;
        return false;
    }

private:
    vector<int> _bitmap;
    size_t _size;
};


void TestBitmap()
{
    BitMap bm(10);
    bm.Set(0);
    bm.Set(11);
    cout << boolalpha << bm.Check(0) << endl;
    cout << boolalpha << bm.Check(2) << endl;
    cout << boolalpha << bm.Check(3) << endl;
    cout << boolalpha << bm.Check(11) << endl;//不允許
}

57、
以下三個問題都是位圖相關類似題目,請給出解決方案:

1)給定100億個整數,設計算法找到只出現一次的整數

思路:
100億整數的文件在32位平臺下大概40GB,一次無法裝下,如果有需要可以將文件分割(Linux 命令 split)。
現在不考慮文件需要分割的情況。

在32爲平臺 int 整數一共有 42 億個多,現在題目給了100億說明肯定有重複。

如果一個整數存儲需要一個字節,那麼 4GB 剛好可以存儲42 億多。

再如果一個整數用1個bit 位表示,需要4GB/8 = 512M 大小的內存空間。因爲 1B = 8 bit。

但是題目要求找出只出現一次的整數,說明一個bit 根本達不到目的,那麼此時就可以再佔用1bit位,用2個bit位表示一個整數。 00 表示不存在, 01 表示出現一次,10 表示出現多次,11 不關心。這樣我們需要表示42 億整數的內存就會是原來的大小翻倍,即1GB。

如果這個時候,考官還想你500M左右處理這道題呢?那麼,我們可以考慮正整數和負整數分開處理。


2)給兩個文件,分別有100億個整數,我們只有1G內存,如何找到兩個文件交集

思路一
使用hash函數將第一個文件所有整數映射到1000個文件中,分別編號a1,a2…a1000每個文件中大約有1000萬個整數,大約40 M內存。

用同樣的方法將第二個文件映射到,b1,b2。。。b1000。由於使用相同的hash函數,所有兩個文件中一樣的數字會被分配到文件編號一直的文件中,分別對a1,b2 …. a1000 b1000 求交集,最後彙總

思路二
42億個整數每個整數用一個bit位需要512M 內存,遍歷一個文件,將出現的數字置爲1,遍歷完後,再遍歷第二個文件,如果對應的bit位不爲0,就是重複出現的數字,直到遍歷完。


3)1個文件有100億個int,1G內存,設計算法找到出現次數不超過2次的所有整數

使用位圖,用兩個比特位表示一個整數,1G內存剛好,00表示沒出現,01表示一次,10表示兩個,11表示多次,最後只需要檢查01 和 10即可。


day25

58 、實現一個簡單的布隆過濾器

size_t BKDRHash(const char * str)
{
    unsigned int seed = 131; // 31 131 1313 13131 131313
    unsigned int hash = 0;
    while (*str)
    {
        hash = hash * seed + (*str++);
    }
    return (hash & 0x7FFFFFFF);
}
size_t SDBMHash(const char* str)
{
    register size_t hash = 0;
    while (size_t ch = (size_t)*str++)
    {
        hash = 65599 * hash + ch;
        //hash = (size_t)ch+(hash<<6)+ (hash<<16)-hash;
    }

    return hash;
}
size_t RSHash(const char *str)
{
    register size_t hash = 0;
    size_t magic = 63689;
    while (size_t ch = (size_t)*str++)
    {
        hash = hash * magic + ch;
        magic *= 378551;
    }

    return hash;
}
size_t APHash(const char* str)
{
    register size_t hash = 0;
    size_t ch;
    for (long i = 0; ch = (size_t)*str++; i++)
    {
        if (0 == (i & 1))
        {
            hash ^= ((hash << 7) ^ (hash >> 3));
        }
        else
        {
            hash ^= (~((hash << 11) ^ ch ^ (hash >> 5)));
        }
    }

    return hash;
}
size_t JSHash(const char* str)
{
    if (!*str)
        return 0;

    register size_t hash = 1315423911;
    while (size_t ch = (size_t)*str++)
    {
        hash ^= ((hash << 5) + ch + (hash >> 2));
    }

    return hash;
}

template<class K>
struct __HashFunc1
{
    size_t operator()(const K& key)
    {
        return BKDRHash(key.c_str());
    }
};
template<class K>
struct __HashFunc2
{
    size_t operator()(const K& key)
    {
        return SDBMHash(key.c_str());
    }
};
template<class K>
struct __HashFunc3
{
    size_t operator()(const K& key)
    {
        return RSHash(key.c_str());
    }
};
template<class K>
struct __HashFunc4
{
    size_t operator()(const K& key)
    {
        return APHash(key.c_str());
    }
};
template<class K>
struct __HashFunc5
{
    size_t operator()(const K& key)
    {
        return JSHash(key.c_str());
    }
};

class BitMap
{
public:
    BitMap(size_t size = 32)
        :_size(size)
    {
        _bitmap.resize(size / 32 + 1);
    }
    void Set(unsigned data)
    {
        // 獲取下標
        size_t index = data / 32;
        // 獲取比特位未知
        size_t seq = data % 32;
        _bitmap[index] = _bitmap[index] | (1 << seq);
    }
    void Reset(unsigned data)
    {
        // 獲取下標
        size_t index = data / 32;
        // 獲取比特位未知
        size_t seq = data % 32;
        int flag = 1 << seq;
        _bitmap[index] = _bitmap[index] & (~flag);
    }

    bool Check(unsigned data)
    {

        // 獲取下標
        size_t index = data / 32;
        // 獲取比特位未知
        size_t seq = data % 32;
        // 防止越界,不允許訪問超過size 的值
        assert(_size >= data);
        if (_bitmap[index] & (1 << seq))
            return true;
        return false;
    }

private:
    vector<int> _bitmap;
    size_t _size;
};

template <class K,
class HashFun1 = __HashFunc1<K>,
class HashFun2 = __HashFunc2<K>,
class HashFun3 = __HashFunc3<K>,
class HashFun4 = __HashFunc4<K>,
class HashFun5 = __HashFunc5<K> >
class BloomFilter
{
public:
    BloomFilter( size_t size = 20)
        :_bitmap(size)
        ,_size(size)
    {}

    void Insert(const K& data)
    {
        size_t index1 = HashFun1()(data) % _size;
        size_t index2 = HashFun2()(data) % _size;
        size_t index3 = HashFun3()(data) % _size;
        size_t index4 = HashFun4()(data) % _size;
        size_t index5 = HashFun5()(data) % _size;
        _bitmap.Set(index1);
        _bitmap.Set(index2);
        _bitmap.Set(index3);
        _bitmap.Set(index4);
        _bitmap.Set(index5);
    }

    bool Find(const K& data)
    {
        size_t index1 = HashFun1()(data) % _size;
        size_t index2 = HashFun2()(data) % _size;
        size_t index3 = HashFun3()(data) % _size;
        size_t index4 = HashFun4()(data) % _size;
        size_t index5 = HashFun5()(data) % _size;
        if (_bitmap.Check(index1) && _bitmap.Check(index2) &&
            _bitmap.Check(index3) && _bitmap.Check(index4) &&
            _bitmap.Check(index5))
            return true;

        return false;
    }
private:
    BitMap _bitmap;
    size_t _size;
};

void TestBloomFilter()
{
    BloomFilter<string> s;
    s.Insert("呵呵");
    s.Insert("霍");
    s.Insert("Tom");
    cout << boolalpha << s.Find("呵呵") << endl;
    cout << boolalpha << s.Find("霍") << endl;
    cout << boolalpha << s.Find("Tom") << endl;
    cout << boolalpha << s.Find("呵") << endl;
    cout << boolalpha << s.Find("不存在的") << endl;
    cout << boolalpha << s.Find("哈哈") << endl;
}

59、1:布隆過濾器存在是準確的還是不存在是準確的?

答:布隆過濾器是存在誤差的,數據分析方法有誤差率。

2:布隆過濾器優缺點?
答:可以快速判斷某個字符串是否在所屬文件中,用在爬蟲中,檢測URL是否已經訪問過。

3:如何擴展BloomFilter使得它支持刪除元素的操作?請設計實現一個支持刪除的布隆過濾器.
答:講bloomFilter 底層的 位圖換位計數 數組即可。


day26

60、模擬實現C庫的memcpy和memmove

memcpy基礎版

void* my_memcpy(void* destination, const void* source, size_t num)
{
    void* ret = destination;
    assert(destination);
    assert(source);
    while (num--)
    {
        *(char*)destination = *(char*)source;
        destination = (char*)destination + 1;
        source = (char*)source + 1;
    }
    return ret;
}

memcpy考慮重疊版

void* my_memcpy(void* destination, const void* source, size_t num)
{
    assert(destination);
    assert(source);

    char* pdest = static_cast<char*>(destination);
    const char* psrc = static_cast<const char*>(source);
    if (pdest > psrc && pdest < psrc + num)
    {
        while (num)
        {
            *(pdest + num - 1) = *(psrc + num - 1);
            num--;
        }
    }
    else
    {
        while (num--)
            *pdest++ = *psrc++;
    }
    return destination;
}

memcpy優化版本

void* my_memcpy_3(void* destination, const void* source, size_t num)
{
    assert(destination);
    assert(source);
    size_t byteNum = num / sizeof(int);
    size_t bitNum = num % sizeof(int);
    int * pdest = (int*)destination;
    const int*  psrc = (const int*)source;
    while (byteNum--)
        *pdest++ = *psrc++;
    char* pd = (char*)pdest;
    const char* ps = (const char*)psrc;
    while (bitNum--)
        *pd++ = *ps++;
    return destination;
}

memmove

void* my_memmove(void* destination, const void* source, size_t num)
{
    assert(destination);
    assert(source);

    char* pdest = static_cast<char*>(destination);
    const char* psrc = static_cast<const char*>(source);
    if (pdest > psrc && pdest < psrc + num)
    {
        while (num)
        {
            *(pdest + num - 1) = *(psrc + num - 1);
            num--;
        }
    }
    else
    {
        while (num--)
            *pdest++ = *psrc++;
    }
    return destination;
}

61、給兩個文件,分別有100億個URL,我們只有1G內存,如何找到兩個文件交集?分別給出精確算法和近似算法

精確算法:Hash分桶,文件切割,處理對應文件,彙總結果。
近似算法:bloom filter 講一個文件映射,然後遍歷另一個文件,結果寫到文件。

day27

62、模擬實現C庫的atoi和itoa
atoi

int my_atoi(const char* str)
{
    if (NULL == str || *str == '\0')
        return 0X7FFFFFFF;
    bool flag = false; // 標記正負
    int ret = 0;
    if (*str == '-')
    {
        str++;
        flag = true;
    }
    else if (*str == '+')
    {
        str++;
    }

    while (*str != '\0')
    {
        if (*str <= '9' && *str >= '0')
        {
            ret = ret * 10 + *str - '0';
            str++;
        }
        else
        {
            ret = 0x7FFFFFFF;
            break;
        }
    }
    if (*str == '\0')
    {
        if (flag)
            return 0 - ret;
        else
            return ret;
    }

    return ret;
}

itoa

char* my_itoa(int num, char* str, int radix)
{
    assert(str);
    const char index[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWZX";
    unsigned temp;

    /* 只有十進制纔有正負*/
    int i = 0;
    if (radix == 10 && num < 0)
    {
        temp = (unsigned)-num;
        str[i++] = '-';
    }
    else
    {
        temp = (unsigned)num;
    }

    do
    {
        str[i++] = index[temp % (unsigned)radix];
        temp /= radix;
    } while (temp);
    str[i] = '\0';
    /* 翻轉*/
    int begin;
    int end = i - 1;
    if (str[0] == '-')
        begin = 1;
    else
        begin = 0;
    while (begin < end)
    {
        std::swap(str[begin], str[end]);
        begin++;
        end--;
    }
    return str;
}

63、給一個超過100G的log file, log中存着IP地址, 設計算法找到出現次數最多的100個IP地址?

轉爲100億個整型,然後分桶,哈希統計。
或者用最小堆。
發佈了127 篇原創文章 · 獲贊 199 · 訪問量 32萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章