链表基础知识
大约 5 分钟
链表基础知识
特点:每一个节点都是在堆内存上独立new出来的,节点内存不连续
- 优点
- 内存利用率高,不需要大块连续内存
- 插入和删除节点不需要移动其它节点,时间复杂度O(1)
- 不需要专门进行扩容操作
- 缺点
- 内存占用量大,每一个节点多出存放地址的空间
- 节点内存不连续,无法进行内存随机访问
- 链表搜索效率不高,只能从头节点开始逐节点遍历
单向链表
特点:
每一个节点除了数据域,还有一个next指针域指向下一个节点(存储了下一个节点的地址),但是无法回退到前一个节点
末尾节点的指针域是NULL
单向链表的实现
//LinkedList.h
#ifndef _LINKEDLIST_H_
#define _LINKEDLIST_H_
#include <iostream>
using std::cout;
using std::endl;
class LinkedList {
public:
LinkedList();
~LinkedList();
void push_back(int val);
void pop_back();
void push_front(int val);
void pop_front();
void remove(int val);
void removeAll(int val);
bool find(int val);
void show();
private:
struct Node{
int _data;
Node* _next;
Node(int data = 0, Node* next = nullptr) : _data(data), _next(next) {}
};
Node* head;
};
#endif // _LINKEDLIST_H_
//LinkedList.cc
#include "LinkedList.h"
LinkedList::LinkedList() {
head = new Node();
}
LinkedList::~LinkedList() {
Node* p = head;
while(p != nullptr) {
head = head->_next;
delete p;
p = head;
}
}
void LinkedList::push_back(int val) {
Node* newNode = new Node(val);
Node* p = head;
while(p->_next != nullptr) {
p = p->_next;
}
p->_next = newNode;
}
void LinkedList::pop_back() {
Node* cur = head->_next;
Node* pre = head;
while(cur->_next != nullptr) {
pre = cur;
cur = cur->_next;
}
delete cur;
pre->_next = nullptr;
}
void LinkedList::push_front(int val) {
Node* newNode = new Node(val, head->_next);
head->_next = newNode;
}
void LinkedList::pop_front() {
Node* p = head->_next;
if(p != nullptr) {
head->_next = p->_next;
delete p;
}
}
void LinkedList::remove(int val) {
Node* cur = head->_next;
Node* pre = head;
while(cur != nullptr) {
if(cur->_data == val) {
pre->_next = cur->_next;
delete cur;
return;
}
else {
pre = cur;
cur = cur->_next;
}
}
}
void LinkedList::removeAll(int val) {
Node* cur = head->_next;
Node* pre = head;
while(cur != nullptr) {
if(cur->_data == val) {
pre->_next = cur->_next;
delete cur;
cur = pre->_next;//注意需要重置cur
}
else {
pre = cur;
cur = cur->_next;
}
}
}
bool LinkedList::find(int val) {
Node* cur = head->_next;
while(cur->_next != nullptr) {
if(cur->_data == val) {
return true;
}
else {
cur = cur->_next;
}
}
return false;
}
void LinkedList::show() {
Node* cur = head->_next;
while(cur) {
cout << cur->_data << " ";
cur = cur->_next;
}
cout << endl;
}
//main.cc
#include "LinkedList.h"
#include <iostream>
#include <time.h>
#include <stdlib.h>
using std::cout;
using std::endl;
int main(void)
{
LinkedList list;
srand(time(nullptr));
for(int i = 0; i < 10; ++i) {
list.push_back(rand()%100);
}
list.show();
list.pop_back();
list.pop_front();
list.show();
list.push_front(100);
cout << list.find(100) << endl;
list.remove(100);
cout << list.find(100) << endl;
list.push_back(100);
list.push_back(100);
list.push_back(100);
list.show();
list.removeAll(100);
list.show();
}
$./main
73 69 66 82 7 66 21 6 88 77
69 66 82 7 66 21 6 88
1
0
69 66 82 7 66 21 6 88 100 100 100
69 66 82 7 66 21 6 88
单向循环链表
特点:
- 每一个节点除了数据域,还有一个next指针域指向下一个节点(存储了下一个节点的地址)
- 末尾节点的指针域指向了头节点
单向循环链表的实现
#include <iostream>
using std::cout;
using std::endl;
class CircleList {
public:
CircleList() {
_head = new Node();
_head->_next = _head;
_tail = _head;
}
~CircleList(){
Node* p = _head->_next;
while(p != _head) { //循环链表结束条件
_head = p->_next;
delete p;
p = _head;
}
delete _head; //最后记得释放头结点
}
void insertTail(int val) {
Node* newNode = new Node(val, _head);
_tail->_next = newNode;
_tail = newNode;
}
void insertHead(int val) {
Node* newNode = new Node(val, _head->_next);
_head->_next = newNode;
if(newNode->_next == _head) { //如果只有一个结点的情况
_tail = newNode;
}
}
void remove(int val) {
Node* p = _head;
Node* temp = nullptr;
while (p->_next != _head) {
if(p->_next->_data == val) {
temp = p->_next;
p->_next = p->_next->_next;
delete temp;
if(p->_next == _head) {
_tail = p;
}
return;
}
else{
p = p->_next;
}
}
}
bool find(int val) const {
Node* p = _head->_next;
while(p != _head) {
if(p->_data == val) {
return true;
}
}
return false;
}
void show() {
Node* p = _head->_next;
while(p != _head) {
cout << p->_data << " ";
p = p->_next; //记得重置p
}
cout << endl;
}
private:
struct Node {
int _data;
Node* _next;
Node(int data = 0, Node* next = nullptr) : _data(data), _next(next) {};
};
Node* _head;
Node* _tail;
};
int main(void)
{
CircleList clist;
srand(time(nullptr));
for(int i = 0; i < 10; ++i) {
clist.insertTail(rand()%100);
}
clist.show();
clist.insertHead(200);
cout << clist.find(200) << endl;
clist.remove(200);
clist.show();
}
$./main
98 13 13 33 99 6 2 35 15 18
1
98 13 13 33 99 6 2 35 15 18
双向链表
特点:
- 每一个节点除了数据域,还有next指针域指向下一个节点,pre指针域指向前一个节点
- 头节点的pre是NULL,末尾节点的next是NULL
基础版
#include <iostream>
using std::cout;
using std::endl;
class DoubleLink{
public:
DoubleLink() {
_head = new Node();
}
~DoubleLink() {
Node* p = _head;
while(p->_next) {
_head = p->_next;
delete p;
p = _head;
}
_head = nullptr;
}
void insertTail(int val) {
Node* newNode = new Node(val);
Node* p = _head;
while(p->_next) {
p = p->_next;
}
p->_next = newNode;
newNode->_pre = p;
}
void insertHead(int val) {
Node* newNode = new Node(val);
newNode->_next = _head->_next;
newNode->_pre = _head;
if(_head->_next != nullptr) {
_head->_next->_pre = newNode;
}
_head->_next = newNode;
}
void remove(int val) {
Node* p = _head;
while(p) {
if(p->_data == val) {
p->_pre->_next = p->_next;
if(p->_next) { //注意加一个判断
p->_next->_pre = p->_pre;
}
delete p;
return;
}
p = p->_next;
}
}
bool find(int val) {
Node* p = _head->_next; //有头结点的注意一下
while(p) {
if(p->_data == val) {
return true;
}
p = p->_next;
}
return false;
}
void show() {
Node* p = _head->_next;
while(p) {
cout << p->_data << " ";
p = p->_next;
}
cout << endl;
}
private:
struct Node {
int _data;
Node* _pre;
Node* _next;
Node(int data = 0, Node* pre = nullptr, Node* next = nullptr) : _data(data), _pre(pre), _next(next) {}
};
Node* _head;
};
int main(void)
{
DoubleLink dlink;
dlink.insertHead(100);
dlink.insertHead(10);
dlink.insertHead(20);
dlink.insertTail(30);
dlink.insertTail(40);
dlink.show();
dlink.remove(100);
dlink.remove(20);
dlink.remove(40);
dlink.show();
cout << dlink.find(10);
}
升级版
双向循环链表
特点:
- 每一个节点除了数据域,还有next指针域指向下一个节点,pre指针域指向前一个节点
- 头节点的pre指向末尾节点,末尾节点的next指向头节点