【笔记】牛客网算法

第一节

冒泡排序

public static void bubbleSort(int[] arr){
        if(arr == null || arr.length < 2)
            return;
        for(int end = arr.length-1; end > 0; end--){
            for(int i = 0; i < end; i++)
                if(arr[i] > arr[i+1])
                    swap(arr,i,i+1);
        }
    }

选择排序

public static void selectionSort(int[] arr){
        if(arr == null || arr.length < 2)
            return;
        for(int i = 0; i < arr.length-1; i++){
            int minIndex = i;
            for(int j = i+1; j < arr.length; j++)
                minIndex = arr[j] < arr[minIndex] ? j : minIndex;
            swap(arr, i, minIndex);
        }
    }

插入排序

public static void insertionSort(int[] arr){
        if(arr == null || arr.length < 2)
            return;
        for(int i = 1; i < arr.length; i++)
            for(int j = i - 1;j >= 0 && arr[j] > arr[j + 1]; j--)//一次次往前交换
                swap(arr, j, j + 1);
    }

对数器

  1. 随机产生器:产生随机数组
  2. 准备一个绝对正确的方法:不需考虑时间复杂度,保证绝对正确即可
  3. 实现比对的方法
  4. 把方法a和方法b比对很多次来验证方法a是否正确
  5. 如果有一个样本是的比对出错,打印样本分析是哪个方法出错
  6. 当样本数量很多时比对测试依然正确,可以确定方法a已经正确

以冒泡排序为例

  1. 随机发生器

    public static int[] generateRandomArray(int size, int value){
        int[] arr = new int[(int) ((size + 1) * Math.random())];
        for (int i = 0; i < arr.length; i++)
            arr[i] = (int)((value + 1) * Math.random()) - (int)(value * Math.random());//只要是随机数就行
        return arr;
    }
  2. 准备一个绝对正确的方法

    public static void rightMathod(int[] arr){
        Arrays.sort(arr);
    }
  3. 实现比对的方法

    //判断两个数组相等
    public static boolean isEqual(int[] arr1, int[] arr2){
        if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null))
            return false;
        if (arr1 == null && arr2 ==null)
            return true;
        if (arr1.length != arr2.length)
            return false;
        for (int i = 0; i < arr1.length; i++) {
            if(arr1[i] != arr2[i])
                return false;
        }
        return true;
    }
  4. 剩下放在main中的步骤

    //Main
    public static void main(String[] args) {
        //自己设置
        int testTime = 500000;
        int size = 10;
        int value = 100;
        boolean succeed = true;
        for (int i = 0; i < testTime; i++){
            int[] arr1 = generateRandomArray(size,value);//生成器
            int[] arr2 = copyArray(arr1);//拷贝arr1
            int[] arr3 = copyArray(arr1);
            bubbleSort(arr1);
            rightMathod(arr2);
            if(!isEqual(arr1,arr2)){
                succeed = false;
                printArrays(arr3);//将错误的样本打印出来
                break;
            }        
        }
        System.out.println(succeed ? "Nice!" : "Error!");
    }
    
    //复制数组
    public static int[] copyArray(int[] arr){
        if (arr == null)
            return null;
        int[] res = new int[arr.length];
        for (int i = 0; i< arr.length; i++)
            res[i] = arr[i];
        return res;
    }
    
    //打印样本
    public static int[] printArrays(int[] arr){
        ...
    }

剖析递归行为和递归行为时间复杂度的估算

递归求数组最大值

public static int getMax(int[] arr, int l,int r){
        if(r == l){
            return arr[l];
        }

        int mid = (l + r)/2;
        int maxLeft = getMax(arr, l, mid);
        int maxRight = getMax(arr, mid+1, r);
        return Math.max(maxLeft, maxRight);
    }

递归的本质是系统对函数进行了栈操作。

master公式

T(N) = a*T(N/b) + O(N^d)
1) log(b,a) > d -> 复杂度为O(N^log(b,a))
2) log(b,a) = d -> 复杂度为O(N^d * logN)
3) log(b,a) < d -> 复杂度为O(N^d)

只要子问题的规模是一致的,就可以用master公式

归并排序

先左边排好序,然后右边排好序,最后总体排好序(分治的思想)

T(N)=2T(N/2)+O(N)

所以时间复杂度:O(N*logN)

//归并排序
    public static void MergeSort(int[] arr) {
        if(arr == null || arr.length < 2)
            return;
        sortProcess(arr, 0, arr.length-1);
    }
    public static void sortProcess(int[] arr, int L, int R){
        if(L == R)
            return;
        int mid = (L+R)/2;
        sortProcess(arr, L, mid);
        sortProcess(arr, mid+1, R);
        Merge(arr, L, mid, R);
    }
    public static void Merge(int[] arr, int L, int mid, int R){
        int[] help = new int[R - L + 1];
        int i = 0;
        int p1 = L;
        int p2 = mid+1;
        //比较坐标p1和p2的值,小的复制进help数组
        while (p1 <= mid && p2 <= R)
            help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
        //两个while只会执行一个
        while(p1 <= mid)
            help[i++] = arr[p1++];
        while (p2 <= R){
            help[i++] = arr[p2++];
        }
        for(i = 0; i < help.length; i++){
            arr[L + i] = help[i];
        }
    }

分治法应用:二分查找

public static boolean Bfind(int[] arr, int a, int L, int R){
        if(arr == null || L > R)
            return false;

        int mid = (L + R)/2;

        if(a == arr[mid])
            return true;
        if(a < arr[mid])
            return Bfind(arr, a, L, mid+1);
        if(a > arr[mid])
            return Bfind(arr, a, mid+1, R);

        return false;
    }

小和问题

在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组
的小和。
例子:
[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

最容易的方法:O(N^2),对数器可用此方法

法二:归并排序

你可能感兴趣的:(java,算法-数据结构)