近期筆試小結(附數據庫工程師面試准備)


《網易 0912》

也不知道在網易招聘官網怎么投了個數據庫管理工程師的,不過還是認真的做了其筆試題。

選擇題不說了,兩道編程題難度一般,需細心!

1.買蘋果

下面都是比較直接的做法,其實可以考慮使用背包模型去做。

#include<iostream>
#include<vector>
using namespace std;
int main()
{
    int n;
    while(cin>>n){
        int result=0;
        if(n<6)
        {
            cout<<-1<<endl;
            return 0;
        }
    
        while(n>0)
        {
            if(n%8==0)
            {
                result+=n/8;  
                break;
            }
            else
            {
                n-=6;
                if(n>=0)
                result++;
            }
        }
        if(n>=0)
            cout<<result<<endl;
        else
            cout<<-1<<endl;
    }
   
    return 0;
}

或(由於范圍比較小可以直接枚舉6個和8個的袋數維護最小值即可。時間復雜度:O(n^2/48) ):

#include <iostream>
#include <cstdio>

using namespace std;

int main(){
    int n, res = 10000;
    while(cin >> n){
        for(int i = 0; i <= 20; i++){
            for(int j = 0; j <= 20; j++){
                if(i * 6 + j * 8 == n){
                    res = min(res,(i + j));
                }
            }
        }
        if(res == 10000) res = -1;
        cout << res << endl;
    }
    
    return 0;
}

  

2.最大奇約數

題意:

定義函數f(x)為x的最大奇數約數,x為正整數,例如f(44) = 11.現在給出一個N,需要求出f(1) + f(2) + f(3) + ... + f(N)

例如: N = 7,則f(1) + f(2) + f(3) + f(4) + f(5) + f(6) + f(7) = 1 + 1 + 3 + 1 + 5 + 7 = 21.

分析:易知奇數的最大奇約數是其自身, 而偶數的最大奇約數是是除去所有偶因子后的那個奇數。最直觀的想法就是挨個遍歷求各個數的最大奇約數的和。

然而很明顯,時間復雜度滿足不了要求

#include <iostream>
using namespace std;

int main() {
    long long N;
    while(cin >> N){
        long long res = 0;
        for (long long i = 1; i <= N; ++i) {
            int temp = i;
            while (temp % 2 == 0) {
                temp /= 2;
            }
            res += temp;
        }
        cout << res << endl;
    }
    
    return 0;
}

需要找規律進行改進,記F(N)=f(1) + f(2) + f(3) + ... + f(N),觀察會發現,

如果N為奇數,則f(2)+f(4)+...+f(N-1)=f(1) + f(2) + f(3)+...+f((N-1)/2)

那么F(N) = 1 + 3 + 5 + ....+N + f(2) + f(4)+...+f(N-1)=F((N-1)/2)+(N+1)*(N+1)/4

如果N為偶數,則F(N)=1+3+5+...+(N-1)+f(2)+...+f(N)=F(N/2)+N*N/4

#include<iostream>
using namespace std;

long long sum(long long n) {
    if (n == 1) {
        return 1;
    }
    if (n % 2 == 0) {
        return  sum(n / 2) + n * n / 4;
    }
    else {
        return sum((n-1)/2) + (n+1)*(n+1)/4;
    }
}
int main() {
    int N;
    while(cin >> N){
        cout << sum(N) << endl;
    }
}

 也可以將奇偶統一可以寫出更簡潔的代碼,如下:

#include <iostream>
using namespace std;

long long n;
long long sum(long long n){
    if(n == 0) return 0;
    return (long long)( (n + 1) / 2) * ( (n + 1) / 2) + sum(n / 2);
}
int main(){
    while(cin >> n){
        cout << sum(n) << endl;
    }
    return 0;
}

這樣每次計算問題規模減半,時間復雜度為:O(log(n))  

 

3.聚簇索引的特點 

4.設計一個簡單的論壇系統的數據庫

這個其實網上有很多比較好的參考資料了:

論壇數據庫的設計(寫得很好)

總結幾種常用的論壇設計方法:

  • 分割思想:

(1)數據庫切分:用戶庫、主題庫、回復庫

