数据结构-循环链表


    循环链表 

        单链表和双链表 
            如果遍历到最后一个节点, 需要返回到第一个节点, 则需要重新遍历一次 


        循环链表 "首尾相连"

            单链表: 
                最后一个节点的next 指向 第一个节点  -->  单向循环链表 

            双向链表 
                最后一个节点的next 指向 第一个节点 
                第一个节点的prev 指向 最后一个节点  -->  双向循环链表 


    1) 单向循环链表 

            构造: 
                typedef int ElemType;   //数据元素中 数据的类型
                typedef struct node             //数据元素的类型
                {
                    ElemType data;          //数据域  --> 存储数据 
                    struct node * next;     //指针域  --> 保存逻辑上的关系(下一个)
                } Node;

                //链表的头结点的类型 
                typedef struct List 
                {
                    Node * first;   //指向链表的第一个节点(head)
                    Node * last;    //指向链表的最后一个节点(tail)
                    int num;        //保存链表的数据元素的个数
                    //... 
                } List ;

            基本操作:  增删改查 

        
            注意: 
                1) 首尾相连 
                    if( list->num != 0 )    //链表不为空 
                    {
                        list->last->next  =  list->first;
                    }

                2)遍历时, 循环条件变为节点个数
                    int n = list->num;
                    while( n-- )    //遍历 
                    {
                    }

            练习: 
                1)根据用户输入的数据的顺序, 创建一个带头结点的单向循环链表
                    并将新链表返回, 然后再打印输出 

                    CircleLinkedListWithHead.c   /  CircleLinkedListWithHead.h 

List * create_Circle_list()
{
    //1.创建一个头结点, 并初始化
    List * list = (List*)malloc( sizeof(List) ); 
    list->first = NULL;	
    list->last = NULL;
    list->num = 0;

    ElemType d;
    while(1)
    {
    	//2.从键盘上获取数据  
    	scanf("%d", &d );
    	if( d == 0 )
    	{
			break;
    	}

		//创建新数据节点 并初始化
    	Node * pnew = (Node*)malloc( sizeof(Node) );
    	pnew->data = d;
    	pnew->next = NULL;

    	//3.把新节点 加入到链表中
    	if( list->first == NULL )  //从无到有
    	{
    		list->first = pnew;
    		list->last = pnew;
    	}
		else 	//从少到多
		{
			//尾插法
			list->last->next = pnew;
			list->last = pnew;

			//头插法
			//pnew->next = list->first;
			//list->first = pnew;
		}

		list->num ++;	//元素个数+1
		
	}

	//4.首尾相连 ☆☆☆
	if( list->num != 0 )	//链表不为空
	{
		list->last->next = list->first;
	}
	
    //5.将新链表返回(返回头结点)
    return list;
}
void print_list( List * list )
{
	if( list == NULL )
	{
		printf("list is NULL \n");
		return ;
	}

	Node * p = list->first;		//遍历指针 
	int n = list->num ;		
	while( n-- )
	{
		printf("%d ", p->data );
		p = p->next;
	}
	putchar('\n');

	printf("num = %d\n\n", list->num );
}

                    main.c  

                        #include "CircleLinkedListWithHead.h"

                        int main()
                        {

                        }


                2)根据用户输入的数据的顺序, 逆序创建一个带头结点的单链表 
                    并将新链表返回 

                        //头插法 


                3) 创建一个带头结点的升序链表 

