数据结构【双向链表】

双向链表:

数据结构示意图1.1.1如下:
在这里插入图片描述

图1.1.1 链表节点结构示意图

让代码来讲话吧:

Structure.h

#ifndef DATA_STRUCTURE_DNLIST_STRUCTURE_H
#define DATA_STRUCTURE_DNLIST_STRUCTURE_H
#include <iostream>

using namespace std;
/*-------------------1.链表----------------------*/
#define Mark 0                     //宏定义链表Mark的起始值,更改此处会产生联动效果
#define HeadM pHead->mark = Mark   //设置链表头结点pHead的mark为0
// (1)单向链表:
// (2)单向循环链表:
typedef struct SCNode {
    int date;           //数据
    unsigned int mark;  //第mark个节点
    SCNode *pNext;      //节点尾指针
} SN,                   //单向
SCN;                    //单向循环
//二者数据结构相同
//=============================================//
// (3)双向链表:
// (4)双向循环链表:
typedef struct DNode {
    int date;           //数据
    unsigned int mark;  //第mark个节点
    DNode *pPrev;       //节点头指针
    DNode *pNext;       //节点尾指针
} DN,                   //双向
DCN;                    //双向循环
//二者数据结构相同
/*-------------------1.链表----------------------*/
#endif //DATA_STRUCTURE_DNLIST_STRUCTURE_H

Structure.cpp

#include "Structure.h"
#include "Operation.h"

/*-------------------1.链表----------------------*/
// (3)双向链表:
//遍历整个链表,返回尾节点
DN *FindTail(DN *pHead) {
    while (pHead->pNext != nullptr)pHead = pHead->pNext;
    return pHead;
}

//查找任意节点[调用此函数前,需确保List中mark的顺序性]
DN *FindNode(DN *pHead, int pos) {
    while (pos-Mark != pHead->mark) {
        pHead = pHead->pNext;
    }
    return pHead;
}

//规律化NODE[mark]
DN *RegularMark(DN *pHead) {
    DN *pNTemp = pHead;
    int count = Mark;
    do {
        pHead->mark = count++;
    }while (nullptr!= (pHead = pHead->pNext));
    return pNTemp;
}
/*-------------------1.链表----------------------*/
Operation.h

#ifndef DATA_STRUCTURE_DNLIST_OPERATION_H
#define DATA_STRUCTURE_DNLIST_OPERATION_H

#include "Structure.h"
/*-------------------1.链表----------------------*/

// (1)单向链表:
//  a.创建链表:
//  b.插入节点:1.头插     2.尾插    3.中间插
//  c.删除节点:1.头删     2.尾删    3.中间删
//  d.打印链表:
// (2)单向循环链表:
//  a.创建链表:
//  b.插入节点:1.头插     2.尾插    3.中间插
//  c.删除节点:1.头删     2.尾删    3.中间删
//  d.打印链表:
//=============================================//
// (3)双向链表:
DN *FindTail(DN *pHead);                //遍历整个链表,返回尾节点
DN *FindNode(DN *pHead, int pos);       //查找任意节点[调用此函数前,需确保List中mark的顺序性]
DN *RegularMark(DN *pHead);             //规律化NODE[mark]
//  a.创建链表:
DN *CreateHead();

//  b.插入节点:1.头插     2.尾插    3.中间插
DN *InsertHead(DN *pHead);              //1.头插
DN *InsertTail(DN *pHead);              //2.尾插
DN *InsertMiddle(DN *pHead, int pos);    //3.中间插

//  c.删除节点:1.头删     2.尾删    3.中间删
DN *DeleteHead(DN *pHead);              //1.头插
DN *DeleteTail(DN *pHead);              //2.尾插
DN *DeleteMiddle(DN *pHead, int pos);    //3.中间插

//  d.打印链表:先正序输出,后逆序输出链表[以pNext方向为正,pPrev方向为逆向]
unsigned int DisplayList(DN *pHead);
// (4)双向循环链表:
//  a.创建链表:
//  b.插入节点:1.头插     2.尾插    3.中间插
//  c.删除节点:1.头删     2.尾删    3.中间删
//  d.打印链表:
/*-------------------1.链表----------------------*/
#endif //DATA_STRUCTURE_DNLIST_OPERATION_H

Operation.cpp
#include "Operation.h"
/*-------------------1.链表----------------------*/
// (1)单向链表:
//  a.创建链表:
//  b.插入节点:1.头插     2.尾插    3.中间插
//  c.删除节点:1.头删     2.尾删    3.中间删
//  d.打印链表:

// (2)单向循环链表:
//  a.创建链表:
//  b.插入节点:1.头插     2.尾插    3.中间插
//  c.删除节点:1.头删     2.尾删    3.中间删
//  d.打印链表:

// (3)双向链表:
/*在此注释中以pNext方向为向后;
 * 以pPrev方向为向前。
 * */

//  a.创建链表:
DN *CreateHead() {
    DN *pHead = new DNode();
    pHead->pNext = nullptr;     //节点尾指向空[不循环]
    pHead->pPrev = nullptr;     //节点头指向空[不循环]
    HeadM;                      //设置HeadMark
    cout << "Enter a value of the ListHead[" << pHead->mark << "]:" << endl;
    cin >> pHead->date;         //键入头结点存储数据
    return pHead;
}

//  b.插入节点:1.头插     2.尾插    3.中间插
DN *InsertHead(DN *pHead) {
    int TpMark = pHead->mark;
    char ch = 'y';
    while ('y' == ch || 'Y' == ch) {
        DN *newNode = new DNode();
        //if (nullptr == newNode){perror("内存申请失败");exit(-1);}
        cout << "Enter a value of the node[" << ++TpMark << "]:" << endl;
        newNode->mark = TpMark;      //设置newNode的mark
        cin >> newNode->date;        //键入存储的值
        pHead->pPrev = newNode;      //使原链表的pPrev指针指向newNode[将NewNode插入pHead前,链接好向前的结构]
        newNode->pNext = pHead;      //使NewNode的尾指针pNext指向pHead[将NewNode插入pHead前,链接好向后的结构]
        pHead = newNode;             //将NewNode赋值给pHead[前移指针pHead,保证pHead始终指向链表头]
        cout << "Enter choice inserter(y/n):" << endl;
        cin >> ch;
    }
    //RegularMark(pHead);
    return pHead;
}

DN *InsertTail(DN *pHead) {
    DN *LTail = pHead;
    char ch = 'Y';
    LTail = FindTail(LTail);       //调用FindTail找到尾节点指针,赋给LTail
    int TpMark = LTail->mark;      //记录尾节点Mark
    while (ch == 'y' || ch == 'Y') {
        DN *newNode = new DNode();
        //if (nullptr == newNode){perror("内存申请失败");exit(-1);}
        cout << "Enter a value of the node[" << ++TpMark << "]:" << endl;
        newNode->mark = TpMark;     //写入TpMark到newNode Mark中
        cin >> newNode->date;
        LTail->pNext = newNode;     //将尾节点LTail的pNext指针指向NewNode[链接NewNode到List尾部,链接好向后的结构]
        newNode->pPrev = LTail;     //将新节点newNode的pPrev指针指向LTail[链接NewNode到List尾部,链接好向前的结构]
        LTail = newNode;            //将NewNode赋给LTail[使LTail始终指向尾节点]
        cout << "Enter choice inserter(y/n):" << endl;
        cin >> ch;
    }
    //RegularMark(pHead);
    return pHead;
}

DN *InsertMiddle(DN *pHead, int pos) {
    DN *pNTemp01 = pHead, *pNTemp02 = nullptr;
    pos--;
    char ch = 'y';
    //int count = 0;
    RegularMark(pHead);
    pNTemp01 = FindNode(pNTemp01, pos);    // 找到插入节点的前一个节点pNTemp01
    pNTemp02 = pNTemp01->pNext;         // 找到应该插入的位置节点pNTemp02
    while (ch == 'y' || ch == 'Y') {
        DN *newNode = new DNode();
        //if (nullptr == newNode){perror("内存申请失败");exit(-1);}
        newNode->mark = pNTemp01->mark;
        newNode->mark++;
        cout << "Enter a value of the inserted node:" << endl;
        cin >> newNode->date;
        pNTemp01->pNext = newNode;      // 将前节点pNTemp01的pNext指针指向新节点newNode[链接NewNode到pNTemp01后,链接好向后的结构]
        newNode->pPrev = pNTemp01;      // 将新节点newNode的pPrev指针指向前节点pNTemp01[链接NewNode到pNTemp01后,链接好向前的结构]
        newNode->pNext = pNTemp02;      // 将pNTemp02赋给newNode->pNext[将pNTemp02链接在newNode后]
        pNTemp02->pPrev = newNode;      // 将newNode赋给pNTemp02->pPrev[将newNode链接在pNTemp02前]
        pNTemp01 = pNTemp01->pNext;     // 将pNTemp01后移一位指向newNode
        //while (nullptr != (pNTemp01 = pNTemp01->pNext))++pNTemp01->mark;
        cout << "Enter choice of inserter(y/n):" << endl;
        cin >> ch;
        //count++;
    }
    //while (nullptr != (pNTemp01 = pNTemp01->pNext))pNTemp01->mark += count;
    RegularMark(pHead);
    return pHead;
}

