代码随想录算法训练营Day1|数组理论基础、704 二分查找、27 移除元素

一、数组理论基础

来自:https://programmercarl.com/%E6%95%B0%E7%BB%84%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html#%E6%95%B0%E7%BB%84%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80

js当中数组的定义: var array = [1, 2, 3, 4, 5]; (字面量定义)

或者: var array = new Array(5);

JavaScript当中没有指针,内存相关的操作向程序员隐藏。

二、704 二分查找

  1. 题目链接:https://leetcode.cn/problems/binary-search/

  1. 思路:暴力解法、二分查找

  1. 暴力解法

从前往后找,遇到相等了的就返回,并跳出循环,时间复杂度O(n)

var search = function(nums, target) {
    var result = -1;
    for (let i=0; i<nums.length; i++) {
        if (nums[i] == target) {
            result = i;
            break;
        }
    }
    return result;
};

  1. 二分查找

我认为二分查找就是一个另类的双指针法,一开始的时候双指针分别是第一个和最后一个元素,然后慢慢地缩小范围。但需要注意的是,这道题的前提是数组有序,因此可以使用二分法。熟练使用左闭右开即可。时间复杂度为O(logn)

var search = function(nums, target) {
    var left = 0;
    var right = nums.length;   // 因为左闭右开,所以right是nums.length,也避免了只有一个元素的问题
    var mid = Math.floor((left + right) / 2);
    while (left < right) {    // 此处小于号就可以
        if (nums[mid] == target) {
            return mid;
        }
        else if (nums[mid] < target) {
            left = mid + 1;    // 因为这个时候mid肯定不会在区间里面了,所以left加一
        }
        else {
            right = mid;
        }
        mid = Math.floor((left + right) / 2);
    }
    return -1;
};

左闭右开需要注意的灵魂有三个:第一个是开始定义的时候,由于是左闭右开,所以right指针定义为数组上限+1;第二个是while的判断条件为小于号,因为左闭右开的区间中左右指针相等是没有意义的;第三个是已经判断了mid指针与target不相等的情况下,right可以直接赋值mid,因为是开区间,下一次循环不会比较nums[mid]。left则要赋值mid+1。

  1. 35 搜索插入位置

题目链接:https://leetcode.cn/problems/search-insert-position/

这题用左闭右闭写一下:

var searchInsert = function(nums, target) {
    var left = 0;
    var right = nums.length - 1;
    var mid = Math.floor((right + left)/2);
    while (left <= right) {
        if (nums[mid] == target) {
            return mid;
        }
        else if (nums[mid] < target) {
            left = mid + 1;
        }
        else {
            right = mid -1;
        }
        mid = Math.floor((right + left)/2);
    }
    return left;
};

  1. 34 在排序数组中查找元素的第一个和最后一个位置

题目链接:https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/

思路一:首先使用二分法找到任意一个等于target的索引,然后在附近查找(还是记住这个吧,下面那个有点难记)

var searchRange = function(nums, target) {
    var left = 0, right = nums.length - 1;
    var result = [-1, -1];
    while (left<=right){
        var mid = left + Math.floor((right - left)/2);
        if (nums[mid] < target) {
            left = mid + 1;
        }
        else if (nums[mid] > target) {
            right = mid - 1;
        }
        else {
           result[0] = result[1] = mid;
           break;
        }
    }
    if (result[0] == -1 && result[1] == -1) {
        return result;
    }
    while (nums[result[0]] == target) {
        result[0] -= 1;
    }
    while (nums[result[1]] == target) {
        result[1] += 1;
    }
    return [result[0] + 1, result[1] - 1];
};

思路二:使用两次二分法,要找的左侧索引相当于是第一个大于等于target的值,右侧索引是第一个大于target的值减去一。

const binarySearch = (nums, target, lower) => {
    // lower是为了增强代码复用性,如果是lower是true的话则是判断大于等于
    let left = 0, right = nums.length - 1, ans = nums.length;
    while (left <= right) {
        const mid = Math.floor((left + right) / 2);
        if (nums[mid] > target || (lower && nums[mid] >= target)) {
            right = mid - 1;
            ans = mid;
        } else {
            left = mid + 1;
        }
    }
    return ans;
}

var searchRange = function(nums, target) {
    let ans = [-1, -1];
    const leftIdx = binarySearch(nums, target, true);
    const rightIdx = binarySearch(nums, target, false) - 1;
    if (leftIdx <= rightIdx && rightIdx < nums.length && nums[leftIdx] === target && nums[rightIdx] === target) {
        ans = [leftIdx, rightIdx];
    } 
    return ans;
};

  1. 69 x的平方根

题目链接:https://leetcode.cn/problems/sqrtx/

var mySqrt = function(x) {
    if (x == 1) {
        return 1;
    }
    // 平方根一定小于这个值的一半
    var left = 1;
    var right = x/2 + 1;
    while (left < right) {
        var mid = Math.floor((left + right) / 2);
        if (mid * mid == x) {
            return mid;
        }
        else if (mid * mid < x) {
            left = mid + 1;
        }
        else {
            right = mid;
        }
    }
    return right - 1;
};

  1. 367 有效的完全平方数

题目链接:https://leetcode.cn/problems/valid-perfect-square/

