牛客剑指offer刷题篇

1. 二维数组中的查找 【数组】

牛客剑指offer题目

  • 思路:
1   2   3 
4   5   6
7   8   9 
10  11  12
target=11

右上角的元素大于这一行的所有元素,小于这一列的所有元素。所以从右上角开始遍历,第一个右上角元素则是array[0][列数-1]=array[0][2],target大于右上角,则行数加1,target小于右上角,则列数减1。直到row<rows && column>=0,说明遍历了所有元素,遍历结束,没有找到目标,返回false。

  • 代码:
public class Solution {
    public boolean Find(int target, int [][] array) {
        //右上角元素array[0][columns-1]
        int rows = array.length;  //数组的长度是属性,不是方法
        int columns = array[0].length;
        int row=0;
        int column=columns-1;
        while(row<rows && column>=0){
            if(target == array[row][column]){
                return true;
            }else if(target>array[row][column]){
                row++;
            }else if(target <array[row][column]){
                column--;
            }
        }
        return false;
    }
}
  • 知识点:
    数组的length是属性,不是方法,二维数组的长度表示方法为:
int rows = array.length;  
int columns = array[0].length;
2. 替换空格 【字符串】

牛客剑指offer题目

  • 思路:
    直接用一个新的字符串,拆分原字符串,一个一个将字符传给新的字符串,遇到空格就传入%20,不是空格就正常传递字符就行了。
  • 代码:
public class Solution {
        public String replaceSpace(StringBuffer str) {
        //StringBuffer没有split这个方法
        StringBuffer newstr=new StringBuffer(); //不能够像String str="ab"这么简单的写法
        for(int i=0;i<str.length();i++){  //记住字符串有长度这个属性啊
            if(str.charAt(i)==' '){
                newstr.append("%20");
            }else{
                newstr.append(str.charAt(i));
            }
        }
        return newstr.toString();
    }
}
  • 知识点:
    StringBuffer没有split这个方法;
    StringBuffer newstr=new StringBuffer(); 不能够像String str="ab"这么简单的写法;
    字符串有length()方法
    可以通过方法charAt()将每个字符变为char类型
3. 从尾到头打印链表 【链表】【栈】

牛客剑指offer题目

  • 思路:
    只是需要将链表的值打印出来,所以没那么复杂,涉及到反转,只需要利用Stack就可以了。
  • 代码:
/**
*    public class ListNode {
*        int val;
*        ListNode next = null;
*
*        ListNode(int val) {
*            this.val = val;
*        }
*    }
*
*/
import java.util.ArrayList;
import java.util.Stack;
public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        //返回链表中的值就可以了,考虑用栈
        ArrayList<Integer> result = new ArrayList<>();
        Stack<Integer> stack=new Stack<>();
        while( listNode != null){
            stack.push(listNode.val);
            listNode=listNode.next; //记得传完值以后,要让节点指向下一个节点啊
        }
        while(!stack.isEmpty()){
            result.add(stack.pop());
        }
        return result;
    }
}
  • 知识点:
    导包:import java.util.ArrayList;以及import java.util.Stack;
    注意,使用一个节点以后,要将下一个节点指向给当前的节点啊
4. 重建二叉树【二叉树】【递归】

牛客剑指offer题目

  • 思路:
    首先了解前序,中序,后序遍历,参考博客
    根据前序和中序重建二叉树的思路:前序的第一个元素是根节点,根据这个根节点,在中序中找到这个根节点的索引,索引前面是这个根的左子树,索引后面是这个根的右子树。同理递归,左子树,右子树。直到pre.length=1找到了递归的出口,递归结束。

  • 代码:

/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
import java.util.Arrays;
public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        //肯定是得用到递归了
        if(pre.length==0){
            return null;
        }
        int rootVal=pre[0]; //先找到根节点
        if(pre.length==1){
            return new TreeNode(rootVal);
        }  //递归的出口,只有一个元素,这个元素作为根节点直接返回即可。
        TreeNode root = new TreeNode(rootVal);
        //在中序中找到根节点那个元素的位置,位置的左面元素则是左子树,右面元素则是右子树
        int index=0;
        for (int i=0;i<in.length;i++){
            if(in[i] == rootVal){
                index=i;  //在中序中找到了根节点在中序的所在位置
                break;
            }
        }
        //同理在继续划分,左子树和右子树,可以使用递归的方式继续划分
        root.left = reConstructBinaryTree(Arrays.copyOfRange(pre,1,index+1),Arrays.copyOfRange(in,0,index));
        root.right= reConstructBinaryTree(Arrays.copyOfRange(pre,index+1,pre.length),Arrays.copyOfRange(in,index+1,in.length));
       //别忘了,我们这是再求左右子树,怎么能不写子树right和left呢
        return root;
    }
}
  • 知识点:
    注意刚开始的一些判定条件,长度为0的二叉树这些特殊的例子。
    这道题注意递归的出口,是pre的长度为1的时候,就应该是递归的出口了,因为没有元素了,所以直接返回这个根节点就可以了。
5. 用两个栈实现队列【队列和栈】

牛客剑指offer题目

  • 思路:
    栈是先进后出,队列是先进先出。用两个栈实现队列的,入队和出队函数。
    入队,没什么好说的,就直接放入元素就可以了,也不需要返回值,所以直接存到stack1中记录即可。
    主要是出队的时候,注意是要先入队的先出,所以需要将元素从stack1中导入到stack2中,然后再tsack2出队,出队元素就是stack2中的顶级元素,这个元素是最先进入栈中的那个元素。
    还有一点需要注意的是,出队以后,方便其他元素下一次入队,是进入到stack1中,所以出队以后,需要将stack2中的元素再放回stack1中。

  • 代码:

//第5题:用两个栈实现队列
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();

    public void push(int node) {
        stack1.push(node);  //入队,没什么好说的,就直接放入元素就可以了,也不需要返回值,所以直接存到stack1中即可
    }

    public int pop() {
        //主要是出队的时候,注意是要先入队的先出,所以需要将元素从stack1中导入到stack2中,然后再出队,出队元素就是stack2中的顶级元素
        //还有一点需要注意的是,出队以后,方便下一次入队,是进入到stack1中,所以出队以后,需要将stack2中的元素再放回stack1中
        while(!stack1.isEmpty()){
            stack2.push(stack1.pop());
        }
        int result= stack2.pop();
        while(!stack2.isEmpty()){
            stack1.push(stack2.pop());
        }
        return result;
    }
  • 知识点:
    补充用两个队列实现栈:
    压入元素,没什么好说的和栈的操作一样,先随便用一个队列存放压入的元素。
    出栈的时候,需要注意q1中入队1 2 3 出栈应该出3,所以我们一个一个出队,不是顶层的元素就出队之后存放到q2中。
    出掉3以后,再将q2中的元素存回q1中,方便下一次其他元素入队
