前言
最近需求要求实现结构体的多字段排序功能,类似数据库的order by a,b,c这种。
想图个便利在网上找来找去也没找到能满足需求的现成内容,csdn上也有一些多字段排序的实现,但就是差在字段判断顺序不能随意更改.
例1: https://blog.youkuaiyun.com/lacoucou/article/details/105569434
例2: https://blog.youkuaiyun.com/qq_45721778/article/details/104693900
这让面对这个需求的我直接丧失了大半信心,但还有两天这功能马上就联调了…
具体需求
- 结构体以给定,里面有几个字段心知肚明
- 根据结构体里的字段进行升序排序
- 用于排序的字段对应值相同情况下用第二个字段判断,第二个字段值相同情况下用第三…(类似数据库order by)
- 用于排序的字段顺序可变
实现
虽然粗糙但暂时满足了需求.
1.确认结构体
比如我们有了这么个极其普通的结构体 现在要根据里面的 errorrate,costtime,worklevel
进行排序,但我们不知道到低先从哪个开始做判断.
struct Toy {
int id;
double errorrate;
double costtime;
int worklevel;
};
2.制定判断标识
所以,我定义了一个字符串,每个字符串来表示一个字段,比如:
字符串是"1" 时,我就先比较 costtime.
字符串是"2" 时,代表比较 errorrate.
字符串是"3" 时,代表比较 worklevel.
要比较多个字段, 所以就用vector来保存了这些数据,准备按vector里的顺序做判断
std::vector<std::string> sortflags;
sortflags.push_back("2"); //代表errorrate
sortflags.push_back("1"); //代表costtime
sortflags.push_back("3"); //代表worklevel
3.了解vector自定义排序
使用标准库的排序算法sort就很省时间,非常有用.就是可能会出现不满足严格的弱序规则时崩溃的问题(我就遇到了,然后又找到一篇文章https://blog.youkuaiyun.com/qq_41767116/article/details/113734605恍然大悟)
std::sort 支持对vector对象进行排序,还支持自定义的排序算法,这也是在找这功能时在csdn上查到的.
我从这位写的博客https://blog.youkuaiyun.com/aastoneaa/article/details/8471722 了解到std::sort如何使用自定义排序算法
include<algorithm> //需要这个头文件
等了解完我选择了写自定义类的方法也是上位播主文章中推荐的方法.
+++
+++
4.自定义排序
思绪回到第2个判断标识上,我们说到要根据不同的标识去做判断,归根结底还是每个字段都要进行单独的判断, 当上一个字段判断结果为相同时才会进入下一个字段的判断, 只是我们不知道顺序罢了.
所以先不管顺序,先写出每种字段的判断逻辑
// 防止忘记,留个结构体水字数
struct Toy {
int id;
double errorrate;
double costtime;
int worklevel;
};
const double EPS = 1.0e-6; //double值大于小于判断都需要精度
// 返回 0(item1 < item2) 或 1(item1 >= item2) 或 4(item1 == item2)
int sortkey1(const Toy& item1, const Toy& item2)
{
if (fabs(item1.costtime - item2.costtime) < EPS)
return 4;
else
return (item1.costtime + EPS) < item2.costtime;
}
int sortkey2(const Toy& item1, const Toy& item2)
{
if (fabs(item1.errorrate - item2.errorrate) < EPS)
return 4;
else
return (item1.errorrate + EPS) < item2.errorrate;
}
int sortkey3(const Toy& item1, const Toy& item2)
{
if (item1.worklevel == item2.worklevel)
return 4;
else
return item1.worklevel < item2.worklevel;
}
再就是根据我们制定的判断标识,具体做判断 , 关键在于
判断结果应该满足是每个字段值相同的情况下才返回下一个字段值的判断结果
判断结果应该满足是每个字段值相同的情况下才返回下一个字段值的判断结果
判断结果应该满足是每个字段值相同的情况下才返回下一个字段值的判断结果
bool operator()(const Toy& item1, const Toy& item2)
{
bool ret = false;
std::vector<int> tmpvec; //结果回收
for (auto sortkey_it : m_sortflags)
{
if (sortkey_it == "1")
tmpvec.push_back(sortkey1(item1,item2));
if (sortkey_it == "2")
tmpvec.push_back(sortkey2(item1, item2));
if (sortkey_it == "3")
tmpvec.push_back(sortkey3(item1, item2));
}
for (auto it : tmpvec )
{
if (it == 4) //表示上一个比较的字段值是相同的情况下才继续下一个字段的比较
{
ret = false;
continue; //如果字段比较结果相同,那就跳过此结果
}
ret = (bool)it;
break; //就返回第一个结果,管他是true还是false
}
return ret;
}
完整的自定义比较类如下:
class CompFunc {
public:
const double EPS = 1.0e-6;
CompFunc(const std::vector<std::string>& sortkeys)
{
m_sortflags = sortkeys; // 字段标志
}
bool operator()(const Toy& item1, const Toy& item2)
{
bool ret = false;
std::vector<int> tmpvec;
for (auto sortkey_it : m_sortflags)
{
if (sortkey_it == "1")
tmpvec.push_back(sortkey1(item1,item2));
if (sortkey_it == "2")
tmpvec.push_back(sortkey2(item1, item2));
if (sortkey_it == "3")
tmpvec.push_back(sortkey3(item1, item2));
}
for (auto it : tmpvec )
{
if (it == 4) //表示上一个比较的字段值是相同的情况下才继续下一个字段的比较
{
ret = false;
continue;
}
ret = (bool)it;
break;
}
return ret;
}
private:
std::vector<std::string> m_sortflags; //保存字段顺序标识
private:
int sortkey1(const Toy& item1, const Toy& item2)
{
if (fabs(item1.costtime - item2.costtime) < EPS)
return 4;
else
return (item1.costtime + EPS) < item2.costtime;
}
int sortkey2(const Toy& item1, const Toy& item2)
{
if (fabs(item1.errorrate - item2.errorrate) < EPS)
return 4;
else
return (item1.errorrate + EPS) < item2.errorrate;
}
int sortkey3(const Toy& item1, const Toy& item2)
{
if (item1.worklevel == item2.worklevel)
return 4;
else
return item1.worklevel < item2.worklevel;
}
};
5.测试数据和结果
namespace TESTSORT1 {
void test_vector_sort()
{
std::vector<Toy> vectoy;
Toy toys[10];
std::cout << "----------before----------" << std::endl;
// 初始化数据
for (int i = 9; i >= 0; --i)
{
toys[i].id = i;
toys[i].costtime = i;
toys[i].errorrate = i * 0.1 * i;
toys[i].worklevel = i + 1000;
vectoy.push_back(toys[i]);
std::cout << "id:" << toys[i].id << "\tErate:" << toys[i].errorrate << "\tCtime:" << toys[i].costtime << " \tWlevel:" << toys[i].worklevel << std::endl;
}
std::cout << "----------after----------" << std::endl;
// 数据稍微修改一下
vectoy[3].errorrate = vectoy[4].errorrate;
vectoy[3].costtime++;
vectoy[3].worklevel += 2;
vectoy[6].errorrate = vectoy[4].errorrate;
vectoy[6].costtime = 1;
vectoy[2].errorrate = vectoy[3].errorrate;
std::vector<std::string> sortflags;
// 字段顺序标识
sortflags.push_back("2"); //先判断errorrate
sortflags.push_back("1"); //再判断costtime
sortflags.push_back("3"); //再判断worklevel
//quicksort(vectoy,0, vectoy.size()-1, CompFunc(sortflags));
std::sort(vectoy.begin(), vectoy.end(), CompFunc(sortflags));
for (auto it : vectoy)
{
std::cout << "id:" << it.id << "\tErate:" << it.errorrate << "\tCtime:" << it.costtime << " \tWlevel:" << it.worklevel << std::endl;
}
}
}
结果:
6. 总结
需求分解,再分解,后整合
7.额外产物
正在学习template,试着做了一下,也能用来排序
template<typename T1,typename T2>
void quicksort(std::vector<T1>& itemArray, int low, int high, T2 compareFunc)
{
if (low < high)
{
int i = low;
int j = high;
//这个队列我只用第一个元素,因为如果是T1的话不知道怎么复制
std::vector<T1> pivot;
pivot.push_back(itemArray[i]);
while (i < j)
{
// i<j , pivot < array[j]
while (i < j && compareFunc(pivot[0],itemArray[j]))
{
--j;
}
if (i < j)
{
itemArray[i] = itemArray[j];
}
//i<j , pivot > array[i]
while (i < j && !compareFunc(pivot[0], itemArray[i]))
{
++i;
}
if (i < j)
{
itemArray[j] = itemArray[i];
}
}
itemArray[i] = pivot[0];
quicksort(itemArray, low, i - 1, compareFunc);
quicksort(itemArray, i + 1, high, compareFunc);
}
}