剑指offer刷题记录

算法与数据结构学习网站

https://visualgo.net/zh

https://www.cs.usfca.edu/~galles/visualization/Algorithms.html

跳台阶

题目描述

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

思路分析

这道题本质上其实是一个斐波那契数列问题:

  • 当台阶为1时,只能跳一步,一种方式
1
1
  • 当台阶为2时,可以选择一步一步跳,也可以选择跳两步直接到
2
12
1
  • 当台阶为3时,可以选择先跳两步再跳一步,也可以选择先跳一步,然后还剩两级台阶,一步一步跳或者一下跳两步
3
21
112
1

此时我们可以发现,每多一级台阶可以分解为前面两种台阶数量的情况:3步时先跳一步再跳两步,此时,跳两步的方式和之前统计的两步时情况一样,即3步=2步的方式总数+1步的方式总数,构成了一个n=(n-1)+(n-2)的斐波那契数列

	public class Solution {
    	public int JumpFloor(int target) {
        	if(target<=2){
            	return target;
        	}
        	return JumpFloor(target-1)+JumpFloor(target-2);
    	}
	}

变态跳台阶

题目描述

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

思路分析
思路和跳台阶类似,但是由于可以多跳,所以我们相加的项不止前两项了,我们要考虑跳3,4,5 。。。n时的情况,不难发现其实规律就是b(n)=1+b(n-1)+b(n-2)+b(n-3)+…+b(1),式子中的1表示直接跳到终点。

	public class Solution {
    	public int JumpFloorII(int target) {
        	if(target <= 2){
            	return target;
        	}
        	int jumpn=1;
        	for(int i=1;i<target;i++){
            	jumpn+=JumpFloorII(target-i);
        	}
        	return jumpn;
    	}
	}

矩形覆盖

题目描述

我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

思路分析

每一个2*1的小矩形在2*n的大矩形中都有两种摆法:

  • 横着放
++

此时大矩形被划分为一个2*1的小矩形和一个2*(n-1)矩形,求这种情况的摆法即求2*(n-1)的大矩形摆法数

  • 竖着放
+-
+-

此时如果竖着放则另外一边因为区域限制也一定只能竖着放,此时大矩形被划分为一个2*1的小矩形和一个2*(n-2)矩形,求这种情况的摆法即求2*(n-2)的大矩形摆法数

所以最终要求2*n矩形摆法即求2(n-1)摆法数+2(n-2)摆法数,斐波那契数列。

	public class Solution {
    	public int RectCover(int target) {
        	if(target<=2){
            	return target;
        	}
        	return RectCover(target-1)+RectCover(target-2);
    	}
	}

二进制中1的个数

题目描述

输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

	public class Solution {
    	public int NumberOf1(int n) {
        	int count = 0;
        	while(n != 0){
            	count += (n & 1); //每次判断最低位是否为1
            	n >>>= 1;//将n的二进制向右移1位
        	}
        	return count;
    	}
	}

调整数组顺序使奇数位于偶数前面

题目描述

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

思路分析

顺序问题且是稳定排序,首先想到了插入排序,将插入排序的比大小替换为看奇偶即可解决问题

	public class Solution {
    	public void reOrderArray(int [] array) {
	//      从第2个元素开始先判断当前元素是不是偶数,偶数则跳过,若是奇数则依次判断前面的数,偶数则向后移,前面的数为奇数则将i指向的奇数插入到当前位置
        	for(int i=1;i<array.length;i++){
	//            如果i指向的是偶数,跳过,判断下一个数
            	if(array[i]%2==0){
                	continue;
            	}
	//            若是奇数先保存当前指向的奇数
            	int x=array[i];
	//            依次判断前面一个数,偶数则向后移动
            	for(int j=i;j>0;j--){
                	if(array[j-1]%2==0){
                    	array[j]=array[j-1];
	//                    若j-1为0则表示i前面的数全是偶数,此时将i指向的数x放到数组头部即可
                    	if(j-1==0){
                        	array[j-1]=x;
                        	break;
                    	}
	//                    若前面的数为奇数则表示此时j的位置前面全是奇数了,将i指向的数放到此处即可
                	}else if(array[j-1]%2!=0){
                    	array[j]=x;
                    	break;
                	}
            	}
        	}
    	}
	}

链表中倒数第k个结点

题目描述

输入一个链表,输出该链表中倒数第k个结点。

思路分析

方法一(栈):

