46、排序的概念

排序的一般定义:排序是计算机经常进行的一种操作,其目的是将一组无序数组调整为有序数组。

数学定义:

假设n个数据元素的序列{R1,R2,....,Rn},其相应的关键字序列为{k1,k2,...,kn}

这些关键字可以进行比较,即:存在关系

kp1<=kp2<=...

按此固有关系将上式记录为:

{RP1, Rp2,Rp3,...Rpn}的操作成为排序。

排序的稳定性:

如果在序列中有两个元素ri和rj,他们的关键字ki==kj,且在排序之前,对象ri排在rj前面;如果在排序之后,对象ri仍在对象rj的前面,则称这个排序是稳定的,否则称为不稳定的。

多关键字排序:排序时需要比较的关键字多于一个:

排序结果首先按关键字1进行排序;

当关键字1相同时按关键字2进行排序;...

当关键字n-1相同时按关键字n进行排序。

#include <iostream>
#include "Wobject.h"
using namespace std;
using namespace WSlib;
struct Test:public Wobject
{
int key1;
int key2;
Test(int k1,int k2)
{
key1=k1;
key2=k2;
}
bool operator ==(const Test& t)
{
return((key1==t.key1)&&(key2==t.key2));
}
bool operator !=(const Test& t)
{
return(! (*this==t));
}
bool operator <(const Test& t)
{
return ((key1<t.key1)||(key1==t.key1)&&(key2<t.key2));
}
bool operator >=(const Test& t)
{
return (!(*this<t));
}
bool operator >(const Test& t)
{
return ((key1>t.key1)||(key1==t.key1)&&(key2>t.key2));
}
bool operator <=(const Test& t)
{
return (!(*this>t));
}
};
int main()
{
Test t1(3,4);
Test t2(3,5);
cout<<(t1>t2)<<endl;
return 0;
}

关键操作:比较,交换

排序的审判:时间性能比较和交换的数量

辅助存储空间:为完成排序操作需要的额外的存储空间,必要时可以“空间换时间”

算法的实现复杂性:过于复杂的排序法可能影响可读性和可维护性。

小结:排序是无序到有序的过程,排序具有稳定性,是选择排序算法的因素之一,比较和交换是排序的基本操作,多关键字排序与但关键字排序无本质区别,排序的时间性能是区分算法好坏的主要因素。

47、选择排序和插入排序 (时间复杂度都为o(n^2))

(1)选择排序(不稳定)

基本思想:每次从后面n-i个待排的数据元素中选出关键字最小的元素,作为有序元素序列第i个元素。

/*选择排序
template <typename T>
static void Select(T array[],int len)
{
for(int i=0;i<len;i++)
{
int min=i;
for(int j=i+1;j<len;j++)
{
if(array[min]>array[j])//从大到小改为小于号
{
min=j;
}
}
if(min!=i)
{
Swap(array[i],array[min]);
}
}
}
*/
template <typename T>
static void Select(T array[],int len,bool min2max=true)
{
for(int i=0;i<len;i++)
{
int min=i;
for(int j=i+1;j<len;j++)
{
if(min2max ?(array[min]>array[j]):(array[min]<array[j]))  
{
min=j;
}
}
if(min!=i)
{
Swap(array[i],array[min]);
}
}
}
/**********************************************
#include <iostream>
#include "Sort.h"
using namespace std;
using namespace WSlib;
int main()
{
int array[]={3,1,2,5,4};
Sort::Select(array,5);
for(int i=0;i<5;i++)
{
cout<<array[i]<<endl;
}
return 0;
}

*********************************************/

(2)插入排序 (稳定)

基本思想:当插入第i(i>=1)个数据元素时,前面的v[0],v[1],...v[i-1]已经排好序,这时,用v[i]的关键字与v[i-1],v[i-2],...,v[0]的关键字进行比较,找到位置后将v[i]插入,原来位置上的对象向后顺移。

