最全剑指offer刷题总记——Java(1),讲的太透彻了

总结

在清楚了各个大厂的面试重点之后,就能很好的提高你刷题以及面试准备的效率,接下来小编也为大家准备了最新的互联网大厂资料。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

        //栈2空,需要从栈1取数据

        while(!stack1.empty()){

            //栈1栈顶入栈栈2

            //pop方法取栈顶并出栈,peek方法仅取栈顶

            stack2.push(stack1.pop());

        }

    }

    //栈2不空,直接从栈2出栈即可,所以返回栈2栈顶

    return stack2.pop();

}

}




* * *



[]( )JZ6 旋转数组的最小数字

============================================================================



题目:[旋转数组的最小数字]( )



*   暴力求解,循环遍历



import java.util.ArrayList;

public class Solution {

//暴力求解,遍历数组

public int minNumberInRotateArray(int [] array) {

    //第一个数值开始

    int min = array[0];

    //循环遍历

    for(int i = 1;i<array.length;i++){

        //比较最小值

        if(array[i] <= min){

            min = array[i];

        }

    }

    //返回最小值

    return min;

}

}




*   二分查找



import java.util.ArrayList;

public class Solution {

// 二分查找

public int minNumberInRotateArray(int[] array) {

	// 数组大小为0

	if (array.length == 0)

		return 0;

	// 定义数组左边界

	int left = 0;

	// 定义数组右边界

	int right = array.length - 1;

	// 定义基准

	int target;

	// 定义中间值

	int mid;

	// 循环查找,条件为左界小于右界

	while (left < right) {

		// 去最右边作为基准,随着边界改变而改变

		target = array[right];

		// 中值随边界变化而变

		mid = (left + right) / 2;

		// 最右边的值大于中间的

		// 本来递增,旋转后得到的非递减

		// 所以最小值一定不在后面,但是mid那个可能是最小的

		if (array[mid] < target) {

			right = mid;

		} else if (array[mid] > target) {

			// 中值大于最后的值

			// 旋转得到表示中值前面的都大于最后的

			// 所以中值后的值中才存在最小的

			left = mid + 1;

		} else {

			// 最后一个值和中值相等

			// 无法判断,去掉最后一个值,继续

			// 因为中值和最后一个相等,去掉最后无碍

			right = right - 1;

		}

	}

	// 最后左右边界相等,即为最小值,返回哪一个边界都可

	// 返回左边界198ms,返回右边界188ms

	// 不懂为什么,但是返回右边界复杂度小

	return array[right];

}

}




* * *



[]( )JZ7 斐波那契数列

=========================================================================



题目:[斐波那契数列]( )



public class Solution {

//斐波那契数列,典型递归调用

public int Fibonacci(int n) {

    //第0项和第1项,为0和1

    if(n==0 || n==1)

        return n;

    else

        //第n项,等于第n-1项和第n-2项的和

        //递归调用,自己调用自己

        return Fibonacci(n-1) + Fibonacci(n-2);

}

}




* * *



[]( )JZ8 跳台阶

======================================================================



题目:[跳台阶]( )



> 同斐波那契数列,递归调用即可。



public class Solution {

public int jumpFloor(int target) {

    //边界条件

    if(target>=0 && target <= 2){

        return target;

    }

    //跳到第n级台阶的跳法等于跳到第n-1级台阶的跳法加上跳到第n-2级台阶的跳法

    //到了n-1级,就只有一种跳法了

    //n-2级,也就剩一种跳法了

    //n-2后,要是一级一级跳,就又是在n-1级里面包含了,可以不考虑

    return jumpFloor(target-1) + jumpFloor(target-2);

}

}




* * *



[]( )JZ9 变态跳台阶

========================================================================



题目:[跳台阶扩展问题]( )



public class Solution {

public int jumpFloorII(int target) {

    //边界条件

    if(target == 0 || target == 1){

        return 1;

    }

    //f(n) = f(n-1) + ......+f(0)

    //f(n-1) = f(n-2) + ......+f(0)

    //相减,所以f(n) = 2*f(n-1)

    //所以是2的n-1次幂

    return 2*jumpFloorII(target - 1);

}

}




* * *



[]( )JZ10 矩形覆盖

========================================================================



题目:[矩形覆盖]( )



*   递推(11ms)



public class Solution {

//递推法

public int rectCover(int target) {

    //边界条件

    if(target == 0 || target == 1 || target == 2){

        return target;

    }

    int res = 0;

    int first = 1;

    int second = 2;

    //循环向后求值

    for(int i=3;i<=target;i++){

        res = first + second;

        first = second;

        second = res;

    }

    return res;

}

}




*   递归(389ms)



public class Solution {

//递归法

public int rectCover(int target) {

    //边界条件

    if(target == 0)

        return 0;

    if(target == 1)

        return 1;

    if(target == 2)

        return 2;

    //递归

    return rectCover(target-2) + rectCover(target-1);

}

}




*   记忆递归(备忘录法)(10ms)



1.  核心代码模式



import java.util.*;

public class Solution {

//声明数组,也可以创建数组,但不能初始化在类文件中

int[] bp = new int[200];

//构造函数中创建并且初始化

//利于类的继承

Solution(){

    for(int i=0;i<bp.length;i++){

        bp[i] = 0;

    }

}

//记忆递归,备忘录法

public int rectCover(int target) {

    //边界条件

    if(target == 1 || target == 2){

        //保存数据

        bp[target] = target;

    }

    //当前值没有计算过,考虑bp[0]的值,那个0为边界,要去掉

    if(bp[target] == 0 && target != 0){

        //递归进行计算

        //计算完毕进行保存

        bp[target] = rectCover(target-2) + rectCover(target-1);

        return bp[target];

    }else{

        //已经计算过该值,直接返回

        return bp[target];

    }

}

}




2.  ACM模式



package com.hnucm;

import java.util.*;

public class Solution {

// 声明数组

static int[] bp;



//记忆递归,备忘录法

public static int rectCover(int target) {

    //边界条件

    if(target == 1 || target == 2){

        //保存数据

        bp[target] = target;

    }

    //当前值没有计算过,考虑bp[0]的值,那个0为边界,要去掉

    if(bp[target] == 0 && target != 0){

        //递归进行计算

        //计算完毕进行保存

        bp[target] = rectCover(target-2) + rectCover(target-1);

        return bp[target];

    }else{

        //已经计算过该值,直接返回

        return bp[target];

    }

}



public static void main(String[] args) {

	Scanner sc = new Scanner(System.in);

	int n;

	bp = new int[200];

	for (int i = 0; i < bp.length; i++) {

		bp[i] = 0;

	}

	while (sc.hasNext()) {

		n = sc.nextInt();

		System.out.println(rectCover(n));

	}

}

}




*   动态规划(11ms)



> **动态规划有点像递归填表,和记忆递归和递推差不多,更像递推填表,参照上面的即可。**



