void IntList_insert(IntList *L, int i, int x)

Download Report

Transcript void IntList_insert(IntList *L, int i, int x)

418115: การโปรแกรมเชิงโครงสร้าง
โครงสร้างข้อมูล 1
ประมุข ขันเงิน
Abstract Data Type
Abstract Data Type
 ชนิดข้อมู ลนามธรรม (Abstract Data Type ย่อว่า ADT) ประกอบด้ วย
 กลุ่มของข้ อมูล
 วิธกี ารจัดการกับกลุ่มของข้ อมูลนี้
 เน้ นว่าเราสามารถทาอะไรได้กบั กลุ่มข้อมู ลได้ บ้าง
 ไม่ได้ เน้ นว่าเราจะเขียนโปรแกรมเพื่อให้ จัดการข้ อมูลนี้ได้ อย่างไร
ตัวอย่าง: เซต
 สัญลักษณ์: S
 ข้ อมูลที่เก็บ:
 ของชนิดเดียวกัน (int, float, char, ฯลฯ) หลายๆ ตัว
 ข้ อมูลที่แตกต่างกันมีได้ เพียงตัวเดียว
 ตัวอย่าง: {1, 2, 5}
 ความสามารถ:
 add(S, x): เพิ่ม x เข้ าใน S
 remove(S, x): ลบ x ออกจาก S
 contains(S, x): ถามว่า x เป็ นสมาชิกของ S หรือไม่?
ตัวอย่าง: เซต
 สมมติเราสนใจเซต S = {1,2,3}
 contains(S, 3)  true
 contains(S, 4)  false
 add( S, 4)
 ได้ S = {1,2,3,4}
 add(S, 5)
 ได้ S = {1,2,3,4,5}
 remove(S, 2)
 ได้ S = {1,3,4,5}
 contains(S, 2)  false
ตัวอย่าง: ลิสต์ (List)
 ใช้ แทนลาดับ (a0, a1, a2, …, an-1) ของข้ อมูล
 สมาชิกลาดับสามารถซา้ กันได้
 ความสามารถ:
 get(L, i): คืนสมาชิกตัวที่ i
 set(L, i, x): ทาให้ สมาชิกตัวที่ i กลายเป็ น x
 find(L, x): หาตาแหน่งของ x ใน L ถ้ าไม่เจอคืน -1 ถ้ ามีหลายตัวคืนตัวหน้ าสุด
 insert(L, i, x): เพิ่ม x เข้ าไปใน i โดยทาให้ มันเป็ นสมาชิกตัวที่ i หลังจากแทน
เสร็จแล้ ว
 remove(i): ลบสมาชิกตัวที่ i ออก
ตัวอย่าง: ลิสต์ (List)
 สมมติว่า L = (7,2,1,3,9)
 get(L, 0)  7 และ get(L, 4)  9
 set(L, 1, 10)
 ได้ L = (7, 10, 1, 3, 9)
 insert(L, 2, 9)
 ได้ L = (7, 10, 9, 1, 3, 9)
 remove(L, 4)
 ได้ L = (7, 10, 9, 1, 9)
 find(L, 10)  1 และ find(L, 9)  2 และ find(L, 2)  -1
ตัวอย่าง: สแตก (Stack)
 ความสามารถ
 push(S, x): ใส่ข้อมูลเข้ าไปวางไว้ บน “หัว” ของ stack
 pop(S): เอาเข้ ามูลที่อยู่ท่ี “หัว” ออกมา
 จาได้ ไหม?
 stack เป็ นหน่วยความจาชนิดหนึ่ง (stack segment)
 มีวิธกี ารจัดการข้ อมูลเหมือนกับ stack เลย
ตัวอย่าง: สแตก (Stack)
 สมมติตอนแรก S = ()  stack ว่าง
 push(S, 5)
 ได้ S = (5)
 push(S, 3)
 ได้ S = (5, 3)
 push(S, 7)
 ได้ S = (5, 3, 7)
 pop(S)  7
 ได้ S = (5, 3)
 push(S, 20)
 ได้ S = (5, 3, 20)
ตัวอย่าง: คิว (Queue)
 ความสามารถ
 enqueue(Q, x): ใส่ข้อมูลเข้ าไปวางไว้ บน “ท้ าย” ของ queue
 dequeue(Q): เอาเข้ ามูลที่อยู่ท่ี “หัว” queue ออกมา
 พบกันทั่วไปในชิวต