List *create_sort_Circle_list()
{
	//1.创建一个头结点, 并初始化
    List * list = (List*)malloc( sizeof(List) ); 
    list->first = NULL;	
    list->last = NULL;
    list->num = 0;

    ElemType d;
    while(1)
    {
    	//2.从键盘上获取数据  
    	scanf("%d", &d );
    	if( d == 0 )
    	{
			break;
    	}

		//创建新数据节点 并初始化
    	Node * pnew = (Node*)malloc( sizeof(Node) );
    	pnew->data = d;
    	pnew->next = NULL;

    	//3.把新节点 加入到链表中
    	if( list->first == NULL )  //从无到有
    	{
    		list->first = pnew;
    		list->last = pnew;
    	}
		else 	//从少到多
		{
			//(找到第一个比它大的数的前面进行插入,没有找到就尾插)
			Node * p = list->first;		//遍历指针 
			Node * pre = NULL;			//记录p的前一个

			int n = list->num;	

			while( n-- )
			{
				if( p->data > pnew->data )
				{
					break;	//找到了
				}
				pre = p;
				p = p->next;
			}

			if( n == -1 )	//没有找到
			{
				//尾插 
				list->last->next = pnew;
				list->last = pnew;				
			}
			else 	//找到了
			{
				if( p == list->first )	//比第一个节点还要小,那么就头插
				{
					//头插
					pnew->next = list->first;
					list->first = pnew;
				}
				else 
				{
					//中间插入
					pre->next = pnew;
					pnew->next = p;
				}
			}
			
		}

		list->num ++;	//元素个数+1
		
	}
	
	//4.首尾相连 ☆☆☆
	if( list->num != 0 )	//链表不为空
	{
		list->last->next = list->first;
	}
	
    //5.将新链表返回(返回头结点)
    return list;
}


                
                4)在链表中查找所有值为x的节点, 将其全部删除 
                    若没有找到就不删除, 将新链表返回 

List * delete_all_x_node( List * list , ElemType x )
{
	if( list == NULL )
	{
		printf("list is NULL \n");
		return NULL;
	}

	//1.查找值为x的节点
	Node * p = list->first;		//遍历指针 
	Node * pre = NULL;			//记录p的前一个

	int n = list->num;
	
	while( n-- )	//遍历, 查找值为x的节点
	{
		if( p->data == x )
		{
			//找到了
			list->num --;	//元素个数 -1 
		
			//2.执行删除操作 
			if( p == list->first )	//删除第一个
			{
				list->first = list->first->next;
				p->next = NULL;
				free( p );

				if( list->num == 0 )	// list->first == NULL 
				{
					//把链表仅剩的一个节点都删除了
					list->last = NULL;
				}

				p = list->first;	
				
			}
			else if( p == list->last )		//删除最后一个
			{
				list->last = pre;
				pre->next = NULL;
				free( p );

				p = NULL;
				
			}
			else	//删除中间  
			{
				pre->next = p->next;
				p->next = NULL;
				free( p );

				p = pre->next;
			}
		}
		else 
		{
			pre = p;
			p = p->next;
		}
	}

	//3.首尾相连 ☆☆☆
	if( list->num != 0 )	//链表不为空
	{
		list->last->next = list->first;
	}
	
    //4.将新链表返回(返回头结点)
    return list;

}


                
                5)在链表中查找值为x的节点, 将所有值为x的节点的值 修改成a 
                    若没有找到就不修改, 将新链表返回 

List * update_node( List * list , ElemType x , ElemType a )
{
	if( list == NULL )
	{
		printf("list is NULL \n");
		return NULL;
	}

	Node * p = list->first;		//遍历指针 

	int n = list->num;
	
	while( n-- )
	{
		if( p->data == x )
		{
			p->data = a;
		}
		p = p->next;
	}

	return list;
}

                6)销毁一条链表 
                        遍历 先释放每一个数据节点
                            再释放头结点

                    

void destroy_list( List * list )
{
	if( list == NULL )
	{
		printf("list is NULL \n");
		return ;
	}

	Node * p = list->first;		//遍历指针 

	int n = list->num;
	
	while( n-- )
	{
		//遍历 先释放每一个数据节点
		list->first = list->first->next;
		p->next = NULL;
		free( p );

		p = list->first;
	}

	//再释放头结点
	list->num = 0;
	//list->first = NULL;	//上面的循环 已经把first置NULL了
	list->last = NULL;
	free( list );
}

    2)双向循环链表 

        构造: 
                //数据元素的类型 
                typedef int ElemType;       //数据元素中数据的类型
                typedef struct node 
                {
                    ElemType data;          //数据域 --> 存储数据 
                    struct node * next;     //指针域 --> 保存逻辑上的下一个
                    struct node * prev;                 //保存逻辑上的上一个
                } DNode;

                //头结点的类型 
                typedef struct List 
                {
                    struct node * first;      //指向链表的第一个节点
                    struct node * last;       //指向链表的最后一个节点
                    int num;            //记录链表中元素的个数 
                    //...
                } DList;

        基本操作: 
                增删改查 

        注意: 
            (1)首尾相连 
                if( list->num != 0 )    //链表不为空 
                {
                    list->last->next  =  list->first;
                    list->first->prev =  list->last;
                }

            (2)遍历时,循环条件变为节点个数
                int n = list->num;
                while( n-- )    //遍历 
                {
                }


        练习: 
            1) 根据用户输入的数据的顺序, 创建一个带头结点的双向循环链表
                将新链表返回,  然后再打印输出

                CircleBothwayLinkedListWithHead.c   /   CircleBothwayLinkedListWithHead.h  

                    

