zoukankan      html  css  js  c++  java
  • 剑指offer编程题66道题 1-25

    1.二维数组中的查找

    题目描述

    在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
    思路:
    利用二维数组由上到下,由左到右递增的规律,
    那么选取右上角a[row][col]与target进行比较,如果等于就直接找到,
    当target小于元素a[row][col]时,那么target必定在元素a所在行的左边,
    即col--;
    当target大于元素a[row][col]时,那么target必定在元素a所在列的下边,
    即row++;
        public class Solution {
      public boolean Find(int target, int [][] array) {         int row=0;         int col=array[0].length-1;         while(row<=array.length-1&&col>=0){             if(target==array[row][col])                 return true;             else if(target>array[row][col])                 row++;             else                 col--;         }         return false;       } }

    注意:

    对于一个二维数组:

    int[][] arr = {  
            {2,3,4},  
            {4,5,6},  
            {7,8,9}  
        };  

    int rows = i.length;//行数
    int columns = i[0].length;//列数

    [[]]这样一个数组代表行数是1,列数时0.

    2.替换空格

    题目描述

    请实现一个函数,将一个字符串中的空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
    思路:构造一个StringBuffer类,逐个添加字符,遇到空间换成%20之后再添加。
    public class Solution {
        
         public String replaceSpace(StringBuffer str) {
             StringBuffer strCopy= new StringBuffer();
             
             for(int i=0 ; i<str.length();i++){
                 String c = String.valueOf(str.charAt(i));
                 if(c.equals(" ")){
                 strCopy.append("%20");
             }else {
                 strCopy.append(c);
             }
            }
    
        return strCopy.toString();
     }
    }

    注意:

    Java中字符数组、String类、StringBuffer三者的相互转换

     

    3.从尾到头打印链表

    题目描述

    输入一个链表,从尾到头打印链表每个节点的值。

    思路:遇到先进后出的情况,可以采取两种思路:

    1.利用堆栈的思想

    2.利用递归的思想

    下面是采用堆栈的思想:

    /**
    *    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) {
            Stack<ListNode> stack = new Stack<ListNode>();
            while(listNode!=null){
                stack.push(listNode);
                listNode = listNode.next;
            }
            
            ArrayList<Integer> list = new ArrayList<Integer>();
            while(!stack.isEmpty()){
                list.add(stack.pop().val);
            }
            return list;
            
        }
    }

    下面是采用递归的思想 :

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

     4.重建二叉树

    题目描述

    输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
    例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
    方式1:
    
    import java.util.*;
    /**
     * 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) {
           if(pre.length == 0||in.length == 0){
                return null;
            }
            TreeNode node = new TreeNode(pre[0]);
            for(int i = 0; i < in.length; i++){
                if(pre[0] == in[i]){
                    node.left = reConstructBinaryTree(Arrays.copyOfRange(pre, 1, i+1), Arrays.copyOfRange(in, 0, i));
                    node.right = reConstructBinaryTree(Arrays.copyOfRange(pre, i+1, pre.length), Arrays.copyOfRange(in, i+1,in.length));
                   break;
            }         }         return node;     } }

    这是形式最为简洁的方法,首先前序的第一个数字肯定是根节点,然后根据这个元素在中序中找到根节点位置i,

    那么中序左边0,i是左子树,右边i+1,in.length是右子树。

    并且也能在前序中确定左子树是从1,i+1的元素,右子树是i+1,pre.length。(注意这里是左边界包含,右边界不包含)

    然后从原数组中分别复制前序和中序的左子树和右子树,递归调用即可。直到pre.length或者in.length(当然这里他俩本来就一样)为0表示递归到叶子节点。

    注:Arrays.copyOfRange()方法

    要使用这个方法,首先要import java.util.*;
    Arrays.copyOfRange(T[ ] original,int from,int to)
    将一个原始的数组original,从小标from开始复制,复制到小标to,生成一个新的数组。
    注意这里包括下标from,不包括下标to。
     
    这个方法在一些处理数组的编程题里很好用,效率和clone基本一致,都是native method,比利用循环复制数组效率要高得多。
     
    2.方式2
    另外一种方法其实跟上面的思路是一样的,就是不需要复制出左子树和右子树的数组,直接在原数组上操作。
    在原数组上操作就是要搞清楚每次递归的时候左子树和右子树在原数组上的位置,
    对于中序比较好确认,以根节点i为分界线,startIn至i-1为左子树,i+1至endIn为右子树
    对于前序,前序的索引需要通过中序左右子树的大小 i-startIn 来计算,前序左子树为startPre+1至startPre+i-startIn,前序右子树为startPre+i-startIn
    startPre>endPre||startIn>endIn为表示已经递归完成
    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;
        }
    }

    5.用两个栈实现队列

    题目描述

    用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
    思路:
    当入队列的时候,直接放入栈1中即可
    当出队列的时候,如果栈2不为空,直接弹出,如果为空从栈1全部放入栈2中,然后再出栈。这样才能保证有序的出队列。
    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(stack2.empty()){
                while(!stack1.empty()){
                    stack2.push(stack1.pop());
                }
            }
            return stack2.pop();
        }
    }

     6.旋转数组的最小数字

    题目描述

    把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
    三种方法,
    1、最笨的一种:
        遍历整个数组,找出其中最小的数。这样肯定拿不到offer
    2、稍微优化:
    public int minNumberInRotateArray(int[] array) {
            if (array.length == 0)
                return 0;
            for (int i = 0; i < array.length - 1; i++) {
                if (array[i] > array[i + 1])
                    return array[i + 1];
            }
            return array[0];
        } 

    3.采用二分法解答这个问题,

    mid = low + (high - low)/2
    需要考虑三种情况:
    (1)array[mid] > array[high]:
    出现这种情况的array类似[3,4,5,6,0,1,2],此时最小数字一定在mid的右边。
    low = mid + 1
    (2)array[mid] == array[high]:
    出现这种情况的array类似 [1,0,1,1,1] 或者[1,1,1,0,1],此时最小数字不好判断在mid左边
    还是右边,这时只好一个一个试 ,
    high = high - 1
    (3)array[mid] < array[high]:
    出现这种情况的array类似[2,2,3,4,5,6,6],此时最小数字一定就是array[mid]或者在mid的左
    边。因为右边必然都是递增的。
    high = mid
    注意这里有个坑:如果待查询的范围最后只剩两个数,那么mid 一定会指向下标靠前的数字
    比如 array = [4,6]
    array[low] = 4 ;array[mid] = 4 ; array[high] = 6 ;
    如果high = mid - 1,就会产生错误, 因此high = mid
    但情形(1)中low = mid + 1就不会错误
    import java.util.ArrayList;
    public class Solution {
       public int minNumberInRotateArray(int [] array) {
            int low = 0;int high = array.length-1;
            while(low<high){
                int mid = (low + high)/2;
                if(array[mid]<array[high]){
                    high=mid;
                }else if(array[mid]==array[high]){
                    high = high-1;
                }else{
                    low = mid +1;
                }
            }
            return array[low];
        }
    }

    7——10 都是斐波拉契数列的变形题

    7.斐波拉契数列

    题目描述

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

    n<=39

    public class Solution {
        public int Fibonacci(int n) {
            if(n<2)
                return n;
            int fb1 = 0;
            int fb2 = 1;
            int fbn = 0;
            for(int i=2;i<=n;i++){
                fbn = fb1+fb2;
                fb1 = fb2;
                fb2 = fbn;
            }
            return fbn;
        }
    }

    8.跳台阶

    题目描述

    一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

                  | 1, (n=1)

    f(n) =     | 2, (n=2)

                  | f(n-1)+f(n-2) ,(n>2,n为整数)


    对于本题,前提只有 一次 1阶或者2阶的跳法。

    a.如果两种跳法,1阶或者2阶,那么假定第一次跳的是一阶,那么剩下的是n-1个台阶,跳法是f(n-1);

    b.假定第一次跳的是2阶,那么剩下的是n-2个台阶,跳法是f(n-2)

    c.由a假设可以得出总跳法为: f(n) = f(n-1) + f(n-2) 

    d.然后通过实际的情况可以得出:只有一阶的时候 f(1) = 1 ,只有两阶的时候可以有 f(2) = 2

    e.可以发现最终得出的是一个斐波那契数列:

     思路:

    1.用迭代的方法

     public int JumpFloor(int target) {
    
            if(target == 1 || target == 2)
                return target;
            
              int floorOne = 1;
              int floorTwo = 2;
              int floorSum = 0;
                
               for(int i=3;i<=target;i++){
                   floorSum = floorOne +floorTwo;
                   floorOne = floorTwo;
                   floorTwo = floorSum;
            }
                return floorSum;  
    }   

     2.用递归的方法

    public int JumpFloor(int target) {
    
            if(target == 1 || target == 2){
                return target;
            }else{
                return JumpFloor(target-1)+JumpFloor(target-2);
            }
        }

    9.变态跳台阶

    题目描述

    一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法

     思路:

    每个台阶都有跳与不跳两种情况(除了最后一个台阶),最后一个台阶必须跳。所以共用2^(n-1)中情况

    解法有两种:一种用移位操作,一个用Math工具

    import java.lang.Math;
    public class Solution {
        public int JumpFloorII(int target) {
            //return (int)Math.pow(2,target-1);
            return 1<< --target;
        }
    }

     10.矩形的覆盖

    题目描述

    我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
    思路:这是一个斐波拉契问题
    public class Solution {
        public int RectCover(int target) {
            if(target == 1 || target == 2)
                return target;
            
              int floorOne = 1;
              int floorTwo = 2;
              int floorSum = 0;
                
               for(int i=3;i<=target;i++){
                   floorSum = floorOne +floorTwo;
                   floorOne = floorTwo;
                   floorTwo = floorSum;
            }
                return floorSum;  
            
        }
    }

    11.二进制中1的个数

    题目描述

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

    方法:

    1.最简单方法

    return Integer.bitCount(n);

    2.用1(1自身左移运算,其实后来就不是1了)和n的每位进行位与,来判断1的个数

        public int NumberOf1(int n) {
           int count = 0;
                int flag1 = 1;
                while (flag1 != 0){
                    if((n & flag1 ) != 0)
                        count++;
                    flag1 = flag1<<1;
                }
            return count;
            } 
    }

    3.如果一个整数不为0,那么这个整数至少有一位是1。如果我们把这个整数减1,那么原来处在整数最右边的1就会变为0,原来在1后面的所有的0都会变成1(如果最右边的1后面还有0的话)。其余所有位将不会受到影响。

    举个例子:一个二进制数1100,从右边数起第三位是处于最右边的一个1。减去1后,第三位变成0,它后面的两位0变成了1,而前面的1保持不变,因此得到的结果是1011.我们发现减1的结果是把最右边的一个1开始的所有位都取反了。这个时候如果我们再把原来的整数和减去1之后的结果做与运算,从原来整数最右边一个1那一位开始所有位都会变成0。如1100&1011=1000.也就是说,把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0.那么一个整数的二进制有多少个1,就可以进行多少次这样的操作。
    public class Solution {
        public int NumberOf1(int n) {
            int count = 0;
            while(n!= 0){
                count++;
                n = n & (n - 1);
             }
            return count;
        }
    }

     12.数值的整数次方

    题目描述

    给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
    思路:本题非常考察代码的完整性
    1.分母为0,次方为负数
    2.分母不为0,次方为0
    3.分不为0,次方为负数
    4.分不为0,次方为正数
    public class Solution {
        public double Power(double base, int exponent) {
            if(base == 0 && exponent < 0){
                throw new RuntimeException("分母不能为0");
            }else if(exponent==0){
                return 1;
            }
            
            double absResult = 0;
            if(exponent<0){
                absResult = absExponent(base,-exponent);
                absResult = 1/absResult;
            }else{
                absResult = absExponent(base,exponent);
            }
            return absResult;
      }
        
        public double absExponent(double base,double exponent){
            double result = base;
            for(int i=1;i<exponent;i++){
                result = base*result;
            }
            return result;
            
        }
    }

     13.调整数组使奇数位于偶数前面

    题目描述

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

    思路:

    时间复杂度为O(n),空间复杂度为O(n)的算法,用空间换时间

    public class Solution {
        public void reOrderArray(int [] array) {
            int[] tempArray = new int[array.length];
            int j =0;
            for (int i = 0; i<array.length;i++){
                if((array[i]&1) == 1){
                    tempArray[j] = array[i];
                    j++;
                }
            }
            for (int i = 0; i<array.length;i++){
                if((array[i]&1) != 1){
                    tempArray[j] = array[i];
                    j++;
                }
            }
            for(int i=0;i<array.length;i++){
                array[i] = tempArray[i];
           }
           
        }
    }

    14.链表中第k个节点

    题目描述

    输入一个链表,输出该链表中倒数第k个结点。
    思路:
    1.两个指针,先让第一个指针和第二个指针都指向头结点,然后再让第一个指正走(k-1)步,到达第k个节点。然后两个指针同时往后移动,当第一个结点到达末尾的时候,第二个结点所在位置就是倒数第k个节点了
    public class Solution {
        public ListNode FindKthToTail(ListNode head,int k) {
            //头结点为空节点或者输入的k为0
            if (head == null || k == 0){
                return null;
            }
            
            ListNode listA = head;
            ListNode listB = head;
            
            for (int i=0; i<k-1;i++){
                if(listA.next != null)
                    listA = listA.next;
                //链表的长度小于k
                else
                     return null;
            }
            //遍历链表一次找到第k个节点
            while(listA.next != null){
                listA = listA.next;
                listB = listB.next;
            }
            return listB;
        }
    }

     2.遇到这种倒置行为一定要想到堆栈,使用Stack,将结点压入栈中,再取出第k个就好

    if(head == null || k ==0 ){
                return null;
            }
            
            //可以先把链表反转,然后找出第k个
            Stack<ListNode> stack = new Stack<ListNode>();
            int count = 0;
            while(head != null){
                stack.push(head);
                head = head.next;
                count++;
            }
             
            if(count < k){
                return null;
            }
             
            ListNode knode = null;
            for(int i = 0; i < k; i++){
                knode = stack.pop();
            }
             
             
            return knode;

     15.反转链表

    题目描述

    输入一个链表,反转链表后,输出新链表的表头。
    思路:利用三个指针,pre head next将指针的指向反向
    public class Solution {
    public ListNode ReverseList(ListNode head) {
           
            if(head==null)
                return null;
            //head为当前节点,如果当前节点为空的话,那就什么也不做,直接返回null;
            ListNode pre = null;
            ListNode next = null;
            //当前节点是head,pre为当前节点的前一节点,next为当前节点的下一节点
            //需要pre和next的目的是让当前节点从pre->head->next1->next2变成pre<-head next1->next2
            //即pre让节点可以反转所指方向,但反转之后如果不用next节点保存next1节点的话,此单链表就此断开了
            //所以需要用到pre和next两个节点
            //1->2->3->4->5
            //1<-2<-3 4->5、
            while(head!=null){
                //做循环,如果当前节点不为空的话,始终执行此循环,此循环的目的就是让当前节点从指向next到指向pre
                //如此就可以做到反转链表的效果
                //先用next保存head的下一个节点的信息,保证单链表不会因为失去head节点的原next节点而就此断裂
                next = head.next;
                //保存完next,就可以让head从指向next变成指向pre了,代码如下
                head.next = pre;
                //head指向pre后,就继续依次反转下一个节点
                //让pre,head,next依次向后移动一个节点,继续下一次的指针反转
                pre = head;
                head = next;
            }
            //如果head为null的时候,pre就为最后一个节点了,但是链表已经反转完毕,pre就是反转后链表的第一个节点
            //直接输出pre就是我们想要得到的反转后的链表
            return pre;
     
    }
    }

     16.合并两个排序的链表

    题目描述

    输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
    思路:递归判断
    public class Solution {
        public ListNode Merge(ListNode list1,ListNode list2) {
            if(list1 == null)
                 return list2;
            else if(list2 == null)
                 return list1;
            ListNode mergedHead = null;
            if(list1.val< list2.val){
                mergedHead = list1;
                mergedHead.next = Merge(list1.next,list2);
            }else{
                mergedHead = list2;
                mergedHead.next = Merge(list1,list2.next);
            }
            return mergedHead;
        }
    }

     迭代:

    public ListNode Merge(ListNode list1, ListNode list2) {
        ListNode head = new ListNode(-1);
        ListNode cur = head;
        while (list1 != null && list2 != null) {
            if (list1.val <= list2.val) {
                cur.next = list1;
                list1 = list1.next;
            } else {
                cur.next = list2;
                list2 = list2.next;
            }
            cur = cur.next;
        }
        if (list1 != null)
            cur.next = list1;
        if (list2 != null)
            cur.next = list2;
        return head.next;
    }

    17.树的子结构

    题目描述

    输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
    思想:
    1.递归思想,如果根节点相同则递归调用DoesTree1HaveTree2(),如果根节点不相同,则判断tree1的左子树和tree2是否相同,再判断右子树和tree2是否相同
    2.如果Tree2为空,则说明第二棵树遍历完了,即匹配成功;如果tree1为空&&tree2不为空说明不匹配;如果两个根节点相同则继续遍历判断
    public class Solution {
        public boolean HasSubtree(TreeNode root1,TreeNode root2) {
           if(root1 ==null || root2 == null)
               return false;
            return DoTreeHavaTree2(root1,root2) || HasSubtree(root1.left,root2) || HasSubtree(root1.right,root2);
        }
        
        public boolean DoTreeHavaTree2(TreeNode root1,TreeNode root2){
            if(root2 == null)
                return true;
            if(root1 == null)
                return false;
            if(root1.val == root2.val)
                return DoTreeHavaTree2(root1.left,root2.left) && DoTreeHavaTree2(root1.right,root2.right);
            else
             return false;
        }
    }

     18.二叉树的镜像

    题目描述

    操作给定的二叉树,将其变换为源二叉树的镜像。
    思路:先前序遍历这棵树的每个结点,如果遍历到的结点有子结点,就交换它的两个子节点,当交换完所有的非叶子结点的左右子结点之后,就得到了树的镜像.
    import java.util.Stack;
    public class Solution {
        public void Mirror(TreeNode root) {
                if(root ==null || root.left == null && root.right ==null)
                    return;
                Stack<TreeNode> tStack = new Stack<TreeNode>(); 
                tStack.add(root);
                while(tStack.size() != 0){
                    TreeNode p = tStack.pop();
                    
                  if(p.left!=null || p.right!=null){
                    TreeNode temp = p.left;
                    p.left = p.right;
                    p.right = temp;
                    
                    if(p.left != null)
                        tStack.push(p.left);
                    if(p.right != null)
                        tStack.push(p.right);
                    }
                }
            }
    }

     19.顺时针打印矩阵

    题目描述

    输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下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  row = matrix.length;
             int  col = matrix[0].length;
             ArrayList<Integer> list = new ArrayList<Integer>();
             if(row == 0 || col ==0) return list;
             // 定义四个关键变量,表示左上和右下的打印范围
              int left = 0;
              int top = 0;
              int right = col - 1;
              int bottom = row - 1;
              while (left <= right && top <= bottom)
              {
                    // left to right
                       for(int i = left; i <= right; ++i)    
                             list.add(matrix[top][i]);
                     // top to bottom
                       for (int i = top + 1; i <= bottom; ++i)
                               list.add(matrix[i][right]);
                    // right to left 特别注意这里和后面两个for循环的判断条件,防止单行或者单列出现重复扫描技术的情况
                    //如果top != bottom说明从右到左的扫描不会和之前从左到右重复
                    if (top != bottom)
                       for (int i = right - 1; i >= left; --i)
                              list.add(matrix[bottom][i]);
                    // bottom to top
                    if (left != right) 
                       for (int i = bottom - 1; i >= top+1; --i)
                              list.add(matrix[i][left]);
                  left++;top++;right--;bottom--;
              }
        return list; 
        }
    }

     20.包含min函数的栈

    题目描述

    定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
    思路:
    用一个栈data保存数据,用另外一个栈min保存依次入栈最小的数
    比如,data中依次入栈,543, 8, 10, 11, 12, 1
        则min依次入栈,543,no,no, no, no, 1
    no代表此次不入栈
    每次入栈的时候,如果入栈的元素比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) {
            stack.push(node);
            if(minstack.empty() || node < minstack.peek())
                minstack.push(node);
        }
        
        public void pop() {
            if(stack.peek() == minstack.peek())
                minstack.pop();
            stack.pop();
        }
        
        public int top() {
            return stack.peek();
        }
        
        public int min() {
             return minstack.peek();
            
        }
    }

     21.栈的压入、弹出序列

    题目描述

    输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

    思路:借用一个辅助的栈,遍历压栈顺序,先讲第一个放入栈中,这里是1,然后判断栈顶元素是不是出栈顺序的第一个元素,这里是4,很显然1≠4,所以我们继续压栈,直到相等以后开始出栈,出栈一个元素,则将出栈顺序向后移动一位,直到不相等,这样循环等压栈顺序遍历完成,如果辅助栈还不为空,说明弹出序列不是该栈的弹出顺序。

    举例:

    入栈1,2,3,4,5

    出栈4,5,3,2,1

    首先1入辅助栈,此时栈顶1≠4,继续入栈2

    此时栈顶2≠4,继续入栈3

    此时栈顶3≠4,继续入栈4

    此时栈顶4=4,出栈4,弹出序列向后一位,此时为5,,辅助栈里面是1,2,3

    此时栈顶3≠5,继续入栈5

    此时栈顶5=5,出栈5,弹出序列向后一位,此时为3,,辅助栈里面是1,2,3

    ….

    依次执行,最后辅助栈为空。如果不为空说明弹出序列不是该栈的弹出顺序。
    import java.util.ArrayList;
    import java.util.Stack;
    public class Solution {
        public boolean IsPopOrder(int [] pushA,int [] popA) {
            if(pushA.length == 0 || popA.length == 0)
                return false;
            int popIndex = 0;
            Stack <Integer> s = new Stack<Integer>();
            for(int i=0;i<pushA.length;i++){
                s.push(pushA[i]);
                while(!s.empty() && popA[popIndex] == s.peek()){
                    s.pop();
                    popIndex++;
                }
            }
          return s.empty();
        }
    }

     22.从上向下打印二叉树

    题目描述

    从上往下打印出二叉树的每个节点,同层节点从左至右打印。
    思路:层次遍历 队列 LinkedList即可做链表也可以做队列
    public class Solution {
        public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
                LinkedList<TreeNode> q = new LinkedList<TreeNode>();
                ArrayList <Integer> list = new ArrayList<Integer>();
                if(root == null)
                    return list;
                q.offer(root);
                while(!q.isEmpty()){
                    TreeNode p = q.poll();
                    list.add(p.val);
                    if(p.left != null)
                        q.offer(p.left);
                    if(p.right != null)
                        q.offer(p.right);
                }
            return list;
        }
    }

     递归解法:

    import java.util.ArrayList;
    public class Solution {
        ArrayList<Integer> list=new ArrayList<Integer>();
        public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
            if(root!=null)
                list.add(root.val);
            print(root);
            return list;
        }
        public  void print(TreeNode root){
            if(root!=null){
                if(root.left!=null)
                    list.add(root.left.val);
                if(root.right!=null)
                    list.add(root.right.val);
                print(root.left);
                print(root.right);
            }
        }
    }

     23.二叉搜索树的后序遍历

    题目描述

    输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
    思路:找住二叉查找树的特点:左子树<根<=右子树  使用分治思想
    import java.util.*;
    public class Solution {
        public static boolean VerifySquenceOfBST(int [] sequence) {
          int length = sequence.length;
            if(sequence == null || sequence.length == 0)
                return false;
        //在二叉搜索树中左子树节点的值小于根节点的值,找到最后一个左子树节点
    int i = 0; for(;i<sequence.length-1;i++){ if(sequence[i]>sequence[length-1]) break; } //在二叉搜索树中右子树节点的值大于根节点的值 int j = i; for(;j<sequence.length-1;j++){ if(sequence[j]<sequence[length-1]) return false; } boolean left = true; if(i>0) left = VerifySquenceOfBST(Arrays.copyOfRange(sequence, 0, i)); boolean right = true; if(i<length-1) right = VerifySquenceOfBST(Arrays.copyOfRange(sequence,i,length-1)); return left && right; } }

     24.二叉树中和为某一值的路径

    题目描述

    输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)
    思路:
    当用前序遍历的方式访问某一个节点时,我们把该节点添加到路径上,并累加该节点的值。如果该节点时叶节点,并且路径中节点的值的和正好等于输入的整数,则添加这条路径;
    当前节点访问结束后,递归函数自动回到它的父节点,我们可以看出保存路径的数据结构实际上是一个栈,而递归调动本质就是一个压栈和出栈的过程。
    public class Solution {
        private ArrayList<ArrayList<Integer>> listAll = new ArrayList<ArrayList<Integer>>();
        private ArrayList<Integer> list = new ArrayList<Integer>();
        public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
            if(root == null) return listAll;
            list.add(root.val);
            target -= root.val;
            if(target == 0 && root.left == null && root.right == null)
                listAll.add(new ArrayList<Integer>(list));
            FindPath(root.left, target);
            FindPath(root.right, target);
            //回退的时候删去当前节点
            list.remove(list.size()-1);
            return listAll;
        }
    }

    25.复杂链表的复制 

    题目描述

    输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
    /*
            1、复制每个节点,如:复制节点A得到A1,将A1插入节点A后面
            2、遍历链表,A1->random = A->random->next;
            3、将链表拆分成原链表和复制后的链表
        */
    public class Solution {
        public RandomListNode Clone(RandomListNode pHead)
        {
            if(pHead == null)
                return null;
            RandomListNode node = pHead;
            while(node != null){
                RandomListNode cloneNode = new RandomListNode(node.label);
                cloneNode.next = node.next;
                node.next = cloneNode;
                node = cloneNode.next;
            }
            
            node = pHead;
            while(node != null){
                if(node.random != null)
                    node.next.random = node.random.next;
                    node = node.next.next;
            }
            
            RandomListNode head = pHead.next;
            RandomListNode cloneNode = head;
            node = pHead;
            while(node != null){
                node.next = node.next.next;
                if(node.next != null)
                    cloneNode.next = cloneNode.next.next;
                node = node.next;
                cloneNode = cloneNode.next;
            }
            return head;
        }
    }
  • 相关阅读:
    Django+Bootstrap+Mysql 搭建个人博客(一)
    Django+nginx+uwsgi部署教程(centos7+ubuntu16.4)
    微信小程序入门(六)
    微信小程序入门(五)
    微信小程序入门(四)
    微信小程序入门(三)
    微信小程序入门(二)
    遗传算法常见问题解惑
    记录使用python实现QPSO求解最大值问题时,遇到的问题
    关于JetBrain系列软件的学生授权认证和授权到期(一年)重新申请的问题
  • 原文地址:https://www.cnblogs.com/xiangkejin/p/9080035.html
Copyright © 2011-2022 走看看