剑指offer题目java实现

Problem2:实现Singleton模式

题目描述:设计一个类,我们只能生成该类的一个实例

 package Problem2;

 public class SingletonClass {

     /*
* 题目描述:设计一个类,我们只能生成该类的一个实例
*/
//volatile:防止指令重排序
private static volatile SingletonClass instance; private SingletonClass() {
} public static SingletonClass getInstace() {
if (instance == null) {
synchronized (SingletonClass.class) {
if (instance == null) {
instance = new SingletonClass();
}
}
} return instance; } }

Problem3:二维数组中的查找

题目描述:在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下的顺序排序。 完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否包含该整数;

 package Problem3;

 public class Find {

     /*
* 题目描述:二维数组中的查找
* 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下的顺序排序。
* 完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否包含该整数
*
*/
public static boolean find(int arr[][],int keyNumber){
//从二维数组的右上角开始选取与keyNumber比较的整数
//column的变化:arr[0].length-1-->0;
//row的变化:0-->arr.length;
int column=arr[0].length-1;
int row=0;
while(column>=0&&row<arr.length){
if(arr[row][column]==keyNumber){
return true;
}
else if(arr[row][column]>keyNumber){
column--;
}
else {
row++;
}
}
return false; }
//测试find函数
public static void main(String[] args) {
/*
* 1 2 8 9
* 2 4 9 12
* 4 7 10 13
* 6 8 11 15
*/
int array[][]=new int[4][4];
array[0][0]=1;
array[0][1]=2;
array[0][2]=8;
array[0][3]=9;
array[1][0]=2;
array[1][1]=4;
array[1][2]=9;
array[1][3]=12;
array[2][0]=4;
array[2][1]=7;
array[2][2]=10;
array[2][3]=13;
array[3][0]=6;
array[3][1]=8;
array[3][2]=11;
array[3][3]=15;
System.out.println(find(array, 7));
System.out.println(find(array, 5));
} }

同理,比较关键字也可以从二维数组的左下角开始选择,则column和row的增减方式调换一下,但是不能选择左上角和右下角的元素作为与查找元素比较的关键字,因为无论比较结果怎样,都无法进一步缩小查找的范围。

Problem4:替换空格

题目描述:请实现一个函数,将字符串的每个空格替换为"%20"。例如输入"We are happy",则输出"We%20are%20happy."。

 package Problem4;

 public class ReplaceBank {

     /*
* 题目描述: 请实现一个函数,将字符串的每个空格替换为"%20"。
* 例如输入"We are happy",则输出"We%20are%20happy."。
*/
/**
* @param args
*/ public String replace(String input) {
StringBuilder builder = new StringBuilder();
if (input == null || input.length() == 0) {
return null;
}
for (int i = 0; i < input.length(); i++) {
if (input.charAt(i) == ' ') {
builder.append("%");
builder.append("2");
builder.append("0");
} else {
builder.append(input.charAt(i));
}
}
return builder.toString();
} // 测试用例
public static void main(String[] args) {
ReplaceBank test = new ReplaceBank();
// 输入的字符串包含空格:最后面,最前面,中间,连续空格
String str1 = "We are happy.";
String str2 = " Wearehappy.";
String str3 = "Wearehappy. ";
String str4 = "We are happy .";
//输入的字符串没有空格
String str5="Wearehappy.";
//特殊输入测试:字符串只有连续空格、只有一个空格、字符串是一个null指针、字符串是一个空字符串;
String str6=" ";
String str7=" ";
String str8=null;
String str9="";
System.out.println(test.replace(str1));
System.out.println(test.replace(str2));
System.out.println(test.replace(str3));
System.out.println(test.replace(str4));
System.out.println(test.replace(str5));
System.out.println(test.replace(str6));
System.out.println(test.replace(str7));
System.out.println(test.replace(str8));
System.out.println(test.replace(str9));
} }

Problem5:从尾到头打印链表

题目描述:输入一个链表的头结点,从尾到头反过来打印出每个结点的值.

 package Problem5;

 import java.util.Stack;

 //首先定义链表结构
class LinkNode{
LinkNode next;
int node_value;
} public class PrintListReverse {
public void reverse(LinkNode headNode){
//用栈的思想来实现链表的倒序输出
Stack<LinkNode> stack=new Stack<LinkNode>();
while(headNode!=null){
stack.push(headNode);
headNode=headNode.next;
}
while(!stack.isEmpty()){
System.out.print(stack.pop().node_value+" ");
}
System.out.println();
} /**
* @param args
*/
public static void main(String[] args) {
//输入的链表有多个结点
PrintListReverse plr=new PrintListReverse();
LinkNode node1=new LinkNode();
LinkNode node2=new LinkNode();
LinkNode node3=new LinkNode();
node1.node_value=1;
node2.node_value=2;
node3.node_value=3;
node1.next=node2;
node2.next=node3;
plr.reverse(node1);
} }

Problem5:重建二叉树

题目描述:输入某二叉树的前序遍历和中序遍历结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不包含重复的数字。例如输入前序遍历序列:{1,2,4,7,3,5,6,8}和中序遍历{4,7,2,1,5,3,8,6},则重建出图中所示二叉树并且输出它的头结点。

重建的二叉树:

剑指offer题目java实现

 package Problem6;

 /* 重建二叉树
* 问题描述:输入某二叉树的前序遍历和中序遍历结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中
* 都不包含重复的数字。例如输入前序遍历序列:{1,2,4,7,3,5,6,8}和中序遍历{4,7,2,1,5,3,8,6},
* 则重建出图中所示二叉树并且输出它的头结点。
*/
//定义二叉树节点
class BinaryTreeNode {
public int value;
public BinaryTreeNode leftNode;
public BinaryTreeNode rightNode; // 无参构造函数
public BinaryTreeNode() { } // 有参构造函数
public BinaryTreeNode(int value) {
this.value = value;
this.leftNode = null;
this.rightNode = null;
}
} public class ConstructBinaryTree { public static BinaryTreeNode construct(int preOrder[], int inOrder[],
int length) throws Exception {
if (preOrder == null || inOrder == null || length < 0) {
return null;
}
return constructCore(preOrder, 0, preOrder.length - 1, inOrder, 0,
inOrder.length - 1);
} public static BinaryTreeNode constructCore(int preOrder[],
int startPreIndex, int endPreIndex, int inOrder[],
int startInIndex, int endInIndex) throws InvalidPutException {
// 头结点的值
int rootValue = preOrder[startInIndex]; // 构建一个只有一个根节点的二叉树
BinaryTreeNode root = new BinaryTreeNode(rootValue);
// 只有一个元素的情况下:
if (startPreIndex == endPreIndex) {
if (startInIndex == endInIndex
&& preOrder[startInIndex] == inOrder[endInIndex]) {
System.out.println("只有一个元素");
return root;
} else {
throw new InvalidPutException();
} }
// 最重要的一步:在中序遍历中找到根结点的索引
int rootInIndex = startInIndex;
while (rootInIndex <= endInIndex && inOrder[rootInIndex] != rootValue) {
rootInIndex++;
}
if (rootInIndex == endInIndex && inOrder[rootInIndex] != rootInIndex) {
throw new InvalidPutException();
}
// 根节点的左子树的长度
int leftLength = rootInIndex - startInIndex;
// 根节点的左子树的最右端的索引值
int leftPreEndIndex = startPreIndex + leftLength;
// 构建左子树
if (leftLength > 0) {
root.leftNode = constructCore(preOrder, startPreIndex + 1,
leftPreEndIndex, inOrder, startInIndex, rootInIndex - 1);
}
// 说明根节点存在右子树
if (leftLength < endPreIndex - startPreIndex) {
root.rightNode = constructCore(preOrder, leftPreEndIndex + 1,
endPreIndex, inOrder, rootInIndex + 1, endInIndex);
}
return root;
} // 按照前序遍历打印二叉树的节点
public static void printPreBinaryTree(BinaryTreeNode root) {
if (root == null) {
return;
} else {
System.out.println(root.value + " ");
}
if (root.leftNode != null) {
printPreBinaryTree(root.leftNode);
}
if (root.rightNode != null) {
printPreBinaryTree(root.rightNode);
}
} public static class InvalidPutException extends Exception { private static final long serialVersionUID = 1L;
} /**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
int preOrder[] = { 1, 2, 4, 7, 3, 5, 6, 8 };
int inOrder[] = { 4, 7, 2, 1, 5, 3, 8, 6 };
ConstructBinaryTree test = new ConstructBinaryTree();
printPreBinaryTree(test.construct(preOrder, inOrder, preOrder.length));
} }

Problem7:用两个栈实现队列

题目描述:用两个栈实现一个队列。队列的声明如下,请实现它的两个函数appendTail和deleteHead,分别完成在队列尾部插入结点和在队列头部删除结点的功能

 package Problem7;

 import java.util.Stack;

 public class ConStructQueue {
/*
* 问题描述:用两个栈实现一个队列。队列的声明如下,请实现它的两个函数appendTail和deleteHead,
* 分别完成在队列尾部插入结点和在队列头部删除结点的功能
*/ /**
* @param args
*/
Stack<String> stack1 = new Stack<String>();
Stack<String> stack2 = new Stack<String>(); // 实现appendTail函数
public void appendTail(String s) {
stack1.push(s);
} // 实现deleteHead函数
public String deleteHead() throws Exception {
if (stack2.isEmpty()) {
while (!stack1.isEmpty()) {
stack2.push(stack1.pop());
}
}
if (stack2.isEmpty()) {
throw new Exception("队列为空,不能进行删除操作");
}
return stack2.pop();
} public static void main(String[] args) throws Exception {
ConStructQueue test = new ConStructQueue();
// 向空的队列中添加元素、删除元素
test.appendTail("1");
System.out.println(test.deleteHead());
// 向非空的队列添加删除元素
test.appendTail("2");
test.appendTail("3");
System.out.println(test.deleteHead()); } }

