剑指offer(链表中倒数第k个节点、链表中环的入口节点、反转链表、合并两个排序的链表、树的子结构)

十、代码的鲁棒性

1. 链表中倒数第k个节点

题目描述:

​ 输入一个链表,输出该链表中倒数第k个结点。

思路:

​ 为了实现只遍历链表一次就能找到倒数第k个节点,我们可以定义两个指针。第一个指针从链表的第一个节点开始向前走k-1步,第二个指针则保持原地不动;从第k步开始,第二个指针也开始从链表的第一个节点开始遍历。由于两个指针的距离始终保持在k-1,当第一个(走在前面的)指针到达链表的尾节点时,第二个(走在后面的)指针恰好指向倒数第k个节点。

代码:

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode FindKthToTail(ListNode head,int k) {
    	//当输入的head为空指针或者k为0时,返回null
        if (head == null || k == 0) {
            return null;
        }
        ListNode ahead = head;
        ListNode behind = head;
        for (int i = 1; i <= k - 1; i++) {
            ahead = ahead.next;
            //若k的值大于链表长度时,返回null
            if (ahead == null) {
                return null;
            }
        }
        while (ahead.next != null) {
            ahead = ahead.next;
            behind = behind.next;
        }
        return behind;
    }
}

2. 链表中环的入口节点

题目描述:

​ 给一个不带头结点的单链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。

思路:

​ 先说个定理:两个指针一个fast、一个slow同时从一个链表的头部出发,fast一次走2步,slow一次走1步,如果该链表有环,两个指针必然在环内相遇,此时只需要把其中的一个指针重新指向链表头部,另一个不变(还在环内),这次两个指针一次走一步,相遇的地方就是入口节点。

代码:

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};
*/
class Solution {
public:
    ListNode* EntryNodeOfLoop(ListNode* pHead)
    {
        ListNode *fast=pHead,*slow=pHead;
        while(fast->next!=NULL){
            fast=fast->next->next;
            slow=slow->next;
            if(fast==slow){
                break;
            }
        }
        if(fast->next==NULL){
            return NULL;
        }else{
            fast=pHead;
            while(fast!=slow){
                fast=fast->next;
                slow=slow->next;
            }
            return fast;
        }
    }
};

3. 反转链表

题目描述:

​ 输入一个链表,反转链表后,输出新链表的表头。

思路:

​ 依次取下原链表中的每一个节点,进行头插。在取节点的过程中,要注意防止断链。

代码:

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode ReverseList(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        ListNode p = head.next;
        head.next = null;
        while (p != null) {
            ListNode q = p.next;//在每次进行头插之前,要先保存好下一个节点,防止断链
            p.next = head;
            head = p;
            p = q;
        }
        return head;
    }
}

4. 合并两个排序的链表

题目描述:

​ 输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

思路:

​ 见代码。

代码:

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        if (list1 == null && list2 == null) {
            return null;
        } else if (list1 == null) {
            return list2;
        } else if (list2 == null) {
            return list1;
        }
        ListNode head = new ListNode(-1);//新建一个头结点,用来存合并之后的链表(点睛之笔)
        ListNode p = head;
        while (list1 != null && list2 != null) {
            if (list1.val < list2.val) {
                p.next = list1;
                list1 = list1.next;
            } else {
                p.next = list2;
                list2 = list2.next;
            }
            p = p.next;
        }
        if (list1 != null) {
            p.next = list1;
        } else {
            p.next = list2;
        }
        return head.next;
    }
}

5. 树的子结构

题目描述:

​ 输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

思路:

​ 见代码。

代码:

/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;
    public TreeNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public boolean HasSubtree(TreeNode root1,TreeNode root2) {
    	boolean result = false;
    	//当root1或root2中有一棵为空树时,直接返回false。
        if (root1 != null && root2 != null) {
        	//判断当前的根节点是否和root2的根节点相同
            if (root1.val == root2.val) {
            	//如果相同,继续判断
                result = doesTree1HaveTree2(root1, root2);
            }
            //若找不到,去左子树继续找
            if (result == false) {
                result = doesTree1HaveTree2(root1.left, root2);
            }
            //若左子树中也找不到,则去右子树继续找
            if (result == false) {
                result = doesTree1HaveTree2(root1.right, root2);
            }
        }
        return result;
    }

    public static boolean doesTree1HaveTree2(TreeNode root1, TreeNode root2) {
    	//若树root2遍历完了都能对应上,返回true
        if (root2 == null) {
            return true;
        }
        //若root2还没有遍历完,root1却遍历完了,返回false
        if (root1 == null) {
            return false;
        }
        if (root1.val != root2.val) {//如果有不相同的节点,返回false
            return false;
        } else {//若根节点相同,则继续判断左右子树是否相同
            return doesTree1HaveTree2(root1.left, root2.left) && doesTree1HaveTree2(root1.right, root2.right);
        } 
    }
}
posted @ 2020-03-15 15:34  Java程序员的进阶之路  阅读(243)  评论(0编辑  收藏  举报