台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 a 棟

33
台台台台台 台台台台台台台台台台台 台 /一 112 台 10 台 A 台 Building A, 10F, 112, Shin-Tai-Wu Road Sec. 1, Shijr, Taipei, Taiwan. 台台02-26962869 台台02-26962867 台台台台台 台台台台台台台 台 /一 540 台 4 台 -1 4-1F, 540, Wen Shing Road, Sec. 1, Taichung, T aiwan. 台台04-23287870 台台04-23108168 台台台台http://www.drmaster.com.tw 台台 Java 台台台台台台台台台 台台台台台 台台 PG20098 台台 台 台台 西 台台 台台台 / 台台台

Upload: moe

Post on 14-Jan-2016

49 views

Category:

Documents


0 download

DESCRIPTION

書名 Java 於資料結構與演算法之實習應用 書號  PG20098 原著  河西朝雄 著 譯者 周明憲 / 徐堯譯. 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟 Building A, 10F, 112, Shin-Tai-Wu Road Sec. 1, Shijr, Taipei, Taiwan. 電話/ 02-26962869  傳真/ 02-26962867 台中辦事處/台中市文心路一段 540 號 4 樓 -1 4-1F, 540, Wen Shing Road, Sec. 1, Taichung, Taiwan. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟 Building A, 10F, 112, Shin-Tai-Wu Road Sec. 1, Shijr, Taipei, Taiwan.電話/ 02-26962869  傳真/ 02-26962867台中辦事處/台中市文心路一段 540 號 4 樓 -1 4-1F, 540, Wen Shing Road, Sec. 1, Taichung, Taiwan.電話/ 04-23287870  傳真/ 04-23108168

博碩網址:http://www.drmaster.com.tw

書名 Java 於資料結構與演算法之實習應用

書號  PG20098原著 河西朝雄著譯者 周明憲 /徐堯譯

Page 2: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

第三章 學習重點 數值分析計算與大量的資料處理都是電腦的重要工作。資料處

理的基本作業有排序 (sort) 與搜尋 (search) 。 排序的方法有基本型及其改良型,前者包括,直接選擇法、氣

泡排序與基本插入法等,後者則有 Shell 排序。本章將會說明這些排序方法。至於更高速的排序方法,如快速排序與堆積排序則會在第 4 章與第 6 章說明。

在搜尋的方法方面,本章將會說明逐筆搜尋與 2 元搜尋,同時也會說明較特殊且高速的搜尋方法—— Hash 。而 2 元搜尋樹搜尋法則將會在第 6 章說明。

本章也會說明將 2 組資料列合而為一的合併 (merge) 、字串的取代 (replace) 與字串的核對 (pattern matching) 。

Page 3: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

3-0 何謂排序與搜尋 排序 (sort)

排序為將資料按照一定的規則予以排列之意。1 、 5 、 11 、 20… 之類由小排到大的順序稱為遞增順序,… 20 、 11 、 5 、 1 之類由大排到小的順序則稱為遞減順序。

搜尋 (search)從大量的資料中找出必要資料的作業稱為搜尋 (search) 。搜尋的方法可大致分為逐筆搜尋法與 2 元搜尋法。

Page 4: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

3-1 基本排序 從數列中找出最小項次,

再與 a1 交換,如此從開始反覆操作直至數列成為 a1 為止的作業稱為直接選擇法排序。

將過程製作成演算法後,其內容如下:

(1) 將對象項 i 從 0 移至,並反覆操作系列步驟。

(2) 將對象項設為初始值(最小值)。

(3) 關於對象項項,則反覆操作下列步驟。

(4) 找出最小項,在 s 中求出其項次編號。

(5) 將 i 項與 s 項交換。

Page 5: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

public void paint(Graphics g) { final int N=6; // 資料數 int[] a={80,41,35,90,40,20}; int min,s,t,i,j;

for (i=0;i<N-1;i++) { min=a[i]; s=i; for (j=i+1;j<N;j++) { if (a[j]<min) { min=a[j]; s=j; } } t=a[i];a[i]=a[s];a[s]=t; }

for (i=0;i<N;i++) g.drawString(""+a[i],i*20,20); }}

