北京大学程序设计MOOC作业详解-08-标准模板库STL(一)

文章介绍了关于STL模板库的几个编程题目,涉及数据覆盖问题、自定义排序方法(包括cmp函数、重载运算符和仿函数)、三维数组模板类的实现、满足memset需求的数组结构调整、lambda表达式的使用以及自定义ostream_iterator。内容涵盖了C++中的容器、迭代器和算法应用。
摘要由CSDN通过智能技术生成

北京大学程序设计MOOC作业详解-08-标准模板库STL(一)

第一题:

这个题要分析分析,首先看它的测试代码:

int main()
{
	int t;
	cin >> t;
	while( t -- ) {
		int m ;
		cin >> m;
		for(int i = 0;i < m; ++i)
			cin >> a[i];
		GoodCopy<int>()(a,a+m,b);
		Print(b,b+m);
		GoodCopy<int>()(a,a+m,a+m/2);
		Print(a+m/2,a+m/2 + m);

		for(int i = 0;i < m; ++i)
			cin >> c[i];
		GoodCopy<string>()(c,c+m,d);
		Print(c,c+m);
		GoodCopy<string>()(c,c+m,c+m/2);
		Print(c+m/2,c+m/2 + m);
	}
	return 0;
}

有代码GoodCopy<int>()(a,a+m,a+m/2);,这涉及到一个严重的问题:数据覆盖。比如说数组a[] = {1, 2, 3, 4, 5, 6, _, _, _};,这里腾出来三个位置(数组大小为6),正确的拷贝应是:{_, _, _, 1, 2, 3, 4, 5, 6}。这需要从后往前拷贝,如果是从前往后,则会变成:{_, _, _, 1, 2, 3, 1, 2, 3}

因此在算法的设计上,要有考究,必须从后往前拷贝, AC代码如下:

template <class T>
struct GoodCopy {
template<class T_ = T>
	void operator()(T_ begin, T_ end, T_ ptr) {
		for (; end != begin; --end) {
			*(ptr +(end - begin - 1)) = *(end - 1);
		}
	}
};

第二题:

这个题先看调用代码:

sort(a,a+10,Closer<int ,int (*)(int ,int)> (n,Distance1));

首先总结自定义sort排序的几种方法:
1 自定义cmp函数,声明如下:

bool cmp(const Type& obj1, const Type& obj2);

2 自定义重载小于号运算符,声明如下:

class Type {
public:
	bool operator<(const Type& obj) const;
};

3 自定义函数对象,即仿函数。仿函数是指,可以将对象看作一种函数,用调用函数的方式去调用,核心是自定义重载圆括号运算符,声明如下:

struct Comperator {
	bool operator()(const Type& obj1, const Type& obj2);
};

此时,传入Comperator类型的对象,例如Comperator cmp;,就可以通过cmp(obj1, obj2);的方式进行比大小。

综上所述,自定义排序规则的核心,是要有比大小的功能

还需要注意,仿函数不是通过类调用的,而是通过对象调用的!所以必须实例化,先看看本题实例化的办法:

Closer<int ,int (*)(int ,int)> (n,Distance1)

显然这是一个匿名对象,第一个参数是对比的对象,第二个参数是比较的函数。然后,两个对象obj1和obj2谁排在前面呢?就看它们和n的相对大小。

AC代码如下:

template <class T1,class T2>
struct Closer {
	T1 m_val;
	T2 m_func;

	template<class T1_ = T1, class T2_ = T2>
	Closer(T1_ val, T2_ func) : m_val(val), m_func(func) { }

	template<class T1_ = T1>
	bool operator()(T1_ n1, T1_ n2) {
		int d1 = m_func(n1, m_val);
		int d2 = m_func(n2, m_val);
		if (d1 != d2) {
			return (d1 < d2);
		}
		return (n1 < n2);
	}
};

第三题:实现三维数组模板类

这个题有点难度,我第一次实现如下:

class CArray3D
{
	// 在此处补充你的代码
public:
	class CArray2D {
	private:
		T** m_data;
		size_t m_row, m_col;
	public:
		CArray2D(size_t row = 0, size_t col = 0) : m_row(row), m_col(col) {
			if (0 == row) {
				m_data = nullptr;
				return;
			}
			m_data = new T * [row];
			for (size_t j = 0; j < row; ++j) {
				m_data[j] = new T[col]();
			}
		}

