链表
链表是一种数据结构,其特点主要包括以下几点:
-
链接性:链表中的每个节点包含一个指针,指向下一个节点(单向链表)或前一个节点(双向链表)。通过这种链接关系,可以将一系列节点按照特定顺序连接在一起。
-
动态性:链表的大小可以动态地调整,可以动态地添加或删除节点,而不需要像数组那样需要提前申请固定大小的内存空间。
-
灵活性:链表可以在任何位置插入或删除节点,而不会影响其他节点的位置,因为每个节点都包含指针信息,可以直接访问相邻节点。
-
内存空间的分散性:链表的节点在内存中可以分散存储,不要求一块连续的内存空间。这也是链表可以动态调整大小的原因。
-
随机访问性较差:链表的节点通过指针链接在一起,所以在访问特定位置的节点时,需要从头节点开始遍历,直到找到目标位置。因此,链表的随机访问时间复杂度为O(n),相对于数组的O(1),效率较低。
总的来说,链表的特点主要体现在链接性、动态性和灵活性上,适用于频繁的插入和删除操作,但不适用于频繁的随机访问操作。
循环链表
链表一般是循环链表,首尾相连!
使用链表的方式:
- STL list【标准模板库中链表】
- 动态链表
- 静态链表
动态链表
动态链表是一种数据结构,它允许在运行时动态地添加、删除和访问数据。
【临时分配链表结点、使用完毕后释放链表结点】
与数组相比,动态链表不需要预先定义大小,可以根据需要动态分配内存,从而节省空间。
- 优点:能及时释放空间,不使用多余内存。
- 缺点:容易出错。
总结来说,动态链表是一种灵活且高效的数据结构,广泛应用于各种算法和系统中。它可以在运行时动态地管理数据,适用于需要频繁插入、删除和访问数据的场景。
洛谷练习—P1996约瑟夫问题
题目详情:
n个人围成一圈,从第一个人开始报数,数到m的人出列,再由下一个人重新从1开始报数,数到m的人再出圈,依次类推,直到所有的人都出圈,请输出依次出圈人的编号。
输入格式:输入两个整数n,m。
输出格式:输出一行n个整数,按顺序输出每个出圈人的编号。
提示:1<m, n <100.
理解:
- n个人围成一圈:用循环链表
- 每遍历m个人,输出第m个节点,并删除
- 直到n个人都被删除。
#include<iostream>
using namespace std;
struct node{
int data;
node *next;
};
int main(){
int n,m;
scanf("%d%d",&n,&m);
node *head,*p,*now,*prev;
head=new node; head->data=1; head->next=NULL;//分配节点一,值为1
now=head;//当前节点为头节点
for(int i=2;i<=n;i++){
p=new node;
p->data=i;
p->next=NULL;
now->next=p;
now=p;
}
now->next=head;//最后一个指向第一个 【形成循环链表】
//以上建立链表
now=head,prev=head;
while((n--)>1){//遍历所有节点
for(int i=1;i<m;i++){//数到m,停下
prev=now;
now=now->next;
}
printf("%d ",now->data);//输出
//输出并删除此元素
prev-> next=now->next;
delete now;
now=prev->next;
}
printf("%d",now->data);//打印最后一个节点
delete now;
return 0;
}
静态链表
静态链表是一种基于数组的数据结构,用于模拟链表的操作。
它的实现原理是通过数组的索引来表示链表中的结点,数组中的每个元素都有两个属性,一个是存储的数据值,另一个是指向下一个结点在数组中的索引。
注意:算法竞赛中常用静态链表,加快编码速度。
静态链表相对于动态链表的缺点是空间利用率低,因为需要提前分配一个固定大小的数组来存储链表,如果链表的元素数量超过了数组的大小,就需要重新分配更大的数组来存储链表。
静态链表的优点是插入和删除操作的时间复杂度为O(1),而动态链表的插入和删除操作的时间复杂度为O(n)。所以在某些场景下,静态链表的性能更好。
用结构体数组实现静态链表
#include<iostream>
using namespace std;
const int N = 105;
struct node
{
int id, nestid;
//int data;
}node[N];
void main() {
int n, m; cin >> n >> m;
node[0].nestid = 1;
for (int i = 1; i <= n; i++) {
node[i].id = i;
node[i].nestid = i + 1;
}
node[n].nestid = 1;//循环链表:收尾相连
int now = 1, prev = 1;
while ((n--) > 1) {
for (int i = 1; i < m; i++) {
prev = now;
now = node[now].nestid;
}
cout << node[now].id<<" ";
node[prev].nestid = node[now].nestid;
now = node[prev].nestid;
}
//直到只剩一个元素停止循环
cout << node[now].id;
}
用一维数组
这个是最简单的实现方法,一维数组nodes[i]中,i存节点的值,nodes[i]存下一个节点。
#include<iostream>
using namespace std;
const int N = 105;
void main() {
int n, m; cin >> n >> m;
int nodes[N];
for (int i = 1; i <= n - 1; i++) {
nodes[i] = i + 1;
}
nodes[n] = 1;
int now = 1, prev = 1;
while ((n--) > 1) {
for (int i = 1; i < m; i++) {
prev = now;
now = nodes[now];
}
cout << now << " ";
nodes[prev] = nodes[now];
now = nodes[prev];
}
cout << now;
}
STL list 链表
逻辑跟上面的相同
遍历用迭代器:iterator
删除erase(),查找end()begin()
void main() {
int n, m; cin >> n >> m;
list<int>node;//建立链表
for (int i = 1; i <= n; i++) {
node.push_back(i);//初始化:插入队尾
}
list<int>::iterator it = node.begin();//遍历:迭代器
while (node.size()> 1) {
for (int i = 1; i < m; i++) {
it++;
if (it == node.end()) {
it = node.begin();
}
}
cout << *it << " ";
list<int>::iterator next = ++it;
if (next == node.end()) {
next = node.begin();
}
node.erase(--it);//删除节点
it = next;
}
cout << *it;
}