//  c.删除节点:1.头删     2.尾删    3.中间删
DN *DeleteHead(DN *pHead) {
    char ch = 'y';
    DN *pNTemp = pHead;
    while ('y' == ch || 'Y' == ch) {
        pNTemp = pHead;         //暂存pHead
        pHead = pHead->pNext;   //将pHead指向下一个节点
        pHead->pPrev = nullptr; //规避野指针
        cout << "Enter choice of deleting head node (y/n):" << endl;
        cin >> ch;
        if (pHead->pNext!= nullptr ) {      //确保DisplayList函数正常调用
            DisplayList(pHead);
            delete pNTemp;         //释放内存
        } else {
            cout << "Can't remove the last node!" << endl;
            return pHead;
        }
    }
    //RegularMark(pHead);
    return pHead;
}

DN *DeleteTail(DN *pHead) {     //(1)不断循环寻找最后节点删除
    //(1)
    char ch = 'y';
    unsigned int count = 0;
    DN *pNTemp = pHead, *pNTemp01 = nullptr, *pNTemp02 = nullptr;
    RegularMark(pHead);
    while ('y' == ch || 'Y' == ch) {
        count = DisplayList(pNTemp);
        if (pHead->pNext == nullptr) {      //确保内存安全
            cout << "Can't remove the last node!" << endl;
            break;
        }
        pNTemp01 = FindNode(pNTemp, count - 2);     // 找到尾节点的前一个节点
        pNTemp02 = pNTemp01->pNext;                      // 暂存尾节点
        pNTemp01->pNext = nullptr;                       // 使得原尾节点前一个节点变为尾节点
        delete pNTemp02;                                 // 释放原尾节点内存
        pNTemp02 = nullptr;                              // 避免pNTemp02野指针
        cout << "Enter choice of deleting tail node (y/n):" << endl;
        cin >> ch;
    }
    //RegularMark(pHead);
    return pHead;
}

DN *DeleteMiddle(DN *pHead, int pos) {
    DN *pNTemp01 = pHead, *pNTemp02 = nullptr, *pNTemp03 = nullptr;
    pNTemp01 = FindNode(pNTemp01, pos - 2);     // 前节点指针
    pNTemp02 = pNTemp01->pNext;                      // 中结点指针
    pNTemp03 = pNTemp02->pNext;                      // 尾结点指针
    pNTemp01->pNext = pNTemp03;         // 前后链接
    pNTemp03->pPrev = pNTemp01;         // 后前连接
    delete pNTemp02;                    // 删除中间节点
    pNTemp02 = nullptr;                 // 避免pNTemp02野指针
    //RegularMark(pHead);
    return pHead;
}

//  d.打印链表:
unsigned int DisplayList(DN *pHead) {
    DN *LTail = FindTail(pHead);
    unsigned int count = 0;
    cout<<"Forward display list:"<<endl;
    do {
        cout << "NODE[" << pHead->mark << "] = " << pHead->date << endl;
        count++;
    } while ((pHead = pHead->pNext) != nullptr);
    cout<<"\nInverted display list:"<<endl;
    do {
        cout << "NODE[" << LTail->mark << "] = " << LTail->date << endl;
    } while ((LTail = LTail->pPrev) != nullptr);
    return count;
}





main.cpp
#include "Structure.h"
#include "Operation.h"

int main() {
    DN *pHead = nullptr;            //创建单向链表类型的头指针pHead,初始化为空指针nullptr
    pHead = CreateHead();           //创建头结点,pHead存储头结点的位置(存储头结点的位置)
//    pHead = InsertHead(pHead);      //调用头插
    pHead = InsertTail(pHead);      //调用尾插
//    pHead = InsertMiddle(pHead,2);      //调用中间插入
//    pHead = DeleteHead(pHead);               //调用头删
//    pHead = DeleteTail(pHead);               //调用尾删
    pHead = DeleteMiddle(pHead,2);      //调用中间删除[至少三个节点]

    int count = DisplayList(pHead);
    cout << "\nCount of list = " << count << "\nThe tail date of list = " << FindTail(pHead)->date << endl;
    delete pHead;       //释放pHead
    return 0;
}


测试结果如下图1.1.2:
在这里插入图片描述

图1.1.2 测试结果

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值