剑指Offer(JAVA版)持续...

本文精选了多道经典算法题目,涵盖二维数组查找、字符串替换、链表操作、二叉树构建与遍历、栈与队列实现、数组旋转、斐波那契数列计算、位运算、数值计算、树结构子树判断、链表反转、数组排序、二叉树镜像变换、矩阵打印、栈数据结构优化、复杂链表复制、二叉搜索树转换、字符串排列、数组数字统计、最小数选取、子数组和、1出现次数、数组排序与最小数生成、丑数计算、唯一字符定位等,提供了详尽的解决方案和代码示例。

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

03二维数组中的查找【数组】

在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

方法一:遍历
方法二:从左下或右上开始遍历

public class Solution {
    public boolean Find(int target, int [][] array) {
        int row = array.length-1;
        int col = 0;
        while(row>=0 && col<array[0].length){
            if(array[row][col]>target)row--;
            else if(array[row][col]<target)col++;
            else return true;
        }
        return false;
    }
}

04替换空格【字符串】

请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

方法一:使用String的repalce方法

public class Solution {
    public String replaceSpace(StringBuffer str) {
        return str.toString().replaceAll(" ", "%20");
    }
}

方法二:作为字符数组,新创建StringBuffer进行添加

public class Solution {
    public String replaceSpace(StringBuffer str) {
        String s = str.toString();
        if(str==null)
            return s;
         char []ss=s.toCharArray();
        StringBuffer sb = new StringBuffer();
        for(int i=0;i<ss.length;i++)
            {
            if(ss[i]==' ')
                {
                 sb.append("%20");
            }
           else
               sb.append(ss[i]);
        }
        return sb.toString();
    }
}

方法三:计算字符串新长度(先遍历一次得到:原length+空格个数*2),从后往前遍历依次将值插入0索引位置

public class Solution {
    public String replaceSpace(StringBuffer str) {
        int length = str.length();
        if(length==0)return str.toString();
        int numspace =0;
        for(int i =0;i<length;i++){
            if(str.charAt(i)==' ')
                numspace++;
        }
        if(numspace==0) return str.toString();
        int newlength = length + numspace*2;
        str.setLength(newlength);
        for(int i =length-1;i>=0;i--){
            if(str.charAt(i)==' '){
                str.setCharAt(--newlength,'0');
                str.setCharAt(--newlength,'2');
                str.setCharAt(--newlength,'%');
            }
            else str.setCharAt(--newlength,str.charAt(i));
        }
        return str.toString();
    }
}

05从尾到头打印链表【链表】

输入一个链表,按链表从尾到头的顺序返回一个ArrayList。

方法一:反转链表,后依次添加至ArrayList

/**
*    public class ListNode {
*        int val;
*        ListNode next = null;
*
*        ListNode(int val) {
*            this.val = val;
*        }
*    }
*
*/
import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        ArrayList<Integer> al = new ArrayList<>();
        ListNode pre = null;
        ListNode next = null;
        while(listNode != null){
            next = listNode.next;
            listNode.next = pre;
            pre = listNode;
            listNode =next;
        }
        while(pre!=null){
            al.add(pre.val);
            pre = pre.next;
        }
        return al;     
    }
}

方法二:遍历链表,依次添加add(0,val)至ArrayLis的0索引位置

/**
*    public class ListNode {
*        int val;
*        ListNode next = null;
*
*        ListNode(int val) {
*            this.val = val;
*        }
*    }
*
*/
import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        ArrayList<Integer> list = new ArrayList<>();
        while(listNode!=null){
            list.add(0,listNode.val);
            listNode = listNode.next;
        }
        return list;
    }
}

06重建二叉树【树】

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

递归思想:先序确定根节点,根据根节点在中序遍历中划分左右子树,左右子树将新的先序序列和中序序列传入遍历

/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        TreeNode root=reConstructBinaryTree(pre,0,pre.length-1,in,0,in.length-1);
        return root;
    }
    //前序遍历{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6}
    private TreeNode reConstructBinaryTree(int [] pre,int startPre,int endPre,int [] in,int startIn,int endIn) {
          
        if(startPre>endPre||startIn>endIn)
            return null;
        //根节点
        TreeNode root=new TreeNode(pre[startPre]);
          
        for(int i=startIn;i<=endIn;i++)
            //分子树遍历
            if(in[i]==pre[startPre]){
                root.left=reConstructBinaryTree(pre,startPre+1,startPre+i-startIn,in,startIn,i-1);
                root.right=reConstructBinaryTree(pre,i-startIn+startPre+1,endPre,in,i+1,endIn);
                      break;
            }
                  
        return root;
    }
}

