Linked List(串列) Why Linked List? Pointer Dynamic Allocation

Slides:



Advertisements
Similar presentations
电子成绩单项目实现.
Advertisements

Memory Pool ACM Yanqing Peng.
第三章 鏈結串列 Linked List.
資料結構與C++程式設計進階 資料結構概論 講師:林業峻 CSIE, NTU 6/ 7, 2010.
高级语言程序设计 C++程序设计教程(下) 2006年春季学期 与一些教材的区别 偏重理论,不去讨论某个系统的具体使用方法,但会涉及实现技术
数据结构与算法 数据结构与算法实验
Chapter 3: Stack.
§4 Additional Binary Tree Operations
Chapter 3.0 C語言的結構與指標 資料結構導論 - C語言實作.
Tree(樹) 什麼是「樹」? 「樹」的範例 「樹」的定義 「樹」的表示法.
Operating System Concepts 作業系統原理 Chapter 3 行程觀念 (Process Concept)
第4章 鏈結串列(Linked Lists) 4-1 動態記憶體配置-(6) 4-2 鏈結串列的基礎-(7)
Linked List Operations
單向鏈結串列 Singly Linked Lists.
第7章 结构体、联合体和枚举类型 本章导读 本章主要知识点 《 C语言程序设计》 (Visual C++ 6.0环境)
程式設計 博碩文化出版發行.
struct 可以在同一個名稱下擁有多種資料型態。使用struct能讓資料的存取和處理更為靈活。
佇列 (Queue).
Chap 18 類別與物件 夫有土者,有大物也。有大物者,不可以物。 物而不物,故能物物。 明乎物物者之非物也,豈獨治天下百姓而已哉!
Derived Class 前言 衍生類別的定義 單一繼承 public, protected, 和 privated 基底類別
第五讲 数据的分组、合并与转换.
Chap 3 堆疊與佇列 Stack and Queue.
补充内容 结构体 概述 定义结构体类型和定义结构体变量 结构体变量的引用 结构体变量的初始化 指针与结构体 用typedef定义类型的别名.
刘胥影 东南大学计算机学院 面向对象程序设计1 2011~2012第3学期 刘胥影 东南大学计算机学院.
C 程式設計— 指標.
Scope & Lifetime 前言 Local Scope Global Functions & Objects
刘胥影 东南大学计算机学院 面向对象程序设计1 2011~2012第3学期 刘胥影 东南大学计算机学院.
C 程式設計— 指標 台大資訊工程學系 資訊系統訓練班.
·线性表的定义及ADT ·线性表的顺序存储结构 ·线性表的链接存储结构 · 单向循环链表 · 双链表、双向循环链表 · 一元多项式的加法
Chapter 3 行程觀念 (Process Concept)
创建型设计模式.
第十五章 Linked List, Stack and Queue
Function.
程序设计期末复习 黎金宁
第三章 C++中的C 面向对象程序设计(C++).
Chap 8 指针 8.1 寻找保险箱密码 8.2 角色互换 8.3 冒泡排序 8.4 电码加密 8.5 任意个整数求和*
第2章 线性表 线性表抽象数据类型 顺序表 主要知识点 单链表 循环单链表 循环双向链表 静态链表 设计举例.
第5章 堆疊(Stacks) 5-1 堆疊的基礎 5-2 堆疊的表示法 5-3 堆疊的應用 - 運算式的計算與轉換
第三章 栈与队列 £3.1 栈 £3.3 队列 £3.2 栈的应用举例 £3.1.1 栈的定义 £3.1.2 栈的顺序存储结构
第三章 栈和队列.
第二章 线性表.
第 六 章 鏈結串列(Link List) 課程名稱:資料結構 授課老師:________ 2019/1/2.
重點 資料結構之選定會影響演算法 選擇對的資料結構讓您上天堂 程式.
五、链 表 1、单链表 2、双向链表 3、链表应用.
陈海明 副教授 信息学院 计算机系 电子信息类非计算机专业选修课 程序设计实践 陈海明 副教授 信息学院 计算机系
樹 2 Michael Tsai 2013/3/26.
第三章 链表 单链表 (Singly Linked List) 循环链表 (Circular List) 多项式及其相加
王玲 第 2 章 线性表 王玲 2019/2/25.
Classes (2) Lecture 7.
第三章 链表 单链表 循环链表 多项式及其相加 双向链表 稀疏矩阵.
常宝宝 北京大学计算机科学与技术系 数据结构(三) 常宝宝 北京大学计算机科学与技术系
第五章 递归与广义表 递归的概念 递归过程与递归工作栈 递归与回溯 广义表.
Speaker: Liu Yu-Jiun Date: 2009/4/29
Linked Lists Prof. Michael Tsai 2013/3/12.
陳維魁 博士 儒林圖書公司 第三章 變數與繫結 陳維魁 博士 儒林圖書公司.
第八章 指標 (Pointer).
Speaker: Liu Yu-Jiun Date: 2009/5/6
本教學投影片係屬教科書著作之延伸,亦受著作權法之保護。
第5章 函 数.
<编程达人入门课程> 本节内容 为什么要使用变量? 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ:
计算机问题求解 – 论题1-5 - 数据与数据结构 2018年10月16日.
唐常杰 四川大学计算机学院 计算机科学技术系
第 3 章 类的基础部分 陈哲 副教授 南京航空航天大学 计算机科学与技术学院.
第六章 类属B树索引技术 对基于树的索引方法给出一种通用算法。该算法是建立在类属B树的概念之上开发的。它将类型系统开放,使系统能支持用户自定义的数据类型、函数和某些特殊的查询谓词的集合。并且,将新的数据类型、函数、查询谓词等登记到数据库管理系统中,
#include <iostream.h>
第4章 鏈結串列(Linked Lists) 4-1 動態記憶體配置-(6) 4-2 鏈結串列的基礎-(7)
2012 程式設計比賽 Openfind 天使帝國 v2.0 (蓋亞的紋章).
Chapter 2 Entity-Relationship Model
第六章 复合数据类型 指针的声明与使用 数组的声明与使用 指针与数组的相互引用 字符串及相关库函数 new与delete
Q1 以下是10個初生嬰兒,首6個月的體重改變紀錄
Presentation transcript:

