【剑指Offer】 数据结构Part

二维数组中查找

题目描述

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

分析

规律
首先选取数组中右上角的数字,

  1. 如果该数字等于要查找的数字,查找过程结束;
  2. 如果该数字小于要查找的数字,剔除该数字所在的行;
  3. 如果该数字大于要查找的数字,剔除该数字所在的列;

一步一步缩小范围,直到找到要查找的数字,或者查找范围为空。

Java代码

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

替换空格

题目描述

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

分析

不考虑Java原有的repalce方法,主要考虑两个问题:

  1. 是在原来的字符串上直接替换,还是新申请一个字符串保存替换后的结果;
  2. 如何替换更有效率?

首先,直接扩展StringBuffer的大小,使用的空间比新申请一个字符串用的空间更小。
其次,如果选择从前往后替换,每替换一个空格,后面的字符就要往后移动一次,效率非常低下。如果从后往前替换,需要先计算好需要的空间,即知道原来的字符串中有多少个空格,然后从后往前处理,每个字符只要访问一次,效率更高。

Java代码

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

从尾到头打印链表

题目描述

输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。
结点ListNode结构如下:

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

    ListNode(int val) {
		this.val = val;
	}
}

分析

打印链表所以一定要遍历链表,要求从后往前打印,则用来实现。每访问一个结点就将其值push入栈中,遍历结束后,从栈中一个一个pop出来。

Java代码

import java.util.*;
public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        ArrayList<Integer> list= new ArrayList<Integer>();
        Stack<Integer> stack = new Stack<Integer>();
        ListNode p = listNode;
        if(p != null){
            while(p.next != null){
                stack.push(p.val);
                p = p.next;
            }
            stack.push(p.val);
        }
        while(!stack.isEmpty()){
            list.add(stack.pop());
        }
        return list;
    }
}


重建二叉树

题目描述

输入某二叉树的前序遍历中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{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;
	}
}

分析

根据前序遍历序列,可以知道树的根节点。如题目中的例子:

先序遍历:1 2 4 7 3 5 6 8
中序遍历:4 7 2 1 5 3 8 6

1是树的节点,红色的数字是子树的元素,蓝色的数字是子树的颜色。
然后对左右子树递归处理
例如左子树

先序遍历:2 4 7
中序遍历:4 7 2

右子树

先序遍历:3 5 6 8
中序遍历:5 3 8 6

以此类推。
在这里插入图片描述

Java代码

public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        return constructBT(pre, 0, pre.length-1,
                           in,  0, in.length-1);
    }
    public TreeNode constructBT(int[] pre, int preLeft, int preRight,
                                int[] in,  int inLeft,  int inRight){
        // 边界条件
        if(preLeft > preRight || inLeft > inRight)
            return null;
        // pre[preleft]一定是树或者子树的根节点
        TreeNode root = new TreeNode(pre[preLeft]);
        // 找到该根节点在中序遍历序列中的位置,
        // 再由此确定左子树和右子树(元素的下标范围、个数)
        for(int i=inLeft; i<=inRight; i++){
            if(in[i] == root.val){
                int lSubTreeLen = i - inLeft;
                int rSubTreeLen = inRight - i;
                // 构建左子树
                root.left = constructBT(pre, preLeft+1, preLeft+lSubTreeLen, 
                                        in, inLeft, i-1);
                // 构建右子树
                root.right = constructBT(pre, preRight-rSubTreeLen+1, preRight,
                                         in, i+1, inRight);
            }
        }
        return root;
    }
}

用两个栈实现队列

题目描述

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

分析

如图所示,每次执行pop(),元素都是从stack2中pop出的;而每次执行push(),元素都是push入stack1的。
在这里插入图片描述
在这里插入图片描述
由于队列是先进先出,栈是先进后出,因此,每次要从队列中pop一个元素出来的时候,都要将元素先从stack1中pop出,再push入stack2。待stack1空后,即所有元素都转移到stack2中,再从stack2中pop出最上面的一个元素即为所求。
push()同理,要将所有的元素都转移到stack1中再接着push。

Java代码

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();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值