数据结构与算法-线性表

线性表作为一种常用的数据结构,在实际中有着广泛的运用。

线性表基本概念

线性表定义

  • 线性表(List)是零个或多个数据元素的集合
  • 线性表中的数据元素之间是有顺序的
  • 线性表中的数据元素个数是有限的
  • 线性表中的数据元素的类型必须相同

数学定义

线性表是具有相同类型的 n( ≥ 0)个数据元素的有限序列
(a1, a2,···, an)
ai是表项,n 是表长度

性质

a0为线性表的第一个元素,只有一个后继
an为线性表的最后一个元素,只有一个前驱
除a0和an外的其它元素ai,既有前驱,又有后继
线性表能够逐项访问和顺序存取

线性表的操作

创建线性表
销毁线性表
清空线性表
将元素插入线性表
将元素从线性表中删除
获取线性表中某个位置的元素
获取线性表的长度

线性表在程序中表现为一种特殊的数据类型
线性表的操作在程序中的表现为一组函数

#ifndef __LIST_H_
#define __LIST_H_

typedef void List;
typedef void ListNode;

//创建并且返回一个空的线性表
List* LinkList_Create();

//销毁一个线性表list
void List_Destroy(List* list);

//将一个线性表list中的所有元素清空, 线性表回到创建时的初始状态
void List_Clear(List* list);

//返回一个线性表list中的所有元素个数
int List_Length(List* list);

//向一个线性表list的pos位置处插入新元素node
int List_Insert(List* list, ListNode* node, int pos);

//获取一个线性表list的pos位置处的元素
ListNode* List_Get(List* list, int pos);

//删除一个线性表list的pos位置处的元素  返回值为被删除的元素,NULL表示删除失败
ListNode* List_Delete(List* list, int pos);

#endif

线性表的顺序存储结构

基本概念

顺序存储

设计与实现

  • 插入元素算法
    判断线性表是否合法
    判断插入位置是否合法
    把最后一个元素到插入位置的元素后移一个位置
    将新元素插入
    线性表长度加1

  • 获取元素操作
    判断线性表是否合法
    判断位置是否合法
    直接通过数组下标的方式获取元素

  • 删除元素算法
    判断线性表是否合法
    判断删除位置是否合法
    将元素取出
    将删除位置后的元素分别向前移动一个位置
    线性表长度减1

(*.h)

#ifndef __MY_SEQLIST_H__ 
#define __MY_SEQLIST_H__

typedef void SeqList;
typedef void SeqListNode;

SeqList* SeqList_Create(int capacity);

void SeqList_Destroy(SeqList* list);

void SeqList_Clear(SeqList* list);

int SeqList_Length(SeqList* list);

int SeqList_Capacity(SeqList* list);

int SeqList_Insert(SeqList* list, SeqListNode* node, int pos);

SeqListNode* SeqList_Get(SeqList* list, int pos);

SeqListNode* SeqList_Delete(SeqList* list, int pos);

#endif

(*.c)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "seqlist.h"

typedef struct _tag_SeqList
{
	int capacity;
	int length;
	unsigned int *node; //unsigned int array[capacity]
}TSeqList;

SeqList* SeqList_Create(int capacity)
{
	TSeqList *ret = NULL;
	if (capacity < 0)
	{
		return NULL;
	}
	ret = (TSeqList *)malloc(sizeof(TSeqList) + sizeof(unsigned int)*capacity);
	if (ret == NULL)
	{
		return NULL;
	}
	memset(ret, 0, sizeof(TSeqList) + sizeof(unsigned int)*capacity);
	ret->node = (unsigned int *)(ret + 1); //ret向后跳sizeof(TSeqList)
	ret->capacity = capacity;
	ret->length = 0;
	return ret;
}

void SeqList_Destroy(SeqList* list)
{
	if (list == NULL)
	{
		return;
	}
	free(list);
	return;
}

//链表清零
void SeqList_Clear(SeqList* list)
{
	TSeqList *tList = NULL;

	if (list == NULL)
	{
		return;
	}
	tList = (TSeqList *)list;
	tList->length = 0;
	return;
}

int SeqList_Length(SeqList* list)
{
	TSeqList *tList = NULL;
	tList = (TSeqList *)list;
	if (list == NULL)
	{
		return -1;
	}

	return tList->length;
}

//线性表的容量和线性表长度是不一样的
int SeqList_Capacity(SeqList* list)
{
	TSeqList *tList = NULL;
	tList = (TSeqList *)list;
	if (list == NULL)
	{
		return -1;
	}

	return tList->capacity;
}

