2.49k likes | 2.6k Views
数 据 结 构 王 理 主 讲. 华北水院水利职业学院. 本书章节. 第 1 章 绪论 第 2 章 线性表 第 3 章 栈和队列 第 4 章 树 第 5 章 图. 第 6 章 第 7 章 第 8 章 第 9 章 第 10 章. 第 1 章 绪论. 教学提示: 本章主要介绍数据结构课程中一些常用术语的含义,算法和算法描述,算法的时间复杂度和空间复杂度评价方法等内容。 教学目标: 1. 掌握集合、线性结构、树和图等常用数据结构的概念; 2. 掌握抽象数据类型的定义和应用,算法的特性和算法描述的方法;
E N D
数 据 结 构 王 理 主 讲 华北水院水利职业学院
本书章节 第1章 绪论 第2章 线性表 第3章 栈和队列 第4章 树 第5章 图 第6章 第7章 第8章 第9章 第10章
第1章 绪论 教学提示: 本章主要介绍数据结构课程中一些常用术语的含义,算法和算法描述,算法的时间复杂度和空间复杂度评价方法等内容。 教学目标: 1. 掌握集合、线性结构、树和图等常用数据结构的概念; 2. 掌握抽象数据类型的定义和应用,算法的特性和算法描述的方法; 3. 掌握评价算法的一般规则,算法的时间复杂度和空间复杂度。
第1章 绪论 • 数据结构的基本概念 • 算法和算法描述 • 算法评价 • 习题
● 绪论___数据结构的基本概念 • 数据结构是介于数学、计算机硬件和计算机软件三者之间的一门课程 • 是程序设计的基础,是设计和实现编译程序、操作系统、数据库系统及其他系统程序和大型应用程序的重要基础 • 算法 + 数据结构 = 程序 • 描述现实世界实体的数学模型及其上的操作在计算机中如何表示和实现 • 八皇后问题、汉诺塔问题、、学生信息检索系统问题、迷宫问题、农夫过河问题等解决这类问题的关键不是数学分析和计算方法,而是设计出合适的数据结构,才能有效的解决问题
● 绪论___数据结构的基本概念 • 数据:能被计算机输入、存储、处理和输出的数字、字符等信息。 • 数据元素:数据的基本单位。 • 数据对象:性质相同的数据元素的集合。 • 数据结构:数据元素及其相互之间的联系。即数据的组织形式 • ① 数据元素之间的逻辑关系,也称逻辑结构 • ② 数据元素及其关系在计算机存储器内的表示,也称数据的存储结构 • ③ 数据的运算,即对数据施加的操作
● 绪论___数据结构的基本概念 • 逻辑结构:数据元素之间的逻辑关系,包括集合、性线结构、树型结构、图形结构。
26 43 79 11 66 … … … 22 47 60 1 5 9 10 22 15 19 42 44 47 34 51 60 52 54 a c d ^ b e 第一子块 第二子块 第三子块 ● 绪论___抽象数据类型 0 1 2 3 4 5 6 7 8 9 15 22 23 29 37 39 1 2 2 4 4 3
索引表 主表最大值 主表首址 主表 Head 哈希地址 a2 … ai … ^ a1 an 关键字值 • ● 绪论___数据结构的基本概念 • 物理结构:数据元素在计算机内的存储式,包括顺序、链接、索引、散列等。
● 绪论___抽象数据类型 ● ADT 抽象数据类型名{ 数据对象:<数据对象的定义> 数据关系:<数据关系的定义> 基本操作:<基本操作的定义> } ADT抽象数据类型名 ● ADT Linear_List{ 数据对象 所有ai 属于同一数据对象,i=1,2,…,n (n≥0) 数据关系 所有数据元素ai 为次序关系(ai ,ai+1 ), ai 无前驱 an无后继 基本操作 设 L 为 Linear_List类型的线性表 • InitList ( L ): 建立一个空的线性表L; • Length ( L ): 求线性表L的长度; • GetElem ( L, i ): 取线性表L中的第i个元素; • Locate ( L, x ): 确定元素x在线性表L中位置; • Insert ( L, i, x ): 在线性表L中第i个元素之前插入一个新元素x; • Delete ( L, i ): 删除线性表L中的第i个元素; • }ADT List举例:一个线性表的抽象数据类型 举例:一个线性表的抽象数据类型
●结论___算法和算法描述 • ● 算法:是对特定问题求解步骤的一种描述,有5种特性 • ① 有穷性 ② 确定性 • ③ 可行性 ④ 输入 ⑤ 输出 • ● 算法的特性 • ① 正确性 ② 可读性 • ③ 健壮性 ④ 高效率与低存储量需求 • ● 算法描述工具----C语言
●结论___算法和算法描述 • ● 算法效率的度量 • ① 执行算法所消耗的时间 • ② 执行算法所占用的存储空间 • ③ 算法的“简单性” • 其中①②的评价标准是程序的执行需要占用多少机器资源 • ③和可读性、易于理解紧密相关
●结论___算法评价 • ● 算法评价主要从时间和空间两方面入手 • ● 时间复杂度: 算法中基本操作重复执行的次数,且是估算的数量级 • ① x = x +1 • ② for ( i = 1 ; i <=n ; i ++ ) • ③ for ( i = 1 ; i <=n ; i ++ ) • for ( j = 1 ; j <=n ; j ++ ) • ● 空间复杂度: 算法执行过程中需要的空间。 • ① 存储程序本身所占用的空间 • ② 算法的输入/输出数据占用的空间 • ③ 算法在运行过程中临时占用的辅助空间
●结论___习题 • 6. a,b,c 三个数求最大值 • #include <stdio.h> • int main() • { int a, b, c; • int max; • printf("请输入三个数:"); • scanf("%d %d %d", &a, &b, &c); • max=a; • if (max < b) max=b; • if (max < c) max=c; • printf("最大的数是:%d\n", max); • return 0; • }
●结论___习题 • 7.猴子吃桃问题 • main( ) • { • int day,x1,x2; • day=9; • x2=1; • while(day>0) • { • x1=(x2+1)*2;/ *第一天的桃子数是第2天桃子数加1后的2倍*/ • x2=x1; • day--; • } • printf("the total is %d\n",x1); • }
●结论___习题 • 6. #include <stdio.h> • int main() • { int a, b, c; • int max; • printf("请输入三个数:"); • scanf("%d %d %d", &a, &b, &c); • max=a; • if (max < b) max=b; • if (max < c) max=c; • printf("最大的数是:%d\n", max); • return 0; • }
第2章 线性表和数组 教学提示: 本章主要介绍线性表的逻辑结构、物理结构;循环链表和双向链表;数组的存储等内容。 教学目标: 1. 掌握线性表的顺序存储结构及相关操作和其时间复杂度; 2. 握握线性表的链式存储结构及相关操作和其时间复杂度; 3. 掌握循环链表和双向链表的存储结构及相关操作; 4. 掌握数组的顺序存储结构; 5. 掌握稀疏矩阵的顺序存储结构和链式存储结构.
第2章 线性表及数组 • 线性表的逻辑结构 • 线性表的顺序存储结构 • 线性表的链式存储结构 • 循环链表和双向链表 • 线性表的应用----多项式相加问题 • 数组的顺序存储 • 稀疏矩阵的顺序存储和链接存储 • 习题
● 线性表及数组应用___线性表的逻辑结构1 • ● 线性表的定义:是 n 个数据元素组成的有限序列。 • 记作:L = ( a1,a2,a3…ai,…,an )。例:学生登记表 • 线性表的长度:线性表中元素的个数。 空表:长度为0。 • 直接前驱结点:对于ai来说,ai-1则是。 直接后继结点:对于ai来说,ai+1则是。 • ● 线性表的基本操作 • InitList ():建立一个空的线性表L; • ClearList (&L):将 L 表置为空; • DestroyList(&L):将 L 表销毁; • ListLength (& L ):求线性表L的长度; • ListEmpty(& L):判断表L为空则返回TURE,否则返回FALSE; • GetElem (L, i ,&e):用e返回线性表L中的第i个元素; • LocateElem(L,e,compare()): 返回L中第1个与e满足关系compare的元素的位序 • PriorElem(L,ce,&pe): 返回元素ce的直接前驱给pe • NextElem(L,CE,&ne): 返回元素ce的直接后继给ne • ListInsert (&L, i, e ):在线性表L中第i个元素之前插入一个新元素x; • ListDelete (&L,i,&e ):删除线性表L中的第i个元素; • ListTraverse(L,visit()):依次对L的每个元素调用函数visit( )
● 线性表及数组应用___线性表的逻辑结构2 ● 操作举例: 线性表L =(26,43,79,11,66),i = 2, j = 4, x = 60, y = 11,作如下操作: ListLength ( L ); 5 ListEmpty ( L ); False GetElem ( L, i, e ); e = 43 ListInsert ( L, i , x ); ( 26, 60, 43, 79, 11, 66 ) ListDelete ( L, j, e ); e = 79 PriorElem ( L, x, e ); e = 26 NextElem ( L, y, e ); e = 66
● 线性表及数组应用___线性表的逻辑结构3 ● 操作举例:写出程序执行后的线性表 InitList (L) ; Int a[ ] = { 48, 26, 57, 34, 62, 79 } for ( i = 1; i <= 6; i ++ ) InsertList ( L, 1, a[ i-1] ) ; for ( i = 1; i <= 6; i ++ ) { GetElem ( L, i, x ) ; if ( ! X%2 ) ListDelete ( L, i, x ); } 结果: L=(79,62,34,57,26,48) 长度为:6 L=(79,57) 长度为:2 X若为偶数,则删除
● 线性表及数组应用___线性表的顺序存储结构1 ● 顺序存储结构 ● 动态分配 ● 基本操作 ● 插入操作 ● 删除操作
ai Ai+k Ai+2k Ai+3k …. Ai+(i-1)k …. Ai+(n-1)k ● 线性表及数组应用__线性表的顺序存储结构2 ● 线性表的顺序存储:是利用内存中一批地址连续的存储单元依次存储线性表中的数据元素 特点: ① 顺序存储 ② 随机存储 每个元素占用的空间大小为L,则元素 Ai的存放位置为: loc (Ai ) = loc (A1 ) + k×( i-1 ) ●动态分配的一维数组实现线性表的顺序存储结构 # define LIST_INIT_SIZE 100初始分配长度 # define LISTINCREMENT 10分配增量 # define LEN sizeof ( ElemType ) typedef struct{ ElemType# *elem;存储空间的基地址 int lenth;当前线性表长 int listsize;当前已分配的存储空间的总长度 }SqList
26 43 79 11 66 … … … 当 前 长 度 总 长 度 • ● 线性表及数组应用__线性表的顺序存储结构3 • ● 线性表的存储结构: • ● 初始化线性表 • Status InitList_Sq(SqList &L) • { • L.elem = (ElemType *) malloc (LIST_INIT_SIZE * LEN); • IF (! L.elem) exit (OVERFLOW); • L.listsize = LIST_INIT_SIZE; • L.length = 0; • return OK; • } 基地址
● 线性表及数组应用__线性表的顺序存储结构4 ● 清空线性表 Status ClearLIST_Sq(SqList &L) { L.length = 0; return OK; } ● 销毁线性表 Status DLIST_Sq(SqList &L) { if (L.elem) free (L.elem); return OK; } ● 求线性表的长度 int ListLength_Sq(SqList L) { return L.length; }
● 线性表及数组应用__线性表的顺序存储结构5 ● 判断线性表是否为空 Status LISTEmpty_Sq(SqList L) { retrun L.length == 0; } ● 读取线性表第 i 个元素值 Status GetElem_Sq(SqList L, int i, ElemType &e) { if ( i<1 || i>L.length ) return ERROR; e = * ( L.elem + (i-1) ×k); return OK; } ● 查找数据 int LocateElem_Sq(SqList L, ElemType e, Status ( *compare) (ElemType, ElemType )) { for ( i =1, p = L.elem; i<=L.length; i++, p++) if ((*compare) (* p,e)) return i; return 0; } If L.length = = 0 then return 1 Else return 0
21 3 8 14 26 33 41 55 67 80 3 8 14 21 26 33 41 55 67 80 • ● 线性表及数组应用__线性表的顺序存储结构6 • ● 顺序表的插入操作 • 插入前: • 插入后 • ① 判断 i 是否越界,1≤ i ≤L.length+1; • ② 判断 L 是否满,若是增加分配并更新 L.elem、L.length 和 L.listsize; • ③ 从最后一个元素向前到第 i个元素为止的,所有元素后移一位 • ④ 将新元素 e 插入到第 i个元素,表长加 1 。
● 线性表及数组应用__线性表的顺序存储结构7 ● 插入算法 Status ListInsert_Sq ( SqList &L , int i , ElemType e ){ if ( i < 1 || i > L.length+1 ) return ERROR ; if ( L.length >= L.listsize ) { newbase = ( ElemType * ) realloc ( L.elem, ( L.listsize+LISTINCREMENT ) * LEN ); If ( ! Newbase ) exit ( OVERFLOW ); L.elem = newbase; L.listsize+ = LISTINCREMENT; } p = L.elem + L.length – 1; 指针 P 指向尾元素 q = L.elem + i -1; 指针 q 指向第 i 个元素 for ( ; p >= q ; p - - ) *(p+1) = *p 元素后移 *q = e; ++ L.length ; return OK ; }
3 8 14 26 33 41 55 67 80 3 8 14 21 26 33 41 55 67 80 • ● 线性表及数组应用__线性表的顺序存储结构8 • ● 顺序表的删除操作 • 删除前: • 删除后 • ① 判断 L是否为空,为空则返回 ERROR; • ② 判断 i是否越界,1≤ i≤L.length; • ③ 将被删除的第 i个元素返回给 e ; • ④ 从第i +1个元素向后到最后一个元素为止的,所有元素前移 • 一位,表长减 1.
● 线性表及数组应用__线性表的顺序存储结构9 • ● 删除算法 • Satus ListDelete_Sq (SqList &L, int i ,ElemType &e) • { • if ( ListEmpty_Sq(L) ) return ERROR; • If ( i <1|| i >L.length ) return ERROR ; • p = L.elem + i -1; 指针 P指向尾元素 • q = L.elem + L.length – 1; 指针 q指向第 i 个元素 • e = *p; • for ( ; p < q ; + + p ) * p = * (p+1) ; 元素前移 • - - L.length ; • return Ok; • }
n+1 n+1 i = 1 i = 1 ● 线性表及数组应用__线性表的顺序存储结构10 ● 插入操作的时间分析 移动次数:0+1+2+3+…+n = n(1+n)/2 Eis = ∑ pi (n - i + 1) = ( 0+1+2+3+ …+ n) / (1+n) = n / 2 ● 删除操作的时间分析 移动次数:0+1+2+3+…+n-1 = n(n-1)/2 Ede = ∑ qi (n - i) = ( 0+1+2+3+ …+ n-1) / n = (n -1)/ 2 时间复杂度: O(n)
● 线性表及数组应用__线性表的顺序存储结构11 ● 从线性表 LB 中依次取得每个数据元素,在 LA 中进行查找,若不存在,则将其插入 LA void union_Sq(SqList &LA, SqList LB) { for ( i = 1; i <= LB.length ; ++ i ) { e = *( LB.elem + i -1 ); ‘从LB中依次取出每个元素 if ( ! LocateElem_Sq (LA, e, equal ) ) ‘判断是否在LA中 ListInsert_Sq (LA, LA.length + 1, e ); 插入LA的表尾 } } 时间复杂度: O(m×n)
● 线性表及数组应用__线性表的顺序存储结构12 ● 两个非递减有序表 LA 和 LB, 将它们合并成一个新的非递减表 LC void merge_Sq(SqList, SqList LB, SqList &LC) { pc = LC.elem = (ElemType *) malloc ( LC.listsize * LEN) if (! pc) exit (OVERFLOW); pa = LA.elem; ’ 指向LA基地址 pb = LB.elem; ‘ 指向LB基地址 pa_last = LA.elem + LA.length -1; ’ 指向LA尾地址 pb_last = LB.elem + LB.length -1; ’ 指向LB尾地址 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 ++; } 构造LC存储空间 时间复杂度: O(m+n) Pa的值送给Pc PaPc指针均后移一位 *pc = *pb Pc ++; pa++
● 线性表及数组应用___线性表的链式存储结构1 ● 单链表的概念 ● 单链表的查找 ● 单链表的插入 ● 单链表的删除
data next 数据域 指针域 • ● 线性表及数组应用__线性表的链式存储结构2 • ● 结点结构 • ● 表示方式 • p →dataP结点的元素 • p →next P结点的后继指针 • p →next →data P结点的后继结点的元素值 • p →next →next P结点的后继结点的指针 • ● 一般情况下,等号左端为指针,等号右端为结点
^ 空表 a1 a2 … an ^ • ● 线性表及数组应用__线性表的链式存储结构2 头指针head 31 H 表头结点 H
● 线性表及数组应用__线性表的链式存储结构2 • ● 线性表的链式存储是利用内存中一组任意的存储单元来存储线性表中的数据元素 • typedef struct node{ • ElemType data ; /*数据域*/ • struct node *next ; /*指针域*/ • }LNode ; • ● 向系统归还结点时,调用库函数free( p ) 实现
^ 空表 ● 线性表及数组应用__线性表的链式存储结构3 ● 初始化单链表 Void InitList ( LinkList * head ) { * head = ( LinkList ) malloc ( Sizeof ( ListNode ) ) ( * head ) → next = NULL; }
● 线性表及数组应用__线性表的链式存储结构4 ● 查找单链表中第 i 个结点 (按序号查找) ListNode *GetElem ( LinkList Head , int i ) { int j; ListNode *p; p = head ; j = 0 ; while ( p → head != NULL && j < i ) { p = p→next ; j ++; } if ( i == j && i != 0 ) return p; else return NULL; } p j = 0 j = 1 j = 2 j = i Head a2 … ai … ^ a1 an 时间复杂度: O(n)
● 线性表及数组应用__线性表的链式存储结构5 ● 查找单链表中元素值为 x的结点(按值查找) ListNode *LocateElem ( LinkList Head , ElemType x ) { ListNode *p; p = head →next; while ( p → next != NULL && p → data != x ) p = p→next ; if (p →data = = x ) return p; else return NULL; } 时间复杂度: O(n) p Head a2 … x … ^ a1 an
● 线性表及数组应用__线性表的链式存储结构6 ● 在单链表中第i 结点前插入一个元素 Void Insert_Linkst ( LNode *Head ,ElemType x, int i ) { ListNode * pre, * s; int j; pre = head; j = 0; while ( pre! = NULL && j < i – 1 ) { pre = pre→next ; j ++; } if ( j != i - 1 ) error ( “positoin error ” ); ① s = ( LNode * ) malloc ( sizeof ( LNode )) ; ② s→data = x ; ③ s→next = pre →next ; ④ pre→next = s ; } j = i - 1 pre pre pre Head … … ^ ④ x j = 0 j = 1 ③ ② s ① 时间复杂度: O(n)
^ y S ② ⑤ ① p q p q ③ ④ ⑥ H x … … ⑥ ⑤ y S ② ① ● 线性表及数组应用__线性表的链式存储结构7 ● 在单链表中值为 x的结点前插入元素 y,若 x 不存在,则将 y 插入表尾 Void Insertx_Linkst ( LNode *H , ElemType x,ElemType y) ①{ s = ( LNode * ) malloc ( sizeof ( LNode )) ; ② s→data = y ; ③ q = H ; ④ p = H→next ; while ( p && p →data != x ){ q = p ; p = p→next ; } ⑤ s→next = p ; ⑥ q→next = s ; } 时间复杂度: O(n) P 用来确定x, q 是 p 的前驱,用于实现在已知结点 q 之后插入
● 线性表及数组应用__线性表的链式存储结构9 ● 头插入法建表 LinkList CreateFromHead ( ) { ListList head; ListNode *s; char ch; int flag = 1; ① head = ( LinkList ) malloc (sizeof ( ListNode ) ) ②head →next = NULL; while ( flag ) { ch = getchar ( ); if ( ch != ‘$’ ) { ① s = ( LinkList ) malloc ( sizeof ( listof ( listNode ) ); ② s →data = ch; ③s →next = head →next; ④head →next = s ; } else flag = 0 ; } return head; } 时间复杂度: O(n) s ch 初始化 s ch 继续链表 Head ^ 键盘读入数据 插入 链表结束
● 线性表及数组应用__线性表的链式存储结构9 ● 尾插入法建表 LinkList CreateFromTail ( ) { ListList head; ListNode *r; * s; char ch; int flag = 1; ① head = ( LinkList ) malloc (sizeof ( ListNode ) ) ②head →next = NULL; r = head; while ( flag ) { ch = getchar ( ); if ( ch != ‘$’ ) { ① s = ( LinkList ) malloc ( sizeof ( listof ( listNode ) ); ② s →data = ch; ③ r →next = s; ④ r = s ; } else { flag = 0 ; r →next = null; } } return head; } s ^ ch r r Head 时间复杂度: O(n) 初始化 继续链表 键盘读入数据 插入 链表结束
● 线性表及数组应用__线性表的链式存储结构8 ● 删除单链表中的第 i个元素 Void DeleteLinkst ( LinkList Head ,int i ) { ListNode * pre; * r; pre = Head ; j = 0; while ( p→next != NULL && j < i – 1 ) { pre = pre→next ; j ++; } if ( j != i - 1) error ( “ position error ” ) ; r =pre→next ; pre→next = r →next; free ( r ) ; } 时间复杂度: O(n) j 用来确定第 i 个元素, pre 用于实现在已知结点 pre 之后删除 free (r) j = i -1 j = i pre r pre pre Head ^ … … j = 1 j = 0
● 线性表及数组应用__线性表的链式存储结构10 ● 删除单链表中所有值为 x 的结点 int Deletex_Linkst ( LNode *H ,ElemType x ){ ① q = H ; ② count = 0 ; while ( q→next) { p = q→next ; if ( p→data = = x ) { ③ q→next = p→next ; ④free ( p ) ; ⑤ ++ count ; } else q = p ; } return count ; free(p) ④ q q ② ① p p ^ … … x H count=0 count=1 ③ ⑤ 时间复杂度: O(n) p 用来确定 x 值, q 用于实现在已知结点 q 之后删除 设 p初始值 删除 用来计数
● 线性表及数组应用__循环链表 和双向链表 1 • ● 循环链表的概念 • ● 循环链表的简单操作 • ● 双向链表的概念 • ● 双向循环链表的插入 • ● 双向循环链表的删除
rear 空表 a1 a2 … an rear • ● 线性表及数组应用__循环链表 和双向链表 2 • ● 循环链表就是将单链表中最后一个结点的指针域指向单链表的表头结点,这样就形成了一个环。 • ● 循环链表中设一尾指针而不设头指针 • ●
a1 a2 … an a1 B1 B2 a2 … … an an ③ A B ② p ① B1 B2 … Bn A ⑤ ④ free(p) ● 线性表及数组应用__循环链表和双向链表 3 ● 两个循环链表首尾相接 {① p = B→next ; ② B→next = A→next ; ③ A→next = p→next ; ④ free ( p ) ; ⑤ A = B ; }