3.4代码的鲁棒性之链表中倒数第k个节点+链表中环的入口节点+反转链表+合并两个排序的链表

面试题22:链表中倒数第k个节点.

下面的这个解法是比较容易想到的,但是存在着几个问题:没有处理节点的数目少于k的情况;没有处理k=0及k为负数的情况。在剑指offer中,k是无符号类型。输入k=0或者k=-1没有意义,可以返回空指针。如果链表的节点数少于k,那么在for循环中q会变成空指针,一旦到空指针了就表明k超过了节点的个数了,此时返回nullptr即可。

class Solution {
public:
    ListNode* getKthFromEnd(ListNode* head, int k) {
        if(head==NULL) return NULL;
        ListNode *p=head;
        ListNode *q=head;//q是快指针
        for(int i=0;i<k-1;i++){
            q=q->next;
        }
        while(q->next!=NULL){
            q=q->next;
            p=p->next;
        }
        return p;
    }
};

改了上面的错误后:

class Solution {
public:
    ListNode* getKthFromEnd(ListNode* head, int k) {
        if(head==NULL||k<=0) return NULL;
        ListNode *p=head;
        ListNode *q=head;//q是快指针
        for(int i=0;i<k-1;i++){
            if(q->next!=NULL){
                q=q->next;
            }
            else{
                return NULL;
            }
        }
        while(q->next!=NULL){
            q=q->next;
            p=p->next;
        }
        return p;
    }
};

面试题23:链表中环的入口节点

题目:如果一个链表中包含环,如何找出环的入口节点? 第一步是先确定这个链表中包含环吗。第二步是找到环的入口。

分析,如果头指针为空或者只有一个结点则直接判断为不是环形链表。如果是快指针最先到达尾部,说明不是环形链表;如果快慢指针相遇了则说明是环形链表。在有“环”的情况下,快慢指针是一定会相遇的。

class Solution {
public:
    ListNode* EntryNodeOfLoop(ListNode* pHead) {
//首先判断有没有环,接着找到入口,快慢指针相遇了就说明存在环
        if(pHead==NULL||pHead->next==NULL) return NULL;
        ListNode* fast=pHead;
        ListNode* slow=pHead;
        while(1){
            if(fast==NULL||fast->next==NULL){
                return NULL;
            }            
            fast=fast->next->next;
            slow=slow->next;
            if(slow==fast) {
                break;
            }
        }
        fast=pHead;
        while(slow!=fast){
            fast=fast->next;
            slow=slow->next;
        }
        return slow;
    }
};

剑指offer上给出的解法:和上面的有不同,指针p1先在链表上向前移动b步;然后指针p1和p2以相同的速度在链表上向前移动,直到它们相遇。它们相遇的结点就是环的入口节点。(即让p1先走b步,再走a步这样的话,如果链表中存在环的话就一定走到了入口节点处了!)

面试题24:反转链表

要考虑到输入的链表头指针是nullptr的情况,输入的链表只有一个结点的特殊情况。

在这道题目中需要定义三个指针,一个指向当前遍历的结点,一个指向它的前一个节点一个指向它的后一个节点。不难分析出反转后链表的头结点是原始链表的尾结点。什么节点时尾结点?自然是Next指针为空指针的结点。在我一开始写的代码中就出现了链表断裂。

class Solution {
public:
    ListNode* ReverseList(ListNode* pHead) {
        if(pHead==NULL||pHead->next==NULL) return pHead;
        ListNode* pre=nullptr;
        ListNode* cur=pHead;
        //ListNode* pnext=pHead->next;没有必要把pnext这样写,这会儿还是可以通过cur找到它后面的结点的
        while(cur!=nullptr){
            ListNode* temp=cur->next;
            cur->next=pre;
            pre=cur;
            cur=temp;
        }
        return pre;
    }
};

还是没有很理解本题的递归解法。

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

关于鲁棒性的问题:每当代码试图访问空指针指向的内存时程序就会崩溃,从而导致鲁棒性问题。这里回顾一下递归的解法,也是按照剑指上给出的。

提交之前先用想的一些特殊的输入来测试一下:比如两个链表的一个或两个头结点为空指针;两个链表中只有一个节点。

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

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值