Linked List(串列) Why Linked List? Pointer Dynamic Allocation Pointer-Based ADT: List, Stack, and Queue Pointer-Based Polynomial ADT Pointer-Based Sparse Matrix ADT Varirants of Linked List Circular Linked List Doubly Linked List

Array-Based List ADT 的缺失: 1. Array has a fixed size. Size Items 1 2 n-1 MAX_SIZE - 1 n 45 21 33 16 ? Items[i] stores the ith item of the list. #define MAX_ITEM 100 typedef int itemType; typedef struct { int Size; Items[MAX_ITEM]; } list; 嚴格言之,用固定大小的陣列並不能完全表示出概念上該是可存無限多資料項的 ADT Lists。

2. Insert 時必須搬動大量的資料項 ListInsert (&L, 2, 44) k 12 3 19 ? 18 10 k+1 5 1 2 k-1 MAX_LIST-1 12 3 19 ? 18 10 k+1 5 44

3. Delete 時必須搬動大量的資料項 ListDelete(&L, 2) Delete k 12 3 19 10 18 ? ? k 1 2 k-1 MAX_LIST-1 k 12 3 19 10 18 ? ? 1 2 k-1 MAX_LIST-1 k 12 3 10 18 ? ? 1 2 k-2 k-1 MAX_LIST-1 k-1 12 3 34 18 ? ? ?

答案 Linked Lists 結論:是否有辦法避免這些缺失?亦即: 1. 所需的記憶體可隨 List 大小伸縮。 2. Insert 時「不須」搬動大量的資料項 3. Delete 時「不須」搬動大量的資料項 答案 Linked Lists

A Linked List 20 45 51 76 84 Insertion: 20 45 51 76 84 60 Deletion: 20 data next a link Insertion: old link 20 45 51 76 84 60 Inserted item Deletion: 20 45 51 60 76 84 Deleted item

Problem : How to implement the links? 答案 Pointer