07用两个栈实现队列【栈、队列】

用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。

准备两个栈,一个用来push,一个用来pop,当push桶中有数据,pop桶中无数据时,将push中数据完全倒入pop桶中

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);
    }
    
    public int pop() {
        if(stack1.empty() && stack2.empty()){
            throw new RuntimeException("Queue is empty!");
        }
        dao();
        return stack2.pop();
    }
    public void dao(){
        if(!stack2.isEmpty()){
            return ;
        }
        while(!stack1.isEmpty()){
            stack2.push(stack1.pop());
        }
    }
}

扩展:用两个队列实现栈
两个队列一个data队列,一个help队列,进栈元素加入data队列,出栈时,将data队列依次出列元素加入help队列,最后一个元素即为栈弹出元素,
后交换data和help队列的引用

import java.util.LinkedList;
import java.util.Queue;

public class TwoQueueStack {
	private Queue<Integer> data;
	private Queue<Integer> help;
	
	public TwoQueueStack() {
        // 用双向链表实现,也可以使用动态数组
        data = new LinkedList<Integer>();
        help = new LinkedList<Integer>();
    }

	//栈的push入栈操作
	public void push(int pushInt) {
		data.add(pushInt);
	}
	//栈的pop出栈操作,栈顶元素弹出
	public int pop() {
		if(data.isEmpty()) {
			throw new ArrayIndexOutOfBoundsException("The stack is empty!");
		}
		while(data.size()!=1) {
			help.add(data.poll());
		}
		int res = data.poll();
		swap();
		return res;
	}
	//栈的peek()操作,获得栈顶元素
	public int peek() {
		if(data.isEmpty()) {
			throw new ArrayIndexOutOfBoundsException("The stack is empty!");
		}
		while(data.size()!=1) {
			help.add(data.poll());
		}
		int res = data.poll();
		//最后也要添加进去
		help.add(res);
		swap();
		return res;
	}
	//交换引用
	public void swap() {
		Queue<Integer> temp = help;
		help = data;
		data = help;
	}

08旋转数组【数组】

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。

方法一:遍历
方法二:二分查找

import java.util.ArrayList;
public class Solution {
    public int minNumberInRotateArray(int [] array) {
        int left = 0;
        int right = array.length-1;
        
        //二分查找
        while(left<right){
            if(array[left]<array[right])
                return array[left];
            int mid = (left+right)>>1;
            if(array[mid]>array[left]){
               left = mid+1;
            }else if(array[mid]<array[right]){
                right = mid;//不能忽略mid
            }else left++;//均相等时,缩小范围
        }
        return array[left];
    }
}

09斐波那契数列【数列】

大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。n<=39

方法一:递归

public class Solution {
    public int Fibonacci(int n) {
        if(n<=1){
            return n;
        }
        return Fibonacci(n-1)+Fibonacci(n-2);
 
    }
}

方法二:整个数列保存为数组

public class Solution {
    public int Fibonacci(int n) {
        int[] res = new int[40];
        res[0]=0;
        res[1]=1;
        for(int i = 2;i<=n;i++){
            res[i] = res[i-1]+res[i-2];
        }
        return res[n];
 
    }
}

方法三:用3个变量保存(方法二优化)

public class Solution {
    public int Fibonacci(int n) {
        int pre1=1;
        int pre2=0;
         if(n < 2){
            return n;
        }
        int res = 0;
        for(int i =2;i<=n;i++){
            res = pre1+pre2;
            pre2 = pre1;
            pre1 = res;
        }
        
        return res;
 
    }
}

10二进制中1的个数【位运算】

输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

方法一:n&(n-1)消除最低位的1

public class Solution {
    public int NumberOf1(int n) {
        int count=0;
    while(n!=0){
        count++;
        n=n&(n-1);
    }
    return count;
    }
}

方法二:依次与1,10,100…与运算,左移至符号位=0

public class Solution {
    public int NumberOf1(int n) {
        int num = 0, flag = 1;
        while (flag != 0) {
            if ((n & flag) != 0) {
                num++;
            }
            flag <<= 1;
        }
        return num;
    }
}

负数不适用,正数可以右移

public class Solution {
    public int NumberOf1(int n) {
        int count=0;
    while(n>0){
    	if((n&1)==1) 
        	count++;
        n>>=1;
    }
    return count;
    }
}

11数值的整数次方【数值】

给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。

保证base和exponent不同时为0

方法一:Math类方法

public class Solution {
    public double Power(double base, int exponent) {
        return Math.pow(base,exponent);
  }
}

方法二:暴力循环相乘

public class Solution {
    public double Power(double base, int exponent) {
        float res = 1; 
        if (base == 0) {
            res = 0;
        }
        if (exponent == 0) {
            res = 1;
        }
        int exp = Math.abs(exponent);
        for (int i = 1; i <= exp; i++) {
            res *= base;
        }
        return exponent>0 ? res :1/res;
    }
}

方法三:二分法递归
正数中偶数 res * res 2^4 = 2^2 * 2^2
正数中奇数 res * res * base 2^5 = 2^2 * 2^2 * 2

public class Solution {
    public double Power(double base, int exponent) {
        int exp = Math.abs(exponent);
        double result = abs(base,exp);
        return exponent>0 ? result:1/result; 
    }
    public double abs (double base,int exp){
        if(exp == 0){
            return 1;
        }
        if(exp ==1){
            return base;
        }
        double res = abs(base,exp>>1);
        res *= res;
        return exp%2==0 ? res : res*base;
    }
}

方法四:位运算累乘 2^5 = 2^(101) =2^1 * 2 * 2^2
幂的二进制为1 ans = ans * base
二进制为0 base=base*base

public class Solution {
    public double Power(double base, int exponent) {
        int exp = Math.abs(exponent);
        double ans = 1;
        while (exp > 0) {
            if ((exp & 1) == 1) {
                ans = ans * base;
            }
            exp >>= 1;
            base *= base;
        }
        return exponent>0 ? ans :1/ans;
    }
}

14调整数组顺序使奇数位于偶数前面【数组】

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变
方法一:两个新数组 一个存奇数一个存偶数,两个合并

public class Solution {
    public void reOrderArray(int [] array) {
        //保持稳定性的排序方式:冒泡、插入、归并
        //两个新数组
        int[] arrayOdd = new int[array.length];
        int[] arrayEven = new int[array.length];
        int counter = 0, counterOdd = 0, counterEven = 0;
        while(counter<array.length){
            if(array[counter]%2==0){//even
                arrayEven[counterEven++] = array[counter++];
        }else{//odd
            arrayOdd[counterOdd++] = array[counter++];
        }
    }
        for(int i=0;i<counterOdd;i++){
            array[i] = arrayOdd[i];
        }
        for(int i=0;i<counterEven;i++){
            array[i+counterOdd] = arrayEven[i];
        }
    }
}

方法二:插入思想 找到奇数往前插入直到前一个也为奇数

public class Solution {
    public void reOrderArray(int [] array) {
        //保持稳定性的排序方式:冒泡、插入、归并
        //插入
        for(int i = 1;i<array.length;i++){
	         if(array[i]%2==1){
	            	int j =i;
	                while(array[j-1]%2==0 && j>0) {
	                	int temp = array[j];
	                    array[j]=array[j-1];
	                    array[j-1]=temp;
	                    if(j>1)j--;
	                    
	                }
             }
        }
    }
}

方法三:冒泡思想
找到第一个偶数i,i==j往后遍历,如果是偶数,继续往后直到奇数;如果是奇数,奇数往前插入,偶数整体往后移动

public class Solution {
    public void reOrderArray(int [] array) {
        //保持稳定性的排序方式:冒泡、插入、归并
        int length = array.length;
        if (length <= 1) {
          return;
      }
        for(int i =0;i<length;i++){
            if(array[i]%2==0){
                int j =i+1;
                while(array[j]%2==0){
                    if(j==length-1)
                        return;
                    j++;
                }
                int temp = array[j];
                while(j>i){
                    array[j]=array[j-1];
                    j--;
                }
                array[i]=temp;
            }
        }
    }
}

15链表中倒数第k个结点【链表】

输入一个链表,输出该链表中倒数第k个结点。
快慢指针,快指针先走k步;快慢再一起走;直到快指针走到尾,慢指针所指为倒数第k个

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

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode FindKthToTail(ListNode head,int k) {
        //快慢指针
        if(head==null || k==0)
            return null;
        ListNode fast =head;
        ListNode slow =head;
        int i =0;
        int j =0;
        while(i<k){
            if(fast==null){
                return null;
            }
            fast = fast.next;
            i++;
        }
        while(fast!=null){
            fast = fast.next;
            slow = slow.next;
        }
        return slow;
    }
}

