线性表:零个或多个数据元素的有序序列。
线性表元素的个数n(n>=0)定义为线性表的长度,当n=0时,称为空表。线性表的长度是有限的,n不能无穷大。
除了第一个元素没有前驱元素,最后一个元素没有后驱元素,其他每个元素都有一个前驱元素和一个后驱元素。
线性表在逻辑结构上属于线性结构,有两种物理结构,分别是顺序存储结构和链式存储结构。
顺序存储结构:
线性表的顺序存储结构指的是用一段地址连续的存储单元依次存储线性表的数据元素。
顺序存储结构的三个属性:
存储空间的起始位置:数组data,它的存储位置就是存储空间的存储位置。
线性表的最大存储容量:数组长度MaxSize。
线性表的当前长度:length。
存储器中的每个存储单元都有自己的编号,这个编号称为地址。
优点:取值,随机存取,只要知道起始位置的地址,可以直接找到第i个元素的地址然后取值。
缺点:不便于插入和删除,因为插入删除之后需要移动大量无关元素的位置。
链式存储结构:
链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素,这组存储单元可以是连续的,也可以是不连续的。这就意味着,这些数据元素可以存在内存中任何未被占用的位置。
链式存储结构中,除了要存数据元素信息外,还要存储它的后继元素的存储地址。
我们把存储数据元素信息的域称为数据域,把存储直接后继位置的域称为指针域。指针域中存储的信息称做指针或链。这两部分信息组成数据元素ai的存储映像,称为结点。
n个结点链结成一个链表,即为线性表的链式存储结构,因为此链表的每个结点中只包含一个指针域,所有叫做单链表。
链表中第一个结点的存储位置叫做头指针,最后一个结点指针为“空”。
优点:便于插入和删除,只需要改变指针指向的地址。
缺点:取值需要指针一个一个走。
为了方便对链表进行操作,会在单链表的第一个结点前附设一个结点,称为头结点。头结点的数据域可以不存储任何信息,也可以存储如线性表的长度等附加信息。
一个单链表有n个结点构成,每个结点分为数据域和指针域两部分,指针域中的信息指向下一个结点。
查找某个元素的时间复杂度都是O(n)
静态链表:
由一个结构体组成,两个变量一个存数据,一个存位置,在数组(data)中的位置不代表它们在链表中的位置,在链表中的位置有另一个游标数组(cur)决定。
静态链表的优缺点:
循环链表:
写的都是看大话数据结构总结的一些知识。
C:语言实现线性表
#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 100
#define OK 1;
#define ERROR 0;
#define TRUE 1;
#define FALSE 0;
typedef int ElemType;
typedef struct
{
ElemType data[MAXSIZE];
int length;
}SqList;
typedef int Status;
Status InitList(SqList *L)
{
/*初始化操作*/
L->length=0;
return OK;
}
Status ListEmpty(SqList L)
{
/*判断线性表是否为空*/
if(L.length==0)
{
return TRUE;
}
else
{
return FALSE;
}
}
Status ClearList(SqList *L)
{
/*将线性表清空*/
L->length = 0;
return OK;
}
Status LocateElem(SqList L,ElemType e)
{
/*在线性表L中查找与给定值e相等的元素,如果查找成功,返回
该元素在表中序号;否则返回0表示失败*/
int i;
for(i=0;i<L.length;i++)
{
if(L.data[i]==e)
{
return i+1;
}
}
return FALSE;
}
Status ListDelete(SqList *L,int i,ElemType *e)
{
/*删除线性表L中的第i个位置的元素,并用e返回其值*/
if(i>L->length||i<=0)
{
return ERROR;
}
if(L->length == 0)
{
return ERROR;
}
*e = L->data[i-1];
for(i=i-1;i<L->length-1;i++)
{
L->data[i] = L->data[i+1];
}
L->length--;
return OK;
}
Status ListLength(SqList L)
{
/*返回线性表L的元素个数*/
return L.length;
}
Status GetElem(SqList L,int i,ElemType *e)
{
//获取第i个元素
if(L.length==0 || i<1 || i>L.length)
return ERROR;
*e = L.data[i-1];
/*这里取得的值赋值到e所指变量中,而如果采用e赋值,
改变的是e的
值,e是局部指针变量,函数结束就释放了,
e所指的变量就取不到值,不会发生任何变化。*/
return OK;
}
Status ListInsert(SqList *L,int i,ElemType e)
{
/*
在L中的第i个位置插入元素e
*/
int k;
if(L->length == MAXSIZE)
{
return ERROR;
}
if(i<1 || i>L->length+1)
{
return ERROR;
}
if(i<=L->length)
{
for(k=L->length;k>=i-1;k--)
{
L->data[k+1] = L->data[k];
}
}
L->data[i-1] = e;
L->length++;
return OK;
}
void Union(SqList *La,SqList Lb)
{
/*两个线性表的并集*/
int La_len = La->length;
int Lb_len = ListLength(Lb);
ElemType e;
for(int i=1;i<Lb_len;i++)
{
GetElem(Lb,i,&e);
if(!LocateElem(*La,e))
{
ListInsert(La,++La_len,e);
}
}
}
int main()
{
int i;
SqList L;
/*函数调用使用&是因为如果不这样调用过去的只是局部变量
当函数结束后数据的改变也就截止了*/
InitList(&L);//初始化
for(i=0;i<=10;i++)//向线性表插入10个元素
{
ListInsert(&L,i+1,i);
}
/*
int l = ListLength(L);//得到线性表的元素个数
printf("线性表的元素个数:%d\n",l);
for(i=0;i<L.length;i++)
{
printf("%d ",L.data[i]);
}
printf("\n");
int e;
GetElem(L,2,&e);//得到线性表中第二个位置的值
printf("线性表中第二个位置的值:%d\n",e);
printf("%d在L中的第%d个位置\n",5,LocateElem(L,5));
if(ListEmpty(L))
{
printf("线性表为空\n");
}
else
{
printf("线性表不为空\n");
}
int k=1;
if(ListDelete(&L,k,&e))
{
printf("删除第%d个元素\n",k);
for(i=0;i<L.length;i++)
{
printf("%d ",L.data[i]);
}
printf("\n");
printf("删除的元素是%d\n",e);
}
else
{
printf("删除失败");
}
if(ClearList(&L))
{
printf("线性表被清空\n");
}*/
SqList La,Lb;
int la=1;
int lb=1;
InitList(&La);
InitList(&Lb);
for(i=1;i<=100;i++)
{
if(i%9==0)
{
ListInsert(&La,la++,i);
}
if(i%3==0)
{
ListInsert(&Lb,lb++,i);
}
}
printf("%d %d\n",La.length,Lb.length);
for(i=0;i<La.length;i++)
{
printf("%d ",La.data[i]);
}
printf("\n");
for(i=0;i<Lb.length;i++)
{
printf("%d ",Lb.data[i]);
}
printf("\n");
Union(&La,Lb);
for(i=0;i<La.length;i++)
{
printf("%d ",La.data[i]);
}
printf("\n");
return 0;
}