Bo2SS

Bo2SS

0 從C到C++

C++ 比 C 難學,究竟難在哪裡?
感性認識一下 C++ 的美?

課程內容#

問:為什麼以C++ 11標準作為學習重點?

答:C++ 03 到 11 變化很大,而 11 到 14、17 變化很小,20 標準很新,還不普遍,所以從 C++ 11 標準入手是非常好的選擇。

C++ 的語言特性#

主要特性#

  1. 頭文件:130 個(C 語言:29 個)
  2. STL
  3. 類和對象
  4. 模板
  5. Lambda(C++ 11)
  6. 異常處理(C++ 11)

解讀#

  1. 頭文件非常多,不能以啃頭文件作為學習方式
  2. STL
  • 本來不屬於標準頭文件,惠普實驗室的大佬們開發的,C++ 後來將其納入
  1. 面向對象(類和對象)
  2. 泛型編程(模板):開發一個函數,相當於開發一百個函數
  3. 函數式編程(Lambda)
  • 相比 C 語言的面向過程編程範式,C++ 增加了 3 種編程範式,工程項目中,一般同時使用 1~2 種編程範式,面向過程和面向對象較常用
  • 新編程範式的本質作用:提高開發效率
    • 不同的編程範式對應不同的開發效率,開發效率考慮寫代碼 + 測試 + 維護的時間
    • 越往後,開發效率可能更高,但寫代碼難度也更高
    • 使用新編程範式的主要情景:可以提高開發效率
  • PS:C 也可以實現面向對象範式,參考微軟的COM in plain C
  1. 異常機制
  • 針對某些特定的運行時錯誤,挽回程序崩潰退出的一種機制
  • 設計哲學:程序的邏輯錯誤優先表現為編譯錯誤,再表現為運行時錯誤

PS:

  • 在學習了 C++ 後,假設還需要學習 java,可以按照編程範式分門別類地去學習
  • C++ 繼承了 C 語言絕大多數的特性(語法規則)
    • C++ 刪除了 C 中的一些特性,但不是關鍵

輸入輸出的程序對比 [C、C++]#

  • 圖片
  • 圖片
  • cin、cout
    • 不需要指定類型(格式佔位符)
    • 左移、右移運算符 [<<、>>]:涉及運算符重載
  • printf
    • "% lf":默認保留 6 位小數
    • "% g":輸出全部有效數字位數,cout 使用的規則
  • scanf、printf 是函數;而 cin、cout 不是函數,是對象
  • [PS] endl:換行 + 清空緩衝區

STL:內置數據結構#

queue、stack、string、unordered_map、map... 相比自己用 C 語言實現,這裡可以很方便地支持任意類型的元素,詳見《面試筆試算法上》——5 STL “容器” 的使用與練習

deque#

string#

  • C 的 strcmp 與 C++ 的 ==
  • C 的 strcat 與 C++ 的 +=
  • C 的 strlen 與 C++ 的 length ()
    • 實現原理不太一樣
    • strlen:每次都需從頭到尾掃一遍字符數組,直到遇到 '\0' --> O (N)
    • .length ():成員屬性 --> O (1)
  • substr (pos, n):從 pos 處向後取 n 位

map 相關#

  1. 基於哈希表 [無序]
  • 存儲、查找的均攤時間複雜度:O (1)
  • [C++ 11] unordered_map
  • [C++ 11 之前,非標準] hash_map
  1. 基於紅黑樹 [有序]
  • map

PS:

  • 外在表現類似數組,但功能更強大
  • find (key):如果找不到,返回 end () [哈希表的結束位置]

代碼演示#

strlen 與 length () 的比較#

map 和 unordered_map 的比較#

  • 圖片
  • 圖片
  • 迭代器的使用先感受下,類型定義明確標明,後面可用 auto 關鍵字代替❗
  • 有序性體現在遍歷元素時
    • unordered_map 對於 key 和 value 都無序,而且還會打亂原始順序
    • map 可當成排序工具
      • 圖片
      • auto 關鍵字的使用
        • 對比前面完整的迭代器類型定義,先感受下
        • 很強大,有點像 cin 和 scanf 的區別,可以自動判斷 iter 的類型
      • 排序結果
        • 圖片
        • 雖然看著有點像計數排序,但不是
        • 時間複雜度涉及底層紅黑樹:O (NlogN)

sort#

隨堂練習#

HZOJ-245:貨倉選址#

圖片

範例輸入

5
1 3 5 6 10

範例輸出

12
  • 思路
    • 注意:輸入可能是亂序
    • 問題轉換
      • 隨機選擇一個點 p,當前距離總和為 s1
      • 當點 p 移動很小的距離 Δ 時,此時距離總和 s2 = s1 + (n1 - n2) * Δ
        • n1:點 p 左邊的商店數;n2:點 p 右邊的商店數
      • 【目標】s2 < s1
        • 當 n1 - n2 <0 時,Δ> 0 --> s2 < s1:即右邊商店多時,往右移動點 p,可以有更優解
        • 當 n1 - n2 > 0 時,Δ <0 --> s2 < s1:即左邊商店多時,往左移動點 p,可以有更優解
      • 【最終目標】n1 = n2 即可,所以找第 n / 2 位元素坐標
        • n 為商店總個數,位數從 0 開始
  • 代碼
    • 解法 1:sort 方法
    • 解法 2:nth_element 方法
      • 圖片
      • nth_element
        • 基於快速選擇算法—— 知乎,借鑒快速排序的 Partition 過程,但不會對整個序列排序
        • 執行完後,【第 k 位放置著從小到大排名第 k 位的元素】❗
      • 小作業:nth_element 使用方法和技巧,見鏈接
    • 方法比較
      • 解法 1:完全排序
      • 解法 2:不完全排序,直接得到第 k 位

