概念
单链表由结点(Node)组成,结点中有一个数据域和一个指针域,数据域存放结点中的数据的数值,指针域存放指向下一个结点的地址,我们所实现的单链表是带有头结点的单链表,头节点与其他结点不同,头结点中的数据域赋值为0或单链表长度等特性信息。
链式存储线性表时,不需要使用地址的连续存储单元,即不要求逻辑上相邻元素在物理地址上也相邻,它通过“链”建立起数据元素之间的逻辑关系,因此插入和删除元素操作不需要移动元素,而只需要修改指针,但也会失去顺序表可随机存取的优点。
C++代码实现
所实现的单链表为带有头结点的单链表,索引从0开始,存储的值为正值。
创建List.h的头文件,定义单链表结构体以及可实现操作的函数声明。代码如下:
#pragma once//防止重复包含头文件
#include <iostream>
using namespace std;
class LinkList {
private:
class Node {
public:
int data = 0;
Node *next = nullptr;
};
Node *head;
int length = 0;
public:
LinkList();//构造函数
void create(int n);//初始化n个结点的单链表
void print();//打印单链表中的数据
int getLength();//获取单链表的长度
void isEmpty();//判断此单链表是否为空
//默认index从0开始
int search(int index);//查找索引为index的数值并返回
int find(int elem);//查找数值为elem的索引并返回
void insertByIndex(int index, int data);//在索引为index之前的位置插入数值为data的结点
void deleteByIndex(int index);//删除索引为index的结点
};
创建List.cpp的源文件,编写头文件函数的具体实现:
#include "List.h"
//LinkList::表示LinkList类作用域下
LinkList::LinkList() {
head = new Node;
head->data = 0;
head->next = nullptr;
int length = 0;
}
int LinkList::getLength(){
cout << "链表中的元素个数为:" << length << endl;
return length;
}
void LinkList::create(int n) {
Node *p = head;
length = n;
for (int i = 0;i < n;i++)
{
cout << "请输入第" << i << "个结点的数据:";
Node *cur = new Node;
cin >> cur->data;
p->next = cur;
p = cur;
cur->next = nullptr;
i++;
}
}
void LinkList::print(){
Node *cur = head->next;
if (!length) {
cout << "链表为空!" << endl;
return;
}
cout << "链表中的元素为:" << endl;
while (cur) {
cout << cur->data << " ";
cur = cur->next;
}
cout << endl;
}
void LinkList::isEmpty(){
if (!length) {
cout << "链表为空~" << endl;
return;
}
cout << "链表不为空" << ",";
this->getLength();
}
int LinkList::search(int index){ //认为索引从0开始
if (index < 0 && index >= length)
cout << "索引输入错误!" << endl;
else {
Node *temp = head->next;
int i = 0;
while (temp) {
if (i == index) {
cout << "链表中索引为" << index << "的数据为" << temp->data << endl;
return temp->data;
}
else {
i++;
temp = temp->next;
}
}
}
return -1;//假定链表中不存入负数
}
int LinkList::find(int elem){
Node *cur = head->next;
int index = 0;
while (cur) {
if (cur->data == elem)
return index;
else {
cur = cur->next;
index++;
}
}
cout << elem << "不在链表中!" << endl;
return -1;
}
void LinkList::insertByIndex(int index, int data) { //在index之前插入数据为data的结点
if (index < 0 && index >= length) {
cout << "索引位置不合法" << endl;
}
else {
Node *cur = head;
int pos = 0;
while (pos != index) {
cur = cur->next;
pos++;
}
Node *temp = new Node;
temp->data = data;
temp->next = cur->next;
cur->next = temp;
length++;
cout << data << "插入成功!" << endl;
}
}
void LinkList::deleteByIndex(int index) {
if (index < 0 && index >= length) {
cout << "索引输入错误!" << endl;
}
else {
Node *cur = head;
int pos = 0;
while (pos != index) {
cur = cur->next;
pos++;
}
Node *temp = cur->next;
cur->next = temp->next;
delete temp;
length--;
cout << "删除成功!" << endl;
}
}
创建单链表.cpp的源文件,测试所写的单链表各功能:
#include "List.h"
#include <iostream>
using namespace std;
void test() {
LinkList list;//创建链表结构体对象
cout << "请输入链表中结点的个数:";
int n;
cin >> n;
list.create(n);
list.print();
list.getLength();
list.isEmpty();
cout << "请输入需要查询的索引位置:";
int index;
cin >> index;
list.search(index);
cout << "请输入需要查询的数据:";
int elem;
cin >> elem;
if (list.find(elem) != -1) {
cout << elem << "在链表中的索引位置是" << list.find(elem) << endl;
}
cout << "请输入需要插入结点的位置索引和数值:";
int pos, data;
cin >> pos >> data;
list.insertByIndex(pos, data);
list.print();
list.getLength();
cout << "请输入需要删除的结点索引:";
int deleteIndex;
cin >> deleteIndex;
list.deleteByIndex(deleteIndex);
list.print();
list.getLength();
}
int main() {
test();
system("pause");
return 0;
}
核心代码详解
void LinkList::create(int n) {
Node *p = head;
length = n;
for (int i = 0;i < n;i++)
{
cout << "请输入第" << i << "个结点的数据:";
Node *cur = new Node;
cin >> cur->data;
p->next = cur;
p = cur;
cur->next = nullptr;
i++;
}
}
上述代码为创建一个长度为n的链表的函数,传参n赋值给链表长度变量length。
1、创建节点指针变量p,并将头结点head的地址赋值给p,此时p与head指向同一块内存空间,对p操作就是对head操作;
2、for循环,依次录入节点;
3、new Node在堆区创建节点指针cur;
4、读入数值并赋值给cur->data;
5、将p的next指针指向cur的地址,此时对p操作了,也即head的next指针也指向cur的地址;
6、将cur的地址赋值给p节点,此时因为head创建在堆区,其地址并不会改变,此时p与cur指向同一块内存空间,对p操作就是对cur操作;
7、cur节点的next指针指向空
8、i++进入下一个输入节点入链
9、 new Node在堆区创建节点指针cur;(这里要理解这次创建的cur与上一次创建的cur地址不同,你可以理解为上一次创建的是cur1,这一次是cur2);
10、读入数值并赋值给cur2->data;
11、将p的next指针指向cur2的地址,此时对p操作了,也即cur1的next指针也指向cur2的地址;
12、将cur2的地址赋值给p节点,此时因为cur1创建在堆区,其地址并不会改变,此时p与cur2指向同一块内存空间,对p操作就是对cur2操作;
.
.
.
相信这两次循环你便能理解这个函数的具体实现过程,理解了这个函数的实现过程,再去看单链表的完整代码,理解起来定然事半功倍。