链表解析(C语言)

链式存储:用一组任意的存储单元存储线性表中的数据元素。用这种方法存储的线性表简称线性链表。

存储链表中结点的一组任意的存储单元可以是连续的,也可以是不连续的,甚至是零散的分布在内存中的任意位置上的。

链表中结点的逻辑顺序和物理顺序不一定相同。

非循环单链表

链表

链表的种类很多,有单链表、双链表、循环链表、非循环链表;在此,我们以非循环单链表为例,来讲链表的创建、求长度、排序、插入和排序。

链表包含以下特征:(1).由n个结点离散分配;(2).每个结点通过指针连接(3)每一个结点由一个前驱结点和一个后驱结点(4).首结点没有前驱结点,尾结点没有后驱结点;

满足上面的4条,我们就称为链表;链表既然由很多个结点,那结点又由什么组成?结点由两个部分组成,一是数据域,用来存放有效数据;二是指针域,用来指向下一个结点;下面用C语言来构建链表数据结构,首先应该构造出结点,然后再把所有的结点连起来,就构成了链表;

结点的构造

typedef struct Node
{
​    int data;//数据域,用来存放数据域;
​    struct Node *pNext;//定义一个结构体指针,指向下一次个与当前结点数据类型相同的结点
}NODE,*PNODE; 
//NODE等价于 struct Node; PNODE等价于struct Node *; 此处用大写是为了与变量区分,可以让人容易变出是个数据类型

typedef 只是给数据类型取个别名,即 typedef 数据类型 别名;我们知道struct Node 是我们定义的数据类型;

结点是通过动态分配和释放来实现的,即需要时分配,不需要时释放。实现分别用malloc()和free()函数。

链表的创建

在创建链表之前,我们需要需要了解一下专业术语:

首结点:存放第一个有效数据的结点;

​尾结点:存放最后一个有效数据的结点;

头结点:头结点的数据类型与首结点的数据类型相同,并且头结点是首结点前面的那个结点,并不存放有效数据;头结点的存在只是为了方便链表的操作。

头指针:指向头结点的指针;

尾指针:指向尾结点的指针;

头插法

首先,我们应该创建一个头结点,并用头指针指向它,用C语言描述:用malloc向计算机申请一块内存,并定义一个指向与头结点数据类型相同的指针(一定要判断申请内存是否成功);

然后,要知道要创建链表的长度,用一个循环来每次创建一个结点,并把每个结点连在一起;

创建头结点,定义头指针

head = (LNODE*)malloc(sizeof(LNODE)); //创建头结点
head->next = NULL; 

循环创建结点

p = (LNODE*)malloc(sizeof(LNODE));
p->data = val;
p->next = head->next; 
head->next = p;  //钩链,新创建的结点总是作为首结点
尾插法

创建头结点,定义头指针、尾指针

head = (LNODE*)malloc(sizeof(LNODE));
head->next = NULL;
tail = head;

循环创建结点

p = (LNODE*)malloc(sizeof(LNODE));
p->data = val;
p->next = tail->next;  
tail->next = p;
tail = p;      //钩链,新创建的结点总是作为尾结点

对于单链表,无论是哪种操作,只要涉及到钩链(或重新钩链),如果没有明确给出直接后继,钩链(或重新钩链)的次序必须是“先右后左”。

向链表中插入结点

在这里插入图片描述

假如要在结点2的前面插入结点p,我们首先要找到结点2的前驱结点1,假设现在q指针指向结点1,则

p->next = q->next;
q->next = p;

删除链表中的结点

按序号删除

在这里插入图片描述

假如要删除结点2,只需要把结点1指针域指针指向结点3,但不要忘记释放结点2所占的内存,否则将会造成内存泄漏;首先必须找到结点2的前驱结点1,假设p指向结点1。

q=p->pNext;  //首先用q保存要删除结点的地址;
p->pNext=q->pNext;  //q->pNext=p->pNext->pNext;  修改指针使结点1指向结点3;
free(q); //释放结点2所占的内存;
按元素值删除

假如要删除结点2,使用元素值进行遍历,找到对应的结点,并且同时标记它的前一个结点。然后把结点1指针域指针指向结点3,释放结点2。

while((q != NULL) && (q->data != key))
	p = q;
	q = q->next;
if(q->data == key)
	p->next = q->next;
	free(q);

查找链表中的结点

按序号查找

在这里插入图片描述

获取链表中第i个结点

假如要获取结点2,首先必须找到结点2的前驱结点1,假设p指向结点1。

q = p->next;   //定位到结点2
data = q->data;  //取出结点2中数据元素
按元素值查找

获取链表中某个值对应结点

假如要获取结点2,直接使用元素值进行遍历,直到定位到对应的结点,并返回该结点。

while((p != NULL) && (p->data != key))
	p = p->next;
if(p->data == key)
	return p;

链表元素的排序

快速排序和冒泡排序的思想对于链表这个数据结构同样适用。

for(i=0, p=head->next; i<len-1; i++, p=p->next)
	for(j=i+1, q=p->next; j<len; j++, q=q->next)
		if(p->data > q->data)
			temp = p->data;
			p->data = q->data;
			q->data = temp;

实例:

#include <stdio.h>
#include <stdlib.h>

#define false 0
#define ture 1

typedef int bool;
typedef struct Lnode 
{
​    int data;       //数据域,保存结点的值
​    struct LNODE *next;  //指针域
}LNODE;

//头插法创建单链表,链表的头结点head作为返回值
LNODE *create_HeadLinkList(void)
{
​    int val, i, len;
​    LNODE *head, *p;
​    head = (LNODE*)malloc(sizeof(LNODE)); //创建头结点
​    if(NULL == head)
​    {
​         printf("Memory allocation failure!\n");
​         exit(-1);
​    }
​    head->next = NULL; 
​    printf("please input the length of list: ");
​    scanf("%d", &len);
​    for(i=0; i<len; i++)
​    {
​         p = (LNODE*)malloc(sizeof(LNODE));
​         if(NULL == p)
​         {
​             printf("Memory allocation failure!\n");
​             exit(-1); 
​         }
​         printf("please input the value of node: ");
​         scanf("%d", &val);
​         p->data = val;
​         p->next = head->next; 
​         head->next = p;    //钩链,新创建的结点总是作为首结点
​    }
​    return head;
}

//尾插法创建单链表,链表的头结点head作为返回值
LNODE *create_TailLinkList(void)
{
​    int val, i, len;
​    LNODE *head, *p, *tail;
​    head = (LNODE*)malloc(sizeof(LNODE));
​    if(NULL == head)
​    {
​         printf("Memory allocation failure!\n");
​         exit(-1);
​    }
​    tail = head;
​    head->next = NULL;
​    printf("please input the length of list: ");
​    scanf("%d", &len);
​    for(i=0; i<len; i++)
​    {
​         p = (LNODE*)malloc(sizeof(LNODE));
​         if(NULL == p)
​         {
​             printf("Memory allocation failure!\n");
​             exit(-1); 
​         }
​         printf("please input the value of node: ");
​         scanf("%d", &val);
​         p->data = val;
​         p->next = tail->next;  
​         tail->next = p;
​         tail = p;      //钩链,新创建的结点总是作为尾结点
​    }
​    return head;
}

//按序号查找
int get_pos_data(LNODE *head, int pos) //链表中第pos个有效元素
{
​    int data;
​    int i = 0;
​    LNODE *p, *q;
​    p = head;   //p指向头结点
​    while((NULL != p) && (i < pos-1))
​    {
​         p = p->next;
​         i++;
​    }
​    if(i != pos-1)
​    {
​         printf("list has empty node\n");
​         exit(-1);
​    }
​    q = p->next;
​    data = q->data;
​    return data;
}

//按值查找
LNODE *Locate_Node(LNODE *head, int key)
{
​    LNODE *p = head->next;
​    while((p != NULL) && (p->data != key))
​    {
​         p = p->next;
​    }
​    if(p->data == key)
​    {
​         return p;
​    }
​    else
​    {
​         printf("所要查找的结点不存在!\n");
​         return NULL;
​    }
}

//按序号删除
void Delete_PosLinkList(LNODE *head, int pos)
{
​    int i=0;
​    LNODE *p, *q;
​    p = head;
​    while((p != NULL) && (i < pos-1))
​    {
​         p = p->next;
​         i++;
​    }
​    if((p == NULL) || (i > pos-1))
​    {
​         printf("链表为空或者输入pos不错误\n");
​         exit(-1);
​    }
​    q = p->next;
​    p->next = q->next;
​    free(q);   //释放q指向结点的内存
​    q = NULL;  //千万不要忘记,否则会出现野指针
}

//按值删除
void Delete_KeyLinkList(LNODE *head, int key)
{
​    LNODE *p = head;
​    LNODE *q = head->next;
​    while((q != NULL) && (q->data != key))
​    {
​         p = q;
​         q = q->next;
​    }
​    if(q->data == key)
​    {
​         p->next = q->next;
​         free(q);
​         q = NULL;
​    }
​    else
​    {
​         printf("所要删除的结点不存在\n");
​    }
}

bool Insert_LinkList(LNODE *head, int pos, int val)
{
​    int i=0;
​    LNODE *p = head;
​    while((p != NULL) && (i < pos-1))
​    {
​         p = p->next;
​         i++;
​    }
​    if((p == NULL) && (i > pos-1))
​    {
​         printf("链表为空或者输入pos不错误\n");
​         return false;
​    }
​    LNODE *q = (LNODE*)malloc(sizeof(LNODE));
​    q->data = val;
​    q->next = p->next;
​    p->next = q;
}

void print_LinkList(LNODE *head)
{
​    LNODE *p = head->next;
​    while(p != NULL)
​    {    
​         printf("%d ", p->data);
​         p = p->next;
​    }
​    printf("\n");
}

int Length_LinkList(LNODE *head)
{
​    int len=0;
​    LNODE *p = head->next;
​    while(p != NULL)
​    {
​         len++;
​         p = p->next;
​    }
​    return len;
}

void Sort_LinkList(LNODE *head)
{
​    int i, j, temp;
​    int len = Length_LinkList(head);
​    LNODE *p, *q;
​    for(i=0, p=head->next; i<len-1; i++, p=p->next)
​    {
​         for(j=i+1, q=p->next; j<len; j++, q=q->next)
​         {
​             if(p->data > q->data)
​             {
​                 temp = p->data;
​                 p->data = q->data;
​                 q->data = temp;
​             }
​         }
​    }
}

int main()
{
​    int val;
​    LNODE *p;
​    //LNODE *list = create_HeadLinkList();
​    LNODE *list = create_TailLinkList();
​    print_LinkList(list);
​    val = get_pos_data(list, 3);
​    printf("val:%d\n", val);
​    p = Locate_Node(list, 4);
​    val = p->data;
​    printf("val:%d\n", val);
​    Delete_KeyLinkList(list, 2);
​    Delete_PosLinkList(list, 3);
​    print_LinkList(list);

​    Insert_LinkList(list, 3, 9);
​    print_LinkList(list);

​    Sort_LinkList(list);
​    print_LinkList(list);

​    return 0;
}

输出

please input the length of list: 5
please input the value of node: 1
please input the value of node: 2
please input the value of node: 3
please input the value of node: 4
please input the value of node: 5
1 2 3 4 5
val:3
val:4
1 3 5
1 3 9 5
1 3 5 9

循环链表

循环链表(Circular Linked List):是一种头尾相连接的链表。其特点是最后一个指针域指向链表的头结点,整个链表的指针域链接成一个环。

链表的创建

如果传入的为空链表,创建头结点并使头尾连接

p = (struct Node*)malloc(sizeof(struct Node));
p->next = p;  //头尾相连

如果传入的为非空链表,遍历找到尾结点,插入新结点,使尾结点指向新结点,新结点指向头结点

for(Target=p; Target->next != p; Target=Target->next); 
q = (struct Node*)malloc(sizeof(struct Node));
q->next = p;     //新结点指向头结点
Target->next = q;   //尾结点指向新结点

删除链表中的结点

如果要删除的结点为头结点,遍历找到尾结点,调整头结点的下一个结点为头结点,尾结点指向新的头结点,释放原来的头结点。

for(Target=p; Target->next != p; Target=Target->next);
q = p;      //标记要删除的结点
p = p->next;   //调整第2个结点为头结点
Target->next = p; //尾结点指向头结点
free(q);

如果删除的结点为普通结点,遍历链表,比较删除的位置是否超出了链表长度,如果超出了链表长度。找到尾节点的前一个结点,并将尾节点前一个结点的指针指向头结点,释放删除结点。如果没有超出链表长度,定位到要删除结点的前一个结点,并将要删除结点的前一个结点的指针指向要删除结点的后一个结点,释放要删除结点。

for(i=1, Target=p; (i!=pos-1)&&(Target->next != p); i++, Target=Target->next);

超出链表

if(Target->next == p) 
	for(Target=p; Target->next->next != p; Target=Target->next);
	q = Target->next;  //标记尾结点
	Target->next = p;
	free(q);

没有超出链表

q = Target->next;    //标记要删除的结点 
Target->next = q->next;
free(q);

向链表中插入结点

如果插入的位置为头结点的前面,遍历定位到尾节点,插入结点的指针指向头结点,尾节点的指针指向新插入的结点。

q = (struct Node*)malloc(sizeof(struct Node));
for(Target=p; Target->next != p; Target=Target->next);
q->next = p;    
Target->next = q;
p = q;

如果插入的位置为普通位置,遍历定位到要插入结点的前一个结点,新插入的结点指针指向插入位置(pos)的结点,插入位置的前一个结点(pos-1)的指针指向新插入的结点。

for(i=1, Target=p; (i != pos-1)&&(Target->next != p); i++, Target=Target->next);
q = (struct Node*)malloc(sizeof(struct Node));
q->next = Target->next;
Target->next = q;

实例:

#include <stdio.h>
#include <stdlib.h>

struct Node
{
​    int data;
​    struct Node *next;
};

struct Node *Create_list(struct Node *head)
{
​    int item;
​    struct Node *p = head;
​    struct Node *Target;
​    struct Node *q;

​    while(1)
​    {
​         if(p == NULL)   //空链表,创建头结点并使头尾连接
​         {
​             p = (struct Node*)malloc(sizeof(struct Node));
​             if(p == NULL)
​             {
​                 printf("创建失败!\n");
​                 exit(0);        //0正常退出,非0异常退出
​             }
​             p->next = p;  //头尾相连
​             return p;
​         }
​         else 
​         {   //使用循环找到尾结点,插入新结点,使尾结点指向新结点,新结点指向尾结点
​             for(Target=p; Target->next != p; Target=Target->next); 
​             q = (struct Node*)malloc(sizeof(struct Node));
​             if(q == NULL)
​             {
​                 printf("创建失败!\n");
​                 exit(0);
​             }
​             printf("输入要插入的值!\n");
​             scanf("%d", &item);
​             q->data = item;
​             q->next = p;     //新结点指向头结点
​             Target->next = q;   //尾结点指向新结点
​             return p;
​         }
​    }
}

struct Node *Delete_Node(struct Node *head, int pos)
{
​    struct Node *Target, *q;
​    struct Node *p = head;
​    int i;
​    if(p == NULL)
​    {
​         printf("这是一个空链表!\n");
​         exit(0);
​    }
​    if(pos == 1)     //删除头结点
​    {          //找到尾结点,为尾结点指向头结点铺垫
​         for(Target=p; Target->next != p; Target=Target->next);
​         q = p;      //标记要删除的结点
​         p = p->next;   //调整第2个结点为头结点
​         Target->next = p; //尾结点指向头结点
​         free(q);
​         return p;
​    }
​    else
​    {           //比较pos和链表的大小
​         for(i=1, Target=p; (i!=pos-1)&&(Target->next != p); i++, Target=Target->next);
​         if(Target->next == p)   //删除的位置大于链表长度,即删除尾结点
​         {            //定位到尾结点的前一个结点
​             for(Target=p; Target->next->next != p; Target=Target->next); 
​             q = Target->next;  //标记尾结点
​             Target->next = p;
​             free(q);
​             return p;
​         }
​         else
​         {
​             q = Target->next;    //标记要删除的结点 
​             Target->next = q->next;
​             free(q);
​             return p;
​         }
​    }
}

struct Node *Insert_Node(struct Node *head, int pos)
{
​    struct Node *Target, *q;
​    struct Node *p = head;
​    int i, item;
​    printf("输入要插入的值!\n");
​    scanf("%d", &item);
​    if(pos == 1)   //插入头结点
​    {
​         q = (struct Node*)malloc(sizeof(struct Node));
​         if(q == NULL)
​         {
​             printf("创建失败!\n");
​             exit(0);
​         }         //定位到尾结点
​         for(Target=p; Target->next != p; Target=Target->next);
​         q->data = item;
​         q->next = p;    
​         Target->next = q;
​         p = q;
​         return p;
​    }
​    else
​    {
​         for(i=1, Target=p; (i != pos-1)&&(Target->next != p); i++, Target=Target->next);
​         q = (struct Node*)malloc(sizeof(struct Node));
​         q->data = item;
​         q->next = Target->next;
​         Target->next = q;
​         return p;
​    }
}

int Find_Node(struct Node *head, int key)
{
​    struct Node *p = head->next;
​    int pos=2;
​    while((p->data != key) && (p->next != head))
​    {
​         pos++;
​         p = p->next;
​    }
​    return pos;
}

void Print_list(struct Node *head)
{
​    struct Node *p;
​    for(p=head->next; p!=head; p=p->next)
​    {
​         printf("%d ", p->data);
​    }
​    printf("\n");
}

/* int main()
{
​    struct Node *list = NULL;
​    int n, i;
​    while(n != 0)
​    {
​         printf("单循环链表的基本操作\n");
​         printf("-------------------\n");
​         printf("1.创建一个结点\n");
​         printf("2.插入一个结点\n");
​         printf("3.删除一个结点\n");
​         printf("4.显示链表成员\n");
​         printf("输入数字0,退出\n");
​         printf("请选择操作\n");
​         fflush(stdin);
​         scanf("%d", &n);
​         switch(n)
​         {
​             case 1:
​             {
​                 list = Create_list(list);
​                 break;
​             }
​             case 2:
​             {
​                 printf("插入的位置是:\n");
​                 scanf("%d", &i);
​                 list = Insert_Node(list, i);
​                 break;
​             }
​             case 3:
​             {
​                 printf("删除的位置是:\n");
​                 scanf("%d", &i);
​                 list = Delete_Node(list, i);
​                 break;
​             }
​             case 4:
​             {
​                 Print_list(list);
​                 break;
​             }
​         }
​    }
​    return 0;
} */

int main()
{
​    struct Node *list = NULL;
​    int i;
​    for(i=0; i<5; i++)
​    {
​         list = Create_list(list);
​    }
​    Print_list(list);
​    list = Insert_Node(list, 3);
​    Print_list(list);
​    list = Delete_Node(list, 3);
​    Print_list(list);
​    printf("值为3的结点是:%d\n", Find_Node(list, 3));
​    return 0;
}

输出

输入要插入的值!
1
输入要插入的值!
2
输入要插入的值!
3
输入要插入的值!
4
1 2 3 4
输入要插入的值!
7
1 7 2 3 4
1 2 3 4
值为3的结点是:4

参考
https://blog.csdn.net/lpp0900320123/article/details/20356143?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-4.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-4.no_search_link

https://blog.csdn.net/qq_40667484/article/details/79515514?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-18.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-18.no_search_link

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值