数据结构与算法(JAVA)--数组--二分法

数据结构与算法(JAVA)–数组–二分法

有很多图片传上来太麻烦了,所以就缺失了,有的代码很简单,可以直接看,这些 都是我整理的数组的二分法的一些题目,和别人的一些题解,希望方便大家学习,

一维数组

1、基础知识

数组是存放在连续内存空间上的相同类型数据的集合。
(1)创建:
数组存储的数据类型[] 数组名字 = new 数组存储的数据类型[长度];
数据类型[] 数组名 = new 数据类型[]{元素1,元素2,元素3…};
int[] arr = new int[]{1,2,3,4,5};
数据类型[] 数组名 = {元素1,元素2,元素3…};
int[] arr = {1,2,3,4,5};

(2)查找:数组可以方便的通过下表索引的方式获取到下表下对应的数据。
数组名[索引]
索引访问数组中的元素:
数组名[索引]=数值,为数组中的元素赋值
变量=数组名[索引],获取出数组中的元素
(3)数组的长度属性: 每个数组都具有长度,而且是固定的,Java中赋予了数组的一个属性,可以获取到数组的长度,语句为: 数组名.length ,属性length的执行结果是数组的长度,int类型结果。由次可以推断出,数组的最大索引值为数组名.length-1 。
(4)需要注意的是
数组下表都是从0开始的。
数组内存空间的地址是连续的
数组有定长特性,长度一旦指定,不可更改。

正是因为数组的在内存空间的地址是连续的,所以我们在删除或者增添元素的时候,就难免要移动其他元素的地址
(5)数组作为方法参数和返回值
一个方法可以有0、1、多个参数;但是只能有0或者1个返回值,不能有多个返回值。
如果希望一个方法当中产生了多个结果数据进行返回,怎么办?
解决方案:使用一个数组作为返回值类型即可。
数组作为方法参数传递,传递的参数是数组内存的地址

数组作为方法的返回值,返回的是数组的内存地址
(6)方法的参数类型区别

2、数组内存

Java虚拟机的内存划分

数组在内存中的存储

public static void main(String[] args) {
   
int[] arr = new int[3];
System.out.println(arr);//[I@5f150435
}

