排序算法学习

来源:互联网 发布:csgo职业选手优化fps 编辑:程序博客网 时间:2024/06/06 02:08

排序算法有:冒泡排序,快速排序,选择排序,堆排序,插入排序,合并排序,希尔排序,计数排序,基数排序,桶排序。


交换排序: 冒泡排序、快速排序

选择排序:直接选择排序、树形选择排序、堆排序

插入排序:    直接插入排序、希尔排序、合并排序

分配排序:    桶排序、基数排序、计数排序


交换排序

1、冒泡排序:

对待排序元素的关键字从后往前进行多遍扫描,遇到相邻两个关键字次序与排序规则不符时,就将这两个元素进行交换。这样关键字较小的那个元素就像一个泡泡一样,从最后面冒到最前面来。


#include <iostream>  using namespace std;    void BubbleSort(int a[], int n) {      for (int i = 0; i < n; i++) //遍历n次          for (int j = n-1; j > i; j--) {              if (a[j] < a[j-1]) { //当前比较前面键值,使当前总为最小的                  swap(a[j-1], a[j]);//交换              }          }  }  int main()  {      int num[6] = {23,45,13,2,99,78};      cout << "冒泡排序前:" << endl;      for (int i = 0; i < 6; i++) {          cout << num[i] << " ";      }      cout << endl;      BubbleSort(num, 6);      cout << "冒泡排序后:" << endl;      for (int i = 0; i < 6; i++) {          cout << num[i] << " ";      }      cout << endl;      return 0;  }  

冒泡的时间复杂度为O(n2),空间复杂度O(1)。稳定。


2、快速排序

快速排序采用了分治算法策略,它是冒泡排序的一种改进。

基本思路是:把待排列的数据分为两个子列,从数列中挑出一个数作为基准,遍历其他数据,把小于它的放前面,大的放在基准的后面。之后,通过递归,将各个子序列划分为更小的序列,直到把小于基准值元素的子数列和大于基准值元素的字数列排序。


#include <iostream>  using namespace std;    //拆分为两个子列  int Partition(int a[], int left, int right) {      int base = a[left];      while (left < right) {          while (left < right && a[right]>base) //从右往左找出第一个比基准小的数据              --right;          a[left] = a[right]; //将这个数放到基准的左边          while (left < right && a[left]<base) //从左往右找出第一个比基准大的数据              ++left;          a[right] = a[left]; //放到右边      }      a[left] = base;      return left; //返回基准的位置  }  void  QuickSort(int a[], int left, int right) {      int i;      if (left < right) {          i = Partition(a, left, right);          QuickSort(a, left, i-1);          QuickSort(a, i+1, right);      }  }  int main()  {      int num[6] = {23,45,13,2,99,78};      cout << "排序前:" << endl;      for (int i = 0; i < 6; i++) {          cout << num[i] << " ";      }      cout << endl;      QuickSort(num, 0, 5);      cout << "排序后:" << endl;      for (int i = 0; i < 6; i++) {          cout << num[i] << " ";      }      cout << endl;      return 0;  }  
</pre><pre name="code" class="cpp">
<strong><span style="font-size:32px;">选择排序</span></strong>
<strong><span style="font-size:32px;"></span></strong>
<span style="font-size:32px;"></span><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; font-family: Arial; font-size: 14px; line-height: 26px;">1,直接选择排序:</p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; font-family: Arial; font-size: 14px; line-height: 26px;">对待排序的序列,选出关键字最小的数据,将它和第一个位置的数据交换,接着,选出关键字次小的数据,将它与第二个位置上的数据交换。以此类推,直到完成整个过程。</p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; font-family: Arial; font-size: 14px; line-height: 26px;">所以如果有n个数据,那个需要遍历n-1遍。其实现代码如下:</p><strong></strong>
#include <iostream>  using namespace std;    void SelectSort(int a[], int n) {      int i, j, small;      for (i = 0; i < n-1; ++i) {          small = i;          for (j = i+1; j < n; ++j) {              if (a[small] > a[j]) small = j;          }          if (small != i)              swap(a[small], a[i]);      }  }  int main()  {      int num[6] = {23,45,13,2,99,78};      cout << "排序前:" << endl;      for (int i = 0; i < 6; i++) {          cout << num[i] << " ";      }      cout << endl;      SelectSort(num, 6);      cout << "排序后:" << endl;      for (int i = 0; i < 6; i++) {          cout << num[i] << " ";      }      cout << endl;      return 0;  }  
直接选择排序的时间复杂度为O(n2)。同样适合对链式结构进行排序


2、堆排序