Problem8:旋转数组的最小数字

题目描述:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1;

 package Problem8;

 public class MinInReversingList {
/*
* 题目描述:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
* 输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1;
*/ /**
* @param args
* @throws Exception
*/
public static int minElement(int array[]) throws Exception {
// 条件判断
if (array == null || array.length <= 0) {
throw new Exception("Invalid parameters");
}
int left = 0;
int right = array.length - 1;
int mid = left;
while (array[left] >= array[right]) {
// 跳出循环的条件
if (right - left == 1) {
mid = right;
break;
}
mid = (left + right) / 2;
if (array[left] == array[mid] && array[mid] == array[right]) {
return minFromSortSearch(array);
// 算法的核心思想
} else {
if (array[mid] >= array[left]) {
left = mid;
}
if (array[mid] <= array[right]) {
right = mid;
}
}
}
return array[right];
} public static int minFromSortSearch(int[] array) {
int minEle = array[0];
for (int i = 1; i < array.length; i++) {
if (array[i] < minEle) {
minEle = array[i];
}
}
return minEle;
} // 测试
public static void main(String[] args) throws Exception {
// 功能测试:输入的数组是升序排序数组的一个旋转,数组中有重复数字或者没有重复数字
int array1[] = { 3, 4, 5, 1, 2 };
System.out.println(minElement(array1));
int array2[] = { 3, 4, 5, 3, 3 };
System.out.println(minElement(array2));
// 边界测试:输入的数组是一个升序排序数组、只包含一个数字的数组
int array3[] = { 3 };
System.out.println(minElement(array3));
// 特殊输入测试:输入null指针
int array4[] = null;
System.out.println(minElement(array4));
} }

Problem9:斐波那契数列

题目描述:写一个函数,输入n,求斐波那契数列的第n项,斐波那契数列的定义如下: n=0,f(n)=0 ;n=1,f(n)=1 n>1;f(n)=f(n-1)+f(n-2).

 package Problem9;

 public class Fibonacci {
/*
* 题目描述: 写一个函数,输入n,求斐波那契数列的第n项,斐波那契数列的定义如下: n=0,f(n)=0 n=1,f(n)=1
* n>1;f(n)=f(n-1)+f(n-2)
*/ /**
* @param args
*/
// 解法1:用递归解决,但是存在很严重的效率问题,做了很多次的重复计算
public static int Fib1(int n) {
if (n == 0) {
return 0;
} else if (n == 1) {
return 1;
} else {
return Fib1(n - 1) + Fib1(n - 2);
} } // 解法2:时间复杂度为O(n),从下向上计算,保存已经计算过的数值,避免重复计算
public static long Fib2(int n) {
long FibOne = 0;
long FibTwo = 1;
long FibN = 0;
int result[] = { 1, 2 };
if (n < 2) {
return result[n];
} else {
for (int i = 2; i <= n; i++) {
FibN = FibTwo + FibOne;
FibOne = FibTwo;
FibTwo = FibN;
}
}
return FibN;
} public static void main(String[] args) {
// 用解法1求序列的第100项,直接不动了
// System.out.println(Fib1(100));
System.out.println(Fib2(100));
}
}

相关题目:一只青蛙一次可以跳上一级台阶,也可以跳上2级。求该青蛙跳上一个n级台阶共有多少种跳法;

Problem10:二进制中1的个数

问题描述: 请实现一个函数,输入一个整数,输出该数二进制表示中1的个数。例如把9表示成二进制是1001,有2位是1 因此如果输入9,该函数输出2;

 package Problem10;

 public class NumberOf1 {
/*
* 问题描述: 请实现一个函数,输入一个整数,输出该数二进制表示中1的个数。例如把9表示成二进制是1001,有2位是1 因此如果输入9,该函数输出2
*/ /**
* @param args
*/
// 可能引起死循环的解法1
public static int numberof1_1(int n) {
int count = 0;
while (n != 0) {
if ((n & 1) != 0) {
count++;
}
n = n >> 1;
}
return count;
} // 常规解法2
public static int numberof1_2(int n) {
int count = 0;
int flag = 1;
while (flag != 0) {
if ((n & flag) != 0) {
count++;
}
flag = flag << 1;
}
return count;
} // 解法3:把一个吧整数减去1,再和原整数做位与运算,会把该整数最右边一个1变成0
// 一个整数的二进制表示中有多少个1,就可以进行多少次这样的操作
public static int numberof1_3(int n) {
int count = 0;
while (n != 0) {
count++;
n = n & (n - 1);
}
return count;
} public static void main(String[] args) {
NumberOf1 test = new NumberOf1();
// 测试一下解法1
System.out.println("解法1:" + test.numberof1_1(9));
// 测试一下解法2
System.out.println("解法2:" + test.numberof1_2(100254));
// 测试一下解法3:正数(边界值)、负数、0
System.out.println("解法3:" + test.numberof1_3(0x7FFFFFFF));
System.out.println("解法3:" + test.numberof1_3(1));
System.out.println("解法3:" + test.numberof1_3(0x80000000));
System.out.println("解法3:" + test.numberof1_3(0xFFFFFFFF));
System.out.println("解法3:" + test.numberof1_3(0));
} }

Problem11:数值的整数次方

问题描述:实现函数double power(double base,int exponent),求base的exponent次方。不能使用库函数,同时不需要考虑大数问题。

 package Problem11;

 public class Power {
/*
* 问题描述: 实现函数double power(double base,int exponent),求base的exponent
* 次方。不能使用库函数,同时不需要考虑大数问题。
*/ /**
* @param args
*/
public double power(double base, int exponet) throws Exception {
double result = 0.0;
if (equals(base, 0) && (exponet < 0)) {
throw new Exception("0的负数次幂无意义");
}
if (exponet == 0) {
return 1.0;
}
if (exponet < 0) {
result = powerWithUnsignedExponent(base, -exponet);
} else {
result = powerWithUnsignedExponent(base, exponet);
}
return result;
} private double powerWithUnsignedExponent(double base, int exponet) {
double result = 1.0;
for (int i = 1; i <= exponet; i++) {
result = result * base;
}
return result;
} // 由于计算机中表示小数都有误差,不能用等号判断两个小数是否相等。如果两个小数的差的绝对值很小
// 我们就可以认为它们是相等的
private boolean equals(double number1, int number2) {
if ((number1 - number2 > -0.00000001)
&& (number1 - number2) < 0.00000001) {
return true;
}
return false;
} // 写测试:把底数和指数分别设置成正数、负数、0;
public static void main(String[] args) throws Exception {
Power test = new Power();
System.out.println(test.power(2, 3));
System.out.println(test.power(2, 0));
System.out.println(test.power(2, -2));
System.out.println(test.power(0, 3));
System.out.println(test.power(0, 0));
System.out.println(test.power(0, -2)); } }

Problem13:O(1)时间删除链表结点

问题描述:给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该结点。

 package Problem13;

 /*
* 问题描述:给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该结点。
*
*/
//定义结点的结构
class ListNode {
ListNode nextNode;
int nodeValue;
} public class DeleteNode {
public static void deleteNode(ListNode head, ListNode deListNode) {
// 空链表
if (head == null || deListNode == null) {
return;
}
// 要删除的链表中只有一个结点
if (head == deListNode) {
head = null;
} else {
// 要删除的结点不在链表的中间,不在尾部
if (deListNode.nextNode != null) {
deListNode.nodeValue = deListNode.nextNode.nodeValue;
deListNode.nextNode = deListNode.nextNode.nextNode;
} else {
ListNode pointNode = head;
while (pointNode.nextNode != deListNode) {
pointNode = pointNode.nextNode;
}
pointNode.nextNode = null;
}
}
} /**
* @param args
*/
// 测试代码
public static void main(String[] args) {
DeleteNode test = new DeleteNode();
ListNode firListNode = new ListNode();
ListNode secListNode = new ListNode();
ListNode thiListNode = new ListNode();
firListNode.nextNode = secListNode;
secListNode.nextNode = thiListNode;
firListNode.nodeValue = 1;
secListNode.nodeValue = 2;
thiListNode.nodeValue = 3;
test.deleteNode(firListNode, thiListNode);
System.out.println(firListNode.nextNode.nodeValue);
} }

Problem14:调整数组顺序使奇数位与偶数的后面

问题描述:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位与数组的前半部分,所有偶数位与数组的后半部分;

 package Problem14;

 /*
* 问题描述:
* 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位与数组的前半部分,所有偶数位与数组的
* 后半部分
*/
public class ReorderOddEven {
public static void reOrder(int array[]) {
int firstIndex = 0;
int lastIndex = array.length - 1;
if (array == null || (0 == array.length)) {
return;
}
while (firstIndex < lastIndex) {
while ((firstIndex < lastIndex) && !(isEven(array[firstIndex]))) {
firstIndex++;
}
while ((firstIndex < lastIndex) && isEven(array[lastIndex])) {
lastIndex--;
}
if (firstIndex < lastIndex) {
int temp = array[firstIndex];
array[firstIndex] = array[lastIndex];
array[lastIndex] = temp;
}
}
} // 进行解耦操作:odd(奇数)、even(偶数)
private static boolean isEven(int n) { return (n & 1) == 0;
} public static void printArr(int array[]) {
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + "");
}
} /**
* @param args
* @throws Exception
*/
public static void main(String[] args) {
ReorderOddEven test = new ReorderOddEven();
// 功能测试
// 奇偶交替出现
int array1[] = { 1, 2, 3, 4, 5, 6 };
test.reOrder(array1);
printArr(array1);
System.out.println("------1------");
// 所有偶数都位于奇数的前面
int array2[] = { 8, 2, 4, 5, 3, 6 };
test.reOrder(array2);
printArr(array2);
System.out.println("------2------");
// 所有奇数都位于偶数的前面
int array3[] = { 5, 3, 1, 7, 2, 4, 6, 8 };
test.reOrder(array3);
printArr(array3);
System.out.println("------3------");
// 输入的数组只有一个元素
int array4[] = { 2 };
test.reOrder(array4);
printArr(array4);
System.out.println("------4------");
// 特殊输入测试:输入null指针
int array5[] = null;
test.reOrder(array5);
printArr(array5);
System.out.println("------5------"); } }