ิ ประจาวัน
 แต่ไร้ คุณค่าทางคณิตศาสตร์
ตัวอย่าง: คิว (Queue)
 สมมติ Q = ()  คิวว่าง
 enqueue(Q, 5)
 Q = (5)
 enqueue(Q, 3)
 Q = (5, 3)
 enqueue(Q, 7)
 Q = (5, 3, 7)
 dequeue(Q)  5
 Q = (3, 7)
 enqueue(Q, 20)
 Q = (3, 7, 20)
 dequeue(Q)  3
 Q = (7, 20)
ทาไมเราต้องพู ดถึง ADT?
 เรามักจะเจอรูปแบบ (pattern) ในการจัดการข้ อมูล
 รูปแบบที่เจอบ่อยๆ เราจะสรุปมาเป็ น ADT
 ประโยชน์
 มีคาศัพท์เวลาเอาไปใช้ คุยกับคนอื่น
 เวลาอธิบายอัลกอริทมึ จะได้ อธิบายสั้นๆ
 เปิ ดโอกาสให้ เราเขียนโปรแกรมได้ หลายๆ แบบ
 เพราะ ADT ไม่พูดถึงวิธกี ารเขียนโปแกรม
 เขียนยังไงก็ได้ ให้ มันทางานได้ ตามนั้นพอ
โครงสร้างข้อมูล (Data Structure)
 โครงสร้างข้อมู ล คือ วิธกี ารเขียนโปรแกรมให้ สอดคล้ องกับความต้ องการของ
ADT
 เวลาพูดถึงโครงสร้ างข้ อมูล เราจะพูดถึง
 วิธกี ารก็บข้ อมูลในหน่วยความจา
 วิธกี ารทาปฏิบัติการต่างๆ ของ ADT
โครงสร้างข้อมูลและ ADT
 ADT หนึ่งมีโครงสร้ างข้ อมูลหลายตัวที่ทางานเป็ นมันได้
 โครงสร้ างข้ อมูลหนึ่งก็สามารถทางานเป็ น ADT หลายตัวได้
 โครงสร้ างข้ อมูลแต่ละตัวมีความแตกต่างกัน
 ความเร็วของปฏิบัติการต่างๆ
 ขนาดหน่วยความจาที่ใช้
อะเรย์และอะเรย์ปรับขนาดได้
อะเรย์
 อะเรย์ คือ ข้ อมูลที่ถูกเรียงติดกันเป็ นพืดๆ ในหน่วยความจา
 ในภาษา C เราเข้ าถึงสมาชิกตัวที่ i ของอะเรย์ A ด้ วยนิพจน์ A[i]
 การเข้ าถึง A[i] ทาได้ อย่างรวดเร็ว (เสียเวลาหนึ่งหน่วย)
 ขนาดของอะเรย์ในภาษา C มักจะถูกกาหนดไว้ ตายตัว
อะเรย์ทปี่ รับขนาดได้
 อะเรย์ท่เี ราใช้ ท่ผี ่านมาต้ องกาหนดขนาดล่วงหน้ าไว้ ใน code
 ไม่สามารถเปลี่ยนแปลงขนาดได้ ตอนโปรแกรมรันไปแล้ ว
 ด้ วย dynamic memory allocation เราสามารถสร้ างอะเรย์ท่เี ราสามารถยืด
หรือหดขนาดมันได้
 อย่างไรก็ดกี ารเข้ าถึงสมาชิกต่างๆ ในอะเรย์ เราจะทาผ่านฟังก์ชัน
 เข้ าถึงตรงๆ ก็ได้ แต่ไม่ปลอดภัย
IntArray
 เราจะสร้ างอะเรย์สาหรับเก็บค่าประเภท int ที่เราสามารถกาหนดขนาดเองเวลา
โปรแกรมรัน
 อะเรย์น้ เี ก็บอยู่ใน struct ต่อไปนี้
typedef struct {
int capacity;
int *data;
} IntArray;
IntArray
 data คือ pointer ไปยังสมาชิกตัวแรกในอะเรย์
 capacity คือ ขนาดของอะเรย์ท่จี องไว้
 ข้ อมูลของอะเรย์จะอยู่ท่ี data[0], data[1], …, data[capacity-1]
