代码训练营 DAY1 打卡

本文由GarfieldTheOldCat原创,转载请标明dekkyandlappy-CSDN博客

Day 1 数组基础,二分查找,移除元素

前几天还在忙毕业事宜,今天才开始正式学习算法,补上第一天的打卡,希望可以尽快追上进度。

数组基础

1.数组定义:存放在连续内存空间上的相同类型的数据的集合,使用下标索引获取对应数据(从0开始) 

2.数组内存空间是连续的 -> 修改列表(删除元素,增加元素),需要移动其他元素的地址。

3.数组的元素是不能删除的,只能覆盖(如何理解?)

4.二维数组的地址是否连续与语言有关。

二分查找

基础

易错点
while(left<right) 还是 <=?
if(nums[middle]>target) right=middle 还是middle-1?

解决方法:
区间定义要明确(左闭右闭[],左闭右开[))
一开始左闭右闭->边界处理时坚持左闭右闭


原则
while:检查初始区间是否符合区间边界定义
if:避免判断过的边界值进入下一轮判断,需要结合区间开闭考虑

LC704:704. 二分查找

标准的二分查找问题。

首先采用遍历的方法尝试解决:Java代码如下,python的就懒得写了。

// 土方法:遍历下标
public int search_0(int[] nums, int target) {
    int lenth_num = nums.length;
    for (int pos = 0; pos < lenth_num; pos++) {
        int num = nums[pos];
        if (target == num) return pos;
        else if (target < num) return -1;
    }
    return -1;
}

