一线教师吐血奉献【链表总结】

1、链表的定义
链表由一个个节点构成,每个节点一般由结构体形式组织:
typedef struct student
{
int num;
char name[20];
struct student* next;            
}STU


[师说]一个存储数据元素的数据域,存储下一个节点地址的指针域
存储链表的数据是离散的
data表示数据,其可以是简单的类型(如int,double等等),也可以是复杂的结构体(struct类型)
next表示指针,它永远指向自身的下一个结点,对于只有一个结点的存在,这个next指针则永远指向自身,对于一个链表的尾部结点,next永远指向开头。


2、简单链表的创建
#include<stdio.h>
struct student
{long num;
float score;
struct student *next;
};
main()
{
struct student a,b,c,*head,*p;

a.num=10101;a.score=89.5;
b.num=10103;b.score=90;
c.num=10107;c.score=85;/*对结点的num和score成员赋值*/
head=&a;/*将结点a的起始地址赋给头指针head */
a.next=&b;/*将结点b的起始地址赋给a结点的next成员*//*将结点c的起始地址赋给b结点的next成员*//*c结点的next成员不存放其他结点地址*//*使p指针指向a结点*/
b.next=&c;
c.next=NULL;
p=head;
do{
 printf("%ld %5.1f\n",p->num,p->score); /*输出p指向的结点的数据*/
 
 p=p->next; /*使p指向下一结点*//*输出完c结点后p的值为NULL*/
 }while(p!=NULL); /*输出完c结点后p的值为NULL*/
}



#include <stdio.h>

struct student {
    long num;
    float score;
    struct student *next;
};

int main() {
    struct student a, b, c, *head;

    a.num = 10101;
    a.score = 89.5;
    b.num = 10103;
    b.score = 90;
    c.num = 10107;
    c.score = 85;  // 对结点的 num 和 score 成员赋值

    head = &a;     // 将结点 a 的起始地址赋给头指针 head
    a.next = &b;   // 将结点 b 的起始地址赋给 a 结点的 next 成员
    b.next = &c;   // 将结点 c 的起始地址赋给 b 结点的 next 成员
    c.next = NULL; // c 结点的 next 成员不存放其他结点地址

    // 输出链表中的每个结点
    while (head != NULL) {
        printf("%ld %5.1f\n", head->num, head->score);  // 输出 head 指向的结点数据
        head = head->next;  // 使 head 指向下一个结点,会丧失掉head这个节点的起始位置
    }

    return 0;
}
【师说】
没有头指针 head 行不行?
- 理论上可以不用 head,但使用 head 有几个重要优点: a. 它提供了一个固定的入口点来访问整个链表。 b. 使得在链表开头添加或删除元素变得更容易。 c. 使代码更清晰,表明链表的起始位置。
- 如果没有 head,我们就需要直接使用 &a 来开始遍历,这样会使代码less flexible不够灵活。
p起什么作用?没有它行不行?
- p 是一个用于遍历链表的临时指针。
- 它的作用是: a. 指向当前正在访问的结点。 b. 通过 p = p->next 移动到下一个结点。
- 没有 p 也可以完成遍历,但会使代码复杂化:
- 我们可以直接使用 head,但这样会改变 head 的值,失去了链表的起始点。
- 或者每次都使用 head->next->next 这样的形式,但这样代码会变得很难读和维护。
本例是比较简单的,所有结点都是在程序中定义的,不是临时开辟的,也不能用完后释放,这种链表称为“静态链表”。
3、链表的创建,增、删、改,查
3.1 尾插和头插
节点定义要牢靠,数据指针记得清; 头尾指针初始化,新添节点malloc行; 头插尾插各有法,巧妙连接灵活应; 遍历全程打印验,释放内存莫忘记。
3.1.1 无头节点的尾插法链表
#include<stdio.h>
#include<stdlib.h>
typedef struct Node{
    
    int data;
    Node *next;
    
}Node;


int main()
{
    int x;
    Node *p,*head=NULL,*r=NULL;
    for (x=1;x <= 3;x++) {
      p = (Node *) malloc(sizeof( Node ));
          p->data = x;      
      p->next = NULL;
        if (head==NULL){
                    head = p;     // 使head指向第一个节点
            r = p;       // r 也指向第一个节点
        }
                else {
             r->next = p;  // 将上一个节点的next指向当前新节点
             r = p;     // 更新r,使其始终指向最后一个节点
        }
      }     

p = head;   //为了让head总指向链表头,不发生变化
while (p!=NULL){
        printf("%d ",p->data);
          p=p->next;
}
}


