第4章 鏈結串列(Linked Lists) 4-1 動態記憶體配置-(6) 4-2 鏈結串列的基礎-(7)

Slides:



Advertisements
Similar presentations
勇闖「卡勒居」 學長姐經驗分享(文組).
Advertisements

电子成绩单项目实现.
親愛的老師您好 感謝您選用本書作為授課教材,博碩文化準備本書精選簡報檔,特別摘錄重點提供給您授課專用。 說明: 博碩文化:
第7章 樹與二元樹 (Trees and Binary Trees)
“八皇后”问题 崔萌萌 吕金华.
第一章 C语言概述 计算机公共教学部.
第三章 鏈結串列 Linked List.
第二章 线性表 1 线性表的逻辑结构及其基本操作 2 线性表的顺序存储结构 3 线性表的链式存储结构 4 静态链表 5 应用实例.
第二章 线性表.
新世代計算機概論 第14章 程式語言.
Linked List(串列) Why Linked List? Pointer Dynamic Allocation
第4章 鏈結串列(Linked Lists) 4-1 動態記憶體配置-(6) 4-2 鏈結串列的基礎-(7)
Linked List Operations
單向鏈結串列 Singly Linked Lists.
第2章 线性表 线性结构 是一个数据元素的有序集合。.
第6章 佇列(Queues) 6-1 佇列的基礎 6-2 佇列的表示法 6-3 環狀佇列 6-4 雙佇列.
第7章 结构体、联合体和枚举类型 本章导读 本章主要知识点 《 C语言程序设计》 (Visual C++ 6.0环境)
Introduction to the C Programming Language
C语言程序设计 课程 第5章 数组 主讲:李祥 博士、副教授 单位:软件学院软件工程系.
程式設計 博碩文化出版發行.
選擇排序法 通訊一甲 B 楊穎穆.
C的發展史 C程式初體驗 C程式設計基本注意事項 上機實習課程
第11章 查找 主要知识点 查找的基本概念 静态查找表 动态查找表 哈希表.
資料結構 第5章 佇列.
第8章 圖形結構(Graphs) 8-1 圖形的基本觀念 8-2 圖形的表示法 8-3 圖形的走訪 8-4 擴張樹
親愛的老師您好 感謝您選用本書作為授課教材,博碩文化準備本書精選簡報檔,特別摘錄重點提供給您授課專用。 說明: 博碩文化:
親愛的老師您好 感謝您選用本書作為授課教材,博碩文化準備本書精選簡報檔,特別摘錄重點提供給您授課專用。 說明: 博碩文化:
补充内容 结构体 概述 定义结构体类型和定义结构体变量 结构体变量的引用 结构体变量的初始化 指针与结构体 用typedef定义类型的别名.
If … else 選擇結構 P27.
结构体和共用体 2 梁春燕 华电信息管理教研室.
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
·线性表的定义及ADT ·线性表的顺序存储结构 ·线性表的链接存储结构 · 单向循环链表 · 双链表、双向循环链表 · 一元多项式的加法
第12章 樹狀搜尋結構 (Search Trees)
第9章 自訂資料型態 – 結構 9-1 結構資料型態 9-2 結構陣列 9-3 指標與結構 9-4 動態記憶體配置 9-5 聯合資料型態
STRUCTURE 授課:ANT 日期:2010/5/12.
计算概论 第十八讲 C语言高级编程 结构与习题课 北京大学信息学院.
第12章 從C到C++語言 12-1 C++語言的基礎 12-2 C++語言的輸出與輸入 12-3 C++語言的動態記憶體配置
程式撰寫流程.
C语言程序设计 李祥.
第5章 堆疊(Stacks) 5-1 堆疊的基礎 5-2 堆疊的表示法 5-3 堆疊的應用 - 運算式的計算與轉換
第5章 堆疊(Stacks) 5-1 堆疊的基礎 5-2 堆疊的表示法 5-3 堆疊的應用 - 運算式的計算與轉換
算法的基本概念.
第3章 堆栈和队列 堆栈 堆栈应用 队列 队列应用 优先级队列 主要知识点.
C语言 程序设计基础与试验 刘新国、2012年秋.
第 六 章 鏈結串列(Link List) 課程名稱:資料結構 授課老師:________ 2019/1/2.
陈海明 副教授 信息学院 计算机系 电子信息类非计算机专业选修课 程序设计实践 陈海明 副教授 信息学院 计算机系
THE C PROGRAMMING LANGUAGE
親愛的老師您好 感謝您選用本書作為授課教材,博碩文化準備本書精選簡報檔,特別摘錄重點提供給您授課專用。 說明: 博碩文化:
第13章 结构体的应用 13.1 了解由用户构造的数据类型 13.2 结构体类型说明及结构体变量 13.3 结构体数组
計數式重複敘述 for 迴圈 P
第5讲 结构化程序设计(Part II) 周水庚 2018年10月11日.
第三章 链表 单链表 (Singly Linked List) 循环链表 (Circular List) 多项式及其相加
第7章 陣列與指標 7-1 陣列的基礎 7-2 一維陣列的處理 7-3 二維與多維陣列的處理 7-4 陣列的函數參數
自我參考結構 (self-reference – 1)
第三章 链表 单链表 循环链表 多项式及其相加 双向链表 稀疏矩阵.
第五章 递归与广义表 递归的概念 递归过程与递归工作栈 递归与回溯 广义表.
第十章 用户自定义数据类型 目录 学生信息管理系统的开发 结构体数据类型的概述 结构体变量的使用 结构体数组
資料結構與C++程式設計進階 排序與搜尋 講師:林業峻 CSIE, NTU 6/ 14, 2010.
2011 邀请中国姐妹学校韩国语研修团项目 申请时间: ~5月 27日 / 项目地点: 汉阳大学 安山校区 / 项目时间: (星期日) ~ 7.22(星期五) 费用: 100万元(韩币/人 (包含项目 - 学费, 教材费, 宿舍费, 接机费用及所有文化体验活动项目费用)
Linked Lists Prof. Michael Tsai 2013/3/12.
第7章 樹與二元樹(Trees and Binary Trees)
7.1 C程序的结构 7.2 作用域和作用域规则 7.3 存储属性和生存期 7.4 变量的初始化
本教學投影片係屬教科書著作之延伸,亦受著作權法之保護。
C程序设计.
<编程达人入门课程> 本节内容 为什么要使用变量? 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ:
#include <iostream.h>
程式設計--linear search 通訊一甲 B 楊穎穆.
C/C++基礎程式設計班 陣列 講師:林業峻 CSIE, NTU 3/14, 2015.
第三章 流程控制 程序的运行流程 选择结构语句 循环结构语句 主讲:李祥 时间:2015年10月.
第六章 复合数据类型 指针的声明与使用 数组的声明与使用 指针与数组的相互引用 字符串及相关库函数 new与delete
Presentation transcript:

第4章 鏈結串列(Linked Lists) 4-1 動態記憶體配置-(6) 4-2 鏈結串列的基礎-(7) 4-3 單向鏈結串列 – (7) 4-4 環狀鏈結串列 – (7)(8) 4-5 雙向鏈結串列 – (8) 4-6 鏈結串列的應用 - 多項式表示法 –(8) 2019/5/14 CH04-鏈結串列

4-1 動態記憶體配置-說明 動態記憶體配置不同於陣列的靜態記憶體配置 靜態記憶體配置是在編譯階段就配置記憶體空間, 動態記憶體配置是等到執行階段,才向作業系統要求配置所需的記憶體空間, 可以讓程式設計者靈活運用程式所需的記憶體空間。 在C語言<stdlib.h>標頭檔的標準函式庫提供兩個函數:malloc()和free(),可以配置和釋放程式所需的記憶體空間。 2019/5/14 CH04-鏈結串列

4-1 動態記憶體配置-malloc() malloc()函數:配置記憶體空間 fp = (資料型態*) malloc(sizeof(資料型態)); 上述語法因為函數傳回void通用型指標,所以需要加上型態迫換,將函數傳回的指標轉換成指定資料型態的指標, sizeof運算子可以計算指定資料型態的大小。 2019/5/14 CH04-鏈結串列

Malloc()函式 - 範例1 說明 配置一個浮點數變數的記憶空間,如下所示: fp = (float *) malloc(sizeof(float)); fp是浮點數的指標變數 傳回值經過型態迫換成float指標後, 在指派給指標fp 如果記憶體不足的話函式回傳NULL 2019/5/14 CH04-鏈結串列

Malloc()函式 - 範例2 說明 結構與結構陣列也可以使用動態記憶體配置來配置所需要的記憶體空間,如下所示: struct test *score; score=(struct test *) malloc(num*sizeof(struct test)); 上述程式碼在宣告結構指標score之後, 呼叫malloc()函式配置test結構陣列的記憶體空間 其大小為num*sizeof(struct test) num是結構陣列的元素個數!! 2019/5/14 CH04-鏈結串列

4-1 動態記憶體配置-free() free()函數:釋放配置的記憶體空間 free()函數可以釋放malloc()函數配置的記憶體空間, 例如:指標fp是一個指向malloc()函數傳回的浮點數記憶體空間的指標 (參考pp.4), 呼叫free()函數釋放這塊記憶體,如下所示: free(fp); 上述程式碼的指標fp可以是float浮點數指標,也可以是malloc()函數傳回的其它資料型態指標、陣列或結構指標。 2019/5/14 CH04-鏈結串列

程式範例: Ch4-1.c 程式目的 使用動態記憶體配置的malloc()函式配置浮點數和結構陣列的記憶體空間來儲存學生的數學成績, 在計算後, 使用free函式釋放掉配置的記憶體空間!! 程式執行結果 請輸入學生人數3 請輸入第1位的成績.68 請輸入第2位的成績.89 請輸入第3位的成績.69 成績總分: 226 平均成績: 75.33 2019/5/14 CH04-鏈結串列

#include <stdio.h> #include <stdlib.h> /* 主程式 */ * 程式範例: Ch4-1.c */ #include <stdio.h> #include <stdlib.h> /* 主程式 */ int main() { struct test { /* 宣告結構 */ int math; }; /* 結構陣列指標變數宣告 */ struct test *score, *ptr; float *ave; int i, num, sum, temp; printf("請輸入學生人數 ==> "); /* 讀取學生人數 */ scanf("%d", &num); 2019/5/14 CH04-鏈結串列

score=(struct test *)malloc(num*sizeof(struct test)); /* 配置成績陣列的記憶體 */ score=(struct test *)malloc(num*sizeof(struct test)); if ( score != NULL ) { /* 檢查指標 */ sum = 0; /* 讀取成績 */ for ( i = 0; i < num; i++ ) { ptr = &score[i]; /* 指向結構的指標 */ printf("請輸入第%d位的成績. ==> ", i+1); scanf("%d", &temp); ptr->math = temp; /* 指定數學成績 */ sum += temp; } /* 配置浮點數的記憶體 */ ave = (float *) malloc(sizeof(float)); *ave = (float) sum / (float) num; /* 計算平均 */ printf("成績總分: %6d\n", sum); printf("平均成績: %6.2f\n", *ave); free(ave); /* 釋回記憶體空間 */ free(score); } else printf("成績結構陣列的記憶體配置失敗!\n"); system("PAUSE"); return 0; } 2019/5/14 CH04-鏈結串列

