复杂链表的复制
题目:复制一个复杂链表(每个节点有两个指针,一个指向下一个节点,一个指向任意一个节点)。
思路:先复制主链,暂时不管随机指向的指针,在复制主链的过程中,通过Map建立新旧链中每个节点的对应关系,然后再通过一次遍历,在新链中建立随机指向的指针。
public class Solution {
public RandomListNode Clone(RandomListNode pHead)
{
RandomListNode oldHead = pHead;
Map<RandomListNode, RandomListNode> occurs = new HashMap<>();
RandomListNode newHead = new RandomListNode(0);
RandomListNode cur = newHead;
// 仅复制next,并建立主链节点的映射关系
while(pHead != null){
RandomListNode temp = new RandomListNode(pHead.label);
cur.next = temp;
occurs.put(pHead, temp);
cur = cur.next;
pHead = pHead.next;
}
cur = newHead.next;
pHead = oldHead;
// 根据映射关系,建立random连接
while(pHead != null){
cur.random = (RandomListNode)occurs.get(pHead.random);
cur = cur.next;
pHead = pHead.next;
}
return newHead.next;
}
}
重建二叉树
题目:根据前序遍历序列和中序遍历序列重建二叉树。
思路:不断分割前序遍历和中序遍历序列(分割后的前序、中序序列分别都是子树的对应序列),递归构建。
public class Solution {
public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
if(pre == null || in == null){
return null;
}
return build(pre, in, 0, pre.length-1, 0, in.length-1);
}
public TreeNode build(int[] pre, int[] in, int preStart, int preEnd, int inStart, int inEnd){
if(preStart > preEnd || inStart > inEnd){
return null;
}
TreeNode head = new TreeNode(pre[preStart]);
// 从中序遍历里面找当前根节点的index
int index = 0;
for(int i = inStart; i <= inEnd; i++){
if(pre[preStart] == in[i]){
index = i;
}
}
// 计算左子树的长度
int leftLen = index - inStart;
// 计算右子树的长度
int rightLen = inEnd - index;
if(leftLen > 0){
head.left = build(pre, in, preStart+1, preStart+leftLen, inStart, index-1);
}
if(rightLen > 0){
head.right = build(pre, in, preStart+leftLen+1, preEnd, index+1, inEnd);
}
return head;
}
}
树的子结构
题目:判断二叉树B是不是A的子树。
思路:分别求A、B的前序遍历序列,判断A的序列是否包含B的序列。
public class Solution {
public boolean HasSubtree(TreeNode root1,TreeNode root2) {
if(root1 == null || root2 == null){
return false;
}
StringBuilder sb1 = new StringBuilder();
StringBuilder sb2 = new StringBuilder();
sequence(root1, sb1);
sequence(root2, sb2);
return sb1.toString().contains(sb2.toString());
}
public void sequence(TreeNode root, StringBuilder sb){
if(root == null){
return;
}
sb.append(root.val);
sequence(root.left, sb);
sequence(root.right, sb);
}
}
二叉树的镜像
题目:求给定二叉树的镜像二叉树。
思路:设原二叉树为A,镜像树为B,则A.left = B.right && A.right = B.left,故,递归交换即可。
public class Solution {
public void Mirror(TreeNode root) {
if(root == null){
return;
}
TreeNode temp = root.left;
root.left = root.right;
root.right = temp;
Mirror(root.left);
Mirror(root.right);
}
}
二叉树中和为某一值的路径
题目:计算二叉树从根节点到叶子节点,每一条路径上的节点值之和为给定数的路径。
思路:前序遍历二叉树,克隆List分别记录每条路径经过的节点,当到达叶子节点时累加List中节点的值,如果为给定目标数,则将其加入结果集合。
public class Solution {
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
ArrayList<ArrayList<Integer>> result = new ArrayList<>();
ArrayList<Integer> path = new ArrayList<>();
if(root == null){
return result;
}
find(root, target, result, (ArrayList<Integer>)path.clone());
return result;
}
public void find(TreeNode root, int target, ArrayList<ArrayList<Integer>> result, ArrayList<Integer> path){
path.add(root.val);
if(root.left == null && root.right == null){
// 是叶子节点
int sum = 0;
for(int i = 0; i < path.size(); i++){
sum += (int)path.get(i);
}
if(sum == target){
result.add(path);
}
return;
}
if(root.left != null){
find(root.left, target, result, (ArrayList<Integer>)path.clone());
}
if(root.right != null){
find(root.right, target, result, (ArrayList<Integer>)path.clone());
}
}
}
二叉搜索树与双向链表
题目:将二叉搜索树转化为双向链表。
思路:中序遍历后的二叉搜索书是有序的,所以在中序遍历的过程中修改指针指向即可。
public class Solution {
private TreeNode cur = null;
private TreeNode head = null;
public TreeNode Convert(TreeNode pRootOfTree) {
convert(pRootOfTree);
return head;
}
private void convert(TreeNode node){
if(node == null){
return;
}
convert(node.left);
if(cur == null){
// 头结点
cur = node;
head = node;
} else {
// 非头结点
cur.right = node;
node.left = cur;
cur = node;
}
convert(node.right);
}
}
字符串的排列
题目:输入一个字符串,按字典序打印出该字符串中字符的所有排列。
思路:依次让每一个字母成为字符串的首字母,并对剩余的子串进行同样的操作。
public class Solution {
public ArrayList<String> Permutation(String str) {
ArrayList<String> result = new ArrayList<>();
if(str == null || str.length() == 0){
return result;
}
TreeSet<String> treeset = new TreeSet<>();
getResult(str.toCharArray(), 0, str.length()-1, treeset);
result.addAll(treeset);
return result;
}
public void getResult(char[] chars, int start, int end, TreeSet<String> treeset){
if(start == end){
treeset.add(String.valueOf(chars));
} else {
for(int i = start; i <= end; i++){
// 换过去
swap(chars, start, i);
// 递归后面的子串
getResult(chars, start+1, end, treeset);
// 换回来
swap(chars, start, i);
}
}
}
public void swap(char[] chars, int pos1, int pos2){
char temp = chars[pos1];
chars[pos1] = chars[pos2];
chars[pos2] = temp;
}
}
变态跳台阶
题目:一次可以跳1~n级台阶,求跳上n级台阶的方法总数。
思路:2 * jump(n-1)
public class Solution {
public int JumpFloorII(int target) {
if(target <= 2){
return target;
}
return 2*JumpFloorII(target-1);
}
}
顺时针打印矩阵
题目:从左上角开始,顺时针打印二维数组。
思路:设置上下左右4个边界,分四次向不同方向打印,每次打印完一行或一列,缩小边界,直到打印完所有元素。
public class Solution {
public ArrayList<Integer> printMatrix(int [][] matrix) {
ArrayList<Integer> list = new ArrayList<>();
int len = matrix.length * matrix[0].length;
int leftBound = 0;
int rightBound = matrix[0].length-1;
int bottomBound = matrix.length-1;
int topBound = 0;
int curX = 0;
int curY = 0;
while(curY <= rightBound && list.size() < len){
// 从左向右走过一行
while(curY <= rightBound && leftBound <= rightBound && list.size() < len){
list.add(matrix[curX][curY]);
if(curY < rightBound){
// 当前行没走完,继续走
curY++;
} else {
// 当前行走完了,往下一行
curX++;
// 上边界缩小
topBound++;
break;
}
}
// 从上到下走过一列
while(curX <= bottomBound && topBound <= bottomBound && list.size() < len){
list.add(matrix[curX][curY]);
if(curX < bottomBound){
curX++;
} else {
curY--;
// 右边界缩小
rightBound--;
break;
}
}
// 从右向左走过一行
while(curY >= leftBound && leftBound <= rightBound && list.size() < len){
list.add(matrix[curX][curY]);
if(curY > leftBound){
curY--;
} else {
curX--;
// 下边界缩小
bottomBound--;
break;
}
}
// 从下向上走过一列
while(curX >= topBound && topBound <= bottomBound && list.size() < len){
list.add(matrix[curX][curY]);
if(curX > topBound){
curX--;
} else {
curY++;
// 左边界缩小
leftBound++;
break;
}
}
}
return list;
}
}
连续子数组的最大和
题目:求一个数组中连续的子数组的最大和。
public class Solution {
public int FindGreatestSumOfSubArray(int[] array) {
int max = array[0];
int sum = array[0];
for(int i = 1; i < array.length; i++){
sum = Math.max(sum + array[i], array[i]);
if(sum > max){
max = sum;
}
}
return max;
}
}
丑数
题目:只包含质因子2、3、5的数称作丑数,求第N个丑数。
思路:每个丑数都可由较小的某一个丑数乘以2或3或5得到,因此分别跟踪乘2、乘3、乘5的index,每个index所指向的那个丑数对应乘以2或3或5中最小的那个数,就是下一个丑数。
public class Solution {
public int GetUglyNumber_Solution(int index) {
if (index <= 0) {
return 0;
}
int multi2 = 0;
int multi3 = 0;
int multi5 = 0;
int[] arr = new int[index];
arr[0] = 1;
for(int i = 1; i < index; i++){
int min = Math.min(Math.min(arr[multi2] * 2, arr[multi3] * 3), arr[multi5] * 5);
arr[i] = min;
if(arr[multi2] * 2 == arr[i]){
multi2++;
}
if(arr[multi3] * 3 == arr[i]){
multi3++;
}
if(arr[multi5] * 5 == arr[i]){
multi5++;
}
}
return arr[index - 1];
}
}