(2)數據表水平切分:用戶庫1-n、主題庫1-n、回復庫1-n (比如按時間分)
(3)分布式數據庫:每台計算機中都有DBMS的一份完整拷貝副本,並具有自己局部的數據庫,位於不同地點的許多計算機通過網絡互相連接,共同組成一個完整的、全局的大型數據庫。

(4)論壇功能可以進行分隔,不同的服務器負責不同的功能

(5)用主從數據庫,master是寫, slave是讀

(6)把內容與其它信息分開,好處就是可以讓每個表的文件最小化,對數據庫操作壓力會減小,這樣保證每張表數據量很小,操作速度會快,也可以在這里使用緩存

  • 索引:

針對是否建立索引有着一定的分歧:

個人覺得建立索引還是很有必要的。理由如下:

(1)建立索引可以加快檢索速度,對於論壇讀和寫的比例相差很大,用戶體驗當然是讀多寫少,所以綜合考慮還是要用索引,而且是加在常用的讀關鍵字上。

(2)索引之所以會降低更新的速度,是因為更新還包括對索引的更新,從更新帖子10萬左右,這句話是說,我們可能對發帖標題,發帖內容,回復標題,回復內容這4個字段做更新。需要注意的是,這四個字段並不是用來建立表連接的字段,為了優化查詢速度我們不會在這四個字段上建立索引,所以從這道題目出發,我們建立的索引不會影響更新帖子的性能。只要被索引的列(例如回復表的標題ID)不被頻繁更新,即使索引所在地行的其它列被頻繁update,索引也不會被更新從而產生性能消耗,一張表一天30萬次的索引更新,因它引起的性能消耗小到即使數據庫安裝在奔騰3單核CPU下都能輕松承擔下來。

(3)對於更新的速度慢的問題,我們有解決的方法,你提交更新了后,前台可以讓程序返回一個正確結果,后台開個線程異步慢慢跟新數據庫就是了,反正更新成功的前提就是假設數據庫連接永遠正確並處於可靠狀態。在數據庫和用戶之間建立一個緩沖區。(如,將更新的數據放到內存中,達到一定數量的時候再統一更新數據庫。假如以100條為例,一旦內存中達到100條數據量將這100條數據統一入庫。減少insert操作)

  • 緩沖: 

讀的時候的緩沖:緩存路由表

           主題緩存表(這個取每個區的前面100條記錄),一般來說負載最大的就是主題的第一頁,所以緩存表是個小表。 

    另外使用hibernate,在數據庫上面加了一層緩存。

           生成靜態頁,緩存最熱,最新的帖子。

          對於經常更新的數據都設計成單獨表 ,這樣可以最大程度的利用hibernate緩存

          緩存常用的數據和表,利用緩存來將經常被訪問的帖子留在內存中,為每條緩存的記錄添加一個訪問時間,如果長時間沒被訪問就從緩存中刪除掉,

          避免內存過大,每次用戶看帖的時候,首先檢索緩存中時候有需要的帖子,沒有的話再訪問數據庫,然后將數據庫返回的帖子信息存儲到緩存中。
寫的時候的緩沖:數據庫和用戶之間建立緩存,將更新的數據放在內存中,異步操作的。所有的寫貼操作 放到一個隊列然后批量執行插入數據庫操作。 

預估計的緩沖:假如用戶第一次打開某標題,那將此標題的相關的前100條數據緩存到客戶斷。這樣避開對數據庫的直接查詢,減少數據庫壓力。

  • 代碼優化 

盡量避免表的連接約束通過代碼來實現約束 例如用戶id的驗證在用戶登錄時驗證這樣就可以把帖子表的用戶id外鍵去掉這樣就成了單表操作、查詢 而連接可以通過觸發來實現這樣最多是查詢了3個表而不是連接中的笛卡爾笛卡爾積  回復表的查詢限定每次查詢的記錄數例如限定10條其它的通過點擊觸發來操作"注代碼優化容易出現bug 原因有些開發工具本身有優化" 

  • 數據庫性能調優 

盡量用硬件來代替軟件優化 原則就是能用硬件的盡量用硬件 比如磁盤陣列 RAID0 有條件用RAID10 加大內存 .避免小表上建索引 對論壇來說數據帖子和回復不是很重要 可以定期刪除一些垃圾帖子 樓主說的幾百萬條記錄的論壇對現在的數據庫管理系統和計算機來說永不着刻意的優化,定期維護打包備份數據庫就可以了

