数据结构(一)线性表双向链表

(一)定义

双向链表是在单链表的每个结点中,再设置一个纸箱其前驱结点的指针域

(二)结点结构

typedef struct Node
{
    ElemType data;
    struct Node* prior;    //直接前驱指针
    struct Node* next;    //直接后继指针
}Node;

typedef struct Node* CLinkList;

(三)双向链表结构

双向循环链表

带有头结点的空链表

带有头结点的数据链表

对于非循环的,直接将头结点的直接前驱指针置为空,将尾结点的直接后驱结点置为空即可

(四)实现双向链表

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

typedef char ElemType;
typedef int Status;

typedef struct Node
{
    ElemType data;
    struct Node* prior;    //直接前驱指针
    struct Node* next;    //直接后继指针
}Node;

typedef struct Node* DLinkList;

//四个基本操作,初始化,清空,获取长度,是否为空
Status InitList(DLinkList* Dl);
Status ClearList(DLinkList* Dl);
int GetLength(DLinkList Dl);
Status EmptyList(DLinkList Dl);

//四个插入,程序,删除操作
Status GetElem(DLinkList Dl, int i, ElemType* e);
int LocateElem(DLinkList Dl, ElemType e);
Status InsertList(DLinkList* Dl, int i, ElemType e);
Status DeleteList(DLinkList* Dl, int i, ElemType* e);

//打印链表
void PrintList(DLinkList Dl, int i);

int main()
{
    DLinkList dbList;
    ElemType e='A';
    int i = 0;
    
    InitList(&dbList);//初始化链表
    
    for (; i < 26;i++)    //插入数据
    {
        //InsertList(&dbList, -1, e + i);    //尾插法
        InsertList(&dbList, GetLength(dbList)+1, e + i);    //头插法
    }

    PrintList(dbList, 3);
    PrintList(dbList, -3);

    system("pause");
    return 0;
}


//四个基本操作,初始化,清空,获取长度,是否为空
//创建一个带有头结点的双向链表
Status InitList(DLinkList* Dl)
{
    *Dl = (DLinkList)malloc(sizeof(Node));
    if (*Dl == NULL)
        return ERROR;
    (*Dl)->next = (*Dl)->prior = (*Dl);    //都指向头结点,虽然这里数据data为char,也是可以存储链表长度的,但是这里不使用
    return OK;
}

//清空双向链表,头结点也释放掉
Status ClearList(DLinkList* Dl)
{
    DLinkList Dlist = *Dl;
    DLinkList q;

    if (Dlist == NULL)
        return ERROR;

    //先将尾结点的next指针置为空,一会作为判断循环退出条件
    Dlist->prior->next = NULL;
    while (Dlist)
    {
        q = Dlist;
        Dlist = Dlist->next;
        free(q);
    }

    return OK;
}

//获取链表长度,不含头结点,使用一条单向指针域即可
int GetLength(DLinkList Dl)
{
    DLinkList cur = Dl->next;
    int length = 0;
    while (cur!=Dl)
    {
        cur = cur->next;
        length++;
    }
    return length;
}

//判断是否链表为空,判断头结点一个指针是否指向自己就可以
Status EmptyList(DLinkList Dl)
{
    if (Dl->prior == Dl)
        return TRUE;
    return FALSE;
}

//四个插入,程序,删除操作
//根据索引获取数据,支持双向索引
Status GetElem(DLinkList Dl, int i, ElemType* e)
{
    int j = 0;
    DLinkList cur = Dl;
    if (e == NULL||i==0)
        return ERROR;
    for (; j < abs(i);j++)
    {
        if (i < 0)
            cur = cur->prior;
        else
            cur = cur->next;
    }
    *e = cur->data;
    return OK;
}

//按照元素进行查找,单向正向查找
int LocateElem(DLinkList Dl, ElemType e)
{
    DLinkList cur = Dl->next;
    int index=1;
    while (cur->data != e&&cur != Dl)
    {
        cur = cur->next;
        index++;
    }
    if (cur == Dl)
        return 0;
    return index;
}

//支持双向插入
Status InsertList(DLinkList* Dl, int i, ElemType e)
{
    DLinkList cur = *Dl;
    DLinkList newNode;
    int j = 0;
    if (*Dl == NULL || abs(i) > GetLength(*Dl) + 1 || i == 0)
        return ERROR;
    for (; j < abs(i);j++)    //找到他的后一个节点,一会向前推
    {
        if (i < 0)
            cur = cur->prior;
        else
            cur = cur->next;
    }

    //创建一个新的结点
    newNode = (DLinkList)malloc(sizeof(Node));
    newNode->data = e;

    if (i<0)
    {
        newNode->next = cur->next;
        newNode->prior = cur;
        cur->next->prior = newNode;
        cur->next = newNode;
    }
    else
    {
        newNode->next = cur;
        newNode->prior = cur->prior;
        cur->prior->next = newNode;
        cur->prior = newNode;
    }
    return OK;
}

//支持双向删除
Status DeleteList(DLinkList* Dl, int i, ElemType* e)
{
    DLinkList cur = *(Dl);
    DLinkList oldNode;
    int j = 0;
    if (*Dl == NULL || abs(i) > GetLength(*Dl) || i == 0 || e == NULL)
        return ERROR;
    for (; j < abs(i); j++)    //找到要删除的结点
    {
        if (i < 0)
            cur = cur->prior;
        else
            cur = cur->next;
    }
    //赋值
    *e = cur->data;
    oldNode = cur;
    //开始交换指针顺序
    oldNode->prior->next = oldNode->next;
    oldNode->next->prior = oldNode->prior;
    free(oldNode);
    return OK;
}


//打印链表,支持双向打印,其实在main方法中稍微调整传入的结点也可以实现
void PrintList(DLinkList Dl, int i)
{
    DLinkList cur = Dl;
    DLinkList start;
    int j = 0;

    if (i==0)    //若是输入0,按照正向找到第一个结点进行输出即可
        cur = cur->next;

    for (; j < abs(i); j++)    //找到要开始打印的最开始那个结点
    {
        if (i < 0)
            cur = cur->prior;
        else
            cur = cur->next;
    }
    start = cur;    //找到最开始打印的那个结点
    while (cur->next!=start)    //进行判断结点
    {
        if (cur!=Dl)    //不打印头结点
            printf("%c ", cur->data);
        cur = cur->next;
    }
    if (cur != Dl)
        printf("%c", cur->data);
    printf("\n");
}

(五)打印预览

(六)总结

双向链表相对于单链表来说,要复杂些,多了个直接前驱指针,对于初入,删除考虑的指针交换需要格外小心。而且占用空间也增加了。
但是他有良好的对称性,是对于某个结点的前后结点的操作,带来了方便,提高了算法时间性能。也就是使用空间来换取时间。
原来单链表要寻找某个结点的前一个节点的时间复杂度是O(n),使用双向链表去查找前一个节点的时间复杂度是O(1)

 

posted @ 2018-08-06 12:55  山上有风景  阅读(502)  评论(0编辑  收藏  举报