码迷,mamicode.com
首页 > 编程语言 > 详细

排序(二)

时间:2014-11-03 14:45:36      阅读:301      评论:0      收藏:0      [点我收藏+]

标签:style   io   color   ar   os   使用   for   sp   strong   

(一) 堆排序:由于优先队列可以花费O(NlogN)时间的排序,基于该想法的排序称之为堆排序。建立N个元素的二叉堆,基本上花费的时间为O(N),再执行N次删除操作。按照顺序呢,最小的元素先离开该堆。通过将这些元素记录到第二个数组然后在将数组拷贝回来,就可得到N个元素的排序。由于每个Delete操作花费时间。

该算法的主要问题在于它使用了一个附加的数组,因此,存储需求增加了一倍,注意,将第二个数组拷贝回第一个数组的额外时间消耗只是O(N),这不可能显著影响运行时间问题,这只是一个空间问题。

避免使用第二个数组的聪明的做法是:在每次delete之后,堆缩小了1,因此,位于堆中最后的单元可以用来存放刚刚删去的元素,使用这种策略,在最后一次delete之后,该数组将以递减的顺序包含这些元素。若果想使这些元素排成典型的递增的顺序,我们可以改变序的特性,是父节点的元素大于孩子节点的元素。

    在实现的过程中,我们使用一个大顶堆(MaxHeap),但是由于速度原因避免了实际上的ADT(每一件事都是在一个数组中试实现的)。第一步,一线性时间建立一个堆。然后通过将堆中的最后元素与第一个元素进行交换,缩减堆的大小,并进行下滤,来实行 N - 1 次delete操作。

堆排序是一种非常稳定的算法,它平均使用的比较次数比最坏情形下的略少。

//4. 堆排序(1)【只是用一个数组,而不必建立一个堆的数据结构(使用大顶堆):从小到大排列的】
#define LChild(i) (i * 2 + 1)  //去 i 节点的左孩子(因为cichu)

//建立一个大顶堆
void BuildMaxHeap(int A[], int i, int N)
{
 int Child;
 int tmp;  //临时变量,用于存放父节点 i 的值
 for(tmp = A[i]; LChild(i) < N; i = Child)  //i 为父节点,LChild为其孩子节点
 {
  Child = LChild(i);       //下滤过程
  if(Child != N-1 && A[Child] < A[Child + 1])  //父节点 i 与其左右孩子进行比较取最大者
   Child++;
  if(tmp < A[Child])
   A[i] = A[Child];  //使用大的孩子节点替代父节点
  else
   break;
 }
 A[i] = tmp;    //填补空穴
}

//(2)建立一个小顶堆.最后得到的结果是从大到小排列的
void BuildMinHeap(int A[], int i, int N)
{
 int Child;
 int tmp;
 for(tmp = A[i]; LChild(i) < N; i = Child)
 {
  Child = LChild(i);
  if(Child != N -1 && A[Child] > A[Child + 1])  //寻找 i 节点的最小孩子,并将其替换 i 处的元素
   Child++;
  if(tmp > A[Child])  //若父节点 i 大于最小孩子,则使用它的最小孩子替换 i 处的元素
   A[i] = A[Child];
  else
   break;
 }
 A[i] = tmp;   //填补空穴
}

void Swap(int *a, int *b)
{
 int tmp;
 tmp = *a;
 *a = *b;
 *b = tmp;
}

void HeapSort(int A[], int N)
{
 int i;
 for(i = N / 2; i >= 0; i--) //注意 i 的起始位置
 {
  //BuildMaxHeap(A, i, N);
  BuildMinHeap(A, i, N);
 }
 for(i = N - 1; i > 0; i--)                                                    
 {
  Swap(&A[0], &A[i]);  //使用一个数组
  //BuildMaxHeap(A, 0, i);
  BuildMinHeap(A, 0, i);
 }
}