Pointers(指標) 電腦將編譯後的可執行程式載入記憶體時,會先在記憶體中的 靜宜大學資訊管理學系 資料結構講義 2018/11/12 Pointers(指標) 電腦將編譯後的可執行程式載入記憶體時,會先在記憶體中的 資料區(heap)中預留一些空間給全域變數(global variables) 以儲存其資料。 address /* global variables */ int i; /* 4 bytes */ char c; /* 1 bytes */ float f; /* 4 bytes */ main () { int x; /* local variable */ i = i + 1; } 100 101 i 102 103 c 104 mention the relationship between variables and address. 105 106 f 107 108 將 i 中的值加 1 後存回 i 中 蔡奇偉副教授 編製

haep 我們稱直接存放資料值的變數為「資料變數(data variable)」。 【範例】 變數 i 儲存一個整數資料值 int i; 靜宜大學資訊管理學系 資料結構講義 2018/11/12 我們稱直接存放資料值的變數為「資料變數(data variable)」。 【範例】 變數 i 儲存一個整數資料值 int i; 變數 c 儲存一個字元資料值 char c; 變數 f 儲存一個實數資料值 float f; 變數 p 儲存一個由兩個整數組成 的結構資料值 struct point2DType { int x, y; }; point2DType P; i c f x y p haep 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 蔡奇偉副教授 編製

若一個變數存放的是位址而非實際的資料值,則該變數 稱為「指標變數(pointer variable)」,或簡稱為「指標 靜宜大學資訊管理學系 資料結構講義 2018/11/12 若一個變數存放的是位址而非實際的資料值,則該變數 稱為「指標變數(pointer variable)」,或簡稱為「指標 (pointer)」。 Look in location 344 for what you want 340 26 344 10 348 344 5 352 pointer p p should be drawn inside the memor area. 9 356 蔡奇偉副教授 編製

指標的宣告 type_name *pointer_name; 宣告變數 pointer_name 為指標變數,且其所指的位址是用來 【範例】 int *iPtr; iPtr 是整數指標變數,其指的位址所存放的資料值 將被視為整數值。 char *cPtr; cPtr 是字元指標變數,其指的位址所存放的資料值 將被視為字元值。

float *fPtr; fPtr 是實數指標變數,其指的位址所存放的資料值 將被視為實數值。 point2DType *pPtr; pPtr 是指標變數,其指的位址所存放的資料值將被 視為 PointType 值。 int *p, q; p 是整數指標變數,而 q 是整數變數。此宣告相當於 底下的宣告: int *p; int q;

如果想將 p 和 q 均宣告為指標,則必須用下列宣告: int *p, *q; 或者是: typedef int* intPtrType; intPtrType p, q;

address-of operator(位址運算子) &variable_name 位址運算元是用來求取變數的記憶體位址。上式的值是 變數 variable_name 的位址。 【範例】 int i, *ip; i ip ? i = 6; i ip 6 ? ip存著i的位址 將變數 i 的位址存入指標 ip 中 ip = &i; 6 ip i

ip = i /* error */ ip = 100; /* error */ OK, ip 存放記憶體絕對位址 100 ip = (int *) 100 i ip 6 100

利用指標來間接存取資料值: 【範例】 *pointer_name * : indirection or dereferencing operator. 此式的值是指標 pointer_name 所指記憶體位址中的資料值。 【範例】 int i, *ip; i ip ? i = 6; i ip 6 ? 將變數 i 的位址存入指標 ip 中 ip = &i; 6 ip i or *ip

注意!!! *ip = 7; 和 i = 7 效果一樣 7 ip i or *ip *ip = *ip + 1; 8 ip i or *ip 注意!!! int *ip; /* 宣告 ip 是一個整數指標變數 */ *ip = 100; /* 將 100 存入指標 ip 所指的位址中 */

Pointer & Function Arguments Since C passes arguments to functions by value, there is no direct way for the called function to alter a variable in the calling function. void swap (int x, int y) { int temp; temp = x; x = y; y = temp; } main () { int a = 3, b = 4; ... swap(a, b); } Because of call by value, swap can’t affect the arguments a and b in the routine that called it. The function above only swap copies of a and b.

The way to obtain the desired effect is for the calling program to pass pointers to the values to be changed. void swap (int *x, int *y) { int temp; temp = *x; *x = *y; *y = temp; } main () { int a = 3, b = 4; ... swap(&a, &b); } a b x y main swap