* * *



[]( )JZ11 二进制中1的个数

============================================================================



题目:[二进制中1的个数]( )



public class Solution {

/**

 * 求负数的补码的方法。 注意: 负数的补码是在其原码的基础上,符号位不变,其余位取反,然后加1

 * @param a

 * @author lhever 2017年4月4日 下午8:42:47

 * @since v1.0

 */

//负数

public static int back(int a)

{

    int s = 0;

    for (int i = 0; i < 32; i++)

    {

        // 0x80000000 是一个首位为1,其余位数为0的整数

        int t = (a & 0x80000000 >>> i) >>> (31 - i);

        if(t == 1){

            s++;

        }

    }

    return s;

}



//正数

int m = 0;

public int normal(int a){

    if(a%2 == 1)

        m++;

    if(a/2 == 0)

        return m;

    else

        return normal(a/2);

}



public int NumberOf1(int n) {

    if(n>=0){

        return normal(n);

    }else{

        return back(n);

    }

}

}




* * *



[]( )JZ12 数值的整数次方

===========================================================================



题目:[数值的整数次方]( )



*   递推(36ms)



public class Solution {

//递推

public double Power(double base, int exponent) {

    double s = 1.0;

    if(base == 0){

        return 0;

    }

    else if(exponent == 0){

        return 1;

    }else if(exponent > 0){

        for(int i=0;i<exponent;i++){

            s *= base;

        }

    }else{

      for(int i=0;i<Math.abs(exponent);i++){

          s *= base;

      }

        s = 1.0/s;

    }

    return s;

}

}




* * *



[]( )JZ13 调整数组顺序使奇数位于偶数前面

===================================================================================



题目:[调整数组顺序使奇数位于偶数前面]( )



> *   **先进先出,采用队列**

> *   **遍历数组,加入队列**

> *   **遍历数组,改变数组值**



*   队列(时间:400ms,O(n),空间:30MB,O(n))



import java.util.*;

public class Solution {

/**

 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可

 *

 * 

 * @param array int整型一维数组 

 * @return int整型一维数组

 */

public int[] reOrderArray (int[] array) {

    // write code here

    //先进先出,采用队列

    Queue<Integer> q1 = new LinkedList();

    Queue<Integer> q2 = new LinkedList();

    //遍历每一个数组元素

    for(int i=0;i<array.length;i++){

        //偶数

        if(array[i] % 2 == 0){

            //加入队列2

            q2.offer(array[i]);

        }

        //奇数

        else{

            //加入队列1

            q1.offer(array[i]);

        }

    }

    //遍历每一个元素,更换值

    for(int i=0;i<array.length;i++){

        //队列1没有出完

        if(!q1.isEmpty()){

            //存储并出队

            array[i] = q1.poll();

        }

        //队列1结束,队列2开始

        else{

            array[i] = q2.poll();

        }

    }

    return array;

}

}




*   直接交换法(时间:O(n),空间:O(n))



import java.util.*;

public class Solution {

/**

 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可

 *

 * 

 * @param array int整型一维数组 

 * @return int整型一维数组

 */

public int[] reOrderArray (int[] array) {

    // write code here

    //定义位置

    int i = 0;

    //创建新数组存储新的

    int[] res = new int[array.length];

    //遍历每一个,先加入奇数

    //array数组中的每一个元素,定义为j

    for(int j : array){

        //奇数

        if(j%2 != 0){

            res[i] = j;

            i++;

        }

    }

    //加入偶数

    for(int j : array){

        //偶数

        if(j%2 == 0){

            res[i] = j;

            i++;

        }

    }

    return res;

}

}




* * *



[]( )JZ14 链表中倒数第k个节点

==============================================================================



题目:[链表中倒数第k个节点]( )



import java.util.*;

/*

  • public class ListNode {

  • int val;

  • ListNode next = null;

  • public ListNode(int val) {

  • this.val = val;
    
  • }

  • }

*/

public class Solution {

/**

 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可

 *

 * 

 * @param pHead ListNode类 

 * @param k int整型 

 * @return ListNode类

 */

//求链表长度

public int length(ListNode pHead){

    //定义长度

    int length = 0;

    //遍历链表

    while(pHead != null){

        //长度加一

        length++;

        //向后遍历

        pHead = pHead.next;

    }

    //返回长度

    return length;

}



//返回倒数第k个节点,就是返回以倒数第k个节点为头节点的剩下的链表

public ListNode FindKthToTail (ListNode pHead, int k) {

    // write code here

    //获得链表长度

    int length = length(pHead);

    //如果没有第k个节点,链表长度小于k,即k不存在

    if(length < k)

        return null;

    //遍历去掉0 -> (length-k-1)的节点

    for(int i=0;i<length-k;i++){

        //将指针移到第length-k个节点

        //后面还剩下k个,所以这就是倒数第k个节点

        pHead = pHead.next;

    }

    //返回这个节点

    return pHead;

}

}




* * *



[]( )JZ15 反转链表

========================================================================



题目:[反转链表]( )  

**解答详情请看:[反转链表]( )**



* * *



[]( )JZ16 合并两个排序的链表

=============================================================================



题目:[合并两个排序的链表]( )



有问题



/*

public class ListNode {

int val;

ListNode next = null;



ListNode(int val) {

    this.val = val;

}

}*/

public class Solution {

//递归、循环,两种解决方式

public ListNode Merge(ListNode list1,ListNode list2) {

    ListNode head = null;

    ListNode cur = head;

    while(list1 != null && list2 != null){

            if(list1.val <= list2.val){

                cur.next = list1;

                list1 = list1.next;

            }else{

                cur.next = list2;

                list2 = list2.next;

            }

            cur = cur.next;

        

    }

    if(cur.next == list1){

        cur.next = list1;

    }else

        cur.next = list2;

    return head.next;

}

}




没问题



/*

public class ListNode {

int val;

ListNode next = null;



ListNode(int val) {

    this.val = val;

}

}*/

public class Solution {

// 递归方式解决

public ListNode Merge(ListNode list1, ListNode list2) {

	// 定义头节点

	ListNode head = null;

	// 有一个链表为空的情况,边界条件,结束条件

	// 返回另一个链表

	if (list1 == null)

		return list2;

	else if (list2 == null)

		return list1;

    //数值小的,加入链表

	if (list1.val <= list2.val) {

		// 加入

		head = list1;

		// 递归剩下的链表,继续加入小的

		head.next = Merge(list1.next, list2);

	} else {

		head = list2;

		head.next = Merge(list1, list2.next);

	}

    //返回所有的加入之后的

	return head;

}

}




* * *



[]( )JZ17 树的子结构

=========================================================================



题目:[树的子结构]( )



> *   **首先遍历大树**

> *   **找到结点值相同的结点**

> *   **进行判断,以这两个点为根的两个树是否为子结构**



