Algorithm——1.排序.md

排序算法

交换类排序:冒泡排序、快速排序

选择类排序:简单选择排序、堆排序

插入类排序:直接插入排序、希尔排序

归并类排序:归并排序递归实现与非递归实现

交换类排序

交换类排序:冒泡排序、快速排序

冒泡排序

冒泡排序(Bubble Sort),排序的基本思想为两两比较小相邻数据的关键字,如果顺序为反则进行交换,直到没有反序的记录为止。

冒泡排序有多种变化,其三种不同实现的代码如下:

//OS:Win    | Terminal:Cmder | Editor:Atom | Language:c

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

//最容易的排序,从第一个元素开始向后与每一个元素进行比较,交换得到最小的元素,放在第i个位置
//这样排序进行较多次无用的比较,当然,这并不是所谓的冒泡排序
void SimpleBubble(int array[] , int length){
    for(int i = 0 ; i < length ; i++){
        for(int j = i+1 ; j < length ; j++){
            if(array[i] > array[j]){
                Swap(&array[i],&array[j]);
            }
        }
    }
}

//从第一个位置开始,由下往上两两元素进行比较,判断并交换得到最小值
//这样排序依次循环确定第i个位置的元素
void LittleBubble(int array[] ,int length){
    for(int i =0 ; i < length ; i++){
        for(int j = length -1 ; j > i ;j --){
            if(array[j] < array[j-1]){
                Swap(&array[j],&array[j-1]);
            }
        }
    }
}

//增加标志位isSwap来表示是否存在反序或者是否进行过交换操作
//直到序列没有反序记录为止
void BubbleSort(int array[] , int length){
    int isSwap = 1;
    for(int i = 0 ; i < length ; i ++){
        if(isSwap){
            isSwap = 0;
            for(int j = length -1 ; j > i ; j --){

                if(array[j] < array[j-1]){

                    Swap(&array[j],&array[j-1]);
                    isSwap = 1;

                }

            }
        }
    }
}

void Print(int array[] , int length){
    for (int i = 0 ; i < length ;i++){
        printf(" %d",array[i]);
    }
}

int main(){
    int array[] = {6,5,3,1,0,2,9,8,4,7};
    BubbleSort(array,10);
    Print(array,10);
    return 0;
}

快速排序

快速排序(Quick Sort)是在实践中最快的已知排序算法,平均运行时间是O(N log N)。快速排序之所以快是因为其精炼的、高度优化的内部循环。最坏情况的性能为O(N2),如今高度优化后的快速排序简单易懂并且不难证明。

和归并排序一样,快速排序也是一种分治的递归算法。

将数组array进行快速排序QuickSort的基本算法由以下简单的四步组成:

  • 1.如果数组长度为0或者1,则直接返回;
  • 2.使用Median3或其他方法获取枢纽元素pivot;
  • 3.划分子集;
  • 4.递归调用QuickSort

int Median3(int array[], int left , int right)
{
  int mid = (left+right)/2;
  if(array[left] > array[mid])
    Swap(&array[left],&array[mid]);
  if(array[left] > array[right])
    Swap(&array[left],&array[right]);
  if(array[mid] > array[right])
    Swap(&array[mid],&array[right]);

  //提前将mid位置元素放置合适位置
  Swap(&array[mid],&array[right-1]);
  return right-1;
}

void Quick_Sort(int array[], int left , int right)
{

  if(left >= right)
    return;
  int pivot = Median3(array,left,right);
  int i = left , j = right-1;
  //Notice1:只有当i<j时才进入循环,i与j循环移动与pivot位置进行比较
  while(i<j){
    //Notice2:指示位置i所处元素与pivot处元素进行比较,如果小,则继续++向后移动;如果大,则跳出该次while循环
    while(array[++i] < array[pivot]){}
    //Notice3:指示位置j所处元素与pivot处元素进行比较,如果大,则继续--向前移动;如果小,则跳出该次while循环
    while(array[--j] > array[pivot]){}

    //Notice4:当i与j都跳出while循环时,比较i与j的大小,若i任然小于j,则交换两个位置的元素,再次进入外部while循环;如果i已经超过j位置,则直接break
    if(i<j){
      Swap(&array[i],&array[j]);
    }else{
      break;
    }

  }
  //Notice5:交换i与pivot处元素,即确定该pivot处元素最终位置,已i位置为界完成此次子集划分
  Swap(&array[i],&array[pivot]);
  //Notice3:递归调用快速排序
  Quick_Sort(array,left,i-1);
  Quick_Sort(array,i+1,right);
}

