zoukankan      html  css  js  c++  java
  • 剑指Offer-1



    做了又忘,忘了又做,怎么刷都是学不会啊啊啊


    1

    从每行每列都是递增的二维数组中找是否存在某数

    public class Solution {
        public boolean Find(int target, int[][] array) {
            
            int rows = array.length;
            int cols = array[0].length;
            
            int i = rows - 1;
            int j = 0;
            
            // 从左下角,或右上角扫描
            while(i >= 0 && j < cols){
                if(target < array[i][j]){
                    i--;
                }else if(target > array[i][j]){
                    j++;
                }else{    
                    return true;
                }
            }
            return false;
        }
    }
    


    2

    字符串替换

    // 内部函数
    public class Solution {
        public String replaceSpace(StringBuffer str) {
            return str.toString().replaceAll(" ", "%20");
        }
    }
    
    // 效率也没差
    public class Solution {
        public String replaceSpace(StringBuffer str) {
            
            StringBuffer sb = new StringBuffer(str.length());	// 避免多次扩容  System.arraycopy
            
            for(int i = 0; i < str.length(); i++){
                if(str.charAt(i) == ' '){
                    sb.append("%20");
                }else{
                    sb.append(str.charAt(i));
                }
            }
            return sb.toString();
        }
    }
    
    // 考察优化
    // 从前向后计算空格数
    // 从后向前替换,这样移动的次数相对少了
    public class Solution {
        public String replaceSpace(StringBuffer str) {
            
            int spaceNum = 0;  // 算出空格总数
            for(int i = 0; i < str.length(); i++){
                if(str.charAt(i) == ' '){
                    spaceNum++;
                }
            }
            
            if(spaceNum == 0) return str.toString();  // 没有空格直接返回
            
            int oldLength = str.length();
            int newLength = str.length() + spaceNum * 2;  // 设置新长度
            str.setLength(newLength);
            
            newLength--;  // Java中没有 ""结尾字符,所以实际大小减一
            for(int i = oldLength-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();
        }
    }
    


    3

    从尾到头遍历链表

    // 递归
    public class Solution {
        ArrayList list = new ArrayList();
        public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
            if(listNode != null){
                printListFromTailToHead(listNode.next);
                list.add(listNode.val);
            }
            return list;
        }
    }
    
    // 非递归
    // 模拟栈,ArrayList(index,val)也可以模拟栈,remove(index),LinkedList??
    // ArrayList的头插法
    public class Solution {
        public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
            ArrayList list = new ArrayList();
            while(listNode != null){
                list.add(0,listNode.val);
                listNode = listNode.next;
            }
            return list;
        }
    }
    


    4

    重建二叉树(前序,中序)

    // 一般树都是递归操作,用索引
    public class Solution {
        public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
            return reConBTree(pre,0,pre.length-1,in,0,in.length-1);	// 传入实际长度
        }
        
        public TreeNode reConBTree(int[] pre,int pleft,int pright,int[] in,int inleft,int inright){
            
            if(pleft > pright || inleft > inright) return null;  // 递归出口
            
            TreeNode root = new TreeNode(pre[pleft]);  // 父节点可以确定的
            
            for(int i = inleft; i <= inright; i++){  // 遍历寻找根节点
                if(pre[pleft] == in[i]){  // in的要排除根节点+-1、
                    root.left = reConBTree(pre,pleft+1,pleft+(i-inleft),in,inleft,i-1);
                    root.right = reConBTree(pre,pleft+1+(i-inleft),pright,in,i+1,inright);
                    break;
                }
            }
            return root;  // 返回父节点
        }
    }
    
    // Arrays.copyOfRange(arr,from,to),注意包括上标,不包括下标
    // Arrays.copyOf(arr,newLength),新长度大于就长度就填充默认值
    import java.util.*;
    public class Solution {
        public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
            
           if(pre.length == 0 || in.length == 0) return null;  // 条件判断
            
            TreeNode root = new TreeNode(pre[0]);  // 创建根节点
            
            for(int i = 0; i < in.length; i++){
                if(pre[0] == in[i]){
                    
                    root.left = reConstructBinaryTree
                        (Arrays.copyOfRange(pre, 1, i+1),
                         Arrays.copyOfRange(in, 0, i));
                    
                    root.right = reConstructBinaryTree
                        (Arrays.copyOfRange(pre, i+1, pre.length), 
                         Arrays.copyOfRange(in, i+1,in.length));
                }
            }
            return root;
        }
    }
    


    5

    两个栈模拟队列,先清空栈2才进入元素,先清空栈1才弹出元素

    import java.util.Stack;
    
    public class Solution {
        Stack<Integer> stack1 = new Stack<Integer>();
        Stack<Integer> stack2 = new Stack<Integer>();
        
        public void push(int node) {
            while(!stack2.isEmpty()){
                stack1.push(stack2.pop());
            }
            stack1.push(node);
        }
        
        public int pop() {
            while(!stack1.isEmpty()){
                stack2.push(stack1.pop());
            }
            return stack2.pop();
        }
    }
    


    6

    旋转数组的最小数字

    // 非递减,即递增有重复
    // 用二分法:最后low指向分界点前一个,high指向分界点后一个,二者相邻,返回low++
    // 第二个指针将指向最小元素
    import java.util.ArrayList;
    public class Solution {
        public int minNumberInRotateArray(int [] array) {
        
            if(array.length == 0) return 0;
            
            int low = 0;
            int high = array.length - 1;
            int mid = 0;
            
            while(low < high){
                mid = low + (high-low)/2;
                if(array[low] < array[mid]){
                    low = mid;
                }else if(array[mid] < array[high]){
                    high = mid;
                }else {  	// 重复元素无法判断是分界前后,只能顺序找
                    low++;  // 且最后也使指针指向分界的后一个
                }
            }
            return array[low];
        }
    }
    


    7

    斐波那契数列

    // 迭代法
    public class Solution {
        public int Fibonacci(int n) {
    
            if(n == 0 || n == 1) return n;
            
            int a = 0;
            int b = 1;
            int c = 0;
            
            while(n > 1){  // 第一项1,上面已经给出了
                c = a + b;
                a = b;
                b = c;
                n--;
            }
            return c;
        }
    }
    
    // 递归
    public class Solution {
        public int Fibonacci(int n) {
            if(n == 0 || n == 1) return n;
            return Fibonacci(n-1) + Fibonacci(n-2);
        }
    }
    


    8

    跳台阶:注重思想,就是斐波那契数列

    迭代没什么好说的

    递归:只剩最后一步时,要么是跳1阶,要么是跳2阶

    // 设n个台阶有f(n)种走法
    // 只剩最后一步时,要么是跳1阶,要么是跳2阶
    // 最后一步跳1阶,即之前有n-1个台阶,据前面的假设,即n-1个台阶有f(n-1)种走法
    // 最后一步跳2阶,即之前有n-2个台阶,据前面的假设,即n-2个台阶有f(n-2)种走法
    // 总结规律:n个台阶的走法等于前两种情况的走法之和即 f(n) = f(n-1)+f(n-2)
    // 变相斐波那契数列
    
    // 注意和上面那题起点不一样
    public class Solution {
        public int JumpFloor(int target) {
            if(target == 1 || target == 2) return target;
            return JumpFloor(target - 1) + JumpFloor(target - 2);
        }
    }
    


    9

    跳台阶II

    // n级台阶,第一步有n种跳法:跳1级、跳2级、……、到跳n级
    // 跳1级,剩下n-1级,则剩下跳法是f(n-1)
    // 跳2级,剩下n-2级,则剩下跳法是f(n-2)
    // 所以f(n) = f(n-1)+f(n-2)+...+f(1)
    // 因为f(n-1) = f(n-2)+f(n-3)+...+f(1)
    // 所以f(n) = 2*f(n-1)
    
    public class Solution {
        public int JumpFloorII(int target) {
            int temp = 1;
            while(target > 1){  // 第一个已给出{
                temp *= 2;
                target--;
            }
            return temp;
        }
    }
    


    10

    矩形覆盖

    // 逆序思想:最后一步有两种情况
    // OO		OO
    // XX		OO
    // XX		XX
    // ..		..
    // XX		XX
    // 第一种情况:阴影部分的n-1块矩形有多少种覆盖方法,为f(n-1);
    // 第二种情况:阴影部分的n-2块矩形有多少种覆盖方法,为f(n-2);
    // 故f(n) = f(n-1) + f(n-2),还是一个斐波那契数列
    
    // 斐波那契数列就是注意起点问题
    public class Solution {
        public int RectCover(int target) {
            if(target == 0) return 0;
            if(target == 1) return 1;
            int a = 1,b = 1,c = 0;
            while(target > 1){ // 第一项1,上面已经给出了
                c = a + b;
                a = b;
                b = c;
                target--;
            }
            return c;
        }
    }
    


    11

    求整数的二进制中1的个数

    // 逐位比较
    public class Solution {
        public int NumberOf1(int n) {
            int cnt = 0;
            while(n != 0){  // 不是大于0,因为有负数
                cnt += n & 1;
                n >>>= 1;
            }
            return cnt;
        }
    }
    
    // 最优解
    // 一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0
    // 那么一个整数的二进制有多少个1,就可以进行多少次这样的操作
    public class Solution {
        public int NumberOf1(int n) {
            int cnt = 0;
            while(n != 0){
                n = n & (n-1);
                cnt++;
            }
            return cnt;
        }
    }
    
    // 作弊解
    // 计算该数的二进制,然后返回二进制中`1`的个数 
    public class Solution {
        public int NumberOf1(int n) {
            return Integer.bitCount(n);
        }
    }
    


    12

    数值的整数次方

    // 连乘思路:O(n)
    public class Solution {
        public double Power(double base, int exponent) {
            double rs = 1;
            for(int i = 0; i < Math.abs(exponent); i++){
                rs *= base;  // 连乘
            }
            if(exponent < 0){
                rs = 1 / rs;  // 负数次幂,直接倒数
            }
            return rs;
      }
    }
    
    // 快速幂:log(n)
    // 11可转化为二进制次幂:11 = 1011 = 2³×1 + 2²×0 + 2¹×1 + 2º×1 = 2³×1 + 2¹×1 + 2º×1
    // base *= base 保持累乘的作用:base-->base2-->base4-->base8-->base16
    // 那么化简后:a¹¹ = a^(2º+2¹+2³) = a^(1+2+8)
    // 那么 a¹¹ = a¹ * a² * a^8 = base * base^2 * base^8
    
    public class Solution {
        public double Power(double base, int exponent) {
            
            double rs = 1;  // 保存结果
            int power = Math.abs(exponent);  // 幂的绝对值
            
            // 快速幂核心
            while(power != 0){
                if( (power & 1) == 1 ){  // 幂的二进制当前位为1即有效,结果相乘
                    rs *= base;
                }
                power >>>= 1;      // 幂右移
                base *= base;  // 保持累乘,后面利用
            }
            
            if(exponent < 0){  // 负次幂,结果取倒数
                rs = 1 / rs;
            }
            return rs;
      }
    }
    


    13

    调整数组顺序,奇数位于偶数前面,且相对顺序不变(稳定性)

    // 分治,思路明了
    import java.util.ArrayList;
    public class Solution {
        public void reOrderArray(int [] array) {
            ArrayList<Integer> list = new ArrayList();
            
            for(int i = 0; i < array.length; i++){
                if(array[i] % 2 != 0){
                    list.add(array[i]);
                }
            }
            
            for(int i = 0; i < array.length; i++){
                if(array[i] % 2 == 0){
                    list.add(array[i]);
                }
            }
            
            for(int i = 0; i < array.length; i++){
                array[i] = (Integer)list.get(i);
            }
        }
    }
    
    // 相对位置不变:保持稳定性即可,冒泡、直接插入等
    // 类似冒泡算法,前偶后奇数就交换:从后往前
    public class Solution {
        public void reOrderArray(int [] array) {
            for(int i = 0; i < array.length; i++){
                for(int j = array.length-1; j > i; j--){
                   if(array[j] % 2 == 1 && array[j-1] % 2 == 0){  // 整体奇数前移
                       int temp = array[j];
                       array[j] = array[j-1];
                       array[j-1] = temp;
                   }
                }
            }
        }
    }
    
    // 就地算法,不借助辅助,原地修改数据结构
    // i 前面的奇数都排好了
    public class Solution {
        public void reOrderArray(int [] array) {
            int temp;
            int i = 0;
            for(int j = 0; j < array.length; j++){
                if(array[j] % 2 != 0){        			// j遇到奇数,非奇后移
                    temp = array[j];         		    // 保存移动覆盖的奇数
                    for(int k = j - 1; k >= i; k--){ 	// i到j的偶数后移一位
                        array[k+1] = array[k];
                    }
                    array[i] = temp;           			// 奇数往前跳动
                    i++;                       			// i指向奇数排好的下一个
                }
            }
        }
    }
    


    14

    返回链表倒数第K个节点

    // 设置快慢指针
    public class Solution {
        public ListNode FindKthToTail(ListNode head,int k) {
            
            ListNode node,pre;
            node = pre = head;
            
            for(int i = 1; i <= k; i++){  // 先驱走k步
                if(pre == null) return null;  // 链表长没有k步长
                pre = pre.next;
            }
            
            while(pre != null){  // 同步走
                pre = pre.next;
                node = node.next;
            }
            return node;
        }
    }
    


    15

    反转链表

    // 需要三个指针
    public class Solution {
        public ListNode ReverseList(ListNode head) {
            
            ListNode pre,next;  // 如果head为空,那么next.next就空指针异常
            pre = next = null;  // 所以只能在head不为空的循环内next了
            
            while(head != null){  // head表示当前节点
                next = head.next;  // 上面说的next,这个要先写
                head.next = pre;
                pre = head;
                head = next;
            }
            return pre;  // 这里注意:上面的循环结束条件为head为空,那么前驱节点才是真正的头节点
        }
    }
    
    


    16

    合并两个链表,并且按大小排序

    public class Solution {
        public ListNode Merge(ListNode list1,ListNode list2) {
            ListNode head,node;  // 要有一个表头head来返回,node保存节点,防止断链
            head = node = new ListNode(0);	// 这里居然这样
            
            while(list1 != null && list2 != null){
                if(list1.val < list2.val){
                    node.next = list1;
                    node = node.next;  // 指针移动
                    list1 = list1.next;
                }else{
                    node.next = list2;
                    node = node.next;
                    list2 = list2.next;
                }
            }
            if(list1 == null) node.next = list2;
            if(list2 == null) node.next = list1;
            return head.next;
        }
    }
    
    


    17

    判断是否某树的子结构(两个递归,一个遍历树,一个匹配)

    // 思路:先序遍历父树,每次遍历都用来匹配
    public class Solution {
        public boolean HasSubtree(TreeNode root1,TreeNode root2) {
            
            // 递归出口:包含了先序遍历的 root != null
            if(root1 == null || root2 == null) return false;
            
            // 先序遍历:当前节点,然后左子树,最后右子树
            return recur(root1,root2) 
                || HasSubtree(root1.left,root2) 
                || HasSubtree(root1.right,root2);
        }
        private boolean recur(TreeNode root1,TreeNode root2){
            if(root2 == null) return true;  		// 可先判断root2为空,即完成遍历匹配
            if(root1 == null) return false;			// 则root1先被击穿
            if(root1.val != root2.val) return false;// 当前节点不匹配
            return recur(root1.left,root2.left) && recur(root1.right,root2.right);
        }
    }
    
    


    18

    转变成二叉树的镜像

    // 递归:也是前序遍历
    public class Solution {
        public void Mirror(TreeNode root) {
            if(root == null) return ;  // 递归出口
            swap(root); 			   // 交换根节点的左右孩子
            Mirror(root.left);  	   // 孩子的孩子也交换
            Mirror(root.right);  	   // 孩子的孩子也交换
        }
        private void swap(TreeNode root){  
            TreeNode temp = root.left;
            root.left = root.right;
            root.right = temp;
        }
    }
    


    19

    螺旋打印矩阵

    // 上下左右,四个边界分别为l,r,t,b
    // 思路1:按题意打印
    import java.util.ArrayList;
    public class Solution {
        public ArrayList<Integer> printMatrix(int [][] matrix) {
            
           ArrayList<Integer> list = new ArrayList();
            
           int left = 0, right = matrix[0].length - 1;  // 四个边界,-1方便后面用等于判断
           int top = 0, bottom = matrix.length - 1;
            
           while(true){  // 无限循环遍历,出口在内部的边界判断
               
               for(int i = left; i <= right; i++) list.add(matrix[top][i]);
               top++;    // 遍历完一行,判断上下是否遍历完
               if(top > bottom) break;
               
               for(int i = top; i <= bottom; i++) list.add(matrix[i][right]);
               right--;
               if(left > right) break;
               
               for(int i = right; i >= left; i--) list.add(matrix[bottom][i]);
               bottom--;
               if(top > bottom) break;
               
               for(int i = bottom; i >= top; i--) list.add(matrix[i][left]);
               left++;
               if(left > right) break;
            }
            return list;
        }
    }
    


    20

    用两个栈求最小元素----O(1)

    import java.util.Stack;
    
    public class Solution {
        
        Stack<Integer> data = new Stack();
        Stack<Integer> min = new Stack();
    
        public void push(int node) {
            data.push(node);
            if(min.isEmpty() || node < min.peek()){
                min.push(node);
            }
        }
        
        public void pop() {
            if(data.peek() == min.peek()){
                min.pop();
            }
            data.pop();
        }
        
        public int top() {
            return data.peek();
        }
        
        public int min() {
            return min.peek();
        }
    }
    


  • 相关阅读:
    93.修改私有属性的值
    92.私有属性和私有方法
    91.多层继承
    python基础入门之四 —— 列表
    python基础入门之三 —— 字符串
    python基础入门之二 —— 条件、循环语句
    如何将本地图片变成图片链接
    python基础入门之一 —— 变量与运算符
    Elasticserach 配置文件详解
    elasticserch:性能优化策略
  • 原文地址:https://www.cnblogs.com/Howlet/p/14089226.html
Copyright © 2011-2022 走看看