【程设大作业】mydeque

  记录下,那段,与 STL 斗智斗勇的岁月~

Task

  一句话,实现 deque<int> 及相应的 iterator
  具体来说,你需要实现 mydequemyIteratormyrIterator 三个类(不允许使用 STL),需要完成的功能如下:

class mydeque {
	public:
		mydeque();
		~mydeque();
		// Add element at the end
		void push_back(int num);
		// Add element at the front
		void push_front(int num);
		// Insert elements at pos
		void insert(myIterator& pos, int num);
		// Erase elements
		void earse(myIterator& pos);
		// Clear content
		void clear();
		// Access element
		int& operator[](int pos);
		// Access first element
		int& front();
		// Access last element
		int& back();
		// Return size
		int size();
		// Return iterator to beginning
		myIterator begin();
		// Return iterator to end
		myIterator end();
		// Return reverse iterator to reverse beginning
		myrIterator rbegin();
		// Return reverse iterator to reverse end
		myrIterator rend();
};
class myIterator {
	public:
		myIterator& operator= (const myIterator &iter);
		bool operator!= (const myIterator &iter);
		bool operator== (const myIterator &iter);
		myIterator& operator++ ();
		myIterator operator++ (int);
		myIterator operator+ (int num);
};
class myrIterator {
	//与 myIterator 同理
};

  可自行增加需要的函数、变量。

  使用 Linux 下的 benchmark 评分。评分标准为跟 STL 比速度,共 18 个测试项(具体看后面的 main.cpp),每个测试项满分为 6 分,总分最终不超过 100 分。(基准实验指 class mydeque : public stl::deque<int> {}

  • 程序正确运行,没有崩溃,内存泄露等错误(3.6分)
  • 程序运行效率达到基准实验的60%(4.2分)
  • 程序运行效率达到基准实验的70%(6.8分)
  • 程序运行效率达到基准实验的80%(5.4分)
  • 程序运行效率达到基准实验的100%(6分)

  STL 效率如下:
在这里插入图片描述
  Iteration 我猜是一个自适应的迭代次数,就是如果你跑得快,就可以多跑一些,跑得慢就会少跑一些,最终用自己的 Iteration 除以 STL 的 Iteration 就可以直到效率比了。

Sol

  deque 的特性是, O ( 1 ) O(1) O(1) 前端后端插入、随机访问, O ( n ) O(n) O(n) 中间插入、删除。要做到这样,最方便的是使用一个长长的一维数组,但是这样的话迁移的效率又不是很高,而且对内存要求苛刻,因此折中一下使用块状链表,有一个大数组 map,里面的每一个元素都指向一个小数组,即双层数组结构。
  然后它的 iterator 比较复杂,要记录两层数组的下标、所在第二层数组的开头、结尾,具体参考《STL源码剖析》。

\\
  然而,要深知,照着 STL 的写法来写一遍,是会有 debuff 的,是永远也别想跟它比效率的。
  比如,就 for (auto it = deq.begin(); it != deq.end(); it++) {} 这句话,如果在 it++ 里执行了大于等于两个操作,你甚至可能要跑 STL 的几倍到十几倍时间。。。
  要想艹 STL 得高分,就必须使尽浑身解数,斗智斗勇。

\\
  首先简化一下我需要记录的东西:

struct MapType {
	int *a,num;
} *map;
int head,tail,l,r,maxsize,totsize;

  map[i].a[j] 就是一个双层数组结构,用于存放东西,用一个常数 alen 表示每个 map.a[] 的定长,maxsize 表示第一层也就是 map[] 的长度(这个是要不断倍长、迁移的)。STL 的写法中记录了一头一尾两个 iterator,在这里我用 headl 表示头的两个下标,tailr 表示尾的两个下标。

  做法很简单,push_back()push_front() 就直接往两边放,第二层不够了就新开第二层,第一层不够了就整体倍长迁移(像 vector 那样)。insert() 就直接找到那个位置,然后把它右边的元素全部往后推一位,erase() 同理。由于第二层数组是定长的,因此简单算个数就能使用 [] 访问了。

  一些优化:
  比如说,myIterator 里就只放一个下标,表示它是从左往右数第几个数。毕竟测试项里没有通过 iterator 访问元素这一项,那我就牺牲这个的复杂度(把它转成通过 [] 来访问),换取一个飞快的 for (auto it = deq.begin(); it != deq.end(); it++) {}
  insert()erase() 在 STL 里都用 iterator 来写,那么我就直接 for 循环,避免过多的调用。
  能用 memcpy 就不要用 for 循环,要始终坚信它写的东西就是比你的快。
  一个重要的优化是,insert() 不要鲁莽地全部往右推,判断一下它是在整个结构的左半边还是右半边,如果在左半边就左边的往左推,如果在右半边就往右推。简单分析一下效率,如果一味往右推,单次操作期望次数 n 2 2 \frac{n^2}{2} 2n2,优化以后单次操作期望次数 2 × ( n 2 ) 2 2 = n 2 4 2\times\frac{(\frac n2)^2}{2}=\frac{n^2}{4} 2×2(2n)2=4n2,能优化掉一半的时间。erase() 同理。
  块大小 alen 可以调调参,理论上当它取 n \sqrt n n 的时候效率是最优的(学分块学疯了),但是 n n n 在实际中是未知的,而且恰好取到 n \sqrt n n 的时候其实挺慢的,STL 其实也取得很小。
  写时复制。这个我没加,但是对于这个题来说(详见 main.cpp,构造了一个 deque 之后传来传去的)显然是一个十分有用的优化。写了的人说十分有用。
  自己写内存分配,不要用 newfree。这两个贼慢,详见 舍友的博客

  放一波效率图,可以看到效率其实还是很烂,毕竟 STL 自带了很多 buff。
在这里插入图片描述

有趣的故事

  我的队友老哥,想写平衡树艹它,于是写了个无旋 treap。
  一开始的时候,main.cpp 里的那些关于暂停的注释是没有的,也就是说,构造 deque 是不计时间的。
  这位老哥的 treap 很优秀,各操作时间很平衡,于是艹爆了 STL 的 O ( n ) O(n) O(n) 操作。但是他复制这个 treap 的时间有点慢。
  万万没想到让这个 benchmark 的测评方法给栽跟头了。由于他跑太快了,于是他要跑很多次,跑很多次的话,他的复制的时间就爆掉了。
  于是就出现了“我跑得太快了,所以我超时了”。

  后来可怜的助教就把注释加上了,构造 deque 也计时间,于是他的平衡树就被打爆了,不得不写标准版。

代码

// main.cpp
// 这是下发的不可修改的文件
// 程序最后可看到 18 个测试项,括号里的数字代表数据规模

#include <benchmark/benchmark.h>
#include <algorithm>
#include <random>

#include "mydeque.hpp"

long getRandom(long from=0, long to=65536) {
  return (rand() % (to-from+1)) + from;
}

mydeque constructRandomDeque(long num) {
  mydeque deq;
  for (long i = 0; i < num; i++) {
    deq.push_back(i);
  }
  return deq;
}

static void BM_deqempty(benchmark::State& state) {
  for (auto _ : state) { mydeque deq; }
}


static void BM_deqPushBack(benchmark::State& state) {
  for (auto _ : state) {
    mydeque deq;
    for (long i = 0; i < state.range(0); i++) {
      deq.push_back(i);
    }
  }
}

static void BM_deqPushfront(benchmark::State& state) {
  for (auto _ : state) {
    mydeque deq;
    for (long i = 0; i < state.range(0); i++) {
      deq.push_front(i);
    }
  }
}

static void BM_deqTraverse(benchmark::State& state) {
  for (auto _ : state) {
    mydeque deq;
    // state.PauseTiming();
    deq = constructRandomDeque(state.range(0));
    // state.ResumeTiming();
    for (auto it = deq.begin(); it != deq.end(); it++) {}
  }
}

static void BM_deqRtraverse(benchmark::State& state) {
  for (auto _ : state) {
    mydeque deq;
    // state.PauseTiming();
    deq = constructRandomDeque(state.range(0));
    // state.ResumeTiming();
    for (auto it = deq.rbegin(); it != deq.rend(); it++) {}
  }
}

static void BM_deqInsert(benchmark::State& state) {
  for (auto _ : state) {
    mydeque deq;
    // state.PauseTiming();
    deq = constructRandomDeque(state.range(0));
    // state.ResumeTiming();
    for (int j = 0; j < state.range(1); ++j)
      deq.insert(deq.begin()+getRandom(0, deq.size()-1), getRandom());
  }
}

static void BM_deqErase(benchmark::State& state) {
  for (auto _ : state) {
    mydeque deq;
    state.PauseTiming();
    deq = constructRandomDeque(state.range(0));
    int max = state.range(0) > state.range(1) ? state.range(0) : state.range(1);
    state.ResumeTiming();
    for (long j = 0; j < state.range(1); ++j) {
        if (deq.size()==0) break;
        deq.erase(deq.begin()+getRandom(0, max-1-j));
    }
  }
}

static void BM_deqAt(benchmark::State& state) {
  for (auto _ : state) {
    mydeque deq;
    // state.PauseTiming();
    deq = constructRandomDeque(state.range(0));
    // state.ResumeTiming();
    for (long j = 0; j < state.range(1); ++j)
      deq[getRandom(0, state.range(0)-1)];
  }
}

// static void BM_deqSort(benchmark::State& state) {
//   mydeque deq;
//   for (auto _ : state) {
//     state.PauseTiming();
//     deq = constructRandomDeque(state.range(0));
//     state.ResumeTiming();
//     std::sort(deq.begin(),deq.end());
//   }
// }

// static void BM_deqShuffle(benchmark::State& state) {
//   mydeque deq;
//   for (auto _ : state) {
//     state.PauseTiming();
//     deq = constructRandomDeque(state.range(0));
//     state.ResumeTiming();
//     std::random_shuffle(deq.begin(),deq.end());
//   }
// }


// Register the function as a benchmark
BENCHMARK(BM_deqempty);
BENCHMARK(BM_deqPushBack)->Arg(32)->Arg(65536)->Arg(1L<<22);
//BENCHMARK(BM_deqPushfront)->Arg(32)->Arg(65536)->Arg(1L<<22);
BENCHMARK(BM_deqTraverse)->Arg(32)->Arg(65536)->Arg(1L<<22);
BENCHMARK(BM_deqRtraverse)->Arg(32)->Arg(65536)->Arg(1L<<22);
BENCHMARK(BM_deqInsert)->Args({32, 512})->Args({65536, 512})->Args({1L<<22, 512});
BENCHMARK(BM_deqErase)->Args({512, 512})->Args({65536, 512})->Args({1L<<22, 512});
BENCHMARK(BM_deqAt)->Args({32, 128})->Args({65536, 128});


// BENCHMARK(BM_deqPushfront)->RangeMultiplier(32)->Range(8L, 8L<<20);
// BENCHMARK(BM_deqTraverse)->RangeMultiplier(32)->Range(8L, 8L<<20);
// BENCHMARK(BM_deqRtraverse)->RangeMultiplier(32)->Range(8L, 8L<<20);
// BENCHMARK(BM_deqInsert)->Ranges({{32L, 8L<<22}, {128, 512}});
// BENCHMARK(BM_deqErase)->Ranges({{32L, 8L<<22}, {128, 512}});
// BENCHMARK(BM_deqAt)->Ranges({{32L, 8L<<22}, {128, 512}});
// BENCHMARK(BM_deqSort)->RangeMultiplier(32)->Range(8L, 8L<<20);
// BENCHMARK(BM_deqShuffle)->RangeMultiplier(32)->Range(8L, 8L<<22);

BENCHMARK_MAIN();

// mydeque.hpp

#ifndef MYDEQUE_HPP
#define MYDEQUE_HPP

#include"myIterator.hpp"

class mydeque {
	public:
		struct MapType {
			int *a,num;
		} *map;
		int head,tail,l,r,maxsize,totsize;
		
		void NewMapType(MapType &x);
		void ChangeMap();
		inline void Release();
		inline void init();
		
		mydeque();
		~mydeque();
		mydeque& operator = (const mydeque &deq);
		
		void push_back(const int &val);
		void push_front(const int &val);
		void insert(myIterator pos,int val);
		void erase(const myIterator &pos);
		void clear();
		
		int& operator [] (int i);
		int& front();
		int& back();
		int size();
		
		myIterator begin();
		myIterator end();
		myrIterator rbegin();
		myrIterator rend();
};

#endif

// myIterator.hpp

#ifndef MYITERATOR_HPP
#define MYITERATOR_HPP

const int alen=130;

class myIterator {
	public:
		int pos;
		
		myIterator(int x=0);
		
		bool operator != (const myIterator &iter);
		bool operator == (const myIterator &iter);
		myIterator& operator ++ ();
		myIterator& operator -- ();
		myIterator operator ++ (int);
		myIterator operator + (int num);
};

class myrIterator {
	public:
		int pos;
		
		myrIterator(int x=0);
		
		bool operator != (const myrIterator &iter);
		bool operator == (const myrIterator &iter);
		myrIterator& operator ++ ();
		myrIterator& operator -- ();
		myrIterator operator ++ (int);
		myrIterator operator + (int num);
};

#endif

// mydeque.cpp

#include<cstring>
#include"mydeque.hpp"
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

inline void mydeque::NewMapType(mydeque::MapType &x)
{
	x.a=new int[alen+2];
	x.num=0;
}

void mydeque::ChangeMap()
{
	int len=maxsize, half=len>>1;
	maxsize<<=1;
	MapType *newmap=new MapType[maxsize+2];
	memcpy(newmap+half,map,len*sizeof(MapType));
	delete[] map;
	map=newmap;
	head+=half, tail+=half;
}

inline void mydeque::Release()
{
	fo(i,head,tail) delete[] map[i].a;
	delete[] map;
}

inline void mydeque::init()
{
	maxsize=2;
	map=new MapType[maxsize+2];
	NewMapType(map[1]);
	head=tail=1;
	l=1, r=0;
	totsize=0;
}

mydeque::mydeque()
{
	init();
}

mydeque::~mydeque()
{
	Release();
}

mydeque& mydeque::operator = (const mydeque &deq)
{
	Release();
	maxsize=deq.maxsize;
	head=deq.head, tail=deq.tail;
	l=deq.l, r=deq.r;
	totsize=deq.totsize;
	map=new MapType[maxsize+2];
	fo(i,head,tail)
	{
		map[i].num=deq.map[i].num;
		map[i].a=new int[alen+2];
		memcpy(map[i].a,deq.map[i].a,sizeof(map[i].a));
	}
}

void mydeque::push_back(const int &val)
{
	if (r==alen)
	{
		if (tail==maxsize-1) ChangeMap();
		NewMapType(map[++tail]);
		r=0;
	}
	map[tail].num++;
	map[tail].a[++r]=val;
	totsize++;
}

void mydeque::push_front(const int &val)
{
	if (l==1)
	{
		if (head==0) ChangeMap();
		NewMapType(map[--head]);
		l=alen+1;
	}
	map[head].num++;
	map[head].a[--l]=val;
	totsize++;
}

void mydeque::insert(myIterator pos,int val)
{
	if (pos.pos>=(totsize>>1))
	{
		push_back(0);
		for(int i=tail, cnt=totsize-1; cnt!=pos.pos; --i)
			for(int j=(i==tail ?r :alen); j>=1 && cnt!=pos.pos; --j, --cnt)
				map[i].a[j]=(j==1) ?map[i-1].a[alen] :map[i].a[j-1] ;
	} else
	{
		push_front(0);
		for(int i=head, cnt=0; cnt!=pos.pos; ++i)
			for(int j=(i==head ?l :1); j<=alen && cnt!=pos.pos; ++j, ++cnt)
				map[i].a[j]=(j==alen) ?map[i+1].a[1] :map[i].a[j+1] ;
	}
	(*this)[pos.pos]=val;
}

void mydeque::erase(const myIterator &pos)
{
	if (pos.pos>=(totsize>>1))
	{
		for(int i=tail, cnt=totsize-1, last=map[i].a[(i==tail ?r :alen)], t, *p; cnt!=pos.pos; --i)
			for(int j=(i==tail ?r :alen); j>=1 && cnt!=pos.pos; --j, --cnt)
			{
				p=(j==1) ?&(map[i-1].a[alen]) :&(map[i].a[j-1]) ;
				t=*p, *p=last, last=t;
			}
		map[tail].num--;
		if (--r==0) {delete[] map[tail--].a; r=alen;}
	} else
	{
		for(int i=head, cnt=0, last=map[i].a[(i==head ?l :1)], t, *p; cnt!=pos.pos; ++i)
			for(int j=(i==head ?l :1); j<=alen && cnt!=pos.pos; ++j, ++cnt)
			{
				p=(j==alen) ?&(map[i+1].a[1]) :&(map[i].a[j+1]) ;
				t=*p, *p=last, last=t;
			}
		map[head].num--;
		if (++l>alen) {delete[] map[head++].a; l=1;}
	}
	totsize--;
}

void mydeque::clear()
{
	Release();
	init();
}

int& mydeque::operator [] (int i)
{
	if (i<map[head].num) return map[head].a[l+i];
	i-=map[head].num;
	int index=i/alen;
	return map[head+index+1].a[i-index*alen+1];
}

int& mydeque::front()
{
	return map[head].a[l];
}

int& mydeque::back()
{
	return map[tail].a[r];
}

int mydeque::size()
{
	return totsize;
}

myIterator mydeque::begin()
{
	return myIterator(0);
}

myIterator mydeque::end()
{
	return myIterator(totsize);
}

myrIterator mydeque::rbegin()
{
	return myrIterator(totsize-1);
}

myrIterator mydeque::rend()
{
	return myrIterator(-1);
}

// myIterator.cpp

#include"myIterator.hpp"

//myIterator

bool myIterator::operator != (const myIterator &iter)
{
	return (this->pos!=iter.pos);
}

bool myIterator::operator == (const myIterator &iter)
{
	return (this->pos==iter.pos);
}

myIterator::myIterator(int x)
{
	pos=x;
}

myIterator& myIterator::operator ++ ()
{
	++pos;
	return *this;
}

myIterator& myIterator::operator -- ()
{
	--pos;
	return *this;
}

myIterator myIterator::operator ++ (int)
{
	myIterator re=*this;
	++pos;
	return re;
}

myIterator myIterator::operator + (int num)
{
	return myIterator(this->pos+num);
}

//myrIterator

bool myrIterator::operator != (const myrIterator &iter)
{
	return (this->pos!=iter.pos);
}

bool myrIterator::operator == (const myrIterator &iter)
{
	return (this->pos==iter.pos);
}

myrIterator::myrIterator(int x)
{
	pos=x;
}

myrIterator& myrIterator::operator ++ ()
{
	--pos;
	return *this;
}

myrIterator& myrIterator::operator -- ()
{
	++pos;
	return *this;
}

myrIterator myrIterator::operator ++ (int)
{
	myrIterator re=*this;
	--pos;
	return re;
}

myrIterator myrIterator::operator + (int num)
{
	return myrIterator(this->pos-num);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 C++ 标准库中,`queue` 是一种容器适配器,它是基于其他容器(如 `deque`)实现的。由于 `queue` 是基于其他容器实现的,所以它并没有提供直接指定最大容量的接口。 但是,我们可以通过在定义 `queue` 时指定底层容器的最大容量来实现队列的最大容量限制。例如,可以使用 `deque` 作为 `queue` 的底层容器,并在定义 `deque` 时指定其最大容量,从而限制 `queue` 的最大容量。 下面是一个使用 `deque` 作为 `queue` 底层容器并指定最大容量的例子: ``` #include <iostream> #include <deque> #include <queue> using namespace std; int main() { const size_t max_size = 100; // 定义队列的最大容量为 100 deque<int> myDeque(max_size); // 定义一个元素类型为 int,最大容量为 max_size 的 deque queue<int> myQueue(myDeque); // 定义一个使用 myDeque 作为底层容器的队列 // 向队列中添加元素 for (int i = 0; i < max_size; i++) { myQueue.push(i); } // 尝试向队列中添加超过最大容量的元素 myQueue.push(max_size); // 输出队列中的元素数量 cout << "队列中的元素数量:" << myQueue.size() << endl; return 0; } ``` 在这个例子中,我们首先定义了一个常量 `max_size`,表示队列的最大容量为 100。然后,使用 `deque` 容器并指定最大容量 `max_size` 创建了一个 `myDeque` 对象。接着,使用 `myDeque` 作为底层容器创建了一个 `myQueue` 队列对象。注意,这里使用 `myDeque` 作为底层容器是通过 `queue` 的构造函数来实现的。 最后,我们向队列中添加了 `max_size` 个元素,并尝试向队列中添加超过最大容量的元素。可以看到,当队列中的元素数量达到最大容量时,再向队列中添加元素会被拒绝,从而实现了队列的最大容量限制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值