void QuickSort(int array[] , int length)
{
  Quick_Sort(array,0,length-1);
}

其中,Quick_Sort()中的外层while循环也可以写成for循环,代码如下:

void Quick_Sort(int array[] , int left ,int right){
  if(left >= right)
    return;
  int pivot = Median3(array,left,right);
  int i = left, j = right-1;

  for( ; ; ){
    if(i>=j)
      break;

    while(array[++i] < array[pivot]){}
    while(array[--j] > array[pivot]){}

    if(i<j)
      Swap(&array[i],&array[j]);
    else
      break;
  }

  Swap(&array[i],&array[pivot]);
  Quick_Sort(array,left,i-1);
  Quick_Sort(array,i+1,right);
}

选择类排序

选择类排序:简单选择排序、堆排序

简单选择排序

简单选择排序(Simple Selection Sort),基本思想是:标记第i个元素为最小值下标min开始向后进行遍历比较,不断更新最小值下标min,结束该次循环后判断min是否改变,若改变即交换i位置元素及min位置的最小元素。

void SelectSort(int array[] , int length){
  int min;
  for(int i = 0 ; i <length ; i++){
    min  = i ;  //Notice1:以第一个元素为最小开始向后遍历比较
    for(int j = i+1 ; j < length ; j++){  //Notice2:j从i+1开始向后遍历
      if(array[j] < array[min]){  //Notice3:每次都是array[j]与array[min]进行比较,来确定和更新最小值所在下标
        min = j;
      }
    }
    if(min != i){
      Swap(&array[i] ,&array[min]);
    }
  }
}

堆排序

堆排序(Heap Sort)是一个非常稳定的算法,排序平均使用的比较只比最坏情况界指出的略少。

void AdjustHeap(int array[] , int i , int length){
  //Notice2:保存开始节点的值为temp,减少直接交换的次数
  int temp = array[i];
  for(int j = i*2 +1 ; j <length ; j = j*2+1){
    if(j+1 < length && array[j] < array[j+1])
      j++;
    if(array[j] < temp )  //Notice3:循环对temp中保存的值进行比较
      break;
    array[i] = array[j];
    i = j;
  }
  array[i] = temp;  //Notice4:最后在temp合适的位置上进行赋值放置
}

void HeapSort(int array[] ,int length){
  //Notice1:首先从最后一个非页子节点开始,由右向左、由下至上开始进行最大堆的构造
  for(int i = length/2 ; i >= 0 ; i --){
    AdjustHeap(array , i ,length);
  }

  for(int i = length -1 ; i > 0 ; i --){
    Swap(&array[0],&array[i]);
    AdjustHeap(array, 0, i );
  }
}

插入类排序

插入类排序:直接插入排序、希尔排序

直接插入排序

直接插入排序(Straight Insertion Sort)是最简单的排序算法之一,主要思想是保证位置0到第i-1位置上的元素为已排序状态,即插入排序利用这样的事实,从i位置循环进行排序。

void InsertionSort(int array[] , int length ){
  int i,j,temp;
  //Notice1:开始以第一个数据为有序序列,从第二个数据开始排序
  for(i = 1 ; i <length ; i++){
    if(array[i] < array[i-1]){  //Notice2:当当前数据大于前一个时,开始向前插入
      temp = array[i];  //保存此时的值,为前方较大元素空出位置
      for(j = i -1 ; j>=0 && array[j] > temp ; j--){  //向前循环直至下标小于0,或者值比当前值更小
        array[j+1] = array[j];  //依次后移
      }
      array[j+1] = temp;  //此时j位置的元素小于当前值,所以插入其后一位j+1即可
    }
  }
}

希尔排序

希尔排序(Shell Sort)是冲破二次时间屏障的第一批算法之一。主要思想是:通过比较一定间隔的元素来工作;各趟比较所用的距离(希尔增量)随着算法的进行而逐渐减小,直到比较相元素的最后一趟排序为止。因此,希尔排序也称为缩小增量排序。

void ShellSort(int array[] , int length){
    int i,j,temp,gap;
    int judgeCount = 0, changeCount = 0;
    //Notice1:设置希尔增量序列初始值 gap = length/2 ,一直循环值gap=1进行最后一次插入排序
    for(gap = length/2 ; gap >0 ; gap /=2){
    //Notice2:内层嵌套一个直接插入循环
        for(i = gap ; i < length ; i++ ){
            judgeCount++;
            if(array[i] < array[i - gap]){
                temp = array[i];
                for(j = i - gap ; j >= 0 && array[j] > temp ; j -=gap){
                    array[j+gap] = array[j];
                    changeCount++;
                }
                array[j+gap] = temp;
            }
        }
    }
    printf("Judge Count : %d ,Change Count : %d .\n", judgeCount,changeCount);
}

归并类排序

归并类排序:归并排序递归实现与非递归实现。

当归并排序以O(NlogN)最坏情形运行时间运行时,而所使用的比较次数几乎是最优的。其核心算法Merge的基本操作为:合并两个已经有序的子列。

递归实现


//Notice1:归并排序的核心,两个有序子列的归并
void Merge(int array[] , int tmpArray[] , int l ,int r ,int rEnd )
{
  //Notice2:根据传入的参数得出所要归并的两个有序子列的总长度length、
  //左端有序子列起点l、终点lEnd、右端有序子列起点r、终点rEnd
  int length = rEnd - l + 1;
  int lEnd = r -1;
  int temp = l;

  //Notice3:当同时满足左端指示位置 l <= lEnd、 右端指示位置 r <= rEnd 时,进入while循环
  //此处while循环判定条件为 <= ,当 = 时,进行该子列最后一个元素的比较
  while( l <= lEnd && r <= rEnd)
  {
    //Notice4:此处判断条件为<=,当两个指示指针所指的两个子列中的元素相等时,保持其先后次序
    if(array[l] <= array[r]) tmpArray[temp++] = array[l++];
    else tmpArray[temp++] = array[r++];
  }

  while( l <= lEnd)
    tmpArray[temp++] = array[l++];
  while( r <= rEnd)
    tmpArray[temp++] = array[r++];

  for(int i = 0 ; i < length ; i++ , rEnd--)
  {
    array[rEnd] = tmpArray[rEnd];
  }
}

void Merge_Sort(int array[] ,int tmpArray[] , int l ,int rEnd )
{
  int mid;
  if( l < rEnd)
  {
    mid = (l + rEnd)/2;
    Merge_Sort(array,tmpArray,l,mid);
    Merge_Sort(array,tmpArray,mid+1,rEnd);
    Merge(array,tmpArray,l,mid+1,rEnd);
  }
}


void MergeSort(int array[] , int length)
{
  int tmpArray[length];
  Merge_Sort(array,tmpArray,0,length-1);
  free(tmpArray);
}

非递归实现


//Notice1:归并排序的核心,合并两个有序子列
void Merge(int array[] , int tmpArray[] , int l , int r, int rEnd)
{
  int length = rEnd - l + 1;
  int lEnd = r - 1;
  int temp = l;

  while( l <= lEnd && r <= rEnd)
  {
    if(array[l] < array[r]) tmpArray[temp++] = array[l++];
    else  tmpArray[temp++] = array[r++];
  }

  while( l <= lEnd)
    tmpArray[temp++] = array[l++];
  while( r <= rEnd)
    tmpArray[temp++] = array[r++];

  for(int i = 0 ; i < length ; i++ , rEnd--)
  {
    array[rEnd] = tmpArray[rEnd];
  }
}

//Notice2:对子列长度为gap时的一轮完整归并
void Merge_Pass(int array[] , int tmpArray[] , int length , int gap)
{
  int i,j;
  for(i = 0 ; i <= length - gap*2 ; i += gap*2)
    Merge(array,tmpArray, i , i+gap , i + gap*2 -1);
  if(i+gap < length)
    Merge(array,tmpArray,i,i+gap,length-1);
  else
    for(j = i ; j < length ;j++)
      tmpArray[j] = array[j];
}

//Notice3:当子列长度gap<length时,进行一轮完整归并,并保证排好序的序列赋值到原数组
void MergeSort(int array[] ,int length)
{
  int gap = 1;
  int tmpArray[length];
  while(gap < length)
  {
    Merge_Pass(array,tmpArray,length,gap);
    gap *= 2;
    Merge_Pass(tmpArray,array,length,gap);
    gap *= 2;
  }
  free(tmpArray);
}
REF

书籍:

数据结构与算法分析、大话数据结构

转载于:https://www.cnblogs.com/sylvan/p/9383107.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值