4-2 鏈結串列的基礎-說明 「有序串列」(Ordered List)或稱為「線性串列」(Linear List)是一種元素間擁有順序的集合,如下所示: (a0, a1, a2, …, an),ai,0 <= i <= n 上述集合是一個線性串列, 如果是空的線性串列,表示串列中沒有任何元素,是使用( )空括號表示。 2019/5/14 CH04-鏈結串列

4-2 鏈結串列的基礎-範例 一些線性串列的範例,如下所示: 上述線性串列中的元素之間有前後關係的循序性!! 英文的月份:( Jan, Feb, March, …, Oct, Nov, Dec )。 英文的星期:( Mon, Tue, Wed, Thu, Fri, Sat, Sun )。 撲克牌的點數:( A, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K )。 樓層:( B2, B1, 1, 2, 3, 4, 5, 6 )。 生肖:( 鼠, 牛, 虎, …… , 狗, 猪 )。 上述線性串列中的元素之間有前後關係的循序性!! 例如: 二月(Feb)之後是三月(March), 之前是一月(Jan) 2019/5/14 CH04-鏈結串列

4-2 鏈結串列的基礎-運算 線性串列的相關運算,如下所示: length():取得線性串列的長度。 get():存取線性串列的第i個元素。 search():從左到右,或從右到左走訪線性串列。 delete():在線性串列第i個元素刪除元素。 insert():在線性串列第i個元素插入元素。 2019/5/14 CH04-鏈結串列

