一、分类
按照性质划分,可分为为顺序表与线性表。
顺序表:逻辑相邻,物理也相邻。线性表:逻辑相邻,物理不一定相邻。
按照实现方式划分,可分为单链表、循环链表、双向链表。
二、基本原理与实现过程
1.顺序表
对于顺序表,我们使用不定长顺序表,在表中存有三个元素:elem(保存内存动态地址)、litsize(顺序表总长度)、length(顺序表内有效元素个数)。
首先是对于结构体的定义:
typedef struct Sqlist
{
int *elem;
int listsize;
int length;
}Sqlist,*PSqlist;
对于顺序表常用的函数有:
void Init_list(PSqlist plist); //初始化函数
void Init_list(PSqlist plist)
{
plist->elem=(int*)malloc(totalsize*sizeof(int));
plist->listsize=totalsize;
plist->length=0;
}
static bool full(PSqlist plist); //内部提供的判满函数
static bool full(PSqlist plist)
{
return plist->length==plist->listsize;
}
static void Inc_count(PSqlist plist); //内部提供的扩容函数
static void Inc_count(PSqlist plist)//vs下2倍,gcc下1.5倍
{
plist->elem=(int *)realloc(plist->elem,plist->listsize*2*sizeof(int));
plist->listsize=totalsize*2;
//有效数据个数是不变了
}
int Search_val(PSqlist plist,int val); //根据关键字value查找函数
int Search_val(PSqlist plist,int val)//假设无序,O(n),有序二分查找
{
if(plist==NULL)
{ return -1 ;}
for (int i=0;i<plist->length-1;i++)
{
if(plist->elem[i]==val)
{
return i;
}
}
return -1;
}
bool Insert_pos(PSqlist plist ,int val,int pos); //按位置插入函数
bool Insert_pos(PSqlist plist ,int val,int pos)
{
if(plist==NULL||plist->listsize<pos||pos<0)
{ return false;}
if(full(plist))
{ Inc_count(plist) ;}
for(int i=plist->length-1; i>=pos;i--)
{
plist->elem[i+1]=plist->elem[i];
}
plist->elem[pos]=val;
plist->length++;
return true;
}
bool DeletePos(PSqlist plist, int val,int pos); //按位置删除函数
bool DeletePos(PSqlist plist, int val,int pos)
{
if(pos<0||pos>plist->length)
{
return false;
}
for(int i=pos;i<plist->length;i++)//后面数据前移
{
plist->elem[i]=plist->elem[i+1];
}
plist->length--;
return true;
bool Deletevalue(PSqlist plist,int value); //删除关键字value函数
bool Deletevalue(PSqlist plist,int value)//删除从头开始的第一个value
{
if(plist==NULL)
{ return false;}
int p=Search_val(plist,value);
if(p<0)
{ return false;}
return DeletePos(plist,value,p);
}
void Destory(PSqlist plist); //销毁函数
void Show(PSqlist plist)
{
for(int i=0;i<plist->length-1;i++)
{
printf("%d ",plist->elem[i]);
}
printf("\n");
}
void Show(PSqlist plist); //打印函数
void Show(PSqlist plist)
{
for(int i=0;i<plist->length-1;i++)
{
printf("%d ",plist->elem[i]);
}
printf("\n");
}
顺序表的优点:结构简单;不频繁申请和释放内存,无内存碎片的问题;存储数据密度大;查找方便
2.链表
链表有单链表、循环链表、双向链表,双向循环链表
今天首先讲解单链表。
对于单链表,需要存储数据与地址两个信息;所以结构体如下:
typedef struct Node
{
int data;
struct Node *next;
}Node,*List;
bool Insert_Head(PList ps,int val)//头插
bool Insert_Head(PList ps,int val)
{
//创建新结点
Node *p=(Node *)malloc(sizeof(Node));
p->data=val;
p->next=ps->next;
ps->next=p;
return true;
}
bool Insert_Tail(PList ps,int val)//尾插
bool Insert_Tail(PList ps,int val)
{
//创建新结点
Node *p=(Node *)malloc(sizeof(Node));
p->data=val;
//找尾结点
Node *q;
for(q=ps;q->next!=NULL;q=q->next)
;
p->next=q->next;
q->next=p;
return true;
}
bool Delete(PList ps,int key)//删除
bool Delete_key(PList ps,int key)
{
Node *p;
Node *q;
for(p=ps;p->next!=NULL;p=p->next)
{
if(p->next->data==key)
{
q=p->next;
//将q从链表中剔除
p->next=q->next;//p->next=q->next->next等价
free(q);
return true;
}
}
return false;
}
链表逆置:
1.方法一:将所有点向后转 O(n)
void Reserve(PList ps)
{
if(ps==NULL||ps->next==NULL||ps->next->next==NULL)
{
return ;
}
Node *p=ps->next;
Node *q=p->next;
Node *r=q->next;
while(q!=NULL)
{
r=q->next;
q->next=p;
p=q;
q=r;
}
ps->next=p;
}
2.将所有节点按照头插的方式来一遍
void Reserve1(PList ps)//链表逆置二 将所有节点按照头插方式来一遍
{
if(ps==NULL||ps->next==NULL||ps->next->next==NULL)
{
return ;
}
Node *p=ps->next;
Node *q;
ps->next=NULL;
while(p!=NULL)
{
q=p->next;
//将p以头插的方式插入ps
p->next=ps->next;
ps->next=p;
p=q;
}
}
void Destory(PList ps)//摧毁函数
void Destory(PList ps)
{
Node *q;
while(ps->next!=NULL)
{
q=ps->next;
ps->next=q->next;
free(q);
}
}
void Show(PList ps)//打印函数
void Show(PList ps)
{
Node *p;
for(p=ps->next;p!=NULL;p=p->next)
{
printf("%d ",p->data);
}
printf("\n");
}
总结:
需要头结点的遍历,这种不修改链表结构
for(Node *p=plist->next;p->next!=NULL;p=p->next)
不需要头结点的遍历,需要修改链表结构
for(Node *p=plist;p->next!=NULL;p=p->next)