2021-02-18

C++语言实现双向链表

与单向链表区别
双向链表与单向链表唯一的区别是prev指针指向前一个节点,相对于单向链表,双向链表可以访问指定节点的前驱,也可以逆向遍历链表。
算法实现
头文件:

#pragma once

#include<iostream>
using namespace std;

using DataType_t = int;

typedef struct ListNode {
    
    
	DataType_t data;
	ListNode* prev;
	ListNode* next;
}Node;
using DbLinkList = Node*;

//清空双向链表[时间复杂度o(n)]:
void clearLink(DbLinkList& head);
//向双向链表头部插入元素[时间复杂度o(1)]:
bool push_front(DbLinkList& head, const DataType_t& data);
//擦除双向链表的首元素[时间复杂度o(1)]:
bool pop_front(DbLinkList& head);
//向双链表的尾部插入元素[时间复杂度o(n)]:
bool push_back(DbLinkList& head, const DataType_t& data);
//擦除双向链表尾部元素[时间复杂度o(n)]:
bool pop_back(DbLinkList& head);
//向指定位置插入指定元素[时间复杂度o(n)]:
bool insert(DbLinkList& head, const unsigned& pos, const DataType_t& data);
//擦除指定位置的元素[时间复杂度o(n)]:
bool erase(DbLinkList& head, const unsigned& pos);
//在双向链表中查找指定元素并返回其位置,未找到则返回-1[时间复杂度o(n)]:
int find(const DbLinkList& head, const DataType_t& data);
//获取指定位置节点的地址[时间复杂度o(n)]:
const Node* getAddr(const DbLinkList& head, const unsigned& pos);
//显示双向链表中的元素[时间复杂的o(n)]:
void display(const DbLinkList& head);

CPP文件:

#include "DoubleLinkList.h"

void clearLink(DbLinkList& head)
{
    
    
    const Node* tmp1 = head, * tmp2 = nullptr;
    while (tmp1) {
    
    
        tmp2 = tmp1;
        tmp1 = tmp1->next;
        delete tmp2;
    }
    head = nullptr;
}

bool push_front(DbLinkList& head, const DataType_t& data)
{
    
       //若链表为空。
    if (!head) {
    
    
        head = new Node;
        if (!head) return false;
        head->data = data;
        head->prev = nullptr;
        head->next = nullptr;
    }
    else {
    
    
        //用首节点的prev指针生成一个新的节点。
        head->prev = new Node;
        if (!head->prev) return false;
        //让新节点的next指针指向原来的首节点。
        head->prev->next = head;
        //让头指针指向新的首节点。
        head = head->prev;
        //将新首节点的prev指针置0。
        head->prev = nullptr;
        //新首节点的data赋值。
        head->data = data;
    }
    return true;
}

bool pop_front(DbLinkList& head)
{
    
    //空链表无法执行擦除操作。
    if (!head) return false;
    Node* tmp = head;
    //让头指针指向第二个元素。
    head = head->next;
    //若第二个节点存在。
    if (head) {
    
    
        head->prev = nullptr;
    }
    delete tmp;
    tmp = nullptr;
    return true;
}

bool push_back(DbLinkList& head, const DataType_t& data)
{
    
       //若链表为空。
    if (!head) {
    
    
        head = new Node;
        if (!head) return false;
        head->data = data;
        head->prev = nullptr;
        head->next = nullptr;
    }
    else {
    
    
        Node* tmp = head;
        //通过检测节点的next指针是否为0,判断tmp是否指向尾节点。
        while (tmp->next) {
    
    
            tmp = tmp->next;
        }
        tmp->next = new Node;
        if (!tmp->next) return false;
        tmp->next->data = data;
        tmp->next->prev = tmp;
        tmp->next->next = nullptr;
    }
    return false;
}

