NewCoder算法基础 Class 1

NewCoder算法基础 Class 1

  • 时间复杂度

        [1]只要高阶项,不要低阶项

        [2]不要高阶项系数

例题1: 一个有序数组A,另一个无序数组B,请打印B中的所有不在A中的数,A数组长度为N,B数组长度为M。

[ 1 ] 方法一:对于数组B中的每一个数,都在A中通过遍历的方式找一下

int Search(int arrA[], int arrB[], int ALength, int BLength){
    int *help=(int *)malloc(sizeof(int)*BLength);
    int k=0;
    for(int i=0; i<BLength; i++){
        for(int j=0; j<ALength; j++){
            if(arrB[i]==arrA[j]){
                break;
            }
            if(j==ALength){
                help[k++]=arrB[i];
            }
        }
    }
}

[ 2 ] 方法二:对于数组B中的每一个数,都在A中通过二分的方式找一下

int BinarySearch(int arr[], int L, int R, int key){
    int L=0, R=arr.size()-1;
    while(L<=R){
        mid=(L+R)/2;
        if(arr[mid]==key){
            return mid;
        }
        else if(arr[mid]>key){
            R=mid-1;
        }
        else{
            L=mid+1;
        }
    }
    return -1;
}

Note:

mid=(L+R)/2   这种写法中L+R容易造成越界,所以可以写成L+(R-L)>>1

[ 3 ] 方法三:先把数组B排序,然后用类似外排的方式打印所有在A中出现的数

      i
  A 1 2 4 6 7
      j
  B 3 6 9

过程:
1) 1<3   i++
2)2<3   i++
3)4>3   print arrB[j]   j++
4)4<6   i++
5)6=6   j++
6)6<9   i++
7)7<9   i++   print arrB[j]
(i, j 任意一方越界循环停止)

int External(int arrA[], int arrB[]){
    int ALength=arrA.size();
    int BLength=arrB.size();
    int *help=(int *)malloc(sizeof(int)*BLength);
    int i=0,j=0,k=0;
    while(i<ALength && j<BLength){
        if(arrB[j]>arrrA[i]){
            i++;
        }
        else{
            if(arrB[j]<arrA[i]){
                help[k++]=arrB[j++];
            }
            else{
                j++;
            }
        }
    }
}
  • 冒泡排序

3 2 0 4 7
3 2 0 4 7--->2 3 0 4 7--->2 0 3 4 7--->2 0 3 4 7

int BubbleSort(int arr[], int len){
    for(int i=0; i<len; i++){
        for(int j=0; j<len-1-i; j++){
            if(arr[j]>arr[j+1]){
                swap(a[j], a[j+1]);
            }
        }
    }
}
int BubbleSort(int arr[], int len){
    for(int end=len-1; end>=0; end--){
        for(int i=0; i<end ;i++){
            if(arr[j]>arr[j+1]){
                swap(a[j], a[j+1]);
            }
        }
    }
}

T(n)=n+(n-1)+(n-2)+......+1=(1+n)*n/2
           =O(n^2)

  • 选择排序

下标: 0~~~~~~~~~~~~~~~~~~~~~~~n-1
           min
                  1~~~~~~~~~~~~~~~~~~~~~n-1
               min'
                      2~~~~~~~~~~~~~~~~~~~n-1
                    min''

int SelectionSort(int arr[], int len){
    for(int i=0; i<len; i++){
        int min=arr[i];
        for(int j=i+1; j<len; j++){
            if(arr[j]<min){
                swap(arr[j], min);
            }
        }
    }
}
int SelectionSort(int arr[], int len){
    for(int i=0; i<len; i++){
        int min=INT_MAX;//INT_MAX为正无穷
        for(int j=i; j<len; j++){
            if(arr[j]<min){
                swap(arr[j], min);
            }
        }
    }
}

    T(n)=n+(n-1)+(n-2)+......+1=(1+n)*n/2
           =O(n^2)

  • 插入排序

5 3 4 0 6
5 | 3 4 0 6
                          swap(5, 3)
5 3 | 4 0 6   ---> 5 3 | 4 0 6 ---> 3 5 | 4 0 6
                          swap(5, 4)
3 5 4 | 0 6   ---> 3 5 4 | 0 6   ---> 3 4 5 | 0 6 ---> 3 4 5 | 0 6
                          swap(5, 0)         swap(4, 0)         swap(3, 0)
3 4 5 0 | 6   ---> 3 4 5 0 | 6   ---> 3 4 0 5 | 6 ---> 3 0 4 5 | 6 ---> 0 3 4 5 | 6
0 3 4 5 6 ---> 0 3 4 5 6 --->0 3 4 5 6 ---> 0 3 4 5 6 ---> 0 3 4 5 6 ---> 0 3 4 5 6

