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;
}