左神算法笔记

一、时间复杂度与空间复杂度

时间复杂度就是统计一个算法中的常数操作

常数操作有 数组寻址操作、循环遍历操作、加减乘除等,与数据量有关都不是常数操作

空间复杂度指程序执行所需要使用的空间大小  常数为O(1)

 

 在不使用额外空间的情况下更换两数的值

无进位相加

通过使用异或的性质 ^ :

  1. 0 ^ N = N    N ^ N = 0
  2. (a^b)^c = a^(b^c)
  3. a^b^a = b^(a^a) = b^0 = b
//使a、b交换值  真的妙
a = a ^ b;  
b = a ^ b;  ---> 结果 b = (a^b)^b = a^(b^b) = a^0 = a;
a = a ^ b;  ---> 结果 a = a^a^b = 0^b = b;

//简化
//防止出现两指针位置相同可以排除额外情况进行交换
if(a == b){
    return;
}
a ^= b;
b ^= a;
a ^= b;

使用这种方式的前提是 a和b值可以相同但一定不会指向同一块地址,如果他们指向同一块地址(位置) 说明a和b完全相同,则异或会为0,使用异或交换的本质是交换地址,基本类型是值交换

二、位运算

按位与( & ): 对应位数都为1 结果为1  否则为0

按位或( | ):对应位数为0 结果为0  否则为1

按位异或( ^ ):对应位数 相同为0  不同为1

面试题一、

一个数组中只有一种数是奇数个,其余为偶数个,求这个奇数个的数

题解:对数组中所有的元素进行异或,根据异或的特性,相同数异或为0,0与任何数异或都是他本身。

public static void num(String[] args){
    int[] arr = {1,2,3,2,1};
    
    for(int i=1; i<arr.length;i++){
        arr[0] ^= arr[i]; 
    }
    System.out.println(arr[0]);
}

面试题二、

一个数组中,有两种数是奇数个a、b,其他都为偶数个,求这两个奇数个的数

题解:对数组元素进行异或,最终得到的结果是两个奇数个数a、b, 异或的结果err,然后获取err中位数为1的位数(因为a与b不相同,所以他们异或的结果肯定至少有一位不为一),那么a和b在此位中一个为0一个为1,找出此位不为一的所有数进行异或,得到的结果就是a或者b,再将这个结果与err进行异或得到另一个值

public static void main(String[] args){
    int arr[] = {1,2,3,4,5,3,2,1};
    //异或结果
    int err = 0;
    for(int i=0;i<arr.length;i++){
        err ^= arr[i];
    }
    
    //a、b不相同 进行异或至少有一位为1
    //获取结果err最右侧为1的位数   000010
    int onlyOne = err & (~err +1); //原数与其补码与运算得到最右侧的1

    //err2为 a或者b
    int err2 = 0;
    for(int i=1;i < arr.length;i++){
        //找出所有onlyOne对应位上不为1的所有数进行异或得到a或b
        //例如 100101 & 000010 -> 000000
        if((arr[i] & onlyOne) == 0){
            err2 ^= arr[i];
        }
    }

    System.out.println("第一个数为" + err2);
    System.out.println("第二个数为" + err ^ err2);

}

三、排序算法  O(n^2)

1、插入排序

插入排序就像是打扑克牌时进行排序一样,抽到一张牌然后一次找到合适的位置将他插入,先在0~2范围内进行排序 然后0~3范围内进行排序,相当于将第三张牌在前两个位置找到合适地方插入,如果第三张牌比前面的大就进行交换

public class insertSort {
	//插入排序
	public static void main(String[] args) {
		int[] arr = {1,2,8,9,7,6,5};
		
		//依次获取  0~1 0~2 0~3 0~3 0~i
		//不需要判断0~0 所以从1开始
		for(int i = 1 ; i < arr.length; i++ ) {
			//依次在每个范围内进行判断大小并交换
			//取最后一个数进行排序 相当于抽一张扑克牌放到抽好的范围内
			for(int j = i - 1 ; j >= 0 && arr[j] > arr[j + 1]; j--) {
				//满足条件 j大于等于0 防止数组越界
				//并且左边的值比右边大进行交换
				swap(arr,j,j+1);
			}
		}
		
		System.out.println(Arrays.toString(arr));
	}
	
