双向循环链表代码编写

 链表的使用里,双向循环链表的使用是最广的。双向链表的每个数据结点中都有两个指针,分别指向直接后继和直接前驱,所以从双向链表的任意一个结点开始,都可以很方便的访问它的前驱结点和后继结点。

 这是我练习的一个双向链表的代码,用VS2017环境编译,因为本人偏爱日志调试的方法,所以代码里日志打印内容占了一定的行数。

 在写之前参考了一些资料,尤其是这篇博客(http://blog.csdn.net/fisherwan/article/details/25796625),写得通俗易懂。感谢作者fisherwan


double way circular List.c 代码

#include "stdafx.h"
#define logpath "E:\\VS2017test\\log.txt"//将日志打印的目录地址用宏表示

int main()
{
	int len = 0;
	char logbody[MAX_LOG];

	//调试开始的初始日志打印,“__LINE__”可以用来获取行号
	snprintf(logbody, MAX_LOG, "line:[%d], begin.", __LINE__);
	write_log_file((char*)logpath, logbody, strlen(logbody));

	//创建链表,并给链表赋初值
	PNODE pHead= Create_List();
	if (pHead)
	{
		snprintf(logbody, MAX_LOG, "line:[%d], creat list succ.", __LINE__);
		write_log_file((char*)logpath, logbody, strlen(logbody));
	}

	//获取刚刚创建的链表长度以供打印
	len = Length_List( pHead);
	snprintf(logbody, MAX_LOG, "line:[%d], lenth of list:%d.", __LINE__,len);
	write_log_file((char*)logpath, logbody, strlen(logbody));

	//准备在链表的第2位插入数值为12的内容
	int insert_pos = 2;
	int insert_val = 12;

	//将内容插入到链表的指定位置
	if (1 == Insert_List(pHead, insert_pos, insert_val))
	{
		snprintf(logbody, MAX_LOG, "line:[%d], insert pos:%d,insert value:%d", __LINE__, insert_pos, insert_val);
		write_log_file((char*)logpath, logbody, strlen(logbody));
	}

	//获取插入内容后的的链表长度以供打印
	len = Length_List(pHead);
	snprintf(logbody, MAX_LOG, "line:[%d], lenth of list:%d.", __LINE__,len);
	write_log_file((char*)logpath, logbody, strlen(logbody));

	//准备把链表的第3位删除。delete_val赋任意初值,在删除过程中赋真实值以供打印。
	int delete_pos = 3;
	int delete_val = 0;
	if (1 == Delete_List(pHead, delete_pos, delete_val))
	{
		snprintf(logbody, MAX_LOG, "line:[%d],delete list succ. delete pos:%d", __LINE__, delete_pos);
		write_log_file((char*)logpath, logbody, strlen(logbody));
	}

	//获取删除内容后的的链表长度以供打印
	len = Length_List(pHead);
	snprintf(logbody, MAX_LOG, "line:[%d], lenth of list:%d.", __LINE__,len);
	write_log_file((char*)logpath, logbody, strlen(logbody));

	//对链表进行排序操作
	if (1 == Sort_List(pHead))
	{
		snprintf(logbody, MAX_LOG, "line:[%d],sort list succ.", __LINE__);
		write_log_file((char*)logpath, logbody, strlen(logbody));
	}

	//获取排序后的的链表长度以供打印
	len = Length_List(pHead);
	snprintf(logbody, MAX_LOG, "line:[%d], lenth of list:%d.", __LINE__,len);
	write_log_file((char*)logpath, logbody, strlen(logbody));

	//调试结束的初始日志打印
	snprintf(logbody, MAX_LOG, "line:[%d], end.", __LINE__);
	write_log_file((char*)logpath, logbody, strlen(logbody));

	return 0;
}

//获取当前时刻,用于日志打印
void get_local_time(char* buffer)
{
	struct tm rawtime;
	time_t time_seconds = time(0);
	localtime_s(&rawtime, &time_seconds);
	snprintf(buffer, 100, "%04d-%02d-%02d %02d:%02d:%02d",
		(rawtime.tm_year + 1900), rawtime.tm_mon, rawtime.tm_mday,
		rawtime.tm_hour, rawtime.tm_min, rawtime.tm_sec);
}
//调用的日志打印函数,日志格式为“时间 行号 内容”。
int write_log_file(char *filename, char* buffer, size_t buf_size)
{	
	FILE *fp_log;
	errno_t err;
	char logcontent[MAX_LOG];
	char now[MAX_UNIT];
	memset(now, 0, sizeof(now));
	get_local_time(now);//获取当前时刻
	//传进来的日志内容buffer里已经有了行号和内容,这里在前面添加上时刻
	snprintf(logcontent, MAX_LOG, "time:%s %s\n", now, buffer);

	//调用文件句柄打开日志文件
	err = fopen_s(&fp_log, (char*)filename, "at+");
	if (0 != err)
	{
		printf("Open log file failed ,errno is : [%d]", err);
		return -1;
	}
	if (fp_log != NULL)
	{
		//将日志内容写入文件,关闭句柄,将句柄置NULL
		err = fwrite(logcontent, strlen(logcontent) + 1, 1, fp_log);
		fclose(fp_log);
		fp_log = NULL;
	}
	else
		return -1;//这是错误情况不打印错误日志,因为这本身就是日志函数

	return 0;
}
PNODE Create_List(void)
{
	int len=3;  //即将创建的链表的长度  
	int i; 
	int val[3] = {30,40,50};//创建链表的三个结点的值 
	int valtmp=0;
	char logbody[MAX_LOG];
	PNODE pHead = (PNODE)malloc(sizeof(NODE));//分配一个头节点  
	if (NULL == pHead)
	{
		snprintf(logbody, MAX_LOG, "line:[%d],Memory allocation failure.", __LINE__);
		write_log_file((char*)logpath, logbody, strlen(logbody));
		exit(-1);
	}
	else
	{
		//一开始建立链表的时候,表头向前和向后都指向自己
		PNODE pTail = pHead;
		pHead->pNext = pHead;
		pHead->pPre = pHead;
		pHead->data = 0;

        //依次插入三个初始的链表内容值
		for (i = 0; i<len; i++)
		{
			PNODE p = (PNODE)malloc(sizeof(NODE));//分配节点内存
			if (NULL == p)
			{
				snprintf(logbody, MAX_LOG, "line:[%d],Memory allocation failure.", __LINE__);
				write_log_file((char*)logpath, logbody, strlen(logbody));
				exit(-1);
			}
			else
			{
				valtmp=val[i];
				p->data = valtmp;
				//打印此刻插入链表的位数序号和内容
				snprintf(logbody, MAX_LOG, "line:[%d], insert list seq %d, date value is %d.", __LINE__,i, p->data);
				write_log_file((char*)logpath, logbody, strlen(logbody));

				//插入时注意维持双向链表的前向和后向的连通
				p->pPre = pTail;
				p->pNext = pHead;
				pTail->pNext = p;
				pHead->pPre = p;
				pTail = p;
			}
		}
	}
	return pHead;
}


//链表的第pos有效元素前面插入元素val  
int Insert_List(PNODE pHead, int pos, int val)
{
	int i = 0;
	PNODE p = pHead;
	char logbody[MAX_LOG];
	while ((NULL != p) && (i<pos - 1)) // 从头节点开始往后寻找,一直找到第pos个元素前面一个元素的位置 
	{
		p = p->pNext;
		i++;
	}
	if (p == NULL || i > pos - 1)  //异常保护,如果链表为空或者上一步搜索出来的i值不合理则报错  
	{
		snprintf(logbody, MAX_LOG, "line:[%d], insert[pos:%d,val:%d] error.", __LINE__,pos,val);
		write_log_file((char*)logpath, logbody, strlen(logbody));
		return 0;
	}

	//p指针指向链表第pos个有效节点的前驱,即指向第pos-1节点。新节点建立要申请NODE结构体大小的内存  
	PNODE q = (PNODE)malloc(sizeof(NODE));

	//插入时注意维持双向链表的前向和后向的连通
	q->data = val;
	q->pNext = p->pNext;
	q->pPre = p;
	p->pNext = q;
	q->pNext->pPre = q;
	return 1;
}
//把第pos位置的链表内容删除
int Delete_List(PNODE pHead, int pos, int val)
{
	int i = 0;
	PNODE p = pHead;
	char logbody[MAX_LOG];
	while ((NULL != p) && (i<pos - 1))// 从头节点开始往后寻找,一直找到第pos个元素前面一个元素的位置 
	{
		p = p->pNext;
		i++;
	}
	if (p == NULL || i > pos - 1)  //异常保护,如果链表为空或者上一步搜索出来的i值不合理则报错   
	{
		snprintf(logbody, MAX_LOG, "line:[%d], delete[pos:%d] error.", __LINE__, pos);
		write_log_file((char*)logpath, logbody, strlen(logbody));
		return 0;
	}

	//删除节点  
	PNODE q = p->pNext;  //q指向待删除的节点;  
	val = q->data;
	p->pNext = q->pNext; //修改链表指针指向;
	q->pNext->pPre = p;
	free(q);           //释放q所指向节点的内存;  
	q = NULL;//重要,否则会出现野指针;
	return 1;
}

//链表有效元素的个数  
int Length_List(PNODE pHead)
{
	char logbody[MAX_LOG];
	int len = 0;  //定义变量要记得初始化; 

	PNODE p = pHead->pNext;
	while (pHead != p)
	{
		len++;
		//打印此时链表的各个位置分别是什么内容
		snprintf(logbody, MAX_LOG, "line:[%d], the value of listnumber %d is %d.", __LINE__, len-1,p->data);
		write_log_file((char*)logpath, logbody, strlen(logbody));
		p = p->pNext;
	}
	//打印此时的链表长度
	snprintf(logbody, MAX_LOG, "line:[%d],current list length is %d.", __LINE__, len);
	write_log_file((char*)logpath, logbody, strlen(logbody));
	return len;
}

//对链表中的元素进行排序  
int Sort_List(PNODE pHead)
{
	int i, j;
	int temp;
	int len = Length_List(pHead);
	PNODE p, q;//指向链表第一个有效元素  

	for (i = 0, p = pHead->pNext; i<len - 1; i++, p = p->pNext)
	{
		for (j = i + 1, q = p->pNext; j<len; j++, q = q->pNext)
		{
			//交换数据,冒泡 
			if (p->data>q->data)
			{
				temp = p->data;
				p->data = q->data;
				q->data = temp;
			}
		}
	}
	return 1;
}

双向链表的头文件stdafx.h

#pragma once

#include "targetver.h"
#include <stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<time.h>


#define MAX_LOG 256
#define MAX_NAME 128
#define MAX_UNIT 256


typedef struct Node
{
	int data;//数据域,用来存放数据域;  
	struct Node *pNext;//定义一个结构体指针,指向下一次个与当前节点数据类型相同的节点 
	struct Node *pPre;//定义一个结构体指针,指向上一次个与当前节点数据类型相同的节点
}NODE, *PNODE;  //NODE等价于 struct Node; PNODE等价于struct Node *; 此处用大写是为了与变量区分,可以让人容易看出是个数据类型

PNODE Create_List(void);
int Insert_List(PNODE pHead, int pos, int val);
int Delete_List(PNODE pHead, int pos, int val);
int Length_List(PNODE pHead);
int Sort_List(PNODE pHead);
void get_local_time(char* buffer);
int write_log_file(char*, char*, size_t);


双向链表的运行日志:


time:2018-01-02 22:16:32 line:[13], begin.
time:2018-01-02 22:16:32 line:[156], insert list seq 0, date value is 30.
time:2018-01-02 22:16:32 line:[156], insert list seq 1, date value is 40.
time:2018-01-02 22:16:32 line:[156], insert list seq 2, date value is 50.
time:2018-01-02 22:16:32 line:[20], creat list succ.
time:2018-01-02 22:16:32 line:[240], the value of listnumber 0 is 30.
time:2018-01-02 22:16:32 line:[240], the value of listnumber 1 is 40.
time:2018-01-02 22:16:32 line:[240], the value of listnumber 2 is 50.
time:2018-01-02 22:16:32 line:[245],current list length is 3.
time:2018-01-02 22:16:32 line:[26], lenth of list:3.
time:2018-01-02 22:16:32 line:[36], insert pos:2,insert value:12
time:2018-01-02 22:16:32 line:[240], the value of listnumber 0 is 30.
time:2018-01-02 22:16:32 line:[240], the value of listnumber 1 is 12.
time:2018-01-02 22:16:32 line:[240], the value of listnumber 2 is 40.
time:2018-01-02 22:16:32 line:[240], the value of listnumber 3 is 50.
time:2018-01-02 22:16:32 line:[245],current list length is 4.
time:2018-01-02 22:16:32 line:[42], lenth of list:4.
time:2018-01-02 22:16:32 line:[50],delete list succ. delete pos:3
time:2018-01-02 22:16:32 line:[240], the value of listnumber 0 is 30.
time:2018-01-02 22:16:32 line:[240], the value of listnumber 1 is 12.
time:2018-01-02 22:16:32 line:[240], the value of listnumber 2 is 50.
time:2018-01-02 22:16:32 line:[245],current list length is 3.
time:2018-01-02 22:16:32 line:[56], lenth of list:3.
time:2018-01-02 22:16:32 line:[240], the value of listnumber 0 is 30.
time:2018-01-02 22:16:32 line:[240], the value of listnumber 1 is 12.
time:2018-01-02 22:16:32 line:[240], the value of listnumber 2 is 50.
time:2018-01-02 22:16:32 line:[245],current list length is 3.
time:2018-01-02 22:16:32 line:[62],sort list succ.
time:2018-01-02 22:16:32 line:[240], the value of listnumber 0 is 12.
time:2018-01-02 22:16:32 line:[240], the value of listnumber 1 is 30.
time:2018-01-02 22:16:32 line:[240], the value of listnumber 2 is 50.
time:2018-01-02 22:16:32 line:[245],current list length is 3.
time:2018-01-02 22:16:32 line:[68], lenth of list:3.
time:2018-01-02 22:16:32 line:[72], end.



VS单步跟踪时观察链表如图:



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值