【代码随想录训练营】【Day01】第一章|数组|数组理论基础|704.二分查找|27.移除元素

数组理论基础


数组是在编程中非常常见的数据存储结构,主要有以下几个特点:

  • 数组的存储地址是一片连续的空间
  • 数组中存储的元素都是相同类型的
  • 修改数组中某一元素的值时,只能覆盖(重新赋值)

更多有关数组的理论基础可查阅:《代码随想录》数组理论基础


704.二分查找

题目详细:LeetCode.704

注意:二分查找对数据样本有明确的要求,即数组中的元素是有序的,所以在今后遇到类似于“在有序的数据样本中查找某一目标数据”时,都可以优先尝试二分查找。

二分查找的关键点在于二分,顾名思义,就是在查找的过程中不断将数据集合一分为二,划分为两个区间,且仅在目标值可能存在的区间中继续进行划分,直到找到目标值或者无法继续划分(找不到目标值),其解题思路简单,类似于根据区间猜数字的小游戏。

Java解法:

class Solution {
    public int search(int[] nums, int target) {
        int l = 0, r = nums.length-1;
        while(l < r){
            int mid = l + (r-l)/ 2;
            int cur_val = nums[mid];
            if(target == cur_val){
                return mid;
            }else if(target > cur_val){
            	// 目标值大于当期mid坐标下的值,则表示目标值在比较大的区间范围内
                l = mid + 1;
            }else if(target < cur_val){
            	// 目标值小于当期mid坐标下的值,则表示目标值在较小的区间范围内
                r = mid - 1;
            }
        }
        return -1;
    }
}

27.移除元素

题目详细:LeetCode.27

注意:本题的难点在于,不能使用额外的数组空间,必须仅使用O(1) 额外空间原地修改输入的数组。

由数组理论基础可知,数组占用的是一片连续的存储空间,所以在对数组进行插入/删除元素时,可能需要向后/向前移动一部分元素。

移除元素这一题的过程,就类似于对数组中某一元素进行删除操作,虽然用额外的数组空间能够非常简单地解答,但在实际应用时,如果数据量非常庞大,则会大大增加算法的空间复杂度。

不能使用额外的数组空间的要求,不仅是为了让我们更好的理解操作数组时对地址空间的影响,也是为了在实际应用中,能够更加效率地解决问题。

  • LeetCode 提供的示例:
输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:
- 函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。
- 你不需要考虑数组中超出新长度后面的元素。
- 例如,函数返回的新长度为 2 ,即 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。

通过示例可以得知:

  • 函数只需要返回新数组的长度(即移除目标元素后的数组长度)
  • 不需要考虑数组中超出新长度后面的元素
  • 不需要考虑新数组中元素的顺序

相当于就是将需要移除的元素,全部移动到数组的尾部,然后输出记录的新的数组长度即可。

Java解法(模拟上述思路解题):

class Solution {
    public int removeElement(int[] nums, int val) {
    	// 利用双指针,方便将需要移除元素往右边挪
    	// ans则记录用于移除元素后的新长度,每移除一个元素则-1
        int l = 0, r = nums.length-1, ans = nums.length;
        while(l <= r){
            int l_val = nums[l], r_val = nums[r];
            if(r_val == val){
            	// 判断右指针当前的元素是否需要被移除,因为需要移除的元素会被放在右边,所以当遇到需要移除的元素时,只需要将右指针左移一位即可,然后继续循环,直到右指针指向不需要移除的元素。
                r--;
                ans--;
                continue; // very important !
            }else if(l_val == val){
            	// 判断左指针当前的元素是否需要被移除,因为需要移除的元素会被放在右边,所以当遇到需要移除的元素时,需要将左指针的元素与右指针的元素进行交换(上面的if已经保证右指针此时指向的元素不需要被移除),接着将右指针左移一位即可。
                nums[l] ^= nums[r];
                nums[r] ^= nums[l];
                nums[l] ^= nums[r];
                r--;
                ans--;
            }
            // 注意,当左右两个指针指向的元素都不需要被移除时,只需要移动左指针,因为右指针的主要作用是为了移除元素,而不是为了排查元素。
            l++;
        }
        return ans;
    }
}

开启护眼模式(简洁上述代码,并利用左指针来记录新数组的长度):

class Solution {
    public int removeElement(int[] nums, int val) {
        int l = 0, r = nums.length-1;
        while(l <= r){
            int l_val = nums[l], r_val = nums[r];
            if(r_val == val){
                r--;
                continue;
            }else if(l_val == val){
                nums[l] ^= nums[r];
                nums[r] ^= nums[l];
                nums[l] ^= nums[r];
                r--;
            }
            l++;
        }
        return l;
    }
}

训练营的第一天,天气晴朗,积极向上又满怀期待,希望接下来的每一天都可以坚持下去,慢慢看到蜕变的自己。

每天都用一句诗来记录此刻的心情和结尾吧:

见兔而顾犬,未为晚也;亡羊而补牢,未为迟也。

你可能感兴趣的:(算法,leetcode,代码随想录)