//第5题补充,两个队列实现栈
    Queue<Integer> queue1=new LinkedList<>();
    Queue<Integer> queue2=new LinkedList<>();

    public void push(int node) {//压入元素,没什么好说的和栈的操作一样,先随便用一个队列存放压入的元素
        queue1.offer(node);
    }
    public int pop(){
        //出栈的时候,需要注意q1中入队1 2 3 出栈应该出3,所以我们一个一个出队,不是顶层的元素就出队之后存放到q2中,
        // 出掉3以后,再将q2中的元素存回q1中,方便下一次其他元素入队
        while (queue1.size()!=1){
            queue2.offer(queue1.poll());
        }
        int result=queue1.poll();
        while(!queue2.isEmpty()){
            queue1.offer(queue2.poll());
        }
        return result;
    }

也不确定代码错还是队,哪天遇到题验证一下吧。

6. 旋转数组的最小数字【数组】【二分法】

牛客剑指offer题目

  • 思路:
    如果使用遍历的思路很简单,但是这没有使用到旋转数组的性质,所以不推荐使用遍历的方法,可以使用二分法的变形。参考博客
  • 代码:
public int minNumberInRotateArray(int [] array) {
        //题目解析参考:https://blog.nowcoder.net/n/dcb0f2e6ffd44e1895b7a5297e362778?f=comment
        //两组例子:4 5 6 7 8 1 2 3以及 1 0 1 1 1
        int low=0;
        int high=array.length-1;
        int mid=0; //随便赋值一个初值,一会还会重新赋值
        while(low<high){ //low的索引小于high的索引,就会一直执行
            if(array[low]<array[high]){
                return array[low];//如果low小于high,那么说明元素本身就是顺序的,那么直接返回最前面的最小的元素就可以了
            }
            mid = low+(high-low)/2;
            if(array[low]<array[mid]){
                low=mid+1;
            }else if(array[mid]<array[high]){
                high=mid;
            }else
                low++;
        }
        return array[low];
    }
7. 斐波那契数列【递归】【递归的改进算法】

牛客剑指offer题目

  • 思路:
    F(n)=F(n-1)+F(n-2)可以采用递归算法,但是递归算法浪费很多计算量,比较麻烦,所以可以参考博客进行改进。
  • 代码:
//递归的方法:但是好多数需要重复计算,浪费时间。
public int Fibonacci(int n) {
        if(n==0) return 0;
        if(n==1) return 1;
        return Fibonacci(n-1)+Fibonacci(n-2);
    }
//递归算法的改进算法
public int Fibonacci(int n){
        if(n==0) return 0;
        if(n==1) return 1;
        //sum存储第n项
        //pre1存储第n-1项
        //pre2存储第n-2项
        // 0 1 2 3 4 5 6
        // 0 1 1 2 3 5 8
        int sum=1;
        int pre1=1;
        int pre2=0;
        for(int i=2;i<=n;i++){
            sum=pre1+pre2;
            pre2=pre1;
            pre1=sum;
        }
        return sum;
    }
8.跳台阶【递归】【递归的改进算法】

牛客剑指offer题目

  • 思路:
    target=1,只有一种可能。
    target=2,有两种跳法:一次跳一次,跳两次。一次直接跳两个。
    target=3,第一次跳1阶,还剩2阶,2阶有2种跳法。或者第一次跳2阶,还剩1阶,1阶有1种跳法。所以一共有2+1=3种跳法。
    target=4,第一次跳1阶,还剩3阶,有3种跳法。第一次跳2阶,还剩2阶,有2种跳法。所以一共3+2=5种跳法。
  • 代码:
// 1 2 3 4 5
    // 1 2 3 5 8
    //递归算法
    public int JumpFloor(int target) {
        if(target==1) return 1;
        if(target==2) return 2;
        return JumpFloor(target-1)+JumpFloor(target-2);
    }
    
    //递归算法改进算法
    public int JumpFloor(int target){
        if(target<=2) return target;
        int sum=0;
        int pre1=2;
        int pre2=1;
        for(int i=3;i<=target;i++){
            sum=pre1+pre2;
            pre2=pre1;
            pre1=sum;
        }
        return sum;
    }
9. 变态跳台阶【递归】【递归推导】

牛客剑指offer题目

  • 思路:
    解题思路参考
    通过查找关系得到表达式,写出代码。
    n=1 f(1)=f(1-1)=1
    n=2 f(2)=f(2-1)+f(2-2)=1+1=2
    n=3 f(3)=f(3-1)+f(3-2)+f(3-3)=2+1+1=4
    n=n-1 f(n-1)=f(n-2)+f(n-3)+…+f(0)
    n=n f(n)=f(n-1)+f(n-2)+f(n-3)+…f(0)

    f(n)-f(n-1)=f(n-1)
    f(n)=2*f(n-1)

  • 代码:

public int JumpFloorII(int target){
        if(target<=1) return 1;
        else
            return JumpFloorII(target-1)*2;
    }
10. 矩形覆盖【递归】【递归推导】

牛客剑指offer题目

  • 思路:
    参考思路博客
    2 ∗ 1 2*1 21 : 1种
    2 ∗ 2 2*2 22 : 2种
    3 ∗ 2 3*2 32 : 最后一级竖着放 1 ∗ 2 1*2 12,还剩 2 ∗ 2 2*2 22,有2种。最后一级横着放2个 1 ∗ 2 1*2 12即为 2 ∗ 2 2*2 22,还剩 1 ∗ 2 1*2 12,有1种。所以一共3种。
    4 ∗ 2 4*2 42 : 最后一级竖着放 1 ∗ 2 1*2 12,还剩 3 ∗ 2 3*2 32,有3种。最后一级横着放2个 1 ∗ 2 1*2 12即为 2 ∗ 2 2*2 22,还剩 2 ∗ 2 2*2 22,有2种。所以一共5种
  • 代码:
//10.矩形覆盖
    //参考:https://blog.nowcoder.net/n/cd6309f586174fda98f8c9bdf2e2df07?f=comment
    public int RectCover(int target) {
        if(target<=2) return target;
        else{
            return RectCover(target-1)+RectCover(target-2);
        }
    }
*待完成11. 二进制中1的个数

牛客剑指offer题目

*待完成12. 数值的整数次方