16反转链表【链表】

输入一个链表,反转链表后,输出新链表的表头。
两个新的节点作为temp指针,交换指向,头变尾,尾变头

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

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode ReverseList(ListNode head) {
        ListNode pre = null;
        ListNode next = null ;
        while(head!=null){
            next = head.next;
            head.next = pre;
            pre = head;
            head = next;
        }
        return pre;
    }
}

17合并两个排序的链表【链表】

输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
方法一:递归 两个链表判断大小相互串起来

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

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        if(list1==null)return list2;
        if(list2==null)return list1;
          if(list1.val<list2.val){
              list1.next = Merge(list1.next,list2);
              return list1;
          }else{
              list2.next=Merge(list1,list2.next);
              return list2;
          }
    }
}

方法二:非递归 建立一个新指针,往指针按顺序串两个链表的最小值

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

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        if(list1==null)return list2;
        if(list2==null)return list1;
        ListNode merge = null;
        ListNode head = null;
        while(list1!=null && list2!=null){
            if(list1.val <= list2.val){
                if(head == null){
                    head = merge = list1;
                }else{
                    merge.next = list1;
                    merge = merge.next;
                }
                list1 = list1.next;
                
            }else{
                if(head == null){
                    head = merge = list2;
                }else{
                    merge.next = list2;
                    merge = merge.next;
                }
                list2 = list2.next;
            }
        }
        if(list1 == null){
                merge.next = list2;
        }
        if(list2 == null){
                merge.next = list1;
        }
        return head;
    }
}

