剑指offer C++题解 3.4代码的鲁棒性

鲁棒是英文Robust的音译,有时也翻译成健壮性。所谓的鲁棒性是指程序能够判断输入是否合乎规范要求,并对不合要求的输入予以合理的处理。
容错性

15 链表中倒数第k个结点

 
 struct ListNode {
     int val;
     ListNode *next;
     ListNode(int x) : val(x), next(NULL) {}
  };
 
class Solution {
public:
//由于返回的是倒数第k个结点,所以返回类型是ListNode
    ListNode* getKthFromEnd(ListNode* head, int k) {
        //如果头指针为空或者返回倒数第0个节点,则返回空指针
        if (head == nullptr || k == 0) { 
            return nullptr;
        }

        ListNode *pHead = head;
        ListNode *pBehind = nullptr;

        /*
         *前面的指针先走k-1步时,后面的指针
         *在第0步(pBehind = head),随后以两
         *者相距k-1步的距离同速向前走。
        */
        for (int i = 0; i < k - 1; ++i) {
            if (pHead -> next != nullptr) {
                pHead = pHead -> next;
            } else { //链表总长度少于k个
                return nullptr;
            }
        }

        pBehind = head;
        while (pHead -> next != nullptr) {
            pHead = pHead -> next;
            pBehind = pBehind -> next;
        }

        return pBehind;
    }
};

16 反转链表
方法一:利用三个指针进行反转链表

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* ReverseList(ListNode* pHead) 
    {
    //输入的链表头指针为NULL或者整个链表只有一个结点时
        if(pHead == nullptr || pHead->next == nullptr)
            return pHead;
            
        ListNode *pReversedHead = nullptr;//后一个结点
        ListNode *pPre = nullptr;//前一个结点
        ListNode *pNode = pHead;//当前结点
        ListNode *pNext = nullptr;
        
        while (pNode != nullptr) {
            pNext = pNode->next; //pNext保存pNode的下一个结点
            if (pNext == nullptr) //pNode的下一个结点为空时,为尾结点
                pReversedHead = pNode;//记录反转后的头结点
            
            pNode->next = pPre; //指针反向,
            
            pPre = pNode; //移动pPre
            pNode = pNext; //移动pNode
        }
        return pReversedHead;//返回头结点(即原来的尾结点)
    }
};

方法二:利用递归

class Solution {
public:
    ListNode* ReverseList(ListNode* pHead) {
        //如果链表为空或者链表中只有一个元素
        if(pHead==NULL||pHead->next==NULL) return pHead;
         
        //先反转后面的链表,走到链表的末端结点
        ListNode* pReverseNode=ReverseList(pHead->next);
         
        //再将当前节点设置为后面节点的后续节点
        pHead->next->next=pHead;
        pHead->next=NULL;
         
        return pReverseNode;
         
    }
};

键盘输入创建链表

#include<iostream>
using namespace std;

struct ListNode
{
	int val;
	ListNode* next;
	ListNode() :val(0), next(nullptr) {};
	ListNode(int x) : val(x), next(nullptr) {};
};
ListNode* create(int n)
{
	auto dummy = new ListNode(0);
	auto pre = dummy;
	for (int i = 0; i < n; i++)
	{
		auto p = new ListNode(0);
		cin >> p->val;
		pre->next = p;
		pre = pre->next;
	}
	return dummy->next;
}
void print(ListNode* head)
{
	while (head)
	{
		if (head->next == NULL)
			cout << head->val<<"-> NULL";
		else
			cout << head->val << "-> ";
		head = head->next;
	}
	cout << endl;
}
int main()
{
	int n;
	cout << "链表的长度为:" << endl;
	cin >> n;
	cout << endl;
	cout << "依次输入元素的值(enter键换行)" << endl;
	auto head = create(n);
	cout << "链表输出为:" << endl;
	print(head);
	system("pause");
	return 0;
}




合并两个排序的链表

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
                               //递归!!!!
class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {    //鲁棒性,①链表1为空时,返回链表2;②链表2为空时,返回链表1;③链表1、2均为空时,返回空表;
        if(pHead1 == nullptr)
            return pHead2;
        else if(pHead2 == nullptr)
            return pHead1;
        
        ListNode* pMergeHead = nullptr;//新链表的指针
        if(pHead1->val < pHead2->val){    //每次选出剩下两个链表合并后的头指针
            pMergeHead = pHead1;
            pMergeHead->next = Merge(pHead1->next,pHead2);
        }
            
        else{    //包括小于等于
            pMergeHead = pHead2;
            pMergeHead->next = Merge(pHead1,pHead2->next);
        }
         // return pMergeHead;
    }
};


树的子结构

我们注意到上述代码有多处判断一个指针是不是NULL,这样做是为了避免试图访问空指针而造成程序崩溃,同时也设置了递归调用的退出条件。在写遍历树的代码的时候一定要高度警惕,在每一处需要访问地址的时候都要问自己这个地址有没有可能是NULL,如果是NULL该怎么处理。

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};
*/

//上述代码中,我们递归调用HasSubtree遍历二叉树A。如果发现某一结点的值和树B的头结点的值相同,则调用DoesTree1HaveTree2,做第二步判断。

bool HasSubtree(BinaryTreeNode* pRoot1, BinaryTreeNode* pRoot2)
{
    bool result = false;
	
	//当Tree1和Tree2都不为零的时候,才进行比较。否则直接返回false
    if(pRoot1 != nullptr && pRoot2 != nullptr)
    {
		//如果找到了对应Tree2的根节点的点
        if(Equal(pRoot1->m_dbValue, pRoot2->m_dbValue))
			//以这个根节点为为起点判断是否包含Tree2
			result = DoesTree1HaveTree2(pRoot1, pRoot2);
		//如果找不到,那么就再去root的左儿子当作起点,去判断是否包含Tree2
		if(!result) 
            result = HasSubtree(pRoot1->m_pLeft, pRoot2);
		//如果还找不到,那么就再去root的右儿子当作起点,去判断是否包含Tree2
		if(!result)
            result = HasSubtree(pRoot1->m_pRight, pRoot2);
    }

    return result;
}
//第二步是判断树A中以R为根结点的子树是不是和树B具有相同的结构。同样,我们也可以用递归的思路来考虑:如果结点R的值和树B的根结点不相同,则以R为根结点的子树和树B肯定不具有相同的结点;如果它们的值相同,则递归地判断它们各自的左右结点的值是不是相同。递归的终止条件是我们到达了树A或者树B的叶结点。参考代码如下:

bool DoesTree1HaveTree2(BinaryTreeNode* pRoot1, BinaryTreeNode* pRoot2)
{	//如果Tree2已经遍历完了都能对应的上,返回true
    if(pRoot2 == nullptr)
        return true;
	//如果Tree2还没有遍历完,Tree1却遍历完了。返回false
    if(pRoot1 == nullptr)
    //此条语句与上条语句不能调换顺序,否则{8,8,7,9,2,#,#,#,#,4,7},{8,9,2}不通过测试
        return false;             
	//如果其中有一个点没有对应上,返回false
    if(!Equal(pRoot1->m_dbValue, pRoot2->m_dbValue))
        return false;
	//如果根节点对应的上,那么就分别去子节点里面匹配
    return DoesTree1HaveTree2(pRoot1->m_pLeft, pRoot2->m_pLeft) &&
        DoesTree1HaveTree2(pRoot1->m_pRight, pRoot2->m_pRight);
}

bool Equal(double num1, double num2)
{
    if(((num1 - num2) > -0.0000001) && ((num1 - num2) < 0.0000001))
        return true;
    else
        return false;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值