您的位置:首页 > 其它

常用算法 stl综合案例

2017-08-09 00:00 465 查看

函数对象(仿函数)

重载函数调用操作符的类,其对象常称为函数对象(function object),即它们是行为类似函数的对象,也叫仿函数(functor),其实就是重载“()”操作符,使得类对象可以像函数那样调用。

注意:

1.函数对象(仿函数)是一个类,不是一个函数。

2.函数对象(仿函数)重载了”() ”操作符使得它可以像函数一样调用。

分类:假定某个类有一个重载的operator(),而且重载的operator()要求获取一个参数,我们就将这个类称为“一元仿函数”(unary functor);相反,如果重载的operator()要求获取两个参数,就将这个类称为“二元仿函数”(binary functor)。

函数对象的作用主要是什么?STL提供的算法往往都有两个版本,其中一个版本表现出最常用的某种运算,另一版本则允许用户通过template参数的形式来指定所要采取的策略。

//函数对象是重载了函数调用符号的类
struct MyPrint
{
void operator()(int val)
{
cout << val << endl;
}
};

void test01()
{

//如何使用
MyPrint print01;
print01(10); //重载了()操作符的类实例化的对象,可以像普通函数那样调用,可以有参数 ,可以有返回值
}

//函数对象超出了普通函数的概念,可以保存函数的调用状态
struct HePrint
{
HePrint()
{
mCount = 0;
}
void operator()(int val)
{
cout << val << endl;
mCount++;
}
int mCount;
};
void test02()
{

HePrint print;
print(10);
print(20);
print(30);
print(40);
cout << print.mCount << endl;
}

//函数对象可以做参数和返回值
struct OurPrint
{
void operator()(int val)
{
cout << val << endl;
}
};

void doBusiness(OurPrint print)
{
print(20);
}

void test03()
{
//函数对象做参数
doBusiness(OurPrint());
}


谓词

谓词是指普通函数或重载的operator()返回值是bool类型的函数对象(仿函数)。如果operator接受一个参数,那么叫做一元谓词,如果接受两个参数,那么叫做二元谓词,谓词可作为一个判断式。

struct GreaterThanFive
{
bool operator()(int v)
{
return v > 5;
}
};

//一元谓词
void test01()
{
vector<int> v;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}

vector<int>::iterator ret = find_if(v.begin(), v.end(), GreaterThanFive());
if (ret == v.end())
{
cout << "没有找到!" << endl;
}
else
{
cout << "找到:" << *ret << endl;
}
}

//二元谓词
struct MyCompare
{
bool operator()(int v1, int v2)
{
return v1 > v2;
}
};

void test02()
{
vector<int> v;
srand((unsigned int)time(NULL));
for (int i = 0; i < 10; i++)
{
v.push_back(rand() % 100);
}

for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;

//排序算法
sort(v.begin(), v.end(), MyCompare());

for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}


内建函数对象

STL内建了一些函数对象。分为:算数类函数对象,关系运算类函数对象,逻辑运算类仿函数。这些仿函数所产生的对象,用法和一般函数完全相同,当然我们还可以产生无名的临时对象来履行函数功能。使用内建函数对象,需要引入头文件 #include<functional>。

6个算数类函数对象,除了negate是一元运算,其他都是二元运算。

template<class T> T plus<T>//加法仿函数
template<class T> T minus<T>//减法仿函数
template<class T> T multiplies<T>//乘法仿函数
template<class T> T divides<T>//除法仿函数
template<class T> T modulus<T>//取模仿函数
template<class T> T negate<T>//取反仿函数


6个关系运算类函数对象,每一种都是二元运算。

template<class T> bool equal_to<T>//等于
template<class T> bool not_equal_to<T>//不等于
template<class T> bool greater<T>//大于
template<class T> bool greater_equal<T>//大于等于
template<class T> bool less<T>//小于
template<class T> bool less_equal<T>//小于等于


逻辑运算类运算函数,not为一元运算,其余为二元运算。