int SeqList_Insert(SeqList* list, SeqListNode* node, int pos)
{
	int			i = 0;
	TSeqList	*tList = NULL;

	tList = (TSeqList *)list;

	if (list == NULL || node == NULL)
	{
		return -1;
	}

	//查看是不是满了
	if (tList->length >= tList->capacity)
	{
		return -2;
	}

	//位置错误判断
	if (pos < 0 || pos >= tList->capacity)
	{
		return -3;
	}

	//优化的容错。。。
	if (pos >= tList->length)
	{
		pos = tList->length;
	}

	//插入算法
	//从pos位置处开始,把数组后面的元素依此后移
	for (i = tList->length; i > pos; i--)
	{
		//把前的元素往后移
		tList->node[i] = tList->node[i - 1];
	}
	//循环跳出以后,pos正好是,要出入的位置
	tList->node[pos] = (unsigned int)node;
	tList->length++;
	return 0;
}

SeqListNode* SeqList_Get(SeqList* list, int pos)
{

	SeqListNode		*ret = NULL;
	TSeqList		*tList = NULL;

	tList = (TSeqList *)list;
	if (list == NULL || pos < 0 || pos >= tList->length)
	{
		return NULL;
	}
	ret = (SeqListNode*)tList->node[pos];
	return ret;
}

SeqListNode* SeqList_Delete(SeqList* list, int pos)
{
	int				i;
	TSeqList		*tList = NULL;
	SeqListNode		*ret = NULL;

	tList = (TSeqList *)list;

	if (list == NULL || pos < 0 || pos >= tList->length)
	{
		return NULL;
	}

	//赋给之前,要把元素缓存下来
	ret = (SeqListNode *)tList->node[pos];
	//删除算法
	for (i = pos + 1; i < tList->length; i++)
	{
		tList->node[i - 1] = tList->node[i];
	}
	tList->length--;

	return ret;
}

(test)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "seqlist.h"

typedef struct _Teacher
{
	char name[64];
	int age;
}Teacher;

int main()
{
	int i = 0, len = 0;
	SeqList *list = NULL;

	Teacher t1, t2, t3;
	t1.age = 31;
	t2.age = 32;
	t3.age = 33;

	list = SeqList_Create(10);

	//头插法
	SeqList_Insert(list, (SeqListNode*)&t1, 0);
	SeqList_Insert(list, (SeqListNode*)&t2, 0);
	SeqList_Insert(list, (SeqListNode*)&t3, 0);

	//循环遍历
	for (i = 0; i < SeqList_Length(list); i++)
	{
		Teacher *tmp = (Teacher *)SeqList_Get(list, i);
		if (tmp != NULL)
		{
			printf("age:%d ", tmp->age);
		}
	}

	//循环删除
	len = SeqList_Length(list);
	for (i = 0; i < len; i++)
	{
		SeqList_Delete(list, 0);
	}
	SeqList_Destroy(list);

	system("pause");
}

优点和缺点

  • 优点:
    无需为线性表中的逻辑关系增加额外的空间
    可以快速的获取表中合法位置的元素

  • 缺点:
    插入和删除操作需要移动大量元素
    当线性表长度变化较大时难以确定存储空间的容量

线性表的链式存储

基本概念

为了表示每个数据元素与其直接后继元素之间的逻辑关系,每个元素除了存储本身的信息外,还需要存储指示其直接后继的信息
顺序存储1
顺序存储2

  • 表头结点
    链表中的第一个结点,包含指向第一个数据元素的指针以及链表自身的一些信息
  • 数据结点
    链表中代表数据元素的结点,包含指向下一个数据元素的指针和数据元素的信息
  • 尾结点
    链表中的最后一个数据结点,其下一元素指针为空,表示无后继

设计与实现

在C语言中可以用结构体来定义链表中的指针域
链表中的表头结点也可以用结构体实现
顺序存储定义

  • 插入元素
    顺序存储插入操作
    带头结点、位置从0的单链表
    返回链表中第3个位置处,元素的值
LinkListNode* LinkList_Get(LinkList* list, int pos)
{ 
	int  i = 0;
	TLinkList *tList = (TLinkList *)list;
	LinkListNode *current = NULL;
	LinkListNode *ret = NULL;

	if (list==NULL ||pos<0 || pos>=tList->length)
	{
		return NULL;
	}
	current = (LinkListNode *)tList;
	for (i=0; i<pos; i++)
	{
		current = current->next;
	}
	ret = current->next;
	return ret ;
}

