链表作为一种线性数据结构,跟数组的区别在于数组是连续的存储单元,而链表是离散的。链表使用节点来对数据进行存储,一个数据存储在一个节点中,节点之间通过指针相连,所以一个节点分为数据域(data)和指针域(next),数据域负责存储用户数据,指针域负责链接节点。
Ⅰ:单链表
每个结点除了存储数据data外,还需要记录下个结点的地址,称为后继指针next。
Ⅱ:双向链表
每个结点除了存储数据data外,还会会记录上一个结点和下一个结点的地址。
Ⅲ:循环列表
循环链表的尾结点不指向空,而是指向头结点,类似一个环形结构。
Ⅰ:由于链表的内存空间是零散的,所以不支持随机访问。
Ⅱ:插入、删除不需要移动数据,所以效率高。
Ⅲ:因为链表的每个内存块都不是连续的,所以不需要提前计算内存的大小,内存空间可以根据结点数量的改变而改变。
Ⅰ:便于首元结点的处理
首元结点的地址保存在头结点的指针域中,所以在链表的第一个位置上的操作和其他位置一致,无须进行特殊处理。
Ⅱ:便于空表和非空表的统一处理
无论链表是否为空,头指针都是指向头结点的非空指针,因此空表和非空表的处理也就统一了。
1.利用c++实现单向链表,首先构建节点类:
#include
#include
using namespace std;
//构建节点类
class NODE {
public:
NODE* pnext;
int data;
};
2.然后构建链表类,链表类应该继承节点的属性,并添加头节点和链表长度属性:
class LINK : public NODE{
public:
//初始化头节点和链表长度
LINK() {
head = new NODE();
len = 0;
};
//对堆区的数据进行释放,释放时需要创建两个临时节点保存数据,否则前一个释放以后找不到后一个节点
~LINK() {
NODE* t = head;
NODE* r = head->pnext;
if (r != nullptr) {
delete t;
t = r;
r = r->pnext;
}
delete t;
};
//定义头节点和链表长度
private:
NODE* head;
int len;
};
3.在类中实现链表判空(empty)、尾插(append)、中间插入(insert)、删除(del)、降序排列(sort)、求链表长度(length)、遍历输出(travel)等算法
判空:只需判定len属性是否为0
bool empty() {
if (len == 0) return true;
else return false;
}
尾插:
void append() {
NODE* t = head; //始终指向尾节点,用于修改每个节点的指针域,初始化指向头节点
NODE* r; //新增的数据节点
int n;
cout << "请输入整型数据,以空格分开,以0结尾:";
while (cin >> n, n != 0) {
r = new NODE();
r->data = n;
t->pnext = r;
t = r;
len++;
}
t->pnext = nullptr;
};
中间插入(insert):
void insert(int val, int pos) {
NODE* r; NODE* s; NODE* t;
s = head->pnext;
for (int i = 0; i < pos-1; i++) {
s = s->pnext;
}
r = new NODE();
r->data = val;
t = s->pnext;
s->pnext = r;
r->pnext = t;
len++;
return;
};
删除(del)(删除pos位置处节点):
void del(int pos) {
NODE* r; NODE* s; NODE* t;
s = head->pnext;
for (int i = 0; i < pos - 1; i++) {
s = s->pnext;
}
t = s->pnext->pnext;
delete s->pnext;
s->pnext = t;
len--;
return;
};
链表降序排列:
void sort() {
NODE* r; NODE* s;
r = head->pnext;
for (r; r->pnext!=nullptr;r=r->pnext) {
//错误写法:for (s=r->pnext; s->pnext!= nullptr; s=s->pnext)
//上述写法无法对尾节点操作,因为直接判定尾节点为空就不进入循环了
for (s=r->pnext; s!= nullptr; s=s->pnext) {
if (r->data < s->data) {
int x = r->data;
r->data = s->data;
s->data = x;
}
}
}
return;
};
求链表长度:
void length() {
cout << "链表长度为:" << len << endl;
return;
};
遍历输出链表:
void travel() {
NODE* p = head->pnext;
//错误写法:while (p->next!=nullptr) 会导致输出不了最后一个节点
//上述写法表示如果这个节点指针域不为空,就打印此节点的数据域,然而最后一个节点指针域是空的
while (p != nullptr) {
cout << p->data << " ";
p = p->pnext;
}
cout << endl;
return;
};
4.测试:
void test() {
LINK link;
link.append();
cout << "尾插法初始化链表为: ";
link.travel();
link.insert(10, 2);
cout << "插入后链表为: ";
link.travel();
link.del(3);
cout << "删除后链表为: ";
link.travel();
link.sort();
cout << "降序排列后链表为: ";
link.travel();
link.length();
return;
}
5.效果:
#include
#include
using namespace std;
class NODE {
public:
NODE* pnext;
int data;
};
class LINK : public NODE{
public:
//初始化头节点和链表长度
LINK() {
head = new NODE();
len = 0;
};
//对堆区的数据进行释放,释放时需要创建两个临时节点保存数据,否则前一个释放以后找不到后一个节点
~LINK() {
NODE* t = head;
NODE* r = head->pnext;
if (r != nullptr) {
delete t;
t = r;
r = r->pnext;
}
delete t;
};
//判空
bool empty() {
if (len == 0) return true;
else return false;
}
//尾插法添加节点,也需要创建两个临时节点
void append() {
NODE* t = head; //始终指向尾节点,用于修改每个节点的指针域,初始化指向头节点
NODE* r; //新增的数据节点
int n;
cout << "请输入整型数据,以空格分开,以0结尾:";
while (cin >> n, n != 0) {
r = new NODE();
r->data = n;
t->pnext = r;
t = r;
len++;
}
t->pnext = nullptr;
};
//链表插入(在pos位置前插入)
void insert(int val, int pos) {
NODE* r; NODE* s; NODE* t;
s = head->pnext;
for (int i = 0; i < pos-1; i++) {
s = s->pnext;
}
r = new NODE();
r->data = val;
t = s->pnext;
s->pnext = r;
r->pnext = t;
len++;
return;
};
//链表删除(删除pos位置处节点)
void del(int pos) {
NODE* r; NODE* s; NODE* t;
s = head->pnext;
for (int i = 0; i < pos - 1; i++) {
s = s->pnext;
}
t = s->pnext->pnext;
delete s->pnext;
s->pnext = t;
len--;
return;
};
//链表排序
void sort() {
NODE* r; NODE* s;
r = head->pnext;
for (r; r->pnext!=nullptr;r=r->pnext) {
//错误写法:for (s=r->pnext; s->pnext!= nullptr; s=s->pnext)
//上述写法无法对尾节点操作,因为直接判定尾节点为空就不进入循环了
for (s=r->pnext; s!= nullptr; s=s->pnext) {
if (r->data < s->data) {
int x = r->data;
r->data = s->data;
s->data = x;
}
}
}
return;
};
void travel() {
NODE* p = head->pnext;
//错误写法:while (p->next!=nullptr) 会导致输出不了最后一个节点
//上述写法表示如果这个节点指针域不为空,就打印此节点的数据域,然而最后一个节点指针域是空的
while (p != nullptr) {
cout << p->data << " ";
p = p->pnext;
}
cout << endl;
return;
};
void length() {
cout << "链表长度为:" << len << endl;
return;
};
private:
NODE* head;
int len;
};
void test() {
LINK link;
link.append();
cout << "尾插法初始化链表为: ";
link.travel();
link.insert(10, 2);
cout << "插入后链表为: ";
link.travel();
link.del(3);
cout << "删除后链表为: ";
link.travel();
link.sort();
cout << "降序排列后链表为: ";
link.travel();
link.length();
return;
}
void main() {
test();
return;
}