Leedcode刷题之数组及二分法

Leedcode-二分法

一、数组的基本知识:

数组是存放在连续内存空间上的相同类型数据的集合。数组可以方便的通过下标索引的方式获取到下标下对应的数据。如图所示,数组的存放结构:
数组结构
需要两点注意的是:数组下标都是从0开始的。数组内存空间的地址是连续的,正是因为数组的在内存空间的地址是连续的,所以我们在删除或者增添元素的时候,就难免要移动其他元素的地址。
移动数组元素如图所示:移动数组元素
我们发现删除或者添加一个元素的时候,其余元素也会随之变化。同时数组的元素是不能删的,只能覆盖。
二维数组是怎么样的 情况呢?如图所示:二维数组
那么二维数组在内存的空间地址是连续的么?不同编程语言的内存管理是不一样的,我主要看了一下java版的,像Java是没有指针的,同时也不对程序员暴露其元素的地址,寻址操作完全交给虚拟机。
所以看不到每个元素的地址情况,这里我以Java为例,也做一个实验。如下做了一个测试:
测试
这里的数值也是16进制,这不是真正的地址,而是经过处理过后的数值,也能看出地址是没有规律的,所以也谈不上是连续的。
所以Java的二维数组可能是如下排列的方式:
二维数组排列
以上内容学习链接:数组理论知识

二、算法的思想:

力扣题目链接:二分法
二分法思路:我们先来看一张图:二分法
从这张图中我们可以发现:图中的数组为有序数组,同时数组中无重复元素,因为一旦有重复元素,使用二分查找法返回的元素下标可能不是唯一的,这些都是使用二分法的 前提条件,当大家看到题目描述满足如上条件的时候,可要想一想是不是可以用二分法了。二分查找涉及的很多的边界条件,逻辑比较简单,但就是写不好。主要是因为对区间的定义没有想清楚,区间的定义就是不变量。要在二分查找的过程中,保持不变量,就是在while寻找中每一次边界的处理都要坚持根据区间的定义来操作,这就是循环不变量规则
写二分法,区间的定义一般为两种,左闭右闭即[left, right],或者左闭右开即[left, right)。

第一种写法的思路:左闭右闭[left,right],区间的定义就决定了二分法的代码应该如何写,因为定义target在[left, right]区间:(如上图所示)

  • while (left <= right) 要使用 <= ,因为left == right是有意义的,所以使用 <=
  • if (nums[middle] > target) right 要赋值为 middle - 1,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle - 1

第二种写法的思路:如果说定义 target 是在一个在左闭右开的区间里,也就是[left, right) ,那么二分法的边界处理方式则截然不同。

  • while (left < right),这里使用 < ,因为left == right在区间[left, right)是没有意义的
  • if (nums[middle] > target) right 更新为 middle,因为当前nums[middle]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为middle,即:下一个查询区间不会去比较nums[middle]
java代码实现

Leedcode中的代码在本地用java进行测试的时候,需要创建对象,然后调用方法进行测试!

package leedcode;
import java.lang.System;

public class binary {
//方法一:左右闭区间[left,right]
	
//	public int search(int[] nums, int target) {
//        // 避免当 target 小于nums[0] nums[nums.length - 1]时多次循环运算
//        if (target < nums[0] || target > nums[nums.length - 1]) {
//            return -1;
//        }
//        int left = 0, right = nums.length - 1;
//        while (left <= right) {
//            int mid = left + ((right - left) >> 1);
//            if (nums[mid] == target)
//                return mid;
//            else if (nums[mid] < target)
//                left = mid + 1;
//            else if (nums[mid] > target)
//                right = mid - 1;
//        }
//        return -1;
//    }
  
方法二:左闭右开区间:[left,right)
public int search(int[] nums, int target) {
        int left = 0, right = nums.length;  //定义target在左闭右开的区间里,即:[left, right)
        while (left < right) {   // 因为left == right的时候,在[left, right)是无效的空间,所以使用 <
            int mid = left + ((right - left) >> 1);
            if (nums[mid] == target)
                return mid;       // target 在左区间,在[left, middle)中
            else if (nums[mid] < target)
                left = mid + 1;    // target 在右区间,在[middle + 1, right)中
            else if (nums[mid] > target)
                right = mid;      // 数组中找到目标值,直接返回下标
        } 
        return -1;  // 未找到目标值
    }
	
	
  public static void main(String[] args){
      binary b=new binary();
      System.out.println(b.search(new int[]{-1,0,3,5,9,12},9));
  } 
}

三、视频可视化演示

为了更好地了解二分法程序的执行过程,我这里用了可视化网站,这个网站支持多种语言,对刷题小白很友好,这里我放上网站链接供大家学习:可视化网站
利用这个网站的可视化,我以左闭右开区间[left,right) 的方法录了一个视频供大家直观地认识程序执行过程,(背景杂音,建议静音观看):二分法可视化程序执行过程

这里给出便于自己理解画出的草图:草稿

二分法主要是区间的问题,以及小于或者大于目标值分别位于哪个区间,如何重新赋值才能变换到相应的区间,这里仅供大家捋清二分法的解题思路,我也是在草稿纸上画画写写,所以想要真的掌握二分法的算法,还需要小伙伴们自己动手多写写画画!在此,感谢看到这篇文章的你,希望对你有所帮助!加油哦 ~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值