剑指Offer(java答案)
- 剑指Offerjava答案
- 3二维数组中的查找
- 4替换空格
- 5从尾到头打印链表
- 6重建二叉树
- 7用两个栈实现队列
- 8旋转数组的最小数字
- 9斐波那契数列
- 10二进制中1的个数
- 11数值的整数次方
- 12打印1到最大的n位数
- 13在O1时间删除链表结点
- 14调整数组顺序使奇数位于偶数前面
- 15链表中倒数第k个结点
- 16反转链表
- 17合并两个排序的链表
- 18树的子结构
- 19二叉树的镜像
- 20顺时针打印矩阵
- 21包含min函数的栈
- 22栈的压入弹出序列
- 23从上往下打印二叉树层次遍历
- 24二叉搜索树的后序遍历序列
- 25二叉树中和为某一值的路径
- 26复杂链表的复制
- 27二叉搜索树与双向链表
- 28字符串的排列
- 29数组中出现次数超过一半的数字
- 30最小的K个数
- 31连续子数组的最大和
- 32整数中1出现的次数从1到n整数中1出现的次数
- 33把数组排成最小的数
- 34丑数
- 35第一个只出现一次的字符
- 36数组中的逆序对
- 37两个链表的第一个公共交点
- 38数字在排序数组中出现的次数
- 39二叉树的深度
- 40-数组只指出现一次的数字
- 41和为S的两个数字
- 42反转单词
- 44扑克牌的顺序
- 45约瑟夫环问题圆圈中最后一个数字
- 46求123n
- 47不用加减乘除做加法
- 49把字符串转换成整数
- 51数组中重复的数
- 52构建乘积数组
- 53正则表达式匹配
- 54表示数值的字符串
- 55字符流中第一个不重复的字符
- 56链表中环的入口结点
- 57删除链表中重复的结点
- 58二叉树的下一个结点
- 59对称的二叉树
- 60把二叉树打印成多行
- 61按之字形顺序打印二叉树
- 62序列化二叉树
- 63二叉搜索树的第K个节点
- 64数据流中的中位数
- 65滑动窗口的最大值
3、二维数组中的查找
题目描述
在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
1 | 2 | 8 | 9 |
---|---|---|---|
2 | 4 | 9 | 12 |
4 | 7 | 10 | 13 |
6 | 8 | 11 | 15 |
思路: 从右上角开始,若小,向下走,删除一行,若大,向左走,删除一列
/*
利用二维数组由上到下,由左到右递增的规律,
那么选取右上角或者左下角的元素a[row] [col]与target进行比较,
当target小于元素a[row] [col]时,那么target必定在元素a所在行的左边,
即col--;
当target大于元素a[row][col]时,那么target必定在元素a所在列的下边,
即row++;
*/
public class Solution {
public boolean Find(int [][] array,int target) {
int row=0;
int col=array[0].length-1;
while(row<=array.length-1&&col>=0){
if(target==array[row][col])
return true;
else if(target>array[row][col])
row++;
else
col--;
}
return false;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
4、替换空格
题目描述
请实现一个函数,将一个字符串中的空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
思路:先遍历一遍字符,统计空格数,由此计算替换之后的总长度。然后从后往前加载字符串,如果发现空格,就替换20%
/*
问题1:替换字符串,是在原来的字符串上做替换,还是新开辟一个字符串做替换!
问题2:在当前字符串替换,怎么替换才更有效率(不考虑java里现有的replace方法)。
从前往后替换,后面的字符要不断往后移动,要多次移动,所以效率低下
从后往前,先计算需要多少空间,然后从后往前移动,则每个字符只为移动一次,这样效率更高一点。
*/
public class Solution {
public String replaceSpace(StringBuffer str) {
int spacenum = 0;//spacenum为计算空格数
for(int i=0;i<str.length();i++){
if(str.charAt(i)==' ')
spacenum++;
}
int indexold = str.length()-1; //indexold为为替换前的str下标
int newlength = str.length() + spacenum*2;//计算空格转换成%20之后的str长度
int indexnew = newlength-1;//indexold为为把空格替换为%20后的str下标
str.setLength(newlength);//使str的长度扩大到转换成%20之后的长度,防止下标越界
for(;indexold>=0 && indexold<newlength;--indexold){
if(str.charAt(indexold) == ' '){ //
str.setCharAt(indexnew--, '0');
str.setCharAt(indexnew--, '2');
str.setCharAt(indexnew--, '%');
}else{
str.setCharAt(indexnew--, str.charAt(indexold));
}
}
return str.toString();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
5、从尾到头打印链表
题目描述:
输入一个链表,从尾到头打印链表每个节点的值。
思路1:栈
/**
* public class ListNode {
* int val;
* ListNode next = null;
* ListNode(int val) {
* this.val = val;
* }
* }
*/
import java.util.Stack;
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
if(listNode == null){
ArrayList list = new ArrayList();
return list;
}
Stack<Integer> stk = new Stack<Integer>();
while(listNode != null){
stk.push(listNode.val);
listNode = listNode.next;
}
ArrayList<Integer> arr = new ArrayList<Integer>();
while(!stk.isEmpty()){
arr.add(stk.pop());
}
return arr;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
思路2:递归
public class Solution {
public void printListFromTailToHead(ListNode listNode) {
if(listNode != null){
if(listNode.next != null){
printListFromTailToHead(listNode.next);
}
System.out.print(""+listNode.var);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
6、重建二叉树
题目描述:已知中序和前序,请重建二叉树
/**
* Definition for binary tree
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Solution {
public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
TreeNode root=reConstructBinaryTree(pre,0,pre.length-1,in,0,in.length-1);
return root;
}
//前序遍历{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6}
private TreeNode reConstructBinaryTree(int [] pre,int startPre,int endPre,int [] in,int startIn,int endIn) {
if(startPre>endPre||startIn>endIn)
return null;
TreeNode root=new TreeNode(pre[startPre]);
for(int i=startIn;i<=endIn;i++)
if(in[i]==pre[startPre]){
root.left=reConstructBinaryTree(pre,startPre+1,startPre+i-startIn,in,startIn,i-1); //注意pre的位置,要用偏移量,不能用i,因为i是在变化
root.right=reConstructBinaryTree(pre,i-startIn+startPre+1,endPre,in,i+1,endIn);
}
return root;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
引申:已知中序和后序求前序
package com.zhuang.tree;
public class Main {
public static class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}
public static TreeNode reConstructBinaryTree(int [] post,int [] in) {
TreeNode root=reConstructBinaryTree(post,0,post.length-1,in,0,in.length-1);
return root;
}
private static TreeNode reConstructBinaryTree(int [] post,int startPost,int endPost,int [] in,int startIn,int endIn) {
if(startPost>endPost||startIn>endIn)
return null;
TreeNode root=new TreeNode(post[endPost]);
for(int i=startIn;i<=endIn;i++)
if(in[i]==post[endPost]){
root.left=reConstructBinaryTree(post,startPost,startPost+i-startIn-1,in,startIn,i-1);
root.right=reConstructBinaryTree(post,startPost+i-startIn,endPost-1,in,i+1,endIn);
}
return root;
}
public static void preOrder(TreeNode root){
if(root == null){
return;
}
System.out.println(root.val);
preOrder(root.left);
preOrder(root.right);
}
public static void main(String[] args){
int[] post = {2,4,3,1,6,7,5};
int[] in = {1,2,3,4,5,6,7};
TreeNode root = reConstructBinaryTree(post, in);
preOrder(root);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
7、用两个栈实现队列
题目描述
用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
思路:入栈给stack1,出栈时,若stack2不为空,则出栈,若为空,把stack1的内容全都放入stack2,然后再出栈
import java.util.Stack;
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() {
while(!stack2.isEmpty())
{
return stack2.pop();
}
while(!stack1.isEmpty())
{
stack2.push(stack1.pop());
}
return stack2.pop();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
8、旋转数组的最小数字
题目描述
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
import java.util.ArrayList;
public class Solution {
/*
* 传进去旋转数组,注意旋转数组的特性: 1.包含两个有序序列 2.最小数一定位于第二个序列的开头 3.前序列的值都>=后序列的值
*/
// 用到了快速排序的快速定位范围的思想,
public int minNumberInRotateArray(int[] array) {
if (array == null || array.length == 0) {
return 0;
}
int low = 0;//指向第一个
int up = array.length - 1;//指向最后一个
int mid = low;
// 当low和up两个指针相邻时候,就找到了最小值,也就是
// 右边序列的第一个值
while (array[low] >= array[up]) {
if (up - low == 1) {
mid = up;
break;
}
// 如果low、up、mid下标所指的值恰巧相等
// 如:{0,1,1,1,1}的旋转数组{1,1,1,0,1}
if (array[low] == array[up] && array[mid] == array[low])
return MinInOrder(array);
mid = (low + up) / 2;
// 这种情况,array[mid]仍然在左边序列中
if (array[mid] >= array[low])
low = mid;// 注意,不能写成low=mid+1;
// 要是这种情况,array[mid]仍然在右边序列中
else if (array[mid] <= array[up])
up = mid;
}
return array[mid];
}
private int MinInOrder(int[] array) {
// TODO Auto-generated method stub
int min = array[0];
for (int i = 1; i < array.length; i++) {
if (array[i] < min) {
min = array[i];
}
}
return min;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
9、斐波那契数列
题目描述
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项。
思路1:递归,简洁但效率不高
public int Fibonacci(int n) {
if(n<=0)
return 0;
if(n==1)
return 1;
return Fibonacci(n-1) + Fibonacci(n-2);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
思路2:循环,O(N)
public class Solution {
public int Fibonacci(int n) {
int preNum=1;
int prePreNum=0;
int result=0;
if(n==0)
return 0;
if(n==1)
return 1;
for(int i=2;i<=n;i++){
result=preNum+prePreNum;
prePreNum=preNum;
preNum=result;
}
return result;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
扩展1:跳台阶
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
思路:斐波拉契数序列,初始条件n=1:只能一种方法,n=2:两种
对于第n个台阶来说,只能从n-1或者n-2的台阶跳上来,所以
F(n) = F(n-1) + F(n-2)
扩展2:变态跳台阶
题目描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
思路:
因为n级台阶,第一步有n种跳法:跳1级、跳2级、到跳n级
跳1级,剩下n-1级,则剩下跳法是f(n-1)
跳2级,剩下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的(n-1)次幂
public class Solution {
public int JumpFloorII(int target) {
if(target<=0)
return 0;
return 1<<(target-1);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
10、二进制中1的个数
题目描述
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
最差的解法:因为要考虑负数的问题,若是负数,因为要保证一直输负数,多以第一位一直为1,最后会变成0XFFFFFFFF,造成死循环
public class Solution {
public int NumberOf1(int n) {
int count= 0;
int flag = 1;
while (n!= 0){
if ((n & 1) != 0){
count++;
}
n = n>>1;
}
return count;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
改进的解法:
public class Solution {
public int NumberOf1(int n) {
int count= 0;
int flag = 1;
while (flag != 0){
if ((n & flag) != 0){
count++;
}
flag = flag << 1;
}
return count;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
最精妙的解法:
public class Solution {
public int NumberOf1(int n) {
int count = 0;
while(n!= 0){
count++;
n = n & (n - 1);//每一次将最后一个1变成0
}
return count;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
11、数值的整数次方
题目描述
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
思路1:本题主要考虑边界问题,全面不够高效的解法,注意:由于计算机表示小数(包括float和double型小数)都有误差,我们不能直接用==判断两个小数是否相等,如果两个小数的差的绝对值很小,比如小于0.0000001,就可以认为他们相等
public class Solution {
public double Power(double base, int exponent) {
double res = 0.0;
if (equal(base, 0.0) && exponent < 0) {
throw new RuntimeException("0的负数次幂没有意义");
}
// 这里定义0的0次方为1
if (exponent == 0) {
return 1.0;
}
if (exponent < 0) {
res = powerWithExponent(1.0/base, -exponent);
} else {
res = powerWithExponent(base, exponent);
}
return res;
}
private double powerWithExponent(double base, int exponent) {
double res = 1.0;
for (int i = 1; i <= exponent; i++) {
res = res * base;
}
return res;
}
// 判断double类型的数据
private boolean equal(double num1, double num2) {
if (Math.abs(num1 - num2) < 0.0000001) {
return true;
}
return false;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
思路2:n为偶数时:a^n=a^n/2 * a^n/2;
n为奇数,a^n=(a^(n-1)/2)* (a^(n-1/2))* a
所以对乘法处进行优化,如果是32次方,等于16次方*16次方
/*
** 对乘法进行优化的部分
*/
private double powerWithExponent(double base, int exponent) {
if(exponent==0){
return 1;
}
if(exponent==1){
return base;
}
double result = powerWithExponent(base,exponent>>1);//每次除以2
result*=result;//最后相乘,如果是奇数,还要乘一个
//如果是奇数次方的情况,最终除2余1要与base相乘
if((exponent & 0x1)==1){
result *= base;
}
return result;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
12、打印1到最大的n位数
题目描述:如n=3,则从1打印到999
public class Solution {
// ====================方法一====================
public static void Print1ToMaxOfNDigits(int n) {
if (n <= 0)
return;
char[] number = new char[n];
//每一个字符设为0
for (int i = 0; i < n; i++) {
number[i] = '0';
}
while (!Increment(number)) {//如果加法溢出,则退出,否则打印数字
PrintNumber(number);
}
}
// 字符串number表示一个数字,在 number上增加1
// 如果做加法溢出,则返回true;否则为false
public static boolean Increment(char[] number) {
boolean isOverflow = false;//溢出标志
int nTakeOver = 0;//进位
int nLength = number.length;
for (int i = nLength - 1; i >= 0; i--) {//从后向前,最后一位数字加1
int nSum = number[i] - '0' + nTakeOver;
if (i == nLength - 1)
nSum++;
if (nSum >= 10) {
if (i == 0)
isOverflow = true;
else {
nSum -= 10;
nTakeOver = 1;
number[i] = (char) ('0' + nSum);
}
} else {
number[i] = (char) ('0' + nSum);
break;
}
}
return isOverflow;
}
// 字符串number表示一个数字,数字有若干个0开头
// 打印出这个数字,并忽略开头的0
public static void PrintNumber(char[] number) {
boolean isBeginning0 = true;
int nLength = number.length;
//标志位的思想,从第一位不为0的数字开始打印,如000123,打印123
for (int i = 0; i < nLength; ++i) {
if (isBeginning0 && number[i] != '0')
isBeginning0 = false;
if (!isBeginning0) {
System.out.print(number[i]);
}
}
System.out.println();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
思路2:用递归,代码简洁,思路不好想,每一位都是从0到9的全排列
public class Solution {
// // ====================方法二:递归====================
public static void Print1ToMaxOfNDigits(int n) {
if (n <= 0)
return;
char[] number = new char[n];
for (int i = 0; i < 10; ++i) {
number[0] = (char) (i + '0');
Print1ToMaxOfNDigitsRecursively(number, n, 0);
}
}
public static void Print1ToMaxOfNDigitsRecursively(char[] number, int length,int index) {
if (index == length - 1) {
PrintNumber(number);
return;
}
for (int i = 0; i < 10; ++i) {
number[index + 1] = (char) (i + '0');
Print1ToMaxOfNDigitsRecursively(number, length, index + 1);
}
}
// 字符串number表示一个数字,数字有若干个0开头
// 打印出这个数字,并忽略开头的0
public static void PrintNumber(char[] number) {
boolean isBeginning0 = true;
int nLength = number.length;
// 标志位的思想,从第一位不为0的数字开始打印,如000123,打印123
for (int i = 0; i < nLength; ++i) {
if (isBeginning0 && number[i] != '0')
isBeginning0 = false;
if (!isBeginning0) {
System.out.print(number[i]);
}
}
System.out.println();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
13、在O(1)时间删除链表结点
给定单向链表头指针和一个节点指针,在O(1)时间删除链表结点
/*
对于删除节点,我们普通的思路就是让该结点的前一个节点指向改节点的下一个节点
*/
public void delete(Node head, Node toDelete){
if(toDelete == null){
return ;
}
if(toDelete.next != null){//删除的节点不是尾节点
toDelete.val = toDelete.next.val;
toDelete.next = toDelete.next.next;
}else if(head == toDelete){//链表只有一个节点,删除头结点也是尾节点
head = null;
}else{ //删除的节点是尾节点的情况
Node node = head;
while(node.next != toDelete){//找到倒数第二个节点
node = node.next;
}
node.next = null;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
14、调整数组顺序使奇数位于偶数前面
题目描述
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
public class Solution {
public void reOrderArray(int [] array) {
//注释的部分使用快速排序的算法,很明显快速排序是不稳定的,这里需要用归并排序
/*
if(array.length == 0){
return;
}
int high = array.length - 1;
int low = 0;
while(low < high){
while(low < high && array[low] % 2 == 1){
low ++;
}
while(low < high && array[high] % 2 == 0){
high --;
}
int temp = array[low];
array[low] = array[high];
array[high] = temp;
}*/
//用用归并排序的思想,因为归并排序是稳定的
int length = array.length;
if(length == 0){
return;
}
int[] des = new int[length];
MergeMethod(array, des, 0,length - 1);
}
public void MergeMethod(int[] array, int [] des, int start, int end){
if(start < end){
int mid = (start + end) / 2;
MergeMethod(array, des, start, mid);
MergeMethod(array, des, mid + 1, end);
Merge(array, des, start, mid, end);
}
}
public void Merge(int[] array, int[] des, int start, int mid, int end){
int i = start;
int j = mid + 1;
int k = start;
while(i <= mid && array[i] % 2 == 1){
des[k++] = array[i++];
}
while(j <= end && array[j] % 2 == 1){
des[k++] = array[j++];
}
while(i <= mid){
des[k++] = array[i++];
}
while(j <= end){
des[k++] = array[j++];
}
for(int m = start; m <= end; m++){
array[m] = des[m];
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
15、链表中倒数第k个结点
题目描述
输入一个链表,输出该链表中倒数第k个结点。
思路:两个指针,先让第一个指针和第二个指针都指向头结点,然后再让第一个指正走(k-1)步,到达第k个节点。然后两个指针同时往后移动,当第一个结点到达末尾的时候,第二个结点所在位置就是倒数第k个节点了
/**
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode FindKthToTail(ListNode head,int k) {
if(head==null||k<=0){
return null;
}
ListNode pre=head;
ListNode last=head;
for(int i=1;i<k;i++){
if(pre.next!=null){
pre=pre.next;
}else{
return null;
}
}
while(pre.next!=null){
pre = pre.next;
last=last.next;
}
return last;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
16、反转链表
题目描述
输入一个链表,反转链表后,输出链表的所有元素。
/*
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)
return null;
if (head.next == null)
return head;
ListNode pPre = null;
ListNode p = head;
ListNode pNext = head.next;
ListNode newHead = null;
while (p != null) {
pNext = p.next;//一定要记录下来后面的节点
if (pNext == null)
newHead = p;
p.next = pPre;//这里的方向已经转变
pPre = p;
p = pNext;//将保存的后面的节点作为下一次循环的p
}
return newHead;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
17、合并两个排序的链表
题目描述
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
/**
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode Merge(ListNode list1, ListNode list2) {
if(list1==null)
return list2;
else if(list2==null)
return list1;
ListNode mergeHead=null;
if(list1.val<list2.val){
mergeHead=list1;
mergeHead.next=Merge(list1.next, list2);
}
else{
mergeHead=list2;
mergeHead.next=Merge(list1, list2.next);
}
return mergeHead;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
非递归方法:
public class Solution {
public ListNode Merge(ListNode list1,ListNode list2) {
if(list1==null&&list2==null)
return null;
if(list1==null&&list2!=null)
return list2;
if(list1!=null&&list2==null)
return list1;
ListNode head = null;
if(list1.val<list2.val){
head = list1;
list1 = list1.next;
}
else{
head = list2;
list2 = list2.next;
}
ListNode cur = head;
cur.next=null;
while(list1!=null&&list2!=null){
if(list1.val<list2.val){
cur.next = list1;
list1 = list1.next;
}
else{
cur.next = list2;
list2 = list2.next;
}
cur = cur.next;
cur.next = null;
}
if(list1==null&&list2!=null){
cur.next =list2;
}else if(list2==null&&list1!=null){
cur.next = list1;
}
return head;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
18、树的子结构
题目描述
输入两颗二叉树A,B,判断B是不是A的子结构。
思路:首先遍历A树,找到A的根节点和B的根节点相同的点,找到之后再遍历A和B的各个子节点是否相同
/**
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(root2==null) return false;
if(root1==null && root2!=null) return false;
boolean flag = false;
if(root1.val==root2.val){
flag = isSubTree(root1,root2);
}
if(!flag){
flag = HasSubtree(root1.left, root2);
}
if(!flag){
flag = HasSubtree(root1.right, root2);
}
return flag;
}
private boolean isSubTree(TreeNode root1, TreeNode root2) {
if(root2==null) return true;
if(root1==null && root2!=null) return false;
if(root1.val==root2.val){
return isSubTree(root1.left, root2.left) && isSubTree(root1.right, root2.right);
}
return false;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
19、二叉树的镜像
题目描述
操作给定的二叉树,将其变换为源二叉树的镜像。
二叉树的镜像定义:源二叉树
8
/ \
6 10
/ \ / \
5 7 9 11
镜像二叉树
8
/ \
10 6
/ \ / \
11 9 7 5
思路1:用栈结构(改成队列结构也可以),将节点依次入栈,每个入栈的节点都镜像他的子节点
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
import java.util.Stack;
public class Solution {
public void Mirror(TreeNode root) {
if(root == null){
return;
}
Stack<TreeNode> stack = new Stack<TreeNode>();
stack.push(root);
while(!stack.isEmpty()){
TreeNode node = stack.pop();
if(node.left != null||node.right != null){
TreeNode temp = node.left;
node.left = node.right;
node.right = temp;
}
if(node.left!=null){
stack.push(node.left);
}
if(node.right!=null){
stack.push(node.right);
}
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
思路2:前序遍历的递归
public class Solution {
public void Mirror(TreeNode root) {
if(root == null) return;
if(root.left != null || root.right != null)
{
//创建临时节点,交换左右节点
TreeNode tempNode = null;
tempNode = root.left;
root.left = root.right;
root.right = tempNode;
Mirror(root.left);
Mirror(root.right);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
20、顺时针打印矩阵
题目描述
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> printMatrix(int [][] matrix) {
ArrayList<Integer> list = new ArrayList<Integer>();
int rows = matrix.length;
int columns = matrix[0].length;
if(matrix == null || columns <= 0 || rows <= 0){
return null;
}
int start = 0;
while(columns > start *2 && rows > start * 2){
print1Circle(list,matrix,columns,rows,start);
start++;
}
return list;
}
public void print1Circle(ArrayList<Integer> list, int[][] matrix,int columns, int rows, int start) {
int endX = columns - 1 - start;
int endY = rows - 1 - start;
//从左往右打印一行
for (int i = start; i <= endX; i++) {
list.add(matrix[start][i]);
}
//从上往下打印一列,至少有两行
if (start < endY){
for (int i = start+1; i <= endY; i++) {
list.add(matrix[i][endX]);
}
}
//从右往左打印一行,至少有两行两列
if (start < endY && start < endX){
for (int i = endX - 1; i >= start; i--) {
list.add(matrix[endY][i]);
}
}
//从下往上打印一列,至少有三行两列
if (start < endY -1 && start < endX){
for (int i = endY - 1; i >= start + 1; i--) {
list.add(matrix[i][start]);
}
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
21、包含min函数的栈
题目描述
定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数。
import java.util.Stack;
/*思路:用一个栈data保存数据,用另外一个辅助栈min保存依次入栈最小的数
比如,data中依次入栈,5, 4, 3, 8, 10, 11, 12, 1
则min依次入栈,5, 4, 3,3,3, 3, 3, 1
每次入栈的时候,如果入栈的元素比min中的栈顶元素小或等于则入栈,否则不如栈。
*/
public class Solution {
Stack data=new Stack();
Stack min=new Stack();
public void push(int node) {
if(min.empty()){
min.push(node);
}else{
int top=(int)min.peek();
if(node<top){
min.push(node);
}else{
min.push(top);
}
}
data.push(node);
}
public void pop() {
if(!(data.empty())){
data.pop();
min.pop();
}
}
public int top() {
return (int)data.peek();
}
public int min() {
if(min.empty()){
return 0;
}
return (int)min.peek();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
22、栈的压入、弹出序列
题目描述
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。
【思路】借用一个辅助的栈,push序列依次入栈,每次都判断,栈顶元素和pop序列是否相等,相等则弹出栈,不相等,则push序列继续入栈,最后判断栈是否为空
举例:
入栈1,2,3,4,5
出栈4,5,3,2,1
首先1入辅助栈,此时栈顶1≠4,继续入栈2
此时栈顶2≠4,继续入栈3
此时栈顶3≠4,继续入栈4
此时栈顶4=4,出栈4,弹出序列向后一位,此时为5,,辅助栈里面是1,2,3
此时栈顶3≠5,继续入栈5
此时栈顶5=5,出栈5,弹出序列向后一位,此时为3,,辅助栈里面是1,2,3
….
依次执行,最后辅助栈为空。如果不为空说明弹出序列不是该栈的弹出顺序。
import java.util.ArrayList;
import java.util.Stack;
public class Solution {
public boolean IsPopOrder(int [] pushA,int [] popA) {
if(pushA.length == 0 || popA.length == 0)
return false;
Stack<Integer> s = new Stack<Integer>();
//用于标识弹出序列的位置
int popIndex = 0;
for(int i = 0; i< pushA.length;i++){
s.push(pushA[i]);
//如果栈不为空,且栈顶元素等于弹出序列
while(!s.empty() &&s.peek() == popA[popIndex]){
//出栈
s.pop();
//弹出序列向后一位
popIndex++;
}
}
return s.empty();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
23、从上往下打印二叉树——层次遍历
题目描述
从上往下打印出二叉树的每个节点,同层节点从左至右打印。
思路:一个队列容器,每次打印节点的时候把此节点的左右子节点加入进去
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<Integer> PrintFromTopToBottom(TreeNode root) {
ArrayList<Integer> list = new ArrayList<Integer>();
if(root==null){
return list;
}
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode treeNode = queue.poll();
if (treeNode.left != null) {
queue.offer(treeNode.left);
}
if (treeNode.right != null) {
queue.offer(treeNode.right);
}
list.add(treeNode.val);
}
return list;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
24、二叉搜索树的后序遍历序列
题目描述
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
public class Solution {
public static boolean VerifySquenceOfBST(int[] sequence) {
if(sequence.length ==0){
return false;
}
return VerifySquenceOfBST1(sequence,0,sequence.length-1);
}
public static boolean VerifySquenceOfBST1(int[] sequence,int start,int end) {
if(start > end)
return true;
int root=sequence[end];//后序遍历最后一个节点为根节点
//在二叉搜索树中左子树节点小于根节点
int i=0;
for(;i<end;i++){
if(sequence[i]>root){
break;
}
}
//在二叉搜索树中右子树节点大于根节点
int j=i;
for(;j<end;j++){
if(sequence[j]<root)
return false;
}
boolean left=true;
boolean right=true;
if(i>start){
left=VerifySquenceOfBST1(sequence,start,i-1);
}
if(i<sequence.length-1)
right=VerifySquenceOfBST1(sequence,i,end-1);
return (left&&right);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
25、二叉树中和为某一值的路径
题目描述
输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。
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;
}
}
*/
public class Solution {
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root, int target) {
ArrayList<ArrayList<Integer>> pathList=new ArrayList<ArrayList<Integer>>();
if(root==null)
return pathList;
Stack<Integer> stack=new Stack<Integer>();
FindPath(root,target,stack,pathList );
return pathList;
}
private void FindPath(TreeNode root, int target, Stack<Integer> path, ArrayList<ArrayList<Integer>> pathList) {
if(root==null)
return;
//如果是叶子节点,判断值是否是目标值
if(root.left==null&&root.right==null){
if(root.val==target){
ArrayList<Integer> list=new ArrayList<Integer>();
for(int i:path){
list.add(new Integer(i));
}
list.add(new Integer(root.val));
pathList.add(list);
}
}
else{//不是叶子节点就遍历其子节点
path.push(new Integer(root.val));
//是按照前序遍历的方式查找路径,如果向上退出到父节点时,要回到target值,而不是target-root.val
FindPath(root.left, target-root.val, path, pathList);
FindPath(root.right, target-root.val, path, pathList);
path.pop();
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
26、复杂链表的复制
题目描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点)。
方法一:用hashMap映射原链表,牺牲O(N)空间换来时间
/*
public class RandomListNode {
int label;
RandomListNode next = null;
RandomListNode random = null;
RandomListNode(int label) {
this.label = label;
}
}
*/
import java.util.HashMap;
public class Solution {
public RandomListNode Clone(RandomListNode pHead)
{
if(pHead == null) return null;
HashMap<RandomListNode, RandomListNode> map = new HashMap<RandomListNode, RandomListNode>();
RandomListNode newHead = new RandomListNode(pHead.label);//复制链表的头结点
RandomListNode pre = pHead, newPre = newHead;
map.put(pre, newPre);
//第一步,hashMap保存,原链表节点映射复制链表节点
while(pre.next != null){
newPre.next = new RandomListNode(pre.next.label);
pre = pre.next;
newPre = newPre.next;
map.put(pre, newPre);
}
//第二步:找到对应的random
pre = pHead;
newPre = newHead;
while(newPre != null){
newPre.random = map.get(pre.random);
pre = pre.next;
newPre = newPre.next;
}
return newHead;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
方法二:不借用辅助空间
/*
public class RandomListNode {
int label;
RandomListNode next = null;
RandomListNode random = null;
RandomListNode(int label) {
this.label = label;
}
}
*/
public class Solution {
public RandomListNode Clone(RandomListNode pHead){
if(pHead==null)
return null;
RandomListNode pCur = pHead;
//第一步:复制next 如原来是A->B->C 变成A->A'->B->B'->C->C'
while(pCur!=null){
RandomListNode node = new RandomListNode(pCur.label);
node.next = pCur.next;
pCur.next = node;
pCur = node.next;
}
//第二步
pCur = pHead;
//复制random pCur是原来链表的结点 pCur.next是复制pCur的结点
while(pCur!=null){
if(pCur.random!=null)
pCur.next.random = pCur.random.next;
pCur = pCur.next.next;
}
//第三步
RandomListNode head = pHead.next;//复制链表的头结点
RandomListNode cur = head;//偶数位置为复制链表
pCur = pHead;//奇数位置为原链表
//拆分链表
while(pCur!=null){
pCur.next = pCur.next.next;
if(cur.next!=null)//注意最后一个复制节点的时候就没有next的next
cur.next = cur.next.next;
cur = cur.next;
pCur = pCur.next;
}
return head;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
27、二叉搜索树与双向链表
题目描述
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
方法一:递归中序遍历
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
//直接用中序遍历
public class Solution {
TreeNode head = null;
TreeNode realHead = null;//双向链表的头结点
public TreeNode Convert(TreeNode pRootOfTree) {
if(pRootOfTree==null) return null;
Convert(pRootOfTree.left);
if (head == null) {
head = pRootOfTree;
realHead = pRootOfTree;
} else {
head.right = pRootOfTree;
pRootOfTree.left = head;
head = pRootOfTree;
}
Convert(pRootOfTree.right);
return realHead;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
方法二:
/** 非递归 */
import java.util.Stack;
public class Solution {
public TreeNode Convert(TreeNode pRootOfTree) {
if(pRootOfTree == null) return pRootOfTree;
TreeNode list = null;
Stack<TreeNode> s = new Stack<>();
while(pRootOfTree != null || !s.isEmpty()){
if(pRootOfTree != null) {
s.push(pRootOfTree);
pRootOfTree = pRootOfTree.right;
} else {
pRootOfTree = s.pop();
if(list == null)
list = pRootOfTree;
else {
list.left = pRootOfTree;
pRootOfTree.right = list;
list = pRootOfTree;
}
pRootOfTree = pRootOfTree.left;
}
}
return list;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
28、字符串的排列
题目描述
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。 结果请按字母顺序输出。
扩展:求字符串的全组合
如:abc,全组合为:a,b,c,ab,ac,bc,abc
public final class PermutationCombinationHolder {
/** 1、数组元素的全组合 */
public static void combination(char[] chars) {
char[] subchars = new char[chars.length]; //存储子组合数据的数组
//全组合问题就是所有元素(记为n)中选1个元素的组合, 加上选2个元素的组合...加上选n个元素的组合的和
for (int i = 0; i < chars.length; ++i) {
final int m = i + 1;
combination(chars, chars.length, m, subchars, m);
}
}
/**
* n个元素选m个元素的组合问题的实现. 原理如下:
* 从后往前选取, 选定位置i后, 再在前i-1个里面选取m-1个.
* 如: 1, 2, 3, 4, 5 中选取3个元素.
* 1) 选取5后, 再在前4个里面选取2个, 而前4个里面选取2个又是一个子问题, 递归即可;
* 2) 如果不包含5, 直接选定4, 那么再在前3个里面选取2个, 而前三个里面选取2个又是一个子问题, 递归即可;
* 3) 如果也不包含4, 直接选取3, 那么再在前2个里面选取2个, 刚好只有两个.
* 纵向看, 1与2与3刚好是一个for循环, 初值为5, 终值为m.
* 横向看, 该问题为一个前i-1个中选m-1的递归.
*/
public static void combination(char[] chars, int n, int m, char[] subchars, int subn) {
if (m == 0) { //出口
for (int i = 0; i < subn; ++i) {
System.out.print(subchars[i]);
}
System.out.println();
} else {
for (int i = n; i >= m; --i) { // 从后往前依次选定一个
subchars[m - 1] = chars[i - 1]; // 选定一个后
combination(chars, i - 1, m - 1, subchars, subn); // 从前i-1个里面选取m-1个进行递归
}
}
}
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
/** 2、数组元素的全排列 */
public static void permutation(char[] chars) {
permutation(chars, 0, chars.length - 1);
}
/** 数组中从索引begin到索引end之间的子数组参与到全排列 */
public static void permutation(char[] chars, int begin, int end) {
if (begin == end) { //只剩最后一个字符时为出口
for (int i = 0; i < chars.length; ++i) {
System.out.print(chars[i]);
}
System.out.println();
} else {
for (int i = begin; i <= end; ++i) { //每个字符依次固定到数组或子数组的第一个
if (canSwap(chars, begin, i)) { //去重
swap(chars, begin, i); //交换
permutation(chars, begin + 1, end); //递归求子数组的全排列
swap(chars, begin, i); //还原
}
}
}
}
public static void swap(char[] chars, int from, int to) {
char temp = chars[from];
chars[from] = chars[to];
chars[to] = temp;
}
//判断去重
public static boolean canSwap(char[] chars, int begin, int end) {
for (int i = begin; i < end; ++i) {
if (chars[i] == chars[end]) {
return false;
}
}
return true;
}
public static void main(String[] args) {
final char[] chars = new char[] {'a', 'b', 'c'};
permutation(chars);
System.out.println("===================");
combination(chars);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
方法二:DFS
import java.util.*;
public class Solution {
private char [] seqs;
private Integer [] book;
//用于结果去重
private HashSet<String> result = new HashSet<String>();
/**
* 输入一个字符串,按字典序打印出该字符串中字符的所有排列。
* 例如输入字符串abc,则打印出由字符a,b,c
* 所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。 结果请按字母顺序输出。
输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。\
* @param str
* @return
*/
public ArrayList<String> Permutation(String str) {
ArrayList<String> arrange = new ArrayList<String>();
if(str == null || str.isEmpty()) return arrange;
char[] strs = str.toCharArray();
seqs = new char[strs.length];
book = new Integer[strs.length];
for (int i = 0; i < book.length; i++) {
book[i] = 0;
}
dfs(strs, 0);
arrange.addAll(result);
Collections.sort(arrange);
return arrange;
}
/**
* 深度遍历法
*/
private void dfs(char[] arrs, int step){
//走完所有可能 记录排列
if(arrs.length == step){
String str = "";
for (int i = 0; i < seqs.length; i++) {
str += seqs[i];
}
result.add(str);
return; //返回上一步
}
//遍历整个序列,尝试每一种可能
for (int i = 0; i < arrs.length; i++) {
//是否走过
if(book[i] == 0){
seqs[step] = arrs[i];
book[i] = 1;
//下一步
dfs(arrs, step + 1);
//走完最后一步 后退一步
book[i] = 0;
}
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
方法三:字典序算法
import java.util.*;
//步骤如下:
//1.从这个序列中从右至左找第一个左邻小于右邻的字符,记录下标为index1 ,如果找不到,说明求解完成。
//2.从这个序列中从右至左找第一个大于str[index1]的字符,记录下标为index2
//3.交换index1和index2的字符,对index1+1后的所有字符进行升序排序,此时得到的即为str按字典序的下一个排列
//4. 重复1~3的步骤,直到全部找完
public class Solution {
public ArrayList<String> Permutation(String str) {
ArrayList<String> res = new ArrayList<>();
if (str != null && str.length() > 0) {
char[] seq = str.toCharArray();
Arrays.sort(seq); //排列
res.add(String.valueOf(seq)); //先输出一个解
int len = seq.length;
while (true) {
int p = len - 1, q;
//从后向前找一个seq[p - 1] < seq[p]
while (p >= 1 && seq[p - 1] >= seq[p]) --p;
if (p == 0) break; //已经是“最小”的排列,退出
//从p向后找最后一个比seq[p]大的数
q = p; --p;
while (q < len && seq[q] > seq[p]) q++;
--q;
//交换这两个位置上的值
swap(seq, q, p);
//将p之后的序列倒序排列
reverse(seq, p + 1);
res.add(String.valueOf(seq));
}
}
return res;
}
public static void reverse(char[] seq, int start) {
int len;
if(seq == null || (len = seq.length) <= start)
return;
for (int i = 0; i < ((len - start) >> 1); i++) {
int p = start + i, q = len - 1 - i;
if (p != q)
swap(seq, p, q);
}
}
public static void swap(char[] cs, int i, int j) {
char temp = cs[i];
cs[i] = cs[j];
cs[j] = temp;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
29、数组中出现次数超过一半的数字
题目描述
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
思路:O(n)的思想是,定义两个变量result 和count,每次循环时,如果array[i]的值等于result ,则count自增,如不等并且count>0,则count自减,count==0,重新对temp赋值为当前array[i],count赋值为1。
如存在大于一半的数,直接返回result 就是了,但测试数据中有不存在的情况,所以最后又来了一遍校验,检查当前result 值是否出现过一半以上。
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
if(array==null || array.length <= 0){
return 0;
}
int result = array[0];
int count = 1;
for (int i = 1; i < array.length; i++) {
if (array[i] == result) {
count++;
} else if (count > 0 ) {
count--;
} else if(count == 0){
result = array[i];
count = 1;
}
}
//验证
count=0;
for(int i=0;i<array.length;i++){
if(array[i]==result)
count++;
}
return count > array.length/2 ? result : 0;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
30、最小的K个数
题目描述
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
思路1:
经典常用的算法,快速排序的精髓利用快速排序划分的思想,每一次划分就会有一个数字位于以数组从小到达排列的的最终位置index;
位于index左边的数字都小于index对应的值,右边都大于index指向的值;
所以,当index > k-1时,表示k个最小数字一定在index的左边,此时,只需要对index的左边进行划分即可;
当index < k - 1时,说明index及index左边数字还没能满足k个数字,需要继续对k右边进行划分;
//这种方式的优点是时间复杂度较小为O(n),缺点就是需要修改输入数组。
import java.util.ArrayList;
public class Solution {
public ArrayList GetLeastNumbers_Solution(int [] input, int k) {
ArrayList aList = new ArrayList();
if(input.length == 0 || k > input.length || k <= 0)
return aList;
int low = 0;
int high = input.length-1;
int index = Partition(input,k,low,high);
while(index != k-1){
if (index > k-1) {
high = index-1;
index = Partition(input,k,low,high);
}else{
low = index+1;
index = Partition(input,k,low,high);
}
}
for (int i = 0; i < k; i++)
aList.add(input[i]);
return aList;
}
//快速排序的分段,小于某个数的放在左边,大于某个数的移到右边
public int Partition(int[] input,int k,int low,int high){
int pivotkey = input[k-1];
swap(input,k-1,low);
while(low < high){
while(low < high && input[high] >= pivotkey)
high--;
swap(input,low,high);
while(low < high && input[low] <= pivotkey)
low++;
swap(input,low,high);
}
return low;
}
private void swap(int[] input, int low, int high) {
int temp = input[high];
input[high] = input[low];
input[low] = temp;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
思路2
可以先创建一个大小为k的数据容器来存储最小的k个数字,从输入的n个整数中一个一个读入放入该容器中,如果容器中的数字少于k个,按题目要求直接返回空;
如果容器中已有k个数字,而数组中还有值未加入,此时就不能直接插入了,而需要替换容器中的值。按以下步骤进行插入:
- 1、先找到容器中的最大值;
- 2、将待查入值和最大值比较,如果待查入值大于容器中的最大值,则直接舍弃这个待查入值即可;如果待查入值小于容器中的最小值,则用这个待查入值替换掉容器中的最大值;
- 3、重复上述步骤,容器中最后就是整个数组的最小k个数字。
-对于这个容器的实现,我们可以使用最大堆的数据结构,最大堆中,根节点的值大于它的子树中的任意节点值。Java中的TreeSet类实现了红黑树的功能,它底层是通过TreeMap实现的,TreeSet中的数据会按照插入数据自动升序排列(按自然顺序)。因此我们直接将数据依次放入到TreeSet中,数组就会自动排序。
public static ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {
ArrayList<Integer> leastNum = new ArrayList<Integer>();
if (input == null || input.length < 1 || k < 1 || k > input.length)
return leastNum;
TreeSet<Integer> kSet = new TreeSet<Integer>();
for (int i = 0; i < input.length; i++) {
if (kSet.size() < k) {
kSet.add(input[i]);
} else {
if (input[i] < kSet.last()) {
kSet.remove(kSet.last());
kSet.add(input[i]);
}
}
}
Iterator<Integer> it = kSet.iterator();
while (it.hasNext()) {
leastNum.add(it.next());
}
return leastNum;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
31、连续子数组的最大和
题目描述:
输入一个整型数组,数组里有正数和负数,数组中一个或者多个连续的数字组成一个子数组,求所有子数组的最大值,要求时间复杂度为O(N)
思路1:
/*
算法时间复杂度O(n)
用total记录累计值,maxSum记录和最大
基于思想:对于一个数A,若是A的左边累计数非负,那么加上A能使得值不小于A,认为累计值对
整体和是有贡献的。如果前几项累计值负数,则认为有害于总和,total记录当前值。
此时 若和大于maxSum 则用maxSum记录下来
*/
public class Solution {
public int FindGreatestSumOfSubArray(int[] array) {
if(array.length==0)
return 0;
else{
int total=array[0],maxSum=array[0];
for(int i=1;i<array.length;i++){
if(total>=0)
total+=array[i];
else
total=array[i];
if(total>maxSum)
maxSum=total;
}
return maxSum;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
思路2:动态规划(相当重点)
public class Solution {
public int FindGreatestSumOfSubArray(int[] array) {
//动态规划,主要是找到状态转移方程
//设f(j)是从s[0]到s[j]最大和
//f(j) = max(s[j], f[j-1]+s[j])
if(array.length == 0)
return 0;
int result = Integer_MinValue;
int sum = 0;
for(int i = 0; i < array.length; ++i)
{
sum = max(array[i], sum + array[i]);
result = max(result, sum);
}
return result;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
32、整数中1出现的次数(从1到n整数中1出现的次数)
题目描述
例如n=13的整数中1出现的次数,1~13中包含1的数字有1、10、11、12、13因此共出现6次
思路:
一、1的数目
编程之美上给出的规律:
1、 如果第i位(自右至左,从1开始标号)上的数字为0,则第i位可能出现1的次数由更高位决定(若没有高位,视高位为0),等于更高位数字 * 当前位数的权重10^(i-1)。
2、如果第i位上的数字为1,则第i位上可能出现1的次数不仅受更高位影响,还受低位影响(若没有低位,视低位为0),等于更高位数字 * 当前位数的权重10^(i-1)+(低位数字+1)。
3、如果第i位上的数字大于1,则第i位上可能出现1的次数仅由更高位决定(若没有高位,视高位为0),等于(更高位数字+1) * 当前位数的权重10^(i-1)。
二、X的数目
这里的 X∈[1,9] ,因为 X=0 不符合下列规律,需要单独计算。
首先要知道以下的规律:
从 1 至 10,在它们的个位数中,任意的 X 都出现了 1 次。
从 1 至 100,在它们的十位数中,任意的 X 都出现了 10 次。
从 1 至 1000,在它们的百位数中,任意的 X 都出现了 100 次。
依此类推,从 1 至 10^ i ,在它们的左数第二位(右数第 i 位)中,任意的 X 都出现了 10^(i-1) 次。
这个规律很容易验证,这里不再多做说明。
接下来以 n=2593,X=5 为例来解释如何得到数学公式。从 1 至 2593 中,数字 5 总计出现了 813 次,其中有 259 次出现在个位,260 次出现在十位,294 次出现在百位,0 次出现在千位。
现在依次分析这些数据,
首先是个位。从 1 至 2590 中,包含了 259 个 10,因此任意的 X 都出现了 259 次。最后剩余的三个数 2591, 2592 和 2593,因为它们最大的个位数字 3 < X,因此不会包含任何 5。(也可以这么看,3 < X,则个位上可能出现的X的次数仅由更高位决定,等于更高位数字(259)*10^(1-1)=259)。
然后是十位。从 1 至 2500 中,包含了 25 个 100,因此任意的 X 都出现了 25×10=250 次。剩下的数字是从 2501 至 2593,它们最大的十位数字 9 > X,因此会包含全部 10 个 5。最后总计 250 + 10 = 260。(也可以这么看,9>X,则十位上可能出现的X的次数仅由更高位决定,等于更高位数字(25+1)*10^(2-1)=260)。
接下来是百位。从 1 至 2000 中,包含了 2 个 1000,因此任意的 X 都出现了 2×100=200 次。剩下的数字是从 2001 至 2593,它们最大的百位数字 5 == X,这时情况就略微复杂,它们的百位肯定是包含 5 的,但不会包含全部 100 个。如果把百位是 5 的数字列出来,是从 2500 至 2593,数字的个数与百位和十位数字相关,是 93+1 = 94。最后总计 200 + 94 = 294。(也可以这么看,5==X,则百位上可能出现X的次数不仅受更高位影响,还受低位影响,等于更高位数字(2)*10^(3-1)+(93+1)=294)。
最后是千位。现在已经没有更高位,因此直接看最大的千位数字2< X,所以不会包含任何 5。(也可以这么看,2< X,则千位上可能出现的X的次数仅由更高位决定,等于更高位数字(0)*10^(4-1)=0)。
到此为止,已经计算出全部数字 5 的出现次数。
总结一下以上的算法,可以看到,当计算右数第 i 位包含的 X 的个数时:
- 1、取第 i 位左边的数字(高位),乘以 10 ^(i−1) ,得到基础值 a 。
- 2、取第 i 位数字,计算修正值:
- 1、如果大于 X,则结果为 a+ 10 ^(i−1) 。
- 2、如果小于 X,则结果为 a 。
- 3、如果等 X,则取第 i 位右边(低位)数字,设为 b ,最后结果为 a+b+1 。
相应的代码非常简单,效率也非常高,时间复杂度只有 O( log 10 n) 。
public class Solution {
public int NumberOf1Between1AndN_Solution(int n) {
//当x=1时
return NumberOfXBetween1AndN_Solution1(n,1);
}
/**
* @param n
* @param x [1,9]
* @return (从1到n整数中x出现的次数)
*/
public int NumberOfXBetween1AndN_Solution1(int n,int x) {
if(n<0||x<1||x>9)
return 0;
int high,low,curr,tmp,i = 1;
high = n;
int total = 0;
while(high!=0){
high = n/(int)Math.pow(10, i);// 获取第i位的高位
tmp = n%(int)Math.pow(10, i);
curr = tmp/(int)Math.pow(10, i-1);// 获取第i位
low = tmp%(int)Math.pow(10, i-1);// 获取第i位的低位
if(curr==x){
total+= high*(int)Math.pow(10, i-1)+low+1;
}else if(curr<x){
total+=high*(int)Math.pow(10, i-1);
}else{
total+=(high+1)*(int)Math.pow(10, i-1);
}
i++;
}
return total;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
33、把数组排成最小的数
题目描述:
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
package cn.zhuang.offer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
public class Main {
/* 解题思路:
* 考虑到大数问题,先将整型数组转换成String数组,然后将String数组排序,最后将排好序的字符串数组拼接出来。关键就是制定排序规则。
* 排序规则如下:
* 若ab > ba 则 a > b,
* 若ab < ba 则 a < b,
* 若ab = ba 则 a = b;
* 解释说明:
* 比如 "3" < "31"但是 "331" > "313",所以要将二者拼接起来进行比较
*/
public static String PrintMinNumber(int [] numbers) {
if(numbers == null || numbers.length == 0) return "";
int len = numbers.length;
String[] str = new String[len];
StringBuilder sb = new StringBuilder();
for(int i = 0; i < len; i++){
str[i] = String.valueOf(numbers[i]);
}
//此处较为重要,新的排序规则,如若取最大值,~c1.compareTo(c2)
Arrays.sort(str,new Comparator<String>(){
@Override
public int compare(String s1, String s2) {
String c1 = s1 + s2;
String c2 = s2 + s1;
return c1.compareTo(c2);
}
});
for(int i = 0; i < len; i++){
sb.append(str[i]);
}
return sb.toString();
}
public static void main(String[] args){
int[] a = {3,32,421};
System.out.println(PrintMinNumber(a));
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
java中的compareto方法,返回参与比较的前后两个字符串的asc码的差值,看下面一组代码
String a=”a”,b=”b”;
System.out.println(a.compareto.b);
则输出-1;
若a=”a”,b=”a”则输出0;
若a=”b”,b=”a”则输出1;
单个字符这样比较,若字符串比较长呢??
若a=”ab”,b=”b”,则输出-1;
若a=”abcdef”,b=”b”则输出-1;
也就是说,如果两个字符串首字母不同,则该方法返回首字母的asc码的差值;
如果首字母相同呢??
若a=”ab”,b=”a”,输出1;
若a=”abcdef”,b=”a”输出5;
若a=”abcdef”,b=”abc”输出3;
若a=”abcdef”,b=”ace”输出-1;
即参与比较的两个字符串如果首字符相同,则比较下一个字符,直到有不同的为止,返回该不同的字符的asc码差值,如果两个字符串不一样长,可以参与比较的字符又完全一样,则返回两个字符串的长度差值
34、丑数
题目描述:
把只包含因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含因子7。习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
思路1:最简单的方法就是先通过将一个数不断除以2,3,5来判定该数是不是丑数,而后在从1开始,依次往后判断每个数是不是丑数,并记下丑数的个数,这样当计算的个数为给定值时,便是需要求的第n个丑数,这种方法的时间复杂度为O(k),这里的k为第n个丑数的大小,比如第1500个丑数的大小为859963392,那么就需要判断859963392次,时间效率非常低。
public boolean IsUgly(int number)
{
while(number % 2 == 0)
number /= 2;
while(number % 3 == 0)
number /= 3;
while(number % 5 == 0)
number /= 5;
return (number == 1) ? true : false;
}
public int GetUglyNumber(int index)
{
if(index <= 0)
return 0;
int number = 0;
int uglyFound = 0;
while(uglyFound < index)
{
++number;
if(IsUgly(number))
{
++uglyFound;
}
}
return number;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
思路Better2:直观的优化措施就是看能不能将时间复杂度降低到O(n),即只在丑数上花时间,而不在非丑数上浪费时间。剑指offer上给的思路很好,用O(n)的辅助空间来得到O(n)的时间复杂度。其核心思想是:每一个丑数必然是由之前的某个丑数与2,3或5的乘积得到的,这样下一个丑数就用之前的丑数分别乘以2,3,5,找出这三这种最小的并且大于当前最大丑数的值,即为下一个要求的丑数。
import java.util.*;
public class Solution
{
public int GetUglyNumber_Solution(int n)
{
if(n==0)return 0;
ArrayList<Integer> res=new ArrayList<Integer>();
res.add(1);
int i2=0,i3=0,i5=0;
while(res.size()<n)
{
int m2=res.get(i2)*2;
int m3=res.get(i3)*3;
int m5=res.get(i5)*5;
int min=Math.min(m2,Math.min(m3,m5));
res.add(min);
if(min==m2)i2++;
if(min==m3)i3++;
if(min==m5)i5++;
}
return res.get(res.size()-1);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
35、第一个只出现一次的字符
描述:在一个字符串(1<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符的位置。若为空串,返回-1。位置索引从0开始,如“abaccdrff”,则输出“b”
第一种,数组方法:
public class Solution {
public int FirstNotRepeatingChar(String str) {
if (str.length() == 0) {
return -1;
}
char c = 'A';
if(str.charAt(0) >= 'a'){
c = 'a';
}
int[] counts = new int[26];
for (int i = 0; i < str.length(); i++) {
counts[str.charAt(i) - c]++;
}
for (int i = 0; i < str.length(); i++) {
if (counts[str.charAt(i) - c] == 1){
return i;
}
}
return -1;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
第二种,HashMap方法:
import java.util.LinkedHashMap;
// use linkedhashmap to keep the order
public class Solution {
public int FirstNotRepeatingChar(String str) {
LinkedHashMap <Character, Integer> map = new LinkedHashMap<Character, Integer>();
for(int i=0;i<str.length();i++){
if(map.containsKey(str.charAt(i))){
int time = map.get(str.charAt(i));
map.put(str.charAt(i), ++time);
}
else {
map.put(str.charAt(i), 1);
}
}
int pos = -1;
int i=0;
for(;i<str.length();i++){
char c = str.charAt(i);
if (map.get(c) == 1) {
return i;
}
}
return pos;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
36、数组中的逆序对
题目描述:
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
{7,5,6,4},则76,75,74,64,65,一共5个
思路:
public class Solution {
private int reversePair = 0;
public int InversePairs(int [] array) {
if(array==null)
return 0;
int len = array.length;
if(len==0)
return 0;
sort(array,0,len-1);
return reversePair;
}
private void sort(int[]arr,int start,int end){
if(start<end){
int mid = start+(end-start)/2;
sort(arr,start,mid);
sort(arr,mid+1,end);
merger(arr,start,mid,mid+1,end);
}
}
private void merger(int[] arr, int start1, int end1, int start2, int end2) {
int len= end2-start1+1;
int[] anx = new int[len];
int k = end2-start1+1;
int i = end1;
int j= end2;
while(i>=start1&j>=start2){
if(arr[i]>arr[j]){
anx[--k]=arr[i];
i--;
reversePair = reversePair+(j-start2+1);
}
else{
anx[--k]=arr[j];
j--;
}
}
for(;i>=start1;i--)
anx[--k]=arr[i];
for(;j>=start2;j--)
anx[--k]=arr[j];
for(int m=0;m<len;m++)
arr[start1++]=anx[m];
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
37、两个链表的第一个公共交点
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
if (pHead1 == null||pHead2 == null) {
return null;
}
int count1 = 0;
ListNode p1 = pHead1;
while (p1!=null){
p1 = p1.next;
count1++;
}
int count2 = 0;
ListNode p2 = pHead2;
while (p2!=null){
p2 = p2.next;
count2++;
}
int flag = count1 - count2;
if (flag > 0){
while (flag>0){
pHead1 = pHead1.next;
flag --;
}
while (pHead1!=pHead2){
pHead1 = pHead1.next;
pHead2 = pHead2.next;
}
return pHead1;
}
if (flag <= 0){
while (flag<0){
pHead2 = pHead2.next;
flag ++;
}
while (pHead1 != pHead2){
pHead2 = pHead2.next;
pHead1 = pHead1.next;
}
return pHead1;
}
return null;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
38、数字在排序数组中出现的次数
思路:重点是有序数组这个条件,采用二分查找法,分别找到第一个和最后一个,这样无论最好最坏复杂度都是O(lgN)
public class Solution {
public int GetNumberOfK(int [] array , int k) {
int num = 0;
if (array != null && array.length > 0) {
int firstKIndex = getFirstK(array, k, 0, array.length - 1);
int lastKIndex = getLastK(array, k, 0, array.length - 1);
if (firstKIndex > -1 && lastKIndex > -1)
num = lastKIndex - firstKIndex + 1;
}
return num;
}
/*
* 找到第一个出现的数字的下标
*/
public int getFirstK(int[] array, int k, int start, int end) {
if (start > end)
return -1;
int middleIndex = start + (end - start) / 2;
int middleData = array[middleIndex];
if (middleData == k) {
//判断是不是第一个K,前一个不等于K,就是第一个K
if (middleIndex > 0 && array[middleIndex - 1] != k || middleIndex == 0) {
return middleIndex;
} else
end = middleIndex - 1;
} else if (middleData > k) {
end = middleIndex - 1;
} else
start = middleIndex + 1;
return getFirstK(array, k, start, end);
}
/*
* 找到最后一个出现的数字的下标
*/
public int getLastK(int array[], int k, int start, int end) {
if (start > end) {
return -1;
}
int middleIndex = (start + end) / 2;
int middleData = array[middleIndex];
if (middleData == k) {
//判断是不是最后一个K,后一个不等于K,就是最后一个K
if (middleIndex < array.length - 1 && array[middleIndex + 1] != k || middleIndex == array.length - 1)
return middleIndex;
else
start = middleIndex + 1;
} else if (middleData < k) {
start = middleIndex + 1;
} else
end = middleIndex - 1;
return getLastK(array, k, start, end);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
39、二叉树的深度
题目描述:
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
思路:递归,下一层根节点到上一层根节点,深度加1,判断左子树和右子树的最大值,然后+1
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
};*/
public class Solution {
public int getHeight(TreeNode root) {
if (root == null)
return 0;
return max(getHeight(root.left), getHeight(root.right)) + 1;
}
private int max(int a, int b) {
return (a > b) ? a : b;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
扩展题:判断二叉树平衡
描述:如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。
第一种思路:简洁,但是效率不高,因为会重复遍历子节点
public class Solution {
public boolean IsBalanced(TreeNode root) {
if (root == null)
return true;
if (Math.abs(getHeight(root.left) - getHeight(root.right)) > 1)
return false;
return IsBalanced(root.left) && IsBalanced(root.right);
}
public int getHeight(TreeNode root) {
if (root == null)
return 0;
return max(getHeight(root.left), getHeight(root.right)) + 1;
}
private int max(int a, int b) {
return (a > b) ? a : b;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
第二种Better思路:从底向上判断,这样可以记录下一层的深度
public class Solution {
public boolean IsBalanced(TreeNode root) {
int depth = 0;
return IsBalanced(root, depth);
}
public boolean IsBalanced(TreeNode root, int depth) {
if (root ==