五、链 表 1、单链表 2、双向链表 3、链表应用.

Slides:



Advertisements
Similar presentations
人 工 智 慧 報 告 五子棋AI設計 報告者 : 潘輝銘.
Advertisements

程序设计实习 3月份练习解答
Memory Pool ACM Yanqing Peng.
第三章 鏈結串列 Linked List.
第三章 链表 单链表 循环链表 多项式及其相加 双向链表 稀疏矩阵.
第二章 线性表 1 线性表的逻辑结构及其基本操作 2 线性表的顺序存储结构 3 线性表的链式存储结构 4 静态链表 5 应用实例.
Chapter 7 Search.
Tree(樹) 什麼是「樹」? 「樹」的範例 「樹」的定義 「樹」的表示法.
第4章 鏈結串列(Linked Lists) 4-1 動態記憶體配置-(6) 4-2 鏈結串列的基礎-(7)
Linked List Operations
單向鏈結串列 Singly Linked Lists.
第6章 佇列(Queues) 6-1 佇列的基礎 6-2 佇列的表示法 6-3 環狀佇列 6-4 雙佇列.
struct 可以在同一個名稱下擁有多種資料型態。使用struct能讓資料的存取和處理更為靈活。
佇列 (Queue).
4.4 佇列 特徵: c b a c b d c b 新增 d 刪除 入口 入口 入口 尾端(rear) 尾端(rear) 尾端(rear)
資料結構 第5章 佇列.
基础综合 C++ Builder 显示与输入接口
親愛的老師您好 感謝您選用本書作為授課教材,博碩文化準備本書精選簡報檔,特別摘錄重點提供給您授課專用。 說明: 博碩文化:
Chap 3 堆疊與佇列 Stack and Queue.
1、基本概念 2、插入排序 3、交换排序 4、选择排序 5、归并排序 6、基数排序 7、外部排序
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
第七章 搜索结构 静态搜索结构 二叉搜索树 AVL树.
Array & General List.
·线性表的定义及ADT ·线性表的顺序存储结构 ·线性表的链接存储结构 · 单向循环链表 · 双链表、双向循环链表 · 一元多项式的加法
哈夫曼编码.
第12章 樹狀搜尋結構 (Search Trees)
第六章 继承性和派生类 胡昊 南京大学计算机系软件所.
授课老师:龚涛 信息科学与技术学院 2018年3月 教材: 《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
程序设计期末复习 黎金宁
第2章 线性表 线性表抽象数据类型 顺序表 主要知识点 单链表 循环单链表 循环双向链表 静态链表 设计举例.
第十章 C高级程序应用—链表* 10.1链表的基本概念 10.2单向链表 10.3双向链表 10.4应用举例.
第二章 线性表.
数据结构与算法
第 六 章 鏈結串列(Link List) 課程名稱:資料結構 授課老師:________ 2019/1/2.
資料結構與C++程式設計進階 堆疊與佇列(Stack & Queue) 講師:林業峻 CSIE, NTU 6/ 21, 2010.
第一章 绪论.
陈海明 副教授 信息学院 计算机系 电子信息类非计算机专业选修课 程序设计实践 陈海明 副教授 信息学院 计算机系
佇列(queue) Lai Ah Fur.
二叉树和其他树 (Binary and other trees)
第三章 栈和队列.
第十章 模板 丘志杰 电子科技大学 计算机学院 软件学院.
Chapter4 Arrays and Matrices
第三章 链表 单链表 (Singly Linked List) 循环链表 (Circular List) 多项式及其相加
Chapter6 队列(Queues) The Abstract Data Type
二叉树的遍历.
自我參考結構 (self-reference – 1)
第三章 链表 单链表 循环链表 多项式及其相加 双向链表 稀疏矩阵.
常宝宝 北京大学计算机科学与技术系 数据结构(三) 常宝宝 北京大学计算机科学与技术系
第五章 递归与广义表 递归的概念 递归过程与递归工作栈 递归与回溯 广义表.
第二章 数组 作为抽象数据类型的数组 顺序表 多项式抽象数据类型 稀疏矩阵 字符串 小结.
top b top a a top 空栈 a 进栈 b 进栈 top top e e e top d d d c c c b b b a a
第九章 排序 概述 插入排序 快速排序 交换排序(起泡排序) 选择排序 归并排序.
Linked Lists Prof. Michael Tsai 2013/3/12.
第四章 栈和队列 栈 ( Stack ) 队列 ( Queue ) 优先队列 (Priority Queue) 小结.
本教學投影片係屬教科書著作之延伸,亦受著作權法之保護。
第三章 数据抽象.
字符串 (String) 字符串是 n (  0 ) 个字符的有限序列, 记作 S = “c1c2c3…cn” 其中,S 是串名字
第二章 Java语法基础.
C++语言程序设计 C++语言程序设计 第十章 多态 第十一组 C++语言程序设计.
授课老师:龚涛 信息科学与技术学院 2016年3月 教材:《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
#include <iostream.h>
第二章 Java基本语法 讲师:复凡.
十二、堆 堆的定义.
第4章 鏈結串列(Linked Lists) 4-1 動態記憶體配置-(6) 4-2 鏈結串列的基礎-(7)
Chapter 2 Entity-Relationship Model
C++语言程序设计 C++语言程序设计 第十一章 异常处理 C++语言程序设计.
由一个佯谬看涡旋电流的存在 PB 田鸿翔 指导老师 万树德.
第10章 二元搜尋樹 (Binary Search Tree)
题目详细要求、参考资料及更新发布于: 第二周 链表与指针 题目详细要求、参考资料及更新发布于:
資料結構與C++程式設計進階 C++與資料結構 講師:林業峻 CSIE, NTU 7/ 5, 2010.
Presentation transcript:

五、链 表 1、单链表 2、双向链表 3、链表应用

1、单链表 链表的引入 为了解决数组的定长结构的局限性,用“链表”的结构实现一个顺序表。应该说明的是所谓的“链表”结构不仅能表示线性群体,也可以表示非线性群体,如树、图等等。 单链表及其变形是实现线性群体的形式。 单链表是一种最简单的链表,也叫线性链表。每个数据元素占用一个“结点”,每个结点至少要有2个信息:一个该数据元素,另一个反映出它与下一个结点的关联关系,即所谓“指针”,它给出下一个结点的开始存储地址。如下: 空表的标志:head = null 结点(头) 结点 结点(尾) data next → ↑ ≡ head currPtr(当前结点) rear null

单链表(结点定义) ADT Node is Data data // 保存信息 next // 指向下一个结点的指针 Operations Constructor NextNode // 返回下一个结点的地址 InsertAfter // 在当前结点后插入一个新结点,并把 // 新结点作为当前结点 DeleteAfter // 删除当前结点后的直接后继 end ADT Node

next = tempPtr->next; 单链表(结点封装的方法) InsertAfter 操作次序 P->next = next; next = p; DeleteAfter tempPtr = next; next = tempPtr->next; data next ↑ currPtr q 新结点p data next currPtr q tempPtr

单链表(链表的实现) 结点的声明 Template <class T> class Node { private: Node <T> *next; public: T data; Node(const T& item, Node<T> *next = Null); void InsertAfter(Node<T> *p); Node<T> *DeleteAfter(void); }; 例 Node<int> t(10) Node<char> *p, *q; q = new Node<char> (‘B’)’; p = new Node<char> (‘A’, q);

生成结点函数GetNode(初始数据值,指针值),返回新结点的指针。 插入头结点函数INSERTFRONT 单链表(构造链表) 生成结点函数GetNode(初始数据值,指针值),返回新结点的指针。 插入头结点函数INSERTFRONT void InsertFront(Node<T> *&head, T item) { head = GetNode(item, head); }; head head ≡ item item p item null

