7.21
嗯,拖了好久好久说要好好地,系统地学习一遍算法,但是每次都因为各种原因,刚开始就结束了。
这一次算是下了一个决心,每天写一个小总结来监督一下自己。
呐,这是第一天的学习内容,今天学的是算法最开始的东西,时间复杂度、栈、基本的排序算法,然后看了一下Java的ArrayList的源码。
时间复杂度
参考:https://blog.csdn.net/qq_41523096/article/details/82142747
一般来说,对于一个算法来说,速度是非常重要的。所以了解自己算法的时间复杂度是必须最先学习的。
:laughing:
为了计算时间复杂度,我们通常会估计算法的操作单元数量,每个单元运行的时间都是相同的。因此,总运行时间和算法的操作单元数量最多相差一个常量系数。
相同大小的不同输入值仍可能造成算法的运行时间不同,因此我们通常使用算法的最坏情况复杂度,记为T(n),定义为任何大小的输入n所需的最大运行时间。另一种较少使用的方法是平均情况复杂度,通常有特别指定才会使用。时间复杂度可以用函数T(n) 的自然特性加以分类,举例来说,有着T(n) =O(n) 的算法被称作“线性时间算法”;而T(n) =O(M^n) 和M= O(T(n)) ,其中M≥n> 1 的算法被称作“指数时间算法”。
一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。一个算法中的语句执行次数称为语句频度或时间频度。记为T(n)。
一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/f (n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n)),称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。
在各种不同算法中,若算法中语句执行次数为一个常数,则时间复杂度为O(1),另外,在时间频度不相同时,时间复杂度有可能相同,如T(n)=n2+3n+4与T(n)=4n2+2n+1它们的频度不同,但时间复杂度相同,都为O(n2)。
嗯,其实上面说的还不是特别精炼,其实总的来说。时间复杂度就是程序经过了多少次运算,这个运算次数和样本量的关系。
比如说,我一个正常的选择排序,就是n+n-1+……,最后运行n(n+1)/2次,时间复杂度就是O(n^2)。
在实际当中,有很多问题都是不容易得出自己算法的时间复杂度的,那么我们怎么办呢?最直接的方法当然就是记忆了。
这里列一个比较难的递归的时间复杂度求导公式。
master定理
假设有递推关系式
[图片上传失败...(image-8a8ff5-1595322971460)]
,其中
[图片上传失败...(image-ac3861-1595322971460)]
为问题规模,
[图片上传失败...(image-5d858-1595322971460)]
为递推的子问题数量,
[图片上传失败...(image-f0d922-1595322971460)]
为每个子问题的规模(假设每个子问题的规模基本一样),
[图片上传失败...(image-8e5766-1595322971460)]
为递推以外进行的计算工作。
a≥1,b>1为常数,f(n) 为函数,T(n) 为非负整数。则有以下结果(分类讨论):
(1)若
[图片上传失败...(image-c64258-1595322971460)]
那么
[图片上传失败...(image-e1f487-1595322971460)]
(2)若
[图片上传失败...(image-12ce66-1595322971461)]
那么
[图片上传失败...(image-1a4c95-1595322971461)]
(3)若
[图片上传失败...(image-ecbe3d-1595322971461)]
且对于某个常数
[图片上传失败...(image-553d05-1595322971461)]
和所有充分大的
[图片上传失败...(image-b77d8e-1595322971461)]
有
[图片上传失败...(image-fcac62-1595322971461)]
那么
[图片上传失败...(image-78e6ca-1595322971461)]
简单的排序算法
因为是第一天,学的内容很简单,主要就是学习了几个排序算法,冒泡排序,选择排序,归并排序,外排序,二分查找,然后做了一个简单的题目,小数和问题。然后最后做了一个检测器,检测算法的稳定性。
我觉得排序算法应该是每个学习编程的人最先接触到的算法,emmm,不知道这样说是不是太以偏概全了,反正我是的。:partly_sunny:,经过这么多年的发展,排序算法已经算是做的比较完善的了,比较基础的比如说,插入排序,选择排序,冒泡排序,比较高级的比如说,快速排序,归并排序,堆排序,再深入一点,还有希尔排序。
理解任何一种算法,都是必须从最简单的开始,只有理解了最简单的代码,之后你才能更好地学习更深入的东西。
对于排序算法,我个人来讲,用的比较多的是快排和堆排,emm,不知道原因。
我觉得排序算法需要说的也不多,这里就不多花时间一一介绍了,我主要说一下归并排序。
归并排序
其实emm,类似于二叉树,就一直二分下去嘛,然后再回来。这里面涉及到了树和递归的概念。用脑子:bridge_at_night:想一想,整个过程还是非常简单的,但是有个位置我觉得需要注意一下。
那就是外排序的位置。
public static void merge(int[] arr,int L,int mid,int R){
int[] help = new int[R - L + 1];
int a0 = 0;
int a1 = L;
int a2 = mid+1 ;
while (a1<=mid && a2 <= R){
help[a0++] = arr[a1] < arr[a2] ? arr[a1++]:arr[a2++];
}
while (a1 <= mid){
help[a0++] = arr[a1++];
}
while (a2 <= R){
help[a0++] = arr[a2++];
}
if (help.length >= 0) System.arraycopy(help, 0, arr, L + 0, help.length);
}
外排序是什么呢?我的理解就是利用空间换时间,不直接对原数组进行改变,而是借用一个另外的数组进行排序。将两个数列拍好之后,再把相应的额外数组copy给原数组的相应位置。大大提高了速度。
这种时间换空间的思想我觉得挺重要的,这就把原本一维的东西升级为了二维,在目前运算量较小的时候看不出来差距,但是一旦数量大了之后,差距就很明显。
下面展示一下各个代码吧,也没有经过啥优化。
binarySearch
package FirstDay;
public class binarySearch {
public static int Search(int[] arr, int L,int R){
int mid = (L + R)/2;
if(L == R){
return arr[L];
}else {
int maxL = Search(arr,L,mid);
int maxR = Search(arr,mid+1,R);
return Math.max(maxL,maxR);
}
}
public static void main(String[] args){
int[] arr = {5,2,1,6,23,21};
System.out.println("the max of arr is "+Search(arr,0,arr.length-1));
}
}
bubbleSort
package FirstDay;
import sun.security.util.Length;
public class bubbleSort {
public static void main(String[] args) {
int lenth = 10;
double testNum = 0;
selectSort standard = new selectSort();
while (testNum < 100000)
{
double[] arr = randMaker(lenth , 20);
double[] arr2 = arr.clone();
standard.sort(arr2);
sort(arr);
boolean yep = match(arr , arr2);
if (!yep){
System.out.println("it's wrong!");
return;
}
System.out.println("success!");
}
}
public static void sort(double[] arr){
if (arr.length < 2 || arr == null){
return;
}
else{
for (int i = 0;i < arr.length-1;i++)
for (int n = 0;n < arr.length- i -1; n++){
if(arr[n] > arr[n+1]){
swap(n,arr);
}
}
}
}
public static void swap(int n,double[] arr){
double a = arr[n];
arr[n] = arr[n+1];
arr[n+1] = a;
}
public static double[] randMaker(int n,int m ){
double[] ary = new double[n];
for (int i =0 ; i < n ; i ++)
{
ary[i] = Math.random()*m;
}
return ary;
}
public static boolean match(double[] m1,double[] m2){
for (int i = 0 ; i < m1.length ; i++)
{
if (m1[i] != m2[i])
{
return false;
}
}
return true;
}
}
mergeSort
package FirstDay;
public class mergeSort {
public static void main(String[] args){
int[] arr = {5,2,1,6,23,21};
mergesort(arr,0,arr.length-1);
print(arr);
}
public static void mergesort(int[] arr,int L,int R){
if(L == R){
return;
}else {
int mid = (L + R) / 2;
mergesort(arr,L,mid);
mergesort(arr,mid+1,R);
merge(arr,L,mid,R);
}
}
public static void print(int[] arr){
for(int i = 0; i= 0) System.arraycopy(help, 0, arr, L + 0, help.length);
}
}
selectSort
package FirstDay;
import java.util.Scanner;
public class selectSort {
public static void main(String[] args){
System.out.println("this is a program named select sort , please input a number and a array, thanks!");
Scanner input = new Scanner(System.in);
int lenth = input.nextInt();
double[] arr = new double[lenth];
for (int i = 0 ;i < lenth; i++)
{
arr[i] = input.nextDouble();
}
sort(arr);
}
double[] arry;
public static void sort(double[] arr){
for(int i =0; i < arr.length-1; i++){
double minNum = arr[i];
int position = i;
for(int n = i; n < arr.length; n++){
if(minNum > arr[n]){
minNum = arr[n];
position = n;
}
}
swap(arr,position,i);
}
}
public static void swap(double[] arr,int n,int m){
double tem = arr[n];
arr[n] = arr[m];
arr[m] = tem;
}
}
SmallSumProblem
package FirstDay;
/**
* @PACKAGE_NAME: FirstDay
* @NAME: SmallSumProblem
* @USER: 暖心校草小玮
* @DATE: 2020/7/21 0021
* @TIME: 9:11
* @MONTH_NAME_SHORT: 7月
* @DAY_NAME_FULL: 星期二
* @PROJECT_NAME: MyEverydayJavaArithmetic
**/
public class SmallSumProblem {
public static void main(String[] args){
int[] arr = {5,2,1,6,23,21};
System.out.println(mergesort(arr,0,arr.length-1));
}
public static int mergesort(int[] arr,int L,int R){
if(L == R){
return 0;
}else {
int mid = (L + R) / 2;
return (
mergesort(arr,L,mid)+
mergesort(arr,mid+1,R)+
merge(arr,L,mid,R));
}
}
public static int merge(int[] arr,int L,int mid,int R){
int[] help = new int[R - L + 1];
int a0 = 0;
int a1 = L;
int a2 = mid+1 ;
int res = 0;
while (a1<=mid && a2 <= R){
res += arr[a1] < arr[a2] ? arr[a1]*(R - a2 + 1) : 0;
help[a0++] = arr[a1] < arr[a2] ? arr[a1++]:arr[a2++];
}
while (a1 <= mid){
help[a0++] = arr[a1++];
}
while (a2 <= R){
help[a0++] = arr[a2++];
}
if (help.length >= 0) System.arraycopy(help, 0, arr, L + 0, help.length);
return res;
}
}
补充
介绍完了之前的东西,我觉得要补充几点。
首先是小数和问题。
一个数组
12 4 5 1 54
当当前数之前的数小于当前的数时,总和加上这个较小数。
例如5,他之前比他小的数就是4,那么总和就要加上4.
如何快速计算出来一个数组中的小数和呢?就得用到归并排序的思想了,同样的道理也适用于求解逆序数,大家可以尝试一下!
下一次学的话,也是学习排序相关的算法吧,先把排序学好再说!
加油!
:beers: