【算法竞赛】链表

链表的特点是用一组位于任意位置的存储单元存储线性表的数据元素,这组存储单元可以是连续的,也可以不连续。

链表是容易理解和操作的基本数据结构,它的操作有初始化、添加、遍历、插入、删除、查找、释放等。

链表分为单向链表和双向链表,如图所示.
在这里插入图片描述
链表的各节点首尾相接,最后的next指针指向第一个data,第一个pre指针指向最后的data.

单向链表只有一个遍历方向,双向链表有两个遍历方向,比单向链表的访问方便一些,也快一些.

在需要频繁访问前后几个节点的场合可以使用双向链表.

使用链表时,可以直接用STLlist,也可以自己写链表.

如果自己写代码实现链表,有两种编码实现方法:动态链表、静态链表.

在算法竞赛中为加快编码速度,一般用静态链表或STLlist.

例1.1
在这里插入图片描述

动态链表

动态链表需要临时分配链表节点,使用完毕后释放链表节点.

动态链表的优点是能及时释放空间,不使用多余内存;缺点是需要管理空间,容易出错.

动态链表是“教科书式”的标准做法.以下代码用动态单向链表实现了例1.1.

#include <bits/stdc++.h>
struct node 
{
	int data;
	node * next;
};

int main()
{
	int n,m;
	scanf("%d %d",&n,&m);
	
	node *head,*now;
	//建立链表头节点
	head=new node;
	head->data=1;
	head->next=NULL;
	
	now=head;
	//建立链表
	node*p;
	for(int i=2;i<=n;i++)
	{
		p=new node;
		p->data=i;
		now->next=p;
		now=p;
	}
	now->next=head;
	
	node*prev=head;
	now=head;
	while(n--)
	{
		for(int i=1;i<m;i++)
		{
			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;
	
}

静态链表

动态链表虽然“标准”,但是竞赛中一般不用.
算法竞赛对内存管理要求不严格,为加快编码速度,一般就在题目允许的存储空间内静态分配,省去了动态分配和释放的麻烦.

静态链表使用预先分配的一段连续空间存储链表.
从物理存储的意义上讲,“连续空间"并不符合链表的本义,因为链表的优点就是能克服连续存储的弊端,但是用连续空间实现的链表,在逻辑上是成立的.

具体有两种做法:
①定义链表结构体数组,和动态链表的结构差不多;
②使用一维数组,直接在数组上进行链表操作.
本节给出3段代码:用结构体数组实现单向静态链表、用结构体数组实现双向静态链表、用一维数组实现单向静态链表.

用结构体数组实现单向静态链表

用结构体数组实现单向静态链表,注意静态分配应该定义在全局,不能定义在函数内部.

#include<bits/stdc++.h>
const int N = 105;
struct node{
    int id, nextid;
    // int data; //如有必要, 定义一个有意义的数据
}nodes[N];
int main(){
    int n, m;
    scanf("%d%d", &n, &m);
    nodes[0].nextid = 1;
    for(int i = 1; i <= n; i++){
        nodes[i].id = i;
        nodes[i].nextid = i + 1;
    }
    nodes[n].nextid = 1;
    int now = 1, prev = 1;
    while((n--) > 1){
        for(int i = 1; i < m; i++){
            prev = now;
            now = nodes[now].nextid;
        }
        printf("%d ", nodes[now].id);
        nodes[prev].nextid = nodes[now].nextid;
        now = nodes[prev].nextid;
    }
    printf("%d", nodes[now].nextid);
    return 0;
}

用结构体数组实现双向循环链表

#include <bits/stdc++.h>
const int N = 105;

struct node{
    // int data;
    int id;
    int preid, nextid;
}nodes[N];

int main()
{
    int n, m;
    scanf("%d%d", &n, &m);
    nodes[0].nextid = 1;
    for(int i = 1; i <= n; i++)
    {
        nodes[i].id = i;
        nodes[i].preid = i - 1;
        nodes[i].nextid = i + 1;
    }
    nodes[n].nextid = 1;
    nodes[1].preid = n;
    int now = 1;
    while((n--) > 1)
    {
        int prev = nodes[now].preid, next = nodes[now].nextid;
        
        nodes[prev].nextid = nodes[now].nextid;
        nodes[next].preid = nodes[now].preid;
        for(int i = 1; i < m; i++)
        {
            now = nodes[now].nextid;
        }
        printf("%d ", nodes[now].id);
    }
    printf("%d", nodes[now].nextid);
    return 0;
}

用一维数组实现单向静态链表

这是最简单的实现方法。定义一个一维数组nodes[],nodes[i]的i就是节点的值,而nodes[i]的值是下一个节点。
它的使用环境很有限,因为它的节点只能存一个数据,就是i。


#include<bits/stdc++. h>

int nodes[150];

int main()
{
	int n, m;
	scanf("%d %d", &n, &m);
	
	for(int i=1;i<=n-1;i++) 
		nodes[i] = i+1;
	
	nodes[n] = 1;
	int now = 1, prev = 1;
	
	while((n--)>1) >1)
	{
		for(int i=1; 1; i<m; i++)
		{
			prev = now; 
			now = nodes[now];
		}
		printf("%d", now);
		nodes[prev] = nodes[now];
		now = nodes[prev];
	}
	printf("%d", now);
	return 0;
}

C++ STL List

在算法竞赛或工程项目中常常使用C++STLlist。list。list是双向链表,由标准模板库(Standard Template Library,STL)管理,通过指针访问节点数据,高效率地删除和插入。
请熟悉list的初始化、插入、删除、遍历、查找、释放。下面是用list实现例1.1的代码。

#include<bits/stdc++.h>
using namespace std;
int 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;
        node.erase(--it);
        if(next == node.end())
        {
            next = node.begin();
        }
        it = next;
    }
    cout<<*it;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值