第二章 算法基础

2.1 插入排序(Insertion-Sort):

时间复杂度:O(n²)
对于少量元素的排序,是一个有效的算法。

为什么叫插入排序呢?
可以类比扑克牌整牌
这里写图片描述
将未排序的数字通过遍历插入到已排好序的数字中的对应位置

如何实现呢
这里写图片描述

#include<cstdio>
#include<iostream>
using namespace std;
int main()
{
    int n,num[10000];
    printf("Please input n:\n");
    scanf("%d",&n);//输入排序的长度
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&num[i]);//赋值
    }
    int key;
    for(int i=2;i<=n;i++)
    {
        key=num[i];//取出要插入的这个数字的值
        int j=i-1;//1~i-1为已经排好的数字
        while(j>0&&num[j]>key)//从后往前将num[i]~num[j+1]向后平移一个单位,
        {                     //num[j]为小于Key(当前所排的数)的最大的数
            num[j+1]=num[j];
            j--;
        }
        num[j+1]=key;//插入
    }
    for(int i=1;i<=n;i++)
    {
        printf("%d ",num[i]);//输出
    }
    printf("\n");
    return 0;
}

循环不变式:
以插排举例后引入了循环不变式的概念

第二章原文是这样说的:
每次循环从数组A中取出第j个元素插入有序区A[1 .. j-1],然后递增j。这样A[1 .. j-1]的有序性始终得到保持,这就是所谓的“循环不变 (loop invariant)”了

百度了下定义是这样的:
循环不变式:反映循环体中所有循环变量的变化规律并在循环体执行前后都为真的谓词称为该循环体的循环不变式 。
循环变量:在循环体中,其值随着循环体的执行不断发生变化的变量称为循环变量 。

恕本人愚钝,看不太懂
只能理解为循环体的遍历定义域,欢迎指正==

不过它的性质还是很好理解和应用的
1 , 初始化:循环的第一次迭代之前,它为真
2,保持 :如果循环的某次迭代之前它为真,那么下次迭代之前它仍为真
3,终止 :在循环终止时,不变式为我们提供一个有用的性质,该性质有助于证明算法是正确的

伪代码:
为使用最清晰、最简洁的表示方式来说明给定的算法,于是有了伪代码
伪代码为更简洁的表达算法本质,常常忽略数据抽象、模块性和错误处理的问题 //为了方便debug,模块性很重要

约定(部分)
缩进表示块结构
迭代增加用关键词to 迭代减少用关键词downto
若无显式说明,不使用全局变量

传参
在第12页第四段有这么几句话:

被调用过程接收其参数自身的副本。
如果它对某个参数赋值,调用过程看不到这种改变。
例如,如果x是某个被调用过程的参数,在被调用过程中的赋值x=y对调用过程是不可见的。

这几句话很晦涩,其实就讲了一个很简单的问题:
当作为一个实参传参进入一个函数时,是复制了那个参数的一个副本传了进去,函数内对该参数的各种操作对原参数是没有影响的,不会使其发生改变,而如果是将地址传进去,那么函数对其的操作会使其发生改变

2.2 分析算法:
分析算法的结果意味着预测算法需要的资源(时间、空间)
算法分析不关注算法实际的运行时间,而是关注运行时间的增长

分析算法,包括最佳情况,平均情况和最坏情况
但我们一般只讨论最坏情况,原因如下:
1,一个算法最坏情况运行时间给出了任何输入的运行时间的一个上界
2,对某些算法,最坏情况经常出现
3,平均情况往往和最坏情况大致一样差

只说下第三条==
以插排为例,平均情况为n/2,最坏情况为n,数量级均为n,当我们讨论算法是,是关注算法运行时间的增长,即数量级,所以他们是相等的

下面试分析一个简单的程序(求i³之和)

    int 
    Sum(int N)
    {
        int i,PartialSum;

/*1*/   PartialSum=0;
/*2*/   for(i=1;i<=N;i++)
/*3*/       PartialSum += i*i*i;
/*4*/   return PartialSum;
    }

