3 如何进行程序设计才能最有效地解决复杂编程问题
0 引言
大概是从项目结题之后开始刷题的。自己的目标也很明确,一定要去民企,最好是互联网公司。但是刷了一些题目之后,发现自己虽然能解决一些问题,但是有时候脑子会卡壳,无法从全局上把握问题。思考之后感觉是自己程序设计以及系统性思维的能力还没有上来,因此写个帖子总结一下,此贴会不断更新。以下是本人对“如何进行程序设计才能最有效地解决复杂编程问题”的一点思考。我把解决这个问题分成了四个步骤,在分析的同时会举出具体的例子帮助大家理解。
1 抽象问题具体化
举例:【反转链表问题】定义一个函数,输入一个链表的头结点,反转该链表并输出反转后链表的头结点。链表结点定义如下: struct ListNode { int m_nKey; ListNode* m_pNext; }
面对一个编程问题,我们通常都要采用“抽象问题具体化”的方法使自己更好地理解问题。原因当然很简单,具体的情景是局部的,包含的信息量少,所以大脑接受起来比较容易;而抽象问题往往从多个具体情景中抽象而来,包含的信息量大,大脑理解起来很困难。举个例子,国标字字珠玑,虽然字数很少,但是看起来很是费劲,需要借助他人或者工具书的帮助才能够理解。 我们在面对复杂编程问题时,也要借鉴这种思路。下面开始对问题进行分析。
举例1:输入一个链表,"head->2->3->7->8->NULL",把链表反转之后是"reverseHead->8->7->3->2->NULL",最后返回一个reverseHead即可; 举例2:输入一个空链表,什么操作也不做,返回原来的头结点即可,reverseHead = head,返回即可; 举例3:输入一个只有一个结点的链表,"head->2->NULL",把链表反转之后是 "reverseHead->2->NULL",最后返回一个reverseHead即可; 举例4:输入一个只有两个结点的链表,"head->2->3->NULL",把链表反转之后是"reverseHead->3->2->NULL",最后返回一个reverseHead即可。
2 具体问题抽象化
(1)对具体的例子进行抽象分析
从1中的四个例子可以作出如下分析。
1)问题的输入是什么。“一个链表的头结点”,表明该函数需要传入的参数为一个头结点,类似于 ListNode* head 这种。
2)问题的输出是什么。“反转后链表的头结点”,表明该函数需要return一个头结点,类似于 ListNode* reverseHead 这种。
3)具体的问题是什么。“反转链表”,表明该函数需要进行的操作是将当前结点的指针指向前一个结点。其中包含例外情况,将在下面的测试用例中具体讨论。
4)接口、变量、操作流程图
接口定义为:ListNode* ReverseList(ListNode* head);
变量定义为:p(当前结点),pPre(指向当前结点的结点),pNext(当前结点下一个结点),reverseHead(返回结点)
操作流程图如图所示。
(3)测试用例
设函数的接口为 ListNode* ReverseList(ListNode* head);
测试用例应当分为几类。根据(1)3)例外情况,应当分为以下几类。
1)空链表,直接什么都不做,返回head即可。
/* head = NULL
*/
ListNode* head = NULL; ListNode* reverseNode = ReverseList(head) ;
2)一个结点,直接什么都不做,返回head即可。
/*
* head -> 1 -> NULL
*/
ListNode* head = NULL; // 存疑 ListNode* pNode1;
pNode1->m_mKey = 1; pNode1->m_pNext = NULL;
head = pNode1; ListNode* reverseNode = ReverseList(head) ;
3)两个及两个以上结点,需要遍历结点,遍历一遍即可。
/*
* head -> 1 -> 2 -> NULL
*/
ListNode* head = NULL; // 存疑
ListNode* pNode1,pNode2;
pNode1->m_mKey = 1, pNode2->m_nKey = 2;
pNode1->m_pNext = pNode2, pNode2->m_pNext = NULL;
head = pNode1;
ListNode* reverseNode = ReverseList(head) ;
3 开始demo
ListNode* ReverseList(ListNode* head){
if(head == NULL || head->next == NULL)
return head;
ListNode* pPre = NULL;
ListNode* p = head;
ListNode* pNext;
ListNode* reverseHead = NULL;
while(p->next != NULL){
pNext = p->next;
p->next = pPre;
pPre = p;
p = pNext;
}
p->next = pPre;
reverseHead = p;
return reverseHead;
}
4 写完代码,用2中的测试用例进行测试,保证测试用例全部通过
5 代码升级
(1)流程分析:在操作流程图中,存在两处判断。
1)结点是否为空 || 结点的指针是否为空
2)判断循环是否结束,p->m_next == NULL?
存在冗余,可以修改为判断p == NULL? 另外,出循环的时候无法获取在循环内作一定修改。
(2)修改结果
ListNode* ReverseList(ListNode* head) { ListNode* pFront = NULL; ListNode* p = head; ListNode* reverseHead= NULL; // 关键点 while(p != NULL){ ListNode* pNext = p->next; // 保存当前结点的下一个结点 p->next = pFront; pFront = p; p = pNext; } reverseHead= pFront; // 避免链表断开 return reverseHead; }