IntArray Functions
 void IntArray_alloc(IntArray *a, int capacity)
 ทาให้ อะเรย์มีขนาดเท่ากับ capacity
 int IntArray_get(IntArray *a, int i)
 คืนสมาชิกตัวที่ i ของอะเรย์ a
 void IntArray_set(IntArray *a, int i, int x)
 ทาให้ สมาชิกตัวที่ i ของอะเรย์ a มีค่าเท่ากับ x
 void IntArray_clear(IntArray *a)
 ยกเลิกการจองพื้นที่ใน heap ของอะเรย์
 void IntArray_init(IntArray *a)
 ตั้งแต่เริ่มต้ นให้ อะเรย์ a กล่าวคือเซตค่า size = capacity = 0 และ data =
NULL
IntArray_init
void IntArray_init(IntArray *a)
{
a->size = 0;
a->capacity = 0;
a->data = NULL;
}
IntArray_get
int IntArray_get(IntArray *a, int i)
{
return a->data[i];
}
IntArray_set
void IntArray_set(IntArray *a, int i, int x)
{
a->data[i] = x;
}
IntArray_alloc
void IntArray_alloc(IntArray *a, int capacity)
{
if (capacity == 0) {
if (a->data != NULL) {
free(a->data);
a->data = NULL;
}
a->capacity = 0;
}
else if (capacity > 0) {
int *new_data;
if (a->data != NULL)
new_data = (int *)realloc(a->data,
sizeof(int)*capacity);
else
new_data = (int *)malloc(sizeof(int) * capacity);
if (new_data != NULL)
a->data = new_data;
else
exit(1);
a->capacity = capacity;
}
}
IntArray_set
void IntArray_clear(IntArray *a)
{
IntArray_alloc(a, 0);
}
ตัวอย่างการใช้งาน
int main()
{
IntArray A;
IntArray_alloc(&A, 3);
IntArray_set(&A, 1, 5);
IntArray_set(&A, 1, 3);
IntArray_set(&A, 1, 7);
printf("A[1] = %d\n", IntArray_get(&A, 1));
IntArray_set(&A, 1, 10);
printf("A[1] = %d\n", IntArray_get(&A, 1));
IntArray_clear(&A);
return 0;
}
ใช้อะเรย์สร้างลิสต์
ทวนความจา: ลิสต์
 ใช้ แทนลาดับ (a0, a1, a2, …, an-1) ของข้ อมูล
 สมาชิกลาดับสามารถซา้ กันได้
 ความสามารถ:
 get(L, i): คืนสมาชิกตัวที่ i
 set(L, i, x): ทาให้ สมาชิกตัวที่ i กลายเป็ น x
 find(L, x): หาตาแหน่งของ x ใน L ถ้ าไม่เจอคืน -1 ถ้ ามีหลายตัวคืนตัวหน้ าสุด
 insert(L, i, x): เพิ่ม x เข้ าไปใน i โดยทาให้ มันเป็ นสมาชิกตัวที่ i หลังจากแทน
เสร็จแล้ ว
 remove(i): ลบสมาชิกตัวที่ i ออก
ในภาษา C
 ต้ องการ user-defined data type ชื่อ IntList สาหรับเก็บ int
 ต้ องการฟังก์ชันต่อไปนี้
 int IntList_get(IntList *L, int i)
 void IntList_set(IntList *L, int i, int x)
 int IntList_find(IntList *L, int x)
 void IntList_insert(IntList *L, int i, int x)
 void IntList_remove(IntList *L, int i)
 void IntList_clear(IntList *L)
 ลบสมาชิกทั้งหมดและคืนหน่วยความจาที่จองไว้ ให้ ระบบ
 void IntList_init(IntList *L)
 ตั้งค่าเริ่มต้ นให้ กบ
ั ลิสต์
ข้อมูลแบบ IntList





ข้ างในมีอะเรย์ยืดหดได้ อยู่หนึ่งตัว (แต่เราจะยืดอย่างเดียว)
มีฟีลด์ size เพื่อบอกว่าตอนนี้ array มีสมาชิกกี่ตัว
เวลาทางาน เราจะทาให้ capacity ของอะเรย์ไม่น้อยกว่า size เสมอ
capacity = จานวนสมาชิกที่สามารถเก็บได้
size = สมาชิกที่เก็บไว้ จริง
typedef struct
{
IntArray arr;
int size;
} IntList;
กฎการเก็บข้อมูล (Representation Invariant)
 เทคนิคหนึ่งที่ใช้ ในการเขียนโครงสร้ างข้ อมูล
 กาหนดข้ อความหนึ่งที่จะเป็ นจริงตลอดชีวต