18树的子结构【树】

输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
递归寻找:先找到tree1与tree2相同的节点(左节点相同或右节点相同),再递归判断相同节点的左节点右节点是否都相同

/**
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;
        }
        return ifContain(root1,root2)||ifContain(root1.left,root2)||ifContain(root1.right,root2);
    }
    private boolean ifContain(TreeNode root1,TreeNode root2){
        if(root2 == null){
            return true;
        }
        if(root1 == null){
            return false;
        }
        
        if(root1.val != root2.val){
            return ifContain(root1.left,root2)||ifContain(root1.right,root2);
        }
        return ifContain(root1.left,root2.left) && ifContain(root1.right,root2.right);
    }
}

19二叉树的镜像

操作给定的二叉树,将其变换为源二叉树的镜像。
方法一:递归思路 根节点左右子树交换,左子树右子树再各自调用交换自己的左右子树

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

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

    }

}
*/
public class Solution {
    public void Mirror(TreeNode root) {
        if(root == null)return;
        //交换左右子树
        TreeNode temp = root.left;
        root.left = root.right;
        root.right = temp;
        //左子树自己递归调用
        if(root.left!=null){
            Mirror(root.left);
        }
        //右子树自己递归
        if(root.right!=null){
            Mirror(root.right);
        }
        
    }
}

方法二:非递归,使用栈,将每一个根节点手动压栈,弹出时交换其左右子树

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

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

    }

}
*/
import java.util.Stack;
public class Solution {
    public void Mirror(TreeNode root) {
        if(root == null)return;
        if(root.left == null && root.right==null)return;
        //使用栈
        Stack<TreeNode> stack = new Stack<TreeNode>();
        stack.push(root);
        
        TreeNode cur;
        TreeNode temp;
        
        //深度优先遍历(先序遍历)
        while(!stack.isEmpty()){
            cur = stack.pop();
            if(cur == null)continue;
            if(cur.left == null && cur.right==null)continue;
            //交换
            temp = cur.left;
            cur.left = cur.right;
            cur.right = temp;
            stack.push(cur.left);
            stack.push(cur.right);
        }
    }
}
 

20顺时针打印矩阵

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.

分圈打印 左上角和右下角确定一个圈,每一圈四个方向打印,再将角点坐标沿对角线向中间移动一位继续递归打印一圈

