第2章-线性表

1.顺序表

1.1 顺序表的定义

1.1.1 静态分配:

#include<stdio.h>
#define MaxSize 10
typedef struct{
	int data[MaxSize];
	int length;
}Sqlist;
//初始化一个顺序表
void InitList(Sqlist &L){
	for(int i=0;i<MaxSize;i++){
		//TODO
		L.data[i]=0;//将所有数据元素设置为默认初始值
	}
	L.length=0;//顺序表初始长度为0
}
int main()
{
	Sqlist L;//声明一个顺序表
	InitList(L);
	for(int i=0;i<MaxSize;i++)
	{
		printf("data[%d]:%d\n",i,L.data[i]);
	}
	return 0;
}

如果不将所有数据元素设置为默认初始值的话,数组元素中就会有各种奇奇怪怪的数据,这是因为内存中会有遗留的“脏数据”

1.1.2 动态分配:

#include<stdio.h>
#include<stdlib.h>
#define InitSize 10//默认的最大长度
typedef struct{
	int *data;//指示动态分配数组的指针
	int MaxSize;//顺序表的最大容量
	int length;//顺序表的当前长度
}Seqlist;
void InitList(Seqlist &L)
{
    //用malloc函数申请一片连续的存储空间
	L.data=(int *)malloc(InitSize*sizeof(int));
	L.length=0;
	L.MaxSize=InitSize;
}
//增加动态数组的长度
void IncreaseSize(Seqlist &L,int len){
	int *p=L.data;
	L.data=(int *)malloc((L.MaxSize+len)*sizeof(int));
	for(int i=0;i<L.length;i++)
	{
		L.data[i]=p[i];
	}
	L.MaxSize=L.MaxSize+len;
	free(p);
}
int main()
{
	Seqlist L;//声明一个顺序表
	InitList(L);//初始化顺序表
	IncreaseSize(L,5);
	return 0;
}

1.2 顺序表上的基本操作

1.2.1 插入操作

#include<stdio.h>
#define MaxSize 10
typedef struct{
	int data[MaxSize];
	int length;
}Sqlist;
//初始化一个顺序表
void InitList(Sqlist &L){
	for(int i=0;i<MaxSize;i++){
		//TODO
		L.data[i]=0;//将所有数据元素设置为默认初始值
	}
	L.length=0;//顺序表初始长度为0
}
bool ListInsert(Sqlist &L,int i,int e)
{
	if(i<1||i>L.length+1) return false;//判断i的范围是否有效
	if(L.length>=MaxSize) return false;//判断当前存储空间是否已满
	for(int j=L.length;j>=i;j--)//将第i个元素及之后元素后移
	{
		L.data[j]=L.data[j-1];
	}
	L.data[i-1]=e;//在位置i处插入e
	L.length++;//长度加一
	return true;
}
int main()
{
	Sqlist L;//声明一个顺序表
	InitList(L);
	L.data[0]=0,L.length++;
	L.data[1]=1,L.length++;
	L.data[2]=2,L.length++;
	ListInsert(L,2,3);
	for(int i=0;i<L.length;i++)
	{
		printf("data[%d]:%d\n",i,L.data[i]);
	}
	return 0;
}

1.2.2 删除操作

#include<stdio.h>
#define MaxSize 10
typedef struct{
	int data[MaxSize];
	int length;
}Sqlist;
//初始化一个顺序表
void InitList(Sqlist &L){
	for(int i=0;i<MaxSize;i++){
		//TODO
		L.data[i]=0;//将所有数据元素设置为默认初始值
	}
	L.length=0;//顺序表初始长度为0
}
bool ListDelete(Sqlist &L,int i,int &e)
{
	if(i<1||i>L.length+1) return false;//判断i的范围是否有效
	e=L.data[i-1];
	for(int j=i;j<L.length;j++)//将第i个元素之后的元素前移
	{
		L.data[j-1]=L.data[j];
	}
	L.length--;//长度减一
	return true;
}
int main()
{
	Sqlist L;//声明一个顺序表
	InitList(L);
	int e=-1;
	L.data[0]=0,L.length++;
	L.data[1]=1,L.length++;
	L.data[2]=2,L.length++;
	L.data[3]=3,L.length++;
	if(ListDelete(L,3,e))
		printf("被删除的元素为%d\n",e);
	else
		printf("位序i不合法,删除失败\n");
	for(int i=0;i<L.length;i++)
	{
		printf("data[%d]:%d\n",i,L.data[i]);
	}	
	return 0;
}

