第三题
/**
* 题目:在一个二维数组中,每一行都按照从左到右的递增顺序排序,
* 每一列都按照从上到下的递增顺序排序。请完成一个函数,输入
* 这样一个二位数组和一个整数,判断数组中是否含有该整数,含有
* 就返回true,否则返回false。
*
* 解题思路:首先选取数组中右上角的数字。如果该数字等于要查找的数字,查找结束;
* 如果该数字大于要查找的数字,剔除这个数字所在的列;如果该数字小于要查找的数
* 字,剔除这个数字所在的行。再从右上角的数字开始,每一步都可以缩小查找范围,
* 直到找到数字,或者查找范围为空。(注意不能选取左上角或者右下角的数据开始)
*
*
*/
public class NO3 {
public static void main(String[] args) {
int[][] arr = { { 1, 2, 8, 9 },
{ 2, 4, 9, 12 },
{ 4, 7, 10, 13 },
{ 6, 8, 11, 15 } };
System.out.println(search(arr, 4));
}
public static boolean search(int[][] arr, int key) {
int i = 0;
int j = arr[0].length - 1;
while (i <= arr.length - 1 && j >= 0) {
if (arr[i][j] == key)
return true;
if (arr[i][j] > key)
j--;
else
i++;
}
return false;
}
}
第四题
package com.jiaohanhan.swordtooffer;
/**
* 问题描述:请实现一个函数,把字符串中的每个空格替换成"%20".
* 输入:"We are happy"
* 输出:"We%20are%20happy"
*
* 解题思路:先通过一次遍历字符串计算出含有的空格数,然后new一个数组
* 长度为原数组的字符串长度+空格数*2,因为一个"%20"等于3空
* 格字符的长度;然后就逆序把字符串拷贝到数组中,每遇到空格
* 就用"%20"替换
*
* 解题思路二:可以直接用StringBuilder,在O(n)时间就能完成
*
*/
public class NO4 {
public static void main(String[] args) {
String s = "we are happy";
char[] charArr = s.toCharArray();
System.out.println(change1(charArr));
System.out.println(change2(s));
}
public static String change1(char[] charArr) {
int count = 0;
for (int i = 0; i < charArr.length; i++)
if (charArr[i] == ' ')
count++;
if (count == 0)
return null;
char[] temp = new char[charArr.length + 2 * count];
int j = temp.length - 1;
int i = charArr.length - 1;
while (i >= 0) {
if (charArr[i] == ' ') {
temp[j] = '0';
temp[j - 1] = '2';
temp[j - 2] = '%';
j = j - 3;
} else {
temp[j] = charArr[i];
j--;
}
i--;
}
return new String(temp);
}
public static String change2(String s) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == ' ')
sb.append("%20");
else
sb.append(s.charAt(i));
}
return sb.toString();
}
}
第五题
package com.jiaohanhan.swordtooffer;
import java.util.Stack;
/**
* 题目描述:输入一个链表的头结点,从尾到头反过来打印出每个结点的值
*
* 解题思路1:递归反转,用头指针和next找到最后一个结点,逆序打印,缺点
* 链表很长的时候,容易造成溢出。优点:没有改变链表的结构。
*
* 解题思路2:直接改变链表的结构,变成逆向的链表,即改变next指针的取反
*
* 解题思路3:用栈保存每一步遍历链表得到的结点,再弹出栈
*
*
*/
public class NO5 {
public static void main(String[] args) {
Node node1 = new Node("A");
Node node2 = new Node("B");
Node node3 = new Node("C");
Node node4 = new Node("D");
Node node5 = new Node("E");
node1.setNext(node2);
node2.setNext(node3);
node3.setNext(node4);
node4.setNext(node5);
System.out.print("解题思路1:");
reverse1(node1);
System.out.println();
System.out.print("解题思路3:");
reverse3(node1);
System.out.println();
System.out.print("解题思路2:");
reverse2(node1);
}
public static void reverse1(Node head) {
if (head != null) {
if (head.getNext() != null)
reverse1(head.getNext());
}
System.out.print(head.getData() + " ");
}
public static void reverse2(Node head) {
Node pre = head;
Node cur = head.getNext();
Node temp;
while (cur != null) {
temp = cur.getNext();
cur.setNext(pre);
pre = cur;
cur = temp;
}
head.setNext(null);
while (pre != null) {
System.out.print(pre.getData() + " ");
pre = pre.getNext();
}
}
public static void reverse3(Node head) {
Stack<Node> stack = new Stack<>();
while (head != null) {
stack.push(head);
head = head.getNext();
}
while (!stack.isEmpty())
System.out.print(stack.pop().getData() + " ");
}
}
class Node {
private String data;
private Node next;
public Node(String data) {
super();
this.data = data;
}
public Node(String data, Node next) {
super();
this.data = data;
this.next = next;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
}
第六题
package com.jiaohanhan.swordtooffer;
/**
* 题目描述:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。
* 假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入
* 前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1
* ,5,3,8,6},则重建出如下图所示的二叉树并输出它的头结点。
*
* 1
* 2 3
* 4 5 6
* 7 8
*
*
* 解题思路:前序遍历的第一个数字就是根结点的值。在中序遍历中根结点左侧的
* 都是左子树的结点,在根结点右侧都是右子树。
*
*
*/
public class NO6 {
public static void main(String[] args) {
String preOrder = "12473568";
String midOrder = "47215386";
BiTree tree = new BiTree(preOrder, midOrder, preOrder.length());
tree.postRootTraverse(tree.root);
}
}
class BiTree{
TreeNode root;
public BiTree(String preOrder,String midOrder, int count){
if(count <= 0)
return;
int i = 0;
for(;i<count;i++){
if(midOrder.charAt(i) == preOrder.charAt(0))
break;
}
root = new TreeNode(preOrder.charAt(0));
root.setLChild(new BiTree(preOrder.substring(1, i+1),
midOrder.substring(0,i), i).root);
root.setRChild(new BiTree(preOrder.substring(i+1),
midOrder.substring(i+1), count-i-1).root);
}
public void postRootTraverse(TreeNode root){
if(root != null){
postRootTraverse(root.getLChild());
postRootTraverse(root.getRChild());
System.out.print(root.getData());
}
}
}
class TreeNode {
private char data;
private TreeNode LChild;
private TreeNode RChild;
public TreeNode(char data){
super();
this.data = data;
}
public TreeNode(char data, TreeNode LChild, TreeNode RChild) {
super();
this.data = data;
this.LChild = LChild;
this.RChild = RChild;
}
public char getData() {
return data;
}
public void setData(char data) {
this.data = data;
}
public TreeNode getLChild() {
return LChild;
}
public void setLChild(TreeNode LChild) {
this.LChild = LChild;
}
public TreeNode getRChild() {
return RChild;
}
public void setRChild(TreeNode RChild) {
this.RChild = RChild;
}
}
第七题(一)
package com.jiaohanhan.swordtooffer;
import java.util.Stack;
/**
* 题目描述:用两个栈实现一个队列。队列的声明如下,请实现它的两个函数
* appendTail和deleteHead,分别完成在队列尾部插入结点和在队列头
* 部删除结点的功能
*
* 解题思路:给出的结构中含有两个栈,s1,s2,对于进队操作,可以直接向s1中
* 压栈,那么对于出队来说就需要s2的参与,把s1中的元素全部弹出压入s2
* 栈中,原先栈底的元素即队列的头元素,就跑到栈顶了,便可以直接进行
* 弹出栈的操作,也就实现了出队的操作
*
* 说明:这里用Object实现泛型,当然也可以通过 public class NO7<T>{ }实现
* 泛型
*
* @author 焦含寒
*
*/
public class NO7 {
private Stack s1 = new Stack();
private Stack s2 = new Stack();
public static void main(String[] args) {
NO7 queue = new NO7();
queue.appendTail("a");
queue.appendTail("b");
queue.appendTail("c");
queue.deleteHead();
queue.deleteHead();
queue.deleteHead();
queue.deleteHead();
}
@SuppressWarnings("unchecked")
public void appendTail(Object x) {
s1.push(x);
}
@SuppressWarnings("unchecked")
public void deleteHead() {
if (s1.size() == 0 && s2.size() == 0)
try {
throw new Exception("队列为空");
} catch (Exception e) {
e.printStackTrace();
}
else {
if (s2.size() != 0) {
System.out.println(s2.pop().toString());
} else {
while (s1.size() > 0)
s2.push(s1.pop());
System.out.println(s2.pop().toString());
}
}
}
}
第七题(二)
package com.jiaohanhan.swordtooffer;
/**
* 题目描述:对一个有几万人的公司的员工的年龄排序
*
* 解题思路:因为年龄都在0~99,所以是一个小范围排序,可以记录下
* 每个年龄出现的次数,再处理
*
* @author 焦含寒
*
*/
public class NO7_5 {
public static void main(String[] args) {
int[] employeeAge = {22,22,34,23,44,56,28,30,38,22,34,20,34,36};
try {
sortAges(employeeAge);
} catch (Exception e) {
e.printStackTrace();
}
for(int i=0;i<employeeAge.length;i++){
System.out.print(employeeAge[i]+ " ");
}
}
public static void sortAges(int[] ages) throws Exception{
if(ages == null || ages.length == 0)
return;
int oldestAge = 99;
int[] timesOfAge = new int[100];
for(int i =0;i<timesOfAge.length;i++)
timesOfAge[i] = 0;
for(int i = 0;i<ages.length;++i){
int age = ages[i];
if(age < 0 || age > oldestAge)
throw new Exception("年龄超出范围");
++timesOfAge[age];
}
int index = 0;
for(int i=0;i<oldestAge;i++){
for(int j = 0;j<timesOfAge[i];j++){
ages[index] = i;
++index;
}
}
}
}
第八题
package com.jiaohanhan.swordtooffer;
/**
* 题目描述:把一个数组最开始的若干元素搬到数组的末尾,称之为数组的旋转。
* 输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如数
* 组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1
*
* 解题思路:假设我们持有两个指针,第一个指针left指向前面的递增数组的元素,
* 第二个指针right指向第二个递增数组的的元素,初始化的时候第一个指针
* 指向数组的第一个元素,第二个指针指向数组的最后一个元素,用二分法的
* 思想,比较左右指针和中间元素mid的大小,要是比左边元素大的话就让
* left=mid,否则就让right=mid;要注意{2,2,2,0,1,2}这种情况的处理
* 当left,mid,right指向的数据相等的时候就要处理这种情况了。
*
*
* @author 焦含寒
*
*/
public class NO8 {
public static void main(String[] args) throws Exception {
int[] arr = {3,4,5,1,2};
System.out.println(findMin(arr));
}
public static int findMin(int[] arr) throws Exception {
if (arr == null || arr.length == 0)
throw new Exception("数组为空");
int left = 0;
int right = arr.length - 1;
if (arr[right] > arr[left])
throw new Exception("非旋转数组");
while (left < right) {
int mid = (left + right) / 2;
if (arr[mid] == arr[left] && arr[left] == arr[right])
return searchMin(arr, left, right);
if (right - left == 1)
break;
if (arr[mid] >= arr[left])
left = mid;
else
right = mid;
}
return arr[right];
}
private static int searchMin(int[] arr, int left, int right) {
int result = arr[left];
for (int i = left + 1; i <= right; ++i)
if (arr[i] < result)
result = arr[i];
return result;
}
}
第九题(一)
package com.jiaohanhan.swordtooffer;
/**
* 题目描述:写一个函数,输入n,求斐波那契数列的第n项
*
* 解题思路:递归最直白,效率有限;可以动态规划,就是有点浪费空间,
* 也可以保存上一步的值再求解。
* 当然O(logn)的复杂度的算法也是有的
*
*
* @author 焦含寒
*
*/
public class NO9_1 {
public static void main(String[] args) {
System.out.println(fibnacci(10));
}
public static long fibnacci(int n) {
long[] a = { 0, 1 };
if (n < 2)
return a[n];
long fib1 = 0;
long fib2 = 1;
long fibN = 0;
for (int i = 2; i <= n; i++) {
fibN = fib1 + fib2;
fib1 = fib2;
fib2 = fibN;
}
return fibN;
}
}
第九题(二)
package com.jiaohanhan.swordtooffer;
/**
* 题目描述:一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。
* 求该青蛙跳上一个n级台阶总共有几种跳法。
*
* 解题思路:类似斐波那契数列,可以递归,也可以动态规划
*
* @author 焦含寒
*
*/
public class NO9_2 {
public static void main(String[] args) {
System.out.println(count1(10));
System.out.println(count2(10));
System.out.println(count3(10));
System.out.println(count4(10));
}
public static int count1(int n) {
if (n <= 0)
throw new IllegalArgumentException("非法参数");
if (n == 1)
return 1;
if (n == 2)
return 2;
return count1(n - 1) + count1(n - 2);
}
public static int count2(int n) {
if (n <= 0)
throw new IllegalArgumentException("非法参数");
int[] arr = new int[n + 1];
arr[1] = 1;
arr[2] = 2;
for (int i = 3; i < n + 1; i++) {
arr[i] = arr[i - 1] + arr[i - 2];
}
return arr[n];
}
public static int count3(int n) {
if (n <= 0)
throw new IllegalArgumentException("非法参数");
int[] arr = new int[3];
arr[1] = 1;
arr[2] = 2;
for (int i = 3; i < n + 1; i++) {
arr[0] = arr[1] + arr[2];
arr[1] = arr[2];
arr[2] = arr[0];
}
return arr[0];
}
public static int count4(int n) {
if (n <= 0)
throw new IllegalArgumentException("非法参数");
int[] arr = new int[3];
int j = 0;
for (int i = 1; i < n + 1; i++) {
j = i % 3;
if (i == 1) {
arr[j] = 1;
} else if (i == 2) {
arr[j] = 2;
} else {
arr[j] = 0;
for (int k = 0; k < 3; k++) {
if (k == j)
continue;
arr[j] += arr[k];
}
}
}
return arr[j];
}
}