简单排序

目录

一、插入排序

(1)定义

(2)算法步骤

(3)算法分析

(4)代码

二、冒泡排序

(1)定义

(2)算法步骤

 (3)算法分析

 (4)代码

三、斐波那契logn解法

(1)推导

①矩阵公式

②快速幂

③矩阵快速幂


一、插入排序

(1)定义

直接插入排序是最简单的排序方法,每次将一个待排序的记录,插入到已经排好序的数据序列中,得到一个新的长度增1的有序表。

 插入到有序序列时,可以用第0个空间暂存这个数据(作为哨兵,免去数组越界的判断),也可以用一个temp变量保存

(2)算法步骤

①待排序记录存储在数组r[1...n]中,可以把第1个记录r[1]看作一个有序序列

②依次将r[i](i=2,……,n)插入到已经排好序的序列r[1...i-1]中,并保持有序性

例如,利用直接插入排序算法对序列{12,2,16,30,28,10,16*,20,6,18}进行非递减排序。

初始状态,把r[1]看作一个有序序列

将2插入到有序序列中

将16插入到有序序列中

 将30插入到有序序列中

 将28插入到有序序列中

将10插入到有序序列中

 

将16插入到有序序列中

将20插入到有序序列中

将 6插入到有序序列中

最后再把18插入到有序序列中

(3)算法分析

①时间复杂度 

  • 最好情况下,待排序序列本身是正序的(如待排序序列是非递减的,题目要求是非递减的),每个记录只需和前一个记录比较一次,不小于前一个记录,则什么都不用做,总的比较次数为:n-1。最好情况下,直接插入排序的时间复杂度为O(n)
  • 最坏情况下,待排序序列本身是逆序的(如待排序序列是非递增的,题目要求是非递减的),每个记录都需要比较i次(和前i-1记录比较+和哨兵r[0]比较),总的比较次数为:\sum_{i=2}^{n}i=\frac{(n+2)(n-1)}{2}。最坏情况下,直接插入排序的时间复杂度为O(n^2) 
  • 平均情况下,若待排序序列出现各种情况的概率均等,则可取最好情况和最坏情况的平均值。平均情况下,直接插入排序的时间复杂度也为O(n^2)

②空间复杂度 

直接插入排序使用了一个辅助空间r[0],空间复杂度为O(1)

③稳定性

直接插入排序时,已经有序的序列中的记录比待排序记录大时才向后移动,和待排序记录相等时不向后移动,因此两个相等的记录在排序前后的位置顺序不是变的。

在有序序列中查找待排序记录的插入位置时,折半查找的方法比顺序查找效率更高,但元素移动效率是不变的,时间复杂度还是O(n^2)

(4)代码

#define INF 0x3f3f3f3f
#define ll long long
#include<iostream>
#include <stdio.h>
using namespace std;

const int Maxsize=100+5;

void StraightInsertSort(int r[],int n){
    int j;
    for(int i=2;i<=n;i++){
        if(r[i-1]>r[i]){
            r[0]=r[i];
            r[i]=r[i-1];
            for(j=i-2;r[j]>r[0];j--)
                r[j+1]=r[j];
            r[j+1]=r[0];
        }
    }
}

int main(){
    int i,n,r[Maxsize+1];
    printf("请输入数列中的元素个数n\n");
    scanf("%d",&n);
    printf("请依次输入数列中的元素:\n");
    for(int i=1;i<=n;i++){
        scanf("%d",&r[i]);
    }
    StraightInsertSort(r,n);
    printf("直接插入排序结果\n");
    for(int i=1;i<=n;i++)
        printf("%d ",r[i]);
    return 0;
}

折半查找插入排序

#define INF 0x3f3f3f3f
#define ll long long
#include<iostream>
#include <stdio.h>
using namespace std;

const int Maxsize=100+5;

void InsertSort(int r[],int n){
    int j,mid;
    for(int i=2;i<=n;i++){
        if(r[i-1]>r[i]){
            r[0]=r[i];
            int low=1,high=i-1;
            while(low<=high){
                mid=(low+high)/2;
                if(r[mid]>r[0]){
                    high=mid-1;
                }
                else{
                    low=mid+1;
                }
            }
            // 将大于 temp 的元素向后移动
            for (j = i; j > high; j--) {
                r[j] = r[j - 1];
            }
            r[high + 1] = r[0]; // 插入 temp 到正确的位置
        }
    }
}

int main(){
    int i,n,r[Maxsize+1];
    printf("请输入数列中的元素个数n\n");
    scanf("%d",&n);
    printf("请依次输入数列中的元素:\n");
    for(int i=1;i<=n;i++){
        scanf("%d",&r[i]);
    }
    InsertSort(r,n);
    printf("直接插入排序结果\n");
    for(int i=1;i<=n;i++)
        printf("%d ",r[i]);
    return 0;
}

