面试题 5,从尾到头打印链表 (链表系列)

注意链表本身没有prev指针。

解法a.

定义指针p1,再定义指针P。

循环开始,p1从头节点开始遍历,而指针P用来存储已经打印出来的节点地址。

当p1->m_pNext节点地址和P相等时,打印p1节点所在的值。P的值改成p1的地址。

重新循环。

这个方法的时间复杂度为O(N^2)

需要考虑只有一个节点,和输入空指针(没有节点)的边界情况,代码:

void PrintReversely(ListNode *pHead){
    if(pHead != null){
        if(pHead -> m_pNext == null){
            printf("%d\n",pHead -> m_nKey);
        }else{
            ListNode *p1 = phead;
            ListNode *P = null;
            while(P != phead -> m_pNext){
                p1 = phead;
                while(p1 -> m_pNext != P){
                    p1 = p1 -> m_pNext;
                }
                printf("%d\n",p1 -> m_nKey);
                P = p1;
            }
            printf("%d\n",p1 -> m_nKey);    //输出头节点的值
        }
    }
}

写完后发现只有一个节点的边界情况已经包含else中,因此最外层if判断可以省略,省略后的代码:

void PrintReversely(ListNode *pHead){
    if(pHead != null){
        ListNode *p1 = phead;
        ListNode *P = null;
        while(P != phead -> m_pNext){
            p1 = phead;
            while(p1 -> m_pNext != P){
                p1 = p1 -> m_pNext;
            }
            printf("%d\n",p1 -> m_nKey);
            P = p1;
        }
        printf("%d\n",p1 -> m_nKey);    //输出头节点的值
    }
}

解法b.

如果想降低时间复杂度,那可以使用以空间换时间的做法。链表的遍历只能从头到尾,而输出是越往后越先输出,因此采用先进后出的栈来临时存放遍历过的结点值。

代码:

void PrintReverselyStack(ListNode *pHead){
    std::Stack<int> stack;
    
    ListNode *p1 = phead;
    while(null != p1){
        stack.push(p1 -> m_nKey);
        p1 = p1 -> m_pNext;
    }
    
    while(!stack.empty()){
        printf("%d\n",stack.top());
        stack.pop();
    }
}

时间复杂度O(N)

当然,也能用递归来实现,代码(错误):

void PrintReverselyRecursive(ListNode *pHead){
    if(null != pHead -> m_pNext){
        PrintReverselyRecursive(pHead -> m_pNext);
    }
    printf("%d\n",pHead -> m_nKey);
}

此代码的错误之处在于又没有考虑空指针输入。

改正:

void PrintReverselyRecursive(ListNode *pHead){
    if(null != pHead){
        if(null != pHead -> m_pNext){
            PrintReverselyRecursive(pHead -> m_pNext);
        }
        printf("%d\n",pHead -> m_nKey);
    }
}

递归的代码相对比较简洁,但是递归调用的缺陷在于,如果链表非常长,会让函数递归调用的层次很深,从而导致函数栈溢出。

因此使用栈更加安全些。

最后,做链表题时,一定不要忘掉考虑空指针的输入情况!

posted on 2013-11-25 11:36  Felix Fang  阅读(131)  评论(0)    收藏  举报

导航