zoukankan      html  css  js  c++  java
  • [数据结构

    一、什么是双向链表?

    双向链表(double linked list)是在单链表的每个结点中,再设置一个指向其前驱结点的指针域。所以在双向链表中的结点都有两个指针域,一个指向直接后继,另一个指向直接前驱。

    既然单链表也可以有循环链表,那么双向链表当然也可以是循环表。

    线性表的双向链表存储结构如下:

    typedef int ElemType;
    typedef struct DulNode
    {
    	ElemType data;  //数据域
    	DulNode *prior;   //指向前驱结点的指针
    	DulNode *next;    //指向后继结点的指针
    }DulNode, DulList;
    

    双向链表的循环、带头结点的空链表如下:


    非空、循环、带头结点的双向链表如下:


    二、双向链表的基本操作

    2.1 插入操作

    双向链表的插入操作:


    实现代码如下:

    // 插入元素操作
    Status insertList(DulList *pList, int i, const ElemType e)
    {
    	// 判断链表是否存在
    	if (!pList)
    	{
    		printf("list not exist!
    ");
    		return FALSE;
    	}
    	// 只能在位置1以及后面插入,所以i至少为1
    	if (i < 1)
    	{
    		printf("i is invalid!
    ");
    		return FALSE;
    	}
    
    	// 找到i位置所在的前一个结点
    	Node *front = pList; // 这里是让front与i不同步,始终指向j对应的前一个结点
    	for (int j = 1; j < i; j++) // j为计数器,赋值为1,对应front指向的下一个结点,即插入位置结点
    	{
    		front = front->next;
    		if (front == NULL)
    		{
    			printf("dont find front!
    ");
    			return FALSE;
    		}
    	}
    		
    	// 创建一个空节点,存放要插入的新元素
    	Node *temp = (Node *)malloc(sizeof(Node));
    	if (!temp)
    	{
    		printf("malloc error!
    ");
    		return FALSE;
    	}
    	temp->data = e;
    
    	// 插入结点
    	temp->prior = front;
    	temp->next = front->next;
    	// 当空链表第一次插入结点时,此时head->next = NULL,调用NULL->prior会出错
    	if (front->next != NULL)
    		front->next->prior = temp;
    	front->next = temp;
    
    	return TRUE;
    }
    

    注意当空链表第一次插入结点的特殊情况。


    2.2 删除操作

    双向链表的删除操作:


    实现代码如下:

    // 删除元素操作
    Status deleteList(DulList *pList, int i, ElemType *e)
    {
    	// 判断链表是否存在
    	if (!pList)
    	{
    		printf("list not exist!
    ");
    		return FALSE;
    	}
    	// 只能删除位置1以及以后的结点
    	if (i < 1)
    	{
    		printf("i is invalid!
    ");
    		return FALSE;
    	}
    
    	// 找到i位置所在的前一个结点
    	Node *front = pList; // 这里是让front与i不同步,始终指向j对应的前一个结点
    	for (int j = 1; j < i; j++) // j为计数器,赋值为1,对应front指向的下一个结点,即插入位置结点
    	{
    		front = front->next;
    		if (front->next == NULL)
    		{
    			printf("dont find front!
    ");
    			return FALSE;
    		}
    	}
    
    	// 提前保存要删除的结点
    	Node *temp = front->next;
    	*e = temp->data; // 将要删除结点的数据赋给e
    
    	// 删除结点	
    	if (front->next->next != NULL) // 删除的不是尾结点,才进入
    	{
    		front->next->prior = front;
    	}
    	front->next = front->next->next;
    
    	// 销毁结点	
    	free(temp);
    	temp = NULL;
    
    	return TRUE;
    }
    

    注意:当双向链表的空链表第一次插入结点时,或者在尾结点后插入或删除的特殊情况,不需要设置插入位置后一个结点的直接前驱指针,因为此时插入位置的后一个结点为 NULL。


    三、完整程序

    #include <stdio.h>
    #include <stdlib.h>
    
    #define TRUE 1
    #define FALSE 0
    typedef int Status; // Status是函数结果状态,成功返回TRUE,失败返回FALSE
    
    typedef int ElemType;
    // 双向非循环链表的结构定义
    typedef struct Node
    {
    	ElemType data;  //数据域
    	Node *prior;   //指向前驱结点的指针
    	Node *next;    //指向后继结点的指针
    }Node, DulList;
    
    void initList(DulList **pList); // 初始化链表操作
    Status insertList(DulList *pList, int i, const ElemType e); // 插入元素操作
    Status deleteList(DulList *pList, int i, ElemType *e); // 删除元素操作
    Status getElem(DulList *pList, int i, ElemType *e); // 获取元素操作
    Status insertListHead(DulList *pList, const ElemType e); // 头部后插入元素操作
    Status insertListTail(DulList *pList, const ElemType e); // 尾部后插入元素操作
    Status clearList(DulList *pList); // 清空链表操作
    void traverseList(DulList *pList); // 遍历链表操作
    int getLength(DulList *pList); // 获取链表长度操作
    
    // 初始化链表操作
    void initList(DulList **pList) // 必须使用双重指针,一重指针申请会出错
    {
    	*pList = (DulList *)malloc(sizeof(Node));
    	if (!pList)
    	{
    		printf("malloc error!
    ");
    		return;
    	}
    
    	(*pList)->data = 0;
    	(*pList)->prior = NULL;
    	(*pList)->next = NULL;
    }
    
    // 插入元素操作
    Status insertList(DulList *pList, int i, const ElemType e)
    {
    	// 判断链表是否存在
    	if (!pList)
    	{
    		printf("list not exist!
    ");
    		return FALSE;
    	}
    	// 只能在位置1以及后面插入,所以i至少为1
    	if (i < 1)
    	{
    		printf("i is invalid!
    ");
    		return FALSE;
    	}
    
    	// 找到i位置所在的前一个结点
    	Node *front = pList; // 这里是让front与i不同步,始终指向j对应的前一个结点
    	for (int j = 1; j < i; j++) // j为计数器,赋值为1,对应front指向的下一个结点,即插入位置结点
    	{
    		front = front->next;
    		if (front == NULL)
    		{
    			printf("dont find front!
    ");
    			return FALSE;
    		}
    	}
    		
    	// 创建一个空节点,存放要插入的新元素
    	Node *temp = (Node *)malloc(sizeof(Node));
    	if (!temp)
    	{
    		printf("malloc error!
    ");
    		return FALSE;
    	}
    	temp->data = e;
    
    	// 插入结点
    	temp->prior = front;
    	temp->next = front->next;
    	// 当空链表第一次插入结点时,此时head->next = NULL,调用NULL->prior会出错
    	if (front->next != NULL)
    		front->next->prior = temp;
    	front->next = temp;
    
    	return TRUE;
    }
    
    // 删除元素操作
    Status deleteList(DulList *pList, int i, ElemType *e)
    {
    	// 判断链表是否存在
    	if (!pList)
    	{
    		printf("list not exist!
    ");
    		return FALSE;
    	}
    	// 只能删除位置1以及以后的结点
    	if (i < 1)
    	{
    		printf("i is invalid!
    ");
    		return FALSE;
    	}
    
    	// 找到i位置所在的前一个结点
    	Node *front = pList; // 这里是让front与i不同步,始终指向j对应的前一个结点
    	for (int j = 1; j < i; j++) // j为计数器,赋值为1,对应front指向的下一个结点,即插入位置结点
    	{
    		front = front->next;
    		if (front->next == NULL)
    		{
    			printf("dont find front!
    ");
    			return FALSE;
    		}
    	}
    
    	// 提前保存要删除的结点
    	Node *temp = front->next;
    	*e = temp->data; // 将要删除结点的数据赋给e
    
    	// 删除结点	
    	if (front->next->next != NULL) // 删除的不是尾结点,才进入
    	{
    		front->next->prior = front;
    	}
    	front->next = front->next->next;
    
    	// 销毁结点	
    	free(temp);
    	temp = NULL;
    
    	return TRUE;
    }
    
    // 获取元素操作
    Status getElem(DulList *pList, int i, ElemType *e)
    {
    	// 判断链表是否存在
    	if (!pList)
    	{
    		printf("list not exist!
    ");
    		return FALSE;
    	}
    	// 只能获取位置1以及以后的元素
    	if (i < 1)
    	{
    		printf("i is invalid!
    ");
    		return FALSE;
    	}
    
    	// 找到i位置所在的结点
    	Node *cur = pList->next; // 这里是让cur指向链表的第1个结点,与j同步
    	for (int j = 1; j < i; j++) // j为计数器,赋值为1,对应cur指向结点
    	{
    		cur = cur->next;
    		if (cur == NULL)
    		{
    			printf("dont find front!
    ");
    			return FALSE;
    		}
    	}
    
    	// 取第i个结点的数据
    	*e = cur->data;
    
    	return TRUE;
    }
    
    // 头部后插入元素操作
    Status insertListHead(DulList *plist, const ElemType e)
    {
    	Node *head;
    	Node *temp;
    
    	// 判断链表是否存在
    	if (!plist)
    	{
    		printf("list not exist!
    ");
    		return false;
    	}
    
    	// 让head指向链表的头结点
    	head = plist;
    
    	// 创建存放插入元素的结点
    	temp = (Node *)malloc(sizeof(Node));
    	if (!temp)
    	{
    		printf("malloc error!
    ");
    		return false;
    	}
    	temp->data = e;
    
    	// 头结点后插入结点
    	temp->prior = head;
    	temp->next = head->next;
    	// 当空链表第一次插入结点时,此时head->next = NULL,调用NULL->prior会出错
    	if (head->next != NULL) 
    		head->next->prior = temp;
    	head->next = temp;
    
    	return true;
    }
    
    // 尾部后插入元素操作
    Status insertListTail(DulList *pList, const ElemType e)
    {
    	Node *cur;
    	Node *temp;
    
    	// 判断链表是否存在
    	if (!pList)
    	{
    		printf("list not exist!
    ");
    		return FALSE;
    	}
    
    	// 找到链表尾节点
    	cur = pList;
    	while (cur->next)
    	{
    		cur = cur->next;
    	}
    
    	// 创建存放插入元素的结点
    	temp = (Node *)malloc(sizeof(Node));
    	if (!temp)
    	{
    		printf("malloc error!
    ");
    		return -1;
    	}
    	temp->data = e;
    
    	// 尾结点后插入结点
    	temp->prior = cur;
    	temp->next = cur->next;
    	cur->next = temp; // 尾结点的直接后继指针是NULL,所以不用指定NULL的前驱指针
    
    	return TRUE;
    }
    
    // 清空链表操作
    Status clearList(DulList *pList)
    {
    	Node *cur; // 当前结点
    	Node *temp; // 事先保存下一结点,防止释放当前结点后导致“掉链”
    
    	// 判断链表是否存在
    	if (!pList)
    	{
    		printf("list not exist!
    ");
    		return FALSE;
    	}
    
    	cur = pList->next; // 指向第一个结点
    	while (cur)
    	{
    		temp = cur->next; // 事先保存下一结点,防止释放当前结点后导致“掉链”
    		free(cur); // 释放当前结点
    		cur = temp; // 将下一结点赋给当前结点p
    	}
    	pList->next = NULL; // 头结点指针域指向空
    
    	return TRUE;
    }
    
    // 遍历链表操作
    void traverseList(DulList *pList)
    {
    	// 判断链表是否存在
    	if (!pList)
    	{
    		printf("list not exist!
    ");
    		return;
    	}
    
    	Node *cur = pList->next;
    	while (cur != NULL)
    	{
    		printf("%d ", cur->data);
    		cur = cur->next;
    	}
    	printf("
    ");
    }
    
    // 获取链表长度操作
    int getLength(DulList *pList)
    {
    	Node *cur = pList;
    	int length = 0;
    
    	while (cur->next)
    	{
    		cur = cur->next;
    		length++;
    	}
    
    	return length;
    }
    
    int main()
    {
    	DulList *pList;
    
    	// 初始化链表
    	initList(&pList);
    	printf("初始化链表!
    
    ");
    
    	// 尾部后插入结点
    	printf("尾部后插入元素1、2、3
    
    ");
    	for (int i = 0; i < 3; i++)
    	{
    		insertListTail(pList, i+1);
    	}
    
    	// 头部后插入元素
    	insertListHead(pList, 5);
    	printf("头部后插入元素5
    
    ");
    
    	// 插入结点
    	insertList(pList, 1, 9);
    	printf("在位置1插入元素9
    
    ");
    
    	// 遍历链表并显示元素操作
    	printf("遍历链表:");
    	traverseList(pList);
    	printf("
    ");
    
    	// 删除结点
    	int val;
    	deleteList(pList, 2, &val);
    	printf("删除位置2的结点,删除结点的数据为: %d
    ", val);
    	printf("
    ");
    
    	// 遍历链表并显示元素操作
    	printf("遍历链表:");
    	traverseList(pList);
    	printf("
    ");
    
    	// 获得链表长度
    	printf("链表长度: %d
    
    ", getLength(pList));
    
    	// 销毁链表
    	clearList(pList);
    	printf("销毁链表
    
    ");
    
    	return 0;
    }
    

    输出结果如下图所示:


    参考:

    《大话数据结构 - 第3章》 线性表


  • 相关阅读:
    silverlight的Datagrid控件列绑定属性笔记
    VC字符串处理整理
    Combobox实现多项选择 Silverlight下“Combobox”怎样实现多项选择?
    C# 类初始化顺序
    Silverlight程序设置断点无法进入调试的解决方案
    有哪些适合新手练手的Python项目?
    Ubuntu 终端常用命令
    浅析python 中__name__ = '__main__' 的作用
    py thon 多线程(转一篇好文章)
    python os.path模块
  • 原文地址:https://www.cnblogs.com/linuxAndMcu/p/10309362.html
Copyright © 2011-2022 走看看