返回第三个位置的
移动pos次以后,当前指针指向哪里?
答案:指向位置2,所以需要返回 ret = current->next;
备注:循环遍历时,遍历第1次,指向位置0;遍历第2次,指向位置1;遍历第3次,指向位置2;遍历第n次,指向位置n-1;
所以如果想返回位置n的元素的值,ret = current->next;

  • 删除元素
    顺序存储删除操作
    (*.h)
#ifndef __MY_LINKLIST_H_
#define __MY_LINKLIST_H_

typedef void LinkList;
/*
typedef struct _tag_LinkListNode LinkListNode;
struct _tag_LinkListNode
{
LinkListNode* next;
};
*/
typedef struct _tag_LinkListNode
{
	struct _tag_LinkListNode* next;
}LinkListNode;

LinkList* LinkList_Create();

void LinkList_Destroy(LinkList* list);

void LinkList_Clear(LinkList* list);

int LinkList_Length(LinkList* list);

int LinkList_Insert(LinkList* list, LinkListNode* node, int pos);

LinkListNode* LinkList_Get(LinkList* list, int pos);

LinkListNode* LinkList_Delete(LinkList* list, int pos);

#endif

(*.c)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "linklist.h"

typedef struct _tag_LinkList
{
	LinkListNode header;
	int length;
}TLinkList;

LinkList* LinkList_Create()
{
	TLinkList *ret = (TLinkList *)malloc(sizeof(TLinkList));
	if (ret == NULL)
	{
		return NULL;
	}
	//memset(ret, 0, sizeof(TLinkList));
	ret->header.next = NULL;
	ret->length = 0;
	return ret;
}

void LinkList_Destroy(LinkList* list)
{
	if (list == NULL)
	{
		return;
	}
	free(list);
	return;
}

void LinkList_Clear(LinkList* list)
{
	TLinkList *tList = NULL;

	if (list == NULL)
	{
		return;
	}
	tList = (TLinkList *)list;
	tList->length = 0;
	tList->header.next = NULL;
	return;
}

int LinkList_Length(LinkList* list)
{

	TLinkList *tList = (TLinkList *)list;
	if (tList == NULL)
	{
		return -1;
	}

	return tList->length;
}

int LinkList_Insert(LinkList* list, LinkListNode* node, int pos)
{
	int				i = 0;
	TLinkList		*tList = NULL;
	LinkListNode	*current = NULL;

	tList = (TLinkList *)list;
	//准备环境让辅助指针变量 指向链表头节点
	current = &tList->header;
	for (i = 0; i < pos && (current->next != NULL); i++)
	{
		current = current->next;
	}
	//让node节点链接后续链表
	node->next = current->next;
	//让前边的链表。链接node
	current->next = node;
	tList->length++;
	return 0;
}

LinkListNode* LinkList_Get(LinkList* list, int pos)
{
	int				i = 0;
	TLinkList		*tList = NULL;
	LinkListNode	*current = NULL;
	LinkListNode	*ret = NULL;

	tList = (TLinkList *)list;

	if (list == NULL || pos < 0 || pos >= tList->length)
	{
		return NULL;
	}
	//准备环境让辅助指针变量 指向链表头节点
	current = &tList->header;
	for (i = 0; i < pos && (current->next != NULL); i++)
	{
		current = current->next;
	}
	ret = current->next;

	return ret;
}

LinkListNode* LinkList_Delete(LinkList* list, int pos)
{
	int				i = 0;
	TLinkList		*tList = NULL;
	LinkListNode	*current = NULL;
	LinkListNode	*ret = NULL;

	tList = (TLinkList *)list;

	if (list == NULL || pos < 0 || pos >= tList->length)
	{
		return NULL;
	}
	//准备环境让辅助指针变量 指向链表头节点
	current = &tList->header;
	for (i = 0; i < pos && (current->next != NULL); i++)
	{
		current = current->next;
	}
	ret = current->next;
	//删除算法
	current->next = ret->next;
	tList->length--;
	return ret;
}

(test)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "linklist.h"

typedef struct Teacher
{
	LinkListNode node;
	char name[64];
	int age;
}Teacher;

int main()
{
	Teacher		t1, t2, t3;
	int			length, i = 0;
	LinkList	*list = NULL;

	t1.age = 31;
	t2.age = 32;
	t3.age = 33;

	list = LinkList_Create();
	length = LinkList_Length(list);

	LinkList_Insert(list, (LinkListNode *)&t1, LinkList_Length(list));
	LinkList_Insert(list, (LinkListNode *)&t2, LinkList_Length(list));
	LinkList_Insert(list, (LinkListNode *)&t3, LinkList_Length(list));

	//遍历链表 
	for (i = 0; i < LinkList_Length(list); i++)
	{
		Teacher *tmp = (Teacher *)LinkList_Get(list, i);
		if (tmp != NULL)
		{
			printf("age:%d ", tmp->age);
		}
	}
	while (LinkList_Length(list) > 0)
	{
		Teacher *tmp = (Teacher *)LinkList_Delete(list, 0);
		if (tmp != NULL)
		{
			printf("age:%d ", tmp->age);
		}
	}
	LinkList_Destroy(list);
	system("pause");
	return 0;
}