4-2 鏈結串列的基礎-使用陣列實作線性串列 郵寄名單 陣列實作線性串列 2019/5/14 CH04-鏈結串列

4-2 鏈結串列的基礎-使用陣列實作線性串列(問題1) 複雜的新增與刪除運算 在新增或刪除名單時,因為陣列儲存的是循序且連續資料,所以在陣列中需要搬移大量元素,才能滿足同縣巿位在同一區段。 例如:在桃園巿新增江小魚,則王小美、李光明和周星星都需要依序往後搬移1個元素,才能將江小魚插入, 同理,刪除王大毛時,之後的所有元素也需要往前搬移。 2019/5/14 CH04-鏈結串列

4-2 鏈結串列的基礎-使用陣列實作線性串列(問題2) 浪費記憶體空間 因為並不知道名單有多少位,所以需要宣告一個很大的結構陣列來儲存名單, 如果最後只使用到幾個元素,就會造成大量記憶體空間的閒置。 2019/5/14 CH04-鏈結串列

4-2 鏈結串列的基礎-使用鏈結串列實作線性串列 「鏈結串列」(Linked Lists)是一種實作線性串列的資料結構。 在現實生活中,鏈結串列如同火車掛車廂以線性方式將車廂連結起來,每個車廂是鏈結串列的節點(Nodes),儲存線性串列的資料,車廂和車廂之間的鏈結,就是節點間的鏈結(Link)。如下圖所示: 2019/5/14 CH04-鏈結串列

4-2 鏈結串列的基礎-使用鏈結串列實作線性串列(實作) 用C語言建立鏈結串列是宣告一個結構作為節點,內含指標的成員變數來鏈結其它節點, 使用動態記憶體配置在執行階段配置節點所需的記憶體空間, 即可解決結構陣列實作上浪費記憶體的問題。 2019/5/14 CH04-鏈結串列

例如:使用鏈結串列建立的郵寄名單,筆者僅以編號(從大到小)代表名單的節點資料,郵寄名單的鏈結串列,如下圖所示: 每一個節點都是一個『結構變數』 節點擁有一個指標成員變數指向下一個指標的位址 first是指向鏈結串列開頭節點的指標 最後一個節點指向NULL  表示串列結束!!! 2019/5/14 CH04-鏈結串列

改用鏈結串列後之陣列問題說明 在串列中新增或刪除節點時 因為鏈結串列的存取是使用指標變數 所以可以在不搬移元素的情況下, 插入或刪除郵件名單(前範例)  解決陣列實作時資料搬移的問題!! 原因 : 只需要改變節點指標變數所指向的節點即可!! 2019/5/14 CH04-鏈結串列

4-3 單向鏈結串列 4-3-1 建立和走訪單向鏈結串列 4-3-2 刪除單向鏈結串列的節點 4-3-3 插入單向鏈結串列的節點 2019/5/14 CH04-鏈結串列

4-3 單向鏈結串列-說明 單向鏈結串列是最簡單的一種鏈結串列 因為節點指標都是指向同一個方向,依序從前一個節點指向下一個節點, 然後最後1個節點指向NULL,所以稱為單向鏈結串列,如下圖所示: 2019/5/14 CH04-鏈結串列

4-3 單向鏈結串列-標頭檔 01: /* 程式範例: Ch4-3.h */ 02: struct Node { /* Node節點結構 */ 03: int data; /* 結構變數宣告 */ 04: struct Node *next; /* 指向下一個節點 */ 05: }; 06: typedef struct Node LNode; /* 串列節點的新型態 */ 07: typedef LNode *List; /* 串列的新型態List */ 08: List first = NULL; /* 串列的開頭指標first */ 09: /* 抽象資料型態的操作函數宣告 */ 10: extern void creatList(int len, int *array); 11: extern int isListEmpty(); 12: extern void printList(); 13: extern List searchNode(int d); 14: extern int deleteNode(List ptr); 15: extern void insertNode(List ptr, int d); 2019/5/14 CH04-鏈結串列

