剑指Offer面试题(第三十二天)面试题53(1)、53(2)、53(3)、54

* 面试题53:在排序数组中查找数字


     * 题目一:数字在排序数组中出现的次数


     * 统计一个数字在排序数组中出现的次数。
     * 例如:输入排序数组{1,2,3,3,3,3,4,5}和数字3,
     * 由于3在这个数组中出现4次,因此输出4
     * 
     * 二分查找/折半查找      https://www.cnblogs.com/yedushusheng/p/5524166.html
     * 
     * 思路:  时间复杂度O(logn)  空间复杂度O(1)
     * 因为查找的是有序表,左移确定重复出现的第一个k和最后一个k的位置,然后就能够得到出现次数了
     *使用二分查找法的思想进行查找 
     * 在数组中找到第一个k:
     * 二分查找中就是在与中间位置进行比较,所以首先计算中间索引值,然后得到数组的中间值,
     * 判断与k的关系:  若等于,看其是否为第一个k,是的情况:(两种情况:根据前一个元素是否存在区分,一种是没有前一个元素,则索引为0;一种是有前一个元素,索引大于0,且前一个值不等于k),则返回中间值即可
     *                                          否的情况,则将结束节点赋值为中间的前一位
     *                 若小于,则将起始位赋值为中间位置的后一位
     *                 若大于,则将结束位赋值为中间位置的前一位
     * 然后接着判断被分割后的部分是怎样的情况(也就是与k的关系,直到找到第一个k为止)
     *                         
     * 在数组中找到最后一个k:同找第一个k类似

package Test;

public class No53GetNumberOfK {

	/*
	 * 面试题53:在排序数组中查找数字
	 * 题目一:数字在排序数组中出现的次数
	 * 统计一个数字在排序数组中出现的次数。
	 * 例如:输入排序数组{1,2,3,3,3,3,4,5}和数字3,
	 * 由于3在这个数组中出现4次,因此输出4
	 * 
	 * 二分查找/折半查找      https://www.cnblogs.com/yedushusheng/p/5524166.html
	 * 
	 * 思路:  时间复杂度O(logn)  空间复杂度O(1)
	 * 因为查找的是有序表,左移确定重复出现的第一个k和最后一个k的位置,然后就能够得到出现次数了
	 *使用二分查找法的思想进行查找 
	 * 在数组中找到第一个k:
	 * 二分查找中就是在与中间位置进行比较,所以首先计算中间索引值,然后得到数组的中间值,
	 * 判断与k的关系:  若等于,看其是否为第一个k,是的情况:(两种情况:根据前一个元素是否存在区分,一种是没有前一个元素,则索引为0;一种是有前一个元素,索引大于0,且前一个值不等于k),则返回中间值即可
	 * 										 否的情况,则将结束节点赋值为中间的前一位
	 * 				若小于,则将起始位赋值为中间位置的后一位
	 * 				若大于,则将结束位赋值为中间位置的前一位
	 * 然后接着判断被分割后的部分是怎样的情况(也就是与k的关系,直到找到第一个k为止)
	 * 						
	 * 在数组中找到最后一个k:同找第一个k类似
	 * 
	 * 
	 * */
	public static void main(String[] args) {
		// TODO Auto-generated method stub

		No53GetNumberOfK g = new No53GetNumberOfK();
		int[] array = {1,2,3,3,3,3,3,4,5};
		int k = 3;
		System.out.println("数组中"+k+"出现了"+g.GetNumberOfK(array,k)+"次!!!");
	}

	//统计数字k出现的次数
	private int GetNumberOfK(int[] array, int k) {
		// TODO Auto-generated method stub
		int number = 0;
		
		if(array != null && array.length != 0) {
			int first = GetFirstNumberOfK(array,k,0,array.length - 1);
			int last = GetLastNumberOfK(array,k,0,array.length - 1);
			if(first > -1 && last > -1)
				number = last-first+1;
		}
		
		return number;
	}

	//获取最后一个k的索引
	private int GetLastNumberOfK(int[] array, int k, int start, int end) {
		// TODO Auto-generated method stub
		if(start > end)
			return -1;
		
		int midIndex = (start+end)/2;
		int midData = array[midIndex];
		
		if(midData == k) {
			if(array[midIndex+1] != k && midIndex < array.length-1 || midIndex == array.length-1)
				return midIndex;
			else 
				start = midIndex+1;
			

		}
		else if(midData > k)
			end = midIndex -1;
		else
			start = midIndex +1;
		
		
		return GetLastNumberOfK(array,k,start,end);
	}

	//获得第一个k的索引
	private int GetFirstNumberOfK(int[] array, int k, int start, int end) {
		// TODO Auto-generated method stub
		
		if(start > end)
			return -1;
		int midIndex = (start+end)/2;
		int midData = array[midIndex];
		//根据二分查找方法 
		if(midData == k) {
			//若是第一个k值(有-1的位置)    或者   已经到达了数组头部(也是第一个看的含义  没有了-1的位置)   则直接返回即可
			if(midIndex > 0 && array[midIndex-1] != k || midIndex == 0)
				return midIndex;
			//因为要求第一个k  所以要将范围缩小在前半部分
			else 
				end = midIndex - 1;
			
		}
		// midData > k 表示在左边
		else if(midData > k) {
			end = midIndex - 1;
		}
		// midData < k 表示在右边   
		else
			start = midIndex + 1;
		
		return GetFirstNumberOfK(array,k,start,end);
	}

	
	
}

 * 题目二:0~n-1中缺失的数字


     * 一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。
     * 在范围0~n-1内的 n个数字中有且只有一个 数字不在该数组中,请找出这个数字
     * 
     * 
     * 思路:时间复杂度O(logn)  空间复杂度O(1)
     * 因为0~n-1这些数字都是排序的,所以在数组中开始是从0,1,2,3,。。。,刚开始是与索引值相等的
     * 但是若某一值m缺失了,那么,m索引处就不同了,不再是m,并且m+1位置也不同了,。。。
     * 
     * 因此,可以通过二分查询的算法对其进行判断:
     * 判断中间位进行判断,判断这个值是否与索引值相等:
     * 若不想等,则判断前一位是否相等,若想等,则表示这就是缺失的那一位;若不想等,则表示缺失的数字在数组的左侧
     *若相等,则缺失的数字在右侧,则继续判断右侧的数字

package Test;

public class No53Second_GetMissingNumber {

	/*
	 * 面试题53:在排序数组中查找数字
	 * 题目二:0~n-1中缺失的数字
	 * 一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。
	 * 在范围0~n-1内的 n个数字中有且只有一个 数字不在该数组中,请找出这个数字
	 * 
	 * 
	 * 思路:时间复杂度O(logn)  空间复杂度O(1)
	 * 因为0~n-1这些数字都是排序的,所以在数组中开始是从0,1,2,3,。。。,刚开始是与索引值相等的
	 * 但是若某一值m缺失了,那么,m索引处就不同了,不再是m,并且m+1位置也不同了,。。。
	 * 
	 * 因此,可以通过二分查询的算法对其进行判断:
	 * 判断中间位进行判断,判断这个值是否与索引值相等:
	 * 若不想等,则判断前一位是否相等,若想等,则表示这就是缺失的那一位;若不想等,则表示缺失的数字在数组的左侧
	 *若相等,则缺失的数字在右侧,则继续判断右侧的数字
	 *
	 *			 
	 * 
	 * */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		No53Second_GetMissingNumber  s = new No53Second_GetMissingNumber();
		int[] array = {0,1,2,3,-1,4,5};
		System.out.println("数组中缺失的数字是: "+s.GetMissingNumber(array));
	}

	private int GetMissingNumber(int[] array) {
		// TODO Auto-generated method stub
		if(array == null || array.length == 0)
			return -1;
		int left = 0;
		int right = array.length - 1;
		
		while(left <= right) {
			int mid = (left + right)/2;
			if(mid == array[mid])
				left = mid + 1;
			else {
				if(mid-1 == array[mid-1])
					return mid;
				else
					right = mid -1;
			}
		}
		//若left指向最后  则表示最后一位是缺失的数字
		if(left == array.length)
			return array.length;
		
		//无效输入:数组不是按要求排序的、有数字不在0~n-1范围内
		return -1;
	}

}

 * 面试题53:在排序数组中查找数字


     * 题目三:数组中数值和下表相等的元素
     * 假设一个单调递增的数组里的每一个元素都是整数并且是唯一的。
     * 请编程实现一个函数,找出数组中任意一个数值等于其下标的元素。
     * 例如:在数组{-3,-1,1,3,5}中,数字3它的下标相等。
     * 
     * 思路:同样运用二分查询的算法进行查找
     * 取中间值m和下标i进行比较:
     * 若m>i,数字大于下标,则可能相等的数字 在左侧
     * 若m<i,数字小于下标,则可能相等的数字 在右侧
     * 若m==i,找到数字与下标相等

