算法之寻找数组中的多数元素

题目

给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。

初步分析

首先,我们先按照一般思路想。可以新定义一个数组来存储原始数组中每个不同元素出现的个数,然后定义两个变量来存储当前最大出现的次数和当前的多数元素。

初步实现代码

private double FindNum(double[] originArr)
{
    int[] tempArr = new int[originArr.Length];
    int maxCount = 1;
    double maxValue = originArr[0];

    for (int i = 0; i < originArr.Length; i++)
    {
        tempArr[i] = 1;
        for (int j = i + 1; j < originArr.Length; j++)
        {
            if (originArr[j] == originArr[i])
            {
                tempArr[i]++;
                if (maxCount < tempArr[i])
                {
                    maxCount = tempArr[i];
                    maxValue = originArr[i];
                }
            }
        }
    }

    return maxValue;
}

简单解释下这个函数,首先我们这个函数接收原始的数组,然后新定义一个整型数组。新数组的最大长度最多也就是原始数组每个元素都不同的情况,不过根据题中描述,多数元素一定存在,那么理论上这个数组的最大长度也就是n/2。简单解释下,长度为n数组多数元素必定占n/2+1以上个,其它元素即使都不同最多也就占n/2-1个,加上那个多数元素最长为n/2即可。不过这里我们并不能这样定义,往后看即知。
maxCount变量代表数组中元素最大出现的次数,初始值为1,maxValue变量代表数组中的多数元素。
这里有个细节,由于存放元素出现次数的数组与原始数组唯一的对应关系就是下标,所以我们存的某一个元素的出现次数在tempArr里是不连续的。比如原始数组为1,1,2,2,2,那么当这段程序执行后,tempArr[0]的值为2,代表1出现了两次,tempArr[1]的值为原始值1,tempArr[2]的值为3,代表2出现了3次,tempArr[3]、tempArr[4]的值都为原始值1。这也是为什么不能把tempArr数组长度定义为n/2的原因。

验证

这里我们新建一个WPF程序,然后在MainWindow.xaml里简单布下局,一个文本框用于输入内容,一个按钮用来给寻找多数元素的函数创造调用时机,多数元素通过后台调用弹窗显示即可。
布局代码:

<Window x:Class="FindMostElement.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:FindMostElement"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Orientation="Horizontal">
            <TextBox x:Name="txt_Arr" Width="200" Height="100" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto"></TextBox>
            <Button x:Name="btn_Start" Margin="10,0,0,0" Width="80" Height="30" Click="btn_Start_Click">开始</Button>
        </StackPanel>
    </Grid>
</Window>

我们这里把MainWindow.xaml.cs的代码也一起贴出来,方便读者自行去验证~

using System;
using System.Windows;

namespace FindMostElement
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        /// <summary>
        /// 给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。
        /// 你可以假设数组是非空的,并且给定的数组总是存在多数元素。
        /// </summary>

        public MainWindow()
        {
            InitializeComponent();
            txt_Arr.Text = "3,2,3";
        }

        private void btn_Start_Click(object sender, RoutedEventArgs e)
        {
            string[] strOriginArr = null;
            double[] originArr = null;

            try
            {
                strOriginArr = txt_Arr.Text.ToString().Split(',');
                originArr = new double[strOriginArr.Length];
                for (int i = 0; i < strOriginArr.Length; i++)
                {
                    originArr[i] = int.Parse(strOriginArr[i]);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("请输入正确的数组元素,元素中间以“,”分隔!\n\r" + ex.Message, "警告", MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            double mostValue = FindNum(originArr);
            MessageBox.Show("该数组中的多数元素为:" + mostValue + "。", "信息", MessageBoxButton.OK, MessageBoxImage.Information);
        }

        private double FindNum(double[] originArr)
        {
            int[] tempArr = new int[originArr.Length];
            int maxCount = 1;
            double maxValue = originArr[0];

            for (int i = 0; i < originArr.Length; i++)
            {
                tempArr[i] = 1;
                for (int j = i + 1; j < originArr.Length; j++)
                {
                    if (originArr[j] == originArr[i])
                    {
                        tempArr[i]++;
                        if(maxCount < tempArr[i])
                        {
                            maxCount = tempArr[i];
                            maxValue = originArr[i];
                        }
                    }
                }
            }

            return maxValue;
        }
    }
}

思考

到这里,我们的一般处理相当于完成了。这时我们应该会想到,既然是算法题,那么理论上必定会有更优的解法。我们可以这样想,对于用程序做一件事情来说,我们要着重关注起始条件和最终要出的结果,要善于发现起始条件中隐含的信息。拿此题来讲,我们的起始条件是一个必定存在多数元素的数组,然后要的结果就是输出多数元素。我们如果想要更优,那么就需要去掉刚才那个方法的一些过程,比如记录每个元素出现次数的数组,记录当前最大出现次数和当前多数元素值的变量。

更优的解法

好了,话不多说,更优的一个解法如下。首先我们假定多数元素为原始数组第一个元素,定义变量mostValue,然后我们遍历数组,遇到和假定的元素一样的就+1,不一样的就-1,最后结果必然是一个大于0的值。然后我们用一个中间变量curSum来表示当前总和,每次当curSum等于0时,把当前遍历到的数组元素值赋给mostValue,当遍历结束后,mostValue就是我们要找的多数元素值。

private double FastFindNum(double[] originArr)
{
    double mostValue = originArr[0];
    int curSum = 0;

    foreach (double tempValue in originArr)
    {
        if (curSum == 0) mostValue = tempValue;
        curSum += (mostValue == tempValue ? 1 : -1);
    }

    return mostValue;
}

理解后,建议大家自行去验证其正确性。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秦朝炼丹师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值