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)