前言
一、消失的数字
我们先来看看题目:
题目很简短。测试用例也给的很少,但是题意明了;
先说说我们最容易想到的方法;
法一:
我们通过题目知道数组是从0->n的连续的数字,在数组中仅仅是缺少一项;既然是这样那我们能不能把0~n的数字求和呢?求和加起来然后再减去数组求和加起来的值是不是就是就是缺失的那一个值?
既然方法论有了,我们来大概算一下它的时间复杂度和空间复杂度,看看有没有价值;
(不理解时间复杂度和空间复杂度的同学,可以看看我以往的博客:数据结构之时间复杂度与空间复杂度)
时间复杂度:我们既然要求和就肯定要遍历数组,算上其它常数次基本操作,时间复杂度就是O(N)
空间复杂度:我们只开辟了常数个额外空间,所以空间复杂度就是O(1);
时间复杂度和空间复杂度看上去还行,值得一写;
法二
我们在这样想想试试,我们额外开辟一块空间为numsSize+1个空间,那么这个新数组的空间的下标是不是就是0~numsSize,我们在对这一块空间做初始化,全部初始化为-1;然后呢我们再把nums数组里面的数放在新开数组所对应的下标里面,像这样tmp[0]=0,tmp[1]=1,tmp[2]=2……,就像这样一 一对应最后如果tmp里面的数据还是-1的话,就说明nums里面缺的是tmp里面数据等于-1的下标;
我们来看看时间复杂度:肯定要实现遍历,而且这几组遍历都是单独的,没有嵌套,O(N)没得跑;
空间复杂度:开辟了额外numsSize+1个空间,空间复杂度为O(N);
通过时间复杂度和空间复杂度看出来,这个方法并不是那么靠谱;
但是我们还是来实现一下他:
法三
我们在初学C语言的时候是不是学过一个叫做异或(^)的操作符,那这个操作符是不是有几个性质;
1、满足交换律
2、a^a=0;
3、a^0=a;
再举个具体的例子:
1 ^2 ^3 ^2 ^ 2 ^ 3 ^ 1^ 3^2=3;
2^ 2^ 2 ^ 3^3 ^ 3^1 ^2 ^1=3;
这是两组相同的数字,顺序不一样,但是最后异或出来的结果是一样的;
由此我我们可以产生联想;
int x=0;
让x与0~n中的每个数字先异或,再于nums中的每个数异或,最后留在x里面的值不就是缺失的值吗?
那我们来算一下时间复杂度:第一次遍历0~n个数据,O(N),再遍历一次数组O(N),加起来就是O(2N),折合一下就是O(N);
空间复杂度:只开辟了常数个额外空间,故空间复杂度为O(1);
接下来我们看看代码实现:
二、左旋转字符串
我们来看看题目:
法一:
我们看到这道题我们大多数人想到的第一方法就是暴力求解,包括博主也是😁😁😁。
那么既然暴力就暴力把,该怎么暴力呢?
题目怒视要求我们左旋k个嘛,那我们就先将每次待旋转的第一个字符先保存起来,然后呢将后面的字符依次向前面挤,等到最后一个位置留出来过后,再将我们刚才保存的字符放在这个位置,这不就完成了一次左旋嘛,我们再来套个循环,不就是左旋k个字符嘛;
现在我们来计算一下时间的复杂度:首先左旋一个字符,然后是往前挪用数据,就是O(N),然后呢总共要旋转k次,故总的时间复杂度就是O(KN);
空间复杂度:就开辟了常数个额外空间,故为O(1);
接下来我们来看看代码实现:
我们可以看到代码虽然简短,但是运行时间比较长;
法二:
既然法一时间复杂度这么高,我们能不能优化一下呢?
当然可以:有一种方法叫做三步翻转法,也就是只需要将字符串反转三步就能得到预期结果;
怎么个说法呢?
假设我们有一个字符串ABCDEF,左旋2个;
第一步:我们先将整个字符串反转一下:FEDCBA
第二步:我们将需要左旋的n个字符再旋转一下;也就是下标从0~n-1的字符串反转一下;
得到:EFDCBA
第三步:然后将n~len(字符串长度)-1区间的字符串再反转一下,是不是就得到了:EFABCD
是不是就得到了我们想要的结果;
当然以上步骤都有些重复,于是我们可以封装再一个函数里面;函数的参数我们可以这样设计
reverse_(char*p,int left,int right)//将[left,right]区间的字符串反转,调用三次函数即可得到目标;
这样的话,我们就可以得出时间复杂度为O(N),空间复杂度:我们只开辟了常数个额外空间,故空间复杂度为O(1);
接下来我们具体实现一下代码:
的确实比暴力快多了;😀😀😀
既然会了三步反转法:我们可以去试一试:倒置字符串
(完结)