剑指offer经典66答案汇总(2)

这篇博客汇总了《剑指Offer》中的66道经典算法题目,涵盖了字符串操作、数组处理、链表问题、二叉树结构以及数据流中的算法挑战。包括寻找唯一字符、逆序对、链表公共节点、数字出现次数、二叉树深度等,旨在提升编程思维和解决实际问题的能力。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

34 第一个只出现一次的字符

35 数组中的逆序对  

36 两个链表的第一个公共结点

37 数字在排序数组中出现的次数

38 二叉树的深度

39 平衡二叉树

40 数组中只出现一次的数字

41 和为S的连续正数序列

42 和为S的两个数字

43 左旋转字符串

44 翻转单词顺序列

45 扑克牌顺子

46 孩子们的游戏(圆圈中最后剩下的数)

47 求1+2+3+…+n

48 不用加减乘除做加法

49 把字符串转换成整数

50 数组中重复的数字

51 构建乘积数组

52 正则表达式匹配

53 表示数值的字符串

54 字符流中第一个不重复的字符

55 链表中环的入口结点

56 删除链表中重复的结点

57 二叉树的下一个结点

58 对称的二叉树

59 按之字形顺序打印二叉树

60 把二叉树打印成多行

61 序列化二叉树

62 二叉搜索树的第k个结点

63 数据流的中位数

64 滑动窗口的最大值

65 矩形中的路径

66 机器人的运动范围


34 第一个只出现一次的字符

在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).

public class Solution {
   public int FirstNotRepeatingChar(String str) {
       if(str.equals(""))
           return -1;
       int [] temp = new int[256];
       int index = 1;
       char [] strArray = str.toCharArray();
       for(int i=0;i<strArray.length;i++)
       {
           if(temp[(int)strArray[i]] == 0)
           {
               temp[(int)strArray[i]] = index;
               index++;
           }else{
               temp[(int)strArray[i]] = -1;
           }
       }//这个循环就是遍历一遍,找到出现一次的字符
       //只要index大于0就是出现一次的字符
       int minIndex = Integer.MAX_VALUE;
       char ch = '1';
       for(int i=0;i<temp.length;i++)
       {
           if(temp[i] > 0)
           {
               if(temp[i] < minIndex)
               {//找最小的index对应的字符
                   minIndex = temp[i];
                   ch = (char)i;
               }
           }
       }
       int result = -1;
       for(int i=0;i<strArray.length;i++)
       {//去找这个字符的下标!
           if(strArray[i] == ch)
               return i;
       }
       return -1;
   }
}

35 数组中的逆序对  

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007

public class Solution {
    public static int InversePairs(int [] array) {
        if(array==null||array.length==0)
        {
            return 0;
        }
        int[] copy = new int[array.length];
        for(int i=0;i<array.length;i++)
        {
            copy[i] = array[i];
        }
        int count = InversePairsCore(array,copy,0,array.length-1);//数值过大求余
        return count;

    }
    private  static int InversePairsCore(int[] array,int[] copy,int low,int high)
    {
        if(low==high)
        {
            return 0;
        }
        int mid = (low+high)>>1;
        int leftCount = InversePairsCore(array,copy,low,mid)%1000000007;
        int rightCount = InversePairsCore(array,copy,mid+1,high)%1000000007;
        int count = 0;
        int i=mid;
        int j=high;
        int locCopy = high;
        while(i>=low&&j>mid)
        {
            if(array[i]>array[j])
            {
                count += j-mid;
                copy[locCopy--] = array[i--];
                if(count>=1000000007)//数值过大求余
                {
                    count%=1000000007;
                }
            }
            else
            {
                copy[locCopy--] = array[j--];
            }
        }
        for(;i>=low;i--)
        {
            copy[locCopy--]=array[i];
        }
        for(;j>mid;j--)
        {
            copy[locCopy--]=array[j];
        }
        for(int s=low;s<=high;s++)
        {
            array[s] = copy[s];
        }
        return (leftCount+rightCount+count)%1000000007;
    }
}

36 两个链表的第一个公共结点

