• 链表反转(逆序)


     在O(1)时间内删除某个节点:http://www.cnblogs.com/youxin/p/3294152.html

    链表逆序(反转)

    可以参考以前写的:4中方式逆序输出链表:http://www.cnblogs.com/youxin/archive/2012/06/01/2531000.html)

    http://blog.sina.com.cn/s/blog_9599e9510101349g.html

    google题目:输入一个链表的头结点,反转该链表,并返回反转后链表的头结点。链表结点定义如下:

    struct ListNode
    {
          int       m_nKey;
          ListNode* m_pNext;
    };

    分析:这是一道广为流传的微软面试题。由于这道题能够很好的反应出程序员思维是否严密,在微软之后已经有很多公司在面试时采用了这道题。

    为了正确地反转一个链表,需要调整指针的指向。与指针操作相关代码总是容易出错的,因此最好在动手写程序之前作全面的分析。在面试的时候不急于动手而是一开始做仔细的分析和设计,将会给面试官留下很好的印象,因为在实际的软件开发中,设计的时间总是比写代码的时间长。与其很快地写出一段漏洞百出的代码,远不如用较多的时间写出一段健壮的代码。

    为了将调整指针这个复杂的过程分析清楚,我们可以借助图形来直观地分析。假设下图中lmn是三个相邻的结点:

    a?b??l  mànà

    假设经过若干操作,我们已经把结点l之前的指针调整完毕,这些结点的m_pNext指针都指向前面一个结点。现在我们遍历到结点m。当然,我们需要把调整结点的m_pNext指针让它指向结点l。但注意一旦调整了指针的指向,链表就断开了,如下图所示:

    a?b?l?m  nà

    因为已经没有指针指向结点n,我们没有办法再遍历到结点n了。因此为了避免链表断开,我们需要在调整mm_pNext之前要把n保存下来。

    接下来我们试着找到反转后链表的头结点。不难分析出反转后链表的头结点是原始链表的尾位结点。什么结点是尾结点?就是m_pNext为空指针的结点。

    基于上述分析,我们不难写出如下代码:

    //返回的不是头结点,是第一个节点
    Node* listReverse(LinkList L)
    {
        LinkList pReversedHead=NULL;
        LinkList p=L;
        LinkList prev=NULL;
        while(p!=NULL)
        {
            LinkList next=p->next;
            // if the next node is null, the currect is the end of original 
                // list, and it's the head of the reversed list
            if(next==NULL)
                pReversedHead=p;
    
              // reverse the linkage between nodes
            p->next=prev;
    
            // move forward on the the list
            prev=p;
            p=next;
        }
        return pReversedHead;
    }

    意上面返回的不是头结点,而是第一个节点.而且由于头结点被当做一个有效的节点 ,所以上面的程序并不好。一个非常好的方法:

    基本原理跟上面的类似,但是想法不同:

    步骤如下:

     1). 若链表为空或只有一个元素,则直接返回;

      2). 设置两个前后相邻的指针p,q. 将p所指向的节点作为q指向节点的后继;

      3). 重复2),直到q为空

      4). 调整链表头和链表尾

    示例:以逆序A->B->C->D为例,图示如下

    Node* listReverse2(LinkList head)
    {
        if(head->next==NULL || head->next->next==NULL)
        {
            return head;
        }
        else
        {
            LinkList p=head->next;
            LinkList q=head->next->next;
            LinkList t=NULL;
            while(q!=NULL)
            {
                t=q->next;//先保留下一个node
                q->next=p;
                
                p=q;
                q=t;
            }
    
            head->next->next=NULL;
            head->next=p;
            return head;
    
        }
    }

    可以看到,没有节点时或只有一个节点时,这时反转后还是一样,所有干脆返回。 这个方法最好。(参考了:

    http://blog.sina.com.cn/s/blog_9599e9510101349g.html)

    上面的还可以用递归,由于有了头结点,就麻烦点。下面的讲了没有头结点的递归和非递归形式:

    将一个链表进行反转(没有头结点),如原来的链表为1->2->3->4->5,经过反转之后的链表为5->4->3->2->1。

    可以有非递归与递归两种方法:

    1.非递归方法:

    Node* reverseLinkList(Node* head)
    {
         Node* p1;
         Node* p2 = NULL;
         Node* tmp;

         p1 = head;
         if(p1)
           p2 = p1->next;
         while(p2)      // 不用测试p1是否为空,如果p2不为空,p1肯定不为空,故p2与p1&&p2的布尔值一致
          {
               tmp = p2->next;
              p2->next = p1;
               p1 = p2;
              p2 = tmp;
         }
         head->next = NULL;      //把反转后的最后一个节点的next域置为NULL
         head = p1;
        return head;
    }

    2.递归方法:

    Node* reverseLinkList_recursion(Node* pNode,Node*& head)
    {
         if(pNode == NULL || pNode->next == NULL)
        {
              head->next = NULL;    //把反转后的最后一个节点的next域置为NULL
              head = pNode;
              return pNode;
         }

         Node* tmpNode = reverseLinkList_recursion(pNode->next,head);//返回原链表中pNode的下一个节点
         tmpNode->next = pNode;
         return pNode;
    }

    递归的方法是把当前节点pNode的后继节点集合看成一个节点,然后与当前节点倒换后继前驱关系。

    这个函数的第二个参数head必须是一个指针的引用,因为在跳出递归的代码段中必须修改head的值,指向新的头结点,如果head指针作为值传递传进reverseLinkList_recursion函数,将不可改变head的值,所以必须传进引用值。

  • 相关阅读:
    NET Core中实现一个Token base的身份认证
    自定义一个服务器感受一下管道是如何监听、接收和响应请求的
    把商品卖给用户
    Mac版Visual Studio预览版
    Docker实战
    大数据的四大特点
    ElasticSearch Nosql
    Oracle和Elasticsearch数据同步
    关于MySql链接url参数的设置 专题
    linux date
  • 原文地址:https://www.cnblogs.com/youxin/p/3285128.html
Copyright © 2020-2023  润新知