链表的数据结构c语言,[数据结构] C语言链表实现

这篇博客详细介绍了链表这一数据结构的基础知识,包括链表的概念、创建、遍历、插入和删除操作。通过实例代码演示了单链表和双向链表的实现,强调了在操作链表时需要注意的指针更新和逻辑。此外,博客还鼓励读者通过实践和画图来理解链表的插入和删除过程,以加深对数据结构的理解。

2017030738970408.png

当我研究数据结构时,我也感到非常困难. 当我发现这是困难的,因为我没有系统地研究它时,许多教授只专注于数据结构的概念而忽略了代码. 为此,我向那些试图自学数据结构的人写了这些博客文章,希望您能避免绕道而行.

我试图用最简单的语言和代码描述链表,实际上链表实现,它也非常简单

下面的讨论将集中在上面的图片上. 由于它是逐渐实现的,因此我不打算在一开始就使此单个链接列表完美. 它只有两部分: 链表的创建和遍历链表的输出

首先,我们必须了解一些简单的概念. 链表由节点组成,每个节点由另一个数据字段和指向下一个节点的指针组成,因此我们可以轻松地编写以下结构<

5dcc580bebeae5dad20b3f60e278e24c.png

struct node//节点

{

int data;//数据域,这里我们选择存储int型

struct node *next;//指针域,指向下一个节点,因此被定义为struct *NODE

};

然后,如果您仔细观察上图,您会发现仍然有一个头指针. 头指针的功能是指向大内存中此链接列表的第一个节点. 一个节点的下一个指针(通常称为头节点)指向下一个指针,因此很容易想到只要有一个头指针,我们就可以轻松地对链表执行各种操作.

因此我们可以继续编写代码:

int main(){

//创建上图的链表

node *head;//一个头指针。用来指向这个链表的第一个节点

node *f=new node;//对应上图第一个节点first node,这种奇葩命名法不是我要让你们学会的,另我使用了new而不是malloc主要是因为惰性

node *s=new node;//对应上图第二个节点second node

node *t=new node;//对应上图第三个节点third node

f->data=8;//第一个node的值

f->next=s;//第一个节点next指针指向第二个节点

s->data=7;//第二个node的值

s->next=t;//第二个节点next指针指向第三个节点

t->data=9;//第三个节点值

t->next=NULL;//从上图得知第三个节点后没有节点了,所以指向NULL,通常称这个节点为尾节点

head=f;//头指针指向第一个节点,至于为什么前面已经说了//打印这个链表里面储存的元素

std::cout<

node *print_ptr=head;//为什么这里要new一个print_ptr?因为我们可能还要利用head进行其他操作,如果直接用head进行下面的操作,就意味着head指向的位置已经改变了

while(print_ptr!=NULL){

std::cout<data<

print_ptr=print_ptr->next;//更新,让当前指针指向下一个节点

}

//输出节点的个数,虽然我们已经知道了是3个

node *length_ptr=head;//同上面的

print_ptrint k=0;

while(length_ptr!=NULL){

k++;length_ptr=length_ptr->next;

}

std::cout<

node *ins_node=new node;//为即将插入的节点从堆上分配点内存

ins_node->data=14;//赋值

ins_node->next=s;//让新节点的next指向第二个节点

f->next=ins_node;//然后让第一个节点的next指向新节点,这就完成了插入//删除之前插入的节点

ins_node=f->next;//让第一个节点next指向新插入的节点,这里你可能会感到疑惑,我建议你画图或者再看看上面的图就能理解,当然你也可以看下面

f->next=ins_node->next;//第一个节点的next指向新节点的next,因为新节点的next相当于指向了第二个节点,所以这里也等价于第一个节点指向第二个节点f->next=f-next->next

system("pause");

}

824512ad45321a288c0a7b766ea77309.gif

上面的代码完成了使用尾部插值创建的链表,其简单的操作包括create / output / insert / delete,但是正如您所看到的,它还存在许多缺陷,例如命名命名,新建后不删除,以及主要的所有实现都没有考虑功能的使用和其他缺陷,但这没关系,因为我们将逐步对其进行修改,以使他成为一个好的链表

也许您还没有弄清楚上面的插入和删除操作,在这里我将再次专门讨论

