Java学习Day4

排序

定义:对一组数进行指定的逻辑规则排列。
排序方法:冒泡、选择、希尔、快速排序、归并排序、插入排序
测试排序方法效率的维度:时间复杂度、空间复杂度

时间复杂度

我们把算法需要执行的运算次数 用 输入大小n 的函数 表示,即 T(n) 。
 此时为了 估算算法需要的运行时间 和 简化算法分析,我们引入时间复杂度的概念。
定义:存在常数 c和函数 f(N),使得当 N >= c 时 T(N) <= f(N),表示为 T(n) = O(f(n)) 。
算法的时间复杂度,用来度量算法的运行时间,记作: T(n) = O(f(n))。
它表示随着输入大小n 的增大,算法执行需要的时间的增长速度可以用 f(n) 来描述。

1.我们知道常数项对函数的增长速度影响并不大,所以当 T(n) = c,c 为一个常数的时候,我们说这个算法的时间复杂度为 O(1);如果 T(n) 不等于一个常数项时,直接将常数项省略。

比如
第一个 Hello, World 的例子中 T(n) = 2,所以我们说那个函数(算法)的时间复杂度为 O(1)。
T(n) = n + 29,此时时间复杂度为 O(n)。

2.我们知道高次项对于函数的增长速度的影响是最大的。n^3 的增长速度是远超 n^2 的,同时 n^2 的增长速度是远超 n 的。 同时因为要求的精度不高,所以我们直接忽略低此项。

比如
T(n) = n^3 + n^2 + 29,此时时间复杂度为 O(n^3)。

3.因为函数的阶数对函数的增长速度的影响是最显著的,所以我们忽略与最高阶相乘的常数。

比如
T(n) = 3n^3,此时时间复杂度为 O(n^3)。

综合起来:如果一个算法的执行次数是 T(n),那么只保留最高次项,同时忽略最高项的系数后得到函数 f(n),此时算法的时间复杂度就是 O(f(n))。为了方便描述,下文称此为 大O推导法。

由此可见,由执行次数 T(n) 得到时间复杂度并不困难,很多时候困难的是从算法通过分析和数学运算得到 T(n)。对此,提供下列四个便利的法则,这些法则都是可以简单推导出来的,总结出来以便提高效率。

1.对于一个循环,假设循环体的时间复杂度为 O(n),循环次数为 m,则这个循环的时间复杂度为 O(n×m)。

void aFunc(int n) {
    for(int i = 0; i < n; i++) {         // 循环次数为 n
        printf("Hello, World!\n");      // 循环体时间复杂度为 O(1)
    }
}

此时时间复杂度为 O(n × 1),即 O(n)。

2.对于多个循环,假设循环体的时间复杂度为 O(n),各个循环的循环次数分别是a, b, c…,则这个循环的时间复杂度为 O(n×a×b×c…)。分析的时候应该由里向外分析这些循环。

void aFunc(int n) {
    for(int i = 0; i < n; i++) {         // 循环次数为 n
        for(int j = 0; j < n; j++) {       // 循环次数为 n
            printf("Hello, World!\n");      // 循环体时间复杂度为 O(1)
        }
    }
}

此时时间复杂度为 O(n × n × 1),即 O(n^2)。

3.对于顺序执行的语句或者算法,总的时间复杂度等于其中最大的时间复杂度。

void aFunc(int n) {
    // 第一部分时间复杂度为 O(n^2)
    for(int i = 0; i < n; i++) {
        for(int j = 0; j < n; j++) {
            printf("Hello, World!\n");
        }
    }
    // 第二部分时间复杂度为 O(n)
    for(int j = 0; j < n; j++) {
        printf("Hello, World!\n");
    }
}

此时时间复杂度为 max(O(n^2), O(n)),即 O(n^2)。

4.对于条件判断语句,总的时间复杂度等于其中 时间复杂度最大的路径 的时间复杂度。

void aFunc(int n) {
    if (n >= 0) {
        // 第一条路径时间复杂度为 O(n^2)
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < n; j++) {
                printf("输入数据大于等于零\n");
            }
        }
    } else {
        // 第二条路径时间复杂度为 O(n)
        for(int j = 0; j < n; j++) {
            printf("输入数据小于零\n");
        }
    }
}

此时时间复杂度为 max(O(n^2), O(n)),即 O(n^2)。

