華爲機試刷題


  1. 服務器廣播

題目:服務器連接方式包括直接相連,間接連接。 A 和 B 直接連接, B 和 c 直接連接,則 A 和 c 間接連接。直接連接和間接連接都可以發送廣播。
給出一個 N * N 數組,代表 N 個服務器, matrix[i][j] == 1 ,則代表 i 和 j 直接連接;不等於 1 時,代表 i 和 j 不直接連接。 matrix[i][i]== 1 ,即自己和自己直接連接。 matrix[i][j]==matrix[j][i] 。計算初始需要給幾臺服務器廣播,纔可以使侮個服務器都收到廣播。
輸入描述: n * n 矩陣,

[[1,1,0],[1,1,0],[0,0,1]]
1
輸出描述:整數

2

 int main () {
    
    //vector<vector<int>> v{{1,0,1,0,1,1},{0,1,0,1,0,0},{1,0,1,0,0,0},{0,1,0,1,0,0},{1,0,0,0,1,0},{1,0,0,0,0,1}};
    vector<vector<int>> v{{1,0,1,0,1,1},{0,1,0,0,0,0},{1,0,1,0,0,0},{0,0,0,1,0,0},{1,0,0,0,1,0},{1,0,0,0,0,1}};
    unordered_map<int,set<int>> a;
    auto len = v.size();
    for (int i = 0;i<len;i++) {
        for (int j=0;j<len;j++) {
            if (v[i][j] == 1) {
                a[i].insert(j);
            }
        }
    }
    for (auto iter = a.begin(); iter != a.end();iter++) {
        int num = iter->first;
        set<int> tmp =iter->second;
        for (int j : tmp) {
            if (num != j) {
                a[j].insert(tmp.begin(),tmp.end());
            }
        }
    }
    set<set<int>> ret;
    for (auto iter = a.begin(); iter != a.end();iter++) {
        set<int> tmp =iter->second;
        ret.insert(tmp);
    }
    cout << ret.size() << endl;

    return 0;
}

心路歷程:用什麼數據結構有時比算法重要,數據結構也能體現解決方法。
開始拿到題目,紙上畫了畫人工是怎麼思考的,第一感覺是想到做過的一道題,一個矩陣,某元素爲1的話會像炸彈那樣令跟它同一行和同一列的元素都變成1,新生成的1不會有這個效果。畫格子畫了會沒啥感覺,又想到這題從描述就天然是個圖問題,用dfs遞歸下?但是天然的對dfs比較排斥,估計寫不來,並沒有做過多思考。再畫兩下,突然就畫出了題解中的數據結構,map, 接下來就比較容易了。中間也考慮過用兩層vector,一想內層的大小不一致不能用, 題解中答案寫完後試了下內層的vector也是可變的,大小能任意變,這題數據結構改成vector<vector> 應該也可以,第一層的下標就相當於map的key, 但是直觀想內層的vector還得去重吧,改成vector<set> 估計會更好;還碰到對set中insert set 的遲疑,需要手動考慮去重嗎?答案是不需要,暴力insert就行了。兩個set怎麼合併 ? a.insert(b.begin(), b.end()) 即可。支持兩層set嗎?支持。怎麼判定最後要多少個廣播?不同的set個數。即相互連接的服務器的set最後裏面的元素是一樣的,爲了保證能一樣,所以用set沒用unordered_set,而爲了性能用了unordered_map。邊界條件處理,這塊得加強,沒多少想法。如果有多個服務器每個都是孤島,沒跟外界連,是否最終會合併成一個?所以針對這種,要將v[i][i] 自己也放進set容器裏

經驗:
1.遇到報錯一定要多檢查括號寫錯沒
ret.insert(tmp); 這句手誤打成了 ret.insert< tmp >; 沒檢查出來,報錯爲:
reference to non-static member function must be called

2.對於迭代器使用不熟練,int num = iter->first; 寫這塊時不確定是
int num = *iter->first;
或者 int num = *(iter)->first)
或者 int num = *(iter->first); 浪費了幾分鐘時間

  1. 首先是優先級要記牢:(優先級越高越先)
    第一優先級: :: 僅此一個作用域解析符
    第二優先級:()[] -> .
    第三優先級:* &

  2. 迭代器:vector中 *iter就能訪問到數據元素了,而map中不需要 * ,auto p = iter->first; 就能訪問到第一個元素了

vector<int> bb{1,2,3,4};
for (auto i = bb.begin();i!=bb.end();i++) {     
    cout << *i << " ";
}
輸出 : 1 2 3 4
unordered_map<int,set<int>> a;
for (auto iter = a.begin(); iter != a.end();iter++) {
        int num = iter->first;
        set<int> tmp =iter->second;
}

  1. 判斷一個string能否轉成int數

string str = "12388sf";
int ret = 0;
to_int(str, ret);

bool to_int(const string& str, int& ret) {
    int len = str.length();
    std::string::size_type sz;
    bool flag = false;
    try {
        ret = stoi(str,&sz);
        if (sz == len) {
            flag = true;
        }
    } catch (std::invalid_argument&) {
    } catch (std::out_of_range&) { // {}內可以是空語句的
    } catch (std::exception& e) {
        cout << e.what() << endl;
    } 
    //} catch (...) {
    //}
    return flag;
}
    