优点和缺点

  • 优点:
    无需一次性定制链表的容量
    插入和删除操作无需移动数据元素

  • 缺点:
    数据元素必须保存后继元素的位置信息
    获取指定数据的元素操作需要顺序访问之前的元素

循环链表

基本概念

将单链表中最后一个数据元素的next指针指向第一个元素
循环链表
循环链表拥有单链表的所有操作

  • 创建链表
  • 销毁链表
  • 获取链表长度
  • 清空链表
  • 获取第pos个元素操作
  • 插入元素到位置pos
  • 删除位置pos处的元素
  • 新增功能:游标的定义

在循环链表中可以定义一个“当前”指针,这个指针通常称为游标,可以通过这个游标来遍历链表中的所有元素
循环链表游标
获取当前游标指向的数据元素
将游标重置指向链表中的第一个数据元素
将游标移动指向到链表中的下一个数据元素
直接指定删除链表中的某个数据元素

CircleListNode* CircleList_DeleteNode(CircleList* list, CircleListNode* node);
CircleListNode* CircleList_Reset(CircleList* list);
CircleListNode* CircleList_Current(CircleList* list);
CircleListNode* CircleList_Next(CircleList* list);

设计与实现

  • 插入元素的分析
    • 普通位置插入元素
    • 添加第一个元素(第一次插入元素)
    • 最后一个位置插入元素
    • 第一个位置插入元素

(*.h)

#ifndef __CIRCLELIST_H_
#define __CIRCLELIST_H_

typedef void CircleList;
/*
typedef struct _tag_CircleListNode CircleListNode;
struct _tag_CircleListNode
{
	CircleListNode* next;
};
*/
typedef struct _tag_CircleListNode
{
	struct _tag_CircleListNode * next;
}CircleListNode;

CircleList* CircleList_Create();

void CircleList_Destroy(CircleList* list);

void CircleList_Clear(CircleList* list);

int CircleList_Length(CircleList* list);

int CircleList_Insert(CircleList* list, CircleListNode* node, int pos);

CircleListNode* CircleList_Get(CircleList* list, int pos);

CircleListNode* CircleList_Delete(CircleList* list, int pos);

//add
CircleListNode* CircleList_DeleteNode(CircleList* list, CircleListNode* node);

CircleListNode* CircleList_Reset(CircleList* list);

CircleListNode* CircleList_Current(CircleList* list);

CircleListNode* CircleList_Next(CircleList* list);

#endif

(*.c)

#include <stdio.h>
#include <malloc.h>

#include "CircleList.h"

typedef struct _tag_CircleList
{
	CircleListNode header;
	CircleListNode* slider;
	int length;
} TCircleList;

CircleList* CircleList_Create() // O(1)
{
	TCircleList* ret = (TCircleList*)malloc(sizeof(TCircleList));
	if (ret == NULL)
	{
		return NULL;
	}
	ret->length = 0;
	ret->header.next = NULL;
	ret->slider = NULL;
	return ret;
}

void CircleList_Destroy(CircleList* list) // O(1)
{
	if (list == NULL)
	{
		return;
	}
	free(list);
}

void CircleList_Clear(CircleList* list) // O(1)
{
	TCircleList* sList = (TCircleList*)list;
	if (sList == NULL)
	{
		return;
	}
	sList->length = 0;
	sList->header.next = NULL;
	sList->slider = NULL;
}

int CircleList_Length(CircleList* list) // O(1)
{
	TCircleList* sList = (TCircleList*)list;
	int ret = -1;
	if (list == NULL)
	{
		return ret;
	}
	ret = sList->length;
	return ret;
}

int CircleList_Insert(CircleList* list, CircleListNode* node, int pos) // O(n)
{
	int				ret = 0, i = 0;
	TCircleList*	sList = (TCircleList*)list;
	if (list == NULL || node == NULL || pos < 0)
	{
		return -1;
	}
	CircleListNode* current = (CircleListNode*)sList;
	for (i = 0; (i < pos) && (current->next != NULL); i++)
	{
		current = current->next;
	}
	//current->next 0号节点的地址
	node->next = current->next; //1
	current->next = node; //2

	//若第一次插入节点
	if (sList->length == 0)
	{
		sList->slider = node;
	}
	sList->length++;
	//若头插法
	if (current == (CircleListNode*)sList)
	{
		//获取最后一个元素
		CircleListNode* last = CircleList_Get(sList, sList->length - 1);
		last->next = current->next; //3

	}
	return ret;
}

