一步一步写算法(之双向链表)

原文:一步一步写算法(之双向链表)

【 声明:版权所有,欢迎转载,请勿用于商业用途。  联系信箱:feixiaoxing @163.com】

    前面的博客我们介绍了单向链表。那么我们今天介绍的双向链表,顾名思义,就是数据本身具备了左边和右边的双向指针。双向链表相比较单向链表,主要有下面几个特点:

    (1)在数据结构中具有双向指针

    (2)插入数据的时候需要考虑前后的方向的操作

    (3)同样,删除数据的是有也需要考虑前后方向的操作

    那么,一个非循环的双向链表操作应该是怎么样的呢?我们可以自己尝试一下:

    (1)定义双向链表的基本结构

typedef struct _DOUBLE_LINK_NODE
{
	int data;
	struct _DOUBLE_LINK_NODE* prev;
	struct _DOUBLE_LINK_NODE* next;
}DOUBLE_LINK_NODE;

    (2)创建双向链表节点

DOUBLE_LINK_NODE* create_double_link_node(int value)
{
	DOUBLE_LINK_NODE* pDLinkNode = NULL;
	pDLinkNode = (DOUBLE_LINK_NODE*)malloc(sizeof(DOUBLE_LINK_NODE));
	assert(NULL != pDLinkNode);

	memset(pDLinkNode, 0, sizeof(DOUBLE_LINK_NODE));
	pDLinkNode->data = value;
	return pDLinkNode;
}

    (3)删除双向链表

void delete_all_double_link_node(DOUBLE_LINK_NODE** pDLinkNode)
{
	DOUBLE_LINK_NODE* pNode;
	if(NULL == *pDLinkNode)
		return ;

	pNode = *pDLinkNode;
	*pDLinkNode = pNode->next;
	free(pNode);
	delete_all_double_link_node(pDLinkNode);
}

    (4)在双向链表中查找数据

DOUBLE_LINK_NODE* find_data_in_double_link(const DOUBLE_LINK_NODE* pDLinkNode, int data)
{
	DOUBLE_LINK_NODE* pNode = NULL;
	if(NULL == pDLinkNode)
		return NULL;

	pNode = (DOUBLE_LINK_NODE*)pDLinkNode;
	while(NULL != pNode){
		if(data == pNode->data)
			return pNode;
		pNode = pNode ->next;
	}

	return NULL;
}

    (5)双向链表中插入数据

STATUS insert_data_into_double_link(DOUBLE_LINK_NODE** ppDLinkNode, int data)
{
	DOUBLE_LINK_NODE* pNode;
	DOUBLE_LINK_NODE* pIndex;

	if(NULL == ppDLinkNode)
		return FALSE;

	if(NULL == *ppDLinkNode){
		pNode = create_double_link_node(data);
	    assert(NULL != pNode);
		*ppDLinkNode = pNode;
		(*ppDLinkNode)->prev = (*ppDLinkNode)->next = NULL;
		return TRUE;
	}

	if(NULL != find_data_in_double_link(*ppDLinkNode, data))
		return FALSE;

	pNode = create_double_link_node(data);
	assert(NULL != pNode);

	pIndex = *ppDLinkNode;
	while(NULL != pIndex->next)
		pIndex = pIndex->next;

	pNode->prev = pIndex;
	pNode->next = pIndex->next;
	pIndex->next = pNode;
	return TRUE;
}

    (6)双向链表中删除数据

STATUS delete_data_from_double_link(DOUBLE_LINK_NODE** ppDLinkNode, int data)
{
	DOUBLE_LINK_NODE* pNode;
	if(NULL == ppDLinkNode || NULL == *ppDLinkNode)
		return FALSE;

	pNode = find_data_in_double_link(*ppDLinkNode, data);
	if(NULL == pNode)
		return FALSE;

	if(pNode == *ppDLinkNode){
		if(NULL == (*ppDLinkNode)->next){
			*ppDLinkNode = NULL;
		}else{
			*ppDLinkNode = pNode->next;
			(*ppDLinkNode)->prev = NULL;
		}

	}else{
		if(pNode->next)
		    pNode->next->prev = pNode->prev;
	    pNode->prev->next = pNode->next;
	}

	free(pNode);
	return TRUE;
}

    (7)统计双向链表中数据的个数

int count_number_in_double_link(const DOUBLE_LINK_NODE* pDLinkNode)
{
	int count = 0;
	DOUBLE_LINK_NODE* pNode = (DOUBLE_LINK_NODE*)pDLinkNode;

	while(NULL != pNode){
		count ++;
		pNode = pNode->next;
	}
	return count;
}

    (8)打印双向链表中数据

void print_double_link_node(const DOUBLE_LINK_NODE* pDLinkNode)
{
	DOUBLE_LINK_NODE* pNode = (DOUBLE_LINK_NODE*)pDLinkNode;

	while(NULL != pNode){
		printf("%d\n", pNode->data);
		pNode = pNode ->next;
	}
}

    注意:

        今天我们讨论的双向链表是非循环的,大家可以考虑一下如果改成循环双向链表,应该怎么写?如果是有序的循环双向链表,又该怎么写?

