堆排序和插入排序(小博学习排序第四天)

1,堆排序

堆排序的算法步骤;

  • 把无序数组构建成一个二叉堆,需要从小到大排序,则构建最大堆;需要从大到小排序,则构建小顶堆。
  • 循环删除堆顶元素,替换到二叉堆的末尾,调整堆产生新的堆顶。

下来我们讲解他的实现过程

构造堆

将无序数组构造成一个大根堆(升序用大根堆,降序就用小根堆)

假设存在以下数组

主要思路:第一次保证0~0位置大根堆结构(废话),第二次保证0~1位置大根堆结构,第三次保证0~2位置大根堆结构...直到保证0~n-1位置大根堆结构(每次新插入的数据都与其父结点进行比较,如果插入的数比父结点大,则与父结点交换,否则一直向上交换,直到小于等于父结点,或者来到了顶端)

插入6的时候,6大于他的父结点3,即arr(1)>arr(0),则交换;此时,保证了0~1位置是大根堆结构,如下图:

                                     (友情提示:待交换的数为蓝色,交换后的数为绿色)

 插入8的时候,8大于其父结点6,即arr(2)>arr(0),则交换;此时,保证了0~2位置是大根堆结构,如下图

插入5的时候,5大于其父结点3,则交换,交换之后,5又发现比8小,所以不交换;此时,保证了0~3位置大根堆结构,如下图 

插入7的时候,7大于其父结点5,则交换,交换之后,7又发现比8小,所以不交换;此时整个数组已经是大根堆结构 

 

2.2 固定最大值再构造堆

此时,我们已经得到一个大根堆,下面将顶端的数与最后一位数交换,然后将剩余的数再构造成一个大根堆

                                    (友情提示:黑色的为固定好的数字,不再参与排序) 

 此时最大数8已经来到末尾,则固定不动,后面只需要对顶端的数据进行操作即可,拿顶端的数与其左右孩子较大的数进行比较,如果顶端的数大于其左右孩子较大的数,则停止,如果顶端的数小于其左右孩子较大的数,则交换,然后继续与下面的孩子进行比较

下图中,5的左右孩子中,左孩子7比右孩子6大,则5与7进行比较,发现5<7,则交换;交换后,发现5已经大于他的左孩子,说明剩余的数已经构成大根堆,后面就是重复固定最大值,然后构造大根堆

如下图:顶端数7与末尾数3进行交换,固定好7,

剩余的数开始构造大根堆 ,然后顶端数与末尾数交换,固定最大值再构造大根堆,重复执行上面的操作,最终会得到有序数组

以上就是他的实现过程现在让我们讲解代码部分

1,首先我们得在数组中建立一个大顶堆或者小顶堆,代码如下面展示

/创造一个大顶堆
void downAdjust(int *a,int index,int length){
  int lchild,rchild;
  //创建左右子树
  lchild=index*2+1;
  rchild=index*2+2;
  //将最初的点定为最大的点
  int max=index;
  //在左右子树中找出比父亲结点大的点
  if(lchild<length&&a[lchild]>a[max]){
    max=lchild;
  }
  if(rchild<length&&a[rchild]>a[max]){
    max=rchild;
  }
  //如果最大的不是指定位置的值,则进行互换
  if(max!=index){
    //将找到的点和max进行互换
    swap(a[max],a[index]);
    //继续进行递归,在以max为根结点的树中找最大值
    downAdjust(a,max,length);
  }
}

2,进行堆排序

void HeapSort(int *a,int length){
  //从他的每个非叶子节点进行调整,原因是在进行大顶堆调整的时候,内部进行创建左右子树
  //所以只需管他所以的非叶子节点
  for(int i=length/2-1;i>=0;i--){
    downAdjust(a,i,length);
  }
  for(int j=length-1;j>0;j--){
    //将最大的值换到最后面
    swap(a[0],a[j]);
    //重新调整大顶堆,这里注意他的长度已经变了,长度一直在减一
    downAdjust(a,0,j);
  }
}

 2,插入排序

/******************* 排序规则 *******************

    每次处理就是将无序数列的第一个元素与有序数列
    的元素从后往前逐个进行比较,找出插入位置,将
    该元素插入到有序数列的合适位置中。

    稳定性:插入排序是稳定的

***********************************************/

插入排序比较简单,相信大家看排序规则就可以看懂,我们直接上代码

void Insert_Sort(int *a,int length){
  int temp=0;
  int index=0;
  for(int i=1;i<length;i++){
    temp=a[i];
    index=i;
    for(int j=i-1;j>=0;j--){
      if(temp<a[j]){
        a[j+1]=a[j];
        index=j;
      }
      else{
        break;
      }
    }
    a[index]=temp;
  }
}

完整代码展示

//小博学习排序第四天
#include<iostream>
#include<algorithm>
using namespace std;
//堆排序是不稳定的排序
//创造一个大顶堆
void downAdjust(int *a,int index,int length){
  int lchild,rchild;
  //创建左右子树
  lchild=index*2+1;
  rchild=index*2+2;
  //将最初的点定为最大的点
  int max=index;
  //在左右子树中找出比父亲结点大的点
  if(lchild<length&&a[lchild]>a[max]){
    max=lchild;
  }
  if(rchild<length&&a[rchild]>a[max]){
    max=rchild;
  }
  //如果最大的不是指定位置的值,则进行互换
  if(max!=index){
    //将找到的点和max进行互换
    swap(a[max],a[index]);
    //继续进行递归,在以max为根结点的树中找最大值
    downAdjust(a,max,length);
  }
}
//开始进行堆排序
void HeapSort(int *a,int length){
  //从他的每个非叶子节点进行调整,原因是在进行大顶堆调整的时候,内部进行创建左右子树
  //所以只需管他所以的非叶子节点
  for(int i=length/2-1;i>=0;i--){
    downAdjust(a,i,length);
  }
  for(int j=length-1;j>0;j--){
    //将最大的值换到最后面
    swap(a[0],a[j]);
    //重新调整大顶堆,这里注意他的长度已经变了,长度一直在减一
    downAdjust(a,0,j);
  }
}
//附加写一个插入排序
/******************* 排序规则 *******************

	每次处理就是将无序数列的第一个元素与有序数列
	的元素从后往前逐个进行比较,找出插入位置,将
	该元素插入到有序数列的合适位置中。

	稳定性:插入排序是稳定的

***********************************************/
void Insert_Sort(int *a,int length){
  int temp=0;
  int index=0;
  for(int i=1;i<length;i++){
    temp=a[i];
    index=i;
    for(int j=i-1;j>=0;j--){
      if(temp<a[j]){
        a[j+1]=a[j];
        index=j;
      }
      else{
        break;
      }
    }
    a[index]=temp;
  }
}
int main(int argc, char const *argv[]) {
  int a1[]={1,3,2,6,5,4,7,8,9};
  int a3[]={1,3,2,6,5,4,7,8,9};
  int length=sizeof(a1)/sizeof(int);
  HeapSort(a1,length);
  for(int i=0;i<length;i++){
    cout<<a1[i]<<" ";
  }
  cout<<endl;
  Insert_Sort(a3,length);
  for(int i=0;i<length;i++){
    cout<<a3[i]<<" ";
  }
  cout<<endl;
  return 0;
}

结果展示

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值