(一)选票统计问题
问题:
现有10个候选人,对这10个候选人分别标号为1–10;有两万人对他们进行选举,产生了两万张选票;每张选票上写的分别是1–10中的其中一个数字,以表示支持那一位候选人,先要求对这两万张选票进行统计,得到每个候选人的票数。
怎么实现?
一般思路:
定义一个11个空间大小的数组(a[0]不使用),每个空间代表一个一位候选人的票数计数。然后对两万张选票进行遍历,票上数字是几,对应数组元素就加一。
代码实现如下:
int main()
{
int num[11];
int x;
for (int i; i <= 20000; i++)
{
scanf("%d", &x);
switch (x)
{
case 1:
num[1]++;
break;
case 2:
num[2]++;
break;
.
.
.
.
}
}
}
其实,在候选人数量仅仅为10这样小的数量,这样方法没问题。
那如果候选人有100人,那是否要写100个case语句呢?
另一种方法:
其实,可以这样实现:
将输入的数字同时作为数组下标,实现对代码简化。实现代码如下:
int main()
{
int num[11];
int x;
for (int i; i <= 20000; i++)
{
scanf("%d", &x);
num[x]++;
}
}
这样,那怕一万个候选人,也只需要改一下申请数组空间就行了。实现代码简直简单到不行。
(二)稀疏矩阵压缩存储形式的转置
稀疏矩阵的压缩存储
稀疏矩阵是指矩阵中只有较少的部分为非零部分,其他部分均为0的矩阵;因为非零部分较少,若按正常存储时,就需要存储大量的零元素。其实,这样浪费了存储空间。为此,我们仅仅存储矩阵中的非零元素的信息就行。以实现对矩阵的压缩。每一个非零元素的存储包括:行信息、列信息、值信息。故一个稀疏矩阵存储下来就是如下这样的二维数组:
因为是转置,其实就是行列的互换.
但是,我们希望在存储的稀疏矩阵依旧是按行存储的,也就是所=说,希望转置过后,存储空间里的顺序依然是按照第一行元素、第二行元素、第三行元素、…这个顺序的。
其实,保持这个按行存取的顺序才是我们分出两个实现方法的原因。
转置方法一:
顺序遍历列方法
为了保持向上面所说的,要实现转置后,矩阵依旧按照行顺序存储,所以我们由小到大依次遍历转置前的矩阵,分别扫描,第一遍,按列扫描列为1的元素,找到了就放进去;第二遍,再次按列扫描列为2的元素,找到了放进去;第三遍…
按照这个顺序依次扫描,依次填入,就能够实现对矩阵转置。但是,这样有一个问题,当非零元素有len个,那么就要对相同的数组进行反复的扫描,这样并不好。一、时间复杂度是len*len这么高;二、反复遍历同一个数据,意义不大。
是否能够实现依次扫描就能够完成转置呢?这样就引出了我们的另一个方法。
转置方法二:
按照上面所说,该方法要是实现对数组的依次遍历就可以完成转置。
首先对新数组的每一行(原数组每一列)进行计数,记录第一列、第二列、第三列分别由多少个非零元素。这里计数就用到了前面我们谈到的选票统计问题,使代码简单。
然后由有一个位置信息,他讲新数组的第一行的起始位置定位1,然后其他行,是改行前一行的行元素个数加前一行的起始位置。
如下图所示:
这两个运算都是对数组进行预处理。为什么这样处理,因为,经过处理后。我们能知道每一行的起始位置在新数组的第几行,所以在遍历旧数组时,见到第n列(新数组的第n行)的元素时,直接将该元素放于新数组的第n列(新数组的第n行)的起始位置,然后当再发现第二个第n列(新数组的第n行)的元素时,就在第n列(新数组的第n行)的起始位置后加一放置。
这样就实现了对原数组的转置。
该算法仅仅只需要对原数组遍历一次,就可以实现对数组的转置。时间复杂度为len。