模組函式說明 10: extern void creatList(int len, int *array); 使用參數的陣列建立串列, 陣列的第1個元素是串列的最後1個元素;最後1個元素是串列的第1個元素 11: extern int isListEmpty(); 檢查串列是否為空的;如果是回傳1, 否則回傳0 12: extern void printList(); 走訪與顯示串列的節點資料 13: extern List searchNode(int d); 使用走訪方式搜尋串列中是否有參數的節點; 如果找到就回傳節點指標; 否則回傳NULL 14: extern int deleteNode(List ptr); 在串列中刪除參數節點指標的節點 15: extern void insertNode(List ptr, int d); 在串列中第1個參數節點指標位置之後, 插入第2個參數資料的節點 2019/5/14 CH04-鏈結串列

4-3-1 建立和走訪單向鏈結串列-建立(說明) 建立單向鏈結串列 在createList()函數是使用for迴圈將取得的陣列值建立成串列節點,每執行一次迴圈,就在串列開頭插入一個新節點,如下所示: for ( i = 0; i < len; i++ ) { newnode = (List) malloc(sizeof(LNode)); newnode->data = array[i]; newnode->next = first; first = newnode; } 2019/5/14 CH04-鏈結串列

4-3-1 建立和走訪單向鏈結串列-建立(圖例) 前頁程式碼的圖形說明 2019/5/14 CH04-鏈結串列

4-3-1 建立和走訪單向鏈結串列-走訪(說明) 單向鏈結串列的走訪 單向鏈結串列的「走訪」(Traverse)和一維陣列的走訪十分相似 其差異在於陣列是遞增索引值來走訪陣列,串列是使用指標運算來處理節點的走訪,如下所示: List current = first; // current是節點的指標 while ( current != NULL ) { ………….. current = current->next; // 每執行一次while迴圈, 就只向下一個節點, 直到到達串列最後的NULL為止 } 2019/5/14 CH04-鏈結串列

4-3-1 建立和走訪單向鏈結串列-走訪(圖例) 2019/5/14 CH04-鏈結串列

程式範例: createList.c與Ch4-3-1.c 程式目的 實作單向鏈結串列的模組函式: createList(), isListEmpty(), printList()與searchNode(), 接著使用陣列值建立單向鍊結串列, 然後輸入值搜尋串列和顯示串列內容!! 程式執行結果 原來的串列: [6][5][4][3][2][1] 串列是否為空的: 0 請輸入搜尋的郵寄編號(-1結束)3 串列包含節點[3] 請輸入搜尋的郵寄編號(-1結束)8 串列不含節點[8] 請輸入搜尋的郵寄編號(-1結束)-1 2019/5/14 CH04-鏈結串列

