1、二分算法 升序 无重复 力扣704
/**
* DAY1 704二分算法 35搜索插入位置 27移除元素
*/
public class day1 {
public static void main(String[] args) {
int[] nums = {-1,0,3,5,9,12};
//int[] nums = {12,9,5,3,0,-1};
int target = 9;
//int i = searchL(nums, target);
int i = searchLR(nums, target);
System.out.println(i);
}
//704二分算法
/**
* 数组基础知识回顾
* 使用数组的步骤 1. 声明数组并【开辟空间】 2 给数组各个元素赋值 3 使用数组
* 不进行开辟空间数组就无法使用!int[] temp = new int[5];
*/
/**
*正确解法
* 核心:用改变数组下标的方式改变遍历区间 并且要抓住题意:升序 无重复
* 左下标一定是小于或者小于等于右下标的
*/
//左闭右闭
public static int searchLR(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);
//这样设计取中间下标,就会避免数组越界的问题
//比如数组 0 1 2 3 目标值是3 第一次l=0 r=3 m=1 第二次l=2 r=3 m=2 在取数组中间下标总是靠右一些
//如果靠左一些 int mid = (left+1) + ((right - left) >> 1);
// 第一次 l=0 r=3 m=2 第二次 l=3 r=3 m=4 数组越界
if (nums[mid] == target)
return mid;
else if (nums[mid] < target)
left = mid + 1;
else if (nums[mid] > target)
right = mid - 1;
}
return -1;
}
//左闭右开
public static int searchL(int[] nums, int target){
if(target<nums[0] || target>nums[nums.length-2]){
return -1;
}
int left = 0;
//int right = nums.length -1;
int right = nums.length; //因为已经采用了开区间,不会让区间里含有nums[nums.length]
while (left < right){ //因为这个时候 left=right 搜索区间就相当于 [3,3) 没有意义
int middle = left + ((right - left)>>1);
if(target == nums[middle]){
return middle;
}else if(target > nums[middle]){
left = middle + 1;
}else if(target < nums[middle]){
right = middle; //因为排除掉mid处的值 其实就是[left,mid),[mid+1,right)
}
}
return -1;
}
/*左闭右开例子:
* 有数组[0 1 2 3 4 5] target = 1
* 第一次 l=0 r=6 m=3
* 第二次 l=0 r=m=3 m=1
* (3位置这个数其实已经比较过,可以不用再比,由于是右开区间 所以r=m 若是r=m-1 会丢失比较数据
*
*)
* */
/**
* 初始解法
* 考虑到二分 也考虑到用中间值去比较
* 但是去动原数组显然是不明智的 因为要考虑引用的替换以及新数组的长度
* 远不如变换遍历的下标轻松快捷
* 在尝试更该引用的时候要注意数组越界以及 int[] temp;这样定义数组 temp实际为 null 在调用temo[0]时就会报空指针异常
*/
/*public static int search(int[] nums, int target) {
//采用左闭右开
for(int i = nums.length/2;i nums[i]){
int[] temp = new int[i];
for(int j = 0 ; (j+i+1) < nums.length ; j++){
temp[j] = nums[j+i+1];
}
}else if(target == nums[i]){
return i;
}
}
return -1;
}*/
}
2、35 搜索插入位置
/**
* 35 搜索插入位置
*/
public class DAY1_2 {
public static void main(String[] args) {
//int[] nums = {-1,0,3,5,9,12};
int[] nums = {12,9,5,3,0,-1};
int target = -19;
int i = searchA(nums, target);
//int i = searchL(nums, target);
//int i = searchLR(nums, target);
System.out.println(i);
}
/**
* 正确解法 左闭右闭
*/
public static int searchLR(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int middle = left + ((right - left) >> 1);
if (nums[middle] == target) {
return middle;
} else if (nums[middle] > target) {
right = middle - 1;
} else if (nums[middle] < target) {
left = middle + 1;
}
}
return left; //right+1也行 因为到这一步区间就是[nums[right],nums[left]],插入值就值这两个之间
}
/**
* 左闭右开
*/
public static int searchL(int[] nums, int target) {
int left = 0;
int right = nums.length; //因为已经采用了开区间,不会让区间里含有nums[nums.length]
while (left < right){
int middle = left + ((right - left) >> 1);
if(nums[middle] == target){
return middle;
}else if(nums[middle] < target){
left = middle + 1;
}else if(nums[middle] > target){
right = middle;
}
}
//退出while的情况只会是 left = right [left,right)
//又由于 right始终是开的 也就是始终比搜索区间高出一位
return right;
}
//降序 左闭右闭
public static int search(int[] nums, int target) {
int left = 0;
int right = nums.length-1;
while (left<=right){
int middle = left + ((right - left) >> 1);
if(nums[middle] == target){
return target;
}else if(nums[middle] < target){
right = middle -1;
}else if(nums[middle] > target){
left = middle + 1;
}
}
return left;
}
//降序 左闭右开
public static int searchA(int[] nums, int target) {
int left = 0;
int right = nums.length;
while (left < right){
int middle = left + ((right - left) >> 1);
if(nums[middle] == target){
return target;
}else if(nums[middle] < target){
right = middle ;
}else if(nums[middle] > target){
left = middle + 1;
}
}
return right;
}
/**
*自己写的 考虑了插入的四种情况 但是显得笨拙
*/
//左闭右闭 分四种情况讨论 可行 但显得有点笨拙
/*public static int searchLR(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int middle = left + ((right - left) >> 1);
if (nums[middle] == target) {
return middle;
} else if (nums[middle] > target) {
right = middle - 1;
} else if (nums[middle] < target) {
left = middle + 1;
}
}
if (left == right) {
if (nums[left] < target) {
return left + 1;
} else if (nums[left] > target) {
return left;
}
} else if (left > right) {
if (right > 0) {
return nums.length;
} else if (right < 0) {
return 0;
}
}
return -1;
}*/
}
3、27 移除元素
/**
* 27 移除元素
*/
public class DAY1_27 {
public static void main(String[] args) {
int[] nums = {0,1,2,2,3,0,4,2};
int val = 2;
//int i = removeElementSF(nums, val);
int i = removeElementD(nums, val);
for (int j = 0; j < i; j++) {
System.out.println(nums[j]);
}
}
/**
* 快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
* 慢指针:指向更新 新数组下标的位置
* 其实就是快指针按照数组顺序依次遍历,
* 把遍历到不等于val的值从数组第0位开始存放,
* 慢指针用于记录一共存放了多少个数字 返回慢指针就行
*/
public static int removeElementSF(int[] nums, int val) {
int slowIndex = 0;
for (int fastIndex = 0; fastIndex < nums.length; fastIndex++) {
if(nums[fastIndex] != val){ //那么原数组元素不做变动
nums[slowIndex] = nums[fastIndex];
slowIndex++;
}
}
return slowIndex;
}
/**
* 双向指针
* 就是左右都开始查找
* 先找到右边第一个不等于val的值
* 然后从左边开始遍历 要是有一个等于val的 就用右边遍历到的值替换他
* 依次进行直到 left>right
*/
public static int removeElementD(int[] nums, int val) {
int left = 0;
int right = nums.length - 1;
while (right>=0 && nums[right] == val) right--;
while (left <= right){
if(nums[left] == val){
nums[left] = nums[right];
right--;
while (right>=0 && nums[right] == val) right--;
}
left++;
}
return left;
}
}