zoukankan      html  css  js  c++  java
  • 47. 数组中出现次数超过一半的数字[Number appears more than half times]

    题目】:数组中有一个数字出现的次数超过了数组长度的一半,找出这个数字。

    例如长度为9的数组{1,2,3,2,2,2,5,4,2}中次数超过了数组长度的一半的数字为2,而长度为8的数组{1,2,3,2,2,2,5,4}则为非法输入。

    思路一】:先对数组进行排序,再遍历排序后的数组,统计每个数的次数,出现次数最大的数即为要找的数。

    时间复杂度:O(nlgn)+ O(n)= O(nlgn);空间复杂度:O(1)。

    思路二】:先对数组进行排序,出现次数超过数组长度的一半的数必然是数组中间的那个数。

    时间复杂度:O(nlgn)+ O(1)= O(nlgn);空间复杂度:O(1)。

    思路三】:使用Hash表。遍历数组中的每个数字,找到它在哈希表中对应的位置并增加它出现的次数。Hash表只适用于元素的范围(range)比较小的情况,而假设我们的数组是一个整型数组,取值范围跨度太大,所以不适合用哈希表,太浪费空间了。

    时间复杂度:O(n);空间复杂度:O(range)。

    思路四】:既然该数字为数组的中位数,即长度为n的数组中第[n/2]大数字。那么有没有更快的方法求解?我们已经有成熟的O(n)算法求解数组的第K大数字,即Kmin。那么可以借鉴其思想。

    时间复杂度:O(n);空间复杂度:O(1)。

    缺点是由于使用了QuickSort的Partition算法,需要交换数组中数字顺序,会修改输入数组。

    具体代码如下:

     C++ Code 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
     
    // 47_NumberAppearMoreThanHalf.cpp : Defines the entry point for the console application.
    //

    #include "stdafx.h"
    #include <iostream>
    using namespace std;

    bool g_bValid = true;

    // swap a and b
    void myswap(int &a, int &b)
    {
        
    int t = a;
        a = b;
        b = t;
    }

    // use Partition of QuickSort to get Kmin
    int Partition(int a[], int left, int right) 
    {
    // partition so that a[left..p-1]<a[p] and a[p+1..right]>=a[p]
        int pivot = a[left], i = left , j = right;
        
    while (i < j) 
        { 
    //  i from left, j from right
            while (a[i] <= pivot ) i++;
            
    while (a[j] > pivot ) j--;
            
    if (i < j) 
                myswap(a[i],a[j]);
        }
        myswap(a[left],a[j]);
        
    return j;
    }

    // check whether array is valid
    bool IsArrayValid(int a[],int n)
    {
        g_bValid = 
    true;
        
    if(NULL==a||n<=0)
        {
            g_bValid = 
    false;
        }
        
    return g_bValid;
    }

    // check whether result appears more than half
    bool IsAppearMoreThanHalf(int a[],int n,int result)
    {
        
    int times = 0;
        
    for (int i=0;i<n;++i)
            
    if (a[i]==result)
                times++;

        
    bool bIsMoreThanHalf = true;
        
    if (2*times<=n)
        {
            g_bValid = 
    false;
            bIsMoreThanHalf = 
    false;
        }

        
    return bIsMoreThanHalf;
    }

    // get number which appears more than half of array
    int NumberAppearMoreThanHalf_Solution1(int a[],int n)
    {
    // O(n)
        // whether array is valid
        if (!IsArrayValid(a,n))
            
    return 0;

        
    int left = 0;
        
    int right = n-1;
        
    int middle = n/2;
        
    int pivot = Partition(a,left,right);
        
    while(pivot!=middle)
        {
            
    if (pivot<middle)
            {
                left = pivot+
    1;
                pivot =Partition(a,left,right);
            }
            
    else
            {
                right = pivot-
    1;
                pivot =Partition(a,left,right);
            }
        }
        
    // pivot == middle
        int result = a[middle];

        
    // check whether result appears more than half
        if (!IsAppearMoreThanHalf(a,n,result))
            
    return 0;

        
    return result;
    }

    // get number which appears more than half of array
    int NumberAppearMoreThanHalf_Solution2(int a[],int n)
    {
    // O(n)
        // whether array is valid
        if (!IsArrayValid(a,n))
            
    return 0;

        
    int result = a[0];
        
    int appearTimes = 1;
        
    for (int i=1;i<n;++i)
        {
            
    if (a[i]==result)
                appearTimes++;
            
    else
                appearTimes--;

            
    if (appearTimes==0)
            {
                result = a[i];
                appearTimes = 
    1;
            }
        }

        
    // check whether result appears more than half
        if (!IsAppearMoreThanHalf(a,n,result))
            
    return 0;

        
    return result;
    }

    void test_base(int a[],int n)
    {
        
    int result = NumberAppearMoreThanHalf_Solution2(a,n);
        
    if (g_bValid)
            cout<<result<<endl;
        
    else
            cout<<
    "Invalid array."<<endl;
    }

    void test_case1()
    {
        
    int a[] = {1,2,3,2,2,2,5,4,2};
        
    int n = sizeof(a)/sizeof(int);
        test_base(a,n); 
    // 2
    }

    void test_case2()
    {
        
    int a[] = {1,2,3,2,2,2,5,4};
        
    int n = sizeof(a)/sizeof(int);
        test_base(a,n); 
    // invalid array
    }

    void test_main()
    {
        test_case1();
        test_case2();
    }

    int _tmain(int argc, _TCHAR* argv[])
    {
        test_main();
        
    return 0;
    }

     思路五】:数组中有个数字出现的次数超过了数组长度的一半。也就是说,有个数字出现的次数比其他所有数字出现次数的和还要多。因此我们可以考虑在遍历数组的时候保存两个值:一个是数组中的一个数字,一个是其对应的出现次数。当我们遍历到下一个数字的时候,如果下一个数字和我们之前保存的数字相同,则次数加1。如果下一个数字和我们之前保存的数字不同,则次数减1。如果次数为零,我们需要保存下一个数字,并把次数设为1。这样最后剩下的数字肯定就是出现次数超过数组长度一半的数字。

    时间复杂度:O(n);空间复杂度:O(1)。

    相比【思路四】不会修改输入数组。

    具体代码如下:

     C++ Code 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
     
    // get number which appears more than half of array
    int NumberAppearMoreThanHalf_Solution2(int a[],int n)
    {
    // O(n)
        // whether array is valid
        if (!IsArrayValid(a,n))
            
    return 0;

        
    int result = a[0];
        
    int appearTimes = 1;
        
    for (int i=1;i<n;++i)
        {
            
    if (a[i]==result)
                appearTimes++;
            
    else
                appearTimes--;

            
    if (appearTimes==0)
            {
                result = a[i];
                appearTimes = 
    1;
            }
        }

        
    // check whether result appears more than half
        if (!IsAppearMoreThanHalf(a,n,result))
            
    return 0;

        
    return result;
    }

     【参考】

    http://zhedahht.blog.163.com/blog/static/25411174201085114733349/

    http://www.cnblogs.com/python27/archive/2011/12/15/2289534.html

    个人学习笔记,欢迎拍砖!---by hellogiser

    Author: hellogiser
    Warning: 本文版权归作者和博客园共有,欢迎转载,但请保留此段声明,且在文章页面明显位置给出原文连接。Thanks!
    Me: 如果觉得本文对你有帮助的话,那么【推荐】给大家吧,希望今后能够为大家带来更好的技术文章!敬请【关注】
  • 相关阅读:
    [jQuery学习系列六]6-jQuery实际操作小案例
    [Java拾遗一] XML的书写规范与解析.
    [数据库操作]Java中的JDBC的使用方法.
    [Java拾遗二]Tomact及Http 部分总结.
    [Java拾遗三]JavaWeb基础之Servlet
    [Java拾遗四]JavaWeb基础之Servlet_Request&&Response
    [Java拾遗五]使用Session防止表单重复提交
    [数据库连接池] Java数据库连接池--DBCP浅析.
    [开发工具]Java开发常用的在线工具
    [数据库连接池二]Java数据库连接池--C3P0和JDNI.
  • 原文地址:https://www.cnblogs.com/hellogiser/p/3744962.html
Copyright © 2011-2022 走看看