剑指offer编程题66道题 36-66
36.两个链表的第一个公共节点
题目描述
/*
public class ListNode {
int val;
ListNode next = null; ListNode(int val) {
this.val = val;
}
}*/
import java.util.*;
public class Solution {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
Stack<ListNode> s1=new Stack<ListNode>();
Stack<ListNode> s2=new Stack<ListNode>(); while(pHead1!=null){
s1.add(pHead1);
pHead1=pHead1.next;
} while(pHead2!=null){
s2.add(pHead2);
pHead2=pHead2.next;
} ListNode foundNode =null;
ListNode node =null;
while(!s1.isEmpty() && !s2.isEmpty()){
node=s1.pop();
if(node==s2.pop())
foundNode =node;
else
return foundNode;
}
return foundNode;
}
}
2.利用HashSet中元素不能重复的原理,首先遍历一个链表进行存放,然后遍历另外一个链表,找到第一个与Set中元素相同的节点。
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
ListNode current1 = pHead1;
ListNode current2 = pHead2; HashSet <ListNode> hashSet = new HashSet<ListNode>();
while (current1 != null) {
hashSet.add(current1);
current1 = current1.next;
}
while (current2 != null) {
if (hashSet.contains(current2))
return current2;
current2 = current2.next;
} return null; }
3.首先遍历两个链表得到它们的长度,就能知道哪个链表比较长,以及长的链表比短的链表多几个节点。第二次遍历的时候,在较长的链表上线走若干步,接着同时在两个链表上遍历,找到的第一个相同的节点就是它们的第一个公共节点。
链接:https://www.nowcoder.com/questionTerminal/6ab1d9a29e88450685099d45c9e31e46
来源:牛客网 public ListNode FindFirstCommonNodeII(ListNode pHead1, ListNode pHead2) {
ListNode current1 = pHead1;// 链表1
ListNode current2 = pHead2;// 链表2
if (pHead1 == null || pHead2 == null)
return null;
int length1 = getLength(current1);
int length2 = getLength(current2);
// 两连表的长度差
// 如果链表1的长度大于链表2的长度
if (length1 >= length2) {
int len = length1 - length2;
// 先遍历链表1,遍历的长度就是两链表的长度差
while (len > ) {
current1 = current1.next;
len--;
}
}
// 如果链表2的长度大于链表1的长度
else if (length1 < length2) {
int len = length2 - length1;
// 先遍历链表1,遍历的长度就是两链表的长度差
while (len > ) {
current2 = current2.next;
len--;
}
}
//开始齐头并进,直到找到第一个公共结点
while(current1!=current2){
current1=current1.next;
current2=current2.next;
}
return current1;
}
// 求指定链表的长度
public static int getLength(ListNode pHead) {
int length = ;
ListNode current = pHead;
while (current != null) {
length++;
current = current.next;
}
return length;
}
37.数字在排序数组中出现的次数
题目描述
public class Solution {
public int GetNumberOfK(int [] array , int k) {
int number = ;
int flag = ;
for(int i: array){
if(i == k){
number++;
flag = ;
}else if(i != k && flag == ){
return number;
}
}
return number;
}
}
二分法:
public class Solution {
public int GetNumberOfK(int [] array , int k) {
int length = array.length;
if(length == ){
return ;
}
int firstK = getFirstK(array, k, , length-);
int lastK = getLastK(array, k, , length-);
if(firstK != - && lastK != -){
return lastK - firstK + ;
}
return ;
}
//递归写法
private int getFirstK(int [] array , int k, int start, int end){
if(start > end){
return -;
}
int mid = (start + end) >> ;
if(array[mid] > k){
return getFirstK(array, k, start, mid-);
}else if (array[mid] < k){
return getFirstK(array, k, mid+, end);
}else if(mid- >= && array[mid-] == k){
return getFirstK(array, k, start, mid-);
}else{
return mid;
}
}
//循环写法
private int getLastK(int [] array , int k, int start, int end){
int length = array.length;
int mid = (start + end) >> ;
while(start <= end){
if(array[mid] > k){
end = mid-;
}else if(array[mid] < k){
start = mid+;
}else if(mid+ < length && array[mid+] == k){
start = mid+;
}else{
return mid;
}
mid = (start + end) >> ;
}
return -;
}
}
public class Solution {
public int GetNumberOfK(int [] array , int k) {
int first = firstGreatOrEqual(array, k);
int last = firstGreat(array, k);
return last - first;
} public int firstGreatOrEqual(int[] array,int key) {
int left = ;
int right = array.length - ; while (left <= right) {
int mid = (left + right) / ;
if (array[mid] >= key) {
right = mid - ;
}
else {
left = mid + ;
}
}
return left;
} public int firstGreat(int[] array, int key) {
int left = ;
int right = array.length - ; while (left <= right) {
int mid = (left + right) / ;
if (array[mid] > key) {
right = mid - ;
}
else {
left = mid + ;
}
}
return left;
} }
38.二叉树的深度
题目描述
/**
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 ;
int left = TreeDepth(root.left);
int right = TreeDepth(root.right); return (left>right)?(left+):(right+);
}
}
2.采用层次遍历的方式
先知道下一层树的个数,然后count++,相等时候,结束一层的层序遍历。
链接:https://www.nowcoder.com/questionTerminal/435fb86331474282a3499955f0a41e8b
来源:牛客网 import java.util.Queue;
import java.util.LinkedList;
public class Solution {
public int TreeDepth(TreeNode pRoot)
{
if(pRoot == null){
return ;
}
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.add(pRoot);
int depth = , count = , nextCount = ;
while(queue.size()!=){
TreeNode top = queue.poll();
count++;
if(top.left != null){
queue.add(top.left);
}
if(top.right != null){
queue.add(top.right);
}
if(count == nextCount){
nextCount = queue.size();
count = ;
depth++;
}
}
return depth;
}
}
39.平衡二叉树
题目描述
public class Solution {
private boolean isBalanced = true;
public boolean IsBalanced_Solution(TreeNode root) {
height(root);
return isBalanced;
} private int height(TreeNode root){
if(root == null)
return ;
int left = height(root.left);
int right = height(root.right);
if(Math.abs(left-right)>)
isBalanced = false;
return +Math.max(left,right);
}
}
40.数组中只出现一次的数字
题目描述
思路:
//使用堆栈来做辅助功能,将数组先排序,依次入栈,每一次数组入栈时和当前堆栈的栈头比较,如果当前堆栈为空,就入栈,如果和当前栈头的元素相同就出栈,当数组中左右元素都入栈完毕,那么当前栈中剩余的2个元素就是只出现一次的两个元素
时间复杂度为O(n2),空间复杂度为O(n)
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
import java.util.*;
public class Solution {
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
Arrays.sort(array);
Stack<Integer> stack = new Stack<Integer>();
int len = array.length; if(array == null){
num1[] = ;
num2[] = ;
}
for(int x = ;x<len;x++){
if(stack.isEmpty()){
stack.push(array[x]);
}else{
if(stack.peek() == array[x])
stack.pop();
else
stack.push(array[x]);
}
}
num1[] = stack.pop();
num2[] = stack.pop();
}
}
用ArrayList,思路和前面的一致
时间复杂度为O(n2),空间复杂度为O(n)
remove不装箱的话会被当作按照下标删除,add会自动装箱
链接:https://www.nowcoder.com/questionTerminal/e02fdb54d7524710a7d664d082bb7811
来源:牛客网 import java.util.ArrayList;
public class Solution {
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
ArrayList<Integer>list=new ArrayList<Integer>();
for(int i=;i<array.length;i++)
{
if(!list.contains(array[i]))
list.add(array[i]);
else
list.remove(new Integer(array[i]));
}
if(list.size()>)
{
num1[]=list.get();
num2[]=list.get();
}
}
}
最好的方法:
时间复杂度为O(n),空间复杂度为O(1)
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
import java.util.*;
public class Solution {
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) { int diff = ;
for(int num : array)
diff ^= num;
for(int num : array){
if((num & diff) == )
num1[] ^= num;
else
num2[] ^= num;
}
}
}
41.和为S的连续证书序列
输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序
链接:https://www.nowcoder.com/questionTerminal/c451a3fd84b64cb19485dad758a55ebe
来源:牛客网 import java.util.ArrayList;
/*
*初始化small=1,big=2;
*small到big序列和小于sum,big++;大于sum,small++;
*当small增加到(1+sum)/2是停止
*/
public class Solution {
public ArrayList<ArrayList<Integer>> FindContinuousSequence(int sum) {
ArrayList<ArrayList<Integer>> lists=new ArrayList<ArrayList<Integer>>();
if(sum<=){return lists;}
int small=;
int big=;
while(small!=(+sum)/){ //当small==(1+sum)/2的时候停止
int curSum=sumOfList(small,big);
if(curSum==sum){
ArrayList<Integer> list=new ArrayList<Integer>();
for(int i=small;i<=big;i++){
list.add(i);
}
lists.add(list);
small++;big++;
}else if(curSum<sum){
big++;
}else{
small++;
}
}
return lists;
}
public int sumOfList(int head,int leap){ //计算当前序列的和
int sum=head;
for(int i=head+;i<=leap;i++){
sum+=i;
}
return sum;
}
}
42.和为S 的两个数字
题目描述
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
ArrayList<Integer> al = new ArrayList<Integer>();
if(array == null)
return al;
int ahead = ;
int behind = array.length-;
while(ahead < behind){
int curSum = array[ahead] + array[behind];
if(sum == curSum){
al.add(array[ahead]);
al.add(array[behind]);
break;
}else if(curSum > sum){
behind--;
}else{
ahead++;
}
}
return al;
}
}
43.左旋转字符串
题目描述
public class Solution {
public String LeftRotateString(String str,int n) {
if(str.length() == )
return ""; char[] a = str.toCharArray();
char[] result = new char[a.length];
for(int i=;i<a.length-n;i++){
result[i] = a[n+i];
}
for(int i=;i<n;i++){
result[a.length-n+i] = a[i];
} return String.valueOf(result); }
}
public class Solution {
public String LeftRotateString(String str,int n) {
if(str.length()==)
return "";
int len = str.length();
str += str;
return str.substring(n, len+n);
}
}
public class Solution {
public String LeftRotateString(String str,int n) {
if(str.length()==)
return "";
//把原字符串截取成俩字符串,然后拼接
String s1 = str.substring(, n);
String s2 = str.substring(n,str.length());
return s2 + s1;
}
}
44.翻转单词顺序列
题目描述
public class Solution {
public String ReverseSentence(String str) {
if(str==null||str.length()==||str.trim().length()==)
return str; String[] a = str.split(" ");
StringBuffer sb = new StringBuffer();
for(int i = ;i<a.length;i++){
if(i!=a.length-)
sb.append(String.valueOf(a[a.length--i])).append(" ");
else
sb.append(String.valueOf(a[a.length--i]));
}
return sb.toString();
}
}
45.扑克牌算子
思路1:
1.将数组排序
2.找到第一个不出现0的位置,记作min
3.max - min < 5 则是顺子
import java.util.Arrays;
public class Solution {
public boolean isContinuous(int [] numbers) {
if(numbers == null || numbers.length == )
return false; Arrays.sort(numbers);
int length = numbers.length;
int positionFlag = ;
boolean endZeroFlag = true;
for(int i= ; i<length-;i++){
if(numbers[i] == ){
positionFlag = i+;
endZeroFlag = false;
}else {
endZeroFlag = true;
}
if(endZeroFlag == true && numbers[i] == numbers[i+])
return false;
} if(numbers[length-] - numbers[positionFlag] >= )
return false;
else
return true;
}
}
思路2:
链接:https://www.nowcoder.com/questionTerminal/762836f4d43d43ca9deb273b3de8e1f4
来源:牛客网 import java.util.Arrays;
public class Solution {
public boolean isContinuous(int[] numbers) {
int numOfZero = ;
int numOfInterval = ;
int length = numbers.length;
if(length == ){
return false;
}
Arrays.sort(numbers);
for (int i = ; i < length - ; i++) {
// 计算癞子数量
if (numbers[i] == ) {
numOfZero++;
continue;
}
// 对子,直接返回
if (numbers[i] == numbers[i + ]) {
return false;
}
numOfInterval += numbers[i + ] - numbers[i] - ;
}
if (numOfZero >= numOfInterval) {
return true;
}
return false;
}
}
46.圆圈中最后剩下的数字
0,1,2,。。。n这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈中删除第m个数字,求出这个圆圈里剩下的最后一个数字。
1.数组来模拟环
public static int findLastNumber(int n,int m){
if(n<||m<) return -;
int[] array = new int[n];
int i = -,step = , count = n;
while(count>){ //跳出循环时将最后一个元素也设置为了-1
i++; //指向上一个被删除对象的下一个元素。
if(i>=n) i=; //模拟环。
if(array[i] == -) continue; //跳过被删除的对象。
step++; //记录已走过的。
if(step==m) { //找到待删除的对象。
array[i]=-;
step = ;
count--;
}
}
return i;//返回跳出循环时的i,即最后一个被设置为-1的元素
}
2.用ArrayList做
index = (index + m) % data.size();
import java.util.ArrayList;
public class Solution {
public int LastRemaining_Solution(int n, int m) {
if (m == || n == ) {
return -;
}
ArrayList<Integer> data = new ArrayList<Integer>();
for (int i = ; i < n; i++) {
data.add(i);
}
int index = -;
while (data.size() > ) {
index = (index + m) % data.size();
data.remove(index);
index--;
}
return data.get();
}
}
47.求1+2+3+...+n
求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
思路1:
通常求求1+2+3+...+n除了用(乘法)公式n(n+1)/2,无外乎循环和递归两种思路,由于已经明确限制for和while的使用,循环已经不能再用了。递归函数也需要用if或者条件判断语句判断是继续递归下去还是终止递归,但是现在题目已经不允许使用这两种语句。
1
.需利用逻辑与的短路特性实现递归终止。
2
.当n==
0
时,(n>
0
)&&((sum+=Sum_Solution(n-
1
))>
0
)只执行前面的判断,为
false
,然后直接返回
0
;
3
.当n>
0
时,执行sum+=Sum_Solution(n-
1
),实现递归计算Sum_Solution(n)。
public class Solution {
public int Sum_Solution(int n) {
int sum = n;
boolean ans = (n>) && ((sum += Sum_Solution(n-))>);
return sum;
}
}
思路2:
用异常退出递归
public class Solution {
public int Sum_Solution(int n) {
return sum(n);
}
int sum(int n){
try{
int i = %n;
return n+sum(n-);
}
catch(Exception e){
return ;
}
}
}
思路3:
Math的幂函数,但是底层还是用了乘法
public class Solution {
public int Sum_Solution(int n) {
n = (int) (Math.pow(n, )+n)>>;
return n;
}
}
48.不做加减乘除做加法
首先看十进制是如何做的: 5+7=12,三步走
第一步:相加各位的值,不算进位,得到2。
第二步:计算进位值,得到10. 如果这一步的进位值为0,那么第一步得到的值就是最终结果。
第三步:重复上述两步,只是相加的值变成上述两步的得到的结果2和10,得到12。
同样我们可以用三步走的方式计算二进制值相加: 5-101,7-111
第一步:相加各位的值,不算进位,得到010,二进制每位相加就相当于各位做异或操作,101^111。
第二步:计算进位值,得到1010,相当于各位做与操作得到101,再向左移一位得到1010,(101&111)<<1。
第三步重复上述两步, 各位相加 010^1010=1000,进位值为100=(010&1010)<<1。 继续重复上述两步:1000^100 = 1100,进位值为0,跳出循环,1100为最终结果。
public class Solution {
public int Add(int num1,int num2) {
while (num2!=) {
int temp = num1^num2;
num2 = (num1&num2)<<;
num1 = temp;
}
return num1;
}
}
49.将字符串转换成整数
将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0
思路:
1.首先判断字符串第一个字符是不是‘-’‘+’号,如果是,则从第二个字符开始判断是不是数字;如果是‘-’,记录负数的标记
2.遍历每一个数字的asc码,if
(a[i] <
48
|| a[i] >
57
)
return
0
;
public class Solution {
public int StrToInt(String str) {
if (str.equals("") || str.length() == )
return ;
char[] a = str.toCharArray();
int sum = ;
int fuhao = ;
boolean fushu = false;
if(a[] == '-'){
fuhao = ;
fushu = true;
}else if (a[] == '+'){
fuhao = ;
} for (int i = fuhao; i < a.length; i++)
{
if (a[i] < || a[i] > )
return ;
sum = sum * + a[i] - ;
}
return fushu == false ? sum : sum * (-);
}
}
犯规写法:
public int StrToInt(String str) {
int result;
if (str == "" || str == null ) {
return ;
}
try {
result = Integer.valueOf(str);
} catch (NumberFormatException e) {
return ;
}
return result;
}
50.数组中重复的数字
题目描述
public class Solution { public boolean duplicate(int numbers[],int length,int [] duplication) {
boolean [] a = new boolean[length];
for(int i=;i<length; i++){
if(a[numbers[i]] == true){
duplication[] = numbers[i];
return true;
}
a[numbers[i]] = true;
}
return false;
}
}
使用Array.sort()或者HashSet(hashSet.add方法返回的是boolean值),但是补不能保证题目条件不能保证题目条件:如果输入长度为7的数组{ 7 5 6 7 5 3 1},那么对应的输出是第一个重复的数字2。
链接:https://www.nowcoder.com/questionTerminal/623a5ac0ea5b4e5f95552655361ae0a8
来源:牛客网 import java.util.*;
public class Solution {
public boolean duplicate(int numbers[],int length,int [] duplication) {
//方法1:
if(numbers == null || numbers.length == ) return false;
Arrays.sort(numbers);
int flag = ;//做标记
for(int i=;i<length-;i++) {
if(numbers[i] == numbers[i+]) {
duplication[] = numbers[i];
flag = ;
break;
}
}
return flag == ? true:false;
//方法2:
HashSet<Integer> hs = new HashSet<>();
for(int i=;i<length;i++) {
if(!hs.add(numbers[i])) {
duplication[]=numbers[i];
return true;
}
}
return false;
}
}
import java.util.ArrayList;
public class Solution {
public int[] multiply(int[] A) {
int length = A.length;
int [] B = new int[length];
B[] = ;
//计算下三角连乘
for(int i = ;i<length;i++){
B[i] = B[i-]*A[i-];
}
int temp = ;
//计算上三角
for(int j=length -;j>=;j--){
temp *= A[j+];
B[j] *= temp;
}
return B;
}
}
52.正则表达式的匹配
思路:
当模式中的第二个字符不是“*”时:
、如果字符串第一个字符和模式中的第一个字符相匹配,那么字符串和模式都后移一个字符,然后匹配剩余的。
、如果 字符串第一个字符和模式中的第一个字符相不匹配,直接返回false。 而当模式中的第二个字符是“*”时:
如果字符串第一个字符跟模式第一个字符不匹配,则模式后移2个字符,继续匹配。如果字符串第一个字符跟模式第一个字符匹配,可以有3种匹配方式:
、模式后移2字符,相当于x*被忽略;
、字符串后移1字符,模式后移2字符;
、字符串后移1字符,模式不变,即继续匹配字符下一位,因为*可以匹配多位; 这里需要注意的是:Java里,要时刻检验数组是否越界。
public class Solution {
public boolean match(char[] str, char[] pattern) {
if (str == null || pattern == null) {
return false;
}
int strIndex = ;
int patternIndex = ;
return matchCore(str, strIndex, pattern, patternIndex);
} public boolean matchCore(char[] str, int strIndex, char[] pattern, int patternIndex) {
//有效性检验:str到尾,pattern到尾,匹配成功
if (strIndex == str.length && patternIndex == pattern.length) {
return true;
}
//pattern先到尾,匹配失败
if (strIndex != str.length && patternIndex == pattern.length) {
return false;
}
//模式第2个是*,且字符串第1个跟模式第1个匹配,分3种匹配模式;如不匹配,模式后移2位
if (patternIndex + < pattern.length && pattern[patternIndex + ] == '*') {
if ((strIndex != str.length && pattern[patternIndex] == str[strIndex]) || (pattern[patternIndex] == '.' && strIndex != str.length)) {
return matchCore(str, strIndex, pattern, patternIndex + )//模式后移2,视为x*匹配0个字符
|| matchCore(str, strIndex + , pattern, patternIndex + )//视为模式匹配1个字符
|| matchCore(str, strIndex + , pattern, patternIndex);//*匹配1个,再匹配str中的下一个
} else {
return matchCore(str, strIndex, pattern, patternIndex + );
}
}
//模式第2个不是*,且字符串第1个跟模式第1个匹配,则都后移1位,否则直接返回false
if ((strIndex != str.length && pattern[patternIndex] == str[strIndex]) || (pattern[patternIndex] == '.' && strIndex != str.length)) {
return matchCore(str, strIndex + , pattern, patternIndex + );
}
return false;
}
}
53.表示数值的字符串
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。
犯规写法1:使用正则表达式
public class Solution { public boolean isNumeric(char[] str) {
String string = String.valueOf(str);
return string.matches("[\\+-]?[0-9]*(\\.[0-9]*)?([eE][\\+-]?[0-9]+)?");
}
}
犯规写法2:使用自带API
public class Solution {
public boolean isNumeric(char[] str) {
try {
double re = Double.parseDouble(new String(str));
} catch (NumberFormatException e) {
return false;
}
return true;
}
}
正规解法:
参考剑指offer
分成A[.[B]][e|EC]或者.B[e|EC]
分表验证ABC三部分,AC都是可带正负符号的整数,B为无符号整数
//参见剑指offer
public class Solution {
private int index = ;
public boolean isNumeric(char[] str) {
if (str.length < )
return false;
boolean flag = scanInteger(str);
if (index < str.length && str[index] == '.') {
index++;
flag = scanUnsignedInteger(str) || flag;
}
if (index < str.length && (str[index] == 'E' || str[index] == 'e')) {
index++;
flag = flag && scanInteger(str);
}
return flag && index == str.length;
}
private boolean scanInteger(char[] str) {
if (index < str.length && (str[index] == '+' || str[index] == '-') )
index++;
return scanUnsignedInteger(str);
}
private boolean scanUnsignedInteger(char[] str) {
int start = index;
while (index < str.length && str[index] >= '' && str[index] <= '')
index++;
return start < index; //是否存在整数
}
}
54.字符流中第一个不重复的字符
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。
思路1:
使用LinkedHashMap实现,注意不能使用hashmap,因为题目要求得到的是第一个不重复的字符,所以必须有序。
import java.util.LinkedHashMap;
public class Solution {
LinkedHashMap<Character,Integer> map =new LinkedHashMap<Character,Integer>();
public void Insert(char ch)
{
if(map.containsKey(ch)){
map.put(ch, map.get(ch)+);
}else{
map.put(ch, );
}
}
//return the first appearence once char in current stringstream
public char FirstAppearingOnce()
{
for(char key:map.keySet()){
if(map.get(key) == )
return key; }
return '#';
}
}
思路2:
一个256大小的数组来实现一个简易的哈希表
public class Solution
{
int[] hashtable=new int[];
StringBuffer s=new StringBuffer();
//Insert one char from stringstream
public void Insert(char ch)
{
s.append(ch);
if(hashtable[ch]==)
hashtable[ch]=;
else hashtable[ch]+=;
}
//return the first appearence once char in current stringstream
public char FirstAppearingOnce()
{
char[] str=s.toString().toCharArray();
for(char c:str)
{
if(hashtable[c]==)
return c;
}
return '#';
}
}
思路3:
同样使用哈希表,不过非常啊巧妙的借助队列
private int[] cnts = new int[];
private Queue<Character> queue = new LinkedList<>(); public void Insert(char ch)
{
cnts[ch]++;
queue.add(ch);
while (!queue.isEmpty() && cnts[queue.peek()] > )
queue.poll();
} public char FirstAppearingOnce()
{
return queue.isEmpty() ? '#' : queue.peek();
}
55.链表中环的入口节点
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
思路1:
设起点到相遇点距离为x,起点到入口点距离为y,环长度为r,则快慢针相遇时,满足2x-x=nr,n为快针在环中转的圈数。--> x=nr
快慢针相遇点距环入口点距离x-y
相遇后,快针从起点重新开始以步长为1速度开始走,经过距离y到达环入口点,慢针走y步后距离环入口点距离为x-y+y=x=nr,即走到了环入口点,两个指针相遇
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead)
{
ListNode fast = pHead;
ListNode slow = pHead;
while(fast !=null && fast.next !=null) {
fast = fast.next.next;
slow = slow.next;
if(fast == slow) {
ListNode p = pHead;
while( p != slow) {
p = p.next;
slow = slow.next;
}
return p;
}
}
return null;
}
}
思路2:
碉堡的解法:利用HashSet,遍历添加链表中的元素,若遇到重复添加则返回哪个元素。
import java.util.HashSet;
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead)
{
HashSet<ListNode> set = new HashSet<ListNode>();
while(pHead != null){
if(!set.add(pHead))
return pHead;
pHead = pHead.next;
}
return null;
}
}
使用ArrayList
List<ListNode> list = new ArrayList<ListNode>();
while(!list.contains(pHead))
{
list.add(pHead);
if(pHead.next!=null)
pHead = pHead.next;
else
break;
}
if(pHead.next == null)return null;
return pHead;
56.删除链表中重复的节点
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
1.递归方法(推荐):
public class Solution {
public ListNode deleteDuplication(ListNode pHead) {
if (pHead == null || pHead.next == null) { // 只有0个或1个结点,则返回
return pHead;
}
if (pHead.val == pHead.next.val) { // 当前结点是重复结点
ListNode pNode = pHead.next;
while (pNode != null && pNode.val == pHead.val) {
// 跳过值与当前结点相同的全部结点,找到第一个与当前结点不同的结点
pNode = pNode.next;
}
return deleteDuplication(pNode); // 从第一个与当前结点不同的结点开始递归
} else { // 当前结点不是重复结点
pHead.next = deleteDuplication(pHead.next); // 保留当前结点,从下一个结点开始递归
return pHead;
}
}
}
2.非递归方法:
public static ListNode deleteDuplication(ListNode pHead) {
ListNode first = new ListNode(-);//设置一个trick
first.next = pHead;
ListNode p = pHead;
ListNode last = first;
while (p != null && p.next != null) {
if (p.val == p.next.val) {
int val = p.val;
while (p!= null&&p.val == val)
p = p.next;
last.next = p;
} else {
last = p;
p = p.next;
}
}
return first.next;
}
57.二叉树的下一个节点
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
思路:
① 如果一个节点的右子树不为空,那么该节点的下一个节点是右子树的最左节点;
② 否则,向上找第一个左链接指向的树包含该节点的祖先节点。
/*
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.right !=null){
TreeLinkNode node = pNode.right;
while (node.left != null) //如果有右子树,则找右子树的最左节点
node = node.left;
return node;
}else{
while(pNode.next != null){
TreeLinkNode parent = pNode.next;
if(parent.left == pNode) //没右子树,则找第一个当前节点是父节点左孩子的节点
return parent;
pNode = pNode.next;
}
}
return null; //退到了根节点仍没找到,则返回null
}
}
58.对称的二叉树
请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
思路:
1.递归算法
public class Solution {
boolean isSymmetrical(TreeNode pRoot)
{
if(pRoot == null)
return true;
return isSymmetrical(pRoot.left,pRoot.right);
} boolean isSymmetrical(TreeNode t1,TreeNode t2){
if(t1 == null && t2 == null)
return true;
if(t1 == null) return t2 == null;
if(t2 == null) return false;
if(t1.val != t2.val) return false;
return isSymmetrical(t1.left,t2.right) && isSymmetrical(t1.right,t2.left);
}
}
2.使用栈
使用stack来保存成对的节点
2).确定入栈顺序,每次入栈都是成对成对的,如left.left, right.right ;left.rigth,right.left
boolean isSymmetricalDFS(TreeNode pRoot)
{
if(pRoot == null) return true;
Stack<TreeNode> s = new Stack<>();
s.push(pRoot.left);
s.push(pRoot.right);
while(!s.empty()) {
TreeNode right = s.pop();//成对取出
TreeNode left = s.pop();
if(left == null && right == null) continue;
if(left == null || right == null) return false;
if(left.val != right.val) return false;
//成对插入
s.push(left.left);
s.push(right.right);
s.push(left.right);
s.push(right.left);
}
return true;
}
59.按照只字形顺序打印二叉树
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
重点学习:使用堆栈实现层级遍历,然后借助一个标记变量实现反向排序是之字形遍历。
每当使下一个层级的节点入堆栈,记录堆栈中节点的个数,然后取出相应个数的节点。
注意:堆栈中是可以存放null值的,当遍历到null时,直接返回,当null也全部从堆栈中弹出后堆栈为空就代表全部遍历完结束。
import java.util.ArrayList; /*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null; public TreeNode(int val) {
this.val = val; } }
*/
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.Queue;
public class Solution {
public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) { ArrayList<ArrayList<Integer>> al = new ArrayList<ArrayList<Integer>>();
Queue <TreeNode> q = new LinkedList<>();
q.add(pRoot);
boolean reverse = false;
while(!q.isEmpty()){
ArrayList<Integer> ll = new ArrayList<Integer>();
int cnt = q.size();
while(cnt-- > ){
TreeNode node = q.poll();
if(node == null)
continue;
ll.add(node.val);
q.add(node.left);
q.add(node.right);
}
if(reverse)
Collections.reverse(ll);
reverse = !reverse;
if(ll.size() != )
al.add(ll);
}
return al;
} }
60.把二叉树打印成多行
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
思路:跟前面那个类似,只是不用翻转顺序
import java.util.ArrayList; /*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null; public TreeNode(int val) {
this.val = val; } }
*/
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.Queue;
public class Solution {
ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
Queue<TreeNode> queue = new LinkedList<>();
queue.add(pRoot);
while (!queue.isEmpty()) {
ArrayList<Integer> list = new ArrayList<>();
int cnt = queue.size();
while (cnt-- > ) {
TreeNode node = queue.poll();
if (node == null)
continue;
list.add(node.val);
queue.add(node.left);
queue.add(node.right);
}
if (list.size() != )
ret.add(list);
}
return ret;
} }
61.序列化二叉树
请实现两个函数,分别用来序列化和反序列化二叉树
思路:
序列化:用前序遍历递归,递归的终止条件
if(root == null)
return "#";
反序列化:同样以前序遍历的书序递归构造
递归的终止条件
if(strr[index].equals("#"))
return null;
用一个位置变量index记录遍历到达的位置
public class Solution { private int index = -;
String Serialize(TreeNode root) {
if(root == null)
return "#";
return root.val +" "+ Serialize(root.left)+" " + Serialize(root.right);
} TreeNode Deserialize(String str) {
index++;
String[] strr = str.split(" "); if(strr[index].equals("#"))
return null;
TreeNode node = new TreeNode(Integer.valueOf(strr[index]));
node.left = Deserialize(str);
node.right = Deserialize(str);
return node; }
}
遍历的时候传入未遍历到不同的字符串
private String deserializeStr; public String Serialize(TreeNode root)
{
if (root == null)
return "#";
return root.val + " " + Serialize(root.left) + " " + Serialize(root.right);
} public TreeNode Deserialize(String str)
{
deserializeStr = str;
return Deserialize();
} private TreeNode Deserialize()
{
if (deserializeStr.length() == )
return null;
int index = deserializeStr.indexOf(" ");
String node = index == - ? deserializeStr : deserializeStr.substring(, index);
deserializeStr = index == - ? "" : deserializeStr.substring(index + );
if (node.equals("#"))
return null;
int val = Integer.valueOf(node);
TreeNode t = new TreeNode(val);
t.left = Deserialize();
t.right = Deserialize();
return t;
}
62.二叉搜索树的第k个节点
给定一颗二叉搜索树,请找出其中的第k小的结点。例如, 5 / \ 3 7 /\ /\ 2 4 6 8 中,按结点数值大小顺序第三个结点的值为4。
思路:利用二叉搜索数中序遍历有序的特点,递归的中序遍历第k个节点就是。
public class Solution { private TreeNode ret =null;
private int cnt = ;
TreeNode KthNode(TreeNode pRoot, int k)
{
inorder(pRoot,k);
return ret;
} void inorder(TreeNode pRoot, int k){
if(pRoot == null || cnt > k)
return;
inorder(pRoot.left,k);
cnt++;
if(cnt == k)
ret = pRoot;
inorder(pRoot.right,k);
} }
63.数据流中的中位数
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
思路:
将数据流分成了两个部分,从中间切分(想象一下数据流有序时的样子),大顶堆里面是小的一半,小顶堆大的一半,当求中位数时只用关心中间的一个数或者两个数,这时关心的数就是堆顶的数
import java.util.PriorityQueue;
public class Solution { private int count = ;
private PriorityQueue<Integer> max = new PriorityQueue<Integer>((o1,o2)->(o2-o1));
private PriorityQueue<Integer> min = new PriorityQueue<Integer>();
public void Insert(Integer num) {
if(count% ==){
//当数据总数为偶数时,新加入的元素,应当进入小根堆
//(注意不是直接进入小根堆,而是经大根堆筛选后取大根堆中最大元素进入小根堆)
//1.新加入的元素先入到大根堆,由大根堆筛选出堆中最大的元素
max.add(num);
//2.筛选后的【大根堆中的最大元素】进入小根堆
min.add(max.poll());
}else {
//当数据总数为奇数时,新加入的元素,应当进入大根堆
//(注意不是直接进入大根堆,而是经小根堆筛选后取小根堆中最大元素进入大根堆)
//1.新加入的元素先入到小根堆,由小根堆筛选出堆中最小的元素
min.add(num);
//2.筛选后的【小根堆中的最小元素】进入大根堆
max.add(min.poll());
}
count++;
} public Double GetMedian() {
if(count% ==)
return (min.peek()+max.peek())/2.0;
else
return (double)min.peek();
} }
64.滑动窗口的最大值
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{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]}。
思路1:朴素的解法:
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> maxInWindows(int [] num, int size)
{
ArrayList<Integer> al = new ArrayList<>();
if(num.length == || size==)
return al;
for(int i = ; i<num.length-size+;i++){
int [] a = new int[size];
for(int j=;j<size;j++){
a[j] = num[i+j];
}
al.add(max(a));
}
return al;
} public Integer max(int[] num){
int max = num[];
for(int i = ;i<num.length;i++){
if(num[i]>max)
max = num[i];
}
return max;
} }
思路2:大顶堆解法
public ArrayList<Integer> maxInWindows(int[] num, int size)
{
ArrayList<Integer> ret = new ArrayList<>();
if (size > num.length || size < )
return ret;
PriorityQueue<Integer> heap = new PriorityQueue<>((o1, o2) -> o2 - o1); /* 大顶堆 */
for (int i = ; i < size; i++)
heap.add(num[i]);
ret.add(heap.peek());
for (int i = , j = i + size - ; j < num.length; i++, j++) { /* 维护一个大小为 size 的大顶堆 */
heap.remove(num[i - ]);
heap.add(num[j]);
ret.add(heap.peek());
}
return ret;
}
65.矩阵中的路径
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。 例如 a b c e s f c s a d e e 这样的3 X 4 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
思路:回溯算法,回溯的思想体现在体现在那个很长的if语句后面的,flag标记的恢复
public class Solution {
public boolean hasPath(char[] matrix, int rows, int cols, char[] str) {
int flag[] = new int[matrix.length];
for (int i = ; i < rows; i++) {
for (int j = ; j < cols; j++) {
if (helper(matrix, rows, cols, i, j, str, , flag))
return true;
}
}
return false;
} private boolean helper(char[] matrix, int rows, int cols, int i, int j, char[] str, int k, int[] flag) {
int index = i * cols + j;
if (i < || i >= rows || j < || j >= cols || matrix[index] != str[k] || flag[index] == )
return false;
if(k == str.length - ) return true;
flag[index] = ;
if (helper(matrix, rows, cols, i - , j, str, k + , flag)
|| helper(matrix, rows, cols, i + , j, str, k + , flag)
|| helper(matrix, rows, cols, i, j - , str, k + , flag)
|| helper(matrix, rows, cols, i, j + , str, k + , flag)) {
return true;
}
flag[index] = ;
return false;
} }
66.机器人的运动范围
地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
思路:从0,0开始通过递归遍历上下左右加起来的结果就是最大的连通区域
public class Solution {
public int movingCount(int threshold, int rows, int cols) {
int flag[][] = new int[rows][cols]; //记录是否已经走过
return helper(, , rows, cols, flag, threshold);
} private int helper(int i, int j, int rows, int cols, int[][] flag, int threshold) {
if (i < || i >= rows || j < || j >= cols || numSum(i) + numSum(j) > threshold || flag[i][j] == ) return ;
flag[i][j] = ;
return helper(i - , j, rows, cols, flag, threshold)
+ helper(i + , j, rows, cols, flag, threshold)
+ helper(i, j - , rows, cols, flag, threshold)
+ helper(i, j + , rows, cols, flag, threshold)
+ ;
} private int numSum(int i) {
int sum = ;
do{
sum += i%;
}while((i = i/) > );
return sum;
}
}
剑指offer编程题66道题 36-66的更多相关文章
- 剑指Offer编程题2——替换空格
剑指Offer编程题2——替换空格 题目描述 请实现一个函数,将一个字符串中的每个空格替换成“%20”.例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happ ...
- 剑指Offer编程题1——二维数组中的查找
剑指Offer编程题1---------------二维数组中的查找 题目描述 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完 ...
- 剑指offer编程题Java实现——面试题12打印1到最大的n位数
题目:打印1到最大的n位数 输入数字n,按顺序打印输出从1到最大的n位十进制数,比如输入3,打印从1到999. 这道题考察的地方是如何表示大数问题.由于n是任意大的数组,如果n太大的话n位数就超过了l ...
- 剑指offer编程题Java实现——面试题12相关题大数的加法、减法、乘法问题的实现
用字符串或者数组表示大数是一种很简单有效的表示方式.在打印1到最大的n为数的问题上采用的是使用数组表示大数的方式.在相关题实现任意两个整数的加法.减法.乘法的实现中,采用字符串对大数进行表示,不过在具 ...
- 剑指offer编程题Java实现——替换空格
题目描述 请实现一个函数,将一个字符串中的空格替换成"%20".例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy. package ...
- 剑指offer编程题Java实现——面试题5从头到尾打印链表
题目描述* 剑指offer面试题5:从尾到头打印链表 输入一个链表的头结点,从尾到头打印出每个结点的值 解决方案一:首先遍历链表的节点后打印,典型的"后进先出",可以使用栈来实现这 ...
- 剑指offer编程题Java实现——面试题6重建二叉树
题目: 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历结果中都不含重复的数字.例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2, ...
- 剑指offer编程题Java实现——面试题11数值的整数次方
题目: 实现函数double power(double base,int exponent),求base的exponent次方.不得使用库函数,同时不需要考虑大数问题. 解题思路:最一般的方法实现数值 ...
- 剑指offer编程题Java实现——面试题14调整数组顺序使奇数位于偶数之前
题目: 输入一个整数数组,实现一个函数来调整该数组中数组的顺序,使得所有的奇数位于数组的前半部分,偶数位于数组的后半部分. 解题思路:数组中维护两个指针,第一个指针初始化时候指向数组头部,第二个指针初 ...
随机推荐
- mac os下android 通过battery-historian进行电量分析
简单介绍下如何用battery-historian进行电量分析,因为battery-hostorian是基于go语言的框架,所以需要安装go 1.安装go 2.配置go环境变量到.bash_profi ...
- cobbler default system 网络安装时主机的menu上只有一个local选项
问题:使用cobbler default system 做pxe网络安装时,主机启动后安装menu上只有一个local选项,看不到对应的system名字 解决:cobbler default syst ...
- cobbler setting dnsmasq
Currently cobbler can help generate a ISC DHCP configuration (package name: dhcpd) it can also alter ...
- Codeforces Round #210 (Div. 1).B
经典的一道DP题. 题目明显是一道DP题,但是比赛的时候一个劲就在想怎么记录状态和转移.最后想到了一种n^3的方法,写了下,不出所料的超时了. 看了别人的代码才发现竟然是先二分然后再进行DP,像这种思 ...
- storyboard设置navigation controller
到storyboard选中我们唯一一个的viewcontroller,找到xcode的菜单栏,Edit->Embed In->NavigationController.这时候storybo ...
- ES6通过WeakMap解决内存泄漏问题
一.Map 1.定义 Map对象保存键值对,类似于数据结构字典:与传统上的对象只能用字符串当键不同,Map对象可以使用任意值当键. 2.语法 new Map([iterable]) 属性 size:返 ...
- OVN实战---《A Primer on OVN》翻译
overview 在本文中,我们将在三个host之间创建一个简单的二层overlay network.首先,我们来简单看一下,整个系统是怎么工作的.OVN基于分布式的control plane,其中各 ...
- 如何让socket编程非阻塞?
import socket # 创建socket client = socket.socket() # 将原来阻塞的位置变成非阻塞(报错) client.setblocking(False) # 百度 ...
- qt下通过socket传送中文
zz 1.在main函数里我之前就加了一句QTextCodec::setCodecForTr( QTextCodec::codecForLocale() ); 现在再加一句QTextCodec::se ...
- C# 调用win api获取chrome浏览器中地址
//FindWindow 查找窗口 //FindWindowEx查找子窗口 //EnumWindows列举屏幕上的所有顶层窗口,如果回调函数成功则返回非零,失败则返回零 //GetWindowText ...