排序算法归纳

部分排序实现:

插入排序

1.直接插入排序

原理:将数组分为无序区和有序区两个区,然后不断将无序区的第一个元素按大小顺序插入到有序区中去,最终将所有无序区元素都移动到有序区完成排序。

要点:设立哨兵,作为临时存储和判断数组边界之用。

实现:

void InsertSort(int *p_iSort, int p_ilength)

{

 /* p_iAfterSort = new int[p_ilength];

  memset((void*) p_iAfterSort, 0x00, sizeof(int) * p_ilength);*/

  int iIndex = 0, iIndex2 = 0;

  int temp = 0;

  for (iIndex = 1; iIndex < p_ilength; iIndex ++)

  {

    iIndex2 = iIndex – 1;

    if (p_iSort[iIndex2] > p_iSort[iIndex])

    {

      temp  = p_iSort[iIndex];

      while (p_iSort[iIndex2] > temp && iIndex2 >= 0)

      {

        p_iSort[iIndex2 + 1] = p_iSort[iIndex2];

        iIndex2 — ;

      }

      p_iSort[iIndex2 + 1] = temp;

    }

  }

}

2.希尔排序

原理:又称增量缩小排序。先将序列按增量划分为元素个数相同的若干组,使用直接插入排序法进行排序,然后不断缩小增量直至为1,最后使用直接插入排序完成排序。

要点:增量的选择以及排序最终以1为增量进行排序结束。

实现:

void shellSort(int *p_iSort, int p_ilength)

{

  int iTempLenth = 0, iIndex = 0, iIndex_2 = 0, temp = 0;

  for(iTempLenth = p_ilength/2; iTempLenth >= 1; iTempLenth = iTempLenth/2)  // 长度折半

  {

    for(iIndex = iTempLenth; iIndex < p_ilength; iIndex ++)     // 直接插入排序算法

    {

      temp = p_iSort[iIndex];

      for(iIndex_2 = iIndex – iTempLenth;(iIndex_2 >= 0) && (p_iSort[iIndex_2] > temp); iIndex_2 = iIndex_2 – iTempLenth)

      {

        p_iSort[iIndex_2 + iTempLenth] = p_iSort[iIndex_2];

      }

      p_iSort[iIndex_2 + iTempLenth] = temp;

    }

  }

}

交换排序

1.冒泡排序

原理:将序列划分为无序和有序区,不断通过交换较大元素至无序区尾完成排序。

要点:设计交换判断条件,提前结束以排好序的序列循环。

实现:

// 冒泡排序

void BubbleSort(int *p_iSort, int p_ilength)

{

  int iIndex = 0, iIndex_2 = 0;

  bool bunSorted = false;

  for (iIndex = p_ilength – 1; iIndex >= 0; iIndex –)

  {

    bunSorted = false;

    for (iIndex_2 = 0; iIndex_2 < iIndex; iIndex_2 ++)

    {

      if (p_iSort[iIndex_2] > p_iSort[iIndex_2 + 1])

      {

        int iTemp = p_iSort[iIndex_2 + 1];

        p_iSort[iIndex_2 + 1] = p_iSort[iIndex_2];

        p_iSort[iIndex_2] = iTemp;

        bunSorted = true;

      }

    }

    if (!bunSorted)

    {

      break;

    }

  }

}

2.快速排序

原理:不断寻找一个序列的中点,然后对中点左右的序列递归的进行排序,直至全部序列排序完成,使用了分治的思想。

要点:递归、分治

实现:

int QuickSortStep(int p_iSort[], int p_iLow, int p_iHigh)
{
 // assert(p_iSort[p_iLow] != 0x00);
  int key = p_iSort[p_iLow];
  while (p_iLow < p_iHigh)
  {
    while (p_iSort[p_iHigh] >= key && p_iLow < p_iHigh)
    {
      p_iHigh –;
    }
    p_iSort[p_iLow] = p_iSort[p_iHigh];
    while (p_iSort[p_iLow] <= key && p_iLow < p_iHigh)
    {
      p_iLow ++;
    }
    p_iSort[p_iHigh] = p_iSort[p_iLow];
      
  }
  p_iSort[p_iLow] = key;
  
  return p_iHigh;
 
}
void QuickSort(int p_iSort[], int p_iLow, int p_iHigh)
{
  if (p_iLow >= p_iHigh) return;                            
  int index = QuickSortStep(p_iSort, p_iLow, p_iHigh);  //完成一次单元排序 
  QuickSort(p_iSort, p_iLow, index – 1);
  QuickSort(p_iSort, index + 1, p_iHigh);
}


选择排序

1.直接选择排序

原理:将序列划分为无序和有序区,寻找无序区中的最小值和无序区的首元素交换,有序区扩大一个,循环最终完成全部排序。

要点:

实现:

Void SelectSort(Node L[])