输入两个链表,找出它们的第一个公共结点。

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
   public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
       if(pHead1 == null || pHead2 == null)
           return null;
       int length1 = 0;
       int length2 = 0;
       ListNode p1 = pHead1,p2 = pHead2;
       while(p1 != null)
       {
           length1++;
           p1 = p1.next;
       }
       while(p2 != null)
       {
           length2++;
           p2 = p2.next;
       }//分别求两个链表长度
       int cha = 0;
       if(length1 > length2)
       {//求长度差,长的先走长度差个单位
           cha = length1 - length2;
           p1 = pHead1;
           p2 = pHead2;
           for(int i=0;i<cha;i++)
               p1 = p1.next;
       }else{
           cha = length2 - length1;
           p2 = pHead2;
           p1 = pHead1;
           for(int i=0;i<cha;i++)
               p2 = p2.next;
       }
       while(p1 != p2)
       {//找第一个相同的节点
           p1 = p1.next;
           p2 = p2.next;
       }
       return p1;
   }
}

37 数字在排序数组中出现的次数

统计一个数字在排序数组中出现的次数。

public class Solution {
   public int GetNumberOfK(int [] array , int k) {
       int leftIndex = -1,start=0,end=array.length-1,rightIndex=-1;
       while(start <= end)
       {
           int mid = (start+end)/2;
           if(array[mid] > k)
           {
               end = mid-1;
           }else if(array[mid] < k){
               start = mid+1;
           }else{
               leftIndex = mid;
               end = mid-1;
           }
       }
       start = 0;
       end = array.length-1;
       while(start <= end)
       {
           int mid = (start+end)/2;
           if(array[mid] > k)
           {
               end = mid-1;
           }else if(array[mid] < k){
               start = mid+1;
           }else{
               rightIndex = mid;
               start = mid+1;
           }
       }
       if(array.length == 0 || rightIndex == -1)
           return 0;
       return rightIndex-leftIndex+1;
   }
}

38 二叉树的深度

输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。

public class Solution {
    public int TreeDepth(TreeNode root) {
        if (root == null)
            return 0;
        if (root != null && root.left == null && root.right == null)
            return 1;
        return TreeDepth(root.left)>TreeDepth(root.right)?TreeDepth(root.left)+1:TreeDepth(root.right)+1;
    }
}

39 平衡二叉树

输入一棵二叉树,判断该二叉树是否是平衡二叉树。

public class Solution {
    public boolean IsBalanced_Solution(TreeNode root) {
        if( root == null) { //一棵空树就是平衡二叉树
            return true;
        }
        if( Math.abs(getDepth(root.left) - getDepth(root.right)) <= 1 ) {
            //满足左右子树高度差小于等于1,那就接着判断左右子树是不是二叉树
            return (IsBalanced_Solution(root.left) && IsBalanced_Solution(root.right));
        } else {
            //不满足左右子树高度差小于等于1,那这棵树肯定不是平衡二叉树啦
            return false;
        }
    }
    
    public int getDepth(TreeNode root) {
        if( root == null ) return 0;
        int left = getDepth(root.left);
        int right = getDepth(root.right);
        return ( left > right ? left : right ) + 1;//树的高度怎么计算就不用我讲了吧
    }
}

40 数组中只出现一次的数字

一个整型数组里除了两个数字之外,其他的数字都出现了偶数次。请写程序找出这两个只出现一次的数字。

//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        int temp = 0;
        for (int i = 0; i < array.length; i++) {
            temp = temp ^ array[i];
        }
        int index = findOne (temp);
        num1[0] = 0;
        num2[0] = 0;
        for (int i = 0; i < array.length; i++) {
            if(IsBit1(array[i], index)){
                num1[0] ^= array[i];
            } else {
                num2[0] ^= array[i];
            }
        }
    }
   private int findOne(int number)
   {
       int index = 0;
       while((number & 1) == 0)
       {
           index++;
           number = number >> 1;
       }
       return index;
   }
   private boolean IsBit1(int number,int index)
   {
       number = number >> index;
       if((number & 1) == 0)
           return false;
       return true;
   }
}

41 和为S的连续正数序列

小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!