牛客剑指offer题目

13. 调整数组顺序使奇数位于偶数前面【数组】【待改进】

牛客剑指offer题目

  • 思路:
    思路比较简单,就是先建立了2个list,一个存奇数,一个存偶数,最终将他们合并到一起。这个方法太笨了,肯定还有更好得办法。
  • 代码:
public void reOrderArray(int [] array) {
        //开辟数组的方法
        ArrayList<Integer> list_ji = new ArrayList<Integer>();
        ArrayList<Integer> list_ou = new ArrayList<Integer>();
        for(int i=0 ; i<array.length; i++){
            if(array[i]% 2 == 0){//偶数
                list_ou.add(array[i]);
            }
            else{
                list_ji.add(array[i]);
            }
        }

        int m = list_ji.size();
        int n = list_ou.size();
        for(int i =0 ; i<array.length; i++){
            if(i < m){array[i]=list_ji.get(i);}
            else{array[i] = list_ou.get(i-m);}  //这种方式赋值更好,相比于下面那种
        }
        /*for(int i=0;i<list_ji.size();i++){
            array[i]=list_ji.get(i);
        }
        for(int j=list_ji.size();j<array.length;j++){
            array[j]=list_ou.get(j-list_ji.size());
        }*/  //2个循环,麻烦
 }
  • 知识:
    学习一种将2个list组合为一个数组得方法。一个循环就可以,没必要使用下面那种2个循环。
14. 链表中倒数第k个结点【链表】

牛客剑指offer题目

  • 思路:
    假设6个节点 1 2 3 4 5 6,那么倒数第2个节点其实就是正数第5个节点,k=2,5=6+1-2。
    所以代码就先,求出链表得长度,然后加1减去k就是正数第几个节点,我们在遍历链表,把这个节点作为返回值即可。
  • 代码:
public ListNode FindKthToTail(ListNode head,int k){
        //假设6个节点,1 2 3 4 5 6 倒数第2个,就是正数第5个,k=2,求正数第5个节点
        //先求链表长度
        int size=0;
        ListNode pHead=head;
        while(pHead!=null){
            size++;
            pHead=pHead.next;
        }
        
        if(k>size) return null;  //注意判断k是否超出范围啊
        else{
            int head_num=size+1-k;  //6+1-2=5
            pHead=head;
            for(int i=1;i<head_num;i++){
                pHead=pHead.next;
            }
            return pHead;
        }
    }
  • 知识点:
    注意求链表长度得代码块。
15. 反转链表【链表】

牛客剑指offer题目

  • 思路:
    反转链表需要三个节点,当前节点head,当前节点得前一个节点pre以及当前节点得后一个节点next。循环:只要当前节点head不为空,就一直交换。
  • 代码:
public ListNode ReverseList(ListNode head){
        //需要三个节点之间得交换来反转链表
        ListNode pre=null;
        ListNode next=null;
        while(head!=null){//head没到null就说明,head指向得不是最后一个节点,就要一直交换
            next=head.next;//首先head指向得是当前节点,那么在最开始head是头节点得时候,pre=null没问题,但是next不应该是null,所以先给next赋值
            head.next=pre;//head是当前节点,因为要反转链表,所以当前节点得next应该是前一个节点,也就是pre
            pre=head; //前两行代码就算转换完毕了,接下来就是移动节点,所有得节点后移,pre就应该是刚才得当前节点
            head=next; //next应该给当前节点赋值
        }
        return pre;
        //最终返回得应该是pre,因为当head指向到最后一个节点的时候,head并不是null,循环执行以后,head指向了null,
        // 在判断发现循环条件不满足,
        // 但是此时的head指向的是null,pre才是最后一个节点,也就是现在的头节点,所以返回pre
    }
  • 知识点:
    注意循环里面交换的过程需要三个节点进行交换;
    注意最后的返回值;
16.合并两个排序的链表【链表】

牛客剑指offer题目

  • 思路:
    先比较两个链表的头节点,list1和list2,假设list1的头节点小,那么将list1赋值给list,注意因为最开始假设list是null,所以要将整个list1赋给list,而不单单是val,这样无效。然后修改list的next的值,利用递归比较list1剩余的节点以及list2的节点。
  • 代码:
public ListNode Merge(ListNode list1,ListNode list2){
        ListNode list=null;
        if(list1==null){
            return list2;
        }else if(list2==null){
            return list1;
        }

        if(list1.val<=list2.val){
//            list.val=list1.val;
            list=list1;  // 上面那样写法不对,因为list最开始是null,所以不存在val,
            //你要直接将list1给list,然后再修改next得值
            list.next=Merge(list1.next,list2);
        }
        if(list1.val>list2.val){
//            list.val=list2.val;
            list=list2; // 上面那样写法不对,因为list最开始是null,所以不存在val,
            //你要直接将list2给list,然后再修改next得值
            list.next=Merge(list1,list2.next);
        }
        return list;
    }
17. 树的子结构【树】【递归】

牛客剑指offer题目

//参考:https://blog.csdn.net/ouyangyanlan/article/details/73042736
    public boolean HasSubtree(TreeNode root1,TreeNode root2){
        //首先总是要先注意各种空树
        if(root1==null) return false;
        if(root2==null) return false; //题中说空树不是任何树的子结构
        //然后考虑两个都不空的时候
        boolean result =false;
        //首先在tree1中找到和root2值相同的节点,然后比较tree1中的左右子树和root2的左右子树 是否也相同
        if(root1!=null && root2!=null){
            if(root1.val==root2.val){  //注意是比较值是否相同
                //根节点就相同,比较左右子树,需要另外一个函数
                result=isSubTree(root1,root2); 
                //找到根节点相同的节点了,开始判断左右子树是否相同
            }
            //左右子树不同的话,还要继续遍历tree1的左右子树,继续找和root2相同的根节点
            if(!result){
                result = HasSubtree(root1.right,root2);
            }
            if(!result){
                result=HasSubtree(root1.left,root2);
            }
        }
        return result;
    }
    public boolean isSubTree(TreeNode root1,TreeNode root2){
        //用来判断tree2是否是tree1的子结构
        if(root1 == null && root2 != null) return false;
        if(root2 == null) return true;
        if(root1.val != root2.val) return false;
        return isSubTree(root1.left, root2.left) && isSubTree(root1.right, root2.right);
        //根节点相同了,就继续比较左右子树是否相同
    }
18.二叉树的镜像【二叉树】【递归】