注意點:
stoi的原型爲:
int stoi (const string& str, size_t* idx = 0, int base = 10);
第二個參數爲能轉成數字的下一位的下標,是指針。第三個參數爲要將第一個參數中數字用什麼進制解釋。如果str的第一個字符不是字符0-9,則會出異常,報 invalid_argument , 如果範圍超過了int,則報 out_of_range 異常,如果這兩點都沒問題,而 sz 的大小和 length 不相等,則說明數字後面還有非數字字符。所以這個函數用起來還是比較麻煩的,得自己check。
另一個用法舉例:

std::string str_hex = "40c3";
int i_hex = std::stoi (str_hex,nullptr,16);
std::cout << str_hex << ": " << i_hex << '\n';

輸出:

40c3:  16579

  • 字符串分割

題目:實現字符串分割算法split(str,delim),傳入待分割字符串和分割字符,返回數組,如果delim參數不傳,則對字符串逐個分割。
比如調用split(“abcbebb”,‘b’),返回爲[“a”,“c”,“e”],調用split(‘abcd’),返回[‘a’,‘b’,‘c’,d’]

std::vector<std::string> split_string(const std::string& s, const char flag = ' ') {
    if (s.empty()) {
        return {};
    } 
    std::vector<std::string> str_vec;
    if (flag != '\0') { // 注意點一:用'\0'表示空字符,表示參數二沒傳入。這個一度阻塞了思路。同時記住'\0'很特殊,是ascii表的第一個,即是基準
        istringstream iss(s);
        std::string temp;
        while (getline(iss, temp, flag)) {
            if (!temp.empty()) {
                str_vec.push_back(temp);
            }
        }
    } else {
        for(char i : s) {
            string t;
            t.push_back(i); // 注意點二:將char轉成string的方法:往新申請的string裏push_back(char)即可。另外注意不要用to_string,這個傳入的參數是int float double等數字型的變量
            str_vec.push_back(t);
        }
    }
    return char_vec;
}

  • 單詞壓縮編碼

題目:給定一個單詞列表,將這個列表編碼成一個索引字符串 S 與一個索引列表 A。
例如,如果這個列表是 [“time”, “me”, “bell”],將其表示爲 S = “time#bell#” 和 indexes = [0, 2, 5]。
對於每一個索引,可以通過從字符串 S 中索引的位置開始讀取字符串,直到 “#” 結束,來恢復之前的單詞列表。
輸入描述:數組。
輸出描述:成功對給定單詞列表進行編碼的最小字符串長度

class Solution {
public:
    int minimumLengthEncoding(vector<string>& words) {
        sort(words.begin(), words.end(), [](const string& a, const string& b) -> bool {return a.length() > b.length();});
        string ret = words[0] + "#";
        for (int i = 1; i < words.size(); ++i) {
            auto found = ret.find(words[i] + "#");
            if (found == string::npos) {
                ret += words[i] + "#";
            }
        }
        return ret.length();
    }
};

思路很簡單,按長度依次處理是關鍵,另外需要注意題目要求必須以“#”結束,所以比如 time# 可以匹配 me,不能匹配 ti, 爲此將每個word加上“#”後才進行find。解法的時空效率不怎麼高


  • 打印任務排序

題目:某個打印機根據打印隊列執行打印任務。打印任務分爲九個優先級,分別採用數字1~9表示,數字越大優先級越高。打印機每次從隊列頭部取出第一個任務A,然後檢查隊列餘下任務中有沒有比A優先級更高的任務,如果有比A優先級高的任務,則將任務A放到隊列尾部,否則執行任務A的打印。請編寫一個程序,根據輸入的打印隊列,輸出實際打印順序

struct mydata{
    int n;
    int pos;
    mydata(int i, int j):n(i),pos(j) {}
};

bool comp(const mydata& a, const mydata& b) {
    return a.n < b.n;
}
int main () {
    int n = 0;
    while(cin>>n) {
        vector<int> p;
        deque<mydata> v;
        for(int i=0;i<n;i++) {
            int p = 0;
            cin >> p;
            mydata m(p,i);
            v.push_back(m);
        }
        while(!v.empty()) {
            auto m = *max_element(v.begin(), v.end(), comp);
            int nn = m.n;
            if (nn == v.front().n) {
                p.push_back(m.pos);
                v.pop_front();
            } else {
                auto t = v.front();
                v.push_back(t);
                v.pop_front();
            }
        }
        for(auto i : p) {
            cout << i << " ";
        }
        cout << endl;
    }
    return 0;
}

輸入

6
1 1 9 1 1 1

輸出

2 3 4 5 0 1
  1. 變量命名比較隨意,機試先追求把題做出來,再追求命名、格式這些東西
  2. deque 可以pop_front,vector沒有
  3. max_element 可以用於自定義對象,要自定義比較函數。同時有min_element,這兩個的自定義比較函數中,對於數值型的統一用 return x < y; 如果需要取最小值,則用 min_element,如果要用最大值則用max_element。
  4. 同樣的需要保留住原始輸入的位置信息,所以創了個新數據類型
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章