• 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;
        }
  • 相关阅读:
    【centos】centos中添加一个新用户,并授权
    linux基础(10)-导航菜单
    linux基础(9)-获取时间
    linux基础(8)-颜色显示
    linux基础(8)-文件处理(awk 、sed、grep)
    linux基础(7)-IO重定向
    linux基础(6)-shell编程
    linux基础(5)-用户及权限
    linux基础(4)-常用命令
    linux基础(3)-java安装
  • 原文地址:https://www.cnblogs.com/ghjnwk/p/9974746.html
Copyright © 2020-2023  润新知