1 / 54

程序设计实习

程序设计实习. 第九讲 链表和二叉树 http://ai.pku.edu.cn/cpp2010. 链 表. 1) 链表的结构和特点 2) 单链表的操作:插入、删除算法. 单链表. 单链表的概念: 单链表( singly linked list) 是一种简单的链表表示,也称为线性链表。是由结点组成。 一个结点由两个域组成: ①数据域 ②指向下一结点的指针域. 特点 每个元素 ( 表项 ) 由结点 ( Node ) 构成 。. 线性结构. 结点可以不连续存储 表可扩充. 单链表中的插入与删除

homer
Télécharger la présentation

程序设计实习

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 程序设计实习 第九讲 链表和二叉树 http://ai.pku.edu.cn/cpp2010

  2. 链 表 1) 链表的结构和特点 2) 单链表的操作:插入、删除算法

  3. 单链表 单链表的概念: 单链表(singly linked list)是一种简单的链表表示,也称为线性链表。是由结点组成。 一个结点由两个域组成: ①数据域 ②指向下一结点的指针域

  4. 特点 • 每个元素(表项)由结点(Node)构成。 线性结构 • 结点可以不连续存储 • 表可扩充

  5. 单链表中的插入与删除 在一个线性单链表中插入结点的情况有三种

  6. 带表头结点的单链表 表头结点位于表的最前端,本身不带数据,仅标志表头。 设置表头结点的目的是统一空表与非空表的操作,简化链表操作的实现。

  7. 从带表头结点的单链表中删除第一个结点 q = p→link; p→link = q→link; delete q; if ( p→link == NULL ) last = p;

  8. 带表头结点的单链表的实现 //节点数据结构 struct LNode { int data; struct LNode * next; }; //链表数据结构 struct LinkList { LNode * pHead; //表头指针 LNode * pCurrent; //当前节点指针 int nLength; //表中节点数目(不含头节点) }; //每个该结构的变量代表一张链表

  9. 用来对链表进行操作的函数: 初始化(创建头节点): bool InitList ( LinkList * pThis ); 销毁整个链表,包括销毁头节点: bool DestroyList (LinkList * pThis); 删除全部节点,但头节点保留: bool ClearList(LinkList * pThis); 判断链表是否为空: bool IsEmpty (LinkList * pThis); 取链表长度: int GetLength (LinkList * pThis); 获取指向第position个节点的指针(position从1开始算) LNode * GetNode(LinkList * pThis, int position); 取值为elem的第一个元素的位置(返回0则没找到) int LocateElem(LinkList * pThis,int elem);

  10. 用来对链表进行操作的函数: 将第 position 个节点的值设为 newData bool SetNodeData(LinkList * pThis,int position, int newData); 获取第 position 个节点的值 bool GetNodeData(LinkList * pThis,int position, int &data); 将一个值为data的节点插入到第beforeWhich个节点前面 (beforeWhich的取值是 1 到 pThis->nLength + 1) bool InsertNode(LinkList * pThis,int beforeWhich, int data); 删除第position 个节点 bool DeleteNode(LinkList * pThis,int position); 使第一个节点成为当前节点 void Rewind(LinkList * pThis); 取当前节点的值,并移动到下一个节点 bool GetNextNodeData( LinkList * pThis, int * pData)

  11. //初始化,分配一个头节点。 bool InitList( LinkList * pThis ) { if (!( pThis->pHead = new LNode)) { return false; } pThis->pHead->next = NULL; pThis->nLength = 0; pThis->pCurrent = pThis->pHead->next ; return true; } //销毁链表。 bool DestroyList(LinkList * pThis) { if (!ClearList( pThis)) { return false; } delete pThis->pHead; return true; }

  12. //使第1个节点成为当前节点 void Rewind(LinkList * pThis) { pThis->pCurrent = pThis->pHead->next ; } //取当前节点数据,并移动到下一个节点 bool GetNextNodeData( LinkList * pThis, int * pData) { if( pThis->pCurrent == NULL ) return false; * pData = pThis->pCurrent->data; pThis->pCurrent= pThis->pCurrent->next ; return true; } //判断链表是否为空。若为空,返回true,否则返回false。 bool IsEmpty(LinkList * pThis) { if ( pThis->pHead->next == NULL) return true; return false; }

  13. //返回链表中 的 当前节点数。 int GetLength(LinkList * pThis) { return pThis->nLength; } //将链表清空,释放当前所有节点(不包括头节点)。 bool ClearList(LinkList * pThis) { if (pThis->pHead == NULL) { return false; } LNode *pTemp = NULL; while (pThis->pHead->next != NULL) { pTemp = pThis->pHead->next; pThis->pHead->next = pTemp->next; delete pTemp; } pThis->nLength = 0; return true; }

  14. //得到指定位置节点的数据。 //节点索引从1到listLength。 bool GetNodeData(LinkList * pThis,int position, int &data) { LNode *pTemp = GetNode(pThis,position); if (!pTemp) return false; data = pTemp->data; return true; }

  15. //将position指定的节点内的数据设置为newData。 //第一个有效节点的position为1。 bool SetNodeData(LinkList * pThis,int position, int newData) { LNode *pTemp = GetNode(pThis,position); if (! pTemp) return false; pTemp->data = newData; return true; }

  16. //在链表中插入一个节点。 //插入的位置由beforeWhich指定,新节点插入在beforeWhich之前 //beforeWhich的取值在1到ListLength+1之间。 bool InsertNode(LinkList * pThis,int beforeWhich, int data) { if (beforeWhich < 1 || beforeWhich > (pThis->nLength + 1)) { return false; } LNode *pTemp = GetNode(pThis, beforeWhich - 1); if (!pTemp) return false; LNode *newNode = new LNode; newNode->data = data; newNode->next = pTemp->next; pTemp->next = newNode; pThis->nLength++; return true; }

  17. //删除一个指定的节点 , 节点位置由position指定。 //positon的值从1到listLength。 //若链表为空或指定的节点不存在则返回false。 bool DeleteNode(LinkList * pThis,int position) { if (position < 1 || position > pThis->nLength) { return false; } LNode *pTemp = GetNode(pThis,position - 1); if (!pTemp) return false; LNode *pDel = NULL; pDel = pTemp->next; pTemp->next = pDel->next; delete pDel; pThis->nLength--; return true; }

  18. //得到指定位置节点的指针。 LNode * GetNode( LinkList * pThis,int position) { LNode *pTemp = NULL; int curPos = -1; pTemp = pThis->pHead; while (pTemp != NULL) { curPos++; if (curPos == position) break; pTemp = pTemp->next; } if (curPos != position) return 0; return pTemp; }

  19. //定位与指定数据相等的数据节点。 //如果在当前链表中已经存在该数据则返回该数据节点的位置。 //若不存在这样的节点则返回0。 int LocateElem(LinkList * pThis,int elem) { LNode *pTemp = NULL; int curIndex = 1; pTemp = pThis->pHead->next; while ((pTemp != NULL) && (pTemp->data != elem)) { pTemp = pTemp->next; curIndex++; } if (pTemp == NULL) return 0; return curIndex; }

  20. int main(){ LinkList MyList; //要再使用一张链表,则可以定义 LinkList MyList2; InitList ( & MyList); InsertNode(& MyList, 1, 10);InsertNode( & MyList, 2, 20); InsertNode(& MyList, 3, 30);InsertNode(& MyList, 4, 40); cout << GetLength(& MyList) << endl; int dataTemp = 0; for (int i = 1; i <= GetLength(& MyList); i++) { GetNodeData(& MyList,i, &dataTemp); cout << dataTemp << endl; } if (SetNodeData(& MyList,3, 50)) { cout <<"DONE\n"; } else { cout << "Failed\n"; }

  21. Rewind( & MyList); while (GetNextNodeData( & MyList,&dataTemp)) cout << dataTemp << endl; if (DeleteNode(& MyList,4)) { cout <<"DONE\n"; } else cout << "Failed\n"; for (i = 1; i <= GetLength(& MyList); i++) { GetNodeData(& MyList,i, &dataTemp); cout << dataTemp << endl; } cout << LocateElem(&MyList,40) << endl; DeleteNode(&MyList,LocateElem( & MyList,50)); DeleteNode(&MyList,LocateElem( & MyList,20)); Rewind( & MyList); while (GetNextNodeData( & MyList,&dataTemp)) cout << dataTemp << endl; return 0; }

  22. 输出: 4 10 20 30 40 DONE 10 20 50 40 DONE 10 20 50 0 10

  23. 静态链表结构 利用数组定义,运算过程中存储空间大小不变 avil 代表下一个空的元素的位置

  24. 静态链表结构 静态链表适用于没有指针的程序设计语言 或既需要依据下标快速随机存取元素,又需要 维持另一种先后关系的情况

  25. 循环链表 (Circular List) 循环链表是单链表的变形。 循环链表最后一个结点的 link 指针不 为 0 (NULL),而是指向了表的前端。 为简化操作,在循环链表中往往加入表头结点。 循环链表的特点是:只要知道表中某一结点的地址,就可搜寻到所有其他结点的地址。

  26. 循环链表的示例 带表头结点的循环链表

  27. 循环链表的插入运算

  28. 单链表的应用举例 - 多项式相加 在多项式的链表表示中每个结点增加了一个数据成员link,作为链接指针。 • 优点是: • 多项式的项数可以动态地增长,不存在存储溢出问题。 • 插入、删除方便,不移动元素。

  29. 多项式链表的相加 设多项式的最高阶数为maxdegree当前的最高阶为n,各个项按指数递增的次序,从0~n排列。这里举出两种不同的表示方法: 第一种表示方法:建立一个具有maxdegree+1个元素的静态数组coef来存储多项式的系数: float coef[maxdegree+1]; //多项式的系数数组 特点:直观,但是空间浪费

  30. 多项式链表的相加 第二种表示方法:利用链表来表示多项式。它适合与项数不定的多项式,对于项数在运算过程中动态增长的多项式,不存在存储溢出的问题。其次对某些非零系数项,在执行加法后变成零系数项,这就需要在结果多项式中删去这些项,利用链表可以很方便的修改指针以完成这种插入和删除操作。

  31. 多项式链表的相加 AH = 1 - 10x6 + 2x8 +7x14 BH = - x4 + 10x6 - 3x10 + 8x14 +4x18

  32. 树和二叉树 树tree的定义 (1) 无结点的树 空树 (2) 非空树 仅有一个根结点 其余结点分为若干 互不相交的子树

  33. A ········································根 3度 D B C H I J ·············叶 0度 E F G K L M 2个3度点 A,D 2个2度点 B,E 2个1度点 C,H 7个叶 F,G,I,J,K,L,M

  34. A ·············································第一层 ······························第二层 D B C ···············第三层 H I J E F G K L ····································第四层 M 树的深度为4

  35. 二叉树 每个结点至多有两棵子树,1度或2度 A C B F G D E H I J

  36. 二叉树 设有n个2度点 则有n+1个叶片 A C B F G D E H I J 第i层结点至多2i-1个(i从1开始算) 深度为k,结点至多2k-1个

  37. 满二叉树 深度为k,结点数2k-1,每层结点数都最大的树 A C B F G D E H J K L M N O I

  38. 完全二叉树 满二叉树去掉最下层最右边若干结点 A C B F G D E H J K L M N O I 满二叉树也是完全二叉树

  39. 完全二叉树 n个结点,深度 k=[log2n] +1 A n个结点,深度为k: 2k-1-1<n≤2k-1 2k-1 ≤ n <2k k-1 ≤ log2n <k k-1 = [log2n] k = [log2n] +1 C B F G D E H I J K L M 1个结点深度为1 2-3个结点深度为2 4-7个结点深度为3 8-15个结点深度为4

  40. 完全二叉树 结点i的左子结点是2i 右子结点是2i+1 (i从1开始算) 1 3 2 6 7 4 5 8 9 10 11 12 13 结点i的父结点是[i/2] 完全二叉树可以用一维数组存储

  41. 二叉树的链式存储 树结点

  42. A C B E F D G H

  43. 二叉树的遍历 先序遍历 PreOrderTraverse 先访问根结点 再访问左子树 后访问右子树 中序遍历 InOrderTraverse 先访问左子树 再访问根结点 后访问右子树 后序遍历 PostOrderTraverse 先访问左子树 再访问右子树 后访问根结点

  44. 假设二叉树的节点形式为: struct CharNode { char data; CharNode * left; CharNode * right; }; 构建如图的二叉树 并用三种方法遍历,最后 销毁该树 A C B F D E G H I

  45. 先序遍历 void PreOrder (CharNode * root, void (*visit)( char & item) ) { if (root) { visit(root->data); // visit the node PreOrder (root->left, visit); // descend left PreOrder (root->right, visit); // descend right } } pa A C B void PrintChar(char & c) { cout<< c;} F D E 则 Preorder(pa,PrintChar); 输出:ABDEGCFHI G H I

  46. 中序遍历 void MiddleOrder (CharNode * root, void (*visit)( char & item) ) { if (root) { MiddleOrder (root->left, visit); visit(root->data); MiddleOrder (root->right, visit); } } A C B F D E DBGEACHFI G H I

More Related