精华内容
下载资源
问答
  • 一步一步教你从零开始写C语言链表

    万次阅读 多人点赞 2017-04-02 14:34:39
    完整源码获取: 微信关注:嵌入式开发圈 ...3、数组移动元素的过程中,要对元素进行大范围的移动,很耗时间,效率也不高。 先来感性的认识一下链表,我们先来认识下简单的链表: 从这幅图我们...

    完整源码获取:
    微信关注:嵌入式云IOT技术圈
    发送"链表"即可获取。watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly95YW5neXVhbnhpbi5ibG9nLmNzZG4ubmV0,size_16,color_FFFFFF,t_70

    为什么要学习链表?

    链表主要有以下几大特性:

    1、解决数组无法存储多种数据类型的问题。

    2、解决数组中,元素个数无法改变的限制(C99的变长数组,C++也有变长数组可以实现)。

    3、数组移动元素的过程中,要对元素进行大范围的移动,很耗时间,效率也不高。

    先来感性的认识一下链表,我们先来认识下简单的链表:

    从这幅图我们得出以下信息:

    这个简单链表的构成:

    头指针(Header),若干个节点(节点包括了数据域和指针域),最后一个节点要指向空。

    实现原理:头指针指向链表的第一个节点,然后第一个节点中的指针指向下一个节点,然后依次指到最后一个节点,这样就构成了一条链表。

    接下来看看链表的数据结构:

    struct  list_node
    {
    	int data ; //数据域,用于存储数据
    	struct list_node *next ; //指针,可以用来访问节点数据,也可以遍历,指向下一个节点
    };
    

    那么如何来创建一个链表的一个节点呢?

    我们写个程序演示一下:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    struct  list_node
    {
    	int data ; 
    	struct list_node *next ;
    };
    
    typedef struct list_node list_single ; 	
    
    int main(void)
    {
    	list_single *node = NULL ;          //1、首先,当然是定义一个头指针
    	node = (list_single *)malloc(sizeof(list_single)); //2、然后分配内存空间
    	if(node == NULL){
    		printf("malloc fair!\n");
    	}
    	memset(node,0,sizeof(list_single)); //3、清一下
    	node->data = 100 ;		    		//4、给链表节点的数据赋值
    	node->next = NULL ;                 //5、将链表的指针域指向空
    	printf("%d\n",node->data);
    	free(node);
    	return 0 ;
    }

    那么,这仅仅只是创建一个链表中的一个节点,为了好看,我们把创建节点封装成函数,以后想创建多少个节点,我们就可以反复调用一个函数来创建,会很方便:

    list_single *create_list_node(int data)
    {
    	list_single *node = NULL ;
    	node = (list_single *)malloc(sizeof(list_single));
    	if(node == NULL){
    		printf("malloc fair!\n");
    	}
    	memset(node,0,sizeof(list_single));
    	node->data = 100 ;
    	node->next = NULL ;
    	return node ;
    }

    接下来在程序上完成的程序:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    struct  list_node
    {
    	int data ; 
    	struct list_node *next ;
    };
    
    typedef struct list_node list_single ; 	
    list_single *create_list_node(int data)
    {
    	list_single *node = NULL ;
    	node = (list_single *)malloc(sizeof(list_single));
    	if(node == NULL){
    		printf("malloc fair!\n");
    	}
    	memset(node,0,sizeof(list_single));
    	node->data = data;
    	node->next = NULL ;
    	return node ;
    }
    int main(void)
    {
    	int data = 100 ;
    	list_single *node_ptr = create_list_node(data); //创建一个节点
    	printf("node_ptr->data=%d\n",node_ptr->data);   //打印节点里的数据
    	printf("node_ptr->next=%d\n",node_ptr->next);  
    	free(node_ptr);
    	return 0 ;
    }

    执行结果 :



    这样我们就完成一个链表节点的创建了,那么它现在的样子如下图:

    链表的结构里,数据存储了100,因为这个链表只有一个节点,所以它的指针域指向了NULL。

       上面只是建立一个单链表的基本雏形,接下来咱们再来增加一点难度。如果创建多个单链表节点,实现单链表的增删改查?把单链表应用起来。

    1、首先定义一个单链表的数据结构

    创建节点函数原型可定义如下:
    struct list *create_node(int data) ;
    如何创建单链表的节点,主要分以下步骤:
    (1)给当前的每个节点的数据结构配置定量的空间大小
       ep : struct list *node = malloc(sizeof(struct list));
    (2)清节点数据(由于结构体变量在未初始化的时候,数据是脏的)
       ep : memset(node,0,sizeof(struct list));
    (3)给节点初始化数据
       ep : node->id = data ; 
    (4)将该节点的指针域设置为NULL
       ep : node->next = NULL ;

    2、单链表的尾插:

    尾插节点函数原型可定义如下:

    如何将当前链表和新的节点相连接?只要实现:
    header->next = new 
    尾插流程如下:

    (1)获取当前节点的位置,也就是访问头节点
       ep : struct list *p = header ;
    (2)判断是否为最后一个节点,如果不是,移动到下一个节点,如果是,将数据插入尾部。
       ep : while(NULL != p->next) p = p->next ;
            p->next = new ;

    3、单链表的头插

    很好理解,头插就是把新的节点插在原来的节点和原来节点的下一个节点之间的一个节点。如图,新的节点插在头节点和节点1。
    所以可以推出头插流程如下:
    (1)获取当前节点的位置,也就是访问头节点
        ep : struct list *p = header ;
    (2)新的节点的下一个节点设置为原来头节点的下一个节点(第一个节点)
        ep : new->next = p->next ;
    (3)原来的头节点的下一个节点设置为现在新插入的头节点
        ep : p->next = new ;

    4、单链表的遍历

    如图为一条单向链表的模型,看图知道该链表由头节点和若干个节点组成,最后一个节点(尾节点)为NULL 。
    从图中可以得出信息,如果我们要打印出各个节点的数据,要考虑以下问题:
    (1)需要打印头节点吗?(头节点肯定是不用打印的,因为这是我们为了操作方便而设置的一个节点)。
    (2)这条链表有多少个节点我们怎么知道?(通过判断该链表是否已经到达了尾节点,标志就是NULL)
    那么可以得到流程如下:
    (1)获取当前节点的位置,也就是访问头节点
        ep : struct list *p = header ;
    (2)由于头节点我们不需要去打印它,这时候,初始化打印的节点需要从第一个节点开始。
        ep : p = p->next ;  
    (3)判断是否为最后一个节点,如果不是,先打印第一个节点的数据(1),然后移动到下一个节点(2),重复这两个步骤。
       如果是最后一个节点,直接打印数据即可。
        while(NULL != p->next){ printf("node:%d\n",p->data) ; p = p->next ;}
        printf("node:%d\n",p->data);
        当然还可以一句代码解决,这样就达到了先偏移,后取数据。
        while(NULL != p->next){ p = p->next ; printf("node:%d\n",p->data) ; }
     

    5、单链表的删除

    删除节点的函数原型可定义如下:
    int detele_list_node(struct list *pH , int data);
    单向链表的删除要考虑两种情况,一种的普通节点的删除(当然,头节点不能算)
    还有一种是尾节点的前一个节点的删除情况,注意,删除完节点还需要释放对应节点的内存空间。

    删除节点的设计流程:
    (1)先定义两个指针,一个表示当前的节点,另一个表示当前节点的上一个节点。
        ep : struct list *p = header ;  //当前节点
             struct list *prev = NULL ; //当前节点的上一个节点
    (2)遍历整个链表,同时保存当前节点的前一个节点
        ep : while(NULL != p->next)
            { 
              //保存了当前的节点的前一个节点
              prev = p ;  
              //保存当前偏移的节点
              p = p->next ; 
              return 0 ;
            }
    (3)在遍历的过程中查找要删除的数据
        ep : while(NULL != p->next)
            { 
              //保存了当前的节点的前一个节点
              prev = p ;  
              //保存当前偏移的节点
              p = p->next ; 
              //查找到了数据
              if(p->id == data)
              {
              
              }
              return 0 ;
            }
    (4)查找到了数据后,分两种情况删除
        ep : 普通节点的删除
            if(p->id == data)
            {
                prev->next = p->next ;
                free(p);
            }
        ep : 考虑尾节点的下一个节点为NULL的节点删除
            if(p->id == data)
            {
                if(p->next == NULL)
                {
                    prev->next = NULL ;
                    free(p);
                }
            }

    6、单链表的逆序

    逆序步骤:

    单链表的基本操作流程咱们基本搞懂了,下面写一个程序,这将会变得非常非常的简单。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    typedef struct slist
    {
    	int id ;
    	struct slist *next ;			
    }L;
     
    //创建一个节点 
    L *create_node(int data)
    {
    	//给每个节点分配结构体一样的空间大小 
    	L *p = malloc(sizeof(L));
    	if(NULL == p)
    	{
    		printf("malloc error!\n");
    		return NULL ;
    	}
    	//由于结构体在未初始化的时候一样是脏数据,所以要清 
    	memset(p,0,sizeof(L));
    	//初始化第一个节点 
    	p->id = data ; 
    	//将节点的后继指针设置为NULL 
    	p->next = NULL ;
    }
     
    //链表的尾插 
    void tail_insert(L *pH , L *new)
    {
    	//获取当前的位置 
    	L *p = pH ; 
    	//如果当前位置的下一个节点不为空 
    	while(NULL != p->next)
    	{
    		//移动到下一个节点 
    		p = p->next ;
    	}
    	//如果跳出以上循环,所以已经到了NULL的这个位置
    	//此时直接把新插入的节点赋值给NULL这个位置 
    	p->next = new ;
    }
     
    //链表的头插 
    void top_insert(L *pH , L *new)
    {
    	L *p = pH ;
    	new->next = p->next ;
    	p->next = new ;
    }
     
    //链表的遍历 
    void Print_node(L *pH)
    {
    	//获取当前的位置 
    	L *p = pH ;
    	//获取第一个节点的位置 
    	p = p->next ;
    	//如果当前位置的下一个节点不为空 
    	while(NULL != p->next)
    	{
    		//(1)打印节点的数据 
    		printf("id:%d\n",p->id);
    		//(2)移动到下一个节点,如果条件仍为真,则重复(1),再(2) 
    		p = p->next ;
    	}
    	//如果当前位置的下一个节点为空,则打印数据
    	//说明只有一个节点 
    	printf("id:%d\n",p->id);
    }
     
    //删除链表中的节点 
    int detele_list_node(L * pH , int data)
    {
    	//获取当前头节点的位置 
    	L *p = pH ;
    	L *prev = NULL;
    	while(NULL != p->next)
    	{
    		//保存当前节点的前一个节点的指针 
    		prev = p ;
    		//然后让当前的指针继续往后移动 
    		p = p->next ; 	
    		//判断,找到了要删除的数据  
    		if(p->id == data)
    		{
    			//两种情况,一种是普通节点,还有一种是尾节点
    			if(p->next != NULL)  //普通节点的情况 
    			{
    				prev->next = p->next ;
    				free(p);
    			}
    			else //尾节点的情况 
    			{
    				prev->next = NULL ; //将这个尾节点的上一个节点的指针域指向空 
    				free(p); 
    			}
    			return 0  ;
    		}
    	}
    	printf("没有要删除的节点\n");
    	return -1 ;
    }
     
    void trave_list(L * pH)
    {
    	//保存第一个节点的位置 
    	L *p = pH->next;
    	L *pBack;
    	int i = 0 ;
    	if(p->next == NULL || p == NULL)
    		return ;
    		
    	while(NULL != p->next) //遍历链表 
    	{
    		//保存第一个节点的下一个节点 
    		pBack = p->next ; 
    		//找到第一个有效节点,其实就是头指针的下一个节点 
    		if(p == pH->next) 
    		{
    			//第一个有效节点就是最后一个节点,所以要指向NULL 
    			p->next = NULL ; 
    		} 
    		else
    		{
    			/*
    			new->next = p->next ;
    			p->next = new ;
    			*/
    			p->next = pH->next ; //尾部连接 
    		}
    		pH->next = p ; //头部连接 
    		p = pBack ; //走下一个节点 
    	}
    	top_insert(pH,p); //插入最后一个节点 
    }
     
    int main(int argc , char **argv) 
    {
    	//创建第一个节点 
    	int i ;
    	L *header = create_node(0); 
    	for(i = 1 ; i < 10 ; i++)
    	{
    		tail_insert(header,create_node(i));
    	}
    	Print_node(header);
    	detele_list_node(header,5);
    	putchar('\n');
    	Print_node(header);
    	putchar('\n');
    	trave_list(header);
    	Print_node(header);
    	return 0 ;
    }
    

    运行结果:

    当然,单链表存在一定的弊端,就是查找数据和删除数据的时候比较麻烦,而双链表的出现就是为了解决它的弊端:

    双链表的引入是为了解决单链表的不足:
    (1)双链表可以往前遍历,也可以往后遍历,具有两个方向
    双链表的节点 = 有效数据 + 两个指针(分别指向前一个节点和后一个节点)
    双向链表的图形结构描述:

    struct double_list                                   struct double_list
    {                                                            {
        数据域;                  ep :------->                   int data ;
        指针域(前向指针) ;                                   struct double_list *prev ;
        指针域(后向指针) ;                                   struct double_list *next ;
    };                                                             };
     

    1、双向链表的创建

    struct list *create_node(int data) ;
    创建步骤(与单链表类似,就是多了一个指针):
    (1)给节点申请空间:
       ep : struct double_list *p = malloc(sizeof(struct double_list));
    (2)初始化数据域
       ep : p->data = data ;
    (3)初始化指针域
       ep : p->prev = NULL ; 
            p->next = NULL ;

    2、双向链表的尾插

    双向链表尾插节点的函数可以定义如下:
    void double_list_tail_insert(struct double_list *header , struct double_list *new) ;
    尾插图示操作:

    尾插的步骤:


    (1)找到链表的尾节点
       ep : 和单链表差不多
            DL *p = header ;
            while(NULL != p->next)
                p = p->next ;
    (2)将新的节点连接到尾节点的后面成为新的节点
       1.原来的next指针指向新节点的首地址。            p->next = new ;
       2.新节点的prev指针指向原来的尾节点的首地址。 new->prev = p;
     

    3、双向链表的头插

    双向链表头插节点的函数可以定义如下:
    void double_list_top_insert(struct double_list *header , struct double_list *new) ;

    4、双向链表的遍历

    4.1 正向遍历
        void double_list_for_each(DL *header)
        步骤:和单链表完全一致,没什么好写的。
        
        
    4.2 反向遍历
        void double_list_for_each_nx(DL *header)
        步骤:(1)和单链表一样,先循环找到最后一个节点的地址
              (2)再依靠prev指针循环往前移动
                 2.1 先打印最后一个数据  ep : printf("%d ",p->data);
                 2.2 向前循环遍历         ep : p = p->prev ;
                 
                 判断条件:header->prev != p->prev,
                 header保存的是头节点的地址,
                 header->prev保存的是头节点的prev的地址,
                 header->next保存的是头节点的next的地址,
                 头节点在创建的时候:
                 header->prev = NULL ;
                 header->next = NULL ;
                 所以这个条件这样写header->prev = NULL也是对的。

    5、双向链表节点的删除


    假设需要删除节点1:    
        首先:
        (1)获取当前节点的地址: 
            ep : p = header;
        (2)遍历所有的节点,找到要删除的节点:
            ep : 
            while(NULL != p->next)
            {
                p = p->next ;
                if(p->data == data)
                {
                
                }
            }
        (3)找到要删除的数据以后,需要做判断,判断两种情况,这和单链表差不多
        3.1 如果找到当前节点的下一个节点不为空
        3.1.1    
            那就把当前节点的prev节点指向要删除的这个节点的prev
            因为当前的prev节点保存的是要删除的上一个节点的指针 
            p->next->prev = p->prev ;
        3.1.2    
            然后将当前节点的prev指针(也就是上一个节点的指针)指向当前节点(要删除的)的下一个节点:
            p->prev->next = p->next ;
        3.1.3    
            最后释放删除指针的空间:
            free(p);
            
        3.2 如果找到当前节点的下一个节点为空
        3.2.1   
        直接把当前指针(要删除的节点)的prev指针(保存着当前指针的上一个节点的地址)的下一个next指针设置为空。
        p->prev->next = NULL ;
        3.2.2
        将删除的指针的空间释放:
        free(p);
        看来,双链表学起来比单链表容易多了!确实啊,多了一个方向,操作起来就更加容易了,但是多了一个方向,一维多了一个指针,相比增加了一定的复杂度,但是,只要牢记prev指针和next指针的指向,那么,手画一图,代码即可写出!
    下面给一个案例实现一下双向链表:
     

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    //创建一个双链表的数据结构
    typedef struct __double_list
    {
    	int data ;
    	struct __double_list *prev ;
    	struct __double_list *next ;
    }DL ; 
     
    //创建双向链表并插入一个节点 
    DL *create_dl_node(int data)
    {
    	DL *p = malloc(sizeof(DL));
    	if(NULL == p)
    	{
    		printf("create dl node fair!\n");
    		return NULL ;
    	}
    	//初始化数据 
    	p->data = data ;
    	//初始化指针 
    	p->next = NULL ;
    	p->prev = NULL ;
    }
     
    //双向链表的尾插 
    void double_list_tail_insert(DL *header , DL *new)
    {
    	//取得当前节点的地址 
    	DL *p = header ;
    	//找到链表的尾节点 
    	while(NULL != p->next)
    	{
    		//找不到接着找 
    		p = p->next ;
    	}
    	//找到了尾节点,指向新节点的首地址 
    	p->next = new ;
    	//新节点的prev指针指向原来的尾节点的首地址。
    	new->prev = p;
    }
     
    //双向链表的头插(也就是插在两个节点之间的插入方式)
    void double_list_top_insert(DL *header , DL *new)
    {
    	//新节点的next指针指向原来的节点一的地址
    	new->next = header->next ; 
    	//判断当前节点的下一个节点的地址是否为空
    	if(NULL != header->next) 
    		header->next->prev = new ; //节点1的prev指针指向新节点的地址 
    	header->next = new ;
    	new->prev = header ;
    }
     
    //双向链表的正向遍历 
    void double_list_for_each(DL *header)
    {
    	DL *p = header ;
    	while(NULL != p->next)
    	{
    		p = p->next ;
    		printf("%d ",p->data);
    	}
    }
     
    //双向链表的反向遍历 
    void double_list_for_each_nx(DL *header)
    {
    	DL *p = header ;
    	//先找到尾节点
    	while(NULL != p->next)
    	{
    		p = p->next ;	
    	} 
    	//已经找到了尾节点,向前遍历,注意,遍历到头节点之前
    	//限制条件: != 头结点 
    	while(NULL != p->prev)
    	{
    		printf("%d ",p->data);
    		p = p->prev ;
    	}
    }
     
    //双向链表节点的删除
    int double_list_delete_node(DL *header , int data)
    {
    	//取得当前节点 
    	DL *p = header;
    	//遍历所有的节点 
    	while(NULL != p->next)
    	{
    		p = p->next ;
    		//找到了对应要删除的数据了 
    		if(p->data == data)
    		{
    			//一样存在两种情况
    			//(1)当前节点的下一个节点不为空
    			if(p->next != NULL)
    			{
    				//那就把当前节点的prev节点指向要删除的这个节点的prev
    				//因为当前的prev节点保存的是要删除的上一个节点的指针 
    				p->next->prev = p->prev ;
    				//还要指定它的next节点 
    				p->prev->next = p->next ;
    				free(p);
    			}
    			//(2)当前节点的下一个节点为空 
    			else
    			{
    				//把 
    				p->prev->next = NULL ;
    				free(p); 
    			}
    			return 0 ;
    		}
    	}
    	printf("\n没有找到对应要删除的节点,或者节点已经被删除!\n");
    	return -1 ;	
    } 
     
    int main(void)
    {
    	int i ;
    	DL *header = create_dl_node(0);
    	for(i = 0 ; i < 10 ; i++)
    	{
    		//双向链表的尾插 
    		//double_list_tail_insert(header,create_dl_node(i));
    		//双向链表的头插 
    		double_list_top_insert(header,create_dl_node(i));
    	}
    	//双向链表的正向遍历 
    	printf("双向链表的正向遍历:");
    	double_list_delete_node(header,5);
    	double_list_for_each(header);
    //	double_list_delete_node(header,9);
    	double_list_delete_node(header,5);
    	putchar('\n');
    	printf("双向链表的反向遍历:");
    	double_list_for_each_nx(header);
    	return 0 ;
    }
    

    运行结果:

    展开全文
  • C语言链表操作详解

    万次阅读 多人点赞 2018-12-29 19:01:55
    插入和删除操作需要移动大量的数组元素,效率慢 只能存储一种类型的数据. 而链表则可以实现以上这些数组所不具备的功能,此时引入了结构体来实现创建链表的操作。 链表的特点: n个节点离散分配 每一个节...

    为什么要使用链表

    在未学习链表时,我们常用的存储数据的方式无非就是数组。使用数组存储数据的好处就是查询快,但是它的弊端也很明显:

    1.  使用前需声明数组的长度,一旦声明长度就不能更改
    2. 插入和删除操作需要移动大量的数组元素,效率慢
    3. 只能存储一种类型的数据.

    而链表则可以实现以上这些数组所不具备的功能,此时引入了结构体来实现创建链表的操作。

    链表的特点:

    1.  n个节点离散分配
    2. 每一个节点之间通过指针相连
    3. 每一个节点有一个前驱节点和一个后继节点
    4. 首节点没有前驱节点,尾节点没有后继节点

    首先先定义一个简单的结构体

    struct link{
           int data;          //定义数据域
           struct link *next; //定义指针域,存储直接后继的节点信息
    };

    数据域的内容可以自己指定,指针域用来存放下一个节点的地址。

    创建链表前须知

    首节点:存放第一个有效数据的节点

    头节点:在单链表的第一个结点之前附设一个结点,它没有直接前驱,称之为头结点,头结点的数据域可以不存储任何信息,指针域指向第一个节点(首节点)的地址。头结点的作用是使所有链表(包括空表)的头指针非空

    头指针:指向头节点的指针

    尾节点:存放最后一个有效数据的节点

    尾指针:指向尾节点的指针

    建立一个单向链表

    方法:定义方法向链表中添加节点来建立一个单向链表

    思路:首先定义一个结构体指针变量p,使用malloc函数为新建节点申请内存空间,让p指向这个节点(指向它刚申请的内存空间),再将其添加到链表中。此时需考虑两种情况:

    1.若单链表为空表,则将新建节点置为头节点

    //若此时只在链表中插入头节点
    struct link *p = head;
    p = (struct link *)malloc(sizeof(struct link));  //让p指向新建节点创建的内存空间
    if(p == NULL){   //新建节点申请内存失败
        exit(0);
    }
    //此时head也指向头节点的地址
    p->next = NULL;     //因为没有后续节点,所以新建节点指针域置空

    2.链表非空,则将新建节点添加到表尾

    //此时已存在头节点,再插入一个节点(首节点)
    struct link *p = NULL,*pr = head;  
    p = (struct link *)malloc(sizeof(struct link));
    if(p == NULL){
        exit(0);
    }
    if(head == NULL){
        head = p;
    }else{
        while(pr->next != NULL){     //当头节点的指针域不为NULL
          pr = pr->next;             //pr指向下一个节点的地址
      }
        pr->next = p;                //使头节点的指针域存储下一个节点的地址
    }

    完整操作

    #include <stdio.h>
    #include <stdlib.h>
    struct link *AppendNode(struct link *head);
    void DisplayNode(struct link *head);
    void DeleteMemory(struct link *head);
    //定义结构体 
    struct link{
    	int data; 				//定义数据域 
    	struct link *next; 			//定义指针域 
    }; 
    int main(){
    	int i =0;         			//定义i,记录创建的节点数 
    	char c;							
    	struct link *head = NULL;		//创建头指针,初始化为NULL 
    	printf("DO you wang to append a new node(Y or N)?");
    	scanf(" %c",&c);				
    	while(c == 'Y' || c == 'y'){	//如果确定继续添加结点 
    		head = AppendNode(head);	//通过函数获得链表的地址,AppendNode函数返回的是链表的头指针
    		                            //可以根据头指针指向的地址来获取链表中保存的数据 
    		DisplayNode(head);			//根据头指针打印链表 
    		printf("DO you want to append a new node(Y or N)?");
    		scanf(" %c",&c);
    		i++; 
    	}
    	printf("%d new nodes hava been appended!\n",i);
    	DeleteMemory(head);			//释放占用的内存 
    }
    struct link *AppendNode(struct link *head){    //声明创建节点函数 
    	struct link *p = NULL,*pr = head;      //创建p指针,初始化为NULL;创建pr指针,通过pr指针来给指针域赋值 
    	int data ;
    	p = (struct link *)malloc(sizeof(struct link)) ; //为指针p申请内存空间,必须操作,因为p是新创建的节点 
    	if(p == NULL){			//如果申请内存失败,则退出程序 
    		printf("NO enough momery to allocate!\n");
    		exit(0);
    	}
    	if(head == NULL){		//如果头指针为NULL,说明现在链表是空表 
    		head = p;								//使head指针指向p的地址(p已经通过malloc申请了内存,所以有地址) 
    	}else{										//此时链表已经有头节点 ,再一次执行了AppendNode函数 
    												//注:假如这是第二次添加节点
    	                                  //因为第一次添加头节点时,pr = head,和头指针一样指向头节点的地址 
    		while(pr->next!= NULL){		//当pr指向的地址,即此时的p的指针域不为NULL(即p不是尾节点) 
    			pr = pr->next;	//使pr指向头节点的指针域
    		}
    		pr->next = p;	//使pr的指针域指向新键节点的地址,此时的next指针域是头节点的指针域 
    	}
    	printf("Input node data:");                   
    	scanf("%d",&data);
    	p->data = data; 			//给p的数据域赋值 
    	p->next = NULL;			//新添加的节点位于表尾,所以它的指针域为NULL 
    	return head;			//返回head的地址 
    }
    void DisplayNode(struct link *head){         	//输出函数,打印链表 
    	struct link *p = head;			// 定义p指针使其指向头节点 
    	int j = 1;									//定义j记录这是第几个数值 
    	while(p != NULL){		//因为p = p->next,所以直到尾节点打印结束 
    		printf("%5d%10d\n",j,p->data);			
    		p = p->next;		//因为节点已经创建成功,所以p的指向由头节点指向下一个节点(每一个节点的指针域都指向了下一个节点) 
    		j++;	
    	}
    }
    void DeleteMemory(struct link *head){			//释放资源函数 
    	struct link *p = head,*pr = NULL;	        //定义p指针指向头节点 
    	while(p != NULL){				//当p的指针域不为NULL 
    		pr = p;									//将每一个节点的地址赋值给pr指针 
    		p = p->next;			        //使p指向下一个节点 
    		free(pr);								//释放此时pr指向节点的内存 
    	}
    }

    第二种创建链表方式-优化

    #include <stdio.h> 
    #include <stdlib.h>
    struct Stu *create(int n);
    void print(struct Stu *head);
    struct Stu{
    	int id;
    	char name[50];
    	struct Stu *next;
    };
    int main(){
    	int n;
    	struct Stu *head = NULL;   //创建头指针 
    	printf("请输入你想要创建的节点个数:\n");
    	scanf("%d",&n);
    	head = create(n);
    	print(head);
    }
    struct Stu *create(int n){
    	struct Stu *head,*node,*end;   						//定义头节点,普通节点,尾节点 
    	head = (struct Stu *)malloc(sizeof(struct Stu)); 	//给头节点申请内存 
    	end = head;        									//若是空表,则头尾地址一致 
    	for(int i=0;i<n;i++){								//利用for循环向链表中添加数据 
    		node = (struct Stu *)malloc(sizeof(struct Stu));//给普通节点申请内存空间 
    		scanf("%d %s",&node->id,node->name);	//给数据域赋值 
    		end->next = node;					//让上一个节点的数据域指向当前节点 
    		end = node;     						//end指向当前节点,最终end指向尾节点 
    	}
    	end->next = NULL;                                   //给end的指针域置空 
    	return head;                                        //返回头节点的地址 
    }
    void print(struct Stu *head){
    	struct Stu *p = head;
    	int j =1;
    	p = p->next;  //不打印头节点的数据域中的值 
    	while(p != NULL){
    		printf("%d\t%d\t%s\n",j,p->id,p->name);
    		p = p->next;
    		j++;
    	}
    }

    前插法创建链表 --逆序输出

    struct link *create(int n){
         struct link *headnode ,*node;
         headnode = (struct link *)malloc(sizeof(struct link));   //为头节点申请内存 
    	 headnode ->next = NULL;     //让头节点的指针域置空 
    	 for(int i=0;i<n;i++){ 
    	 	node = (struct link *)malloc(sizeof(struct link));  //给新建节点申请内存 
    	 	scanf("%d",&node->data);       //新建节点数据域传值 
    	 	node->next = headnode->next;   //新建节点的数据域指向头节点 == 创建尾节点 
    	 	headnode->next = node;         //将新建节点数据域传给头节点 
    	 }	
    	 return headnode;  
    }

     

    删除节点

    void deleteNode(struct Stu *head,int n){         //删除n处的节点 
    	struct  Stu *p = head,*pr = head;
    	int i =0;
    	while(i<n&&p!=NULL){       //到达指定节点,此时p指向指定节点,pr指向上一节点 
    		pr = p;            //将p的地址赋值给pr
    		p = p->next;       //p指向下一节点
    		i++;
    	}
    	if(p!=NULL){               //当p不为空时,即p不能指向尾节点之后的节点
    		pr->next = p->next;
    		free(p);
    	} else{
    		printf("节点不存在!\n"); 
    	}
    } 

    我在这着重解释一下p->next = NULL和p!=NULL的区别,因为我刚开始也经常弄错!!

    • while(p->next != NULL) 循环结束时,此时p的位置是尾节点的位置,但如果用于输出函数的判断条件,则尾节点的数据不会输出。
    • while(p!=NULL) 循环结束时, 此时p指向的位置为尾节点的下一个节点,因为没有申请内存空间,所以是一个未知的区域。

    插入节点

    void insertNode(struct Stu *head,int n){    //插入节点 
    	struct Stu *p = head,*pr;
    	pr = (struct Stu*)malloc(sizeof(struct Stu));  //让pr指向新建节点申请的内存 
    	printf("input data:\n");
    	scanf("%d %s",&pr->id,pr->name);
    	int i = 0;
        //当插入位置是尾节点时,只要在尾节点后再插入一个节点,并让尾节点的指针域指向新建节点,新建节点的指针域置空
        while(i<n&&p!=NULL){             //使p指向将要插入节点的位置 
        	p = p->next;
    		i++;
    	}
    	if(p!=NULL){            //如果p没越界 
    		pr->next = p->next; //将新建节点的地址指向将要插入节点的后一个节点的地址 
    		p->next = pr;       //使插入节点指向新建节点 
    	}else{
    		printf("节点不存在!\n");
    	}
    }

    修改节点

    void change(struct Stu *head,int n){
    	struct Stu *p = head;
    	int i = 0;
    	while(i<n && p!=NULL){      //使p指向需修改节点 
    		p = p->next;
    		i++;
    	}
    	if(p != NULL){             
    	printf("请输入修改之后的值:\n");
    	scanf("%d %s",&p->id,p->name);	
    	}else{
    		printf("节点不存在!\n");
    	} 

    链表的逆序

     

    思路:假如此时链表有两个有效节点,头节点为0号,中间的节点为1号,尾节点为2号

    1.定义三个指针pf指向链表的头节点(0号),tmp,pb初始化为NULL

    2.让pb指向pf的下一个节点(1号),并将此时头节点的指针域置空(变为尾节点)

    3.第一次while循环,让tmp指向pb(1号),然后让pb指向下一个节点,再让tmp让1号节点的指针域(tmp->next = pf)指向pf(上一个节点)(0号),再将pf指向tmp(1号)(pf = tmp)

    4.第二次while循环,让tmp指向pb(2号),然后让pb指向下一个节点,此时pb==NULL,所以这是最后一次循环,再让tmp让2号节点的指针域(tmp->next = pf)指向pf(1号),再将pf指向tmp(2号)

     5.此时链表逆序完成,让头指针指向首节点(2号),返回头指针即可

                                                                              逆序后

    STU *link_reversed_order(STU *head)
    {
        STU *pf = NULL, *pb = NULL, *tmp = NULL; 
        pf = head;  //将头节点的地址赋值给pf 
        if(head == NULL) { //如果链表为空 
            printf("链表为空,不需要逆序!\n");
            return head;
        } else if(head->next == NULL) {  //如果只有一个节点 
            printf("链表只有一个节点,不需要逆序!\n");
            return head;
        } else {
            pb = pf->next;  //pb指向pf的下一个节点 
            head->next = NULL; //头节点的指针域置空(变为尾节点) 
            while(pb != NULL)	//当pb不为空时 
            {
                tmp = pb;	//将pb的地址赋值给temp 
                pb = pb->next; //pb指向下一个节点 
                tmp->next = pf;	//pb的上一个节点的指针域指向pf 
                pf = tmp;	//让pf指向tmp 
            }
            head = pf;
            return head;
        }    
    }*/

    所有操作

    #include <stdio.h> 
    #include <stdlib.h>
    struct Stu *create(int n);
    void print(struct Stu *head);
    void deleteNode(struct Stu *head,int n);
    void insertNode(struct Stu *head,int n);
    void change(struct Stu *head,int n);
    struct Stu{
    	int id;
    	char name[50];
    	struct Stu *next;
    };
    int main(){
    	int n,j,in,cn;
    	char c;
    	struct Stu *head = NULL;   //创建头指针 
    	printf("请输入你想要创建的节点个数:\n");
    	scanf("%d",&n);
    	head = create(n);
    	print(head);
    	while(true){
    	printf("请选择你想要执行的操作:\n");
    	printf("1.插入节点\n2.删除节点\n3.修改节点\n4.退出程序\n");
    	scanf(" %c",&c);
    	if(c =='1'){
    	printf("你想要在哪插入节点:\n");
    	scanf("%d",&in);
    	insertNode(head,in);
    	print(head); 
    	}else if(c == '2'){
    	printf("你想要删除哪个节点的数据:\n");
    	scanf("%d",&j);
    	deleteNode(head,j);
    	print(head);
    	}else if(c =='3'){
    	printf("你想要修改哪个节点的数据:\n");
    	scanf("%d",&cn);
    	change(head,cn);
    	print(head); 
    	}else if(c =='4'){
    		exit(0);
    	} 		
     } 
    }
    struct Stu *create(int n){
    	struct Stu *head,*node,*end;   						//定义头节点,普通节点,尾节点 
    	head = (struct Stu *)malloc(sizeof(struct Stu)); 	//给头节点申请内存 
    	//head->id = n;										//头节点的数据域保存链表的长度 
    	end = head;        									//若是空表,则头尾地址一致 
    	for(int i=0;i<n;i++){								//利用for循环向链表中添加数据 
    		node = (struct Stu *)malloc(sizeof(struct Stu));//给普通节点申请内存空间 
    		scanf("%d %s",&node->id,node->name);			//给数据域赋值 
    		end->next = node;								//让上一个节点的数据域指向当前节点 
    		end = node;     								//end指向当前节点,最终end指向尾节点 
    	}
    	end->next = NULL;                                   //给end的指针域置空 
    	return head;                                        //返回头节点的地址 
    }
    void print(struct Stu *head){
    	struct Stu *p = head;
    	int j =1;
    	p = p->next;  //不打印头节点的数据域中的值 
    	while(p != NULL){
    		printf("%d\t%d\t%s\n",j,p->id,p->name);
    		p = p->next;
    		j++;
    	}
    }
    void deleteNode(struct Stu *head,int n){         //删除n处的节点 
    	struct  Stu *p = head,*pr = head;
    	int i =0;
    	while(i<n&&p!=NULL){       //到达指定节点,此时p指向指定节点,pr指向上一节点 
    		pr = p;
    		p = p->next;
    		i++;
    	}
    	if(p!=NULL){
    		pr->next = p->next;
    		free(p);
    	} else{
    		printf("节点不存在!\n"); 
    	}
    } 
    void insertNode(struct Stu *head,int n){    //插入节点 
    	struct Stu *p = head,*pr;
    	pr = (struct Stu*)malloc(sizeof(struct Stu));  //让pr指向新建节点申请的内存 
    	printf("input data:\n");
    	scanf("%d %s",&pr->id,pr->name);
    	int i = 0;
        //当插入位置是尾节点时,只要在尾节点后再插入一个节点,并让尾节点的指针域指向新建节点,新建节点的指针域置空
        while(i<n&&p!=NULL){             //使p指向将要插入节点的位置 
        	p = p->next;
    		i++;
    	}
    	if(p!=NULL){            //如果p没越界 
    		pr->next = p->next; //将新建节点的地址指向将要插入节点的后一个节点的地址 
    		p->next = pr;       //使插入节点指向新建节点 
    	}else{
    		printf("节点不存在!\n");
    	}
    }
    void change(struct Stu *head,int n){
    	struct Stu *p = head;
    	int i = 0;
    	while(i<n && p!=NULL){      //使p指向需修改节点 
    		p = p->next;
    		i++;
    	}
    	if(p != NULL){             
    	printf("请输入修改之后的值:\n");
    	scanf("%d %s",&p->id,p->name);	
    	}else{
    		printf("节点不存在!\n");
    	} 	 
    } 

     

     

    展开全文
  • C语言链表

    2020-11-14 12:02:36
    数组和结构体的缺点:地址连续,增加删除元素困难,删除一个空间后把后面的所有数据往前移动,运算量大 链表解决了上述问题 A指针变量存放b的地址,b存放c的地址 给整个数据的存储添加很多的灵活性 比如说要删除b,...

    什么是链表

    数据结构→数据存放的思想

    数组 数据的集合
    数组是在内存连续的一段空间存放数据
    结构体
    大小和数组不一样,可以放很多数据

    数组和结构体的缺点:地址连续,增加删除元素困难,删除一个空间后把后面的所有数据往前移动,运算量大

    链表解决了上述问题
    在这里插入图片描述

    A指针变量存放b的地址,b存放c的地址
    给整个数据的存储添加很多的灵活性
    比如说要删除b,只需把a的地址指向c即可

    例子
    在这里插入图片描述

    此时的数组是一串连续的数据
    而这三个结构体是互相没有联系的三个地址中存放了数据
    若要他们有联系,那么需要在他们中引入地址
    在这里插入图片描述

    指针存放的是地址,所以需要取址符
    输出
    在这里插入图片描述

    这样输出也可以
    在这里插入图片描述
    在这里插入图片描述

    有了结构体的next指针
    可以将三个离散的地址联系起来,并且相较于数组,删改更方便
    可以通过链表头——t1,就可以遍历整个链表

    链表的遍历

    提到数组的缺点
    在这里插入图片描述

    这个数组只有三个空间,如果还要加一个空间,就造成了数组溢出
    而链表就非常简单,只要在后面链接一个就可以了
    在这里插入图片描述

    遍历链表的函数体
    在这里插入图片描述

    形式参数为一个结构体指针
    这个指针作为链表的头,用point指针控制输出
    用point指针先将链表头中的数据输出,
    然后将point赋值为链表头中的next地址,即下一个链的地址
    由此反复,直到下一个为初始值设置的NULL结束
    这里要传递的实际参数是链表头的地址,如果穿链表头的next,就相当于传了第二个地址,那么链表头的数据data就没有输出
    全代码
    在这里插入图片描述
    在这里插入图片描述

    复杂的业务场景需要很多的数据类型,就像学生成绩实例一样。所以需要用到结构体
    链表就是在结构体的基础上多了一个存放下个地址的指针。

    链表的查找

    链表的个数只需要设置一个num来记录head的地址变化的次数即可
    函数体如下
    在这里插入图片描述

    Num初始值设为0,第一次地址变化成第二个链的地址,记1
    第二次地址变化成第三个链的地址,记2
    第三次地址变化成第四个链的地址,记3
    第四次地址变化成第四个链中存放的NULL地址,记4

    通过链表遍历来一个个对比数据,如果数据对了就返回1,没找到就返回0,然后在主函数中判断
    函数体
    在这里插入图片描述

    主函数中判断部分
    在这里插入图片描述

    结果
    在这里插入图片描述

    链表的插入

    比如要在第三个链的后面插入一个数据
    那么只要先找到3
    再把3所在链存放的地址指向要加的那个数据的地址
    然后把所加数据中的指针指向原来第三个链后面的链的地址
    函数体部分
    在这里插入图片描述

    在实际编写中遇到几个问题
    在while循环中,是用来遍历整个链表,如果把没有找到就退出编写一个else语句放在if后面while的里面,就会造成,程序只找一次,只对链表头里面的数据进行判断
    在这里插入图片描述

    如果先把找到3,3链的next地址给要插入的链,那么此时这个next的地址就是要插入链的地址,可以看到结果最后无限输出插入的10
    在这里插入图片描述
    在这里插入图片描述

    如果想指向next地址存放的next,
    此时的head->next已经被赋值成要插入的point的next,就是说找到的数据后面一个链是要插入的那个链的地址
    插入链的next是初始化的值NULL
    那么在编写的遍历输出函数中,遇到null就停止输出
    在这里插入图片描述

    最后一个值就没有输出来
    所以正确的做法是先将找到的3所在链中的next,把这个next给到要插入的链的next
    再把找到的3所在链中的next赋值成要插入的链的地址

    于是在最开始所述的插入的流程有些许错误
    插入的流程应为
    先找在哪一个数据后面插入
    把找到的数据所在的链中存放的next地址给到要插入的链中存放的next地址
    最后再把找到的数据所在的链存放的next地址赋值成要插入的地址

    全代码
    在这里插入图片描述

    在指定节点前方插入数据

    在前方插入需要考虑是不是在链表头的前面插入,如果在,那么新的链表头变成插入的那个数
    函数体
    在这里插入图片描述

    结果
    在这里插入图片描述

    在实际情况中需要进行判断,判断是不是头节点

    插入函数代码
    在这里插入图片描述

    形式参数有 一个会在他前面插东西的数据,头节点的地址,要插入的节点的地址
    先保存头节点的位置
    判断这个节点是不是头节点,头节点和其他情况不同
    若不是头节点,则遍历找到前面要插新节点的数据(这里的数据用前面一个节点存放的next指针指向的数据来判断,这样就可以插在前面一个数据的后面,比较简单)
    先把找到的数据的前一个节点存放的next指针赋值给新节点的next,再变动找到的数据的前一个节点存放的next指针,把他赋值为新节点的地址
    返回head的原始地址,因为在遍历的时候head会变化,如果直接返回head,就会使找到的数据的前一个节点变为头节点,这个新头节点的前几项会消失
    在这里插入图片描述
    在这里插入图片描述

    全部代码
    在这里插入图片描述

    运行结果
    在这里插入图片描述

    删除指定节点

    分两种情况
    一种删除的是头节点
    这种情况下,直接把原头节点中存放的next作为新的头节点
    另一种删除的不是头节点
    这种情况下,先找到要删除的值,把上一个值的next赋值成这个值所在节点存放的next值
    在这里插入图片描述

    结果
    在这里插入图片描述

    全代码
    在这里插入图片描述

    动态链表创建

    头插法

    像堆栈一样,把最先进来的数据当作链表的头,那么只要把函数体当中的返回值设成新的表头地址就可以了
    函数体
    在这里插入图片描述

    这里要注意的是,新开辟的指针需要空间,而且空间需要结构体指针,这样才能有int data和next指针
    如果不给指针空间,则会造成如下段错误
    在这里插入图片描述

    使用malloc函数需要调用stdlib头文件
    在这里插入图片描述

    结果如下
    在这里插入图片描述

    可以循环插入,链表从无到有
    在这里插入图片描述

    这时候一开始链表是没有的,head指针是初始化的NULL,这时就需要做一个判断
    在这里插入图片描述

    并且在主函数中做一个循环来输入数据
    在这里插入图片描述

    优化头插法

    在主函数中只有调用函数和最开始传递的值,使代码尽量简洁
    在这里插入图片描述
    在这里插入图片描述

    尾插法

    插在链表的最后面。一开始链表头是空的,所以链表头指针head就赋值为new。后来不是空了,那么需要一个指针来移动,head链表头不能动,那么就定义一个结构体指针获取head的值,来代替head移动,每一次都移动到链表的最后,并把这个指针的next值赋值为new。
    函数体
    在这里插入图片描述

    展开全文
  • 本题要求实现一个函数,实现对单循环链表中奇数和偶数结点的移动,要求奇数在前面,偶数在后面,且结点之间的相对顺序不变。 1.建立结构体并定义宏变量。代码如下: 项目场景: 提示:这里简述项目相关背景: 例如:...

    本题要求实现一个函数,实现对单循环链表中奇数和偶数结点的移动,要求奇数在前面,偶数在后面,且结点之间的相对顺序不变。

    先建立结构体。
    PNode、LinkList分别表示结点和链表,重定义int为DataType(方便修改数据类型)

    typedef int DataType;
    struct Node {
    	DataType	  data;
    	struct Node* next;
    };
    typedef struct Node* PNode;
    typedef struct Node* LinkList;
    

    创建一个空链表,输入数据

    LinkList CreateList()//用尾指针创建链表
    {
    	LinkList head = (LinkList)malloc(sizeof(struct Node));
    	PNode cur = NULL;//cur表示当前的指针
    	PNode tail = head;
    	DataType data;
    	scanf_s("%d", &data);
    	while (data != -1){
    		cur = (struct Node*)malloc(sizeof(struct Node));
    		cur->data = data;
    		tail->next = cur;
    		tail = cur;
    		scanf_s("%d", &data);
    	}
    	tail->next = head;
    	return tail;
    }
    

    接下来就是关键的结点移动啦

    PNode  Move_Odd_Even(LinkList tail)
    {
        PNode p = tail->next, r, q;
        p = p->next;
        if (p->data % 2 != 0){
            r = p;
            q = p;
            p = p->next;
        }
        else{
            r = tail->next;
            q = p;
            p = p->next;
        }
        while (p != tail->next){
            if (p->data % 2 != 0) {//奇数结点
                if (r->next != p) {
                    q->next = p->next;
                    if (p == tail){
                        tail = q;
                    }
                    p->next = r->next;
                    r->next = p;
                    r = p;
                    q = p;
                    p = p->next;
                }
                else{
                    r = p;
                    p = p->next;
                }
            }
            else{//偶数结点
                q = p;
                p = p->next;
            }
        }
        return tail;
    }
    

    最后是完整代码和运行结果:

    #include<stdio.h>
    #include<stdlib.h>
    
    typedef int DataType;
    struct Node {
    	DataType	  data;
    	struct Node* next;
    };
    typedef struct Node* PNode;
    typedef struct Node* LinkList;
    
    LinkList CreateList()
    {
    	LinkList head = (LinkList)malloc(sizeof(struct Node));
    	PNode cur = NULL;//cur表示当前的指针
    	PNode tail = head;
    	DataType data;
    	scanf_s("%d", &data);
    	while (data != -1){
    		cur = (struct Node*)malloc(sizeof(struct Node));
    		cur->data = data;
    		tail->next = cur;
    		tail = cur;
    		scanf_s("%d", &data);
    	}
    	tail->next = head;
    	return tail;
    }
    void print(LinkList tail)
    {
    	PNode  head = tail->next;
    	PNode p = head->next;
    	while (p != head){
    		printf("%d ", p->data);
    		p = p->next;
    	}
    }
    void DestoryList_Link(LinkList tail)
    {
    	PNode pre = tail->next;
    	PNode p = pre->next;
    	while (p != tail){
    		free(pre);
    		pre = p;
    		p = pre->next;
    	}
    	free(pre);
    	free(tail);
    }
    PNode  Move_Odd_Even(LinkList tail)
    {
        PNode p = tail->next, r, q;
        p = p->next;
        if (p->data % 2 != 0){
            r = p;
            q = p;
            p = p->next;
        }
        else{
            r = tail->next;
            q = p;
            p = p->next;
        }
        while (p != tail->next) {
            if (p->data % 2 != 0){//奇数结点
                if (r->next != p){
                    q->next = p->next;
                    if (p == tail){
                        tail = q;
                    }
                    p->next = r->next;
                    r->next = p;
                    r = p;
                    q = p;
                    p = p->next;
                }
                else{
                    r = p;
                    p = p->next;
                }
            }
            else{//偶数结点
                q = p;
                p = p->next;
            }
        }
        return tail;
    }
    int main()
    {
    	LinkList tail = NULL;
    	LinkList p = NULL;
    	tail = CreateList();
    	p = Move_Odd_Even(tail);
    	print(p);
    	DestoryList_Link(tail);
    	return 0;
    }
    

    运行结果:
    在这里插入图片描述
    ps:如果想借鉴本篇代码来过实验的话,一定要记得修改print函数哈,不然可能会出现格式错误的问题哦。

    编译器:visual studio 2019,使用其他编译器的小伙伴记得修改相关函数哦(例如scanf函数)

    展开全文
  • C语言链表5

    2021-06-08 09:16:57
    5、双链表的引入和基本实现 ...局限性主要体现在单链表只能经由指针单向移动(一旦指针移动过某个节点就无法再回来,如果要再次操作这个节点除非从头指针开始再次遍历一次),因此单链表的某些操作就
  • void DestoryList_Link(LinkList tail) { PNode pre = tail->next; PNode p = pre->next; while (p != tail) { free(pre); pre = p; p = pre->next; } free(pre); free(tail);...}
  • 基于VC6++开发环境,使用自定义的循环链表存储蛇身,使用绘图函数绘制蛇身,使用键盘控制蛇的移动方向。
  • C语言链表各类操作详解

    千次阅读 2019-01-23 23:17:09
    i 时,遍历链表,让p的指针向后移动,不断指向下一结点,j累加1; 若到链表末尾p为空,说明第i个元素不存在; 否则查找成功,返回结点p的数据。 /*初始条件:顺序线性表L已存在,1≤i≤ListLength(L)*/ /*操作结果...
  • 链表是实现表结构的一种实现方式,链表允许数据节点在内存中不连续存储,从而避免了线性表在插入和删除操作中表的部分或者全部需要整体移动的线性开销。链表由一系列在内存中不连续的存在的结构组成,结构见图1,每个...
  • C语言链表详解(通俗易懂,超详细)

    千次阅读 多人点赞 2020-03-01 15:56:07
    插入 删除 不需移动其他元素, 只需改变指针;2:链表各个节点在内存中空间不要求连续!空间利用率高 缺点:1.访问数组元素效率低;2:数组的存储空间连续,内存空间利用率低 1.单链表 通俗讲就是结构体变量与结构体变量...
  • 2021-06-16-C语言链表

    2021-06-16 20:21:38
    数组修改起来不方便,需要进行大量移动,但是数组有下标,查找第几号数据比较方便,链表对于数组操作比较快,但是不能按下标进行快速查找。 拿到链表的第一个节点,就相当于拿到整个链表。 如果链表没有头部,在...
  • 一步一步教你从零开始写C语言链表(超详细) 杨源鑫 嵌入式系统工程师、物联网创业合伙人,业务经理兼产品经理 285 人赞同了该文章 为什么要学习链表? 链表主要有以下几大特性: 1、解决数组无法存储多种数据...
  • 为了避免插入和删除的线性开销,我们允许表可以不连续存储,否则表的部分或全部需要整体移动。 节点构成 表元素 指向包含该元素后继元的结构的指针 最后一个单元的 Next 指针指向 NULL, 该值由C定义且不能与其他...
  • listNode *FindNtoTail(listNode *head,unsigned int n) { if(head == NULL || n == 0) return NULL;listNode *pHead = head; listNode *pBehind = NULL; unsigned int i;...// pHead 向前移动 n-1 for(i
  • 为什么要学习链表链表主要有以下几大特性: 1、解决数组无法存储多种数据类型的问题。 2、解决数组中,元素个数...3、数组移动元素的过程中,要对元素进行大范围的移动,很耗时间,效率也不高。 先来感性的...
  • 为什么要学习链表?...3、数组移动元素的过程中,要对元素进行大范围的移动,很耗时间,效率也不高。 先来感性的认识一下链表,我们先来认识下简单的链表: 从这幅图我们得出以下信息: 这个简单链表的构成:...
  • 下面代码主要实现链表的创建,插入,删除,并且能将两个年龄递增链表进行合并成递减链表 然而在插入和删除操作中gets函数无法起作用,strcmp函数也出现位置冲突报错。。功力不足实在解决不了。。跪求大神解答。。...
  • C语言实现链表贪吃蛇

    2020-12-17 04:41:46
    C语言链表写的贪吃蛇(程序设计时做的,做的不好大佬勿喷) 借助游戏内容分析贪吃蛇所需的功能主要包括这几块: 1.移动光标模块 2.打印地图模块和基本规则信息 读取最高分文件 3.打印初始蛇模块 打印时给予蛇的...
  • 静态链表的初始长度一般是固定的,在做插入和删除操作时不需要移动元素,仅需修改指针。动态链表是相对于静态链表而言的,一般地,在描述线性表的链式存储结构时如果没有特别说明即默认描述的是动态链表
  • void print(LinkList head) { PNode p = head->next; while (p) { printf("%d ", p->data); p = p->next; } }

空空如也

空空如也

1 2 3 4 5 ... 19
收藏数 371
精华内容 148
关键字:

c语言链表移动

c语言 订阅