import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> printMatrix(int [][] matrix) {
        int lR = 0;
        int lC = 0;
        int rR = matrix.length-1;
        int rC = matrix[0].length-1;
        ArrayList<Integer> arr = new ArrayList<Integer>();
        while(lR<=rR && lC<=rC ){
            printEdge(matrix,lR++,lC++,rR--,rC--,arr);
        }
        return arr;
    }
    private  static void printEdge(int[][] matrix ,int lR,int lC,int rR,int rC,ArrayList arr){
        if(rR==lR){
            for(int i=lC;i<=rC;i++)
                arr.add(matrix[lR][i]);
            }
        else if(lC==rC){
            for(int i=lR;i<=rR;i++)
                arr.add(matrix[i][rC]);
            }
        else{
            int row = lR;
            int col = lC;a
            //一圈打印
            while(col<rC){
                arr.add(matrix[lR][col++]);
            }
            while(row<rR){
                arr.add(matrix[row++][rC]);
            }
            while(col>lC){
                arr.add(matrix[rR][col--]);
            }
            while(row>lR){
                arr.add(matrix[row--][lC]);
            }
        }
       
    }
}

21包含min的栈【栈】

定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
注意:保证测试中不会当栈为空的时候,对栈调用pop()或者min()或者top()方法。
建立两个栈,一个作为主栈进行压栈和弹栈操作,另一个为min栈,每次压栈的数与min栈栈顶比较,小于压入,不小将min栈栈顶元素重复压入

import java.util.Stack;

public class Solution {
    Stack<Integer> stack = new Stack<Integer>();
    Stack<Integer> minstack = new Stack<Integer>();
    
    public void push(int node) {
        if(stack.isEmpty()){
            stack.push(node);
            minstack.push(node);
        }else if(node<minstack.peek()){
            minstack.push(node);
        }else{
            minstack.push(minstack.peek());
        }stack.push(node);
    }
    
    public void pop() {
        stack.pop();
        minstack.pop();        
    }
    public int top() {
        return stack.peek();
    }
    
    public int min() {
        return minstack.peek();
    }
}

22栈的压入、弹出序列【栈】

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
创建一个栈,按所给压入顺序压入,如果与所给序列的数值相同则弹栈,不相同继续压栈,压入到最后一位并完成所有符合的弹栈后,最终判断是否将所有元素弹出

import java.util.ArrayList;
import java.util.Stack;
public class Solution {
    public boolean IsPopOrder(int [] pushA,int [] popA) {
        Stack<Integer> stack = new Stack<Integer>();
        if(pushA.length != popA.length)return false;
        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();
    }
}

23从上往下打印二叉树【树】

从上往下打印出二叉树的每个节点,同层节点从左至右打印。
按层遍历思想:使用队列,将每一层节点入队,后循环按顺序从左到右节点出队后将其节点的下一层左右节点入队

import java.util.ArrayList;
/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;
    }
}
*/

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
public class Solution {
    public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
        ArrayList<Integer> res = new ArrayList();
        Queue<TreeNode> temp = new LinkedList();
        if(root == null) return res;
        temp.offer(root);
        while(!temp.isEmpty()){
            TreeNode point = temp.poll();
            res.add(point.val);
            if(point.left!=null) temp.offer(point.left);
            if(point.right!=null) temp.offer(point.right);
        }
        return res;
    }
}

24二叉搜索树的后序遍历序列【树】

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。

递归判断:根节点是子树后序遍历的最后一个,
不断地确定出左子树区间和右子树区间,并且判断:左子树区间的所有结点值 < 根结点值 < 右子树区间所有结点值
二叉搜索树:左节点比根节点小,右节点比根节点大

public class Solution {
    public boolean VerifySquenceOfBST(int [] sequence) {
        if(sequence == null || sequence.length == 0)return false;
        return isBST(sequence,0,sequence.length-1);
    }
    private boolean isBST(int[] seq,int start,int end){
        if(start >= end){
            return true;
        }
        //最后为子树根节点
        int val = seq[end];
        int split = start;
        //找到右子树根节点,比val大的第一个树
        while(split < end && seq[split]< val)
            split++;
        for(int i = split;i<end;i++){
			//	判断是否右子树都比root大    
            if(seq[i]<val)return false;
        }
        //如果只剩一个节点 start = split = end
        return isBST(seq,start,split-1) && isBST(seq,split,end-1);
    }
}

25二叉树中和为某一值的路径【树】

输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)
树的深度遍历:先序遍历

import java.util.ArrayList;
/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

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

    }

}
*/
import java.util.ArrayList;
public class Solution {
    public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
        ArrayList<ArrayList<Integer>> pathArr = new ArrayList<>(); 
        while(root == null) return pathArr;
        
