二分
查找
由于后面的题都使用二分,就没有给代码写注释
/**
* @Author xiangcheng
* @Date 2021/8/17 15:58
* @Version 1.0
*/
public class exer {
public static void main(String[] args) {
int[] arr = {2,4,5,8,11,32,34,53,67};
System.out.println(mer1(arr,5,0,arr.length - 1));
}
private static int mer(int[] arr,int num){
if (arr == null || arr.length == 0){
return -1;
}
int re = -1;
int l = 0,r = arr.length - 1;
int mid;
while (l <= r){
mid = l + (r - l)/2;
if (arr[mid] < num){
l = mid + 1;
}else if (arr[mid] > num){
r = mid - 1;
}else {
re = mid;
break;
}
}
return re;
}
private static int mer1(int[] arr,int num,int l,int r){
int re = -1,mid;
if (l <= r){
mid = l + (r - l)/2;
if (arr[mid] > num){
re = mer1(arr,num,l,mid - 1);
}else if (arr[mid] < num){
re = mer1(arr,num,mid + 1,r);
}else {
re = l;
}
}
return re;
}
}
局部最小
-
给定一个无序数组arr,已知任意相邻的两个元素,值都不重复。请返回任意一个局部最小的位置。
-
所谓局部最小的位置是指,如果arr[0]<arr[1],
-
那么位置0就是一个局部最小的位置。
-
如果arrN-1小于arr[N-2],
-
那么位置N-1也是局部最小的位置。如果位置i既不是最左位置也不是最右位置。
-
那么只要满足arr[i]同时小于它左右两侧的值即( arr[i-1]和arr[i+1]),
-
那么位置i也是一个局部最小的位置。
package 二分.sousuo.exer_;
/**
* @Author xiangcheng
* @Date 2021/8/18 9:18
* @Version 1.0
*/
/**
* 给定一个无序数组arr,已知任意相邻的两个元素,值都不重复。请返回任意一个局部最小的位置。
* 所谓局部最小的位置是指,如果arr[0]<arr[1],
* 那么位置0就是一个局部最小的位置。
* 如果arr[N-1](也就是arr最右的数)小于arr[N-2],
* 那么位置N-1也是局部最小的位置。如果位置i既不是最左位置也不是最右位置。
* 那么只要满足arr[i]同时小于它左右两侧的值即( arr[i-1]和arr[i+1]),
* 那么位置i也是一个局部最小的位置。
*/
public class LocalMinimum {
public static void main(String[] args) {
int[] arr = {11,8,7,2,3,4,5,6,8,9,19};//测试用例
System.out.println(findMin(arr));
}
private static int findMin(int[] arr){
//当数据少于两个那么直接返回-1 ,因为不存在局部最小
if (arr == null || arr.length <= 2){
return -1;
}
//考虑最左位置
if (arr[0] < arr[1]){
return 0;
}
//考虑最右位置
if (arr[arr.length - 1] < arr[arr.length -2]){
return arr.length - 1;
}
//在中间位置查找
return Find1(arr,0,arr.length - 1);
}
/**
* 使用循环查找
* @param arr 查找的数组
* @return 如果返回-1那么就为没有局部最小
*/
private static int Find(int[] arr){
//上一个方法已经排除了特殊情况,那么直接查找即可
//l标记数组头,r标记数组尾
int l = 0,r = arr.length - 1;
//mid标记中间,一开始可以不用赋初值。re作为放回值
int mid = 0,re = -1;
while (l < r){
//这里做mid的赋值
mid = l + ((r - l)>>1);
//当中间这个数比左右任意一个数大,那么就在另一部分查找,大于哪边就去哪边查找
if (arr[mid] > arr[mid + 1]){
l = mid + 1;
}else if (arr[mid] > arr[mid - 1]){
r = mid - 1;
}else{
//当排除了前两种情况剩下的就为局部最小,也为re赋值,作为返回
re = mid;
break;
}
}
return re;
}
/**
* 使用递归做法
* @param arr 查找的数组
* @param l 左边的边界值
* @param r 右边的边界值
* @return 如果返回-1即为没有局部最小
*/
private static int Find1(int[] arr,int l,int r){
//mid标记中间
int mid = l + ((r - l)>>1);
//re作为返回值
int re = -1;
//因为局部最小不可能用一个数来比较,所以只需要递归到 l < r
if (l < r){
//当中间这个数比左右任意一个数大,那么就在另一部分查找,大于哪边就去哪边查找
if (arr[mid] > arr[mid - 1]){
re = Find1(arr,l,mid - 1);
}else if (arr[mid] > arr[mid + 1]){
re = Find1(arr,mid + 1,r);
}else {
re = mid;
}
}
return re;
}
}
查找一个数出现在数组中最左的位置
(最右与这个思路相同)
/**
* @Author xiangcheng
* @Date 2021/8/17 16:28
* @Version 1.0
*/
/*
查找一个数出现在数组中最左的位置
(最右与这个思路相同)
*/
public class Test01 {
public static void main(String[] args) {
int[] arr = {1,2,4,4,4,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,8,11,32,34,53,67};
// System.out.println(mer1(arr,5,0,arr.length - 1));
System.out.println(mer2(arr,5));
}
//定义一个全局变量来记录查找数出现的位置,每查到一次就更新
private static int re = -1;
/**
* 使用递归
* @param arr 查找的数组
* @param num 查找的数
* @param l 标记最左边的位置
* @param r 编辑最右边的位置
* @return 最后一次查找到的位置,-1即为没找到
*/
private static int mer1(int[] arr,int num,int l,int r){
//定义mid标记中间位置
int mid;
//此时需要查到每个数上所以需要加=
if (l <= r){
mid = l + (r - l)/2;
if (arr[mid] > num){
mer1(arr,num,l,mid - 1);
}else if (arr[mid] < num){
mer1(arr,num,mid + 1,r);
}else {
//只有查找到才给mid更新位置
re = mid;
//继续向左边查找
mer1(arr,num,l,r - 1);
}
}
return re;
}
/**
* 使用循环实现
* @param arr 待查找的数组
* @param num 查找的数
* @return
*/
private static int mer2(int[] arr,int num){
//排除边界值
if (arr == null|| arr.length == 0){
return -1;
}
//l标记数组最左,r标记数组最右
int l = 0,r = arr.length - 1;
//mid标记中间
int mid;
//与递归的思路相同
while (l <= r){
mid = l + ((r - l)>>1);
if (arr[mid] > num){
r = mid - 1;
}else if (arr[mid] < num){
l = mid + 1;
}else {
re = mid;
r = mid - 1;
}
}
return re;
}
}
旋转数组
给定一个有序循环数组arr,返回arr中的最小值。
有序循环数组是指,有序数组左边任意长度的部分放到右边去,右边的部分拿到左边来。
比如数组[1,2,3,3,4],是有序循环数组,[4,1,2,3,3]也是。
emmm使用递归的方法我写出来的有问题,有待后续补充
/**
* @Author xiangcheng
* @Date 2021/8/18 10:22
* @Version 1.0
*/
/*
给定一个有序循环数组arr,返回arr中的最小值。
有序循环数组是指,有序数组左边任意长度的部分放到右边去,右边的部分拿到左边来。
比如数组[1,2,3,3,4],是有序循环数组,[4,1,2,3,3]也是。
*/
public class LoopArray {
public static void main(String[] args) {
int[] arr = {3,4,5,1,2,2,2,2,2,2};
System.out.println(FindMin(arr));
}
/**
* 对数组进行校验
* @param arr 待查找的数组
* @return 返回这个最小值
*/
private static int FindMin(int[] arr){
if (arr == null || arr.length == 0){
return -1;
}
if (arr.length == 1){
return 0;
}
return Find(arr);
}
/**
* 使用循环进行查找
* @param arr 待查找的数组
* @return 最小值所在位置
*/
private static int Find(int[] arr){
int l = 0,r = arr.length - 1;
int mid,re = -1;
while (l <= r){
mid = l + ((r - l)>>1);
//当左边小于最右边,那么就说明在l到r这一段为有序
if (arr[l] < arr[r]){
re = l;
break;
}
//当l与r之间距离为1,并且 arr[l] > arr[r] 就说明arr[r]就是这个最小值
if (r - l== 1 && arr[l] > arr[r]){
re = r;
break;
}
//排除前两种情况,剩下就是经过旋转过的
//当arr[mid] > arr[r]时,即中间位置的值大于末尾处的值,说明在mid-r中间经过了旋转
if (arr[mid] > arr[r]){
l = mid;
}else if (arr[mid] < arr[l]){
r = mid;
} else {//排除了前两种情况,就说明左右两边的值与中间的值相等,那么最小值在两边出现的可能性都有,就只能循环查找
re = arr[l];
for (int i = l;i < r;i++){
re = Math.min(re,arr[i]);
}
break;
}
}
return re;
}
}
找arr[]==i
给定一个有序数组arr,其中不含有重复元素,请找到满足arr[]==i条件的最左的位置。如果所有位置上的数都不满足条件,返回-1。
/**
* @Author xiangcheng
* @Date 2021/8/18 11:22
* @Version 1.0
*/
/*
给定一个有序数组arr,其中不含有重复元素,
请找到满足arr[]==i条件的最左的位置。
如果所有位置上的数都不满足条件,返回-1。
*/
public class FindOf {
public static void main(String[] args) {
int[] arr = {-1,0,2,4,5,6,7};
// int[] arr = {0,1,2,4,5,6,7};
System.out.println(FindTo(arr));
}
/**
* 对数组进行校验
* @param arr 待查找的数组
* @return 返回arr[i]==i
*/
private static int FindTo(int[] arr){
if (arr == null || arr.length == 0){
return -1;
}
if (arr.length == 1){
return 0;
}
// return Find0(arr);
return Find1(arr,0,arr.length - 1);
}
//记录最后一次出现满足条件的位置,用于返回
private static int m = -1;
/**
* 使用循环进行查找
* @param arr 待查找的数组
* @return arr[i] = i 所在位置
*/
private static int Find0(int[] arr){
//l标记数组头,r标记数组尾
int l = 0,r = arr.length - 1;
//mid标记中间,一开始可以不用赋初值
int mid;
while (l <= r){
//这里做mid的赋值
mid = l + ((r - l)>>1);
//当中间这个值大于他的索引,就说明左边可能存在
if (arr[mid] > mid){
r = mid;
//当中间这个值小于他的索引,就说明右边可能存在
}else if (arr[mid] < mid){
l = mid;
}else {
//剩下就是满足条件的
m = mid;
//再进行循环找到最左的位置
r = mid - 1;
}
}
return m;
}
private static int Find1(int[] arr,int l,int r){
if (l <= r){
int mid = l + ((r - l)>>1);
//当中间这个值大于他的索引,就说明左边可能存在
if (arr[mid] > mid){
Find1(arr,l,mid - 1);
//当中间这个值小于他的索引,就说明右边可能存在
}else if (arr[mid] < mid){
Find1(arr,mid + 1,r);
}else {
//剩下就是满足条件的
m = mid;
//再进行循环找到最左的位置
Find1(arr,l,r - 1);
}
}
return m;
}
}