对应OJ在线编程题目:

题目描述

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
解法一:时间复杂度为O(n^2);
 public class Solution {
public void reOrderArray(int [] array) {
for(int i=0;i<array.length-1;i++)
{
for(int j=0;j<array.length-1;j++)
{
if(array[j]%2==0&&array[j+1]%2==1)
{
int t=array[j];
array[j]=array[j+1];
array[j+1]=t;
}
}
} }
}

解法二:时间复杂度O(n),空间复杂度O(n)

 public class Solution {
public void reOrderArray(int [] array) { reOrderCore(array,array.length);
}
private void reOrderCore(int array[],int len){ if(array==null||array.length==0){
return;
} //用空间换时间
int newArray[]=new int[len];
int index=0;
for(int i=0;i<len;i++){
if((array[i]&1)==1){
newArray[index++]=array[i];
}
}
for(int i=0;i<len;i++){
if((array[i]&1)==0){
newArray[index++]=array[i];
}
}
for(int i=0;i<len;i++){
array[i]=newArray[i];
}
}
}

Problem15:链表中倒数第K个结点

问题描述:输入一个链表,输出该链表中倒数第K个结点。为了符合大多数人的习惯,从1开始计数,即链表的尾结点是倒数第一个结点。例如一个链表有6个结点,从头结点开始它们的值依次是1、2、3、4、5、6.这个链表的倒数第三个结点是值为4的结点。

 package Problem15;

 /*
* 问题描述:
* 输入一个链表,输出该链表中倒数第K个结点。为了符合大多数人的习惯,从1开始计数,即链表的
* 尾结点是倒数第一个结点。例如一个链表有6个结点,从头结点开始它们的值依次是1、2、3、4、5、6.
* 这个链表的倒数第三个结点是值为4的结点。
*/ //定义链表的结点结构
class ListNode {
ListNode next;
int data; public ListNode(int data) {
this.data = data;
}
} public class FindKthFromTail { /*
* 需要考虑三种情况: 1.链表为空; 2.链表的长度不足k; 3.k=0;
*/ public static int find(ListNode head, int k) {
if (head == null || (0 == k)) {
return 0;
}
ListNode first = head;
ListNode second = null;
// 第一个指针先走k-1
for (int i = 1; i < k; i++) {
if (first.next != null) {
first = first.next;
} else {
return 0;
}
}
second = head;
while (first.next != null) {
first = first.next;
second = second.next;
}
return second.data;
} /**
* @param args
*/
// 进行测试
public static void main(String[] args) {
FindKthFromTail test = new FindKthFromTail();
// 功能测试:第K个结点位与链表的中间、头部、尾部
ListNode node1 = new ListNode(1);
ListNode node2 = new ListNode(2);
ListNode node3 = new ListNode(3);
ListNode node4 = new ListNode(4);
node1.next = node2;
node2.next = node3;
node3.next = node4;
node4.next = null;
System.out.println(test.find(node1, 3));
System.out.println(test.find(node1, 1));
System.out.println(test.find(node1, 4));
// 特殊输入测试:k=0
System.out.println(test.find(node1, 0));
// k>4
System.out.println(test.find(node1, 5));
// 链表为空
node1 = null;
System.out.println(test.find(node1, 3));
} }

Problem16:反转链表

问题描述:定义一个函数,输入一个链表的头结点,反转该链表并输出反转后链表的头结点。

解法一:非递归实现

 package Problem16;

 /*
* 问题描述:
* 定义一个函数,输入一个链表的头结点,反转该链表并输出反转后链表的头结点。
*/
//定义链表的结点结构
class ListNode {
ListNode next;
int data; public ListNode(int data) {
this.data = data;
}
} public class ReversList { public static int getReverseListHead(ListNode head) {
// 定义一个结点,用来保存找到的反转链表的表头结点
// 当前正在遍历的结点p
ListNode pNode = head;
// 结点p的前一个结点
ListNode preNode = null;
// 结点p的后一个结点
ListNode reversHeadNode = null;
if (head == null) {
return 0;
}
if (head.next == null) {
return head.data;
} else {
while (pNode != null) {
ListNode nextNode = null;
nextNode = pNode.next;
if (nextNode == null) {
reversHeadNode = pNode;
}
pNode.next = preNode;
preNode = pNode;
pNode = nextNode;
}
}
return reversHeadNode.data; } /**
* @param args
*/
// 测试
public static void main(String[] args) {
ReversList test = new ReversList();
ListNode node1 = new ListNode(1);
ListNode node2 = new ListNode(2);
ListNode node3 = new ListNode(3);
ListNode node4 = new ListNode(4);
node1.next = node2;
node2.next = node3;
node3.next = node4;
node4.next = null;
// 功能测试:链表中有多个结点、有一个结点
// 特殊输入测试:链表头结点为null指针
System.out.println(test.getReverseListHead(node1));
} }

解法二:递归实现

 /*
public class ListNode {
int val;
ListNode next = null; ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode ReverseList(ListNode head) {
//链表只有一个节点或者到达了链表的尾部
if(head==null||head.next==null){
return head;
}
ListNode newList=ReverseList(head.next); head.next.next=head;
head.next=null;
return newList;
}
}

Problem17:合并两个排序的链表

问题描述: 输入两个递增排序的链表,合并这两个链表并使新链表中的结点仍然是按照递增排序的。

 package Problem17;

 /*
* 问题描述:
* 输入两个递增排序的链表,合并这两个链表并使新链表中的结点仍然是按照递增排序的。
*/ //定义链表结点的结构
class ListNode {
ListNode next;
int data; public ListNode() { } public ListNode(int data) {
this.data = data;
}
} public class MergeList { public static ListNode Merge(ListNode head1, ListNode head2) {
if (head1 == null) {
return head2;
}
if (head2 == null) {
return head1;
}
ListNode mergeNode = null;
;
if (head1.data < head2.data) {
mergeNode = head1;
mergeNode.next = Merge(head1.next, head2);
} else {
mergeNode = head2;
mergeNode.next = Merge(head1, head2.next);
}
return mergeNode;
} public static void printList(ListNode merge) {
ListNode head = merge;
while (head != null) {
System.out.print(head.data);
head = head.next;
}
System.out.println();
} /**
* @param args
*/ // 测试
public static void main(String[] args) {
// 功能测试:输入的两个链表有多个结点,结点的值互不相同或者存在相等的多个结点
// 特殊输入测试:两个链表中的一个或者两个头结点为null指针、两个链表中只有一个结点
MergeList test = new MergeList();
ListNode head1 = new ListNode(2);
ListNode head2 = new ListNode(4);
ListNode head3 = new ListNode(8);
head1.next = head2;
head2.next = head3;
head3.next = null;
ListNode sec1 = new ListNode(1);
ListNode sec2 = new ListNode(3);
ListNode sec3 = new ListNode(9);
sec1.next = sec2;
sec2.next = sec3;
sec3.next = null;
ListNode merge = new ListNode();
merge = test.Merge(head1, sec1);
test.printList(merge);
} }

Problem18:树的子结构

题目描述:输入两棵二叉树A和B,判断B是不是A的子结构;

 /**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null; public TreeNode(int val) {
this.val = val; } }
*/
public class Solution {
//空树不是任何一个树的子结构
public boolean HasSubtree(TreeNode root1,TreeNode root2) {
if(root1==null||root2==null){
return false;
}
boolean result=false; //第一步;在树A中找到和树B根结点值相同的节点,因此需要对二叉树进行遍历
if(root1!=null&&root2!=null){
if(root1.val==root2.val){
result=DoTheJudge(root1,root2);
}
if(!result){
result=HasSubtree(root1.left,root2);
}
if(!result){
result=HasSubtree(root1.right,root2);
}
}
return result;
}
private boolean DoTheJudge(TreeNode root1,TreeNode root2){
//遍历完树A都没有,其中都没有完全匹配树B的子结构
if(root1==null&&root2!=null){
return false;
}
//root2的所有节点与root1中进行了匹配
if(root2==null){
return true;
}
if(root1.val!=root2.val){
return false;
}else{
return DoTheJudge(root1.left,root2.left)&&DoTheJudge(root1.right,root2.right);
}
}
}

Problem19:二叉树的镜像

题目描述:请完成一个函数,输入一个二叉树,该函数输出它的镜像;

 package Problem19;

 /*
* 问题描述:
* 请完成一个函数,输入一个二叉树,该函数输出它的镜像;
*/ //定义二叉树的结构
class BinaryTreeNode {
BinaryTreeNode leftNode;
BinaryTreeNode rightNode;
int value;
} public class MirrorOfBinaryTree { public static void mirroOfBinTree(BinaryTreeNode root) {
if (root == null) {
return;
}
if (root.leftNode == null || root.rightNode == null) {
return;
}
// 交换一个结点的左右子节点
int tempValue;
tempValue = root.leftNode.value;
root.leftNode.value = root.rightNode.value;
root.rightNode.value = tempValue;
// 递归操作左右子节点
if (root.leftNode != null) {
mirroOfBinTree(root.leftNode);
}
if (root.rightNode != null) {
mirroOfBinTree(root.rightNode);
}
} /**
* @param args
*/
// 功能测试:普通的二叉树,二叉树的所有结点都没有左子树或者右子树,只有一个结点的二叉树
// 特殊输入测试:二叉树的根节点为null指针
public static void main(String[] args) {
BinaryTreeNode root1 = new BinaryTreeNode();
BinaryTreeNode node1 = new BinaryTreeNode();
BinaryTreeNode node2 = new BinaryTreeNode();
BinaryTreeNode node3 = new BinaryTreeNode();
BinaryTreeNode node4 = new BinaryTreeNode();
BinaryTreeNode node5 = new BinaryTreeNode();
BinaryTreeNode node6 = new BinaryTreeNode();
root1.leftNode = node1;
root1.rightNode = node2;
node1.leftNode = node3;
node1.rightNode = node4;
node4.leftNode = node5;
node4.rightNode = node6;
root1.value = 8;
node1.value = 8;
node2.value = 7;
node3.value = 9;
node4.value = 2;
node5.value = 4;
node6.value = 7; MirrorOfBinaryTree.mirroOfBinTree(root1);
System.out.println(root1.rightNode.value);
} }

