等价类划分法是将程序所有可能的输入数据(有效和无效)划分为若干个等价类,然后从每个部分中选取具有代表性的数据当做测试用例进行合理分类,以达到选用适当数据代表所有数据集的效果。这样,在测试或者开发过程中做到提前规划,可以减少程序漏洞,使得具体工作更加系统化,提高工作效率。自己本身是做测试工作的,虽然做了好多年,遗憾的是对于测试理论方面的东西没有深入的理解和实践,仅限于工作业务方面的熟悉,希望自己作为重点来提升自己。最近在做leetcode,结合实践总结下自己的心得。
其中一道题是删除排序列表中的重复元素,要求是给定一个排序链表,删除所有含有重复数字的节点,仅保留原始链表中没有重复出现的数字(比如输入[1,2,2,3,3,4,5],输出[1,4,5])。从黑盒角度看,输入至少应该包含以下几类:
输入分类 | 具体用例 | 预期输出 | |||
---|---|---|---|---|---|
空链表 | 没有元素 | [] | [] | ||
非空链表 | 无重复元素 | 有一个节点 | [1] | [1] | |
有多个节点 | [1,3,4,5,6,7] | [1,3,4,5,6,7] | |||
有重复元素 | 链表头部重复 | 单个元素重复 | [1,1,2,3,4] | [2,3,4] | |
多个元素重复 | [1,1,2,2,2,4,5] | [4,5] | |||
链表中间重复 | 单个元素重复 | [1,2,2,4,5] | [1,4,5] | ||
多个元素重复 | [1,2,2,3,3,3,4,5] | [1,4,5] | |||
链表尾部重复 | 单个元素重复 | [9,8,4,4,4] | [9,8] | ||
多个元素重复 | [9,8,8,4,4] | [9] | |||
仅有重复元素 | 单个元素重复 | [2,2,2,2,2] | [] | ||
多个元素重复 | [3,3,4,4,4] | [] |
此外还应该增加一些性能类测试,比如大量数据输入时的时间和空间复杂度是否满足使用。
在编程时,主要的逻辑流程就是设定三个临时指针变量分别用于记录前一节点(pre),当前节点(curr),下一个节点(next),另外有一个单独变量(del)用于记录重复节点的值,一个bool型变量has记录前面是否有重复;遍历next节点,如果该节点的值与del一致,则删除next节点,同时置has为真,next更新为下一节点。如果该节点值与del不一致,当has为假,不需要删除节点,三个节点一起后移;当has为真,表明前一个元素对应的值为重复元素,则删除curr节点。每次删除节点后要使用free()函数释放节点内存,同时将上一节点的next指针地址复制为下一个节点。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* deleteDuplicates(struct ListNode *test) {
struct ListNode *n_pre, *n_curr, *n_next;
bool has = 0;
if((test) && (test->next!=NULL)){
int del = test->val;
n_next = test->next;
n_curr = test;
n_pre = NULL;
while(n_next->next != NULL){
if(n_next->val == del){
has = 1;
n_curr->next = n_next->next;
free(n_next);
n_next = n_curr->next;
}
else{
if(has){
if(n_pre!=NULL)
n_pre ->next = n_next;
if(n_curr==test)
test=n_next;
free(n_curr);
}
else
n_pre = n_curr;
del = n_next ->val;
has = 0;
n_curr = n_next;
n_next = n_next->next;
}
}
if(n_curr->val == n_next->val){
if(n_pre!=n_curr){
if(n_pre)
n_pre ->next = NULL;
else
return NULL;
}
else
return NULL;
}
if ((has) && (n_curr->val != n_next->val)){
if(n_pre)
n_pre->next = n_next;
else
test = n_next;
}
return test;
}
else
return test;
}
还有一道题是两数之和,是一个构建链表的过程,跟这个题有点相反的意思,也是一个比较不错的典型。在完成后执行各种输入后输出都正常,但提交结果时出现了错误member access within misaligned address 0x000000000031 for type 'struct ListNode', which requires 8 byte alignment,后来将创建节点的next指针指向NULL,提交正确。
在编码过程中的一些收获:1、指针定义后一定要初始化,如果暂时没有指向内容的话就设置为NULL;
2、在链表循环中切忌给节点指针随便赋值,因为改变的是上一节点next指向的节点。
在编程时也应该综合以上分类,在逻辑判断时同时考虑各种情况避免遗漏。实际测试和开发目的一致,都是让程序尽可能的稳定健壮。后期做类似逻辑算法类题目,可以先等价划分类,之后依据各类写判断逻辑实现代码,然后设计具体用例测试,通过之后提交答案。