1.2.3 查找操作

按位查找

ElemType GetElem(SeqList L,int i)
{
    return L.data[i-1];
}

按值查找

#include<stdio.h>
#include<stdlib.h>
#define InitSize 10//默认的最大长度
typedef struct{
	int *data;//指示动态分配数组的指针
	int MaxSize;//顺序表的最大容量
	int length;//顺序表的当前长度
}Seqlist;
void InitList(Seqlist &L)
{
    //用malloc函数申请一片连续的存储空间
	L.data=(int *)malloc(InitSize*sizeof(int));
	L.length=0;
	L.MaxSize=InitSize;
}
//增加动态数组的长度
void IncreaseSize(Seqlist &L,int len){
	int *p=L.data;
	L.data=(int *)malloc((L.MaxSize+len)*sizeof(int));
	for(int i=0;i<L.length;i++)
	{
		L.data[i]=p[i];
	}
	L.MaxSize=L.MaxSize+len;
	free(p);
}
int LocateElem(Seqlist L,int e)
{
	for(int i=0;i<L.length;i++)
	{
		if(L.data[i]==e)
			return i+1;
	}
	return 0;
}
int main()
{
	Seqlist L;//声明一个顺序表
	InitList(L);//初始化顺序表
	//IncreaseSize(L,5);
	L.data[0]=1,L.length++;
	L.data[1]=2,L.length++;
	L.data[2]=3,L.length++;
//	for(int i=0;i<L.length;i++){
//		//TODO
//		printf("L%d:%d\n",i,L.data[i]);
//	}
	printf("%d",LocateElem(L,2));
	return 0;
}

2.链表

2.1单链表

2.1.1单链表的定义

struct LNode{
    ElemType data;
    struct LNode *next;
}LNode,*LinkList;
typedef struct LNode LNode;
typedef	struct LNode *LinkList;//用LinkList表示这是一个指向LNode类型的指针

更简介的写法:

typedef struct LNode
{
    ElemType data;
    struct LNode *next;
}LNode,*LinkList;

强调这是一个单链表 --使用LinkList

强调这是一个结点 --使用LNode *

不带头结点的单链表

#include<stdio.h>
#include<stdlib.h>
typedef struct LNode{
	int data;
	struct LNode *next;
}LNode,*LinkList;
bool InitList(LinkList &L)
{
	L=NULL;
	return true;
}
//bool Empty(LinkList L)
//{
//	if(L==NULL) return true;
//	else return false;
//}
bool empty(LinkList L)
{
	return (L==NULL);
}
int main()
{
	LinkList L;
	InitList(L);	
	return 0;
}

带头结点的单链表

#include<stdio.h>
#include<stdlib.h>
typedef struct LNode{//定义单链表结点类型
	int data;//每个结点存放一个数据元素
	struct LNode *next;//指针指向下一个结点
}LNode,*LinkList;

bool InitList(LinkList &L)
{
	L=(LNode *)malloc(sizeof(LNode));//分配一个头结点
	if(L==NULL)	return false;//内存不足,分配失败
	L->next=NULL;//头结点之后暂时还没有结点
	return true;
}

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

int main()
{
	LinkList L;//声明一个指向单链表的指针
	InitList(L);//初始化一个空表
	return 0;
}

2.1.2单链表的插入