        ArrayList<Integer> path = new ArrayList<Integer>();
        find(root,target,path,pathArr);
        return pathArr;
    }
    private void find (TreeNode root,int target,ArrayList<Integer> path,ArrayList<ArrayList<Integer>> pathArr){
        //节点为null
        if(root == null) return;
        path.add(root.val);
        target = target - root.val;
        //target不满足
        if(target<0) return ;
        //到叶子节点,找到正确的
        if(target == 0 && root.left==null && root.right==null ){
            pathArr.add(path);
            return;
        }
        find(root.left,target,new ArrayList<>(path),pathArr);
        find(root.right,target,new ArrayList<>(path),pathArr);
    }
}

26复杂链表的复制【链表】

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
方法一:插入复制节点
1 -> 2 -> 3
1 -> 1’ -> 2 -> 2’ -> 3 -> 3’ 可以通过next找到random节点

/*
public class RandomListNode {
    int label;
    RandomListNode next = null;
    RandomListNode random = null;

    RandomListNode(int label) {
        this.label = label;
    }
}
*/
public class Solution {
    public RandomListNode Clone(RandomListNode pHead){
        if(pHead==null)
            return null;
        //插入复制节点
        RandomListNode listNode = pHead;
        while(listNode!=null){
            RandomListNode copyList = new RandomListNode(listNode.label);
            RandomListNode nextNode = listNode.next;
            copyList.next = nextNode ;
            listNode.next= copyList;
            listNode = nextNode; 
        }
        //插入随机节点
        listNode = pHead;
        while(listNode!=null){
            listNode.next.random = listNode.random==null ? null : listNode.random.next;
            listNode = listNode.next.next;
        }
        //拆链
        listNode = pHead;
       RandomListNode Clone = pHead.next;
        while(listNode != null){
            RandomListNode copyList = listNode.next;
            listNode.next = copyList.next;
            copyList.next = copyList.next==null ? null :copyList.next.next;
            listNode = listNode.next;
        }
        return Clone;
        
    }
}

方法二:哈希表 通过哈希表(key,value)找到random的复制节点

/*
public class RandomListNode {
    int label;
    RandomListNode next = null;
    RandomListNode random = null;

    RandomListNode(int label) {
        this.label = label;
    }
}
*/
import java.util.HashMap;
public class Solution {
    public RandomListNode Clone(RandomListNode pHead){
        //建立哈希表
        HashMap<RandomListNode,RandomListNode> map = new HashMap<RandomListNode,RandomListNode>();
        RandomListNode listCode = pHead;
        //建立对应哈希表
        while(listCode!=null){
            map.put(listCode,new RandomListNode(listCode.label));
            listCode = listCode.next;
        }
        listCode = pHead;
        while(listCode !=null){
            map.get(listCode).next = map.get(listCode.next);
            map.get(listCode).random = map.get(listCode.random);
            listCode = listCode.next;
        }
        return map.get(pHead);
        
    }
}

27二叉搜索树与双向链表【链表、树】

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
方法一:递归 左子树为双向链表,左子树最右节点最后指向根节点,右子树为双向链表,根节点指向右子树最左节点

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

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

    }

}
*/
public class Solution {
    public TreeNode Convert(TreeNode root) {
        if(root==null)
            return null;
        if(root.left==null && root.right==null)
            return root;
        // 1.将左子树构造成双链表,并返回链表头节点,先找最左边界
        TreeNode left = Convert(root.left);
        TreeNode p = left;
        // 2.定位至左子树双链表最后一个节点,是否有右子树
        while(p!=null&&p.right!=null){
            p = p.right;
        }
        // 3.如果左子树链表不为空的话,将当前root追加到左子树链表
        if(left!=null){
            p.right = root;
            root.left = p;
        }
        // 4.将右子树构造成双链表,并返回链表头节点,右子树最左边界
        TreeNode right = Convert(root.right);
        // 5.如果右子树链表不为空的话,将该链表追加到root节点之后
        if(right!=null){
            right.left = root;
            root.right = right;
        }
        return left!=null?left:root; 
    }
}

方法二:非递归 使用栈进行中序遍历,不断改变指针指向

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

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

    }

}
*/
import java.util.Stack;
public class Solution {
    public TreeNode Convert(TreeNode root) {
        if(root==null)
            return null;
        Stack<TreeNode> stack = new Stack<TreeNode>();
        TreeNode p = root;
        TreeNode pre = null;// 用于保存中序遍历序列的上一节点
        boolean isFirst = true;
        while(p!=null||!stack.isEmpty()){
            //先压入左边界
            while(p!=null){
                stack.push(p);
                p = p.left;
            }
            //左子树最左节点
            p = stack.pop();
            if(isFirst){
                root = p;// 将中序遍历序列中的第一个节点记为root
                pre = root;
                isFirst = false;
            }else{
                pre.right = p;
                p.left = pre;
                pre = p;
            }      
            p = p.right;
        }
        return root;
    }
}

