算法与数据结构学习网站
https://www.cs.usfca.edu/~galles/visualization/Algorithms.html
跳台阶
题目描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
思路分析
这道题本质上其实是一个斐波那契数列问题:
- 当台阶为1时,只能跳一步,一种方式
1 |
1 |
- 当台阶为2时,可以选择一步一步跳,也可以选择跳两步直接到
2 | |
1 | 2 |
1 |
- 当台阶为3时,可以选择先跳两步再跳一步,也可以选择先跳一步,然后还剩两级台阶,一步一步跳或者一下跳两步
3 | ||
2 | 1 | |
1 | 1 | 2 |
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;
}
}