Page 6: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

3-1 氣泡排序 比較相鄰的 2 項

資料,下面(後面)的資料項若比上面(前面)的資料項小,則反覆進行這 2個資料項更換作業。由於較小的資料項恰似氣泡由下往上升的樣子,因此稱為氣泡排序。

Page 7: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

public void paint(Graphics g) { final int N=6; // 資料數 int[] a={80,41,35,90,40,20}; int t,i,j;

for (i=0;i<N-1;i++) { for (j=N-1;j>i;j--) { if (a[j]<a[j-1]) { t=a[j];a[j]=a[j-1];a[j-1]=t; } } }

for (i=0;i<N;i++) g.drawString(""+a[i],i*20,20); }}

Page 8: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

3-1 shaker 排序 氣泡排序由於不論述列是否正

確排列皆會無條件的反覆進行比較,因此效率較差。如果由數列的前方(左側)開始掃瞄至交換為止的位置為 i 與 i+1 ,則 i+1~n-1 的元素由於應已正確排列,因此無須再次掃瞄。為此,我們可將最後交換的位置儲存至 shift ,並將此位置設為下次掃瞄時的範圍。

不過要是有小的元素位於右端,則最後的交換均會在數列的後方進行,上面的 shift 的效果就少得很多。因此掃瞄的方向就要改為由左至右及由右至左雙向交互進行。這就是 shaker 排序。

Page 9: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

public void paint(Graphics g) { final int N=9; // 資料數 int[] a={3,5,6,9,2,7,8,10,4}; int left,right,i,shift=0,t; left=0;right=N-1; while (left<right) { for (i=left;i<right;i++) { if (a[i]>a[i+1]) { t=a[i];a[i]=a[i+1];a[i+1]=t; shift=i; } } right=shift; for (i=right;i>left;i--) { if (a[i]<a[i-1]) { t=a[i];a[i]=a[i-1];a[i-1]=t; shift=i; } } left=shift; } for (i=0;i<N;i++) g.drawString(""+a[i],i*20,20); }}

Page 10: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

3-2 Shell 排序 基本插入法的原理為將數

列的視為已排列整齊的數列,再查出應放入此數列的哪個位置,然後再將其插入至此位置。這個步驟只要將 i 在 1 至反覆操作即可。

至於要放在數列的哪個位置,則只要由數列的右端開始,在比數列記錄項較小的空間中反覆執行交換作業。

Page 11: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

public void paint(Graphics g) { final int N=100; // 資料數 int i,j,t; int[] a=new int[N];

for (i=0;i<N;i++) // N個亂數 a[i]=(int)(1000*Math.random()+1);

for (i=1;i<N;i++) { for (j=i-1;j>=0;j--) { if (a[j]>a[j+1]) { t=a[j];a[j]=a[j+1];a[j+1]=t; } else break; } }

int y=0; for (i=0;i<N;i++) { if (i%14==0) y++; g.drawString(""+a[i],(i%14)*30,y*20); } }}

Page 12: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

3-2 shell 排序 不以基本插入法一口氣將數列 a0, a1, a2, ..., an-1做排序,而按一定的間隔 (gap) 將數列分成數個數列,再逐一以基本插入法做排序。像這樣一面將一部份的數列大略做排序,一面將一部份的數列收斂至全部數列(將 gap 設為 1 )後,最後的排序作業即告結束。也就是說,在適用 gap=1 的基本插入法以前,先將較小的元素大略的排列至前面,較大的元素則排列至後面,以減低比較與交換的次數,這種方式稱為 shell 排序。這個名稱是取自提出此方法的 Shell 。

gap 的決定方式中有最佳的方式,不過這裡僅單純的說明將 gap劃分一半的方法。

Page 13: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

3-2 shell 排序 我們以圖 3.8 為例。假設資

