C++有关排序的问题

本文详细介绍了C++中sort函数在基本数据类型和自定义类型上的使用,包括升序和降序排列,以及如何通过函数指针、仿函数和运算符重载来定制排序规则。同时,讲解了priority_queue的用法,特别关注了自定义数据类型在大顶堆中的应用。
摘要由CSDN通过智能技术生成

1简述

C++中经常会遇到与排序相关的问题,最常见的就是sort函数和priority_queue容器。

对于基本数据类型,我们一般会按照升序或者降序来进行排列;而对于自定义的数据类型,我们通常需要自定义排序方案。

2 sort的使用

Sort函数使用模板: Sort(start,end,排序方法)

2.1 用于基本数据类型

以int类型为例。

1 ) 默认从小到大排列

sort默认下,也就是不适用第三个参数的时候,是升序排列。

//从小到大排列
vector<int> nums={13,4,6,1,23,5};
sort(nums.begin(),nums.end());


//这里注意,sort的前两个参数是迭代器类型;
//当传入指针作为迭代器的时候,第二个参数也要是尾后迭代器,所以这里是a+10
int a[10]={9,6,3,8,5,2,7,4,1,0};
sort(a,a+10,greater<int>());

2) 使用第三个参数实现从大到小排列

Sort函数的第三个参数可以用这样的语句告诉程序你所采用的排序原则

  • less<数据类型>():从小到大排序(这是默认使用的,所以一般不用)

  • greater<数据类型>():从大到小排序

注意:greater<>() 和 less<>() 都是头文件< functional >中的仿函数。库中写好了这俩仿函数,直接使用即可。(仿函数本质上是类对象,见STL源码解析)

//从大到小排列
vector<int> nums={13,4,6,1,23,5};
sort(nums.begin(),nums.end(),greater<int>());


//这里注意,sort的前两个参数是迭代器类型;
//当传入指针作为迭代器的时候,第二个参数也要是尾后迭代器,所以这里是a+10
int a[10]={9,6,3,8,5,2,7,4,1,0};
sort(a,a+10,greater<int>());

所以以后对基本类型进行从大到小排序,直接使用greater<>()第三个参数即可。

2.2 用于自定义数据类型

假设有一个自定义类:

class student
{
	public:
		int name;
		int socre;
};

现有一个vector< student > vec,现在需要根据name实现从大到小和从小到大排序。

1)使用函数指针

bool less(const student &s1,const student s2)
{
	return s1.name<s2.name;//从小到大排序
}
bool greater(const student &s1,const student s2)
{
	return s1.name>s2.name;//从大到小排序
}


sort(vec.begin(), vec.end(), less);
sort(vec.begin(), vec.end(), greater);

注意:

  • 这里的sort的第三个参数,是函数指针!
  • 当sort是在某一个类中使用的时候,比较函数(less和greater)必须要写在类的外部(全局区域)或声明为静态函数!!!
  • 当sort是在main中使用的时候,比较函数也要写在main外边(全局区域)。

2)使用仿函数

class less
{
  public:
    bool operator()(const Student& s1, const Student& s2)
    {
        return s1.name < s2.name; //从小到大排序
    }
};
 
sort(vec.begin(), vec.end(), less());

这里的sort的第三个参数是仿函数,本质上是一个less类对象。

3)自定义比较运算符

这个方式的话,与上面两个不一样,这需要修改Student类的定义,在类中重载比较运算符。当然也可以声明为友元,通常重载比较运算符函数就是声明为友元函数的!

class student
{
	public:
		int name;
		int socre;
		
		bool operator <(const Student & s2) const
		{
			return name < s2.name;//按name从小到大排序
		}
};

sort(vec.begin(),vec.end());

这个方法就是需要在类的内部做一定的修改。但是这个方法一般是最优先选择的。

4) 一个例子

#include <vector>
#include <algorithm>//包含sort和find算法
#include <iostream>
using namespace std;
class Test
{
 public:
	int a;
	int b;
	Test(int m_a, int m_b) {
		a = m_a;
		b = m_b;
	}
	//重载<运算符,先按a升序排列,若a相等,则按b升序排列;如果是降序就是重载">"运算符
	bool operator < (const Test& testobj) const
	{
		if (a < testobj.a)
			return true;
		else if (a == testobj.a)
		{
			if (b < testobj.b)
				return true;
			else
				return false;
		}
		else
			return false;
	}
	//重载 == 运算符
	bool operator == (const Test& testobj) const
	{
		//如果a相等,则判断为相等
		//return a == testobj.a;
		//如果a和b都相等,则判断为相等
		return (a == testobj.a) && (b == testobj.b);
	}
};

