排序算法总结

回到数据结构与算法,再次把一些经典的排序算法的代码回顾一下。

1. 插入排序

可以想象手里有一副扑克牌,从左到右是{4, 3, 7, 1, 9}。一张一张来看。

4

4,3 排序有误。将3插入到4前,变成 3,4。

3,4,7排序无误。

3,4,7,1排序有误,将1插入到3前,变成1,3,4,7

......

代码为:

void swap(int &a, int & b)

{

  int temp = a;

  a = b;

  b= temp;

}

void insertSort(int a[], int length)

{

  for(int i=1;i<length;i++)

  {

    for(int j=i;i>=0;j--)

    {

      if(a[j-1]>a[j]) swap(a[j-1], a[j]);

    }

  }

}

特征:原地排序,空间复杂度为O(1)。平均时间复杂度为O(n2),最坏时间复杂度为O(n2)。排序是稳定的。

当基本有序时,使用插入排序效率很高。

2. 冒泡排序

特点就是像冒泡泡一样,一点一点往前移动。总是将当前数与右边数相比,如果当前数更大就和右边的数交换,否则不交换。

例如数组为{4, 3, 7, 1, 9}。

第一轮:{3, 4, 1, 7, 9}

第二轮:{3, 1, 4, 7, 9}

第三轮:{1, 3, 4, 7, 9} 结束

代码如下:

void bubbleSort(int a[], int length)

{

  int count = 1;

  while(count >0)

  {

    count = 0;

    for(int i=0;i<length-1;i++)

    {

      if(a[i]>a[i+1])

      {

        swap(a[i], a[i+!]);

        count++;

      }

    }

  }

}

特点:原地排序,空间复杂度为O(1)。时间复杂度为O(n2),最坏时间复杂度为O(n2)。排序是稳定的。

3. 堆排序

关于堆排序介绍众多,此处就不再放图了。主要就是不断交换父节点和子节点来维持有序。

代码如下:

void heapSort(int a[], int length)

{

  //从最后一个父节点开始,初始化堆

  for(int i = len/2-1;i>=0;i--)

  {

    heapify(a, i, length);    

  }

  //然后不断将最大值与最后的子节点交换,得到有序序列

  for(int i=length-1;i>=0;i--)

  {

    swap(a[0], a[i]);

    heapify(a, 0, i);

  }

}

void heapify(int a[], int start, int end)

{

  int father = start, son = 2*father + 1;

  while(son < end)

  {

    if(son + 1<end &&a[son] < a[son + 1] ) son++;

    if(a[father] > a[son]) break;

    else

    {

      swap(a[son], a[father]);

      father = son;

       son = father*2 + 1;

    }

  }

}

特点:原地排序,空间复杂度为O(1)。时间复杂度为O(nlogn),最好时间复杂度最坏时间复杂度都是O(nlogn)。为不稳定排序。

适合求topK问题。

4. 快速排序

简单来说,就是一路从右往左扫掠,另一路从左往右扫掠,一旦遇到违反排序的,就交换。

void quickSort(int a[], int start, int end)

{

  if(start>=end) return;

  int pivot = onceSort(a, start, end);

  quickSort(a, start, pivot-1);

  quickSort(a, pivot+1, end);

}

int onceSort(int a[], int start, int end)

{

  int i = start, j = end;

  while(i<j)

  {

    while(i<j && a[i]<a[j]) j--;

    if(i<j) swap(a[i], a[j]);

    while(i<j && a[i]<a[j]) i++;

    if(i<j) swap(a[i], a[j]);

  }

  return i;

}

特点:原地排序,空间复杂度为O(logn)。平均时间复杂度O(nlogn),最坏时间复杂度为O(n2)。排序不稳定。

当数组基本有序时,时间复杂度反而会增加。因此一般在排序前先要打乱来保证高效。

5. 归并排序

典型的二分法排序。

void mergeSort(int a[], const int length)

{

  int help[length];

  merge(a, help, 0, length-1); 

}

void merge(int a[], int help[], int start, int end)

{

  if(start>=end) return; 

  int mid = (start + end)/2;

  merge(a, help, start, mid);

  merge(a, help, mid+1, end);

  int l1 = start, l2 = mid, r1 = mid+1, r2 = end;

  int k = l1;

  while(l1<=l2 && r1<=r2)

  {

    if(a[l1]<a[r1])

      help[k++] = a[l1++];

    else

      help[k++] = a[r1++];

  }  

  while(l1<=l2)  

    help[k++] = a[l1++];

  while(r1<=r2)

    help[k++] = a[r1++];

  for(int k = l1;k<=r2;k++)

    a[k] = help[k];

}

特点:借助辅助数组,非原地排序。空间复杂度为O(n)。平均时间复杂度为O(nlogn),最坏时间复杂度也为O(nlogn),为稳定排序。

 

原文地址:https://www.cnblogs.com/corineru/p/11080477.html