/**

public class TreeNode {

int val = 0;

TreeNode left = null;

TreeNode right = null;



public TreeNode(int val) {

    this.val = val;



}

}

*/

public class Solution {

//遍历整个树,大树

public boolean HasSubtree(TreeNode root1,TreeNode root2) {

    //空树

    if(root1 == null || root2 == null)

        return false;

    //如果当前结点值和子树根节点相同,判断以该结点为根的树是否是子结构

    if(root1.val == root2.val){

        //判断是否是子结构

        if(judge(root1,root2)){

            return true;

        }

    }

    //当前结点值不等

    //递归遍历左右孩子,有一个是,那就是,所以采用||

    return HasSubtree(root1.left,root2) || HasSubtree(root1.right,root2);

}



//判断是否是子结构

public boolean judge(TreeNode node1,TreeNode node2){

    //子树已经循环完毕,证明全部匹配,是子结构

    if(node2 == null){

        return true;

    }

    //整个树已经循环完毕,未成功匹配完全,不是子结构

    if(node1 == null){

        return false;

    }

    //当前结点值相等,那么只需要左右孩子为子结构,那么就为子结构

    if(node1.val == node2.val){

        //要两个同时为子结构,当前的才是子结构,所以是&&

        return judge(node1.left,node2.left) && judge(node1.right,node2.right);

    }

    return false;

}

}




* * *



[]( )JZ18 二叉树的镜像

==========================================================================



题目:[二叉树的镜像]( )



**方法一:BFS(广度优先搜索)**  

**方法二:DFS(深度优先搜索)**  

**方法三:中序遍历**  

**方法四:递归**



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 {

/**

 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可

 *

 * 

 * @param pRoot TreeNode类

 * @return TreeNode类

 */

// 镜像变换

public TreeNode Mirror(TreeNode pRoot) {

	// write code here

	// 递归遍历

	if (pRoot == null) {

		return null;

	}

	// 中序遍历

	// 将当前结点的右子树当作根节点,将它的子树镜像变换

	Mirror(pRoot.right);

	// 将当前结点的左右子树整个交换

	TreeNode node = pRoot.right;

	pRoot.right = pRoot.left;

	pRoot.left = node;

	// 左子树变成了右子树,将右子树做根节点,将它的子树再变换

	Mirror(pRoot.right);

	// 返回根节点

	return pRoot;

}

}




* * *



[]( )JZ20 包含min函数的栈

=============================================================================



题目:[包含min函数的栈]( )



> **以空间换时间,很常见的方法。**



import java.util.Stack;

public class Solution {

Stack<Integer> stack1 = new Stack<Integer>();

Stack<Integer> stack2 = new Stack<Integer>();



public void push(int node) {

    stack1.push(node);

    if(stack2.isEmpty() || stack2.peek() >= node){

        stack2.push(node);

    }else{

        stack2.push(stack2.peek());

    }

}



public void pop() {

    stack1.pop();

    stack2.pop();

}



public int top() {

    return stack1.peek();

}



public int min() {

    return stack2.peek();

}

}




* * *



[]( )JZ21 栈的压入、弹出序列

=============================================================================



题目:[栈的压入、弹出序列]( )



![](https://img-blog.csdnimg.cn/20210616162229446.png)



> **辅助栈进行判断,后面循环,必加入数据,还需要判断栈不为空,还未理解这一点。**



import java.util.*;

public class Solution {

public boolean IsPopOrder(int [] pushA,int [] popA) {

    if(pushA.length == 0 || popA.length == 0 || popA.length != pushA.length){

        return false;

    }

    //辅助栈

    Stack<Integer> stack = new Stack<Integer>();

    //定义指向出栈数组的指针

    int j = 0;

    //入栈

    for(int i=0;i<pushA.length;i++){

        //入栈

        stack.push(pushA[i]);

        //判断当前栈顶元素是否等于出栈数组元素

        while(!stack.isEmpty() && stack.peek() == popA[j]){

            //栈顶元素等于出栈元素

            //栈顶出栈,指针后移

            stack.pop();

            j++;

        }

    }

    //判断是否出栈顺序正确

    //辅助栈空,正确,不为空,错误

    return stack.isEmpty();

}

}




* * *



[]( )JZ22 从上往下打印二叉树

=============================================================================



题目:[从上往下打印二叉树]( )



> *   **层次遍历**

> *   **广度优先搜索**



/**

public class TreeNode {

int val = 0;

TreeNode left = null;

TreeNode right = null;



public TreeNode(int val) {

    this.val = val;



}

}

*/

import java.util.*;

public class Solution {

//先序遍历,深度优先递归,广度优先队列

//深度优先dfs不对,要广度优先搜索bfs

public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {

    //建立链表存储结点值

    ArrayList<Integer> array = new ArrayList<Integer>();

    //新建队列进行广度优先搜索,层次遍历

    Queue<TreeNode> queue = new LinkedList<TreeNode>();

    //空树,返回空表

    if(root == null){

        return array;

    }

    //不空,使用队列进行层次遍历

    //将根结点加入队列,一层一层结点加入

    queue.offer(root);

    //队列不为空,即树没有遍历完全,无限循环

    while(!queue.isEmpty()){

        //取出队列头,将头节点的值加入链表

        TreeNode node = queue.poll();

        array.add(node.val);

        //当前结点的左孩子,右孩子都加入,即当前结点的下一层结点全部加入

        if(node.left != null){

            queue.offer(node.left);

        }

        if(node.right != null){

            queue.offer(node.right);

        }

    }

    return array;

}

}




* * *



[]( )JZ23 二叉搜索树的后序遍历序列

================================================================================



题目:[二叉搜索树的后序遍历序列]( )



public class Solution {

//划分出左右子树

public boolean divideToLeft(int[] s,int left,int right){

    //左边界大于右边界,证明全部化分,正确,是二叉搜索树

    //递归结束条件

    if(left >= right){

        return true;

    }

    //找到后序遍历的根

    int root = s[right];

    //定义左右子树边界

    int i;

    //从左边边界遍历到根前一个结点

    for(i = left;i<right;i++){

        //值大于根的值,证明是根的右子树,跳出循环

        if(s[i] > root){

            break;

        }

    }

    //边界前是左子树,后是右子树

    //遍历右子树,确保后面不存在左子树的值,确保为二叉搜索树

    for(int j=i;j<right;j++){

        if(s[j] < root){

            return false;

        }

    }

    //返回左右子树

    return divideToLeft(s,left,i-1) && divideToLeft(s,i,right-1);

}

public boolean VerifySquenceOfBST(int [] sequence) {

    //空树

    if(sequence.length == 0){

        return false;

    }

    //返回整个数组的树

    return divideToLeft(sequence,0,sequence.length-1);

}

}




* * *



[]( )JZ24 二叉树中和为某一值的路径

================================================================================



题目:[二叉树中和为某一值的路径]( )



import java.util.ArrayList;

import java.util.Stack;

/**

public class TreeNode {

int val = 0;

TreeNode left = null;

TreeNode right = null;



public TreeNode(int val) {

    this.val = val;



}

}

*/

public class Solution {

ArrayList<ArrayList<Integer>> res = new ArrayList<>();

ArrayList<Integer> path = new ArrayList<>();



public ArrayList<ArrayList<Integer>> FindPath(TreeNode root, int target) {

    if (root == null) {

        return res;

    }

    Path(root, target);

    return res;

}

public void Path(TreeNode root, int target) {

    //因为FindPath中和 下面程序中都进行了判null操作,root绝对不可能为 null

    path.add(root.val);

    //已经到达叶子节点,并且正好加出了target

    if (root.val == target && root.left == null && root.right == null) {

        //将该路径加入res结果集中

        res.add(new ArrayList(path));

    }

    //如果左子树非空,递归左子树

    if (root.left != null) {

        Path(root.left, target - root.val);

    }

    //如果右子树非空,递归右子树

    if (root.right != null) {

        Path(root.right, target - root.val);

    }

    //这个路径到达叶子结点,去掉这条路径的最后一个结点

    //返回父结点,找另一条路径

    path.remove(path.size() - 1);

    return;

}

}




* * *



[]( )JZ25 复杂链表的复制

===========================================================================



题目:[复杂链表的复制]( )  

![在这里插入图片描述](https://img-blog.csdnimg.cn/2021051908394950.png)



/*

public class RandomListNode {

int label;

RandomListNode next = null;

RandomListNode random = null;



RandomListNode(int label) {

    this.label = label;

}

}

*/

/*

*解题思路:

*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;

    }

    

    //重头开始遍历,以pHead为头节点的链表已经改变

    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;

}

}




* * *



[]( )JZ26 二叉搜索树与双向链表

==============================================================================



题目:[二叉搜索树与双向链表]( )



> **思路:将整棵树遍历,中序遍历二叉搜索树,直接得到有序的结点。然后将这些结点的关系重新链接,形成双向链表,后只需要返回第一个结点,即是头结点。**



/**

public class TreeNode {

int val = 0;

TreeNode left = null;

TreeNode right = null;



public TreeNode(int val) {

    this.val = val;



}

}

*/

import java.util.*;

public class Solution {

//总方法

public TreeNode Convert(TreeNode pRootOfTree) {

    if(pRootOfTree == null){

        return null;

    }

    ArrayList<TreeNode> list = new ArrayList<>();

    //遍历

    Order(pRootOfTree,list);

    //返回头结点

    return ConVert(list);

}



//中序遍历,保存节点

public void Order(TreeNode root,ArrayList<TreeNode> list){

    //递归遍历左子树

    if(root.left != null){

        Order(root.left,list);

    }

    //加入当前节点

    list.add(root);

    //遍历右子树

    if(root.right != null){

        Order(root.right,list);

    }

}



//将链表中排好序的节点,重新链接关系

//返回头结点

public TreeNode ConVert(ArrayList<TreeNode> list){

    //如果只有一个数,无需链接

    if(list.size() == 1){

        return list.get(0);

    }else{

        //第一个只向后链接

        list.get(0).right = list.get(1);

        //最后一个只向前链接

        list.get(list.size()-1).left = list.get(list.size()-2);

        //中间的结点双向链接

        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);

        }

    }

    //返回头结点

    return list.get(0);

}

}