import java.util.ArrayList;
import java.util.LinkedList;
public class Solution {
    public ArrayList<ArrayList<Integer>> FindContinuousSequence(int sum) {
        ArrayList<ArrayList<Integer>> res = new ArrayList();
        if(sum <= 1){
            return res;    
        }
        LinkedList<Integer> queue = new LinkedList();
        int n = 1, halfSum = (sum >> 1) + 1, queueSum = 0;
        while(n <= halfSum){
            queue.addLast(n);
            queueSum += n;
            if(queueSum > sum){
                while(queueSum > sum){
                    queueSum -= queue.pollFirst();
                }
            }
            if(queueSum == sum){
                ArrayList<Integer> one = new ArrayList();
                one.addAll(queue);
                res.add(one);
                queueSum -= queue.pollFirst();
            }
            n++;
        }
        return res;
    }
}

42 和为S的两个数字

输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。

import java.util.ArrayList;

public class Solution {
    public ArrayList<Integer> FindNumbersWithSum(int [] array, int sum){
        ArrayList<Integer> resultList = new ArrayList<>();
        if(array.length <= 1)
            return resultList;
        for(int i=0,j=array.length-1;i<j;)
        {
            if(array[i] + array[j] == sum)
            {
                resultList.add(array[i]);
                resultList.add(array[j]);
                return resultList;
            //找到直接返回
            }else if(array[i] + array[j] > sum)
            {
            //和已经大于目标值了,所以大数要减小!
                j--;
            }else{
            //和小于目标值,所以小数要表大!
                i++;
            }
        }
        return resultList;
    }
}

43 左旋转字符串

汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!

public class Solution {
    public String LeftRotateString(String str,int n) {
        if (str == null || str.equals(""))
            return "";
        n = n % str.length();
        char [] strA = str.toCharArray();
        reverse(strA, 0, str.length() - 1);
        reverse(strA, 0, str.length() - n - 1);
        reverse(strA, str.length() - n, str.length() - 1);
        return String.valueOf(strA);
    }
    public void reverse(char [] strA, int start, int end) {
        while(start < end) {
            char ch = strA[start];
            strA[start] = strA[end];
            strA[end] = ch;
            start ++;
            end --;
        }
    }
}

44 翻转单词顺序列

牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?

public class Solution {
    public String ReverseSentence(String str) {
        if(str == null || str == "")
            return str;
        char[] strA = str.toCharArray();
        reverse(strA, 0, strA.length - 1);
        int begin = 0;
        for(int i = 0; i < strA.length; i++) {
            if(strA[i] == ' ') {
                reverse(strA, begin, i - 1);
                begin = i + 1;
            }
        }
        if (begin < strA.length - 1){
            reverse(strA, begin, strA.length - 1);
        }
        return String.valueOf(strA);
    }
    private void reverse(char [] str, int begin, int end) {
        while (begin < end) {
            char temp = str[begin];
            str[begin] = str[end];
            str[end] = temp;
            begin ++;
            end --;
        }
    }
}

45 扑克牌顺子

LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张^_^)...他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子.....LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何, 如果牌能组成顺子就输出true,否则就输出false。为了方便起见,你可以认为大小王是0。

import java.util.Arrays;
public class Solution {
    public boolean isContinuous(int [] numbers) {
        if (numbers.length <= 4)
            return false;
        Arrays.sort(numbers);
        int numberZero = 0;
        int numberNeed = 0;
        for(int i = 0; i < numbers.length; i++) {
            if (numbers[i] == 0) {
                numberZero ++;
            } else {
                if(i < (numbers.length - 1)) {
                    if (numbers[i] == numbers[i + 1])
                        return false;
                    numberNeed += (numbers[i + 1] - numbers[i] - 1);
                }
            }
        }
        return numberZero >= numberNeed;
    }
}

46 孩子们的游戏(圆圈中最后剩下的数)

每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)

import java.util.*;
public class Solution {
    public int LastRemaining_Solution(int n, int m) {
        LinkedList<Integer> list = new LinkedList<>();
        for(int i = 0; i < n; i++) {
            list.add(i);
        }
        int a = 0;
        while(list.size() > 1) {
            a = (a + m - 1) % list.size();
            list.remove(a);
        }
        if (list.size() == 1) {
            return list.get(0);
        }
        return -1;
    }
}

