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;
    }

 

posted @ 2018-11-17 17:13  十步一杀2017  阅读(636)  评论(0编辑  收藏  举报