1. 程式人生 > >1.1 《演算法》第一章之導論和基本程式設計模型

1.1 《演算法》第一章之導論和基本程式設計模型

文章目錄

《演算法》全書組織結構

  1. 基礎:介紹實現,分析和比較演算法的基本原則和方法,包括下面概述的內容
  2. 排序:陣列排序,優先佇列,選舉及歸併等
  3. 查詢:龐大的資料集查詢指定條目的演算法
  4. 圖:圖的主要內容是物件和它們的連線(有權重和方向),解決最短路徑等問題
  5. 字串:研究對字串操作的演算法
  6. 背景:討論其他領域前沿,如運籌學

概述

  • 演算法:解決問題的方法

  • 資料結構:便於演算法操作的組織資料的方法

  • 本章介紹學習前的準備:

  1. Java基礎程式設計模型(用到的Java庫,語法等)
  2. 介紹資料抽象並定義抽象資料型別以進行模組化程式設計,介紹實現抽象資料型別的過程,即定義其API,通過Java的類機制來使用
  3. 學習三種基礎的抽象資料型別:揹包,佇列,棧,然後用陣列等實現它們的API,作為演算法實現的樣板和起點
  4. 分析演算法效能方法:基本做法:先對效能提出假設,建模,實驗驗證,必要時重複上述過程分析演算法效能方法
  5. 最後一個連通性問題,活學活用,最終實現union-find抽象資料結構

演算法

  • 編寫程式:計算機語言實現一個已有方法(載體)
  • 是演算法,而不是程式語言描述解決問題的方法,該方法要有限,確定,有效
  • 以求兩非負整數的公約數的演算法為例(書)
  • 便於演算法實現----》資料結構組織資料
  • 編寫複雜問題,要理解,定義和控制問題的複雜度及分解這些問題,然後選擇合適演算法
  • 選擇演算法-》分析演算法效能(時間空間複雜度)

基礎程式設計模型

  • 使用特定程式語言使得分離演算法的思想和實現細節變得困難,所以只用大部分程式語言共有的語法
  • 實現演算法所用到的語言特性,軟體庫和作業系統特性稱為基礎程式設計模型,可作為文件來查詢
  • P9顯示本書程式碼風格,力求與各種Java程式設計慣例和語言構造相一致

Java程式基本結構

  • 原始資料型別:精確地定義整數,浮點數等(不同取值範圍),組合為類似數學公式定義的表示式
  • 語句:宣告,賦值,條件,迴圈,呼叫和返回
  • 陣列:相同資料型別
  • 靜態方法:封裝和重用程式碼,使coder能用獨立模組程式設計
  • 字串:一連串字元
  • 標準輸入/輸出
  • 資料抽象:定義非原始資料型別,資料抽象和封裝(自定義類)

原始資料型別和表示式

  • 資料型別:一組資料及對其所能進行的操作的集合
  • Java控制變數(通過識別符號來引用),而變數有型別和值
  • 初級運算(加減乘除)的關鍵是參與運算的資料型別和產生的資料型別應該一致

表示式

  • Java運算子優先順序:見書
  • 浮點-》整型,會截斷小數部分,而非四捨五入
  • int型用一個字長為32位的機器字即可表示(有的電腦有字長為64位的機器字,但int型還是32位)
  • Java作為強型別語言,會檢查型別的一致性
  • 用模板來描述語句結構(如判斷語句)

程式碼的簡便寫法

  • 宣告與初始化一起
  • 隱式賦值:i++;i +=2;
  • 單語句省略花括號

靜態方法

  • 靜態方法又稱為函式
  • 典型靜態方法:判斷素數,計算平方根等(見書)
  • 方法的返回值替代表達式中的方法呼叫
  • 返回語句結束函式並將控制權交給呼叫者

方法性質

  1. 方法引數按值傳遞:方法處理的是引數的值,而不是引數本身,該方式使得改變引數變數的值對呼叫者無影響(不要試圖修改引數變數來改變引數)。值傳遞意味陣列引數將會是原陣列的別名(指向同一個記憶體空間):方法中使用陣列引數變數能引用呼叫者的陣列並改變其內容(不能改變原陣列變數本身)
  2. 方法名可過載
  3. 方法只能返回一個值,但能包含多個返回語句
  4. void的靜態方法會產生副作用(輸入輸出等)

遞迴(❤)

  • 遞迴總有一個簡單情況,方法的第一條語句總是包含return的條件語句
  • 遞迴呼叫自己總是去嘗試解決子問題,hence,遞迴收斂到最簡單的情況
  • 遞迴呼叫的父問題與子問題不該有交集(如二分法的子問題操作的陣列部分不同)

靜態方法庫

  • 定義Java類中的一組靜態方法
  • 模組化即面向物件程式設計
  • Java開發模式是編寫一個靜態方法庫(包含main方法)來完成某任務,實現模組(靜態方法庫)化程式設計
  • main方法的引數是輸入的字串組成的陣列
  • main方法可編成測試用例,當用例複雜時,可獨立成一模組

外部庫

  1. 系統標準庫(預設匯入):java.lang.*
  2. 匯入的系統庫
  3. 第三方庫
  4. 本地庫

  • 第三方及自己模組化方法編寫的方法庫擴充套件了程式設計模型
  • 用例指的是呼叫其他庫中方法的程式
  • 實現指的是實現某API的Java程式碼
  • Math庫方法例項(見書P17)
  • 本書編寫的庫(見P18-19)

自己編寫庫

  1. 編寫用例,在實現演算法中將計算過程分解為可控的部分
  2. 明確庫與其對應的API(API將實現與呼叫分離)
  3. 實現API及能獨立測試的main函式

