数据结构--常用排序算法

排序算法的稳定性的定义

       例如一组未被排序的数中{B,A1,D,C,A2}存在一对或多对相同的数          
     据A1,A2排序前A1的位置在A2前面,如果使用排序算法后A1,A2的相
     对位置保持不变{A1,A2,B,C,D},那么我们称这个算法是稳定的。

注:排序算法是否为稳定的是由具体算法决定的,不稳定的算法在某种条件下可以变为稳定的算法,而稳定的算法在某种条件下也可以变为不稳定的算法。

冒泡排序

       冒泡排序的简要思路
       1.比较相邻的两个元素如果前一个大于后一个则交换两个数的位置从第一
       对到最后一对,第一次循环结束后最后一个数将是数组里面最大的
       2.除了最后一个元素外,对其他元素重复上述过程,循环直到没有一对数据
       需要进行比较为止。
代码实现:
#include<stdio.h>

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

}

void bubble(int arry[],int len){

  int i,j;
  for(i=0;i<len-1;i++){
    for(j=0;j<len-1-i;j++){
      if(arry[j]>arry[j+1])
        swap(&arry[j],&arry[j+1]);
    }
  }
}
int main()
{
  int arr[]={10,9,8,7,6,5,4,3,2,1};
  int s=sizeof(arr)/sizeof(int);
  printf("len :%d\n",s);
  bubble(arr,s);
  int i=0;
  for(;i<s;i++){
    printf("%d\n",arr[i]);
  }
  return 0;
}

选择排序

   原理:第1趟,在待排序记录r[1]~r[n]中选出最小的记录,将它与r[1]交         
   换;第2趟,在待排序记录r[2]~r[n]中选出最小的记录,将它与r[2]交换;
   以此类推,第i趟在待排序记录r[i]~r[n]中选出最小的记录,它与r[i]交换,
   使有序序列不断增长直到全部排序完毕。
代码实现:


#include<stdio.h>

void select(int arr[],int len){

  if(len<=1){
    return;
  }
  int i=0;
  for(;i<len;i++){
     int min=i;        // i为已排序序列的末尾
     int j=i+1;
     for(;j<len;j++){
        if(arr[j]<arr[min])
          min=j;        // 找出未排序序列中的最小值
     }
     if(min!=i){
       int tmp;
       tmp=arr[i];
       arr[i]=arr[min];
       arr[min]=tmp;
     }
  }
}
int main()
{
  int i;
  int arr[]={5,4,8,9,2,1};
  int s=sizeof(arr)/sizeof(int);
  select(arr,s);
  for(i=0;i<s;i++){
    printf("%d ",arr[i]);
  }
  printf("\n");

  return 0;
}

这里写图片描述

插入排序

   原理:插入排序就像打扑克,左手拿已经排好的扑克,右手拿的是准备插入到
      已排好序列的扑克。将{5,6,7,3,4}分为两部分左手拿的是{5,6,7} 
      右手是{3,4}想要将3插入到{4,5,6,7}中组成有序顺子那么该拿着3从7开始
      依次向前比较直到找到属于它位置,同理重复上述步骤即可将4有序插入。
      插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的
      效率但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位
代码实现:

#include<stdio.h>

void insert(int arr[],int len){
  if(len<=1){
    return;
  }
  int i=1;
  for(;i<len;i++){
    int value=arr[i];     // 右手抓到一张扑克牌
    int j=i;
    for(;j>0;--j){        // 拿在左手上的牌总是排序好的
      if(arr[j-1]>value){ // 将抓到的牌与手牌从右向左进行比较
        arr[j]=arr[j-1];  // 如果该手牌比抓到的牌大,就将其右移
      }else{
        break;
      }
    }
    arr[j]=value;         // 直到该手牌比抓到的牌小(或二者相等)
  }
}

int main()
{

  int arr[]={5,4,2,1,8,9,3};
  int s=sizeof(arr)/sizeof(int);
  insert(arr,s);
  int i=0;
  for(;i<s;i++){
    printf("%d ",arr[i]);
  }
   printf("\n");
  return 0;
}