* * *



[]( )JZ27 字符串的排列

==========================================================================



题目:[字符串的排列]( )



> **就是全排列问题。**  

> **思路:先固定一个位置的数字,然后对剩下的数字继续全排列,直到只剩下一个位置固定,证明一次全排列结束,输出这个序列。(递归)**



*   不考虑`字典序输出`和`重复的数值`,只考虑`全排列`



import java.util.ArrayList;

public class Solution {

//保存每一个排列序列

ArrayList<String> list = new ArrayList<>();



//全排列

public void Permutation(char c[],int start,int end){

    //只有最后一个没固定,只有一种情况

    //一次全排列结束

    if(start == end){

        list.add(new String(c));

    }else{

        for(int i = start;i<=end;i++){

            //确定首位的数

            swap(c,start,i);

            //后面的所有数,全排列

            Permutation(c,start+1,end);

            //避免重复排列

            swap(c,start,i);

        }

    }

}



//交换

public void swap(char c[],int i,int j){

    char temp;

    if(i != j){

        temp = c[i];

        c[i] = c[j];

        c[j] = temp;

    }

}



public ArrayList<String> Permutation(String str) {

    char c[] = str.toCharArray();

    Permutation(c,0,str.length()-1);

    return list;

}

}




*   不考虑`字典序`,考虑`全排列`和`重复值`



import java.util.ArrayList;

public class Solution {

//保存每一个排列序列

ArrayList<String> list = new ArrayList<>();



//全排列

public void Permutation(char c[],int start,int end){

    //只有最后一个没固定,只有一种情况

    //一次全排列结束

    if(start == end){

        list.add(new String(c));

    }else{

        for(int i = start;i<=end;i++){

            //后面存在一个相同值

            //进行下一个循环

            if(!isSwap(c,i,end)){

                continue;

            }

            //无重复值

            //执行下述代码

            swap(c,start,i);

            //后面的所有数,全排列

            Permutation(c,start+1,end);

            //避免重复排列

            swap(c,start,i);

        }

    }

}



//交换

public void swap(char c[],int i,int j){

    char temp;

    if(i != j){

        temp = c[i];

        c[i] = c[j];

        c[j] = temp;

    }

}



//去重复

public boolean isSwap(char c[],int i,int end){

    for(int j=i+1;j<=end;j++){

        //i后面的值存在和i重复的

        //跳过此次循环

        if(c[j] == c[i])

            return false;

    }

    return true;

}



public ArrayList<String> Permutation(String str) {

    char c[] = str.toCharArray();

    Permutation(c,0,str.length()-1);

    return list;

}

}




*   全部考虑



import java.util.*;

public class Solution {

//保存每一个排列序列

ArrayList<String> list = new ArrayList<>();



//全排列

public void Permutation(char c[],int start,int end){

    //只有最后一个没固定,只有一种情况

    //一次全排列结束

    if(start == end){

        list.add(new String(c));

    }else{

        for(int i = start;i<=end;i++){

            //跳出此次循环

            if(!isSwap(c,i,end)){

                continue;

            }

            swap(c,start,i);

            //后面的所有数,全排列

            Permutation(c,start+1,end);

            //避免重复排列

            swap(c,start,i);

        }

    }

}



//交换

public void swap(char c[],int i,int j){

    char temp;

    if(i != j){

        temp = c[i];

        c[i] = c[j];

        c[j] = temp;

    }

}



//去重复

public boolean isSwap(char c[],int i,int end){

    for(int j=i+1;j<=end;j++){

        //i后面的值存在和i重复的

        //跳过此次循环

        if(c[j] == c[i])

            return false;

    }

    return true;

}



public ArrayList<String> Permutation(String str) {

    if(str == ""){

        return null;

    }

    char c[] = str.toCharArray();

    Permutation(c,0,str.length()-1);

    //内置函数,升序排列,字典序

    Collections.sort(list);

    return list;

}

}