47 求1+2+3+…+n

求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

public class Solution {
    public int Sum_Solution(int n) {
        int sum = n;
        //短路与,当n小于0的时候,就不执行后面的代码了
        boolean ans = (n>0)&&((sum+=Sum_Solution(n-1))>0);
        return sum;
    }
}

48 不用加减乘除做加法

写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。

public class Solution {
    public int Add(int num1,int num2) {
        int sum = 0;
        int carry = 0;
        do {
            sum = num1 ^ num2;
            carry = num1 & num2;
            if (carry != 0) 
                carry = carry << 1;
            num1 = sum;
            num2 = carry;
        } while(carry != 0);
        return sum;
    }
}

49 把字符串转换成整数

将一个字符串转换成一个整数(实现Integer.valueOf(string)的功能,但是string不符合数字要求时返回0),要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0。

public class Solution {
    public int StrToInt(String str) {
        if(str == null || str == "" || str.equals("+") || str.equals("-"))
            return 0;
        int flag = 0;
        long sum = 0;
        char [] strA = str.toCharArray();
        for(int i = 0; i < strA.length; i++) {
            if(strA[0] == '-' && i == 0){
                flag = 1;
                continue;
            }
            if(strA[0] == '+' && i == 0){
                continue;
            }
            if(!judge(strA[i]))
                return 0;
            sum = sum * 10 + strA[i] - '0';
        }
        if (flag == 1) {
            sum = sum * (-1);
            if (sum < Integer.MIN_VALUE)
                return 0;
            return (int)sum;
        }
        if (sum > Integer.MAX_VALUE)
            return 0;
        return (int)sum;
    }
    public boolean judge(char ch) {
        int number = ch - '0';
        if(number >= 0 && number <= 9) {
            return true;
        }
        return false;
    }
}

50 数组中重复的数字

在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。

public class Solution {
    // Parameters:
    //    numbers:     an array of integers
    //    length:      the length of array numbers
    //    duplication: (Output) the duplicated number in the array number,length of duplication array is 1,so using duplication[0] = ? in implementation;
    //                  Here duplication like pointor in C/C++, duplication[0] equal *duplication in C/C++
    //    这里要特别注意~返回任意重复的一个,赋值duplication[0]
    // Return value:       true if the input is valid, and there are some duplications in the array number
    //                     otherwise false
    public boolean duplicate(int numbers[], int length, int[] duplication) {
		if (numbers == null || length <= 0)
			return false;
		// 必须保证数组中的元素都在0到n-1之间
		for (int i = 0; i < length; i++)
			if (numbers[i] < 0 || numbers[i] > length)
				return false;
 
		int[] count = new int[length];
		for (int i = 0; i < length; i++) {
			count[i] = 0;
		}
 
		for (int i = 0; i < length; i++) {
			if (count[numbers[i]] == 1) {
				duplication[0] = numbers[i];
				return true;
			}
			count[numbers[i]]++;
		}
		return false;
	}
}

51 构建乘积数组

给定一个数组A[0,1,...,n-1],请构建一个数组B[0,1,...,n-1],其中B中的元素B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。不能使用除法。

import java.util.ArrayList;
public class Solution {
    public static int[] multiply(int[] A) {
		if (A == null || A.length <= 0) {
			return null;
		}
		int[] c = new int[A.length];
		int[] d = new int[A.length];
		int[] b = new int[A.length];
		
		d[0] = 1; // 计算D
	    for (int i = 1; i < A.length; i++) {
	        d[i] = d[i-1] * A[i-1];
	    } 	    
	    c[A.length-1] = 1; // 计算C
	    for (int j = A.length-1; j > 0; j--) {
	        c[j-1] = c[j] * A[j]; 
	    }	    
	    // 计算B
	    for (int i = 0; i < A.length; i++) {
	        b[i] = d[i]*c[i];
	    }	    
	    return b;
    }
}

52 正则表达式匹配

请实现一个函数用来匹配包括'.'和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配

public class Solution {
    public boolean match(char[] str, char[] pattern)
    {
        if(str==null||pattern==null) return false;
        
		return matchCore(str,0,pattern,0);
    }
	