package Test;

public class No53Third_GetNumberSameAsIndex {

	/*
	 * 面试题53:在排序数组中查找数字
	 * 题目三:数组中数值和下表相等的元素
	 * 假设一个单调递增的数组里的每一个元素都是整数并且是唯一的。
	 * 请编程实现一个函数,找出数组中任意一个数值等于其下标的元素。
	 * 例如:在数组{-3,-1,1,3,5}中,数字3它的下标相等。
	 * 
	 * 思路:同样运用二分查询的算法进行查找
	 * 取中间值m和下标i进行比较:
	 * 若m>i,数字大于下标,则可能相等的数字 在左侧
	 * 若m<i,数字小于下标,则可能相等的数字 在右侧
	 * 若m==i,找到数字与下标相等
	 * 
	 * */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		No53Third_GetNumberSameAsIndex g = new No53Third_GetNumberSameAsIndex();
		int[] array = {-3,-1,1,3,5};
		//其中下标是从0~n-1,并且返回的是下标值  ********
		System.out.println("数组中数字和其下表相等的位置是:"+g.GetNumberSameAsIndex(array));
	}

	private int GetNumberSameAsIndex(int[] array) {
		// TODO Auto-generated method stub
		if(array == null || array.length == 0)
			return -1;
		
		int left = 0;
		int right = array.length -1;
		
		while(left <= right) {
			int mid = (left + right)/2;
			if(mid == array[mid])
				return mid;
			else if(mid > array[mid])
				left = mid+1;
			else
				right = mid-1;
		}
		
		return -1;
	}

}

 * 面试题54:二叉搜索树的第K大节点


     * 题目:给定一个二叉搜索树,请找出其中第K大的节点。
     * 例如:在5->(left)3   5->(right)7 的二叉搜索树中,
     *             3->(left)2    3->(right)4
     *             7->(left)6      7->(right)8
     * 按节点数值大小排序,第三大节点的值是4
     * 
     * 
     * 思路:使用中序遍历二叉树的方法,进行遍历。
     * {2,3,4,5,6,7,8}则将其以递增的形式输出,则此数组中第3个就是要找的
 

package Test;

import Test.No32First_PrintBinaryTreeFromTopToBottom.BinaryTreeNode;

public class No54KthNode {

	/*
	 * 面试题54:二叉搜索树的第K大节点
	 * 题目:给定一个二叉搜索树,请找出其中第K大的节点。
	 * 例如:在5->(left)3   5->(right)7 的二叉搜索树中,
	 * 			3->(left)2    3->(right)4
	 * 			7->(left)6	  7->(right)8
	 * 按节点数值大小排序,第三大节点的值是4
	 * 
	 * 
	 * 思路:使用中序遍历二叉树的方法,进行遍历。
	 * {2,3,4,5,6,7,8}则将其以递增的形式输出,则此数组中第3个就是要找的
	 * 
	 * 
	 * */
	
	
	static class BinaryTreeNode{
		int val;
		BinaryTreeNode left;
		BinaryTreeNode right;
		
		BinaryTreeNode(int val){
			this.val = val;
		}
		
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		No54KthNode k = new No54KthNode();
		BinaryTreeNode root1 = new BinaryTreeNode(5);
		BinaryTreeNode one = new BinaryTreeNode(3);
		BinaryTreeNode two = new BinaryTreeNode(7);
		BinaryTreeNode three = new BinaryTreeNode(2);
		BinaryTreeNode four = new BinaryTreeNode(4);
		BinaryTreeNode five = new BinaryTreeNode(6);
		BinaryTreeNode six = new BinaryTreeNode(8);
		
		root1.left = one;
		root1.right = two;
		one.left = three;
		one.right = four;
		two.left = five;
		two.right = six;
		three.left = null;
		three.right = null;
		four.left = null;
		four.right = null;
		five.left = null;
		five.right = null;
		six.left = null;
		six.right = null;
		
