1 / 62

第九章 群体类

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

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

  10. #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是一个质数

  11. 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; }

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

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

  14. 例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; }; //实现略

  15. data2 在结点p之后插入一个结点 顺序访问线性群体 … data1 … p q data

  16. data2 data3 删除p结点之后的结点 顺序访问线性群体 data1 … … p q

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

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

  19. // 生成节点 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; }

  20. 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( ); } }

  21. //查找节点 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; }

  22. //在表头插入节点 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); } }

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

  24. // 删除链表中第一个数据域等于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; } }

  25. // 在有序链表中插入一个节点 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); } }

  26. //清空链表--删除链表中的所有节点 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

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

  28. #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;

  29. 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); }

  30. 例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"

  31. 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);

  32. 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;

  33. 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

  34. 例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); }

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

  36. 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; }

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

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

  39. 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) 栈的应用举例——表达式处理 特殊的线性群体——栈

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

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

  42. 栈的基本操作 特殊的线性群体——栈 • 初始化 • 入栈 • 出栈 • 清空栈 • 访问栈顶元素 • 检测栈的状态(满、空)

  43. 例9-8 栈类模板 特殊的线性群体——栈 //9-8.h #ifndef STACK_CLASS #define STACK_CLASS #include <iostream.h> #include <stdlib.h> const int MaxStackSize = 50;

  44. template <class T> class Stack { private: T stacklist[MaxStackSize]; int top; public: Stack (void); void Push (const T& item); T Pop (void); void ClearStack(void); T Peek (void) const; int StackEmpty(void) const; int StackFull(void) const; }; //类的实现略

  45. 栈的应用 特殊的线性群体——栈 • 例9.9 一个简单的整数计算器 实现一个简单的整数计算器,能够进行加、减、乘、除和乘方运算。使用时算式采用后缀输入法,每个操作数、操作符之间都以空白符分隔。例如,若要计算"3+5"则输入"3 5 +"。乘方运算符用"^"表示。每次运算在前次结果基础上进行,若要将前次运算结果清除,可键入"c"。当键入"q"时程序结束。 • 9-9.h 9-9.cpp

  46. //9_9.h #include <iostream.h> #include <math.h> #include <stdlib.h> #include <string.h> enum Boolean {False, True}; #include "9_8.h"

  47. class Calculator { private: Stack<int> S; void Enter(int num); Boolean GetTwoOperands(int& opnd1, int& opnd2); void Compute(char op); public: Calculator(void) {} void Run(void); void Clear(void); };

  48. void Calculator::Enter(int num) { S.Push(num); } Boolean Calculator::GetTwoOperands (int& opnd1, int& opnd2) { if (S.StackEmpty( )) { cerr << "Missing operand!" << endl; return False; } opnd1 = S.Pop( ); if (S.StackEmpty( )) { cerr << "Missing operand!" << endl; return False; } opnd2 = S.Pop( ); return True; }

  49. void Calculator::Compute(char op) { Boolean result; int operand1, operand2; result = GetTwoOperands(operand1, operand2); if (result == True) { switch(op) { case '+': S.Push(operand2+operand1); break; case '-': S.Push(operand2-operand1); break; case '*': S.Push(operand2*operand1); break;

  50. case '/': if (operand1 == 0) { cerr << "Divide by 0!" << endl; S.ClearStack( ); } else S.Push(operand2/operand1); break; case '^': S.Push(pow(operand2,operand1)); break; } cout<<'='<<S.Peek( )<<' '; } else S.ClearStack( ); }

More Related