ิ การทางานของโครงสร้ างข้ อมูล
 แล้ วใช้ ข้อความนี้เป็ นไกด์ในการเขียนฟังก์ชันต่างๆ
 ในที่น้ เี ราจะตั้งกฎสองกฎ
 ฟี ลด์ size มีค่าเท่ากับจานวนสมาชิกในลิสต์
 สมาชิกตัวที่ i ในอะเรย์ arr คือสมาชิกตัวที่ i ของลิสต์
IntList_get
 get(L, i): คืนสมาชิกตัวที่ i
 กฎ: สมาชิกตัวที่ i ในอะเรย์ arr คือสมาชิกตัวที่ i ของลิสต์
 ใช้ IntArray_get ทางาน
int IntList_get(IntList *L, int i)
{
return IntArray_get(&L->arr, i);
}
IntList_set
 set(L, i, x): ทาให้ สมาชิกตัวที่ i กลายเป็ น x
 กฎ: สมาชิกตัวที่ i ในอะเรย์ arr คือสมาชิกตัวที่ i ของลิสต์
 ก็ใช้ IntArray_set ทางานเช่นกัน
void IntList_set(IntList *L, int i, int x)
{
IntArray_set(&L->arr, i, x);
}
IntList_find
 find(L, x): หาตาแหน่งของ x ใน L ถ้ าไม่เจอคืน -1 ถ้ ามีหลายตัวคืนตัวหน้ าสุด
 วนดูสมาชิกข้ างในอะเรย์ทลี ะตัว แล้ วตอบตาแหน่งของตัวแรกที่เจอ
int IntList_find(IntList *L, int x)
{
int i;
for(i = 0; i < L->size; i++)
if (x == IntArray_get(&L->arr, i))
return i;
return -1;
}
IntList_insert
 insert(L, i, x): เพิ่ม x เข้ าไปใน i โดยทาให้ มันเป็ นสมาชิกตัวที่ i หลังจากแทนเสร็จ
แล้ ว
 กฎ:
 ฟี ลด์ size มีค่าเท่ากับจานวนสมาชิกในลิสต์
 สมาชิกตัวที่ i ในอะเรย์ arr คือสมาชิกตัวที่ i ของลิสต์
Before & After
IntList
IntArray
L=
size = 5
arr =
capacity = 7
data=
5
3
7
10
9
??? ???
IntList_insert(&L, 2, 4)
IntList
IntArray
L=
size = 6
arr =
capacity = 7
data=
5
3
4
7
10
9
???
Before & After
IntList
IntArray
L=
size = 5
arr =
capacity = 7
data=
5
3
7
10
9
??? ???
IntList_insert(&L, 2, 4)
IntList
IntArray
L=
size = 6
arr =
capacity = 7
data=
5
3
4
7
10
9
???
Before & After
 จากกฎ เราต้ อง
 เลื่อนข้ อมูลที่อยู่ทางด้ านขวาของช่อง i ไปทางขวาหนึ่งช่อง
 เอาค่าใหม่ไปใส่ช่อง i
 เพิ่ม size ขึ้น 1
IntList
IntArray
L=
size = 5
arr =
capacity = 7
data=
5
3
7
10
9
data=
5
3
4
7
10
??? ???
IntList
IntArray
L=
size = 6
arr =
capacity = 7
9
???
กรณีอะเรย์เต็ม
IntList
IntArray
L=
size = 3
arr =
capacity = 3
data=
5
3
IntList_insert(&L, 0, 11)
???
7
กรณีอะเรย์เต็ม
 เราต้ องเอาเลข 7 ไปใส่ช่องที่ 3
 แต่มน
ั ไม่มชี ่องที่ 3
 ฉะนั้นเราต้ องยึดอะเรย์
 แต่จะยืดเท่าไหร่ ด?ี