二、冒泡排序

(1)定义

冒泡排序是一种最简单的交换排序算法,通过两两比较关键字,如果逆序就交换,使关键字大的记录像泡泡一样冒出来放在尾部。重复执行若干次冒泡排序,最终得到有序序列。

(2)算法步骤

①设待排序的记录存储在数组r[1...n]中,首先第一个记录和第二个记录关键字比较,若逆序则交换;然后第二个记录和第三个记录关键字比较,……,以此类推,直到第n-1个记录和第n个记录关键字比较完毕为止。第一趟排序结束,关键字最大的记录在最后一个位置。

②第二趟排序,对前n-1个元素进行冒泡排序,关键字次大的记录在n-1位置。

③重复上述过程,直到某一趟排序中没有进行交换记录为止,说明序列已经有序。

 (3)算法分析

①时间复杂度

  • 最好情况下,待排序序列本身是正序的(如待排序序列是非递减的,题目要求也是非递减排序),只需要一趟排序,n-1次比较,无交换记录。最好情况下,冒泡排序时间复杂度为O(n)。
  • 最坏情况下,待排序序列本身是逆序的(如待排序序列是非递增的,题目要求是非递减排序),需要n-1趟排序,每趟排序i-1次比较,总的比较次数为:\sum_{i=n}^{2}(i-1)=\frac{n(n-1)}{2},还有交换时间,最坏情况下,冒泡排序的时间复杂度为O(n^2)
  • 平均情况下,若待排序序列出现各种情况的概率均等,则可取最好情况和最坏情况的平均值。平均情况下,冒泡排序的时间复杂度也为O(n^2)。

②空间复杂度

冒泡排序使用了一些辅助空间i,j,temp,flag,空间复杂度为O(1)。

③稳定性

冒泡排序是稳定的排序方法

冒泡排序比插入排序差,因为有大量的交换

 (4)代码

#define INF 0x3f3f3f3f
#define ll long long
#include<iostream>
#include <stdio.h>
using namespace std;

const int Maxsize=100+5;

void BubbleSort(int r[],int n){
    int i=n-1,temp;
    bool flag=true;
    while(i>=2 && flag){
        flag=false;
        for(int j=0;j<=i-1;j++){//进行一趟排序
            if(r[j]>r[j+1]){//交换两个记录
                flag=true;
                temp=r[j];
                r[j]=r[j+1];
                r[j+1]=temp;
            }
        }
        for(int k=0;k<=i;k++)//测试每趟排序结果
            printf("%d ",r[k]);
        printf("\n");
        i--;
    }
}

int main(){
    int i,n,r[Maxsize+1];
    printf("请输入数列中的元素个数n\n");
    scanf("%d",&n);
    printf("请依次输入数列中的元素:\n");
    for(int i=0;i<n;i++){
        scanf("%d",&r[i]);
    }
    BubbleSort(r,n);
    printf("冒泡排序结果\n");
    for(int i=0;i<n;i++)
        printf("%d ",r[i]);
    return 0;
}

三、斐波那契logn解法

 斐波那契数列以如下被以递推的方法定义:F(1)=1,F(2)=1,F(n)=F(n-1)+F(n-2)(n≥3)

 斐波那契数列有多种解法:

  • 通项公式 O(1)
  • 递归 O(1.618^n)
  • 动态规划 O(n)
  • 矩阵快速幂 O(logn)

本节就是讲用矩阵快速来求解斐波那契数列

(1)推导

①矩阵公式

F(n+1)=1*F(n)+1*F(n-1)

F(n)=1*F(n)+0*F(n-1)

F(n)=1*F(n-1)+1*F(n-2)

F(n-1)=1*F(n-1)+0*F(n-2)

通过这4个公式得到以下公式 

\begin{bmatrix} F(n+1) & F(n) \\ F(n) & F(n-1) \end{bmatrix}=\begin{bmatrix} 1 & 1\\ 1& 0 \end{bmatrix}\begin{bmatrix} F(n)& F(n-1)\\ F(n-1)& F(n-2) \end{bmatrix}

 n~2要乘n-2个\begin{bmatrix} 1 & 1\\ 1& 0 \end{bmatrix}

 \begin{bmatrix} F(n+1) & F(n) \\ F(n) & F(n-1) \end{bmatrix}=\begin{bmatrix} 1 & 1\\ 1& 0 \end{bmatrix}\wedge (n-1) \begin{bmatrix} F(2)& F(1)\\ F(1)& F(0) \end{bmatrix}

 \begin{bmatrix} F(n+1) & F(n) \\ F(n) & F(n-1) \end{bmatrix}=\begin{bmatrix} 1 & 1\\ 1& 0 \end{bmatrix}\wedge (n-1) \begin{bmatrix} 1& 1\\ 1& 0 \end{bmatrix}