		CArray2D(const CArray2D& arr2D)
			: m_row(arr2D.m_row), m_col(arr2D.m_col) {
			if (arr2D.m_data) {
				m_data = new T * [m_row];
				for (size_t j = 0; j < m_row; ++j) {
					m_data[j] = new T[m_col]();
					for (size_t k = 0; k < m_col; ++k) {
						m_data[j][k] = arr2D.m_data[j][k];
					}
				}
			}
			else {
				m_data = nullptr;
			}
		}

		CArray2D& operator=(const CArray2D& arr2D) {
			if (this == &arr2D) { return *this; }
			if (m_data) {
				for (size_t j = 0; j < m_row; ++j) {
					delete[] m_data[j];
				}
				delete[] m_data;
			}
			m_row = arr2D.m_row, m_col = arr2D.m_col;
			if (!arr2D.m_data) {
				m_data = nullptr;
				return *this;
			}
			m_data = new T * [m_row];
			for (size_t j = 0; j < m_row; ++j) {
				m_data[j] = new T[m_col]();
				for (size_t k = 0; k < m_col; ++k) {
					m_data[j][k] = arr2D.m_data[j][k];
				}
			}
			return *this;
		}

		T*& operator[](size_t idx) {
			return m_data[idx];
		}

		operator T** () {
			return m_data;
		}

		~CArray2D() {
			if (m_data) {
				for (size_t j = 0; j < m_row; ++j) {
					delete[] m_data[j];
				}
				delete[] m_data;
				m_data = nullptr;
			}
		}
	};

	CArray3D(size_t layer, size_t row, size_t col) : m_layer(layer) {
		m_arr2D = new CArray2D[m_layer];
		for (size_t i = 0; i < m_layer; ++i) {
			m_arr2D[i] = CArray2D(row, col);
		}
	}

	CArray2D& operator[](size_t layer) {
		return m_arr2D[layer];
	}

	~CArray3D() {
		if (m_arr2D) {
			delete[] m_arr2D;
			m_arr2D = nullptr;
		}
	}

private:
	CArray2D* m_arr2D;
	size_t m_layer;
};

但是memset这个重要操作没法实现,测试代码有这个调用:

memset(a[1], -1, 20 * sizeof(int));
memset(a[1][1], 0, 5 * sizeof(int));

旨在对三维数组的某一层或者某一行赋值,按照我们的实现,对某一行赋值,也就是memset(a[1][1], 0, 5 * sizeof(int));是没有问题的。但是对某一层赋值是不行的,这是因为memset只能给一段连续的内存空间赋值。但是从二维数组的实现可以看到:

	m_data = new T * [row];
	for (size_t j = 0; j < row; ++j) {
		m_data[j] = new T[col]();
	}

这个二维数组,并非new在一块,地址间可能是分散的,所以需要用连续的存储,修改代码得到:

class CArray3D
{
	// 在此处补充你的代码
public:
	class CArray2D {
	private:
		T* m_data;
		size_t m_row, m_col;
	public:
		CArray2D(size_t row = 0, size_t col = 0) : m_row(row), m_col(col) {
			if (!row || !col) {
				m_data = nullptr;
				return;
			}
			m_data = new T[row * col];
		}

		CArray2D(const CArray2D& arr2D)
			: m_row(arr2D.m_row), m_col(arr2D.m_col) {
			if (arr2D.m_data) {
				m_data = new T[m_row * m_col];
				for (size_t j = 0; j < m_row * m_col; ++j) {
					m_data[j] = arr2D.m_data[j];
				}
			}
			else {
				m_data = nullptr;
			}
		}

		CArray2D& operator=(const CArray2D& arr2D) {
			if (this == &arr2D) { return *this; }
			if (m_data) {
				delete[] m_data;
			}
			m_row = arr2D.m_row, m_col = arr2D.m_col;
			if (!arr2D.m_data) {
				m_data = nullptr;
				return *this;
			}
			m_data = new T[m_row * m_col];
			for (size_t j = 0; j < m_row * m_col; ++j) {
				m_data[j] = arr2D.m_data[j];
			}
			return *this;
		}

		T* operator[](size_t idx) {
			return (m_data + idx * m_col);
		}

		operator T* () {
			return m_data;
		}

		~CArray2D() {
			if (m_data) {
				delete[] m_data;
				m_data = nullptr;
			}
		}
	};

	CArray3D(size_t layer, size_t row, size_t col) : m_layer(layer) {
		m_arr2D = new CArray2D[m_layer];
		for (size_t i = 0; i < m_layer; ++i) {
			m_arr2D[i] = CArray2D(row, col);
		}
	}

