zoukankan      html  css  js  c++  java
  • 经典的7种排序算法 原理C++实现

    经典的7种排序算法 原理C++实现

    排序是编程过程中常常遇到的操作,它在非常大程度上影响了程序的运行效率。

    7种常见的排序算法大致能够分为两类:第一类是低级排序算法,有选择排序、冒泡排序、插入排序。第二类是高级排序算法。有堆排序、排序树、归并排序、高速排序。

    一、低级排序算法

    1. 选择排序

    排序过程:给定一个数值集合。循环遍历集合。每次遍历从集合中选择出最小或最大的放入集合的开头或结尾的位置,下次循环从剩余的元素集合中遍历找出最小的并如上操作,最后直至全部原集合元素都遍历完成,排序结束。

    实现代码

    //选择排序法 
    template 
    void Sort::SelectSort(T* array, int size) 

        int minIndex;                                                                  
        for(int i = 0; i < size; i++)                                               
        { 
            minIndex = i;                                                              
            for(int j = i + 1; j < size; j++) 
            { 
                if(array[minIndex] > array[j])                                 
                { 
                    minIndex = j;                                                     
                } 
            } 
            if(minIndex != i)                                                         
            { 
                Swap(array, i, minIndex); 
            } 
        } 
    }

    分析总结:选择排序时间复杂度比較高。达到了O(n^2),每次选择都要遍历一遍无序区间。

    选择排序对一类重要的元素序列具有较好的效率,就是元素规模非常大,而排序码却比較小的序列。

    另外要说明的是选择排序是一种不稳定的排序方法。

    2. 冒泡排序

    排序过程:冒泡排序的过程形如其名,就是依次比較相邻两个元素。优先级高(或大或小)的元素向后移动,直至到达序列末尾,无序区间就会对应地缩小。下一次再从无序区间进行冒泡操作,依此循环直至无序区间为1,排序结束。

    实现代码:

    //冒泡排序法 
    template 
    void Sort::BubbleSort(T* array, int size) 

        for(int i = 0; i < size; i++) 
        { 
            for(int j = 1; j < size - i; j++) 
            { 
                if(array[j] < array[j - 1]) 
                { 
                    Swap(array, j, j - 1); 
                } 
            } 
        } 
    }

    分析总结:冒泡排序的时间复杂度也比較高,达到O(n^2),每次遍历无序区间都将优先级高的元素移动到无序区间的末尾。冒泡排序是一种稳定的排序方式。

    3. 插入排序

    排序过程:将前面的区间(初始区间为1。包括第一个元素)视作有序区间,然后将有序区间的后一元素插入到前面有序区间的适当位置。

    直至有有序区间扩展到原区间的大小。排序结束。

    实现代码:

    //插入排序 
    template 
    void Sort::InsertSort(T* array, int size) 

        for(int i = 1; i < size; i++) 
        { 
            for(int j = i; j > 0; j--) 
            { 
                if(array[j] < array[j - 1]) 
                { 
                    Swap(array, j, j-1); 
                } 
            } 
        } 
    }

    分析总结:插入排序的时间复杂度达到O(n^2),排序的执行时间和待排序元素的原始排列顺序密切相关。插入排序是一种稳定的排序方法。

    二、高级排序算法

    1. 高速排序

    排序过程:高速排序应该是应用最广泛的排序算法。它是採用了分治的思想(这样的思想非常重要)。

    其主要的思想就是任取待排序序列中的某个元素(元素的选取方式在一定程序上会影响实现过程和排序效率)作为标杆。将待排序序列划分为左右两个子序列,左側元素小于标杆元素。右側元素大于标杆元素。标杆元素则排在这两个子序列的中间,然后再对这两个子序列反复上述的方法。直至排序结束。

    实现代码:

    //高速排序 
    template 
    void Sort::QuickSort(T *array, int left, int right) 

        if(left < right) 
        { 
            int i = left -1, j = right + 1; 
            T mid = array[(left + right) / 2]; 
            while(true) 
            { 
                while(array[++i] < mid); 
                while(array[--j] > mid); 
                if(i >= j) 
                { 
                    break; 
                } 
                Swap(array, i, j); 
            } 
            QuickSort(array, left, i - 1); 
            QuickSort(array, j + 1, right); 
        } 
    }

    分析总结:高速排序的时间复杂度为O(nlogn),是一种不稳定的排序算法。

    2. 归并排序

    排序过程:归并排序的原理比較简单,也是基于分治思想的。它将待排序的元素序列分成两个长度相等的子序列,然后为每个子序列排序,然后再将它们合并成一个序列。

    实现代码:

    //归并排序 
    template 
    void Sort::MergeSort(T* array, int left, int right) 

        if(left < right) 
        { 
            int mid = (left + right) / 2; 
            MergeSort(array, left, mid); 
            MergeSort(array, mid + 1, right); 
            Merge(array, left, mid, right); 
        } 

    //合并两个已排好序的子链 
    template 
    void Sort::Merge(T* array, int left, int mid, int right) 

        T* temp = new T[right - left + 1]; 
        int i = left, j = mid + 1, m = 0; 
        while(i <= mid && j <= right) 
        { 
            if(array[i] < array[j]) 
            { 
                temp[m++] = array[i++]; 
            } 
            else 
            { 
                temp[m++] = array[j++]; 
            } 
        } 
        while(i <= mid) 
        { 
            temp[m++] = array[i++]; 
        } 
        while(j <= right) 
        { 
            temp[m++] = array[j++];

        } 
        for(int n = left, m = 0; n <= right; n++, m++) 
        { 
            array[n] = temp[m]; 
        } 
        delete temp; 
    }

    分析总结:归并排序最好、最差和平均时间复杂度都是O(nlogn)。是一种稳定的排序算法。

    3. 堆排序

    排序过程:堆排序的过程分为两个步骤,第一步是依据初始输入数据,建立一个初始堆;第二步是将堆顶元素与当前无序区间的最后一个元素进行交换,然后再从堆顶元素開始对堆进行调整。

    实现代码:

    //堆排序 
    template 
    void Sort::HeapSort(T* array, int size) 

        int lastP = size / 2; 
        //从最后一个有孩子的结点開始建初始堆 
        for(int i = lastP - 1; i >= 0; i--) 
        { 
            HeapAjust(array, i, size); 
        } 
        int j = size; 
        //将堆顶元素和无序区间的最后一个元素交换。再调整堆 
        while(j > 0) 
        { 
            Swap(array, 0, j - 1); 
            j--; 
            HeapAjust(array, 0, j); 
        } 

    //调整堆 
    template 
    void Sort::HeapAjust(T *array, int toAjust, int size) 

        int pos = toAjust; 
        while((pos * 2 + 1) < size) 
        { 
            int lChild = pos * 2 + 1; 
            if(array[lChild] > array[pos]) 
            { 
                pos = lChild;//左孩子大 
            } 
            int rChild = lChild + 1; 
            if(rChild < size && array[rChild] > array[pos]) 
            { 
                pos = rChild;//右孩子更大 
            } 
            if(pos != toAjust) //父结点比当中一个孩子小 
            { 
                Swap(array, toAjust, pos); 
                toAjust = pos; 
            } 
            else 
            { 
                break; 
            } 
        } 
    }

    分析总结:堆排序的算法时间复杂度为O(nlogn),它是一种不稳定的排序算法。

    4. 排序树

    排序过程:排序树算法应用了AVL树的原理,仅仅只是排序树不是平衡的。它的特点就是父结点元素总是比左孩子元素要大却比右孩子元素要小。依据这个特点,能够将原数组元素组织成排序树,然后在对排序树进行中序遍历。中序遍历的结果就是排好序的序列。在算法的实现中採用的数组的形式来存储排序树,并採用的三个辅助数组来表示元素与元素之间在树中的关系,这三个辅助数组各自是父结点索引表、左孩子索引个、右孩子索引表。最后採用了递归的方法对排序树进行了中序遍历。

    实现代码:

    template 
    void Sort::TreeSort(T* array, int size) 

        int *parent = new int[size];//父结点子针 
        int *lChild = new int[size];//左孩子子针 
        int *rChild = new int[size];//右孩子子针 
        //将各结点左右子结点指针均置为-1。表示没有左右子结点 
        for(int i = 0; i < size; i++) 
        { 
            lChild[i] = -1; 
            rChild[i] = -1; 
        }

        parent[0] = -1; //将第一个元素作为根结点。其父结点置为-1 
        //从第2个数開始构造树 
        for(int i = 1; i < size; i++) 
        { 
            int last = 0; 
            while(true) 
            { 
                int compare = array[i] - array[last]; 
                if(compare > 0) //比当前值大,进入右子树 
                { 
                    if(rChild[last] == -1) 
                    { 
                        rChild[last] = i; 
                        parent[i] = last; 
                        break; 
                    } 
                    else 
                    { 
                        last = rChild[last]; 
                    } 
                } 
                else //比当前值小。进入左子树 
                { 
                    if(lChild[last] == -1) 
                    { 
                        lChild[last] = i; 
                        parent[i] = last; 
                        break; 
                    } 
                    else 
                    { 
                        last = lChild[last]; 
                    } 
                } 
            } 
        } 
        //保存排序树的中序遍历结果 
        T* midRetrival = new T[size]; 
        TreeMidRetrival(array, midRetrival, 0, lChild, rChild); 
        //将排好序的中序数组拷贝到原数组 
        for(int i = 0; i < size; i++) 
        { 
            array[i] = midRetrival[i]; 
        } 

    //中序遍历 
    template 
    void Sort::TreeMidRetrival(T *array, T* temp, int pos, T* lChild, T* rChild) 

        static int i = 0; 
        if(pos != -1) 
        { 
            TreeMidRetrival(array, temp, lChild[pos], lChild, rChild);//遍历左子树 
            temp[i++] = array[pos];//保存当前值 
            TreeMidRetrival(array, temp, rChild[pos], lChild, rChild);//遍历右子树 
        } 
        else 
        { 
            return; 
        } 
    }

    分析总结:算法中排序树建立的时间复杂度是O(nlogn)。中序遍历的时间复杂度是O(n),故排序树排序的时间复杂度为O(nlogn)。排序树的空间复杂度比較高,由于它使用了几个额外的存储空间的存储排序树元素之间的关系。

    模板类定义例如以下所看到的:

    template 
    class Sort 

    public: 
        void SelectSort(T* array, int size); 
        void InsertSort(T* array, int size); 
        void BubbleSort(T* array, int size); 
        void MergeSort(T* array, int left, int right); 
        void Merge(T* array, int left, int mid, int right); 
        void HeapSort(T *array, int size); 
        void HeapAjust(T *array, int toAjust, int size); 
        void QuickSort(T *array, int left, int right); 
        void TreeSort(T *array, int size); 
        void TreeMidRetrival(T* array, T* temp, int pos, T* lChild, T* rChild); 
        void Swap(T* array, int x, int y); 
    };

    template //交换两个元素 
    void Sort::Swap(T* array, int x, int y) 

        T temp = array[x]; 
        array[x] = array[y]; 
        array[y] = temp; 
    }


  • 相关阅读:
    Unity 用代码设置UGUI的渲染层级
    C/C++常考基础面试题(更新)
    运行Jar包程序Shell
    Shell编程语法
    Spring多个数据源问题:DataSourceAutoConfiguration required a single bean, but * were found
    Maven项目的发布,发布到Nexus
    Consul入门
    application.properties 改成 application.yml
    奖学金申请模板
    jvm
  • 原文地址:https://www.cnblogs.com/cxchanpin/p/6943756.html
Copyright © 2011-2022 走看看