#include <iostream>    using namespace std;    //用数组二叉树  void HeapAdjust(int a[], int s, int n)//构成堆  {      int j;      while(2*s + 1 < n) //第s个结点有右子树       {          j=2*s + 1 ;          if((j+1) < n)          {                          if(a[j] < a[j+1])//右左子树小于右子树,则需要比较右子树                  j++; //序号增加1,指向右子树           }          if(a[s] < a[j])//比较s与j为序号的数据          {                          swap(a[s], a[j]);                         s = j ;//堆被破坏,需要重新调整          }          else //比较左右孩子均大则堆未破坏,不再需要调整              break;      }  }  void HeapSort(int a[],int n)//堆排序  {      int t, i;      int j;      for(i = n/2 - 1; i >= 0; i--)    //将a[0,n-1]建成大根堆          HeapAdjust(a, i, n);      for(i = n-1; i > 0; i--)      {          swap(a[0], a[i]);          HeapAdjust(a, 0, i);        //将a[0]至a[i]重新调整为堆      }    }  int main()  {      int num[6] = {23,45,13,2,99,78};        cout << "排序前:" << endl;      for (int i = 0; i < 6; i++) {          cout << num[i] << " ";      }      cout << endl;        HeapSort(num, 6);        cout << "排序后:" << endl;      for (int i = 0; i < 6; i++) {          cout << num[i] << " ";      }      cout << endl;      return 0;  }  

堆排序的时间复杂度为O(nlog2n),空间复杂度为O(1)。不稳定。


插入排序


1,直接插入排序

直接插入排序是一种简单直观的排序算法,它的工作原理是通过建有序序列,对于没有排序的数据,在已排序序列中从后往前扫描,找到相应位置,并插入。故,如果是数组这样的连续空间的数据序列,那就每次插入都要将其位置的后面数据都向后移动,相对比较麻烦。


#include <iostream>  using namespace std;    void InsertSort(int a[], int n) {      int i, j, temp;      for (i = 1; i < n; ++i) {          temp = a[i]; //先保存当前值          for (j = i-1; j >= 0 && temp < a[j]; --j) //从后往前移,直到找到适合位置              a[j+1] = a[j]; //往后移一位,腾出位置          a[j+1] = temp; //将值放入已找出的适当位置      }  }  int main()  {      int num[6] = {23,45,13,2,99,78};      cout << "排序前:" << endl;      for (int i = 0; i < 6; i++) {          cout << num[i] << " ";      }      cout << endl;      InsertSort(num, 6);      cout << "排序后:" << endl;      for (int i = 0; i < 6; i++) {          cout << num[i] << " ";      }      cout << endl;      return 0;  }  

这样的直接插入排序空间复杂度O(1),但是时间复杂度为O(n2)


2、希尔排序

希尔排序(Shell Sort)是插入排序的一种。是针对直接插入排序算法的改进。该方法又称缩小增量排序,因DL.Shell于1959年提出而得名。

它的具体做法是:先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成(n除以d1)个组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。


#include <iostream>  using namespace std;    void ShellSort(int a[], int n) {      int d, i, j, temp;      d = n/2; //分成n/2组      while (d >= 1) {           for (i = d; i < n; ++i) { //对每组进行直接插入排序              temp = a[i];              j = i - d;              while (j >= 0 && a[j] > temp) {                  a[j+d] = a[j];                  j -= d;              }              a[j+d] = temp;          }          d /= 2;      }  }  int main()  {      int num[6] = {23,45,13,2,99,78};      cout << "排序前:" << endl;      for (int i = 0; i < 6; i++) {          cout << num[i] << " ";      }      cout << endl;      ShellSort()      cout << "排序后:" << endl;      for (int i = 0; i < 6; i++) {          cout << num[i] << " ";      }      cout << endl;      return 0;  }  
希尔排序时间复杂度为O(n4/3),空间复杂度为O(1),但是不适合在链表结构上使用。

3、合并排序(归并排序)

 合并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。合并排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。合并排序也叫归并排序。


