几种常用的排序算法(快速排序,希尔排序,堆排序,选择排序,冒泡排序)

1、归并排序
      基本原理:归并排序也称合并排序,其算法思想是将待排序序列分为两部分,依次对分得的两个部分再次使用归并排序,之后再对其进行合并。操作步骤如下。
(1)将所要进行的排序序列分为左右两个部分,如果要进行排序的序列的起始元素下标为first,最后一个元素的下标为last,那么左右两部分之间的临界点下标mid=(first+last)/2,这两部分分别是A[first … mid]和A[mid+1 … last]。
(2)将上面所分得的两部分序列继续按照步骤(1)继续进行划分,直到划分的区间长度为1。
(3)将划分结束后的序列进行归并排序,排序方法为对所分的n个子序列进行两两合并,得到n/2或n/2+l个含有两个元素的子序列,再对得到的子序列进行合并,直至得到一个长度为n的有序序列为止。
//归并排序
void merge(int arr[], int low, int mid, int high)
{
int i, k;
int *tmp = (int *)malloc((high-low+1)*sizeof(int));
//申请空间,使其大小为两个
int left_low = low;
int left_high = mid;
int right_low = mid + 1;
int right_high = high;
for(k=0; left_low<=left_high && right_low<=right_high; k++){  // 比较两个指针所指向的元素
if(arr[left_low]<=arr[right_low]){
tmp[k] = arr[left_low++];
}else{
tmp[k] = arr[right_low++];
}
}
if(left_low <= left_high){  //若第一个序列有剩余,直接复制出来粘到合并序列尾
//memcpy(tmp+k, arr+left_low, (left_high-left_low+l)*sizeof(int));
for(i=left_low;i<=left_high;i++)
tmp[k++] = arr[i];
}
if(right_low <= right_high){
//若第二个序列有剩余,直接复制出来粘到合并序列尾
//memcpy(tmp+k, arr+right_low, (right_high-right_low+1)*sizeof(int));
for(i=right_low; i<=right_high; i++)
tmp[k++] = arr[i];
}
for(i=0; i
arr[low+i] = tmp[i];
free(tmp);
return;
}

void merge_sort(int arr[],  int first,  int last){
int mid = 0;
if(first
mid = (first+last)/2;
merge_sort(arr, first, mid);
merge_sort(arr, mid+1,last);
merge(arr,first,mid,last);
}
return;
}


2.快速排序
      快速排序算法 的基本思想是:将所要进行排序的数分为左右两个部分,其中一部分的所有数据都比另外一 部分的数据小,然后将所分得的两部分数据进行同样的划分,重复执行以上的划分操作,直 到所有要进行排序的数据变为有序为止。




(1)定义两个变量low和high,将low、high分别设置为要进行排序的序列的起始元素和最后一个元素的下标。第一次,low和high的取值分别为0和n-1,接下来的每次取值由划分得到的序列起始元素和最后一个元素的下标来决定。
(2)定义一个变量key,接下来以key的取值为基准将数组A划分为左右两个部分,使数组平均分为两个长度相等的可以最理想,但不易实现,通 常,key值为要进行排序序列的第一个元素值。第一次的取值为A[0],以后毎次取值由要划 分序列的起始元素决定。
(3)从high所指向的数组元素开始向左扫描,扫描的同时将下标为high的数组元素依次与划分基准值key进行比较操作,直到high不大于low或找到第一个小于基准值key的数组元素,然后将该值赋值给low所指向的数组元素,同时将low右移一个位置。
(4)如果low依然小于high,那么由low所指向的数组元素开始向右扫描,扫描的同时将下标为low的数组元素值依次与划分的基准值key进行比较操作,直到low不小于high或找到第一个大于基准值key的数组元素,然后将该值赋给high所指向的数组元素,同时将high左移一个位置。
(5)重复步骤(3) (4),直到low的植不小于high为止,这时成功划分后得到的左右两部分分别为A[low……pos-1]和A[pos+1……high],其中,pos下标所对应的数组元素的值就是进行划分的基准值key,所以在划分结束时还要将下标为pos的数组元素赋值 为 key。
(6)将划分得到的左右两部分A[low……pos-1]和A[pos+1……high]继续采用以上操作步骤进行划分,直到得到有序序列为止。
//快速排序
int partition(int arr[], int low, int high){
int key;
key = arr[low];
while(low
while(low key )
high--;
if(low
arr[low++] = arr[high];
while( low
low++;
if(low
arr[high--] = arr[low];
}
arr[low] = key;
return low;
}
void quick_sort(int arr[], int start, int end){
int pos;
if (start
pos = partition(arr, start, end);
quick_sort(arr,start,pos-1);
quick_sort(arr,pos+1,end);
}
return;
}
3.冒泡排序
  冒泡排序的基本思想就是不断比较相邻的两个数,让较大的元素不断地往后移。经过一轮比较,就选出最大的数;经过第2轮比较,就选出次大的数,以此类推。
//冒泡排序
//一般实现
void bubble_sort(int a[],int n)//n为数组a的元素个数
{
//一定进行N-1轮比较
for(int i=0; i
{
//每一轮比较前n-1-i个,即已排序好的最后i个不用比较
for(int j=0; j
{
if(a[j] > a[j+1])
{
int temp = a[j];
a[j] = a[j+1];
a[j+1]=temp;
}
}
}
}
//优化实现  在数组已经排序好的情况下,会提前退出循环,减小了算法的时间复杂度。
void bubble_sort_better(int a[],int n)//n为数组a的元素个数
{
//最多进行N-1轮比较
for(int i=0; i
{
bool isSorted = true;
//每一轮比较前n-1-i个,即已排序好的最后i个不用比较
for(int j=0; j
{
if(a[j] > a[j+1])
{
isSorted = false;
int temp = a[j];
a[j] = a[j+1];
a[j+1]=temp;
}
}
if(isSorted) break; //如果没有发生交换,说明数组已经排序好了
}
}
4.选择排序
选择排序(从小到大)的基本思想是,首先,选出最小的数,放在第一个位置;然后,选出第二小的数,放在第二个位置;以此类推,直到所有的数从小到大排序。
//选择排序实现
void select_sort(int a[],int n)//n为数组a的元素个数
{
//进行N-1轮选择
for(int i=0; i
{
int min_index = i; 
//找出第i小的数所在的位置
for(int j=i+1; j
{
if(a[j] < a[min_index])
{
min_index = j;
}
}
//将第i小的数,放在第i个位置;如果刚好,就不用交换
if( i != min_index)
{
int temp = a[i];
a[i] = a[min_index];
a[min_index] = temp;
}
}
}


5.插入排序
    插入排序的基本思想是,将元素逐个添加到已经排序好的数组中去,同时要求,插入的元素必须在正确的位置,这样原来排序好的数组是仍然有序的。
//插入排序实现,这里按从小到大排序
void insert_sort(int a[],int n)//n为数组a的元素个数
{
//进行N-1轮插入过程
for(int i=1; i
{
//首先找到元素a[i]需要插入的位置
int j=0;
while( (a[j]
{
j++;
}
//将元素插入到正确的位置
if(i != j)  //如果i==j,说明a[i]刚好在正确的位置
{
int temp = a[i];
for(int k = i; k > j; k--)
{
a[k] = a[k-1];
}
a[j] = temp;
}
}
}
6.希尔排序
基本思想:先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。
//希尔排序
void shellsort(int a[], int n)
{
int i, j, gap;




for (gap = n / 2; gap > 0; gap /= 2)
for (i = gap; i < n; i++)
for (j = i - gap; j >= 0 && a[j] > a[j + gap]; j -= gap)
{
int temp = a[j];
a[j] = a[j + gap];
a[j + gap] = temp;
}
}
7.堆排序
     堆排序是利用堆的性质进行的一种选择排序。
1.堆
  堆实际上是一棵完全二叉树,其任何一非叶节点满足性质:
  Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]或者Key[i]>=Key[2i+1]&&key>=key[2i+2]
  即任何一非叶节点的关键字不大于或者不小于其左右孩子节点的关键字。
  堆分为大顶堆和小顶堆,满足Key[i]>=Key[2i+1]&&key>=key[2i+2]称为大顶堆,满足 Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]称为小顶堆。由上述性质可知大顶堆的堆顶的关键字肯定是所有关键字中最大的,小顶堆的堆顶的关键字是所有关键字中最小的。
2.堆排序的思想
   利用大顶堆(小顶堆)堆顶记录的是最大关键字(最小关键字)这一特性,使得每次从无序中选择最大记录(最小记录)变得简单。
    其基本思想为(大顶堆):
    1)将初始待排序关键字序列(R1,R2....Rn)构建成大顶堆,此堆为初始的无须区;
    2)将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,......Rn-1)和新的有序区(Rn),且满足R[1,2...n-1]<=R[n]; 
    3)由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,......Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2....Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。


//堆排序
void HeapAdjust(int *a, int i, int size)  //调整堆 
{
int lchild = 2 * i;       //i的左孩子节点序号 
int rchild = 2 * i + 1;     //i的右孩子节点序号 
int max = i;            //临时变量 
if (i <= size / 2)          //如果i不是叶节点就不用进行调整 
{
if (lchild <= size&&a[lchild]>a[max])
{
max = lchild;
}
if (rchild <= size&&a[rchild]>a[max])
{
max = rchild;
}
if (max != i)
{
int temp = a[max];
a[max] = a[i];
a[i] = temp;
HeapAdjust(a, max, size);    //避免调整之后以max为父节点的子树不是堆 
}
}
}




void BuildHeap(int *a, int size)    //建立堆 
{
int i;
for (i = size / 2; i >= 0; i--)    //非叶节点最大序号值为size/2 
{
HeapAdjust(a, i, size);
}
}




void HeapSort(int *a, int size)    //堆排序 
{
int i;
BuildHeap(a, size);
for (i = size-1; i >=0; i--)
{
int temp = a[0];
a[0] = a[i];
a[i] = temp;           //交换堆顶和最后一个元素,即每次将剩余元素中的最大者放到最后面 
//BuildHeap(a,i-1);        //将余下元素重新建立为大顶堆 
HeapAdjust(a, 0, i - 1);      //重新调整堆顶节点成为大顶堆
}
}b

你可能感兴趣的:(C/C++)