ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

第二十三篇,数据结构(起始篇)—单向链表。

2022-02-28 09:32:26  阅读:111  来源: 互联网

标签:node head struct 第二十三 list 链表 数据结构 节点


一、数据结构学习大纲。
1、数组与链表之间存储区别。
2、链表模型、使用场景。
3、链表种类?
传统链表   --> 单向链表、单向循环链表、双向链表、双向循环链表。
非传统链表 --> 内核链表
4、栈/队列
5、二叉树

无论学习什么数据结构,其实都是在学习该结构的增删改查。

二、回顾学习过一些数据结构  -- 增删改查。
变量: int x = 0;
增:x = 10;
删:x = 0;
改:x = 20
查:printf("%d\n",x);

数组:int x[3] = {0};
增:x[0] = 1 x[1] = 2 x[2] = 3
删:bzero(x,sizeof(x));
改:x[0] = 4 x[1] = 5 x[2] = 6
查:printf("x[0] = %d\n",x[0]);
    printf("x[1] = %d\n",x[1]);
    printf("x[2] = %d\n",x[2]);

三、数组与链表之间存储区别。
1、什么是数组?
1)数组空间在哪里申请?
如果数组是局部变量,那么该数组的空间就会在栈区申请。
如果数组是全局变量,那么该数组的空间就会在数据段申请。

2)数组的空间在内存中是连续的吗?
是。例如:int x[3]  -> 在内存空间上连续申请12个字节,然后使用变量x间接访问这片内存空间。

3)数组在内存特点?
在内存空间中是连续的,所以只需要通过数组名+下标方式就可以访问任意一个成员。
因为下标其实就是数组首元素的地址往上偏移的单位数。

4)数组本身空间特点?
1>. 在定义数组时,必须声明数组的大小。  --> 限制了数组空间操作的大小。
2>. 在给数组元素赋值时,必须要检查好空间大小,不然容易越界访问一些未知区域。

2、什么是链表?
1)链表的空间是在哪里申请?
链表空间都是在堆区申请。  
通过malloc函数申请空间,通过free函数释放空间。

2)链表的空间在内存是连续的吗?
不是连续,链表其实就是把那些零碎的空间组织起来,形成一个结构。

3)链表在内存特点?
由于链表每一个节点空间不是连续的,所以不能通过下标偏移来访问下一个成员。
每一个节点除了存储自身的数据(数据域)之外,还会存储下一个成员的地址(指针域),某一个成员就是通过本身的指针域中的地址来访问下一个成员的。

4)链表本身空间特点?
链表没有固定的长度,只要运行内存足够大,那么链表就可以无限延伸。
如果想在链表中新增新的节点,只需要申请新的空间,然后让最后那个节点存储新节点的地址就可以。
  

四、链表的模型。
1、链表的组成?
一个链表中会有非常多的成员组成,而每一个成员,我们都称之为节点。
头节点:排在第一个位置的节点称之为头节点。
头节点的特点: 数据域无效、指针域有效。

数据域无效  --> 数据域空间是存在的,但是不存储数据。
指针域有效  --> 指针域空间是存在的,也存储数据。

其他节点: 数据域有效,指针域也有效。

2、一个节点由什么组成?
由两部分组成:
数据域: 就是节点中存储自身数据的空间。        --> char/short/int/long/float/double/数组/指针/结构体
指针域: 就是节点中存储下一个节点地址的空间。  --> 指针

结论:数据域与指针域类型是不一样的,所以要描述一个节点,需要通过一个结构体来描述。

3、一个节点既有数据域,也有指针域,该如何描述?
struct list_node{
    /* 数据域 */

    /* 指针域 */
};

   例子1: 假设链表中每一个节点都是存储一个int类型的数据,那么节点如何表示?

struct list_node{
    int data;  //数据域
    struct list_node *next; //指针域
};


   例子2: 假设链表每一个节点都是存储一张图片的名字/路径,那么节点如何表示?

struct list_node{
    char picname[50];
    struct list_node *next; //指针域
}

   
   例子3: 假设链表中每一个节点都是存储一个小孩,那么节点如何表示?

struct list_node{
    char name[50];
    char passwd[50];
    int age;
    char tel[50];
    int state;
    time_t start_login_time;
    float total_login_time;
    time_t start_study_time;
    float total_study_time;
    time_t start_play_time;
    float total_play_time;
    int level;
    int register_flags;
    int login_flags;
    char leave_say[50];
    struct safe qa;
    
    struct list_node *next; //指针域
};

结论:链表中数据域存什么,取决于你,你想存什么,就存什么。
      链表指针域固定这么写的。


五、举例子。
假设有一条链表,每一个节点都是存储int类型的数据,分别依次是10,20,30,请把链表写出来。

1、设计链表中节点
由于每一个节点都是存放一个int类型的数据,所以节点应该表示如下:
struct list_node{
    int data;
    struct list_node *next;
};

2、初始化头节点。
1>. 为头节点申请空间。
2>. 为头节点空间赋值。

3、插入节点
1>. 为新节点申请空间
2>. 为新节点赋值
3>. 寻找最后一个节点
4>. 让最后一个节点指向新节点

----------------------------------------------------------------------------------------

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

struct list_node{
    int data;   //数据域
    struct list_node *next; //指针域    
};

struct list_node *init_list_head()
{
    //1. 为头节点申请空间
    struct list_node *head = malloc(sizeof(struct list_node));
    if(head == NULL)
        printf("malloc head error!\n");
    
    //2. 为头节点空间赋值
    head->next = NULL;
    
    //3. 将头节点的地址返回到主函数,给别的函数使用。
    return head;
}

void list_add_tail(struct list_node *head,int num)
{
    //1. 为新节点申请空间
    struct list_node *new = malloc(sizeof(struct list_node));
    if(new == NULL)
        printf("malloc new error!\n");
    
    //2. 为新节点的数据域与指针域赋值
    new->data = num;
    new->next = NULL;
    
    //3. 寻找链表中最后一个节点
    struct list_node *p = NULL;
    for( p=head ; p->next!=NULL ; p=p->next);
    
    //从循环中出来,p->next一定是NULL
    //说明当前p指向的节点就是链表中最后一个节点
    
    //4. 让最后一个节点的指针域存储着新节点的地址就可以。
    p->next = new;
    
    return;
}

int main(int argc,char *argv[])
{
    //1. 初始化头节点
    struct list_node *head = NULL;
    head = init_list_head();
    
    //2. 插入节点到链表末尾
    list_add_tail(head,10);
    list_add_tail(head,20);
    list_add_tail(head,30);
    list_add_tail(head,40);
    list_add_tail(head,50);
    
    return 0;
}


-----------------------------------------------------------------------


   练习1: 完成 show_list_node(???) 函数接口

void show_list_node(struct list_node *head)
{
    struct list_node *p = NULL;
    for(p=head->next;p!=NULL;p=p->next)
    {
        printf("p->data:%d\n",p->data);
    }
    
    return;
}

  练习2: 完成链表 list_add_head() 头插功能。

list_add_tail()  -> 尾插:在链表的末尾新增一个节点。
list_add_head()  -> 头插:在链表头节点之后插入新节点。


void list_add_head(struct list_node *head,int num)
{
    //1. 为新节点申请空间
    struct list_node *new = malloc(sizeof(struct list_node));
    if(new == NULL)
        printf("malloc new error!\n");
    
    //2. 为新节点赋值。
    new->data = num;
    new->next = head->next;
    head->next = new;
    
    return;
}

  练习3: 完成链表 delete_list_node()  删除功能。

----------------------------最终代码--------------------------
#include <stdlib.h>
#include <stdio.h>

struct list_node{
    int data;   //数据域
    struct list_node *next; //指针域    
};

struct list_node *init_list_head()
{
    //1. 为头节点申请空间
    struct list_node *head = malloc(sizeof(struct list_node));
    if(head == NULL)
        printf("malloc head error!\n");
    
    //2. 为头节点空间赋值
    head->next = NULL;
    
    //3. 将头节点的地址返回到主函数,给别的函数使用。
    return head;
}

void list_add_tail(struct list_node *head,int num)
{
    //1. 为新节点申请空间
    struct list_node *new = malloc(sizeof(struct list_node));
    if(new == NULL)
        printf("malloc new error!\n");
    
    //2. 为新节点的数据域与指针域赋值
    new->data = num;
    new->next = NULL;
    
    //3. 寻找链表中最后一个节点
    struct list_node *p = NULL;
    for( p=head ; p->next!=NULL ; p=p->next);
    
    //从循环中出来,p->next一定是NULL
    //说明当前p指向的节点就是链表中最后一个节点
    
    //4. 让最后一个节点的指针域存储着新节点的地址就可以。
    p->next = new;
    
    return;
}

void show_list_node(struct list_node *head)
{
    struct list_node *p = NULL;
    for(p=head->next;p!=NULL;p=p->next)
    {
        printf("p->data:%d\n",p->data);
    }
    
    return;
}

void list_add_head(struct list_node *head,int num)
{
    //1. 为新节点申请空间
    struct list_node *new = malloc(sizeof(struct list_node));
    if(new == NULL)
        printf("malloc new error!\n");
    
    //2. 为新节点赋值。
    new->data = num;
    new->next = head->next;
    head->next = new;
    
    return;
}

int search_list_node(struct list_node *head,int num)
{
    struct list_node *p = NULL;
    for(p=head->next;p!=NULL;p=p->next)
    {
        if(p->data == num)
        {
            printf("search node:%d\n",p->data);
            return 0;
        }
    }
    return -1;
}

int delete_list_node(struct list_node *head,int num)
{
    struct list_node *p = NULL;
    struct list_node *q = NULL;
    
    for(q=head,p=head->next;p!=NULL;q=p,p=p->next)
    {
        if(p->data == num)
        {
            q->next = p->next;
            free(p);
            return 0;
        }
    }
    
    return -1;
}

void delete_list(struct list_node *head)
{
    struct list_node *p = NULL;
    struct list_node *q = NULL;
    for(p=q=head;p!=NULL;p=q)
    {
        q = p->next;
        free(p);
    }
}

int main(int argc,char *argv[])
{
    //1. 初始化头节点
    struct list_node *head = NULL;
    head = init_list_head();
    
    //2. 插入节点到链表末尾
    list_add_tail(head,10);
    list_add_tail(head,20);
    list_add_tail(head,30);
    
    //3. 遍历链表,将链表中每一个节点数据域都打印出来。
    show_list_node(head);  //10 20 30
    
    //4. 头插一些节点
    list_add_head(head,8);
    list_add_head(head,5);
    list_add_head(head,3);
    
    show_list_node(head);  //3 5 8 10 20 30
    
    //5. 根据特征值查找某一个值。
    search_list_node(head,20);
    
    delete_list_node(head,10);
    
    show_list_node(head);  //3 5 8 20 30
    
    //6. 删除整条链表。
    delete_list(head);
    
    return 0;
}


 

标签:node,head,struct,第二十三,list,链表,数据结构,节点
来源: https://blog.csdn.net/weixin_44651073/article/details/123174841

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有