转载请注明出处https://www.cnblogs.com/xiaovw/articles/9969729.html
学习目标:
1、线性表的定义
2、熟悉线性表的存储结构
3、能熟练进行单链表、循环列表、双向链表的各种操作(增、删、改、查、排序、合并),达到能手撸代码
4、知道在链表中设置头节点有什么好处?
5、知道空表该怎么表示?
6、链表的数据元素有两个域名,不再是简单的数据类型,编程时该怎么表示?
学习过程:
1、什么是线性表[List]?
答:零个或多个具有相同数据类型都的数据元素的有限序列。
2、线性表的存储结构都是什么?
答:线性表的存储结构包括 顺序存储结构 和 链式存储结构,其中链式存储结构包括:单链表、静态链表、循环链表和双向链表。
顺序存储结构:是指用一段地址连续的存储单元依次存储线性表的数据元素。
链式存储结构:具有不受固定的存储空间限制,可以比较快捷的插入和删除操作的特点。
3、顺序存储结构
因为顺序存储结构是用一段连续地址来存储数据类型相同的数据元素,所以可以借助一维数组来实现顺序存储结构,即把第一个数据元素存到下标为0的位置中。
(1)数据长度和线性表长度的区别:
数组长度是存放线性表的存储空间长度;线性表长度是线性表中元素的个数。需要注意的是:在任何时刻,线性表的长度应该小于等于数组的长度。
(2)获得指定位置元素「GetElem」:
实现思路:
首先,判断需要获得元素的下标位置是否合理。
然后,将线性表L中的第i个位置元素值返回。
时间复杂度:O(1)
代码实现:
1 /* 初始条件:顺序线性表L已存在 */ 2 /* 操作结果:返回L中第1个与e满足关系的数据元素的位序。 */ 3 /* 若这样的数据元素不存在,则返回值为0 */ 4 int LocateElem(SqList L, ElemType e){ 5 int i; 6 if(L.length == 0){ 7 return 0; 8 } 9 for(i = 0; i < L.length; i ++){ 10 if(L.data[i] == e){ 11 break; 12 } 13 } 14 15 if (i >= L.length) { 16 return 0; 17 } 18 return i + 1; 19 }
(3)在指定位置插入元素「ListInsert」
实现思路:
首先,判断插入位置是否合理;
其次,判断线性表长度是否大于等于线性表长度
然后,从最后一个位置开始向前便利到第i个位置,分别将他们都向后移动一个位置;
最后,将要插入元素填入位置i处,表长加一。
时间复杂度:O(n)
代码实现:
1 /* 初始条件:线性表已经存在,1 <= i <= ListLength(L)*/ 2 /* 操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1 */ 3 Status ListInsert(SqList *L, int i, ElemType e){ 4 int k; 5 if(L->length == MAXSIZE){ /* 线性表已满 */ 6 return ERROR; 7 } 8 9 if(i > L->length+1 || i < 1){ /* 插入元素位置不在线性表范围 */ 10 return ERROR; 11 } 12 13 if(i <= L->length){ /* 插入元素不在表尾 */ 14 for (k = L->length-1; k >= i - 1 ; k --) { /* 将要插入位置之后的数据元素向后移动一位 */ 15 L->data[k+1] = L->data[k]; 16 } 17 } 18 19 L->data[i - 1] = e; /* 将新元素插入 */ 20 L->length++; 21 return OK; 22 }
(4)线性表的删除「ListDelete」
实现思路:
首先,判断删除位置是否合理;
其次,取出删除元素;
再次,从删除元素位置开始遍历到最后一个元素位置,分别将它们都向前移动一个位置;
最后,表长减1。
时间复杂度:O(n)
代码实现:
/* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) */ /* 操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1 */ Status ListDetele(SqList *L, int i, ElemType *e){ int k; if(L->length == 0){ return ERROR; } if(i > L->length || i < 1){ return ERROR; } *e = L->data[i -1]; if(i < L->length){ for(k = i; k < L->length; k ++){ L->data[k-1] = L->data[k]; } } L->length--; return OK; }
(5)线性表的合并
实现思路:
首先,确定两个链表A、B是谁合并到谁;
其次,获取两个链表的长度;
最后,循环移动B线性表中的数据元素到A线性表中
代码展示:
1 Status unionL(SqList *La, SqList Lb){ 2 int i, La_len, Lb_len; 3 ElemType e; 4 La_len = ListLength(*La); 5 Lb_len = ListLength(Lb); 6 for(i = 1; i < Lb_len; i ++){ 7 GetElem(Lb, i, &e); 8 if (!LocateElem(*La, e)) { 9 ListInsert(La, ++La_len, e); 10 } 11 } 12 return OK; 13 }
(5)线性表顺序存储结构的优缺点
优点:
- 无须为表中元素之间的逻辑关系而增加额外的存储空间
- 可以快速的存取表中任意位置的元素
缺点:
- 插入和删除操作需要移动大量元素
- 当线性表长度变化较大时,难以确定存储空间的容量
- 造成存储空间的“碎片”
附:线性表的顺序存储结构的测试源码,C++
// // main.cpp // 线性表--顺序存储结构 // // Created by Mr.G on 2018/10/16. // Copyright © 2018年 Mr.G. All rights reserved. // // #include "stdio.h" #define MAXSIZE 20 /* 存储空间初始分配量 */ #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 typedef int ElemType; /* ElemType类型根据实际情况而定,这里假设为int */ typedef int Status; typedef struct { ElemType data[MAXSIZE]; /* 数组data的存储位置就是存储空间的存储位置*/ int length; /* 线性表的当前长度*/ }SqList; /* 初始化顺序线性表 */ Status InitList(SqList *L){ L->length = 0; return OK; } /* 初始条件:顺序线性表已存在。操作结果:若L为空表,则返回true,否则返回false */ Status ListEmpty(SqList L){ if(L.length == 0){ return TRUE; }else{ return FALSE; } } /* 初始条件:线性表已经存在,操作结果:将线性表L重置为空表 */ Status ClearList(SqList *L){ L->length = 0; return OK; } /* 初始条件:线性表已经存在,1 <= i <= ListLength(L)*/ /* 操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1 */ Status ListInsert(SqList *L, int i, ElemType e){ int k; if(L->length == MAXSIZE){ /* 线性表已满 */ return ERROR; } if(i > L->length+1 || i < 1){ /* 插入元素位置不在线性表范围 */ return ERROR; } if(i <= L->length){ /* 插入元素不在表尾 */ for (k = L->length-1; k >= i - 1 ; k --) { /* 将要插入位置之后的数据元素向后移动一位 */ L->data[k+1] = L->data[k]; } } L->data[i - 1] = e; /* 将新元素插入 */ L->length++; return OK; } Status visit(ElemType e){ printf("%d",e); return OK; } /* 依次对L的每个数据元素输出 */ Status ListTraverse(SqList L){ int i; for(i = 0; i < L.length; i ++){ printf("\t"); visit(L.data[i]); } printf("\n"); return OK; } /* 根据制定位置获得元素,*/ Status GetElem(SqList L, int i, ElemType *e){ if(L.length == 0 || i < 1 || i > L.length){ return ERROR; } *e = L.data[i - 1]; return OK; } /* 初始条件:顺序线性表L已存在 */ /* 操作结果:返回L中第1个与e满足关系的数据元素的位序。 */ /* 若这样的数据元素不存在,则返回值为0 */ int LocateElem(SqList L, ElemType e){ int i; if(L.length == 0){ return 0; } for(i = 0; i < L.length; i ++){ if(L.data[i] == e){ break; } } if (i >= L.length) { return 0; } return i + 1; } int ListLength(SqList L){ return L.length; } /* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) */ /* 操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1 */ Status ListDetele(SqList *L, int i, ElemType *e){ int k; if(L->length == 0){ return ERROR; } if(i > L->length || i < 1){ return ERROR; } *e = L->data[i -1]; if(i < L->length){ for(k = i; k < L->length; k ++){ L->data[k-1] = L->data[k]; } } L->length--; return OK; } Status unionL(SqList *La, SqList Lb){ int i, La_len, Lb_len; ElemType e; La_len = ListLength(*La); Lb_len = ListLength(Lb); for(i = 1; i < Lb_len; i ++){ GetElem(Lb, i, &e); if (!LocateElem(*La, e)) { ListInsert(La, ++La_len, e); } } return OK; } int main(){ SqList L; SqList Lb; Status i; ElemType e; int j,k; i = InitList(&L); printf("初始化后:L.length=%d\n",L.length); for(j = 1; j <= 10; j ++){ ListInsert(&L, 1, j); } printf("在表头依次插入元素后:L->data="); ListTraverse(L); printf("L.length=%d",L.length); i = ListEmpty(L); printf("L是否为空:i=%d(1:是,0:否)\n",i); i = ClearList(&L); printf("清空L后:L.length=%d\n",L.length); for (j = 1; j <= 10; j ++) { ListInsert(&L, j, j); } printf("在表尾依次插入元素后:L->data="); ListTraverse(L); printf("L.length=%d\n",L.length); GetElem(L, 5, &e); printf("第5个元素的值为:%d\n",e); for(j = 3; j <= 4; j ++){ k = LocateElem(L, j); if(k){ printf("第%d个元素的值为%d\n",k,j); }else{ printf("没有值为%d的元素\n",j); } } k = ListLength(L); /* k为表长 */ for(j = k + 1; j >= k; j --){ i = ListDetele(&L,j,&e); if(i == ERROR){ printf("删除第%d个元素失败!!\n",j); }else{ printf("删除第%d个元素的值为:%d\n",j,e); } } printf("依次输出L的元素:"); ListTraverse(L); j = 5; ListDetele(&L,j,&e); /* 删除第五个数据 */ printf("删除第%d个元素的值为:%d\n",j,e); printf("依次输出L的元素:"); ListTraverse(L); //构造一个有10个数的Lb i = InitList(&Lb); for(j = 6; j <= 15; j ++){ i = ListInsert(&Lb, 1, j); } unionL(&L,Lb); printf("依次输出合并了Lb的L的元素:"); ListTraverse(L); return 0; }
小V