代码随想录训练营第一天|LeetCode704二分查找,LeetCode35,LeetCode34,LeetCode27移除元素
题目链接704二分查找
思路1:二分查找基础版
/**
* 二分查找基础版
* @param a 待查找的数组
* @param target 待查找的目标值
* @return 找到则返回索引,找不到返回-1
*/
public static int binarySearch(int[] a, int target){
int i = 0, j = a.length - 1; //设置指针和初值
while (i <= j){ //i~j范围内有东西
int m = (i + j) >>> 1;
if(target < a[m]){ //目标在左边
j = m - 1;
}else if(a[m] < target){ //目标在右边
i = m + 1;
}else{ //找到了
return m;
}
}
return -1;
}
- 问题1:为什么是i <= j意味着区间内有未比较的元素,而不是i < j?
答:因为i = j它们指向的元素也会参与比较,如果是i < j的话就会漏掉最后一次比较,
导致最终找不到元素。 - 问题2:(i+j)/2有没有问题?
答:当i和j比较大时相加超过了Java中整数的表示范围就会溢出变成负数,这是因为Java中
采用补码表示法来存储整数,把最高位视为符号位,正数的二进制表示与其原码相同,而
负数的补码是其原码取反然后加1。而采用无符号右移一位的形式(i+j)>>>1也可以达到
(i+j)/2的效果,无符号右移不考虑符号位,空缺的位用0填充,这样就不会因为符号位
的影响而导致负数的右移结果仍然为负数。 - 问题3:都写成小于号有啥好处?
答:因为数组是升序排列,这样可以和人类思维习惯保持一致。
思路2:二分查找改变版
/**
* 二分查找改动版
* @param a 待查找的数组
* @param target 待查找的目标值
* @return 找到则返回索引,找不到返回-1
*/
public static int binarySearchAlternative(int[] a, int target){
int i = 0, j = a.length; //第一处
while (i < j){ //第二处
int m = (i + j) >>> 1;
if(target < a[m]){
j = m; //第三处
}else if(a[m] < target){
i = m + 1;
}else{
return m;
}
}
return -1;
}
- 第一处和第三处改动是因为只把j当作边界,其指向的一定不是查找元素,而第二处当i=j时,若查找数组内没有
的元素时,会陷入一个死循环。
题目链接35搜索插入位置
思路:要理解谁是插入位置,用二分查找基础版代码改写,找到返回 m,没找到 i 代表插入点
public int searchInsert(int[] a, int target) {
int i = 0, j = a.length - 1;
while (i <= j) {
int m = (i + j) >>> 1;
if (target < a[m]) {
j = m - 1;
} else if (a[m] < target) {
i = m + 1;
} else {
return m;
}
}
return i; // 原始 return -1
}
题目链接34在排序数组中查找元素的第一个位置和最后一个位置
思路:利用二分查找中的leftmost和rightmost方法,分别用方法找到第一个元素和最后一个元素,然后再合并
//返回重复元素中的最左侧元素
public int leftMost(int[] nums, int target){
int i = 0, j = nums.length - 1;
int candidate = -1;
while(i <= j){
int m = (i + j) >>> 1;
if(target < nums[m]){
j = m - 1;
}else if(nums[m] < target){
i = m + 1;
}else{
candidate = m;
j = m - 1;
}
}
return candidate;
}
//返回重复元素中的最右侧元素
public int rightMost(int[] nums, int target){
int i = 0, j = nums.length - 1;
int candidate = -1;
while(i <= j){
int m = (i + j) >>> 1;
if(target < nums[m]){
j = m - 1;
}else if(nums[m] < target){
i = m + 1;
}else{
candidate = m;
i = m + 1;
}
}
return candidate;
}
public int[] searchRange(int[] nums, int target) {
int x = leftMost(nums, target);
if(x == -1){
return new int[]{-1,-1};
}else{
return new int[]{x,rightMost(nums, target)};
}
}
题目链接27移除元素
思路一:暴力求解,外层循环遍历位置,如果找到与查找元素相等,再使用内层循环将查找元素之后的元素前移,覆盖掉所查找元素。
public int removeElement(int[] nums, int val) {
int length = nums.length;
for(int i = 0; i < length; i++){
if(nums[i] == val){
for(int j = i + 1; j < length; j++){
nums[j-1] = nums[j];
}
i--;
length--;
}
}
return length;
}
思路二:双指针法,定义一个fast指针,代表新数组所需要的元素,再定义一个slow指针,代表新数组下标位置,如果新数组里的元素不等于所查找的值,则将元素赋给新数组下标位置,慢指针自增。
public int removeElement(int[] nums, int val) {
// 快慢指针
int slow = 0;
for (int fast = 0; fast < nums.length; fast++) {
if (nums[fast] != val) {
nums[slow] = nums[fast];
slow++;
}
}
return slow;
}