数据结构期末复习线性表(2)

1 线性表

线性表是由n个数据元素组成的有限序列,其中元素的个数n为线性表的长度。当n=0时称为空表。通常将非空线性表表示为L=(a1,a2,a3,a4…,an)

1.1线性表的特点

线性表中各元素具有相同的数据类型;
i是ai在线性表中的位序;
对于一个非空线性表,有且只有一个开始节点a1和一个终端节点an。除此之外,其他的所有节点都具有一个直接前驱和直接后继。

2 顺序表

线性表的元素按照逻辑顺序依次存放在一组地址连续的存储单元里。用这种存储方法的线性表称为顺序表。

2.1静态分配存储单元

#define maxsize 1024
typedef int datatype;
typedef struct{
	datatype data[maxsize];//采用一维数组存储线性表 
	int last;//顺序表当前的长度 
}Sqlist;

2.1.1初始化顺序表

#define maxsize 1024
typedef int datatype;
typedef struct{
	datatype data[maxsize];//采用一维数组存储线性表 
	int last;//顺序表当前的长度 
}Sqlist;

void Initlist(Sqlist &L){
for(i=0;i<maxsize;i++){
	L.data[i]=0;//所有数据元素初始化为0 
}
    L.last=0;// 链表长度初始化为0 
}

int main(){
    SqList L; 					//声明一个顺序表
    InitList(L);				//初始化顺序表
    
    return 0;
}

2.2动态分配存储单元

sequenlist*InitList()
{
	sequenlist*L=(sequenlist*)malloc(sizeof(sequenlist));//分配顺序表的动态存储空间
	L->last=0;//表长初始化为0 
	 return L;
}

通过函数返回值将结果带回到主函数


void InitList(sequenlist**L)
{
	L*=(sequenlist*)malloc(sizeof(sequenlist));
	L->=0;
}
  

通过二级指针将L*带回主函数

2.3 顺序表的特点

1 可以随机访问
2 存储密度高
3 顺序存取元素;
4 缺点是插入和删除时效率比较低,需要移动大量的元素。

2.4顺序表的插入

#define maxsize 1024
typedef int datatype;
typedef struct{
	datatype data[maxsize];//采用一维数组存储线性表 
	int last;//顺序表当前的长度 
}Sqlist;

//在顺序表的第i个元素的位置插入一个新的结点x
int Insert(Sqlist *L,int i,datatype x) 
{
	if(L->last==maxsize){printf("表满");return 0; 
	}
	else if(i<1||i>L->last){paintf("非法插入位置");return 0;
	}
	else {
		for(j=L->last;j>=i;j--)
		{
			L->data[j]=L->data[j-1]; //将第i个元素及之后的元素后移
		}
		L->data[i-1]=x;//在位置i处放入e,注意位序、数组下标的关系
		L->last++;
	}
	return 1;
}

int main()
{
	Sqlist L;
	InitList L;
	scarnf("%d",&last);
	for(k=0;k<last;k++)
	{
		scarnf("%d",data[k]);
	}
	Insert(L,3,3);
	return 0;
}

2.4.1顺序表插入的时间复杂度

for(j=L->last;j>=i;j--)
		{
			L->data[j]=L->data[j-1]; //将第i个元素及之后的元素后移
		}

最好的情况是新元素插入到表尾,不需要移动元素,时间复杂度为O(1);
最坏的情况是新元素插入到表头,需要将n个元素全部后移,时间复杂度为O(n)
一般情况时新元素插入到随机位置,时间复杂度为O(n);

2.5顺序表的删除

#define maxsize 1024
typedef int datatype;
typedef struct{
	datatype data[maxsize];//采用一维数组存储线性表 
	int last;//顺序表当前的长度 
}Sqlist;

//在顺序表的第i个元素的位置插入一个新的结点x
int delete(Sqlist *L,int i) 
{

	 if(i<1||i>L->last){paintf("非法删除位置");return 0;
	}
	else {
	
		for(j=i;j<L->last;j++)
		{
			L->data[j-1]=L->data[j]; //节点前移 
		}
	
		L->last--; 
	}
	return 1;
}