时间复杂度分析的基本策略是:从内向外分析,从最深层开始分析。如果遇到函数调用,要深入函数进行分析。

一. 基础题

void aFunc(int n) {
    for (int i = 0; i < n; i++) {
        for (int j = i; j < n; j++) {
            printf("Hello World\n");
        }
    }
}

O(n^2)。

二. 进阶题

void aFunc(int n) {
    for (int i = 2; i < n; i++) {
        i *= 2;
        printf("%i\n", i);
    }
}

参考答案:
O(log n)。

三. 再次进阶

long aFunc(int n) {
    if (n <= 1) {
        return 1;
    } else {
        return aFunc(n - 1) + aFunc(n - 2);
    }
}

参考答案:
O(2^n)。

空间复杂度

一个程序的空间复杂度是指运行完一个程序所需内存的大小。利用程序的空间复杂度,可以对程序的运行所需要
的内存多少有个预先估计。一个程序执行时除了需要存储空间和存储本身所使用的指令、常数、变量和输入数据
外,还需要一些对数据进行操作的工作单元和存储一些为现实计算所需信息的辅助空间。程序执行时所需存储空
间包括以下两部分。  
(1)固定部分。这部分空间的大小与输入/输出的数据的个数多少、数值无关。主要包括指令空间(即代码空间
)、数据空间(常量、简单变量)等所占的空间。这部分属于静态空间。
(2)可变空间,这部分空间的主要包括动态分配的空间,以及递归栈所需的空间等。这部分的空间大小与算法
有关。

一个算法所需的存储空间用f(n)表示。S(n)=O(f(n))  其中n为问题的规模,S(n)表示空间复杂度。
要注意的是递归算法的空间复杂度,假如递归深度为N*每次递归的辅助空间大小,如果每次递归的辅助空间为常数,则空间复杂度为O(N)
空间复杂度:函数中创建对象的个数关于问题规模函数表达式,一般情况用O的渐进表示法表示。
在这里插入图片描述

冒泡排序

冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。

Demo

	public static void main(String[] args) {
		int[] arr = new int[] {3,1,7,5};
//		冒泡排序
		bubbleSort(arr);
		for(int i:arr) {
			System.out.println(i);
		}
	}
//	冒泡排序
	public static void bubbleSort(int[] arr) {
		for (int i = 0; i<arr.length; i++) {//确定的是整体的次数
			for (int j = 0; j<(arr.length-i-1); j++) {//确定一个元素需要比较的次数
				if (arr[j]>arr[j+1]) {
					arr[j] ^= arr[j+1];
					arr[j+1] ^=arr[j];
					arr[j] ^= arr[j+1];
				}
			}
		}
	}

逻辑图
在这里插入图片描述

选择排序

表现最稳定的排序算法之一,因为无论什么数据进去都是O(n2)的时间复杂度,所以用到它的时候,数据规模越小越好
选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕

Demo

	public static void main(String[] args) {
		int[] arr = new int[] {3,1,7,5};

//		选择排序
		int[] arr1 = {3,1,7,5};
			selectSort(arr1);
			for(int i:arr1) {
				System.out.println(i);
		}
		//	选择排序
	public static void selectSort(int[] arr1) {
		for (int i = 0; i<arr1.length; i++) {//确定的是整体的次数
			for (int j = i; j<(arr1.length-1); j++) {//确定一个元素需要比较的次数
				if (arr1[i]>arr1[j+1]) {//用第0个元素和第j+1个元素比较
					arr1[i] ^= arr1[j+1];
					arr1[j+1] ^=arr1[i];
					arr1[i] ^= arr1[j+1];
				}
			}
		}
	}

逻辑图
在这里插入图片描述

更多排序算法以后更新

查找

注意点

1.查到了,返回当前元素的下标
2.查不到,默认返回-1。
3.当有多个相同的值,只查出现的第一个,查到了立刻停止查找。

普通查找

Demo

	public static void main(String[] args) {
//查找
		int[] arr = {1,3,5,2,4,6,9,10};
		int index = search(arr, 12);
//		System.out.println(index);

//查找
	public static int search(int[] arr,int key) {
		for (int i = 0; i < arr.length; i++) {
			if (arr[i] == key) {
				return i;
			}
		}return -1;
	}

二分查找

引用至 https://blog.csdn.net/maoyuanming0806/article/details/78176957
二分查找是一种查询效率非常高的查找算法。又称折半查找。
有序的序列,每次都是以序列的中间位置的数来与待查找的关键字进行比较,每次缩小一半的查找范围,直到匹配成功。
一个情景:将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。

Demo

	public static void main(String[] args) {
//		二分查找
		int[] arr1 = {1,3,5,7};
		int index1 =binarySearch(arr1, 5);
		System.out.println(index1);
	}
//	二分查找
	public static int binarySearch(int[] arr1,int key) {
		int l=0;
		int h=arr1.length-1;//下标的最大值
		while(l<=h) {
			int m =(l+h)/2;
			if (arr1[m] == key) {
				return m;
			}else if(arr1[m] >key){
				h=m+1;
			}else if(arr1[m] <key){
				l=m+1;
			}
		}return -1;
	}

逻辑图
在这里插入图片描述

面向对象基础

面向对象是相对面向过程而言,面向对象和面向过程都是一种思想
1.面向对象是基于面向过程的
2.面向对象比面向过程高级
3.面向对象和面向过程都是思想
4.面向对象使人们从开发者变成了指挥者
5.面向对象更符合人们的思考习惯
6.面向过程比面向对象更底层,更高效
7.面向对象典型语言:Java 面向过程典型语言:C
面向过程
强调的是功能行为
关注的是解决问题需要哪些步骤
面向对象
将功能封装进对象,强调具备了功能的对象
关注的是解决问题需要哪些对象
面向对象是基于面向过程的。

类与对象的关系

  • 面向对象的三要素----最好都写英文
  • 名字:遵守大驼峰原则,所有单词的首字母大写
  • 属性:成员变量:遵守小驼峰原则
  • 行为:成员方法,遵守小驼峰原则
  • 举例:
  • 类名:人:(Person)
  • 属性:身高:height 体重:weight 年龄:age 姓名:name
  • 行为:吃饭 eat 打电话 callPhone(String tel)

将Person类实例化处理

创建Person类的一个对象,构成:new + 类名 +()

  • new:1.在堆内开辟一块儿内存空间 2.将地址返回
  • 每创建一个类就相当于创建了一个新的数据类型
  • Person+()其实是一个整体,是一个空参的方法.
  • 作用:
    1.告诉别人我这里创建的是一个Person类型的对象
    2.给当前对象的属性赋默认值,如果是整型:就是0 如果是引用数据类型:就是null

//类的关键字是class
class Person{
	//成员变量
	double height;
	double weight;
	int age;
	String name;//String是java专门封装的字符串的类,字符串的表示方法:""
	//成员方法
	public void eat() {
		System.out.println("吃饭");
	}
	public void call(String tel) {
		System.out.println(name+"给"+tel+"打电话");
	}
}
public class Demo3 {

	public static void main(String[] args) {
		//将Person类实例化处理
		//创建Person类的一个对象,构成:new + 类名 +()
		//new:1.在堆内开辟一块儿内存空间    2.将地址返回
		//每创建一个类就相当于创建了一个新的数据类型
		//Person+()其实是一个整体,是一个空参的方法.
		//作用:1.告诉别人我这里创建的是一个Person类型的对象  
		//2.给当前对象的属性赋默认值,如果是整型:就是0   如果是引用数据类型:就是null
		Person p = new Person();
		
//		通过.语法调用自己的属性和方法
		p.name = "张东升";
		p.eat();
		p.call("朱朝阳");
	
	}

}

在这里插入图片描述

对象的内存

在这里插入图片描述

单点调试

当程序出现问题时,可以通过Debug对程序代码一段一段的执行。

  1. 先给可能出现问题之前的代码行双击打一个断点。执行到断点前停止

在这里插入图片描述
2. 点击Debug按钮进入调试界面区
在这里插入图片描述
在这里插入图片描述

匿名对象

使用匿名对象作为方法的参数
作用:更加高效的使用内存,节省代码

Demo

public class Demo4 {

	public static void main(String[] args) {
		Dog dog = new Dog();//普通的对象
		new Dog();//匿名对象:没有名字的对象---垃圾
		
		//使用
		dog.age = 10;
		new Dog().name = "金毛";
		//经常使用匿名对象作为方法的参数,节省代码,节省内存
		feedAnimal(new Dog());
	}
	
	public static void feedAnimal(Dog dog) {
		dog.eat();
	}

}

class Dog{
	String name;
	int age;
	public void run() {
		System.out.println("跑");
	}
	public void eat() {
		
	}

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值