《啊哈!算法》第1章

章节构成:

阿哈算法结构1

桶排序(伪)

例:

题目:

将一堆数排序,给了范围0~10,设一个数组a[11],下标表示数,值表示该数出现几次,初始情况如图所示:
桶排序图示
每个数出现一次,该数组值就 +1。

代码:

#include<iostream>
using namespace std;
int main()
{
    int a[11],i,j,t;
    for(i=0;i<=10;i++){
        a[i]=0;//初始化为0
    }
    for(i=0;i<5;i++){//循环读入5个数
        cin>>t;
        a[t]++;
    }

    for(i=0;i<=10;i++){
        for(j=1;j<=a[i];j++){
            cout<<i<<" "; //出现几次,打印机次!!
        }
    }

    return 0;
}

这种排序方法是一种简化的桶排序,真正的桶排序要比这个复杂。

注意

  1. 理解起来就是,把取值范围内的每个数看成一个"桶",输入的数放在与它对应的"桶"里,在按顺序遍历这些"桶",一次将桶清理干净

  2. 时间复杂度:O(M+N),但十分浪费空间!

  3. 当前的简化版不是一个真正意义上的排序算法,它仅仅排序了分数,但是这分数是谁的最后全乱了!!

冒泡排序

基本思想

每次比较两个相邻元素,如果顺序错误,就交换。
冒泡排序图示

例:

题目:

12,35,99,18,76从大到小排?

排列过程:

  • 1235991876(1,2位比较)
  • 3512991876(2,3位比较)
  • 3599121876(3,4位比较)
  • 3599181276(4,5位比较)
  • 3599187612(第一趟比较完~)

直至第4趟,是比较第1个与第2个数,完成后就剩第1个数,也就只能放在第1位了~~

总结:

  • 有n个数进行排序,需进行n-1趟操作
  • 每一趟从第1位开始,进行相邻两数比较,比较完成后向后挪移为继续比较
  • 重复上1步骤,直至最后一个尚未归为的数
  • 已经归位的数无需再比较~~

代码实现:

#include<iostream>
using namespace std;
int main()
{
    int a[100],i,j,t,n;
    cin>>n;
    for(i=0;i<n;i++){
        cin>>a[i];
    }

    //核心部分:
    for(i=0;i<n-1;i++){//n个数排序,进行n-1趟!!
        for(j=0;j<n-i-1;j++){//从第一位数开始,直到最后一个尚未归位的数
            if(a[j] < a[j+1]){//从大到小~~
                t=a[j];a[j]=a[j+1];a[j+1]=t;
            }
        }
    }
    for(i=0;i<n;i++){
        printf("%d ",a[i]);
    }

    return 0;
}

注意

  • 核心:双重嵌套循环~
  • 时间复杂度:O(N2),复杂度非常高

快速排序

优点

快速排序解决了桶排序浪费空间和冒泡排序浪费时间的问题,

原理

  • 在需要排序的数中找一个数为基准数(一般以第一个数位基准数),目标是通过移动数列中的其他数,将这个基准数,置于序列中间的某个位置,使左边的数比它小,右边的数比他大(如果是从小到大排),不断更新基准数,多轮重复,直到所有的数都归位为止。
  • 他是基于"二分"的思想,每次交换都是跳跃式的,不会像冒泡一样只在相邻的数之间交换,交换的距离大了,交换的次数小了,速度就高了。
  • 注意他的最差时间复杂度与冒泡一样为O(N2),而平均时间复杂度为O(NlogN)。

例:

题目:

将 6,1,2,7,9,3,4,5,10,8 从小到大排列?

方法:

  1. 设i,j为哨兵,分别从两端进行探测,先从右往左(j–)找一个小于6的数,再从左往右(i++)找一个大于6的数,然后交换数值。每次都是j先出发,直到i与j相遇,将基准数与相遇位置的数进行对调。这时"第一轮探测"结束。
  2. 当前以6位分界点分成了两个序列,左边为:3,1,2,5,4,右边为:9,7,10,8。这时按照之前的方法先处理左边的,直到左边全排好了,也就是成了1,2,3,4,5,再去管右边。
  3. 再次注意:每次都是"j哨兵"先动,原因是:最后和基准数交换的是比它小的,而j的职责就是找比基准数小的数,最后由i去碰j,或者是j最后碰到i了,此时的i还停留在上一次运行,所以他脚底下的数是经过上一次交换过来的比基准数小的数。

过程图解:

快速排序过程图解

代码:

#include<iostream>
using namespace std;

int a[101],n;

void quicksort(int left,int right){
    int i,j,t,temp;
    if(left > right){//递归结束条件
        return ;
    }
    temp=a[left];//temp中存的是基准数
    i=left;
    j=right;
    while(i != j){
        //顺序很重要!!先从右往左!!
        while(a[j]>=temp && i<j){
            j--;
        }
        //再从左往右
        while(a[i]<=temp && i<j){
            i++;
        }
        //交换两个数位置
        if(i<j){//确定当i,j没有碰面!
            t=a[i];a[i]=a[j];a[j]=t;
        }
    }
    //最终将基准数归位!!
    a[left]=a[i];
    a[i]=temp;

    quicksort(left,j-1);//继续处理左边的,递归
    quicksort(i+1,right);//继续处理右边的,递归
}

int main()
{
    int i,j,t;

    cin>>n;
    for(i=1;i<=n;i++){
        cin>>a[i];
    }

    quicksort(1,n);//快速排序调用!

    for(i=1;i<=n;i++){
        printf("%d ",a[i]);
    }

    return 0;
}

/* 测试数据:
10
6 1 2 7 9 3 4 5 10 8
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值