> **字典序法**



> **思路:先给定一个排列,然后找下一个排列顺序。下一个排列顺序要刚好比这个排列大,但是它们中间不能有排列,直到找到排列的左邻没有小于右邻的值为止,即是递减排列了,证明全排结束,全部找到了。**



> **步骤:**

> 

> 1.  从右至左,找到左邻小于右邻的第一个数,记录位置`i`和值`c[i]`

> 2.  从右至左,找到第一个比`c[i]`大的数,记录位置`j`和值`c[j]`

> 3.  交换这两个数字

> 4.  然后将`i`后面的值进行从小到大升序排列。

> 5.  因为交换的是第一个比`c[i]`大的值和`c[i]`,所以交换后,`i`后面的值还是从大到小排序的。所以`逆序`就可。



* * *



[]( )JZ28 数组中出现次数超过一半的数字

==================================================================================



题目:[数组中出现次数超过一半的数字]( )



*   哈希法



> **思路:新建一个哈希数组,用于在位置为值的地方存放这个值出现的次数,遍历哈希数组,找出大小比原数组的一半大的数,这个数存放的位置,就是众数的值。**



public class Solution {

public int MoreThanHalfNum_Solution(int [] array) {

    //只有一个元素,直接返回

    if(array.length == 1){

        return array[0];

    }

    //采用hash数组,存放出现次数

    int hash[] = new int[10001];

    //初始化数组

    for(int i=0;i<hash.length;i++){

        hash[i] = 0;

    }

    //遍历原数组,将值的个数存在在a中

    //存放的位置为值

    for(int i=0;i<array.length;i++){

        hash[array[i]]++;

    }

    //找出a中值大于原数组一半的数,证明是众数

    //返回a中的位置,即为众数的值

    for(int i=0;i<hash.length;i++){

        if(hash[i] > (array.length/2)){

            return i;

        }

    }

    //都不符合上述情况,返回0

    return 0;

}

}




*   候选法



> **思路:将两个数对比,如果这两个数不相同,则一起去掉。所以有两种情况:一种是去掉的是一个众数和一个非众数;另一种是两个非众数。无论是哪一种,如果众数存在,最后剩下的,肯定是众数。**



* * *



[]( )JZ29 最小的K个数

==========================================================================



题目:[最小的K个数]( )



> **主要思路:先对数组进行排序,然后返回前k个数。**



*   内置函数排序(不推荐)



> **因为肯定是要考我们排序算法,直接使用内置函数了,就没有意义了。**



import java.util.*;

public class Solution {

public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {

    ArrayList<Integer> list = new ArrayList<>();

    ArrayList<Integer> array = new ArrayList<>();

    for(int i=0;i<input.length;i++){

        array.add(input[i]);

    }

    Collections.sort(array);

    for(int i=0;i<k;i++){

        list.add(array.get(i));

    }

    return list;

}

}




*   快速排序(推荐)



> **思路:二分法进行分区,递归快排。**



`单向扫描快排法`



> **思路:从左到右扫描,小于基准,不动,看下一个;大于基准,将当前值和最后一个交换,最后一个值固定,右指针前移;继续对比当前值和基准,循环进行。**



import java.util.*;

public class Solution {

public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {

    ArrayList<Integer> list = new ArrayList<>();

    //对数组排序

    quickSort(input,0,input.length-1);

    //取前k个加入list

    for(int i=0;i<k;i++){

        list.add(input[i]);

    }

    return list;

}



// 单向扫描分区

public static int partition(int s[], int start, int end) {

	//确定基准

	int base = s[start];

	//左指针

	int i = start + 1;

	//右指针

	int j = end;

	//从左到右扫描

	while (i <= j) {

		//这个数小于基准,不变,看下一个

		if(s[i]<=base) {

			i++;

		}else {

			//大于基准,放到后面

			swap(s,i,j);

			//不在考虑放在后面的数字,固定

			j--;

		}

	}

	//无论如何,j最后指向的都是小于base的最后一个数

	swap(s, start, j);

	return j;

}



//快排

public static void quickSort(int[] s,int start,int end) {

	//相等代表只有一个,排序无意义

	if(start < end) {

		int mid = partition(s,start,end);

		//递归排序分区好的左右两边

		quickSort(s, start, mid-1);

		quickSort(s, mid+1, end);

	}

}



//交换

public static void swap(int[] s,int i,int j) {

	int temp = 0;

	temp = s[i];

	s[i] = s[j];

	s[j] = temp;

}

}




`双向扫描快排法`



> **思路:定义第一个数为基准;从左到右找第一个比基准大的数字,从右到左找第一个比基准小的数字;如果左指针小于右指针,交换他们,循环往复;直到左指针大于等于右指针结束;交换基准和右指针位置,分区完成。然后递归对每一个子块快排即可。**



import java.util.*;

public class Solution {

public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {

    ArrayList<Integer> list = new ArrayList<>();

    //对数组排序

    quickSort(input,0,input.length-1);

    //取前k个加入list

    for(int i=0;i<k;i++){

        list.add(input[i]);

    }

    return list;

}



//双向扫描分区

public static int partition(int[] s,int start,int end) {

	//确定基准

	int base = s[start];

	//i从前开始

	int i = start + 1;

	//j从后开始

	int j = end;

	//只要i在j前

	while(i<=j) {

		//找到第一个比基准大的i

		while(i<=j && s[i]<=base) {

			i++;

		}

		//找到第一个比基准小的j

		while(i<=j && s[j]>=base) {

			j--;

		}

		if(i<j) {

			swap(s,i,j);

		}

	}

	swap(s,start,j);

	return j;

}



//快排

public static void quickSort(int[] s,int start,int end) {

	//相等代表只有一个,排序无意义

	if(start < end) {

		int mid = partition(s,start,end);

		//递归排序分区好的左右两边

		quickSort(s, start, mid-1);

		quickSort(s, mid+1, end);

	}

}



//交换

public static void swap(int[] s,int i,int j) {

	int temp = 0;

	temp = s[i];

	s[i] = s[j];

	s[j] = temp;

}

}




> **还有`三分快排法`,感兴趣可以继续探索。**



另外两种方法博客链接:[快速排序的三种分区方法(整理)]( )



* * *



[]( )JZ30 连续子数组的最大和

=============================================================================



题目:[连续子数组的最大和]( )