单链表(构造链表) 插入表尾结点函数INSERTREAR(被插入表的头指针, 初始数据值) 与被插入表的状态有关:如果是空表就转化为插入头结点的情况,如果是非空表,就先要确定表尾的位置。 currPtr 新结点 void InsertRear(Node<T> *&head, const T& item) { Node<T> *newNode, *currPtr = head; if (currPtr = = NULL) InsertFront(head, item); else { while (currPtr->next ! = NULL) currPtr = currPtr ->next ; newNode = GetNode(item); currPtr ->next = newNode; } ≡

例:生成一个数据元素为a 、b 、c 的单链表。 从头部插入生成 单链表(构造链表) 例:生成一个数据元素为a 、b 、c 的单链表。 从头部插入生成 从尾部插入生成 C NULL B C NULL A B C NULL A NULL A B NULL A B C NULL

单链表 单链表几种形式 head null head null tail head ※ 始终标记 head 始终标记 tail

几种单链表表空的判断条件 head = NULL head =NULL tail = NULL ≡ head = 标记结点指针 ※ head ≡ tail ≡

单链表(删除结点) 删除头结点 以简单单链表为例 要点:更新头指针。 next head × × head 删除头结点 以简单单链表为例 要点:更新头指针。 head × × head = head->next; void DeleteFront(Node<T> *& head) { // 保存被删除结点指针 Node<T> * P = head; // 确定该表非空 if (head ! = NULL) head = head->next; //将头指针指向原表的第二个结点 delete p; } next head

通用的删除算法:删除第1个与给定关键字匹配的结点。 单链表(删除结点) 通用的删除算法:删除第1个与给定关键字匹配的结点。 先要确定与给定关键字匹配的结点,还要确定该结点的前驱结点, 引入前驱指针prevPtr。 key prevPtr currPtr

关键:确定加入新结点的位置。办法:扫描链表并进行比较。 确定位置的条件: 例:按输入顺序65,60,82,74建立有序表。 按升序建立有序表。 关键:确定加入新结点的位置。办法:扫描链表并进行比较。 确定位置的条件: 例:按输入顺序65,60,82,74建立有序表。 空表直接用InsertFront 即可。 因为60<65,所以插在65前面,成为新的头结点。也可用InsertFront。 因为82≮60,继续扫描, 82≮65,此时已达表尾,因插入表尾。 65 null 65 null 60 60 65 null 82 null

建立有序表(续) 因为74≮60,继续扫描, 74≮65,继续扫描,74<82,因此82是第1个data值大于给定关键字值的。说明74应插入在82的前面。而当前结点是82,所以还需要一个指针与当前结点指针同步移动,但始终指向当前结点的前驱。 60 65 82 null 74

建立有序表的算法 void InsertOrder(Node<T> *& head, T item) { Node<T> *currPtr, *prevPtr, *newNode; // currPtr, prevPtr, newNode prevPtr = NULL; currPtr = head; while(currPtr ! = NULL) // 遍历表并找到插入点 if (item< currPtr ->data) // 当item<当前结点数据值, 则找到插入点 break; prevPtr = currPtr; currPtr = cuurPtr->NextNode( ); } if (prevPtr = = NULL) InsertFront(head, team); else newNode = GetNode(item); prevPtr->InsertAfter(newNode);

用链表排序 利用有序表可以对数据元素排序,基本思想如下: 对n个元素的数组,逐个将数组元素插入到一个有序链表中,再遍历该链表,将排好序的元素拷贝回数组。 void LinkSort(T a[], int n) { Node<T> *ordlist = NULL, *currPtr; int i; for (i= 0; i<n; i+ + ) // 按序将元素从数组插入表中 InsertOrder(ordlist, a[i]); currPtr = ordlist; i = 0; while(currPtr ! = NULL) a[+ +] = currPtr->data; currPtr = currPtr->nextNode( ); ClearList(ordlist); }

类LinkedList LinkedList类的实现 data Front Rear prevPtr currPtr Position=2 Size = 4 data

复制表 // 把L拷贝到一初始为空的当前表中 Void LinkedList<T>∷CopyList(const LinkedList<T>& L) { Node<T> *p = L.front; //用指针p遍历表 while (p! =NULL) InsertRear(p->data); p = p->NextNode( ); } if (position = = -1) // 若为空表则返回 return; prevPtr = NULL; // 在新表中重新设置prevP和trcurrPtr currPtr = front; for (int pos = 0; pos! = position; pos + +) prevPtr = currPtr; currPtr = currPtr->NextNode( );

清空表 // 将当前表清空 void LinkedList<T>∷ClearList(void) { // 将当前表清空 void LinkedList<T>∷ClearList(void) { Node<T> *currPosition, *nextPosition; currPosition = front; while(currPosition ! = NULL) nextPosition = currPosition->NextNode( ); FreeNode(currPosition); currPosition = nextPosition; // 移至下一个结点 } front = rear = NULL; prevPtr =currPtr = NULL; size = 0; position = -1;

表遍历 // 将表的位置置到pos void LinkedList<T>∷Reset(int pos) { int StartPos; if (front = = NULL) //若表空,则返回 return; if (pos<0 ‖pos>size -1 ) // 若位置非法,退出程序 cerr<<“Reset: Invalid list position: “<< pos << endl; }

表遍历(续) // 遍历表设置pos点(接上页算法) if (pos = = 0) { prevPtr = NULL; currPtr = front; position = 0; } else // 重置 currPtr, prevPtr 和 position currPtr = front->NextNode( ); prevPtr = front; startPos = 1; for (position = startPos; position ! = pos; position+ +) { // 右移直到position = = pos prevPtr = currPtr; currPtr = currPtr->NextNode();

遍历函数 基本方法:prevPtr移到currptr // 将prevPtr和currPtr 指针右移一个结点 Void LinkedList<T>∷Next(void) { // 若已到表尾或表为空,返回 if (currPtr ! = NULL) // 将两个指针右移一个结点 prevPtr = currPtr; currPtr = currPtr->NextNode( ); position + +; }

表插入方法 往表的当前位置插入结点 Void LinkedList<T>∷InsertAt(const T& item) { Node<T> *newNode; if (prevPtr = = NULL) { // 往表头插入,包括往空表中插入 newNode = GetNode(item, front); front = newNode; } else { // 往表中插入, 在prevPtr后插入结点 newNode = GetNode(item); prevPtr->InsertAfter(newNode);

表插入方法(续) // 若prevPtr = =rear,往空表中插入 if (prevPtr = = rear) { rear = newNode; position = size; } // 改变currPtr及增加表的大小 currPtr = newNode; size + +;

表插入示意 InsertAt示意 NULL NULL NULL front rear prevPtr currPtr position(-1) size(0) front rear prevPtr currPtr position(0) size(1) item front rear prevPtr currPtr position(1) size(3) item item item

表删除方法 // 删除表中的当前结点 Void LinkedList<T>∷DeleteAt(void) { Node<T> *p; if (currPtr = = NULL) // 若表为空或到表尾, 出错 cerr <<“Invalid deletion!” << endl; exit(1); } if (prevPtr = =NULL) // 被删除的结点是头结点或表中结点 p = front; front = front->NextNode( );

表删除方法(续) else // 删除prevPtr之后的中间结点, 并保留其地址 p = prevPtr->DeleteAfter(); // 若表尾被删除, prevPtr为新表尾且position减1; 否则不变。若p是最后结点,则rear =NULL且position = -1 if (p = = rear) { rear = prevPtr; position - -; } currPtr = p->NextNode(); FreeNode(p); size - -;

表删除示意 DeleteAt示意 删除结点后成为空表 NULL NULL NULL front rear prevPtr currPtr position(0) size(1) front rear prevPtr currPtr position(-1) size(0)

表删除示意 DeleteAt示意 当前结点是尾结点的情况 front ∧ rear prevPtr currPtr front ∧ NULL front rear prevPtr currPtr position(3) size(4) ∧ front rear prevPtr currPtr position(2) size(3) ∧

header->next = = header 循环表 循环表的构造 在单链表基础上增加一个构造完全相同,但未作初始化的结点,表尾结点的next域的指针指向这个新结点。这个新增结点称为头结点,用header表示。 头指针 第一个结点 在循环表中,表空的判定条件为: header->next = = header header ∧ next

循环结点定义与实现 循环结点的声明 class Cnode { private: cnode<T> *next; // 指向下一个结点的指针 public: T date; CNode(void); // 构造函数 CNode(const T& item); void InsertAfter(CNode<T> *p): // 修改表的方法 CNode<T> *DeleteAfter(void); // 获取下一个结点的地址 CNode<T> *NextNode(void) const; } 循环结点的实现 一般结点初始化 next = = NULL,循环结点初始化是指向自己 next = = this

p->next = =currPtr->next; currPtr-> = = p; 循环表结点的删除 循环表结点的插入与删除 循环表结点的插入 与一般单链表的区别 currPtr currPtr p->next = =currPtr->next; currPtr-> = = p; 循环表结点的删除 与一般单链表的区别和共同点 tempPtr = = currPtr->next; currPtr->next = = temPtr->next;

循环表的生成 循环链表的生成算法 No Yes 初始化头结点 确定当前指针 还有要插入的结点 结束返回 生成新结点 插入当前结点之后

问题:给定整数m, n个人循环报数,报到数m的人出局,剩下的n-1人继续上述游戏,直到剩下最后1人获胜。 约瑟夫问题 问题:给定整数m, n个人循环报数,报到数m的人出局,剩下的n-1人继续上述游戏,直到剩下最后1人获胜。 遍历循环链表基础上,做相应的操作(删除结点),直到表中只剩下一个结点。 No Yes 计数 移动指针 初始化 计数不满m次 表中结点数大于1 当前指针指向头结点 删除结点 结束 再次 移动指针

二、双向链表 双向链表的示意 头结点 头指针 left right left right left right

双向链表结点的定义 双向链表结点的声明 class DNode { private: DNode<T> *left; // 指向左、右结点的指针 DNode<T> *right; public: T data; // data为公有成员 DNode(void); // 构造函数 DNode(const T& item); void InsertRight(DNode<T> *p); // 修改表的方法 void InsertLeft(DNode<T> *p); DNode<T> *DeleteNode(void); DNode<T> *NextNodeRight(void) const; // 取得左、右方向结点的指针 DNode<T> *NextNodeLeft(void) const; }

DNode〈T〉∷DNode(const T& item) { // 建立一个指向自身的结点并初始化data域 left = right = this; data = item; } 将结点p插入到当前结点右边 当前结点 右结点 将右结点接入插入结点的右指针域 将插入结点接入右结点的左指针域 ④ ③ ② ① 将当前结点接入插入结点的左指针域 将插入结点接入当前结点的右指针域 p right left left right

插入右结点算法 Insert Right 的方法 void DNode<T> ∷InsertRight(DNode<T> *p) { // 将p与当前结点的右后继结点相连接 p->right = right; right->left = p; // 将p与当前结点相连接 p->left = this; right = p; } 对InsertLeft而言,只要将InsertRight中的right和left互换即可。

删除当前结点算法 删除当前结点 当前结点 将右结点接入左结点的右指针域 将左结点接入右结点的左指针域 Dnode<T> *Dnode<T>∷DeleteNode(void) { left->right = right; // 将右结点接入左结点的右指针域 right ->left = left; // 将左结点接入右结点的左指针域 // 返回当前结点的指针 return this; }

双向链表只有一个结点的情况 被删除结点是双向链表中仅有的一个结点 删除前 删除后

关键:确定加入新结点的位置。办法:扫描链表并进行比较。 例:按输入顺序65,60,82,74建立有序表。 双向链表应用 用双向链表排序 按升序建立有序表。 关键:确定加入新结点的位置。办法:扫描链表并进行比较。 例:按输入顺序65,60,82,74建立有序表。 65 60 65 60 65 82 60 65 74 82

双向链表应用(续) 单链表排序的局限性 Y N Y Y N N Y Y Y N 输入item 左移 当前指针 右移 当前指针 item≤data 当前结点是头结点 当前结点 是头结点 右插 新结点 左插 新结点 item≤data item≤data 继续输入 结束

用“结构”把系数和指数组成单链表结点中的data,取名Term 链表的应用 1、多项式 其中 因此二元组序列 可以唯一确定一个一元多项式 p(x) 单链表结点结构示意 用“结构”把系数和指数组成单链表结点中的data,取名Term coefficient exponent next

多项式相加 PA = 1-10x6+2x8 +7x14 PB = -x4 +10x6 -3x10 +8x14 +4x18 -x21 CA = 1 -x4 +2x8 -3x10 +15x14 +4x18 -x21 PA PB PC 1 -10 6 2 8 7 14 ∧ -1 4 10 6 -3 10 8 14 4 18 -1 21 ∧ 1 -1 4 2 8 -3 10 15 14 4 18 -1 21 ∧

多项式相加(续) 相加算法思想 N Y N N Y Y N Y N Y Currpa为空 两表指数相等 Currpa指数低 初始化 PC=PA、pa=PA、pb=PB currpa=pa->next, currpb=pb->next currpa∧currpb为空 Currpa为空 两表指数相等 Currpa指数低 pb的剩余部分插入pa表尾 系数和等于零 pa当前指针右移 pb当前结点插入pa之前,pb当前指针右移 修改pa的系数,pb删除当前结点,指针右移 pa、pb删除当前结点,指针右移 结束

稀疏矩阵 稀疏矩阵的三元组存储 0 0 1 0 0 0 0 0 0 0 2 5 0 0 0 0 0 7 0 8 三元组序列 进一步可表示成 0 2 1 2 0 2 2 1 5 3 2 7 3 4 8

十字链表表示稀疏矩阵 十字链表的构成 每一行或列中的非零元素分别组成一条循环链表,若某行(列)全为零元素,该行(列)所对应的链表是只含有头结点的空链表。 各行的链表的头结点另行构成一条循环链表,各列的链表亦然。 整个链表由一个“头结点”引入。 每一个非零元素的结点,按其所在的行与列,同时处于相应的行循环链表与列循环链表中。 结点的构造 head row col down value right

十字链表的例 例 T 4 5 T 5 T 5 T 5 T 5 T 5 T 5 F 1 2 T 5 T 5 F 2 F 2 1 5 T 5 F 1 2 T 5 T 5 F 2 F 2 1 5 T 5 F 3 2 7 F 3 4 8

静态链表 用数组模拟链表的操作 0 1 2 3 4 pl = 5 maxsize-1 在a2后插入c pl = 6 删除a1 pl = 2 · · · 1 2 3 4 -1 6 7 n-1 a0 a1 a2 a3 a4 c · · · 1 2 5 4 -1 3 7 n-1 a0 a1 a2 a3 a4 c · · · 2 6 3 4 -1 7 n-1

作 业 书面作业 1、9.7 2、9.30 3、9.31 上机题 1、9.10 2、9.14