左神算法基础班 第1章 第一周学习——认识复杂度和简单排序算法

左神算法课基础班

1. 复杂度、对数器、二分法、位运算与异或运算

1.1 排序

1.1.1 Java自带的排序函数

Arrays.sort(arr);

1.1.2 冒泡排序  

  • 时间复杂度O(n*n),空间复杂度O(1)

1.1.3 选择排序  

  • 时间复杂度O(n*n),空间复杂度O(1)
  • 保留数组中最小or最大的值的下标

1.1.4 插入排序 

  • 时间复杂度:最坏情况O(n*n),最好情况O(n),空间复杂度O(1)
  • 插入到合适的位置,内部循环从后往前看

1.2 对数器

  1.  有一个你想要测的方法a
  2.  实现复杂度不好但是容易实现的方法b
  3.  实现一个随机样本产生器
  4.  把方法a和方法b跑相同的随机样本, 看看得到的结果是否一样。
  5.  如果有一个随机样本使得比对结果不一致, 打印样本进行人工干预, 改对方法a或者方法b
  6.  当样本数量很多时比对测试依然正确, 可以确定方法a已经正确。

在我们遇到问题时候,a是我们设计出来的算法,b是暴力穷举法,这时候为了验证算法是否可靠,就可以通过产生大量的随机样本去检测算法的正确性,且我们可以通过改变输入,纠正两种算法种的错误。任何题目都可以有暴力做法。随机数组生成如下:

public static int[] GenerateRandomArray(int maxSize, int maxValue) {
	// Math.random()				等概率返回 [0, 1) 之间的小数
	// Math.random() * N			[0, N)
	// (int) (Math.random() * N)	[0, N-1]
	int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
	for (int i = 0; i < arr.length; i++) {
		arr[i] = (int) ((maxValue + 1) * Math.random());
	}
	return arr;
}

1.3 二分查找

public boolean BinarySearch(int[] sortedArr, int num) {
	if (sortedArr == null || sortedArr.length == 0) {
		return false;
	}
	int left = 0;
	int right = sortedArr.length - 1;
	int mid = 0;
	while(left <= right) {
		// mid = (L + R) / 2;		// mid = L + (R -L) / 2 	万一 L与R 分别是 10亿和20亿, 会出错
		mid = left + ((right - left) >> 1);	
		if (sortedArr[mid] == num) {
			return true;
		} else if (sortedArr[mid] > num) {
			right = mid - 1;
		} else if (sortedArr[mid] < num) {
			left = mid + 1;
		}
	}
	return sortedArr[mid] == num;
}

1.4 位运算符 

位运算的实现是比加减乘除快的多的

  • a左移一位,意味着a * 2
a << 1;
  • a右移一位,意味着a / 2 
a >> 1;

下面两个代码是等价的,但是位运算要更快 

a << 1 | 1;
a * 2 + 1;

1.5 异或(^)

1.5.1 不申请额外存储空间,交换两个变量

public void Swap(int a, int b) {
	a = a ^ b;
	b = a ^ b;
	a = a ^ b;
}

1.5.2 一个数组中有一种数出现了奇数次,其他数都出现了偶数次,找到并打印这种数

public int FindOdd(int[] arr) {
	int res = 0;
	for (int i = 0; i < arr.length; i++) {
		res ^= arr[i];
	}
	return res;
}

1.5.3 一个int类型的数,提取出其二进制最右侧的1

public int ExtractRightOne(int x) {
	return x & (~x + 1);
}

1.5.4 一个数组有两种数出现了奇数次,其他数都出现了偶数次,找到并打印这两种数

public int[] FindTwoOdd(int[] arr) {
	int xor = 0;
	for (int i = 0; i < arr.length; i++) {
		xor ^= arr[i];
	}
		
	int rightOne = xor & (~xor + 1);	// 提取出最右侧的1
	int xor2 = 0;						// xor'
	for (int i = 0; i < arr.length; i++) {
		if ((arr[i] & rightOne) == 0) {
			xor2 ^= arr[i];
		}
	}

	int[] res = new int[] {xor2, (xor ^ xor2)};
	return res;
}

1.5.5 计算一个数,二进制格式下1的个数

public int Bit1Counts(int a) {
	int count = 0;
	int temp = a;
	while (temp != 0) {
		int rightOne = temp & (~temp + 1);
		count++;
		temp ^= rightOne;
	}
	return count;
}

1.6 递归函数的时间复杂度(master公式)

递归的子过程是等规模的,每次递归的子过程会把递归分为b个过程,调用a个过程,时间复杂度即 O(a\cdot T(\frac{N}{b}))

剩余部分则是 O(N^{d})

T(N) = a\cdot T(\frac{N}{b}) + O(N^{d})

  1. 若 log_{b}(a) > d,则时间复杂度为 O(log_{b}(a))
  2. 若 log_{b}(a) = d,则时间复杂度为 O(N^{d} \cdot logN)
  3. 若 log_{b}(a) < d,则时间复杂度为 O(N^{d})

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值