*   动态规划法(填表法)



> **思路:定义一个表dp,根据原来数组的位置去填表,dp\[i\]表示以第i个位置为结尾的子数组的最大和。所以dp\[i\]等于array\[i\]和array\[i\]+dp\[i-1\]之间的最大值。每一个dp填入后,最大和就是这张表中的最大值。**



public class Solution {

//动态规划法,填表法

public int FindGreatestSumOfSubArray(int[] array) {

    //dp[i]表示以i结尾的子数组的最大和

    int dp[] = new int[array.length];

    //第一个值只有一个值,所以就是第一个值

    //可以保证无论数是有正负,还是全负全正,都通用

    dp[0] = array[0];

    //初始化最大值为第一个

    int max = dp[0];

    //一重循环,时间复杂度O(n)

    for(int i=1;i<array.length;i++){

        //下一个值为当前的值加上上一个dp和当前值之间的最大值

        //如果上一个dp是负数,那么当前值最大,即子数组开头换了位置一样

        dp[i] = Math.max(array[i],array[i]+dp[i-1]);

        //最大值,就是当前最大值和dp之比,动态变化

        max = Math.max(max,dp[i]);

    }

    //返回子数组最大和

    return max;

}

}




*   备忘录法(查表法)



> **思路:基本使用动态规划法的题目,备忘录法同样可以解决。思路几乎一样,就是一个查表,一个填表。**



*   变量判断法



> **思路:设置一个变量tmp = 0。如果tmp+array\[i\] < 0, 说明以i结尾的不作贡献,重新赋值tmp = 0,否则更新tmp = tmp + array\[i\]。最后判断tmp是否等于0, 如果等于0, 说明数组都是负数,选取一个最大值为答案**



* * *



[]( )JZ31 整数中1出现的次数(从1到n整数中1出现的次数)

============================================================================================



题目:[整数中1出现的次数(从1到n整数中1出现的次数)]( )



*   暴力解法,空间复杂度较高



> **思路:计算出一个数中1的个数,然后分别求每一个数中1的个数,求和即可。**



public class Solution {

//返回1-n这n个数中1的个数

public int NumberOf1Between1AndN_Solution(int n) {

    int s = 0;

    for(int i=1;i<=n;i++){

        s += NumofOne(i);

    }

    return s;

}



//返回这个数中1的个数

public int NumofOne(int n){

    int num = 0;

    String s = n + "";

    char c[] = s.toCharArray();

    for(int i=0;i<s.length();i++){

        if(c[i] == '1'){

            num++;

        }

    }

    return num;

}

}




* * *



[]( )JZ32 把数组排成最小的数

=============================================================================



题目:[把数组排成最小的数]( )



*   全排列



> **思路:把数组里的整数元素,看成一个字符串整体。进行字符串全排列,然后进行升序排序,第一个即是最小值。**



import java.util.*;

public class Solution {

//保存每一个排列序列

ArrayList<String> list = new ArrayList<>();



//得到组成的最小值

public String PrintMinNumber(int [] numbers) {

    String str = "";

    //输入为空,输出为空

    if(numbers.length == 0){

        return str;

    }

    //将整数数组变为字符串数组

    String s[] = new String[numbers.length];

    for(int i=0;i<numbers.length;i++){

        s[i] = numbers[i] + "";

    }

    //进行全排列

    Permutation(s,0,s.length-1);

    //字典序

    Collections.sort(list);

    //最小值

    str = list.get(0);

    //返回最小数

    return str;

}



//全排列

public void Permutation(String s[],int start,int end){

    //只有最后一个没固定,只有一种情况

    //一次全排列结束

    if(start == end){

        //把结果加入list中保存

        String str = "";

        for(int i=0;i<s.length;i++){

            str += s[i];

        }

        list.add(str);

    }else{

        for(int i = start;i<=end;i++){

            //有重复,跳出此次循环

            if(isEquals(s,i,end)){

                continue;

            }

            //固定开头的值

            swap(s,start,i);

            //后面的所有数,全排列

            Permutation(s,start+1,end);

            //避免重复排列

            swap(s,start,i);

        }

    }

}



//是否有重复

//去重复

public boolean isEquals(String s[],int i,int end){

    for(int j=i+1;j<=end;j++){

        if(s[i].equals(s[j])){

            return true;

        }

    }

    return false;

}



//交换

public void swap(String s[],int i,int j){

    String temp;

    temp = s[i];

    s[i] = s[j];

    s[j] = temp;

}

}




* * *



[]( )JZ33 丑数

======================================================================



题目:[丑数]( )



*   穷举法(枚举法)



> **思路:第一个丑数是1,下一个丑数肯定是前一个丑数乘以2/3/5的最小值。即第n个丑数肯定可以由第i个丑数乘以2/3/5得到。所以使用这个规律进行枚举。因为得到最小的之后,还有两个剩下的值需要继续比,所以需要三个指针,指向不同的第i个丑数。**



import java.util.*;

public class Solution {

//控制一个数组列举丑数,然后返回这个丑数

public int GetUglyNumber_Solution(int index) {

    //第0个丑数,不合理,返回0

    if(index == 0){

        return 0;

    }

    //创建数组保存丑数

    //为了不浪费空间,刚好到所求的第n个丑数

    int res[] = new int[index];

    //第一个是1

    res[0] = 1;

    //定义三个表示丑数*2*3*5

    int p2 = 0;

    int p3 = 0;

    int p5 = 0;

    //循环放入丑数

    for(int i=1;i<index;i++){

        //下一个丑数是三个里面最小的

        res[i] = Math.min(res[p2]*2,Math.min(res[p3]*3,res[p5]*5));

        //三个指针向后移动

        if(res[i] == res[p2]*2){

            p2++;

        }

        if(res[i] == res[p3]*3){

            p3++;

        }

        if(res[i] == res[p5]*5){

            p5++;

        }

    }

    //返回最后一个丑数,即是所求

    return res[index-1];

}

}




* * *



[]( )JZ34 第一个只出现一次的字符

===============================================================================



题目:[第一个只出现一次的字符]( )



*   数组法



> **思路:在这个值的位置,放置它出现的次数。但是有点问题,使用map得到值增加有点问题。所以改成了ASC码和数组保存其出现的次数。**



import java.util.*;

public class Solution {

public int FirstNotRepeatingChar(String str) {

    //asc码的长度多1

    int map[] = new int[128];

    //遍历得到每个字符出现次数

    for(int i=0;i<str.length();i++){

        map[str.charAt(i)]++;

    }

    //遍历,找到第一个出现次数只有一次的字符

    for(int i=0;i<str.length();i++){

        if(map[str.charAt(i)]==1){

            //返回位置

            return i;

        }

    }

    //没有只出现一次的字符

    return -1;

} 

}




*   哈希法



