线性表是一种常用的简单数据结构,它是由零个或多个元素组成的有限序列。强调了元素的有限性和元素之间的顺序性。
线性表有两种物理结构,第一种是顺序存储结构,例如我们熟悉的数组。
线性表的顺序存储结构,指的是用一段地址连续的存储单元依次存储线性表的数据元素。
顺序结构最大的特点就是:顺序存储,随机访问 。对于初学者来说使用非常便利。但是,它致命的缺点在于:
1.相邻的两元素的存储位置(地址)也具有邻居关系,导致进行插入、删除等操作时需要移动大量元素,耗费时间。
2. 使用前需声明数组的长度,开辟固定的空间,且后续不能再修改。
3.只能存储一种类型的数据,内容单一,无法满足实际应用。
于是我们引入了线性表的另一种物理结构:链式存储结构。
链式存储结构的特点是用一组任意的存储单元来存储线性表的数据元素,不要求它们的地址相连,需要的时候就开辟新的空间。
位置都不连在一起,我怎么找数据?
在链式结构中,除了要存数据元素信息外,还要存储它后继元素的存储地址!它的特点与顺序结构相反:随机存储,顺序访问。
链表可以动态地进行存储分配,也正是因为这个特点,它具有强大功能,能够实现许多操作,例如:链表的创建、修改、查找、增加、删除、清空、输出......
链表的结构特点:
- 基于结构体指针。
- 具有头节点(head),一般没有数据域,仅有指针域。
- 所有节点离散分布,仅由指针相联系。
- 头节点只有后继节点,尾节点只有前驱节点。
准备工作
(原创不易,希望各位看官不要吝啬手指,点点赞吧)
引用头文件
#include
#include
定义结构体
//一个简单结构体,只存一个数
typedef struct Node
{
int data;
struct Node *next;
}linklist;
malloc函数
使用形式:
指针自身 = (指针类型*)malloc(sizeof(指针类型))
注意事项:
malloc函数的返回的是无类型指针,在使用时一定要强制转换为所需要的类型。
重点:在使用malloc开辟空间时,使用完成一定要释放空间,如果不释放会造内存泄漏。
在使用malloc函数开辟的空间中,不要进行指针的移动,因为一旦移动之后可能出现申请的空间和释放空间大小的不匹配。
头插法创建链表
头插法顾名思义,就是把新建的单元插在链表头部,这样形成的链表数据是反着存入的。
linklist *create_head(int n)
{
linklist *head,*pt;
head=(linklist *)malloc(sizeof(linklist));
//注意初始化为NULL,以免造成内存泄漏。
head->next=NULL;
for(int i=1;i<=n;i++)
{
pt=(linklist*)malloc(sizeof(linklist));
scanf("%d",&pt->data);
//head的后继交给pt,成为pt的后继
//pt又成为head的后继
pt->next=head->next;
head->next=pt;
}
return head;
}
pt->next=head->next;
head->next=pt;
如图所示:
尾插法创建链表
linklist *create_tail(int n)
{
//尾插法需要用到一个tail指针,一直跟随在链表尾部
linklist *head,*tail,*pt;
head=(linklist *)malloc(sizeof(linklist));
//最开始头即是尾
tail=head;
for(int i=1;i<=n;i++)
{
pt=(linklist*)malloc(sizeof(linklist));
scanf("%d",&pt->data);
pt->next=NULL;
//把pt接在尾部,然后tail后移
tail->next=pt;
tail=pt;
}
return head;
}
tail->next=pt;
tail=pt;
如图所示:
创建好链表以后,我们进行一些基本操作:
·检查链表是否为空
int judgeempty(linklist *head)
{
if(head->next==NULL)
{
printf("The Linklist is empty.\n");
return 0;
}
else
{
printf("The Linklist has something in it.\n");
return 1;
}
}
·求链表长度
int list_length(linklist *head)
{
linklist *p;
int cnt=0;
p=head->next;
while(p!=NULL)
{
cnt++;
p=p->next;
}
return cnt;
}
·输出链表
void output(linklist *head)
{
linklist *p;
p=head->next;
printf("H");
while(p!=NULL)
{
printf("-->%d",p->data);
p=p->next;
}
printf("\n");
}
求长度和输出的思路很简单,就是将创建好的链表遍历一遍。
查找链表元素
接着我们实现查找功能
1.输入位置,查找对应元素。
int get_it(linklist *head,int i,int *num)
{
int cnt=1;
linklist *temp;
temp=head->next;
while(temp&&cntnext;
cnt++;
}
if(!temp||cnt>i)
{
return 0;
}
*num=temp->data;
return 1;
}
2.输入元素,查找对应位置。
int search_it(linklist *head,int i,int *n)
{
linklist *now=head->next;
int index=1,num;
while(now!=NULL)
{
num=now->data ;
if(num==i)
{
*n=index;
return 1;
}
now=now->next;
index++;
}
return 0;
}
修改、插入、删除操作
1.修改
int a;
printf("修改\n");
printf("输入你想要修改的位置 ");
scanf("%d",&a);
HEAD=change_it(HEAD,a);
output(HEAD);
linklist *change_it(linklist *head,int n)
{
linklist *t=head;
int i=0;
while(inext;
i++;
}
if(t!=NULL)
{
printf("Input the value you want: ");
scanf("%d",&t->data);
printf("Change,Done!\n");
return head;
}
else
printf("The node does not exist.\n");
return head;
}
2.插入
int b;
printf("插入\n");
printf("输入你想要插入的位置 ");
scanf("%d",&b);
HEAD=insert_it(HEAD,b);
output(HEAD);
linklist *insert_it(linklist *head,int n)
{
linklist *t=head;
linklist *ip;
int i=1;
while(inext;
i++;
}
if(t!=NULL)
{
ip=(linklist*)malloc(sizeof(linklist));
printf("Input the value you want: ");
scanf("%d",&ip->data);
ip->next=t->next;
t->next=ip;
printf("Insert,Done!\n");
return head;
}
else
printf("The node does not exist.\n");
return head;
}
3.删除
int c;
printf("删除\n");
printf("输入你想要删除的位置 ");
scanf("%d",&c);
HEAD=delete_it(HEAD,c);
output(HEAD);
linklist *delete_it(linklist *head,int n)
{
linklist *t=head;
linklist *dp;
int i=0;
while(inext;
i++;
}
if(t!=NULL)
{
dp->next=t->next;
free(t);
printf("Delete,Done!\n");
return head;
}
else
printf("The node does not exist.\n");
return head;
}
完成了上述的一系列操作,我们要具备环保意识,回收内存!
清空链表
void clearlist(linklist *head)
{
linklist *q,*qr;
q=head;
while(q!=NULL)
{
qr=q->next;
free(q);
q=qr;
}
head->next=NULL;
if(q==NULL)
printf("CLEAR!");
return ;
}
最后献上完整的主函数
int main()
{
int n;
scanf("%d",&n);
linklist *HEAD;
//创建链表
printf("创建链表\n");
//HEAD=create_head(n);
HEAD=create_tail(n);
printf("\n");
//检查、求长度、输出
printf("检查、求长度、输出链表\n");
if(judgeempty(HEAD))
{
printf("%d\n",list_length(HEAD));
output(HEAD);
}
printf("\n");
//查找数据
printf("查找下标对应数\n");
int num;
int *place;
scanf("%d",&num);
if(get_it(HEAD,num,place))
{
printf("The number with index %d is %d.\n",num,*place);
}
else
printf("ERROR!\n");
printf("\n");
//查找下标
printf("查找数对应下标\n");
int *index;
int element;
scanf("%d",&element);
if(search_it(HEAD,element,index))
{
printf("Congratulations!The index of %d is %d.\n",element,*index);
}
else
{
printf("Sorry!%d can't be found!\n",element);
}
printf("\n");
//修改、插入、删除
int a,b,c;
printf("修改\n");
printf("输入你想要修改的位置 ");
scanf("%d",&a);
HEAD=change_it(HEAD,a);
output(HEAD);
printf("插入\n");
printf("输入你想要插入的位置 ");
scanf("%d",&b);
HEAD=insert_it(HEAD,b);
output(HEAD);
printf("删除\n");
printf("输入你想要删除的位置 ");
scanf("%d",&c);
HEAD=delete_it(HEAD,c);
output(HEAD);
printf("\n");
//清空
clearlist(HEAD);
return 0;
}