nowcoder basic algorithm chapter 1

三大简单的排序算法:

1:冒泡排序

def bubble_sort(array):
    """冒泡排序

    基本思想:和名称很像,每次比较两个数,较大的数字上浮,每进行一遍,最后的数总是最大的
    接着遍历其它的数字
    实现方式:和基本思想一样

    时间复杂度:等差数列求和, O(N^2)
    空间复杂度:交换两个数组的时候需要额外的空间,O(1)
    稳定性: 是稳定的算法,因为当两个数相等的时候,可以控制不让它进行交换
    """
    array_lenght = len(array)

    for i in range(0,array_lenght):
        for j in range(1,array_lenght-i):
            if array[j] < array[j-1]:
                array[j], array[j-1] = array[j-1], array[j]
    return array

2:选择排序

def selection_sort(array):
    """选择排序

    基本思想:遍历n次,每次从未排序的数字当中选择最大的,放到末尾,
    实现方式:每次遍历的时候把最小的数放在开头,这样开头部分有序,然后遍历未排序的部分

    时间复杂度:等差数列,O(N^2)
    空间复杂度: O(1)
    稳定性:稳定的, 在寻找最小数的时候,如果和当前数相等,那么可以跳过
    """
    array_length = len(array)
    for i in range(0, array_length):
        min = i
        for j in range(i+1, array_length):
            if array[j] < array[min]:
                min = j
        array[i], array[min] = array[min], array[i]
    return array

3:插入排序

def insert_sort(array):
    """插入排序

    基本思想:从第一个元素开始,认为已经排好序了,然后取出下一个元素,和前面的元素比较
            ,如果比前面元素小,那么交换,直到没有比它小的元素。
    实现方式:从低到高不断遍历,第i位以前都被认为是排序好的,将第i+1位往前不断交换插入前面

    时间复杂度: 依赖数据状况 最好状况O(N), 最坏状况O(N^2)
    空间复杂度: O(1)
    稳定性:  稳定的, 当遇到和自己想同的元素的时候,不往前交换
    """
    array_length = len(array)
    for i in range(1, array_length):
        for j in range(i, 0, -1):
            if array[j] < array[j-1]:
                array[j], array[j-1] = array[j-1], array[j]
            else:
                break
    return array

三大排序的算法复杂度都是O(N^2)

递归算法:

  当使用递归算法的时候,在系统当中其实使用的是栈的形式来实现的。当遇到一个递归函数的时候,会把它压入栈中,包括函数执行的行数,还有临时变量等等。 

递归算法的复杂度
递归算法的时间复杂度:使用master方法来进行估算 a表示递归发生的次数,b表示子问题相对于原来问题的规模,$O(N^d)$表示剩余子程序的规模 $T(N) = a * T(\frac{N}{b}) + O(N^d)$

当$\log_ba > d$    时间复杂度为$O(N^{\log_ba })$ 

当$\log_ba = d$  的时候 时间复杂度$O(N^d * logN) $

当$\log_ba < d$  的时候 时间复杂度$O(N^d)$

比如下面一个Java代码,使用递归求最大值,我们可以分析它的压栈的情况,也可以分析它的复杂度。在下面问题当中,因为出现了两次递归,所以a=2,子问题规模是原来的一半,所以b=2,剩余问题的复杂度为O(1),d=0,所以这里$\log_2 2 >0 $,所以算法复杂度为O(N)

public class getMax {
    public static int getMaxValue(int[] arr, int L, int R){
        if(L == R){
            return arr[L];
        }
        int mid = (L+R)/2;
        int maxLeft = getMaxValue(arr, L, mid);
        int maxRight = getMaxValue(arr, mid+1, R);
        return Math.max(maxLeft, maxRight);

    }

    public static void main(String[] args){
        int[] arr = {4, 3, 2, 1};
        System.out.println(getMaxValue(arr,0,3));
    }
}

归并排序:

  使用分治法和递归的解法引出来递归问题,归并问题的思路是这样的:要求一个数组a的排序,可以将数组分成两部分,将这两部分排完序以后合并一下,合并的策略是将两个数组都从左又进行遍历,大的放在数组里面,然后指针加1.

def merge_sort(array):
    """归并排序

    使用分治法的思想来进行排序, 使用递归的方法来进行实现

    时间复杂度:使用master公式  a=2, b=2。除去递归,其它问题的复杂度,也就是merge的复杂度o(n),
              所以,整体的复杂度为O(n*logn)
    空间复杂度:归并排序每次递归需要用到一个辅助表,长度与待排序的表相等,
              虽然递归次数是O(log2n),但每次递归都会释放掉所占的辅助空间,
              所以下次递归的栈空间和辅助空间与这部分释放的空间就不相关了,因而空间复杂度还是O(n)
    稳定性: 稳定性算法。在merge的时候,如果两个值相等,可以控制让左边先进来,然后右边再进来。
    """
    if len(array) <= 1:
        return array
    mid = int(len(array)/2)
    left = merge_sort(array[:mid])
    right = merge_sort(array[mid:])
    return merge(left, right)

def merge(left, right):
    """合并算法

    刚开始并没与价差两个数组的长度,而是直接相减,直到有一个为为止

    时间复杂度:o(n)  需要遍历一边left和right
    空间复杂度为 o(n) 需要一个result数组来存储结果

    """
    result  = []
    l, r = 0, 0
    while(l < len(left) and r < len(right)):
        if left[l] < right[r]:
            result.append(left[l])
            l += 1
        else:
            result.append(right[r])
            r += 1

    result += (left[l:])
    result += (right[r:])
    return result

小和问题

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

暴力解法,将每个数都遍历一边,时间复杂度为$O(N^2)$

可以借用上面的一些思想,要计算一组数的小和,只需要计算左边的小和加上右边的小和再加上合并时候的小和,合并时候的小和求解的时候数组已经排好序了,计算小和时可以用一些策略加速。

Java版本的代码如下

public static int samllSumCount(int[] arr){
        if (arr == null || arr.length <2){
            return 0;
        }
        return mergeSort(arr, 0, arr.length-1);
    }

    public static int mergeSort(int[] arr, int l, int r){
        if (l == r){
            return 0;
        }
        int mid = l + ((r-l) >> 1);  //注意当a的值大于0的时候,>>1表示除以2,a的值小于0的时候, >>1 比除2小1
        return mergeSort(arr, l, mid) + mergeSort(arr, mid+1, r) + merge(arr, l, mid, r);
    }

    public static int merge(int[] arr, int l, int m, int r){
        /**
         * 其实就是在排序的过程当中插入了一些计算小数的代码
         * 在计算的过程当中,其实i-m 和 m-r之间是已经排序好的数字
         * 利用这一特点可以批处理完成小和的计算
         */
        int[] help = new int[r-l+1];
        int p0 = l;
        int p1 = m+1;
        int res = 0;
        int i = 0;

        while(p0 <=m && p1 <= r){
            if(arr[p0] < arr[p1]){
                res += arr[p0] * (r-p1+1);
                help[i++] = arr[p0++];
            } else{
                help[i++] = arr[p1++];
            }
        }
        while(p0 <= m){
            help[i++] = arr[p0++];
        }
        while(p1 <= r){
            help[i++] = arr[p1++];
        }
        for(i=0; i<help.length; i++){
            arr[l+i] = help[i];
        }
        return res;
    }

猜你喜欢

转载自www.cnblogs.com/jiaxin359/p/9239882.html
今日推荐