/*插入排序*/
template <typename T>
static void Insert(T array[],int len,bool min2max=true)
{
for(int i=1;i<len;i++) //没必要从0开始,因为一个元素也是有序的
{
int k=i;
T e=array[i];
for(int j=i-1; (j>=0)&&(min2max? (array[j]>e):(array[j]<e)) ; j--)
{
array[j+1]=array[j];
k=j;
}
if(k != i)
{
array[k]=e; //相等不需要赋值 
}
}
}
//以上是有判断大到小还是小到大
/*template <typename T>
static void Insert(T array[],int len)
{
for(int i=1;i<len;i++) //没必要从0开始,因为一个元素也是有序的
{
int k=i;
T e=array[i];
for(int j=i-1; (j>=0)&&(array[j]>e); j--)
{
array[j+1]=array[j];
k=j;
}
if(k != i)
{
array[k]=e; //相等不需要赋值 
}
}
}
*/
//上边是改进,下边是改进前
/*
for(int i=1;i<len;i++) 
{
int k=i;
T e=array[i];
for(int j=i-1;j>=0;j--)
{
if(array[j]>e)
{
array[j+1]=array[j];
k=j;
}
else
{
break;
}
}
if(k != i)
{
array[k]=e; //相等不需要赋值 
}
}
*/
/*****************插入排序测试*****************************
#include <iostream>
#include "Sort.h"
using namespace std;
using namespace WSlib;
int main()
{
int array[]={3,1,2,5,4};
Sort::Insert(array,5,false);
for(int i=0;i<5;i++)
{
cout<<array[i]<<endl;
}
return 0;
}

************************************************/

小结:选择排序每次选择未排元素中的最小元素。插入排序每次将第i个元素插入前面i-1个已排元素中。选择排序是不稳定的排序法,插入排序是稳定的排序方法。选择排序和插入排序的时间复杂度为o(n^2).

48、冒泡排序和希尔排序

(3)冒泡排序  o(n^2) 稳定

 每次从后向前进行(假设为第i次),j=n-1,n-2,...,i,两两比较 v[j-1] 和 v[j]的关键字,如果发生逆序,则交换v[j-1]和v[j]。

第i次冒泡排序:将最小元素放到第i个位置上。

template <typename T>
static void Bubble(T array[],int len, bool min2max=true)
{
bool exchange=true;
for(int i=0;(i<len)&&exchange;i++)
{
exchange=false;
for(int j=len-1;j>i;j--)
{
if(min2max? array[j]<array[j-1]:array[j-1]<array[j])
{
Swap(array[j],array[j-1]);
exchange=true;
}
}
}
}
};
}
#endif
/*******************************************************
#include <iostream>
#include "Sort.h"
using namespace std;
using namespace WSlib;
int main()
{
int array[]={3,1,2,5,4};
Sort::Bubble(array,5,false); //设置静态成员函数的好处,直接通过类名调用,不用定义对象
for(int i=0;i<5;i++)
{
cout<<array[i]<<endl;
}
return 0;
}

******************************************************/

(4)希尔排序  o(n^1.5)  不稳定

基本思想:将待排序列划分为若干组,在每一组内进行插入排序,以使整个序列基本有序,然后再对整个序列进行插入排序。

例如:将n个元素分成d个子序列:

{R[1],R[1+d],R[1+2d],...,R[1+kd]}

{R[2],R[2+d],R[2+2d],...,R[2+kd]}

...

{R[d],R[d+d],R[d+2d],...,R[(1+k)d]}

其中,d称为增量,它的值在排序过程中从大到小逐渐缩小,直至最后一趟排序减为1。

template <typename T>
static void Shell(T array[],int len,bool min2max=true)
{
int d=len;
do
{
d=d/3+1;
for(int i=d;i<len;i+=d)
{
int k=i;
T e=array[i];
for(int j=i-d;(j>=0)&&(min2max? array[j]>e:array[j]<e);j-=d)
{
array[j+d]=array[j];
k=j;
}
if(k != i)
{
array[k]=e;
}
}
}
while(d>1);
}
/**************************************************
#include <iostream>
#include "Sort.h"
using namespace std;
using namespace WSlib;
int main()
{
int array[]={3,1,2,5,4};
Sort::Shell(array,5,false); 
for(int i=0;i<5;i++)
{
cout<<array[i]<<endl;
}
return 0;

}

**************************************************/