以上方法执行,输出的结果是[I@5f150435,这个是什么呢?是数组在内存中的地址。new出来的内容,都是在堆内存中存储的,而方法中的变量arr保存的是数组的地址。输出arr[0],就会输出arr保存的内存地址中数组中0索引上的元素

注意:arr = null 这行代码,意味着变量arr将不会在保存数组的内存地址,也就不允许再操作数组了,因此运行的时候会抛出NullPointerException 空指针异常

3、基础题目:

1、数组最大值

public static void main(String[] args) {
   
int[] arr = {
    5, 15, 2000, 10000, 100, 4000 };
//定义变量,保存数组中0索引的元素
int max = arr[0];
//遍历数组,取出每个元素
for (int i = 0; i < arr.length; i++) {
   
//遍历到的元素和变量max比较
//如果数组元素大于max
if (arr[i] > max) {
   
//max记录住大值
max = arr[i];
}
}
System.out.println("数组最大值是: " + max);
}

2、数组反转

实现思想:数组最远端的元素互换位置。
实现反转,就需要将数组最远端元素位置交换
定义两个变量,保存数组的最小索引和最大索引
两个索引上的元素交换位置
最小索引++,最大索引–,再次交换位置
最小索引超过了最大索引,数组反转操作结束

public static void main(String[] args) {
   
int[] arr = {
    1, 2, 3, 4, 5 };
/*
循环中定义变量min=0最小索引
max=arr.length‐1最大索引
min++,max‐‐
*/
for (int min = 0, max = arr.length ‐ 1; min <= max; min++, max‐‐) {
   
//利用第三方变量完成数组中的元素交换
int temp = arr[min];
arr[min] = arr[max];
arr[max] = temp;
}
// 反转后,遍历数组
for (int i = 0; i < arr.length; i++) {
   
System.out.println(arr[i]);
}
}

4、二分查找法

二分查找也称折半查找(Binary Search),是一种在有序数组中查找某一特定元素的搜索算法。我们可以从定义可知,运用二分搜索的前提是数组必须是有序的,这里需要注意的是,我们的输入不一定是数组,也可以是数组中某一区间的起始位置和终止位置
用二分查找,可以解决如下问题:

  1. 查找一个元素;
  2. 求大于某个元素的最小值;
  3. 求大于等于某个元素的最小值;
  4. 求小于某个元素的最大值;
  5. 求小于等于某个元素的最大值;

https://leetcode-cn.com/problems/search-insert-position/solution/yi-wen-dai-ni-gao-ding-er-fen-cha-zhao-j-69ao/

二分查找的核心思想:
二分查找的核心思想是「减而治之」,即「不断缩小问题规模」。
二分查找的两种思路(请特别留意第 2 种思路,掌握它能思路清晰地解决「力扣」上的所有二分查找问题)
思路 1:在循环体内部查找元素
while(left <= right) 这种写法表示在循环体内部直接查找元素;
退出循环的时候 left 和 right 不重合,区间 [left, right] 是空区间。
思路 2:在循环体内部排除元素(重点)
while(left < right) 这种写法表示在循环体内部排除元素;
退出循环的时候 left 和 right 重合,区间 [left, right] 只剩下成 1 个元素,这个元素 有可能 就是我们要找的元素。第 2 种思路可以归纳为「左右边界向中间走,两边夹」,这种思路在解决复杂问题的时候,可以使得思考的过程变得简单
归纳出最重要的部分:
while (left < right) 退出循环的时候有 left == right 成立,因此无需考虑返回 left 还是 right;
始终思考下一轮搜索区间是什么,如果是 [mid, right] 就对应 left = mid ,如果是 [left, mid - 1] 就对应 right = mid - 1,是保留 mid 还是 +1+1、-1−1 就在这样的思考中完成;
从一个元素什么时候不是解开始考虑下一轮搜索区间是什么 ,把区间分为 2 个部分(一个部分肯定不存在目标元素,另一个部分有可能存在目标元素),问题会变得简单很多,这是一条 非常有用 的经验;
每一轮区间被划分成 2 部分,理解 区间划分 决定中间数取法( 无需记忆,需要练习 + 理解 ),在调试的过程中理解 区间和中间数划分的配对关系:
划分 [left, mid] 与 [mid + 1, right] ,mid 被分到左边,对应 int mid = left + (right - left) / 2;;
划分 [left, mid - 1] 与 [mid, right] ,mid 被分到右边,对应 int mid = left + (right - left + 1) / 2;
要找的目标值性质简单的时候,用 while (left <= right) ,这种思路在循环体里就可以退出,并且向左走和向右走都比较好判断的时候,例如二分查找的最基本问题:「力扣」第 704 题。
相对地,如果目标值性质比较复杂,向左走和向右走比较难分析的时候,用 while (left < right) 这种两边夹的思路会使得问题变得简单很多。这一类问题通常的问法是「找左边第一个等于 target 的元素」、「找不超过 target 的最大数值」这样的「边界」的问题,「力扣」第 34 题、35 题(本题)、第 275 题、第 69 题就是这样的问题。
做题的话,一般来说都没那么好找,所以基本上用 while (left < right) 这种写法能保证写对。while (left < right) 这种思路只用思考两个区间,我觉得更简单
如果你的逻辑把区间 [left, right] 分成 [left, mid] 和 [mid + 1, right],边界设置就是 left = mid + 1 和 right = mid。反之是另外一种。逻辑和具体问题相关。
永远去思考目标元素在哪个区间里,就知道边界怎么设置了。

绝大部分写二分的题解都有写注释:下一轮搜索的区间在什么地方,我是根据这一点决定 left 和 right 该怎么写。

题目:35. 搜索插入位置

难度简单
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素
方法1 暴力方法,也挺简单

public int searchInsert(int[] nums, int target) {
   
        //四种情况
        //target<nums[i]  return 0;
        //target>nums[i]  return nums.length
        //targrt=nums[i]  return i;
        //nums[k]>target,return k
        for(int i=0;i<nums.length;i++){
   // 一旦发现大于或者等于target的num[i],
        //那么i就是我们要的结果  1 3 4情况
            if(nums[i]>=target){
   
                return i;
            }  
        }
        return nums.length;//第二种情况   // 目标值在数组所有元素之后的情况
    }

//方法2:二分法在循环体内部查找元素

 public int searchInsert(int[] nums, int target) {
   
      int left=0;
      int right=nums.length-1;
          while(left<=right){
      //定义target在左闭右闭的区间里,[left, right]
          // 当left==right,区间[left, right]依然有效 
              int mid=left
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值