template<class T> bool logical_and<T>//逻辑与
template<class T> bool logical_or<T>//逻辑或
template<class T> bool logical_not<T>//逻辑非

内建函数对象举例:

/*
template<class T> bool equal_to<T>//等于 template<class T> bool not_equal_to<T>//不等于 template<class T> bool greater<T>//大于 template<class T> bool greater_equal<T>//大于等于 template<class T> bool less<T>//小于 template<class T> bool less_equal<T>//小于等于*/

void test01()
{
equal_to<int> MyEqual;
plus<int> MyPlus;
if (MyEqual(10, 20))
{
cout << "相等!" << endl;
}
else
{
cout << "不相等!" << endl;
}

cout << "MyPlus:" << MyPlus(10, 20) << endl;

}

void test02()
{
vector<int> v;
srand((unsigned int)time(NULL));
for (int i = 0; i < 10; i++)
{
v.push_back(rand() % 100);
}

for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
sort(v.begin(), v.end(), greater<int>());

for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;

}


函数对象适配器

#include<iostream>
#include<vector>
#include <functional>  // 头文件
#include<algorithm>
using namespace std;

//函数适配器bind1st bind2nd
//现在我有这个需求 在遍历容器的时候,我希望将容器中的值全部加上100之后显示出来,怎么做哇?
struct myprint : public binary_function<int, int, void>
{   //二元函数对象 所以需要继承 binary_fucntion<参数类型,参数类型,返回值类型>
void operator()(int v1, int v2) const
{
cout << v1 + v2 << " ";
}
};
void test02()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);

//我们直接给函数对象绑定参数 编译阶段就会报错
//for_each(v.begin(), v.end(), bind2nd(myprint(),100));
//如果我们想使用绑定适配器,需要我们自己的函数对象继承binary_function 或者 unary_function
//根据我们函数对象是一元函数对象 还是二元函数对象
for_each(v.begin(), v.end(), bind2nd(myprint(), 100));
cout << endl;

//总结:  bind1st和bind2nd区别?
//bind1st : 将参数绑定为函数对象的第一个参数
//bind2nd : 将参数绑定为函数对象的第二个参数
//bind1st bind2nd将二元函数对象转为一元函数对象
}

//函数对象适配器 not1 not2
struct myprint02
{
void operator()(int v1) const
{
cout << v1 << " ";
}
};
void test03()
{

vector<int> v;
v.push_back(2);
v.push_back(1);
v.push_back(5);
v.push_back(4);

vector<int>::iterator it = find_if(v.begin(), v.end(), not1(bind2nd(less_equal<int>(), 2)));
cout << "it:" << *it << endl;
sort(v.begin(), v.end(), not2(greater<int>()));

for_each(v.begin(), v.end(), myprint02());
cout << endl;

//not1 对一元函数对象取反
//not2 对二元函数对象取反
}

//如何给一个普通函数使用绑定适配器(bind1st bind2nd)绑定一个参数?
//ptr_fun
void myprint04(int v1, int v2)
{
cout << v1 + v2 << " ";
}
void test04()
{

vector<int> v;
v.push_back(2);
v.push_back(1);
v.push_back(5);
v.push_back(4);

//1 将普通函数适配成函数对象
//2 然后通过绑定器绑定参数
for_each(v.begin(), v.end(), bind2nd(ptr_fun(myprint04), 100));
cout << endl;

//总结: ptr_fun 将普通函数转变为函数对象
}