	public boolean matchCore(char[] str,int s, char[] pattern,int p) {
		if(str.length<=s&&pattern.length<=p)
			return true;//都匹配完了
		if(str.length>s&&pattern.length<=p)
			return false;//模式完了,字符串还有
		//模式串a*a没结束,匹配串可结束可不结束
		
		if(p+1<pattern.length&&pattern[p+1]=='*'){//当前pattern的下一个是*号时
			
			//字符串完了
			if(s>=str.length) return matchCore(str, s, pattern, p+2);
			else{
			
			if(pattern[p]==str[s]||pattern[p]=='.'){
				//当前位置匹配完成,移动到下一个模式串
				return matchCore(str,s+1, pattern,p+2)
						||matchCore(str,s+1, pattern,p)
						||matchCore(str,s, pattern,p+2);
			}else
				return matchCore(str, s, pattern, p+2);
			}
		}
		//当前pattern的下一个不是*时候
		if(s>=str.length) return false;
		else{
		if(str[s]==pattern[p]||pattern[p]=='.')
			return matchCore(str, s+1, pattern, p+1);
		}
		return false;
    }
}

53 表示数值的字符串

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。

public class Solution {
    public boolean isNumeric(char[] str) {
        if (str == null || str.length == 0)
            return false;
        int[] index = new int[1];
        index[0] = 0; // 用于记录当前字符位置
        // 先判断A
        boolean isNumeric; //用于记录是否满足条件
        isNumeric = isInteger(str, index);
        // 判断B
        if (index[0] < str.length && (str[index[0]] == '.')) {
            index[0]++;
            isNumeric = isUnsignedInteger(str, index) || isNumeric; // .B和A.和A.B形式均可以
        }
        // 判断e后面的A
        if (index[0] < str.length && (str[index[0]] == 'e' || str[index[0]] == 'E')) {
            index[0]++;
            isNumeric = isInteger(str, index) && isNumeric;
        }
        if (isNumeric && index[0] == str.length)
            return true;
        else
            return false;
    }
 
    private boolean isInteger(char[] str, int[] index) { // 用int[]才能传值,int的话需要定义index为全局变量
        if (index[0] < str.length && (str[index[0]] == '+' || str[index[0]] == '-'))
            index[0]++;
        return isUnsignedInteger(str, index);
    }
 
    private boolean isUnsignedInteger(char[] str, int[] index) {
        int start = index[0];
        while (index[0] < str.length && (str[index[0]] - '0' <= 9 && str[index[0]] - '0' >= 0))
            index[0]++;
        if (index[0] > start)
            return true;
        else
            return false;
    }
}

54 字符流中第一个不重复的字符

请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。

public class Solution {
    //Insert one char from stringstream
    int[] charArray = new int[256];
    int index = 1;
    public void Insert(char ch)
    {
        if(charArray[(int)ch] == 0){
            charArray[(int)ch] = index;
            index++;
        } else {
            charArray[(int)ch] = -1;
        }
    }
  //return the first appearence once char in current stringstream
    public char FirstAppearingOnce()
    {
        char result = '#';
        int min = Integer.MAX_VALUE;
        for(int i = 0; i < 256; i++) {
            if(charArray[i] > 0) {
                if(min > charArray[i]) {
                    min = charArray[i];
                    result = (char)i;
                }
            }
        }
        return result;
    }
}

55 链表中环的入口结点

给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。

https://blog.youkuaiyun.com/sniperken/article/details/53870463

/*
 public class ListNode {
    int val;
    ListNode next = null;
    ListNode(int val) {
        this.val = val;
    }
}
*/
public class Solution 
{
 
    public ListNode EntryNodeOfLoop(ListNode pHead)
    {
        if(pHead == null || pHead.next == null)
    	   return null;
       ListNode fast = pHead;//快指针每次走两步
       ListNode slow = pHead;//每次走一步
       while(fast!=null && fast.next !=null)//因为fast每次要走两步,所有需要判断fast的下一个是否为空
       {
    	   slow = slow.next;
    	   fast = fast.next.next;
    	   //判断是否相遇 相遇后让快指针从头开始走,每次都是走一步,第二次相遇的节点就是环的入口
    	   if(fast.val == slow.val)
    	   {
    		  fast = pHead;
    		  while(fast.val != slow.val)
    		  {
    			  fast = fast.next;
    			  slow = slow.next;
    		  }
    	   }
    	   if(fast.val == slow.val)
    	   {
    		   return slow;
    	   }
       }
       return null;//要是没有相遇,此链表没有环返回空
    }
}