这里写图片描述

希尔排序

      希尔排序通过将比较的全部元素分为几个区域来提升插入排序的性能。
      这样可以让一个元素可以一次性地朝最终位置前进一大步。
      然后算法再取越来越小的步长进行排序,算法的最后一步就是普通的插入排序。

这里写图片描述

代码实现:

#include<stdio.h>

void shell(int arr[],int size){

  if(size<=1){
    return;
  }
  int i,j,tmp;
  int gap=size/2;
  for(;gap>0;gap=gap/2){
    for(i=gap;i<size;i++){
       j=i-gap;
       tmp=arr[i];
       while(j>=0&&arr[j]>tmp){
         arr[j+gap]=arr[j];
         j=j-gap;
       }
       arr[j+gap]=tmp;
    }
  }

}


int main(){

  int arr[]={9,6,5,2,7,1};
  int s=sizeof(arr)/sizeof(int);
  shell(arr,s);
  int i=0;
  for(;i<s;i++){
    printf("%d ",arr[i]);
  }
  printf("\n");

  return 0;
}

这里写图片描述

堆排序

       堆是一种近似完全二叉树的结构(通常堆是通过一维数组来实现的),
       并满足性质:以最大堆为例,
       其中父结点的值总是大于它的孩子节点。小堆与之相反
       若从小到大排,创建大堆
       若从大到小排,创建小堆
       a.将无需序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;
       b.将堆顶元素与end元素交换,将最大元素"沉"到数组末端;
       c.重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前end元素
       反复执行调整+交换步骤,直到整个序列有序。

这里写图片描述

代码实现:


#include<stdio.h>

void swap(int*arr,int a,int b){
  int tmp;
  tmp=arr[a];
  arr[a]=arr[b];
  arr[b]=tmp;
}


 /*大根堆调整*/  
  void Adjustdown(int * array, int  heapSize, int  currentNode)  
  {  
      int  leftChild, rightChild,  largest;  
      leftChild = 2*currentNode + 1;  
      rightChild = 2*currentNode + 2;  
      if(leftChild < heapSize && array[leftChild] > array[currentNode])  
          largest = leftChild;  
      else  
          largest = currentNode;  
      if(rightChild < heapSize && array[rightChild] > array[largest])  
          largest = rightChild;  
      if(largest != currentNode)  
      {  
          swap(array, largest, currentNode);  
          Adjustdown(array, heapSize, largest);  
      }  
 }      /*构建大根堆*/  
void HeapCreate(int * array, int  heapSize)  
 {  /*注意:这里只是经过一次堆调整之后,并不是将这些数完全用堆排序完成排序*/ 
     int i;  
     for(i = heapSize/2; i >= 0; i--)  
     {  
   Adjustdown(array, heapSize, i);  
     }  
  } 


int main(){

  int arr[]={9,4,6,3,7,2,8,1};
  int s=sizeof(arr)/sizeof(int);
  //heap(arr,s)
  int i=s;
  for(;i>1;i--){
    HeapCreate(arr,i);
    swap(arr,0,i-1);
  }
  printf("Output the MaxHeap:\n");
  for(i=0;i<s;i++){
    printf("%d ",arr[i]);
  }

  printf("\n");
  return 0;
}

这里写图片描述

归并排序

 把长度为n的数组按n/2切分,在对分开的两个子数组按n/4切分,以此类推直到不能
 切分为止,然后进行归并回溯,在回溯的过程中申请一个空间来暂时保存排序的数组,
 排序结束再将结果拷贝回原来的数组中即可

这里写图片描述

