牛客链接
描述:
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度,根节点的深度视为 1 。
方法一:使用递归方式
分别遍历根的左和根的右,然后取两边中较大的一边,最后再加上有根节点的那一层就是整棵树的深度。
public class Solution {
public int TreeDepth(TreeNode root) {
if(root == null){
return 0;
}
int leftTree = TreeDepth(root.left);
int rightTree = TreeDepth(root.right);
return (leftTree > rightTree) ? leftTree + 1 : rightTree + 1;
}
}
方法二:分别遍历左子树高度和右子树高度,然后取两边最大值即可。
public class Solution {
public int max = 0;
public void TreeNodeHelper(TreeNode root,int cur){
if(root == null){
if(max < cur){
max = cur;
}
return;
}
TreeNodeHelper(root.left,cur + 1);
TreeNodeHelper(root.right,cur + 1);
}
public int TreeDepth(TreeNode root) {
if(root == null){
return 0;
}
int depth = 0;
TreeNodeHelper(root,depth);
return max;
}
}
方法三:层序遍历。利用二叉树的层序遍历,遍历完一层之后,深度加1,直到遍历完整颗二叉树。
public class Solution {
public int TreeDepth(TreeNode root) {
if(root == null){
return 0;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int depth = 0;
while(!queue.isEmpty()){
int size = queue.size();
depth++;
while(size != 0){
TreeNode cur = queue.poll();
if(cur.left != null){
queue.offer(cur.left);
}
if(cur.right != null){
queue.offer(cur.right);
}
size--;
}
}
return depth;
}
}
牛客链接
描述:
一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
解题思路:先将整个数组进行异或,得到那两个只出现一次数字进行以异或的结果,然后将该数字与1进行一位与运算,得到这两个数字第一次出现不同的位置,以该位置为标志位重新对整个数组进行遍历,数组中的每个元素都与其进行与运算,得到两字不用的结果,其结果就是该数组中只出现一次的两个数字。
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
if(array == null || num1 == null || num2 == null){
return;
}
int res = array[0];
num1[0] = 0;
num2[0] = 0;
//得到的是两个不同的数进行异或的结果,因为相同的两个数进行异或结果为0
for(int i = 1;i < array.length;i++){
res ^= array[i];
}
int len = Integer.SIZE;//得到int类型有多少个比特位
int flg = 1;
while(len >= 0){
len --;
//找到两位不同的数异或得到的数中第一个为1的位置,并记录在flg中
if(((flg << len) & res) != 0){
flg = flg << len;
break;
}
}
//以标志位为基准,遍历数组,得到只出现一次的两个数字
for(int i = 0;i < array.length;i++){
if((flg & array[i]) == 0){
num1[0] ^= array[i];
}else{
num2[0] ^= array[i];
}
}
}
}
牛客链接
描述:
小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22
。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列?
返回值描述:
输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序。
解题思路:
不考虑负数情况,定义两个位置,表示起始和结束,start和end之间就是一段连续递增的序列;这两个位置,相当于动态窗口的两边,根据其窗口内的值的和来确定窗口的位置和大小。我们采用闭区间方式进行解决。
import java.util.ArrayList;
public class Solution {
public ArrayList<ArrayList<Integer>> FindContinuousSequence(int sum) {
ArrayList<ArrayList<Integer>> res = new ArrayList<>();
if(sum == 0){
return res;
}
//分别用left和right标记连续序列的前两个数
int left = 1;
int right = 2;
while(left < right){
//连续序列求和公式,属于闭区间
int total = (left + right) * (right - left + 1) / 2;
if(total == sum){
ArrayList<Integer> list = new ArrayList<>();
for(int i = left;i <= right;i++){
list.add(i);
}
res.add(list);
//从保存序列的下一位left重新开始遍历
left++;
}else if(total < sum){
//说明该序列区间中的数据和小于sum,应该扩大区间,以包含更多数据
right++;
}else{
//说明该序列区间中的数据和大于sum,应该缩小区间,以包含较少数据
left++;
}
}
return res;
}
}
牛客链接
描述:
汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列 S ,请你把其循环左移 K 位后的序列输出。例如,字符序列 S = ”abcXYZdef”
, 要求输出循环左移 3
位后的结果,即 “XYZdefabc”
。是不是很简单?OK,搞定它!
方法一:字符串座左移1位的方法就是,取出字符串中的第一个元素,然后将后面的元素挨个往前移动一位,再把取出的第一位元素放到最后一位即可。然后需要左移几位,就循环调用几次即可。
public class Solution {
public void LeftRotateStringHelper(char[] str){
char tmp = str[0];
int i = 0;
for( i = 0; i < str.length - 1;i++){
str[i] = str[i + 1];
}
str[i] = tmp;
}
public String LeftRotateString(String str,int n) {
if(str.length() == 0 || n ==0){
return str;
}
//将字符串转换为字符数组
char[] s = str.toCharArray();
//如果n过大,则很多左移是重复的,可以在这对其求余
n %= str.length();
while(n > 0){
LeftRotateStringHelper(s);
n--;
}
return new String(s);
}
}
方法二:先逆置字符串的前n个,再逆置剩下的字符串。然后再整体进行逆置同样可以达到左旋n个字符串的效果。
如:abcXYZdef
字符串需要左旋三位得到XYZdefabc
,我们就可以:
1.逆置前三位:cba
;
2.逆置字符串剩下的元素:fedZYX
,此事得到的字符串就是:cbafedZYX
;
3.再对2得到的字符串进行整体逆置:XYZdefabc
。
public class Solution {
//字符串逆置
public void Rverse(char[] s,int start,int end){
while(start < end){
char tmp = s[start];
s[start] = s[end];
s[end] = tmp;
start++;
end--;
}
}
public String LeftRotateString(String str,int n) {
if(str.length() == 0 || n == 0){
return str;
}
n %= str.length();
char[] s = str.toCharArray();
Rverse(s,0,n - 1);
Rverse(s,n,s.length - 1);
Rverse(s,0,s.length - 1);
return new String(s);
}
}
牛客链接
描述:
牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“nowcoder. a am I”
。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a nowcoder.
”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?
**解题思路:**先局部逆置,再整体逆置。
首先遍历数组,直到遇到空格就说明此时将一个单词遍历完毕,对这个单词进行逆置操作,以此方式遍历、逆置完整个数组,最后再将整个数组进行逆置即可。
如:“nowcoder. a am I”
.redocwon a ma I
,然后对其整体进行逆置得:I am a nowcoder.
。public class Solution {
public void Reverse(char[] s,int start,int end){
while(start < end){
char tmp = s[start];
s[start] = s[end];
s[end] = tmp;
start++;
end--;
}
}
public String ReverseSentence(String str) {
if(str == null || str.length() == 0){
return str;
}
int i = 0;
int j = i;
//将字符串转换为字符数组
char[] list = str.toCharArray();
int len = list.length;
while(i < len){
//遍历字符数组,直到遇到第一个空格
while(i < len && !Character.isSpace(list[i])) {
i++;
}
Reverse(list,j,i - 1);
//再次遍历,避免出现两个空格的情况
//过滤所有空格,指向下一个有效子串的开始
while(i < len && Character.isSpace(list[i])){
i++;
}
//保存起始位置
j = i;
}
//此时i指向的是最后一个单词的最后位置,它还未进行翻转,
//这里需要将最后一个单词翻转
Reverse(list, j, i-1);
Reverse(list, 0, i-1);
return new String(list);
}
}
牛客链接
描述:
给定一个二叉树,返回该二叉树的之字形层序遍历,(第一层从左向右,下一层从右向左,一直这样交替)。
解题思路:
之字形打印,本质也是对树形结构的层序遍历,不过在遍历的过程中,需要更改遍历顺序,可以采用stack
和queue
的方式来进行处理。当前层从左向右遍历,那么下层就从left到right入栈,当前层如果从右向左遍历,那么下层就从right到
left入栈。
根据栈先进后出的思想来对二叉树进行层序遍历
import java.util.*;
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> res = new ArrayList<>();
if(pRoot == null){
return res;
}
//保存要遍历的节点
Stack<TreeNode> stack = new Stack<>();
//作为临时队列来进行辅助
Queue<TreeNode> queue = new LinkedList<>();
ArrayList<Integer> list = new ArrayList<>();
stack.push(pRoot);
int flg = 1;//1表示从左向右,2表示从右向左
while(!stack.isEmpty()){
int size = stack.size();
//清空本轮stack结构,并遍历,stack本身有逆序的功能
for(int i = 0;i < size;i++){
TreeNode cur = stack.pop();
list.add(cur.val);
TreeNode first = (flg == 1) ? cur.left : cur.right;
TreeNode sceond = (flg== 1) ? cur.right : cur.left;
//将下一轮访问顺序放入q中
if(first != null){
queue.offer(first);
}
if(sceond != null){
queue.offer(sceond);
}
}
res.add(new ArrayList(list));//浅拷贝
list.clear();
//将下一轮访问节点入栈,进行逆序
while(!queue.isEmpty()){
stack.push(queue.poll());
}
//一层遍历完之后,改变入栈顺序
flg = (flg == 1) ? 2 : 1;
}
return res;
}
}
牛客链接
给定一棵二叉搜索树,请找出其中的第k小的结点。例如,(5,3,7,2,4,6,8)
中,按结点数值大小顺序第三小结点的值为4。
解题思路:BST
本身就是有序的,中序遍历即是升序;要求第k小,即中序遍历时到达第k个元素(二叉搜索树,不存在两个相同的节点值),此处,我们采用循环中序遍历的方式。
import java.util.Stack;
//BST本身就是有序的,中序遍历即是升序
//要求第k小,即中序遍历时到达第k个元素(二叉搜索树,不存在两个相同的节点值)
//此处,我们不使用递归,我们采用循环中序遍历的方式
public class Solution {
TreeNode KthNode(TreeNode pRoot, int k){
if(pRoot == null || k <= 0){
return null;
}
Stack<TreeNode> st = new Stack<>();
TreeNode node = pRoot;
do{
while(node != null){
st.push(node);
node = node.left;
}
if(!st.empty()){
node = st.pop();
//访问当前节点,中序
k--;
if(k == 0){
return node;//找到当前第k小节点
}
node = node.right;
}
}while(node != null || !st.empty());
//node有可能为空,但是只要stack不为空,就要继续pop求下一个。
//有没有可能st为空?有可能,这个时候就要检测node,如果node不为空,就要整体检测右子树
//走到这里,就说明没有找到
return null;
}
}
以上。