面试题8、9、10、11

面试题8.二叉树的下一个节点

题目描述:

给定一个二叉树和其中的一个节点,请找出中序遍历顺序的下一个节点并返回。注意,树中的节点不仅包括左右子节点,同时包含指向父节点的指针。

1、暴力法

可以先依据已给的节点找到根节点,再对根节点所在的树进行中序遍历,最后根据中序遍历结果找到给定节点的下一节点

/*
public class TreeLinkNode {
    int val;
    TreeLinkNode left = null;
    TreeLinkNode right = null;
    TreeLinkNode next = null;

    TreeLinkNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
	List<TreeLinkNode> list = new ArrayList<>();
    public TreeLinkNode GetNext(TreeLinkNode pNode) {
        TreeLinkNode par = pNode;
        while(par.next != null) par = par.next; //找到根节点
        InOrder(par);
        for(int i = 0; i < list.size() - 1; i++) { //遍历集合
        	if(par == list.get(i)) {
        		//如果匹配到的节点是最后一个节点,则说明没有下一节点,返回null。若存在则返回下一节点
        		return list.get(i + 1);
        	}
        }
        return null;
    }
    //对树进行中序遍历,并将每个节点存入集合中
    public void InOrder(TreeLinkNode par) {
    	if(par != null) {
    		InOrder(par.left);
    		list.add(par);
    		InOrder(par.right);
    	}
    }
}

2、优解

在这里插入图片描述
可以把中序下一节点归为几种类型:

1、有右子树,则下一节点是右子树中的最左节点,例如 B 的下一节点是 H。

2、无右子树,且该节点是其父节点的左子节点,则下一节点是该节点的父节点,例如 H 的下一节点是 E。

3、无右子树,且该节点是其父节点的右子节点,则我们一直沿着父节点追溯,直到找到某个节点是它自己的父节点的左子节点,如果存在这样的节点,那么这个节点的父节点就是我们要找的下一个节点。若不存在这样的节点,则没有下一节点。
例如 :I 的父亲节点是 E,E 也是其父亲节点的右子节点,继续回溯,找到 E 的父亲节点是 B,而 B 是其父节点 A 的左子节点,那我们就找到了 I 的下一节点是 A。而 G 就找不到这样的父节点,则G没有下一节点。

/*
public class TreeLinkNode {
    int val;
    TreeLinkNode left = null;
    TreeLinkNode right = null;
    TreeLinkNode next = null;

    TreeLinkNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public TreeLinkNode GetNext(TreeLinkNode pNode) {
    	if(pNode == null) return null;
		//1.有右子树
		if(pNode.right != null) {
			TreeLinkNode pRight = pNode.right;
			while(pRight.left != null) pRight = pRight.left; //不断去找最左子节点
			return pRight;
		}
		//2.没有右子树
		while(pNode.next != null) { //回溯父节点
			//如果当前节点是其父节点的左子节点,那么其父节点就是答案
			if(pNode.next.left == pNode) return pNode.next; 
			//不然就继续回溯父节点
			pNode = pNode.next;
		}
		return null;
    }
}

————————————————————————————————————————

面试题9.用两个栈实现队列

在这里插入图片描述
解题思路:

  • 栈无法实现队列功能:栈底元素(对应队首元素)无法直接删除,需要将上方所有元素出栈。
  • 双栈可以实现列表倒序:设含三个元素的栈 A = [1,2,3] 和空栈 B = []。若循环执行 A 元素出栈并加入栈 B,直到栈 A 为空,此时 A = [],B = [3,2,1],即栈 B 元素实现栈 A 元素倒序
  • 利用栈 B 删除队首元素:倒序后,B 执行出栈操作则相当于删除 A 的栈底元素,即对应队首元素。

函数设计:

栈 A 用于加入队尾操作,栈 B 用于将元素倒序,从而实现删除队首元素

  • 加入队尾函数,将数字加入栈 A 即可。
  • 删除队首元素,有三种情况:
    • 当栈 B 不为空:B 中仍有已完成倒序的元素,因此直接返回 B 的栈顶元素。
    • 否则,当 A 为空:即两个栈都为空,无元素,因此返回 -1 。
    • 否则,将栈 A 元素全部转移至栈 B 中,实现元素倒序,并返回栈 B 的栈顶元素。
class CQueue {
    LinkedList<Integer> A, B;
    public CQueue() {
        A = new LinkedList<Integer>();
        B = new LinkedList<Integer>();
    }
    public void appendTail(int value) {
        A.addLast(value);
    }
    public int deleteHead() {
    	if(!B.isEmpty()) return B.removeLast(); //B不为空,则返回B的最后一个元素
    	if(A.isEmpty()) return -1; //A,B均为空,则不存在元素,返回-1
    	while(!A.isEmpty()) { //A不为空,B为空,将A的元素全部出栈,加入到B中,则实现了A元素的倒序
    		B.addLast(A.removeLast());
    	}
    	return B.removeLast();
    }

———————————————————————————————————————

面试题10.斐波那契数列

题目一

在这里插入图片描述

1.递归法
  • 原理:把 f(n) 问题的计算拆分成 f(n-1) 和 f(n-2) 两个子问题的计算,并递归,以 f(0) 和 f(1) 为终止条件。
  • 缺点:大量重复的递归计算,例如 f(n) 和 f(n-1) 两个向下递归需要各自计算 f(n-2) 的值。
class Solution {
    public int fib(int n) {
        if(n <= 0) return 0;
        if(n == 1) return 1;
        return (fib(n - 1) + fib(n - 2)) % 1000000007;
    }
}

2.动态规划
  • 状态定义:设 dp 为一维数组,其中 dp[i] 的值代表斐波那契数列第 i 个数字。
  • 状态转移方程: dp[i] = dp[i-1] + dp[i-2]
  • 初始化: dp[0] = 0,dp[1] = 1

由于 dp 列表第 i 项只与第 i-1 和第 i-2 项有关,因此只需要初始化三个整形变量 temp, f0, f1 ,利用辅助变量 temp 使 f0,f1 两数字交替前进即可

class Solution {
    public int fib(int n) {
		if(n <= 0) return 0;
		int f0 = 0;
		int f1 = 1;
		int temp = 0;
		for(int i = 2; i <= n; i++) {
			temp = (f0 + f1) % 1000000007;
			f0 = f1;
			f1 = temp;
		}
		return f1;
    }
}

题目二

在这里插入图片描述
设跳上 n 级台阶有 f(n) 种跳法。在所有跳法中,青蛙的最后一步只有两种情况:跳上1级或者2级。

  • 1.当为 1 级台阶时:剩 n-1 个台阶,此情况共有 f(n-1) 种跳法
  • 2.当为 2 级台阶时:剩 n-2 个台阶,此情况共有 f(n-2) 种跳法

f(n) 为以上两种情况之和,即 f(n) = f(n-1) + f(n-2)
在这里插入图片描述
动态规划:

  • 状态定义:dp[i]代表跳上第 i 级台阶的方法数
  • 状态转移方程:dp[i] = dp[i - 1] + dp[i - 2]
  • 初始化: dp[0] = 1,dp[1] = 1

由于第 i 项只与第 i-1 和第 i-2 项有关,所以只需要初始化三个整型变量

class Solution {
    public int numWays(int n) {
    	if(n <= 0) return 0;
		int f0 = 1;
		int f1 = 1;
		int temp = 0;
		for(int i = 2; i <= n; i++) {
			temp = (f0 + f1) % 1000000007;
			f0 = f1;
			f1 = temp;
		}
		return f1;
    }
}

———————————————————————————————————————

面试题11.旋转数组的最小数字

在这里插入图片描述
寻找旋转数组的最小元素即为寻找右排序数组的收个元素numbers[x],称 x 为旋转点
在这里插入图片描述
1.初始化:声明 i,j 双指针分别指向nums数组前后两端;

2.循环二分:设 m = (i + j) / 2 为每次二分的中点,分为三种情况:

  • 当 nums[m] > nums[j] 时,m 一定在左排序数组中,即旋转点 x 一定在 [m+1,j] 闭区间内,因此执行 i = m + 1;
  • 当 nums[m] < nums[j] 时,m 一定在右排序数组中,即旋转点 x 一定在 [i,m] 闭区间内,因此执行 j = m;
  • 当 nums[m] = nums[j] 时,无法判断 m 在哪个排序区间中,所以,执行 j = j - 1 缩小判断范围

3.返回值:当 i = j 时跳出二分循环,并返回 nums[i] 即可。
在这里插入图片描述

class Solution {
    public int minArray(int[] numbers) {
    	int len = number.length;
		int i = 0, j = len - 1;
		while(i < j) {
			int m = (i + j) / 2;
			if(numbers[m] > numbers[j]) i = m + 1;
			else if(numbers[m] < numbers[j]) j = m;
			//方案一
			//else j = j - 1;
			
			//方案二 
			//实际上,当出现 nums[m]=nums[j] 时,一定有区间[i,m]内所有元素相等或
			//区间[m,j]内所有元素相等(或两者皆满足)。对于寻找此类数组的最小值问题,
			//可直接放弃二分查找,而使用线性查找替代。
			else {
				int x = i;
				for(int k = i + 1; k < j; k++) {
					if(numbers[k] < numbers[x]) x = k;
				}
				return numbers[x];
			}
		}
		return numbers[i];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值