240 likes | 387 Views
《 数据结构 》 第二章 线性表. 运城学院 计算机科学与技术系. 2.1 线性表的逻辑结构. 1 、线性表定义: n ( n≧0 )个结点的有穷序列。即: a 1 、 a 2 、 a 3 … a i 、 a i+1 … a n 其中: a i 代表一个结点, i 为 a i 在线性表中的位置序号, n 为表长。或者: B=(A,R), A={a i |i=1,2,..,n},R={<a i-1 ,a i >|I=2,3, … ,n} 特点:一个始点 a 1 一个终点 a n 线性表的逻辑结构为线性结构
E N D
《数据结构》第二章 线性表 运城学院 计算机科学与技术系 运城学院计科系《数据结构》
2.1线性表的逻辑结构 1、线性表定义:n(n≧0)个结点的有穷序列。即: a1、a2、a3…ai、ai+1…an 其中:ai代表一个结点,i为ai在线性表中的位置序号,n为表长。或者:B=(A,R), A={ai|i=1,2,..,n},R={<ai-1,ai>|I=2,3,…,n} 特点:一个始点a1 一个终点 an 线性表的逻辑结构为线性结构 1-1的关系 2、常见的操作: 3、存储结构:顺序存储(顺序表) 链式存储(链表) 初始化 求表长 访问元素 查找 删除元素 插入元素 运城学院计科系《数据结构》
数据元素的逻辑顺序与在内存中的存储顺序完全一致,不需要额外存储数据元素之间的关系数据元素的逻辑顺序与在内存中的存储顺序完全一致,不需要额外存储数据元素之间的关系 l.data a1 a2 a3 an l.length 2.2顺序存储结构 一、顺序表:线性表的顺序存储。 存储分配:一块连续的存储空间----存放元素 一个整形单元----记表中的元素个数 顺序表的 语言描述: typedef struct node {datatype data[listsize]; int length; }seqlist; seqlist l; l------顺序表 特点:随机存取---loc(ai)=loc(a1)+(i-1)*c 存取密度高---物理上的相邻体现逻辑上的 相邻 运城学院计科系《数据结构》
l.data l->data a1 a2 a3 an l.length l->length 2.2顺序存储结构 访问形式: 结构体变量形式 指针变量形式 l.data[i] 访问第i个结点 l->data[i] l.length 表长 l->length l.data[l.length] 访问最后元素 l->data[l->length] 运城学院计科系《数据结构》
void insertlist(seqlist*l,int i,datatype x) {if(i<1||i>l->length+1) error(“position error”); if(l->length=listsize) error(“overflow”); for(j=l->length-1;j>=i-1;j--) l->data[j+1]=l->data[j]; l->data[i-1]=x; l->length++; } 注意:指针作为函数参数的特点,在此算法中结构体变量不能作为函数参数。 基本思想:是指在表的第i个(1≦i≦n+1)的位置上,插入一个新结点x,使长度为n的线性表 (a1,a2,…,ai-1,ai,…,an) 变成长度为n+1的线性表 (a1,a2,…,ai-1,x,a'i+1,…,a'n+1) 2.2顺序存储结构 时间复杂度:O(n) 1)、插入insertlist(l,i,x) 2、基本操作在顺序表上的实现 判断i位置的合法性 元素移动n-i+1次 插入并修改表长 运城学院计科系《数据结构》
插入insertlist(l,i,x) 时间效率分析 基本操作------元素的移动 因为元素的移动次数与位序有关,所以讨论平均移动次数。 平均移动次数= 其中Pi=1/(n+1)为每个位置插入的概率 在第i位插入元素移动的次数为:n-i+1 所以Eis=T(n)==n/2 时间复杂性为O(T(n))=O(n) 结论:插入时元素的移动次数是表长的一半,效率较低。 运城学院计科系《数据结构》
基本思想:删除表中第i个(1≦i≦n)位置上的元素,使长度为n的线性表基本思想:删除表中第i个(1≦i≦n)位置上的元素,使长度为n的线性表 (a1,a2,…,ai-1,ai,ai+1…,an) 变成长度为n-1的线性表 (a1,a2,…,ai-1,a'i,…,a'n-1) 2、基本操作在顺序表上的实现 时间复杂度:O(n) 2)、删除第i个元素 2、基本操作在顺序表上的实现 void deletelist(seqlist*l,int i) {if(i<1||i>l->length) error(“position error”); for(j=i;j<=l->length-1;j++) l->data[j-1]=l->data[j]; l->length--; } 注意:在此算法中结构体变量不能作为函数参数。 同理分析:Eds=(n-1)/2 时间复杂性为O(T(n))=O(n) 结论:插、删时元素的移动次数为O(n),效率较低。 几乎表长的一半 运城学院计科系《数据结构》
void creatsqlist(SEQLIST *L) { int i; printf("请输入元素个数\n"); scanf("%d",&L->len); printf("请输入%d个元素\n"); for(i=0;i<L->len;i++) scanf("%d",&L->data[i].key); /*略去了其他数据项的输入*/ } 2.2顺序存储结构 时间复杂度:O(n) 1、线性表的创建 2、基本操作在顺序表上的实现 运城学院计科系《数据结构》
依次将La,Lb中的较小者续接到Lc中 void Sqmerge(SQLIST La,SQLIST Lb, SQLIST *Lc) { int i=0,j=0,k=0,m,n; n=La.len;m=Lb.len;Lc->len=m+n; while(i<n&&j<m) if(La.elem[i].key<=Lb.elem[j].key) Lc->elem[k++]=La.elem[i++]; else Lc->elem[k++]=Lb.elem[j++]; while(i<n)Lc->elem[k++]=La.elem[i++]; while(j<m)Lc->elem[k++]=Lb.elem[j++]; } 将La或Lb中的剩余部分续接到Lc 2、顺序表 时间复杂度:O(n+m) 【例】线性表的合并 顺序表的操作实例 运城学院计科系《数据结构》
链表需要有个指向首结点或者头结点的指针来标识;链表需要有个指向首结点或者头结点的指针来标识; head a1 a2 a3 a3 a1 a2 an an ^ ^ 头结点 L 2.3链表之单链表 一、单链表定义 结点:数据元素与指向后继的指针存储在一起称为结点 数据域:结点中存放数据元素的部分 指针域:结点中存放指针的部分 单链表:结点中只有一个指针的链表 运城学院计科系《数据结构》
typedef struct node { datatype data; /*数据域,存放数据元素*/ struct node *next; /*指针域,指向后继元素*/ }listnode, Typedef listnode * linklist; /*定义头指针类型*/ 2.3链表之单链表 单链表的数据类型定义: 运城学院计科系《数据结构》
LINKLIST creatlinklist() { LINKLIST head; NODEPTR p,q;int i,n;elemtype e; L=(NODEPTR)malloc(LEN); L->next=NULL; q=L; scanf("%d",&n); for(i=1;i<=n;i++) { p=(NODEPTR)malloc(LEN); getelem(&e);p->data=e; q->next=p;q=p;}//for q->next=NULL; return L; } 2.3链表之单链表 时间复杂度:O(n) 1、线性表的创建 二、单链表的基本操作 运城学院计科系《数据结构》
void travlist(LINKLIST L) {/* L带有头结点*/ NODEPTR p; p=L->next; /*指向首结点*/ while(p) {/*沿着next链依次访问结点*/ printf("%3d",p->data.key); p=p->next; } printf("\n"); } 2.3链表之单链表 时间复杂度:O(n) 2、线性表的遍历 单链表的基本操作 运城学院计科系《数据结构》
NODEPTR getelemi(LINKLIST L,int i, NODEPTR *pre)/*L带有头结点*/ {NODEPTR p,q;int j; if(i<=0)return NULL; q=L; *pre=NULL; p=L->next;j=1;/* p指向了第j个元素*/ while(p&&j<i) /*只要p不空,向后数*/ { q=p;p=p->next;++j; /*记下p的前驱*/ } if(p)*pre=q; return p; } 2.3链表之单链表 时间复杂度:O(n) 3、定位第i个元素 单链表的基本操作 运城学院计科系《数据结构》
NODEPTR preelem(LINKLIST L, NODEPTR p) { NODEPTR q; q=L; while(q&&q->next!=p) q=q->next; return q; } 2.3链表之单链表 时间复杂度:O(n) 4、定位结点p的前驱 单链表的基本操作 运城学院计科系《数据结构》
int delelem(LINKLIST L,int i) { NODEPTR p,pre; p=getelem(L,i,&pre); if(p){pre->next=p->next;free(p);return 1;} else return 0; } 2.3链表之单链表 时间复杂度:O(n) 5、删除第i个结点 单链表的基本操作 pre p 运城学院计科系《数据结构》
s ① ② s ① ② int inselem(LINKLIST L,int i, elemtype e, int mark)/*mark表示在第i个结点的前或后*/ { NODEPTR p,s,pre; p=getelemi(L,I,&pre); if(p) { s=(NODEPTR)malloc(LEN); s->data=e; if(mark==1) {s->next=p->next;p->next=s;} else {s->next=p;pre->next=s;} return 1; } else return 0; } pre p pre p 2.3链表之单链表 时间复杂度:O(n) 6、在第i个结点处插入 单链表的基本操作 运城学院计科系《数据结构》
LINKLIST LkMerge(LINKLIST La, LINKLIST Lb)/*La,Lb没有头结点*/ {NODEPTR pa,pb,tail; LINKLIST head; pa=La;pb=Lb; /*下面if语句定位新链表的首结点*/ if(pa->data.key<=pb->data.key) {head=tail=pa;pa=pa->next;} else {head=tail=pb;pb=pb->next;} while(pa&&pb) if(pa->data.key<=pb->data.key) {tail->next=pa;tail=pa;pa=pa->next;} else {tail->next=pb;tail=pb;pb=pb->next;} tail->next=pa?pa:pb; /*如果pa,pb哪个未空,直接续接到tail后面*/ return head; } 2.3链表之单链表 线性表的合并 时间复杂度:O(n+m) 单链表的操作实例 运城学院计科系《数据结构》
^ ^ 2.3链表之循环链表和双向链表 循环链表:表尾结点的next指针指向表头结点的链表。 双向链表:结点存在两个指针域,一个指向后继结点,另一个指向前驱结点。 双向循环链表: 运城学院计科系《数据结构》
typedef struct dbnode { elemtype data; struct dbnode *prior; struct dbnode *next; }DBNODE, *DBNODEPTR,*DBLINKLIST; 2.3链表之循环链表和双向链表 双向链表的数据类型定义: 运城学院计科系《数据结构》
int inselem(DBNODEPTR p,elemtype e) {/* p指向某双向循环链表中的某个结点,将e结点插入到p的后面*/ DBNODEPTR s,r; s=(DBNODEPTR)malloc(sizeof(DBNODE)); if(!s)return 0; s->data=e;/*建立新的结点*/ r=p->next; s->next=r; r->prior=s; p->next=s; s->prior=p; } p r s 2.3链表之循环链表和双向链表 双向循环链表的结点插入算法 运城学院计科系《数据结构》
void delelem(DBNODEPTR p) {/*p指向某双向循环链表某结点,这个算法将其从链表中摘除*/ DBNODEPTR q,r; q=p->prior; r=p->next; q->next=r; r->prior=q; /*也可以这样操作: p->prior->next=p->next; p->next->prior=p->prior; */ } q p r 2.3链表之循环链表和双向链表 双向循环链表的结点删除算法 运城学院计科系《数据结构》
2.4链表与顺序表对比 运城学院计科系《数据结构》
《数据结构》第二章 线性表 O V E R 运城学院计科系《数据结构》