CircleListNode* CircleList_Get(CircleList* list, int pos) // O(n)
{
	TCircleList*		sList = (TCircleList*)list;
	CircleListNode*		ret = NULL;
	int					i = 0;
	if (list == NULL || pos < 0)
	{
		return NULL;
	}
	//if( (sList != NULL) && (pos >= 0) && (sList->length > 0) )
	{
		CircleListNode* current = (CircleListNode*)sList;

		for (i = 0; i < pos; i++)
		{
			current = current->next;
		}

		ret = current->next;
	}
	return ret;
}

CircleListNode* CircleList_Delete(CircleList* list, int pos) // O(n)
{
	TCircleList* sList = (TCircleList*)list;
	CircleListNode* ret = NULL;
	int i = 0;
	if ((sList != NULL) && (pos >= 0) && (sList->length > 0))
	{
		CircleListNode* current = (CircleListNode*)sList;
		CircleListNode* last = NULL;
		for (i = 0; i < pos; i++)
		{
			current = current->next;
		}
		//若删除第一个元素
		if (current == (CircleListNode*)sList)
		{
			last = (CircleListNode*)CircleList_Get(sList, sList->length - 1);
		}
		//求要删除的元素
		ret = current->next;
		current->next = ret->next;
		sList->length--;
		//判断链表是否为空
		if (last != NULL)
		{
			sList->header.next = ret->next;
			last->next = ret->next;
		}
		//若删除的元素为游标所指的元素
		if (sList->slider == ret)
		{
			sList->slider = ret->next;
		}
		//若删除元素后,链表长度为0
		if (sList->length == 0)
		{
			sList->header.next = NULL;
			sList->slider = NULL;
		}
	}
	return ret;
}

CircleListNode* CircleList_DeleteNode(CircleList* list, CircleListNode* node) // O(n)
{
	TCircleList* sList = (TCircleList*)list;
	CircleListNode* ret = NULL;
	int i = 0;
	if (sList != NULL)
	{
		CircleListNode* current = (CircleListNode*)sList;
		//查找node在循环链表中的位置i
		for (i = 0; i < sList->length; i++)
		{
			if (current->next == node)
			{
				ret = current->next;
				break;
			}
			current = current->next;
		}
		//如果ret找到,根据i去删除	
		if (ret != NULL)
		{
			CircleList_Delete(sList, i);
		}
	}
	return ret;
}

CircleListNode* CircleList_Reset(CircleList* list) // O(1)
{
	TCircleList*		sList = (TCircleList*)list;
	CircleListNode*		ret = NULL;
	if (sList != NULL)
	{
		sList->slider = sList->header.next;
		ret = sList->slider;
	}
	return ret;
}

CircleListNode* CircleList_Current(CircleList* list) // O(1)
{
	TCircleList*		sList = (TCircleList*)list;
	CircleListNode*		ret = NULL;
	if (sList != NULL)
	{
		ret = sList->slider;
	}
	return ret;
}

CircleListNode* CircleList_Next(CircleList* list) // O(1)
{
	TCircleList* sList = (TCircleList*)list;
	CircleListNode* ret = NULL;
	if ((sList != NULL) && (sList->slider != NULL))
	{
		ret = sList->slider;
		sList->slider = ret->next;
	}
	return ret;
}

(test)

#include <stdio.h>
#include <stdlib.h>

#include "CircleList.h"

struct Value
{
	CircleListNode circlenode;
	int v;
};

