算法训练营Day1(数组)

语言

采用的Java语言,一些分析也是用于Java,请注意。

数组理论基础

开源地址

代码随想录 (programmercarl.com)

我的认识

数组的特点是在内存空间上连续且数据类型一样。

数组知道下标,get(index)时的时间复杂度为O(1),但并不意味着它寻找某个元素是O(1),而是O(n){不采用一些算法遍历去寻找的话}

正由于数组在内存空间上是连续的,所以必须在增加、删除元素的时候,移动其他元素来保持连续

对于二维数组:

public class Test {
    public static void main(String[] args) {
        int [] x1 = {1,2,3};
        int [][] x2 = {{1, 2, 3}, {3, 4, 5}, {6, 7, 8}, {9,9,9}};
        System.out.println(x1.getClass().getName());
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        System.out.println(x2.getClass().getName());
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        System.out.println(x2[0]);
        System.out.println(x2[1]);
        System.out.println(x2[2]);
        System.out.println(x2[3]);

    }
}

结果:

[I
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
[[I
[I@378bf509
[I@5fd0d5ae
[I@2d98a335
[I@16b98e56

Java中,数组属于对象,那么可以看到上面代码中x1和x2的类名,就是数组,数组并不是基本数据类型,所以不管是一维数组还是二维数组,都是存在堆中的。

对于每个小数组(每一行),他的内存地址并没有规则,说明二维数组的每一个行的内存地址没有关系

随想录中的解释是这样的:

算法训练营Day1(数组)_第1张图片

二分查找

理论

对于二分法,最重要的就是理解两个要点:

1、用二分法的必要条件

        对于这个,二分法必须建立在数组中元素是有序且无重复元素的,这个必须保证才能用二分法

2、通过定义区间判断边界

下面围绕第二点做出论述

易错点:

1、while循环中 left < right 还是 <=right

2、

如果nums[mid] > traget ,那么我们应该更新右面的指针,right指针是rigit = middle 还是middle-1

同样,nums[mid]

 解决问题

1、明确区间

在区间搜索的时候,要确定是  [ ) 还是[ ],个人平常就喜欢使用左闭又开,因为这样计算index 的时候,很方便

比如  0 1  2  3  4  5),区间范围是[0,5),那么这6个元素,实际上存在的只有0到4这5个,

那么算长度的话,直接5-0 =5

比如  3  4  5  6 7)  7-3 =4,长度就是 4(3到6这几个数) 

2、循环不变量

while循环中,保持对刚开始定义的区间的[]  [)确定下来,每次循环都按照左闭右开的去做题,就是循环不变量,坚持后面边界处理的时候,都是左闭右开。

3、两种写法(加上了我自己理解的注释)
public class Test {
    public static void main(String[] args) {
        int [] x1 = {1,2,3};
        System.out.println(binarySearch1(x1, 2));
        System.out.println(binarySearch2(x1, 1));

    }

    /**
     * [ ]
     * @param arr
     */
    public static int binarySearch1(int [] arr,int target){
        //[] 右边界要等于最后一位
        int left = 0,right = arr.length-1;
        //left=right是因为[]的时候,合法,比如[1,1]
        while (left<=right){
            //主要两个int值相加可能越界的问题
            int mid = (left+right)/2;

            if(arr[mid]>target){
                //左闭右闭,以为arr[mid]已经确定大于target了,所以不能包含这个值,所以right要=mid-1
                right = mid - 1;
            }else if(arr[mid] < target){
                //同上,因为确定不在区间内了,所以也不能包含
                left = mid + 1;
            }else {
                return mid;
            }
        }
        //没找到
        return -1;
    }

    /**
     * [ )
     */
    public static int binarySearch2(int [] arr,int target){
        //【) right指针就可以直接等于length了。!
        int left = 0,right = arr.length;
        // [1,1) 不合法,所以不加=号
        while (left < right){
            int mid = (left+right)/2;
            if(arr[mid]>target){
                // [) arr[mid]大于target,所以可以直接等于,比如 3 大于 2 ,right = 3的话也没事,因为取不到3
                right = mid;
            }else if(arr[mid] < target){
                // 【)左面是开区间,所以确定mid的值不在目标里的话,就必须+1跳过去
                left = mid+1;
            }else {
                return mid;
            }
        }
        return -1;
    }
}

复杂度

二分查找复杂度为Logn级别的,做题的时候可以留意

做题

704二分查找

704二分查找

class Solution {
    public int search(int[] nums, int target) {
        //【) right指针就可以直接等于length了。!
        int left = 0,right = nums.length;
        // [1,1) 不合法,所以不加=号
        while (left < right){
            int mid = (left+right)/2;
            if(nums[mid]>target){
                // [) arr[mid]大于target,所以可以直接等于,比如 3 大于 2 ,right = 3的话也没事,因为取不到3
                right = mid;
            }else if(nums[mid] < target){
                // 【)左面是开区间,所以确定mid的值不在目标里的话,就必须+1跳过去
                left = mid+1;
            }else {
                return mid;
            }
        }
        return -1;
    }
}

35.搜索插入位置

35. 搜索插入位置 - 力扣(LeetCode)

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

 注意排序数组,目标值,logn,可以想到用二分查找

class Solution {
    public int searchInsert(int[] nums, int target) {
        int left = 0,right = nums.length;
        while (left < right){
            int mid = (left+right)/2;
            if(nums[mid]>target){
                right = mid;
            }else if(nums[mid] < target){
                left = mid+1;
            }else {
                return mid;
            }
        }
        // 1 5 3 6  target = 2
        // l=0,r=3, m = 1 ,( nums[mid] = 3 ) > target   --> r = mid=2;
        // l = 0, r = 2 m = 0  (nums[mid]=1) > target -->l = mid+1 = 1;
        // l = 1,r = 2. m = 1 -(nums[mid=3 > 2)------>r = mid =1
        // l = r = 1 ==插入位置
        return left;
    }
}

27.移除元素

27. 移除元素 - 力扣(LeetCode)

数组的移除不是简单的删除,因为数组是一个连续的空间,删除一个元素,需要移动后面的元素,保证数组的连续

暴力解法

暴力删除的话,比如123456 删除4  需要先找到4,然后再把后面向前移动

class Solution {
    public int removeElement(int[] nums, int val) {
        int size = nums.length;
		for(int i =0;i

快慢指针

这个也是比较好理解的,快指针遍历,满指针存放新的数组。 理解这里就可以 了

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

日后补充

今天作业完成,后续还需要做这些题

  • 34.在排序数组中查找元素的第一个和最后一个位置(opens new window)
  • 69.x 的平方根(opens new window)
  • 367.有效的完全平方数(opens new window)

 随想录34题题解,这里我有些理解不了的地方,群友可以探讨一下。

代码随想录 (programmercarl.com)

你可能感兴趣的:(算法,算法)