//mem_fun mem_fun_ref
//如果我们容器中存储的是对象或者对象指针,如果能指定某个成员函数处理成员数据。
class student
{
public:
student(string name, int age) :name(name), age(age) {}
void print()
{
cout << "name:" << name << " age:" << age << endl;
}
void print2(int a)
{
cout << "name:" << name << " age:" << age << " a:" << a << endl;
}
int age;
string name;
};
void test05()
{
//mem_fun : 如果存储的是对象指针,需要使用mem_fun
vector<student*> v;
student* s1 = new student("zhaosi", 10);
student* s2 = new student("liuneng", 20);
student* s3 = new student("shenyang", 30);
student* s4 = new student("xiaobao", 40);

v.push_back(s1);
v.push_back(s2);
v.push_back(s3);
v.push_back(s4);

for_each(v.begin(), v.end(), mem_fun(&student::print));
cout << "-----------------------------" << endl;

//mem_fun_ref : 如果存储的是对象,需要使用mem_fun_ref

vector<student> v2;
v2.push_back(student("zhaosi", 50));
v2.push_back(student("liuneng", 60));
v2.push_back(student("shenyang", 70));
v2.push_back(student("xiaobao", 80));

for_each(v2.begin(), v2.end(), mem_fun_ref(&student::print));
}


算法概述

算法主要是由头文件<algorithm> <functional> <numeric>组成。

<algorithm>是所有STL头文件中最大的一个,其中常用的功能涉及到比较,交换,查找,遍历,复制,修改,反转,排序,合并等...

<numeric>体积很小,只包括在几个序列容器上进行的简单运算的模板函数.

<functional> 定义了一些模板类,用以声明函数对象。

常用遍历算法

/*
遍历算法 遍历容器元素
@param beg 开始迭代器
@param end 结束迭代器
@param _callback  函数回调或者函数对象
@return 函数对象
*/
for_each(iterator beg, iterator end, _callback);
/*
transform算法 将指定容器区间元素搬运到另一容器中
注意 : transform 不会给目标容器分配内存,所以需要我们提前分配好内存
@param beg1 源容器开始迭代器
@param end1 源容器结束迭代器
@param beg2 目标容器开始迭代器
@param _cakkback 回调函数或者函数对象
@return 返回目标容器迭代器
*/
transform(iterator beg1, iterator end1, iterator beg2, _callbakc)

for_each:

/*

template<class _InIt,class _Fn1> inline
void for_each(_InIt _First, _InIt _Last, _Fn1 _Func)
{
for (; _First != _Last; ++_First)
_Func(*_First);
}

*/

//普通函数
void print01(int val)
{
cout << val << " ";
}
//函数对象
struct print001
{
void operator()(int val)
{
cout << val << " ";
}
};

//for_each算法基本用法
void test01()
{

vector<int> v;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}

//遍历算法
for_each(v.begin(), v.end(), print01);
cout << endl;

for_each(v.begin(), v.end(), print001());
cout << endl;

}

struct print02
{
print02()
{
mCount = 0;
}
void operator()(int val)
{
cout << val << " ";
mCount++;
}
int mCount;
};

//for_each返回值
void test02()
{

vector<int> v;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}

print02& p = for_each(v.begin(), v.end(), print02());
cout << endl;
cout << p.mCount << endl;
}

struct print03 : public binary_function<int, int, void>
{
void operator()(int val, int bindParam) const
{
cout << val + bindParam << " ";
}
};

//for_each绑定参数输出
void test03()
{
vector<int> v;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}

for_each(v.begin(), v.end(), bind2nd(print03(), 100));
}

transform:

//transform 将一个容器中的值搬运到另一个容器中
/*
template<class _InIt, class _OutIt, class _Fn1> inline
_OutIt _Transform(_InIt _First, _InIt _Last,_OutIt _Dest, _Fn1 _Func)
{

for (; _First != _Last; ++_First, ++_Dest)
*_Dest = _Func(*_First);
return (_Dest);
}

template<class _InIt1,class _InIt2,class _OutIt,class _Fn2> inline
_OutIt _Transform(_InIt1 _First1, _InIt1 _Last1,_InIt2 _First2, _OutIt _Dest, _Fn2 _Func)
{
for (; _First1 != _Last1; ++_First1, ++_First2, ++_Dest)
*_Dest = _Func(*_First1, *_First2);
return (_Dest);
}
*/

