1 / 71

第九章 群体类

第九章 群体类. C++ 语言程序设计. 清华大学计算机与信息管理中心 郑 莉. 线性群体 线性群体的概念 直接访问群体 -- 数组类 顺序访问群体 -- 链表类 栈类 队列类. 本章主要内容. 群体的概念. 群体 是指由多个数据元素组成的集合体。群体可以分为两个大类: 线性群体 和 非线性群体 。 线性群体中的元素按位置排列有序,可以区分为第一个元素、第二个元素等。 非线性群体不用位置顺序来标识元素。. …. 第一个元素. 第二个元素. 第三个元素. 最后一个元素. 线性群体的概念.

amal-riggs
Download Presentation

第九章 群体类

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. 第九章 群体类 C++语言程序设计 清华大学计算机与信息管理中心 郑 莉

  2. 线性群体 线性群体的概念 直接访问群体--数组类 顺序访问群体--链表类 栈类 队列类 本章主要内容

  3. 群体的概念 群体是指由多个数据元素组成的集合体。群体可以分为两个大类:线性群体和非线性群体。 线性群体中的元素按位置排列有序,可以区分为第一个元素、第二个元素等。 非线性群体不用位置顺序来标识元素。

  4. 第一个元素 第二个元素 第三个元素 最后一个元素 线性群体的概念 线性群体中的元素次序与其位置关系是对应的。在线性群体中,又可按照访问元素的不同方法分为直接访问、顺序访问和索引访问。 在本章我们只介绍直接访问和顺序访问。

  5. 数组 直接访问线性群体 • 静态数组是具有固定元素个数的群体,其中的元素可以通过下标直接访问。 • 缺点:大小在编译时就已经确定,在运行时无法修改。 • 动态数组由一系列位置连续的,任意数量相同类型的元素组成。 • 优点:其元素个数可在程序运行时改变。

  6. 动态数组类模板 直接访问线性群体 • 数组类模板:例9.1(9_1.h)

  7. #ifndef ARRAY_CLASS #define ARRAY_CLASS #include <iostream.h> #include <stdlib.h> #ifndef NULL const int NULL = 0; #endif // NULL enum ErrorType { invalidArraySize, memoryAllocationError, indexOutOfRange }; char *errorMsg[] = { "Invalid array size", "Memory allocation error", "Invalid index: " };

  8. template <class T> class Array { private: T* alist; int size; void Error(ErrorType error,int badIndex=0) const; public: Array(int sz = 50); Array(const Array<T>& A); ~Array(void); Array<T>& operator= (const Array<T>& rhs); T& operator[](int i); operator T* (void) const; int ListSize(void) const; void Resize(int sz); };

  9. 数组类模板部分成员函数的实现 // 构造函数 template <class T> Array<T>::Array(int sz) { if (sz <= 0) //sz为数组大小(元素个数),若小于0,则输出错误信息 Error(invalidArraySize); size = sz; // 将元素个数赋值给变量size alist = new T[size]; //动态分配size个T类型的元素空间 if (alist == NULL) //如果分配内存不成功,输出错误信息 Error(memoryAllocationError); }

  10. 拷贝构造函数 template <class T> Array<T>::Array(const Array<T>& X) { int n = X.size; size = n; alist = new T[n]; if (alist == NULL) Error(memoryAllocationError); T* srcptr = X.alist; // X.alist是对象X的数组首地址 T* destptr = alist; // alist是本对象中的数组首地址 while (n--) // 逐个复制数组元素 *destptr++ = *srcptr++; }

  11. A的数组元素 占用的内存 A的数组元素 占用的内存 A A alist size alist size B alist size 拷贝前 拷贝后 浅拷贝 template <class T> Array<T>::Array( const Array<T>& X) { size = X.size; alist= X.alist; } void main(void) { Array<int> A(10); ...... Array<int> B(A); ...... }

  12. A的数组元素 占用的内存 A alist size A的数组元素 占用的内存 A alist size B alist size B的数组元素 占用的内存 拷贝前 拷贝后 深拷贝

  13. 重载"="运算符 template <class T> Array<T>& Array<T>::operator= (const Array<T>& rhs) { int n = rhs.size; if (size != n) { delete [] alist; alist = new T[n]; if (alist == NULL) Error(memoryAllocationError); size = n; } T* destptr = alist; T* srcptr = rhs.alist; while (n--) *destptr++ = *srcptr++; return *this; }

  14. 重载下标操作符 template <class T> T& Array<T>::operator[] (int n) { // 检查下标是否越界 if (n < 0 || n > size-1) Error(indexOutOfRange,n); // 返回下标为n的数组元素 return alist[n]; }

  15. 为什么有的函数返回引用 • 如果一个函数的返回值是一个对象的值,它就被认为是一个常量,不能成为左值。 • 如果返回值为引用。由于引用的实质就是对象的地址,所以通过引用当然可以改变对象的值。

  16. 重载指针转换操作符 template <class T> Array<T>::operator T* (void) const { // 返回当前对象中私有数组的首地址 return alist; }

  17. #include <iostream.h> void main() { int a[10]; void read(int *p, int n); read(a, 10); } void read(int *p, int n) { for (int i=0; i<n; i++) cin>>p[i]; } void main() { Array<int> a(10); void read(int *p, n); read(a, 10); } void read(int *p, int n) { for (int i=0; i<n; i++) cin>>p[i]; } 指针转换运算符的作用

  18. Array类的应用 直接访问线性群体 • 例9.2 求范围2~N中的质数,N在程序运行时由键盘输入。

  19. #include <iostream.h> #include <iomanip.h> #include "9_1.h" void main(void) { Array<int> A(10); int n; int primecount = 0, i, j; cout << "Enter a value >= 2 as upper limit for prime numbers: "; cin >> n; A[primecount++] = 2; // 2是一个质数

  20. for(i = 3; i < n; i++) { if (primecount == A.ListSize()) A.Resize(primecount + 10); if (i % 2 == 0) continue; j = 3; while (j <= i/2 && i % j != 0) j += 2; if (j > i/2) A[primecount++] = i; } for (i = 0; i < primecount; i++) { cout << setw(5) << A[i]; if ((i+1) % 10 == 0) cout << endl; } cout << endl; }

  21. 链表 顺序访问线性群体 • 链表是一种动态数据结构,可以用来表示顺序访问的线性群体。 • 链表是由系列结点组成的,结点可以在运行时动态生成。 • 每一个结点包括数据域和指向链表中下一个结点的指针(即下一个结点的地址)。如果链表每个结点中只有一个指向后继结点的指针,则该链表称为单链表。

  22. head rear data1 data2 data3 datan NULL 单链表 顺序访问线性群体

  23. 单链表的结点类模板(例9-3) 顺序访问线性群体 template <class T> class Node { private: Node<T> *next; public: T data; Node(const T& item,Node<T>* ptrnext = NULL); void InsertAfter(Node<T> *p); Node<T> *DeleteAfter(void); Node<T> *NextNode(void) const; };

  24. data2 在结点之后插入一个结点 顺序访问线性群体 … data1 … data p template <class T> void Node<T>::InsertAfter(Node<T> *p) { //p节点指针域指向当前节点的后继节点 p->next = next; next = p; //当前节点的指针域指向p }

  25. data2 data3 删除结点之后的结点 顺序访问线性群体 data1 … … tempPtr Node<T> *Node<T>::DeleteAfter(void) { Node<T> *tempPtr = next; if (next == NULL) return NULL; next = tempPtr->next; return tempPtr; }

  26. 链表的基本操作 顺序访问线性群体 • 生成结点 • 输出链表 • 查找结点 • 插入结点 • 删除结点 • 清空链表

  27. 实现链表操作函数(例9-4) 顺序访问线性群体 #ifndef NODE_LIBRARY #define NODE_LIBRARY #include <iostream.h> #include <stdlib.h> #include "9_3.h"

  28. // 生成节点 template <class T> Node<T> *GetNode(const T& item, Node<T> *nextPtr = NULL) { Node<T> *newNode; newNode = new Node<T>(item, nextPtr); if (newNode == NULL) { cerr << "Memory allocation failure!" << endl; exit(1); } return newNode; }

  29. enum AppendNewline {noNewline,addNewline}; // 输出链表 template <class T> void PrintList(Node<T> *head, AppendNewline addnl = noNewline) { Node<T> *currPtr = head; while(currPtr != NULL) { if(addnl == addNewline) cout << currPtr->data << endl; else cout << currPtr->data << " "; currPtr = currPtr->NextNode(); } }

  30. //查找节点 template <class T> int Find(Node<T> *head, T& item, Node<T>* &prevPtr) { Node<T> *currPtr = head; prevPtr = NULL; while(currPtr != NULL) { if (currPtr->data == item) return 1; prevPtr = currPtr; currPtr = currPtr->NextNode(); } return 0; }

  31. //在表头插入节点 template <class T> void InsertFront(Node<T>* & head, T item) { head = GetNode(item,head); } //在表尾添加节点 template <class T> void InsertRear(Node<T>* & head, const T& item) { Node<T> *newNode, *currPtr = head; if (currPtr == NULL) InsertFront(head,item); else { while(currPtr->NextNode() != NULL) currPtr = currPtr->NextNode(); newNode = GetNode(item); currPtr->InsertAfter(newNode); } }

  32. // 删除链表的第一个节点 template <class T> void DeleteFront(Node<T>* & head) { Node<T> *p = head; if (head != NULL) { head = head->NextNode(); delete p; } }

  33. // 删除链表中第一个数据域等于key的节点 template <class T> void Delete (Node<T>* & head, T key) { Node<T> *currPtr = head, *prevPtr = NULL; if (currPtr == NULL) return; while (currPtr != NULL && currPtr->data != key) { prevPtr = currPtr; currPtr = currPtr->NextNode(); } if (currPtr != NULL) { if(prevPtr == NULL) head = head->NextNode(); else prevPtr->DeleteAfter(); delete currPtr; } }

  34. // 在有序链表中插入一个节点 template <class T> void InsertOrder(Node<T>* & head, T item) { Node<T> *currPtr, *prevPtr, *newNode; prevPtr = NULL; currPtr = head; while (currPtr != NULL) { if (item < currPtr->data) break; prevPtr = currPtr; currPtr = currPtr->NextNode(); } if (prevPtr == NULL) InsertFront(head,item); else { newNode = GetNode(item); prevPtr->InsertAfter(newNode); } }

  35. //清空链表--删除链表中的所有节点 template <class T> void ClearList(Node<T> * &head) { Node<T> *currPtr, *nextPtr; currPtr = head; while(currPtr != NULL) { nextPtr = currPtr->NextNode(); delete currPtr; currPtr = nextPtr; } head = NULL; } #endif // NODE_LIBRARY

  36. 链表应用举例 顺序访问线性群体 • 例9.5 从键盘输入10个整数,用这些整数值作为结点数据,生成一个链表,按顺序输出链表中结点的数值。然后从键盘输入一个待查找整数,在链表中查找该整数,若找到则删除该整数所在的结点(如果出现多次,全部删除),然后输出删除结点以后的链表。在程序结束之前清空链表。 9-5.cpp

  37. #include <iostream.h> #include "9_3.h" #include "9_4.h" void main(void) { Node<int> *head = NULL, *prevPtr, *delPtr; int i, key, item; for (i=0;i < 10;i++) { cin>>item; InsertFront(head, item); } cout << "List: "; PrintList(head,noNewline); cout << endl;

  38. cout << "请输入一个需要删除的整数: "; cin >> key; prevPtr = head; while (Find(head,key,prevPtr) != NULL) { if(prevPtr == NULL) head = head->NextNode(); else delPtr=prevPtr->DeleteAfter(); delete delPtr; } cout << "List: "; PrintList(head,noNewline); cout << endl; ClearList(head); }

  39. 链表类模板(例9-6) 顺序访问线性群体 //9_6.h #ifndef LINKEDLIST_CLASS #define LINKEDLIST_CLASS #include <iostream.h> #include <stdlib.h> #ifndef NULL const int NULL = 0; #endif // NULL #include "9_3.h"

  40. template <class T> class LinkedList { private: Node<T> *front, *rear; Node<T> *prevPtr, *currPtr; int size; int position; Node<T> *GetNode(const T& item, Node<T> *ptrNext=NULL); void FreeNode(Node<T> *p); void CopyList(const LinkedList<T>& L);

  41. public: LinkedList(void); LinkedList(const LinkedList<T>& L); ~LinkedList(void); LinkedList<T>& operator= (const LinkedList<T>& L); int ListSize(void) const; int ListEmpty(void) const; void Reset(int pos = 0); void Next(void); int EndOfList(void) const; int CurrentPosition(void) const;

  42. void InsertFront(const T& item); void InsertRear(const T& item); void InsertAt(const T& item); void InsertAfter(const T& item); T DeleteFront(void); void DeleteAt(void); T& Data(void); void ClearList(void); }; #endif // LINKEDLIST_CLASS

  43. 链表类应用举例(例9-7) 顺序访问线性群体 #include <iostream.h> #include "9_6.h" #include "9_6.cpp" void main(void) { LinkedList<int> Link; int i, key, item; for (i=0;i < 10;i++) { cin>>item; Link.InsertFront(item); }

  44. cout << "List: "; Link.Reset(); while(!Link.EndOfList()) { cout <<Link.Data() << " "; Link.Next(); } cout << endl; cout << "请输入一个需要删除的整数: "; cin >> key; Link.Reset();

  45. while (!Link.EndOfList()) { if(Link.Data() == key) Link.DeleteAt(); Link.Next(); } cout << "List: "; Link.Reset(); while(!Link.EndOfList()) { cout <<Link.Data() << " "; Link.Next(); } cout << endl; }

  46. 入栈 出栈 an ┆ a2 a1 栈顶 栈底 特殊的线性群体——栈 栈是只能从一端访问的线性群体,可以访问的这一端称栈顶,另一端称栈底。

  47. 入栈 出栈 ④ 参数 当前现场 返回地址 参数 main{} 调fun(参数) 结束 fun(参数) 返回 ③ 当前现场 返回地址 ② ① ⑤ 出栈 ⑧ ⑦ ⑥ 当前现场 返回地址 栈的应用举例——函数调用 特殊的线性群体——栈

  48. b a / t1 + d c t1 * + t1=a/b a/b+c*d a/b+c*d a/b+c*d (a) (b) (c) t2 t1 + t3 t2=c*d t3=t1+t2 a/b+c*d a/b+c*d (d) (e) 栈的应用举例——表达式处理 特殊的线性群体——栈

  49. 栈的基本状态 特殊的线性群体——栈 • 栈空 • 栈中没有元素 • 栈满 • 栈中元素个数达到上限 • 一般状态 • 栈中有元素,但未达到栈满状态

  50. 入栈 出栈 入栈 出栈 数组下标 数组下标 max n 1 0 max n 1 0 ┆ an ┆ a1 a0 栈顶 栈顶 初始状态(栈空) 一般状态 入栈 出栈 数组下标 amax ┆ an ┆ a1 a0 max n 1 0 栈顶 栈满状态

More Related