算法与数据结构P2

左神一周刷爆力扣视频P2

评估算法的核心指标:

  • 时间复杂度(流程决定)
  • 额外空间复杂度(流程决定)
  • 常数时间项(实现细节决定)

何为常数时间的操作?

  • 如果一个操作的执行时间不以具体样本量为转移,每次执行时间都是固定时间。称这样的操作为常数时间的操作。

常见的常数时间的操作:

  • 常见的算术运算(+,-,*,/,%)
  • 常见的位运算(>>(带符号右移), >>>(不带符号右移), <<, |, &, ^等)
  • 赋值,比较,自增,自减操作等
  • 数组寻址操作

选择排序

  • 一个有N个数的数组,先在0—N-1位置上找到最小值在哪,然后把这个最小值和0位置的数交换,全局最小值就来到了0位置,也就是排完序后最小值在最左边。
  • 在1—N-1位置上找这个范围内的最小值,找到之后把这个数和1位置上的数交换,则也就是找到了全局第二小的数。
  • 在2—N-1的范围上找最小值,找到之后把这个数和2位置上的数交换。
  • 以此类推。。。

这个流程发生了多少次常数操作:要分析一下究竟干了什么,

  • 首先0—N-1位置上每个数都要看一下(数组寻址),看到时候要和之前找到的最小值作比较,每一个位置就是一次 “看+比”,执行完“看+比”之后,单独做一次交换操作。
  • 1—N-1上同样是这样的事,执行“看+比”,然后只执行一次交换。
  • 以此类推。。。

一共发生了多少次常数级别的操作:
第一次:N* (看+比)+1(就是N次 “看+比”,和一次的交换)
第二次:N-1*(看+比)+1
第三次:N-2*(看+比)+1
。。。。
其中,“看”这个操作,就是每个数只发生一次,“比”也是每个数只发生一次,
也就是
第一次:N* (2)+1
第二次:N-1*(2)+1
第三次:N-2*(2)+1
。。。
将上面都加起来就是
2*(N+N-1+N-2+…1)+N
前面是2乘以一个等差数列(有等差数列,时间复杂度就是O(N^2))

python
arr = [5, 4, 6, 3, 2, 1]

for i in range(len(arr)):
    minindex = i
    for j in range(i+1, len(arr)):   # i~N-1上找最小值下标
        if arr[minindex]>arr[j]:
            minindex = j
    arr[minindex],arr[i] = arr[i],arr[minindex]  # 让最左边的数和最小值交换
for i in arr:
    print(i)
c++
// 0~N-1
// 1~N-1
//...
for(int i = 0; i<arr.length;i++)
{
	int minindex = i;
	for(int j = i+1; j<arr.length; j++) // i ~ N-1 上找最小值下标
	{
		minindex = arr[j] < arr[minindex] ? j : minindex;
	}
	swap(arr,i,minindex);
}

如何确定算法流程的总操作数与样本数量之间的表达式关系?

  • 想象该算法流程所处理的数据状况,按最差情况来
  • 把整个流程彻底拆分为一个个基本动作,保证每个动作都是常数时间的操作
  • 如果数据量为N,看看基本动作的数量和N是什么关系

如何确定算法流程的时间复杂度?

  • 当完成了表达式的建立,只要把最高阶项留下即可,低阶项都去掉,高阶项的系数也去掉。记为O(忽略系数的高阶项)

冒泡排序

例如对 5 4 6 3 2 1排序,
从下标0~5开始

  • 0~1下标的数,谁大谁在后面,5比4大,5在后面,交换,此时是4 5 6 3 2 1
  • 1~2下标数,谁大谁在后面,6大,不用交换,4 5 6 3 2 1
  • 2~3下标数,谁大谁在后面,6和3交换,此时是4 5 3 6 2 1
  • 3~4下标数谁大谁在后面,6和2交换,4 5 3 2 6 1
  • 4~5下标数谁大谁在后面,6和1交换,4 5 3 2 1 6

到此,最大值已找到,接下来在0~4范围内重复这件事
。。。
第二大值找到,在0~3范围内重复这件事
以此类推。。。

常数级别的操作是:
0~5下标数操作时,N-1对儿比较之后决定交换还是不交换
0~4下标数操作时,N-2对儿比较之后决定交换还是不交换
0~3下标数操作时,N-3对儿比较之后决定交换还是不交换
。。。
N-1 + N-2 + N-3 + …等差数列,时间复杂度O(N^2)

arr = [5, 4, 6, 3, 2, 1]
# 0~N-1下标上的数比较
# 0~N-2
# 0~N-3
# ...
for i in range(1,len(arr)): # 共N-1 次
    # 0 和 1 之间,谁大谁换
    # 1 和 2 之间,谁大谁换
    # 。。。以此类推
    for j in range(0,len(arr)-i):  # 0~N-1之间,谁大谁换,,0~N-2之间,谁大谁换,,,,
        if arr[j] > arr[j+1]:
            arr[j],arr[j+1] = arr[j+1],arr[j]
for i in arr:
    print(i)
# 0~N-1下标上的数比较
# 0~N-2
# 0~N-3
# ...
for(int e = arr.length-1; e>0; e--)  // 0~e
{
	for(int i = 0; i<e; i++)
	{
		if(arr[i] > arr[i+1])
		{
			swap(arr,i,i+1);
		}
	}
}

插入排序
例如:0~5下标对应数组:
5 4 3 4 3 2

  • 0~0范围有序,自然有序,
  • 0~1范围有序,1上的数往前看,比前面的小的话就与前者交换,4和5交换,4 5 3 4 3 2
  • 0~2范围有序,2上的数往前看,比前面的小的话就与前者交换,直到不再比左边的数小,或者左边没数了,停止,3 4 5 4 3 2
  • 0~3范围有序,3上的数往前看,3 4 4 5 3 2
  • 0~4:3 3 4 4 5 2
  • 0~5:2 3 3 4 4 5

按最差情况计算时间复杂度:例如:6 5 4 3 2 1,O(N^2)

c++
for(int i = 0; i < arr,length; i++)  // 0~i做到有序
{
	for(int j = i-1; j>=0 && arr[j] > arr[j+1]; j--)
	{
		swap(arr,j, j+1);
	}
}

常见的时间复杂度:
排名从好到差
O(1)
O(logN)
O(N)
O(N*logN)
O(N^2), O(N^3)… O(N^K)
O(2^N), O(3^N),… O(k^N)
O(N!)

二分法

时间复杂度O(logN)

int L = 0int R = arr.lenght-1;
int mid = 0;
while(L<R)
{
	mid = L + ((R - L) >> 1); // (R+L)/2
	if(arr[mid] == num)
	{
		return true;
	}
	else if(arr[mid]>num)
	{
		R = mid -1;
	}
	else
	{
		L = mid + 1;
	}
}

return arr[L] == num;

认识异或运算
异或运算:相同为0,不同为1
异或运算就记成无进位相加
性质:

a^a == 0
0^a == a
异或运算满足交换律和结合律

不需要创建新变量的数据交换:

a = a^b
b = a^b
a = a^b
三行代码交换a和b,但不建议这么用
用:
tmp = a;
a = b;
b = tmp;

异或练习

  1. 一个数组中只有一种数出现了奇数次,其他都出现偶数次,怎么找到并打印这个数
    链接: 力扣原题.

  2. 怎么把一个int类型的数,提取最右侧的1来
    A = A&(~A+1)

  3. 一个数组中只有两种数出现了奇数次,其他都出现偶数次,怎么找到并打印这个数
    链接: 力扣原题.

  4. 一个数二进制中1的个数
    链接: 力扣原题.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一记绝尘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值