กรณีอะเรย์เต็ม
 ยืดเพิ่ม 1 ช่อง
 หมายความว่าเพิ่มแต่ละครั้งต้ องยืดหนึ่งครั้ง
 แต่การยืดอะเรย์แต่ละครั้งจะมีการจองหน่วยความจาใหม่
 การจองหน่วยความจาใหม่เป็ นปฏิบัติการที่ช้า
 ยืดเพิ่ม 2 เท่าของขนาดเดิม
 จองให้ capacity ใหม่เท่ากับ size*2
 ไม่ต้องจองหน่วยความจาบ่อยมาก
 ถ้ าข้ อมูลใหญ่ นานๆ จะจองครั้งหนึ่ง  เร็ว
 แต่เปลืองหน่วยความจามากถึง 2 เท่า
 ปัญหา: ถ้ า size = 0 มันจะทาให้ capacity ใหม่เท่ากับ 0
 แทนที่จะมีช่องให้ เก็บข้ อมูลกลับไม่มี
กรณีอะเรย์เต็ม
 ยืดเป็ น 2*size+1
 ใช้ หน่วยความจาพอๆ กับการยืดสองเท่า
 แต่ไม่มีปัญหากรณี size = 0
 ถ้ า size = 0 จะจองให้ capacity = 1
 ถ้ า size = 1 จะจองให้ capacity = 3
 ถ้ า size = 3 จะจองให้ capacity = 7
 ถ้ า size = 7 จะจองให้ capacity = 15
 เช่นนี้ไปเรื่อยๆ
กรณีอะเรย์เต็ม
IntList
IntArray
L=
size = 3
arr =
capacity = 3
data=
5
3
7
IntList_insert(&L, 0, 11)
IntList
IntArray
L=
size = 4
arr =
capacity = 7
data= 11
5
3
7
??? ??? ???
IntList_insert
void IntList_insert(IntList *L, int i, int x)
{
int k;
if (i < 0 || i > L->size)
{
fprintf(stderr, "index out of range!");
return;
}
if (L->size + 1 > L->arr.capacity)
IntArray_alloc(&L->arr, (L->size) * 2 + 1);
for(k = L->size; k > i; k--)
IntArray_set(&L->arr, k, IntArray_get(&L->arr, k-1));
IntArray_set(&L->arr, i, x);
L->size++;
}
IntList_insert
void IntList_insert(IntList *L, int i, int x)
{
int k;
if (i < 0 || i > L->size)
{
fprintf(stderr, "index out of range!");
return;
}
เช็คว่า i อยู่ในช่วงที่
ถูกต้ องหรือไม่
if (L->size + 1 > L->arr.capacity)
IntArray_alloc(&L->arr, (L->size) * 2 + 1);
for(k = L->size; k > i; k--)
IntArray_set(&L->arr, k, IntArray_get(&L->arr, k-1));
IntArray_set(&L->arr, i, x);
L->size++;
}
IntList_insert
void IntList_insert(IntList *L, int i, int x)
{
int k;
if (i < 0 || i > L->size)
{
fprintf(stderr, "index out of range!");
return;
}
if (L->size + 1 > L->arr.capacity)
IntArray_alloc(&L->arr, (L->size) * 2 + 1);
ยืดอะเรย์ถ้าเต็ม
for(k = L->size; k > i; k--)
IntArray_set(&L->arr, k, IntArray_get(&L->arr, k-1));
IntArray_set(&L->arr, i, x);
L->size++;
}
IntList_insert
void IntList_insert(IntList *L, int i, int x)
{
int k;
if (i < 0 || i > L->size)
{
fprintf(stderr, "index out of range!");
return;
}
if (L->size + 1 > L->arr.capacity)
IntArray_alloc(&L->arr, (L->size) * 2 + 1);
for(k = L->size; k > i; k--)
IntArray_set(&L->arr, k, IntArray_get(&L->arr, k-1));
IntArray_set(&L->arr, i, x);
L->size++;
}
เลื่อนสมาชิก
IntList_insert
void IntList_insert(IntList *L, int i, int x)
{
int k;
if (i < 0 || i > L->size)
{
fprintf(stderr, "index out of range!");
return;
}
if (L->size + 1 > L->arr.capacity)
IntArray_alloc(&L->arr, (L->size) * 2 + 1);
for(k = L->size; k > i; k--)
IntArray_set(&L->arr, k, IntArray_get(&L->arr, k-1));
IntArray_set(&L->arr, i, x);
L->size++;
}
เอาค่าใหม่แทรก
แล้ วเพิ่ม size
IntList_remove
 remove(i): ลบสมาชิกตัวที่ i ออก
 กฎ:
 ฟี ลด์ size มีค่าเท่ากับจานวนสมาชิกในลิสต์
 สมาชิกตัวที่ i ในอะเรย์ arr คือสมาชิกตัวที่ i ของลิสต์
