数据结构与算法:线性表之双链表的基本操作

一、循环链表

定义:循环链表是另一种形式的链式存储结构,它的特点是表中最后一个结点的指针指向头结点,整个链表形成一个环。由此,可以从表中任一结点出发均可寻找到表中其他结点。

二、双向链表

定义:双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点

单向链表特点
  1.我们可以轻松的到达下一个节点, 但是回到前一个节点是很难的.
  2.只能从头遍历到尾或者从尾遍历到头(一般从头到尾)
  
双向链表特点
  1.每次在插入或删除某个节点时, 需要处理四个节点的引用, 而不是两个. 实现起来要困难一些
  2.相对于单向链表, 必然占用内存空间更大一些.
  3.既可以从头遍历到尾, 又可以从尾遍历到头

双向链表中各节点包含以下 3 部分信息:
  指针域:用于指向当前节点的直接前驱节点;
  数据域:用于存储数据元素。
  指针域:用于指向当前节点的直接后继节点;

2.1创造结点

typedef struct DuNode
{
	int data;				//数据域
	struct DuNode* next;		//后继
	struct DuNode* pre;	//前驱
}DuNode,*DuLinkList;

2.2 全局定义链表头尾指针 (方便调用)
struct DuNode* head= NULL;
struct DuNode* end = NULL;

2.3.1 创建双链表,实现在双链表中增加一个结点:头或尾添加
void AddListTill(int a )
{
		//创建一个节点
		struct DuNode* temp=(struct DuNode*)malloc(sizeof(struct DuNode));		//此处注意强制类型转换

		//节点数据进行赋值
		temp->data=a;
		temp->next=NULL;		
		temp->pre=NULL;
		
		//连接分两种情况
		//1.一个节点都没有
		//2.已经有节点了,添加到尾巴上
		if(NULL==head)//没有节点
		{	
			head=temp;
		//	end=temp;
		}
		else
		{
		end->next=temp;
	//	end=temp;			//尾结点应该始终指向最后一个
		}
		end=temp;			//尾结点应该始终指向最后一个
}


2.3.2 在指定位置插入节点 :特定位置添加
void AddListRand(int index,int a)
{	

    if (NULL==head)
	{
		printf("链表没有节点\n");
		return;
	}	
    struct DuNode* pt =FindNode(index);//添加之前需要找到该结点的位置
	if(NULL==pt)    //没有此节点
	{
		printf("没有指定节点\n");
		return;
	}
	
    //有此节点
    //创建临时节点,申请内存
	struct DuNode* temp =(struct DuNode *)malloc(sizeof(struct DuNode));

	if(temp== NULL)
    {
        printf("malloc error!\r\n");
        return ;
    }  
    	//节点成员进行赋值
    else{
	temp->data=a;
	temp->next=NULL;
	temp->pre=NULL;
	}
	
	//连接到链表上 
	//1.找到的节点在尾部 
	//2.找到的节点在中间 
	if (pt == end)
	{
	//尾巴的下一个指向新插入的节点
	end->next=temp;
	//新的尾巴
	temp->pre = end;
	end=temp;
	}else
	{
	// 先连后面 (先将要插入的节点指针指向原来找到节点的下一个)
	temp->next=pt->next;
	temp->pre = pt->pre;
	//后连前面
	pt->next=temp;
	}

}


2.4 查询指定的节点 (遍历:只能一个一个找)
struct DuNode* FindNode(int a )//a是结点的序号
{
	struct DuNode *temp =head;//创建一个临时结点为头结点
	while(temp !=NULL)
	{
	if(a == temp->data)//找到该节点
	{
		return temp;
	}
	temp = temp->next;//依次顺序寻找
	}
	//没找到
		return NULL;
} 

2.5 链表清空:全部删除
void FreeList()
{
	//一个一个NULL
	struct DuNode *temp =head;		//定义一个临时变量来指向头结点
	while (temp !=NULL)
	{
	//	printf("%d\n",temp->a);
		struct DuNode* pt =temp;
		temp = temp->next;		//temp指向下一个的地址 即实现++操作
		free(pt);					//释放当前
	}
	//头尾清空	不然下次的头就接着0x10
	head =NULL;
	end =NULL;
}