Problem20:顺时针打印矩阵

题目描述:输入一个矩阵,按照从外向里以顺时针的顺组依次打印出每一个数字。

解法一:

 import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> printMatrix(int [][] matrix) { ArrayList<Integer> result=new ArrayList<>();
if(matrix==null||matrix.length<0|matrix[0].length<0){
return result;
}
int rows=matrix.length;
int columns=matrix[0].length;
int start=0;
//打印的起始位置
//循环中打印每一圈
while(rows>2*start&&columns>2*start){
result=printMatrixCicle(matrix,rows,columns,start,result);
start++;
}
return result; }
//
private ArrayList<Integer> printMatrixCicle(int matrix[][],int rows,int columns,int start,ArrayList<Integer> result){
int endX=rows-1-start;
int endY=columns-1-start;
//从左到右打印一行
for(int i=start;i<=endY;i++){
result.add(matrix[start][i]);
}
//从上到下打印一列:至少需要两行,即终止行号需要大于起始行号
if(start<endX){
for(int i=start+1;i<=endX;i++){
result.add(matrix[i][endY]);
}
}
//从右到左打印一行,至少需要两行两列
if(start<endX&&start<endY){
for(int i=endY-1;i>=start;i--){
result.add(matrix[endX][i]);
}
}
//从下到上打印一列,至少需要三行两列
if(endX>start+1&&endY>start){
for(int i=endX-1;i>=start+1;i--){
result.add(matrix[i][start]);
}
}
return result;
}
}

解法二:

 import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> printMatrix(int [][] matrix) {
ArrayList<Integer> result=new ArrayList<Integer>();
if(matrix==null||matrix.length<=0||matrix[0].length<=0){
return result;
}
int up=0;
int down=matrix.length-1;
int left=0;
int right=matrix[0].length-1;
while(left<=right&&up<=down){
//从左到右打印一行
for(int i=left;i<=right;i++){
result.add(matrix[up][i]);
}
up++;
//从上到下打印一列
for(int i=up;i<=down;i++){
result.add(matrix[i][right]);
}
right--;
//从右到左打印一行,if判断是否有下一行需要打印
if(up-1!=down){
for(int i=right;i>=left;i--){
result.add(matrix[down][i]);
}
}
down--;
//从下到上打印一列 ,if判断是否还有下一列需要打印
if(left!=right+1){
for(int i=down;i>=up;i--){
result.add(matrix[i][left]);
}
}
left++; }
return result; }
}

Problem21:包含min函数的栈

题目描述:定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的min函数。在该栈中,调用min、push以及pop的时间复杂度为O(1)

 import java.util.Stack;

 public class Solution {

     //数据栈
private Stack<Integer> stackData;
//辅助栈
private Stack<Integer> stackMin; public Solution(){
stackData=new Stack<Integer>();
stackMin=new Stack<Integer>();
} //入栈操作
public void push(int node) {
stackData.push(node);
if(stackMin.empty()){
stackMin.push(node);
}else{
if(node<=stackMin.peek()){
stackMin.push(node);
}
}
}
//出栈操作
public void pop() {
int data=stackData.pop();
if(data==stackMin.peek()){
stackMin.pop();
} }
//返回栈顶的元素
public int top() {
return stackData.peek();
}
//得到栈的最小元素
public int min() {
return stackMin.peek();
}
}

Problem22:栈的压入、弹出序列

题目描述:输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出序列。假设压入栈的所有数字均不相等。例如序列1/2/3/4/5是某栈的压栈序列,序列4/5/3/2/1是该压栈序列对应的一个弹出序列,但4/3/5/1/2就不可能是该压栈序列的弹出序列;

 package Problem22;

 import java.util.Stack;

 /*
* 问题描述:输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出序列。假设压入栈的所有数字均不相等。
* 例如序列1/2/3/4/5是某栈的压栈序列,序列4/5/3/2/1是该压栈序列对应的一个弹出序列,但4/3/5/1/2就不可能是该压栈序列的弹出序列
*/ public class IsPopOrder {
/*
* 输入两个整数序列,第一个序列表示压入顺序,判断第二个序列是否为弹出顺序.假设入栈所有数字均不相等
*/
public boolean isPopOrder(int[] line1, int[] line2) {
if (line1 == null || line2 == null)
return false;
int point1 = 0;
Stack<Integer> stack = new Stack<Integer>();
for (int i = 0; i < line2.length; i++) {
if (!stack.isEmpty() && stack.peek() == line2[i]) {
stack.pop();
} else {
if (point1 == line1.length)
return false;
else {
do
stack.push(line1[point1++]);
while (stack.peek() != line2[i] && point1 != line1.length);
if (stack.peek() == line2[i])
stack.pop();
else
return false;
}
}
}
return true;
} public static void main(String args[]) {
int[] array1 = { 1, 2, 3, 4, 5 };
int[] array2 = { 4, 3, 5, 1, 2 };
IsPopOrder test = new IsPopOrder();
System.out.println(test.isPopOrder(array1, array2));
}
}

Problem23:从上往下打印二叉树

题目描述:

从上往下打印出二叉树的每个节点,同层节点从左至右打印。

 import java.util.ArrayList;
import java.util.LinkedList;
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null; public TreeNode(int val) {
this.val = val; } }
*/
public class Solution {
public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
//定义一个辅助队列
LinkedList<TreeNode> queue=new LinkedList<>();
//存储一层的打印结果
ArrayList<Integer> resultCur=new ArrayList<Integer>();
//存储所有的打印结果
//ArrayList<ArrayList<Integer>> result=new ArrayList<>();
if(root==null){
return resultCur;
}
//定义两个变量last和nlast,分别记录当前行的最右节点和下一行的最右节点
TreeNode last=root;
TreeNode nlast=root;
TreeNode cur=root;
queue.add(root);
while(queue.size()!=0){
cur=queue.poll();
resultCur.add(cur.val);
if(cur.left!=null){
queue.add(cur.left);
nlast=cur.left;
}
if(cur.right!=null){
queue.add(cur.right);
nlast=cur.right;
}
if(cur==last){
//result.add(resultCur);
// resultCur=new ArrayList<>();
last=nlast;
}
}
return resultCur;
}
}

Problem24 二叉搜索树的后序遍历序列

题目描述:

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。

Problem29 数组中出现次数超过一半的数字

 public class Solution {
public boolean VerifySquenceOfBST(int [] sequence) {
if(sequence.length == 0) return false;
return IsTreeBST(sequence, 0, sequence.length-1);
}
public boolean IsTreeBST(int [] sequence,int start,int end ){
//if(end <= start) return true;
int i = start;
for (; i < end; i++) {
if(sequence[i] > sequence[end]) break;
}
int j;
for (j = i; j < end; j++) {
if(sequence[j] < sequence[end]) return false;
}
boolean left=true;
//根结点左子树不为空
if(i>0){
left=IsTreeBST(sequence, start, i-1);
}
boolean right=true;
//根结点右子树不为空
if(j<end-1){
return IsTreeBST(sequence, i, end-1);
}
return left&&right;
}
}

Problem24 二叉树中和为某一值的路径

题目描述:

输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。

 import java.util.ArrayList;
//import java.util.Stack;
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null; public TreeNode(int val) {
this.val = val; } }
*/
//需要注意的是递归函数的参数传递,如果是在FindPathCore函数中定义curResult和curSum时,不传入该函数的话,它的下一级递归函数是无法看到这两个值的
public class Solution {
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
//定义result用于存储找到的所有符合条件的路径
ArrayList<ArrayList<Integer>> result=new ArrayList<>();
ArrayList<Integer> curResult=new ArrayList<>();
if(root==null){
return result;
}
int curSum=0;
FindPathCore(root,target,curResult,result,curSum);
return result;
} private void FindPathCore(TreeNode root,int target,ArrayList<Integer> curResult,ArrayList<ArrayList<Integer>> result,int curSum){
if(root==null){
return;
}
boolean isLeaf=(root.left==null&&root.right==null); curSum+=root.val;
//如果让前节点是叶子节点
if(isLeaf){
if(curSum==target){
curResult.add(root.val);
result.add(new ArrayList<Integer>(curResult));
//路径中取出该叶子节点
curResult.remove(curResult.size()-1);
}
//返回上层节点,并从当前路径和中减去该叶子节点
curSum-=root.val;
return;
}
curResult.add(root.val);
FindPathCore(root.left,target,curResult,result,curSum);
FindPathCore(root.right,target,curResult,result,curSum);
curResult.remove(curResult.size()-1); }
}

Problem28 字符串的排列

题目描述:

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

输入描述:

输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。
 import java.util.*;
