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

面试题21:调整数组顺序使奇数位于偶数前面

在这里插入图片描述

public class Demo21 {
	public static void main(String[] args) {
		int[] arr = { 3, -3, 4, 7, 4, 2, 9, 0, -1 };
		adjustOrder(arr);
		for (int i = 0; i < arr.length; i++) {
			System.out.print(arr[i] + " ");
		}
	}

	public static void adjustOrder(int[] arr) {
		int pre = 0; // 偶数指针,从前向后找
		int after = arr.length - 1; // 奇数指针,从后向前找

		while (pre < after) { // pre >= after 证明已经有序
			// 前面的even指针移动到偶数
			if (isOdd(arr[pre])) {
				pre++;
			}
			// 后面的指针移动到奇数上
			if (!isOdd(arr[after])) {
				after--;
			}
			// 上面两个if同时失效,满足交换条件,做一次交换,指针分别向中间靠拢一步
			if (!isOdd(arr[pre]) && isOdd(arr[after])) {
				int temp = arr[pre];
				arr[pre] = arr[after];
				arr[after] = temp;
				pre++;
				after--;
			}
		}
	}

	private static boolean isOdd(int num) {
		return (num & 1) == 1;
	}

}

面试题22:链表中倒数第k个节点

在这里插入图片描述

public class Demo22 {
	public static void main(String[] args){
		// 测试链表
		ListNode head = new ListNode(1);
		ListNode node2 = new ListNode(2);
		ListNode node3 = new ListNode(3);
		ListNode node4 = new ListNode(4);
		ListNode node5 = new ListNode(5);
		ListNode node6 = new ListNode(6);
		ListNode node7 = new ListNode(7);
		ListNode node8 = new ListNode(8);
		head.next = node2;
		node2.next = node3;
		node3.next = node4;
		node4.next = node5;
		node5.next = node6;
		node6.next = node7;
		node7.next = node8;
		int[] ks = {8,7,6,5,4,3,2,1,0,9};
		
		// 打印原链表
		System.out.print("测试链表:");
		ListNode p = head;
		while (p != null) {
			System.out.print(p.val + " ");
			p = p.next;
		}
		
		// 测试
		System.out.println();
		for(int i = 0; i < ks.length; i++){
			ListNode node = lastNodeK(head, ks[i]);
			if(node != null)
				System.out.println("倒数第" + ks[i] + "个结点为:" + node.val);
		}
		
	}
	
	// 单指针法:该方法用一个指针走两遍
	public static ListNode lastNodeK(ListNode head, int k){
		// 判空
		if(head == null){ return head; }
		
		ListNode p = head;
		// 先遍历一遍确定链表长度
		int len = 0;
		while(p !=null){
			len++;
			p = p.next;
		}
		// 判断 k是否合理
		if(k > len || k <= 0){
			System.out.println( "k = " + k +", 而链表长度为" + len + ", 所以k的合理取值为 1~" + len);
			return null;
		}
		
		// 找到第k个节点
		p = head;
		int steps = len - k;
		while(steps -- > 0){
			p = p.next;
		}
		
		return p;
	}
	
	// 前后指针法,效率比前一种要高一点
	public static ListNode lastNodeK1(ListNode head, int k){
		if(head == null || k == 0) {
			return null;
		}
		
		ListNode pAhead = head;
		ListNode pBehind = null;
		// 前指针先走 k-1步
		for(int i = 0; i < k - 1; i++){
			if(pAhead.next != null){
				pAhead = pAhead.next;
			}else{
				System.out.println("k = " + k + ", 不在合理范围");
				return null;
			}
		}
		// 一起走,直到前指针走到尾巴
		pBehind = head;
		while(pAhead.next != null){
			pAhead = pAhead.next;
			pBehind = pBehind.next;
		}
		
		return pBehind;
	}
}

测试输出:

测试链表:1 2 3 4 5 6 7 8 
倒数第8个结点为:1
倒数第7个结点为:2
倒数第6个结点为:3
倒数第5个结点为:4
倒数第4个结点为:5
倒数第3个结点为:6
倒数第2个结点为:7
倒数第1个结点为:8
k = 0, 而链表长度为8, 所以k的合理取值为 1~8
k = 9, 而链表长度为8, 所以k的合理取值为 1~8

面试题23:链表中环的入口

在这里插入图片描述

public class Demo23 {
	public static void main(String[] args) {
		// 测试链表
		ListNode head = new ListNode(1);
		ListNode node2 = new ListNode(2);
		ListNode node3 = new ListNode(3);
		ListNode node4 = new ListNode(4);
		ListNode node5 = new ListNode(5);
		ListNode node6 = new ListNode(6);
		ListNode node7 = new ListNode(7);
		ListNode node8 = new ListNode(8);
		head.next = node2;
		node2.next = node3;
		node3.next = node4;
		node4.next = node5;
		node5.next = node6;
		node6.next = node7;
		node7.next = node8;
		node8.next = node3; // node3是入口
		
		ListNode in = meetingNode(head);
		if(in != null)
			System.out.println(in.val);
		else
			System.out.println("没有环");
	}

	
	/**
	 * 快慢指针法:
	 * 设起始点到环入口为:x,环长为c,入口到相遇点为a,相遇点到入口为b
	 * slow = x + mc + a
	 * fast = x + nc + a
	 * slow*2 = fast
	 * 推出:x = (n-2m)c - a = (n - 2m - 1)c + b
	 * 所以x长度为环的整数倍,加上相遇点到入口的距离,这时只需要一个
	 * 指针从起始点走,一个指针从相遇点走最后肯定会在入口相遇
	 */
	public static ListNode meetingNode(ListNode head) {
		if(head == null){ return null;}
		
		ListNode fast = head;
		ListNode slow = head;
		
		while(fast != null){
			slow = slow.next;
			fast = fast.next;
			if(fast != null){ // 当前不为空才能在走一步,不然可能报空指针异常
				fast = fast.next;
			}
			if(fast == slow){ break; }
		}
		
		if(fast == null) return null;
		
		slow = head;
		while(slow != fast){
			slow = slow.next;
			fast = fast.next;
		}
		
		return slow;
	}
	
}

面试题24:反转链表

在这里插入图片描述

public class Demo24 {
	public static void main(String[] args) {
		// 测试链表
		ListNode head = new ListNode(1);
		ListNode node2 = new ListNode(2);
		ListNode node3 = new ListNode(3);
		ListNode node4 = new ListNode(4);
		ListNode node5 = new ListNode(5);
		ListNode node6 = new ListNode(6);
		ListNode node7 = new ListNode(7);
		ListNode node8 = new ListNode(8);
		head.next = node2;
		node2.next = node3;
		node3.next = node4;
		node4.next = node5;
		node5.next = node6;
		node6.next = node7;
		node7.next = node8;
		
		System.out.print("反转之前:");
		ListNode p = head;
		while (p != null) {
			System.out.print(p.val + " ");
			p = p.next;
		}
		
		System.out.print("\n反转之后:");
		p = reverseList(head);
		while (p != null) {
			System.out.print(p.val + " ");
			p = p.next;
		}
	}

	private static ListNode reverseList(ListNode head) {
		// 空和一个结点不需反转,直接返回
		if(head == null || head.next == null) return head;
		
		ListNode dummyHead = new ListNode(-1);
		dummyHead.next = head;
		ListNode p = head;
		ListNode temp = null;
		
		// 反转
		while(p.next != null){
			temp = p.next;
			p.next = temp.next;
			temp.next = dummyHead.next;
			dummyHead.next = temp;
			temp = p;
		}
		
		return dummyHead.next;
	}
	
}

测试输出:

反转之前:1 2 3 4 5 6 7 8 
反转之后:8 7 6 5 4 3 2 1 

面试题25:合并两个排序的链表

在这里插入图片描述

public class Demo25 {
	public static void main(String[] args) {
		ListNode L1 = getList1(); // 1 3 5 7
		ListNode L2 = getList2(); // 2 4 6 8
		// 测试
		ListNode p = mergeList(L1,L2);
		while (p != null) {
			System.out.print(p.val + " ");
			p = p.next;
		}
	}
	
	private static ListNode mergeList(ListNode l1, ListNode l2) {
		// 如果一个为空就直接把另一个返回,剩下就是两个都不为空
		if(l1 == null) return l2;
		if(l2 == null) return l1;
		
		ListNode dummy = new ListNode(-1);
		ListNode p = dummy;
		
		// 合并两个链表,直到有一个遍历完
		while(l1 != null && l2 != null){
			if(l1.val < l2.val){
				p.next = l1;
				l1 = l1.next;
			}else{
				p.next = l2;
				l2 = l2.next;
			}
			p = p.next;
		}
		
		// 如果l1遍历完了,就把l2剩下的接在后面
		// 如果l1没有遍历完则,把l1剩下的接在后面
		p.next = (l1 == null)? l2:l1;
		
		return dummy.next;
	}



	private static ListNode getList1() {
		// 测试链表
		ListNode head = new ListNode(1);
		ListNode node2 = new ListNode(2);
		ListNode node3 = new ListNode(5);
		ListNode node4 = new ListNode(7);
		head.next = node2;
		node2.next = node3;
		node3.next = node4;
		return head;
	}
	
	private static ListNode getList2() {
		// 测试链表
		ListNode head = new ListNode(2);
		ListNode node2 = new ListNode(4);
		ListNode node3 = new ListNode(6);
		ListNode node4 = new ListNode(8);
		head.next = node2;
		node2.next = node3;
		node3.next = node4;
		return head;
	}
}

测试输出:

1 2 3 4 5 6 7 8 

面试题26:树的子结构

在这里插入图片描述

/*
class BiTreeNode {
	int val;
	BiTreeNode left;
	BiTreeNode right;
	
	BiTreeNode(int x){
		val = x;
	}
}
*/
public class Demo26 {
	public static void main(String[] args){
		BiTreeNode mainTree = getMainTree();
		BiTreeNode subTree = getSubTree();
		boolean has = hasSubTree(mainTree, subTree);
		System.out.println(has);
	}
	
	// 遍历主树
	private static boolean hasSubTree(BiTreeNode root1, BiTreeNode root2) {

		boolean result = false;

		if (root1 != null && root2 != null) {

			if (root1.val == root2.val) // 如果根节点相等
				result = isSubTree(root1, root2);

			if (!result)
				result = hasSubTree(root1.left, root2);

			if (!result)
				result = hasSubTree(root1.right, root2);
		}

		return result;
	}

	private static boolean isSubTree(BiTreeNode root1, BiTreeNode root2) {
		if(root2 == null) return true;
		if(root1 == null) return false;
		
		if(root1.val != root2.val) return false;
		
		return isSubTree(root1.left, root2.left) && isSubTree(root1.right, root2.right);
	}

	public static BiTreeNode getMainTree() {
		BiTreeNode A = new BiTreeNode(8);
		BiTreeNode A2 = new BiTreeNode(8);
		BiTreeNode A3 = new BiTreeNode(7);
		BiTreeNode A4 = new BiTreeNode(9);
		BiTreeNode A5 = new BiTreeNode(2);
		BiTreeNode A6 = new BiTreeNode(4);
		BiTreeNode A7 = new BiTreeNode(7);
		A.left = A2;
		A.right = A3;
		A2.left = A4;
		A2.right = A5;
		A5.left = A6;
		A5.right = A7;
		return A;
	}
	
