題目描述
數組中有一個數字出現的次數超過數組長度的一半,請找出這個數字。例如輸入一個長度爲9的數組{1,2,3,2,2,2,5,4,2}。由於數字2在數組中出現了5次,超過數組長度的一半,因此輸出2。如果不存在則輸出0。
分析 :
思路1:首先我們相當的應該就是遍歷,判斷每個數出現的次數。即雙層循環,判斷每個數出現的次數,如果有一個數字出現次數超過一般的數字,則輸出。若遍歷完沒有找到,則輸出0。時間複雜度爲O(n^2)
思路2:使用map將數據存於map,然後遍歷map, 找到value值超過一半的數字。缺點:需要額外空間。
接下來介紹劍指offer中提到的兩種解法。
思路3:最直觀的做法是排序(快速排序/STL中的sort())後找中間的數即可,但時間複雜度爲O(NlgN)。最後遍歷一遍,判斷找到的 數是否是出現次數超過一半的數字。
思路4:數組中出現次數超過一半的數字,如果存在,則這個數出現的次數肯定比其他數字出現總數多,所以易定義兩個變量,從第一個數開始找,並記錄第一個數爲result爲需要找的數,它出現的次數初始化爲times=1; 以後只要找到和result相等的數times++, 否則times-- .當times等於0的時候,改變result等於當前指向的數,繼續找。最後遍歷一遍,判斷找到的 數是否是出現次數超過一半的數字。
實現
這裏僅僅介紹後兩種實現方法。
1. 排序後找中位數(這裏可以使用快排自已查找,也可以使用sort().STL中的算法)
//方法1:基於快速排序
//Partion要選擇一個基準值
int Partion(vector<int>& number, int left, int right)
{
int begin = left, end = right;
int key = number[right];
while(begin < end)
{
//begin找大於
while(begin < end && number[begin] <= key)
{
++begin;
}
//end找下於
while(begin < end && number[end] >= key)
{
--end;
}
swap(number[begin], number[end]);
}
swap(number[begin],number[right]);
return begin;
}
int MoreThanHalfNum2(vector<int> numbers) {
int len = numbers.size();
if(len <=0 )
{
return 0;
}
int middle = len/2;
int div = Partion(numbers, 0,len-1);
int start = 0;
int end = len-1;
while(div != middle)
{
if(div > middle)
{
end = div-1;
div = Partion(numbers, start, end);
}
else
{
start = div+1;
div = Partion(numbers,start ,end);
}
}
int result = numbers[middle];
//檢查是否超過了一半
int count = 0;
for(int i=0; i<len; ++i)
{
if(numbers[i] == result)
{
count++;
}
if(count > len/2)
{
return result;
}
}
return 0;
}
//方法2:STL中的sort
//使用sort
int MoreThanHalfNum1(vector<int> numbers) {
int len = numbers.size();
if(len <= 0)
{
return 0;
}
sort(numbers.begin(), numbers.end());
int result = numbers[len/2];
int count = 0;
for(int i=0; i<len; i++)
{
if(numbers[i] == result)
{
count++;
}
if(count > len/2)
{
return result;
}
}
return 0;
}
2. 定義兩個變量,這種方法不易想到,而且不易理解。但是需要記住的是數組中出現次數超過一半的數字,如果存在,則這個數出現的次數肯定比其他數字出現總數多。
//數組中出現次數超過一般的數字(定義兩個變量)
//方法2:利用數組的特點進行求解
int MoreThanHalfNum_Solution(vector<int> numbers)
{
int len = numbers.size();
if(len <= 0)
{
return 0;
}
int result = numbers[0];
int count = 1;
for(int i = 1; i < len; ++i)
{
if(numbers[i] == result)
count ++;
else if(count == 0)
{
result = numbers[i];
count = 1;
}
else
count --;
}
//檢查是否超過了一半
count = 0;
for(int i=0; i<len; ++i)
{
if(numbers[i] == result)
{
count++;
}
if(count > len/2)
{
return result;
}
}
return 0;
}
總結:數組中出現次數超過的一半的數字,需要記住1. 這個數字出現的次數,肯定比其他總數多。2. 排序後,前部分的數字均爲這個數字。基於以上兩點,可以相當最優方法進行求解。