剑指offer学习5(Java语言版)

面试题31:栈的压入弹出序列

在这里插入图片描述

public class Demo31 {
	public static void main(String[] args) {
		int[] pushArr = { 1, 2, 3, 4, 5 };
		int[][] popArr = { { 4, 3, 5, 2, 1 }, { 4, 3, 5, 1, 2 } };
		for (int i = 0; i < popArr.length; i++) {
			System.out.println(isPopOrder(pushArr, popArr[i]));
		}
	}

	public static boolean isPopOrder(int[] pushArr, int[] popArr) {
		if (pushArr == null || popArr == null
			||pushArr.length == 0 || popArr.length == 0 
			|| pushArr.length != popArr.length) return false;
		
		    Stack<Integer> stack = new Stack<Integer>();
		    int j = 0;
		    for (int current : pushArr) {
		      stack.push(current);
		      while ((!stack.empty()) && (stack.peek() == popArr[j])) {
		        stack.pop();
		        j++;
		      }
		    }
		    
		    if (stack.empty()) {
		      return true;
		    } else {
		      return false;
		    }
	}
}

面试题32:从上到下打印二叉树

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

public class Demo1 {
	public static void main(String[] args){
		BiTreeNode root = createBiTree();
		System.out.println("一行打印:");
		printBiTreeUpDown(root);
		System.out.println("\n逐行打印:");
		printBiTreeByLine(root);
		BiTreeNode root2 = createBiTree2();
		System.out.println("\n逐行Z字形打印:");
		printBiTreeByZ(root2);
	}
	
	// 层次遍历,一行打印
	public static void printBiTreeUpDown(BiTreeNode root) {
		if (root == null) return;

		Queue<BiTreeNode> queue = new LinkedList<BiTreeNode>();
		queue.offer(root);

		while (!queue.isEmpty()) {
			BiTreeNode node = queue.poll();
			System.out.print(node.val + " ");
			if (node.left != null) {
				queue.offer(node.left);
			}
			if (node.right != null) {
				queue.offer(node.right);
			}
		}
	}
	
	// 层次遍历:逐行打印
	public static void printBiTreeByLine(BiTreeNode root) {
		if (root == null) return;

		Queue<BiTreeNode> queue = new LinkedList<BiTreeNode>();
		queue.offer(root);

		int count = 1;
		while (!queue.isEmpty()) {
			int k;
			for(k = count, count = 0; k > 0; k--){
				BiTreeNode node = queue.poll();
				System.out.print(node.val + " ");
				if (node.left != null) {
					queue.offer(node.left);
					count++;
				}
				if (node.right != null) {
					queue.offer(node.right);
					count++;
				}
			}
			System.out.println();
		}
	}
	
	// 层次遍历:逐行Z字形打印
	public static void printBiTreeByZ(BiTreeNode root) {
		if (root == null)
			return;

		Stack<BiTreeNode> S1 = new Stack<BiTreeNode>(); // 装正序
		Stack<BiTreeNode> S2 = new Stack<BiTreeNode>(); // 
		S1.push(root);
		
		while (!S1.isEmpty() || !S2.isEmpty()) {
			if (!S1.isEmpty() && S2.isEmpty()) {
				while (!S1.isEmpty()) {
					BiTreeNode node = S1.pop();
					System.out.print(node.val + " ");
					if (node.left != null) {
						S2.push(node.left);
					}
					if (node.right != null) {
						S2.push(node.right);
					}
				}
			} else if (S1.isEmpty() && !S2.isEmpty()) {
				while (!S2.isEmpty()) {
					BiTreeNode node = S2.pop();
					System.out.print(node.val + " ");
					if (node.right != null) {
						S1.push(node.right);
					}
					if (node.left != null) {
						S1.push(node.left);
					}
				}
			}
			System.out.println();
		}
		
	}
	
	// 构造一颗树
	private static BiTreeNode createBiTree() {
		BiTreeNode root = new BiTreeNode(8);
		BiTreeNode node2 = new BiTreeNode(6);
		BiTreeNode node3 = new BiTreeNode(10);
		BiTreeNode node4 = new BiTreeNode(5);
		BiTreeNode node5 = new BiTreeNode(7);
		BiTreeNode node6 = new BiTreeNode(9);
		BiTreeNode node7 = new BiTreeNode(11);
		
		root.left = node2;
		root.right = node3;
		node2.left = node4;
		node2.right = node5;
		node3.left = node6;
		node3.right = node7;
		
		return root;
	}
	