牛客剑指offer上的题目

  • 思路:
    交换root根节点的左右节点,然后再以同样的方法继续交换左右节点,所以用到递归,那么什么时候递归结束呢?就是root.left==null && root.right==null作为递归的出口。
  • 代码:
public void Mirror(TreeNode root) {
        if(root==null) return;  //虽然函数本身是void函数,但是仍然可以存在return,
        //只不过不返回值即可,遇到return就结束这个函数
        if(root.left==null && root.right==null) return; 
        //左子树 和 右子树 都为空的时候  直接结束函数
        //其实这句就是递归的出口

        TreeNode val=root.left;
        root.left=root.right;
        root.right=val;//交换根节点的左右节点

        //然后递归 以同样的方式继续交换左右节点下面的节点
        Mirror(root.left);
        Mirror(root.right);
    }
19. 包含min函数的栈【栈】【迭代器】

牛客剑指offer题目

  • 思路:
    有两种方法:方法1:剑指offer上的方法,利用一个辅助栈,基础栈中每存入一个元素,辅助栈中就存入最小的元素。详细可以参考书上的解析,代码可以参考链接中的武汉孙一峰的解析
    方法2:就是利用迭代器找到最小值返回最小值。
  • 代码:
    //包含min函数的栈
    //有两种方法:一种是借助辅助栈使用剑指offer书上的方法
    //一种是使用迭代器的方法

    //使用迭代器的方法比较简单,先使用迭代器的方法:
    Stack<Integer> stack=new Stack<>();
    public void push(int node) {
        stack.push(node);
    }

    public void pop() {
        stack.pop();
    }

    public int top() {
        return stack.peek();
    }

    public int min() {
        //迭代器的使用,需要导包
        Iterator<Integer> it =stack.iterator();
        int min=stack.peek();
        int tmp=0;
        while (it.hasNext()){
            tmp=it.next();//只是用一次
            if(min>tmp) min=tmp;
        }

//        Iterator<Integer> it =stack.iterator();
//        int min=stack.peek();
//        while (it.hasNext()){
//            if(min>it.next()) min=it.next();
//        } // 错误示范。错误原因:每次使用it.next都会是下一个元素了,所以循环中多次使用next肯定是有问题的
        return min;
    }
  • 知识:
    最主要的就是这个迭代器的错误使用,迭代器再使用的时候,我犯了一个错误就是在循环中多次使用了next,但是实际上,每次使用next都是使用的下一个元素,所以以后在使用next的时候,要先将其赋值给一个tmp变量,然后使用tmp去计算。
20.栈的压入、弹出序列【栈】

牛客剑指offer题目

  • 思路:
    栈中每次存入一个元素,就和popA去比较,相同就弹出,且popA进入下一个元素
    如果栈顶元素和popA的首个元素相等,那么需要出栈,
    出栈以后是要继续判断栈顶元素和popA的下一个元素是否相等的,
    如果相等要继续出栈,不相等才进行下一轮的进栈,

  • 代码:

public boolean IsPopOrder(int [] pushA,int [] popA){
        // 对于栈来说,长度是属性 
        if(pushA.length == 0 || popA.length== 0 || pushA.length != popA.length){
            return false;
        }
        
        int j=0;
        Stack<Integer> stack=new Stack<>();
        for(int i=0;i<pushA.length;i++){
            stack.push(pushA[i]);  //先把元素放入栈中,看看能不能把栈中的元素清空,能清空返回true
            
            //栈中每次存入一个元素,就和popA去比较,相同就弹出,且popA进入下一个元素
            //如果栈顶元素和popA的首个元素相等,那么需要出栈,
            //出栈以后是要继续判断栈顶元素和popA的下一个元素是否相等的,
            //如果相等要继续出栈,不相等才进行下一轮的进栈,
            //所以这里需要的应该是一个循环来进行 多次的判断
            while(!stack.isEmpty() && stack.peek()==popA[j]){
                stack.pop();
                j++;
            }
        }
        return stack.isEmpty();
    }
21.从上往下打印二叉树【二叉树】【打印】【队列】

牛客剑指offer题目

  • 思路:
    打印二叉树需要借助队列,将根节点先存入队列,然后只要队列不为空,就要一直循环,然后将队列中的节点逐个冒出,将值存入list中,然后把左右子节点再依次存放到队列中。
  • 代码:
//从上往下打印二叉树
    //打印链表需要借助队列
    public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
        ArrayList<Integer> list=new ArrayList<>();
        if(root==null) return list;
        Queue<TreeNode> queue=new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()){
            TreeNode temp=queue.poll();
            list.add(temp.val);
//            queue.offer(temp.left);
//            queue.offer(temp.right);
// 这样写是错的,一定要先判断是否为空再offer值,不然offer,提供的是null
            if(temp.left!=null)  queue.offer(temp.left);
            if(temp.right!=null) queue.offer(temp.right);
        }
        return list;
    }
22.二叉搜索树的后序遍历序列【二叉搜索树】【递归】

牛客剑指offer题目

  • 思路:
    搜索二叉树,根节点的值大于其左子树中任意一个节点的值,小于其右节点中任意一节点的值。
  • 代码:
//这是对的代码
public boolean VerifySquenceOfBST(int [] sequence) {
       //截取数列考虑Arrays.copyOfRange
         
        if(sequence.length==0 || sequence==null) return false;
         
        //1. 找到根节点
        int root=sequence[sequence.length-1];
        //2. 找到第一个大于根节点的索引
        //如果后面的值有小于根节点的,那么就返回false
        //如果后面的值都大于根节点,那么就递归左右子树,最后返回left&&right
        //这种递归不可能存在于else中,递归不可能存在循环中啊!!!
         
        // 找到第一个大于或者等于root的索引i
        //如果只有两个数得时候比如6,7那么是找到等于root=7得那个值得索引
        int i=0; //把i定义在外面,可以省一个索引的变量
        for(;i<sequence.length-1;i++){
            if(sequence[i]<root) continue;
            else break;  //一旦发现大于root的值,记录索引i 退出循环
        }
         
        for(int j=i ; j<sequence.length-1;j++){
            if(sequence[j]<root) return false;
            //else
            //如果有值小于root,那么就直接返回false了。
            //如果没有返回false,就直接说明了,所有值都大于root了,可以去完成递归了。
            //但是递归一定不要写在循环里面!!!写在外面。
        }
         
        //没有返回false,就说明所有值都大于root了,可以去进行递归了
        boolean left = true ;
        if(i>0){  //说明还有元素
            left = VerifySquenceOfBST(Arrays.copyOfRange(sequence,0,i)); //不包括下标i的数
        }
         
        boolean right = true;
        if(sequence.length-i-1>0){
            right = VerifySquenceOfBST(Arrays.copyOfRange(sequence,i,sequence.length-1));
        }
        return left&&right;
    }

