这篇文章主要介绍了c#中单链表有什么用,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。
不带头节点链表
单向链表是链表的一种。单向链表由一系列内存不连续的节点组成,每个节点都包含指向值的域和指向下个节点的next指针。最后一个节点的next域为NULL值,代表链表结束。
链表示意图如下:
一,结构体
1,结构体定义:
struct LinkNode
{
void *x;
struct LinkNode *next;
};
2,结构体大小:
1)取结构体大小方法:sizeof(struct LinkNode);在64位系统取出结构体大小为:sizeof(struct LinkNode) = 16;
2)为什么是16字节:因为本结构体包含的两个指针成员x和next。指针是存放地址的,故在同一系统平台下,指针变量取的sizeof大小都是一致。即对于指针变量的大小并不取决于指针类型,而取决于系统平台。32位=4字节,64位=8字节。故有在64位系统中sizeof(void *) == sizeof(struct LinkNode *) == 8。在32位系统中,sizeof(void *) == sizeof(struct LinkNode *) == 4。
3)若假设节点ABCD是内存连续的且A作为内存其起始点,即A的地址为0x00,那么此时B,C,D的地址就依次为:0x10,0x20,0x30。且A的next指针指向的是节点B(A->next = &B),依次类推,直到节点D的next存的是结束符NULL。
二,链表创建
1,链表创建一(使用一级指针)
程序说明:
1)程序调用方式szyu_link_create1("AA", "BB", NULL),NULL用于正确终止for循环。
2)创建新增节点node。
3)如果链表为空,则先把新增节点(node)赋值给head,并让last指向最新的head;如果链表不为空,则把尾节点的next指向最新节点,并重新赋值尾节点即可。
struct LinkNode
*szyu_link_create1(void *x, ...)
{
struct LinkNode *head = NULL;
struct LinkNode *last = head;
va_list args;
va_start(args, x);
for ( ; x ; x = va_arg(args, void *) )
{
struct LinkNode *node = NULL;
node = (struct LinkNode *)malloc(sizeof(struct LinkNode));
if ( node == NULL )
{
return head;
}
node->x = x;
node->next = NULL;
if ( head == NULL )
{
head = node;
last = head;
}
else
{
last->next = node;
last = node;
}
}
va_end(args);
return head;
}
2,链表创建二(使用二级指针)
程序说明:
1)二级指针node存在如下成立等式:*node == head,**node = *head;
2)for刚开始为*node(*node = head)创建空间,既创建头节点。
3)为(*node)节点的x赋值,并让指针node指向新节点的next,即赋值后*node即为前node的next域值。
4)第二次for开始,为*node赋值就是给(*node)->next赋值。
5)重复步骤3,4直到NULL传入为止。
6)最后一个节点把node指向next后并未对其进行赋NULL值处理。故在for循环结束要为*node赋值NULL。代表链表结束。
struct LinkNode
*szyu_link_create2(void *x, ...)
{
struct LinkNode *head = NULL;
struct LinkNode **node = &head;
va_list args;
va_start(args, x);
for ( ; x; x = va_arg(args, void *) )
{
(*node) = (struct LinkNode *)malloc(sizeof(struct LinkNode));
if ( (*node) == NULL )
{
return head;
}
(*node)->x = x;
node = &(*node)->next;
}
*node = NULL;
va_end(args);
return head;
}
3,一级指针和二级指针对比
一级指针
1)优点:程序通俗易懂。
2)缺点:插入头节点和插入其他位置方式不同,代码风格不统一。
二级指针
1)优点:无论插入什么位置,代码风格高度统一。代码精简。
2)缺点:技巧性较高。
4,设计思路
二级指针的使用场景就是“间接改变一级指针所指向的内存地址”。而改变的一级指针指向的即为next指针。除头节点外的所有节点新增地址都存在上一节点的next中,固有设计node指向next,为*node赋值时即为next赋值。
三,链表插入
1,链表插入一(使用一级指针)
程序说明:
1)key的判断保证输入位置不能小于1,并可以在链表最后面插入新节点。
2)初始化新增节点。
3)int cnt = 2;在key = 1时,是要重新插入第一个节点;key = 2 时插入的前一个节点即为第一节点。所以在key <= 2时是不需要进行链表移动,故cnt = 2开始执行。
4)由于不带头节点,故在插入时,插入在头节点位置和后面位置需要区分考虑。
struct LinkNode
*szyu_link_insert1(struct LinkNode *head, void *x, int key)
{
if ( head == NULL )
{
return head;
}
int len = szyu_link_length2(head);
if ( key < 1 || key - 1 > len )
{
return head;
}
struct LinkNode *node = NULL;
node = (struct LinkNode *)malloc(sizeof(struct LinkNode));
if ( node == NULL )
{
return head;
}
node->x = x;
node->next = NULL;
struct LinkNode *list = head;
int cnt = 2;
for ( ; cnt < key; cnt++ )
{
list = list->next;
}
if ( key == 1 )
{
node->next = head;
head = node;
}
else
{
node->next = list->next;
list->next = node;
}
return head;
}
2,链表插入二(使用二级指针)
程序说明:
1)key - 1是保证在链表后面插入节点正常。
2)由于使用二级指针故cnt初始化为1即可,无须单独考虑插入头节点情况。
struct LinkNode
*szyu_link_insert2(struct LinkNode *head, void *x, int key)
{
if ( head == NULL )
{
return head;
}
int len = szyu_link_length2(head);
if ( key < 1 || key - 1 > len )
{
return head;
}
struct LinkNode **list = &head;
int cnt = 1;
while ( cnt < key )
{
list = &(*list)->next;
cnt += 1;
}
struct LinkNode *remain = *list;
*list = (struct LinkNode *)malloc(sizeof(struct LinkNode));
if ( (*list) == NULL )
{
return head;
}
(*list)->x = x;
(*list)->next = remain;
return head;
}
3,使用一级指针和二级指针优缺点同链表创建
4,使用二级指针设计思路
1)首先使二级指针list指向一级指针head。
2)循环使用链表停留在插入位置的前一节点上。此时若不是头节点位置,list指向的是插入位置前一节点的next,插入时,只需为*list赋值新节点地址即可。若是头节点,则*list指向头节点。
3)无论是否为头节点,把剩下链表赋值给remain(若插入为头节点,此时remain保留的事整个链表;若插入在最后,则remain为NULL)。
4)为*list赋值新节点地址。并把remain追加到*list节点后面即可。
四,链表节点删除
1,链表节点删除(一级指针)
程序说明:
1)如果头节点为要删除节点,只需要把head指向head->next即可。
2)由于delete停留在要删除节点的前一节点,故只需要把delete->next赋值给remain,在free即可。
struct LinkNode
*szyu_link_delete1(struct LinkNode *head, void *x)
{
if ( head == NULL )
{
return NULL;
}
struct LinkNode *delete = head;
struct LinkNode *remain = NULL;
if ( strcmp((char *)delete->x, (char *)x) == 0 )
{
remain = delete;
head = delete->next;
}
else
{
while ( delete->next != NULL && strcmp((char *)delete->next->x, (char *)x) != 0)
{
delete = delete->next;
}
remain = delete->next;
delete->next = delete->next->next;
}
free(remain);
return head;
}
2,链表节点删除(二级指针)
程序说明
1)若为头节点时,*list = head,head = head->next即可,即为*list = (*list)->next。
2)若不为头节点,list是指向next的指针,此时*list即为下个节点,若要删除下个节点,为*list重新赋值即可改变下个节点指向。
struct LinkNode
*szyu_link_delete2(struct LinkNode *head, void *x)
{
if ( head == NULL )
{
return head;
}
struct LinkNode **list = &head;
while ( (*list) != NULL && strcmp((char *)(*list)->x, (char *)x) != 0 )
{
list = &(*list)->next;
}
struct LinkNode *remain = *list;
(*list) = (*list)->next;
free(remain);
return head;
}
五,链表释放
程序说明:
1)使用二级指针后的*head即为删除节点。
void
szyu_link_free2(struct LinkNode **head)
{
if ( *head == NULL || head == NULL )
{
return;
}
struct LinkNode *next = NULL;
for ( ; *head; *head = next )
{
next = (*head)->next;
free(*head);
}
}
六,链表长度获取
程序说明:
1)由于链表不带头节点,故直接赋值head链表。
int
szyu_link_length2(struct LinkNode *head)
{
if ( head == NULL )
{
return 0;
}
struct LinkNode *pLen = head;
int len = 0;
for ( ; pLen != NULL; pLen = pLen->next )
{
len += 1;
}
return len;
}
七,链表打印
void
szyu_link_print1(struct LinkNode *head)
{
if ( head == NULL )
{
return;
}
struct LinkNode *print = head;
for ( ; print != NULL; print = print->next )
{
printf("%s ", (char *)print->x);
}
printf("\n");
}
感谢你能够认真阅读完这篇文章,希望小编分享的“c#中单链表有什么用”这篇文章对大家有帮助,同时也希望大家多多支持亿速云,关注亿速云行业资讯频道,更多相关知识等着你来学习!
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。