		int index = 3;
		System.out.println("按照节点数值大小进行排序,数组中第"+index+"大节点的值是:"+k.GetKthNode(root1,index).val);
	}
	int count = 0;
	private BinaryTreeNode GetKthNode(BinaryTreeNode root1, int index) {
		// TODO Auto-generated method stub
		if(root1 == null || index <= 0)
			return null;
		
		//中序遍历二叉树  左 根 右
		//左
		BinaryTreeNode node = GetKthNode(root1.left,index);
		//在中序遍历时,总是走到了最左边的叶子节点    然后 叶子节点的根  (若存在) 然后根的右子节点
		if(node != null)//返回给上一级调用这个函数的地方  其父节点
			return node;
		
		//根    类似于中序遍历时,打印根节点的值一样的作用   然后当找到了第index大的元素时,则返回给原函数
		count ++;
		if(count == index)
			return root1;
		
		//右
		node = GetKthNode(root1.right,index);
		if(node != null)
			return node;
		
		
		return null;
	}

	
	

}

 * 面试题55:二叉树的深度


     * 题目一:二叉树的深度


     * 输入一棵二叉树的根节点,求树的深度。
     * 从根节点到叶节点依次经过的节点(含根、叶节点)
     * 形成树的一条路径,最长路径的长度为树的深度。
     * 
     * 
     * 思路:计算树的深度有以下几种几种情况:
     * 1>若只有根节点,则深度为1
     * 2>若根节点只有右子树,则根节点深度=右子树深度+1
     * 3>若根节点只有左子树,则根节点深度=左子树深度+1
     * 4>若根节点有左、右子树,则根节点深度=max{左子树深度,右子树深度} +1
 

package Test;

import Test.No54KthNode.BinaryTreeNode;

public class No55First_TreeDepth {

	/*
	 * 面试题55:二叉树的深度
	 * 题目一:二叉树的深度
	 * 输入一棵二叉树的根节点,求树的深度。
	 * 从根节点到叶节点依次经过的节点(含根、叶节点)
	 * 形成树的一条路径,最长路径的长度为树的深度。
	 * 
	 * 
	 * 思路:计算树的深度有以下几种几种情况:
	 * 1>若只有根节点,则深度为1
	 * 2>若根节点只有右子树,则根节点深度=右子树深度+1
	 * 3>若根节点只有左子树,则根节点深度=左子树深度+1
	 * 4>若根节点有左、右子树,则根节点深度=max{左子树深度,右子树深度} +1
	 * 
	 * */
	static class BinaryTreeNode{
		int val;
		BinaryTreeNode left;
		BinaryTreeNode right;
		
		BinaryTreeNode(int val){
			this.val = val;
		}
		
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		No55First_TreeDepth f = new No55First_TreeDepth();
		BinaryTreeNode root1 = new BinaryTreeNode(5);
		BinaryTreeNode one = new BinaryTreeNode(3);
		BinaryTreeNode two = new BinaryTreeNode(7);
		BinaryTreeNode three = new BinaryTreeNode(2);
		BinaryTreeNode four = new BinaryTreeNode(4);
		BinaryTreeNode five = new BinaryTreeNode(6);
		BinaryTreeNode six = new BinaryTreeNode(8);
		
		root1.left = one;
		root1.right = two;
		one.left = three;
		one.right = four;
		two.left = null;
		two.right = six;
		three.left = null;
		three.right = null;
		four.left = five;
		four.right = null;
		five.left = null;
		five.right = null;
		six.left = null;
		six.right = null;
		
		System.out.println("二叉树的深度为:"+f.TreeDepth(root1));
		
	}

	//因为是从为null的节点开始计数,叶节点是由为空的节点+1的来,以此类推
	private int TreeDepth(BinaryTreeNode root1) {
		// TODO Auto-generated method stub
		if(root1 == null)
			return 0;
		
		int left = TreeDepth(root1.left);
		int right =TreeDepth(root1.right);
		return (left > right)?(left+1):(right+1);
	}

}

 * 面试题55:二叉树的深度


     * 题目二:平衡二叉树


     * 输入一棵二叉树的根节点,判断该树是不是平衡二叉树。
     * 如果某二叉树中二年以节点的左右子树的深度相差不超过1,
     * 那么它就是一颗平衡二叉树
     * 
     * 思路:
     * 方法一:每个节点会遍历多次
     * 在求二叉树深度,对其进行比较保证其左右子树差不超过1
     *优点:简洁
     *缺点:某一个节点会被重复遍历多次,时间效率低 
     * 
     * 方法二;每个节点遍历一次
     * 可以采用后序遍历  左右根的顺序  
     * 遍历每个节点的时候记录其深度,
     * 这样就可以一边遍历,一边判断深度的大小了

