题目1:704. 二分查找 - 力扣(LeetCode)
上来先暴力解题一波
class Solution {
public int search(int[] nums, int target) {
//暴力解法
for(int i=0;i<nums.length;i++)
{
if(nums[i]==target)
{
return i;
}
}
return -1;
}
}
数组长度为n的情况下时间复杂度为O(n)
之前学过算法但是一年没刷题忘记了TT
二分法:
使用注意事项:注意边界
1.左闭右闭
class Solution {
public int search(int[] nums, int target) {
//二分查找
// Arrays.sort(nums);
int i=0;
int j=nums.length-1;//因为是左右闭合 最左右均能取到
while(i<=j)//等号这种情况 当i=4 m=5 j=6 target=nums[4]时即可验证 i和j同时指向一处时仍有意义
{
int k=i+(j-i)/2;
// 当我们要计算左右边界的中间值时,如果左右边界的值非常大,相加可能会导致整数溢出。
//例如,如果 left 是一个很大的正整数,而 right 是一个接近最大整数值的负整数,它们相加可能会超出整数所能表示的范围。
//为了避免这种情况,我们使用 (right - left) / 2 来计算中间值。这样计算是安全的,因为我们先计算了两个边界值的差值,然后再除以 2。差值计算不会导致溢出,因为两个边界值的差值一定不会超过它们的和。
//通过这种方式,我们可以确保在任何情况下都能够正确计算数组的中间索引,而不会因为边界值的大小而产生错误的结果。
if(target==nums[k])
{
return k;
}
else if(target<nums[k])
{
j=k-1;
continue;
}else{
i=k+1;
continue;
}
}
return -1;
}
}
我觉得比较重要的点就是自己在纸上画一画模拟一下过程 知道判断结束后i和j应当怎么变化。以及i=j这种临界条件的判定 写注释的地方尤其要注意
左闭右闭的情况搞清楚了 左闭右开就很容易理解 有点像链表里面的链表头那个意思
class Solution {
public int search(int[] nums, int target) {
//二分查找
// Arrays.sort(nums);
int i=0;
int j=nums.length;//因为是左闭右开 最右取不到
while(i<j) //i=j时无意义
{
int k=i+(j-i)/2;
if(target==nums[k])
{
return k;
}
else if(target<nums[k])
{
j=k;
continue;
}else{
i=k+1;
continue;
}
}
return -1;
}
}
做完了看题解 发现有意思的一个小点:
(j-i)/2 可以用 (j-i)>>1代替。位运算比除法运算更高效。在许多编程语言中,位运算比除法运算要快得多。因此,使用 (j - i) >>1
可以获得更好的性能和执行速度。
相关题目:
啊啊啊真的很笨 看到题目中要插入就没细想 做错了
其实正确解法和二分法很像,只需思考没有的元素应当插入在哪:没有元素时,i>j,在此上一步一定是i=j=m,i>j时,无论是因为i=m+1(意思就是target比nums[m]大,此时i=m+1恰好指向应该插入的位置)引起的i>j,还是j=m-1(意思是target比现存nums[m]小,i恰好就是该插入的位置)引起的i>j, i的位置就是要插入的位置,实在不明白就画个图TAT。
class Solution {
public int searchInsert(int[] nums, int target) {
if(target<nums[0])
return 0;
if(target>nums[nums.length-1])
return nums.length;
int i=0;
int j=nums.length-1;
while(i<=j)
{
int m=i+((j-i)>>2);
if(target==nums[m])
{
return m;
}else if(target>nums[m])
{
i=m+1;
continue;
}else{
j=m-1;
continue;
}
}
return i;
}
}
一个小错误:(j-i)>>2要再用()括一层 因为位运算优先级小于减法运算。。
34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)
上来就是一个错误:
未考虑到数组为空的情况
自己写的:笨笨的 用的快慢指针的方法
应该可以用二分法
还没做出来。。。
原始代码:
- 最坏情况下的时间复杂度是 O(n),其中 n 是数组的长度。在最坏情况下,需要遍历整个数组,直到找到目标值或确定不存在目标值。
- 最好情况下的时间复杂度是 O(1),当数组为空或目标值不在数组范围内时,可以立即返回结果。
优化后的代码:
- 最坏情况下的时间复杂度是 O(log n),其中 n 是数组的长度。通过使用二分查找,可以将查找范围在每次迭代中减半,从而提高查找效率。
- 最好情况下的时间复杂度是 O(1),当数组为空或目标值不在数组范围内时,可以立即返回结果。
快慢指针法:非常巧妙的方法,用i对数组进行遍历,用l和f两个快慢指针指示如何移动数组元素的位置。
class Solution {
public int removeElement(int[] nums, int val) {
// 无需考虑数组中超出新长度后面的元素
int length=nums.length;
int f=0;//快指针
int l=0;//慢指针
for(int i=0;i<nums.length;i++)
{
//i想象成移动的红框 对这个数组遍历,对每一个元素都做出判定
//快慢指针就起到一个指示的作用 每次检测到一个val 快指针就要比慢指针快走一步
if(nums[i]==val)
{
f++;//有=val时,本轮循环i和f均++ 若无连着的val值,将在下一轮中用nums[f]覆盖本轮nums[l].若有连续val值,则会发生i和f继续前进,直到Nums[i]!=val
length--;//每判定一个val length-1
}else{
nums[l++]=nums[f++];//没有=val时,l,f随着i一起变化
}
}
return length;
}
}