作业:如何实现划分之后使用冒泡排序?

小结:冒泡排序每次从后向前将较小的元素交互到位,冒泡排序是一种稳定的排序法,复杂度为o(n^2),希尔排序通过分组的方式进行多次插入排序,希尔排序是一种不稳定的排序法,复杂度为o(n^1.5).

49、归并排序和快速排序

(5)归并排序

将两个或两个以上的有序序列合并成一个新的有序序列,有序序列v[0]...v[m]和v[m+1]...v[-1]==>v[0]...v[n-1]

这种归并方法称为2路归并。

将3个有序序列归并为一个新的有序序列,称为3路归并。

将N个有序序列归并为一个新的有序序列,称为N路归并。

将多个有序序列归并为一个新的有序序列,称为多路归并。

template <typename T> //合并两路
static void Merge(T src[],T helper[],int begin,int mid,int end,bool min2max=true)
{
int i=begin;
int j=mid+1;
int k=begin;
while((i<=mid)&&(j<=end))
{
if(min2max? (src[i]<src[j]):(src[i]>src[j]))
{
helper[k++]=src[i++];
}
else
{
helper[k++]=src[j++];
}
}
while(i<=mid)
{
helper[k++]=src[i++];
}
while(j<=end)
{
helper[k++]=src[j++];
}
for(i=begin;i<=end;i++)
{
src[i]=helper[i];
}
}
template <typename T>
static void Merge(T src[],T helper[],int begin,int end, bool min2max=true)
{
if(begin<end)
{
int mid=(begin+end)/2;
Merge(src,helper,begin,mid,min2max);//对左边排序
Merge(src,helper,mid+1,end,min2max);  //对右边排序
Merge(src,helper,begin,mid,end,min2max); //合并两组数据
}

}

/*归并排序*/
template <typename T>
static void Merge(T array[],int len,bool min2max=true)
{
T* helper=new T[len];
if(helper!=NULL)
{
Merge(array,helper,0,len-1,min2max);
}
delete[] helper;

}

/************************************************

nclude <iostream>
#include "Sort.h"
using namespace std;
using namespace WSlib;
int main()
{
int array[]={9,5,8,7,3,6,1,2,5,4};
Sort::Merge(array,10); 
for(int i=0;i<10;i++)
{
cout<<array[i]<<endl;
}
return 0;

}

***************************************************/

(6)快速排序

基本思想:任取序列中的某个数据元素作为基准将整个序列划分为左右两个子序列:

左侧子序列中的所有元素都小于或等于基准元素。

右侧序列中的所有元素都大于基准元素

基准元素排在这两个子序列中间

分别对这两个子序列重复进行划分,直到所有的数据元素都排在相应位置上为止。

template <typename T>
static int Partition(T array[],int begin,int end,bool min2max)
{
T pv=array[begin];
while(begin<end)
{
while((begin<end)&&(min2max?(array[end]>pv):(array[end]<pv)))
{
end--;
}
Swap(array[begin],array[end]);
while((begin<end)&&(min2max?(array[end]<=pv):(array[end]>=pv)))
{
begin++;
}
Swap(array[begin],array[end]);
}
array[begin]=pv;
return begin;
}
template <typename T>
static void Quick(T array[],int begin,int end, bool min2max=true)
{
if(begin<end)
{
int pivot=Partition(array,begin,end,min2max);
Quick(array,begin,pivot-1,min2max);
Quick(array,pivot+1,end,min2max);
}

}

template <typename T>
static void Quick(T array[],int len,bool min2max=true)
{
Quick(array,0,len-1,min2max);
}
/**************************************************
int array[]={9,5,8,7,3,6,1,2,5,4};
Sort::Quick(array,10,false); 
for(int i=0;i<10;i++)
{
cout<<array[i]<<endl;
}
return 0;

*************************************************/

小结:归并排序需要额外的辅助空间才能完成,空间复杂度为o(n)

