c语言中头结点不为零怎么写,C语言不带表头结点的单链表操作

什么是链表

简单理解为链表的功能与数组功能相似用来存储数据,链表作为一种基本的数据结构在程序开发过程当中经常会使用到。对C语言来说链表的实现主要依靠结构体(可以存储多种数据类型)和指针,所以本文相关内容和程序需要有C语言当中指针和结构体的基础。

为什么使用链表

1、解决数组无法存储多种数据类型的问题。

2、解决数组中,元素个数无法改变的限制。

3、数组移动元素的过程中,要对元素进行大范围的移动,很耗时间,效率也不高。

链表的组成

1、链表由节点组成。

2、节点由值域与指针域组成。

3、还需要一个头指针指向链表(例如一个指针指向一个数组,那么数组名也可以代表此数组的首地址)

4、链表的基本表示

struct node //表示图片中的体

{

int num;

char a;

struct node *next; //指向下一个相同数据类型的结构体,next存放下一个结点的地址

}

8d7d59755ed146f7b5bf7226e9ba0f0f.png

> head头指针指向头结点,其中头结点的next指针指向下一个节点的头,一次类推直到最后一个节点的尾指针指向NULL(空指针)时离链表结束。

(这里的体是结构体类型,next是指向下一个结点的指针,结点的组成:数据域(体是数据域的一种体现)+(指针域) )

链表的特点

1、链表结点可以连续,可以不连续存储。

2、结点的逻辑顺序与物理顺序可以不一致。

3、表可扩充。

逻辑顺序与物理顺序的理解

可以使用的存储空间2e7469f68aaef75202277fda7a24a364.png

482e78691749a4f548ec86b72f133413.png

解释:各个结点的物理存储位置可能不是连续的,a0后面是a2而不是a1,但是每个结点的尾地址都是按照逻辑顺序指向下一个结点的。(意思就是说我有10个抽屉,每个抽屉编号1~10,但是这10个抽屉不是按顺序摆放,将扑克牌A到10一次按抽屉顺序放入10个抽屉里,每次取按顺序取出,例如第一个抽屉在第1层,里面是A,接下来要去2但是存放2的抽屉在第5层因此要去第5层取2)

怎么插入一个节点

在头结点的后面插入一个节点

插入一个中间节点

在头节点前面插入节点(头插)

尾插

1、

22cf8b9ca9046947b4e5970dae8c835b.png

将一个新的结点插入到最后一个节点的后面,先将前一个尾结点的地址指向新结点的地址P,然后再将新结点的指针域改成NULL。

2、

2d7d8c91b3e1d8566b1bea924afad712.png

头插入:新建一个结点他的指针域指向头结点的头地址,修改之后在将链表的头地址指向新建结点的地址这样新建结点就成为链表新的头节点

**

对于链表中&head,一级指针,二级指针的总结

分析:

1,只要是修改头指针则必须传递头指针的地址,否则传递头指针值即可(即头指针本身)。这与普通变量类似,当需要修改普通变量的值,需传递其地址,否则传递普通变量的值即可(即这个变量的拷贝)。使用二级指针,很方便就修改了传入的结点一级指针的值。 如果用一级指针,则只能通过指针修改指针所指内容,却无法修改指针的值,也就是指针所指的内存块。所以创建链表和销毁链表需要二级指针或者一级指针引用。

2,不需要修改头指针的地方用一级指针就可以了,比如插入,删除,遍历,清空结点。假如头指针是L,则对L->next 及之后的结点指针只需要传递一级指针。

3,比如一个结点p,在函数里要修改p的指向就要用二级指针,如果只是修改p的next指向则用一级指针就可以了

~~~*以下代码均是对于无表头结点的操作*~

**

写头插时应该先想好代码的思路:

创建结点元素

创建空链表

创建结点

插入节点函数

编译链表

//创建结点元素

//创建空链表

//创建结点

//插入节点函数

//编译链表

#include #includestruct node //构思结点元素

{

int num;

char name[20];

int age;

struct node * next;

};

typedef struct node Node;

typedef Node * Link;

void creat_list(Link * head) //创建链表初始化为空表

{

*head = NULL;

}

void is_malloc_ok(Link new_node) //判断创建的结点是否成功分配了空间

{

if(NULL == new_node)

{

printf("malloc error!\n");

exit(-1);

}

}

void creat_new_node(Link * new_node)

{

*new_node = (Link)malloc(sizeof(Node));

is_malloc_ok(*new_node);

}

void display(Link head) //打印链表

{

Link p;

p = head;

if(NULL == head)

{

printf("Link is empty!\n");

return;

}

while(NULL != p)

{

printf("%d\n",p->num);

p = p->next;

}

}