bool pop_back(DbLinkList& head)
{
    
    
    if (!head) return false;
    Node* tmp = head;
    //当tmp节点的next指向了一个节点时,tmp后移。
    while (tmp->next) {
    
    
        tmp = tmp->next;
    }
    //循环结束后,tmp指向了尾元素。
    //若tmp前面还有节点:
    if (tmp->prev) {
    
    
        //通过尾元素的prev指针修改倒数第二个元素的next指针。
        tmp->prev->next = nullptr;
    }//若tmp前面没有节点,即链表中只有一个元素。
    else {
    
    
        head = tmp->next;  //即head=nullptr。
    }
    delete tmp;
    return true;
}

bool insert(DbLinkList& head, const unsigned& pos, const DataType_t& data)
{
    
       //若指定插入位置为0,直接调用push_front函数处理。
    if (!pos) return push_front(head, data);  
    //若指定插入位置不为0且链表为空,无法执行插入操作。
    if (!head) return false;
    //两个临时指针,tmp1指向插入位置之前的节点,tmp2指向插入位置处的原有节点。
    Node* tmp1 = head, * tmp2 = nullptr;
    for (unsigned index = 0; index != pos - 1; ++index) {
    
    
        //若tmp1还未指向插入位置前的节点就到达了尾节点,说明插入
        //位置pos大于链表长度,无法执行插入操作!
        if (!tmp1->next) return false;  
        tmp1 = tmp1->next;
    }
    //循环结束时,tmp1指向了pos-1位置,此时让tmp2指向pos位置。
    tmp2 = tmp1->next;
    tmp1->next = new Node;
    if (!tmp1->next) return false;
    tmp1->next->data = data;
    tmp1->next->prev = tmp1;
    tmp1->next->next = tmp2;
    return true;
}

bool erase(DbLinkList& head, const unsigned& pos)
{
    
    
    if (!head) return false;  //若head为空链表。
    if (!pos) return pop_front(head);  //若指定擦除位置为0。
    //两个临时指针,tmp1指向删除位置之前的节点,tmp2指向需要删除的节点。
    Node* tmp1 = head, * tmp2 = nullptr;
    for (unsigned index = 0; index != pos - 1; ++index) {
    
    
        //若tmp1还未指向插入位置前的节点就到达了尾节点,说明插入
        //位置pos大于链表长度,无法执行插入操作!
        if (!tmp1->next) return false;
        tmp1 = tmp1->next;
    }
    tmp2 = tmp1->next;
    //若tmp2为空指针,即指定擦除位置为尾元素之后的位置,无法指向擦除操作。
    if (!tmp2) return false;
    //让pos-1节点的next指针指向pos之后位置的节点。
    tmp1->next = tmp2->next;
    //若pos之后还有节点,即擦除的不是尾元素。
    if (tmp2->next) {
    
    
        tmp2->next->prev = tmp1;
    }
    delete tmp2;
    return true;
}

int find(const DbLinkList& head, const DataType_t& data)
{
    
     
    const Node* tmp = head;
    int pos = 0;
    while (tmp) {
    
    
        if (tmp->data == data) return pos;
        tmp = tmp->next;
        ++pos;
    }
    return -1;
}

const Node* getAddr(const DbLinkList& head, const unsigned& pos)
{
    
    
    const Node* tmp = head;
    unsigned index = 0;  //index指示tmp指针目前指向的节点位置。
    //让tmp指针对链表从头遍历到尾(即tmp为null),若遍历到了指定位置pos,则
    //跳出循环。
    while (tmp) {
    
    
        if (index == pos) break;
        tmp = tmp->next;
        ++index;
    }
    return tmp;  //若遍历完整个链表未找到pos位置,则tmp必为null。
}

void display(const DbLinkList& head)
{
    
    
    const Node* tmp = head;
    int count = 0;
    while (tmp) {
    
    
        ++count;  //若tmp非空,说明tmp指向了一个节点,则计数器加一。
        cout << tmp->data << " ";
        tmp = tmp->next;
    }
    cout << "\n元素个数:" << count << endl;
}

猜你喜欢

转载自blog.csdn.net/weixin_48343353/article/details/113844066