struct transformTest01
{
int operator()(int val)
{
return val + 100;
}
};
struct print01
{
void operator()(int val)
{
cout << val << " ";
}
};
void test01()
{
vector<int> vSource;
for (int i = 0; i < 10; i++)
{
vSource.push_back(i + 1);
}

//目标容器
vector<int> vTarget;
//给vTarget开辟空间
vTarget.resize(vSource.size());
//将vSource中的元素搬运到vTarget
vector<int>::iterator it = transform(vSource.begin(), vSource.end(), vTarget.begin(), transformTest01());
//打印
for_each(vTarget.begin(), vTarget.end(), print01()); cout << endl;

}

//将容器1和容器2中的元素相加放入到第三个容器中
struct transformTest02
{
int operator()(int v1, int v2)
{
return v1 + v2;
}
};
void test02()
{

vector<int> vSource1;
vector<int> vSource2;
for (int i = 0; i < 10; i++)
{
vSource1.push_back(i + 1);
}

//目标容器
vector<int> vTarget;
//给vTarget开辟空间
vTarget.resize(vSource1.size());
transform(vSource1.begin(), vSource1.end(), vSource2.begin(), vTarget.begin(), transformTest02());
//打印
for_each(vTarget.begin(), vTarget.end(), print01()); cout << endl;
}


常用查找算法:

/*
find算法 查找元素
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param value 查找的元素
@return 返回查找元素的位置
*/
find(iterator beg, iterator end, value)
/*
adjacent_find算法 查找相邻重复元素
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param  _callback 回调函数或者谓词(返回bool类型的函数对象)
@return 返回相邻元素的第一个位置的迭代器
*/
adjacent_find(iterator beg, iterator end, _callback);
/*
binary_search算法 二分查找法
注意: 在无序序列中不可用
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param value 查找的元素
@return bool 查找返回true 否则false
*/
bool binary_search(iterator beg, iterator end, value);
/*
find_if算法 条件查找
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param  callback 回调函数或者谓词(返回bool类型的函数对象)
@return bool 查找返回true 否则false
*/
find_if(iterator beg, iterator end, _callback);
/*
count算法 统计元素出现次数
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param  value回调函数或者谓词(返回bool类型的函数对象)
@return int返回元素个数
*/
count(iterator beg, iterator end, value);
/*
count算法 统计元素出现次数
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param  callback 回调函数或者谓词(返回bool类型的函数对象)
@return int返回元素个数
*/
count_if(iterator beg, iterator end, _callback);


常用排序算法

/*
merge算法 容器元素合并,并存储到另一容器中
@param beg1 容器1开始迭代器
@param end1 容器1结束迭代器
@param beg2 容器2开始迭代器
@param end2 容器2结束迭代器
@param dest  目标容器开始迭代器
*/
merge(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest)
/*
sort算法 容器元素排序
注意:两个容器必须是有序的
@param beg 容器1开始迭代器
@param end 容器1结束迭代器
@param _callback 回调函数或者谓词(返回bool类型的函数对象)
*/
sort(iterator beg, iterator end, _callback)
/*
sort算法 对指定范围内的元素随机调整次序
@param beg 容器开始迭代器
@param end 容器结束迭代器
*/
random_shuffle(iterator beg, iterator end)
/*
reverse算法 反转指定范围的元素
@param beg 容器开始迭代器
@param end 容器结束迭代器
*/
reverse(iterator beg, iterator end)


常用拷贝和替换算法:

/*
copy算法 将容器内指定范围的元素拷贝到另一容器中
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param dest 目标容器结束迭代器
*/
copy(iterator beg, iterator end, iterator dest)
/*
replace算法 将容器内指定范围的旧元素修改为新元素
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param oldvalue 旧元素
@param oldvalue 新元素
*/
replace(iterator beg, iterator end, oldvalue, newvalue)
/*
replace_if算法 将容器内指定范围满足条件的元素替换为新元素
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param callback函数回调或者谓词(返回Bool类型的函数对象)
@param oldvalue 新元素
*/
replace_if(iterator beg, iterator end, _callback, newvalue)
/*
swap算法 互换两个容器的元素
@param c1容器1
@param c2容器2
*/
swap(container c1, container c2)


