准备春招算法第一天|704. 二分查找|27. 移除元素

83 阅读3分钟

复习数组基础知识

不同语言在数组的存储方式有所差别

在C++中二维数组存储的地址空间是连续的

20210310150641186.png

在java中二维数据存储的地址空间是不连续的

20201214111631844.png

704. 二分查找

思路

  • 暴力解法:直接for循环判断解决问题;
  • 自创写法:通过双指针寻找;
var search = function(nums, target) {
    var left = 0;
    var right= nums.length-1;
    while(left<=right){
        if(nums[left] == target){
            return left;
        }else if(nums[right] == target){
            return right;
        }else{
            left++;
            right--;
        }
    }
    return -1;
};
  • 二分法:通过判断是否在区间内简化算法;(时间复杂度:O(log n)、空间复杂度:O(1))

左闭右闭区间 [left, right]

var search = function(nums, target) {
    // right是数组最后一个数的下标,num[right]在查找范围内,是左闭右闭区间
    let mid, left = 0, right = nums.length - 1;
    // 当left=right时,由于nums[right]在查找范围内,所以要包括此情况
    while (left <= right) {
        // 位运算 + 防止大数溢出
        mid = left + ((right - left) >> 1);
        // 如果中间数大于目标值,要把中间数排除查找范围,所以右边界更新为mid-1;如果右边界更新为mid,那中间数还在下次查找范围内
        if (nums[mid] > target) {
            right = mid - 1;  // 去左面闭区间寻找
        } else if (nums[mid] < target) {
            left = mid + 1;   // 去右面闭区间寻找
        } else {
            return mid;
        }
    }
    return -1;
};

左闭右开区间 [left, right)

var search = function(nums, target) {
    // right是数组最后一个数的下标+1,nums[right]不在查找范围内,是左闭右开区间
    let mid, left = 0, right = nums.length;    
    // 当left=right时,由于nums[right]不在查找范围,所以不必包括此情况
    while (left < right) {
        // 位运算 + 防止大数溢出
        mid = left + ((right - left) >> 1);
        // 如果中间值大于目标值,中间值不应在下次查找的范围内,但中间值的前一个值应在;
        // 由于right本来就不在查找范围内,所以将右边界更新为中间值,如果更新右边界为mid-1则将中间值的前一个值也踢出了下次寻找范围
        if (nums[mid] > target) {
            right = mid;  // 去左区间寻找
        } else if (nums[mid] < target) {
            left = mid + 1;   // 去右区间寻找
        } else {
            return mid;
        }
    }
    return -1;
};

总结

使用二分法的前提是数组为有序数组,题目中强调数组中无重复元素,因为一旦有重复元素,使用二分查找法返回的元素下标可能不是唯一的,当大家看到题目描述满足上述条件的时候,需要思考是否使用二分法。

27. 移除元素

思路

  • 暴力解法:直接使用双层for循环判断解决问题;
  • 自创写法:通过双指针寻找;
var removeElement = function(nums, val) {   
    let left =0;
    let right =nums.length-1;
    while(left<=right){
        if(nums[left]==val){
            nums.splice(left,1);
            right--;
        }else if(nums[right]==val){
            nums.splice(right,1)
            right--;
        }else{
            left++;
            right--;
        }
    }
    return nums.length;
};
  • 快慢指针:通过快慢指针寻找符合条件的新数组的成员进行覆盖;(时间复杂度:O(n)、空间复杂度:O(1))
var removeElement = (nums, val) => {
    let k = 0;
    for(let i = 0;i < nums.length;i++){
        if(nums[i] != val){
            nums[k++] = nums[i]
        }
    }
    return k;
};

总结

快慢指针的应用前提是采用筛选满足条件的值以及在同一数据上进行操作,满足这样的一个条件可以采取这样的思路;