Codewars(3)

前一阵子在codewars上做了一道题,觉得很不错,期间也踩了很多很多坑,在此把我做题的整个过程和牵扯到的知识点做一个汇总,以及我在思考过程中犯的一些错误,希望看完之后能对你有帮助,不要犯我犯的错误,想些这篇已经很久了,一直没有时间。废话不多说,上题!
Input

a string strng of n positive numbers (n = 0 or n >= 2)
Let us call weight of a number the sum of its digits. For example 99 will have “weight” 18, 100 will have “weight” 1.
Two numbers are “close” if the difference of their weights is small.
Task:
For each number in strng calculate its “weight” and then find two numbers of strng that have:
the smallest difference of weights ie that are the closest
with the smallest weights
and with the smallest indices (or ranks, numbered from 0) in strng
Output:
an array of two arrays, each subarray in the following format:
[number-weight, index in strng of the corresponding number, original corresponding number instrng]
or a pair of two subarrays (Haskell, Clojure, FSharp) or an array of tuples (Elixir, C++)
or a (char
) in C mimicking an array of two subarrays
or a matrix in R (2 rows, 3 columns, no columns names)
The two subarrays are sorted in ascending order by their number weights if these weights are different, by their indexes in the string if they have the same weights.
Examples:
Let us call that function closest
*
**strng = “103 123 4444 99 2000”
the weights are 4, 6, 16, 18, 2 (ie 2, 4, 6, 16, 18)
closest should return [[2, 4, 2000], [4, 0, 103]] (or ([2, 4, 2000], [4, 0, 103])
or [{2, 4, 2000}, {4, 0, 103}] or … depending on the language)
because 2000 and 103 have for weight 2 and 4, ther indexes in strng are 4 and 0.
The smallest difference is 2.
4 (for 103) and 6 (for 123) have a difference of 2 too but they are not
the smallest ones with a difference of 2 between their weights.

strng = “80 71 62 53”
All the weights are 8.
closest should return [[8, 0, 80], [8, 1, 71]]
71 and 62 have also:

  • the smallest weights (which is 8 for all)
  • the smallest difference of weights (which is 0 for all pairs)
  • but not the smallest indices in strng.

    strng = “444 2000 445 544”
    the weights are 12, 2, 13, 13 (ie 2, 12, 13, 13)
    closest should return [[13, 2, 445], [13, 3, 544]] or ([13, 2, 445], [13, 3, 544])
    or [{13, 2, 445}, {13, 3, 544}] or …
    444 and 2000 have the smallest weights (12 and 2) but not the smallest difference of weights;
    they are not the closest.
    Here the smallest difference is 0 and in the result the indexes are in ascending order.

    closest(“444 2000 445 644 2001 1002”) --> [[3, 4, 2001], [3, 5, 1002]] or ([3, 4, 2001],
    [3, 5, 1002]]) or [{3, 4, 2001}, {3, 5, 1002}] or …
    Here the smallest difference is 0 and in the result the indexes are in ascending order.

    closest(“239382 162 254765 182 485944 468751 49780 108 54”)
    The weights are: 27, 9, 29, 11, 34, 31, 28, 9, 9.
    closest should return [[9, 1, 162], [9, 7, 108]] or ([9, 1, 162], [9, 7, 108])
    or [{9, 1, 162}, {9, 7, 108}] or …
    108 and 54 have the smallest difference of weights too, they also have
    the smallest weights but they don’t have the smallest ranks in the original string.

    closest(“54 239382 162 254765 182 485944 468751 49780 108”)
    closest should return [[9, 0, 54], [9, 2, 162]] or ([9, 0, 54], [9, 2, 162])
    or [{9, 0, 54}, {9, 2, 162}] or …**