public class Solution {
public ArrayList<String> Permutation(String str) {
ArrayList<String> re = new ArrayList<String>();
if (str == null || str.length() == 0) {
return re;
}
HashSet<String> set = new HashSet<String>();
fun(set, str.toCharArray(), 0);
re.addAll(set);
Collections.sort(re);
return re;
}
void fun(HashSet<String> re, char[] str, int beginIndex) {
if (beginIndex == str.length) {
//找到了一个字符串
re.add(new String(str));
return;
}
for (int i = beginIndex; i < str.length; i++) {
swap(str, i, beginIndex);
fun(re, str, beginIndex + 1);
swap(str, i, beginIndex);
}
}
void swap(char[] str, int i, int j) {
if (i != j) {
char t = str[i];
str[i] = str[j];
str[j] = t;
}
}
}

Problem29 数组中出现次数超过一半的数字

题目描述

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。

解法一:其中对于错误输入的判断值得借鉴
 public class Solution {
boolean isInvalid=false; public int MoreThanHalfNum_Solution(int [] array) { if(CheckInvalidArray(array,array.length)){
return -1;
}
int result=array[0];
int count=1;
for(int i=1;i<array.length;i++){
if(count==0){
result=array[i];
count=1;
}else if(array[i]==result){
count++;
}else{
count--;
}
}
if(!CheckMoreThanHalf(array,array.length,result)){
result=0;
}
return result;
}
//判断数组是否为null或者数组长度为0,我们在这里封装成一个函数,以后多考虑这种用法
private boolean CheckInvalidArray(int[] arr,int length){ isInvalid=false;
//输入无效的情况
if(arr==null||length==0){
isInvalid=true;
}
return isInvalid;
}
//判断一个数字的出现次数是否超过数组元素的一半
private boolean CheckMoreThanHalf(int[] arr,int length,int number){
int count=0;
for(int i=0;i<length;i++){
if(arr[i]==number){
count++;
}
}
boolean isMoreThanHalf=true;
if(count*2<=length){
isInvalid=true;
isMoreThanHalf=false;
}
return isMoreThanHalf;
}
}

解法二:使用哈希表,时间复杂度和空间复杂度都为O(n)

 public class Solution {

     public int MoreThanHalfNum_Solution(int [] array) {
if(array==null||array.length==0){
return -1;
}
//使用哈希表
int mapArr[]=new int[256];
for(int i=0;i<mapArr.length;i++){
mapArr[i]=0;
}
for(int i=0;i<array.length;i++){
mapArr[array[i]]++;
}
for(int i=0;i<mapArr.length;i++){
if(mapArr[i]>(array.length)/2){
return i;
}
}
//没有找到这样的数字
return 0;
}
}

Problem31连续子数组的最大和

题目描述:

HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。你会不会被他忽悠住?(子向量的长度至少是1)

 public class Solution {
public int FindGreatestSumOfSubArray(int[] array)throws Exception{
if(array==null||array.length==0){
throw new Exception("重新考虑输入");
}
//用于保存已经找到的连续子数组的最大和
int maxSum=array[0];
//当前最大和
int curSum=array[0];
for(int i=1;i<array.length;i++){
//curSum<=0;说明从开始的位置到i-1的位置的元素不可能属于这个连续子数组的范围
//因此从数组下标为i的元素继续寻找
if(curSum<=0){
curSum=array[i];
}else{
curSum+=array[i];
}
//判断此时经过一次循环我们找到的最大值与之前已经有的最大值的大小
if(curSum>maxSum){
maxSum=curSum;
}
}
return maxSum;
}
}

Problem32 整数中1出现的次数

题目描述:

求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数。

解法一:时间复杂度N*(logN)

 public class Solution {
public int NumberOf1Between1AndN_Solution(int n) {
int count=0;
for(int i=1;i<=n;i++){ count+=core(i);
} return count;
}
private int core(int n){
int num=0;
while(n!=0){
if(n%10==1){
num++;
}
n/=10;
}
return num;
}
}

Problem33 把数组排成最小的数

题目描述:

输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。

 import java.util.*;

 /*
* 解题思路:
* 先将整型数组转换成String数组,然后将String数组排序,最后将排好序的字符串数组拼接出来。关键就是制定排序规则。
* 排序规则如下:
* 若ab > ba 则 a > b,
* 若ab < ba 则 a < b,
* 若ab = ba 则 a = b;
* 解释说明:
* 比如 "3" < "31"但是 "331" > "313",所以要将二者拼接起来进行比较
*/
public class Solution {
public String PrintMinNumber(int [] numbers) {
if(numbers==null||numbers.length<1){
return "";
}
int len=numbers.length;
//将数组元素转换为字符串数组中的元素,即用字符串表示数字,应对大数溢出问题
String[] str=new String[len];
for(int i=0;i<len;i++){
str[i]=String.valueOf(numbers[i]);
}
Arrays.sort(str,new Comparator<String>(){
@Override
public int compare(String c1,String c2){
return (c1+c2).compareTo(c2+c1);
}
});
StringBuilder builder=new StringBuilder();
for(int i=0;i<len;i++){
builder.append(str[i]);
}
return builder.toString();
}
}

Problem34 丑数

题目描述:

把只包含因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。

解法1:

 public class Solution {
public int GetUglyNumber_Solution(int index) { if(index<=0){
return 0;
}
int uglyCount=0;
int num=0;
while(uglyCount<index){
++num;
if(isUglyNumber(num)){
++uglyCount;
}
}
return num;
}
private boolean isUglyNumber(int num){ while(num%2==0){
num=num/2;
}
while(num%3==0){
num/=3;
}
while(num%5==0){
num/=5;
}
return (num==1)?true:false;
}
}

解法二:

 public class Solution {
//用空间换时间的方法
public int GetUglyNumber_Solution(int index) {
if(index<=0){
return 0;
}
//辅助数组,大小为index
int uglyArr[]=new int[index];
//题目中描述的第一个丑数为1
uglyArr[0]=1;
//下一个丑数的位置
int nextUgly=1; int index2=0;
int index3=0;
int index5=0; int ugly2=uglyArr[index2];
int ugly3=uglyArr[index3];
int ugly5=uglyArr[index5]; //找到的丑数的数目还不满足要求
while(nextUgly<index){
int min=Min(ugly2*2,ugly3*3,ugly5*5);
uglyArr[nextUgly]=min; while(uglyArr[index2]*2<=uglyArr[nextUgly]){ ugly2=uglyArr[++index2];
} while(uglyArr[index3]*3<=uglyArr[nextUgly]){ ugly3=uglyArr[++index3];
} while(uglyArr[index5]*5<=uglyArr[nextUgly]){ ugly5=uglyArr[++index5];
}
++nextUgly;
}
return uglyArr[nextUgly-1];
}
//求三个数的最小值的Min函数
private int Min(int num1,int num2,int num3){
int min=num1<num2?num1:num2;
return min<num3?min:num3;
}
}

Problem35 第一个只出现一次的字符

题目描述:

在一个字符串(1<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置

 public class Solution {
public int FirstNotRepeatingChar(String str) {
//哈希表的使用
int mapArr[]=new int[256];
for(int i=0;i<mapArr.length;i++){
mapArr[i]=0;
}
char chs[]=str.toCharArray();
int lens=chs.length;
for(int i=0;i<lens;i++){
mapArr[chs[i]]++;
} for(int i=0;i<lens;i++){
if(mapArr[chs[i]]==1){
return i;
}
}
return -1;
}
}

Problem36数组中的逆序对

题目描述

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007

输入描述:

题目保证输入的数组中没有的相同的数字

数据范围:

对于%50的数据,size<=10^4

对于%75的数据,size<=10^5

对于%100的数据,size<=2*10^5

示例1

输入

1,2,3,4,5,6,7,0

输出

7
 /*
*归并排序的思想,最后求得的逆序数进行取摸 % 1000000007
*/
public class Solution {
   public int InversePairs(int [] array) {
            if(array==null || array.length<=0){
                return 0;
            }
            int pairsNum=mergeSort(array,0,array.length-1);
            return pairsNum;
        }
         
        public int mergeSort(int[] array,int left,int right){
            if(left==right){
                return 0;  
            }
            int mid=(left+right)/2;
            int leftNum=mergeSort(array,left,mid);
            int rightNum=mergeSort(array,mid+1,right);
            return (Sort(array,left,mid,right)+leftNum+rightNum)%1000000007;
        }
         
        public int Sort(int[] array,int left,int middle,int right){
            int current1=middle;
            int current2=right;
            int current3=right-left;
            int temp[]=new int[right-left+1];
            int pairsnum=0;
            while(current1>=left && current2>=middle+1){
                if(array[current1]>array[current2]){
                    temp[current3--]=array[current1--];
                    pairsnum+=(current2-middle);     //这个地方是current2-middle!!!!
                    if(pairsnum>1000000007)//数值过大求余
                    {
                        pairsnum%=1000000007;
                    }
                }else{
                    temp[current3--]=array[current2--];
                }
            }
            while(current1>=left){
                temp[current3--]=array[current1--];
            }
            while(current2>=middle+1){
                temp[current3--]=array[current2--];
            }
            //将临时数组赋值给原数组
            int i=0;
            while(left<=right){
                array[left++]=temp[i++];
            }
            return pairsnum;
        }
}

Problem37 两个链表的第一个公共节点

题目描述:输入两个链表,找出它们的第一个公共结点。

解法一:蛮力法

碰到这道题,很多人的第一反应就是蛮力法:在第一链表上顺序遍历每个结点,每遍历到一个结点的时候,在第二个链表上顺序遍历每个结点。如果在第二个链表上有一个结点和第一个链表上的结点一样,说明两个链表在这个结点上重合,于是就找到了它们的公共结点。如果第一个链表的长度为m,第二个链表的长度为n,显然该方法的时间复杂度是O(mn)。

解法二:借助外部空间法

首先,经过分析我们发现两个有公共结点而部分重合的链表,拓扑形状看起来像一个Y,而不可能像X,如下图所示,两个链表在值为6的结点处交汇:

剑指offer题目java实现

  如果两个链表有公共结点,那么公共结点出现在两个链表的尾部。如果我们从两个链表的尾部开始往前比较,最后一个相同的结点就是我们要找的结点。But,在单链表中只能从头结点开始按顺序遍历,最后才能到达尾结点。最后到达的尾结点却要最先被比较,这是“后进先出”的特性。于是,我们可以使用栈的特点来解决这个问题:分别把两个链表的结点放入两个栈里,这样两个链表的尾结点就位于两个栈的栈顶,接下来比较两个栈顶的结点是否相同。如果相同,则把栈顶弹出接着比较下一个栈顶,直到找到最后一个相同的结点

 import java.util.*;
/*
public class ListNode {
int val;
ListNode next = null; ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
//时间复杂度为O(mn),空间复杂度为O(m+n)的方法
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
if(pHead1==null||pHead2==null){
return null;
} Stack<ListNode> stack1=new Stack<>();
Stack<ListNode> stack2=new Stack<>();
while(pHead1!=null){
stack1.push(pHead1);
pHead1=pHead1.next;
}
while(pHead2!=null){
stack2.push(pHead2);
pHead2=pHead2.next;
}
if(stack1.peek().val!=stack2.peek().val){
return null;
}
ListNode node1=null;
ListNode node2=null;
ListNode common=null;
while(!stack1.empty()&&!stack2.empty()){
node1=stack1.peek();
node2=stack2.peek();
if(node1.val==node2.val){
stack1.pop();
stack2.pop();
common=node1;
}else{
break;
}
} return common;
}
}

解法三:不借助外部空间法

那么,可不可以不借助栈来实现了呢?答案是可以的,我们可以首先遍历两个链表得到它们的长度,就能知道哪个链表比较长,以及长的链表比短的链表多几个结点。在第二次遍历的时候,在较长的链表上先走若干步,接着再同时在两个链表上遍历,找到的第一个相同的结点就是它们的第一个公共结点

  比如在上图的两个链表中,我们可以先遍历一次得到它们的长度分别为5和4,也就是较长的链表与较短的链表相比多一个结点。第二次先在长的链表上走1步,到达结点2。接下来分别从结点2和结点4出发同时遍历两个结点,直到找到它们第一个相同的结点6,这就是我们想要的结果。

 /*
public class ListNode {
int val;
ListNode next = null; ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
//时间复杂度为O(mn),空间复杂度为O(1)的方法
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
int len1=getLengthOfList(pHead1);
int len2=getLengthOfList(pHead2);
ListNode longList=null;
ListNode shortList=null;
//两个链表的长度差
int diff=0;
if(len1<len2){
diff=len2-len1;
longList=pHead2;
shortList=pHead1;
}else{
diff=len1-len2;
longList=pHead1;
shortList=pHead2;
}
//长链表先走diff步
while(diff>0){
longList=longList.next;
diff--;
}
while(longList!=null&&shortList!=null&&longList!=shortList){ longList=longList.next;
shortList=shortList.next;
}
return longList;
}
private int getLengthOfList(ListNode head){
int len=0;
while(head!=null){
len++;
head=head.next;
}
return len;
}
}

\Problem39 二叉树的深度&&平衡二叉树判断

题目一:

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

 /**
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;
}
int lh=TreeDepth(root.left);
int rh=TreeDepth(root.right);
return lh>rh?(lh+1):(rh+1);
}
}

题目二:

平衡二叉树判断:输入一棵二叉树,判断该二叉树是否是平衡二叉树。

解法一:递归实现(需要重复遍历节点多次)

 import java.util.*;

 /*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}*/
public class CheckBalance {
public boolean check(TreeNode root) {
// write code here
if(root==null){
return true;
}
int lh=getDepth(root.left);
int rh=getDepth(root.right);
int diff=lh-rh;
if(diff>1||diff<-1){
return false;
}
return check(root.left)&&check(root.right);
}
//返回一个节点的深度,即该节点到叶子节点的路径最大长度
private int getDepth(TreeNode root){
if(root==null){
return 0;
}
int lh=getDepth(root.left);
int rh=getDepth(root.right); return lh>rh?(lh+1):rh+1;
}
}

解法二:

 import java.util.*;

 /*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}*/
public class CheckBalance {
public boolean check(TreeNode root) {
//定义一个引用类型的数据作为平衡标记,通过传引用的方式在递归左右子树时修改平衡标记
boolean[] res=new boolean[1];
//从根节点开始遍历树,遍历过程中修改平衡标记
res[0]=true;
postCheck(root,1,res); return res[0];
}
public int postCheck(TreeNode root,int depth,boolean[] res){
if(root==null){
return depth;
}
//遍历一次左子树,获取深度(深度已经在参数改变了,目的是为了检查左子树是否平衡)
//若遍历左子树过程中修改了平衡标记为false,则子树非平衡,所以当前结点为根的子树非平衡,不再递归,直接返回 int left_depth=postCheck(root.left,depth+1,res);
if(res[0]==false){
return depth;
}
//若左子树是平衡的,则遍历右子树并获取深度
//若遍历右子树过程中修改了平衡标记为false,则子树非平衡,所以当前结点为根的子树非平衡,不再递归,直接返回
int right_depth=postCheck(root.right,depth+1,res);
if(res[0]==false){
return depth;
} //若左右子树都是平衡的,则对左右子树深度进行比较,判断当前结点为根的子树是否平衡
if(Math.abs(left_depth-right_depth)>1){//高度差大于1,当前子树不平衡,修改平衡标记
res[0]=false;
}
//用左右子树深度最大者作为自己的高度
return Math.max(left_depth,right_depth);
}
}

Problem38 数字在排序数组中出现的次数

题目描述

统计一个数字在排序数组中出现的次数。
 public class Solution {
//需要充分利用排序数组这个特点
public int GetNumberOfK(int [] array , int k) { if(array==null||array.length==){
return ;
}
int firstIndexK=getTheIndexOfFirst(array,array.length,k,,array.length-);
int lastIndexK=getTheIndexOfLast(array,array.length,k,,array.length-);
if(firstIndexK>-||lastIndexK>-){
return(lastIndexK-firstIndexK+);
}
//数组中不存在数字k
return ;
}
//找到第一次出现k的位置
private int getTheIndexOfFirst(int[] arr,int len,int k,int start,int end){
if(start>end){
return -;
}
int mid=(start+end)/;
int dataMid=arr[mid];
if(dataMid>k){
end=mid-;
}else if(dataMid<k){
start=mid+;
}else{
if(mid!=){
if(arr[mid-]!=k){
return mid;
}else{
end=mid-;
}
}else{
return mid;
}
}
return getTheIndexOfFirst(arr,len,k,start,end);
}
//找到最后一次出现k的位置
private int getTheIndexOfLast(int[] arr,int len,int k,int start,int end){
if(start>end){
return -;
}
int mid=(start+end)/;
int dataMid=arr[mid];
if(arr[mid]<k){
start=mid+;
}else if(arr[mid]>k){
end=mid-;
}else{
if(mid!=len-){
if(arr[mid+]!=k){
return mid;
}else{
start=mid+;
}
}else{
return mid;
}
}
return getTheIndexOfLast(arr,len,k,start,end);
} }

Problem40 数组中只出现一次的数字

题目描述:

一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

 //num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
//输入不符合要求
if(array==null||array.length==0){
return;
}
//int xor存放异或结果
int xor=0;
//flag用于寻找和标记第N位为1的位置
int flag=1;
//先对整个数组的元素进行异或运算
for(int i=0;i<array.length;i++){
xor^=array[i];
}
while((xor&flag)==0){
flag=flag<<1;
}
for(int i=0;i<array.length;i++){
if((array[i]&flag)==0){
num1[0]^=array[i];
}else{
num2[0]^=array[i];
}
}
}
}

Problem41 和为S的两个数字VS和为S的连续正数序列

题目一描述:

输入一个递增排序的数组和一个数字S,在数组中查找两个数,是的他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。

 import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
ArrayList<Integer> list=new ArrayList<Integer>();
if(array==null||array.length<){
return list;
}
int small=;
int big=array.length-;
while(small<big){ if(array[small]+array[big]>sum)
big--;
else if(array[small]+array[big]<sum)
small++;
else
{
list.add(array[small]);
list.add(array[big]);
return list;
}
}
return list;
}
}

题目二描述:和为S的连续正数序列

输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序

 import java.util.ArrayList;
public class Solution {
public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) { ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>(); int small = 1;
int big = 2;
int curSum = small+big;
while(small < big && small < (sum+1)/2){
if(curSum == sum){
ArrayList<Integer> list = new ArrayList<Integer>(); for(int i=small;i<=big;i++){
list.add(i);
}
result.add(list); //更新
curSum -= small;
//区间右移
small ++;
big ++;
curSum += big;
}else if(curSum < sum){ //tmp_sum小,右侧扩展
//区间右侧 右移,包括更大的数字
big ++;
curSum += big; //更新
}else{ //tmp_sum大,左侧紧缩
curSum -= small;
small ++; //左侧紧缩
}
}
return result;
}
}

Problem42 翻转单词的顺序

牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?

 import java.util.*;
public class Solution {
public String ReverseSentence(String str) {
if(str==null||str.length()==0){
//反复错在这个位置,之前返回的是null值
return str;
}
char chas[]=str.toCharArray();
rolateWord(chas);
return String.valueOf(chas);
}
private void rolateWord(char chas[]){
if(chas==null||chas.length==0){
return;
}
reverse(chas,0,chas.length-1);
//下面对每一个局部的单词进行逆序
//标记一个单词的开始
int start=0;
//标记一个单词的结束
int end=0;
//遍历字符数组
int i=0;
while(i<chas.length){
while(i<chas.length&&chas[i]==' '){
i++;
start=i;
}
while(i<chas.length&&chas[i]!=' '){
i++;
end=i;
}
reverse(chas,start,end-1);
} }
private void reverse(char chas[],int left,int right){
while(left<right){
char temp=chas[left];
chas[left]=chas[right];
chas[right]=temp;
left++;
right--;
}
}
}

