数据结构算法每日一练(六)数组主元素
难度: ⭐⭐⭐
题目: 已知一个整数序列 A = ( a 0 , a 1 . . . . . . a n − 1 ) A = (a_0,a_1......a_{n-1}) A=(a0,a1......an−1),其中 0 ≤ a i < n ( 0 ≤ i < n ) 0≤a_i<n(0≤i<n) 0≤ai<n(0≤i<n)。
若存在 a p i = a p 2 = . . . . . . a p m = x 且 m > n / 2 ( 0 ≤ p k < n , 1 ≤ k ≤ m ) a_{pi}=a_{p2}= ......a_{pm}=x且m>n/2 (0≤p_k<n,1≤k≤m) api=ap2=......apm=x且m>n/2(0≤pk<n,1≤k≤m),则称x为A的主元素。
例如A=(0, 5, 5, 3, 5, 7, 5, 5),则5为主元素;又如A=(0, 5, 5, 3, 5, 1, 5, 7), 则A中没有主元素。假设A中的n个元素保存在一个一维数组中,请设计
一个尽可能高效的算法,找出A的主元素。若存在主元素,则输出该元素;否则输出-1.要求:
(1)给出算法的基本设计思想。
(2)根据设计思想,采用C或C++或Java语言描述算法,关键之处给出注释。
(3)说明你所设计算法的时间复杂度和空间复杂度。
解法一
(1)算法思想: 构建一个大小为n的数组,用于对每个整数的计数,遍历一遍序列然后再遍历1次这个数组找到最大值,然后判断条件是否符合主元素条件。此解法易思考。 类似于桶排序,桶排序思想可参考👉桶排序
(2)
/** * 主元素算法 * @param A 判定数组 * @param n 数量n * @return */ int Majority(int A[], int n) { int i, *B; //定义B数组 B = (int *)malloc(sizeof(int)*n); //分配空间 memset(B, 0, sizeof(int)*n); //赋初值为0 for (int i = 0; i < n; i++) { B[ A[i] ]++; //将A数组对应的值在B数组对应+1; } int max = 0; for (int j = 0; j < n; j++) { //遍历完查最大数量的数值 if (B[j] > max) { max = B[j]; } } if (max > n / 2) //判断最多数量的数是否满足主元素的条件大于n/2 return max; else return -1; }
(3)时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( n ) O(n) O(n);
解法二(最优解法)
(1)算法思想: 策略是从前向后扫描数组元素,标记出一个可能成为主元素的元素Num。然后重新计数,确定Num是否是主元素。
算法可分为以下两步:
①选取候选的主元素。依次扫描所给数组中的每个整数,将第一个遇到的整数Num保存到c中,记录Num的出现次数为1; 若遇到的下一个整数仍等于Num,则计数加1,否则计数减1;当计数减到0时,将遇到的下一个整数保存到c中,计数重新记为1,开始新一轮计数,即从当前位置开始重复上述过程,直到扫描完全部数组元素。
②判断c中元素是否是真正的主元素。再次扫描该数组,统计C中元素出现的次数,若大于n/2,则为主元素;否则,序列中不存在主元素。
(2)
/** * 主元素算法 * @param A 判定数组 * @param n 数量n * @return */ int Majority1(int A[], int n) { int i , c, count = 1; c = A[0]; //设置A[0]为候选主元素 for (int i = 1; i < n; i++) { if (A[i] == c) { //如果是候选主元素,则计数加一 count++; }else { if (count > 0) { //如果不是候选主元素,计数减一 count--; }else { //计数为0,更换候选主元素 c = A[i]; count = 1; } } }//for if (count > 0) { //统计候选主元素的次数 for (int i = count = 0; i < n; i++) { if (A[i] == c) { count++; } } }//if if (count > n / 2) //判断条件 return c; else return -1; }
(3)时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( 1 ) O(1) O(1)
解法三
(1)算法思想: 先对整个序列进行排序,然后遍历一遍看哪个元素最多,此算法也易想,相较于上述两种算法效率稍低。
(2)时间复杂度
若使用快速排序或者归并排序时间复杂度为 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n);
冒泡排序获插入排序等时间复杂度为 O ( n 2 ) O(n^2) O(n2);
快速排序空间复杂度为 O ( l o g 2 n ) O(log_2n) O(log2n),归并排序空间复杂度为 O ( n ) O(n) O(n)。