只讨论内部排序算法:
1.比较排序,时间复杂度O(nlogn)~O(n^2)有:冒泡排序。选择排序,插入排序,归并排序,堆排序,快速排序
2.非比较排序,时间复杂度可以达到O(n),主要有:计数排序,基数排序,桶排序
排序算法的性能:
排序算法的稳定性:
可以简单定义为:如果Ai = Aj,排序前Ai在Aj之前,排序后Ai还在Aj之前,则称为这种排序算法是稳定的。也就是保证排序前后两个相等的数的相对顺序不变。排序算法的好处是:排序算法如果是稳定的,那么从一个键上排序。然后再从另一个键上排序,前一个键排序的结果可以为后一个键排序所用
冒泡排序:
冒泡排序是一种很简单的排序的算法,它重复走访过要排序的元素,依次比较相邻两个元素,如果它们的顺序错误就把它们调换过来,直到没有元素再需要交换,排序完成。
原理如下:
1.比较相邻的元素,如果前一个比后一个大,就把它们两个调换位置。
2.对每一对相邻元素同样的工作,从开始第一队到结尾的最后一对。这捕做完,最后的元素会是最大的数。
3.针对所有的元素重复以上的步骤,除了最后一个。
4.持续每次对越来越小的元素重复上面的步骤,直到没有任何一对数字需要比较。
代码:
void sort(int sort[]){
for(int j=0;j<sort.length()-1;j++){//每次最大的元素排到数组最后
for(int i=0;i<sort.length()-1-j;i++){//依次比较相邻的两个元素
if(A[i])>A[i+1]{//交换
swaf(A,i,i+1);
}
}
}
}
改进:设置一标志性变量pos,用于记录每趟排序最后一次进行交换的位置,由于pos位置之后的记录均已交换到位,故在进行下一趟排序只要扫描到pos位置即可。void sort2(int sort2[]){
int i = sort2.length()-1;//初始时,最后位置保持不变
while(i>0){
int pos = 0;//每趟开始,无记录交换
for(int j = 0;j<i;j++){
if(sort2[j] > sort2[j+1]){
pos = j;//记录交换位置
swaf(sort,j,j+1);
}
i =pos;//为下一趟排序做准备
}
}
}
再改进:鸡尾酒排序此算法和冒泡排序的不同处在于从低到高然后从高到低,而冒泡排序则仅从低到高去比较序列里的每个元素,可以得到比冒泡排序稍微好一点的效能。
void sort3(int A[], int n){
int left = 0;//初始化边界
int right = n-1;
while(left < right){
for(int i = left;i < right; i++){//前半轮,将最大元素放到最后
if(A[i] > A[i+1]){
swap(A,i,i+1);
}
}
right--;
for(int i =right;i>left;i--){
if(A[i-1]>A[i]){
swap(A,i-1.i);
}
}
left++
}
}
选择排序:
选择排序是一种简单直观的排序算法,它的工作原理很容易理解:初始时在序列中找到最小(大)元素,放在序列的起始位置作为排序;然后再从剩余排序元素中继续寻找最小(大)元素,放到已排序的末尾,以此类推,直到所有元素均排序完毕。选择排序和冒泡排序的区别:冒泡排序通过依次交换相邻两个顺序不合法的元素位置,从而将当前最小(大)元素放大合适的位置;选择排序每遍历一次都记住当前最小(大)元素的位置,最后仅需交换操作即可将其放到合适的位置。
void SelectSort(int A[],int n){
for(int i=0;i<n-1;i++){//i为已排序列的末尾
int min =i;
for(int j = i+1;j< n;j++){//末排序序列
if(A[j]<A[min]){ //找出末排序序列中最小值
min = j;
}
if(min != i){
Swap(A,min,i)//放到已排序序列的末尾
}
}
}
}
插入排序:
插入排序是一种简单直观的排序算法,工作原理类似于我们抓扑克牌
具体原理如下:
1.从第一个元素开始,该元素可以认为已经被排序
2.取出下一个元素,在已经排序的元素序列中从后往前扫描
3.如果该元素大于新元素,将该元素移到下一个位置
4.重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
5.将新元素插入到该位置后
6.重复步骤2-5
void insertsort(int A[],int n){
for(int i =1;i<n;i++){
int s = A[i];
int j= i-1;
while(j>=0 && A[j]>s){
A[j+1] = A[j];
j--;
}
A[J+1] = s;
}
}
改进后插入排序:二分插入排序,因为左边的数字总是排序好的,所以可以用二分排序来优化void insertdichot(int A[],int n){
for(int i = 1;i<n;i++){
int get = A[i]; //右边的数
int left = 0;
int right = i -1;//左右边边界初始化
while(left <= right){//采用二分定位新数
int mid = (left + right)/2
if(A[mid] > get){
right = mid -1;
else
left = mid + 1;
}
for(int j =i -1;j>=left;j--){//将欲插入新数位置右边的数整体向右移动一个单位
A[j+1] = A[j];
}
A[left] = get;
}
}
}
归并排序:
归并排序的实现分为递归实现与非递归实现,递归实现的归并排序是算法设计中分治策略的典型应用,我们将一个大问题分割成小问题。然后用小问题的答案来解决整个大问题。非递归实现的归并排序首先是进行两两归并,然后四四归并,然后八八归并,一直下去直到并到整个数组。
归并操作步骤如下:
1.申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
2.设定两个指针,最初位置分别为两个已经排序序列的起始位置
3.比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一个位置
4.重复步骤3直到某一指针到达序列尾
5.将另一序列剩下的所有元素直接复制到合并序列尾
void Merge(int A[],int left,int mid,int right){//合并两个已排好序的数组A[left...mid]和A[mid+1...right]
int len = right - left +1;
int *temp = new int[len];//辅助空间
int index = 0;
int i = left;//前一数组的起始元素
int j=mid+1;//后一数组的起始元素
while(i<mid && j<=right){
temp[index] = A[i] <=A[j] ? A[i++] :A[j++];
}
while(i <= mid){
temp[index++] = A[i++];
}
while(j <= right){
temp[index++] = A[j++];
}
for(int k = 0;k<len;K++){
A[left++] =temp[k];
}
}
void MergeSortRecursion(int A[],int left,int right)//递归实现的归并排序
{
if(left == right)
return;
int mid = (left + right)/2;
MergeSortRecursion(A, left, mid);
MergeSortRecursion(A, mid + 1, right);
Merge(A, left, mid, right);
}
void MergeSortIteration(int A[], int len) // 非递归(迭代)实现的归并排序(自底向上)
{
int left, mid, right;// 子数组索引,前一个为A[left...mid],后一个子数组为A[mid+1...right]
for (int i = 1; i < len; i *= 2) // 子数组的大小i初始为1,每轮翻倍
{
left = 0;
while (left + i < len) // 后一个子数组存在(需要归并)
{
mid = left + i - 1;
right = mid + i < len ? mid + i : len - 1;// 后一个子数组大小可能不够
Merge(A, left, mid, right);
left = right + 1; // 前一个子数组索引向后移动
}
}
}
快速排序算法:
快速排序是对冒泡排序的一种改进,通过一趟排序将要排序的数据分割成独立的两个部分,其中一部分的所有数据都比另一部分的所有数据,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个暑假变成有序序列
一趟快速排序的算法是:
1.设置两个变量i,j,排序开始的时候:i=0;j=N-1;
2.以第一个数组元素作为关键数据,赋值给key,即key=A[0];
3.从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于key的值A[j],将A[j]和A[i]交换;
4.从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]互换;
5.重复第3,4步直到i=j;(3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于key的时候改变j,i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i,j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束)
int Partition(int A[],int left,int right){ // 划分函数
int pivot = A[right]; //每次都选择最后一个元素作为基准
int tail = left -1;//tail为小于基准的子数组最后一个元素的索引
for(int i = left;i<right;i++){//遍历基准以外的其他元素
if(A[i]< = pivot){
swap(A,++tail,i);
}
}
swap(A,tail+1,right);//最后把基准放到前一个子数组的后边,剩下的子数组既是大于基准的子数组
return tail +1;
void QuickSort(int A[]),int left,int right{
if(left >=right){
return;
int position = Partition(A,left,right);//基准的索引
QuickSort(A,left,position-1);
QuickSort(A.position+1,right);
}
}
}
希尔排序:
希尔排序是插入排序的一种,是直接插入排序算法的一种。
举个例子:
void shell(int a[])
int d=a.length;
while(true)
{
d=d/2;
for(int x=0;x<d;x++)
{
for(int i=x+d;i<a.length;i=i+d)
{
int temp=a[i];
int j;
for(j=i-d;j>=0&&a[j]>temp;j=j-d)
{
a[j+d]=a[j];
}
a[j+d]=temp;
}
}
if(d==1)
{
break;
}
}
}
排序算法很基础也很重要。也要注意每种算法所适合的场景。