提高速度的關鍵: 
(1)建立合理的索引並在查詢時充分利用; 
(2)避免使用關聯,這樣避免整表掃描;使用關聯不如多次使用主鍵查詢來的快; 
(3)一些處理的功能盡可能放到內存中來做,比如組織主題和回復; 
(4)海量緩存(使用靜態頁面也是個不錯的做法)

(5)定期對表進行轉儲

 

數據庫面試相關准備題

4.存儲過程和函數的區別是什么?

5.什么是數據庫事務?

6.游標的作用是什么,如何知道游標已經到了最后?

7.觸發器

8.什么叫SQL注入式攻擊,如何防范?

9.解釋聚簇索引和非聚簇索引之間的區別?

10.SELECT INTO 和 INSERT INTO SELECT 兩種表復制語句

11.SQL 中GO的作用

每個被GO分隔的語句都是一個單獨的事務,一個語句執行失敗不會影響其它語句執行。

12.SQL中的case when then else end用法

13.一點實例明白mysql數據庫存儲過程 

14.數據庫中最常用的是哪兩種並發控制協議?

數據庫並發控制 你選樂觀鎖還是悲觀鎖?

15.

----------------------------------- 

《新美大0911》

1.收紅包問題

題意:在一個桌子上放了若干個數值不等的紅包,圍成一個圈,現在讓你選取若干個紅包,要求相鄰的兩個紅包不能同時選取,編程求出選取紅包所得錢數的最大值;

輸入:

2

1,2,3

1,2,3,4

輸出:

3

6

分析:

按順序依次選取紅包,則每個位置都有兩種狀態,選取和不選取,最終要使紅包錢數最大化,可以用動態規划的思想來解決。在動態規划中,每一步的選擇都是前面所有步驟的總結,每一步選擇最優從而使最終方案最優。DP算法的這種本質決定了問題必須是線性結構,不過本問題是環狀結構,第一個紅包(指邏輯上的第一個,其實環上的任何一個紅包都可以作為第一個)的狀態不僅會影響第二個紅包的狀態,還會影響最后一個紅包的狀態,那么要先將環狀結構轉化為線性結構,具體的實現方式是:

(1)選取第一個紅包的所有方案:選取第一個紅包,從而第二個紅包和最后一個紅包都不能選取,剩余紅包的狀態不確定;

(2)不選取第一個紅包的所有方案:不選取第一個紅包,那么剩下的紅包狀態都不確定。

若桌上某個位置的紅包被選取,那么其后一個位置的紅包就不能選取;若某一位置的紅包未選取,其后一個位置的紅包就可以選取,也可以不被選取。DP轉移方程:

 -> dp[i+1][0]=max(dp[i][0],dp[i][1]);

  -> dp[i+1][1]=dp[i][0] ;

其中i表示紅包的索引,第二維中的0表示不選取第i個紅包,1表示選取第i個紅包,即第二維用來表示紅包的兩種狀態;

而dp[i][0]表示從紅包0到紅包i中選取紅包,並且不選取紅包i,得到的紅包錢數最大值;dp[i][1]表示從紅包0到紅包i中選取紅包,並且選取紅包i,所得到的紅包錢數最大值。---算法時間復雜度為O(n)

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

vector<int> Hbao;

int Choose(int begin, int end)
{
    if (end < begin) return 0;
    int dp[2], temp;
    dp[0] = dp[1] = 0;
    for (int i = begin; i <= end; i++)
    {
        temp = dp[0];
        dp[0] = max(dp[0], dp[1]);
        dp[1] = temp + Hbao[i];
    }
    return max(dp[0], dp[1]);
}

int main()
{
    int t, n, val, res;
    cin >> t;
    while (t--)
    {
        cin >> val;
        Hbao.push_back(val);
        while (cin.get() == ','){
            cin >> val;
            Hbao.push_back(val);
        }
        n = Hbao.size();
        //1.選取紅包0,然后對紅包2到紅包n-2進行DP
        res = Hbao[0] + Choose(2, n - 2);
        //2.不選取紅包0,然后對紅包1到紅包n-1進行DP
        res = max(res, Choose(1, n - 1));
        cout << res << endl;
        Hbao.clear();
    }
    
    return 0;
}

 

可參考:http://blog.csdn.net/lrgdongnan/article/details/52506373 

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM