【LeetCode-算法入门】结构体和链表

1. 结构体

C语言中, 数组 是一个存储 相同类型 数据项的变量;而 结构体 可以存储 不同类型 的数据项。

(a) 定义
struct 结构体名 { 
    类型名1 成员名1;
    类型名2 成员名2;
    类型名3 成员名3; 
    ...
}结构体变量;
struct 结构体名 { 
    类型名1 成员名1;
    类型名2 成员名2;
    类型名3 成员名3; 
    ...
};
struct 结构体名 结构体变量
struct  { 
    类型名1 成员名1;
    类型名2 成员名2;
    类型名3 成员名3; 
    ...
}结构体变量;
  • struct 是关键字,是结构体类型的标志

  • 结构体名 ,相当于 int , 只是表明一个类型

  • 结构体变量 ,相当于真正的变量

(b) 注意点
  1. 不允许对结构体本身递归定义:line3

    1 struct Student {
    2     int age;
    3     struct Student stu;
    4 };
    
  2. 结构体内可含其他结构体

     1 struct Date {
     2     int year;
     3     int month;
     4     int day;
     5 };
     6 
     7 struct Student {
     8     char *name;
     9     struct Date birthday;
    10 };
    
  3. 定义结构体类型只说明了该类型的组成情况,并没有给它分配存储空间(就和系统不为int类型本身分配空间一样),只有当定义结构体变量时,才会跟配存储空间

    1 struct Student {
    2     char *name;
    3     int age;
    4 };
    5 
    6 struct Student stu;
    

    line1-4没有给分配存储空间;line6定义 结构体变量 stu 时才分配了存储空间。

  4. 结构体变量占用的内存空间是其成员所占内存之和,且个成员在内存中按定义的顺序依次排列

    1 struct Student {
    2     char *name; // 姓名
    3     int age; // 年龄
    4     float height; // 身高
    5 };
    

    在16位的环境下,一个 s t u d e n t student student 变量共占用内存: 2 + 2 + 4 = 8 2+2+4=8 2+2+4=8 字节

© 结构体初始化

将个成员的初始化值,按顺序放在一对花括号内,并用逗号分隔,一一对应赋值:

1 struct Student {
2     char *name;
3     int age;
4 };
5 
6 struct Student stu = {"MJ", 27};

只能在定义变量的同时进行初始化,初始化和定义变量不可以分开。

(d) 结构体的使用
  1. 一般以成员为单位进行,格式:结构体变量名 . 成员名。line9对结构体的成员 a g e age age 进行了赋值。

    1 struct Student {
    2     char *name;
    3     int age;
    4 };
    5 
    6 struct Student stu;
    7 
    8 // 访问stu的age成员
    9 stu.age = 27;
    

    "."称为成员运算符,它在所有运算符中优先级最高。

  2. 如果结构体里的成员也是结构体变量,那么可以连续使用成员运算符“.” 访问最低一级的成员。

     1 struct Date {
     2      int year;
     3      int month;
     4      int day;
     5 };
     6 
     7 struct Student {
     8     char *name;
     9     struct Date birthday;
    10 };
    11 
    12 struct Student stu;
    13 
    14 stu.birthday.year = 1986;
    15 stu.birthday.month = 9;
    16 stu.birthday.day = 10;
    
  3. 相同类型的结构体变量之间可以整体赋值

     1 struct Student {
     2     char *name;
     3     int age;
     4 };
     5 
     6 struct Student stu1 = {"MJ", 27};
     7 
     8 // 将stu1直接赋值给stu2
     9 struct Student stu2 = stu1;
    10 
    11 printf("age is %d", stu2.age);
    输出:age is 27
    
(e) 结构体数组
  1. 定义:定义一个变量名为stu的结构体数组,数组元素个数为5

    struct Student {
        char *name;
        int age;
    };
    struct Student stu[5]; //定义1
    
    struct Student {
        char *name;
        int age;
    } stu[5]; //定义2
    
    typedef struct Student {
        char *name;
        int age;
    } Node; //定义2
    Node stu[5];
    
    struct {
        char *name;
        int age;
    } stu[5]; //定义3
    
  2. 初始化

    struct {
        char *name;
        int age;
    } stu[2] = { {"MJ", 27}, {"JJ", 30} };
    

    也可以用数组下标访问每一个结构体元素,跟普通数组的用法是一样的

(f) 结构体作为函数参数

结构体变量作为函数参数进行传递时,传递的是全部成员的值,即将实参中成员的值赋给对应的形参成员,所以形参的改变不会影响实参。

 1 #include <stdio.h>
 2 
 3 // 定义一个结构体
 4 struct Student {
 5     int age;
 6 };
 7 
 8 void test(struct Student stu) {
 9     printf("修改前的形参:%d \n", stu.age);
10     // 修改实参中的age
11     stu.age = 10;
13     printf("修改后的形参:%d \n", stu.age);
14 }
15 
16 int main(int argc, const char * argv[]) {
18     struct Student stu = {30};
19     printf("修改前的实参:%d \n", stu.age);
21     // 调用test函数
22     test(stu);
25     printf("修改后的实参:%d \n", stu.age);
26     return 0;
27 }
img
(g) 指向结构体的指针
  • 因为每个结构体变量都有自己的存储空间和地址,所以指针也可以指向结构体变量
  • 结构体指针变量的形式:struct 结构体名称 *指针变量名
  • 访问结构体成员的方式:
    • 结构体变量名. 成员名
    • (*指针变量名) . 成员名
    • 指针变量名 -> 成员名
 1 #include <stdio.h>
 2 
 3 int main(int argc, const char * argv[]) {
 4     // 定义一个结构体类型
 5     struct Student {
 6         char *name;
 7         int age;
 8     };
 9     
10     // 定义一个结构体变量
11     struct Student stu = {"MJ", 27};
12     
13     // 定义一个指向结构体的指针变量
14     struct Student *p;
15     
16     // 指向结构体变量stu
17     p = &stu;
18 
19     /*
20      这时候可以用3种方式访问结构体的成员
21      */
22     // 方式1:结构体变量名.成员名
23     printf("name=%s, age = %d \n", stu.name, stu.age);
24     
25     // 方式2:(*指针变量名).成员名
26     printf("name=%s, age = %d \n", (*p).name, (*p).age);
27     
28     // 方式3:指针变量名->成员名
29     printf("name=%s, age = %d \n", p->name, p->age);
30     
31     return 0;
32 }
img

2. 链表

  • 链表是一种常见的数据结构, 结构体指针 在这里得到了充分的利用。

  • 链表可以动态的进行存储分配,即,链表是一个功能强大的 数组 ,它可以在节点中定义多个数据类型;也可根据需要添加、删除、插入节点。

  • 链表有一个 头指针 ,一般用 head 表示,用于存放地址。

  • 链表中的节点分为 两类 :

    • 链表中的节点有两个部分:数据域+指针域
    • 头节点:没有数据域
    • 一般节点:指针域+数据域
  • 链表像一条锁链:head指向第一个元素 → \to 第一个元素指向第二个元素 → \to …… → \to 最后一个元素结束,称为 表尾 ,其指针域存放 “NULL” 表示时空地址。

  • 应用:链表的创建,修改,删除,插入,输出,排序,反序,清空链表的元素,求链表的长度等等。

(a) 创建链表
typedef struct student{
    int score;
    struct student *next;
} LinkList;(注意分号)
(b) 初始化链表
LinkList *creat(int n){
    LinkList *head, *node, *end;//定义头节点,普通节点,尾部节点;
    head = (LinkList*)malloc(sizeof(LinkList));//分配地址
    end = head;         //若是空链表则头尾节点一样
    for (int i = 0; i < n; i++) {
        node = (LinkList*)malloc(sizeof(LinkList));
        scanf("%d", &node->score);
        end->next = node;
        end = node;
    }
    end->next = NULL;//结束创建
    return head;
}

分析一下上面的过程吼:

  1. 先定义三个结构体指针: h e a d head head 作为头节点; n o d e node node 作为一般节点; e n d end end​ 作为尾节点。

  2. 先给头节点 h e a d head head 分配地址空间,大小为结构体的大小;

  3. 首先让尾节点 e n d end end​ 指向头节点 h e a d head head​​ ,此时链表为空链表(此时 h e a d head head​ 和 e n d end end​ 是指向的同一个结构体);

  4. 其次通过遍历,得到 n n n​​​ 个普通节点:首先给 n o d e node node​​​ 分配空间,将输入的值赋给 n o d e node node​​​ 的 s c o r e score score​​​ ,然后将 e n d end end​​​ (此时为头节点 h e a d head head )中的 n e x t next next 结构体赋为前面定义好的 n o d e node node ;最后让 e n d end end 指向 n o d e node node​​ 结构体,重复。【本质上就是 e n d end end 的滑动,指来指去】

  5. 最后将 e n d end end​ 中的结构体指向 NULL ,结束链表的创建。

© 修改链表节点值
void change(LinkList *list,int n) {//n为第n个节点
    LinkList *t = list;
    int i = 0;
    while (i < n && t != NULL) {
        t = t->next;
        i++;
    }
    if (t != NULL) {
        puts("输入要修改的值");
        scanf("%d", &t->score);
    }
    else {
        puts("节点不存在");
    }
}

分析一下上面的过程吼:

  1. 先定义一个结构体指针 t t t ,指向结构体指针 l i s t list list​​ 。
  2. 然后通过while先遍历到需要改的节点(t != NULL 为了防止要改的节点超出范围/尾节点)
(d) 删除链表节点

在这里插入图片描述

删除节点就是将 前一个节点的指针域 要越过删除的节点,指向下一个节点。即:p->next = q->next,释放节点q的空间,free(q)

void delet(LinkList *list, int n) {//删除节点n
    LinkList *t = list, *in;
    int i = 0;
    while (i < n && t != NULL) {//遍历到要删除的点前一个节点
        in = t;//要删除的前一个节点
        t = t->next;//要删除的节点
        i++;
    }
    if (t != NULL) {
        in->next = t->next; 
        //让指向前一个节点的next结构体的in,指向下一个节点的next结构体
        
        free(t);//释放要删除的节点的空间
    }
    else {
        puts("节点不存在");
    }
}
(e) 插入链表节点

在这里插入图片描述

插入节点就是用 插入前节点指针域 链接上插入节点的 数据域 ,再把 插入节点指针域 链接上 插入后节点数据域 ,此时这个数据域存在前节点的指针域中。根据图,插入节点也就是:e->next = head->next ; head->next = e。

void insert(LinkList *list, int n) {
    LinkList *t = list, *in;
    int i = 0;
    while (i < n && t != NULL) {
        t = t->next;
        i++;
    }
    if (t != NULL) {
        in = (LinkList*)malloc(sizeof(LinkList));
        puts("输入要插入的值");
        scanf("%d", &in->score);
        in->next = t->next;//填充in节点的指针域,也就是说把in的指针域指向t的下一个节点
        t->next = in;//填充t节点的指针域,把t的指针域重新指向in
    }
    else {
        puts("节点不存在");
    }
}
(f) 输出链表

遍历输出:

while (h->next != NULL) {
        h = h->next;
        printf("%d  ", h->score);
}

Reference

  1. https://www.runoob.com/cprogramming/c-structures.html
  2. https://www.cnblogs.com/mjios/archive/2013/03/24/2977910.html
  3. https://www.cnblogs.com/ariel-dreamland/p/10469696.html
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值