{

Int i,j,k;//分别为有序区,无序区,无序区最小元素指针

For(i=0;i<length;i++)

{

k=i;

For(j=i+1;j<length;j++)

{

If(L[j]<L[k])

k=j;

}

If(k!=i)//若发现最小元素,则移动到有序区

{

Int temp=L[k];

L[k]=L[i];

L[i]=L[temp];

}

 

}

}

2.堆排序

原理:利用大根堆或小根堆思想,首先建立堆,然后将堆首与堆尾交换,堆尾之后为有序区。

要点:建堆、交换、调整堆

实现:

Void HeapSort(Node L[])

{

BuildingHeap(L);//建堆(大根堆)

For(int i=n;i>0;i–)//交换

{

Int temp=L[i];

L[i]=L[0];

L[0]=temp;

Heapify(L,0,i);//调整堆

}

}


Void BuildingHeap(Node L[])

{ For(i=length/2 -1;i>0;i–)

Heapify(L,i,length);

}

归并排序

原理:将原序列划分为有序的两个序列,然后利用归并算法进行合并,合并之后即为有序序列。

要点:归并、分治

实现:

Void MergeSort(Node L[],int m,int n)

{

Int k;

If(m<n)

{

K=(m+n)/2;

MergeSort(L,m,k);

MergeSort(L,k+1,n);

Merge(L,m,k,n);

}

}


基数排序

原理:将数字按位数划分出n个关键字,每次针对一个关键字进行排序,然后针对排序后的序列进行下一个关键字的排序,循环至所有关键字都使用过则排序完成。

要点:对关键字的选取,元素分配收集。

实现:

Void RadixSort(Node L[],length,maxradix)

{

Int m,n,k,lsp;

k=1;m=1;

Int temp[10][length-1];

Empty(temp); //清空临时空间

While(k<maxradix) //遍历所有关键字

{

For(int i=0;i<length;i++) //分配过程

{

If(L[i]<m)

Temp[0][n]=L[i];

Else

Lsp=(L[i]/m)%10; //确定关键字

Temp[lsp][n]=L[i];

n++;

}

CollectElement(L,Temp); //收集

n=0;

m=m*10;

k++;

}

}

部分算法复杂度:

1.插入排序:每次将一个待排的记录插入到前面的已经排好的队列中的适当位置。
①.直接插入排序


直接排序法在最好情况下(待排序列已按关键码有序),每趟排序只需作1次比较而不需要移动元素。所以n个元素比较次数为n-1,移动次数0。
最差的情况下(逆序),其中第i个元素必须和前面的元素进行比较i次,移动个数i+1,所以总共的比较次数 比较多,就不写出来了


总结:是一种稳定的排序方法,时间复杂度O(n^2),排序过程中只要一个辅助空间,所以空间复杂度O(1)


②.希尔排序


缩小增量排序,对直接插入排序的一种改进
分组插入方法。


总结:是一种不稳定的排序方法,时间复杂度O(n^1.25),空间复杂度O(1)


2.交换排序
①.冒泡排序


最好的情况下,就是正序,所以只要比较一次就行了,复杂度O(n)
最坏的情况下,就是逆序,要比较n^2次才行,复杂度O(n^2)


总结:稳定的排序方法,时间复杂度O(n^2),空间复杂度O(1),当待排序列有序时,效果比较好。


②.快速排序


通过一趟排序将待排的记录分割成独立的两部分,其中一部分记录的关键字均比另一个部分的关键字小,然后再分别对这两个部分记录继续进行排序,以达到整个序列有效。


总结:在所有同数量级O(nlogn)的排序方法中,快速排序是性能最好的一种方法,在待排序列无序时最好。算法的时间复杂度是O(nlogn),最坏的时间复杂度O(n^2),空间复杂度O(nlogn)


3.选择排序
①.直接选择排序


和序列的初始状态无关


总结:时间复杂度O(n^2),无论最好还是最坏


②.堆排序


直接选择排序的改进


总结:时间复杂度O(nlogn),无论在最好还是最坏情况下都是O(nlogn)


4.归并排序


总结:时间复杂度O(nlogn),空间复杂度O(n)


5.基数排序


按组成关键字的各个数位的值进行排序,是分配排序的一种。不需要进行排码值间的比较就能够进行排序。


总结:时间复杂度O(d(n+rd))


总总结:


n比较小的时候,适合 插入排序和选择排序


基本有序的时候,适合 直接插入排序和冒泡排序


n很大但是关键字的位数较少时,适合 链式基数排序


n很大的时候,适合 快速排序 堆排序 归并排序


无序的时候,适合 快速排序


稳定的排序:冒泡排序 插入排序 归并排序 基数排序


复杂度是O(nlogn):快速排序 堆排序 归并排序


辅助空间(大 次大):归并排序 快速排序


好坏情况一样:简单选择(n^2),堆排序(nlogn),归并排序(nlogn)


最好是O(n)的:插入排序 冒泡排序

点赞