void insert_node_head(Link * head,Link new_node)

{

new_node->next = *head; //通过图2进行理解这里的地址转换

*head = new_node;

}

int main()

{

int i = 0;

Link head = NULL; //初始化避野指针

Link new_node = NULL;

creat_list(&head); //**head是头地址,对于链表创建是对于地址的操作,

因为链表名本身代表链表首地址,因此在调用函数操作传入一个二阶地址,

通过解引用(*head)来操作指针head**

for(i=0; i < 10; i++)

{

creat_new_node(&new_node);

new_node->num = i + 1;

insert_node_head(&head,new_node);

}

display(head);

return 0;

}

0a2412fa0972d208fbaf74c28af688b7.png

写尾插法的代码

void insert_node_tail(Link * head,Link new_node)

{

Link p; //尾插需要找到上一个结点的尾指针,因此需要一个变量定义此指针

p = *head;

if(NULL == *head) //链表为空时先插入第一个结点

{

*head = new_node;

new_node->next = NULL;

}

else //当P不为空时插入第二个结点

{

while(NULL != p->next)

{

p = p->next;

}

p->next = new_node; //尾插让新结点的next都是NULL,头指向上一个结点

new_node->next = NULL;

}

e699517ca50f981089814b738c0bb119.png

**

总结

由上面的例子以及比较,我们可以看见:

1、头插法相对简便,但插入的数据与插入的顺序相反;

2、尾插法操作相对复杂,但插入的数据与插入顺序相同。

**

随机数倒叙排列

int insert_node_mid_sort(Link * head,Link new_node)

{

Link p,q;

p = q = *head;

if(NULL == *head) //空表时将第一个结点插入此时头结点地址不为空

{

*head = new_node;

new_node->next = NULL;

}

else if((*head)->num > new_node->num) //当结点的num>头结点时将他插入到头结点前面

{

new_node->next = *head;

*head = new_node;

}

else

{

while(p != NULL && p->num < new_node->num) //参考下图分析代码 (&&有一个不满足条件时就跳出代码执行下面的代码)

{

q = p;

p = p->next;

}

q->next = new_node;

new_node->next = p;

}

}

7acd8bbc79ed6533a989843927be0f4f.png

a5e421283e14aaa76359facc4de1bb5e.png只需要将6的头插入到5的尾,6的尾巴插入到7的头就可以了。

while(p != NULL && p->num < new_node->num)

因为此时链表中已经有了结点,所以P不是NULL,切有num,此时5<6,

p,q,head,地址相同,此时p指向下一个结点

q = p;

p = p->next;

7718352213ea4941d1c8c633addae9d4.png

q->next = new_node;

new_node->next = p;

此时将new_node的头地址,尾地址互换插入到链表中去,

**

将一个num添加到一个结点的前面

void insert_node_mid_front(Link * head,Link new_node,int loc)

{

Link p,q;

p = q = *head;

if((*head)->num == loc)

{

new_node->next = *head;

*head = new_node;

}

else

{

while(p != NULL && p->num != loc)

{

q = p;

p = p->next;

}

q->next = new_node;

new_node->next = p;

}

} //此代码原理同上只是新建一个loc

#if//在主函数中打印

printf("\n\n");

create_node(&new_node);

new_node->num = 100;

printf("please input loc:\n");

scanf("%d",&loc);

insert_node_mid_front(&head,new_node,loc);

display(head);

fba447fc08d6220a078b3e7b553ef1b6.png

392407aa7bd54ec256cd2e0e5539714d.png

删除结点

void delete_node(Link * head,int num_del)

{

Link p, q;

p = q = *head;

if(NULL == *head)

{

printf("Link is empty!\n");

return;

}

else if((*head)->num == num_del)

{

*head = (*head)->next ;

free(p);

return;

}

else

{

while(NULL != p && p->num != num_del)

{

q = p;

p = p->next;

}

if(NULL == p)

{

printf("no such node!\n");

}

else

{

q->next = p->next;

free(p);

}

}

}

41cc8d616338f47414a6c6b5af9635c1.png259d66c50b6400a7296cd7ed5086261a.png

**

查找结点

Link search_node(Link head,int num_serch)

{

Link p;

p = head;

if(NULL == head)

{

printf("Link is empty!\n");

return NULL;

}

else

{

while( p != NULL && p->num != num_serch)

{

p = p->next;

}

if(NULL == p)

{

return NULL;

}

else

{

return p;

}

}

}

**

对于如何使用1级指针或者2级指针参考博客

待续…

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值