350 likes | 488 Views
回顾:绪论( 1/2 ). 为什么要学数据结构? 什么是数据结构? 研究计算机操作对象及其关系与操作 相互之间存在一种或多种特定关系的数据元素的集合 从操作对象抽象出来的数学模型, “ 关系 ” 描述数据元素之间的逻辑关系,也称之为逻辑结构 什么是数据类型? 值的集合以及定义在这个值集合上的一组操作 什么是抽象数据类型? 与数据类型的差异???. 回顾:绪论( 2/2 ). 算法?算法性能的度量? 时间 空间. 空间复杂度度量. 存储空间的固定部分 程序指令代码的空间,常数、简单变量、定长成分 ( 如数组元素、结构成分、对象的数据成员等 ) 变量所占空间
E N D
回顾:绪论(1/2) • 为什么要学数据结构? • 什么是数据结构? • 研究计算机操作对象及其关系与操作 • 相互之间存在一种或多种特定关系的数据元素的集合 • 从操作对象抽象出来的数学模型,“关系”描述数据元素之间的逻辑关系,也称之为逻辑结构 • 什么是数据类型? • 值的集合以及定义在这个值集合上的一组操作 • 什么是抽象数据类型? • 与数据类型的差异???
回顾:绪论(2/2) • 算法?算法性能的度量? • 时间 • 空间
空间复杂度度量 • 存储空间的固定部分程序指令代码的空间,常数、简单变量、定长成分(如数组元素、结构成分、对象的数据成员等)变量所占空间 • 可变部分尺寸与实例特性有关的成分变量所占空间、引用变量所占空间、递归栈所用空间、通过new和delete命令动态使用空间 • 空间复杂度 原地工作(额外空间相对输入数据量来说是常数) O(1) 如果我不用任何附加的空间,实现两个数的交换, 怎么做?
算法时间复杂度分析练习(1/3) • 2100, (3/2)n,(2/3)n, nn ,n0.5 , n! ,2n ,lgn ,nlgn, n(3/2) • 常见的时间复杂度按数量级递增排列,依次为:常数阶0(1)、对数阶0(log2n)、线性阶0(n)、线性对数阶0(nlog2n)、平方阶0(n2)、立方阶0(n3)、k次方阶0(nk)、指数阶0(2n)。先将题中的函数分成如下几类: 常数阶:2100对数阶:lgn K次方阶:n0.5、n(3/2)指数阶 (按指数由小到大排):nlgn、(3/2)n、2n、 n!、 nn注意:(2/3)^n由于底数小于1,所以是一个递减函数,其数量级应小于常数阶。根据以上分析按增长率由小至大的顺序可排列如下:(2/3)n < 2100 < lgn < n0.5 < nlgn < n(3/2) < (3/2)n < 2n < n! < nn
算法时间复杂度分析练习(2/3) • 给一个实数序列,求子序列的最大和 int MaxSubeqSum(const int A[],int N) { int thisusm,maxsum,i,j,k; for (maxsum=0,i=0;i<N;i++) for(j=i;j<N;j++) { thissum=0; for(k=i;k<=j;k++) thissum+=A[k]; if(thissum>maxsum) maxsum=thissum; } return maxsum; }
算法时间复杂度分析练习(3/3) • 求菲波拉契数:f0=1,f1=1,fn=fn-1+fn-2 • Long int • Fib(int n) • { if (N<=1) • return 1; • else return Fib(n-1)+Fib(n-2); • } 时间复杂度? 能有更好的算法吗? (时间复杂度低)
第二章 线性表 • 线性表的类型定义 • 线性表的顺序表示和实现 • 线性表的链式表示 • 线性表的应用
c y z a b 线性结构 线性结构示例:英文字母表 特点: 数据的非空有限集合中, • 存在唯一的一个被称作“第一个”的数据元素; • 存在唯一的一个被称做“最后一个”的数据元素; • 除第一个元素外,其他每一个元素有一个且仅有一个直接前驱。 • 除最后一个元素外,其他每一个元素有一个且仅有一个直接后继。
线性表的类型定义 定义:线性表(Linear list)是n(0)个数据元素的有限序列,记作(a1, …ai-1, ai , ai+1 , …, an),其中ai是表中数据元素,n是表长度。 i称为ai在线性表中的位序;n=0是为空表。 特点: • 同一线性表中的元素具有相同特性 • 复杂的数据元素可由若干个数据项组成 • 相邻数据元素之间存在序偶关系
例子 • (1,2,3,4,5,6,7,8,9,10,11) • (2,5,7,4,8,4,3,6,7,44444,3) • (‘ab’,’cd’,’af’,’adsd’) • 字符串 • 以下是错误的例子 • (a,b,c,d),其中a=3,b=5,c=‘a’,d=(1,2) • 自然数
线性表的ADT定义 ADT List{ 数据对象:D={ai| ai ElemSet,i=1,2,…,n,n 0} 数据关系:R1={< ai-1, ai >| ai-1, aiD,i=2,…n} 基本操作: InitList(&L) DestroyList(&L) ClearList(&L) ListEmpty(L) ListLength(L) GetElem(L,i,&e)
线性表的ADT定义 LocateElem(L,e,compare( )) PriorElem(L,cur_e,&pre_e) NextElem(L,cur_e,&next_e) ListInsert(&L,i,e) ListDelete(&L,i,&e) ListTraverse(L,visit( )) } ADT List • 在此ADT基础上还可定义更复杂的一些操作
两个更复杂的操作(1) • 例2-1:集合的并(union)操作 void union(List &La,List Lb){ La_len=ListLength(La); Lb_len=ListLength(Lb); for (i=1;i<=Lb_len;i++){ GetElem(Lb,i,e); if (! LocateElem(La,e,equal)) ListInsert(La,++La_len,e); } } //union 算法2.1
两个更复杂的操作(2) • 例2-2:两个有序表的归并(merge)操作 void MergeList(List La,List Lb,List & Lc){ //非递减表La,Lb归并为非递减表Lc InitList(Lc); i=j=1;k=0; La_len=ListLength(La); Lb_len=ListLength(Lb); //指针平行移动,一次扫描完成
两个更复杂的操作(3) while((i<=La_len) && (j <= Lb_len)){ GetElem(La,i,ai);GetElem(Lb,j,bj); if (ai <= bj){ ListInsert(Lc,++k,ai);++i;} else {ListInsert(Lc,++k,bj);++j;} } //若La或Lb还有剩余部分,则直接把这些元素 //依次加到Lc表尾 while (i<=La_len){ //La有剩余 GetElem(La,i++,ai); Listinsert(Lc,++k,ai); }
两个更复杂的操作(4) while (j<=Lb_len){//Lb有剩余 GetElem(Lb,j++,bji);Listinsert(Lc,++k,bj); } } //union 算法2.2 时间复杂度分析: • 算法的时间复杂度取决于ADT List定义中基本操作的执行时间
1 2 3 4 5 6 45 89 90 67 40 78 如何表示线性表? • 定义:将线性表中的元素相继存放在一个连续的存储空间中。 • 存储结构:类似一维数组 • 特点:线性表的顺序存储方式——顺序表 • 存取方式:顺序访问, 可以随机存取
1 2 … i … … … n a1a2 … a i … … … an a a+l … a+(i-1)*l … … … a+(n-1)*lidle 假设每个元素占用 l 个存储地址,则 LOC(a i+1) = LOC( a i )+ l LOC(a i) = LOC(a1)+(i-1)*l 顺序表的存储方式
创造我们自己的一维数组(1/3) 假设C语言中没有数组,你怎么实现数组? #define LIST_INIT_SIZE 100 //线性表存储空间初始分配量 #define LISTINCREMENT 10 //存储空间分配增量 typedef struct { ElemType *elem; //存储空间基址 int length; //当前元素个数(表长) int listsize; //当前分配存储容量 //以sizeof(ElemType)为单位 } SqList
创造我们自己的一维数组(2/3) • 初始化 void InitList_sq ( SqList & L ) { L.elem = ( ElemType * ) malloc (LIST_INIT_SIZE * sizeof (ElemType) ); if ( ! L.elem ) exit (OVERFLOW); L.length = 0; L.listsize = LIST_INIT_SIZE ; return OK; }
创造我们自己的一维数组(3/3) • 按值查找:在顺序表中从头查找结点值等于给定值 x的结点 int LocateElem ( SqList &L, ElemType x,equal ) { int i = 0; while ( i < L.length && L.elem[i] != x ) i++; if ( i < L.length ) return ++i; else return 0; }
求表的长度 int ListLength ( SqList L ) { return L.length; }
提取函数:在表中提取第 i 个元素的值 Status GetElem( SqList L, int i ,ElemType &e) { if ( i >= 1 && i <= L.length ){ e= L.elem[i];return (OK);} else{ printf ( “参数i 不合理!\n” ); return ERROR;} }
1 2 3 4 5 6 7 25 34 57 16 48 09 63 i 插入 x 50 1 2 3 4 5 6 7 8 25 34 57 50 16 48 09 63 50 • 插入元素
顺序表的插入 Status ListInsert_Sq ( SqList &L, int i, ElemType e ) {//在表L中第 i 个位置前插入新元素 e if (i < 1 || i > L.length+1 |) return ERROR; if (L.length >= L.listsize){//当前存储空间满,增加分配 newbase = (ElemType *) realloc (L.elem, (L.listsize+LISTINCREMENT)*sizeof(ElemType)); if (!newbase) exit(OVERFLOW);//存储分配失败 L.elem = newbase;L.listsize+=LISTINCREMENT; }
q=&(L.elem[i-1]); //q为插入位置 for (p=&(L.elem[L.length-1]);p>=q;--p) *(p+1)=*p; //插入位置及之后的元素右移 } L.elem[i-1]=e; L.length--; }
性能分析: • 基本操作为“移动”(实质为赋值)操作; 在第i个位置前插入需移动n-i+1个元素 • 假设在任何位置插入元素都是等概率的,则移动次数的期望值(平均次数)Eis为 因此,算法的平均时间复杂度为O(n)
1 2 3 4 5 6 7 8 16 25 34 57 50 16 48 09 63 删除x 1 2 3 4 5 6 7 25 34 57 50 48 09 63 • 删除元素
顺序表的删除 Status ListDelete_Sq ( SqList &L,int i, ElemType &e ) { //在表L中删除第i个元素,并用e返回其值 if ((i<1) ||(i>L.length)) return ERROR; p=&(L.elem[i-1]); //p 为被删除元素的位置 e=*p; q=L.elem+L.length-1;//表尾元素位置 for (++p;p<=q;++p) *(p-1)=*p;//元素左移 --L.length; //表长减一 return OK; }
性能分析: • 基本操作为“移动”(赋值)操作; 删除第i个位置的元素需(向左)移动n-i个元素 • 假设在任何位置删除元素都是等概率的,则移动次数的期望值(平均次数)Edl为 因此,算法的平均时间复杂度也为O(n)!
集合的“并”运算 • void Union ( SqList &La, SqList Lb ) { • int n = ListLength ( La); • int m = ListLength ( Lb); • for ( int i = 1; i <= m; i++ ) { • GetElem ( Lb, i,e ); //在Lb中取一元素 • if (!LocateElem(La,e,equal)) // La中若没有 • //则向La表尾插入该元素 • ListInsert_Sq(La,++n,e); • } • } • 时间复杂度:O(La.length* Lb.length) • 空间复杂度: O(La.length+ Lb.length) 顺序表的应用(1)
顺序表的应用(2) • 集合的“减”运算 • void Intersection ( SqList &La, SqList &Lb ) { • int n = ListLength ( La); • int m = ListLength ( Lb); • for ( int i = 1; i <= m; i++ ) { • GetElem ( Lb, i,e ); //在Lb中取一元素 • if (k=LocateElem(La,e,equal)) // La中若有 • //则从La中删除该元素 • ListDelete_Sq(La,k,e); • } • } • 时间复杂度:O(La.length* Lb.length) • 空间复杂度: O(1)
顺序表的应用(3) • 顺序表的归并(算法2.7) void MergeList_Sq(SqList La,SqList Lb,SqList &Lc) { pa=La.elem;pb=Lb.elem; Lc.listsize=Lc.length=La.length+Lb.length; pc=Lc.elem=(ElemType *) malloc(Lc.listsize * sizeof (ElemType)); if (!Lc.elem) exit(OVERFLOW); pa_last =La.elem+La.length-1; pb_last =Lb.elem+Lb.length-1;
while (pa <=pa_last &&pb<=pb_last){ if (*pa<=*pb) *pc++=*pa++; else *pc++=*pb++; } while (pa<=pa_last) * pc++=*pa++; while (pb<=pb_last) * pc++=*pb++; }//MergeList_Sq • 时间复杂度:O(La.length+Lb.length) • 空间复杂度:O(La.length+Lb.length)
Homework 2 • 思考题: 试分析顺序表的插入与删除操作的最坏时间复杂度。 • 作业: 习题集第18页 题2.21