> **思路:由于在这个位置保存其出现的次数有问题,而题目只需要我们判断第一个出现一次的字符的位置,所以可以把出现一次的看做无重复,把多次出现的,记作重复,采用boolean进行判断。**



import java.util.*;

public class Solution {

public int FirstNotRepeatingChar(String str) {

    //创建哈希表

    HashMap<Character,Boolean> map = new HashMap<>();

    //遍历判断其是否重复出现

    for(int i=0;i<str.length();i++){

        //重复出现

        if(map.get(str.charAt(i)) != null){

            //设置true,表示重复出现

            map.put(str.charAt(i),true);

        }else{

            //第一次出现

            map.put(str.charAt(i),false);

        }

    }

    //重新遍历原字符串

    //找出第一个不重复出现的字符的位置

    for(int i=0;i<str.length();i++){

        //map中key为这个字符的,不重复

        if(map.get(str.charAt(i)) == false){

            //返回其在原数组中的位置

            return i;

        }

    }

    //都不符合,返回-1

    return -1;

}

}




* * *



[]( )JZ35 数组中的逆序对

===========================================================================



题目:[数组中的逆序对]( )



*   暴力解法



> **思路:以当前数字固定,找出它后面比它小的数字的个数,这些数字就可以和它组成这么多逆序对。然后遍历数组,使固定的数字不同。得到的所有数之和即为逆序对总数。因为有双重循环,所以对于10^5来说,一定超时了。**



public class Solution {

//所有的逆序对

public int InversePairs(int [] array) {

    int res = 0;

    for(int i=0;i<array.length-1;i++){

        res += NumsOfSmall(array,i);

    }

    return res;

}



//在后面中小于这个数的数字个数

public int NumsOfSmall(int[] array,int i){

    int s = 0;

    for(int j=i+1;j<array.length;j++){

        if(array[i] > array[j]){

            s++;

            s %= 1000000007;

        }

    }

    return s;

}

}




*   归并排序法



> **思路:先分解数组,直到不能再分,然后进行对比两个数组,排序,将小的先放入,大的后放入,大的还要计算逆序数。`只要前一个数组的当前值大于后一个数组的值,那么在前一个数组中,位于这个数后面的所有数,都可以和后一个数组的这个数组成逆序对。`最后返回逆序对总数。**



public class Solution {

//记录逆序对总数

private int res;

//归并排序

private void MergeSort(int[] array, int start, int end){

    if(start>=end)return;

    //分界点

    int mid = (start+end)/2;

    //分成两个数组

    MergeSort(array, start, mid);

    MergeSort(array, mid+1, end);

    //进行合并,排序,并记录逆序数

    MergeOne(array, start, mid, end);

}



//合并,排序,记录逆序数

private void MergeOne(int[] array, int start, int mid, int end){

    //保存排序后的数组

    int[] temp = new int[end-start+1];

    //两个指针,指向两个数组的第一个数值

    int k=0,i=start,j=mid+1;

    while(i<=mid && j<= end){

        //如果前面的元素小于后面的不能构成逆序对

        if(array[i] <= array[j])

            temp[k++] = array[i++];

        else{

            //如果前面的元素大于后面的

            //那么在前面元素之后的元素都能和后面的元素构成逆序对

            temp[k++] = array[j++];

            //计算逆序对数量

            res = (res + (mid-i+1))%1000000007;

        }

    }

    //合并第一个数组

    while(i<= mid)

        temp[k++] = array[i++];

    //合并第二个数组

    while(j<=end)

        temp[k++] = array[j++];

    //改变数组位置,进行排序

    for(int l=0; l<temp.length; l++){

        array[start+l] = temp[l];

    }

}



//调用方法,计算数量

public int InversePairs(int [] array) {

    MergeSort(array, 0, array.length-1);

    return res;

}

}




* * *



[]( )JZ36 两个链表的第一个公共结点

================================================================================



题目:[两个链表的第一个公共结点]( )



/*

public class ListNode {

int val;

ListNode next = null;



ListNode(int val) {

    this.val = val;

}

}*/

public class Solution {

//双重遍历,寻找第一个相同的节点

public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {

    //保存第二个链表

	ListNode temp = pHead2;

    //两个链表都不为空

	if (pHead2 != null && pHead1 != null) {

        //pHead1不为空,进入循环

		while (pHead1 != null) {

            //pHead2不为空,进入循环,双重循环

			while (pHead2 != null) {

                //两个相等,返回任意一个都行

				if (pHead2 == pHead1) {

					return pHead2;

				}

                //不等,第一个链表不变,第二个向右继续

				pHead2 = pHead2.next;

			}

            //一轮过后,第二个链表指针指向了末尾

            //需要重新指向开头

			pHead2 = temp;

            //第一个链表向右,开始下一轮

			pHead1 = pHead1.next;

		}

	}

    //没有,返回空

	return null;

}

}




* * *



[]( )JZ38 二叉树的深度

==========================================================================



题目:[二叉树的深度]( )



**方法一:分治法,先求左子树,后求右子树(递归DFS)**



> 分治法简介:**求一个规模为n的问题,先求左边规模大约为n/2的问题,再求右边规模大约为n/2的问题,然后合并左边,右边的解,从而求得最终解**。具体可参考归并排序。  

> 步骤:

> 

> *   求 pro(left, rigth) -> int

> *   先求pro(left, (left+right)/2) -> left

> *   再求pro((left+right)/2 + 1, right) -> right

> *   merge(left, right) -> result



> 求二叉树的最大深度,我们不必管函数具体是怎么实现的。  

> 所以最终结果为 **max( 头结点左子树的最大深度, 头结点右子树的最大深度)+1**  

> 步骤:

> 

> *   求TreeDepth(TreeNode pRoot)->int

> *   先求 TreeDepth(pRoot.left) ->left

> *   再求TreeDepth(pRoot.right) ->right

> *   return **Math.max(left, right) + 1**



/**

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) {

    //树不存在,返回0

    if(root == null){

        return 0;

    }

    //找出左子树的深度

    int left = TreeDepth(root.left);

    //找出右子树的深度

    int right = TreeDepth(root.right);

    //返回左右子树大的深度加一,就是最长的路径,即整个树的深度

    return Math.max(left,right) + 1;

}

}




**方法二:层次遍历(队列BFS)**



> 非递归:求最大深度,可用队列。因为要满足先进先出的特性。

> 

> *   初始化:一个队列queue<TreeNode\*> q, 将root节点入队列q

> *   如果队列不空,做如下操作:

> *   弹出队列头,保存为node,将node的左右非空孩子加入队列

> *   做2,3步骤,知道队列为空



/**

public class TreeNode {

int val = 0;

TreeNode left = null;

TreeNode right = null;



public TreeNode(int val) {

    this.val = val;



}

}

*/

import java.util.*;