Dynamic Allocation(動態配置) 如前所述,電腦將編譯後的可執行程式載入記憶體時,會 先在記憶體中的資料區(heap)中預留一些空間給全域變 數(global variables)以儲存其資料。此類配置的方式稱為 「靜態配置(static allocation)」; 這類的變數稱為「靜態 配置的變數(statically allocated variables)。如果所需的記 憶體是在程式執行時才配置的,則稱之為「動態配置 (dynamic allocation)」; 這類的變數稱為「動態配置的變 數(dynamically allocated variables)。同時,這些變數並沒 有名稱,而必須倚靠指標來間接存取其中的資料值。

C 提供以下的動態配置函數(定義於 stdlib.h 中): void *malloc(size_t size) malloc returns a pointer to space for an objects of size size, or NULL if the request cannot be satisfied. The space is uninitialized. void *calloc(size_t nobj, size_t size) calloc returns a pointer to space for an array of nobj objects, each of size size, or NULL if the request cannot be satisfied. The space is initialized to zero bytes. void *realloc(void *p, size_t size) realloc changes the size of the object pointed to by p to size. The contents will be unchanged up to the minimum of the old and new sizes. If the new size is larger, the new space is uninitialized. realloc returns a pointer to the new space, or NULL if the request cannot be satisfied, in which case *p is unchanged.

【範例】 type casting int *ip = (int *) malloc(sizeof(int)); 動態配置一塊記憶體來儲存整數資料,並將此記憶體的 位址存入指標變數 ip 中。 float *fp = (float *) malloc(sizeof(float)); 動態配置一塊記憶體來儲存浮點數資料,並將此記憶體 的位址存入指標變數 fp 中。 typedef struct { int x, y; } point; point *pp = (point *) malloc(sizeof(point)); 動態配置一塊記憶體來儲存 point 型態的資料,並將此 記憶體的位址存入指標變數 pp 中。 fp *fp pp *pp x y

int *ip = (int *) calloc(1, sizeof(int)); 動態配置一塊記憶體來儲存整數資料,並將此記憶體的 int *ip = (int *) malloc(sizeof(int)); int *iArray = (int *) calloc(100, sizeof(int)); 動態配置一塊記憶體來儲存 100 個整數資料,並將此記 憶體的位址存入指標變數 iArray 中。 iArray 1 98 99 int *iArray = (int *) malloc(100*sizeof(int)); iArray 1 98 99 ?

【範例】 C 提供下面的函式來解除一塊經由動態配置而得的記憶體 區塊,並將之歸還給系統。(定義於 stdlib.h 中) void free (void *p) free deallocates the space pointed by p; it does nothing if p is NULL. p must be a pointer to space previuosly allocated by calloc, malloc, or realloc. 【範例】 int *ip = (int *) malloc(sizeof(int)); int *iArray = (int *) calloc(100, sizeof(int)); free(ip); free(iArray);

在以後的討論中,我們將採用下列的巨集定義: /************************************************* * FILE: new.h *************************************************/ #ifndef NEW_H_ #define NEW_H_ #include <stdlib.h> #ifndef NULL #define NULL ((void *) 0) #endif #define NEW1(type) (((type) *) calloc(1,sizeof(type))) #define NEW(n,type) (((type) *) calloc((n), sizeof(type))) #define FREE(ptr) (free(ptr))

【範例】 int * P, *Q; int X; P Q ? X P = &X; P X ? *P = 6; P X or *P 6 P = NEW1(int); P *P X 6 *P = 7; P *P X 7 6

Q = P; P *P or *Q X 7 6 Q Q = NEW1(int); *Q = 8 P *P X 7 6 Q *Q 8 P = NULL; P X 7 6 Q *Q 8 FREE(Q); Q = NULL; P X 7 6 Q memory leak

指標和陣列 int iArray[100]; int *iPtr = NEW(100, int); iArray 和 iPtr 都是含有 100 個整數的陣列。兩者都可以 用 index 的方式來存取其中的元素,比方說: iArray[50] 和 iPtr[50] 分別代表 iArray 和 iPtr 中的第 51 個整數。兩者的區別在 於 iArray 是以靜態配置的方式產生,而iPtr是以動態配置 的方式產生。因此,iArray 無法用 free 函數去除其所佔 的記憶體空間,而 iPtr 卻可以。比方說:

Question: FREE(iArray); /* error */ FREE(iPtr); /* OK */ iPtr = NEW(200, int); iPtr is now an integer array of 200 elements. Question: What are the values of the following sizeof operations? sizeof(iArray) sizeof(iPtr)

