43、输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
TreeNode.java
package cn.ctgu.offer.BinaryTreeAndList;
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
Solution.java
package cn.ctgu.offer.BinaryTreeAndList;
/*
* 题目:
* 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。
* 要求不能创建任何新的结点,只能调整树中结点指针的指向。
*
* 思路:
* 1、将该二叉搜索树中序遍历,即有序
* 2、将根节点的左子树放在左边,右子树放在右边,根放中间
*
* */
public class Solution {
TreeNode head=null;
TreeNode realHead=null;
public TreeNode Convert(TreeNode pRootOfTree) {
ConvertSub(pRootOfTree);
return realHead;
}
private void ConvertSub(TreeNode pRootOfTree) {
if(pRootOfTree==null) {
return;
}
ConvertSub(pRootOfTree.left);
if(head==null) {
head=pRootOfTree;
realHead=pRootOfTree;
}else {
head.right=pRootOfTree;
pRootOfTree.left=head;
head=pRootOfTree;
}
ConvertSub(pRootOfTree.right);
}
}
44、输入两个链表,找出它们的第一个公共结点。
ListNode.java
package cn.ctgu.offer.CommonListNode;
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
package cn.ctgu.offer.CommonListNode;
import java.util.Stack;
/*
* 题目:
* 输入两个链表,找出它们的第一个公共结点。
*
* 思路:
* 1、两个链表有公共结点则表明从公共结点之后的结点都相同
* 2、可以从两个链表尾部开始比较,最后一个相同的结点即为两链表的第一个公共结点
* 3、建立两个栈,分别将其结点存储进去,然后出栈,一个一个比较直到最后一个相同结点
*
*
* */
public class CommonListNode {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
if (pHead1 == null || pHead2 == null) {
return null;
}
Stack<ListNode>stack1=new Stack<ListNode>();
Stack<ListNode>stack2=new Stack<ListNode>();
ListNode node=null;
while(pHead1!=null) {
stack1.push(pHead1);
pHead1=pHead1.next;
}
while(pHead2!=null) {
stack2.push(pHead2);
pHead2=pHead2.next;
}
while(!stack1.isEmpty()&&!stack2.isEmpty()&&stack1.peek()==stack2.peek()) {
stack1.pop();
node=stack2.pop();
}
return node;
}
}
45、输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
RandomListNode.java
package cn.ctgu.offer.CopyList;
public class RandomListNode {
int label;
RandomListNode next = null;
RandomListNode random = null;
RandomListNode(int label) {
this.label = label;
}
}
package cn.ctgu.offer.CopyList;
/*
* 题目:
*
* 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点)
* 返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
*
*
* 解题思路:
* 1、遍历链表,复制每个结点,如复制结点A得到A1,将结点A1插到结点A后面
* 2、重新遍历链表,复制老结点的随机指针给新结点,如A1.random=A.random
* 3、拆分链表,将链表拆分为原链表和复制后的链表
*
* */
public class Solution {
public RandomListNode Clone(RandomListNode pHead) {
if(pHead==null) {
return null;
}
RandomListNode currentNode=pHead;
//1、复制每个结点,如复制结点A得到A1,将结点A1插到结点A后面
while(currentNode!=null) {
RandomListNode cloneNode=new RandomListNode(currentNode.label);
RandomListNode nextNode=currentNode.next;
currentNode.next=cloneNode;
cloneNode.next=nextNode;
currentNode=nextNode;
}
currentNode=pHead;
//2、重新遍历链表,复制老结点的随机指针给新结点,如A1.random = A.random.next;
while(currentNode!=null) {
if(currentNode.random==null) {
currentNode.next.random=null;
}else {
currentNode.next.random=currentNode.random.next;
}
currentNode=currentNode.next.next;
}
//3、拆分链表,将链表拆分成原链表和复制后的链表
currentNode=pHead;
RandomListNode pCloneHead=pHead.next;
while(currentNode!=null) {
RandomListNode cloneNode=currentNode.next;
currentNode.next=cloneNode.next;
if(cloneNode.next==null) {
cloneNode.next=null;
}else {
cloneNode.next=cloneNode.next.next;
}
currentNode=currentNode.next;
}
return pCloneHead;
}
}
46、在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
package cn.ctgu.offer.DeleteRepeatListNode;
/*
* 题目:
* 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。
* 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
*
* 思路:
* 1、首先添加一个头结点,以方便遇到第一个,第二个结点就相同的情况
* 2、设置pre last指针,pre指针指向当前确定不重复的那个结点,而last指针相当于工作指针一直往后面搜索
*
* */
public class Solution {
public ListNode deleteDuplication(ListNode pHead)
{
if(pHead==null || pHead.next==null)
return pHead;
ListNode Head=new ListNode(0);
Head.next = pHead;
ListNode pre=Head;
ListNode last=Head.next;
while(last!=null) {
if(last.next!=null && last.val==last.next.val) {
//找到最后一个相同结点
while(last.next!=null && last.val==last.next.val) {
last=last.next;
}
//此处的pre.next指向第一个相同结点中的第一个,last.next指向第一个非相同结点
pre.next=last.next;
last=last.next;
}else{
pre=pre.next;
last=last.next;
}
}
return Head.next;
}
}
//递归写法
/*public class Solution {
public ListNode deleteDuplication(ListNode pHead) {
if (pHead == null || pHead.next == null) { // 只有0个或1个结点,则返回
return pHead;
}
if (pHead.val == pHead.next.val) { // 当前结点是重复结点
ListNode pNode = pHead.next;
while (pNode != null && pNode.val == pHead.val) {
// 跳过值与当前结点相同的全部结点,找到第一个与当前结点不同的结点
pNode = pNode.next;
}
return deleteDuplication(pNode); // 从第一个与当前结点不同的结点开始递归
} else { // 当前结点不是重复结点
pHead.next = deleteDuplication(pHead.next); // 保留当前结点,从下一个结点开始递归
return pHead;
}
}
}*/
47、给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
package cn.ctgu.offer.EntryNodeLoop;
/*
* 题目:
* 给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
*
* 思路:
* 1、第一步确定链表中是否包含环,怎么确定呢?
* 我们定义两个指针慢指针和快指针,慢指针每次走一步,快指针每次走两步,如果他俩重合了,这就说明链表中存在环。
* (因为有环,所以最后只能一直在里面转圈,不可能出去,所以相遇的话只可能是在环里)
* 2、第二步求环的长度,两者碰头后,让其中一个继续走,每走一步步数加一,然后求得环的长度。
* 3、第三步求环的初始节点,仍然是两个指针
* 其中一个事先走环长个节点,然后两者同时移动,直到两者碰头,然后那个节点就是环的初始节点。
* (因为两者相差一个环长)
*
*
* */
public class EntryNodeLoop {
public ListNode EntryNodeOfLoop(ListNode pHead)
{
//确定是否有环
if(isCircle(pHead)) {
//求环的长度
int len=getLenOfCircle(pHead);
ListNode pA=pHead;
ListNode pB=pHead;
for(int i=0;i<len;i++) {
pB=pB.next;
}
while(pA.val!=pB.val) {
pA=pA.next;
pB=pB.next;
}
return pA;
}
return null;
}
private boolean isCircle(ListNode pHead) {
try {
ListNode pA=pHead.next;
ListNode pB=pHead.next.next;
while(pA!=null && pB!=null && pA.val!=pB.val) {
pA=pA.next;
pB=pB.next.next;
}
return true;
}catch(Exception e) {
return false;
}
}
//求环的长度
public static int getLenOfCircle(ListNode pHead) {
ListNode pA=pHead.next;
ListNode pB=pHead.next.next;
while(pA!=null && pB!=null && pA.val!=pB.val) {
pA=pA.next;
pB=pB.next.next;
}
int length=1;
while(pB.next.val!=pA.val) {
pB=pB.next;
length++;
}
return length;
}
}
/*//左神讲的
//先说个定理:两个指针一个fast、一个slow同时从一个链表的头部出发
//fast一次走2步,slow一次走一步,如果该链表有环,两个指针必然在环内相遇
//此时只需要把其中的一个指针重新指向链表头部,另一个不变(还在环内),
//这次两个指针一次走一步,相遇的地方就是入口节点。
//这个定理可以自己去网上看看证明。
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead){
ListNode fast = pHead;
ListNode slow = pHead;
while(fast != null && fast.next !=null){
fast = fast.next.next;
slow = slow.next;
if(fast == slow)
break;
}
if(fast == null || fast.next == null)
return null;
fast = pHead;
while(fast != slow){
fast = fast.next;
slow = slow.next;
}
return fast;
}
}
*/
/*import java.util.*;
//将所有的结点添加到hashSet中,当遇到第一个重复的结点则为入口结点,因为后面只有不断的转圈
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead)
{
if(pHead==null)
return null;
ListNode pNode=pHead;
HashSet<ListNode> pSet = new HashSet<ListNode>();
while(pNode!=null){
if(!pSet.add(pNode))
return pNode;
pNode=pNode.next;
}
return null;
}
}*/
48、给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
TreeLinkNode.java
package cn.ctgu.offer.GetNextTreeNode;
public class TreeLinkNode {
int val;
TreeLinkNode left = null;
TreeLinkNode right = null;
TreeLinkNode next = null;
TreeLinkNode(int val) {
this.val = val;
}
}
package cn.ctgu.offer.GetNextTreeNode;
/*
* 题目:
* 给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。
* 注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
*
*
* 思路:
* 1、首先理解题目的意思:这颗二叉树每个结点有三个指向,左孩子、右孩子、指向父结点的指针
* 题目要求将该树进行中序遍历,并找出中序遍历后所给定结点的下一个结点
*
* 2、我们知道二叉树的中序遍历是先遍历左子树,然后遍历根结点,最后遍历右子树。
* 根据题意,我们需要找到当前节点的下一个需要遍历的结点,首先判断该结点是否存在右子树
* 如果存在,那么下一个结点一定在其右子树中,且是右子树中第一个没有左孩子的结点(每一个有左孩子的结点,就需要遍历其左子树)。
*
* 3、如果结点没有右子树,那么如果该结点是父结点的左孩子,那么下一个结点就是其父结点;
*
* 4、结点没有右子树,且如果当前结点是父结点的右孩子,那么继续往上找到父结点的父结点,直到找到一个结点是父结点的左孩子的结点
* 那么其父结点就是下一个结点
*
* 5、如果一直遍历到根结点也没有找到存在结点是父结点的左孩子的结点,那么当前给定结点是二叉树中序遍历的最后的结点,
* 下一个结点就是null。
*
* */
public class Solution {
public TreeLinkNode GetNext(TreeLinkNode pNode) {
if(pNode==null)
return null;
TreeLinkNode p;
//判断是否存在右子树
if(pNode.right!=null) {
p=pNode.right;
//找到第一个没有左结点的结点
while(p!=null && p.left!=null) {
p=p.left;
}
return p;
}
//没有右子树
p=pNode.next;//指向父节点
while(p!=null) {
//当前结点是父结点的左孩子,下一个结点就是父结点
if(pNode==p.left) {
return p;
}
//当前结点是父结点的右孩子,则往上找
pNode=p;
p=p.next;
}
return p;
}
}
49、 输入一棵二叉树,判断该二叉树是否是平衡二叉树。
package cn.ctgu.offer.IsBalanceBinaryTree;
/*
* 题目:
* 输入一棵二叉树,判断该二叉树是否是平衡二叉树。
*
* 思路:
* 1、平衡二叉树左右子树的高度差不能大于1,且左右子树均为平衡二叉树
* 2、分别计算左右子树的高度
* 3、做差比较
*
* */
public class IsBalanceBinarryTree {
public boolean IsBalanced_Solution(TreeNode root) {
if(root==null) {
return true;
}
int left=depth(root.left);
int right=depth(root.right);
if(Math.abs(left-right)>1) {
return false;
}
boolean booleft=IsBalanced_Solution(root.left);
boolean boolright=IsBalanced_Solution(root.right);
return booleft&&boolright;//左右子树都为平衡二叉树的时候该数才为平衡二叉树
}
/*private int depth(TreeNode root) {
if(root==null)
return 0;
int depth=0;//每次递归的时候都会将depth置为0,所以不能这样弄
int left=depth(root.left);
int right=depth(root.right);
//计算二叉树的高度,左右子树高的为二叉树的高度
if(left>right)
depth=left+1;//加上根节点
if(left<right)
depth=right+1;
return depth;
}*/
private int depth(TreeNode root) {
if(root==null)
return 0;
int left=depth(root.left);
int right=depth(root.right);
//计算二叉树的高度,左右子树高的为二叉树的高度
return (left>right)?(left+1):(right+1);//需要加上根节点
}
}
49、请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
package cn.ctgu.offer.IsSysmmetric;
/*
* 题目:
* 请实现一个函数,用来判断一颗二叉树是不是对称的。
* 注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
*
* 思路:
* 1、首先根节点以及其左右子树,左子树的左子树和右子树的右子树相同
* 2、左子树的右子树和右子树的左子树相同即可,采用递归
* 3、 非递归也可,采用栈或队列存取各级子树根节点
*
* 方法二:二叉树是否对称,只要采用前序、中序、后序、层次遍历等任何一种遍历方法,分为先左后右和先
右后左两种方法,只要两次结果相等就说明这棵树是一颗对称二叉树。
* */
public class Solution {
boolean isSymmetrical(TreeNode pRoot)
{
if(pRoot==null) {
return true;
}
return f(pRoot.left,pRoot.right);
}
boolean f(TreeNode t1,TreeNode t2) {
if(t1==null && t2==null) {
return true;
}
if(t1!=null && t2!=null) {
return t1.val==t2.val && f(t1.left,t2.right) && f(t1.right,t2.left);
}
return false;
}
}
/*
* 用栈来实现
* 1、使用stack来保存成对的节点
* 2、出栈的时候也是成对成对的 ,
* 3、若都为空,继续;一个为空,返回false;不为空,比较当前值,值不等,返回false;
* 4、确定入栈顺序,每次入栈都是成对成对的,如left.left, right.right ;left.rigth,right.left
*
* */
/*boolean isSymmetricalDFS(TreeNode pRoot)
{
if(pRoot == null) return true;
Stack<TreeNode> s = new Stack<>();
s.push(pRoot.left);
s.push(pRoot.right);
while(!s.empty()) {
TreeNode right = s.pop();//成对取出
TreeNode left = s.pop();
if(left == null && right == null) continue;
if(left == null || right == null) return false;
if(left.val != right.val) return false;
//成对插入
s.push(left.left);
s.push(right.right);
s.push(left.right);
s.push(right.left);
}
return true;
}*/
50、给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。
package cn.ctgu.offer.KthTreeNode;
import java.util.ArrayList;
/*
* 题目:
* 给定一棵二叉搜索树,请找出其中的第k小的结点。
* 例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。
*
*
* 思路:
* 1、由于是二叉搜索树,即左子树小于根节点,根节点大于右子树
* 2、将二叉搜索树进行中序遍历后的值存储到list中即为有序,而且是升序
* 3、然后直接将倒数第K个输出即可
*
* */
/*public class Solution {
int index=0;//计数器
TreeNode KthNode(TreeNode pRoot, int k)
{
if(pRoot!=null) {//中序遍历寻找第k个
TreeNode node=KthNode(pRoot.left,k);
if(node!=null) {
return node;
}
index++;
if(index==k) {
return pRoot;
}
node=KthNode(pRoot.right,k);
if(node!=null) {
return node;
}
}
return null;
}
}*/
public class Solution {
TreeNode KthNode(TreeNode pRoot, int k) {
if(pRoot==null || k<=0) {
return null;
}
ArrayList<TreeNode>node=null;
InorderTraversal(pRoot,node);
if(k>node.size()) {
return null;
}
return node.get(k-1);
}
void InorderTraversal(TreeNode root,ArrayList<TreeNode>node) {
if(root==null)
return;
InorderTraversal(root.left,node);
node.add(root);
InorderTraversal(root.right,node);
}
}
51、 输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
package cn.ctgu.offer.LengthBinarryTree;
/*
* 题目:
* 输入一棵二叉树,求该树的深度。
* 从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
*
* 思路:
* 1、采用递归
* 2、递归遍历左子树和右子树,并记录其长度
* 3、比较长度,最长的一个即为该数的深度
*
* */
public class Solution {
public int TreeDepth(TreeNode root) {
if(root==null) {
return 0;
}
int left=TreeDepth(root.left);
int right=TreeDepth(root.right);
return Math.max(left, right)+1;
}
}
52、输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
ListNode.java
package cn.ctgu.offer.MergeListNode;
public class ListNode {
public int val;
public ListNode next= null;
public ListNode(int val) {
this.val = val;
}
}
BuildListNode.java
package cn.ctgu.offer.MergeListNode;
/*
* 该类的主要功能是完成构建链表,输入是一个数组,输出是第一个节点
*
* */
public class BuildListNode {
private ListNode first;
private ListNode last;
private ListNode newNode;
private int[] input;
BuildListNode(int[]input){
this.first=null;
this.last=null;
this.input=input;
}
public ListNode input() {
if(input.length>0) {
for(int i=0;i<input.length;i++) {
newNode=new ListNode(input[i]);
newNode.next=null;
if(first==null) {
first=newNode;
last=newNode;
}else {
last.next=newNode;
last=newNode;
}
}
}
return first;
}
}
MergeList.java
package cn.ctgu.offer.MergeListNode;
/*
* 输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
*
* 思路:
* 1、判断两个链表是否为空
* 2、一个一个元素比较,大的放在小的后面
*
*
* */
public class MergeList {
public ListNode Merge(ListNode list1,ListNode list2) {
ListNode current=null;
ListNode mergeHead=null;
if(list1==null&&list2==null) {
return null;
}
if(list1==null&&list2!=null) {
return list2;
}
if(list1!=null&&list2==null) {
return list1;
}
//由于要保持合成后的链表单调不减,即保持单调递增关系,但两个链表不一定没有重复的元素
while(list1!=null && list2!=null) {
if(list1.val<=list2.val) {
if(mergeHead==null) {
mergeHead=current=list1;
}else {
current.next=list1;
current=current.next;
}
list1=list1.next;
}else {
if(mergeHead==null) {
mergeHead=current=list2;
}else {
current.next=list2;
current=current.next;
}
list2=list2.next;
}
}
//list1链表先遍历完
if(list1==null) {
current.next=list2;
}else {//list2链表先遍历完
current.next=list1;
}
return mergeHead;
}
//递归版本
public ListNode Merge1(ListNode list1,ListNode list2) {
if(list1 == null){
return list2;
}
if(list2 == null){
return list1;
}
if(list1.val <= list2.val){
list1.next = Merge(list1.next, list2);
return list1;
}else{
list2.next = Merge(list1, list2.next);
return list2;
}
}
public static void main(String[]args) {
MergeList merge=new MergeList();
int[]node1= {1,2,3,4,5};
int[]node2= {3,5,8,9,10};
BuildListNode build1=new BuildListNode(node1);
ListNode list1=build1.input();
BuildListNode build2=new BuildListNode(node2);
ListNode list2=build2.input();
ListNode newList=merge.Merge(list1, list2);
/*
*
* 结果:
* 1
2
3
3
4
5
5
8
9
10
* */
while(newList!=null) {
System.out.println(newList.val);
newList=newList.next;
}
}
}
53、操作给定的二叉树,将其变换为源二叉树的镜像。
package cn.ctgu.offer.MirrorBinaryTree;
/*
* 操作给定的二叉树,将其变换为源二叉树的镜像。
*
* 思路:使用递归
* 1、如果根节点为空,则返回
* 2、如果左节点和右节点都为空则结束
* 3、否则交换根节点的左节点和右节点
* 4、当左节点不为空,则将左节点当作根节点递归建立镜像
* 5、当右节点不为空,则将右节点当作根节点递归建立镜像
*
* */
public class MirrorTree {
public void Mirror(TreeNode root) {
if(root==null) {
return;
}
if(root.left==null&&root.right==null) {
return;
}
swap(root);
//swap1(root.left,root.right);//这样不行
if(root.left!=null){
Mirror(root.left);
}
if(root.right!=null) {
Mirror(root.right);
}
}
//该函数主要用于交换节点
public void swap(TreeNode root) {
TreeNode temp=root.left;
root.left=root.right;
root.right=temp;
}
/*这样不行
* public void swap1(TreeNode node1,TreeNode node2) {
TreeNode temp=node1;
node1=node2;
node2=node1;
}*/
}
54、输入一个链表,输出该链表中倒数第k个结点。
package cn.ctgu.offer.outputListNodeK;
import java.util.ArrayList;
/*
*
* 输入一个链表,输出该链表中倒数第k个结点。
*
*
* 思路:
* 1、遍历所有节点,将节点对象存储到数组中
* 2、当K大于节点长度或小于等于0,则返回null
* 3、当头节点为null,则返回null
* 4、取出倒数第k个节点
* 5、头节点也需要存储
*
*
* */
public class OutputListNode {
public ListNode FindKthToTail(ListNode head,int k) {
ListNode list=head;
ArrayList<ListNode>node=new ArrayList<ListNode>();
if(head==null){
return null;
}
node.add(list);
while(list.next!=null) {
node.add(list.next);
list=list.next;
}
if(k>node.size()||k<=0){
return null;
}
return node.get(node.size()-k);
}
/*
* 两个指针,先让第一个指针和第二个指针都指向头结点,然后再让第一个指正走(k-1)步,到达第k个节点。
* 然后两个指针同时往后移动,当第一个结点到达末尾的时候,第二个结点所在位置就是倒数第k个节点了。。
*
* 假设长度为n,由于长度是固定的,所以顺着第k个就是倒数第n-k+1
* 所以倒数第k个就是顺数第n-k+1个
* */
public ListNode FindKthToTail1(ListNode head,int k) {
if(head==null||k<=0){
return null;
}
ListNode pre=head;
ListNode last=head;
for(int i=1;i<k;i++){
if(pre.next!=null){
pre=pre.next;
}else{
return null;
}
}
while(pre.next!=null){
pre = pre.next;
last=last.next;
}
return last;
}
}
55、从上往下打印出二叉树的每个节点,同层节点从左至右打印。
package cn.ctgu.offer.PrintBinaryTree;
/*
*
* 从上往下打印出二叉树的每个节点,同层节点从左至右打印。
*
* 思路:
* 1、设置last和nlast,last表示打印行当前的最右节点,nlast表示下一行的最右节点
* 2、当last等于nlast,则换行
* 3、用arrayList模拟一个队列来存储相应的TreeNode
*
* */
import java.util.ArrayList;
public class Print {
public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
ArrayList<Integer> array=new ArrayList<Integer>();
ArrayList<TreeNode> queue=new ArrayList<TreeNode>();
if(root==null) {
return null;
}
queue.add(root);
while(queue.size()!=0) {
TreeNode tmp=queue.remove(0);
if(tmp.left!=null) {
queue.add(tmp.left);
}
if(tmp.right!=null) {
queue.add(tmp.right);
}
array.add(tmp.val);
}
return array;
}
}
56、输入一个链表,从尾到头打印链表每个节点的值。
package cn.ctgu.offer.printListNode;
import java.util.ArrayList;
import java.util.Iterator;
import cn.ctgu.offer.ReverseLinkedList.ListNode;
/*
* 输入一个链表,从尾到头打印链表每个节点的值。
* */
public class PrintListNode {
/*
* 将链表中的节点从头到尾数出来存放到一个数组中,然后将数组从尾到头打印出来
*
*
* */
//该函数完成的主要功能是:将链表中的值存放到一个数组中
public ArrayList<Integer> NodeToArray(ListNode listNode) {
ArrayList<Integer>array=new ArrayList<Integer>();
int i=0;
int element=0;
ListNode node=listNode;
element=node.val;
array.add(i, element);
while(node.nextNode!=null) {
node=node.nextNode;
element=node.val;
i++;
array.add(i, element);
}
return array;
}
public void ReverseOutput(ArrayList<Integer> array) {
int []reverse=new int[array.size()];
int j=0;
for(Iterator<Integer>it=array.iterator();it.hasNext();) {
reverse[j]=(Integer)it.next();
j++;
}
for(int k=array.size();k>=1;k--) {
System.out.println(reverse[k-1]);
}
}
public static void main(String[]args) {
PrintListNode printNode=new PrintListNode();
ListNode listNode=new ListNode(0);
int[]input=new int[] {1,2,3,4,5,10,12};
BuildListNode build=new BuildListNode(input);
listNode=build.input();
ArrayList<Integer>array=new ArrayList<Integer>();
array=printNode.NodeToArray(listNode);
printNode.ReverseOutput(array);
}
}
57、从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
package cn.ctgu.offer.PrintTree;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
/*
* 题目:
* 从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
*
* 思路:
* 1、按层次输出二叉树
* 2、访问根节点,并将根节点入队
* 3、当队列不空的时候,重复以下操作
* 1)弹出一个元素,作为当前的根节点
* 2)如果根节点有左孩子,访问左孩子,并将左孩子入队
* 3)如果根节点有右孩子,访问右孩子,并将右孩子入队
*
* */
public class Solution {
ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
//存放结果
ArrayList<ArrayList<Integer>>result=new ArrayList<ArrayList<Integer>>();
if(pRoot==null) {
return result;
}
//使用队列先进先出
Queue<TreeNode> queue=new LinkedList<TreeNode>();
//存放每行的列表
ArrayList<Integer>arrayList=new ArrayList<Integer>();
//记录本层打印了多少个
int start=0;
//记录下一层打几个
int end=1;
queue.add(pRoot);
while(!queue.isEmpty()) {
TreeNode temp = queue.remove();
//添加到本行的arrayList
arrayList.add(temp.val);
start++;
//每打印一个结点,就把此结点的下一层的左右结点加入队里,并记录下一层要打印的个数
if(temp.left!=null) {
queue.add(temp.left);
}
if(temp.right!=null) {
queue.add(temp.right);
}
//判断本层是否打印完成
if(start==end) {
//此时的queue中存储的都是下一层的结点,则end即为queue大小
end=queue.size();//上一层的结点都出队了
start=0;
//把arrayList添加到结果列表result中
result.add(arrayList);
//重置arrayList
arrayList=new ArrayList<Integer>();
}
}
return result;
}
}
/*
* 创建二叉树
*
* */
/*private TreeNode createBinaryTreeByArray(int[] array, int index) {
TreeNode tn = null;
if (index < array.length) {
int value = array[index];
tn = new TreeNode(value);
tn.left = createBinaryTreeByArray(array, 2 * index + 1);
tn.right = createBinaryTreeByArray(array, 2 * index + 2);
return tn;
}
return tn;
}*/
58、请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
package cn.ctgu.offer.PrintZTree;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
/*
* 题目:
* 请实现一个函数按照之字形打印二叉树
* 即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
*
* 思路:
* 1、大家的实现很多都是将每层的数据存进ArrayList中,偶数层时进行reverse操作,
* 在海量数据时,这样效率太低了。
*
* 2、不必将每层的数据存进ArrayList中,偶数层时进行reverse操作,直接按打印顺序存入
* 3、利用Java中的LinkedList的底层实现是双向链表的特点。
* 4、双向遍历,奇数层时从前向后遍历,偶数层时从后向前遍历
*
*
*
* */
public class Solution {
public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>>ret=new ArrayList<>();
if(pRoot==null) {
return ret;
}
ArrayList<Integer>list=new ArrayList<>();
LinkedList<TreeNode>queue=new LinkedList<>();
queue.addLast(null);//层分隔符
queue.addLast(pRoot);
boolean leftToRigth=true;
while(queue.size()!=1) {
TreeNode node=queue.removeFirst();
if(node==null) {//到达层分隔符
Iterator<TreeNode>iter=null;
if(leftToRigth) {
iter=queue.iterator();//从前往后遍历
}else {
iter=queue.descendingIterator();//从后往前遍历
}
leftToRigth=!leftToRigth;
//开始遍历
while(iter.hasNext()) {
TreeNode temp=(TreeNode)iter.next();
list.add(temp.val);
}
ret.add(new ArrayList<Integer>(list));
list.clear();
queue.addLast(null);//添加层分隔符
continue;//跳出本次循环
}
if(node.left!=null) {
queue.addLast(node.left);
}
if(node.right!=null) {
queue.addLast(node.right);
}
}
return ret;
}
}
59、输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
package cn.ctgu.offer.RebuiltBinarytree;
/*
* 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。
* 假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
* 例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6}
* 则重建二叉树并返回。
*
* */
/*
* 解题思路:
* 1、通过前序遍历确定根节点为1
* 2、通过中序遍历确定4,7,2为左子树,5,3,8,6为右子树
* 3、重建二叉树:通过上面的步骤分别对左子树和右子树进行重建并返回根节点
* 4、然后输出先序遍历和中序遍历进行验证
*
* */
public class Solution {
TreeNode rebuild(TreeNode root,int []pre,int[] in) {
if(pre.length==0) {
root=null;
return root;
}
int rootdata=pre[0];//根的值
int index=0;
int left_len=0;
int right_len=0;
for(int i=0;i<=in.length-1;i++) {
if(rootdata==in[i]){
index=i;
left_len=i;
right_len=in.length-i-1;
}
}
int[]left_pre=new int[index];
int[]right_pre=new int[in.length-index-1];
int[]left_in=new int[index];
int[]right_in=new int[in.length-index-1];
for(int j=0;j<=left_len-1;j++) {
left_pre[j]=pre[j+1];//left是左子树的前序遍历
}
for(int k=left_len+1;k<=pre.length-1;k++) {
right_pre[k-right_len]=pre[k];//right是右子树的前序遍历
}
for(int le=0;le<=left_len-1;le++) {
left_in[le]=in[le];//左子树的中序遍历
}
for(int ri=index+1;ri<=in.length-1;ri++) {
right_in[ri-right_len]=in[ri];//右子树的中序遍历
}
root=new TreeNode(pre[0]);
if(root!=null) {
root.val=rootdata;
root.left=rebuild(root.left,left_pre,left_in);//递归创建左孩子
root.right=rebuild(root.right,right_pre,right_in);//递归创建右孩子
}
return root;
}
//检验是否创建成功
//递归前序遍历二叉树
void preOrderTraverse(TreeNode t) {
if(t!=null) {
System.out.println(t.val);
preOrderTraverse(t.left);
preOrderTraverse(t.right);
}
}
//递归中序遍历二叉树
void InOrderTraverse(TreeNode t) {
if(t!=null) {
InOrderTraverse(t.left);
System.out.println(t.val);
InOrderTraverse(t.right);
}
}
public static void main(String[]args) {
Solution solution=new Solution();
int[]pre= {1,2,4,7,3,5,6,8};
int[]in= {4,7,2,1,5,3,8,6};
TreeNode root=new TreeNode(pre[0]);
root=solution.rebuild(root, pre, in);
System.out.println("---------前序遍历------------");
solution.preOrderTraverse(root);
System.out.println("---------中序遍历------------");
solution.InOrderTraverse(root);
}
}
60、输入一个链表,反转链表后,输出链表的所有元素。
package cn.ctgu.offer.ReverseLinkedList;
/*
* 输入一个链表,反转链表后,输出链表的所有元素。
*
* 思路:
* 1、先将链表中的所有元素取出来放入一个数组中
* 2、从数组的最后一个元素开始重建新链表
* 3、从新链表中取出元素
*
* */
public class Reversed {
public ListNode ReverseList(ListNode head) {
if(head==null) {
return null;
}else {
int[]node=getElement(head);
head=BuildList(node);
}
return head;
}
//将链表中的元素取出来
public int[] getElement(ListNode head) {
int count=1;
ListNode list=head.nextNode;
while(list!=null) {
list=list.nextNode;
count++;//计算链表的长度
}
int[]node=new int[count];
int i=0;
list=head;
while(list!=null) {
node[i]=list.val;
i++;
list=list.nextNode;
}
return node;
}
//将取出来的链表元素从尾到头重建一个新的链表
public ListNode BuildList(int[] node) {
ListNode newNode=null;
ListNode first=null;
ListNode last=null;
if(node.length>0) {
for(int i=node.length-1;i>=0;i--) {
newNode=new ListNode(node[i]);
newNode.nextNode=null;
if(first==null) {
first=newNode;
last=newNode;
}else {
last.nextNode=newNode;
last=newNode;
}
}
}
return first;//指向第一个节点,而不是头结点,java中头结点就是第一个节点
}
//牛客网思路
public ListNode ReverseList1(ListNode head) {
if(head==null)
return null;
//head为当前节点,如果当前节点为空的话,那就什么也不做,直接返回null;
ListNode pre = null;
ListNode next = null;
//当前节点是head,pre为当前节点的前一节点,next为当前节点的下一节点
//需要pre和next的目的是让当前节点从pre->head->next1->next2变成pre<-head next1->next2
//即pre让节点可以反转所指方向,但反转之后如果不用next节点保存next1节点的话,此单链表就此断开了
//所以需要用到pre和next两个节点
//1->2->3->4->5
//1<-2<-3 4->5
while(head!=null){
//做循环,如果当前节点不为空的话,始终执行此循环,此循环的目的就是让当前节点从指向next到指向pre
//如此就可以做到反转链表的效果
//先用next保存head的下一个节点的信息,保证单链表不会因为失去head节点的原next节点而就此断裂
next = head.nextNode;
//保存完next,就可以让head从指向next变成指向pre了,代码如下
head.nextNode = pre;
//head指向pre后,就继续依次反转下一个节点
//让pre,head,next依次向后移动一个节点,继续下一次的指针反转
pre = head;
head = next;
}
//如果head为null的时候,pre就为最后一个节点了,但是链表已经反转完毕,pre就是反转后链表的第一个节点
//直接输出pre就是我们想要得到的反转后的链表
return pre;
}
public static void main(String[]args) {
int[] num={1,2,3,4,10,6,7,8,9};
Reversed list=new Reversed();
ListNode link=list.BuildList(num);//链表经过重建为变为了9,8,7,6,5,4,3,2,1
ListNode node=list.ReverseList1(link);//再经过重建回来应变为1,2,3,4,5,6,7,8,9
System.out.println(node.val);
while(node.nextNode!=null) {
node=node.nextNode;
System.out.println(node.val);
}
}
}
61、请实现两个函数,分别用来序列化和反序列化二叉树
package cn.ctgu.offer.SerializeAndDeserializeTree;
/*
* 题目:
* 请实现两个函数,分别用来序列化和反序列化二叉树
*
* 思路:
* 1、根据前序遍历规则完成序列化和反序列化,所谓的序列化指的是遍历二叉树为字符串,所谓反序列化指的是依据字符串重新构造成二叉树。
* 2、依据前序遍历序列反序列化二叉树,因为前序遍历序列是从根节点开始的
* 当在遍历二叉树时碰到null指针时,这些指针被序列化一个特殊的字符'#'
* 3、另外结点之间的数值用逗号隔开
* */
public class Solution {
int index=-1;//计数变量
String Serialize(TreeNode root) {
StringBuilder sb=new StringBuilder();
if(root==null) {
sb.append("#,");
return sb.toString();
}
sb.append(root.val+",");
sb.append(Serialize(root.left));
sb.append(Serialize(root.right));
return sb.toString();
}
TreeNode Deserialize(String str) {
index++;//很巧妙
String[]strr=str.split(",");
TreeNode node=null;
if(!strr[index].equals("#")) {
node=new TreeNode(Integer.valueOf(strr[index]));
node.left=Deserialize(str);//执行这个的时候index变成1,然后再执行上面那条语句
node.right=Deserialize(str);
}
return node;
}
}
62、用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
package cn.ctgu.offer.StackToQueue;
import java.util.Stack;
/*
* 1、栈具有先进后出的特性
* 2、队列具有先进先出的特性
* 3、压栈的时候正常压栈到stack1
* 4、stack2始终维持出栈
* 4、出栈的时候,先判断stack2是否为空,若不为空则将其出栈(因为每次只会输出一个元素)
* a、若为空,则将stack1中的所有元素放入到stack2中,那么此时stack2就保持了压栈的顺序,然后执行出栈
* b、若不为空,则先出栈,此时stack1必定为空,将stack2中的所有元素放入到stack1中
* 5、两个栈始终保持stack1有元素,stack2为空
*
* */
public class NewQueue {
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
void push(int node) {
stack1.push(node);
}
int pop() {
int j=0;
/*if(!stack2.isEmpty()) {
j=stack2.pop();
}*/
while(!stack1.isEmpty()) {
stack2.push(stack1.pop());
}
j=stack2.pop();
while(!stack2.isEmpty()) {
stack1.push(stack2.pop());
}
return j;
}
}
63、输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
package cn.ctgu.offer.SubTree;
/*
* 输入两棵二叉树A,B,判断B是不是A的子结构。
* (ps:我们约定空树不是任意一个树的子结构)
*
* 思路:
* 1、当Tree1和Tree2都不为空的时候,才进行比较。否则直接返回false
* 2、如果Tree1.val==Tree2.val,即找到了A和B树对应的根节点的点,则以这个根节点为起点判断是否包含Tree2
* 3、如果找不到,那么就再去A树root的左儿子当作起点,去判断是否包含B数
* 4、如果还找不到,那么就再去A树root的右儿子当作起点,去判断是否包含B数
*
* */
public class ValidateSubTree {
public boolean HasSubtree(TreeNode root1,TreeNode root2) {
boolean result=false;
//当Tree1和Tree2都不为零的时候,才进行比较。否则直接返回false
if(root1!=null&&root2!=null) {
//如果找到了对应Tree2的根节点的点
if(root1.val==root2.val) {
//以这个根节点为为起点判断是否包含Tree2
result=DoesTree1HaveTree2(root1, root2);
}
//如果找不到,那么就再去root的左儿子当作起点,去判断时候包含Tree2
if(!result) {
result=HasSubtree(root1.left, root2);
}
//如果还找不到,那么就再去root的右儿子当作起点,去判断时候包含Tree2
if(!result) {
result=HasSubtree(root1.right, root2);
}
}
//返回结果
return result;
}
/*
* 以某个节点为起点判断是否包含B树,该函数的前提是已经找到了B树在A树中的位置
*
* 1、如果B树遍历完了都能对应上,返回true
* 2、如果B树还没有遍历完,A树却遍历完了,返回false
* 3、如果其中有一个点没有对应上,返回false
* 4、如果根节点对应不上,那么就分别去子节点里面匹配
*
* */
public boolean DoesTree1HaveTree2(TreeNode node1,TreeNode node2) {
if(node2==null) {
return true;
}
if(node1==null&&node2!=null) {
return false;
}
if(node1.val!=node2.val) {
return false;
}
return DoesTree1HaveTree2(node1.left, node2.left)&&DoesTree1HaveTree2(node1.right, node2.right);
}
}
64、输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)
package cn.ctgu.offer.SumBinaryTree;
import java.util.ArrayList;
/*
* 输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。
* 路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。
* (注意: 在返回值的list中,数组长度大的数组靠前)
*
*
* */
public class Solution {
ArrayList<ArrayList<Integer>>res=new ArrayList();
ArrayList<Integer>path=new ArrayList<Integer>();
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
if(root==null) {
return res;
}
findPath(root,target);
return res;
}
public void findPath(TreeNode root,int target) {
//因为FindPath中和 下面程序中都进行了判null操作,root绝对不可能为 null
path.add(root.val);
//已经到达叶子节点,并且正好加出了target
if(root.val==target&&root.left==null&&root.right==null) {
//将该路径加入res结果集中
res.add(new ArrayList(path));
}
//如果左子树非空,递归左子树
if(root.left!=null) {
findPath(root.left,target-root.val);
}
//如果右子树非空,递归右子树
if(root.right!=null) {
findPath(root.right,target-root.val);
}
//无论当前路径是否加出了target,必须去掉最后一个,然后返回父节点,去查找另一条路径,最终的path肯定为null
path.remove(path.size()-1);
}
}
65、在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007 题目保证输入的数组中没有的相同的数字
数据范围:
对于%50的数据,size<=10^4
对于%75的数据,size<=10^5
对于%100的数据,size<=2*10^5**
例如:
输入:
1,2,3,4,5,6,7,0
输出:
7
链接:https://www.nowcoder.com/questionTerminal/96bd6684e04a44eb80e6a68efc0ec6c5
来源:牛客网
/*
*归并排序的思想,最后求得的逆序数进行取摸 % 1000000007
*/
public class Solution {
public int InversePairs(int [] array) {
if(array==null || array.length<=0){
return 0;
}
int pairsNum=mergeSort(array,0,array.length-1);
return pairsNum;
}
public int mergeSort(int[] array,int left,int right){
if(left==right){
return 0;
}
int mid=(left+right)/2;
int leftNum=mergeSort(array,left,mid);
int rightNum=mergeSort(array,mid+1,right);
return (Sort(array,left,mid,right)+leftNum+rightNum)%1000000007;
}
public int Sort(int[] array,int left,int middle,int right){
int current1=middle;
int current2=right;
int current3=right-left;
int temp[]=new int[right-left+1];
int pairsnum=0;
while(current1>=left && current2>=middle+1){
if(array[current1]>array[current2]){
temp[current3--]=array[current1--];
pairsnum+=(current2-middle); //这个地方是current2-middle!!!!
if(pairsnum>1000000007)//数值过大求余
{
pairsnum%=1000000007;
}
}else{
temp[current3--]=array[current2--];
}
}
while(current1>=left){
temp[current3--]=array[current1--];
}
while(current2>=middle+1){
temp[current3--]=array[current2--];
}
//将临时数组赋值给原数组
int i=0;
while(left<=right){
array[left++]=temp[i++];
}
return pairsnum;
}
}
链接:https://www.nowcoder.com/questionTerminal/96bd6684e04a44eb80e6a68efc0ec6c5
来源:牛客网
//大家好,我是yishuihan
/*参考《剑指offer》,有两种思路。第一就是暴力求解法,时间复杂度为o(n^2),空间复杂度o(1)
第二种思路就是使用归并排序的思想进行处理,时间复杂度o(nlog(n)),空间复杂度0(n)*/
class Solution {
public:
int InversePairs(vector<int> data) {
if(data.size()<=1) return 0;//如果少于等于1个元素,直接返回0
int* copy=new int[data.size()];
//初始化该数组,该数组作为存放临时排序的结果,最后要将排序的结果复制到原数组中
for(unsigned int i=0;i<data.size();i++)
copy[i]=0;
//调用递归函数求解结果
int count=InversePairCore(data,copy,0,data.size()-1);
delete[] copy;//删除临时数组
return count;
}
//程序的主体函数
int InversePairCore(vector<int>& data,int*& copy,int start,int end)
{
if(start==end)
{
copy[start]=data[start];
return 0;
}
//将数组拆分成两部分
int length=(end-start)/2;//这里使用的下标法,下面要用来计算逆序个数;也可以直接使用mid=(start+end)/2
//分别计算左边部分和右边部分
int left=InversePairCore(data,copy,start,start+length)%1000000007;
int right=InversePairCore(data,copy,start+length+1,end)%1000000007;
//进行逆序计算
int i=start+length;//前一个数组的最后一个下标
int j=end;//后一个数组的下标
int index=end;//辅助数组下标,从最后一个算起
int count=0;
while(i>=start && j>=start+length+1)
{
if(data[i]>data[j])
{
copy[index--]=data[i--];
//统计长度
count+=j-start-length;
if(count>=1000000007)//数值过大求余
count%=1000000007;
}
else
{
copy[index--]=data[j--];
}
}
for(;i>=start;--i)
{
copy[index--]=data[i];
}
for(;j>=start+length+1;--j)
{
copy[index--]=data[j];
}
//排序
for(int i=start; i<=end; i++) {
data[i] = copy[i];
}
//返回最终的结果
return (count+left+right)%1000000007;
}
};
66、请实现一个函数用来匹配包括’.’和’‘的正则表达式。模式中的字符’.’表示任意一个字符,而’‘表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串”aaa”与模式”a.a”和”ab*ac*a”匹配,但是与”aa.a”和”ab*a”均不匹配
链接:https://www.nowcoder.com/questionTerminal/45327ae22b7b413ea21df13ee7d6429c
来源:牛客网
/*
解这题需要把题意仔细研究清楚,反正我试了好多次才明白的。
首先,考虑特殊情况:
1>两个字符串都为空,返回true
2>当第一个字符串不空,而第二个字符串空了,返回false(因为这样,就无法
匹配成功了,而如果第一个字符串空了,第二个字符串非空,还是可能匹配成
功的,比如第二个字符串是“a*a*a*a*”,由于‘*’之前的元素可以出现0次,
所以有可能匹配成功)
之后就开始匹配第一个字符,这里有两种可能:匹配成功或匹配失败。但考虑到pattern
下一个字符可能是‘*’, 这里我们分两种情况讨论:pattern下一个字符为‘*’或
不为‘*’:
1>pattern下一个字符不为‘*’:这种情况比较简单,直接匹配当前字符。如果
匹配成功,继续匹配下一个;如果匹配失败,直接返回false。注意这里的
“匹配成功”,除了两个字符相同的情况外,还有一种情况,就是pattern的
当前字符为‘.’,同时str的当前字符不为‘\0’。
2>pattern下一个字符为‘*’时,稍微复杂一些,因为‘*’可以代表0个或多个。
这里把这些情况都考虑到:
a>当‘*’匹配0个字符时,str当前字符不变,pattern当前字符后移两位,
跳过这个‘*’符号;
b>当‘*’匹配1个或多个时,str当前字符移向下一个,pattern当前字符
不变。(这里匹配1个或多个可以看成一种情况,因为:当匹配一个时,
由于str移到了下一个字符,而pattern字符不变,就回到了上边的情况a;
当匹配多于一个字符时,相当于从str的下一个字符继续开始匹配)
之后再写代码就很简单了。
*/
class Solution {
public:
bool match(char* str, char* pattern)
{
if (*str == '\0' && *pattern == '\0')
return true;
if (*str != '\0' && *pattern == '\0')
return false;
//if the next character in pattern is not '*'
if (*(pattern+1) != '*')
{
if (*str == *pattern || (*str != '\0' && *pattern == '.'))
return match(str+1, pattern+1);
else
return false;
}
//if the next character is '*'
else
{
if (*str == *pattern || (*str != '\0' && *pattern == '.'))
return match(str, pattern+2) || match(str+1, pattern);
else
return match(str, pattern+2);
}
}
};