• C++ 数据结构 1:线性表


    1 数据结构

    1.1 数据结构中基本概念

    数据:程序的操作对象,用于描述客观事物。

    数据的特点:

    • 可以输入到计算机

    • 可以被计算机程序处理

    数据是一个抽象的概念,将其进行分类后得到程序设计语言中的类型。如:int,float,char等等。

    数据元素:组成数据的基本单位。

    数据项:一个数据元素由若干数据项组成。

    数据对象:性质相同的数据元素的集合(比如:数组,链表)。

    示例代码:

    //声明一个结构体类型
    struct _MyTeacher   //一种数据类型
    {
    	char	name[32];
    	char	tile[32];
    	int	age;
    	char	addr[128];
    };
    
    int main()
    {
    	struct _MyTeacher  t1;        //数据元素
            struct _MyTeacher tArray[30]; //数据对象
    	memset(&t1, 0, sizeof(t1));
    
    	strcpy(t1.name, "name"); //数据项
    	strcpy(t1.addr, "addr"); //数据项
    	strcpy(t1.tile, "addr"); //数据项
    	t1.age = 1;
    }
    

    数据元素之间不是独立的,存在特定的关系,这些关系即结构。

    数据结构 指 数据对象中数据元素 之间的关系。

    1.2 数据的逻辑结构

    数据元素之间的 逻辑关系。即 从逻辑关系上描述数据,它与数据的存储无关,是独立于计算机的。逻辑结构可细分为4类:

    • 集合:数据元素间除 “同属一个结合”外,无其他关系。

    • 线性结构:一个对一个,如线性表、栈、队列。

    • 树形结构:一个对多个,如树。

    • 图状结构:多个对多个,如图。

    1.3 数据的物理结构

    数据的物理结构也称存储结构,是 数据的逻辑结构在计算机存储器内的表示(或映像)。它依赖于计算机。

    存储结构可分为四大类:顺序、链式、索引、散列。

    最常用的存储结构:

    • 顺序存储结构:借助元素在存储器中的相对位置来表示数据元素间的逻辑关系。

    • 链式存储结构:借助指示元素存储地址的指针表示数据元素间的逻辑关系。

    1.4 数据的逻辑结构与存储结构的关系

    • 算法设计 -> 逻辑结构

    • 算法实现 -> 存储结构

    2 线性表

    2.1 线性表基本概念

    2.1.1 线性表定义

    定义:线性表(List)是零个或多个数据元素的有限序列。

    特点:

    • 数据元素之间是 有顺序的

    • 数据元素个数是 有限的

    • 数据元素的 类型必须相同

    2.1.2 数学定义

    线性表是具有相同类型的 n(≥ 0)个数据元素的有限序列(a0, a1, a2, …, an)。

    ai是表项,n 是表长度。

    2.1.3 性质

    a0 为线性表的第一个元素,只有一个后继。

    an 为线性表的最后一个元素,只有一个前驱。

    除 a0 和 an 外的其它元素 ai,既有前驱,又有后继。

    线性表能够逐项访问和顺序存取。

    2.1.4 线性表操作

    • 创建线性表

    • 销毁线性表

    • 清空线性表

    • 将元素插入线性表

    • 将元素从线性表中删除

    • 获取线性表中某个位置的元素

    • 获取线性表的长度

    2.1.5 线性表的抽象数据类型定义

    ADT线性表(List)
    
    Data
    线性表的数据对象集合为{a1,a2,……,an},每个元素的类型均为 DataType。其中,除第一个元素 a1 外,每个元素有且只有一个直接前驱元素,除了最后一个元素 an 外,每个元素有且只有一个直接后继元素。数据元素之间的关系是一一对应的。
    
    Operation(操作)
    	// 初始化,建立一个空的线性表L。
    	InitList(*L);
    	// 若线性表为空,返回true,否则返回false
    	ListEmpty(L);
    	// 将线性表清空
    	ClearList(*L);
    	// 将线性表 L 中的第 i 个位置的元素返回给 e
    	GetElem(L, i, *e);
    	// 在线性表L中的第 i 个位置插入新元素 e
    	ListInsert(*L, i, e);
    	// 删除线性表L中的第 i 个位置元素,并用 e 返回其值
    	ListDelete(*L,i,*e);
    	// 返回线性表 L 的元素个数
    	ListLength(L);
    	// 销毁线性表
    	DestroyList(*L);
    
    endADT
    

    2.2 顺序存储的线性表

    2.2.1 基本概念

    线性表的顺序存储结构,指的是 用一段地址连续的存储单元依次存储线性表的数据元素

    线性表 (a1,a2,……,an)的顺序存储示意图如下:

    2.2.2 设计与实现

    部分操作算法如下所示:

    插入元素算法:

    1. 判断线性表是否合法

    2. 判断插入位置是否合法

    3. 把最后一个元素到插入位置的元素后移一个位置

    4. 将新元素插入

    5. 线性表长度加1

    获取元素算法:

    1. 判断线性表是否合法

    2. 判断位置是否合法

    3. 直接通过数组下标的方式获取元素

    删除元素算法:

    1. 判断线性表是否合法

    2. 判断删除位置是否合法

    3. 将元素取出

    4. 将删除位置后的元素分别向前移动一个位置

    5. 线性表长度减1

    实现代码

    SqList.h

    #define MAXSIZE 50	// 最大容量
    typedef int ElemType;	// 数据类型
    
    
    // 定义线性表结构体
    typedef struct SQLIST
    {
        ElemType data[MAXSIZE];	// 数组
        int length;			//数组长度 
    }SqList;
    
    // 初始化,建立一个空的线性表L
    void InitList(SqList *L);
    
    // 若线性表为空,返回true,否则返回false
    int ListEmpty(SqList L);
    
    // 将线性表清空
    void ClearList(SqList *L);
    
    // 将线性表L 中的第 pos 个位置的元素返回给 e
    void GetElem(SqList L, int pos, ElemType *e);
    
    // 在线性表L 中的第 pos 个位置插入新元素 e
    void ListInsert(SqList *L, int pos, ElemType e);
    
    // 删除线性表L 中的第 pos 个位置元素,并用 e 返回其值
    void ListDelete(SqList *L, int pos, ElemType *e);
    
    // 返回线性表L 的元素个数
    int ListLength(SqList L);
    

    SqList.c

    #include "SqList.h"
    #include <string.h>
    
    void InitList(SqList *L)
    {
    	// 初始化
    	L->length = 0;
    	
    	memset(L->data, 0, sizeof(L->data));
    }
    
    int ListEmpty(SqList L)
    {
    	if (L.length == 0)
    	{
    		return 1;
    	}
    
    	return 0;
    }
    
    void ClearList(SqList *L)
    {
    	InitList(L);
    }
    
    void GetElem(SqList L, int pos, ElemType *e)
    {
    	// pos值是否合法
    	if (pos < 0 || pos >= L.length)
    	{
    		return;
    	}
    
    	// 如果为空
    	if (L.length == 0)
    	{
    		return;
    	}
    
    	// 赋值
    	*e = L.data[pos];
    }
    
    void ListInsert(SqList *L, int pos, ElemType e)
    {
    	int i = -1;
    
    	// 错误处理
    	if (pos < 0 || pos > L->length)
    	{
    		return;
    	}
    
    	if (L->length >= MAXSIZE)
    	{
    		// 已满
    		return;
    	}
    
    	// 移动
    	// 从length-1 到 pos 依次后移
    	for (i = L->length - 1; i >= pos; --i)
    	{
    		L->data[i + 1] = L->data[i];
    	}
    
    	// 赋值 -- 数据的拷贝
    	L->data[pos] = e;
    	
    	L->length++;
    }
    
    void ListDelete(SqList *L, int pos, ElemType *e)
    {
    	int i = -1;
    
    	// 错误处理
    	if (pos < 0 || pos >= L->length)
    	{
    		return;
    	}
    
    	if (L->length == 0)
    	{
    		// 空表
    		return;
    	}
    
    	// 保存要删除的位置的值
    	*e = L->data[pos];
    
    	// pos+1 --> length-1 
    	for (i = pos + 1; i < L->length; ++i)
    	{
    		// 前移操作
    		L->data[i - 1] = L->data[i];
    	}
    
    	// 长度
    	L->length--;
    }
    
    int ListLength(SqList L)
    {
    	return L.length;
    }
    

    main.c

    #include "SqList.h"
    
    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
    	SqList list;
    
    	int i = -1;
    
    	// 初始化
    	InitList(&list);
    
    	printf("初始化顺序线性表成功
    ");
    
    	printf("
    ");
    
    	// 插入操作
    	for (i = 0; i < 10; ++i)
    	{
    		ListInsert(&list, i, i + 1);
    
    		printf("InsertElem value = %d
    ",i + 1);
    	}
    
    	printf("
    ");
    
    	// 遍历
    	for (i = 0; i < ListLength(list); ++i)
    	{
    		int tmp;
    
    		GetElem(list, i, &tmp);
    
    		printf("GetElem value = %d
    ", tmp);
    	}
    
    	printf("
    ");
    
    	// 删除顺序表元素
    	while (ListEmpty(list) != 1)
    	{
    		int tmp;
    
    		ListDelete(&list, 0, &tmp);
    
    		printf("DeleteElem value = %d
    ", tmp);
    	}
    
    	printf("
    ");
    
    	system("pause");
    
    	return 0;
    }
    

    运行结果

    2.2.3 优点和缺点

    优点

    • 无需为线性表中的逻辑关系增加额外的空间

    • 可以快速的获取表中合法位置的元素

    缺点

    • 插入和删除操作需要移动大量元素

    • 当线性表长度变化较大时难以确定存储空间的容量

    2.3 链式存储的线性表

    2.3.1 基本概念

    链式存储定义:为了表示每个数据元素与其直接后继元素之间的逻辑关系,每个元素除了存储本身的信息外,还需要存储指示其直接后继的信息。

    单链表:线性表的链式存储结构中,每个节点中只包含一个指针域,这样的链表叫单链表。

    表头结点:链表中的第一个结点,包含指向第一个数据元素的指针以及链表自身的一些信息

    数据结点:链表中代表数据元素的结点,包含指向下一个数据元素的指针和数据元素的信息

    尾结点:链表中的最后一个数据结点,其下一元素指针为空,表示无后继。

    2.3.2 设计与实现

    插入操作

    node->next = current->next;
    current->next = node;
    

    删除操作

    current->next = ret->next

    实现代码

    Linklist.h

    #ifndef _LINKLIST_H
    #define _LINKLIST_H
    
    typedef int ElemType;
    
    #if 1	// 传统链表
    
    typedef struct NODE
    {
    	ElemType data;	// 数据
    	struct NODE *next;	// 指向后继节点的指针
    }Node;
    
    typedef struct LINKLIST
    {
    	int length;
    	Node header;
    }LinkList;
    
    
    #else	// 企业链表
    
    // 小链表节点
    typedef struct NODE
    {
    	struct NODE *next;	// 指向后继节点的指针
    }Node;
    
    // 业务节点(包含一个小链表节点)
    typedef struct _LISTNODE
    {
    	Node node;	// 小链表节点
    	void *data;	// 指向数据地址的指针
    }ListNode;
    
    typedef struct LINKLIST
    {
    	int length;
    	ListNode header;
    }LinkList;
    
    #endif
    
    // 初始化,建立一个空的线性表L。
    void InitList(LinkList *L);
    // 若线性表为空,返回true,否则返回false
    int ListEmpty(LinkList L);
    // 将线性表清空
    void ClearList(LinkList *L);
    // 将线性表L中的第pos个位置的元素返回给e
    void GetElem(LinkList L, int pos, ElemType *e);
    // 在线性表L中的第pos个位置插入新元素e
    void ListInsert(LinkList *L, int pos, ElemType e);
    // 删除线性表L中的第pos个位置元素,并用e返回其值
    void ListDelete(LinkList *L, int pos, ElemType *e);
    // 返回线性表L的元素个数
    int ListLength(LinkList L);
    // 销毁线性表
    void DestroyList(LinkList *L);
    
    #endif	// _LINKLIST_H
    

    Linklist.c

    #include "LinkList.h"
    
    #include <stdio.h>
    #include <stdlib.h>
    
    void InitList(LinkList *L)
    {
    	// 初始化
    	L->length = 0;
    	L->header.next = NULL;
    }
    
    int ListEmpty(LinkList L)
    {
    	if (L.length == 0)
    	{
    		return 1;
    	}
    	return 0;
    }
    
    void ClearList(LinkList *L)
    {
    	// 删除并释放所有节点
    	while (L->length)
    	{
    		int tmp;
    		ListDelete(L, 0, &tmp);
    	}
    }
    
    void GetElem(LinkList L, int pos, ElemType *e)
    {
    	// 辅助指针 指向头结点
    	Node* pCur = &L.header;
    
    	int i = -1;
    
    
    	// 错误检查
    	if (pos < 0 || pos >= L.length)
    	{
    		return;
    	}
    
    	// 遍历链表, 找到pos位置
    	for (i = 0; i <= pos; ++i)
    	{
    		// 辅助指针后移
    		pCur = pCur->next;
    	}
    	// 取值
    	*e = pCur->data;
    }
    
    void ListInsert(LinkList *L, int pos, ElemType e)
    {
    	// 辅助指针 指向头结点
    	Node* pCur = &L->header;
    
    	int i = -1;
    
    	// 创建新节点
    	Node* pNew = (Node*)malloc(sizeof(Node));
    
    	// 错误检查
    	if (pos < 0 || pos > L->length)
    	{
    		return;
    	}
    
    	// 遍历链表, 找到pos位置
    	for (i = 0; i < pos; ++i)
    	{
    		// 辅助指针后移
    		pCur = pCur->next;
    	}
    	
    	// 初始化
    	pNew->data = e;
    
    	// 插入节点操作
    	// 先连接后半部分
    	pNew->next = pCur->next;
    	// 前半部分
    	pCur->next = pNew;
    
    	// 长度+1
    	L->length++;
    }
    
    void ListDelete(LinkList *L, int pos, ElemType *e)
    {
    	// 辅助指针 指向头结点
    	Node* pCur = &L->header;
    
    	// 辅助指针
    	Node* pDel = pCur->next;
    
    	int i = -1;
    
    	// 错误检查
    	if (pos < 0 || pos >= L->length)
    	{
    		return;
    	}
    
    	if (L->length == 0)
    	{
    		return;
    	}
    
    	// 遍历链表, 找到pos位置
    	for (i = 0; i < pos; ++i)
    	{
    		// 辅助指针后移
    		pCur = pCur->next;
    	}
    	// 保存pos位置节点的值
    	*e = pDel->data;
    
    	// 删除节点从链表中
    	pCur->next = pDel->next;
    	// 释放内存
    	free(pDel);
    
    	// 长度-1
    	L->length--;
    }
    
    int ListLength(LinkList L)
    {
    	return L.length;
    }
    
    void DestroyList(LinkList *L)
    {
    	ClearList(L);
    }
    

    main.c

    #include "LinkList.h"
    
    #include <stdio.h>
    #include <stdlib.h>
    
    void main()
    {
    	LinkList ls;
    
    	int i = -1;
    
    	// init
    	InitList(&ls);
    
    	printf("初始化链式线性表成功
    ");
    
    	printf("
    ");
    
    	// insert data
    	for (i = 0; i < 10; ++i)
    	{
    		ListInsert(&ls, i, i + 1);
    
    		printf("InsertElem value = %d
    ",i + 1);
    	}
    
    	printf("
    ");
    
    	// 遍历
    	for (i = 0; i < ListLength(ls); ++i)
    	{
    		int tmp;
    
    		GetElem(ls, i, &tmp);
    
    		printf("GetElem value = %d
    ", tmp);
    	}
    	printf("
    ");
    
    	// 删除全部
    	while (ListEmpty(ls) != 1)
    	{
    		int tmp;
    
    		// 逐个删除
    		ListDelete(&ls, 0, &tmp);
    
    		printf("DeleteElem value = %d
    ", tmp);
    	}
    
    	printf("
    ");
    
    	// 销毁链表
    	DestroyList(&ls);
    
    	system("pause");
    }
    

    运行结果

    2.3.3 优点和缺点

    优点

    • 无需一次性定制链表的容量

    • 插入和删除操作无需移动数据元素

    缺点

    • 数据元素必须保存后继元素的位置信息

    • 获取指定数据的元素操作需要顺序访问之前的元素

    2.3.4 链表技术领域推演

    2.4 循环链表

    2.4.1 基本概念

    循环链表的定义:单链表中最后一个结点的指针域指向头结点,整个链表形成一个环。

    循环链表的操作

    循环链表拥有单链表的所有操作:

    • 创建链表

    • 销毁链表

    • 获取链表长度

    • 清空链表

    • 获取第pos个元素操作

    • 插入元素到位置pos

    • 删除位置pos处的元素

    循环链表新操作

    • 将游标重置指向链表中的第一个数据元素
      CircleListNode* CircleList_Reset(CircleList* list);

    • 获取当前游标指向的数据元素
      CircleListNode* CircleList_Current(CircleList* list);

    • 将游标移动指向到链表中的下一个数据元素
      CircleListNode* CircleList_Next(CircleList* list);

    • 直接指定删除链表中的某个数据元素
      CircleListNode* CircleList_DeleteNode(CircleList* list, CircleListNode* node);

    游标的定义:在循环链表中可以定义一个“当前”指针,这个指针通常称为游标,可以通过这个游标来遍历链表中的所有元素。

    2.4.2 设计与实现

    循环链表插入元素的分析

    1. 普通插入元素

    1. 尾插法

    辅助指针向后跳length次,指向最后面那个元素(length-1位置),因为是循环链表,所以length位置元素即为第一个数据节点。

    CircleList_Insert(list, (CircleListNode*)&v1, CircleList_Length(list));

    1. 头插法

    注意:

    • 要进行头插法,需要求出尾节点,连接新的0号位置节点

    • 第一次插入元素时,让游标指向0号结点(即第一个数据节点)

    CircleList_Insert(list, (CircleListNode*)&v1, 0);

    1. 第一次插入元素(相当于头插法)

    要求出尾节点,尾节点指针指向第一个数据节点(即自己指向自己)

    循环链表删除结点分析

    1. 删除普通结点

    1. 删除头结点(删除0号位置处元素)

    要求出尾结点,连接新的零号位置节点

    2.4.2.1 循环链表基本功能实现代码

    实现代码:

    CircleList.h

    #ifndef _CIRCLE_LIST_H
    #define _CIRCLE_LIST_H
    
    //自定义循环链表数据类型
    typedef void CircleList;
    //自定义循环链表节点数据类型
    typedef struct tag_CirclListNode
    {
    	struct tag_CirclListNode *next;
    }CircleListNode;
    
    //创建结构体管理链表
    typedef struct tag_CircleList
    {
    	//循环链表头结点
    	CircleListNode	header;
    	//循环链表游标
    	CircleListNode	*slider;
    	//循环链表长度
    	int		length;
    }TCircleList;
    
    //创建循环链表
    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);
    
    //------------------ new add ------------------
    
    //直接指定删除链表中的某个数据元素
    CircleListNode* CircleList_DeleteNode(CircleList* list, CircleListNode* node);
    
    //将游标重置指向链表中的第一个数据元素
    CircleListNode* CircleList_Reset(CircleList* list);
    
    //获取当前游标指向的数据元素
    CircleListNode* CircleList_Current(CircleList* list);
    
    //将游标移动指向到链表中的下一个数据元素
    CircleListNode* CircleList_Next(CircleList* list);
    
    #endif //_CIRCLE_LIST_H
    

    CircleList.c

    #include "CircleList.h"
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    //创建循环链表
    CircleList* CircleList_Create()
    {
    	//定义TCircleList指针变量,并分配内存空间
    	TCircleList* tlist = (TCircleList*)malloc(sizeof(TCircleList));
    
    	if (tlist == NULL)
    	{
    		printf("error: TCircleList* tlist = (TCircleList*)malloc(sizeof(TCircleList)) 
    ");
    		return NULL;
    	}
    
    	//数据初始化
    	tlist->header.next = NULL;
    	tlist->slider = NULL;
    	tlist->length = 0;
    
    	return (CircleList*)tlist;
    }
    
    //销毁循环链表
    void CircleList_Destroy(CircleList* list)
    {
    	//定义TCircleList指针变量
    	TCircleList *tlist = NULL;
    
    	//判断list是否为有效指针
    	if (list == NULL)
    	{
    		printf("Destory error: list 为无效指针
    ");
    
    		return;
    	}
    
    	free(list);
    }
    
    //清空循环链表
    void CircleList_Clear(CircleList* list)
    {
    	//定义TCircleList指针变量
    	TCircleList *tlist = NULL;
    
    	//判断list是否为有效指针
    	if (list == NULL)
    	{
    		printf("Clear error: list 为无效指针
    ");
    
    		return;
    	}
    
    	//类型转换并赋值
    	tlist = (TCircleList*)list;
    	//将长度重置为0
    	tlist->length = 0;
    	//头结点指针域指向空
    	tlist->header.next = NULL;
    	//游标指向空
    	tlist->slider = NULL;
    }
    
    //获取循环链表长度
    int CircleList_Length(CircleList* list)
    {
    	//定义TCircleList指针变量
    	TCircleList *tlist = NULL;
    
    	//判断list是否为有效指针
    	if (list == NULL)
    	{
    		printf("Length error: list 为无效指针
    ");
    
    		return -1;
    	}
    	//类型转换并赋值
    	tlist = (TCircleList*)list;
    
    	return tlist->length;
    }
    
    //在循环链表中插入新节点
    int CircleList_Insert(CircleList* list, CircleListNode* node, int pos)
    {
    	int i;
    
    	//定义TCircleList指针变量
    	TCircleList *tlist = NULL;
    	//定义辅助指针变量
    	CircleListNode	*currentNode = NULL;
    
    	//判断list是否为有效指针
    	if (list == NULL || node == NULL || pos < 0)
    	{
    		printf("Insert error: if (list == NULL || node == NULL || pos < 0)
    ");
    
    		return -1;
    	}
    	//类型转换并赋值
    	tlist = (TCircleList*)list;
    
    	//元素插入
    	//step 1: 使用辅助指针变量,指向头结点
    	currentNode = &tlist->header;
    	//step 2: 找到pos-1位置节点
    	for (i = 0; i < pos; ++i)
    	{
    		//判断是否有后继节点
    		if (currentNode->next != NULL)
    		{
    			//指针后移
    			currentNode = currentNode->next;
    		}
    		else
    		{
    			//没有后继节点跳出循环
    			break;
    		}
    	}
    
    	//step 3: 将node节点的指针指向当前节点(pos-1)的后继节点(pos)
    	node->next = currentNode->next;
    
    	//step 4: 当前节点的指针指向node节点的地址
    	currentNode->next = node;
    
    	//step 5: 如果是第一次插入节点
    	if (tlist->length == 0)
    	{
    		//将游标指向新插入节点
    		tlist->slider = node;
    	}
    
    	//step 6: 链表长度加1
    	tlist->length++;
    
    	//step 7:若头插法 currentNode仍然指向头部
    	//原因: 跳0步, 没有跳走
    	if (currentNode == &tlist->header)
    	{
    		CircleListNode* lastNode = CircleList_Get(list, tlist->length - 1);
    
    		//最后一个节点的指针,指向第一个数据节点
    		lastNode->next = currentNode->next;
    	}
    
    	return 0;
    }
    
    //获取循环链表中的指定位置的节点
    CircleListNode* CircleList_Get(CircleList* list, int pos)
    {
    	int i;
    
    	//定义TCircleList指针变量
    	TCircleList *tlist = NULL;
    	//定义辅助指针变量
    	CircleListNode	*currentNode = NULL;
    
    	//判断list是否为有效指针
    	if (list == NULL || pos < 0)
    	{
    		printf("CircleList_Get error: if (list == NULL || pos < 0)
    ");
    		return NULL;
    	}
    
    	//类型转换并赋值
    	tlist = (TCircleList*)list;
    
    	//step 1: 使用辅助指针变量,指向头结点
    	currentNode = &tlist->header;
    
    	//step 2: 找到pos位置节点
    	for (i = 0; i <= pos; ++i)
    	{
    		//判断是否有后继节点
    		if (currentNode->next != NULL)
    		{
    			//指针后移
    			currentNode = currentNode->next;
    		}
    		else
    		{
    			//没有后继节点跳出循环
    			printf("error: 没找到指定位置的元素
    ");
    
    			return NULL;
    		}
    	}
    	return currentNode;
    }
    
    
    //删除循环链表中的指定位置的节点
    //-------------------------------
    CircleListNode* CircleList_Delete(CircleList* list, int pos)
    {
    	int i;
    
    	//定义TCircleList指针变量
    	TCircleList *tlist = NULL;
    	//定义链表节点指针,保存要删除的节点地址
    	CircleListNode	*deleteNode = NULL;
    	//定义链表节点指针,保存最后一个节点
    	CircleListNode  *lastNode = NULL;
    	//定义辅助指针变量
    	CircleListNode  *currentNode = NULL;
    
    	//判断list是否为有效指针
    	if (list == NULL || pos < 0)
    	{
    		printf("CircleList_Delete error: if (list == NULL || pos < 0)
    ");
    
    		return NULL;
    	}
    	//类型转换并赋值
    	tlist = (TCircleList*)list;
    
    	//判断链表中是否有节点
    	if (tlist->length <= 0)
    	{
    		printf("error: 链表为空,不能删除
    ");
    
    		return NULL;
    	}
    
    	//元素删除
    
    	//step 1: 辅助指针变量,指向头结点
    	currentNode = &tlist->header;
    
    	//step 2: 找到pos-1位置节点
    	for (i = 0; i < pos; ++i)
    	{
    		//指针后移
    		currentNode = currentNode->next;
    	}
    
    	//step 3: 保存要删除的节点的地址
    	deleteNode = currentNode->next;
    
    	//step 4-1: 判断删除的元素是否为第一个元素
    	if (currentNode == &tlist->header)
    	{
    		//step 4-2: 找到最后一个节点
    		lastNode = CircleList_Get(list, tlist->length - 1);
    	}
    
    	//step 4-3: 判断lastNode是否为空
    	if (lastNode != NULL)
    	{
    		//step 4-4: 将最后一个节点的地址指向要删除节点的后继节点
    		lastNode->next = deleteNode->next;
    	}
    
    	//step 4-5: 将头结点的指针指向要删除节点的后继节点
    	currentNode->next = deleteNode->next;
    
    	//step 5: 链表长度减1
    	tlist->length--;
    
    	//step 6-1: 判断删除的元素是否为游标指向的元素
    	if (tlist->slider == deleteNode)
    	{
    		//step 6-2: 游标后移
    		tlist->slider = deleteNode->next;
    	}
    
    	//step 7-1: 判断删除元素后,链表长度是否为零
    	if (tlist->length == 0)
    	{
    		//step 7-2: 链表头结点指针域指向空
    		tlist->header.next = NULL;
    		//step 7-3: 链表游标指向空
    		tlist->slider = NULL;
    	}
    
    	return deleteNode;
    }
    
    
    //------------------ new add ------------------
    
    //直接指定删除链表中的某个数据元素
    CircleListNode* CircleList_DeleteNode(CircleList* list, CircleListNode* node)
    {
    	int i;
    	int nPos = 0;
    
    	//定义TCircleList指针变量
    	TCircleList *tlist = NULL;
    
    	//定义辅助指针变量
    	CircleListNode* currentNode = NULL;
    	//定义辅助指针变量,用来保存要删除的节点地址
    	CircleListNode* delNode = NULL;
    
    	//判断list是否为有效指针
    	if (list == NULL || node == NULL)
    	{
    		printf("CircleList_DeleteNode error: if (list == NULL || node == NULL)
    ");
    
    		return NULL;
    	}
    
    	//类型转换并赋值
    	tlist = (TCircleList*)list;
            // 辅助指针变量,指向头结点
            currentNode = &tlist->header;
    
    	//查找node节点在循环链表中的位置
    	for (i = 0; i < tlist->length; ++i)
    	{
    		//从第一个数据节点开始判断,查找等于node的节点
    		if (currentNode->next == node)
    		{
    			//保存与node节点相等的节点的位置
    			nPos = i;
    			//保存要删除的节点地址
    			delNode = currentNode->next;
    
    			//跳出循环
    			break;
    		}
    		//当前节点指针后移
    		currentNode = currentNode->next;
    	}
    
    	//如果找到delNode,根据nPos删除该节点
    	if (delNode != NULL)
    	{
    		//删除指定位置的元素
    		CircleList_Delete(list, nPos);
    	}
    
    	return delNode;
    }
    
    
    //将游标重置指向链表中的第一个数据元素
    CircleListNode* CircleList_Reset(CircleList* list)
    {
    	//定义TCircleList指针变量
    	TCircleList *tlist = NULL;
    
    	//判断list是否为有效指针
    	if (list == NULL)
    	{
    		printf("CircleList_Reset error: if (list == NULL)
    ");
    
    		return NULL;
    	}
    
    	//类型转换并赋值
    	tlist = (TCircleList*)list;
    	//重置游标位置
    	tlist->slider = tlist->header.next;
    
    	return tlist->slider;
    }
    
    
    //获取当前游标指向的数据元素
    CircleListNode* CircleList_Current(CircleList* list)
    {
    	//定义TCircleList指针变量
    	TCircleList *tlist = NULL;
    
    	//判断list是否为有效指针
    	if (list == NULL)
    	{
    		printf("CircleList_Current error: if (list == NULL)
    ");
    
    		return NULL;
    	}
    
    	//类型转换并赋值
    	tlist = (TCircleList*)list;
    
    	return tlist->slider;
    }
    
    
    //将游标移动指向到链表中的下一个数据元素
    CircleListNode* CircleList_Next(CircleList* list)
    {
    	//定义链表节点指针变量
    	CircleListNode	*currNode = NULL;
    	//定义TCircleList指针变量
    	TCircleList *tlist = NULL;
    
    	//判断list是否为有效指针
    	if (list == NULL)
    	{
    		printf("CircleList_Next error: if (list == NULL)
    ");
    
    		return NULL;
    	}
    
    	//类型转换并赋值
    	tlist = (TCircleList*)list;
    	//存储当前游标位置
    	currNode = tlist->slider;
    
    	//判断当前游标是否指向空
    	if (tlist->slider != NULL)
    	{
    		//游标后移
    		tlist->slider = currNode->next;
    	}
    
    	return currNode;
    }
    

    循环链表基本功能测试.c

    #include "CircleList.h"
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    typedef struct tag_value
    {
    	CircleListNode circleNode;
    	int v;
    }Value;
    
    #if 1
    
    int main()
    {
    	int i;
    
    	//定义Value结构体数组
    	Value val[10];	
    	//创建循环链表
    	CircleList* list = CircleList_Create();
    
    	Value *pVal;
    
    	//循环初始化数组
    	for (i = 0; i < sizeof(val) / sizeof(Value); ++i)
    	{
    		val[i].v = i + 20;
    
    		//往循环链表中插入数据
    		CircleList_Insert(list, (CircleListNode*)&val[i], i);
    	}
    
    	//遍历循环链表
    	//************* 怎么证明是循环链表? *************
    	for (i = 0; i < CircleList_Length(list) * 2; ++i)	//打印两遍
    	{
    		pVal = (Value*)CircleList_Get(list, i);
    
    		printf("Value %d = %d
    ", i, pVal->v);
    	}
    
    	//删除节点
    	while (CircleList_Length(list) > 0)
    	{
    		pVal = (Value*)CircleList_Delete(list, 0);
    
    		printf("Delete Value: val = %d
    ", pVal->v);
    	}
    
    	//销毁循环链表
    	CircleList_Destroy(list);
    
    	system("pause");
    
    	return 0;
    }
    
    #endif
    

    运行结果:

    2.4.2.2 约瑟夫问题求解

    实现代码:

    CircleList.h

    #ifndef _CIRCLE_LIST_H
    #define _CIRCLE_LIST_H
    
    //自定义循环链表数据类型
    typedef void CircleList;
    //自定义循环链表节点数据类型
    typedef struct tag_CirclListNode
    {
    	struct tag_CirclListNode *next;
    }CircleListNode;
    
    //创建结构体管理链表
    typedef struct tag_CircleList
    {
    	//循环链表头结点
    	CircleListNode	header;
    	//循环链表游标
    	CircleListNode	*slider;
    	//循环链表长度
    	int				length;
    }TCircleList;
    
    //创建循环链表
    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);
    
    //------------------ new add ------------------
    
    //直接指定删除链表中的某个数据元素
    CircleListNode* CircleList_DeleteNode(CircleList* list, CircleListNode* node);
    
    //将游标重置指向链表中的第一个数据元素
    CircleListNode* CircleList_Reset(CircleList* list);
    
    //获取当前游标指向的数据元素
    CircleListNode* CircleList_Current(CircleList* list);
    
    //将游标移动指向到链表中的下一个数据元素
    CircleListNode* CircleList_Next(CircleList* list);
    
    #endif //_CIRCLE_LIST_H
    

    CircleList.c

    #include "CircleList.h"
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    //创建循环链表
    CircleList* CircleList_Create()
    {
    	//定义TCircleList指针变量,并分配内存空间
    	TCircleList* tlist = (TCircleList*)malloc(sizeof(TCircleList));
    
    	if (tlist == NULL)
    	{
    		printf("error: TCircleList* tlist = (TCircleList*)malloc(sizeof(TCircleList)) 
    ");
    		return NULL;
    	}
    
    	//数据初始化
    	tlist->header.next = NULL;
    	tlist->slider = NULL;
    	tlist->length = 0;
    
    	return (CircleList*)tlist;
    }
    
    //销毁循环链表
    void CircleList_Destroy(CircleList* list)
    {
    	//定义TCircleList指针变量
    	TCircleList *tlist = NULL;
    
    	//判断list是否为有效指针
    	if (list == NULL)
    	{
    		printf("Destory error: list 为无效指针
    ");
    
    		return;
    	}
    
    	free(list);
    }
    
    //清空循环链表
    void CircleList_Clear(CircleList* list)
    {
    	//定义TCircleList指针变量
    	TCircleList *tlist = NULL;
    
    	//判断list是否为有效指针
    	if (list == NULL)
    	{
    		printf("Clear error: list 为无效指针
    ");
    
    		return;
    	}
    
    	//类型转换并赋值
    	tlist = (TCircleList*)list;
    	//将长度重置为0
    	tlist->length = 0;
    	//头结点指针域指向空
    	tlist->header.next = NULL;
    	//游标指向空
    	tlist->slider = NULL;
    }
    
    //获取循环链表长度
    int CircleList_Length(CircleList* list)
    {
    	//定义TCircleList指针变量
    	TCircleList *tlist = NULL;
    
    	//判断list是否为有效指针
    	if (list == NULL)
    	{
    		printf("Length error: list 为无效指针
    ");
    
    		return -1;
    	}
    	//类型转换并赋值
    	tlist = (TCircleList*)list;
    
    	return tlist->length;
    }
    
    //在循环链表中插入新节点
    int CircleList_Insert(CircleList* list, CircleListNode* node, int pos)
    {
    	int i;
    
    	//定义TCircleList指针变量
    	TCircleList *tlist = NULL;
    	//定义辅助指针变量
    	CircleListNode	*currentNode = NULL;
    
    	//判断list是否为有效指针
    	if (list == NULL || node == NULL || pos < 0)
    	{
    		printf("Insert error: if (list == NULL || node == NULL || pos < 0)
    ");
    
    		return -1;
    	}
    	//类型转换并赋值
    	tlist = (TCircleList*)list;
    
    	//元素插入
    	//step 1: 使用辅助指针变量,指向头结点
    	currentNode = &tlist->header;
    	//step 2: 找到pos-1位置节点
    	for (i = 0; i < pos; ++i)
    	{
    		//判断是否有后继节点
    		if (currentNode->next != NULL)
    		{
    			//指针后移
    			currentNode = currentNode->next;
    		}
    		else
    		{
    			//没有后继节点跳出循环
    			break;
    		}
    	}
    
    	//step 3: 将node节点的指针指向当前节点(pos-1)的后继节点(pos)
    	node->next = currentNode->next;
    
    	//step 4: 当前节点的指针指向node节点的地址
    	currentNode->next = node;
    
    	//step 5: 如果是第一次插入节点
    	if (tlist->length == 0)
    	{
    		//将游标指向新插入节点
    		tlist->slider = node;
    	}
    
    	//step 6: 链表长度加1
    	tlist->length++;
    
    	//step 7:若头插法 currentNode仍然指向头部
    	//原因: 跳0步, 没有跳走
    	if (currentNode == &tlist->header)
    	{
    		CircleListNode* lastNode = CircleList_Get(list, tlist->length - 1);
    
    		//最后一个节点的指针,指向第一个数据节点
    		lastNode->next = currentNode->next;
    	}
    
    	return 0;
    }
    
    //获取循环链表中的指定位置的节点
    CircleListNode* CircleList_Get(CircleList* list, int pos)
    {
    	int i;
    
    	//定义TCircleList指针变量
    	TCircleList *tlist = NULL;
    	//定义辅助指针变量
    	CircleListNode	*currentNode = NULL;
    
    	//判断list是否为有效指针
    	if (list == NULL || pos < 0)
    	{
    		printf("CircleList_Get error: if (list == NULL || pos < 0)
    ");
    		return NULL;
    	}
    
    	//类型转换并赋值
    	tlist = (TCircleList*)list;
    
    	//step 1: 使用辅助指针变量,指向头结点
    	currentNode = &tlist->header;
    
    	//step 2: 找到pos位置节点
    	for (i = 0; i <= pos; ++i)
    	{
    		//判断是否有后继节点
    		if (currentNode->next != NULL)
    		{
    			//指针后移
    			currentNode = currentNode->next;
    		}
    		else
    		{
    			//没有后继节点跳出循环
    			printf("error: 没找到指定位置的元素
    ");
    
    			return NULL;
    		}
    	}
    	return currentNode;
    }
    
    
    //删除循环链表中的指定位置的节点
    //-------------------------------
    CircleListNode* CircleList_Delete(CircleList* list, int pos)
    {
    	int i;
    
    	//定义TCircleList指针变量
    	TCircleList *tlist = NULL;
    	//定义链表节点指针,保存要删除的节点地址
    	CircleListNode	*deleteNode = NULL;
    	//定义链表节点指针,保存最后一个节点
    	CircleListNode  *lastNode = NULL;
    	//定义辅助指针变量
    	CircleListNode  *currentNode = NULL;
    
    	//判断list是否为有效指针
    	if (list == NULL || pos < 0)
    	{
    		printf("CircleList_Delete error: if (list == NULL || pos < 0)
    ");
    
    		return NULL;
    	}
    	//类型转换并赋值
    	tlist = (TCircleList*)list;
    
    	//判断链表中是否有节点
    	if (tlist->length <= 0)
    	{
    		printf("error: 链表为空,不能删除
    ");
    
    		return NULL;
    	}
    
    	//元素删除
    
    	//step 1: 辅助指针变量,指向头结点
    	currentNode = &tlist->header;
    
    	//step 2: 找到pos-1位置节点
    	for (i = 0; i < pos; ++i)
    	{
    		//指针后移
    		currentNode = currentNode->next;
    	}
    
    	//step 3: 保存要删除的节点的地址
    	deleteNode = currentNode->next;
    
    	//step 4-1: 判断删除的元素是否为第一个元素
    	if (currentNode == &tlist->header)
    	{
    		//step 4-2: 找到最后一个节点
    		lastNode = CircleList_Get(list, tlist->length - 1);
    	}
    
    	//step 4-3: 判断lastNode是否为空
    	if (lastNode != NULL)
    	{
    		//step 4-4: 将最后一个节点的地址指向要删除节点的后继节点
    		lastNode->next = deleteNode->next;
    	}
    
    	//step 4-5: 将头结点的指针指向要删除节点的后继节点
    	currentNode->next = deleteNode->next;
    
    	//step 5: 链表长度减1
    	tlist->length--;
    
    	//step 6-1: 判断删除的元素是否为游标指向的元素
    	if (tlist->slider == deleteNode)
    	{
    		//step 6-2: 游标后移
    		tlist->slider = deleteNode->next;
    	}
    
    	//step 7-1: 判断删除元素后,链表长度是否为零
    	if (tlist->length == 0)
    	{
    		//step 7-2: 链表头结点指针域指向空
    		tlist->header.next = NULL;
    		//step 7-3: 链表游标指向空
    		tlist->slider = NULL;
    	}
    
    	return deleteNode;
    }
    
    
    //------------------ new add ------------------
    
    //直接指定删除链表中的某个数据元素
    CircleListNode* CircleList_DeleteNode(CircleList* list, CircleListNode* node)
    {
    	int i;
    	int nPos = 0;
    
    	//定义TCircleList指针变量
    	TCircleList *tlist = NULL;
    
    	//定义辅助指针变量
    	CircleListNode* currentNode = NULL;
    	//定义辅助指针变量,用来保存要删除的节点地址
    	CircleListNode* delNode = NULL;
    
    	//判断list是否为有效指针
    	if (list == NULL || node == NULL)
    	{
    		printf("CircleList_DeleteNode error: if (list == NULL || node == NULL)
    ");
    
    		return NULL;
    	}
    
    	//类型转换并赋值
    	tlist = (TCircleList*)list;
    
            //辅助指针变量,指向头结点
            currentNode = &tlist->header;
    
    	//查找node节点在循环链表中的位置
    	for (i = 0; i < tlist->length; ++i)
    	{
    		//从第一个数据节点开始判断,查找等于node的节点
    		if (currentNode->next == node)
    		{
    			//保存与node节点相等的节点的位置
    			nPos = i;
    			//保存要删除的节点地址
    			delNode = currentNode->next;
    
    			//跳出循环
    			break;
    		}
    		//当前节点指针后移
    		currentNode = currentNode->next;
    	}
    
    	//如果找到delNode,根据nPos删除该节点
    	if (delNode != NULL)
    	{
    		//删除指定位置的元素
    		CircleList_Delete(list, nPos);
    	}
    
    	return delNode;
    }
    
    
    //将游标重置指向链表中的第一个数据元素
    CircleListNode* CircleList_Reset(CircleList* list)
    {
    	//定义TCircleList指针变量
    	TCircleList *tlist = NULL;
    
    	//判断list是否为有效指针
    	if (list == NULL)
    	{
    		printf("CircleList_Reset error: if (list == NULL)
    ");
    
    		return NULL;
    	}
    
    	//类型转换并赋值
    	tlist = (TCircleList*)list;
    	//重置游标位置
    	tlist->slider = tlist->header.next;
    
    	return tlist->slider;
    }
    
    
    //获取当前游标指向的数据元素
    CircleListNode* CircleList_Current(CircleList* list)
    {
    	//定义TCircleList指针变量
    	TCircleList *tlist = NULL;
    
    	//判断list是否为有效指针
    	if (list == NULL)
    	{
    		printf("CircleList_Current error: if (list == NULL)
    ");
    
    		return NULL;
    	}
    
    	//类型转换并赋值
    	tlist = (TCircleList*)list;
    
    	return tlist->slider;
    }
    
    
    //将游标移动指向到链表中的下一个数据元素
    CircleListNode* CircleList_Next(CircleList* list)
    {
    	//定义链表节点指针变量
    	CircleListNode	*currNode = NULL;
    	//定义TCircleList指针变量
    	TCircleList		*tlist = NULL;
    
    	//判断list是否为有效指针
    	if (list == NULL)
    	{
    		printf("CircleList_Next error: if (list == NULL)
    ");
    
    		return NULL;
    	}
    
    	//类型转换并赋值
    	tlist = (TCircleList*)list;
    	//存储当前游标位置
    	currNode = tlist->slider;
    
    	//判断当前游标是否指向空
    	if (tlist->slider != NULL)
    	{
    		//游标后移
    		tlist->slider = currNode->next;
    	}
    
    	return currNode;
    }
    

    约瑟夫问题求解.c

    #include "circlelist.h"
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    /*
    	约瑟夫问题-循环链表典型应用
    	n 个人围成一个圆圈,首先第 1 个人从 1 开始一个人一个人顺时针报数,
    	报到第 m 个人,令其出列。然后再从下一 个人开始从 1 顺时针报数,报
    	到第 m 个人,再令其出列,…,如此下去,求出列顺序。
    
    	求解: 假设 m = 3, n = 8 (1 2 3 4 5 6 7 8)
    	结果: 3 6 1 5 2 8 4 7
    */
    
    
    // 定义数据结构
    // 业务节点的定义
    typedef struct _Value
    {
    	// 内部的链表节点
    	CircleListNode node;
    	int v;	// 数据
    }Value;
    
    void joseph_question()
    {
    	Value val[8];
    
    	int i = -1;
    
    	Value* p;
    
    	// 创建循环链表
    	CircleList *list = CircleList_Create();
    
    	// 初始化数组
    	for (i = 0; i < 8; ++i)
    	{
    		val[i].v = i + 1;
    
    		// 节点插入到链表
    		CircleList_Insert(list, &val[i].node, i);
    	}
    
    	// 遍历
    	for (i = 0; i < CircleList_Length(list); ++i)
    	{
    		p = (Value*)CircleList_Get(list, i);
    
    		printf("%d  ", p->v);
    	}
    
    	printf("
    ");
    
    	// 出链表操作
    	printf("删除的节点的次序
    ");
    
    	// 游标重置, 指向第一个数据节点
    	CircleList_Reset(list);
    
    	while (CircleList_Length(list))
    	{
    		// 游标后移的步长
    		for (i = 0; i < 2; ++i)
    		{
    			// 游标后移
    			CircleList_Next(list);
    		}
    
    		// 获取当前游标指向的节点
    		p = (Value*)CircleList_Current(list);
    		
    		printf("%d  ", p->v);
    
    		// 删除当前节点
    		CircleList_DeleteNode(list, (CircleListNode*)p);
    	}
    
    	printf("
    ");
    
    	CircleList_Destroy(list);
    }
    
    #if 1
    
    void main()
    {
    	joseph_question();
    
    	system("pause");
    }
    
    #endif
    

    运行结果:

    2.4.3 优点和缺点

    优点:

    • 循环链表可以完全取代单链表的使用

    • 循环链表的Next和Current操作可以高效的遍历链表中的所有元素

    缺点:

    • 代码复杂度提高了

    2.5 双向链表

    2.4.1 基本概念

    双向链表的定义:在单链表的结点中增加一个指向其前驱的 pre 指针

    双向链表的操作

    双向链表的拥有单向链表的所有操作:

    • 创建链表

    • 销毁链表

    • 获取链表长度

    • 清空链表

    • 获取第 pos 个元素操作

    • 插入元素到位置 pos

    • 删除位置 pos 处的元素

    双向链表的新操作:

    • 获取当前游标指向的数据元素
      DLinkListNode* DLinkList_Current(DLinkList* list);

    • 将游标重置指向链表中的第一个数据元素
      DLinkListNode* DLinkList_Reset(DLinkList* list);

    • 将游标移动指向到链表中的下一个数据元素
      DLinkListNode* DLinkList_Next(DLinkList* list);

    • 将游标移动指向到链表中的上一个数据元素
      DLinkListNode* DLinkList_Pre(DLinkList* list);

    • 直接指定删除链表中的某个数据元素
      DLinkListNode* DLinkList_DeleteNode(DLinkList* list, DLinkListNode* node);

    2.4.2 设计与实现

    插入操作

    current->next = node;
    node->next = next;
    next->pre = node;
    node->pre = current;
    

    删除操作

    current->next = next;
    next->pre = current;
    

    示例代码:

    Dlinklist.h

    #ifndef _DLINK_LIST_H
    #define _DLINK_LIST_H
    
    //自定义双向链表数据类型
    typedef void DLinkList;
    
    //自定义双向链表节点数据类型
    typedef struct tag_dLinkListNode
    {
    	struct tag_dLinkListNode *prev;
    	struct tag_dLinkListNode *next;
    }DLinkListNode;
    
    //定义数据结构体
    typedef struct tag_value
    {
    	//包含双向链表的一个节点
    	DLinkListNode head;
    	int value;
    }Value;
    
    //定义管理双向链表的结构体
    typedef struct _tag_dlinklist
    {
    	DLinkListNode head;
    	DLinkListNode *slider;
    	int length;
    }TDLinkList;
     
    //创建链表
    DLinkList* DLinkList_Create();
    
    //销毁链表
    void DLinkList_Destroy(DLinkList* list);
    
    //清空链表
    void DLinkList_Clear(DLinkList* list);
    
    //获取链表长度
    int DLinkList_Length(DLinkList* list);
    
    //获取第pos个元素操作
    DLinkListNode* DLinkList_Get(DLinkList* list, int pos);
    
    //插入元素到位置pos
    int DLinkList_Insert(DLinkList* list, DLinkListNode* node, int pos);
    
    //删除位置pos处的元素
    DLinkListNode* DLinkList_Delete(DLinkList* list, int pos);
    
    //获取当前游标指向的数据元素
    DLinkListNode* DLinkList_Current(DLinkList* list);
    
    //将游标重置指向链表中的第一个数据元素
    DLinkListNode* DLinkList_Reset(DLinkList* list);
    
    //将游标移动指向到链表中的下一个数据元素
    DLinkListNode* DLinkList_Next(DLinkList* list);
    
    //将游标移动指向到链表中的上一个数据元素
    DLinkListNode* DLinkList_Prev(DLinkList* list);
    
    //直接指定删除链表中的某个数据元素
    DLinkListNode* DLinkList_DeleteNode(DLinkList* list, DLinkListNode* node);
    
    #endif //_DLINK_LIST_H
    

    Dlinklist.c

    #include "DLinkList.h"
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    
    //创建链表
    DLinkList* DLinkList_Create()
    {
    	//定义结构体类型指针变量,并分配内存空间
    	TDLinkList* dlist = (TDLinkList*)malloc(sizeof(TDLinkList));
    
    	//如果分配内存成功,则初始化变量
    	if (dlist != NULL)
    	{
    		dlist->head.next = NULL;
    		dlist->slider = NULL;
    		dlist->length = 0;
    
    		return (DLinkList*)dlist;
    	}
    
    	//失败返回空
    	printf("分配内存失败
    ");
    
    	return NULL;
    }
    
    //销毁链表
    void DLinkList_Destroy(DLinkList* list)
    {
    	//判断list是否为有效指针
    	if (list != NULL)
    	{
    		//释放内存空间
    		free(list);
    	}
    }
    
    //清空链表
    void DLinkList_Clear(DLinkList* list)
    {
    	//判断list是否为有效指针
    	if (list != NULL)
    	{
    		//定义结构体类型指针,并给其赋值
    		TDLinkList* dlist = (TDLinkList*)list;
    
    		//数据重置
    		dlist->length = 0;
    		dlist->head.next = NULL;
    		dlist->slider = NULL;
    	}
    }
    
    //获取链表长度
    int DLinkList_Length(DLinkList* list)
    {
    	//判断list是否为有效指针
    	if (list != NULL)
    	{
    		//定义结构体类型指针,并给其赋值
    		TDLinkList* dlist = (TDLinkList*)list;
    
    		return dlist->length;
    	}
    
    	printf("DLinkList_Length error: list 指针无效
    ");
    
    	return -1;
    }
    
    //获取第pos个元素操作
    DLinkListNode* DLinkList_Get(DLinkList* list, int pos)
    {
    	//判断list是否为有效指针
    	if (list != NULL)
    	{
    		//定义结构体类型指针,并给其赋值
    		TDLinkList* dlist = (TDLinkList*)list;
    		//定义辅助指针变量, 并初始化,指向头节点
    		DLinkListNode* currNode = &dlist->head;
    
    		int i = -1;
    
    		//循环查找pos位置元素
    		for (i = 0; i <= pos; ++i)
    		{
    			currNode = currNode->next;
    		}
    
    		return currNode;
    	}
    
    	printf("DLinkList_Get error: list 指针无效
    ");
    
    	return NULL;
    }
    
    //插入元素到位置pos
    int DLinkList_Insert(DLinkList* list, DLinkListNode* node, int pos)
    {
    	//判断list是否为有效指针
    	if (list != NULL)
    	{
    		//定义结构体类型指针,并给其赋值
    		TDLinkList* dlist = (TDLinkList*)list;
    		//定义辅助指针变量, 并初始化,指向头节点
    		DLinkListNode* currNode = &dlist->head;
    		//定义辅助指针变量
    		DLinkListNode* posNode = NULL;
    
    		int i = -1;
    
    		//循环查找pos-1位置元素
    		for (i = 0; i < pos; ++i)
    		{
    			//判断是否有后继节点
    			if (currNode->next != NULL)
    			{
    				//指针后移
    				currNode = currNode->next;
    			}
    			else
    			{
    				//没有后继节点,结束循环
    				break;
    			}
    		}
    		//赋值,辅助指针变量指向pos位置节点
    		posNode = currNode->next;
    
    		//开始插入元素
    		//step1: 将新节点的next域指针指向pos位置节点的地址
    		node->next = posNode;
    
    		//step2: 将当前节点的next域指针指向新插入节点的地址
    		currNode->next = node;
    
    		//step3: 将pos位置的节点的prev域指针指向新插入节点的地址
    		//********** 特殊处理 **********
    		if (posNode != NULL)	//当链表插入第一个元素需要特殊处理
    		{
    			posNode->prev = node;
    		}
    
    		//step4: 将新插入节点的地址指向当前节点的地址
    		node->prev = currNode;
    		//********** 特殊处理 **********
    		if (currNode == &dlist->head)	//如果链表为空
    		{
    			//将第一个节点的前驱节点设为空
    			node->prev = NULL;
    			//游标指向第一个节点
    			dlist->slider = node;
    		}
    
    		//step4: 链表长度加1
    		dlist->length++;
    
    		return 0;
    	}
    
    	printf("DLinkList_Insert error: list 指针无效
    ");
    
    	return -1;
    }
    
    //删除位置pos处的元素
    DLinkListNode* DLinkList_Delete(DLinkList* list, int pos)
    {
    	//判断list是否为有效指针
    	if (list != NULL && pos >= 0)
    	{
    		//定义结构体类型指针,并给其赋值
    		TDLinkList* dlist = (TDLinkList*)list;
    
    		//定义辅助指针变量, 并初始化,指向头节点
    		DLinkListNode* currNode = &dlist->head;
    		//定义辅助指针变量
    		DLinkListNode* delNode = NULL;
    		DLinkListNode* nextNode = NULL;
    
    		int i = -1;
    
    		//循环查找pos-1位置元素
    		for (i = 0; i < pos; ++i)
    		{
    			currNode = currNode->next;
    		}
    
    		//赋值
    		delNode = currNode->next;
    		nextNode = delNode->next;
    
    		//开始删除元素
    
    		//step1: 将当前节点的next域指针指向被删除节点的后继节点
    		currNode->next = nextNode;
    		//****** 需要特殊处理 ******
    		if (nextNode != NULL)
    		{
    			//step2: nextNode节点的prev域指针指向当前节点的地址
    			nextNode->prev = currNode;
    
    			//****** 需要特殊处理 ******
    			if (currNode == &dlist->head)	//如果当前节点为头结点
    			{
    				//将nextNode节点指向空
    				nextNode->prev = NULL;
    			}
    		}
    
    		//step 3: 链表长度减1
    		dlist->length--;
    
    		//判断删除的元素是不是当前游标指向的位置
    		if (dlist->slider == delNode)
    		{
    			//如果是,游标后移
    			dlist->slider = nextNode;
    		}
    
    		return delNode;
    	}
    
    	printf("DLinkList_Delete error: list指针 或 pos位置无效
    ");
    	return NULL;
    }
    
    //获取当前游标指向的数据元素
    DLinkListNode* DLinkList_Current(DLinkList* list)
    {
    	//判断list是否为有效指针
    	if (list != NULL)
    	{
    		//定义结构体类型指针,并给其赋值
    		TDLinkList* dlist = (TDLinkList*)list;
    
    		return dlist->slider;
    	}
    
    	printf("DLinkList_Current error: list 指针无效
    ");
    
    	return NULL;
    }
    
    //将游标重置指向链表中的第一个数据元素
    DLinkListNode* DLinkList_Reset(DLinkList* list)
    {
    	//判断list是否为有效指针
    	if (list != NULL)
    	{
    		//定义结构体类型指针,并给其赋值
    		TDLinkList* dlist = (TDLinkList*)list;
    
    		dlist->slider = dlist->head.next;
    
    		return dlist->slider;
    	}
    
    	printf("DLinkList_Reset error: list 指针无效
    ");
    
    	return NULL;
    }
    
    //将游标移动指向到链表中的下一个数据元素
    DLinkListNode* DLinkList_Next(DLinkList* list)
    {
    	//判断list是否为有效指针
    	if (list != NULL)
    	{
    		//定义结构体类型指针,并给其赋值
    		TDLinkList* dlist = (TDLinkList*)list;
    		//定义链表节点指针保存当前游标地址
    		DLinkListNode* currSlider = dlist->slider;
    
    		//游标后移
    		if (dlist->slider->next != NULL)
    		{
    			dlist->slider = dlist->slider->next;
    
    			return currSlider;
    		}
    		else
    		{
    			return NULL;
    		}
    	}
    
    	printf("DLinkList_Next error: list 指针无效
    ");
    
    	return NULL;
    }
    
    //将游标移动指向到链表中的上一个数据元素
    DLinkListNode* DLinkList_Prev(DLinkList* list)
    {
    	//判断list是否为有效指针
    	if (list != NULL)
    	{
    		//定义结构体类型指针,并给其赋值
    		TDLinkList* dlist = (TDLinkList*)list;
    		//定义链表节点指针保存当前游标地址
    		DLinkListNode* currSlider = dlist->slider;
    
    		//游标前移
    		dlist->slider = dlist->slider->prev;
    
    		return currSlider;
    	}
    
    	printf("DLinkList_Prev error: list 指针无效
    ");
    
    	return NULL;
    }
    
    //直接指定删除链表中的某个数据元素
    DLinkListNode* DLinkList_DeleteNode(DLinkList* list, DLinkListNode* node)
    {
    	//判断list是否为有效指针
    	if (list != NULL)
    	{
    		int nPos = 0;
    
    		//定义结构体类型指针,并给其赋值
    		TDLinkList* dlist = (TDLinkList*)list;
    
    		int i = -1;
    
    		DLinkListNode* delNode = NULL;
    
    		//查找与node节点相等的节点
    		for (i = 0; i < dlist->length; ++i)
    		{
    			if (node == DLinkList_Get(list, i))
    			{
    				//保存位置
    				nPos = i;
    
    				//跳出循环
    				break;
    			}
    		}
    		//删除指定nPos位置节点
    		delNode = DLinkList_Delete(list, nPos);
    
    		return delNode;
    	}
    
    	printf("DLinkList_DeleteNode error: list or node 指针无效
    ");
    
    	return NULL;
    }
    

    双向链表基本功能测试

    #include "DLinkList.h"
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    
    //双向链表测试程序
    void dLinkListTest()
    {
    	int i = -1;
    
    	//定义Value结构体数组
    	Value val[10];
    	//当前游标指向的节点
    	Value* pVal = NULL;
    
    	//创建双向链表
    	DLinkList *dlist = DLinkList_Create();
    
    	//判断是否创建成功
    	if (dlist == NULL)
    	{
    		printf("双向链表创建失败
    ");
    
    		return;
    	}
    	
    	//初始化并向链表中插入数据
    	for (i = 0; i < 10; ++i)
    	{
    		val[i].value = i + 10;
    
    		//向尾部插入元素
    		DLinkList_Insert(dlist, (DLinkListNode*)&val[i], i);
    	}
    
    	printf("初始化双向链表成功
    ");
    	printf("
    ");
    
    	//遍历双向链表
    	printf("双向链表为:
    ");
    
    	for (i = 0; i < DLinkList_Length(dlist); ++i)
    	{
    		//获取指定位置元素
    		pVal = (Value*)DLinkList_Get(dlist, i);
    
    		printf("%d	", pVal->value);
    	}
    
    	printf("
    ");
    
    	printf("删除最后一个节点成功
    ");
    	printf("
    ");
    
    	//删除最后一个节点
    	DLinkList_Delete(dlist, DLinkList_Length(dlist) - 1);
    
    	//删除第一节点
    	DLinkList_Delete(dlist, 0);
    
    	printf("删除第一节点成功
    ");
    	printf("
    ");
    
    	//再次遍历链表
    	printf("删除最后一个节点和第一节点的双向链表为:
    ");
    	for (i = 0; i < DLinkList_Length(dlist); ++i)
    	{
    		//获取指定位置元素
    		pVal = (Value*)DLinkList_Get(dlist, i);
    
    		printf("%d	", pVal->value);
    	}
    
    	printf("
    ");
    
    	//重置游标
    	DLinkList_Reset(dlist);
    
    	//游标后移
    	DLinkList_Next(dlist);
    	//获取当前游标指向的节点
    	pVal = (Value*)DLinkList_Current(dlist);
    	//打印当前节点的value值
    	printf("游标后移后,打印当前游标指向的节点的value值: value = %d
    ", pVal->value);
    	printf("
    ");
    
    	//删除游标指向的当前节点
    	DLinkList_DeleteNode(dlist, (DLinkListNode*)pVal);
    	//再次获取当前游标指向的节点
    	pVal = (Value*)DLinkList_Current(dlist);
    	//再次打印当前节点的value值
    	printf("删除游标指向的当前节点,打印当前游标指向的节点的value值: value = %d
    ", pVal->value);
    	printf("
    ");
    
    	//向前移动游标
    	DLinkList_Prev(dlist);
    	//第三次获取当前游标指向的节点
    	pVal = (Value*)DLinkList_Current(dlist);
    	//第三次打印当前节点的value值
    	printf("向前移动游标,打印当前游标指向的节点的value值: value = %d
    ", pVal->value);
    	printf("
    ");
    
    	//打印链表的长度
    	printf("链表的长度, Length = %d
    ", DLinkList_Length(dlist));
    	printf("
    ");
    
    	//销毁双向链表
    	DLinkList_Destroy(dlist);
    }
    
    void main()
    {
    	dLinkListTest();
    
    	system("pause");
    }
    

    运行结果:

    2.4.3 优点和缺点

    优点:

    • 双向链表在单链表的基础上增加了指向前驱的指针

    • 功能上双向链表可以完全取代单链表的使用

    • 双向链表的 Next,Pre 和 Current 操作可以高效的遍历链表中的所有元素

    缺点:

    • 代码复杂
  • 相关阅读:
    刚刚开通
    腾讯面试经历2015
    排序之归并排序
    AC自动机
    后缀数组初步
    概率dp初探
    【NOIP2015】反思+题解
    Built-in functions
    poj2528 Mayor's posters(线段树区间覆盖)
    Codeforces #317 C.Lengthening Sticks(数学)
  • 原文地址:https://www.cnblogs.com/PikapBai/p/13362815.html
Copyright © 2020-2023  润新知