56 删除链表中重复的结点

在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5

/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public ListNode deleteDuplication(ListNode pHead)
    {
        if(pHead == null) 
            return null;
        ListNode node = new ListNode(Integer.MIN_VALUE);
        node.next = pHead;
        ListNode pre = node, p = pHead;
        boolean deleteMode = false;
        while(p != null) {
            if(p.next != null && p.next.val == p.val) {
                p.next = p.next.next;
                deleteMode = true;
            }else if(deleteMode) {
                pre.next = p.next;
                p = pre.next;
                deleteMode = false;
            } else {
                pre = p;
                p = p.next;
            }
        }
        return node.next;
    }
}

57 二叉树的下一个结点

给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。

public class Solution {
    public TreeLinkNode GetNext(TreeLinkNode pNode)
    {
        if(pNode == null)
            return null;
        if(pNode.right != null){
            TreeLinkNode temp = pNode.right;
            while(temp.left != null)
                temp = temp.left;
            return temp;
        }
        TreeLinkNode temp = pNode.next;
        TreeLinkNode pre = pNode;
        while(temp != null) {
            if(temp.left == pre)
                return temp;
            pre = temp;
            temp = temp.next;
        }
        return temp;
    }
}

58 对称的二叉树

请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。

public class Solution {
    boolean isSymmetrical(TreeNode pRoot)
    {
        return judge(pRoot,pRoot);
    }
    boolean judge(TreeNode pRoot1,TreeNode pRoot2)
    {
        if(pRoot1 == null && pRoot2 == null)
            return true;
//左右子树都是null,是true
        if(pRoot1 == null || pRoot2 == null)
            return false;
//左右子树一个为null一个不为空,那么肯定不是对称的
        if(pRoot1.left != null && pRoot2.right != null && pRoot1.left.val != pRoot2.right.val)
            return false;
//左右子树不为null,但val值不相等,不对称
        if(pRoot1.right != null && pRoot2.left != null && pRoot1.right.val != pRoot2.left.val)
            return false;
//右 左子树不为null,但val值不相等,不对称
        return judge(pRoot1.left,pRoot2.right) && judge(pRoot1.right,pRoot2.left);
//最后进行递归判断
    }
}

59 按之字形顺序打印二叉树

请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。

import java.util.*;

/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
        if (pRoot == null) {
            return ret;
        }
        ArrayList<Integer> list = new ArrayList<>();
        LinkedList<TreeNode> queue = new LinkedList<>();
        queue.addLast(null);
    //层分隔符
        queue.addLast(pRoot);
        boolean leftToRight = true;

        while (queue.size() != 1) {
            TreeNode node = queue.removeFirst();
            if (node == null) {
    //到达层分隔符
                Iterator<TreeNode> iter = null;
                if (leftToRight) {
                    iter = queue.iterator();
    //从前往后遍历
                } else {
                    iter = queue.descendingIterator();
    //从后往前遍历
                }
                leftToRight = !leftToRight;
                while (iter.hasNext()) {
                    TreeNode temp = (TreeNode)iter.next();
                    list.add(temp.val);
                }
                ret.add(new ArrayList<Integer>(list));
                list.clear();
                queue.addLast(null);
    //添加层分隔符
                continue;
    //一定要continue
            }
            if (node.left != null) {
                queue.addLast(node.left);
            }
            if (node.right != null) {
                queue.addLast(node.right);
            }
        }
        return ret;
    }
}

60 把二叉树打印成多行

从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。