DList * create_Circle_bothway_list()
{
    //1.创建一个头结点, 并初始化
	DList * list = (DList *)malloc( sizeof(DList) );
	list->first = NULL;
	list->last = NULL;
	list->num = 0;

    ElemType d;
    while(1)
    {
	    //2.获取数据 
		scanf("%d", &d );
		if( d == 0 )
		{
			break;
		}
	    
	    //3.创建数据节点,并初始化 
		DNode * pnew = (DNode*)malloc( sizeof(DNode) );
		pnew->data = d;
		pnew->next = NULL;
		pnew->prev = NULL;
	    
	    //4.把新节点 加入到链表中 
		if( list->first == NULL )	//从无到有 
		{
			list->first = pnew;
			list->last = pnew;
		}
		else	//从少到多 
		{
			//尾插法 
			list->last->next = pnew;
			pnew->prev = list->last;
			list->last = pnew;

			//头插法
			//pnew->next = list->first;
			//list->first->prev = pnew;
			//list->first = pnew;
			
		}
		list->num ++;	//元素个数 +1 
	}

	//5.首尾相连 
	if( list->num != 0 )	//链表不为空
	{
		list->last->next = list->first;
		list->first->prev = list->last;
	}	

    //6.将新链表返回 
    return list;
}

                    

void print_list( DList * list )
{
	if( list == NULL )	//链表不存在 
	{
		printf("list is NULL \n");
		return ;
	}

	//正向
	DNode * p = list->first;	//遍历指针 

	int n = list->num;
	
	while( n-- )
	{
		printf("%d ", p->data );
		p = p->next;
	}
	putchar('\n');

	//倒序
	p = list->last;
	n = list->num;
	while( n-- )
	{
		printf("%d ", p->data );
		p = p->prev;
	}
	putchar('\n');

	printf("num = %d\n\n", list->num );
	
}

                main.c  
                    #include "CircleBothwayLinkedListWithHead.h"

                    int main()
                    {
                    }


            2)根据用户输入的数据的顺序, 逆序创建一个带头结点的双向链表
                将新链表返回,  然后再打印输出 

                    //头插法 

            3)根据用户输入的数据, 创建一个有序的带头结点的双向链表 (升序), 将新链表返回

                /*
                    分情况讨论:
                        比第一个还要小, 头插法
                        比最后一个还要大, 尾插法
                        中间插入, 找到第一个比它大的数的前面进行插入

                            注意: 中间插入时, 先赋值pnew的成员, 再去修复前后的关系 
                                                ☆☆☆
                */

DList * create_sort_Circle_bothway_list()
{
	//1.创建一个头结点, 并初始化
	DList * list = (DList *)malloc( sizeof(DList) );
	list->first = NULL;
	list->last = NULL;
	list->num = 0;

    ElemType d;
    while(1)
    {
	    //2.获取数据 
		scanf("%d", &d );
		if( d == 0 )
		{
			break;
		}
	    
	    //3.创建数据节点,并初始化 
		DNode * pnew = (DNode*)malloc( sizeof(DNode) );
		pnew->data = d;
		pnew->next = NULL;
		pnew->prev = NULL;
	    
	    //4.把新节点 加入到链表中
	    if( list->first == NULL )	//从无到有
	    {
			list->first = pnew;
			list->last = pnew;
	    }
	    else	//从少到多 
	    {
	    	/*
				分情况讨论:
					比第一个还要小, 头插法
					比最后一个还要大, 尾插法
					中间插入, 找到第一个比它大的数的前面进行插入
	    	*/
			DNode * p = list->first;		//遍历指针 
			
			int n = list->num;
			
			while( n-- )
			{
				if( p->data > pnew->data )
				{
					break; 	//找到了
				}
				p = p->next;
			}

			if( n == -1 )	//没找到 
			{
				//尾插法
				list->last->next = pnew;
				pnew->prev = list->last;
				list->last = pnew;
			}
			else	//找到了 
			{
				if( p == list->first )	
				{
					//头插法
					pnew->next = list->first;
					list->first->prev = pnew;
					list->first = pnew;
				}
				else 
				{
					//中间插入
					//注意: 先赋值pnew的成员, 再去修复前后的关系
					pnew->next = p;
					pnew->prev = p->prev;
					p->prev->next = pnew;
					p->prev = pnew;
				}
			}
	    }
		list->num ++;	//元素个数 +1
	    
	}

	//5.首尾相连 
	if( list->num != 0 )	//链表不为空
	{
		list->last->next = list->first;
		list->first->prev = list->last;
	}

	//6.将新链表返回
	return list;
}


     
            4)在双向链表中找到值为x的节点, 将其全部删除 
                如果没有找到 就不删除, 将新链表返回 

                    注意: 删除中间节点时, 先去修复前后的关系, 再去断开自身的连接  
                                                ☆☆☆