对于上面得代码,可以完全通过,下面这段代码对于例子:[4,6,7,5]不能通过,找到错误得原因就是:当执行到 6,7这个数组得时候,会返回false,理由是index这里出现问题,对于上面那段代码:i是等于1的,i=0的时候满足循环条件执行循环体,然后i++之后的i变为1了。而对于下面那段错误的代码index=0,i=0执行循环条件,但是if不能执行,然后i=1了,但是index却还是0。所以导致后面判断的时候发现6<7直接返回false了。

所以再找 第一个大于root的索引i的值的时候,要注意只剩下两个元素的时候,最后一个为根,你找不到大于根的值了,此时要让这个索引等于这个根的索引。

//这是不能通过得代码,除了找序号那里有点不同,其他地方完全相同,但是就是不通过,搞不懂哪里有问题。
//搜索二叉树;根节点的值大于其左子树中任意一个节点的值,小于其右节点中任意一节点的值.
        // 0 2 1 5 3 7 9 8 6 length=9
        public boolean VerifySquenceOfBST ( int[] sequence){
            if(sequence.length==0 || sequence==null) return false;
            int val = sequence[sequence.length - 1];
            int index = 0;
            for (int i = 0; i < sequence.length-1; i++) {
                if (sequence[i] > val) {
                    index = i;
                    break;  // index=5
                }
            }//错误原因是索引找错了。
            /*
            for (int i = 0; i < sequence.length; i++) {
                if (sequence[i] >= val) {
                    index = i;
                    //System.out.println(index);
                    break;  // index=5
                }
            }*/  //改成这样索引就不会出错了,找到第一个大于或者等于根的索引         
//            System.out.println(index);
            for (int j = index; j < sequence.length - 1; j++) {
                if (sequence[j] < val) return false;
            }

            //同理遍历根节点的左子树和右子树
            boolean left = true;
            if (index > 0) {  //说明左子树还有元素
                left = VerifySquenceOfBST(Arrays.copyOfRange(sequence, 0, index));
            }
            boolean right = true;
            if (sequence.length - index - 1 > 0) {//9-5-1=3  还有3个元素说明,还有右子树
                right = VerifySquenceOfBST(Arrays.copyOfRange(sequence, index, sequence.length - 1));//索引是length-1
            }
            return left && right;  //两个都为true才返回true
        }
23.复杂链表的复制【链表】【HashMap】

牛客剑指offer题目

  • 思路:
    首先需要建立一个新的链表,这个新的链表要和原链表建立起来联系,选择使用hashmap映射将新旧链表建立起来联系,新链表存入label值。然后再将新链表连接起来,即存入next和random的值。
  • 代码:
//要想复制得到新的链表,首先你得创建一个新的链表,而且新的链表要和原链表建立起来联系
    public RandomListNode Clone(RandomListNode pHead){
        //先利用hashmap建立起来原链表和新链表的关系
        HashMap<RandomListNode,RandomListNode> map=new HashMap<>();//建立原链表与新链表的关系
        RandomListNode p1=pHead;用来创建新旧链表的对应节点关系
        while(p1!=null){
            map.put(p1,new RandomListNode(p1.label)); //把label逐个存入到新的链表中
            p1=p1.next;
        }//至此,新链表和旧链表关系创建成功,但是新链表只有label值,next和random都还为null

        RandomListNode p2=pHead;// 用来存储next和random指针
        while(p2!=null){
            //由于最后一个节点的next可能为null,所以需要判断一下,是否是最后一个节点
            //但是random就没有为null的节点,所以不需要判断
            if(p2.next!=null){
                map.get(p2).next=map.get(p2.next);  //map.get(p2)表示得到新链表的节点,这个节点的next值应当存储下一个节点,
                // 下一个节点就应该是刚才建立的新节点new RandomListNode的第二个节点
            }else{
                map.get(p2).next=null;
            }
            map.get(p2).random=map.get(p2.random);
            p2=p2.next;
        }
        return map.get(pHead);
    }
24.二叉搜索树与双向链表【二叉树】【链表】【ArrayList】

牛客剑指offer题目

  • 思路:
    二叉搜索树,左子树的所有节点的值小于根节点的值,右子树所有节点的值大于根节点的值,左<根<右。
    将二叉搜索树变为排序好的链表实际上就是中序遍历,然后将中序的结果串起来。
    中序遍历参考博客
  • 代码:
 ArrayList<TreeNode> list=new ArrayList<>();
    public TreeNode Convert(TreeNode pRootOfTree){
        if(pRootOfTree == null) return null;
        //二叉搜索树,左子树的所有节点的值小于根节点的值,右子树所有节点的值大于根节点的值,左<根<右
        //将二叉搜索树变为排序好的链表实际上就是中序,然后将中序的结果串起来
        Trans(pRootOfTree); //得到排序好的list
        //2 5 9 串联起来
        for(int i=0;i<list.size()-1;i++){
            list.get(i).right=list.get(i+1);//当前值的右节点 存的应该是list中的下一个值
            list.get(i+1).left=list.get(i); //i+1 的left存的是前一个节点信息
            //这样写的话,就不涉及到特殊的第一个节点和最后一个节点了
        }
        return list.get(0);
    }
    public void Trans(TreeNode pRootOfTree){
        if(pRootOfTree==null) return ;
        else {
            Trans(pRootOfTree.left);
            list.add(pRootOfTree);
            Trans(pRootOfTree.right);
        }
    }
  • 知识点:
    将list串联起来为双线链表的时候,注意i的取值,从0开始到<size-1即可,这种方法设计的好厉害啊。
25. 【未解决】二叉树中和为某一值的路径 【未解决】
26. 字符串的排列【未解决】
27.最小的K个数【数组】【排序】

牛客剑指offer题目

  • 思路:
    思路很简单,就是先使用快排进行排序,然后取前k个元素即可。
  • 代码:
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k){
        ArrayList<Integer> list=new ArrayList<>();
        if(k>input.length) return list;  //注意判断k是否小于input的长度,如果大于是不符合条件的
        Arrays.sort(input);
        for(int i=0;i<k;i++){
            list.add(input[i]);
        }
        return list;
    }
28.【未解决】顺时针打印矩阵
29. 【待优化】数组中出现次数超过一半的数字【数组】
30. 【待解决】连续子数组的最大和

牛客剑指offer上题目