料有 8個,則最初的 gap 為8/2=4 ,然後將 4個間隔的數列 (51,45) 、 (60,70) 、 ...逐一以基本插入法進行排序。

接下來將 gap 設為 4/2=2 ,在將 2個間隔的數列 (45,55,51,80) 、 (60,21,70,30) 逐一以基本插入法進行排序。

然後接下來將 gap 設為 2/2=2 ,在將 1個間隔的數列 (45,21,...) 以基本插入法進行排序。由於 gap=1 的數列為全部數列,因此排序作業就此結束。

上圖中 gap=2 時的數列有以下 2 組: 01234567 ○□○□○□○□

Page 14: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

3-2 shell 排序 如製作成演算法,則其大致內容如下:(1) 設定 gap 的初始值 (N/2)(2) 反覆操作以下步驟直至 gap 的值為 1 。

(3)gap間隔的數列共有 gap個,因此反覆操 作的次數為此 gap個數以下

(4) 以基本插入法將 gap間隔的數列 (aj, aj+gap, aj+2gap,...)進行排序

Page 15: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

3-2 Shell 排序的改良 gap 的選定方法

一般來說,將 gap 系統選為可互為基礎的方法是最有效率的方法,而更簡單的方法有:使用 ..., 121, 40, 13, 4, 1 這類數列的方法。 gap 的初始值為從資料數 N 以內的前面之數列中選出的最大值,而像練習問題 19 中以 ..., 8, 4, 2, 1 之類的 2 的 2 次方來取出 gap 後,效率就變得較差。

資料的區域參考性將數列○□○□○□○□分成○○○○與□□□□這 2個數列,再以基本插入法分別進行排序時,由於陣列元素的參考會分散,因此記憶存取的效率就會很差。如要查詢大量資料時,最好查詢連續的資料區塊,這樣存取的效率會比較好。這種情形稱為區域參考 (local reference) 。

Page 16: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

3-3 逐筆搜尋與衛兵 逐筆搜尋

是指從儲存於陣列等的資料之開頭起,依序逐一搜尋所要找的資料,當找到所要的資料後,便停止搜尋作業,是一種單純的搜尋法。

設衛兵以設衛兵的方式來改進結束判斷的設定。

Page 17: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

逐筆搜尋 --- public void actionPerformed(ActionEvent e) { final int N=10; // 資料數 int i; Girl[] a={new Girl("Ann",18),new Girl("Rolla",19), new Girl("Nancy",16),new Girl("Eluza",17), new Girl("Juliet",18),new Girl("Machilda",20), new Girl("Emy",15),new Girl("Candy",16), new Girl("Ema",17),new Girl("Mari",18)};

String key=tf.getText();

i=0; while (i<N && !key.equals(a[i].name)) i++;

if (i<N) ta.setText(ta.getText()+a[i].name+","+a[i].age+"\n"); else ta.setText(ta.getText()+key+" 的資料找不到 \n"); }

Page 18: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

設衛兵 ---public void actionPerformed(ActionEvent e) { final int N=10; // 資料數 int i; Girl[] a={new Girl("Ann",18),new Girl("Rolla",19), new Girl("Nancy",16),new Girl("Eluza",17), new Girl("Juliet",18),new Girl("Machilda",20), new Girl("Emy",15),new Girl("Candy",16), new Girl("Ema",17),new Girl("Mari",18), new Girl("",0)}; // 衛兵

String key=tf.getText();

a[N]=new Girl(key,0); i=0; while (!key.equals(a[i].name)) i++;

if (i<N) ta.setText(ta.getText()+a[i].name+","+a[i].age+"\n"); else ta.setText(ta.getText()+key+" 的資料找不到 \n"); }

Page 19: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

3-4 2 元搜尋 2 元搜尋法為在資料按遞增

順序或遞減順序排列時有效的搜尋法。