[师说]一定要连接起来

3.1.2. 有头节点的尾插法
#include<stdio.h>
#include<stdlib.h>
typedef struct Node{
int data;
Node *next;

}Node;
int main()
{
int x;
Node *p;
// 头节点通常初始化,不存储实际数据
Node *head = (Node *) malloc(sizeof(Node));
head->next = NULL;
Node *r = head;  // r 开始时指向头节点
for (x = 1; x <= 3; x++) {
p = (Node *) malloc(sizeof(Node));
p->data = x;
p->next = NULL;
r->next = p;  // 始终在尾部插入新节点
r = p;        // 更新尾指针
}
p = head->next;   //为了让head总指向链表下一个指针,不发生变化
while (p!=NULL){
printf("%d ",p->data);
p=p->next;
}
}
3.1.3 无头节点的头插法链表
#include<stdio.h>
#include<stdlib.h>
typedef struct Node{
    
    int data;
    Node *next;
    
}Node;


int main()
{
    int x;
    Node *p,*head=NULL,
    for (x=1;x <= 3;x++) {
      p = (Node *) malloc(sizeof( Node ));
          p->data = x;      
          p->next = head;
          head=p; 
    }

p = head;   //为了让head总指向链表头,不发生变化
while (p!=NULL){
        printf("%d ",p->data);
          p=p->next;
}
}
3.1.4 带头节点的头插法链表
#include<stdio.h>
#include<stdlib.h>
typedef struct Node{
    
    int data;
    Node *next;
    
}Node;


int main()
{
    int x; Node *p;
    Node *head = (Node *)malloc(sizeof(Node)); // 创建一个虚拟头节点
    head->next = NULL; // 初始化虚拟头节点的next指针
    for (x=1;x <= 3;x++) {
      p = (Node *) malloc(sizeof( Node ));
          p->data = x;      
          p->next = head->next;
          head->next=p; 
    }

p = head->next    ;   //为了让head总指向链表头,不发生变化
while (p!=NULL){
        printf("%d ",p->data);
          p=p->next;
}
}
3.2 插入节点 无论是头节点、尾节点或者中间节点 (有无头结点都包括)
#include<stdio.h>
#include<stdlib.h>
typedef struct Node{
int data;
Node *next;

}Node;
int main()
{
int x,i,e;
Node *p,*head=NULL,*r=NULL;
for (x=1;x <= 3;x++) {
p = (Node *) malloc(sizeof( Node ));
p->data = x;
p->next = NULL;
if (head==NULL){
head = p;     // 使head指向第一个节点
r = p;       // r 也指向第一个节点
}
else {
r->next = p;  // 将上一个节点的next指向当前新节点
r = p;     // 更新r,使其始终指向最后一个节点
}
}
printf("请输入要插入的位置和元素:");
scanf("%d %d", &i, &e);

Node *pTemp = (Node *)malloc(sizeof(Node));
pTemp->data = e;


if (i == 1) { // 插入第1个结点的操作  //带头结点不包括



    pTemp->next = head;

    head = pTemp;

  
}else{


    p = head;
    int j = 1; //有头结点的int j = 0;  // 从头结点开始计数
    while (p != NULL && j < i - 1) { // 循环找到要插入位置的前驱结点
        p = p->next;
        j++;
    }
    if (p == NULL) {
        printf("插入位置无效\n");
        free(pTemp);
        return 1;
    }
    pTemp->next = p->next;
    p->next = pTemp;
}

p = head;   //为了让head总指向链表头,不发生变化
while (p!=NULL){
printf("%d ",p->data);
p=p->next;
}
}
- 不带头结点:位置从1开始计数,1表示第一个实际数据节点。
- 带头结点:位置从0开始计数,0表示头结点,1表示第一个实际数据节点。
3.3 删除
3.3.1 不带头结点的删除
#include <stdio.h>
#include <stdlib.h>

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