2.1.2.1按位序插入

在第i个位置插入元素e(带头结点)

typedef struct LNode
{
    ELemType data;
    struct LNode *next;
}LNode,*LinkList;

bool ListInsert(LinkList &L,int i,ElemType e)
{
    if(i<1) //位序肯定是大于等于1的
        return false;
    LNode *p; //指针p指向当前扫描到的结点
    int j=0; //j用来表示当前p指向的是第几个结点
    p=L; //刚开始先指向头结点,头结点是第0个结点(一般不存数据)
    while(p!=NULL && j<i-1) //用一个while循环找到第i-1个结点
    {
        p=p->next;
        j++;
    }
    if(p==NULL) //i值不合法(如果插入的位置比总结点个数还大1的话)
        return false;
    LNode *s=(LNode *)malloc(sizeof(LNode));
    s->data=e;
    s->next=p->next; //把p的后继结点变成s的后继结点
    p->next=s;	//p的后继结点变成s
    return true;//插入成功
}

平均时间复杂度O(n)

不带头结点

typedef struct LNode
{
    ELemType data;
    struct LNode *next;
}LNode,*LinkList;

bool ListInsert(LinkList &L,int i,ElemType e)
{
    if(i<1) //位序肯定是大于等于1的
        return false;
    if(i==1)	//插入第一个结点时与其他结点不同
    {
        LNode *s=(LNode *)malloc(sizeof(LNode));
        s->data=e;
        s->next=L;
        L=s;	//把头指针指向新结点
        return true;
    }
    LNode *p; //指针p指向当前扫描到的结点
    int j=1; //j用来表示当前p指向的是第几个结点
    p=L; //指向第1个结点(此时就不是头结点了)
    while(p!=NULL && j<i-1) //用一个while循环找到第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; //把p的后继结点变成s的后继结点
    p->next=s;	//p的后继结点变成s
    return true;//插入成功
}
2.1.2.2指定结点的后插操作
//后插操作,在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;	//写入数据元素e
    s->next=p->next;
    p->next=s;	//将s连到p之后
    return true;
}

时间复杂度为O(1)

前面的插入函数就可以改成:

typedef struct LNode
{
    ELemType data;
    struct LNode *next;
}LNode,*LinkList;

bool ListInsert(LinkList &L,int i,ElemType e)
{
    if(i<1) //位序肯定是大于等于1的
        return false;
    LNode *p; //指针p指向当前扫描到的结点
    int j=0; //j用来表示当前p指向的是第几个结点
    p=L; //刚开始先指向头结点,头结点是第0个结点(一般不存数据)
    while(p!=NULL && j<i-1) //用一个while循环找到第i-1个结点
    {
        p=p->next;
        j++;
    }
  	return InsertNextNode(p,e);
}
2.1.2.3指定结点的前插操作

其实本质上还是后插操作,只是把原来前面结点的数据复制到后面的结点上,再把新的数据放到前面这个结点上,看起来就像前插操作。

bool InsertPriorLNode(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;		//将新元素e放到p中,类似于前插操作
    return true;
}
bool InsertPriorLNode(LNode *p,LNode *s)
{
    if(p==NULL || s==NULL) return false;
    s->next=p->next;
    p->next=s;
    ELemType temp=p->data;	//设置一个临时变量存储p的数据
    p->data=s->data;
    s->data=temp;
    return true;
}

2.1.3单链表的删除操作

2.1.3.1按位序删除
typedef struct LNode
{
    ELemType data;
    struct LNode *next;
}LNode,*LinkList;