归并排序的时间复杂度为o(n*logn),是一种稳定的排序法

快速排序通过递归的方式对排序问题进行划分,快速排序的时间复杂度为o(n*logn),是一种不稳定的排序法。

50、应用实例(计算时间)

排序类(Sort)与数组类(Array)的关系

新增的成员函数:array(): T* ( 数组首地址)

**********************50***************************/
template <typename T>
static void Select(Array<T>& array,bool min2max=true)
{
Select(array.array(),array.length(),min2max);
}
template <typename T>
static void Insert(Array<T>& array,bool min2max=true)
{
Insert(array.array(),array.length(),min2max);
}
template <typename T>
static void Bubble(Array<T>& array,bool min2max=true)
{
Bubble(array.array(),array.length(),min2max);
}
template <typename T>
static void Shell(Array<T>& array,bool min2max=true)
{
Shell(array.array(),array.length(),min2max);
}
template <typename T>
static void Merge(Array<T>& array,bool min2max=true)
{
Merge(array.array(),array.length(),min2max);
}
template <typename T>
static void Quick(Array<T>& array,bool min2max=true)
{
Quick(array.array(),array.length(),min2max);
}
};
/***********排序方法应用到数组类*********************
#include <iostream>
#include "Sort.h"
#include "StaticArray.h"
using namespace std;
using namespace WSlib;
int main()
{
StaticArray<double,5> sa;
for(int i=0;i<5;i++)
{
sa[i]=i;
}
Sort::Insert(sa,false); 
for(int i=0;i<5;i++)
{
cout<<sa[i]<<endl;
}
return 0;
}

*************************************************/

当待排序数据元素为体积庞大的对象时,如何提高排序效率?

问题分析:

排序过程中不可避免的需要进行交换操作,它的本质为数据元素间的相互复制,当数据元素较大时,交换操作耗时巨大。

解决方案:代理模式

1.为待排数据元素设置代理对象。

2.对代理对象所组成的序列进行排序。

3.需要访问有序数组时,通过访问代理序列完成。

//代理模式提高效率
#include <iostream>
#include "Sort.h"
#include <ctime> //计时头文件
using namespace std;
using namespace WSlib;
struct Test:public Wobject
{
int id;
int data1[1000];
double data2[500];
bool operator <(const Test& obj)
{
return id<obj.id;
}
bool operator >= (const Test& obj)
{
return id>=obj.id;
}
bool operator> (const Test& obj)
{
return id>obj.id;
}
bool operator<= (const Test& obj)
{
return id<=obj.id;
}
};
//定义代理类
class TestProxy:public Wobject
{
protected:
Test* m_pTest;
public:
int id()
{
return m_pTest->id;
}
int* data1()
{
return m_pTest->data1;
}
double* data2()
{
return m_pTest->data2;
}
//请出委托者
Test& test() const
{
return *m_pTest;
}
bool operator <(const TestProxy& obj)
{
return test()<obj.test();
}
bool operator >= (const TestProxy& obj)
{
return test()>=obj.test();
}
bool operator> (const TestProxy& obj)
{
return test()>obj.test();
}
bool operator<= (const TestProxy& obj)
{
return test()<=obj.test();
}
Test& operator =(Test& test)
{
m_pTest=&test;
return test;
}
};
Test t[1000];
TestProxy pt[1000];
int main()
{
clock_t begin=0;//定义时间变量
clock_t end=0;
for(int i=0;i<1000;i++)
{
t[i].id=i;
pt[i]=t[i];
}
begin=clock();//排序前标记时间
Sort::Bubble(pt,1000,false);
end=clock(); //排序之后标记时间
cout<<"time:"<<(end-begin)<<endl;
for(int i=0;i<1000;i++)
{
cout<<t[i].id<<" "<<pt[i].id()<<endl;//原始数据与排序之后的顺序
}
return 0;
}

小结:排序类和数组类之间存在关联关系,排序类能够对数组类对象进行排序,当排序体积庞大的对象时,使用代理模式间接完成,代理模式避开了大对象交换的耗时操作,代理模式是空间换时间的体现。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值