zoukankan      html  css  js  c++  java
  • 排序sort (一)

    这两天学习排序,简单的记录下,等看完之后再进行总结。

    1.首先看了交换排序,顾名思义,也就是当无序时进行元素交换,从而达到元素有序。

    【1】初级的是冒泡排序,冒泡排序的思想是:两两相邻的数据元素进行比较,如果反序则交换,直到有序为止,同时每一轮比较之后较小(大)的数上浮,较大(小)的数下沉,因此命名为冒泡排序。因为是两两相邻的数进行比较,且相等时不进行交换,所以是一种稳定的排序算法。

    冒泡排序(Bubble Sort)代码实现为:

    void bubblesort(vector<int>& a)                 //这里的排序进行从小到大的排序,当从大到小时改变if中的条件即可
    {
     bool change = true;                         //此处设置标志位的作用是当两两元素比较为有序时,则不需要进行后面的比较
     for (int i = 0; i<a.size() && change; i++)  //注意此处i为有符号,a.size()为无符号,会有警告,可以设置为size_t类型,但注意超出a[]的下标,因为无符号类型没有负数,会转化为非常大的正数
     {
        change = false;
        for (int j = a.size() - 2; j >= i; j--)
            if (a[j]>a[j + 1])
            {
             swap(a[j], a[j + 1]);
             change = true;
            }
     }
     cout << "bubble sort:" << endl;
     for (size_t k = 0; k != a.size(); k++)   //此处可以用范围for语句打印
     {
        cout << a[k] << "  ";
     }
    }

    【2】在冒泡的基础上改进为快速排序,快速排序的思想是:将待排序的元素分为独立的两部分,(设置pivotkey,使得它左边元素均小于它,右边的元素均大于它,返回pivotkey的下标pivot)然后再对这两部分(【low到pivot-1】和【pivot+1到high】)继续进行快排。(递归)。

    当元素基本上有序时,此时选择的pivotkey将待排序的元素可能分成严重不均等的两部分,(一部分可能有一个元素,另一部分可能有n-1个元素),此时快速排序退化为冒泡排序。同时因为它比较和交换是跳跃的,所以是一种不稳定的排序方法。

    快速排序(Quick Sort)代码实现为:

    /*************************************************Quick Sort***********************************************************/
    int partition(vector<int>& a,int low,int high)
    {
        //设置枢轴元素为第一个元素,使得枢轴元素的左边均小于它,右边均大于它,这里总是选择下标为low的元素作为pivot,改进为三中选一,或九中选一等。
        int pivotkey = a[low];        
        while (low < high)
        {
            while (low < high&&a[high] >= pivotkey)
                high--;               
            swap(a[low], a[high]);
            while (low < high&&a[low] <= pivotkey)
                low++;
            swap(a[low], a[high]);
        }
        return low;
    }
    void Quicksort(vector<int>& a,int low,int high)  //当数组大时快排效果好,当数组小时,因为快排中总是用递归,反而性能不好,此处可以加一个判断,当大于某个临界时用快排,否则用直接插入排序
    {
        int pivot;
        if (low < high)  //if((high-low)>MAX_LENGTH) 
        {
            pivot = partition(a, low, high);
            Quicksort(a,low,pivot-1);
            Quicksort(a,pivot+1,high);
        }
      //else
        //Insertsort(a);
    }

    2.对选择排序,重点在选择二字,也就是每进行一轮比较,选择出最小(大)元素,选择好后再进行交换。

    因为是跳跃式交换,所以是一种不稳定的排序算法。

    【1】简单选择排序(Simple Selection Sort)代码实现为:  

    void selectsort(vector<int>& a)
    {
        for (int i = 0; i < a.size(); i++)
        {
            int min = i;
            for (int j = i + 1; j < a.size(); j++)
                if (a[min]>a[j])
                   min = j;
            if (i != min)
                swap(a[min], a[i]);            
        }
        cout << "simple selection sort:" << endl;
        for (auto c : a)
            cout << c << "  ";
    
    }

    【2】在简单选择选择排序的基础上改进出了一种新的方法为堆排序,堆排序重点在于建立堆和重建堆,堆首先是一颗完全二叉树,分为大顶堆和小顶堆。(这里默认读者已经对堆的概念已经掌握),算法的主要步骤是:首先对待排序的元素建立一个大顶堆(从非叶节点开始),然后交换堆顶元素(最大的元素)(移走)和末尾元素,对剩下的元素(n-1)进行重建堆,就会得到次大的元素,不断的重复,就可以得到一个有序序列了。

    它比较和交换是跳跃的,所以是一种不稳定的排序方法。

    堆排序(Heap Sort)代码实现为:

    void HeapAdjust(vector<int>& array, int i, int len)   //调整第i个元素,使之成为一个大顶堆(下溯过程),(隐含了i的左右两边已经是堆的情况)
    {
        int temp = array[i];
        int j = 2 * i + 1;
        while (j < len)
        {
            if (j + 1 < len && array[j] < array[j + 1])
                ++j;
            if (array[j] < temp)
                break;
            else
            {
                array[i] = array[j];
                i = j;
                j = 2 * i + 1;
            }
        }
        array[i] = temp;
    }
    
    void heapsort(vector<int>& a)
    {
        //建立一个大顶堆(也可以是小顶堆),从非叶节点开始建立堆
        for (int i = (a.size() - 2) / 2; i >= 0; i--)
            HeapAdjust(a, i, a.size());
        for (int i = a.size() - 1; i>0; i--)
        {
            swap(a[0], a[i]);
            HeapAdjust(a, 0, i );   //表示调整第0个元素,让剩下的i个元素保持大顶堆
        }
        cout << "heap sort:" << endl;
        for (auto c : a)
            cout << c << "  ";

    }

    3.插入排序,它的思想是将一个记录插入到已经排序好的有序表中,从而得到一个新的,记录数增1的有序表。具体是这样实现的:从第二个元素开始(外循环),依次与前面的元素进行比较(内循环),然后选择合适的位置插入元素。

    也就是设置a[i](从第二个元素开始)为哨兵,每次将a[i]和a[i-1]进行比较,如果无序,则将元素后移,直到找出正确的位置插入a[i]。因为插入的元素如果与之前一个元素相等,插入的位置为相等元素的后面,所以是一种稳定的排序算法。在直接插入排序的基础还有二分插入排序和二路插入排序。

     【1】直接插入排序(Insertion Sort)代码实现为:

    /**************************************************Insertion Sort***************************************************/
    void Insertsort(vector<int>& a)
    {
        int temp,j;
        for (int i = 1; i != a.size(); ++i)           //外循环
        {
            if (a[i] < a[i - 1])
            {
                temp = a[i];     //复制为哨兵,也就是待排序(插入)的元素
                for (j = i - 1; j >= 0 && temp < a[j]; j--)     //比哨兵大(小)的元素后移,内循环
                    a[j + 1] = a[j];
                a[j + 1] = temp;   //插入到正确的位置
            }
        }
        cout << "Insertion sort:" << endl;
        for (auto c : a)
            cout << c << "  ";
    }

     二分插入排序代码:

    /******************在直接插入的基础上有二分插入排序和二路插入排序****************************************************/
    void BInsertsort(vector<int>& a)   //二分插入排序
    {
        int temp, j,low,high;
        for (int i = 1; i != a.size(); ++i)
        {
            if (a[i] < a[i - 1])
            {
                low = 0;
                high = i - 1;
                temp = a[i];    
                while (low <= high)     //和直接插入排序不同的就是在low和high中折半查找插入的位置
                {
                    int m = (low + high) / 2;
                    if (temp < a[m])
                        high = m - 1;
                    else
                        low = m + 1;
                }                         //循环完后low>high,[high+1]为插入位置
                for (j = i - 1; j>=high+1; j--)    
                    a[j + 1] = a[j];
                a[high + 1] = temp;   //插入到正确的位置
            }
        }
        cout << "Binary Insertion sort:" << endl;
        for (auto c : a)
            cout << c << "  ";
    }

    【2】在直接插入排序的基础上,对其进行了改进有了希尔排序,本质是一样的,都是找到正确的插入位置插入待排序的元素,希尔排序的精华在于:不像直接插入排序,小的关键字a[i]不是一步一步向前移动(a[i]与a[i-1]比较),而是跳跃式的向前移动(a[i] 与a[i - increasment]比较),跳跃的距离取决于增量序列 increasment,但最后的增量值一定是1,这样和直接插入排列一样,但移动的次数大大减小,因为元素已经基本有序。因为是跳跃式的移动和比较,所以是一种不稳定的排序方法。

    希尔排序(Shell Sort)代码实现为:

    /********************************************Shell Sort*****************************************************************/
    void Shellsort(vector<int>& a)
    {
        int temp,j;
        int increasment = a.size() / 2;   //设置增量序列
        while (increasment >= 1)  //最后的增量序列必须为1,类似与插入排序,才能保证序列有序
        {
            for (int i = increasment; i != a.size(); ++i)
            {
                if (a[i] < a[i - increasment])
                {
                    temp = a[i];     //复制为哨兵,也就是待排序(插入)的元素
                    for (j = i - increasment; j >= 0 && temp < a[j]; j-=increasment)     //比哨兵大(小)的元素后移
                        a[j + increasment] = a[j];
                    a[j + increasment] = temp;   //插入到正确的位置
                }
            }
            increasment/= 2;
        }
        cout << "Shell sort:" << endl;
        for (auto c : a)
            cout << c << "  ";
    }

    4.归并排序,它的思想是:借助辅助空间temp,将待排序序列a分成若干个子序列,再进行归并为有序的temp,之后再把有序的temp归并为整体有序序列a。

    归并排序有两种实现方式:第一种是递归方法实现,空间复杂度除了temp,还有递归用到的栈空间,所以整体的空间复杂度为n+log2n;而非递归方法,只有辅助空间temp,所以它的空间复杂度为n,因此归并排序更推荐使用非递归归并排序方法。因为它比较元素时如果相等,先将位于序列前面的元素归并在前面,所以不会改变相等元素的顺序,是一种稳定的排序方法。此处是两两归并,也称2路归并排序。

    归并排序(Merging Sort)代码实现为:

    【1】递归方法:

    /*******************************************Merging Sort(递归方法)*****************************************************************/
    //此函数的功能是借助temp将a(从i到n大小,以m为分界的两个子序列)归并(合并)为一个有序的序列a.(先将a中的子序列归并到temp,再将归并好的子序列传回给a)
    void Merge(vector<int>&temp,vector<int>&a,int i,int m,int n)
    {
        int j,k,l,beg=i;
        for (j = m + 1, k = i; i <= m &&j <= n; ++k)
        {
            if (a[i] <= a[j])     //此处为小于等于,否则不是稳定的排序方法
                temp[k] = a[i++];
            else
                temp[k] = a[j++];
        }
        if (i <= m)                   //将剩余的元素a[i.....,m]复制到temp
        {
            for (l = 0; l <= m - i; l++)
                temp[k + l] = a[i + l];  
        }
        if (j <= n)                  //将剩余的元素a[j.....,n]复制到temp
        {
            for (l = 0; l <= n - j; l++)
                temp[k + l] = a[j + l];
        }
        for (; beg <= n; beg++)    //这里实现将a归并的后的temp赋给a,也就是temp[beg....n]复制到a[beg....n],此处beg为刚传进来的i,所以开始的时候i没有参与运算时要进行初始化beg
            a[beg] = temp[beg];
    }
    //temp为辅助空间,a为待排序的序列,将a不断分为若干个子序列,归并到temp,再传回给a
    void Msort(vector<int>&temp, vector<int>&a, int s, int e)
    {
        int m;
        if (s<e)
        {
            m = (s + e) / 2;             //不断递归将a平分为a[s....m]和a[m+1....e]两部分
            Msort(temp,a,s, m);       //递归实现将a[s...m]归并为有序的temp[s...m]再到a[s...m]
            Msort(temp,a,m + 1,e);   //递归实现将a[m+1....e]归并为有序的temp[m+1....e]再到a[m+1....e]
            Merge(temp,a,s,m,e);   //将a[s...m]和a[m+1....e]归并到有序的temp[s...e]再到a[s...e]
        }
    }
    void Mergingsort(vector<int>&a)
    {
        vector<int> temp(a.size());  //申请了一个大小为a.size()的辅助空间
        Msort(temp, a, 0, a.size() - 1);
        cout << "递归实现:Merging sort:" << endl;
        for (auto c : a)
            cout << c << "  ";
    }

    【2】非递归方法

    /*************************************************Merging Sort(非递归方法)******************************************************************/
    void MergePass(vector<int>&a, vector<int>&temp, int s, int e)
    {
        int i = 0;
        while (i <= e - 2 * s + 1)   //归并长度相等的子序列
        {
            Merge(temp, a, i, i + s - 1, i + 2 * s - 1);
            i = i + 2 * s;
        }
        if (i < e - s + 1)           //归并长度不相等的子序列
            Merge(temp, a, i, i + s - 1, e);
    
         //else                     //若剩下单独的子序列,此处可以省略,因为总是从a归并到temp再到a
            //for (int j = i; i <= e; j++)  
            //    temp[j] = a[j];
    }
    void Mergesort(vector<int>&a)
    {
        vector<int>temp(a.size()); //辅助空间
        int k = 1;
        while (k < a.size())
        {
            MergePass(a, temp, k, a.size() - 1);
            k *= 2;
        }
        cout << "非递归实现:Merging sort:" << endl;
        for (auto c : a)
            cout << c << "  ";
    }

     5.测试代码:

    int main()
    {
        vector<int> a = { 9, 1, 5, 8, 3, 7, 4, 6, 2 };
        vector<int> b = { 9, 1, 5, 8, 3, 7, 4, 6, 2 };
        vector<int> c = { 9, 1, 5, 8, 3, 7, 4, 6, 2 };
        vector<int> d = { 9, 1, 5, 8, 3, 7, 4, 6, 2 };
        vector<int> e = { 9, 1, 5, 8, 3, 7, 4, 6, 2 };
        vector<int> f = { 9, 1, 5, 8, 3, 7, 4, 6, 2 };
        vector<int> g = { 9, 1, 5, 8, 3, 7, 4, 6, 2 };
        vector<int> h = { 9, 1, 5, 8, 3, 7, 4, 6, 2 };
        vector<int> i = { 9, 1, 5, 8, 3, 7, 4, 6, 2 };
        int low = 0, high = a.size()-1;
        bubblesort(b);
        cout << endl;
        selectsort(c);
        cout << endl;
        Quicksort(a,low,high);
        cout << "Quick sort:" << endl;
        for (auto c : a)
            cout << c << "  ";
        cout << endl;
        heapsort(d);
        cout << endl;
        Insertsort(e);
        cout << endl;
        Shellsort(f);
        cout << endl;
        BInsertsort(g);
        cout << endl;
        Mergingsort(h);
        cout << endl;
        Mergesort(i);
        return 0;
    }

    测试结果:

  • 相关阅读:
    idea 添加不同的模板文件比如 . vm 文件
    Spring ioc及aop思想
    idea配置非maven项目配置及热部署配置
    AES对称式加密及https加密算法中数字证书(非对称加密)
    Solr(搜索引擎)简单介绍(不一定要会用但是要知道)
    回炉重造-基础规则之初识static关键字
    回炉重造-基础规则之初识IO流
    回炉重造-基础规则之类和对象
    回炉重造-基础规则之行为方法
    回炉重造-基础规则之Java的数组
  • 原文地址:https://www.cnblogs.com/liuamin/p/6698307.html
Copyright © 2011-2022 走看看