Data Structure and Algorithm การเรียงลำดับข้อมูล (Sorting)
Sorting การแสดงผล การคำนวณ การจัดเรียงข้อมูลเป็นขั้นตอนหนึ่งที่สำคัญในการประมวลผลข้อมูล ข้อมูลที่จัดเรียงแล้วช่วยให้เราทำงานกับข้อมูลนั้นได้ง่ายขึ้นเช่น การแสดงผล การคำนวณ
ประโยชน์ด้านการแสดงผล รายการโทรศัพท์ จัดเรียงตามวันที่
ประโยชน์ด้านการค้นหาและแสดงผล จัดเรียงตาม Sender, Subject, Date/Time
ประโยชน์ด้านการคำนวณ การหาค่า Median (ค่าข้อมูล ณ. ตำแหน่งกลางของชุดข้อมูล) 20 25 45 46 49 55 65 73 80 92 101 การหาค่า Maximum หากข้อมูลไม่ได้จัดเรียงต้องใช้เวลา O(N) เพื่อหาค่า Max แต่เมื่อข้อมูลจัดเรียงเรียบร้อยแล้วใช้เวลา O(1)
Ascending VS. Descending Order เราสามารถจัดเรียงข้อมูลได้ทุกประเภท int, float, boolean, character, string ฯลฯ ข้อมูลที่จัดเรียงต้องมี Key ที่ใช้เป็นตัววัดลำดับข้อมูลเช่น จำนวน วันที่ ชื่อ เป็นต้น การจัดเรียงจากน้อยไปหามาก (Ascending Order) เช่นเรียงตัวเลข, วันที่ในรายการสมุดบัญชีธนาคาร การจัดเรียงมากไปหาน้อย (Descending Order) เช่น เรียง E-mail โดยเอาวันที่ล่าสุดขึ้นมาก่อน
สมมติมีข้อมูล 1 ชุด จำนวน 6 ตัว ดังนี้ ข้อมูลใน Array ชื่อ List Data Representation for Sorting ข้อมูลที่ต้องการจัดเรียงนั้นมีอยู่หลายรูปแบบ เช่น ข้อมูลที่อยู่ใน Array, อยู่ในไฟล์, อยู่ในฐานข้อมูล ในวิชานี้เราศึกษาถึงรูปแบบการเรียงลำดับข้อมูลใน Array เท่านั้น สมมติมีข้อมูล 1 ชุด จำนวน 6 ตัว ดังนี้ 44 33 11 85 77 60 ข้อมูลในอุดมคติ List 44 33 11 85 77 60 ข้อมูลใน Array ชื่อ List
ความหมาย การจัดเรียงลำดับ (Sorting) หมายถึงการจัดเรียงข้อมูล ให้เรียงลำดับตามเงื่อนไขที่กำหนดไว้ (มากไปน้อย หรือ น้อยไปมาก) ในกรณีที่ข้อมูลในแต่ละ Record มีหลาย Field เราต้องพิจารณาเลือก Field ที่สนใจเพื่อใช้ในการเรียงลำดับ เช่น การจัดเรียงลำดับประวัตินักศึกษา อาจใช้หมายเลขประจำตัวของนักศึกษาเป็น Field โดยเรียงจากน้อยไปมาก เป็นต้น
ประเภทของการเรียงลำดับข้อมูล การจัดเรียงภายใน (Internal Sorting) การจัดเรียงลำดับข้อมูลที่เก็บอยู่ในหน่วยความจำของเครื่องคอมพิวเตอร์ การจัดเรียงแบบนี้จะต้องอาศัยเทคนิคและวิธีการของโครงสร้างข้อมูลมาช่วย เช่น การใช้ Array หรือ Linked-List เข้ามาช่วย การจัดเรียงภายนอก (External Sorting) การจัดเรียงข้อมูลที่เก็บอยู่ในสื่อบันทึกข้อมูล เช่น Disk โดยทั่วไปการเรียงประเภทนี้ มักใช้กับข้อมูลที่มีจำนวนมาก ที่ไม่สามารถเก็บไว้ในหน่วยความจำได้หมด การเรียงในแบบนี้จะต้องแบ่งข้อมูลออกเป็นส่วนย่อย แล้วนำมาเรียงด้วยการจัดเรียงแบบภายในก่อน แล้วจึงนำแต่ละส่วนย่อยมารวมกัน
วิธีการจัดเรียงข้อมูล การจัดเรียงแบบแลกเปลี่ยน (Exchange Sort) การจัดเรียงแบบแทรก (Insertion Sort) การจัดเรียงแบบเลือก (Selection Sort)
การจัดเรียงแบบแลกเปลี่ยน (Bubble Sort) เป็นการจัดเรียงโดยการเปรียบเทียบค่า 2 ค่าที่ติดกัน ทำต่อเนื่องกันไปเรื่อย ๆ และตัดสินใจว่าจะสลับตำแหน่งกันหรือไม่ เช่น ถ้าต้องการเรียงข้อมูลจากน้อยไปมาก ก็คือ ข้อมูลที่มีค่าน้อย ต้องอยู่ในตำแหน่งหน้า ข้อมูลที่มีค่ามาก จะอยู่ตำแหน่งหลัง ข้อมูล 2 ตัวที่อยู่ติดกัน ถ้า ถ้าข้อมูลตัวแรกมากกว่าตัวหลัง ก็จะต้องสลับตำแหน่งกัน แต่ถ้าข้อมูลตัวแรกน้อยกว่าข้อมูลตัวหลัง ก็ไม่ต้องสลับตำแหน่ง ทำเช่นนี้ซ้ำกันไปเรื่อย ๆ จนกว่าการเปรียบเทียบของข้อมูลตลอดทั้งชุดจะไม่ต้องมีการสลับตำแหน่งเลย
Bubble Sort เป็นวิธีที่ง่ายเช่นกัน แนวคิด คือค่าที่มากๆ จะต้องถูกนำไป (ลอยไป) ไว้ด้านท้าย เหมือนลูกโป่งที่ขนาดใหญ่จะลอยได้เร็วและสูง แนวคิด เริ่มนำข้อมูลตัวแรกเทียบกับตัวที่ 2 ตัวไหนมากก็จะถูกสลับกัน ทำอย่างนี้ไปจนถึงตัวสุดท้าย เราจะได้ค่าที่มากที่สุด 1 ตัวไว้ด้านท้าย แต่ละครั้งจะได้ค่ามากที่สุดไปไว้ท้ายสุด จากนั้นเริ่มการเปรียบเทียบใหม่ตั้งแต่ตัวแรกถึงตัวที่ N-1 จากนั้นเริ่มการเปรียบเทียบใหม่ตั้งแต่ตัวแรกถึงตัวที่ N-2 … จากนั้นเริ่มการเปรียบเทียบใหม่ตั้งแต่ตัวแรกถึงตัวที่ N-x ทำจน x = N-1
ตัวอย่าง Function การจัดเรียงแบบ Bubble
ตัวอย่าง: Bubble Sort 44 55 12 42 94 18 06 67 ข้อมูล 8 ตัวทำ 7 รอบ รอบที่ ข้อมูล 1 06 44 55 12 42 94 18 67 2 06 12 44 55 18 42 94 67 3 06 12 18 44 55 42 67 94 4 06 12 18 42 44 55 67 94 5 06 12 18 42 44 55 67 94 6 06 12 18 42 44 55 67 94 7 06 12 18 42 44 55 67 94
Program bublesort1; const n = 8; a:array[1..n] of integer = (44,55,12,42,94,18,06,67); VAR i,j,l,temp : integer; begin for i := 2 to n do for j := n downto i do if a[j-1] > a[j] then temp := a[j-1]; a[j-1] := a[j]; a[j] := temp; end; for l := 1 to n do write(a[l]:10); writeln;readln; end.
int main() { int A[ELEMENTS]={44,55,12,42,94,6,18,67}; int x; cout<<"NON SORTED LIST:"<<endl; for(x=0;x<ELEMENTS;x++) cout<<A[x]<<endl; } exchange_sort(A,ELEMENTS); cout<<endl<<"SORTED LIST"<<endl; for(x=1;x<=ELEMENTS;x++) return 0; #include <iostream.h> #define ELEMENTS 8 void exchange_sort(int x[],int length) { int temp; for(int i=0;i<length;i++) for (int j=length;j>i;j--) if(x[j-1]>x[j]) temp=x[j-1]; x[j-1]=x[j]; x[j]=temp; }
การจัดเรียงแบบแทรก (Insertion Sort) เป็นการจัดเรียงโดยการนำข้อมูลที่จะทำการเรียงนั้น ๆ ไปจัดเรียงทีละตัว โดยการแทรกตัวที่จะเรียงไว้ในตำแหน่งที่เหมาะสมของข้อมูลที่มีการจัดเรียงเรียบร้อยแล้ว ณ ตำแหน่งที่ถูกต้อง วิธีการลักษณะนี้จะคล้ายกับการหยิบไพ่ขึ้นมาเรียงทีละใบ ซึ่ง ไพ่ใบแรกจะไม่ต้องสนใจอะไร แต่เมื่อหยิบไพ่ใบที่ 2 ก็จะต้องพิจารณาว่าจะไว้ก่อนหรือไว้หลังใบแรก และเช่นเดียวกัน เมื่อหยิบไพ่ใบถัด ๆ มา ก็จะต้องพิจารณาว่าจะวางใบตำแหน่งใดเพื่อให้เกิดการเรียงลำดับ จนกระทั่งหมด
ตัวอย่าง Function การเรียงแบบ Insertion
ตัวอย่าง: Insertion Sort 44 55 12 42 94 18 06 67 ตัวอย่าง: Insertion Sort ข้อมูล 8 ตัวทำ 7 รอบ รอบที่ ข้อมูล 2 44 55 12 42 94 18 06 67 3 12 44 55 42 94 18 06 67 4 12 42 44 55 94 18 06 67 5 12 42 44 55 94 18 06 67 6 12 18 42 44 55 94 06 67 7 06 12 18 42 44 55 94 67 8 06 12 18 42 44 55 67 94
Program insertionsort; const n = 8; a:array[1..n] of integer = (44,55,12,42,94,18,06,67); VAR i,j,l,x : integer; begin for i := 2 to n do x := a[i]; j := i-1; while (x < a[j]) and (j > 0) do a[j+1] := a[j]; j := j - 1; end; a[j+1] := x; for l := 1 to n do write(a[l]:10); writeln;readln; end.
int main() { int A[ELEMENTS]={44,55,12,42,94,06,18,67}; int x; cout<<"NON SORTED LIST:"<<endl; for(x=0;x<ELEMENTS;x++) cout<<A[x]<<endl; } insertion_sort(A,ELEMENTS); cout<<endl<<"SORTED LIST"<<endl; return 0; #include <iostream.h> #define ELEMENTS 8 void insertion_sort(int x[],int length) { int key,i; for(int j=1;j<length;j++) key=x[j]; i=j-1; while(x[i]>key && i>=0) x[i+1]=x[i]; i--; } x[i+1]=key;
การจัดเรียงแบบเลือก (Selection Sort) เป็นการจัดเรียงโดยการเริ่มต้นค้นหาข้อมูลตัวที่น้อยที่สุดจากข้อมูลที่มีอยู่ทั้งหมด แล้วเอามาเก็บไว้ข้างนอก แล้วกลับไปหาข้อมูลตัวที่น้อยที่สุดในกองต่อไปจนกว่าจะหมดกอง
ตัวอย่าง Function การเรียงแบบ Selection
ตัวอย่าง: Selection Sort 44 55 12 42 94 18 06 67 ข้อมูล 8 ตัวทำ 7 รอบ รอบที่ ข้อมูล 1 06 55 12 42 94 18 44 67 2 06 12 55 42 94 18 44 67 3 06 12 18 42 94 55 44 67 4 06 12 18 42 94 55 44 67 5 06 12 18 42 44 55 94 67 6 06 12 18 42 44 55 94 67 7 06 12 18 42 44 55 67 94
Program selectionsort; const n = 8; a:array[1..n] of integer = (44,55,12,42,94,18,06,67); VAR i,j,k,l,temp : integer; begin for i := 1 to n-1 do k := i; for j := i+1 to n do if a[k] >a[j] then k := j; temp := a[i]; a[i] := a[k]; a[k] := temp; for l := 1 to n do write(a[l]:10); end; writeln;readln; end.
int main() { int A[ELEMENTS]={44,55,12,42,94,6,18,67}; int x; cout<<"NON SORTED LIST:"<<endl; for(x=0;x<ELEMENTS;x++) cout<<A[x]<<endl; } selection_sort(A,ELEMENTS); cout<<endl<<"SORTED LIST"<<endl; for(x=0;x< ELEMENTS;x++) return 0; #include <iostream.h> #define ELEMENTS 8 void selection_sort(int x[],int length) { int k,temp; for(int i=0;i<length;i++) k=i; for (int j=i+1;j< length;j++) if(x[j]< x[k]) k=j; } temp=x[i]; x[i]=x[k]; x[k]=temp;
Shellsort Shellsort ตั้งตามชื่อของผู้คิดค้นการจัดเรียงแบบนี้ คือ Donald Shell และเป็นอัลกอริทึมแรกที่ทำลายขอบเขตเวลาที่เป็น quadratic ในขณะทำงานแต่ละ phase นั้น shell sort ใช้การเปรียบเทียบค่าที่อยู่ในตำแหน่งที่ห่างกัน ระยะห่างดังกล่าวนี้จะลดลงลงเรื่อย ๆ จนกระทั่งถึงขั้นตอนสุดท้ายที่เป็นการเปรียบเทียบค่าที่อยู่ติดกัน ด้วยเหตุที่ระยะห่างของค่าที่นำมาเปรียบเทียบกันลดลงในระหว่างการทำงานของอัลกอริทึมนี้เอง จึงเรียกShellsort อีกอย่างว่า diminishing increment sort
Shellsort ใช้การลำดับของ h1, h2, Shellsort ใช้การลำดับของ h1, h2, . . . , ht ซึ่งเรียกว่า increment sequence และลำดับที่ใช้จะมีค่าลักษณะใดก็ได้เพียงแต่มีเงื่อนไขว่า h1 = 1 เท่านั้น แต่แน่นอนว่าบางลำดับจะทำงานได้ดีกว่าบางลำดับ (จะกล่าวถึงอีกครั้ง) ในการทำงานแต่ phase ที่ใช้ลำดับการเพิ่ม hk ผลที่ได้ คือ สำหรับแต่ละค่า i เราจะได้ว่า a[i] a[i + hk] กล่าวคือสมาชืกทุกตัวที่มีระยะห่างกัน hk จะอยู่ในลำดับที่มีกี่จัดเรียงอย่างถูกต้อง ซึ่งเรียกว่า hk-sorted file
Figure Shellsort หลังการทำงานแต่ละ pass คุณสมบัติที่สำคัญของ Shellsort คือ การทำ hk-sorted แล้วตามด้วย hk-1-sorted นั้นยังคงสภาพของ hk-sorted Original 81 94 11 96 12 35 17 95 28 58 41 75 15 -------------------------------------------------------------------------------------- After 5-sort 35 17 11 28 12 41 75 15 96 58 81 94 95 After 3-sort 28 12 11 35 15 41 58 17 94 75 81 96 95 After 1-sort 11 12 15 17 28 35 41 58 75 81 94 95 96 Figure Shellsort หลังการทำงานแต่ละ pass
Shellsort routine ใช้ลำดับการเพิ่มของ Shell public static void shellsort( Comparable [ ] a ) { int j; /* 1*/ for( int gap = a.length / 2; gap > 0; gap /= 2 ) /* 2*/ for( int i = gap; i < a.length; i++ ) /* 3*/ Comparable tmp = a[ i ]; /* 4*/ for( j = i; j >= gap && tmp.compareTo( a[ j - gap ] ) < 0; j -= gap ) /* 5*/ a[ j ] = a[ j - gap ]; /* 6*/ a[ j ] = tmp; }
Heapsort ในการจัดเรียงด้วยเวลา O(N log N) อัลกอริทึมที่ใช้พื้นฐานแนวคิดนี้ เรียกว่า heapsort และมี Big-Oh running time ดีกว่าอัลกอริทึมอื่น ๆ ที่กล่าวมาแล้ว วิธีการ คือ สร้าง binary heap (มีสมาชิก N ตัว) ซึ่งใช้เวลา O(N) จากนั้นทำ deleteMin N ครั้ง สมาชิกที่ถูกย้ายออกไปจาก heap ตัวแรก คือตัวที่มีค่าน้อยที่สุด และเรียงลำดับตามค่าไป แล้วนำไปเก็บใน Arrray อีกตัวหนึ่งจากนั้นก็คัดลอกกลับไปยัง Array เดิมซึ่งก็จะเป็นคำตอบในการจัดเรียง เนื่องจากการทำ deleteMin แต่ละตัวใช้ O(log N) ดังนั้น running time รวมทั้งหมด คือ O(N log N)
หลังจบอัลกอริทึมจะได้ Arrayที่มีสมาชิกเรียงตามลำดับค่า หลังการทำงานจนเสร็จ Array ที่ได้ก็จะเป็น Arrayของสมาชิกที่จัดเรียงจากมากไปน้อย ถ้าต้องการจัดเรียงจากน้อยไปมาก เราก็จะใช้ heap ที่ parent มีค่ามากกว่า child ของมัน นั่นคือ (max)heap เราจะใช้ (max)heap ในการ implement ของเราและยังคงใช้ Array เช่นเดิม ขั้นแรกสร้าง heap ด้วย linear time จากนั้นทำ deleteMaxes จำนวน N - 1 ครั้ง ด้วยการสลับสมาชิกตัวสุดท้ายใน heap กับสมาชิกตัวแรก แล้วลดขนาดของ heap ลงแล้วทำการ percolating down หลังจบอัลกอริทึมจะได้ Arrayที่มีสมาชิกเรียงตามลำดับค่า
Figure 7.6 (Max) heap หลังการ build_heap Figure 7.7 Heap หลัง deleteMax ครั้งแรก Figure 7.6 (Max) heap หลังการ build_heap
Quick Sort หลักการดำเนินงาน * หาตำแหน่งในการแยกลิสต์ * แบ่งแยกข้อมูลออกเป็นสองส่วน และหาตำแหน่ง ในการแยกลิสต์ * ทำจนกว่าข้อมูลจะเรียงลำดับเรียบร้อย
เริ่มต้นใช้ข้อมูลตัวแรกเป็นตัวเปรียบเทียบ (pivot) 15 9 7 16 31 2 20 25 17 12 Ex เริ่มต้นใช้ข้อมูลตัวแรกเป็นตัวเปรียบเทียบ (pivot) 15 9 7 16 31 2 20 25 17 12 first last pivot ทำการเปรียบเทียบค่าของข้อมูลที่ชี้โดย first กับ Pivot ถ้าน้อยกว่า pivot ให้ทำการเลื่อน first ไปยังข้อมูลต่อไปและเปรียบเทียบไปจนกว่าจะพบ แล้วจึงหยุดการเปรียบเทียบ
เมื่อพบตำแหน่งแล้ว จะหันมาพิจารณาที่ last ชี้อยู่ 15 9 7 16 31 2 20 25 17 12 first last เมื่อพบตำแหน่งแล้ว จะหันมาพิจารณาที่ last ชี้อยู่ หากค่าที่ last ชี้อยู่มีค่าน้อยกว่าที่ first ชี้อยู่ให้สลับตำแหน่ง 15 9 7 12 31 2 20 25 17 16 first last
จากชุดข้อมูลหลังจากการสลับค่า 15 9 7 12 31 2 20 25 17 16 first last ทำการเปรียบเทียบค่าของข้อมูลที่ชี้โดย first กับ Pivot ถ้าน้อยกว่า pivot ให้ทำการเลื่อน first ไปยังข้อมูลต่อไปและเปรียบเทียบไปจนกว่าจะพบ แล้วจึงหยุดการเปรียบเทียบ
เมื่อพบตำแหน่งแล้ว จะหันมาพิจารณาที่ last ชี้อยู่ 15 9 7 12 31 2 20 25 17 16 first last เมื่อพบตำแหน่งแล้ว จะหันมาพิจารณาที่ last ชี้อยู่ หากค่าที่ last ชี้อยู่มีค่าน้อยกว่าที่ first ชี้อยู่ให้สลับตำแหน่ง 15 9 7 12 16 2 20 25 17 31 first last
ย้อนกระบวนการไปพิจารณา first และ last จะได้ 15 9 7 12 16 2 20 25 17 31 first last สลับตำแหน่งข้อมูล 15 9 7 12 2 16 20 25 17 31 first last
ย้อนกระบวนการไปพิจารณา first และ last จะได้ 15 9 7 12 2 16 20 25 17 31 last first พบตำแหน่งที่จะใช้แบ่งชุดข้อมูลแล้ว จึงทำการแบ่งข้อมูล ออกเป็นสองชุด
ทำการแบ่งข้อมูลออกเป็นสองชุด 15 9 7 12 2 16 20 25 17 31 สลับค่าข้อมูลตัวแรกกับสุดท้ายของข้อมูลชุดซ้าย 2 9 7 12 15 16 20 25 17 31 จะได้ว่า 15 และ 16 อยู่ในตำแหน่งที่ถูกต้อง(ตน.5และ ตน.6) เรียบร้อยแล้วไม่ต้องนำมาพิจารณาอีก ดำเนินการกับข้อมูลที่เหลือเหมือนเดิมจนกว่าจะได้ข้อมูลในตำแหน่งต่างๆ จนครบ และดำเนินการแบบเดียวกับกับข้อมูลชุดขวามือ
พิจารณาชุดข้อมูลด้านซ้ายมือก่อน ข้อมูลเริ่มต้นชุดซ้าย ข้อมูลเริ่มต้นชุดขวา 2 9 7 12 20 25 17 31 first last first last pivot พิจารณาชุดข้อมูลด้านซ้ายมือก่อน
2 9 7 12 2 7 9 12 2 7 9 12 first last pivot first last pivot first last pivot First มากกว่า pivot พิจารณา last เลื่อนมาด้านซ้ายจนพบ 7 ซึ่งน้อยกว่า 9 จึงหยุด ทำการสลับตำแหน่งข้อมูล First มากกว่า pivot พิจารณา last เลื่อนมาด้านซ้ายจนมาชี้ที่ pivot พบว่าไม่มีข้อมูลใดต่อไปอีก ทำการแบ่งข้อมูลออกเป็นสองชุดแสดงว่า pivot อยู่ในตำแหน่งที่ถูกต้องแล้ว (2 อยู่ ตน. 1) 2 7 9 12
7 9 12 7 9 12 last last pivot first pivot first เปรียบเทียบ first กับ pivot พบว่า first มากกว่า พิจารณา last พบว่ามากกว่า first จึงเลื่อนมาด้านซ้ายจนมาชี้ที่ pivot และไม่มีตัวอื่นอีกจะได้ตำแหน่งในการแยก list
ดำเนินการกับข้อมูลชุดด้านขวามือต่อ ได้ว่า pivot (7) อยู่ในตำแหน่งที่ถูกต้องแล้ว 9 12 9 12 first pivot last pivot last first สรุปตำแหน่งข้อมูลที่ทราบแล้วคือตำแหน่งที่ 1 ถึง 5 ได้แก่ 2 7 9 12 และ 15 ตามลำดับ ดำเนินการกับข้อมูลชุดด้านขวามือต่อ
กระบวนการแยกชุดข้อมูล Merge Sort กระบวนการแยกชุดข้อมูล
กระบวนการรวมชุดข้อมูล ได้ชุดข้อมูลที่เรียงลำดับเรียบร้อยแล้ว
Merge Sort Algorithm Analysis จำนวนครั้งของการเปรียบเทียบทั้งหมด = ผลบวกทุกระดับของ ( จำนวนลิสต์ในแต่ละระดับ * จำนวนครั้งของการเปรียบเทียบแต่ละลิสต์ ) = (N-1) *1 + (N/2 -1)*2 + (N/4 – 1)*4 +..+(2-1)*N/2 = ( N-1) + (N-2)+(N-4)+..+(N-N/2) = Nlog2N จะได้ bigO(Nlog2N)