常见经典排序算法

插入排序:
算法简介:接插入排序(Insertion Sort)的基本思想是:每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子序列中的适当位置,直到全部记录插入完成为止。时间复杂度为O(n^2)。 最稳定的排序算法但是效率很低
代码实现:
void InsertSort(int *arr,int n)
{
                 for (int index = 0; index < n-1; ++index)
                {
                                 int end = index+1;
                                 int tmp = arr [end];
                                 while (end>0&&tmp<arr [end - 1])
                                {
                                                 arr[end] = arr [end - 1];
                                                end--;
                                }
                                 arr[end] = tmp;
                }
}
 
显然当最小的数字在数组的最右端时,此时需要将整个数组进行移位,效率很低,而希尔排序可以有效的改善这个问题
希尔排序:希尔排序(Shell Sort)是插入排序的一种。也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法
       先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率上比前两种方法有较大提高。
代码实现:
void ShellSort(int *arr, int n)//希尔排序
{
                 int gap = n ;
                 while (gap>1)//由于gap=gap/3+1 最小值为1 则在gap=1时跳出循环
                {
                                gap = gap / 3 + 1; //{ 2, 8, 9, 6, 1, 3, 4, 5, 7, 0 ,-1,-2}//注意这里的+1  当gap=1时此时排序等同于插入排序 但是由于之前将最小的数据已经移到最左边所以效率
//高于插入排序
                                 for (int index = 0; index <n-gap; ++index)
                                {
                                                 int end = index;
                                                 int tmp = arr [end+gap];
                                                 while (end>= 0 && arr [end]>tmp)                                                {
                                                                 arr[end+gap] = arr [end];
                                                                end -=gap;//此时插入间距为end
                                                }
                                                 arr[end+gap] = tmp;
                                }
                }
}
附注:上面希尔排序的步长选择都是从n/3+1开始,每次再取上一次的三分之一加1,直到最后为1。关于希尔排序步长参见维基百科:http://zh.wikipedia.org/wiki/%E5%B8%8C%E5%B0%94%E6%8E%92%E5%BA%8F
冒泡排序:20世纪经典算法之一,
原理是临近的数字两两进行比较,按照从小到大或者从大到小的顺序进行交换,这样一趟过去后,最大或最小的数字被交换到了最后一位,再进行第二趟冒泡,由此我们可以写出以下代码:
void BubbleSort(int *arr, int n)
{
                 for (int i = 0; i < n; ++i)
                {
                                 for (int j = 0; j < n - i - 1; ++j)
                                {
                                                 if (arr [j]>arr[j + 1])
                                                                swap( arr[j], arr [j + 1]);
                                }
                }
}
这里我们设立一个标记变量 flag用来标记数列中的数是否在循环结束前就已经排好序
代码改进如下:
void BubbleSort(int *arr, int n)
{

                 bool flag = true ;
                 int k = n ;
                 while (flag)
                {
                                flag = false;
                                 for (int i = 1; i < k; ++i)
                                {
                                                 if (arr [i - 1]<arr[i])
                                                {
                                                                swap( arr[i - 1], arr [i]);
                                                                flag = true;//有发生交换 继续冒泡 否则说明已经有序
                                                }

                                }
                                k--;
                }

}
    如果这一趟发生了交换,这flag为rrue,则还需要继续进行冒泡,否则说明数组已经有序,后面的不必进行下去。
那么这里给出这样一种情况:(2,1,3,4,5,6,7,8,9,10)第一次循环交换之后我们会发现,后面的数组已经有序,不再需要我们进行冒泡,后面的操作都是不必要的 这里我们只需要记录下最后一次进行交换的位置,那么下次遍历只要遍历到这个位置就可以结束。
代码进一步优化如下:
void BubbleSort(int *arr, int n)
{

                 int flag = n ;//第一次遍历终点为数组尾
                 while (flag > 0)
                {
                                 int k = flag;
                                flag = 0;
                                 for (int j = 1; j < k; ++j)
                                {
                                                 if (arr [j - 1] > arr[j])
                                                {
                                                                swap( arr[j - 1], arr [j]);
                                                                flag = j;//后面的已经排序好 记录下下一次排序的终点
                                                }
                                }
                }
}
虽然有了这么多优化,但是冒泡排序总体还是一种效率很低的排序,数据规模过大时不适合使用这种排序

堆排序:
堆的定义:
               1.堆是一颗完全二叉树
               2.父结点总是大于或等于(小于或等于)任何一个子节点
               3每个结点的左子树和右子树都是一个二叉堆
               当父结点总是大于或等于任何一个子节点值时为最大堆。反之则为最小堆
