【选择排序】和【堆排序】

一、选择排序

(1)选择排序的算法思路(假设升序)
每次从待排序的区间中找到一个最小数,然后将这个最小数和该区间的第一个数交换数值;然后缩小区间,继续这样的的方法,最终到该待排区间为一个数时停止,排序完成;

总体算法分三步完成:选数据—>将所选数据放入合适位置—>缩小需要排序的范围

(2)图说
这里写图片描述
(3)程序

#include<iostream>
#include<assert.h>
using namespace  std;


---------//核心代码


void SelectSort(int* arr,int sz)//sz为数组的尾下标
{
    assert(arr);
    int left=0;//记录待排序区间的首下标
    while(left<sz)
    {
        int min=left;//记录待排序区间的最小值的小标
        int begin=left;//遍历待排序区间
        //while循环用于寻找待排序区间的最小值,并用min记住最小值下标
        while (begin<=sz)
        {
            if (arr[min]>arr[begin])
            {
                min=begin;//更新最小值的下标
            }
            ++begin;//比较下一个数字
        }
        swap(arr[left],arr[min]);//将该区间的最小值放在该区间的首位;
        ++left;//缩小待排序区间
    }
}


----------


void Printf(int* arr,int sz)
{
    assert(arr);
    int i=0;
    while (i<sz)
    {
        cout<<arr[i]<<" ";
        i++;
    }
}
int main()
{
    int a[]={5,6,7,9,3,4,1,2};
    int sz=sizeof(a)/sizeof(a[0]);
    SelectSort(a,sz-1);
    Printf(a,sz);
    return 0;
}

(4)测试结果:
这里写图片描述

(5)选择排序的优化
上面的选择排序的写法每次只能在待排序的区间中寻找出一个最小的数,那么一共要寻找N趟,每趟比较和交换N次;所以时间复杂度为O(N^2);

选择排序的优化是,每趟从待排序区间找出两个数,一个最大数,一个最小数,最小数放待排序区间的第一位,最大数放待排序区间的最后一位;
这样只需要寻找N/2趟;

(5.1)优化后的图解
这里写图片描述

(5.2)优化后的选择排序的程序

#include<iostream>
#include<assert.h>
using namespace  std;


----------//选择排序的优化

void SelectSort(int* arr,int sz)
{
    assert(arr);
    int left=0;//待排序区间的首下标
    int right=sz;//待排序区间的尾下标
    while (left<right)
    {
        int min=left;//记录最小值的小标
        int max=right;//记录最大值的下标
        int begin=left;//遍历待排序区间
        while (begin<=right)//一趟排序下来找到一个最大值一个最小值
        {
            if (arr[min]>arr[begin])
            {
                min=begin;
            }
            if (arr[max]<arr[begin])
            {
                max=begin;
            }
            ++begin;
        }

        swap(arr[min],arr[left]);//将小值放在区间的第一位
        if(max==left)//处理最大值为第一个输的情况,此时经过上面最小值和地址为元素的交换,此时最大值放在min下标的位置
        {
            max=min;//将最大值小标更新交换以后的最小值的下标处,此时该处存放的是最大值
        }
        swap(arr[max],arr[right]);//将最大值放在区间的最后一位
        ++left;
        --right;
    }
}


----------


void Printf(int* arr,int sz)
{
    assert(arr);
    int i=0;
    while (i<sz)
    {
        cout<<arr[i]<<" ";
        i++;
    }
}
int main()
{
    int a[]={5,6,7,9,3,4,1,2};
    int sz=sizeof(a)/sizeof(a[0]);
    SelectSort(a,sz-1);
    Printf(a,sz);
    return 0;
}

(6)选择排序的时间复杂度&&空间复杂度

  • 时间复杂度:O(N^2)
  • 空间复杂度:O(1)

二、堆排序

1.堆排序的思想:

假设升序排列,先将一个待排序的数组建成大堆,然后这时这个数的第一个数即堆顶的元素就是最大值,那么这时候将堆顶的元素和数组的最后一个元素交换;这时最后一个位置的元素就是将这些数排列好后的最大值应该待的位置;这时候将剩下的元素继续调成大堆;由于之前是将首尾元素相交换,所以每次是都从第一个元素开始,向下调整堆为大堆;调整好以后继续重复上面的操作;直到数组的待排序元素只剩一个时,就不用交换调堆,该数就是最小的数,就应该放在第一个位置;排序结束;

升序—建大堆 ; 降序—建小堆

这里写图片描述

2.时间复杂度

每次调堆O(lgN);一共拿N次元素;
所以时间复杂度O(N*lgN)

3.代码实现


//堆排:升序---建大堆 ;  降序---建小堆

#include<iostream>
using namespace std;
#include<cassert>


----------*******************核心代码*********************************
//向下调堆
void AdjustDown(int* arr,int father,int sz)
{
    assert(arr);
    int child=father*2+1;
    while (child<sz)
    {
        //找到左右孩子较大的一个
        if (child+1<sz&&arr[child+1]>arr[child])
        {
            child++;
        }
        if (child<sz&&arr[father]<arr[child])
        {
            swap(arr[father],arr[child]);
            father=child;
            child=father*2+1;
        }
        else
            return ;
    }
}
void HeapSort(int* arr,int sz)
{
    assert(arr&&sz>0);
    //1.建大堆  
    for (int i=(sz-2)/2;i>=0;i--)
    {
        AdjustDown(arr,i,sz);
    }
    //2.交换首尾元素;继续调堆
    while (sz>1)//待排序的元素个数必须大于一个
    {
        sz--;
        swap(arr[0],arr[sz]);
        AdjustDown(arr,0,sz);
    }
}
---------*******************核心代码*********************************
int main()
{
    int arr[]={6,4,5,2,9,8,1};
    int sz=sizeof(arr)/sizeof(arr[0]);
    HeapSort(arr,sz);
    for (int i=0;i<sz;i++)
    {
        cout<<arr[i]<<" ";
    }
    cout<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值