线性表
目录
相关习题!!!:
- 习题见:数据结构(线性表习题1)、以及数据结构(线性表习题2)
- 详细一元多项式的操作方法见文章:【数据结构】一元多项式的表示与相加(无序输入 有序输出)
线性表
特征:有限、性质相同、逻辑关系是一对一
2.1线性表的线性表示和实现
2.1.1创建动态分配的一维数组(顺序存储结构)
定义:线性表顺序存储结构是用一组地址连续的存储单元依次存储线性表的数据元素
a
1
a_1
a1的存放地址也是整个空间的起始地址
1.线性表的动态分配存储结构的定义
线性表的动态分配存储结构的定义代码如下:
//线性表的动态分配存储结构
#define LIST_INST_SIZE 100 //线性表储存空间的初始分配量
#define LIST_INCREMENT 10 //线性表储存空间的分配增量
typedef struct{
//ElemType *elem;
int *elem; //存储空间的基址
int length; //当前数组的长度
int listsize; //当前分配的存储容量
}SqList;
2.创建一个新的 空的线性表 (顺序存储结构)
//创建一个新的 空的线性表 (顺序存储结构)
void InitList_Sq(SqList &L){
L.elem = (int *)malloc(LIST_INST_SIZE*sizeof(int));
//开辟空间后 就可以 直接 使用*(L.elem+i) 进行赋值 访问
if(!L.elem){
cout<<"存储空间开辟失败"<<endl;
exit(0);
}//存储空间开辟失败
L.length = 0; //现在是空表 空表长度为零
L.listsize = LIST_INST_SIZE; //初始开辟的容量大小
}// InitList_Sq
3.赋值
//使用刚刚创建的线性表进行 赋值
void GiveValue_toList(SqList &L){
//例如 进行1 2 3 ...的赋值 一直到 最大
for(int i = 0;i<L.listsize;i++){
*(L.elem+i) = i+1;
//或写成L.elem[i]=i+1;
//现在 elem是一个 一级指针
L.length++; //更新表长
}
}//赋值1 2 3一直到 最大容量
4.输出结果
//输出结果
void PrintValue_List(SqList &L){
for(int i = 0;i<L.length;i++){
cout<<*(L.elem+i)<<endl;
}
cout<<"输出完毕!"<<endl;
}
5.释放空间
//释放空间
void Free_Listsqace(SqList &L){
free(L.elem);
}
主函数
//demo
int main()
{
SqList L;
InitList_Sq(L); //创建一个新的线性表
GiveValue_toList(L);//赋值
PrintValue_List(L); //打印结果
Free_Listsqace(L);//最后释放内存空间
return 0;
}
2.1.2动态分配的一维数组(顺序存储结构)的插入、删除和查找
1.在i位置上 插入元素 e
插入一次平均移动数据:(n+n-1+…+n-i+1+…+1+0)/(n+1)=n/2 算法的时间复杂度 O(n) 插入操作数据移动量大
void ListInsert_Sq(SqList &L,int i ,int e){
//判断插入位置的合法性
//i为要插入的位置 第i位
int *newbase,*p,*q;
if(i<1||i>L.length+1)
return;
//如果当前空间已经满了 开辟新空间
if(L.length>=L.listsize){
//新建
newbase =(int *)realloc(L.elem,(L.listsize+LIST_INCREMENT)*sizeof(int));
if(!newbase) //新建失败
exit(0);
L.elem = newbase;
L.listsize += LIST_INCREMENT;
}
q = L.elem+i-1; //记录插入位置的 地址
for( p= L.elem+L.length-1 ;p>=q;p--)
*(p+1)=*p;//从要插入的位置 元素向后移
*q = e;//插入元素 e;
++L.length;//更新表长
}// 插入元素
插入元素
void InsertList (SqList &L, int i, int e)//线性表用指针数组实现
//在线性表的第i个数据元素前插入一个值为 x 的新元素
{ int j,*newbase;
if ( i <1|| i>L.length+1) return -1;//检查插入位置i的合理性
if (L.length==L.listsize) //检查空间是否够用
{//空间不够用时,增大空间
newbase=(int*)realloc(L.elem,(L.listsize+LISTINCREMENT)* sizeof(int));
if(newbase==0) exit(0);
L.elem=newbase;
L.listsize= L.listsize+LISTINCREMENT;
}
//将an ~ ai顺序向下移动,为新元素让出位置
for(j=L.length;j>=i;j--)
L.elem[j]=L.elem[j-1];
L.elem[i-1]=e;//插入新元素
++L.length;//修改表长
}
算法的时间复杂度O(n)
.插入操作数据移动量大
2.删除第i个元素
删除线性表的第 i 个数据元素
void DeleteList(SqList &L; int i){ //删除线性表的第 i 个数据元素
int j;
if(i<1 || i>L.length) //判断删除的数据元素是否存在
{ printf ("不存在第 i 个元素");return;
//直接返回,返回0代表要删除的元素不存在,没用删除任何数据
}
for(j=i+1;j<=L.length;j++)
L.elem[j-2]=L.elem[j-1]; //将ai₊₁~an顺序向上移动,从而删除ai
//向前移动
L.length--; //修改表长
}
//删除线性表的第 i 个数据元素ai ,e存放被删除的数据元素ai的值
int DeleteList2 (SqList &L; int i, int& e){
//删除线性表的第 i 个数据元素ai ,e存放被删除的数据元素ai的值
int j;
if(i<1 || i>L.length){
//判断删除的数据元素是否存在
printf ("不存在第i个元素");
return 0; ;//直接返回,返回0代表要删除的元素不存在,没用删除任何数据
}
e=L.elem[i-1]; //将第 i 个数据元素的值存入e
for(j=i+1;j<=L.length;j++)
L.elem[j-2]=L.elem[j-1]; //将ai₊₁~an顺序向上移动,从而删除ai
L.length--; //修改表长
return 1; //删除成功
}//该函数执行结束后,e中存放原表中ai的值
算法的时间复杂度O(n)
.删除操作数据移动量大
3.查找元素 e
查找 如果有 找到第一个 就输出
int ListSearch(SqList L, int e){
//在线性表中的查找与给定值e相等的数据元素。若存在返回其位置序号1
int i;
for(i=1;i<=L.length;i++)
if ( L.elem[i-1]== e) return i;//数据元素的序号与存放位置差
return -1; //没找到e
} //从表尾向前查找也是可以的:
//时间复杂度 为O(n)
查找 如果有 有几个输出几个
int ListSearch(SqList L, int e){
//在线性表中的查找与给定值e相等的数据元素。若存在返回其位置序号1
int i,count_e=0;
for(i=1;i<=L.length;i++){// 这里 i从1 开始 所以下面 括号里写[i-1]
if ( L.elem[i-1]== e) {
count_e++;
}
}
if(count_e) return count_e;
return -1; //没找到e
} //从表尾向前查找也是可以的:
//时间复杂度 为O(n)
2.1.3 顺序存储的优缺点
顺序存储结构的优缺点:
优点 ·
- 逻辑相邻一定物理相邻 ·
- 可随机存取任一元素 ·
- 存储空间使用紧凑
缺点 ·插入、删除操作需要移动大量的元素 ·插入时要考虑空间问题,是否溢出
2.2链表
2.2.1 线性链表
线性表的单链存储结构
特点:逻辑相邻不一定物理相邻,只能顺序存取
保存头指针,设置尾结点
带表头结点的线性单链表:表头结点通常空着或存放特殊的信息,比如线性表的长度
表头结点的直接后继才是线性表的第一个数据元素
动态链表
动态链表的定义
动态链表的定义 定义链表类型 结构体
// 动态链表的定义 定义链表类型 结构体
typedef struct Node{
int data;
struct Node *next;
}Node,*LinkList;//可以定义一个 名字也为 LNode的链表吗?
//例如
//定义的时候
typedef struct node{
//定义单链表中存放每个数据元素的结点类型
int data ; // ElemType表示数据元素的类型
struct node *next;
}Node, *LinkList;
//其中struct node 为结构体 类型 node为标识符
//可以用Node 来定义结构体链表 或者 用LinkList结构体指针 来定义
//定义了个 结构体类型的指针
LinkList h,p;//定义指针类型变量
Node *q; //定义指针类型变量
/*定义指针类型变量时没有指向
实际的结点空间,必须初始化*/
补充 结构体中 typedef的使用
typedef struct Student
{
char name[16];
int age;
}stu;
//则 stu 等价于结构体类型 struct Student
// stu scc;等价于 struct Student scc;
//typedef结构体和结构体指针方式
typedef struct Student
{
char name[16];
int age;
}stu, *stu_p;
int main()
{
stu a;
a.age = 16;
printf("%d\n", a.age);
stu_p p;
p = (Student *)malloc(sizeof(struct Student));
p->age = 14;
printf("%d\n", p->age);
free(p);
return 0;
}
//per 等价于 struct Person,perptr 等价于 struct Person*
单链表的基本操作
定义指针类型变量时没有指向实际的结点空间,必须初始化
赋值 初始化
创建动态链表 并初始化 p->data 为结点的值域 p->next为结点的指针域
//赋值方法
//1. malloc( )
p=(LinkList)malloc(sizeof(Node));
//或者
p2=(Node *)malloc(sizeof(Node));
//2. 赋值语句
q=p;
//把已经存在的结点地址p赋给一个指针变量 q,这样p和q指向同一个结点空间
有个疑问 链表最后一个的后继为什么是 NULL 而不是乱码(就是一个随意的地址)?
**
链表未结束的循环条件
typedef struct node{
//定义单链表中存放每个数据元素的结点类型
int data ; // ElemType表示数据元素的类型
struct node *next;
}*LinkList;
LinkList p;//定义指针类型
p=h->next;
while(p!=NULL){
p=p->next;//指针 p后移
}//或while(p)
查找
从表中第一个数据元素开始顺次比较直到找到x,或找到表尾
不可任意读取 必须顺序读取
∙查找:在头指针为h的带表头结点的单链表查找是否存在值为x的结点,若有则返回指向x结点的指针;否则返回NULL
LinkList p;//定义指针类型
p=h->next;
while(p!=NULL&&p->data!= e ){
p=p->next;//指针 p后移
}//或while(p)
if(p!=NULL) cout<<"成功找到 e"<<endl;
else cout<<"未找到 e "<<endl;
//或者写成
LinkList p;//定义指针类型
p=h->next;
while(p!=NULL){
if(p->data== e ) cout<<"成功找到 e"<<endl;
p=p->next;//指针 p后移
}//或while(p)
cout<<"未找到 e "<<endl;
写成函数的形式
LinkList search(LinkList h, int x){
//在头指针为h的带表头结点的单链表中查找是否存在值为x的结点
LinkList p;
p=h->next;//线性表第一个数据元素结点地址
while(p!=NULL&&p->data!=x)
p=p->next; //尚未找到,在下一个结点继续查找
return p;
}
//时间复杂度O(n)
插入
在线性单链表的p结点之后插入一个新的结点x
void insert(LinkList &p,int x){
//在线性单链表的p结点之后插入一个新的结点x
LinkList s;
s=(LinkList)malloc(sizeof(Node));//生成结点空间s
s->data=x;//插入的数据x放入结点空间s
s->next=p->next; //p的直接后继为新插结点s的直接后继
p->next=s; //新插结点s为p的直接后继
}
在第i个 存储结点后 插入一个结点
void insert(LinkList &L,int i,int e){
//在在第i个 存储结点后 插入一个结点
LinkList s,p;
s=(LinkList)malloc(sizeof(Node));//生成结点空间s
p=L->next;
while(p!=NULL){
i--;
if(i==0){
break;
}
p=p->next;
}
if(i!=0){
cout<<"i 输入非法"<<endl;
return;
}
s->data=e;//插入的数据x放入结点空间s
s->next=p->next; //p的直接后继为新插结点s的直接后继
p->next=s; //新插结点s为p的直接后继
}
删除
删除线性单链表中p结点的 直接后继结点
void delete(LinkList &p){
//删除线性单链表中p结点的 直接后继结点
if(p->next)//p结点的直接后继结点是否存在?
{ //q为p的直接后继—被删结点 p🡪next=q🡪next;//p的直接后继改为q的直接后继
free(p->next);//释放直接后继的空间
}
}//我直接这样写行吗 ?
//下面是课件上的
void delete(LinkList &p){
//删除线性单链表中p结点的 直接后继结点
LinkList q;
if(p->next)//p结点的直接后继结点是否存在?
{
q=p->next;//q为p的直接后继—被删结点 p🡪next=q🡪next;//p的直接后继改为q的直接后继
free(q);//释放q的空间
}
}
删除数据为 x
void del(LinkList &h,int x){
//在头指针为h的带表头结点的单链表中删除结点x
LinkList p,q;
p=h->next;//线性表的第一个数据元素
q=h;//p的前驱结点地址
while(p!=NULL){
if(p->data==x){
q->next=p->next;
free(p);
return;
}
else{
q=p;
p=p->next;
}
}
}
删除所有数据为 x
void del(LinkList &h,int x,int &count){
//在头指针为h的带表头结点的单链表中删除结点x
count=0;
LinkList p,q;
p=h->next;//线性表的第一个数据元素
q=h;//p的前驱结点地址
while(p!=NULL){
if(p->data==x){
q->next=p->next;
free(p);
p=q->next;
count++;
}
else{
q=p;
p=p->next;
}
}
}
链表特点:插入和删除均不需要移动数据
相关习题!!!:
- 习题见:数据结构(线性表习题1)、以及数据结构(线性表习题2)
- 详细一元多项式的操作方法见文章:【数据结构】一元多项式的表示与相加(无序输入 有序输出)