If n == 0, `closest("") should return [] or ([], []) in Haskell, Clojure, FSharp or [{}, {}] in Elixir or { {0,0,0},{0,0,0}} in C++, “[{}, {}]” in Go, “{ {0,0,0},{0,0,0}}” in C, NULL in R.
See Example tests for the format of the results in your language.

这是给定的函数:
char* closest(char* strng) {
// your code
}
在这个函数体里写你的方法。
嗯,题目是很长,开始我本来想跳过的,但是学习就是不断的自虐,不然怎么能够进步!
简而言之,就是给一个字符串,这个字符串由一串数字组成,其中数字之间以空格" "隔开,然后需要计算每个数字的weight,最后将最小的并且是相差最小的两个数字的weight包括其在字符串中是第几个以及它本身是多少合并成一个特定的格式进行输出(这一点我开始并没有注意到,我用自己的方法得到了结果但一直无法通过) 如果有一样的weight那么必须返回字符串中位置靠前的两个。 确实挺坑的当时觉得,后来确实让我学到了不少。好了下面来讲一下我的思路,如果你有更好的思路那么请一定要贴出来。

下面,踩坑现场来了:
1、看完这道题之后,我以为他要我仅仅返回一个二维数组(并没有注意到返回的是一个char型指针)。
那么好了,一维数组返回是很简单,申请内存就OK,但是二维数组怎么返回呢?这里我想当然的用一维数组的方式去声明一个二维数组,像这样:int (*arr)[2][3]=…结果发现其实这样是错的,其中的原因较为复杂,简单来说可以看下面:

为了让C语言函数返回一个二维数组,有些人这样定义函数:
int **foo(int rows, int columns)
然后在函数中费劲心机拼出来一个这样的malloc语句:
int (result)[columns] = (int ()[columns])malloc(rows * columns * sizeof(int));
在函数内读写这个数组发现很正常,等把数组返回给调用函数后,再读写这个数组就crash掉了。
原因其实很简单,函数返回值类型是int **foo,而实际返回值类型是int (*bar)[columns],它们的区别就是foo[1] - foo[0] = sizeof(int *),而bar[1] - bar[0] = sizeof(int [columns])。把bar赋值给foo以后,访问foo[1][0]会访问到啥呢?首先要看foo[1]是个啥,foo[1] = *(foo + 1),那个1虽然看起来是1,但是实际加到foo上的大小实际上是foo指向元素的大小,如果说sizeof(int) = sizeof(int *) = 4的话,也就是foo值增加4,然后对这个地址取值,放到bar中取到了啥呢,实际上取出来的是bar[0][1],所以说,实际上foo[1] = bar[0][1],然而bar[0][1]并不是一个指针或者数组,所以读foo[1][0]就会crash掉。
正确的做法是这样的:

int **result = (int **)malloc(rows * sizeof(int *));
for (int i = 0; i < columns; i++)
{
result[i] = (int *)malloc(columns * sizeof(int));
}
好了,简单介绍完了之后那么我们就可以利用这个二维数组来返回了(并没有注意函数类型)。”解决“了这个问题之后,我就想,既然要计算weight,那么我们就来先计算输入字符串中每个数字串的weight吧,当时感觉好像也不是很难,就创建一个数组,然后把每个数字串的weight保存进去,然后再找最小的两个呗,然而我错了,之后返回的类型中有数字串的下标和原本的数字串,如果单单一遍计算weight的话之后还要重新回到字符串进行比对,这样问题就会很复杂,这个题目给的数字串也很刁钻,如果有一样的话我如何知道我找的这两个最小的到底是那两个。这样明显是行不通的。而且我考虑到为了节省空间,我不想直接声明一个数组,这样它的长度被固定了可能造成空间的浪费。那么先来计算总共多少个数字串需要进行比对吧。

   char *p = strng;
   int number = 1;                   //help us to create the temporary array
   while(*p)
   {
   
       if((*p++)==' ') number++;
   }

这样用指针就可以声明一个大小刚刚好的数组了,那么我们具体要多大的数组呢,考虑到效率的问题,我只想要遍历这个字符串一次,那么我就必须把题目要求的信息全部存起来,那么总共多少个呢,是的,number*3个元素,同时你也明白为什么我这里要声明一个char *p,因为之后要移动strng了,当然处于安全性,你可以在声明一个来保护strng,当然这里无可厚非了。我们用int型指针数组来处理这一段,需要把数字串转为int,C语言中转换还是比较难受的,总的来说这种转换不算难。这一段总体的代码如下:

   char *p = strng;
   int number = 1;                   //help us to create the temporary array
   int arrflag = 0;
   int putInToArrFlag = 0;
   long long strToNum = 0;
   int numberWeight = 0;
   while(*p)
   {
   
       if((*p++)==' ') number++;
   }
   long *arr = (long *)malloc(sizeof(long)*number*3);
   while(*strng)
   {
   
       if(*strng!=' ')
       {
   
           strToNum = strToNum*10 + (*strng-'0');
           numberWeight = numberWeight + (*strng-'0');
           *strng++;
       }
       else
       {
   
           arr[putInToArrFlag++] = numberWeight;
           arr[putInToArrFlag++] = arrflag++;
           arr[putInToArrFlag++] = strToNum;
           strToNum = 0;
           numberWeight = 0;
           *strng++;
       }
   }
   arr[putInToArrFlag++]=numberWeight;
   arr[putInToArrFlag++]=arrflag;
   arr[putInToArrFlag] = strToNum;

当然,你可以处理逻辑把这段代码改写的更好,注意这最后3行是个我踩的最后一个坑,调试程序时才发现的,写程序还是不能太急躁。
好了,这里我们得到了一个arr数组,那么下来就找呗,简单!然而我又想错了,此时的问题简化为,如何在一个数组中找到最小并且相差最小的两个数字并且要找出这两个数字下标还有这两个数字本身,同时还要注意一些刁难你的数字。好吧,条件是有点多,那么一步一步来吧,最开始我想直接找,直接用一个循环只找arr数组中0 ,3, 6,…的两个数字差值最小的两个数,接着把他们放进二维数组呗,然而并不能,会有很多很多的偶然性,能被刁难住!不论我怎么样去处理这个逻辑都不行。难道我得再用一个一维数组把这些weight找出来再排序再到arr中找?这也太脑残了吧!果断放弃。最后觉得还是直接排arr靠谱,但是只排序0,3,6…这些,仅仅把其后的下标和原数字跟着调换就行了,诶呀我去,这一通给我整的。代码如下

int temp1,temp2,temp3;
   for(int i=0;i<number*
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值