(二) 归并排序:归并排序以O(NlogN)最坏情形的时间运行,而且它使用的比较次数几乎是最优的,是递归算法的一个很好的实例。

    这个算法的基本操作是合并两个已经排好序的表,由于这两个表已经排好序,所以若将输出放到第三个表中时,则该算法可以同过对这两个表的一次排序来完成。基本的排序算法是取两个排序数组 A,B,一个输出数组 C,以及三个计数器 Aptr,Bptr,Cptr分别指向三个数组的首元素。A[Aptr] 和 B[Bptr]中的较小的元素被存放到C[Cptr]中,相关计数器向下移动一个位置,当一个表用完的时候,将另一个表的剩余元素拷贝到C 数组中。

合并两个已经排好序的表所用的时间显然是线性的,因为进行了N - 1次比较,其中N是数组元素的个数。归并算法很容易描述,当N = 1时,只有一个元素需要排序,结果是显然的。否则,递归的将前半部分和后半部分的数据,各自进行归并排序。得到排序后的两部分数据,然后使用上述提到的合并算法对两个一排好序的表进行合并。

    该算法是经典的分治策略:它将问题分成一些小的问题,然后递归的求解,而治的阶段是将分的阶段所得到的各个答案进行修补到一起。分治策略是递归非常有力的用法。

//归并排序(分治策略):递归
//参数:Lpos:左半部分数组的第一个元素,Rpos是右半部分数组的第一个元素,Rend是右半部分数组的最后一个元素

void Merge(int A[], int TmpArray[], int Lpos, int Rpos, int Rend)
{
 int i, Lend, NumElements, TmpPos;  //初始化

 Lend = Rpos -1;   // Lend是左半部分数组的最后一个元素
 NumElements = Rend - Lpos + 1;  //数组的总长度
 TmpPos = Lpos;    //TmpArray是一个临时数组,用于存放已经排好序的元素
 while(Lpos <= Lend && Rpos <= Rend)
 {
  if(A[Lpos] <= A[Rpos])
   TmpArray[TmpPos++] = A[Lpos++];
  else
   TmpArray[TmpPos++] = A[Rpos++];
 }
 while(Lpos <= Lend)    //左半部分数组有剩余
  TmpArray[TmpPos++] = A[Lpos++];
 while(Rpos <= Rend)    //右半部分数组有剩余
  TmpArray[TmpPos++] = A[Rpos++];

 for(i = 0; i < NumElements; i++, Rend--)  //将临时数组中的元素存放到原数组中
  A[Rend] = TmpArray[Rend];
}

void MSort(int A[], int TmpArray[], int Left, int Right)
{
 int Center;
 if(Left < Right)
 {
  Center = (Left + Right) / 2; //分界点
  MSort(A, TmpArray, Left, Center);   //分(左)
  MSort(A, TmpArray, Center + 1, Right);
  Merge(A, TmpArray, Left, Center + 1, Right); //合
 }

}

void MergeSort(int A[], int N)
{
 int *TmpArray;  // 创建一个临时数组
 TmpArray = new int[N];
 if(TmpArray != NULL)
 {
  MSort(A, TmpArray, 0, N - 1);
  free(TmpArray);
 }
 else
  throw exception("No space for tmp array!");
}

虽然归并排序的时间复杂度为O(NlogN),但它很难用于主存排序,主要问题在于合并两个排序表需要线性附加内存,在整个算法中还要花费将数据拷贝到临时数组,再从临时数组中拷贝回来这样一些附加工作,其结果严重放慢的排序的速度。这种拷贝可以通过在递归交替层次审慎的交换 A 和 TmpArray 的角色得到避免。对于重要的内部排序而言人们还是选择快速排序。

 //各种排序算法
#include<iostream>
using namespace std;
typedef int ElemType;
//4. 堆排序(1)【只是用一个数组,而不必建立一个堆的数据结构(使用大顶堆):从小到大排列的】
#define LChild(i) (i * 2 + 1)  //去 i 节点的左孩子(因为cichu)
void BuildMaxHeap(int A[], int i, int N)
{
 int Child;
 int tmp;  //临时变量,用于存放父节点 i 的值
 for(tmp = A[i]; LChild(i) < N; i = Child)  //i 为父节点,LChild为其孩子节点
 {
  Child = LChild(i);       //下滤过程
  if(Child != N-1 && A[Child] < A[Child + 1])  //父节点 i 与其左右孩子进行比较取最大者
   Child++;
  if(tmp < A[Child])
   A[i] = A[Child];  //使用大的孩子节点替代父节点
  else
   break;
 }
 A[i] = tmp;    //填补空穴
}
//(2)建立一个小顶堆.最后得到的结果是从大到小排列的
void BuildMinHeap(int A[], int i, int N)
{
 int Child;
 int tmp;
 for(tmp = A[i]; LChild(i) < N; i = Child)
 {
  Child = LChild(i);
  if(Child != N -1 && A[Child] > A[Child + 1])  //寻找 i 节点的最小孩子,并将其替换 i 处的元素
   Child++;
  if(tmp > A[Child])  //若父节点 i 大于最小孩子,则使用它的最小孩子替换 i 处的元素
   A[i] = A[Child];
  else 
   break;
 }
 A[i] = tmp;   //填补空穴
}
void Swap(int *a, int *b)
{
 int tmp;
 tmp = *a;
 *a = *b;
 *b = tmp;
}
void HeapSort(int A[], int N)
{
 int i;
 for(i = N / 2; i >= 0; i--) //注意 i 的起始位置
 {
  //BuildMaxHeap(A, i, N);
  BuildMinHeap(A, i, N);
 }
 for(i = N - 1; i > 0; i--)                                                     
 {
  Swap(&A[0], &A[i]);  //使用一个数组
  //BuildMaxHeap(A, 0, i);
  BuildMinHeap(A, 0, i);
 }
}
//归并排序(分治策略):递归
//参数:Lpos:左半部分数组的第一个元素,Rpos是右半部分数组的第一个元素,Rend是右半部分数组的最后一个元素
void Merge(int A[], int TmpArray[], int Lpos, int Rpos, int Rend)
{
 int i, Lend, NumElements, TmpPos;  //初始化
 Lend = Rpos -1;   // Lend是左半部分数组的最后一个元素
 NumElements = Rend - Lpos + 1;  //数组的总长度
 TmpPos = Lpos;    //TmpArray是一个临时数组,用于存放已经排好序的元素
 while(Lpos <= Lend && Rpos <= Rend)
 {
  if(A[Lpos] <= A[Rpos])
   TmpArray[TmpPos++] = A[Lpos++];
  else
   TmpArray[TmpPos++] = A[Rpos++];
 }
 while(Lpos <= Lend)    //左半部分数组有剩余
  TmpArray[TmpPos++] = A[Lpos++];
 while(Rpos <= Rend)    //右半部分数组有剩余
  TmpArray[TmpPos++] = A[Rpos++];
 for(i = 0; i < NumElements; i++, Rend--)  //将临时数组中的元素存放到原数组中
  A[Rend] = TmpArray[Rend];
}
void MSort(int A[], int TmpArray[], int Left, int Right)
{
 int Center;
 if(Left < Right)
 {
  Center = (Left + Right) / 2; //分界点
  MSort(A, TmpArray, Left, Center);   //分(左)
  MSort(A, TmpArray, Center + 1, Right);
  Merge(A, TmpArray, Left, Center + 1, Right); //合
 }
}
void MergeSort(int A[], int N)
{
 int *TmpArray;  // 创建一个临时数组
 TmpArray = new int[N];
 if(TmpArray != NULL)
 {
  MSort(A, TmpArray, 0, N - 1);
  free(TmpArray);
 }
 else
  throw exception("No space for tmp array!");
}int main()
{
 int arr[10] = {5, 2, 8, 6, 3, 1, 7, 9, 4, 10};
 HeapSort(arr, 10);
 
 for(int i = 0; i < 10; i++)
  cout << arr[i] << " ";
 cout << endl;
 system("pause");
 return 0;
}

 

 

排序(二)

标签:style   io   color   ar   os   使用   for   sp   strong   

原文地址:http://my.oschina.net/u/2260265/blog/340175

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!