《剑指Offer》
- 牛客网 20到40题
- 面试题21:栈的压人弹出序列
- 面试题22:由上向下打印二叉树 广度优先遍历****
- 面试题23:二叉搜索树的后序遍历
- 面试题24:二叉树中和为某一值的路径 ****
- 面试题25:复杂链表的复制 ****
- 面试题26:二叉搜索树与双向链表
- 面试题27:字符串的排序 **** ------递归
- 面试题28:数组中出现超过次数一半的数字
- 面试题29:最小的K个数
- 面试题30:连续子数组的最大和
- 面试题31:整数中1出现的次数 ***
- 面试题32:把数组排成最小的数
- 面试题33:丑数
- 面试题34:第一个只出现一次的字符
- 面试题35:数组中的逆序对
- 面试题36:两个链表的第一个公共结点
- 面试题37:数字在排序数组中的次数
- 面试题38:二叉树的深度
- 面试题39:判断是否为平衡二叉树 ****
- 面试题40:数组中只出现一次的数字
牛客网 20到40题
面试题21:栈的压人弹出序列
题目:输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
思路:借用一个辅助的栈,遍历压栈顺序,先讲第一个放入栈中,这里是1,然后判断栈顶元素是不是出栈顺序的第一个元素,这里是4,很显然1≠4,所以我们继续压栈,直到相等以后开始出栈,出栈一个元素,则将出栈顺序向后移动一位,直到不相等,这样循环等压栈顺序遍历完成,如果辅助栈还不为空,说明弹出序列不是该栈的弹出顺序。
遇到的问题:在while出栈循环中,发现当循环出栈到j = 4,data_pushA已空时,还会调用peek(),因此抛出EmptyException()。所以我加上了一句,如果data_pushA为空,break;
代码:运行时间:17ms 占用内存:9384k
import java.util.ArrayList;
import java.util.Stack;
public class Solution {
public boolean IsPopOrder(int [] pushA,int [] popA) {
if(pushA == null||popA == null) return false;
if(pushA.length != popA.length) return false;
Stack data_pushA = new Stack();
int length = pushA.length;
int j = 0;
for(int i = 0;i<length;i++)
{
data_pushA.push(pushA[i]);
if((int)data_pushA.peek() == popA[j]) //如果发现栈顶指针等于出栈的序列的某个值
{
while((int)data_pushA.peek() == popA[j])
{
data_pushA.pop();
j++;
if(data_pushA.isEmpty()) break;
}
}
}
return data_pushA.isEmpty();
}
}
面试题22:由上向下打印二叉树 广度优先遍历****
题目:从上往下打印出二叉树的每个节点,同层节点从左至右打印。
思路:使用两个队列一个存放节点,一个存放值。先将根节点加入到队列中,然后遍历队列中的元素,遍历过程中,访问该元素的左右节点,再将左右子节点加入到队列中来。
遇到的问题:我广度优先遍历新建一个二叉树,利用ArrayList,虽然代码量少,循环的上下界很容易错,调试了1小时。广度优先遍历精髓:一个for循环,i<arrayList.size(),在不断遍历当前结点的时候,如果有左子树,将左子树存入arrayList中,有右子树存入arrayList。循环中每次去TreeNode = arrayList.get(i); i++。巧妙就巧妙在,虽然只用了一个arrayList,但是for()循环遍历整个二叉树,每循环一次,都将当前结点的左右子树(若有)存放到arrayList中,因此arrayList的size()是一个动态增加的过程,arrayList(i++)就是二叉树的每一层从左到右。
public class Task22 {
public TreeNode newTree(int[]array) //按广度优先新建树
{
ArrayList<TreeNode> treeNodes = new ArrayList<>();
TreeNode root = new TreeNode(array[0]);
treeNodes.add(root);
for(int i = 0; i< array.length;i++)
{
TreeNode node = new TreeNode(array[i]);
treeNodes.add(node);
}
for(int i = 1; i<= array.length/2;i++)
{
treeNodes.get(i).left = treeNodes.get(2*i);
treeNodes.get(i).right = treeNodes.get(2*i+1);
}
return treeNodes.get(1);
}
public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
ArrayList<TreeNode> list = new ArrayList<TreeNode>();
ArrayList<Integer> deque = new ArrayList<Integer>();
if(root == null)
{
return deque;
}
list.add(root);
for(int i = 0;i<list.size();i++)
{
TreeNode node = list.get(i);
if(node.left != null)
{
list.add(node.left);
}
if(node.right != null)
{
list.add(node.right);
}
deque.add(node.val);
}
return deque;
}
public static void main(String[] args) {
Task22 task22 = new Task22();
int [] array = {10,6,14,4,8,12,16};
TreeNode root = task22.newTree(array);
ArrayList<Integer> deque = task22.PrintFromTopToBottom(root);
for(Integer i:deque)
System.out.println(i);
}
}
面试题23:二叉搜索树的后序遍历
题目:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
思路:注意:需要先知道的:二叉搜索树后序遍历,最右一个为根结点root,左子树值都比root小,右子树值都比root大。
代码:能不用递归就尽量不用递归,根据二叉树后序遍历得的结果的特点运行时间:15ms 占用内存:9252k
注意: 一维数组判断是否为空1、array null 判断指针 2、array.length == 0;判断长度是否为空
public class Solution {
public boolean VerifySquenceOfBST(int [] sequence) {
if(sequence == null) return false;
if(sequence.length == 0) return false;
if(sequence.length <2) return true;
int length = sequence.length - 1;
int root = 0,i = 0;
while(length>=0)
{
root = sequence[length];
while(sequence[i]<root) i++;
while(sequence[i]>root) i++;
if(i<length -1) return false;
length --;
i = 0;
}
return true;
}
}
面试题24:二叉树中和为某一值的路径 ****
题目:输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)。
思路:当使用前序遍历方式访问某一结点时,我们把该结点添加到路径上,并累加该结点的值,如果该结点是叶结点,并且路径中的节点值刚好等于输入的整数,那么当前路径符合要求,将其打印出来,如果不是叶节点,继续前序遍历。注意:在当前结点访问结束后要删除当前结点的值!如果是根结点也要删除当前结点!
代码:运行时间:22ms 占用内存:9628k
一开始写的时候没看清楚题目,路径要求是根结点到叶结点,因此写出的版本是找出从根结点出发之和等于target的路径
需要注意: path是ArrayList类型,paths是 ArrayList<ArrayList>,当有合适的path时,不能paths.add(path); 因为后面中会改变path值,不是add到paths就可以,需要新建一个ArrayList temp变量用于复制path值,再add。
当写出能查找所有路径时,再添加判断当前结点是否为叶结点,是才添加。sumOfArrayList(path) == target&&(root.right == null)&&(root.left == null)
public class Solution {
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
ArrayList<ArrayList<Integer>> paths = new ArrayList<ArrayList<Integer>>();
ArrayList<Integer> path = new ArrayList<Integer>();
qianxubianli(root,paths,path,target);
return paths;
}
public void qianxubianli(TreeNode root,ArrayList<ArrayList<Integer>> paths,ArrayList<Integer> path,int target)
{
if(root == null) return;
path.add(root.val);
if(sumOfArrayList(path) == target&&(root.right == null)&&(root.left == null))
{
ArrayList<Integer> temp = new ArrayList<>();
for(int i:path)
temp.add(i);
paths.add(temp); //注意此处不能直接paths.add(path);因为后面path会改变,导致添加到paths中的ArrayList<Integer>也改变
}
if(root.left == null&&root.right == null) path.remove(path.size() - 1);
qianxubianli(root.left,paths,path,target);
qianxubianli(root.right,paths,path,target);
if(root.left != null||root.right != null) path.remove(path.size() - 1);
}
public int sumOfArrayList( ArrayList<Integer> path)
{
if(path.size() == 0) return 0;
int sum = 0;
for(int i:path)
sum+= i;
return sum;
}
}
面试题25:复杂链表的复制 ****
题目:输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
思路:
解题思路:
1、遍历链表,复制每个结点,如复制结点A得到A1,将结点A1插到结点A后面;
2、重新遍历链表,复制老结点的随机指针给新结点,如A1.random = A.random.next;
3、拆分链表,将链表拆分为原链表和复制后的链表
注意事项:不能动不动就A1.next.next你无法确定A1.next是否为空,应该使用A1.next == null?null:A1.next.next;代替
代码:
/*
*解题思路:
*1、遍历链表,复制每个结点,如复制结点A得到A1,将结点A1插到结点A后面;
*2、重新遍历链表,复制老结点的随机指针给新结点,如A1.random = A.random.next;
*3、拆分链表,将链表拆分为原链表和复制后的链表
*/
public class Solution {
public RandomListNode Clone(RandomListNode pHead) {
if(pHead == null) {
return null;
}
RandomListNode currentNode = pHead;
//1、复制每个结点,如复制结点A得到A1,将结点A1插到结点A后面;
while(currentNode != null){
RandomListNode cloneNode = new RandomListNode(currentNode.label);
RandomListNode nextNode = currentNode.next;
currentNode.next = cloneNode;
cloneNode.next = nextNode;
currentNode = nextNode;
}
currentNode = pHead;
//2、重新遍历链表,复制老结点的随机指针给新结点,如A1.random = A.random.next;
while(currentNode != null) {
currentNode.next.random = currentNode.random==null?null:currentNode.random.next;
currentNode = currentNode.next.next;
}
//3、拆分链表,将链表拆分为原链表和复制后的链表
currentNode = pHead;
RandomListNode pCloneHead = pHead.next;
while(currentNode != null) {
RandomListNode cloneNode = currentNode.next;
currentNode.next = cloneNode.next;
cloneNode.next = cloneNode.next==null?null:cloneNode.next.next;
currentNode = currentNode.next;
}
return pCloneHead;
}
}
面试题26:二叉搜索树与双向链表
题目:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
思路:先将二叉搜索树中序遍历,将结点依次放入ArrayList list,然后将list[i]的左结点left更改list[i-1],list[i]更改为list[i+1]
代码:运行时间:25ms 占用内存:9436k
import java.util.ArrayList;
public class Solution {
public TreeNode Convert(TreeNode pRootOfTree) {
if(pRootOfTree == null) return null;
ArrayList<TreeNode> list = new ArrayList<>();
bianli(list,pRootOfTree);
if(list.size() == 1) return list.get(0);
list.get(0).right = list.get(1);
for(int i = 1;i<list.size()-1;i++)
{
list.get(i).left = list.get(i-1);
list.get(i).right = list.get(i+1);
}
list.get(list.size()-1).left = list.get(list.size() - 2);
return list.get(0);
}
public void bianli(ArrayList<TreeNode> list,TreeNode root)
{
if(root == null)
return;
bianli(list,root.left);
list.add(root);
bianli(list,root.right);
}
}
面试题27:字符串的排序 **** ------递归
题目:输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
思路:
代码:输入描述: 输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Solution {
//解法来源:牛客网评论。基于回溯的递归实现。
/**剑指offer的思路解析。
* 步骤:
*
* @param str
* @return
*/
public ArrayList<String> Permutation(String str) {
List <String> res = new ArrayList<String>();
if(str != null && str.length() >0){
PermutationHelp(str.toCharArray(),0,res);
Collections.sort(res); //按字典序 输出字符串数组。
}
return (ArrayList)res;
}
public void PermutationHelp(char[] chars, int index, List<String> list) {
if(index == chars.length -1){ //当递归交换到最后一个位置的时候,就看看list有么有这个字符串,没有的话就放进去。
String val = String.valueOf(chars);
if (!list.contains(val)) {//如果最后list没有这个string,因为可能交换后有重复的
list.add(val);
}
}
else {
for (int i = index; i < chars.length; i++) { //循环来执行交换操作,先交换,然后固定这个,下一个交换。最后要交换回来不要影响执行
swap(chars, index, i);
PermutationHelp(chars, index+1, list);//依次固定一个
swap(chars, index, i);
}
}
}
public void swap(char[] chars,int i, int j) {//交换数组中的两个位置中的值
char temp =chars[i];
chars[i] = chars[j];
chars[j] = temp;
}
}
面试题28:数组中出现超过次数一半的数字
题目:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
思路:
代码:运行时间:15ms 占用内存:9408k
import java.util.Arrays;
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
if(array == null) return 0;
if(array.length == 1) return array[0];
Arrays.sort(array);
int length = array.length;
for(int i = 0; i<length/2+1;i++)
{
if(i+length/2+1 == length) return 0;
if(array[i] == array[i+length/2]) return array[i];
}
return 0;
}
}
面试题29:最小的K个数
题目:输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
思路:那就是要非常熟悉java.util包里的常用工具类,在做题时候会极大的帮助我们加快解题速度。用Array.sort()
代码:运行时间:28ms 占用内存:9328k
注意:若函数要求返回ArrrayList ,即使返回的结果是空,也不能返回null,而是新建一个空的ArrrayList。
import java.util.ArrayList;
import java.util.Arrays;
public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer> result = new ArrayList<Integer>();
if(input == null) return result;
if(k>input.length) return result;
Arrays.sort(input);
for(int i = 0;i<k;i++)
result.add(input[i]);
return result;
}
}
面试题30:连续子数组的最大和
题目:HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)
思路:当连续子数组中有正数时,用一个tempMax = 0,如果tempMax +array[i] >0,则tempMax = temp+array[i],否则tempMax = 0,遍历数组,然后用一个max存储最大值。当连续子数组中都为负数时,用一个min = Integer.MIN_VALUE,同时遍历负数数组的最大值。
代码:运行时间:20ms 占用内存:9284k
public class Solution {
public int FindGreatestSumOfSubArray(int[] array) {
if(array == null||array.length == 0) return 0;
int max = 0,min = Integer.MIN_VALUE;
int maxtemp = 0,mintemp = 0;
for(int i = 0;i<array.length;i++)
{
if(maxtemp +array[i]>0)
{
maxtemp = maxtemp+array[i];
}
else{
maxtemp = 0;
}
if(array[i]<0)
{
mintemp = array[i];
}
else{
mintemp = 0;
}
if(maxtemp>= max)
max = maxtemp;
if(mintemp>= min)
min = mintemp;
}
return max == 0? min:max;
}
}
面试题31:整数中1出现的次数 ***
题目:求出113的整数中1出现的次数,并算出1001300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。
思路:
首先要知道以下的规律:
- 从 1 至 10,在它们的个位数中,任意的 X 都出现了 1 次。
- 从 1 至 100,在它们的十位数中,任意的 X 都出现了 10 次。
- 从 1 至 1000,在它们的千位数中,任意的 X 都出现了 100 次。
接下来以 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 的出现次数。
public class Solution {
public int NumberOf1Between1AndN_Solution(int n) {
int low,cur,temp,i=1;
int high = n;//记录高位数
int total = 0; //总的1的数量
if(n < 1)
return 0;
while(high!=0) {
//记忆2593 此时i=2,依次拆分25 9 3
high = n/powerBaseof10(i);//// 获取第i位的高位
temp = n%powerBaseof10(i);
cur = temp/powerBaseof10(i-1);// 获取第i位
low = temp%powerBaseof10(i-1);// 获取第i位的低位
if(cur ==1) {
total += high * powerBaseof10(i-1) + low +1;
}
else if (cur > 1) {
total += (high + 1) * powerBaseof10(i -1);
}
else {
total += high * powerBaseof10(i-1);
}
i++;
}
return total;
}
//就是求10^base次方
public int powerBaseof10(int base) {
return (int)Math.pow(10, base);
}
}
面试题32:把数组排成最小的数
题目:输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
思路:关键在于自定义一个比较大小的函数,比较两个字符串s1, s2大小的时候,先将它们拼接起来,比较s1+s2,和s2+s1那个大,如果s1+s2大,那说明s2应该放前面,所以按这个规则,s2就应该排在s1前面。
代码:运行时间:18ms 占用内存:9532k
import java.util.ArrayList;
public class Solution {
public String PrintMinNumber(int [] numbers) {
StringBuffer result = new StringBuffer();
int i = 0,j = 0;
int a = 0,b = 0;
for(i = 0;i<numbers.length;i++)
{
for(j=i+1;j<numbers.length;j++)
{
a = Integer.valueOf(numbers[i] +""+numbers[j]);
b = Integer.valueOf(numbers[j] +""+numbers[i]);
if(a>b)
{
int temp = numbers[i];
numbers[i]= numbers[j];
numbers[j] = temp;
}
}
}
for(i = 0;i<numbers.length;i++)
result.append(numbers[i]);
return result.toString();
}
}
面试题33:丑数
题目:把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
思路:1、最笨的方法,先判断这个数是不是丑数,然后从1开始遍历,满足要求count++,当count == N,此时的数即是第n个要求的数。会提示算法过于复杂,运算时间过长。
2、一个丑数的因子只有2,3,5,那么丑数p = 2 ^ x * 3 ^ y * 5 ^ z,换句话说一个丑数一定由另一个丑数乘以2或者乘以3或者乘以5得到,那么我们从1开始乘以2,3,5,就得到2,3,5三个丑数,在从这三个丑数出发乘以2,3,5就得到4,6,10,6,9,15,10,15,25九个丑数,我们发现这种方法会得到重复的丑数,而且我们题目要求第N个丑数,这样的方法得到的丑数也是无序的。那么我们可以维护三个队列:
(1)丑数数组: 1
乘以2的队列:2
乘以3的队列:3
乘以5的队列:5
选择三个队列头最小的数2加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列;
(2)丑数数组:1,2
乘以2的队列:4
乘以3的队列:3,6
乘以5的队列:5,10
选择三个队列头最小的数3加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列;
(3)丑数数组:1,2,3
乘以2的队列:4,6
乘以3的队列:6,9
乘以5的队列:5,10,15
选择三个队列头里最小的数4加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列;
(4)丑数数组:1,2,3,4
乘以2的队列:6,8
乘以3的队列:6,9,12
乘以5的队列:5,10,15,20
选择三个队列头里最小的数5加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列;
(5)丑数数组:1,2,3,4,5
乘以2的队列:6,8,10,
乘以3的队列:6,9,12,15
乘以5的队列:10,15,20,25
选择三个队列头里最小的数6加入丑数数组,但我们发现,有两个队列头都为6,所以我们弹出两个队列头,同时将12,18,30放入三个队列;
代码:运行时间:16ms 占用内存:9376k 巧妙的很
import java.util.ArrayList;
public class Solution {
public int GetUglyNumber_Solution(int index) {
if(index<1) return 0;
if(index == 1) return 1;
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(1);
int u2 = 0,u3 = 0,u5 = 0;
int m2 = 0,m3 = 0,m5 = 0;
while(list.size()<index)
{
m2 = list.get(u2)*2;
m3 = list.get(u3)*3;
m5 = list.get(u5)*5;
int min = Math.min(m2,Math.min(m3,m5));
if(min == m2) u2++;
if(min == m3) u3++;
if(min == m5) u5++;
list.add(min);
}
return list.get(list.size() -1);
}
}
面试题34:第一个只出现一次的字符
题目:在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).
思路:先遍历一遍所有字符串,并将它们的次数存放在hashmap中,再遍历一遍,查找是否hashmap中有当前遍历的的字符是value是1的,有则返回字符串中的位置。
代码:运行时间:35ms 占用内存:9976k
import java.util.HashMap;
public class Solution {
public int FirstNotRepeatingChar(String str) {
HashMap list = new HashMap();
int temp = 0;
for(int i = 0; i<str.length();i++)
{
if(list.containsKey(str.charAt(i)))
{
temp = (int)list.get(str.charAt(i));
list.remove(str.charAt(i));
list.put(str.charAt(i),temp+1);
}
else
{
list.put(str.charAt(i),1);
}
}
for(int i = 0;i<str.length();i++)
{
if((int)list.get(str.charAt(i)) == 1)
return i;
}
return -1;
}
}
面试题35:数组中的逆序对
题目:在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
思路:
代码:
面试题36:两个链表的第一个公共结点
题目:输入两个链表,找出它们的第一个公共结点。
思路:
注意:while((!stack1.isEmpty())&&(!stack2.isEmpty())&&stack1.peek() == stack2.peek())是不同于
while(stack1.peek() == stack2.peek()&&(!stack1.isEmpty())&&(!stack2.isEmpty())),要先判断是否为空,再使用peek(),否则会提示空异常
代码:运行时间:18ms占用内存:9652k
public class Solution {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2)
{
if(pHead1 == null ||pHead2 == null) return null;
Stack stack1 = new Stack();
Stack stack2 = new Stack();
ListNode tempHead1 = pHead1,tempHead2 = pHead2;
ListNode temp = null;
while(tempHead1 != null)
{
stack1.push(tempHead1);
tempHead1 = tempHead1.next;
}
while(tempHead2 != null)
{
stack2.push(tempHead2);
tempHead2 = tempHead2.next;
}
while((!stack1.isEmpty())&&(!stack2.isEmpty())&&stack1.peek() == stack2.peek())
{
temp = (ListNode)stack1.pop();
stack2.pop();
}
return temp;
}
}
面试题37:数字在排序数组中的次数
题目:统计一个数字在排序数组中出现的次数。
思路:我的思路:先用二分法找到一个排序数组中i,array[i]等于要找的数,然后i向下和向上查找等于查找的数次数,eclipse运行感觉没问题,牛客提示运行超时???
遍历所有的数组元素倒是没有提示错误,太阳了猫
代码:
public int GetNumberOfK(int [] array , int k) {
if(array == null) return 0;
if(k<array[0]) return 0;
if(k>array[array.length - 1]) return 0;
int low = 0,high = array.length - 1;
int count = 0,left = 0,right = 0;
while(low< high)
{
int i = (low+high)/2+low;
if(array[i] <k)
{
low = i;
}
else if(array[i] >k)
{
high = i;
}
else if(array[i] == k)
{
right = i;
break;
}
}
left = right - 1;
while(left>0&&array[left] == k)
{
left --;
count ++;
}
while((right<array.length)&&array[right] == k)
{
right ++;
count++;
}
return count;
}
遍历所有元素:
public int GetNumberOfK(int [] array , int k) {
int count=0;
for(int i=0;i<array.length;i++){
if(array[i]==k)
count++;
}
return count;
}
面试题38:二叉树的深度
题目:输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
思路:后序遍历每一个结点,若碰到叶结点返回。一个结点后序遍历后,返回该结点的左子树深度与右子树深度最大值+1.
代码:运行时间:14ms 占用内存:9400k
public class Solution {
public int TreeDepth(TreeNode root) {
if(root == null) return 0;
int left = TreeDepth(root.left);
int right = TreeDepth(root.right);
return Math.max(left,right)+1;
}
}
面试题39:判断是否为平衡二叉树 ****
题目:输入一棵二叉树,判断该二叉树是否是平衡二叉树。
思路:如果一个结点的左右深度之差小于等于1,那么这个结点是平衡二叉树的一个结点,遍历所有的结点,如果都是那么即为平衡二叉树。首先要会写求二叉树的深度递归,其次当遍历所有的结点时,遍历到结点不存在时,要返回true,唯一返回false的地方是进行左右深度处判断,有一个结点深度不满足,返回false,递归所有的结果与运算返回false。
代码:运行时间:16ms 占用内存:9280k
public class Solution {
public boolean IsBalanced_Solution(TreeNode root) {
if(root == null) return true;
return Math.abs(getRootDepth(root.left) - getRootDepth(root.right))<=1
&&IsBalanced_Solution(root.left)&&IsBalanced_Solution(root.right);
}
public int getRootDepth(TreeNode root)
{
if(root == null) return 0;
int left = getRootDepth(root.left);
int right = getRootDepth(root.right);
return 1+Math.max(left,right);
}
}
面试题40:数组中只出现一次的数字
题目:一个整型数组里除了两个数字之外,其他的数字都出现了偶数次。请写程序找出这两个只出现一次的数字。
思路:容易想到的思路:使用堆栈来做辅助功能,将数组先排序,依次入栈,每一次数组入栈时和当前堆栈的栈头比较,如果当前堆栈为空,就入栈,如果和当前栈头的元素相同就出栈,当数组中左右元素都入栈完毕,那么当前栈中剩余的2个元素就是只出现一次的两个元素
代码:运行时间:17ms 占用内存:9556k
import java.util.Arrays;
import java.util.Stack;
public class Solution {
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
if(array == null) return ;
Arrays.sort(array);
int i = 0;
Stack stack = new Stack();
for(i = 0;i<array.length;i++)
{
if(stack.isEmpty())
{
stack.push(array[i]);
}
else if(array[i] != (int)stack.peek())
{
stack.push(array[i]);
}
else if(array[i] == (int)stack.peek())
{
stack.pop();
}
}
num1[0] = (int)stack.pop();
num2[0] = (int)stack.pop();
}
}