算法竞赛入门经典-STL数据结构的使用

本文通过三个具体的程序设计案例介绍了如何运用栈、队列和双端队列等数据结构解决问题,展示了标准模板库(STL)在提高编程效率方面的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.洗牌在生活中十分常见,现在需要写一个程序模拟洗牌的过程。 现在需要洗2n张牌,从上到下依次是第1张,第2张,第3张一直到第2n张。首先,我们把这2n张牌分成两堆,左手拿着第1张到第n张(上半堆),右手拿着第n+1张到第2n张(下半堆)。接着就开始洗牌的过程,先放下右手的最后一张牌,再放下左手的最后一张牌,接着放下右手的倒数第二张牌,再放下左手的倒数第二张牌,直到最后放下左手的第一张牌。接着把牌合并起来就可以了。 例如有6张牌,最开始牌的序列是1,2,3,4,5,6。首先分成两组,左手拿着1,2,3;右手拿着4,5,6。在洗牌过程中按顺序放下了6,3,5,2,4,1。把这六张牌再次合成一组牌之后,我们按照从上往下的顺序看这组牌,就变成了序列1,4,2,5,3,6。 现在给出一个原始牌组,请输出这副牌洗牌k次之后从上往下的序列。


思路使用栈(这里没有用到STL 容器)

#include<iostream>
#include<string.h>
#include<ctype.h>
using namespace std;
 
int stack[200+10];
int stack1[100+10];
int stack2[100+10];
int length=0;
int length1=0;
int length2=0;
int main()
{
    int T;
    int n,k;
     
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&k);
        for(int i=0;i<2*n;i++)
        {scanf("%d",&stack[2*n-1-i]);}
        length=2*n;
 
        while(k--)
 
        {//分成两堆
            length1=0;
            length2=0;
            for(int i=0;i<n;i++)
            stack1[length1++]=stack[--length];//左手
            for(int i=n;i<2*n;i++)
            stack2[length2++]=stack[--length];//右手
        //洗牌
            length=0;
            while(length1>0 && length2>0)
            {
                stack[length++]=stack2[--length2];//长度先减一,再出栈
                stack[length++]=stack1[--length1];
            }
            //和牌
 
        }
        //输出栈(自顶向下)
            while(length>1)
            {
                printf("%d ",stack[--length]);
            }
            printf("%d\n",stack[--length]);
    }
    //system("pause");
    return 0;
}
2. 小明同学把1到n这n个数字按照一定的顺序放入了一个队列Q中。现在他对队列Q执行了如下程序:

while(!Q.empty())              //队列不空,执行循环

{

    int x=Q.front();            //取出当前队头的值x

    Q.pop();                 //弹出当前队头

    Q.push(x);               //把x放入队尾

    x = Q.front();              //取出这时候队头的值

    printf("%d\n",x);          //输出x

    Q.pop();                 //弹出这时候的队头

}

做取出队头的值操作的时候,并不弹出当前队头。
小明同学发现,这段程序恰好按顺序输出了1,2,3,...,n。现在小明想让你构造出原始的队列,你能做到吗?

解题思路:把该过程逆向(使用双端队列实现)

#include<iostream>
#include<string.h>
#include<ctype.h>
using namespace std;
 
#include<deque>
int main()
{
    int T;
    int n;
    scanf("%d",&T);
    while(T--)
    {
        deque<int> q;
        scanf("%d",&n);
        while(n)
        {
            q.push_front(n--);
            int t=q.back();
            q.push_front(t);
            q.pop_back();
        }
 
        //从队头到队尾,依次输出
        while(q.size()>1)
        {cout<<q.front()<<" ";
        q.pop_front();
        }
        cout<<q.front()<<endl;
    }
    //system("pause");
    return 0;
}

3. 已知n个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围。从编号1开始报数,数到k的那个人出列;他的下一个人又从1开始报数,数到k的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。 
例如n=10,k=3时,输出的出列顺序是3,6,9,2,7,1,8,5,10,4。

解题思路,典型的使用链表实现。STL中链表实现方式有多种,这里使用vector

#include<iostream>
#include<vector>
using namespace std;

int main()
{
    vector<int> a;              //定义向量对象a
    int n,k,x=0;
    cout<<"请输入总人数n和出环位置k:"<<endl;
    cin>>n>>k;
    for(int i=0; i<n; i++)
    {
        a.push_back(i+1);   //初始化向量对象a,依次将i+1插入尾部
    }
    while(a.size())             //当向量中元素个数不为0
    {
        x = (x+k-1) % a.size();     //计算出环位置
        cout<<a[x]<<" ";
        a.erase(a.begin()+x);       //在向量中删除此元素
    }
	system("pause");
    return 0;
}


看到这里,读者应该能发现,在程序设计过程中使用现成的数据结构实现,能减少程序设计的麻烦,算法思路更加明确

这里做一个简单的总结,总结程序设计构成中常用的STL

1)。栈 stack

  1. #include <stack>  //引入栈
  2. 在STL中栈一共就5个常用操作函数(top()、push()、pop()、 size()、empty() ),很好记的。
定义一个stack的变量     stack<Type> s   //例如  stack <int> Q,q,、、

入栈,如例:s.push(x);

出栈,如例:s.pop();注意,出栈操作只是删除栈顶元素,并不返回该元素。

访问栈顶,如例:s.top()

判断栈空,如例:s.empty(),当栈空时,返回true。

访问栈中的元素个数,如例:s.size()


2)队列

定义一个queue的变量     queue<Type> M   //例如  queue <int> Q,q,、、
查看是否为空范例        M.empty()    是的话返回1,不是返回0;
从已有元素后面增加元素   M.push()
输出现有元素的个数      M.size()
显示第一个元素          M.front()
显示最后一个元素        M.back()
清除第一个元素          M.pop()


3)双端队列

使用deque容器之前必须加上<deque>头文件:#include<deuqe>;

deque<Elem> c 创建一个空的deque;

c.front()返回c容器的第一个元素

c.back()返回c容器的最后一个元素

c.size()返回c容器中实际拥有的元素个数

c.push_back(num)在末尾位置插入元素

c.pop_back()删除末尾位置的元素

c.push_front(num)在开头位置插入元素

c.pop_front()删除开头位置的元素

另外常用的还有:

c.insert(pos,num)在pos位置插入元素num

c.erase(pos)删除pos位置的元素

下面看一道经典的约瑟夫环的题

问题描述: 
已知n个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围。从编号1开始报数,数到k的那个人出列;他的下一个人又从1开始报数,数到k的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。 
例如n=10,k=3时,输出的出列顺序是3,6,9,2,7,1,8,5,10,4。

#include<iostream>
#include<deque>
using namespace std;

int main()
{
    deque<int> a;              //定义向量对象a
    int n,k,x=0;
    cout<<"请输入总人数n和出环位置k:"<<endl;
    cin>>n>>k;
    for(int i=0; i<n; i++)
    {
        a.push_back(i+1);   //初始化向量对象a,依次将i+1插入尾部
    }
    while(a.size())             //当向量中元素个数不为0
    {
        x = (x+k-1) % a.size();     //计算出环位置
        cout<<a[x]<<" ";
        a.erase(a.begin()+x);       //在向量中删除此元素
    }
	system("pause");
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值