31.整数中1出现的次数(从1到n整数中1出现的次数)【数字变字符串】【数组】【字符串】

牛客剑指offer上的题目

  • 思路:
    就是把数字变成字符串,然后字符串变为数组,检查数组的每个值是否是1。
  • 代码:
public int NumberOf1Between1AndN_Solution(int n){
        int count=0;
        for(int i=0;i<=n;i++){
            String str=String.valueOf(i);
            char[] arr=str.toCharArray();
            for(int j=0;j<arr.length;j++){
                if(arr[j] == '1') count++;
            }
        }
        return count;
    }
  • 知识点:
    主要要记住数字怎么变为字符串,String str=String.valueOf(i);
    然后补充一下字符串变为数字:int i=Integer.parseInt(str)
32. 把数组排成最小的数【数字变字符串】【数组】【字符串】

牛客上剑指offer题目

  • 思路:
    比较两个字符串s1, s2大小的时候,先将它们拼接起来,比较s1+s2,和s2+s1那个大,如果s1+s2大,那说明s2应该放前面,所以按这个规则,s2就应该排在s1前面。所以其实就是通过循环,每次循环得到一个最合适数。
  • 代码:
public String PrintMinNumber(int [] numbers){
        for(int i=0;i<numbers.length;i++){  //确保每个位置得到最小的数字,
            // 比如第一轮循环确保第一个位置是最小的,第二轮循环确保第二个位置是最小的
            for(int j=i+1;j<numbers.length;j++){ //和上面那个数字进行比较
                Long val1=Long.valueOf(numbers[i]+""+numbers[j]);  //数字变字符串,以及字符串变为数字
                Long val2=Long.valueOf(numbers[j]+""+numbers[i]);
                if(val2<val1){
                    int tmp=numbers[i];
                    numbers[i]=numbers[j];
                    numbers[j]=tmp;
                }
            }
        }
        String reslut=new String("");
        for(int i=0;i<numbers.length;i++){
            reslut=reslut+""+numbers[i];
        }
        return reslut;
    }
  • 知识点:
    主要要记住数字怎么变为字符串,String str=String.valueOf(i);
    String str=i+""
    然后补充一下字符串变为数字:int i=Integer.parseInt(str)
【未解决】 33. 丑数
34. 第一个只出现一次的字符【hashmap】【字符串】【出现次数】

牛客剑指offer上题目

  • 思路:
    凡是出现统计次数的问题都要考虑hashmap。
  • 代码:
public int FirstNotRepeatingChar(String str) {
        HashMap<Character,Integer> map = new HashMap<>();
        for(int i=0;i<str.length();i++){ //字符串的length是方法,不是属性
            char c=str.charAt(i);
            //取出字符,然后判断这个字符在 hashmap的键中是否存在,存在则值加1 不存在,则添加并将值设为1
            if(map.containsKey(c)){
                int time = map.get(c); //取出字符所对应的值
                time++;
                map.put(c,time);
            }else{
                map.put(c,1); //如果不存在这个键,就存放进去,让次数为1
            }
        }

        //遍历取出第一个出现一次的字符
        for(int i=0;i<str.length();i++){  //遍历字符串,而不是遍历hashmap
            char c=str.charAt(i);
            int time = map.get(c);
            if(time==1) return i;
        }
        return -1;
    }
  • 知识点:
    字符串在拆分的时候,不是只有变为数组这一种方法啊,还可以调用charAt()方法来得到某一个字符。
    另外hashmap在存值的时候,只能通过一种put函数存,不能通过对get某一个元素进行赋值的方法存值。
【未解决】35.数组中的逆序对
36.两个链表的第一个公共结点【链表】

牛客剑指offer上题目

  • 思路:
    求2个链表的长度,然后让长的链表先走差个长度。在同时遍历两个链表,遇到的第一个相同的节点作为返回值即可。
  • 代码:
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2){
        //求2个链表的长度
        int count1=0;
        ListNode p1=pHead1;
        while (p1!=null){
            count1++;
            p1=p1.next;
        }

        int count2=0;
        ListNode p2=pHead2;
        while (p2!=null){
            count2++;
            p2=p2.next;
        }

        //让长的链表先走  两个长度之差的  步数
        int sub=count1-count2;
        p1=pHead1;
        p2=pHead2;
        if(sub>0){ //说明链表1长
            for(int i=0;i<sub;i++){
                p1=p1.next;
            }
        }else {
            sub=-sub;
            for (int i=0;i<sub;i++){
                p2=p2.next;
            }
        }

        //2个链表一起走,相同的那个节点就作为返回值
        while (p1!=null && p2!=null){
            if(p1==p2) return p1;
            p1=p1.next;
            p2=p2.next;
        }
        return null;
37. 数字在排序数组中出现的次数【二分法】
  • 思路:
    自己的方法太low了,就是遍历。有机会看看二分法把。
  • 代码:
public int GetNumberOfK(int [] array , int k){
        int num=0;
        for(int i=0;i<array.length;i++){
            if(array[i] == k) num++;
        }
        return num;
    }
38.二叉树的深度【二叉树】【队列】

牛客上剑指offer题目

  • 思路:
    和打印二叉树差不多,借助对列,只不过这里需要一个额外的size变量来记录每层节点的个数,这一层的所有节点都出队以后,结果才递增一个。
  • 代码:
public int TreeDepth(TreeNode root){
        if(root==null) return 0;
        //打印二叉树的时候用到了对列,现在这个和打印二叉树差不多,也考虑队列
        Queue<TreeNode> queue=new LinkedList<>();
        queue.offer(root);
        int result=0;
        while (!queue.isEmpty()){
            int size=queue.size();
            for(int i=0;i<size;i++){
                TreeNode temp = queue.poll();
                if(temp.left!=null) queue.offer(temp.left);
                if(temp.right!=null) queue.offer(temp.right);
            }
            result++;
        }
        return result;
    }
39.平衡二叉树【二叉树深度】【递归】【Math】

牛客网上剑指offer题目
可参考博客

  • 思路:
    平衡二叉树的定义就是:
    条件一:它必须是二叉查找树。
    条件二:每个节点的左子树和右子树的高度差至多为1。
    所以先设定一个代码用来求二叉树的深度。
    然后判断是否是平衡二叉树的时候,要保证左右子树都是平衡二叉树,同时左右子树的高度最多为1,即左右子树的深度小于2.
  • 代码:
 //平衡二叉树
    public boolean IsBalanced_Solution(TreeNode root){
        if(root==null) return true;
        return IsBalanced_Solution(root.right) && IsBalanced_Solution(root.left) &&
                Math.abs(TreeDeepth(root.left)-TreeDeepth(root.right))<2;
    }
    //求二叉树高度的代码
    public int TreeDeepth(TreeNode root){
        if(root==null) return 0;
        int left=TreeDeepth(root.left);
        int right=TreeDeepth(root.right);
        return Math.max(left,right)+1;
    }
  • 知识点:
    二叉树深度的代码。
    Math工具的使用。Math.max(a,b)以及Math.abs(a-b) 注意这里面是减法。
40.数组中只出现一次的数字【hashmap】【次数】

牛客剑指offer代码

  • 思路:
    统计次数,用hashmap。
    新建的map中的值是null,所以不能使用map.get(元素) ==0这种没办法确定次数,要用map.containsKey
  • 代码:
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]){
        //统计次数用hashmap
        HashMap<Integer,Integer> map=new HashMap<>();
        for(int i=0;i<array.length;i++){
            //新建的map中的值是null,所以不能使用map.get(元素) ==0这种没办法确定次数
            if(map.containsKey(array[i])){
                int time=map.get(array[i])+1;
                map.put(array[i],time);
            }else{
                map.put(array[i],1);
            }
        }
        //存入到两个数组中,先借助list倒一下
        ArrayList<Integer> list=new ArrayList<>();
        for (int i=0;i<array.length;i++){
            if(map.get(array[i])==1) list.add(array[i]);
        }
        num1[0]=list.get(0);
        num2[0]=list.get(1);
    }