Problem44 扑克牌的顺子

题目描述:

LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张^_^)...他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子.....LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何。为了方便起见,你可以认为大小王是0。

 import java.util.*;

 public class Solution {

     public boolean isContinuous(int [] numbers) {
if(numbers==null||numbers.length<1){
return false;
}
int numberOfZero=0;
int gapsOfNum=0;
for(int i=0;i<numbers.length;i++){
if(numbers[i]==0){
numberOfZero++;
}
}
Arrays.sort(numbers);
int small=numberOfZero;
int big=small+1;
while(big<numbers.length){
//不能出现对子,否则不可能出现顺子
if(numbers[small]==numbers[big]){
return false;
}
gapsOfNum+=numbers[big]-numbers[small]-1;;
small=big;
big++;
}
return (gapsOfNum>numberOfZero)?false:true;
}
}

Problem45 y圆圈中最后剩下的数字(约瑟夫环问题)

题目描述:

每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)

 import java.util.*;

 public class Solution {

     public int LastRemaining_Solution(int n, int m) {
//输入不合法
if(n<1||m<1){
return -1;
}
ArrayList<Integer> list=new ArrayList<Integer>();
for(int i=0;i<n;i++){
list.add(i);
}
int listSize=n;
Iterator<Integer> it=list.iterator();
while(list.size()>1){
for(int i=1;i<=m;i++){
if(it.hasNext()){
it.next();
}else{
//当迭代器扫描到链表尾部时,需要把迭代器移动到链表的头部
it=list.iterator();
it.next();
}
}
it.remove();
listSize--;
}
it=list.iterator();
return it.next();
}
}

Problem46 计算1+2+3+。。。n

题目描述:

求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

 public class Solution {
//常用方法是使用循环或者递归实现
//循环只是让相同代码重复执行遍而已
public int Sum_Solution(int n) {
int sum=n;
boolean flag=(n>0)&&((sum+=Sum_Solution(n-1))>0);
return sum;
}
}

Problem47 不用加减乘除做加法

题目描述:写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。

 public class Solution {
//使用位运算
//1.不考虑进位各位相加 2.考虑进位3.两步结果相加
public int Add(int num1,int num2) {
int sum,carry;
if(num1==0){
return num2;
}
if(num2==0){
return num1;
}
while(num2!=0){
sum=num1^num2;
carry=(num1&num2)<<1;
num1=sum;
num2=carry;
} return num1;
}
}

Problem49 把字符串转换为整数

题目描述:

将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0

输入描述:

输入一个字符串,包括数字字母符号,可以为空

输出描述:

如果是合法的数值表达则返回该数字,否则返回0
示例1

输入

+2147483647
1a33

输出

2147483647
0
 public class Solution {
public int StrToInt(String str) {
int sum =0;
//判断非法输入
if(str == "0" || str == "" ||str.length() == 0){ return 0;
}
//将字符串转换为对应的字符数组
char chs[]=str.toCharArray();
for(int i = 0; i < chs.length;i++){
//是正负号的话,跳出当前if循环,开始下一趟for循环
if(chs[i]== '+' || chs[i] == '-'){
continue;
}
if(chs[i] >'9' || chs[i] <'0'){
return 0;
}else{
sum = (chs[i] - '0') + sum*10;
}
}
if( chs[0] == '-'){
sum = sum * (-1);
}
return sum;
}
}

Problem51 数组中重复的数字

题目描述

在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
 public class Solution {
// Parameters:
// numbers: an array of integers
// length: the length of array numbers
// duplication: (Output) the duplicated number in the array number,length of duplication array is 1,so using duplication[0] = ? in implementation;
// Here duplication like pointor in C/C++, duplication[0] equal *duplication in C/C++
// 这里要特别注意~返回任意重复的一个,赋值duplication[0]
// Return value: true if the input is valid, and there are some duplications in the array number
// otherwise false
public boolean duplicate(int numbers[],int length,int [] duplication) { if(numbers==null||length==0){
return false;
}
//条件判断,保证数组中的元素值在0-n-1的范围内
for(int i=0;i<length;i++){
if(numbers[i]<0||numbers[i]>length-1){
return false;
}
}
for(int i=0;i<length;i++){
while(numbers[i]!=i){
if(numbers[numbers[i]]==numbers[i]){
duplication[0]=numbers[i];
return true;
}
int temp=numbers[i];
numbers[i]=numbers[temp];
numbers[temp]=temp;
}
}
return false;
}
}

Proble52 构建乘积数组

题目描述:给定一个数组A[0,1,...,n-1],请构建一个数组B[0,1,...,n-1],其中B中的元素B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。不能使用除法。

 1 import java.util.ArrayList;
2 public class Solution {
3 public int[] multiply(int[] A) {
4 //将B[i]的计算拆分成A[0]*A[1]...A[i-1]和A[i+1]*...A[n-1]两部分,用一个矩阵来创建数组B
5 int len=A.length;
6 int B[]=new int[len];
7 B[0]=1;
8 //先求矩阵上三角部分
9 for(int i=1;i<len;i++){
10 B[i]=B[i-1]*A[i-1];
11 }
12 //再求下三角部分
13 int temp=1;
14 for(int i=len-2;i>=0;i--){
15 //temp随着i的减小不断变化
16 temp*=A[i+1];
17 B[i]*=temp;
18 }
19 return B;
20 }
21 }

Problem53 正则表达式匹配

题目描述:

请实现一个函数用来匹配包括'.'和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配

 public class Solution {
public boolean match(char[] str, char[] pattern)
{
//条件判断
if(str==null||pattern==null){
return false;
}
return matchCore(str,0,pattern,0);
}
private boolean matchCore(char[] str,int strIndex,char[] pattern,int pIndex){
//有效性检验
//str和pattern同时到达尾部,完成匹配,返回true
if(strIndex==str.length&&pIndex==pattern.length){
return true;
}
//str不到尾,pattern到尾部,返回false
if(strIndex!=str.length&&pIndex==pattern.length){
return false;
} //模式第2个是*,且字符串第1个跟模式第1个匹配,分3种匹配模式;如不匹配,模式后移2位
if (pIndex + 1 < pattern.length && pattern[pIndex + 1] == '*') {
if(strIndex!=str.length&&str[strIndex]==pattern[pIndex]||strIndex!=str.length&&pattern[pIndex]=='.'){
//1.模式串后后移两位,相当于第一个匹配的字符被忽略掉,从字符串的下一个字符继续开始匹配
//2.字符串后移一位,模式串后移两位,相当于模式匹配一个字符
//3.字符串后移一位,模式串不动,因为模式串的第二个字符'*‘,可以匹配前面出现的多个字符
return matchCore(str,strIndex,pattern,pIndex+2)||matchCore(str,strIndex+1,pattern,pIndex+2)||
matchCore(str,strIndex+1,pattern,pIndex);
}else{
return matchCore(str,strIndex,pattern,pIndex+2);
}
}
//模式串第二个字符不是'*',并且两个串的第一个字符是匹配的
if(strIndex!=str.length&&str[strIndex]==pattern[pIndex]||strIndex!=str.length&&pattern[pIndex]=='.'){
return matchCore(str,strIndex+1,pattern,pIndex+1); //否则,直接返回false
}else{
return false;
}
}
}

Problem54 表示数值的字符串

题目描述:

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。

思路:使用正则表达式来解决

 public class Solution {
public boolean isNumeric(char[] str) {
String string = String.valueOf(str);
return string.matches("[\\+-]?[0-9]*(\\.[0-9]*)?([eE][\\+-]?[0-9]+)?");
}
}

Problem55 字符流中第一个不重复的字符

题目描述:

请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。 

输出描述:
如果当前字符流没有存在出现一次的字符,返回#字符。

思路:定义一个数据容器来保存字符在字符流中的位置,当一个字符第一次从字符流中读出来时,把它在字符流中的位置保存在数据容器中,当这个字符再次出现时,那么就吧它在数据容器中保存的值更新为一个特殊的值(比如负数值)

 public class Solution {
//Insert one char from stringstream
//定义一个数组模拟哈希表 int mapArr[]=new int[256];
int index=0; public Solution(){
//最开始,哈希表中所有元素都初始化为-1
for(int i=0;i<256;i++){
mapArr[i]=-1;
}
}
public void Insert(char ch)
{
if(mapArr[ch]==-1){
mapArr[ch]=index++;
}else{
mapArr[ch]=-2;
}
}
//return the first appearence once char in current stringstream
public char FirstAppearingOnce()
{
int minIndex=256;
char ch='#';
for(int i=0;i<256;i++){
if(mapArr[i]>=0&&mapArr[i]<minIndex){
minIndex=mapArr[i];
ch=(char)i;
}
}
return ch;
}
}

Problem56 链表中环的入口结点

题目描述:

一个链表中包含环,请找出该链表的环的入口结点。

 /*
public class ListNode {
int val;
ListNode next = null; ListNode(int val) {
this.val = val;
}
}
*/
public class Solution { //分成两步:1.判断是否存在环,并找到快慢两个指针相遇的位置
// 2.根据找到的这个相遇位置,统计环中节点的数目n,先让快指针走n步,然后
// 快慢两个指针一起运动,快慢指针相遇时的节点就是环的入口节点
public ListNode EntryNodeOfLoop(ListNode pHead)
{
//getMeetingNode函数返回null或者环中的一个节点(快慢指针相遇的节点)
ListNode meetNode=getMeetingNode(pHead);
if(meetNode==null){
return null;
}
//根据找到的这个节点统计环中节点的数目
int loopNodes=1;
ListNode cur=meetNode.next;
while(cur!=meetNode){
loopNodes++;
cur=cur.next;
}
//快指针先走loopNodes步
ListNode fast=pHead;
for(int i=1;i<=loopNodes;i++){
fast=fast.next;
}
//慢指针开始跟快指针一起移动
ListNode slow=pHead;
while(slow!=fast){
slow=slow.next;
fast=fast.next;
}
return fast;
}
//先找两个指针相遇的位置
private ListNode getMeetingNode(ListNode pHead){
if(pHead==null){
return null;
}
ListNode slow=pHead.next;
//只有一个结点 ,直接返回null
if(slow==null){
return null;
}
ListNode fast=slow.next;
while(slow!=null&&fast!=null){
if(slow==fast){
return fast;
}
//slow指针在链表上一次移动一步
slow=slow.next;
//fast指针一次在链表上移动两步
fast=fast.next;
if(fast!=null){
fast=fast.next;
}
}
//链表中不存在环,则直接返回null;
return null;
} }

Problem57删除链表中重复的节点

题目描述:

在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5

 /*
public class ListNode {
int val;
ListNode next = null; ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode deleteDuplication(ListNode pHead)
{
//使用虚拟头结点,方便在链表头部进行的一些操作
ListNode visualHead = new ListNode(-1);
visualHead.next = pHead;
//当前节点的前一个节点
ListNode pre = visualHead;
while(pHead != null && pHead.next != null){
if(pHead.val == pHead.next.val){
int value = pHead.val;
while(pHead != null && pHead.val == value)
pHead = pHead.next;
pre.next = pHead;
}else{
pre = pHead;
pHead = pHead.next;
}
}
return visualHead.next;
}
}

Problem58二叉树的下一个节点

题目描述:

给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。

 /*
public class TreeLinkNode {
int val;
TreeLinkNode left = null;
TreeLinkNode right = null;
TreeLinkNode next = null; TreeLinkNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public TreeLinkNode GetNext(TreeLinkNode pNode)
{
if(pNode==null){
return null;
}
TreeLinkNode pNext=null;
if(pNode.right!=null){
TreeLinkNode pRight=pNode.right;
while(pRight.left!=null){
pRight=pRight.left;
}
pNext=pRight;
}else if(pNode.next!=null){
TreeLinkNode pCur=pNode;
TreeLinkNode parent=pNode.next;
while(parent!=null&&pCur!=parent.left){
pCur=parent;
parent=pCur.next;
}
pNext=parent;
}
return pNext;
}
}

Problem59二叉树的镜像(对称的二叉树)

操作给定的二叉树,将其变换为源二叉树的镜像。 

输入描述:
二叉树的镜像定义:源二叉树
8
/ \
6 10
/ \ / \
5 7 9 11
镜像二叉树
8
/ \
10 6
/ \ / \
11 9 7 5
 /**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null; public TreeNode(int val) {
this.val = val; } }
*/
public class Solution {
public void Mirror(TreeNode root) {
if(root==null){
return ;
}
TreeNode tempNode;
tempNode=root.left; root.left=root.right;
root.right=tempNode; Mirror(root.left);
Mirror(root.right);
}
}

剑指offer书上面对应的题目是判断两个二叉树是否为彼此的镜像二叉树,可以通过二叉树的前序遍历和对称前序遍历来验证;(根->左->右和根->右->左);两个遍历序列相同则说明是对称二叉树,反之则不是;

OJ在线:

题目描述

请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
 /*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null; public TreeNode(int val) {
this.val = val; } }
*/
public class Solution {
boolean isSymmetrical(TreeNode pRoot)
{
return isSymmetricalCore(pRoot,pRoot);
}
private boolean isSymmetricalCore(TreeNode pRoot1,TreeNode pRoot2){
if(pRoot1==null&&pRoot2==null){
return true;
}
if(pRoot1==null||pRoot2==null){
return false;
}
if(pRoot1.val!=pRoot2.val){
return false;
}
return isSymmetricalCore(pRoot1.left,pRoot2.right)&&isSymmetricalCore(pRoot1.right,pRoot2.left);
}
}

Problem60 把二叉树打印成多行

题目描述:从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。

 import java.util.ArrayList;
import java.util.LinkedList; class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null; public TreeNode(int val) {
this.val = val; } } public class Solution {
//层次遍历二叉树
ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
//定义一个辅助队列
LinkedList<TreeNode> queue=new LinkedList<TreeNode>();
//last表示打印当前行时的最右节点
TreeNode last=pRoot;
//nlast表示要打印的下一行的最右节点
TreeNode nlast=pRoot;
//当前正在打印的节点
TreeNode cur=pRoot; queue.add(cur);
//result存放一层的打印结果
ArrayList<Integer> result=new ArrayList<Integer>(); //存储所有的打印结果
ArrayList<ArrayList<Integer>> resultAll=new ArrayList<>();
if(cur==null){
return resultAll;
}
while(queue.size()!=0){
//弹出队列中的一个节点
cur=queue.poll();
result.add(cur.val);
if(cur.left!=null){
queue.add(cur.left);
nlast=cur.left;
}
if(cur.right!=null){
queue.add(cur.right);
nlast=cur.right;
}
//一层打印结束
if(cur==last){
resultAll.add(result);
last=nlast;
result=new ArrayList<Integer>();
}
}
return resultAll;
} }

Problem61 按之字形顺序打印二叉树

题目描述:

请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。

Problem62 序列化二叉树

 import java.util.*;

 /*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null; public TreeNode(int val) {
this.val = val; } }
*/
public class Solution {
public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) { ArrayList<ArrayList<Integer>> result=new ArrayList<>();
if(pRoot==null){
return result;
} //需要使用两个辅助栈
Stack<TreeNode> stack1=new Stack<>();
Stack<TreeNode> stack2=new Stack<>();
//先使用辅助栈1
stack1.push(pRoot);
while(!stack1.empty()||!stack2.empty()){
if(!stack1.empty()){
ArrayList<Integer> curResult=new ArrayList<>();
while(!stack1.empty()){
TreeNode dataPop=stack1.pop();
curResult.add(dataPop.val);
if(dataPop.left!=null){
stack2.push(dataPop.left);
}
if(dataPop.right!=null){
stack2.push(dataPop.right);
}
}
result.add(curResult);
}else{
ArrayList<Integer> curResult=new ArrayList<>(); while(!stack2.empty()){
TreeNode dataPop=stack2.pop();
curResult.add(dataPop.val);
if(dataPop.right!=null){
stack1.push(dataPop.right);
}
if(dataPop.left!=null){
stack1.push(dataPop.left);
}
}
result.add(curResult);
}
}
return result; } }

Problem62 序列化二叉树

题目描述:

请实现两个函数,分别用来序列化和反序列化二叉树

 /*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null; public TreeNode(int val) {
this.val = val; } }
*/
public class Solution { String Serialize(TreeNode root) {
if(root == null)
return "";
StringBuilder builder = new StringBuilder();
serializeCore(root, builder);
return builder.toString();
} void serializeCore(TreeNode root, StringBuilder builder) {
if(root == null) {
builder.append("#,");
return;
}
builder.append(root.val).append(",");
serializeCore(root.left, builder);
serializeCore(root.right, builder);
} TreeNode Deserialize(String str) {
if(str.length() == 0)
return null;
String[] strs = str.split(",");
return Deserialize2(strs);
}
int index = 0; TreeNode Deserialize2(String[] strs) {
if(!strs[index].equals("#")) {
TreeNode root = new TreeNode(0);
root.val = Integer.parseInt(strs[index++]);
root.left = Deserialize2(strs);
root.right = Deserialize2(strs);
return root;
}else{
index++;
return null;
}
}
}

Problem63  二叉搜索树的第k个结点

题目描述:给定一颗二叉搜索树,请找出其中的第k大的结点。例如, 5 / \ 3 7 /\ /\ 2 4 6 8 中,按结点数值大小顺序第三个结点的值为4。

 /*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null; public TreeNode(int val) {
this.val = val; } }
*/
public class Solution {
//对二叉搜索树进行一次中序遍历就可以找到第K大的数
TreeNode KthNode(TreeNode pRoot, int k)
{
if(pRoot==null||k==0){
return null;
}
int result[]=new int[1];
result[0]=k;
return KthNodeCore(pRoot,result);
}
private TreeNode KthNodeCore(TreeNode pRoot,int[] result){
TreeNode target=null;
if(target==null&&pRoot.left!=null){
target=KthNodeCore(pRoot.left,result);
}
if(target==null&&result[0]==1){
target=pRoot;
}else{
result[0]--;
}
if(target==null&&pRoot.right!=null){
target=KthNodeCore(pRoot.right,result);
}
return target;
}
}

Problem65滑动窗口的最大值

题目描述:

给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

 import java.util.*;
public class Solution {
public ArrayList<Integer> maxInWindows(int [] num, int size)
{
ArrayList<Integer> result=new ArrayList<>(0);
if(num==null||num.length<1||num.length<size||size<1){
return result;
}
//定义双端队列
ArrayDeque<Integer> queue=new ArrayDeque<>();
for(int i=0;i<num.length;i++){ while(!queue.isEmpty()&&num[queue.peekLast()]<=num[i]){
queue.pollLast();
}
queue.addLast(i);
//删除队头失效的最大值
if(i-size>=queue.peekFirst()){
queue.pollFirst();
}
if(i>=size-1){
result.add(num[queue.peekFirst()]);
}
}
return result; }
}
上一篇:matplotlib curve.py


下一篇:在cshtml页面中,以‘@’开始的表达式 表示C#语句,会被编译执行