《剑指offer》第三十九题:数组中出现次数超过一半的数字
// 面试题39:数组中出现次数超过一半的数字 // 题目:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例 // 如输入一个长度为9的数组{1, 2, 3, 2, 2, 2, 5, 4, 2}。由于数字2在数组中 // 出现了5次,超过数组长度的一半,因此输出2。 #include <cstdio> #include "Array.h" bool g_bInputInvalid = false; bool CheckInvalidArray(int* numbers, int length) //检查是否有效输入 { g_bInputInvalid = false; if (numbers == nullptr || length <= 0) g_bInputInvalid = true; return g_bInputInvalid; } bool CheckMoreThanHalf(int* numbers, int length, int number) //检查结果是否出现次数超过一半 { int times = 0; for (int i = 0; i < length; ++i) { if (numbers[i] == number) ++times; } bool isMoreThanHalf = true; if (times * 2 <= length) { g_bInputInvalid = true; isMoreThanHalf = false; } return isMoreThanHalf; } // ====================方法1==================== // 利用随机快速排序算法, 随机选一个数排序, 查看此数位置, // 如位于前半部分,则需要查找的数位于后半部分, 反之, 位于前半部分 int MoreThanHalfNum_Solution1(int* numbers, int length) { if (CheckInvalidArray(numbers, length)) return 0; int middle = length >> 1; //此处表示中位 = length/2 int start = 0; int end = length - 1; int index = Partition(numbers, length, start, end); //随机快速排序算法 while (index != middle) //如果随机选择的数刚好处于中位, 则跳出 { if (index < middle) //位于前半部分 { start = index + 1; index = Partition(numbers, length, start, end); } else //index > middle { end = index - 1; index = Partition(numbers, length, start, end); } } int result = numbers[index]; if (!CheckMoreThanHalf(numbers, length, result)) result = 0; return result; } // ====================方法2==================== // 从第一个数开始计算次数, 次数为0时换为当前数, 则寻找的数是最后times不为0的数 int MoreThanHalfNum_Solution2(int* numbers, int length) { if (CheckInvalidArray(numbers, length)) return 0; int times = 1; int result = numbers[0]; for (int i = 1; i < length; ++i) { if (times == 0) { result = numbers[i]; times = 1; } else if (result == numbers[i]) ++times; else --times; } if (!CheckMoreThanHalf(numbers, length, result)) result = 0; return result; }
//Array.cpp #include <stdlib.h> #include "Array.h" #include <exception> // Random Partition int RandomInRange(int min, int max) //范围内随机选一个数字 { int random = rand() % (max - min + 1) + min; return random; } void Swap(int* num1, int* num2) { int temp = *num1; *num1 = *num2; *num2 = temp; } int Partition(int data[], int length, int start, int end) { if(data == nullptr || length <= 0 || start < 0 || end >= length) throw new std::exception("Invalid Parameters"); int index = RandomInRange(start, end); Swap(&data[index], &data[end]); //随机值放到最后 int small = start - 1; //small作为最小数字的索引一直把比随机值小的数字往前放 for(index = start; index < end; ++ index) { if(data[index] < data[end]) { ++ small; if(small != index) Swap(&data[index], &data[small]); } } ++ small; Swap(&data[small], &data[end]); //把随机值放到比它小的数字后 return small; }
#pragma once __declspec( dllexport ) int Partition(int data[], int length, int start, int end);
// ====================测试代码==================== void Test(const char* testName, int* numbers, int length, int expectedValue, bool expectedFlag) { if (testName != nullptr) printf("%s begins: \n", testName); int* copy = new int[length]; for (int i = 0; i < length; ++i) copy[i] = numbers[i]; printf("Test for solution1: "); int result = MoreThanHalfNum_Solution1(numbers, length); if (result == expectedValue && g_bInputInvalid == expectedFlag) printf("Passed.\n"); else printf("Failed.\n"); printf("Test for solution2: "); result = MoreThanHalfNum_Solution2(copy, length); if (result == expectedValue && g_bInputInvalid == expectedFlag) printf("Passed.\n"); else printf("Failed.\n"); delete[] copy; } // 存在出现次数超过数组长度一半的数字 void Test1() { int numbers[] = { 1, 2, 3, 2, 2, 2, 5, 4, 2 }; Test("Test1", numbers, sizeof(numbers) / sizeof(int), 2, false); } // 不存在出现次数超过数组长度一半的数字 void Test2() { int numbers[] = { 1, 2, 3, 2, 4, 2, 5, 2, 3 }; Test("Test2", numbers, sizeof(numbers) / sizeof(int), 0, true); } // 出现次数超过数组长度一半的数字都出现在数组的前半部分 void Test3() { int numbers[] = { 2, 2, 2, 2, 2, 1, 3, 4, 5 }; Test("Test3", numbers, sizeof(numbers) / sizeof(int), 2, false); } // 出现次数超过数组长度一半的数字都出现在数组的后半部分 void Test4() { int numbers[] = { 1, 3, 4, 5, 2, 2, 2, 2, 2 }; Test("Test4", numbers, sizeof(numbers) / sizeof(int), 2, false); } // 输入空指针 void Test5() { int numbers[] = { 1 }; Test("Test5", numbers, 1, 1, false); } // 输入空指针 void Test6() { Test("Test6", nullptr, 0, 0, true); } int main(int argc, char* argv[]) { Test1(); Test2(); Test3(); Test4(); Test5(); Test6(); return 0; }
分析:第二种思路没有改变数组且想法清奇。
class Solution { public: int MoreThanHalfNum_Solution(vector<int> numbers) { int length = (int)numbers.size(); if (numbers.empty()) return 0; int times = 1; int result = numbers[0]; for (int i = 1; i < length; ++i) { if (times == 0) { times = 1; result = numbers[i]; } else if (result == numbers[i]) ++times; else --times; } if (!CheckMoreTnanHalf(numbers, length, result)) result = 0; return result; } bool CheckMoreTnanHalf(vector<int> numbers, int length, int result) { int times = 0; for (int i = 0; i < length; ++i) { if (numbers[i] == result) ++times; } bool isMoreTnanHalf = false; if (length < (times << 1)) isMoreTnanHalf = true; return isMoreTnanHalf; } };