0
点赞
收藏
分享

微信扫一扫

前端视角如何理解“时间复杂度O(n)”

我是小瘦子哟 03-22 23:30 阅读 2

二分查找

分类:1)找某个数;2)找左边界;3)找右边界

注意点:1)mid = left + (right - left) / 2

​ 2)if中的判断条件,目的是把数组分开,确定更小的搜索区间

1.搜索插入位置35简单

class Solution {
    public int searchInsert(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] == target) {
                return mid;
            } else if (nums[mid] < target) {
                left = mid + 1;
            } else if (nums[mid] > target) {
                right = mid - 1;
            }
        }
        // if (left > nums.length) {
        //     return left + 1;
        // }
        // return left;
        return left;
    }
}

记录:数组是排序好的,从中找到目标值,返回索引。定义初始搜索区间[0, n - 1],是个闭区间,所以结束循环的条件是left <= right,计算mid,调整搜索区间:如果mid的值大于target,说明右半部分区间都大于target,就要缩小搜索区间到左半部分;如果mid的值小于target,说明左半部分区间都小于target,就要缩小搜索区间到右半部分,直到找到target返回下标或者数组中不存在target。

2. 搜索二维矩阵 74中等

class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        // 循环判断target是否在某行中
        int m = matrix.length;
        int n = matrix[0].length;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (matrix[i][j] >= matrix[i][0] && matrix[i][j] <= matrix[i][n-1]) {
                    // 二分查找
                    int left = 0;
                    int right = n - 1;
                    while (left <= right) {
                        int mid = left + (right - left) / 2;
                        if (matrix[i][mid] == target) {
                            return true;
                        } else if (matrix[i][mid] > target) {
                            right = mid - 1;
                        } else if (matrix[i][mid] < target) {
                            left = mid + 1;
                        }
                    }
                } else {
                    continue;
                }
            }
        }
        // 在某行中使用二分查找
        return false;
    }
}

记录:二维有序,所以先确定要搜索的行,在某行中进行二分查找,找到target。

3. 在排序数组中查找元素的第一个和最后一个位置 34 中等

class Solution {
    public int[] searchRange(int[] nums, int target) {
        // 定义二位result数组存放结果
        int[] result = new int[2];
        result[0] = -1;
        result[1] = -1;
        // if (nums.length == 0) {
        //     return result;
        // }
        // if (nums.length == 1 && nums[0] != target) {
        //     return result;
        // }
        // 同时寻找左边界和右边界,l_代表左边界,r_代表右边界
        int l_left = 0;
        int l_right = nums.length;
        int r_left = 0;
        int r_right = nums.length;
        // 左边界
        while (l_left < l_right) {
            int mid = l_left + (l_right - l_left) / 2;
            if (nums[mid] == target) {
                l_right = mid;
            } else if (nums[mid] < target) {
                l_left = mid + 1;
            } else if (nums[mid] > target) {
                l_right = mid;
            }
        }
        // 右边界
        while (r_left < r_right) {
            int mid = r_left + (r_right - r_left) / 2;
             if (nums[mid] == target) {
                r_left = mid + 1;
             } else if (nums[mid] < target) {
                r_left = mid + 1;
             } else if (nums[mid] > target) {
                r_right = mid;
             }
        }

        if (l_left < nums.length && nums[l_left] == target) {
            result[0] = l_left;
        }
        // 这里的条件一定要注意!!!
        if (r_left - 1 >= 0 && r_left - 1 < nums.length && nums[r_left - 1] == target) {
            result[1] = r_left - 1;
        }

    return result;
    }
}

记录:题目中寻找第一个和最后一个位置,相当于搜索左边界和右边界,将问题转换后按逻辑写。

4. 搜索旋转排序数组 33 中等

// 思路一:排序后进行二分查找
// 思路二:找到旋转点,分成左右两部分,比较一下target和两部分的开始值,确定其中一部分进行二分查找——>最后计算一下在原nums中的下标
// 思路三:直接二分,判断两边那边有序,在顺序区间二分,如果没在,再分为有序和乱序两部分
// 思路二
// class Solution {
//     public int search(int[] nums, int target) {
//         int n = nums.length;
//         int index = 0;
//         for (int i = 1; i < n; i++) {
//             if (nums[i] <= nums[i - 1]) {
//                 index = i;
//                 break;
//             }
//         }
//         int left = 0;
//         int right = n - 1;
//         // 设个标识标记是前半部分还是后半部分
//         int flag = 0; // 0表示二分前半部分,1表示二分后半部分
//         // 判断一下二分前一部分,还是后一部分
//         if (nums[0] > target) {
//             left = index;
//             flag = 1;
//         } else {
//             if (index != 0) {
//                 right = index - 1;
//             }
//         }
//         // 二分
//         while (left <= right) {
//             int mid = left + (right - left) / 2;
//             if (nums[mid] == target) {
//                 // if (flag == 0) {
//                 //     System.out.println("111");
//                 //     return mid + (n - index);
//                 // }
//                 // return mid - index;
//                 return mid;
//             } else if (nums[mid] < target) {
//                 left = mid + 1;
//             } else if (nums[mid] > target) {
//                 right = mid - 1;
//             }
//         }
//         return -1;
//     }
// }

// 思路三
class Solution {
    public int search(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] == target) {
                return mid;
            } else if (nums[mid] >= nums[left]) {
                // 主要是判断下次是选顺序区间还是乱序区间
                // 左半部分有序
                if (nums[mid] > target && target >= nums[left]) {
                    right = mid - 1;
                } else {
                    left = mid + 1;
                }
                // if (nums[left] < nums[mid] && nums[left] >= target) {
                //     left = mid + 1;
                // } else {
                //     right = mid - 1;
                // }
            } else if (nums[mid] <= nums[right]) {
                // 右半部分有序
                // if (nums[left] > target) {
                //     left = mid + 1;
                // } else {
                //     right = mid - 1;
                // }
                if (nums[mid] < target && target <= nums[right]) {
                    left = mid + 1;
                } else {
                    right = mid - 1;
                }
            }
            System.out.println("left:" + left + "--mid:" + mid);
        }
        return -1;
    }
}

5. 寻找旋转排序数组中的最小值 153 中等

// 思路一:排序后第一个
// class Solution {
//     public int findMin(int[] nums) {
//         Arrays.sort(nums);
//         return nums[0];
//     }
// }

// // 思路二:本身有序,旋转是向后移一位——>找到中间点
// class Solution {
//     public int findMin(int[] nums) {
//         int n = nums.length;
//         int result = nums[0];
//         for (int i = 1; i < n; i++) {
//             if (nums[i] < nums[i-1]) {
//                 result = nums[i];
//                 break;
//             }
//         }
//         return result;
//     }
// }

// 思路三:二分查找
class Solution {
    public int findMin(int[] nums) {
        int left = 0;
        int right = nums.length - 1;
        int minValue = 5001;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[left] <= nums[mid]) {
                minValue = Math.min(minValue, nums[left]);
                left = mid + 1;
            } else {
                minValue = Math.min(minValue, nums[mid]);
                right = mid - 1;
            }
        }
        return minValue;
    }
}
举报

相关推荐

0 条评论