28字符串的排列【字符串 动态规划】

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
回溯法 排列组合问题一般是回溯法DFS

import java.util.List;
import java.util.Collections;
import java.util.ArrayList;
public class Solution {
    public ArrayList<String> Permutation(String str) {
        List<String> res = new ArrayList<>();
        if (str != null && str.length() > 0) {
            PermutationHelper(str.toCharArray(), 0, res);
            Collections.sort(res);
        }
        return (ArrayList)res;
    }
 
    public void PermutationHelper(char[] cs, int i, List<String> list) {
        //出口:到最后一位 保证不重复
        if (i == cs.length - 1) {
            String val = String.valueOf(cs);
            if (!list.contains(val))
                list.add(val);
        } else {
            //入口:从当前到最后一位
            for (int j = i; j < cs.length; j++) {
                swap(cs, i, j);
                PermutationHelper(cs, i+1, list);
                swap(cs, i, j);
            }
        }
    }
 
    public void swap(char[] cs, int i, int j) {
        char temp = cs[i];
        cs[i] = cs[j];
        cs[j] = temp;
    }
}

29数组中一个数字【数组】

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
方法一:排好序的最中间位置数字
方法二:HashMap存数字和个数

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        HashMap<Integer,Integer> map = new HashMap<>();
        for(int i =0;i < array.length;i++){
            if(!map.containsKey(array[i])){
                map.put(array[i],1);
            }else{
            int count = map.get(array[i]);
            map.put(array[i],count+1);
            }
        }
        //遍历map 方法1:得到所有键,键找值;方法二:得到所有键值对,分别拿键拿值
        // map.entrySet()返回的是键值对对象Set
        Set<Map.Entry<Integer,Integer>> set = map.entrySet();
        for(Map.Entry<Integer, Integer> me : set){
            Integer key = me.getKey();
            Integer val = me.getValue();
            if(val>array.length/2){
                return key;
            }
        }
        return 0;
    }
}

方法三:数组特点 这个数字的个数比其他所有数字之和要大
整体遍历一次:若相同,count++;不同count–;当count为0,遍历数字为下一个;最后留着的数字,去验证是否个数大于一半

public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        if(array.length==0) return 0;
        int count = 0;
        int result = array[0];
        for(int i =1;i<array.length;i++){
            if(count == 0){
                result = array[i];
                count = 1;
            }
            else if(array[i] == result){
                count++;
            }
            else {
                count--;
            }
        }
        //判断是否长度超过一半
        count = 0;
        for(int i=0;i<array.length;i++)
        {
            if(array[i] == result) count++;
        }
         
        return (count > array.length/2) ? result : 0;
        
    }
}

30最小的K个数【数组】

输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
方法一:排序

方法二:建立个数为k的大根堆

import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        ArrayList<Integer> array = new ArrayList<Integer>();
        if(input == null ||input.length==0 || k==0 || k>input.length){
            return array;
        } 
        //排一个k的数组
        for (int i = 1; i < k; i++) {
			// 不越界 j>=0 ; 前一个比后一个大,则继续
			for (int j = i - 1; j >= 0 && input[j] > input[j + 1]; j--) {
				int temp = input[j];
				input[j] = input[j + 1];
				input[j + 1] = temp;
			}
		}
        //进行插入
        for(int i =k;i<input.length;i++){
            if(input[i] < input[k-1]){
                input[k-1]=input[i];
                for (int j = k - 1; (j-1)>= 0 && input[j-1] > input[j]; j--) {
				int temp = input[j];
				input[j] = input[j-1];
				input[j - 1] = temp;
			}
            }
        }
        
        for(int i = 0;i<k;i++){
            array.add(input[i]);
        }
        return array;
        
    }
}

31连续子数组的最大和【数组】

方法一:重复利用

public class Solution {
    public int FindGreatestSumOfSubArray(int[] array) {
        //重复使用
        int max = Integer.MIN_VALUE;
        for(int i =0;i<array.length;i++){
            int sum = 0;
            for(int j = i;j<array.length;j++){
                sum += array[j];
                if(sum>max){
                     max=sum;
                }
            }
        }
        return max;
    }       
}
 