常用算数生成算法:

/*
accumulate算法 计算容器元素累计总和
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param value累加值
*/
accumulate(iterator beg, iterator end, value)
/*
fill算法 向容器中添加元素
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param value t填充元素
*/
fill(iterator beg, iterator end, value)


常用集合算法:

/*
set_intersection算法 求两个set集合的交集
注意:两个集合必须是有序序列
@param beg1 容器1开始迭代器
@param end1 容器1结束迭代器
@param beg2 容器2开始迭代器
@param end2 容器2结束迭代器
@param dest  目标容器开始迭代器
@return 目标容器的最后一个元素的迭代器地址
*/
set_intersection(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest)
/*
set_union算法 求两个set集合的并集
注意:两个集合必须是有序序列
@param beg1 容器1开始迭代器
@param end1 容器1结束迭代器
@param beg2 容器2开始迭代器
@param end2 容器2结束迭代器
@param dest  目标容器开始迭代器
@return 目标容器的最后一个元素的迭代器地址
*/
set_union(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest)
/*
set_difference算法 求两个set集合的差集
注意:两个集合必须是有序序列
@param beg1 容器1开始迭代器
@param end1 容器1结束迭代器
@param beg2 容器2开始迭代器
@param end2 容器2结束迭代器
@param dest  目标容器开始迭代器
@return 目标容器的最后一个元素的迭代器地址
*/
set_difference(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest)


5. STL综合案例(学校演讲比赛)

学校演讲比赛介绍

1)某市举行一场演讲比赛( speech_contest ),共有24个人参加。比赛共三轮,前两轮为淘汰赛,第三轮为决赛。

2)比赛方式:分组比赛,每组6个人;选手每次要随机分组,进行比赛;

第一轮分为4个小组,每组6个人。比如100-105为一组,106-111为第二组,依次类推,

每人分别按照抽签(draw)顺序演讲。当小组演讲完后,淘汰组内排名最后的三个选手,然后继续下一个小组的比赛。

第二轮分为2个小组,每组6人。比赛完毕,淘汰组内排名最后的三个选手,然后继续下一个小组的比赛。

第三轮只剩下6个人,本轮为决赛,选出前三名。

4)比赛评分:10个评委打分,去除最低、最高分,求平均分

每个选手演讲完由10个评委分别打分。该选手的最终得分是去掉一个最高分和一个最低分,求得剩下的8个成绩的平均分。

选手的名次按得分降序排列,若得分一样,按参赛号升序排名。

用STL编程,求解这个问题

1) 请打印出所有选手的名字与参赛号,并以参赛号的升序排列。

2) 打印每一轮比赛后,小组比赛成绩和小组晋级名单

3) 打印决赛前三名,选手名称、成绩。

需求分析

//产生选手 ( ABCDEFGHIJKLMNOPQRSTUVWXYZ ) 姓名、得分;选手编号

//第1轮 选手抽签 选手比赛 查看比赛结果

//第2轮 选手抽签 选手比赛 查看比赛结果

//第3轮 选手抽签 选手比赛 查看比赛结果

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include <vector>
#include <map>
#include <string>
#include <algorithm>
#include <deque>
#include <functional>
#include <numeric>
#include <ctime>
using namespace std;

/*
需求分析:
1) 产生选手 ( ABCDEFGHIJKLMNOPQRSTUVWX ) 姓名、得分;选手编号

2) 第1轮	选手抽签 选手比赛 查看比赛结果
3) 第2轮	选手抽签 选手比赛 查看比赛结果
4) 第3轮	选手抽签 选手比赛 查看比赛结果
*/

class Speaker
{
public:
string mName; //姓名
int mScore[3]; //分数
};