我們將搜尋範圍的上下限分別設為 upper 與 low ,然後將位於 x=(low+upper)/2位置的資料與鍵值(欲搜尋的鍵值)做一比較。若鍵值較大,則鍵值的位置應位於x的上面,因此 low便為 x+1 。反之,若鍵值較小,則鍵值的位置應位於 x的下面,因此 low便為 x-1 。如此在low≦ upper 之間反覆操作這個步驟。

Page 20: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

3-4 2 元搜尋實際搜尋範例: (1) 起先由於 low=0 , upper=9 ,因此 x=(0+9)/2

=4 ,然後將第 4個的資料 31 與欲搜尋的資料 50做一比較,由於 50 比 31 大,因此 low=4+1=5 。

(2) 當 low=5 , upper=9 時, x=(5+9)/2=7 ,然後將第 7個的資料 70 與欲搜尋的資料 50做一比較,由於 50 比 70 小,因此 upper=7-1=6 。

(3) 當 low=5 , upper=6 時, x=(5+6)/2=5 ,然後將第 5個的資料 50 與欲搜尋的資料 50做一比較,而找出欲搜尋的資料。

Page 21: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

public void actionPerformed(ActionEvent e) { final int N=10; // 資料數 int[] a={2,3,7,11,31,50,55,70,77,80}; int key,low,high,mid,flag=0;

key=Integer.parseInt(tf.getText()); low=0;high=N-1; while (low<=high) { mid=(low+high)/2; if (a[mid]==key) { ta.setText(ta.getText()+a[mid]+" 在第 "+mid+"個位置 \n"); flag=1; break; } if (a[mid]<key) low=mid+1; else high=mid-1; } if (flag!=1) ta.setText(ta.getText()+key+"沒找到 \n");}

Page 22: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

3-5 合併 合併 (merge) 是指將經排序過

的 2 組(或 3 組以上)的資料組合成 1個經過同樣排序的資料。

設 a 、 b 為經過排序的 2 組資料, c 為新的資料,各資料的開頭編號各為 i 、 j 、 p ,合併演算法的大致內容如下:

(1) 反覆操作下列步驟至資料 a或b 的尾端。(2) 比較 ai 與 bj ,將較小的一方複製至 cp ,然後將較小方的資料的編號向前推進 1位。

(3) 將尾端為止的資料複製至 c 。

Page 23: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

public void paint(Graphics g) { final int M=10, // a[] 的資料數 N=5; // b[] 的資料數 int i=0,j=0,p=0; int[] a={2,4,5,7,8,10,15,20,30,40}, b={6,11,25,33,35}, c=new int[M+N];

while (i<M && j<N) { // 當還未達到 a[],b[] 的尾端時 if (a[i]<=b[j]) c[p++]=a[i++]; else c[p++]=b[j++]; } while (i<M) // 直到 a[] 的尾端 c[p++]=a[i++]; while (j<N) // 直到 b[] 的尾端 c[p++]=b[j++];

for (i=0;i<M+N;i++) g.drawString(""+c[i],i*20,20); }}

Page 24: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

3-6 字串的核對 (pattern matching) 設文字 text 內含有欲核對

的字串 key。如欲從 text 之中找出 key,可先從 text 的開頭起逐字與 key 做比較。如發現到 key,方法 search 會傳回 key在 text 中的位置,如果搜尋到 text 的結尾(正確來說應是 text的結尾至 key的長度前的位置)卻未找到 key,方法 search便會傳回 -1 的值。

Page 25: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

…… int search(int p,String text,String key) { int i,m,n;

m=text.length(); n=key.length(); for (i=p;i<=m-n;i++) { if ((text.substring(i,i+n)).equals(key)) return i; } return -1; }

