数据结构Part
二维数组中查找
题目描述
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
分析
规律:
首先选取数组中右上角的数字,
- 如果该数字等于要查找的数字,查找过程结束;
- 如果该数字小于要查找的数字,剔除该数字所在的行;
- 如果该数字大于要查找的数字,剔除该数字所在的列;
一步一步缩小范围,直到找到要查找的数字,或者查找范围为空。
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方法,主要考虑两个问题:
- 是在原来的字符串上直接替换,还是新申请一个字符串保存替换后的结果;
- 如何替换更有效率?
首先,直接扩展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();
}
}