1. 二叉搜索树与双向链表
题目描述
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
链接:https://www.nowcoder.com/questionTerminal/947f6eb80d944a84850b0538bf0ec3a5
来源:牛客网
方法一:非递归版
解题思路:
1.核心是中序遍历的非递归算法。
2.修改当前遍历节点与前一遍历节点的指针指向。
import java.util.Stack;
public TreeNode ConvertBSTToBiList(TreeNode root) {
if(root==null)
return null;
Stack<TreeNode> stack = new Stack<TreeNode>();
TreeNode p = root;
TreeNode pre = null;// 用于保存中序遍历序列的上一节点
boolean isFirst = true;
while(p!=null||!stack.isEmpty()){
while(p!=null){
stack.push(p);
p = p.left;
}
p = stack.pop();
if(isFirst){
root = p;// 将中序遍历序列中的第一个节点记为root
pre = root;
isFirst = false;
}else{
pre.right = p;
p.left = pre;
pre = p;
}
p = p.right;
}
return root;
}
方法二:递归版
链接:https://www.nowcoder.com/questionTerminal/947f6eb80d944a84850b0538bf0ec3a5
来源:牛客网
//直接用中序遍历
public class Solution {
TreeNode head = null;
TreeNode realHead = null;
public TreeNode Convert(TreeNode pRootOfTree) {
ConvertSub(pRootOfTree);
return realHead;
}
private void ConvertSub(TreeNode pRootOfTree) {
if(pRootOfTree==null) return;
ConvertSub(pRootOfTree.left);
if (head == null) {
head = pRootOfTree;
realHead = pRootOfTree;
} else {
head.right = pRootOfTree;
pRootOfTree.left = head;
head = pRootOfTree;
}
ConvertSub(pRootOfTree.right);
}
}
2. 复杂链表的复制
题目描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
链接:https://www.nowcoder.com/questionTerminal/f836b2c43afc4b35ad6adc41ec941dba
来源:牛客网
/*
*解题思路:
*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;
}
}
3. 字符串的排列
题目描述
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
输入描述:
输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。
链接:https://www.nowcoder.com/questionTerminal/fe6b651b66ae47d7acce78ffdd9a96c7
来源:牛客网
基于回溯法思想:
import java.util.List;
import java.util.Collections;
import java.util.ArrayList;
public class Solution {
public static void main(String[] args) {
Solution p = new Solution();
System.out.println(p.Permutation("abc").toString());
}
public ArrayList<String> Permutation(String str) {
List<String> res = new ArrayList<>();
if (str != null && str.length() > 0) {
PermutationHelper(str.toCharArray(), 0, res);
Collections.sort(res);
}
return (ArrayList)res;
}
public void PermutationHelper(char[] cs, int i, List<String> list) {
if (i == cs.length - 1) {
String val = String.valueOf(cs);
if (!list.contains(val))
list.add(val);
} else {
for (int j = i; j < cs.length; j++) {
swap(cs, i, j);
PermutationHelper(cs, i+1, list);
swap(cs, i, j);
}
}
}
public void swap(char[] cs, int i, int j) {
char temp = cs[i];
cs[i] = cs[j];
cs[j] = temp;
}
}
链接:https://www.nowcoder.com/questionTerminal/fe6b651b66ae47d7acce78ffdd9a96c7
来源:牛客网
import java.util.ArrayList;
import java.util.List;
import java.util.Collections;
public class Solution {
public ArrayList<String> Permutation(String str) {
List<String> resultList = new ArrayList<>();
if(str.length() == 0)
return (ArrayList)resultList;
//递归的初始值为(str数组,空的list,初始下标0)
fun(str.toCharArray(),resultList,0);
Collections.sort(resultList);
return (ArrayList)resultList;
}
private void fun(char[] ch,List<String> list,int i){
//这是递归的终止条件,就是i下标已经移到char数组的末尾的时候,考虑添加这一组字符串进入结果集中
if(i == ch.length-1){
//判断一下是否重复
if(!list.contains(new String(ch))){
list.add(new String(ch));
return;
}
}else{
//这一段就是回溯法,这里以"abc"为例
//递归的思想与栈的入栈和出栈是一样的,某一个状态遇到return结束了之后,会回到被调用的地方继续执行
//1.第一次进到这里是ch=['a','b','c'],list=[],i=0,我称为 状态A ,即初始状态
//那么j=0,swap(ch,0,0),就是['a','b','c'],进入递归,自己调自己,只是i为1,交换(0,0)位置之后的状态我称为 状态B
//i不等于2,来到这里,j=1,执行第一个swap(ch,1,1),这个状态我称为 状态C1 ,再进入fun函数,此时标记为T1,i为2,那么这时就进入上一个if,将"abc"放进list中
/-------》此时结果集为["abc"]
//2.执行完list.add之后,遇到return,回退到T1处,接下来执行第二个swap(ch,1,1),状态C1又恢复为状态B
//恢复完之后,继续执行for循环,此时j=2,那么swap(ch,1,2),得到"acb",这个状态我称为C2,然后执行fun,此时标记为T2,发现i+1=2,所以也被添加进结果集,此时return回退到T2处往下执行
/-------》此时结果集为["abc","acb"]
//然后执行第二个swap(ch,1,2),状态C2回归状态B,然后状态B的for循环退出回到状态A
// a|b|c(状态A)
// |
// |swap(0,0)
// |
// a|b|c(状态B)
// / \
// swap(1,1)/ \swap(1,2) (状态C1和状态C2)
// / \
// a|b|c a|c|b
//3.回到状态A之后,继续for循环,j=1,即swap(ch,0,1),即"bac",这个状态可以再次叫做状态A,下面的步骤同上
/-------》此时结果集为["abc","acb","bac","bca"]
// a|b|c(状态A)
// |
// |swap(0,1)
// |
// b|a|c(状态B)
// / \
// swap(1,1)/ \swap(1,2) (状态C1和状态C2)
// / \
// b|a|c b|c|a
//4.再继续for循环,j=2,即swap(ch,0,2),即"cab",这个状态可以再次叫做状态A,下面的步骤同上
/-------》此时结果集为["abc","acb","bac","bca","cab","cba"]
// a|b|c(状态A)
// |
// |swap(0,2)
// |
// c|b|a(状态B)
// / \
// swap(1,1)/ \swap(1,2) (状态C1和状态C2)
// / \
// c|b|a c|a|b
//5.最后退出for循环,结束。
for(int j=i;j<ch.length;j++){
swap(ch,i,j);
fun(ch,list,i+1);
swap(ch,i,j);
}
}
}
//交换数组的两个下标的元素
private void swap(char[] str, int i, int j) {
if (i != j) {
char t = str[i];
str[i] = str[j];
str[j] = t;
}
}
}
1. 求1+2+3+…+n
题目描述
求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
链接:https://www.nowcoder.com/questionTerminal/7a0da8fc483247ff8800059e12d7caf1
来源:牛客网
解题思路:
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 int Sum_Solution(int n) {
int sum = n;
boolean ans = (n>0)&&((sum+=Sum_Solution(n-1))>0);
return sum;
}
链接:https://www.nowcoder.com/questionTerminal/7a0da8fc483247ff8800059e12d7caf1
来源:牛客网
//用异常退出递归
public class Solution {
public int Sum_Solution(int n) {
return sum(n);
}
int sum(int n){
try{
int i = 1%n;
return n+sum(n-1);
}
catch(Exception e){
return 0;
}
}
}
2. 不用加减乘除做加法
题目描述
写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
链接:https://www.nowcoder.com/questionTerminal/59ac416b4b944300b617d4f7f111b215
来源:牛客网
public class Solution {
public int Add(int num1,int num2) {
while (num2!=0) {
int temp = num1^num2;
num2 = (num1&num2)<<1;
num1 = temp;
}
return num1;
}
}
首先看十进制是如何做的: 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为最终结果。
链接:https://www.nowcoder.com/questionTerminal/59ac416b4b944300b617d4f7f111b215
来源:牛客网
13+11 = ?;
13 的二进制 1 1 0 1 -----a 13
11 的二进制 1 0 1 1 -----b 11
(a&b) <<1 -> 1 0 0 1 0 -----d 18
a^b -> 0 1 1 0 -----e 6
(d&e) <<1 -> 0 0 1 0 0 ------f 4
d^e -> 1 0 1 0 0 -----g 20
(f&g) <<1 -> 0 1 0 0 0 ------h 8
f^g -> 1 0 0 0 0 ------i 16
(h&i) <<1 -> 0 0 0 0 0 ------h 0 ---- --------退出循环
h^i -> 1 1 0 0 0 ------i 24
1. 二叉树的镜像
题目描述
操作给定的二叉树,将其变换为源二叉树的镜像。
链接:https://www.nowcoder.com/questionTerminal/564f4c26aa584921bc75623e48ca3011
来源:牛客网
/* 先前序遍历这棵树的每个结点,如果遍历到的结点有子结点,就交换它的两个子节点,
当交换完所有的非叶子结点的左右子结点之后,就得到了树的镜像 */
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public void Mirror(TreeNode root) {
if(root == null)
return;
if(root.left == null && root.right == null)
return;
TreeNode pTemp = root.left;
root.left = root.right;
root.right = pTemp;
if(root.left != null)
Mirror(root.left);
if(root.right != null)
Mirror(root.right);
}
}
链接:https://www.nowcoder.com/questionTerminal/564f4c26aa584921bc75623e48ca3011
来源:牛客网
/**
使用层序遍历的思想
*/
public class Solution {
public void Mirror(TreeNode root) {
if(root == null) // 保证程序的健壮性
return;
// LinkedList实现了Queue接口,所以可将LinkedList当作队列使用
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.add(root);
TreeNode rootNode, leftNode, rightNode;
while(!queue.isEmpty()){
rootNode = queue.poll();
leftNode = rightNode = null;
if(rootNode.left != null){
leftNode = rootNode.left;
queue.add(leftNode);
}
if(rootNode.right != null){
rightNode = rootNode.right;
queue.add(rightNode);
}
rootNode.left = rightNode;
rootNode.right = leftNode;
}
}
}
1. 把字符串转换成整数
题目描述
将一个字符串转换成一个整数(实现Integer.valueOf(string)的功能,但是string不符合数字要求时返回0),要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0。
输入描述:
输入一个字符串,包括数字字母符号,可以为空
输出描述:
如果是合法的数值表达则返回该数字,否则返回0
示例1
输入
+2147483647
1a33
输出
2147483647
0
链接:https://www.nowcoder.com/questionTerminal/1277c681251b4372bdef344468e4f26e
来源:牛客网
public class Solution {
public static boolean flag;
public static int StrToInt(String str) {
flag = false;
//判断输入是否合法
if (str == null || str.trim().equals("")) {
flag = true;
return 0;
}
// symbol=0,说明该数为正数;symbol=1,该数为负数;start用来区分第一位是否为符号位
int symbol = 0;
int start = 0;
char[] chars = str.trim().toCharArray();
if (chars[0] == '+') {
start = 1;
} else if (chars[0] == '-') {
start = 1;
symbol = 1;
}
int result = 0;
for (int i = start; i < chars.length; i++) {
if (chars[i] > '9' || chars[i] < '0') {
flag = true;
return 0;
}
int sum= result * 10 + (int) (chars[i] - '0');
if((sum-(int) (chars[i] - '0'))/10!=result){ //这个用来判断溢出,如果溢出是不会相等的
flag=true;
return 0;
}
result=result * 10 + (int) (chars[i] - '0');
/*
* 本人认为java热门第一判断是否溢出是错误的,举个反例
* 当输入为value=2147483648时,在计算机内部的表示应该是-2147483648
* 显然value>Integer.MAX_VALUE是不成立的
*/
}
// 注意:java中-1的n次方不能用:(-1)^n .'^'异或运算
// 注意,当value=-2147483648时,value=-value
result = (int) Math.pow(-1, symbol) * result;
return result;
}
}