排序算法(三)选择排序

选择排序

基本思想

每一趟在后面n-i+1个待排序的元素中找到最小的然后放置在第i个位置,就是位置0-length-1下标相当于是已经知道顺序的了,然后在序列里找每组序列的最小元素放在这个已知位置就可以了。

简单选择排序

基本思想

每一趟排序记录当前元素的最终位置,设置一个变量,每当要交换的时候,随时更改变量的值

#include <iostream>
using namespace std;
void EasyChoiceSort(int *Array,int length);
int main ()
{
    int Array[11]={0,2,4,1,6,3,7,8,9,5,1};
    int length=10;
    EasyChoiceSort(Array,length);
    cout<<"Result1:";
    for(int i=1;i<=10;i++)
    cout<<Array[i]<<" ";
    cout<<endl;
    return 0;
}
void EasyChoiceSort(int *Array,int length)
{
    int location;
    for(int i=1;i<=length;i++)
    {
        location=i;
        for(int j=i+1;j<=length;j++)
        {
            if(Array[j]<Array[location])
                location=j;
        }
        //一开始没加这句,其实无形之中提升了效率,如果位置都相同的话,就不用再交换一次了
        if(location!=i)
        {
            Array[0]=Array[location];
            Array[location]=Array[i];
            Array[i]=Array[0];
        }
        
    }
}

简单排序算法性能分析
性能分析
空间O(1)
时间简单排序的过程中,元素移动的操作次数很少,不会超过3(n-1)次,最好的情况是移动0次,此时对应的表已经有序;但元素间比较的次数与序列的初始状态无关,始终是n(n-1)/2次,所以时间复杂度为O(n2)
稳定性不稳定
适用性顺序表,但是如果给链表其实也可以

堆排序

堆排序特点

在排序过程中,将整个顺序表看成一棵完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系,在当前无序区中选择关键字最大(或最小)的元素。

堆的定义
大根堆

最大元素存放在根结点中,且对任意非根结点,它的值小于或等于其双亲结点

小根堆

最小元素存放在根结点中,且对任意非根结点,它的值大于或等于其双亲结点

相关算法思路
  • 构建初始堆
    初始建堆的过程就是一个不断筛选的过程。假设该序列长度为n,最后一个元素应当是n/2的子节点,对以n/2为根的子树筛选,若根结点小于左右子结点的最大值,则交换,使该子树称为堆。然后依次向前n/2~1为根的子树进行筛选,看结点值是否大于左右子结点的最大值,若不是交换,但是交换后可能会破坏下一级的堆,于是继续采用上述方法构造下一级的堆,直到以该结点为根的子树重新成为堆为止。反复利用上述调整堆的方法建堆,知道根结点为止。
  • 堆排序
    首先将存放在数组中的n个元素建成初始堆,由于堆本身的特点,堆顶元素就是最大值。堆顶元素输出之后,将堆底元素送入堆顶,此时根结点已不满足大根堆的性质,堆被破坏,将堆顶元素向下调整使其继续保持大根堆的性质,再输出堆顶元素,反复至堆中只剩下一个元素为止。
    用了将堆顶元素和最后一个元素交换,然后每次进行调整的范围都减小1.
  • 堆的删除和插入
    堆的删除只能在堆顶进行,将堆顶元素和最后一个元素交换,然后再将新的堆顶元素进行向下调整AdjustDown()
    堆的插入先将新的结点放在最后,然后再对这个结点进行向上调整AdjustUp()
  • 向上调整堆
    不断向上调整和父节点进行比较,结点值大于双亲结点,将双亲结点下调,并继续向上比较
#include <iostream>
using namespace std;

void BuildMaxHeap(int *Array,int length);
void AdjustDown(int *Array,int k,int length);
void HeapSort(int *Array,int length);
void AdjustUp(int *Array,int length);
void DeleteElement(int *Array,int length);
void InsertElement(int *Array,int length,int key);

