1.在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
/**
* 思路:左下角的值是一行中最小,一列中最大的,所以从这个值开始比较,如果比这个值大,就将列加1,再比较,
* 如果比这个值小,就将行加1,往上查,总的时间复杂度为O(M+N)
*/
public boolean Find(int target, int [][] array) {
int rows = array.length;
if(rows ==0){
return false;
}
int cols = array[0].length;
if(cols == 0){
return false;
}
int row = rows-1;
int col = 0;
while(row>=0 && col<cols){
if(array[row][col]<target){
col++;
}else if(array[row][col]>target){
row--;
}else{
return true;
}
}
return false;
}
2.请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
public String replaceSpace(StringBuffer str) {
if(str!=null){
return str.toString().replaceAll(" ","%20");
}
return null;
}
3.输入一个链表,按链表从尾到头的顺序返回一个ArrayList。
/**
* 非递归方法,因为链表是从头到尾遍历的,但题目要求从尾到头的输出
* 我们可以利用ArrayList的add重载方法,没次添加数据都添加在第一个位置
*/
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
ArrayList<Integer> result = new ArrayList();
while(listNode!=null){
result.add(0,listNode.val);
listNode = listNode.next;
}
return result;
}
}
/**
* 递归方法
*
*/
public class Solution {
ArrayList<Integer> result = new ArrayList();
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
if(listNode!=null){
printListFromTailToHead(listNode.next);
result.add(listNode.val);
}
return result;
}
}
4.用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
/**
* 思路:栈:先进后出,队列:先进先出,其中一个栈专门用来接收存,另一个栈用来弹出数据,如果数据为空,* 将栈1的数据都压入栈2,这样需要
*/
public class Solution {
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
stack1.push(node);
}
public int pop() {
if(stack2.size()==0){
while(stack1.size()>0){
stack2.push(stack1.pop());
}
}
return stack2.pop();
}
}
5.大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。
/**
* 裴波那契数列特点F(1)=1;F(2)=1;F(n)= F(n-1)+F(n-2) (n>=3);
* 时间复杂度:O(2^n)
* 空间复杂度:O(1)
*/
public class Solution {
public int Fibonacci(int n) {
if(n<=1){
return n;
}
return Fibonacci(n-1) + Fibonacci(n-2);
}
}
/**
* 时间复杂度O(n)
* 空间复杂度O(n)
*/
public int Fibonacci(int n) {
int[] num = new int[40];
num[0] = 0;
num[1] = 1;
for(int i=2;i<=n;i++){
num[i]= num[i-1]+num[i-2];
}
return num[n];
}
/**
* 时间复杂度O(n)
* 空间复杂度O(1)
*/
public int Fibonacci(int n) {
if(n == 0){
return 0;
}else if(n == 1){
return 1;
}
int sum = 0;
int two = 0;
int one = 1;
for(int i=2;i<=n;i++){
sum = two + one;
two = one;
one = sum;
}
return sum;
}
6.一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)
首先可知,第一阶有只能一步,一种;,第二阶可以两次一步、一次两步两种
若楼梯阶级 n = 3
跳 2 步到 3:剩下的是第一步没跳,起始跳到第一步只有一种
跳 1 步到 3:剩下的是第二步没跳,起始跳到第二步有两种
通过分类讨论,问题规模就减少了:
若楼梯阶级 n = n
跳 2 步到 n:剩下的是第 n - 2 步没跳,起始跳到第 n - 2 步设它为 pre2 种
跳 1 步到 n:剩下的是第 n - 1 步没跳,起始跳到第 n - 1 步设它为 pre1 种
同时可以发现第 n 阶的解法,只要用到 n - 1 和 n - 2 阶是多少,其他的不用考虑,因此用两个变量临时存下来即可
public class Solution {
public int JumpFloor(int target) {
if(target <= 2){
return target;
}
int pre2 = 1, pre1 = 2;
for (int i = 3; i <= target; i++){
int cur = pre2 + pre1;
pre2 = pre1;
pre1 = cur;
}
return pre1;
}
}
7.一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
/**
* 假设跳到第n个台阶的方法一共有f(n)种,从最后一个台阶开始推,可以从第n-1个台阶跳1个台阶一下到上,
* 也可以从第n-2个台阶跳2个台阶一下跳上,依次类推。。。而跳到第n-1个台阶有f(n-1)中方法,跳到第n-2
* 个台阶有f(n-2)中方法,那么f(n)=f(n-1)+f(n-2)+...f(1); f(n-1) = f(n-2)+f(n-3)+...+f(1)
* 那么就有f(n) = 2*f(n-1);f(n)= 2^2 *f(n-2); f(n) = 2^(n-1) *f(1);而f(1) = 1;
* 最后得出f(n) = 2^(n-1);
*/
public class Solution {
public int JumpFloorII(int target) {
return (int)Math.pow(2,target-1);
}
}
8.我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
/**
* 思路:竖着放相当于一下跳1个台阶,横着放相当于一下跳2个台阶,所以也是斐波那契数列
*/
public class Solution {
public int RectCover(int target) {
if(target<=2){
return target;
}
int pre1 = 1;
int pre2 = 2;
for(int i=3;i<=target; i++){
int count = pre1+pre2;
pre1 = pre2;
pre2 = count;
}
return pre2;
}
}
9.输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
/**
* 如果一个整数不为0,那么这个整数至少有一位是1。如果我们把这个整数减1,那么原来处在整数最右边的1就**会变为0,原来在1后面的所有的0都会变成1(如果最右边的1后面还有0的话)。其余所有位将不会受到影响。
*举个例子:一个二进制数1100,从右边数起第三位是处于最右边的一个1。减去1后,第三位变成0,它后面的两*位0变成了1,而前面的1保持不变,因此得到的结果是1011.我们发现减1的结果是把最右边的一个1开始的所有位都取反了。这个时候如果我们再把原来的整数和减去1之后的结果做与运算,从原来整数最右边一个1那一位开始所有位都会变成0。如1100&1011=1000.也就是说,把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0.那么一个整数的二进制有多少个1,就可以进行多少次这样的操作。
*/
public class Solution {
public int NumberOf1(int n) {
int count = 0;
while(n!=0){
count ++;
n = n & (n-1);
}
return count;
}
}
10.给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。保证base和exponent不同时为0
public class Solution {
/**
* 方法一:直接调用库函数
*/
public double Power(double base, int exponent) {
return Math.pow(base, exponent);
}
/**
* 方法二:暴力法
*/
public double Power(double base, int exponent) {
if(exponent==0){
return 1;
}
if(exponent == 1){
return base;
}
boolean isNegative = exponent<0;
if(isNegative){
exponent = Math.abs(exponent);
}
double result = base;
for(int i=1; i<exponent; i++){
result = result * base;
}
return isNegative?1/result:result;
}
}
11.输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
//思路一:可以新开辟两个数组,一个存奇数,一个存偶数,然后将两个数组合并
//思路二:用两个变量i、j控制下标,遍历数组中每个元素,如果遇到偶数,则停下来,开始从其后面一位遍历,如果遇到奇数,则将当前j位置的元素需要插入到i位置,但不能直接插入,先用一个临时变量暂存,然后将i到j-1位置的数都后移一位,再将奇数插入到i的位置。
public class Solution {
public void reOrderArray(int [] array) {
for(int i = 0;i<array.length; i++){
if(array[i] %2==0){
for(int j=i+1;j<array.length; j++){
if(array[j]%2==0){
//如果遍历到最后一位了,还是偶数,说明数组结构已经满足要求,结束全过程
if(j==array.length-1){
return;
}
}else{
int temp = array[j];
while(i<j){
array[j] = array[j-1];
j--;
}
array[i] = temp;
break;
}
}
}
}
}
}
12.输入一个链表,输出该链表中倒数第k个结点。
//思路一:倒数第k个,就是正着数的第n-k+1个,n为链表总个数。所以先计算出链表总个数,然后从第一个元素开始遍历,小于n-k+1就将后一位赋值给当前节点,不满足循环条件时,即为找到了倒数第k个节点
public class Solution {
public ListNode FindKthToTail(ListNode head ,int k) {
int totalCount = 0;
if(head!=null){
totalCount++;
}else{
return null;
}
ListNode tempNode = head.next;
while(tempNode!=null){
totalCount++;
tempNode = tempNode.next;
}
if(k>totalCount){
return null;
}
ListNode result = head;
int i = 1;
while(i<totalCount-k+1){
result = result.next;
i++;
}
return result;
}
//思路二:很多链表问题活用指针都能解决问题,如果一个解决不了,那就用两个。本题的解决思路,倒数第k个元素,就是正着数第n-k+1个(从1开始),如果快指针先走k-1步,然后慢指针和快指针再一起走,这样两者相差k-1步,当快指针走到n时,那么慢指针走到n-(k-1),也即n-k+1,就是我们要找的倒数第k个元素。
public ListNode FindKthToTail(ListNode head , int k) {
if(k<1){
return null;
}
ListNode fast = head;
ListNode low = head;
for(int i=1;i<k ; i++){
if(fast == null){
return null;
}
fast = fast.next;
}
if(fast==null){
return null;
}
while(fast.next!=null){
fast = fast.next;
low = low.next;
}
return low;
}
}
13.输入一个链表,反转链表后,输出新链表的表头
public class Node{
public int value;
public Node next;
public Node(int data){
this.value = data;
}
}
/**
*1.遍历法
*目的:需要从第一个节点开始,遍历修改每个节点的next属性
*思路:遍历每一个节点,用临时变量next存储当前节点的下一个节点,以保证遍历能够持续,修*改当前节点的next属性,也即使其指向prev节点,
*用临时变量prev存储修改后的节点,
public static Node reverseList(Node node){
Node prev = null;
Node next = null;
while(node != null){
//先将当前节点的next赋值给临时变量,为了能保证循环继续,因为第二步要修改其next
next = node.next;
//将上一步遍历后的节点设置给当前节点的next,实际上完成了一次翻转。
node.next = prev;
//然后将修改后的当前节点在赋值给prev,作为处理结果供下次遍历使用。
prev = node;
//将next节点赋值给当前节点,判断循环是否继续
node = next;
}
return prev;
}
/**
* 2.递归法:递归法是从最后一个Node开始,弹栈的过程中将指针顺序置换的。
* 递归实质上就是系统帮你压栈的过程,系统在压栈的时候会保留现场。
*/
public static Node reverse(Node node){
if(node == null || node.next == null){
return node;
}
Node temp = node.next;
Node newNode = reverse(node.next);
temp.next = node;
node.next = null;
return newNode;
}
我看看一个递归过程1->2->3->4,
1)程序到达 Node newNode = reverse(node.next);时进入递归。
2)假设此时递归到达了3节点,此时node = 3,temp = 4;
3)执行reverse(node.next),传入node为4的节点,返回node为4的节点。
4)接下来就是弹栈过程,执行temp.next = node;相当于4->3.
5) 执行node.next = null,即把3节点指向4节点的指针断掉。
6)返回新链表的头结点newNode,继续恢复2节点的压栈现场。最后完成整个链表的翻转。
14.打印10000以内的猫扑素数
形如以 2 开头, 之后跟任意多个 3 的十进制整数而且是个素数, 则它是猫扑素数. 如 2, 23, 233, 2333, 23333 都是猫扑素数, 而 233333 则不是, 它可以分解为 353 x 661.
//判断是否是猫扑数
public static boolean isMopNum(int n){
if(n<10)
return n==2;
else
return n%10 == 3 && isMopNum(n/10);
}
public static boolean isPrimeNum(int n){
if(n<2) return false;
for(int i = 2; i<= Math.squrt(n); i++){
if(n%i ==0){
return false;
}
}
return true;
}
public static void main(String[] args){
for(int i = 0;i<10000;i++){
if(isMopNum(i) && isPrimeNum(n)){
System.Out.println(i);
}
}
}