	// 构造一颗树
		private static BiTreeNode createBiTree2() {
			BiTreeNode root = new BiTreeNode(1);
			BiTreeNode node2 = new BiTreeNode(2);
			BiTreeNode node3 = new BiTreeNode(3);
			BiTreeNode node4 = new BiTreeNode(4);
			BiTreeNode node5 = new BiTreeNode(5);
			BiTreeNode node6 = new BiTreeNode(6);
			BiTreeNode node7 = new BiTreeNode(7);
			BiTreeNode node8 = new BiTreeNode(8);
			BiTreeNode node9 = new BiTreeNode(9);
			BiTreeNode node10 = new BiTreeNode(10);
			BiTreeNode node11 = new BiTreeNode(11);
			BiTreeNode node12 = new BiTreeNode(12);
			BiTreeNode node13 = new BiTreeNode(13);
			BiTreeNode node14 = new BiTreeNode(14);
			BiTreeNode node15 = new BiTreeNode(15);
			
			root.left = node2;
			root.right = node3;
			node2.left = node4;
			node2.right = node5;
			node3.left = node6;
			node3.right = node7;
			node4.left = node8;
			node4.right = node9;
			node5.left = node10;
			node5.right = node11;
			node6.left = node12;
			node6.right = node13;
			node7.left = node14;
			node7.right = node15;
			
			return root;
		}
}

输出结果:

一行打印:
8 6 10 5 7 9 11 
逐行打印:
8 
6 10 
5 7 9 11 

逐行Z字形打印:
1 
3 2 
4 5 6 7 
15 14 13 12 11 10 9 8 

面试题33:二叉搜索树的后续遍历序列

在这里插入图片描述
在这里插入图片描述
分析:二叉搜索树左子树节点比根节点小,右子树节点不根节点大。后序遍历序列,最后一个是根节点,从右往左找,找到第一个小于根节点的节点

  1. 若该节点挨着根节点,说明没有右子树
  2. 没有,则说明没有左子树

找到左右子树位置后,可以判断左子树所有节点是否小于根节点,右子树所有节点是否大于根节点,不满足则返回false。满足,则左子树和右子树依次递归下去。

public class Demo {
	public static void main(String[] args) {
		int[][] arrs = { 
				{ 5, 7, 6, 9, 11, 10, 8 },    // 满二叉树 true
				{ 1, 2, 3},                   // 左支树 true
				{ 6, 5, 4},                   // 右支树 true
				{5},                          // 一个节点的二叉树 true
				{ 5, 9, 7, 6, 11, 10, 8 },    // 非二叉搜索树 false
				{}
		};
		for(int [] arr :arrs){
			boolean bool = isPostOrder(arr);
			System.out.println(bool);
		}
	}

	public static boolean isPostOrder(int[] arr) {
		int len = arr.length;
		if (arr == null || len < 2) // 把空的情况也作为true吧,这个看出题者意图归到哪边
			return true;
		return isPostOrder(arr, 0, len - 1);
	}

	private static boolean isPostOrder(int[] arr, int start, int end) {

		if(end - start <= 1) return true;
		
		int k;// 找到左子树根节点
		for (k = end - 1; k >= start; k--) {
			if (arr[k] < arr[end])
				break;
		}
		// 检查右子树是否大于根节点
		for (int i = k + 1; i < end; i++) {
			if (arr[i] < arr[end]) return false;
		}
		// 检查左子树是否大于根节点
		for (int i = start; i < k; i++) {
			if (arr[i] > arr[end]) return false;
		}

		return isPostOrder(arr, start, k) && isPostOrder(arr, k + 1, end - 1);
	}
}

面试题34:二叉树中和为某一值的路径

在这里插入图片描述

public class Demo {
	public static void main(String[] args) {
		BiTreeNode root = createBiTree();
		printSumPath(root, 22);
	}

	public static void printSumPath(BiTreeNode root, int k) {
		if (root == null)
			return;
		Stack<Integer> stack = new Stack<Integer>();
		printSumPath(root, k, stack);
	}

	public static void printSumPath(BiTreeNode root, int k, Stack<Integer> path) {
		if (root == null)
			return; // 结束条件

		if (root.left == null && root.right == null) { // 如果到了叶子节点
			if (root.val == k) {
				for (int i : path)
					System.out.print(i + ", ");
				System.out.println(root.val);
			}
		} else { // 非叶子节点递归
			path.push(root.val);
			// 每次减去root.val,到叶节点时刚好和叶节点相等
			printSumPath(root.left, k - root.val, path);
			printSumPath(root.right, k - root.val, path);
			path.pop(); // 回退
		}
	}

	// 构造一颗树
	private static BiTreeNode createBiTree() {
		BiTreeNode root = new BiTreeNode(10);
		BiTreeNode node2 = new BiTreeNode(5);
		BiTreeNode node3 = new BiTreeNode(12);
		BiTreeNode node4 = new BiTreeNode(4);
		BiTreeNode node5 = new BiTreeNode(7);

		root.left = node2;
		root.right = node3;
		node2.left = node4;
		node2.right = node5;

		return root;
	}
}

测试输出:

10, 5, 7
10, 12

面试题35:复杂链表的复制

在这里插入图片描述
参考:复杂链表的复制(Java)

public class Demo {

	public ComplexListNode copyComplexLinkedList(ComplexListNode head){
		if(head == null) return null;
		
		Map<ComplexListNode, ComplexListNode> map = new HashMap<>();
		
		ComplexListNode p1 = head;  // 原链表指针
		ComplexListNode newHead = new ComplexListNode(p1.val);
		ComplexListNode p2 = newHead; // 新链表指针
		map.put(p1, p2);
		
		// 先遍历第一遍,建立单链表
		while(p1.next != null){
			p2.next = new ComplexListNode(p1.next.val); // 拷贝一个节点
			p2 = p2.next; // p2指到最后一个节点
			p1 = p1.next; 
			map.put(p1, p2); // HashMap保存对应<原节点,新节点>
		}
		
		// 再遍历一遍,建立sibling关系
		p1 = head;
		p2 = newHead;
		while(p1 != null){
			p2.next = map.get(p1.sibling);
			p1 = p1.next;
			p2 = p2.next;
		}
		return newHead;
	}
}

面试题36:二叉搜索树与双向链表

在这里插入图片描述

public class Demo {

	public static void main(String[] args) {
		BiTreeNode root = createBiTree();
		BiTreeNode head = convert(root);
		// 遍历双向链表并打印
		while (head != null) {
			System.out.println(head.val);
			head = head.right;
		}
	}

	// 记录子树链表的最后一个节点,终结点只可能为只含左子树的非叶节点与叶节点
	private static BiTreeNode leftLast = null;

	public static BiTreeNode convert(BiTreeNode root) {
		if (root == null)
			return null;
		if (root.left == null && root.right == null) {
			leftLast = root;// 最后的一个节点可能为最右侧的叶节点
			return root;
		}
		// 1.将左子树构造成双链表,并返回链表头节点
		BiTreeNode left = convert(root.left);
		// 3.如果左子树链表不为空的话,将当前root追加到左子树链表
		if (left != null) {
			leftLast.right = root;
			root.left = leftLast;
		}
		leftLast = root;// 当根节点只含左子树时,则该根节点为最后一个节点
		// 4.将右子树构造成双链表,并返回链表头节点
		BiTreeNode right = convert(root.right);
		// 5.如果右子树链表不为空的话,将该链表追加到root节点之后
		if (right != null) {
			right.left = root;
			root.right = right;
		}
		return left != null ? left : root;
	}

	// 构造一颗树
	private static BiTreeNode createBiTree() {
		BiTreeNode root = new BiTreeNode(10);
		BiTreeNode node2 = new BiTreeNode(6);
		BiTreeNode node3 = new BiTreeNode(14);
		BiTreeNode node4 = new BiTreeNode(4);
		BiTreeNode node5 = new BiTreeNode(8);
		BiTreeNode node6 = new BiTreeNode(12);
		BiTreeNode node7 = new BiTreeNode(16);

		root.left = node2;
		root.right = node3;
		node2.left = node4;
		node2.right = node5;
		node3.left = node6;
		node3.right = node7;

		return root;
	}
}

面试题38:字符串的排列

在这里插入图片描述

public class Demo {
	public static void main(String[] args){
		Demo demo = new Demo();
		String str = "abc";
		ArrayList<String> ss = demo.permutation(str);
		for(String s: ss){
			System.out.println(s);
		}
	}
	
	public ArrayList<String> permutation(String str) {
		ArrayList<String> resultList = new ArrayList<>();
		if (str.length() == 0)
			return resultList;
		// 递归的初始值为(str数组,空的list,初始下标0)
		fun(str.toCharArray(), resultList, 0);
		Collections.sort(resultList);
		return resultList;
	}

	private void fun(char[] ch, List<String> list, int i) {
		// 这是递归的终止条件,就是i下标已经移到char数组的末尾的时候,考虑添加这一组字符串进入结果集中
		if (i == ch.length - 1) {
			// 判断一下是否重复
			if (!list.contains(new String(ch))) {
				list.add(new String(ch));
				return;
			}
		} else {
			for (int j = i; j < ch.length; j++) {
				swap(ch, i, j);
				fun(ch, list, i + 1);
				swap(ch, i, j);
			}
		}
	}
	// 交换数组的两个下标的元素
	private void swap(char[] str, int i, int j) {
		if (i != j) {
			char t = str[i];
			str[i] = str[j];
			str[j] = t;
		}
	}
}

面试题39:数组中出现次数超过一半的数

在这里插入图片描述

public class Demo {
	public static void main(String[] args) throws Exception {
		
		int[] arr = { 1, 2, 3, 2, 2, 2, 5, 4, 2 };
		int num = moreThanHalfNum(arr);
		System.out.println(num);
		
		int[] arr1 = { 1, 4, 3, 2, 6, 2, 5, 4, 2 };
		int num1 = moreThanHalfNum1(arr1);
		System.out.println(num1);
	}

	// 用HashMap保存:时间复杂度O(n), 空间复杂度 O(n)
	private static int moreThanHalfNum(int[] arr) throws Exception {
		if (arr == null)
			throw new Exception("空数组");
		Map<Integer, Integer> map = new HashMap<>();
		for (int i = 0; i < arr.length; i++) {
			if (map.get(arr[i]) == null) {
				map.put(arr[i], 1);
			} else {
				map.put(arr[i], map.get(arr[i]) + 1);
			}
		}

		int maxKey = 0;
		int maxValue = 0;
		for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
			if (entry.getValue() > maxValue) {
				maxValue = entry.getValue();
				maxKey = entry.getKey();
			}
		}
		// 这里应当检查一下该数是否真的过半
		if (!checkMoreThanHalf(arr, maxKey)) {
			System.out.print("没有过半的数:");
			return 0;
		}
		return maxKey;
	}
	
	// 改进:时间复杂度O(n), 空间复杂度O(1)
	private static int moreThanHalfNum1(int[] arr) throws Exception {
		if (arr == null || arr.length <= 0) throw new Exception("空数组");
		
		int result = arr[0];
		int times = 1;
		for (int i = 1; i < arr.length; i++) {
			if(times == 0){
				result = arr[i];
				times = 1;
			}else if (result == arr[i]) {
				times += 1;
			} else{
				times -= 1;
			}
		}
		// 这里应当检查一下该数是否真的过半
		if(!checkMoreThanHalf(arr, result)){
			System.out.print("没有过半的数:");
			return 0;
		}
		
		return result;
	}
	
	private static boolean checkMoreThanHalf(int[] arr, int result) {
		int count = 0;
		for(int i = 0; i < arr.length; i++){
			if(arr[i] == result) count++;
		}
		return count > (arr.length >> 1);
	}
}

面试题40:最小的k个数

在这里插入图片描述

public class Demo {
	public static void main(String[] args) {
		int[] arr = { 1, 1, 1, 2, 3, 4, 5, 6 };
		int k = 3;
		ArrayList<Integer> smallK = GetLeastNumbers_Solution(arr, k);
		for (Integer i : smallK) {
			System.out.println(i + " ");
		}
	}

	public static ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {
		ArrayList<Integer> result = new ArrayList<Integer>();
		int length = input.length;
		if (k > length || k == 0) {return result;}
		
		PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(k,
				new Comparator<Integer>() {
					@Override
					public int compare(Integer o1, Integer o2) {
						return o2.compareTo(o1);
					}
				});
		
		for (int i = 0; i < length; i++) {
			if (maxHeap.size() != k) {
				maxHeap.offer(input[i]);
			} else if (maxHeap.peek() > input[i]) {
				maxHeap.poll();
				maxHeap.offer(input[i]);
			}
		}
		
		for (Integer integer : maxHeap) {
			result.add(integer);
		}
		
		return result;
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值