//类外定义一个比较函数compare
bool compare(const Test& t1, const Test& t2) {
	if (t1.a != t2.a)
		return t1.a < t2.a;
	else if(t1.b != t2.b)
		return t1.b < t2.b;
	return false;
}
int main()
{
	vector<Test> vec;
	vec.push_back(Test(1, 1));
	vec.push_back(Test(3, 6));
	vec.push_back(Test(2, 1));
	vec.push_back(Test(3, 4));

	//排序sort
	sort(vec.begin(), vec.end());//类中定义好排序规则
	//sort(vec.begin(), vec.end(), compare);//类外定义一个比较函数
	for (int i = 0; i< (int)vec.size(); ++i)
	{
		cout << vec[i].a <<" "<< vec[i].b <<endl;
	}

	//查找元素
	vector<Test>::iterator ite = find(vec.begin(), vec.end(), Test(3, 4));
	if(ite!=vec.end())
	{
	cout << "已找到" << ite->a << " " << ite->b << endl;
	}
	else {
		cout << "cannot find " << endl;
	}

	system("pause");
	return 0;
}

3 priority_queue的使用

虽然堆本质上是一个完全二叉树,但是他的底部是一个vector。
STL堆的底层是一个vector

定义:priority_queue<Type, Container, Functional>

Type 就是数据类型,Container 就是容器类型(Container必须是用数组实现的容器,比如vector,deque等等,但不能用 list。STL里面默认用的是vector),Functional 就是比较的方式,当需要用自定义的数据类型时才需要传入这三个参数,使用基本数据类型时,只需要传入数据类型,默认是大顶堆。

3.1 优先级队列存放的是基本数据类型

以int为例

1)定义大根堆

priority_queue<int> a;

//等同于priority_queue<int, vector<int>, less<int> > a;

2)定义小根堆

priority_queue<int, vector<int>, greater<int> > c;  //这样就是小顶堆

这里需要注意的是:

  • 如同上面的sort一样,默认是使用less,而不一样的是,sort使用less是进行从小到大的排序。而priority_queue则是使用less创建大根堆!!!
  • 另外最重要的一点是:sort是函数,所以,sort的第三个参数是类对象,因此可以传入仿函数(本质上是类对象);而priority_queue模板类,是一种类型,它的三个参数都是类型!!因此传入的应该是类型!

3.2 优先级队列存放的是自定义数据类型

假设存放的是Student类对象。

class Student
{
	public:
		int name;
		int socre;
	
		Student(int a,int b)
		{
			name=a;
			score=b;
		}
};

现在把n个student对象放到优先级队列中。大小比较策略是根据name的大小。

1)运算符重载

这个方式,要对类的定义进行修改,或者使用友元函数进行运算符的重载。

class Student
{
	public:
		int name;
		int socre;
		
		Student(int a,int b)//构造函数
		{
			name=a;
			score=b;
		}
		
		bool operator <(const Student & s2) const
		{
			return name < s2.name;//在sort中是按name从小到大排序,在priority_queue则会构建大根堆
		}
};

Student stu1(1,1);
Student stu1(3,1);
Student stu1(2,1);

priority_queue< Student > d;
d.push(stu1);
d.push(stu2);
d.push(stu3);

运算符重载是一个一劳永逸的方式。只要对类的运算符进行了重载,在涉及到对类对象进行大小比较的时候,就不需要去额外设计一些东西了。比如,类中如果进行了运算符的重载,那么对这个类对象使用sort等设计到大小比较排序函数、使用priority_queue等里面涉及到大小比较的数据结构的时候,都不用额外的考虑某些东西了。

2) 仿函数

class comp//重写仿函数
{
  public:
    bool operator()(const Student& s1, const Student& s2)
    {
        return s1.name < s2.name; 在sort中是按name从小到大排序,在priority_queue则会构建大根堆
    }
};

Student stu1(1,1);
Student stu1(3,1);
Student stu1(2,1);

// 这是错误的,comp不是类模板
//priority_queue<Student,vector<Student>,comp<Student>> d;
priority_queue<Student,vector<Student>,comp> d;
d.push(stu1);
d.push(stu2);
d.push(stu3);

实际上这个方法的本质和3.1节是一样的,只不过3.1用的是系统库里面自带的仿函数,而这里,我们根据自定义类型的具体情况,进行仿函数的重写。如同3.1节,这里我们重写了仿函数后,将Student类对象放入优先级队列中就如同将int类型的变量放入优先级队列一样。

如同3.1节,这里的第三个参数同样不是对象,而是类型!!

3) 第三个参数不能使用函数指针

函数指针是一个函数对象,因此不能像泛型算法那样,传入函数指针!
第三个参数只能是类型!!

4)一个例子

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


struct Student //运算符重载<
{
    int name;
    int score;
    Student(int a,int b) {
        name = a;
        score=b;
        }

    //方法1,运算符重载
    bool operator<(const Student& a) const
    {
        return name < a.name; //大顶堆
    }
};

//方法2
struct comp //重写仿函数
{
    bool operator() ( const Student a, const Student b) 
    {
        return a.name < b.name; //大顶堆
    }
};

int main() 
{
    Student stu1(1,1);
    Student stu2(3,1);
    Student stu3(2,1);

    priority_queue<Student> d;
    
    d.push(stu1);
    d.push(stu2);
    d.push(stu3);
    while (!d.empty()) 
    {
        cout << d.top().name << endl;
        d.pop();
    }
    cout << endl;

    priority_queue<Student,vector<Student>,comp> e;
    e.push(stu1);
    e.push(stu2);
    e.push(stu3);
    while (!e.empty()) 
    {
        cout << e.top().name << endl;
        e.pop();
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值