41. 和为S的连续正数序列【Arraylist】【数组】

牛客网剑指offer上题目

  • 思路:
    for循环遍历到sum的所有值,然后再求和的过程中需要引入变量j,来作为逐渐递增的元素,借助while循环判断是否继续递增。
  • 代码:
public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum){
        ArrayList<ArrayList<Integer>> result=new ArrayList<>();
        for(int i=1;i<sum;i++){  //连续正序列,不包括本身
            int temp=0;  //用来记录和
            int j=i;//递增相加的时候,不能用i,所以引入另一个变量j来做递增相加
            while (temp<sum){
                temp=temp+j;
                j++;
            }

            if(sum==temp){
                ArrayList<Integer> list_temp=new ArrayList<>();  //if里面的变量是局部变量
                for(int m=i;m<j;m++){
                    list_temp.add(m);
                }
                result.add(list_temp);
            }
        }
        return result;
    }
42.和为S的两个数字【数组】【双指针】

牛客网剑指offer上题目

  • 思路:
    首先是找和为sum的组合,找的方法可以使用两个指针,一个头,一个尾,然后求和,移动指针。将乘积可以存在链表的第三个位置。求乘积最小的时候,选择遍历每个第三位的倍数。
  • 代码:
public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum){
        //首先数组是递增的所以可以使用两个指针同时移动的方法
        //先将所有和为sum的结果存放在一个list中
        ArrayList<Integer> list=new ArrayList<>();
        int i=0;
        int j=array.length-1;
        while(i<j){
            if(array[i]+array[j]>sum){
                j--;
            }else if(array[i]+array[j]<sum){
                i++;
            }else{
                list.add(array[i]);
                list.add(array[j]);
                //把乘也存入
                list.add(array[i]*array[j]);
                i++;  //和为sum以后,2个指针都要移动
                j--;
            }
        }
//        System.out.println(list);
        ArrayList<Integer> result=new ArrayList<>();
        if(list.size()==0) return result;  //如果没有和为sum的就返回空的就可以了
        int temp=list.get(2);
        for(int m=2;m<list.size();m=m+3){
            if(list.get(m)<=temp){
                result.add(list.get(m-2));
                result.add(list.get(m-1));
            }
        }
//        System.out.println(result);
        return result;
    }
43.左旋转字符串【字符串】

牛客网剑指offer题目

  • 思路:
    移动的时候注意元素变化。
  • 代码:
public String LeftRotateString(String str,int n) {
        char[] arr=str.toCharArray();
        char[] result=str.toCharArray();
        for(int i=0;i<arr.length;i++){
            int index=i-n%arr.length;
            if(index<0) index=index+arr.length;
            result[index]=arr[i];
        }
        return new String(result);
    }
44.翻转单词顺序列【字符串】

牛客网上剑指offer题目

  • 思路:
    先交换数组,记得只交换一半。然后将新的数组拼接成新的字符串。
  • 代码:
public String ReverseSentence(String str){
        String[] strs=str.split(" ");
        for(int i=0;i<strs.length/2;i++){  //记得除以2啊,只交换一次即可
            String temp=strs[i];
            strs[i]=strs[strs.length-1-i];
            strs[strs.length-1-i]=temp;
        }

        //将字符串数组,拼成字符串
        StringBuffer result=new StringBuffer();
        for(int i=0;i<strs.length;i++){
            result.append(strs[i]);
            result.append(" ");
        }
        return result.toString().trim();  //去掉空格不是很容易吗。。。
        /*String result="";
        for(int i=0;i<array.length;i++){
            result = result+" "+array[i];
        }
        return result.trim();*/  //也可以使用这种方式对字符串进行拼接
    }
45.扑克牌顺子【数组】

牛客网剑指offer上题目

  • 思路:
    先排序,然后计算0的个数,后面的差值,差一个2,0的个数减少一个,差一个3,0的个数减少2个。最后统计0个个数的正负。
    注意两种特殊情况:首先数组为空,直接返回false。
    其次,一旦有两个相同的元素,直接返回false
  • 代码:
public boolean isContinuous(int [] numbers){
    // 1 3 5 0 0 其中0可以当作任何数  1 2 3 4 5可以构成顺子
    //先排序 0 0 1 3 5 然后求除了0以外的差值,2补一个0 3补2个0
    if(numbers.length==0) return false;  //注意几种特殊情况,首先数组为空,直接返回false
    Arrays.sort(numbers);
    int count_zero=0;
    for(int i=0;i<numbers.length;i++){
        if(numbers[i]==0) count_zero++;
        else break;
    } //0的个数
    for(int i=count_zero+1;i<numbers.length;i++){
        if(numbers[i] ==numbers[i-1]) return false;  //其次,一旦有两个相同的元素,直接返回false
        int sub=numbers[i]-numbers[i-1];
        if(sub==1) continue;
        else count_zero=count_zero-(sub-1);
    }
    if (count_zero>=0) return true;
    else return false;
}

46.孩子们的游戏(圆圈中最后剩下的数)【数组】【循环】【需要仔细研读】

