【leetcode刷题1】复杂度 简单排序算法
时间复杂度
常数操作
操作时间和数据量没关系
int a =arr[i]
是常数操作,int b=list.get(i)
不是常数操作,加减乘除位运算是常数操作。
时间复杂度是按最差情况估计
以选择排序为例
遍历第一个位置到最后一个位置,找到最小的数放第一个位置;然后遍历第二个位置到最后一个位置,找到最小的数放在第二个位置…
时间复杂度分析:
分析时间复杂度就是看包含了多少常数操作。在选择排序中,一共包含看一眼这个数,跟当前最小值比较和交换三种常数操作。
第一次遍历,看了n眼,比较了n次,交换了一次;第二次遍历,看了n-1眼,比较了了n-1次,交换了一次…
最终看了:n+n-1+n-2…+1次
比较了:n+n-1+n-2…+1次
交换了n次
求和计算常数操作的次数应该是: a x 2 + b x + c ax^2+bx+c ax2+bx+c的形式,取最高次项并去掉系数得到时间复杂度为 O ( n 2 ) O(n^2) O(n2)
额外空间复杂度
如果只需要有限几个变量就可以完成,那额外空间复杂度就是 O ( 1 ) O(1) O(1)
如果需要额外数组,还得跟原数组等规模,那额外空间复杂度就是 O ( n ) O(n) O(n)
选择排序
void simpleSort(vector<int>& nums)
{
int size=nums.size();
if (size==0 ||size==1)
return ;
for(int i=0;i<size;i++)
{
int minadd=i;
int min=nums[i];
for(int j=i;j<size;j++)
{
if(nums[j]<nums[minadd])
{
minadd=j;
}
}
min=nums[minadd];
nums[minadd]=nums[i];
nums[i]=min;
}
}
冒泡排序
基本思想:
- 基本思想: 冒泡排序,类似于水中冒泡,较大的数沉下去,较小的数慢慢冒起来,假设从小到大,即为较大的数慢慢往后排,较小的数慢慢往前排。
- 直观表达,每一趟遍历,将一个最大的数移到序列末尾。
算法描述:
比较相邻的元素,如果前一个比后一个大,交换之。
第一趟排序第1个和第2个一对,比较与交换,随后第2个和第3个一对比较交换,这样直到倒数第2个和最后1个,将最大的数移动到最后一位。
第二趟将第二大的数移动至倒数第二位
时间复杂度:与选择排序类似。
void bumbleSort(vector<int>& nums)
{
int size=nums.size();
if (size==0 ||size==1)
return ;
for(int i=0;i<size;i++)
{
for(int j=i;j<size-1;j++)
{
if(nums[j]>nums[j+1])
{
int t=nums[j+1];
nums[j+1]=nums[j];
nums[j]=t;
}
}
}
}
用异或实现两数交换
异或运算满足交换律和结合律 跟顺序没有关系
异或可以看作是无进位相加
0^任何数=任何数本身
//a和b需要是两块独立的内存
int a=12;
int b=17;
a=a^b;//a=12^17 b=17
b=a^b;//a=12^17 b=12^17^17=12^0=12
a=a^b;//a=12^17^12=17 b=12
例题
在一个int数组中
1)有一个数字出现了奇数次,其他的数字都出现了偶数次,怎么找到这个数字
2)有两个数字出现了奇数次,其他的数字都出现了偶数次,怎么找到这两个数字
要求:时间复杂度:O(n),空间复杂度O(1)
1)把所有数全异或起来
2)把所有数全异或起来得到ab,其中必有一位不为零,是a和b不一样的位,根据这一位=1或=0将数组中的数分为两类(一定是一类里面包含a另一类里面包含b),对其中的一类数进行全部异或,得到a或者b,再跟ab异或,得到,另一个。
位运算
提取二进制数最右边的1
int rightone=a&(~a+1)
插入排序
插入排序操作类似于摸牌并将其从大到小排列。每次摸到一张牌后,根据其点数插入到确切位置。
例如我们有一组数字:{5,2,4,6,1,3},我们要将这组数字从小到大进行排列。 我们从第二个数字开始,将其认为是新增加的数字,这样第二个数字只需与其左边的第一个数字比较后排好序;在第三个数字,认为前两个已经排好序的数字为手里整理好的牌,那么只需将第三个数字与前两个数字比较即可;以此类推,直到最后一个数字与前面的所有数字比较结束,插入排序完成。
① 从第一个元素开始,该元素可以认为已经被排序
② 取出下一个元素,在已经排序的元素序列中从后向前扫描
③如果该元素(已排序)大于新元素,将该元素移到下一位置
④ 重复步骤③,直到找到已排序的元素小于或者等于新元素的位置
⑤将新元素插入到该位置后
⑥ 重复步骤②~⑤
插入排序的算法流程跟原来数组的情况有关,时间复杂度按最差情况估计为 O ( n 2 ) O(n^2) O(n2)
//从小到大插入排序
void insertionSort(vector<int>& nums)
{
int size=nums.size();
if (size==0 ||size==1)
return ;
for(int i=1;i<size;i++)
{
for(int j=i-1;j>0 &&nums[j]>nums[j+1];j--)
{
int t=nums[j+1];
nums[j+1]=nums[j];
nums[j]=t;
}
}
}
二分法
1)在一个有序数组中找一个数是不是存在
2)遍历查找时间复杂度O(n),二分法时间复杂度 O ( l o g 2 N ) O(log_2N) O(log2N):一个数一次砍掉一半,几次能砍到1(8->4->2->1)
在一个有序数组中找到大于等于某个数最左侧的位置,只找一个数的话找到了就可以停止,找最左侧位置的话,就一定得二分到结束。
3)一个无序数组中,相邻数一定不相等,求一个局部最小值
首先判断首位是否是局部最小,如果不是那范围内一定存在一个局部最小值,取中点,判断是不是局部最小值,如果不是,取小于M的一侧,在这一侧必存在一个局部最小值,重复上述过程。