剑指offer(三)链表

面试题5:从尾到头打印链表

题目:输入一个链表,从尾到头打印链表每个节点的值。
解题思路:利用栈的先进后出特性来保存节点。

    vector<int> printListFromTailToHead(ListNode* head) {
        vector<int> res;
        stack<ListNode*> nodes;
        ListNode* pNode = head;
        while(pNode != NULL){
            nodes.push(pNode);
            pNode = pNode->next;
        }
        while(!nodes.empty()){
            pNode = nodes.top();
            res.push_back(pNode->val);
            nodes.pop();
        }
        return res;
    }

面试题13:O(1)时间删除链表结点

题目:给定单向链表的头指针和一个节点指针,定义一个函数在O(1)时间删除该节点。
解题思路:分三种情况讨论。

void DeleteNode(ListNode** phead,ListNode* pToBeDelete)  
{  
    if (*phead == NULL || pToBeDelete == NULL)  
        return;  

    //删除非尾节点  
    if (pToBeDelete->_next != NULL)  
    {  
        ListNode* pNext = pToBeDelete->_next;  
        pToBeDelete->_data = pNext->_data;  
        pToBeDelete->_next = pNext->_next;  

        delete pNext;  
        pNext = NULL;  
    }  

    //链表只有一个节点,删除头结点(也是尾节点)  
    else if (*phead == pToBeDelete)  
    {  
        delete pToBeDelete;  
        pToBeDelete = NULL;  
        *phead = NULL;  
    }  

    //链表有多个节点,删除节点是尾节点  
    else  
    {  
        ListNode* cur = *phead;  
        while (cur->_next != pToBeDelete)  
        {  
            cur = cur->_next;  
        }  
        delete pToBeDelete;  
        pToBeDelete = nullptr;  
        cur->_next = nullptr;  
    }  
}  

面试题15:链表中倒数第k个结点

题目:输入一个链表,输出该链表中倒数第k个结点。
解题思路:前后两个指针,前指针先走k-1步,然后一起走,前指针走到尾,后指针就是答案。
举一反三:用一个指针遍历链表不能解决的问题,可以尝试用两个指针,其中一个遍历速度快一点或者先出发。如求链表的中间结点(一个指针一次走一步,另一个指针一次走两步)。如判断一个单向链表是否构成了环形结构。

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
            val(x), next(NULL) {
    }
};*/
class Solution {
public:
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
        if(pListHead == NULL || k == 0)
            return NULL;
        ListNode* pAhead = pListHead;
        ListNode* pBefore = pListHead;
        for(int i=0; i<k-1 ;i++){
            if(pAhead->next != NULL)
                pAhead = pAhead->next;
            else
                return NULL;
        }
        while(pAhead->next != NULL){
            pAhead = pAhead->next;
            pBefore = pBefore->next;
        }
        return pBefore;
    }
};

面试题16:反转链表

题目:输入一个链表,反转链表后,输出链表的所有元素。

class Solution {
public:
    ListNode* ReverseList(ListNode* pHead) {
        ListNode *pReverseHead = NULL;
        ListNode *pNode = pHead;
        ListNode *pPrev = NULL;
        while(pNode != NULL){
            ListNode *pNext = pNode->next;
            if(pNext == NULL)
                pReverseHead = pNode;
            pNode->next = pPrev;
            pPrev = pNode;
            pNode = pNext;
        }
        return pReverseHead;
    }
};

面试题17:合并两个排序的链表

题目:输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
解题思路:递归。需要考虑链表为空。

class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
        if(pHead1 == NULL)
            return pHead2;
        else if(pHead2 == NULL)
            return pHead1;
        ListNode * pMergeHead = NULL;
        if(pHead1->val < pHead2->val){
            pMergeHead = pHead1;
            pMergeHead->next = Merge(pHead1->next, pHead2);
        }else{
            pMergeHead = pHead2;
            pMergeHead->next = Merge(pHead2->next, pHead1);
        }
        return pMergeHead;
    }
};

面试题26:

题目:输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
解题思路:把大问题分解成若干个小问题。

struct RandomListNode {
    int label;
    struct RandomListNode *next, *random;
    RandomListNode(int x) :
            label(x), next(NULL), random(NULL) {
    }
};