var isPerfectSquare = function(num) {
    if (num == 1) {
        return true;
    }
    var left = 0, right = Math.floor(num/2);
    while (left <= right) {
        var mid = left + Math.floor((right - left)/2);
        // 加入常量计算mid的平方,减少冗余计算,提高效率
        const square = mid * mid;
        if (square > num) {
            right = mid - 1;
        }
        else if (square < num) {
            left = mid + 1;
        }
        else {
            return true;
        }
    }
    return false;
};

三、27 移除元素

  1. 题目链接:https://leetcode.cn/problems/remove-element/

  1. 思路:快慢指针法,时间复杂度为O(n)

  1. 代码

var removeElement = function(nums, val) {
    var slow = 0;
    var fast = 0;
    while (fast < nums.length) {
        // 与val不相同的数值才会被赋给slow指针
        if (nums[fast] !== val) {
            nums[slow] = nums[fast];
            slow++;
        }
        fast++;
    }
    return slow;
};

  1. 26 删除排序数组中的重复项

题目链接:https://leetcode.cn/problems/remove-duplicates-from-sorted-array/

代码:

var removeDuplicates = function(nums) {
    if (nums.length == 0) {
        return 0;
    }
    var fast = 1;
    var slow = 0;
    while (fast < nums.length) {
        if (nums[fast-1] < nums[fast]) {
            slow++;
            nums[slow] = nums[fast];
        }
        fast++;
    }
    return slow + 1;
};

  1. 283 移动零

题目链接:https://leetcode.cn/problems/move-zeroes/

使用双指针,左指针指向当前已经处理好的序列的尾部,右指针指向待处理序列的头部。

右指针不断向右移动,每次右指针指向非零数,则将左右指针对应的数交换,同时左指针右移。

左指针左边均为非零数;右指针左边直到左指针处均为零。

代码:

var moveZeroes = function(nums) {
    var fast = 0;    
    var slow = 0;    // slow代表当前所有不为0的序列总和,可以看作是一个计数器而已
    var temp = 0;
    while (fast < nums.length) {
        if (nums[fast] != 0) {
            // 当fast探测到不为0的数值时,就与当前的交换
            temp = nums[slow];
            nums[slow] = nums[fast];
            nums[fast] = temp;
            // slow左边是已经处理过的序列,每次fast找到一个,slow就应该加一
            slow++;
        }
        fast++;
    }
    return nums;
};

  1. 844 比较含退格的字符串

题目链接:https://leetcode.cn/problems/backspace-string-compare/

思路:暴力解法、双指针法

暴力解法:我第一次写的,不知道是个啥东西。。。

var backspaceCompare = function(s, t) {
    var i = s.length - 1, j = t.length - 1;
    var skipS = 0, skipT = 0;
    // 大循环
    while (i >= 0 || j >= 0) {
        // S循环
        while (i >= 0) {
            if (s[i] == "#") {
                skipS += 1;
                i--;
            }
            else if (s[i] != "#" && skipS != 0) {
                skipS--;
                i--;
            }
            else {
                break;
            }
        }
        // T循环
        while (j >= 0) {
            if (t[j] == "#") {
                skipT += 1;
                j--;
            }
            else if (t[j] != "#" && skipT != 0) {
                skipT--;
                j--;
            }
            else {
                break;
            }
        }
        // 如果不相等,输出false
        if (s[i] !== t[j]) {
            return false;
        }
        i--;
        j--;
    }
    return true;
};

双指针法:

这里没什么需要注意的,但是我在数组的slice方法那里卡了很久,用了splice没发现自己错了。。。还是需要再熟悉熟悉

var backspaceCompare = function(s, t) {
    var rewriteString = function(string) {
        var array = string.split('');
        var slow = 0;
        var fast = 0;
        while (fast < array.length) {
            if (array[fast] != '#') {
                array[slow] = array[fast];
                slow++;
            }
            else {
                slow = slow ? slow-1 : 0;
            }
            fast++;
        }
        rewrite = array.slice(0, slow).join('');
        return rewrite;
    }
    return rewriteString(s) == rewriteString(t);
};

  1. 977 有序数组的平方

题目链接:https://leetcode.cn/problems/backspace-string-compare/

思路:我们新建一个长度和原来相等的数组,然后在原数组上利用左右指针,不断比较,当中较大的一个填入结果数组的末尾,如果left指针对应的值填进去了,那么left++,反之right--。

var sortedSquares = function(nums) {
    var result = [];
    var left = 0, right = nums.length - 1;
    var k = nums.length - 1;
    // 这里是小于等于,不然的话如果i和j相等,就跳出循环,则当前的元素就被落下了
    while (left <= right) {
        numLeft = nums[left] * nums[left];
        numRight = nums[right] * nums[right];
        if (numLeft >= numRight) {
            result[k--] = numLeft;
            left++;
        }
        else {
            result[k--] = numRight;
            right--;
        }
    }
    return result;
};

四、总结

今日学习时间:2.5h左右

其实这一部分题目我之前是跟卡哥做过一遍的,但现在看来忘得也都差不多了,果然还是得多次刷,多次复习,在我看来二分法就是另一种形式的双指针法,其中的原理是相通的。这一次算是二刷,题目都有思路但不一定能一次写对,下一次刷的时候需要重点注意的题目有:34/283/977。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
第二十二天的算法训练主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组中找到长度最小的子数组
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值