单链表


从一个错误的嵌套结构体开始入手

struct Test
{
int x;
int y;
struct Test test;
};

会陷入无限的递归出现段错误

struct Test
{
int x;
int y;
struct Test *test;//变成指针之后就不会报错
};

变成指针以后就不会循环了,因为这个语句的意思只是声明了一个指针,而不是无限循环。这就是链表的结构。

单链表

在这里插入图片描述
把结构体改变为链表的结构

//链表book
struct Book2
{
    char title[128];
    char author[40];
    struct Book2 *next;
    
};

在单链表中插入元素
先连后断,保证能找到后一个节点。

#include<stdio.h>
#include<stdlib.h>
//链表book
struct Book2
{
    char title[128];
    char author[40];
    struct Book2 *next;
    
};

void getInput(struct Book *book)
{
    printf("input book name:\n");
    scanf("%s",book->title);
    printf("input writer:\n");
    scanf("%s",book->author);
}




void addBook(strcut Book **library)//这里要进行两层的解引用,它是一个指向Book2结构的指针的指针 因为要修改指针的值,所以要传入指针的地址
{
    struct Book2 *book,*temp;
    book=(struct Book2*)malloc(sizeof(struct Book2));
    if (book==NULL)
    {
        printf("fail!\n");
        exit(1);
    }
    getInput(book);
    if(*library !=NULL)
    {
        temp=*library;
        *library=book;
        book->next=temp;//把book.next指向上一次的地址
    }
    else{//*library 是空的,即直接把head指向这个新生成的节点
        *library=book;
        book->next=NULL;
    }
    
}
void printLibrary(struct Book *library)
{
    struct Book *book;
    int count=1;
    book=library;
    while (book!=NULL)
    {
        printf("Book%d:",count);
        printf("name:%s",book->title);
        book=book->next;
        count++;
    }
}

void releaseLibrary(struct Book **library)
{
	struct Book *temp;
	
    while (*library!=NULL)
    {
    	//这里需要增加一个临时变量,使得释放能正常运行
    	temp=*library;
    	*library=(*library)->next;
        free(temp);
    }
}



int main()
{
    
    struct Book2 *library=NULL;
    addBook(&library);//这里要给library的地址,而不是library,因为要修改的并不是library,而是其前一个节点
    //打印成员
    printLibrary(library);
    releaseLibrary(library);
    return 0;
}

形参里解引用两次的解释:
首先简单来说假如你写一个函数执行完使得p=3并传回主函数,你怎么写?
一种是直接赋值/例子说明,语法简略: int func(int p){ p=3,return p }
这样做的时候你不得不在main函数里面写上 p= func§;因为函数里面的局部变量不会改变main里面的值,所以你需要“返回+赋值”两道工序。
指针的作用就是解决这道工序,传入地址再更改可以改值!
如果改为 void func(int *p){ *p=3 }即更改完毕,main里面写 func(&p); 就好。

该题同理,头插法用常人的思路只用一个" * "能不能写?能写!
以下为我的代码/这里没有语法问题(这个函数需要你能看明白):

struct Book *add_book(struct Book *library)
{
        struct Book *book=NULL,*temp;
        book = (struct Book *)malloc(sizeof(struct Book));
        if(book==NULL)        exit(1);
        printf("请输入书名:\n");
        scanf("%s",book->title);
        printf("请输入作者:\n");
        scanf("%s",book->author);
        if(library!=NULL)
        {
                temp = library;
                library = book;
                book->next = temp; 
        }        
        else
        {
                library = book;
                book->next = NULL;
        }
        printf("插入成功\n");
        return library;
}

通常看这些代码没那么多*脑袋就不晕了,这里你要明白,这个形参里的指针library跟例子里面的常量p是一样的,无论library怎么改都不会进入main函数,所以必须要有“返回+赋值”。
于是主程序必须写成这样:{… library=add_book(library); …}。放心,该头插法可以正常运行。

现在我们跟示例一样将p变成p,函数变为void add_book(struct Book **library),将里面所有的library直接变为library, 这样函数没有返回值,main函数里面的语句变为:add_book(&library);

用我这种思路的好处是:1.你不必纠结于“”的具体意义就能写出的函数。2.你可以主动“降维”,写单*的函数实现相同的功能,有效降低大脑负荷。