public class Solution {

//层次遍历,广度优先搜索BFS

public int TreeDepth(TreeNode root) {

    //空树,返回0

    if(root == null){

        return 0;

    }

    Queue<TreeNode> queue = new LinkedList();

    TreeNode nlast = null;

    TreeNode last = root;

    int depth = 0;

    queue.offer(root);

    while(!queue.isEmpty()){

        TreeNode cur = queue.poll(); 

        if(cur.left != null){

            queue.offer(cur.left);

            nlast = cur.left;

        }

        if(cur.right != null){

            queue.offer(cur.right);

            nlast = cur.right;

        }

        if(cur == last){

            depth++;

            last = nlast;

        }

    }

    return depth;

}

}




* * *



[]( )JZ39 平衡二叉树


# 难道这样就够了吗?不,远远不够!

提前多熟悉阿里往年的面试题肯定是对面试有很大的帮助的,但是作为技术性职业,手里有实打实的技术才是你面对面试官最有用的利器,这是从内在散发出来的自信。

备战阿里时我花的最多的时间就是在学习技术上,占了我所有学习计划中的百分之70,这是一些我学习期间觉得还是很不错的一些学习笔记

我为什么要写这篇文章呢,其实我觉得学习是不能停下脚步的,在网络上和大家一起分享,一起讨论,不单单可以遇到更多一样的人,还可以扩大自己的眼界,学习到更多的技术,我还会在csdn、博客、掘金等网站上分享技术,这也是一种学习的方法。

今天就分享到这里了,谢谢大家的关注,以后会分享更多的干货给大家!

![阿里一面就落马,恶补完这份“阿里面试宝典”后,上岸蚂蚁金服](https://img-blog.csdnimg.cn/img_convert/80acb7db86f0f49d876b1656f8a1396d.webp?x-oss-process=image/format,png)

![阿里一面就落马,恶补完这份“阿里面试宝典”后,上岸蚂蚁金服](https://img-blog.csdnimg.cn/img_convert/8e530b4829ae1020f39e28e653ab2e70.webp?x-oss-process=image/format,png)

![image.png](https://img-blog.csdnimg.cn/img_convert/753ec902f24d6a5a52fd6afb2e95efc9.webp?x-oss-process=image/format,png)





> **本文已被[CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)收录**

**[需要这份系统化的资料的朋友,可以点击这里获取](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**

lass ListNode {

    int val;

    ListNode next = null;



    ListNode(int val) {

        this.val = val;

    }

}*/

public class Solution {

    //双重遍历,寻找第一个相同的节点

	public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {

        //保存第二个链表

		ListNode temp = pHead2;

        //两个链表都不为空

		if (pHead2 != null && pHead1 != null) {

            //pHead1不为空,进入循环

			while (pHead1 != null) {

                //pHead2不为空,进入循环,双重循环

				while (pHead2 != null) {

                    //两个相等,返回任意一个都行

					if (pHead2 == pHead1) {

						return pHead2;

					}

                    //不等,第一个链表不变,第二个向右继续

					pHead2 = pHead2.next;

				}

                //一轮过后,第二个链表指针指向了末尾

                //需要重新指向开头

				pHead2 = temp;

                //第一个链表向右,开始下一轮

				pHead1 = pHead1.next;

			}

		}

        //没有,返回空

		return null;

	}

}




JZ38 二叉树的深度

==========================================================================

题目:二叉树的深度

方法一:分治法,先求左子树,后求右子树(递归DFS)

分治法简介:求一个规模为n的问题,先求左边规模大约为n/2的问题,再求右边规模大约为n/2的问题,然后合并左边,右边的解,从而求得最终解。具体可参考归并排序。

步骤:

  • 求 pro(left, rigth) -> int
  • 先求pro(left, (left+right)/2) -> left
  • 再求pro((left+right)/2 + 1, right) -> right
  • merge(left, right) -> result

求二叉树的最大深度,我们不必管函数具体是怎么实现的。

所以最终结果为 max( 头结点左子树的最大深度, 头结点右子树的最大深度)+1

步骤:

  • 求TreeDepth(TreeNode pRoot)->int
  • 先求 TreeDepth(pRoot.left) ->left
  • 再求TreeDepth(pRoot.right) ->right
  • return Math.max(left, right) + 1

/**

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) {

        //树不存在,返回0

        if(root == null){

            return 0;

        }

        //找出左子树的深度

        int left = TreeDepth(root.left);

        //找出右子树的深度

        int right = TreeDepth(root.right);

        //返回左右子树大的深度加一,就是最长的路径,即整个树的深度

        return Math.max(left,right) + 1;

    }

}



方法二:层次遍历(队列BFS)

非递归:求最大深度,可用队列。因为要满足先进先出的特性。

  • 初始化:一个队列queue<TreeNode*> q, 将root节点入队列q
  • 如果队列不空,做如下操作:
  • 弹出队列头,保存为node,将node的左右非空孩子加入队列
  • 做2,3步骤,知道队列为空

/**

public class TreeNode {

    int val = 0;

    TreeNode left = null;

    TreeNode right = null;



    public TreeNode(int val) {

        this.val = val;



    }



}

*/

import java.util.*;

    

public class Solution {

    //层次遍历,广度优先搜索BFS

    public int TreeDepth(TreeNode root) {

        //空树,返回0

        if(root == null){

            return 0;

        }

        Queue<TreeNode> queue = new LinkedList();

        TreeNode nlast = null;

        TreeNode last = root;

        int depth = 0;

        queue.offer(root);

        while(!queue.isEmpty()){

            TreeNode cur = queue.poll(); 

            if(cur.left != null){

                queue.offer(cur.left);

                nlast = cur.left;

            }

            if(cur.right != null){

                queue.offer(cur.right);

                nlast = cur.right;

            }

            if(cur == last){

                depth++;

                last = nlast;

            }

        }

        return depth;

    }

}




JZ39 平衡二叉树

难道这样就够了吗?不,远远不够!

提前多熟悉阿里往年的面试题肯定是对面试有很大的帮助的,但是作为技术性职业,手里有实打实的技术才是你面对面试官最有用的利器,这是从内在散发出来的自信。

备战阿里时我花的最多的时间就是在学习技术上,占了我所有学习计划中的百分之70,这是一些我学习期间觉得还是很不错的一些学习笔记

我为什么要写这篇文章呢,其实我觉得学习是不能停下脚步的,在网络上和大家一起分享,一起讨论,不单单可以遇到更多一样的人,还可以扩大自己的眼界,学习到更多的技术,我还会在csdn、博客、掘金等网站上分享技术,这也是一种学习的方法。

今天就分享到这里了,谢谢大家的关注,以后会分享更多的干货给大家!

[外链图片转存中…(img-ayp4E2Fm-1715596303413)]

[外链图片转存中…(img-uSbdbpNy-1715596303413)]

[外链图片转存中…(img-3sGeDfaO-1715596303413)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

  • 23
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值