在上图中,我们需要在1和2之间插入一个存储14数据的节点,因此我们需要更改下一个指针,因此在f-> next = s之前;它无法使用,您可能会考虑让One指向新节点,然后将新节点指向第三个节点,例如:

f->next=ins_node;

ins_node->next=f->next;

ae5688cd27d7ba479bca81c746f103a8.png

事实通常证明第一感觉是错误的. 如果仔细看一下上面的代码,您会发现添加f-> next = ins_node,那么第二个等效于ins_node-> next = ins_node;它指向你!所以我们需要反过来考虑,首先让新节点指向第二个节点,然后让第一个节点指向新节点,就像上面的代码一样,至于删除,我没有得到图片,我简要地讲一下,删除是直接的. 让第一个节点的下一个指向需要删除的节点,然后让第一个节点的下一个指向需要删除的节点. 接下来,您可能会想为什么不让第一个节点指向第二个节点呢?您可以自己回答这个问题

这时,已经实现了一个简单的链表,但是我们仍然需要继续改进,因为我们有时不知道每个节点中存储的数据,因此我们需要一个动态链表,下面将实现用户数据的输入存储在一个链结构中,这是一个动态链表

#include

struct node

{

int data;

struct node *next;

};

node *create_linklist();

void print_linklist(node *head);

node *create_linklist(){

node *head=new node;

node *new_node,*current;//一个新节点作为当前节点

current=head;//头结点复制给当前节点

int k;

printf("输入节点个数:");

scanf("%d",&k);

for (int i = 0; i < k; ++i)

{

new_node=new node;

if(i==0)//这里也就是创建第一个节点的情况

{

head=new_node;//把头指针指向第一个节点

}

printf("输入节点数据:");

scanf("%d",&new_node->data);

current->next=new_node;//注意这里由于创建了一个新节点,而当前节点还是开始的head的那个位置,所以就需要更新一下,让当前节点next等于new_node的位置

new_node->next=NULL;//当前节点的next=NULL表示当前节点就是最后一个节点了

current=new_node;//然后把new_node节点复制给当前节点,以便继续后面的节点添加

}

return head;

}

void print_linklist(node *head)//参数传入一个头指针用来指向第一个节点

{

node *phead=head;

while(phead!=NULL)

{

printf("%d", phead->data);

phead=phead->next;

}

}

int main(){

node *h=create_linklist();

print_linklist(h);

system("pause");

}

再次考虑以上图片. 以下是一个双向链接列表. 双链列表和单个项链列表之间的区别在于,它具有指向上一个节点的指针,因此我们应该定义这样的结构

ffeaf2a3dc4615f42e026b9df01e9c58.gif

typedef struct NODE

{

int data;

struct NODE *next;

struct NODE *pre;

}node;

由于不可避免地需要从头到尾遍历双向链接列表的某些操作,因此我们应该添加一个概念,即尾指针,即指向尾节点的指针,如下所示以实现双向链接列表,其中有三个节点abc,并且有两种输出方法

#include

typedef struct NODE

{

int data;

struct NODE *next;

struct NODE *pre;

}node;

int main(){

node *a=new node,*b=new node,*c=new node;

node *head=a;

node *tail=c;

a->data=9;

a->next=b;

a->pre=NULL;

b->data=17;

b->next=c;

b->pre=a;

c->data=6;

c->next=NULL;

c->pre=b;

//输出

/*node *print_head=head;

while(print_head!=NULL){

std::cout<data<

print_head=print_head->next;

}*/

/*node *print_head=tail;

while(print_head!=NULL){

std::cout<data<

print_head=print_head->pre;

}*/

system("pause");

}

双向链表的难点不是创建输出,而是插入和删除. 我没有画照片,所以这需要读者仔细考虑. 建议画一幅图,它很容易理解. 以下代码是在上面创建abc的基础上实现的. 在ab之间插入k,然后将其删除

//插入

node *k=new node;

k->data=698;

k->pre=a;

k->next=a->next;

k->next->pre=a;

a->next=k;

//删除

k->pre->next=k->next;

k->next->pre=k->pre;

我不考虑循环链表链表实现,因为它将尾节点指向头节点形成一个环,所以该循环链表不是循环链表,而是循环链表

本文来自电脑杂谈,转载请注明本文网址:

http://www.pc-fly.com/a/jisuanjixue/article-268186-1.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值