package Test;

import Test.No55First_TreeDepth.BinaryTreeNode;

public class No55Second_isBalanced {

	/*
	 * 面试题55:二叉树的深度
	 * 题目二:平衡二叉树
	 * 输入一棵二叉树的根节点,判断该树是不是平衡二叉树。
	 * 如果某二叉树中二年以节点的左右子树的深度相差不超过1,
	 * 那么它就是一颗平衡二叉树
	 * 
	 * 思路:
	 * 方法一:每个节点会遍历多次
	 * 在求二叉树深度,对其进行比较保证其左右子树差不超过1
	 *优点:简洁
	 *缺点:某一个节点会被重复遍历多次,时间效率低 
	 * 
	 * 方法二;每个节点遍历一次
	 * 可以采用后序遍历  左右根的顺序  
	 * 遍历每个节点的时候记录其深度,
	 * 这样就可以一边遍历,一边判断深度的大小了
	 * 
	 * 
	 * 
	 * */
	
	static class BinaryTreeNode{
		int val;
		BinaryTreeNode left;
		BinaryTreeNode right;
		
		BinaryTreeNode(int val){
			this.val = val;
		}
		
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		No55Second_isBalanced s = new No55Second_isBalanced();
		BinaryTreeNode root1 = new BinaryTreeNode(5);
		BinaryTreeNode one = new BinaryTreeNode(3);
		BinaryTreeNode two = new BinaryTreeNode(7);
		BinaryTreeNode three = new BinaryTreeNode(2);
		BinaryTreeNode four = new BinaryTreeNode(4);
		BinaryTreeNode five = new BinaryTreeNode(6);
		BinaryTreeNode six = new BinaryTreeNode(8);
		
		root1.left = one;
		root1.right = two;
		one.left = three;
		one.right = four;
		two.left = null;
		two.right = six;
		three.left = null;
		three.right = null;
		four.left = five;
		four.right = null;
		five.left = null;
		five.right = null;
		six.left = null;
		six.right = null;
		
		System.out.println("方法一:");
		if(s.isBalanced_Solution1(root1))
			System.out.println("该二叉树是平衡二叉树!");
		else
			System.out.println("该二叉树不是平衡二叉树!!!!");
		
		
		System.out.println("方法二:");
		if(s.isBalanced_Solution2(root1))
			System.out.println("该二叉树是平衡二叉树!");
		else
			System.out.println("该二叉树不是平衡二叉树!!!!");

	}

	//方法二;每个节点仅遍历一次
	private boolean isBalanced_Solution2(BinaryTreeNode root1) {
		// TODO Auto-generated method stub
		if(root1 == null)
		{
			return true;
		}
		int res = treeDepth(root1);
		if(res == -1)
			return false;
		return true;
	}

	//计算深度
	private int treeDepth(BinaryTreeNode root1) {
		// TODO Auto-generated method stub
		if(root1 == null)
			return 0;
		
		int leftDepth = treeDepth(root1.left);
		if(leftDepth == -1)
			return -1;
		
		int rightDepth = treeDepth(root1.right);
		if(rightDepth == -1)
			return -1;
		
		//判断该节点是否平衡
		int diff = leftDepth - rightDepth;
		if(diff > 2 || diff < -2)
			return -1;
		
		//返回根结点的长度 : 左右子树中较大值+1
		return (leftDepth>rightDepth)?(leftDepth+1):(rightDepth+1);
	}



	//方法一:重复遍历节点
	private boolean isBalanced_Solution1(BinaryTreeNode root1) {
		// TODO Auto-generated method stub
		if(root1 == null)
			return true;
		
		int left = TreeDepth(root1.left);
		int right = TreeDepth(root1.right);
		int diff = left - right;
		if(diff < -1 || diff >2)
			return false;
		
		return isBalanced_Solution1(root1.left) && isBalanced_Solution1(root1.right);
	}
	//计算树的深度
	private int TreeDepth(BinaryTreeNode root1) {
		// TODO Auto-generated method stub
		if(root1 == null)
			return 0;
		
		int left = TreeDepth(root1.left);
		int right =TreeDepth(root1.right);
		return (left > right)?(left+1):(right+1);
	}
}

猜你喜欢

转载自blog.csdn.net/weixin_43137176/article/details/89606004