……… bt.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { String text="This is a pen. That is a pencil."; String key=tf.getText(); int m,n,p;

m=text.length(); n=key.length(); p=search(0,text,key); while (p!=-1){ ta.setText(ta.getText()+text.substring(p,m)+"\n"); p=search(p+n,text,key); }

Page 26: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

3-6 鍵值字串與區分字元 如要讓程式瞭解 pen 與 pencil 、 sharpen 的 pen 之不同,只要在字

串 pen被找到時,讓 pen 的前後字串為區分字元(空格或 . )即可。pen 的位置若在文字的開頭或結尾時, (1)部分的程式寫法如下:

if ((text.substring(i,i+n)).equals(key)) { if (i==p || i==m-n) return i; String f=text.substring(i-1,i); String b=text.substring(i+n,i+n+1); if ((f.equals(" ") || f.equals(".")) && (b.equals(" ") || b.equals("."))) return i; }

Page 27: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

3-6 Boyer-Moore 法 Boyer-Moore 法則以字串與鍵值的右端為基準進行核對作

業,當這 2 者不相符時,並不向前推進 1個字元,而是向欲核對字元的右端的字元之值前進。

Boyer-Moore 法演算法的大致內容如下:(1) 製作「前進值」表。(2) 核對完 text 以前,反覆操作以下步驟。

(3) 如 text 中的 p位置的字元與 key 右邊的字元 ( 以 key[n-1]表示 ) 一致的話,就將以 p-n+1 為首位的 n 字元的字串與 key比較,若一致的話,即為 p-n+1所求的位置。 (4) 將位置 p向後移動「前進值」所定的字元。

Page 28: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

……… int search(int p,String text,String key) { int i,m,n; m=text.length(); n=key.length(); while (p<m) { if ((text.substring(p-n+1,p+1)).equals(key)) return p-n+1; p=p+skip[text.charAt(p)]; // 將搜尋位置向前推進 } return -1; // 若沒找到 }

void table(String key) { // 製作跳躍表 int k,n; n=key.length(); for (k=0;k<=255;k++) skip[k]=n; for (k=0;k<n-1;k++) skip[key.charAt(k)]=n-k-1; }………

Page 29: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

3-7 字串的取代 從 text 中找出 key字串的內容,再以字串 rep 的

內容取代 key字串的內容。

Page 30: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

……… int search(int p,String text,String key) { int i,m,n; m=text.length(); n=key.length(); for (i=p;i<=m-n;i++) { if ((text.substring(i,i+n)).equals(key)) return i; } return -1; }

String replace(String text,String key,String rep) { int p; String d1,d2; p=search(0,text,key); while (p!=-1) { d1=text.substring(0,p); d2=text.substring(p+key.length(),text.length()); text=d1+rep+d2; p=search(p+rep.length(),text,key); } return text; }……

Page 31: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

3-8 Hash(雜湊 ) 假設有一個班級的學生資

料,如要以學號管理這些資料,只要將這班學生的學號設為記錄編號並存入陣列(檔案),便可立即以記錄編號基礎來參考這些資料。

不過,有些資料無法以編號來管理,這時一般都將姓名等非數值資料設為鍵值,再進行搜尋,否則無法將資料參考起來。

Page 32: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

3-8 Hash 函數 我們將由英文大寫字母組成的姓名設為鍵值,將這些鍵值的名

稱設為 A1 、 A2 、 A3... 、 An ,共有 n個字元。若將鍵值的長度設為 n個字元,則所能取得的姓名組合便有 26n+1個,這是 1個很大的數值範圍。因此我們使用鍵值開頭的 A1 、鍵值中間的An/2 與鍵值倒數第 2位的 An-1 這 3個字元,來設定下列的函數。

假設鍵值為“ SUZUKI”,則

因此只要將數值 428視為陣列的元素或檔案的紀錄編號,再對應至 SUZUKI 即可。

1000)%2626()( 212/121 nnn AAAAAAhash

1000)%26)'''('26)'''(')'''((')"(" 2 AKAZASSUZUKIhash

1000)%676065018( 428

Page 33: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

……… int hash(String s) { // Hash函數 final int ModSize=1000; int n; n=s.length(); return (s.charAt(0)-'A'+(s.charAt(n/2-1)-'A')*26 +(s.charAt(n-2)-'A')*26*26)%ModSize; }

…………