线性表之双向链表

一,双向链表的基础知识

1.双向链表的概念

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

2.双向链表实现的难点

  • 每一个数据元素有两个指针域,一个指向其前驱结点,一个指向其后继结点。
  • 第一个结点的前驱为NULL,最后一个节点的后继为NULL。

二,双向链表的实现

1.双向链表的基本功能(DoubleLinkList.h)

# ifndef DOUBLE_LINK_LIST
# define DOUBLE_LINK_LIST

/* 业务节点 */
typedef void Node;

/* 链表节点(被包含) */
typedef struct DoubleNode
{
    struct DoubleNode * prev;
    struct DoubleNode * next;
}DoubleNode;

/* 双向链表 */
typedef struct DoubleLinkList
{
    Node * ptr;
    DoubleNode  header;
    DoubleNode  slider;
    int length;
}DoubleLinkList;

/* 创建链表 */
DoubleLinkList * createList();

/* 清空链表 */
void clear(DoubleLinkList * list);

/* 销毁链表 */
void destory(DoubleLinkList * list);

/* 链表长度 */
int length(DoubleLinkList * list);

/* 链表是否为空 */
int empty(DoubleLinkList * list);

/* 插入链表 */
void insert(DoubleLinkList * list, int pos, Node * node);

/* 删除链表 */
Node * del(DoubleLinkList * list, int pos);

/* 删除某个元素 */
Node * delNode(DoubleLinkList * list, Node * node);

/* 获取链表 */
Node * get(DoubleLinkList * list, int pos);

/* 重置游标 */
void resetSlider(DoubleLinkList * list);

/* 游标下移 */
Node * next(DoubleLinkList * list);

/* 游标上移 */
Node * prev(DoubleLinkList * list);

/* 获取当前游标的结点 */
Node * current(DoubleLinkList * list);

# endif

2.双向链表基本功能的实现(DoubleLinkList.c)

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

/* 创建链表 */
DoubleLinkList * createList()
{
    DoubleLinkList * list = (DoubleLinkList *)malloc(sizeof(DoubleLinkList));

    /* 初始化头指针 */
    list->ptr = &(list->header);
    /* 初始化头结点 */
    list->header.next = NULL;
    list->header.prev = NULL;
    /* 初始化游标 */
    list->slider.next = NULL;
    /* 初始化链表长度 */
    list->length = 0;

    return list;
}

/* 清空链表 */
void clear(DoubleLinkList * list)
{
    /* 置空头结点 */
    list->header.next = NULL;
    list->header.prev = NULL;
    /* 置空游标 */
    list->slider.next = NULL;
    /* 置空链表长度 */
    list->length = 0;
}

/* 销毁链表 */
void destory(DoubleLinkList * list)
{
    if (list != NULL)
    {
        free(list);
        list = NULL;
    }
}

/* 链表长度 */
int length(DoubleLinkList * list)
{
    return list->length;
}

/* 链表是否为空 */
int empty(DoubleLinkList * list)
{
    if (list->length <= 0)
    {
        return 1;
    }
    else {
        return 0;
    }
}

/* 插入链表 */
void insert(DoubleLinkList * list, int pos, Node * node)
{
    if (list == NULL)
    {
        printf("链表为NULL\n");
        return;
    }
    /* 判断插入位置合法性 */
    pos = pos < 0 ? 0 : pos;
    pos = pos > length(list) ? length(list) : pos;
    /* 将业务结点转换为链表结点 */
    DoubleNode * _node = (DoubleNode *)node;
    /* 判断是否是第一次插入 */
    if (length(list) == 0)
    {
        list->header.next = _node;
        _node->prev = NULL;
        _node->next = NULL;
        list->length++;
        return;
    }
    /* 判断是否是插入头部 */
    if (pos == 0)
    {
        list->header.next->prev = _node;
        _node->next = list->header.next;
        list->header.next = _node;
        _node->prev = NULL;
    }
    else {
        DoubleNode * posNode = list->header.next;
        DoubleNode * previous = posNode;
        for (int i = 0; i < pos; i++)
        {
            previous = posNode;
            posNode = posNode->next;
        }
        previous->next = _node;
        _node->prev = previous;
        posNode->prev = _node;
        _node->next = posNode;
    }
    list->length++;
}