int InsertSort(int arr[], int len){
    for(int i=1; i<len; i++){
        for(int j=i-1; j>=0; j--){
            if(arr[j]>a[j+1]){
                swap(a[j], a[j+1]);//此处也可使用三目运算符
            }
        }
    }
}

    最好情况:O(n)
    最坏情况:O(n^2)

  • 对数器
  1. 无OJ线上测试,可以验证自己写的是否正确
  2. 小样本正确,大样本WA 希望依然使用小样本检测问题
  3. 贪心策略 用对数器无需证明

对数器构造

  1. 产生堆积样本的发生器
  2. 寻找一个完全准确的方法(容易实现,无需考虑时间复杂度)
  3. 数据完全拷贝两份,分别传递到测试函数和完全正确的函数中,进行结果比较

对数器的概念和使用

  1. 有一个你想要测的方法a
  2. 实现一个绝对正确但是复杂度不好的方法b
  3. 实现一个随机样本产生器
  4. 实现比对的方法
  5. 把方法a和方法b比对很多次来验证方法a是否正确。
  6. 如果有一个样本使得比对出错,打印样本分析是哪个方法出错
  7. 当样本数量很多时比对测试依然正确,可以确定方法a已经正确。
  • 递归行为的实质

递归思想(例如数组中找最大值)
                    L        |        R
               max_L        max_R
             Max(max_L, max_R)

int getMax(int arr[], int L, int R){
    if(L==R){
        return arr[L];
    }
    int mid=L+(R-L)>>1;
    int maxLeft=getMax(arr, L, mid);
    int maxRight=getMax(arr, mid+1, R);
    return max(maxLeft, maxRight);
}

      max(arr, 0, 3)
                mid=1
                max(arr, 0, 1)
                      mid=0
                      max(arr, 0, 0)
                            return


      max(arr, 0, 3)
                mid=1
                max(arr, 0, 1)
                      mid=0    maxLeft=4

Note:

        在递归的过程中,系统会将代码行、形参、局部变量等信息压入系统栈中,当遇到return后将栈顶信息弹出还原现场并将值带回,然后执行接下来的语句
        任何递归行为都可以改成非递归(无需系统压栈,自己手动压栈)---> 从递归改为迭代

  • 计算递归行为的时间复杂度Master公式

T(n)=aT(n/b)+O(n^d)

        比如二分查找    T(n)=2T(n/2)+O(1)

Master公式

log ⁡ b a &gt; d − − − &gt; O ( n l o g b a ) \log_ba&gt;d \kern{10px} ---&gt; \kern{10px} O(n^{log_ba}) logba>d>O(nlogba)

log ⁡ b a = d − &gt; O ( n d ∗ l o g b a ) \log_ba=d \kern{10px} -&gt; \kern{10px} O(n^d*{log_ba}) logba=d>O(ndlogba)

log ⁡ b a &lt; d &MediumSpace;&MediumSpace;&MediumSpace;&MediumSpace;&MediumSpace;&MediumSpace;&MediumSpace;&MediumSpace;&MediumSpace; − − − &gt; O ( n d ) \log_ba&lt;d \:\:\:\:\:\:\:\:\:---&gt; \kern{10px} O(n^d) logba<d>O(nd)

  • 归并排序

Merge过程
    5 3 6 | 2 0 1
    3 5 6 | 0 1 2
     i        |  j
    将小的数值填入到辅助数组help中,对应区域指针向后移一位

void MergeSort(int arr[], int L, int R){
    if(L==R){
        return ;
    }
    int mid=L+(R-L)>>1;
    MergeSort(arr, L, mid);
    MergeSort(arr, mid+1, R);
    merge(arr, L, mid, R);
}

void merge(int arr[], int L, int mid, int R){
    int i=L, j=mid+1, k=0;
    int *help=(int *)malloc(sizeof(int)*(R-L+1));
    //两个有且只有一个越界
    while(i<=mid && j<=R){
        help[k++]=arr[i]<arr[j]?arr[i++]:arr[j++];
    }
    while(i<=mid){
        help[k++]=arr[i++];
    }
    while(j<=R){
        help[k++]=arr[j++];
    }
}

例题2:小和问题
        在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组的小和。

例子: [1,3,4,2,5]
    1左边比1小的数,没有;
    3左边比3小的数,1;
    4左边比4小的数,1、3;
    2左边比2小的数,1;
    5左边比5小的数,1、3、4、2;
    所以小和为1+1+3+1+1+3+4+2=16

[ 1 ] 方法一:暴力 遍历所有比current小的数进行累和

int function(int arr[], int len){
    int sum=0;
    for(int i=1; i<len; i++){
        for(int j=0; j<i; j++){
            if(arr[j]<arr[i]){
                sum+=arr[j];
            }
        }
    }
}

[ 2 ] 方法二:在归并过程中计算小和

                                     1 3 4 | 2 5
                                         /          \
                                  1 3 | 4     2 | 5
                                   /
                               1 | 3

       1)1 | 3 归并成 1 3 产生小和1
       2)2 | 5 归并成 2 5 产生小和2
       3)1 3 | 4 归并成 1 3 4 产生小和1 3
           i        j    arr[i]

Note:

merge过程加速了累和(原因:有效地利用循环过程)
    注:位运算运算速度快于基本运算

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值