DList * delete_all_x_node( DList * list, ElemType x )
{
	if( list == NULL )	//链表不存在 
	{
		printf("list is NULL \n");
		return NULL;
	}

	//1.找到值为x的节点
	DNode * p = list->first;	//遍历指针 
	
	int n = list->num;

	while( n-- )
	{
		if( p->data == x )
		{
			//找到了,就删除(分情况讨论)
			list->num --;	//元素个数 -1 

			if( p == list->first )	//删除第一个节点 (要考虑只剩下一个节点的情况)
			{
				list->first = list->first->next;
				if( list->first != NULL )	// list->num != 0 
				{
					list->first->prev = NULL;
				}
				p->next = NULL;

				free( p );

				p = list->first;	//p从下一个开始再去找

				//当唯一的一个节点也删除了  	
				if( list->first == NULL )	// list->num == 0
				{
					list->last = NULL;
				}
			}
			else if( p == list->last )	//删除最后一个节点
			{
				list->last = list->last->prev;
				if( list->last != NULL )
				{
					list->last->next = NULL;
				}
				p->prev = NULL;

				free( p );
				
				p = NULL;	//已经删到末尾了,就不需要继续了
			}
			else 	//删除中间的节点
			{
				DNode * temp = p->next;

				//先去修复前后的关系, 再去断开自身的连接  
				p->prev->next = p->next;
				p->next->prev = p->prev;
				p->next = NULL;
				p->prev = NULL;

				free( p );

				p = temp;	//继续往下找
			}
			
		}
		else 
		{
			//没找到. 就继续往下找 
			p = p->next;
		}
	}

	//5.首尾相连 
	if( list->num != 0 )	//链表不为空
	{
		list->last->next = list->first;
		list->first->prev = list->last;
	}

	//将新链表返回
	return list;
}

            5)在双向链表中找到值为x的节点, 将所有值为x的节点的值 修改成a 
                没有找到就不修改, 将新链表返回 

DList * update_node( DList * list, ElemType x , ElemType a )
{
	if( list == NULL )	//链表不存在 
	{
		printf("list is NULL \n");
		return NULL;
	}

	DNode * p = list->first;		//遍历指针 

	int n = list->num;
	
	while( n-- )
	{
		if( p->data == x )	//找到了
		{
			p->data = a;
		}
		p = p->next;
	}

	return list;
}

            6)销毁一条双向链表
                    先释放每一个数据节点 
                    再去释放头结点

void destroy_list( DList * list )
{
	if( list == NULL )	//链表不存在 
	{
		printf("list is NULL \n");
		return ;
	}

	DNode * p = list->first;	//遍历指针 

	int n = list->num;

	//先释放每一个数据节点 
	while( n-- )
	{
		list->first = list->first->next;
		if( list->first != NULL )
		{
			//如果是删除最后一个节点时, first再往下走就已经为NULL了,
			//再去访问这个prev就会产生"段错误"
			list->first->prev = NULL;
		}
		p->next = NULL;

		free(p);

		p = list->first;
	}

    //再去释放头结点
    list->first = NULL;
    list->last = NULL;
    list->num = 0;
    free( list );
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值