<pre name="code" class="cpp">#include <iostream>  using namespace std;    //2-路合并  void Merge(int a[], int r[], int left, int mid, int n) {//a[]归并到r[]      int s1 = left, s2 = mid+1, s3 = left;      while (s1 <= mid && s2 <= n)//比较较小的数据填充到a[]          if (a[s1] <= a[s2])               r[s3++] = a[s1++];          else               r[s3++] = a[s2++];      while (s1 <= mid) r[s3++] = a[s1++];//将未填充的数补全      while (s2 <= n) r[s3++] = a[s2++];//将未填充的数补全  }  //2-路归并排序  void MergePass(int a[], int r[], int n, int len) {      int beg = 0, end;      while (beg + len < n) {          end = beg + 2*len - 1;          if (end >= n)//最后一个可能少于len个              end = n - 1;          Merge(a, r, beg, beg+len-1, end);//合并          beg = end + 1;//提供给下次开始      }      if (beg < n)          while (beg < n) {              r[beg] = a[beg];              beg++;          }  }  void MergeSort(int a[], int n) {      int len = 1;//当前进行归并有序数列的长度      int f = 0;      int *p = (int *)malloc(sizeof(int)*n);      while(len < n) {          if (f) MergePass(p, a, n, len);//交替归并到p和a          else MergePass(a, p, n, len);          len *= 2;          f = 1 - f;      }      if (f) //排序后是归并到p的情况,复制回到a          for (f = 0; f < n; f++)              a[f] = p[f];      free(p);  }  int main()  {      int num[6] = {23,45,13,2,99,78};      cout << "排序前:" << endl;      for (int i = 0; i < 6; i++) {          cout << num[i] << " ";      }      cout << endl;      MergeSort(num, 6);      cout << "排序后:" << endl;      for (int i = 0; i < 6; i++) {          cout << num[i] << " ";      }      cout << endl;      return 0;  }  


分配排序


桶排序:

假定:输入是由一个随机过程产生的[0, 1)区间上均匀分布的实数。将区间[0, 1)划分为n个大小相等的子区间(桶),每桶大小1/n:[0, 1/n), [1/n, 2/n), [2/n, 3/n),…,[k/n, (k+1)/n ),…将n个输入元素分配到这些桶中,对桶中元素进行排序,然后依次连接桶输入0 ≤A[1..n] <1辅助数组B[0..n-1]是一指针数组,指向桶(链表)。

他的排序过程如图:




桶排序只是用于关键字取值范围较小的情况,否则会因为所需箱子的数目太多而导致资源的浪费。这种排序的使用价值不大,他一般用于基数排序的一个中间过程。


2,基数排序:

基数排序是桶排序的一种改进和推广。它的基本思想是,先设立r个队列,队列编号分别为0~r-1,(r为关键字的基数),然后按照下面的规则对关键字进行“分配”和“收集”。

1, 按照最低有效位的值,把n个关键字分配到上述的r个队列里,然后从小到达将各队列中关键字收集起来。

2, 再按低次有效位的值把刚刚收集起来的关键字分配到r个队列中,重复收集工作。

3, 重复上述分配和收集工作,直到最高的有效位。(也就是说,如果数位为d,则需要重复进行d次。d由所有元素中最长的一个元素的位数计量。)

图示如下:



上图过程,就是先按个位分配然后收集,再按十位,百位分配和收集,最后就得出排序结果。

在C++中可以使用库函数lexicongraphical_compare()进行字典次序比较。

每一趟分配的时间是O(n),所以总时间的开销为O(d(n+r)) = O(n),通常d,r为常数。空间负复杂度为O(n+r)。基数排序使用于采用链式结构存储结构的排序。

3,计数排序:

 计数排序是一个类似于桶排序的排序算法,其优势是对已知数量范围的数组进行排序。它创建一个长度为这个数据范围的数组C,C中每个元素记录要排序数组中对应记录的出现个数。这个算法于1954年由 Harold H. Seward 提出。

计数排序算法的基本思想是对于给定的输入序列中的每一个元素x,确定该序列中值小于x的元素的个数。一旦有了这个信息,就可以将x直接存放到最终的输出序列的正确位置上。例如,如果输入序列中只有17个元素的值小于x的值,则x可以直接存放在输出序列的第18个位置上。

#include <iostream>    using namespace std;  //n为个数,k为最大值,b为输出  void CountingSort(int a[], int b[], int n, int k)  {      int* c = new int[k+1];      memset(c, 0, (k+1) * sizeof(int));      for (int j = 0; j < n; j++) c[a[j]]++;//保存每个下标的值的个数      for (int j = 1; j <= k; j++) c[j] += c[j - 1];//从前到后累计的位置      for (int j = n - 1; j >= 0; j--)      {          b[c[a[j]] - 1] = a[j];          c[a[j]]--;        }      delete []c;  }    int main()  {      int num[6] = {23,45,13,2,99,78};      int out[6];      cout << "排序前:" << endl;      int max = 0;      for (int i = 0; i < 6; i++) {          if (max < num[i]) max = num[i];          cout << num[i] << " ";      }      cout << endl;        CountingSort(num, out, 6, max);        cout << "排序后:" << endl;      for (int i = 0; i < 6; i++) {          cout << out[i] << " ";      }      cout << endl;      return 0;  }  



0 0
原创粉丝点击