本篇主要专注与于解析操作代码的各部分意义,包含链表的增删改查算法实现
下面进入正题:
通过创建结构体实现链表,结构体包含节点的主要信息,包括但不限于节点数据域、节点指针域,下面是一段节点定义代码,定义了一个节点结构体。为方便理解,尽可能地简化了语法并在栈区创建节点对象及相关来链表元素,链表实例化对象及要素写在全局区中来调用,代码仅仅表现大体逻辑框架,接近伪代码
#include <iostream>
using namespace std;
struct Node {
Node* next;//指针域
int data;//数据域
};
Node* head; Node* tail;
int curlength;
void CreateList() {//创建一个只有头节点的链表
Node* p = new Node();
p->data = 0;
p->next = NULL;
head = p;
tail = p;
cout << "已创建新表" << endl;
}
void tailinsert() {//尾插法
Node* tmp = new Node();
cout << "输入新节点data: ";
cin >> tmp->data;//给新节点的数据赋值
tmp->next = NULL;//给新节点的指针赋值
tail->next = tmp;//尾指针的节点next指向新节点
tail = tmp;//尾指针迁移
}
以上两段代码负责创建链表和尾端插入元素
bool empty(SeqStack &S) {//判断栈指针是-1,如果是就是空栈
return S.top == -1;
}
用于检测是否空栈的函数
Clear()——链表清除函数
这个清除的思路是指针迭代,指针p和指针tmp的任务各不相同,tmp用于删除p经过的节点,p移动至下一节点后,tmp将后一个节点删除,然后tmp又指向p所指的节点,p再次向下一节点移动,上述操作重复,直到p指向NULL,此时链表只剩头节点,让tail指向head所指,然后头节点的指针指向NULL,任务就结束了
void Clear() {
Node* p, * tmp;//两个临时指针,tmp用于删节点
p = head->next;//p指向头节点下一位节点
while(p != NULL) {//tmp和p通过反复迭代将节点全部删除,最后只剩头节点
tmp = p;//tmp也指向p指向的节点
p = p->next;//p移动到下一个节点
delete tmp;//用tmp将指向节点删除
}
head->next = NULL;//头节点next指针指向NULL
tail = head;//tail指向head的箭头指向
curlength = 0;
}
Traverse()——链表遍历函数
用一个指针p遍历链表,p!=NULL的意义是,当p指向空的时候,while不再执行,这意味着之前经过的所有节点都被访问了一遍数据域,算法符合任务目标
void Traverse(){
if (empty()) {
cout << "链表为空,无数据" << endl;
return;
}
Node* p = head->next;//p指向1号节点
cout << "result: ";
while (p != NULL) {//遍历所有节点并打印数据
cout <<"此链表数据为: " << p->data << " ";
p = p->next;
}
cout << endl;
}
Getposition()——序号查找函数
这个算法负责获得节点序号为参数i的节点首地址,核心语句是while循环,新建一个变量count用于计数和条件判断,初始化为0,则参数是几那么就执行循环几次,循环结束后的p指向的便是符合参数i的节点序号的节点。
Node* Getposition(int i){
if ( i < -1 || i > curlength - 1)//叛定参数是否合法
return NULL;
Node* p = head;//p指向head所指
int count = 0;
while (count < i) {//当count递增至i的值时,函数返回p指向的地址(或者说返回p指针)
p = p->next;
count++;
}
return p;
}
Search()——数据查找函数
算法通过接收一个参数值,用指针p遍历链表查找这个值在哪个节点,最后返回这个节点的序号。核心代码还是while循环,判断条件的意义是,指针p没有到达链表尾,也没有找到参数对应的节点,符合条件则继续遍历链表;若指针p指向NULL则是未找到数据,否则便返回那个节点的序号
int Search(int value) {
Node* p = head->next;
int count = 0;
while(p != NULL && p->data != value) {//当p既不指向NULL,p指向的节点data也不是要搜索的参数
p = p->next;//p移动至下一个节点
count++;//计数+1
}
if(p == NULL)
return - 1;
else
return count;//返回计数值
}
Insert()——元素插入函数
算法接收两个参数:插入元素的位数、插入元素的数据;老生重谈,涉及链表序号的操作,参数需要做合法性判断,p用于指向目标位数的前一位节点,q指向定义的新节点,然后将参数数据值赋到q指向节点的数据域里,然后q指向的节点的指针域指向q所指节点的next指针所指,再将p指向的节点的next指针指向q所指。
Caution!:核心代码吗中p与q的关系,p指向的节点就是q指向节点的前一个节点
int Insert(int i,int value) {
Node* p, * q;
if (i < 0 || i > curlength)//判定参数值在合法范围内
return 0;
p = Getposition(i);
q = new Node();
q->data = value; //赋值到新节点
q->next = p->next//q指向节点的next之指针指向前一个节点的next所指
p->next = q;//p指向的节点的next指针指向q指针所指
if (p == tail)
tail = q;
curlength++;//链表长度+1
}
Delete()——元素删除函数()
依然是做个参数合法性判断后,才开始执行删除任务。需要两个指针,一个指针用于引导,一个指针用于删除;首先pre指针指向目标节点系数,p指向pre指向节点的下一位节点,此时进入一个判断,若p指向与tail指向相同,则tail指向pre所指,然后pre指向节点的指针域指向NULL,再删除p指向的对象。不然就让pre指向节点的next指针去指向p指向节点的next所指,然后删除p指向的对象
int Delete(int i) {
Node* pre, * p;
if (i < 0 || i > curlength)//判定参数值在合法范围内
return 0;
pre = Getposition(i);
p = pre->next;
if(p == tail) {
tail = pre;
pre->next = NULL;
delete p;
}
else{
pre->next = p->next;
delete p;
}
curlength--;
Inverse()——链表反转函数 (我自认为这个算法是最麻烦的)
本算法使用两个指针进行迭代,逐步将链表顺序反转。首先p指向头节点的下一个节点,让头节点的next指向空,下面进入一个判断:若p不指向空,则tail指向p所指对象,此时1号节点变成最后一位节点(因为tail已经指向1号了,逻辑上成为了尾节点)。接下来进入循环,当p不指向空时持续执行;tmp指向p所指节点的下一个节点,然后p所指节点的next指针指向头节点的next指向然后头节点的next再指向p所指节点,p接下来指向tmp所指节点
Caution!:在一次循环中,tmp指向节点在p指向节点的下一位
void Inverse() {
Node* p, * tmp;
p = head->next;//指针p指向1号节点
head->next = NULL;//头节点next指向空
if(p != NULL)
tail = p;
while(p != NULL) {
tmp = p->next;
p->next = head->next;
head->next = p;
p = tmp;
}
}
Visit()——元素访问函数
算法接收一个变量i,通过while循环遍历到i指定的序号,然后访问对应节点的数据域。切记凡是涉及序号查找的任务,最好做一个参数合法性判断,来增加算法的健壮性。
int Visit(int i) {
Node* p = head->next;//p指针指向头节点下一个节点
int count = 0;
if (i < 0 || i > curlength-1)//判定参数值在合法范围内
return 0;
while(count < i) {
p = p->next;//p指针移动至下一个节点
count++;//计数增加
}
return p->data;
}