* 程式範例: createList.c */ /* 函數: 建立串列 */ void createList(int len, int *array) { int i; List newnode; for ( i = 0; i < len; i++ ) { /* 配置節點記憶體 */ newnode = (List) malloc(sizeof(LNode)); newnode->data = array[i]; /* 建立節點內容 */ newnode->next = first; first = newnode; } /* 函數: 檢查串列是否是空的 */ int isListEmpty() { if ( first == NULL ) return 1; else return 0; 2019/5/14 CH04-鏈結串列

List current = first; /* 目前的串列指標 */ /* 函數: 顯示串列資料 */ void printList() { List current = first; /* 目前的串列指標 */ while ( current != NULL ) { /* 顯示主迴圈 */ printf("[%d]", current->data); current = current->next; /* 下一個節點 */ } printf("\n"); /* 函數: 搜尋節點資料 */ List searchNode(int d) { List current = first; /* 目前的串列指標 */ while ( current != NULL ) { /* 搜尋主迴圈 */ if ( current->data == d ) /* 是否找到資料 */ return current; /* 找到 */ return NULL; /* 沒有找到 */ 2019/5/14 CH04-鏈結串列

/* 程式範例: Ch4-3-1.c */ #include <stdio.h> #include <stdlib.h> #include "Ch4-3.h" #include "createList.c" /* 主程式 */ int main() { int temp; /* 宣告變數 */ int data[6]={ 1, 2, 3, 4, 5, 6 }; /* 建立串列的陣列 */ List ptr; /* 4-3-1: 建立, 走訪與搜尋串列 */ createList(6, data); /* 建立串列 */ printf("原來的串列: "); printList(); /* 顯示串列 */ printf("串列是否空的: %d\n", isListEmpty()); temp = 0; 2019/5/14 CH04-鏈結串列

printf("請輸入搜尋的郵寄編號(-1結束) ==> "); scanf("%d", &temp); /* 讀取節點值 */ while ( temp != -1 ) { printf("請輸入搜尋的郵寄編號(-1結束) ==> "); scanf("%d", &temp); /* 讀取節點值 */ if ( temp != -1 ) /* 搜尋節點資料 */ if ( searchNode(temp) != NULL ) printf("串列包含節點[%d]\n", temp); else printf("串列不含節點[%d]\n", temp); } system("PAUSE"); return 0; 2019/5/14 CH04-鏈結串列

4-3-2 刪除單向鏈結串列的節點-情況1 刪除串列的第1個節點:只需將串列指標first指向下一個節點,如下圖所示: first = first->next; 2019/5/14 CH04-鏈結串列

4-3-2 刪除單向鏈結串列的節點-情況2 刪除串列的最後1個節點:只需將最後1個節點ptr的前一個節點指標指向NULL,如下圖所示: while (current->next!=ptr) current = current->next; current->next = NULL; 2019/5/14 CH04-鏈結串列

4-3-2 刪除單向鏈結串列的節點-情況3(1) 刪除串列的中間節點:將刪除節點前一個節點的next指標,指向刪除節點下一個節點, 例如:刪除節點3,如下圖所示: 2019/5/14 CH04-鏈結串列

4-3-2 刪除單向鏈結串列的節點-情況3(2) 在執行刪除節點3操作後的串列圖形,如下所示: while (current->next!=ptr) current = current->next; current->next = ptr->next; 2019/5/14 CH04-鏈結串列

程式範例: deleteNode.c與Ch4-3-2.c 程式目的 實作單向鏈結串列的deleteNode()函式, 然後輸入欲刪除的節點值, 在串列中分別刪除3種不同情況的節點!! 程式執行結果 原來的串列: [6][5][4][3][2][1] 請輸入刪除的郵寄編號(-1結束) 6 刪除節點: 6 刪除後串列: [5][4][3][2][1] 請輸入刪除的郵寄編號(-1結束) 3 刪除節點: 3 刪除後串列: [5][4][2][1] 請輸入刪除的郵寄編號(-1結束) 1 刪除節點: 1 刪除後串列: [5][4][2] 請輸入刪除的郵寄編號(-1結束) -1 2019/5/14 CH04-鏈結串列

int deleteNode(List ptr) { List current = first; /* 指向前一節點 */ * 程式範例: deleteNode.c */ /* 函數: 刪除節點 */ int deleteNode(List ptr) { List current = first; /* 指向前一節點 */ int value = ptr->data; /* 取得刪除的節點值 */ if ( isListEmpty() ) /* 檢查串列是否是空的 */ return -1; if (ptr==first || ptr==NULL) {/* 串列開始或NULL */ /* 情況1: 刪除第一個節點 */ first = first->next; /* 刪除第1個節點 */ } else { while (current->next!=ptr) /* 找節點ptr的前節點 */ current = current->next; if ( ptr->next == NULL ) /* 是否是串列結束 */ /* 情況2: 刪除最後一個節點 */ current->next = NULL; /* 刪除最後一個節點 */ else /* 情況3: 刪除中間節點 */ current->next = ptr->next; /* 刪除中間節點 */ } free(ptr); /* 釋放節點記憶體 */ return value; /* 傳回刪除的節點值 */ 2019/5/14 CH04-鏈結串列

#include <stdio.h> #include <stdlib.h> #include "Ch4-3.h" /* 程式範例: Ch4-3-2.c */ #include <stdio.h> #include <stdlib.h> #include "Ch4-3.h" #include "createList.c" #include "deleteNode.c" /* 主程式 */ int main() { int temp; /* 宣告變數 */ int data[6]={ 1, 2, 3, 4, 5, 6 };/* 建立串列的陣列 */ List ptr; createList(6, data); /* 建立串列 */ printf("原來的串列: "); printList(); /* 顯示串列 */ 2019/5/14 CH04-鏈結串列

printf("請輸入刪除的郵寄編號(-1結束) ==> "); scanf("%d", &temp); /* 讀取郵寄編號 */ /* 4-3-2: 節點刪除 */ temp = 0; while ( temp != -1 ) { printf("請輸入刪除的郵寄編號(-1結束) ==> "); scanf("%d", &temp); /* 讀取郵寄編號 */ if ( temp != -1 ) { /* 搜尋節點資料 */ ptr = searchNode(temp); /* 找尋節點 */ if ( ptr != NULL ) { temp = deleteNode(ptr); /* 刪除節點 */ printf("刪除節點: %d\n", temp); printf("刪除後串列: "); printList(); /* 顯示刪除後串列 */ } system("PAUSE"); return 0; 2019/5/14 CH04-鏈結串列

4-3-3 插入單向鏈結串列的節點-情況1 將節點插入串列第1個節點之前:只需將新節點newnode的指標指向串列的第1個節點first,新節點就成為串列的第1個節點,如下圖所示: newnode->next = first; first = newnode; 2019/5/14 CH04-鏈結串列

4-3-3 插入單向鏈結串列的節點-情況2 將節點插在串列的最後1個節點之後:只需將原來串列最後1個節點的指標指向新節點newnode,新節點指向NULL,如下圖所示: ptr->next = newnode; newnode->next = NULL; 2019/5/14 CH04-鏈結串列

4-3-3 插入單向鏈結串列的節點-情況3(1) 將節點插在串列的中間位置:假設節點是插在p和q兩個節點之間,p是q的前一個節點,如下圖所示: 2019/5/14 CH04-鏈結串列

4-3-3 插入單向鏈結串列的節點-情況3(2) 只需將p指標指向新節點newnode,然後將新節點指標指向q,就可以插入新節點,如下圖所示: newnode->next=ptr->next; ptr->next = newnode; 2019/5/14 CH04-鏈結串列

程式範例: insertNode.c與Ch4-3-3.c 程式目的 實作單向鏈結串列的insertNode()函式, 然後輸入欲插入節點的位置與節點值, 在串列中分別插入3種不同情況的節點!! 程式執行結果 原來的串列: [6][5][4][3][2][1] 插入後串列: [50][6][5][4][3][2][1] 請輸入插入其後的郵寄編號(-1結束) 1 請輸入新的郵寄編號(0~100)10 插入後串列: [50][6][5][4][3][2][1][10] 請輸入插入其後的郵寄編號(-1結束) 6 請輸入新的郵寄編號(0~100) 8 插入後串列: [50][6][8][5][4][3][2][1][10] 請輸入插入其後的郵寄編號(-1結束) -1 2019/5/14 CH04-鏈結串列

/* 程式範例: insertNode.c */ /* 函數: 插入節點 */ void insertNode(List ptr, int d) { List newnode; /* 配置節點記憶體 */ newnode = (List) malloc(sizeof(LNode)); newnode->data = d; /* 指定節點值 */ if ( ptr == NULL ) { /* 情況1: 插入第一個節點 */ newnode->next = first; /* 新節點成為串列開始 */ first = newnode; } else { if ( ptr->next == NULL ) { /* 串列最後一個節點 */ /* 情況2: 插入最後一個節點 */ ptr->next = newnode; /* 最後指向新節點 */ newnode->next = NULL; /* 新節點指向NULL */ /* 情況3: 插入成為中間節點 */ newnode->next=ptr->next; /* 新節點指向下一節點 */ ptr->next = newnode; /* 節點ptr指向新節點 */ 2019/5/14 CH04-鏈結串列

#include <stdio.h> #include <stdlib.h> #include "Ch4-3.h" * 程式範例: Ch4-3-3.c */ #include <stdio.h> #include <stdlib.h> #include "Ch4-3.h" #include "createList.c" #include "insertNode.c" /* 主程式 */ int main() { int temp; /* 宣告變數 */ int data[6]={ 1, 2, 3, 4, 5, 6 }; /* 建立串列的陣列 */ List ptr; createList(6, data); /* 建立串列 */ printf("原來的串列: "); printList(); /* 顯示串列 */ /* 4-3-3: 節點插入 */ temp = 0; insertNode(NULL, 50); /* 插入第一個節點 */ printf("插入後串列: "); printList(); /* 顯示插入後串列 */ 2019/5/14 CH04-鏈結串列

printf("請輸入插入其後的郵寄編號(-1結束) ==> "); scanf("%d", &temp); /* 讀取郵寄編號 */ while ( temp != -1 ) { printf("請輸入插入其後的郵寄編號(-1結束) ==> "); scanf("%d", &temp); /* 讀取郵寄編號 */ if ( temp != -1 ) { ptr = searchNode(temp); /* 找尋節點 */ if ( ptr != NULL ) printf("請輸入新的郵寄編號(0~100) ==> "); scanf("%d", &temp); /* 讀取新的郵寄編號 */ insertNode(ptr, temp); printf("插入後串列: "); printList(); /* 顯示插入後串列 */ } system("PAUSE"); return 0; 2019/5/14 CH04-鏈結串列

4-4 環狀鏈結串列-說明 如果將最後一個節點的指標改為指向單向鏈結串列開始的第1個節點 (不再指向NULL),這種串列稱為「環狀鏈結串列」(Circular Lists)。 2019/5/14 CH04-鏈結串列

4-4 環狀鏈結串列-應用 (例) 電腦在處理程式的記憶體空間或輸出入緩衝區的記憶體就是使用環狀串列連接各節點 每一個節點就是一塊佔用或閒置的記憶體空間!! 環狀串列的完整範例程式Ch4-4.c 因與單向鏈結串列只差在將最後一個節點從指向NULL, 改為止向第一個節點!! 故未列完整程式碼於此!! 2019/5/14 CH04-鏈結串列

4-4 環狀鏈結串列-建立與走訪 環狀鏈結串列的建立只需將最後1個節點的last指標指向第1個節點,即可完成環狀鏈結串列的建立,如下所示: last->next = first; 環狀鏈結串列的走訪檢查是否到串列結束的條件是current->next == first,如下所示: CList current = first; do { ……… current = current->next; } while ( current != first ); 2019/5/14 CH04-鏈結串列

4-4 環狀鏈結串列-插入節點 環狀鏈結串列插入與單向串列插入不同 所以環狀串列節點插入都是單向串列的中間節點插入 因為環狀串列皆指向下一個節點 所以環狀串列節點插入都是單向串列的中間節點插入 只有第一個節點是例外狀況!! 環狀串列的節點插入可以分成兩種狀況 將節點插入第1個節點之前成為串列開始 將節點插在串列中指定節點之後 2019/5/14 CH04-鏈結串列

4-4 環狀鏈結串列-插入節點 (情況1-步驟) 將節點插入第1個節點之前成為串列開始,可以分成三個步驟,如下所示: Step 1: 將新節點newnode的next指標指向串列的第1個節點。 newnode->next = first; Step 2: 然後找到最後1個節點previous且將其指標指向新節點。 previous = first; while ( previous->next != first ) previous = previous->next; previous->next = newnode; Step 3: 將串列的開始指向新節點,新節點成為串列的第1個節點。 first = newnode; 2019/5/14 CH04-鏈結串列

4-4 環狀鏈結串列-插入節點 (情況1-圖例) 2019/5/14 CH04-鏈結串列

4-4 環狀鏈結串列-插入節點 (情況2-步驟) 將節點插在串列中指定節點之後,例如:將節點插在節點ptr之後,分成二個步驟,如下所示: Step 1:將新節點newnode的next指標指向節點ptr的下一個節點。 newnode->next = ptr->next; Step 2:將節點ptr的指標指向新節點newnode。 ptr->next = newnode; 2019/5/14 CH04-鏈結串列

4-4 環狀鏈結串列-插入節點 (情況2-圖例) 2019/5/14 CH04-鏈結串列

4-4 環狀鏈結串列-刪除節點 (情況1-步驟) 刪除環狀串列的第1個節點可以分成二個步驟,如下所示: Step 1: 將串列開始的first指標移至第2個節點。 first = first->next; Step 2: 將最後1個節點的previous指標指向第2個節點。 previous->next = ptr->next; 2019/5/14 CH04-鏈結串列

4-4 環狀鏈結串列-刪除節點 (情況1-圖例) 2019/5/14 CH04-鏈結串列

4-4 環狀鏈結串列-刪除節點 (情況2-步驟) 刪除環狀串列的中間節點,例如:刪除節點ptr分成二個步驟,如下所示: Step 1:先找到節點ptr的前一個節點previous。 while ( previous->next != ptr ) previous = previous->next; Step 2:將前節點的指標指向節點ptr的下節點。 previous->next = ptr->next; 2019/5/14 CH04-鏈結串列

4-4 環狀鏈結串列-刪除節點 (情況2-圖例) 2019/5/14 CH04-鏈結串列

4-5 雙向鏈結串列 4-5-1 雙向鏈結串列的建立與走訪 4-5-2 雙向鏈結串列內節點的插入 4-5-3 雙向鏈結串列內節點的刪除 2019/5/14 CH04-鏈結串列

4-5 雙向鏈結串列-說明 「雙向鏈結串列」(Doubly Linked Lists)是另一種常見的鏈結串列結構,這是一種允許無方向性走訪的鏈結串列。 所以,我們可以將兩個方向相反的單向鏈結串列結合起來,建立一種無方向性的鏈結串列,這就是雙向鏈結串列,如下圖所示: 2019/5/14 CH04-鏈結串列

4-5 雙向鏈結串列-標頭檔 01: /* 程式範例: Ch4-5.h */ 02: struct Node { /* Node節點結構 */ 03: int data; /* 結構變數宣告 */ 04: struct Node *next; /* 指向下一個節點 */ 05: struct Node *previous; /* 指向前一個節點 */ 06: }; 07: typedef struct Node DNode; /* 雙向串列節點的新型態 */ 08: typedef DNode *DList; /* 雙向串列的新型態 */ 09: DList first = NULL; /* 雙向串列的開頭指標 */ 10: DList now = NULL; /* 雙向串列目前節點指標 */ 11: /* 抽象資料型態的操作函數宣告 */ 12: extern void creatDList(int len, int *array); 13: extern void printDList(); 14: extern void nextNode(); 15: extern void previousNode(); 16: extern void resetNode(); 17: extern DList readNode(); 18: extern void insertDNode(DList ptr, int d); 19: extern void deleteDNode(DList ptr); 2019/5/14 CH04-鏈結串列

模組函式說明 12: extern void creatDList(int len, int *array); 使用參數的陣列建立雙向串列 13: extern void printDList(); 走訪和顯示雙向串列的節點資料 14: extern void nextNode(); 移動now指標到串列的下一個節點 15: extern void previousNode(); 移動now指標到串列的前一個節點 16: extern void resetNode(); 重設now指標指向串列的第一個節點 17: extern DList readNode(); 傳回目前走訪節點的mow指標 18: extern void insertDNode(DList ptr, int d); 在串列中第一個參數節點指標位置之後, 插入第2個參數資料的節點 19: extern void deleteDNode(DList ptr); 在串列刪除參數節點指標的節點 2019/5/14 CH04-鏈結串列

4-5-1 雙向鏈結串列的建立與走訪-建立(步驟) 雙向鏈結串列比單向鏈結串列多一個指向前一個節點的指標, createDList()函數使用for迴圈建立雙向串列的其它節點,其作法是將每一個新節點都插入在雙向鏈結串列的最後, 一共有四個步驟,如下所示: Step 1:將新節點的next指標指向NULL。 Step 2:將新節點的previous指標指向before指標。 Step 3:將before指向節點的next指標指向新節點。 Step 4:before指標指向串列的最後1個節點。 2019/5/14 CH04-鏈結串列

4-5-1 雙向鏈結串列的建立與走訪-建立(圖例) for ( i = 1; i < len; i++ ) { newnode = (DList) malloc(sizeof(DNode)); newnode->data = array[i]; newnode->next = NULL; newnode->previous=before; before->next=newnode; before = newnode; } 2019/5/14 CH04-鏈結串列

4-5-1 雙向鏈結串列的建立與走訪-走訪 雙向鏈結串列走訪比單向鏈結串列靈活, 因為可以分別使用next和previous指標從兩個方向進行走訪,模組函數nextNode()、previousNode()和resetNode()可以移動now指標來走訪雙向串列下一個或前一個節點。 在C++語言的STL(Standard Template Library)或Java語言的Collections物件, 雙向串列的相關走訪函數就是容器物件(Container)的「重複指標」(Iterators)。 2019/5/14 CH04-鏈結串列

4-5-2 雙向鏈結串列內節點的插入-情況1(步驟) 將節點插在串列中第1個節點之前:新節點將成為串列的第1個節點,其步驟如下所示: Step 1:將新節點newnode的next指標指向雙向串列的第1個節點。 newnode->next = first; Step 2:將原串列第1個節點的previous指標指向新節點。 first->previous = newnode; Step 3:將原串列的first指標指向新節點,新節點成為環狀串列的開始。 first = newnode; 2019/5/14 CH04-鏈結串列

4-5-2 雙向鏈結串列內節點的插入-情況1(圖例) 2019/5/14 CH04-鏈結串列

4-5-2 雙向鏈結串列內節點的插入-情況2(步驟) 將節點插在串列的最後:新節點是插入成為串列的最後1個節點,其步驟如下所示: Step 1:將最後1個節點ptr的next指標指向新節點newnode。 ptr->next = newnode; Step 2:將新節點的previous指標指向原串列的最後1個節點。 newnode->previous=ptr; Step 3:將新節點的next指標指向NULL。 newnode->next = NULL; 2019/5/14 CH04-鏈結串列

4-5-2 雙向鏈結串列內節點的插入-情況2(圖例) 2019/5/14 CH04-鏈結串列

4-5-2 雙向鏈結串列內節點的插入-情況3(步驟) 將節點插入成為串列的中間節點:如果節點是插在ptr節點之後,插入步驟如下所示: Step 1:將ptr節點next指向下一個節點的previous指標指向新節點。 ptr->next->previous = newnode; Step 2:新節點的next指標指向ptr節點next指標的下一個節點。 newnode->next = ptr->next; Step 3:新節點的previous指標指向ptr節點。 newnode->previous = ptr; Step 4:將ptr節點的next指標指向新節點。 ptr->next = newnode; 2019/5/14 CH04-鏈結串列

4-5-2 雙向鏈結串列內節點的插入-情況3(圖例) 2019/5/14 CH04-鏈結串列

4-5-3 雙向鏈結串列內節點的刪除-情況1 刪除串列的第1個節點:原串列的第2個節點就成為第1個節點,其步驟如下所示: Step 1:將first指標指向第2個節點。 first = first->next; Step 2:將原串列第2個節點的previous指標指定成NULL。 first->previous = NULL; 2019/5/14 CH04-鏈結串列

4-5-3 雙向鏈結串列內節點的刪除-情況2 刪除最後1個節點:原串列最後第2個節點就成為最後1個節點,其步驟如下所示: Step 1:將原串列的最後1個節點的前一個節點的next指標指定成NULL。 ptr->previous->next = NULL; 2019/5/14 CH04-鏈結串列

4-5-3 雙向鏈結串列內節點的刪除-情況3(步驟) 刪除串列內的中間節點:若刪除的是串列中的ptr節點,操作步驟,如下所示: Step 1:將ptr節點previous指標指向的前一個節點的next指標指向ptr節點的下一個節點。 ptr->previous->next = ptr->next; Step 2:將ptr節點next指標指向的後一個節點的previous指標指向ptr節點的前一個節點。 ptr->next->previous = ptr->previous; 2019/5/14 CH04-鏈結串列

4-5-3 雙向鏈結串列內節點的刪除-情況3(圖例) 2019/5/14 CH04-鏈結串列

4-6 鏈結串列的應用 - 多項式表示法(開頭節點串列) 開頭節點的觀念 是指在串列的開頭新增一個虛節點,這個節點並沒有儲存資料,只是作為串列的第1個節點,所有環狀串列的節點都是中間節點, 換句話說,刪除和插入的操作就只剩下第4-4節的第二種情況。(刪除環狀串列的中間節點) 2019/5/14 CH04-鏈結串列

4-6 鏈結串列的應用 - 多項式表示法(標頭檔) 01: /* 程式範例: Ch4-6.h */ 02: struct Node { /* Node節點結構 */ 03: float coef; int exp; /* 結構變數宣告 */ 04: struct Node *next; /* 指向下一個節點 */ 05: }; 06: typedef struct Node PNode; 07: typedef PNode *PList; /* 多項式串列的新型態 */ 08: /* 抽象資料型態的操作函數宣告 */ 09: extern PList createPoly(int len, float *array); 10: extern void printPoly(PList first); 2019/5/14 CH04-鏈結串列

成員函式說明 09: extern PList createPoly(int len, float *array); 使用參數陣列建立多項式串列, 陣列元素值是係數, 索引值是指數, 傳回值是串列的開頭指標!! 10: extern void printPoly(PList first); 顯示多項式, 參數是串列開頭指標!! 2019/5/14 CH04-鏈結串列

4-6 鏈結串列的應用 - 多項式表示法(多項式圖例) 使用含開頭節點的環狀串列來處理多項式,如下所示:(每一個項目都是PNode形態的節點) A(X)=7X4+3X2+4 B(X)=6X5+5X4+X2+7X+9 2019/5/14 CH04-鏈結串列

程式範例: createPoly.c與Ch4-6.c 題目 在C程式中實作多項式標頭檔的createPoly()和printPoly()函式, 然後使用陣列值建立多項式的鏈結串列, 並且顯示多項式的內容!! 程式輸出 7.0X^4+3.0X^2+4.0X^0 6.0X^5+5.0X^4+1.0X^2+7.0X^1+9.0X^0 2019/5/14 CH04-鏈結串列

/* 程式範例: createPoly.c */ /* 函數: 建立多項式的串列 */ PList createPoly(int len, float *array) { int i; PList first, ptr, newnode; /* 建立開頭節點 */ first = (PList) malloc(sizeof(PNode)); first->coef = 0.0; /* 建立開頭節點的內容 */ first->exp = -1; ptr = first; /* 前一個節點指標 */ for ( i = len-1; i >= 0; i-- ) { if ( array[i] != 0.0 ) { /* 配置節點記憶體 */ newnode = (PList) malloc(sizeof(PNode)); newnode->coef = array[i]; /* 建立節點的內容 */ newnode->exp = i; ptr->next = newnode; /* 連結新節點 */ ptr = newnode; /* 成為前一個節點 */ } ptr->next = first; /* 連結第1個節點, 建立環狀串列 */ return first; 2019/5/14 CH04-鏈結串列

void printPoly(PList first) { /* 函數: 顯示多項式 */ void printPoly(PList first) { PList ptr = first->next; /* 串列真正的開頭 */ float c; int e; while ( ptr != first ) { /* 顯示主迴圈 */ c = ptr->coef; e = ptr->exp; printf("%3.1fX^%d", c, e); ptr = ptr->next; /* 下一個節點 */ if ( ptr != first ) printf("+"); } printf("\n"); 2019/5/14 CH04-鏈結串列

#include <stdio.h> #include <stdlib.h> #include "Ch4-6.h" /* 程式範例: Ch4-6.c */ #include <stdio.h> #include <stdlib.h> #include "Ch4-6.h" #include "createPoly.c" /* 主程式 */ int main() { PList a = NULL; /* 多項式串列1的開頭指標 */ PList b = NULL; /* 多項式串列2的開頭指標 */ /* 建立多項式物件所需的陣列 */ float list1[6] = { 4, 0, 3, 0, 7, 0 }; float list2[6] = { 9, 7, 1, 0, 5, 6 }; a = createPoly(6, list1); /* 建立多項式串列1 */ b = createPoly(6, list2); /* 建立多項式串列2 */ printPoly(a); /* 顯示多項式1 */ printPoly(b); /* 顯示多項式2 */ system("PAUSE"); return 0; } 2019/5/14 CH04-鏈結串列

作業1 [課本4-53] 學習評量1, 2, 4, 6 紙本繳交 繳交期限 : 2008-4-22 (下課前交) 2019/5/14 CH04-鏈結串列