烟雨科技有限责任公司     数据结构用面向对象方法与C++语言描述     第二版    殷人昆编著   清华大学出版社      课件        本课件由迷若烟雨提供
课程介绍1. 为什么要学习数据结构?2. 该课程的主要内容是什么?如何学习?3. 考核方式?    总成绩=平时( 40% )+期末(60%)    平时=上机实习+平时作业+上课回答问题       +4次课内考试(每次5分)4. 教材:殷人...
数据结构          数据(的集合)             结构          逻辑结构                     物理结构线         树       图       集        连   非性      ...
如何学习?1. 预习:粗读教材,发现问题2. 听课:重点、难点,初步解决问题3. 复习:细读教材,解决问题(理解抽象数   据类型、看懂C++类的定义和实现)4. 做题:巩固知识(C++类的定义和实现)5. 实习:验证(调试程序)
上机实习作业:1.   顺序表及其应用2.   链表及其应用.doc3.   栈及其应用4.   队列及其应用5.   稀疏矩阵及其基本操作6.   二叉树及其基本操作7.   堆及其基本操作8.   Huffman树及其基本操作9.   图及...
第一章   绪论内容提要:  本章主要介绍数据结构的基本概念、术语,以及有关算法的描述、分析的基本常识。
§1.1 数据结构的概念            第一章 绪论1.1.1 为什么要讨论数据结构?通过前面的学习,我们知道:  计算机科学是研究信息表示和信息处理的科学;  信息是当今社会的重要资源;  计算机产业及计算机科学技术飞速发展,计算机的...
§1.1 数据结构的概念           第一章 绪论解决思路1:发展硬件技术,开发出更快、更高级的硬件产品,但有没有其它途径呢?思路2:  将一“大堆杂乱无章”的数据交给计算机处理是很不明智的,结果是加工处理的效率会非常低,有时甚至根本无...
线性      例1 书目自动检索系统                      表                                          书目文件                 书目卡片        001  ...
例1 书目自动检索系统 问题:图书管理,完成书目的自动检索。 数据:一本本书籍,更确切地说是每本书的 信息,如:书名、作者、出版社、出版日期、 书号、内容提要等等。 操作:书目检索、入库、借书、还书等。 涉及到:书目数据逻辑组织、存储方...
例2 人机对奕问题(井字棋)                  树……..                               ……..       …...   …...   …...   …...
例2 人机对奕问题 问题:人机对弈,即人与计算机下棋。 数据:各种棋局状态,确切说就是描述棋盘 格局的信息。 操作:走棋,即选择一种策略使棋局状态发 生变化(由一个格局派生出另一个格局) 涉及到:“格局‖数据逻辑组织、存储方法;走 棋操...
例3 多叉路口交通灯管理问题               图      AB   AC   AD                         C                             DBA    BC   BD     ...
例3 多叉路口交通灯管理问题 问题:多叉路口的交通灯管理,即在多 叉路口应怎样设置交通灯,以保证交通 畅通。 数据:路口各条道路的信息 操作:设置信号灯(求出各个可以同时 通行的路的集合) 涉及到:路口各条道路信息数据及其关 系的逻辑组...
§1.1 数据结构的概念                            第一章 绪论 1.1.2 数据结构及其术语 [数据 Data] 用于描述客观事物的数值、字符等一切可以输 入到计算机中,并由计算机加工处理的符号集合。 [数据元素 ...
§1.1 数据结构的概念             第一章 绪论 数据结构=数据+结构      记作 Data_Structure=(D,R) 其中:Data_Structure是数据结构的名称    D是数据元素的有限集合(一般为一个数据对象...
§1.1 数据结构的概念           第一章 绪论[数据的存储结构]:是数据结构在计算机内的表示,它包括数据元素的表示和关系的表示。数据在计算机中的存储:两种形式 连续:数据元素逐个连续存放(通过物理相邻来表示关系) 非连续:数据元素不...
§1.2 数据结构的抽象形式          第一章 绪论1.2.1 数据类型 Data Type    一个值的集合和定义在这个值集上的一组操作的总称。(1) 高级语言中的数据类型实际上包括:数据的逻辑结构、数据的存储结构及所定义的操作的实...
§1. 2 数据结构的抽象形式          第一章 绪论抽象数据类型=数学模型+操作    (其它教材上对ADT的描述)        =数据结构+操作        =数据+结构+操作  它是一种描述用户和数据之间接口的抽象模型,亦即它...
内容回顾:1.数据结构=数据+结构=(D,R)2.结构包括:逻辑结构:集合、线性、树型、图物理结构(存储结构):连续、非连续3.数据类型:(D,R,P)4.抽象数据类型: (D,R,P)
§1.3 作为ADT的C++类              第一章 绪论 1.3.1 面向对象的概念 面向对象 = 对象+类+继承+通信  对象:在应用问题中出现的各种实体、事件、规  格说明等。由一组属性值和在这组值上的一组服  务(或称操作)...
继承:是面向对象方法最有特色的方面。  • 派生类:载重车,轿车,摩托车,…     子类、特化类(特殊化类)  • 基类:车辆     父类、超类、泛化类(一般化类)     各派生类中的公共部分,包括属性和服务,集    中到基类中,派生类...
§ 1.3 作为ADT的C++类                    第一章 绪论  传统的大型结构化程序设计是面向过程的,首先着眼于系统要实现的功能。一个数据结构可能被多个过程调用,修改数据结构,意味着必须修改相关的过程,这样做烦琐又容易产...
§ 1.3 作为ADT的C++类       第一章 绪论 1.3.2 C++中的类 例1:计算圆的周长和面积  圆是平面上与圆心等距离的所有点的集合。从图形显示角 度看,圆的抽象数据类型包括圆心和半径;而从计量角度看, 它所需要的抽象数据类型...
§ 1.3 作为ADT的C++类                               第一章 绪论问题描述:给定圆的半径,求其周长和面积 ADT定义:    ADT circle       data : 0 r ,实数      st...
§1.3 作为ADT的C++类                   第一章 绪论例2:掷色子游戏。问题描述:每次掷出N个色子,统计每个色子的点数和每次的总        点数,看看谁的高。问题分析:该问题的数据包括色子数、每个色子的点数和总点数...
§ 1.3 作为ADT的C++类                                第一章 绪论例3:复数的运算问题描述:在高级语言中,没有复数类型,但是我们可以借助已     有的数据类型解决复数类型的问题,如复数运算。ADT定义...
§1.3.2 C++中的类               第一章 绪论  C++对于面向对象程序设计的支持,核心部分就是类的定义,类的定义体现了抽象数据类型的思想:   •说明与实现的分离   •信息隐蔽对类的成员规定三级存取: • 公共(pub...
§1.4 算法定义                 第一章 绪论  1.算法的定义和特性   算法 (Algorithm ): 算法是解决某一特定任    务的指令的有限序列。   算法具有以下五个特性:    (1)有穷性 一个算法必须总...
§1.4 算法定义           第一章 绪论 (3)可行性 一个算法是可行的。即算法描述的  操作都是可以通过已经实现的基本运算执行有  限次来实现的。 (4)输入 一个算法有零个或多个输入,这些输  入取自于某个特定的对象集合。 (5...
§1.4 算法定义              第一章 绪论2. 算法与数据结构的关系   算法+数据结构=程序    在计算机解决问题的过程中算法与数据结构是缺一不  可的两个方面:    问题的数据组织——数据结构    数据的处理——算法 ...
怎样才能设计一个好的算法呢? 要设计一个好的算法通常要考虑以下的要求:⑴正确: 算法的执行结果应当满足预先规定的功能和性能 要求。⑵可读: 一个算法应当思路清晰、层次分明、简单明了、 易读易懂。⑶健壮:当输入不合法数据时,应能作适当处理,不至引...
§1.5 算法的性能分析与度量             第一章 绪论1. 5.1 算法的描述 1. 自然语言描述:容易,但有时罗嗦、有二义性 2. 图示(流程图、N-S图、PAD图等):直观清晰,但不易实现 3. 程序设计语言:可以直接运行,但...
§1.5 算法的性能分析与度量          第一章 绪论 如何评价算法的优劣呢?   我们可以从一个算法的时间复杂度与空间复杂度来评价算 法的优劣。   一个算法转换成程序并在计算机上执行时,其运行所需要 的时间取决于下列因素: ⑴硬件的...
§1.5 算法的性能分析与度量        第一章 绪论   显然,在各种因素都不能确定的情况下,很难比较出算法 的执行时间。   也就是说,使用执行算法的绝对时间来衡量算法的效率是 不合适的。为此,可以将上述各种与计算机相关的软、硬件因 素...
§1.5 算法的性能分析与度量         第一章 绪论 1.5.2 算法效率的度量  先验估计(事前估计):根据算法的逻辑特征(基本操作的             次数)来估算。  后期测试(事后计算):选择样本数据、运行环境,运行算法 ...
§1.5 算法的性能分析与度量              第一章 绪论 算法复杂性的度量属于事前估计。分为:   时间复杂度和空间复杂度的度量。 时间复杂度:   一个程序的时间复杂度(Time complexity)是指程序运行 从开始到结束...
§1.5 算法的性能分析与度量            第一章 绪论1.5.4 O表示的含义——算法的渐进分析 1. 算法的渐进分析    算法的渐进分析简称算法分析。通常将问题的规模作为   分析的参数,求算法的时间开销与问题规模n的关系。  ...
§1.5 算法的性能分析与度量                        第一章 绪论例:考察T(n)=3n+2。当n>=2时,3n+2<=3n+n=4n,所以  T(n)=O(n)例:考察T(n)=10n2+4n+2。当n>=2时, 10...
§1.5 算法的性能分析与度量                  第一章 绪论算法分析举例 例1:分析下面程序段的时间复杂性 (1)                 T(n)=O(n) i=1; k=0 ;          ◇ 这个函数是按线...
§1.5 算法的性能分析与度量                  第一章 绪论 (3) for(i=1;i<=n; i ++)     T(n)=O(n2)   for(j=1;j<=n; j ++)   ◇ 是平方阶递增的       { +...
§1.5 算法的性能分析与度量                第一章 绪论 (5)                ◆ T(n)=O(1) x=91; y=100;       ◇ 这个程序看起来有点吓人, while(y>0)         ...
§1.5 算法的性能分析与度量                                第一章 绪论 (6)                     ◆ T(n)=O(log2n) i=1;                    设其原操...
§1.5 算法的性能分析与度量                          第一章 绪论例2:分析下面函数的复杂性  A是一个含有n个不同元素的实数数组,给出求A之最大和最小元素的算法如下,分析它的时间复杂性。  算法 SM(A,n,ma...
§1.5 算法的性能分析与度量                        第一章 绪论 2. 常见的算法时间复杂度: 常见的时间复杂度,按数量级递增排列依次为:     常数阶O(1)、对数阶O(log2n)、线性阶O(n)、线性对数阶 O...
§1.5 算法的性能分析与度量             第一章 绪论4. 平均时间复杂性定义:设一个领域问题的输入规模为n,Dn是该领域问题的所有输入的集合,任一输入I Dn,P(I)是I出现的概率, P(I)=1,t(I)是算法在输入I下执行...
§1.5 算法的性能分析与度量                 第一章 绪论  例如:查找一个元素是否存在的算法  int loc(int a[],int x)  { a[0]=x;    i=n;    while(a[i]!=x)     ...
§1.5 算法的性能分析与度量        第一章 绪论5. 算法时间复杂性的实质: 算法与问题规模及时间的关系。 同一问题,规模相同,用不同的算法解决,花费时间是不同的; 同一问题,用不同的算法解决,在相同的时间内所解决的问题 规模大小不同...
本章小结                第一章 绪论1. 为什么要讨论数据结构(数据结构的重要性)2. 数据结构的有关概念及它们之间的关系   数据结构、逻辑结构、存储结构、数据类型、  ADT等3. 算法及算法分析基础   算法的定义及特点、...
作业:1.总结本章主要内容2.自己找出或设计三个算法,并分析其时  间复杂度。(要求三个算法的渐进时间  复杂度不能相同)
第二章 线性表内容提要:  线性表是最简单、最基本、也是最常用的一种线性结构。  它有两种存储方法:顺序存储和链式存储,它的主要基本操作是插入、删除和检索等。
第二章 线性表2.1 线性表2.2 顺序表2.3 单链表2.4 线性链表的其它变形2.5 单链表的应用:一元多项式及其运算
2.1 线性表 linear list2.1.1 线性表的概念 线性表是具有相同数据类型的n(n>=0)个数据元素的有限序列,通常记为:      L=(a1,a2,… ai-1,ai,ai+1,…an)几个概念:表中相邻元素之间存在着顺序关系...
说明:  ai为序号为 i 的数据元素(i=1,2,…,n),通常我们将它的数据类型抽象为datatype。datatype根据具体问题而定,如在学生情况信息表中,它是用户自定义的学生类型。
线性表的抽象数据类型: 抽象数据类型是一个(D,R,P)三元组,分析可得线性表的抽象数据类型: ADT List { D={ ai | ai ∈ElemSet, i=1,2,...,n, n≥0 }                       ...
2.1.2 线性表的类定义Enum bool {false, true}class LinearList {Public:LinearList();~LinearList();virtual int Size() const =0;virtua...
2.2 顺序表(Sequential List)2.2.1 顺序表的定义和特点定义 :把线性表中的所有表项按照其逻辑顺序依次存  储到计算机存储器中指定存储位置开始的一块连续  的存储空间中。特点:  ①逻辑顺序与物理顺序一致  ②可顺序或随机...
顺序表可以用C++的一维数组来实现。 C++的一维数组可以是静态分配的,也可以是动态分配的。数组的下标位置:      0 1           i-2   i-1       n-1      a1 a2   …     ai-1 ai  ...
2.2.2顺序表(SeqList)类的定义#include<iostream.h>#include<stdlib.h>typedef int T;class SeqList {    T *data;       //顺序表存储数组    in...
int Length ( ) const { return last+1; }   //返回元素的个数int Find (T & x ) const; //返回元素x在表中的位置void Insert (T & x, int i ); //在位...
顺序表部分公共操作的实现:①构造函数SeqList :: SeqList ( int sz ) {              //构造函数,通过指定sz,定义数组的长度  if ( sz > 0 ) {      data = new T[sz...
(定位元素x的位置,返回值为x在②搜索或定位:顺序表中的位置;返回值为-1表示       不存在) data            MaxSize 23 75 41 38 54 62 17  ii   i     i   i     last...
int SeqList::Find ( T & x ) const { //搜索函数:在表中从前向后顺序查找 x    int i = 0;    while (           i <= last && )                ...
搜索成功:           n 1ACN =              pi   ci           i   0 若搜索概率相等,则      1n1        1ACN =     ( i 1)   (1 2  n)     ...
③插入元素分析:在i位置插入元素x时,线性表的逻辑结构发生什么变化?(a1, …, ai-1, ai, …, an) 改变为(a1, …, ai-1, x, ai, …, an)     <ai-1, ai>              <ai-...
例如: x=66; Insert ( x, 4) Last ++;//当前最后元素下标加1 for(j = Last ; j >i ; j --)           data[j]=data[j-1];   data[i]=x;//在i位置插...
Insert ( const T& x, int i)思路:①若顺序表的长度等于MaxSize,则满(不能插入);②若给定的位置小于0或大于长度,出错;③否则,Last加1,把i及其后面的元素向后移动 一个位置;④把数据X插入到i位置
//在指定位置i插入一个数据元素xvoid SeqList::Insert ( const T& x, int i){//i为下标,不是序号     if(last == MaxSize-1)      {         cerr<<“顺序表...
//从后向前把前一个元素迁移到后一个元素位置  直到存储位置为i为止 last++;//当前最后元素下标加1for(int j = Last ; j > i ; j --)     data[j]=data[j-1]; data[i]=x;//...
考虑移动元素的平均情况:  假设在第 i (0<=i<=n)个元素位置插入的概率为 pi ,则在线性表中插入一个元素所需移动元素次数的期望值为:             n     Eis           pi (n i )        ...
④删除元素分析:删除i处的元素时,线性表的逻辑结构发生什么变化?(a1, …, ai-1, ai, …, an) 改变为(a1, …, ai-1, ai+1, …, an)<ai-1, ai>, <ai, ai+1>              ...
删除值为x的元素int SeqList :: Remove ( T & x ) {   int i = Find (x)-1;            //在表中搜索 x        if ( i >= 0 ) {        last-- ...
⑤输入操作void SeqList :: input(){//从键盘逐个输入数据建立顺序表cout <<“开始建立顺序表,请输入表中元素个数”;   while (1){       cin >>last;       if (last<=Ma...
⑥输出操作void SeqList :: output(){//将顺序表全部元素输出到屏幕上    cout <<“顺序表元素个数为:”<<Last+1<<endl;    for (int i=0;i<=last;i++)       cou...
2.2.4 顺序表的应用例1:编写一个程序向顺序表中插入5个整数值,然后以插  入次序显示这5个数,最后删除这5个数。typedef int T;#include”SeqList.h”void main(void){ SeqList myLis...
例2. 集合的“并”运算利用两个线性表LA和LB分别表示两个集合A和B, 现要求一个新的集合A=A∪B。步骤:①取LB中的一个元素    x=LB.GetData(i)   ②在LA中找这个元素    LA.Find(x)   ③如果LA中没有...
求集合的“并”集void Union ( SeqList &LA, SeqList LB ) {   int n = LA.Length ( );   int m = LB.Length ( );   for ( int i = 1; i <=...
例3. 集合的“交”运算利用两个线性表LA和LB分别表示两个集合A和B, 现要求一个新的集合A=A∩B。步骤:①依次取LA中的元素    x=LA.GetData(i)   ②在LB中找这个元素    LB.Find(x)   ③如果LB中没有...
求“交”集     void Intersection(SeqList &LA, SeqList LB ) {    int n = LA.Length ( );    int m = LB.Length ( );    int i = 1; ...
例4. 已知线性表LA和线性表LB中的数据元素按值非递减有序排列,现要求将LA和LB归并为一个新的线性表LC,且LC中的元素仍按值非递减有序排列。 此问题的思路如下: ①从线性表LA,LB中依次取得每个数据元素: LA.GetData(i), ...
void MergeList(SeqList LA,SeqList LB,SeqList &LC) { int i=j=1;k=0;    T xa,xb;    int LALength=LA.Length();    int LBLengt...
while(i<=LALength){    xa=LA.GetData(i++); LC.Insert(xa,k++);    }while(j<= LBLength){    xb=LB.GetData(j++); LC.Insert(xb...
2.3 单链表(Singly Linked List)一、单链表的概念二、结点类的定义与实现三、单链表类的定义与实现
顺序表的优缺点:优点:  ①无需为表示结点间的逻辑关系增加额外的存储 空间,存储利用率高。  ②随机访问缺点:  ①在表中插入或删除元素时,平均移动一半的元 素,运行效率很低。  ②占用连续空间  为了克服顺序表的缺点,采用链接方式存储线性 表...
2.3.1 单链表的概念 用一组地址任意的存储单元存放线性表中的数据元素。元素(数据元素的映象)+ 指针(指示后继元素存储位置)= 结点(node)  单链表的一个存储结点包含两个部分:        data   link  构成的线性表为:
头指针     线性表为空表时,       头指针       头结点的指针域为空     空指针 头结点       a1   a2   … ...   an ^ 以线性表中第一个数据元素 的存储a1地址作为线性表的地址,称作线性表的头指针...
单链表的存储映像
2.3.2 单链表的类定义  通常使用两个类:链表的结点类和链表类,协同表示单链表。 定义方式主要有两种: 复合方式、 嵌套方式
#include <iostream.h> #include <stdlib.h> class List; //前视定义,否则友元无法定义 class LinkNode{        链表结点类的定义     friend class Lis...
单链表类的定义 class List {private:     LinkNode *first; //指向头结点的头指针public:     List () { first = new LinkNode ();}              ...
~List (){makeEmpty(); delete first;}    //析构函数void MakeEmpty ( );     //链表置空int Length ( ) const;      //求链表长度LinkNode * g...
void Insert (int x, int i );void Remove (int &x, int i ); int IsEmpty()const{    return(first->link==NULL? 1:0;}  void inp...
①删去链表中除表头结点外的所有其它结点MakeEmpty ( )思路:①设指针q;②当链表不为空(first的link不为NULL):  q指向first的下一结点;  把q结点从链表中摘除,删除q ;
void List :: MakeEmpty ( ) {//删去链表中除表头结点外的所有其它结点   LinkNode *q;   while ( first→link != NULL ) {      q = first→link;     ...
②求单链表的长度:Length ( ) 思路:①指针p指向first的下一结点,计数器为0;②当p不为NULL:  计数器+1;  p指向下一结点;③返回计数器的值。
求表长int List::Length ( ) const {LinkNode *p = first→link;    //检测指针p指示第一个结点    int count = 0;    while ( p != NULL ) {    /...
③在表中搜索数据x的结点:Find ( int x )LinkNode * List::Find ( int x ){   LinkNode *p = first→link; //指针 p 指示第一个结点    while ( p != NUL...
④定位函数,返回表中第i个数据的地址LinkNode * List::Locate ( int i ){   if ( i < 0 ) return NULL;    LinkNode *p = first;     int j = 0;   ...
⑤取出链表中第i个元素的值DataType List::GetData( int i ) {//提取第 i 个结点的值    LinkNode *p = Locate( i ); // p 指向链表第 i 个结点      return p->...
⑥给链表中第i个元素赋值void List::SetData(DataType x,int i ) {//给第 i 个结点赋值    if ( i <= 0 ) return;    LinkNode *p = Locate ( i ); //...
⑦将元素x插入到链表中第i个位置处 Insert (DataType x , int i)有序对 <ai-1, ai>改变为 <ai-1, x> 和<x, ai>        ai-1           ai                 x
void List::Insert (DataType x , int i){//在第i个结点处插入一个data域值为x的新结点    LinkNode *p = Locate ( i-1);     LinkNode * newNode = ...
⑧删除第i个结点并通过引用返回被删结点的data Remove (DataType &x , int i)    有序对<ai-1, ai> 和 <ai, ai+1>      改变为 <ai-1, ai+1>       ai-1     a...
void List::Remove (DataType &x, int i ){    LinkNode *p = Locate (i-1), *q;    q = p->link;    p->link = q->link;   //重新链接...
⑨单链表的输入(前插法)  自行设置一个输入数据的结束标志,用以结束结点的输入。设计思路:①建立一头结点②输入一结点的值③当结点的值不为结束标志时,创建新结点,插 入到头结点后,直到输入的值为结束标志。
⑨单链表的输入(前插法)void List :: input (DataType endTag){  LinkNode *newnode; DataType val;  first=new LinkNode ();  if (first==NU...
⑩单链表的输出void List ::output ( ) {//依次输出各结点的值LinkNode *p=first->link;while(p!=NULL) {   cout<<p->data<<endl;   p=p->link;   }}
2.4 线性链表的其它变形             例如 n = 8 m = 32.4.1 循环链表                0                  7                      8 1         1 ...
2.4.1 循环链表   对于单链表而言,最后一个结点的指针域是空指针,如果将该链表头指针置入该指针域,则使得链表头尾结点相连,就构成了单循环链表。      a1   a2   … ... an  和单链表的差别仅在于,判别链表中最后一个结点...
   循环链表的示例first   a0   a1     a2    an-1   带表头结点的循环链表first        a0     a1    an-1                         (非空表)first  ...
循环链表类的定义P67  循环链表与单链表的操作实现,最主要的不同就是扫描到链尾,遇到的不是NULL,而是表头。
循环链表的搜索算法first        31   48     15    57搜索15              p      p     搜索成功             pfirst        31   48     15    ...
循环链表的搜索算法CircListNode * CircList::Find( DataType x ){//在链表中从头搜索其数据值为 x 的结点    CircListNode * p = first->link;    while ( p...
带尾指针的循环链表    22    31   48   15   57                              rear     如果插入与删除仅在链表的两端发生,可  采用带表尾指针的循环链表结构。   在表尾插入: 时...
用循环链表求解约瑟夫问题  约瑟夫问题:  n 个人围成一个圆圈,首先第 1 个人从 1 开始,一个人一个人顺时针报数, 报到第 m 个人,令其出列。然后再从下一 个人开始,从 1 顺时针报数,报到第 m 个人,再令其出列,…,如此下去,直到圆...
例如 n = 8 m = 3             0                                 0                                 0     7                    ...
0                          0                        0     7                        7                          7         8 ...
求解Josephus问题的算法#include <iostream.h>#include ―CircList.h‖void Josephus(CircList & Js, int n, int m) {   CircLinkNode *p = ...
pre->link = p->link; delete p;     //删去         p = pre->link;     }};void main() {   CircList clist;   int i, n m;   cout...
2.4.2 双向链表 (Doubly Linked          List)  双向链表是指在前驱和后继方向都能游历(遍历)的线性链表。  双向链表每个结点结构:        lLink   data     rLink      前驱方...
first                             first            非空表                   空表    结点指向     p == p->lLink->rLink == p->rLink-...
双向循环链表的搜索算法DblNode *DblList::Find (DataType x, int d) {//在双向循环链表中寻找其值等于x的结点, //按d确定  搜索方向,d为0,向左搜索, d为1,向右搜索     DblNode *...
双向循环链表的插入算法first         31       48   15后插入25                   pfirst         31       48   25   15                   p ...
双向循环链表的删除算法first             31     48     15    非空表删除48                      p        p->rLink->lLink = p->lLink;        ...
2.5      单链表的应用          多项式(Polynomial)    Pn ( x ) a0 a1 x a2 x 2  an x n            n                        i        ...
2.5.1 多项式的存储表示  第一种: 静态数组表示  在类的私有域中定义多项式的数据成员:  private:   int degree;   float coef [maxDegree+1];        则Pn(x)可以表示为:   ...
第二种:动态数组表示在类的私有域中定义多项式的数据成员:private:   int degree;   float * coef;      Polynomial :: Polynomial (int sz) {          degre...
第三种:只存储非零系数项的系数和指数       0   1   2          i          mcoef   a0 a1 a2 ……        ai    ……   amexp    e0 e1 e2 ……        e...
两个多项式存储的例子  A(x) = 2.0x1000+1.8  B(x) = 1.2 + 51.3x50 + 3.7x101   A.start A.finish B.start     B.finish free              ...
第四种:多项式的链表存储表示   多项式顺序存储表示的缺点:     插入和删除时项数可能有较大变化,因此      要移动大量数据     不利于多个多项式的同时处理   采用多项式的链表表示可以克服这类困难:     多项式的项数...
多项式的链表结构   在多项式的链表表示中,每个结点三个数据    成员:        coef   exp     link   通过链接指针,可以将多项式各项按指数递    增的顺序链接成一个单链表。   在此结构上,新项的加入和废...
2.6 静态链表   处理时中可以不改变各元素的物理位置,只    要重新链接就能改变这些元素的逻辑顺序。   它是利用数组定义的,在整个运算过程中存    储空间的大小不会变化。   静态链表每个结点由两个数据成员构成:    data...
2.1 线性表2.2 顺序表2.3 单链表2.4 线性链表的其它变形2.5 单链表的应用:一元多项式及其运算作业:(p84)6,20,21,27,28
第三章 栈与队列•   栈•   队列•   栈的应用:表达式求值•   栈的应用:递归•   队列的应用:打印杨辉三角形•   优先级队列         157
3.1 栈 ( Stack )3.1.1 栈的定义     只允许在一端插入和删除的线性表。 退栈             进栈     允许插入和删除的一端称为栈顶      (top),另一端称为栈底(bottom) top        ...
栈的抽象数据类型class Stack {                         //栈的类定义  public:   Stack(){ };                           //构造函数   ~Stack(){ ...
栈的数组存储表示 — 顺序栈           0 1   2 3 4 5 6 7 8 9         maxSize-1elements       top (栈空)   typedef int DataType;   class Se...
void overflowProcess();            //栈的溢出处理public:   SeqStack(int sz =50);                      //构造函数   ~SeqStack() { del...
top     b             top      a            atop   空栈            a 进栈         b 进栈top     e    top      e            e    ...
dtop   c            c      b    top     b            b      a            a    top     a    d 退栈         c 退栈         b 退栈 ...
①顺序栈的构造函数#include <assert.h>#include <iostream.h>SeqStack:: SeqStack(int sz){    elements=new DataType[sz];    assert(elem...
②顺序栈的溢出处理void SeqStack::overflowProcess() {//私有函数:当栈满则执行扩充栈存储空间处理   DataType *newArray = new DataType [2*maxSize];        ...
③入栈操作void SeqStack::Push(DataType & x) {//若栈满,则溢出处理,将元素x插入该栈栈顶   if (IsFull() == true) overflowProcess( );  //栈满   element...
⑤取栈顶元素bool SeqStack::getTop(DataType& x) {//若栈不空则x为该栈栈顶元素    if (IsEmpty() == true) return false;    x = elements[top];   ...
双栈共享一个栈空间    0                                maxSize-1Vb[0]                t[0]     t[1]            b[1]两个栈共享一个数组空间V[maxS...
3.1.3 栈的链接存储表示 — 链式栈top                   ^ •   链式栈无栈满问题,空间可扩充 •   插入与删除仅在栈顶处执行 •   链式栈的栈顶在链头 •   适合于多栈操作             171
链式栈 (LinkedStack)类的定义typedef int DataType;struct StackNode {                   //栈结点类定义public:   DataType data;           ...
class LinkedStack : { //链式栈类定义private:   StackNode *top;             //栈顶指针public:  LinkedStack() : top(NULL) {}      //构造...
bool getTop(DataType & x) const; //取栈顶 元素     bool IsEmpty() const {            return (top == NULL)? true:false; }     in...
链式栈类操作的实现#include <iostream.h>①清空栈操作void LinkedStack::makeEmpty( ) {//逐次删去链式栈中的元素直至栈顶指针为空。   StackNode *p;   while (top !=...
②入栈操作void LinkedStack::Push(DataType &x) {//将元素值x插入到链式栈的栈顶,即链头。   top = new StackNode(x, top);        //创建新结点   assert (to...
④取栈顶元素bool LinkedStack::getTop(DataType & x) const {     if (IsEmpty() == true) return false; //栈空返回     x = top->data;   ...
公共邮箱:rj09123@163.com  密码:098765
关于栈的进一步讨论  问题:当进栈元素的编号为1, 2, …, n时,可能的出栈序列有多少种?            179
•    设进栈元素数为n,可能出栈序列数为mn:     n = 0,m0 = 1: 出栈序列{}。     n = 1,m1 = 1: 出栈序列{1}。     n = 2,m2 = 2:= m0*m1+m1*m0      a) 出...
= m0*m2 = 2   出栈序列中1在第2位。1前有2后有3,     出栈序列为 {2, 1, 3}。= m1*m1 = 1   出栈序列中1在第3位。前面2个元素有     m2 = 2个出栈序列:{2, 3, 1}, {3, 2,...
= m0*m3 = 5b) 出栈序列中1在第2位。前面有2,后面3、   4有m2 = 2个出栈序列: {2, 1, 3, 4}, {2, 1,   4, 3}。 = m1*m2 = 2c) 出栈序列中1在第3位。前面2、3有m2 =   2个...
 一般地,设有 n 个元素按序号1, 2, …, n  进栈,轮流让 1在出栈序列的第1, 第2, …  第n位,则可能的出栈序列数为:  n -1         mi * mn   i 1   m0 * mn    1   m1 * m ...
3.1.4 栈的应用--数制转换    算法基于原理: N = (N div d)×d + N mod d
例如:(1348)10 = (2504)8 ,其   运算过程如下:     N N div 8 N mod 8 计   1348 168    4         输 算    168  21    0         出 顺        ...
思路:①初始化栈②输入要转换的数据N③当N不为0,把N%8取余入栈④当栈不为空,栈顶元素出栈,输出。
void conversion (){SeqStack S; int N; int x=0; bool continue1;cin >>N;   while (N) {     S.Push( N % 8);     N = N/8;     ...
3.1.5 栈的应用--括号匹配的检验假设在表达式中([]())或[([ ][ ])]等为正确的格式,[( ])或([( ))或 (()])为不正确的格式。则 检验括号是否匹配的方法可用“期待的急迫程度”这个概念来描述。   例如:考虑下列括号...
算法的设计思路:①左括号入栈②右括号,检验栈空? 若空,表明右括号多了              匹配,栈顶的左括号出栈 非空,与栈顶元素比较              不匹配,出错③表达式检验结束时,若空,则匹配,若非 空,则表明左括号多了。
bool matching(char *exp) { int state = 1; i = 0;L = Length(exp);char x; while (i<L && state) {    switch (exp[i] ){     ca...
3.1.6 栈的应用举例--表达式求值1. 表达式的三种表示方法:设 Exp = S1 + OP + S2则称 OP + S1 + S2        为前缀表示法     S1 + OP + S2   为中缀表示法     S1 + S2 +...
例如: Exp = a b + (c d / e) f 前缀式:      + ab     c/def 中缀式:      a b + (c d / e) f 后缀式:      ab cde/ f +结论: 1)操作数之间的相对次序不变; ...
例如: Exp = a b + (c d / e) f 前缀式:      + ab     c/def 中缀式:      a b + (c d / e) f 后缀式:      ab cde/ f +结论:4)前缀式的运算规则为:   连续...
2.如何从后缀式求值?先找运算符, 再找操作数例如:      ab      cde/   f    +a b                      d/e      c-d/e                              ...
模拟一个简单的后缀表达式计算器Calculator类的定义:class Calculator{public:       Calculator(int sz){S=sz;};       void Run();                 ...
①将操作数的值value入操作数栈void Calculator:: AddOperand(double value){      S.Push(value);};②清栈void Calculator:: Clear() {      S.Ma...
③从操作数栈中取出两个操作数bool Calculator::Get2Operands(double&left,   double&right){   if (S.IsEmpty())        {cerr<<“缺少右操作数!”<<endl...
④读字符串并求后缀表达式的值void Calculator::Run{      char ch; double newoperand;      while(cin.get(ch),ch!=„#‟){        switch(ch){  ...
⑤取两个操作数,根据操作符op计算void Calculator:: DoOperator(char op){      double left,right,value; bool result;      result = get2Opera...
case „/‟: if (right==0.0){                   cerr<<“Divide by 0!”<<endl; Clear();}                   else{value=left/right...
3.如何将中缀表示→转后缀表示?•   先对中缀表达式按运算优先次序加上括号,再    把操作符后移到右括号的后面并以就近移动为    原则,最后将所有括号消去。•   如中缀表示 (A+B)*D-E/(F+A*D)+C,其转换为    后缀表...
如何将中缀表示→转前缀表示?•   先对中缀表达式按运算优先次序通统加上括    号,再把操作符前移到左括号前并以就近移    动为原则,最后将所有括号消去。•   如中缀表示 (A+B)*D-E/(F+A*D)+C,其转    换为前缀表达式...
计算机如何将中缀表示转换为后缀表示?    使用栈可将表达式的中缀表示转换成它的前缀表                     栈•    示和后缀表示。•   为了实现这种转换,需要考虑各操作符的优先级。          各个算术操作符的优...
栈顶算符为θ1:θ 1       θ 2   +   -   *   /   (   )   #      +         >   >   <   <   <   >   >      -         >   >   <   <   ...
中缀表达式转换为后缀表达式的算法•   例:中缀表达式为A+B*(C-D)-E/F,求其后缀表    达式。•   ①操作符栈初始化,将结束符‘#‟进栈。然后读    入中缀表达式字符流的首字符ch。•   ②重复执行以下步骤,直到ch = „...
a.若ch是操作数直接输出,读入下一个字符ch。     b.若ch是操作符,判断ch的优先级icp和位于栈顶的       操作符op的优先级isp:      若 icp(ch) > isp(op),令ch进栈,读入下一个字符      ...
中缀表达式转换为后缀表达式的算法:void postfix(expression e) //把中缀表达式e转换成后缀表示并输出   Stack S; char ch=„#‟,ch1,op;   S.Push(ch);cin.get(ch);  ...
4.如何应用中缀表示计算表达式的值        a+b*(c-d)-e/f                  rst1     rst4               rst2             rst3                 ...
中缀算术表达式求值   对中缀表达式求值的一般规则:    1. 建立并初始化OPTR栈和OPND栈,然后       在OPTR栈中压入一个“#”    2. 扫描中缀表达式,取一字符送入ch。    3. 当ch != „#‟ 或OPTR...
①若ch是操作数,进OPND栈,从中缀表达式取下一字符送入ch;②若ch是操作符,比较icp(ch)的优先级和isp(OPTR)的优先级:若icp(ch) > isp(OPTR),则ch进OPTR栈,从中缀表达式取下一字符送入ch;若icp...
void InFixRun() { SeqStack <char> OPTR, SeqStack <double>OPND; char ch, op; double x; OPTR.Push(„#‟); cin.get (ch);       ...
if (icp(ch) > isp(op))     //栈顶优先级低             { OPTR.Push (ch); cin.get(ch); }          else if (icp(ch) < isp(op)) {   ...
3.2 栈与递归•   递归的定义    若一个对象部分地包含它自己,或用它自己    给自己定义, 则称这个对象是递归的;若一    个过程直接地或间接地调用自己, 则称这个    过程是递归的过程。•   以下三种情况常常用到递归方法。  ...
1. 定义是递归的例如,阶乘函数            1,     当n 0时    n!         n (n 1)!, 当 n 1时求解阶乘函数的递归算法long Factorial(long n) {  if (n == 0) re...
求解阶乘 n! 的过程           主程序 main : fact(4)        参数 4   计算 4*fact(3)   返回 24递   参                                 结   回归   ...
2.数据结构是递归的•   例如,单链表结构     f     f•   一个结点,它的指针域为NULL,是一个单    链表;•   一个结点,它的指针域指向单链表,仍是一    个单链表。               216
搜索链表最后一个结点并打印其数值void Print(ListNode *f) {  if (f ->link == NULL)      cout << f ->data << endl;  else Print(f ->link);}   ...
在链表中寻找等于给定值的结点并打印其数值void Print(ListNode*f, DataType value) {   if (f != NULL)      if (f -> data == value)          cout <...
3. 问题的解法是递归的:汉诺塔问题         219
3. 问题的解法是递归的•   汉诺塔(Tower of Hanoi)问题的解法:    如果 n = 1,则将这一个盘子直接从 A 柱移    到 C 柱上。否则,执行以下三步:    ① 用 C 柱做过渡,将 A 柱上的 (n-1) 个盘 ...
• 解决方法:   n=1时,直接把圆盘从A移到C – n>1时, – ①先把上面n-1个圆盘从A移到B   ②然后将n号盘从A移到C – ③再将n-1个盘从B移到C。 – 即把求解n个圆盘的Hanoi问题转化为求解n-1个圆盘的   Hano...
main() { int m;   printf("Input number of disks:‖);     scanf("%d",&m);     hanoi(m,A,B,C);(0) }void hanoi(int n,char x,ch...
构成递归的条件•   不能无限制地调用本身,必须有一个出口,    化简为非递归状况直接处理。     Procedure <name> ( <parameter list> ) {      if ( < initial condition>...
3.2.2递归过程与递归工作栈•   递归过程在实现时,需要自己调用自己。•   层层向下递归,退出时的次序正好相反:              递归调用    n! (n-1)!   (n-2)! 1! 0!=1              返...
long Factorial(long n) {            int temp;            if (n == 0) return 1;            else temp = n * Factorial(n-1);R...
1. 递归工作栈•   每一次递归调用时,需要为过程中使用的参    数、局部变量等另外分配存储空间。•   每层递归调用需分配的空间形成递归工作记    录,按后进先出的栈组织。•   栈顶的工作记录是当前正在执行的这一层的    工作记录。...
函数递归时的活动记彔调用块   ……………….      <下一条指令>      Function(<参数表>)函数块      ……………….           <return>返回地址(下一条指令) 局部变量 参数         227
2. 递归过程改为非递归过程•   递归过程简洁、易编、易懂•   递归过程效率低,重复计算多•   改为非递归过程的目的是提高效率•   单向递归和尾递归可直接用迭代实现其    非递归过程•   其他情形必须借助栈实现非递归过程      ...
斐波那契数列的函数Fib(n)的定义               n,         n 0,1Fib(n)       Fib(n 1) Fib(n 2), n 1如 F0 = 0, F1 = 1, F2 = 1, F3 = 2, F4 =...
时间复杂度:      2 n                    Fib(5)                 Fib(4)                        Fib(3)        Fib(3)            Fi...
单向递归用迭代法实现long FibIter(long n) {   if (n <= 1) return n;   long twoback = 0, oneback = 1, Current;   for (int i = 2; i <= ...
尾递归用迭代法实现25 36 72 18 99        49 54 63void recfunc(int A[ ], int n) {   //从n到0输出数组各项的值。   if (n >= 0) {       cout << “va...
void sterfunc(int A[ ], int n) {//消除了尾递归的非递归函数  while (n >= 0) {     cout << "value:" << A[n] << endl;     n--;  }}       ...
3.2.3 递归与回溯  对一个包含有许多结点,且每个结点有多个分支的问题,可以先选择一个分支进行搜索。当搜索到某一结点,发现无法再继续搜索下去时,可以沿搜索路径回退到前一结点,沿另一分支继续搜索。  如果回退之后没有其它选择,再沿搜索路径回退...
3.3 队列 ( Queue )               a0 a1 a2     an-1     front                         rear•   定义     队列是只允许在一端删除,在另一端插入     ...
队列的抽象数据类型class Queue {public:   Queue() { };        //构造函数   ~Queue() { };       //析构函数   virtual bool EnQueue(DataType x)...
3.3.2 队列的数组存储表示 ─顺序队          列       队列的进队和出队                           Afront rear 空队列         front rear A进队 A B       ...
队列的进队和出队原则进队时先将新元素按 rear 指示位置加入,再将队 尾指针加一 rear = rear + 1。队尾指针指示实际队尾的后一位置。出队时先按队头指针指示位置取出元素,再将队头指针进一 front = front + 1,队头指...
6        7 front     6          7 front         6         7 front                 rear5                0 5                ...
6   7 front              rear5           0                循环队列 (Circular Queue)4           1    3队列存放数组被当作首尾相接的表处理。       ...
循环队列的类定义#include <assert.h>#include <iostream.h>class SeqQueue {              //队列类定义protected:   int rear, front;        ...
~SeqQueue() { delete[ ] elements; } //析构函数   bool EnQueue(DataType x);       //新元素进队列   bool DeQueue(DataType& x);        ...
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社
Upcoming SlideShare
Loading in …5
×

数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社

2,811 views

Published on

数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社

Published in: Education
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
2,811
On SlideShare
0
From Embeds
0
Number of Embeds
4
Actions
Shares
0
Downloads
15
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

数据结构(用面向对象方法与C++语言描述第二版)殷人昆编著清华大学出版社

  1. 1. 烟雨科技有限责任公司 数据结构用面向对象方法与C++语言描述 第二版 殷人昆编著 清华大学出版社 课件 本课件由迷若烟雨提供
  2. 2. 课程介绍1. 为什么要学习数据结构?2. 该课程的主要内容是什么?如何学习?3. 考核方式? 总成绩=平时( 40% )+期末(60%) 平时=上机实习+平时作业+上课回答问题 +4次课内考试(每次5分)4. 教材:殷人昆,《数据结构(面向对象方法与C++ 语言描述)》(第2版)清华大学出版社1. 为什么要学习数据结构?2. 该课程的主要内容是什么? 数据结构=数据+结构(关系)
  3. 3. 数据结构 数据(的集合) 结构 逻辑结构 物理结构线 树 图 集 连 非性 结 结 合 续 连结 构 构 续构 顺 链 顺 链 邻 邻 序 式顺 链 序 式 接 接 存 存序 表 存 存 矩 表 储 储表 储 储 阵 存 存 储 储
  4. 4. 如何学习?1. 预习:粗读教材,发现问题2. 听课:重点、难点,初步解决问题3. 复习:细读教材,解决问题(理解抽象数 据类型、看懂C++类的定义和实现)4. 做题:巩固知识(C++类的定义和实现)5. 实习:验证(调试程序)
  5. 5. 上机实习作业:1. 顺序表及其应用2. 链表及其应用.doc3. 栈及其应用4. 队列及其应用5. 稀疏矩阵及其基本操作6. 二叉树及其基本操作7. 堆及其基本操作8. Huffman树及其基本操作9. 图及其基本操作
  6. 6. 第一章 绪论内容提要: 本章主要介绍数据结构的基本概念、术语,以及有关算法的描述、分析的基本常识。
  7. 7. §1.1 数据结构的概念 第一章 绪论1.1.1 为什么要讨论数据结构?通过前面的学习,我们知道: 计算机科学是研究信息表示和信息处理的科学; 信息是当今社会的重要资源; 计算机产业及计算机科学技术飞速发展,计算机的硬件技 术和软件技术都有了极大的发展,计算机应用也已经渗透到 了社会的各个领域。同时出现了如下一些变化: 计算机由最初的单一科学计算到几乎无所不能; 加工处理的对象由数值型变为数值型和非数值型; 处理的数据量由小变为大、巨大(海量存储、计算); 数据之间的关系由简单变复杂、很复杂;
  8. 8. §1.1 数据结构的概念 第一章 绪论解决思路1:发展硬件技术,开发出更快、更高级的硬件产品,但有没有其它途径呢?思路2: 将一“大堆杂乱无章”的数据交给计算机处理是很不明智的,结果是加工处理的效率会非常低,有时甚至根本无法进行。 于是: 人们开始考虑如何更有效地描述、表示、处理数据的问题, 除了不断提高计算机技术外(其它课程),很重要的一个方面 是通过研究、分析问题数据本身的特点,利用这些特点提高数 据表示和处理的效率。——这就是数据结构信息的表示和组织形式直接影响到数据处理的效率!下面举几个例子,目的是加深对数据结构的理解,从中也可以看出研究数据结构的重要性!
  9. 9. 线性 例1 书目自动检索系统 表 书目文件 书目卡片 001 高等数学 樊映川 S01 002 登录号: 理论力学 罗远祥 L01 索引表 003 书名: 高等数学 华罗庚 S01 004 线性代数 栾汝书 S02 …… 作者名: …… …… ……按书名 分类号: 按作者名 按分类号高等数学 出版单位: 001,003…… 樊映川 001, … L 002, … 002,……..理论力学 出版时间:华罗庚 002, …. S 001, 003,线性代数 004,…… …… …… 栾汝书 004, …. …… …….. 价格: ……. …….
  10. 10. 例1 书目自动检索系统 问题:图书管理,完成书目的自动检索。 数据:一本本书籍,更确切地说是每本书的 信息,如:书名、作者、出版社、出版日期、 书号、内容提要等等。 操作:书目检索、入库、借书、还书等。 涉及到:书目数据逻辑组织、存储方法;检 索、入库等操作实现算法。
  11. 11. 例2 人机对奕问题(井字棋) 树…….. …….. …... …... …... …...
  12. 12. 例2 人机对奕问题 问题:人机对弈,即人与计算机下棋。 数据:各种棋局状态,确切说就是描述棋盘 格局的信息。 操作:走棋,即选择一种策略使棋局状态发 生变化(由一个格局派生出另一个格局) 涉及到:“格局‖数据逻辑组织、存储方法;走 棋操作实现算法。
  13. 13. 例3 多叉路口交通灯管理问题 图 AB AC AD C DBA BC BD B DA DB DC E EA EB EC ED A
  14. 14. 例3 多叉路口交通灯管理问题 问题:多叉路口的交通灯管理,即在多 叉路口应怎样设置交通灯,以保证交通 畅通。 数据:路口各条道路的信息 操作:设置信号灯(求出各个可以同时 通行的路的集合) 涉及到:路口各条道路信息数据及其关 系的逻辑组织、存储方法;信号灯设置 的操作实现算法。
  15. 15. §1.1 数据结构的概念 第一章 绪论 1.1.2 数据结构及其术语 [数据 Data] 用于描述客观事物的数值、字符等一切可以输 入到计算机中,并由计算机加工处理的符号集合。 [数据元素 Data Element] 表示一个事物的一组数据称作一个 数据元素。如学生信息可包括:学号、姓名、性别等。 [数据项 Data Item] 构成数据元素的成份,是数据不可分割的最 小单位 。 [数据对象 Data Object]具有相同性质的数据成员(数据元素)的 集合,是数据的子集。 [结构 Structure ] 数据元素之间的关系(Relation)。 [数据结构 Data Structure ]由一个数据对象以及该对象中的所有 数据元素之间的关系组成,即带结构的数据元素的集合。
  16. 16. §1.1 数据结构的概念 第一章 绪论 数据结构=数据+结构 记作 Data_Structure=(D,R) 其中:Data_Structure是数据结构的名称 D是数据元素的有限集合(一般为一个数据对象) R是D上关系的有限集注:这里说的数据元素之间的关系是指元素之间本身固有的逻辑关系,与计算机无关。因此又称为:数据的逻辑结构 我们研究数据结构的目的是要利用数据之间的关系(结构), 因此,在存储时既要存储数据本身,还要存储关系!!
  17. 17. §1.1 数据结构的概念 第一章 绪论[数据的存储结构]:是数据结构在计算机内的表示,它包括数据元素的表示和关系的表示。数据在计算机中的存储:两种形式 连续:数据元素逐个连续存放(通过物理相邻来表示关系) 非连续:数据元素不连续存放(如何表示关系?)这样:一个数据结构要存放,一方面要把数据元素存起来,另一方面,还必须把数据元素之间的逻辑关系也表示出来,那么:要么用数据元素在物理上的相邻来表示逻辑关系要么用数据元素的存储地址、索引表、散列函数来表示逻辑关系 顺序存储结构 链式存储、索引存储、散列存储
  18. 18. §1.2 数据结构的抽象形式 第一章 绪论1.2.1 数据类型 Data Type 一个值的集合和定义在这个值集上的一组操作的总称。(1) 高级语言中的数据类型实际上包括:数据的逻辑结构、数据的存储结构及所定义的操作的实现。(2)高级语言中的数据类型按值的不同特性分为: 原子类型(如整型、实型、字符型、布尔型) 结构类型(由原子类型按照一定的规则构造而成, 如数组、结构体)(3)数据类型逻辑概念与其在计算机程序中的实现有很重要的区别,例如线性表数据类型有两种传统的实现方式:基于数组的顺序表示和基于链表的链式表示;(4)我们可以撇开计算机不考虑,现实中的任何一个问题都可以 定义为一个数据类型——称为抽象数据类型。
  19. 19. §1. 2 数据结构的抽象形式 第一章 绪论抽象数据类型=数学模型+操作 (其它教材上对ADT的描述) =数据结构+操作 =数据+结构+操作 它是一种描述用户和数据之间接口的抽象模型,亦即它给出了一种用户自定义的数据类型。 三元组表示:(D,R,P) 其中D是数据对象,R是D上的关系集,P是对D的基本操作集。ADT 抽象数据类型名 { 数据对象:<数据对象的定义> 数据关系:<数据关系的定义> 基本操作:<基本操作的定义> }ADT 抽象数据类型名抽象数据类型的作用:抽象数据类型可以使我们更容易描述现实世界。例:用线性表描述学生成绩表,用树描述棋局关系等。
  20. 20. 内容回顾:1.数据结构=数据+结构=(D,R)2.结构包括:逻辑结构:集合、线性、树型、图物理结构(存储结构):连续、非连续3.数据类型:(D,R,P)4.抽象数据类型: (D,R,P)
  21. 21. §1.3 作为ADT的C++类 第一章 绪论 1.3.1 面向对象的概念 面向对象 = 对象+类+继承+通信 对象:在应用问题中出现的各种实体、事件、规 格说明等。由一组属性值和在这组值上的一组服 务(或称操作)构成。 类 (class),实例 (instance) 具有相同属性和服务的对象归于同一类,形成类。 类中的一个对象为该类的一个实例。
  22. 22. 继承:是面向对象方法最有特色的方面。 • 派生类:载重车,轿车,摩托车,… 子类、特化类(特殊化类) • 基类:车辆 父类、超类、泛化类(一般化类) 各派生类中的公共部分,包括属性和服务,集 中到基类中,派生类中只保留自己特有的属性和 服务。这样减少了数据的存储和程序代码的重复 。– 通信 • 各个类的对象间通过消息传递进行通信。– 消息:一个类的对象要求另一个类的对象执行某个服务的指 令,必要时还要传递调用参数。
  23. 23. § 1.3 作为ADT的C++类 第一章 绪论 传统的大型结构化程序设计是面向过程的,首先着眼于系统要实现的功能。一个数据结构可能被多个过程调用,修改数据结构,意味着必须修改相关的过程,这样做烦琐又容易产生错误。 面向对象程序设计(Object-Oriented Programing,OOP) 程序围绕被操作的数据来设计, “类”作为构造程序的基本单 位。 “类”具有封装、数据抽象、继承和多态等特点!
  24. 24. § 1.3 作为ADT的C++类 第一章 绪论 1.3.2 C++中的类 例1:计算圆的周长和面积 圆是平面上与圆心等距离的所有点的集合。从图形显示角 度看,圆的抽象数据类型包括圆心和半径;而从计量角度看, 它所需要的抽象数据类型只需半径即可。如果从计量角度来 给出圆的抽象数据类型,那么它的数据取值范围应为半径的 取值范围,即为非负实数,而它的操作形式为确定圆的半径 (赋值);求圆的面积;求圆的周长。
  25. 25. § 1.3 作为ADT的C++类 第一章 绪论问题描述:给定圆的半径,求其周长和面积 ADT定义: ADT circle data : 0 r ,实数 structure: NULL operations: area {计算面积 s=πr2} circumference {计算周长 l=2πr} END ADT
  26. 26. §1.3 作为ADT的C++类 第一章 绪论例2:掷色子游戏。问题描述:每次掷出N个色子,统计每个色子的点数和每次的总 点数,看看谁的高。问题分析:该问题的数据包括色子数、每个色子的点数和总点数, 色子数是大于0的整数N;每个色子的点数是1-6; 总点数是N~6N; 该问题的操作包括掷色子、输出各个色子的点数、 求总点数。ADT定义: ADT dice data : N {色子数} ;K1,K2…Kn {每个色子的点数} S {总点数} structure: N S 6N, S=K1+K2+…+KN, 1 Ki 6 operations: toss {掷色子} displaytoss {显示每个色子的点数} total {计算总点数} END ADT
  27. 27. § 1.3 作为ADT的C++类 第一章 绪论例3:复数的运算问题描述:在高级语言中,没有复数类型,但是我们可以借助已 有的数据类型解决复数类型的问题,如复数运算。ADT定义: ADT complex data : c1,c2 {c1,c2均为实数} structure: z=c1+c2i operations: create(z,x,y) {生成一复数} add(z1,z2,s) {复数加法} subtract(z1,z2,difference) {复数减法} multiply(z1,z2,product) {复数乘法} get_realpart(z) {求实部} get_imagpart(z) {求虚部} printc(z) {输出一复数} END ADT
  28. 28. §1.3.2 C++中的类 第一章 绪论 C++对于面向对象程序设计的支持,核心部分就是类的定义,类的定义体现了抽象数据类型的思想: •说明与实现的分离 •信息隐蔽对类的成员规定三级存取: • 公共(public):其它类的对象或操作可访问,构成类的接口 • 私有 (private):只能该类的对象和成员函数及友元访问 • 保护 (protected):除了具有私有成员的特性,允许该类的 派生类访问
  29. 29. §1.4 算法定义 第一章 绪论 1.算法的定义和特性  算法 (Algorithm ): 算法是解决某一特定任 务的指令的有限序列。  算法具有以下五个特性: (1)有穷性 一个算法必须总是在执行有穷步 之后结束,且每一步都在有穷时间内完成。 (2)确定性 算法中每一条指令必须有确切的 含义。不存在二义性。且算法只有一个入口和 一个出口。
  30. 30. §1.4 算法定义 第一章 绪论 (3)可行性 一个算法是可行的。即算法描述的 操作都是可以通过已经实现的基本运算执行有 限次来实现的。 (4)输入 一个算法有零个或多个输入,这些输 入取自于某个特定的对象集合。 (5)输出 一个算法有一个或多个输出,这些输 出是同输入有特定关系的量。
  31. 31. §1.4 算法定义 第一章 绪论2. 算法与数据结构的关系 算法+数据结构=程序 在计算机解决问题的过程中算法与数据结构是缺一不 可的两个方面: 问题的数据组织——数据结构 数据的处理——算法 算法与数据结构是相辅相承的。解决某一特定类型问 题的算法可以选定不同的数据结构,而且选择恰当与否直 接影响算法的效率。反之,一种数据结构的优劣由各种算 法的执行来体现。
  32. 32. 怎样才能设计一个好的算法呢? 要设计一个好的算法通常要考虑以下的要求:⑴正确: 算法的执行结果应当满足预先规定的功能和性能 要求。⑵可读: 一个算法应当思路清晰、层次分明、简单明了、 易读易懂。⑶健壮:当输入不合法数据时,应能作适当处理,不至引 起严重后果。⑷高效:有效使用存储空间和有较高的时间效率。
  33. 33. §1.5 算法的性能分析与度量 第一章 绪论1. 5.1 算法的描述 1. 自然语言描述:容易,但有时罗嗦、有二义性 2. 图示(流程图、N-S图、PAD图等):直观清晰,但不易实现 3. 程序设计语言:可以直接运行,但太严格。 4. 算法语言(伪代码):严谨、简洁,易程序实现 为了解决理解与执行这两者之间的矛盾,人们常常使用一 种称为伪码语言的描述方法来进行算法描述。伪码语言介于高 级程序设计语言和自然语言之间,它忽略高级程序设计语言中 一些严格的语法规则与描述细节,因此它比程序设计语言更容 易描述和被人理解,而比自然语言更接近程序设计语言。它虽 然不能直接执行但很容易被转换成高级语言。
  34. 34. §1.5 算法的性能分析与度量 第一章 绪论 如何评价算法的优劣呢? 我们可以从一个算法的时间复杂度与空间复杂度来评价算 法的优劣。 一个算法转换成程序并在计算机上执行时,其运行所需要 的时间取决于下列因素: ⑴硬件的速度。例如使用486机还是使用586机。 ⑵书写程序的语言。实现语言的级别越高,其执行效率就越低。 ⑶编译程序所生成目标代码的质量。对于代码优化较好的编译程 序其所生成的程序质量较高。 ⑷问题的规模。例如,求100以内的素数与求1000以内的素数其 执行时间必然是不同的。
  35. 35. §1.5 算法的性能分析与度量 第一章 绪论 显然,在各种因素都不能确定的情况下,很难比较出算法 的执行时间。 也就是说,使用执行算法的绝对时间来衡量算法的效率是 不合适的。为此,可以将上述各种与计算机相关的软、硬件因 素都确定下来,这样一个特定算法的运行工作量的大小就只依 赖于问题的规模(通常用正整数n表示),或者说它是问题规 模的函数。 算法效率的度量分为:事前估计、后期测试。
  36. 36. §1.5 算法的性能分析与度量 第一章 绪论 1.5.2 算法效率的度量 先验估计(事前估计):根据算法的逻辑特征(基本操作的 次数)来估算。 后期测试(事后计算):选择样本数据、运行环境,运行算法 计算出空间、时间。 优点:可比性强 优点:精确 缺点:不精确,仅仅是估计 缺点:可比性差,效率低 那么,如何撇开计算机本身来估算一个算法的复杂性呢?
  37. 37. §1.5 算法的性能分析与度量 第一章 绪论 算法复杂性的度量属于事前估计。分为: 时间复杂度和空间复杂度的度量。 时间复杂度: 一个程序的时间复杂度(Time complexity)是指程序运行 从开始到结束所需要的时间。 一个算法是由控制结构和原操作构成的,其执行时间取决于两者的综合效果。为了便于比较同一问题的不同的算法,通常的做法是:从算法中选取一种对于所研究的问题来说是基本运算的原操作,以该原操作重复执行的次数作为算法的时间度量。一般情况下,算法中原操作重复执行的次数是规模n的某个函数T(n)。(计算T(n)见p29) 许多时候要精确地计算T(n)是困难的,我们引入渐进时间复杂度在数量上估计一个算法的执行时间,也能够达到分析算法的目的。
  38. 38. §1.5 算法的性能分析与度量 第一章 绪论1.5.4 O表示的含义——算法的渐进分析 1. 算法的渐进分析 算法的渐进分析简称算法分析。通常将问题的规模作为 分析的参数,求算法的时间开销与问题规模n的关系。 当且仅当存在正整数c和n0,使得T(n)≤c*f(n),对所有 的n≥n0成立,则称该算法的时间增长率在O(f(n))中, 记为T(n)=O(f(n)) 。
  39. 39. §1.5 算法的性能分析与度量 第一章 绪论例:考察T(n)=3n+2。当n>=2时,3n+2<=3n+n=4n,所以 T(n)=O(n)例:考察T(n)=10n2+4n+2。当n>=2时, 10n2+4n+2 <= 10n2+5n, 当n >=5时, 10n2+5n <= 11n2 ,所以, T(n)=O(n2)例:考察T(n)=6*2n+n2。当n>=4时, n2<= 2n,有: 6*2n+n2<=7*2n 所以, T(n)=O(2n) 当我们评价一个算法的时间性能时,主要标准就是算法的 渐进时间复杂度,因此,在算法分析时,往往对两者不予区分, 经常将渐进时间复杂度T(n)=O(f(n))简称为时间复杂度。
  40. 40. §1.5 算法的性能分析与度量 第一章 绪论算法分析举例 例1:分析下面程序段的时间复杂性 (1) T(n)=O(n) i=1; k=0 ; ◇ 这个函数是按线性阶递增的 while(i<n) { k=k+10*i; i++; } (2) i=0; k=0; do{ T(n)=O(n) k=k+10*i; i++; } ◇ 这也是线性阶递增的 while(i<n);
  41. 41. §1.5 算法的性能分析与度量 第一章 绪论 (3) for(i=1;i<=n; i ++) T(n)=O(n2) for(j=1;j<=n; j ++) ◇ 是平方阶递增的 { ++x; s+=x; } (4) T(n)= O(n2) for(i=1;i<=n; i ++) ◇ 这也是平方阶递增的 for(j=1;j<=i; j ++) ++x;
  42. 42. §1.5 算法的性能分析与度量 第一章 绪论 (5) ◆ T(n)=O(1) x=91; y=100; ◇ 这个程序看起来有点吓人, while(y>0) 总共循环运行了1000次,但是 if(x>100) 我们看到n没有? 没。这段程序 {x=x-10;y--;} 的运行是和n无关的,就算它再 else x++; 循环一万年,我们也不管他, 只是一个常数阶的函数。
  43. 43. §1.5 算法的性能分析与度量 第一章 绪论 (6) ◆ T(n)=O(log2n) i=1; 设其原操作执行的次数为t(n) while(i<=n) 于是,2t(n) n i=i*2; 从而有:t(n) log2n (7) s=0; ◆ T(n)=O(n3) for(i=0;i<=n;i++) n i j for(j=0;j<=i;j++) t(n)= 1=…… i=0 j=0 k=0 for(k=0;k<=j;k++) s++;
  44. 44. §1.5 算法的性能分析与度量 第一章 绪论例2:分析下面函数的复杂性 A是一个含有n个不同元素的实数数组,给出求A之最大和最小元素的算法如下,分析它的时间复杂性。 算法 SM(A,n,max,min) SM1 [初始化] max min a[1] 1 SM2 [比较] for(i=2;i<=n;i++) n { if(a[i]>max) max a[i] n-1 if(a[i]<min) min a[i] } n-1 T(n)=3n-1 T(n)=O(n)
  45. 45. §1.5 算法的性能分析与度量 第一章 绪论 2. 常见的算法时间复杂度: 常见的时间复杂度,按数量级递增排列依次为: 常数阶O(1)、对数阶O(log2n)、线性阶O(n)、线性对数阶 O(nlog2n)、平方阶O(n2)、立方阶O(n3)、k次方阶O(nk)、指数阶 O(2n)。 3. 大O运算规则 O(f(n))+O(g(n))=O(max(f(n),g(n))) O(f(n))+O(g(n))=O(f(n)+g(n)) O(f(n)) •O(g(n))=O(f(n) •g(n)) O(c•f(n))=O(f(n))
  46. 46. §1.5 算法的性能分析与度量 第一章 绪论4. 平均时间复杂性定义:设一个领域问题的输入规模为n,Dn是该领域问题的所有输入的集合,任一输入I Dn,P(I)是I出现的概率, P(I)=1,t(I)是算法在输入I下执行的基本运算次数,则平均时间复杂性定义为: T(n)= {P(I)*t(I)} I Dn最好时间复杂性、最坏时间复杂性、平均时间复杂性对于有些算法,问题规模相同,如果输入集不同,其效率不同 Tmin:最好情况不能代表算法的性能 Tmax:最坏情况可以知道算法至少能达到的性能 Tavg:较好地反映了算法的性能,但并不一定总是可行的!
  47. 47. §1.5 算法的性能分析与度量 第一章 绪论 例如:查找一个元素是否存在的算法 int loc(int a[],int x) { a[0]=x; i=n; while(a[i]!=x) i=i-1; return i } 最好:T ( n ) =c O(1) 最坏:T ( n ) =n+c O( n ) 平均:T ( n ) =n/2+c O( n )
  48. 48. §1.5 算法的性能分析与度量 第一章 绪论5. 算法时间复杂性的实质: 算法与问题规模及时间的关系。 同一问题,规模相同,用不同的算法解决,花费时间是不同的; 同一问题,用不同的算法解决,在相同的时间内所解决的问题 规模大小不同; 思考:―复杂性渐进阶比较低的算法比复杂性渐进阶比较高 的算法有效‖,这种说法正确吗? 另外需要注意: 当两个算法的复杂性渐进阶相同时,必须进一步考察T(n)的常数因子。
  49. 49. 本章小结 第一章 绪论1. 为什么要讨论数据结构(数据结构的重要性)2. 数据结构的有关概念及它们之间的关系 数据结构、逻辑结构、存储结构、数据类型、 ADT等3. 算法及算法分析基础 算法的定义及特点、算法分析的方法(渐进时间复杂度)
  50. 50. 作业:1.总结本章主要内容2.自己找出或设计三个算法,并分析其时 间复杂度。(要求三个算法的渐进时间 复杂度不能相同)
  51. 51. 第二章 线性表内容提要: 线性表是最简单、最基本、也是最常用的一种线性结构。 它有两种存储方法:顺序存储和链式存储,它的主要基本操作是插入、删除和检索等。
  52. 52. 第二章 线性表2.1 线性表2.2 顺序表2.3 单链表2.4 线性链表的其它变形2.5 单链表的应用:一元多项式及其运算
  53. 53. 2.1 线性表 linear list2.1.1 线性表的概念 线性表是具有相同数据类型的n(n>=0)个数据元素的有限序列,通常记为: L=(a1,a2,… ai-1,ai,ai+1,…an)几个概念:表中相邻元素之间存在着顺序关系。将 ai-1 称为 ai 的直接前趋,ai+1 称为 ai 的直接后继。就是说:对于ai,表名 数据元素(结点、表项) 表长 空表当 i=2,...,n 时,有且仅有一个直接前趋 ai-1.,当表头 表尾i=1,2,...,n-1 时,有且仅有一个直接后继 ai+1,而a1 是表中第一个元素,它没有前趋,an 是最后一个元素无后继。
  54. 54. 说明: ai为序号为 i 的数据元素(i=1,2,…,n),通常我们将它的数据类型抽象为datatype。datatype根据具体问题而定,如在学生情况信息表中,它是用户自定义的学生类型。
  55. 55. 线性表的抽象数据类型: 抽象数据类型是一个(D,R,P)三元组,分析可得线性表的抽象数据类型: ADT List { D={ ai | ai ∈ElemSet, i=1,2,...,n, n≥0 } ( i 为 ai 在线性表中的位序) R={ <ai-1 ,ai >|ai-1 ,ai∈D, i=2,...,n } 操作: 初始化操作 Create() CopyList() 结构销毁操作 引用型操作Length()search()Locate()getData()Isempty()Isfull() 加工型操作SetData()Insert()Remove()Sort() }
  56. 56. 2.1.2 线性表的类定义Enum bool {false, true}class LinearList {Public:LinearList();~LinearList();virtual int Size() const =0;virtual int Length() const =0;virtual int Search(int &x) const =0;virtual int Locate(int i) const =0;virtual datetype *GetData(int i) const =0;} (44页)
  57. 57. 2.2 顺序表(Sequential List)2.2.1 顺序表的定义和特点定义 :把线性表中的所有表项按照其逻辑顺序依次存 储到计算机存储器中指定存储位置开始的一块连续 的存储空间中。特点: ①逻辑顺序与物理顺序一致 ②可顺序或随机访问表项
  58. 58. 顺序表可以用C++的一维数组来实现。 C++的一维数组可以是静态分配的,也可以是动态分配的。数组的下标位置: 0 1 i-2 i-1 n-1 a1 a2 … ai-1 ai … an 所有数据元素的存储位置均取决于第一个数据元素的存储位置: LOC(ai) = LOC(a1) + (i-1)×sizeof(T)
  59. 59. 2.2.2顺序表(SeqList)类的定义#include<iostream.h>#include<stdlib.h>typedef int T;class SeqList { T *data; //顺序表存储数组 int MaxSize; //是问题要求的元素数目的最大值 int last; //当前最后元素下标public: SeqList ( int sz ); ~SeqList ( ) { delete [ ] data; }
  60. 60. int Length ( ) const { return last+1; } //返回元素的个数int Find (T & x ) const; //返回元素x在表中的位置void Insert (T & x, int i ); //在位置i插入元素xint Remove (T & x ); //删除值为x的元素int IsEmpty ( ) { return last ==-1; } //表空否int IsFull ( ) { return last == MaxSize-1; }T GetData ( int i ) { //取第i个表项的值 return data[i-1] };void SetData ( int i, T & x) { //为第i个表项赋值 if (i >0 && i <= last+1) data[i-1] = x ;}void input();void output(); }
  61. 61. 顺序表部分公共操作的实现:①构造函数SeqList :: SeqList ( int sz ) { //构造函数,通过指定sz,定义数组的长度 if ( sz > 0 ) { data = new T[sz]; if ( data != NULL ) { MaxSize = sz; last = -1;} else {cerr <<“存储分配错误!”<<endl;exit(1);} }} 设计思路: 若sz>0,则为数组申请空间: ①若申请成功,MaxSize=sz,last=-1 ②若申请失败,则提示出错
  62. 62. (定位元素x的位置,返回值为x在②搜索或定位:顺序表中的位置;返回值为-1表示 不存在) data MaxSize 23 75 41 38 54 62 17 ii i i i last i i 1 0 7 3 2 基本操作是: 将顺序表中的元素 x = 38 50 逐个和定值x相比较。
  63. 63. int SeqList::Find ( T & x ) const { //搜索函数:在表中从前向后顺序查找 x int i = 0; while ( i <= last && ) data[i] != x i++; if ( i > last ) return -1; elsereturn i+1;}设计思路:① x与data[i]逐个循环比较,直到x=data[i]或i>last② 若i>last,则没有找到x,否则返回i+1
  64. 64. 搜索成功: n 1ACN = pi ci i 0 若搜索概率相等,则 1n1 1ACN = ( i 1) (1 2  n) ni0 n 1 (1 n) n 1 n n 2 2 搜索不成功 数据比较 n 次 算法时间复杂度: O(n)
  65. 65. ③插入元素分析:在i位置插入元素x时,线性表的逻辑结构发生什么变化?(a1, …, ai-1, ai, …, an) 改变为(a1, …, ai-1, x, ai, …, an) <ai-1, ai> <ai-1, x>, <x, ai>a1 a2 … ai-1 ai … ana1 a2 … ai-1 x ai … an 表的长度增加1
  66. 66. 例如: x=66; Insert ( x, 4) Last ++;//当前最后元素下标加1 for(j = Last ; j >i ; j --) data[j]=data[j-1]; data[i]=x;//在i位置插入x j j j j i 21 18 30 75 42 56 87 0 21 18 30 75 66 42 56 87
  67. 67. Insert ( const T& x, int i)思路:①若顺序表的长度等于MaxSize,则满(不能插入);②若给定的位置小于0或大于长度,出错;③否则,Last加1,把i及其后面的元素向后移动 一个位置;④把数据X插入到i位置
  68. 68. //在指定位置i插入一个数据元素xvoid SeqList::Insert ( const T& x, int i){//i为下标,不是序号 if(last == MaxSize-1) { cerr<<“顺序表已满无法插入!”<<endl;exit(1); } if(i<0‖i>last+1) //当i等于Last+1时表示插入在最后 { cerr<<"参数i越界出错!"<<endl; exit(1); }
  69. 69. //从后向前把前一个元素迁移到后一个元素位置 直到存储位置为i为止 last++;//当前最后元素下标加1for(int j = Last ; j > i ; j --) data[j]=data[j-1]; data[i]=x;//在第i项处插入x}
  70. 70. 考虑移动元素的平均情况: 假设在第 i (0<=i<=n)个元素位置插入的概率为 pi ,则在线性表中插入一个元素所需移动元素次数的期望值为: n Eis pi (n i ) i 0 若假定在线性表中任何一个位置上进行插入的概率都是相等的,则移动元素的期望值为: n 1 n Eis (n i) n 1i 0 2
  71. 71. ④删除元素分析:删除i处的元素时,线性表的逻辑结构发生什么变化?(a1, …, ai-1, ai, …, an) 改变为(a1, …, ai-1, ai+1, …, an)<ai-1, ai>, <ai, ai+1> <ai-1, ai+1>a1 a2 … ai-1 ai ai+1 … ana1 a2 … ai-1 表的长度减少
  72. 72. 删除值为x的元素int SeqList :: Remove ( T & x ) { int i = Find (x)-1; //在表中搜索 x if ( i >= 0 ) { last-- ; for ( int j = i; j <= last; j++ ) data[j] = data[j+1]; return 1; //成功删除 } 思路: ①在顺序表中查找值为x的元素 return 0; //表中没有 x ②若找到,last--,把x后的元 } 素向前移动,返回1,若找不到, 返回0。
  73. 73. ⑤输入操作void SeqList :: input(){//从键盘逐个输入数据建立顺序表cout <<“开始建立顺序表,请输入表中元素个数”; while (1){ cin >>last; if (last<=MaxSize-1) break; cout <<“元素个数有误,范围1~”<<MaxSize-1<<endl; } for (int i=0;i<=last;i++) {cout<<“请输入第”<<i+1<<“个元素:”<<endl; cin>>data [i];} cout<<“表建立完成!” <<endl;}
  74. 74. ⑥输出操作void SeqList :: output(){//将顺序表全部元素输出到屏幕上 cout <<“顺序表元素个数为:”<<Last+1<<endl; for (int i=0;i<=last;i++) cout<<i+1<< “:” <<data [i]<<endl; }
  75. 75. 2.2.4 顺序表的应用例1:编写一个程序向顺序表中插入5个整数值,然后以插 入次序显示这5个数,最后删除这5个数。typedef int T;#include”SeqList.h”void main(void){ SeqList myList(100); for(int i=0;i<5;i++) ∥插入5个整型元素 myList.Insert(i+10,i); for(int i=0;i<5;i++) cout<<myList.GetData(i)<<“ ”; for(int i=0;i<5;i++) myList.Remove(i);} 程序输出为: 10 11 12 13 14
  76. 76. 例2. 集合的“并”运算利用两个线性表LA和LB分别表示两个集合A和B, 现要求一个新的集合A=A∪B。步骤:①取LB中的一个元素 x=LB.GetData(i) ②在LA中找这个元素 LA.Find(x) ③如果LA中没有这个元素则插入LA LA.Insert(x,++Last)
  77. 77. 求集合的“并”集void Union ( SeqList &LA, SeqList LB ) { int n = LA.Length ( ); int m = LB.Length ( ); for ( int i = 1; i <= m; i++ ) { T x = LB.GetData(i); //在B中取一元素 int k = LA.Find (x); //在A中搜索它 if ( k == -1 ) //若未找到插入它 { LA.Insert (x,n++); } } } 时间复杂度: O(LA.Length()×LB.Length())
  78. 78. 例3. 集合的“交”运算利用两个线性表LA和LB分别表示两个集合A和B, 现要求一个新的集合A=A∩B。步骤:①依次取LA中的元素 x=LA.GetData(i) ②在LB中找这个元素 LB.Find(x) ③如果LB中没有这个元素则在LA中删除 LA.Remove(x)
  79. 79. 求“交”集 void Intersection(SeqList &LA, SeqList LB ) { int n = LA.Length ( ); int m = LB.Length ( ); int i = 1; while ( i <= n ) { T x = LA.GetData (i); //在LA中取一元素 int k = LB.Find (x); //在LB中搜索它 if ( k == -1 ) { LA.Remove (x); n--; } else i++; //未找到在LA中删除它 }}
  80. 80. 例4. 已知线性表LA和线性表LB中的数据元素按值非递减有序排列,现要求将LA和LB归并为一个新的线性表LC,且LC中的元素仍按值非递减有序排列。 此问题的思路如下: ①从线性表LA,LB中依次取得每个数据元素: LA.GetData(i), LB.GetData(j) ②较小者插入LC ③把没有插完的线性表中的元素全部插入 LC
  81. 81. void MergeList(SeqList LA,SeqList LB,SeqList &LC) { int i=j=1;k=0; T xa,xb; int LALength=LA.Length(); int LBLength=LB.Length(); while((i<=LALength)&&(j<=LBLength)){ xa=LA.GetData(i); xb=LB.GetData(j); if(xa<=xb) {LC.Insert(xa,k++);++i;} else {LC.Insert(xb,k++);++j;} }
  82. 82. while(i<=LALength){ xa=LA.GetData(i++); LC.Insert(xa,k++); }while(j<= LBLength){ xb=LB.GetData(j++); LC.Insert(xb,k++); } } 时间复杂度为: O(LA.Length()+LB.Length())
  83. 83. 2.3 单链表(Singly Linked List)一、单链表的概念二、结点类的定义与实现三、单链表类的定义与实现
  84. 84. 顺序表的优缺点:优点: ①无需为表示结点间的逻辑关系增加额外的存储 空间,存储利用率高。 ②随机访问缺点: ①在表中插入或删除元素时,平均移动一半的元 素,运行效率很低。 ②占用连续空间 为了克服顺序表的缺点,采用链接方式存储线性 表。链接方式存储的线性表称作链表。
  85. 85. 2.3.1 单链表的概念 用一组地址任意的存储单元存放线性表中的数据元素。元素(数据元素的映象)+ 指针(指示后继元素存储位置)= 结点(node) 单链表的一个存储结点包含两个部分: data link 构成的线性表为:
  86. 86. 头指针 线性表为空表时, 头指针 头结点的指针域为空 空指针 头结点 a1 a2 … ... an ^ 以线性表中第一个数据元素 的存储a1地址作为线性表的地址,称作线性表的头指针(first,head)。 有时为了操作方便,在第一个结点之前虚加一个“头结点”,以指向头结点的指针为链表的头指针。
  87. 87. 单链表的存储映像
  88. 88. 2.3.2 单链表的类定义 通常使用两个类:链表的结点类和链表类,协同表示单链表。 定义方式主要有两种: 复合方式、 嵌套方式
  89. 89. #include <iostream.h> #include <stdlib.h> class List; //前视定义,否则友元无法定义 class LinkNode{ 链表结点类的定义 friend class List; private: LinkNode *link; int data;public:LinkNode (LinkNode *ptr = NULL) {link=ptr;} LinkNode(const int & item, LinkNode *ptr = NULL) { data=item;link=ptr;} ~LinkNode(){};};
  90. 90. 单链表类的定义 class List {private: LinkNode *first; //指向头结点的头指针public: List () { first = new LinkNode ();} //带头结点构造函数 List ( const int &x ) { //不带头结点构造函数 first = new LinkNode ( x ); }
  91. 91. ~List (){makeEmpty(); delete first;} //析构函数void MakeEmpty ( ); //链表置空int Length ( ) const; //求链表长度LinkNode * getHead() const {return first;}LinkNode *Find ( int x );LinkNode *Locate ( int i );DataType GetData ( int i );void SetData (int x,int i );
  92. 92. void Insert (int x, int i );void Remove (int &x, int i ); int IsEmpty()const{ return(first->link==NULL? 1:0;} void input(DataType endTag); void output();};
  93. 93. ①删去链表中除表头结点外的所有其它结点MakeEmpty ( )思路:①设指针q;②当链表不为空(first的link不为NULL): q指向first的下一结点; 把q结点从链表中摘除,删除q ;
  94. 94. void List :: MakeEmpty ( ) {//删去链表中除表头结点外的所有其它结点 LinkNode *q; while ( first→link != NULL ) { q = first→link; first→link = q→link; //将表头结点后第一个结点从链中摘下 delete q; //释放它 }};
  95. 95. ②求单链表的长度:Length ( ) 思路:①指针p指向first的下一结点,计数器为0;②当p不为NULL: 计数器+1; p指向下一结点;③返回计数器的值。
  96. 96. 求表长int List::Length ( ) const {LinkNode *p = first→link; //检测指针p指示第一个结点 int count = 0; while ( p != NULL ) { //逐个结点检测 count++; p = p→link; } return count;}
  97. 97. ③在表中搜索数据x的结点:Find ( int x )LinkNode * List::Find ( int x ){ LinkNode *p = first→link; //指针 p 指示第一个结点 while ( p != NULL && p→data != x ) p = p→link; return p; // p 在搜索成功时返回找到的结点地址 // p 在搜索不成功时返回空值} 思路:①指针p指向first->link ②当链表没有结束且结点的数 据不为x, 则p指向下一结点。 ③返回p
  98. 98. ④定位函数,返回表中第i个数据的地址LinkNode * List::Locate ( int i ){ if ( i < 0 ) return NULL; LinkNode *p = first; int j = 0; while ( p != NULL && j < i ) // j = i 停 { p = p→link; j++; } return p;} 思路:①i<0,返回空 ② 指针p指向first ,j=0 ③当链表没有结束且结点的序号不为i, 则p指向下一结点,j加1。 ④返回p
  99. 99. ⑤取出链表中第i个元素的值DataType List::GetData( int i ) {//提取第 i 个结点的值 LinkNode *p = Locate( i ); // p 指向链表第 i 个结点 return p->data;} 教材上的程序和功能不完全一致p63 思路:①p指针定位到第i个元素 ② 返回p指针的数据域
  100. 100. ⑥给链表中第i个元素赋值void List::SetData(DataType x,int i ) {//给第 i 个结点赋值 if ( i <= 0 ) return; LinkNode *p = Locate ( i ); // p 指向链表第 i 个结点 if (p!=NULL) p->data=x;} 思路:① 若i<=0,返回 ② p指针定位到第i个元素 ③若p不为空,使指针的数据域为x ④返回
  101. 101. ⑦将元素x插入到链表中第i个位置处 Insert (DataType x , int i)有序对 <ai-1, ai>改变为 <ai-1, x> 和<x, ai> ai-1 ai x
  102. 102. void List::Insert (DataType x , int i){//在第i个结点处插入一个data域值为x的新结点 LinkNode *p = Locate ( i-1); LinkNode * newNode = new LinkNode (x) ; //构造新结点newNode newnode->link=p->link; p->link=newNode; //新结点插入第i个结点前 p ai-1 ai} newNode x 算法的时间复杂度为: O(Length())
  103. 103. ⑧删除第i个结点并通过引用返回被删结点的data Remove (DataType &x , int i) 有序对<ai-1, ai> 和 <ai, ai+1> 改变为 <ai-1, ai+1> ai-1 ai ai+1
  104. 104. void List::Remove (DataType &x, int i ){ LinkNode *p = Locate (i-1), *q; q = p->link; p->link = q->link; //重新链接 x = q->data; delete q;} p q ai-1 ai ai+1算法的时间复杂度为: O(Length())
  105. 105. ⑨单链表的输入(前插法) 自行设置一个输入数据的结束标志,用以结束结点的输入。设计思路:①建立一头结点②输入一结点的值③当结点的值不为结束标志时,创建新结点,插 入到头结点后,直到输入的值为结束标志。
  106. 106. ⑨单链表的输入(前插法)void List :: input (DataType endTag){ LinkNode *newnode; DataType val; first=new LinkNode (); if (first==NULL) {cerr<<"存储分配错误"<<endl;exit(1);} cin>>val; while(val!=endTag) { newnode=new LinkNode (val); if (newnode==NULL) {cerr<<"存储分配错误"<<endl;exit(1);} newnode->link=first->link; first->link=newnode; cin>>val; }}
  107. 107. ⑩单链表的输出void List ::output ( ) {//依次输出各结点的值LinkNode *p=first->link;while(p!=NULL) { cout<<p->data<<endl; p=p->link; }}
  108. 108. 2.4 线性链表的其它变形 例如 n = 8 m = 32.4.1 循环链表 0 7 8 1 1 6 7 22.4.2 双向链表 6 3 2 5 5 4 3 4
  109. 109. 2.4.1 循环链表 对于单链表而言,最后一个结点的指针域是空指针,如果将该链表头指针置入该指针域,则使得链表头尾结点相连,就构成了单循环链表。 a1 a2 … ... an 和单链表的差别仅在于,判别链表中最后一个结点的条件不再是“后继是否为空”,而是“后继是否为头结点”。
  110. 110.  循环链表的示例first a0 a1 a2 an-1 带表头结点的循环链表first a0 a1 an-1 (非空表)first (空表)
  111. 111. 循环链表类的定义P67 循环链表与单链表的操作实现,最主要的不同就是扫描到链尾,遇到的不是NULL,而是表头。
  112. 112. 循环链表的搜索算法first 31 48 15 57搜索15 p p 搜索成功 pfirst 31 48 15 57 p p p p p搜索25 搜索不成功 113
  113. 113. 循环链表的搜索算法CircListNode * CircList::Find( DataType x ){//在链表中从头搜索其数据值为 x 的结点 CircListNode * p = first->link; while ( p!= first && p->data != x ) p = p->link; if ( p != first ) return p; //搜索成功 else return NULL; //搜索不成功} first 31 48 15 57搜索15 p
  114. 114. 带尾指针的循环链表 22 31 48 15 57 rear 如果插入与删除仅在链表的两端发生,可 采用带表尾指针的循环链表结构。  在表尾插入: 时间复杂性 O(1)  在表尾删除: 时间复杂性 O(n)  在表头插入: 时间复杂性 O(1)  在表头删除: 时间复杂性 O(1)思考:如何改进使在表尾删除的时间复杂度也为O(1)? 115
  115. 115. 用循环链表求解约瑟夫问题 约瑟夫问题: n 个人围成一个圆圈,首先第 1 个人从 1 开始,一个人一个人顺时针报数, 报到第 m 个人,令其出列。然后再从下一 个人开始,从 1 顺时针报数,报到第 m 个人,再令其出列,…,如此下去,直到圆圈中只剩一个人为止。此人即为优胜者。 用不带表头结点的循环链表来组织。 116
  116. 116. 例如 n = 8 m = 3 0 0 0 7 7 7 8 1 1 8 1 1 8 1 16 7 2 6 7 2 6 7 2 6 3 2 6 3 2 6 3 2 5 5 4 5 5 4 5 5 4 3 3 3 4 4 4 0 0 0 7 7 7 8 1 1 8 1 1 8 1 1 6 7 2 6 7 2 6 7 2 6 3 2 6 3 2 6 3 2 5 5 4 5 5 4 5 5 4 3 3 3 4 4 4 117
  117. 117. 0 0 0 7 7 7 8 1 1 8 1 1 8 1 16 7 2 6 7 2 6 7 2 6 3 2 6 3 2 6 3 2 5 5 4 5 5 4 5 5 4 3 3 3 4 4 4 n=8 m=3算法讨论: 目前指针指向8,如何把8从链表中摘除? 118
  118. 118. 求解Josephus问题的算法#include <iostream.h>#include ―CircList.h‖void Josephus(CircList & Js, int n, int m) { CircLinkNode *p = Js.getHead(); *pre = NULL; int i, j; for ( i = 0; i < n-1; i++ ) { //执行n-1次 for ( j = 1; j < m; j++) //数m-1个人 { pre = p; p = p->link; } cout << ―出列的人是” << p->data << endl; 119
  119. 119. pre->link = p->link; delete p; //删去 p = pre->link; }};void main() { CircList clist; int i, n m; cout << ―输入游戏者人数和报数间隔 : ‖; cin >> n >> m; for (i = 1; i <= n; i++ ) clist.insert(i, i); //约瑟夫环 Josephus(clist, n, m); //解决约瑟夫问题} 120
  120. 120. 2.4.2 双向链表 (Doubly Linked List) 双向链表是指在前驱和后继方向都能游历(遍历)的线性链表。 双向链表每个结点结构: lLink data rLink 前驱方向   后继方向 双向链表通常采用带表头结点的循环链表形式。 121
  121. 121. first first 非空表 空表  结点指向 p == p->lLink->rLink == p->rLink->lLink rLink lLink p->lLink p p->rLink 122
  122. 122. 双向循环链表的搜索算法DblNode *DblList::Find (DataType x, int d) {//在双向循环链表中寻找其值等于x的结点, //按d确定 搜索方向,d为0,向左搜索, d为1,向右搜索 DblNode *p= (d == 0)?first->lLink : first->rLink; while ( p != first && p->data != x ) p = (d == 0) ? p->lLink : p->rLink; if ( p != first ) return p; //搜索成功 else return NULL; //搜索失败}; 123
  123. 123. 双向循环链表的插入算法first 31 48 15后插入25 pfirst 31 48 25 15 p newNode newNode->rLink = p->rLink; p->rLink = newNode; newNode->rLink->lLink = newNode; newNode->lLink = p;
  124. 124. 双向循环链表的删除算法first 31 48 15 非空表删除48 p p->rLink->lLink = p->lLink; p->lLink->rLink = p->rLink; 125
  125. 125. 2.5 单链表的应用 多项式(Polynomial) Pn ( x ) a0 a1 x a2 x 2  an x n n i ai x i 0 n阶多项式 Pn(x) 有 n+1 项。 系数 a0, a1, a2, …, an 指数 0, 1, 2, …, n。按升幂排列 126
  126. 126. 2.5.1 多项式的存储表示 第一种: 静态数组表示 在类的私有域中定义多项式的数据成员: private: int degree; float coef [maxDegree+1]; 则Pn(x)可以表示为: pl.degree = n, pl.coef[i] = ai, 0 i n 0 1 2 degree maxDegreecoef a0 a1 a2 …… an ……… n 127
  127. 127. 第二种:动态数组表示在类的私有域中定义多项式的数据成员:private: int degree; float * coef; Polynomial :: Polynomial (int sz) { degree = sz; coef = new float [degree + 1]; } 以上两种存储表示适用于指数连续排列的多项式。但对于多数项的系数为零的稀疏多项式,如 P101(x) = 3+5x50128 101, 空间利用率太低。 -4x
  128. 128. 第三种:只存储非零系数项的系数和指数 0 1 2 i mcoef a0 a1 a2 …… ai …… amexp e0 e1 e2 …… ei …… emstruct term { //多项式的项定义 float coef; //系数 int exp; //指数};static term termArray[maxTerms]; //项数组static int free, maxTerms; //当前空闲位置指针 129
  129. 129. 两个多项式存储的例子 A(x) = 2.0x1000+1.8 B(x) = 1.2 + 51.3x50 + 3.7x101 A.start A.finish B.start B.finish free maxTermscoef 1.8 2.0 1.2 51.3 3.7 ……exp 0 1000 0 50 101 …… 两个多项式存放在termArray中 131
  130. 130. 第四种:多项式的链表存储表示 多项式顺序存储表示的缺点:  插入和删除时项数可能有较大变化,因此 要移动大量数据  不利于多个多项式的同时处理 采用多项式的链表表示可以克服这类困难:  多项式的项数可以动态地增长,不存在存 储溢出问题。  插入、删除斱便,不移动元素。 132
  131. 131. 多项式的链表结构 在多项式的链表表示中,每个结点三个数据 成员: coef exp link 通过链接指针,可以将多项式各项按指数递 增的顺序链接成一个单链表。 在此结构上,新项的加入和废项的删除执行 简单的链表插入和删除操作即可解决。 133
  132. 132. 2.6 静态链表 处理时中可以不改变各元素的物理位置,只 要重新链接就能改变这些元素的逻辑顺序。 它是利用数组定义的,在整个运算过程中存 储空间的大小不会变化。 静态链表每个结点由两个数据成员构成: data域存储数据,link域存放链接指针。 所有结点形成一个结点数组。 155
  133. 133. 2.1 线性表2.2 顺序表2.3 单链表2.4 线性链表的其它变形2.5 单链表的应用:一元多项式及其运算作业:(p84)6,20,21,27,28
  134. 134. 第三章 栈与队列• 栈• 队列• 栈的应用:表达式求值• 栈的应用:递归• 队列的应用:打印杨辉三角形• 优先级队列 157
  135. 135. 3.1 栈 ( Stack )3.1.1 栈的定义 只允许在一端插入和删除的线性表。 退栈 进栈 允许插入和删除的一端称为栈顶 (top),另一端称为栈底(bottom) top an-1• 特点 an-2 后进先出 (LIFO) a0 bottom 158
  136. 136. 栈的抽象数据类型class Stack { //栈的类定义 public: Stack(){ }; //构造函数 ~Stack(){ }; virtual void Push(DataType & x) ; //进栈 virtual bool Pop(DataType& x); //出栈 virtual bool getTop(DataType & x); //取栈顶 virtual bool IsEmpty(); //判栈空 virtual bool IsFull(); //判栈满}; 栈的抽象数据类型有两种典型的存储表示,基 于数组的存储表示实现的栈称为顺序栈,基于链表 的存储表示实现的栈称为链式栈。 159
  137. 137. 栈的数组存储表示 — 顺序栈 0 1 2 3 4 5 6 7 8 9 maxSize-1elements top (栈空) typedef int DataType; class SeqStack { //顺序栈类定义 private: DataType *elements; //栈元素存放数组 int top; //栈顶指针 int maxSize; //栈最大容量 160
  138. 138. void overflowProcess(); //栈的溢出处理public: SeqStack(int sz =50); //构造函数 ~SeqStack() { delete []elements; } //析构函数 void Push(DataType &x); //进栈 bool Pop(DataType& x); //出栈 bool getTop(DataType& x); //取栈顶内容 bool IsEmpty() const { return top == -1; } bool IsFull() const { return top == maxSize-1; } int getSize() const {return top+1;} void MakeEmpty(){top=-1;}friend ostream&operator<<(ostream&os,SeqStack &S)}; 161
  139. 139. top b top a atop 空栈 a 进栈 b 进栈top e top e e d d top d c c c b b b a a a e 进栈 f 进栈溢出 e 退栈 162
  140. 140. dtop c c b top b b a a top a d 退栈 c 退栈 b 退栈 atop a 退栈 top 空栈 163
  141. 141. ①顺序栈的构造函数#include <assert.h>#include <iostream.h>SeqStack:: SeqStack(int sz){ elements=new DataType[sz]; assert(elements!=NULL); top=-1; maxSize=sz;} 断言(assert)机制是C++提供的一种功 能,若参数表中给定的条件满足,则继续执行
  142. 142. ②顺序栈的溢出处理void SeqStack::overflowProcess() {//私有函数:当栈满则执行扩充栈存储空间处理 DataType *newArray = new DataType [2*maxSize]; //创建更大的存储数组 for (int i = 0; i <= top; i++) newArray[i] = elements[i]; maxSize += maxSize; delete [ ]elements; elements = newArray; //改变elements指针}; 思路:①创建更大的存储数组 ②把原来的元素复制到新数组中 ③改变栈大小,删除原来的数组, 栈指针指向新数组 165
  143. 143. ③入栈操作void SeqStack::Push(DataType & x) {//若栈满,则溢出处理,将元素x插入该栈栈顶 if (IsFull() == true) overflowProcess( ); //栈满 elements[++top] = x; //栈顶指针先加1, 再进栈};④出栈操作bool SeqStack::Pop(DataType & x) {//若栈不空,函数退出栈顶元素并将栈顶元素的值赋给x,//返回true,否则返回false if (IsEmpty() == true) return false; x = elements[top--]; //先取元素,栈顶指针退1 return true; //退栈成功}; 166
  144. 144. ⑤取栈顶元素bool SeqStack::getTop(DataType& x) {//若栈不空则x为该栈栈顶元素 if (IsEmpty() == true) return false; x = elements[top]; return true;};⑥输出栈中元素的重载操作<<ostream& operator<<(ostream&os,SeqStack &S){ os<<―top=―<<S.top<<endl; for (int i=0;i<=S.top;i++) os<<i<<―:‖<<S. elements[i] <<endl; return os; 167};
  145. 145. 双栈共享一个栈空间 0 maxSize-1Vb[0] t[0] t[1] b[1]两个栈共享一个数组空间V[maxSize]设立栈顶指针数组 t[2] 和栈底指针数组 b[2] t[i]和b[i]分别指示第 i 个栈的栈顶与栈底初始 t[0] = b[0] = -1, t[1] = b[1] = maxSize栈满条件:t[0]+1 == t[1] //栈顶指针相遇栈空条件:t[0] = b[0]或t[1] = b[1] //退到栈底 168
  146. 146. 3.1.3 栈的链接存储表示 — 链式栈top ^ • 链式栈无栈满问题,空间可扩充 • 插入与删除仅在栈顶处执行 • 链式栈的栈顶在链头 • 适合于多栈操作 171
  147. 147. 链式栈 (LinkedStack)类的定义typedef int DataType;struct StackNode { //栈结点类定义public: DataType data; //栈结点数据 StackNode *link; //结点链指针 StackNode(DataType d = 0, StackNode *next = NULL) : data(d), link(next) { } ~StackNode();}; 172
  148. 148. class LinkedStack : { //链式栈类定义private: StackNode *top; //栈顶指针public: LinkedStack() : top(NULL) {} //构造函数 ~LinkedStack() { makeEmpty(); } //析构函数 void Push(DataType &x); //进栈 bool Pop(DataType & x); //退栈 173
  149. 149. bool getTop(DataType & x) const; //取栈顶 元素 bool IsEmpty() const { return (top == NULL)? true:false; } int getSize()const; void makeEmpty(); //清空栈的内容 friend ostream& operator << (ostream& os, LinkedStack& s) ; //输出栈元素的重载操作 <<}; 174
  150. 150. 链式栈类操作的实现#include <iostream.h>①清空栈操作void LinkedStack::makeEmpty( ) {//逐次删去链式栈中的元素直至栈顶指针为空。 StackNode *p; while (top != NULL) { //逐个结点释放 p = top; top = top->link; delete p; }}; top ^ 175
  151. 151. ②入栈操作void LinkedStack::Push(DataType &x) {//将元素值x插入到链式栈的栈顶,即链头。 top = new StackNode(x, top); //创建新结点 assert (top != NULL); //创建失败退出}; top x top ^③出栈操作bool LinkedStack::Pop(DataType & x) {//删除栈顶结点, 返回被删栈顶元素的值。 if (IsEmpty() == true) return false; //栈空返回 StackNode *p = top; //暂存栈顶元素 top = top->link; //退栈顶指针 x = p->data; delete p; //释放结点 return true; 176};
  152. 152. ④取栈顶元素bool LinkedStack::getTop(DataType & x) const { if (IsEmpty() == true) return false; //栈空返回 x = top->data; //返回栈顶元素的值 return true; }; top⑤ ^ostream& operator << (ostream& os, LinkedStack& S){// //输出栈元素的重载操作 << os<<“栈中元素个数=”<<S.getSize()<<endl; StackNode *p=S.top;int i=0; while(p!=NULL) {os<<++i<<“:”<<p->data<<endl; p=p->link;} return os; 177};
  153. 153. 公共邮箱:rj09123@163.com 密码:098765
  154. 154. 关于栈的进一步讨论 问题:当进栈元素的编号为1, 2, …, n时,可能的出栈序列有多少种? 179
  155. 155. • 设进栈元素数为n,可能出栈序列数为mn:  n = 0,m0 = 1: 出栈序列{}。  n = 1,m1 = 1: 出栈序列{1}。  n = 2,m2 = 2:= m0*m1+m1*m0 a) 出栈序列中1在第1位。1进 1出 2进 2出, 出栈序列为{1, 2}。= m0*m1= 1 b) 出栈序列中1在第2位。1进 2进 2出 1出, 出栈序列为{2, 1}。= m1*m0 = 1  n = 3,m3 = 5: = m0*m2+m1*m1+m2*m0 a) 出栈序列中1在第1位。后面2个元素有m2 = 2个出栈序列:{1, 2, 3}, {1, 3, 2}。 180
  156. 156. = m0*m2 = 2  出栈序列中1在第2位。1前有2后有3, 出栈序列为 {2, 1, 3}。= m1*m1 = 1  出栈序列中1在第3位。前面2个元素有 m2 = 2个出栈序列:{2, 3, 1}, {3, 2, 1}。 = m2*m0 = 2 n = 4,m4 = 14: = m0*m3+m1*m2+m2*m1+m3*m0 a) 出栈序列中1在第1位。后面3个元素有 m3 = 5个出栈序列: {1, 2, 3, 4}, {1, 2, 4, 3}, {1, 3, 2, 4}, {1, 3, 4, 2}, {1, 4, 3, 2}。 181
  157. 157. = m0*m3 = 5b) 出栈序列中1在第2位。前面有2,后面3、 4有m2 = 2个出栈序列: {2, 1, 3, 4}, {2, 1, 4, 3}。 = m1*m2 = 2c) 出栈序列中1在第3位。前面2、3有m2 = 2个出栈序列,后面有4: {3, 2,1, 4}, {2, 3, 1,4}。 = m2*m1 = 2d) 出栈序列中1在第4位。前面3个元素有 m3 = 5个出栈序列:{2, 3, 4, 1}, {2, 4, 3, 1}, {3, 2, 4, 1}, {3, 4, 2, 1}, {4, 3, 2, 1}。 = m3*m0 = 5 182
  158. 158.  一般地,设有 n 个元素按序号1, 2, …, n 进栈,轮流让 1在出栈序列的第1, 第2, … 第n位,则可能的出栈序列数为: n -1 mi * mn i 1 m0 * mn 1 m1 * m n 2  mn 1 * m0 i 0 推导结果为: n 1 1 n n 1 C2n mi * mn i 1 i 0 183
  159. 159. 3.1.4 栈的应用--数制转换 算法基于原理: N = (N div d)×d + N mod d
  160. 160. 例如:(1348)10 = (2504)8 ,其 运算过程如下: N N div 8 N mod 8 计 1348 168 4 输 算 168 21 0 出 顺 顺 序 21 2 5 序 2 0 2
  161. 161. 思路:①初始化栈②输入要转换的数据N③当N不为0,把N%8取余入栈④当栈不为空,栈顶元素出栈,输出。
  162. 162. void conversion (){SeqStack S; int N; int x=0; bool continue1;cin >>N; while (N) { S.Push( N % 8); N = N/8; } continue1=S.Pop(x); while (continue) { cout << x <<endl; continue=S.Pop(x); }} // conversion
  163. 163. 3.1.5 栈的应用--括号匹配的检验假设在表达式中([]())或[([ ][ ])]等为正确的格式,[( ])或([( ))或 (()])为不正确的格式。则 检验括号是否匹配的方法可用“期待的急迫程度”这个概念来描述。 例如:考虑下列括号序列: [ ( [ ] [ ] )] 分析可能出现的不匹配的情况:• 到来的右括弧并非是所“期待”的;• 直到结束,也没有到来所“期待”的括弧。
  164. 164. 算法的设计思路:①左括号入栈②右括号,检验栈空? 若空,表明右括号多了 匹配,栈顶的左括号出栈 非空,与栈顶元素比较 不匹配,出错③表达式检验结束时,若空,则匹配,若非 空,则表明左括号多了。
  165. 165. bool matching(char *exp) { int state = 1; i = 0;L = Length(exp);char x; while (i<L && state) { switch (exp[i] ){ case ‗(‗,‘[‗:{S.Push(exp[i]); i++; break;} case‟)‘: { if(!S.StackEmpty()&&S.GetTop()==‗(‗ ) {S.Pop(char &x); i++;} else {state = 0;} break; } case‘]‘:{自己写出} } if (S.StackEmpty()&&state) return trueelse return false
  166. 166. 3.1.6 栈的应用举例--表达式求值1. 表达式的三种表示方法:设 Exp = S1 + OP + S2则称 OP + S1 + S2 为前缀表示法 S1 + OP + S2 为中缀表示法 S1 + S2 + OP 为后缀表示法
  167. 167. 例如: Exp = a b + (c d / e) f 前缀式: + ab c/def 中缀式: a b + (c d / e) f 后缀式: ab cde/ f +结论: 1)操作数之间的相对次序不变; 2)运算符的相对次序不同; 3)中缀式有操作符的优先级问题,还有可加括号改 变运算顺序的问题,所以编译程序一般不使用中缀表 示处理表达式。
  168. 168. 例如: Exp = a b + (c d / e) f 前缀式: + ab c/def 中缀式: a b + (c d / e) f 后缀式: ab cde/ f +结论:4)前缀式的运算规则为: 连续出现的两个操作数和在它们之前且紧靠它们的运算符构成一个最小表达式;5)后缀式的运算规则为: 运算符在式中出现的顺序恰为表达式的运算顺序;每个运算符和在它之前出现且紧靠它的两个操作数构成一个最小表达式。
  169. 169. 2.如何从后缀式求值?先找运算符, 再找操作数例如: ab cde/ f +a b d/e c-d/e (c-d/e) f
  170. 170. 模拟一个简单的后缀表达式计算器Calculator类的定义:class Calculator{public: Calculator(int sz){S=sz;}; void Run(); //执行表达式计算 void Clear();private: SeqStack S; void AddOperand(double value); //进操作数栈 bool Get2Operands(double&left, double&right); void DoOperator(char op);//形成运算指令,计算 };
  171. 171. ①将操作数的值value入操作数栈void Calculator:: AddOperand(double value){ S.Push(value);};②清栈void Calculator:: Clear() { S.MakeEmpty();};
  172. 172. ③从操作数栈中取出两个操作数bool Calculator::Get2Operands(double&left, double&right){ if (S.IsEmpty()) {cerr<<“缺少右操作数!”<<endl;return false;} S.Pop(right); if (S.IsEmpty()) {cerr<<“缺少左操作数!”<<endl;return false;} S.Pop(left); return true;};
  173. 173. ④读字符串并求后缀表达式的值void Calculator::Run{ char ch; double newoperand; while(cin.get(ch),ch!=„#‟){ switch(ch){ case „+‟:case „-‟: case „*‟: case „/‟: DoOperator(ch); break; default:cin.putback(ch); cin>>newOperand; AddOperand(newOperand); } } S.Pop(newoperand ); cout<< newoperand; };
  174. 174. ⑤取两个操作数,根据操作符op计算void Calculator:: DoOperator(char op){ double left,right,value; bool result; result = get2Operands(left, right) if (result) switch(op){ case „+‟:value=left+right;S.Push(value);break; case „-‟: value=left-right;S.Push(value);break; case „*‟: value=left*right;S.Push(value);break;
  175. 175. case „/‟: if (right==0.0){ cerr<<“Divide by 0!”<<endl; Clear();} else{value=left/right;S.Push(value);} break; } else Clear(); };void main(){ Calculator CALC(20); CALC.Run();}
  176. 176. 3.如何将中缀表示→转后缀表示?• 先对中缀表达式按运算优先次序加上括号,再 把操作符后移到右括号的后面并以就近移动为 原则,最后将所有括号消去。• 如中缀表示 (A+B)*D-E/(F+A*D)+C,其转换为 后缀表达式的过程如下: ( (( (A+ B ) * D ) – ( E / ( F+ (A* D ) )) ) + C ) 后缀表示 A B + D * E F A D * + / - C + 201
  177. 177. 如何将中缀表示→转前缀表示?• 先对中缀表达式按运算优先次序通统加上括 号,再把操作符前移到左括号前并以就近移 动为原则,最后将所有括号消去。• 如中缀表示 (A+B)*D-E/(F+A*D)+C,其转 换为前缀表达式的过程如下:( ( ( (A+ B ) * D ) – ( E / ( F+ (A* D ) ) ) ) + C )前缀表示 + - * + A B D / E + F * A D C 202
  178. 178. 计算机如何将中缀表示转换为后缀表示? 使用栈可将表达式的中缀表示转换成它的前缀表 栈• 示和后缀表示。• 为了实现这种转换,需要考虑各操作符的优先级。 各个算术操作符的优先级 操作符 ch # ( *, /, % +, - ) isp (栈内) 0 1 5 3 6 icp (栈外) 0 6 4 2 1• isp叫做栈内(in stack priority)优先数• icp叫做栈外(in coming 203 priority)优先数。
  179. 179. 栈顶算符为θ1:θ 1 θ 2 + - * / ( ) # + > > < < < > > - > > < < < > > * > > > > < > > / > > > > < > > ( < < < < < = X ) > > > > X > > # < < < < < X =
  180. 180. 中缀表达式转换为后缀表达式的算法• 例:中缀表达式为A+B*(C-D)-E/F,求其后缀表 达式。• ①操作符栈初始化,将结束符‘#‟进栈。然后读 入中缀表达式字符流的首字符ch。• ②重复执行以下步骤,直到ch = „#‟,同时栈 顶的操作符也是‘#‟,停止循环。 205
  181. 181. a.若ch是操作数直接输出,读入下一个字符ch。 b.若ch是操作符,判断ch的优先级icp和位于栈顶的 操作符op的优先级isp:  若 icp(ch) > isp(op),令ch进栈,读入下一个字符 ch。  若 icp(ch) < isp(op),退栈并输出。  若 icp(ch) == isp(op),退栈但不输出,若退出的 是“(”号读入下一个字符ch。 • ③算法结束,输出序列即为所需的后缀表达式。 • 例:中缀表达式为A+B*(C-D)-E/F,求其后缀表达式。操作符 ch # ( *, /, % +, - ) ABCD-*+EF/-isp (栈内) 0 1 5 3 6icp (栈外) 0 6 4 206 2 1
  182. 182. 中缀表达式转换为后缀表达式的算法:void postfix(expression e) //把中缀表达式e转换成后缀表示并输出 Stack S; char ch=„#‟,ch1,op; S.Push(ch);cin.get(ch); While(S.IsEmpty()==false&&ch!=„#‟) if (isdigit(ch)){cout <<ch;cin.get(ch);} else {S.getTop(ch1); if (isp(ch1)<icp(ch)){S.Push(ch);cin.get(ch);} else if (isp(ch1)>icp(ch)) {S.Pop(op);cout<<op;} else { S.Pop(op); if(op==„(„) cin.get(ch); } } 207};
  183. 183. 4.如何应用中缀表示计算表达式的值 a+b*(c-d)-e/f rst1 rst4 rst2 rst3 rst5 使用两个栈,操作符栈OPTR (operator), 操作数栈OPND(operand) 为了实现这种计算,需要考虑各操作符的 优先级 208
  184. 184. 中缀算术表达式求值 对中缀表达式求值的一般规则: 1. 建立并初始化OPTR栈和OPND栈,然后 在OPTR栈中压入一个“#” 2. 扫描中缀表达式,取一字符送入ch。 3. 当ch != „#‟ 或OPTR栈的栈顶 != „#‟时, 执 行以下工作, 否则结束算法。在OPND栈 的栈顶得到运算结果。 209
  185. 185. ①若ch是操作数,进OPND栈,从中缀表达式取下一字符送入ch;②若ch是操作符,比较icp(ch)的优先级和isp(OPTR)的优先级:若icp(ch) > isp(OPTR),则ch进OPTR栈,从中缀表达式取下一字符送入ch;若icp(ch) < isp(OPTR),则从OPND栈退出a2和a1,从OPTR栈退出θ, 形成运算指令 (a1)θ(a2),结果进OPND栈; 若icp(ch) == isp(OPTR) 且ch == ),则从OPTR栈退出(,对消括号,然后从中缀表达式取下一字符送入ch; 操作符 ch # ( * , / , %+, - ) a+b * (c-d)-e/f i sp( 栈内) 0 1 5 3 6 i cp( 栈外) 210 0 6 4 2 1
  186. 186. void InFixRun() { SeqStack <char> OPTR, SeqStack <double>OPND; char ch, op; double x; OPTR.Push(„#‟); cin.get (ch); //读入一个字符 op = # ; while (ch != # || op != #) { if (isdigit(ch)) //是操作数, 进栈 { cin.putback(ch);cin>>x OPND.Push(x); cin.get(ch); } else { //是操作符, 比较优先级 OPTR.GetTop(op); //读一个操作符 211
  187. 187. if (icp(ch) > isp(op)) //栈顶优先级低 { OPTR.Push (ch); cin.get(ch); } else if (icp(ch) < isp(op)) { OPTR.Pop(op); //栈顶优先级高 DoOperator(op); } else if (ch == „)‟) //优先级相等 { OPTR.Pop(op); cin.get(ch); } } OPTR.GetTop(op); } /*end of while*/} 212
  188. 188. 3.2 栈与递归• 递归的定义 若一个对象部分地包含它自己,或用它自己 给自己定义, 则称这个对象是递归的;若一 个过程直接地或间接地调用自己, 则称这个 过程是递归的过程。• 以下三种情况常常用到递归方法。  定义是递归的  数据结构是递归的  问题的解法是递归的 213
  189. 189. 1. 定义是递归的例如,阶乘函数 1, 当n 0时 n! n (n 1)!, 当 n 1时求解阶乘函数的递归算法long Factorial(long n) { if (n == 0) return 1; else return n*Factorial(n-1);} 214
  190. 190. 求解阶乘 n! 的过程 主程序 main : fact(4) 参数 4 计算 4*fact(3) 返回 24递 参 结 回归 数 参数 3 计算 3*fact(2) 返回 6 果 归调 传 返 求用 递 参数 2 计算 2*fact(1) 返回 2 回 值 参数 1 计算 1*fact(0) 返回 1 参数 0 直接定值 = 1 返回 1 215
  191. 191. 2.数据结构是递归的• 例如,单链表结构 f f• 一个结点,它的指针域为NULL,是一个单 链表;• 一个结点,它的指针域指向单链表,仍是一 个单链表。 216
  192. 192. 搜索链表最后一个结点并打印其数值void Print(ListNode *f) { if (f ->link == NULL) cout << f ->data << endl; else Print(f ->link);} 递归找链尾 f a0 a1 a2 a3 a4 f f f f 217
  193. 193. 在链表中寻找等于给定值的结点并打印其数值void Print(ListNode*f, DataType value) { if (f != NULL) if (f -> data == value) cout << f -> data << endl; else Print(f -> link, value);} 递归找含value值的结点 f x f f f 218
  194. 194. 3. 问题的解法是递归的:汉诺塔问题 219
  195. 195. 3. 问题的解法是递归的• 汉诺塔(Tower of Hanoi)问题的解法: 如果 n = 1,则将这一个盘子直接从 A 柱移 到 C 柱上。否则,执行以下三步: ① 用 C 柱做过渡,将 A 柱上的 (n-1) 个盘 子移到 B 柱上; ② 将 A 柱上最后一个盘子直接移到 C 柱上; ③ 用 A 柱做过渡,将 B 柱上的 (n-1) 个盘 子移到 C 柱上。 220
  196. 196. • 解决方法: n=1时,直接把圆盘从A移到C – n>1时, – ①先把上面n-1个圆盘从A移到B ②然后将n号盘从A移到C – ③再将n-1个盘从B移到C。 – 即把求解n个圆盘的Hanoi问题转化为求解n-1个圆盘的 Hanoi问题,依次类推,直至转化成只有一个圆盘的Hanoi 问题 – 执行情况: » 递归工作栈保存内容:形参n,x,y,z和返回地址 » 返回地址用行编号表示 n x y z 返回地址
  197. 197. main() { int m; printf("Input number of disks:‖); scanf("%d",&m); hanoi(m,A,B,C);(0) }void hanoi(int n,char x,char y,char z)(1) {(2) if(n==1)(3) move(1,x,z);(4) else{(5) hanoi(n-1,x,z,y);(6) move(n,x,z);(7) hanoi(n-1,y,x,z);(8) }(9) }
  198. 198. 构成递归的条件• 不能无限制地调用本身,必须有一个出口, 化简为非递归状况直接处理。 Procedure <name> ( <parameter list> ) { if ( < initial condition> ) //递归结束条件 return ( initial value ); else //递归 return (<name> ( parameter exchange )); } 223
  199. 199. 3.2.2递归过程与递归工作栈• 递归过程在实现时,需要自己调用自己。• 层层向下递归,退出时的次序正好相反: 递归调用 n! (n-1)! (n-2)! 1! 0!=1 返回次序• 主程序第一次调用递归过程为外部调用;• 递归过程每次递归调用自己为内部调用。• 它们返回调用它的过程的地址不同。 224
  200. 200. long Factorial(long n) { int temp; if (n == 0) return 1; else temp = n * Factorial(n-1);RetLoc2 return temp; } void main() { int result; result = Factorial(4);RetLoc1 cout << result <<endl; } 225
  201. 201. 1. 递归工作栈• 每一次递归调用时,需要为过程中使用的参 数、局部变量等另外分配存储空间。• 每层递归调用需分配的空间形成递归工作记 录,按后进先出的栈组织。• 栈顶的工作记录是当前正在执行的这一层的 工作记录。称之为活动记录。 活动 局部变量 递归 记录 返回地址 工作记录 框架 参 226 数
  202. 202. 函数递归时的活动记彔调用块 ………………. <下一条指令> Function(<参数表>)函数块 ………………. <return>返回地址(下一条指令) 局部变量 参数 227
  203. 203. 2. 递归过程改为非递归过程• 递归过程简洁、易编、易懂• 递归过程效率低,重复计算多• 改为非递归过程的目的是提高效率• 单向递归和尾递归可直接用迭代实现其 非递归过程• 其他情形必须借助栈实现非递归过程 229
  204. 204. 斐波那契数列的函数Fib(n)的定义 n, n 0,1Fib(n) Fib(n 1) Fib(n 2), n 1如 F0 = 0, F1 = 1, F2 = 1, F3 = 2, F4 = 3, F5 = 5求解斐波那契数列的递归算法 long Fib(long n) { if (n <= 1) return n; else return Fib(n-1)+Fib(n-2); } 230
  205. 205. 时间复杂度: 2 n Fib(5) Fib(4) Fib(3) Fib(3) Fib(2) Fib(2) Fib(1) Fib(2) Fib(1) Fib(1) Fib(0) Fib(1) Fib(0) Fib(1) Fib(0) 斐波那契数列的递归调用树 调用次数 NumCall(k) = 2*Fib(k+1)-1 如 F0 = 0, F1 = 1, F2 = 231 F3 = 2, F4 = 3, F5 = 5 1,
  206. 206. 单向递归用迭代法实现long FibIter(long n) { if (n <= 1) return n; long twoback = 0, oneback = 1, Current; for (int i = 2; i <= n; i++) { Current = twoback + oneback; twoback = oneback; oneback = Current; } return Current;} 232
  207. 207. 尾递归用迭代法实现25 36 72 18 99 49 54 63void recfunc(int A[ ], int n) { //从n到0输出数组各项的值。 if (n >= 0) { cout << “value:”<<A[n] << endl; n--; recfunc(A, n); } 尾递归:递归调用语句只有一个,} 而且放在过程的最后 233
  208. 208. void sterfunc(int A[ ], int n) {//消除了尾递归的非递归函数 while (n >= 0) { cout << "value:" << A[n] << endl; n--; }} 234
  209. 209. 3.2.3 递归与回溯 对一个包含有许多结点,且每个结点有多个分支的问题,可以先选择一个分支进行搜索。当搜索到某一结点,发现无法再继续搜索下去时,可以沿搜索路径回退到前一结点,沿另一分支继续搜索。 如果回退之后没有其它选择,再沿搜索路径回退到更前结点,…。依次执行,直到搜索到问题的解,或搜索完全部可搜索的分支没有解存在为止。 (p110迷宫问题) 回溯法与分治法本质相同,可用递归求解。 235
  210. 210. 3.3 队列 ( Queue ) a0 a1 a2 an-1 front rear• 定义  队列是只允许在一端删除,在另一端插入 的线性表  允许删除的一端叫做队头(front),允许插 入的一端叫做队尾(rear)。• 特性:先进先出(FIFO, First In First Out) 236
  211. 211. 队列的抽象数据类型class Queue {public: Queue() { }; //构造函数 ~Queue() { }; //析构函数 virtual bool EnQueue(DataType x) = 0; //进队列 virtual bool DeQueue(DataType& x) = 0; //出队列 virtual bool getFront(DataType& x) = 0; //取队头 virtual bool IsEmpty() const = 0; //判队列空 virtual bool IsFull() const = 0; //判队列满}; 237
  212. 212. 3.3.2 队列的数组存储表示 ─顺序队 列 队列的进队和出队 Afront rear 空队列 front rear A进队 A B A B C Dfront rear B进队 front rear C, D进队 B C D C Dfront rear A退队 front rear B退队 假溢出 C D E F G C D E F Gfront rear E,F,G进队 front rear H进队,溢出 238
  213. 213. 队列的进队和出队原则进队时先将新元素按 rear 指示位置加入,再将队 尾指针加一 rear = rear + 1。队尾指针指示实际队尾的后一位置。出队时先按队头指针指示位置取出元素,再将队头指针进一 front = front + 1,队头指针指示实际队头位置。队满时再进队将溢出出错;队空时再出队将队空处理。解决假溢出的办法? 将队列元素存放数组首尾相接,形成循环(环形)队列。 239
  214. 214. 6 7 front 6 7 front 6 7 front rear5 0 5 0 5 A A04 1 4 1 4 B1 rear C 3 2 3 2 rear 3 2 空队列 A进队 B, C进队 6 7 6 7 6 75 5 0 5 GH A0 F I 04 B1 4 B 1 4 E 1 rear C C DCrear 3 2 front rear 3 2 front 3 2 front A退队 B退队 D,E,F,G,H,I 进队(队满) 240
  215. 215. 6 7 front rear5 0 循环队列 (Circular Queue)4 1 3队列存放数组被当作首尾相接的表处理。 2 空队列 队头、队尾指针加1时从maxSize-1直接进到0,可用语言的取模(余数)运算实现。 队头指针进1: front = (front+1) % maxSize; 队尾指针进1: rear = (rear+1) % maxSize; 队列初始化: front = rear = 0; 队空条件: front == rear; 队满条件: (rear+1)241 maxSize == front %
  216. 216. 循环队列的类定义#include <assert.h>#include <iostream.h>class SeqQueue { //队列类定义protected: int rear, front; //队尾与队头指针 DataType*elements; //队列存放数组 int maxSize; //队列最大容量public: SeqQueue(int sz = 10); //构造函数 242
  217. 217. ~SeqQueue() { delete[ ] elements; } //析构函数 bool EnQueue(DataType x); //新元素进队列 bool DeQueue(DataType& x); //退出队头元素 bool getFront(DataType& x); //取队头元素值 void makeEmpty() { front = rear = 0; } bool IsEmpty() const { return front == rear; } bool IsFull() const { return ((rear+1)% maxSize == front); } int getSize() const { return (rear-front+maxSize) % maxSize; } friend ostream& operator <<(ostream& os,SeqQueue &Q);//输出队列中元素的重载操作<<}; 243

×