bool ListDelete(LinkList &L,int i,ElemType &e)
{
    if(i<1) //位序肯定是大于等于1的
        return false;
    LNode *p; //指针p指向当前扫描到的结点
    int j=0; //j用来表示当前p指向的是第几个结点
    p=L; //刚开始先指向头结点,头结点是第0个结点(一般不存数据)
    while(p!=NULL && j<i-1) //用一个while循环找到第i-1个结点
    {
        p=p->next;
        j++;
    }
    if(p==NULL) 
        return false;
    if(p->next==NULL) //第i-1个结点之后已经没有结点
        return false;
    LNode *q=p->next;	//让q指向要被删除的结点
    p->next=q->next;	//将*q结点从链中断开
    e=q->data;			//用e返回元素的值
    free(q);		//释放结点的存储空间
    return true;//删除成功
}
2.1.3.2删除指定结点
//删除指定结点p
bool DeleteNode(LNode *p)
{
    if(p==NULL) return false;
    LNode *q=p->next;		//令q指向*p的后继结点
    p->data=p->next->data;	//和后继结点交换数据
    p->next=q->next;		//将*q从链中断开
    free(q);				//释放后继结点的存储空间
    return true;
}

2.1.4单链表的查找

2.1.4.1按位查找
//按位查找,返回第i个元素(带头结点)
LNode *GetElem(LinkList L,int i)
{
	if(i<0) return NULL;
    LNode *p;	//指针p指向当前扫描到的结点
    int j=0;	//j表示当前p指向的是哪个结点
    p=L;		//L指向头结点,头结点是第0个结点
    while(p!=NULL && j<i)	//循环找到第i个结点
    {
        p=p->next;
        j++;
	}
    return p;
}
LNode *GetElem(LinkList L,int i)
{
	int j=1;//表示指针p刚开始在第一个结点
    LNode *p=L->next;
    if(i==0) return L;
    if(i<1) return NULL;
    while(p!=NULL && j<i)
    {
        p=p->next;
        j++;
	}
    return p;
}

所以前面的插入还有删除函数都可以改成:

bool ListInsert(LinkList &L,int i,ElemType e)
{
    if(i<1) //位序肯定是大于等于1的
        return false;
    LNode *p=GetElem(L,i-1);
  	return InsertNextNode(p,e);
}
2.1.4.2按值查找
//按值查找,找到数据元素==e的结点
LNode *LocateElem(LinkList L,ElemType e)
{
    LNode *p=L->next;
    //从第一个结点开始查找数据为e的结点
    while(p!=NULL && p->data!=e)
    {
        p=p->next;
    }
    return p;//找到后返回该结点指针,否则返回NULL
}

2.1.5求单链表的长度

int Length(LinkList L)
{
    int len=0;//统计表长
    LNode *p=L;
    while(p->next!=NULL)
    {
        p=p->next;
        len++;
    }
    return len;
}

2.1.6单链表的建立

2.1.6.1 尾插法建立单链表
LinkList List_TailInsert(LinkList &L)
{
	int x;
	L=(LNode *)malloc(sizeof(LNode));
	LNode *s;
	LNode *r=L;
	scanf("%d",&x);
	while(x!=9999)
	{
		s=(LNode *)malloc(sizeof(LNode));
		s->data=x;
		r->next=s;
		r=s;
		scanf("%d",&x);
	}
	r->next=NULL;
	return L;
}
2.1.6.2 头插法建立单链表
LinkList List_HeadInsert(LinkList &L)
{
	LNode *s;
	int x;
	L=(LinkList)malloc(sizeof(LNode));
	L->next=NULL;						//初始为空链表
	scanf("%d",&x);						//输入结点的值
	while(x!=9999)
	{
		s=(LNode*)malloc(sizeof(LNode));//创建新结点
		s->data=x;
		s->next=L->next;
		L->next=s;						//将新结点插入表中,L为头指针
		scanf("%d",&x);
	}
	return L;
}

重要应用:链表的逆置

2.1.7 单链表实验程序

#include<stdio.h>
#include<stdlib.h>
typedef struct LNode{
	int data;
	struct LNode *next;
}LNode,*LinkList;

bool InitList(LinkList &L)
{
	L=(LNode *)malloc(sizeof(LNode));
	if(L==NULL)	return false;
	L->next=NULL;
	return true;
}