Before & After
IntList
IntArray
L=
size = 5
arr =
capacity = 7
data=
5
3
7
10
9
??? ???
IntList_remove (&L, 1)
IntList
IntArray
L=
size = 4
arr =
capacity = 7
data=
5
7
9
10 ??? ??? ???
Before & After
 จากกฎ เราต้ อง
 เลื่อนข้ อมูลที่อยู่ทางด้ านขวาของช่อง i ไปทางซ้ ายหนึ่งช่อง
 ลด size ลง 1
IntList
IntArray
L=
size = 5
arr =
capacity = 7
data=
5
3
7
10
data=
5
7
10
9
9
??? ???
IntList
IntArray
L=
size = 6
arr =
capacity = 7
??? ??? ???
IntList_remove
void IntList_remove(IntList *L, int i)
{
int k;
if (i < 0 || i > L->size-1)
{
fprintf(stderr, "index out of range!");
return;
}
for(k = i; k < L->size-1; k++)
IntArray_set(&L->arr, k, IntArray_get(&L->arr, k+1));
L->size--;
}
IntList_remove
void IntList_remove(IntList *L, int i)
{
int k;
เช็คว่า i อยู่ในช่วงที่
ถูกต้ องหรือไม่
if (i < 0 || i > L->size-1)
{
fprintf(stderr, "index out of range!");
return;
}
for(k = i; k < L->size-1; k++)
IntArray_set(&L->arr, k, IntArray_get(&L->arr, k+1));
L->size--;
}
IntList_remove
void IntList_remove(IntList *L, int i)
{
int k;
if (i < 0 || i > L->size-1)
{
fprintf(stderr, "index out of range!");
return;
}
เลื่อนสมาชิก
for(k = i; k < L->size-1; k++)
IntArray_set(&L->arr, k, IntArray_get(&L->arr, k+1));
L->size--;
}
IntList_remove
void IntList_remove(IntList *L, int i)
{
int k;
if (i < 0 || i > L->size-1)
{
fprintf(stderr, "index out of range!");
return;
}
for(k = i; k < L->size-1; k++)
IntArray_set(&L->arr, k, IntArray_get(&L->arr, k+1));
L->size--;
}
ลดค่า size
IntList_clear
 ลบสมาชิกทั้งหมดและคืนหน่วยความจาที่จองไว้ ให้ ระบบ
 ใช้ IntArray_clear
void IntList_clear(IntList *L)
{
IntArray_clear(&L->arr);
L->size = 0;
}
IntList_init
 ตั้งค่าเริ่มต้ นให้ กบ
ั ลิสต์
 ตอนแรกลิสต์ยังไม่มีสมาชิก ฉะนั้น size = 0
 และต้ องเรียก IntArray_init
void IntList_init(IntList *L)
{
L->size = 0;
IntArray_init(&L->arr);
}
ตัวอย่างการใช้งาน
int main()
{
IntList L;
IntList_init(&L);
IntList_insert(&L, 0, 10);
printf("%d\n", IntList_get(&L, 0));
IntList_insert(&L, 0, 5);
printf("%d %d\n", IntList_get(&L, 0), IntList_get(&L, 1));
IntList_insert(&L, 1, 3);
printf("%d %d %d\n", IntList_get(&L, 0),
IntList_get(&L, 1), IntList_get(&L, 2));
IntList_remove(&L, 0);
printf("%d %d\n", IntList_get(&L, 0), IntList_get(&L, 1));
return 0;
}
ใช้ลสิ ต์สร้างสแตก
ทวนความจา: สแตก
 ความสามารถ
 push(S, x): ใส่ข้อมูลเข้ าไปวางไว้ บน “หัว” ของ stack
 pop(S): เอาเข้ ามูลที่อยู่ท่ี “หัว” ออกมา
ในภาษา C
 ต้ องการ user-defined type IntStack
 ต้ องการฟังก์ชันต่อไปนี้
 void IntStack_push(IntStack *S, int x)
 int IntStack_pop(IntStack *S)
 void IntStack_clear(IntStack *S)
 ลบสมาชิกทั้งหมดและคืนหน่วยความจาที่จองไว้ ให้ ระบบ
 void IntStack_init(IntStack *S)
 ตั้งค่าเริ่มต้ นให้ กบ
