merge两个有序数组 & 查找一个有序数组中指定元素

来源:互联网 发布:人工智能 数据挖掘 编辑:程序博客网 时间:2024/05/17 22:13
20. 合并有序数组。给定升序排列的两个数组array1和array2,要求将array2合并到array1中,并保持结果有序是升序的。假设
array1有足够的空间容纳array2.起初array1和array2的元素各有m和n个。
solution:
可以直接使用归并排序中的merge()对有序数组直接合并。但是由于不能使用额外的存储,不像归并排序那样,所以插入一个元素
可能会导致元素的搬动,为了减少元素搬动,可以从array1的右边开始插入元素,也就是array1[n+m-1]。
由于两个数组都是升序的,那么从两个数组最大的元素开始比较,也就是array1[m-1]和array2[n-1]。将大的一个加入到array1[n+m-1]。
换句话说,新数组的最后一个元素要么来自array1[m-1],要么是array2[n-1]。当完成插入后,被插入的指针左移一步。这样可以保证没
有元素在插入之前被flush掉。插入之后再被其他元素flush掉是没有问题的。
算法的代码实现如下:
public class question20 {
    
    void merge2SortedArrays(int[] array1, int[] array2){
        int m=array1.length;
        int n=array2.length;
        
        while(n>0){
            if(m<0 || array1[m-1] < array2[n-1])
                //当数组1取完了所有的元素,或者当前1数组的元素比2数组的元素小,由于是升序排序并且是由右边插入元素
                //那么我们取数组2的元素插入。之后数组2的指针左移一步
                array1[n+m-1]=array2[n--];
            else
                //数组1插入到n+m-1的位置,m向左移动一步
                array1[n+m-1]=array1[m--];

        }
        
    }

}

21. 区间搜索。给定一个升序的数组,这个数组中可能含有相同的元素,并且给定一个目标值。要求找出目标值在数组中的开始下标以及
终止下标。如果这个数组不含有该目标值,就返回[-1,-1]。要求算法的时间复杂度是O(logn),其中n是数组长度。
比如,array={1,3,4,4,5},target=4,那么返回[2,3]。如果target是8,那么返回[-1,-1]。
solution:
借鉴二分查找的算法思想。分两步进行,第一步为了找到起点,先找到比目标值下标最靠近的目标元素,也就是左邻居。比如上面的左邻
居是3。那么开始下标就是3的下标++。第二步和第一步类似,为了找到终止下标,要找到右邻居,比如上面的5,那么就是5的下标--。
由于是2步的二分查找,算法的时间复杂度仍然是O(logn)。算法的代码实现如下:
public class question21 {
    int[] searchRange(int[] array, int target) {
        int[] range = { -1, -1 };
        // 注意left是数组的第一个下标-1,因为左邻居可能不存在,比如{1,1,3,5}找1,那么左邻居下标是-1,起点下标就是-1+1=0
        int left = -1;
        // 注意right是数组的最后一个下标+1,因为右邻居可能不存在,比如{1,2,4,4},找4,那么右邻居的下标是4,4-1=3才是终止下标
        int right = array.length;

        int mid;// 二分查找的中间值

        if (array[right - 1] < target)// 由于array是升序排列的,此时一定不存在数组中的target
            return range;

        // 搜索左邻居,使用二分查找
        while (left < right) {
            mid = (left + right) / 2;
            if (array[mid] < target)
                //
                left = mid + 1;
            else
                right = mid;
        }
        if (array[left + 1] == target)// left是左邻居的下标
            range[0] = left + 1;// left+1就是目标值的起始下标
        else
            // 找不到左邻居,那么target一定不出现在数组中,直接返回range
            return range;

        // 搜索右邻居
        // 先将right回朔到原先的位置,保留上面计算出来的left.
        right = array.length;
        left++;
        // 在起始下标left+1和right之间进行二分查找
        while (left < right) {
            mid = (left + right) / 2;
            if (array[mid] > target) {
                right = mid;
            } else
                left = mid + 1;
        }
        range[1] = right - 1;// 终止下标是右邻居下标-1
        return range;
    }

}

22. 插入位置。给定一个升序排序的数组和一个目标值,返回目标值的下标。如果目标值不在数组里,那么返回这个目标值插入数组后的下标。
要求插入之后数组仍然保持有序。并且假定数组不存在重复元素。要求算法的时间复杂度是O(logn)
比如,array={1,2,4,5,6},target=3,则返回2。又如,array={1,2,3,4,5},target=4,那么返回3。
solution:
由于是升序排序的,只要找到第一个大于或者等于target的元素就行了。使用一般的二分查找算法就可以实现了。具体的代码如下:
public class question22 {
    int toSearchInsert(int[] array, int target){
        int left=0, right=array.length-1, mid;
        while(left<=right){
            mid=(left+right)/2;
            if(array[mid]==target){
                return mid;
            }else if(array[mid]>target){
                right=mid-1;
            }else{
                left=mid+1;
            }
        }
        return left;
    }
}
0 0