public class Solution {
    ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) 
    {
        ArrayList<ArrayList<Integer>> list1 = new  ArrayList<ArrayList<Integer>>();
	        LinkedList<TreeNode> queue = new LinkedList<TreeNode>();
	       if(pRoot == null)
	       {
	           return list1;
	       }
	        TreeNode current = pRoot;
	        queue.offer(current);
	        int count;//记录当前层已经打印的个数
	        int last;//记录当前层一个有多少个
	        while(!queue.isEmpty())
	        {
	        	count = 0;
	        	last = queue.size();
                ArrayList<Integer> list2 = new ArrayList<Integer>();
	        	//打印一层
	        	while(count < last)
	        	{
	        		current = queue.pop();//出队一个节点并将气质加入到list2中
	        		list2.add(current.val);
	        		count++;//每出队一个几点就增加一个当前层已经打印的节点个数
	        		if(current.left != null)
	        			queue.offer(current.left);
	        		if(current.right != null)
	        			queue.offer(current.right);
	        	}
	        	list1.add(list2);
                
	        }
	       return list1; 
	    }
    }

61 序列化二叉树

请实现两个函数,分别用来序列化和反序列化二叉树

/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    //使用前序遍历序列化二叉树
    String Serialize(TreeNode root) {
        StringBuffer sb=new StringBuffer();
        if(root==null){
            //空节点(#)
            sb.append("#,");
            return sb.toString();
        }
        sb.append(root.val+",");
        sb.append(Serialize(root.left));
        sb.append(Serialize(root.right));
        return sb.toString();
  }
    //反序列化:根据某种遍历方式得到的序列化字符串结果,重构二叉树
    int index=-1;
    TreeNode Deserialize(String str) {
        index++;
        int len=str.length();
        if(index>=len) return null;
        //以逗号分隔,返回一个字符串数组
        String[] str1=str.split(",");
        TreeNode node=null;
        //遍历str1数组,重构二叉树:当前遍历元素非 # 则作为一个结点插入树中,作为上一个结点的左儿子;
        //当前遍历元素为 # 则表示此子树已结束,遍历下一个元素作为上一个结点的右孩子
        //即遍历到数字作为上一个结点的左孩子,遇到#变向作为上一个结点的右孩子
        if(!str1[index].equals("#")){
            node=new TreeNode(Integer.valueOf(str1[index]));
            node.left=Deserialize(str);
            node.right=Deserialize(str);
        }
        return node;
  }
}

62 二叉搜索树的第k个结点

给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8)    中,按结点数值大小顺序第三小结点的值为4。

public  class  Solution{
     int index = 0;
     TreeNode KthNode(TreeNode ppRootOfTree, int k){ 
         if (ppRootOfTree != null){
            TreeNode treeNode = KthNode(ppRootOfTree.left,k);
            if (treeNode != null )  return treeNode;
            index ++;
            if (index == k) return ppRootOfTree;
            treeNode  = KthNode(ppRootOfTree.right,k);
            if (treeNode != null)   return treeNode;
         }
         return null;
     }
}

63 数据流的中位数

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。

import java.util.Comparator;
import java.util.PriorityQueue;
public class Solution {
 
    private int count = 0;
    private PriorityQueue<Integer> minHeap = new PriorityQueue<>();
    private PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(15,new Comparator<Integer>(){
        @Override
        public int compare(Integer o1,Integer o2){
            return o2-o1;
        }
    });
    public void Insert(Integer num) {
        if(count%2==0){
            maxHeap.offer(num);
            int filteredMaxNum = maxHeap.poll();
            minHeap.offer(filteredMaxNum);
        }else{
            minHeap.offer(num);
            int filteredMinNum = minHeap.poll();
            maxHeap.offer(filteredMinNum);
        }
        count++;
    }
 
    public Double GetMedian() {
        if(count%2==0){
            return new Double((minHeap.peek()+maxHeap.peek()))/2;
        }else{
            return new Double(minHeap.peek());
        }
    }
}

64 滑动窗口的最大值

给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

https://blog.youkuaiyun.com/gg543012991/article/details/52854245