指標算式 If p is a pointer to some element of an array, then p++ increments p to point to the next element, and p += i increments it to point i elements beyond where it currently does. 1 i MAX_ARRAY - 1 p p+1 p+i p + MAX_ARRAY - 1 If p and q point to members of the same array, then relations like ==, !=, <, >=, etc., work properly. For example, p < q is true if p points to an earlier member of the array than q does.

int *iA = NEW(MAX_SIZE, int); char *cA = NEW(MAX_SIZE, char); #define MAX_SIZE 26 int *iA = NEW(MAX_SIZE, int); char *cA = NEW(MAX_SIZE, char); int *iEnd = iA + MAX_SIZE, *ip; for (ip = iA; ip < iEnd; ip++) *ip = ip - iA; for (int i = 0; i < MAX_SIZE; i++) iA[i] = i; for (int i = 0; i < MAX_SIZE; i++) cA[i] = 'A' + i; char *cEnd = cA + MAX_SIZE, *cp; for (cp = cA; cp < cEnd; cp++) *cp = 'A' + (cp - cA);

struct 指標中的成員存取 or struct_pointer_name->member_name typedef struct { int x, y; } point2DType; point2DType *P = NEW1(point2DType), *Q = NEW1(point2DType); P->x = 100; P->y = 200; Q->x = P->x + 1; Q->y = Q->x * 2; or (*P).x = 100; (*P).y = 200; (*Q).x = (*P).x + 1; (*Q).y = (*Q).x * 2; P *P x y Q *Q x y

A Linked List 如何表示節點? 節點 20 45 51 76 84 由上圖可知,每一個節點包含兩個部份:data 和 link。 next a link 由上圖可知,每一個節點包含兩個部份:data 和 link。 我們可以用 struct 的方式來表示這樣的結構,以及用指 標來表示 link。節點的結構如下: struct node { int Data; struct node *Next; }

或者: struct node; /* forward definition */ typedef struct node* ptrType; struct node { int Data; ptrType Next; } 節點變數的宣告如下: struct node aNode; struct node *aNodePtr;

A Pointer-Based Implementation of the ADT List List ADT List Copy Dummy Head Node Link List

List ADT OBJECTS: A list that contains a sequence of data items. FUNCTIONS: list CreateList () boolean IsEmpty(list L) int ListLength (list L) boolean ListRetrieve (list L, int i, itemType Data) boolean ListInsert (list L, int i, ItemType Data) boolean ListDelete (list L, int i)