	/**
	 * 必须传入数组,才能交换数据  交换地址引用
	 */
	public static void swap(int[] arr,int i,int j) {
		arr[i] ^= arr[j];
		arr[j] ^= arr[i];
		arr[i] ^= arr[j];
	}

}

2、选择排序

选择排序就是每次将最小值移到最前面,每次定义循环第一位为最小值,如果遍历到比最小值还小的数,就进行交换   (选择排序更优)

public class bubbleSort {
	
	public static void main(String[] args) {
		
		int[] arr = {1,2,8,9,7,6,5};
		
		//定义循环次数
		for(int i = 0 ; i < arr.length; i++) {
			//将每次的循环第一位作为最小值
			int minIndex = i;
			//从最小值后一位向后遍历 如果比最小值还小就进行交换
			for(int j = i + 1 ; j < arr.length; j++) {
				if(arr[j] < arr[minIndex]) {
					swap(arr,j,minIndex);
				}
			}
		}
		
		System.out.println(Arrays.toString(arr));
	}
	
	public static void swap(int[] arr,int i,int j) {
		arr[i] ^= arr[j];
		arr[j] ^= arr[i];
		arr[i] ^= arr[j];
	}
	
}

3、冒泡排序

冒泡排序区别于选择排序,冒泡排序是每次找出最大值,并且是在两个相邻元素之间进行交换,如果前一个数比当前数小那么就交换两数,最终获取一轮循环的最大值,在进行n-1次循环,依次向后找出每轮的最大值并交换到数组末尾

public class bubbleSort {

	public static void main(String[] args) {
		int[] arr = {1,5,9,7,6,3,2};
		
		for(int i = arr.length - 1; i > 0 ; i--) {
			for(int j = 0; j < i; j++) {
				if(arr[j] > arr[j + 1]) {
					swap(arr,j,j+1);
				}
			}
		}
		System.out.println(Arrays.toString(arr));
	}
	
	public static void swap(int[] arr,int i,int j) {
		arr[i] ^= arr[j];
		arr[j] ^= arr[i];
		arr[i] ^= arr[j];
	}
}

补充:二分查找

public void twoSelect() {
		int[] arr1 = {1,2,3,5,7,9};
		int val = 7;
		int n = arr1.length;
		
		int left = 0;
		int right = n - 1 ;
		
		while(left <= right) {
            //int mid = left + (right - left)/2 右移一位比除二更快;
			int mid = left + (right - left)>>1;
			if(arr1[mid] < val) {
				left = mid + 1;
			}else {
				right = mid - 1 ;
			}
		}
		System.out.println(left);
	}

4、递归算法  O(NlogN)

master公式  计算递归算法的时间复杂度

前提:每次递归调用子问题的规模都是等量的

T(N)=      a    *      T(N/b)      +      O(N^d)

母问题 = 子问题被调用的次数  *  子问题每次调用的规模  + 剩下的时间复杂度

 

5、归并排序   --  O(NlogN)、空间复杂度O(N)

采用了分治思想:将大的事物通过递归逐步划分成小的具体实现    使用空间换时间

public class mergeSort {
	public static void main(String[] args) {
		int[] arr = {1,5,9,7,2,3,4,6};
		process(arr,0,arr.length-1);
		System.out.println(Arrays.toString(arr));
	}
	
	public static void process(int[] arr,int L,int R) {
		if(L == R) {
			return;
		}
		int mid = L + ((R - L) >> 1);
        //分治思想 先递归
		process(arr,L,mid);
		process(arr,mid +1,R);

        //再合并排序
		merge(arr,L,mid,R);
	}
	
	public static void merge(int[] arr,int L,int M,int R) {
		//L~R上一共有多少个数就开多大空间
		int[] temp = new int[R-L+1];
		//定义一个变量作为temp的指针
		int i = 0;
		//定义两个指针分别指向两个数组的起始位置
		//指向左部分
		int p1 = L;  
		//指向右部分
		int p2 = M + 1;
		
		while(p1 <= M && p2 <= R) {
			temp[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
		}
		
		//经过了循环后查看哪一个指针没有越界
		//将没越界的全部复制进去
		while(p1 <= M) {
			temp[i++] = arr[p1++];
		}
		
		while(p2 <= R) {
			temp[i++] = arr[p2++];
		}
		
		//对临时数组进行复制
		for(i = 0 ; i < temp.length; i++) {
			arr[i + L] = temp[i];
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值