排序数列中元素出现次数问题

  关于“排序数列中元素出现次数问题”,这里采用折半法给出两种思路:

    1、找到元素在数组中的一个索引位置,由于是数组是有序排列,所有在这个位置左右移动,就可以找出其所有出现的位置;

    2、通过折半法找到元素的最大索引位置与最小索引位置,然后两个索引位置相减再加一,就是元素的出现的次数。

  方法1中查找元素位置的方法复杂度为O(logn),左右遍历元素的复杂度为O(n),所以总的复杂度为O(logn) + k,其中k为元素出现的次数。

当n小,k大时不太适合用这种方法。

  方法2中时间复杂度为O(logn),但是进行了两次查找,若n较大时也是一个不小的开。所以当n大,k小时,不太适合用这种方法。

  方法1的PHP代码实现

/*
 * @function:折半查找顺序数组中元素出现的位置
 * @param:顺序排列的数组
 * @param:要查找的元素
 * @param: true 查找最大位置, false查找最小位置
 * @return:返回元素的位置索引,没有返回零
 */
function arr_index_value($arr, $num, $flag){
    $left = 0;
    $right = count($arr) - 1;
    while($left <= $right){
        $mid = (int)(($left + $right)/2);
        if($arr[$mid] > $num){
            //注意为了在所有情况下$mid值能随着while循环朝着某个方向变化,$mid必须加一付给$right
            $right = $mid - 1;
        }else if($arr[$mid] < $num){
            //注意为了在所有情况下$mid值能随着while循环朝着某个方向变化,$mid必须减一付给$left
            $left = $mid + 1;
        }else{
            //若$flag为真,获得最大索引位置
            if(!$flag){
                if($mid == 0 || $arr[$mid-1] != $num)    return $mid;
                $right = $mid - 1;
            }
            //若$flag为假,获得最小索引位置
            else{
                if($mid == $right || $arr[$mid+1] != $num)    return $mid;
                $left = $mid + 1;
            }

        }
    }
}

  方法2的PHP代码实现

/*
 * @function:折半查找有序数组中某元素出现的次数。
 *              思路:分别找到元素在数组中出现的最大索引位置与最小索引位置,
 *                 然后两个相减,再加一就是出现的次数。
 * @param:顺序排列的有序数组
 * @param:需要查找的元素
 * @return:元素出现的次数,若不存在则返回零
 */
function arr_count_values2($arr, $num){
    //最大索引位置 - 最小索引位置 + 1
    $max = arr_index_value($arr, $num, true);
    $min =arr_index_value($arr, $num, false);
    if($max == 0) return 0;
    return $max - $min + 1;
}

/*
 * @function:折半查找顺序数组中元素出现的位置
 * @param:顺序排列的数组
 * @param:要查找的元素
 * @param: true 查找最大位置, false查找最小位置
 * @return:返回元素的位置索引,没有返回零
 */
function arr_index_value($arr, $num, $flag){
    $left = 0;
    $right = count($arr) - 1;
    while($left <= $right){
        $mid = (int)(($left + $right)/2);
        if($arr[$mid] > $num){
            //注意为了在所有情况下$mid值能随着while循环朝着某个方向变化,$mid必须加一付给$right
            $right = $mid - 1;
        }else if($arr[$mid] < $num){
            //注意为了在所有情况下$mid值能随着while循环朝着某个方向变化,$mid必须减一付给$left
            $left = $mid + 1;
        }else{
            //若$flag为假,获得最小索引位置
            if(!$flag){
                if($mid == 0 || $arr[$mid-1] != $num)    return $mid;
                $right = $mid - 1;
            }
            //若$flag为真,获得最大索引位置
            else{
                if($mid == $right || $arr[$mid+1] != $num)    return $mid;
                $left = $mid + 1;
            }

        }
    }
}

//测试
$a = array(1, 2, 2, 2, 3, 3, 5, 8);
print arr_count_values2($a, 2);

  

  我们可以进一步优化方法2,减少一次折半过程,使得时间复杂度进一步减小。

  方法3的PHP实现代码:

    

/*
 * @function:折半查找有序数组中某元素出现的次数。
 *              思路:分别找到元素在数组中出现的最大索引位置与最小索引位置,
 *                 然后两个相减,再加一就是出现的次数。
 * @param:顺序排列的有序数组
 * @param:需要查找的元素
 * @return:元素出现的次数,若不存在则返回零
 */
function arr_count_values3($arr, $num){
    $left = 0;
    $right = count($arr) - 1;

    //折半法获得元素一位置
    while($left <= $right){
        $mid = (int)(($left + $right)/2);
        if($arr[$mid] > $num){
            //注意为了在所有情况下$mid值能随着while循环朝着某个方向变化,$mid必须加一付给$right
            $right = $mid - 1;
        }else if($arr[$mid] < $num){
            //注意为了在所有情况下$mid值能随着while循环朝着某个方向变化,$mid必须减一付给$left
            $left = $mid + 1;
        }else{
            break;
        }
    }

    if($left > $right) return 0;

    $mid_t = $mid;
    $left_t = $left;
    $right_t = $right;

    //折半法获得最小索引位置
    while($left <= $right){
        $mid = (int)(($left + $right)/2);
        if($mid == 0 || $arr[$mid-1] != $num){
            $min = $mid;
            break;
        }
        $right = $mid - 1;
    }

    //折半法获得最大索引位置
    while($left_t <= $right_t){
        $mid_t = (int)(($left_t + $right_t)/2);
        if($mid_t == $right_t || $arr[$mid_t+1] != $num){
            $max =  $mid_t;
            break;
        }
        $left_t = $mid_t + 1;
    }

    //元素出现次数 = 最大索引位置 - 最小索引位置 + 1
    $max = arr_index_value($arr, $num, true);
    $min =arr_index_value($arr, $num, false);
    return $max - $min + 1;
}

  可见,方法3是最优的解法,适合各种情况,尤其在数组很大,元素出现的次数较多时,更能体现其优势~

