题目15 链表中倒数第K个节点

/////////////////////////////////////////////////////////////////////////////////////
// 5. 题目15 链表中倒数第K个节点
//时间复杂度:O(n),空间复杂度O(n)

ListNode<int>* KthNodeFromEnd(ListNode<int>* pNode, int k)
{
    if (pNode == NULL || k <= 0)
    {
        return NULL;
    }

    ListNode<int>* p = pNode;
    stack<ListNode<int>*> stStack;

    // 把链表数据全部压入栈
    while (p)
    {
        stStack.push(p);
        p = p->m_pNextNode;
    }

    // 检查K的合法性
    if (k > (int)stStack.size())
    {
        return NULL;
    }

    while (!stStack.empty() && --k > 0)
    {
        stStack.pop();
    }

    return stStack.top();
}

// 方法二:倒序第K个元素 --> 正序(N - K)
//时间复杂度:O(n), 空间复杂度O(1) --> 需要遍历两次链表
ListNode<int>* KthNodeFromEnd_2(ListNode<int>* pNode, int k)
{
    if (NULL == pNode || k <= 0)
    {
        return NULL;
    }

    ListNode<int>* p = pNode;
    int iNodeCount = 0;

    while (p)
    {
        iNodeCount++;
        p = p->m_pNextNode;
    }

    int iLoopCount = iNodeCount - k;
    if (iLoopCount < 0)
    {
        return NULL;
    }

    while (pNode && iLoopCount-- > 0)
    {
        pNode = pNode->m_pNextNode;
    }

    return pNode;
}

//方法三: 定义两个指针,两个指针之间相差(K-1),第一个指针遍历完,第二个指针指向的就是需要的节点
//时间复杂度:O(n),空间复杂度 : O(1)  --> 只遍历一遍链表
ListNode<int>* KthNodeFromEnd_3(ListNode<int>* pNode, int k)
{
    if (pNode == NULL || k <= 0)
    {
        return NULL;
    }

    ListNode<int>* p1 = pNode;
    ListNode<int>* p2 = pNode;

    while (p1 && p1)
    {
        if (--k < 0)
        {
            p2 = p2->m_pNextNode;
        }

        p1 = p1->m_pNextNode;
    }

    // K 超出了链表个数
    if (k > 0 && p1 == NULL)
    {
        return NULL;
    }

    return p2;
}

// 扩展一:
// 求链表的中间节点,如果链表中节点数为奇数,返回中间节点,如果为偶数,返回中间两个的任意一个
// 声明两个指针:p1+1, p2+2 --> p1
// 时间复杂度: O(n),空间复杂度:O(1)
ListNode<int>* GetListMiddleNode(ListNode<int>* pNode)
{
    if (NULL == pNode)
    {
        return NULL;
    }

    ListNode<int>* p1 = pNode;
    ListNode<int>* p2 = pNode;

    while (p1 && p2)
    {
        //这两个顺序不能变,否则链表个数为奇数时,P1会多移动一位!!!
        // p2 + 2
        if (!p2->m_pNextNode)
        {
            break;
        }

        p2 = p2->m_pNextNode->m_pNextNode;

        // p1 + 1
        p1 = p1->m_pNextNode;
    }

    return p1; 
}


void KthNodeFromEndTestFunc()
{
    cout << "\n\n --------------- KthNodeFromEndTestFunc Start -------------->" << endl;

    int aiArray[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
    int iLen = sizeof(aiArray) / sizeof(int);
    TRAVERSAL_ARRAY(aiArray, iLen);

    CSingleList<int>* pList = new CSingleList<int>();
    if (!pList)
    {
        return;
    }

    for (int i = 0; i < iLen; i++)
    {
        pList->Insert(aiArray[i]);
    }
    pList->Traversal();

    int k = 16;
    cout << "方法一: 打印链表倒数第K个数: " << endl;
    ListNode<int>* pKNode = KthNodeFromEnd(pList->GetHeadNode(), k);
    if (pKNode)
    {
        printf("倒数第%d个值为: %d\n", k, pKNode->m_stData);
    }

    cout << "方法二: 打印链表倒数第K个数: " << endl;
    pKNode = KthNodeFromEnd_2(pList->GetHeadNode(), k);
    if (pKNode)
    {
        printf("倒数第%d个值为: %d\n", k, pKNode->m_stData);
    }

    cout << "方法三: 打印链表倒数第K个数: " << endl;
    pKNode = KthNodeFromEnd_3(pList->GetHeadNode(), k);
    if (pKNode)
    {
        printf("倒数第%d个值为: %d\n", k, pKNode->m_stData);
    }

    cout << "扩展一: 打印链表中间节点: " << endl;
    pKNode = GetListMiddleNode(pList->GetHeadNode());
    if (pKNode)
    {
        printf("链表中间%d个值为: %d\n", iLen / 2, pKNode->m_stData);
    }

    // 释放内存
    SAVE_DELETE(pList);

    cout << "\n\n --------------- KthNodeFromEndTestFunc End -------------->" << endl;

}
posted @ 2019-07-28 13:37  VIP丶可乐  阅读(109)  评论(0编辑  收藏  举报