bool Empty(LinkList L)
{
	if(L->next==NULL) return true;
	else return false;
}
//bool ListInsert(LinkList &L,int i,int e)
//{
//    if(i<1) //位序肯定是大于等于1的
//        return false;
//    if(i==1)	//插入第一个结点时与其他结点不同
//    {
//        LNode *s=(LNode *)malloc(sizeof(LNode));
//        s->data=e;
//        s->next=L;
//        L=s;	//把头指针指向新结点
//        return true;
//    }
//    LNode *p; //指针p指向当前扫描到的结点
//    int j=1; //j用来表示当前p指向的是第几个结点
//    p=L; //指向第1个结点(此时就不是头结点了)
//    while(p!=NULL && j<i-1) //用一个while循环找到第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; //把p的后继结点变成s的后继结点
//    p->next=s;	//p的后继结点变成s
//    return true;//插入成功
//}

//后插操作,在p结点之后插入元素e
bool InsertNextNode(LNode *p,int e) 
{
    if(p==NULL) return false;
    LNode *s=(LNode *)malloc(sizeof(LNode));
    if(s==NULL)	//内存分配失败
        return false;
    s->data=e;	//写入数据元素e
    s->next=p->next;
    p->next=s;	//将s连到p之后
    return true;
}

//bool ListInsert(LinkList &L,int i,int e)
//{
//    if(i<1) //位序肯定是大于等于1的
//        return false;
//    LNode *p; //指针p指向当前扫描到的结点
//    int j=0; //j用来表示当前p指向的是第几个结点
//    p=L; //刚开始先指向头结点,头结点是第0个结点(一般不存数据)
//    while(p!=NULL && j<i-1) //用一个while循环找到第i-1个结点
//    {
//        p=p->next;
//        j++;
//    }
//  	return InsertNextNode(p,e);
//}
//bool InsertPriorLNode(LNode *p,int 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;		//将新元素e放到p中,类似于前插操作
//    return true;
//}
bool InsertPriorLNode(LNode *p,LNode *s)
{
    if(p==NULL || s==NULL) return false;
    s->next=p->next;
    p->next=s;
    int temp=p->data;	//设置一个临时变量存储p的数据
    p->data=s->data;
    s->data=temp;
    return true;
}
bool ListDelete(LinkList &L,int i,int &e)
{
    if(i<1) //位序肯定是大于等于1的
        return false;
    LNode *p; //指针p指向当前扫描到的结点
    int j=0; //j用来表示当前p指向的是第几个结点
    p=L; //刚开始先指向头结点,头结点是第0个结点(一般不存数据)
    while(p!=NULL && j<i-1) //用一个while循环找到第i-1个结点
    {
        p=p->next;
        j++;
    }
    if(p==NULL) 
        return false;
    if(p->next==NULL) //第i-1个结点之后已经没有结点
        return false;
    LNode *q=p->next;	//让q指向要被删除的结点
    p->next=q->next;	//将*q结点从链中断开
    e=q->data;			//用e返回元素的值
    free(q);		//释放结点的存储空间
    return true;//删除成功
}

//删除指定结点p
bool DeleteNode(LNode *p)
{
    if(p==NULL) return false;
    LNode *q=p->next;		//令q指向*p的后继结点
    p->data=p->next->data;	//和后继结点交换数据
    p->next=q->next;		//将*q从链中断开
    free(q);				//释放后继结点的存储空间
    return true;
}