代码实现:
#include<stdlib.h>
#include<stdio.h>
//合并两个已经排好序的数组[left...mid]和[mid+1...left]
void merge(int a[],int left,int right,int mid){

  int len=right-left+1;
  int *tmp=(int*)malloc(sizeof(int)*len);
  int index=0;
  int begin1=left;
  int begin2=mid+1;
  while(begin1<=mid&&begin2<=right){
    if(a[begin1]<a[begin2]){
      tmp[index++]=a[begin1++];
    }else{
      tmp[index++]=a[begin2++];
    }
  }
  while(begin1<=mid){
    tmp[index++]=a[begin1++];
  }
  while(begin2<=right){
    tmp[index++]=a[begin2++];
  }
  //将数据拷贝回原来的空间
  int i=0;
  for(;i<len;i++){
    a[left++]=tmp[i];
  }
  free(tmp);

}


void mergesort(int arr[],int left,int right){

  if(left==right){
    return ;
  }
  int mid=(left+right)/2;
  mergesort(arr,left,mid);
  mergesort(arr,mid+1,right);
  merge(arr,left,right,mid);


}


int main()
{

  int arr[]={3,9,5,2,7};
  int s=sizeof(arr)/sizeof(int);
  mergesort(arr,0,s-1);
  int i=0;

  for(;i<s;i++){
    printf("%d ",arr[i]);
  }

  printf("\n");
  return 0;
}

这里写图片描述

快速排序

 给定一个数组,选取arr[size-1]为基准数key,把所有比基准值小的元素放
 在基准前面,所有比基准值大的元素放在基准的后面(相同的数可以到任一边)
 给定left right  
 arr[left]<=key left++             arr[right]>=key right++
 当left right 重合时 将区间划分为左右两部分 按照上述步骤递归直到划分的
 区间中只剩一个数为止

这里写图片描述

代码实现:
#include<stdio.h>
void swap(int a,int b){
  int tmp;
  tmp=a;
  a=b;
  b=tmp;
}

void quicksort(int a[],int begin,int end){

  if(begin>end){
    return ;
  }

  int tmp;
  int left=begin;
  int right=end;
  int key=a[end];
  while(begin<end){

    while(begin<end&&a[begin]<=key){
         ++begin;
    }
    while(begin<end&&a[end]>=key){
         --end;
    }
    if(begin<end){
      //swap(a[left],a[right]);
      tmp=a[begin];
      a[begin]=a[end];
      a[end]=tmp;
    }
  }
  swap(a[begin],a[key]);

  quicksort(a,left,begin-1);
  quicksort(a,begin+1,right);
}


int main()
{
  int arr[]={9,5,2,7};
  int s=sizeof(arr)/sizeof(arr[0]);

  quicksort(arr,0,s-1);
  int i=0;
  for(;i<s;i++){

    printf("%d ",arr[i]);
  }

  printf("\n");
  return 0;
}

这里写图片描述

  挖坑法原理:
  1、寻找坑位,然后将其分为两段数组,然后对这两段数组递归排序;
  2、指定一个基数temp(三数取中法),定义两个指针begin一个指向起始位置,
  end一个指向最后一个元素的位置。begin寻找比基数(temp)大的数字,找到 后
  将begin的数据赋给end,begin成为一个坑,然后end寻找比基数(temp)小的
  数字,找到将end的数据赋给begin,end成为一个新坑,循环这个过程,直到
  begin指针与end指针相遇,然后将temp的数据返回给那个坑,然后进行递归操作。
代码实现:


#include<stdio.h>

void quicksort(int a[],int begin,int end){
  if(begin>end){
    return ;
  }

   int key=a[end];
   int left=begin;
   int right=end;
   while(begin<end){

     while(begin<end&&a[begin]<=key){
         ++begin;
      }
     if(begin<end){
       a[end]=a[begin];
     }
     while(begin<end&&a[end]>=key){
       --end;
     }
     if(begin<end){
       a[begin]=a[end];
     }
   }
   a[begin]=key;

   quicksort(a,left,begin-1);
   quicksort(a,begin+1,right);

}


int main(){

  int arr[]={9,5,2,7};
  int s=sizeof(arr)/sizeof(int);

  quicksort(arr,0,s-1);
  int i=0;
  for(;i<s;i++){

    printf("%d ",arr[i]);

  }
  printf("\n");

  return 0;
}

这里写图片描述

猜你喜欢

转载自blog.csdn.net/Important_/article/details/80538871