920 likes | 1.12k Views
อาร์เรย์ และ การจัดเรียงข้อมูล. a. 7. 6. 4. 5. 8. 9. a คือตัวแปรที่เป็นชื่อของอาร์เรย์นี้ ในภาษาจาวานั้น อาร์เรย์เป็น object ชนิดหนึ่ง เพราะฉะนั้น a ในที่นี้คือ object reference นั่นเอง พูดง่ายๆคือ a เป็นตัวลูกศรที่จะชี้ไปยังส่วนที่เก็บค่าต่างๆของอาร์เรย์นั่นเอง.
E N D
อาร์เรย์ และ การจัดเรียงข้อมูล a 7 6 4 5 8 9 a คือตัวแปรที่เป็นชื่อของอาร์เรย์นี้ ในภาษาจาวานั้นอาร์เรย์เป็น object ชนิดหนึ่งเพราะฉะนั้น a ในที่นี้คือ object reference นั่นเองพูดง่ายๆคือ a เป็นตัวลูกศรที่จะชี้ไปยังส่วนที่เก็บค่าต่างๆของอาร์เรย์นั่นเอง อ.ดร.วิษณุ โคตรจรัส
Index ของอาร์เรย์ • เราใช้ a[index] อ่านตำแหน่งต่างๆของอาร์เรย์จากรูปในหน้าที่แล้ว • a[0] จะหมายถึง 7 และ a[1] จะหมายถึง 6 • ค่า index จะเริ่มต้นที่ 0 (สำหรับภาษาจาวา) • ทุกค่า index จะต้องเป็นจำนวนเต็มเสมอรวมทั้งเป็น expression ที่คำนวณแล้วได้จำนวนเต็มก็ได้ • เช่นถ้า b=2 กับ c=3 แล้วa[b+c] ถือว่าเป็นการกล่าวถึงสมาชิกของอาร์เรย์ได้ถูกต้องตัว a[b+c] นี้ก็สามารถถูกนำไปใช้ได้เหมือนตัวแปรธรรมดาเช่น a[b+c] += 5; อ.ดร.วิษณุ โคตรจรัส
ขนาดของอาร์เรย์ • ในจาวาเองทุกๆอาร์เรย์จะมีตัวแปร length • ที่จะถูกตั้งค่าไว้โดยอัตโนมัติตอนที่อาร์เรย์ถูกสร้างขึ้น • เราสามารถใช้ a.length หาขนาดของอาร์เรย์ a ได้ทันที อ.ดร.วิษณุ โคตรจรัส
การสร้างอาร์เรย์ใน Java • ประกอบด้วยสองขั้นตอนใหญ่ๆคือ • Array Declaration: คือการนิยามตัวแปรอาร์เรย์ขึ้นมา • Array Allocation: คือการสร้างออบเจ็กต์ที่เป็นอาร์เรย์ขึ้นมาจริงๆแล้วใช้ assignment operator ทำให้ตัวแปรในข้อ 1 ชี้มายังตัวออบเจ็กต์นี้ อ.ดร.วิษณุ โคตรจรัส
การสร้างอาร์เรย์ใน Java (ต่อ) int x[]; //Declaration –นี่คือการนิยามตัวแปร x ให้เป็นอาร์เรย์ของ //จำนวนเต็มแต่ยังไม่มีออบเจ็กต์จริงๆเกิดขึ้น x=new int[5]; //Allocation –นี่คือการสร้างอาร์เรย์ออบเจ็กต์ขนาด5 ช่อง // เมื่อทำแล้วจะได้อาร์เรย์ที่มีค่าในแต่ละช่องเป็นค่า default //ของข้อมูลชนิดนั้นในที่นี้จะมี 0 อยู่ทั้ง 5 ช่องจากนั้นเอา // x ชี้ไปที่ตัวอาร์เรย์ จะเห็นว่าต้องมีการกำหนดจำนวนช่องให้แน่นอนตอนสร้าง อ.ดร.วิษณุ โคตรจรัส
การสร้างอาร์เรย์ใน Java โดยใช้ initializer list • เราสามารถสร้างอาร์เรย์ x ของจำนวนเต็ม 1 2 และ 3 ได้ด้วย int x[] = {1,2,3}; โดย {1,2,3}คือ initializer list นั่นเอง อ.ดร.วิษณุ โคตรจรัส
null null null การสร้างอาร์เรย์ของ Object • ถ้ามีออบเจ็กต์ type MyObject เราก็สร้างอาร์เรย์ขนาด 3 ช่องของมันได้ MyObject a[] = new MyObject[3]; • โดยตัวอาร์เรย์ที่ถูกสร้างจะมีค่าเริ่มต้นของสมาชิกเป็นnull a อ.ดร.วิษณุ โคตรจรัส
0 0 0 0 0 0 0 0 0 อาร์เรย์ของอาร์เรย์ • int x[][] = new int[3][3]; a อ.ดร.วิษณุ โคตรจรัส
x 5 6 1 2 3 4 อาร์เรย์ของอาร์เรย์ (ใช้ initializer list) • int x[] = {{1,2},{3,4},{5,6}}; อ.ดร.วิษณุ โคตรจรัส
Matrix • สำหรับอาร์เรย์ 2 ชั้นนั้นเราสามารถดูให้เป็นเมตริกซ์ได้โดยให้index ตัวแรกแทนแถวและตัวที่ 2 แทนหลัก • อาร์เรย์จากหน้าที่แล้วจึงสามารถมองได้เป็น อ.ดร.วิษณุ โคตรจรัส
การเว้นไม่ initialize ค่าในตอนสร้างอาร์เรย์ int y [][] = new int [2][]; • ตัว index ตัวหลังนั้นเราเว้นไว้ได้ • อาร์เรย์ในชั้นที่ 2 ก็จะเป็น null ไปก่อน • แต่อย่างไรก็ตามต้องมีการ initialize อาร์เรย์ชั้นนี้ให้เรียบร้อยก่อนที่จะมีการนำสมาชิกภายในไปใช้ • ในตัวอย่างนี้เราอาจทำการ initialize อาร์เรย์ย่อยได้ดังนี้ y[0] = new int[2]; y[1] = new int[3]; • จะเห็นได้ว่าอาร์เรย์ย่อยไม่จำเป็นต้องมีขนาดเท่ากัน อ.ดร.วิษณุ โคตรจรัส
5 2 4 3 1 Bubble Sort • เปรียบเทียบสองจำนวนแรกในอาร์เรย์ สลับที่สองจำนวนนี้ถ้าจำนวนขวามีค่าน้อยกว่าจำนวนซ้าย • ต่อจากนั้นก็มาเปรียบเทียบตัวเลขคู่ถัดมาในอาร์เรย์นั้น(ซึ่งตัวแรกอาจเคยถูกสลับที่มาแล้ว) แล้วทำการสลับที่ในกรณีเดียวกัน 2 5 4 3 1 • ทำไปเรื่อยๆจนถึงคู่สุดท้าย แล้วก็กลับมาเปรียบเทียบคู่แรกใหม่ ทำไปเรื่อยๆ อ.ดร.วิษณุ โคตรจรัส
Running time ของ Bubble Sort • การสลับที่ต้องมีจำนวนครั้งที่เพียงพอที่จะเคลื่อนค่าที่ • มากที่สุดจากช่องซ้ายสุดของอาร์เรย์ไปยังช่องขวาสุด • น้อยที่สุดจากช่องขวาสุดของอาร์เรย์ไปยังช่องซ้ายสุด n จำนวน 5 2 4 3 1 Big O = O(n2) • n-1 ครั้งแรกจะเพียงพอทำให้ค่าที่อยู่ช่องซ้ายสุด (5) เคลื่อนไปยังช่องขวาสุดได้แต่ข้อมูลในช่องขวาสุด (1) นั้นจะเลื่อนมาได้เพียงหนึ่งช่องเท่านั้น • ดังนั้นต้องทำซ้ำอย่างนี้ไปอีก n-1 รอบ อ.ดร.วิษณุ โคตรจรัส
1: public static void bubblesort(int[] array){ 2: for (int pass = 1; pass<=array.length-1; pass++) 3: for(int element=0; element<= array.length –2; element++) 4: if(array[element] > array[element+1]) 5: swap(array, element, element +1); 6: } เปรียบเทียบและสลับ 1 คู่ } 1: public static void swap(int[] array, int a, int b){ 2: int temp = array[a]; 3: array[a]= array[b]; 4: array[b]= temp; 5:} อ.ดร.วิษณุ โคตรจรัส
4 4 3 4 3 4 5 3 3 4 3 2 2 3 5 4 2 2 3 1 2 4 3 5 1 1 5 1 2 2 2 4 5 5 1 1 5 1 1 5 Worst case ของ Bubble Sort (อาร์เรย์เรียงจากมากไปน้อย) ไม่ต้องสลับ ลูปแรกมีการเปรียบเทียบn-1ครั้งและมีการสลับที่n-1ครั้ง ลูปที่2 มีการเปรียบเทียบn-1ครั้งเหมือนเดิมแต่มีการสลับที่แค่n-2 ครั้ง อ.ดร.วิษณุ โคตรจรัส
Worst case ของ Bubble Sort (ต่อ) • มีการเปรียบเทียบข้อมูลn-1 ครั้งในแต่ละลูปและโค้ดเรามีทั้งหมดn-1 ลูป • เพราะฉะนั้นจะมีการเปรียบเทียบข้อมูลในอาร์เรย์(n-1)2ครั้ง • มีการสลับที่ของข้อมูลในอาร์เรย์ (n-1) + (n-2) + (n-3) +…+ 1 = n(n-1)/2ครั้ง • bubble sort (worst case) = (n-1)2 + n(n-1)/2 * unit time ของการสลับที่ = (n-1)2 + n(n-1)/2 * 3 = (5n2– 7n +2) /2 อ.ดร.วิษณุ โคตรจรัส
Selection Sort • เก็บค่า index ของสมาชิกตัวแรกของอาร์เรย์ a เอาไว้ในตัวแปร maxindex • ตรวจสอบสมาชิกของอาร์เรย์ที่ละตัวตามลำดับถ้าเจอตัวที่มีค่ามากกว่า a[maxindex] ก็ให้เปลี่ยนค่า maxindex ให้เก็บ index ค่าใหม่ตรวจจนหมดอาร์เรย์ • สลับที่สมาชิกตัวสุดท้ายของอาร์เรย์กับ a[maxindex] (ถ้าสองตัวนี้ไม่ใช่ตัวเดียวกัน) เพื่อให้ค่าที่มากที่สุดไปอยู่ขวาสุด • ทำข้อ 1 – 3 ใหม่กับสมาชิก n-1 ตัวที่เหลือแล้วทำต่อไปโดยสมาชิกจะลดลงเรื่อยๆ อ.ดร.วิษณุ โคตรจรัส
maxindex =0 maxindex ยังคงเป็น 0 ตอนนี้ก็สลับที่a[maxindex] กับa[4] เริ่มใหม่ maxindex =0 maxindex กลายเป็น 1 แล้วสลับที่a[maxindex] กับa[3] 5 2 4 4 3 3 1 1 2 5 ตัวอย่าง Selection Sort เปรียบเทียบ n-1 ครั้ง สลับที่ 1 ครั้ง เปรียบเทียบ n-2 ครั้ง สลับที่ 1 ครั้ง อ.ดร.วิษณุ โคตรจรัส
Big O = O(n2) Selection Sort (Code) ลดขนาดอาร์เรย์ลงเรื่อยๆ public static void selectionSort(int[] a){ int maxindex; //index of the largest value for(int unsorted= a.length; unsorted > 1; unsorted--){ maxindex = 0; for(int index= 1; index < unsorted; index++){ if(array[maxindex] < array[index]) maxindex = index; } if(a[maxindex] != a[unsorted -1]) swap(array, maxindex, unsorted -1); } } ไล่ตรวจอาร์เรย์ 1 รอบเพื่อ update maxindex สลับที่ถ้าไม่ใช่ค่าเดียวกัน อ.ดร.วิษณุ โคตรจรัส
Selection Sort (worst case) • นับเวลาจากการเปรียบเทียบข้อมูลในอาร์เรย์และการ assign ข้อมูลเข้าหรือออกจากอาร์เรย์เท่านั้น • จะเกิดเมื่อมีการสลับที่ทุกรอบการใช้ลูป นั่นคือ ข้อมูลเกือบจะเรียงกัน แต่ตัวที่น้อยที่สุดอยู่ท้ายสุด เช่น 2,3,4,5,1 นับได้ดังนี้ • การเปรียบเทียบข้อมูลในอาร์เรย์ (n-1) + (n-2) + … + 1 ครั้ง • การเปรียบเทียบข้อมูลในอาร์เรย์เพื่อตัดสินการสลับที่ (n-1) ครั้ง • การสลับที่สมาชิกของอาร์เรย์ n-1 ครั้ง รวมได้ (n2 + 7n -8) /2 ซึ่งเร็วกว่า bubble sort เมื่อ n เป็น 3 ขึ้นไป อ.ดร.วิษณุ โคตรจรัส
Insertion Sort • แบ่งอาร์เรย์เป็นข้างซ้ายและขวา ให้ข้างซ้ายเป็นข้างที่ถือว่าจัดเรียงแล้ว (แน่นอนว่าในตอนเริ่มต้องมีจำนวนสมาชิกในข้างนี้แค่ตัวเดียว) • ตรวจข้างขวาของอาร์เรย์โดยไล่ตรวจที่ละตัว ถ้าเจอตัวที่มีค่าน้อยกว่าตัวสุดท้ายของข้างซ้าย ให้เอาตัวนั้นไปใส่ในข้างซ้ายให้ถูกที่ • ทำซ้ำข้อ 1 โดยขยายขนาดของข้างซ้ายเพิ่มขึ้น 1 ตัว ทำไปเรื่อยๆ อ.ดร.วิษณุ โคตรจรัส
6 5 5 6 3 3 7 7 4 4 ตัวอย่าง Insertion Sort ด้านซ้ายนี่ถือว่าเรียงแล้ว ต้องเอา 5 ไปใส่ข้างหน้า (หรือเลื่อน 6 มาแล้วค่อยเอา 5 ใส่แทน) เลื่อน 5 กับ 6 อย่างละ 1 ช่อง แล้วเอา 3 ใส่ช่องแรก อ.ดร.วิษณุ โคตรจรัส
3 3 5 5 6 6 7 7 4 4 ไม่ต้องเลื่อนเพราะอยู่ถูกที่ เลื่อน 5,6,7 แล้วเอา 4 ใส่ช่องหลัง 3 ก็จะเสร็จ ตัวอย่าง Insertion Sort (ต่อ) อ.ดร.วิษณุ โคตรจรัส
Big O = O(n2) 5 6 3 7 4 ตัวที่จะจับยัดเข้าด้านซ้าย public static void insertionSort(int[] a){ int index; for(int numSorted = 1; numSorted < a.length; numSorted++){ int temp = a[numSorted]; for(index = numSorted; index >0; index--){ if(temp< a[index-1]){ a[index] = a[index –1]; } else{ break; } } a[index] = temp; } } ไล่เปรียบเทียบกับ 6 และ 5 ตามลำดับ ทำให้ 6 เลื่อนขวา 1 ช่อง และ 5 ก็เลื่อนขวา 1 ช่อง พอไม่มีตัวไหนเลื่อนได้ก็ใส่ 3 ลงที่ที่ 5 เคยอยู่ อ.ดร.วิษณุ โคตรจรัส
Insertion Sort (worst case) • Worst case เกิดเมื่อต้องมีการเลื่อนสมาชิกของอาร์เรย์มากที่สุด • นั่นคืออาร์เรย์ต้องเรียงจากมากไปน้อย • แต่ละรอบ ทุกสมาชิกของข้างซ้ายของอาร์เรย์จะต้องถูกเลื่อนไป 1 ตำแหน่ง อ.ดร.วิษณุ โคตรจรัส
Insertion Sort (worst case ต่อ) • unit time ของ worst case insertion sort =(1+2+..+n-1)*2 + n-1 = n(n-1) + n-1 = (n+1)(n-1) = n2–1 เร็วกว่า selection sort เมื่อ n มีค่าไม่เกิน 6 เท่านั้น อ.ดร.วิษณุ โคตรจรัส
Insertion Sort (กรณีเฉลี่ย) ถ้าเราอยู่ที่ลูปนอกสุดลูปที่ i • ถ้า a[i] อยู่ในที่ที่ไม่ต้องมีการเลื่อนจะเกิดการเปรียบเทียบ temp< a[index-1] เพียงครั้งเดียวเท่านั้น • ถ้า a[i] อยู่ในที่ที่ต้องมีการเลื่อนตำแหน่งจะมีการเปรียบเทียบเกิดขึ้นได้ตั้งแต่หนึ่งครั้งจนถึง i ครั้ง • เช่นถ้า i มีค่าเป็น 2 จะต้องมีการเปรียบเทียบ a[2] กับ a[1] (นี่นับเป็นครั้งที่หนึ่ง) • และจากนั้นถ้าเกิดการเลื่อน a[1] ไปใส่ a[2] จะต้องเปรียบเทียบค่าเดิมของ a[2] กับ a[0] ด้วย อ.ดร.วิษณุ โคตรจรัส
Insertion Sort (กรณีเฉลี่ย ต่อ) • ฉะนั้นถ้าเราดูแต่จำนวนครั้งของการเปรียบเทียบจะได้ว่าในลูปที่ i มีจำนวนการเปรียบเทียบโดยเฉลี่ย ครั้ง • ดังนั้นถ้าคิดทุกลูปค่าจำนวนครั้งของการเปรียบเทียบที่เกิดขึ้นจะเป็น ครั้ง • ดังนั้นจึงสรุปได้ว่าค่าเวลาเฉลี่ยจะเข้าใกล้ค่าเวลาของworst case อ.ดร.วิษณุ โคตรจรัส
Merge Sort • แบ่งอาร์เรย์เป็นสองส่วนไปจัดเรียงข้อมูลในแต่ละส่วนให้เรียบร้อย • (แต่ละอาร์เรย์ก็แบ่งสองส่วนได้อีกทำเป็นrecursion ได้) • รวมอาร์เรย์ที่จัดเรียงเรียบร้อยเข้าด้วยกัน อ.ดร.วิษณุ โคตรจรัส
การรวมอาร์เรย์ใน merge sort (ขั้นที่ 1) • สมมติเราจะ รวมอาร์เรย์a (1,5,8,9) กับb (2,4,6,7) • ให้มีcounter อยู่ที่index แรกของอาร์เรย์ทั้งสองแล้วสร้างอาร์เรย์สำหรับเก็บผลการรวมขึ้นมา–ในที่นี้เราเรียกว่าอาร์เรย์ c b c a 1 5 8 9 2 4 6 7 indexB indexC indexA อ.ดร.วิษณุ โคตรจรัส
การรวมอาร์เรย์ใน merge sort (ขั้นที่ 2) • เปรียบเทียบ a[indexA] กับ b[indexB] เอาค่าที่น้อยกว่า ใส่ c[indexC] แล้วเลื่อน counter ของตัวที่น้อยนั้นและของ c เพื่อเตรียมเปรียบเทียบครั้งต่อไป b c a 1 5 8 9 2 4 6 7 1 indexB indexA indexC อ.ดร.วิษณุ โคตรจรัส
การรวมอาร์เรย์ใน merge sort (ขั้นที่ 3) • เปรียบเทียบ a[indexA] กับ b[indexB] แบบนี้ไปเรื่อยๆจนหมดหนึ่งอาร์เรย์ ในที่นี้ b จะหมดก่อน b c a 1 5 8 9 2 4 6 7 1 2 4 5 6 7 indexB indexC indexA • หลังจากนี้ก็แค่ก็อปปี้ส่วนที่เหลือของ a ใส่ c ให้หมด • ก็จะได้อาร์เรย์ผลลัพธ์ที่สมบูรณ์ อ.ดร.วิษณุ โคตรจรัส
Worst case ของการรวมอาร์เรย์ • Worst case ของการรวมอาร์เรย์นี้ เกิดเมื่อต้องมีการเปรียบเทียบสมาชิกอาร์เรย์ทั้งสอง(a กับ b) ไปจนถึงสมาชิกตัวสุดท้ายของทั้งคู่ • ซึ่งจะมีการเปรียบเทียบทั้งหมด n-1 ครั้ง (n คือขนาดของอาร์เรย์ผลลัพธ์) เพราะฉะนั้น เวลาในการทำงานของการรวมอาร์เรย์จึงเป็น O(n) อ.ดร.วิษณุ โคตรจรัส
โค้ดของการรวมอาร์เรย์โค้ดของการรวมอาร์เรย์ public static int[] merge(int[] a, int[] b){ int aIndex = 0; int bIndex = 0; int cIndex = 0; int aLength = a.length; int bLength = b.length; int cLength = aLength + bLength; int[] c = new int[cLength]; //ก่อนอื่นเปรียบเทียบ a กับ b แล้วเอาเลขออกใส่ c จนอาร์เรย์ตัวใดตัวหนึ่งหมด while((aIndex < aLength) && (bIndex < bLength){ if(a[aIndex]<=b[bIndex]){ c[cIndex] = a[aIndex]; aIndex++; }else{ c[cIndex] = b[bIndex]; bIndex++; } cIndex++; } มีต่อ อ.ดร.วิษณุ โคตรจรัส
โค้ดของการรวมอาร์เรย์(ต่อ)โค้ดของการรวมอาร์เรย์(ต่อ) //ต่อไปก็ก็อปปี้อาร์เรย์ที่เหลือใส่ c ให้หมด if(aIndex == aLength){ //ถ้า a ถูกใช้หมดก่อน while(bIndex<bLength){ c[cIndex] = b[bIndex]; bIndex++; cIndex++; } }else{ //ถ้า b ถูกใช้หมดก่อน while(aIndex<aLength){ c[cIndex] = a[aIndex]; aIndex++; cIndex++; } } return c; } อ.ดร.วิษณุ โคตรจรัส
การแบ่งอาร์เรย์เป็นส่วนๆการแบ่งอาร์เรย์เป็นส่วนๆ • ไม่ต้องมาจัดเรียงข้อมูลในตอนนี้เลยด้วยซ้ำ เพราะ • เมื่อเราแบ่งอาร์เรย์ไปเรื่อยๆ ผลสุดท้ายจะได้อาร์เรย์ที่มีขนาดเพียงหนึ่งมารวมกัน ตอนรวมอาร์เรย์ขนาดหนึ่งเข้าด้วยกันก็จะเป็นการจัดเรียงไปในตัวเลย • ฉะนั้น การรวมอาร์เรย์ที่ใหญ่ขึ้นมาก็จะมีการจัดเรียงอยู่แล้วโดยปริยาย อ.ดร.วิษณุ โคตรจรัส
โค้ดของการแบ่งอาร์เรย์เป็นส่วนๆโค้ดของการแบ่งอาร์เรย์เป็นส่วนๆ 1: public static int[] mergeSort(int[] unsort, int left, int right){ 2: if(left == right){//ถ้าไม่เหลืออะไรให้จัดแล้วต้องตอบเป็นอาร์เรย์ขนาด 1 3: int[] x = new int[1]; 4: x[0] = unsort[left]; 5: return x; 6: } 7: else if(left<right){//ถ้ายังจัดได้ก็แบ่งอาร์เรย์ต่อไป 8: int center = (left+right)/2; 9: int[] result1 = mergeSort(unsort,left,center); 10: int[] result2 = mergeSort(unsort,center+1,right); 11: return merge(result1,result2); 12: } 13: } อ.ดร.วิษณุ โคตรจรัส
เวลาในการทำงานของ merge sort • ถ้ามีสมาชิกตัวเดียวเราเห็นได้ชัดว่าเวลาที่ใช้นั้นคงที่ (ให้เป็น 1 ได้) • ส่วนถ้ามีสมาชิกหลายตัวเวลาที่ใช้จะเท่ากับเวลาที่ทำงานครึ่งซ้ายรวมกับครึ่งขวาแล้วรวมกับเวลาที่ใช้ในการรวมอาร์เรย์ทั้งสองอีกทีหนึ่ง (ซึ่งเป็นO(n)) อ.ดร.วิษณุ โคตรจรัส
เวลาในการทำงานของ merge sort (ต่อ) • เอา n หารตลอดเราจะได้ (1) • เราเอามาเขียนเป็นชุดของสมการ โดยเปลี่ยนค่า n ไปเรื่อยๆ จะได้สมการชุดในหน้าถัดไป อ.ดร.วิษณุ โคตรจรัส
เวลาในการทำงานของ merge sort (ต่อ 2) (2) (3) (x) อ.ดร.วิษณุ โคตรจรัส
เวลาในการทำงานของ merge sort (ต่อ 3) • เอาสมการตั้งแต่ (1) ถึง (x) มาบวกกัน จะได้ • จะเห็นว่าmerge sort นั้นมีเวลาเร็วกว่าการจัดเรียงข้อมูลแบบอื่นๆที่กล่าวมา • แต่การจัดเรียงข้อมูลด้วยวิธีนี้มีข้อเสียคือต้องใช้พื้นที่ในการสร้างอาร์เรย์ที่เป็นผลรวมของสองอาร์เรย์ย่อย อ.ดร.วิษณุ โคตรจรัส
Quick Sort 1. ถ้าไม่มีสมาชิกในอาร์เรย์หรือมีสมาชิกแค่ตัวเดียวให้ตอบเป็นอาร์เรย์นั้นเลยเพราะถือว่าแบบนี้ข้อมูลจัดเรียงในตัวเองอยู่แล้ว 2. เลือกตัวเลขใดตัวเลขหนึ่งในอาร์เรย์มาให้จำนวนนั้นเป็นจุดหลัก (ต่อไปนี้จะเรียกว่า pivot) 3. ใช้ pivot เป็นหลักเลื่อนสมาชิกทุกตัวที่น้อยกว่า pivot ไปอยู่ด้านซ้ายของ pivot และเลื่อนสมาชิกทุกตัวที่มากกว่า pivot ไปอยู่ด้านขวาของ pivot (ส่วนสมาชิกที่มีค่าเท่ากับpivot ก็แล้วแต่จะจัดการวิธีที่ดีที่สุดน่าจะเป็นการกระจายจำนวนเหล่านี้ไปยังทั้งสองข้างเท่าๆกัน) ขั้นตอนนี้เรียกว่าการ partition 4. ตอนนี้ตัว pivot ก็จะได้อยู่ในที่ที่ถูกต้องเรียบร้อยแล้วที่เราเหลือต้องทำก็คือทำquick sort กับอาร์เรย์ย่อยข้างซ้ายและข้างขวาของ pivot 5. คำตอบคืออาร์เรย์ที่เกิดจากการต่อ quicksort(ด้านซ้าย) กับ pivot กับ quicksort(ด้านขวา) อ.ดร.วิษณุ โคตรจรัส
3 3 5 1 6 4 1 5 6 4 ภาพหลักการทำ quick sort เลือกตัวนี้เป็น pivot ต่อไป แบ่งข้อมูลเป็นสองข้างของ 4 เอาไป quick sort เอาไป quick sort เอาผลมาต่อกัน จะได้คำตอบ อ.ดร.วิษณุ โคตรจรัส
ขั้นที่ 1: เมื่ออาร์เรย์มีขนาดเล็ก • ถ้าไม่มีสมาชิกในอาร์เรย์หรือมีสมาชิกแค่ตัวเดียวให้ตอบเป็นอาร์เรย์นั้นเลยเพราะถือว่าแบบนี้ข้อมูลจัดเรียงในตัวเองอยู่แล้ว • แต่เราจะเห็นว่าจริงๆแล้วถ้าอาร์เรย์มีจำนวนสมาชิกน้อย(น้อยกว่า 20) ใช้ insertion sort จะเร็วกว่าซะอีกดังนั้นถ้าอาร์เรย์มีขนาดเล็กเราควรใช้วิธีจัดเรียงแบบอื่นแล้วค่อยเอาคำตอบจากวิธีนั้นมา อ.ดร.วิษณุ โคตรจรัส
ขั้นที่ 2: การเลือก pivot • ห้ามใช้ตัวแรกของอาร์เรย์เป็นpivot เด็ดขาด • เพราะว่าถ้าอาร์เรย์นั้นจัดเรียงอยู่แล้ว(ไม่ว่าจะมากไปน้อยหรือน้อยไปมากก็ตาม) ตอนที่แบ่งข้างโดยใช้จุดหลักนี้จะพบว่าข้างหนึ่งเป็นอาร์เรย์ว่างเสมอทำให้ไร้ประสิทธิภาพ อ.ดร.วิษณุ โคตรจรัส
5 4 4 3 3 2 2 1 1 5 การเลือก pivot ที่ไม่ดี (ใช้ตัวแรกของสมาชิกอาร์เรย์) pivot ไม่มีด้านขวา สมาชิกจะไปกองอยู่ข้างเดียวหมด นั้นคือแทนที่จะลดขนาดของปัญหาลงได้ครึ่งหนึ่ง กลับลดลงได้แค่หนึ่งหน่วย อ.ดร.วิษณุ โคตรจรัส
การเลือก pivot ที่ดี • เลือกpivotโดยใช้random number ส่วนใหญ่แล้วจะได้การแบ่งpartition สองข้างที่ดี • แต่ว่าวิธีนี้มีข้อเสียที่ว่าการสร้างrandom number นั้นเสียเวลาในตัวของมันเอง • ใช้ค่ามัธยฐานจากอาร์เรย์ช่องแรกกับช่องกลางและช่องสุดท้าย • จริงๆแล้วค่าpivot ที่จะทำให้แบ่งอาร์เรย์ได้เป็นสองข้างเท่ากันนั้นจะต้องเป็นค่ามัธยฐานจากทั้งอาร์เรย์ • แต่จะมานั่งคิดค่านี้มันก็จะเสียเวลาเกินไปดังนั้นจึงได้มีการลองใช้ค่ามัธยฐานจากอาร์เรย์ช่องแรกกับช่องกลางและช่องสุดท้ายพบว่าได้ผลดีเช่นกัน อ.ดร.วิษณุ โคตรจรัส
โค้ดการเลือก pivot โดยหา median of 3 1: private static int pivotIndex(int[] a, int l, int r){ 2: int c = (l+r)/2; 3: if((a[l]<=a[r] && a[l]>=a[c]) || 4: (a[l]>=a[r] && a[l]<=a[c])) 5: return l; 6: if((a[c]<=a[l] && a[c]>=a[r]) || 7: (a[c]>=a[l] && a[c]<=a[r]) 8: return c; 9: return r; 10: } อ.ดร.วิษณุ โคตรจรัส
ขั้นที่ 3: การแบ่ง partition • เอา pivot ที่เลือกได้แล้วออกไปให้พ้นทางโดยสลับที่กับสมาชิกตัวสุดท้ายของอาร์เรย์ • ให้ตัวแปร i เป็น index ของตำแหน่งแรกของอาร์เรย์และตัวแปร j เป็น index ของตำแหน่งรองสุดท้ายของอาร์เรย์ (อย่าลืมว่าตอนนี้เราใช้ตำแหน่งสุดท้ายเก็บ pivot ไปแล้ว) • ดูตำแหน่งของ i กับ jในอาร์เรย์เป็นหลักให้เลื่อน i ไปทางขวาเรื่อยๆข้ามสมาชิกในอาร์เรย์ที่มีค่าน้อยกว่าค่าของ pivot แต่ต้องหยุดเมื่อเจอสมาชิกในอาร์เรย์ที่มีค่ามากกว่าหรือเท่ากับค่าของ pivot • ในทำนองเดียวกันให้เลื่อน j ไปทางซ้ายเรื่อยๆข้ามสมาชิกที่มีค่ามากกว่าค่าของ pivot และหยุดเมื่อเจอสมาชิกในอาร์เรย์ที่มีค่าน้อยกว่าหรือเท่ากับค่าของ pivot อ.ดร.วิษณุ โคตรจรัส
ถ้า i ยังอยู่ด้านซ้ายของ j อยู่ก็สลับที่สมาชิกในอาร์เรย์สองตำแหน่งนั้นเสียนี่เป็นการเอาค่าน้อย(เมื่อเทียบกับ pivot)ไปไว้ข้างซ้ายและค่ามากไปไว้ด้านขวาของอาร์เรย์ส่วนกรณีถ้า i ไม่อยู่ทางด้านซ้ายของ j ให้ข้ามไปทำข้อ 8 • เลื่อน i ไปทางขวาหนึ่งหน่วยและเลื่อน j ไปทางซ้ายหนึ่งหน่วยเพื่อหลบเลี่ยงช่องที่พึ่งมีการสลับที่สมาชิกไป • จากนั้นเริ่มทำจากข้อ 3 อีกครั้งหนึ่ง • ถ้ามาถึงขั้นตอนนี้ให้สลับที่สมาชิกที่อยู่ที่ตำแหน่ง i กับ pivot ก็จะได้อาร์เรย์ที่มี pivot อยู่ในตำแหน่งที่ถูกต้องด้านซ้ายจะมีสมาชิกที่มีค่าน้อยกว่าส่วนด้านขวาจะมีสมาชิกที่มีค่ามากกว่า อ.ดร.วิษณุ โคตรจรัส