\begin{bmatrix} F(n+1) & F(n) \\ F(n) & F(n-1) \end{bmatrix}=\begin{bmatrix} 1 & 1\\ 1& 0 \end{bmatrix}\wedge n

所以,只要求出 \begin{bmatrix} 1 & 1\\ 1& 0 \end{bmatrix}\wedge n,结果的第一行第二列和第二行第一列都是F(n)的解

②快速幂

快速幂公式如下:

a^{b}的值可能会很大,会溢出,所以a先求余再求b次方(b次方后可能会很大,所以还需求余)。求余是因为有些题不需要这么大的数,只需要后几位就行

可以按照下面公式处理快速求a^{b} 

 

 b是奇数的话,b/2会向下取整所以还需乘a

例如求2^n,初始时,ans=1,a=2

Ⅰ如果n=4,要求2^{4}则先求

a=a*a=2*2,n=n/2=2【2^{4}=(2^{2})^{4/2}

a=a*a=(2*2)*(2*2),n=n/2=1【2^{4}=(k^{2})^{2/2},k=2^{2}

ans=ans*a=1* (2*2)*(2*2),n=n/2=0

Ⅱ如果n=5,要求2^{5}则先求

ans=ans*a=2,a=a*a=2*2,n=n/2=2【2^{5}=2*(2^{2})^{5/2}

a=a*a=(2*2)*(2*2),n=n/2=1【2^{5}=2*(k^{2})^{2/2},k=2^{2}

ans=ans*a=2*(2*2)*(2*2),n=n/2=0

如果不用快速幂,a*a*a*a需要计算3次,使用快速幂只需要计算log4=2次,先计算2*2,再计算(2*2)*(2*2) ,如上所示求到n/2^{x}=1,即x=logn

int Pow(int n,int p){
    int ans=1;
    while(n){
        if(n&1)//奇数,最后n=1时也是奇数
            ans=ans*a%p;
        a=a*a%p;
        n=n/2;
    }
}

③矩阵快速幂

 矩阵乘法运算是第一个矩阵的第i行元素分别乘以第二个矩阵的第j列元素,然后求和得到结果矩阵的c_{ij}元素,如图所示

\begin{bmatrix} a_{00} &a_{01} \\ a_{10}& a_{11} \end{bmatrix} \begin{bmatrix} b_{00} &b_{01} \\ b_{10}& b_{11} \end{bmatrix} = \begin{bmatrix} a_{00}b_{00} +a_{01}b_{10} &a_{00}b_{01} +a_{01}b_{11} \\ a_{10}b_{00} +a_{11}b_{10}& a_{10}b_{01} +a_{11}b_{11} \end{bmatrix}

单位矩阵(类似单位1)

\begin{bmatrix} 1 & 0\\ 0 & 1 \end{bmatrix}

矩阵快速幂和普通的快速幂类似,只是执行的是矩阵乘法而已。

求解矩阵c的n次幂,首先初始化c矩阵,初始化ans为单位矩阵,然后判断n如果为奇数,则res与c执行矩阵乘法,c与c执行矩阵乘法,n=n/2,循环,直到n=0时停止。

#define INF 0x3f3f3f3f
#define ll long long
#include<iostream>
#include <stdio.h>
#include <cstring>
using namespace std;

const int MOD=10000;
struct mat{
    ll a[2][2];
};

mat mat_mul(mat x,mat y){
    mat res;
    memset(res.a,0,sizeof(res.a));
    for(int i=0;i<2;i++){
        for(int j=0;j<2;j++){
            for(int k=0;k<2;k++){
                res.a[i][j]=(res.a[i][j]+x.a[i][k]*y.a[k][j])%MOD;
            }
        }
    }
    return res;
}

void mat_pow(int n){
    mat c,ans;
    c.a[0][0]=1;c.a[0][1]=1;c.a[1][0]=1;c.a[1][1]=0;
    memset(ans.a,0,sizeof(ans.a));
    for(int i=0;i<2;i++)
        ans.a[i][i]=1;//单位矩阵
    while(n){
        if(n&1)
            ans=mat_mul(ans,c);
        c=mat_mul(c,c);
        n=n>>1;
    }
    printf("%I64d\n",ans.a[0][1]);
}
int main(){
    int n;
    printf("输入指数\n");
    scanf("%d",&n);
    printf("F(%d)斐波那契数列值为:\n",n);
    mat_pow(n);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值