左神算法笔记(一)时间复杂度

时间复杂度

第一节讲解的是时间复杂度问题,所以在这里简单说明一下时间复杂度问题,时间复杂度为代码运行的基于单步时间的长度,时间复杂度在数学层面上为基于极限的一种思考,由于无法得知数据量本身的大小情况,因此算法运算的时间复杂度为当数据趋于无穷的时候需要花费的时间长度。在这里由于趋于无穷,因此将系数不再进行考虑,同时也只考虑最高次项,对其余次项进行忽略。

冒泡排序:

import java.util.Arrays;
public class Code_BubbleSort{ 
    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 swap(int[] arr,int i, int j){
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }

对于冒泡排序而言,自身存在的不足是不会将swap转换放到外面单独写一个函数,如果直接在原本的代码中写入转换排序可能导致主体代码过长,反而不容易进行思路的梳理。
冒泡排序中间存在两个循环,两个循环长度N(N-1),在时间复杂度上为o(N^2)。

选择排序

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 swap(int[] arr,int i,int j){
	int tmp = arr[i];
	arr[i] = arr[j];
	arr[j] = tmp;
}

选择排序跟冒泡排序的区别在于选择排序是寻找最小的数字,将其放在前面顺位依次走,冒泡排序是首先去寻找最大的数值,然后将其放在最后,一个从小到大,一个从大到小,所以两者的时间复杂度相同。

插入排序

public class InsertionSort{
	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);
		}
	}
}
public static void swap(int [] arr,int i, int j){
	int  tmp = arr[i];
	arr[i] = arr[j];
	arr[j] = tmp;
}

 

冒泡排序和选择排序跟数据本身没有任何关系,数据自身的状况不影响时间复杂度,但是插入排序跟数据状况有关系,数据处理实际的时间跟数据有关系,数据自身的是否有序会影响处理时间。因此会有最好情况,最差情况,平均情况。数据最差情况是时间复杂度的表达式。

对数器

对数器概念及使用
随机数组,在笔试阶段需要准备数组的对数器,样本比较器

// test 
public static void rightMathod(int [] arr){
	Arrays.sort(arr);
}
//test
public static int[] generateRandomArray(int size,int value){
	//math.random()->double[0,1)
	//(int)((size+1)*Math.random())->[0,size]整数
	//size = 6,size+1 = 7;
	//Math.random()->[0,1)*7->[0,7)double
	//double-> int [0,6] ->int
	
	//生成长度随机的数组
	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;
}
	

额外空间复杂度

面对数组中有限几个变量可以将程序跑完,额外空间复杂度为1.
流程跑下去自己申请的空间,跟原来数组长度一样或者为原来数组长度一半那空间复杂度都为N。

递归算法

递归时间复杂度计算公式
== 递归时间复杂度计算需要深入理解==
只需要递归复杂度公式满足上面T(N)公式,可以计算出abd三个参数的数值,然后去计算log(b,a)跟d的关系,从而可以计算复杂度。
N为父样本量,N/b为子样本的样本数目,a为拆分发生的次数。后面为除去子过程调用外,剩余的次数。

归并排序

public static void mergeSort(int[] arr){
	if(arr == null|| arr.length<2){
		return;
	}
	mergeSort(arr,0,arr.length-1);
}
public static void mergeSort(int[] arr,int l,int r){
	if(l==r){
		return;
	}
	int mid = l+((r-1)>>1);//二进制向右平移,其实本质上相当于与除以2
	mergeSort(arr,1,mid); //先将左边部分进行内部排序
	mergeSort(arr,mid+1,r);//再将右边部分进行排序
	merge(arr,1,mid,r);//最后将左边右边按照下标进行比较,从而实现整体的比较
}
public static void merge(int[] arr,int l,int m,int r){
	int[] help = new int [r-l+1];
	int i = 0;
	int p1 = 1;
	int p2 = m+1;
	//下面的判断是主要的分析过程
	while(p1<=m &&p2<=r){
		help[i++] = arr[p1] <arr[p2]? arr[p1++]:arr[p2++];
	}
	//下面两个其实可以写成if else的形式,两个while循环最终只会执行其中一个 
	while(p1<m){
		help[i++] = arr[p1++];
	}
	while(p2<=r){
		help[i++] = arr[p2++];
	}
	for(i = 0;i<help.length;i++){
		arr[l+i] = help[i];
	}
}

归并排序是在递归排序上延申出来的排序方法,归并排序较冒泡排序等方法简单的地方在于:归并排序将两边数据进行遍历的时候便开始做好排序,提供额外的信息量,从而使得整体算法复杂度降低。冒泡排序等由于排序本身跟数据无关,纯粹根据形式走,数据本身的特点无法利用,因此复杂度较高。
归并排序的时间复杂度为o(N*logN),空间复杂度为O(N)。(由于在算法执行过程中,创建了一个全新的与原数组长度相同的数组,因此空间复杂度为N)。

小和问题和逆序对问题

问题描述

public static int smallSum(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);
	return mergeSort(arr,1,mid)+mergeSort(arr,mid+1,r)+merge(arr,1,mid,r);
}
public static int merge(int[] arr,int l,int m,int r){
	int[] help = new int[r-l+1];
	int i = 0;
	int p1 = 1;
	int p2 = m+1;
	int res = 0;
	while(p1<m && p2<=r){
		res += arr[p1]<arr[p2]?(r-p2+1)*arr[p1]:0;
		help[i++] = arr[p1]<arr[p2]?arr[p1++]:arr[p2++];
	}
	while(p1<=m){
		help[i++] = arr[p1++];
	}
	while(p2<=r){
		help[i++] = arr[p2++];
	}
	for(i = 0;i<help.length;i++){
		arr[l+i] = help[i];
	}
	return res;
} 

对于小和问题的解决方法可以看到,基本依照于归并排列,利用归并排列将数据从小到大进行排列的同时,将左边比右边小的数值可以全部计算出,从而实现结果。

发布了24 篇原创文章 · 获赞 6 · 访问量 509

猜你喜欢

转载自blog.csdn.net/qq_35065720/article/details/104130277