1.1 《演算法》第一章之導論和基本程式設計模型
阿新 • • 發佈:2018-12-11
文章目錄
《演算法》全書組織結構
- 基礎:介紹實現,分析和比較演算法的基本原則和方法,包括下面概述的內容
- 排序:陣列排序,優先佇列,選舉及歸併等
- 查詢:龐大的資料集查詢指定條目的演算法
- 圖:圖的主要內容是物件和它們的連線(有權重和方向),解決最短路徑等問題
- 字串:研究對字串操作的演算法
- 背景:討論其他領域前沿,如運籌學
概述
-
演算法:解決問題的方法
-
資料結構:便於演算法操作的組織資料的方法
-
本章介紹學習前的準備:
- Java基礎程式設計模型(用到的Java庫,語法等)
- 介紹資料抽象並定義抽象資料型別以進行模組化程式設計,介紹實現抽象資料型別的過程,即定義其API,通過Java的類機制來使用
- 學習三種基礎的抽象資料型別:揹包,佇列,棧,然後用陣列等實現它們的API,作為演算法實現的樣板和起點
- 分析演算法效能方法:基本做法:先對效能提出假設,建模,實驗驗證,必要時重複上述過程分析演算法效能方法
- 最後一個連通性問題,活學活用,最終實現union-find抽象資料結構
演算法
- 編寫程式:計算機語言實現一個已有方法(載體)
- 是演算法,而不是程式語言描述解決問題的方法,該方法要有限,確定,有效
- 以求兩非負整數的公約數的演算法為例(書)
- 便於演算法實現----》資料結構組織資料
- 編寫複雜問題,要理解,定義和控制問題的複雜度及分解這些問題,然後選擇合適演算法
- 選擇演算法-》分析演算法效能(時間空間複雜度)
基礎程式設計模型
- 使用特定程式語言使得分離演算法的思想和實現細節變得困難,所以只用大部分程式語言共有的語法
- 實現演算法所用到的語言特性,軟體庫和作業系統特性稱為基礎程式設計模型,可作為文件來查詢
- P9顯示本書程式碼風格,力求與各種Java程式設計慣例和語言構造相一致
Java程式基本結構
- 原始資料型別:精確地定義整數,浮點數等(不同取值範圍),組合為類似數學公式定義的表示式
- 語句:宣告,賦值,條件,迴圈,呼叫和返回
- 陣列:相同資料型別
- 靜態方法:封裝和重用程式碼,使coder能用獨立模組程式設計
- 字串:一連串字元
- 標準輸入/輸出
- 資料抽象:定義非原始資料型別,資料抽象和封裝(自定義類)
原始資料型別和表示式
- 資料型別:一組資料及對其所能進行的操作的集合
- Java控制變數(通過識別符號來引用),而變數有型別和值
- 初級運算(加減乘除)的關鍵是參與運算的資料型別和產生的資料型別應該一致
表示式
- Java運算子優先順序:見書
- 浮點-》整型,會截斷小數部分,而非四捨五入
- int型用一個字長為32位的機器字即可表示(有的電腦有字長為64位的機器字,但int型還是32位)
- Java作為強型別語言,會檢查型別的一致性
- 用模板來描述語句結構(如判斷語句)
程式碼的簡便寫法
- 宣告與初始化一起
- 隱式賦值:i++;i +=2;
- 單語句省略花括號
靜態方法
- 靜態方法又稱為函式
- 典型靜態方法:判斷素數,計算平方根等(見書)
- 方法的返回值替代表達式中的方法呼叫
- 返回語句結束函式並將控制權交給呼叫者
方法性質
- 方法引數按值傳遞:方法處理的是引數的值,而不是引數本身,該方式使得改變引數變數的值對呼叫者無影響(不要試圖修改引數變數來改變引數)。值傳遞意味陣列引數將會是原陣列的別名(指向同一個記憶體空間):方法中使用陣列引數變數能引用呼叫者的陣列並改變其內容(不能改變原陣列變數本身)
- 方法名可過載
- 方法只能返回一個值,但能包含多個返回語句
- void的靜態方法會產生副作用(輸入輸出等)
遞迴(❤)
- 遞迴總有一個簡單情況,方法的第一條語句總是包含return的條件語句
- 遞迴呼叫自己總是去嘗試解決子問題,hence,遞迴收斂到最簡單的情況
- 遞迴呼叫的父問題與子問題不該有交集(如二分法的子問題操作的陣列部分不同)
靜態方法庫
- 定義Java類中的一組靜態方法
- 模組化即面向物件程式設計
- Java開發模式是編寫一個靜態方法庫(包含main方法)來完成某任務,實現模組(靜態方法庫)化程式設計
- main方法的引數是輸入的字串組成的陣列
- main方法可編成測試用例,當用例複雜時,可獨立成一模組
外部庫
- 系統標準庫(預設匯入):java.lang.*
- 匯入的系統庫
- 第三方庫
- 本地庫
- 第三方及自己模組化方法編寫的方法庫擴充套件了程式設計模型
- 用例指的是呼叫其他庫中方法的程式
- 實現指的是實現某API的Java程式碼
- Math庫方法例項(見書P17)
- 本書編寫的庫(見P18-19)
自己編寫庫
- 編寫用例,在實現演算法中將計算過程分解為可控的部分
- 明確庫與其對應的API(API將實現與呼叫分離)
- 實現API及能獨立測試的main函式
tips
- 建立陣列時指明陣列大小是因為讓編譯器知道為陣列預留的空間
- 陣列的方法,包括reverse和矩陣乘法(見書)
字串
- 一連串字元(char型值)組成
- 字串拼接
- 字串型別轉換(同基本資料型別)
- 自動轉換:基本資料型別+字串=字串
- 命令列引數:main方法的引數是命令列輸入的字串組成的陣列
- 以後學習string類在Java中的表示方法
輸入輸出
- 本書提供一外部庫用以建立Java程式與外界交流的簡易模型
- 經典模型中,Java程式可從命令列引數或從一個抽象字元流(即標準輸入流)中獲得輸入,並將輸出寫入一個名為標準輸出流的字元流中
- 我們需要考慮Java程式和作業系統之間的介面-》簡要地討論OS和IDE所提供的相應機制:
- 預設情況下,命令列引數,標準輸入輸出與應用程式繫結,而應用程式是由能接受命令輸入的作業系統或開發環境所支援 。我們籠統地用終端來指這個應用程式提供的供輸入和顯示的視窗
命令和引數
- 通過命令列向OS輸入命令列引數(預設為字串),OS將其傳遞給Java程式
- OS常用命令(見書,javac/java/more)
標準輸出(❤)
- 格式化輸出(printf),第一個引數為格式字串,描述第二個引數如何在輸出被轉化為一個字串(PS:該方法有多個引數)
- 格式字串第一個字元是%,後面跟一個字元表示的轉換程式碼(如d,f,s), 之間可插入整數表示轉換後的字串寬度),若寬度不夠,則左邊加空格,要想右邊加空格,要使用負寬度,若轉換後的字串寬度要大於指定寬度,則寬度設定忽略
- 例項: “3.2f”:表示小數位保留2位(小數點後的2,若對於字串,則表示擷取的長度),寬度為3(更多見書P23)
- 轉換程式碼表示的資料型別和對應引數資料型別匹配
- Sring的format方法引數同printf
標準輸入(❤)
- 從標準輸入流中獲取資料
- by default,系統會將標準輸出定位到虛擬終端
- 輸入的內容即為輸入流
- 輸入時,多引數用空白字元分隔,空白字元包括空格,製表符,換行符等
- 我們標準輸入庫中靜態方法的API(P24)
重定向和管道(理解基本意思)
- 標準輸入和輸出使得我們可利用命令列的擴充套件功能,只需要向命令中加入提示符(> 或<),可將輸出或輸入重定向為一檔案
- 標準輸出流預設列印至虛擬終端,可重定向為一檔案
- 標準輸入流預設從終端讀取資料,可重定向為一檔案
- 一個程式的輸出重定向另一個程式的輸入稱為管道(% java Random 1000 10 20 | java Average)
- 上述解釋:兩個Java程式,第一個產生隨機數,第二個求平均值,將Random的標準輸出流和Average的標準輸入流指定為同一個流
- 上述方法突破能處理的輸入輸出流的長度限制(計算機即使沒有空間儲存10億個數也行),一邊輸出,將輸出流的末尾新增字串,一邊輸入,從輸入流的開頭刪除字串
- 見書P25圖
本書提供的庫
- 基於檔案的輸入輸出庫(In,Out):我們庫提供了向檔案寫入或讀取檔案的陣列的方法
- 標準繪相簿(StdDraw):當前,輸入輸出抽象層的重點只是文字字串,加入標準繪相簿(標準繪圖抽象層),產生影象輸出的抽象層,該標準繪相簿預設比例尺為段位正方形(所有座標均在0和1之間),標準的實現將畫布顯示為一視窗,圖形為黑色,背景為白色,各API使用見書P26-27
二分查詢
- 特點:高效,應用廣泛,藉此展示學習演算法的過程
- 程式見P28
- 關注其功能(波浪線處):輸入流(這裡重定向為一文字檔案)獲取entry,過濾掉存在於白名單檔案(另一個文字檔案)的entry,並列印(輸出)輸入流中還剩下的entry
資料抽象
即面向物件程式設計,模組化程式設計,其好處在於
- 模組化程式設計複用程式碼
- 方便構造資料結構(如鏈式)
- 準確定義演算法問題,如對於一些演算法問題,解決方式都是定義資料結構並高效地實現它們的一組操作
答疑
- Java的位元組碼:Java程式的一種低階表示,可以運行於JVM,將程式抽象為位元組碼並保證其跨平臺性
- Java允許整型溢位並返回錯誤的值,因為Java對於原始資料型別不自動檢查溢位
- 使用內建常數:Double.POSITIVE_INFINITY和Double.NEGATIVE_INFINITY可將double變數初始化為無限大
- Java自動進行型別轉換
- 若使用一變數前未初始化,且程式碼中存在使用該未初始化的變數的路徑,Java丟擲編譯異常
- 1/0與1.0/0.0,前者產生執行時除以0的異常(終止程式,由於該值未定義),後者是無窮大
- 只有原始資料型別定義<和>運算子
- 表示式a/b的商會向0取整
- &,|,^表示整數的位邏輯操作,&&和||僅在獨立的布林表示式中有效
- 巢狀if語句中的else與最近的if相搭配
- for迴圈頭部程式碼和主體程式碼在同一個程式碼段中,遞增變數迴圈結束後便不可用,而等價的while則不然