单链表
链表的概念
链表是一种常用的数据结构,通常用来存储一组同类数据。它与数组最大的不同在于其可以动态的进行内存分配,在需要增加一个元素时,动态的为它申请存储空间,但这样便无法保证元素是连续存储的,链表利用指向下一个节点的指针解决了这个问题。
下图给出了最简单的链表——单链表的结构。
链表有各种形式,除了单链表外还有双链表,循环链表等。
图示如下:
下面我们来讨论单链表。
单链表的存储
每个节点由两部分组成:数据元素本身和指向下一节点的指针。
struct linkNode {
datatype data;
linkNode* next;
};
datatype表示任意一种数据类型,第二个成员是指向自身类型的一个指针,这种结构称为自引用结构。
单链表的操作
单链表最基本的操作包括:
- 创建一个单链表
- 插入一个节点
- 删除一个节点
- 访问单链表的每一个节点
创建操作
- 定义一个单链表
- 创建一个空的单链表(只含有头结点)
- 依次从键盘读入数据
- 键入单链表的表尾
(具体见单链表实例)
插入操作
在链表中地址为p的节点后面插入另一个元素x:
- 申请一个节点
- 将x放入该节点的数据部分
- 将该接点链接到节点p后面
tmp = new linkNode;//创建一个新节点
tmp->data = x;//把x放入新节点的数据成员中
tmp->next = p->next;//把新节点和p的下一节点相连
p->next = tmp;//把p和新节点连接起来
删除操作
将p设为x前面的那个节点,让p的next链绕过x:
考虑到内存泄露,完整的删除操作应包括:
- 丛链表中删除节点
- 回收节点的空间
delPtr = p->next;//保存被删节点的地址
p->next = delPtr->next;//将此节点从链中删除
delete delPtr;//回收被删节点的空间
在上述的描述有一个基本问题:它假定了不论x在什么位置,在它前面总是有一个节点。而链表中的第一个节点前面是没有节点的,因此删除和插入第一个节点就需要特殊处理,但特殊情况通常会给程序设计带来很多麻烦。对于上述问题的解决方案就是引入一个头结点。
头结点不存放数据,只是作为链表的开始标记,位于链表的最前面,指向链表的第一个元素,现在就不在有特殊情况了。
单链表实例
题目: 创建并访问一个带头结点的,存储整型数据的单链表,数据从键盘键入,0作为结束标志。
代码清单:
# include <iostream>
using namespace std;
struct linkRec {
int data;
linkRec* next;
};
int main() {
int x;//存放输入的值
linkRec* head, * p, * rear;//head为头指针,rear为创建链表的表尾节点
head = rear = new linkRec;//创建空链表,头结点也是最后一个节点
cout << "请输入要创建的链表值(以0作为结束标志):"<<endl;
//创建链表的其他节点
while (true) {
cin >> x;
if (x == 0) break;
p = new linkRec;//申请一个新的节点
p->data = x;//将x的值存入新节点
rear->next = p;//将p链到表尾
rear = p;//p作为新的表尾
}
rear->next = NULL;//设置rear为表尾,其后没有其他节点了
//读链表
cout << "链表的内容为" << endl;
p = head->next;//p指向第一个节点
while (p != NULL) {
cout << p->data << '\t';
p = p->next;
}
cout << endl;
return 0;
}
运行结果:
约瑟夫环问题
题目: 已知 n 个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围。从编号为 k 的人开始报数,数到 m 的那个人出圈;他的下一个人又从 1 开始报数,数到 m 的那个人又出圈;依此规律重复下去,直到剩余最后一个胜利者。(在此题中令k=1,m=3)
思路:
- 单循环链表表示n个人围成一圈
- 模拟报数过程,逐个删除节点
代码清单:
# include <iostream>
using namespace std;
struct node {
int data;
node* next;
};
int main() {
node* head, * p, * q;
int n, i;
cout << "\ninput n:";
cin >> n;
//建立链表
head = p = new node;
p->data = 0;
for (i = 1; i < n; ++i) {
q = new node;
q->data = i;
p->next = q;
p = q;
}
p->next = head;//收尾相连
//删除过程
q = head;//head报数为1
while (q->next != q) {//表中元素多于一个
p = q->next;
q = p->next;//p报数为2,q报数为3
p->next = q->next;//删除q
cout << q->data+1 << '\t';//显示被删者的编号
delete q;//回收被删者的空间
q = p->next;//让q指向报1的节点
}
//打印结果
cout << "\n最后剩下:" << q->data << endl;
return 0;
}
运行结果:
中间一行为删除者编号