【预告: 下面我们讨论的是循环单向链表】

时间: 2024-10-22 21:58:00

一步一步写算法(之双向链表)的相关文章

一步一步写算法(之 算法总结)

原文:一步一步写算法(之 算法总结) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com]       自10月初编写算法系列的博客以来,陆陆续续以来写了几十篇.按照计划,还有三个部分的内容没有介绍,主要是(Dijkstra算法.二叉平衡树.红黑树).这部分会在后面的博客补充完整.这里主要是做一个总结,有兴趣的朋友可以好好看看,欢迎大家提出宝贵意见.       (1) 排序算法     快速排序           合并排序     堆排序

一步一步写算法(之排序二叉树)

原文:一步一步写算法(之排序二叉树) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com]     前面我们讲过双向链表的数据结构.每一个循环节点有两个指针,一个指向前面一个节点,一个指向后继节点,这样所有的节点像一颗颗珍珠一样被一根线穿在了一起.然而今天我们讨论的数据结构却有一点不同,它有三个节点.它是这样定义的: typedef struct _TREE_NODE { int data; struct _TREE_NODE* parent;

一步一步写算法(之单向链表)

原文:一步一步写算法(之单向链表) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com]     有的时候,处于内存中的数据并不是连续的.那么这时候,我们就需要在数据结构中添加一个属性,这个属性会记录下面一个数据的地址.有了这个地址之后,所有的数据就像一条链子一样串起来了,那么这个地址属性就起到了穿线连结的作用.     相比较普通的线性结构,链表结构的优势是什么呢?我们可以总结一下:     (1)单个节点创建非常方便,普通的线性内存通常在创

一步一步写算法(之二叉树深度遍历)

原文:一步一步写算法(之二叉树深度遍历) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com]     深度遍历是软件开发中经常遇到的遍历方法.常用的遍历方法主要有下面三种:(1)前序遍历:(2)中序遍历:(3)后序遍历.按照递归的方法,这三种遍历的方法其实都不困难,前序遍历就是根-左-右,中序遍历就是左-根-右,后续遍历就是左-右-根.代码实现起来也不复杂.     1)前序遍历 void preorder_traverse(TREE_NOD

一步一步写算法(之克鲁斯卡尔算法 下)

原文:一步一步写算法(之克鲁斯卡尔算法 下) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com]     前面在讨论克鲁斯卡尔的算法的时候,我们分析了算法的基本过程.基本数据结构和算法中需要解决的三个问题(排序.判断.合并).今天,我们继续完成剩下部分的内容.合并函数中,我们调用了两个基本函数,find_tree_by_index和delete_mini_tree_from_group,下面给出详细的计算过程. MINI_GENERATE_T

一步一步写算法(之线性结构的处理)

原文:一步一步写算法(之线性结构的处理) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com]     我们知道,在内存中的空间都是连续的.也就是说,0x00000001下面的地址必然是0x00000002.所以,空间上是不会出现地址的突变的.那什么数据结构类型是连续内部空间呢,其实就是数组,当然也可以是堆.数组有很多优势,它可以在一段连续空间内保存相同类型的数据,并且对这些数据进行管理.所以从这个意义上说,掌握了数组才能说明你数据结构入门了.

一步一步写算法(之排序二叉树线索化)

原文:一步一步写算法(之排序二叉树线索化) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com]     前面我们谈到了排序二叉树,还没有熟悉的同学可以看一下这个,二叉树基本操作.二叉树插入.二叉树删除1.删除2.删除3.但是排序二叉树也不是没有缺点,比如说,如果我们想在排序二叉树中删除一段数据的节点怎么办呢?按照现在的结构,我们只能一个一个数据查找验证,首先看看在不在排序二叉树中,如果在那么删除:如果没有这个数据,那么继续查找.那么有没有方法

一步一步写算法(之字符串查找 中篇)

原文:一步一步写算法(之字符串查找 中篇) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com]     昨天我们编写了简单的字符查找函数.虽然比较简单,但是也算能用.然而,经过我们仔细分析研究一下,这么一个简单的函数还是有改进的空间的.在什么地方改进呢?大家可以慢慢往下看.     下面的代码是优化前的代码,现在再贴一次,这样分析起来也方便些: char* strstr(const char* str, char* data) { int i

一步一步写算法(之非递归排序)

原文:一步一步写算法(之非递归排序) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com]        在上面一篇博客当中,我们发现普通查找和排序查找的性能差别很大.作为一个100万的数据,如果使用普通的查找方法,那么每一个数据查找平均下来就要几十万次,那么二分法的查找呢,20多次就可以搞定.这中间的差别是非常明显的.既然排序有这么好的效果,那么这篇博客中,我们就对排序算做一个总结.     按照我个人的理解,排序可以分为两种:一种是非递归排