	public static BiTreeNode getSubTree() {
		BiTreeNode A2 = new BiTreeNode(8);
		BiTreeNode A4 = new BiTreeNode(9);
		BiTreeNode A5 = new BiTreeNode(2);
		A2.left = A4;
		A2.right = A5;
		return A2;
	}
}

面试题27:二叉树的镜像

在这里插入图片描述

public class Demo27 {
	public static void main(String[] args){
		/* 要镜像翻转的二叉树
		 *      8
		 *   6    10
		 *  5 7  9  11 
		 */
		BiTreeNode root = createBiTree();
		// 层次遍历
		System.out.println("原二叉树:");
		traverse(root);
		
		System.out.println("\n镜像对称后:");
		mirror(root);
		traverse(root);
	}

	private static void mirror(BiTreeNode root) {
		if(root == null) return;
		
		if(root.left == null && root.right == null) return;
		
		BiTreeNode temp = root.left;
		root.left = root.right;
		root.right = temp;
		
		if(root.left != null)
			mirror(root.left);
		if(root.right != null)
			mirror(root.right);
	}
	
	// 层次遍历
	private static void traverse(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);
			}
		}
		
	}
	
	// 构造一颗树
	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;
	}
}

测试输出:

原二叉树:
8 6 10 5 7 9 11 
镜像对称后:
8 10 6 11 9 7 5 

面试题28:对称的二叉树

在这里插入图片描述

public class Demo28 {
	public static void main(String[] args){
		/* 准备一颗对称二叉树
		 *      8
		 *   6     6
		 *  5 7   7 5 
		 */
		BiTreeNode root = createBiTree();
		
		boolean bool = isSymetrical(root);
		System.out.println(bool);
	}
	
	public static boolean isSymetrical(BiTreeNode root) {
		return isSymetrical(root, root);
	}

	private static boolean isSymetrical(BiTreeNode left, BiTreeNode right) {
		// 都为空
		if(left == null && right == null) return true;
		// 有一个为空
		if(left == null || right == null) return false;
		// 都不为空
		if(left.val != right.val) return false;
		return isSymetrical(left.left, right.right) && isSymetrical(left.right, right.left);
	}

	// 层次遍历
	private static void traverse(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);
			}
		}
		
	}
	
	// 构造一颗树
	private static BiTreeNode createBiTree() {
		BiTreeNode root = new BiTreeNode(8);
		BiTreeNode node2 = new BiTreeNode(6);
		BiTreeNode node3 = new BiTreeNode(6);
		BiTreeNode node4 = new BiTreeNode(5);
		BiTreeNode node5 = new BiTreeNode(7);
		BiTreeNode node6 = new BiTreeNode(7);
		BiTreeNode node7 = new BiTreeNode(5);
		
		root.left = node2;
		root.right = node3;
		node2.left = node4;
		node2.right = node5;
		node3.left = node6;
		node3.right = node7;
		
		return root;
	}
}

测试输出:true

面试题29:顺时针打印矩阵

在这里插入图片描述

public class Demo29 {
	public static void main(String[] args){
		// int[][] mat1 = {{}};
		int[][] matrix = { 
				{ 1, 2, 3, 4 }, 
				{ 5, 6, 7, 8 }, 
				{ 9, 10, 11, 12 },
				{ 13, 14, 15, 16 } };
		
		ArrayList<Integer> list = printMatrix(matrix);
		for(Integer i : list){
			System.out.print(i + " ");
		}
	}
	
