zoukankan      html  css  js  c++  java
  • 数据结构学习笔记(5.线性表之双向循环链表)

    在前两节的基础上,实现双向循环链表。

    本节知识点:

    1.双向循环链表的结构:

    上面就是双向循环链表的结构图,对于双向链表的插入有3个位置,A、B、C。

    但是在插入第一个元素的时候(其实插入第一个元素的时候,就是循环建立的时候),有些特殊,所以就画了一个图,如下:

    本节代码:

    DcLinkList.c:

    /*******************************************************************************************************
    文件名:DcLinkList.c
    头文件:DcLinkList.h 
    时间: 2013/08/26
    作者: Hao
    功能:  可以复用 带有增 删 改 查 功能的双向循环链表
    难道: 1.typedef struct Str_DcLinkList DcLinkListNode;  //这个结构体是链表的真身 
    		struct Str_DcLinkList   //每一个链表元素的结构都会包含这个结构  因为当给链表元素强制类型 
    		{                     //转换成(DcLinkListNode* )的时候  其实就是要开始对每个元素中的 DcLinkListNode进行赋值了 
    			DcLinkListNode* next;
    		}; 
    		这个链表结构在链表元素中起到的作用 是本节的难点 
    		2.切记一个问题  就是已经是链表中元素的 千万不要再往链表中添加了 否则链表一定出现无穷的错误 
    		3.对于pos值的问题  add、get、del三个函数中 的链表都是 从1开始的到length  0是链表头 
    						  在add函数中pos为0的时候是和pos为1的情况是一样的  都是头插法  0~~~~~无穷大 
    		                  在get函数中pos为0的时候是获得链表头 地址      0~~~~~length 
    						  在del函数中pos为0的时候是无效的 del失败       1~~~~~length 
    *******************************************************************************************************/
    #include <stdio.h>
    #include <stdlib.h>
    #include <malloc.h>
    #include "DcLinkList.h"
    
    typedef struct str_list_head  //这个是链表头 其实也可以当作一个没有前驱的 链表元素 元素的内容是链表长度 
    {
    	//DcLinkListNode* next;
    	DcLinkListNode head; //这个参数要特别重视 每一个链表元素结构的第一个参数一定是 DcLinkListNode
    	                   //因为在寻找链表元素后继的时候 其实就是将链表元素强制类型转换成 DcLinkListNode*  然后给next进行赋值 其实就是给 DcLinkListNode变量赋值 
    	DcLinkListNode* slider; 
    	int length; //链表长度 
    }list_head;
    
    /*******************************************************************************************************
    函数名: Creat_DcLinkListHead
    函数功能:创建一个链表的链表头 并给链表头分配空间
    参数: void
    返回值:ret 成功返回链表头地址  失败返回NULL 
    *******************************************************************************************************/
    DcLinkList* Creat_DcLinkListHead(void)
    {
    	list_head* ret = NULL;
    	ret = (list_head* )malloc( sizeof(list_head)*1 );
    	if(NULL != ret) //malloc分配成功 
    	{
    		ret->length = 0;
    		//ret -> next = NULL;
    		ret->head.next = NULL;
    		ret->head.pre = NULL;
    		ret->slider = NULL;
    	}
    	return (DcLinkList* )ret; 
    }
    
    /*******************************************************************************************************
    函数名:Destroy_DcLinkListHead
    函数功能:释放一个链表头指针 
    参数:DcLinkList* head 链表头指针 
    返回值: ret 释放成功返回1  释放失败返回0 
    *******************************************************************************************************/
    int Destroy_DcLinkListHead(DcLinkList* head)
    {
    	int ret = 0; 
    	list_head* lhead = (list_head* )head;
    	if( NULL != lhead )
    	{
    		free(lhead);
    		ret = 1;
    	}
    	return ret;
    }
    
    /*******************************************************************************************************
    函数名:Get_Length
    函数功能:获得链表的长度 
    参数: DcLinkList* head 链表头指针 
    返回值: ret 成功返回链表长度  失败返回0 
    *******************************************************************************************************/
    int Get_Length(DcLinkList* head) 
    {
    	int ret = 0;
    	list_head* lhead = (list_head* )head;
    	if( NULL != lhead )
    	{
    		ret = lhead -> length;
    	}	
    	return ret;
    }
    
    /*******************************************************************************************************
    函数名:Clean_DcLinkListHead
    函数功能:	清空链表 
    参数: DcLinkList* head 链表头指针 
    返回值:ret 成功返回1 失败返回0 
    *******************************************************************************************************/
    int Clean_DcLinkListHead(DcLinkList* head) 
    {
    	int ret = 0;
    	list_head* lhead = (list_head* )head;
    	if( NULL != lhead )
    	{
    		lhead -> length = 0;
    		//lhead	 -> next = NULL;
    		lhead -> head.next = NULL;
    		lhead->head.pre = NULL;
    		lhead->slider = NULL;
    		ret = 1;
    	}	
    	return ret;
    }
    
    /*******************************************************************************************************
    函数名:Add_DcLinkList
    函数功能:往链表里面添加一个链表元素 如果pos的值是0(就是链表头)和1(链表的第一元素 链表元素个数是从1开始算的)都是头插法
              pos的值大于链表长度是尾插法  这里面pos值得注意的是 i=1 pos为a的时候 是把链表元素插入第a个元素的位置 
              当i=0 pos为a的时候 是把链表元素插入 第a个元素位置的后面    切忌:这里面0位置是链表头指针 从1开始是链表元素 
    参数:   DcLinkList* head链表头指针    DcLinkListNode* Node插入元素的指针(被强制类型转化成DcLinkListNode*)  int pos 插入位置 
             pos的有效值范围是 从0到无穷大  
    返回值: ret 插入成功返回1  插入失败返回0 
    *******************************************************************************************************/
    int Add_DcLinkList(DcLinkList* head, DcLinkListNode* Node, int pos)
    {
    	int ret = 0;
    	int i = 0;
    	list_head* lhead = (list_head* )head;
    	DcLinkListNode* node = (DcLinkListNode* )head;
    	DcLinkListNode* Last = NULL;
    	ret=( NULL != node) && ( NULL != Node) && (pos >= 0);
    	if(1 == ret)
    	{
    		for(i=1; ( (i<pos) && (node->next != NULL) ); i++)
    		{
    			node = node->next;
    		}
    		Node -> next = node -> next;
    		node -> next = Node;
    		
    		if(NULL != Node->next) 
    		{
    			Node->next->pre = Node; 
    		}
    		Node->pre = node;
    		
    		if(lhead->length == 0)//第一次插入元素的时候把游标 指向这个元素  
    		{
    			lhead->slider = Node;
    		}
    		lhead -> length++; //这个一定要在后面调用 lhead->length值的前面更新 
    		/*判断是否为头插法  所谓头插法 就是pos为0和1的情况 其实也就是没有进for循环的情况  剩下的无论pos为多少  进入多少次循环都没有头插法*/
    		if(node == (DcLinkListNode* )head) 
    		{
    			Last =(DcLinkListNode* )Get_DcLinkListNode(lhead, lhead->length); //获得链表最后一个元素 
    			Last->next = Node; //把头插法的数据连接到 链表的最后一个元素的后面 
    			Node->pre = Last;
    		}
    		
    	}
    	return ret;
    }
    
    /*******************************************************************************************************
    函数名:Get_DcLinkListNode
    函数功能:获得链表中第pos个元素位置的链表元素 链表是从1开始的  0是链表头   pos为0的时候表示get链表头 
    参数: DcLinkList* head链表头指针    int pos获得链表元素的位置  pos的有效取值范围是 1 到  length  0是链表头 
    返回值: DcLinkListNode*类型 第pos个链表元素的地址 
    *******************************************************************************************************/
    DcLinkListNode* Get_DcLinkListNode(DcLinkList* head, int pos)
    {
    	int ret = 0;
    	int i = 0;
    	list_head* lhead = (list_head* )head;
    	/*本来pos应该是有上限的  但是变成了循环链表pos理论上说就可以无穷大了  但是get函数应该是在链表中有值的情况下才成立的 即(lhead->length>0)*/ 
    	ret=( NULL != lhead) && (pos >= 0) && (lhead->length>0); 
    	if(1 == ret)
    	{
    		DcLinkListNode* node = (DcLinkListNode* )head;
    		for(i=0; i<pos; i++) //执行 pos次   得到的是第pos位置的node 
    		{
    			node = node->next;
    		}	
    		return (DcLinkListNode*)node;
    	}
    	return NULL;
    }
    
    /*******************************************************************************************************
    函数名:Del_DcLinkListNode
    函数功能:删除链表中第pos位置的链表元素 
    参数: DcLinkList* head链表头指针    int pos删除链表元素的位置  pos是删除的链表元素的位置 跟get和add中的
           pos是配套的  有效取值范围依然是 1到 length  在这个函数里面由于不能删除链表头 所以pos为0的时候无效 
    返回值: DcLinkListNode* ret这个返回值很重要 因为这个删除仅仅是把链表元素踢出了链表 并没有free开辟的内存
             应该通过这个返回的地址free  释放内存
    		 删除成功返回 删除链表元素的地址   删除失败返回 NULL 
    *******************************************************************************************************/
    DcLinkListNode* Del_DcLinkListNode(DcLinkList* head, int pos)
    {
    	DcLinkListNode* ret = NULL;
    	DcLinkListNode* Last = NULL;
    	int i = 0;
    	list_head* lhead = (list_head* )head;
    	DcLinkListNode* first = lhead->head.next;
    	
    	if(( NULL != lhead) && (pos > 0) && (lhead->length>0))
    	{
    		DcLinkListNode* node = (DcLinkListNode* )head;
    		for(i=1; i<pos; i++)//执行 pos次   得到的是第pos位置的node  这个方法行不通 
    		{                   //因为要想删除第pos位置的node 应该先找到它上一个链表元素 
    			node = node->next; //所以这里面i=1 比get函数少执行了一次  得到第pos-1位置的node 
    		}
    		/*判断是不是 pos为1的 情况删除头节点后面的第一个元素(这个是没有进入for循环的)  跟循环一圈后的情况不一样  */
    		/*循环一圈的是进入for循环的情况   此时的node不再是head了 而是链表最后一个元素*/ 
    		if(node == (DcLinkListNode* )head)
    		{
    			Last =(DcLinkListNode* )Get_DcLinkListNode(lhead, lhead->length);
    		}
    		
    		ret = node->next;
    		node->next = ret->next;	
    		/*判断是不是循环了一圈后回来的情况 */
    		if((first == ret) && (NULL == Last))
    		{
    			Last =(DcLinkListNode* )Get_DcLinkListNode(lhead, lhead->length);
    		}
    		/*判断是否要删除链表中的第一个元素*/
    		if( Last != NULL )
    		{
    			Last->next = ret->next; 
    			lhead->head.next = ret->next;
    			ret->next->pre = Last;
    		}
    		
        	if(1 != lhead->length) //判断删除的值是否为最后一个元素 
    		{
    			ret->next->pre = ret->pre;
    			if(node == (DcLinkListNode* )head)//判断删除的值是否为第一个元素 
    			{
    				ret->next->pre =  Last;
    			} 
    			if(lhead->slider == ret) //判断删除的节点是否为游标的位置 
    			{
    				lhead->slider = ret->next; 
    			} 
    		} 
    		lhead->length--; //这个一定要写在 Get_DcLinkListNode 后面 不然的话 pos就为0了 
    		/*判断链表是否 减到了空  如果链表中不再有元素 就把head.next赋值为NULL*/
    		/*单向链表不需要这个的原因 是因为单向链表的最后一个元素的next就是NULL 而双向链表没有NULL的了*/
    		if(0 == lhead->length)
    		{
    			lhead->head.next = NULL;
    			lhead->head.pre = NULL;
    			lhead->slider = NULL; 
    		}
    		
    	}
    	return (DcLinkListNode*)ret;
    }
    
    /*******************************************************************************************************
    函数名: DcLinkList_Slider
    函数功能:获得当前游标指向的数据
    参数: DcLinkList* head
    返回值:成功返回 DcLinkListNode* ret  失败返回NULL 
    *******************************************************************************************************/
    DcLinkListNode* DcLinkList_Slider(DcLinkList* head)
    {
    	DcLinkListNode* ret = NULL;
    	list_head* lhead = (list_head* )head;
    	if( (NULL != lhead)&&(NULL != lhead->slider) )//保证slider是有效的 
    	{
    		ret = lhead->slider;
    	}
    	return ret;
    }
    
    /*******************************************************************************************************
    函数名: DcLinkList_Reset
    函数功能:重置游标 让游标指向head头节点后面的第一个元素 
    参数: DcLinkList* head
    返回值:成功返回 当前游标的指向DcLinkListNode* ret  失败返回NULL 
    *******************************************************************************************************/
    DcLinkListNode* DcLinkList_Reset(DcLinkList* head)
    {
    	DcLinkListNode* ret = NULL;
    	list_head* lhead = (list_head* )head;
    	if(NULL != lhead)
    	{
    		lhead->slider = lhead->head.next;
    		ret = lhead->slider;
    	}
    	return ret;
    }
    
    /*******************************************************************************************************
    函数名: DcLinkList_Next
    函数功能:使游标指向下一个元素 
    参数: DcLinkList* head
    返回值:成功返回 前游标的指向DcLinkListNode* ret  失败返回NULL 
    *******************************************************************************************************/
    DcLinkListNode* DcLinkList_Next(DcLinkList* head)
    {
    	DcLinkListNode* ret = NULL;
    	list_head* lhead = (list_head* )head;
    	if((NULL != lhead)&&(NULL != lhead->slider)) //保证游标是有效的 
    	{
    		ret = lhead->slider;
    		lhead->slider = ret->next; 
    	}
    	return ret;
    }
    
    /*******************************************************************************************************
    函数名: DLinkList_Pre
    函数功能:使游标指向上一个元素 
    参数: DLinkList* head
    返回值:成功返回 前游标的指向DLinkListNode* ret  失败返回NULL 
    *******************************************************************************************************/
    DcLinkListNode* DcLinkList_Pre(DcLinkList* head)
    {
    	DcLinkListNode* ret = NULL;
    	list_head* lhead = (list_head* )head;
    	if((NULL != lhead)&&(NULL != lhead->slider)) //保证游标是有效的 
    	{
    		ret = lhead->slider;
    		lhead->slider = ret->pre; 
    	}
    	return ret;
    }
    
    /*******************************************************************************************************
    函数名: DcLinkList_Del
    函数功能:删除链表中的某个指定元素 
    参数: DcLinkList* head   DcLinkListNode* node为指定的元素 
    返回值:成功返回 删除的链表元素  失败返回NULL 
    *******************************************************************************************************/
    DcLinkListNode* DcLinkList_Del(DcLinkList* head,DcLinkListNode* node)
    {	//这个函数主要是用来删除游标的返回值的 
     
    	DcLinkListNode* ret = NULL;
    	list_head* lhead = (list_head* )head;
    	int i=0; 
    	if((NULL != head)&&(NULL != node))
    	{
    		DcLinkListNode* current = (DcLinkListNode*)lhead;
    		for(i=1; i<=lhead->length; i++)
    		{
    			if(node == current->next)
    			{
    				ret = current->next;
    				break; 
    			} 
    			current = current->next;
    		}
    		
    		if(NULL == ret)  //说明没有找到node 
    		{
    			printf("put error!!!
    "); 
    		}
    		else //找到了node 
    		{
    			Del_DcLinkListNode(lhead,i); 
    		} 
    	}	
    	return ret;//返回删除的链表元素 
    }
    
    
    
    
    
    


    DcLinkList.h:

    #ifndef __DcLinkList_H__
    #define __DcLinkList_H__
    
    typedef void DcLinkList;  //这个是为了 封装方便 
    typedef struct Str_DcLinkList DcLinkListNode;  //这个结构体是链表的真身 
    struct Str_DcLinkList   //每一个链表元素的结构都会包含这个结构  因为当给链表元素强制类型 
    {                     //转换成(DcLinkListNode* )的时候  其实就是要开始对每个元素中的 DcLinkListNode进行赋值了 
    	DcLinkListNode* next;
    	DcLinkListNode* pre;
    };
    
    DcLinkList* Creat_DcLinkListHead(void);
    
    int Destroy_DcLinkListHead(DcLinkList* head);
    
    int Get_Length(DcLinkList* head);
    
    int Clean_DcLinkListHead(DcLinkList* head);
    
    int Add_DcLinkList(DcLinkList* head, DcLinkListNode* Node, int pos);
    
    DcLinkListNode* Get_DcLinkListNode(DcLinkList* head, int pos);
    
    DcLinkListNode* Del_DcLinkListNode(DcLinkList* head, int pos); 
    
    DcLinkListNode* DcLinkList_Del(DcLinkList* head,DcLinkListNode* node);
    
    DcLinkListNode* DcLinkList_Next(DcLinkList* head);
    
    DcLinkListNode* DcLinkList_Pre(DcLinkList* head);
    
    DcLinkListNode* DcLinkList_Reset(DcLinkList* head);
    
    DcLinkListNode* DcLinkList_Slider(DcLinkList* head);
     
    #endif
    
    


    main.c:

    #include <stdio.h>
    #include <stdlib.h>
    #include <malloc.h>
    #include <string.h>
    #include "DcLinkList.h"
    
    typedef struct _tag_str
    {
    	DcLinkListNode head;
    	int i;
    }str;
    
    int main()
    {
    	int i = 0;
    	str str1,str2,str3,str4,str5,str6,*strp;
    	str1.i=1;
    	str2.i=2;
    	str3.i=3;
    	str4.i=4;
    	str5.i=5;
    	str6.i=6;
    	DcLinkList* head = NULL;
    	head = Creat_DcLinkListHead();
    	Add_DcLinkList(head, (DcLinkListNode*)&str1, Get_Length(head)+1);
    	Add_DcLinkList(head, (DcLinkListNode*)&str2, Get_Length(head)+1);
    	Add_DcLinkList(head, (DcLinkListNode*)&str3, Get_Length(head)+1);
    	Add_DcLinkList(head, (DcLinkListNode*)&str4, Get_Length(head)+1);
    	Add_DcLinkList(head, (DcLinkListNode*)&str5, Get_Length(head)+1);	
    	Add_DcLinkList(head, (DcLinkListNode*)&str6, 3);	
    	for(i = 1; i<=Get_Length(head); i++)
    	{
    		strp = (str*)Get_DcLinkListNode(head, i);
    		printf("%d
    ",strp->i);
    	}
    	Del_DcLinkListNode(head, 1);
    	printf("
    ");
    	
    	for(i = 1+5; i<=Get_Length(head)+5; i++)
    	{
    		strp = (str*)Get_DcLinkListNode(head, i);
    		printf("%d
    ",strp->i);
    	}	
    	
    	printf("
    ");
    	
    	DcLinkList_Reset(head);
    	for(i = 1; i<=Get_Length(head)*3; i++)
    	{
    		DcLinkList_Pre(head);
    		strp = (str*)DcLinkList_Slider(head);	
    		printf("%d
    ",strp->i);
    	}
    	
    	i = 6;
    	while(i--)
    	{
    		Del_DcLinkListNode(head, i);	
    	}
    	
    	for(i = 1; i<=Get_Length(head); i++)
    	{
    		strp = (str*)Get_DcLinkListNode(head, i);
    		printf("%d
    ",strp->i);
    	}
    	
    	Destroy_DcLinkListHead(head);
    	return 0;
    }
    
    
    
    
    


     

  • 相关阅读:
    Statspack之十四"log file sync" 等待事件
    log file sync(日志文件同步) 与 Log file parallel write 等待事件(1)
    OWI诊断的步骤(基础)
    linux下防火墙配置
    linux中oracle开机启动(2)
    Linux开机自动启动ORACLE设置
    linux find mtime参数详解
    linux 下 rman自动备份
    JSTL 标签库 下载及配置
    JSTL I18N 格式标签库 使用之一_____数字日期格式化
  • 原文地址:https://www.cnblogs.com/james1207/p/3283480.html
Copyright © 2011-2022 走看看