C++语言实现单链表
单链表的优缺点
单链表的优点:
- 进行插入与删除操作只需要移动一个指针,相比顺序表更加方便快捷。
- 没有空间限制,可以自由伸缩(只要内存充足),可以按需随时分配内存。
单链表的缺点:
- 需要占用额外的存储空间用来存放指针。
- 访问元素需要指针从头开始遍历,访问速度较慢。
算法实现:
头文件:
#ifndef _LINK_LIST_H_
#define _LINK_LIST_H_
#define ELEMENT_TYPE int
typedef struct ListNode{
ELEMENT_TYPE ele;
struct ListNode* next;
}Node;
typedef Node* LinkList_t;
//向单链表头部插入元素[时间复杂的o(1)]:
bool push_front(LinkList_t& head, const ELEMENT_TYPE& e);
//擦除单链表头部的元素[时间复杂的o(1)]:
bool pop_front(LinkList_t& head);
//向单链表尾部插入元素[时间复杂度o(n)]:
bool push_back(LinkList_t& head, const ELEMENT_TYPE& e);
//擦除单链表尾部的元素[时间复杂的o(n)]:
bool pop_back(LinkList_t& head);
//向指定位置插入元素[时间复杂度o(n)]:
bool insert(LinkList_t& head, const unsigned& pos, const ELEMENT_TYPE& e);
//擦除指定位置的元素[时间复杂度o(n)]:
bool erase(LinkList_t& head, const unsigned& pos);
//在单链表中查找指定元素并返回其位置,未找到则返回-1[时间复杂度o(n)]:
int find(const LinkList_t& head, const ELEMENT_TYPE& e);
//获取指定位置节点的地址[时间复杂度o(n)]:
const Node* getAddr(const LinkList_t& head, const unsigned& pos);
//显示单链表中的元素:
void display(const LinkList_t& head);
#endif // !_LINK_LIST_H_
CPP文件:
#include "LinkList.h"
#include<iostream>
using namespace std;
bool push_front(LinkList_t& head, const ELEMENT_TYPE& e)
{
//创建一个临时指针保存头指针中存放的地址,方便新插入元素
//的next指针指向该地址而成为链表中的第一个元素。
Node* const tmp = head; //tmp作用是临时存放原首节点的地址。
head = new Node;
if (!head) return false; //防止内存分配出错。
head->ele = e;
head->next = tmp;
return true;
}
bool pop_front(LinkList_t& head)
{ //链表为空,无法执行擦除操作。
if (!head) return false;
//链表不为空时,释放头部节点的内存:
Node* tmp = head;
head = head->next;
delete tmp;
return true;
}
bool push_back(LinkList_t& head, const ELEMENT_TYPE& e)
{ //当链表为空:
if (!head) {
head = new Node;
if (!head) return false; //防止内存分配出错。
head->ele = e;
head->next = nullptr;
}
else { //当链表不为空:
Node* tmp = head;
//当tmp指向的不是尾元素时,tmp循环指向下一个元素
while (tmp->next) {
tmp = tmp->next;
}//当tmp指向了尾元素,让尾元素的指针域指向一个新节点
tmp->next = new Node;
if (!tmp) return false; //防止内存分配出错。
tmp->next->ele = e;
tmp->next->next = nullptr;
}
return true;
}
bool pop_back(LinkList_t& head)
{ //链表为空,无法执行擦除操作。
if (!head) return false;
//链表不为空时,释放尾部节点的内存,有两种情况:
Node* tmp = head;
//1.若链表中只有一个元素:
if (!tmp->next) {
delete tmp;
head = nullptr;
return true;
}
//2.若链表中超过一个元素:循环迭代,直到tmp指向倒数第二个元素。
while (tmp->next->next) {
tmp = tmp->next;
}//当tmp指向了倒数第二个元素,利用倒数第二元素的指针
//域释放尾元素内存。
delete tmp->next;
//让倒数第二元素的指针域为null,变成尾元素。
tmp->next = nullptr;
return true;
}
bool insert(LinkList_t& head, const unsigned& pos, const ELEMENT_TYPE& e)
{
if (!pos) return push_front(head, e); //若指定插入位置为0。
if (!head) return false; //若head为空链表且指定插入位置不为0。
//两个临时指针,tmp1指向插入位置之前的节点,tmp2指向插入位置处的原有节点。
Node* tmp1 = head, *tmp2 = nullptr;
//tmp1初始指向0号节点,i初始值为0,每次i加1,tmp1指针后移一位,这样i的值
//刚好等于tmp1指向的位置。循环移动tmp1指针pos-1次,使tmp1指向第pos-1个节点。
for (unsigned i = 0; i != pos - 1; ++i) {
//若tmp1还未到达pos-1位置处,其指针域就为NULL,说明插入
//位置pos大于链表长度,无法执行插入操作!
if (!tmp1->next) return false;
//否则,tmp1指针后移。
tmp1 = tmp1->next;
}
//循环结束时,tmp1指向了pos-1位置,此时让tmp2指向pos位置。
tmp2 = tmp1->next;
//让pos-1位置的节点的指针域指向一个新节点,让新节点的指针域指向原链表pos位
//置处的节点:
tmp1->next = new Node;
if (!tmp1) return false;
tmp1->next->ele = e;
tmp1->next->next = tmp2;
return true;
}
bool erase(LinkList_t& head, const unsigned& pos)
{
if (!head) return 0; //若head为空链表。
if (!pos) return pop_front(head); //若指定擦除位置为0。
//两个临时指针,tmp1指向删除位置之前的节点,tmp2指向需要删除的节点。
Node* tmp1 = head, * tmp2 = nullptr;
//tmp1初始指向0号节点,i初始值为0,每次i加1,tmp1指针后移一位,这样i的值
//刚好等于tmp1指向的位置。循环移动tmp1指针pos-1次,使tmp1指向第pos-1个节点。
for (unsigned i = 0; i != pos - 1; ++i) {
//若tmp1还未到达pos-1位置处,其指针域就为NULL,说明指定擦除位置pos大于
//链表长度,无法执行擦除操作!
if (!tmp1->next) return false;
//否则,tmp1指针后移。
tmp1 = tmp1->next;
}
//循环结束时,tmp1指向了pos-1位置。注意,此时与insert不同的是,链表尾节点
//的后一个位置能插入节点却无法删除节点,因此先判断此时pos-1位置是否是最后
//一个节点,若是,说明pos位置无法执行擦除操作。
if (!tmp1->next) return false;
//否则,让tmp2指向pos位置。
tmp2 = tmp1->next;
//让pos-1位置的节点的指针域指向pos后面那个节点,并释放pos处的节点内存。
tmp1->next = tmp2->next;
delete tmp2;
return true;
}
int find(const LinkList_t& head, const ELEMENT_TYPE& e)
{
const Node* tmp = head;
int pos = 0;
while (tmp) {
//让tmp指针遍历链表,若tmp指向了一个节点(不为null)且该节点的数据域与e相等,则
//返回此时tmp指针所指节点的位置pos。
if (tmp->ele == e) return pos;
tmp = tmp->next;
++pos;
}
return -1; //tmp指向了null(链表尾)也未找到指定元素。
}
const Node* getAddr(const LinkList_t& 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 LinkList_t& head)
{
const Node* tmp = head;
int count = 0;
while (tmp) {
cout << tmp->ele << " ";
tmp = tmp->next;
++count;
}
cout << "\n元素个数:" << count << endl;
}