int main()
{
	Sqlist L;
	InitList L;
	scarnf("%d",&last);
	for(k=0;k<last;k++)
	{
		scarnf("%d",data[k]);
	}
	delete(L,3;
	return 0;
}
	

2.5.1顺序表删除的时间复杂度

	for(j=i;j<L->last;j++)
		{
			L->data[j-1]=L->data[j]; //节点前移 
		}
	

最好的情况是删除表尾,时间复杂度为O(1);
最坏的情况是删除表头,时间复杂度为O(n)
一般情况时时间复杂度为O(n);

3 单链表

优点:不需要连续的存储空间;
缺点:不可以随机存取,需要浪费空间存储指针。

3.1 单链表结构类型定义

typedef struct node
{
	ElemType data;
	struct node * next;
 } linklist;
 linklist*head,*p;

3.2 初始化不带头结点的单链表

linklist*CreatList()
{
	linklist*head,*p,*r;
	char ch;
	head=NULL;
	r=NULL;//建立空表,尾指针为空 
	while((ch=getchar())!='\n') 
	{
		p=(linklist*)malloc(sizeof(linklist)); 
		p->data=ch;// 输入的字符赋值给新节点的数据域 
		if(head==NULL)
		head=p;//将新节点插入到空表 
		else
		r->next=p;//新节点插入到非空表的表尾 
		r=p;//指针r指向表尾 
	}
	if(r!=NULL)
	r->next=NULL;//对于非空表,将终端节点的指针域 置空 
	return head;
 } 

3.3 判断不带头节点的单链表是否为空

//判断单链表是否为空
bool Empty(LinkList L){
    return (L==NULL);
}

bool是布尔型变量,作用是逻辑型变量的定义符。bool取false和true,是0和1的区别,false代表0,true可以代表很多,不一定是1.
Empty是检查一个变量是否为空

3.4 初始化带头节点的单链表

linklist*CreatList()//头插法 
{
	linklist*head,*p;
	char ch;
	head=(linklist*)malloc(sizeof(linklist));
	head->next=NULL;
	while((ch=getchar())!='\n')
	{
		p=(linklist*)malloc(sizeof(linklist));
		p->data=ch;
		p->next=head->next;
		head->next=p;
	}
	return head;
 } 

3.5 判断不带头节点的单链表是否为空

//判断单链表是否为空
//判断单链表是否为空(带头结点)
bool Empty(LinkList L){
    if(L->next == NULL){
        return true;
    }else{
        return false;
    }
}

3.6 单链表的插入

3.6.1带头结点的单链表的插入

void Insert(linklist*L,datatype x,int i) //L指向具有头结点的单链表 
{
	linklist*p,*s;
	p=Get(L,i-1) ;//调用get算法,查找到第i-1个节点*p 
	if(p==NULL)printf("查找不到第一个节点");
	else 
	{
		s=(linklist*)malloc(sizeof(linklist));
		s->data=x;
		s->next=p->next;//新节点的指针域指向节点ai 
		p->next=s;//节点ai的指针域指向新的节点 
	}
}

3.6.2不带头结点的单链表的插入

bool ListInsert(LinkList &L,int i,ElemType e){
    if(i<1)
    {
        return false;
    }
    if(i==1)		//插入第1个结点的操作与其他结点操作不同
    {
        LNode *s =(LNode *)malloc(sizeof(LNode));
        s->data = e;
        s->next = L->next;
        L = s;		//头指针指向新结点
        return true;
    }
    
    
    LNode *p;		//指针p指向当前扫描到的结点
    int j=1;		//当前p指向的是第几个结点
    p = L;			//p指向第1个结点(注意:不是头结点)
    
    while(P!=NULL && j<i-1){	//循环找到第 i-1 个结点
        p=p->next;
        j++;
    }
    if(p==NULL)		//i值不合法
    {
        return false;
    }
    LNode *s = (LNode *)malloc(sizeof(LNode));
    s->data=e;
    //将s指向结点的next指针指向p指向结点的next指针
    s->next = p->next;	
    p->next = s;	//将p指向结点的next指针指向s
    return true;	//插入成功
}
    
}

3.7 指定节点的后插操作

//后插操作:在p结点之后插入元素e
bool InsertNextNode(LNode *p,ElemType e){
    if(p==NULL)
    {
        return false;
    }
    LNode *s =(LNode *)malloc(sizeof(LNode));
    if(s==NULL)		//某些情况下有可能分配失败,比如内存不足
    {
        return false;	
    }
    s->data = e;
    s->next = p->next;
    p->next = s;
    return true;
}

3.8 指定节点的前插操作

bool InsertPriorNode(LNode *p,ElemType e){
    if(p==NULL)
    {
        return false;
    }
    LNode *s =(LNode *)malloc(sizeof(LNode));
    if(s==NULL)			//内存分配失败
    {
        return false;
    }
    s->next = p->next;	
    p->next = s;		//新结点 s 连接到 p 之后
    s->data = p->data;	//将p中元素复制到s中
    p->data = e;		//p中元素覆盖为e
    return true;
}

4双向链表

4.1 双向链表结构体类型定义


typedef struct dnode
{
	datatype data;
	struct dnode*prior,*next;
}linklist;
dlinklist*h

4.2 双向链表的后插

void DinsertAfter(dlinklist*p,datatype x) 
{//在带头结点的非空双向链表中,将值为x的新节点插入到*p之后 
	dlinklist*s=(dlinklist)malloc(sizeof(dlinklist));
	s->data=x;
	s->prior=p;
	s->next=p->next;
	p->next=s;
		
}

4.3双向链表的前插

void DinsertBefore(dlinklist*p,datatype x) 
{
	dlinklist*s=(dlinklist*)malloc(sizeof(dlinklist));
	s->data=x;
	s->prior=p->prior;
	s->next=p;
	p->prior-next=s;
	p->prior=s;
}

5 循环链表

循环链表是一种首尾相接的链式存储结构。通过表尾指针可以一步得到表头指针。

6 顺序表和链表的比较

线性表优点缺点
顺序表(顺序存储)支持随机存取、存储密度高大片连续空间分配不方便,改变容量不方便
链表(链式存储)离散的小空间分配方便,改变容量方便不可随机存取,存储密度低

*表长难以预估、经常要增加/删除元素——链表

*表长可预估、查询(搜索)操作较多——顺序表

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值