/* 删除链表 */
Node * del(DoubleLinkList * list, int pos)
{
    if (list == NULL)
    {
        printf("链表为NULL\n");
        return NULL;
    }
    if (length(list) == 0)
    {
        printf("链表长度为0,删除失败\n");
        return NULL;
    }
    /* 判断删除位置合法性 */
    pos = pos < 0 ? 0 : pos;
    pos = pos > length(list) ? length(list) : pos;
    /* 定义删除的返回结点 */
    DoubleNode * result = NULL;
    /* 判断是否删除第一个 */
    if (pos == 0)
    {
        result = list->header.next;
        list->header.next = list->header.next->next;
        list->header.next->prev = NULL;
    }
    else {
        DoubleNode * posNode = list->header.next;
        DoubleNode * previous = posNode;
        for (int i = 0; i < pos; i++)
        {
            previous = posNode;
            posNode = posNode->next;
        }
        result = posNode;
        previous->next = posNode->next;
        posNode->next->prev = previous;
    }
    list->length--;
    return result;
}

/* 删除某个元素 */
Node * delNode(DoubleLinkList * list, Node * node)
{
    /* 找出该元素位置 */
    int pos = -1;
    /* 遍历寻找 */
    for (int i = 0; i < length(list); i++)
    {
        Node * tmp = get(list, i);
        if (tmp == node)
        {
            pos = i;
        }
    }
    /* 如果未找到退出 */
    if (pos == -1)
    {
        printf("未找到该元素\n");
        return NULL;
    }
    /* 找到后删除 */
    Node * result = del(list, pos);

    return result;
}

/* 获取链表 */
Node * get(DoubleLinkList * list, int pos)
{
    DoubleNode * posNode = list->header.next;
    for (int i = 0; i < pos; i++)
    {
        posNode = posNode->next;
    }
    return posNode;
}

/* 重置游标 */
void resetSlider(DoubleLinkList * list)
{
    list->slider.next = list->header.next;
}

/* 游标下移 */
Node * next(DoubleLinkList * list)
{
    if (list->slider.next->next != NULL)
    {
        DoubleNode * result = list->slider.next;
        list->slider.next = list->slider.next->next;
        return result;
    }
    return NULL;
}

/* 游标上移 */
Node * prev(DoubleLinkList * list)
{
    if (list->slider.next->prev != NULL)
    {
        DoubleNode * result = list->slider.next;
        list->slider.next = list->slider.next->prev;
        return result;
    }
    return NULL;
}

/* 获取当前游标的结点 */
Node * current(DoubleLinkList * list)
{
    return list->slider.next;
}

3.双向链表的测试

# define _CRT_SECURE_NO_WARNINGS
# include<stdio.h>
# include<string.h>
# include"DoubleLinkList.h"

typedef struct Student
{
    DoubleNode next;
    char name[64];
    int age;
}Student;

int main()
{
    Student s1;
    strcpy(s1.name, "刘备");
    s1.age = 56;

    Student s2;
    strcpy(s2.name, "关羽");
    s2.age = 40;

    Student s3;
    strcpy(s3.name, "张飞");
    s3.age = 38;

    /* 创建双向链表 */
    DoubleLinkList * list = createList();

    /* 插入数据 */
    insert(list, 0, &s1);
    insert(list, 0, &s2);
    insert(list, 0, &s3);

    /* 遍历 */
    printf("##############基本遍历##############\n");
    for (int i = 0; i < length(list); i++)
    {
        Student * stu = (Student *)get(list,i);
        printf("name = %s,age = %d\n", stu->name, stu->age);
    }

    /* 删除 */
    del(list, 0);
    printf("##############删除0号位置后遍历##############\n");
    for (int i = 0; i < length(list); i++)
    {
        Student * stu = (Student *)get(list, i);
        printf("name = %s,age = %d\n", stu->name, stu->age);
    }
    /* 删除节点 */
    delNode(list,&s2);
    printf("##############删除s2后遍历##############\n");
    for (int i = 0; i < length(list); i++)
    {
        Student * stu = (Student *)get(list, i);
        printf("name = %s,age = %d\n", stu->name, stu->age);
    }

    /* 重置游标 */
    resetSlider(list);
    Student * ss1 = (Student *)current(list);
    printf("##############重置游标##############\n");
    printf("name = %s,age = %d\n", ss1->name, ss1->age);
    /* 游标下移 */
    next(list);
    Student * ss2 = (Student *)current(list);
    printf("##############游标下移##############\n");
    printf("name = %s,age = %d\n", ss2->name, ss2->age);

    next(list);
    Student * ss3 = (Student *)current(list);
    printf("##############游标下移##############\n");
    printf("name = %s,age = %d\n", ss3->name, ss3->age);

    /* 游标上移 */
    prev(list);
    ss2 = (Student *)current(list);
    printf("##############游标上移##############\n");
    printf("name = %s,age = %d\n", ss2->name, ss2->age);
}
时间: 2024-10-05 11:06:16

