承接上文选择排序、冒泡排序、异或运算
上文题目2代码
![](https://gitee.com/pingfanrenbiji/pictures/raw/master/2022-1-17/1642427991083-image.png)
定义一个eor变量
定义一个整型数组int[]
用eor变量异或数组中的每一个元素
那么eor就变成了a^b
又因为知道了该数组中有2种数出现了奇数次
所以a != b
所有eor != 0
意味着eor必有一个位置上是1
选最右侧的1
int rightOne = eor & (~eor + 1)
这一行的代码表示把某一个不等于0的数最右侧的1提取出来
![](https://gitee.com/pingfanrenbiji/pictures/raw/master/2022-1-17/1642428640596-image.png)
一个数与上这个数取反加1就是把这个数最右侧的1取出来了
onlyOne就是eor'
遍历数组,数组中的每个数和rightOne做与运算结果为0或1的
才让eor'与这个数做与运算
总之那个位置上等于1或等于0的才异或
一个数字和0000000100异或不等于0 就是等于1
那么这个数字的从右往左的第三位必须是1才不等于0
onlyOne是a或者b
另一个就是eor异或上onlyOne的结果
插入排序
定义一个数组
3,2,5,4,2,3,3,
下标是0,1,2,3,4,5,6
![](https://gitee.com/pingfanrenbiji/pictures/raw/master/2022-1-17/1642429807271-image.png)
0~0 范围是有序的 只有一个数
0~1排序 如果当前位置所在的数比左边小就交换
所以3,2交换
![](https://gitee.com/pingfanrenbiji/pictures/raw/master/2022-1-17/1642429919948-image.png)
2这个数来到了0位置 再往前看 没有数了 可以停了 此时也做到了0~1上有序了
![](https://gitee.com/pingfanrenbiji/pictures/raw/master/2022-1-17/1642430080513-image.png)
画线部分表示已经做到有序了
接下来想做到0~2范围有序
所以从2开始往前看
5的前一个数是3 不需要交换
此时就做到了0~2有序了
![](https://gitee.com/pingfanrenbiji/pictures/raw/master/2022-1-17/1642430229248-image.png)
接下来0~3范围上做到有序 从3开始往前看 4比5小 所以交换
![](https://gitee.com/pingfanrenbiji/pictures/raw/master/2022-1-17/1642430324855-image.png)
当前这个数就来到了4这个位置 再往前看
4>3 停 此时就已经做到了0~3有序了
依此类推
就像打扑克 抓了一张新牌 旧牌从右往左划到适当的位置插入进去
时间复杂度
数据状况不同会导致时间复杂度不一样
选择排序和插入排序的数据状况不影响时间复杂度
![](https://gitee.com/pingfanrenbiji/pictures/raw/master/2022-1-17/1642430974670-image.png)
这种情况是等差数列 时间复杂度是O(N^2)级别
![](https://gitee.com/pingfanrenbiji/pictures/raw/master/2022-1-17/1642431062225-image.png)
这种情况只需要往前看看 不需要排序 时间复杂度是O(N)级别
时间复杂度按最差情况来估计你的算法表现
所以说插入排序是O(N^2)的算法
插入排序代码
![](https://gitee.com/pingfanrenbiji/pictures/raw/master/2022-1-17/1642431426796-image.png)
插入排序最差是N^ 选择排序和冒泡排序严格是N^ 所以插入排序的算法性能要优于选择排序和冒泡排序
0~0范围是有序的
所以从下标1位置开始
第一个for循环 表示想在0~1范围有序,想在0~2范围有序,想在0~3范围有序...
第二个for循环
比如第i的位置 此时想让0~i位置有序
已经做到了在0~i-1范围上有序了
![](https://gitee.com/pingfanrenbiji/pictures/raw/master/2022-1-17/1642431915160-image.png)
比如第i位置上的数是Y
j=i-1
j+1就是i
j位置上的数和j+1位置上的数比较
前一个要小了 这个时候要交换
![](https://gitee.com/pingfanrenbiji/pictures/raw/master/2022-1-17/1642432046542-image.png)
交换完了之后 Y就来到了i-1位置
接下来往左移动再比较j--位置上的数即i-2
j+1位置上的数就是i-1
如果j位置上的数大于j+1位置上的数
还是往左移
j就是当前数的前一个数,当前数在j+1位置
![](https://gitee.com/pingfanrenbiji/pictures/raw/master/2022-1-17/1642432318304-image.png)
所以第二个for循环就是在做:
一直往前看如果一直都小的话就一直往前换
换到越界停或者换到不比左边位置小了停
二分法
1、在一个有序的数组中,找某个数是否存在
如果从左往右遍历找的话 就是一个O(N)的算法
每个数只碰一次
通过二分法找最快
先找到中点位置的数
![](https://gitee.com/pingfanrenbiji/pictures/raw/master/2022-1-17/1642432986371-image.png)
如果x > num
所以x右边一定不会有num
就可以在x左边继续二分
如果x < num
所以x左边是不需要看的
只在x右边二分
这个算法的时间复杂度怎么估计? 一次砍一半 一共砍几次 时间复杂度是
比如数组中一定有8个数
砍一半4个
再砍2个
再砍1个
如果还找不到就是不存在这个数
如果找到了就是砍3次就是
2、在一个有序数组中,找>=某个数最左侧的位置
![](https://gitee.com/pingfanrenbiji/pictures/raw/master/2022-1-17/1642434211431-image.png)
先找到中点位置 假设这个位置是t
![](https://gitee.com/pingfanrenbiji/pictures/raw/master/2022-1-17/1642434368737-image.png)
是否满足大于等于num
4是满足的 所以左侧有可能还存在大于等于3的数 所以继续二分
![](https://gitee.com/pingfanrenbiji/pictures/raw/master/2022-1-17/1642434425028-image.png)
假设中点是箭头所指的2
次是不满足大于等于num
所以右边去二分
![](https://gitee.com/pingfanrenbiji/pictures/raw/master/2022-1-17/1642434726921-image.png)
再找中点位置 上图箭头3
比之前记录的t的位置更靠左 所以放弃之前t的位置记 上图箭头3的位置为t
满足大与等于3 再往左分
![](https://gitee.com/pingfanrenbiji/pictures/raw/master/2022-1-17/1642434779812-image.png)
也满足 再分就没有数据可分了
二分法找一个数存不存在 当找到了 就不需要二分了
而找大于等于num最左侧的位置一定是二分到结束的 即二分到某一个范围上没有数才停
3、局部最小值问题
在数组中 无序 任何2个相邻的数不相等
局部最小定义
如果0位置上的数小于1位置上的数 那么0位置就是局部最小的位置
如果N-1位置上的数 小于 N-2位置上的数
那么N-1就是局部最小位置
如果中间位置i即比左边i-1位置小同时也比i+1位置小
那么i位置就是局部最小
i位置必须同时小于左边和右边的数 才叫局部最小
在这样一个数组中 找出一个局部最小即可
能不能求的过程 时间复杂度好于O(N)
最好二分 ?
这道题目的解析 请看下文讲解哟