十种经典排序算法

来源:互联网 发布:手机淘宝店铺模板复制 编辑:程序博客网 时间:2024/05/18 03:58

经典排序算法

冒泡排序

算法原理:冒泡算法在对n个数进行由小到大的排序时,进行n-1次循环,每次循环将相邻的两个数进行比较,将较大的换到最上面;这样,每一次循环都将未排序的数中最大的数换到最上面,就行泡泡一样往上冒,进行n-1次后,就排好了序。

 

算法过程:

1.   令输入数组为a[n],设m为已排好序的数的个数。m=0。

2.   令i从0到n-m-1,比较a[i],a[i+1],若a[i]>a[i+1],将它们交换。

3.   m=m+1。若n-1=m,跳到第4步;否则回到第2步。(这里之所以为n-1=m是因为,n个数排列,若n-1个最大的数已经排好,那么整个数组就排好序了,不难理解。)

4.   终止。

 

C语言描述:

void bubble_sort(int* a, int n){    int i, m=0;while(n-1>m){       for(i=0;i<n-m-1;++i){           if(a[i]>a[i+1]){               int temp = a[i];               a[i] = a[i+1];               a[i+1] = temp;           }    }    m = m + 1;    }} 

 

复杂度分析:

T(n) = Θ(n²)

 

插入排序

算法原理:对于给定的n个数,从第二个数开始,将依次将每个数插入前面排好序的序列中恰当的位置,最后得到排好序的序列。

 

算法过程:

1.   输入乱序数组a[n](n个元素)。令i=1(从第二个数开始,数组索引从0开始)。

2.   令j=i(从当前位置开始往前插入)。

3.   若a[j-1]>a[j],将它们交换,并让j=j-1,继续执行这一步,直到j<=0。否则令i=1+1。

4.   若i==n则终止,否则跳回第2步。

 

C语言描述:

void insert_sort(int* a, int n){    int i;   for(i=1;i<n;++i){        intj = i;       while(a[j]<a[j-1]){           int temp = a[j];           a[j] = a[j-1];           a[j-1] = temp;           j = j -1;        }    }} 


复杂度分析:

T(n) = Θ(n²)

 

希尔排序

算法原理:希尔排序是改进的插入排序,之所以叫希尔排序是因为它是一个叫希尔的人提出的。希尔排序使用一个步长来划分子序列,使用插入排序给子序列排序,然后递减步长,再排序子序列,直到步长为1。步长初始值通常取为原序列长度的一半。

 

算法过程:

1.   输入序列a[n]。

2.   令步长step=n/2。划分出子序列a[0],a[step],a[2*step],·······。

3.   使用插入排序给子序列排序。

4.   递减步长,step=step-1。

5.   若step为0,则跳到6,否则跳到3。

6.   终止。

 

C语言描述:

void shell_sort(int* a, int n){   int step= n / 2;   while(step >= 1){      int i= step;      while(i < n){         intj = i;         while(j >= step && a[j] < a[j - step]){            intt = a[j];            a[j]= a[j - step];            a[j- step] = t;            j-= step;         }         i+= step;      }      step-= 1;   }} 


复杂度分析:

T(n) = Θ(nlogn)

 

选择排序

算法原理:在对n个数进行排列时,进行n-1次循环,每次循环选出剩下的数中最小的一个,将它加入到已排好序的序列后面。

 

算法过程:

1.   输入乱序数组a[n]。令i=0。

2.   令m=i,j=m+1。

3.   若a[m]>a[j],令m = j,j=j+1。重复这一步,直到j==n-1。

4.   将a[i]与a[m]进行交换。若i<n-1,i=i+1,跳回2;否则跳到5。

5.   终止。

 

C语言描述:

void select_sort(int *a, int n){    inti,j,m;   for(i=0;i<n-1;++i){        inttemp;        m =i;       for(j=i+1;j<n;++j)           if(a[m]>a[j]) m = j;       temp = a[i];       a[i] = a[m];       a[m] = temp;    }}

 

复杂度分析:

T(n) = Θ(n²)

 

快速排序

算法原理:快速排序是一个迭代循环的过程。每一次迭代,它随机选取一个数作为关键数,然后将所有小于它的数放在它的左边,大于的位于右边。然后将左边和右边的数迭代进行快速排序,最后得到排好序的序列。

 

算法过程:

一趟快速排序的过程:

1.   设输入数组a[n],low,high。low标记左边的索引,high标记右边的索引。

2.   令i=low,j=high,key=a[i]。