方法二:动态规划

public class Solution {
    public int FindGreatestSumOfSubArray(int[] array) {
        //动态规划
        //All[i-1] = max{arr[i-1],End[i-1],All[i-2]}
        int n =array.length;
        int End[] = new int[n];
        int All[] = new int[n];
        End[n-1] = array[n-1];
        End[0] = All[0]= array[0];
        for(int i =1;i<n;i++){
            End[i]= max(End[i-1]+array[i],array[i]);
            All[i] = max(End[i],All[i-1]);
        }
        return All[n-1];
    }
    public static int max(int n,int m){
            return m>n ? m : n;
        }
}

3.遍历一边

public class Solution {
    public int FindGreatestSumOfSubArray(int[] array) {
        //两个变量,遍历一次
        if(array == null || array.length==0){
            return 0;
        }
        int cur = 0;
        int max = Integer.MIN_VALUE;
        for(int i=0;i<array.length;i++){
            cur = cur + array[i];
            max = Math.max(max,cur);
            if(cur<0){
                cur=0;
            }
        }
        return max;
    }
}
 

32整数中1出现的次数(从1到n整数中1出现的次数)

1.暴力遍历

public class Solution {
    public int NumberOf1Between1AndN_Solution(int n) {
        int count=0;
        for(int i=n;i>0;i--){
            for(int j=i;j>0;j/=10){
                if(j%10==1) count++;
            }
        }
        return count;
    }
}

2.

33.把数组排成最小的数

输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
贪心策略:自定义比较器 比较str1.str2 和 str2.str1 的大小

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
public class Solution {
    public static class MyComparator implements Comparator<String> {
		@Override
		public int compare(String a, String b) {
				return (a+b).compareTo(b+a) ;
		}
	}
	
    public static String PrintMinNumber(int [] numbers) {
        String[] strs = new String[numbers.length];
        for(int i=0;i<numbers.length;i++){
            strs[i] = String.valueOf(numbers[i]);
        }
        if(strs==null || strs.length==0) {
			return "";
		}
		Arrays.sort(strs,new MyComparator());
		String res = "";
		for(int i =0;i<strs.length;i++) {
			res += strs[i];
		}
		return res;

    }
}

34.丑数

把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
**维护三个指针,分别指向*2,3,5数组中的第几个数

public class Solution {
    public int GetUglyNumber_Solution(int index) {
        if(index<=0) return 0;
        int[] num = new int[index];
        num[0] = 1;
        //维持三个指针
        int m=0,n=0,l=0;
        for(int i = 1;i<index;i++){
            num[i] = Math.min(num[m]*2,Math.min(num[n]*3,num[l]*5));
            if(num[i]==num[m]*2) m++;
            if(num[i]==num[n]*3) n++;
            if(num[i]==num[l]*5) l++;
        }
        return num[index-1];
    }
}

35.第一个只出现一次的字符

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

import java.util.HashMap;
public class Solution {
    public int FirstNotRepeatingChar(String str) {
        //遍历两遍
        if(str.length() == 0|| str == null){
            return -1;
        }
        HashMap<Character,Integer> map = new HashMap<>();
        for(int i = 0; i < str.length();i++){
            if(!map.keySet().contains(str.charAt(i))){
                map.put(str.charAt(i),1);
            }else{
                map.put(str.charAt(i),map.get(str.charAt(i))+1);
            }
        }
        for(int i = 0; i < str.length();i++){
            if(map.get(str.charAt(i)) == 1){
                return i;
            }
        }
        return -1;
    }
}

方法二:优化方法一,最大z的ASCII码为122

import java.util.HashMap;
public class Solution {
    public int FirstNotRepeatingChar(String str) {
        //遍历两遍
        if(str==null || str.length() == 0)return -1;
        int[] count = new int[123];
        //用一个类似hash的东西来存储字符出现的次数
        for(int i=0; i < str.length();i++)
            count[str.charAt(i)]++;
        for(int i=0; i < str.length();i++)
            if(count[str.charAt(i)]==1)
                return i;
        return -1;
    }
}

方法三:只出现一次的数从前从后索引值相同

import java.util.HashMap;
public class Solution {
    public int FirstNotRepeatingChar(String str) {
        for (int i = 0; i < str.length(); i++) {
            char t = str.charAt(i);
            if (str.indexOf(t) == i && str.lastIndexOf(t) == i){
                return i;
            }
        }
        return -1;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值