堆排序,归并排序

堆排序

堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。
因为这种排序方式为堆排,所以肯定要先创建一个堆

堆排序分为两个步骤:
  • 1、堆排序需要一个大顶堆(每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆)或者小顶堆,最后堆排序结果一个正序一个逆序
void  heapify(int tree[],int n,int i)
{
  int max = i;    
  int c1 = 2 * i + 1;
  int c2 = 2 * i + 2;

  if(c1 < n && tree[c1] > tree[max])   //筛选出最大的数的下标
  {
      max = c1;
  }
  if(c2 < n && tree[c2] > tree[max])
  {
      max = c2;
  }
  if(max != i)
  {
      swap(tree,max,i);    //交换,让该结点的数大于孩子结点
      
  }

}

2、创建一个堆
堆需要满足的条件是
对于堆中的任意一个三角形结构,顶部元素的值必须是三个数最大的,

每个三角形结构里有三个数(例如0,1,2 **** 1,3,4 **** 2,5,6****3,7,8都可以看做是一个三角形结构),在这三个数中去挑选出最大的数去作为顶部元素在这里插入图片描述
如果想要满足堆的条件,就必须从堆的最底部元素开始遍历,才能够保证每个结点的值都大于孩子节点的值

  • 怎么获得孩子节点和父节点
    • 已知父结点,获得孩子结点的下标
      • parent = i;
      • c1 = 2 * i + 1;
      • c2 = 2 * i + 2;
    • 已知孩子结点,获得父节点的下标
      • parent = (c1 - 1)/2;
      • parent = (c2 - 1) / 2;

找到所有的父节点就可以去遍历整个三角型结构,即构建成了一个堆
从上图中可以看到父节点为3,2,1,0.所以找到最后一个结点的下标,然后在找到其父节点,利用循环递减就可以遍历整个结构中的三角结构。

void build_heap(int tree[],int n)
{
    int last_node = n - 1;
    int parent = (last_node - 1) / 2;
    for(int i = parent;i >= 0;i--)
    {
        heapify(tree,n,i);
    }
}

到这里堆就构建完成了

堆排序思想:
构建好堆之后,把最大的元素即tree[0]和最后一个元素进行交换,这趟就将最大的元素 沉到了数组最低部,去掉这个最大的元素,对这个堆再进行heapify就构建成了一个新堆
这个新堆的元素个数为n - 1,在这个新堆继续把最大的元素沉到堆的底部,再去heapify


最终的数组就是一个有序数组

归并排序

归并排序利用分治思想,将一个数组打散,然后又合并成一个新的数组

在这里插入图片描述

  • 第一次merge,会创建两个新的数组,假设arr1.left和arr2.right,这两个数组会将arr数组一分为二,并且会开始进行排序,返回排序内容给arr(此时的排序结果可能不对)

  • 第二次merge仅考虑左半边,即数组arr1.left,会把arr1.left一分为二,创建数组arr2.left和arr2.right ,并把arr1.left的内容写入这两个数组,进入合并部分,会大致的进行排序,

  • 第三次merge已经将一个大的数组分为了一个数字,一个数字一定有序,所以一定为得到正确的排序结果返回给arr2.left和arr2.right,逐层返回,最终存储到arr数组

  • 这样逐级调用

  • 第三次merge想要执行,就要去从形参arr中去获取地址,就要去找上一层merge,逐层递归,找到数组。

#include<stdio.h>
void merge(int *arr,int L,int M,int R)
{
    int LEFT_SIZE = M - L;
    int RIGHT_SIZE = R - M + 1;
    int left[LEFT_SIZE];
    int right[RIGHT_SIZE];
    
    //分开
    for(int i = 0;i < M;i++)
    {
        left[i-L] = arr[i];

    }
    for(int i = M;i <= R;i++)
    {
        right[i-M] = arr[i];
    }
    //合并
    int i = 0,j = 0,k = L;
    while( i < LEFT_SIZE && j < RIGHT_SIZE  )
    {
        if(left[i] < right[j])
        {
            arr[k] = left[i];
            k++;
            i++;
        }
        else 
        {
            arr[k] = right[j];
            j++;
            k++;
        }
    }
    while(i < LEFT_SIZE)
    {
        arr[k] = left[i];
        i++;
        k++;
    }
    while(j < RIGHT_SIZE)
    {
        arr[k] = right[j];
        j++;
        k++;

    }
}
void mergeSort(int * arr,int L,int R)
{
    if(L == R)
        return ;
    else
    {
        int M = (L+R) / 2;
        mergeSort(arr,L,M); //递归拆开数组并进行合并
        mergeSort(arr,M+1,R);
        merge(arr,L,M+1,R); 
    }
}
int main(void)
{
    int a[100];
    int n ;
    printf("输入要排序的数据个数:");
    scanf("%d",&n);
    for(int i = 0; i < n;i++)
    {
        scanf("%d",&a[i]);
    }
    mergeSort(a,0,n-1);
    for(int i = 0;i< n;i++)
    {
        printf("%d\t",a[i]);
    }
    printf("\n");

}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值