美文网首页
数据结构与算法(四)-- 双向链表

数据结构与算法(四)-- 双向链表

作者: 樂亦leeyii | 来源:发表于2020-04-08 11:05 被阅读0次

    双向链表

    双向链表的数据结构设计

    双向链表的数据结构设计和单向链表的结构差别不大,需要添加一个指针域用来指向前驱结点。

    typedef int Element;
    
    typedef struct Node{
        Element data;       // 数据域
        struct Node *piror; // 前驱
        struct Node *next;  // 后继
        
    } Node, *LinkList;
    

    创建

    创建双向链表步骤如下:

    1. 创建头结点
    2. 创建一个指针变量 tail 指向尾结点
    3. 新建一个结点,将新建结点的 prior 指向 tail, 将 tail->next 指向新建的结点
    4. tail 指向新建的结点
    5. 重复 3 4 步
    Status CreateLinkList(LinkList *l) {
        
        if (*l) return ERROR;
        
        // 正在被创建节点的指针
        Node *p = NULL;
        // 指向尾结点的指针
        Node *tail = NULL;
        
        // 1. 创建头结点
        Node *head = (LinkList)malloc(sizeof(Node));
        if (head == NULL) return ERROR;
        head->prior = NULL;
        head->next = NULL;
        head->data = -1;
        *l = head;
        tail = head;
    
        printf("依次输入创建双向链表的元素,0结束输入\n");
        int a;
        while (1) {
            scanf("%d", &a);
            if (a == 0) break;
            p = (LinkList)malloc(sizeof(Node));
            if (!p) return ERROR;
            p->data = a;
            p->prior = tail;
            p->next = NULL;
            
            tail->next = p;
            tail = p;
        }
        
        return OK;
    }
    

    插入

    插入结点时要考虑两种情况:

    • 插入位置在链表的中间

    如同所示在结点1结点2之间插入结点4的步骤如下:

    1. 结点4next 指向 结点2
    2. 结点2prior 指向 结点4
    3. 结点1next 指向 结点4
    4. 结点4prior 指向 结点1
    • 插入位置在链表的尾部

    如同所示插入结点在链表的尾部。

    代码如下:

    Status ListInsert(LinkList *l, int index, Element e) {
        
        if (*l == NULL || index < 0) return ERROR;
        
        Node *p = *l, *node = NULL;
        
        // 1. 寻找插入位置的前一个节点
        for (int i = 0; i < index; i++) {
            p = p -> next;
            if (p == NULL) {
                printf("插入位置大于链表长度\n");
                return ERROR;
            }
        }
        
        // 2. 创建插入节点
        node = (LinkList)malloc(sizeof(node));
        if (!node) return ERROR;
        node->data = e;
        node->prior = NULL;
        node->next = NULL;
        
        // 3. 插入节点
        
        // 插入位置在不是一个节点
        if (p->next != NULL) {
            // 建立插入节点和后继节点之间的关系
            node->next = p->next;
            p->next->prior = node;
        }
        // 建立插入节点和前驱节点之间的关系
        p->next = node;
        node->prior = p;
        
        return OK;
    }
    

    根据位置删除结点

    根据位置阐述结点思路如下:

    1. 寻找到要删除的结点
    2. 如果删除的结点不是尾结点, 将删除结点的前驱的后继指向删除结点的后继, 将删除结点的后继的前驱指向删除结点的前驱
    3. 如果删除的结点时尾结点, 将尾结点的前驱的后继指向 NULL.

    代码如下:

    Status ListDelete(LinkList *l, int index, Element *e) {
        
        if (*l == NULL || index < 0) return ERROR;
        
        Node *p = *l;
        
        if (p->next == NULL) {
            printf("要删除的链表为空\n");
            return ERROR;
        }
        
        // 1.寻找要删除节点
        for (int i = 0; i <= index; i++) {
            p = p->next;
            if (!p) {
                // 删除位置不合法
                printf("删除位置大于链表长度\n");
                return ERROR;
            }
        }
        
        if (p->next == NULL) {
            p->prior->next = NULL;
        } else {
            p->prior->next = p->next;
            p->next->prior = p->prior;
        }
        *e = p->data;
        free(p);
        
        return ERROR;
    }
    

    根据数据删除结点

    根据数据删除结点的思路和根据位置删除结点的思路大致相同,不同是寻找删除结点的方式。根据数据删除结点,首先要遍历链表对比每个结点的数据,如果相同说明找到了删除的结点。

    Status ListDeleteValue(LinkList *l, Element e) {
        
        if (*l == NULL) return ERROR;
        
        Node *p = (*l)->next;
        
        while (p && p->data != e) {
            p = p->next;
        }
        
        if (!p) {
            printf("没有找到查到要删除的元素");
            return ERROR;
        }
        
        if (p->next == NULL) {
            p->prior->next = NULL;
        } else {
            p->prior->next = p->next;
            p->next->prior = p->prior;
        }
        free(p);
        
        return OK;
    }
    

    双向循环链表

    双向循环链表的尾结点的后继指向了头结点,头结点的前驱指向了尾结点。形成了一个闭环,因此叫做双向循环链表。

    数据结构

    双向循环链表和双向链表的数据结构相同。

    typedef int Element;
    
    typedef struct Node{
        Element data;       // 数据域
        struct Node *piror; // 前驱
        struct Node *next;  // 后继
        
    } Node, *LinkList;
    

    创建

    // 创建双向链表
    Status CreateList(LinkList *l) {
        
        // 创建头结点
        *l = (LinkList)malloc(sizeof(Node));
        if (*l == NULL) return ERROR;
        (*l)->next = *l;
        (*l)->prior = *l;
        
        // 尾结点指针
        Node *tail = *l;
        
        for (int i = 0; i < 10; i++) {
            // 创建新的结点
            Node *n = malloc(sizeof(Node));
            n->data = i;
            // 1. 尾结点的后继的前驱指向新建结点, 相当于头结点的前驱指向 n
            tail->next->prior = n; // (*l)->prior = n;
            // 2. 新建结点的后继指向尾结点的后继, 相当于n的后继指向 头结点
            n->next = tail->next; // n->next = (*l);
            // 3. 新建结点的前驱指向尾结点
            n->prior = tail;
            // 4. 尾结点的后置指向新建结点
            tail->next = n;
            // 5. 尾指针指向新建结点
            tail = n;
        }
        
        return OK;
    }
    

    插入

    双向循环链表插入思路和双向链表插入思路一样,只不过不需要考虑尾结点的情况。

    //插入元素
    Status ListInsert(LinkList *l, int index, Element e) {
        
        if (*l == NULL || index < 0) return ERROR;
        
        Node *p = (*l);
        // 寻找插入位置的前一个结点
        for (int i = 0; i < index; i++) {
            p = p->next;
            // 遍历一遍, 还没有到插入位置, 直接return
            if (p == *l) {
                printf("插入位置超过链表的长度\n");
                return ERROR;
            };
        }
        
        Node *n = malloc(sizeof(Node));
        if (!n) return ERROR;
        n->data = e;
        // 插入结点的后继指向插入结点前驱的后继
        n->next = p->next;
        // 插入结点后继的前驱指向插入结点
        p->next->prior = n;
        // 插入结点前驱的后继指向插入结点
        p->next = n;
        // 插入结点前驱指向p
        n->prior = p;
        
        return OK;
    }
    

    删除

    /// 删除
    Status ListDelete(LinkList *l, int index, Element *e) {
        
        if (*l == NULL || index < 0) return ERROR;
        
        Node *p = *l;
        
        if (p->next == p) {
            printf("链表为空\n");
            return ERROR;
        }
        // 查找删除的元素
        for (int i = 0; i <= index; i++) {
            p = p->next;
            if (p == *l) {
                printf("删除位置超过链表长度\n");
                return ERROR;
            }
        }
        // 删除结点的前驱的后继指向删除结点的后继
        p->prior->next = p->next;
        // 删除结点的后继的前驱指向删除结点的前驱
        p->next->prior = p->prior;
        *e = p->data;
        // 释放删除结点内存
        free(p);
        return OK;
    }
    

    相关文章

      网友评论

          本文标题:数据结构与算法(四)-- 双向链表

          本文链接:https://www.haomeiwen.com/subject/jfizphtx.html