【写给自己】剑指offer 编程题思路总结(一)

适逢实习招聘,虽然自己的剑指offer已经刷过一遍,但是深感记忆的有限性。

纸笔不如博文,在此写下自己对于剑指offer上题目的总结。

目标是以精简的语言写出思路,以便自己以后翻阅。


1.二维数组中的查找

题目描述

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

代码如下:

public class Solution {
    public boolean Find(int target, int [][] array) {
        int rows = array.length;            //得到行数  
        int cols = array[0].length;        //得到列数
        int i=rows-1,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;
    }
}

因为行列的数字排列均有序,本题可以采用对每行采用二分排序的方法来对每行进行查找,从而达到O(nlogn)的复杂度

但是可以从第rows-1行,第0列开始,判断数组的数字与所求target之间的大小,这样所走的路程不会超过行列数之和,复杂度为O(m+n)


2.替换空格

题目描述

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

public class Solution {
    public String replaceSpace(StringBuffer str) {
    	StringBuffer str1 = new StringBuffer();
        String str2 = str.toString();
        for(int i = 0 ; i<str2.length();i++){
          
            if(str2.charAt(i)!=' '){
                str1.append(str2.charAt(i));
            }
            else {str1.append("%20");};
        }
        
        return str1.toString() ; 
        

    }
}

本题输入的是一个StringBuffer类。本题考查的是java中String族的API。

StringBuffer.toString()返回一个String对象

String.charAt(i) 返回第i个char类型的字符

因为本题是将字符进行替换,所以可以判断string.charAt是否为' '  if-then地将结果append到StringBuffer上,因为StringBuffer可变,可以用来节省内存。

最后用toString()方法来返回一个String对象


我们总结一下String族的异同:

1.三者在执行速度方面的比较:StringBuilder >  StringBuffer  >  String

String最慢的原因:

  String为字符串常量,而StringBuilder和StringBuffer均为字符串变量,即String对象一旦创建之后该对象是不可更改的,但后两者的对象是变量,是可以更改的。

2. 再来说线程安全

  在线程安全上,StringBuilder是线程不安全的,而StringBuffer是线程安全的

如果一个StringBuffer对象在字符串缓冲区被多个线程使用时,StringBuffer中很多方法可以带有synchronized关键字,所以可以保证线程是安全的,但StringBuilder的方法则没有该关键字,所以不能保证线程安全,有可能会出现一些错误的操作。所以如果要进行的操作是多线程的,那么就要使用StringBuffer,但是在单线程的情况下,还是建议使用速度比较快的StringBuilder。

 3. 总结一下
  String:适用于少量的字符串操作的情况

  StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况

  StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况


3.从尾到头打印链表

题目描述

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

代码如下:

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

本题输入一个List Node类型的结点,根据上述定义,ListNode节点:有一个val ,一个next指针(?),一个构造函数。

看题意,本题输入一个ListNode,返回一个ArrayList。

假如我们采用从头到尾,每次加入一个节点的方法(链表只有next指针),时间复杂度过大。

所以我们采用递归的思想。

当节点的下一个值不为null时,则将其next放入原函数,同时在之后将其值放入ArrayList中,这样,当我们运行到链表的尾部时,函数返回此arrayList,同时,将此ListNode节点的值也加入了arraylist中,最后返回的arraylist中,存放的便是从尾到头的链表的val。


4.重建二叉树

题目描述

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{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) {
          return reConBTree(pre,0,pre.length-1,in,0,in.length-1);
    }
    public TreeNode reConBTree(int [] pre,int preleft,int preright,int [] in,int inleft,int inright){
        if(preleft > preright || inleft> inright)//当到达边界条件时候返回null
            return null;
        //新建一个TreeNode
        TreeNode root = new TreeNode(pre[preleft]);
        //对中序数组进行输入边界的遍历
        for(int i = inleft; i<= inright; i++){
            if(pre[preleft] == in[i]){
                //重构左子树,注意边界条件
                root.left = reConBTree(pre,preleft+1,preleft+i-inleft,in,inleft,i-1);
                //重构右子树,注意边界条件
                root.right = reConBTree(pre,preleft+i+1-inleft,preright,in,i+1,inright);
            }
        }
        return root;     
    }
}

对二叉树进行递归,由于先序遍历的第一个节点是根节点,所以在中序遍历中找寻该值。

将此树的左子树 和 右子树进行递归。



5.用2个栈实现队列

题目描述

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

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!");
        }
        if(!stack2.empty()){
            return stack2.pop();
        }
        else {
            while(!stack1.empty()){
                stack2.push(stack1.pop());
            }
            return stack2.pop();
        }
    }
}

每当push时,直接将值压入push栈中。

每当pop时,需要考虑pop栈的情况,如果pop栈非空,则弹出最顶端节点,如果pop栈为空,则将push栈中内容全部放入pop栈中,然后pop出顶端节点。此时push pop结果与队列相同。


6.旋转数组的最小数字

题目描述

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。


public class Solution {
    public int minNumberInRotateArray(int [] array) {
        int low = 0 ; int high = array.length - 1;   
        while(low < high){
            int mid = low + (high - low) / 2;        
            if(array[mid] > array[high]){
                low = mid + 1;
            }else if(array[mid] == array[high]){
                high = high - 1;
            }else{
                high = mid;
            }   
        }
        return array[low];
    }
}



采用二分法解答这个问题,
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就不会错误



本系列暂定为牛客网上的66题,分11篇。

猜你喜欢

转载自blog.csdn.net/ach_orite/article/details/79755763
今日推荐