//按位查找,返回第i个元素(带头结点)
LNode *GetElem(LinkList L,int i)
{
	if(i<0) return NULL;
    LNode *p;	//指针p指向当前扫描到的结点
    int j=0;	//j表示当前p指向的是哪个结点
    p=L;		//L指向头结点,头结点是第0个结点
    while(p!=NULL && j<i)	//循环找到第i个结点
    {
        p=p->next;
        j++;
	}
    return p;
}
bool ListInsert(LinkList &L,int i,int e)
{
    if(i<1) //位序肯定是大于等于1的
        return false;
    LNode *p=GetElem(L,i-1);
  	return InsertNextNode(p,e);
}
//按值查找,找到数据元素==e的结点
LNode *LocateElem(LinkList L,int e)
{
    LNode *p=L->next;
    //从第一个结点开始查找数据为e的结点
    while(p!=NULL && p->data!=e)
    {
        p=p->next;
    }
    return p;//找到后返回该结点指针,否则返回NULL
}
int Length(LinkList L)
{
    int len=0;//统计表长
    LNode *p=L;
    while(p->next!=NULL)
    {
        p=p->next;
        len++;
    }
    return len;
}
int main()
{
	LinkList L;
	InitList(L);
	ListInsert(L,1,1);
	ListInsert(L,2,2);
	ListInsert(L,3,3);
	ListInsert(L,4,4);
	LNode *a=GetElem(L,2);
	printf("%d\n",a->data);
	printf("%d\n",Length(L));
	return 0;
}

2.2双链表

typedef struct DNode{		//定义双链表结点类型
	Elemtype data;			//数据域
	struct DNode *prior, *next;//前驱和后继指针
}DNode,*DLinkList;

2.2.1 初始化双链表

bool InitDLinkList(DLinkList &L)
{
	L=(DNode *)malloc(sizeof(DNode));
	if(L==NULL) return false;
	L->prior=NULL;		//头结点的prior永远指向NULL
	L->next=NULL;		//头结点之后暂时还没有结点
	return true;
}
//判断链表是否为空
bool Empty(DLinkList L)
{
    if(L->next==NULL) return true;
    else return false;
}

2.2.2 双链表的插入和删除

//在p结点之后插入s结点
bool InsertNextDNode(DNode *p,DNode *s)
{
	if(p==NULL || s==NULL)
		return false;
	s->next=p->next;
	if(p->next!=NULL)	//如果p结点之后还有结点
		p->next->prior=s;
	s->prior=p;
	p->next=s;
	return true;
}
//删除p结点的后继结点
bool DeleteNextDNode(DNode *p)
{
	if(p==NULL) return false;
	DNode *q=p->next;		//找到p的后继结点q
	if(q==NULL)	return false;	//若p没有后继,返回false
	p->next=q->next;		
	if(q->next!=NULL)		//q结点不是最后一个结点
		q->next->prior=p;
	free(q);				//释放结点空间
	return true;
}
void Destory(DLinkList &L)	//循环释放各个数据结点
{
    while(L->next!=NULL)
        DeleteNextDNode(L);
    free(L);		//释放头结点
    L=NULL;			//头结点指向NULL
}

2.2.3 双链表的遍历

//后向遍历
while(p->next!=NULL)
{
    //todo
    p=p->next;
}
//前向遍历
while(p->prior!=NULL)
{
    //todo
    p=p->prior;
}

2.3循环单链表

typedef struct LNode{
	int data;
	struct LNode *next;
}LNode,*LinkList;

bool InitList(LinkList &L)
{
	L=(LNode *)malloc(sizeof(LNode));
	if(L==NULL)	return false;
	L->next=L;			//头结点的next指向头结点
	return true;
}
//判断循环单链表是否为空
bool Empty(LinkList L)
{
	if(L->next==L) return true;
	else return false;
}
//判断结点p是否为循环单链表的表尾结点
bool isTail(LinkList L,LNode *p)
{
	if(p->next==L)	return true;
	else return false;
}

2.4 循环双链表