	// 方法1
	public static ArrayList<Integer> printMatrix(int[][] matrix) {
		ArrayList<Integer> list = new ArrayList<Integer>();
		
		if(matrix == null) return list; // 这里只检查了输入为空的情况,输入不是矩阵的情况没检查
		int row = matrix.length;
		int col = matrix[0].length;
		if (row == 0 || col == 0) // 如果是空的就返回
			return list;

		int left = 0, right = col - 1, up = 0, down = row - 1;
		
		while (left <= right && up <= down) {
			for (int i = left; i <= right; i++) {
				list.add(matrix[up][i]);
			}

			for (int i = up + 1; i <= down; i++) {
				list.add(matrix[i][right]);
			}

			if (up != down)
				for (int i = right - 1; i >= left; i--) {
					list.add(matrix[down][i]);
				}

			if (left != right)
				for (int i = down - 1; i > up; i--) {
					list.add(matrix[i][left]);
				}
			left++;
			up++;
			right--;
			down--;
		}

		return list;
	}
	
	// 方法2
	public static ArrayList<Integer> printMatrix2(int[][] matrix) {
		ArrayList<Integer> list = new ArrayList<Integer>();
		
		if(matrix == null) return list; // 这里只检查了输入为空的情况,输入不是矩阵的情况没检查
		int rows = matrix.length;
		int cols = matrix[0].length;
		if (rows == 0 || cols == 0) // 如果是空的就返回
			return list;

		int left = 0, right = cols - 1, up = 0, down = rows - 1;
		int totalSteps = rows*cols; // 一共要走的步数
		
		int x, y;
		for(x = 0, y = 0; totalSteps > 0; totalSteps--){
			list.add(matrix[x][y]);
			// 向右走
			if(x == up){
				if(y < right) y++;
				else if(y == right) x++;
				continue;
			}
			// 向下走
			if(y == right){
				if(x < down) x++;
				else if(x == down) y--;
				continue;
			}
			// 向左走
			if(x == down){
				if(y > left) y--;
				else if(y == left) x--;
				continue;
			}
			// 向上走
			if(y == left){
				if(x > up + 1) x--;
				else if(x == up + 1){y++; left++; up++; right--; down--;}
				continue;
			}
		}
		return list;
	}
}

测试输出:

1 2 3 4 8 12 16 15 14 13 9 5 6 7 11 10 

面试题30:包含min函数的栈

在这里插入图片描述

public class MinInStack {
	private Stack<Integer> dataStack;
	private Stack<Integer> minStack;
	
	public MinInStack(){
		this.dataStack = new Stack<Integer>();
		this.minStack = new Stack<Integer>();
	}
	
	public boolean isEmpty(){
		return dataStack.isEmpty();
	}
	
	public Integer peek(){
		return dataStack.peek();
	}
	
	public void push(Integer item){
		dataStack.push(item);
		if(minStack.isEmpty() || item < minStack.peek()){
			minStack.push(item);
		}else{
			minStack.push(minStack.peek());
		}
	}
	
	public Integer pop(){
		Integer i = null;
		if(!dataStack.isEmpty() && !minStack.isEmpty()){
			 i = dataStack.pop();
			 minStack.pop();
		}
		return i;
	}
	
	public Integer min(){
		if(!dataStack.isEmpty() && !minStack.isEmpty()){
			return minStack.peek();
		}
		return null;
	}

	// 测试
	public static void main(String[] args){
		MinInStack minStack = new MinInStack();
		
		minStack.push(3);
		minStack.push(4);
		minStack.push(2);
		minStack.push(1);
		System.out.print("min = " + minStack.min() + " peek = " + minStack.pop() + "\n");
		System.out.print("min = " + minStack.min() + " peek = " + minStack.pop() + "\n");
		System.out.print("min = " + minStack.min() + " peek = " + minStack.pop() + "\n");
		System.out.print("min = " + minStack.min() + " peek = " + minStack.pop() + "\n");
		System.out.print("min = " + minStack.min() + " peek = " + minStack.pop() + "\n");
		System.out.print("min = " + minStack.min() + " peek = " + minStack.pop() + "\n");
	}
	
}

测试输出:

min = 1 peek = 1
min = 2 peek = 2
min = 3 peek = 4
min = 3 peek = 3
min = null peek = null
min = null peek = null
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值