2.5 双向链表更改节点数据
/*更新函数,其中,add 表示更改结点在双链表中的位置,newElem 为新数据的值*/
Node *ModifyList(DuNode * p,int add,int newElem)
{
    DuNode * temp=p;
    /*遍历到被删除结点*/
    for (int i=1; i<add; i++) 
    {
        temp=temp->next;
    }
    temp->data=newElem;
    return p;
}

2.6.1 结点的删除:尾删除
void DeleteListTail()
{ 
	if (NULL == end)
	{
		printf("链表为空,无需删除\n");
		return;
	}
	//链表不为空 
	//链表有一个节点
	 if (head==end)
	 {
		 free(head);
		 head=NULL;
		 end=NULL; 
	 }
	 else
	 {
		//找到尾巴前一个节点
		 struct DuNode* temp =head;
		 while (temp->next!=end)
		 {
			 temp = temp->next;
		 }
		 //找到了,删尾巴
		//释放尾巴
		 free(end);
		 //尾巴迁移
		 end=temp;
		 //尾巴指针为NULL
		 end->next=NULL;
	 }

}

2.6.2 结点的删除:头删除
void DeleteListHead()
{	//记住旧头
	struct DuNode* temp=head;
	//链表检测 
	if (NULL==head)
	{
			printf("链表为空\n");
			return;
	}

	head=head->next;//头的第二个节点变成新的头
	free(temp);

}
2.6.3 删除指定节点
void DeleteListRand(int a)
{

	//链表判断 是不是没有东西
	if(NULL==head)
	{
	printf("链表没东西\n");
	return;
	}
    //链表有东西,找这个节点
	struct DuNode* temp =FindNode(a);//找到这个结点
	if(NULL==temp)
	{
	printf("查无此点\n");
	return;
	}
	//找到了,且只有一个节点
	if(head==end)
	{
	free(head);
	head=NULL;
	end=NULL;
	}
	else if(head->next==end) //有两个节点
	{
	//看是删除头还是删除尾
	if(end==temp)
		{	DeleteListTail(); }
	else if(temp==head)
		{	DeleteListHead(); }	
	}
	else//多个节点
	{
		//看是删除头还是删除尾
		if(end==temp)
			DeleteListTail();
		else if(temp==head)
			DeleteListHead();	
		else	//删除中间某个节点
		{	//找要删除temp前一个,遍历
			struct DuNode* pt =head;
			while(pt->next!=temp)
			{
			pt=pt->next;
			}
			//找到了
			//让前一个直接连接后一个 跳过指定的即可
			 pt->next=temp->next;
			 free(temp);
		
		}
	}

}


2.7 双向链表的打印
/*输出链表的功能函数*/
void PrintList(DuNode * head)
{
    DuNode * temp=head;
    while (temp) 
    {
        /*如果该节点无后继节点,说明此节点是链表的最后一个节点*/
        if (temp->next==NULL) 
        {
            printf("%d\n",temp->data);
        }
        else
        {
            printf("%d->",temp->data);
        }
        temp=temp->next;
    }
}

2.8 检测主程序
void main ()
{	
	struct Node *pFind ;
	//创建5个节点
	for(i=0;i<6;i++)
	AddListTill(i);
	
//	AddListRand(4,14);		//在指定位置4增加节点14
//	DeleteListTail();		//删除一个尾结点
	DeleteListRand(4);		//删除4节点
	ScanList();				//便利输出链表
	FreeList();				//删除链表
/*	pFind = FindNode(5);	//查找5节点

	if (pFind !=  NULL)
	{
		printf("找到%d\n",pFind->a);	//找到节点并且输出该节点数据
	}
	else
	{
		printf("No Find!\n");
	}

*/

}


你可能感兴趣的:(C语言学习,链表,数据结构,c++)