文章和資源同步更新至微信公衆號:算法工程師之路
不知道各位寫C++代碼的童鞋們,有沒有發現一個現象,自己寫的CPP代碼怎麼那麼像C代碼呢?筆者也深有感觸,但是自從C++11標準出現以後,CPP的代碼就開始精簡很多了,風格也極大的發生了變化,今天筆者就開始整理一些C++的新特性,並展示如何在實際應用中使用!讓你的代碼更Cpp些!
1. nullptr
nullptr是爲了補充並替代NULL的,由於之前老版本的NULL定義一般爲0,但有時候又被編譯器定義爲((void*)0)。這樣就會出現混亂,特別是進行函數重載的時候,就會讓編譯器搞不清楚NULL的具體類型,因此,引入nullptr可以更好的區分0和空指針,因此,在新版中,儘量使用nullptr代表空指針進行初始化。
2.初始化列表
使用初始化列表的方式可以極大的簡化構造函數的代碼量,使得程序更加簡潔。
struct TrieNode
{
int path, end;
vector<TrieNode*> children_;
TrieNode() : path(0), end(0), children_(26, nullptr){}
};
TrieNode();
3.auto、decltype類型
在C++中最煩的就算是各種類型聲明的編寫,太多字母了,而且有時候也會忘記,由於他們的類型定義太多太亂了!因此C++11中使用auto對數據類型進行自動推倒。新版中,已經棄用了之前有類似功能的register關鍵字,變得更加強大,比如下面例子:
for(vector<int>::const_iterator itr = vec.cbegin(); itr != vec.cend(); ++itr)
// 可以改寫爲
for(auto itr = vec.cbegin(); itr != vec.cend(); ++itr);
是不是可以方便很多了,但是auto類型有個缺陷的地方,如果我們想要根據某個數或者表達式的類型去定義一個變量,而不進行初始化,那我們使用auto就不行了,所以C++引入了另外一個關鍵字decltype。
int a = 0;
decltype(a) b; // b的類型爲int
decltype((a)) b = 10; // 多加一層括號,表示一個表達式,此時b類型爲int&, 必須賦初值
decltype(f()) b = 2; // b的類型爲函數返回值類型,注意函數不運行,編譯器只是經過推理得到其返回值類型
4.範圍for語句
相信學過python的同學都很清楚,在python中經常使用的for語句是for…in…,十分的方便,而在C中for循環是又醜又長,C++標準爲了簡化代碼量,提供了新的範圍for語句:for(auto c : str);
// C風格
for(std::vector<int>::iterator i = arr.begin(); i != arr.end(); ++i) {
std::cout << *i << std::endl;
}
// C++11
for(auto &i : arr){
cout << i << " "; // 加上引用可以爲左值,用於修改
}
是不是瞬間覺得C++好多了,沒有指針了,也沒有很長的類型聲明瞭,這纔是擼代碼的感覺啊!!!
5.智能指針(shared_ptr和make_shared)
我在刷題的時候,由於是參考了JAVA版的,在JAVA中可以靠JVM的垃圾回收機制,特別是考慮到大數據問題,在棧區建立一個鏈表或者樹結構可能會導致空間不夠,因此一般會在堆區進行建立,但是釋放問題是真的很繁瑣!即使new和delete已經比C中的分配內存方便多了,但還是繁瑣,因此我們可以使用智能指針來讓程序自動維護開闢的空間!以防止由於我們不當操作出現內存泄露和野指針的問題!
在C++11中,智能指針包含在< memory >中,分爲shared_ptr、unique_ptr、weak_ptr,其中shared_ptr允許多個指針指向同一個對象,而unique_ptr爲獨佔式的佔有一個對象。最後的weak_ptr爲一個弱引用,指向shared_ptr所管理的對象!
shared_ptr採用引用計數的方式管理所指向的對象。當有一個新的shared_ptr指向同一個對象時(複製shared_ptr等),引用計數加1。當shared_ptr離開作用域時,引用計數減1。當引用計數爲0時,釋放所管理的內存。
由於shared_ptr是一個類模板,因此不可以直接使用指針對其進行賦值!但**一般不建議使用new方法對智能指針初始化,這樣會造成閱讀代碼的困惑!建議使用make_shared函數進行初始化!**當然爲了代碼簡潔,我們可以使用auto類型進行類型推倒!
shared_ptr<int>p = new int(5); //錯誤
shared_ptr<int>p(new int(5));
shared_ptr<int>p = make_shared<int>(5); //建議
auto p = make_shared<int>(5);
題目:前綴樹
實現一個 Trie (前綴樹),包含 insert, search, 和 startsWith 這三個操作。
示例:
Trie trie = new Trie();
trie.insert(“apple”);
trie.search(“apple”); // 返回 true
trie.search(“app”); // 返回 false
trie.startsWith(“app”); // 返回 true
trie.insert(“app”);
trie.search(“app”); // 返回 true
這次的題目是簡單的實現一個前綴樹的功能,筆者實現了兩個版本的(簡單和複雜),參考了LeetCode中大佬的答案,將代碼優化的更加的CPP,簡單版的題目如上面所示,僅僅實現插入和查找兩個功能!而複雜版可以記錄前綴爲str的字符串的個數,並且支持插入和刪除字符串的操作!主要目的是瞭解如何更加CPP的寫代碼,不再C風格!
具體的前綴樹的操作原理自行百度,很簡單的,就是如何定義每個節點,怎麼進行查找判斷!
class TrieTree{
public:
TrieTree() : root_(make_shared<TrieNode>()){}
void insert(string word_){
auto res = root_;
for(auto c : word_){ // 範圍for語句,auto類型推導
if (res->children_[c-'a'] == nullptr){
res->children_[c-'a'] = make_shared<TrieNode>(); // 智能指針分配內存
}
res = res->children_[c-'a'];
}
res->isWord_ = true;
}
bool search(string word){
shared_ptr<TrieNode> res = find(word);
return res != nullptr && res->isWord_ == true;
}
bool startsWith(string prefix){
return find(prefix) != nullptr;
}
private:
struct TrieNode
{
bool isWord_;
vector<shared_ptr<TrieNode>> children_; // 孩子節點的集合
TrieNode() : isWord_(false), children_(26, nullptr){} // 初始化列表
};
shared_ptr<TrieNode> find(string& prefix){
auto res = root_;
for(int i = 0;i < prefix.size() && res != nullptr; ++i){
res = res->children_[prefix[i] - 'a'];
}
return res;
}
shared_ptr<TrieNode> root_; // 智能指針
};
資源分享
以上完整代碼文件(C++版),文件名爲:前綴樹(簡單OR複雜),請關注我的個人公衆號 (算法工程師之路),回覆"左神算法基礎CPP"即可獲得,並實時更新!希望大家多多支持哦~
公衆號簡介:分享算法工程師必備技能,談談那些有深度有意思的算法,主要範圍:C++數據結構與算法/深度學習(CV),立志成爲Offer收割機!堅持分享算法題目和解題思路(Day By Day)