算法总结

倾然丶 夕夏残阳落幕 提交于 2021-01-16 18:41:20

算法与数据结构核心指标和梳理脉络

核心指标

时间复杂度

  • 要拆分成最基本的常数时间
  • 最终的时间复杂度会忽略的低阶项和高阶项系数,并且会抹掉常数项;
  • 如果高阶都一样,基本就不用理论分析了,直接随即大样本进行测试就行,因为加减乘除和位运算都是o(1)的常数项时间,显然位运算是更快的;

空间复杂度

  • 流程必须要的空间不算额外空间;作为输入参数输出结果都不是额外空间,比如对数组的拷贝功能,需要申请一个新的数组进行输出,则认为还是bigo(1)的额外空间;
  • 空间是固定的,与功能无关自己的空间,如果开辟的空间和样本数无关,就是bigO(1),否则如果需要开辟额外数组,那么就是bigO(N);

常数项时间

  • 固定的不能再拆分的固定时间的操作

分析算法怎么样一定要先看算法的最好和最差的情况,这就是说算法怎么样的思路; 考研中题目特点:怎么是最优解;以及别的思路怎么说服你才是最优解,以及怎么给低分,同样时间复杂度,怎么给高分;

梳理脉络

  • 知道怎么算的算法
  • 知道怎么试的算法
  • 对数器
    • 如果没有想到比较的方法,使用暴力递归也是一个最终极的对数器方案

刷题:

务必要注意general case 和 corner case 两种情况,缩进不要大于三次

  1. 选择排序
for (var i = 0; i < len - 1; i++) {
        minIndex = i;
        for (var j = i + 1; j < len; j++) {
            if (arr[j] < arr[minIndex]) {     // 寻找最小的数
                minIndex = j;                 // 将最小数的索引保存
            }
        }
        temp = arr[i];
        arr[i] = arr[minIndex];
        arr[minIndex] = temp;
    }
  1. 冒泡排序
for (var i = 0; i < len - 1; i++) {
        for (var j = 0; j < len - 1 - i; j++) {
            if (arr[j] > arr[j+1]) {        // 相邻元素两两对比
                var temp = arr[j+1];        // 元素交换
                arr[j+1] = arr[j];
                arr[j] = temp;
            }
        }
    }
  1. 插入排序

    • 将无序的元素插入到有序的元素序列中,插入后仍然有序
    	if (arr == null || arr.length < 2) {
    			return;
    		}
    		// 不只1个数
    		for (int i = 1; i < arr.length; i++) { // 0 ~ i 做到有序
    			for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
    				swap(arr, j, j + 1);
    			}
    		}
    
  2. 二分法
    有序并不是二分的必要条件,经典二分都是有序;

写法 :

  1. n2 换一种写法 n << 2; n2+1 换一种写法 n<<2|1,因为最低为是拿0来补的;
  2. (L+R)2 防止数太大会溢出,所以换一种写法 L+((R-L)>>2) 防止溢出;结果一样
  • 在一个有序数组中,找某一个数是否存在;
  • 再有一个有序数组中,找>= 某个数最左侧的位置;
  • 在一个有序数组中,找<=某个数最右侧的位置;
  • 局部最小值问题;
  1. 异或运算
    异或可以直接记忆为 无进位相加;就是加后不算进位就行;
    同一个数异或另一个数两次值不变,因为自己异或自己就是0;异或0就是自己;
    异或满足结合律和交换律;
  • 如何不用额外变量就交换两个数;
// 这事花招,其实直接用个临时变量temp也都一样;
// 注意交换的两个数不能在同一块内存,否则就都是零了,
// 需要有一个临时的存储空间来存储中间值;
a=a^b;
b=a^b;
a=a^b;

  • 一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这种数
		int[] nArr = {1,3,3,4,4,5,5,1,1};
		int eor = 0;// 必须要是0,或者数组里面的元素,因为只有自己异或0才是自己;
		for (int i : nArr) {
			eor = eor^i;
		}
		System.out.println(eor);
  • 怎么把一个int类型的数,提取出最右侧的1;
		int num = Integer.parseInt("00000000000000001110101010011001",2);
		System.out.println(num);
		
		System.out.println(Integer.toBinaryString(num&(~num+1)));
  • 一个数组中有两种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这两种数;
//找到他们自己异或之后得到的数,然后寻址这个数最右侧的1;就可以推断原来数组中的奇数中的一个数这一位肯定是1,
//所以就可以拿当前所有的数去异或这个最右侧为1的这个数,肯定就把这个位上为1的那个数找到了,
// 然后再和原来所有数异或之后的数再和这个奇数异或一下,两个数就都出来了;必须要再全部异或一下,因为不确定那位是1的就是那个奇数;
		int eor = 0;
		int[] numArr = {1,2,3,3,4,2,1,4,4,5,6,4,6,5,5,8};
		for(int i : numArr) {
			eor ^= i;
			
		}
		
		int rightone = eor & ((~eor)+1);
		
		int eor2 = 0;
		for(int i : numArr) {
			
			if ((rightone & i) != 0) {
				
			   eor2 ^= i;
			}
		}
		
		System.out.println(eor2+"......"+(eor^eor2));

  • 如何算一个数的二进制位中1有多少个?
// 还是根据最右侧的1来处理,先获取最右侧的1然后计数加1,然后再用他自己异或上这个最右侧1,直到整个数不为0;
int num = 123;
while(num != 0) {
int rightone = num & ((~num)+1);
n ^= rightone;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!