int main ()
{
    int Array[9]={0,53,17,78,9,45,65,87,32};
    int length=8;
    BuildMaxHeap(Array, length);
    cout<<"ResultInitial:";
    for(int i=1;i<=8;i++)
    cout<<Array[i]<<" ";
    cout<<endl;
    DeleteElement(Array, length);
    cout<<"ResultDelete:";
       for(int i=1;i<=7;i++)
       cout<<Array[i]<<" ";
       cout<<endl;
   /* HeapSort(Array, length);
    cout<<"Result1:";
    for(int i=1;i<=8;i++)
    cout<<Array[i]<<" ";
    cout<<endl;*/
   
    return 0;
}
//初始建堆
void BuildMaxHeap(int *Array,int length)
{
    //遍历整个数组只需要从n/2往前,利用完全二叉树的性质就可以对整个数组进行排列
    for(int i=length/2;i>0;i--)
    {
        AdjustDown(Array,i, length);
    }
}
//先从宏观上看不要陷入算法的细节
//函数AdjustDown是为了找到k的最终位置,而在寻找的过程中把中间不恰当的位置都掉换了
void AdjustDown(int *Array,int k,int length)
{
    Array[0]=Array[k];
    for(int i=2*k;i<=length;i=i*2)//注意这里for循环条件的理解
    {
        //先对两个子节点的大小进行判断,选择出二者中最合适的
        if(Array[i]<Array[i+1]&&i<length)
            ++i;
        //将选择出来的与标准值进行比较
        if(Array[i]<Array[0])
            break;
        else{//将发生交换的进行记录,就知道可能会发生破坏的下一级的堆的位置,不发生交换的当然不会发生破坏,就直接记录这个发生交换的就行
            Array[k]=Array[i];
            k=i;//当发现不平衡并对当前一层调整完之后,更新为根继续,i和k是相等的,这样利用i乘2之后才能得到下一个的子节点
        }
    }
     Array[k]=Array[0];//找到最终k的位置
}
//堆排序算法
//第一个位置排列的一定是最大的元素,利用最后一个元素与第一个元素交换,然后向下调整
//这样最后一个位置放置的就是整个序列中最大的元素,然后依次递减
void HeapSort(int *Array,int length)
{
    BuildMaxHeap(Array, length);
    for(int i=length;i>0;i--)
    {
        Array[0]=Array[i];
        Array[i]=Array[1];
        Array[1]=Array[0];
        //每次交换之后都要把当前的堆进行重新调整
        AdjustDown(Array, 1, i-1);//注意最后一个数的范围是i-1,因为i的位置已经放置上合适的值了,得在前面的那些数中进行寻找
    }
}
//堆中元素删除算法
void DeleteElement(int *Array,int length)
{
    Array[1]=Array[length];
    AdjustDown(Array,1, length);
    //交换之后实际上是在最后一个位置,那么就得让数组的实际长度发生变化来体现那个数被删除了
    //直接length--没用,传参
    //length--;
}
//堆中元素插入操作
void InsertElement(int *Array,int length,int key)
{
        Array[length+1]=key;//注意不要越界,元素的范围
    AdjustUp(Array, length+1);
    //length++;
      
}
//向上调整堆的算法
void AdjustUp(int *Array,int length)
{
    Array[0]=Array[length];//0结点为一个中间值结点
    int i=length/2;//从上往下是乘2,从下往上是/2操作
    while(i>0&&Array[i]<Array[0])
    {
        Array[length]=Array[i];
        length=i;
        i=length/2;
    }
    Array[length]=Array[0];
}
//明确当前操作的是哪个结点,真正对结果有影响的是哪个结点,不要被其他部分干扰

堆排序算法性能分析
性能分析
时间建堆时间为O(n),之后有n-1次向下调整操作,每次调整的时间复杂度为O(h)【树的高度】,在最好、最坏、平均情况下时间复杂度为O(nlog2n)
空间O(1)
稳定性不稳定
适用性利用了完全二叉树的性质,用顺序表存储
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值