tips

  • 建立陣列時指明陣列大小是因為讓編譯器知道為陣列預留的空間
  • 陣列的方法,包括reverse和矩陣乘法(見書)

字串

  1. 一連串字元(char型值)組成
  2. 字串拼接
  3. 字串型別轉換(同基本資料型別)
  4. 自動轉換:基本資料型別+字串=字串
  5. 命令列引數:main方法的引數是命令列輸入的字串組成的陣列
  6. 以後學習string類在Java中的表示方法

輸入輸出

  • 本書提供一外部庫用以建立Java程式與外界交流的簡易模型
  • 經典模型中,Java程式可從命令列引數或從一個抽象字元流(即標準輸入流)中獲得輸入,並將輸出寫入一個名為標準輸出流的字元流中
  • 我們需要考慮Java程式和作業系統之間的介面-》簡要地討論OS和IDE所提供的相應機制:
    • 預設情況下,命令列引數,標準輸入輸出與應用程式繫結,而應用程式是由能接受命令輸入的作業系統或開發環境所支援 。我們籠統地用終端來指這個應用程式提供的供輸入和顯示的視窗

命令和引數

  • 通過命令列向OS輸入命令列引數(預設為字串),OS將其傳遞給Java程式
  • OS常用命令(見書,javac/java/more)

標準輸出(❤)


  • 格式化輸出(printf),第一個引數為格式字串,描述第二個引數如何在輸出被轉化為一個字串(PS:該方法有多個引數)
    1. 格式字串第一個字元是%,後面跟一個字元表示的轉換程式碼(如d,f,s), 之間可插入整數表示轉換後的字串寬度),若寬度不夠,則左邊加空格,要想右邊加空格,要使用負寬度,若轉換後的字串寬度要大於指定寬度,則寬度設定忽略
    2. 例項: “3.2f”:表示小數位保留2位(小數點後的2,若對於字串,則表示擷取的長度),寬度為3(更多見書P23)
    3. 轉換程式碼表示的資料型別和對應引數資料型別匹配
    4. Sring的format方法引數同printf

標準輸入(❤)

  • 從標準輸入流中獲取資料
  • by default,系統會將標準輸出定位到虛擬終端
  • 輸入的內容即為輸入流
  • 輸入時,多引數用空白字元分隔,空白字元包括空格,製表符,換行符等
  • 我們標準輸入庫中靜態方法的API(P24)

重定向和管道(理解基本意思)

  • 標準輸入和輸出使得我們可利用命令列的擴充套件功能,只需要向命令中加入提示符(> 或<),可將輸出或輸入重定向為一檔案
  • 標準輸出流預設列印至虛擬終端,可重定向為一檔案
  • 標準輸入流預設從終端讀取資料,可重定向為一檔案
  • 一個程式的輸出重定向另一個程式的輸入稱為管道(% java Random 1000 10 20 | java Average)
  • 上述解釋:兩個Java程式,第一個產生隨機數,第二個求平均值,將Random的標準輸出流和Average的標準輸入流指定為同一個流
  • 上述方法突破能處理的輸入輸出流的長度限制(計算機即使沒有空間儲存10億個數也行),一邊輸出,將輸出流的末尾新增字串,一邊輸入,從輸入流的開頭刪除字串
  • 見書P25圖

本書提供的庫

  1. 基於檔案的輸入輸出庫(In,Out):我們庫提供了向檔案寫入或讀取檔案的陣列的方法
  2. 標準繪相簿(StdDraw):當前,輸入輸出抽象層的重點只是文字字串,加入標準繪相簿(標準繪圖抽象層),產生影象輸出的抽象層,該標準繪相簿預設比例尺為段位正方形(所有座標均在0和1之間),標準的實現將畫布顯示為一視窗,圖形為黑色,背景為白色,各API使用見書P26-27

二分查詢

  • 特點:高效,應用廣泛,藉此展示學習演算法的過程
  • 程式見P28
  • 關注其功能(波浪線處):輸入流(這裡重定向為一文字檔案)獲取entry,過濾掉存在於白名單檔案(另一個文字檔案)的entry,並列印(輸出)輸入流中還剩下的entry

資料抽象

即面向物件程式設計,模組化程式設計,其好處在於

  1. 模組化程式設計複用程式碼
  2. 方便構造資料結構(如鏈式)
  3. 準確定義演算法問題,如對於一些演算法問題,解決方式都是定義資料結構並高效地實現它們的一組操作

答疑

  1. Java的位元組碼:Java程式的一種低階表示,可以運行於JVM,將程式抽象為位元組碼並保證其跨平臺性
  2. Java允許整型溢位並返回錯誤的值,因為Java對於原始資料型別不自動檢查溢位
  3. 使用內建常數:Double.POSITIVE_INFINITY和Double.NEGATIVE_INFINITY可將double變數初始化為無限大
  4. Java自動進行型別轉換
  5. 若使用一變數前未初始化,且程式碼中存在使用該未初始化的變數的路徑,Java丟擲編譯異常
  6. 1/0與1.0/0.0,前者產生執行時除以0的異常(終止程式,由於該值未定義),後者是無窮大
  7. 只有原始資料型別定義<和>運算子
  8. 表示式a/b的商會向0取整
  9. &,|,^表示整數的位邏輯操作,&&和||僅在獨立的布林表示式中有效
  10. 巢狀if語句中的else與最近的if相搭配
  11. for迴圈頭部程式碼和主體程式碼在同一個程式碼段中,遞增變數迴圈結束後便不可用,而等價的while則不然