class Solution {
public:
    RandomListNode* Clone(RandomListNode* pHead)
    {
        CloneNodes(pHead); //复制原始链表的任意结点N并创建新结点N'并链接在N后面
        ConnectSiblingNodes(pHead); //复制sibling
        return ReconnectNodes(pHead); //拆分原始链表和初始链表
    }
    void CloneNodes(RandomListNode* pHead){
        RandomListNode* pNode = pHead;
        while(pNode != NULL){
            RandomListNode* pCloned = new RandomListNode(pNode->label);
            pCloned->next = pNode->next;
            pNode->next = pCloned;
            pNode = pCloned->next;
        }
    }
    void ConnectSiblingNodes(RandomListNode* pHead){
        RandomListNode* pNode = pHead;
        while(pNode != NULL){
            RandomListNode* pCloned = pNode->next;
            if(pNode->random != NULL)
                pCloned->random = pNode->random->next;
            pNode = pCloned->next;
        }
    }
    RandomListNode* ReconnectNodes(RandomListNode* pHead){
        RandomListNode* pNode = pHead;
        RandomListNode* pClonedHead = NULL;
        RandomListNode* pClonedNode = NULL;
        if(pNode != NULL){
            pClonedHead = pClonedNode = pNode->next;
            pNode->next = pClonedNode->next;
            pNode = pNode->next;
        }
        while(pNode != NULL){
            pClonedNode->next = pNode->next;
            pClonedNode = pClonedNode->next;
            pNode->next = pClonedNode->next;
            pNode = pNode->next;
        }
        return pClonedHead;
    }
};

面试题27:二叉搜索树与双向链表

题目:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
解题思路:按照中序遍历的顺序去递归。

class Solution {
public:
    TreeNode* Convert(TreeNode* pRootOfTree)
    {
        TreeNode *pLastNodeInList = NULL;
        ConvertNode(pRootOfTree,&pLastNodeInList); //pLastNodeInList指向双向链表的尾结点
        TreeNode *pHeadOfList = pLastNodeInList; //需返回头结点
        while(pHeadOfList != NULL && pHeadOfList->left != NULL){
            pHeadOfList = pHeadOfList->left;
        }
        return pHeadOfList;
    }
    void ConvertNode(TreeNode* pNode, TreeNode** pLastNodeInList){
        if(pNode == NULL)
            return;
        TreeNode * pCurrent = pNode;
        if(pCurrent->left != NULL)
            ConvertNode(pCurrent->left,pLastNodeInList);
        pCurrent->left = *pLastNodeInList;
        if(*pLastNodeInList != NULL)
            (*pLastNodeInList)->right = pCurrent;
        *pLastNodeInList = pCurrent;
        if(pCurrent->right != NULL)
            ConvertNode(pCurrent->right,pLastNodeInList);
    }
};

面试题56:链表中环的入口结点

题目:一个链表中包含环,请找出该链表的环的入口结点。
解题思路:这道题包含了1.判断链表是不是环形;2.求这个环形的长度;3.求环形的入口结点。

class Solution {
public:
    ListNode* MeetingNode(ListNode* pHead){
        if(pHead == NULL)
            return NULL;
        ListNode* pSlow = pHead->next;
        if(pSlow == NULL)
            return NULL;
        ListNode* pFast = pSlow->next;
        while(pSlow!=NULL && pFast!=NULL){
            if(pSlow == pFast)
                return pFast;
            pSlow = pSlow->next;
            pFast = pFast->next;
            if(pFast != NULL)
                pFast = pFast->next;
        }
        return NULL;
    }
    ListNode* EntryNodeOfLoop(ListNode* pHead)
    {
        //判断链表是否是环形,若是环形返回环形中的某个结点meetingNode
        ListNode* meetingNode = MeetingNode(pHead);
        if(meetingNode == NULL)
            return NULL;
        //求出环中结点数目m
        ListNode* pNode1 = meetingNode;
        int nodesInLoop = 1;
        while(pNode1->next != meetingNode){
            nodesInLoop++;
            pNode1 = pNode1->next;
        }
        //两个指针,一个指针先走m步后,另一个指针再出发,两个相遇点即是环的入口结点
        pNode1 = pHead;
        for(int i=0;i<nodesInLoop;i++)
            pNode1 = pNode1->next;
        ListNode* pNode2 = pHead;
        while(pNode1 != pNode2){
            pNode1 = pNode1->next;
            pNode2 = pNode2->next;
        }
        return pNode1;
    }
};

面试题57:删除链表中重复的结点

题目:在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5

class Solution {
public:
    ListNode* deleteDuplication(ListNode* pHead)
    {
        if(pHead == NULL)
            return NULL;
        ListNode* pPreNode = NULL; //保存需要删除的结点的前一个结点
        ListNode* pNode = pHead;
        while(pNode != NULL){
            ListNode* pNext = pNode->next;
            bool needDelete = false;
            if(pNext!=NULL && pNext->val==pNode->val)//判断是否有重复的数
                needDelete = true;
            if(!needDelete){ //没有重复的数,就往前推进
                pPreNode = pNode;
                pNode = pNode->next;
            }else{ //有重复的数,删除它们
                int value = pNode->val;
                ListNode* pToBeDel = pNode;
                while(pToBeDel != NULL && pToBeDel->val == value){
                    pNext = pToBeDel->next;
                    delete pToBeDel;
                    pToBeDel = pNext;
                }
                if(pPreNode == NULL) 
                    pHead = pNext;  //头结点重复,重新定义头结点
                else
                    pPreNode->next = pNext;
                pNode = pNext;
            }
        }
        return pHead;
    }
};
阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页