C语言数据结构单向链表

链表的学习

文章目录

  • 链表的学习
    • 1.什么是链表
    • 2.静态创建链表
      • 1.链表与数组的区别及实现
    • 3.链表的遍历
    • 4.链表的查找
      • 统计链表节点个数及链表查找
    • 5.链表的插入
      • 在节点的后方插入新数据
      • 在节点的前方插入
    • 6.链表的删除
    • 7.链表的修改
    • 8.动态创建链表
      • 头插法
      • 尾插法

1.什么是链表

在这里插入图片描述

一种数据存放的思想,数据结构。方便:增删改查

我们所熟知的数组,他的地址是连续的,一个接着一个,那么如果我想要删除或者修改其中的某一个元素时,那么这个时候数组就及其的不方便,因为的地址是连续的。

那么如果我们在这个时候引入链表,他的每个元素存放着下一个元素的地址,这样删除修改元素时只需修改下一个元素地址走向

2.静态创建链表

1.链表与数组的区别及实现

#include <stdio.h>

struct Test{
    int data;
    struct Test *next;
};//分号不能丢
int main()
{
    int array[]={2,5,9};
    for(int i =0;i<sizeof(array)/sizeof(array[0]);i++){
        printf("%d ",array[i]);
    }
    putchar('
');//单引号
    struct Test t1 ={5,NULL};
    struct Test t2 ={2,NULL};
    struct Test t3 ={1,NULL};
    t1.next = &t2;//用.运算符给结构体指针赋值
    t2.next = &t3;//t2结构体指针里面存放着t3结构体(属于一个块地址,里面存放着很多变量)的地址
    //链表思想,一个链一个
    printf("%d %d %d",t1.data,t1.next->data,t1.next->next->data);
    return 0;
}

3.链表的遍历

#include <stdio.h>

struct Test{
    int data;
    struct Test *next;
};//分号不能丢
void prinftlink(struct Test *head)
{
    struct Test *point;
    point = head;
    //我们知道链表的第一个指针存放下一个结构体的地址,第二个存放第三个,而第三个,也就是最后一个他的指针是空的,即NULL
    //所以我们可以利用这一特性来写循环
/*********************************
    while(1){
        if(point != NULL){
           printf("%d ",point->data);
            point = point ->next;
        }else{
            break;//跳出while(1)死循环
        }
**********************************/
        while(point != NULL){
            printf("%d ",point->data);
            point = point ->next;//指针偏移,指向下一个结构体指针
        }
    
    
}
int main()
{
    int array[]={2,5,9};
    for(int i =0;i<sizeof(array)/sizeof(array[0]);i++){
        printf("%d ",array[i]);
    }
    putchar('
');//单引号
    struct Test t1 ={5,NULL};
    struct Test t2 ={2,NULL};
    struct Test t3 ={1,NULL};
    struct Test t4 ={4,NULL};
    t1.next = &t2;//用.运算符给结构体指针赋值
    t2.next = &t3;//t2结构体指针里面存放着t3结构体(属于一个块地址,里面存放着很多变量)的地址
    t3.next = &t4;
//数越多,越傻瓜
  //  printf("%d %d %d",t1.data,t1.next->data,t1.next->next->data);
    prinftlink(&t1);//这个我们只需要把结构体的头地址放入即可遍历出来
    return 0;
}

4.链表的查找

统计链表节点个数及链表查找

#include <stdio.h>

struct Test{
    int data;
    struct Test *next;
};//分号不能丢

void prinftlink(struct Test *head)
{
    struct Test *point;
    point = head;
    while(point != NULL)
    {
        printf("%d ",point->data);
        point = point ->next;//指针偏移,指向下一个结构体指针
    }   
}

int getTotalNodeNum(struct Test *head)//查找链表节点个数
{
	int cnt = 0;
    while(head != NULL)
    {
        cnt++;
        head = head ->next;//指针偏移,指向下一个结构体指针
    } 
    return cnt;
}

int searchlink(struct Test *head,int data)//查找链表中是否有这个元素
{
    while(head != NULL)
    {
    	if(head ->data == data){
            return 1;
        }
        head = head ->next;//指针偏移,指向下一个结构体指针
    } 
    return 0;
}
int main()
{
    int array[]={2,5,9};
    for(int i =0;i<sizeof(array)/sizeof(array[0]);i++){
        printf("%d ",array[i]);
    }
    putchar('
');//单引号
    struct Test t1 ={5,NULL};
    struct Test t2 ={2,NULL};
    struct Test t3 ={1,NULL};
    struct Test t4 ={4,NULL};
    t1.next = &t2;//用.运算符给结构体指针赋值
    t2.next = &t3;//t2结构体指针里面存放着t3结构体(属于一个块地址,里面存放着很多变量)的地址
    t3.next = &t4;

    prinftlink(&t1);//这个我们只需要把结构体的头地址放入即可遍历出来
    int a = getTotalNodeNum(&t1);
    printf("total num is %d
",a);
    
    int b = searchlink(&t1,0);
    if(b){
        printf("you found it
");
    }else{
         printf("you don't found it
");
    }
    return 0;
}

5.链表的插入

在节点的后方插入新数据

1 2 3 4 这是一个链表,一个链一个,如果我们想在3的后面插入一个100,一共分三步

  1. 找到3这个数
  2. 让新节点的指针指到4
  3. 3的指针指到新节点
#include <stdio.h>

struct Test{
    int data;
    struct Test *next;
};//分号不能丢

void prinftlink(struct Test *head)
{
    struct Test *point;
    point = head;
    while(point != NULL)
    {
        printf("%d ",point->data);
        point = point ->next;//指针偏移,指向下一个结构体指针
    }   
}

int insertlink(struct Test *head,int data,struct Test *New)//在链表中插入新元素
{
    struct Test *p = head;
    while(p != NULL)
    {
    	if(p ->data == data){
            New ->next = p->next;
            p ->next = New;
            return 1;
        }
        p = p ->next;//指针偏移,指向下一个结构体指针
    } 
    return 0;
}
int main()
{
    int array[]={2,5,9};
    for(int i =0;i<sizeof(array)/sizeof(array[0]);i++){
        printf("%d ",array[i]);
    }
    putchar('
');//单引号
    struct Test t1 ={5,NULL};
    struct Test t2 ={2,NULL};
    struct Test t3 ={1,NULL};
    struct Test t4 ={4,NULL};
    t1.next = &t2;//用.运算符给结构体指针赋值
    t2.next = &t3;//t2结构体指针里面存放着t3结构体(属于一个块地址,里面存放着很多变量)的地址
    t3.next = &t4;

    struct Test New ={100,NULL};
    prinftlink(&t1);//这个我们只需要把结构体的头地址放入即可遍历出来
    puts("after insert behind is  
");
    insertlink(&t1,1,&New);
	prinftlink(&t1);
}

在节点的前方插入

难点在于链表的头如何插入?如果在链表头插入新元素,这个时候链表的头就发生了改变

#include <stdio.h>

struct Test{
    int data;
    struct Test *next;
};//分号不能丢

void prinftlink(struct Test *head)
{
    struct Test *point;
    point = head;
    while(point != NULL)
    {
        printf("%d ",point->data);
        point = point ->next;//指针偏移,指向下一个结构体指针
    }   
}

int insertlinkfrombehind(struct Test *head,int data,struct Test *New)//在链表中插入新元素
{
    struct Test *p = head;
    while(p != NULL)
    {
    	if(p ->data == data){
            New ->next = p->next;
            p ->next = New;
            return 1;
        }
        p = p ->next;//指针偏移,指向下一个结构体指针
    } 
    return 0;
}

struct 	Test *insertlinkfromforward(struct Test *head,int data,struct Test *New)
{
	struct Test *p = head;
	//如果是头节点的话,直接插入,把New返回回去
    	if(p ->data == data){
            New ->next = head;
            return New;
        }
	//如果不是头节点,那我就可以向下遍历了
    while(p->next != NULL)
    {
        if(p->next->data == data)//这是变化的地方
        {
            New->next =p->next;
            p ->next = New;
            return head;//换完直接返回退出,不用向下走了
        }
        p = p ->next;
    }
    return head;
}

int main()
{
	struct Test *head = NULL;
    
    struct Test t1 ={5,NULL};
    struct Test t2 ={2,NULL};
    struct Test t3 ={1,NULL};
    struct Test t4 ={4,NULL};
    t1.next = &t2;//用.运算符给结构体指针赋值
    t2.next = &t3;//t2结构体指针里面存放着t3结构体(属于一个块地址,里面存放着很多变量)的地址
    t3.next = &t4;

    head = &t1;
    
    struct Test New ={100,NULL};
    prinftlink(head);//这个我们只需要把结构体的头地址放入即可遍历出来
    puts("after insert behind is  
");
    insertlinkfrombehind(head,1,&New);
	prinftlink(head);
    
    struct Test New2 ={200,NULL};
    head = insertlinkfromforward(head,1,&New2);
    puts("after insert forword is  
");
    prinftlink(head);
}

6.链表的删除

假设目前五个数 1 2 3 4 5 想要删除某个节点

情况一:如果是头节点呢?删除之后头节点会变

情况二:不是头节点,if(p->next->data == data) p->next = p->next->next

#include <stdio.h>
#include <stdlib.h>
struct Test{
    int data;
    struct Test *next;
};//分号不能丢

void prinftlink(struct Test *head)
{
    struct Test *point;
    point = head;
    while(point != NULL)
    {
        printf("%d ",point->data);
        point = point ->next;//指针偏移,指向下一个结构体指针
    }   
}

struct Test* deleteNote(struct Test *head,int data)
{
    struct Test *p = head;
    if(p->data == data)
    {
        head = head->next;
        free(p);
    }
    while(p->next != NULL)
    {
        if(p->next->data == data)
        {
            p->next = p->next->next;
            return head;
        }
        p = p->next;
    }
    return head;
}


int main()
{
	struct Test *head = NULL;
    
    //struct Test t1 ={5,NULL};
    struct Test *p =(struct Test*)malloc(sizeof(struct Test));//动态创建,继续用以前的静态创建会报错
    struct Test t2 ={2,NULL};
    struct Test t3 ={1,NULL};
    struct Test t4 ={4,NULL};
    
   // t1.next = &t2;//用.运算符给结构体指针赋值
    p->data = 1;
    p->next = &t2;
    
    t2.next = &t3;//t2结构体指针里面存放着t3结构体(属于一个块地址,里面存放着很多变量)的地址
    t3.next = &t4;

   // head = &t1;
    head = p;
    prinftlink(head);//这个我们只需要把结构体的头地址放入即可遍历出来
    putchar('
');
	head = deleteNote(head,2);
	prinftlink(head);
}

7.链表的修改

直接在查找的基础上改不就行了吗?

#include <stdio.h>

struct Test{
    int data;
    struct Test *next;
};//分号不能丢

void prinftlink(struct Test *head)
{
    struct Test *point;
    point = head;
    while(point != NULL)
    {
        printf("%d ",point->data);
        point = point ->next;//指针偏移,指向下一个结构体指针
    }   
}

int getTotalNodeNum(struct Test *head)//查找链表节点个数
{
	int cnt = 0;
    while(head != NULL)
    {
        cnt++;
        head = head ->next;//指针偏移,指向下一个结构体指针
    } 
    return cnt;
}

int searchlink(struct Test *head,int data,int newdata)//查找链表中是否有这个元素
{
    while(head != NULL)
    {
    	if(head ->data == data){
            head ->data = newdata;
            return 1;
        }
        head = head ->next;//指针偏移,指向下一个结构体指针
    } 
    return 0;
}
int main()
{
    int array[]={2,5,9};
    for(int i =0;i<sizeof(array)/sizeof(array[0]);i++){
        printf("%d ",array[i]);
    }
    putchar('
');//单引号
    struct Test t1 ={5,NULL};
    struct Test t2 ={2,NULL};
    struct Test t3 ={1,NULL};
    struct Test t4 ={4,NULL};
    t1.next = &t2;//用.运算符给结构体指针赋值
    t2.next = &t3;//t2结构体指针里面存放着t3结构体(属于一个块地址,里面存放着很多变量)的地址
    t3.next = &t4;

    prinftlink(&t1);//这个我们只需要把结构体的头地址放入即可遍历出来
    int a = getTotalNodeNum(&t1);
    printf("total num is %d
",a);
    
    int b = searchlink(&t1,0);
    if(b){
        printf("you found it
");
    }else{
         printf("you don't found it
");
    }
    return 0;
}

8.动态创建链表

头插法

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

struct Test{
    int data;
    struct Test *next;
};//分号不能丢

void printflink(struct Test *head)
{
    struct Test *point;
    point = head;
    while(point != NULL)
    {
        printf("%d ",point->data);
        point = point ->next;//指针偏移,指向下一个结构体指针
    }   
}

struct Test *insertFormHead(struct Test *head)
{
    struct Test* New;
    
	while(1)
    {
            New = (struct Test *)malloc(sizeof(struct Test));
            printf("input your new node data:
");
            scanf("%d,",&(New->data));
			
        	if(New ->data ==0){
                printf("0 quit
");
                return head;
            }
            if(head == NULL){
                head = New;
            }else{
                New->next = head;
                head = New;   
            }        
    }
    return head;
}
int main()
{
    struct Test *head = NULL;
	head = insertFormHead(head);
	printflink(head);
    return 0;
}

对上优化

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

struct Test{
    int data;
    struct Test *next;
};//分号不能丢

void printflink(struct Test *head)
{
    struct Test *point;
    point = head;
    while(point != NULL)
    {
        printf("%d ",point->data);
        point = point ->next;//指针偏移,指向下一个结构体指针
    }   
}

struct Test *insertFormHead(struct Test *head,struct Test *New)
{
    if(head == NULL){
        head = New;
    }else{
        New->next = head;
        head = New;   
    }        
    return head;
}
struct Test *creatlink(struct Test *head)
{
    struct Test* New;
     
	while(1)
	{
        New = (struct Test *)malloc(sizeof(struct Test));
        printf("input your new node data:
");
        scanf("%d,",&(New->data));

        if(New ->data ==0){
            printf("0 quit
");
            return head;
        }
        head = insertFormHead(head,New);
     }
}
int main()
{
    struct Test *head = NULL;
	head = insertFormHead(head);
	printflink(head);
    return 0;
}

尾插法

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

struct Test{
    int data;
    struct Test *next;
};//分号不能丢

void printflink(struct Test *head)
{
    struct Test *point;
    point = head;
    while(point != NULL)
    {
        printf("%d ",point->data);
        point = point ->next;//指针偏移,指向下一个结构体指针
    }   
}

struct Test *insertFormHead(struct Test *head,struct Test *New)
{
    if(head == NULL){
        head = New;
    }else{
        New->next = head;
        head = New;   
    }        
    return head;
}
struct Test *creatlink(struct Test *head)
{
    struct Test* New;
     
	while(1)
	{
        New = (struct Test *)malloc(sizeof(struct Test));
        printf("input your new node data:
");
        scanf("%d,",&(New->data));

        if(New ->data ==0){
            printf("0 quit
");
            return head;
        }
        head = insertFormHead(head,New);
     }
}
struct Test *insertbehind(struct Test *head,struct Test *new)
{
    struct Test *p = head;
    if(p == NULL){
        head = new;
        return head;
    }
    while(p ->next != NULL)//没有到尾巴前一直偏移指针
    {
        p = p->next;
    }
    p->next = new;
    return head;
}
struct Test *creatlink2(struct Test *head)
{
    struct Test* New;
     
	while(1)
	{
        New = (struct Test *)malloc(sizeof(struct Test));
        printf("input your new node data:
");
        scanf("%d,",&(New->data));

        if(New ->data ==0){
            printf("0 quit
");
            return head;
        }
        head = insertbehind(head,New);
     }
}
int main()
{
    struct Test *head = NULL;
	head = creatlink2(head);
	printflink(head);
    //头插法
    struct Test t1 ={1000,NULL};
    head = insertFormHead(head,&t1);
    printflink(head);
    //尾插法
    struct Test t2 ={2000,NULL};
    head = insertbehind(head,&t2);
    printflink(head);
    return 0;
}

至此,单向链表完结啦!! “请不要相信胜利就像山坡上的蒲公英一样唾手可得,但是请相信生活中总有美好值得我们全力以赴,哪怕粉身碎骨!”