300 likes | 409 Views
第 14 章 线性表. 本章目标 介绍线性表的概念及其存储结构的 C++ 定义: 顺序表 和 链表 学习要求 掌握线性表的顺序存储结构 C++ 定义 ---- 即顺序表类的定义 掌握顺序表的 插入函数 和 删除函数 掌握线性表的非顺序存储结构 C++ 定义 ---- 即单链表类的定义 掌握单链表的 插入函数 和 删除函数. 什么是线性表. 日常生活中常见到表格形式的一类数据 列车时刻表 学生成绩表 周名缩写表. 车次. 火车种类. 始发站. 终点站. 始发时间. 到达时间. 140. 特快. 西安. 上海. 20 : 30. 19 : 45.
E N D
第14章线性表 本章目标 介绍线性表的概念及其存储结构的C++定义:顺序表和链表 学习要求 掌握线性表的顺序存储结构C++定义----即顺序表类的定义 掌握顺序表的插入函数和删除函数 掌握线性表的非顺序存储结构C++定义----即单链表类的定义 掌握单链表的插入函数和删除函数
什么是线性表 日常生活中常见到表格形式的一类数据 列车时刻表 学生成绩表 周名缩写表
车次 火车种类 始发站 终点站 始发时间 到达时间 140 特快 西安 上海 20:30 19:45 42 特快 西安 北京 17:30 7:45 361 普快 西安 铜川 9:45 13:10 …… …… …… …… …… …… 列车时刻表
学号 姓名 性别 分数 99011001 周敏 女 86 99011002 苏伊诗 女 92 99011003 王苏朋 男 76 …… …… …… …… 学生成绩表
Sun. Mon. Tue. Wen. Thu. Fri. Sat. 周名缩写表
上述表格形式的数据具有如下共同特点: 1)表中每一行信息虽然组成的内容不同,但都代表某个明确独立的含义,将表中每一行信息称之为一个数据元素; 2)每个数据元素在表中的位置固定,除了第一个元素和最后一个元素外,其余元素都有唯一一个前驱元素和唯一一个后继元素; 3)表中数据元素的个数不相同,有长有短; 4)大多数表中数据元素会增加或减少,是动态变化的。但也有一些表是固定不变, 将这些表格形式的数据加以抽象,统称为线性表。
线性表的定义 线性表是由n(n≥0)个数据元素a 1,a 2,a 3,…,a n组成的有限序列,记为:(a 1,a 2,a 3,…,a n)。 线性表中当前存储的数据元素数目叫做表的长度,n为表长,当线性表中不包含任何数据元素时,称为空表。显然n=0时称为空表。 1)数据元素a i(1≤i≤n)表示某个具体意义的信息,它可以是一个数,或者是一个字符串,或者是由数和字符串组成的更复杂的信息。但同一个线性表中的所有数据元素必须具有相同的属性(或者说具有相同的数据类型); 2)若线性表是非空表,则有:(ⅰ)第一个元素a 1无前趋元素;(ⅱ)最后一个元素a n无后继元素;(ⅲ)其它元素a i(1<i<n)均只有一个直接前趋a i-1和一个直接后继a i+1。
日常生活中线性表的例子 某校1998—2003年在校学生人数(2500,3450,5000,5100,5400,5500)是一个线性表,每个数据元素是一个正整数,表长为6; 每个月份英文缩写名称组成一个线性表(JAN.,FEB.,MAR.,APR.,MAY.,JUN.,JUL.,AUG.,SEP.,OCT.,NOV.,DEC.),表中数据元素是一字符串,表长为12; 屏幕上若干像素((50,50,RED),(100,150,RED),(200,200,BLUE),(200,250,GREEN))亦是一线性表,表中数据元素是由行坐标、列坐标和颜色三个数据项组成的一个像素信息,表长为4。 电话号码簿、股票市场里的信息列表、航班时刻表、各种各样的统计报表等等
对线性表有哪些处理(或操作)呢? 基本的、常用的处理如下: 1)统计线性表里总共有多少个元素。简称求表长,记作Length(L), 2)获取线性表中某个数据元素的信息。简称取元素,记作Get(L,i) 3)置换或修改线性表中某个数据元素的信息。简称置换元素,记作Replace(L,i,x) 4)在线性表中某个位置上增加一个数据元素。简称插入元素,记作Insert(L,i,x) 5)删除线性表中某个位置上的一个数据元素。简称删除元素,记作Delete(L,i) 6)查找某个数据元素是否在线性表中存在。简称查找元素,记作Locate(L,x) 7)求前驱元素,记作Prior(L,i),取元素ai的直接前驱 8)求后继元素,记作Succ(L,i) 9)对线性表中的数据元素按某个数据项的值递增(或递减)的顺序排列。简称排序,记作Sort(L)。
顺序表类的定义(1) • #include <iostream.h> • template <class datatype> • class seqlist • { private: • datatype *data; • int maxsize; //maxsize为线性表的最大可能长度 • int last; //last为线性表中表尾元素的下标 • public: • seqlist() //创建100个元素的线性表的构造函数 • { maxsize=100; • data=new datatype[maxsize]; • last=-1; //last为-1表示为空表 • } • seqlist(int sz) //创建sz个元素的线性表的构造函数 • { if(sz>0) • maxsize=sz; • else • maxsize=100; • data=new datatype[maxsize]; • last= -1; //last为 -1表示为空表 • } • bool isempty(){ return last==-1?true:false; } //判空表 • bool isfull(){ return last==maxsize-1; } //判表满 • int length(){ return last+1; } //求表长
顺序表类的定义(2) • bool getdata(int i,datatype &x) //取元素 • { i--; • if (i>=0&&i<=last) • { x = data[i]; • return true; • } • else • { cout<<”非法位置读取元素,不能读取!\n”; • return false; • } • } • bool get_prior(int i,datatype &x); //取前驱元素 • bool get_succ(int i,datatype &x); //取后继元素 • bool replace(int i,datatype x) //置换元素 • { i--; • if (i>=0&&i<=last) • { data[i]=x; • return true; • } • else • { cout<<”非法位置修改元素,不能修改!\n”; • return false; • } • } • bool insert_data(int i,datatype x); //插入元素 • bool delete_data(int i); //删除元素 • void print_list(); //显示表中所有元素 • int find_data(datatype x); //查找元素 • void sort(); //排序元素 • ~seqlist(){ delete[] data; } //析构函数 };
顺序表类定义的说明 • maxsize为线性表中数据元素个数最大可能值 • last为线性表中当前实际存入的最后一个数据元素下标 • 构造函数有两个重载形式seqlist()和seqlist(int sz)。 • 构造函数创建了datatype类型的一维数组,数据元素在数组里按次序紧密存放 • 析构函数~seqlist()将申请的存储空间释放,data指针指向该空间的首地址 • 数据元素的类型定义为模板是为了定义一个通用类型的线性表
学生成绩单的定义 struct student_score_table { long num; char name[9]; char sex; float score; }
声明几个顺序表类的对象 • seqlist <int> la(2000); • seqlist <string> lb(2000); • seqlist <struct student_score_table> lc(2000); 以上顺序表类中,还有插入、删除、查找、排序四个函数没有定义函数体,下面讨论插入和删除两个函数的算法实现,查找和排序函数的算法实现放在下一章讨论。
template<class datatype> bool seqlist<datatype>:: insert_data(int i, datatype x) { if ( isfull() ) // 判定表满否 { cout<<”表已满,不能插入!\n”; return false; } if ( i>=1 && i<=last+2 ) // 判定插入位置i的合法性 { // 第n至第i个元素循环后移一个存储位置 for (int j=last;j>=i-1;j--) data[j+1]=data[j]; data[j+1] =x; // x成为线性表中第i个元素 last++; // 线性表的长度加1 return true; } else { cout<<"插入位置错误,不能插入!\n"; return false; } } 插入算法的主要步骤和C函数如下: 插入前:(a 1,a 2,…,a i-1,a i,…,a n) 插入后:(a 1,a 2,…,a i-1,x ,a i,…,a n) 第1步: 判定表不满方可插入; 第2步: 判定插入位置i的合法性; 第3步: 将第n至第i个元素后移一个存储位置; 第4步: 将x插入到a i-1之后; 第5步: 线性表的长度加1。
template<class datatype> bool eqlist<datatype>:: delete_data(int i) { if( isempty() ) // 判定表空否 { cout<<”表已空,不能删除!\n”; return false; } if ((i>=1)&&(i<=last+1)) // 判定删除位置i的合法性 { // 第i+1至第n个元素循环前移一个存储位置 for (int j=i-1;j<last;j++) data[j]=data[j+1]; last--; // 线性表的长度减1 return true; } else { cout<<”删除位置错误,不能删除!\n”; return false; } } 删除算法的主要步骤和C++函数如下: 删除前:(a 1,a 2,…,a i-1,a i,a i+1,…,a n) 删除后:(a 1,a 2,…,a i-1,a i+1,…,a n) 第1步:判定表不空方可删除; 第2步:判定删除位置i值的合法性; 第3步:将第i+1至第n个元素依次向前移动一个存储位置; 第4步:将线性表的长度减1。
综合分析线性表的顺序存储结构有五个特点 1)可以随机存取线性表中每一个元素,存取元素的速度与它在表中的位置无关; 2)不需要额外开辟空间存储关系集合,其逻辑关系隐含在物理存储结构中; 3)插入和删除元素速度较慢,平均地讲每插入或删除一个元素需要移动表长的一半元素; 4)扩充性差,注意顺序表类定义中MAXSIZE值的确定是件困难的事,MAXSIZE值太大造成存储空间冗余,太小不利于扩充; 5)需要一整块连续空间,但表中元素进进出出,不可能一下子放满,有存储空间空闲不能作为它用。
线性表的非顺序存储结构----链表类 设计新的存储结构,改进顺序表的结构: 1、线性表中有多少元素就开辟多少存储空间,不预留空间; 2、元素之间不需要紧挨着存放,元素可以散落在存储器中任何地方; 3、插入和删除都不需要移动大量元素;
data next 每个元素的存储结构 template<class datatype> class NODE { public: datatype data; //数据域 NODE<datatype> *next; //指针域 };
线性表的非顺序存储结构图 指针将线性表中每一个元素有机地连接在一起,指针像链条一样,所以线性表的非顺序存储结构又称链式结构 有了结点类的定义,就可以将整个线性表定义成如下单链表类:
单链表类(1) • template<class datatype> • class LIST • { private: • NODE<datatype> *head; • public: • LIST(){ head=NULL; } //构造函数 • int length(); //求表长 • bool get_data(int i,datatype &x); //取元素 • bool get_succ(int i,datatype &x); //取前驱元素 • bool get_proc(int i,datatype &x); //取后继元素 • bool replace_data(int i,datatype x); //置换元素 • NODE<datatype> *find_data(int i); //查找元素 • void sort(); //排序 • bool insert_data(datatype data,int i); //插入元素
单链表类(2) • bool delete_data(int i); //删除元素 • void print_list(); //打印所有元素 • bool insert_rear(datatype data); //从表尾插入元素 • bool insert_head(datatype data); //从表头插入元素 • ~LIST() //析构函数 • { • NODE<datatype> *p; • while(head) //将链表中所有元素占用空间释放 • { • p = head; • head = head->next; • delete p; • } • } • };
单链表类的特点 1)线性表中实际有多少元素就存储多少个结点 2)元素存放可以不连续,其物理存放次序与逻辑次序不一定一致,换句话说,a i-1可能存放在存储器的下半区,而a i可能存放在存储器的上半区 3)线性表中元素的逻辑次序通过每个结点指针有机地连接来体现 4)插入和删除不需要大量移动表中元素。
Newnode = new NODE<datatype>; newnode->data=data; newnode->next=current; previous->next=newnode;
template<class datatype> bool LIST<datatype>:: insert_data(float data,int i) { NODE<datatype> *current,*previous,*newnode; int j=1; if((i>length()+1)||(i<0)) //判定插入位置正确与否 { cout<<"插入位置不正确,不能插入!\n"; return false; } newnode=(NODE *)malloc(sizeof(NODE)); //申请新结点空间 if(newnode==NULL) { cout<<"内存无空间,表已满,不能插入!\n"; return false; } newnode->data=data; newnode->next=NULL; if(i==1) //插入表头,另做处理 { newnode->next=head; head=newnode; return true; } current=previous=head; while(current!=NULL&&j<i) //寻找插入位置 { previous=current; current=current->next; j++; }; newnode->next=current; //修改新结点的指针域 previous->next=newnode; //修改ai-1结点的指针域 return true; }
previous->next=current->next; delete current;
template<class datatype> bool LIST<datatype>:: delete_data(int i) { NODE<datatype> *current,*previous; int j=1; if(head==NULL) //判定是否为空表 { cout<<"表已空,不能删除。\n"; return false; }; if((i<1)||(i>length())) //判定删除位置是否正确 { cout<<"删除位置不正确,不能删除!\n"; return false; } current=previous=head; while(current&&j<i) //寻找删除位置 { previous=current; current=current->next; j++; }; if(head==current) //删除表头元素另做处理 { head=head->next; delete current; } else { previous->next=current->next; //修改ai-1结点指针域 delete current; //释放被删结点的存储空间 } return true; }
作业 作业1:编写测试主函数,测试顺序表的插入和删除函数的正确性 作业2:编写测试主函数,测试单链表的插入和删除函数的正确性 作业3:本章习题1 作业4:本章习题2 作业1和作业2提示:粘贴插入和删除函数 编写打印线性表函数 再粘贴测试主函数