3.   若a[j]<key,交换a[i]和a[j],此时key值在a[j]中。否则j=j-1,重复这一步。

4.   若a[i]>key,交换a[i]和a[j],此时key值在a[i]中。否则i=i+1,重复这一步。

5.   若i==j,跳到6,否则跳到3。

6.   一趟快速排序终止。

 

C语言描述:

void quick_sort(int *a, int low, int high){   int i =low;   int j =high;   int key =a[i];   if (low>= high) return;   while (i!= j){      while(a[j] >= key && j > i)         j =j - 1;      a[i] =a[j];      a[j] =key;      while(a[i] <= key && i < j)         i =i + 1;      a[j] =a[i];      a[i] =key;   }   quick_sort(a,low, i - 1);   quick_sort(a,i + 1, high);}


复杂度分析:

T(n) = Θ(nlogn)

 

归并排序

算法原理:归并排序是算法中分治法应用的典型。它将要排序的序列分为两部分,将这两部分分别排好序后,可以以O(n)时间复杂度将两个部分整合。两个部分的排序也可以使用归并排序,从而迭代得到结果。

 

算法过程:

合并过程:

1.   输入数组a[n],low、middle、high。合并的两个部分的索引范围为[low,middle),[middle,high)。

2.   定义一个临时数组temp[m],m=high-low。

3.   设置两个指针i,j分别指向两个部分的起始位置low,middle。

4.   比较a[i],a[j]将较小的放入数组temp,并递增相应指针。

5.   重复第4步直到两个部分所有的数都放入temp数组。

6.   将temp数组的所有内容复制到a[n]中[low,high)的相应位置。

完整迭代过程:

1.   输入数组a[n],low、high指示索引范围[low,high)。

2.   若high-low<2,说明只有一个元素,终止迭代,返回。否则到3。

3.   令middle=low+(low+high)/2。

4.   迭代a[n],low,middle。

5.   迭代a[n],middle,high。

6.   合并a[n],low,middle,high。

7.   终止。

 

C语言描述:

void merge(int* a, int n1, int n2, int n3){   int i =n1;   int j =n2;   int k =0;   int* temp= (int*)malloc(sizeof(int)*(n3 - n1));   while (i< n2 && j < n3){      if(a[i] < a[j]) temp[k++] = a[i++];      elsetemp[k++] = a[j++];   }   while(i<n2)      temp[k++]= a[i++];   while(j<n3)      temp[k++]= a[j++];   for (k =0; k < n3 - n1; ++k)      a[k+n1]= temp[k];   free(temp);}void merge_sort(int* a, int low, inthigh){//[low,high)   intmiddle = low + (high - low) / 2;   if (high- low < 2) return;//only one element   merge_sort(a,low, middle);   merge_sort(a,middle, high);   merge(a,low, middle, high);} 


复杂度分析:

T(n) = Θ(nlogn)

 

计数排序

算法原理:对于一个数,若知道比它小的数有多少个,那么就可以确定它的位置了。计数排序是对整数进行排序的算法。一个待排序序列中所有的数必然是在某一个范围内的。定义一个长度为这个范围长度的计数数组并初始化为0,对需排序序列进行一遍扫描,将数的大小作为数组的索引,将计数数组元素的值加1.然后再按索引顺序输出相应个索引值,则得到排好序的序列。此算法适宜对数值相对集中的序列进行排序。

 

算法过程:

1.   输入需排序数组a[n]。

2.   扫描一遍a[n],令small记录最小的数,big记录最大的数。

3.   令m=big-small+1,创建计数数组b[m],并初始化为0。

4.   遍历数组a[n],使b[a[i]-small] += 1。

5.   遍历数中b[n],依次在在数组a[n]中加入b[j]个j+samll。由于计数时使用的索引为a[i]-small,因此需要使用j+small还原。b[j]标记其个数。

6.   终止。

 

C语言描述:

void count_sort(int *a, int n){   int *b;   int i, j,m;    int small= a[0];   int big =a[0];   for (i =1; i < n; ++i){      if(a[i] < small) small = a[i];      if(a[i] > big) big = a[i];   }    m = big -small + 1;   b =(int*)malloc(sizeof(int)*m);   for (i =0; i < m; ++i) b[i] = 0;    for (i =0; i < n; ++i)      b[a[i]- small] += 1;    j = 0;   for (i =0; i < m; ++i){      while(b[i] > 0){         a[j++]= i + small;         b[i]-= 1;      }   }    free(b);} 


负杂度分析:

T(n) = Θ(n)

 

堆排序

算法原理:

堆:堆分为最大堆和最小堆,是一种特殊的完全二叉树,特点是:它的根节点比其他所有节点都要小(最小堆)或大(最大堆),而它的左右子树也是最小堆或最大堆。按照从上到下,从左到右,分层地依次存入数组的话,节点a[i]的左子节点为a[2i+1],右子节点为a[2i+2]。

数组的堆化:是指对不符合堆标准的数组进行调整使其符合堆标准的过程。一种方法是从最后一个元素到第一个元素,将其与其父节点进行比较,若比父节点小(或大)则交换它们,一直比较到根节点。

堆的删除:堆的删除是指移除根节点,并将最后一个元素作为根节点,然后重新调整数组为堆的过程。

堆的调整:堆的调整是指删除堆以后对数组进行的调整,使其符合堆的标准的过程。一种方法是从根节点开始,将其与较小(或大)的子节点进行比较,若比子节点大,则交换它们,一直比较交换直到到了叶子节点或者交换条件不成立。

堆排序:对需排序的数组进行堆化,然后不断删除堆,调整堆,直到堆为空。将堆删除的节点依次收集得到排好序的序列。这个方法就称为堆排序。

 

算法过程:

1.   初始化堆,即将输入数组a[n]堆化。

2.   定义临时数组b[n]。

3.   执行堆的删除操作,将删除的节点加入到b[n]中。

4.   将删除节点后的堆进行调整。

5.   若堆不为空,回到3。

6.   输出数组b[n]。

7.   终止。

 

C语言描述:

void heap_init(int* a, int n){   int i;   for (i =n - 1; i > 0; --i){      int j= i;      while(j > 0){         intp;         inttemp;         if(j % 2) p = (j - 1) / 2;//左节点         elsep = (j - 2) / 2;//右节点         if(a[j] < a[p]){            temp= a[p];            a[p]= a[j];            a[j]= temp;         }         j =p;      }   }}void heap_adjust(int *a, int n){   int i =0;   while (i< n){      int c= i;      int l= 2 * i + 1 < n ? 2 * i + 1 : i;      int r= 2 * i + 2 < n ? 2 * i + 2 : i;      int j= a[l] < a[r] ? l : r;      if(a[i] <= a[j]) break;      else{         inttemp = a[i];         a[i]= a[j];         a[j]= temp;         i =j;      }   }}int heap_delete(int *a, int n){   int e =a[0];   a[0] =a[n - 1];   heap_adjust(a,n - 1);   return e;}void heap_sort(int* a, int n){   int i =n, j = 0;   int* b =(int*)malloc(sizeof(int)*n);    heap_init(a,n);   while (i> 0)      b[j++]= heap_delete(a, i--);    for (i =0; i < n; ++i)      a[i] =b[i];    free(b);}


负责度分析:

T(n) = Θ(n)

 

桶排序

算法原理: 一个序列的数,必然是在某一个范围内的,这个范围由最小的数与最大的数决定。桶排序将这个范围细分为多分区间,这些区间称为桶。在对一个序列进行排序时,将数放入相应的桶中,若桶中有多个数则该桶再次进行桶排序,最后从小到大依次出桶,就得到排好序的序列了。

 

算法过程:

1.   输入数组a[n],定义桶数组t[n]。需要注意的是,数组t[n]是一个元素为数组的数组,当然,也可以说是元素为容器的数组,因为每个元素都可能容纳若干数。

2.   遍历数组a[n],得到最小的数为small,最大的位high。

3.   令len=(high – small)/n。len记录细分区间的长度。

4.   遍历数组a[n]。令j=(a[i]-small)/len。将a[i]放入桶t[j]。

5.   顺序遍历桶数组t[n],若桶t[j]只包含一个数则将其出桶,否则将该桶进行桶排序(实现为迭代)后出桶。

6.   终止。

 

C语言描述:

最小堆排序:

void bucket_sort(int *a, int n){   int i, j;   intlen;//区间长度   int**bucket;//桶数组   int*elems;//桶容量数组   int num =n + 1;//桶个数   int small= a[0];   int big =a[0];    if (n ==2){//只有两个元素,排列好后返回      if(a[0] > a[1]){         inttemp = a[0];         a[0]= a[1];         a[1]= temp;      }      return;   }    for (i =0; i < n; ++i){//找出最大最小值      if(a[i] < small) small = a[i];      if(a[i] > big) big = a[i];   }   if (big== small) return;//所有元素相同,直接返回,若没有这句,将导致出桶时无限迭代   len =(big - small) / n + 1;    elems = (int*)malloc(sizeof(int)*num);   for (i =0; i < num; ++i)      elems[i]= n / 5 + 2;   bucket =(int**)malloc(sizeof(int*)*num);   for (i =0; i < num; ++i){      bucket[i]= (int*)malloc(elems[i]*sizeof(int));//为桶分配空间,至少有两个元素的空间      bucket[i][0]= 0;//第一个元素记录有多少个数入桶   }    for (i =0; i < n; ++i){//入桶      j =(a[i] - small) / len;      bucket[j][0]+= 1;      if(bucket[j][0] >= elems[j]){//桶空间不足,重新分配         intk;         elems[j]*= 2;         int*temp = (int*)malloc(sizeof(int)*elems[j]);         for(k = 0; k < bucket[j][0]; ++k)            temp[k]= bucket[j][k];         free(bucket[j]);         bucket[j]= temp;      }      bucket[j][bucket[j][0]]= a[i];   }    for (i =0, j = 0; i < num; ++i){//出桶      if(bucket[i][0] == 0) continue;      elseif (bucket[i][0] == 1)         a[j++]= bucket[i][1];      else{//桶元素有多个,迭代         intk;         intm = bucket[i][0];         int*p = &bucket[i][1];         bucket_sort(p,m);         for(k = 0; k < m; ++k)            a[j++]= p[k];      }   }    free(elems);   for (i =0; i < n; ++i)      free(bucket[i]);   free(bucket);} 


复杂度分析:

T(n) = Θ(n)

 

基数排序

算法原理:基数排序是一种针对整数的排序,它可以看做是一种特殊的桶排序,使用0-9共10个桶。基数排序分为两种:LSD(least significant digital)和MSD(most significant digital)。LSD从数的最低位(即个位)开始,将数依次放入对应的桶中,然后依次出桶,再按数的下一位为基准依次入桶,然后依次出桶,直到最高位为止。MSD与LSD相仿,不过是从数的最高位开始,到最低位止,并且入桶后并不立刻出桶,而是以桶内的数为序列,以下一位为基准,再次进行基数排序。基数排序一般用于整数排序,但也可以用于浮点数,不过算法需要进行一些改变。

 

算法过程:

LSD:

1.   输入数组a[n]。遍历数组,得出最大的数,其位数为m。

2.   令i为数的当前基准位数。i=1。

3.   以数的第i位为基准依次入桶。

4.   依次出桶。i=i+1。若i>m,跳到5,否则重复3,4。

5.   终止。

MSD:

1.   输入数组a[n]。遍历数组得到最大的数,其位数为m。

2.   令i为数的当前基准位数。i=m。

3.   以数的第i位为基准入桶。i=i-1。

4.   若i>0,以各桶内的数为输入序列,分别跳到3。(此处应为实现为迭代)。

5.   依次出桶,得到最后排好序的序列。

6.   终止。

 

C语言描述:

LSD:

#define getbit(n,b) ((int)(n/pow(10, b-1))%10)void radix_sort(int* a, int n){   int i;   int* len;   int*bucket[10];   int bits= 1, big = a[0];    for (i =1; i < n; ++i)      if(a[i]>big) big = a[i];    while (big/ (int)(pow(10, bits)))      bits+= 1;//位数    len =(int*)malloc(sizeof(int) * 10);//分配空间   for (i =0; i < 10; ++i)      len[i]= n / 5 + 2;   for (i =0; i < 10; ++i){      bucket[i]= (int*)malloc(sizeof(int)*len[i]);   }    i = 0;   while(++i <= bits)   {      int j,h;       for (j= 0; j < 10; ++j)//重置桶         bucket[j][0]= 0;       for (j= 0; j < n; ++j){//入桶         intb = getbit(a[j], i);         bucket[b][0]+= 1;         if(bucket[b][0] >= len[b]){            intk;            len[b]*= 2;            int*t = (int*)malloc(sizeof(int)*len[b]);            for(k = 0; k < bucket[b][0]; ++k)                t[k]= bucket[b][k];            free(bucket[b]);            bucket[b]= t;         }         bucket[b][bucket[b][0]]= a[j];      }       h = 0;      for (j = 0; j < 10; ++j){//出桶         intk;         for(k = 1; k <= bucket[j][0]; ++k)            a[h++]= bucket[j][k];      }    }    free(len);   for (i =0; i < 10; ++i)      free(bucket[i]);} 


复杂度分析:

T(n) = Θ(n)

 

注:

1.   logn表示log₂n

 

1 0
原创粉丝点击