int main()
{
	int				i = 0;
	CircleList*		list = CircleList_Create();

	struct Value v1;
	struct Value v2;
	struct Value v3;
	struct Value v4;
	struct Value v5;
	struct Value v6;
	struct Value v7;
	struct Value v8;

	v1.v = 1;
	v2.v = 2;
	v3.v = 3;
	v4.v = 4;
	v5.v = 5;
	v6.v = 6;
	v7.v = 7;
	v8.v = 8;

	CircleList_Insert(list, (CircleListNode*)&v1, 0);
	CircleList_Insert(list, (CircleListNode*)&v2, 0);
	CircleList_Insert(list, (CircleListNode*)&v3, 0);
	CircleList_Insert(list, (CircleListNode*)&v4, 0);

	for (i = 0; i < 2 * CircleList_Length(list); i++)
	{
		struct Value* pv = (struct Value*)CircleList_Get(list, i);
		printf("%d
", pv->v);
	}

	while (CircleList_Length(list) > 0)
	{
		CircleList_Delete(list, 0);
	}
	printf("
");

	CircleList_Destroy(list);

	system("pause");
	return 0;
}

优点和缺点

  • 优点:功能强了。
    循环链表只是在单链表的基础上做了一个加强
    循环链表可以完全取代单链表的使用
    循环链表的Next和Current操作可以高效的遍历链表中的所有元素

  • 缺点:
    代码复杂度提高了

约瑟夫问题-循环链表典型应用
n 个人围成一个圆圈,首先第 1 个人从 1 开始一个人一个人顺时针报数,报到第 m 个人,令其出列。然后再从下一 个人开始从 1 顺时针报数,报到第 m 个人,再令其出列,···,如此下去,求出列顺序
循环链表约瑟夫问题

#include <stdio.h>
#include <stdlib.h>

#include "CircleList.h"

struct Value
{
	CircleListNode header;
	int v;
};

void main()
{
	int				i = 0;
	CircleList*		list = CircleList_Create();

	struct Value v1, v2, v3, v4, v5, v6, v7, v8;

	v1.v = 1;	v2.v = 2;	v3.v = 3;	v4.v = 4;
	v5.v = 5;	v6.v = 6;	v7.v = 7;	v8.v = 8;

	CircleList_Insert(list, (CircleListNode*)&v1, CircleList_Length(list));
	CircleList_Insert(list, (CircleListNode*)&v2, CircleList_Length(list));
	CircleList_Insert(list, (CircleListNode*)&v3, CircleList_Length(list));
	CircleList_Insert(list, (CircleListNode*)&v4, CircleList_Length(list));
	CircleList_Insert(list, (CircleListNode*)&v5, CircleList_Length(list));
	CircleList_Insert(list, (CircleListNode*)&v6, CircleList_Length(list));
	CircleList_Insert(list, (CircleListNode*)&v7, CircleList_Length(list));
	CircleList_Insert(list, (CircleListNode*)&v8, CircleList_Length(list));

	for (i = 0; i < CircleList_Length(list); i++)
	{
		struct Value* pv = (struct Value*)CircleList_Next(list);
		printf("%d
", pv->v);
	}
	printf("
");

	//重置游标
	CircleList_Reset(list);
	while (CircleList_Length(list) > 0)
	{
		struct Value* pv = NULL;
		for (i = 1; i < 3; i++)
		{
			CircleList_Next(list);
		}
		pv = (struct Value*)CircleList_Current(list);
		printf("%d
", pv->v);
		CircleList_DeleteNode(list, (CircleListNode*)pv);
	}
	CircleList_Destroy(list);

	system("pause");
}

双向链表

基本概念

单链表的结点都只有一个指向下一个结点的指针
单链表的数据元素无法直接访问其前驱元素
逆序访问单链表中的元素是极其耗时的操作

len = LinkList_Length(list);
for (i=len-1; len>=0; i++) //O(n)
{
LinkListNode *p = LinkList_Get(list, i); //O(n)
//访问数据元素p中的元素
}

双向链表的定义
在单链表的结点中增加一个指向其前驱的pre指针
双向链表
双向链表拥有单链表的所有操作

  • 创建链表
  • 销毁链表
  • 获取链表长度
  • 清空链表
  • 获取第pos个元素操作
  • 插入元素到位置pos
  • 删除位置pos处的元素

设计与实现

  • 插入操作
    双向链表插入操作

  • 删除操作
    双向链表删除操作
    双向链表的新操作
    获取当前游标指向的数据元素
    将游标重置指向链表中的第一个数据元素
    将游标移动指向到链表中的下一个数据元素
    将游标移动指向到链表中的上一个数据元素
    直接指定删除链表中的某个数据元素

DLinkListNode* DLinkList_DeleteNode(DLinkList* list, DLinkListNode* node);
DLinkListNode* DLinkList_Reset(DLinkList* list);
DLinkListNode* DLinkList_Current(DLinkList* list);
DLinkListNode* DLinkList_Next(DLinkList* list);
DLinkListNode* DLinkList_Pre(DLinkList* list);

(*.h)

#ifndef __MY_DLINKLIST_H_
#define __MY_DLINKLIST_H_

typedef void DLinkList;
/*
typedef struct _tag_DLinkListNode DLinkListNode;
struct _tag_DLinkListNode
{
DLinkListNode* next;
DLinkListNode* pre;
};
*/

typedef struct _tag_DLinkListNode
{
	struct _tag_DLinkListNode* next;
	struct _tag_DLinkListNode * pre;
}DLinkListNode;

DLinkList* DLinkList_Create();

void DLinkList_Destroy(DLinkList* list);

void DLinkList_Clear(DLinkList* list);

int DLinkList_Length(DLinkList* list);

int DLinkList_Insert(DLinkList* list, DLinkListNode* node, int pos);

DLinkListNode* DLinkList_Get(DLinkList* list, int pos);

DLinkListNode* DLinkList_Delete(DLinkList* list, int pos);

//add
DLinkListNode* DLinkList_DeleteNode(DLinkList* list, DLinkListNode* node);

DLinkListNode* DLinkList_Reset(DLinkList* list);

DLinkListNode* DLinkList_Current(DLinkList* list);

DLinkListNode* DLinkList_Next(DLinkList* list);

DLinkListNode* DLinkList_Pre(DLinkList* list);

#endif

(*.c)

#include <stdio.h>
#include <malloc.h>

#include "DLinkList.h"

typedef struct _tag_DLinkList
{
	DLinkListNode header;
	DLinkListNode* slider;
	int length;
} TDLinkList;

DLinkList* DLinkList_Create()
{
	TDLinkList* ret = (TDLinkList*)malloc(sizeof(TDLinkList));

	if (ret != NULL)
	{
		ret->length = 0;
		ret->header.next = NULL;
		ret->header.pre = NULL;
		ret->slider = NULL;
	}

	return ret;
}

void DLinkList_Destroy(DLinkList* list)
{
	if (list != NULL)
	{
		free(list);
	}
}

void DLinkList_Clear(DLinkList* list)
{
	TDLinkList* sList = (TDLinkList*)list;

	if (sList != NULL)
	{
		sList->length = 0;
		sList->header.next = NULL;
		sList->header.pre = NULL;
		sList->slider = NULL;
	}
}

int DLinkList_Length(DLinkList* list)
{
	int				ret = -1;
	TDLinkList*		sList = (TDLinkList*)list;

	if (sList != NULL)
	{
		ret = sList->length;
	}

	return ret;
}

//大家一定要注意:教科书不会告诉你 项目上如何用;哪些点是项目的重点
int DLinkList_Insert(DLinkList* list, DLinkListNode* node, int pos)
{
	int				ret = 0, i = 0;
	TDLinkList*		sList = (TDLinkList*)list;

	if (list == NULL || node == NULL || pos < 0)
	{
		return -1;
	}

	{
		DLinkListNode* current = (DLinkListNode*)sList;
		DLinkListNode* next = NULL; //需要增加next指针

		for (i = 0; (i < pos) && (current->next != NULL); i++)
		{
			current = current->next;
		}

		next = current->next;

		//步骤1-2
		current->next = node;
		node->next = next;

		//步骤3-4 
		if (next != NULL) //当链表插入第一个元素,需要特殊处理
		{
			next->pre = node;
		}
		node->pre = current;

		if (sList->length == 0)
		{
			sList->slider = node; //当链表插入第一个元素处理游标
		}

		//若在0位置插入,需要特殊处理 新来结点next前pre指向null
		if (current == (DLinkListNode*)sList)
		{
			node->pre = NULL;
		}

		sList->length++;
	}

	return ret;
}

DLinkListNode* DLinkList_Get(DLinkList* list, int pos)
{
	int				i = 0;
	TDLinkList*		sList = (TDLinkList*)list;
	DLinkListNode*	ret = NULL;

	if ((sList != NULL) && (0 <= pos) && (pos < sList->length))
	{
		DLinkListNode* current = (DLinkListNode*)sList;

		for (i = 0; i < pos; i++)
		{
			current = current->next;
		}

		ret = current->next;
	}

	return ret;
}

//插入第一个节点
//删除的是最后一个结点,该是如何处理
DLinkListNode* DLinkList_Delete(DLinkList* list, int pos)
{
	int				i = 0;
	TDLinkList*		sList = (TDLinkList*)list;
	DLinkListNode*	ret = NULL;

	if (sList == NULL || pos < 0)
	{
		return NULL;
	}
	//if( (sList != NULL) && (0 <= pos) && (pos < sList->length) )
	{
		DLinkListNode* current = (DLinkListNode*)sList;
		DLinkListNode* next = NULL; //需要增加next指针

		for (i = 0; i < pos; i++)
		{
			current = current->next;
		}

		ret = current->next;
		next = ret->next;

		//步骤1
		current->next = next;

		//步骤2 
		if (next != NULL)//需要特殊处理
		{
			next->pre = current;

			if (current == (DLinkListNode*)sList) //若第0个位置,需要特殊处理
			{
				next->pre = NULL;
			}
		}

		if (sList->slider == ret)
		{
			sList->slider = next;
		}

		sList->length--;
	}

	return ret;
}

DLinkListNode* DLinkList_DeleteNode(DLinkList* list, DLinkListNode* node)
{
	int				i = 0;
	TDLinkList*		sList = (TDLinkList*)list;
	DLinkListNode*	ret = NULL;

	if (sList != NULL)
	{
		DLinkListNode* current = (DLinkListNode*)sList;

		for (i = 0; i < sList->length; i++)
		{
			if (current->next == node)
			{
				ret = current->next;
				break;
			}

			current = current->next;
		}

		if (ret != NULL)
		{
			DLinkList_Delete(sList, i);
		}
	}

	return ret;
}

DLinkListNode* DLinkList_Reset(DLinkList* list)
{
	TDLinkList*		sList = (TDLinkList*)list;
	DLinkListNode*	ret = NULL;

	if (sList != NULL)
	{
		sList->slider = sList->header.next;
		ret = sList->slider;
	}

	return ret;
}

DLinkListNode* DLinkList_Current(DLinkList* list)
{
	TDLinkList*		sList = (TDLinkList*)list;
	DLinkListNode*	ret = NULL;

	if (sList != NULL)
	{
		ret = sList->slider;
	}

	return ret;
}

DLinkListNode* DLinkList_Next(DLinkList* list)
{
	TDLinkList*		sList = (TDLinkList*)list;
	DLinkListNode*	ret = NULL;

	if ((sList != NULL) && (sList->slider != NULL))
	{
		ret = sList->slider;
		sList->slider = ret->next;
	}

	return ret;
}

DLinkListNode* DLinkList_Pre(DLinkList* list)
{
	TDLinkList*		sList = (TDLinkList*)list;
	DLinkListNode*	ret = NULL;

	if ((sList != NULL) && (sList->slider != NULL))
	{
		ret = sList->slider;
		sList->slider = ret->pre;
	}
	return ret;
}

(test)

#include <stdio.h>
#include <stdlib.h>

#include "DLinkList.h"

struct Value
{
	DLinkListNode node;
	int v;
};

int main()
{
	int i = 0;
	DLinkList* list = DLinkList_Create();
	struct Value* pv = NULL;
	struct Value v1, v2, v3, v4, v5;

	v1.v = 1;	v2.v = 2;	v3.v = 3;
	v4.v = 4;	v5.v = 5;

	DLinkList_Insert(list, (DLinkListNode*)&v1, DLinkList_Length(list));
	DLinkList_Insert(list, (DLinkListNode*)&v2, DLinkList_Length(list));
	DLinkList_Insert(list, (DLinkListNode*)&v3, DLinkList_Length(list));
	DLinkList_Insert(list, (DLinkListNode*)&v4, DLinkList_Length(list));
	DLinkList_Insert(list, (DLinkListNode*)&v5, DLinkList_Length(list));

	for(i=0; i<DLinkList_Length(list); i++)
	{
		pv = (struct Value*)DLinkList_Get(list, i);
		printf("%d
", pv->v);
	}
	printf("
");

	DLinkList_Delete(list, DLinkList_Length(list)-1);
	DLinkList_Delete(list, 0);
	//DLinkList_Delete(list, 3);

	for(i=0; i<DLinkList_Length(list); i++)
	{
		pv = (struct Value*)DLinkList_Next(list);
		printf("%d
", pv->v);
	}
	printf("
");

	DLinkList_Reset(list);
	DLinkList_Next(list);

	pv = (struct Value*)DLinkList_Current(list);
	printf("%d
", pv->v);

	DLinkList_DeleteNode(list, (DLinkListNode*)pv);

	pv = (struct Value*)DLinkList_Current(list);
	printf("%d
", pv->v);

	DLinkList_Pre(list);
	pv = (struct Value*)DLinkList_Current(list);
	printf("%d
", pv->v);

	printf("Length: %d
", DLinkList_Length(list));

	DLinkList_Destroy(list);

	system("pause");
	return 0;
}

优点和缺点

  • 优点:双向链表在单链表的基础上增加了指向前驱的指针
    功能上双向链表可以完全取代单链表的使用
    循环链表的Next,Pre和Current操作可以高效的遍历链表中的所有元素

  • 缺点:代码复杂

原文地址:https://www.cnblogs.com/cj5785/p/10664711.html