堆的结构示意图如下:
存储方式:
我们使用数组来存储一个堆,可以看出设父节点下标值为i 其左孩子下标值为2*i+1,右孩子为2*1+2;
代码实现如下:
void AdJust_down(int *arr, int parent, int size )
{
                 int child = 2 * parent +1;
                 while (child<size )
                {
                                 if (child+1<size &&arr[child+1]> arr[child])
                                {
                                                ++child;
                                }
                                 if (arr [parent]> arr[child])
                                                 break;
                                swap( arr[parent ], arr[child]);
                                 parent = child;
                                child = 2 * parent;
                }
}
void HeapSort(int *arr,int n)
{
                 for (int i = n/2-1; i >= 0; --i)
                {
                                AdJust_down( arr, i, n );
                }
                 for (int i = n - 1; i >= 1; --i)
                {
                                swap( arr[0], arr [i]);
                                AdJust_down( arr, 0, i);
                }
}
思路分析:
1.如果要对数字进行升序,我们首先首先将数组初始化为原始大堆
                 for (int i = n/2-1; i >= 0; --i)
                {
                                AdJust_down( arr, i, n );//从最后一个非叶子节点开始调整
                }
2.进行排序(以升序为例)
大堆的性质为:最大的数据一定在堆顶将堆顶和堆最后一个元素进行交换,则最大的数字此时在数字尾部,再将堆顶元素下调,且堆的大小减1,知道堆大小为1循环结束,排序完成。
代码如下:
                 for (int i = n - 1; i >= 1; --i)
                {
                                swap( arr[0], arr [i]);
                                AdJust_down( arr, 0, i);
                }

选择排序
选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。 选择排序是不稳定的排序方法
为了减少比较的次数,我们一次遍历同时选出最大值和最小值,代码实现如下:
void SelectSort(int *arr, int n)
{
                
                 int i = 0, j = n - 1;
                 int max = j;
                 int min = i;
                 int left = 0; int right = n - 1;
                 while (left<=right)
                {
                                min = left;
                                max = right; ///!!!!!!!!!!!重点
                                 for (i = left, j = right; i <= j; i++)
                                {
                                                 if (arr [min]>arr[i])
                                                                min = i;
                                                 if (arr [max] < arr[i]) ////{ 2, 9, 6, 1, 3, 4, 5, 7, 0 ,-8,1,-2}
                                                                max = i;
                                }
                                 if (left != min)
                                {
                                                swap( arr[left], arr [min]);
                                                 if (max == left)
                                                                max = min;
                                }
                                                
                                 if (right != max)
                                                swap( arr[right], arr [max]);
                                left++;
                                right--;
                }

}
这里我们必须注意到,以升序为例,如果一次遍历找到的最大值刚好在数组左边,此时肯定会先被移走,此时就最大值得下标就得更新为转移后的位置

快速排序:
该方法的基本思想是:
1.先从数列中取出一个数作为key。
2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
3.再对左右区间重复第二步,直到各区间只有一个数。
代码实现如下:
int PartSort(int *arr, int left, int right)
{
                 int key = arr [right]; //{ 10,2, 8, 9, 6, 1, 3, 4, 5, 7, 0 ,-1,-2,-100};
                 int begin = left ;
                 int end = right - 1;
                 while (begin<end)
                {
                                 while (begin<end&&arr [begin]<=key)
                                {
                                                begin++;
                                }
                                 while (begin<end&&arr [end]>=key)
                                {
                                                end--;
                                }
                                 if (begin < end)
                                                swap( arr[begin], arr [end]);
                }
                 if (arr [begin]>arr[right])
                {
                                swap( arr[begin], arr [right]);
                                 return begin;
                }
                 return right ;

}
void QuickSort(int *arr, int left,int right)
{              
                 if (left >= right)
                                 return;
                 int div = PartSort(arr , left, right);
                QuickSort( arr, div + 1, right );
                QuickSort( arr, left , div - 1);

}
时间: 2024-10-25 05:52:43

常见经典排序算法的相关文章

常见经典排序算法学习总结,附算法原理及实现代码(插入、shell、冒泡、选择、归并、快排等)

博主在学习过程中深感基础的重要,经典排序算法是数据结构与算法学习过程中重要的一环,这里对笔试面试最常涉及到的7种排序算法(包括插入排序.希尔排序.选择排序.冒泡排序.快速排序.堆排序.归并排序)进行了详解.每一种算法都有基本介绍.算法原理分析.算法代码. 转载请注明出处:http://blog.csdn.net/lsh_2013/article/details/47280135 插入排序 1)算法简介 插入排序(Insertion Sort)的算法描述是一种简单直观的排序算法.它的工作原理是通过