void createSpeaker(vector<int>&v, map<int, Speaker>&m)
{
string nameSeed = "ABCDEFGHIJKLMNOPQRSTUVWX";
for (int i = 0; i < 24; i++)
{
string name = "选手";
name += nameSeed[i];

Speaker sp;
sp.mName = name;
//默认三轮的分数都为0
for (int j = 0; j < 3; j++)
{
sp.mScore[j] = 0;
}
//v容器中放入的是选手编号
v.push_back(100 + i);
m.insert(make_pair(100 + i, sp));
}
}

void speechDraw(vector<int>&v)
{
random_shuffle(v.begin(), v.end());
}

//演讲比赛方法
void speechContest(int index, vector<int>&v1, map<int, Speaker>&m, vector<int>&v2)
{
//创建一个临时容器 保存小组的分数
multimap<int, int, greater<int>>groupScore; //key 是分数 value 是选手编号  , greater<int>()从大到小排序
int num = 0;
for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
{
num++;
//评委打分
deque<int>d;
for (int i = 0; i < 10; i++)
{
int score = rand() % 41 + 60; // 60 ~100
d.push_back(score);
}
//对分数进行排序
sort(d.begin(), d.end(), greater<int>()); //从大到小
//去除最高分和最低分
d.pop_back();  //最低分
d.pop_front(); //最高分

//总分
int sum = accumulate(d.begin(), d.end(), 0);
//平均分
int avg = sum / d.size();

//打印选手的分数
//cout << "选手姓名" << m[*it].mName << " 获取平均分:" << avg << endl;
//将分数放入到m容器中 选手身上

m[*it].mScore[index - 1] = avg;

groupScore.insert(make_pair(avg, *it));

//每6人一组 进行筛选
if (num % 6 == 0)
{

////查看小组比赛成绩
//cout << "小组比赛成绩如下" << endl;
//for (multimap<int, int, greater<int>>::iterator mit = groupScore.begin(); mit != groupScore.end(); mit++)
//{
//	cout << "编号" << mit->second << " 姓名:" << m[mit->second].mName << " 成绩: " << m[mit->second].mScore[index - 1] << endl;
//}

//取临时容器的前三个数据 ,进入到下一轮比赛
int count = 0;
for (multimap<int, int, greater<int>>::iterator mit = groupScore.begin(); mit != groupScore.end() && count < 3; mit++, count++)
{
v2.push_back((*mit).second);
}

groupScore.clear();
}
}
}

void showScore(int index, vector<int>&v, map<int, Speaker>&m)
{
cout << "第" << index << "轮比赛成绩如下:" << endl;

for (map<int, Speaker>::iterator it = m.begin(); it != m.end(); it++)
{
cout << "选手编号: " << it->first << " 选手姓名 " << it->second.mName << " 分数: " << it->second.mScore[index - 1] << endl;
}

cout << "晋级选手编号" << endl;
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << endl;
}
}

int main() {

srand((unsigned int)time(NULL));

//创建容器 放选手编号
vector<int>v;

//创建容器 对应的放入选手编号以及具体的选手
map<int, Speaker>m;

//创建24名选手
createSpeaker(v, m);

//抽签
speechDraw(v);

//比赛
//v2 容器存放 进入第二轮比赛的人员的编号
vector<int>v2;
speechContest(1, v, m, v2);

//打印成绩
showScore(1, v2, m);

//第二轮比赛
speechDraw(v2);
vector<int>v3; // 存放进入第三轮的人员编号容器
speechContest(2, v2, m, v3);
showScore(2, v3, m);

//第三轮比赛
speechDraw(v3);
vector<int>v4; // 存放进入第三轮的人员编号容器
speechContest(3, v3, m, v4);
showScore(3, v4, m);

////测试
//for (map<int, Speaker>::iterator it = m.begin(); it != m.end(); it++)
//{
//	cout << "选手姓名:" << it->second.mName << endl;
//}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息