牛客网上剑指offer题目

  • 思路:
    先将元素存入到list中。list中只要元素数量大于1,就要一直删除元素。删除元素靠循环m,没循环一次删除一个元素,所以需要一个额外的元素cur来记录当前位置,从当前位置往后不断循环。
  • 代码:
public int LastRemaining_Solution(int n, int m){
        if(n<1||m<1) return -1;  //注意特殊情况
        ArrayList<Integer> list=new ArrayList<>();
        for(int i=0;i<n;i++){
            list.add(i);
        } //将孩子序号存入到一个list中  [0 1 2 3 4 5 6]  m=3
        int cur=-1;  //指向当前指针
        while(list.size()>1){
            for(int i=0;i<m;i++){//控制循环次数,每到这个循环执行一次删除一个元素
                cur++;
                if(cur==list.size()) cur=0;
            }//循环执行完一遍,删除一个元素
            list.remove(cur);//删除这个元素以后,cur需要变化一下   删除2以后,变为[0 1 3 4 5 6] 此时cur=2,但是下一次循环中需要cur为2,所以这里先递减一次
            cur--;
        }
        return list.get(0);
    }
47.求1+2+3+…+n【递归】
  • 代码:
public int Sum_Solution(int n) {
        if(n==1) return 1;
        return n+Sum_Solution(n-1);
    }
48. 【待解决】不用加减乘除做加法【运算符】【进制】
49. 把字符串转换成整数
50. 数组中重复的数字

牛客网上剑指offer题目

  • 思路:
    如果是正确排序,应该是让每个元素处于自己应该在的位置,即numbers[i]=i才是正确的。所以就不断交换,直到元素到了正确位置,一旦发现交换的两个位置相同了,则说明这个就是重复元素,返回这个元素,并且返回true即可。
  • 代码:
public boolean duplicate(int numbers[],int length,int [] duplication){
        for(int i=0;i<length;i++){
            while(numbers[i]!=i){  //不断的交换位置,让数字处于属于自己该有的位置处
                //不在它应该在的位置,就交换元素,交换过程中一旦发现,这两个元素相同,那么这就是重复元素了。
                int val=numbers[i];   //多定义一个变量可以省每次去查找数组的时间
                if(val==numbers[val]){
                    duplication[0]=numbers[i];
                    return true;
                }else{
                   // int temp=numbers[i];
                    numbers[i]=numbers[val];
                    numbers[val]=val;
                }
            }
        }
        return false;
    }
51.构建乘积数组

剑指offer题目

  • 思路:
    摆矩阵,横坐标是A,纵坐标是B,参考:披萨大叔
    先计算下三角,再计算上三角。
  • 代码:
public int[] multiply(int[] A) {
        //利用矩阵法,画图解析一下,先计算下三角,再计算上三角
        int[] B=new int[A.length];
        //先计算下三角
        B[0]=1;
        for(int i=1;i<B.length;i++){
            B[i]=B[i-1]*A[i-1];
        }
        //计算上三角
        int temp=1;
        for(int i=B.length-2;i>=0;i--){
            temp=temp*A[i+1];  //先计算右三角,再让右三角和B相乘
            B[i]=B[i]*temp;
        }
        return B;
    }
52.正则表达式匹配
53.表示数值的字符串
54.字符流中第一个不重复的字符
55.链表中环的入口结点

牛客网上剑指offer题目

  • 思路:
    先判断是否有环,两个指针都从头开始走,一个一次走一步,一个一次走两步,如果有环那么快和慢指针一定会相遇,如果没环,那么快指针同时快指针的next都会指向null。
    然后判断环中节点的个数,刚才的两个相遇的指针一定是环中的点,当这个环的点再次回到这个点,走了几步就是环中节点的个数。
    然后一个指针从头开始,另一个从环的节点个数那里开始,两者相遇就是环的开端。
  • 代码:
if(pHead == null){
            return null;
        }
        // 判断是否是环,两个指针从头节点开始,slow一次走一个,fast一次走俩个,
        //如果是环,fast总会返回来和slow相遇,如果不是环,fast走到尾节点也不会和slow相遇
        boolean isHuan = false; //默认不是环
        ListNode slow=pHead;
        ListNode fast=pHead;
        while(fast!=null && fast.next!=null){ //fast没到尾节点,就一直往后走,到尾节点,也没相遇,那就不是环
            slow=slow.next;
            fast=fast.next.next;
            if(slow == fast){
                isHuan = true;
                break;
            }
        }
         
        //如果不是环,那么返回null
        if(!isHuan)  {
            return null;
        }else{
            //如果是环那么,确定环中节点的个数,刚才相遇的节点一定是环中的节点,
        //所以从这个节点开始,在回到这个节点所走的步数也就是环中节点的个数了
            int n=1;
        // fast和slow现在指的是同一个节点,相遇的那个节点
            fast=fast.next;
            while(fast!=slow){
                fast=fast.next;
                n++;
            }   //n是环中节点的个数
         
        //找到节点的入口,一个节点从头开始,另一个节点从节点长度那么长开始,相遇的节点就是环的入口
            slow = pHead;
            fast = pHead;
            for(int i=0;i<n;i++){
                fast=fast.next;
            }
        //然后每个节点走一步,直到相遇,则为节点的入口
            while(slow!=fast){
                slow=slow.next;
                fast=fast.next;
            }
        }       
        return fast;
    }
56. 删除链表中重复的结点

牛客网上剑指offer答案

  • 思路:
    有一点很重要的是,循环链表的时候,需要注意最后一个节点,当p指向最后一个节点的时候,p的next是null,如果你使用p.next!=null作为判断条件是可以的,但是如果你后面写p.next.val会发生异常,因为没有val这个值,所以有时我们考虑的是判断while (p.next!=null)
  • 代码:
public ListNode deleteDuplication(ListNode pHead){
        HashSet<Integer> set=new HashSet<>();
        ListNode p=pHead;
        while (p.next!=null){
            //到最后一个指针的时候,是p.next为null,因为后面用到了p.next.val,空指针是没有val的,所以这里只能写p.next
            if(p.val == p.next.val) set.add(p.val);
            p=p.next;
        }
        //重新组合一个链表作为返回值,首先单独处理头节点
        while (pHead!=null && set.contains(pHead.val)){
            pHead=pHead.next;
        }
        if(pHead==null) return null;
        ListNode pre=pHead;
        ListNode cur=pHead.next;
        while (cur!=null){
            if(set.contains(cur.val)){
                pre.next=cur.next;
                cur=cur.next;
            }else{
                pre=pre.next;
                cur=cur.next;
            }
        }
        return pHead;
    }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值