int main() {
    int x, i, e, delPos;
    Node *p, *head = NULL, *r = NULL;

    // 构建初始链表
    for (x = 1; x <= 3; x++) {
        p = (Node *)malloc(sizeof(Node));
        p->data = x;
        p->next = NULL;
        if (head == NULL) {
            head = p;  // 使head指向第一个节点
            r = p;     // r 也指向第一个节点
        } else {
            r->next = p;  // 将上一个节点的next指向当前新节点
            r = p;        // 更新r,使其始终指向最后一个节点
        }
    }

    // 输入插入位置和要插入的值
    printf("请输入要插入的位置和元素:");
    scanf("%d %d", &i, &e);

    // 创建新的节点
    Node *pTemp = (Node *)malloc(sizeof(Node));
    if (pTemp == NULL) {
        printf("内存分配失败\n");
        return 1;
    }
    pTemp->data = e;

    if (i == 1) { // 插入到链表头部的情况
        pTemp->next = head;
        head = pTemp;
    } else {
        p = head;
        int j = 1;
        while (p != NULL && j < i - 1) { // 循环找到要插入位置的前驱结点
            p = p->next;
            j++;
        }
        if (p == NULL) {
            printf("插入位置无效\n");
            free(pTemp);
            return 1;
        }
        pTemp->next = p->next;
        p->next = pTemp;
    }

    // 打印链表
    printf("插入之后的链表: ");
    p = head;
    while (p != NULL) {
        printf("%d ", p->data);
        p = p->next;
    }
    printf("\n");

    // 删除节点
    printf("请输入要删除的位置:");
    scanf("%d", &delPos);

    if (delPos == 1) { // 删除第一个结点
        p = head;
        head = p->next;
        free(p);
    } else {
        p = head;
        int j = 1;
        while (p != NULL && j < delPos - 1) { // 找到待删除位序的前一位结点
            p = p->next;
            j++;
        }
        if (p == NULL || p->next == NULL) {
            printf("删除位置无效\n");
            // 注意:这里应添加部分代码,以便在输入位置无效时,不继续执行下面的代码
            return 1;
        }
        Node *q = p->next; // 待删除结点
        p->next = q->next; // q跳过
        free(q);
    }

    // 打印链表
    printf("删除之后的链表: ");
    p = head;
    while (p != NULL) {
        printf("%d ", p->data);
        p = p->next;
    }
    printf("\n");

    return 0;
}
3.3.2 带头结点的删除

#include <stdlib.h>
#include <stdio.h>
typedef struct Node {
    int data;
    struct Node *next;
} Node;

int main() {
    int x, i, e, delPos;
    Node *p, *head, *r;

    // 创建头结点
    head = (Node *)malloc(sizeof(Node));
    if (head == NULL) {
        printf("内存分配失败\n");
        return 1;
    }
    head->data = 0;  // 头结点不存储有效数据
    head->next = NULL;
    r = head;  // r 指向当前链表的尾部

    // 构建初始链表,三个节点
    for (x = 1; x <= 3; x++) {
        p = (Node *)malloc(sizeof(Node));
        if (p == NULL) {
            printf("内存分配失败\n");
            return 1;
        }
        p->data = x;
        p->next = NULL;
        r->next = p;  // 将新节点连接到链表末尾
        r = p;        // 更新r,使其指向最后一个节点
    }

    // 输入插入位置和要插入的值
    printf("请输入要插入的位置和元素:");
    scanf("%d %d", &i, &e);

    // 创建新的节点
    Node *pTemp = (Node *)malloc(sizeof(Node));
    if (pTemp == NULL) {
        printf("内存分配失败\n");
        return 1;
    }
    pTemp->data = e;

    // 插入节点
    p = head;
    int j = 0;  // 从头结点开始计数
    while (p != NULL && j < i - 1) { // 循环找到要插入位置的前驱结点
        p = p->next;
        j++;
    }
    if (p == NULL) {
        printf("插入位置无效\n");
        free(pTemp);
        return 1;
    }
    pTemp->next = p->next;
    p->next = pTemp;

    // 打印链表
    printf("插入之后的链表: ");
    p = head->next;  // 跳过头结点
    while (p != NULL) {
        printf("%d ", p->data);
        p = p->next;
    }
    printf("\n");

    // 删除节点
    printf("请输入要删除的位置:");
    scanf("%d", &delPos);

    p = head;
    for (j = 0; j < delPos - 1 && p != NULL; j++) { // 循环找到要删除位置的前驱结点
        p = p->next;
    }
    if (p == NULL || p->next == NULL) {
        printf("删除位置无效\n");
        return 1;
    }
    Node *q = p->next; // 待删除结点
    p->next = q->next; // q跳过
    free(q);

    // 打印链表
    printf("删除之后的链表: ");
    p = head->next;  // 跳过头结点
    while (p != NULL) {
        printf("%d ", p->data);
        p = p->next;
    }
    printf("\n");

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值