算法与数据结构的学习
1:常数时间的操作
常数时间的操作包括:
1)常见的算术运算
2)常见的位运算
(>>:带符号右移:右移后的缺省位子用符号位来填补;正数用0去填补;负数用1去填补)
(>>>:不带符号右移:不管是正数还是负数,右移后的缺省位都用0去填补)
3)赋值,比较,自增,自减的操作
4)数组的寻址操作
执行时间与数据量无关的操作称为常数时间的操作,执行时间不以数据量的变化而转移;反知,如果执行的时间与样本量的大小有关则不是常数时间的操作
时间复杂度就是衡量程序在执行的过程中发生了多少次常数时间的操作
2:选择排序
(每一次去拿一个假设的最值,依次去和数组中的其他值去进行比较,找出未排序的数中真正的最值)
每次执行的操作可以分为两步
看:数组去寻址。(常数时间的操作)
比:把寻址拿到的数与原来的数进行比较。(常数时间的操作)
看+比 得到最值的下标,在此之后
进行一次交换操作。
如何确定算法流程的总操作数量与样本量之间的关系?
如何确定算法流程的时间复杂度?时间复杂度的定义
当完成了表达式的建立,只要把最高项留下即可。低阶项都去掉,高阶项的系数也去掉。
记为:O(忽略掉系数的高阶项)
代码实现
第一个for:定下起点 起点的范围是:0~N-2;
0的时候 去比较下标 1到N-1
1的时候 去比较下标 2到N-1
N-2的时候 去比较下标 N-1和其关系
3:冒泡排序
步骤:
0~N-1上:两两比较,比个子,进行交换,个大的排到最右边
0~N-2上:两两比较
0~N-3上:两两比较
…
0~1上:两两比较
N2+常数的交换
N-12+常数的交换
2(n+n-1+…1)+n 时间复杂度也是O(n^2)
冒泡排序与选择排序的时间复杂度与数组的初始状态无关,依旧是O(n^2),因为初始状态只影响低阶项(是否进行交换)(初始状态的有序状态)
4:插入排序
0~0 上有序
0~1 上有序
0~2 上有序
…
0~N-2上有序 (N-1)2+常数次交换操作
0~N-1上有序 N2+常数次交换操作
但是:注意这种操作数组的初始状态是会影响到时间复杂度的影响,因为当数组有序,向前看一个数,就已经发现该数比前一个数进行看比了,假设数组全部有序,那么每次只要看一眼就可以,此时算法的复杂度是O(N)
但是:要想象该算法流程所处理的数据状况,要按照最差的情况来
时间复杂度也是O(N^2)
5:额外空间复杂度(由设计的流程决定)
解决问题所申请的内存空间与所给的数据量无关,仅仅有限的内存,不需要额外申请数组
O(1):固定常数的,常数操作
需要生成一个与原始Array一样的Array,此时的额外空间复杂度依然是O(1);因为申请的Array空间是用户所需求的,不是额外的空间复杂度。
与功能无关,自己流程实现必须开辟的内存空间称为额外空间复杂度。
6:常数项时间
常数时间的操作:(位运算大于加减大于乘除)
什么是最优解
7:关于对数器
使用Math.Random生成一个维度随机,值随机的一维数组
package com.fangjun;
/**
* 生成一个长度随机,里面值也是随机的一维数组
*/
public class day01 {
public static void main(String[] args) {
int[] ints = GenerateRandomArray.genArr(120, 130);
for (int i=0;i<ints.length;i++){
System.out.println(ints[i]);
}
}
}
class GenerateRandomArray{
public static int[] genArr(int maxsize,int maxValue){
int[] ints = new int[(int) (Math.random() * maxsize)];
for (int i=0;i<ints.length;i++){
ints[i]=(int)(Math.random()*(maxValue+1))-(int)(Math.random()*maxValue);
}
return ints;
}
}
8:关于二分法(细节)
1)有序数组,找某个数是否存在
进行遍历的复杂度是O(n)
二分查找复杂度是O(logN)
判断一个有序的数组是否含有某个值
假设找数组中是否存在7这个数
找到中点 如果中点是9,那么可以判断7位于数组的前半部分;在这前半部分的基础上再继续进行二分,如果中点是3,那么代表7在数组的后半部分;继续对剩下的部分继续进行二分,如果都二分完了,还是没找到7那么就代表数组中没有7。
package com.fangjun.codeo1;
public class Test001 {
public static void main(String[] args) {
int[] arrs={};
boolean test = TK.test(arrs, 3);
System.out.println(test);
}
}
class TK{
public static boolean test(int[] arr,int number){
boolean flag=false;
if (arr==null||arr.length==0){
flag=false;
}
int L=0;
int R=arr.length-1;
int mid;
while (L<R){
mid=L+((R-L)>>1);
if (arr[mid]==number){
flag=true;
break; // debug了一次
}else if (arr[mid]>number){
R=mid-1;
}else{
L=mid+1;
}
}
return flag;
}
}
2)一个有序数组中,找出大于等于某个数的最左侧位置
中点是否大于2;如果是大于等于2;丢掉右侧在剩下的数组中找中点,是否大于2?是,再去左侧,中点是大于等于2,再二分;如果是小于,那么最后一次二分标记就是要找的位置。(最左侧对号)
3)在一个有序数组中,找出小于等于某个数的最右侧位置
Tips常见的写法
mid =(L+R)/2 ---->L+(R-L)/2---->L+(R-L)>>1。 ()
N*2----->N<<1(一个数乘以2等于这个数向左移动一位)
N*2+1 等价于 N左移(左移后低位是0)一位后再与1或
(N<<1)|1
4)局部最小的寻找
无序数组
任意相邻两个数不相等
返回一个局部最小值
二分?
9:认识异或运算
无进位相加
10:单链表反转
1)第一步:用next变量记录当前节点的下一节点
2)第二步:打破指向下一节点的指针,并且将下一节点指向pre(存储前一节点)
3)第三步:更新pre为当前head的位置;
4)第四步:更新head为next的值
11:双向链表的反转
1)第一步:记录head的下一节点为 next
2)第二步:将当前节点的下一节点指向pre
3)第三步:将当前节点的上一节点指向next
4)第四步:更新pre节点W为当前head
5)第五步:将当前head指向Next
12:删除单链表的指定值
(JVM的内存释放)
13:栈和队列
栈:先进后出,如弹夹:先进后出
队列:先进先出
1)双向链表实现栈和队列
2)数组实现栈和队列
数组实现固定长度的栈
数组实现队列
双栈实现
14:递归的实现
递归的时间(子规模等量)
15:哈希表
哈希表,增删改查,时间复杂度O(1)
哈希表中的数据都是按照值进行索引
底层会调用equals方法