ั สแตก
IntStack
 สแตกคล้ ายๆ กับลิสต์ แต่ทาอะไรได้ น้อยกว่า
 ฉะนั้นเราน่าจะสามารถเอาลิสต์มาทาสแตกได้
typedef struct
{
IntList list;
} IntStack;
 clear และ init จะเรียก IntList_clear และ IntList_init เท่านั้น
Representation Invariant
 ตาแหน่ง list.size-1 คือ “หัว” ของสแตก
 ฉะนั้น
 เวลา push ให้ เอาไปใส่ท่ตี าแหน่งที่ถดั จากตาแหน่งสุดท้ าย
 เวลา pop ให้ ลบตาแหน่งสุดท้ ายออก
IntStack_push
void IntStack_push(IntStack *S, int x)
{
IntList_insert(&S->list, S->list.size, x);
}
IntStack_pop
int IntStack_push(IntStack *S)
{
int head = S->list.size-1;
int result = IntList_get(&S->list, head);
IntList_remove(&S->list, head);
return result;
}
ใช้ลสิ ต์สร้างคิว
ทวนความจา: คิว
 ความสามารถ
 enqueue(Q, x): ใส่ข้อมูลเข้ าไปวางไว้ บน “ท้ าย” ของ queue
 dequeue(Q): เอาเข้ ามูลที่อยู่ท่ี “หัว” queue ออกมา
ในภาษา C
 ต้ องการ user-defined type IntQueue
 ต้ องการฟังก์ชันต่อไปนี้
 void IntQueue_enqueue(IntQueue *Q, int x)
 int IntQueue_dequeue(IntQueue *Q)
 void IntQueue_clear(IntQueue *Q)
 ลบสมาชิกทั้งหมดและคืนหน่วยความจาที่จองไว้ ให้ ระบบ
 void IntQueue_init(IntQueue *Q)
 ตั้งค่าเริ่มต้ นให้ กบ
ั สแตก
IntQueue
 คิวคล้ ายๆ กับลิสต์ แต่ทาอะไรได้ น้อยกว่า (อีกแล้ ว)
 ฉะนั้นเราน่าจะสามารถเอาลิสต์มาทาคิวได้ (อีกแล้ ว)
typedef struct
{
IntList list;
} IntQueue;
 clear และ init จะเรียก IntList_clear และ IntList_init เท่านั้น
Representation Invariant
 ตาแหน่ง list.size-1 คือ “ท้ าย” ของคิว
 ตาแหน่ง 0 คือ “หัว” ของคิว
 ฉะนั้น
 เวลา enqueue ให้ เอาไปใส่ท่ตี าแหน่งที่ถดั จากสุดท้ าย
 เวลา dequeue ให้ ลบตาแหน่งที่ 0 ออก
IntQueue_enqueue
void IntQueue_enqueue(IntQueue *Q, int x)
{
IntQueue_insert(&Q->list, Q->list.size, x);
}
IntQueue_dequeue
int IntQueue_dequeue(IntQueue *Q)
{
int result = IntList_get(&Q->list, 0);
IntList_remove(&Q->list, 0);
return result;
}
ประสิทธิภาพ
ประสิทธิภาพ
 เวลาในการทางานของโปรแกรมขึ้นอยู่กบ
ั จานวนคาสั่งที่มนั ทา
 ส่วนมากเวลาเราประเมินประสิทธิภาพของโปรแกรม จะนับจานวนครั้งของคาสั่งที่
ถูกทางานบ่อยที่สดุ (หรือคาสั่งที่เราสนใจมากที่สดุ )
 คาสั่งที่เราจะนับเรียกว่า main operation
 ในกรณีน้ เี ราสนใจการอ่านและกาหนดค่าให้ อะเรย์
 จานวนครั้งที่ใช้ IntArray_get
 จานวนครั้งที่ใช้ IntArray_set
ประสิทธิภาพของปฏิบตั กิ ารของ IntList
 int IntList_get(IntList *L, int i)
 เรียก IntArray_get เพียงแค่ครั้งเดียว
 void IntList_set(IntList *L, int i, int x)
 เรียก IntArray_set เพียงแค่ครั้งเดียว
ประสิทธิภาพของปฏิบตั กิ ารของ IntList
 int IntList_find(IntList *L, int x)
 เวลาการทางานขึ้นกับข้ อมูล
 ถ้ า x อยู่ท่ตี าแหน่ง k มันจะเรียก IntArray_get เป็ นจานวน k ครั้ง
 ถ้ ามีข้อมูล n ตัว แต่ไม่มี x อยู่ใน list เลย มันจะเรียก IntArray_get เป็ นจานวน
n ครั้ง
 เวลาวิเคราะห์ เราจะคานึงกรณีท่มี ันทางานช้ าสุด
 ฉะนั้นเวลาการทางานของ IntList_find คือ n คาสั่ง
สัญกรณ์ Big-Oh
 เวลาบอกประสิทธิภาพการทางานของโปรแกรม
นักวิทยาการคอมพิวเตอร์มกั จะตัดรายละเอียดที่ไม่จาเป็ นออก
 คานึงถึงความสัมพันธ์ระหว่างเวลาการทางานกับจานวนข้ อมูล
แทนที่จะคานึงถึงจานวนคาสั่งจริงๆ
 ถ้ าเวลาการทางานไม่ข้ น
ึ กับจานวนข้ อมูล
เราจะเขียนว่าเวลาการทางานเป็ น O(1)
 ถ้ าเวลาการทางานมีค่าประมาณจานวนข้ อมูล
เราจะเขียนว่าเวลาการทางานเป็ น O(n)
สัญกรณ์ Big-Oh
 กฎ: ถ้ าจานวนคาสั่งฟังก์ชัน f(n) ให้ ตด
ั
 พจน์ไม่ใช่พจน์ท่ใี หญ่สดุ ออก
 ลบสัมประสิทธิ์ของพจน์ท่สี าคัญที่สดุ ออก
 ตัวอย่าง
 f(n) = 3n2 + n + 1  O(n2)
 f(n) = 100n + 10000  O(n)
 f(n) = 123456789  O(1)
ประสิทธิภาพของปฏิบตั กิ ารของ IntList
 int IntList_get(IntList *L, int i)
 เวลาการทางานไม่ข้ น
ึ กับจานวนข้ อมูล
 O(1)
 void IntList_set(IntList *L, int i, int x)
 เวลาการทางานไม่ข้ น
ึ กับจานวนข้ อมูล
 O(1)
 int IntList_find(IntList *L, int x)
 กรณีท่แี ย่ท่สี ดุ คือไม่เจอข้ อมูล
 O(n)
ประสิทธิภาพของปฏิบตั กิ ารของ IntList
 void IntList_insert(IntList *L, int i, int x)
 ถ้ าแทรกเข้ าที่สดุ ท้ าย ไม่ต้องเลื่อนข้ อมูล  O(1)
 ถ้ าแทรกเข้ าที่ตาแหน่ง k ต้ องเลื่อนข้ อมูล n-k ตัว
 ถ้ าแทรกเข้ าที่ตาแหน่งแรก ต้ องเลื่อนข้ อมูล n ตัว  O(n)
 ดังนั้นเวลาการทางานคือ O(n)
ประสิทธิภาพของปฏิบตั กิ ารของ IntList
 void IntList_remove(IntList *L, int i)
 ถ้ าลบที่ตาแหน่งสุดท้ าย ไม่ต้องเลื่อนข้ อมูล  O(1)
 ถ้ าลบที่ตาแหน่ง k ต้ องเลื่อนข้ อมูล n-k-1 ตัว
 ถ้ าลบที่ตาแหน่งแรก ต้ องเลื่อนข้ อมูล n-1 ตัว  O(n)
 ดังนั้นเวลาการทางานคือ O(n)
ประสิทธิภาพการทางานของ IntStack
 void IntStack_push(IntStack *S, int x)
 แทรกที่ตาแหน่งสุดท้ าย ไม่ต้องเลื่อนข้ อมูล  O(1)
 int IntStack_pop(IntStack *S)
 ลบที่ตาแหน่งสุดท้ าย ไม่ต้องเลื่อนข้ อมูล  O(1)
ประสิทธิภาพการทางานของ IntQueue
 void IntQueue_enqueue(IntQueue *Q, int x)
 แทรกที่ตาแหน่งสุดท้ าย ไม่ต้องเลื่อนข้ อมูล  O(1)
 int IntQueue_dequeue(IntQueue *Q)
 ลบที่ตาแหน่งสแรก ต้ องเลื่อนข้ อมูล n ทั้งหมด  O(n)