	CArray2D& operator[](size_t layer) {
		return m_arr2D[layer];
	}

	~CArray3D() {
		if (m_arr2D) {
			delete[] m_arr2D;
			m_arr2D = nullptr;
		}
	}

private:
	CArray2D* m_arr2D;
	size_t m_layer;
};

这里需要注意一点:核心思想是将二维数组变成了一维数组,但是在memset取行的时候,移动的步长是列数。因为赋值必须一定到对应行的开头,而移动的距离则是列数

思考:这样做有普适性吗?即:任意多维度的数组,都能这么做吗?其实有点离谱,memset的需求不是特别合理,建议调研openCV或者其他的包含多维张量的第三方库,看看是怎么实现的,我也没有很多研究

第四题:

这个题很简单,和之前的排序一样,用写一个函数对象,完成过滤器的功能。AC代码如下:

template<class T>
struct FilterClass {
	T m, n;
	FilterClass(T m_, T n_) : m(m_), n(n_) { }
	bool operator()(T val) {
		return (m < val) && (val < n);
	}
};

第五题:

浅用了一手lambda表达式,lambda表达式很重要,在后面再详细解说。AC代码如下:

[](double n1, double n2) {return n1 > n2; }

第六题:

写一个自己的ostream_iterator,吸取上次的经验,先去看看STL是怎么实现的:

	ostream_iterator& operator=(const _Ty& _Val) { // insert value into output stream, followed by delimiter
        *_Myostr << _Val;
        if (_Mydelim) {
            *_Myostr << _Mydelim;
        }

        return *this;
    }

    _NODISCARD ostream_iterator& operator*() noexcept /* strengthened */ {
        return *this;
    }

    ostream_iterator& operator++() noexcept /* strengthened */ {
        return *this;
    }

到这里大家就都明白了,参照STL,完成AC代码:

template<class T>
class myostream_iteraotr
{
	// 在此处补充你的代码
	string m_delim;
	ostream& m_out;

public:
	myostream_iteraotr(ostream& out, const string& delim)
		: m_delim(delim), m_out(out) { }

	myostream_iteraotr& operator++() {
		return *this;
	}

	myostream_iteraotr& operator*() {
		return *this;
	}

	template<class T_ = T>
	void operator=(const T_& val) {
		m_out << val << m_delim;
	}
};

详细解释这个代码:这个代码还是有一定难度的,首先明确功能:
1)要有前置++运算符函数,但显然并不做什么事情;
2)一个关键代码:*x = *s;,这既包括重载解引用运算,又包括重载赋值运算,但是解引用运算是无参的,所以只能在赋值运算上输出

第七题:

在这里插入图片描述
在这里插入图片描述

第七题分析:

这个题考察STL中list的用法,AC代码如下:

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

class List {
private:
	int m_id;
	list<int> m_list;
public:
	List(int id) : m_id(id) { }

	void addNum(int val) {
		m_list.push_back(val);
	}

	void unique() {
        m_list.sort();
		m_list.unique();
	}

	void out() {
		m_list.sort();
		for (auto& it : m_list) {
			cout << it << " ";
		}
		cout << endl;
	}

	void merge(List& rhs) {
		m_list.merge(rhs.m_list);
	}

	int getId() const {
		return m_id;
	}
};

List& findEleInVec(vector<List>& lst, int id) {
	for (List& cur : lst) {
		if (id == cur.getId()) {
			return cur;
		}
	}
}

int main() {
	vector<List> listVec;
	int T;
	cin >> T;
	string cmd;
	while (T--) {
		cin >> cmd;
		int id;
		if ("new" == cmd) {
			cin >> id;
			listVec.emplace_back(id);
		}
		else if ("add" == cmd) {
			int val;
			cin >> id >> val;
			findEleInVec(listVec, id).addNum(val);
		}
		else if ("out" == cmd) {
			cin >> id;
			findEleInVec(listVec, id).out();
		}
		else if ("merge" == cmd) {
			int id2;
			cin >> id >> id2;
			List& mg = findEleInVec(listVec, id2);
			findEleInVec(listVec, id).merge(mg);
		}
		else if ("unique" == cmd) {
			cin >> id;
			findEleInVec(listVec, id).unique();
		}
	}
	return 0;
}

有两个点需要注意
1)执行unique算法前,必须先排序,这是为了O(n)实现;
2)本题要求从小到大输出,记得排序。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值