typedef struct DNode{
    ElemType data;
    struct DNode *prior,*next;
}DNode,*DLinkList;
//初始化空的循环双链表
bool InitDlinkList(DLinkList &L)
{
    L=(DLnode *)malloc(sizeof(DNode));
    if(L==NULL)
        return false;
    L->prior=L;		//头结点的prior指向头结点
    L->next=L;		//头结点的next指向头结点
    return true;
}
//判断循环双链表是否为空
bool Empty(DLinkList L)
{
    if(L->next==L)
        return true;
    else true false;
}
//判断结点p是否为循环双链表的的表尾结点
bool isTail(DLinkList L,DNode *p){
    if(p->next==NULL) return true;
    else return false;
}
int main()
{
    DLinkList L;
    InitDLinkList(L);
    return 0;
}
//在p结点之后插入s结点
bool InsertNextNode(DNode *p,DNode *s)
{
    s->next=p->next;
    p->next->prior=s;
    s->prior=p;
    p->next=s;
}

2.5 静态链表

#include<stdio.h>
#define MaxSize 10	//静态链表的最大长度
typedef struct {	//静态链表结构类型的定义
	int data;		//存储数据元素
	int next;		//下一个元素的数组下标
}SLinkList[MaxSize];

struct Node{
	int data;		
	int next;
};

int main()
{
	struct Node x;
	printf("size-x:%d\n",sizeof(x));
	struct Node a[MaxSize];
	printf("size-a:%d\n",sizeof(a));
	SLinkList b;
	printf("size-b:%d\n",sizeof(b));
	return 0;
}

output:

size-x:8
size-a:80
size-b:80

3.顺序表和链表的比较

3.1 逻辑结构

都属于线性表,都是线性结构(数据元素之间只存在一对一的关系)

3.2 存储结构

顺序表:

  • 优点:支持随机存取,存储密度高
  • 缺点:大片连续空间分配不方便,改变容量不方便

链表

  • 优点:离散的小空间分配方便,改变容量方便
  • 缺点:不可随机存取,存储密度低

3.3 基本操作

3.3.1创建:

顺序表:需要预分配大片连续空间。若分配空间过小,则之后不方便拓展容量;若分配空间过大,则浪费内存资源。即便是动态分配,容量可以改变,但是需要移动大量元素,时间代价高。

链表:只需要分配一个头结点(也可以不用头结点,只声明一个头指针),之后方便拓展

3.3.2销毁:

顺序表:

​ 静态分配:静态数组 (系统自动回收)

​ 动态分配:动态数组 (malloc,free) 需要手动free释放;malloc和free必须成对出现

链表:依次删除各个结点(free)

3.3.4 插入/删除:

顺序表:插入/删除元素都要将后续元素都后移/前移;时间复杂度O(n),时间开销主要来自于移动元素

若数据元素很大,则移动的时间代价很高

链表:插入/删除元素只需修改指针即可;时间复杂度O(n),时间开销主要来自于查找元素

查找元素的时间代价更低

3.3.5 查找

顺序表:

​ 按位查找:O(1)

​ 按值查找:O(n),若表内元素有序,可在更短时间内找到

链表:

​ 按位查找:O(n)

​ 按值查找:O(n)

3.4 适用场景

顺序表 链表
弹性(可扩容) ×
增、删 ×
×

3.5 答题思路:

逻辑结构......

存储结构......

基本操作......

补充说明:

引用调用:

#include<iostream>
using namespace std;

void test(int x)
{
	x=1024;
	cout<<"x:"<<x<<endl;
}
int main()
{
	int x=1;
	cout<<"x:"<<x<<endl;
	test(x);
	cout<<"x:"<<x<<endl;
	return 0;
}

//x:1
//x:1024
//x:1
#include<iostream>
using namespace std;

void test(int &x)
{
	x=1024;
	cout<<"x:"<<x<<endl;
}
int main()
{
	int x=1;
	cout<<"x:"<<x<<endl;
	test(x);
	cout<<"x:"<<x<<endl;
	return 0;
}

//x:1
//x:1024
//x:1024
posted @ 2023-06-08 20:02  Jinx8823  阅读(2)  评论(0编辑  收藏  举报