import java.util.*;
public class Solution {
	public ArrayList<Integer> maxInWindows(int[] num,int size){
		ArrayList<Integer> ret = new ArrayList<>();
		if(num == null){
			return ret;
		}
		if(num.length < size || size < 1){
			return ret;
		}
		
		LinkedList<Integer> indexDeque = new LinkedList<>();		//用于保存滑动窗口中的数字
		
		/*
		滑动窗口内部,用于判断窗口中的最大值
		*/
		for(int i = 0; i < size - 1; i++){
			while(!indexDeque.isEmpty()&& num[i] > num[indexDeque.getLast()]){			//getLast为插入端
				indexDeque.removeLast();			//将前面比K小的直接移除队列,因为不可能成为滑动窗口的最大值
			}
			indexDeque.addLast(i);					//将数字存入滑动窗口中
		}
		 
		/*
		滑动整个窗口
		*/
		for(int i = size - 1; i < num.length; i++){
			while(!indexDeque.isEmpty() && num[i] > num[indexDeque.getLast()]){			//getLast为插入端,队尾
				indexDeque.removeLast();				//将前面比K小的直接移除队列,因为不可能成为滑动窗口的最大值
			}
			indexDeque.addLast(i);
			//System.out.println("indexDeque = " + indexDeque.getFirst() + ",i = " + i);				//getFirst为允许删除端,队头
			if(i - indexDeque.getFirst() + 1 > size){
				indexDeque.removeFirst();
			}
			
			ret.add(num[indexDeque.getFirst()]);		//每次添加的是num[indexDeque.getFirst()],而不是indexDeque.getFirst().
		}
		return ret;
	}
}

65 矩形中的路径

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。 例如 a b c e s f c s a d e e 这样的3 X 4 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。

public class Solution {
    public boolean hasPath(char[] matrix, int rows, int cols, char[] str)
    {
        if (matrix == null || str == null || matrix.length < 1|| matrix.length<str.length) return false;
        boolean[] visited = new boolean[rows*cols];
        int curLength = 0;
        for (int i = 0;i < rows;++i) {
            for (int j = 0; j < cols; ++j) {
                if (coreHasPath(matrix,rows,cols,i,j,str,visited,curLength)) return true;
            }
        }
        return false;
    }
    private boolean coreHasPath(char[] matrix, int rows, int cols, int row, int col, char[] str, boolean[] visited, int curLength) {
        if (curLength == str.length) return true;
        boolean hasPath = false;
        if (row>=0 && row < rows && col>=0 && col<cols && !visited[row*cols+col] && matrix[row*cols+col] == str[curLength]) {
            curLength++;
            visited[row*cols+col] = true;
            hasPath = coreHasPath(matrix,rows,cols,row-1,col,str,visited,curLength) ||
                    coreHasPath(matrix,rows,cols,row+1,col,str,visited,curLength) ||
                    coreHasPath(matrix,rows,cols,row,col-1,str,visited,curLength) ||
                    coreHasPath(matrix,rows,cols,row,col+1,str,visited,curLength);
            if (!hasPath) {
                visited[row*cols+col] = false; //return hasPath回到上一层调用,curLength的值会自动回到上一层调用时的值
            }
        }
        return hasPath;
    }
}

66 机器人的运动范围

地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?

public class Solution {
    public int movingCount(int threshold, int rows, int cols) {
        if (rows <= 0 || cols <= 0 || threshold < 0)
            return 0;
 
        boolean[] isVisited = new boolean[rows * cols];
        int count = movingCountCore(threshold, rows, cols, 0, 0, isVisited);// 用两种方法试一下
        return count;
    }
 
    private int movingCountCore(int threshold, int rows, int cols, int row, int col, boolean[] isVisited) {
        if (row < 0 || col < 0 || row >= rows || col >= cols || isVisited[row * cols + col]
                || cal(row) + cal(col) > threshold)
            return 0;
        isVisited[row * cols + col] = true;
        return 1 + movingCountCore(threshold, rows, cols, row - 1, col, isVisited)
                + movingCountCore(threshold, rows, cols, row + 1, col, isVisited)
                + movingCountCore(threshold, rows, cols, row, col - 1, isVisited)
                + movingCountCore(threshold, rows, cols, row, col + 1, isVisited);
    }
 
    private int cal(int num) {
        int sum = 0;
        while (num > 0) {
            sum += num % 10;
            num /= 10;
        }
        return sum;
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值