七种常见经典排序算法总结(C++)

最近想复习下C++,很久没怎么用了,毕业时的一些经典排序算法也忘差不多了,所以刚好一起再学习一遍. 除了冒泡.插入.选择这几个复杂度O(n^2)的基本排序算法,希尔.归并.快速.堆排序,多多少少还有些晦涩难懂,幸好又博客园大神dreamcatcher-cx都总结成了图解,一步步很详细,十分感谢. 而且就时间复杂度来说,这几种算法到底有什么区别呢,刚好做了下测试. 代码参考: http://yansu.org/2015/09/07/sort-algorithms.html //: basic_so

常见比较排序算法的比较

几种常见的排序算法之比较 排序的基本概念以及其算法的种类,介绍几种常见的排序算法的算法:冒泡排序.选择排序.插入排序.归并排序.快速排序.希尔排序的算法和分析它们各自的复杂度,然后以表格的形式,清晰直观的表现出它们的复杂度的不同.在研究学习了之前几种排序算法的基础上,讨论发现一种新的排序算法,并通过了进一步的探索,找到了新的排序算法较之前几种算法的优势与不足. 排序算法,是计算机编程中的一个常见问题.在日常的数据处理中,面对纷繁的数据,我们也许有成百上千种要求,因此只有当数据经过恰当的排序后,才

经典排序算法 - 桶排序Bucket sort

经典排序算法 - 桶排序Bucket sort 补充说明三点 1,桶排序是稳定的 2,桶排序是常见排序里最快的一种,比快排还要快-大多数情况下 3,桶排序非常快,但是同时也非常耗空间,基本上是最耗空间的一种排序算法 我自己的理解哈,可能与网上说的有一些出入,大体都是同样的原理 无序数组有个要求,就是成员隶属于固定(有限的)的区间,如范围为[0-9](考试分数为1-100等) 例如待排数字[6 2 4 1 5 9] 准备10个空桶,最大数个空桶 [6 2 4 1 5 9]           待排

【最全】经典排序算法(C语言)

本文章包括所有基本排序算法(和其中一些算法的改进算法): 直接插入排序.希尔排序.直接选择排序.堆排序.冒泡排序.快速排序.归并排序.基数排序. 算法复杂度比较: 算法分类 一.直接插入排序 一个插入排序是另一种简单排序,它的思路是:每次从未排好的序列中选出第一个元素插入到已排好的序列中. 它的算法步骤可以大致归纳如下: 从未排好的序列中拿出首元素,并把它赋值给temp变量: 从排好的序列中,依次与temp进行比较,如果元素比temp大,则将元素后移(实际上放置temp的元素位置已经空出) 直到

经典排序算法及python实现

今天我们来谈谈几种经典排序算法,然后用python来实现,最后通过数据来比较几个算法时间 选择排序 选择排序(Selection sort)是一种简单直观的排序算法.它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完. 选择排序是不稳定的排序方法(比如序列[5, 5, 3]第一次就将第一个[5]与[3]交换,导致第一个5挪动到第二个5后面).(注:选自百度百科) 假如,有一个无须序列A={6,3,1,9,2,5,8,7,4},

经典排序算法(动图演示)

算法概述 0.1 算法分类 十种常见排序算法可以分为两大类: 非线性时间比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此称为非线性时间比较类排序. 线性时间非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此称为线性时间非比较类排序. 0.2 算法复杂度 0.3 相关概念 稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面. 不稳定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b

一文搞定十大经典排序算法(Java实现)

本文总结十大经典排序算法及变形,并提供Java实现. 参考文章: 十大经典排序算法总结(Java语言实现) 快速排序算法—左右指针法,挖坑法,前后指针法,递归和非递归 快速排序及优化(三路划分等) 一.排序算法概述 1.定义 将杂乱无章的数据元素,通过一定的方法按关键字顺序排列的过程叫做排序. 2.分类 十种常见排序算法可以分为两大类: 非线性时间比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此称为非线性时间比较类排序. 线性时间非比较类排序:不通过比较

【转】十大经典排序算法

[转]十大经典排序算法:https://www.cnblogs.com/onepixel/articles/7674659.html 0.算法概述 0.1 算法分类 十种常见排序算法可以分为两大类: 比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序. 非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序. 0.2 算法复杂度 0.3 相关概念 稳定:如果