typedef int listItemType; typedef struct node { listItemType Item; 下圖顯示串列的表示法: aList NULL 20 45 76 84 typedef int listItemType; typedef struct node { listItemType Item; struct node *Next; } nodeType, *nodePtrType; typedef nodePtrType list;

/* Create an empty list */ list CreateList (void) { return NULL; } list L = CreateList(); L /* Check if L is an empty list */ boolean IsEmpty(list L) { return (L == NULL)? TRUE : FALSE; }

/* Calculate the length of list L */ int ListLength (list L) { nodePtrType p; int len; len = 0; for (p = L; p != NULL; p = p->Next) len++; return len; } 20 45 76 84 L NULL Item Next p p p p

/* Get the address of the n-th node of L for n = 0, 1, ..., * ListLength(L) - 1. */ static nodePtrType Ptrto (list L, int n) { nodePtrType p; if (n < 0) return NULL; for (p = L; n > 0 && p != NULL; n--, p = p->Next) ; return p; } 20 45 76 84 L 1 n ListLength(L) - 1 p p p

boolean ListRetrieve (list L, int n, listItemType *Data) { nodePtrType p = PtrTo(L, n); if (p == NULL) return FALSE; else { *Data = p->Item; return TRUE; } 20 45 76 84 L 1 n ListLength(L) - 1 p p p

Insert Operation 我們考慮將資料項插入串列的二種情形: 一、資料項插在最前面的位置: L 20 45 51 ListInsert(L, 0, 56); L 20 45 51 56

L 20 45 51 ListInsert(L, 0, 56); NewPtr 56 L 20 45 51 nodePtrType NewPtr = NEW1(nodeType); NewPtr->Item = 56; NewPtr->Next = L; L = NewPtr;

先建後拆 L 20 45 51 ListInsert(L, 0, 56); NewPtr 56 L 20 45 51 nodePtrType NewPtr = NEW1(nodeType); NewPtr->Item = 56; L = NewPtr; NewPtr->Next = L;

二、資料項不是插在最前面的位置: L 20 45 51 ListInsert(L, 1, 56); 56 L 20 45 51

L 20 45 51 ListInsert(L, 1, 56); NewPtr 56 L 20 45 51 Prev nodePtrType NewPtr = NEW1(nodeType); NewPtr->Item = 56; nodePtrType Prev = PtrTo(L, 1-1); NewPtr->Next = Prev->Next; Prev->Next = NewPtr;

boolean ListInsert(list *L, int n, listItemType NewItem) { boolean Success = (n >= 0 && n <= ListLength(*L)) ? TRUE : FALSE; nodePtrType NewPtr, Prev; if (Success) { NewPtr = NEW1(nodeType); Success = (NewPtr != NULL) ? TRUE : FALSE; NewPtr->Item = NewItem; /* attach new node to list */ if (n == 0) { /* insert new node at beginning of list */ NewPtr->Next = *L; *L = NewPtr; }

else { Prev = PtrTo(*L, n - 1); /* Insert new node after node to which Prev points */ NewPtr->Next = Prev->Next; Prev->Next = NewPtr; } /* end if */ return Success; } /* end ListInsert */

Delete Operation 我們考慮將資料項從串列中刪除的二種情形: 一、刪除在最前面的資料項: L 20 45 51 76 ListDelete(L, 0) L 45 51 76

L 20 45 51 76 ListDelete(L, 0) L 20 45 51 76 Cur nodePtrType Cur = L; /* save pointer to node */ L = L->Next; Free(Cur);

一、刪除不在最前面的資料項: L 20 45 51 76 ListDelete(L, 1) L 20 45 51 76 Prev Cur nodePtrType Prev = PtrTo(1-1); nodePtrType Cur = Prev->Next Prev->Next = Cur->Next; Free(Cur);

boolean ListDelete(list *L, int n) { ptrType Cur, Prev; boolean Success = (n >= 0) && n < ListLength(*L) )? TRUE : FALSE; if (Success) { if (n == 0) { /* delete the first node from the list */ Cur = *L; /* save pointer to node */ *L = (*L)->Next; } else { Prev = PtrTo(*L, n - 1); /* delete the node after the node to which Prev points */ Cur = Prev->Next; /* save pointer to node */ Prev->Next = Cur->Next; FREE(Cur); return Success;

List Copy 串列有兩種拷貝的方式: 淺拷貝(shallow copy):不拷貝資料項目 深拷貝(deep copy):拷貝資料項目 以下圖為例: 淺拷貝 L 20 45 51 76 Copy of L L 20 45 51 76 深拷貝 Copy of L 20 45 51 76

淺拷貝 list ListShallowCopy (list L) { return L; } 20 45 51 76 L list M; M = ListShallowCopy(L); 20 45 51 76 M L

20 45 51 76 M L ListDelete(M, 1); L 20 51 76 M ListInsert(L, 1, 99); 20 99 51 76 M L

深拷貝 p M q 20 45 51 76 L 20 45 51 76 L p M 20 q 45

M 20 45 51 76 L p q M 20 45 51 76 L p q

list ListDeepCopy (list L) { nodePtrType p, q; list M; if (IsEmpty(L)) return NULL; /* original list is empty */ p = L; M = q = NEW1(nodeType); assert(q != NULL); while (TRUE) { q->Item = p->Item; if (p->Next != NULL) { p = p->Next; q->Next = NEW1(nodeType); assert(q->Next != NULL); q = q->Next; } else break; q->Next = NULL; return M;

L 20 45 51 76 M 20 45 51 76 ListDelete(&M, 1); L 20 45 51 76 M 20 51 76 ListInsert(&M, 1, 99); L 20 45 51 76 M 20 99 51 76

Linked Lists with Dummy Head Nodes 首先,我們回憶一下:在做串列的 insert 和 delete 時 ,由於第一個節點異於其他節點,我們的處理方法就 必須分為兩種狀況來處理: 增刪第一個節點 增刪非第一個節點

L 20 45 51 一、資料項插在最前面的位置: ListInsert(L, 0, 56); L 20 45 51 56 二、資料項不是插在最前面的位置: ListInsert(L, 1, 56); L 20 45 51 56

L 20 45 51 76 一、刪除在最前面的資料項: ListDelete(L, 0) L 20 45 51 76 二、刪除不在最前面的資料項: ListDelete(L, 1) L 20 45 51 76

如何避免這樣的區分呢? 答案 Dummy Head Node

前面所指出的問題,完全是因為第一個節點異於其他節點的 緣故。因此破解的方法就是想辦法讓第一個節點和其他的節 點一樣。為此,我們構建一個「空頭」節點來取代指標 L: L 20 45 51 76 ? 20 45 51 76 Dummy Head Node 如此一來,第一個節點就和其他的節點一樣是靠前一個節點 (即空頭節點)的 Next 指標所串接起來的。 接下來,我們看看這一個新的表示方式,是否真的解決了 前述的問題。

哈!哈! 果然很像 Insert 56 第一個節點: ? 20 45 51 Dummy Head Node 56 非第一個節點: ? 20

哈!哈! 真的很像 Delete 第一個節點: ? Dummy Head Node 20 45 51 76 非第一個節點: ? Dummy

/* Create an empty list */ void CreateList (list L) { L->Next = NULL; } nodeType L; CreateList(&L); ? Dummy Head Node /* Check if L is an empty list */ boolean IsEmpty(list L) { return (L->Next == NULL)? TRUE : FALSE; }

/* Calculate the length of list L */ int ListLength (list L) { nodePtrType p; int len; len = 0; for (p = L->Next; p != NULL; p = p->Next) len++; return len; } ? Dummy Head Node 20 45 76 84 Item Next p p p p

/* Get the address of the n-th node of L for n = 0, 1, ..., * ListLength(L) - 1. */ static nodePtrType Ptrto (list L, int n) { nodePtrType p; if (n < -1) return NULL; else if (n == -1) return L; for (p = L->Next; n > 0 && p != NULL; n--, p = p->Next) ; return p; } 1 n ListLength(L) - 1 ? Dummy Head Node 20 45 76 84 p p p

boolean ListRetrieve (list L, int n, listItemType *Data) { nodePtrType p = PtrTo(L, n); if (p == NULL || p == L) return FALSE; else { *Data = p->Item; return TRUE; } 1 n ListLength(L) - 1 ? Dummy Head Node 20 45 76 84 p p p

boolean ListInsert(list L, int n, listItemType NewItem) { boolean Success = (n >= 0 && n <= ListLength(L)) ? TRUE : FALSE; nodePtrType NewPtr, Prev; if (Success) { NewPtr = NEW1(nodeType); Success = (NewPtr != NULL) ? TRUE : FALSE; NewPtr->Item = NewItem; Prev = PtrTo(L, n - 1); /* Insert new node after node to which Prev points */ NewPtr->Next = Prev->Next; Prev->Next = NewPtr; } return Success;

boolean ListDelete(list L, int n) { ptrType Cur, Prev; boolean Success = (n >= 0) && n < ListLength(L) )? TRUE : FALSE; if (Success) { Prev = PtrTo(L, n - 1); /* delete the node after the node to which Prev points */ Cur = Prev->Next; /* save pointer to node */ Prev->Next = Cur->Next; } FREE(Cur); return Success;

Linked List Data with an Aggregate Type 為了簡化的緣故,在前面的說明中,我們僅用 int 的資料型態, 然而,在實際的情形中,資料不是如此的簡單。比方說,我們 可以建立一個學生資料的串列如下: struct studentType { char Name[20]; // 姓名 char Address[30]; // 地址 char PhoneNumber[10]; // 電話 int Age; // 年齡 char Department[4]; // 系別 int Year; // 年級 char Class; // 班級 }; typedef studentType listItemType;

char PhoneNumber[10]; // 電話 int Age; // 年齡 char Department[4]; // 系別 struct studentType { char Name[20]; // 姓名 char Address[30]; // 地址 char PhoneNumber[10]; // 電話 int Age; // 年齡 char Department[4]; // 系別 int Year; // 年級 char Class; // 班級 }; typedef studentType listItemType; L Name Address