递归分治区域
通过直接或间接的调用自身来达到缩小解范围的方式来解题
应用条件
- 原问题可以拆解为若干个较小规模的解
- 若干个较小规模的解可以组合成原问题的解
0714 斐波那契数列、兔子序列
斐波那契数列(1,1,2,3,5,8,13,21,34…)黄金分割数列,兔子序列
package 递归调用;
public class T001斐波那契数列兔子序列 {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(feiBoNaQieShuLie(10)); // 55
}
public static int feiBoNaQieShuLie(int i) {
if (i == 1) {
return 1;
}
if (i == 2) {
return 1;
}
return feiBoNaQieShuLie(i - 1) + feiBoNaQieShuLie(i - 2);
}
}
0714 汉诺塔
- 将 n-1 个盘子从柱1放到柱2
- 将一个盘子从柱1放到柱3
- 将柱2的n-1个盘子放到柱3
f(n) = 2f(n-1) + 1;
// 计算搬运次数
package 递归调用;
public class T002汉诺塔 {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(hanNuoTa(64)); // 1.8446744E19
}
public static float hanNuoTa(int i) {
if (i == 1) {
return 1;
}
return 2 * hanNuoTa(i - 1) + 1;
}
}
package 递归调用;
public class T002汉诺塔 {
public static void main(String[] args) {
hanNuoTa2('a', 'b', 'c', 3); //a->c a->b c->b a->c b->a b->c a->c
}
public static void hanNuoTa2(char a, char b, char c, int i) {
if (i == 1) {
System.out.println(a + "->" + c);
} else {
hanNuoTa2(a, c, b, i - 1); // 将i-1个盘子从a放到b上
hanNuoTa2(a, b, c, 1); // 将剩下的一个盘子从a放到c上
hanNuoTa2(b, a, c, i - 1); // 将i-1个盘子从b放到c上
}
}
}
查找检索
0714 折半、二分查找
时间复杂度:O(logn)
package 查找算法;
public class 折半查找 {
public static void main(String[] args) {
// 限定条件 有序
int[] arr = { 1, 5, 6, 8, 9, 45, 56 };
int target = 45;
int start = 0;
int end = arr.length - 1;
int pos = -1;
int mean;
while (start <= end) {
mean = (start + end) / 2;
if (arr[mean] > target) {
end = mean;
} else if (arr[mean] < target) {
start = mean;
} else {
pos = mean;
break;
}
}
// { 1, 5, 6, 8, 9, 45, 56 };
// 45;
System.out.println(pos); // 5
}
}
排序
- 冒泡
- 选择
- 插入
- 快速
- 希尔
- 堆
- 归并
0714 冒泡
- 判断相邻数字,将大的放到右边,一次循环下来就将最大的数方到了最右边
- 控制循环的范围,直到一次循环中没有数字交换即排序完成
O(n^2)、稳定、原地操作
package 排序算法;
public class 冒泡排序 {
public static void main(String[] args) {
// TODO Auto-generated method stub
double[] arr = { 12.9, 53.54, 75.0, 99.1, 3.14 };
// 外层循环控制范围
for (int i = 1; i < arr.length; i++) {
// 内层循环判定规则,不满足的交换数据
for (int j = 0; j < arr.length - i; j++) {
if (arr[j] > arr[j + 1]) {
double tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
for (double i : arr) {
System.out.print(i + "\t"); // 3.14 12.9 53.54 75.0 99.1
}
}
}
0714 选择
- 每一次循环将最大值拿出来和当前范围内的最后一个数据进行交换
O(n^2)、稳定、原地操作
package 排序算法;
public class 选择排序t {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] arr = { 58, 61, 32, 54, 91, 52, 46, 25 };
int end;
int tmp;
for (int i = arr.length - 1; i >= 0; i--) {
tmp = 0;
for (int j = 0; j < i; j++) {
if (arr[j] > arr[tmp]) {
tmp = j;
}
}
arr[i] += arr[tmp];
arr[tmp] = Math.abs(arr[tmp] - arr[i]);
arr[i] -= arr[tmp];
}
for (int i : arr) {
System.out.print(i + "\t"); // 0 25 46 52 54 58 61 91
}
}
}
0716 插入
整理手中的一手好牌懂不懂。
- 从当前位置向前检索,找到自己合适的位置进行插入
package 排序算法;
import java.util.Arrays;
public class 插入排序 {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] arr = { 58, 61, 32, 54, 91, 52, 46, 25 };
for (int i = 1; i < arr.length; i++) {
int j = i;
while (true) {
if (j == 0) {
break;
}
if (arr[j] >= arr[j - 1]) {
break;
}
int tmp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = tmp;
j--;
}
}
System.out.println(Arrays.toString(arr)); // [25, 32, 46, 52, 54, 58, 61, 91]
}
}
0716 快速
一个基准,大于该基准的放到右边,小于的放到左边
缩小范围,直到所有的数据都排完序为止
时间复杂度不稳定,原地操作
package 排序算法;
import java.util.Arrays;
public class 快速排序 {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] arr = { 58, 61, 32, 54, 91, 52, 46, 25 };
kuaiPai(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr));
}
public static void kuaiPai(int[] arr, int start, int end) {
if (start >= end) {
return;
}
int target = arr[start]; // 用第一个数当做基准,大于的放到右边,小于的放到左边
int min = start;
int max = end;
int i = start;
int tmp;
while (max != min) { // 当左指针和右指针指向同一个数据时则分区完毕
if (arr[i] > target) {
tmp = arr[i];
arr[i] = arr[max];
arr[max] = tmp;
max--;
continue;
}
if (arr[i] < target) {
tmp = arr[min];
arr[min] = arr[i];
arr[i] = tmp;
min++;
}
i++;
}
kuaiPai(arr, start, max - 1); // 将未确定位置的 小于 当前基准的数据进行排序
kuaiPai(arr, max + 1, end); // 将未确定位置的 大于 当前基准的数据进行排序
}
}
58 61 32 54 91 52 46 25 ;
58 52 32 25 91 62 46 54
32 25 46 52 58 54 91 62
25 32 46 52 54 58 61 91
希尔
插入排序的优化
插入排序让这个数据和自己前边的每一个都要比较嘛,一个接着一个
这个设定了一个
增量
(依次递减折半),将这个索引和自己差着一个增量的数据进行比较交换,一趟下来就比较了一半的次数,第二趟的时候在将
增量折半
后再增量排一次,直到增量为1为止程序:三层for循环
- 第一层控制增量,从数组长度一半开始递减,每次折半
- 第二层控制插入排序次数:一般次数和增量数量相同(数组长度奇数次数加一)
- 第三层循环:从当前索引向后以增量为步长进行进行判定交换
package 排序算法;
import java.util.Arrays;
public class 希尔排序 {
public static void main(String[] args) {
// 三层for循环
// 第一层控制增量
int[] arr = { 58, 62, 32, 54, 91, 52, 46, 25, 1 };
for (int d = arr.length / 2; d > 0; d /= 2) {
// 第二层遍历
for (int i = 0; i <= d; i++) {
// 第三层判定交换
for (int j = i; j < arr.length; j += d) {
if (j - d >= 0) {
if (arr[j - d] > arr[j]) {
int tmp = arr[j - d];
arr[j - d] = arr[j];
arr[j] = tmp;
}
}
}
}
System.out.println(Arrays.toString(arr));
}
}
}
[ 1, 52, 32, 25, 58, 62, 46, 54, 91]
[ 1, 25, 32, 52, 46, 54, 58, 62, 91]
[ 1, 25, 32, 46, 52, 54, 58, 62, 91]
[ 58, 52, 32, 25, 91, 62, 46, 54]
[ 32, 25, 46, 52, 58, 54, 91, 62]
[ 25, 32, 46, 52, 54, 58, 62, 91]
堆
归并
思路看图
[参考网站](【算法】排序算法之归并排序 - 知乎 (zhihu.com))
程序思路:
- 递归传入数组,分裂排序的开始和结束标记。
- 计算分裂点,将分裂的两个数组继续传入该函数进行排序,当要排序的数量为1时返回
- 生成于要排序数组的大小一致的临时数组,
- 两个数组中第一个较小的数拿出来放到临时数组中直到,要排序的两个数组都没数为止
- 使用临时数组覆盖原来数组的部分
package 排序算法;
import java.util.Arrays;
public class 归并排序 {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] arr = { 58, 62, 32, 54, 91, 52, 46, 25, 1 };
guiBing(arr, 0, arr.length);
System.out.println(Arrays.toString(arr));
}
public static void guiBing(int[] arr, int start, int end) {
if (end - start <= 1) {
return;
}
int[] tmpArr = new int[end - start + 1];// 排好序的
int mean = start + (int) Math.ceil(end - start + 1) / 2;// 分裂点
int you = mean; // 分裂的右边的起始索引
int zuo = start; // 分裂左边的起始索引
guiBing(arr, start, you);
guiBing(arr, you, end);
int i = 0; // 临时数组的索引
while (true) {
if (zuo >= mean || you >= end) {
break;
}
if (arr[zuo] <= arr[you]) {
tmpArr[i] = arr[zuo];
zuo++;
i++;
} else {
tmpArr[i] = arr[you];
you++;
i++;
}
}
if (i < tmpArr.length) {
// 左边数组没判定完
if (zuo < mean) {
for (int index = zuo; index < mean; index++) {
tmpArr[i] = arr[index];
i++;
}
}
// 右边数组没判定完
if (you < end) {
for (int index = you; index < end; index++) {
tmpArr[i++] = arr[index];
}
}
}
i = 0;
for (int index = start; index < end; index++) {
arr[index] = tmpArr[i++];
}
}
}
终端
[1, 25, 32, 46, 52, 54, 58, 62, 91]
奇奇怪怪的需求
不创建新变量将连个变量值进行颠倒
int a = 1;
int b = 2;
a = a + b; // 3 2
b = a - b; // 3 1
a = a - b; // 2 1
System.out.println(a + "" + b); // 2 1
面向对象设计的三大特性
0720 父类的私有属性是否可以被继承
目前来说网上流传的有两种
- 子类可以通过父类的
get/set
方法来访问设置父类的私有属性。 - 不可以:大圈(子类)套小圈(父类)
不管怎么说子类创建的原因都是父类当前所支持的属性方法不能满足当前的业务需求,所以子类的限定或者适配群体肯定要比父类要小,那就意味着子类具有更多的属性或方法。所以从满足条件来说子类所需条件肯定要比父类要多,但是父类中的私有属性根本就没再这个圈子中,只不过这个父类的圈中有一些可以访问甚至修改父类私有属性的方法被子类继承了,
结论:
子类对于父类的私有属性,感觉相当于外部类相对于私有属性一样,就感觉好像不是在写子类,而是在相对于这个父类而言,我在写一个测试类,只不过这个测试类不用声明对象,就可以直接调用或使用父类的所有可被继承的属性或方法。就很微妙。
package com.test;
public class 继承测试 {
public static void main(String[] args) {
B b = new B();
b.setID(50);
System.out.println(b.getID());
System.out.println(b.toString()); // com.test.B@5aaa6d82
}
}
class A {
private int ID = 20;
public String name;
public int getID() {
return this.ID;
}
public void setID(int ID) {
this.ID = ID;
}
}
class B extends A {
}
0720 追踪println源码
// println打印输入任意对象类型时,实际上会调用String类中定义的valueOf方法,将对象转换为String进行输出
String s = String.valueOf(x);
// String中的valueOf针对null进行了字符串话处理,如果是非空对象则调用对象中的toString方法
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
// 所有的Java对象都直接或者间接的继承于Object类,所以当前类中即使没有定义toString方法,也会从
// Object类中继承得到toString方法。Object类中定义的toString方法为【当前对象所属类的全名称@16进制hash值字符串】
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
// 哈希值是一个和当前对象存储位置相关的整型值
0720 对象比较
一般情况下基本数据类型在比较的时候是比较值,引用数据类型比较的是引用值(使用地址创建出来的一个哈希值)
堆和栈
栈
缺点:存储长度一致
优点:快速定位
堆
优点:想要多大内存就有多大内存
缺点:定位元素困难且慢
缝合怪(一般是这么用的)
栈来存储变量名-堆的存储地址
0720 面向对象和面向过程的区别
面向过程
- 优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式 开发、Linux/Unix等一般采用面向过程开发,性能是最重要的因素。
- 缺点:没有面向对象易维护、易复用、易扩展
面向对象
- 优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系 统,使系统更加灵活、更加易于维护
- 缺点:性能比面向过程低