最先想到的是利用栈,遍历下链表并将访问到的数据压入栈中,倒数第k个元素即从栈顶开始第k个元素

	/*
	public class ListNode {
    	int val;
    	ListNode next = null;

    	ListNode(int val) {
        	this.val = val;
    	}
	}*/
	import java.util.Stack;
	public class Solution {
    	public ListNode FindKthToTail(ListNode head,int k) {
        	if(k==0){
            	return null;
        	}
        	Stack<ListNode> stack = new Stack<>();
        	int n=0;
        	while(head!=null){
            	stack.push(head);
            	head = head.next;
            	n++;
        	}
        	if(k>n){
            	return null;
        	}
        	for(int i=1;i<k;i++){
            	stack.pop();
        	}
        	return stack.pop();
    	}
	}

方法二(快慢指针):

方法二的思路是让两个指针都指向头结点,其中一个随着遍历向后移动,当遍历到第k个元素时,另外一个也开始从头结点依次向后移动,这样就保证了两个指针间的距离都是k,也就是说,当第一个指针遍历结束指向链表尾部时,第二个指针指向的位置就是倒数第k个元素的位置

	public ListNode FindKthToTail(ListNode head,int k) {
        	ListNode p, q;
        	p = q = head;
        	int i = 0;
        	for (; p != null; i++) {
            	if (i >= k)
                	q = q.next;
            	p = p.next;
        	}
        	return i < k ? null : q;
    	}

用两个栈实现队列

题目描述

用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。

	import java.util.Stack;

	public class Solution {
    	Stack<Integer> stack1 = new Stack<Integer>();
    	Stack<Integer> stack2 = new Stack<Integer>();
    	int n = 0;

    	public void push(int node) {
        	stack1.push(node);
    	}


    	public int pop(){
        	stack2.removeAllElements();
        	for(int i=stack1.size()-1;i>=n;i--)
            	stack2.push(stack1.get(i));
        	n++;
        	return stack2.pop();
    	}
	}

从头到尾打印链表

题目描述

输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。

	/**
	*    public class ListNode {
	*        int val;
	*        ListNode next = null;
	*
	*        ListNode(int val) {
	*            this.val = val;
	*        }
	*    }
	*
	*/
	import java.util.*;
	public class Solution {
	     public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
	        Stack<Integer> stack = new Stack<>();
	        ArrayList<Integer> arrayList = new ArrayList<>();
	        int n = 0;
	        for(;listNode!=null;listNode = listNode.next){
	            stack.push(listNode.val);
	            n++;
	        }
	        for(int i=0;i<n;i++){
	            arrayList.add(stack.pop());
	        }
	        return arrayList;
	    }
	}

替换空格

题目描述

请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

	public class Solution {
    public String replaceSpace(StringBuffer str) {
    	return str.toString().replace(" ","%20");
    }
}

二叉树的深度

题目描述

输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。

	/**
	public class TreeNode {
    	int val = 0;
    	TreeNode left = null;
    	TreeNode right = null;

    	public TreeNode(int val) {
        	this.val = val;

    	}

	}
	*/
	public class Solution {
    	public int TreeDepth(TreeNode root) {
        	if (root == null) {
            	return 0;
        	}
			//计算root左子树的深度
        	int leftHeight = TreeDepth(root.left);
			//计算root右子树的深度
        	int rightHeight = TreeDepth(root.right);
			//由于计算的是子树的深度并未包含根节点,所以要+1
        	return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
    	}
	}

重建二叉树

题目描述

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

	/**
 	* Definition for binary tree
 	* public class TreeNode {
 	*     int val;
 	*     TreeNode left;
 	*     TreeNode right;
 	*     TreeNode(int x) { val = x; }
 	* }
 	*/
	import java.util.Arrays;
	public class Solution {
    	public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        	if (pre.length == 0 || in.length == 0) {
            	return null;
        	}
        	TreeNode root = new TreeNode(pre[0]);
        	// 在中序中找到前序的根
        	for (int i = 0; i <= in.length; i++) {
            	if (in[i] == pre[0]) {
                	// 左子树,注意 copyOfRange 函数,左闭右开
                	root.left = reConstructBinaryTree(Arrays.copyOfRange(pre, 1, i + 1), Arrays.copyOfRange(in, 0, i));
                	// 右子树,注意 copyOfRange 函数,左闭右开
                	root.right = reConstructBinaryTree(Arrays.copyOfRange(pre, i + 1, pre.length), Arrays.copyOfRange(in, i + 1, in.length));
                	break;
            	}
        	}
        	return root;
    	}
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值