尾插法
void addBook(strcut Book **library)//这里要进行两层的解引用,它是一个指向Book2结构的指针的指针 因为要修改指针的值,所以要传入指针的地址
{
    struct Book2 *book,*temp;
    book=(struct Book2*)malloc(sizeof(struct Book2));
    if (book==NULL)
    {
        printf("fail!\n");
        exit(1);
    }
    getInput(book);
    if(*library !=NULL)
    {
        temp=*library;//这个和头插一样
        //定位单链表的尾部位置
        while(temp->next !=NULL)
        {
            temp=temp->next;
        }
        //找到最后一个节点之后插入数据
        temp->next=book;
        book->next=NULL;
    }
    else{//*library 是空的,即直接把head指向这个新生成的节点
        *library=book;
        book->next=NULL;
    }
    
}

高效尾插法

void addBook(strcut Book **library)//这里要进行两层的解引用,它是一个指向Book2结构的指针的指针 因为要修改指针的值,所以要传入指针的地址
{
    static struct Book *tail;//用来记录单链表尾部的位置,需要变成静态变量 不需要每次都进行遍历
    struct Book2 *book,*temp;
    book=(struct Book2*)malloc(sizeof(struct Book2));
    if (book==NULL)
    {
        printf("fail!\n");
        exit(1);
    }
    getInput(book);
    if(*library !=NULL)
    {
        tail->next=book;
        book->next=NULL;
    }
    else{//*library 是空的,即直接把head指向这个新生成的节点
        *library=book;
        book->next=NULL;
    }
    //最后把book设置为新的tail
    
    tail=book;
}
搜索节点
struct Book *searchBook(struct Book *library,char *target)
{
 struct Book *book;
 book=library;
 while (book!=NULL)
 {
     if(!strcmp(book->title,target)||!strcmp(book->author,target))//strcmp 若俩字符串相等则返回0 否则返回1
     {
      break;//找到就跳出   
     }
     book=book->next;
 }
 return book;
    
}
实战训练

单链表的插入相比数组,并不用把数组插入元素后面的元素都往后移动。
例:
让用户输入若干整数,然后进行插入:

struct Node
{
    int value;
    struct Node *next;
};

void insertNode(struct Node **head,int value)//value 是插进去具体的值
{
    struct Node *prev;
    struct Node *curr;
    struct Node *new;
    
    curr=*head;
    pre=NULL;
    while(curr!=NULL && curr->value<value)//来查找定位
    {
        prev=curr;
        curr=curr->next;
    }
    //申请一个内存空间来存放新的节点插入找到的位置之前
    new=(struct Node *)malloc(sizeof(struct Node));
    if (new==NULL)
    {
        exit(1);
    }
    new->value=value;
    new->next=curr;
    if (prev==NULL)//自身是一个空的单链表
    {
        *head=new;
    }
    else{
        prev->next=new;
    }
}

void printNode(struct Node *head)
{
    struct Node *curr;
    curr=head;
    while (curr!=NULL)
    {
        printf("%d\n",curr->value);
        curr=curr->next;
    }
    putchar('\n');
}




int main()
{
    struct Node *head=NULL;
    int input;
    while (1)
    {
        printf("input a num:");
        if(input==-1)
        {
            break;
        }
        insertNode(&head,input);
        printNode()
    }
    return 0;
}

例2:
链表的删除

struct Node
{
    int value;
    struct Node *next;
};

void insertNode(struct Node **head,int value)//value 是插进去具体的值
{
    struct Node *prev;
    struct Node *curr;
    struct Node *new;
    
    curr=*head;
    pre=NULL;
    while(curr!=NULL && curr->value<value)//来查找定位
    {
        prev=curr;
        curr=curr->next;
    }
    //申请一个内存空间来存放新的节点插入找到的位置之前
    new=(struct Node *)malloc(sizeof(struct Node));
    if (new==NULL)
    {
        exit(1);
    }
    new->value=value;
    new->next=curr;
    if (prev==NULL)//自身是一个空的单链表
    {
        *head=new;
    }
    else{
        prev->next=new;
    }
}

void printNode(struct Node *head)
{
    struct Node *curr;
    curr=head;
    while (curr!=NULL)
    {
        printf("%d\n",curr->value);
        curr=curr->next;
    }
    putchar('\n');
}

void deleteNode(struct Node **head,int input)
{
    struct Node *prev;
    struct Node *curr;
    curr=*head;
    prev=NULL;
    while(curr!=NULL && curr->value!=value)
    {
        
        pre=curr;
        curr=curr->next;
    }
    if (curr==NULL)
    {
        printf("not found!");
        return ;
    }
    else{
            if (prev==NULL)
            {
                *head=curr->next;
            }
            else
            {
                 pre->next=cur->next;
                cur->next=NULL;
        
            }
       
    }
}


int main()
{
    struct Node *head=NULL;
    int input;
    while (1)
    {
        printf("input a num:");
        if(input==-1)
        {
            break;
        }
        deleteNode(&head,input);
        printNode()
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值