线性表之双向链表的相关文章

(续)线性表之双向链表(C语言实现)

在前文实现单向链表的基本操作下,本文实现 双向链表的基本操作. 双向链表与单链表差异,是双向链表结点中有前向指针和后向指针. 所以在插入和删除新结点元素时候不见要考虑后向指针还要考虑 前向指针. 以下是双向链表的C代码: #include<stdio.h> typedef struct node { int data; struct node *next; struct node *prior }Node; //链表的初始化 Node* InitList(int number) { int i

[数据结构 - 第3章补充] 线性表之双向链表(C语言实现)

一.什么是循环链表? 双向链表(double linked list)是在单链表的每个结点中,再设置一个指向其前驱结点的指针域.所以在双向链表中的结点都有两个指针域,一个指向直接后继,另一个指向直接前驱. 既然单链表也可以有循环链表,那么双向链表当然也可以是循环表. 线性表的双向链表存储结构如下: typedef int ElemType; typedef struct DulNode { ElemType data; //数据域 DulNode *prior; //指向前驱结点的指针 DulN

数据结构与算法-线性表之双向链表

参考博客: http://www.cnblogs.com/skywang12345/p/3561803.html https://blog.csdn.net/howlaa/article/details/38513235 1.概述 线性表是一种线性结构,由数组.单项链表和双向链表组成,这里讲讨论双向链表的理论知识和实现代码. 双向链表和单项链表类似,双向链表由两个指针作为指针域,分别指向前驱节点和后驱节点.可以从任何节点开始向前或者向后删除.增加.查找节点. 双链表的示意图如下: 在插入节点的时

数据结构之—线性表之—双向链表之—浅谈双循环链表

双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱.所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点.一般我们都构造双向循环链表. /********************************************************************** * Copyright (c)2015,WK Studios * Filename: Tw_Node.c * Compiler: GCC,VS,VC6.

几种常见的线性表存储结构

1.线性表的的动态分配顺序存储结构 1 #define LIST_INIT_SIZE 100 //线性表存储空间的初始分配量 2 #define LISTINCREMENT 100 //线性表存储空间的分配增量 3 typedef struct { 4 ElemType *elem; //存储空间基址 5 int length; //当前长度 6 int size; //当前分配的存储容量 7 }SqList; //动态分配 + 顺序存储结构 2.线性表的单链表存储结构 1 typedef st

线性表.04.链式存储结构(双向循环链表)

以下是用双向循环链表实现的线性表 #include <stdio.h> #include <stdlib.h> #include <time.h> #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 typedef int ElemType;//ElemType这里假设为int,可以根据需要进行更改 typedef int Status;//Status是函数的类型,其值是函数结果状态代码,如OK等 t

线性表(List) 二

线性表的链式存储结构 概念 为了表示每个数据元素 ai 与其直接后继元素 ai+1 之间的逻辑关系,对于数据元素 ai 来说,除了要存储其本身的信息之外,还需要存储一个指示其直接后继的信息(即直接后继的物理位置). 将存储数据元素信息的域称为数据域,把存储直接后继位置的域成为指针域.指针域中存储的信息成为指针或链,这两个部分信息组成数据元素 ai 的存储映像,称为结点(Node). n个结点( ai 的存储映像)链结成一个链表,即为线性表 a1,a2,...,an 的链式存储结构,因此此链表的每

线性表的Java实现--链式存储(双向链表)

有了单向链表的基础,双向链表的实现就容易多了. 双向链表的一般情况: 增加节点: 删除节点: 双向链表的Java实现: package com.liuhao.algorithm;      public class DuLinkList<T> {          /**       * 内部类:链表中的一个节点       *        * @author liuhao data 节点中的数据 prev 指向前一个节点的引用 next 指向下一个节点的引用       */       

数据结构与算法--线性表系列(循环链表、双向链表)

hello,everybody,今天我们来学习线性表的最后两种形式,循环链表.双向链表.这两种链表,是链式存储结构的不同形式.书归正传,我们先来看看循环链表吧. 大家思考一个问题,我们把线性表各个元素比作下图的路线图上的城市: 我们的线性表各个结点的指针,都是指向唯一的后继结点,线性表的终端结点的指针为空.这样的话,如果我们在南京,我们需要先访问南京右j边的城市,再访问南京左边的城市.根据线性表的结构,我们只能返回上海,从上海依次访问到北京.因为我们的终端结点的指针为空,如果直接访问南京右边的城