时间: 2024-09-29 19:02:15

排序数列中元素出现次数问题的相关文章

九度oj 题目1349:数字在排序数组中出现的次数

题目描述: 统计一个数字在排序数组中出现的次数. 输入: 每个测试案例包括两行: 第一行有1个整数n,表示数组的大小.1<=n <= 10^6. 第二行有n个整数,表示数组元素,每个元素均为int. 第三行有1个整数m,表示接下来有m次查询.1<=m<=10^3. 下面有m行,每行有一个整数k,表示要查询的数. 输出: 对应每个测试案例,有m行输出,每行1整数,表示数组中该数字出现的次数. 样例输入: 8 1 2 3 3 3 3 4 5 1 3 样例输出: 4 使用库函数即可解决

剑指offer面试题38:数字在排序数组中出现的次数

题目描述: 统计一个数字在排序数组中出现的次数. 输入: 每个测试案例包括两行: 第一行有1个整数n,表示数组的大小.1<=n <= 10^6. 第二行有n个整数,表示数组元素,每个元素均为int. 第三行有1个整数m,表示接下来有m次查询.1<=m<=10^3. 下面有m行,每行有一个整数k,表示要查询的数. 输出: 对应每个测试案例,有m行输出,每行1整数,表示数组中该数字出现的次数. 样例输入: 81 2 3 3 3 3 4 513 样例输出: 4 //source:http

【剑指offer】面试题38:数字在排序数组中出现的次数

题目: 统计一个数字在排序数组中出现的次数. 思路: 对二分查找进行改进,找到数字在数组中第一次出现和最后一次出现的位置,这样就得到它出现的次数. 以找第一次出现的位置为例:如果mid元素大于k,则在前半段找:如果小于k,则在后半段找:如果等于k,则要看mid的前一个元素是不是k,如果是,则在前半段找,如果不是,则这就是第一次出现的位置. 时间复杂度O(logn). 代码: class Solution { public: int GetNumberOfK(vector<int> data ,

【剑指offer】数字在排序数组中出现的次数

转载请注明出处:http://blog.csdn.net/ns_code/article/details/27364557 题目描写叙述: 统计一个数字在排序数组中出现的次数. 输入: 每一个測试案例包括两行: 第一行有1个整数n,表示数组的大小.1<=n <= 10^6. 第二行有n个整数,表示数组元素,每一个元素均为int. 第三行有1个整数m,表示接下来有m次查询.1<=m<=10^3. 以下有m行,每行有一个整数k,表示要查询的数. 输出: 相应每一个測试案例,有m行输出,

九度_题目1349:数字在排序数组中出现的次数

//用map来实现总是超出最大的内存限制 题目描述: 统计一个数字在排序数组中出现的次数. 输入: 每个测试案例包括两行: 第一行有1个整数n,表示数组的大小.1<=n <= 10^6. 第二行有n个整数,表示数组元素,每个元素均为int. 第三行有1个整数m,表示接下来有m次查询.1<=m<=10^3. 下面有m行,每行有一个整数k,表示要查询的数. 输出: 对应每个测试案例,有m行输出,每行1整数,表示数组中该数字出现的次数. 样例输入: 8 1 2 3 3 3 3 4 5 1

《剑指offer》:[38]数字在排序数组中出现的次数

"沟通.学习能力就是看面试者能否清晰.有条理地表达自己,是否会在自己所得到的信息不够的情况下主动发问澄清,能否在得到一些暗示之后迅速做出反应纠正错误"                                                                                                                   ---陈黎明(MSoft-SDE) 题目:统计一个数字在排序数组中出现的次数.例如输入排序数组{1,2,3,3

剑指Offer--038-数字在排序数组中出现的次数

链接 牛客OJ:数字在排序数组中出现的次数 九度OJ:http://ac.jobdu.com/problem.php?pid=1349 GitHub代码: 038-数字在排序数组中出现的次数 CSDN题解:剑指Offer–038-数字在排序数组中出现的次数 牛客OJ 九度OJ CSDN题解 GitHub代码 038-数字在排序数组中出现的次数 1505-数字在排序数组中出现的次数 剑指Offer–038-数字在排序数组中出现的次数 038-数字在排序数组中出现的次数 题意 题目描述 统计一个数字

剑指offer:数字在排序数组中出现的次数

题目描述: 统计一个数字在排序数组中出现的次数. 思路分析: 1. 直观思路是直接遍历一遍,统计.复杂度也只要O(n). 2. 显然这道题要考察的内容不这么简单,实际上考虑二分的思想来完成.分别二分查找第一个k和最后一个k.具体来说,利用二分查找思想,找到k,再判断当前的前一个是否为k或是否为第一个元素,若是,则返回:否则即第一个k在前面,则右边界r左移,继续递归查找.对于最后一个k的查找思路类似. 代码: 思路二: 1 class Solution { 2 public: 3 int GetF

【剑指offer】数组在排序数组中出现的次数

题目描述 统计一个数字在排序数组中出现的次数. 分析:数组有序,采用二分查找无疑 两种方法,时间复杂度差不多,都是利用二分查找,不过统计k出现的次数有所不同而已 方法1:二分查找k,找到任意一个k的下标index,index向两边扩展即可 方法2:二分查找k+0.5和k-0.5的插入位置index1和index2,因为数组元素都是整数,index1-index2就是k出现的次数 时间复杂度:O(log N) 方法1: int GetNumberOfK(vector<int> v ,int k)