声明不计时间。
第1行和第4行各占一个时间单元。
第3行每执行一次占用四个时间单元(两次乘法,一次加法,一次赋值)
第2行在初始化i,测试i<=N和对i的自增运算中隐含着开销。这些所有开销为初始化一个时间单元,所有的测试N+1个时间单元,以及所有的自增运算N个时间单元,共2N+2。
忽略调用函数与返回值的开销,总共为6N+4。所以说该函数为O(N)。

2.3设计算法

分治法的思想:
*将原问题分解为几个规模较小但类似于原问题的子问题,递归地求解这些子问题,然后再合并这些子问题的解来建立原问题的解*

//分治的思想博大精深,非常重要,第四章还会再详细讨论
//我会告诉你我就是因为分治,递归没学好才下决心啃算导的嘛(:зゝ∠)

分治模式在每层递归时都有三个步骤:
分解原问题为若干子问题,这些子问题是原问题规模较小的实例
解决这些子问题,递归地求解各子问题。若子问题的规模足够小,则直接求解
合并这些子问题的解成原问题的解

下面以归并排序为例解释
分解:分解待排序的n个元素的序列成各具n/2个元素的两个子序列
解决:使用归并排序递归地排序两个子序列
合并:合并两个已排序的子序列以产生已排序的答案

归并排序:

主要思想
这里写图片描述

这里写图片描述

#include<cstdio>
#include<cstring>
#include<iostream>
int maxnum=9999999;
using namespace std;
void Merge(int *A,int p,int q,int r)
{
    int n1=q-p+1;//长度
    int n2=r-q;
    int L[n1+1];
    int R[n2+1];
    for(int i=1;i<=n1;i++)//赋值
    {
        L[i]=A[p+i-1];
    }
    for(int i=1;i<=n2;i++)//赋值
    {
        R[i]=A[q+i];
    }
    L[n1+1]=maxnum;//哨兵标记
    R[n2+1]=maxnum;
    int i=1,j=1;
    for(int k=p;k<=r;k++)//并
    {
        if(L[i]<=R[j])
        {
            A[k]=L[i];
            i++;
        }
        else 
        {
            A[k]=R[j];
            j++;
        }
    }
    return;
}
void MergeSort(int *A,int p,int r)
{
    if(p<r)
    {
        int q=(p+r)/2;
        MergeSort(A,p,q);//左半部分
        MergeSort(A,q+1,r);//右半部分
        Merge(A,p,q,r);
    }
    return ;
}
int main()
{
    int n;
    scanf("%d",&n);
    int A[10000];
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&A[i]);
    }
    MergeSort(A,1,n);
    for(int i=1;i<=n;i++)
    {
        printf("%d ",A[i]);
    }
    int x;
    scanf("%d",&x);
    return 0;
}

下面看一下递归树
这里写图片描述
这个树的深度是logn,所以层数是logn+1
而每层的时间是cn
所以总时间就是cn*(logn+1)=cnlogn+cn
所以时间复杂度为nlogn

求最大子列和nlogn实现

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int max3(int a,int b,int c)
{
    if(a>b&&a>c)
        return a;
    else if(b>a&&b>c)
        return b;
    else if(c>a&&c>b)
        return c;
}
int maxsubsum(int A[],int left,int right)
{
    if(left==right)
        if(A[left]>0)
            return A[left];
        else 
            return 0;

    int center=(left+right)/2;
    int maxleftsum=maxsubsum(A,left,center);
    int maxrightsum=maxsubsum(A,center+1,right);

    int maxleftbordersum=0;
    int leftbordersum=0;
    for(int i=center;i>=left;i--)
    {
        leftbordersum+=A[i];
        if(leftbordersum>maxleftbordersum)
            maxleftbordersum=leftbordersum;
    }
    int maxrightbordersum=0;
    int rightbordersum=0;
    for(int i=center+1;i<=right;i++)
    {
        rightbordersum+=A[i];
        if(leftbordersum>maxrightbordersum)
            maxrightbordersum=rightbordersum;
    }

    return max3(maxleftsum,maxrightsum,maxleftbordersum+maxrightbordersum);
}
int main()
{
    int n;
    scanf("%d",&n);
    int a[n];
    for(int i=0;i<n;i++)
    {
        scanf("%d",&a[i]);
    }
    int ans=maxsubsum(a,0,n-1);
    printf("%d\n",ans);
    return 0;
}

以上所有 如有错漏 欢迎打脸==

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值