随后试图使用二分法解决,但是第一次写的运行有问题(还没学):

    // 二分查找,但是有问题
    public int search_1(int[] nums, int target) throws Throwable {
        int lenth_num = nums.length;
        int small =0;
        int big=lenth_num-1;
        int middle=(big-small)/2;

        while(small < big){
            if (target < nums[middle]) big=middle;
            else if ( target > nums[middle]) small =middle;
            else return middle;
            middle=small+(big-small)/2;
        }

        return -1;

核查以上代码,发现问题在于完全没有注意区间的开闭,导致while和if处有误,随后更正代码:

// 二分查找 左闭右闭
    public int search_2(int[] nums, int target) throws Throwable {
        int lenth_num = nums.length;
        int small =0;
        int big=lenth_num-1;

        while(small <= big){
            Thread.sleep(500);
            int middle=(big+small)/2;
            System.out.println(small+" "+big+" "+middle);
            if (target < nums[middle]) big=middle-1;
            else if ( target > nums[middle]) small =middle+1;
            else return middle;
        }
        return -1;
    }

    // 二分查找 左闭右开
    public int search(int[] nums, int target) throws Throwable {
        int small =0;
        int big=nums.length;

        while(small<big){
            int middle=(small+big)/2;
            if (nums[middle]>target) big=middle;
            else if (nums[middle]<target) small =middle+1;
            else return middle;
        }

        return  -1;
    }

再用python完成二分查找:

    # 二分查找,左闭右闭
    def search_1(self, nums, target):
        left = 0;
        right = len(nums) - 1

        while left <= right:
            middle = int((left + right) / 2)  # 取整的影响?
            if nums[middle] > target:
                right = middle - 1
            elif nums[middle] < target:
                left = middle + 1
            else:
                return middle

        return -1

    # 二分查找,左闭右开
    def search(self, nums, target):
        left = 0;
        right = len(nums)

        while left < right:
            middle = int((left + right) / 2)  # 取整的影响?
            if nums[middle] > target:
                right = middle
            elif nums[middle] < target:
                left = middle + 1
            else:
                return middle

        return -1

另外,利用python中list的特性可以给出一种方法(当然不见得会更快):

# 直接使用python数组的特性:
    #   target in nums
    #   list.index(value):value在list中首次出现的下标
    # 作弊解法,时间复杂度 O(N),
    def search_0(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """

        if target in nums:
            return nums.index(target)
        else:
            return -1

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

第一次写的时候错误百出,按照力扣官方题解写的。

Q:什么时候向上取整,什么时候向下取整?

java版本

    // 基础二分v4
    public int[] searchRange(int[] nums, int target)throws Exception {
        // target_left
        int right=nums.length-1;
        int left=0;
        int target_left;
        int target_right = 0;

        if((target<nums[0]) || (target>nums[nums.length-1])){
            return new int[]{-1,-1};
        }

        while(left<right){
            target_left=(left+right)/2;
            if (nums[target_left]>target) right=target_left-1;
            else if(nums[target_left]<target) left=target_left+1;
            else right=target_left;
//            System.out.println(target_left);
        }
        target_left=left;
        if(nums[left]!=target){
            return new int[]{-1,-1};
        }
//        System.out.println(left);

        left=0;
        right=nums.length-1;
        while(left<right){
            target_right=(left+right+1)/2;
//            System.out.println(left+" "+target_right+" "+right);
//            Thread.sleep(500);
            if (nums[target_right]>target) right=target_right-1;
            else if(nums[target_right]<target) left=target_right+1;
            else left=target_right;
        }
//        System.out.println(left+" "+target_right+" "+right);
        target_right=left;
//        System.out.println(left+" "+target_right+" "+right);
        return new int[]{target_left,target_right};
    }

python版本

class Solution(object):
    def searchRange(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        left=0
        right=len(nums)-1
        target_left=0
        target_right=0;

        if not nums: return [-1, -1]
        if target<nums[0] or target>nums[-1]: return [-1, -1]

        while left<right:
            target_left=int((left+right)/2)
            if nums[target_left]>target:right=target_left-1
            elif nums[target_left]<target: left=target_left+1
            else: right=target_left

        if nums[left] !=target: return [-1, -1]
        target_left=left

        left=0
        right=len(nums)-1
        while left<right:
            target_right=int((left+right+1)/2)
            if nums[target_right]>target:right=target_right-1
            elif nums[target_right]<target: left=target_right+1
            else: left=target_right
        target_right=left

        return [target_left,target_right]

if __name__ == "__main__":
    s = Solution()
    print(s.searchRange([5,7,7,8,8,10],8))

LC35:35. 搜索插入位置

没什么好说的

    public int searchInsert(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        int pos;

        while (left <= right) {
            pos = (left + right) / 2;
            if (nums[pos] < target) left = pos + 1;
            else if (nums[pos] > target) right = pos - 1;
            else return pos;
        }
        System.out.println(left);
        return left;
    }

移除元素(双指针)

基础

数组中的元素在内存中是连续的,不能删除,只能覆盖(即将不需要删除的元素向前移动)

eraser操作的时间复杂度是O(N)

双指针法通过两个指针,在一次遍历中完成移除操作(此方法也可以对字符串等使用)

快指针:寻找不需要删除的元素,每次遍历+1;

慢指针:标记需要更新的下表,每更新一次+1

双指针法时间复杂度O(N),空间复杂度O(1)。

LC27: 27. 移除元素

最基本的双指针法,先按照暴力方法过一遍,对于这道题,暴力方法即为遍历数组,遇到需要删除的元素后把后面所有元素向前移动一位。

第一遍暴力解法运行结果与实际不符:

//暴力解法v1,错误
    public int removeElement_1(int[] nums, int val) {
        int k=0;

        for(int i=0;i<nums.length;i++){
            if (nums[i]==val){
                for (int p=i+1;p<nums.length;p++){
                    nums[p-1]=nums[p];
                }
                k++;
            }
        }

        return nums.length-k;
    }

检查原因,发现在每每次移动之后,控制位置的i应该倒退一位,否则被删除的元素下一位若是需要删除的元素的话会被错过。

为了解决这个问题,一种办法是i--:

public int removeElement(int[] nums, int val) {
        int size=nums.length;
        
        for(int i=0;i<size;i++){
            if (nums[i]==val){
                for (int p=i+1;p<size;p++){
                    nums[p-1]=nums[p];
                }
                i--;
                size--;
            }
        }
        
        return size;
    }

另一种办法是倒着处理:

    public int removeElement_22(int[] nums, int val) {
        int k=0;

        for(int i=nums.length-1;i>=0;i--){
            if (nums[i]==val){
                for (int p=i+1;p<nums.length;p++){
                    nums[p-1]=nums[p];
                }
                k++;
            }
        }

        return nums.length-k;
    }

当然,今天的重点是双指针法,对于这道题,明白了双指针的思路后处理起来就不难了。

java版本

public int removeElement(int[] nums, int val) {
        int fast;
        int slow=0;
        int k=0;

        for (fast=0;fast<nums.length;fast++){
            if(nums[fast]!=val){
                nums[slow]=nums[fast];
                slow++;
                k++;
            }
        }
        return k;
    }

python版本

class Solution(object):
    def removeElement(self, nums, val):
        """
        :type nums: List[int]
        :type val: int
        :rtype: int
        """
        slow=0
        k=0

        for fast in range(0,len(nums)):
            if nums[fast] != val:
                nums[slow]=nums[fast]
                slow=slow+1
                k=k+1
        return k

总结

今天主要学习了数组的基本知识和两种算法:

二分查找:时间复杂度为O(log N)

双指针:是一种原地算法。

一个原地算法(in-place algorithm)是一种使用小的,固定数量的额外之空间来转换资料的算法。当算法执行时,输入的资料通常会被要输出的部分覆盖掉。不是原地算法有时候称为非原地(not-in-place)或不得其所(out-of-place)。

疑问与TODO

疑问

3.数组的元素是不能删除的,只能覆盖(如何理解?)

Q:什么时候向上取整,什么时候向下取整?

TODO

双指针:lc26,283,844,977

  • 16
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值