HZOJ-166:字符串操作 1#

圖片

範例輸入

AAAAAA
2
xxx

範例輸出

6
AxxxAAAAA
6

HZOJ-216:獲取姓名並排序#

image-20210707181842636

範例輸入

5
Mr.DMY
Mr.ZY
Mr.LYH
Ms.Grace
Mr.Bill

範例輸出

Bill
DMY
Grace
LYH
ZY
  • 思路
    • 可以使用 sort
    • 還可以使用 map 結構,底層是紅黑樹,默認按 key 排序
  • 代碼
    • image-20210707183303642
    • 使用 map
    • 了解迭代器 iter 的使用,->first 代表 key,->second 代表 value
    • 注意
      • 需要先將名字進行截取
      • value 值用來計數,名字可能有重複

HZOJ-287:合併果子#

image-20210707182026288

範例輸入

3
1 2 9

範例輸出

15
  • 思路
    • 一開始越小越好,所以可以使用最小堆
    • 小作業:討論合併果子和哈夫曼編碼之間的關係,見鏈接
  • 代碼
    • image-20210707182403298
    • priority_queue
      • 它是一個模板,<> 裡放的都是類型,默認為最大堆
      • 若要改為最小堆,需要先定義容器類型:vector;然後,
        • 方式一:定義 CMP 類,重載 () 運算符,使其可以被當成可調用對象(即仿函數、函數對象)
        • 方式二、方式三:使用模板類,後續再學

HZOJ-256:國王遊戲#

image-20210707182416911

範例輸入

3
1 1
2 3
7 4
4 6

範例輸出

2
  • 思路
    • 目標:求最小的最大值
    • 算法是想:微擾
        • 所有人左手上的數字序列為 $a_0,a_1,a_2,...,a_i,...,a_n$,
        • 所有人右手上的數字序列為 $b_0,b_1,b_2,...,b_i,...,b_n$,
        • 排在該大臣前面的所有人的左手上的數的乘積為 $A_{i-1}=a_0*...*a_{i-1}$,
        • 每位大臣獲得的金幣數為 $G_i=A_{i-1}/b_i$
      • 假設有一個排列 1
        • $G_1,G_2,...,[G_{i-1},G_i],...,G_n$,其中 $G_i$ 最大
      • 此時若調換相鄰的兩位大臣的位置,即 $(a_{i-1},b_{i-1})$ 和 $(a_i,b_i)$,會產生新的排列 2
        • $G_1,G_2,...,[G_{i}^{'},G_{i-1}^{'}],...,G_n$
        • ❗ 排列 1 和排列 2 哪個更接近目標呢?只需要關注發生改變的金幣數 $G_{i}^{'},G_{i-1}^{'}$ 和最大的金幣數 $G_i$ 的關係。如果均為小於關係,則排列 2 更接近目標
      • 分析
        • 只有被調換的兩位大臣的金幣數有變化
          • 已知:$G_i=A_{i-1}/b_i$
          • 調換後:$G_{i}^{'}=A_{i-2}/b_i$,$G_{i-1}^{'}=A_{i-2}*a_i/b_{i-1}$
        • 易知 $G_{i}^{'}<G_{i}$,
        • 而如果 $G_{i-1}^{'}<G_i$ 也成立,則排列 2 更接近目標,即
          • $G_{i-1}^{'}<G_i$
          • $A_{i-2}*a_i/b_{i-1}<A_{i-1}/b_{i}$
          • $A_{i-2}*a_i/b_{i-1}<A_{i-2}*a_{i-1}/b_{i}$
          • $a_i*b_{i}<a_{i-1}*b_{i-1}$
      • 👉大臣交換位置的條件:
        • $a_i*b_{i}<a_{i-1}*b_{i-1}$
  • 代碼
    • image-20210707182504308
    • image-20210707182511242
    • image-20210707182516988
    • 算法:基於微擾的思想,設計出排序規則即可
      • 使用 pair 對類型方便組織左、右手上的數字
      • 國王一直在最前,不參與排序
    • ⭐數據結構:大整數
      • 單獨封裝一個處理進位的方法,乘法和除法過程結尾都使用一次
      • ❓ 構造函數裡可以直接使用 push_back (),後續學習關注
      • 除法的設計,主要利用餘數,可能產生前導 0,要處理
      • ❓ 重載小於號需要在方法末尾添加const,否則不生效,可能會優先使用 vector 的小於比較,具體原因後續學習
    • PS:這裡真正將數據結構和算法分開了,這也是面向對象相比面向過程的優勢

Tips#

  • 不僅要知其然,還要知其所以然,更要知其四五六個所以然
  • C++ 的學習沒有標準答案,多互相討論
  • 學會查看cppreference
  • 推薦
    • 書籍:C++ Primer、Effective C++、深度探索 C++ 對象模型...
    • 視頻:侯捷老師
載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。