排序算法小结(C++)

排序算法:
1、计数排序(rank sort)

先计算名次:
template<class T>
void Rank(T a[],int n,int r[])
{//计算a[0:n-1]中n个元素的排名
    for(int i=0;i<n;i++)
        r[i]=0;//初始化
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            if(a[j]<=a[i])
                r[i]++;
            else
                r[j]++;
}
可以根据a的元素之间所进行的比较操作来估算程序的时间复杂性,这些比较操作是由if语句来完成的。总的比较次数为(n-1)n/2。

再按名次排序:
template <class T>
void Rearrange(T a[],int n,int r[])
{//按序重排数组a中的元素,使用附加数组u
    T *u=new T[n+1];
    //在u中移动到正确的位置
    for(int i=0;i<n;i++)
        u[r[i]]=a[i];
    for(int i=0;i<n;i++)
        a[i]=u[i];
    delete []u;
}
在函数Rearrange执行期间移动元素的次数为2n。完成整个排序需要执行(n-1)n/2次比较操作和2n次移动操作。

另外一种重排元素的函数如下,该函数没有使用附加数组u。
假定已经使用函数Rank计算出一个数组中每个元素的名次。
template <class T>
void Rearrange(T a[],int n,int r[])
{//原地重排数组元素
    for(int i=0;i<n;i++)
    //获取应该排在a[i]处的元素
        while(r[i]!=i)
        {
            int t=r[i];
            Swap(a[i],a[t]);
            Swap(r[i],r[t]);
        }
}
template <class T>
inline void Swap(T& a,T& b)
{
    T temp=a;a=b;b=temp;
}
此程序执行的最少交换次数为0(初始数组已经是按序排列),最大交换次数为2(n-1)。当使用本函数来代替上面的函数时,最坏情况下所需要的执行时间将增加,因为需要移动更多的元素(每次交换需要移动三次),不过程序所需要的内存减少了。
综上考虑,两种计数排序方法各有利弊。

2、选择排序(selection sort)
template <class T>
void SelectionSort(T a[],int n)
{//对数组a[0:n-1]中的n个元素进行排序
    for(int size=n;size>1;size--)
    {
        int j=Max(a,size);
        Swap(a[j],a[size-1]);
    }
}
template<class T>
int Max(T a[],int n)
{//寻找a[0;n-1]中的最大元素
    int pos=0;
    for(int i=1;i<n;i++)
        if(a[pos]<a[i]
            pos=i;
    return pos;
}
每次调用max(a,size)需要执行size-1次比较,所以总的比较次数为1+2+3+...+n-1=(n-1)n/2。元素的移动次数为3(n-1)。选择排序所需要的比较次数与按名次排序所需要的比较次数相同,但所需要的元素移动次数多出50%。

上述选择排序函数的一个缺点是:即使元素已经按序排列,程序仍然继续运行。为了终止不必要的循环,在查找最大元素的期间,可以顺便检查数组是否已按序排列。下面程序给出了一个按照这种思想实现的选择排序函数。在该函数中,把查找最大元素的循环直接与函数SelectionSort合并在一起,而不是把它作为一个独立的函数。
template <class T>
void SelectionSort(T a[],int n)
{//及时终止的选择排序
    bool sorted=false;
    for(int size=n;!sorted && (size>1);size--)
    {
        int pos=0;
        sorted=true;
        //找最大元素
        for(int i=1;i<size;i++)
            if(a[pos]<=a[i])
                pos=i;//如果已经按序排列,那么就不会有else的机会,sorted也就一直为true,最红将终止外部for循环。
            else
                sorted=false;//非未按序排列,表示需要外部for循环
        Swap(a[pos],a[size-1]);
    }
}
其最好的情况出现在数组a最初是有序数组的情形,此时外部for循环仅执行一次,a中元素之间的比较次数为n-1。在最坏情况下,外部for循环一直循环到size=1,执行的比较次数为(n-1)n/2。最好和最坏情况下的交换次数与上面的优化前的程序完全相同,但最坏情况下,优化后的程序可能要略微慢一些,因为它必须进行变量维护的额外工作。
综上所述,优化后的选择排序函数是最佳选择。

3、冒泡排序(bubble sort)
template <class T>
void Bubble(T a[],int n)
{//把数组a[0:n-1]中最大元素通过冒泡移到右边
    for(int i=0;i<n-1;i++)
        if(a[i]>a[i+1])
            Swap(a[i],a[i+1]);
}
template <class T>
void BubbleSort(T a[],int n)
{//对数组a[0:n-1]中的n个元素进行冒泡排序
    for(int i=n;i>1;i--)
        Bubble(a,i);
}
元素比较次数为(n-1)n/2,与SelectionSort相同。
与选择排序情形一样,可以重新设计一个及时终止的冒泡排序函数。如果在一次冒泡过程中没有发生元素互换,则说明书中已经按序排列,没有必要再继续进行冒泡过程。下面程序给出了一个及时终止的冒泡排序函数。
template <class T>
bool Bubble(T a[],int n)
{//把a[0:n-1]中最大元素冒泡至右端
    bool swapped=false;//尚未发生交换
    for(int i=0;i<n-1;i++)
        if(a[i]>a[i+1])
        {
            Swap(a[i],a[i+1]);
            swapped=true;
        }
    return swapped;
}
template<class T>
void BubbleSort(T a[],int n)
{//及时终止的冒泡程序
    for(int i=n;i>1 && Bubble(a,i);i--);
}
在最坏情况下所执行的比较次数与原来的函数一样,在最好的情况下比较次数为n-1。

4、插入排序(insertion sort)
template <class T>
void Insert(T a[],int n,const T &x)
{//向有序数组a[0:n-1]中插入元素x
    int i;
    for(i=n-1;i>=0 && x<a[i];i--)
        a[i+1]=a[i];
    a[i+1]=x;
}
template <class T>
void InsertionSort(T a[],int n)
{//对a[0:n-1]进行排序
    for(int i=1;i<n;i++)
    {
        T t=a[i];
        Insert(a,i,t);
    }
}
在最好的情况下比较次数为n-1,而在最坏情况下比较次数为(n-1)n/2。

综上所述,利用"附加数组重排数组元素"和"原地重排数组元素"使用的是同一个计算名次的函数Rank,比较次数为(n-1)n/2。
但移动元素的次数不一样,"附加数组重排数组元素"需要执行2n次移动操作,而"原地重排数组元素"的最少交换次数为0,最大的交换次数为2(n-1),注意每次交换需要移动三次。

"选择排序"总的比较次数也为(n-1)n/2,但元素移动次数为3(n-1),比计数排序所需移动次数多出50%。"及时终止的选择排序"最好的情况是比较次数为n-1,最坏的情况下,比较次数为(n-1)n/2。
"冒泡排序"总的比较次数为(n-1)n/2,元素移动次数视具体数据而定,"及时终止的冒泡排序"在最好的情况下比较次数为n-1,最坏的情况为(n-1)n/2。
"插入排序"最好的情况下比较次数为n-1,而在最坏情况下比较次数为(n-1)n/2。

你可能感兴趣的:(C++,算法,优化,Class,ini,insert)