专栏首页章鱼的慢慢技术路用数组解决问题(一)

用数组解决问题(一)

由于数组在编程中极为常见,并且数组技巧在非数组场合下也常常被使用,因此数组可以作为用数据结构解决问题的重要练兵场所。

一、基础知识概述

1,存储

这是最基本的操作。数组是一组变量的集合,我们可以对其中的每个变量进行赋值。

tenIntegerArray[0] = 5;  //把整数5赋值给前面所声明的数组的第1个元素 
int tenIntegerArray[10] = {1,2,3,6,12,-57,30987,0,-287,9};  //给数组中的每个元素赋一个特定的值 
int tenIntegerArray[10];
for(int i = 0;i < 10;i++)
    tenIntegerArray[i] = -1;  //把一个数组的10个元素都初始化为-1 

2,复制

复制一个数组,只需要使用一个循环和一条赋值语句,就像初始化数值一样。

int tenIntegerArray[10] = {1,2,3,6,12,-57,30987,0,-287,9};
int secondArray[10];
for(int i = 0;i < 10;i++)
    secondArray[i] = tenIntegerArray[i];

3,提取和搜索

除了能够把值放入数组中之外,我们还需要能够把它们从数组中提取出来。

int num = tenIntegerArray[0];

但通常情况下并没有这么简单。我们常常不知道所需要的位置,必须通过对数组进行搜索才能找到一个特定值的位置。如果数组中的元素并没有特定的顺序,最好执行线性搜索,即从数组的一端开始查看每个元素,直到找到所需要的值。

const int ARRAY_SIZE = 10;  //数组长度 
int intArray[ARRAY_SIZE] = {4,5,9,12,-4,0,-57,30987,-287,1};  //数组本身 
int targeValue = 12;  //在数组中所找到的值
int targePos = 0;  //所找到的值的位置 
while((intArray[targetPos] != targeValue)&&(targePos < ARRAY_SIZE))  //使用ARRAY_SIZE常量限制这个数组的迭代次数 
    targePos++;

有时候,我们所搜索的并不是一个固定的值,而是一个与数组中的其他值存在关系的值。

例如,我们可能想要在数组中搜索最大值。我把完成这个任务的机制称为“山丘之王”,用一个变量表示数组到目前为止所找到的最大值。用一个循环遍历数组中的所有元素,每当遇到一个比当前最大值更大的值时,就把以前的国王从山丘上踢下去并取而代之:

const int ARRAY_SIZE = 10;  //数组长度 
int intArray[ARRAY_SIZE] = {4,5,9,12,-4,0,-57,30987,-287,1};  //数组本身 
int highestValue = intArray[0];  //在声明之时,它被赋值为数组中的第一个元素的值 
for(int i = 1;i < ARRAY_SIZE;i++){  //从数组的第二个元素开始循环 
    if(intArray[i] > highestValue)  //把当前位置的值与highestValue进行比较 
        highestValue = intArray[i];  //适时替换highestValue的值 

4,排序

按特定的顺序排列数据。

用qsort进行快速方便的排序

为了使用qsort,必须编写一个比较函数。这个函数被qsort函数调用,用于比较数组中的两个元素,判断哪个应该出现在排序序列中的更前面。这个函数应该返回一个整数,根据第一个元素是大于、小于或等于第二个元素,这个整数的值分别为正数、负数或零。具体返回的值无关紧要,重要的是它是大于、小于还是等于零。现在,我们通过采用qsort对一个包含10个整数的数组进行排序的简单例子来说明这种排序方法。先写出比较函数:

    int compareFunc(const void * voidA,const void * voidB){
        int * intA = (int *)(voidA);
        int * intA = (int *)(voidB);
        return *intA - *intB;
    }

有了比较函数之后,下面是qsort的一个示例用法:

    const int ARRAY_SIZE = 10;
    int intArray[ARRAY_SIZE] = {87,28,100,78,84,98,75,70,81,68};
    qsort(intArray,ARRAY_SIZE,sizeof(int),compareFunc);

但在有些情况下,需要自己编写排序代码。

我建议是使用一种插入排序算法。它的工作方式与人们在打桥牌时所使用的理牌方式相似:一次抓起一张牌,把它插入到手里这把牌中的适当位置以维持整体的顺序,并移动其余的牌以留出空间。

以下是整数数组的插入排序算法的基本实现:

int start = 0;  //数组中的第一个元素 
int end = ARRAY_SIZE - 1;  //数组中的最后一个元素 
for(int i = start + 1;i <= end;i++){  //外层循环选择下一张需要插入的牌,它被插入到当前按升序排列的一把牌中 
    for(int j = i;j > start&&intArray[j-1] > intArray[j];j--){  //不断地把当前值与它的前一个值进行交换,直到它到达正确的位置 
        int temp = intArray[j-1];  //把当前值交换到数组中的前一个位置 
        intArray[j-1] = intArray[j];
        intArray[j] = temp;
    }
}

5,计算统计数据

与提取操作相似。区别在于它是根据数组中的所有值进行计算所产生的统计数据。例如计算一组学生成绩的平均值:

    const int ARRAY_SIZE = 10;
    int gradeArray[ARRAY_SIZE] = {87,76,100,97,64,83,88,92,74,95};
    double sum = 0;
    for(int i = 0;i < ARRAY_SIZE;i++)
        sum += gradeArray[i];
    double arerage = sum / ARRAY_SIZE;

另一个简单例子就是数据验证。假设有一个称为vendorPayments的包含double值的数组,表示向销售商的支付情况。

二、用数组解决问题

问题:寻找众数

在统计学中,一组值的众数就是最常出现的值。编写代码,处理一个包含了调查数据的数组,确定这个数据集的众数。在这个数组中,接受调查者用1~10范围内的一个数表示一个问题的答案。对于我们而言,如果存在多个众数,可以任选其一。

1,我们首先观察这个问题的一个示例数组以及它的长度常量:

    const int ARRAY_SIZE = 12;
    int surveyData[ARRAY_SIZE] = {4,7,3,8,9,7,3,9,9,3,3,10};

简化后:

    int surveyData[ARRAY_SIZE] = {4,7,7,9,9,9,8,3,3,3,3,10};

现在,2个7出现在一起,3个9也是如此,所有的3也都几张在一起。当数据按照这种方式排列之后,我们就能够线性地处理这个数组并找到众数了。

2,我们先编写一些伪码:(所谓伪码,就是与程序代码相似的语句,它可以提醒我们在编写每条语句时应该要完成什么任务)

    int mostFrequent = ?;
    int highestFrequency = ?;
    int currentFrequency = 0;
    for(int i = 0;i < ARRAY_SIZE;i++){  //处理数组 
        currentFrequency++;  //对当前值的出现次数进行计数的变量的值加1 
        if(surveyData[i] is last occurrence of a value){  //检查,观察是否到达了一个特定值的最后一次出现 
            if(currentFrequency > highestFrequency){  //如果是,这个值就成为新的最常见的值 
                highestFrequency = currentFrequency;
                mostFrequent = surveyData[i];
            }
            currentFrequency = 0;  //由于下一个被读取的值将是新值的第一次出现,因此我们把计数器重置为0 
        }
    }

,3,我们回到前面暂时跳过的if语句的逻辑。

怎么才能知道一个值在数组中最后一次出现呢?

由于数组中的值已经分组,因此当数组中的下一个值与当前值不同时(surveyData[i] 不等于 surveyData[i+1]),我们就知道现在是当前值的最后一次出现。

4,现在可以考虑变量初始值的问题。

现在,“当前的最常见”值用2个变量表示,mostFrequent表示值本身,highestFrequency表示它的出现次数。如果能把mostFrequent初始化为数组中所出现的第一个值并把highestFrequency初始化为这个值在数组中的出现次数当然是最好不过了,但在进入循环并开始计数之前,还没有办法确定第一个值的出现次数。此时,我们可能会想到,不管第一个值的出现次数是多少,它总是大于零。因此,我们把highestFrequency变量值初始化为零。当我们到达第一个值的最后一次出现时,这段代码就会把highestFrequency变量的值替换为第一个值的出现次数。完整代码如下:

    int mostFrequent;
    int highestFrequency = 0;
    int currentFrequency = 0;
    for(int i = 0;i < ARRAY_SIZE;i++){  
        currentFrequency++;  
        //if(surveyData[i] is last occurrence of a value)
        if(i == ARRAY_SIZE - 1 || surveyData[i] != surveyData[i+1]){  
            if(currentFrequency > highestFrequency){   
                highestFrequency = currentFrequency;
                mostFrequent = surveyData[i];
            }
            currentFrequency = 0;  
        }
    }

对数组进行排序的问题,我们只要在代码开始时调用qsort就可以了:

    qosrt(surveyData,ARRAY_SIZE,sizeof(int),comareFunc);

 5,重构

是不是存在这样一个数组,我们可以对它应用“寻找最大值”方法,从而得到调查数据的众数?答案是肯定的。

我们所需要的数组是surveyData数组的柱状图。柱状图就是显示在底层数据中的不同数据的出现频率的图形。我们所需要的数组就是表示这种柱状图的数据。换句话说,我们将在一个长度为10个元素的数组中存储1到10的每个值在surveyData数组中的出现频率。以下是创建柱状图的代码:

    const int MAX_RESPONSE = 10;
    int histogram[MAX _RESPONSE];  //保存柱状图数据的数组 
    for(int i = 0;i < MAX_RESPONSE;i++){  //把数组的每个值初始化为0 
        histogram[i] = 0;
    }
    for(int i = 0;i < ARRAY_SIZE;i++){  //对surveyData数组中的每个值的出现次数进行计数 
        histogram[surveyData[i] - 1]++;
    }

(注意:柱状图代码是独立编写的,这样就可以对它进行单独的测试。)

对上面的代码进行了测试之后,现在就可以搜索histogram数组的最大值了:

    int mostFrequent = 0;  //初始化 
    for(int i = 1;i < MAX_RESPONSE;i++){
        if(histogram[i] > histogram[mostFrequent])
            mostFrequent = i;
    }
    mostFrequent++;

注意:mostFrequent将是histogram数组中最大值的位置,而不是最大值本身。  因此,我们把它初始化为0而不是location[0]的值;在if语句中,我们把它与histogram[mostFrequent]进行比较,而不是与mostFrequent本身进行比较。在找到一个更大值时,我们把mostFrequent赋值为1而不是histogram[i]。最后,我们把mostFrequent的值增加1,这正好与前一个循环中减去1得到正确的数组位置的操作相反。例如,如果把mostFrequent告诉我们最大的数组位置是5,表示调查数据中最常出现的项是6。

总结

柱状图解决方案的复杂度随着SurveyData数组的元素数量增加而线性增长,这也是我们能够期待的最好结果了。因此,相比原来的排序方法,它是更好的解决方案。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 2013年第四届蓝桥杯C/C++B组省赛题目解析

    Zoctopus
  • 火柴棍等式

    Zoctopus
  • 2015年第六届蓝桥杯C/C++B组省赛题目解析

    Zoctopus
  • 二维数组-c语言学习笔记

    Youngxj
  • C语言入门系列之6.一维和二维数组

    在程序设计中,为了处理方便,把具有相同类型的若干变量按有序的形式组织起来,这些按序排列的同类数据元素的集合称为数组。 在C语言中,数组属于构造数据类型; 数...

    cutercorley
  • Linux编译C++

    1)此时脚本开始运行 2)选择python3解释编译ycm文件 此时脚本文件会问你是选择python2还是python3来编译ycm文件?我在这里选择3...

    承苏凯
  • Golang面试题

    最近在很多地方看到了golang的面试题,看到了很多人对Golang的面试题心存恐惧,也是为了复习基础,我把解题的过程总结下来。 面试题 写出下面代码输出内容。...

    李海彬
  • Android 媒体开发之MediaPlayer状态机接口方法实例解析

    MediaPlayer 对象声明周期 : 从 Idle 到 End 状态就是 MediaPlayer 整个生命周期;

    砸漏
  • 自动化构建工具Gradle配置与使用

    ? Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化构建工具。它使用一种基于Groovy的特定领域语言(DSL)来声明项目...

    吴柯
  • Oracle 使用concat函数需要注意的地方

    oracle中使用concatconcat只能连接两个字符,而“||”可以连接多个字符。

    斯文的程序

扫码关注云+社区

领取腾讯云代金券